PageRenderTime 51ms CodeModel.GetById 22ms RepoModel.GetById 0ms app.codeStats 1ms

/jEdit/tags/jedit-4-2-pre4/org/gjt/sp/jedit/textarea/TextAreaPainter.java

#
Java | 1257 lines | 710 code | 156 blank | 391 comment | 118 complexity | cabe5074949ddaf73e055d91566fb2c4 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. * TextAreaPainter.java - Paints the text area
  3. * :tabSize=8:indentSize=8:noTabs=false:
  4. * :folding=explicit:collapseFolds=1:
  5. *
  6. * Copyright (C) 1999, 2003 Slava Pestov
  7. *
  8. * This program is free software; you can redistribute it and/or
  9. * modify it under the terms of the GNU General Public License
  10. * as published by the Free Software Foundation; either version 2
  11. * of the License, or any later version.
  12. *
  13. * This program is distributed in the hope that it will be useful,
  14. * but WITHOUT ANY WARRANTY; without even the implied warranty of
  15. * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
  16. * GNU General Public License for more details.
  17. *
  18. * You should have received a copy of the GNU General Public License
  19. * along with this program; if not, write to the Free Software
  20. * Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA.
  21. */
  22. package org.gjt.sp.jedit.textarea;
  23. //{{{ Imports
  24. import javax.swing.text.*;
  25. import javax.swing.JComponent;
  26. import java.awt.event.MouseEvent;
  27. import java.awt.font.*;
  28. import java.awt.*;
  29. import java.util.HashMap;
  30. import org.gjt.sp.jedit.buffer.IndentFoldHandler;
  31. import org.gjt.sp.jedit.syntax.*;
  32. import org.gjt.sp.jedit.Buffer;
  33. import org.gjt.sp.jedit.Debug;
  34. import org.gjt.sp.jedit.OperatingSystem;
  35. import org.gjt.sp.util.Log;
  36. //}}}
  37. /**
  38. * The text area painter is the component responsible for displaying the
  39. * text of the current buffer. The only methods in this class that should
  40. * 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 Slava Pestov
  50. * @version $Id: TextAreaPainter.java 4805 2003-06-22 01:09:47Z spestov $
  51. */
  52. public class TextAreaPainter extends JComponent implements TabExpander
  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. * Below selection layer. The JDiff plugin will use this.
  63. * @see #addExtension(int,TextAreaExtension)
  64. * @since jEdit 4.0pre4
  65. */
  66. public static final int BACKGROUND_LAYER = -60;
  67. /**
  68. * The line highlight and collapsed fold highlight layer.
  69. * @see #addExtension(int,TextAreaExtension)
  70. * @since jEdit 4.0pre7
  71. */
  72. public static final int LINE_BACKGROUND_LAYER = -50;
  73. /**
  74. * Below selection layer.
  75. * @see #addExtension(int,TextAreaExtension)
  76. * @since jEdit 4.0pre4
  77. */
  78. public static final int BELOW_SELECTION_LAYER = -40;
  79. /**
  80. * Selection layer. Most extensions will be above this layer, but some
  81. * (eg, JDiff) will want to be below the selection.
  82. * @see #addExtension(int,TextAreaExtension)
  83. * @since jEdit 4.0pre4
  84. */
  85. public static final int SELECTION_LAYER = -30;
  86. /**
  87. * Wrap guide layer. Most extensions will be above this layer.
  88. * @since jEdit 4.0pre4
  89. */
  90. public static final int WRAP_GUIDE_LAYER = -20;
  91. /**
  92. * Below most extensions layer.
  93. * @see #addExtension(int,TextAreaExtension)
  94. * @since jEdit 4.0pre4
  95. */
  96. public static final int BELOW_MOST_EXTENSIONS_LAYER = -10;
  97. /**
  98. * Default extension layer. This is above the wrap guide but below the
  99. * structure highlight.
  100. * @since jEdit 4.0pre4
  101. */
  102. public static final int DEFAULT_LAYER = 0;
  103. /**
  104. * Block caret layer. Most extensions will be below this layer.
  105. * @since jEdit 4.2pre1
  106. */
  107. public static final int BLOCK_CARET_LAYER = 50;
  108. /**
  109. * Bracket highlight layer. Most extensions will be below this layer.
  110. * @since jEdit 4.0pre4
  111. */
  112. public static final int BRACKET_HIGHLIGHT_LAYER = 100;
  113. /**
  114. * Text layer. Most extensions will be below this layer.
  115. * @since jEdit 4.2pre1
  116. */
  117. public static final int TEXT_LAYER = 200;
  118. /**
  119. * Caret layer. Most extensions will be below this layer.
  120. * @since jEdit 4.2pre1
  121. */
  122. public static final int CARET_LAYER = 300;
  123. /**
  124. * Highest possible layer.
  125. * @since jEdit 4.0pre4
  126. */
  127. public static final int HIGHEST_LAYER = Integer.MAX_VALUE;
  128. //}}}
  129. //{{{ setBounds() method
  130. /**
  131. * It is a bad idea to override this, but we need to get the component
  132. * event before the first repaint.
  133. */
  134. public void setBounds(int x, int y, int width, int height)
  135. {
  136. if(x == getX() && y == getY() && width == getWidth()
  137. && height == getHeight())
  138. {
  139. return;
  140. }
  141. super.setBounds(x,y,width,height);
  142. textArea.recalculateVisibleLines();
  143. if(textArea.getBuffer().isLoaded())
  144. textArea.recalculateLastPhysicalLine();
  145. textArea.propertiesChanged();
  146. textArea.scrollBarsInitialized = true;
  147. } //}}}
  148. //{{{ isManagingFocus() method
  149. /**
  150. * Returns if this component can be traversed by pressing the
  151. * Tab key. This returns false.
  152. */
  153. public boolean isManagingFocus()
  154. {
  155. return false;
  156. } //}}}
  157. //{{{ getFocusTraversalKeysEnabled() method
  158. /**
  159. * Makes the tab key work in Java 1.4.
  160. * @since jEdit 3.2pre4
  161. */
  162. public boolean getFocusTraversalKeysEnabled()
  163. {
  164. return false;
  165. } //}}}
  166. //{{{ Getters and setters
  167. //{{{ getStyles() method
  168. /**
  169. * Returns the syntax styles used to paint colorized text. Entry <i>n</i>
  170. * will be used to paint tokens with id = <i>n</i>.
  171. * @see org.gjt.sp.jedit.syntax.Token
  172. */
  173. public final SyntaxStyle[] getStyles()
  174. {
  175. return styles;
  176. } //}}}
  177. //{{{ setStyles() method
  178. /**
  179. * Sets the syntax styles used to paint colorized text. Entry <i>n</i>
  180. * will be used to paint tokens with id = <i>n</i>.
  181. * @param styles The syntax styles
  182. * @see org.gjt.sp.jedit.syntax.Token
  183. */
  184. public final void setStyles(SyntaxStyle[] styles)
  185. {
  186. // assumed this is called after a font render context is set up.
  187. // changing font render context settings without a setStyles()
  188. // call will not reset cached monospaced font info.
  189. fonts.clear();
  190. this.styles = styles;
  191. styles[Token.NULL] = new SyntaxStyle(getForeground(),null,getFont());
  192. for(int i = 0; i < styles.length; i++)
  193. {
  194. styles[i].setCharWidth(getCharWidth(styles[i].getFont()));
  195. }
  196. repaint();
  197. } //}}}
  198. //{{{ getCaretColor() method
  199. /**
  200. * Returns the caret color.
  201. */
  202. public final Color getCaretColor()
  203. {
  204. return caretColor;
  205. } //}}}
  206. //{{{ setCaretColor() method
  207. /**
  208. * Sets the caret color.
  209. * @param caretColor The caret color
  210. */
  211. public final void setCaretColor(Color caretColor)
  212. {
  213. this.caretColor = caretColor;
  214. if(textArea.getBuffer() != null)
  215. textArea.invalidateLine(textArea.getCaretLine());
  216. } //}}}
  217. //{{{ getSelectionColor() method
  218. /**
  219. * Returns the selection color.
  220. */
  221. public final Color getSelectionColor()
  222. {
  223. return selectionColor;
  224. } //}}}
  225. //{{{ setSelectionColor() method
  226. /**
  227. * Sets the selection color.
  228. * @param selectionColor The selection color
  229. */
  230. public final void setSelectionColor(Color selectionColor)
  231. {
  232. this.selectionColor = selectionColor;
  233. if(textArea.getBuffer() != null)
  234. textArea.invalidateSelectedLines();
  235. } //}}}
  236. //{{{ getMultipleSelectionColor() method
  237. /**
  238. * Returns the multiple selection color.
  239. * @since jEdit 4.2pre1
  240. */
  241. public final Color getMultipleSelectionColor()
  242. {
  243. return multipleSelectionColor;
  244. } //}}}
  245. //{{{ setMultipleSelectionColor() method
  246. /**
  247. * Sets the multiple selection color.
  248. * @param multipleSelectionColor The multiple selection color
  249. * @since jEdit 4.2pre1
  250. */
  251. public final void setMultipleSelectionColor(Color multipleSelectionColor)
  252. {
  253. this.multipleSelectionColor = multipleSelectionColor;
  254. if(textArea.getBuffer() != null)
  255. textArea.invalidateSelectedLines();
  256. } //}}}
  257. //{{{ getLineHighlightColor() method
  258. /**
  259. * Returns the line highlight color.
  260. */
  261. public final Color getLineHighlightColor()
  262. {
  263. return lineHighlightColor;
  264. } //}}}
  265. //{{{ setLineHighlightColor() method
  266. /**
  267. * Sets the line highlight color.
  268. * @param lineHighlightColor The line highlight color
  269. */
  270. public final void setLineHighlightColor(Color lineHighlightColor)
  271. {
  272. this.lineHighlightColor = lineHighlightColor;
  273. if(textArea.getBuffer() != null)
  274. textArea.invalidateLine(textArea.getCaretLine());
  275. } //}}}
  276. //{{{ isLineHighlightEnabled() method
  277. /**
  278. * Returns true if line highlight is enabled, false otherwise.
  279. */
  280. public final boolean isLineHighlightEnabled()
  281. {
  282. return lineHighlight;
  283. } //}}}
  284. //{{{ setLineHighlightEnabled() method
  285. /**
  286. * Enables or disables current line highlighting.
  287. * @param lineHighlight True if current line highlight should be enabled,
  288. * false otherwise
  289. */
  290. public final void setLineHighlightEnabled(boolean lineHighlight)
  291. {
  292. this.lineHighlight = lineHighlight;
  293. if(textArea.getBuffer() != null)
  294. textArea.invalidateSelectedLines();
  295. } //}}}
  296. //{{{ getStructureHighlightColor() method
  297. /**
  298. * Returns the structure highlight color.
  299. * @since jEdit 4.2pre3
  300. */
  301. public final Color getStructureHighlightColor()
  302. {
  303. return structureHighlightColor;
  304. } //}}}
  305. //{{{ setStructureHighlightColor() method
  306. /**
  307. * Sets the structure highlight color.
  308. * @param structureHighlightColor The bracket highlight color
  309. * @since jEdit 4.2pre3
  310. */
  311. public final void setStructureHighlightColor(
  312. Color structureHighlightColor)
  313. {
  314. this.structureHighlightColor = structureHighlightColor;
  315. StructureMatcher.Match match = textArea.getStructureMatch();
  316. if(match != null)
  317. {
  318. textArea.invalidateLineRange(
  319. match.startLine,match.endLine
  320. );
  321. }
  322. } //}}}
  323. //{{{ isStructureHighlightEnabled() method
  324. /**
  325. * Returns true if structure highlighting is enabled, false otherwise.
  326. * @since jEdit 4.2pre3
  327. */
  328. public final boolean isStructureHighlightEnabled()
  329. {
  330. return structureHighlight;
  331. } //}}}
  332. //{{{ setStructureHighlightEnabled() method
  333. /**
  334. * Enables or disables structure highlighting.
  335. * @param structureHighlight True if structure highlighting should be
  336. * enabled, false otherwise
  337. * @since jEdit 4.2pre3
  338. */
  339. public final void setStructureHighlightEnabled(boolean structureHighlight)
  340. {
  341. this.structureHighlight = structureHighlight;
  342. StructureMatcher.Match match = textArea.getStructureMatch();
  343. if(match != null)
  344. {
  345. textArea.invalidateLineRange(
  346. match.startLine,
  347. match.endLine
  348. );
  349. }
  350. } //}}}
  351. //{{{ isBlockCaretEnabled() method
  352. /**
  353. * Returns true if the caret should be drawn as a block, false otherwise.
  354. */
  355. public final boolean isBlockCaretEnabled()
  356. {
  357. return blockCaret;
  358. } //}}}
  359. //{{{ setBlockCaretEnabled() method
  360. /**
  361. * Sets if the caret should be drawn as a block, false otherwise.
  362. * @param blockCaret True if the caret should be drawn as a block,
  363. * false otherwise.
  364. */
  365. public final void setBlockCaretEnabled(boolean blockCaret)
  366. {
  367. this.blockCaret = blockCaret;
  368. extensionMgr.removeExtension(caretExtension);
  369. if(blockCaret)
  370. addExtension(BLOCK_CARET_LAYER,caretExtension);
  371. else
  372. addExtension(CARET_LAYER,caretExtension);
  373. if(textArea.getBuffer() != null)
  374. textArea.invalidateLine(textArea.getCaretLine());
  375. } //}}}
  376. //{{{ getEOLMarkerColor() method
  377. /**
  378. * Returns the EOL marker color.
  379. */
  380. public final Color getEOLMarkerColor()
  381. {
  382. return eolMarkerColor;
  383. } //}}}
  384. //{{{ setEOLMarkerColor() method
  385. /**
  386. * Sets the EOL marker color.
  387. * @param eolMarkerColor The EOL marker color
  388. */
  389. public final void setEOLMarkerColor(Color eolMarkerColor)
  390. {
  391. this.eolMarkerColor = eolMarkerColor;
  392. repaint();
  393. } //}}}
  394. //{{{ getEOLMarkersPainted() method
  395. /**
  396. * Returns true if EOL markers are drawn, false otherwise.
  397. */
  398. public final boolean getEOLMarkersPainted()
  399. {
  400. return eolMarkers;
  401. } //}}}
  402. //{{{ setEOLMarkersPainted() method
  403. /**
  404. * Sets if EOL markers are to be drawn.
  405. * @param eolMarkers True if EOL markers should be drawn, false otherwise
  406. */
  407. public final void setEOLMarkersPainted(boolean eolMarkers)
  408. {
  409. this.eolMarkers = eolMarkers;
  410. repaint();
  411. } //}}}
  412. //{{{ getWrapGuideColor() method
  413. /**
  414. * Returns the wrap guide color.
  415. */
  416. public final Color getWrapGuideColor()
  417. {
  418. return wrapGuideColor;
  419. } //}}}
  420. //{{{ setWrapGuideColor() method
  421. /**
  422. * Sets the wrap guide color.
  423. * @param wrapGuideColor The wrap guide color
  424. */
  425. public final void setWrapGuideColor(Color wrapGuideColor)
  426. {
  427. this.wrapGuideColor = wrapGuideColor;
  428. repaint();
  429. } //}}}
  430. //{{{ isWrapGuidePainted() method
  431. /**
  432. * Returns true if the wrap guide is drawn, false otherwise.
  433. * @since jEdit 4.0pre4
  434. */
  435. public final boolean isWrapGuidePainted()
  436. {
  437. return wrapGuide;
  438. } //}}}
  439. //{{{ setWrapGuidePainted() method
  440. /**
  441. * Sets if the wrap guide is to be drawn.
  442. * @param wrapGuide True if the wrap guide should be drawn, false otherwise
  443. */
  444. public final void setWrapGuidePainted(boolean wrapGuide)
  445. {
  446. this.wrapGuide = wrapGuide;
  447. repaint();
  448. } //}}}
  449. //{{{ getFoldLineStyle() method
  450. /**
  451. * Returns the fold line style. The first element is the style for
  452. * lines with a fold level greater than 3. The remaining elements
  453. * are for fold levels 1 to 3.
  454. */
  455. public final SyntaxStyle[] getFoldLineStyle()
  456. {
  457. return foldLineStyle;
  458. } //}}}
  459. //{{{ setFoldLineStyle() method
  460. /**
  461. * Sets the fold line style. The first element is the style for
  462. * lines with a fold level greater than 3. The remaining elements
  463. * are for fold levels 1 to 3.
  464. * @param foldLineStyle The fold line style
  465. */
  466. public final void setFoldLineStyle(SyntaxStyle[] foldLineStyle)
  467. {
  468. this.foldLineStyle = foldLineStyle;
  469. repaint();
  470. } //}}}
  471. //{{{ setAntiAliasEnabled() method
  472. /**
  473. * Sets if anti-aliasing should be enabled. Has no effect when
  474. * running on Java 1.1.
  475. * @since jEdit 3.2pre6
  476. */
  477. public void setAntiAliasEnabled(boolean antiAlias)
  478. {
  479. this.antiAlias = antiAlias;
  480. updateRenderingHints();
  481. } //}}}
  482. //{{{ isAntiAliasEnabled() method
  483. /**
  484. * Returns if anti-aliasing is enabled.
  485. * @since jEdit 3.2pre6
  486. */
  487. public boolean isAntiAliasEnabled()
  488. {
  489. return antiAlias;
  490. } //}}}
  491. //{{{ setFractionalFontMetricsEnabled() method
  492. /**
  493. * Sets if fractional font metrics should be enabled. Has no effect when
  494. * running on Java 1.1.
  495. * @since jEdit 3.2pre6
  496. */
  497. public void setFractionalFontMetricsEnabled(boolean fracFontMetrics)
  498. {
  499. this.fracFontMetrics = fracFontMetrics;
  500. updateRenderingHints();
  501. } //}}}
  502. //{{{ isFractionalFontMetricsEnabled() method
  503. /**
  504. * Returns if fractional font metrics are enabled.
  505. * @since jEdit 3.2pre6
  506. */
  507. public boolean isFractionalFontMetricsEnabled()
  508. {
  509. return fracFontMetrics;
  510. } //}}}
  511. //{{{ getFontRenderContext() method
  512. /**
  513. * Returns the font render context.
  514. * @since jEdit 4.0pre4
  515. */
  516. public FontRenderContext getFontRenderContext()
  517. {
  518. return fontRenderContext;
  519. } //}}}
  520. //}}}
  521. //{{{ addExtension() method
  522. /**
  523. * Adds a text area extension, which can perform custom painting and
  524. * tool tip handling.
  525. * @param extension The extension
  526. * @since jEdit 4.0pre4
  527. */
  528. public void addExtension(TextAreaExtension extension)
  529. {
  530. extensionMgr.addExtension(DEFAULT_LAYER,extension);
  531. repaint();
  532. } //}}}
  533. //{{{ addExtension() method
  534. /**
  535. * Adds a text area extension, which can perform custom painting and
  536. * tool tip handling.
  537. * @param layer The layer to add the extension to. Note that more than
  538. * extension can share the same layer.
  539. * @param extension The extension
  540. * @since jEdit 4.0pre4
  541. */
  542. public void addExtension(int layer, TextAreaExtension extension)
  543. {
  544. extensionMgr.addExtension(layer,extension);
  545. repaint();
  546. } //}}}
  547. //{{{ removeExtension() method
  548. /**
  549. * Removes a text area extension. It will no longer be asked to
  550. * perform custom painting and tool tip handling.
  551. * @param extension The extension
  552. * @since jEdit 4.0pre4
  553. */
  554. public void removeExtension(TextAreaExtension extension)
  555. {
  556. extensionMgr.removeExtension(extension);
  557. repaint();
  558. } //}}}
  559. //{{{ getExtensions() method
  560. /**
  561. * Returns an array of registered text area extensions. Useful for
  562. * debugging purposes.
  563. * @since jEdit 4.1pre5
  564. */
  565. public TextAreaExtension[] getExtensions()
  566. {
  567. return extensionMgr.getExtensions();
  568. } //}}}
  569. //{{{ getToolTipText() method
  570. /**
  571. * Returns the tool tip to display at the specified location.
  572. * @param evt The mouse event
  573. */
  574. public String getToolTipText(MouseEvent evt)
  575. {
  576. if(!textArea.getBuffer().isLoaded())
  577. return null;
  578. return extensionMgr.getToolTipText(evt.getX(),evt.getY());
  579. } //}}}
  580. //{{{ getFontMetrics() method
  581. /**
  582. * Returns the font metrics used by this component.
  583. */
  584. public FontMetrics getFontMetrics()
  585. {
  586. return fm;
  587. } //}}}
  588. //{{{ setFont() method
  589. /**
  590. * Sets the font for this component. This is overridden to update the
  591. * cached font metrics and to recalculate which lines are visible.
  592. * @param font The font
  593. */
  594. public void setFont(Font font)
  595. {
  596. super.setFont(font);
  597. fm = getFontMetrics(font);
  598. textArea.recalculateVisibleLines();
  599. textArea.propertiesChanged();
  600. } //}}}
  601. //{{{ paintComponent() method
  602. /**
  603. * Repaints the text.
  604. * @param g The graphics context
  605. */
  606. public void paintComponent(Graphics _gfx)
  607. {
  608. Graphics2D gfx = (Graphics2D)_gfx;
  609. gfx.setRenderingHints(renderingHints);
  610. fontRenderContext = gfx.getFontRenderContext();
  611. Rectangle clipRect = gfx.getClipBounds();
  612. gfx.setColor(getBackground());
  613. gfx.fillRect(clipRect.x,clipRect.y,clipRect.width,clipRect.height);
  614. Buffer buffer = textArea.getBuffer();
  615. if(!buffer.isLoaded())
  616. return;
  617. int x = textArea.getHorizontalOffset();
  618. int height = fm.getHeight();
  619. int firstInvalid = clipRect.y / height;
  620. // Because the clipRect's height is usually an even multiple
  621. // of the font height, we subtract 1 from it, otherwise one
  622. // too many lines will always be painted.
  623. int lastInvalid = (clipRect.y + clipRect.height - 1) / height;
  624. if(Debug.PAINT_TIMER && lastInvalid - firstInvalid >= 1)
  625. Log.log(Log.DEBUG,this,"repainting " + (lastInvalid - firstInvalid) + " lines");
  626. int y = (clipRect.y - clipRect.y % height);
  627. textArea.updateMaxHorizontalScrollWidth = false;
  628. extensionMgr.paintScreenLineRange(textArea,gfx,
  629. firstInvalid,lastInvalid,y,height);
  630. if(textArea.updateMaxHorizontalScrollWidth)
  631. textArea.updateMaxHorizontalScrollWidth();
  632. if(buffer.isNextLineRequested())
  633. {
  634. int h = clipRect.y + clipRect.height;
  635. textArea.chunkCache.invalidateChunksFrom(lastInvalid + 1);
  636. repaint(0,h,getWidth(),getHeight() - h);
  637. }
  638. textArea.displayManager._notifyScreenLineChanges();
  639. } //}}}
  640. //{{{ nextTabStop() method
  641. /**
  642. * Implementation of TabExpander interface. Returns next tab stop after
  643. * a specified point.
  644. * @param x The x co-ordinate
  645. * @param tabOffset Ignored
  646. * @return The next tab stop after <i>x</i>
  647. */
  648. public float nextTabStop(float x, int tabOffset)
  649. {
  650. int ntabs = (int)(x / textArea.tabSize);
  651. return (ntabs + 1) * textArea.tabSize;
  652. } //}}}
  653. //{{{ getPreferredSize() method
  654. /**
  655. * Returns the painter's preferred size.
  656. */
  657. public Dimension getPreferredSize()
  658. {
  659. Dimension dim = new Dimension();
  660. char[] foo = new char[80];
  661. for(int i = 0; i < foo.length; i++)
  662. foo[i] = ' ';
  663. dim.width = (int)(getFont().getStringBounds(foo,0,foo.length,
  664. fontRenderContext).getWidth());
  665. dim.height = fm.getHeight() * 25;
  666. return dim;
  667. } //}}}
  668. //{{{ getMinimumSize() method
  669. /**
  670. * Returns the painter's minimum size.
  671. */
  672. public Dimension getMinimumSize()
  673. {
  674. return getPreferredSize();
  675. } //}}}
  676. //{{{ Package-private members
  677. //{{{ Instance variables
  678. /* package-private since they are accessed by inner classes and we
  679. * want this to be fast */
  680. JEditTextArea textArea;
  681. SyntaxStyle[] styles;
  682. Color caretColor;
  683. Color selectionColor;
  684. Color multipleSelectionColor;
  685. Color lineHighlightColor;
  686. Color structureHighlightColor;
  687. Color eolMarkerColor;
  688. Color wrapGuideColor;
  689. SyntaxStyle[] foldLineStyle;
  690. boolean blockCaret;
  691. boolean lineHighlight;
  692. boolean structureHighlight;
  693. boolean eolMarkers;
  694. boolean wrapGuide;
  695. boolean antiAlias;
  696. boolean fracFontMetrics;
  697. // should try to use this as little as possible.
  698. FontMetrics fm;
  699. //}}}
  700. //{{{ TextAreaPainter constructor
  701. /**
  702. * Creates a new painter. Do not create instances of this class
  703. * directly.
  704. */
  705. TextAreaPainter(JEditTextArea textArea)
  706. {
  707. enableEvents(AWTEvent.FOCUS_EVENT_MASK
  708. | AWTEvent.KEY_EVENT_MASK
  709. | AWTEvent.MOUSE_EVENT_MASK);
  710. this.textArea = textArea;
  711. fonts = new HashMap();
  712. extensionMgr = new ExtensionManager();
  713. setAutoscrolls(true);
  714. setOpaque(true);
  715. setRequestFocusEnabled(false);
  716. setCursor(Cursor.getPredefinedCursor(Cursor.TEXT_CURSOR));
  717. fontRenderContext = new FontRenderContext(null,false,false);
  718. addExtension(LINE_BACKGROUND_LAYER,new PaintLineBackground());
  719. addExtension(SELECTION_LAYER,new PaintSelection());
  720. addExtension(WRAP_GUIDE_LAYER,new PaintWrapGuide());
  721. addExtension(BRACKET_HIGHLIGHT_LAYER,new StructureMatcher
  722. .Highlight(textArea));
  723. addExtension(TEXT_LAYER,new PaintText());
  724. caretExtension = new PaintCaret();
  725. } //}}}
  726. //}}}
  727. //{{{ Private members
  728. //{{{ Instance variables
  729. private ExtensionManager extensionMgr;
  730. private PaintCaret caretExtension;
  731. private RenderingHints renderingHints;
  732. private FontRenderContext fontRenderContext;
  733. private HashMap fonts;
  734. //}}}
  735. //{{{ updateRenderingHints() method
  736. private void updateRenderingHints()
  737. {
  738. HashMap hints = new HashMap();
  739. if(antiAlias)
  740. {
  741. //hints.put(RenderingHints.KEY_RENDERING, RenderingHints.VALUE_RENDER_QUALITY);
  742. hints.put(RenderingHints.KEY_ANTIALIASING, RenderingHints.VALUE_ANTIALIAS_ON);
  743. hints.put(RenderingHints.KEY_TEXT_ANTIALIASING, RenderingHints.VALUE_TEXT_ANTIALIAS_ON);
  744. }
  745. else
  746. {
  747. hints.put(RenderingHints.KEY_ANTIALIASING, RenderingHints.VALUE_ANTIALIAS_OFF);
  748. hints.put(RenderingHints.KEY_TEXT_ANTIALIASING, RenderingHints.VALUE_TEXT_ANTIALIAS_OFF);
  749. }
  750. hints.put(RenderingHints.KEY_FRACTIONALMETRICS,
  751. fracFontMetrics ?
  752. RenderingHints.VALUE_FRACTIONALMETRICS_ON
  753. : RenderingHints.VALUE_FRACTIONALMETRICS_OFF);
  754. renderingHints = new RenderingHints(hints);
  755. fontRenderContext = new FontRenderContext(null,antiAlias,
  756. fracFontMetrics);
  757. } //}}}
  758. //{{{ getCharWidth() method
  759. private int getCharWidth(Font font)
  760. {
  761. Integer returnValue = (Integer)fonts.get(font);
  762. if(returnValue == null)
  763. {
  764. int minWidth = Integer.MAX_VALUE;
  765. int maxWidth = Integer.MIN_VALUE;
  766. FontMetrics fm = getFontMetrics(font);
  767. int[] widths = fm.getWidths();
  768. for(int i = 0; i < widths.length; i++)
  769. {
  770. int width = widths[i];
  771. if(width == 0 || !font.canDisplay((char)i))
  772. continue;
  773. minWidth = Math.min(width,minWidth);
  774. maxWidth = Math.max(width,maxWidth);
  775. }
  776. String str = "iwiwiwiau1234";
  777. double width1 = font.createGlyphVector(textArea.getPainter()
  778. .getFontRenderContext(),str).getLogicalBounds()
  779. .getWidth();
  780. double width2 = str.length() * maxWidth;
  781. if(minWidth == maxWidth
  782. && width1 == width2)
  783. {
  784. Log.log(Log.DEBUG,this,"Using monospaced font optimization: " + font);
  785. returnValue = new Integer(maxWidth);
  786. }
  787. else
  788. {
  789. Log.log(Log.DEBUG,this,"Not using monospaced font optimization: " + font);
  790. Log.log(Log.DEBUG,this,"Minimum width = " + minWidth
  791. + ", maximum width = " + maxWidth);
  792. returnValue = new Integer(0);
  793. }
  794. fonts.put(font,returnValue);
  795. }
  796. return returnValue.intValue();
  797. } //}}}
  798. //}}}
  799. //{{{ Inner classes
  800. //{{{ PaintLineBackground class
  801. class PaintLineBackground extends TextAreaExtension
  802. {
  803. //{{{ paintValidLine() method
  804. public void paintValidLine(Graphics2D gfx, int screenLine,
  805. int physicalLine, int start, int end, int y)
  806. {
  807. // minimise access$ methods
  808. JEditTextArea textArea = TextAreaPainter.this.textArea;
  809. Buffer buffer = textArea.getBuffer();
  810. //{{{ Paint line highlight and collapsed fold highlight
  811. boolean collapsedFold =
  812. (physicalLine < buffer.getLineCount() - 1
  813. && buffer.isFoldStart(physicalLine)
  814. && !textArea.displayManager
  815. .isLineVisible(physicalLine + 1));
  816. SyntaxStyle foldLineStyle = null;
  817. if(collapsedFold)
  818. {
  819. int level = buffer.getFoldLevel(physicalLine + 1);
  820. if(buffer.getFoldHandler() instanceof IndentFoldHandler)
  821. level = Math.max(1,level / buffer.getIndentSize());
  822. if(level > 3)
  823. level = 0;
  824. foldLineStyle = TextAreaPainter.this.foldLineStyle[level];
  825. }
  826. int caret = textArea.getCaretPosition();
  827. boolean paintLineHighlight = isLineHighlightEnabled()
  828. && caret >= start && caret < end
  829. && textArea.selection.size() == 0;
  830. Color bgColor;
  831. if(paintLineHighlight)
  832. bgColor = lineHighlightColor;
  833. else if(collapsedFold)
  834. {
  835. bgColor = foldLineStyle.getBackgroundColor();
  836. if(bgColor == null)
  837. bgColor = getBackground();
  838. }
  839. else
  840. bgColor = getBackground();
  841. if(paintLineHighlight || collapsedFold)
  842. {
  843. gfx.setColor(bgColor);
  844. gfx.fillRect(0,y,getWidth(),fm.getHeight());
  845. } //}}}
  846. //{{{ Paint token backgrounds
  847. ChunkCache.LineInfo lineInfo = textArea.chunkCache
  848. .getLineInfo(screenLine);
  849. if(lineInfo.chunks != null)
  850. {
  851. float baseLine = y + fm.getHeight()
  852. - fm.getLeading() - fm.getDescent();
  853. Chunk.paintChunkBackgrounds(
  854. lineInfo.chunks,gfx,
  855. textArea.getHorizontalOffset(),
  856. baseLine);
  857. } //}}}
  858. } //}}}
  859. } //}}}
  860. //{{{ PaintSelection class
  861. class PaintSelection extends TextAreaExtension
  862. {
  863. //{{{ paintValidLine() method
  864. public void paintValidLine(Graphics2D gfx, int screenLine,
  865. int physicalLine, int start, int end, int y)
  866. {
  867. if(textArea.selection.size() == 0)
  868. return;
  869. gfx.setColor(textArea.isMultipleSelectionEnabled()
  870. ? getMultipleSelectionColor()
  871. : getSelectionColor());
  872. for(int i = textArea.selection.size() - 1;
  873. i >= 0; i--)
  874. {
  875. paintSelection(gfx,screenLine,
  876. physicalLine,start,end,y,
  877. (Selection)textArea.selection
  878. .get(i));
  879. }
  880. } //}}}
  881. //{{{ paintSelection() method
  882. private void paintSelection(Graphics2D gfx, int screenLine,
  883. int physicalLine, int start, int end, int y, Selection s)
  884. {
  885. if(end <= s.start || start > s.end)
  886. return;
  887. int selStartScreenLine = textArea.getScreenLineOfOffset(s.start);
  888. int selEndScreenLine = textArea.getScreenLineOfOffset(s.end);
  889. Buffer buffer = textArea.getBuffer();
  890. int lineStart = buffer.getLineStartOffset(physicalLine);
  891. int x1, x2;
  892. if(s instanceof Selection.Rect)
  893. {
  894. start -= lineStart;
  895. end -= lineStart;
  896. Selection.Rect rect = (Selection.Rect)s;
  897. int _start = rect.getStartColumn(buffer);
  898. int _end = rect.getEndColumn(buffer);
  899. int lineLen = buffer.getLineLength(physicalLine);
  900. int[] total = new int[1];
  901. int rectStart = buffer.getOffsetOfVirtualColumn(
  902. physicalLine,_start,total);
  903. if(rectStart == -1)
  904. {
  905. x1 = (_start - total[0]) * textArea.charWidth;
  906. rectStart = lineLen;
  907. }
  908. else
  909. x1 = 0;
  910. int rectEnd = buffer.getOffsetOfVirtualColumn(
  911. physicalLine,_end,total);
  912. if(rectEnd == -1)
  913. {
  914. x2 = (_end - total[0]) * textArea.charWidth;
  915. rectEnd = lineLen;
  916. }
  917. else
  918. x2 = 0;
  919. if(end <= rectStart || start > rectEnd)
  920. return;
  921. x1 = (rectStart < start ? 0
  922. : x1 + textArea.offsetToXY(physicalLine,rectStart,textArea.returnValue).x);
  923. x2 = (rectEnd > end ? getWidth()
  924. : x2 + textArea.offsetToXY(physicalLine,rectEnd,textArea.returnValue).x);
  925. }
  926. else if(selStartScreenLine == selEndScreenLine
  927. && selStartScreenLine != -1)
  928. {
  929. x1 = textArea.offsetToXY(physicalLine,
  930. s.start - lineStart,textArea.returnValue).x;
  931. x2 = textArea.offsetToXY(physicalLine,
  932. s.end - lineStart,textArea.returnValue).x;
  933. }
  934. else if(screenLine == selStartScreenLine)
  935. {
  936. x1 = textArea.offsetToXY(physicalLine,
  937. s.start - lineStart,textArea.returnValue).x;
  938. x2 = getWidth();
  939. }
  940. else if(screenLine == selEndScreenLine)
  941. {
  942. x1 = 0;
  943. x2 = textArea.offsetToXY(physicalLine,
  944. s.end - lineStart,textArea.returnValue).x;
  945. }
  946. else
  947. {
  948. x1 = 0;
  949. x2 = getWidth();
  950. }
  951. if(x1 < 0)
  952. x1 = 0;
  953. if(x2 < 0)
  954. x2 = 0;
  955. if(x1 == x2)
  956. x2++;
  957. gfx.fillRect(x1,y,x2 - x1,fm.getHeight());
  958. } //}}}
  959. } //}}}
  960. //{{{ PaintWrapGuide class
  961. class PaintWrapGuide extends TextAreaExtension
  962. {
  963. public void paintScreenLineRange(Graphics2D gfx, int firstLine,
  964. int lastLine, int[] physicalLines, int[] start,
  965. int[] end, int y, int lineHeight)
  966. {
  967. if(textArea.getDisplayManager().wrapMargin != 0
  968. && isWrapGuidePainted())
  969. {
  970. gfx.setColor(getWrapGuideColor());
  971. int x = textArea.getHorizontalOffset()
  972. + textArea.getDisplayManager()
  973. .wrapMargin;
  974. gfx.drawLine(x,y,x,y + (lastLine - firstLine
  975. + 1) * lineHeight);
  976. }
  977. }
  978. public String getToolTipText(int x, int y)
  979. {
  980. if(textArea.getDisplayManager().wrapMargin != 0 && isWrapGuidePainted())
  981. {
  982. int wrapGuidePos = textArea.getDisplayManager().wrapMargin
  983. + textArea.getHorizontalOffset();
  984. if(Math.abs(x - wrapGuidePos) < 5)
  985. {
  986. return String.valueOf(textArea.getBuffer()
  987. .getProperty("maxLineLen"));
  988. }
  989. }
  990. return null;
  991. }
  992. } //}}}
  993. //{{{ PaintText class
  994. class PaintText extends TextAreaExtension
  995. {
  996. public void paintValidLine(Graphics2D gfx, int screenLine,
  997. int physicalLine, int start, int end, int y)
  998. {
  999. ChunkCache.LineInfo lineInfo = textArea.chunkCache
  1000. .getLineInfo(screenLine);
  1001. Font defaultFont = getFont();
  1002. Color defaultColor = getForeground();
  1003. gfx.setFont(defaultFont);
  1004. gfx.setColor(defaultColor);
  1005. int x = textArea.getHorizontalOffset();
  1006. int originalX = x;
  1007. float baseLine = y + fm.getHeight()
  1008. - fm.getLeading() - fm.getDescent();
  1009. if(lineInfo.chunks != null)
  1010. {
  1011. x += Chunk.paintChunkList(lineInfo.chunks,
  1012. gfx,textArea.getHorizontalOffset(),
  1013. baseLine,!Debug.DISABLE_GLYPH_VECTOR);
  1014. }
  1015. Buffer buffer = textArea.getBuffer();
  1016. if(!lineInfo.lastSubregion)
  1017. {
  1018. gfx.setFont(defaultFont);
  1019. gfx.setColor(eolMarkerColor);
  1020. gfx.drawString(":",Math.max(x,
  1021. textArea.getHorizontalOffset()
  1022. + textArea.getDisplayManager().wrapMargin + textArea.charWidth),
  1023. baseLine);
  1024. x += textArea.charWidth;
  1025. }
  1026. else if(physicalLine < buffer.getLineCount() - 1
  1027. && buffer.isFoldStart(physicalLine)
  1028. && !textArea.displayManager
  1029. .isLineVisible(physicalLine + 1))
  1030. {
  1031. int level = buffer.getFoldLevel(physicalLine + 1);
  1032. if(buffer.getFoldHandler() instanceof IndentFoldHandler)
  1033. level = Math.max(1,level / buffer.getIndentSize());
  1034. if(level > 3)
  1035. level = 0;
  1036. SyntaxStyle foldLineStyle = TextAreaPainter.this.foldLineStyle[level];
  1037. Font font = foldLineStyle.getFont();
  1038. gfx.setFont(font);
  1039. gfx.setColor(foldLineStyle.getForegroundColor());
  1040. int nextLine;
  1041. int nextScreenLine = screenLine + 1;
  1042. if(nextScreenLine < textArea.getVisibleLines())
  1043. {
  1044. nextLine = textArea.chunkCache.getLineInfo(nextScreenLine)
  1045. .physicalLine;
  1046. }
  1047. else
  1048. {
  1049. nextLine = textArea.displayManager
  1050. .getNextVisibleLine(physicalLine);
  1051. }
  1052. if(nextLine == -1)
  1053. nextLine = textArea.getLineCount();
  1054. int count = nextLine - physicalLine - 1;
  1055. String str = " [" + count + " lines]";
  1056. float width = (float)font.getStringBounds(
  1057. str,fontRenderContext).getWidth();
  1058. gfx.drawString(str,x,baseLine);
  1059. x += width;
  1060. }
  1061. else if(eolMarkers)
  1062. {
  1063. gfx.setFont(defaultFont);
  1064. gfx.setColor(eolMarkerColor);
  1065. gfx.drawString(".",x,baseLine);
  1066. x += textArea.charWidth;
  1067. }
  1068. lineInfo.width = (x - originalX);
  1069. if(lineInfo.width > textArea.maxHorizontalScrollWidth)
  1070. textArea.updateMaxHorizontalScrollWidth = true;
  1071. }
  1072. } //}}}
  1073. //{{{ PaintCaret class
  1074. class PaintCaret extends TextAreaExtension
  1075. {
  1076. public void paintValidLine(Graphics2D gfx, int screenLine,
  1077. int physicalLine, int start, int end, int y)
  1078. {
  1079. if(!textArea.isCaretVisible())
  1080. return;
  1081. int caret = textArea.getCaretPosition();
  1082. if(caret < start || caret >= end)
  1083. return;
  1084. int offset = caret - textArea.getLineStartOffset(physicalLine);
  1085. textArea.offsetToXY(physicalLine,offset,textArea.returnValue);
  1086. int caretX = textArea.returnValue.x;
  1087. int height = fm.getHeight();
  1088. gfx.setColor(caretColor);
  1089. if(textArea.isOverwriteEnabled())
  1090. {
  1091. gfx.drawLine(caretX,y + height - 1,
  1092. caretX + textArea.charWidth,y + height - 1);
  1093. }
  1094. else if(blockCaret)
  1095. gfx.drawRect(caretX,y,textArea.charWidth - 1,height - 1);
  1096. else
  1097. gfx.drawLine(caretX,y,caretX,y + height - 1);
  1098. }
  1099. } //}}}
  1100. //}}}
  1101. }