/* * Gutter.java * :tabSize=4:indentSize=4:noTabs=false: * :folding=explicit:collapseFolds=1: * * Copyright (C) 1999, 2000 mike dillon * Portions copyright (C) 2001, 2002 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.textarea; //{{{ Imports import java.awt.*; import java.awt.event.*; import javax.swing.*; import javax.swing.border.*; import javax.swing.event.*; import org.gjt.sp.jedit.Registers; import org.gjt.sp.jedit.buffer.BufferAdapter; import org.gjt.sp.jedit.buffer.BufferListener; import org.gjt.sp.jedit.buffer.JEditBuffer; import org.gjt.sp.util.Log; //}}} /** * The gutter is the component that displays folding indicators and line * numbers to the left of the text area. The only methods in this class * that should be called by plugins are those for adding and removing * text area extensions. * * @see #addExtension(TextAreaExtension) * @see #addExtension(int,TextAreaExtension) * @see #removeExtension(TextAreaExtension) * @see TextAreaExtension * @see TextArea * * @author Mike Dillon and Slava Pestov * @version $Id: Gutter.java 23461 2014-04-06 18:00:29Z ezust $ */ public class Gutter extends JComponent implements SwingConstants { //{{{ Layers /** * The lowest possible layer. * @see #addExtension(int,TextAreaExtension) * @since jEdit 4.0pre4 */ public static final int LOWEST_LAYER = Integer.MIN_VALUE; /** * Default extension layer. This is above the wrap guide but below the * bracket highlight. * @since jEdit 4.0pre4 */ public static final int DEFAULT_LAYER = 0; /** * Highest possible layer. * @since jEdit 4.0pre4 */ public static final int HIGHEST_LAYER = Integer.MAX_VALUE; //}}} //{{{ Fold painters /** * Fold painter service. * @since jEdit 4.3pre16 */ public static final String FOLD_PAINTER_PROPERTY = "foldPainter"; public static final String FOLD_PAINTER_SERVICE = "org.gjt.sp.jedit.textarea.FoldPainter"; public static final String DEFAULT_FOLD_PAINTER_SERVICE = "Triangle"; //{{{ setFolderPainter() method public void setFoldPainter(FoldPainter painter) { if (painter == null) foldPainter = new TriangleFoldPainter(); else foldPainter = painter; } //}}} //}}} Fold painters //{{{ Gutter constructor public Gutter(TextArea textArea) { this.textArea = textArea; enabled = true; selectionAreaEnabled = true; selectionAreaWidth = SELECTION_GUTTER_WIDTH; setAutoscrolls(true); setOpaque(true); setRequestFocusEnabled(false); extensionMgr = new ExtensionManager(); mouseHandler = new MouseHandler(); addMouseListener(mouseHandler); addMouseMotionListener(mouseHandler); bufferListener = new BufferAdapter() { public void bufferLoaded(JEditBuffer buffer) { updateLineNumberWidth(); } public void contentInserted(JEditBuffer buffer, int startLine, int offset, int numLines, int length) { if (numLines != 0) updateLineNumberWidth(); } public void contentRemoved(JEditBuffer buffer, int startLine, int offset, int numLines, int length) { if (numLines != 0) updateLineNumberWidth(); } }; updateBorder(); setFoldPainter(textArea.getFoldPainter()); } //}}} //{{{ paintComponent() method public void paintComponent(Graphics _gfx) { Graphics2D gfx = (Graphics2D)_gfx; gfx.setRenderingHints(textArea.getPainter().renderingHints); // fill the background Rectangle clip = gfx.getClipBounds(); gfx.setColor(getBackground()); int bgColorWidth = isSelectionAreaEnabled() ? FOLD_MARKER_SIZE : clip.width; gfx.fillRect(clip.x, clip.y, bgColorWidth, clip.height); if (isSelectionAreaEnabled()) { if (selectionAreaBgColor == null) selectionAreaBgColor = getBackground(); gfx.setColor(selectionAreaBgColor); gfx.fillRect(clip.x + FOLD_MARKER_SIZE, clip.y, clip.width - FOLD_MARKER_SIZE, clip.height); } // if buffer is loading, don't paint anything if (textArea.getBuffer().isLoading()) return; int lineHeight = textArea.getPainter().getLineHeight(); if(lineHeight == 0) return; int firstLine = clip.y / lineHeight; int lastLine = (clip.y + clip.height - 1) / lineHeight; if(lastLine - firstLine > textArea.getVisibleLines()) { Log.log(Log.ERROR,this,"BUG: firstLine=" + firstLine); Log.log(Log.ERROR,this," lastLine=" + lastLine); Log.log(Log.ERROR,this," visibleLines=" + textArea.getVisibleLines()); Log.log(Log.ERROR,this," height=" + getHeight()); Log.log(Log.ERROR,this," painter.height=" + lineHeight); Log.log(Log.ERROR,this," clip.y=" + clip.y); Log.log(Log.ERROR,this," clip.height=" + clip.height); Log.log(Log.ERROR,this," lineHeight=" + lineHeight); } int y = clip.y - clip.y % lineHeight; extensionMgr.paintScreenLineRange(textArea,gfx, firstLine,lastLine,y,lineHeight); for (int line = firstLine; line <= lastLine; line++, y += lineHeight) { paintLine(gfx,line,y); } } //}}} //{{{ addExtension() method /** * Adds a text area extension, which can perform custom painting and * tool tip handling. * @param extension The extension * @since jEdit 4.0pre4 */ public void addExtension(TextAreaExtension extension) { extensionMgr.addExtension(DEFAULT_LAYER,extension); repaint(); } //}}} //{{{ addExtension() method /** * Adds a text area extension, which can perform custom painting and * tool tip handling. * @param layer The layer to add the extension to. Note that more than * extension can share the same layer. * @param extension The extension * @since jEdit 4.0pre4 */ public void addExtension(int layer, TextAreaExtension extension) { extensionMgr.addExtension(layer,extension); repaint(); } //}}} //{{{ removeExtension() method /** * Removes a text area extension. It will no longer be asked to * perform custom painting and tool tip handling. * @param extension The extension * @since jEdit 4.0pre4 */ public void removeExtension(TextAreaExtension extension) { extensionMgr.removeExtension(extension); repaint(); } //}}} //{{{ getExtensions() method /** * Returns an array of registered text area extensions. Useful for * debugging purposes. * @since jEdit 4.1pre5 */ public TextAreaExtension[] getExtensions() { return extensionMgr.getExtensions(); } //}}} //{{{ getToolTipText() method /** * Returns the tool tip to display at the specified location. * @param evt The mouse event */ public String getToolTipText(MouseEvent evt) { if(textArea.getBuffer().isLoading()) return null; return extensionMgr.getToolTipText(evt.getX(),evt.getY()); } //}}} //{{{ setBorder() method /** * Convenience method for setting a default matte border on the right * with the specified border width and color * @param width The border width (in pixels) * @param color1 The focused border color * @param color2 The unfocused border color * @param color3 The gutter/text area gap color */ public void setBorder(int width, Color color1, Color color2, Color color3) { borderWidth = width; focusBorder = new CompoundBorder(new MatteBorder(0,0,0,width,color3), new MatteBorder(0,0,0,width,color1)); noFocusBorder = new CompoundBorder(new MatteBorder(0,0,0,width,color3), new MatteBorder(0,0,0,width,color2)); updateBorder(); } //}}} //{{{ updateBorder() method /** * Sets the border differently if the text area has focus or not. */ public void updateBorder() { if (textArea.hasFocus()) setBorder(focusBorder); else setBorder(noFocusBorder); } //}}} //{{{ setBorder() method /* * JComponent.setBorder(Border) is overridden here to cache the left * inset of the border (if any) to avoid having to fetch it during every * repaint. */ public void setBorder(Border border) { super.setBorder(border); if (border == null) { collapsedSize.width = 0; collapsedSize.height = 0; } else { Insets insets = border.getBorderInsets(this); collapsedSize.width = FOLD_MARKER_SIZE + insets.right; if (isSelectionAreaEnabled()) collapsedSize.width += selectionAreaWidth; collapsedSize.height = gutterSize.height = insets.top + insets.bottom; lineNumberWidth = fm.charWidth('5') * getLineNumberDigitCount(); gutterSize.width = FOLD_MARKER_SIZE + insets.right + lineNumberWidth; } revalidate(); } //}}} //{{{ setMinLineNumberDigitCount() method public void setMinLineNumberDigitCount(int min) { if (min == minLineNumberDigits) return; minLineNumberDigits = min; if (textArea.getBuffer() != null) updateLineNumberWidth(); } //}}} //{{{ getMinLineNumberDigitCount() method private int getMinLineNumberDigitCount() { return minLineNumberDigits; } //}}} //{{{ getLineNumberDigitCount() method private int getLineNumberDigitCount() { JEditBuffer buf = textArea.getBuffer(); int minDigits = getMinLineNumberDigitCount(); if (buf == null) return minDigits; int count = buf.getLineCount(); int digits; for (digits = 0; count > 0; digits++) count /= 10; return (digits < minDigits) ? minDigits : digits; } //}}} //{{{ setBuffer() method void setBuffer(JEditBuffer newBuffer) { if (buffer != null) buffer.removeBufferListener(bufferListener); buffer = newBuffer; if (buffer != null) buffer.addBufferListener(bufferListener); updateLineNumberWidth(); } //}}} //{{{ updateLineNumberWidth() method private void updateLineNumberWidth() { Font f = getFont(); if (f != null) setFont(getFont()); } //}}} //{{{ dispose() method void dispose() { if (buffer != null) { buffer.removeBufferListener(bufferListener); buffer = null; } } //}}} //{{{ setFont() method /* * JComponent.setFont(Font) is overridden here to cache the font * metrics for the font. This avoids having to get the font metrics * during every repaint. */ public void setFont(Font font) { super.setFont(font); fm = getFontMetrics(font); Border border = getBorder(); if(border != null) { lineNumberWidth = fm.charWidth('5') * getLineNumberDigitCount(); gutterSize.width = FOLD_MARKER_SIZE + border.getBorderInsets(this).right + lineNumberWidth; revalidate(); } } //}}} //{{{ Getters and setters //{{{ setGutterEnabled() method /* Enables showing or hiding the gutter. */ public void setGutterEnabled(boolean enabled) { this.enabled = enabled; revalidate(); } //}}} //{{{ isSelectionAreaEnabled() method public boolean isSelectionAreaEnabled() { return selectionAreaEnabled; } //}}} //{{{ setSelectionAreaEnabled() method public void setSelectionAreaEnabled(boolean enabled) { if (isSelectionAreaEnabled() == enabled) return; selectionAreaEnabled = enabled; if (enabled) collapsedSize.width += selectionAreaWidth; else collapsedSize.width -= selectionAreaWidth; revalidate(); } //}}} //{{{ setSelectionAreaBackground() method public void setSelectionAreaBackground(Color bgColor) { selectionAreaBgColor = bgColor; repaint(); } //}}} //{{{ setSelectionAreaWidth() method public void setSelectionAreaWidth(int width) { selectionAreaWidth = width; revalidate(); } //}}} //{{{ getHighlightedForeground() method /** * Get the foreground color for highlighted line numbers * @return The highlight color */ public Color getHighlightedForeground() { return intervalHighlight; } //}}} //{{{ setHighlightedForeground() method public void setHighlightedForeground(Color highlight) { intervalHighlight = highlight; } //}}} //{{{ getCurrentLineForeground() method public Color getCurrentLineForeground() { return currentLineHighlight; } //}}} //{{{ setCurrentLineForeground() method public void setCurrentLineForeground(Color highlight) { currentLineHighlight = highlight; } //}}} //{{{ getFoldColor() method public Color getFoldColor() { return foldColor; } //}}} //{{{ setFoldColor() method public void setFoldColor(Color foldColor) { this.foldColor = foldColor; } //}}} //{{{ getPreferredSize() method /* * Component.getPreferredSize() is overridden here to support the * collapsing behavior. */ public Dimension getPreferredSize() { if (! enabled) return disabledSize; if (expanded) return gutterSize; else return collapsedSize; } //}}} //{{{ getMinimumSize() method public Dimension getMinimumSize() { return getPreferredSize(); } //}}} //{{{ getLineNumberAlignment() method /** * Identifies whether the horizontal alignment of the line numbers. * @return Gutter.RIGHT, Gutter.CENTER, Gutter.LEFT */ public int getLineNumberAlignment() { return alignment; } //}}} //{{{ setLineNumberAlignment() method /** * Sets the horizontal alignment of the line numbers. * @param alignment Gutter.RIGHT, Gutter.CENTER, Gutter.LEFT */ public void setLineNumberAlignment(int alignment) { if (this.alignment == alignment) return; this.alignment = alignment; repaint(); } //}}} //{{{ isExpanded() method /** * Identifies whether the gutter is collapsed or expanded. * @return true if the gutter is expanded, false if it is collapsed */ public boolean isExpanded() { return expanded; } //}}} //{{{ setExpanded() method /** * Sets whether the gutter is collapsed or expanded and force the text * area to update its layout if there is a change. * @param expanded true if the gutter is expanded, * false if it is collapsed */ public void setExpanded(boolean expanded) { if (this.expanded == expanded) return; this.expanded = expanded; textArea.revalidate(); } //}}} //{{{ toggleExpanded() method /** * Toggles whether the gutter is collapsed or expanded. */ public void toggleExpanded() { setExpanded(!expanded); } //}}} //{{{ getHighlightInterval() method /** * Sets the number of lines between highlighted line numbers. * @return The number of lines between highlighted line numbers or * zero if highlighting is disabled */ public int getHighlightInterval() { return interval; } //}}} //{{{ setHighlightInterval() method /** * Sets the number of lines between highlighted line numbers. Any value * less than or equal to one will result in highlighting being disabled. * @param interval The number of lines between highlighted line numbers */ public void setHighlightInterval(int interval) { if (interval <= 1) interval = 0; this.interval = interval; repaint(); } //}}} //{{{ isCurrentLineHighlightEnabled() method public boolean isCurrentLineHighlightEnabled() { return currentLineHighlightEnabled; } //}}} //{{{ setCurrentLineHighlightEnabled() method public void setCurrentLineHighlightEnabled(boolean enabled) { if (currentLineHighlightEnabled == enabled) return; currentLineHighlightEnabled = enabled; repaint(); } //}}} //{{{ getStructureHighlightColor() method /** * Returns the structure highlight color. * @since jEdit 4.2pre3 */ public final Color getStructureHighlightColor() { return structureHighlightColor; } //}}} //{{{ setStructureHighlightColor() method /** * Sets the structure highlight color. * @param structureHighlightColor The structure highlight color * @since jEdit 4.2pre3 */ public final void setStructureHighlightColor(Color structureHighlightColor) { this.structureHighlightColor = structureHighlightColor; repaint(); } //}}} //{{{ isStructureHighlightEnabled() method /** * Returns true if structure highlighting is enabled, false otherwise. * @since jEdit 4.2pre3 */ public final boolean isStructureHighlightEnabled() { return structureHighlight; } //}}} //{{{ setStructureHighlightEnabled() method /** * Enables or disables structure highlighting. * @param structureHighlight True if structure highlighting should be * enabled, false otherwise * @since jEdit 4.2pre3 */ public final void setStructureHighlightEnabled(boolean structureHighlight) { this.structureHighlight = structureHighlight; repaint(); } //}}} public void setSelectionPopupHandler(GutterPopupHandler handler) { mouseHandler.selectionPopupHandler = handler; } public GutterPopupHandler getSelectionPopupHandler() { return mouseHandler.selectionPopupHandler; } public void setMouseActionsProvider(MouseActionsProvider mouseActionsProvider) { mouseHandler.mouseActions = mouseActionsProvider; } //}}} //{{{ Private members //{{{ Instance variables private static final int FOLD_MARKER_SIZE = 12; private static final int SELECTION_GUTTER_WIDTH = 12; // The selection gutter exists only if the gutter is not expanded private boolean enabled; private final TextArea textArea; private MouseHandler mouseHandler; private ExtensionManager extensionMgr; private Dimension gutterSize = new Dimension(0,0); private Dimension collapsedSize = new Dimension(0,0); private int lineNumberWidth; private Dimension disabledSize = new Dimension(0,0); private Color intervalHighlight; private Color currentLineHighlight; private Color foldColor; private Color selectionAreaBgColor; private FontMetrics fm; private int alignment; private int interval; private boolean currentLineHighlightEnabled; private boolean expanded; private boolean selectionAreaEnabled; private boolean structureHighlight; private Color structureHighlightColor; private int borderWidth; private Border focusBorder, noFocusBorder; private FoldPainter foldPainter; private JEditBuffer buffer; private BufferListener bufferListener; private int minLineNumberDigits; private int selectionAreaWidth; //}}} //{{{ paintLine() method private void paintLine(Graphics2D gfx, int line, int y) { JEditBuffer buffer = textArea.getBuffer(); if(buffer.isLoading()) return; FontMetrics textAreaFm = textArea.getPainter().getFontMetrics(); int lineHeight = textArea.getPainter().getLineHeight(); int baseline = textAreaFm.getAscent(); ChunkCache.LineInfo info = textArea.chunkCache.getLineInfo(line); int physicalLine = info.physicalLine; // Skip lines beyond EOF if(physicalLine == -1) return; boolean drawFoldMiddle = true; //{{{ Paint fold start and end indicators if(info.firstSubregion && buffer.isFoldStart(physicalLine)) { drawFoldMiddle = false; foldPainter.paintFoldStart(this, gfx, line, physicalLine, textArea.displayManager.isLineVisible(physicalLine+1), y, lineHeight, buffer); } else if(info.lastSubregion && buffer.isFoldEnd(physicalLine)) { drawFoldMiddle = false; foldPainter.paintFoldEnd(this, gfx, line, physicalLine, y, lineHeight, buffer); } //}}} //{{{ Paint bracket scope else if(structureHighlight) { StructureMatcher.Match match = textArea.getStructureMatch(); int caretLine = textArea.getCaretLine(); if(textArea.isStructureHighlightVisible() && physicalLine >= Math.min(caretLine,match.startLine) && physicalLine <= Math.max(caretLine,match.startLine)) { int caretScreenLine; if(caretLine > textArea.getLastPhysicalLine()) caretScreenLine = Integer.MAX_VALUE; else if(textArea.displayManager.isLineVisible( textArea.getCaretLine())) { caretScreenLine = textArea .getScreenLineOfOffset( textArea.getCaretPosition()); } else { caretScreenLine = -1; } int structScreenLine; if(match.startLine > textArea.getLastPhysicalLine()) structScreenLine = Integer.MAX_VALUE; else if(textArea.displayManager.isLineVisible( match.startLine)) { structScreenLine = textArea .getScreenLineOfOffset( match.start); } else { structScreenLine = -1; } if(caretScreenLine > structScreenLine) { int tmp = caretScreenLine; caretScreenLine = structScreenLine; structScreenLine = tmp; } gfx.setColor(structureHighlightColor); drawFoldMiddle = false; if(structScreenLine == caretScreenLine) { // do nothing drawFoldMiddle = true; } // draw |^ else if(line == caretScreenLine) { gfx.fillRect(5, y + lineHeight / 2, 5, 2); gfx.fillRect(5, y + lineHeight / 2, 2, lineHeight - lineHeight / 2); } // draw |_ else if(line == structScreenLine) { gfx.fillRect(5, y, 2, lineHeight / 2); gfx.fillRect(5, y + lineHeight / 2, 5, 2); } // draw | else if(line > caretScreenLine && line < structScreenLine) { gfx.fillRect(5, y, 2, lineHeight); } } } //}}} if(drawFoldMiddle && buffer.getFoldLevel(physicalLine) > 0) { foldPainter.paintFoldMiddle(this, gfx, line, physicalLine, y, lineHeight, buffer); } //{{{ Paint line numbers if(info.firstSubregion && expanded) { String number = Integer.toString(physicalLine + 1); int offset; switch (alignment) { case RIGHT: offset = lineNumberWidth - (fm.stringWidth(number) + 1); break; case CENTER: offset = (lineNumberWidth - fm.stringWidth(number)) / 2; break; case LEFT: default: offset = 0; break; } if (physicalLine == textArea.getCaretLine() && currentLineHighlightEnabled) { gfx.setColor(currentLineHighlight); } else if (interval > 1 && (physicalLine + 1) % interval == 0) gfx.setColor(intervalHighlight); else gfx.setColor(getForeground()); gfx.drawString(number, FOLD_MARKER_SIZE + offset, baseline + y); } //}}} } //}}} //}}} //{{{ MouseHandler class class MouseHandler extends MouseInputAdapter { MouseActionsProvider mouseActions; boolean drag; int toolTipInitialDelay, toolTipReshowDelay; int selectionStart; boolean selectLines; int selAnchorLine; GutterPopupHandler selectionPopupHandler; //{{{ mouseEntered() method public void mouseEntered(MouseEvent e) { ToolTipManager ttm = ToolTipManager.sharedInstance(); toolTipInitialDelay = ttm.getInitialDelay(); toolTipReshowDelay = ttm.getReshowDelay(); ttm.setInitialDelay(0); ttm.setReshowDelay(0); } //}}} //{{{ mouseExited() method public void mouseExited(MouseEvent evt) { ToolTipManager ttm = ToolTipManager.sharedInstance(); ttm.setInitialDelay(toolTipInitialDelay); ttm.setReshowDelay(toolTipReshowDelay); } //}}} //{{{ mousePressed() method public void mousePressed(MouseEvent e) { textArea.requestFocus(); boolean outsideGutter = (e.getX() >= getWidth() - borderWidth * 2); if(TextAreaMouseHandler.isPopupTrigger(e) || outsideGutter) { if ((selectionPopupHandler != null) && (! outsideGutter) && (e.getX() > FOLD_MARKER_SIZE)) { int screenLine = e.getY() / textArea.getPainter().getLineHeight(); int line = textArea.chunkCache.getLineInfo(screenLine) .physicalLine; if (line >= 0) { selectionPopupHandler.handlePopup( e.getX(), e.getY(), line); return; } } e.translatePoint(-getWidth(),0); textArea.mouseHandler.mousePressed(e); drag = true; } else { JEditBuffer buffer = textArea.getBuffer(); int screenLine = e.getY() / textArea.getPainter().getLineHeight(); int line = textArea.chunkCache.getLineInfo(screenLine) .physicalLine; if(line == -1) return; if (e.getX() >= FOLD_MARKER_SIZE) { selectionStart = textArea.getLineStartOffset(line); int selectionEnd = getFoldEndOffset(line); Selection s = new Selection.Range( selectionStart, selectionEnd); if(textArea.isMultipleSelectionEnabled()) textArea.addToSelection(s); else textArea.setSelection(s); textArea.moveCaretPosition(selectionEnd, false); selectLines = true; selAnchorLine = line; return; } //{{{ Determine action String defaultAction; String variant; if(buffer.isFoldStart(line)) { defaultAction = "toggle-fold"; variant = "fold"; } else if(structureHighlight && textArea.isStructureHighlightVisible() && textArea.lineInStructureScope(line)) { defaultAction = "match-struct"; variant = "struct"; } else return; String action = null; if (mouseActions != null) action = mouseActions.getActionForEvent( e,variant); if(action == null) action = defaultAction; //}}} //{{{ Handle actions StructureMatcher.Match match = textArea .getStructureMatch(); if(action.equals("select-fold")) { textArea.displayManager.expandFold(line,true); textArea.selectFold(line); } else if(action.equals("narrow-fold")) { int[] lines = buffer.getFoldAtLine(line); textArea.displayManager.narrow(lines[0],lines[1]); } else if(action.startsWith("toggle-fold")) { if(textArea.displayManager .isLineVisible(line + 1)) { textArea.collapseFold(line); } else { if(action.endsWith("-fully")) { textArea.displayManager .expandFold(line, true); } else { textArea.displayManager .expandFold(line, false); } } } else if(action.equals("match-struct")) { if(match != null) textArea.setCaretPosition(match.end); } else if(action.equals("select-struct")) { if(match != null) { match.matcher.selectMatch( textArea); } } else if(action.equals("narrow-struct")) { if(match != null) { int start = Math.min( match.startLine, textArea.getCaretLine()); int end = Math.max( match.endLine, textArea.getCaretLine()); textArea.displayManager.narrow(start,end); } } //}}} } } //}}} //{{{ mouseDragged() method public void mouseDragged(MouseEvent e) { if(drag /* && e.getX() >= getWidth() - borderWidth * 2 */) { e.translatePoint(-getWidth(),0); textArea.mouseHandler.mouseDragged(e); } else if(selectLines) { int screenLine = e.getY() / textArea.getPainter().getLineHeight(); int line; if(e.getY() < 0) { textArea.scrollUpLine(); line = textArea.getFirstPhysicalLine(); } else if(e.getY() >= getHeight()) { textArea.scrollDownLine(); line = textArea.getLastPhysicalLine(); } else line = textArea.chunkCache.getLineInfo(screenLine) .physicalLine; int selStart, selEnd; if(line < selAnchorLine) { selStart = textArea.getLineStartOffset(line); selEnd = getFoldEndOffset(selAnchorLine); textArea.moveCaretPosition(selStart, false); } else { selStart = textArea.getLineStartOffset(selAnchorLine); selEnd = getFoldEndOffset(line); textArea.moveCaretPosition(selEnd, false); } textArea.resizeSelection(selStart, selEnd, 0, false); } } //}}} //{{{ getFoldEndOffset() method private int getFoldEndOffset(int line) { JEditBuffer buffer = textArea.getBuffer(); int endLine; if ((line == buffer.getLineCount() - 1) || (textArea.displayManager.isLineVisible(line + 1))) { endLine = line; } else { int[] lines = buffer.getFoldAtLine(line); endLine = lines[1]; } if(endLine == buffer.getLineCount() - 1) return buffer.getLineEndOffset(endLine) - 1; else return buffer.getLineEndOffset(endLine); } //}}} //{{{ mouseReleased() method public void mouseReleased(MouseEvent e) { if(drag && e.getX() >= getWidth() - borderWidth * 2) { e.translatePoint(-getWidth(),0); textArea.mouseHandler.mouseReleased(e); } if (selectLines) { Selection sel = textArea.getSelectionAtOffset(selectionStart); if(sel != null) Registers.setRegister('%', textArea.getSelectedText(sel)); } drag = false; selectLines = false; } //}}} } //}}} }