PageRenderTime 235ms CodeModel.GetById 176ms app.highlight 51ms RepoModel.GetById 1ms app.codeStats 0ms

/jEdit/tags/jedit-4-3-pre5/org/gjt/sp/jedit/textarea/Gutter.java

#
Java | 873 lines | 545 code | 107 blank | 221 comment | 96 complexity | 7be51f6ba0c03eeef5e6af3fbbcb8f23 MD5 | raw file
  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 javax.swing.*;
 30import javax.swing.border.*;
 31import javax.swing.event.*;
 32import org.gjt.sp.jedit.View;
 33import org.gjt.sp.jedit.GUIUtilities;
 34import org.gjt.sp.jedit.buffer.JEditBuffer;
 35import org.gjt.sp.util.Log;
 36//}}}
 37
 38/**
 39 * The gutter is the component that displays folding triangles and line
 40 * numbers to the left of the text area. The only methods in this class
 41 * that should be called by plugins are those for adding and removing
 42 * text area extensions.
 43 *
 44 * @see #addExtension(TextAreaExtension)
 45 * @see #addExtension(int,TextAreaExtension)
 46 * @see #removeExtension(TextAreaExtension)
 47 * @see TextAreaExtension
 48 * @see JEditTextArea
 49 *
 50 * @author Mike Dillon and Slava Pestov
 51 * @version $Id: Gutter.java 5458 2006-06-20 21:27:24Z kpouer $
 52 */
 53public class Gutter extends JComponent implements SwingConstants
 54{
 55	//{{{ Layers
 56	/**
 57	 * The lowest possible layer.
 58	 * @see #addExtension(int,TextAreaExtension)
 59	 * @since jEdit 4.0pre4
 60	 */
 61	public static final int LOWEST_LAYER = Integer.MIN_VALUE;
 62
 63	/**
 64	 * Default extension layer. This is above the wrap guide but below the
 65	 * bracket highlight.
 66	 * @since jEdit 4.0pre4
 67	 */
 68	public static final int DEFAULT_LAYER = 0;
 69
 70	/**
 71	 * Highest possible layer.
 72	 * @since jEdit 4.0pre4
 73	 */
 74	public static final int HIGHEST_LAYER = Integer.MAX_VALUE;
 75	//}}}
 76
 77	//{{{ Gutter constructor
 78	public Gutter(JEditTextArea textArea)
 79	{
 80		this.textArea = textArea;
 81
 82		setAutoscrolls(true);
 83		setOpaque(true);
 84		setRequestFocusEnabled(false);
 85
 86		extensionMgr = new ExtensionManager();
 87
 88		MouseHandler ml = new MouseHandler();
 89		addMouseListener(ml);
 90		addMouseMotionListener(ml);
 91
 92		updateBorder();
 93	} //}}}
 94
 95	//{{{ paintComponent() method
 96	public void paintComponent(Graphics _gfx)
 97	{
 98		Graphics2D gfx = (Graphics2D)_gfx;
 99
100		// fill the background
101		Rectangle clip = gfx.getClipBounds();
102		gfx.setColor(getBackground());
103		gfx.fillRect(clip.x, clip.y, clip.width, clip.height);
104
105		// if buffer is loading, don't paint anything
106		if (textArea.getBuffer().isLoading())
107			return;
108
109		int lineHeight = textArea.getPainter().getFontMetrics()
110			.getHeight();
111
112		if(lineHeight == 0)
113			return;
114
115		int firstLine = clip.y / lineHeight;
116		int lastLine = (clip.y + clip.height - 1) / lineHeight;
117
118		if(lastLine - firstLine > textArea.getVisibleLines())
119		{
120			Log.log(Log.ERROR,this,"BUG: firstLine=" + firstLine);
121			Log.log(Log.ERROR,this,"     lastLine=" + lastLine);
122			Log.log(Log.ERROR,this,"     visibleLines=" + textArea.getVisibleLines());
123			Log.log(Log.ERROR,this,"     height=" + getHeight());
124			Log.log(Log.ERROR,this,"     painter.height=" + textArea.getPainter().getHeight());
125			Log.log(Log.ERROR,this,"     clip.y=" + clip.y);
126			Log.log(Log.ERROR,this,"     clip.height=" + clip.height);
127			Log.log(Log.ERROR,this,"     lineHeight=" + lineHeight);
128		}
129	
130		int y = (clip.y - clip.y % lineHeight);
131
132		extensionMgr.paintScreenLineRange(textArea,gfx,
133			firstLine,lastLine,y,lineHeight);
134
135		for (int line = firstLine; line <= lastLine;
136			line++, y += lineHeight)
137		{
138			paintLine(gfx,line,y);
139		}
140	} //}}}
141
142	//{{{ addExtension() method
143	/**
144	 * Adds a text area extension, which can perform custom painting and
145	 * tool tip handling.
146	 * @param extension The extension
147	 * @since jEdit 4.0pre4
148	 */
149	public void addExtension(TextAreaExtension extension)
150	{
151		extensionMgr.addExtension(DEFAULT_LAYER,extension);
152		repaint();
153	} //}}}
154
155	//{{{ addExtension() method
156	/**
157	 * Adds a text area extension, which can perform custom painting and
158	 * tool tip handling.
159	 * @param layer The layer to add the extension to. Note that more than
160	 * extension can share the same layer.
161	 * @param extension The extension
162	 * @since jEdit 4.0pre4
163	 */
164	public void addExtension(int layer, TextAreaExtension extension)
165	{
166		extensionMgr.addExtension(layer,extension);
167		repaint();
168	} //}}}
169
170	//{{{ removeExtension() method
171	/**
172	 * Removes a text area extension. It will no longer be asked to
173	 * perform custom painting and tool tip handling.
174	 * @param extension The extension
175	 * @since jEdit 4.0pre4
176	 */
177	public void removeExtension(TextAreaExtension extension)
178	{
179		extensionMgr.removeExtension(extension);
180		repaint();
181	} //}}}
182
183	//{{{ getExtensions() method
184	/**
185	 * Returns an array of registered text area extensions. Useful for
186	 * debugging purposes.
187	 * @since jEdit 4.1pre5
188	 */
189	public TextAreaExtension[] getExtensions()
190	{
191		return extensionMgr.getExtensions();
192	} //}}}
193
194	//{{{ getToolTipText() method
195	/**
196	 * Returns the tool tip to display at the specified location.
197	 * @param evt The mouse event
198	 */
199	public String getToolTipText(MouseEvent evt)
200	{
201		if(textArea.getBuffer().isLoading())
202			return null;
203
204		return extensionMgr.getToolTipText(evt.getX(),evt.getY());
205	} //}}}
206
207	//{{{ setBorder() method
208	/**
209	 * Convenience method for setting a default matte border on the right
210	 * with the specified border width and color
211	 * @param width The border width (in pixels)
212	 * @param color1 The focused border color
213	 * @param color2 The unfocused border color
214	 * @param color3 The gutter/text area gap color
215	 */
216	public void setBorder(int width, Color color1, Color color2, Color color3)
217	{
218		this.borderWidth = width;
219
220		focusBorder = new CompoundBorder(new MatteBorder(0,0,0,width,color3),
221			new MatteBorder(0,0,0,width,color1));
222		noFocusBorder = new CompoundBorder(new MatteBorder(0,0,0,width,color3),
223			new MatteBorder(0,0,0,width,color2));
224		updateBorder();
225	} //}}}
226
227	//{{{ updateBorder() method
228	/**
229	 * Sets the border differently if the text area has focus or not.
230	 */
231	public void updateBorder()
232	{
233		if (textArea.hasFocus())
234			setBorder(focusBorder);
235		else
236			setBorder(noFocusBorder);
237	} //}}}
238
239	//{{{ setBorder() method
240	/*
241	 * JComponent.setBorder(Border) is overridden here to cache the left
242	 * inset of the border (if any) to avoid having to fetch it during every
243	 * repaint.
244	 */
245	public void setBorder(Border border)
246	{
247		super.setBorder(border);
248
249		if (border == null)
250		{
251			collapsedSize.width = 0;
252			collapsedSize.height = 0;
253		}
254		else
255		{
256			Insets insets = border.getBorderInsets(this);
257			collapsedSize.width = FOLD_MARKER_SIZE + insets.right;
258			collapsedSize.height = gutterSize.height
259				= insets.top + insets.bottom;
260			gutterSize.width = FOLD_MARKER_SIZE + insets.right
261				+ fm.stringWidth("12345");
262		}
263
264		revalidate();
265	} //}}}
266
267	//{{{ setFont() method
268	/*
269	 * JComponent.setFont(Font) is overridden here to cache the baseline for
270	 * the font. This avoids having to get the font metrics during every
271	 * repaint.
272	 */
273	public void setFont(Font font)
274	{
275		super.setFont(font);
276
277		fm = getFontMetrics(font);
278
279		baseline = fm.getAscent();
280
281		Border border = getBorder();
282		if(border != null)
283		{
284			gutterSize.width = FOLD_MARKER_SIZE
285				+ border.getBorderInsets(this).right
286				+ fm.stringWidth("12345");
287			revalidate();
288		}
289	} //}}}
290
291	//{{{ Getters and setters
292
293	//{{{ getHighlightedForeground() method
294	/**
295	 * Get the foreground color for highlighted line numbers
296	 * @return The highlight color
297	 */
298	public Color getHighlightedForeground()
299	{
300		return intervalHighlight;
301	} //}}}
302
303	//{{{ setHighlightedForeground() method
304	public void setHighlightedForeground(Color highlight)
305	{
306		intervalHighlight = highlight;
307	} //}}}
308
309	//{{{ getCurrentLineForeground() method
310	public Color getCurrentLineForeground()
311 	{
312		return currentLineHighlight;
313	} //}}}
314
315	//{{{ setCurrentLineForeground() method
316	public void setCurrentLineForeground(Color highlight)
317	{
318		currentLineHighlight = highlight;
319 	} //}}}
320
321	//{{{ getFoldColor() method
322	public Color getFoldColor()
323 	{
324		return foldColor;
325	} //}}}
326
327	//{{{ setFoldColor() method
328	public void setFoldColor(Color foldColor)
329	{
330		this.foldColor = foldColor;
331 	} //}}}
332
333	//{{{ getPreferredSize() method
334	/*
335	 * Component.getPreferredSize() is overridden here to support the
336	 * collapsing behavior.
337	 */
338	public Dimension getPreferredSize()
339	{
340		if (expanded)
341			return gutterSize;
342		else
343			return collapsedSize;
344	} //}}}
345
346	//{{{ getMinimumSize() method
347	public Dimension getMinimumSize()
348	{
349		return getPreferredSize();
350	} //}}}
351
352	//{{{ getLineNumberAlignment() method
353	/**
354	 * Identifies whether the horizontal alignment of the line numbers.
355	 * @return Gutter.RIGHT, Gutter.CENTER, Gutter.LEFT
356	 */
357	public int getLineNumberAlignment()
358	{
359		return alignment;
360	} //}}}
361
362	//{{{ setLineNumberAlignment() method
363	/**
364	 * Sets the horizontal alignment of the line numbers.
365	 * @param alignment Gutter.RIGHT, Gutter.CENTER, Gutter.LEFT
366	 */
367	public void setLineNumberAlignment(int alignment)
368	{
369		if (this.alignment == alignment) return;
370
371		this.alignment = alignment;
372
373		repaint();
374	} //}}}
375
376	//{{{ isExpanded() method
377	/**
378	 * Identifies whether the gutter is collapsed or expanded.
379	 * @return true if the gutter is expanded, false if it is collapsed
380	 */
381	public boolean isExpanded()
382	{
383		return expanded;
384	} //}}}
385
386	//{{{ setExpanded() method
387	/**
388	 * Sets whether the gutter is collapsed or expanded and force the text
389	 * area to update its layout if there is a change.
390	 * @param expanded true if the gutter is expanded,
391	 *                   false if it is collapsed
392	 */
393	public void setExpanded(boolean expanded)
394	{
395		if (this.expanded == expanded) return;
396
397		this.expanded = expanded;
398
399		textArea.revalidate();
400	} //}}}
401
402	//{{{ toggleExpanded() method
403	/**
404	 * Toggles whether the gutter is collapsed or expanded.
405	 */
406	public void toggleExpanded()
407	{
408		setExpanded(!expanded);
409	} //}}}
410
411	//{{{ getHighlightInterval() method
412	/**
413	 * Sets the number of lines between highlighted line numbers.
414	 * @return The number of lines between highlighted line numbers or
415	 *          zero if highlighting is disabled
416	 */
417	public int getHighlightInterval()
418	{
419		return interval;
420	} //}}}
421
422	//{{{ setHighlightInterval() method
423	/**
424	 * Sets the number of lines between highlighted line numbers. Any value
425	 * less than or equal to one will result in highlighting being disabled.
426	 * @param interval The number of lines between highlighted line numbers
427	 */
428	public void setHighlightInterval(int interval)
429	{
430		if (interval <= 1) interval = 0;
431		this.interval = interval;
432		repaint();
433	} //}}}
434
435	//{{{ isCurrentLineHighlightEnabled() method
436	public boolean isCurrentLineHighlightEnabled()
437	{
438		return currentLineHighlightEnabled;
439	} //}}}
440
441	//{{{ setCurrentLineHighlightEnabled() method
442	public void setCurrentLineHighlightEnabled(boolean enabled)
443	{
444		if (currentLineHighlightEnabled == enabled) return;
445
446		currentLineHighlightEnabled = enabled;
447
448		repaint();
449	} //}}}
450
451	//{{{ getStructureHighlightColor() method
452	/**
453	 * Returns the structure highlight color.
454	 * @since jEdit 4.2pre3
455	 */
456	public final Color getStructureHighlightColor()
457	{
458		return structureHighlightColor;
459	} //}}}
460
461	//{{{ setStructureHighlightColor() method
462	/**
463	 * Sets the structure highlight color.
464	 * @param structureHighlightColor The structure highlight color
465	 * @since jEdit 4.2pre3
466	 */
467	public final void setStructureHighlightColor(Color structureHighlightColor)
468	{
469		this.structureHighlightColor = structureHighlightColor;
470		repaint();
471	} //}}}
472
473	//{{{ isStructureHighlightEnabled() method
474	/**
475	 * Returns true if structure highlighting is enabled, false otherwise.
476	 * @since jEdit 4.2pre3
477	 */
478	public final boolean isStructureHighlightEnabled()
479	{
480		return structureHighlight;
481	} //}}}
482
483	//{{{ setStructureHighlightEnabled() method
484	/**
485	 * Enables or disables structure highlighting.
486	 * @param structureHighlight True if structure highlighting should be
487	 * enabled, false otherwise
488	 * @since jEdit 4.2pre3
489	 */
490	public final void setStructureHighlightEnabled(boolean structureHighlight)
491	{
492		this.structureHighlight = structureHighlight;
493		repaint();
494	} //}}}
495
496	//}}}
497
498	//{{{ Private members
499
500	//{{{ Instance variables
501	private static final int FOLD_MARKER_SIZE = 12;
502
503	private View view;
504	private JEditTextArea textArea;
505
506	private ExtensionManager extensionMgr;
507
508	private int baseline;
509
510	private Dimension gutterSize = new Dimension(0,0);
511	private Dimension collapsedSize = new Dimension(0,0);
512
513	private Color intervalHighlight;
514	private Color currentLineHighlight;
515	private Color foldColor;
516
517	private FontMetrics fm;
518
519	private int alignment;
520
521	private int interval;
522	private boolean currentLineHighlightEnabled;
523	private boolean expanded;
524
525	private boolean structureHighlight;
526	private Color structureHighlightColor;
527
528	private int borderWidth;
529	private Border focusBorder, noFocusBorder;
530	//}}}
531
532	//{{{ paintLine() method
533	private void paintLine(Graphics2D gfx, int line, int y)
534	{
535		JEditBuffer buffer = textArea.getBuffer();
536		if(buffer.isLoading())
537			return;
538
539		int lineHeight = textArea.getPainter().getFontMetrics()
540			.getHeight();
541
542		ChunkCache.LineInfo info = textArea.chunkCache.getLineInfo(line);
543		int physicalLine = info.physicalLine;
544
545		// Skip lines beyond EOF
546		if(physicalLine == -1)
547			return;
548
549		//{{{ Paint fold triangles
550		if(info.firstSubregion && buffer.isFoldStart(physicalLine))
551		{
552			int _y = y + lineHeight / 2;
553			gfx.setColor(foldColor);
554			if(textArea.displayManager
555				.isLineVisible(physicalLine + 1))
556			{
557				gfx.drawLine(1,_y - 3,10,_y - 3);
558				gfx.drawLine(2,_y - 2,9,_y - 2);
559				gfx.drawLine(3,_y - 1,8,_y - 1);
560				gfx.drawLine(4,_y,7,_y);
561				gfx.drawLine(5,_y + 1,6,_y + 1);
562			}
563			else
564			{
565				gfx.drawLine(4,_y - 5,4,_y + 4);
566				gfx.drawLine(5,_y - 4,5,_y + 3);
567				gfx.drawLine(6,_y - 3,6,_y + 2);
568				gfx.drawLine(7,_y - 2,7,_y + 1);
569				gfx.drawLine(8,_y - 1,8,_y);
570			}
571		}
572		else if(info.lastSubregion && buffer.isFoldEnd(physicalLine))
573		{
574			gfx.setColor(foldColor);
575			int _y = y + lineHeight / 2;
576			gfx.drawLine(4,_y,4,_y + 3);
577			gfx.drawLine(4,_y + 3,7,_y + 3);
578		} //}}}
579		//{{{ Paint bracket scope
580		else if(structureHighlight)
581		{
582			StructureMatcher.Match match = textArea.getStructureMatch();
583			int caretLine = textArea.getCaretLine();
584
585			if(textArea.isStructureHighlightVisible()
586				&& physicalLine >= Math.min(caretLine,match.startLine)
587				&& physicalLine <= Math.max(caretLine,match.startLine))
588			{
589				int caretScreenLine;
590				if(caretLine > textArea.getLastPhysicalLine())
591					caretScreenLine = Integer.MAX_VALUE;
592				else if(textArea.displayManager.isLineVisible(
593						textArea.getCaretLine()))
594				{
595					caretScreenLine = textArea
596						.getScreenLineOfOffset(
597						textArea.getCaretPosition());
598				}
599				else
600				{
601					caretScreenLine = -1;
602				}
603
604				int structScreenLine;
605				if(match.startLine > textArea.getLastPhysicalLine())
606					structScreenLine = Integer.MAX_VALUE;
607				else if(textArea.displayManager.isLineVisible(
608						match.startLine))
609				{
610					structScreenLine = textArea
611						.getScreenLineOfOffset(
612						match.start);
613				}
614				else
615				{
616					structScreenLine = -1;
617				}
618
619				if(caretScreenLine > structScreenLine)
620				{
621					int tmp = caretScreenLine;
622					caretScreenLine = structScreenLine;
623					structScreenLine = tmp;
624				}
625
626				gfx.setColor(structureHighlightColor);
627				if(structScreenLine == caretScreenLine)
628				{
629					// do nothing
630				}
631				// draw |^
632				else if(line == caretScreenLine)
633				{
634					gfx.fillRect(5,
635						y
636						+ lineHeight / 2,
637						5,
638						2);
639					gfx.fillRect(5,
640						y
641						+ lineHeight / 2,
642						2,
643						lineHeight - lineHeight / 2);
644				}
645				// draw |_
646				else if(line == structScreenLine)
647				{
648					gfx.fillRect(5,
649						y,
650						2,
651						lineHeight / 2);
652					gfx.fillRect(5,
653						y + lineHeight / 2,
654						5,
655						2);
656				}
657				// draw |
658				else if(line > caretScreenLine
659					&& line < structScreenLine)
660				{
661					gfx.fillRect(5,
662						y,
663						2,
664						lineHeight);
665				}
666			}
667		} //}}}
668
669		//{{{ Paint line numbers
670		if(info.firstSubregion && expanded)
671		{
672			String number = Integer.toString(physicalLine + 1);
673
674			int offset;
675			switch (alignment)
676			{
677			case RIGHT:
678				offset = gutterSize.width - collapsedSize.width
679					- (fm.stringWidth(number) + 1);
680				break;
681			case CENTER:
682				offset = ((gutterSize.width - collapsedSize.width)
683					- fm.stringWidth(number)) / 2;
684				break;
685			case LEFT: default:
686				offset = 0;
687				break;
688			}
689
690			boolean highlightCurrentLine = currentLineHighlightEnabled
691				&& textArea.getSelectionCount() == 0;
692			if (physicalLine == textArea.getCaretLine() && highlightCurrentLine)
693			{
694				gfx.setColor(currentLineHighlight);
695			}
696			else if (interval > 1 && (line
697				+ textArea.getFirstLine() + 1)
698				% interval == 0)
699				gfx.setColor(intervalHighlight);
700			else
701				gfx.setColor(getForeground());
702
703			gfx.drawString(number, FOLD_MARKER_SIZE + offset,
704				baseline + y);
705		} //}}}
706	} //}}}
707
708	//}}}
709
710	//{{{ MouseHandler class
711	class MouseHandler extends MouseInputAdapter
712	{
713		MouseActions mouseActions = new MouseActions("gutter");
714		boolean drag;
715		int toolTipInitialDelay, toolTipReshowDelay;
716
717		//{{{ mouseEntered() method
718		public void mouseEntered(MouseEvent e)
719		{
720			ToolTipManager ttm = ToolTipManager.sharedInstance();
721			toolTipInitialDelay = ttm.getInitialDelay();
722			toolTipReshowDelay = ttm.getReshowDelay();
723			ttm.setInitialDelay(0);
724			ttm.setReshowDelay(0);
725		} //}}}
726
727		//{{{ mouseExited() method
728		public void mouseExited(MouseEvent evt)
729		{
730			ToolTipManager ttm = ToolTipManager.sharedInstance();
731			ttm.setInitialDelay(toolTipInitialDelay);
732			ttm.setReshowDelay(toolTipReshowDelay);
733		} //}}}
734
735		//{{{ mousePressed() method
736		public void mousePressed(MouseEvent e)
737		{
738			textArea.requestFocus();
739
740			if(GUIUtilities.isPopupTrigger(e)
741				|| e.getX() >= getWidth() - borderWidth * 2)
742			{
743				e.translatePoint(-getWidth(),0);
744				textArea.mouseHandler.mousePressed(e);
745				drag = true;
746			}
747			else
748			{
749				JEditBuffer buffer = textArea.getBuffer();
750
751				int screenLine = e.getY() / textArea.getPainter()
752					.getFontMetrics().getHeight();
753
754				int line = textArea.chunkCache.getLineInfo(screenLine)
755					.physicalLine;
756
757				if(line == -1)
758					return;
759
760				//{{{ Determine action
761				String defaultAction;
762				String variant;
763				if(buffer.isFoldStart(line))
764				{
765					defaultAction = "toggle-fold";
766					variant = "fold";
767				}
768				else if(structureHighlight
769					&& textArea.isStructureHighlightVisible()
770					&& textArea.lineInStructureScope(line))
771				{
772					defaultAction = "match-struct";
773					variant = "struct";
774				}
775				else
776					return;
777
778				String action = mouseActions.getActionForEvent(
779					e,variant);
780
781				if(action == null)
782					action = defaultAction;
783				//}}}
784
785				//{{{ Handle actions
786				StructureMatcher.Match match = textArea
787					.getStructureMatch();
788
789				if(action.equals("select-fold"))
790				{
791					textArea.displayManager.expandFold(line,true);
792					textArea.selectFold(line);
793				}
794				else if(action.equals("narrow-fold"))
795				{
796					int[] lines = buffer.getFoldAtLine(line);
797					textArea.displayManager.narrow(lines[0],lines[1]);
798				}
799				else if(action.startsWith("toggle-fold"))
800				{
801					if(textArea.displayManager
802						.isLineVisible(line + 1))
803					{
804						textArea.displayManager.collapseFold(line);
805					}
806					else
807					{
808						if(action.endsWith("-fully"))
809						{
810							textArea.displayManager
811								.expandFold(line,
812								true);
813						}
814						else
815						{
816							textArea.displayManager
817								.expandFold(line,
818								false);
819						}
820					}
821				}
822				else if(action.equals("match-struct"))
823				{
824					if(match != null)
825						textArea.setCaretPosition(match.end);
826				}
827				else if(action.equals("select-struct"))
828				{
829					if(match != null)
830					{
831						match.matcher.selectMatch(
832							textArea);
833					}
834				}
835				else if(action.equals("narrow-struct"))
836				{
837					if(match != null)
838					{
839						int start = Math.min(
840							match.startLine,
841							textArea.getCaretLine());
842						int end = Math.max(
843							match.endLine,
844							textArea.getCaretLine());
845						textArea.displayManager.narrow(start,end);
846					}
847				} //}}}
848			}
849		} //}}}
850
851		//{{{ mouseDragged() method
852		public void mouseDragged(MouseEvent e)
853		{
854			if(drag /* && e.getX() >= getWidth() - borderWidth * 2 */)
855			{
856				e.translatePoint(-getWidth(),0);
857				textArea.mouseHandler.mouseDragged(e);
858			}
859		} //}}}
860
861		//{{{ mouseReleased() method
862		public void mouseReleased(MouseEvent e)
863		{
864			if(drag && e.getX() >= getWidth() - borderWidth * 2)
865			{
866				e.translatePoint(-getWidth(),0);
867				textArea.mouseHandler.mouseReleased(e);
868			}
869
870			drag = false;
871		} //}}}
872	} //}}}
873}