From 554fd8c5195424bdbcabf5de30fdc183aba391bd Mon Sep 17 00:00:00 2001 From: upstream source tree Date: Sun, 15 Mar 2015 20:14:05 -0400 Subject: obtained gcc-4.6.4.tar.bz2 from upstream website; verified gcc-4.6.4.tar.bz2.sig; imported gcc-4.6.4 source tree from verified upstream tarball. downloading a git-generated archive based on the 'upstream' tag should provide you with a source tree that is binary identical to the one extracted from the above tarball. if you have obtained the source via the command 'git clone', however, do note that line-endings of files in your working directory might differ from line-endings of the respective files in the upstream repository. --- libjava/classpath/javax/swing/text/GlyphView.java | 1333 +++++++++++++++++++++ 1 file changed, 1333 insertions(+) create mode 100644 libjava/classpath/javax/swing/text/GlyphView.java (limited to 'libjava/classpath/javax/swing/text/GlyphView.java') diff --git a/libjava/classpath/javax/swing/text/GlyphView.java b/libjava/classpath/javax/swing/text/GlyphView.java new file mode 100644 index 000000000..3f4ccf9c2 --- /dev/null +++ b/libjava/classpath/javax/swing/text/GlyphView.java @@ -0,0 +1,1333 @@ +/* GlyphView.java -- A view to render styled text + Copyright (C) 2005 Free Software Foundation, Inc. + +This file is part of GNU Classpath. + +GNU Classpath is free software; you can redistribute it and/or modify +it under the terms of the GNU General Public License as published by +the Free Software Foundation; either version 2, or (at your option) +any later version. + +GNU Classpath 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 for more details. + +You should have received a copy of the GNU General Public License +along with GNU Classpath; see the file COPYING. If not, write to the +Free Software Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA +02110-1301 USA. + +Linking this library statically or dynamically with other modules is +making a combined work based on this library. Thus, the terms and +conditions of the GNU General Public License cover the whole +combination. + +As a special exception, the copyright holders of this library give you +permission to link this library with independent modules to produce an +executable, regardless of the license terms of these independent +modules, and to copy and distribute the resulting executable under +terms of your choice, provided that you also meet, for each linked +independent module, the terms and conditions of the license of that +module. An independent module is a module which is not derived from +or based on this library. If you modify this library, you may extend +this exception to your version of the library, but you are not +obligated to do so. If you do not wish to do so, delete this +exception statement from your version. */ + + +package javax.swing.text; + +import gnu.classpath.SystemProperties; + +import java.awt.Color; +import java.awt.Container; +import java.awt.Font; +import java.awt.FontMetrics; +import java.awt.Graphics; +import java.awt.Graphics2D; +import java.awt.Rectangle; +import java.awt.Shape; +import java.awt.Toolkit; +import java.awt.font.FontRenderContext; +import java.awt.font.TextHitInfo; +import java.awt.font.TextLayout; +import java.awt.geom.Rectangle2D; + +import javax.swing.SwingConstants; +import javax.swing.event.DocumentEvent; +import javax.swing.text.Position.Bias; + +/** + * Renders a run of styled text. This {@link View} subclass paints the + * characters of the Element it is responsible for using + * the style information from that Element. + * + * @author Roman Kennke (roman@kennke.org) + */ +public class GlyphView extends View implements TabableView, Cloneable +{ + + /** + * An abstract base implementation for a glyph painter for + * GlyphView. + */ + public abstract static class GlyphPainter + { + /** + * Creates a new GlyphPainer. + */ + public GlyphPainter() + { + // Nothing to do here. + } + + /** + * Returns the ascent of the font that is used by this glyph painter. + * + * @param v the glyph view + * + * @return the ascent of the font that is used by this glyph painter + */ + public abstract float getAscent(GlyphView v); + + /** + * Returns the descent of the font that is used by this glyph painter. + * + * @param v the glyph view + * + * @return the descent of the font that is used by this glyph painter + */ + public abstract float getDescent(GlyphView v); + + /** + * Returns the full height of the rendered text. + * + * @return the full height of the rendered text + */ + public abstract float getHeight(GlyphView view); + + /** + * Determines the model offset, so that the text between p0 + * and this offset fits within the span starting at x with + * the length of len. + * + * @param v the glyph view + * @param p0 the starting offset in the model + * @param x the start location in the view + * @param len the length of the span in the view + */ + public abstract int getBoundedPosition(GlyphView v, int p0, float x, + float len); + + /** + * Paints the glyphs. + * + * @param view the glyph view to paint + * @param g the graphics context to use for painting + * @param a the allocation of the glyph view + * @param p0 the start position (in the model) from which to paint + * @param p1 the end position (in the model) to which to paint + */ + public abstract void paint(GlyphView view, Graphics g, Shape a, int p0, + int p1); + + /** + * Maps a position in the document into the coordinate space of the View. + * The output rectangle usually reflects the font height but has a width + * of zero. + * + * @param view the glyph view + * @param pos the position of the character in the model + * @param a the area that is occupied by the view + * @param b either {@link Position.Bias#Forward} or + * {@link Position.Bias#Backward} depending on the preferred + * direction bias. If null this defaults to + * Position.Bias.Forward + * + * @return a rectangle that gives the location of the document position + * inside the view coordinate space + * + * @throws BadLocationException if pos is invalid + * @throws IllegalArgumentException if b is not one of the above listed + * valid values + */ + public abstract Shape modelToView(GlyphView view, int pos, Position.Bias b, + Shape a) + throws BadLocationException; + + /** + * Maps a visual position into a document location. + * + * @param v the glyph view + * @param x the X coordinate of the visual position + * @param y the Y coordinate of the visual position + * @param a the allocated region + * @param biasRet filled with the bias of the model location on method exit + * + * @return the model location that represents the specified view location + */ + public abstract int viewToModel(GlyphView v, float x, float y, Shape a, + Position.Bias[] biasRet); + + /** + * Determine the span of the glyphs from location p0 to + * location p1. If te is not null, + * then TABs are expanded using this TabExpander. + * The parameter x is the location at which the view is + * located (this is important when using TAB expansion). + * + * @param view the glyph view + * @param p0 the starting location in the document model + * @param p1 the end location in the document model + * @param te the tab expander to use + * @param x the location at which the view is located + * + * @return the span of the glyphs from location p0 to + * location p1, possibly using TAB expansion + */ + public abstract float getSpan(GlyphView view, int p0, int p1, + TabExpander te, float x); + + + /** + * Returns the model location that should be used to place a caret when + * moving the caret through the document. + * + * @param v the glyph view + * @param pos the current model location + * @param b the bias for p + * @param a the allocated region for the glyph view + * @param direction the direction from the current position; Must be one of + * {@link SwingConstants#EAST}, {@link SwingConstants#WEST}, + * {@link SwingConstants#NORTH} or {@link SwingConstants#SOUTH} + * @param biasRet filled with the bias of the resulting location when method + * returns + * + * @return the location within the document that should be used to place the + * caret when moving the caret around the document + * + * @throws BadLocationException if pos is an invalid model + * location + * @throws IllegalArgumentException if d is invalid + */ + public int getNextVisualPositionFrom(GlyphView v, int pos, Position.Bias b, + Shape a, int direction, + Position.Bias[] biasRet) + throws BadLocationException + + { + int result = pos; + switch (direction) + { + case SwingConstants.EAST: + result = pos + 1; + break; + case SwingConstants.WEST: + result = pos - 1; + break; + case SwingConstants.NORTH: + case SwingConstants.SOUTH: + default: + // This should be handled in enclosing view, since the glyph view + // does not layout vertically. + break; + } + return result; + } + + /** + * Returns a painter that can be used to render the specified glyph view. + * If this glyph painter is stateful, then it should return a new instance. + * However, if this painter is stateless it should return itself. The + * default behaviour is to return itself. + * + * @param v the glyph view for which to create a painter + * @param p0 the start offset of the rendered area + * @param p1 the end offset of the rendered area + * + * @return a painter that can be used to render the specified glyph view + */ + public GlyphPainter getPainter(GlyphView v, int p0, int p1) + { + return this; + } + } + + /** + * A GlyphPainter implementation based on TextLayout. This should give + * better performance in Java2D environments. + */ + private static class J2DGlyphPainter + extends GlyphPainter + { + + /** + * The text layout. + */ + TextLayout textLayout; + + /** + * Creates a new J2DGlyphPainter. + * + * @param str the string + * @param font the font + * @param frc the font render context + */ + J2DGlyphPainter(String str, Font font, FontRenderContext frc) + { + textLayout = new TextLayout(str, font, frc); + } + + /** + * Returns null so that GlyphView.checkPainter() creates a new instance. + */ + public GlyphPainter getPainter(GlyphView v, int p0, int p1) + { + return null; + } + + /** + * Delegates to the text layout. + */ + public float getAscent(GlyphView v) + { + return textLayout.getAscent(); + } + + /** + * Delegates to the text layout. + */ + public int getBoundedPosition(GlyphView v, int p0, float x, float len) + { + int pos; + TextHitInfo hit = textLayout.hitTestChar(len, 0); + if (hit.getCharIndex() == -1 && ! textLayout.isLeftToRight()) + pos = v.getEndOffset(); + else + { + pos = hit.isLeadingEdge() ? hit.getInsertionIndex() + : hit.getInsertionIndex() - 1; + pos += v.getStartOffset(); + } + return pos; + } + + /** + * Delegates to the text layout. + */ + public float getDescent(GlyphView v) + { + return textLayout.getDescent(); + } + + /** + * Delegates to the text layout. + */ + public float getHeight(GlyphView view) + { + return textLayout.getAscent() + textLayout.getDescent() + + textLayout.getLeading(); + } + + /** + * Delegates to the text layout. + */ + public float getSpan(GlyphView v, int p0, int p1, TabExpander te, float x) + { + float span; + if (p0 == v.getStartOffset() && p1 == v.getEndOffset()) + span = textLayout.getAdvance(); + else + { + int start = v.getStartOffset(); + int i0 = p0 - start; + int i1 = p1 - start; + TextHitInfo hit0 = TextHitInfo.afterOffset(i0); + TextHitInfo hit1 = TextHitInfo.afterOffset(i1); + float x0 = textLayout.getCaretInfo(hit0)[0]; + float x1 = textLayout.getCaretInfo(hit1)[0]; + span = Math.abs(x1 - x0); + } + return span; + } + + /** + * Delegates to the text layout. + */ + public Shape modelToView(GlyphView v, int pos, Bias b, Shape a) + throws BadLocationException + { + int offs = pos - v.getStartOffset(); + // Create copy here to protect original shape. + Rectangle2D bounds = a.getBounds2D(); + TextHitInfo hit = + b == Position.Bias.Forward ? TextHitInfo.afterOffset(offs) + : TextHitInfo.beforeOffset(offs); + float[] loc = textLayout.getCaretInfo(hit); + bounds.setRect(bounds.getX() + loc[0], bounds.getY(), 1, + bounds.getHeight()); + return bounds; + } + + /** + * Delegates to the text layout. + */ + public void paint(GlyphView view, Graphics g, Shape a, int p0, int p1) + { + // Can't paint this with plain graphics. + if (g instanceof Graphics2D) + { + Graphics2D g2d = (Graphics2D) g; + Rectangle2D b = a instanceof Rectangle2D ? (Rectangle2D) a + : a.getBounds2D(); + float x = (float) b.getX(); + float y = (float) b.getY() + textLayout.getAscent() + + textLayout.getLeading(); + // TODO: Try if clipping makes things faster for narrow views. + textLayout.draw(g2d, x, y); + } + } + + /** + * Delegates to the text layout. + */ + public int viewToModel(GlyphView v, float x, float y, Shape a, + Bias[] biasRet) + { + Rectangle2D bounds = a instanceof Rectangle2D ? (Rectangle2D) a + : a.getBounds2D(); + TextHitInfo hit = textLayout.hitTestChar(x - (float) bounds.getX(), 0); + int pos = hit.getInsertionIndex(); + biasRet[0] = hit.isLeadingEdge() ? Position.Bias.Forward + : Position.Bias.Backward; + return pos + v.getStartOffset(); + } + + } + + /** + * The default GlyphPainter used in GlyphView. + */ + static class DefaultGlyphPainter extends GlyphPainter + { + FontMetrics fontMetrics; + + /** + * Returns the full height of the rendered text. + * + * @return the full height of the rendered text + */ + public float getHeight(GlyphView view) + { + updateFontMetrics(view); + float height = fontMetrics.getHeight(); + return height; + } + + /** + * Paints the glyphs. + * + * @param view the glyph view to paint + * @param g the graphics context to use for painting + * @param a the allocation of the glyph view + * @param p0 the start position (in the model) from which to paint + * @param p1 the end position (in the model) to which to paint + */ + public void paint(GlyphView view, Graphics g, Shape a, int p0, + int p1) + { + updateFontMetrics(view); + Rectangle r = a instanceof Rectangle ? (Rectangle) a : a.getBounds(); + TabExpander tabEx = view.getTabExpander(); + Segment txt = view.getText(p0, p1); + + // Find out the X location at which we have to paint. + int x = r.x; + int p = view.getStartOffset(); + if (p != p0) + { + int width = Utilities.getTabbedTextWidth(txt, fontMetrics,x, tabEx, + p); + x += width; + } + // Find out Y location. + int y = r.y + fontMetrics.getHeight() - fontMetrics.getDescent(); + + // Render the thing. + g.setFont(fontMetrics.getFont()); + Utilities.drawTabbedText(txt, x, y, g, tabEx, p0); + + } + + /** + * Maps a position in the document into the coordinate space of the View. + * The output rectangle usually reflects the font height but has a width + * of zero. + * + * @param view the glyph view + * @param pos the position of the character in the model + * @param a the area that is occupied by the view + * @param b either {@link Position.Bias#Forward} or + * {@link Position.Bias#Backward} depending on the preferred + * direction bias. If null this defaults to + * Position.Bias.Forward + * + * @return a rectangle that gives the location of the document position + * inside the view coordinate space + * + * @throws BadLocationException if pos is invalid + * @throws IllegalArgumentException if b is not one of the above listed + * valid values + */ + public Shape modelToView(GlyphView view, int pos, Position.Bias b, + Shape a) + throws BadLocationException + { + updateFontMetrics(view); + Element el = view.getElement(); + Segment txt = view.getText(el.getStartOffset(), pos); + Rectangle bounds = a instanceof Rectangle ? (Rectangle) a + : a.getBounds(); + TabExpander expander = view.getTabExpander(); + int width = Utilities.getTabbedTextWidth(txt, fontMetrics, bounds.x, + expander, + view.getStartOffset()); + int height = fontMetrics.getHeight(); + Rectangle result = new Rectangle(bounds.x + width, bounds.y, + 0, height); + return result; + } + + /** + * Determine the span of the glyphs from location p0 to + * location p1. If te is not null, + * then TABs are expanded using this TabExpander. + * The parameter x is the location at which the view is + * located (this is important when using TAB expansion). + * + * @param view the glyph view + * @param p0 the starting location in the document model + * @param p1 the end location in the document model + * @param te the tab expander to use + * @param x the location at which the view is located + * + * @return the span of the glyphs from location p0 to + * location p1, possibly using TAB expansion + */ + public float getSpan(GlyphView view, int p0, int p1, + TabExpander te, float x) + { + updateFontMetrics(view); + Segment txt = view.getText(p0, p1); + int span = Utilities.getTabbedTextWidth(txt, fontMetrics, (int) x, te, + p0); + return span; + } + + /** + * Returns the ascent of the text run that is rendered by this + * GlyphPainter. + * + * @param v the glyph view + * + * @return the ascent of the text run that is rendered by this + * GlyphPainter + * + * @see FontMetrics#getAscent() + */ + public float getAscent(GlyphView v) + { + updateFontMetrics(v); + return fontMetrics.getAscent(); + } + + /** + * Returns the descent of the text run that is rendered by this + * GlyphPainter. + * + * @param v the glyph view + * + * @return the descent of the text run that is rendered by this + * GlyphPainter + * + * @see FontMetrics#getDescent() + */ + public float getDescent(GlyphView v) + { + updateFontMetrics(v); + return fontMetrics.getDescent(); + } + + /** + * Determines the model offset, so that the text between p0 + * and this offset fits within the span starting at x with + * the length of len. + * + * @param v the glyph view + * @param p0 the starting offset in the model + * @param x the start location in the view + * @param len the length of the span in the view + */ + public int getBoundedPosition(GlyphView v, int p0, float x, float len) + { + updateFontMetrics(v); + TabExpander te = v.getTabExpander(); + Segment txt = v.getText(p0, v.getEndOffset()); + int pos = Utilities.getTabbedTextOffset(txt, fontMetrics, (int) x, + (int) (x + len), te, p0, false); + return pos + p0; + } + + /** + * Maps a visual position into a document location. + * + * @param v the glyph view + * @param x the X coordinate of the visual position + * @param y the Y coordinate of the visual position + * @param a the allocated region + * @param biasRet filled with the bias of the model location on method exit + * + * @return the model location that represents the specified view location + */ + public int viewToModel(GlyphView v, float x, float y, Shape a, + Bias[] biasRet) + { + Rectangle r = a instanceof Rectangle ? (Rectangle) a : a.getBounds(); + int p0 = v.getStartOffset(); + int p1 = v.getEndOffset(); + TabExpander te = v.getTabExpander(); + Segment s = v.getText(p0, p1); + int offset = Utilities.getTabbedTextOffset(s, fontMetrics, r.x, (int) x, + te, p0); + int ret = p0 + offset; + if (ret == p1) + ret--; + biasRet[0] = Position.Bias.Forward; + return ret; + } + + private void updateFontMetrics(GlyphView v) + { + Font font = v.getFont(); + if (fontMetrics == null || ! font.equals(fontMetrics.getFont())) + { + Container c = v.getContainer(); + FontMetrics fm; + if (c != null) + fm = c.getFontMetrics(font); + else + fm = Toolkit.getDefaultToolkit().getFontMetrics(font); + fontMetrics = fm; + } + } + } + + /** + * The GlyphPainer used for painting the glyphs. + */ + GlyphPainter glyphPainter; + + /** + * The start offset within the document for this view. + */ + private int offset; + + /** + * The end offset within the document for this view. + */ + private int length; + + /** + * The x location against which the tab expansion is done. + */ + private float tabX; + + /** + * The tab expander that is used in this view. + */ + private TabExpander tabExpander; + + /** + * Creates a new GlyphView for the given Element. + * + * @param element the element that is rendered by this GlyphView + */ + public GlyphView(Element element) + { + super(element); + offset = 0; + length = 0; + } + + /** + * Returns the GlyphPainter that is used by this + * GlyphView. If no GlyphPainer has been installed + * null is returned. + * + * @return the glyph painter that is used by this + * glyph view or null if no glyph painter has been + * installed + */ + public GlyphPainter getGlyphPainter() + { + return glyphPainter; + } + + /** + * Sets the {@link GlyphPainter} to be used for this GlyphView. + * + * @param painter the glyph painter to be used for this glyph view + */ + public void setGlyphPainter(GlyphPainter painter) + { + glyphPainter = painter; + } + + /** + * Checks if a GlyphPainer is installed. If this is not the + * case, a default painter is installed. + */ + protected void checkPainter() + { + if (glyphPainter == null) + { + if ("true".equals( + SystemProperties.getProperty("gnu.javax.swing.noGraphics2D"))) + { + glyphPainter = new DefaultGlyphPainter(); + } + else + { + Segment s = getText(getStartOffset(), getEndOffset()); + glyphPainter = new J2DGlyphPainter(s.toString(), getFont(), + new FontRenderContext(null, + false, + false)); + } + } + } + + /** + * Renders the Element that is associated with this + * View. + * + * @param g the Graphics context to render to + * @param a the allocated region for the Element + */ + public void paint(Graphics g, Shape a) + { + checkPainter(); + int p0 = getStartOffset(); + int p1 = getEndOffset(); + + Rectangle r = a instanceof Rectangle ? (Rectangle) a : a.getBounds(); + Container c = getContainer(); + + Color fg = getForeground(); + JTextComponent tc = null; + if (c instanceof JTextComponent) + { + tc = (JTextComponent) c; + if (! tc.isEnabled()) + fg = tc.getDisabledTextColor(); + } + Color bg = getBackground(); + if (bg != null) + { + g.setColor(bg); + g.fillRect(r.x, r.y, r.width, r.height); + } + + + // Paint layered highlights if there are any. + if (tc != null) + { + Highlighter h = tc.getHighlighter(); + if (h instanceof LayeredHighlighter) + { + LayeredHighlighter lh = (LayeredHighlighter) h; + lh.paintLayeredHighlights(g, p0, p1, a, tc, this); + } + } + + g.setColor(fg); + glyphPainter.paint(this, g, a, p0, p1); + boolean underline = isUnderline(); + boolean striked = isStrikeThrough(); + if (underline || striked) + { + View parent = getParent(); + // X coordinate. + if (parent != null && parent.getEndOffset() == p1) + { + // Strip whitespace. + Segment s = getText(p0, p1); + while (s.count > 0 && Character.isWhitespace(s.array[s.count - 1])) + { + p1--; + s.count--; + } + } + int x0 = r.x; + int p = getStartOffset(); + TabExpander tabEx = getTabExpander(); + if (p != p0) + x0 += (int) glyphPainter.getSpan(this, p, p0, tabEx, x0); + int x1 = x0 + (int) glyphPainter.getSpan(this, p0, p1, tabEx, x0); + // Y coordinate. + int y = r.y + r.height - (int) glyphPainter.getDescent(this); + if (underline) + { + int yTmp = y; + yTmp += 1; + g.drawLine(x0, yTmp, x1, yTmp); + } + if (striked) + { + int yTmp = y; + yTmp -= (int) glyphPainter.getAscent(this); + g.drawLine(x0, yTmp, x1, yTmp); + } + } + } + + + /** + * Returns the preferred span of the content managed by this + * View along the specified axis. + * + * @param axis the axis + * + * @return the preferred span of this View. + */ + public float getPreferredSpan(int axis) + { + float span = 0; + checkPainter(); + GlyphPainter painter = getGlyphPainter(); + switch (axis) + { + case X_AXIS: + TabExpander tabEx = null; + View parent = getParent(); + if (parent instanceof TabExpander) + tabEx = (TabExpander) parent; + span = painter.getSpan(this, getStartOffset(), getEndOffset(), + tabEx, 0.F); + break; + case Y_AXIS: + span = painter.getHeight(this); + if (isSuperscript()) + span += span / 3; + break; + default: + throw new IllegalArgumentException("Illegal axis"); + } + return span; + } + + /** + * Maps a position in the document into the coordinate space of the View. + * The output rectangle usually reflects the font height but has a width + * of zero. + * + * @param pos the position of the character in the model + * @param a the area that is occupied by the view + * @param b either {@link Position.Bias#Forward} or + * {@link Position.Bias#Backward} depending on the preferred + * direction bias. If null this defaults to + * Position.Bias.Forward + * + * @return a rectangle that gives the location of the document position + * inside the view coordinate space + * + * @throws BadLocationException if pos is invalid + * @throws IllegalArgumentException if b is not one of the above listed + * valid values + */ + public Shape modelToView(int pos, Shape a, Position.Bias b) + throws BadLocationException + { + GlyphPainter p = getGlyphPainter(); + return p.modelToView(this, pos, b, a); + } + + /** + * Maps coordinates from the View's space into a position + * in the document model. + * + * @param x the x coordinate in the view space + * @param y the y coordinate in the view space + * @param a the allocation of this View + * @param b the bias to use + * + * @return the position in the document that corresponds to the screen + * coordinates x, y + */ + public int viewToModel(float x, float y, Shape a, Position.Bias[] b) + { + checkPainter(); + GlyphPainter painter = getGlyphPainter(); + return painter.viewToModel(this, x, y, a, b); + } + + /** + * Return the {@link TabExpander} to use. + * + * @return the {@link TabExpander} to use + */ + public TabExpander getTabExpander() + { + return tabExpander; + } + + /** + * Returns the preferred span of this view for tab expansion. + * + * @param x the location of the view + * @param te the tab expander to use + * + * @return the preferred span of this view for tab expansion + */ + public float getTabbedSpan(float x, TabExpander te) + { + checkPainter(); + TabExpander old = tabExpander; + tabExpander = te; + if (tabExpander != old) + { + // Changing the tab expander will lead to a relayout in the X_AXIS. + preferenceChanged(null, true, false); + } + tabX = x; + return getGlyphPainter().getSpan(this, getStartOffset(), + getEndOffset(), tabExpander, x); + } + + /** + * Returns the span of a portion of the view. This is used in TAB expansion + * for fragments that don't contain TABs. + * + * @param p0 the start index + * @param p1 the end index + * + * @return the span of the specified portion of the view + */ + public float getPartialSpan(int p0, int p1) + { + checkPainter(); + return glyphPainter.getSpan(this, p0, p1, tabExpander, tabX); + } + + /** + * Returns the start offset in the document model of the portion + * of text that this view is responsible for. + * + * @return the start offset in the document model of the portion + * of text that this view is responsible for + */ + public int getStartOffset() + { + Element el = getElement(); + int offs = el.getStartOffset(); + if (length > 0) + offs += offset; + return offs; + } + + /** + * Returns the end offset in the document model of the portion + * of text that this view is responsible for. + * + * @return the end offset in the document model of the portion + * of text that this view is responsible for + */ + public int getEndOffset() + { + Element el = getElement(); + int offs; + if (length > 0) + offs = el.getStartOffset() + offset + length; + else + offs = el.getEndOffset(); + return offs; + } + + private Segment cached = new Segment(); + + /** + * Returns the text segment that this view is responsible for. + * + * @param p0 the start index in the document model + * @param p1 the end index in the document model + * + * @return the text segment that this view is responsible for + */ + public Segment getText(int p0, int p1) + { + try + { + getDocument().getText(p0, p1 - p0, cached); + } + catch (BadLocationException ex) + { + AssertionError ae; + ae = new AssertionError("BadLocationException should not be " + + "thrown here. p0 = " + p0 + ", p1 = " + p1); + ae.initCause(ex); + throw ae; + } + + return cached; + } + + /** + * Returns the font for the text run for which this GlyphView + * is responsible. + * + * @return the font for the text run for which this GlyphView + * is responsible + */ + public Font getFont() + { + Document doc = getDocument(); + Font font = null; + if (doc instanceof StyledDocument) + { + StyledDocument styledDoc = (StyledDocument) doc; + font = styledDoc.getFont(getAttributes()); + } + else + { + Container c = getContainer(); + if (c != null) + font = c.getFont(); + } + return font; + } + + /** + * Returns the foreground color which should be used to paint the text. + * This is fetched from the associated element's text attributes using + * {@link StyleConstants#getForeground}. + * + * @return the foreground color which should be used to paint the text + */ + public Color getForeground() + { + Element el = getElement(); + AttributeSet atts = el.getAttributes(); + return StyleConstants.getForeground(atts); + } + + /** + * Returns the background color which should be used to paint the text. + * This is fetched from the associated element's text attributes using + * {@link StyleConstants#getBackground}. + * + * @return the background color which should be used to paint the text + */ + public Color getBackground() + { + Element el = getElement(); + AttributeSet atts = el.getAttributes(); + // We cannot use StyleConstants.getBackground() here, because that returns + // BLACK as default (when background == null). What we need is the + // background setting of the text component instead, which is what we get + // when background == null anyway. + return (Color) atts.getAttribute(StyleConstants.Background); + } + + /** + * Determines whether the text should be rendered strike-through or not. This + * is determined using the method + * {@link StyleConstants#isStrikeThrough(AttributeSet)} on the element of + * this view. + * + * @return whether the text should be rendered strike-through or not + */ + public boolean isStrikeThrough() + { + Element el = getElement(); + AttributeSet atts = el.getAttributes(); + return StyleConstants.isStrikeThrough(atts); + } + + /** + * Determines whether the text should be rendered as subscript or not. This + * is determined using the method + * {@link StyleConstants#isSubscript(AttributeSet)} on the element of + * this view. + * + * @return whether the text should be rendered as subscript or not + */ + public boolean isSubscript() + { + Element el = getElement(); + AttributeSet atts = el.getAttributes(); + return StyleConstants.isSubscript(atts); + } + + /** + * Determines whether the text should be rendered as superscript or not. This + * is determined using the method + * {@link StyleConstants#isSuperscript(AttributeSet)} on the element of + * this view. + * + * @return whether the text should be rendered as superscript or not + */ + public boolean isSuperscript() + { + Element el = getElement(); + AttributeSet atts = el.getAttributes(); + return StyleConstants.isSuperscript(atts); + } + + /** + * Determines whether the text should be rendered as underlined or not. This + * is determined using the method + * {@link StyleConstants#isUnderline(AttributeSet)} on the element of + * this view. + * + * @return whether the text should be rendered as underlined or not + */ + public boolean isUnderline() + { + Element el = getElement(); + AttributeSet atts = el.getAttributes(); + return StyleConstants.isUnderline(atts); + } + + /** + * Creates and returns a shallow clone of this GlyphView. This is used by + * the {@link #createFragment} and {@link #breakView} methods. + * + * @return a shallow clone of this GlyphView + */ + protected final Object clone() + { + try + { + return super.clone(); + } + catch (CloneNotSupportedException ex) + { + AssertionError err = new AssertionError("CloneNotSupportedException " + + "must not be thrown here"); + err.initCause(ex); + throw err; + } + } + + /** + * Tries to break the view near the specified view span len. + * The glyph view can only be broken in the X direction. For Y direction it + * returns itself. + * + * @param axis the axis for breaking, may be {@link View#X_AXIS} or + * {@link View#Y_AXIS} + * @param p0 the model location where the fragment should start + * @param pos the view position along the axis where the fragment starts + * @param len the desired length of the fragment view + * + * @return the fragment view, or this if breaking was not + * possible + */ + public View breakView(int axis, int p0, float pos, float len) + { + View brokenView = this; + if (axis == X_AXIS) + { + checkPainter(); + int end = glyphPainter.getBoundedPosition(this, p0, pos, len); + int breakLoc = getBreakLocation(p0, end); + if (breakLoc != -1) + end = breakLoc; + if (p0 != getStartOffset() || end != getEndOffset()) + { + brokenView = createFragment(p0, end); + if (brokenView instanceof GlyphView) + ((GlyphView) brokenView).tabX = pos; + } + } + return brokenView; + } + + /** + * Determines how well the specified view location is suitable for inserting + * a line break. If axis is View.Y_AXIS, then + * this method forwards to the superclass, if axis is + * View.X_AXIS then this method returns + * {@link View#ExcellentBreakWeight} if there is a suitable break location + * (usually whitespace) within the specified view span, or + * {@link View#GoodBreakWeight} if not. + * + * @param axis the axis along which the break weight is requested + * @param pos the starting view location + * @param len the length of the span at which the view should be broken + * + * @return the break weight + */ + public int getBreakWeight(int axis, float pos, float len) + { + int weight; + if (axis == Y_AXIS) + weight = super.getBreakWeight(axis, pos, len); + else + { + checkPainter(); + int start = getStartOffset(); + int end = glyphPainter.getBoundedPosition(this, start, pos, len); + if (end == 0) + weight = BadBreakWeight; + else + { + if (getBreakLocation(start, end) != -1) + weight = ExcellentBreakWeight; + else + weight = GoodBreakWeight; + } + } + return weight; + } + + private int getBreakLocation(int start, int end) + { + int loc = -1; + Segment s = getText(start, end); + for (char c = s.last(); c != Segment.DONE && loc == -1; c = s.previous()) + { + if (Character.isWhitespace(c)) + { + loc = s.getIndex() - s.getBeginIndex() + 1 + start; + } + } + return loc; + } + + /** + * Receives notification that some text attributes have changed within the + * text fragment that this view is responsible for. This calls + * {@link View#preferenceChanged(View, boolean, boolean)} on the parent for + * both width and height. + * + * @param e the document event describing the change; not used here + * @param a the view allocation on screen; not used here + * @param vf the view factory; not used here + */ + public void changedUpdate(DocumentEvent e, Shape a, ViewFactory vf) + { + preferenceChanged(null, true, true); + } + + /** + * Receives notification that some text has been inserted within the + * text fragment that this view is responsible for. This calls + * {@link View#preferenceChanged(View, boolean, boolean)} for the + * direction in which the glyphs are rendered. + * + * @param e the document event describing the change; not used here + * @param a the view allocation on screen; not used here + * @param vf the view factory; not used here + */ + public void insertUpdate(DocumentEvent e, Shape a, ViewFactory vf) + { + preferenceChanged(null, true, false); + } + + /** + * Receives notification that some text has been removed within the + * text fragment that this view is responsible for. This calls + * {@link View#preferenceChanged(View, boolean, boolean)} on the parent for + * width. + * + * @param e the document event describing the change; not used here + * @param a the view allocation on screen; not used here + * @param vf the view factory; not used here + */ + public void removeUpdate(DocumentEvent e, Shape a, ViewFactory vf) + { + preferenceChanged(null, true, false); + } + + /** + * Creates a fragment view of this view that starts at p0 and + * ends at p1. + * + * @param p0 the start location for the fragment view + * @param p1 the end location for the fragment view + * + * @return the fragment view + */ + public View createFragment(int p0, int p1) + { + checkPainter(); + Element el = getElement(); + GlyphView fragment = (GlyphView) clone(); + fragment.offset = p0 - el.getStartOffset(); + fragment.length = p1 - p0; + fragment.glyphPainter = glyphPainter.getPainter(fragment, p0, p1); + return fragment; + } + + /** + * Returns the alignment of this view along the specified axis. For the Y + * axis this is (height - descent) / height for the used font, + * so that it is aligned along the baseline. + * For the X axis the superclass is called. + */ + public float getAlignment(int axis) + { + checkPainter(); + float align; + if (axis == Y_AXIS) + { + GlyphPainter painter = getGlyphPainter(); + float height = painter.getHeight(this); + float descent = painter.getDescent(this); + float ascent = painter.getAscent(this); + if (isSuperscript()) + align = 1.0F; + else if (isSubscript()) + align = height > 0 ? (height - (descent + (ascent / 2))) / height + : 0; + else + align = height > 0 ? (height - descent) / height : 0; + } + else + align = super.getAlignment(axis); + + return align; + } + + /** + * Returns the model location that should be used to place a caret when + * moving the caret through the document. + * + * @param pos the current model location + * @param bias the bias for p + * @param a the allocated region for the glyph view + * @param direction the direction from the current position; Must be one of + * {@link SwingConstants#EAST}, {@link SwingConstants#WEST}, + * {@link SwingConstants#NORTH} or {@link SwingConstants#SOUTH} + * @param biasRet filled with the bias of the resulting location when method + * returns + * + * @return the location within the document that should be used to place the + * caret when moving the caret around the document + * + * @throws BadLocationException if pos is an invalid model + * location + * @throws IllegalArgumentException if d is invalid + */ + public int getNextVisualPositionFrom(int pos, Position.Bias bias, Shape a, + int direction, Position.Bias[] biasRet) + throws BadLocationException + { + checkPainter(); + GlyphPainter painter = getGlyphPainter(); + return painter.getNextVisualPositionFrom(this, pos, bias, a, direction, + biasRet); + } +} -- cgit v1.2.3