< prev index next >

src/java.desktop/share/classes/javax/swing/text/BoxView.java

Print this page




  24  */
  25 package javax.swing.text;
  26 
  27 import java.io.PrintStream;
  28 import java.util.Vector;
  29 import java.awt.*;
  30 import javax.swing.event.DocumentEvent;
  31 import javax.swing.SizeRequirements;
  32 
  33 /**
  34  * A view that arranges its children into a box shape by tiling
  35  * its children along an axis.  The box is somewhat like that
  36  * found in TeX where there is alignment of the
  37  * children, flexibility of the children is considered, etc.
  38  * This is a building block that might be useful to represent
  39  * things like a collection of lines, paragraphs,
  40  * lists, columns, pages, etc.  The axis along which the children are tiled is
  41  * considered the major axis.  The orthogonal axis is the minor axis.
  42  * <p>
  43  * Layout for each axis is handled separately by the methods
  44  * <code>layoutMajorAxis</code> and <code>layoutMinorAxis</code>.
  45  * Subclasses can change the layout algorithm by
  46  * reimplementing these methods.    These methods will be called
  47  * as necessary depending upon whether or not there is cached
  48  * layout information and the cache is considered
  49  * valid.  These methods are typically called if the given size
  50  * along the axis changes, or if <code>layoutChanged</code> is
  51  * called to force an updated layout.  The <code>layoutChanged</code>
  52  * method invalidates cached layout information, if there is any.
  53  * The requirements published to the parent view are calculated by
  54  * the methods <code>calculateMajorAxisRequirements</code>
  55  * and  <code>calculateMinorAxisRequirements</code>.
  56  * If the layout algorithm is changed, these methods will
  57  * likely need to be reimplemented.
  58  *
  59  * @author  Timothy Prinzing
  60  */
  61 public class BoxView extends CompositeView {
  62 
  63     /**
  64      * Constructs a <code>BoxView</code>.
  65      *
  66      * @param elem the element this view is responsible for
  67      * @param axis either <code>View.X_AXIS</code> or <code>View.Y_AXIS</code>
  68      */
  69     public BoxView(Element elem, int axis) {
  70         super(elem);
  71         tempRect = new Rectangle();
  72         this.majorAxis = axis;
  73 
  74         majorOffsets = new int[0];
  75         majorSpans = new int[0];
  76         majorReqValid = false;
  77         majorAllocValid = false;
  78         minorOffsets = new int[0];
  79         minorSpans = new int[0];
  80         minorReqValid = false;
  81         minorAllocValid = false;
  82     }
  83 
  84     /**
  85      * Fetches the tile axis property.  This is the axis along which
  86      * the child views are tiled.
  87      *
  88      * @return the major axis of the box, either
  89      *  <code>View.X_AXIS</code> or <code>View.Y_AXIS</code>
  90      *
  91      * @since 1.3
  92      */
  93     public int getAxis() {
  94         return majorAxis;
  95     }
  96 
  97     /**
  98      * Sets the tile axis property.  This is the axis along which
  99      * the child views are tiled.
 100      *
 101      * @param axis either <code>View.X_AXIS</code> or <code>View.Y_AXIS</code>
 102      *
 103      * @since 1.3
 104      */
 105     public void setAxis(int axis) {
 106         boolean axisChanged = (axis != majorAxis);
 107         majorAxis = axis;
 108         if (axisChanged) {
 109             preferenceChanged(null, true, true);
 110         }
 111     }
 112 
 113     /**
 114      * Invalidates the layout along an axis.  This happens
 115      * automatically if the preferences have changed for
 116      * any of the child views.  In some cases the layout
 117      * may need to be recalculated when the preferences
 118      * have not changed.  The layout can be marked as
 119      * invalid by calling this method.  The layout will
 120      * be updated the next time the <code>setSize</code> method
 121      * is called on this view (typically in paint).
 122      *
 123      * @param axis either <code>View.X_AXIS</code> or <code>View.Y_AXIS</code>
 124      *
 125      * @since 1.3
 126      */
 127     public void layoutChanged(int axis) {
 128         if (axis == majorAxis) {
 129             majorAllocValid = false;
 130         } else {
 131             minorAllocValid = false;
 132         }
 133     }
 134 
 135     /**
 136      * Determines if the layout is valid along the given axis.
 137      * @return if the layout is valid along the given axis
 138      *
 139      * @param axis either <code>View.X_AXIS</code> or <code>View.Y_AXIS</code>
 140      *
 141      * @since 1.4
 142      */
 143     protected boolean isLayoutValid(int axis) {
 144         if (axis == majorAxis) {
 145             return majorAllocValid;
 146         } else {
 147             return minorAllocValid;
 148         }
 149     }
 150 
 151     /**
 152      * Paints a child.  By default
 153      * that is all it does, but a subclass can use this to paint
 154      * things relative to the child.
 155      *
 156      * @param g the graphics context
 157      * @param alloc the allocated region to paint into
 158      * @param index the child index, &gt;= 0 &amp;&amp; &lt; getViewCount()
 159      */
 160     protected void paintChild(Graphics g, Rectangle alloc, int index) {
 161         View child = getView(index);
 162         child.paint(g, alloc);
 163     }
 164 
 165     // --- View methods ---------------------------------------------
 166 
 167     /**
 168      * Invalidates the layout and resizes the cache of
 169      * requests/allocations.  The child allocations can still
 170      * be accessed for the old layout, but the new children
 171      * will have an offset and span of 0.
 172      *
 173      * @param index the starting index into the child views to insert
 174      *   the new views; this should be a value &gt;= 0 and &lt;= getViewCount
 175      * @param length the number of existing child views to remove;
 176      *   This should be a value &gt;= 0 and &lt;= (getViewCount() - offset)
 177      * @param elems the child views to add; this value can be
 178      *   <code>null</code>to indicate no children are being added
 179      *   (useful to remove)
 180      */
 181     public void replace(int index, int length, View[] elems) {
 182         super.replace(index, length, elems);
 183 
 184         // invalidate cache
 185         int nInserted = (elems != null) ? elems.length : 0;
 186         majorOffsets = updateLayoutArray(majorOffsets, index, nInserted);
 187         majorSpans = updateLayoutArray(majorSpans, index, nInserted);
 188         majorReqValid = false;
 189         majorAllocValid = false;
 190         minorOffsets = updateLayoutArray(minorOffsets, index, nInserted);
 191         minorSpans = updateLayoutArray(minorSpans, index, nInserted);
 192         minorReqValid = false;
 193         minorAllocValid = false;
 194     }
 195 
 196     /**
 197      * Resizes the given layout array to match the new number of
 198      * child views.  The current number of child views are used to
 199      * produce the new array.  The contents of the old array are
 200      * inserted into the new array at the appropriate places so that
 201      * the old layout information is transferred to the new array.
 202      *
 203      * @param oldArray the original layout array
 204      * @param offset location where new views will be inserted
 205      * @param nInserted the number of child views being inserted;
 206      *          therefore the number of blank spaces to leave in the
 207      *          new array at location <code>offset</code>
 208      * @return the new layout array
 209      */
 210     int[] updateLayoutArray(int[] oldArray, int offset, int nInserted) {
 211         int n = getViewCount();
 212         int[] newArray = new int[n];
 213 
 214         System.arraycopy(oldArray, 0, newArray, 0, offset);
 215         System.arraycopy(oldArray, offset,
 216                          newArray, offset + nInserted, n - nInserted - offset);
 217         return newArray;
 218     }
 219 
 220     /**
 221      * Forwards the given <code>DocumentEvent</code> to the child views
 222      * that need to be notified of the change to the model.
 223      * If a child changed its requirements and the allocation
 224      * was valid prior to forwarding the portion of the box
 225      * from the starting child to the end of the box will
 226      * be repainted.
 227      *
 228      * @param ec changes to the element this view is responsible
 229      *  for (may be <code>null</code> if there were no changes)
 230      * @param e the change information from the associated document
 231      * @param a the current allocation of the view
 232      * @param f the factory to use to rebuild if the view has children
 233      * @see #insertUpdate
 234      * @see #removeUpdate
 235      * @see #changedUpdate
 236      * @since 1.3
 237      */
 238     protected void forwardUpdate(DocumentEvent.ElementChange ec,
 239                                  DocumentEvent e, Shape a, ViewFactory f) {
 240         boolean wasValid = isLayoutValid(majorAxis);
 241         super.forwardUpdate(ec, e, a, f);
 242 
 243         // determine if a repaint is needed
 244         if (wasValid && (! isLayoutValid(majorAxis))) {
 245             // Repaint is needed because one of the tiled children
 246             // have changed their span along the major axis.  If there
 247             // is a hosting component and an allocated shape we repaint.
 248             Component c = getContainer();
 249             if ((a != null) && (c != null)) {


 273      * @param width true if the width preference should change
 274      * @param height true if the height preference should change
 275      */
 276     public void preferenceChanged(View child, boolean width, boolean height) {
 277         boolean majorChanged = (majorAxis == X_AXIS) ? width : height;
 278         boolean minorChanged = (majorAxis == X_AXIS) ? height : width;
 279         if (majorChanged) {
 280             majorReqValid = false;
 281             majorAllocValid = false;
 282         }
 283         if (minorChanged) {
 284             minorReqValid = false;
 285             minorAllocValid = false;
 286         }
 287         super.preferenceChanged(child, width, height);
 288     }
 289 
 290     /**
 291      * Gets the resize weight.  A value of 0 or less is not resizable.
 292      *
 293      * @param axis may be either <code>View.X_AXIS</code> or
 294      *          <code>View.Y_AXIS</code>
 295      * @return the weight
 296      * @exception IllegalArgumentException for an invalid axis
 297      */
 298     public int getResizeWeight(int axis) {
 299         checkRequests(axis);
 300         if (axis == majorAxis) {
 301             if ((majorRequest.preferred != majorRequest.minimum) ||
 302                 (majorRequest.preferred != majorRequest.maximum)) {
 303                 return 1;
 304             }
 305         } else {
 306             if ((minorRequest.preferred != minorRequest.minimum) ||
 307                 (minorRequest.preferred != minorRequest.maximum)) {
 308                 return 1;
 309             }
 310         }
 311         return 0;
 312     }
 313 
 314     /**
 315      * Sets the size of the view along an axis.  This should cause
 316      * layout of the view along the given axis.
 317      *
 318      * @param axis may be either <code>View.X_AXIS</code> or
 319      *          <code>View.Y_AXIS</code>
 320      * @param span the span to layout to >= 0
 321      */
 322     void setSpanOnAxis(int axis, float span) {
 323         if (axis == majorAxis) {
 324             if (majorSpan != (int) span) {
 325                 majorAllocValid = false;
 326             }
 327             if (! majorAllocValid) {
 328                 // layout the major axis
 329                 majorSpan = (int) span;
 330                 checkRequests(majorAxis);
 331                 layoutMajorAxis(majorSpan, axis, majorOffsets, majorSpans);
 332                 majorAllocValid = true;
 333 
 334                 // flush changes to the children
 335                 updateChildSizes();
 336             }
 337         } else {
 338             if (((int) span) != minorSpan) {
 339                 minorAllocValid = false;


 356      */
 357     void updateChildSizes() {
 358         int n = getViewCount();
 359         if (majorAxis == X_AXIS) {
 360             for (int i = 0; i < n; i++) {
 361                 View v = getView(i);
 362                 v.setSize((float) majorSpans[i], (float) minorSpans[i]);
 363             }
 364         } else {
 365             for (int i = 0; i < n; i++) {
 366                 View v = getView(i);
 367                 v.setSize((float) minorSpans[i], (float) majorSpans[i]);
 368             }
 369         }
 370     }
 371 
 372     /**
 373      * Returns the size of the view along an axis.  This is implemented
 374      * to return zero.
 375      *
 376      * @param axis may be either <code>View.X_AXIS</code> or
 377      *          <code>View.Y_AXIS</code>
 378      * @return the current span of the view along the given axis, >= 0
 379      */
 380     float getSpanOnAxis(int axis) {
 381         if (axis == majorAxis) {
 382             return majorSpan;
 383         } else {
 384             return minorSpan;
 385         }
 386     }
 387 
 388     /**
 389      * Sets the size of the view.  This should cause
 390      * layout of the view if the view caches any layout
 391      * information.  This is implemented to call the
 392      * layout method with the sizes inside of the insets.
 393      *
 394      * @param width the width &gt;= 0
 395      * @param height the height &gt;= 0
 396      */
 397     public void setSize(float width, float height) {
 398         layout(Math.max(0, (int)(width - getLeftInset() - getRightInset())),
 399                Math.max(0, (int)(height - getTopInset() - getBottomInset())));
 400     }
 401 
 402     /**
 403      * Renders the <code>BoxView</code> using the given
 404      * rendering surface and area
 405      * on that surface.  Only the children that intersect
 406      * the clip bounds of the given <code>Graphics</code>
 407      * will be rendered.
 408      *
 409      * @param g the rendering surface to use
 410      * @param allocation the allocated region to render into
 411      * @see View#paint
 412      */
 413     public void paint(Graphics g, Shape allocation) {
 414         Rectangle alloc = (allocation instanceof Rectangle) ?
 415                            (Rectangle)allocation : allocation.getBounds();
 416         int n = getViewCount();
 417         int x = alloc.x + getLeftInset();
 418         int y = alloc.y + getTopInset();
 419         Rectangle clip = g.getClipBounds();
 420         for (int i = 0; i < n; i++) {
 421             tempRect.x = x + getOffset(X_AXIS, i);
 422             tempRect.y = y + getOffset(Y_AXIS, i);
 423             tempRect.width = getSpan(X_AXIS, i);
 424             tempRect.height = getSpan(Y_AXIS, i);
 425             int trx0 = tempRect.x, trx1 = trx0 + tempRect.width;
 426             int try0 = tempRect.y, try1 = try0 + tempRect.height;
 427             int crx0 = clip.x, crx1 = crx0 + clip.width;
 428             int cry0 = clip.y, cry1 = cry0 + clip.height;
 429             // We should paint views that intersect with clipping region
 430             // even if the intersection has no inside points (is a line).
 431             // This is needed for supporting views that have zero width, like
 432             // views that contain only combining marks.
 433             if ((trx1 >= crx0) && (try1 >= cry0) && (crx1 >= trx0) && (cry1 >= try0)) {
 434                 paintChild(g, tempRect, i);
 435             }
 436         }
 437     }
 438 
 439     /**
 440      * Fetches the allocation for the given child view.
 441      * This enables finding out where various views
 442      * are located.  This is implemented to return
 443      * <code>null</code> if the layout is invalid,
 444      * otherwise the superclass behavior is executed.
 445      *
 446      * @param index the index of the child, &gt;= 0 &amp;&amp; &gt; getViewCount()
 447      * @param a  the allocation to this view
 448      * @return the allocation to the child; or <code>null</code>
 449      *          if <code>a</code> is <code>null</code>;
 450      *          or <code>null</code> if the layout is invalid
 451      */
 452     public Shape getChildAllocation(int index, Shape a) {
 453         if (a != null) {
 454             Shape ca = super.getChildAllocation(index, a);
 455             if ((ca != null) && (! isAllocationValid())) {
 456                 // The child allocation may not have been set yet.
 457                 Rectangle r = (ca instanceof Rectangle) ?
 458                     (Rectangle) ca : ca.getBounds();
 459                 if ((r.width == 0) && (r.height == 0)) {
 460                     return null;
 461                 }
 462             }
 463             return ca;
 464         }
 465         return null;
 466     }
 467 
 468     /**
 469      * Provides a mapping from the document model coordinate space
 470      * to the coordinate space of the view mapped to it.  This makes


 495      * @return the location within the model that best represents the
 496      *  given point in the view &gt;= 0
 497      * @see View#viewToModel
 498      */
 499     public int viewToModel(float x, float y, Shape a, Position.Bias[] bias) {
 500         if (! isAllocationValid()) {
 501             Rectangle alloc = a.getBounds();
 502             setSize(alloc.width, alloc.height);
 503         }
 504         return super.viewToModel(x, y, a, bias);
 505     }
 506 
 507     /**
 508      * Determines the desired alignment for this view along an
 509      * axis.  This is implemented to give the total alignment
 510      * needed to position the children with the alignment points
 511      * lined up along the axis orthogonal to the axis that is
 512      * being tiled.  The axis being tiled will request to be
 513      * centered (i.e. 0.5f).
 514      *
 515      * @param axis may be either <code>View.X_AXIS</code>
 516      *   or <code>View.Y_AXIS</code>
 517      * @return the desired alignment &gt;= 0.0f &amp;&amp; &lt;= 1.0f; this should
 518      *   be a value between 0.0 and 1.0 where 0 indicates alignment at the
 519      *   origin and 1.0 indicates alignment to the full span
 520      *   away from the origin; an alignment of 0.5 would be the
 521      *   center of the view
 522      * @exception IllegalArgumentException for an invalid axis
 523      */
 524     public float getAlignment(int axis) {
 525         checkRequests(axis);
 526         if (axis == majorAxis) {
 527             return majorRequest.alignment;
 528         } else {
 529             return minorRequest.alignment;
 530         }
 531     }
 532 
 533     /**
 534      * Determines the preferred span for this view along an
 535      * axis.
 536      *
 537      * @param axis may be either <code>View.X_AXIS</code>
 538      *           or <code>View.Y_AXIS</code>
 539      * @return   the span the view would like to be rendered into &gt;= 0;
 540      *           typically the view is told to render into the span
 541      *           that is returned, although there is no guarantee;
 542      *           the parent may choose to resize or break the view
 543      * @exception IllegalArgumentException for an invalid axis type
 544      */
 545     public float getPreferredSpan(int axis) {
 546         checkRequests(axis);
 547         float marginSpan = (axis == X_AXIS) ? getLeftInset() + getRightInset() :
 548             getTopInset() + getBottomInset();
 549         if (axis == majorAxis) {
 550             return ((float)majorRequest.preferred) + marginSpan;
 551         } else {
 552             return ((float)minorRequest.preferred) + marginSpan;
 553         }
 554     }
 555 
 556     /**
 557      * Determines the minimum span for this view along an
 558      * axis.
 559      *
 560      * @param axis may be either <code>View.X_AXIS</code>
 561      *           or <code>View.Y_AXIS</code>
 562      * @return  the span the view would like to be rendered into &gt;= 0;
 563      *           typically the view is told to render into the span
 564      *           that is returned, although there is no guarantee;
 565      *           the parent may choose to resize or break the view
 566      * @exception IllegalArgumentException for an invalid axis type
 567      */
 568     public float getMinimumSpan(int axis) {
 569         checkRequests(axis);
 570         float marginSpan = (axis == X_AXIS) ? getLeftInset() + getRightInset() :
 571             getTopInset() + getBottomInset();
 572         if (axis == majorAxis) {
 573             return ((float)majorRequest.minimum) + marginSpan;
 574         } else {
 575             return ((float)minorRequest.minimum) + marginSpan;
 576         }
 577     }
 578 
 579     /**
 580      * Determines the maximum span for this view along an
 581      * axis.
 582      *
 583      * @param axis may be either <code>View.X_AXIS</code>
 584      *           or <code>View.Y_AXIS</code>
 585      * @return   the span the view would like to be rendered into &gt;= 0;
 586      *           typically the view is told to render into the span
 587      *           that is returned, although there is no guarantee;
 588      *           the parent may choose to resize or break the view
 589      * @exception IllegalArgumentException for an invalid axis type
 590      */
 591     public float getMaximumSpan(int axis) {
 592         checkRequests(axis);
 593         float marginSpan = (axis == X_AXIS) ? getLeftInset() + getRightInset() :
 594             getTopInset() + getBottomInset();
 595         if (axis == majorAxis) {
 596             return ((float)majorRequest.maximum) + marginSpan;
 597         } else {
 598             return ((float)minorRequest.maximum) + marginSpan;
 599         }
 600     }
 601 
 602     // --- local methods ----------------------------------------------------
 603 
 604     /**


 835         int n = getViewCount();
 836         for (int i = 0; i < n; i++) {
 837             View v = getView(i);
 838             int max = (int) v.getMaximumSpan(axis);
 839             if (max < targetSpan) {
 840                 // can't make the child this wide, align it
 841                 float align = v.getAlignment(axis);
 842                 offsets[i] = (int) ((targetSpan - max) * align);
 843                 spans[i] = max;
 844             } else {
 845                 // make it the target width, or as small as it can get.
 846                 int min = (int)v.getMinimumSpan(axis);
 847                 offsets[i] = 0;
 848                 spans[i] = Math.max(min, targetSpan);
 849             }
 850         }
 851     }
 852 
 853     /**
 854      * Calculates the size requirements for the major axis
 855      * <code>axis</code>.
 856      *
 857      * @param axis the axis being studied
 858      * @param r the <code>SizeRequirements</code> object;
 859      *          if <code>null</code> one will be created
 860      * @return the newly initialized <code>SizeRequirements</code> object
 861      * @see javax.swing.SizeRequirements
 862      */
 863     protected SizeRequirements calculateMajorAxisRequirements(int axis, SizeRequirements r) {
 864         // calculate tiled request
 865         float min = 0;
 866         float pref = 0;
 867         float max = 0;
 868 
 869         int n = getViewCount();
 870         for (int i = 0; i < n; i++) {
 871             View v = getView(i);
 872             min += v.getMinimumSpan(axis);
 873             pref += v.getPreferredSpan(axis);
 874             max += v.getMaximumSpan(axis);
 875         }
 876 
 877         if (r == null) {
 878             r = new SizeRequirements();
 879         }
 880         r.alignment = 0.5f;
 881         r.minimum = (int) min;
 882         r.preferred = (int) pref;
 883         r.maximum = (int) max;
 884         return r;
 885     }
 886 
 887     /**
 888      * Calculates the size requirements for the minor axis
 889      * <code>axis</code>.
 890      *
 891      * @param axis the axis being studied
 892      * @param r the <code>SizeRequirements</code> object;
 893      *          if <code>null</code> one will be created
 894      * @return the newly initialized <code>SizeRequirements</code> object
 895      * @see javax.swing.SizeRequirements
 896      */
 897     protected SizeRequirements calculateMinorAxisRequirements(int axis, SizeRequirements r) {
 898         int min = 0;
 899         long pref = 0;
 900         int max = Integer.MAX_VALUE;
 901         int n = getViewCount();
 902         for (int i = 0; i < n; i++) {
 903             View v = getView(i);
 904             min = Math.max((int) v.getMinimumSpan(axis), min);
 905             pref = Math.max((int) v.getPreferredSpan(axis), pref);
 906             max = Math.max((int) v.getMaximumSpan(axis), max);
 907         }
 908 
 909         if (r == null) {
 910             r = new SizeRequirements();
 911             r.alignment = 0.5f;
 912         }
 913         r.preferred = (int) pref;
 914         r.minimum = min;
 915         r.maximum = max;
 916         return r;
 917     }
 918 
 919     /**
 920      * Checks the request cache and update if needed.
 921      * @param axis the axis being studied
 922      * @exception IllegalArgumentException if <code>axis</code> is
 923      *  neither <code>View.X_AXIS</code> nor <code>View.Y_AXIS</code>
 924      */
 925     void checkRequests(int axis) {
 926         if ((axis != X_AXIS) && (axis != Y_AXIS)) {
 927             throw new IllegalArgumentException("Invalid axis: " + axis);
 928         }
 929         if (axis == majorAxis) {
 930             if (!majorReqValid) {
 931                 majorRequest = calculateMajorAxisRequirements(axis,
 932                                                               majorRequest);
 933                 majorReqValid = true;
 934             }
 935         } else if (! minorReqValid) {
 936             minorRequest = calculateMinorAxisRequirements(axis, minorRequest);
 937             minorReqValid = true;
 938         }
 939     }
 940 
 941     /**
 942      * Computes the location and extent of each child view
 943      * in this <code>BoxView</code> given the <code>targetSpan</code>,
 944      * which is the width (or height) of the region we have to
 945      * work with.
 946      *
 947      * @param targetSpan the total span given to the view, which
 948      *  would be used to layout the children
 949      * @param axis the axis being studied, either
 950      *          <code>View.X_AXIS</code> or <code>View.Y_AXIS</code>
 951      * @param offsets an empty array filled by this method with
 952      *          values specifying the location  of each child view
 953      * @param spans  an empty array filled by this method with
 954      *          values specifying the extent of each child view
 955      */
 956     protected void baselineLayout(int targetSpan, int axis, int[] offsets, int[] spans) {
 957         int totalAscent = (int)(targetSpan * getAlignment(axis));
 958         int totalDescent = targetSpan - totalAscent;
 959 
 960         int n = getViewCount();
 961 
 962         for (int i = 0; i < n; i++) {
 963             View v = getView(i);
 964             float align = v.getAlignment(axis);
 965             float viewSpan;
 966 
 967             if (v.getResizeWeight(axis) > 0) {
 968                 // if resizable then resize to the best fit
 969 
 970                 // the smallest span possible


 979                     // if the alignment is 1 then we need to fit into the ascent
 980                     viewSpan = Math.max(Math.min(maxSpan, totalAscent), minSpan);
 981                 } else {
 982                     // figure out the span that we must fit into
 983                     float fitSpan = Math.min(totalAscent / align,
 984                                              totalDescent / (1.0f - align));
 985                     // fit into the calculated span
 986                     viewSpan = Math.max(Math.min(maxSpan, fitSpan), minSpan);
 987                 }
 988             } else {
 989                 // otherwise use the preferred spans
 990                 viewSpan = v.getPreferredSpan(axis);
 991             }
 992 
 993             offsets[i] = totalAscent - (int)(viewSpan * align);
 994             spans[i] = (int)viewSpan;
 995         }
 996     }
 997 
 998     /**
 999      * Calculates the size requirements for this <code>BoxView</code>
1000      * by examining the size of each child view.
1001      *
1002      * @param axis the axis being studied
1003      * @param r the <code>SizeRequirements</code> object;
1004      *          if <code>null</code> one will be created
1005      * @return the newly initialized <code>SizeRequirements</code> object
1006      */
1007     protected SizeRequirements baselineRequirements(int axis, SizeRequirements r) {
1008         SizeRequirements totalAscent = new SizeRequirements();
1009         SizeRequirements totalDescent = new SizeRequirements();
1010 
1011         if (r == null) {
1012             r = new SizeRequirements();
1013         }
1014 
1015         r.alignment = 0.5f;
1016 
1017         int n = getViewCount();
1018 
1019         // loop through all children calculating the max of all their ascents and
1020         // descents at minimum, preferred, and maximum sizes
1021         for (int i = 0; i < n; i++) {
1022             View v = getView(i);
1023             float align = v.getAlignment(axis);
1024             float span;
1025             int ascent;


1098      * @return the offset (location) for the specified child
1099      */
1100     protected int getOffset(int axis, int childIndex) {
1101         int[] offsets = (axis == majorAxis) ? majorOffsets : minorOffsets;
1102         return offsets[childIndex];
1103     }
1104 
1105     /**
1106      * Fetches the span of a particular child's current layout.
1107      * @param axis the axis being studied
1108      * @param childIndex the index of the requested child
1109      * @return the span (width or height) of the specified child
1110      */
1111     protected int getSpan(int axis, int childIndex) {
1112         int[] spans = (axis == majorAxis) ? majorSpans : minorSpans;
1113         return spans[childIndex];
1114     }
1115 
1116     /**
1117      * Determines in which direction the next view lays.
1118      * Consider the View at index n. Typically the <code>View</code>s
1119      * are layed out from left to right, so that the <code>View</code>
1120      * to the EAST will be at index n + 1, and the <code>View</code>
1121      * to the WEST will be at index n - 1. In certain situations,
1122      * such as with bidirectional text, it is possible
1123      * that the <code>View</code> to EAST is not at index n + 1,
1124      * but rather at index n - 1, or that the <code>View</code>
1125      * to the WEST is not at index n - 1, but index n + 1.
1126      * In this case this method would return true,
1127      * indicating the <code>View</code>s are layed out in
1128      * descending order. Otherwise the method would return false
1129      * indicating the <code>View</code>s are layed out in ascending order.
1130      * <p>
1131      * If the receiver is laying its <code>View</code>s along the
1132      * <code>Y_AXIS</code>, this will return the value from
1133      * invoking the same method on the <code>View</code>
1134      * responsible for rendering <code>position</code> and
1135      * <code>bias</code>. Otherwise this will return false.
1136      *
1137      * @param position position into the model
1138      * @param bias either <code>Position.Bias.Forward</code> or
1139      *          <code>Position.Bias.Backward</code>
1140      * @return true if the <code>View</code>s surrounding the
1141      *          <code>View</code> responding for rendering
1142      *          <code>position</code> and <code>bias</code>
1143      *          are layed out in descending order; otherwise false
1144      */
1145     protected boolean flipEastAndWestAtEnds(int position,
1146                                             Position.Bias bias) {
1147         if(majorAxis == Y_AXIS) {
1148             int testPos = (bias == Position.Bias.Backward) ?
1149                           Math.max(0, position - 1) : position;
1150             int index = getViewIndexAtPosition(testPos);
1151             if(index != -1) {
1152                 View v = getView(index);
1153                 if(v != null && v instanceof CompositeView) {
1154                     return ((CompositeView)v).flipEastAndWestAtEnds(position,
1155                                                                     bias);
1156                 }
1157             }
1158         }
1159         return false;
1160     }
1161 
1162     // --- variables ------------------------------------------------




  24  */
  25 package javax.swing.text;
  26 
  27 import java.io.PrintStream;
  28 import java.util.Vector;
  29 import java.awt.*;
  30 import javax.swing.event.DocumentEvent;
  31 import javax.swing.SizeRequirements;
  32 
  33 /**
  34  * A view that arranges its children into a box shape by tiling
  35  * its children along an axis.  The box is somewhat like that
  36  * found in TeX where there is alignment of the
  37  * children, flexibility of the children is considered, etc.
  38  * This is a building block that might be useful to represent
  39  * things like a collection of lines, paragraphs,
  40  * lists, columns, pages, etc.  The axis along which the children are tiled is
  41  * considered the major axis.  The orthogonal axis is the minor axis.
  42  * <p>
  43  * Layout for each axis is handled separately by the methods
  44  * {@code layoutMajorAxis} and {@code layoutMinorAxis}.
  45  * Subclasses can change the layout algorithm by
  46  * reimplementing these methods.    These methods will be called
  47  * as necessary depending upon whether or not there is cached
  48  * layout information and the cache is considered
  49  * valid.  These methods are typically called if the given size
  50  * along the axis changes, or if {@code layoutChanged} is
  51  * called to force an updated layout.  The {@code layoutChanged}
  52  * method invalidates cached layout information, if there is any.
  53  * The requirements published to the parent view are calculated by
  54  * the methods {@code calculateMajorAxisRequirements}
  55  * and  {@code calculateMinorAxisRequirements}.
  56  * If the layout algorithm is changed, these methods will
  57  * likely need to be reimplemented.
  58  *
  59  * @author  Timothy Prinzing
  60  */
  61 public class BoxView extends CompositeView {
  62 
  63     /**
  64      * Constructs a {@code BoxView}.
  65      *
  66      * @param elem the element this view is responsible for
  67      * @param axis either {@code View.X_AXIS} or {@code View.Y_AXIS}
  68      */
  69     public BoxView(Element elem, int axis) {
  70         super(elem);
  71         tempRect = new Rectangle();
  72         this.majorAxis = axis;
  73 
  74         majorOffsets = new int[0];
  75         majorSpans = new int[0];
  76         majorReqValid = false;
  77         majorAllocValid = false;
  78         minorOffsets = new int[0];
  79         minorSpans = new int[0];
  80         minorReqValid = false;
  81         minorAllocValid = false;
  82     }
  83 
  84     /**
  85      * Fetches the tile axis property.  This is the axis along which
  86      * the child views are tiled.
  87      *
  88      * @return the major axis of the box, either
  89      *  {@code View.X_AXIS} or {@code View.Y_AXIS}
  90      *
  91      * @since 1.3
  92      */
  93     public int getAxis() {
  94         return majorAxis;
  95     }
  96 
  97     /**
  98      * Sets the tile axis property.  This is the axis along which
  99      * the child views are tiled.
 100      *
 101      * @param axis either {@code View.X_AXIS} or {@code View.Y_AXIS}
 102      *
 103      * @since 1.3
 104      */
 105     public void setAxis(int axis) {
 106         boolean axisChanged = (axis != majorAxis);
 107         majorAxis = axis;
 108         if (axisChanged) {
 109             preferenceChanged(null, true, true);
 110         }
 111     }
 112 
 113     /**
 114      * Invalidates the layout along an axis.  This happens
 115      * automatically if the preferences have changed for
 116      * any of the child views.  In some cases the layout
 117      * may need to be recalculated when the preferences
 118      * have not changed.  The layout can be marked as
 119      * invalid by calling this method.  The layout will
 120      * be updated the next time the {@code setSize} method
 121      * is called on this view (typically in paint).
 122      *
 123      * @param axis either {@code View.X_AXIS} or {@code View.Y_AXIS}
 124      *
 125      * @since 1.3
 126      */
 127     public void layoutChanged(int axis) {
 128         if (axis == majorAxis) {
 129             majorAllocValid = false;
 130         } else {
 131             minorAllocValid = false;
 132         }
 133     }
 134 
 135     /**
 136      * Determines if the layout is valid along the given axis.
 137      * @return if the layout is valid along the given axis
 138      *
 139      * @param axis either {@code View.X_AXIS} or {@code View.Y_AXIS}
 140      *
 141      * @since 1.4
 142      */
 143     protected boolean isLayoutValid(int axis) {
 144         if (axis == majorAxis) {
 145             return majorAllocValid;
 146         } else {
 147             return minorAllocValid;
 148         }
 149     }
 150 
 151     /**
 152      * Paints a child.  By default
 153      * that is all it does, but a subclass can use this to paint
 154      * things relative to the child.
 155      *
 156      * @param g the graphics context
 157      * @param alloc the allocated region to paint into
 158      * @param index the child index, &gt;= 0 &amp;&amp; &lt; getViewCount()
 159      */
 160     protected void paintChild(Graphics g, Rectangle alloc, int index) {
 161         View child = getView(index);
 162         child.paint(g, alloc);
 163     }
 164 
 165     // --- View methods ---------------------------------------------
 166 
 167     /**
 168      * Invalidates the layout and resizes the cache of
 169      * requests/allocations.  The child allocations can still
 170      * be accessed for the old layout, but the new children
 171      * will have an offset and span of 0.
 172      *
 173      * @param index the starting index into the child views to insert
 174      *   the new views; this should be a value &gt;= 0 and &lt;= getViewCount
 175      * @param length the number of existing child views to remove;
 176      *   This should be a value &gt;= 0 and &lt;= (getViewCount() - offset)
 177      * @param elems the child views to add; this value can be
 178      *   {@code null} to indicate no children are being added
 179      *   (useful to remove)
 180      */
 181     public void replace(int index, int length, View[] elems) {
 182         super.replace(index, length, elems);
 183 
 184         // invalidate cache
 185         int nInserted = (elems != null) ? elems.length : 0;
 186         majorOffsets = updateLayoutArray(majorOffsets, index, nInserted);
 187         majorSpans = updateLayoutArray(majorSpans, index, nInserted);
 188         majorReqValid = false;
 189         majorAllocValid = false;
 190         minorOffsets = updateLayoutArray(minorOffsets, index, nInserted);
 191         minorSpans = updateLayoutArray(minorSpans, index, nInserted);
 192         minorReqValid = false;
 193         minorAllocValid = false;
 194     }
 195 
 196     /**
 197      * Resizes the given layout array to match the new number of
 198      * child views.  The current number of child views are used to
 199      * produce the new array.  The contents of the old array are
 200      * inserted into the new array at the appropriate places so that
 201      * the old layout information is transferred to the new array.
 202      *
 203      * @param oldArray the original layout array
 204      * @param offset location where new views will be inserted
 205      * @param nInserted the number of child views being inserted;
 206      *          therefore the number of blank spaces to leave in the
 207      *          new array at location {@code offset}
 208      * @return the new layout array
 209      */
 210     int[] updateLayoutArray(int[] oldArray, int offset, int nInserted) {
 211         int n = getViewCount();
 212         int[] newArray = new int[n];
 213 
 214         System.arraycopy(oldArray, 0, newArray, 0, offset);
 215         System.arraycopy(oldArray, offset,
 216                          newArray, offset + nInserted, n - nInserted - offset);
 217         return newArray;
 218     }
 219 
 220     /**
 221      * Forwards the given {@code DocumentEvent} to the child views
 222      * that need to be notified of the change to the model.
 223      * If a child changed its requirements and the allocation
 224      * was valid prior to forwarding the portion of the box
 225      * from the starting child to the end of the box will
 226      * be repainted.
 227      *
 228      * @param ec changes to the element this view is responsible
 229      *  for (may be {@code null} if there were no changes)
 230      * @param e the change information from the associated document
 231      * @param a the current allocation of the view
 232      * @param f the factory to use to rebuild if the view has children
 233      * @see #insertUpdate
 234      * @see #removeUpdate
 235      * @see #changedUpdate
 236      * @since 1.3
 237      */
 238     protected void forwardUpdate(DocumentEvent.ElementChange ec,
 239                                  DocumentEvent e, Shape a, ViewFactory f) {
 240         boolean wasValid = isLayoutValid(majorAxis);
 241         super.forwardUpdate(ec, e, a, f);
 242 
 243         // determine if a repaint is needed
 244         if (wasValid && (! isLayoutValid(majorAxis))) {
 245             // Repaint is needed because one of the tiled children
 246             // have changed their span along the major axis.  If there
 247             // is a hosting component and an allocated shape we repaint.
 248             Component c = getContainer();
 249             if ((a != null) && (c != null)) {


 273      * @param width true if the width preference should change
 274      * @param height true if the height preference should change
 275      */
 276     public void preferenceChanged(View child, boolean width, boolean height) {
 277         boolean majorChanged = (majorAxis == X_AXIS) ? width : height;
 278         boolean minorChanged = (majorAxis == X_AXIS) ? height : width;
 279         if (majorChanged) {
 280             majorReqValid = false;
 281             majorAllocValid = false;
 282         }
 283         if (minorChanged) {
 284             minorReqValid = false;
 285             minorAllocValid = false;
 286         }
 287         super.preferenceChanged(child, width, height);
 288     }
 289 
 290     /**
 291      * Gets the resize weight.  A value of 0 or less is not resizable.
 292      *
 293      * @param axis may be either {@code View.X_AXIS} or
 294      *          {@code View.Y_AXIS}
 295      * @return the weight
 296      * @exception IllegalArgumentException for an invalid axis
 297      */
 298     public int getResizeWeight(int axis) {
 299         checkRequests(axis);
 300         if (axis == majorAxis) {
 301             if ((majorRequest.preferred != majorRequest.minimum) ||
 302                 (majorRequest.preferred != majorRequest.maximum)) {
 303                 return 1;
 304             }
 305         } else {
 306             if ((minorRequest.preferred != minorRequest.minimum) ||
 307                 (minorRequest.preferred != minorRequest.maximum)) {
 308                 return 1;
 309             }
 310         }
 311         return 0;
 312     }
 313 
 314     /**
 315      * Sets the size of the view along an axis.  This should cause
 316      * layout of the view along the given axis.
 317      *
 318      * @param axis may be either {@code View.X_AXIS} or
 319      *          {@code View.Y_AXIS}
 320      * @param span the span to layout to >= 0
 321      */
 322     void setSpanOnAxis(int axis, float span) {
 323         if (axis == majorAxis) {
 324             if (majorSpan != (int) span) {
 325                 majorAllocValid = false;
 326             }
 327             if (! majorAllocValid) {
 328                 // layout the major axis
 329                 majorSpan = (int) span;
 330                 checkRequests(majorAxis);
 331                 layoutMajorAxis(majorSpan, axis, majorOffsets, majorSpans);
 332                 majorAllocValid = true;
 333 
 334                 // flush changes to the children
 335                 updateChildSizes();
 336             }
 337         } else {
 338             if (((int) span) != minorSpan) {
 339                 minorAllocValid = false;


 356      */
 357     void updateChildSizes() {
 358         int n = getViewCount();
 359         if (majorAxis == X_AXIS) {
 360             for (int i = 0; i < n; i++) {
 361                 View v = getView(i);
 362                 v.setSize((float) majorSpans[i], (float) minorSpans[i]);
 363             }
 364         } else {
 365             for (int i = 0; i < n; i++) {
 366                 View v = getView(i);
 367                 v.setSize((float) minorSpans[i], (float) majorSpans[i]);
 368             }
 369         }
 370     }
 371 
 372     /**
 373      * Returns the size of the view along an axis.  This is implemented
 374      * to return zero.
 375      *
 376      * @param axis may be either {@code View.X_AXIS} or
 377      *          {@code View.Y_AXIS}
 378      * @return the current span of the view along the given axis, >= 0
 379      */
 380     float getSpanOnAxis(int axis) {
 381         if (axis == majorAxis) {
 382             return majorSpan;
 383         } else {
 384             return minorSpan;
 385         }
 386     }
 387 
 388     /**
 389      * Sets the size of the view.  This should cause
 390      * layout of the view if the view caches any layout
 391      * information.  This is implemented to call the
 392      * layout method with the sizes inside of the insets.
 393      *
 394      * @param width the width &gt;= 0
 395      * @param height the height &gt;= 0
 396      */
 397     public void setSize(float width, float height) {
 398         layout(Math.max(0, (int)(width - getLeftInset() - getRightInset())),
 399                Math.max(0, (int)(height - getTopInset() - getBottomInset())));
 400     }
 401 
 402     /**
 403      * Renders the {@code BoxView} using the given
 404      * rendering surface and area
 405      * on that surface.  Only the children that intersect
 406      * the clip bounds of the given {@code Graphics}
 407      * will be rendered.
 408      *
 409      * @param g the rendering surface to use
 410      * @param allocation the allocated region to render into
 411      * @see View#paint
 412      */
 413     public void paint(Graphics g, Shape allocation) {
 414         Rectangle alloc = (allocation instanceof Rectangle) ?
 415                            (Rectangle)allocation : allocation.getBounds();
 416         int n = getViewCount();
 417         int x = alloc.x + getLeftInset();
 418         int y = alloc.y + getTopInset();
 419         Rectangle clip = g.getClipBounds();
 420         for (int i = 0; i < n; i++) {
 421             tempRect.x = x + getOffset(X_AXIS, i);
 422             tempRect.y = y + getOffset(Y_AXIS, i);
 423             tempRect.width = getSpan(X_AXIS, i);
 424             tempRect.height = getSpan(Y_AXIS, i);
 425             int trx0 = tempRect.x, trx1 = trx0 + tempRect.width;
 426             int try0 = tempRect.y, try1 = try0 + tempRect.height;
 427             int crx0 = clip.x, crx1 = crx0 + clip.width;
 428             int cry0 = clip.y, cry1 = cry0 + clip.height;
 429             // We should paint views that intersect with clipping region
 430             // even if the intersection has no inside points (is a line).
 431             // This is needed for supporting views that have zero width, like
 432             // views that contain only combining marks.
 433             if ((trx1 >= crx0) && (try1 >= cry0) && (crx1 >= trx0) && (cry1 >= try0)) {
 434                 paintChild(g, tempRect, i);
 435             }
 436         }
 437     }
 438 
 439     /**
 440      * Fetches the allocation for the given child view.
 441      * This enables finding out where various views
 442      * are located.  This is implemented to return
 443      * {@code null} if the layout is invalid,
 444      * otherwise the superclass behavior is executed.
 445      *
 446      * @param index the index of the child, &gt;= 0 &amp;&amp; &gt; getViewCount()
 447      * @param a  the allocation to this view
 448      * @return the allocation to the child; or {@code null}
 449      *          if {@code a} is {@code null};
 450      *          or {@code null} if the layout is invalid
 451      */
 452     public Shape getChildAllocation(int index, Shape a) {
 453         if (a != null) {
 454             Shape ca = super.getChildAllocation(index, a);
 455             if ((ca != null) && (! isAllocationValid())) {
 456                 // The child allocation may not have been set yet.
 457                 Rectangle r = (ca instanceof Rectangle) ?
 458                     (Rectangle) ca : ca.getBounds();
 459                 if ((r.width == 0) && (r.height == 0)) {
 460                     return null;
 461                 }
 462             }
 463             return ca;
 464         }
 465         return null;
 466     }
 467 
 468     /**
 469      * Provides a mapping from the document model coordinate space
 470      * to the coordinate space of the view mapped to it.  This makes


 495      * @return the location within the model that best represents the
 496      *  given point in the view &gt;= 0
 497      * @see View#viewToModel
 498      */
 499     public int viewToModel(float x, float y, Shape a, Position.Bias[] bias) {
 500         if (! isAllocationValid()) {
 501             Rectangle alloc = a.getBounds();
 502             setSize(alloc.width, alloc.height);
 503         }
 504         return super.viewToModel(x, y, a, bias);
 505     }
 506 
 507     /**
 508      * Determines the desired alignment for this view along an
 509      * axis.  This is implemented to give the total alignment
 510      * needed to position the children with the alignment points
 511      * lined up along the axis orthogonal to the axis that is
 512      * being tiled.  The axis being tiled will request to be
 513      * centered (i.e. 0.5f).
 514      *
 515      * @param axis may be either {@code View.X_AXIS}
 516      *   or {@code View.Y_AXIS}
 517      * @return the desired alignment &gt;= 0.0f &amp;&amp; &lt;= 1.0f; this should
 518      *   be a value between 0.0 and 1.0 where 0 indicates alignment at the
 519      *   origin and 1.0 indicates alignment to the full span
 520      *   away from the origin; an alignment of 0.5 would be the
 521      *   center of the view
 522      * @exception IllegalArgumentException for an invalid axis
 523      */
 524     public float getAlignment(int axis) {
 525         checkRequests(axis);
 526         if (axis == majorAxis) {
 527             return majorRequest.alignment;
 528         } else {
 529             return minorRequest.alignment;
 530         }
 531     }
 532 
 533     /**
 534      * Determines the preferred span for this view along an
 535      * axis.
 536      *
 537      * @param axis may be either {@code View.X_AXIS}
 538      *           or {@code View.Y_AXIS}
 539      * @return   the span the view would like to be rendered into &gt;= 0;
 540      *           typically the view is told to render into the span
 541      *           that is returned, although there is no guarantee;
 542      *           the parent may choose to resize or break the view
 543      * @exception IllegalArgumentException for an invalid axis type
 544      */
 545     public float getPreferredSpan(int axis) {
 546         checkRequests(axis);
 547         float marginSpan = (axis == X_AXIS) ? getLeftInset() + getRightInset() :
 548             getTopInset() + getBottomInset();
 549         if (axis == majorAxis) {
 550             return ((float)majorRequest.preferred) + marginSpan;
 551         } else {
 552             return ((float)minorRequest.preferred) + marginSpan;
 553         }
 554     }
 555 
 556     /**
 557      * Determines the minimum span for this view along an
 558      * axis.
 559      *
 560      * @param axis may be either {@code View.X_AXIS}
 561      *           or {@code View.Y_AXIS}
 562      * @return  the span the view would like to be rendered into &gt;= 0;
 563      *           typically the view is told to render into the span
 564      *           that is returned, although there is no guarantee;
 565      *           the parent may choose to resize or break the view
 566      * @exception IllegalArgumentException for an invalid axis type
 567      */
 568     public float getMinimumSpan(int axis) {
 569         checkRequests(axis);
 570         float marginSpan = (axis == X_AXIS) ? getLeftInset() + getRightInset() :
 571             getTopInset() + getBottomInset();
 572         if (axis == majorAxis) {
 573             return ((float)majorRequest.minimum) + marginSpan;
 574         } else {
 575             return ((float)minorRequest.minimum) + marginSpan;
 576         }
 577     }
 578 
 579     /**
 580      * Determines the maximum span for this view along an
 581      * axis.
 582      *
 583      * @param axis may be either {@code View.X_AXIS}
 584      *           or {@code View.Y_AXIS}
 585      * @return   the span the view would like to be rendered into &gt;= 0;
 586      *           typically the view is told to render into the span
 587      *           that is returned, although there is no guarantee;
 588      *           the parent may choose to resize or break the view
 589      * @exception IllegalArgumentException for an invalid axis type
 590      */
 591     public float getMaximumSpan(int axis) {
 592         checkRequests(axis);
 593         float marginSpan = (axis == X_AXIS) ? getLeftInset() + getRightInset() :
 594             getTopInset() + getBottomInset();
 595         if (axis == majorAxis) {
 596             return ((float)majorRequest.maximum) + marginSpan;
 597         } else {
 598             return ((float)minorRequest.maximum) + marginSpan;
 599         }
 600     }
 601 
 602     // --- local methods ----------------------------------------------------
 603 
 604     /**


 835         int n = getViewCount();
 836         for (int i = 0; i < n; i++) {
 837             View v = getView(i);
 838             int max = (int) v.getMaximumSpan(axis);
 839             if (max < targetSpan) {
 840                 // can't make the child this wide, align it
 841                 float align = v.getAlignment(axis);
 842                 offsets[i] = (int) ((targetSpan - max) * align);
 843                 spans[i] = max;
 844             } else {
 845                 // make it the target width, or as small as it can get.
 846                 int min = (int)v.getMinimumSpan(axis);
 847                 offsets[i] = 0;
 848                 spans[i] = Math.max(min, targetSpan);
 849             }
 850         }
 851     }
 852 
 853     /**
 854      * Calculates the size requirements for the major axis
 855      * {@code axis}.
 856      *
 857      * @param axis the axis being studied
 858      * @param r the {@code SizeRequirements} object;
 859      *          if {@code null} one will be created
 860      * @return the newly initialized {@code SizeRequirements} object
 861      * @see javax.swing.SizeRequirements
 862      */
 863     protected SizeRequirements calculateMajorAxisRequirements(int axis, SizeRequirements r) {
 864         // calculate tiled request
 865         float min = 0;
 866         float pref = 0;
 867         float max = 0;
 868 
 869         int n = getViewCount();
 870         for (int i = 0; i < n; i++) {
 871             View v = getView(i);
 872             min += v.getMinimumSpan(axis);
 873             pref += v.getPreferredSpan(axis);
 874             max += v.getMaximumSpan(axis);
 875         }
 876 
 877         if (r == null) {
 878             r = new SizeRequirements();
 879         }
 880         r.alignment = 0.5f;
 881         r.minimum = (int) min;
 882         r.preferred = (int) pref;
 883         r.maximum = (int) max;
 884         return r;
 885     }
 886 
 887     /**
 888      * Calculates the size requirements for the minor axis
 889      * {@code axis}.
 890      *
 891      * @param axis the axis being studied
 892      * @param r the {@code SizeRequirements} object;
 893      *          if {@code null} one will be created
 894      * @return the newly initialized {@code SizeRequirements} object
 895      * @see javax.swing.SizeRequirements
 896      */
 897     protected SizeRequirements calculateMinorAxisRequirements(int axis, SizeRequirements r) {
 898         int min = 0;
 899         long pref = 0;
 900         int max = Integer.MAX_VALUE;
 901         int n = getViewCount();
 902         for (int i = 0; i < n; i++) {
 903             View v = getView(i);
 904             min = Math.max((int) v.getMinimumSpan(axis), min);
 905             pref = Math.max((int) v.getPreferredSpan(axis), pref);
 906             max = Math.max((int) v.getMaximumSpan(axis), max);
 907         }
 908 
 909         if (r == null) {
 910             r = new SizeRequirements();
 911             r.alignment = 0.5f;
 912         }
 913         r.preferred = (int) pref;
 914         r.minimum = min;
 915         r.maximum = max;
 916         return r;
 917     }
 918 
 919     /**
 920      * Checks the request cache and update if needed.
 921      * @param axis the axis being studied
 922      * @exception IllegalArgumentException if {@code axis} is
 923      *  neither {@code View.X_AXIS} nor {@code View.Y_AXIS}
 924      */
 925     void checkRequests(int axis) {
 926         if ((axis != X_AXIS) && (axis != Y_AXIS)) {
 927             throw new IllegalArgumentException("Invalid axis: " + axis);
 928         }
 929         if (axis == majorAxis) {
 930             if (!majorReqValid) {
 931                 majorRequest = calculateMajorAxisRequirements(axis,
 932                                                               majorRequest);
 933                 majorReqValid = true;
 934             }
 935         } else if (! minorReqValid) {
 936             minorRequest = calculateMinorAxisRequirements(axis, minorRequest);
 937             minorReqValid = true;
 938         }
 939     }
 940 
 941     /**
 942      * Computes the location and extent of each child view
 943      * in this {@code BoxView} given the {@code targetSpan},
 944      * which is the width (or height) of the region we have to
 945      * work with.
 946      *
 947      * @param targetSpan the total span given to the view, which
 948      *  would be used to layout the children
 949      * @param axis the axis being studied, either
 950      *          {@code View.X_AXIS} or {@code View.Y_AXIS}
 951      * @param offsets an empty array filled by this method with
 952      *          values specifying the location  of each child view
 953      * @param spans  an empty array filled by this method with
 954      *          values specifying the extent of each child view
 955      */
 956     protected void baselineLayout(int targetSpan, int axis, int[] offsets, int[] spans) {
 957         int totalAscent = (int)(targetSpan * getAlignment(axis));
 958         int totalDescent = targetSpan - totalAscent;
 959 
 960         int n = getViewCount();
 961 
 962         for (int i = 0; i < n; i++) {
 963             View v = getView(i);
 964             float align = v.getAlignment(axis);
 965             float viewSpan;
 966 
 967             if (v.getResizeWeight(axis) > 0) {
 968                 // if resizable then resize to the best fit
 969 
 970                 // the smallest span possible


 979                     // if the alignment is 1 then we need to fit into the ascent
 980                     viewSpan = Math.max(Math.min(maxSpan, totalAscent), minSpan);
 981                 } else {
 982                     // figure out the span that we must fit into
 983                     float fitSpan = Math.min(totalAscent / align,
 984                                              totalDescent / (1.0f - align));
 985                     // fit into the calculated span
 986                     viewSpan = Math.max(Math.min(maxSpan, fitSpan), minSpan);
 987                 }
 988             } else {
 989                 // otherwise use the preferred spans
 990                 viewSpan = v.getPreferredSpan(axis);
 991             }
 992 
 993             offsets[i] = totalAscent - (int)(viewSpan * align);
 994             spans[i] = (int)viewSpan;
 995         }
 996     }
 997 
 998     /**
 999      * Calculates the size requirements for this {@code BoxView}
1000      * by examining the size of each child view.
1001      *
1002      * @param axis the axis being studied
1003      * @param r the {@code SizeRequirements} object;
1004      *          if {@code null} one will be created
1005      * @return the newly initialized {@code SizeRequirements} object
1006      */
1007     protected SizeRequirements baselineRequirements(int axis, SizeRequirements r) {
1008         SizeRequirements totalAscent = new SizeRequirements();
1009         SizeRequirements totalDescent = new SizeRequirements();
1010 
1011         if (r == null) {
1012             r = new SizeRequirements();
1013         }
1014 
1015         r.alignment = 0.5f;
1016 
1017         int n = getViewCount();
1018 
1019         // loop through all children calculating the max of all their ascents and
1020         // descents at minimum, preferred, and maximum sizes
1021         for (int i = 0; i < n; i++) {
1022             View v = getView(i);
1023             float align = v.getAlignment(axis);
1024             float span;
1025             int ascent;


1098      * @return the offset (location) for the specified child
1099      */
1100     protected int getOffset(int axis, int childIndex) {
1101         int[] offsets = (axis == majorAxis) ? majorOffsets : minorOffsets;
1102         return offsets[childIndex];
1103     }
1104 
1105     /**
1106      * Fetches the span of a particular child's current layout.
1107      * @param axis the axis being studied
1108      * @param childIndex the index of the requested child
1109      * @return the span (width or height) of the specified child
1110      */
1111     protected int getSpan(int axis, int childIndex) {
1112         int[] spans = (axis == majorAxis) ? majorSpans : minorSpans;
1113         return spans[childIndex];
1114     }
1115 
1116     /**
1117      * Determines in which direction the next view lays.
1118      * Consider the View at index n. Typically the {@code View}s
1119      * are layed out from left to right, so that the {@code View}
1120      * to the EAST will be at index n + 1, and the {@code View}
1121      * to the WEST will be at index n - 1. In certain situations,
1122      * such as with bidirectional text, it is possible
1123      * that the {@code View} to EAST is not at index n + 1,
1124      * but rather at index n - 1, or that the {@code View}
1125      * to the WEST is not at index n - 1, but index n + 1.
1126      * In this case this method would return true,
1127      * indicating the {@code View}s are layed out in
1128      * descending order. Otherwise the method would return false
1129      * indicating the {@code View}s are layed out in ascending order.
1130      * <p>
1131      * If the receiver is laying its {@code View}s along the
1132      * {@code Y_AXIS}, this will return the value from
1133      * invoking the same method on the {@code View}
1134      * responsible for rendering {@code position} and
1135      * {@code bias}. Otherwise this will return false.
1136      *
1137      * @param position position into the model
1138      * @param bias either {@code Position.Bias.Forward} or
1139      *          {@code Position.Bias.Backward}
1140      * @return true if the {@code View}s surrounding the
1141      *          {@code View} responding for rendering
1142      *          {@code position} and {@code bias}
1143      *          are layed out in descending order; otherwise false
1144      */
1145     protected boolean flipEastAndWestAtEnds(int position,
1146                                             Position.Bias bias) {
1147         if(majorAxis == Y_AXIS) {
1148             int testPos = (bias == Position.Bias.Backward) ?
1149                           Math.max(0, position - 1) : position;
1150             int index = getViewIndexAtPosition(testPos);
1151             if(index != -1) {
1152                 View v = getView(index);
1153                 if(v != null && v instanceof CompositeView) {
1154                     return ((CompositeView)v).flipEastAndWestAtEnds(position,
1155                                                                     bias);
1156                 }
1157             }
1158         }
1159         return false;
1160     }
1161 
1162     // --- variables ------------------------------------------------


< prev index next >