Frames | No Frames |
1: /* GlyphView.java -- A view to render styled text 2: Copyright (C) 2005 Free Software Foundation, Inc. 3: 4: This file is part of GNU Classpath. 5: 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) 9: any later version. 10: 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. 15: 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 19: 02110-1301 USA. 20: 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 24: combination. 25: 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. */ 37: 38: 39: package javax.swing.text; 40: 41: import java.awt.Color; 42: import java.awt.Font; 43: import java.awt.FontMetrics; 44: import java.awt.Graphics; 45: import java.awt.Rectangle; 46: import java.awt.Shape; 47: import java.awt.Toolkit; 48: import java.text.BreakIterator; 49: 50: import javax.swing.SwingConstants; 51: import javax.swing.event.DocumentEvent; 52: import javax.swing.text.Position.Bias; 53: 54: /** 55: * Renders a run of styled text. This {@link View} subclass paints the 56: * characters of the <code>Element</code> it is responsible for using 57: * the style information from that <code>Element</code>. 58: * 59: * @author Roman Kennke (roman@kennke.org) 60: */ 61: public class GlyphView extends View implements TabableView, Cloneable 62: { 63: 64: /** 65: * An abstract base implementation for a glyph painter for 66: * <code>GlyphView</code>. 67: */ 68: public abstract static class GlyphPainter 69: { 70: /** 71: * Creates a new <code>GlyphPainer</code>. 72: */ 73: public GlyphPainter() 74: { 75: // Nothing to do here. 76: } 77: 78: /** 79: * Returns the ascent of the font that is used by this glyph painter. 80: * 81: * @param v the glyph view 82: * 83: * @return the ascent of the font that is used by this glyph painter 84: */ 85: public abstract float getAscent(GlyphView v); 86: 87: /** 88: * Returns the descent of the font that is used by this glyph painter. 89: * 90: * @param v the glyph view 91: * 92: * @return the descent of the font that is used by this glyph painter 93: */ 94: public abstract float getDescent(GlyphView v); 95: 96: /** 97: * Returns the full height of the rendered text. 98: * 99: * @return the full height of the rendered text 100: */ 101: public abstract float getHeight(GlyphView view); 102: 103: /** 104: * Determines the model offset, so that the text between <code>p0</code> 105: * and this offset fits within the span starting at <code>x</code> with 106: * the length of <code>len</code>. 107: * 108: * @param v the glyph view 109: * @param p0 the starting offset in the model 110: * @param x the start location in the view 111: * @param len the length of the span in the view 112: */ 113: public abstract int getBoundedPosition(GlyphView v, int p0, float x, 114: float len); 115: 116: /** 117: * Paints the glyphs. 118: * 119: * @param view the glyph view to paint 120: * @param g the graphics context to use for painting 121: * @param a the allocation of the glyph view 122: * @param p0 the start position (in the model) from which to paint 123: * @param p1 the end position (in the model) to which to paint 124: */ 125: public abstract void paint(GlyphView view, Graphics g, Shape a, int p0, 126: int p1); 127: 128: /** 129: * Maps a position in the document into the coordinate space of the View. 130: * The output rectangle usually reflects the font height but has a width 131: * of zero. 132: * 133: * @param view the glyph view 134: * @param pos the position of the character in the model 135: * @param a the area that is occupied by the view 136: * @param b either {@link Position.Bias#Forward} or 137: * {@link Position.Bias#Backward} depending on the preferred 138: * direction bias. If <code>null</code> this defaults to 139: * <code>Position.Bias.Forward</code> 140: * 141: * @return a rectangle that gives the location of the document position 142: * inside the view coordinate space 143: * 144: * @throws BadLocationException if <code>pos</code> is invalid 145: * @throws IllegalArgumentException if b is not one of the above listed 146: * valid values 147: */ 148: public abstract Shape modelToView(GlyphView view, int pos, Position.Bias b, 149: Shape a) 150: throws BadLocationException; 151: 152: /** 153: * Maps a visual position into a document location. 154: * 155: * @param v the glyph view 156: * @param x the X coordinate of the visual position 157: * @param y the Y coordinate of the visual position 158: * @param a the allocated region 159: * @param biasRet filled with the bias of the model location on method exit 160: * 161: * @return the model location that represents the specified view location 162: */ 163: public abstract int viewToModel(GlyphView v, float x, float y, Shape a, 164: Position.Bias[] biasRet); 165: 166: /** 167: * Determine the span of the glyphs from location <code>p0</code> to 168: * location <code>p1</code>. If <code>te</code> is not <code>null</code>, 169: * then TABs are expanded using this <code>TabExpander</code>. 170: * The parameter <code>x</code> is the location at which the view is 171: * located (this is important when using TAB expansion). 172: * 173: * @param view the glyph view 174: * @param p0 the starting location in the document model 175: * @param p1 the end location in the document model 176: * @param te the tab expander to use 177: * @param x the location at which the view is located 178: * 179: * @return the span of the glyphs from location <code>p0</code> to 180: * location <code>p1</code>, possibly using TAB expansion 181: */ 182: public abstract float getSpan(GlyphView view, int p0, int p1, 183: TabExpander te, float x); 184: 185: 186: /** 187: * Returns the model location that should be used to place a caret when 188: * moving the caret through the document. 189: * 190: * @param v the glyph view 191: * @param pos the current model location 192: * @param b the bias for <code>p</code> 193: * @param a the allocated region for the glyph view 194: * @param direction the direction from the current position; Must be one of 195: * {@link SwingConstants#EAST}, {@link SwingConstants#WEST}, 196: * {@link SwingConstants#NORTH} or {@link SwingConstants#SOUTH} 197: * @param biasRet filled with the bias of the resulting location when method 198: * returns 199: * 200: * @return the location within the document that should be used to place the 201: * caret when moving the caret around the document 202: * 203: * @throws BadLocationException if <code>pos</code> is an invalid model 204: * location 205: * @throws IllegalArgumentException if <code>d</code> is invalid 206: */ 207: public int getNextVisualPositionFrom(GlyphView v, int pos, Position.Bias b, 208: Shape a, int direction, 209: Position.Bias[] biasRet) 210: throws BadLocationException 211: 212: { 213: int result = pos; 214: switch (direction) 215: { 216: case SwingConstants.EAST: 217: result = pos + 1; 218: break; 219: case SwingConstants.WEST: 220: result = pos - 1; 221: break; 222: case SwingConstants.NORTH: 223: case SwingConstants.SOUTH: 224: default: 225: // This should be handled in enclosing view, since the glyph view 226: // does not layout vertically. 227: break; 228: } 229: return result; 230: } 231: 232: /** 233: * Returns a painter that can be used to render the specified glyph view. 234: * If this glyph painter is stateful, then it should return a new instance. 235: * However, if this painter is stateless it should return itself. The 236: * default behaviour is to return itself. 237: * 238: * @param v the glyph view for which to create a painter 239: * @param p0 the start offset of the rendered area 240: * @param p1 the end offset of the rendered area 241: * 242: * @return a painter that can be used to render the specified glyph view 243: */ 244: public GlyphPainter getPainter(GlyphView v, int p0, int p1) 245: { 246: return this; 247: } 248: } 249: 250: /** 251: * The default <code>GlyphPainter</code> used in <code>GlyphView</code>. 252: */ 253: static class DefaultGlyphPainter extends GlyphPainter 254: { 255: /** 256: * Returns the full height of the rendered text. 257: * 258: * @return the full height of the rendered text 259: */ 260: public float getHeight(GlyphView view) 261: { 262: Font font = view.getFont(); 263: FontMetrics metrics = Toolkit.getDefaultToolkit().getFontMetrics(font); 264: float height = metrics.getHeight(); 265: return height; 266: } 267: 268: /** 269: * Paints the glyphs. 270: * 271: * @param view the glyph view to paint 272: * @param g the graphics context to use for painting 273: * @param a the allocation of the glyph view 274: * @param p0 the start position (in the model) from which to paint 275: * @param p1 the end position (in the model) to which to paint 276: */ 277: public void paint(GlyphView view, Graphics g, Shape a, int p0, 278: int p1) 279: { 280: Color oldColor = g.getColor(); 281: int height = (int) getHeight(view); 282: Segment txt = view.getText(p0, p1); 283: Rectangle bounds = a.getBounds(); 284: TabExpander tabEx = null; 285: View parent = view.getParent(); 286: if (parent instanceof TabExpander) 287: tabEx = (TabExpander) parent; 288: 289: int width = Utilities.getTabbedTextWidth(txt, g.getFontMetrics(), 290: bounds.x, tabEx, txt.offset); 291: // Fill the background of the text run. 292: Color background = view.getBackground(); 293: if (background != null) 294: { 295: g.setColor(background); 296: g.fillRect(bounds.x, bounds.y, width, height); 297: } 298: // Draw the actual text. 299: g.setColor(view.getForeground()); 300: g.setFont(view.getFont()); 301: int ascent = g.getFontMetrics().getAscent(); 302: if (view.isSuperscript()) 303: // TODO: Adjust font for superscripting. 304: Utilities.drawTabbedText(txt, bounds.x, bounds.y + ascent - height / 2, 305: g, tabEx, txt.offset); 306: else if (view.isSubscript()) 307: // TODO: Adjust font for subscripting. 308: Utilities.drawTabbedText(txt, bounds.x, bounds.y + ascent + height / 2, 309: g, tabEx, txt.offset); 310: else 311: Utilities.drawTabbedText(txt, bounds.x, bounds.y + ascent, g, tabEx, 312: txt.offset); 313: 314: if (view.isStrikeThrough()) 315: { 316: int strikeHeight = (int) (getAscent(view) / 2); 317: g.drawLine(bounds.x, bounds.y + strikeHeight, bounds.height + width, 318: bounds.y + strikeHeight); 319: } 320: if (view.isUnderline()) 321: { 322: int lineHeight = (int) getAscent(view); 323: g.drawLine(bounds.x, bounds.y + lineHeight, bounds.height + width, 324: bounds.y + lineHeight); 325: } 326: g.setColor(oldColor); 327: } 328: 329: /** 330: * Maps a position in the document into the coordinate space of the View. 331: * The output rectangle usually reflects the font height but has a width 332: * of zero. 333: * 334: * @param view the glyph view 335: * @param pos the position of the character in the model 336: * @param a the area that is occupied by the view 337: * @param b either {@link Position.Bias#Forward} or 338: * {@link Position.Bias#Backward} depending on the preferred 339: * direction bias. If <code>null</code> this defaults to 340: * <code>Position.Bias.Forward</code> 341: * 342: * @return a rectangle that gives the location of the document position 343: * inside the view coordinate space 344: * 345: * @throws BadLocationException if <code>pos</code> is invalid 346: * @throws IllegalArgumentException if b is not one of the above listed 347: * valid values 348: */ 349: public Shape modelToView(GlyphView view, int pos, Position.Bias b, 350: Shape a) 351: throws BadLocationException 352: { 353: Element el = view.getElement(); 354: Font font = view.getFont(); 355: FontMetrics fm = view.getContainer().getFontMetrics(font); 356: Segment txt = view.getText(el.getStartOffset(), pos); 357: int width = fm.charsWidth(txt.array, txt.offset, txt.count); 358: int height = fm.getHeight(); 359: Rectangle bounds = a.getBounds(); 360: Rectangle result = new Rectangle(bounds.x + width, bounds.y, 361: bounds.x + width, height); 362: return result; 363: } 364: 365: /** 366: * Determine the span of the glyphs from location <code>p0</code> to 367: * location <code>p1</code>. If <code>te</code> is not <code>null</code>, 368: * then TABs are expanded using this <code>TabExpander</code>. 369: * The parameter <code>x</code> is the location at which the view is 370: * located (this is important when using TAB expansion). 371: * 372: * @param view the glyph view 373: * @param p0 the starting location in the document model 374: * @param p1 the end location in the document model 375: * @param te the tab expander to use 376: * @param x the location at which the view is located 377: * 378: * @return the span of the glyphs from location <code>p0</code> to 379: * location <code>p1</code>, possibly using TAB expansion 380: */ 381: public float getSpan(GlyphView view, int p0, int p1, 382: TabExpander te, float x) 383: { 384: Element el = view.getElement(); 385: Font font = view.getFont(); 386: FontMetrics fm = Toolkit.getDefaultToolkit().getFontMetrics(font); 387: Segment txt = view.getText(p0, p1); 388: int span = Utilities.getTabbedTextWidth(txt, fm, (int) x, te, p0); 389: return span; 390: } 391: 392: /** 393: * Returns the ascent of the text run that is rendered by this 394: * <code>GlyphPainter</code>. 395: * 396: * @param v the glyph view 397: * 398: * @return the ascent of the text run that is rendered by this 399: * <code>GlyphPainter</code> 400: * 401: * @see FontMetrics#getAscent() 402: */ 403: public float getAscent(GlyphView v) 404: { 405: Font font = v.getFont(); 406: FontMetrics fm = v.getContainer().getFontMetrics(font); 407: return fm.getAscent(); 408: } 409: 410: /** 411: * Returns the descent of the text run that is rendered by this 412: * <code>GlyphPainter</code>. 413: * 414: * @param v the glyph view 415: * 416: * @return the descent of the text run that is rendered by this 417: * <code>GlyphPainter</code> 418: * 419: * @see FontMetrics#getDescent() 420: */ 421: public float getDescent(GlyphView v) 422: { 423: Font font = v.getFont(); 424: FontMetrics fm = v.getContainer().getFontMetrics(font); 425: return fm.getDescent(); 426: } 427: 428: /** 429: * Determines the model offset, so that the text between <code>p0</code> 430: * and this offset fits within the span starting at <code>x</code> with 431: * the length of <code>len</code>. 432: * 433: * @param v the glyph view 434: * @param p0 the starting offset in the model 435: * @param x the start location in the view 436: * @param len the length of the span in the view 437: */ 438: public int getBoundedPosition(GlyphView v, int p0, float x, float len) 439: { 440: TabExpander te = v.getTabExpander(); 441: Segment txt = v.getText(p0, v.getEndOffset()); 442: Font font = v.getFont(); 443: FontMetrics fm = v.getContainer().getFontMetrics(font); 444: int pos = Utilities.getTabbedTextOffset(txt, fm, (int) x, 445: (int) (x + len), te, p0, false); 446: return pos; 447: } 448: 449: /** 450: * Maps a visual position into a document location. 451: * 452: * @param v the glyph view 453: * @param x the X coordinate of the visual position 454: * @param y the Y coordinate of the visual position 455: * @param a the allocated region 456: * @param biasRet filled with the bias of the model location on method exit 457: * 458: * @return the model location that represents the specified view location 459: */ 460: public int viewToModel(GlyphView v, float x, float y, Shape a, 461: Bias[] biasRet) 462: { 463: Rectangle b = a.getBounds(); 464: int pos = getBoundedPosition(v, v.getStartOffset(), b.x, x - b.x); 465: return pos; 466: } 467: } 468: 469: /** 470: * The GlyphPainer used for painting the glyphs. 471: */ 472: GlyphPainter glyphPainter; 473: 474: /** 475: * The start offset within the document for this view. 476: */ 477: private int startOffset; 478: 479: /** 480: * The end offset within the document for this view. 481: */ 482: private int endOffset; 483: 484: /** 485: * Creates a new <code>GlyphView</code> for the given <code>Element</code>. 486: * 487: * @param element the element that is rendered by this GlyphView 488: */ 489: public GlyphView(Element element) 490: { 491: super(element); 492: startOffset = -1; 493: endOffset = -1; 494: } 495: 496: /** 497: * Returns the <code>GlyphPainter</code> that is used by this 498: * <code>GlyphView</code>. If no <code>GlyphPainer</code> has been installed 499: * <code>null</code> is returned. 500: * 501: * @return the glyph painter that is used by this 502: * glyph view or <code>null</code> if no glyph painter has been 503: * installed 504: */ 505: public GlyphPainter getGlyphPainter() 506: { 507: return glyphPainter; 508: } 509: 510: /** 511: * Sets the {@link GlyphPainter} to be used for this <code>GlyphView</code>. 512: * 513: * @param painter the glyph painter to be used for this glyph view 514: */ 515: public void setGlyphPainter(GlyphPainter painter) 516: { 517: glyphPainter = painter; 518: } 519: 520: /** 521: * Checks if a <code>GlyphPainer</code> is installed. If this is not the 522: * case, a default painter is installed. 523: */ 524: protected void checkPainter() 525: { 526: if (glyphPainter == null) 527: glyphPainter = new DefaultGlyphPainter(); 528: } 529: 530: /** 531: * Renders the <code>Element</code> that is associated with this 532: * <code>View</code>. 533: * 534: * @param g the <code>Graphics</code> context to render to 535: * @param a the allocated region for the <code>Element</code> 536: */ 537: public void paint(Graphics g, Shape a) 538: { 539: Element el = getElement(); 540: checkPainter(); 541: getGlyphPainter().paint(this, g, a, getStartOffset(), getEndOffset()); 542: } 543: 544: 545: /** 546: * Returns the preferred span of the content managed by this 547: * <code>View</code> along the specified <code>axis</code>. 548: * 549: * @param axis the axis 550: * 551: * @return the preferred span of this <code>View</code>. 552: */ 553: public float getPreferredSpan(int axis) 554: { 555: float span = 0; 556: checkPainter(); 557: GlyphPainter painter = getGlyphPainter(); 558: if (axis == X_AXIS) 559: { 560: Element el = getElement(); 561: TabExpander tabEx = null; 562: View parent = getParent(); 563: if (parent instanceof TabExpander) 564: tabEx = (TabExpander) parent; 565: span = painter.getSpan(this, getStartOffset(), getEndOffset(), 566: tabEx, 0.F); 567: } 568: else 569: span = painter.getHeight(this); 570: 571: return span; 572: } 573: 574: /** 575: * Maps a position in the document into the coordinate space of the View. 576: * The output rectangle usually reflects the font height but has a width 577: * of zero. 578: * 579: * @param pos the position of the character in the model 580: * @param a the area that is occupied by the view 581: * @param b either {@link Position.Bias#Forward} or 582: * {@link Position.Bias#Backward} depending on the preferred 583: * direction bias. If <code>null</code> this defaults to 584: * <code>Position.Bias.Forward</code> 585: * 586: * @return a rectangle that gives the location of the document position 587: * inside the view coordinate space 588: * 589: * @throws BadLocationException if <code>pos</code> is invalid 590: * @throws IllegalArgumentException if b is not one of the above listed 591: * valid values 592: */ 593: public Shape modelToView(int pos, Shape a, Position.Bias b) 594: throws BadLocationException 595: { 596: GlyphPainter p = getGlyphPainter(); 597: return p.modelToView(this, pos, b, a); 598: } 599: 600: /** 601: * Maps coordinates from the <code>View</code>'s space into a position 602: * in the document model. 603: * 604: * @param x the x coordinate in the view space 605: * @param y the y coordinate in the view space 606: * @param a the allocation of this <code>View</code> 607: * @param b the bias to use 608: * 609: * @return the position in the document that corresponds to the screen 610: * coordinates <code>x, y</code> 611: */ 612: public int viewToModel(float x, float y, Shape a, Position.Bias[] b) 613: { 614: checkPainter(); 615: GlyphPainter painter = getGlyphPainter(); 616: return painter.viewToModel(this, x, y, a, b); 617: } 618: 619: /** 620: * Return the {@link TabExpander} to use. 621: * 622: * @return the {@link TabExpander} to use 623: */ 624: public TabExpander getTabExpander() 625: { 626: TabExpander te = null; 627: View parent = getParent(); 628: 629: if (parent instanceof TabExpander) 630: te = (TabExpander) parent; 631: 632: return te; 633: } 634: 635: /** 636: * Returns the preferred span of this view for tab expansion. 637: * 638: * @param x the location of the view 639: * @param te the tab expander to use 640: * 641: * @return the preferred span of this view for tab expansion 642: */ 643: public float getTabbedSpan(float x, TabExpander te) 644: { 645: Element el = getElement(); 646: return getGlyphPainter().getSpan(this, el.getStartOffset(), 647: el.getEndOffset(), te, x); 648: } 649: 650: /** 651: * Returns the span of a portion of the view. This is used in TAB expansion 652: * for fragments that don't contain TABs. 653: * 654: * @param p0 the start index 655: * @param p1 the end index 656: * 657: * @return the span of the specified portion of the view 658: */ 659: public float getPartialSpan(int p0, int p1) 660: { 661: Element el = getElement(); 662: Document doc = el.getDocument(); 663: Segment seg = new Segment(); 664: try 665: { 666: doc.getText(p0, p1 - p0, seg); 667: } 668: catch (BadLocationException ex) 669: { 670: AssertionError ae; 671: ae = new AssertionError("BadLocationException must not be thrown " 672: + "here"); 673: ae.initCause(ex); 674: throw ae; 675: } 676: FontMetrics fm = null; // Fetch font metrics somewhere. 677: return Utilities.getTabbedTextWidth(seg, fm, 0, null, p0); 678: } 679: 680: /** 681: * Returns the start offset in the document model of the portion 682: * of text that this view is responsible for. 683: * 684: * @return the start offset in the document model of the portion 685: * of text that this view is responsible for 686: */ 687: public int getStartOffset() 688: { 689: int start = startOffset; 690: if (start < 0) 691: start = super.getStartOffset(); 692: return start; 693: } 694: 695: /** 696: * Returns the end offset in the document model of the portion 697: * of text that this view is responsible for. 698: * 699: * @return the end offset in the document model of the portion 700: * of text that this view is responsible for 701: */ 702: public int getEndOffset() 703: { 704: int end = endOffset; 705: if (end < 0) 706: end = super.getEndOffset(); 707: return end; 708: } 709: 710: /** 711: * Returns the text segment that this view is responsible for. 712: * 713: * @param p0 the start index in the document model 714: * @param p1 the end index in the document model 715: * 716: * @return the text segment that this view is responsible for 717: */ 718: public Segment getText(int p0, int p1) 719: { 720: Segment txt = new Segment(); 721: try 722: { 723: getDocument().getText(p0, p1 - p0, txt); 724: } 725: catch (BadLocationException ex) 726: { 727: AssertionError ae; 728: ae = new AssertionError("BadLocationException should not be " 729: + "thrown here. p0 = " + p0 + ", p1 = " + p1); 730: ae.initCause(ex); 731: throw ae; 732: } 733: 734: return txt; 735: } 736: 737: /** 738: * Returns the font for the text run for which this <code>GlyphView</code> 739: * is responsible. 740: * 741: * @return the font for the text run for which this <code>GlyphView</code> 742: * is responsible 743: */ 744: public Font getFont() 745: { 746: Element el = getElement(); 747: AttributeSet atts = el.getAttributes(); 748: String family = StyleConstants.getFontFamily(atts); 749: int size = StyleConstants.getFontSize(atts); 750: int style = Font.PLAIN; 751: if (StyleConstants.isBold(atts)) 752: style |= Font.BOLD; 753: if (StyleConstants.isItalic(atts)) 754: style |= Font.ITALIC; 755: Font font = new Font(family, style, size); 756: return font; 757: } 758: 759: /** 760: * Returns the foreground color which should be used to paint the text. 761: * This is fetched from the associated element's text attributes using 762: * {@link StyleConstants#getForeground}. 763: * 764: * @return the foreground color which should be used to paint the text 765: */ 766: public Color getForeground() 767: { 768: Element el = getElement(); 769: AttributeSet atts = el.getAttributes(); 770: return StyleConstants.getForeground(atts); 771: } 772: 773: /** 774: * Returns the background color which should be used to paint the text. 775: * This is fetched from the associated element's text attributes using 776: * {@link StyleConstants#getBackground}. 777: * 778: * @return the background color which should be used to paint the text 779: */ 780: public Color getBackground() 781: { 782: Element el = getElement(); 783: AttributeSet atts = el.getAttributes(); 784: // We cannot use StyleConstants.getBackground() here, because that returns 785: // BLACK as default (when background == null). What we need is the 786: // background setting of the text component instead, which is what we get 787: // when background == null anyway. 788: return (Color) atts.getAttribute(StyleConstants.Background); 789: } 790: 791: /** 792: * Determines whether the text should be rendered strike-through or not. This 793: * is determined using the method 794: * {@link StyleConstants#isStrikeThrough(AttributeSet)} on the element of 795: * this view. 796: * 797: * @return whether the text should be rendered strike-through or not 798: */ 799: public boolean isStrikeThrough() 800: { 801: Element el = getElement(); 802: AttributeSet atts = el.getAttributes(); 803: return StyleConstants.isStrikeThrough(atts); 804: } 805: 806: /** 807: * Determines whether the text should be rendered as subscript or not. This 808: * is determined using the method 809: * {@link StyleConstants#isSubscript(AttributeSet)} on the element of 810: * this view. 811: * 812: * @return whether the text should be rendered as subscript or not 813: */ 814: public boolean isSubscript() 815: { 816: Element el = getElement(); 817: AttributeSet atts = el.getAttributes(); 818: return StyleConstants.isSubscript(atts); 819: } 820: 821: /** 822: * Determines whether the text should be rendered as superscript or not. This 823: * is determined using the method 824: * {@link StyleConstants#isSuperscript(AttributeSet)} on the element of 825: * this view. 826: * 827: * @return whether the text should be rendered as superscript or not 828: */ 829: public boolean isSuperscript() 830: { 831: Element el = getElement(); 832: AttributeSet atts = el.getAttributes(); 833: return StyleConstants.isSuperscript(atts); 834: } 835: 836: /** 837: * Determines whether the text should be rendered as underlined or not. This 838: * is determined using the method 839: * {@link StyleConstants#isUnderline(AttributeSet)} on the element of 840: * this view. 841: * 842: * @return whether the text should be rendered as underlined or not 843: */ 844: public boolean isUnderline() 845: { 846: Element el = getElement(); 847: AttributeSet atts = el.getAttributes(); 848: return StyleConstants.isUnderline(atts); 849: } 850: 851: /** 852: * Creates and returns a shallow clone of this GlyphView. This is used by 853: * the {@link #createFragment} and {@link #breakView} methods. 854: * 855: * @return a shallow clone of this GlyphView 856: */ 857: protected final Object clone() 858: { 859: try 860: { 861: return super.clone(); 862: } 863: catch (CloneNotSupportedException ex) 864: { 865: AssertionError err = new AssertionError("CloneNotSupportedException " 866: + "must not be thrown here"); 867: err.initCause(ex); 868: throw err; 869: } 870: } 871: 872: /** 873: * Tries to break the view near the specified view span <code>len</code>. 874: * The glyph view can only be broken in the X direction. For Y direction it 875: * returns itself. 876: * 877: * @param axis the axis for breaking, may be {@link View#X_AXIS} or 878: * {@link View#Y_AXIS} 879: * @param p0 the model location where the fragment should start 880: * @param pos the view position along the axis where the fragment starts 881: * @param len the desired length of the fragment view 882: * 883: * @return the fragment view, or <code>this</code> if breaking was not 884: * possible 885: */ 886: public View breakView(int axis, int p0, float pos, float len) 887: { 888: if (axis == Y_AXIS) 889: return this; 890: 891: checkPainter(); 892: GlyphPainter painter = getGlyphPainter(); 893: 894: // Try to find a suitable line break. 895: BreakIterator lineBreaker = BreakIterator.getLineInstance(); 896: Segment txt = new Segment(); 897: try 898: { 899: int start = getStartOffset(); 900: int length = getEndOffset() - start; 901: getDocument().getText(start, length, txt); 902: } 903: catch (BadLocationException ex) 904: { 905: AssertionError err = new AssertionError("BadLocationException must not " 906: + "be thrown here."); 907: err.initCause(ex); 908: throw err; 909: } 910: int breakLocation = 911: Utilities.getBreakLocation(txt, getContainer().getFontMetrics(getFont()), 912: (int) pos, (int) (pos + len), 913: getTabExpander(), p0); 914: View brokenView = createFragment(p0, breakLocation); 915: return brokenView; 916: } 917: 918: /** 919: * Determines how well the specified view location is suitable for inserting 920: * a line break. If <code>axis</code> is <code>View.Y_AXIS</code>, then 921: * this method forwards to the superclass, if <code>axis</code> is 922: * <code>View.X_AXIS</code> then this method returns 923: * {@link View#ExcellentBreakWeight} if there is a suitable break location 924: * (usually whitespace) within the specified view span, or 925: * {@link View#GoodBreakWeight} if not. 926: * 927: * @param axis the axis along which the break weight is requested 928: * @param pos the starting view location 929: * @param len the length of the span at which the view should be broken 930: * 931: * @return the break weight 932: */ 933: public int getBreakWeight(int axis, float pos, float len) 934: { 935: int weight; 936: if (axis == Y_AXIS) 937: weight = super.getBreakWeight(axis, pos, len); 938: else 939: { 940: // FIXME: Commented out because the Utilities.getBreakLocation method 941: // is still buggy. The GoodBreakWeight is a reasonable workaround for 942: // now. 943: // int startOffset = getStartOffset(); 944: // int endOffset = getEndOffset() - 1; 945: // Segment s = getText(startOffset, endOffset); 946: // Container c = getContainer(); 947: // FontMetrics fm = c.getFontMetrics(c.getFont()); 948: // int x0 = (int) pos; 949: // int x = (int) (pos + len); 950: // int breakLoc = Utilities.getBreakLocation(s, fm, x0, x, 951: // getTabExpander(), 952: // startOffset); 953: // if (breakLoc == startOffset || breakLoc == endOffset) 954: // weight = GoodBreakWeight; 955: // else 956: // weight = ExcellentBreakWeight; 957: weight = GoodBreakWeight; 958: } 959: return weight; 960: } 961: 962: /** 963: * Receives notification that some text attributes have changed within the 964: * text fragment that this view is responsible for. This calls 965: * {@link View#preferenceChanged(View, boolean, boolean)} on the parent for 966: * both width and height. 967: * 968: * @param e the document event describing the change; not used here 969: * @param a the view allocation on screen; not used here 970: * @param vf the view factory; not used here 971: */ 972: public void changedUpdate(DocumentEvent e, Shape a, ViewFactory vf) 973: { 974: preferenceChanged(this, true, true); 975: } 976: 977: /** 978: * Receives notification that some text has been inserted within the 979: * text fragment that this view is responsible for. This calls 980: * {@link View#preferenceChanged(View, boolean, boolean)} for the 981: * direction in which the glyphs are rendered. 982: * 983: * @param e the document event describing the change; not used here 984: * @param a the view allocation on screen; not used here 985: * @param vf the view factory; not used here 986: */ 987: public void insertUpdate(DocumentEvent e, Shape a, ViewFactory vf) 988: { 989: preferenceChanged(this, true, false); 990: } 991: 992: /** 993: * Receives notification that some text has been removed within the 994: * text fragment that this view is responsible for. This calls 995: * {@link View#preferenceChanged(View, boolean, boolean)} on the parent for 996: * width. 997: * 998: * @param e the document event describing the change; not used here 999: * @param a the view allocation on screen; not used here 1000: * @param vf the view factory; not used here 1001: */ 1002: public void removeUpdate(DocumentEvent e, Shape a, ViewFactory vf) 1003: { 1004: preferenceChanged(this, true, false); 1005: } 1006: 1007: /** 1008: * Creates a fragment view of this view that starts at <code>p0</code> and 1009: * ends at <code>p1</code>. 1010: * 1011: * @param p0 the start location for the fragment view 1012: * @param p1 the end location for the fragment view 1013: * 1014: * @return the fragment view 1015: */ 1016: public View createFragment(int p0, int p1) 1017: { 1018: GlyphView fragment = (GlyphView) clone(); 1019: if (p0 != getStartOffset()) 1020: fragment.startOffset = p0; 1021: if (p1 != getEndOffset()) 1022: fragment.endOffset = p1; 1023: return fragment; 1024: } 1025: 1026: /** 1027: * Returns the alignment of this view along the specified axis. For the Y 1028: * axis this is <code>(height - descent) / height</code> for the used font, 1029: * so that it is aligned along the baseline. 1030: * For the X axis the superclass is called. 1031: */ 1032: public float getAlignment(int axis) 1033: { 1034: float align; 1035: if (axis == Y_AXIS) 1036: { 1037: checkPainter(); 1038: GlyphPainter painter = getGlyphPainter(); 1039: float height = painter.getHeight(this); 1040: float descent = painter.getDescent(this); 1041: align = (height - descent) / height; 1042: } 1043: else 1044: align = super.getAlignment(axis); 1045: 1046: return align; 1047: } 1048: 1049: /** 1050: * Returns the model location that should be used to place a caret when 1051: * moving the caret through the document. 1052: * 1053: * @param pos the current model location 1054: * @param bias the bias for <code>p</code> 1055: * @param a the allocated region for the glyph view 1056: * @param direction the direction from the current position; Must be one of 1057: * {@link SwingConstants#EAST}, {@link SwingConstants#WEST}, 1058: * {@link SwingConstants#NORTH} or {@link SwingConstants#SOUTH} 1059: * @param biasRet filled with the bias of the resulting location when method 1060: * returns 1061: * 1062: * @return the location within the document that should be used to place the 1063: * caret when moving the caret around the document 1064: * 1065: * @throws BadLocationException if <code>pos</code> is an invalid model 1066: * location 1067: * @throws IllegalArgumentException if <code>d</code> is invalid 1068: */ 1069: public int getNextVisualPositionFrom(int pos, Position.Bias bias, Shape a, 1070: int direction, Position.Bias[] biasRet) 1071: throws BadLocationException 1072: { 1073: checkPainter(); 1074: GlyphPainter painter = getGlyphPainter(); 1075: return painter.getNextVisualPositionFrom(this, pos, bias, a, direction, 1076: biasRet); 1077: } 1078: }