PageRenderTime 52ms CodeModel.GetById 17ms RepoModel.GetById 0ms app.codeStats 1ms

/jEdit/tags/jedit-4-5-pre1/org/gjt/sp/jedit/textarea/Gutter.java

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