Source for javax.swing.text.BoxView

   1: /* BoxView.java -- An composite view
   2:    Copyright (C) 2005, 2006  Free Software Foundation, Inc.
   3: 
   4: This file is part of GNU Classpath.
   5: 
   6: GNU Classpath is free software; you can redistribute it and/or modify
   7: it under the terms of the GNU General Public License as published by
   8: the Free Software Foundation; either version 2, or (at your option)
   9: any later version.
  10: 
  11: GNU Classpath is distributed in the hope that it will be useful, but
  12: WITHOUT ANY WARRANTY; without even the implied warranty of
  13: MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
  14: General Public License for more details.
  15: 
  16: You should have received a copy of the GNU General Public License
  17: along with GNU Classpath; see the file COPYING.  If not, write to the
  18: Free Software Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA
  19: 02110-1301 USA.
  20: 
  21: Linking this library statically or dynamically with other modules is
  22: making a combined work based on this library.  Thus, the terms and
  23: conditions of the GNU General Public License cover the whole
  24: combination.
  25: 
  26: As a special exception, the copyright holders of this library give you
  27: permission to link this library with independent modules to produce an
  28: executable, regardless of the license terms of these independent
  29: modules, and to copy and distribute the resulting executable under
  30: terms of your choice, provided that you also meet, for each linked
  31: independent module, the terms and conditions of the license of that
  32: module.  An independent module is a module which is not derived from
  33: or based on this library.  If you modify this library, you may extend
  34: this exception to your version of the library, but you are not
  35: obligated to do so.  If you do not wish to do so, delete this
  36: exception statement from your version. */
  37: 
  38: 
  39: package javax.swing.text;
  40: 
  41: import java.awt.Graphics;
  42: import java.awt.Rectangle;
  43: import java.awt.Shape;
  44: 
  45: import javax.swing.SizeRequirements;
  46: import javax.swing.event.DocumentEvent;
  47: 
  48: /**
  49:  * An implementation of {@link CompositeView} that arranges its children in
  50:  * a box along one axis. This is comparable to how the <code>BoxLayout</code>
  51:  * works, but for <code>View</code> children.
  52:  *
  53:  * @author Roman Kennke (roman@kennke.org)
  54:  */
  55: public class BoxView
  56:   extends CompositeView
  57: {
  58: 
  59:   /**
  60:    * The axis along which this <code>BoxView</code> is laid out.
  61:    */
  62:   private int myAxis;
  63: 
  64:   /**
  65:    * Indicates if the layout is valid along X_AXIS or Y_AXIS.
  66:    */
  67:   private boolean[] layoutValid = new boolean[2];
  68: 
  69:   /**
  70:    * Indicates if the requirements for an axis are valid.
  71:    */
  72:   private boolean[] requirementsValid = new boolean[2];
  73: 
  74:   /**
  75:    * The spans along the X_AXIS and Y_AXIS.
  76:    */
  77:   private int[][] spans = new int[2][];
  78: 
  79:   /**
  80:    * The offsets of the children along the X_AXIS and Y_AXIS.
  81:    */
  82:   private int[][] offsets = new int[2][];
  83: 
  84:   /**
  85:    * The size requirements along the X_AXIS and Y_AXIS.
  86:    */
  87:   private SizeRequirements[] requirements = new SizeRequirements[2];
  88: 
  89:   /**
  90:    * The current span along X_AXIS or Y_AXIS.
  91:    */
  92:   private int[] span = new int[2];
  93: 
  94:   /**
  95:    * The SizeRequirements of the child views along the X_AXIS and Y_AXIS.
  96:    */
  97:   private SizeRequirements[][] childReqs = new SizeRequirements[2][];
  98: 
  99:   /**
 100:    * Creates a new <code>BoxView</code> for the given
 101:    * <code>Element</code> and axis. Valid values for the axis are
 102:    * {@link View#X_AXIS} and {@link View#Y_AXIS}.
 103:    *
 104:    * @param element the element that is rendered by this BoxView
 105:    * @param axis the axis along which the box is laid out
 106:    */
 107:   public BoxView(Element element, int axis)
 108:   {
 109:     super(element);
 110:     myAxis = axis;
 111:     layoutValid[0] = false;
 112:     layoutValid[1] = false;
 113:     span[0] = 0;
 114:     span[1] = 0;
 115:     requirements[0] = new SizeRequirements();
 116:     requirements[1] = new SizeRequirements();
 117: 
 118:     // Initialize the cache arrays.
 119:     spans[0] = new int[0];
 120:     spans[1] = new int[0];
 121:     offsets[0] = new int[0];
 122:     offsets[1] = new int[0];
 123:   }
 124: 
 125:   /**
 126:    * Returns the axis along which this <code>BoxView</code> is laid out.
 127:    *
 128:    * @return the axis along which this <code>BoxView</code> is laid out
 129:    *
 130:    * @since 1.3
 131:    */
 132:   public int getAxis()
 133:   {
 134:     return myAxis;
 135:   }
 136: 
 137:   /**
 138:    * Sets the axis along which this <code>BoxView</code> is laid out.
 139:    *
 140:    * Valid values for the axis are {@link View#X_AXIS} and
 141:    * {@link View#Y_AXIS}.
 142:    *
 143:    * @param axis the axis along which this <code>BoxView</code> is laid out
 144:    *
 145:    * @since 1.3
 146:    */
 147:   public void setAxis(int axis)
 148:   {
 149:     myAxis = axis;
 150:   }
 151: 
 152:   /**
 153:    * Marks the layout along the specified axis as invalid. This is triggered
 154:    * automatically when any of the child view changes its preferences
 155:    * via {@link #preferenceChanged(View, boolean, boolean)}.
 156:    *
 157:    * The layout will be updated the next time when 
 158:    * {@link #setSize(float, float)} is called, typically from within the 
 159:    * {@link #paint(Graphics, Shape)} method.
 160:    *
 161:    * Valid values for the axis are {@link View#X_AXIS} and
 162:    * {@link View#Y_AXIS}.
 163:    *
 164:    * @param axis an <code>int</code> value
 165:    *
 166:    * @since 1.3
 167:    */
 168:   public void layoutChanged(int axis)
 169:   {
 170:     if (axis != X_AXIS && axis != Y_AXIS)
 171:       throw new IllegalArgumentException("Invalid axis parameter.");
 172:     layoutValid[axis] = false;
 173:   }
 174: 
 175:   /**
 176:    * Returns <code>true</code> if the layout along the specified
 177:    * <code>axis</code> is valid, <code>false</code> otherwise.
 178:    *
 179:    * Valid values for the axis are {@link View#X_AXIS} and
 180:    * {@link View#Y_AXIS}.
 181:    *
 182:    * @param axis the axis
 183:    *
 184:    * @return <code>true</code> if the layout along the specified
 185:    *         <code>axis</code> is valid, <code>false</code> otherwise
 186:    *
 187:    * @since 1.4
 188:    */
 189:   protected boolean isLayoutValid(int axis)
 190:   {
 191:     if (axis != X_AXIS && axis != Y_AXIS)
 192:       throw new IllegalArgumentException("Invalid axis parameter.");
 193:     return layoutValid[axis];
 194:   }
 195: 
 196:   /**
 197:    * Paints the child <code>View</code> at the specified <code>index</code>.
 198:    * This method modifies the actual values in <code>alloc</code> so make
 199:    * sure you have a copy of the original values if you need them.
 200:    *
 201:    * @param g the <code>Graphics</code> context to paint to
 202:    * @param alloc the allocated region for the child to paint into
 203:    * @param index the index of the child to be painted
 204:    *
 205:    * @see #childAllocation(int, Rectangle)
 206:    */
 207:   protected void paintChild(Graphics g, Rectangle alloc, int index)
 208:   {
 209:     View child = getView(index);
 210:     child.paint(g, alloc);
 211:   }
 212: 
 213:   /**
 214:    * Replaces child views by some other child views. If there are no views to
 215:    * remove (<code>length == 0</code>), the result is a simple insert, if
 216:    * there are no children to add (<code>view == null</code>) the result
 217:    * is a simple removal.
 218:    *
 219:    * In addition this invalidates the layout and resizes the internal cache
 220:    * for the child allocations. The old children's cached allocations can
 221:    * still be accessed (although they are not guaranteed to be valid), and
 222:    * the new children will have an initial offset and span of 0.
 223:    *
 224:    * @param offset the start offset from where to remove children
 225:    * @param length the number of children to remove
 226:    * @param views the views that replace the removed children
 227:    */
 228:   public void replace(int offset, int length, View[] views)
 229:   {
 230:     int numViews = 0;
 231:     if (views != null)
 232:       numViews = views.length;
 233: 
 234:     // Resize and copy data for cache arrays.
 235:     // The spansX cache.
 236:     int oldSize = getViewCount();
 237: 
 238:     int[] newSpansX = new int[oldSize - length + numViews];
 239:     System.arraycopy(spans[X_AXIS], 0, newSpansX, 0, offset);
 240:     System.arraycopy(spans[X_AXIS], offset + length, newSpansX,
 241:                      offset + numViews,
 242:                      oldSize - (offset + length));
 243:     spans[X_AXIS] = newSpansX;
 244: 
 245:     // The spansY cache.
 246:     int[] newSpansY = new int[oldSize - length + numViews];
 247:     System.arraycopy(spans[Y_AXIS], 0, newSpansY, 0, offset);
 248:     System.arraycopy(spans[Y_AXIS], offset + length, newSpansY,
 249:                      offset + numViews,
 250:                      oldSize - (offset + length));
 251:     spans[Y_AXIS] = newSpansY;
 252: 
 253:     // The offsetsX cache.
 254:     int[] newOffsetsX = new int[oldSize - length + numViews];
 255:     System.arraycopy(offsets[X_AXIS], 0, newOffsetsX, 0, offset);
 256:     System.arraycopy(offsets[X_AXIS], offset + length, newOffsetsX,
 257:                      offset + numViews,
 258:                      oldSize - (offset + length));
 259:     offsets[X_AXIS] = newOffsetsX;
 260: 
 261:     // The offsetsY cache.
 262:     int[] newOffsetsY = new int[oldSize - length + numViews];
 263:     System.arraycopy(offsets[Y_AXIS], 0, newOffsetsY, 0, offset);
 264:     System.arraycopy(offsets[Y_AXIS], offset + length, newOffsetsY,
 265:                      offset + numViews,
 266:                      oldSize - (offset + length));
 267:     offsets[Y_AXIS] = newOffsetsY;
 268: 
 269:     // Actually perform the replace.
 270:     super.replace(offset, length, views);
 271: 
 272:     // Invalidate layout information.
 273:     layoutValid[X_AXIS] = false;
 274:     requirementsValid[X_AXIS] = false;
 275:     layoutValid[Y_AXIS] = false;
 276:     requirementsValid[Y_AXIS] = false;
 277:   }
 278: 
 279:   /**
 280:    * Renders the <code>Element</code> that is associated with this
 281:    * <code>View</code>.
 282:    *
 283:    * @param g the <code>Graphics</code> context to render to
 284:    * @param a the allocated region for the <code>Element</code>
 285:    */
 286:   public void paint(Graphics g, Shape a)
 287:   {
 288:     Rectangle alloc;
 289:     if (a instanceof Rectangle)
 290:       alloc = (Rectangle) a;
 291:     else
 292:       alloc = a.getBounds();
 293: 
 294:     int x = alloc.x + getLeftInset();
 295:     int y = alloc.y + getTopInset();
 296: 
 297:     Rectangle clip = g.getClipBounds();
 298:     Rectangle tmp = new Rectangle();
 299:     int count = getViewCount();
 300:     for (int i = 0; i < count; ++i)
 301:       {
 302:         tmp.x = x + getOffset(X_AXIS, i);
 303:         tmp.y = y + getOffset(Y_AXIS, i);
 304:         tmp.width = getSpan(X_AXIS, i);
 305:         tmp.height = getSpan(Y_AXIS, i);
 306:         if (tmp.intersects(clip))
 307:           paintChild(g, tmp, i);
 308:       }
 309:   }
 310: 
 311:   /**
 312:    * Returns the preferred span of the content managed by this
 313:    * <code>View</code> along the specified <code>axis</code>.
 314:    *
 315:    * @param axis the axis
 316:    *
 317:    * @return the preferred span of this <code>View</code>.
 318:    */
 319:   public float getPreferredSpan(int axis)
 320:   {
 321:     updateRequirements(axis);
 322:     // Add margin.
 323:     float margin;
 324:     if (axis == X_AXIS)
 325:       margin = getLeftInset() + getRightInset();
 326:     else
 327:       margin = getTopInset() + getBottomInset();
 328:     return requirements[axis].preferred + margin;
 329:   }
 330: 
 331:   /**
 332:    * Returns the maximum span of this view along the specified axis.
 333:    * This returns <code>Integer.MAX_VALUE</code> for the minor axis
 334:    * and the preferred span for the major axis.
 335:    *
 336:    * @param axis the axis
 337:    *
 338:    * @return the maximum span of this view along the specified axis
 339:    */
 340:   public float getMaximumSpan(int axis)
 341:   {
 342:     updateRequirements(axis);
 343:     // Add margin.
 344:     float margin;
 345:     if (axis == X_AXIS)
 346:       margin = getLeftInset() + getRightInset();
 347:     else
 348:       margin = getTopInset() + getBottomInset();
 349:     return requirements[axis].maximum + margin;
 350:   }
 351: 
 352:   /**
 353:    * Returns the minimum span of this view along the specified axis.
 354:    * This calculates the minimum span using
 355:    * {@link #calculateMajorAxisRequirements} or
 356:    * {@link #calculateMinorAxisRequirements} (depending on the axis) and
 357:    * returns the resulting minimum span.
 358:    *
 359:    * @param axis the axis
 360:    *
 361:    * @return the minimum span of this view along the specified axis
 362:    */
 363:   public float getMinimumSpan(int axis)
 364:   {
 365:     updateRequirements(axis);
 366:     // Add margin.
 367:     float margin;
 368:     if (axis == X_AXIS)
 369:       margin = getLeftInset() + getRightInset();
 370:     else
 371:       margin = getTopInset() + getBottomInset();
 372:     return requirements[axis].minimum + margin;
 373:   }
 374: 
 375:   /**
 376:    * This method is obsolete and no longer in use. It is replaced by
 377:    * {@link #calculateMajorAxisRequirements(int, SizeRequirements)} and
 378:    * {@link #calculateMinorAxisRequirements(int, SizeRequirements)}.
 379:    *
 380:    * @param axis the axis that is examined
 381:    * @param sr the <code>SizeRequirements</code> object to hold the result,
 382:    *        if <code>null</code>, a new one is created
 383:    *
 384:    * @return the size requirements for this <code>BoxView</code> along
 385:    *         the specified axis
 386:    */
 387:   protected SizeRequirements baselineRequirements(int axis,
 388:                                                   SizeRequirements sr)
 389:   {
 390:     updateChildRequirements(axis);
 391: 
 392:     SizeRequirements res = sr;
 393:     if (res == null)
 394:       res = new SizeRequirements();
 395: 
 396:     float minLeft = 0;
 397:     float minRight = 0;
 398:     float prefLeft = 0;
 399:     float prefRight = 0;
 400:     float maxLeft = 0;
 401:     float maxRight = 0;
 402:     for (int i = 0; i < childReqs[axis].length; i++)
 403:       {
 404:         float myMinLeft = childReqs[axis][i].minimum * childReqs[axis][i].alignment;
 405:         float myMinRight = childReqs[axis][i].minimum - myMinLeft;
 406:         minLeft = Math.max(myMinLeft, minLeft);
 407:         minRight = Math.max(myMinRight, minRight);
 408:         float myPrefLeft = childReqs[axis][i].preferred * childReqs[axis][i].alignment;
 409:         float myPrefRight = childReqs[axis][i].preferred - myPrefLeft;
 410:         prefLeft = Math.max(myPrefLeft, prefLeft);
 411:         prefRight = Math.max(myPrefRight, prefRight);
 412:         float myMaxLeft = childReqs[axis][i].maximum * childReqs[axis][i].alignment;
 413:         float myMaxRight = childReqs[axis][i].maximum - myMaxLeft;
 414:         maxLeft = Math.max(myMaxLeft, maxLeft);
 415:         maxRight = Math.max(myMaxRight, maxRight);
 416:       }
 417:     int minSize = (int) (minLeft + minRight);
 418:     int prefSize = (int) (prefLeft + prefRight);
 419:     int maxSize = (int) (maxLeft + maxRight);
 420:     float align = prefLeft / (prefRight + prefLeft);
 421:     if (Float.isNaN(align))
 422:       align = 0;
 423: 
 424:     res.alignment = align;
 425:     res.maximum = maxSize;
 426:     res.preferred = prefSize;
 427:     res.minimum = minSize;
 428:     return res;
 429:   }
 430: 
 431:   /**
 432:    * Calculates the layout of the children of this <code>BoxView</code> along
 433:    * the specified axis.
 434:    *
 435:    * @param span the target span
 436:    * @param axis the axis that is examined
 437:    * @param offsets an empty array, filled with the offsets of the children
 438:    * @param spans an empty array, filled with the spans of the children
 439:    */
 440:   protected void baselineLayout(int span, int axis, int[] offsets,
 441:                                 int[] spans)
 442:   {
 443:     updateChildRequirements(axis);
 444:     updateRequirements(axis);
 445: 
 446:     // Calculate the spans and offsets using the SizeRequirements uility
 447:     // methods.
 448:     SizeRequirements.calculateAlignedPositions(span, requirements[axis],
 449:                                                childReqs[axis], offsets, spans);
 450:   }
 451: 
 452:   /**
 453:    * Calculates the size requirements of this <code>BoxView</code> along
 454:    * its major axis, that is the axis specified in the constructor.
 455:    *
 456:    * @param axis the axis that is examined
 457:    * @param sr the <code>SizeRequirements</code> object to hold the result,
 458:    *        if <code>null</code>, a new one is created
 459:    *
 460:    * @return the size requirements for this <code>BoxView</code> along
 461:    *         the specified axis
 462:    */
 463:   protected SizeRequirements calculateMajorAxisRequirements(int axis,
 464:                                                            SizeRequirements sr)
 465:   {
 466:     SizeRequirements res = sr;
 467:     if (res == null)
 468:       res = new SizeRequirements();
 469: 
 470:     float min = 0;
 471:     float pref = 0;
 472:     float max = 0;
 473: 
 474:     int n = getViewCount();
 475:     for (int i = 0; i < n; i++)
 476:       {
 477:         View child = getView(i);
 478:         min += child.getMinimumSpan(axis);
 479:         pref = child.getPreferredSpan(axis);
 480:         max = child.getMaximumSpan(axis);
 481:       }
 482: 
 483:     res.minimum = (int) min;
 484:     res.preferred = (int) pref;
 485:     res.maximum = (int) max;
 486:     res.alignment = 0.5F;
 487: 
 488:     return res;
 489:   }
 490: 
 491:   /**
 492:    * Calculates the size requirements of this <code>BoxView</code> along
 493:    * its minor axis, that is the axis opposite to the axis specified in the
 494:    * constructor.
 495:    *
 496:    * @param axis the axis that is examined
 497:    * @param sr the <code>SizeRequirements</code> object to hold the result,
 498:    *        if <code>null</code>, a new one is created
 499:    *
 500:    * @return the size requirements for this <code>BoxView</code> along
 501:    *         the specified axis
 502:    */
 503:   protected SizeRequirements calculateMinorAxisRequirements(int axis,
 504:                                                             SizeRequirements sr)
 505:   {
 506:     SizeRequirements res = sr;
 507:     if (res == null)
 508:       res = new SizeRequirements();
 509: 
 510:     res.minimum = 0;
 511:     res.preferred = 0;
 512:     res.maximum = 0;
 513:     res.alignment = 0.5F;
 514:     int n = getViewCount();
 515:     for (int i = 0; i < n; i++)
 516:       {
 517:         View child = getView(i);
 518:         res.minimum = Math.max((int) child.getMinimumSpan(axis), res.minimum);
 519:         res.preferred = Math.max((int) child.getPreferredSpan(axis),
 520:                                  res.preferred);
 521:         res.maximum = Math.max((int) child.getMaximumSpan(axis), res.maximum);
 522:       }
 523: 
 524:     return res;
 525:   }
 526:   
 527: 
 528:   /**
 529:    * Returns <code>true</code> if the specified point lies before the
 530:    * given <code>Rectangle</code>, <code>false</code> otherwise.
 531:    *
 532:    * &quot;Before&quot; is typically defined as being to the left or above.
 533:    *
 534:    * @param x the X coordinate of the point
 535:    * @param y the Y coordinate of the point
 536:    * @param r the rectangle to test the point against
 537:    *
 538:    * @return <code>true</code> if the specified point lies before the
 539:    *         given <code>Rectangle</code>, <code>false</code> otherwise
 540:    */
 541:   protected boolean isBefore(int x, int y, Rectangle r)
 542:   {
 543:     boolean result = false;
 544: 
 545:     if (myAxis == X_AXIS)
 546:       result = x < r.x;
 547:     else
 548:       result = y < r.y;
 549: 
 550:     return result;
 551:   }
 552: 
 553:   /**
 554:    * Returns <code>true</code> if the specified point lies after the
 555:    * given <code>Rectangle</code>, <code>false</code> otherwise.
 556:    *
 557:    * &quot;After&quot; is typically defined as being to the right or below.
 558:    *
 559:    * @param x the X coordinate of the point
 560:    * @param y the Y coordinate of the point
 561:    * @param r the rectangle to test the point against
 562:    *
 563:    * @return <code>true</code> if the specified point lies after the
 564:    *         given <code>Rectangle</code>, <code>false</code> otherwise
 565:    */
 566:   protected boolean isAfter(int x, int y, Rectangle r)
 567:   {
 568:     boolean result = false;
 569: 
 570:     if (myAxis == X_AXIS)
 571:       result = x > r.x;
 572:     else
 573:       result = y > r.y;
 574: 
 575:     return result;
 576:   }
 577: 
 578:   /**
 579:    * Returns the child <code>View</code> at the specified location.
 580:    *
 581:    * @param x the X coordinate
 582:    * @param y the Y coordinate
 583:    * @param r the inner allocation of this <code>BoxView</code> on entry,
 584:    *        the allocation of the found child on exit
 585:    *
 586:    * @return the child <code>View</code> at the specified location
 587:    */
 588:   protected View getViewAtPoint(int x, int y, Rectangle r)
 589:   {
 590:     View result = null;
 591:     int count = getViewCount();
 592:     Rectangle copy = new Rectangle(r);
 593: 
 594:     for (int i = 0; i < count; ++i)
 595:       {
 596:         copy.setBounds(r);
 597:         // The next call modifies copy.
 598:         childAllocation(i, copy);
 599:         if (copy.contains(x, y))
 600:           {
 601:             // Modify r on success.
 602:             r.setBounds(copy);
 603:             result = getView(i);
 604:             break;
 605:           }
 606:       }
 607:     
 608:     if (result == null && count > 0)
 609:       return getView(count - 1);
 610:     return result;
 611:   }
 612: 
 613:   /**
 614:    * Computes the allocation for a child <code>View</code>. The parameter
 615:    * <code>a</code> stores the allocation of this <code>CompositeView</code>
 616:    * and is then adjusted to hold the allocation of the child view.
 617:    * 
 618:    * @param index
 619:    *          the index of the child <code>View</code>
 620:    * @param a
 621:    *          the allocation of this <code>CompositeView</code> before the
 622:    *          call, the allocation of the child on exit
 623:    */
 624:   protected void childAllocation(int index, Rectangle a)
 625:   {
 626:     if (! isAllocationValid())
 627:       layout(a.width, a.height);
 628: 
 629:     a.x += offsets[X_AXIS][index];
 630:     a.y += offsets[Y_AXIS][index];
 631:     a.width = spans[X_AXIS][index];
 632:     a.height = spans[Y_AXIS][index];
 633:   }
 634: 
 635:   /**
 636:    * Lays out the children of this <code>BoxView</code> with the specified
 637:    * bounds.
 638:    *
 639:    * @param width the width of the allocated region for the children (that
 640:    *        is the inner allocation of this <code>BoxView</code>
 641:    * @param height the height of the allocated region for the children (that
 642:    *        is the inner allocation of this <code>BoxView</code>
 643:    */
 644:   protected void layout(int width, int height)
 645:   {
 646:     int[] newSpan = new int[]{ width, height };
 647:     int count = getViewCount();
 648: 
 649:     // Update minor axis as appropriate. We need to first update the minor
 650:     // axis layout because that might affect the children's preferences along
 651:     // the major axis.
 652:     int minorAxis = myAxis == X_AXIS ? Y_AXIS : X_AXIS;
 653:     if ((! isLayoutValid(minorAxis)) || newSpan[minorAxis] != span[minorAxis])
 654:       {
 655:         layoutValid[minorAxis] = false;
 656:         span[minorAxis] = newSpan[minorAxis];
 657:         layoutMinorAxis(span[minorAxis], minorAxis, offsets[minorAxis],
 658:                         spans[minorAxis]);
 659: 
 660:         // Update the child view's sizes.
 661:         for (int i = 0; i < count; ++i)
 662:           {
 663:             getView(i).setSize(spans[X_AXIS][i], spans[Y_AXIS][i]);
 664:           }
 665:         layoutValid[minorAxis] = true;
 666:       }
 667: 
 668: 
 669:     // Update major axis as appropriate.
 670:     if ((! isLayoutValid(myAxis)) || newSpan[myAxis] != span[myAxis])
 671:       {
 672:         layoutValid[myAxis] = false;
 673:         span[myAxis] = newSpan[myAxis];
 674:         layoutMajorAxis(span[myAxis], myAxis, offsets[myAxis],
 675:                         spans[myAxis]);
 676: 
 677:         // Update the child view's sizes.
 678:         for (int i = 0; i < count; ++i)
 679:           {
 680:             getView(i).setSize(spans[X_AXIS][i], spans[Y_AXIS][i]);
 681:           }
 682:         layoutValid[myAxis] = true;
 683:       }
 684: 
 685:     if (layoutValid[myAxis] == false)
 686:       System.err.println("WARNING: Major axis layout must be valid after layout");
 687:     if (layoutValid[minorAxis] == false)
 688:       System.err.println("Minor axis layout must be valid after layout");
 689:   }
 690: 
 691:   /**
 692:    * Performs the layout along the major axis of a <code>BoxView</code>.
 693:    *
 694:    * @param targetSpan the (inner) span of the <code>BoxView</code> in which
 695:    *        to layout the children
 696:    * @param axis the axis along which the layout is performed
 697:    * @param offsets the array that holds the offsets of the children on exit
 698:    * @param spans the array that holds the spans of the children on exit
 699:    */
 700:   protected void layoutMajorAxis(int targetSpan, int axis, int[] offsets,
 701:                                  int[] spans)
 702:   {
 703:     // Set the spans to the preferred sizes. Determine the space
 704:     // that we have to adjust the sizes afterwards.
 705:     long sumPref = 0;
 706:     int n = getViewCount();
 707:     for (int i = 0; i < n; i++)
 708:       {
 709:         View child = getView(i);
 710:         spans[i] = (int) child.getPreferredSpan(axis);
 711:         sumPref = spans[i];
 712:       }
 713: 
 714:     // Try to adjust the spans so that we fill the targetSpan.
 715:     long diff = targetSpan - sumPref;
 716:     float factor = 0.0F;
 717:     int[] diffs = null;
 718:     if (diff != 0)
 719:       {
 720:         long total = 0;
 721:         diffs = new int[n];
 722:         for (int i = 0; i < n; i++)
 723:           {
 724:             View child = getView(i);
 725:             int span;
 726:             if (diff < 0)
 727:               {
 728:                 span = (int) child.getMinimumSpan(axis);
 729:                 diffs[i] = spans[i] - span;
 730:               }
 731:             else
 732:               {
 733:                 span = (int) child.getMaximumSpan(axis);
 734:                 diffs[i] = span - spans[i];
 735:               }
 736:             total += span;
 737:           }
 738: 
 739:         float maxAdjust = Math.abs(total - sumPref);
 740:         factor = diff / maxAdjust;
 741:         factor = Math.min(factor, 1.0F);
 742:         factor = Math.max(factor, -1.0F);
 743:       }
 744: 
 745:     // Actually perform adjustments.
 746:     int totalOffs = 0;
 747:     for (int i = 0; i < n; i++)
 748:       {
 749:         offsets[i] = totalOffs;
 750:         if (diff != 0)
 751:           {
 752:             float adjust = factor * diffs[i];
 753:             spans[i] += Math.round(adjust);
 754:           }
 755:         // Avoid overflow here.
 756:         totalOffs = (int) Math.min((long) totalOffs + (long) spans[i],
 757:                                     Integer.MAX_VALUE);
 758:       }
 759:   }
 760: 
 761:   /**
 762:    * Performs the layout along the minor axis of a <code>BoxView</code>.
 763:    *
 764:    * @param targetSpan the (inner) span of the <code>BoxView</code> in which
 765:    *        to layout the children
 766:    * @param axis the axis along which the layout is performed
 767:    * @param offsets the array that holds the offsets of the children on exit
 768:    * @param spans the array that holds the spans of the children on exit
 769:    */
 770:   protected void layoutMinorAxis(int targetSpan, int axis, int[] offsets,
 771:                                  int[] spans)
 772:   {
 773:     int count = getViewCount();
 774:     for (int i = 0; i < count; i++)
 775:       {
 776:         View child = getView(i);
 777:         int max = (int) child.getMaximumSpan(axis);
 778:         if (max < targetSpan)
 779:           {System.err.println("align: " + child);
 780:             // Align child when it can't be made as wide as the target span.
 781:             float align = child.getAlignment(axis);
 782:             offsets[i] = (int) ((targetSpan - max) * align);
 783:             spans[i] = max;
 784:           }
 785:         else
 786:           {
 787:             // Expand child to target width if possible.
 788:             int min = (int) child.getMinimumSpan(axis);
 789:             offsets[i] = 0;
 790:             spans[i] = Math.max(min, targetSpan);
 791:           }
 792:       }
 793:   }
 794: 
 795:   /**
 796:    * Returns <code>true</code> if the cached allocations for the children
 797:    * are still valid, <code>false</code> otherwise.
 798:    *
 799:    * @return <code>true</code> if the cached allocations for the children
 800:    *         are still valid, <code>false</code> otherwise
 801:    */
 802:   protected boolean isAllocationValid()
 803:   {
 804:     return isLayoutValid(X_AXIS) && isLayoutValid(Y_AXIS);
 805:   }
 806: 
 807:   /**
 808:    * Return the current width of the box. This is the last allocated width.
 809:    *
 810:    * @return the current width of the box
 811:    */
 812:   public int getWidth()
 813:   {
 814:     return span[X_AXIS];
 815:   }
 816: 
 817:   /**
 818:    * Return the current height of the box. This is the last allocated height.
 819:    *
 820:    * @return the current height of the box
 821:    */
 822:   public int getHeight()
 823:   {
 824:     return span[Y_AXIS];
 825:   }
 826: 
 827:   /**
 828:    * Sets the size of the view. If the actual size has changed, the layout
 829:    * is updated accordingly.
 830:    *
 831:    * @param width the new width
 832:    * @param height the new height
 833:    */
 834:   public void setSize(float width, float height)
 835:   {
 836:     layout((int) width, (int) height);
 837:   }
 838: 
 839:   /**
 840:    * Returns the span for the child view with the given index for the specified
 841:    * axis.
 842:    *
 843:    * @param axis the axis to examine, either <code>X_AXIS</code> or
 844:    *        <code>Y_AXIS</code>
 845:    * @param childIndex the index of the child for for which to return the span
 846:    *
 847:    * @return the span for the child view with the given index for the specified
 848:    *         axis
 849:    */
 850:   protected int getSpan(int axis, int childIndex)
 851:   {
 852:     if (axis != X_AXIS && axis != Y_AXIS)
 853:       throw new IllegalArgumentException("Illegal axis argument");
 854:     return spans[axis][childIndex];
 855:   }
 856: 
 857:   /**
 858:    * Returns the offset for the child view with the given index for the
 859:    * specified axis.
 860:    *
 861:    * @param axis the axis to examine, either <code>X_AXIS</code> or
 862:    *        <code>Y_AXIS</code>
 863:    * @param childIndex the index of the child for for which to return the span
 864:    *
 865:    * @return the offset for the child view with the given index for the
 866:    *         specified axis
 867:    */
 868:   protected int getOffset(int axis, int childIndex)
 869:   {
 870:     if (axis != X_AXIS && axis != Y_AXIS)
 871:       throw new IllegalArgumentException("Illegal axis argument");
 872:     return offsets[axis][childIndex];
 873:   }
 874: 
 875:   /**
 876:    * Returns the alignment for this box view for the specified axis. The
 877:    * axis that is tiled (the major axis) will be requested to be aligned
 878:    * centered (0.5F). The minor axis alignment depends on the child view's
 879:    * total alignment.
 880:    *
 881:    * @param axis the axis which is examined
 882:    *
 883:    * @return the alignment for this box view for the specified axis
 884:    */
 885:   public float getAlignment(int axis)
 886:   {
 887:      updateRequirements(axis);
 888:      return requirements[axis].alignment;
 889:   }
 890:   
 891:   /**
 892:    * Called by a child View when its preferred span has changed.
 893:    * 
 894:    * @param width indicates that the preferred width of the child changed.
 895:    * @param height indicates that the preferred height of the child changed.
 896:    * @param child the child View. 
 897:    */
 898:   public void preferenceChanged(View child, boolean width, boolean height)
 899:   {
 900:     if (width)
 901:       {
 902:         layoutValid[X_AXIS] = false;
 903:         requirementsValid[X_AXIS] = false;
 904:       }
 905:     if (height)
 906:       {
 907:         layoutValid[Y_AXIS] = false;
 908:         requirementsValid[Y_AXIS] = false;
 909:       }
 910:     super.preferenceChanged(child, width, height);
 911:   }
 912:   
 913:   /**
 914:    * Maps the document model position <code>pos</code> to a Shape
 915:    * in the view coordinate space.  This method overrides CompositeView's
 916:    * method to make sure the children are allocated properly before
 917:    * calling the super's behaviour.
 918:    */
 919:   public Shape modelToView(int pos, Shape a, Position.Bias bias)
 920:       throws BadLocationException
 921:   {
 922:     // Make sure everything is allocated properly and then call super
 923:     if (! isAllocationValid())
 924:       {
 925:         Rectangle bounds = a.getBounds();
 926:         setSize(bounds.width, bounds.height);
 927:       }
 928:     return super.modelToView(pos, a, bias);
 929:   }
 930: 
 931:   /**
 932:    * Returns the resize weight of this view. A value of <code>0</code> or less
 933:    * means this view is not resizeable. Positive values make the view
 934:    * resizeable. This implementation returns <code>0</code> for the major
 935:    * axis and <code>1</code> for the minor axis of this box view.
 936:    *
 937:    * @param axis the axis
 938:    *
 939:    * @return the resizability of this view along the specified axis
 940:    *
 941:    * @throws IllegalArgumentException if <code>axis</code> is invalid
 942:    */
 943:   public int getResizeWeight(int axis)
 944:   {
 945:     if (axis != X_AXIS && axis != Y_AXIS)
 946:       throw new IllegalArgumentException("Illegal axis argument");
 947:     int weight = 1;
 948:     if (axis == myAxis)
 949:       weight = 0;
 950:     return weight;
 951:   }
 952: 
 953:   /**
 954:    * Returns the child allocation for the child view with the specified
 955:    * <code>index</code>. If the layout is invalid, this returns
 956:    * <code>null</code>.
 957:    *
 958:    * @param index the child view index
 959:    * @param a the allocation to this view
 960:    *
 961:    * @return the child allocation for the child view with the specified
 962:    *         <code>index</code> or <code>null</code> if the layout is invalid
 963:    *         or <code>a</code> is null
 964:    */
 965:   public Shape getChildAllocation(int index, Shape a)
 966:   {
 967:     Shape ret = null;
 968:     if (isAllocationValid() && a != null)
 969:       ret = super.getChildAllocation(index, a);
 970:     return ret;
 971:   }
 972: 
 973:   protected void forwardUpdate(DocumentEvent.ElementChange ec, DocumentEvent e,
 974:                                Shape a, ViewFactory vf)
 975:   {
 976:     // FIXME: What to do here?
 977:     super.forwardUpdate(ec, e, a, vf);
 978:   }
 979: 
 980:   public int viewToModel(float x, float y, Shape a, Position.Bias[] bias)
 981:   {
 982:     // FIXME: What to do here?
 983:     return super.viewToModel(x, y, a, bias);
 984:   }
 985: 
 986:   protected boolean flipEastAndWestAtEnds(int position, Position.Bias bias)
 987:   {
 988:     // FIXME: What to do here?
 989:     return super.flipEastAndWestAtEnds(position, bias);
 990:   }
 991: 
 992:   /**
 993:    * Updates the child requirements along the specified axis. The requirements
 994:    * are only updated if the layout for the specified axis is marked as
 995:    * invalid.
 996:    *
 997:    * @param axis the axis to be updated
 998:    */
 999:   private void updateChildRequirements(int axis)
1000:   {
1001:     if (! isLayoutValid(axis))
1002:       {
1003:         int numChildren = getViewCount();
1004:         if (childReqs[axis] == null || childReqs[axis].length != numChildren)
1005:           childReqs[axis] = new SizeRequirements[numChildren];
1006:         for (int i = 0; i < numChildren; ++i)
1007:           {
1008:             View child = getView(i);
1009:             childReqs[axis][i] =
1010:               new SizeRequirements((int) child.getMinimumSpan(axis),
1011:                                    (int) child.getPreferredSpan(axis),
1012:                                    (int) child.getMaximumSpan(axis),
1013:                                    child.getAlignment(axis));
1014:           }
1015:       }
1016:   }
1017: 
1018:   /**
1019:    * Updates the view's cached requirements along the specified axis if
1020:    * necessary. The requirements are only updated if the layout for the
1021:    * specified axis is marked as invalid.
1022:    *
1023:    * @param axis the axis
1024:    */
1025:   private void updateRequirements(int axis)
1026:   {
1027:     if (! requirementsValid[axis])
1028:       {
1029:         if (axis == myAxis)
1030:           requirements[axis] = calculateMajorAxisRequirements(axis,
1031:                                                            requirements[axis]);
1032:         else
1033:           requirements[axis] = calculateMinorAxisRequirements(axis,
1034:                                                            requirements[axis]);
1035:         requirementsValid[axis] = true;
1036:       }
1037:   }
1038: }