4
* VisibleWhitespaceTokenPainter - Renders tokens in an instance of
5
* RSyntaxTextArea, with special glyphs to denote spaces and tabs.
7
* This library is distributed under a modified BSD license. See the included
8
* RSyntaxTextArea.License.txt file for details.
10
package org.fife.ui.rsyntaxtextarea;
12
import java.awt.Color;
13
import java.awt.FontMetrics;
14
import java.awt.Graphics2D;
15
import javax.swing.text.TabExpander;
19
* A token painter that visibly renders whitespace (spaces and tabs).<p>
21
* The current implementation paints as follows:
23
* <li>The first tab or space, if any, is found in the token.</li>
24
* <li>If a tab was found, all characters up to it are painted as a
26
* <li>If a space was found, all characters up to and including it are
27
* painted (it is painted with a special symbol to denote it as
29
* <li>If neither a tab nor a whitespace was found, all characters in the
30
* token are painted.</li>
31
* <li>Repeat until all characters are painted.</li>
33
* This means that rendering hints are applied to all groups of characters
34
* within a token, excluding whitespace and tabs.<p>
36
* A problem with this implementation is that FontMetrics.charsWidth() is still
37
* used to calculate the width of a group of chars painted. Thus, the group of
38
* characters will be painted with the rendering hints specified, but the
39
* following tab (or group of characters if the current group was the end of a
40
* token) will not necessarily be painted at the proper x-coordinate (as
41
* FontMetrics.charsWidth() returns an <code>int</code> and not a
42
* <code>float</code>). The way around this would be to calculate the token's
43
* width in such a way that a float is returned (Font.getStringBounds()?).
45
* @author Robert Futrell
48
class VisibleWhitespaceTokenPainter extends DefaultTokenPainter {
55
protected float paintImpl(Token token, Graphics2D g, float x, float y,
56
RSyntaxTextArea host, TabExpander e, float clipStart,
60
int textOffs = token.getTextOffset();
61
char[] text = token.getTextArray();
62
int end = token.getEndOffset();
65
int flushIndex = textOffs;
68
fg = host.getSelectedTextColor();
72
fg = host.getForegroundForToken(token);
73
bg = host.getBackgroundForToken(token);
75
g.setFont(host.getFontForTokenType(token.getType()));
76
FontMetrics fm = host.getFontMetricsForTokenType(token.getType());
78
int ascent = fm.getAscent();
79
int height = fm.getHeight();
81
for (int i=textOffs; i<end; i++) {
87
// Fill in background.
88
nextX = x+fm.charsWidth(text, flushIndex,flushLen);
89
float nextNextX = e.nextTabStop(nextX, 0);
91
paintBackground(x,y, nextNextX-x,height, g,
92
ascent, host, bg, !selected);
96
// Paint chars cached before the tab.
98
g.drawChars(text, flushIndex, flushLen, (int)x,(int)y);
103
// Draw an arrow representing the tab.
104
int halfHeight = height / 2;
105
int quarterHeight = halfHeight / 2;
106
int ymid = (int)y - ascent + halfHeight;
107
g.drawLine((int)nextX,ymid, (int)nextNextX,ymid);
108
g.drawLine((int)nextNextX,ymid, (int)nextNextX-4,ymid-quarterHeight);
109
g.drawLine((int)nextNextX,ymid, (int)nextNextX-4,ymid+quarterHeight);
116
// NOTE: There is a little bit of a "fudge factor"
117
// here when "smooth text" is enabled, as "width"
118
// below may well not be the width given to the space
119
// by fm.charsWidth() (it depends on how it places the
120
// space with respect to the preceding character).
121
// But, we assume the approximation is close enough for
122
// our drawing a dot for the space.
124
// "flushLen+1" ensures text is aligned correctly (or,
125
// aligned the same as in getWidth()).
126
nextX = x+fm.charsWidth(text, flushIndex,flushLen+1);
127
int width = fm.charWidth(' ');
131
paintBackground(x,y, nextX-x,height, g,
132
ascent, host, bg, !selected);
136
// Paint chars before space.
138
g.drawChars(text, flushIndex, flushLen, (int)x,(int)y);
142
// Paint a dot representing the space.
143
int dotX = (int)(nextX - width/2f); // "2.0f" for FindBugs
144
int dotY = (int)(y - ascent + height/2f); // Ditto
145
g.drawLine(dotX, dotY, dotX, dotY);
153
// fall-through for now.
162
nextX = x+fm.charsWidth(text, flushIndex,flushLen);
164
if (flushLen>0 && nextX>=clipStart) {
166
paintBackground(x,y, nextX-x,height, g,
167
ascent, host, bg, !selected);
170
g.drawChars(text, flushIndex, flushLen, (int)x,(int)y);
173
if (host.getUnderlineForToken(token)) {
176
g.drawLine(origX,y2, (int)nextX,y2);
179
// Don't check if it's whitespace - some TokenMakers may return types
180
// other than Token.WHITESPACE for spaces (such as Token.IDENTIFIER).
181
// This also allows us to paint tab lines for MLC's.
182
if (host.getPaintTabLines() && origX==host.getMargin().left) {// && isWhitespace()) {
183
paintTabLines(token, origX, (int)y, (int)nextX, g, e, host);
b'\\ No newline at end of file'