1
2
3
4
5
6
7
8
9
10
11
12 package org.syntax.jedit;
13
14 import java.awt.Color;
15 import java.awt.Cursor;
16 import java.awt.Dimension;
17 import java.awt.Font;
18 import java.awt.FontMetrics;
19 import java.awt.Graphics;
20 import java.awt.Rectangle;
21 import java.awt.Toolkit;
22 import java.awt.event.MouseEvent;
23
24 import javax.swing.JComponent;
25 import javax.swing.ToolTipManager;
26 import javax.swing.text.PlainDocument;
27 import javax.swing.text.Segment;
28 import javax.swing.text.TabExpander;
29 import javax.swing.text.Utilities;
30
31 import org.syntax.jedit.tokenmarker.Token;
32 import org.syntax.jedit.tokenmarker.TokenMarker;
33
34 import com.eviware.soapui.SoapUI;
35
36 /***
37 * The text area repaint manager. It performs double buffering and paints
38 * lines of text.
39 * @author Slava Pestov
40 * @version $Id$
41 */
42 public class TextAreaPainter extends JComponent implements TabExpander
43 {
44 private int areaWidth;
45 private int areaHeight;
46
47 /***
48 * Creates a new repaint manager. This should be not be called
49 * directly.
50 */
51 public TextAreaPainter(JEditTextArea textArea, TextAreaDefaults defaults)
52 {
53 this.textArea = textArea;
54
55 setAutoscrolls(true);
56 setDoubleBuffered(true);
57 setOpaque(true);
58 setBorder( null );
59
60 ToolTipManager.sharedInstance().registerComponent(this);
61
62 currentLine = new Segment();
63 currentLineIndex = -1;
64
65 setCursor(Cursor.getPredefinedCursor(Cursor.TEXT_CURSOR));
66
67 setFont(new Font("Monospaced",Font.PLAIN,14));
68 setForeground(Color.black);
69 setBackground(Color.white);
70
71 blockCaret = defaults.blockCaret;
72 styles = defaults.styles;
73
74
75 caretColor = defaults.caretColor;
76 selectionColor = defaults.selectionColor;
77 lineHighlightColor = defaults.lineHighlightColor;
78 lineHighlight = defaults.lineHighlight;
79 bracketHighlightColor = defaults.bracketHighlightColor;
80 bracketHighlight = defaults.bracketHighlight;
81 paintInvalid = defaults.paintInvalid;
82 eolMarkerColor = defaults.eolMarkerColor;
83 eolMarkers = defaults.eolMarkers;
84 }
85
86 /***
87 * Returns if this component can be traversed by pressing the
88 * Tab key. This returns false.
89 */
90 public final boolean isManagingFocus()
91 {
92 return false;
93 }
94
95 /***
96 * Returns the syntax styles used to paint colorized text. Entry <i>n</i>
97 * will be used to paint tokens with id = <i>n</i>.
98 * @see org.syntax.jedit.Token
99 */
100 public final SyntaxStyle[] getStyles()
101 {
102 return styles;
103 }
104
105 /***
106 * Sets the syntax styles used to paint colorized text. Entry <i>n</i>
107 * will be used to paint tokens with id = <i>n</i>.
108 * @param styles The syntax styles
109 * @see org.syntax.jedit.Token
110 */
111 public final void setStyles(SyntaxStyle[] styles)
112 {
113 this.styles = styles;
114 repaint();
115 }
116
117 /***
118 * Returns the caret color.
119 */
120 public final Color getCaretColor()
121 {
122 return caretColor;
123 }
124
125 /***
126 * Sets the caret color.
127 * @param caretColor The caret color
128 */
129 public final void setCaretColor(Color caretColor)
130 {
131 this.caretColor = caretColor;
132 invalidateSelectedLines();
133 }
134
135 /***
136 * Returns the selection color.
137 */
138 public final Color getSelectionColor()
139 {
140 return selectionColor;
141 }
142
143 /***
144 * Sets the selection color.
145 * @param selectionColor The selection color
146 */
147 public final void setSelectionColor(Color selectionColor)
148 {
149 this.selectionColor = selectionColor;
150 invalidateSelectedLines();
151 }
152
153 /***
154 * Returns the line highlight color.
155 */
156 public final Color getLineHighlightColor()
157 {
158 return lineHighlightColor;
159 }
160
161 /***
162 * Sets the line highlight color.
163 * @param lineHighlightColor The line highlight color
164 */
165 public final void setLineHighlightColor(Color lineHighlightColor)
166 {
167 this.lineHighlightColor = lineHighlightColor;
168 invalidateSelectedLines();
169 }
170
171 /***
172 * Returns true if line highlight is enabled, false otherwise.
173 */
174 public final boolean isLineHighlightEnabled()
175 {
176 return lineHighlight;
177 }
178
179 /***
180 * Enables or disables current line highlighting.
181 * @param lineHighlight True if current line highlight should be enabled,
182 * false otherwise
183 */
184 public final void setLineHighlightEnabled(boolean lineHighlight)
185 {
186 this.lineHighlight = lineHighlight;
187 invalidateSelectedLines();
188 }
189
190 /***
191 * Returns the bracket highlight color.
192 */
193 public final Color getBracketHighlightColor()
194 {
195 return bracketHighlightColor;
196 }
197
198 /***
199 * Sets the bracket highlight color.
200 * @param bracketHighlightColor The bracket highlight color
201 */
202 public final void setBracketHighlightColor(Color bracketHighlightColor)
203 {
204 this.bracketHighlightColor = bracketHighlightColor;
205 invalidateLine(textArea.getBracketLine());
206 }
207
208 /***
209 * Returns true if bracket highlighting is enabled, false otherwise.
210 * When bracket highlighting is enabled, the bracket matching the
211 * one before the caret (if any) is highlighted.
212 */
213 public final boolean isBracketHighlightEnabled()
214 {
215 return bracketHighlight;
216 }
217
218 /***
219 * Enables or disables bracket highlighting.
220 * When bracket highlighting is enabled, the bracket matching the
221 * one before the caret (if any) is highlighted.
222 * @param bracketHighlight True if bracket highlighting should be
223 * enabled, false otherwise
224 */
225 public final void setBracketHighlightEnabled(boolean bracketHighlight)
226 {
227 this.bracketHighlight = bracketHighlight;
228 invalidateLine(textArea.getBracketLine());
229 }
230
231 /***
232 * Returns true if the caret should be drawn as a block, false otherwise.
233 */
234 public final boolean isBlockCaretEnabled()
235 {
236 return blockCaret;
237 }
238
239 /***
240 * Sets if the caret should be drawn as a block, false otherwise.
241 * @param blockCaret True if the caret should be drawn as a block,
242 * false otherwise.
243 */
244 public final void setBlockCaretEnabled(boolean blockCaret)
245 {
246 this.blockCaret = blockCaret;
247 invalidateSelectedLines();
248 }
249
250 /***
251 * Returns the EOL marker color.
252 */
253 public final Color getEOLMarkerColor()
254 {
255 return eolMarkerColor;
256 }
257
258 /***
259 * Sets the EOL marker color.
260 * @param eolMarkerColor The EOL marker color
261 */
262 public final void setEOLMarkerColor(Color eolMarkerColor)
263 {
264 this.eolMarkerColor = eolMarkerColor;
265 repaint();
266 }
267
268 /***
269 * Returns true if EOL markers are drawn, false otherwise.
270 */
271 public final boolean getEOLMarkersPainted()
272 {
273 return eolMarkers;
274 }
275
276 /***
277 * Sets if EOL markers are to be drawn.
278 * @param eolMarkers True if EOL markers should be drawn, false otherwise
279 */
280 public final void setEOLMarkersPainted(boolean eolMarkers)
281 {
282 this.eolMarkers = eolMarkers;
283 repaint();
284 }
285
286 /***
287 * Returns true if invalid lines are painted as red tildes (~),
288 * false otherwise.
289 */
290 public boolean getInvalidLinesPainted()
291 {
292 return paintInvalid;
293 }
294
295 /***
296 * Sets if invalid lines are to be painted as red tildes.
297 * @param paintInvalid True if invalid lines should be drawn, false otherwise
298 */
299 public void setInvalidLinesPainted(boolean paintInvalid)
300 {
301 this.paintInvalid = paintInvalid;
302 }
303
304 /***
305 * Adds a custom highlight painter.
306 * @param highlight The highlight
307 */
308 public void addCustomHighlight(Highlight highlight)
309 {
310 highlight.init(textArea,highlights);
311 highlights = highlight;
312 }
313
314 /***
315 * Highlight interface.
316 */
317 public interface Highlight
318 {
319 /***
320 * Called after the highlight painter has been added.
321 * @param textArea The text area
322 * @param next The painter this one should delegate to
323 */
324 void init(JEditTextArea textArea, Highlight next);
325
326 /***
327 * This should paint the highlight and delgate to the
328 * next highlight painter.
329 * @param gfx The graphics context
330 * @param line The line number
331 * @param y The y co-ordinate of the line
332 */
333 void paintHighlight(Graphics gfx, int line, int y);
334
335 /***
336 * Returns the tool tip to display at the specified
337 * location. If this highlighter doesn't know what to
338 * display, it should delegate to the next highlight
339 * painter.
340 * @param evt The mouse event
341 */
342 String getToolTipText(MouseEvent evt);
343 }
344
345 /***
346 * Returns the tool tip to display at the specified location.
347 * @param evt The mouse event
348 */
349 public String getToolTipText(MouseEvent evt)
350 {
351 if(highlights != null)
352 return highlights.getToolTipText(evt);
353 else
354 return null;
355 }
356
357 /***
358 * Returns the font metrics used by this component.
359 */
360 public FontMetrics getFontMetrics()
361 {
362 return fm;
363 }
364
365 /***
366 * Sets the font for this component. This is overridden to update the
367 * cached font metrics and to recalculate which lines are visible.
368 * @param font The font
369 */
370 public void setFont(Font font)
371 {
372 super.setFont(font);
373 fm = Toolkit.getDefaultToolkit().getFontMetrics(font);
374 textArea.recalculateVisibleLines();
375 }
376
377 /***
378 * Repaints the text.
379 * @param g The graphics context
380 */
381 public void paint(Graphics gfx)
382 {
383 Rectangle clipRect = gfx.getClipBounds();
384
385 gfx.setColor(getBackground());
386 gfx.fillRect(clipRect.x,clipRect.y,clipRect.width,clipRect.height);
387
388
389
390 int height = fm.getHeight();
391 int firstLine = textArea.getFirstLine();
392 int firstInvalid = firstLine + clipRect.y / height;
393
394
395
396 int lastInvalid = firstLine + (clipRect.y + clipRect.height - 1) / height;
397
398 try
399 {
400 TokenMarker tokenMarker = textArea.getDocument()
401 .getTokenMarker();
402 int x = 0;
403
404 for(int line = firstInvalid; line <= lastInvalid; line++)
405 {
406 paintLine(gfx,tokenMarker,line,x);
407 }
408
409 if(tokenMarker != null && tokenMarker.isNextLineRequested())
410 {
411 int h = clipRect.y + clipRect.height;
412 repaint(0,h,getWidth(),getHeight() - h);
413 }
414 }
415 catch(Exception e)
416 {
417 System.err.println("Error repainting line"
418 + " range {" + firstInvalid + ","
419 + lastInvalid + "}:");
420 SoapUI.logError( e );
421 }
422 }
423
424 /***
425 * Marks a line as needing a repaint.
426 * @param line The line to invalidate
427 */
428 public final void invalidateLine(int line)
429 {
430 repaint(0,textArea.lineToY(line) + fm.getMaxDescent() + fm.getLeading(),
431 getWidth(),fm.getHeight());
432 }
433
434 /***
435 * Marks a range of lines as needing a repaint.
436 * @param firstLine The first line to invalidate
437 * @param lastLine The last line to invalidate
438 */
439 public final void invalidateLineRange(int firstLine, int lastLine)
440 {
441 repaint(0,textArea.lineToY(firstLine) + fm.getMaxDescent() + fm.getLeading(),
442 getWidth(),(lastLine - firstLine + 1) * fm.getHeight());
443 }
444
445 /***
446 * Repaints the lines containing the selection.
447 */
448 public final void invalidateSelectedLines()
449 {
450 invalidateLineRange(textArea.getSelectionStartLine(),
451 textArea.getSelectionEndLine());
452 }
453
454 /***
455 * Implementation of TabExpander interface. Returns next tab stop after
456 * a specified point.
457 * @param x The x co-ordinate
458 * @param tabOffset Ignored
459 * @return The next tab stop after <i>x</i>
460 */
461 public float nextTabStop(float x, int tabOffset)
462 {
463 if( tabSize == 0 )
464 initTabSize();
465
466 int offset = 0;
467 int ntabs = ((int)x - offset) / tabSize;
468 return (ntabs + 1) * tabSize + offset;
469 }
470
471 private void initTabSize()
472 {
473 tabSize = fm.charWidth(' ') * ((Integer)textArea
474 .getDocument().getProperty(
475 PlainDocument.tabSizeAttribute)).intValue();
476 if( tabSize == 0 )
477 tabSize = fm.charWidth(' ') * 4;
478 }
479
480 /***
481 * Returns the painter's preferred size.
482 */
483 public Dimension getPreferredSize()
484 {
485 Dimension dim = new Dimension();
486 dim.width = fm.charWidth('w') * textArea.getMaxLineLength() + 5;
487 dim.height = fm.getHeight() * textArea.getLineCount() + 5;
488 return dim;
489 }
490
491
492 /***
493 * Returns the painter's minimum size.
494 */
495 public Dimension getMinimumSize()
496 {
497 return getPreferredSize();
498 }
499
500 public Dimension getMaximumSize()
501 {
502 return getPreferredSize();
503 }
504
505
506 int currentLineIndex;
507 Token currentLineTokens;
508 Segment currentLine;
509
510
511 protected JEditTextArea textArea;
512
513 protected SyntaxStyle[] styles;
514 protected Color caretColor;
515 protected Color selectionColor;
516 protected Color lineHighlightColor;
517 protected Color bracketHighlightColor;
518 protected Color eolMarkerColor;
519
520 protected boolean blockCaret;
521 protected boolean lineHighlight;
522 protected boolean bracketHighlight;
523 protected boolean paintInvalid;
524 protected boolean eolMarkers;
525
526
527
528 protected int tabSize;
529 protected FontMetrics fm;
530
531 protected Highlight highlights;
532
533 protected void paintLine(Graphics gfx, TokenMarker tokenMarker,
534 int line, int x)
535 {
536 Font defaultFont = getFont();
537 Color defaultColor = getForeground();
538
539 currentLineIndex = line;
540 int y = textArea.lineToY(line);
541
542 int lineWidth = 0;
543
544 if(line < 0 || line >= textArea.getLineCount())
545 {
546 if(paintInvalid)
547 {
548 paintHighlight(gfx,line,y);
549 styles[Token.INVALID].setGraphicsFlags(gfx,defaultFont);
550 gfx.drawString("~",0,y + fm.getHeight());
551 }
552 }
553 else if(tokenMarker == null)
554 {
555 lineWidth = paintPlainLine(gfx,line,defaultFont,defaultColor,x,y);
556 }
557 else
558 {
559 lineWidth = paintSyntaxLine(gfx,tokenMarker,line,defaultFont,
560 defaultColor,x,y);
561 }
562
563 if( lineWidth > areaWidth )
564 areaWidth = lineWidth;
565 }
566
567 protected int paintPlainLine(Graphics gfx, int line, Font defaultFont,
568 Color defaultColor, int x, int y)
569 {
570 paintHighlight(gfx,line,y);
571 textArea.getLineText(line,currentLine);
572
573 gfx.setFont(defaultFont);
574 gfx.setColor(defaultColor);
575
576 y += fm.getHeight();
577 x = Utilities.drawTabbedText(currentLine,x,y,gfx,this,0);
578
579 if(eolMarkers)
580 {
581 gfx.setColor(eolMarkerColor);
582 gfx.drawString(".",x,y);
583 }
584
585 return x;
586 }
587
588 protected int paintSyntaxLine(Graphics gfx, TokenMarker tokenMarker,
589 int line, Font defaultFont, Color defaultColor, int x, int y)
590 {
591 textArea.getLineText(currentLineIndex,currentLine);
592 currentLineTokens = tokenMarker.markTokens(currentLine,
593 currentLineIndex);
594
595 paintHighlight(gfx,line,y);
596
597 gfx.setFont(defaultFont);
598 gfx.setColor(defaultColor);
599 y += fm.getHeight();
600 x = SyntaxUtilities.paintSyntaxLine(currentLine,
601 currentLineTokens,styles,this,gfx,x,y);
602
603 if(eolMarkers)
604 {
605 gfx.setColor(eolMarkerColor);
606 gfx.drawString(".",x,y);
607 }
608
609 return x;
610 }
611
612 protected void paintHighlight(Graphics gfx, int line, int y)
613 {
614 if(line >= textArea.getSelectionStartLine()
615 && line <= textArea.getSelectionEndLine())
616 paintLineHighlight(gfx,line,y);
617
618 if(highlights != null)
619 highlights.paintHighlight(gfx,line,y);
620
621 if(bracketHighlight && line == textArea.getBracketLine())
622 paintBracketHighlight(gfx,line,y);
623
624 if(line == textArea.getCaretLine())
625 paintCaret(gfx,line,y);
626 }
627
628 protected void paintLineHighlight(Graphics gfx, int line, int y)
629 {
630 int height = fm.getHeight();
631 y += fm.getLeading() + fm.getMaxDescent();
632
633 int selectionStart = textArea.getSelectionStart();
634 int selectionEnd = textArea.getSelectionEnd();
635
636 if(selectionStart == selectionEnd)
637 {
638 if(lineHighlight)
639 {
640 gfx.setColor(lineHighlightColor);
641 gfx.fillRect(0,y,getWidth(),height);
642 }
643 }
644 else
645 {
646 gfx.setColor(selectionColor);
647
648 int selectionStartLine = textArea.getSelectionStartLine();
649 int selectionEndLine = textArea.getSelectionEndLine();
650 int lineStart = textArea.getLineStartOffset(line);
651
652 int x1, x2;
653 if(textArea.isSelectionRectangular())
654 {
655 int lineLen = textArea.getTabExpandedLineLength(line);
656 x1 = textArea._offsetToX(line,Math.min(lineLen,
657 selectionStart - textArea.getLineStartOffset(
658 selectionStartLine)));
659 x2 = textArea._offsetToX(line,Math.min(lineLen,
660 selectionEnd - textArea.getLineStartOffset(
661 selectionEndLine)));
662 if(x1 == x2)
663 x2++;
664 }
665 else if(selectionStartLine == selectionEndLine)
666 {
667 x1 = textArea._offsetToX(line,
668 selectionStart - lineStart);
669 x2 = textArea._offsetToX(line,
670 selectionEnd - lineStart);
671 }
672 else if(line == selectionStartLine)
673 {
674 x1 = textArea._offsetToX(line,
675 selectionStart - lineStart);
676 x2 = getWidth();
677 }
678 else if(line == selectionEndLine)
679 {
680 x1 = 0;
681 x2 = textArea._offsetToX(line,
682 selectionEnd - lineStart);
683 }
684 else
685 {
686 x1 = 0;
687 x2 = getWidth();
688 }
689
690
691 gfx.fillRect(x1 > x2 ? x2 : x1,y,x1 > x2 ?
692 (x1 - x2) : (x2 - x1),height);
693 }
694
695 }
696
697 protected void paintBracketHighlight(Graphics gfx, int line, int y)
698 {
699 int position = textArea.getBracketPosition();
700 if(position == -1)
701 return;
702 y += fm.getLeading() + fm.getMaxDescent();
703 int x = textArea._offsetToX(line,position);
704 gfx.setColor(bracketHighlightColor);
705
706
707
708 gfx.drawRect(x,y,fm.charWidth('(') - 1,
709 fm.getHeight() - 1);
710 }
711
712 protected void paintCaret(Graphics gfx, int line, int y)
713 {
714 if(textArea.isCaretVisible())
715 {
716 int offset = textArea.getCaretPosition()
717 - textArea.getLineStartOffset(line);
718 int caretX = textArea._offsetToX(line,offset);
719 int caretWidth = ((blockCaret ||
720 textArea.isOverwriteEnabled()) ?
721 fm.charWidth('w') : 1);
722 y += fm.getLeading() + fm.getMaxDescent();
723 int height = fm.getHeight();
724
725 gfx.setColor(caretColor);
726
727 if(textArea.isOverwriteEnabled())
728 {
729 gfx.fillRect(caretX,y + height - 1,
730 caretWidth,1);
731 }
732 else
733 {
734 gfx.drawRect(caretX,y,caretWidth, height - 1);
735 }
736 }
737 }
738 }