1 /*
2 * Copyright 1997-2006 Sun Microsystems, Inc. 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. Sun designates this
8 * particular file as subject to the "Classpath" exception as provided
9 * by Sun 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 Sun Microsystems, Inc., 4150 Network Circle, Santa Clara,
22 * CA 95054 USA or visit www.sun.com if you need additional information or
23 * have any 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 *
439 * @param pos the position to convert >= 0
440 * @param b a bias value of either <code>Position.Bias.Forward</code>
441 * or <code>Position.Bias.Backward</code>
442 * @param a the allocated region to render into
443 * @param direction the direction from the current position that can
444 * be thought of as the arrow keys typically found on a keyboard;
445 * this may be one of the following:
446 * <ul>
447 * <li><code>SwingConstants.WEST</code>
448 * <li><code>SwingConstants.EAST</code>
449 * <li><code>SwingConstants.NORTH</code>
450 * <li><code>SwingConstants.SOUTH</code>
451 * </ul>
452 * @param biasRet an array containing the bias that was checked
453 * @return the location within the model that best represents the next
454 * location visual position
455 * @exception BadLocationException
456 * @exception IllegalArgumentException if <code>direction</code> is invalid
457 */
458 public int getNextVisualPositionFrom(int pos, Position.Bias b, Shape a,
459 int direction, Position.Bias[] biasRet)
460 throws BadLocationException {
461 Rectangle alloc = getInsideAllocation(a);
462
463 switch (direction) {
464 case NORTH:
465 return getNextNorthSouthVisualPositionFrom(pos, b, a, direction,
466 biasRet);
467 case SOUTH:
468 return getNextNorthSouthVisualPositionFrom(pos, b, a, direction,
469 biasRet);
470 case EAST:
471 return getNextEastWestVisualPositionFrom(pos, b, a, direction,
472 biasRet);
473 case WEST:
474 return getNextEastWestVisualPositionFrom(pos, b, a, direction,
475 biasRet);
476 default:
477 throw new IllegalArgumentException("Bad direction: " + direction);
478 }
479 }
480
481 /**
482 * Returns the child view index representing the given
483 * position in the model. This is implemented to call the
484 * <code>getViewIndexByPosition</code>
485 * method for backward compatibility.
486 *
487 * @param pos the position >= 0
488 * @return index of the view representing the given position, or
489 * -1 if no view represents that position
490 * @since 1.3
491 */
492 public int getViewIndex(int pos, Position.Bias b) {
493 if(b == Position.Bias.Backward) {
494 pos -= 1;
495 }
496 if ((pos >= getStartOffset()) && (pos < getEndOffset())) {
497 return getViewIndexAtPosition(pos);
498 }
499 return -1;
500 }
501
502 // --- local methods ----------------------------------------------------
503
504
505 /**
506 * Tests whether a point lies before the rectangle range.
507 *
508 * @param x the X coordinate >= 0
509 * @param y the Y coordinate >= 0
510 * @param alloc the rectangle
511 * @return true if the point is before the specified range
512 */
513 protected abstract boolean isBefore(int x, int y, Rectangle alloc);
514
515 /**
516 * Tests whether a point lies after the rectangle range.
517 *
518 * @param x the X coordinate >= 0
519 * @param y the Y coordinate >= 0
520 * @param alloc the rectangle
521 * @return true if the point is after the specified range
522 */
523 protected abstract boolean isAfter(int x, int y, Rectangle alloc);
524
525 /**
526 * Fetches the child view at the given coordinates.
527 *
528 * @param x the X coordinate >= 0
529 * @param y the Y coordinate >= 0
530 * @param alloc the parent's allocation on entry, which should
531 * be changed to the child's allocation on exit
532 * @return the child view
533 */
534 protected abstract View getViewAtPoint(int x, int y, Rectangle alloc);
535
536 /**
537 * Returns the allocation for a given child.
538 *
539 * @param index the index of the child, >= 0 && < getViewCount()
540 * @param a the allocation to the interior of the box on entry,
541 * and the allocation of the child view at the index on exit.
542 */
543 protected abstract void childAllocation(int index, Rectangle a);
544
545 /**
546 * Fetches the child view that represents the given position in
547 * the model. This is implemented to fetch the view in the case
548 * where there is a child view for each child element.
549 *
550 * @param pos the position >= 0
551 * @param a the allocation to the interior of the box on entry,
552 * and the allocation of the view containing the position on exit
553 * @return the view representing the given position, or
554 * <code>null</code> if there isn't one
555 */
556 protected View getViewAtPosition(int pos, Rectangle a) {
557 int index = getViewIndexAtPosition(pos);
558 if ((index >= 0) && (index < getViewCount())) {
559 View v = getView(index);
560 if (a != null) {
561 childAllocation(index, a);
562 }
563 return v;
564 }
565 return null;
566 }
567
568 /**
569 * Fetches the child view index representing the given position in
570 * the model. This is implemented to fetch the view in the case
571 * where there is a child view for each child element.
572 *
573 * @param pos the position >= 0
574 * @return index of the view representing the given position, or
575 * -1 if no view represents that position
576 */
577 protected int getViewIndexAtPosition(int pos) {
578 Element elem = getElement();
579 return elem.getElementIndex(pos);
580 }
581
582 /**
583 * Translates the immutable allocation given to the view
584 * to a mutable allocation that represents the interior
585 * allocation (i.e. the bounds of the given allocation
586 * with the top, left, bottom, and right insets removed.
587 * It is expected that the returned value would be further
588 * mutated to represent an allocation to a child view.
589 * This is implemented to reuse an instance variable so
590 * it avoids creating excessive Rectangles. Typically
591 * the result of calling this method would be fed to
592 * the <code>childAllocation</code> method.
593 *
594 * @param a the allocation given to the view
595 * @return the allocation that represents the inside of the
596 * view after the margins have all been removed; if the
597 * given allocation was <code>null</code>,
598 * the return value is <code>null</code>
599 */
600 protected Rectangle getInsideAllocation(Shape a) {
601 if (a != null) {
602 // get the bounds, hopefully without allocating
603 // a new rectangle. The Shape argument should
604 // not be modified... we copy it into the
605 // child allocation.
606 Rectangle alloc;
607 if (a instanceof Rectangle) {
608 alloc = (Rectangle) a;
609 } else {
610 alloc = a.getBounds();
611 }
612
613 childAlloc.setBounds(alloc);
614 childAlloc.x += getLeftInset();
615 childAlloc.y += getTopInset();
616 childAlloc.width -= getLeftInset() + getRightInset();
617 childAlloc.height -= getTopInset() + getBottomInset();
618 return childAlloc;
619 }
620 return null;
621 }
622
623 /**
624 * Sets the insets from the paragraph attributes specified in
625 * the given attributes.
626 *
627 * @param attr the attributes
628 */
629 protected void setParagraphInsets(AttributeSet attr) {
630 // Since version 1.1 doesn't have scaling and assumes
631 // a pixel is equal to a point, we just cast the point
632 // sizes to integers.
633 top = (short) StyleConstants.getSpaceAbove(attr);
634 left = (short) StyleConstants.getLeftIndent(attr);
635 bottom = (short) StyleConstants.getSpaceBelow(attr);
636 right = (short) StyleConstants.getRightIndent(attr);
637 }
638
639 /**
640 * Sets the insets for the view.
641 *
642 * @param top the top inset >= 0
643 * @param left the left inset >= 0
644 * @param bottom the bottom inset >= 0
645 * @param right the right inset >= 0
646 */
647 protected void setInsets(short top, short left, short bottom, short right) {
648 this.top = top;
649 this.left = left;
650 this.right = right;
651 this.bottom = bottom;
652 }
653
654 /**
655 * Gets the left inset.
656 *
657 * @return the inset >= 0
658 */
659 protected short getLeftInset() {
660 return left;
661 }
662
663 /**
664 * Gets the right inset.
665 *
666 * @return the inset >= 0
667 */
668 protected short getRightInset() {
669 return right;
670 }
671
672 /**
673 * Gets the top inset.
674 *
675 * @return the inset >= 0
676 */
677 protected short getTopInset() {
678 return top;
679 }
680
681 /**
682 * Gets the bottom inset.
683 *
684 * @return the inset >= 0
685 */
686 protected short getBottomInset() {
687 return bottom;
688 }
689
690 /**
691 * Returns the next visual position for the cursor, in either the
692 * north or south direction.
693 *
694 * @param pos the position to convert >= 0
695 * @param b a bias value of either <code>Position.Bias.Forward</code>
696 * or <code>Position.Bias.Backward</code>
697 * @param a the allocated region to render into
698 * @param direction the direction from the current position that can
699 * be thought of as the arrow keys typically found on a keyboard;
700 * this may be one of the following:
701 * <ul>
702 * <li><code>SwingConstants.NORTH</code>
703 * <li><code>SwingConstants.SOUTH</code>
704 * </ul>
705 * @param biasRet an array containing the bias that was checked
706 * @return the location within the model that best represents the next
707 * north or south location
708 * @exception BadLocationException
709 * @exception IllegalArgumentException if <code>direction</code> is invalid
710 * @see #getNextVisualPositionFrom
711 *
712 * @return the next position west of the passed in position
713 */
714 protected int getNextNorthSouthVisualPositionFrom(int pos, Position.Bias b,
715 Shape a, int direction,
716 Position.Bias[] biasRet)
717 throws BadLocationException {
718 return Utilities.getNextVisualPositionFrom(
719 this, pos, b, a, direction, biasRet);
720 }
721
722 /**
723 * Returns the next visual position for the cursor, in either the
724 * east or west direction.
725 *
726 * @param pos the position to convert >= 0
727 * @param b a bias value of either <code>Position.Bias.Forward</code>
728 * or <code>Position.Bias.Backward</code>
729 * @param a the allocated region to render into
730 * @param direction the direction from the current position that can
731 * be thought of as the arrow keys typically found on a keyboard;
732 * this may be one of the following:
733 * <ul>
734 * <li><code>SwingConstants.WEST</code>
735 * <li><code>SwingConstants.EAST</code>
736 * </ul>
737 * @param biasRet an array containing the bias that was checked
738 * @return the location within the model that best represents the next
739 * west or east location
740 * @exception BadLocationException
741 * @exception IllegalArgumentException if <code>direction</code> is invalid
742 * @see #getNextVisualPositionFrom
743 */
744 protected int getNextEastWestVisualPositionFrom(int pos, Position.Bias b,
745 Shape a,
746 int direction,
747 Position.Bias[] biasRet)
748 throws BadLocationException {
749 return Utilities.getNextVisualPositionFrom(
750 this, pos, b, a, direction, biasRet);
751 }
752
753 /**
754 * Determines in which direction the next view lays.
755 * Consider the <code>View</code> at index n. Typically the
756 * <code>View</code>s are layed out from left to right,
757 * so that the <code>View</code> to the EAST will be
758 * at index n + 1, and the <code>View</code> to the WEST
759 * will be at index n - 1. In certain situations,
760 * such as with bidirectional text, it is possible
761 * that the <code>View</code> to EAST is not at index n + 1,
762 * but rather at index n - 1, or that the <code>View</code>
763 * to the WEST is not at index n - 1, but index n + 1.
764 * In this case this method would return true, indicating the
765 * <code>View</code>s are layed out in descending order.
766 * <p>
767 * This unconditionally returns false, subclasses should override this
768 * method if there is the possibility for laying <code>View</code>s in
769 * descending order.
770 *
771 * @param position position into the model
772 * @param bias either <code>Position.Bias.Forward</code> or
773 * <code>Position.Bias.Backward</code>
774 * @return false
775 */
776 protected boolean flipEastAndWestAtEnds(int position,
777 Position.Bias bias) {
778 return false;
779 }
780
781
782 // ---- member variables ---------------------------------------------
783
784
785 private static View[] ZERO = new View[0];
786
787 private View[] children;
788 private int nchildren;
789 private short left;
790 private short right;
791 private short top;
792 private short bottom;
793 private Rectangle childAlloc;
794 }