Docjar: A Java Source and Docuemnt Enginecom.*    java.*    javax.*    org.*    all    new    plug-in

Quick Search    Search Deep

Source code: jsource/gui/TextAreaPainter.java


1   package jsource.gui;
2   
3   
4   /**
5    * TextAreaPainter.java  12/17/02
6    *
7    * This program is free software; you can redistribute it and/or modify
8    * it under the terms of the GNU Library General Public License as published
9    * by the Free Software Foundation; either version 2 of the License, or
10   * (at your option) any later version.
11   *
12   * This program is distributed in the hope that it will be useful,
13   * but WITHOUT ANY WARRANTY; without even the implied warranty of
14   * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
15   * GNU Library General Public License for more details.
16   */
17  import javax.swing.ToolTipManager;
18  import javax.swing.text.*;
19  import javax.swing.JComponent;
20  import java.awt.event.MouseEvent;
21  import java.awt.*;
22  import jsource.syntax.*;
23  import jsource.syntax.tokenmarker.*;
24  
25  
26  /**
27   * <code>TextAreaPainter</code> is the repaint manager for a <code>JSEditor</code> object.
28   *
29   * Based on <code>TextAreaPainter</code> in jEdit syntax package.
30   *
31   * @author Panagiotis Plevrakis
32   * <br>Email: pplevrakis@hotmail.com
33   * <br>URL:   http://jsource.sourceforge.net
34   */
35  public class TextAreaPainter extends JComponent implements TabExpander {
36  
37      int currentLineIndex = 0;
38      Token currentLineTokens = null;
39      Segment currentLine = null;
40  
41      protected JSEditor textArea = null;
42  
43      protected SyntaxStyle[] styles = null;
44      protected Color caretColor = null;
45      protected Color selectionColor = null;
46      protected Color lineHighlightColor = null;
47      protected Color bracketHighlightColor = null;
48      protected Color eolMarkerColor = null;
49  
50      protected boolean blockCaret = false;
51      protected boolean lineHighlight = false;
52      protected boolean bracketHighlight = false;
53      protected boolean paintInvalid = false;
54      protected boolean eolMarkers = false;
55      protected int cols = 0;
56      protected int rows = 0;
57  
58      protected int tabSize = 0;
59      protected FontMetrics fm = null;
60  
61      protected Highlight highlights = null;
62  
63      /**
64       * Creates a new repaint manager. This should not be called directly.
65       */
66      public TextAreaPainter(JSEditor textArea, TextAreaDefaults defaults) {
67          this.textArea = textArea;
68  
69          setAutoscrolls(true);
70          setDoubleBuffered(true);
71          setOpaque(true);
72  
73          ToolTipManager.sharedInstance().registerComponent(this);
74  
75          currentLine = new Segment();
76          currentLineIndex = -1;
77  
78          setCursor(Cursor.getPredefinedCursor(Cursor.TEXT_CURSOR));
79  
80          setFont(new Font("Courier", Font.PLAIN, 14));
81          setForeground(new Color(209, 209, 209));
82          setBackground(new Color(70, 70, 70));
83  
84          blockCaret = defaults.blockCaret;
85          styles = defaults.styles;
86          cols = defaults.cols;
87          rows = defaults.rows;
88          caretColor = defaults.caretColor;
89          selectionColor = defaults.selectionColor;
90          lineHighlightColor = defaults.lineHighlightColor;
91          lineHighlight = defaults.lineHighlight;
92          bracketHighlightColor = defaults.bracketHighlightColor;
93          bracketHighlight = defaults.bracketHighlight;
94          paintInvalid = defaults.paintInvalid;
95          eolMarkerColor = defaults.eolMarkerColor;
96          eolMarkers = defaults.eolMarkers;
97      }
98  
99      /**
100      * Returns the syntax styles used to paint colorized text. Entry <i>n</i>
101      * will be used to paint tokens with id = <i>n</i>.
102      */
103     public final SyntaxStyle[] getStyles() {
104         return styles;
105     }
106 
107     /**
108      * Sets the syntax styles used to paint colorized text. Entry <i>n</i>
109      * will be used to paint tokens with id = <i>n</i>.
110      * @param styles The syntax styles
111      */
112     public final void setStyles(SyntaxStyle[] styles) {
113         this.styles = styles;
114         repaint();
115     }
116 
117     /**
118      * Returns the caret color.
119      */
120     public final Color getCaretColor() {
121         return caretColor;
122     }
123 
124     /**
125      * Sets the caret color.
126      * @param caretColor The caret color
127      */
128     public final void setCaretColor(Color caretColor) {
129         this.caretColor = caretColor;
130         invalidateSelectedLines();
131     }
132 
133     /**
134      * Returns the selection color.
135      */
136     public final Color getSelectionColor() {
137         return selectionColor;
138     }
139 
140     /**
141      * Sets the selection color.
142      * @param selectionColor The selection color
143      */
144     public final void setSelectionColor(Color selectionColor) {
145         this.selectionColor = selectionColor;
146         invalidateSelectedLines();
147     }
148 
149     /**
150      * Returns the line highlight color.
151      */
152     public final Color getLineHighlightColor() {
153         return lineHighlightColor;
154     }
155 
156     /**
157      * Sets the line highlight color.
158      * @param lineHighlightColor The line highlight color
159      */
160     public final void setLineHighlightColor(Color lineHighlightColor) {
161         this.lineHighlightColor = lineHighlightColor;
162         invalidateSelectedLines();
163     }
164 
165     /**
166      * Returns true if line highlight is enabled, false otherwise.
167      */
168     public final boolean isLineHighlightEnabled() {
169         return lineHighlight;
170     }
171 
172     /**
173      * Enables or disables current line highlighting.
174      * @param lineHighlight true if current line highlight should be enabled,false otherwise
175      */
176     public final void setLineHighlightEnabled(boolean lineHighlight) {
177         this.lineHighlight = lineHighlight;
178         invalidateSelectedLines();
179     }
180 
181     /**
182      * Returns the bracket highlight color.
183      */
184     public final Color getBracketHighlightColor() {
185         return bracketHighlightColor;
186     }
187 
188     /**
189      * Sets the bracket highlight color.
190      * @param bracketHighlightColor The bracket highlight color
191      */
192     public final void setBracketHighlightColor(Color bracketHighlightColor) {
193         this.bracketHighlightColor = bracketHighlightColor;
194         invalidateLine(textArea.getBracketLine());
195     }
196 
197     /**
198      * Returns true if bracket highlighting is enabled, false otherwise.
199      * When bracket highlighting is enabled, the bracket matching the
200      * one before the caret (if any) is highlighted.
201      */
202     public final boolean isBracketHighlightEnabled() {
203         return bracketHighlight;
204     }
205 
206     /**
207      * Enables or disables bracket highlighting.
208      * When bracket highlighting is enabled, the bracket matching the
209      * one before the caret (if any) is highlighted.
210      * @param bracketHighlight True if bracket highlighting should be enabled, false otherwise
211      */
212     public final void setBracketHighlightEnabled(boolean bracketHighlight) {
213         this.bracketHighlight = bracketHighlight;
214         invalidateLine(textArea.getBracketLine());
215     }
216 
217     /**
218      * Returns true if the caret should be drawn as a block, false otherwise.
219      */
220     public final boolean isBlockCaretEnabled() {
221         return blockCaret;
222     }
223 
224     /**
225      * Sets if the caret should be drawn as a block, false otherwise.
226      * @param blockCaret True if the caret should be drawn as a block,false otherwise.
227      */
228     public final void setBlockCaretEnabled(boolean blockCaret) {
229         this.blockCaret = blockCaret;
230         invalidateSelectedLines();
231     }
232 
233     /**
234      * Returns the EOL marker color.
235      */
236     public final Color getEOLMarkerColor() {
237         return eolMarkerColor;
238     }
239 
240     /**
241      * Sets the EOL marker color.
242      * @param eolMarkerColor The EOL marker color
243      */
244     public final void setEOLMarkerColor(Color eolMarkerColor) {
245         this.eolMarkerColor = eolMarkerColor;
246         repaint();
247     }
248 
249     /**
250      * Returns true if EOL markers are drawn, false otherwise.
251      */
252     public final boolean getEOLMarkersPainted() {
253         return eolMarkers;
254     }
255 
256     /**
257      * Sets if EOL markers are to be drawn.
258      * @param eolMarkers True if EOL markers should be drawn, false otherwise
259      */
260     public final void setEOLMarkersPainted(boolean eolMarkers) {
261         this.eolMarkers = eolMarkers;
262         repaint();
263     }
264 
265     /**
266      * Returns true if invalid lines are painted as red tildes (~),false otherwise.
267      */
268     public boolean getInvalidLinesPainted() {
269         return paintInvalid;
270     }
271 
272     /**
273      * Sets if invalid lines are to be painted as red tildes.
274      * @param paintInvalid True if invalid lines should be drawn, false otherwise
275      */
276     public void setInvalidLinesPainted(boolean paintInvalid) {
277         this.paintInvalid = paintInvalid;
278     }
279 
280     /**
281      * Adds a custom highlight painter.
282      * @param highlight The highlight
283      */
284     public void addCustomHighlight(Highlight highlight) {
285         highlight.init(textArea, highlights);
286         highlights = highlight;
287     }
288 
289     /**
290      * Highlight interface.
291      */
292     public interface Highlight {
293 
294         /**
295          * Called after the highlight painter has been added.
296          * @param textArea The text area
297          * @param next The painter this one should delegate to
298          */
299         void init(JSEditor textArea, Highlight next);
300 
301         /**
302          * This should paint the highlight and delgate to the
303          * next highlight painter.
304          * @param gfx The graphics context
305          * @param line The line number
306          * @param y The y co-ordinate of the line
307          */
308         void paintHighlight(Graphics gfx, int line, int y);
309 
310         /**
311          * Returns the tool tip to display at the specified
312          * location. If this highlighter doesn't know what to
313          * display, it should delegate to the next highlight
314          * painter.
315          * @param evt The mouse event
316          */
317         String getToolTipText(MouseEvent evt);
318     }
319 
320     /**
321      * Returns the tool tip to display at the specified location.
322      * @param evt The mouse event
323      */
324     public String getToolTipText(MouseEvent evt) {
325         if (highlights != null)
326             return highlights.getToolTipText(evt);
327         else
328             return null;
329     }
330 
331     /**
332      * Returns the font metrics used by this component.
333      */
334     public FontMetrics getFontMetrics() {
335         return fm;
336     }
337 
338     /**
339      * Sets the font for this component. This is overridden to update the
340      * cached font metrics and to recalculate which lines are visible.
341      * @param font The font
342      */
343     public void setFont(Font font) {
344         super.setFont(font);
345         fm = Toolkit.getDefaultToolkit().getFontMetrics(font);
346         textArea.recalculateVisibleLines();
347     }
348 
349     /**
350      * Repaints the text.
351      * @param g The graphics context
352      */
353     public void paint(Graphics gfx) {
354         tabSize = fm.charWidth(' ') * ((Integer) textArea.getDocument().getProperty(
355                         PlainDocument.tabSizeAttribute)).intValue();
356 
357         Rectangle clipRect = gfx.getClipBounds();
358 
359         gfx.setColor(getBackground());
360         gfx.fillRect(clipRect.x, clipRect.y, clipRect.width, clipRect.height);
361 
362         // We don't use yToLine() here because that method doesn't
363         // return lines past the end of the document
364         int height = fm.getHeight();
365         int firstLine = textArea.getFirstLine();
366         int firstInvalid = firstLine + clipRect.y / height;
367         // Because the clipRect's height is usually an even multiple
368         // of the font height, we subtract 1 from it, otherwise one
369         // too many lines will always be painted.
370         int lastInvalid = firstLine
371                 + (clipRect.y + clipRect.height - 1) / height;
372 
373         try {
374             TokenMarker tokenMarker = textArea.getDocument().getTokenMarker();
375             int x = textArea.getHorizontalOffset();
376 
377             for (int line = firstInvalid; line <= lastInvalid; line++) {
378                 paintLine(gfx, tokenMarker, line, x);
379             }
380 
381             if (tokenMarker != null && tokenMarker.isNextLineRequested()) {
382                 int h = clipRect.y + clipRect.height;
383 
384                 repaint(0, h, getWidth(), getHeight() - h);
385             }
386         } catch (Exception e) {
387             System.err.println("Error repainting line" + " range {" + firstInvalid + "," + lastInvalid + "}:");
388             e.printStackTrace();
389         }
390     }
391 
392     /**
393      * Marks a line as needing a repaint.
394      * @param line The line to invalidate
395      */
396     public final void invalidateLine(int line) {
397         repaint(0, textArea.lineToY(line) + fm.getMaxDescent() + fm.getLeading(),
398                 getWidth(), fm.getHeight());
399     }
400 
401     /**
402      * Marks a range of lines as needing a repaint.
403      * @param firstLine The first line to invalidate
404      * @param lastLine The last line to invalidate
405      */
406     public final void invalidateLineRange(int firstLine, int lastLine) {
407         repaint(0, textArea.lineToY(firstLine) + fm.getMaxDescent() + fm.getLeading(),
408                 getWidth(), (lastLine - firstLine + 1) * fm.getHeight());
409     }
410 
411     /**
412      * Repaints the lines containing the selection.
413      */
414     public final void invalidateSelectedLines() {
415         invalidateLineRange(textArea.getSelectionStartLine(),
416                 textArea.getSelectionEndLine());
417     }
418 
419     /**
420      * Implementation of TabExpander interface. Returns next tab stop after
421      * a specified point.
422      * @param x The x co-ordinate
423      * @param tabOffset Ignored
424      * @return The next tab stop after <i>x</i>
425      */
426     public float nextTabStop(float x, int tabOffset) {
427         int offset = textArea.getHorizontalOffset();
428         int ntabs = ((int) x - offset) / tabSize;
429 
430         return (ntabs + 1) * tabSize + offset;
431     }
432 
433     /**
434      * Returns the painter's preferred size.
435      */
436     public Dimension getPreferredSize() {
437         Dimension dim = new Dimension();
438 
439         dim.width = fm.charWidth('w') * cols;
440         dim.height = fm.getHeight() * rows;
441         return dim;
442     }
443 
444     /**
445      * Returns the painter's minimum size.
446      */
447     public Dimension getMinimumSize() {
448         return getPreferredSize();
449     }
450 
451     protected void paintLine(Graphics gfx, TokenMarker tokenMarker,
452             int line, int x) {
453         Font defaultFont = getFont();
454         Color defaultColor = getForeground();
455 
456         currentLineIndex = line;
457         int y = textArea.lineToY(line);
458 
459         if (line < 0 || line >= textArea.getLineCount()) {
460             if (paintInvalid) {
461                 paintHighlight(gfx, line, y);
462                 styles[Token.INVALID].setGraphicsFlags(gfx, defaultFont);
463                 gfx.drawString("~", 0, y + fm.getHeight());
464             }
465         } else if (tokenMarker == null) {
466             paintPlainLine(gfx, line, defaultFont, defaultColor, x, y);
467         } else {
468             paintSyntaxLine(gfx, tokenMarker, line, defaultFont,
469                     defaultColor, x, y);
470         }
471     }
472 
473     protected void paintPlainLine(Graphics gfx, int line, Font defaultFont,
474             Color defaultColor, int x, int y) {
475         paintHighlight(gfx, line, y);
476         textArea.getLineText(line, currentLine);
477 
478         gfx.setFont(defaultFont);
479         gfx.setColor(defaultColor);
480 
481         y += fm.getHeight();
482         x = Utilities.drawTabbedText(currentLine, x, y, gfx, this, 0);
483 
484         if (eolMarkers) {
485             gfx.setColor(eolMarkerColor);
486             gfx.drawString(".", x, y);
487         }
488     }
489 
490     protected void paintSyntaxLine(Graphics gfx, TokenMarker tokenMarker,
491             int line, Font defaultFont, Color defaultColor, int x, int y) {
492         textArea.getLineText(currentLineIndex, currentLine);
493         currentLineTokens = tokenMarker.markTokens(currentLine,
494                 currentLineIndex);
495 
496         paintHighlight(gfx, line, y);
497 
498         gfx.setFont(defaultFont);
499         gfx.setColor(defaultColor);
500         y += fm.getHeight();
501         x = SyntaxUtilities.paintSyntaxLine(currentLine,
502                 currentLineTokens, styles, this, gfx, x, y);
503 
504         if (eolMarkers) {
505             gfx.setColor(eolMarkerColor);
506             gfx.drawString(".", x, y);
507         }
508     }
509 
510     protected void paintHighlight(Graphics gfx, int line, int y) {
511         if (line >= textArea.getSelectionStartLine()
512                 && line <= textArea.getSelectionEndLine())
513             paintLineHighlight(gfx, line, y);
514 
515         if (highlights != null)
516             highlights.paintHighlight(gfx, line, y);
517 
518         if (bracketHighlight && line == textArea.getBracketLine())
519             paintBracketHighlight(gfx, line, y);
520 
521         if (line == textArea.getCaretLine())
522             paintCaret(gfx, line, y);
523     }
524 
525     protected void paintLineHighlight(Graphics gfx, int line, int y) {
526         int height = fm.getHeight();
527 
528         y += fm.getLeading() + fm.getMaxDescent();
529 
530         int selectionStart = textArea.getSelectionStart();
531         int selectionEnd = textArea.getSelectionEnd();
532 
533         if (selectionStart == selectionEnd) {
534             if (lineHighlight) {
535                 gfx.setColor(lineHighlightColor);
536                 gfx.fillRect(0, y, getWidth(), height);
537             }
538         } else {
539             gfx.setColor(selectionColor);
540 
541             int selectionStartLine = textArea.getSelectionStartLine();
542             int selectionEndLine = textArea.getSelectionEndLine();
543             int lineStart = textArea.getLineStartOffset(line);
544 
545             int x1, x2;
546 
547             if (textArea.isSelectionRectangular()) {
548                 int lineLen = textArea.getLineLength(line);
549 
550                 x1 = textArea._offsetToX(line, Math.min(lineLen,
551                         selectionStart - textArea.getLineStartOffset(
552                         selectionStartLine)));
553                 x2 = textArea._offsetToX(line, Math.min(lineLen,
554                         selectionEnd - textArea.getLineStartOffset(
555                         selectionEndLine)));
556                 if (x1 == x2)
557                     x2++;
558             } else if (selectionStartLine == selectionEndLine) {
559                 x1 = textArea._offsetToX(line,
560                         selectionStart - lineStart);
561                 x2 = textArea._offsetToX(line,
562                         selectionEnd - lineStart);
563             } else if (line == selectionStartLine) {
564                 x1 = textArea._offsetToX(line,
565                         selectionStart - lineStart);
566                 x2 = getWidth();
567             } else if (line == selectionEndLine) {
568                 x1 = 0;
569                 x2 = textArea._offsetToX(line,
570                         selectionEnd - lineStart);
571             } else {
572                 x1 = 0;
573                 x2 = getWidth();
574             }
575 
576             // "inlined" min/max()
577             gfx.fillRect(x1 > x2 ? x2 : x1, y, x1 > x2 ? (x1 - x2) : (x2 - x1), height);
578         }
579 
580     }
581 
582     protected void paintBracketHighlight(Graphics gfx, int line, int y) {
583         int position = textArea.getBracketPosition();
584 
585         if (position == -1)
586             return;
587         y += fm.getLeading() + fm.getMaxDescent();
588         int x = textArea._offsetToX(line, position);
589 
590         gfx.setColor(bracketHighlightColor);
591 
592         // Hack!!! Since there is no fast way to get the character
593         // from the bracket matching routine, we use ( since all
594         // brackets probably have the same width anyway
595         gfx.drawRect(x, y, fm.charWidth('(') - 1, fm.getHeight() - 1);
596     }
597 
598     protected void paintCaret(Graphics gfx, int line, int y) {
599         if (textArea.isCaretVisible()) {
600             int offset = textArea.getCaretPosition()
601                     - textArea.getLineStartOffset(line);
602             int caretX = textArea._offsetToX(line, offset);
603             int caretWidth = ((blockCaret || textArea.isOverwriteEnabled())
604                     ? fm.charWidth('w')
605                     : 1);
606 
607             y += fm.getLeading() + fm.getMaxDescent();
608             int height = fm.getHeight();
609 
610             gfx.setColor(caretColor);
611 
612             if (textArea.isOverwriteEnabled()) {
613                 gfx.fillRect(caretX, y + height - 1,
614                         caretWidth, 1);
615             } else {
616                 gfx.drawRect(caretX, y, caretWidth, height - 1);
617             }
618         }
619     }
620 }