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 &gt;= 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, &gt;= 0 &amp;&amp; &lt; 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; &gt;= 0 and &lt;= getViewCount
 173      * @param length the number of existing child views to remove;
 174      *   this should be a value &gt;= 0 and &lt;= (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, &gt;= 0 &amp;&amp; &lt; 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 &gt;= 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 &gt;= 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 &gt;= 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 &gt;= 0
 382      * @param y   y coordinate of the view location to convert &gt;= 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 &gt;= 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 &gt;=0.  If the value is -1, a position
 440      * will be calculated automatically.  If the value &lt; -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 &gt;= 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 &gt;= 0
 517      * @param y the Y coordinate &gt;= 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 &gt;= 0
 527      * @param y the Y coordinate &gt;= 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 &gt;= 0
 537      * @param y the Y coordinate &gt;= 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, &gt;= 0 &amp;&amp; &lt; 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 &gt;= 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 &gt;= 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 &gt;= 0
 651      * @param left the left inset &gt;= 0
 652      * @param bottom the bottom inset &gt;= 0
 653      * @param right the right inset &gt;= 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 &gt;= 0
 666      */
 667     protected short getLeftInset() {
 668         return left;
 669     }
 670 
 671     /**
 672      * Gets the right inset.
 673      *
 674      * @return the inset &gt;= 0
 675      */
 676     protected short getRightInset() {
 677         return right;
 678     }
 679 
 680     /**
 681      * Gets the top inset.
 682      *
 683      * @return the inset &gt;= 0
 684      */
 685     protected short getTopInset() {
 686         return top;
 687     }
 688 
 689     /**
 690      * Gets the bottom inset.
 691      *
 692      * @return the inset &gt;= 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 &gt;= 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 &gt;= 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 }