/* * Copyright (c) 1999, 2010, Oracle and/or its affiliates. All rights reserved. * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER. * * This code is free software; you can redistribute it and/or modify it * under the terms of the GNU General Public License version 2 only, as * published by the Free Software Foundation. Oracle designates this * particular file as subject to the "Classpath" exception as provided * by Oracle in the LICENSE file that accompanied this code. * * This code is distributed in the hope that it will be useful, but WITHOUT * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License * version 2 for more details (a copy is included in the LICENSE file that * accompanied this code). * * You should have received a copy of the GNU General Public License version * 2 along with this work; if not, write to the Free Software Foundation, * Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA. * * Please contact Oracle, 500 Oracle Parkway, Redwood Shores, CA 94065 USA * or visit www.oracle.com if you need additional information or have any * questions. */package javax.swing.text; import java.awt.*;import java.text.BreakIterator;import javax.swing.event.*;import java.util.BitSet;import java.util.Locale; import javax.swing.UIManager;import sun.swing.SwingUtilities2;import static sun.swing.SwingUtilities2.IMPLIED_CR; /** * A GlyphView is a styled chunk of text that represents a view * mapped over an element in the text model. This view is generally * responsible for displaying text glyphs using character level * attributes in some way. * An implementation of the GlyphPainter class is used to do the * actual rendering and model/view translations. This separates * rendering from layout and management of the association with * the model. * <p> * The view supports breaking for the purpose of formatting. * The fragments produced by breaking share the view that has * primary responsibility for the element (i.e. they are nested * classes and carry only a small amount of state of their own) * so they can share its resources. * <p> * Since this view * represents text that may have tabs embedded in it, it implements the * <code>TabableView</code> interface. Tabs will only be * expanded if this view is embedded in a container that does * tab expansion. ParagraphView is an example of a container * that does tab expansion. * <p> * * @since 1.3 * * @author Timothy Prinzing */public class GlyphView extends View implements TabableView, Cloneable { /** * Constructs a new view wrapped on an element. * * @param elem the element */ public GlyphView(Element elem) { super(elem); offset = 0; length = 0; Element parent = elem.getParentElement(); AttributeSet attr = elem.getAttributes(); // if there was an implied CR impliedCR = (attr != null && attr.getAttribute(IMPLIED_CR) != null && // if this is non-empty paragraph parent != null && parent.getElementCount() > 1); skipWidth = elem.getName().equals("br"); } /** * Creates a shallow copy. This is used by the * createFragment and breakView methods. * * @return the copy */ protected final Object clone() { Object o; try { o = super.clone(); } catch (CloneNotSupportedException cnse) { o = null; } return o; } /** * Fetch the currently installed glyph painter. * If a painter has not yet been installed, and * a default was not yet needed, null is returned. */ public GlyphPainter getGlyphPainter() { return painter; } /** * Sets the painter to use for rendering glyphs. */ public void setGlyphPainter(GlyphPainter p) { painter = p; } /** * Fetch a reference to the text that occupies * the given range. This is normally used by * the GlyphPainter to determine what characters * it should render glyphs for. * * @param p0 the starting document offset >= 0 * @param p1 the ending document offset >= p0 * @return the <code>Segment</code> containing the text */ public Segment getText(int p0, int p1) { // When done with the returned Segment it should be released by // invoking: // SegmentCache.releaseSharedSegment(segment); Segment text = SegmentCache.getSharedSegment(); try { Document doc = getDocument(); doc.getText(p0, p1 - p0, text); } catch (BadLocationException bl) { throw new StateInvariantError("GlyphView: Stale view: " + bl); } return text; } /** * Fetch the background color to use to render the * glyphs. If there is no background color, null should * be returned. This is implemented to call * <code>StyledDocument.getBackground</code> if the associated * document is a styled document, otherwise it returns null. */ public Color getBackground() { Document doc = getDocument(); if (doc instanceof StyledDocument) { AttributeSet attr = getAttributes(); if (attr.isDefined(StyleConstants.Background)) { return ((StyledDocument)doc).getBackground(attr); } } return null; } /** * Fetch the foreground color to use to render the * glyphs. If there is no foreground color, null should * be returned. This is implemented to call * <code>StyledDocument.getBackground</code> if the associated * document is a StyledDocument. If the associated document * is not a StyledDocument, the associated components foreground * color is used. If there is no associated component, null * is returned. */ public Color getForeground() { Document doc = getDocument(); if (doc instanceof StyledDocument) { AttributeSet attr = getAttributes(); return ((StyledDocument)doc).getForeground(attr); } Component c = getContainer(); if (c != null) { return c.getForeground(); } return null; } /** * Fetch the font that the glyphs should be based * upon. This is implemented to call * <code>StyledDocument.getFont</code> if the associated * document is a StyledDocument. If the associated document * is not a StyledDocument, the associated components font * is used. If there is no associated component, null * is returned. */ public Font getFont() { Document doc = getDocument(); if (doc instanceof StyledDocument) { AttributeSet attr = getAttributes(); return ((StyledDocument)doc).getFont(attr); } Component c = getContainer(); if (c != null) { return c.getFont(); } return null; } /** * Determine if the glyphs should be underlined. If true, * an underline should be drawn through the baseline. */ public boolean isUnderline() { AttributeSet attr = getAttributes(); return StyleConstants.isUnderline(attr); } /** * Determine if the glyphs should have a strikethrough * line. If true, a line should be drawn through the center * of the glyphs. */ public boolean isStrikeThrough() { AttributeSet attr = getAttributes(); return StyleConstants.isStrikeThrough(attr); } /** * Determine if the glyphs should be rendered as superscript. */ public boolean isSubscript() { AttributeSet attr = getAttributes(); return StyleConstants.isSubscript(attr); } /** * Determine if the glyphs should be rendered as subscript. */ public boolean isSuperscript() { AttributeSet attr = getAttributes(); return StyleConstants.isSuperscript(attr); } /** * Fetch the TabExpander to use if tabs are present in this view. */ public TabExpander getTabExpander() { return expander; } /** * Check to see that a glyph painter exists. If a painter * doesn't exist, a default glyph painter will be installed. */ protected void checkPainter() { if (painter == null) { if (defaultPainter == null) { // the classname should probably come from a property file. String classname = "javax.swing.text.GlyphPainter1"; try { Class c; ClassLoader loader = getClass().getClassLoader(); if (loader != null) { c = loader.loadClass(classname); } else { c = Class.forName(classname); } Object o = c.newInstance(); if (o instanceof GlyphPainter) { defaultPainter = (GlyphPainter) o; } } catch (Throwable e) { throw new StateInvariantError("GlyphView: Can't load glyph painter: " + classname); } } setGlyphPainter(defaultPainter.getPainter(this, getStartOffset(), getEndOffset())); } } // --- TabableView methods -------------------------------------- /** * Determines the desired span when using the given * tab expansion implementation. * * @param x the position the view would be located * at for the purpose of tab expansion >= 0. * @param e how to expand the tabs when encountered. * @return the desired span >= 0 * @see TabableView#getTabbedSpan */ public float getTabbedSpan(float x, TabExpander e) { checkPainter(); TabExpander old = expander; expander = e; if (expander != old) { // setting expander can change horizontal span of the view, // so we have to call preferenceChanged() preferenceChanged(null, true, false); } this.x = (int) x; int p0 = getStartOffset(); int p1 = getEndOffset(); float width = painter.getSpan(this, p0, p1, expander, x); return width; } /** * Determines the span along the same axis as tab * expansion for a portion of the view. This is * intended for use by the TabExpander for cases * where the tab expansion involves aligning the * portion of text that doesn't have whitespace * relative to the tab stop. There is therefore * an assumption that the range given does not * contain tabs. * <p> * This method can be called while servicing the * getTabbedSpan or getPreferredSize. It has to * arrange for its own text buffer to make the * measurements. * * @param p0 the starting document offset >= 0 * @param p1 the ending document offset >= p0 * @return the span >= 0 */ public float getPartialSpan(int p0, int p1) { checkPainter(); float width = painter.getSpan(this, p0, p1, expander, x); return width; } // --- View methods --------------------------------------------- /** * Fetches the portion of the model that this view is responsible for. * * @return the starting offset into the model * @see View#getStartOffset */ public int getStartOffset() { Element e = getElement(); return (length > 0) ? e.getStartOffset() + offset : e.getStartOffset(); } /** * Fetches the portion of the model that this view is responsible for. * * @return the ending offset into the model * @see View#getEndOffset */ public int getEndOffset() { Element e = getElement(); return (length > 0) ? e.getStartOffset() + offset + length : e.getEndOffset(); } /** * Lazily initializes the selections field */ private void initSelections(int p0, int p1) { int viewPosCount = p1 - p0 + 1; if (selections == null || viewPosCount > selections.length) { selections = new byte[viewPosCount]; return; } for (int i = 0; i < viewPosCount; selections[i++] = 0); } /** * Renders a portion of a text style run. * * @param g the rendering surface to use * @param a the allocated region to render into */ public void paint(Graphics g, Shape a) { checkPainter(); boolean paintedText = false; Component c = getContainer(); int p0 = getStartOffset(); int p1 = getEndOffset(); Rectangle alloc = (a instanceof Rectangle) ? (Rectangle)a : a.getBounds(); Color bg = getBackground(); Color fg = getForeground(); if (c != null && ! c.isEnabled()) { fg = (c instanceof JTextComponent ? ((JTextComponent)c).getDisabledTextColor() : UIManager.getColor("textInactiveText")); } if (bg != null) { g.setColor(bg); g.fillRect(alloc.x, alloc.y, alloc.width, alloc.height); } if (c instanceof JTextComponent) { JTextComponent tc = (JTextComponent) c; Highlighter h = tc.getHighlighter(); if (h instanceof LayeredHighlighter) { ((LayeredHighlighter)h).paintLayeredHighlights (g, p0, p1, a, tc, this); } } if (Utilities.isComposedTextElement(getElement())) { Utilities.paintComposedText(g, a.getBounds(), this); paintedText = true; } else if(c instanceof JTextComponent) { JTextComponent tc = (JTextComponent) c; Color selFG = tc.getSelectedTextColor(); if (// there's a highlighter (bug 4532590), and (tc.getHighlighter() != null) && // selected text color is different from regular foreground (selFG != null) && !selFG.equals(fg)) { Highlighter.Highlight[] h = tc.getHighlighter().getHighlights(); if(h.length != 0) { boolean initialized = false; int viewSelectionCount = 0; for (int i = 0; i < h.length; i++) { Highlighter.Highlight highlight = h[i]; int hStart = highlight.getStartOffset(); int hEnd = highlight.getEndOffset(); if (hStart > p1 || hEnd < p0) { // the selection is out of this view continue; } if (!SwingUtilities2.useSelectedTextColor(highlight, tc)) { continue; } if (hStart <= p0 && hEnd >= p1){ // the whole view is selected paintTextUsingColor(g, a, selFG, p0, p1); paintedText = true; break; } // the array is lazily created only when the view // is partially selected if (!initialized) { initSelections(p0, p1); initialized = true; } hStart = Math.max(p0, hStart); hEnd = Math.min(p1, hEnd); paintTextUsingColor(g, a, selFG, hStart, hEnd); // the array represents view positions [0, p1-p0+1] // later will iterate this array and sum its // elements. Positions with sum == 0 are not selected. selections[hStart-p0]++; selections[hEnd-p0]--; viewSelectionCount++; } if (!paintedText && viewSelectionCount > 0) { // the view is partially selected int curPos = -1; int startPos = 0; int viewLen = p1 - p0; while (curPos++ < viewLen) { // searching for the next selection start while(curPos < viewLen && selections[curPos] == 0) curPos++; if (startPos != curPos) { // paint unselected text paintTextUsingColor(g, a, fg, p0 + startPos, p0 + curPos); } int checkSum = 0; // searching for next start position of unselected text while (curPos < viewLen && (checkSum += selections[curPos]) != 0) curPos++; startPos = curPos; } paintedText = true; } } } } if(!paintedText) paintTextUsingColor(g, a, fg, p0, p1); } /** * Paints the specified region of text in the specified color. */ final void paintTextUsingColor(Graphics g, Shape a, Color c, int p0, int p1) { // render the glyphs g.setColor(c); painter.paint(this, g, a, p0, p1); // render underline or strikethrough if set. boolean underline = isUnderline(); boolean strike = isStrikeThrough(); if (underline || strike) { // calculate x coordinates Rectangle alloc = (a instanceof Rectangle) ? (Rectangle)a : a.getBounds(); View parent = getParent(); if ((parent != null) && (parent.getEndOffset() == p1)) {Lines 1–500 of 1335. Load more
/* * Copyright (c) 1999, 2010, Oracle and/or its affiliates. All rights reserved. * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER. * * This code is free software; you can redistribute it and/or modify it * under the terms of the GNU General Public License version 2 only, as * published by the Free Software Foundation. Oracle designates this * particular file as subject to the "Classpath" exception as provided * by Oracle in the LICENSE file that accompanied this code. * * This code is distributed in the hope that it will be useful, but WITHOUT * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License * version 2 for more details (a copy is included in the LICENSE file that * accompanied this code). * * You should have received a copy of the GNU General Public License version * 2 along with this work; if not, write to the Free Software Foundation, * Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA. * * Please contact Oracle, 500 Oracle Parkway, Redwood Shores, CA 94065 USA * or visit www.oracle.com if you need additional information or have any * questions. */ package javax.swing.text; import java.awt.*; import java.text.BreakIterator; import javax.swing.event.*; import java.util.BitSet; import java.util.Locale; import javax.swing.UIManager; import sun.swing.SwingUtilities2; import static sun.swing.SwingUtilities2.IMPLIED_CR; /** * A GlyphView is a styled chunk of text that represents a view * mapped over an element in the text model. This view is generally * responsible for displaying text glyphs using character level * attributes in some way. * An implementation of the GlyphPainter class is used to do the * actual rendering and model/view translations. This separates * rendering from layout and management of the association with * the model. * <p> * The view supports breaking for the purpose of formatting. * The fragments produced by breaking share the view that has * primary responsibility for the element (i.e. they are nested * classes and carry only a small amount of state of their own) * so they can share its resources. * <p> * Since this view * represents text that may have tabs embedded in it, it implements the * <code>TabableView</code> interface. Tabs will only be * expanded if this view is embedded in a container that does * tab expansion. ParagraphView is an example of a container * that does tab expansion. * <p> * * @since 1.3 * * @author Timothy Prinzing */ public class GlyphView extends View implements TabableView, Cloneable { /** * Constructs a new view wrapped on an element. * * @param elem the element */ public GlyphView(Element elem) { super(elem); offset = 0; length = 0; Element parent = elem.getParentElement(); AttributeSet attr = elem.getAttributes(); // if there was an implied CR impliedCR = (attr != null && attr.getAttribute(IMPLIED_CR) != null && // if this is non-empty paragraph parent != null && parent.getElementCount() > 1); skipWidth = elem.getName().equals("br"); } /** * Creates a shallow copy. This is used by the * createFragment and breakView methods. * * @return the copy */ protected final Object clone() { Object o; try { o = super.clone(); } catch (CloneNotSupportedException cnse) { o = null; } return o; } /** * Fetch the currently installed glyph painter. * If a painter has not yet been installed, and * a default was not yet needed, null is returned. */ public GlyphPainter getGlyphPainter() { return painter; } /** * Sets the painter to use for rendering glyphs. */ public void setGlyphPainter(GlyphPainter p) { painter = p; } /** * Fetch a reference to the text that occupies * the given range. This is normally used by * the GlyphPainter to determine what characters * it should render glyphs for. * * @param p0 the starting document offset >= 0 * @param p1 the ending document offset >= p0 * @return the <code>Segment</code> containing the text */ public Segment getText(int p0, int p1) { // When done with the returned Segment it should be released by // invoking: // SegmentCache.releaseSharedSegment(segment); Segment text = SegmentCache.getSharedSegment(); try { Document doc = getDocument(); doc.getText(p0, p1 - p0, text); } catch (BadLocationException bl) { throw new StateInvariantError("GlyphView: Stale view: " + bl); } return text; } /** * Fetch the background color to use to render the * glyphs. If there is no background color, null should * be returned. This is implemented to call * <code>StyledDocument.getBackground</code> if the associated * document is a styled document, otherwise it returns null. */ public Color getBackground() { Document doc = getDocument(); if (doc instanceof StyledDocument) { AttributeSet attr = getAttributes(); if (attr.isDefined(StyleConstants.Background)) { return ((StyledDocument)doc).getBackground(attr); } } return null; } /** * Fetch the foreground color to use to render the * glyphs. If there is no foreground color, null should * be returned. This is implemented to call * <code>StyledDocument.getBackground</code> if the associated * document is a StyledDocument. If the associated document * is not a StyledDocument, the associated components foreground * color is used. If there is no associated component, null * is returned. */ public Color getForeground() { Document doc = getDocument(); if (doc instanceof StyledDocument) { AttributeSet attr = getAttributes(); return ((StyledDocument)doc).getForeground(attr); } Component c = getContainer(); if (c != null) { return c.getForeground(); } return null; } /** * Fetch the font that the glyphs should be based * upon. This is implemented to call * <code>StyledDocument.getFont</code> if the associated * document is a StyledDocument. If the associated document * is not a StyledDocument, the associated components font * is used. If there is no associated component, null * is returned. */ public Font getFont() { Document doc = getDocument(); if (doc instanceof StyledDocument) { AttributeSet attr = getAttributes(); return ((StyledDocument)doc).getFont(attr); } Component c = getContainer(); if (c != null) { return c.getFont(); } return null; } /** * Determine if the glyphs should be underlined. If true, * an underline should be drawn through the baseline. */ public boolean isUnderline() { AttributeSet attr = getAttributes(); return StyleConstants.isUnderline(attr); } /** * Determine if the glyphs should have a strikethrough * line. If true, a line should be drawn through the center * of the glyphs. */ public boolean isStrikeThrough() { AttributeSet attr = getAttributes(); return StyleConstants.isStrikeThrough(attr); } /** * Determine if the glyphs should be rendered as superscript. */ public boolean isSubscript() { AttributeSet attr = getAttributes(); return StyleConstants.isSubscript(attr); } /** * Determine if the glyphs should be rendered as subscript. */ public boolean isSuperscript() { AttributeSet attr = getAttributes(); return StyleConstants.isSuperscript(attr); } /** * Fetch the TabExpander to use if tabs are present in this view. */ public TabExpander getTabExpander() { return expander; } /** * Check to see that a glyph painter exists. If a painter * doesn't exist, a default glyph painter will be installed. */ protected void checkPainter() { if (painter == null) { if (defaultPainter == null) { // the classname should probably come from a property file. String classname = "javax.swing.text.GlyphPainter1"; try { Class c; ClassLoader loader = getClass().getClassLoader(); if (loader != null) { c = loader.loadClass(classname); } else { c = Class.forName(classname); } Object o = c.newInstance(); if (o instanceof GlyphPainter) { defaultPainter = (GlyphPainter) o; } } catch (Throwable e) { throw new StateInvariantError("GlyphView: Can't load glyph painter: " + classname); } } setGlyphPainter(defaultPainter.getPainter(this, getStartOffset(), getEndOffset())); } } // --- TabableView methods -------------------------------------- /** * Determines the desired span when using the given * tab expansion implementation. * * @param x the position the view would be located * at for the purpose of tab expansion >= 0. * @param e how to expand the tabs when encountered. * @return the desired span >= 0 * @see TabableView#getTabbedSpan */ public float getTabbedSpan(float x, TabExpander e) { checkPainter(); TabExpander old = expander; expander = e; if (expander != old) { // setting expander can change horizontal span of the view, // so we have to call preferenceChanged() preferenceChanged(null, true, false); } this.x = (int) x; int p0 = getStartOffset(); int p1 = getEndOffset(); float width = painter.getSpan(this, p0, p1, expander, x); return width; } /** * Determines the span along the same axis as tab * expansion for a portion of the view. This is * intended for use by the TabExpander for cases * where the tab expansion involves aligning the * portion of text that doesn't have whitespace * relative to the tab stop. There is therefore * an assumption that the range given does not * contain tabs. * <p> * This method can be called while servicing the * getTabbedSpan or getPreferredSize. It has to * arrange for its own text buffer to make the * measurements. * * @param p0 the starting document offset >= 0 * @param p1 the ending document offset >= p0 * @return the span >= 0 */ public float getPartialSpan(int p0, int p1) { checkPainter(); float width = painter.getSpan(this, p0, p1, expander, x); return width; } // --- View methods --------------------------------------------- /** * Fetches the portion of the model that this view is responsible for. * * @return the starting offset into the model * @see View#getStartOffset */ public int getStartOffset() { Element e = getElement(); return (length > 0) ? e.getStartOffset() + offset : e.getStartOffset(); } /** * Fetches the portion of the model that this view is responsible for. * * @return the ending offset into the model * @see View#getEndOffset */ public int getEndOffset() { Element e = getElement(); return (length > 0) ? e.getStartOffset() + offset + length : e.getEndOffset(); } /** * Lazily initializes the selections field */ private void initSelections(int p0, int p1) { int viewPosCount = p1 - p0 + 1; if (selections == null || viewPosCount > selections.length) { selections = new byte[viewPosCount]; return; } for (int i = 0; i < viewPosCount; selections[i++] = 0); } /** * Renders a portion of a text style run. * * @param g the rendering surface to use * @param a the allocated region to render into */ public void paint(Graphics g, Shape a) { checkPainter(); boolean paintedText = false; Component c = getContainer(); int p0 = getStartOffset(); int p1 = getEndOffset(); Rectangle alloc = (a instanceof Rectangle) ? (Rectangle)a : a.getBounds(); Color bg = getBackground(); Color fg = getForeground(); if (c != null && ! c.isEnabled()) { fg = (c instanceof JTextComponent ? ((JTextComponent)c).getDisabledTextColor() : UIManager.getColor("textInactiveText")); } if (bg != null) { g.setColor(bg); g.fillRect(alloc.x, alloc.y, alloc.width, alloc.height); } if (c instanceof JTextComponent) { JTextComponent tc = (JTextComponent) c; Highlighter h = tc.getHighlighter(); if (h instanceof LayeredHighlighter) { ((LayeredHighlighter)h).paintLayeredHighlights (g, p0, p1, a, tc, this); } } if (Utilities.isComposedTextElement(getElement())) { Utilities.paintComposedText(g, a.getBounds(), this); paintedText = true; } else if(c instanceof JTextComponent) { JTextComponent tc = (JTextComponent) c; Color selFG = tc.getSelectedTextColor(); if (// there's a highlighter (bug 4532590), and (tc.getHighlighter() != null) && // selected text color is different from regular foreground (selFG != null) && !selFG.equals(fg)) { Highlighter.Highlight[] h = tc.getHighlighter().getHighlights(); if(h.length != 0) { boolean initialized = false; int viewSelectionCount = 0; for (int i = 0; i < h.length; i++) { Highlighter.Highlight highlight = h[i]; int hStart = highlight.getStartOffset(); int hEnd = highlight.getEndOffset(); if (hStart > p1 || hEnd < p0) { // the selection is out of this view continue; } if (!SwingUtilities2.useSelectedTextColor(highlight, tc)) { continue; } if (hStart <= p0 && hEnd >= p1){ // the whole view is selected paintTextUsingColor(g, a, selFG, p0, p1); paintedText = true; break; } // the array is lazily created only when the view // is partially selected if (!initialized) { initSelections(p0, p1); initialized = true; } hStart = Math.max(p0, hStart); hEnd = Math.min(p1, hEnd); paintTextUsingColor(g, a, selFG, hStart, hEnd); // the array represents view positions [0, p1-p0+1] // later will iterate this array and sum its // elements. Positions with sum == 0 are not selected. selections[hStart-p0]++; selections[hEnd-p0]--; viewSelectionCount++; } if (!paintedText && viewSelectionCount > 0) { // the view is partially selected int curPos = -1; int startPos = 0; int viewLen = p1 - p0; while (curPos++ < viewLen) { // searching for the next selection start while(curPos < viewLen && selections[curPos] == 0) curPos++; if (startPos != curPos) { // paint unselected text paintTextUsingColor(g, a, fg, p0 + startPos, p0 + curPos); } int checkSum = 0; // searching for next start position of unselected text while (curPos < viewLen && (checkSum += selections[curPos]) != 0) curPos++; startPos = curPos; } paintedText = true; } } } } if(!paintedText) paintTextUsingColor(g, a, fg, p0, p1); } /** * Paints the specified region of text in the specified color. */ final void paintTextUsingColor(Graphics g, Shape a, Color c, int p0, int p1) { // render the glyphs g.setColor(c); painter.paint(this, g, a, p0, p1); // render underline or strikethrough if set. boolean underline = isUnderline(); boolean strike = isStrikeThrough(); if (underline || strike) { // calculate x coordinates Rectangle alloc = (a instanceof Rectangle) ? (Rectangle)a : a.getBounds(); View parent = getParent(); if ((parent != null) && (parent.getEndOffset() == p1)) {