PageRenderTime 58ms CodeModel.GetById 19ms app.highlight 32ms RepoModel.GetById 1ms app.codeStats 0ms

/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}