/jEdit/tags/jedit-4-2-final/org/gjt/sp/jedit/textarea/Gutter.java

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