GNU Classpath (0.20) | |
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: int height = (int) getHeight(view); 281: Segment txt = view.getText(p0, p1); 282: Rectangle bounds = a.getBounds(); 283: 284: TabExpander tabEx = null; 285: View parent = view.getParent(); 286: if (parent instanceof TabExpander) 287: tabEx = (TabExpander) parent; 288: 289: // Fill the background of the text run. 290: Color background = view.getBackground(); 291: g.setColor(background); 292: int width = Utilities.getTabbedTextWidth(txt, g.getFontMetrics(), 293: bounds.x, tabEx, txt.offset); 294: g.fillRect(bounds.x, bounds.y, width, height); 295: 296: // Draw the actual text. 297: g.setColor(view.getForeground()); 298: g.setFont(view.getFont()); 299: if (view.isSuperscript()) 300: // TODO: Adjust font for superscripting. 301: Utilities.drawTabbedText(txt, bounds.x, bounds.y - height / 2, g, tabEx, 302: txt.offset); 303: else if (view.isSubscript()) 304: // TODO: Adjust font for subscripting. 305: Utilities.drawTabbedText(txt, bounds.x, bounds.y + height / 2, g, tabEx, 306: txt.offset); 307: else 308: Utilities.drawTabbedText(txt, bounds.x, bounds.y, g, tabEx, 309: txt.offset); 310: 311: if (view.isStikeThrough()) 312: { 313: int strikeHeight = (int) (getAscent(view) / 2); 314: g.drawLine(bounds.x, bounds.y + strikeHeight, bounds.height + width, 315: bounds.y + strikeHeight); 316: } 317: if (view.isUnderline()) 318: { 319: int lineHeight = (int) getAscent(view); 320: g.drawLine(bounds.x, bounds.y + lineHeight, bounds.height + width, 321: bounds.y + lineHeight); 322: } 323: } 324: 325: /** 326: * Maps a position in the document into the coordinate space of the View. 327: * The output rectangle usually reflects the font height but has a width 328: * of zero. 329: * 330: * @param view the glyph view 331: * @param pos the position of the character in the model 332: * @param a the area that is occupied by the view 333: * @param b either {@link Position.Bias#Forward} or 334: * {@link Position.Bias#Backward} depending on the preferred 335: * direction bias. If <code>null</code> this defaults to 336: * <code>Position.Bias.Forward</code> 337: * 338: * @return a rectangle that gives the location of the document position 339: * inside the view coordinate space 340: * 341: * @throws BadLocationException if <code>pos</code> is invalid 342: * @throws IllegalArgumentException if b is not one of the above listed 343: * valid values 344: */ 345: public Shape modelToView(GlyphView view, int pos, Position.Bias b, 346: Shape a) 347: throws BadLocationException 348: { 349: Element el = view.getElement(); 350: Font font = view.getFont(); 351: FontMetrics fm = view.getContainer().getFontMetrics(font); 352: Segment txt = view.getText(el.getStartOffset(), pos); 353: int width = fm.charsWidth(txt.array, txt.offset, txt.count); 354: int height = fm.getHeight(); 355: Rectangle bounds = a.getBounds(); 356: Rectangle result = new Rectangle(bounds.x + width, bounds.y, 357: bounds.x + width, height); 358: return result; 359: } 360: 361: /** 362: * Determine the span of the glyphs from location <code>p0</code> to 363: * location <code>p1</code>. If <code>te</code> is not <code>null</code>, 364: * then TABs are expanded using this <code>TabExpander</code>. 365: * The parameter <code>x</code> is the location at which the view is 366: * located (this is important when using TAB expansion). 367: * 368: * @param view the glyph view 369: * @param p0 the starting location in the document model 370: * @param p1 the end location in the document model 371: * @param te the tab expander to use 372: * @param x the location at which the view is located 373: * 374: * @return the span of the glyphs from location <code>p0</code> to 375: * location <code>p1</code>, possibly using TAB expansion 376: */ 377: public float getSpan(GlyphView view, int p0, int p1, 378: TabExpander te, float x) 379: { 380: Element el = view.getElement(); 381: Font font = view.getFont(); 382: FontMetrics fm = Toolkit.getDefaultToolkit().getFontMetrics(font); 383: Segment txt = view.getText(p0, p1); 384: int span = Utilities.getTabbedTextWidth(txt, fm, (int) x, te, p0); 385: return span; 386: } 387: 388: /** 389: * Returns the ascent of the text run that is rendered by this 390: * <code>GlyphPainter</code>. 391: * 392: * @param v the glyph view 393: * 394: * @return the ascent of the text run that is rendered by this 395: * <code>GlyphPainter</code> 396: * 397: * @see FontMetrics#getAscent() 398: */ 399: public float getAscent(GlyphView v) 400: { 401: Font font = v.getFont(); 402: FontMetrics fm = v.getContainer().getFontMetrics(font); 403: return fm.getAscent(); 404: } 405: 406: /** 407: * Returns the descent of the text run that is rendered by this 408: * <code>GlyphPainter</code>. 409: * 410: * @param v the glyph view 411: * 412: * @return the descent of the text run that is rendered by this 413: * <code>GlyphPainter</code> 414: * 415: * @see FontMetrics#getDescent() 416: */ 417: public float getDescent(GlyphView v) 418: { 419: Font font = v.getFont(); 420: FontMetrics fm = v.getContainer().getFontMetrics(font); 421: return fm.getDescent(); 422: } 423: 424: /** 425: * Determines the model offset, so that the text between <code>p0</code> 426: * and this offset fits within the span starting at <code>x</code> with 427: * the length of <code>len</code>. 428: * 429: * @param v the glyph view 430: * @param p0 the starting offset in the model 431: * @param x the start location in the view 432: * @param len the length of the span in the view 433: */ 434: public int getBoundedPosition(GlyphView v, int p0, float x, float len) 435: { 436: TabExpander te = v.getTabExpander(); 437: Segment txt = v.getText(p0, v.getEndOffset()); 438: Font font = v.getFont(); 439: FontMetrics fm = v.getContainer().getFontMetrics(font); 440: int pos = Utilities.getTabbedTextOffset(txt, fm, (int) x, 441: (int) (x + len), te, p0, false); 442: return pos; 443: } 444: 445: /** 446: * Maps a visual position into a document location. 447: * 448: * @param v the glyph view 449: * @param x the X coordinate of the visual position 450: * @param y the Y coordinate of the visual position 451: * @param a the allocated region 452: * @param biasRet filled with the bias of the model location on method exit 453: * 454: * @return the model location that represents the specified view location 455: */ 456: public int viewToModel(GlyphView v, float x, float y, Shape a, 457: Bias[] biasRet) 458: { 459: Rectangle b = a.getBounds(); 460: int pos = getBoundedPosition(v, v.getStartOffset(), b.x, x - b.x); 461: return pos; 462: } 463: } 464: 465: /** 466: * The GlyphPainer used for painting the glyphs. 467: */ 468: GlyphPainter glyphPainter; 469: 470: /** 471: * The start offset within the document for this view. 472: */ 473: int startOffset; 474: 475: /** 476: * The end offset within the document for this view. 477: */ 478: int endOffset; 479: 480: /** 481: * Creates a new <code>GlyphView</code> for the given <code>Element</code>. 482: * 483: * @param element the element that is rendered by this GlyphView 484: */ 485: public GlyphView(Element element) 486: { 487: super(element); 488: startOffset = element.getStartOffset(); 489: endOffset = element.getEndOffset(); 490: } 491: 492: /** 493: * Returns the <code>GlyphPainter</code> that is used by this 494: * <code>GlyphView</code>. If no <code>GlyphPainer</code> has been installed 495: * <code>null</code> is returned. 496: * 497: * @return the glyph painter that is used by this 498: * glyph view or <code>null</code> if no glyph painter has been 499: * installed 500: */ 501: public GlyphPainter getGlyphPainter() 502: { 503: return glyphPainter; 504: } 505: 506: /** 507: * Sets the {@link GlyphPainter} to be used for this <code>GlyphView</code>. 508: * 509: * @param painter the glyph painter to be used for this glyph view 510: */ 511: public void setGlyphPainter(GlyphPainter painter) 512: { 513: glyphPainter = painter; 514: } 515: 516: /** 517: * Checks if a <code>GlyphPainer</code> is installed. If this is not the 518: * case, a default painter is installed. 519: */ 520: protected void checkPainter() 521: { 522: if (glyphPainter == null) 523: glyphPainter = new DefaultGlyphPainter(); 524: } 525: 526: /** 527: * Renders the <code>Element</code> that is associated with this 528: * <code>View</code>. 529: * 530: * @param g the <code>Graphics</code> context to render to 531: * @param a the allocated region for the <code>Element</code> 532: */ 533: public void paint(Graphics g, Shape a) 534: { 535: Element el = getElement(); 536: checkPainter(); 537: getGlyphPainter().paint(this, g, a, el.getStartOffset(), 538: el.getEndOffset()); 539: } 540: 541: 542: /** 543: * Returns the preferred span of the content managed by this 544: * <code>View</code> along the specified <code>axis</code>. 545: * 546: * @param axis the axis 547: * 548: * @return the preferred span of this <code>View</code>. 549: */ 550: public float getPreferredSpan(int axis) 551: { 552: float span = 0; 553: checkPainter(); 554: GlyphPainter painter = getGlyphPainter(); 555: if (axis == X_AXIS) 556: { 557: Element el = getElement(); 558: TabExpander tabEx = null; 559: View parent = getParent(); 560: if (parent instanceof TabExpander) 561: tabEx = (TabExpander) parent; 562: span = painter.getSpan(this, getStartOffset(), getEndOffset(), 563: tabEx, 0.F); 564: } 565: else 566: span = painter.getHeight(this); 567: return span; 568: } 569: 570: /** 571: * Maps a position in the document into the coordinate space of the View. 572: * The output rectangle usually reflects the font height but has a width 573: * of zero. 574: * 575: * @param pos the position of the character in the model 576: * @param a the area that is occupied by the view 577: * @param b either {@link Position.Bias#Forward} or 578: * {@link Position.Bias#Backward} depending on the preferred 579: * direction bias. If <code>null</code> this defaults to 580: * <code>Position.Bias.Forward</code> 581: * 582: * @return a rectangle that gives the location of the document position 583: * inside the view coordinate space 584: * 585: * @throws BadLocationException if <code>pos</code> is invalid 586: * @throws IllegalArgumentException if b is not one of the above listed 587: * valid values 588: */ 589: public Shape modelToView(int pos, Shape a, Position.Bias b) 590: throws BadLocationException 591: { 592: GlyphPainter p = getGlyphPainter(); 593: return p.modelToView(this, pos, b, a); 594: } 595: 596: /** 597: * Maps coordinates from the <code>View</code>'s space into a position 598: * in the document model. 599: * 600: * @param x the x coordinate in the view space 601: * @param y the y coordinate in the view space 602: * @param a the allocation of this <code>View</code> 603: * @param b the bias to use 604: * 605: * @return the position in the document that corresponds to the screen 606: * coordinates <code>x, y</code> 607: */ 608: public int viewToModel(float x, float y, Shape a, Position.Bias[] b) 609: { 610: checkPainter(); 611: GlyphPainter painter = getGlyphPainter(); 612: return painter.viewToModel(this, x, y, a, b); 613: } 614: 615: /** 616: * Return the {@link TabExpander} to use. 617: * 618: * @return the {@link TabExpander} to use 619: */ 620: public TabExpander getTabExpander() 621: { 622: TabExpander te = null; 623: View parent = getParent(); 624: 625: if (parent instanceof TabExpander) 626: te = (TabExpander) parent; 627: 628: return te; 629: } 630: 631: /** 632: * Returns the preferred span of this view for tab expansion. 633: * 634: * @param x the location of the view 635: * @param te the tab expander to use 636: * 637: * @return the preferred span of this view for tab expansion 638: */ 639: public float getTabbedSpan(float x, TabExpander te) 640: { 641: Element el = getElement(); 642: return getGlyphPainter().getSpan(this, el.getStartOffset(), 643: el.getEndOffset(), te, x); 644: } 645: 646: /** 647: * Returns the span of a portion of the view. This is used in TAB expansion 648: * for fragments that don't contain TABs. 649: * 650: * @param p0 the start index 651: * @param p1 the end index 652: * 653: * @return the span of the specified portion of the view 654: */ 655: public float getPartialSpan(int p0, int p1) 656: { 657: Element el = getElement(); 658: Document doc = el.getDocument(); 659: Segment seg = new Segment(); 660: try 661: { 662: doc.getText(p0, p1 - p0, seg); 663: } 664: catch (BadLocationException ex) 665: { 666: AssertionError ae; 667: ae = new AssertionError("BadLocationException must not be thrown " 668: + "here"); 669: ae.initCause(ex); 670: throw ae; 671: } 672: FontMetrics fm = null; // Fetch font metrics somewhere. 673: return Utilities.getTabbedTextWidth(seg, fm, 0, null, p0); 674: } 675: 676: /** 677: * Returns the start offset in the document model of the portion 678: * of text that this view is responsible for. 679: * 680: * @return the start offset in the document model of the portion 681: * of text that this view is responsible for 682: */ 683: public int getStartOffset() 684: { 685: return startOffset; 686: } 687: 688: /** 689: * Returns the end offset in the document model of the portion 690: * of text that this view is responsible for. 691: * 692: * @return the end offset in the document model of the portion 693: * of text that this view is responsible for 694: */ 695: public int getEndOffset() 696: { 697: return endOffset; 698: } 699: 700: /** 701: * Returns the text segment that this view is responsible for. 702: * 703: * @param p0 the start index in the document model 704: * @param p1 the end index in the document model 705: * 706: * @return the text segment that this view is responsible for 707: */ 708: public Segment getText(int p0, int p1) 709: { 710: Segment txt = new Segment(); 711: try 712: { 713: getDocument().getText(p0, p1 - p0, txt); 714: } 715: catch (BadLocationException ex) 716: { 717: AssertionError ae; 718: ae = new AssertionError("BadLocationException should not be " 719: + "thrown here. p0 = " + p0 + ", p1 = " + p1); 720: ae.initCause(ex); 721: throw ae; 722: } 723: 724: return txt; 725: } 726: 727: /** 728: * Returns the font for the text run for which this <code>GlyphView</code> 729: * is responsible. 730: * 731: * @return the font for the text run for which this <code>GlyphView</code> 732: * is responsible 733: */ 734: public Font getFont() 735: { 736: Element el = getElement(); 737: AttributeSet atts = el.getAttributes(); 738: String family = StyleConstants.getFontFamily(atts); 739: int size = StyleConstants.getFontSize(atts); 740: int style = Font.PLAIN; 741: if (StyleConstants.isBold(atts)) 742: style |= Font.BOLD; 743: if (StyleConstants.isItalic(atts)) 744: style |= Font.ITALIC; 745: Font font = new Font(family, style, size); 746: return font; 747: } 748: 749: /** 750: * Returns the foreground color which should be used to paint the text. 751: * This is fetched from the associated element's text attributes using 752: * {@link StyleConstants#getForeground}. 753: * 754: * @return the foreground color which should be used to paint the text 755: */ 756: public Color getForeground() 757: { 758: Element el = getElement(); 759: AttributeSet atts = el.getAttributes(); 760: return StyleConstants.getForeground(atts); 761: } 762: 763: /** 764: * Returns the background color which should be used to paint the text. 765: * This is fetched from the associated element's text attributes using 766: * {@link StyleConstants#getBackground}. 767: * 768: * @return the background color which should be used to paint the text 769: */ 770: public Color getBackground() 771: { 772: Element el = getElement(); 773: AttributeSet atts = el.getAttributes(); 774: return StyleConstants.getBackground(atts); 775: } 776: 777: /** 778: * Determines whether the text should be rendered strike-through or not. This 779: * is determined using the method 780: * {@link StyleConstants#isStrikeThrough(AttributeSet)} on the element of 781: * this view. 782: * 783: * @return whether the text should be rendered strike-through or not 784: */ 785: public boolean isStikeThrough() 786: { 787: Element el = getElement(); 788: AttributeSet atts = el.getAttributes(); 789: return StyleConstants.isStrikeThrough(atts); 790: } 791: 792: /** 793: * Determines whether the text should be rendered as subscript or not. This 794: * is determined using the method 795: * {@link StyleConstants#isSubscript(AttributeSet)} on the element of 796: * this view. 797: * 798: * @return whether the text should be rendered as subscript or not 799: */ 800: public boolean isSubscript() 801: { 802: Element el = getElement(); 803: AttributeSet atts = el.getAttributes(); 804: return StyleConstants.isSubscript(atts); 805: } 806: 807: /** 808: * Determines whether the text should be rendered as superscript or not. This 809: * is determined using the method 810: * {@link StyleConstants#isSuperscript(AttributeSet)} on the element of 811: * this view. 812: * 813: * @return whether the text should be rendered as superscript or not 814: */ 815: public boolean isSuperscript() 816: { 817: Element el = getElement(); 818: AttributeSet atts = el.getAttributes(); 819: return StyleConstants.isSuperscript(atts); 820: } 821: 822: /** 823: * Determines whether the text should be rendered as underlined or not. This 824: * is determined using the method 825: * {@link StyleConstants#isUnderline(AttributeSet)} on the element of 826: * this view. 827: * 828: * @return whether the text should be rendered as underlined or not 829: */ 830: public boolean isUnderline() 831: { 832: Element el = getElement(); 833: AttributeSet atts = el.getAttributes(); 834: return StyleConstants.isUnderline(atts); 835: } 836: 837: /** 838: * Creates and returns a shallow clone of this GlyphView. This is used by 839: * the {@link #createFragment} and {@link #breakView} methods. 840: * 841: * @return a shallow clone of this GlyphView 842: */ 843: protected final Object clone() 844: { 845: try 846: { 847: return super.clone(); 848: } 849: catch (CloneNotSupportedException ex) 850: { 851: AssertionError err = new AssertionError("CloneNotSupportedException " 852: + "must not be thrown here"); 853: err.initCause(ex); 854: throw err; 855: } 856: } 857: 858: /** 859: * Tries to break the view near the specified view span <code>len</code>. 860: * The glyph view can only be broken in the X direction. For Y direction it 861: * returns itself. 862: * 863: * @param axis the axis for breaking, may be {@link View#X_AXIS} or 864: * {@link View#Y_AXIS} 865: * @param p0 the model location where the fragment should start 866: * @param pos the view position along the axis where the fragment starts 867: * @param len the desired length of the fragment view 868: * 869: * @return the fragment view, or <code>this</code> if breaking was not 870: * possible 871: */ 872: public View breakView(int axis, int p0, float pos, float len) 873: { 874: if (axis == Y_AXIS) 875: return this; 876: 877: checkPainter(); 878: GlyphPainter painter = getGlyphPainter(); 879: int breakLocation = painter.getBoundedPosition(this, p0, pos, len); 880: // Try to find a suitable line break. 881: BreakIterator lineBreaker = BreakIterator.getLineInstance(); 882: Segment txt = new Segment(); 883: try 884: { 885: getDocument().getText(getStartOffset(), getEndOffset(), txt); 886: } 887: catch (BadLocationException ex) 888: { 889: AssertionError err = new AssertionError("BadLocationException must not " 890: + "be thrown here."); 891: err.initCause(ex); 892: throw err; 893: } 894: lineBreaker.setText(txt); 895: int goodBreakLocation = lineBreaker.previous(); 896: if (goodBreakLocation != BreakIterator.DONE) 897: breakLocation = goodBreakLocation; 898: 899: View brokenView = createFragment(p0, breakLocation); 900: return brokenView; 901: } 902: 903: /** 904: * Determines how well the specified view location is suitable for inserting 905: * a line break. If <code>axis</code> is <code>View.Y_AXIS</code>, then 906: * this method forwards to the superclass, if <code>axis</code> is 907: * <code>View.X_AXIS</code> then this method returns 908: * {@link View#ExcellentBreakWeight} if there is a suitable break location 909: * (usually whitespace) within the specified view span, or 910: * {@link View#GoodBreakWeight} if not. 911: * 912: * @param axis the axis along which the break weight is requested 913: * @param pos the starting view location 914: * @param len the length of the span at which the view should be broken 915: * 916: * @return the break weight 917: */ 918: public int getBreakWeight(int axis, float pos, float len) 919: { 920: int weight; 921: if (axis == Y_AXIS) 922: weight = super.getBreakWeight(axis, pos, len); 923: else 924: { 925: // Determine the model locations at pos and pos + len. 926: int spanX = (int) getPreferredSpan(X_AXIS); 927: int spanY = (int) getPreferredSpan(Y_AXIS); 928: Rectangle dummyAlloc = new Rectangle(0, 0, spanX, spanY); 929: Position.Bias[] biasRet = new Position.Bias[1]; 930: int offset1 = viewToModel(pos, spanY / 2, dummyAlloc, biasRet); 931: int offset2 = viewToModel(pos, spanY / 2, dummyAlloc, biasRet); 932: Segment txt = getText(offset1, offset2); 933: BreakIterator lineBreaker = BreakIterator.getLineInstance(); 934: lineBreaker.setText(txt); 935: int breakLoc = lineBreaker.previous(); 936: if (breakLoc == offset1) 937: weight = View.BadBreakWeight; 938: else if(breakLoc == BreakIterator.DONE) 939: weight = View.GoodBreakWeight; 940: else 941: weight = View.ExcellentBreakWeight; 942: } 943: return weight; 944: } 945: 946: /** 947: * Receives notification that some text attributes have changed within the 948: * text fragment that this view is responsible for. This calls 949: * {@link View#preferenceChanged(View, boolean, boolean)} on the parent for 950: * both width and height. 951: * 952: * @param e the document event describing the change; not used here 953: * @param a the view allocation on screen; not used here 954: * @param vf the view factory; not used here 955: */ 956: public void changedUpdate(DocumentEvent e, Shape a, ViewFactory vf) 957: { 958: getParent().preferenceChanged(this, true, true); 959: } 960: 961: /** 962: * Receives notification that some text has been inserted within the 963: * text fragment that this view is responsible for. This calls 964: * {@link View#preferenceChanged(View, boolean, boolean)} on the parent for 965: * width. 966: * 967: * @param e the document event describing the change; not used here 968: * @param a the view allocation on screen; not used here 969: * @param vf the view factory; not used here 970: */ 971: public void insertUpdate(DocumentEvent e, Shape a, ViewFactory vf) 972: { 973: getParent().preferenceChanged(this, true, false); 974: } 975: 976: /** 977: * Receives notification that some text has been removed within the 978: * text fragment that this view is responsible for. This calls 979: * {@link View#preferenceChanged(View, boolean, boolean)} on the parent for 980: * width. 981: * 982: * @param e the document event describing the change; not used here 983: * @param a the view allocation on screen; not used here 984: * @param vf the view factory; not used here 985: */ 986: public void removeUpdate(DocumentEvent e, Shape a, ViewFactory vf) 987: { 988: getParent().preferenceChanged(this, true, false); 989: } 990: 991: /** 992: * Creates a fragment view of this view that starts at <code>p0</code> and 993: * ends at <code>p1</code>. 994: * 995: * @param p0 the start location for the fragment view 996: * @param p1 the end location for the fragment view 997: * 998: * @return the fragment view 999: */ 1000: public View createFragment(int p0, int p1) 1001: { 1002: GlyphView fragment = (GlyphView) clone(); 1003: fragment.startOffset = p0; 1004: fragment.endOffset = p1; 1005: return fragment; 1006: } 1007: 1008: /** 1009: * Returns the alignment of this view along the specified axis. For the Y 1010: * axis this is <code>(height - descent) / height</code> for the used font, 1011: * so that it is aligned along the baseline. 1012: * For the X axis the superclass is called. 1013: */ 1014: public float getAlignment(int axis) 1015: { 1016: float align; 1017: if (axis == Y_AXIS) 1018: { 1019: checkPainter(); 1020: GlyphPainter painter = getGlyphPainter(); 1021: float height = painter.getHeight(this); 1022: float descent = painter.getDescent(this); 1023: align = (height - descent) / height; 1024: } 1025: else 1026: align = super.getAlignment(axis); 1027: 1028: return align; 1029: } 1030: 1031: /** 1032: * Returns the model location that should be used to place a caret when 1033: * moving the caret through the document. 1034: * 1035: * @param pos the current model location 1036: * @param bias the bias for <code>p</code> 1037: * @param a the allocated region for the glyph view 1038: * @param direction the direction from the current position; Must be one of 1039: * {@link SwingConstants#EAST}, {@link SwingConstants#WEST}, 1040: * {@link SwingConstants#NORTH} or {@link SwingConstants#SOUTH} 1041: * @param biasRet filled with the bias of the resulting location when method 1042: * returns 1043: * 1044: * @return the location within the document that should be used to place the 1045: * caret when moving the caret around the document 1046: * 1047: * @throws BadLocationException if <code>pos</code> is an invalid model 1048: * location 1049: * @throws IllegalArgumentException if <code>d</code> is invalid 1050: */ 1051: public int getNextVisualPositionFrom(int pos, Position.Bias bias, Shape a, 1052: int direction, Position.Bias[] biasRet) 1053: throws BadLocationException 1054: { 1055: checkPainter(); 1056: GlyphPainter painter = getGlyphPainter(); 1057: return painter.getNextVisualPositionFrom(this, pos, bias, a, direction, 1058: biasRet); 1059: } 1060: }
GNU Classpath (0.20) |