001 /*
002 * Copyright 1997-2006 Sun Microsystems, Inc. All Rights Reserved.
003 * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER.
004 *
005 * This code is free software; you can redistribute it and/or modify it
006 * under the terms of the GNU General Public License version 2 only, as
007 * published by the Free Software Foundation. Sun designates this
008 * particular file as subject to the "Classpath" exception as provided
009 * by Sun in the LICENSE file that accompanied this code.
010 *
011 * This code is distributed in the hope that it will be useful, but WITHOUT
012 * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
013 * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License
014 * version 2 for more details (a copy is included in the LICENSE file that
015 * accompanied this code).
016 *
017 * You should have received a copy of the GNU General Public License version
018 * 2 along with this work; if not, write to the Free Software Foundation,
019 * Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA.
020 *
021 * Please contact Sun Microsystems, Inc., 4150 Network Circle, Santa Clara,
022 * CA 95054 USA or visit www.sun.com if you need additional information or
023 * have any questions.
024 */
025 package javax.swing.text;
026
027 import java.util.Vector;
028 import java.awt.*;
029 import javax.swing.event.*;
030 import javax.swing.SwingConstants;
031
032 /**
033 * <code>CompositeView</code> is an abstract <code>View</code>
034 * implementation which manages one or more child views.
035 * (Note that <code>CompositeView</code> is intended
036 * for managing relatively small numbers of child views.)
037 * <code>CompositeView</code> is intended to be used as
038 * a starting point for <code>View</code> implementations,
039 * such as <code>BoxView</code>, that will contain child
040 * <code>View</code>s. Subclasses that wish to manage the
041 * collection of child <code>View</code>s should use the
042 * {@link #replace} method. As <code>View</code> invokes
043 * <code>replace</code> during <code>DocumentListener</code>
044 * notification, you normally won't need to directly
045 * invoke <code>replace</code>.
046 *
047 * <p>While <code>CompositeView</code>
048 * does not impose a layout policy on its child <code>View</code>s,
049 * it does allow for inseting the child <code>View</code>s
050 * it will contain. The insets can be set by either
051 * {@link #setInsets} or {@link #setParagraphInsets}.
052 *
053 * <p>In addition to the abstract methods of
054 * {@link javax.swing.text.View},
055 * subclasses of <code>CompositeView</code> will need to
056 * override:
057 * <ul>
058 * <li>{@link #isBefore} - Used to test if a given
059 * <code>View</code> location is before the visual space
060 * of the <code>CompositeView</code>.
061 * <li>{@link #isAfter} - Used to test if a given
062 * <code>View</code> location is after the visual space
063 * of the <code>CompositeView</code>.
064 * <li>{@link #getViewAtPoint} - Returns the view at
065 * a given visual location.
066 * <li>{@link #childAllocation} - Returns the bounds of
067 * a particular child <code>View</code>.
068 * <code>getChildAllocation</code> will invoke
069 * <code>childAllocation</code> after offseting
070 * the bounds by the <code>Inset</code>s of the
071 * <code>CompositeView</code>.
072 * </ul>
073 *
074 * @author Timothy Prinzing
075 * @version 1.75 05/05/07
076 */
077 public abstract class CompositeView extends View {
078
079 /**
080 * Constructs a <code>CompositeView</code> for the given element.
081 *
082 * @param elem the element this view is responsible for
083 */
084 public CompositeView(Element elem) {
085 super (elem);
086 children = new View[1];
087 nchildren = 0;
088 childAlloc = new Rectangle();
089 }
090
091 /**
092 * Loads all of the children to initialize the view.
093 * This is called by the {@link #setParent}
094 * method. Subclasses can reimplement this to initialize
095 * their child views in a different manner. The default
096 * implementation creates a child view for each
097 * child element.
098 *
099 * @param f the view factory
100 * @see #setParent
101 */
102 protected void loadChildren(ViewFactory f) {
103 if (f == null) {
104 // No factory. This most likely indicates the parent view
105 // has changed out from under us, bail!
106 return;
107 }
108 Element e = getElement();
109 int n = e.getElementCount();
110 if (n > 0) {
111 View[] added = new View[n];
112 for (int i = 0; i < n; i++) {
113 added[i] = f.create(e.getElement(i));
114 }
115 replace(0, 0, added);
116 }
117 }
118
119 // --- View methods ---------------------------------------------
120
121 /**
122 * Sets the parent of the view.
123 * This is reimplemented to provide the superclass
124 * behavior as well as calling the <code>loadChildren</code>
125 * method if this view does not already have children.
126 * The children should not be loaded in the
127 * constructor because the act of setting the parent
128 * may cause them to try to search up the hierarchy
129 * (to get the hosting <code>Container</code> for example).
130 * If this view has children (the view is being moved
131 * from one place in the view hierarchy to another),
132 * the <code>loadChildren</code> method will not be called.
133 *
134 * @param parent the parent of the view, <code>null</code> if none
135 */
136 public void setParent(View parent) {
137 super .setParent(parent);
138 if ((parent != null) && (nchildren == 0)) {
139 ViewFactory f = getViewFactory();
140 loadChildren(f);
141 }
142 }
143
144 /**
145 * Returns the number of child views of this view.
146 *
147 * @return the number of views >= 0
148 * @see #getView
149 */
150 public int getViewCount() {
151 return nchildren;
152 }
153
154 /**
155 * Returns the n-th view in this container.
156 *
157 * @param n the number of the desired view, >= 0 && < getViewCount()
158 * @return the view at index <code>n</code>
159 */
160 public View getView(int n) {
161 return children[n];
162 }
163
164 /**
165 * Replaces child views. If there are no views to remove
166 * this acts as an insert. If there are no views to
167 * add this acts as a remove. Views being removed will
168 * have the parent set to <code>null</code>,
169 * and the internal reference to them removed so that they
170 * may be garbage collected.
171 *
172 * @param offset the starting index into the child views to insert
173 * the new views; >= 0 and <= getViewCount
174 * @param length the number of existing child views to remove;
175 * this should be a value >= 0 and <= (getViewCount() - offset)
176 * @param views the child views to add; this value can be
177 * <code>null</code>
178 * to indicate no children are being added (useful to remove)
179 */
180 public void replace(int offset, int length, View[] views) {
181 // make sure an array exists
182 if (views == null) {
183 views = ZERO;
184 }
185
186 // update parent reference on removed views
187 for (int i = offset; i < offset + length; i++) {
188 if (children[i].getParent() == this ) {
189 // in FlowView.java view might be referenced
190 // from two super-views as a child. see logicalView
191 children[i].setParent(null);
192 }
193 children[i] = null;
194 }
195
196 // update the array
197 int delta = views.length - length;
198 int src = offset + length;
199 int nmove = nchildren - src;
200 int dest = src + delta;
201 if ((nchildren + delta) >= children.length) {
202 // need to grow the array
203 int newLength = Math.max(2 * children.length, nchildren
204 + delta);
205 View[] newChildren = new View[newLength];
206 System.arraycopy(children, 0, newChildren, 0, offset);
207 System.arraycopy(views, 0, newChildren, offset,
208 views.length);
209 System.arraycopy(children, src, newChildren, dest, nmove);
210 children = newChildren;
211 } else {
212 // patch the existing array
213 System.arraycopy(children, src, children, dest, nmove);
214 System.arraycopy(views, 0, children, offset, views.length);
215 }
216 nchildren = nchildren + delta;
217
218 // update parent reference on added views
219 for (int i = 0; i < views.length; i++) {
220 views[i].setParent(this );
221 }
222 }
223
224 /**
225 * Fetches the allocation for the given child view to
226 * render into. This enables finding out where various views
227 * are located.
228 *
229 * @param index the index of the child, >= 0 && < getViewCount()
230 * @param a the allocation to this view
231 * @return the allocation to the child
232 */
233 public Shape getChildAllocation(int index, Shape a) {
234 Rectangle alloc = getInsideAllocation(a);
235 childAllocation(index, alloc);
236 return alloc;
237 }
238
239 /**
240 * Provides a mapping from the document model coordinate space
241 * to the coordinate space of the view mapped to it.
242 *
243 * @param pos the position to convert >= 0
244 * @param a the allocated region to render into
245 * @param b a bias value of either <code>Position.Bias.Forward</code>
246 * or <code>Position.Bias.Backward</code>
247 * @return the bounding box of the given position
248 * @exception BadLocationException if the given position does
249 * not represent a valid location in the associated document
250 * @see View#modelToView
251 */
252 public Shape modelToView(int pos, Shape a, Position.Bias b)
253 throws BadLocationException {
254 boolean isBackward = (b == Position.Bias.Backward);
255 int testPos = (isBackward) ? Math.max(0, pos - 1) : pos;
256 if (isBackward && testPos < getStartOffset()) {
257 return null;
258 }
259 int vIndex = getViewIndexAtPosition(testPos);
260 if ((vIndex != -1) && (vIndex < getViewCount())) {
261 View v = getView(vIndex);
262 if (v != null && testPos >= v.getStartOffset()
263 && testPos < v.getEndOffset()) {
264 Shape childShape = getChildAllocation(vIndex, a);
265 if (childShape == null) {
266 // We are likely invalid, fail.
267 return null;
268 }
269 Shape retShape = v.modelToView(pos, childShape, b);
270 if (retShape == null && v.getEndOffset() == pos) {
271 if (++vIndex < getViewCount()) {
272 v = getView(vIndex);
273 retShape = v.modelToView(pos,
274 getChildAllocation(vIndex, a), b);
275 }
276 }
277 return retShape;
278 }
279 }
280 throw new BadLocationException(
281 "Position not represented by view", pos);
282 }
283
284 /**
285 * Provides a mapping from the document model coordinate space
286 * to the coordinate space of the view mapped to it.
287 *
288 * @param p0 the position to convert >= 0
289 * @param b0 the bias toward the previous character or the
290 * next character represented by p0, in case the
291 * position is a boundary of two views; either
292 * <code>Position.Bias.Forward</code> or
293 * <code>Position.Bias.Backward</code>
294 * @param p1 the position to convert >= 0
295 * @param b1 the bias toward the previous character or the
296 * next character represented by p1, in case the
297 * position is a boundary of two views
298 * @param a the allocated region to render into
299 * @return the bounding box of the given position is returned
300 * @exception BadLocationException if the given position does
301 * not represent a valid location in the associated document
302 * @exception IllegalArgumentException for an invalid bias argument
303 * @see View#viewToModel
304 */
305 public Shape modelToView(int p0, Position.Bias b0, int p1,
306 Position.Bias b1, Shape a) throws BadLocationException {
307 if (p0 == getStartOffset() && p1 == getEndOffset()) {
308 return a;
309 }
310 Rectangle alloc = getInsideAllocation(a);
311 Rectangle r0 = new Rectangle(alloc);
312 View v0 = getViewAtPosition(
313 (b0 == Position.Bias.Backward) ? Math.max(0, p0 - 1)
314 : p0, r0);
315 Rectangle r1 = new Rectangle(alloc);
316 View v1 = getViewAtPosition(
317 (b1 == Position.Bias.Backward) ? Math.max(0, p1 - 1)
318 : p1, r1);
319 if (v0 == v1) {
320 if (v0 == null) {
321 return a;
322 }
323 // Range contained in one view
324 return v0.modelToView(p0, b0, p1, b1, r0);
325 }
326 // Straddles some views.
327 int viewCount = getViewCount();
328 int counter = 0;
329 while (counter < viewCount) {
330 View v;
331 // Views may not be in same order as model.
332 // v0 or v1 may be null if there is a gap in the range this
333 // view contains.
334 if ((v = getView(counter)) == v0 || v == v1) {
335 View endView;
336 Rectangle retRect;
337 Rectangle tempRect = new Rectangle();
338 if (v == v0) {
339 retRect = v0.modelToView(p0, b0, v0.getEndOffset(),
340 Position.Bias.Backward, r0).getBounds();
341 endView = v1;
342 } else {
343 retRect = v1.modelToView(v1.getStartOffset(),
344 Position.Bias.Forward, p1, b1, r1)
345 .getBounds();
346 endView = v0;
347 }
348
349 // Views entirely covered by range.
350 while (++counter < viewCount
351 && (v = getView(counter)) != endView) {
352 tempRect.setBounds(alloc);
353 childAllocation(counter, tempRect);
354 retRect.add(tempRect);
355 }
356
357 // End view.
358 if (endView != null) {
359 Shape endShape;
360 if (endView == v1) {
361 endShape = v1.modelToView(v1.getStartOffset(),
362 Position.Bias.Forward, p1, b1, r1);
363 } else {
364 endShape = v0.modelToView(p0, b0, v0
365 .getEndOffset(),
366 Position.Bias.Backward, r0);
367 }
368 if (endShape instanceof Rectangle) {
369 retRect.add((Rectangle) endShape);
370 } else {
371 retRect.add(endShape.getBounds());
372 }
373 }
374 return retRect;
375 }
376 counter++;
377 }
378 throw new BadLocationException(
379 "Position not represented by view", p0);
380 }
381
382 /**
383 * Provides a mapping from the view coordinate space to the logical
384 * coordinate space of the model.
385 *
386 * @param x x coordinate of the view location to convert >= 0
387 * @param y y coordinate of the view location to convert >= 0
388 * @param a the allocated region to render into
389 * @param bias either <code>Position.Bias.Forward</code> or
390 * <code>Position.Bias.Backward</code>
391 * @return the location within the model that best represents the
392 * given point in the view >= 0
393 * @see View#viewToModel
394 */
395 public int viewToModel(float x, float y, Shape a,
396 Position.Bias[] bias) {
397 Rectangle alloc = getInsideAllocation(a);
398 if (isBefore((int) x, (int) y, alloc)) {
399 // point is before the range represented
400 int retValue = -1;
401
402 try {
403 retValue = getNextVisualPositionFrom(-1,
404 Position.Bias.Forward, a, EAST, bias);
405 } catch (BadLocationException ble) {
406 } catch (IllegalArgumentException iae) {
407 }
408 if (retValue == -1) {
409 retValue = getStartOffset();
410 bias[0] = Position.Bias.Forward;
411 }
412 return retValue;
413 } else if (isAfter((int) x, (int) y, alloc)) {
414 // point is after the range represented.
415 int retValue = -1;
416 try {
417 retValue = getNextVisualPositionFrom(-1,
418 Position.Bias.Forward, a, WEST, bias);
419 } catch (BadLocationException ble) {
420 } catch (IllegalArgumentException iae) {
421 }
422
423 if (retValue == -1) {
424 // NOTE: this could actually use end offset with backward.
425 retValue = getEndOffset() - 1;
426 bias[0] = Position.Bias.Forward;
427 }
428 return retValue;
429 } else {
430 // locate the child and pass along the request
431 View v = getViewAtPoint((int) x, (int) y, alloc);
432 if (v != null) {
433 return v.viewToModel(x, y, alloc, bias);
434 }
435 }
436 return -1;
437 }
438
439 /**
440 * Provides a way to determine the next visually represented model
441 * location that one might place a caret. Some views may not be visible,
442 * they might not be in the same order found in the model, or they just
443 * might not allow access to some of the locations in the model.
444 * This is a convenience method for {@link #getNextNorthSouthVisualPositionFrom}
445 * and {@link #getNextEastWestVisualPositionFrom}.
446 *
447 * @param pos the position to convert >= 0
448 * @param b a bias value of either <code>Position.Bias.Forward</code>
449 * or <code>Position.Bias.Backward</code>
450 * @param a the allocated region to render into
451 * @param direction the direction from the current position that can
452 * be thought of as the arrow keys typically found on a keyboard;
453 * this may be one of the following:
454 * <ul>
455 * <li><code>SwingConstants.WEST</code>
456 * <li><code>SwingConstants.EAST</code>
457 * <li><code>SwingConstants.NORTH</code>
458 * <li><code>SwingConstants.SOUTH</code>
459 * </ul>
460 * @param biasRet an array containing the bias that was checked
461 * @return the location within the model that best represents the next
462 * location visual position
463 * @exception BadLocationException
464 * @exception IllegalArgumentException if <code>direction</code> is invalid
465 */
466 public int getNextVisualPositionFrom(int pos, Position.Bias b,
467 Shape a, int direction, Position.Bias[] biasRet)
468 throws BadLocationException {
469 Rectangle alloc = getInsideAllocation(a);
470
471 switch (direction) {
472 case NORTH:
473 return getNextNorthSouthVisualPositionFrom(pos, b, a,
474 direction, biasRet);
475 case SOUTH:
476 return getNextNorthSouthVisualPositionFrom(pos, b, a,
477 direction, biasRet);
478 case EAST:
479 return getNextEastWestVisualPositionFrom(pos, b, a,
480 direction, biasRet);
481 case WEST:
482 return getNextEastWestVisualPositionFrom(pos, b, a,
483 direction, biasRet);
484 default:
485 throw new IllegalArgumentException("Bad direction: "
486 + direction);
487 }
488 }
489
490 /**
491 * Returns the child view index representing the given
492 * position in the model. This is implemented to call the
493 * <code>getViewIndexByPosition</code>
494 * method for backward compatibility.
495 *
496 * @param pos the position >= 0
497 * @return index of the view representing the given position, or
498 * -1 if no view represents that position
499 * @since 1.3
500 */
501 public int getViewIndex(int pos, Position.Bias b) {
502 if (b == Position.Bias.Backward) {
503 pos -= 1;
504 }
505 if ((pos >= getStartOffset()) && (pos < getEndOffset())) {
506 return getViewIndexAtPosition(pos);
507 }
508 return -1;
509 }
510
511 // --- local methods ----------------------------------------------------
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,
656 short right) {
657 this .top = top;
658 this .left = left;
659 this .right = right;
660 this .bottom = bottom;
661 }
662
663 /**
664 * Gets the left inset.
665 *
666 * @return the inset >= 0
667 */
668 protected short getLeftInset() {
669 return left;
670 }
671
672 /**
673 * Gets the right inset.
674 *
675 * @return the inset >= 0
676 */
677 protected short getRightInset() {
678 return right;
679 }
680
681 /**
682 * Gets the top inset.
683 *
684 * @return the inset >= 0
685 */
686 protected short getTopInset() {
687 return top;
688 }
689
690 /**
691 * Gets the bottom inset.
692 *
693 * @return the inset >= 0
694 */
695 protected short getBottomInset() {
696 return bottom;
697 }
698
699 /**
700 * Returns the next visual position for the cursor, in either the
701 * north or south direction.
702 *
703 * @param pos the position to convert >= 0
704 * @param b a bias value of either <code>Position.Bias.Forward</code>
705 * or <code>Position.Bias.Backward</code>
706 * @param a the allocated region to render into
707 * @param direction the direction from the current position that can
708 * be thought of as the arrow keys typically found on a keyboard;
709 * this may be one of the following:
710 * <ul>
711 * <li><code>SwingConstants.NORTH</code>
712 * <li><code>SwingConstants.SOUTH</code>
713 * </ul>
714 * @param biasRet an array containing the bias that was checked
715 * @return the location within the model that best represents the next
716 * north or south location
717 * @exception BadLocationException
718 * @exception IllegalArgumentException if <code>direction</code> is invalid
719 * @see #getNextVisualPositionFrom
720 *
721 * @return the next position west of the passed in position
722 */
723 protected int getNextNorthSouthVisualPositionFrom(int pos,
724 Position.Bias b, Shape a, int direction,
725 Position.Bias[] biasRet) throws BadLocationException {
726 return Utilities.getNextVisualPositionFrom(this , pos, b, a,
727 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,
753 Position.Bias b, Shape a, int direction,
754 Position.Bias[] biasRet) throws BadLocationException {
755 return Utilities.getNextVisualPositionFrom(this , pos, b, a,
756 direction, biasRet);
757 }
758
759 /**
760 * Determines in which direction the next view lays.
761 * Consider the <code>View</code> at index n. Typically the
762 * <code>View</code>s are layed out from left to right,
763 * so that the <code>View</code> to the EAST will be
764 * at index n + 1, and the <code>View</code> to the WEST
765 * will be at index n - 1. In certain situations,
766 * such as with bidirectional text, it is possible
767 * that the <code>View</code> to EAST is not at index n + 1,
768 * but rather at index n - 1, or that the <code>View</code>
769 * to the WEST is not at index n - 1, but index n + 1.
770 * In this case this method would return true, indicating the
771 * <code>View</code>s are layed out in descending order.
772 * <p>
773 * This unconditionally returns false, subclasses should override this
774 * method if there is the possibility for laying <code>View</code>s in
775 * descending order.
776 *
777 * @param position position into the model
778 * @param bias either <code>Position.Bias.Forward</code> or
779 * <code>Position.Bias.Backward</code>
780 * @return false
781 */
782 protected boolean flipEastAndWestAtEnds(int position,
783 Position.Bias bias) {
784 return false;
785 }
786
787 // ---- member variables ---------------------------------------------
788
789 private static View[] ZERO = new View[0];
790
791 private View[] children;
792 private int nchildren;
793 private short left;
794 private short right;
795 private short top;
796 private short bottom;
797 private Rectangle childAlloc;
798 }
|