KickJava   Java API By Example, From Geeks To Geeks.

Java > Open Source Codes > org > gjt > sp > jedit > textarea > TextAreaPainter


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
23 package org.gjt.sp.jedit.textarea;
24
25 //{{{ Imports
26
import javax.swing.text.*;
27 import javax.swing.JComponent JavaDoc;
28 import java.awt.event.MouseEvent JavaDoc;
29 import java.awt.font.*;
30 import java.awt.geom.AffineTransform JavaDoc;
31 import java.awt.*;
32 import java.lang.reflect.Constructor JavaDoc;
33 import java.lang.reflect.Field JavaDoc;
34 import java.util.*;
35 import org.gjt.sp.jedit.buffer.IndentFoldHandler;
36 import org.gjt.sp.jedit.buffer.JEditBuffer;
37 import org.gjt.sp.jedit.syntax.Chunk;
38 import org.gjt.sp.jedit.syntax.SyntaxStyle;
39 import org.gjt.sp.jedit.syntax.Token;
40 import org.gjt.sp.jedit.Debug;
41
42 import org.gjt.sp.util.Log;
43 //}}}
44

45 /**
46  * The text area painter is the component responsible for displaying the
47  * text of the current buffer. The only methods in this class that should
48  * be called by plugins are those for adding and removing
49  * text area extensions.
50  *
51  * @see #addExtension(TextAreaExtension)
52  * @see #addExtension(int,TextAreaExtension)
53  * @see #removeExtension(TextAreaExtension)
54  * @see TextAreaExtension
55  * @see TextArea
56  *
57  * @author Slava Pestov
58  * @version $Id: TextAreaPainter.java 7156 2006-10-02 21:33:17Z kpouer $
59  */

60 public class TextAreaPainter extends JComponent JavaDoc implements TabExpander
61 {
62     //{{{ Layers
63
/**
64      * The lowest possible layer.
65      * @see #addExtension(int,TextAreaExtension)
66      * @since jEdit 4.0pre4
67      */

68     public static final int LOWEST_LAYER = Integer.MIN_VALUE;
69
70     /**
71      * Below selection layer. The JDiff plugin will use this.
72      * @see #addExtension(int,TextAreaExtension)
73      * @since jEdit 4.0pre4
74      */

75     public static final int BACKGROUND_LAYER = -60;
76
77     /**
78      * The line highlight and collapsed fold highlight layer.
79      * @see #addExtension(int,TextAreaExtension)
80      * @since jEdit 4.0pre7
81      */

82     public static final int LINE_BACKGROUND_LAYER = -50;
83
84     /**
85      * Below selection layer.
86      * @see #addExtension(int,TextAreaExtension)
87      * @since jEdit 4.0pre4
88      */

89     public static final int BELOW_SELECTION_LAYER = -40;
90
91     /**
92      * Selection layer. Most extensions will be above this layer, but some
93      * (eg, JDiff) will want to be below the selection.
94      * @see #addExtension(int,TextAreaExtension)
95      * @since jEdit 4.0pre4
96      */

97     public static final int SELECTION_LAYER = -30;
98
99     /**
100      * Wrap guide layer. Most extensions will be above this layer.
101      * @since jEdit 4.0pre4
102      */

103     public static final int WRAP_GUIDE_LAYER = -20;
104
105     /**
106      * Below most extensions layer.
107      * @see #addExtension(int,TextAreaExtension)
108      * @since jEdit 4.0pre4
109      */

110     public static final int BELOW_MOST_EXTENSIONS_LAYER = -10;
111
112     /**
113      * Default extension layer. This is above the wrap guide but below the
114      * structure highlight.
115      * @since jEdit 4.0pre4
116      */

117     public static final int DEFAULT_LAYER = 0;
118
119     /**
120      * Block caret layer. Most extensions will be below this layer.
121      * @since jEdit 4.2pre1
122      */

123     public static final int BLOCK_CARET_LAYER = 50;
124
125     /**
126      * Bracket highlight layer. Most extensions will be below this layer.
127      * @since jEdit 4.0pre4
128      */

129     public static final int BRACKET_HIGHLIGHT_LAYER = 100;
130
131     /**
132      * Text layer. Most extensions will be below this layer.
133      * @since jEdit 4.2pre1
134      */

135     public static final int TEXT_LAYER = 200;
136
137     /**
138      * Caret layer. Most extensions will be below this layer.
139      * @since jEdit 4.2pre1
140      */

141     public static final int CARET_LAYER = 300;
142
143     /**
144      * Highest possible layer.
145      * @since jEdit 4.0pre4
146      */

147     public static final int HIGHEST_LAYER = Integer.MAX_VALUE;
148     //}}}
149

150     //{{{ setBounds() method
151
/**
152      * It is a bad idea to override this, but we need to get the component
153      * event before the first repaint.
154      */

155     public void setBounds(int x, int y, int width, int height)
156     {
157         if(x == getX() && y == getY() && width == getWidth()
158             && height == getHeight())
159         {
160             return;
161         }
162
163         super.setBounds(x,y,width,height);
164
165         textArea.recalculateVisibleLines();
166         if(!textArea.getBuffer().isLoading())
167             textArea.recalculateLastPhysicalLine();
168         textArea.propertiesChanged();
169         textArea.updateMaxHorizontalScrollWidth();
170         textArea.scrollBarsInitialized = true;
171
172         textArea.repaintMgr.updateGraphics();
173     } //}}}
174

175     //{{{ getFocusTraversalKeysEnabled() method
176
/**
177      * Makes the tab key work in Java 1.4.
178      * @since jEdit 3.2pre4
179      */

180     public boolean getFocusTraversalKeysEnabled()
181     {
182         return false;
183     } //}}}
184

185     //{{{ Getters and setters
186

187     //{{{ getStyles() method
188
/**
189      * Returns the syntax styles used to paint colorized text. Entry <i>n</i>
190      * will be used to paint tokens with id = <i>n</i>.
191      * @see org.gjt.sp.jedit.syntax.Token
192      */

193     public final SyntaxStyle[] getStyles()
194     {
195         return styles;
196     } //}}}
197

198     //{{{ setStyles() method
199
/**
200      * Sets the syntax styles used to paint colorized text. Entry <i>n</i>
201      * will be used to paint tokens with id = <i>n</i>.
202      * @param styles The syntax styles
203      * @see org.gjt.sp.jedit.syntax.Token
204      */

205     public final void setStyles(SyntaxStyle[] styles)
206     {
207         // assumed this is called after a font render context is set up.
208
// changing font render context settings without a setStyles()
209
// call will not reset cached monospaced font info.
210
fonts.clear();
211
212         this.styles = styles;
213         styles[Token.NULL] = new SyntaxStyle(getForeground(),null,getFont());
214         repaint();
215     } //}}}
216

217     //{{{ getCaretColor() method
218
/**
219      * Returns the caret color.
220      */

221     public final Color getCaretColor()
222     {
223         return caretColor;
224     } //}}}
225

226     //{{{ setCaretColor() method
227
/**
228      * Sets the caret color.
229      * @param caretColor The caret color
230      */

231     public final void setCaretColor(Color caretColor)
232     {
233         this.caretColor = caretColor;
234         if(textArea.getBuffer() != null)
235             textArea.invalidateLine(textArea.getCaretLine());
236     } //}}}
237

238     //{{{ getSelectionColor() method
239
/**
240      * Returns the selection color.
241      */

242     public final Color getSelectionColor()
243     {
244         return selectionColor;
245     } //}}}
246

247     //{{{ setSelectionColor() method
248
/**
249      * Sets the selection color.
250      * @param selectionColor The selection color
251      */

252     public final void setSelectionColor(Color selectionColor)
253     {
254         this.selectionColor = selectionColor;
255         textArea.repaint();
256     } //}}}
257

258     //{{{ getMultipleSelectionColor() method
259
/**
260      * Returns the multiple selection color.
261      * @since jEdit 4.2pre1
262      */

263     public final Color getMultipleSelectionColor()
264     {
265         return multipleSelectionColor;
266     } //}}}
267

268     //{{{ setMultipleSelectionColor() method
269
/**
270      * Sets the multiple selection color.
271      * @param multipleSelectionColor The multiple selection color
272      * @since jEdit 4.2pre1
273      */

274     public final void setMultipleSelectionColor(Color multipleSelectionColor)
275     {
276         this.multipleSelectionColor = multipleSelectionColor;
277         textArea.repaint();
278     } //}}}
279

280     //{{{ getLineHighlightColor() method
281
/**
282      * Returns the line highlight color.
283      */

284     public final Color getLineHighlightColor()
285     {
286         return lineHighlightColor;
287     } //}}}
288

289     //{{{ setLineHighlightColor() method
290
/**
291      * Sets the line highlight color.
292      * @param lineHighlightColor The line highlight color
293      */

294     public final void setLineHighlightColor(Color lineHighlightColor)
295     {
296         this.lineHighlightColor = lineHighlightColor;
297         if(textArea.getBuffer() != null)
298             textArea.invalidateLine(textArea.getCaretLine());
299     } //}}}
300

301     //{{{ isLineHighlightEnabled() method
302
/**
303      * Returns true if line highlight is enabled, false otherwise.
304      */

305     public final boolean isLineHighlightEnabled()
306     {
307         return lineHighlight;
308     } //}}}
309

310     //{{{ setLineHighlightEnabled() method
311
/**
312      * Enables or disables current line highlighting.
313      * @param lineHighlight True if current line highlight should be enabled,
314      * false otherwise
315      */

316     public final void setLineHighlightEnabled(boolean lineHighlight)
317     {
318         this.lineHighlight = lineHighlight;
319         textArea.repaint();
320     } //}}}
321

322     //{{{ getStructureHighlightColor() method
323
/**
324      * Returns the structure highlight color.
325      * @since jEdit 4.2pre3
326      */

327     public final Color getStructureHighlightColor()
328     {
329         return structureHighlightColor;
330     } //}}}
331

332     //{{{ setStructureHighlightColor() method
333
/**
334      * Sets the structure highlight color.
335      * @param structureHighlightColor The bracket highlight color
336      * @since jEdit 4.2pre3
337      */

338     public final void setStructureHighlightColor(
339         Color structureHighlightColor)
340     {
341         this.structureHighlightColor = structureHighlightColor;
342         textArea.invalidateStructureMatch();
343     } //}}}
344

345     //{{{ isStructureHighlightEnabled() method
346
/**
347      * Returns true if structure highlighting is enabled, false otherwise.
348      * @since jEdit 4.2pre3
349      */

350     public final boolean isStructureHighlightEnabled()
351     {
352         return structureHighlight;
353     } //}}}
354

355     //{{{ setStructureHighlightEnabled() method
356
/**
357      * Enables or disables structure highlighting.
358      * @param structureHighlight True if structure highlighting should be
359      * enabled, false otherwise
360      * @since jEdit 4.2pre3
361      */

362     public final void setStructureHighlightEnabled(boolean structureHighlight)
363     {
364         this.structureHighlight = structureHighlight;
365         textArea.invalidateStructureMatch();
366     } //}}}
367

368     //{{{ isBlockCaretEnabled() method
369
/**
370      * Returns true if the caret should be drawn as a block, false otherwise.
371      */

372     public final boolean isBlockCaretEnabled()
373     {
374         return blockCaret;
375     } //}}}
376

377     //{{{ setBlockCaretEnabled() method
378
/**
379      * Sets if the caret should be drawn as a block, false otherwise.
380      * @param blockCaret True if the caret should be drawn as a block,
381      * false otherwise.
382      */

383     public final void setBlockCaretEnabled(boolean blockCaret)
384     {
385         this.blockCaret = blockCaret;
386         extensionMgr.removeExtension(caretExtension);
387         if(blockCaret)
388             addExtension(BLOCK_CARET_LAYER,caretExtension);
389         else
390             addExtension(CARET_LAYER,caretExtension);
391         if(textArea.getBuffer() != null)
392             textArea.invalidateLine(textArea.getCaretLine());
393     } //}}}
394

395     //{{{ getEOLMarkerColor() method
396
/**
397      * Returns the EOL marker color.
398      */

399     public final Color getEOLMarkerColor()
400     {
401         return eolMarkerColor;
402     } //}}}
403

404     //{{{ setEOLMarkerColor() method
405
/**
406      * Sets the EOL marker color.
407      * @param eolMarkerColor The EOL marker color
408      */

409     public final void setEOLMarkerColor(Color eolMarkerColor)
410     {
411         this.eolMarkerColor = eolMarkerColor;
412         repaint();
413     } //}}}
414

415     //{{{ getEOLMarkersPainted() method
416
/**
417      * Returns true if EOL markers are drawn, false otherwise.
418      */

419     public final boolean getEOLMarkersPainted()
420     {
421         return eolMarkers;
422     } //}}}
423

424     //{{{ setEOLMarkersPainted() method
425
/**
426      * Sets if EOL markers are to be drawn.
427      * @param eolMarkers True if EOL markers should be drawn, false otherwise
428      */

429     public final void setEOLMarkersPainted(boolean eolMarkers)
430     {
431         this.eolMarkers = eolMarkers;
432         repaint();
433     } //}}}
434

435     //{{{ getWrapGuideColor() method
436
/**
437      * Returns the wrap guide color.
438      */

439     public final Color getWrapGuideColor()
440     {
441         return wrapGuideColor;
442     } //}}}
443

444     //{{{ setWrapGuideColor() method
445
/**
446      * Sets the wrap guide color.
447      * @param wrapGuideColor The wrap guide color
448      */

449     public final void setWrapGuideColor(Color wrapGuideColor)
450     {
451         this.wrapGuideColor = wrapGuideColor;
452         repaint();
453     } //}}}
454

455     //{{{ isWrapGuidePainted() method
456
/**
457      * Returns true if the wrap guide is drawn, false otherwise.
458      * @since jEdit 4.0pre4
459      */

460     public final boolean isWrapGuidePainted()
461     {
462         return wrapGuide;
463     } //}}}
464

465     //{{{ setWrapGuidePainted() method
466
/**
467      * Sets if the wrap guide is to be drawn.
468      * @param wrapGuide True if the wrap guide should be drawn, false otherwise
469      */

470     public final void setWrapGuidePainted(boolean wrapGuide)
471     {
472         this.wrapGuide = wrapGuide;
473         repaint();
474     } //}}}
475

476     //{{{ getFoldLineStyle() method
477
/**
478      * Returns the fold line style. The first element is the style for
479      * lines with a fold level greater than 3. The remaining elements
480      * are for fold levels 1 to 3.
481      */

482     public final SyntaxStyle[] getFoldLineStyle()
483     {
484         return foldLineStyle;
485     } //}}}
486

487     //{{{ setFoldLineStyle() method
488
/**
489      * Sets the fold line style. The first element is the style for
490      * lines with a fold level greater than 3. The remaining elements
491      * are for fold levels 1 to 3.
492      * @param foldLineStyle The fold line style
493      */

494     public final void setFoldLineStyle(SyntaxStyle[] foldLineStyle)
495     {
496         this.foldLineStyle = foldLineStyle;
497         repaint();
498     } //}}}
499

500     //{{{ setAntiAliasEnabled() method
501
/**
502      * @deprecated use setAntiAlias(AntiAlias newMode)
503      */

504     public void setAntiAliasEnabled(boolean isEnabled) {
505         
506         setAntiAlias(new AntiAlias(isEnabled));
507     }
508     /**
509      * As of jEdit 4.3pre4, a new JDK 1.6 subpixel antialias mode is supported.
510      *
511      * @since jEdit 4.2pre4
512      */

513     public void setAntiAlias(AntiAlias newValue)
514     {
515         this.antiAlias = newValue;
516         updateRenderingHints();
517     } //}}}
518

519     /**
520      * @return the AntiAlias value that is currently used for TextAreas.
521      * @since jedit 4.3pre4
522      */

523     public AntiAlias getAntiAlias() {
524         return antiAlias;
525     }
526     
527     //{{{ isAntiAliasEnabled() method
528
/**
529      * Returns if anti-aliasing is enabled.
530      * @since jEdit 3.2pre6
531      * @deprecated - use @ref getAntiAlias()
532      */

533     public boolean isAntiAliasEnabled()
534     {
535         return antiAlias.val() > 0;
536     } //}}}
537

538     //{{{ setFractionalFontMetricsEnabled() method
539
/**
540      * Sets if fractional font metrics should be enabled. Has no effect when
541      * running on Java 1.1.
542      * @since jEdit 3.2pre6
543      */

544     public void setFractionalFontMetricsEnabled(boolean fracFontMetrics)
545     {
546         this.fracFontMetrics = fracFontMetrics;
547         updateRenderingHints();
548     } //}}}
549

550     //{{{ isFractionalFontMetricsEnabled() method
551
/**
552      * Returns if fractional font metrics are enabled.
553      * @since jEdit 3.2pre6
554      */

555     public boolean isFractionalFontMetricsEnabled()
556     {
557         return fracFontMetrics;
558     } //}}}
559

560     //{{{ getFontRenderContext() method
561
/**
562      * Returns the font render context.
563      * @since jEdit 4.0pre4
564      */

565     public FontRenderContext getFontRenderContext()
566     {
567         return fontRenderContext;
568     } //}}}
569

570     //}}}
571

572     //{{{ addExtension() method
573
/**
574      * Adds a text area extension, which can perform custom painting and
575      * tool tip handling.
576      * @param extension The extension
577      * @since jEdit 4.0pre4
578      */

579     public void addExtension(TextAreaExtension extension)
580     {
581         extensionMgr.addExtension(DEFAULT_LAYER,extension);
582         repaint();
583     } //}}}
584

585     //{{{ addExtension() method
586
/**
587      * Adds a text area extension, which can perform custom painting and
588      * tool tip handling.
589      * @param layer The layer to add the extension to. Note that more than
590      * extension can share the same layer.
591      * @param extension The extension
592      * @since jEdit 4.0pre4
593      */

594     public void addExtension(int layer, TextAreaExtension extension)
595     {
596         extensionMgr.addExtension(layer,extension);
597         repaint();
598     } //}}}
599

600     //{{{ removeExtension() method
601
/**
602      * Removes a text area extension. It will no longer be asked to
603      * perform custom painting and tool tip handling.
604      * @param extension The extension
605      * @since jEdit 4.0pre4
606      */

607     public void removeExtension(TextAreaExtension extension)
608     {
609         extensionMgr.removeExtension(extension);
610         repaint();
611     } //}}}
612

613     //{{{ getExtensions() method
614
/**
615      * Returns an array of registered text area extensions. Useful for
616      * debugging purposes.
617      * @since jEdit 4.1pre5
618      */

619     public TextAreaExtension[] getExtensions()
620     {
621         return extensionMgr.getExtensions();
622     } //}}}
623

624     //{{{ getToolTipText() method
625
/**
626      * Returns the tool tip to display at the specified location.
627      * @param evt The mouse event
628      */

629     public String JavaDoc getToolTipText(MouseEvent JavaDoc evt)
630     {
631         if(textArea.getBuffer().isLoading())
632             return null;
633
634         return extensionMgr.getToolTipText(evt.getX(),evt.getY());
635     } //}}}
636

637     //{{{ getFontMetrics() method
638
/**
639      * Returns the font metrics used by this component.
640      */

641     public FontMetrics getFontMetrics()
642     {
643         return fm;
644     } //}}}
645

646     //{{{ setFont() method
647
/**
648      * Sets the font for this component. This is overridden to update the
649      * cached font metrics and to recalculate which lines are visible.
650      * @param font The font
651      */

652     public void setFont(Font font)
653     {
654         super.setFont(font);
655         fm = getFontMetrics(font);
656         textArea.recalculateVisibleLines();
657         if(textArea.getBuffer() != null
658             && !textArea.getBuffer().isLoading())
659             textArea.recalculateLastPhysicalLine();
660         textArea.propertiesChanged();
661     } //}}}
662

663     //{{{ getStringWidth() method
664
/**
665      * Returns the width of the given string, in pixels, using the text
666      * area's current font.
667      *
668      * @since jEdit 4.2final
669      */

670     public float getStringWidth(String JavaDoc str)
671     {
672         if(textArea.charWidth != 0)
673             return textArea.charWidth * str.length();
674         else
675         {
676             return (float)getFont().getStringBounds(
677                 str,getFontRenderContext()).getWidth();
678         }
679     } //}}}
680

681     //{{{ update() method
682
/**
683      * Repaints the text.
684      * @param _gfx The graphics context
685      */

686     public void update(Graphics _gfx)
687     {
688         paint(_gfx);
689     } //}}}
690

691     //{{{ paint() method
692
/**
693      * Repaints the text.
694      * @param _gfx The graphics context
695      */

696     public void paint(Graphics _gfx)
697     {
698         Graphics2D gfx = textArea.repaintMgr.getGraphics();
699
700         gfx.setRenderingHints(renderingHints);
701         fontRenderContext = gfx.getFontRenderContext();
702
703         Rectangle clipRect = _gfx.getClipBounds();
704
705         JEditBuffer buffer = textArea.getBuffer();
706         int height = fm.getHeight();
707         if(height == 0 || buffer.isLoading())
708         {
709             _gfx.setColor(getBackground());
710             _gfx.fillRect(clipRect.x,clipRect.y,clipRect.width,clipRect.height);
711         }
712         else
713         {
714             long prepareTime = System.currentTimeMillis();
715             FastRepaintManager.RepaintLines lines
716                 = textArea.repaintMgr.prepareGraphics(clipRect,
717                 textArea.getFirstLine(),gfx);
718             prepareTime = (System.currentTimeMillis() - prepareTime);
719
720             long linesTime = System.currentTimeMillis();
721             int numLines = (lines.last - lines.first + 1);
722
723             int y = lines.first * height;
724             gfx.fillRect(0,y,getWidth(),numLines * height);
725
726             extensionMgr.paintScreenLineRange(textArea,gfx,
727                 lines.first,lines.last,y,height);
728             linesTime = (System.currentTimeMillis() - linesTime);
729
730             textArea.repaintMgr.setFastScroll(
731                 clipRect.equals(new Rectangle(0,0,
732                 getWidth(),getHeight())));
733
734             long blitTime = System.currentTimeMillis();
735             textArea.repaintMgr.paint(_gfx);
736             blitTime = (System.currentTimeMillis() - blitTime);
737
738             if(Debug.PAINT_TIMER && numLines >= 1)
739                 Log.log(Log.DEBUG,this,"repainting " + numLines + " lines took " + prepareTime + "/" + linesTime + "/" + blitTime + " ms");
740         }
741
742         textArea.updateMaxHorizontalScrollWidth();
743     } //}}}
744

745     //{{{ nextTabStop() method
746
/**
747      * Implementation of TabExpander interface. Returns next tab stop after
748      * a specified point.
749      * @param x The x co-ordinate
750      * @param tabOffset Ignored
751      * @return The next tab stop after <i>x</i>
752      */

753     public float nextTabStop(float x, int tabOffset)
754     {
755         int ntabs = (int)(x / textArea.tabSize);
756         return (ntabs + 1) * textArea.tabSize;
757     } //}}}
758

759     //{{{ getPreferredSize() method
760
/**
761      * Returns the painter's preferred size.
762      */

763     public Dimension getPreferredSize()
764     {
765         Dimension dim = new Dimension();
766
767         char[] foo = new char[80];
768         for(int i = 0; i < foo.length; i++)
769             foo[i] = ' ';
770         dim.width = (int)getStringWidth(new String JavaDoc(foo));
771         dim.height = fm.getHeight() * 25;
772         return dim;
773     } //}}}
774

775     //{{{ getMinimumSize() method
776
/**
777      * Returns the painter's minimum size.
778      */

779     public Dimension getMinimumSize()
780     {
781         return getPreferredSize();
782     } //}}}
783

784     //{{{ Package-private members
785

786     //{{{ Instance variables
787
/* package-private since they are accessed by inner classes and we
788      * want this to be fast */

789     TextArea textArea;
790
791     SyntaxStyle[] styles;
792     Color caretColor;
793     Color selectionColor;
794     Color multipleSelectionColor;
795     Color lineHighlightColor;
796     Color structureHighlightColor;
797     Color eolMarkerColor;
798     Color wrapGuideColor;
799
800     SyntaxStyle[] foldLineStyle;
801
802     boolean blockCaret;
803     boolean lineHighlight;
804     boolean structureHighlight;
805     boolean eolMarkers;
806     boolean wrapGuide;
807     AntiAlias antiAlias;
808     boolean fracFontMetrics;
809
810     // should try to use this as little as possible.
811
FontMetrics fm;
812     //}}}
813

814     //{{{ TextAreaPainter constructor
815
/**
816      * Creates a new painter. Do not create instances of this class
817      * directly.
818      */

819     TextAreaPainter(TextArea textArea)
820     {
821         enableEvents(AWTEvent.FOCUS_EVENT_MASK
822             | AWTEvent.KEY_EVENT_MASK
823             | AWTEvent.MOUSE_EVENT_MASK);
824
825         this.textArea = textArea;
826         antiAlias = new AntiAlias(0);
827         fonts = new HashMap();
828         extensionMgr = new ExtensionManager();
829
830         setAutoscrolls(true);
831         setOpaque(true);
832         setRequestFocusEnabled(false);
833         setDoubleBuffered(false);
834
835         setCursor(Cursor.getPredefinedCursor(Cursor.TEXT_CURSOR));
836
837         fontRenderContext = new FontRenderContext(null,false,false);
838
839         addExtension(LINE_BACKGROUND_LAYER,new PaintLineBackground());
840         addExtension(SELECTION_LAYER,new PaintSelection());
841         addExtension(WRAP_GUIDE_LAYER,new PaintWrapGuide());
842         addExtension(BRACKET_HIGHLIGHT_LAYER,new StructureMatcher
843
            .Highlight(textArea));
844         addExtension(TEXT_LAYER,new PaintText());
845         caretExtension = new PaintCaret();
846     } //}}}
847

848     //}}}
849

850     //{{{ Private members
851

852     //{{{ Instance variables
853
private ExtensionManager extensionMgr;
854     private PaintCaret caretExtension;
855     private RenderingHints renderingHints;
856     private FontRenderContext fontRenderContext;
857     private Map fonts;
858     //}}}
859

860     private static Object JavaDoc sm_hrgbRender = null;
861     private static Constructor JavaDoc sm_frcConstructor = null;
862     
863     static
864     {
865             try
866             {
867                 Field JavaDoc f = RenderingHints.class.getField("VALUE_TEXT_ANTIALIAS_LCD_HRGB");
868                 sm_hrgbRender = f.get(null);
869                 Class JavaDoc[] fracFontMetricsTypeList = new Class JavaDoc[] {AffineTransform JavaDoc.class, Object JavaDoc.class, Object JavaDoc.class};
870                 sm_frcConstructor = FontRenderContext.class.getConstructor(fracFontMetricsTypeList);
871             }
872             catch (NullPointerException JavaDoc npe) {}
873             catch (SecurityException JavaDoc se) {}
874             catch (NoSuchFieldException JavaDoc nsfe) {}
875             catch (IllegalArgumentException JavaDoc iae) {}
876             catch (IllegalAccessException JavaDoc iae) {}
877             catch (NoSuchMethodException JavaDoc nsme) {}
878     }
879     //{{{ updateRenderingHints() method
880
private void updateRenderingHints()
881     {
882         Map<RenderingHints.Key,Object JavaDoc> hints = new HashMap<RenderingHints.Key,Object JavaDoc>();
883
884         hints.put(RenderingHints.KEY_FRACTIONALMETRICS,
885             fracFontMetrics ? RenderingHints.VALUE_FRACTIONALMETRICS_ON
886                 : RenderingHints.VALUE_FRACTIONALMETRICS_OFF);
887
888         if (antiAlias.val() == 0) {
889             hints.put(RenderingHints.KEY_ANTIALIASING, RenderingHints.VALUE_ANTIALIAS_OFF);
890             hints.put(RenderingHints.KEY_TEXT_ANTIALIASING, RenderingHints.VALUE_TEXT_ANTIALIAS_OFF);
891         }
892         /** LCD HRGB mode - works with JRE 1.6 only, which is why we use reflection */
893         else if (antiAlias.val() == 2 && sm_hrgbRender != null )
894         {
895             hints.put(RenderingHints.KEY_TEXT_ANTIALIASING, sm_hrgbRender);
896             Object JavaDoc fontRenderHint = fracFontMetrics ?
897                 RenderingHints.VALUE_FRACTIONALMETRICS_ON :
898                 RenderingHints.VALUE_FRACTIONALMETRICS_OFF;
899             Object JavaDoc[] paramList = new Object JavaDoc[] {null, sm_hrgbRender, fontRenderHint};
900             try
901             {
902                 fontRenderContext = (FontRenderContext) sm_frcConstructor.newInstance(paramList);
903             }
904             catch (Exception JavaDoc e)
905             {
906                 fontRenderContext = new FontRenderContext(null, antiAlias.val() > 0, fracFontMetrics);
907             }
908         }
909         else /** Standard Antialias Version */
910         {
911             hints.put(RenderingHints.KEY_RENDERING, RenderingHints.VALUE_RENDER_QUALITY);
912             hints.put(RenderingHints.KEY_ANTIALIASING, RenderingHints.VALUE_ANTIALIAS_ON);
913             hints.put(RenderingHints.KEY_TEXT_ANTIALIASING, RenderingHints.VALUE_TEXT_ANTIALIAS_ON);
914             fontRenderContext = new FontRenderContext(null, antiAlias.val() > 0, fracFontMetrics);
915         }
916
917         renderingHints = new RenderingHints(hints);
918            
919         
920     } //}}}
921

922     //}}}
923

924     //{{{ Inner classes
925

926     //{{{ PaintLineBackground class
927
class PaintLineBackground extends TextAreaExtension
928     {
929         //{{{ shouldPaintLineHighlight() method
930
private boolean shouldPaintLineHighlight(int caret, int start, int end)
931         {
932             if(!isLineHighlightEnabled()
933                 || caret < start || caret >= end)
934             {
935                 return false;
936             }
937
938             int count = textArea.getSelectionCount();
939             if(count == 1)
940             {
941                 Selection s = textArea.getSelection(0);
942                 return s.getStartLine() == s.getEndLine();
943             }
944             else
945                 return (count == 0);
946         } //}}}
947

948         //{{{ paintValidLine() method
949
public void paintValidLine(Graphics2D gfx, int screenLine,
950             int physicalLine, int start, int end, int y)
951         {
952             // minimise access$ methods
953
TextArea textArea = TextAreaPainter.this.textArea;
954             JEditBuffer buffer = textArea.getBuffer();
955
956             //{{{ Paint line highlight and collapsed fold highlight
957
boolean collapsedFold =
958                 (physicalLine < buffer.getLineCount() - 1
959                 && buffer.isFoldStart(physicalLine)
960                 && !textArea.displayManager
961                 .isLineVisible(physicalLine + 1));
962
963             SyntaxStyle foldLineStyle = null;
964             if(collapsedFold)
965             {
966                 int level = buffer.getFoldLevel(physicalLine + 1);
967                 if(buffer.getFoldHandler() instanceof IndentFoldHandler)
968                     level = Math.max(1,level / buffer.getIndentSize());
969                 if(level > 3)
970                     level = 0;
971                 foldLineStyle = TextAreaPainter.this.foldLineStyle[level];
972             }
973
974             int caret = textArea.getCaretPosition();
975             boolean paintLineHighlight = shouldPaintLineHighlight(
976                 caret,start,end);
977
978             Color bgColor;
979             if(paintLineHighlight)
980                 bgColor = lineHighlightColor;
981             else if(collapsedFold)
982             {
983                 bgColor = foldLineStyle.getBackgroundColor();
984                 if(bgColor == null)
985                     bgColor = getBackground();
986             }
987             else
988                 bgColor = getBackground();
989
990             if(paintLineHighlight || collapsedFold)
991             {
992                 gfx.setColor(bgColor);
993                 gfx.fillRect(0,y,getWidth(),fm.getHeight());
994             } //}}}
995

996             //{{{ Paint token backgrounds
997
ChunkCache.LineInfo lineInfo = textArea.chunkCache
998                 .getLineInfo(screenLine);
999
1000            if(lineInfo.chunks != null)
1001            {
1002                float baseLine = y + fm.getHeight()
1003                    - fm.getLeading() - fm.getDescent();
1004                Chunk.paintChunkBackgrounds(
1005                    lineInfo.chunks,gfx,
1006                    textArea.getHorizontalOffset(),
1007                    baseLine);
1008            } //}}}
1009
} //}}}
1010
} //}}}
1011

1012    //{{{ PaintSelection class
1013
class PaintSelection extends TextAreaExtension
1014    {
1015        //{{{ paintValidLine() method
1016
public void paintValidLine(Graphics2D gfx, int screenLine,
1017            int physicalLine, int start, int end, int y)
1018        {
1019            if(textArea.getSelectionCount() == 0)
1020                return;
1021
1022            gfx.setColor(textArea.isMultipleSelectionEnabled()
1023                ? getMultipleSelectionColor()
1024                : getSelectionColor());
1025
1026            Iterator<Selection> iter = textArea.getSelectionIterator();
1027            while(iter.hasNext())
1028            {
1029                Selection s = iter.next();
1030                paintSelection(gfx,screenLine,physicalLine,y,s);
1031            }
1032        } //}}}
1033

1034        //{{{ paintSelection() method
1035
private void paintSelection(Graphics2D gfx, int screenLine,
1036            int physicalLine, int y, Selection s)
1037        {
1038            int[] selectionStartAndEnd
1039                = textArea.selectionManager
1040                .getSelectionStartAndEnd(
1041                screenLine,physicalLine,s);
1042            if(selectionStartAndEnd == null)
1043                return;
1044
1045            int x1 = selectionStartAndEnd[0];
1046            int x2 = selectionStartAndEnd[1];
1047
1048            gfx.fillRect(x1,y,x2 - x1,fm.getHeight());
1049        } //}}}
1050
} //}}}
1051

1052    //{{{ PaintWrapGuide class
1053
class PaintWrapGuide extends TextAreaExtension
1054    {
1055        public void paintScreenLineRange(Graphics2D gfx, int firstLine,
1056            int lastLine, int[] physicalLines, int[] start,
1057            int[] end, int y, int lineHeight)
1058        {
1059            if(textArea.wrapMargin != 0
1060                && !textArea.wrapToWidth
1061                && isWrapGuidePainted())
1062            {
1063                gfx.setColor(getWrapGuideColor());
1064                int x = textArea.getHorizontalOffset()
1065                    + textArea.wrapMargin;
1066                gfx.drawLine(x,y,x,y + (lastLine - firstLine
1067                    + 1) * lineHeight);
1068            }
1069        }
1070
1071        public String JavaDoc getToolTipText(int x, int y)
1072        {
1073            if(textArea.wrapMargin != 0
1074                && !textArea.wrapToWidth
1075                && isWrapGuidePainted())
1076            {
1077                int wrapGuidePos = textArea.wrapMargin
1078                    + textArea.getHorizontalOffset();
1079                if(Math.abs(x - wrapGuidePos) < 5)
1080                {
1081                    return String.valueOf(textArea.getBuffer()
1082                        .getProperty("maxLineLen"));
1083                }
1084            }
1085
1086            return null;
1087        }
1088    } //}}}
1089

1090    //{{{ PaintText class
1091
class PaintText extends TextAreaExtension
1092    {
1093        public void paintValidLine(Graphics2D gfx, int screenLine,
1094            int physicalLine, int start, int end, int y)
1095        {
1096            ChunkCache.LineInfo lineInfo = textArea.chunkCache
1097                .getLineInfo(screenLine);
1098
1099            Font defaultFont = getFont();
1100            Color defaultColor = getForeground();
1101
1102            gfx.setFont(defaultFont);
1103            gfx.setColor(defaultColor);
1104
1105            int x = textArea.getHorizontalOffset();
1106            int originalX = x;
1107
1108            float baseLine = y + fm.getHeight()
1109                - fm.getLeading() - fm.getDescent();
1110
1111            if(lineInfo.chunks != null)
1112            {
1113                x += Chunk.paintChunkList(lineInfo.chunks,
1114                    gfx,textArea.getHorizontalOffset(),
1115                    baseLine,!Debug.DISABLE_GLYPH_VECTOR);
1116            }
1117
1118            JEditBuffer buffer = textArea.getBuffer();
1119
1120            if(!lineInfo.lastSubregion)
1121            {
1122                gfx.setFont(defaultFont);
1123                gfx.setColor(eolMarkerColor);
1124                gfx.drawString(":",Math.max(x,
1125                    textArea.getHorizontalOffset()
1126                    + textArea.wrapMargin + textArea.charWidth),
1127                    baseLine);
1128                x += textArea.charWidth;
1129            }
1130            else if(physicalLine < buffer.getLineCount() - 1
1131                && buffer.isFoldStart(physicalLine)
1132                && !textArea.displayManager
1133                .isLineVisible(physicalLine + 1))
1134            {
1135                int level = buffer.getFoldLevel(physicalLine + 1);
1136                if(buffer.getFoldHandler() instanceof IndentFoldHandler)
1137                    level = Math.max(1,level / buffer.getIndentSize());
1138                if(level > 3)
1139                    level = 0;
1140                SyntaxStyle foldLineStyle = TextAreaPainter.this.foldLineStyle[level];
1141
1142                Font font = foldLineStyle.getFont();
1143                gfx.setFont(font);
1144                gfx.setColor(foldLineStyle.getForegroundColor());
1145
1146                int nextLine;
1147                int nextScreenLine = screenLine + 1;
1148                if(nextScreenLine < textArea.getVisibleLines())
1149                {
1150                    nextLine = textArea.chunkCache.getLineInfo(nextScreenLine)
1151                        .physicalLine;
1152                }
1153                else
1154                {
1155                    nextLine = textArea.displayManager
1156                        .getNextVisibleLine(physicalLine);
1157                }
1158
1159                if(nextLine == -1)
1160                    nextLine = textArea.getLineCount();
1161
1162                int count = nextLine - physicalLine - 1;
1163                String JavaDoc str = " [" + count + " lines]";
1164
1165                float width = getStringWidth(str);
1166
1167                gfx.drawString(str,x,baseLine);
1168                x += width;
1169            }
1170            else if(eolMarkers)
1171            {
1172                gfx.setFont(defaultFont);
1173                gfx.setColor(eolMarkerColor);
1174                gfx.drawString(".",x,baseLine);
1175                x += textArea.charWidth;
1176            }
1177
1178            lineInfo.width = (x - originalX);
1179        }
1180    } //}}}
1181

1182    //{{{ PaintCaret class
1183
class PaintCaret extends TextAreaExtension
1184    {
1185        public void paintValidLine(Graphics2D gfx, int screenLine,
1186            int physicalLine, int start, int end, int y)
1187        {
1188            if(!textArea.isCaretVisible())
1189                return;
1190
1191            int caret = textArea.getCaretPosition();
1192            if(caret < start || caret >= end)
1193                return;
1194
1195            int offset = caret - textArea.getLineStartOffset(physicalLine);
1196            textArea.offsetToXY(physicalLine, offset, textArea.offsetXY);
1197            int caretX = textArea.offsetXY.x;
1198            int height = fm.getHeight();
1199
1200            gfx.setColor(caretColor);
1201
1202            if(textArea.isOverwriteEnabled())
1203            {
1204                gfx.drawLine(caretX,y + height - 1,
1205                    caretX + textArea.charWidth,y + height - 1);
1206            }
1207            else if(blockCaret)
1208                gfx.drawRect(caretX,y,textArea.charWidth - 1,height - 1);
1209            else
1210                gfx.drawLine(caretX,y,caretX,y + height - 1);
1211        }
1212    } //}}}
1213

1214    //}}}
1215
}
1216
Popular Tags