0001: /*
0002: * TextAreaPainter.java - Paints the text area
0003: * :tabSize=8:indentSize=8:noTabs=false:
0004: * :folding=explicit:collapseFolds=1:
0005: *
0006: * Copyright (C) 1999, 2005 Slava Pestov
0007: *
0008: * This program is free software; you can redistribute it and/or
0009: * modify it under the terms of the GNU General Public License
0010: * as published by the Free Software Foundation; either version 2
0011: * of the License, or any later version.
0012: *
0013: * This program is distributed in the hope that it will be useful,
0014: * but WITHOUT ANY WARRANTY; without even the implied warranty of
0015: * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
0016: * GNU General Public License for more details.
0017: *
0018: * You should have received a copy of the GNU General Public License
0019: * along with this program; if not, write to the Free Software
0020: * Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA.
0021: */
0022:
0023: package org.gjt.sp.jedit.textarea;
0024:
0025: //{{{ Imports
0026: import javax.swing.text.*;
0027: import javax.swing.JComponent;
0028: import java.awt.event.MouseEvent;
0029: import java.awt.font.*;
0030: import java.awt.geom.AffineTransform;
0031: import java.awt.*;
0032: import java.lang.reflect.Constructor;
0033: import java.lang.reflect.Field;
0034: import java.util.*;
0035: import org.gjt.sp.jedit.buffer.IndentFoldHandler;
0036: import org.gjt.sp.jedit.buffer.JEditBuffer;
0037: import org.gjt.sp.jedit.syntax.Chunk;
0038: import org.gjt.sp.jedit.syntax.SyntaxStyle;
0039: import org.gjt.sp.jedit.syntax.Token;
0040: import org.gjt.sp.jedit.Debug;
0041:
0042: import org.gjt.sp.util.Log;
0043:
0044: //}}}
0045:
0046: /**
0047: * The text area painter is the component responsible for displaying the
0048: * text of the current buffer. The only methods in this class that should
0049: * be called by plugins are those for adding and removing
0050: * text area extensions.
0051: *
0052: * @see #addExtension(TextAreaExtension)
0053: * @see #addExtension(int,TextAreaExtension)
0054: * @see #removeExtension(TextAreaExtension)
0055: * @see TextAreaExtension
0056: * @see TextArea
0057: *
0058: * @author Slava Pestov
0059: * @version $Id: TextAreaPainter.java 7156 2006-10-02 21:33:17Z kpouer $
0060: */
0061: public class TextAreaPainter extends JComponent implements TabExpander {
0062: //{{{ Layers
0063: /**
0064: * The lowest possible layer.
0065: * @see #addExtension(int,TextAreaExtension)
0066: * @since jEdit 4.0pre4
0067: */
0068: public static final int LOWEST_LAYER = Integer.MIN_VALUE;
0069:
0070: /**
0071: * Below selection layer. The JDiff plugin will use this.
0072: * @see #addExtension(int,TextAreaExtension)
0073: * @since jEdit 4.0pre4
0074: */
0075: public static final int BACKGROUND_LAYER = -60;
0076:
0077: /**
0078: * The line highlight and collapsed fold highlight layer.
0079: * @see #addExtension(int,TextAreaExtension)
0080: * @since jEdit 4.0pre7
0081: */
0082: public static final int LINE_BACKGROUND_LAYER = -50;
0083:
0084: /**
0085: * Below selection layer.
0086: * @see #addExtension(int,TextAreaExtension)
0087: * @since jEdit 4.0pre4
0088: */
0089: public static final int BELOW_SELECTION_LAYER = -40;
0090:
0091: /**
0092: * Selection layer. Most extensions will be above this layer, but some
0093: * (eg, JDiff) will want to be below the selection.
0094: * @see #addExtension(int,TextAreaExtension)
0095: * @since jEdit 4.0pre4
0096: */
0097: public static final int SELECTION_LAYER = -30;
0098:
0099: /**
0100: * Wrap guide layer. Most extensions will be above this layer.
0101: * @since jEdit 4.0pre4
0102: */
0103: public static final int WRAP_GUIDE_LAYER = -20;
0104:
0105: /**
0106: * Below most extensions layer.
0107: * @see #addExtension(int,TextAreaExtension)
0108: * @since jEdit 4.0pre4
0109: */
0110: public static final int BELOW_MOST_EXTENSIONS_LAYER = -10;
0111:
0112: /**
0113: * Default extension layer. This is above the wrap guide but below the
0114: * structure highlight.
0115: * @since jEdit 4.0pre4
0116: */
0117: public static final int DEFAULT_LAYER = 0;
0118:
0119: /**
0120: * Block caret layer. Most extensions will be below this layer.
0121: * @since jEdit 4.2pre1
0122: */
0123: public static final int BLOCK_CARET_LAYER = 50;
0124:
0125: /**
0126: * Bracket highlight layer. Most extensions will be below this layer.
0127: * @since jEdit 4.0pre4
0128: */
0129: public static final int BRACKET_HIGHLIGHT_LAYER = 100;
0130:
0131: /**
0132: * Text layer. Most extensions will be below this layer.
0133: * @since jEdit 4.2pre1
0134: */
0135: public static final int TEXT_LAYER = 200;
0136:
0137: /**
0138: * Caret layer. Most extensions will be below this layer.
0139: * @since jEdit 4.2pre1
0140: */
0141: public static final int CARET_LAYER = 300;
0142:
0143: /**
0144: * Highest possible layer.
0145: * @since jEdit 4.0pre4
0146: */
0147: public static final int HIGHEST_LAYER = Integer.MAX_VALUE;
0148:
0149: //}}}
0150:
0151: //{{{ setBounds() method
0152: /**
0153: * It is a bad idea to override this, but we need to get the component
0154: * event before the first repaint.
0155: */
0156: public void setBounds(int x, int y, int width, int height) {
0157: if (x == getX() && y == getY() && width == getWidth()
0158: && height == getHeight()) {
0159: return;
0160: }
0161:
0162: super .setBounds(x, y, width, height);
0163:
0164: textArea.recalculateVisibleLines();
0165: if (!textArea.getBuffer().isLoading())
0166: textArea.recalculateLastPhysicalLine();
0167: textArea.propertiesChanged();
0168: textArea.updateMaxHorizontalScrollWidth();
0169: textArea.scrollBarsInitialized = true;
0170:
0171: textArea.repaintMgr.updateGraphics();
0172: } //}}}
0173:
0174: //{{{ getFocusTraversalKeysEnabled() method
0175: /**
0176: * Makes the tab key work in Java 1.4.
0177: * @since jEdit 3.2pre4
0178: */
0179: public boolean getFocusTraversalKeysEnabled() {
0180: return false;
0181: } //}}}
0182:
0183: //{{{ Getters and setters
0184:
0185: //{{{ getStyles() method
0186: /**
0187: * Returns the syntax styles used to paint colorized text. Entry <i>n</i>
0188: * will be used to paint tokens with id = <i>n</i>.
0189: * @see org.gjt.sp.jedit.syntax.Token
0190: */
0191: public final SyntaxStyle[] getStyles() {
0192: return styles;
0193: } //}}}
0194:
0195: //{{{ setStyles() method
0196: /**
0197: * Sets the syntax styles used to paint colorized text. Entry <i>n</i>
0198: * will be used to paint tokens with id = <i>n</i>.
0199: * @param styles The syntax styles
0200: * @see org.gjt.sp.jedit.syntax.Token
0201: */
0202: public final void setStyles(SyntaxStyle[] styles) {
0203: // assumed this is called after a font render context is set up.
0204: // changing font render context settings without a setStyles()
0205: // call will not reset cached monospaced font info.
0206: fonts.clear();
0207:
0208: this .styles = styles;
0209: styles[Token.NULL] = new SyntaxStyle(getForeground(), null,
0210: getFont());
0211: repaint();
0212: } //}}}
0213:
0214: //{{{ getCaretColor() method
0215: /**
0216: * Returns the caret color.
0217: */
0218: public final Color getCaretColor() {
0219: return caretColor;
0220: } //}}}
0221:
0222: //{{{ setCaretColor() method
0223: /**
0224: * Sets the caret color.
0225: * @param caretColor The caret color
0226: */
0227: public final void setCaretColor(Color caretColor) {
0228: this .caretColor = caretColor;
0229: if (textArea.getBuffer() != null)
0230: textArea.invalidateLine(textArea.getCaretLine());
0231: } //}}}
0232:
0233: //{{{ getSelectionColor() method
0234: /**
0235: * Returns the selection color.
0236: */
0237: public final Color getSelectionColor() {
0238: return selectionColor;
0239: } //}}}
0240:
0241: //{{{ setSelectionColor() method
0242: /**
0243: * Sets the selection color.
0244: * @param selectionColor The selection color
0245: */
0246: public final void setSelectionColor(Color selectionColor) {
0247: this .selectionColor = selectionColor;
0248: textArea.repaint();
0249: } //}}}
0250:
0251: //{{{ getMultipleSelectionColor() method
0252: /**
0253: * Returns the multiple selection color.
0254: * @since jEdit 4.2pre1
0255: */
0256: public final Color getMultipleSelectionColor() {
0257: return multipleSelectionColor;
0258: } //}}}
0259:
0260: //{{{ setMultipleSelectionColor() method
0261: /**
0262: * Sets the multiple selection color.
0263: * @param multipleSelectionColor The multiple selection color
0264: * @since jEdit 4.2pre1
0265: */
0266: public final void setMultipleSelectionColor(
0267: Color multipleSelectionColor) {
0268: this .multipleSelectionColor = multipleSelectionColor;
0269: textArea.repaint();
0270: } //}}}
0271:
0272: //{{{ getLineHighlightColor() method
0273: /**
0274: * Returns the line highlight color.
0275: */
0276: public final Color getLineHighlightColor() {
0277: return lineHighlightColor;
0278: } //}}}
0279:
0280: //{{{ setLineHighlightColor() method
0281: /**
0282: * Sets the line highlight color.
0283: * @param lineHighlightColor The line highlight color
0284: */
0285: public final void setLineHighlightColor(Color lineHighlightColor) {
0286: this .lineHighlightColor = lineHighlightColor;
0287: if (textArea.getBuffer() != null)
0288: textArea.invalidateLine(textArea.getCaretLine());
0289: } //}}}
0290:
0291: //{{{ isLineHighlightEnabled() method
0292: /**
0293: * Returns true if line highlight is enabled, false otherwise.
0294: */
0295: public final boolean isLineHighlightEnabled() {
0296: return lineHighlight;
0297: } //}}}
0298:
0299: //{{{ setLineHighlightEnabled() method
0300: /**
0301: * Enables or disables current line highlighting.
0302: * @param lineHighlight True if current line highlight should be enabled,
0303: * false otherwise
0304: */
0305: public final void setLineHighlightEnabled(boolean lineHighlight) {
0306: this .lineHighlight = lineHighlight;
0307: textArea.repaint();
0308: } //}}}
0309:
0310: //{{{ getStructureHighlightColor() method
0311: /**
0312: * Returns the structure highlight color.
0313: * @since jEdit 4.2pre3
0314: */
0315: public final Color getStructureHighlightColor() {
0316: return structureHighlightColor;
0317: } //}}}
0318:
0319: //{{{ setStructureHighlightColor() method
0320: /**
0321: * Sets the structure highlight color.
0322: * @param structureHighlightColor The bracket highlight color
0323: * @since jEdit 4.2pre3
0324: */
0325: public final void setStructureHighlightColor(
0326: Color structureHighlightColor) {
0327: this .structureHighlightColor = structureHighlightColor;
0328: textArea.invalidateStructureMatch();
0329: } //}}}
0330:
0331: //{{{ isStructureHighlightEnabled() method
0332: /**
0333: * Returns true if structure highlighting is enabled, false otherwise.
0334: * @since jEdit 4.2pre3
0335: */
0336: public final boolean isStructureHighlightEnabled() {
0337: return structureHighlight;
0338: } //}}}
0339:
0340: //{{{ setStructureHighlightEnabled() method
0341: /**
0342: * Enables or disables structure highlighting.
0343: * @param structureHighlight True if structure highlighting should be
0344: * enabled, false otherwise
0345: * @since jEdit 4.2pre3
0346: */
0347: public final void setStructureHighlightEnabled(
0348: boolean structureHighlight) {
0349: this .structureHighlight = structureHighlight;
0350: textArea.invalidateStructureMatch();
0351: } //}}}
0352:
0353: //{{{ isBlockCaretEnabled() method
0354: /**
0355: * Returns true if the caret should be drawn as a block, false otherwise.
0356: */
0357: public final boolean isBlockCaretEnabled() {
0358: return blockCaret;
0359: } //}}}
0360:
0361: //{{{ setBlockCaretEnabled() method
0362: /**
0363: * Sets if the caret should be drawn as a block, false otherwise.
0364: * @param blockCaret True if the caret should be drawn as a block,
0365: * false otherwise.
0366: */
0367: public final void setBlockCaretEnabled(boolean blockCaret) {
0368: this .blockCaret = blockCaret;
0369: extensionMgr.removeExtension(caretExtension);
0370: if (blockCaret)
0371: addExtension(BLOCK_CARET_LAYER, caretExtension);
0372: else
0373: addExtension(CARET_LAYER, caretExtension);
0374: if (textArea.getBuffer() != null)
0375: textArea.invalidateLine(textArea.getCaretLine());
0376: } //}}}
0377:
0378: //{{{ getEOLMarkerColor() method
0379: /**
0380: * Returns the EOL marker color.
0381: */
0382: public final Color getEOLMarkerColor() {
0383: return eolMarkerColor;
0384: } //}}}
0385:
0386: //{{{ setEOLMarkerColor() method
0387: /**
0388: * Sets the EOL marker color.
0389: * @param eolMarkerColor The EOL marker color
0390: */
0391: public final void setEOLMarkerColor(Color eolMarkerColor) {
0392: this .eolMarkerColor = eolMarkerColor;
0393: repaint();
0394: } //}}}
0395:
0396: //{{{ getEOLMarkersPainted() method
0397: /**
0398: * Returns true if EOL markers are drawn, false otherwise.
0399: */
0400: public final boolean getEOLMarkersPainted() {
0401: return eolMarkers;
0402: } //}}}
0403:
0404: //{{{ setEOLMarkersPainted() method
0405: /**
0406: * Sets if EOL markers are to be drawn.
0407: * @param eolMarkers True if EOL markers should be drawn, false otherwise
0408: */
0409: public final void setEOLMarkersPainted(boolean eolMarkers) {
0410: this .eolMarkers = eolMarkers;
0411: repaint();
0412: } //}}}
0413:
0414: //{{{ getWrapGuideColor() method
0415: /**
0416: * Returns the wrap guide color.
0417: */
0418: public final Color getWrapGuideColor() {
0419: return wrapGuideColor;
0420: } //}}}
0421:
0422: //{{{ setWrapGuideColor() method
0423: /**
0424: * Sets the wrap guide color.
0425: * @param wrapGuideColor The wrap guide color
0426: */
0427: public final void setWrapGuideColor(Color wrapGuideColor) {
0428: this .wrapGuideColor = wrapGuideColor;
0429: repaint();
0430: } //}}}
0431:
0432: //{{{ isWrapGuidePainted() method
0433: /**
0434: * Returns true if the wrap guide is drawn, false otherwise.
0435: * @since jEdit 4.0pre4
0436: */
0437: public final boolean isWrapGuidePainted() {
0438: return wrapGuide;
0439: } //}}}
0440:
0441: //{{{ setWrapGuidePainted() method
0442: /**
0443: * Sets if the wrap guide is to be drawn.
0444: * @param wrapGuide True if the wrap guide should be drawn, false otherwise
0445: */
0446: public final void setWrapGuidePainted(boolean wrapGuide) {
0447: this .wrapGuide = wrapGuide;
0448: repaint();
0449: } //}}}
0450:
0451: //{{{ getFoldLineStyle() method
0452: /**
0453: * Returns the fold line style. The first element is the style for
0454: * lines with a fold level greater than 3. The remaining elements
0455: * are for fold levels 1 to 3.
0456: */
0457: public final SyntaxStyle[] getFoldLineStyle() {
0458: return foldLineStyle;
0459: } //}}}
0460:
0461: //{{{ setFoldLineStyle() method
0462: /**
0463: * Sets the fold line style. The first element is the style for
0464: * lines with a fold level greater than 3. The remaining elements
0465: * are for fold levels 1 to 3.
0466: * @param foldLineStyle The fold line style
0467: */
0468: public final void setFoldLineStyle(SyntaxStyle[] foldLineStyle) {
0469: this .foldLineStyle = foldLineStyle;
0470: repaint();
0471: } //}}}
0472:
0473: //{{{ setAntiAliasEnabled() method
0474: /**
0475: * @deprecated use setAntiAlias(AntiAlias newMode)
0476: */
0477: public void setAntiAliasEnabled(boolean isEnabled) {
0478:
0479: setAntiAlias(new AntiAlias(isEnabled));
0480: }
0481:
0482: /**
0483: * As of jEdit 4.3pre4, a new JDK 1.6 subpixel antialias mode is supported.
0484: *
0485: * @since jEdit 4.2pre4
0486: */
0487: public void setAntiAlias(AntiAlias newValue) {
0488: this .antiAlias = newValue;
0489: updateRenderingHints();
0490: } //}}}
0491:
0492: /**
0493: * @return the AntiAlias value that is currently used for TextAreas.
0494: * @since jedit 4.3pre4
0495: */
0496: public AntiAlias getAntiAlias() {
0497: return antiAlias;
0498: }
0499:
0500: //{{{ isAntiAliasEnabled() method
0501: /**
0502: * Returns if anti-aliasing is enabled.
0503: * @since jEdit 3.2pre6
0504: * @deprecated - use @ref getAntiAlias()
0505: */
0506: public boolean isAntiAliasEnabled() {
0507: return antiAlias.val() > 0;
0508: } //}}}
0509:
0510: //{{{ setFractionalFontMetricsEnabled() method
0511: /**
0512: * Sets if fractional font metrics should be enabled. Has no effect when
0513: * running on Java 1.1.
0514: * @since jEdit 3.2pre6
0515: */
0516: public void setFractionalFontMetricsEnabled(boolean fracFontMetrics) {
0517: this .fracFontMetrics = fracFontMetrics;
0518: updateRenderingHints();
0519: } //}}}
0520:
0521: //{{{ isFractionalFontMetricsEnabled() method
0522: /**
0523: * Returns if fractional font metrics are enabled.
0524: * @since jEdit 3.2pre6
0525: */
0526: public boolean isFractionalFontMetricsEnabled() {
0527: return fracFontMetrics;
0528: } //}}}
0529:
0530: //{{{ getFontRenderContext() method
0531: /**
0532: * Returns the font render context.
0533: * @since jEdit 4.0pre4
0534: */
0535: public FontRenderContext getFontRenderContext() {
0536: return fontRenderContext;
0537: } //}}}
0538:
0539: //}}}
0540:
0541: //{{{ addExtension() method
0542: /**
0543: * Adds a text area extension, which can perform custom painting and
0544: * tool tip handling.
0545: * @param extension The extension
0546: * @since jEdit 4.0pre4
0547: */
0548: public void addExtension(TextAreaExtension extension) {
0549: extensionMgr.addExtension(DEFAULT_LAYER, extension);
0550: repaint();
0551: } //}}}
0552:
0553: //{{{ addExtension() method
0554: /**
0555: * Adds a text area extension, which can perform custom painting and
0556: * tool tip handling.
0557: * @param layer The layer to add the extension to. Note that more than
0558: * extension can share the same layer.
0559: * @param extension The extension
0560: * @since jEdit 4.0pre4
0561: */
0562: public void addExtension(int layer, TextAreaExtension extension) {
0563: extensionMgr.addExtension(layer, extension);
0564: repaint();
0565: } //}}}
0566:
0567: //{{{ removeExtension() method
0568: /**
0569: * Removes a text area extension. It will no longer be asked to
0570: * perform custom painting and tool tip handling.
0571: * @param extension The extension
0572: * @since jEdit 4.0pre4
0573: */
0574: public void removeExtension(TextAreaExtension extension) {
0575: extensionMgr.removeExtension(extension);
0576: repaint();
0577: } //}}}
0578:
0579: //{{{ getExtensions() method
0580: /**
0581: * Returns an array of registered text area extensions. Useful for
0582: * debugging purposes.
0583: * @since jEdit 4.1pre5
0584: */
0585: public TextAreaExtension[] getExtensions() {
0586: return extensionMgr.getExtensions();
0587: } //}}}
0588:
0589: //{{{ getToolTipText() method
0590: /**
0591: * Returns the tool tip to display at the specified location.
0592: * @param evt The mouse event
0593: */
0594: public String getToolTipText(MouseEvent evt) {
0595: if (textArea.getBuffer().isLoading())
0596: return null;
0597:
0598: return extensionMgr.getToolTipText(evt.getX(), evt.getY());
0599: } //}}}
0600:
0601: //{{{ getFontMetrics() method
0602: /**
0603: * Returns the font metrics used by this component.
0604: */
0605: public FontMetrics getFontMetrics() {
0606: return fm;
0607: } //}}}
0608:
0609: //{{{ setFont() method
0610: /**
0611: * Sets the font for this component. This is overridden to update the
0612: * cached font metrics and to recalculate which lines are visible.
0613: * @param font The font
0614: */
0615: public void setFont(Font font) {
0616: super .setFont(font);
0617: fm = getFontMetrics(font);
0618: textArea.recalculateVisibleLines();
0619: if (textArea.getBuffer() != null
0620: && !textArea.getBuffer().isLoading())
0621: textArea.recalculateLastPhysicalLine();
0622: textArea.propertiesChanged();
0623: } //}}}
0624:
0625: //{{{ getStringWidth() method
0626: /**
0627: * Returns the width of the given string, in pixels, using the text
0628: * area's current font.
0629: *
0630: * @since jEdit 4.2final
0631: */
0632: public float getStringWidth(String str) {
0633: if (textArea.charWidth != 0)
0634: return textArea.charWidth * str.length();
0635: else {
0636: return (float) getFont().getStringBounds(str,
0637: getFontRenderContext()).getWidth();
0638: }
0639: } //}}}
0640:
0641: //{{{ update() method
0642: /**
0643: * Repaints the text.
0644: * @param _gfx The graphics context
0645: */
0646: public void update(Graphics _gfx) {
0647: paint(_gfx);
0648: } //}}}
0649:
0650: //{{{ paint() method
0651: /**
0652: * Repaints the text.
0653: * @param _gfx The graphics context
0654: */
0655: public void paint(Graphics _gfx) {
0656: Graphics2D gfx = textArea.repaintMgr.getGraphics();
0657:
0658: gfx.setRenderingHints(renderingHints);
0659: fontRenderContext = gfx.getFontRenderContext();
0660:
0661: Rectangle clipRect = _gfx.getClipBounds();
0662:
0663: JEditBuffer buffer = textArea.getBuffer();
0664: int height = fm.getHeight();
0665: if (height == 0 || buffer.isLoading()) {
0666: _gfx.setColor(getBackground());
0667: _gfx.fillRect(clipRect.x, clipRect.y, clipRect.width,
0668: clipRect.height);
0669: } else {
0670: long prepareTime = System.currentTimeMillis();
0671: FastRepaintManager.RepaintLines lines = textArea.repaintMgr
0672: .prepareGraphics(clipRect, textArea.getFirstLine(),
0673: gfx);
0674: prepareTime = (System.currentTimeMillis() - prepareTime);
0675:
0676: long linesTime = System.currentTimeMillis();
0677: int numLines = (lines.last - lines.first + 1);
0678:
0679: int y = lines.first * height;
0680: gfx.fillRect(0, y, getWidth(), numLines * height);
0681:
0682: extensionMgr.paintScreenLineRange(textArea, gfx,
0683: lines.first, lines.last, y, height);
0684: linesTime = (System.currentTimeMillis() - linesTime);
0685:
0686: textArea.repaintMgr
0687: .setFastScroll(clipRect.equals(new Rectangle(0, 0,
0688: getWidth(), getHeight())));
0689:
0690: long blitTime = System.currentTimeMillis();
0691: textArea.repaintMgr.paint(_gfx);
0692: blitTime = (System.currentTimeMillis() - blitTime);
0693:
0694: if (Debug.PAINT_TIMER && numLines >= 1)
0695: Log.log(Log.DEBUG, this , "repainting " + numLines
0696: + " lines took " + prepareTime + "/"
0697: + linesTime + "/" + blitTime + " ms");
0698: }
0699:
0700: textArea.updateMaxHorizontalScrollWidth();
0701: } //}}}
0702:
0703: //{{{ nextTabStop() method
0704: /**
0705: * Implementation of TabExpander interface. Returns next tab stop after
0706: * a specified point.
0707: * @param x The x co-ordinate
0708: * @param tabOffset Ignored
0709: * @return The next tab stop after <i>x</i>
0710: */
0711: public float nextTabStop(float x, int tabOffset) {
0712: int ntabs = (int) (x / textArea.tabSize);
0713: return (ntabs + 1) * textArea.tabSize;
0714: } //}}}
0715:
0716: //{{{ getPreferredSize() method
0717: /**
0718: * Returns the painter's preferred size.
0719: */
0720: public Dimension getPreferredSize() {
0721: Dimension dim = new Dimension();
0722:
0723: char[] foo = new char[80];
0724: for (int i = 0; i < foo.length; i++)
0725: foo[i] = ' ';
0726: dim.width = (int) getStringWidth(new String(foo));
0727: dim.height = fm.getHeight() * 25;
0728: return dim;
0729: } //}}}
0730:
0731: //{{{ getMinimumSize() method
0732: /**
0733: * Returns the painter's minimum size.
0734: */
0735: public Dimension getMinimumSize() {
0736: return getPreferredSize();
0737: } //}}}
0738:
0739: //{{{ Package-private members
0740:
0741: //{{{ Instance variables
0742: /* package-private since they are accessed by inner classes and we
0743: * want this to be fast */
0744: TextArea textArea;
0745:
0746: SyntaxStyle[] styles;
0747: Color caretColor;
0748: Color selectionColor;
0749: Color multipleSelectionColor;
0750: Color lineHighlightColor;
0751: Color structureHighlightColor;
0752: Color eolMarkerColor;
0753: Color wrapGuideColor;
0754:
0755: SyntaxStyle[] foldLineStyle;
0756:
0757: boolean blockCaret;
0758: boolean lineHighlight;
0759: boolean structureHighlight;
0760: boolean eolMarkers;
0761: boolean wrapGuide;
0762: AntiAlias antiAlias;
0763: boolean fracFontMetrics;
0764:
0765: // should try to use this as little as possible.
0766: FontMetrics fm;
0767:
0768: //}}}
0769:
0770: //{{{ TextAreaPainter constructor
0771: /**
0772: * Creates a new painter. Do not create instances of this class
0773: * directly.
0774: */
0775: TextAreaPainter(TextArea textArea) {
0776: enableEvents(AWTEvent.FOCUS_EVENT_MASK
0777: | AWTEvent.KEY_EVENT_MASK | AWTEvent.MOUSE_EVENT_MASK);
0778:
0779: this .textArea = textArea;
0780: antiAlias = new AntiAlias(0);
0781: fonts = new HashMap();
0782: extensionMgr = new ExtensionManager();
0783:
0784: setAutoscrolls(true);
0785: setOpaque(true);
0786: setRequestFocusEnabled(false);
0787: setDoubleBuffered(false);
0788:
0789: setCursor(Cursor.getPredefinedCursor(Cursor.TEXT_CURSOR));
0790:
0791: fontRenderContext = new FontRenderContext(null, false, false);
0792:
0793: addExtension(LINE_BACKGROUND_LAYER, new PaintLineBackground());
0794: addExtension(SELECTION_LAYER, new PaintSelection());
0795: addExtension(WRAP_GUIDE_LAYER, new PaintWrapGuide());
0796: addExtension(BRACKET_HIGHLIGHT_LAYER,
0797: new StructureMatcher.Highlight(textArea));
0798: addExtension(TEXT_LAYER, new PaintText());
0799: caretExtension = new PaintCaret();
0800: } //}}}
0801:
0802: //}}}
0803:
0804: //{{{ Private members
0805:
0806: //{{{ Instance variables
0807: private ExtensionManager extensionMgr;
0808: private PaintCaret caretExtension;
0809: private RenderingHints renderingHints;
0810: private FontRenderContext fontRenderContext;
0811: private Map fonts;
0812: //}}}
0813:
0814: private static Object sm_hrgbRender = null;
0815: private static Constructor sm_frcConstructor = null;
0816:
0817: static {
0818: try {
0819: Field f = RenderingHints.class
0820: .getField("VALUE_TEXT_ANTIALIAS_LCD_HRGB");
0821: sm_hrgbRender = f.get(null);
0822: Class[] fracFontMetricsTypeList = new Class[] {
0823: AffineTransform.class, Object.class, Object.class };
0824: sm_frcConstructor = FontRenderContext.class
0825: .getConstructor(fracFontMetricsTypeList);
0826: } catch (NullPointerException npe) {
0827: } catch (SecurityException se) {
0828: } catch (NoSuchFieldException nsfe) {
0829: } catch (IllegalArgumentException iae) {
0830: } catch (IllegalAccessException iae) {
0831: } catch (NoSuchMethodException nsme) {
0832: }
0833: }
0834:
0835: //{{{ updateRenderingHints() method
0836: private void updateRenderingHints() {
0837: Map<RenderingHints.Key, Object> hints = new HashMap<RenderingHints.Key, Object>();
0838:
0839: hints
0840: .put(
0841: RenderingHints.KEY_FRACTIONALMETRICS,
0842: fracFontMetrics ? RenderingHints.VALUE_FRACTIONALMETRICS_ON
0843: : RenderingHints.VALUE_FRACTIONALMETRICS_OFF);
0844:
0845: if (antiAlias.val() == 0) {
0846: hints.put(RenderingHints.KEY_ANTIALIASING,
0847: RenderingHints.VALUE_ANTIALIAS_OFF);
0848: hints.put(RenderingHints.KEY_TEXT_ANTIALIASING,
0849: RenderingHints.VALUE_TEXT_ANTIALIAS_OFF);
0850: }
0851: /** LCD HRGB mode - works with JRE 1.6 only, which is why we use reflection */
0852: else if (antiAlias.val() == 2 && sm_hrgbRender != null) {
0853: hints.put(RenderingHints.KEY_TEXT_ANTIALIASING,
0854: sm_hrgbRender);
0855: Object fontRenderHint = fracFontMetrics ? RenderingHints.VALUE_FRACTIONALMETRICS_ON
0856: : RenderingHints.VALUE_FRACTIONALMETRICS_OFF;
0857: Object[] paramList = new Object[] { null, sm_hrgbRender,
0858: fontRenderHint };
0859: try {
0860: fontRenderContext = (FontRenderContext) sm_frcConstructor
0861: .newInstance(paramList);
0862: } catch (Exception e) {
0863: fontRenderContext = new FontRenderContext(null,
0864: antiAlias.val() > 0, fracFontMetrics);
0865: }
0866: } else /** Standard Antialias Version */
0867: {
0868: hints.put(RenderingHints.KEY_RENDERING,
0869: RenderingHints.VALUE_RENDER_QUALITY);
0870: hints.put(RenderingHints.KEY_ANTIALIASING,
0871: RenderingHints.VALUE_ANTIALIAS_ON);
0872: hints.put(RenderingHints.KEY_TEXT_ANTIALIASING,
0873: RenderingHints.VALUE_TEXT_ANTIALIAS_ON);
0874: fontRenderContext = new FontRenderContext(null, antiAlias
0875: .val() > 0, fracFontMetrics);
0876: }
0877:
0878: renderingHints = new RenderingHints(hints);
0879:
0880: } //}}}
0881:
0882: //}}}
0883:
0884: //{{{ Inner classes
0885:
0886: //{{{ PaintLineBackground class
0887: class PaintLineBackground extends TextAreaExtension {
0888: //{{{ shouldPaintLineHighlight() method
0889: private boolean shouldPaintLineHighlight(int caret, int start,
0890: int end) {
0891: if (!isLineHighlightEnabled() || caret < start
0892: || caret >= end) {
0893: return false;
0894: }
0895:
0896: int count = textArea.getSelectionCount();
0897: if (count == 1) {
0898: Selection s = textArea.getSelection(0);
0899: return s.getStartLine() == s.getEndLine();
0900: } else
0901: return (count == 0);
0902: } //}}}
0903:
0904: //{{{ paintValidLine() method
0905: public void paintValidLine(Graphics2D gfx, int screenLine,
0906: int physicalLine, int start, int end, int y) {
0907: // minimise access$ methods
0908: TextArea textArea = TextAreaPainter.this .textArea;
0909: JEditBuffer buffer = textArea.getBuffer();
0910:
0911: //{{{ Paint line highlight and collapsed fold highlight
0912: boolean collapsedFold = (physicalLine < buffer
0913: .getLineCount() - 1
0914: && buffer.isFoldStart(physicalLine) && !textArea.displayManager
0915: .isLineVisible(physicalLine + 1));
0916:
0917: SyntaxStyle foldLineStyle = null;
0918: if (collapsedFold) {
0919: int level = buffer.getFoldLevel(physicalLine + 1);
0920: if (buffer.getFoldHandler() instanceof IndentFoldHandler)
0921: level = Math.max(1, level / buffer.getIndentSize());
0922: if (level > 3)
0923: level = 0;
0924: foldLineStyle = TextAreaPainter.this .foldLineStyle[level];
0925: }
0926:
0927: int caret = textArea.getCaretPosition();
0928: boolean paintLineHighlight = shouldPaintLineHighlight(
0929: caret, start, end);
0930:
0931: Color bgColor;
0932: if (paintLineHighlight)
0933: bgColor = lineHighlightColor;
0934: else if (collapsedFold) {
0935: bgColor = foldLineStyle.getBackgroundColor();
0936: if (bgColor == null)
0937: bgColor = getBackground();
0938: } else
0939: bgColor = getBackground();
0940:
0941: if (paintLineHighlight || collapsedFold) {
0942: gfx.setColor(bgColor);
0943: gfx.fillRect(0, y, getWidth(), fm.getHeight());
0944: } //}}}
0945:
0946: //{{{ Paint token backgrounds
0947: ChunkCache.LineInfo lineInfo = textArea.chunkCache
0948: .getLineInfo(screenLine);
0949:
0950: if (lineInfo.chunks != null) {
0951: float baseLine = y + fm.getHeight() - fm.getLeading()
0952: - fm.getDescent();
0953: Chunk.paintChunkBackgrounds(lineInfo.chunks, gfx,
0954: textArea.getHorizontalOffset(), baseLine);
0955: } //}}}
0956: } //}}}
0957: } //}}}
0958:
0959: //{{{ PaintSelection class
0960: class PaintSelection extends TextAreaExtension {
0961: //{{{ paintValidLine() method
0962: public void paintValidLine(Graphics2D gfx, int screenLine,
0963: int physicalLine, int start, int end, int y) {
0964: if (textArea.getSelectionCount() == 0)
0965: return;
0966:
0967: gfx
0968: .setColor(textArea.isMultipleSelectionEnabled() ? getMultipleSelectionColor()
0969: : getSelectionColor());
0970:
0971: Iterator<Selection> iter = textArea.getSelectionIterator();
0972: while (iter.hasNext()) {
0973: Selection s = iter.next();
0974: paintSelection(gfx, screenLine, physicalLine, y, s);
0975: }
0976: } //}}}
0977:
0978: //{{{ paintSelection() method
0979: private void paintSelection(Graphics2D gfx, int screenLine,
0980: int physicalLine, int y, Selection s) {
0981: int[] selectionStartAndEnd = textArea.selectionManager
0982: .getSelectionStartAndEnd(screenLine, physicalLine,
0983: s);
0984: if (selectionStartAndEnd == null)
0985: return;
0986:
0987: int x1 = selectionStartAndEnd[0];
0988: int x2 = selectionStartAndEnd[1];
0989:
0990: gfx.fillRect(x1, y, x2 - x1, fm.getHeight());
0991: } //}}}
0992: } //}}}
0993:
0994: //{{{ PaintWrapGuide class
0995: class PaintWrapGuide extends TextAreaExtension {
0996: public void paintScreenLineRange(Graphics2D gfx, int firstLine,
0997: int lastLine, int[] physicalLines, int[] start,
0998: int[] end, int y, int lineHeight) {
0999: if (textArea.wrapMargin != 0 && !textArea.wrapToWidth
1000: && isWrapGuidePainted()) {
1001: gfx.setColor(getWrapGuideColor());
1002: int x = textArea.getHorizontalOffset()
1003: + textArea.wrapMargin;
1004: gfx.drawLine(x, y, x, y + (lastLine - firstLine + 1)
1005: * lineHeight);
1006: }
1007: }
1008:
1009: public String getToolTipText(int x, int y) {
1010: if (textArea.wrapMargin != 0 && !textArea.wrapToWidth
1011: && isWrapGuidePainted()) {
1012: int wrapGuidePos = textArea.wrapMargin
1013: + textArea.getHorizontalOffset();
1014: if (Math.abs(x - wrapGuidePos) < 5) {
1015: return String.valueOf(textArea.getBuffer()
1016: .getProperty("maxLineLen"));
1017: }
1018: }
1019:
1020: return null;
1021: }
1022: } //}}}
1023:
1024: //{{{ PaintText class
1025: class PaintText extends TextAreaExtension {
1026: public void paintValidLine(Graphics2D gfx, int screenLine,
1027: int physicalLine, int start, int end, int y) {
1028: ChunkCache.LineInfo lineInfo = textArea.chunkCache
1029: .getLineInfo(screenLine);
1030:
1031: Font defaultFont = getFont();
1032: Color defaultColor = getForeground();
1033:
1034: gfx.setFont(defaultFont);
1035: gfx.setColor(defaultColor);
1036:
1037: int x = textArea.getHorizontalOffset();
1038: int originalX = x;
1039:
1040: float baseLine = y + fm.getHeight() - fm.getLeading()
1041: - fm.getDescent();
1042:
1043: if (lineInfo.chunks != null) {
1044: x += Chunk.paintChunkList(lineInfo.chunks, gfx,
1045: textArea.getHorizontalOffset(), baseLine,
1046: !Debug.DISABLE_GLYPH_VECTOR);
1047: }
1048:
1049: JEditBuffer buffer = textArea.getBuffer();
1050:
1051: if (!lineInfo.lastSubregion) {
1052: gfx.setFont(defaultFont);
1053: gfx.setColor(eolMarkerColor);
1054: gfx.drawString(":", Math.max(x, textArea
1055: .getHorizontalOffset()
1056: + textArea.wrapMargin + textArea.charWidth),
1057: baseLine);
1058: x += textArea.charWidth;
1059: } else if (physicalLine < buffer.getLineCount() - 1
1060: && buffer.isFoldStart(physicalLine)
1061: && !textArea.displayManager
1062: .isLineVisible(physicalLine + 1)) {
1063: int level = buffer.getFoldLevel(physicalLine + 1);
1064: if (buffer.getFoldHandler() instanceof IndentFoldHandler)
1065: level = Math.max(1, level / buffer.getIndentSize());
1066: if (level > 3)
1067: level = 0;
1068: SyntaxStyle foldLineStyle = TextAreaPainter.this .foldLineStyle[level];
1069:
1070: Font font = foldLineStyle.getFont();
1071: gfx.setFont(font);
1072: gfx.setColor(foldLineStyle.getForegroundColor());
1073:
1074: int nextLine;
1075: int nextScreenLine = screenLine + 1;
1076: if (nextScreenLine < textArea.getVisibleLines()) {
1077: nextLine = textArea.chunkCache
1078: .getLineInfo(nextScreenLine).physicalLine;
1079: } else {
1080: nextLine = textArea.displayManager
1081: .getNextVisibleLine(physicalLine);
1082: }
1083:
1084: if (nextLine == -1)
1085: nextLine = textArea.getLineCount();
1086:
1087: int count = nextLine - physicalLine - 1;
1088: String str = " [" + count + " lines]";
1089:
1090: float width = getStringWidth(str);
1091:
1092: gfx.drawString(str, x, baseLine);
1093: x += width;
1094: } else if (eolMarkers) {
1095: gfx.setFont(defaultFont);
1096: gfx.setColor(eolMarkerColor);
1097: gfx.drawString(".", x, baseLine);
1098: x += textArea.charWidth;
1099: }
1100:
1101: lineInfo.width = (x - originalX);
1102: }
1103: } //}}}
1104:
1105: //{{{ PaintCaret class
1106: class PaintCaret extends TextAreaExtension {
1107: public void paintValidLine(Graphics2D gfx, int screenLine,
1108: int physicalLine, int start, int end, int y) {
1109: if (!textArea.isCaretVisible())
1110: return;
1111:
1112: int caret = textArea.getCaretPosition();
1113: if (caret < start || caret >= end)
1114: return;
1115:
1116: int offset = caret
1117: - textArea.getLineStartOffset(physicalLine);
1118: textArea
1119: .offsetToXY(physicalLine, offset, textArea.offsetXY);
1120: int caretX = textArea.offsetXY.x;
1121: int height = fm.getHeight();
1122:
1123: gfx.setColor(caretColor);
1124:
1125: if (textArea.isOverwriteEnabled()) {
1126: gfx.drawLine(caretX, y + height - 1, caretX
1127: + textArea.charWidth, y + height - 1);
1128: } else if (blockCaret)
1129: gfx.drawRect(caretX, y, textArea.charWidth - 1,
1130: height - 1);
1131: else
1132: gfx.drawLine(caretX, y, caretX, y + height - 1);
1133: }
1134: } //}}}
1135:
1136: //}}}
1137: }
|