Source for javax.swing.text.GlyphView

   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: 
  48: /**
  49:  * Renders a run of styled text. This {@link View} subclass paints the
  50:  * characters of the <code>Element</code> it is responsible for using
  51:  * the style information from that <code>Element</code>.
  52:  *
  53:  * @author Roman Kennke (roman@kennke.org)
  54:  */
  55: public class GlyphView
  56:   extends View
  57:   implements TabableView, Cloneable
  58: {
  59: 
  60:   /**
  61:    * An abstract base implementation for a glyph painter for
  62:    * <code>GlyphView</code>.
  63:    */
  64:   public abstract static class GlyphPainter
  65:   {
  66:     /**
  67:      * Creates a new <code>GlyphPainer</code>.
  68:      */
  69:     public GlyphPainter()
  70:     {
  71:     }
  72: 
  73:     /**
  74:      * Returns the full height of the rendered text.
  75:      *
  76:      * @return the full height of the rendered text
  77:      */
  78:     public abstract float getHeight(GlyphView view);
  79:     
  80:     /**
  81:      * Paints the glyphs.
  82:      *
  83:      * @param view the glyph view to paint
  84:      * @param g the graphics context to use for painting
  85:      * @param a the allocation of the glyph view
  86:      * @param p0 the start position (in the model) from which to paint
  87:      * @param p1 the end position (in the model) to which to paint
  88:      */
  89:     public abstract void paint(GlyphView view, Graphics g, Shape a, int p0,
  90:                                int p1);
  91: 
  92:     /**
  93:      * Maps a position in the document into the coordinate space of the View.
  94:      * The output rectangle usually reflects the font height but has a width
  95:      * of zero.
  96:      *
  97:      * @param view the glyph view
  98:      * @param pos the position of the character in the model
  99:      * @param a the area that is occupied by the view
 100:      * @param bias either {@link Position.Bias.Forward} or
 101:      *        {@link Position.Bias.Backward} depending on the preferred
 102:      *        direction bias. If <code>null</code> this defaults to
 103:      *        <code>Position.Bias.Forward</code>
 104:      *
 105:      * @return a rectangle that gives the location of the document position
 106:      *         inside the view coordinate space
 107:      *
 108:      * @throws BadLocationException if <code>pos</code> is invalid
 109:      * @throws IllegalArgumentException if b is not one of the above listed
 110:      *         valid values
 111:      */
 112:     public abstract Shape modelToView(GlyphView view, int pos, Position.Bias b,
 113:                                       Shape a)
 114:       throws BadLocationException;
 115: 
 116:     /**
 117:      * Determine the span of the glyphs from location <code>p0</code> to
 118:      * location <code>p1</code>. If <code>te</code> is not <code>null</code>,
 119:      * then TABs are expanded using this <code>TabExpander</code>.
 120:      * The parameter <code>x</code> is the location at which the view is
 121:      * located (this is important when using TAB expansion).
 122:      *
 123:      * @param view the glyph view
 124:      * @param p0 the starting location in the document model
 125:      * @param p0 the end location in the document model
 126:      * @param te the tab expander to use
 127:      * @param x the location at which the view is located
 128:      *
 129:      * @return the span of the glyphs from location <code>p0</code> to
 130:      *         location <code>p1</code>, possibly using TAB expansion
 131:      */
 132:     public abstract float getSpan(GlyphView view, int p0, int p1,
 133:                                   TabExpander te, float x);
 134: 
 135:   }
 136: 
 137:   /**
 138:    * The default <code>GlyphPainter</code> used in <code>GlyphView</code>.
 139:    */
 140:   static class DefaultGlyphPainter extends GlyphPainter
 141:   {
 142:     /**
 143:      * Returns the full height of the rendered text.
 144:      *
 145:      * @return the full height of the rendered text
 146:      */
 147:     public float getHeight(GlyphView view)
 148:     {
 149:       Font font = view.getFont();
 150:       FontMetrics metrics = view.getContainer().getFontMetrics(font);
 151:       float height = metrics.getHeight();
 152:       return height;
 153:     }
 154:     
 155:     /**
 156:      * Paints the glyphs.
 157:      *
 158:      * @param view the glyph view to paint
 159:      * @param g the graphics context to use for painting
 160:      * @param a the allocation of the glyph view
 161:      * @param p0 the start position (in the model) from which to paint
 162:      * @param p1 the end position (in the model) to which to paint
 163:      */
 164:     public void paint(GlyphView view, Graphics g, Shape a, int p0,
 165:                       int p1)
 166:     {
 167:       int height = (int) getHeight(view);
 168:       Segment txt = view.getText(p0, p1);
 169:       Rectangle bounds = a.getBounds();
 170: 
 171:       TabExpander tabEx = null;
 172:       View parent = view.getParent();
 173:       if (parent instanceof TabExpander)
 174:         tabEx = (TabExpander) parent;
 175: 
 176:       // FIXME: Set character attributes like font-family, font-size, colors.
 177:       Color foreground = view.getForeground();
 178:       g.setColor(foreground);
 179:       Utilities.drawTabbedText(txt, bounds.x, bounds.y, g, tabEx,
 180:                                txt.offset);
 181:     }
 182: 
 183:     /**
 184:      * Maps a position in the document into the coordinate space of the View.
 185:      * The output rectangle usually reflects the font height but has a width
 186:      * of zero.
 187:      *
 188:      * @param view the glyph view
 189:      * @param pos the position of the character in the model
 190:      * @param a the area that is occupied by the view
 191:      * @param bias either {@link Position.Bias.Forward} or
 192:      *        {@link Position.Bias.Backward} depending on the preferred
 193:      *        direction bias. If <code>null</code> this defaults to
 194:      *        <code>Position.Bias.Forward</code>
 195:      *
 196:      * @return a rectangle that gives the location of the document position
 197:      *         inside the view coordinate space
 198:      *
 199:      * @throws BadLocationException if <code>pos</code> is invalid
 200:      * @throws IllegalArgumentException if b is not one of the above listed
 201:      *         valid values
 202:      */
 203:     public Shape modelToView(GlyphView view, int pos, Position.Bias b,
 204:                              Shape a)
 205:       throws BadLocationException
 206:     {
 207:       Element el = view.getElement();
 208:       Font font = view.getFont();
 209:       FontMetrics fm = view.getContainer().getFontMetrics(font);
 210:       Segment txt = view.getText(el.getStartOffset(), pos);
 211:       int width = fm.charsWidth(txt.array, txt.offset, txt.count);
 212:       int height = fm.getHeight();
 213:       Rectangle bounds = a.getBounds();
 214:       Rectangle result = new Rectangle(bounds.x + width, bounds.y,
 215:                                        bounds.x + width, height);
 216:       return result;
 217:     }
 218: 
 219:     /**
 220:      * Determine the span of the glyphs from location <code>p0</code> to
 221:      * location <code>p1</code>. If <code>te</code> is not <code>null</code>,
 222:      * then TABs are expanded using this <code>TabExpander</code>.
 223:      * The parameter <code>x</code> is the location at which the view is
 224:      * located (this is important when using TAB expansion).
 225:      *
 226:      * @param view the glyph view
 227:      * @param p0 the starting location in the document model
 228:      * @param p0 the end location in the document model
 229:      * @param te the tab expander to use
 230:      * @param x the location at which the view is located
 231:      *
 232:      * @return the span of the glyphs from location <code>p0</code> to
 233:      *         location <code>p1</code>, possibly using TAB expansion
 234:      */
 235:     public float getSpan(GlyphView view, int p0, int p1,
 236:                          TabExpander te, float x)
 237:     {
 238:       Element el = view.getElement();
 239:       Font font = view.getFont();
 240:       FontMetrics fm = view.getContainer().getFontMetrics(font);
 241:       Segment txt = view.getText(p0, p1);
 242:       int span = Utilities.getTabbedTextWidth(txt, fm, (int) x, te, p0);
 243:       return span;
 244:     }
 245:   }
 246: 
 247:   /**
 248:    * The GlyphPainer used for painting the glyphs.
 249:    */
 250:   GlyphPainter glyphPainter;
 251: 
 252:   /**
 253:    * Creates a new <code>GlyphView</code> for the given <code>Element</code>.
 254:    *
 255:    * @param element the element that is rendered by this GlyphView
 256:    */
 257:   public GlyphView(Element element)
 258:   {
 259:     super(element);
 260:   }
 261: 
 262:   /**
 263:    * Returns the <code>GlyphPainter</code> that is used by this
 264:    * <code>GlyphView</code>. If no <code>GlyphPainer</code> has been installed
 265:    * <code>null</code> is returned.
 266:    *
 267:    * @return the glyph painter that is used by this
 268:    *         glyph view or <code>null</code> if no glyph painter has been
 269:    *         installed
 270:    */
 271:   public GlyphPainter getGlyphPainter()
 272:   {
 273:     return glyphPainter;
 274:   }
 275: 
 276:   /**
 277:    * Sets the {@link GlyphPainter} to be used for this <code>GlyphView</code>.
 278:    *
 279:    * @param painter the glyph painter to be used for this glyph view
 280:    */
 281:   public void setGlyphPainter(GlyphPainter painter)
 282:   {
 283:     glyphPainter = painter;
 284:   }
 285: 
 286:   /**
 287:    * Checks if a <code>GlyphPainer</code> is installed. If this is not the
 288:    * case, a default painter is installed.
 289:    */
 290:   protected void checkPainter()
 291:   {
 292:     if (glyphPainter == null)
 293:       glyphPainter = new DefaultGlyphPainter();
 294:   }
 295: 
 296:   /**
 297:    * Renders the <code>Element</code> that is associated with this
 298:    * <code>View</code>.
 299:    *
 300:    * @param g the <code>Graphics</code> context to render to
 301:    * @param a the allocated region for the <code>Element</code>
 302:    */
 303:   public void paint(Graphics g, Shape a)
 304:   {
 305:     Element el = getElement();
 306:     checkPainter();
 307:     getGlyphPainter().paint(this, g, a, el.getStartOffset(),
 308:                             el.getEndOffset());
 309:   }
 310: 
 311: 
 312:   /**
 313:    * Returns the preferred span of the content managed by this
 314:    * <code>View</code> along the specified <code>axis</code>.
 315:    *
 316:    * @param axis the axis
 317:    *
 318:    * @return the preferred span of this <code>View</code>.
 319:    */
 320:   public float getPreferredSpan(int axis)
 321:   {
 322:     Element el = getElement();
 323:     checkPainter();
 324:     GlyphPainter painter = getGlyphPainter();
 325:     TabExpander tabEx = null;
 326:     View parent = getParent();
 327:     if (parent instanceof TabExpander)
 328:       tabEx = (TabExpander) parent;
 329:     // FIXME: Figure out how to determine the x parameter.
 330:     float span = painter.getSpan(this, el.getStartOffset(), el.getEndOffset(),
 331:                                  tabEx, 0.F);
 332:     return span;
 333:   }
 334: 
 335:   /**
 336:    * Maps a position in the document into the coordinate space of the View.
 337:    * The output rectangle usually reflects the font height but has a width
 338:    * of zero.
 339:    *
 340:    * @param pos the position of the character in the model
 341:    * @param a the area that is occupied by the view
 342:    * @param b either {@link Position.Bias#Forward} or
 343:    *        {@link Position.Bias#Backward} depending on the preferred
 344:    *        direction bias. If <code>null</code> this defaults to
 345:    *        <code>Position.Bias.Forward</code>
 346:    *
 347:    * @return a rectangle that gives the location of the document position
 348:    *         inside the view coordinate space
 349:    *
 350:    * @throws BadLocationException if <code>pos</code> is invalid
 351:    * @throws IllegalArgumentException if b is not one of the above listed
 352:    *         valid values
 353:    */
 354:   public Shape modelToView(int pos, Shape a, Position.Bias b)
 355:     throws BadLocationException
 356:   {
 357:     GlyphPainter p = getGlyphPainter();
 358:     return p.modelToView(this, pos, b, a);
 359:   }
 360: 
 361:   /**
 362:    * Maps coordinates from the <code>View</code>'s space into a position
 363:    * in the document model.
 364:    *
 365:    * @param x the x coordinate in the view space
 366:    * @param y the y coordinate in the view space
 367:    * @param a the allocation of this <code>View</code>
 368:    * @param b the bias to use
 369:    *
 370:    * @return the position in the document that corresponds to the screen
 371:    *         coordinates <code>x, y</code>
 372:    */
 373:   public int viewToModel(float x, float y, Shape a, Position.Bias[] b)
 374:   {
 375:     // FIXME: not implemented
 376:     return 0;
 377:   }
 378: 
 379:   /**
 380:    * Return the {@link TabExpander} to use.
 381:    *
 382:    * @return the {@link TabExpander} to use
 383:    */
 384:   public TabExpander getTabExpander()
 385:   {
 386:     // TODO: Figure out if this is correct.
 387:     TabExpander te = null;
 388:     View parent = getParent();
 389: 
 390:     if (parent instanceof ParagraphView)
 391:       te = (ParagraphView) parent;
 392:     
 393:     return te;
 394:   }
 395: 
 396:   /**
 397:    * Returns the preferred span of this view for tab expansion.
 398:    *
 399:    * @param x the location of the view
 400:    * @param te the tab expander to use
 401:    *
 402:    * @return the preferred span of this view for tab expansion
 403:    */
 404:   public float getTabbedSpan(float x, TabExpander te)
 405:   {
 406:     Element el = getElement();
 407:     return getGlyphPainter().getSpan(this, el.getStartOffset(),
 408:                                      el.getEndOffset(), te, x);
 409:   }
 410: 
 411:   /**
 412:    * Returns the span of a portion of the view. This is used in TAB expansion
 413:    * for fragments that don't contain TABs.
 414:    *
 415:    * @param p0 the start index
 416:    * @param p1 the end index
 417:    *
 418:    * @return the span of the specified portion of the view
 419:    */
 420:   public float getPartialSpan(int p0, int p1)
 421:   {
 422:     Element el = getElement();
 423:     Document doc = el.getDocument();
 424:     Segment seg = new Segment();
 425:     try
 426:       {
 427:         doc.getText(p0, p1 - p0, seg);
 428:       }
 429:     catch (BadLocationException ex)
 430:       {
 431:         throw new AssertionError("BadLocationException must not be thrown "
 432:                                  + "here");
 433:       }
 434:     FontMetrics fm = null; // Fetch font metrics somewhere.
 435:     return Utilities.getTabbedTextWidth(seg, fm, 0, null, p0);
 436:   }
 437: 
 438:   /**
 439:    * Returns the starting offset in the document model of the portion
 440:    * of text that this view is responsible for.
 441:    *
 442:    * @return the starting offset in the document model of the portion
 443:    *         of text that this view is responsible for
 444:    */
 445:   public int getBeginIndex()
 446:   {
 447:     return getElement().getStartOffset();
 448:   }
 449: 
 450:   /**
 451:    * Returns the end offset in the document model of the portion
 452:    * of text that this view is responsible for.
 453:    *
 454:    * @return the end offset in the document model of the portion
 455:    *         of text that this view is responsible for
 456:    */
 457:   public int getEndIndex()
 458:   {
 459:     return getElement().getEndOffset();
 460:   }
 461: 
 462:   /**
 463:    * Returns the text segment that this view is responsible for.
 464:    *
 465:    * @param p0 the start index in the document model
 466:    * @param p1 the end index in the document model
 467:    *
 468:    * @return the text segment that this view is responsible for
 469:    */
 470:   public Segment getText(int p0, int p1)
 471:   {
 472:     Segment txt = new Segment();
 473:     try
 474:       {
 475:         getDocument().getText(p0, p1 - p0, txt);
 476:       }
 477:     catch (BadLocationException ex)
 478:       {
 479:         throw new AssertionError("BadLocationException should not be "
 480:                                  + "thrown here. p0 = " + p0 + ", p1 = " + p1);
 481:       }
 482: 
 483:     return txt;
 484:   }
 485: 
 486:   /**
 487:    * Returns the font for the text run for which this <code>GlyphView</code>
 488:    * is responsible.
 489:    *
 490:    * @return the font for the text run for which this <code>GlyphView</code>
 491:    *         is responsible
 492:    */
 493:   public Font getFont()
 494:   {
 495:     Element el = getElement();
 496:     AttributeSet atts = el.getAttributes();
 497:     String family = StyleConstants.getFontFamily(atts);
 498:     int size = StyleConstants.getFontSize(atts);
 499:     int style = Font.PLAIN;
 500:     if (StyleConstants.isBold(atts))
 501:         style |= Font.BOLD;
 502:     if (StyleConstants.isItalic(atts))
 503:       style |= Font.ITALIC;
 504:     Font font = new Font(family, style, size);
 505:     return font;
 506:   }
 507: 
 508:   /**
 509:    * Returns the foreground color which should be used to paint the text.
 510:    * This is fetched from the associated element's text attributes using
 511:    * {@link StyleConstants#getForeground}.
 512:    *
 513:    * @return the foreground color which should be used to paint the text
 514:    */
 515:   public Color getForeground()
 516:   {
 517:     Element el = getElement();
 518:     AttributeSet atts = el.getAttributes();
 519:     return StyleConstants.getForeground(atts);
 520:   }
 521: }