Statistics
| Revision:

svn-gvsig-desktop / trunk / org.gvsig.desktop / org.gvsig.desktop.library / org.gvsig.utils / src / main / java / org / gvsig / utils / console / jedit / TextAreaPainter.java @ 40561

History | View | Annotate | Download (17.8 KB)

1
/**
2
 * gvSIG. Desktop Geographic Information System.
3
 *
4
 * Copyright (C) 2007-2013 gvSIG Association.
5
 *
6
 * This program is free software; you can redistribute it and/or
7
 * modify it under the terms of the GNU General Public License
8
 * as published by the Free Software Foundation; either version 3
9
 * of the License, or (at your option) any later version.
10
 *
11
 * This program is distributed in the hope that it will be useful,
12
 * but WITHOUT ANY WARRANTY; without even the implied warranty of
13
 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
14
 * GNU General Public License for more details.
15
 *
16
 * You should have received a copy of the GNU General Public License
17
 * along with this program; if not, write to the Free Software
18
 * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston,
19
 * MA  02110-1301, USA.
20
 *
21
 * For any additional information, do not hesitate to contact us
22
 * at info AT gvsig.com, or visit our website www.gvsig.com.
23
 */
24
package org.gvsig.utils.console.jedit;
25
/*
26
 * TextAreaPainter.java - Paints the text area
27
 * Copyright (C) 1999 Slava Pestov
28
 *
29
 * You may use and modify this package for any purpose. Redistribution is
30
 * permitted, in both source and binary form, provided that this notice
31
 * remains intact in all source distributions of this package.
32
 */
33

    
34
import java.awt.Color;
35
import java.awt.Cursor;
36
import java.awt.Dimension;
37
import java.awt.Font;
38
import java.awt.FontMetrics;
39
import java.awt.Graphics;
40
import java.awt.Rectangle;
41
import java.awt.Toolkit;
42
import java.awt.event.MouseEvent;
43

    
44
import javax.swing.JComponent;
45
import javax.swing.ToolTipManager;
46
import javax.swing.text.PlainDocument;
47
import javax.swing.text.Segment;
48
import javax.swing.text.TabExpander;
49
import javax.swing.text.Utilities;
50

    
51
/**
52
 * The text area repaint manager. It performs double buffering and paints
53
 * lines of text.
54
 * @author Slava Pestov
55
 * @version $Id$
56
 */
57
public class TextAreaPainter extends JComponent implements TabExpander
58
{
59
        /**
60
         * Creates a new repaint manager. This should be not be called
61
         * directly.
62
         */
63
        public TextAreaPainter(JEditTextArea textArea, TextAreaDefaults defaults)
64
        {
65
                this.textArea = textArea;
66

    
67
                setAutoscrolls(true);
68
                setDoubleBuffered(true);
69
                setOpaque(true);
70

    
71
                ToolTipManager.sharedInstance().registerComponent(this);
72

    
73
                currentLine = new Segment();
74
                currentLineIndex = -1;
75

    
76
                setCursor(Cursor.getPredefinedCursor(Cursor.TEXT_CURSOR));
77

    
78
                setFont(new Font("Monospaced",Font.PLAIN,14));
79
                setForeground(Color.black);
80
                setBackground(Color.white);
81

    
82
                blockCaret = defaults.blockCaret;
83
                styles = defaults.styles;
84
                cols = defaults.cols;
85
                rows = defaults.rows;
86
                caretColor = defaults.caretColor;
87
                selectionColor = defaults.selectionColor;
88
                lineHighlightColor = defaults.lineHighlightColor;
89
                lineHighlight = defaults.lineHighlight;
90
                bracketHighlightColor = defaults.bracketHighlightColor;
91
                bracketHighlight = defaults.bracketHighlight;
92
                paintInvalid = defaults.paintInvalid;
93
                eolMarkerColor = defaults.eolMarkerColor;
94
                eolMarkers = defaults.eolMarkers;
95
        }
96

    
97
        /**
98
         * Returns if this component can be traversed by pressing the
99
         * Tab key. This returns false.
100
         */
101
        public final boolean isManagingFocus()
102
        {
103
                return false;
104
        }
105

    
106
        /**
107
         * Returns the syntax styles used to paint colorized text. Entry <i>n</i>
108
         * will be used to paint tokens with id = <i>n</i>.
109
         * @see org.gjt.sp.jedit.syntax.Token
110
         */
111
        public final SyntaxStyle[] getStyles()
112
        {
113
                return styles;
114
        }
115

    
116
        /**
117
         * Sets the syntax styles used to paint colorized text. Entry <i>n</i>
118
         * will be used to paint tokens with id = <i>n</i>.
119
         * @param styles The syntax styles
120
         * @see org.gjt.sp.jedit.syntax.Token
121
         */
122
        public final void setStyles(SyntaxStyle[] styles)
123
        {
124
                this.styles = styles;
125
                repaint();
126
        }
127

    
128
        /**
129
         * Returns the caret color.
130
         */
131
        public final Color getCaretColor()
132
        {
133
                return caretColor;
134
        }
135

    
136
        /**
137
         * Sets the caret color.
138
         * @param caretColor The caret color
139
         */
140
        public final void setCaretColor(Color caretColor)
141
        {
142
                this.caretColor = caretColor;
143
                invalidateSelectedLines();
144
        }
145

    
146
        /**
147
         * Returns the selection color.
148
         */
149
        public final Color getSelectionColor()
150
        {
151
                return selectionColor;
152
        }
153

    
154
        /**
155
         * Sets the selection color.
156
         * @param selectionColor The selection color
157
         */
158
        public final void setSelectionColor(Color selectionColor)
159
        {
160
                this.selectionColor = selectionColor;
161
                invalidateSelectedLines();
162
        }
163

    
164
        /**
165
         * Returns the line highlight color.
166
         */
167
        public final Color getLineHighlightColor()
168
        {
169
                return lineHighlightColor;
170
        }
171

    
172
        /**
173
         * Sets the line highlight color.
174
         * @param lineHighlightColor The line highlight color
175
         */
176
        public final void setLineHighlightColor(Color lineHighlightColor)
177
        {
178
                this.lineHighlightColor = lineHighlightColor;
179
                invalidateSelectedLines();
180
        }
181

    
182
        /**
183
         * Returns true if line highlight is enabled, false otherwise.
184
         */
185
        public final boolean isLineHighlightEnabled()
186
        {
187
                return lineHighlight;
188
        }
189

    
190
        /**
191
         * Enables or disables current line highlighting.
192
         * @param lineHighlight True if current line highlight should be enabled,
193
         * false otherwise
194
         */
195
        public final void setLineHighlightEnabled(boolean lineHighlight)
196
        {
197
                this.lineHighlight = lineHighlight;
198
                invalidateSelectedLines();
199
        }
200

    
201
        /**
202
         * Returns the bracket highlight color.
203
         */
204
        public final Color getBracketHighlightColor()
205
        {
206
                return bracketHighlightColor;
207
        }
208

    
209
        /**
210
         * Sets the bracket highlight color.
211
         * @param bracketHighlightColor The bracket highlight color
212
         */
213
        public final void setBracketHighlightColor(Color bracketHighlightColor)
214
        {
215
                this.bracketHighlightColor = bracketHighlightColor;
216
                invalidateLine(textArea.getBracketLine());
217
        }
218

    
219
        /**
220
         * Returns true if bracket highlighting is enabled, false otherwise.
221
         * When bracket highlighting is enabled, the bracket matching the
222
         * one before the caret (if any) is highlighted.
223
         */
224
        public final boolean isBracketHighlightEnabled()
225
        {
226
                return bracketHighlight;
227
        }
228

    
229
        /**
230
         * Enables or disables bracket highlighting.
231
         * When bracket highlighting is enabled, the bracket matching the
232
         * one before the caret (if any) is highlighted.
233
         * @param bracketHighlight True if bracket highlighting should be
234
         * enabled, false otherwise
235
         */
236
        public final void setBracketHighlightEnabled(boolean bracketHighlight)
237
        {
238
                this.bracketHighlight = bracketHighlight;
239
                invalidateLine(textArea.getBracketLine());
240
        }
241

    
242
        /**
243
         * Returns true if the caret should be drawn as a block, false otherwise.
244
         */
245
        public final boolean isBlockCaretEnabled()
246
        {
247
                return blockCaret;
248
        }
249

    
250
        /**
251
         * Sets if the caret should be drawn as a block, false otherwise.
252
         * @param blockCaret True if the caret should be drawn as a block,
253
         * false otherwise.
254
         */
255
        public final void setBlockCaretEnabled(boolean blockCaret)
256
        {
257
                this.blockCaret = blockCaret;
258
                invalidateSelectedLines();
259
        }
260

    
261
        /**
262
         * Returns the EOL marker color.
263
         */
264
        public final Color getEOLMarkerColor()
265
        {
266
                return eolMarkerColor;
267
        }
268

    
269
        /**
270
         * Sets the EOL marker color.
271
         * @param eolMarkerColor The EOL marker color
272
         */
273
        public final void setEOLMarkerColor(Color eolMarkerColor)
274
        {
275
                this.eolMarkerColor = eolMarkerColor;
276
                repaint();
277
        }
278

    
279
        /**
280
         * Returns true if EOL markers are drawn, false otherwise.
281
         */
282
        public final boolean getEOLMarkersPainted()
283
        {
284
                return eolMarkers;
285
        }
286

    
287
        /**
288
         * Sets if EOL markers are to be drawn.
289
         * @param eolMarkers True if EOL markers should be drawn, false otherwise
290
         */
291
        public final void setEOLMarkersPainted(boolean eolMarkers)
292
        {
293
                this.eolMarkers = eolMarkers;
294
                repaint();
295
        }
296

    
297
        /**
298
         * Returns true if invalid lines are painted as red tildes (~),
299
         * false otherwise.
300
         */
301
        public boolean getInvalidLinesPainted()
302
        {
303
                return paintInvalid;
304
        }
305

    
306
        /**
307
         * Sets if invalid lines are to be painted as red tildes.
308
         * @param paintInvalid True if invalid lines should be drawn, false otherwise
309
         */
310
        public void setInvalidLinesPainted(boolean paintInvalid)
311
        {
312
                this.paintInvalid = paintInvalid;
313
        }
314

    
315
        /**
316
         * Adds a custom highlight painter.
317
         * @param highlight The highlight
318
         */
319
        public void addCustomHighlight(Highlight highlight)
320
        {
321
                highlight.init(textArea,highlights);
322
                highlights = highlight;
323
        }
324

    
325
        /**
326
         * Highlight interface.
327
         */
328
        public interface Highlight
329
        {
330
                /**
331
                 * Called after the highlight painter has been added.
332
                 * @param textArea The text area
333
                 * @param next The painter this one should delegate to
334
                 */
335
                void init(JEditTextArea textArea, Highlight next);
336

    
337
                /**
338
                 * This should paint the highlight and delgate to the
339
                 * next highlight painter.
340
                 * @param gfx The graphics context
341
                 * @param line The line number
342
                 * @param y The y co-ordinate of the line
343
                 */
344
                void paintHighlight(Graphics gfx, int line, int y);
345

    
346
                /**
347
                 * Returns the tool tip to display at the specified
348
                 * location. If this highlighter doesn't know what to
349
                 * display, it should delegate to the next highlight
350
                 * painter.
351
                 * @param evt The mouse event
352
                 */
353
                String getToolTipText(MouseEvent evt);
354
        }
355

    
356
        /**
357
         * Returns the tool tip to display at the specified location.
358
         * @param evt The mouse event
359
         */
360
        public String getToolTipText(MouseEvent evt)
361
        {
362
                if(highlights != null)
363
                        return highlights.getToolTipText(evt);
364
                else
365
                        return null;
366
        }
367

    
368
        /**
369
         * Returns the font metrics used by this component.
370
         */
371
        public FontMetrics getFontMetrics()
372
        {
373
                return fm;
374
        }
375

    
376
        /**
377
         * Sets the font for this component. This is overridden to update the
378
         * cached font metrics and to recalculate which lines are visible.
379
         * @param font The font
380
         */
381
        public void setFont(Font font)
382
        {
383
                super.setFont(font);
384
                fm = Toolkit.getDefaultToolkit().getFontMetrics(font);
385
                textArea.recalculateVisibleLines();
386
        }
387

    
388
        /**
389
         * Repaints the text.
390
         * @param g The graphics context
391
         */
392
        public void paint(Graphics gfx)
393
        {
394
                tabSize = fm.charWidth(' ') * ((Integer)textArea
395
                        .getDocument().getProperty(
396
                        PlainDocument.tabSizeAttribute)).intValue();
397

    
398
                Rectangle clipRect = gfx.getClipBounds();
399

    
400
                gfx.setColor(getBackground());
401
                gfx.fillRect(clipRect.x,clipRect.y,clipRect.width,clipRect.height);
402

    
403
                // We don't use yToLine() here because that method doesn't
404
                // return lines past the end of the document
405
                int height = fm.getHeight();
406
                int firstLine = textArea.getFirstLine();
407
                int firstInvalid = firstLine + clipRect.y / height;
408
                // Because the clipRect's height is usually an even multiple
409
                // of the font height, we subtract 1 from it, otherwise one
410
                // too many lines will always be painted.
411
                int lastInvalid = firstLine + (clipRect.y + clipRect.height - 1) / height;
412

    
413
                try
414
                {
415
                        TokenMarker tokenMarker = ((SyntaxDocument)textArea.getDocument())
416
                                .getTokenMarker();
417
                        int x = textArea.getHorizontalOffset();
418

    
419
                        for(int line = firstInvalid; line <= lastInvalid; line++)
420
                        {
421
                                paintLine(gfx,tokenMarker,line,x);
422
                        }
423

    
424
                        if(tokenMarker != null && tokenMarker.isNextLineRequested())
425
                        {
426
                                int h = clipRect.y + clipRect.height;
427
                                repaint(0,h,getWidth(),getHeight() - h);
428
                        }
429
                }
430
                catch(Exception e)
431
                {
432
                        System.err.println("Error repainting line"
433
                                + " range {" + firstInvalid + ","
434
                                + lastInvalid + "}:");
435
                        e.printStackTrace();
436
                }
437
        }
438

    
439
        /**
440
         * Marks a line as needing a repaint.
441
         * @param line The line to invalidate
442
         */
443
        public final void invalidateLine(int line)
444
        {
445
                repaint(0,textArea.lineToY(line) + fm.getMaxDescent() + fm.getLeading(),
446
                        getWidth(),fm.getHeight());
447
        }
448

    
449
        /**
450
         * Marks a range of lines as needing a repaint.
451
         * @param firstLine The first line to invalidate
452
         * @param lastLine The last line to invalidate
453
         */
454
        public final void invalidateLineRange(int firstLine, int lastLine)
455
        {
456
                repaint(0,textArea.lineToY(firstLine) + fm.getMaxDescent() + fm.getLeading(),
457
                        getWidth(),(lastLine - firstLine + 1) * fm.getHeight());
458
        }
459

    
460
        /**
461
         * Repaints the lines containing the selection.
462
         */
463
        public final void invalidateSelectedLines()
464
        {
465
                invalidateLineRange(textArea.getSelectionStartLine(),
466
                        textArea.getSelectionEndLine());
467
        }
468

    
469
        /**
470
         * Implementation of TabExpander interface. Returns next tab stop after
471
         * a specified point.
472
         * @param x The x co-ordinate
473
         * @param tabOffset Ignored
474
         * @return The next tab stop after <i>x</i>
475
         */
476
        public float nextTabStop(float x, int tabOffset)
477
        {
478
                int offset = textArea.getHorizontalOffset();
479
                int ntabs = ((int)x - offset) / tabSize;
480
                return (ntabs + 1) * tabSize + offset;
481
        }
482

    
483
        /**
484
         * Returns the painter's preferred size.
485
         */
486
        public Dimension getPreferredSize()
487
        {
488
                Dimension dim = new Dimension();
489
                dim.width = fm.charWidth('w') * cols;
490
                dim.height = fm.getHeight() * rows;
491
                return dim;
492
        }
493

    
494

    
495
        /**
496
         * Returns the painter's minimum size.
497
         */
498
        public Dimension getMinimumSize()
499
        {
500
                return getPreferredSize();
501
        }
502

    
503
        // package-private members
504
        int currentLineIndex;
505
        Token currentLineTokens;
506
        Segment currentLine;
507

    
508
        // protected members
509
        protected JEditTextArea textArea;
510

    
511
        protected SyntaxStyle[] styles;
512
        protected Color caretColor;
513
        protected Color selectionColor;
514
        protected Color lineHighlightColor;
515
        protected Color bracketHighlightColor;
516
        protected Color eolMarkerColor;
517

    
518
        protected boolean blockCaret;
519
        protected boolean lineHighlight;
520
        protected boolean bracketHighlight;
521
        protected boolean paintInvalid;
522
        protected boolean eolMarkers;
523
        protected int cols;
524
        protected int rows;
525

    
526
        protected int tabSize;
527
        protected FontMetrics fm;
528

    
529
        protected Highlight highlights;
530

    
531
        protected void paintLine(Graphics gfx, TokenMarker tokenMarker,
532
                int line, int x)
533
        {
534
                Font defaultFont = getFont();
535
                Color defaultColor = getForeground();
536

    
537
                currentLineIndex = line;
538
                int y = textArea.lineToY(line);
539

    
540
                if(line < 0 || line >= textArea.getLineCount())
541
                {
542
                        if(paintInvalid)
543
                        {
544
                                paintHighlight(gfx,line,y);
545
                                styles[Token.INVALID].setGraphicsFlags(gfx,defaultFont);
546
                                gfx.drawString("~",0,y + fm.getHeight());
547
                        }
548
                }
549
                else if(tokenMarker == null)
550
                {
551
                        paintPlainLine(gfx,line,defaultFont,defaultColor,x,y);
552
                }
553
                else
554
                {
555
                        paintSyntaxLine(gfx,tokenMarker,line,defaultFont,
556
                                defaultColor,x,y);
557
                }
558
        }
559

    
560
        protected void paintPlainLine(Graphics gfx, int line, Font defaultFont,
561
                Color defaultColor, int x, int y)
562
        {
563
                paintHighlight(gfx,line,y);
564
                textArea.getLineText(line,currentLine);
565

    
566
                gfx.setFont(defaultFont);
567
                gfx.setColor(defaultColor);
568

    
569
                y += fm.getHeight();
570
                x = Utilities.drawTabbedText(currentLine,x,y,gfx,this,0);
571

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

    
579
        protected void paintSyntaxLine(Graphics gfx, TokenMarker tokenMarker,
580
                int line, Font defaultFont, Color defaultColor, int x, int y)
581
        {
582
                textArea.getLineText(currentLineIndex,currentLine);
583
                currentLineTokens = tokenMarker.markTokens(currentLine,
584
                        currentLineIndex);
585

    
586
                paintHighlight(gfx,line,y);
587

    
588
                gfx.setFont(defaultFont);
589
                gfx.setColor(defaultColor);
590
                y += fm.getHeight();
591
                x = SyntaxUtilities.paintSyntaxLine(currentLine,
592
                        currentLineTokens,styles,this,gfx,x,y);
593

    
594
                if(eolMarkers)
595
                {
596
                        gfx.setColor(eolMarkerColor);
597
                        gfx.drawString(".",x,y);
598
                }
599
        }
600

    
601
        protected void paintHighlight(Graphics gfx, int line, int y)
602
        {
603
                if(line >= textArea.getSelectionStartLine()
604
                        && line <= textArea.getSelectionEndLine())
605
                        paintLineHighlight(gfx,line,y);
606

    
607
                if(highlights != null)
608
                        highlights.paintHighlight(gfx,line,y);
609

    
610
                if(bracketHighlight && line == textArea.getBracketLine())
611
                        paintBracketHighlight(gfx,line,y);
612

    
613
                if(line == textArea.getCaretLine())
614
                        paintCaret(gfx,line,y);
615
        }
616

    
617
        protected void paintLineHighlight(Graphics gfx, int line, int y)
618
        {
619
                int height = fm.getHeight();
620
                y += fm.getLeading() + fm.getMaxDescent();
621

    
622
                int selectionStart = textArea.getSelectionStart();
623
                int selectionEnd = textArea.getSelectionEnd();
624

    
625
                if(selectionStart == selectionEnd)
626
                {
627
                        if(lineHighlight)
628
                        {
629
                                gfx.setColor(lineHighlightColor);
630
                                gfx.fillRect(0,y,getWidth(),height);
631
                        }
632
                }
633
                else
634
                {
635
                        gfx.setColor(selectionColor);
636

    
637
                        int selectionStartLine = textArea.getSelectionStartLine();
638
                        int selectionEndLine = textArea.getSelectionEndLine();
639
                        int lineStart = textArea.getLineStartOffset(line);
640

    
641
                        int x1, x2;
642
                        if(textArea.isSelectionRectangular())
643
                        {
644
                                int lineLen = textArea.getLineLength(line);
645
                                x1 = textArea._offsetToX(line,Math.min(lineLen,
646
                                        selectionStart - textArea.getLineStartOffset(
647
                                        selectionStartLine)));
648
                                x2 = textArea._offsetToX(line,Math.min(lineLen,
649
                                        selectionEnd - textArea.getLineStartOffset(
650
                                        selectionEndLine)));
651
                                if(x1 == x2)
652
                                        x2++;
653
                        }
654
                        else if(selectionStartLine == selectionEndLine)
655
                        {
656
                                x1 = textArea._offsetToX(line,
657
                                        selectionStart - lineStart);
658
                                x2 = textArea._offsetToX(line,
659
                                        selectionEnd - lineStart);
660
                        }
661
                        else if(line == selectionStartLine)
662
                        {
663
                                x1 = textArea._offsetToX(line,
664
                                        selectionStart - lineStart);
665
                                x2 = getWidth();
666
                        }
667
                        else if(line == selectionEndLine)
668
                        {
669
                                x1 = 0;
670
                                x2 = textArea._offsetToX(line,
671
                                        selectionEnd - lineStart);
672
                        }
673
                        else
674
                        {
675
                                x1 = 0;
676
                                x2 = getWidth();
677
                        }
678

    
679
                        // "inlined" min/max()
680
                        gfx.fillRect(x1 > x2 ? x2 : x1,y,x1 > x2 ?
681
                                (x1 - x2) : (x2 - x1),height);
682
                }
683

    
684
        }
685

    
686
        protected void paintBracketHighlight(Graphics gfx, int line, int y)
687
        {
688
                int position = textArea.getBracketPosition();
689
                if(position == -1)
690
                        return;
691
                y += fm.getLeading() + fm.getMaxDescent();
692
                int x = textArea._offsetToX(line,position);
693
                gfx.setColor(bracketHighlightColor);
694
                // Hack!!! Since there is no fast way to get the character
695
                // from the bracket matching routine, we use ( since all
696
                // brackets probably have the same width anyway
697
                gfx.drawRect(x,y,fm.charWidth('(') - 1,
698
                        fm.getHeight() - 1);
699
        }
700

    
701
        protected void paintCaret(Graphics gfx, int line, int y)
702
        {
703
                if(textArea.isCaretVisible())
704
                {
705
                        int offset = textArea.getCaretPosition()
706
                                - textArea.getLineStartOffset(line);
707
                        int caretX = textArea._offsetToX(line,offset);
708
                        int caretWidth = ((blockCaret ||
709
                                textArea.isOverwriteEnabled()) ?
710
                                fm.charWidth('w') : 1);
711
                        y += fm.getLeading() + fm.getMaxDescent();
712
                        int height = fm.getHeight();
713

    
714
                        gfx.setColor(caretColor);
715

    
716
                        if(textArea.isOverwriteEnabled())
717
                        {
718
                                gfx.fillRect(caretX,y + height - 1,
719
                                        caretWidth,1);
720
                        }
721
                        else
722
                        {
723
                                gfx.drawRect(caretX,y,caretWidth - 1,height - 1);
724
                        }
725
                }
726
        }
727
}