Frames | No Frames |
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: * "Before" 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: * "After" 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: }