Source for javax.swing.text.View

   1: /* View.java -- 
   2:    Copyright (C) 2002, 2004, 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.Container;
  42: import java.awt.Graphics;
  43: import java.awt.Rectangle;
  44: import java.awt.Shape;
  45: 
  46: import javax.swing.JComponent;
  47: import javax.swing.SwingConstants;
  48: import javax.swing.event.DocumentEvent;
  49: 
  50: public abstract class View implements SwingConstants
  51: {
  52:   public static final int BadBreakWeight = 0;
  53:   public static final int ExcellentBreakWeight = 2000;
  54:   public static final int ForcedBreakWeight = 3000;
  55:   public static final int GoodBreakWeight = 1000;
  56: 
  57:   public static final int X_AXIS = 0;
  58:   public static final int Y_AXIS = 1;
  59:     
  60:   private float width, height;
  61:   private Element elt;
  62:   private View parent;
  63: 
  64:   /**
  65:    * Creates a new <code>View</code> instance.
  66:    *
  67:    * @param elem an <code>Element</code> value
  68:    */
  69:   public View(Element elem)
  70:   {
  71:     elt = elem;
  72:   }
  73: 
  74:   public abstract void paint(Graphics g, Shape s);
  75: 
  76:   public void setParent(View parent)
  77:   {
  78:     this.parent = parent;
  79:   }
  80:     
  81:   public View getParent()
  82:   {
  83:     return parent;
  84:   }
  85:     
  86:   public Container getContainer()
  87:   {
  88:     View parent = getParent();
  89:     if (parent == null)
  90:       throw new AssertionError("The parent of a View must not be null.");
  91: 
  92:     return parent.getContainer();
  93:   }
  94:   
  95:   public Document getDocument()
  96:   {
  97:     return getElement().getDocument();
  98:   }
  99:     
 100:   public Element getElement()
 101:   {
 102:     return elt;
 103:   }
 104: 
 105:   public abstract float getPreferredSpan(int axis);
 106: 
 107:   public int getResizeWeight(int axis)
 108:   {
 109:     return 0;
 110:   }
 111: 
 112:   public float getMaximumSpan(int axis)
 113:   {
 114:     if (getResizeWeight(axis) <= 0)
 115:       return getPreferredSpan(axis);
 116: 
 117:     return Integer.MAX_VALUE;
 118:   }
 119: 
 120:   public float getMinimumSpan(int axis)
 121:   {
 122:     if (getResizeWeight(axis) <= 0)
 123:       return getPreferredSpan(axis);
 124: 
 125:     return Integer.MAX_VALUE;
 126:   }
 127:   
 128:   public void setSize(float width, float height)
 129:   {
 130:     // The default implementation does nothing.
 131:   }
 132:   
 133:   public float getAlignment(int axis)
 134:   {
 135:     return 0.5f;
 136:   }
 137: 
 138:   public AttributeSet getAttributes()
 139:   {
 140:     return getElement().getAttributes();
 141:   }
 142:   
 143:   public boolean isVisible()
 144:   {
 145:     return true;
 146:   }
 147: 
 148:   public int getViewCount()
 149:   {
 150:     return 0;
 151:   }
 152:   
 153:   public View getView(int index)
 154:   {
 155:     return null;
 156:   }
 157: 
 158:   public ViewFactory getViewFactory()
 159:   {
 160:     View parent = getParent();
 161:     return parent != null ? parent.getViewFactory() : null;
 162:   }
 163: 
 164:   public void replace(int offset, int length, View[] views)
 165:   {
 166:     // Default implementation does nothing.
 167:   }
 168: 
 169:   public void insert(int offset, View view)
 170:   {
 171:     View[] array = { view };
 172:     replace(offset, 1, array);
 173:   }
 174: 
 175:   public void append(View view)
 176:   {
 177:     View[] array = { view };
 178:     int offset = getViewCount();
 179:     replace(offset, 0, array);
 180:   }
 181: 
 182:   public void removeAll()
 183:   {
 184:     replace(0, getViewCount(), new View[0]); 
 185:   }
 186: 
 187:   public void remove(int index)
 188:   {
 189:     replace(index, 1, null); 
 190:   }
 191: 
 192:   public View createFragment(int p0, int p1)
 193:   {
 194:     // The default implementation doesn't support fragmentation.
 195:     return this;
 196:   }
 197: 
 198:   public int getStartOffset()
 199:   {
 200:     return getElement().getStartOffset();
 201:   }
 202: 
 203:   public int getEndOffset()
 204:   {
 205:     return getElement().getEndOffset();
 206:   }
 207: 
 208:   public Shape getChildAllocation(int index, Shape a)
 209:   {
 210:     return null;
 211:   }
 212:   
 213:   /**
 214:    * @since 1.4
 215:    */
 216:   public int getViewIndex(float x, float y, Shape allocation)
 217:   {
 218:     return -1;
 219:   }
 220:   
 221:   /**
 222:    * @since 1.4
 223:    */
 224:   public String getToolTipText(float x, float y, Shape allocation)
 225:   {
 226:     int index = getViewIndex(x, y, allocation);
 227: 
 228:     if (index < -1)
 229:       return null;
 230: 
 231:     Shape childAllocation = getChildAllocation(index, allocation);
 232: 
 233:     if (childAllocation.getBounds().contains(x, y))
 234:       return getView(index).getToolTipText(x, y, childAllocation);
 235: 
 236:     return null;
 237:   }
 238: 
 239:   /**
 240:    * @since 1.3
 241:    */
 242:   public Graphics getGraphics()
 243:   {
 244:     return getContainer().getGraphics();
 245:   }
 246: 
 247:   public void preferenceChanged(View child, boolean width, boolean height)
 248:   {
 249:     if (parent != null)
 250:       parent.preferenceChanged(this, width, height);
 251:   }
 252: 
 253:   public int getBreakWeight(int axis, float pos, float len)
 254:   {
 255:     return BadBreakWeight;
 256:   }
 257: 
 258:   public View breakView(int axis, int offset, float pos, float len)
 259:   {
 260:     return this;
 261:   }
 262: 
 263:   /**
 264:    * @since 1.3
 265:    */
 266:   public int getViewIndex(int pos, Position.Bias b)
 267:   {
 268:     return -1;
 269:   }
 270: 
 271:   /**
 272:    * Receive notification about an insert update to the text model.
 273:    *
 274:    * The default implementation of this method does the following:
 275:    * <ul>
 276:    * <li>Call {@link #updateChildren} if the element that this view is
 277:    * responsible for has changed. This makes sure that the children can
 278:    * correctly represent the model.<li>
 279:    * <li>Call {@link #forwardUpdate}. This forwards the DocumentEvent to
 280:    * the child views.<li>
 281:    * <li>Call {@link #updateLayout}. Gives the view a chance to either
 282:    * repair its layout, reschedule layout or do nothing at all.</li>
 283:    * </ul>
 284:    *
 285:    * @param ev the DocumentEvent that describes the change
 286:    * @param shape the shape of the view
 287:    * @param vf the ViewFactory for creating child views
 288:    */
 289:   public void insertUpdate(DocumentEvent ev, Shape shape, ViewFactory vf)
 290:   {
 291:     Element el = getElement();
 292:     DocumentEvent.ElementChange ec = ev.getChange(el);
 293:     if (ec != null)
 294:       updateChildren(ec, ev, vf);
 295:     forwardUpdate(ec, ev, shape, vf);
 296:     updateLayout(ec, ev, shape);
 297:   }
 298: 
 299:   /**
 300:    * Receive notification about a remove update to the text model.
 301:    *
 302:    * The default implementation of this method does the following:
 303:    * <ul>
 304:    * <li>Call {@link #updateChildren} if the element that this view is
 305:    * responsible for has changed. This makes sure that the children can
 306:    * correctly represent the model.<li>
 307:    * <li>Call {@link #forwardUpdate}. This forwards the DocumentEvent to
 308:    * the child views.<li>
 309:    * <li>Call {@link #updateLayout}. Gives the view a chance to either
 310:    * repair its layout, reschedule layout or do nothing at all.</li>
 311:    * </ul>
 312:    *
 313:    * @param ev the DocumentEvent that describes the change
 314:    * @param shape the shape of the view
 315:    * @param vf the ViewFactory for creating child views
 316:    */
 317:   public void removeUpdate(DocumentEvent ev, Shape shape, ViewFactory vf)
 318:   {
 319:     Element el = getElement();
 320:     DocumentEvent.ElementChange ec = ev.getChange(el);
 321:     if (ec != null)
 322:         updateChildren(ec, ev, vf);
 323:     forwardUpdate(ec, ev, shape, vf);
 324:     updateLayout(ec, ev, shape);
 325:   }
 326: 
 327:   /**
 328:    * Receive notification about a change update to the text model.
 329:    *
 330:    * The default implementation of this method does the following:
 331:    * <ul>
 332:    * <li>Call {@link #updateChildren} if the element that this view is
 333:    * responsible for has changed. This makes sure that the children can
 334:    * correctly represent the model.<li>
 335:    * <li>Call {@link #forwardUpdate}. This forwards the DocumentEvent to
 336:    * the child views.<li>
 337:    * <li>Call {@link #updateLayout}. Gives the view a chance to either
 338:    * repair its layout, reschedule layout or do nothing at all.</li>
 339:    * </ul>
 340:    *
 341:    * @param ev the DocumentEvent that describes the change
 342:    * @param shape the shape of the view
 343:    * @param vf the ViewFactory for creating child views
 344:    */
 345:   public void changedUpdate(DocumentEvent ev, Shape shape, ViewFactory vf)
 346:   {
 347:     Element el = getElement();
 348:     DocumentEvent.ElementChange ec = ev.getChange(el);
 349:     if (ec != null)
 350:       updateChildren(ec, ev, vf);
 351:     forwardUpdate(ec, ev, shape, vf);
 352:     updateLayout(ec, ev, shape);
 353:   }
 354: 
 355:   /**
 356:    * Updates the list of children that is returned by {@link #getView}
 357:    * and {@link #getViewCount}.
 358:    *
 359:    * Element that are specified as beeing added in the ElementChange record are
 360:    * assigned a view for using the ViewFactory. Views of Elements that
 361:    * are specified as beeing removed are removed from the list.
 362:    *
 363:    * @param ec the ElementChange record that describes the change of the
 364:    *           element
 365:    * @param ev the DocumentEvent describing the change of the document model
 366:    * @param vf the ViewFactory to use for creating new views
 367:    *
 368:    * @return whether or not the child views represent the child elements of
 369:    *         the element that this view is responsible for. Some views may
 370:    *         create views that are responsible only for parts of the element
 371:    *         that they are responsible for and should then return false.
 372:    *
 373:    * @since 1.3
 374:    */
 375:   protected boolean updateChildren(DocumentEvent.ElementChange ec,
 376:                                    DocumentEvent ev,
 377:                                    ViewFactory vf)
 378:   {
 379:     Element[] added = ec.getChildrenAdded();
 380:     Element[] removed = ec.getChildrenRemoved();
 381:     int index = ec.getIndex();
 382: 
 383:     View[] newChildren = new View[added.length];
 384:     for (int i = 0; i < added.length; ++i)
 385:       newChildren[i] = vf.create(added[i]);
 386:     replace(index, removed.length, newChildren);
 387: 
 388:     return true;
 389:   }
 390: 
 391:   /**
 392:    * Forwards the DocumentEvent to child views that need to get notified
 393:    * of the change to the model. This calles {@link #forwardUpdateToView}
 394:    * for each View that must be forwarded to.
 395:    *
 396:    * @param ec the ElementChange describing the element changes (may be
 397:    *           <code>null</code> if there were no changes)
 398:    * @param ev the DocumentEvent describing the changes to the model
 399:    * @param shape the current allocation of the view
 400:    * @param vf the ViewFactory used to create new Views
 401:    *
 402:    * @since 1.3
 403:    */
 404:   protected void forwardUpdate(DocumentEvent.ElementChange ec,
 405:                                DocumentEvent ev, Shape shape, ViewFactory vf)
 406:   {
 407:     int count = getViewCount();
 408:     for (int i = 0; i < count; i++)
 409:       {
 410:         View child = getView(i);
 411:         forwardUpdateToView(child, ev, shape, vf);
 412:       }
 413:   }
 414: 
 415:   /**
 416:    * Forwards an update event to the given child view. This calls
 417:    * {@link #insertUpdate}, {@link #removeUpdate} or {@link #changedUpdate},
 418:    * depending on the type of document event.
 419:    *
 420:    * @param view the View to forward the event to
 421:    * @param ev the DocumentEvent to forward
 422:    * @param shape the current allocation of the View
 423:    * @param vf the ViewFactory used to create new Views
 424:    *
 425:    * @since 1.3
 426:    */
 427:   protected void forwardUpdateToView(View view, DocumentEvent ev, Shape shape,
 428:                                      ViewFactory vf)
 429:   {
 430:     DocumentEvent.EventType type = ev.getType();
 431:     if (type == DocumentEvent.EventType.INSERT)
 432:       view.insertUpdate(ev, shape, vf);
 433:     else if (type == DocumentEvent.EventType.REMOVE)
 434:       view.removeUpdate(ev, shape, vf);
 435:     else if (type == DocumentEvent.EventType.CHANGE)
 436:       view.changedUpdate(ev, shape, vf);
 437:   }
 438: 
 439:   /**
 440:    * Updates the layout.
 441:    *
 442:    * @param ec the ElementChange that describes the changes to the element
 443:    * @param ev the DocumentEvent that describes the changes to the model
 444:    * @param shape the current allocation for this view
 445:    *
 446:    * @since 1.3
 447:    */
 448:   protected void updateLayout(DocumentEvent.ElementChange ec,
 449:                               DocumentEvent ev, Shape shape)
 450:   {
 451:     Rectangle b = shape.getBounds();
 452:     if (ec != null)
 453:       preferenceChanged(this, true, true);
 454:   }
 455: 
 456:   /**
 457:    * Maps a position in the document into the coordinate space of the View.
 458:    * The output rectangle usually reflects the font height but has a width
 459:    * of zero.
 460:    *
 461:    * @param pos the position of the character in the model
 462:    * @param a the area that is occupied by the view
 463:    * @param b either {@link Position.Bias#Forward} or
 464:    *        {@link Position.Bias#Backward} depending on the preferred
 465:    *        direction bias. If <code>null</code> this defaults to
 466:    *        <code>Position.Bias.Forward</code>
 467:    *
 468:    * @return a rectangle that gives the location of the document position
 469:    *         inside the view coordinate space
 470:    *
 471:    * @throws BadLocationException if <code>pos</code> is invalid
 472:    * @throws IllegalArgumentException if b is not one of the above listed
 473:    *         valid values
 474:    */
 475:   public abstract Shape modelToView(int pos, Shape a, Position.Bias b)
 476:     throws BadLocationException;
 477: 
 478:   /**
 479:    * Maps a region in the document into the coordinate space of the View.
 480:    *
 481:    * @param p1 the beginning position inside the document
 482:    * @param b1 the direction bias for the beginning position
 483:    * @param p2 the end position inside the document
 484:    * @param b2 the direction bias for the end position
 485:    * @param a the area that is occupied by the view
 486:    *
 487:    * @return a rectangle that gives the span of the document region
 488:    *         inside the view coordinate space
 489:    *
 490:    * @throws BadLocationException if <code>p1</code> or <code>p2</code> are
 491:    *         invalid
 492:    * @throws IllegalArgumentException if b1 or b2 is not one of the above
 493:    *         listed valid values
 494:    */
 495:   public Shape modelToView(int p1, Position.Bias b1,
 496:                int p2, Position.Bias b2, Shape a)
 497:     throws BadLocationException
 498:   {
 499:     if (b1 != Position.Bias.Forward && b1 != Position.Bias.Backward)
 500:       throw new IllegalArgumentException
 501:     ("b1 must be either Position.Bias.Forward or Position.Bias.Backward");
 502:     if (b2 != Position.Bias.Forward && b2 != Position.Bias.Backward)
 503:       throw new IllegalArgumentException
 504:     ("b2 must be either Position.Bias.Forward or Position.Bias.Backward");
 505:     Shape s1 = modelToView(p1, a, b1);
 506:     Shape s2 = modelToView(p2, a, b2);
 507:     return s1.getBounds().union(s2.getBounds());
 508:   }
 509: 
 510:   /**
 511:    * Maps coordinates from the <code>View</code>'s space into a position
 512:    * in the document model.
 513:    *
 514:    * @param x the x coordinate in the view space
 515:    * @param y the y coordinate in the view space
 516:    * @param a the allocation of this <code>View</code>
 517:    * @param b the bias to use
 518:    *
 519:    * @return the position in the document that corresponds to the screen
 520:    *         coordinates <code>x, y</code>
 521:    */
 522:   public abstract int viewToModel(float x, float y, Shape a, Position.Bias[] b);
 523: 
 524: 
 525:   /**
 526:    * Dumps the complete View hierarchy. This method can be used for debugging
 527:    * purposes.
 528:    */
 529:   void dump()
 530:   {
 531:     // Climb up the hierarchy to the parent.
 532:     View parent = getParent();
 533:     if (parent != null)
 534:       parent.dump();
 535:     else
 536:       dump(0);
 537:   }
 538: 
 539:   /**
 540:    * Dumps the view hierarchy below this View with the specified indentation
 541:    * level.
 542:    *
 543:    * @param indent the indentation level to be used for this view
 544:    */
 545:   void dump(int indent)
 546:   {
 547:     for (int i = 0; i < indent; ++i)
 548:       System.out.print('.');
 549:     System.out.println(this);
 550: 
 551:     int count = getViewCount();
 552:     for (int i = 0; i < count; ++i)
 553:       getView(i).dump(indent + 1);
 554:   }
 555: }