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