1 /* GlyphView.java -- A view to render styled text
2 Copyright (C) 2005 Free Software Foundation, Inc.
4 This file is part of GNU Classpath.
6 GNU Classpath is free software; you can redistribute it and/or modify
7 it under the terms of the GNU General Public License as published by
8 the Free Software Foundation; either version 2, or (at your option)
11 GNU Classpath is distributed in the hope that it will be useful, but
12 WITHOUT ANY WARRANTY; without even the implied warranty of
13 MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
14 General Public License for more details.
16 You should have received a copy of the GNU General Public License
17 along with GNU Classpath; see the file COPYING. If not, write to the
18 Free Software Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA
21 Linking this library statically or dynamically with other modules is
22 making a combined work based on this library. Thus, the terms and
23 conditions of the GNU General Public License cover the whole
26 As a special exception, the copyright holders of this library give you
27 permission to link this library with independent modules to produce an
28 executable, regardless of the license terms of these independent
29 modules, and to copy and distribute the resulting executable under
30 terms of your choice, provided that you also meet, for each linked
31 independent module, the terms and conditions of the license of that
32 module. An independent module is a module which is not derived from
33 or based on this library. If you modify this library, you may extend
34 this exception to your version of the library, but you are not
35 obligated to do so. If you do not wish to do so, delete this
36 exception statement from your version. */
39 package javax.swing.text;
41 import gnu.classpath.SystemProperties;
43 import java.awt.Color;
44 import java.awt.Container;
46 import java.awt.FontMetrics;
47 import java.awt.Graphics;
48 import java.awt.Graphics2D;
49 import java.awt.Rectangle;
50 import java.awt.Shape;
51 import java.awt.Toolkit;
52 import java.awt.font.FontRenderContext;
53 import java.awt.font.TextHitInfo;
54 import java.awt.font.TextLayout;
55 import java.awt.geom.Rectangle2D;
57 import javax.swing.SwingConstants;
58 import javax.swing.event.DocumentEvent;
59 import javax.swing.text.Position.Bias;
62 * Renders a run of styled text. This {@link View} subclass paints the
63 * characters of the <code>Element</code> it is responsible for using
64 * the style information from that <code>Element</code>.
66 * @author Roman Kennke (roman@kennke.org)
68 public class GlyphView extends View implements TabableView, Cloneable
72 * An abstract base implementation for a glyph painter for
73 * <code>GlyphView</code>.
75 public abstract static class GlyphPainter
78 * Creates a new <code>GlyphPainer</code>.
82 // Nothing to do here.
86 * Returns the ascent of the font that is used by this glyph painter.
88 * @param v the glyph view
90 * @return the ascent of the font that is used by this glyph painter
92 public abstract float getAscent(GlyphView v);
95 * Returns the descent of the font that is used by this glyph painter.
97 * @param v the glyph view
99 * @return the descent of the font that is used by this glyph painter
101 public abstract float getDescent(GlyphView v);
104 * Returns the full height of the rendered text.
106 * @return the full height of the rendered text
108 public abstract float getHeight(GlyphView view);
111 * Determines the model offset, so that the text between <code>p0</code>
112 * and this offset fits within the span starting at <code>x</code> with
113 * the length of <code>len</code>.
115 * @param v the glyph view
116 * @param p0 the starting offset in the model
117 * @param x the start location in the view
118 * @param len the length of the span in the view
120 public abstract int getBoundedPosition(GlyphView v, int p0, float x,
126 * @param view the glyph view to paint
127 * @param g the graphics context to use for painting
128 * @param a the allocation of the glyph view
129 * @param p0 the start position (in the model) from which to paint
130 * @param p1 the end position (in the model) to which to paint
132 public abstract void paint(GlyphView view, Graphics g, Shape a, int p0,
136 * Maps a position in the document into the coordinate space of the View.
137 * The output rectangle usually reflects the font height but has a width
140 * @param view the glyph view
141 * @param pos the position of the character in the model
142 * @param a the area that is occupied by the view
143 * @param b either {@link Position.Bias#Forward} or
144 * {@link Position.Bias#Backward} depending on the preferred
145 * direction bias. If <code>null</code> this defaults to
146 * <code>Position.Bias.Forward</code>
148 * @return a rectangle that gives the location of the document position
149 * inside the view coordinate space
151 * @throws BadLocationException if <code>pos</code> is invalid
152 * @throws IllegalArgumentException if b is not one of the above listed
155 public abstract Shape modelToView(GlyphView view, int pos, Position.Bias b,
157 throws BadLocationException;
160 * Maps a visual position into a document location.
162 * @param v the glyph view
163 * @param x the X coordinate of the visual position
164 * @param y the Y coordinate of the visual position
165 * @param a the allocated region
166 * @param biasRet filled with the bias of the model location on method exit
168 * @return the model location that represents the specified view location
170 public abstract int viewToModel(GlyphView v, float x, float y, Shape a,
171 Position.Bias[] biasRet);
174 * Determine the span of the glyphs from location <code>p0</code> to
175 * location <code>p1</code>. If <code>te</code> is not <code>null</code>,
176 * then TABs are expanded using this <code>TabExpander</code>.
177 * The parameter <code>x</code> is the location at which the view is
178 * located (this is important when using TAB expansion).
180 * @param view the glyph view
181 * @param p0 the starting location in the document model
182 * @param p1 the end location in the document model
183 * @param te the tab expander to use
184 * @param x the location at which the view is located
186 * @return the span of the glyphs from location <code>p0</code> to
187 * location <code>p1</code>, possibly using TAB expansion
189 public abstract float getSpan(GlyphView view, int p0, int p1,
190 TabExpander te, float x);
194 * Returns the model location that should be used to place a caret when
195 * moving the caret through the document.
197 * @param v the glyph view
198 * @param pos the current model location
199 * @param b the bias for <code>p</code>
200 * @param a the allocated region for the glyph view
201 * @param direction the direction from the current position; Must be one of
202 * {@link SwingConstants#EAST}, {@link SwingConstants#WEST},
203 * {@link SwingConstants#NORTH} or {@link SwingConstants#SOUTH}
204 * @param biasRet filled with the bias of the resulting location when method
207 * @return the location within the document that should be used to place the
208 * caret when moving the caret around the document
210 * @throws BadLocationException if <code>pos</code> is an invalid model
212 * @throws IllegalArgumentException if <code>d</code> is invalid
214 public int getNextVisualPositionFrom(GlyphView v, int pos, Position.Bias b,
215 Shape a, int direction,
216 Position.Bias[] biasRet)
217 throws BadLocationException
223 case SwingConstants.EAST:
226 case SwingConstants.WEST:
229 case SwingConstants.NORTH:
230 case SwingConstants.SOUTH:
232 // This should be handled in enclosing view, since the glyph view
233 // does not layout vertically.
240 * Returns a painter that can be used to render the specified glyph view.
241 * If this glyph painter is stateful, then it should return a new instance.
242 * However, if this painter is stateless it should return itself. The
243 * default behaviour is to return itself.
245 * @param v the glyph view for which to create a painter
246 * @param p0 the start offset of the rendered area
247 * @param p1 the end offset of the rendered area
249 * @return a painter that can be used to render the specified glyph view
251 public GlyphPainter getPainter(GlyphView v, int p0, int p1)
258 * A GlyphPainter implementation based on TextLayout. This should give
259 * better performance in Java2D environments.
261 private static class J2DGlyphPainter
268 TextLayout textLayout;
271 * Creates a new J2DGlyphPainter.
273 * @param str the string
274 * @param font the font
275 * @param frc the font render context
277 J2DGlyphPainter(String str, Font font, FontRenderContext frc)
279 textLayout = new TextLayout(str, font, frc);
283 * Returns null so that GlyphView.checkPainter() creates a new instance.
285 public GlyphPainter getPainter(GlyphView v, int p0, int p1)
291 * Delegates to the text layout.
293 public float getAscent(GlyphView v)
295 return textLayout.getAscent();
299 * Delegates to the text layout.
301 public int getBoundedPosition(GlyphView v, int p0, float x, float len)
304 TextHitInfo hit = textLayout.hitTestChar(len, 0);
305 if (hit.getCharIndex() == -1 && ! textLayout.isLeftToRight())
306 pos = v.getEndOffset();
309 pos = hit.isLeadingEdge() ? hit.getInsertionIndex()
310 : hit.getInsertionIndex() - 1;
311 pos += v.getStartOffset();
317 * Delegates to the text layout.
319 public float getDescent(GlyphView v)
321 return textLayout.getDescent();
325 * Delegates to the text layout.
327 public float getHeight(GlyphView view)
329 return textLayout.getAscent() + textLayout.getDescent()
330 + textLayout.getLeading();
334 * Delegates to the text layout.
336 public float getSpan(GlyphView v, int p0, int p1, TabExpander te, float x)
339 if (p0 == v.getStartOffset() && p1 == v.getEndOffset())
340 span = textLayout.getAdvance();
343 int start = v.getStartOffset();
346 TextHitInfo hit0 = TextHitInfo.afterOffset(i0);
347 TextHitInfo hit1 = TextHitInfo.afterOffset(i1);
348 float x0 = textLayout.getCaretInfo(hit0)[0];
349 float x1 = textLayout.getCaretInfo(hit1)[0];
350 span = Math.abs(x1 - x0);
356 * Delegates to the text layout.
358 public Shape modelToView(GlyphView v, int pos, Bias b, Shape a)
359 throws BadLocationException
361 int offs = pos - v.getStartOffset();
362 // Create copy here to protect original shape.
363 Rectangle2D bounds = a.getBounds2D();
365 b == Position.Bias.Forward ? TextHitInfo.afterOffset(offs)
366 : TextHitInfo.beforeOffset(offs);
367 float[] loc = textLayout.getCaretInfo(hit);
368 bounds.setRect(bounds.getX() + loc[0], bounds.getY(), 1,
374 * Delegates to the text layout.
376 public void paint(GlyphView view, Graphics g, Shape a, int p0, int p1)
378 // Can't paint this with plain graphics.
379 if (g instanceof Graphics2D)
381 Graphics2D g2d = (Graphics2D) g;
382 Rectangle2D b = a instanceof Rectangle2D ? (Rectangle2D) a
384 float x = (float) b.getX();
385 float y = (float) b.getY() + textLayout.getAscent()
386 + textLayout.getLeading();
387 // TODO: Try if clipping makes things faster for narrow views.
388 textLayout.draw(g2d, x, y);
393 * Delegates to the text layout.
395 public int viewToModel(GlyphView v, float x, float y, Shape a,
398 Rectangle2D bounds = a instanceof Rectangle2D ? (Rectangle2D) a
400 TextHitInfo hit = textLayout.hitTestChar(x - (float) bounds.getX(), 0);
401 int pos = hit.getInsertionIndex();
402 biasRet[0] = hit.isLeadingEdge() ? Position.Bias.Forward
403 : Position.Bias.Backward;
404 return pos + v.getStartOffset();
410 * The default <code>GlyphPainter</code> used in <code>GlyphView</code>.
412 static class DefaultGlyphPainter extends GlyphPainter
414 FontMetrics fontMetrics;
417 * Returns the full height of the rendered text.
419 * @return the full height of the rendered text
421 public float getHeight(GlyphView view)
423 updateFontMetrics(view);
424 float height = fontMetrics.getHeight();
431 * @param view the glyph view to paint
432 * @param g the graphics context to use for painting
433 * @param a the allocation of the glyph view
434 * @param p0 the start position (in the model) from which to paint
435 * @param p1 the end position (in the model) to which to paint
437 public void paint(GlyphView view, Graphics g, Shape a, int p0,
440 updateFontMetrics(view);
441 Rectangle r = a instanceof Rectangle ? (Rectangle) a : a.getBounds();
442 TabExpander tabEx = view.getTabExpander();
443 Segment txt = view.getText(p0, p1);
445 // Find out the X location at which we have to paint.
447 int p = view.getStartOffset();
450 int width = Utilities.getTabbedTextWidth(txt, fontMetrics,x, tabEx,
454 // Find out Y location.
455 int y = r.y + fontMetrics.getHeight() - fontMetrics.getDescent();
458 g.setFont(fontMetrics.getFont());
459 Utilities.drawTabbedText(txt, x, y, g, tabEx, p0);
464 * Maps a position in the document into the coordinate space of the View.
465 * The output rectangle usually reflects the font height but has a width
468 * @param view the glyph view
469 * @param pos the position of the character in the model
470 * @param a the area that is occupied by the view
471 * @param b either {@link Position.Bias#Forward} or
472 * {@link Position.Bias#Backward} depending on the preferred
473 * direction bias. If <code>null</code> this defaults to
474 * <code>Position.Bias.Forward</code>
476 * @return a rectangle that gives the location of the document position
477 * inside the view coordinate space
479 * @throws BadLocationException if <code>pos</code> is invalid
480 * @throws IllegalArgumentException if b is not one of the above listed
483 public Shape modelToView(GlyphView view, int pos, Position.Bias b,
485 throws BadLocationException
487 updateFontMetrics(view);
488 Element el = view.getElement();
489 Segment txt = view.getText(el.getStartOffset(), pos);
490 Rectangle bounds = a instanceof Rectangle ? (Rectangle) a
492 TabExpander expander = view.getTabExpander();
493 int width = Utilities.getTabbedTextWidth(txt, fontMetrics, bounds.x,
495 view.getStartOffset());
496 int height = fontMetrics.getHeight();
497 Rectangle result = new Rectangle(bounds.x + width, bounds.y,
503 * Determine the span of the glyphs from location <code>p0</code> to
504 * location <code>p1</code>. If <code>te</code> is not <code>null</code>,
505 * then TABs are expanded using this <code>TabExpander</code>.
506 * The parameter <code>x</code> is the location at which the view is
507 * located (this is important when using TAB expansion).
509 * @param view the glyph view
510 * @param p0 the starting location in the document model
511 * @param p1 the end location in the document model
512 * @param te the tab expander to use
513 * @param x the location at which the view is located
515 * @return the span of the glyphs from location <code>p0</code> to
516 * location <code>p1</code>, possibly using TAB expansion
518 public float getSpan(GlyphView view, int p0, int p1,
519 TabExpander te, float x)
521 updateFontMetrics(view);
522 Segment txt = view.getText(p0, p1);
523 int span = Utilities.getTabbedTextWidth(txt, fontMetrics, (int) x, te,
529 * Returns the ascent of the text run that is rendered by this
530 * <code>GlyphPainter</code>.
532 * @param v the glyph view
534 * @return the ascent of the text run that is rendered by this
535 * <code>GlyphPainter</code>
537 * @see FontMetrics#getAscent()
539 public float getAscent(GlyphView v)
541 updateFontMetrics(v);
542 return fontMetrics.getAscent();
546 * Returns the descent of the text run that is rendered by this
547 * <code>GlyphPainter</code>.
549 * @param v the glyph view
551 * @return the descent of the text run that is rendered by this
552 * <code>GlyphPainter</code>
554 * @see FontMetrics#getDescent()
556 public float getDescent(GlyphView v)
558 updateFontMetrics(v);
559 return fontMetrics.getDescent();
563 * Determines the model offset, so that the text between <code>p0</code>
564 * and this offset fits within the span starting at <code>x</code> with
565 * the length of <code>len</code>.
567 * @param v the glyph view
568 * @param p0 the starting offset in the model
569 * @param x the start location in the view
570 * @param len the length of the span in the view
572 public int getBoundedPosition(GlyphView v, int p0, float x, float len)
574 updateFontMetrics(v);
575 TabExpander te = v.getTabExpander();
576 Segment txt = v.getText(p0, v.getEndOffset());
577 int pos = Utilities.getTabbedTextOffset(txt, fontMetrics, (int) x,
578 (int) (x + len), te, p0, false);
583 * Maps a visual position into a document location.
585 * @param v the glyph view
586 * @param x the X coordinate of the visual position
587 * @param y the Y coordinate of the visual position
588 * @param a the allocated region
589 * @param biasRet filled with the bias of the model location on method exit
591 * @return the model location that represents the specified view location
593 public int viewToModel(GlyphView v, float x, float y, Shape a,
596 Rectangle r = a instanceof Rectangle ? (Rectangle) a : a.getBounds();
597 int p0 = v.getStartOffset();
598 int p1 = v.getEndOffset();
599 TabExpander te = v.getTabExpander();
600 Segment s = v.getText(p0, p1);
601 int offset = Utilities.getTabbedTextOffset(s, fontMetrics, r.x, (int) x,
603 int ret = p0 + offset;
606 biasRet[0] = Position.Bias.Forward;
610 private void updateFontMetrics(GlyphView v)
612 Font font = v.getFont();
613 if (fontMetrics == null || ! font.equals(fontMetrics.getFont()))
615 Container c = v.getContainer();
618 fm = c.getFontMetrics(font);
620 fm = Toolkit.getDefaultToolkit().getFontMetrics(font);
627 * The GlyphPainer used for painting the glyphs.
629 GlyphPainter glyphPainter;
632 * The start offset within the document for this view.
637 * The end offset within the document for this view.
642 * The x location against which the tab expansion is done.
647 * The tab expander that is used in this view.
649 private TabExpander tabExpander;
652 * Creates a new <code>GlyphView</code> for the given <code>Element</code>.
654 * @param element the element that is rendered by this GlyphView
656 public GlyphView(Element element)
664 * Returns the <code>GlyphPainter</code> that is used by this
665 * <code>GlyphView</code>. If no <code>GlyphPainer</code> has been installed
666 * <code>null</code> is returned.
668 * @return the glyph painter that is used by this
669 * glyph view or <code>null</code> if no glyph painter has been
672 public GlyphPainter getGlyphPainter()
678 * Sets the {@link GlyphPainter} to be used for this <code>GlyphView</code>.
680 * @param painter the glyph painter to be used for this glyph view
682 public void setGlyphPainter(GlyphPainter painter)
684 glyphPainter = painter;
688 * Checks if a <code>GlyphPainer</code> is installed. If this is not the
689 * case, a default painter is installed.
691 protected void checkPainter()
693 if (glyphPainter == null)
696 SystemProperties.getProperty("gnu.javax.swing.noGraphics2D")))
698 glyphPainter = new DefaultGlyphPainter();
702 Segment s = getText(getStartOffset(), getEndOffset());
703 glyphPainter = new J2DGlyphPainter(s.toString(), getFont(),
704 new FontRenderContext(null,
712 * Renders the <code>Element</code> that is associated with this
715 * @param g the <code>Graphics</code> context to render to
716 * @param a the allocated region for the <code>Element</code>
718 public void paint(Graphics g, Shape a)
721 int p0 = getStartOffset();
722 int p1 = getEndOffset();
724 Rectangle r = a instanceof Rectangle ? (Rectangle) a : a.getBounds();
725 Container c = getContainer();
727 Color fg = getForeground();
728 JTextComponent tc = null;
729 if (c instanceof JTextComponent)
731 tc = (JTextComponent) c;
732 if (! tc.isEnabled())
733 fg = tc.getDisabledTextColor();
735 Color bg = getBackground();
739 g.fillRect(r.x, r.y, r.width, r.height);
743 // Paint layered highlights if there are any.
746 Highlighter h = tc.getHighlighter();
747 if (h instanceof LayeredHighlighter)
749 LayeredHighlighter lh = (LayeredHighlighter) h;
750 lh.paintLayeredHighlights(g, p0, p1, a, tc, this);
755 glyphPainter.paint(this, g, a, p0, p1);
756 boolean underline = isUnderline();
757 boolean striked = isStrikeThrough();
758 if (underline || striked)
760 View parent = getParent();
762 if (parent != null && parent.getEndOffset() == p1)
765 Segment s = getText(p0, p1);
766 while (s.count > 0 && Character.isWhitespace(s.array[s.count - 1]))
773 int p = getStartOffset();
774 TabExpander tabEx = getTabExpander();
776 x0 += (int) glyphPainter.getSpan(this, p, p0, tabEx, x0);
777 int x1 = x0 + (int) glyphPainter.getSpan(this, p0, p1, tabEx, x0);
779 int y = r.y + r.height - (int) glyphPainter.getDescent(this);
784 g.drawLine(x0, yTmp, x1, yTmp);
789 yTmp -= (int) glyphPainter.getAscent(this);
790 g.drawLine(x0, yTmp, x1, yTmp);
797 * Returns the preferred span of the content managed by this
798 * <code>View</code> along the specified <code>axis</code>.
800 * @param axis the axis
802 * @return the preferred span of this <code>View</code>.
804 public float getPreferredSpan(int axis)
808 GlyphPainter painter = getGlyphPainter();
812 TabExpander tabEx = null;
813 View parent = getParent();
814 if (parent instanceof TabExpander)
815 tabEx = (TabExpander) parent;
816 span = painter.getSpan(this, getStartOffset(), getEndOffset(),
820 span = painter.getHeight(this);
825 throw new IllegalArgumentException("Illegal axis");
831 * Maps a position in the document into the coordinate space of the View.
832 * The output rectangle usually reflects the font height but has a width
835 * @param pos the position of the character in the model
836 * @param a the area that is occupied by the view
837 * @param b either {@link Position.Bias#Forward} or
838 * {@link Position.Bias#Backward} depending on the preferred
839 * direction bias. If <code>null</code> this defaults to
840 * <code>Position.Bias.Forward</code>
842 * @return a rectangle that gives the location of the document position
843 * inside the view coordinate space
845 * @throws BadLocationException if <code>pos</code> is invalid
846 * @throws IllegalArgumentException if b is not one of the above listed
849 public Shape modelToView(int pos, Shape a, Position.Bias b)
850 throws BadLocationException
852 GlyphPainter p = getGlyphPainter();
853 return p.modelToView(this, pos, b, a);
857 * Maps coordinates from the <code>View</code>'s space into a position
858 * in the document model.
860 * @param x the x coordinate in the view space
861 * @param y the y coordinate in the view space
862 * @param a the allocation of this <code>View</code>
863 * @param b the bias to use
865 * @return the position in the document that corresponds to the screen
866 * coordinates <code>x, y</code>
868 public int viewToModel(float x, float y, Shape a, Position.Bias[] b)
871 GlyphPainter painter = getGlyphPainter();
872 return painter.viewToModel(this, x, y, a, b);
876 * Return the {@link TabExpander} to use.
878 * @return the {@link TabExpander} to use
880 public TabExpander getTabExpander()
886 * Returns the preferred span of this view for tab expansion.
888 * @param x the location of the view
889 * @param te the tab expander to use
891 * @return the preferred span of this view for tab expansion
893 public float getTabbedSpan(float x, TabExpander te)
896 TabExpander old = tabExpander;
898 if (tabExpander != old)
900 // Changing the tab expander will lead to a relayout in the X_AXIS.
901 preferenceChanged(null, true, false);
904 return getGlyphPainter().getSpan(this, getStartOffset(),
905 getEndOffset(), tabExpander, x);
909 * Returns the span of a portion of the view. This is used in TAB expansion
910 * for fragments that don't contain TABs.
912 * @param p0 the start index
913 * @param p1 the end index
915 * @return the span of the specified portion of the view
917 public float getPartialSpan(int p0, int p1)
920 return glyphPainter.getSpan(this, p0, p1, tabExpander, tabX);
924 * Returns the start offset in the document model of the portion
925 * of text that this view is responsible for.
927 * @return the start offset in the document model of the portion
928 * of text that this view is responsible for
930 public int getStartOffset()
932 Element el = getElement();
933 int offs = el.getStartOffset();
940 * Returns the end offset in the document model of the portion
941 * of text that this view is responsible for.
943 * @return the end offset in the document model of the portion
944 * of text that this view is responsible for
946 public int getEndOffset()
948 Element el = getElement();
951 offs = el.getStartOffset() + offset + length;
953 offs = el.getEndOffset();
957 private Segment cached = new Segment();
960 * Returns the text segment that this view is responsible for.
962 * @param p0 the start index in the document model
963 * @param p1 the end index in the document model
965 * @return the text segment that this view is responsible for
967 public Segment getText(int p0, int p1)
971 getDocument().getText(p0, p1 - p0, cached);
973 catch (BadLocationException ex)
976 ae = new AssertionError("BadLocationException should not be "
977 + "thrown here. p0 = " + p0 + ", p1 = " + p1);
986 * Returns the font for the text run for which this <code>GlyphView</code>
989 * @return the font for the text run for which this <code>GlyphView</code>
992 public Font getFont()
994 Document doc = getDocument();
996 if (doc instanceof StyledDocument)
998 StyledDocument styledDoc = (StyledDocument) doc;
999 font = styledDoc.getFont(getAttributes());
1003 Container c = getContainer();
1011 * Returns the foreground color which should be used to paint the text.
1012 * This is fetched from the associated element's text attributes using
1013 * {@link StyleConstants#getForeground}.
1015 * @return the foreground color which should be used to paint the text
1017 public Color getForeground()
1019 Element el = getElement();
1020 AttributeSet atts = el.getAttributes();
1021 return StyleConstants.getForeground(atts);
1025 * Returns the background color which should be used to paint the text.
1026 * This is fetched from the associated element's text attributes using
1027 * {@link StyleConstants#getBackground}.
1029 * @return the background color which should be used to paint the text
1031 public Color getBackground()
1033 Element el = getElement();
1034 AttributeSet atts = el.getAttributes();
1035 // We cannot use StyleConstants.getBackground() here, because that returns
1036 // BLACK as default (when background == null). What we need is the
1037 // background setting of the text component instead, which is what we get
1038 // when background == null anyway.
1039 return (Color) atts.getAttribute(StyleConstants.Background);
1043 * Determines whether the text should be rendered strike-through or not. This
1044 * is determined using the method
1045 * {@link StyleConstants#isStrikeThrough(AttributeSet)} on the element of
1048 * @return whether the text should be rendered strike-through or not
1050 public boolean isStrikeThrough()
1052 Element el = getElement();
1053 AttributeSet atts = el.getAttributes();
1054 return StyleConstants.isStrikeThrough(atts);
1058 * Determines whether the text should be rendered as subscript or not. This
1059 * is determined using the method
1060 * {@link StyleConstants#isSubscript(AttributeSet)} on the element of
1063 * @return whether the text should be rendered as subscript or not
1065 public boolean isSubscript()
1067 Element el = getElement();
1068 AttributeSet atts = el.getAttributes();
1069 return StyleConstants.isSubscript(atts);
1073 * Determines whether the text should be rendered as superscript or not. This
1074 * is determined using the method
1075 * {@link StyleConstants#isSuperscript(AttributeSet)} on the element of
1078 * @return whether the text should be rendered as superscript or not
1080 public boolean isSuperscript()
1082 Element el = getElement();
1083 AttributeSet atts = el.getAttributes();
1084 return StyleConstants.isSuperscript(atts);
1088 * Determines whether the text should be rendered as underlined or not. This
1089 * is determined using the method
1090 * {@link StyleConstants#isUnderline(AttributeSet)} on the element of
1093 * @return whether the text should be rendered as underlined or not
1095 public boolean isUnderline()
1097 Element el = getElement();
1098 AttributeSet atts = el.getAttributes();
1099 return StyleConstants.isUnderline(atts);
1103 * Creates and returns a shallow clone of this GlyphView. This is used by
1104 * the {@link #createFragment} and {@link #breakView} methods.
1106 * @return a shallow clone of this GlyphView
1108 protected final Object clone()
1112 return super.clone();
1114 catch (CloneNotSupportedException ex)
1116 AssertionError err = new AssertionError("CloneNotSupportedException "
1117 + "must not be thrown here");
1124 * Tries to break the view near the specified view span <code>len</code>.
1125 * The glyph view can only be broken in the X direction. For Y direction it
1128 * @param axis the axis for breaking, may be {@link View#X_AXIS} or
1129 * {@link View#Y_AXIS}
1130 * @param p0 the model location where the fragment should start
1131 * @param pos the view position along the axis where the fragment starts
1132 * @param len the desired length of the fragment view
1134 * @return the fragment view, or <code>this</code> if breaking was not
1137 public View breakView(int axis, int p0, float pos, float len)
1139 View brokenView = this;
1143 int end = glyphPainter.getBoundedPosition(this, p0, pos, len);
1144 int breakLoc = getBreakLocation(p0, end);
1147 if (p0 != getStartOffset() || end != getEndOffset())
1149 brokenView = createFragment(p0, end);
1150 if (brokenView instanceof GlyphView)
1151 ((GlyphView) brokenView).tabX = pos;
1158 * Determines how well the specified view location is suitable for inserting
1159 * a line break. If <code>axis</code> is <code>View.Y_AXIS</code>, then
1160 * this method forwards to the superclass, if <code>axis</code> is
1161 * <code>View.X_AXIS</code> then this method returns
1162 * {@link View#ExcellentBreakWeight} if there is a suitable break location
1163 * (usually whitespace) within the specified view span, or
1164 * {@link View#GoodBreakWeight} if not.
1166 * @param axis the axis along which the break weight is requested
1167 * @param pos the starting view location
1168 * @param len the length of the span at which the view should be broken
1170 * @return the break weight
1172 public int getBreakWeight(int axis, float pos, float len)
1176 weight = super.getBreakWeight(axis, pos, len);
1180 int start = getStartOffset();
1181 int end = glyphPainter.getBoundedPosition(this, start, pos, len);
1183 weight = BadBreakWeight;
1186 if (getBreakLocation(start, end) != -1)
1187 weight = ExcellentBreakWeight;
1189 weight = GoodBreakWeight;
1195 private int getBreakLocation(int start, int end)
1198 Segment s = getText(start, end);
1199 for (char c = s.last(); c != Segment.DONE && loc == -1; c = s.previous())
1201 if (Character.isWhitespace(c))
1203 loc = s.getIndex() - s.getBeginIndex() + 1 + start;
1210 * Receives notification that some text attributes have changed within the
1211 * text fragment that this view is responsible for. This calls
1212 * {@link View#preferenceChanged(View, boolean, boolean)} on the parent for
1213 * both width and height.
1215 * @param e the document event describing the change; not used here
1216 * @param a the view allocation on screen; not used here
1217 * @param vf the view factory; not used here
1219 public void changedUpdate(DocumentEvent e, Shape a, ViewFactory vf)
1221 preferenceChanged(null, true, true);
1225 * Receives notification that some text has been inserted within the
1226 * text fragment that this view is responsible for. This calls
1227 * {@link View#preferenceChanged(View, boolean, boolean)} for the
1228 * direction in which the glyphs are rendered.
1230 * @param e the document event describing the change; not used here
1231 * @param a the view allocation on screen; not used here
1232 * @param vf the view factory; not used here
1234 public void insertUpdate(DocumentEvent e, Shape a, ViewFactory vf)
1236 preferenceChanged(null, true, false);
1240 * Receives notification that some text has been removed within the
1241 * text fragment that this view is responsible for. This calls
1242 * {@link View#preferenceChanged(View, boolean, boolean)} on the parent for
1245 * @param e the document event describing the change; not used here
1246 * @param a the view allocation on screen; not used here
1247 * @param vf the view factory; not used here
1249 public void removeUpdate(DocumentEvent e, Shape a, ViewFactory vf)
1251 preferenceChanged(null, true, false);
1255 * Creates a fragment view of this view that starts at <code>p0</code> and
1256 * ends at <code>p1</code>.
1258 * @param p0 the start location for the fragment view
1259 * @param p1 the end location for the fragment view
1261 * @return the fragment view
1263 public View createFragment(int p0, int p1)
1266 Element el = getElement();
1267 GlyphView fragment = (GlyphView) clone();
1268 fragment.offset = p0 - el.getStartOffset();
1269 fragment.length = p1 - p0;
1270 fragment.glyphPainter = glyphPainter.getPainter(fragment, p0, p1);
1275 * Returns the alignment of this view along the specified axis. For the Y
1276 * axis this is <code>(height - descent) / height</code> for the used font,
1277 * so that it is aligned along the baseline.
1278 * For the X axis the superclass is called.
1280 public float getAlignment(int axis)
1286 GlyphPainter painter = getGlyphPainter();
1287 float height = painter.getHeight(this);
1288 float descent = painter.getDescent(this);
1289 float ascent = painter.getAscent(this);
1290 if (isSuperscript())
1292 else if (isSubscript())
1293 align = height > 0 ? (height - (descent + (ascent / 2))) / height
1296 align = height > 0 ? (height - descent) / height : 0;
1299 align = super.getAlignment(axis);
1305 * Returns the model location that should be used to place a caret when
1306 * moving the caret through the document.
1308 * @param pos the current model location
1309 * @param bias the bias for <code>p</code>
1310 * @param a the allocated region for the glyph view
1311 * @param direction the direction from the current position; Must be one of
1312 * {@link SwingConstants#EAST}, {@link SwingConstants#WEST},
1313 * {@link SwingConstants#NORTH} or {@link SwingConstants#SOUTH}
1314 * @param biasRet filled with the bias of the resulting location when method
1317 * @return the location within the document that should be used to place the
1318 * caret when moving the caret around the document
1320 * @throws BadLocationException if <code>pos</code> is an invalid model
1322 * @throws IllegalArgumentException if <code>d</code> is invalid
1324 public int getNextVisualPositionFrom(int pos, Position.Bias bias, Shape a,
1325 int direction, Position.Bias[] biasRet)
1326 throws BadLocationException
1329 GlyphPainter painter = getGlyphPainter();
1330 return painter.getNextVisualPositionFrom(this, pos, bias, a, direction,