Frames | No Frames |
1: /* CompositeView.java -- An abstract view that manages child views 2: Copyright (C) 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.Insets; 42: import java.awt.Rectangle; 43: import java.awt.Shape; 44: 45: import javax.swing.SwingConstants; 46: 47: /** 48: * An abstract base implementation of {@link View} that manages child 49: * <code>View</code>s. 50: * 51: * @author Roman Kennke (roman@kennke.org) 52: */ 53: public abstract class CompositeView 54: extends View 55: { 56: 57: /** 58: * The child views of this <code>CompositeView</code>. 59: */ 60: View[] children; 61: 62: /** 63: * The allocation of this <code>View</code> minus its insets. This is 64: * initialized in {@link #getInsideAllocation} and reused and modified in 65: * {@link #childAllocation(int, Rectangle)}. 66: */ 67: Rectangle insideAllocation; 68: 69: /** 70: * The insets of this <code>CompositeView</code>. This is initialized 71: * in {@link #setInsets}. 72: */ 73: Insets insets; 74: 75: /** 76: * Creates a new <code>CompositeView</code> for the given 77: * <code>Element</code>. 78: * 79: * @param element the element that is rendered by this CompositeView 80: */ 81: public CompositeView(Element element) 82: { 83: super(element); 84: children = new View[0]; 85: insets = new Insets(0, 0, 0, 0); 86: } 87: 88: /** 89: * Loads the child views of this <code>CompositeView</code>. This method 90: * is called from {@link #setParent} to initialize the child views of 91: * this composite view. 92: * 93: * @param f the view factory to use for creating new child views 94: * 95: * @see #setParent 96: */ 97: protected void loadChildren(ViewFactory f) 98: { 99: Element el = getElement(); 100: int count = el.getElementCount(); 101: View[] newChildren = new View[count]; 102: for (int i = 0; i < count; ++i) 103: { 104: Element child = el.getElement(i); 105: View view = f.create(child); 106: newChildren[i] = view; 107: } 108: replace(0, getViewCount(), newChildren); 109: } 110: 111: /** 112: * Sets the parent of this <code>View</code>. 113: * In addition to setting the parent, this calls {@link #loadChildren}, if 114: * this <code>View</code> does not already have its children initialized. 115: * 116: * @param parent the parent to set 117: */ 118: public void setParent(View parent) 119: { 120: super.setParent(parent); 121: if (parent != null && ((children == null) || children.length == 0)) 122: loadChildren(getViewFactory()); 123: } 124: 125: /** 126: * Returns the number of child views. 127: * 128: * @return the number of child views 129: */ 130: public int getViewCount() 131: { 132: return children.length; 133: } 134: 135: /** 136: * Returns the child view at index <code>n</code>. 137: * 138: * @param n the index of the requested child view 139: * 140: * @return the child view at index <code>n</code> 141: */ 142: public View getView(int n) 143: { 144: return children[n]; 145: } 146: 147: /** 148: * Replaces child views by some other child views. If there are no views to 149: * remove (<code>length == 0</code>), the result is a simple insert, if 150: * there are no children to add (<code>view == null</code>) the result 151: * is a simple removal. 152: * 153: * @param offset the start offset from where to remove children 154: * @param length the number of children to remove 155: * @param views the views that replace the removed children 156: */ 157: public void replace(int offset, int length, View[] views) 158: { 159: // Check for null views to add. 160: for (int i = 0; i < views.length; ++i) 161: if (views[i] == null) 162: throw new NullPointerException("Added views must not be null"); 163: 164: int endOffset = offset + length; 165: 166: // First we set the parent of the removed children to null. 167: for (int i = offset; i < endOffset; ++i) 168: children[i].setParent(null); 169: 170: View[] newChildren = new View[children.length - length + views.length]; 171: System.arraycopy(children, 0, newChildren, 0, offset); 172: System.arraycopy(views, 0, newChildren, offset, views.length); 173: System.arraycopy(children, offset + length, newChildren, 174: offset + views.length, 175: children.length - (offset + length)); 176: children = newChildren; 177: 178: // Finally we set the parent of the added children to this. 179: for (int i = 0; i < views.length; ++i) 180: views[i].setParent(this); 181: } 182: 183: /** 184: * Returns the allocation for the specified child <code>View</code>. 185: * 186: * @param index the index of the child view 187: * @param a the allocation for this view 188: * 189: * @return the allocation for the specified child <code>View</code> 190: */ 191: public Shape getChildAllocation(int index, Shape a) 192: { 193: Rectangle r = getInsideAllocation(a); 194: childAllocation(index, r); 195: return r; 196: } 197: 198: /** 199: * Maps a position in the document into the coordinate space of the View. 200: * The output rectangle usually reflects the font height but has a width 201: * of zero. 202: * 203: * @param pos the position of the character in the model 204: * @param a the area that is occupied by the view 205: * @param bias either {@link Position.Bias#Forward} or 206: * {@link Position.Bias#Backward} depending on the preferred 207: * direction bias. If <code>null</code> this defaults to 208: * <code>Position.Bias.Forward</code> 209: * 210: * @return a rectangle that gives the location of the document position 211: * inside the view coordinate space 212: * 213: * @throws BadLocationException if <code>pos</code> is invalid 214: * @throws IllegalArgumentException if b is not one of the above listed 215: * valid values 216: */ 217: public Shape modelToView(int pos, Shape a, Position.Bias bias) 218: throws BadLocationException 219: { 220: boolean backward = bias == Position.Bias.Backward; 221: int testpos = backward ? Math.max(0, pos - 1) : pos; 222: 223: Shape ret = null; 224: if (! backward || testpos >= getStartOffset()) 225: { 226: int childIndex = getViewIndexAtPosition(testpos); 227: if (childIndex != -1 && childIndex < getViewCount()) 228: { 229: View child = getView(childIndex); 230: if (child != null && testpos >= child.getStartOffset() 231: && testpos < child.getEndOffset()) 232: { 233: Shape childAlloc = getChildAllocation(childIndex, a); 234: if (childAlloc != null) 235: { 236: ret = child.modelToView(pos, childAlloc, bias); 237: // Handle corner case. 238: if (ret == null && child.getEndOffset() == pos) 239: { 240: childIndex++; 241: if (childIndex < getViewCount()) 242: { 243: child = getView(childIndex); 244: childAlloc = getChildAllocation(childIndex, a); 245: ret = child.modelToView(pos, childAlloc, bias); 246: } 247: } 248: } 249: } 250: } 251: else 252: { 253: throw new BadLocationException("Position " + pos 254: + " is not represented by view.", pos); 255: } 256: } 257: return ret; 258: } 259: 260: /** 261: * A helper method for {@link #modelToView(int, Position.Bias, int, 262: * Position.Bias, Shape)}. This creates a default location when there is 263: * no child view that can take responsibility for mapping the position to 264: * view coordinates. Depending on the specified bias this will be the 265: * left or right edge of this view's allocation. 266: * 267: * @param a the allocation for this view 268: * @param bias the bias 269: * 270: * @return a default location 271: */ 272: private Shape createDefaultLocation(Shape a, Position.Bias bias) 273: { 274: Rectangle alloc = a.getBounds(); 275: Rectangle location = new Rectangle(alloc.x, alloc.y, 1, alloc.height); 276: if (bias == Position.Bias.Forward) 277: location.x = alloc.x + alloc.width; 278: return location; 279: } 280: 281: /** 282: * Maps a region in the document into the coordinate space of the View. 283: * 284: * @param p1 the beginning position inside the document 285: * @param b1 the direction bias for the beginning position 286: * @param p2 the end position inside the document 287: * @param b2 the direction bias for the end position 288: * @param a the area that is occupied by the view 289: * 290: * @return a rectangle that gives the span of the document region 291: * inside the view coordinate space 292: * 293: * @throws BadLocationException if <code>p1</code> or <code>p2</code> are 294: * invalid 295: * @throws IllegalArgumentException if b1 or b2 is not one of the above 296: * listed valid values 297: */ 298: public Shape modelToView(int p1, Position.Bias b1, 299: int p2, Position.Bias b2, Shape a) 300: throws BadLocationException 301: { 302: // TODO: This is most likely not 100% ok, figure out what else is to 303: // do here. 304: return super.modelToView(p1, b1, p2, b2, a); 305: } 306: 307: /** 308: * Maps coordinates from the <code>View</code>'s space into a position 309: * in the document model. 310: * 311: * @param x the x coordinate in the view space, x >= 0 312: * @param y the y coordinate in the view space, y >= 0 313: * @param a the allocation of this <code>View</code> 314: * @param b the bias to use 315: * 316: * @return the position in the document that corresponds to the screen 317: * coordinates <code>x, y</code> >= 0 318: */ 319: public int viewToModel(float x, float y, Shape a, Position.Bias[] b) 320: { 321: if (x >= 0 && y >= 0) 322: { 323: Rectangle r = getInsideAllocation(a); 324: View view = getViewAtPoint((int) x, (int) y, r); 325: return view.viewToModel(x, y, r, b); 326: } 327: return 0; 328: } 329: 330: /** 331: * Returns the next model location that is visible in eiter north / south 332: * direction or east / west direction. This is used to determine the placement 333: * of the caret when navigating around the document with the arrow keys. This 334: * is a convenience method for {@link #getNextNorthSouthVisualPositionFrom} 335: * and {@link #getNextEastWestVisualPositionFrom}. 336: * 337: * @param pos 338: * the model position to start search from 339: * @param b 340: * the bias for <code>pos</code> 341: * @param a 342: * the allocated region for this view 343: * @param direction 344: * the direction from the current position, can be one of the 345: * following: 346: * <ul> 347: * <li>{@link SwingConstants#WEST}</li> 348: * <li>{@link SwingConstants#EAST}</li> 349: * <li>{@link SwingConstants#NORTH}</li> 350: * <li>{@link SwingConstants#SOUTH}</li> 351: * </ul> 352: * @param biasRet 353: * the bias of the return value gets stored here 354: * @return the position inside the model that represents the next visual 355: * location 356: * @throws BadLocationException 357: * if <code>pos</code> is not a valid location inside the document 358: * model 359: * @throws IllegalArgumentException 360: * if <code>direction</code> is invalid 361: */ 362: public int getNextVisualPositionFrom(int pos, Position.Bias b, Shape a, 363: int direction, Position.Bias[] biasRet) 364: throws BadLocationException 365: { 366: int retVal = -1; 367: switch (direction) 368: { 369: case SwingConstants.WEST: 370: case SwingConstants.EAST: 371: retVal = getNextEastWestVisualPositionFrom(pos, b, a, direction, 372: biasRet); 373: break; 374: case SwingConstants.NORTH: 375: case SwingConstants.SOUTH: 376: retVal = getNextNorthSouthVisualPositionFrom(pos, b, a, direction, 377: biasRet); 378: break; 379: default: 380: throw new IllegalArgumentException("Illegal value for direction."); 381: } 382: return retVal; 383: } 384: 385: /** 386: * Returns the index of the child view that represents the specified 387: * model location. 388: * 389: * @param pos the model location for which to determine the child view index 390: * @param b the bias to be applied to <code>pos</code> 391: * 392: * @return the index of the child view that represents the specified 393: * model location 394: */ 395: public int getViewIndex(int pos, Position.Bias b) 396: { 397: if (b == Position.Bias.Backward && pos != 0) 398: pos -= 1; 399: int i = -1; 400: if (pos >= getStartOffset() && pos < getEndOffset()) 401: i = getViewIndexAtPosition(pos); 402: return i; 403: } 404: 405: /** 406: * Returns <code>true</code> if the specified point lies before the 407: * given <code>Rectangle</code>, <code>false</code> otherwise. 408: * 409: * "Before" is typically defined as being to the left or above. 410: * 411: * @param x the X coordinate of the point 412: * @param y the Y coordinate of the point 413: * @param r the rectangle to test the point against 414: * 415: * @return <code>true</code> if the specified point lies before the 416: * given <code>Rectangle</code>, <code>false</code> otherwise 417: */ 418: protected abstract boolean isBefore(int x, int y, Rectangle r); 419: 420: /** 421: * Returns <code>true</code> if the specified point lies after the 422: * given <code>Rectangle</code>, <code>false</code> otherwise. 423: * 424: * "After" is typically defined as being to the right or below. 425: * 426: * @param x the X coordinate of the point 427: * @param y the Y coordinate of the point 428: * @param r the rectangle to test the point against 429: * 430: * @return <code>true</code> if the specified point lies after the 431: * given <code>Rectangle</code>, <code>false</code> otherwise 432: */ 433: protected abstract boolean isAfter(int x, int y, Rectangle r); 434: 435: /** 436: * Returns the child <code>View</code> at the specified location. 437: * 438: * @param x the X coordinate 439: * @param y the Y coordinate 440: * @param r the inner allocation of this <code>BoxView</code> on entry, 441: * the allocation of the found child on exit 442: * 443: * @return the child <code>View</code> at the specified location 444: */ 445: protected abstract View getViewAtPoint(int x, int y, Rectangle r); 446: 447: /** 448: * Computes the allocation for a child <code>View</code>. The parameter 449: * <code>a</code> stores the allocation of this <code>CompositeView</code> 450: * and is then adjusted to hold the allocation of the child view. 451: * 452: * @param index the index of the child <code>View</code> 453: * @param a the allocation of this <code>CompositeView</code> before the 454: * call, the allocation of the child on exit 455: */ 456: protected abstract void childAllocation(int index, Rectangle a); 457: 458: /** 459: * Returns the child <code>View</code> that contains the given model 460: * position. The given <code>Rectangle</code> gives the parent's allocation 461: * and is changed to the child's allocation on exit. 462: * 463: * @param pos the model position to query the child <code>View</code> for 464: * @param a the parent allocation on entry and the child allocation on exit 465: * 466: * @return the child view at the given model position 467: */ 468: protected View getViewAtPosition(int pos, Rectangle a) 469: { 470: View view = null; 471: int i = getViewIndexAtPosition(pos); 472: if (i >= 0 && i < getViewCount() && a != null) 473: { 474: view = getView(i); 475: childAllocation(i, a); 476: } 477: return view; 478: } 479: 480: /** 481: * Returns the index of the child <code>View</code> for the given model 482: * position. 483: * 484: * @param pos the model position for whicht the child <code>View</code> is 485: * queried 486: * 487: * @return the index of the child <code>View</code> for the given model 488: * position 489: */ 490: protected int getViewIndexAtPosition(int pos) 491: { 492: // We have a 1:1 mapping of elements to views here, so we forward 493: // this to the element. 494: Element el = getElement(); 495: return el.getElementIndex(pos); 496: } 497: 498: /** 499: * Returns the allocation that is given to this <code>CompositeView</code> 500: * minus this <code>CompositeView</code>'s insets. 501: * 502: * Also this translates from an immutable allocation to a mutable allocation 503: * that is typically reused and further narrowed, like in 504: * {@link #childAllocation}. 505: * 506: * @param a the allocation given to this <code>CompositeView</code> 507: * 508: * @return the allocation that is given to this <code>CompositeView</code> 509: * minus this <code>CompositeView</code>'s insets or 510: * <code>null</code> if a was <code>null</code> 511: */ 512: protected Rectangle getInsideAllocation(Shape a) 513: { 514: if (a == null) 515: return null; 516: 517: Rectangle alloc = a.getBounds(); 518: // Initialize the inside allocation rectangle. This is done inside 519: // a synchronized block in order to avoid multiple threads creating 520: // this instance simultanously. 521: Rectangle inside; 522: synchronized(this) 523: { 524: inside = insideAllocation; 525: if (inside == null) 526: { 527: inside = new Rectangle(); 528: insideAllocation = inside; 529: } 530: } 531: inside.x = alloc.x + insets.left; 532: inside.y = alloc.y + insets.top; 533: inside.width = alloc.width - insets.left - insets.right; 534: inside.height = alloc.height - insets.top - insets.bottom; 535: return inside; 536: } 537: 538: /** 539: * Sets the insets defined by attributes in <code>attributes</code>. This 540: * queries the attribute keys {@link StyleConstants#SpaceAbove}, 541: * {@link StyleConstants#SpaceBelow}, {@link StyleConstants#LeftIndent} and 542: * {@link StyleConstants#RightIndent} and calls {@link #setInsets} to 543: * actually set the insets on this <code>CompositeView</code>. 544: * 545: * @param attributes the attributes from which to query the insets 546: */ 547: protected void setParagraphInsets(AttributeSet attributes) 548: { 549: Float l = (Float) attributes.getAttribute(StyleConstants.LeftIndent); 550: short left = 0; 551: if (l != null) 552: left = l.shortValue(); 553: Float r = (Float) attributes.getAttribute(StyleConstants.RightIndent); 554: short right = 0; 555: if (r != null) 556: right = r.shortValue(); 557: Float t = (Float) attributes.getAttribute(StyleConstants.SpaceAbove); 558: short top = 0; 559: if (t != null) 560: top = t.shortValue(); 561: Float b = (Float) attributes.getAttribute(StyleConstants.SpaceBelow); 562: short bottom = 0; 563: if (b != null) 564: bottom = b.shortValue(); 565: setInsets(top, left, bottom, right); 566: } 567: 568: /** 569: * Sets the insets of this <code>CompositeView</code>. 570: * 571: * @param top the top inset 572: * @param left the left inset 573: * @param bottom the bottom inset 574: * @param right the right inset 575: */ 576: protected void setInsets(short top, short left, short bottom, short right) 577: { 578: insets.top = top; 579: insets.left = left; 580: insets.bottom = bottom; 581: insets.right = right; 582: } 583: 584: /** 585: * Returns the left inset of this <code>CompositeView</code>. 586: * 587: * @return the left inset of this <code>CompositeView</code> 588: */ 589: protected short getLeftInset() 590: { 591: return (short) insets.left; 592: } 593: 594: /** 595: * Returns the right inset of this <code>CompositeView</code>. 596: * 597: * @return the right inset of this <code>CompositeView</code> 598: */ 599: protected short getRightInset() 600: { 601: return (short) insets.right; 602: } 603: 604: /** 605: * Returns the top inset of this <code>CompositeView</code>. 606: * 607: * @return the top inset of this <code>CompositeView</code> 608: */ 609: protected short getTopInset() 610: { 611: return (short) insets.top; 612: } 613: 614: /** 615: * Returns the bottom inset of this <code>CompositeView</code>. 616: * 617: * @return the bottom inset of this <code>CompositeView</code> 618: */ 619: protected short getBottomInset() 620: { 621: return (short) insets.bottom; 622: } 623: 624: /** 625: * Returns the next model location that is visible in north or south 626: * direction. 627: * This is used to determine the 628: * placement of the caret when navigating around the document with 629: * the arrow keys. 630: * 631: * @param pos the model position to start search from 632: * @param b the bias for <code>pos</code> 633: * @param a the allocated region for this view 634: * @param direction the direction from the current position, can be one of 635: * the following: 636: * <ul> 637: * <li>{@link SwingConstants#NORTH}</li> 638: * <li>{@link SwingConstants#SOUTH}</li> 639: * </ul> 640: * @param biasRet the bias of the return value gets stored here 641: * 642: * @return the position inside the model that represents the next visual 643: * location 644: * 645: * @throws BadLocationException if <code>pos</code> is not a valid location 646: * inside the document model 647: * @throws IllegalArgumentException if <code>direction</code> is invalid 648: */ 649: protected int getNextNorthSouthVisualPositionFrom(int pos, Position.Bias b, 650: Shape a, int direction, 651: Position.Bias[] biasRet) 652: throws BadLocationException 653: { 654: // TODO: It is unknown to me how this method has to be implemented and 655: // there is no specification telling me how to do it properly. Therefore 656: // the implementation was done for cases that are known. 657: // 658: // If this method ever happens to act silly for your particular case then 659: // it is likely that it is a cause of not knowing about your case when it 660: // was implemented first. You are free to fix the behavior. 661: // 662: // Here are the assumptions that lead to the implementation: 663: // If direction is NORTH chose the View preceding the one that contains the 664: // offset 'pos' (imagine the views are stacked on top of each other where 665: // the top is 0 and the bottom is getViewCount()-1. 666: // Consecutively when the direction is SOUTH the View following the one 667: // the offset 'pos' lies in is questioned. 668: // 669: // This limitation is described as PR 27345. 670: int index = getViewIndex(pos, b); 671: View v = null; 672: 673: if (index == -1) 674: return pos; 675: 676: switch (direction) 677: { 678: case NORTH: 679: // If we cannot calculate a proper offset return the one that was 680: // provided. 681: if (index <= 0) 682: return pos; 683: 684: v = getView(index - 1); 685: break; 686: case SOUTH: 687: // If we cannot calculate a proper offset return the one that was 688: // provided. 689: if (index >= getViewCount() - 1) 690: return pos; 691: 692: v = getView(index + 1); 693: break; 694: default: 695: throw new IllegalArgumentException(); 696: } 697: 698: return v.getNextVisualPositionFrom(pos, b, a, direction, biasRet); 699: } 700: 701: /** 702: * Returns the next model location that is visible in east or west 703: * direction. 704: * This is used to determine the 705: * placement of the caret when navigating around the document with 706: * the arrow keys. 707: * 708: * @param pos the model position to start search from 709: * @param b the bias for <code>pos</code> 710: * @param a the allocated region for this view 711: * @param direction the direction from the current position, can be one of 712: * the following: 713: * <ul> 714: * <li>{@link SwingConstants#EAST}</li> 715: * <li>{@link SwingConstants#WEST}</li> 716: * </ul> 717: * @param biasRet the bias of the return value gets stored here 718: * 719: * @return the position inside the model that represents the next visual 720: * location 721: * 722: * @throws BadLocationException if <code>pos</code> is not a valid location 723: * inside the document model 724: * @throws IllegalArgumentException if <code>direction</code> is invalid 725: */ 726: protected int getNextEastWestVisualPositionFrom(int pos, Position.Bias b, 727: Shape a, int direction, 728: Position.Bias[] biasRet) 729: throws BadLocationException 730: { 731: // TODO: It is unknown to me how this method has to be implemented and 732: // there is no specification telling me how to do it properly. Therefore 733: // the implementation was done for cases that are known. 734: // 735: // If this method ever happens to act silly for your particular case then 736: // it is likely that it is a cause of not knowing about your case when it 737: // was implemented first. You are free to fix the behavior. 738: // 739: // Here are the assumptions that lead to the implementation: 740: // If direction is EAST increase the offset by one and ask the View to 741: // which that index belong to calculate the 'next visual position'. 742: // If the direction is WEST do the same with offset 'pos' being decreased 743: // by one. 744: // This behavior will fail in a right-to-left or bidi environment! 745: // 746: // This limitation is described as PR 27346. 747: int index; 748: 749: View v = null; 750: 751: switch (direction) 752: { 753: case EAST: 754: index = getViewIndex(pos + 1, b); 755: // If we cannot calculate a proper offset return the one that was 756: // provided. 757: if (index == -1) 758: return pos; 759: 760: v = getView(index); 761: break; 762: case WEST: 763: index = getViewIndex(pos - 1, b); 764: // If we cannot calculate a proper offset return the one that was 765: // provided. 766: if (index == -1) 767: return pos; 768: 769: v = getView(index); 770: break; 771: default: 772: throw new IllegalArgumentException(); 773: } 774: 775: return v.getNextVisualPositionFrom(pos, 776: b, 777: a, 778: direction, 779: biasRet); 780: } 781: 782: /** 783: * Determines if the next view in horinzontal direction is located to 784: * the east or west of the view at position <code>pos</code>. Usually 785: * the <code>View</code>s are laid out from the east to the west, so 786: * we unconditionally return <code>false</code> here. Subclasses that 787: * support bidirectional text may wish to override this method. 788: * 789: * @param pos the position in the document 790: * @param bias the bias to be applied to <code>pos</code> 791: * 792: * @return <code>true</code> if the next <code>View</code> is located 793: * to the EAST, <code>false</code> otherwise 794: */ 795: protected boolean flipEastAndWestAtEnds(int pos, Position.Bias bias) 796: { 797: return false; 798: } 799: }