/jEdit/tags/jedit-4-0-pre5/org/gjt/sp/jedit/textarea/TextAreaPainter.java
Java | 1050 lines | 580 code | 130 blank | 340 comment | 89 complexity | 5213ca126863237a3bdeb8afb670bbb9 MD5 | raw file
Possible License(s): BSD-3-Clause, AGPL-1.0, Apache-2.0, LGPL-2.0, LGPL-3.0, GPL-2.0, CC-BY-SA-3.0, LGPL-2.1, GPL-3.0, MPL-2.0-no-copyleft-exception, IPL-1.0
1/*
2 * TextAreaPainter.java - Paints the text area
3 * :tabSize=8:indentSize=8:noTabs=false:
4 * :folding=explicit:collapseFolds=1:
5 *
6 * Copyright (C) 1999, 2000, 2001, 2002 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
23package org.gjt.sp.jedit.textarea;
24
25//{{{ Imports
26import javax.swing.text.*;
27import javax.swing.JComponent;
28import java.awt.event.MouseEvent;
29import java.awt.font.*;
30import java.awt.geom.*;
31import java.awt.*;
32import java.util.ArrayList;
33import java.util.HashMap;
34import org.gjt.sp.jedit.syntax.*;
35import org.gjt.sp.jedit.Buffer;
36import org.gjt.sp.util.Log;
37//}}}
38
39/**
40 * The text area painter is the component responsible for displaying the
41 * text of the current buffer. The only methods in this class that should
42 * be called by plugins are those for adding and removing
43 * text area extensions.
44 *
45 * @see #addExtension(TextAreaExtension)
46 * @see #addExtension(int,TextAreaExtension)
47 * @see #removeExtension(TextAreaExtension)
48 * @see TextAreaExtension
49 * @see JEditTextArea
50 *
51 * @author Slava Pestov
52 * @version $Id: TextAreaPainter.java 4012 2002-02-05 06:28:10Z spestov $
53 */
54public class TextAreaPainter extends JComponent implements TabExpander
55{
56 //{{{ Layers
57 /**
58 * The lowest possible layer.
59 * @see #addExtension(int,TextAreaExtension)
60 * @since jEdit 4.0pre4
61 */
62 public static final int LOWEST_LAYER = Integer.MIN_VALUE;
63
64 /**
65 * Below selection layer. The JDiff plugin will use this.
66 * @see #addExtension(int,TextAreaExtension)
67 * @since jEdit 4.0pre4
68 */
69 public static final int BELOW_SELECTION_LAYER = -40;
70
71 /**
72 * Selection layer. Most extensions will be above this layer, but some
73 * (eg, JDiff) will want to be below the selection.
74 * @see #addExtension(int,TextAreaExtension)
75 * @since jEdit 4.0pre4
76 */
77 public static final int SELECTION_LAYER = -30;
78
79 /**
80 * Wrap guide layer. Most extensions will be above this layer.
81 * @since jEdit 4.0pre4
82 */
83 public static final int WRAP_GUIDE_LAYER = -20;
84
85 /**
86 * Below most extensions layer.
87 * @see #addExtension(int,TextAreaExtension)
88 * @since jEdit 4.0pre4
89 */
90 public static final int BELOW_MOST_EXTENSIONS_LAYER = -10;
91
92 /**
93 * Default extension layer. This is above the wrap guide but below the
94 * bracket highlight.
95 * @since jEdit 4.0pre4
96 */
97 public static final int DEFAULT_LAYER = 0;
98
99 /**
100 * Bracket highlight layer. Most extensions will be below this layer.
101 * @since jEdit 4.0pre4
102 */
103 public static final int BRACKET_HIGHLIGHT_LAYER = 100;
104
105 /**
106 * Highest possible layer.
107 * @since jEdit 4.0pre4
108 */
109 public static final int HIGHEST_LAYER = Integer.MAX_VALUE;
110 //}}}
111
112 //{{{ TextAreaPainter constructor
113 /**
114 * Creates a new painter. Do not create instances of this class
115 * directly.
116 */
117 public TextAreaPainter(JEditTextArea textArea)
118 {
119 enableEvents(AWTEvent.FOCUS_EVENT_MASK
120 | AWTEvent.KEY_EVENT_MASK
121 | AWTEvent.MOUSE_EVENT_MASK);
122
123 this.textArea = textArea;
124
125 extensionMgr = new ExtensionManager();
126
127 setAutoscrolls(true);
128 setOpaque(true);
129
130 setCursor(Cursor.getPredefinedCursor(Cursor.TEXT_CURSOR));
131
132 fontRenderContext = new FontRenderContext(null,false,false);
133
134 addExtension(SELECTION_LAYER,new PaintSelection());
135 addExtension(WRAP_GUIDE_LAYER,new WrapGuide());
136 addExtension(BRACKET_HIGHLIGHT_LAYER,new BracketHighlight());
137 } //}}}
138
139 //{{{ isManagingFocus() method
140 /**
141 * Returns if this component can be traversed by pressing the
142 * Tab key. This returns false.
143 */
144 public boolean isManagingFocus()
145 {
146 return false;
147 } //}}}
148
149 //{{{ getFocusTraversalKeysEnabled() method
150 /**
151 * Makes the tab key work in Java 1.4.
152 * @since jEdit 3.2pre4
153 */
154 public boolean getFocusTraversalKeysEnabled()
155 {
156 return false;
157 } //}}}
158
159 //{{{ Getters and setters
160
161 //{{{ getStyles() method
162 /**
163 * Returns the syntax styles used to paint colorized text. Entry <i>n</i>
164 * will be used to paint tokens with id = <i>n</i>.
165 * @see org.gjt.sp.jedit.syntax.Token
166 */
167 public final SyntaxStyle[] getStyles()
168 {
169 return styles;
170 } //}}}
171
172 //{{{ setStyles() method
173 /**
174 * Sets the syntax styles used to paint colorized text. Entry <i>n</i>
175 * will be used to paint tokens with id = <i>n</i>.
176 * @param styles The syntax styles
177 * @see org.gjt.sp.jedit.syntax.Token
178 */
179 public final void setStyles(SyntaxStyle[] styles)
180 {
181 this.styles = styles;
182 styles[Token.NULL] = new SyntaxStyle(getForeground(),null,getFont());
183 repaint();
184 } //}}}
185
186 //{{{ getCaretColor() method
187 /**
188 * Returns the caret color.
189 */
190 public final Color getCaretColor()
191 {
192 return caretColor;
193 } //}}}
194
195 //{{{ setCaretColor() method
196 /**
197 * Sets the caret color.
198 * @param caretColor The caret color
199 */
200 public final void setCaretColor(Color caretColor)
201 {
202 this.caretColor = caretColor;
203 if(textArea.getBuffer() != null)
204 textArea.invalidateLine(textArea.getCaretLine());
205 } //}}}
206
207 //{{{ getSelectionColor() method
208 /**
209 * Returns the selection color.
210 */
211 public final Color getSelectionColor()
212 {
213 return selectionColor;
214 } //}}}
215
216 //{{{ setSelectionColor() method
217 /**
218 * Sets the selection color.
219 * @param selectionColor The selection color
220 */
221 public final void setSelectionColor(Color selectionColor)
222 {
223 this.selectionColor = selectionColor;
224 if(textArea.getBuffer() != null)
225 textArea.invalidateSelectedLines();
226 } //}}}
227
228 //{{{ getLineHighlightColor() method
229 /**
230 * Returns the line highlight color.
231 */
232 public final Color getLineHighlightColor()
233 {
234 return lineHighlightColor;
235 } //}}}
236
237 //{{{ setLineHighlightColor() method
238 /**
239 * Sets the line highlight color.
240 * @param lineHighlightColor The line highlight color
241 */
242 public final void setLineHighlightColor(Color lineHighlightColor)
243 {
244 this.lineHighlightColor = lineHighlightColor;
245 if(textArea.getBuffer() != null)
246 textArea.invalidateLine(textArea.getCaretLine());
247 } //}}}
248
249 //{{{ isLineHighlightEnabled() method
250 /**
251 * Returns true if line highlight is enabled, false otherwise.
252 */
253 public final boolean isLineHighlightEnabled()
254 {
255 return lineHighlight;
256 } //}}}
257
258 //{{{ setLineHighlightEnabled() method
259 /**
260 * Enables or disables current line highlighting.
261 * @param lineHighlight True if current line highlight should be enabled,
262 * false otherwise
263 */
264 public final void setLineHighlightEnabled(boolean lineHighlight)
265 {
266 this.lineHighlight = lineHighlight;
267 if(textArea.getBuffer() != null)
268 textArea.invalidateSelectedLines();
269 } //}}}
270
271 //{{{ getFoldedLineColor() method
272 /**
273 * Returns the background color of a collapsed fold line.
274 */
275 public final Color getFoldedLineColor()
276 {
277 return foldedLineColor;
278 } //}}}
279
280 //{{{ setFoldedLineColor() method
281 /**
282 * Sets the background color of a collapsed fold line.
283 * @param foldedLineColor The folded line color
284 */
285 public final void setFoldedLineColor(Color foldedLineColor)
286 {
287 this.foldedLineColor = foldedLineColor;
288 repaint();
289 } //}}}
290
291 //{{{ getBracketHighlightColor() method
292 /**
293 * Returns the bracket highlight color.
294 */
295 public final Color getBracketHighlightColor()
296 {
297 return bracketHighlightColor;
298 } //}}}
299
300 //{{{ setBracketHighlightColor() method
301 /**
302 * Sets the bracket highlight color.
303 * @param bracketHighlightColor The bracket highlight color
304 */
305 public final void setBracketHighlightColor(Color bracketHighlightColor)
306 {
307 this.bracketHighlightColor = bracketHighlightColor;
308 if(textArea.getBuffer() != null)
309 textArea.invalidateLine(textArea.getBracketLine());
310 } //}}}
311
312 //{{{ isBracketHighlightEnabled() method
313 /**
314 * Returns true if bracket highlighting is enabled, false otherwise.
315 * When bracket highlighting is enabled, the bracket matching the
316 * one before the caret (if any) is highlighted.
317 */
318 public final boolean isBracketHighlightEnabled()
319 {
320 return bracketHighlight;
321 } //}}}
322
323 //{{{ setBracketHighlightEnabled() method
324 /**
325 * Enables or disables bracket highlighting.
326 * When bracket highlighting is enabled, the bracket matching the
327 * one before the caret (if any) is highlighted.
328 * @param bracketHighlight True if bracket highlighting should be
329 * enabled, false otherwise
330 */
331 public final void setBracketHighlightEnabled(boolean bracketHighlight)
332 {
333 this.bracketHighlight = bracketHighlight;
334 if(textArea.getBuffer() != null)
335 textArea.invalidateLine(textArea.getBracketLine());
336 } //}}}
337
338 //{{{ isBlockCaretEnabled() method
339 /**
340 * Returns true if the caret should be drawn as a block, false otherwise.
341 */
342 public final boolean isBlockCaretEnabled()
343 {
344 return blockCaret;
345 } //}}}
346
347 //{{{ setBlockCaretEnabled() method
348 /**
349 * Sets if the caret should be drawn as a block, false otherwise.
350 * @param blockCaret True if the caret should be drawn as a block,
351 * false otherwise.
352 */
353 public final void setBlockCaretEnabled(boolean blockCaret)
354 {
355 this.blockCaret = blockCaret;
356 if(textArea.getBuffer() != null)
357 textArea.invalidateLine(textArea.getCaretLine());
358 } //}}}
359
360 //{{{ getEOLMarkerColor() method
361 /**
362 * Returns the EOL marker color.
363 */
364 public final Color getEOLMarkerColor()
365 {
366 return eolMarkerColor;
367 } //}}}
368
369 //{{{ setEOLMarkerColor() method
370 /**
371 * Sets the EOL marker color.
372 * @param eolMarkerColor The EOL marker color
373 */
374 public final void setEOLMarkerColor(Color eolMarkerColor)
375 {
376 this.eolMarkerColor = eolMarkerColor;
377 repaint();
378 } //}}}
379
380 //{{{ getEOLMarkersPainted() method
381 /**
382 * Returns true if EOL markers are drawn, false otherwise.
383 */
384 public final boolean getEOLMarkersPainted()
385 {
386 return eolMarkers;
387 } //}}}
388
389 //{{{ setEOLMarkersPainted() method
390 /**
391 * Sets if EOL markers are to be drawn.
392 * @param eolMarkers True if EOL markers should be drawn, false otherwise
393 */
394 public final void setEOLMarkersPainted(boolean eolMarkers)
395 {
396 this.eolMarkers = eolMarkers;
397 repaint();
398 } //}}}
399
400 //{{{ getWrapGuideColor() method
401 /**
402 * Returns the wrap guide color.
403 */
404 public final Color getWrapGuideColor()
405 {
406 return wrapGuideColor;
407 } //}}}
408
409 //{{{ setWrapGuideColor() method
410 /**
411 * Sets the wrap guide color.
412 * @param wrapGuideColor The wrap guide color
413 */
414 public final void setWrapGuideColor(Color wrapGuideColor)
415 {
416 this.wrapGuideColor = wrapGuideColor;
417 repaint();
418 } //}}}
419
420 //{{{ isWrapGuidePainted() method
421 /**
422 * Returns true if the wrap guide is drawn, false otherwise.
423 * @since jEdit 4.0pre4
424 */
425 public final boolean isWrapGuidePainted()
426 {
427 return wrapGuide;
428 } //}}}
429
430 //{{{ setWrapGuidePainted() method
431 /**
432 * Sets if the wrap guide is to be drawn.
433 * @param wrapGuide True if the wrap guide should be drawn, false otherwise
434 */
435 public final void setWrapGuidePainted(boolean wrapGuide)
436 {
437 this.wrapGuide = wrapGuide;
438 repaint();
439 } //}}}
440
441 //{{{ setAntiAliasEnabled() method
442 /**
443 * Sets if anti-aliasing should be enabled. Has no effect when
444 * running on Java 1.1.
445 * @since jEdit 3.2pre6
446 */
447 public void setAntiAliasEnabled(boolean antiAlias)
448 {
449 this.antiAlias = antiAlias;
450 updateRenderingHints();
451 } //}}}
452
453 //{{{ isAntiAliasEnabled() method
454 /**
455 * Returns if anti-aliasing is enabled.
456 * @since jEdit 3.2pre6
457 */
458 public boolean isAntiAliasEnabled()
459 {
460 return antiAlias;
461 } //}}}
462
463 //{{{ setFractionalFontMetricsEnabled() method
464 /**
465 * Sets if fractional font metrics should be enabled. Has no effect when
466 * running on Java 1.1.
467 * @since jEdit 3.2pre6
468 */
469 public void setFractionalFontMetricsEnabled(boolean fracFontMetrics)
470 {
471 this.fracFontMetrics = fracFontMetrics;
472 updateRenderingHints();
473 } //}}}
474
475 //{{{ isFractionalFontMetricsEnabled() method
476 /**
477 * Returns if fractional font metrics are enabled.
478 * @since jEdit 3.2pre6
479 */
480 public boolean isFractionalFontMetricsEnabled()
481 {
482 return fracFontMetrics;
483 } //}}}
484
485 //{{{ getFontRenderContext() method
486 /**
487 * Returns the font render context.
488 * @since jEdit 4.0pre4
489 */
490 public FontRenderContext getFontRenderContext()
491 {
492 return fontRenderContext;
493 } //}}}
494
495 //}}}
496
497 //{{{ addCustomHighlight() method
498 /**
499 * @deprecated Write a <code>TextAreaExtension</code> instead.
500 */
501 public void addCustomHighlight(TextAreaHighlight highlight)
502 {
503 Log.log(Log.WARNING,this,"Old highlighter API not supported: "
504 + highlight);
505 } //}}}
506
507 //{{{ removeCustomHighlight() method
508 /**
509 * @deprecated Write a <code>TextAreaExtension</code> instead.
510 */
511 public void removeCustomHighlight(TextAreaHighlight highlight)
512 {
513 Log.log(Log.WARNING,this,"Old highlighter API not supported: "
514 + highlight);
515 } //}}}
516
517 //{{{ addExtension() method
518 /**
519 * Adds a text area extension, which can perform custom painting and
520 * tool tip handling.
521 * @param extension The extension
522 * @since jEdit 4.0pre4
523 */
524 public void addExtension(TextAreaExtension extension)
525 {
526 extensionMgr.addExtension(DEFAULT_LAYER,extension);
527 repaint();
528 } //}}}
529
530 //{{{ addExtension() method
531 /**
532 * Adds a text area extension, which can perform custom painting and
533 * tool tip handling.
534 * @param layer The layer to add the extension to. Note that more than
535 * extension can share the same layer.
536 * @param extension The extension
537 * @since jEdit 4.0pre4
538 */
539 public void addExtension(int layer, TextAreaExtension extension)
540 {
541 extensionMgr.addExtension(layer,extension);
542 repaint();
543 } //}}}
544
545 //{{{ removeExtension() method
546 /**
547 * Removes a text area extension. It will no longer be asked to
548 * perform custom painting and tool tip handling.
549 * @param extension The extension
550 * @since jEdit 4.0pre4
551 */
552 public void removeExtension(TextAreaExtension extension)
553 {
554 extensionMgr.removeExtension(extension);
555 repaint();
556 } //}}}
557
558 //{{{ getToolTipText() method
559 /**
560 * Returns the tool tip to display at the specified location.
561 * @param evt The mouse event
562 */
563 public String getToolTipText(MouseEvent evt)
564 {
565 if(!textArea.getBuffer().isLoaded())
566 return null;
567
568 return extensionMgr.getToolTipText(evt.getX(),evt.getY());
569 } //}}}
570
571 //{{{ getFontMetrics() method
572 /**
573 * Returns the font metrics used by this component.
574 */
575 public FontMetrics getFontMetrics()
576 {
577 return fm;
578 } //}}}
579
580 //{{{ setFont() method
581 /**
582 * Sets the font for this component. This is overridden to update the
583 * cached font metrics and to recalculate which lines are visible.
584 * @param font The font
585 */
586 public void setFont(Font font)
587 {
588 super.setFont(font);
589 fm = getFontMetrics(font);
590 textArea.recalculateVisibleLines();
591 } //}}}
592
593 //{{{ paintComponent() method
594 /**
595 * Repaints the text.
596 * @param g The graphics context
597 */
598 public void paintComponent(Graphics _gfx)
599 {
600 Graphics2D gfx = (Graphics2D)_gfx;
601 gfx.setRenderingHints(renderingHints);
602 fontRenderContext = gfx.getFontRenderContext();
603
604 Rectangle clipRect = gfx.getClipBounds();
605
606 gfx.setColor(getBackground());
607 gfx.fillRect(clipRect.x,clipRect.y,clipRect.width,clipRect.height);
608
609 Buffer buffer = textArea.getBuffer();
610 if(!buffer.isLoaded())
611 return;
612
613 int x = textArea.getHorizontalOffset();
614
615 int height = fm.getHeight();
616 int firstInvalid = clipRect.y / height;
617 // Because the clipRect's height is usually an even multiple
618 // of the font height, we subtract 1 from it, otherwise one
619 // too many lines will always be painted.
620 int lastInvalid = (clipRect.y + clipRect.height - 1) / height;
621
622 textArea.chunkCache.updateChunksUpTo(lastInvalid);
623
624 int lineCount = textArea.getVirtualLineCount();
625
626 int y = (clipRect.y - clipRect.y % height);
627
628 try
629 {
630 boolean updateMaxHorizontalScrollWidth = false;
631
632 for(int line = firstInvalid; line <= lastInvalid; line++)
633 {
634 ChunkCache.LineInfo lineInfo = textArea.chunkCache
635 .getLineInfo(line);
636 if(!lineInfo.chunksValid)
637 System.err.println("text area painter: not valid");
638
639 lineInfo.width = paintLine(gfx,buffer,lineInfo,line,x,y) - x;
640 if(lineInfo.width > textArea.maxHorizontalScrollWidth)
641 updateMaxHorizontalScrollWidth = true;
642
643 y += height;
644 }
645
646 if(buffer.isNextLineRequested())
647 {
648 int h = clipRect.y + clipRect.height;
649 textArea.chunkCache.invalidateChunksFrom(lastInvalid + 1);
650 repaint(0,h,getWidth(),getHeight() - h);
651 }
652
653 if(updateMaxHorizontalScrollWidth)
654 textArea.updateMaxHorizontalScrollWidth();
655 }
656 catch(Exception e)
657 {
658 Log.log(Log.ERROR,this,"Error repainting line"
659 + " range {" + firstInvalid + ","
660 + lastInvalid + "}:");
661 Log.log(Log.ERROR,this,e);
662 }
663 } //}}}
664
665 //{{{ nextTabStop() method
666 /**
667 * Implementation of TabExpander interface. Returns next tab stop after
668 * a specified point.
669 * @param x The x co-ordinate
670 * @param tabOffset Ignored
671 * @return The next tab stop after <i>x</i>
672 */
673 public float nextTabStop(float x, int tabOffset)
674 {
675 int ntabs = (int)(x / textArea.tabSize);
676 return (ntabs + 1) * textArea.tabSize;
677 } //}}}
678
679 //{{{ getPreferredSize() method
680 /**
681 * Returns the painter's preferred size.
682 */
683 public Dimension getPreferredSize()
684 {
685 Dimension dim = new Dimension();
686
687 char[] foo = new char[80];
688 for(int i = 0; i < foo.length; i++)
689 foo[i] = ' ';
690 dim.width = (int)(getFont().getStringBounds(foo,0,foo.length,
691 fontRenderContext).getWidth());
692 dim.height = fm.getHeight() * 25;
693 return dim;
694 } //}}}
695
696 //{{{ getMinimumSize() method
697 /**
698 * Returns the painter's minimum size.
699 */
700 public Dimension getMinimumSize()
701 {
702 return getPreferredSize();
703 } //}}}
704
705 //{{{ Private members
706
707 //{{{ Instance variables
708 private JEditTextArea textArea;
709
710 private SyntaxStyle[] styles;
711 private Color caretColor;
712 private Color selectionColor;
713 private Color lineHighlightColor;
714 private Color foldedLineColor;
715 private Color bracketHighlightColor;
716 private Color eolMarkerColor;
717 private Color wrapGuideColor;
718
719 private boolean blockCaret;
720 private boolean lineHighlight;
721 private boolean bracketHighlight;
722 private boolean eolMarkers;
723 private boolean wrapGuide;
724 private boolean antiAlias;
725 private boolean fracFontMetrics;
726
727 // should try to use this as little as possible.
728 private FontMetrics fm;
729
730 private ExtensionManager extensionMgr;
731
732 private RenderingHints renderingHints;
733 private FontRenderContext fontRenderContext;
734 //}}}
735
736 //{{{ updateRenderingHints() method
737 private void updateRenderingHints()
738 {
739 HashMap hints = new HashMap();
740
741 if(antiAlias)
742 {
743 hints.put(RenderingHints.KEY_RENDERING, RenderingHints.VALUE_RENDER_QUALITY);
744 hints.put(RenderingHints.KEY_ANTIALIASING, RenderingHints.VALUE_ANTIALIAS_ON);
745 hints.put(RenderingHints.KEY_TEXT_ANTIALIASING, RenderingHints.VALUE_TEXT_ANTIALIAS_ON);
746 }
747 else
748 {
749 hints.put(RenderingHints.KEY_ANTIALIASING, RenderingHints.VALUE_ANTIALIAS_OFF);
750 hints.put(RenderingHints.KEY_TEXT_ANTIALIASING, RenderingHints.VALUE_TEXT_ANTIALIAS_OFF);
751 }
752
753 hints.put(RenderingHints.KEY_FRACTIONALMETRICS,
754 fracFontMetrics ?
755 RenderingHints.VALUE_FRACTIONALMETRICS_ON
756 : RenderingHints.VALUE_FRACTIONALMETRICS_OFF);
757
758 renderingHints = new RenderingHints(hints);
759 fontRenderContext = new FontRenderContext(null,antiAlias,
760 fracFontMetrics);
761 } //}}}
762
763 //{{{ paintLine() method
764 private int paintLine(Graphics2D gfx, Buffer buffer,
765 ChunkCache.LineInfo lineInfo, int screenLine,
766 int x, int y)
767 {
768 int physicalLine = lineInfo.physicalLine;
769
770 if(physicalLine == -1)
771 extensionMgr.paintInvalidLine(gfx,screenLine,y);
772 else
773 {
774 boolean collapsedFold = (physicalLine < buffer.getLineCount() - 1
775 && buffer.isFoldStart(physicalLine)
776 && !textArea.getFoldVisibilityManager()
777 .isLineVisible(physicalLine + 1)
778 && lineInfo.firstSubregion);
779 int start = textArea.getScreenLineStartOffset(screenLine);
780 int end = textArea.getScreenLineEndOffset(screenLine);
781
782 int caret = textArea.getCaretPosition();
783 boolean paintLineHighlight = lineHighlight
784 && caret >= start && caret < end
785 && textArea.selection.size() == 0;
786
787 Color bgColor;
788
789 if(paintLineHighlight)
790 bgColor = lineHighlightColor;
791 else if(collapsedFold)
792 bgColor = foldedLineColor;
793 else
794 bgColor = getBackground();
795
796 if(paintLineHighlight || collapsedFold)
797 {
798 gfx.setColor(bgColor);
799 gfx.fillRect(0,y,getWidth(),fm.getHeight());
800 }
801
802 extensionMgr.paintValidLine(gfx,screenLine,physicalLine,
803 start,end,y);
804
805 Font defaultFont = getFont();
806 Color defaultColor = getForeground();
807
808 gfx.setFont(defaultFont);
809 gfx.setColor(defaultColor);
810
811 float baseLine = y + fm.getHeight()
812 - fm.getLeading() - fm.getDescent();
813
814 if(lineInfo.chunks != null)
815 {
816 x += ChunkCache.paintChunkList(
817 lineInfo.chunks,gfx,x,baseLine,
818 getWidth(),bgColor,true);
819 }
820
821 gfx.setFont(defaultFont);
822 gfx.setColor(eolMarkerColor);
823
824 if(!lineInfo.lastSubregion)
825 {
826 gfx.drawString(":",Math.max(x,
827 textArea.getHorizontalOffset()
828 + textArea.wrapMargin),
829 baseLine);
830 x += textArea.charWidth;
831 }
832 else if(collapsedFold)
833 {
834 int nextLine = textArea.getFoldVisibilityManager()
835 .getNextVisibleLine(physicalLine);
836 if(nextLine == -1)
837 nextLine = buffer.getLineCount();
838
839 int count = nextLine - physicalLine - 1;
840 String str = " [" + count + " lines]";
841 gfx.drawString(str,x,baseLine);
842 x += (int)(getFont().getStringBounds(
843 str,fontRenderContext)
844 .getWidth());
845 }
846 else if(eolMarkers)
847 {
848 gfx.drawString(".",x,baseLine);
849 x += textArea.charWidth;
850 }
851
852 paintCaret(gfx,physicalLine,start,end,y,bgColor);
853 }
854
855 return x;
856 } //}}}
857
858 //{{{ paintCaret() method
859 private void paintCaret(Graphics2D gfx, int physicalLine,
860 int start, int end, int y, Color bgColor)
861 {
862 if(!textArea.isCaretVisible())
863 return;
864
865 int caret = textArea.getCaretPosition();
866 if(caret < start || caret >= end)
867 return;
868
869 int offset = caret - textArea.getLineStartOffset(physicalLine);
870 textArea.offsetToXY(physicalLine,offset,textArea.returnValue);
871 int caretX = textArea.returnValue.x;
872 int height = fm.getHeight();
873
874 gfx.setColor(caretColor);
875
876 if(textArea.isOverwriteEnabled())
877 {
878 gfx.drawLine(caretX,y + height - 1,
879 caretX + textArea.charWidth,y + height - 1);
880 }
881 else if(blockCaret)
882 {
883 // Workaround for bug in Graphics2D in JDK1.4 under
884 // Windows; calling setPaintMode() does not reset
885 // graphics mode.
886 Graphics2D blockgfx = (Graphics2D)gfx.create();
887 blockgfx.setXORMode(bgColor);
888 blockgfx.fillRect(caretX,y,textArea.charWidth,height);
889 blockgfx.dispose();
890 }
891 else
892 {
893 gfx.drawLine(caretX,y,caretX,y + height - 1);
894 }
895 } //}}}
896
897 //}}}
898
899 //{{{ PaintSelection class
900 class PaintSelection extends TextAreaExtension
901 {
902 //{{{ paintValidLine() method
903 public void paintValidLine(Graphics2D gfx, int screenLine,
904 int physicalLine, int start, int end, int y)
905 {
906 if(textArea.selection.size() == 0)
907 return;
908
909 gfx.setColor(getSelectionColor());
910 for(int i = textArea.selection.size() - 1;
911 i >= 0; i--)
912 {
913 paintSelection(gfx,screenLine,
914 physicalLine,start,end,y,
915 (Selection)textArea.selection
916 .get(i));
917 }
918 } //}}}
919
920 //{{{ paintSelection() method
921 private void paintSelection(Graphics2D gfx, int screenLine,
922 int physicalLine, int start, int end, int y, Selection s)
923 {
924 if(end <= s.start || start > s.end)
925 return;
926
927 int selStartScreenLine = textArea.getScreenLineOfOffset(s.start);
928 int selEndScreenLine = textArea.getScreenLineOfOffset(s.end);
929
930 int lineStart = textArea.getLineStartOffset(physicalLine);
931 int x1, x2;
932
933 if(s instanceof Selection.Rect)
934 {
935 int lineLen = textArea.getLineLength(physicalLine);
936 x1 = textArea.offsetToXY(physicalLine,Math.min(lineLen,
937 s.start - textArea.getLineStartOffset(
938 s.startLine)),textArea.returnValue).x;
939 x2 = textArea.offsetToXY(physicalLine,Math.min(lineLen,
940 s.end - textArea.getLineStartOffset(
941 s.endLine)),textArea.returnValue).x;
942
943 if(x1 > x2)
944 {
945 int tmp = x2;
946 x2 = x1;
947 x1 = tmp;
948 }
949 }
950 else if(selStartScreenLine == selEndScreenLine
951 && selStartScreenLine != -1)
952 {
953 x1 = textArea.offsetToXY(physicalLine,
954 s.start - lineStart,textArea.returnValue).x;
955 x2 = textArea.offsetToXY(physicalLine,
956 s.end - lineStart,textArea.returnValue).x;
957 }
958 else if(screenLine == selStartScreenLine)
959 {
960 x1 = textArea.offsetToXY(physicalLine,
961 s.start - lineStart,textArea.returnValue).x;
962 x2 = getWidth();
963 }
964 else if(screenLine == selEndScreenLine)
965 {
966 x1 = 0;
967 x2 = textArea.offsetToXY(physicalLine,
968 s.end - lineStart,textArea.returnValue).x;
969 }
970 else
971 {
972 x1 = 0;
973 x2 = getWidth();
974 }
975
976 if(x1 == x2)
977 x2++;
978
979 gfx.fillRect(x1,y,x2 - x1,fm.getHeight());
980 } //}}}
981 } //}}}
982
983 //{{{ WrapGuide class
984 class WrapGuide extends TextAreaExtension
985 {
986 public void paintValidLine(Graphics2D gfx, int screenLine,
987 int physicalLine, int start, int end, int y)
988 {
989 paintInvalidLine(gfx,screenLine,y);
990 }
991
992 public void paintInvalidLine(Graphics2D gfx, int screenLine, int y)
993 {
994 if(!textArea.wrapToWidth && textArea.wrapMargin != 0
995 && isWrapGuidePainted())
996 {
997 gfx.setColor(getWrapGuideColor());
998 int x = textArea.getHorizontalOffset() + textArea.wrapMargin;
999 gfx.drawLine(x,y,x,y + fm.getHeight());
1000 }
1001 }
1002
1003 public String getToolTipText(int x, int y)
1004 {
1005 if(!textArea.wrapToWidth && textArea.wrapMargin != 0
1006 && isWrapGuidePainted())
1007 {
1008 int wrapGuidePos = textArea.wrapMargin
1009 + textArea.getHorizontalOffset();
1010 if(Math.abs(x - wrapGuidePos) < 5)
1011 {
1012 return String.valueOf(textArea.getBuffer()
1013 .getProperty("maxLineLen"));
1014 }
1015 }
1016
1017 return null;
1018 }
1019 } //}}}
1020
1021 //{{{ BracketHighlight class
1022 class BracketHighlight extends TextAreaExtension
1023 {
1024 public void paintValidLine(Graphics2D gfx, int screenLine,
1025 int physicalLine, int start, int end, int y)
1026 {
1027 if(!isBracketHighlightEnabled() || !textArea.isBracketHighlightVisible())
1028 return;
1029
1030 int bracketLine = textArea.getBracketLine();
1031 int bracketOffset = textArea.getBracketPosition();
1032 if(bracketLine == -1 || bracketOffset == -1)
1033 return;
1034
1035 int bracketLineStart = textArea.getLineStartOffset(bracketLine);
1036 if(bracketOffset + bracketLineStart < start
1037 || bracketOffset + bracketLineStart >= end)
1038 return;
1039
1040 textArea.offsetToXY(bracketLine,bracketOffset,textArea.returnValue);
1041 gfx.setColor(getBracketHighlightColor());
1042 // Hack!!! Since there is no fast way to get the character
1043 // from the bracket matching routine, we use ( since all
1044 // brackets probably have the same width anyway
1045 gfx.drawRect(textArea.returnValue.x,y,(int)gfx.getFont().getStringBounds(
1046 "(",getFontRenderContext()).getWidth() - 1,
1047 fm.getHeight() - 1);
1048 }
1049 } //}}}
1050}