public float getWidthUpTo(int numChars, RSyntaxTextArea textArea, TabExpander e, float x0) { float width = x0; FontMetrics fm = textArea.getFontMetricsForTokenType(getType()); if (fm != null) { int w; int currentStart = textOffset; int endBefore = textOffset + numChars; for (int i = currentStart; i < endBefore; i++) { if (text[i] == '\t') { // Since TokenMaker implementations usually group all // adjacent whitespace into a single token, there // aren't usually any characters to compute a width // for here, so we check before calling. w = i - currentStart; if (w > 0) width += fm.charsWidth(text, currentStart, w); currentStart = i + 1; width = e.nextTabStop(width, 0); } } // Most (non-whitespace) tokens will have characters at this // point to get the widths for, so we don't check for w>0 (mini- // optimization). w = endBefore - currentStart; width += fm.charsWidth(text, currentStart, w); } return width - x0; }
public int getListOffset(RSyntaxTextArea textArea, TabExpander e, float x0, float x) { // If the coordinate in question is before this line's start, quit. if (x0 >= x) return getOffset(); float currX = x0; // x-coordinate of current char. float nextX = x0; // x-coordinate of next char. float stableX = x0; // Cached ending x-coord. of last tab or token. TokenImpl token = this; int last = getOffset(); FontMetrics fm = null; while (token != null && token.isPaintable()) { fm = textArea.getFontMetricsForTokenType(token.getType()); char[] text = token.text; int start = token.textOffset; int end = start + token.textCount; for (int i = start; i < end; i++) { currX = nextX; if (text[i] == '\t') { nextX = e.nextTabStop(nextX, 0); stableX = nextX; // Cache ending x-coord. of tab. start = i + 1; // Do charsWidth() from next char. } else { nextX = stableX + fm.charsWidth(text, start, i - start + 1); } if (x >= currX && x < nextX) { if ((x - currX) < (nextX - x)) { return last + i - token.textOffset; } return last + i + 1 - token.textOffset; } } stableX = nextX; // Cache ending x-coordinate of token. last += token.textCount; token = (TokenImpl) token.getNextToken(); } // If we didn't find anything, return the end position of the text. return last; }
public int getOffsetBeforeX( RSyntaxTextArea textArea, TabExpander e, float startX, float endBeforeX) { FontMetrics fm = textArea.getFontMetricsForTokenType(getType()); int i = textOffset; int stop = i + textCount; float x = startX; while (i < stop) { if (text[i] == '\t') x = e.nextTabStop(x, 0); else x += fm.charWidth(text[i]); if (x > endBeforeX) { // If not even the first character fits into the space, go // ahead and say the first char does fit so we don't go into // an infinite loop. int intoToken = Math.max(i - textOffset, 1); return getOffset() + intoToken; } i++; } // If we got here, the whole token fit in (endBeforeX-startX) pixels. return getOffset() + textCount - 1; }
/** * Paints this token, using special symbols for whitespace characters. * * @param g The graphics context in which to paint. * @param x The x-coordinate at which to paint. * @param y The y-coordinate at which to paint. * @param host The text area this token is in. * @param e How to expand tabs. * @param clipStart The left boundary of the clip rectangle in which we're painting. This * optimizes painting by allowing us to not paint not paint when this token is "to the left" * of the clip rectangle. * @return The x-coordinate representing the end of the painted text. */ public final float paint( Graphics2D g, float x, float y, RSyntaxTextArea host, TabExpander e, float clipStart) { int origX = (int) x; int end = textOffset + textCount; float nextX = x; int flushLen = 0; int flushIndex = textOffset; Color fg = host.getForegroundForToken(this); Color bg = host.getBackgroundForTokenType(type); g.setFont(host.getFontForTokenType(type)); FontMetrics fm = host.getFontMetricsForTokenType(type); int ascent = fm.getAscent(); int height = fm.getHeight(); for (int i = textOffset; i < end; i++) { switch (text[i]) { case '\t': // Fill in background. nextX = x + fm.charsWidth(text, flushIndex, flushLen); float nextNextX = e.nextTabStop(nextX, 0); if (bg != null) { paintBackground(x, y, nextNextX - x, height, g, ascent, host, bg); } g.setColor(fg); // Paint chars cached before the tab. if (flushLen > 0) { g.drawChars(text, flushIndex, flushLen, (int) x, (int) y); flushLen = 0; } flushIndex = i + 1; // Draw an arrow representing the tab. int halfHeight = height / 2; int quarterHeight = halfHeight / 2; int ymid = (int) y - ascent + halfHeight; g.drawLine((int) nextX, ymid, (int) nextNextX, ymid); g.drawLine((int) nextNextX, ymid, (int) nextNextX - 4, ymid - quarterHeight); g.drawLine((int) nextNextX, ymid, (int) nextNextX - 4, ymid + quarterHeight); x = nextNextX; break; case ' ': // NOTE: There is a little bit of a "fudge factor" // here when "smooth text" is enabled, as "width" // below may well not be the width given to the space // by fm.charsWidth() (it depends on how it places the // space with respect to the preceding character). // But, we assume the approximation is close enough for // our drawing a dot for the space. // "flushLen+1" ensures text is aligned correctly (or, // aligned the same as in getWidth()). nextX = x + fm.charsWidth(text, flushIndex, flushLen + 1); int width = fm.charWidth(' '); // Paint background. if (bg != null) { paintBackground(x, y, nextX - x, height, g, ascent, host, bg); } g.setColor(fg); // Paint chars before space. if (flushLen > 0) { g.drawChars(text, flushIndex, flushLen, (int) x, (int) y); flushLen = 0; } // Paint a dot representing the space. dotRect.x = nextX - width / 2.0f; // "2.0f" for FindBugs dotRect.y = y - ascent + height / 2.0f; // Ditto g.fill(dotRect); flushIndex = i + 1; x = nextX; break; case '\f': // ??? // fall-through for now. default: flushLen += 1; break; } } nextX = x + fm.charsWidth(text, flushIndex, flushLen); if (flushLen > 0 && nextX >= clipStart) { if (bg != null) { paintBackground(x, y, nextX - x, height, g, ascent, host, bg); } g.setColor(fg); g.drawChars(text, flushIndex, flushLen, (int) x, (int) y); } if (host.getUnderlineForToken(this)) { g.setColor(fg); int y2 = (int) (y + 1); g.drawLine(origX, y2, (int) nextX, y2); } // Don't check if it's whitespace - some TokenMakers may return types // other than Token.WHITESPACE for spaces (such as Token.IDENTIFIER). // This also allows us to paint tab lines for MLC's. if (host.getPaintTabLines() && origX == host.getMargin().left) { // && isWhitespace()) { paintTabLines(origX, (int) y, (int) nextX, g, e, host); } return nextX; }