PageRenderTime 116ms CodeModel.GetById 12ms RepoModel.GetById 1ms app.codeStats 0ms

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

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