Source for javax.swing.text.View

   1: /* View.java -- 
   2:    Copyright (C) 2002, 2004, 2005, 2006  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.SwingUtilities;
  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:   /**
  77:    * Sets the parent for this view. This is the first method that is beeing
  78:    * called on a view to setup the view hierarchy. This is also the last method
  79:    * beeing called when the view is disconnected from the view hierarchy, in
  80:    * this case <code>parent</code> is null.
  81:    *
  82:    * If <code>parent</code> is <code>null</code>, a call to this method also
  83:    * calls <code>setParent</code> on the children, thus disconnecting them from
  84:    * the view hierarchy. That means that super must be called when this method
  85:    * is overridden.
  86:    *
  87:    * @param parent the parent to set, <code>null</code> when this view is
  88:    *        beeing disconnected from the view hierarchy
  89:    */
  90:   public void setParent(View parent)
  91:   {
  92:     if (parent == null)
  93:       {
  94:         int numChildren = getViewCount();
  95:         for (int i = 0; i < numChildren; i++)
  96:           getView(i).setParent(null);
  97:       }
  98: 
  99:     this.parent = parent;
 100:   }
 101:     
 102:   public View getParent()
 103:   {
 104:     return parent;
 105:   }
 106:     
 107:   public Container getContainer()
 108:   {
 109:     View parent = getParent();
 110:     if (parent == null)
 111:       return null;
 112:     else
 113:       return parent.getContainer();
 114:   }
 115:   
 116:   public Document getDocument()
 117:   {
 118:     return getElement().getDocument();
 119:   }
 120:     
 121:   public Element getElement()
 122:   {
 123:     return elt;
 124:   }
 125: 
 126:   /**
 127:    * Returns the preferred span along the specified axis. Normally the view is
 128:    * rendered with the span returned here if that is possible.
 129:    *
 130:    * @param axis the axis
 131:    *
 132:    * @return the preferred span along the specified axis
 133:    */
 134:   public abstract float getPreferredSpan(int axis);
 135: 
 136:   /**
 137:    * Returns the resize weight of this view. A value of <code>0</code> or less
 138:    * means this view is not resizeable. Positive values make the view
 139:    * resizeable. The default implementation returns <code>0</code>
 140:    * unconditionally.
 141:    *
 142:    * @param axis the axis
 143:    *
 144:    * @return the resizability of this view along the specified axis
 145:    */
 146:   public int getResizeWeight(int axis)
 147:   {
 148:     return 0;
 149:   }
 150: 
 151:   /**
 152:    * Returns the maximum span along the specified axis. The default
 153:    * implementation will forward to
 154:    * {@link #getPreferredSpan(int)} unless {@link #getResizeWeight(int)}
 155:    * returns a value > 0, in which case this returns {@link Integer#MIN_VALUE}.
 156:    *
 157:    * @param axis the axis
 158:    *
 159:    * @return the maximum span along the specified axis
 160:    */
 161:   public float getMaximumSpan(int axis)
 162:   {
 163:     float max = Integer.MAX_VALUE;
 164:     if (getResizeWeight(axis) <= 0)
 165:       max = getPreferredSpan(axis);
 166:     return max;
 167:   }
 168: 
 169:   /**
 170:    * Returns the minimum span along the specified axis. The default
 171:    * implementation will forward to
 172:    * {@link #getPreferredSpan(int)} unless {@link #getResizeWeight(int)}
 173:    * returns a value > 0, in which case this returns <code>0</code>.
 174:    *
 175:    * @param axis the axis
 176:    *
 177:    * @return the minimum span along the specified axis
 178:    */
 179:   public float getMinimumSpan(int axis)
 180:   {
 181:     float min = 0;
 182:     if (getResizeWeight(axis) <= 0)
 183:       min = getPreferredSpan(axis);
 184:     return min;
 185:   }
 186:   
 187:   public void setSize(float width, float height)
 188:   {
 189:     // The default implementation does nothing.
 190:   }
 191:   
 192:   /**
 193:    * Returns the alignment of this view along the baseline of the parent view.
 194:    * An alignment of <code>0.0</code> will align this view with the left edge
 195:    * along the baseline, an alignment of <code>0.5</code> will align it
 196:    * centered to the baseline, an alignment of <code>1.0</code> will align
 197:    * the right edge along the baseline.
 198:    *
 199:    * The default implementation returns 0.5 unconditionally.
 200:    *
 201:    * @param axis the axis
 202:    *
 203:    * @return the alignment of this view along the parents baseline for the
 204:    *         specified axis
 205:    */
 206:   public float getAlignment(int axis)
 207:   {
 208:     return 0.5f;
 209:   }
 210: 
 211:   public AttributeSet getAttributes()
 212:   {
 213:     return getElement().getAttributes();
 214:   }
 215:   
 216:   public boolean isVisible()
 217:   {
 218:     return true;
 219:   }
 220: 
 221:   public int getViewCount()
 222:   {
 223:     return 0;
 224:   }
 225:   
 226:   public View getView(int index)
 227:   {
 228:     return null;
 229:   }
 230: 
 231:   public ViewFactory getViewFactory()
 232:   {
 233:     View parent = getParent();
 234:     return parent != null ? parent.getViewFactory() : null;
 235:   }
 236: 
 237:   /**
 238:    * Replaces a couple of child views with new child views. If
 239:    * <code>length == 0</code> then this is a simple insertion, if
 240:    * <code>views == null</code> this only removes some child views.
 241:    *
 242:    * @param offset the offset at which to replace
 243:    * @param length the number of child views to be removed
 244:    * @param views the new views to be inserted, may be <code>null</code>
 245:    */
 246:   public void replace(int offset, int length, View[] views)
 247:   {
 248:     // Default implementation does nothing.
 249:   }
 250: 
 251:   public void insert(int offset, View view)
 252:   {
 253:     View[] array = { view };
 254:     replace(offset, 1, array);
 255:   }
 256: 
 257:   public void append(View view)
 258:   {
 259:     View[] array = { view };
 260:     int offset = getViewCount();
 261:     replace(offset, 0, array);
 262:   }
 263: 
 264:   public void removeAll()
 265:   {
 266:     replace(0, getViewCount(), new View[0]); 
 267:   }
 268: 
 269:   public void remove(int index)
 270:   {
 271:     replace(index, 1, null); 
 272:   }
 273: 
 274:   public View createFragment(int p0, int p1)
 275:   {
 276:     // The default implementation doesn't support fragmentation.
 277:     return this;
 278:   }
 279: 
 280:   public int getStartOffset()
 281:   {
 282:     return getElement().getStartOffset();
 283:   }
 284: 
 285:   public int getEndOffset()
 286:   {
 287:     return getElement().getEndOffset();
 288:   }
 289: 
 290:   public Shape getChildAllocation(int index, Shape a)
 291:   {
 292:     return null;
 293:   }
 294:   
 295:   /**
 296:    * @since 1.4
 297:    */
 298:   public int getViewIndex(float x, float y, Shape allocation)
 299:   {
 300:     return -1;
 301:   }
 302:   
 303:   /**
 304:    * @since 1.4
 305:    */
 306:   public String getToolTipText(float x, float y, Shape allocation)
 307:   {
 308:     int index = getViewIndex(x, y, allocation);
 309: 
 310:     if (index < -1)
 311:       return null;
 312: 
 313:     Shape childAllocation = getChildAllocation(index, allocation);
 314: 
 315:     if (childAllocation.getBounds().contains(x, y))
 316:       return getView(index).getToolTipText(x, y, childAllocation);
 317: 
 318:     return null;
 319:   }
 320: 
 321:   /**
 322:    * @since 1.3
 323:    */
 324:   public Graphics getGraphics()
 325:   {
 326:     return getContainer().getGraphics();
 327:   }
 328: 
 329:   public void preferenceChanged(View child, boolean width, boolean height)
 330:   {
 331:     if (parent != null)
 332:       parent.preferenceChanged(this, width, height);
 333:   }
 334: 
 335:   public int getBreakWeight(int axis, float pos, float len)
 336:   {
 337:     return BadBreakWeight;
 338:   }
 339: 
 340:   public View breakView(int axis, int offset, float pos, float len)
 341:   {
 342:     return this;
 343:   }
 344: 
 345:   /**
 346:    * @since 1.3
 347:    */
 348:   public int getViewIndex(int pos, Position.Bias b)
 349:   {
 350:     return -1;
 351:   }
 352: 
 353:   /**
 354:    * Receive notification about an insert update to the text model.
 355:    *
 356:    * The default implementation of this method does the following:
 357:    * <ul>
 358:    * <li>Call {@link #updateChildren} if the element that this view is
 359:    * responsible for has changed. This makes sure that the children can
 360:    * correctly represent the model.<li>
 361:    * <li>Call {@link #forwardUpdate}. This forwards the DocumentEvent to
 362:    * the child views.<li>
 363:    * <li>Call {@link #updateLayout}. Gives the view a chance to either
 364:    * repair its layout, reschedule layout or do nothing at all.</li>
 365:    * </ul>
 366:    *
 367:    * @param ev the DocumentEvent that describes the change
 368:    * @param shape the shape of the view
 369:    * @param vf the ViewFactory for creating child views
 370:    */
 371:   public void insertUpdate(DocumentEvent ev, Shape shape, ViewFactory vf)
 372:   {
 373:     Element el = getElement();
 374:     DocumentEvent.ElementChange ec = ev.getChange(el);
 375:     if (ec != null)
 376:       updateChildren(ec, ev, vf);
 377:     forwardUpdate(ec, ev, shape, vf);
 378:     updateLayout(ec, ev, shape);
 379:   }
 380: 
 381:   /**
 382:    * Receive notification about a remove update to the text model.
 383:    *
 384:    * The default implementation of this method does the following:
 385:    * <ul>
 386:    * <li>Call {@link #updateChildren} if the element that this view is
 387:    * responsible for has changed. This makes sure that the children can
 388:    * correctly represent the model.<li>
 389:    * <li>Call {@link #forwardUpdate}. This forwards the DocumentEvent to
 390:    * the child views.<li>
 391:    * <li>Call {@link #updateLayout}. Gives the view a chance to either
 392:    * repair its layout, reschedule layout or do nothing at all.</li>
 393:    * </ul>
 394:    *
 395:    * @param ev the DocumentEvent that describes the change
 396:    * @param shape the shape of the view
 397:    * @param vf the ViewFactory for creating child views
 398:    */
 399:   public void removeUpdate(DocumentEvent ev, Shape shape, ViewFactory vf)
 400:   {
 401:     Element el = getElement();
 402:     DocumentEvent.ElementChange ec = ev.getChange(el);
 403:     if (ec != null)
 404:       {
 405:         if (! updateChildren(ec, ev, vf))
 406:           ec = null;
 407:       }
 408:     forwardUpdate(ec, ev, shape, vf);
 409:     updateLayout(ec, ev, shape);
 410:   }
 411: 
 412:   /**
 413:    * Receive notification about a change update to the text model.
 414:    *
 415:    * The default implementation of this method does the following:
 416:    * <ul>
 417:    * <li>Call {@link #updateChildren} if the element that this view is
 418:    * responsible for has changed. This makes sure that the children can
 419:    * correctly represent the model.<li>
 420:    * <li>Call {@link #forwardUpdate}. This forwards the DocumentEvent to
 421:    * the child views.<li>
 422:    * <li>Call {@link #updateLayout}. Gives the view a chance to either
 423:    * repair its layout, reschedule layout or do nothing at all.</li>
 424:    * </ul>
 425:    *
 426:    * @param ev the DocumentEvent that describes the change
 427:    * @param shape the shape of the view
 428:    * @param vf the ViewFactory for creating child views
 429:    */
 430:   public void changedUpdate(DocumentEvent ev, Shape shape, ViewFactory vf)
 431:   {
 432:     Element el = getElement();
 433:     DocumentEvent.ElementChange ec = ev.getChange(el);
 434:     if (ec != null)
 435:       updateChildren(ec, ev, vf);
 436:     forwardUpdate(ec, ev, shape, vf);
 437:     updateLayout(ec, ev, shape);
 438:   }
 439: 
 440:   /**
 441:    * Updates the list of children that is returned by {@link #getView}
 442:    * and {@link #getViewCount}.
 443:    *
 444:    * Element that are specified as beeing added in the ElementChange record are
 445:    * assigned a view for using the ViewFactory. Views of Elements that
 446:    * are specified as beeing removed are removed from the list.
 447:    *
 448:    * @param ec the ElementChange record that describes the change of the
 449:    *           element
 450:    * @param ev the DocumentEvent describing the change of the document model
 451:    * @param vf the ViewFactory to use for creating new views
 452:    *
 453:    * @return whether or not the child views represent the child elements of
 454:    *         the element that this view is responsible for. Some views may
 455:    *         create views that are responsible only for parts of the element
 456:    *         that they are responsible for and should then return false.
 457:    *
 458:    * @since 1.3
 459:    */
 460:   protected boolean updateChildren(DocumentEvent.ElementChange ec,
 461:                                    DocumentEvent ev,
 462:                                    ViewFactory vf)
 463:   {
 464:     Element[] added = ec.getChildrenAdded();
 465:     Element[] removed = ec.getChildrenRemoved();
 466:     int index = ec.getIndex();
 467: 
 468:     View[] newChildren = new View[added.length];
 469:     for (int i = 0; i < added.length; ++i)
 470:       newChildren[i] = vf.create(added[i]);
 471:     replace(index, removed.length, newChildren);
 472: 
 473:     return true;
 474:   }
 475: 
 476:   /**
 477:    * Forwards the DocumentEvent to child views that need to get notified
 478:    * of the change to the model. This calles {@link #forwardUpdateToView}
 479:    * for each View that must be forwarded to.
 480:    *
 481:    * If <code>ec</code> is not <code>null</code> (this means there have been
 482:    * structural changes to the element that this view is responsible for) this
 483:    * method should recognize this and don't notify newly added child views.
 484:    *
 485:    * @param ec the ElementChange describing the element changes (may be
 486:    *           <code>null</code> if there were no changes)
 487:    * @param ev the DocumentEvent describing the changes to the model
 488:    * @param shape the current allocation of the view
 489:    * @param vf the ViewFactory used to create new Views
 490:    *
 491:    * @since 1.3
 492:    */
 493:   protected void forwardUpdate(DocumentEvent.ElementChange ec,
 494:                                DocumentEvent ev, Shape shape, ViewFactory vf)
 495:   {
 496:     int count = getViewCount();
 497:     if (count > 0)
 498:       {
 499:         // Determine start index.
 500:         int startOffset = ev.getOffset();
 501:         int startIndex = getViewIndex(startOffset, Position.Bias.Backward);
 502: 
 503:         // For REMOVE events we have to forward the event to the last element,
 504:         // for the case that an Element has been removed that represente
 505:         // the offset.
 506:         if (startIndex == -1 && ev.getType() == DocumentEvent.EventType.REMOVE
 507:             && startOffset >= getEndOffset())
 508:           {
 509:             startIndex = getViewCount() - 1;
 510:           }
 511: 
 512:         // When startIndex is on a view boundary, forward event to the
 513:         // previous view too.
 514:         if (startIndex >= 0)
 515:           {
 516:             View v = getView(startIndex);
 517:             if (v != null)
 518:               {
 519:                 if (v.getStartOffset() == startOffset && startOffset > 0)
 520:                   startIndex = Math.max(0, startIndex - 1);
 521:               }
 522:           }
 523:         startIndex = Math.max(0, startIndex);
 524: 
 525:         // Determine end index.
 526:         int endIndex = startIndex;
 527:         if (ev.getType() != DocumentEvent.EventType.REMOVE)
 528:           {
 529:             endIndex = getViewIndex(startOffset + ev.getLength(),
 530:                                     Position.Bias.Forward);
 531:             if (endIndex < 0)
 532:               endIndex = getViewCount() - 1;
 533:           }
 534: 
 535:         // Determine hole that comes from added elements (we don't forward
 536:         // the event to newly added views.
 537:         int startAdded = endIndex + 1;
 538:         int endAdded = startAdded;
 539:         Element[] added = (ec != null) ? ec.getChildrenAdded() : null;
 540:         if (added != null && added.length > 0)
 541:           {
 542:             startAdded = ec.getIndex();
 543:             endAdded = startAdded + added.length - 1;
 544:           }
 545: 
 546:         // Forward event to all views between startIndex and endIndex,
 547:         // and leave out all views in the hole.
 548:         for (int i = startIndex; i <= endIndex; i++)
 549:           {
 550:             // Skip newly added child views.
 551:             if (! (i >= startAdded && i <= endAdded))
 552:               {
 553:                 View child = getView(i);
 554:                 if (child != null)
 555:                   {
 556:                     Shape childAlloc = getChildAllocation(i, shape);
 557:                     forwardUpdateToView(child, ev, childAlloc, vf);
 558:                   }
 559:               }
 560:           }
 561:       }
 562:   }
 563: 
 564:   /**
 565:    * Forwards an update event to the given child view. This calls
 566:    * {@link #insertUpdate}, {@link #removeUpdate} or {@link #changedUpdate},
 567:    * depending on the type of document event.
 568:    *
 569:    * @param view the View to forward the event to
 570:    * @param ev the DocumentEvent to forward
 571:    * @param shape the current allocation of the View
 572:    * @param vf the ViewFactory used to create new Views
 573:    *
 574:    * @since 1.3
 575:    */
 576:   protected void forwardUpdateToView(View view, DocumentEvent ev, Shape shape,
 577:                                      ViewFactory vf)
 578:   {
 579:     DocumentEvent.EventType type = ev.getType();
 580:     if (type == DocumentEvent.EventType.INSERT)
 581:       view.insertUpdate(ev, shape, vf);
 582:     else if (type == DocumentEvent.EventType.REMOVE)
 583:       view.removeUpdate(ev, shape, vf);
 584:     else if (type == DocumentEvent.EventType.CHANGE)
 585:       view.changedUpdate(ev, shape, vf);
 586:   }
 587: 
 588:   /**
 589:    * Updates the layout.
 590:    *
 591:    * @param ec the ElementChange that describes the changes to the element
 592:    * @param ev the DocumentEvent that describes the changes to the model
 593:    * @param shape the current allocation for this view
 594:    *
 595:    * @since 1.3
 596:    */
 597:   protected void updateLayout(DocumentEvent.ElementChange ec,
 598:                               DocumentEvent ev, Shape shape)
 599:   {
 600:     if (ec != null && shape != null)
 601:       preferenceChanged(null, true, true);
 602:     Container c = getContainer();
 603:     if (c != null)
 604:       c.repaint();
 605:   }
 606: 
 607:   /**
 608:    * Maps a position in the document into the coordinate space of the View.
 609:    * The output rectangle usually reflects the font height but has a width
 610:    * of zero.
 611:    *
 612:    * @param pos the position of the character in the model
 613:    * @param a the area that is occupied by the view
 614:    * @param b either {@link Position.Bias#Forward} or
 615:    *        {@link Position.Bias#Backward} depending on the preferred
 616:    *        direction bias. If <code>null</code> this defaults to
 617:    *        <code>Position.Bias.Forward</code>
 618:    *
 619:    * @return a rectangle that gives the location of the document position
 620:    *         inside the view coordinate space
 621:    *
 622:    * @throws BadLocationException if <code>pos</code> is invalid
 623:    * @throws IllegalArgumentException if b is not one of the above listed
 624:    *         valid values
 625:    */
 626:   public abstract Shape modelToView(int pos, Shape a, Position.Bias b)
 627:     throws BadLocationException;
 628: 
 629:   /**
 630:    * Maps a region in the document into the coordinate space of the View.
 631:    *
 632:    * @param p1 the beginning position inside the document
 633:    * @param b1 the direction bias for the beginning position
 634:    * @param p2 the end position inside the document
 635:    * @param b2 the direction bias for the end position
 636:    * @param a the area that is occupied by the view
 637:    *
 638:    * @return a rectangle that gives the span of the document region
 639:    *         inside the view coordinate space
 640:    *
 641:    * @throws BadLocationException if <code>p1</code> or <code>p2</code> are
 642:    *         invalid
 643:    * @throws IllegalArgumentException if b1 or b2 is not one of the above
 644:    *         listed valid values
 645:    */
 646:   public Shape modelToView(int p1, Position.Bias b1,
 647:                int p2, Position.Bias b2, Shape a)
 648:     throws BadLocationException
 649:   {
 650:     if (b1 != Position.Bias.Forward && b1 != Position.Bias.Backward)
 651:       throw new IllegalArgumentException
 652:     ("b1 must be either Position.Bias.Forward or Position.Bias.Backward");
 653:     if (b2 != Position.Bias.Forward && b2 != Position.Bias.Backward)
 654:       throw new IllegalArgumentException
 655:     ("b2 must be either Position.Bias.Forward or Position.Bias.Backward");
 656: 
 657:     Shape s1 = modelToView(p1, a, b1);
 658:     // Special case for p2 == end index.
 659:     Shape s2;
 660:     if (p2 != getEndOffset())
 661:       {
 662:         s2 = modelToView(p2, a, b2);
 663:       }
 664:     else
 665:       {
 666:         try
 667:           {
 668:             s2 = modelToView(p2, a, b2);
 669:           }
 670:         catch (BadLocationException ex)
 671:           {
 672:             // Assume the end rectangle to be at the right edge of the
 673:             // view.
 674:             Rectangle aRect = a instanceof Rectangle ? (Rectangle) a
 675:                                                      : a.getBounds();
 676:             s2 = new Rectangle(aRect.x + aRect.width - 1, aRect.y, 1,
 677:                                aRect.height);
 678:           }
 679:       }
 680: 
 681:     // Need to modify the rectangle, so we create a copy in all cases.
 682:     Rectangle r1 = s1.getBounds();
 683:     Rectangle r2 = s2 instanceof Rectangle ? (Rectangle) s2
 684:                                            : s2.getBounds();
 685: 
 686:     // For multiline view, let the resulting rectangle span the whole view.
 687:     if (r1.y != r2.y)
 688:       {
 689:         Rectangle aRect = a instanceof Rectangle ? (Rectangle) a
 690:                                                  : a.getBounds();
 691:         r1.x = aRect.x;
 692:         r1.width = aRect.width;
 693:       }
 694: 
 695:     return SwingUtilities.computeUnion(r2.x, r2.y, r2.width, r2.height, r1);
 696:   }
 697: 
 698:   /**
 699:    * Maps a position in the document into the coordinate space of the View.
 700:    * The output rectangle usually reflects the font height but has a width
 701:    * of zero.
 702:    *
 703:    * This method is deprecated and calls
 704:    * {@link #modelToView(int, Position.Bias, int, Position.Bias, Shape)} with
 705:    * a bias of {@link Position.Bias#Forward}.
 706:    *
 707:    * @param pos the position of the character in the model
 708:    * @param a the area that is occupied by the view
 709:    *
 710:    * @return a rectangle that gives the location of the document position
 711:    *         inside the view coordinate space
 712:    *
 713:    * @throws BadLocationException if <code>pos</code> is invalid
 714:    *
 715:    * @deprecated Use {@link #modelToView(int, Shape, Position.Bias)} instead.
 716:    */
 717:   public Shape modelToView(int pos, Shape a) throws BadLocationException
 718:   {
 719:     return modelToView(pos, a, Position.Bias.Forward);
 720:   }
 721: 
 722:   /**
 723:    * Maps coordinates from the <code>View</code>'s space into a position
 724:    * in the document model.
 725:    *
 726:    * @param x the x coordinate in the view space
 727:    * @param y the y coordinate in the view space
 728:    * @param a the allocation of this <code>View</code>
 729:    * @param b the bias to use
 730:    *
 731:    * @return the position in the document that corresponds to the screen
 732:    *         coordinates <code>x, y</code>
 733:    */
 734:   public abstract int viewToModel(float x, float y, Shape a, Position.Bias[] b);
 735: 
 736:   /**
 737:    * Maps coordinates from the <code>View</code>'s space into a position
 738:    * in the document model. This method is deprecated and only there for
 739:    * compatibility.
 740:    *
 741:    * @param x the x coordinate in the view space
 742:    * @param y the y coordinate in the view space
 743:    * @param a the allocation of this <code>View</code>
 744:    *
 745:    * @return the position in the document that corresponds to the screen
 746:    *         coordinates <code>x, y</code>
 747:    *
 748:    * @deprecated Use {@link #viewToModel(float, float, Shape, Position.Bias[])}
 749:    *             instead.
 750:    */
 751:   public int viewToModel(float x, float y, Shape a)
 752:   {
 753:     return viewToModel(x, y, a, new Position.Bias[0]);
 754:   }
 755: 
 756:   /**
 757:    * Dumps the complete View hierarchy. This method can be used for debugging
 758:    * purposes.
 759:    */
 760:   protected void dump()
 761:   {
 762:     // Climb up the hierarchy to the parent.
 763:     View parent = getParent();
 764:     if (parent != null)
 765:       parent.dump();
 766:     else
 767:       dump(0);
 768:   }
 769: 
 770:   /**
 771:    * Dumps the view hierarchy below this View with the specified indentation
 772:    * level.
 773:    *
 774:    * @param indent the indentation level to be used for this view
 775:    */
 776:   void dump(int indent)
 777:   {
 778:     for (int i = 0; i < indent; ++i)
 779:       System.out.print('.');
 780:     System.out.println(this + "(" + getStartOffset() + "," + getEndOffset() + ": " + getElement());
 781: 
 782:     int count = getViewCount();
 783:     for (int i = 0; i < count; ++i)
 784:       getView(i).dump(indent + 1);
 785:   }
 786: 
 787:   /**
 788:    * Returns the document position that is (visually) nearest to the given
 789:    * document position <code>pos</code> in the given direction <code>d</code>.
 790:    *
 791:    * @param pos the document position
 792:    * @param b the bias for <code>pos</code>
 793:    * @param a the allocation for this view
 794:    * @param d the direction, must be either {@link SwingConstants#NORTH},
 795:    *        {@link SwingConstants#SOUTH}, {@link SwingConstants#WEST} or
 796:    *        {@link SwingConstants#EAST}
 797:    * @param biasRet an array of {@link Position.Bias} that can hold at least
 798:    *        one element, which is filled with the bias of the return position
 799:    *        on method exit
 800:    *
 801:    * @return the document position that is (visually) nearest to the given
 802:    *         document position <code>pos</code> in the given direction
 803:    *         <code>d</code>
 804:    *
 805:    * @throws BadLocationException if <code>pos</code> is not a valid offset in
 806:    *         the document model
 807:    * @throws IllegalArgumentException if <code>d</code> is not a valid direction
 808:    */
 809:   public int getNextVisualPositionFrom(int pos, Position.Bias b,
 810:                                        Shape a, int d,
 811:                                        Position.Bias[] biasRet)
 812:     throws BadLocationException
 813:   {
 814:     int ret = pos;
 815:     Rectangle r;
 816:     View parent;
 817: 
 818:     switch (d)
 819:     {
 820:       case EAST:
 821:         // TODO: take component orientation into account?
 822:         // Note: If pos is below zero the implementation will return
 823:         // pos + 1 regardless of whether that value is a correct offset
 824:         // in the document model. However this is what the RI does.
 825:         ret = Math.min(pos + 1, getEndOffset());
 826:         break;
 827:       case WEST:
 828:         // TODO: take component orientation into account?
 829:         ret = Math.max(pos - 1, getStartOffset());
 830:         break;
 831:       case NORTH:
 832:         // Try to find a suitable offset by examining the area above.
 833:         parent = getParent();
 834:         r =  parent.modelToView(pos, a, b).getBounds();
 835:         ret = parent.viewToModel(r.x, r.y - 1, a, biasRet);
 836:         break;
 837:       case SOUTH:
 838:         // Try to find a suitable offset by examining the area below. 
 839:         parent = getParent();
 840:         r =  parent.modelToView(pos, a, b).getBounds();
 841:         ret = parent.viewToModel(r.x + r.width, r.y + r.height, a, biasRet);
 842:         break;
 843:       default:
 844:         throw new IllegalArgumentException("Illegal value for d");
 845:     }
 846:     
 847:     return ret;
 848:   }
 849: }