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:         updateChildren(ec, ev, vf);
 405:     forwardUpdate(ec, ev, shape, vf);
 406:     updateLayout(ec, ev, shape);
 407:   }
 408: 
 409:   /**
 410:    * Receive notification about a change update to the text model.
 411:    *
 412:    * The default implementation of this method does the following:
 413:    * <ul>
 414:    * <li>Call {@link #updateChildren} if the element that this view is
 415:    * responsible for has changed. This makes sure that the children can
 416:    * correctly represent the model.<li>
 417:    * <li>Call {@link #forwardUpdate}. This forwards the DocumentEvent to
 418:    * the child views.<li>
 419:    * <li>Call {@link #updateLayout}. Gives the view a chance to either
 420:    * repair its layout, reschedule layout or do nothing at all.</li>
 421:    * </ul>
 422:    *
 423:    * @param ev the DocumentEvent that describes the change
 424:    * @param shape the shape of the view
 425:    * @param vf the ViewFactory for creating child views
 426:    */
 427:   public void changedUpdate(DocumentEvent ev, Shape shape, ViewFactory vf)
 428:   {
 429:     Element el = getElement();
 430:     DocumentEvent.ElementChange ec = ev.getChange(el);
 431:     if (ec != null)
 432:       updateChildren(ec, ev, vf);
 433:     forwardUpdate(ec, ev, shape, vf);
 434:     updateLayout(ec, ev, shape);
 435:   }
 436: 
 437:   /**
 438:    * Updates the list of children that is returned by {@link #getView}
 439:    * and {@link #getViewCount}.
 440:    *
 441:    * Element that are specified as beeing added in the ElementChange record are
 442:    * assigned a view for using the ViewFactory. Views of Elements that
 443:    * are specified as beeing removed are removed from the list.
 444:    *
 445:    * @param ec the ElementChange record that describes the change of the
 446:    *           element
 447:    * @param ev the DocumentEvent describing the change of the document model
 448:    * @param vf the ViewFactory to use for creating new views
 449:    *
 450:    * @return whether or not the child views represent the child elements of
 451:    *         the element that this view is responsible for. Some views may
 452:    *         create views that are responsible only for parts of the element
 453:    *         that they are responsible for and should then return false.
 454:    *
 455:    * @since 1.3
 456:    */
 457:   protected boolean updateChildren(DocumentEvent.ElementChange ec,
 458:                                    DocumentEvent ev,
 459:                                    ViewFactory vf)
 460:   {
 461:     Element[] added = ec.getChildrenAdded();
 462:     Element[] removed = ec.getChildrenRemoved();
 463:     int index = ec.getIndex();
 464: 
 465:     View[] newChildren = new View[added.length];
 466:     for (int i = 0; i < added.length; ++i)
 467:       newChildren[i] = vf.create(added[i]);
 468:     replace(index, removed.length, newChildren);
 469: 
 470:     return true;
 471:   }
 472: 
 473:   /**
 474:    * Forwards the DocumentEvent to child views that need to get notified
 475:    * of the change to the model. This calles {@link #forwardUpdateToView}
 476:    * for each View that must be forwarded to.
 477:    *
 478:    * If <code>ec</code> is not <code>null</code> (this means there have been
 479:    * structural changes to the element that this view is responsible for) this
 480:    * method should recognize this and don't notify newly added child views.
 481:    *
 482:    * @param ec the ElementChange describing the element changes (may be
 483:    *           <code>null</code> if there were no changes)
 484:    * @param ev the DocumentEvent describing the changes to the model
 485:    * @param shape the current allocation of the view
 486:    * @param vf the ViewFactory used to create new Views
 487:    *
 488:    * @since 1.3
 489:    */
 490:   protected void forwardUpdate(DocumentEvent.ElementChange ec,
 491:                                DocumentEvent ev, Shape shape, ViewFactory vf)
 492:   {
 493:     int count = getViewCount();
 494:     if (count > 0)
 495:       {
 496:         int startOffset = ev.getOffset();
 497:         int endOffset = startOffset + ev.getLength();
 498:         int startIndex = getViewIndex(startOffset, Position.Bias.Backward);
 499:         int endIndex = getViewIndex(endOffset, Position.Bias.Forward);
 500:         int index = -1;
 501:         int addLength = -1;
 502:         if (ec != null)
 503:           {
 504:             index = ec.getIndex();
 505:             addLength = ec.getChildrenAdded().length;
 506:           }
 507: 
 508:         if (startIndex >= 0 && endIndex >= 0)
 509:           {
 510:             for (int i = startIndex; i <= endIndex; i++)
 511:               {
 512:                 // Skip newly added child views.
 513:                 if (index >= 0 && i >= index && i < (index+addLength))
 514:                   continue;
 515:                 View child = getView(i);
 516:                 forwardUpdateToView(child, ev, shape, vf);
 517:               }
 518:           }
 519:       }
 520:   }
 521: 
 522:   /**
 523:    * Forwards an update event to the given child view. This calls
 524:    * {@link #insertUpdate}, {@link #removeUpdate} or {@link #changedUpdate},
 525:    * depending on the type of document event.
 526:    *
 527:    * @param view the View to forward the event to
 528:    * @param ev the DocumentEvent to forward
 529:    * @param shape the current allocation of the View
 530:    * @param vf the ViewFactory used to create new Views
 531:    *
 532:    * @since 1.3
 533:    */
 534:   protected void forwardUpdateToView(View view, DocumentEvent ev, Shape shape,
 535:                                      ViewFactory vf)
 536:   {
 537:     DocumentEvent.EventType type = ev.getType();
 538:     if (type == DocumentEvent.EventType.INSERT)
 539:       view.insertUpdate(ev, shape, vf);
 540:     else if (type == DocumentEvent.EventType.REMOVE)
 541:       view.removeUpdate(ev, shape, vf);
 542:     else if (type == DocumentEvent.EventType.CHANGE)
 543:       view.changedUpdate(ev, shape, vf);
 544:   }
 545: 
 546:   /**
 547:    * Updates the layout.
 548:    *
 549:    * @param ec the ElementChange that describes the changes to the element
 550:    * @param ev the DocumentEvent that describes the changes to the model
 551:    * @param shape the current allocation for this view
 552:    *
 553:    * @since 1.3
 554:    */
 555:   protected void updateLayout(DocumentEvent.ElementChange ec,
 556:                               DocumentEvent ev, Shape shape)
 557:   {
 558:     if (ec != null && shape != null)
 559:       preferenceChanged(null, true, true);
 560:     Container c = getContainer();
 561:     if (c != null)
 562:       c.repaint();
 563:   }
 564: 
 565:   /**
 566:    * Maps a position in the document into the coordinate space of the View.
 567:    * The output rectangle usually reflects the font height but has a width
 568:    * of zero.
 569:    *
 570:    * @param pos the position of the character in the model
 571:    * @param a the area that is occupied by the view
 572:    * @param b either {@link Position.Bias#Forward} or
 573:    *        {@link Position.Bias#Backward} depending on the preferred
 574:    *        direction bias. If <code>null</code> this defaults to
 575:    *        <code>Position.Bias.Forward</code>
 576:    *
 577:    * @return a rectangle that gives the location of the document position
 578:    *         inside the view coordinate space
 579:    *
 580:    * @throws BadLocationException if <code>pos</code> is invalid
 581:    * @throws IllegalArgumentException if b is not one of the above listed
 582:    *         valid values
 583:    */
 584:   public abstract Shape modelToView(int pos, Shape a, Position.Bias b)
 585:     throws BadLocationException;
 586: 
 587:   /**
 588:    * Maps a region in the document into the coordinate space of the View.
 589:    *
 590:    * @param p1 the beginning position inside the document
 591:    * @param b1 the direction bias for the beginning position
 592:    * @param p2 the end position inside the document
 593:    * @param b2 the direction bias for the end position
 594:    * @param a the area that is occupied by the view
 595:    *
 596:    * @return a rectangle that gives the span of the document region
 597:    *         inside the view coordinate space
 598:    *
 599:    * @throws BadLocationException if <code>p1</code> or <code>p2</code> are
 600:    *         invalid
 601:    * @throws IllegalArgumentException if b1 or b2 is not one of the above
 602:    *         listed valid values
 603:    */
 604:   public Shape modelToView(int p1, Position.Bias b1,
 605:                int p2, Position.Bias b2, Shape a)
 606:     throws BadLocationException
 607:   {
 608:     if (b1 != Position.Bias.Forward && b1 != Position.Bias.Backward)
 609:       throw new IllegalArgumentException
 610:     ("b1 must be either Position.Bias.Forward or Position.Bias.Backward");
 611:     if (b2 != Position.Bias.Forward && b2 != Position.Bias.Backward)
 612:       throw new IllegalArgumentException
 613:     ("b2 must be either Position.Bias.Forward or Position.Bias.Backward");
 614:     Rectangle s1 = (Rectangle) modelToView(p1, a, b1);
 615:     Rectangle s2 = (Rectangle) modelToView(p2, a, b2);
 616:     return SwingUtilities.computeUnion(s1.x, s1.y, s1.width, s1.height, s2);
 617:   }
 618: 
 619:   /**
 620:    * Maps a position in the document into the coordinate space of the View.
 621:    * The output rectangle usually reflects the font height but has a width
 622:    * of zero.
 623:    *
 624:    * This method is deprecated and calls
 625:    * {@link #modelToView(int, Position.Bias, int, Position.Bias, Shape)} with
 626:    * a bias of {@link Position.Bias#Forward}.
 627:    *
 628:    * @param pos the position of the character in the model
 629:    * @param a the area that is occupied by the view
 630:    *
 631:    * @return a rectangle that gives the location of the document position
 632:    *         inside the view coordinate space
 633:    *
 634:    * @throws BadLocationException if <code>pos</code> is invalid
 635:    *
 636:    * @deprecated Use {@link #modelToView(int, Shape, Position.Bias)} instead.
 637:    */
 638:   public Shape modelToView(int pos, Shape a) throws BadLocationException
 639:   {
 640:     return modelToView(pos, a, Position.Bias.Forward);
 641:   }
 642: 
 643:   /**
 644:    * Maps coordinates from the <code>View</code>'s space into a position
 645:    * in the document model.
 646:    *
 647:    * @param x the x coordinate in the view space
 648:    * @param y the y coordinate in the view space
 649:    * @param a the allocation of this <code>View</code>
 650:    * @param b the bias to use
 651:    *
 652:    * @return the position in the document that corresponds to the screen
 653:    *         coordinates <code>x, y</code>
 654:    */
 655:   public abstract int viewToModel(float x, float y, Shape a, Position.Bias[] b);
 656: 
 657:   /**
 658:    * Maps coordinates from the <code>View</code>'s space into a position
 659:    * in the document model. This method is deprecated and only there for
 660:    * compatibility.
 661:    *
 662:    * @param x the x coordinate in the view space
 663:    * @param y the y coordinate in the view space
 664:    * @param a the allocation of this <code>View</code>
 665:    *
 666:    * @return the position in the document that corresponds to the screen
 667:    *         coordinates <code>x, y</code>
 668:    *
 669:    * @deprecated Use {@link #viewToModel(float, float, Shape, Position.Bias[])}
 670:    *             instead.
 671:    */
 672:   public int viewToModel(float x, float y, Shape a)
 673:   {
 674:     return viewToModel(x, y, a, new Position.Bias[0]);
 675:   }
 676: 
 677:   /**
 678:    * Dumps the complete View hierarchy. This method can be used for debugging
 679:    * purposes.
 680:    */
 681:   protected void dump()
 682:   {
 683:     // Climb up the hierarchy to the parent.
 684:     View parent = getParent();
 685:     if (parent != null)
 686:       parent.dump();
 687:     else
 688:       dump(0);
 689:   }
 690: 
 691:   /**
 692:    * Dumps the view hierarchy below this View with the specified indentation
 693:    * level.
 694:    *
 695:    * @param indent the indentation level to be used for this view
 696:    */
 697:   void dump(int indent)
 698:   {
 699:     for (int i = 0; i < indent; ++i)
 700:       System.out.print('.');
 701:     System.out.println(this + "(" + getStartOffset() + "," + getEndOffset() + ": " + getElement());
 702: 
 703:     int count = getViewCount();
 704:     for (int i = 0; i < count; ++i)
 705:       getView(i).dump(indent + 1);
 706:   }
 707: 
 708:   /**
 709:    * Returns the document position that is (visually) nearest to the given
 710:    * document position <code>pos</code> in the given direction <code>d</code>.
 711:    *
 712:    * @param pos the document position
 713:    * @param b the bias for <code>pos</code>
 714:    * @param a the allocation for this view
 715:    * @param d the direction, must be either {@link SwingConstants#NORTH},
 716:    *        {@link SwingConstants#SOUTH}, {@link SwingConstants#WEST} or
 717:    *        {@link SwingConstants#EAST}
 718:    * @param biasRet an array of {@link Position.Bias} that can hold at least
 719:    *        one element, which is filled with the bias of the return position
 720:    *        on method exit
 721:    *
 722:    * @return the document position that is (visually) nearest to the given
 723:    *         document position <code>pos</code> in the given direction
 724:    *         <code>d</code>
 725:    *
 726:    * @throws BadLocationException if <code>pos</code> is not a valid offset in
 727:    *         the document model
 728:    * @throws IllegalArgumentException if <code>d</code> is not a valid direction
 729:    */
 730:   public int getNextVisualPositionFrom(int pos, Position.Bias b,
 731:                                        Shape a, int d,
 732:                                        Position.Bias[] biasRet)
 733:     throws BadLocationException
 734:   {
 735:     int ret = pos;
 736:     Rectangle r;
 737:     View parent;
 738: 
 739:     switch (d)
 740:     {
 741:       case EAST:
 742:         // TODO: take component orientation into account?
 743:         // Note: If pos is below zero the implementation will return
 744:         // pos + 1 regardless of whether that value is a correct offset
 745:         // in the document model. However this is what the RI does.
 746:         ret = Math.min(pos + 1, getEndOffset());
 747:         break;
 748:       case WEST:
 749:         // TODO: take component orientation into account?
 750:         ret = Math.max(pos - 1, getStartOffset());
 751:         break;
 752:       case NORTH:
 753:         // Try to find a suitable offset by examining the area above.
 754:         parent = getParent();
 755:         r =  parent.modelToView(pos, a, b).getBounds();
 756:         ret = parent.viewToModel(r.x, r.y - 1, a, biasRet);
 757:         break;
 758:       case SOUTH:
 759:         // Try to find a suitable offset by examining the area below. 
 760:         parent = getParent();
 761:         r =  parent.modelToView(pos, a, b).getBounds();
 762:         ret = parent.viewToModel(r.x + r.width, r.y + r.height, a, biasRet);
 763:         break;
 764:       default:
 765:         throw new IllegalArgumentException("Illegal value for d");
 766:     }
 767:     
 768:     return ret;
 769:   }
 770: }