Statistics
| Revision:

svn-gvsig-desktop / tags / v1_0_2_Build_910 / libraries / libIverUtiles / src / com / iver / utiles / console / jedit / TextAreaPainter.java @ 11275

History | View | Annotate | Download (16.8 KB)

1
package com.iver.utiles.console.jedit;
2
/*
3
 * TextAreaPainter.java - Paints the text area
4
 * Copyright (C) 1999 Slava Pestov
5
 *
6
 * You may use and modify this package for any purpose. Redistribution is
7
 * permitted, in both source and binary form, provided that this notice
8
 * remains intact in all source distributions of this package.
9
 */
10

    
11
import java.awt.Color;
12
import java.awt.Cursor;
13
import java.awt.Dimension;
14
import java.awt.Font;
15
import java.awt.FontMetrics;
16
import java.awt.Graphics;
17
import java.awt.Rectangle;
18
import java.awt.Toolkit;
19
import java.awt.event.MouseEvent;
20

    
21
import javax.swing.JComponent;
22
import javax.swing.ToolTipManager;
23
import javax.swing.text.PlainDocument;
24
import javax.swing.text.Segment;
25
import javax.swing.text.TabExpander;
26
import javax.swing.text.Utilities;
27

    
28
/**
29
 * The text area repaint manager. It performs double buffering and paints
30
 * lines of text.
31
 * @author Slava Pestov
32
 * @version $Id$
33
 */
34
public class TextAreaPainter extends JComponent implements TabExpander
35
{
36
        /**
37
         * Creates a new repaint manager. This should be not be called
38
         * directly.
39
         */
40
        public TextAreaPainter(JEditTextArea textArea, TextAreaDefaults defaults)
41
        {
42
                this.textArea = textArea;
43

    
44
                setAutoscrolls(true);
45
                setDoubleBuffered(true);
46
                setOpaque(true);
47

    
48
                ToolTipManager.sharedInstance().registerComponent(this);
49

    
50
                currentLine = new Segment();
51
                currentLineIndex = -1;
52

    
53
                setCursor(Cursor.getPredefinedCursor(Cursor.TEXT_CURSOR));
54

    
55
                setFont(new Font("Monospaced",Font.PLAIN,14));
56
                setForeground(Color.black);
57
                setBackground(Color.white);
58

    
59
                blockCaret = defaults.blockCaret;
60
                styles = defaults.styles;
61
                cols = defaults.cols;
62
                rows = defaults.rows;
63
                caretColor = defaults.caretColor;
64
                selectionColor = defaults.selectionColor;
65
                lineHighlightColor = defaults.lineHighlightColor;
66
                lineHighlight = defaults.lineHighlight;
67
                bracketHighlightColor = defaults.bracketHighlightColor;
68
                bracketHighlight = defaults.bracketHighlight;
69
                paintInvalid = defaults.paintInvalid;
70
                eolMarkerColor = defaults.eolMarkerColor;
71
                eolMarkers = defaults.eolMarkers;
72
        }
73

    
74
        /**
75
         * Returns if this component can be traversed by pressing the
76
         * Tab key. This returns false.
77
         */
78
        public final boolean isManagingFocus()
79
        {
80
                return false;
81
        }
82

    
83
        /**
84
         * Returns the syntax styles used to paint colorized text. Entry <i>n</i>
85
         * will be used to paint tokens with id = <i>n</i>.
86
         * @see org.gjt.sp.jedit.syntax.Token
87
         */
88
        public final SyntaxStyle[] getStyles()
89
        {
90
                return styles;
91
        }
92

    
93
        /**
94
         * Sets the syntax styles used to paint colorized text. Entry <i>n</i>
95
         * will be used to paint tokens with id = <i>n</i>.
96
         * @param styles The syntax styles
97
         * @see org.gjt.sp.jedit.syntax.Token
98
         */
99
        public final void setStyles(SyntaxStyle[] styles)
100
        {
101
                this.styles = styles;
102
                repaint();
103
        }
104

    
105
        /**
106
         * Returns the caret color.
107
         */
108
        public final Color getCaretColor()
109
        {
110
                return caretColor;
111
        }
112

    
113
        /**
114
         * Sets the caret color.
115
         * @param caretColor The caret color
116
         */
117
        public final void setCaretColor(Color caretColor)
118
        {
119
                this.caretColor = caretColor;
120
                invalidateSelectedLines();
121
        }
122

    
123
        /**
124
         * Returns the selection color.
125
         */
126
        public final Color getSelectionColor()
127
        {
128
                return selectionColor;
129
        }
130

    
131
        /**
132
         * Sets the selection color.
133
         * @param selectionColor The selection color
134
         */
135
        public final void setSelectionColor(Color selectionColor)
136
        {
137
                this.selectionColor = selectionColor;
138
                invalidateSelectedLines();
139
        }
140

    
141
        /**
142
         * Returns the line highlight color.
143
         */
144
        public final Color getLineHighlightColor()
145
        {
146
                return lineHighlightColor;
147
        }
148

    
149
        /**
150
         * Sets the line highlight color.
151
         * @param lineHighlightColor The line highlight color
152
         */
153
        public final void setLineHighlightColor(Color lineHighlightColor)
154
        {
155
                this.lineHighlightColor = lineHighlightColor;
156
                invalidateSelectedLines();
157
        }
158

    
159
        /**
160
         * Returns true if line highlight is enabled, false otherwise.
161
         */
162
        public final boolean isLineHighlightEnabled()
163
        {
164
                return lineHighlight;
165
        }
166

    
167
        /**
168
         * Enables or disables current line highlighting.
169
         * @param lineHighlight True if current line highlight should be enabled,
170
         * false otherwise
171
         */
172
        public final void setLineHighlightEnabled(boolean lineHighlight)
173
        {
174
                this.lineHighlight = lineHighlight;
175
                invalidateSelectedLines();
176
        }
177

    
178
        /**
179
         * Returns the bracket highlight color.
180
         */
181
        public final Color getBracketHighlightColor()
182
        {
183
                return bracketHighlightColor;
184
        }
185

    
186
        /**
187
         * Sets the bracket highlight color.
188
         * @param bracketHighlightColor The bracket highlight color
189
         */
190
        public final void setBracketHighlightColor(Color bracketHighlightColor)
191
        {
192
                this.bracketHighlightColor = bracketHighlightColor;
193
                invalidateLine(textArea.getBracketLine());
194
        }
195

    
196
        /**
197
         * Returns true if bracket highlighting is enabled, false otherwise.
198
         * When bracket highlighting is enabled, the bracket matching the
199
         * one before the caret (if any) is highlighted.
200
         */
201
        public final boolean isBracketHighlightEnabled()
202
        {
203
                return bracketHighlight;
204
        }
205

    
206
        /**
207
         * Enables or disables bracket highlighting.
208
         * When bracket highlighting is enabled, the bracket matching the
209
         * one before the caret (if any) is highlighted.
210
         * @param bracketHighlight True if bracket highlighting should be
211
         * enabled, false otherwise
212
         */
213
        public final void setBracketHighlightEnabled(boolean bracketHighlight)
214
        {
215
                this.bracketHighlight = bracketHighlight;
216
                invalidateLine(textArea.getBracketLine());
217
        }
218

    
219
        /**
220
         * Returns true if the caret should be drawn as a block, false otherwise.
221
         */
222
        public final boolean isBlockCaretEnabled()
223
        {
224
                return blockCaret;
225
        }
226

    
227
        /**
228
         * Sets if the caret should be drawn as a block, false otherwise.
229
         * @param blockCaret True if the caret should be drawn as a block,
230
         * false otherwise.
231
         */
232
        public final void setBlockCaretEnabled(boolean blockCaret)
233
        {
234
                this.blockCaret = blockCaret;
235
                invalidateSelectedLines();
236
        }
237

    
238
        /**
239
         * Returns the EOL marker color.
240
         */
241
        public final Color getEOLMarkerColor()
242
        {
243
                return eolMarkerColor;
244
        }
245

    
246
        /**
247
         * Sets the EOL marker color.
248
         * @param eolMarkerColor The EOL marker color
249
         */
250
        public final void setEOLMarkerColor(Color eolMarkerColor)
251
        {
252
                this.eolMarkerColor = eolMarkerColor;
253
                repaint();
254
        }
255

    
256
        /**
257
         * Returns true if EOL markers are drawn, false otherwise.
258
         */
259
        public final boolean getEOLMarkersPainted()
260
        {
261
                return eolMarkers;
262
        }
263

    
264
        /**
265
         * Sets if EOL markers are to be drawn.
266
         * @param eolMarkers True if EOL markers should be drawn, false otherwise
267
         */
268
        public final void setEOLMarkersPainted(boolean eolMarkers)
269
        {
270
                this.eolMarkers = eolMarkers;
271
                repaint();
272
        }
273

    
274
        /**
275
         * Returns true if invalid lines are painted as red tildes (~),
276
         * false otherwise.
277
         */
278
        public boolean getInvalidLinesPainted()
279
        {
280
                return paintInvalid;
281
        }
282

    
283
        /**
284
         * Sets if invalid lines are to be painted as red tildes.
285
         * @param paintInvalid True if invalid lines should be drawn, false otherwise
286
         */
287
        public void setInvalidLinesPainted(boolean paintInvalid)
288
        {
289
                this.paintInvalid = paintInvalid;
290
        }
291

    
292
        /**
293
         * Adds a custom highlight painter.
294
         * @param highlight The highlight
295
         */
296
        public void addCustomHighlight(Highlight highlight)
297
        {
298
                highlight.init(textArea,highlights);
299
                highlights = highlight;
300
        }
301

    
302
        /**
303
         * Highlight interface.
304
         */
305
        public interface Highlight
306
        {
307
                /**
308
                 * Called after the highlight painter has been added.
309
                 * @param textArea The text area
310
                 * @param next The painter this one should delegate to
311
                 */
312
                void init(JEditTextArea textArea, Highlight next);
313

    
314
                /**
315
                 * This should paint the highlight and delgate to the
316
                 * next highlight painter.
317
                 * @param gfx The graphics context
318
                 * @param line The line number
319
                 * @param y The y co-ordinate of the line
320
                 */
321
                void paintHighlight(Graphics gfx, int line, int y);
322

    
323
                /**
324
                 * Returns the tool tip to display at the specified
325
                 * location. If this highlighter doesn't know what to
326
                 * display, it should delegate to the next highlight
327
                 * painter.
328
                 * @param evt The mouse event
329
                 */
330
                String getToolTipText(MouseEvent evt);
331
        }
332

    
333
        /**
334
         * Returns the tool tip to display at the specified location.
335
         * @param evt The mouse event
336
         */
337
        public String getToolTipText(MouseEvent evt)
338
        {
339
                if(highlights != null)
340
                        return highlights.getToolTipText(evt);
341
                else
342
                        return null;
343
        }
344

    
345
        /**
346
         * Returns the font metrics used by this component.
347
         */
348
        public FontMetrics getFontMetrics()
349
        {
350
                return fm;
351
        }
352

    
353
        /**
354
         * Sets the font for this component. This is overridden to update the
355
         * cached font metrics and to recalculate which lines are visible.
356
         * @param font The font
357
         */
358
        public void setFont(Font font)
359
        {
360
                super.setFont(font);
361
                fm = Toolkit.getDefaultToolkit().getFontMetrics(font);
362
                textArea.recalculateVisibleLines();
363
        }
364

    
365
        /**
366
         * Repaints the text.
367
         * @param g The graphics context
368
         */
369
        public void paint(Graphics gfx)
370
        {
371
                tabSize = fm.charWidth(' ') * ((Integer)textArea
372
                        .getDocument().getProperty(
373
                        PlainDocument.tabSizeAttribute)).intValue();
374

    
375
                Rectangle clipRect = gfx.getClipBounds();
376

    
377
                gfx.setColor(getBackground());
378
                gfx.fillRect(clipRect.x,clipRect.y,clipRect.width,clipRect.height);
379

    
380
                // We don't use yToLine() here because that method doesn't
381
                // return lines past the end of the document
382
                int height = fm.getHeight();
383
                int firstLine = textArea.getFirstLine();
384
                int firstInvalid = firstLine + clipRect.y / height;
385
                // Because the clipRect's height is usually an even multiple
386
                // of the font height, we subtract 1 from it, otherwise one
387
                // too many lines will always be painted.
388
                int lastInvalid = firstLine + (clipRect.y + clipRect.height - 1) / height;
389

    
390
                try
391
                {
392
                        TokenMarker tokenMarker = ((SyntaxDocument)textArea.getDocument())
393
                                .getTokenMarker();
394
                        int x = textArea.getHorizontalOffset();
395

    
396
                        for(int line = firstInvalid; line <= lastInvalid; line++)
397
                        {
398
                                paintLine(gfx,tokenMarker,line,x);
399
                        }
400

    
401
                        if(tokenMarker != null && tokenMarker.isNextLineRequested())
402
                        {
403
                                int h = clipRect.y + clipRect.height;
404
                                repaint(0,h,getWidth(),getHeight() - h);
405
                        }
406
                }
407
                catch(Exception e)
408
                {
409
                        System.err.println("Error repainting line"
410
                                + " range {" + firstInvalid + ","
411
                                + lastInvalid + "}:");
412
                        e.printStackTrace();
413
                }
414
        }
415

    
416
        /**
417
         * Marks a line as needing a repaint.
418
         * @param line The line to invalidate
419
         */
420
        public final void invalidateLine(int line)
421
        {
422
                repaint(0,textArea.lineToY(line) + fm.getMaxDescent() + fm.getLeading(),
423
                        getWidth(),fm.getHeight());
424
        }
425

    
426
        /**
427
         * Marks a range of lines as needing a repaint.
428
         * @param firstLine The first line to invalidate
429
         * @param lastLine The last line to invalidate
430
         */
431
        public final void invalidateLineRange(int firstLine, int lastLine)
432
        {
433
                repaint(0,textArea.lineToY(firstLine) + fm.getMaxDescent() + fm.getLeading(),
434
                        getWidth(),(lastLine - firstLine + 1) * fm.getHeight());
435
        }
436

    
437
        /**
438
         * Repaints the lines containing the selection.
439
         */
440
        public final void invalidateSelectedLines()
441
        {
442
                invalidateLineRange(textArea.getSelectionStartLine(),
443
                        textArea.getSelectionEndLine());
444
        }
445

    
446
        /**
447
         * Implementation of TabExpander interface. Returns next tab stop after
448
         * a specified point.
449
         * @param x The x co-ordinate
450
         * @param tabOffset Ignored
451
         * @return The next tab stop after <i>x</i>
452
         */
453
        public float nextTabStop(float x, int tabOffset)
454
        {
455
                int offset = textArea.getHorizontalOffset();
456
                int ntabs = ((int)x - offset) / tabSize;
457
                return (ntabs + 1) * tabSize + offset;
458
        }
459

    
460
        /**
461
         * Returns the painter's preferred size.
462
         */
463
        public Dimension getPreferredSize()
464
        {
465
                Dimension dim = new Dimension();
466
                dim.width = fm.charWidth('w') * cols;
467
                dim.height = fm.getHeight() * rows;
468
                return dim;
469
        }
470

    
471

    
472
        /**
473
         * Returns the painter's minimum size.
474
         */
475
        public Dimension getMinimumSize()
476
        {
477
                return getPreferredSize();
478
        }
479

    
480
        // package-private members
481
        int currentLineIndex;
482
        Token currentLineTokens;
483
        Segment currentLine;
484

    
485
        // protected members
486
        protected JEditTextArea textArea;
487

    
488
        protected SyntaxStyle[] styles;
489
        protected Color caretColor;
490
        protected Color selectionColor;
491
        protected Color lineHighlightColor;
492
        protected Color bracketHighlightColor;
493
        protected Color eolMarkerColor;
494

    
495
        protected boolean blockCaret;
496
        protected boolean lineHighlight;
497
        protected boolean bracketHighlight;
498
        protected boolean paintInvalid;
499
        protected boolean eolMarkers;
500
        protected int cols;
501
        protected int rows;
502

    
503
        protected int tabSize;
504
        protected FontMetrics fm;
505

    
506
        protected Highlight highlights;
507

    
508
        protected void paintLine(Graphics gfx, TokenMarker tokenMarker,
509
                int line, int x)
510
        {
511
                Font defaultFont = getFont();
512
                Color defaultColor = getForeground();
513

    
514
                currentLineIndex = line;
515
                int y = textArea.lineToY(line);
516

    
517
                if(line < 0 || line >= textArea.getLineCount())
518
                {
519
                        if(paintInvalid)
520
                        {
521
                                paintHighlight(gfx,line,y);
522
                                styles[Token.INVALID].setGraphicsFlags(gfx,defaultFont);
523
                                gfx.drawString("~",0,y + fm.getHeight());
524
                        }
525
                }
526
                else if(tokenMarker == null)
527
                {
528
                        paintPlainLine(gfx,line,defaultFont,defaultColor,x,y);
529
                }
530
                else
531
                {
532
                        paintSyntaxLine(gfx,tokenMarker,line,defaultFont,
533
                                defaultColor,x,y);
534
                }
535
        }
536

    
537
        protected void paintPlainLine(Graphics gfx, int line, Font defaultFont,
538
                Color defaultColor, int x, int y)
539
        {
540
                paintHighlight(gfx,line,y);
541
                textArea.getLineText(line,currentLine);
542

    
543
                gfx.setFont(defaultFont);
544
                gfx.setColor(defaultColor);
545

    
546
                y += fm.getHeight();
547
                x = Utilities.drawTabbedText(currentLine,x,y,gfx,this,0);
548

    
549
                if(eolMarkers)
550
                {
551
                        gfx.setColor(eolMarkerColor);
552
                        gfx.drawString(".",x,y);
553
                }
554
        }
555

    
556
        protected void paintSyntaxLine(Graphics gfx, TokenMarker tokenMarker,
557
                int line, Font defaultFont, Color defaultColor, int x, int y)
558
        {
559
                textArea.getLineText(currentLineIndex,currentLine);
560
                currentLineTokens = tokenMarker.markTokens(currentLine,
561
                        currentLineIndex);
562

    
563
                paintHighlight(gfx,line,y);
564

    
565
                gfx.setFont(defaultFont);
566
                gfx.setColor(defaultColor);
567
                y += fm.getHeight();
568
                x = SyntaxUtilities.paintSyntaxLine(currentLine,
569
                        currentLineTokens,styles,this,gfx,x,y);
570

    
571
                if(eolMarkers)
572
                {
573
                        gfx.setColor(eolMarkerColor);
574
                        gfx.drawString(".",x,y);
575
                }
576
        }
577

    
578
        protected void paintHighlight(Graphics gfx, int line, int y)
579
        {
580
                if(line >= textArea.getSelectionStartLine()
581
                        && line <= textArea.getSelectionEndLine())
582
                        paintLineHighlight(gfx,line,y);
583

    
584
                if(highlights != null)
585
                        highlights.paintHighlight(gfx,line,y);
586

    
587
                if(bracketHighlight && line == textArea.getBracketLine())
588
                        paintBracketHighlight(gfx,line,y);
589

    
590
                if(line == textArea.getCaretLine())
591
                        paintCaret(gfx,line,y);
592
        }
593

    
594
        protected void paintLineHighlight(Graphics gfx, int line, int y)
595
        {
596
                int height = fm.getHeight();
597
                y += fm.getLeading() + fm.getMaxDescent();
598

    
599
                int selectionStart = textArea.getSelectionStart();
600
                int selectionEnd = textArea.getSelectionEnd();
601

    
602
                if(selectionStart == selectionEnd)
603
                {
604
                        if(lineHighlight)
605
                        {
606
                                gfx.setColor(lineHighlightColor);
607
                                gfx.fillRect(0,y,getWidth(),height);
608
                        }
609
                }
610
                else
611
                {
612
                        gfx.setColor(selectionColor);
613

    
614
                        int selectionStartLine = textArea.getSelectionStartLine();
615
                        int selectionEndLine = textArea.getSelectionEndLine();
616
                        int lineStart = textArea.getLineStartOffset(line);
617

    
618
                        int x1, x2;
619
                        if(textArea.isSelectionRectangular())
620
                        {
621
                                int lineLen = textArea.getLineLength(line);
622
                                x1 = textArea._offsetToX(line,Math.min(lineLen,
623
                                        selectionStart - textArea.getLineStartOffset(
624
                                        selectionStartLine)));
625
                                x2 = textArea._offsetToX(line,Math.min(lineLen,
626
                                        selectionEnd - textArea.getLineStartOffset(
627
                                        selectionEndLine)));
628
                                if(x1 == x2)
629
                                        x2++;
630
                        }
631
                        else if(selectionStartLine == selectionEndLine)
632
                        {
633
                                x1 = textArea._offsetToX(line,
634
                                        selectionStart - lineStart);
635
                                x2 = textArea._offsetToX(line,
636
                                        selectionEnd - lineStart);
637
                        }
638
                        else if(line == selectionStartLine)
639
                        {
640
                                x1 = textArea._offsetToX(line,
641
                                        selectionStart - lineStart);
642
                                x2 = getWidth();
643
                        }
644
                        else if(line == selectionEndLine)
645
                        {
646
                                x1 = 0;
647
                                x2 = textArea._offsetToX(line,
648
                                        selectionEnd - lineStart);
649
                        }
650
                        else
651
                        {
652
                                x1 = 0;
653
                                x2 = getWidth();
654
                        }
655

    
656
                        // "inlined" min/max()
657
                        gfx.fillRect(x1 > x2 ? x2 : x1,y,x1 > x2 ?
658
                                (x1 - x2) : (x2 - x1),height);
659
                }
660

    
661
        }
662

    
663
        protected void paintBracketHighlight(Graphics gfx, int line, int y)
664
        {
665
                int position = textArea.getBracketPosition();
666
                if(position == -1)
667
                        return;
668
                y += fm.getLeading() + fm.getMaxDescent();
669
                int x = textArea._offsetToX(line,position);
670
                gfx.setColor(bracketHighlightColor);
671
                // Hack!!! Since there is no fast way to get the character
672
                // from the bracket matching routine, we use ( since all
673
                // brackets probably have the same width anyway
674
                gfx.drawRect(x,y,fm.charWidth('(') - 1,
675
                        fm.getHeight() - 1);
676
        }
677

    
678
        protected void paintCaret(Graphics gfx, int line, int y)
679
        {
680
                if(textArea.isCaretVisible())
681
                {
682
                        int offset = textArea.getCaretPosition()
683
                                - textArea.getLineStartOffset(line);
684
                        int caretX = textArea._offsetToX(line,offset);
685
                        int caretWidth = ((blockCaret ||
686
                                textArea.isOverwriteEnabled()) ?
687
                                fm.charWidth('w') : 1);
688
                        y += fm.getLeading() + fm.getMaxDescent();
689
                        int height = fm.getHeight();
690

    
691
                        gfx.setColor(caretColor);
692

    
693
                        if(textArea.isOverwriteEnabled())
694
                        {
695
                                gfx.fillRect(caretX,y + height - 1,
696
                                        caretWidth,1);
697
                        }
698
                        else
699
                        {
700
                                gfx.drawRect(caretX,y,caretWidth - 1,height - 1);
701
                        }
702
                }
703
        }
704
}