KickJava   Java API By Example, From Geeks To Geeks.

Java > Open Source Codes > org > eclipse > jface > text > WhitespaceCharacterPainter


1 /*******************************************************************************
2  * Copyright (c) 2006, 2007 Wind River Systems, Inc. and others.
3  * All rights reserved. This program and the accompanying materials
4  * are made available under the terms of the Eclipse Public License v1.0
5  * which accompanies this distribution, and is available at
6  * http://www.eclipse.org/legal/epl-v10.html
7  *
8  * Contributors:
9  * Anton Leherbauer (Wind River Systems) - initial API and implementation - https://bugs.eclipse.org/bugs/show_bug.cgi?id=22712
10  *******************************************************************************/

11 package org.eclipse.jface.text;
12
13 import org.eclipse.swt.custom.StyleRange;
14 import org.eclipse.swt.custom.StyledText;
15 import org.eclipse.swt.custom.StyledTextContent;
16 import org.eclipse.swt.events.PaintEvent;
17 import org.eclipse.swt.events.PaintListener;
18 import org.eclipse.swt.graphics.Color;
19 import org.eclipse.swt.graphics.FontMetrics;
20 import org.eclipse.swt.graphics.GC;
21 import org.eclipse.swt.graphics.Point;
22
23
24 /**
25  * A painter for drawing visible characters for (invisible) whitespace
26  * characters.
27  *
28  * @since 3.3
29  */

30 public class WhitespaceCharacterPainter implements IPainter, PaintListener {
31
32     private static final char SPACE_SIGN= '\u00b7';
33     private static final char IDEOGRAPHIC_SPACE_SIGN= '\u00b0';
34     private static final char TAB_SIGN= '\u00bb';
35     private static final char CARRIAGE_RETURN_SIGN= '\u00a4';
36     private static final char LINE_FEED_SIGN= '\u00b6';
37     
38     /** Indicates whether this painter is active. */
39     private boolean fIsActive= false;
40     /** The source viewer this painter is attached to. */
41     private ITextViewer fTextViewer;
42     /** The viewer's widget. */
43     private StyledText fTextWidget;
44     /** Tells whether the advanced graphics sub system is available. */
45     private boolean fIsAdvancedGraphicsPresent;
46
47     /**
48      * Creates a new painter for the given text viewer.
49      *
50      * @param textViewer the text viewer the painter should be attached to
51      */

52     public WhitespaceCharacterPainter(ITextViewer textViewer) {
53         super();
54         fTextViewer= textViewer;
55         fTextWidget= textViewer.getTextWidget();
56         GC gc= new GC(fTextWidget);
57         gc.setAdvanced(true);
58         fIsAdvancedGraphicsPresent= gc.getAdvanced();
59         gc.dispose();
60     }
61
62     /*
63      * @see org.eclipse.jface.text.IPainter#dispose()
64      */

65     public void dispose() {
66         fTextViewer= null;
67         fTextWidget= null;
68     }
69
70     /*
71      * @see org.eclipse.jface.text.IPainter#paint(int)
72      */

73     public void paint(int reason) {
74         IDocument document= fTextViewer.getDocument();
75         if (document == null) {
76             deactivate(false);
77             return;
78         }
79         if (!fIsActive) {
80             fIsActive= true;
81             fTextWidget.addPaintListener(this);
82             redrawAll(true);
83         } else if (reason == CONFIGURATION || reason == INTERNAL) {
84             redrawAll(false);
85         } else if (reason == TEXT_CHANGE) {
86             // redraw current line only
87
try {
88                 IRegion lineRegion =
89                     document.getLineInformationOfOffset(getDocumentOffset(fTextWidget.getCaretOffset()));
90                 int widgetOffset= getWidgetOffset(lineRegion.getOffset());
91                 int charCount= fTextWidget.getCharCount();
92                 int redrawLength= Math.min(lineRegion.getLength(), charCount - widgetOffset);
93                 if (widgetOffset >= 0 && redrawLength > 0) {
94                     fTextWidget.redrawRange(widgetOffset, redrawLength, true);
95                 }
96             } catch (BadLocationException e) {
97                 // ignore
98
}
99         }
100     }
101
102     /*
103      * @see org.eclipse.jface.text.IPainter#deactivate(boolean)
104      */

105     public void deactivate(boolean redraw) {
106         if (fIsActive) {
107             fIsActive= false;
108             fTextWidget.removePaintListener(this);
109             if (redraw) {
110                 redrawAll(true);
111             }
112         }
113     }
114
115     /*
116      * @see org.eclipse.jface.text.IPainter#setPositionManager(org.eclipse.jface.text.IPaintPositionManager)
117      */

118     public void setPositionManager(IPaintPositionManager manager) {
119         // no need for a position manager
120
}
121
122     /*
123      * @see org.eclipse.swt.events.PaintListener#paintControl(org.eclipse.swt.events.PaintEvent)
124      */

125     public void paintControl(PaintEvent event) {
126         if (fTextWidget != null) {
127             handleDrawRequest(event.gc, event.x, event.y, event.width, event.height);
128         }
129     }
130
131     /**
132      * Draw characters in view range.
133      *
134      * @param gc
135      * @param x
136      * @param y
137      * @param w
138      * @param h
139      */

140     private void handleDrawRequest(GC gc, int x, int y, int w, int h) {
141         int lineCount= fTextWidget.getLineCount();
142         int startLine= (y + fTextWidget.getTopPixel()) / fTextWidget.getLineHeight();
143         int endLine= (y + h - 1 + fTextWidget.getTopPixel()) / fTextWidget.getLineHeight();
144         if (startLine <= endLine && startLine < lineCount) {
145             int startOffset= fTextWidget.getOffsetAtLine(startLine);
146             int endOffset =
147                 endLine < lineCount - 1 ? fTextWidget.getOffsetAtLine(endLine + 1) : fTextWidget.getCharCount();
148
149             if (fIsAdvancedGraphicsPresent) {
150                 int alpha= gc.getAlpha();
151                 gc.setAlpha(100);
152                 handleDrawRequest(gc, startOffset, endOffset);
153                 gc.setAlpha(alpha);
154             } else
155                 handleDrawRequest(gc, startOffset, endOffset);
156         }
157     }
158
159     /**
160      * Draw characters of content range.
161      *
162      * @param gc the GC
163      * @param startOffset inclusive start index
164      * @param endOffset exclusive end index
165      */

166     private void handleDrawRequest(GC gc, int startOffset, int endOffset) {
167         StyledTextContent content= fTextWidget.getContent();
168         int length= endOffset - startOffset;
169         String JavaDoc text= content.getTextRange(startOffset, length);
170         StyleRange styleRange= null;
171         Color fg= null;
172         Point selection= fTextWidget.getSelection();
173         StringBuffer JavaDoc visibleChar= new StringBuffer JavaDoc(10);
174         for (int textOffset= 0; textOffset <= length; ++textOffset) {
175             int delta= 0;
176             boolean eol= false;
177             if (textOffset < length) {
178                 delta= 1;
179                 char c= text.charAt(textOffset);
180                 switch (c) {
181                 case ' ' :
182                     visibleChar.append(SPACE_SIGN);
183                     // 'continue' would improve performance but may produce drawing errors
184
// for long runs of space if width of space and dot differ
185
break;
186                 case '\u3000' : // ideographic whitespace
187
visibleChar.append(IDEOGRAPHIC_SPACE_SIGN);
188                     // 'continue' would improve performance but may produce drawing errors
189
// for long runs of space if width of space and dot differ
190
break;
191                 case '\t' :
192                     visibleChar.append(TAB_SIGN);
193                     break;
194                 case '\r' :
195                     visibleChar.append(CARRIAGE_RETURN_SIGN);
196                     if (textOffset >= length - 1 || text.charAt(textOffset + 1) != '\n') {
197                         eol= true;
198                         break;
199                     }
200                     continue;
201                 case '\n' :
202                     visibleChar.append(LINE_FEED_SIGN);
203                     eol= true;
204                     break;
205                 default :
206                     delta= 0;
207                     break;
208                 }
209             }
210             if (visibleChar.length() > 0) {
211                 int widgetOffset= startOffset + textOffset - visibleChar.length() + delta;
212                 if (!eol || !isFoldedLine(content.getLineAtOffset(widgetOffset))) {
213                     if (widgetOffset >= selection.x && widgetOffset < selection.y) {
214                         fg= fTextWidget.getSelectionForeground();
215                     } else if (styleRange == null || styleRange.start + styleRange.length <= widgetOffset) {
216                         styleRange= fTextWidget.getStyleRangeAtOffset(widgetOffset);
217                         if (styleRange == null || styleRange.foreground == null) {
218                             fg= fTextWidget.getForeground();
219                         } else {
220                             fg= styleRange.foreground;
221                         }
222                     }
223                     draw(gc, widgetOffset, visibleChar.toString(), fg);
224                 }
225                 visibleChar.delete(0, visibleChar.length());
226             }
227         }
228     }
229
230     /**
231      * Check if the given widget line is a folded line.
232      *
233      * @param widgetLine the widget line number
234      * @return <code>true</code> if the line is folded
235      */

236     private boolean isFoldedLine(int widgetLine) {
237         if (fTextViewer instanceof ITextViewerExtension5) {
238             ITextViewerExtension5 extension= (ITextViewerExtension5)fTextViewer;
239             int modelLine= extension.widgetLine2ModelLine(widgetLine);
240             int widgetLine2= extension.modelLine2WidgetLine(modelLine + 1);
241             return widgetLine2 == -1;
242         }
243         return false;
244     }
245
246     /**
247      * Redraw all of the text widgets visible content.
248      *
249      * @param redrawBackground If true, clean background before painting text.
250      */

251     private void redrawAll(boolean redrawBackground) {
252         int startLine= fTextWidget.getTopPixel() / fTextWidget.getLineHeight();
253         int startOffset= fTextWidget.getOffsetAtLine(startLine);
254         int endLine= 1 + (fTextWidget.getTopPixel() + fTextWidget.getClientArea().height) / fTextWidget.getLineHeight();
255         int endOffset;
256         if (endLine >= fTextWidget.getLineCount()) {
257             endOffset= fTextWidget.getCharCount();
258         } else {
259             endOffset= fTextWidget.getOffsetAtLine(endLine);
260         }
261         if (startOffset < endOffset) {
262             // add 2 for line separator characters
263
endOffset= Math.min(endOffset + 2, fTextWidget.getCharCount());
264             int redrawOffset= startOffset;
265             int redrawLength= endOffset - redrawOffset;
266             fTextWidget.redrawRange(startOffset, redrawLength, redrawBackground);
267         }
268     }
269
270     /**
271      * Draw string at widget offset.
272      *
273      * @param gc
274      * @param offset the widget offset
275      * @param s the string to be drawn
276      * @param fg the foreground color
277      */

278     private void draw(GC gc, int offset, String JavaDoc s, Color fg) {
279         // Compute baseline delta (see https://bugs.eclipse.org/bugs/show_bug.cgi?id=165640)
280
int baseline= fTextWidget.getBaseline(offset);
281         FontMetrics fontMetrics= gc.getFontMetrics();
282         int fontBaseline= fontMetrics.getAscent() + fontMetrics.getLeading();
283         int baslineDelta= baseline - fontBaseline;
284         
285         Point pos= fTextWidget.getLocationAtOffset(offset);
286         gc.setForeground(fg);
287         gc.drawString(s, pos.x, pos.y + baslineDelta, true);
288     }
289
290     /**
291      * Convert a document offset to the corresponding widget offset.
292      *
293      * @param documentOffset
294      * @return widget offset
295      */

296     private int getWidgetOffset(int documentOffset) {
297         if (fTextViewer instanceof ITextViewerExtension5) {
298             ITextViewerExtension5 extension= (ITextViewerExtension5)fTextViewer;
299             return extension.modelOffset2WidgetOffset(documentOffset);
300         }
301         IRegion visible= fTextViewer.getVisibleRegion();
302         int widgetOffset= documentOffset - visible.getOffset();
303         if (widgetOffset > visible.getLength()) {
304             return -1;
305         }
306         return widgetOffset;
307     }
308
309     /**
310      * Convert a widget offset to the corresponding document offset.
311      *
312      * @param widgetOffset
313      * @return document offset
314      */

315     private int getDocumentOffset(int widgetOffset) {
316         if (fTextViewer instanceof ITextViewerExtension5) {
317             ITextViewerExtension5 extension= (ITextViewerExtension5)fTextViewer;
318             return extension.widgetOffset2ModelOffset(widgetOffset);
319         }
320         IRegion visible= fTextViewer.getVisibleRegion();
321         if (widgetOffset > visible.getLength()) {
322             return -1;
323         }
324         return widgetOffset + visible.getOffset();
325     }
326
327 }
328
Popular Tags