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