Source for javax.swing.SizeRequirements

   1: /* SizeRequirements.java --
   2:    Copyright (C) 2002, 2005 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: package javax.swing;
  39: 
  40: import java.io.Serializable;
  41: 
  42: /**
  43:  * This class calculates information about the size and position requirements
  44:  * of components.
  45:  *
  46:  * Two types of layout are supported:
  47:  * <ul>
  48:  * <li>Tiled: the components are placed at position top-left or bottom-right
  49:  *    position within their allocated space</li>
  50:  * <li>Aligned: the components are placed aligned in their allocated space
  51:  *    according to their alignment value</li>
  52:  * </ul>
  53:  *
  54:  * @author Andrew Selkirk
  55:  * @author Roman Kennke (roman@kennke.org)
  56:  */
  57: public class SizeRequirements implements Serializable
  58: {
  59:   /**
  60:    * The serialVersionUID.
  61:    */
  62:   private static final long serialVersionUID = 9217749429906736553L;
  63: 
  64:   /**
  65:    * The minimum reasonable width or height of a component.
  66:    */
  67:   public int minimum;
  68: 
  69:   /**
  70:    * The preferred width or height of a component.
  71:    */
  72:   public int preferred;
  73: 
  74:   /**
  75:    * The maximum reasonable width or height of a component.
  76:    */
  77:   public int maximum;
  78: 
  79:   /**
  80:    * The horizontal or vertical alignment of a component.
  81:    */
  82:   public float alignment;
  83: 
  84:   /**
  85:    * Creates a SizeRequirements object with minimum, preferred and
  86:    * maximum size set to zero, and an alignment value of 0.5.
  87:    */
  88:   public SizeRequirements()
  89:   {
  90:     this (0, 0, 0, 0.5F);
  91:   }
  92: 
  93:   /**
  94:    * Creates a SizeRequirements object with the specified minimum,
  95:    * preferred, maximum and alignment values.
  96:    *
  97:    * @param min the minimum reasonable size of the component
  98:    * @param pref the preferred size of the component
  99:    * @param max the maximum size of the component
 100:    * @param align the alignment of the component
 101:    */
 102:   public SizeRequirements(int min, int pref, int max, float align)
 103:   {
 104:     minimum = min;
 105:     preferred = pref;
 106:     maximum = max;
 107:     alignment = align;
 108:   }
 109: 
 110:   /**
 111:    * Returns a String representation of this SizeRequirements object,
 112:    * containing information about the minimum, preferred, maximum and
 113:    * alignment value.
 114:    *
 115:    * @return a String representation of this SizeRequirements object
 116:    */
 117:   public String toString()
 118:   {
 119:     StringBuilder b = new StringBuilder();
 120:     b.append("<[");
 121:     b.append(minimum);
 122:     b.append(',');
 123:     b.append(preferred);
 124:     b.append(',');
 125:     b.append(maximum);
 126:     b.append("]@");
 127:     b.append(alignment);
 128:     b.append('>');
 129:     return b.toString();
 130:   }
 131: 
 132:   /**
 133:    * Calculates how much space is nessecary to place a set of components
 134:    * end-to-end. The size requirements of the components is specified
 135:    * in <code>children</code>.
 136:    *
 137:    * @param children the SizeRequirements of each of the components
 138:    *
 139:    * @return the SizeRequirements that describe how much space is needed
 140:    *     to place the components end-to-end
 141:    */
 142:   public static SizeRequirements
 143:   getTiledSizeRequirements(SizeRequirements[] children)
 144:   {
 145:     long minimum = 0;
 146:     long preferred = 0;
 147:     long maximum = 0;
 148:     for (int i = 0; i < children.length; i++)
 149:       {
 150:         minimum += children[i].minimum;
 151:         preferred += children[i].preferred;
 152:         maximum += children[i].maximum;
 153:       }
 154:     // Overflow check.
 155:     if (minimum > Integer.MAX_VALUE)
 156:       minimum = Integer.MAX_VALUE;
 157:     if (preferred > Integer.MAX_VALUE)
 158:       preferred = Integer.MAX_VALUE;
 159:     if (maximum > Integer.MAX_VALUE)
 160:       maximum = Integer.MAX_VALUE;
 161:     SizeRequirements result = new SizeRequirements((int) minimum,
 162:                                                    (int) preferred,
 163:                                                    (int) maximum,
 164:                                                    0.5F);
 165:     return result;
 166:   }
 167: 
 168:   /**
 169:    * Calculates how much space is nessecary to place a set of components
 170:    * aligned according to their alignment value.
 171:    * The size requirements of the components is specified in
 172:    * <code>children</code>.
 173:    *
 174:    * @param children the SizeRequirements of each of the components
 175:    *
 176:    * @return the SizeRequirements that describe how much space is needed
 177:    *     to place the components aligned
 178:    */
 179:   public static SizeRequirements
 180:   getAlignedSizeRequirements(SizeRequirements[] children)
 181:   {
 182:     float minLeft = 0;
 183:     float minRight = 0;
 184:     float prefLeft = 0;
 185:     float prefRight = 0;
 186:     float maxLeft = 0;
 187:     float maxRight = 0;
 188:     for (int i = 0; i < children.length; i++)
 189:       {
 190:         float myMinLeft = children[i].minimum * children[i].alignment;
 191:         float myMinRight = children[i].minimum - myMinLeft;
 192:         minLeft = Math.max(myMinLeft, minLeft);
 193:         minRight = Math.max(myMinRight, minRight);
 194:         float myPrefLeft = children[i].preferred * children[i].alignment;
 195:         float myPrefRight = children[i].preferred - myPrefLeft;
 196:         prefLeft = Math.max(myPrefLeft, prefLeft);
 197:         prefRight = Math.max(myPrefRight, prefRight);
 198:         float myMaxLeft = children[i].maximum * children[i].alignment;
 199:         float myMaxRight = children[i].maximum - myMaxLeft;
 200:         maxLeft = Math.max(myMaxLeft, maxLeft);
 201:         maxRight = Math.max(myMaxRight, maxRight);
 202:       }
 203:     int minSize = (int) (minLeft + minRight);
 204:     int prefSize = (int) (prefLeft + prefRight);
 205:     int maxSize = (int) (maxLeft + maxRight);
 206:     float align = prefLeft / (prefRight + prefLeft);
 207:     if (Float.isNaN(align))
 208:       align = 0;
 209:     return new SizeRequirements(minSize, prefSize, maxSize, align);
 210:   }
 211: 
 212:   /**
 213:    * Calculate the offsets and spans of the components, when they should
 214:    * be placed end-to-end.
 215:    *
 216:    * You must specify the amount of allocated space in
 217:    * <code>allocated</code>, the total size requirements of the set of
 218:    * components in <code>total</code> (this can be calculated using
 219:    * {@link #getTiledSizeRequirements} and the size requirements of the
 220:    * components in <code>children</code>.
 221:    *
 222:    * The calculated offset and span values for each component are then
 223:    * stored in the arrays <code>offsets</code> and <code>spans</code>.
 224:    *
 225:    * The components are placed in the forward direction, beginning with
 226:    * an offset of 0.
 227:    *
 228:    * @param allocated the amount of allocated space
 229:    * @param total the total size requirements of the components
 230:    * @param children the size requirement of each component
 231:    * @param offsets will hold the offset values for each component
 232:    * @param spans will hold the span values for each component
 233:    */
 234:   public static void calculateTiledPositions(int allocated,
 235:                                              SizeRequirements total,
 236:                                              SizeRequirements[] children,
 237:                                              int[] offsets, int[] spans)
 238:   {
 239:     calculateTiledPositions(allocated, total, children, offsets, spans, true);
 240:   }
 241: 
 242:   /**
 243:    * Calculate the offsets and spans of the components, when they should
 244:    * be placed end-to-end.
 245:    *
 246:    * You must specify the amount of allocated space in
 247:    * <code>allocated</code>, the total size requirements of the set of
 248:    * components in <code>total</code> (this can be calculated using
 249:    * {@link #getTiledSizeRequirements} and the size requirements of the
 250:    * components in <code>children</code>.
 251:    *
 252:    * The calculated offset and span values for each component are then
 253:    * stored in the arrays <code>offsets</code> and <code>spans</code>.
 254:    *
 255:    * Depending on the value of <code>forward</code> the components are
 256:    * placed in the forward direction (left-right or top-bottom), where
 257:    * the offsets begin with 0, or in the reverse direction
 258:    * (right-left or bottom-top).
 259:    *
 260:    * @param allocated the amount of allocated space
 261:    * @param total the total size requirements of the components
 262:    * @param children the size requirement of each component
 263:    * @param offsets will hold the offset values for each component
 264:    * @param spans will hold the span values for each component
 265:    * @param forward whether the components should be placed in the forward
 266:    *     direction (left-right or top-bottom) or reverse direction
 267:    *     (right-left or bottom-top)
 268:    */
 269:   public static void calculateTiledPositions(int allocated,
 270:                                              SizeRequirements total,
 271:                                              SizeRequirements[] children,
 272:                                              int[] offsets, int[] spans,
 273:                                              boolean forward)
 274:   {
 275:     int span = 0;
 276:     if (forward)
 277:       {
 278:         int offset = 0;
 279:         for (int i = 0; i < children.length; i++)
 280:           {
 281:             offsets[i] = offset;
 282:             spans[i] = children[i].preferred;
 283:             span += spans[i];
 284:             offset += children[i].preferred;
 285:           }
 286:       }
 287:     else
 288:       {
 289:         int offset = allocated;
 290:         for (int i = 0; i < children.length; i++)
 291:           {
 292:             offset -= children[i].preferred;
 293:             offsets[i] = offset;
 294:             span += spans[i];
 295:             spans[i] = children[i].preferred;
 296:           }
 297:       }
 298:     // Adjust spans so that we exactly fill the allocated region. If
 299:     if (span > allocated)
 300:       adjustSmaller(allocated, children, spans, span);
 301:     else if (span < allocated)
 302:       adjustGreater(allocated, children, spans, span);
 303: 
 304:     // Adjust offsets.
 305:     if (forward)
 306:       {
 307:         int offset = 0;
 308:         for (int i = 0; i < children.length; i++)
 309:           {
 310:             offsets[i] = offset;
 311:             offset += spans[i];
 312:           }
 313:       }
 314:     else
 315:       {
 316:         int offset = allocated;
 317:         for (int i = 0; i < children.length; i++)
 318:           {
 319:             offset -= spans[i];
 320:             offsets[i] = offset;
 321:           }
 322:       }
 323:   }
 324: 
 325:   private static void adjustSmaller(int allocated, SizeRequirements[] children,
 326:                                     int[] spans, int span)
 327:   {
 328:     // Sum up (prefSize - minSize) over all children
 329:     int sumDelta = 0;
 330:     for (int i = 0; i < children.length; i++)
 331:       sumDelta += children[i].preferred - children[i].minimum;
 332: 
 333:     // If we have sumDelta == 0, then all components have prefSize == maxSize
 334:     // and we can't do anything about it.
 335:     if (sumDelta == 0)
 336:       return;
 337: 
 338:     // Adjust all sizes according to their preferred and minimum sizes.
 339:     for (int i = 0; i < children.length; i++)
 340:       {
 341:         double factor = ((double) (children[i].preferred - children[i].minimum))
 342:                         / ((double) sumDelta);
 343:         // In case we have a sumDelta of 0, the factor should also be 0.
 344:         if (Double.isNaN(factor))
 345:           factor = 0;
 346:         spans[i] -= factor * (span - allocated);
 347:       }
 348:   }
 349: 
 350:   private static void adjustGreater(int allocated, SizeRequirements[] children,
 351:                                     int[] spans, int span)
 352:   {
 353:     // Sum up (maxSize - prefSize) over all children
 354:     long sumDelta = 0;
 355:     for (int i = 0; i < children.length; i++)
 356:       {
 357:         sumDelta += children[i].maximum - children[i].preferred;
 358:       }
 359: 
 360:     // If we have sumDelta == 0, then all components have prefSize == maxSize
 361:     // and we can't do anything about it.
 362:     if (sumDelta == 0)
 363:       return;
 364: 
 365:     // Adjust all sizes according to their preferred and minimum sizes.
 366:     for (int i = 0; i < children.length; i++)
 367:       {
 368:         double factor = ((double) (children[i].maximum - children[i].preferred))
 369:                         / ((double) sumDelta);
 370:         spans[i] += factor * (allocated - span);
 371:       }
 372:   }
 373: 
 374:   /**
 375:    * Calculate the offsets and spans of the components, when they should
 376:    * be placed end-to-end.
 377:    *
 378:    * You must specify the amount of allocated space in
 379:    * <code>allocated</code>, the total size requirements of the set of
 380:    * components in <code>total</code> (this can be calculated using
 381:    * {@link #getTiledSizeRequirements} and the size requirements of the
 382:    * components in <code>children</code>.
 383:    *
 384:    * The calculated offset and span values for each component are then
 385:    * stored in the arrays <code>offsets</code> and <code>spans</code>.
 386:    *
 387:    * The components are tiled in the forward direction, beginning with
 388:    * an offset of 0.
 389:    * 
 390:    * @param allocated the amount of allocated space
 391:    * @param total the total size requirements of the components
 392:    * @param children the size requirement of each component
 393:    * @param offsets will hold the offset values for each component
 394:    * @param spans will hold the span values for each component
 395:    */
 396:   public static void calculateAlignedPositions(int allocated,
 397:                                                SizeRequirements total,
 398:                                                SizeRequirements[] children,
 399:                                                int[] offsets, int[] spans)
 400:   {
 401:     calculateAlignedPositions(allocated, total, children, offsets, spans,
 402:                               true);
 403:   }
 404: 
 405:   /**
 406:    * Calculate the offsets and spans of the components, when they should
 407:    * be placed end-to-end.
 408:    *
 409:    * You must specify the amount of allocated space in
 410:    * <code>allocated</code>, the total size requirements of the set of
 411:    * components in <code>total</code> (this can be calculated using
 412:    * {@link #getTiledSizeRequirements} and the size requirements of the
 413:    * components in <code>children</code>.
 414:    *
 415:    * The calculated offset and span values for each component are then
 416:    * stored in the arrays <code>offsets</code> and <code>spans</code>.
 417:    *
 418:    * Depending on the value of <code>forward</code> the components are
 419:    * placed in the forward direction (left-right or top-bottom), where
 420:    * the offsets begin with 0, or in the reverse direction
 421:    * (right-left or bottom-top).
 422:    *
 423:    * @param allocated the amount of allocated space
 424:    * @param total the total size requirements of the components
 425:    * @param children the size requirement of each component
 426:    * @param spans will hold the span values for each component
 427:    * @param forward whether the components should be placed in the forward
 428:    *     direction (left-right or top-bottom) or reverse direction
 429:    *     (right-left or bottom-top)
 430:    */
 431:   public static void calculateAlignedPositions(int allocated,
 432:                                                SizeRequirements total,
 433:                                                SizeRequirements[] children,
 434:                                                int[] offset, int[] spans,
 435:                                                boolean forward)
 436:   {
 437:     // First we compute the position of the baseline.
 438:     float baseline = allocated * total.alignment;
 439: 
 440:     // Now we can layout the components along the baseline.
 441:     for (int i = 0; i < children.length; i++)
 442:       {
 443:         float align = children[i].alignment;
 444:         // Try to fit the component into the available space.
 445:         int[] spanAndOffset = new int[2];
 446:         if (align < .5F || baseline == 0)
 447:           adjustFromRight(children[i], baseline, allocated, spanAndOffset);
 448:         else
 449:           adjustFromLeft(children[i], baseline, allocated, spanAndOffset);
 450:         spans[i] = spanAndOffset[0];
 451:         offset[i] = spanAndOffset[1];
 452:       }
 453:   }
 454: 
 455:   /**
 456:    * Adjusts the span and offset of a component for the aligned layout.
 457:    *
 458:    * @param reqs
 459:    * @param baseline
 460:    * @param allocated
 461:    * @param spanAndOffset
 462:    */
 463:   private static void adjustFromRight(SizeRequirements reqs, float baseline,
 464:                                       int allocated, int[] spanAndOffset)
 465:   {
 466:     float right = allocated - baseline;
 467:     // If the resulting span exceeds the maximum of the component, then adjust
 468:     // accordingly.
 469:     float maxRight = ((float) reqs.maximum) * (1.F - reqs.alignment);
 470:     if (right / (1.F - reqs.alignment) > reqs.maximum)
 471:       right = maxRight;
 472:     // If we have not enough space on the left side, then adjust accordingly.
 473:     if (right / (1.F - reqs.alignment) * reqs.alignment > allocated - baseline)
 474:       right = ((float) (allocated - baseline))
 475:              / reqs.alignment * (1.F - reqs.alignment);
 476: 
 477:     spanAndOffset[0] = (int) (right / (1.F - reqs.alignment));
 478:     spanAndOffset[1] = (int) (baseline - spanAndOffset[0] * reqs.alignment);
 479:   }
 480: 
 481:   /**
 482:    * Adjusts the span and offset of a component for the aligned layout.
 483:    *
 484:    * @param reqs
 485:    * @param baseline
 486:    * @param allocated
 487:    * @param spanAndOffset
 488:    */
 489:   private static void adjustFromLeft(SizeRequirements reqs, float baseline,
 490:                                      int allocated, int[] spanAndOffset)
 491:   {
 492:     float left = baseline;
 493:     // If the resulting span exceeds the maximum of the component, then adjust
 494:     // accordingly.
 495:     float maxLeft = ((float) reqs.maximum) * reqs.alignment;
 496:     if (left / reqs.alignment > reqs.maximum)
 497:       left = maxLeft;
 498:     // If we have not enough space on the right side, then adjust accordingly.
 499:     if (left / reqs.alignment * (1.F - reqs.alignment) > allocated - baseline)
 500:       left = ((float) (allocated - baseline))
 501:              / (1.F - reqs.alignment) * reqs.alignment;
 502: 
 503:     spanAndOffset[0] = (int) (left / reqs.alignment);
 504:     spanAndOffset[1] = (int) (baseline - spanAndOffset[0] * reqs.alignment);
 505:   }
 506: 
 507:   /**
 508:    * Returns an array of new preferred sizes for the children based on
 509:    * <code>delta</code>. <code>delta</code> specifies a change in the
 510:    * allocated space. The sizes of the children will be shortened or
 511:    * lengthened to accomodate the new allocation.
 512:    *
 513:    * @param delta the change of the size of the total allocation for
 514:    *     the components
 515:    * @param children the size requirements of each component
 516:    *
 517:    * @return the new preferred sizes for each component
 518:    */
 519:   public static int[] adjustSizes(int delta, SizeRequirements[] children)
 520:   {
 521:     return null; // TODO
 522:   }
 523: }