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