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