/* * EditPane.java - Text area and buffer switcher * :tabSize=4:indentSize=4:noTabs=false: * :folding=explicit:collapseFolds=1: * * Copyright (C) 2000, 2005 Slava Pestov * * This program is free software; you can redistribute it and/or * modify it under the terms of the GNU General Public License * as published by the Free Software Foundation; either version 2 * of the License, or any later version. * * This program is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the * GNU General Public License for more details. * * You should have received a copy of the GNU General Public License * along with this program; if not, write to the Free Software * Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA. */ package org.gjt.sp.jedit; //{{{ Imports import java.awt.BorderLayout; import java.awt.Color; import java.awt.Dimension; import java.awt.Graphics2D; import java.util.HashMap; import java.util.Map; import javax.annotation.Nonnull; import javax.annotation.Nullable; import javax.swing.JPanel; import javax.swing.SwingUtilities; import org.gjt.sp.jedit.EditBus.EBHandler; import org.gjt.sp.jedit.buffer.JEditBuffer; import org.gjt.sp.jedit.bufferset.BufferSet; import org.gjt.sp.jedit.bufferset.BufferSetListener; import org.gjt.sp.jedit.gui.TabbedBufferSwitcher; import org.gjt.sp.jedit.gui.StatusBar; import org.gjt.sp.jedit.msg.BufferChanging; import org.gjt.sp.jedit.msg.BufferUpdate; import org.gjt.sp.jedit.msg.EditPaneUpdate; import org.gjt.sp.jedit.msg.PropertiesChanged; import org.gjt.sp.jedit.options.GeneralOptionPane; import org.gjt.sp.jedit.options.GutterOptionPane; import org.gjt.sp.jedit.syntax.SyntaxStyle; import org.gjt.sp.jedit.textarea.AntiAlias; import org.gjt.sp.jedit.textarea.Gutter; import org.gjt.sp.jedit.textarea.GutterPopupHandler; import org.gjt.sp.jedit.textarea.JEditTextArea; import org.gjt.sp.jedit.textarea.MouseHandler; import org.gjt.sp.jedit.textarea.Selection; import org.gjt.sp.jedit.textarea.StatusListener; import org.gjt.sp.jedit.textarea.TextArea; import org.gjt.sp.jedit.textarea.TextAreaExtension; import org.gjt.sp.jedit.textarea.TextAreaPainter; import org.gjt.sp.jedit.textarea.TextAreaTransferHandler; import org.gjt.sp.util.SyntaxUtilities; import org.gjt.sp.util.ThreadUtilities; //}}} /** * A panel containing a text area.<p> * * In a BeanShell script, you can obtain the current edit pane from the * <code>editPane</code> variable.<p> * * * Each View can have multiple editPanes, one is active at a time. * Each EditPane has a single JEditTextArea, and is operating on single buffer. * The EditPane also can switch buffers. * * This is the "controller" between a JEditTextArea (view) and a buffer (model). * * This class does not have a public constructor. * Edit panes can be created and destroyed using methods in the * {@link View} class.<p> * * * @see View#splitHorizontally() * @see View#splitVertically() * @see View#unsplitCurrent() * @see View#unsplit() * @see View#getEditPane() * @see View#getEditPanes() * * @author Slava Pestov * @version $Id: EditPane.java 23821 2015-01-04 14:31:09Z ezust $ */ public class EditPane extends JPanel implements BufferSetListener { //{{{ getView() method /** * Returns the view containing this edit pane. * @return the view that contains this EditPane * @since jEdit 2.5pre2 */ public View getView() { return view; } //}}} // {{{ get(TextArea) method /** * Returns the EditPane of a TextArea. * * @param ta the textArea * @return the EditPane containing the TextArea. */ public static EditPane get(TextArea ta) { if (ta == null) return null; return (EditPane)SwingUtilities.getAncestorOfClass(EditPane.class, ta); } // }}} //{{{ getBuffer() method /** * Returns the current buffer. * @return the current buffer * @since jEdit 2.5pre2 */ public Buffer getBuffer() { return buffer; } //}}} //{{{ setBuffer() methods /** * Sets the current buffer. * @param buffer The buffer to edit. * @since jEdit 2.5pre2 */ public void setBuffer(@Nonnull Buffer buffer) { setBuffer(buffer, true); } /** * Sets the current buffer. * @param buffer The buffer to edit. * @param requestFocus true if the textarea should request focus, false otherwise * @since jEdit 4.3pre6 */ public void setBuffer(@Nonnull final Buffer buffer, boolean requestFocus) { if(buffer == null) throw new NullPointerException("The buffer cannot be null"); if(this.buffer == buffer) return; if (bufferSet.indexOf(buffer) == -1) { jEdit.getBufferSetManager().addBuffer(this, buffer); } //if(buffer.insideCompoundEdit()) // buffer.endCompoundEdit(); EditBus.send(new BufferChanging(this, buffer)); if (bufferSet.indexOf(this.buffer) != -1) { // when closing the last buffer of a bufferSet, the current buffer will still be the closed // buffer until a new empty buffer is created. // So if the current buffer is not anymore in the bufferSet, do not set the recentBuffer recentBuffer = this.buffer; } if(recentBuffer != null) saveCaretInfo(); this.buffer = buffer; textArea.setBuffer(buffer); if(!init) { view.updateTitle(); if(bufferSwitcher != null) { if(bufferSwitcher.getSelectedItem() != buffer) bufferSwitcher.setSelectedItem(buffer); bufferSwitcher.setToolTipText(buffer.getPath()); } EditBus.send(new EditPaneUpdate(this,EditPaneUpdate .BUFFER_CHANGED)); } if (requestFocus) { SwingUtilities.invokeLater(new Runnable() { public void run() { // only do this if we are the current edit pane if(view.getEditPane() == EditPane.this && (bufferSwitcher == null || !bufferSwitcher.specialState())) { textArea.requestFocus(); } } }); } // If the buffer is loading, the caret info will be loaded on // BufferUpdate.LOADED. Otherwise, we don't need to wait for IO. if (!buffer.isLoading()) { ThreadUtilities.runInDispatchThread(new Runnable() { public void run() { // avoid a race condition // see bug #834338 if(buffer == getBuffer()) loadCaretInfo(); // This must happen after loadCaretInfo. // Otherwise caret is not restored properly. int check = jEdit.getIntegerProperty("checkFileStatus"); if (jEdit.isStartupDone() && (check & GeneralOptionPane.checkFileStatus_focusBuffer) > 0) jEdit.checkBufferStatus(view, true); } }); } } //}}} //{{{ prevBuffer() method /** * Selects the previous buffer. * @since jEdit 2.7pre2 */ public void prevBuffer() { Buffer buffer = bufferSet.getPreviousBuffer(bufferSet.indexOf(this.buffer)); setBuffer(buffer); } //}}} //{{{ nextBuffer() method /** * Selects the next buffer. * @since jEdit 2.7pre2 */ public void nextBuffer() { Buffer buffer = bufferSet.getNextBuffer(bufferSet.indexOf(this.buffer)); setBuffer(buffer); } //}}} //{{{ recentBuffer() method /** * Selects the most recently edited buffer. * @since jEdit 2.7pre2 */ public void recentBuffer() { if(recentBuffer != null) setBuffer(recentBuffer); else getToolkit().beep(); } //}}} //{{{ focusOnTextArea() method /** * Sets the focus onto the text area. * @since jEdit 2.5pre2 */ public void focusOnTextArea() { SwingUtilities.invokeLater(new Runnable() { public void run() { textArea.requestFocus(); } }); } //}}} //{{{ getTextArea() method /** * Returns the view's text area. * @return the text area of the edit pane * @since jEdit 2.5pre2 */ public JEditTextArea getTextArea() { return textArea; } //}}} //{{{ getBufferSwitcher() method /** * Returns the buffer switcher combo box instance. * @return the buffer switcher (it can be null) * @since jEdit 4.1pre8 */ public TabbedBufferSwitcher getBufferSwitcher() { return bufferSwitcher; } //}}} //{{{ focusBufferSwitcher() method /** * Pops up and focuses on the buffer switcher combo box. * @since jEdit 4.3pre18 * (previously known as showBufferSwitcher) */ public void focusBufferSwitcher() { if(bufferSwitcher == null) getToolkit().beep(); else { SwingUtilities.invokeLater(new Runnable() { public void run() { bufferSwitcher.requestFocus(); bufferSwitcher.showPopup(); } }); } } //}}} //{{{ saveCaretInfo() method /** * Saves the caret information to the current buffer. * @since jEdit 2.5pre2 */ public void saveCaretInfo() { if(!buffer.isLoaded()) return; buffer.setIntegerProperty(Buffer.CARET, textArea.getCaretPosition()); CaretInfo caretInfo = caretsForPath.get(buffer.getPath()); if (caretInfo == null) { caretInfo = new CaretInfo(); caretsForPath.put(buffer.getPath(), caretInfo); } caretInfo.caret = textArea.getCaretPosition(); Selection[] selection = textArea.getSelection(); for(int i = 0; i < selection.length; i++) selection[i] = (Selection)selection[i].clone(); buffer.setProperty(Buffer.SELECTION,selection); caretInfo.selection = selection; caretInfo.rectangularSelection = textArea.isRectangularSelectionEnabled(); caretInfo.multipleSelection = textArea.isMultipleSelectionEnabled(); buffer.setIntegerProperty(Buffer.SCROLL_VERT, textArea.getFirstPhysicalLine()); caretInfo.scrollVert = textArea.getFirstPhysicalLine(); buffer.setIntegerProperty(Buffer.SCROLL_HORIZ, textArea.getHorizontalOffset()); caretInfo.scrollHoriz = textArea.getHorizontalOffset(); if (!buffer.isUntitled()) { BufferHistory.setEntry(buffer.getPath(), textArea.getCaretPosition(), (Selection[])buffer.getProperty(Buffer.SELECTION), buffer.getStringProperty(JEditBuffer.ENCODING), buffer.getMode().getName()); } } //}}} //{{{ loadCaretInfo() method /** * Loads the caret and selection information from this EditPane, fall * back to the information from the current buffer if none is already * in this EditPane. * @since jEdit 2.5pre2 */ public void loadCaretInfo() { // get our internal map of buffer -> CaretInfo since there might // be current info already CaretInfo caretInfo = caretsForPath.get(buffer.getPath()); if (caretInfo == null) { caretInfo = new CaretInfo(); } // set the position of the caret itself. // Caret position could be stored in the internal map already, // if so, use that one first. Otherwise, fall back to any // previously saved caret position that was stored in the // buffer properties. int caret = caretInfo.caret; if (caret == -1 || buffer.getBooleanProperty(Buffer.CARET_POSITIONED)) { Integer i = (Integer) buffer.getProperty(Buffer.CARET); caret = i == null ? -1 : i; } buffer.unsetProperty(Buffer.CARET_POSITIONED); if(caret != -1) textArea.setCaretPosition(Math.min(caret, buffer.getLength())); // set any selections Selection[] selection = caretInfo.selection; if ( selection == null ) { selection = (Selection[]) buffer.getProperty(Buffer.SELECTION); } if(selection != null) { for(int i = 0; i < selection.length; i++) { Selection s = selection[i]; int max = buffer.getLength(); if(s.getStart() > max || s.getEnd() > max) selection[i] = null; } } textArea.setSelection(selection); textArea.setRectangularSelectionEnabled(caretInfo.rectangularSelection); textArea.setMultipleSelectionEnabled(caretInfo.multipleSelection); // set firstLine value int firstLine = caretInfo.scrollVert; if ( firstLine == -1 ) { Integer i = (Integer) buffer.getProperty(Buffer.SCROLL_VERT); firstLine = i == null ? -1 : i; } if(firstLine != -1) textArea.setFirstPhysicalLine(firstLine); // set horizontal offset int horizontalOffset = caretInfo.scrollHoriz; if (horizontalOffset == -1) { Integer i = (Integer) buffer.getProperty(Buffer.SCROLL_HORIZ); horizontalOffset = i == null ? -1 : i; } if(horizontalOffset != -1) textArea.setHorizontalOffset(horizontalOffset); /* Silly bug workaround #8694. If you look at the above code, * note that we restore the saved caret position first, then * scroll to the saved location. However, the caret changing * can itself result in scrolling to a different location than * what was saved; and since moveCaretPosition() calls * updateBracketHighlight(), the bracket highlight's out of * bounds calculation will rely on a different set of physical * first/last lines than what we will end up with eventually. * Instead of confusing the user with status messages that * appear at random when switching buffers, we simply hide the * message altogether. */ view.getStatus().setMessage(null); } //}}} //{{{ bufferRenamed() method /** * This method should be called by the Buffer when the path is changing. * @param oldPath the old path of the buffer * @param newPath the new path of the buffer */ void bufferRenamed(String oldPath, String newPath) { CaretInfo caretInfo = caretsForPath.remove(oldPath); if (caretInfo != null) caretsForPath.put(newPath, caretInfo); } //}}} //{{{ CaretInfo class /** * Need to track this info for each buffer that this EditPane might edit * since a buffer may be open in more than one EditPane at a time. That * means we need to track this info at the EditPane level rather than * the buffer level. */ private static class CaretInfo { public int caret = -1; public Selection[] selection; public int scrollVert = -1; public int scrollHoriz = -1; public boolean rectangularSelection; public boolean multipleSelection; } //}}} //{{{ goToNextMarker() method /** * Moves the caret to the next marker. * @since jEdit 4.3pre3 */ public void goToNextMarker(boolean select) { java.util.List<Marker> markers = buffer.getMarkers(); if(markers.isEmpty()) { getToolkit().beep(); return; } Marker marker = null; int caret = textArea.getCaretPosition(); for (Marker _marker : markers) { if (_marker.getPosition() > caret) { marker = _marker; break; } } // the markers list is not empty at this point if(marker == null) marker = markers.get(0); if(select) textArea.extendSelection(caret,marker.getPosition()); else if(!textArea.isMultipleSelectionEnabled()) textArea.selectNone(); textArea.moveCaretPosition(marker.getPosition()); } //}}} //{{{ goToPrevMarker() method /** * Moves the caret to the previous marker. * @since jEdit 2.7pre2 */ public void goToPrevMarker(boolean select) { java.util.List<Marker> markers = buffer.getMarkers(); if(markers.isEmpty()) { getToolkit().beep(); return; } int caret = textArea.getCaretPosition(); Marker marker = null; for(int i = markers.size() - 1; i >= 0; i--) { Marker _marker = markers.get(i); if(_marker.getPosition() < caret) { marker = _marker; break; } } if(marker == null) marker = markers.get(markers.size() - 1); if(select) textArea.extendSelection(caret,marker.getPosition()); else if(!textArea.isMultipleSelectionEnabled()) textArea.selectNone(); textArea.moveCaretPosition(marker.getPosition()); } //}}} //{{{ goToMarker() method /** * Moves the caret to the marker with the specified shortcut. * @param shortcut The shortcut * @param select True if the selection should be extended, * false otherwise * @since jEdit 3.2pre2 */ public void goToMarker(char shortcut, boolean select) { Marker marker = buffer.getMarker(shortcut); if(marker == null) { getToolkit().beep(); return; } int pos = marker.getPosition(); if(select) textArea.extendSelection(textArea.getCaretPosition(),pos); else if(!textArea.isMultipleSelectionEnabled()) textArea.selectNone(); textArea.moveCaretPosition(pos); } //}}} //{{{ addMarker() method /** * Adds a marker at the caret position. * @since jEdit 3.2pre1 */ public void addMarker() { int caretLine = textArea.getCaretLine(); // always add markers on selected lines Selection[] selection = textArea.getSelection(); for (Selection s : selection) { int startLine = s.getStartLine(); if (startLine != s.getEndLine() && startLine != caretLine) { buffer.addMarker('\0', s.getStart()); } if (s.getEndLine() != caretLine) buffer.addMarker('\0', s.getEnd()); } // toggle marker on caret line buffer.addOrRemoveMarker('\0',textArea.getCaretPosition()); } //}}} //{{{ swapMarkerAndCaret() method /** * Moves the caret to the marker with the specified shortcut, * then sets the marker position to the former caret position. * @param shortcut The shortcut * @since jEdit 3.2pre2 */ public void swapMarkerAndCaret(char shortcut) { Marker marker = buffer.getMarker(shortcut); if(marker == null) { getToolkit().beep(); return; } int caret = textArea.getCaretPosition(); textArea.setCaretPosition(marker.getPosition()); buffer.addMarker(shortcut,caret); } //}}} //{{{ handlePropertiesChanged() method @EBHandler public void handlePropertiesChanged(PropertiesChanged msg) { propertiesChanged(); loadBufferSwitcher(); } //}}} //{{{ getMinimumSize() method /** * Returns 0,0 for split pane compatibility. */ @Override public final Dimension getMinimumSize() { return new Dimension(0,0); } //}}} //{{{ getBufferSet() method /** * Returns the current buffer set. * This can be changed by setBufferSetScope(). * @return the buffer set which is currently used by this EditPane * @since jEdit 4.3pre17 */ public BufferSet getBufferSet() { return bufferSet; } //}}} //{{{ bufferAdded() method /** * A buffer was added in the bufferSet. * @param buffer the added buffer * @param index the position where it was added * @since jEdit 4.3pre15 */ public void bufferAdded(Buffer buffer, int index) { if (buffer == null) return; if (bufferSwitcher != null) bufferSwitcher.updateBufferList(); if (bufferSet.indexOf(this.buffer) == -1) { // it happens when having 1 untitled buffer if I open a file. The untitled buffer // is closed but the new buffer is not yet opened setBuffer(buffer); } } //}}} //{{{ bufferRemoved() method /** * A buffer was removed from the bufferSet. * @param buffer the removed buffer * @param index the position where it was before being removed * @since jEdit 4.3pre15 */ public void bufferRemoved(Buffer buffer, int index) { if (buffer.isUntitled()) { // the buffer was a new file so I do not need to keep it's informations caretsForPath.remove(buffer.getPath()); } if (buffer == this.buffer) { // The closed buffer is the current buffer Buffer newBuffer = recentBuffer != null ? recentBuffer : bufferSet.getPreviousBuffer(index); if(newBuffer != null && !newBuffer.isClosed()) { setBuffer(newBuffer); if (bufferSet.size() > 1) { recentBuffer = bufferSet.getPreviousBuffer(index -1); } } else if(bufferSet.size() != 0) { setBuffer(bufferSet.getBuffer(0)); recentBuffer = null; } } if(buffer == recentBuffer) recentBuffer = null; if (bufferSwitcher != null) bufferSwitcher.updateBufferList(); } //}}} //{{{ bufferMoved() method /** * A buffer was moved in the BufferSet. * @param buffer the moved buffer * @param oldIndex the position it was before * @param newIndex the new position * @since jEdit 4.3pre15 */ public void bufferMoved(Buffer buffer, int oldIndex, int newIndex) { if (bufferSwitcher != null) bufferSwitcher.updateBufferList(); } //}}} //{{{ bufferSetSorted() method /** * The bufferSet was sorted * @since jEdit 4.3pre16 */ public void bufferSetSorted() { if (bufferSwitcher != null) bufferSwitcher.updateBufferList(); } //}}} //{{{ toString() method @Override public String toString() { return getClass().getName() + '[' + (view.getEditPane() == this ? "active]" : "inactive]"); } //}}} //{{{ Package-private members //{{{ EditPane constructor EditPane(@Nonnull View view, @Nullable BufferSet bufferSetSource, @Nonnull Buffer buffer) { super(new BorderLayout()); BufferSet.Scope scope = jEdit.getBufferSetManager().getScope(); BufferSet source = bufferSetSource; switch (scope) { case editpane: // do nothing break; case view: { EditPane editPane = view.getEditPane(); if (editPane != null) { // if we have an editpane we copy it source = editPane.getBufferSet(); } } break; case global: View activeView = jEdit.getActiveView(); if (activeView != null) { EditPane editPane = activeView.getEditPane(); if (editPane != null) { source = editPane.getBufferSet(); } } break; } bufferSet = new BufferSet(source); init = true; this.view = view; textArea = new JEditTextArea(view); bufferSet.addBufferSetListener(this); textArea.getPainter().setAntiAlias(new AntiAlias(jEdit.getProperty("view.antiAlias"))); textArea.setMouseHandler(new MouseHandler(textArea)); textArea.setTransferHandler(new TextAreaTransferHandler()); markerHighlight = new MarkerHighlight(); Gutter gutter = textArea.getGutter(); gutter.setGutterEnabled(GutterOptionPane.isGutterEnabled()); gutter.setMinLineNumberDigitCount(GutterOptionPane.getMinLineNumberDigits()); gutter.setSelectionAreaEnabled(GutterOptionPane.isSelectionAreaEnabled()); gutter.addExtension(markerHighlight); gutter.setSelectionPopupHandler( new GutterPopupHandler() { public void handlePopup(int x, int y, int line) { Buffer buffer = getBuffer(); buffer.addOrRemoveMarker('\0', buffer.getLineStartOffset(line)); } }); textArea.addStatusListener(new StatusHandler()); add(BorderLayout.CENTER,textArea); propertiesChanged(); setBuffer(buffer); // need to add the buffer to the bufferSet. // It may not have been done by the setBuffer() because the EditPane is not yet known by jEdit, and for // view and global scope it is added through this list if (bufferSet.indexOf(buffer) == -1) bufferSet.addBuffer(buffer); loadBufferSwitcher(); init = false; EditBus.addToBus(this); } //}}} //{{{ close() method void close() { saveCaretInfo(); EditBus.send(new EditPaneUpdate(this,EditPaneUpdate.DESTROYED)); EditBus.removeFromBus(this); textArea.dispose(); } //}}} //}}} //{{{ Private members //{{{ Instance variables private boolean init; /** The View where the edit pane is. */ private final View view; private final BufferSet bufferSet; /** The current buffer. */ private Buffer buffer; private Buffer recentBuffer; private TabbedBufferSwitcher bufferSwitcher; /** The textArea inside the edit pane. */ private final JEditTextArea textArea; private final MarkerHighlight markerHighlight; // A map of buffer.getPath() -> CaretInfo. This is necessary for // when the same buffer is open in more than one EditPane and the user // is switching between buffers. We want to keep the caret in the // right position in each EditPane, which won't be the case if we // just use the buffer caret property. private final Map<String, CaretInfo> caretsForPath = new HashMap<String, CaretInfo>(); //}}} //{{{ propertiesChanged() method private void propertiesChanged() { TextAreaPainter painter = textArea.getPainter(); initPainter(painter); Gutter gutter = textArea.getGutter(); gutter.setExpanded(jEdit.getBooleanProperty( "view.gutter.lineNumbers")); int interval = jEdit.getIntegerProperty( "view.gutter.highlightInterval",5); gutter.setHighlightInterval(interval); gutter.setCurrentLineHighlightEnabled(jEdit.getBooleanProperty( "view.gutter.highlightCurrentLine")); gutter.setStructureHighlightEnabled(jEdit.getBooleanProperty( "view.gutter.structureHighlight")); gutter.setStructureHighlightColor( jEdit.getColorProperty("view.gutter.structureHighlightColor")); Color backgroundColor = jEdit.getColorProperty("view.gutter.bgColor"); gutter.setBackground(backgroundColor); textArea.setBackground(backgroundColor); gutter.setForeground( jEdit.getColorProperty("view.gutter.fgColor")); gutter.setHighlightedForeground( jEdit.getColorProperty("view.gutter.highlightColor")); gutter.setFoldColor( jEdit.getColorProperty("view.gutter.foldColor")); markerHighlight.setMarkerHighlightColor( jEdit.getColorProperty("view.gutter.markerColor")); markerHighlight.setMarkerHighlightEnabled(jEdit.getBooleanProperty( "view.gutter.markerHighlight")); gutter.setCurrentLineForeground( jEdit.getColorProperty("view.gutter.currentLineColor")); String alignment = jEdit.getProperty( "view.gutter.numberAlignment"); if ("right".equals(alignment)) { gutter.setLineNumberAlignment(Gutter.RIGHT); } else if ("center".equals(alignment)) { gutter.setLineNumberAlignment(Gutter.CENTER); } else // left == default case { gutter.setLineNumberAlignment(Gutter.LEFT); } gutter.setFont(jEdit.getFontProperty("view.gutter.font")); gutter.setGutterEnabled(GutterOptionPane.isGutterEnabled()); gutter.setMinLineNumberDigitCount( GutterOptionPane.getMinLineNumberDigits()); gutter.setSelectionAreaEnabled( GutterOptionPane.isSelectionAreaEnabled()); gutter.setSelectionAreaBackground( GutterOptionPane.getSelectionAreaBackground()); gutter.setSelectionAreaWidth( GutterOptionPane.getSelectionAreaWidth()); int width = jEdit.getIntegerProperty( "view.gutter.borderWidth",3); gutter.setBorder(width, jEdit.getColorProperty("view.gutter.focusBorderColor"), jEdit.getColorProperty("view.gutter.noFocusBorderColor"), textArea.getPainter().getBackground()); gutter.setFoldPainter(textArea.getFoldPainter()); textArea.setCaretBlinkEnabled(jEdit.getBooleanProperty( "view.caretBlink")); textArea.setElectricScroll(jEdit.getIntegerProperty( "view.electricBorders",0)); // Set up the right-click popup menu textArea.createPopupMenu(null); // use old property name for backwards compatibility textArea.setQuickCopyEnabled(jEdit.getBooleanProperty( "view.middleMousePaste")); textArea.setDragEnabled(jEdit.getBooleanProperty( "view.dragAndDrop")); textArea.setJoinNonWordChars(jEdit.getBooleanProperty( "view.joinNonWordChars")); textArea.setCtrlForRectangularSelection(jEdit.getBooleanProperty( "view.ctrlForRectangularSelection")); textArea.propertiesChanged(); if (bufferSwitcher != null) { bufferSwitcher.setMaximumRowCount(jEdit.getIntegerProperty( "bufferSwitcher.maxRowCount",10)); } } //}}} //{{{ initPainter() method /** * Init the painter of a textarea. * * @param painter the painter of a textarea * @since jEdit 4.3pre12 */ public static void initPainter(TextAreaPainter painter) { painter.setFont(jEdit.getFontProperty("view.font")); painter.setStructureHighlightEnabled(jEdit.getBooleanProperty( "view.structureHighlight")); painter.setStructureHighlightColor( jEdit.getColorProperty("view.structureHighlightColor")); painter.setEOLMarkersPainted(jEdit.getBooleanProperty( "view.eolMarkers")); painter.setEOLMarkerChar( jEdit.getProperty("view.eolMarkerChar", "ยท")); painter.setEOLMarkerColor( jEdit.getColorProperty("view.eolMarkerColor")); painter.setWrapGuidePainted(jEdit.getBooleanProperty( "view.wrapGuide")); painter.setWrapGuideColor( jEdit.getColorProperty("view.wrapGuideColor")); painter.setCaretColor( jEdit.getColorProperty("view.caretColor")); painter.setSelectionColor( jEdit.getColorProperty("view.selectionColor")); painter.setMultipleSelectionColor( jEdit.getColorProperty("view.multipleSelectionColor")); painter.setBackground( jEdit.getColorProperty("view.bgColor")); painter.setForeground( jEdit.getColorProperty("view.fgColor")); painter.setBlockCaretEnabled(jEdit.getBooleanProperty( "view.blockCaret")); painter.setThickCaretEnabled(jEdit.getBooleanProperty( "view.thickCaret")); painter.setLineHighlightEnabled(jEdit.getBooleanProperty( "view.lineHighlight")); painter.setLineHighlightColor( jEdit.getColorProperty("view.lineHighlightColor")); painter.setAntiAlias(new AntiAlias(jEdit.getProperty("view.antiAlias"))); painter.setFractionalFontMetricsEnabled(jEdit.getBooleanProperty( "view.fracFontMetrics")); painter.setSelectionFgColor(jEdit.getColorProperty( "view.selectionFgColor")); painter.setSelectionFgColorEnabled(jEdit.getBooleanProperty( "view.selectionFg")); String defaultFont = jEdit.getProperty("view.font"); int defaultFontSize = jEdit.getIntegerProperty("view.fontsize",12); painter.setStyles(SyntaxUtilities.loadStyles(defaultFont,defaultFontSize)); SyntaxStyle[] foldLineStyle = new SyntaxStyle[4]; for(int i = 0; i <= 3; i++) { foldLineStyle[i] = GUIUtilities.parseStyle( jEdit.getProperty("view.style.foldLine." + i), defaultFont,defaultFontSize); } painter.setFoldLineStyle(foldLineStyle); } //}}} //{{{ loadBufferSwitcher() method void loadBufferSwitcher() { if(jEdit.getBooleanProperty("view.showBufferSwitcher")) { if(bufferSwitcher == null) { bufferSwitcher = new TabbedBufferSwitcher(this); add(BorderLayout.NORTH,bufferSwitcher); bufferSwitcher.updateBufferList(); revalidate(); } } else if(bufferSwitcher != null) { remove(bufferSwitcher); revalidate(); bufferSwitcher = null; } } //}}} //{{{ handleBufferUpdate() method @EBHandler public void handleBufferUpdate(BufferUpdate msg) { Buffer _buffer = msg.getBuffer(); if(msg.getWhat() == BufferUpdate.CREATED) { if(bufferSwitcher != null) bufferSwitcher.updateBufferList(); /* When closing the last buffer, the BufferUpdate.CLOSED * handler doesn't call setBuffer(), because null buffers * are not supported. Instead, it waits for the subsequent * 'Untitled' file creation. */ if(buffer.isClosed()) { // since recentBuffer will be set to the one that // was closed recentBuffer = null; } } else if(msg.getWhat() == BufferUpdate.CLOSED) { if(bufferSwitcher != null) bufferSwitcher.updateBufferList(); if(_buffer == buffer) { // The closed buffer is the current buffer Buffer newBuffer = recentBuffer != null ? recentBuffer : _buffer.getPrev(); if(newBuffer != null && !newBuffer.isClosed()) { setBuffer(newBuffer); recentBuffer = newBuffer.getPrev(); } } else if(_buffer == recentBuffer) recentBuffer = null; Buffer closedBuffer = msg.getBuffer(); if (closedBuffer.isUntitled()) { // the buffer was a new file so I do not need to keep it's informations caretsForPath.remove(closedBuffer.getPath()); } } else if(msg.getWhat() == BufferUpdate.LOAD_STARTED) { if(_buffer == buffer) { textArea.setCaretPosition(0); textArea.getPainter().repaint(); } } else if(msg.getWhat() == BufferUpdate.LOADED) { if(_buffer == buffer) { textArea.repaint(); if(bufferSwitcher != null) bufferSwitcher.updateBufferList(); if(view.getEditPane() == this) { StatusBar status = view.getStatus(); status.updateCaretStatus(); status.updateBufferStatus(); status.updateMiscStatus(); } loadCaretInfo(); } } else if(msg.getWhat() == BufferUpdate.DIRTY_CHANGED) { if(_buffer == buffer && bufferSwitcher != null) { if(buffer.isDirty()) bufferSwitcher.repaint(); else bufferSwitcher.updateBufferList(); } } else if(msg.getWhat() == BufferUpdate.MARKERS_CHANGED) { if(_buffer == buffer) textArea.getGutter().repaint(); } else if(msg.getWhat() == BufferUpdate.PROPERTIES_CHANGED) { if(_buffer == buffer && buffer.isLoaded()) { textArea.propertiesChanged(); if(view.getEditPane() == this) view.getStatus().updateBufferStatus(); } } else if(msg.getWhat() == BufferUpdate.SAVED && _buffer == buffer) { textArea.propertiesChanged(); } } //}}} //}}} //{{{ StatusHandler class class StatusHandler implements StatusListener { public void statusChanged(org.gjt.sp.jedit.textarea.TextArea textArea, int flag, boolean value) { StatusBar status = view.getStatus(); if(status == null) return; switch(flag) { case OVERWRITE_CHANGED: status.setMessageAndClear( jEdit.getProperty("view.status.overwrite-changed", new Integer[] { value ? 1 : 0 })); break; case MULTI_SELECT_CHANGED: status.setMessageAndClear( jEdit.getProperty("view.status.multi-changed", new Integer[] { value ? 1 : 0 })); break; case RECT_SELECT_CHANGED: status.setMessageAndClear( jEdit.getProperty("view.status.rect-select-changed", new Integer[] { value ? 1 : 0 })); break; } status.updateMiscStatus(); } public void bracketSelected(org.gjt.sp.jedit.textarea.TextArea textArea, int line, String text) { StatusBar status = view.getStatus(); if(status == null) return; status.setMessageAndClear(jEdit.getProperty( "view.status.bracket",new Object[] { line, text })); } public void narrowActive(org.gjt.sp.jedit.textarea.TextArea textArea) { StatusBar status = view.getStatus(); if(status == null) return; status.setMessageAndClear( jEdit.getProperty("view.status.narrow")); } } //}}} //{{{ MarkerHighlight class class MarkerHighlight extends TextAreaExtension { private boolean markerHighlight; private Color markerHighlightColor; //{{{ getMarkerHighlightColor() method public Color getMarkerHighlightColor() { return markerHighlightColor; } //}}} //{{{ setMarkerHighlightColor() method public void setMarkerHighlightColor(Color markerHighlightColor) { this.markerHighlightColor = markerHighlightColor; } //}}} //{{{ isMarkerHighlightEnabled() method public boolean isMarkerHighlightEnabled() { return markerHighlight; } //}}} //{{{ isMarkerHighlightEnabled() public void setMarkerHighlightEnabled(boolean markerHighlight) { this.markerHighlight = markerHighlight; } //}}} //{{{ paintValidLine() method @Override public void paintValidLine(Graphics2D gfx, int screenLine, int physicalLine, int start, int end, int y) { if(isMarkerHighlightEnabled()) { Buffer buffer = (Buffer)textArea.getBuffer(); if(buffer.getMarkerInRange(start,end) != null) { gfx.setColor(getMarkerHighlightColor()); int height = textArea.getPainter().getLineHeight(); gfx.fillRect(0, y, textArea.getGutter().getWidth(), height); } } } //}}} //{{{ getToolTipText() method @Override public String getToolTipText(int x, int y) { if(isMarkerHighlightEnabled()) { int lineHeight = textArea.getPainter().getLineHeight(); if(lineHeight == 0) return null; int line = y / lineHeight; int start = textArea.getScreenLineStartOffset(line); int end = textArea.getScreenLineEndOffset(line); if(start == -1 || end == -1) return null; Buffer buffer = (Buffer)textArea.getBuffer(); Marker marker = buffer.getMarkerInRange(start,end); if(marker != null) { char shortcut = marker.getShortcut(); if(shortcut == '\0') return jEdit.getProperty("view.gutter.marker.no-name"); else { String[] args = { String.valueOf(shortcut) }; return jEdit.getProperty("view.gutter.marker",args); } } } return null; } //}}} } //}}} }