1 /* 2 * Copyright (c) 1997, 2006, Oracle and/or its affiliates. All rights reserved. 3 * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER. 4 * 5 * This code is free software; you can redistribute it and/or modify it 6 * under the terms of the GNU General Public License version 2 only, as 7 * published by the Free Software Foundation. Oracle designates this 8 * particular file as subject to the "Classpath" exception as provided 9 * by Oracle in the LICENSE file that accompanied this code. 10 * 11 * This code is distributed in the hope that it will be useful, but WITHOUT 12 * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or 13 * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License 14 * version 2 for more details (a copy is included in the LICENSE file that 15 * accompanied this code). 16 * 17 * You should have received a copy of the GNU General Public License version 18 * 2 along with this work; if not, write to the Free Software Foundation, 19 * Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA. 20 * 21 * Please contact Oracle, 500 Oracle Parkway, Redwood Shores, CA 94065 USA 22 * or visit www.oracle.com if you need additional information or have any 23 * questions. 24 */ 25 package javax.swing.text; 26 27 import java.util.Vector; 28 import java.awt.*; 29 import javax.swing.event.*; 30 import javax.swing.SwingConstants; 31 32 /** 33 * <code>CompositeView</code> is an abstract <code>View</code> 34 * implementation which manages one or more child views. 35 * (Note that <code>CompositeView</code> is intended 36 * for managing relatively small numbers of child views.) 37 * <code>CompositeView</code> is intended to be used as 38 * a starting point for <code>View</code> implementations, 39 * such as <code>BoxView</code>, that will contain child 40 * <code>View</code>s. Subclasses that wish to manage the 41 * collection of child <code>View</code>s should use the 42 * {@link #replace} method. As <code>View</code> invokes 43 * <code>replace</code> during <code>DocumentListener</code> 44 * notification, you normally won't need to directly 45 * invoke <code>replace</code>. 46 * 47 * <p>While <code>CompositeView</code> 48 * does not impose a layout policy on its child <code>View</code>s, 49 * it does allow for inseting the child <code>View</code>s 50 * it will contain. The insets can be set by either 51 * {@link #setInsets} or {@link #setParagraphInsets}. 52 * 53 * <p>In addition to the abstract methods of 54 * {@link javax.swing.text.View}, 55 * subclasses of <code>CompositeView</code> will need to 56 * override: 57 * <ul> 58 * <li>{@link #isBefore} - Used to test if a given 59 * <code>View</code> location is before the visual space 60 * of the <code>CompositeView</code>. 61 * <li>{@link #isAfter} - Used to test if a given 62 * <code>View</code> location is after the visual space 63 * of the <code>CompositeView</code>. 64 * <li>{@link #getViewAtPoint} - Returns the view at 65 * a given visual location. 66 * <li>{@link #childAllocation} - Returns the bounds of 67 * a particular child <code>View</code>. 68 * <code>getChildAllocation</code> will invoke 69 * <code>childAllocation</code> after offseting 70 * the bounds by the <code>Inset</code>s of the 71 * <code>CompositeView</code>. 72 * </ul> 73 * 74 * @author Timothy Prinzing 75 */ 76 public abstract class CompositeView extends View { 77 78 /** 79 * Constructs a <code>CompositeView</code> for the given element. 80 * 81 * @param elem the element this view is responsible for 82 */ 83 public CompositeView(Element elem) { 84 super(elem); 85 children = new View[1]; 86 nchildren = 0; 87 childAlloc = new Rectangle(); 88 } 89 90 /** 91 * Loads all of the children to initialize the view. 92 * This is called by the {@link #setParent} 93 * method. Subclasses can reimplement this to initialize 94 * their child views in a different manner. The default 95 * implementation creates a child view for each 96 * child element. 97 * 98 * @param f the view factory 99 * @see #setParent 100 */ 101 protected void loadChildren(ViewFactory f) { 102 if (f == null) { 103 // No factory. This most likely indicates the parent view 104 // has changed out from under us, bail! 105 return; 106 } 107 Element e = getElement(); 108 int n = e.getElementCount(); 109 if (n > 0) { 110 View[] added = new View[n]; 111 for (int i = 0; i < n; i++) { 112 added[i] = f.create(e.getElement(i)); 113 } 114 replace(0, 0, added); 115 } 116 } 117 118 // --- View methods --------------------------------------------- 119 120 /** 121 * Sets the parent of the view. 122 * This is reimplemented to provide the superclass 123 * behavior as well as calling the <code>loadChildren</code> 124 * method if this view does not already have children. 125 * The children should not be loaded in the 126 * constructor because the act of setting the parent 127 * may cause them to try to search up the hierarchy 128 * (to get the hosting <code>Container</code> for example). 129 * If this view has children (the view is being moved 130 * from one place in the view hierarchy to another), 131 * the <code>loadChildren</code> method will not be called. 132 * 133 * @param parent the parent of the view, <code>null</code> if none 134 */ 135 public void setParent(View parent) { 136 super.setParent(parent); 137 if ((parent != null) && (nchildren == 0)) { 138 ViewFactory f = getViewFactory(); 139 loadChildren(f); 140 } 141 } 142 143 /** 144 * Returns the number of child views of this view. 145 * 146 * @return the number of views >= 0 147 * @see #getView 148 */ 149 public int getViewCount() { 150 return nchildren; 151 } 152 153 /** 154 * Returns the n-th view in this container. 155 * 156 * @param n the number of the desired view, >= 0 && < getViewCount() 157 * @return the view at index <code>n</code> 158 */ 159 public View getView(int n) { 160 return children[n]; 161 } 162 163 /** 164 * Replaces child views. If there are no views to remove 165 * this acts as an insert. If there are no views to 166 * add this acts as a remove. Views being removed will 167 * have the parent set to <code>null</code>, 168 * and the internal reference to them removed so that they 169 * may be garbage collected. 170 * 171 * @param offset the starting index into the child views to insert 172 * the new views; >= 0 and <= getViewCount 173 * @param length the number of existing child views to remove; 174 * this should be a value >= 0 and <= (getViewCount() - offset) 175 * @param views the child views to add; this value can be 176 * <code>null</code> 177 * to indicate no children are being added (useful to remove) 178 */ 179 public void replace(int offset, int length, View[] views) { 180 // make sure an array exists 181 if (views == null) { 182 views = ZERO; 183 } 184 185 // update parent reference on removed views 186 for (int i = offset; i < offset + length; i++) { 187 if (children[i].getParent() == this) { 188 // in FlowView.java view might be referenced 189 // from two super-views as a child. see logicalView 190 children[i].setParent(null); 191 } 192 children[i] = null; 193 } 194 195 // update the array 196 int delta = views.length - length; 197 int src = offset + length; 198 int nmove = nchildren - src; 199 int dest = src + delta; 200 if ((nchildren + delta) >= children.length) { 201 // need to grow the array 202 int newLength = Math.max(2*children.length, nchildren + delta); 203 View[] newChildren = new View[newLength]; 204 System.arraycopy(children, 0, newChildren, 0, offset); 205 System.arraycopy(views, 0, newChildren, offset, views.length); 206 System.arraycopy(children, src, newChildren, dest, nmove); 207 children = newChildren; 208 } else { 209 // patch the existing array 210 System.arraycopy(children, src, children, dest, nmove); 211 System.arraycopy(views, 0, children, offset, views.length); 212 } 213 nchildren = nchildren + delta; 214 215 // update parent reference on added views 216 for (int i = 0; i < views.length; i++) { 217 views[i].setParent(this); 218 } 219 } 220 221 /** 222 * Fetches the allocation for the given child view to 223 * render into. This enables finding out where various views 224 * are located. 225 * 226 * @param index the index of the child, >= 0 && < getViewCount() 227 * @param a the allocation to this view 228 * @return the allocation to the child 229 */ 230 public Shape getChildAllocation(int index, Shape a) { 231 Rectangle alloc = getInsideAllocation(a); 232 childAllocation(index, alloc); 233 return alloc; 234 } 235 236 /** 237 * Provides a mapping from the document model coordinate space 238 * to the coordinate space of the view mapped to it. 239 * 240 * @param pos the position to convert >= 0 241 * @param a the allocated region to render into 242 * @param b a bias value of either <code>Position.Bias.Forward</code> 243 * or <code>Position.Bias.Backward</code> 244 * @return the bounding box of the given position 245 * @exception BadLocationException if the given position does 246 * not represent a valid location in the associated document 247 * @see View#modelToView 248 */ 249 public Shape modelToView(int pos, Shape a, Position.Bias b) throws BadLocationException { 250 boolean isBackward = (b == Position.Bias.Backward); 251 int testPos = (isBackward) ? Math.max(0, pos - 1) : pos; 252 if(isBackward && testPos < getStartOffset()) { 253 return null; 254 } 255 int vIndex = getViewIndexAtPosition(testPos); 256 if ((vIndex != -1) && (vIndex < getViewCount())) { 257 View v = getView(vIndex); 258 if(v != null && testPos >= v.getStartOffset() && 259 testPos < v.getEndOffset()) { 260 Shape childShape = getChildAllocation(vIndex, a); 261 if (childShape == null) { 262 // We are likely invalid, fail. 263 return null; 264 } 265 Shape retShape = v.modelToView(pos, childShape, b); 266 if(retShape == null && v.getEndOffset() == pos) { 267 if(++vIndex < getViewCount()) { 268 v = getView(vIndex); 269 retShape = v.modelToView(pos, getChildAllocation(vIndex, a), b); 270 } 271 } 272 return retShape; 273 } 274 } 275 throw new BadLocationException("Position not represented by view", 276 pos); 277 } 278 279 /** 280 * Provides a mapping from the document model coordinate space 281 * to the coordinate space of the view mapped to it. 282 * 283 * @param p0 the position to convert >= 0 284 * @param b0 the bias toward the previous character or the 285 * next character represented by p0, in case the 286 * position is a boundary of two views; either 287 * <code>Position.Bias.Forward</code> or 288 * <code>Position.Bias.Backward</code> 289 * @param p1 the position to convert >= 0 290 * @param b1 the bias toward the previous character or the 291 * next character represented by p1, in case the 292 * position is a boundary of two views 293 * @param a the allocated region to render into 294 * @return the bounding box of the given position is returned 295 * @exception BadLocationException if the given position does 296 * not represent a valid location in the associated document 297 * @exception IllegalArgumentException for an invalid bias argument 298 * @see View#viewToModel 299 */ 300 public Shape modelToView(int p0, Position.Bias b0, int p1, Position.Bias b1, Shape a) throws BadLocationException { 301 if (p0 == getStartOffset() && p1 == getEndOffset()) { 302 return a; 303 } 304 Rectangle alloc = getInsideAllocation(a); 305 Rectangle r0 = new Rectangle(alloc); 306 View v0 = getViewAtPosition((b0 == Position.Bias.Backward) ? 307 Math.max(0, p0 - 1) : p0, r0); 308 Rectangle r1 = new Rectangle(alloc); 309 View v1 = getViewAtPosition((b1 == Position.Bias.Backward) ? 310 Math.max(0, p1 - 1) : p1, r1); 311 if (v0 == v1) { 312 if (v0 == null) { 313 return a; 314 } 315 // Range contained in one view 316 return v0.modelToView(p0, b0, p1, b1, r0); 317 } 318 // Straddles some views. 319 int viewCount = getViewCount(); 320 int counter = 0; 321 while (counter < viewCount) { 322 View v; 323 // Views may not be in same order as model. 324 // v0 or v1 may be null if there is a gap in the range this 325 // view contains. 326 if ((v = getView(counter)) == v0 || v == v1) { 327 View endView; 328 Rectangle retRect; 329 Rectangle tempRect = new Rectangle(); 330 if (v == v0) { 331 retRect = v0.modelToView(p0, b0, v0.getEndOffset(), 332 Position.Bias.Backward, r0). 333 getBounds(); 334 endView = v1; 335 } 336 else { 337 retRect = v1.modelToView(v1.getStartOffset(), 338 Position.Bias.Forward, 339 p1, b1, r1).getBounds(); 340 endView = v0; 341 } 342 343 // Views entirely covered by range. 344 while (++counter < viewCount && 345 (v = getView(counter)) != endView) { 346 tempRect.setBounds(alloc); 347 childAllocation(counter, tempRect); 348 retRect.add(tempRect); 349 } 350 351 // End view. 352 if (endView != null) { 353 Shape endShape; 354 if (endView == v1) { 355 endShape = v1.modelToView(v1.getStartOffset(), 356 Position.Bias.Forward, 357 p1, b1, r1); 358 } 359 else { 360 endShape = v0.modelToView(p0, b0, v0.getEndOffset(), 361 Position.Bias.Backward, r0); 362 } 363 if (endShape instanceof Rectangle) { 364 retRect.add((Rectangle)endShape); 365 } 366 else { 367 retRect.add(endShape.getBounds()); 368 } 369 } 370 return retRect; 371 } 372 counter++; 373 } 374 throw new BadLocationException("Position not represented by view", p0); 375 } 376 377 /** 378 * Provides a mapping from the view coordinate space to the logical 379 * coordinate space of the model. 380 * 381 * @param x x coordinate of the view location to convert >= 0 382 * @param y y coordinate of the view location to convert >= 0 383 * @param a the allocated region to render into 384 * @param bias either <code>Position.Bias.Forward</code> or 385 * <code>Position.Bias.Backward</code> 386 * @return the location within the model that best represents the 387 * given point in the view >= 0 388 * @see View#viewToModel 389 */ 390 public int viewToModel(float x, float y, Shape a, Position.Bias[] bias) { 391 Rectangle alloc = getInsideAllocation(a); 392 if (isBefore((int) x, (int) y, alloc)) { 393 // point is before the range represented 394 int retValue = -1; 395 396 try { 397 retValue = getNextVisualPositionFrom(-1, Position.Bias.Forward, 398 a, EAST, bias); 399 } catch (BadLocationException ble) { } 400 catch (IllegalArgumentException iae) { } 401 if(retValue == -1) { 402 retValue = getStartOffset(); 403 bias[0] = Position.Bias.Forward; 404 } 405 return retValue; 406 } else if (isAfter((int) x, (int) y, alloc)) { 407 // point is after the range represented. 408 int retValue = -1; 409 try { 410 retValue = getNextVisualPositionFrom(-1, Position.Bias.Forward, 411 a, WEST, bias); 412 } catch (BadLocationException ble) { } 413 catch (IllegalArgumentException iae) { } 414 415 if(retValue == -1) { 416 // NOTE: this could actually use end offset with backward. 417 retValue = getEndOffset() - 1; 418 bias[0] = Position.Bias.Forward; 419 } 420 return retValue; 421 } else { 422 // locate the child and pass along the request 423 View v = getViewAtPoint((int) x, (int) y, alloc); 424 if (v != null) { 425 return v.viewToModel(x, y, alloc, bias); 426 } 427 } 428 return -1; 429 } 430 431 /** 432 * Provides a way to determine the next visually represented model 433 * location that one might place a caret. Some views may not be visible, 434 * they might not be in the same order found in the model, or they just 435 * might not allow access to some of the locations in the model. 436 * This is a convenience method for {@link #getNextNorthSouthVisualPositionFrom} 437 * and {@link #getNextEastWestVisualPositionFrom}. 438 * This method enables specifying a position to convert 439 * within the range of >=0. If the value is -1, a position 440 * will be calculated automatically. If the value < -1, 441 * the {@code BadLocationException} will be thrown. 442 * 443 * @param pos the position to convert 444 * @param b a bias value of either <code>Position.Bias.Forward</code> 445 * or <code>Position.Bias.Backward</code> 446 * @param a the allocated region to render into 447 * @param direction the direction from the current position that can 448 * be thought of as the arrow keys typically found on a keyboard; 449 * this may be one of the following: 450 * <ul> 451 * <li><code>SwingConstants.WEST</code> 452 * <li><code>SwingConstants.EAST</code> 453 * <li><code>SwingConstants.NORTH</code> 454 * <li><code>SwingConstants.SOUTH</code> 455 * </ul> 456 * @param biasRet an array containing the bias that was checked 457 * @return the location within the model that best represents the next 458 * location visual position 459 * @exception BadLocationException the given position is not a valid 460 * position within the document 461 * @exception IllegalArgumentException if <code>direction</code> is invalid 462 */ 463 public int getNextVisualPositionFrom(int pos, Position.Bias b, Shape a, 464 int direction, Position.Bias[] biasRet) 465 throws BadLocationException { 466 if (pos < -1) { 467 throw new BadLocationException("invalid position", pos); 468 } 469 Rectangle alloc = getInsideAllocation(a); 470 471 switch (direction) { 472 case NORTH: 473 return getNextNorthSouthVisualPositionFrom(pos, b, a, direction, 474 biasRet); 475 case SOUTH: 476 return getNextNorthSouthVisualPositionFrom(pos, b, a, direction, 477 biasRet); 478 case EAST: 479 return getNextEastWestVisualPositionFrom(pos, b, a, direction, 480 biasRet); 481 case WEST: 482 return getNextEastWestVisualPositionFrom(pos, b, a, direction, 483 biasRet); 484 default: 485 throw new IllegalArgumentException("Bad direction: " + direction); 486 } 487 } 488 489 /** 490 * Returns the child view index representing the given 491 * position in the model. This is implemented to call the 492 * <code>getViewIndexByPosition</code> 493 * method for backward compatibility. 494 * 495 * @param pos the position >= 0 496 * @return index of the view representing the given position, or 497 * -1 if no view represents that position 498 * @since 1.3 499 */ 500 public int getViewIndex(int pos, Position.Bias b) { 501 if(b == Position.Bias.Backward) { 502 pos -= 1; 503 } 504 if ((pos >= getStartOffset()) && (pos < getEndOffset())) { 505 return getViewIndexAtPosition(pos); 506 } 507 return -1; 508 } 509 510 // --- local methods ---------------------------------------------------- 511 512 513 /** 514 * Tests whether a point lies before the rectangle range. 515 * 516 * @param x the X coordinate >= 0 517 * @param y the Y coordinate >= 0 518 * @param alloc the rectangle 519 * @return true if the point is before the specified range 520 */ 521 protected abstract boolean isBefore(int x, int y, Rectangle alloc); 522 523 /** 524 * Tests whether a point lies after the rectangle range. 525 * 526 * @param x the X coordinate >= 0 527 * @param y the Y coordinate >= 0 528 * @param alloc the rectangle 529 * @return true if the point is after the specified range 530 */ 531 protected abstract boolean isAfter(int x, int y, Rectangle alloc); 532 533 /** 534 * Fetches the child view at the given coordinates. 535 * 536 * @param x the X coordinate >= 0 537 * @param y the Y coordinate >= 0 538 * @param alloc the parent's allocation on entry, which should 539 * be changed to the child's allocation on exit 540 * @return the child view 541 */ 542 protected abstract View getViewAtPoint(int x, int y, Rectangle alloc); 543 544 /** 545 * Returns the allocation for a given child. 546 * 547 * @param index the index of the child, >= 0 && < getViewCount() 548 * @param a the allocation to the interior of the box on entry, 549 * and the allocation of the child view at the index on exit. 550 */ 551 protected abstract void childAllocation(int index, Rectangle a); 552 553 /** 554 * Fetches the child view that represents the given position in 555 * the model. This is implemented to fetch the view in the case 556 * where there is a child view for each child element. 557 * 558 * @param pos the position >= 0 559 * @param a the allocation to the interior of the box on entry, 560 * and the allocation of the view containing the position on exit 561 * @return the view representing the given position, or 562 * <code>null</code> if there isn't one 563 */ 564 protected View getViewAtPosition(int pos, Rectangle a) { 565 int index = getViewIndexAtPosition(pos); 566 if ((index >= 0) && (index < getViewCount())) { 567 View v = getView(index); 568 if (a != null) { 569 childAllocation(index, a); 570 } 571 return v; 572 } 573 return null; 574 } 575 576 /** 577 * Fetches the child view index representing the given position in 578 * the model. This is implemented to fetch the view in the case 579 * where there is a child view for each child element. 580 * 581 * @param pos the position >= 0 582 * @return index of the view representing the given position, or 583 * -1 if no view represents that position 584 */ 585 protected int getViewIndexAtPosition(int pos) { 586 Element elem = getElement(); 587 return elem.getElementIndex(pos); 588 } 589 590 /** 591 * Translates the immutable allocation given to the view 592 * to a mutable allocation that represents the interior 593 * allocation (i.e. the bounds of the given allocation 594 * with the top, left, bottom, and right insets removed. 595 * It is expected that the returned value would be further 596 * mutated to represent an allocation to a child view. 597 * This is implemented to reuse an instance variable so 598 * it avoids creating excessive Rectangles. Typically 599 * the result of calling this method would be fed to 600 * the <code>childAllocation</code> method. 601 * 602 * @param a the allocation given to the view 603 * @return the allocation that represents the inside of the 604 * view after the margins have all been removed; if the 605 * given allocation was <code>null</code>, 606 * the return value is <code>null</code> 607 */ 608 protected Rectangle getInsideAllocation(Shape a) { 609 if (a != null) { 610 // get the bounds, hopefully without allocating 611 // a new rectangle. The Shape argument should 612 // not be modified... we copy it into the 613 // child allocation. 614 Rectangle alloc; 615 if (a instanceof Rectangle) { 616 alloc = (Rectangle) a; 617 } else { 618 alloc = a.getBounds(); 619 } 620 621 childAlloc.setBounds(alloc); 622 childAlloc.x += getLeftInset(); 623 childAlloc.y += getTopInset(); 624 childAlloc.width -= getLeftInset() + getRightInset(); 625 childAlloc.height -= getTopInset() + getBottomInset(); 626 return childAlloc; 627 } 628 return null; 629 } 630 631 /** 632 * Sets the insets from the paragraph attributes specified in 633 * the given attributes. 634 * 635 * @param attr the attributes 636 */ 637 protected void setParagraphInsets(AttributeSet attr) { 638 // Since version 1.1 doesn't have scaling and assumes 639 // a pixel is equal to a point, we just cast the point 640 // sizes to integers. 641 top = (short) StyleConstants.getSpaceAbove(attr); 642 left = (short) StyleConstants.getLeftIndent(attr); 643 bottom = (short) StyleConstants.getSpaceBelow(attr); 644 right = (short) StyleConstants.getRightIndent(attr); 645 } 646 647 /** 648 * Sets the insets for the view. 649 * 650 * @param top the top inset >= 0 651 * @param left the left inset >= 0 652 * @param bottom the bottom inset >= 0 653 * @param right the right inset >= 0 654 */ 655 protected void setInsets(short top, short left, short bottom, short right) { 656 this.top = top; 657 this.left = left; 658 this.right = right; 659 this.bottom = bottom; 660 } 661 662 /** 663 * Gets the left inset. 664 * 665 * @return the inset >= 0 666 */ 667 protected short getLeftInset() { 668 return left; 669 } 670 671 /** 672 * Gets the right inset. 673 * 674 * @return the inset >= 0 675 */ 676 protected short getRightInset() { 677 return right; 678 } 679 680 /** 681 * Gets the top inset. 682 * 683 * @return the inset >= 0 684 */ 685 protected short getTopInset() { 686 return top; 687 } 688 689 /** 690 * Gets the bottom inset. 691 * 692 * @return the inset >= 0 693 */ 694 protected short getBottomInset() { 695 return bottom; 696 } 697 698 /** 699 * Returns the next visual position for the cursor, in either the 700 * north or south direction. 701 * 702 * @param pos the position to convert >= 0 703 * @param b a bias value of either <code>Position.Bias.Forward</code> 704 * or <code>Position.Bias.Backward</code> 705 * @param a the allocated region to render into 706 * @param direction the direction from the current position that can 707 * be thought of as the arrow keys typically found on a keyboard; 708 * this may be one of the following: 709 * <ul> 710 * <li><code>SwingConstants.NORTH</code> 711 * <li><code>SwingConstants.SOUTH</code> 712 * </ul> 713 * @param biasRet an array containing the bias that was checked 714 * @return the location within the model that best represents the next 715 * north or south location 716 * @exception BadLocationException 717 * @exception IllegalArgumentException if <code>direction</code> is invalid 718 * @see #getNextVisualPositionFrom 719 * 720 * @return the next position west of the passed in position 721 */ 722 protected int getNextNorthSouthVisualPositionFrom(int pos, Position.Bias b, 723 Shape a, int direction, 724 Position.Bias[] biasRet) 725 throws BadLocationException { 726 return Utilities.getNextVisualPositionFrom( 727 this, pos, b, a, direction, biasRet); 728 } 729 730 /** 731 * Returns the next visual position for the cursor, in either the 732 * east or west direction. 733 * 734 * @param pos the position to convert >= 0 735 * @param b a bias value of either <code>Position.Bias.Forward</code> 736 * or <code>Position.Bias.Backward</code> 737 * @param a the allocated region to render into 738 * @param direction the direction from the current position that can 739 * be thought of as the arrow keys typically found on a keyboard; 740 * this may be one of the following: 741 * <ul> 742 * <li><code>SwingConstants.WEST</code> 743 * <li><code>SwingConstants.EAST</code> 744 * </ul> 745 * @param biasRet an array containing the bias that was checked 746 * @return the location within the model that best represents the next 747 * west or east location 748 * @exception BadLocationException 749 * @exception IllegalArgumentException if <code>direction</code> is invalid 750 * @see #getNextVisualPositionFrom 751 */ 752 protected int getNextEastWestVisualPositionFrom(int pos, Position.Bias b, 753 Shape a, 754 int direction, 755 Position.Bias[] biasRet) 756 throws BadLocationException { 757 return Utilities.getNextVisualPositionFrom( 758 this, pos, b, a, direction, biasRet); 759 } 760 761 /** 762 * Determines in which direction the next view lays. 763 * Consider the <code>View</code> at index n. Typically the 764 * <code>View</code>s are layed out from left to right, 765 * so that the <code>View</code> to the EAST will be 766 * at index n + 1, and the <code>View</code> to the WEST 767 * will be at index n - 1. In certain situations, 768 * such as with bidirectional text, it is possible 769 * that the <code>View</code> to EAST is not at index n + 1, 770 * but rather at index n - 1, or that the <code>View</code> 771 * to the WEST is not at index n - 1, but index n + 1. 772 * In this case this method would return true, indicating the 773 * <code>View</code>s are layed out in descending order. 774 * <p> 775 * This unconditionally returns false, subclasses should override this 776 * method if there is the possibility for laying <code>View</code>s in 777 * descending order. 778 * 779 * @param position position into the model 780 * @param bias either <code>Position.Bias.Forward</code> or 781 * <code>Position.Bias.Backward</code> 782 * @return false 783 */ 784 protected boolean flipEastAndWestAtEnds(int position, 785 Position.Bias bias) { 786 return false; 787 } 788 789 790 // ---- member variables --------------------------------------------- 791 792 793 private static View[] ZERO = new View[0]; 794 795 private View[] children; 796 private int nchildren; 797 private short left; 798 private short right; 799 private short top; 800 private short bottom; 801 private Rectangle childAlloc; 802 }