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