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 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: }