source: other-projects/rsyntax-textarea/src/java/org/fife/ui/rsyntaxtextarea/VisibleWhitespaceToken.java@ 25584

Last change on this file since 25584 was 25584, checked in by davidb, 12 years ago

Initial cut an a text edit area for GLI that supports color syntax highlighting

File size: 7.7 KB
Line 
1/*
2 * 10/28/2004
3 *
4 * VisibleWhitespaceToken.java - Token that paints special symbols for its
5 * whitespace characters (space and tab).
6 *
7 * This library is distributed under a modified BSD license. See the included
8 * RSyntaxTextArea.License.txt file for details.
9 */
10package org.fife.ui.rsyntaxtextarea;
11
12import java.awt.Color;
13import java.awt.FontMetrics;
14import java.awt.Graphics2D;
15import java.awt.geom.Rectangle2D;
16import javax.swing.text.Segment;
17import javax.swing.text.TabExpander;
18
19
20/**
21 * This token class paints spaces and tabs with special symbols so the user
22 * can see the whitespace in his document. Rendering hints are honored.<p>
23 *
24 * The current implementation paints as follows:
25 * <ul>
26 * <li>The first tab or space, if any, is found in the token.</li>
27 * <li>If a tab was found, all characters up to it are painted as a
28 * group.</li>
29 * <li>If a space was found, all characters up to and including it are
30 * painted (it is painted with a special symbol to denote it as
31 * a space).</li>
32 * <li>If neither a tab nor a whitespace was found, all characters in the
33 * token are painted.</li>
34 * <li>Repeat until all characters are painted.</li>
35 * </ul>
36 * This means that rendering hints are applied to all groups of characters
37 * within a token, excluding whitespace and tabs.<p>
38 *
39 * A problem with this implementation is that FontMetrics.charsWidth() is still
40 * used to calculate the width of a group of chars painted. Thus, the group of
41 * characters will be painted with the rendering hints specified, but the
42 * following tab (or group of characters if the current group was the end of a
43 * token) will not necessarily be painted at the proper x-coordinate (as
44 * FontMetrics.charsWidth() returns an <code>int</code> and not a
45 * <code>float</code>). The way around this would be to calculate the token's
46 * width in such a way that a float is returned (Font.getStringBounds()?).
47 *
48 * @author Robert Futrell
49 * @version 0.5
50 * @see Token
51 * @see DefaultToken
52 */
53public class VisibleWhitespaceToken extends DefaultToken {
54
55 private Rectangle2D.Float dotRect;
56
57
58 /**
59 * Creates a "null token." The token itself is not null; rather, it
60 * signifies that it is the last token in a linked list of tokens and
61 * that it is not part of a "multi-line token."
62 */
63 public VisibleWhitespaceToken() {
64 super();
65 dotRect = new Rectangle2D.Float(0,0, 1,1);
66 }
67
68
69 /**
70 * Constructor.
71 *
72 * @param line The segment from which to get the token.
73 * @param beg The first character's position in <code>line</code>.
74 * @param end The last character's position in <code>line</code>.
75 * @param startOffset The offset into the document at which this
76 * token begins.
77 * @param type A token type listed as "generic" above.
78 */
79 public VisibleWhitespaceToken(final Segment line, final int beg,
80 final int end, final int startOffset, final int type) {
81 this(line.array, beg,end, startOffset, type);
82 }
83
84
85 /**
86 * Constructor.
87 *
88 * @param line The segment from which to get the token.
89 * @param beg The first character's position in <code>line</code>.
90 * @param end The last character's position in <code>line</code>.
91 * @param startOffset The offset into the document at which this
92 * token begins.
93 * @param type A token type listed as "generic" above.
94 */
95 public VisibleWhitespaceToken(final char[] line, final int beg,
96 final int end, final int startOffset, final int type) {
97 super(line, beg,end, startOffset, type);
98 }
99
100
101 /**
102 * Paints this token, using special symbols for whitespace characters.
103 *
104 * @param g The graphics context in which to paint.
105 * @param x The x-coordinate at which to paint.
106 * @param y The y-coordinate at which to paint.
107 * @param host The text area this token is in.
108 * @param e How to expand tabs.
109 * @param clipStart The left boundary of the clip rectangle in which we're
110 * painting. This optimizes painting by allowing us to not paint
111 * not paint when this token is "to the left" of the clip rectangle.
112 * @return The x-coordinate representing the end of the painted text.
113 */
114 public final float paint(Graphics2D g, float x, float y,
115 RSyntaxTextArea host, TabExpander e,
116 float clipStart) {
117
118 int origX = (int)x;
119 int end = textOffset + textCount;
120 float nextX = x;
121 int flushLen = 0;
122 int flushIndex = textOffset;
123 Color fg = host.getForegroundForToken(this);
124 Color bg = host.getBackgroundForTokenType(type);
125 g.setFont(host.getFontForTokenType(type));
126 FontMetrics fm = host.getFontMetricsForTokenType(type);
127
128 int ascent = fm.getAscent();
129 int height = fm.getHeight();
130
131 for (int i=textOffset; i<end; i++) {
132
133 switch (text[i]) {
134
135 case '\t':
136
137 // Fill in background.
138 nextX = x+fm.charsWidth(text, flushIndex,flushLen);
139 float nextNextX = e.nextTabStop(nextX, 0);
140 if (bg!=null) {
141 paintBackground(x,y, nextNextX-x,height, g,
142 ascent, host, bg);
143 }
144 g.setColor(fg);
145
146 // Paint chars cached before the tab.
147 if (flushLen > 0) {
148 g.drawChars(text, flushIndex, flushLen, (int)x,(int)y);
149 flushLen = 0;
150 }
151 flushIndex = i + 1;
152
153 // Draw an arrow representing the tab.
154 int halfHeight = height / 2;
155 int quarterHeight = halfHeight / 2;
156 int ymid = (int)y - ascent + halfHeight;
157 g.drawLine((int)nextX,ymid, (int)nextNextX,ymid);
158 g.drawLine((int)nextNextX,ymid, (int)nextNextX-4,ymid-quarterHeight);
159 g.drawLine((int)nextNextX,ymid, (int)nextNextX-4,ymid+quarterHeight);
160
161 x = nextNextX;
162 break;
163
164 case ' ':
165
166 // NOTE: There is a little bit of a "fudge factor"
167 // here when "smooth text" is enabled, as "width"
168 // below may well not be the width given to the space
169 // by fm.charsWidth() (it depends on how it places the
170 // space with respect to the preceding character).
171 // But, we assume the approximation is close enough for
172 // our drawing a dot for the space.
173
174 // "flushLen+1" ensures text is aligned correctly (or,
175 // aligned the same as in getWidth()).
176 nextX = x+fm.charsWidth(text, flushIndex,flushLen+1);
177 int width = fm.charWidth(' ');
178
179 // Paint background.
180 if (bg!=null) {
181 paintBackground(x,y, nextX-x,height, g,
182 ascent, host, bg);
183 }
184 g.setColor(fg);
185
186 // Paint chars before space.
187 if (flushLen>0) {
188 g.drawChars(text, flushIndex, flushLen, (int)x,(int)y);
189 flushLen = 0;
190 }
191
192 // Paint a dot representing the space.
193 dotRect.x = nextX - width/2.0f; // "2.0f" for FindBugs
194 dotRect.y = y - ascent + height/2.0f; // Ditto
195 g.fill(dotRect);
196 flushIndex = i + 1;
197 x = nextX;
198 break;
199
200
201 case '\f':
202 // ???
203 // fall-through for now.
204
205 default:
206 flushLen += 1;
207 break;
208
209 }
210 }
211
212 nextX = x+fm.charsWidth(text, flushIndex,flushLen);
213
214 if (flushLen>0 && nextX>=clipStart) {
215 if (bg!=null) {
216 paintBackground(x,y, nextX-x,height, g,
217 ascent, host, bg);
218 }
219 g.setColor(fg);
220 g.drawChars(text, flushIndex, flushLen, (int)x,(int)y);
221 }
222
223 if (host.getUnderlineForToken(this)) {
224 g.setColor(fg);
225 int y2 = (int)(y+1);
226 g.drawLine(origX,y2, (int)nextX,y2);
227 }
228
229 // Don't check if it's whitespace - some TokenMakers may return types
230 // other than Token.WHITESPACE for spaces (such as Token.IDENTIFIER).
231 // This also allows us to paint tab lines for MLC's.
232 if (host.getPaintTabLines() && origX==host.getMargin().left) {// && isWhitespace()) {
233 paintTabLines(origX, (int)y, (int)nextX, g, e, host);
234 }
235
236 return nextX;
237
238 }
239
240
241}
Note: See TracBrowser for help on using the repository browser.