/jEdit/tags/jedit-4-1-pre5/org/gjt/sp/jedit/textarea/Gutter.java
Java | 930 lines | 584 code | 116 blank | 230 comment | 98 complexity | 8a3303bcd2ff7792f7276b47c4e16005 MD5 | raw file
Possible License(s): BSD-3-Clause, AGPL-1.0, Apache-2.0, LGPL-2.0, LGPL-3.0, GPL-2.0, CC-BY-SA-3.0, LGPL-2.1, GPL-3.0, MPL-2.0-no-copyleft-exception, IPL-1.0
1/*
2 * Gutter.java
3 * :tabSize=8:indentSize=8:noTabs=false:
4 * :folding=explicit:collapseFolds=1:
5 *
6 * Copyright (C) 1999, 2000 mike dillon
7 * Portions copyright (C) 2001, 2002 Slava Pestov
8 *
9 * This program is free software; you can redistribute it and/or
10 * modify it under the terms of the GNU General Public License
11 * as published by the Free Software Foundation; either version 2
12 * of the License, or any later version.
13 *
14 * This program is distributed in the hope that it will be useful,
15 * but WITHOUT ANY WARRANTY; without even the implied warranty of
16 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
17 * GNU General Public License for more details.
18 *
19 * You should have received a copy of the GNU General Public License
20 * along with this program; if not, write to the Free Software
21 * Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA.
22 */
23
24package org.gjt.sp.jedit.textarea;
25
26//{{{ Imports
27import java.awt.*;
28import java.awt.event.*;
29import java.util.ArrayList;
30import javax.swing.*;
31import javax.swing.border.*;
32import javax.swing.event.*;
33import org.gjt.sp.jedit.*;
34import org.gjt.sp.util.Log;
35//}}}
36
37/**
38 * The gutter is the component that displays folding triangles and line
39 * numbers to the left of the text area. The only methods in this class
40 * that should be called by plugins are those for adding and removing
41 * text area extensions.
42 *
43 * @see #addExtension(TextAreaExtension)
44 * @see #addExtension(int,TextAreaExtension)
45 * @see #removeExtension(TextAreaExtension)
46 * @see TextAreaExtension
47 * @see JEditTextArea
48 *
49 * @author Mike Dillon and Slava Pestov
50 * @version $Id: Gutter.java 4328 2002-08-27 22:31:39Z spestov $
51 */
52public class Gutter extends JComponent implements SwingConstants
53{
54 //{{{ Layers
55 /**
56 * The lowest possible layer.
57 * @see #addExtension(int,TextAreaExtension)
58 * @since jEdit 4.0pre4
59 */
60 public static final int LOWEST_LAYER = Integer.MIN_VALUE;
61
62 /**
63 * Default extension layer. This is above the wrap guide but below the
64 * bracket highlight.
65 * @since jEdit 4.0pre4
66 */
67 public static final int DEFAULT_LAYER = 0;
68
69 /**
70 * Highest possible layer.
71 * @since jEdit 4.0pre4
72 */
73 public static final int HIGHEST_LAYER = Integer.MAX_VALUE;
74 //}}}
75
76 //{{{ Gutter constructor
77 public Gutter(View view, JEditTextArea textArea)
78 {
79 this.view = view;
80 this.textArea = textArea;
81
82 setAutoscrolls(true);
83 setOpaque(true);
84
85 extensionMgr = new ExtensionManager();
86
87 MouseHandler ml = new MouseHandler();
88 addMouseListener(ml);
89 addMouseMotionListener(ml);
90
91 addExtension(new MarkerHighlight());
92 } //}}}
93
94 //{{{ paintComponent() method
95 public void paintComponent(Graphics _gfx)
96 {
97 Graphics2D gfx = (Graphics2D)_gfx;
98
99 // fill the background
100 Rectangle clip = gfx.getClipBounds();
101 gfx.setColor(getBackground());
102 gfx.fillRect(clip.x, clip.y, clip.width, clip.height);
103
104 // if buffer is loading, don't paint anything
105 if (!textArea.getBuffer().isLoaded())
106 return;
107
108 // paint highlights and line numbers
109 int lineHeight = textArea.getPainter().getFontMetrics()
110 .getHeight();
111
112 int firstLine = clip.y / lineHeight;
113 int lastLine = (clip.y + clip.height - 1) / lineHeight;
114
115 FontMetrics pfm = textArea.getPainter().getFontMetrics();
116 Color fg = getForeground();
117
118 int baseline = (int)((this.baseline + lineHeight
119 - pfm.getDescent()) / 2.0);
120
121 boolean highlightCurrentLine = currentLineHighlightEnabled
122 && textArea.selection.size() == 0;
123
124 int y = (clip.y - clip.y % lineHeight);
125
126 Buffer buffer = textArea.getBuffer();
127 textArea.chunkCache.updateChunksUpTo(lastLine);
128
129 for (int line = firstLine; line <= lastLine;
130 line++, y += lineHeight)
131 {
132 ChunkCache.LineInfo info = textArea.chunkCache.getLineInfo(line);
133 if(!info.chunksValid)
134 System.err.println("gutter paint: not valid");
135 int physicalLine = info.physicalLine;
136
137 //{{{ Paint text area extensions
138 if(physicalLine != -1)
139 {
140 int start = textArea.getScreenLineStartOffset(line);
141 int end = textArea.getScreenLineEndOffset(line);
142
143 extensionMgr.paintValidLine(gfx,line,physicalLine,start,end,y);
144 }
145 else
146 extensionMgr.paintInvalidLine(gfx,line,y);
147 //}}}
148
149 // Skip lines beyond EOF
150 if(physicalLine == -1)
151 return;
152
153 //{{{ Paint fold triangles
154 if(info.firstSubregion
155 && physicalLine != buffer.getLineCount() - 1
156 && buffer.isFoldStart(physicalLine))
157 {
158 int _y = y + lineHeight / 2;
159 gfx.setColor(foldColor);
160 if(textArea.getFoldVisibilityManager()
161 .isLineVisible(physicalLine + 1))
162 {
163 gfx.drawLine(1,_y - 3,10,_y - 3);
164 gfx.drawLine(2,_y - 2,9,_y - 2);
165 gfx.drawLine(3,_y - 1,8,_y - 1);
166 gfx.drawLine(4,_y,7,_y);
167 gfx.drawLine(5,_y + 1,6,_y + 1);
168 }
169 else
170 {
171 gfx.drawLine(4,_y - 5,4,_y + 4);
172 gfx.drawLine(5,_y - 4,5,_y + 3);
173 gfx.drawLine(6,_y - 3,6,_y + 2);
174 gfx.drawLine(7,_y - 2,7,_y + 1);
175 gfx.drawLine(8,_y - 1,8,_y);
176 }
177 } //}}}
178 //{{{ Paint bracket scope
179 else if(bracketHighlight)
180 {
181 int bracketLine = textArea.getBracketLine();
182 int caretLine = textArea.getCaretLine();
183
184 if(textArea.isBracketHighlightVisible()
185 && physicalLine >= Math.min(caretLine,bracketLine)
186 && physicalLine <= Math.max(caretLine,bracketLine))
187 {
188 int caretScreenLine;
189 if(caretLine > textArea.getLastPhysicalLine())
190 caretScreenLine = Integer.MAX_VALUE;
191 else
192 {
193 caretScreenLine = textArea
194 .getScreenLineOfOffset(
195 textArea.getCaretPosition());
196 }
197
198 int bracketScreenLine;
199 if(bracketLine > textArea.getLastPhysicalLine())
200 bracketScreenLine = Integer.MAX_VALUE;
201 else
202 {
203 bracketScreenLine = textArea.chunkCache
204 .getScreenLineOfOffset(
205 bracketLine,
206 textArea.getBracketPosition());
207 }
208
209 if(caretScreenLine > bracketScreenLine)
210 {
211 int tmp = caretScreenLine;
212 caretScreenLine = bracketScreenLine;
213 bracketScreenLine = tmp;
214 }
215
216 gfx.setColor(bracketHighlightColor);
217 if(bracketScreenLine == caretScreenLine)
218 {
219 // do nothing
220 }
221 else if(line == caretScreenLine)
222 {
223 gfx.fillRect(5,
224 y
225 + lineHeight / 2,
226 5,
227 2);
228 gfx.fillRect(5,
229 y
230 + lineHeight / 2,
231 2,
232 lineHeight - lineHeight / 2);
233 }
234 else if(line == bracketScreenLine)
235 {
236 gfx.fillRect(5,
237 y,
238 2,
239 lineHeight / 2);
240 gfx.fillRect(5,
241 y + lineHeight / 2,
242 5,
243 2);
244 }
245 else if(line > caretScreenLine
246 && line < bracketScreenLine)
247 {
248 gfx.fillRect(5,
249 y,
250 2,
251 lineHeight);
252 }
253 }
254 } //}}}
255
256 //{{{ Paint line numbers
257 if(info.firstSubregion && expanded)
258 {
259 String number = Integer.toString(physicalLine + 1);
260
261 int offset;
262 switch (alignment)
263 {
264 case RIGHT:
265 offset = gutterSize.width - collapsedSize.width
266 - (fm.stringWidth(number) + 1);
267 break;
268 case CENTER:
269 offset = ((gutterSize.width - collapsedSize.width)
270 - fm.stringWidth(number)) / 2;
271 break;
272 case LEFT: default:
273 offset = 0;
274 break;
275 }
276
277 if (physicalLine == textArea.getCaretLine() && highlightCurrentLine)
278 {
279 gfx.setColor(currentLineHighlight);
280 }
281 else if (interval > 1 && (line
282 + textArea.getFirstLine() + 1)
283 % interval == 0)
284 gfx.setColor(intervalHighlight);
285 else
286 gfx.setColor(fg);
287
288 gfx.drawString(number, FOLD_MARKER_SIZE + offset,
289 baseline + y);
290 } //}}}
291 }
292 } //}}}
293
294 //{{{ addExtension() method
295 /**
296 * Adds a text area extension, which can perform custom painting and
297 * tool tip handling.
298 * @param extension The extension
299 * @since jEdit 4.0pre4
300 */
301 public void addExtension(TextAreaExtension extension)
302 {
303 extensionMgr.addExtension(DEFAULT_LAYER,extension);
304 repaint();
305 } //}}}
306
307 //{{{ addExtension() method
308 /**
309 * Adds a text area extension, which can perform custom painting and
310 * tool tip handling.
311 * @param layer The layer to add the extension to. Note that more than
312 * extension can share the same layer.
313 * @param extension The extension
314 * @since jEdit 4.0pre4
315 */
316 public void addExtension(int layer, TextAreaExtension extension)
317 {
318 extensionMgr.addExtension(layer,extension);
319 repaint();
320 } //}}}
321
322 //{{{ removeExtension() method
323 /**
324 * Removes a text area extension. It will no longer be asked to
325 * perform custom painting and tool tip handling.
326 * @param extension The extension
327 * @since jEdit 4.0pre4
328 */
329 public void removeExtension(TextAreaExtension extension)
330 {
331 extensionMgr.removeExtension(extension);
332 repaint();
333 } //}}}
334
335 //{{{ getExtensions() method
336 /**
337 * Returns an array of registered text area extensions. Useful for
338 * debugging purposes.
339 * @since jEdit 4.1pre5
340 */
341 public TextAreaExtension[] getExtensions()
342 {
343 return extensionMgr.getExtensions();
344 } //}}}
345
346 //{{{ getToolTipText() method
347 /**
348 * Returns the tool tip to display at the specified location.
349 * @param evt The mouse event
350 */
351 public String getToolTipText(MouseEvent evt)
352 {
353 if(!textArea.getBuffer().isLoaded())
354 return null;
355
356 return extensionMgr.getToolTipText(evt.getX(),evt.getY());
357 } //}}}
358
359 //{{{ setBorder() method
360 /**
361 * Convenience method for setting a default matte border on the right
362 * with the specified border width and color
363 * @param width The border width (in pixels)
364 * @param color1 The focused border color
365 * @param color2 The unfocused border color
366 * @param color3 The gutter/text area gap color
367 */
368 public void setBorder(int width, Color color1, Color color2, Color color3)
369 {
370 this.borderWidth = width;
371
372 focusBorder = new CompoundBorder(new MatteBorder(0,0,0,width,color3),
373 new MatteBorder(0,0,0,width,color1));
374 noFocusBorder = new CompoundBorder(new MatteBorder(0,0,0,width,color3),
375 new MatteBorder(0,0,0,width,color2));
376 updateBorder();
377 } //}}}
378
379 //{{{ updateBorder() method
380 /**
381 * Sets the border differently if the text area has focus or not.
382 */
383 public void updateBorder()
384 {
385 // because we are called from the text area's focus handler,
386 // we do an invokeLater() so that the view's focus handler
387 // has a chance to execute and set the edit pane properly
388 SwingUtilities.invokeLater(new Runnable()
389 {
390 public void run()
391 {
392 if(view.getEditPane() == null)
393 return;
394
395 if(view.getEditPane().getTextArea() == textArea)
396 setBorder(focusBorder);
397 else
398 setBorder(noFocusBorder);
399 }
400 });
401 } //}}}
402
403 //{{{ setBorder() method
404 /*
405 * JComponent.setBorder(Border) is overridden here to cache the left
406 * inset of the border (if any) to avoid having to fetch it during every
407 * repaint.
408 */
409 public void setBorder(Border border)
410 {
411 super.setBorder(border);
412
413 if (border == null)
414 {
415 collapsedSize.width = 0;
416 collapsedSize.height = 0;
417 }
418 else
419 {
420 Insets insets = border.getBorderInsets(this);
421 collapsedSize.width = FOLD_MARKER_SIZE + insets.right;
422 collapsedSize.height = gutterSize.height
423 = insets.top + insets.bottom;
424 gutterSize.width = FOLD_MARKER_SIZE + insets.right
425 + fm.stringWidth("12345");
426 }
427
428 revalidate();
429 } //}}}
430
431 //{{{ setFont() method
432 /*
433 * JComponent.setFont(Font) is overridden here to cache the baseline for
434 * the font. This avoids having to get the font metrics during every
435 * repaint.
436 */
437 public void setFont(Font font)
438 {
439 super.setFont(font);
440
441 fm = getFontMetrics(font);
442
443 baseline = fm.getAscent();
444
445 Border border = getBorder();
446 if(border != null)
447 {
448 gutterSize.width = FOLD_MARKER_SIZE
449 + border.getBorderInsets(this).right
450 + fm.stringWidth("12345");
451 revalidate();
452 }
453 } //}}}
454
455 //{{{ Getters and setters
456
457 //{{{ getHighlightedForeground() method
458 /**
459 * Get the foreground color for highlighted line numbers
460 * @return The highlight color
461 */
462 public Color getHighlightedForeground()
463 {
464 return intervalHighlight;
465 } //}}}
466
467 //{{{ setHighlightedForeground() method
468 public void setHighlightedForeground(Color highlight)
469 {
470 intervalHighlight = highlight;
471 } //}}}
472
473 //{{{ getCurrentLineForeground() method
474 public Color getCurrentLineForeground()
475 {
476 return currentLineHighlight;
477 } //}}}
478
479 //{{{ setCurrentLineForeground() method
480 public void setCurrentLineForeground(Color highlight)
481 {
482 currentLineHighlight = highlight;
483 } //}}}
484
485 //{{{ getFoldColor() method
486 public Color getFoldColor()
487 {
488 return foldColor;
489 } //}}}
490
491 //{{{ setFoldColor() method
492 public void setFoldColor(Color foldColor)
493 {
494 this.foldColor = foldColor;
495 } //}}}
496
497 //{{{ getPreferredSize() method
498 /*
499 * Component.getPreferredSize() is overridden here to support the
500 * collapsing behavior.
501 */
502 public Dimension getPreferredSize()
503 {
504 if (expanded)
505 return gutterSize;
506 else
507 return collapsedSize;
508 } //}}}
509
510 //{{{ getMinimumSize() method
511 public Dimension getMinimumSize()
512 {
513 return getPreferredSize();
514 } //}}}
515
516 //{{{ getLineNumberAlignment() method
517 /**
518 * Identifies whether the horizontal alignment of the line numbers.
519 * @return Gutter.RIGHT, Gutter.CENTER, Gutter.LEFT
520 */
521 public int getLineNumberAlignment()
522 {
523 return alignment;
524 } //}}}
525
526 //{{{ setLineNumberAlignment() method
527 /**
528 * Sets the horizontal alignment of the line numbers.
529 * @param alignment Gutter.RIGHT, Gutter.CENTER, Gutter.LEFT
530 */
531 public void setLineNumberAlignment(int alignment)
532 {
533 if (this.alignment == alignment) return;
534
535 this.alignment = alignment;
536
537 repaint();
538 } //}}}
539
540 //{{{ isExpanded() method
541 /**
542 * Identifies whether the gutter is collapsed or expanded.
543 * @return true if the gutter is expanded, false if it is collapsed
544 */
545 public boolean isExpanded()
546 {
547 return expanded;
548 } //}}}
549
550 //{{{ setExpanded() method
551 /**
552 * Sets whether the gutter is collapsed or expanded and force the text
553 * area to update its layout if there is a change.
554 * @param collapsed true if the gutter is expanded,
555 * false if it is collapsed
556 */
557 public void setExpanded(boolean expanded)
558 {
559 if (this.expanded == expanded) return;
560
561 this.expanded = expanded;
562
563 textArea.revalidate();
564 } //}}}
565
566 //{{{ toggleExpanded() method
567 /**
568 * Toggles whether the gutter is collapsed or expanded.
569 */
570 public void toggleExpanded()
571 {
572 setExpanded(!expanded);
573 } //}}}
574
575 //{{{ getHighlightInterval() method
576 /**
577 * Sets the number of lines between highlighted line numbers.
578 * @return The number of lines between highlighted line numbers or
579 * zero if highlighting is disabled
580 */
581 public int getHighlightInterval()
582 {
583 return interval;
584 } //}}}
585
586 //{{{ setHighlightInterval() method
587 /**
588 * Sets the number of lines between highlighted line numbers. Any value
589 * less than or equal to one will result in highlighting being disabled.
590 * @param interval The number of lines between highlighted line numbers
591 */
592 public void setHighlightInterval(int interval)
593 {
594 if (interval <= 1) interval = 0;
595 this.interval = interval;
596 repaint();
597 } //}}}
598
599 //{{{ isCurrentLineHighlightEnabled() method
600 public boolean isCurrentLineHighlightEnabled()
601 {
602 return currentLineHighlightEnabled;
603 } //}}}
604
605 //{{{ setCurrentLineHighlightEnabled() method
606 public void setCurrentLineHighlightEnabled(boolean enabled)
607 {
608 if (currentLineHighlightEnabled == enabled) return;
609
610 currentLineHighlightEnabled = enabled;
611
612 repaint();
613 } //}}}
614
615 //{{{ getBracketHighlightColor() method
616 /**
617 * Returns the bracket highlight color.
618 */
619 public final Color getBracketHighlightColor()
620 {
621 return bracketHighlightColor;
622 } //}}}
623
624 //{{{ setBracketHighlightColor() method
625 /**
626 * Sets the bracket highlight color.
627 * @param bracketHighlightColor The bracket highlight color
628 * @since jEdit 4.0pre1
629 */
630 public final void setBracketHighlightColor(Color bracketHighlightColor)
631 {
632 this.bracketHighlightColor = bracketHighlightColor;
633 repaint();
634 } //}}}
635
636 //{{{ isBracketHighlightEnabled() method
637 /**
638 * Returns true if bracket highlighting is enabled, false otherwise.
639 * When bracket highlighting is enabled, the bracket matching the
640 * one before the caret (if any) is highlighted.
641 * @since jEdit 4.0pre1
642 */
643 public final boolean isBracketHighlightEnabled()
644 {
645 return bracketHighlight;
646 } //}}}
647
648 //{{{ setBracketHighlightEnabled() method
649 /**
650 * Enables or disables bracket highlighting.
651 * When bracket highlighting is enabled, the bracket matching the
652 * one before the caret (if any) is highlighted.
653 * @param bracketHighlight True if bracket highlighting should be
654 * enabled, false otherwise
655 * @since jEdit 4.0pre1
656 */
657 public final void setBracketHighlightEnabled(boolean bracketHighlight)
658 {
659 this.bracketHighlight = bracketHighlight;
660 repaint();
661 } //}}}
662
663 //{{{ getMarkerHighlightColor() method
664 public Color getMarkerHighlightColor()
665 {
666 return markerHighlightColor;
667 } //}}}
668
669 //{{{ setMarkerHighlightColor() method
670 public void setMarkerHighlightColor(Color markerHighlightColor)
671 {
672 this.markerHighlightColor = markerHighlightColor;
673 } //}}}
674
675 //{{{ isMarkerHighlightEnabled() method
676 public boolean isMarkerHighlightEnabled()
677 {
678 return markerHighlight;
679 } //}}}
680
681 //{{{ isMarkerHighlightEnabled()
682 public void setMarkerHighlightEnabled(boolean markerHighlight)
683 {
684 this.markerHighlight = markerHighlight;
685 } //}}}
686
687 //}}}
688
689 //{{{ Private members
690 private static final int FOLD_MARKER_SIZE = 12;
691
692 private View view;
693 private JEditTextArea textArea;
694
695 private ExtensionManager extensionMgr;
696
697 private int baseline;
698
699 private Dimension gutterSize = new Dimension(0,0);
700 private Dimension collapsedSize = new Dimension(0,0);
701
702 private Color intervalHighlight;
703 private Color currentLineHighlight;
704 private Color foldColor;
705
706 private FontMetrics fm;
707
708 private int alignment;
709
710 private int interval;
711 private boolean currentLineHighlightEnabled;
712 private boolean expanded;
713
714 private boolean bracketHighlight;
715 private Color bracketHighlightColor;
716
717 private boolean markerHighlight;
718 private Color markerHighlightColor;
719
720 private int borderWidth;
721 private Border focusBorder, noFocusBorder;
722 //}}}
723
724 //{{{ MouseHandler class
725 class MouseHandler extends MouseInputAdapter
726 {
727 boolean drag;
728 int toolTipInitialDelay, toolTipReshowDelay;
729
730 //{{{ mouseEntered() method
731 public void mouseEntered(MouseEvent e)
732 {
733 ToolTipManager ttm = ToolTipManager.sharedInstance();
734 toolTipInitialDelay = ttm.getInitialDelay();
735 toolTipReshowDelay = ttm.getReshowDelay();
736 ttm.setInitialDelay(0);
737 ttm.setReshowDelay(0);
738 } //}}}
739
740 //{{{ mouseExited() method
741 public void mouseExited(MouseEvent evt)
742 {
743 ToolTipManager ttm = ToolTipManager.sharedInstance();
744 ttm.setInitialDelay(toolTipInitialDelay);
745 ttm.setReshowDelay(toolTipReshowDelay);
746 } //}}}
747
748 //{{{ mousePressed() method
749 public void mousePressed(MouseEvent e)
750 {
751 if(GUIUtilities.isPopupTrigger(e)
752 || e.getX() >= getWidth() - borderWidth * 2)
753 {
754 e.translatePoint(-getWidth(),0);
755 textArea.mouseHandler.mousePressed(e);
756 drag = true;
757 }
758 else
759 {
760 Buffer buffer = textArea.getBuffer();
761
762 int screenLine = e.getY() / textArea.getPainter()
763 .getFontMetrics().getHeight();
764 textArea.chunkCache.updateChunksUpTo(screenLine);
765
766 int line = textArea.chunkCache.getLineInfo(screenLine)
767 .physicalLine;
768
769 if(line == -1)
770 return;
771
772 FoldVisibilityManager foldVisibilityManager
773 = textArea.getFoldVisibilityManager();
774
775 //{{{ Clicking on fold triangle does various things
776 if(buffer.isFoldStart(line))
777 {
778 StringBuffer property = new StringBuffer(
779 "view.gutter.gutter");
780 if(e.isShiftDown())
781 property.append("Shift");
782 if(e.isControlDown())
783 property.append("Control");
784 if(e.isAltDown())
785 property.append("Alt");
786 property.append("Click");
787
788 String action = jEdit.getProperty(property
789 .toString());
790 if(action == null)
791 {
792 action = jEdit.getProperty(
793 "view.gutter.gutterClick");
794 }
795
796 if(action == null)
797 action = "toggleFold";
798
799 if(action.equals("selectFold"))
800 {
801 foldVisibilityManager
802 .expandFold(line,true);
803 textArea.selectFold(line);
804 }
805 else if(foldVisibilityManager
806 .isLineVisible(line + 1))
807 {
808 foldVisibilityManager
809 .collapseFold(line);
810 }
811 else
812 {
813 if(action.equals(
814 "toggleFoldFully"))
815 {
816 foldVisibilityManager
817 .expandFold(line,
818 true);
819 }
820 else
821 {
822 foldVisibilityManager
823 .expandFold(line,
824 false);
825 }
826 }
827 } //}}}
828 //{{{ Clicking in bracket scope locates matching bracket
829 else if(bracketHighlight)
830 {
831 if(textArea.isBracketHighlightVisible())
832 {
833 int bracketLine = textArea.getBracketLine();
834 int caretLine = textArea.getCaretLine();
835 if(caretLine != bracketLine)
836 {
837 if(caretLine > bracketLine)
838 {
839 int tmp = caretLine;
840 caretLine = bracketLine;
841 bracketLine = tmp;
842 }
843
844 if(line >= caretLine
845 && line <= bracketLine)
846 {
847 if(e.isControlDown())
848 textArea.selectToMatchingBracket();
849 else
850 textArea.goToMatchingBracket();
851 }
852 }
853 }
854 } //}}}
855 }
856 } //}}}
857
858 //{{{ mouseDragged() method
859 public void mouseDragged(MouseEvent e)
860 {
861 if(drag /* && e.getX() >= getWidth() - borderWidth * 2 */)
862 {
863 e.translatePoint(-getWidth(),0);
864 textArea.mouseHandler.mouseDragged(e);
865 }
866 } //}}}
867
868 //{{{ mouseReleased() method
869 public void mouseReleased(MouseEvent e)
870 {
871 if(drag && e.getX() >= getWidth() - borderWidth * 2)
872 {
873 e.translatePoint(-getWidth(),0);
874 textArea.mouseHandler.mouseReleased(e);
875 }
876
877 drag = false;
878 } //}}}
879 } //}}}
880
881 //{{{ MarkerHighlight class
882 class MarkerHighlight extends TextAreaExtension
883 {
884 //{{{ paintValidLine() method
885 public void paintValidLine(Graphics2D gfx, int screenLine,
886 int physicalLine, int start, int end, int y)
887 {
888 if(isMarkerHighlightEnabled())
889 {
890 Buffer buffer = textArea.getBuffer();
891 if(buffer.getMarkerInRange(start,end) != null)
892 {
893 gfx.setColor(getMarkerHighlightColor());
894 FontMetrics fm = textArea.getPainter().getFontMetrics();
895 gfx.fillRect(0,y,textArea.getGutter()
896 .getWidth(),fm.getHeight());
897 }
898 }
899 } //}}}
900
901 //{{{ getToolTipText() method
902 public String getToolTipText(int x, int y)
903 {
904 if(isMarkerHighlightEnabled())
905 {
906 int start = textArea.xyToOffset(0,y);
907 if(start == -1)
908 return null;
909
910 int end = textArea.getScreenLineEndOffset(
911 textArea.getScreenLineOfOffset(start));
912
913 Marker marker = textArea.getBuffer().getMarkerInRange(start,end);
914 if(marker != null)
915 {
916 char shortcut = marker.getShortcut();
917 if(shortcut == '\0')
918 return jEdit.getProperty("view.gutter.marker.no-name");
919 else
920 {
921 String[] args = { String.valueOf(shortcut) };
922 return jEdit.getProperty("view.gutter.marker",args);
923 }
924 }
925 }
926
927 return null;
928 } //}}}
929 } //}}}
930}