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:     SizeRequirements result = new SizeRequirements();
 146:     for (int i = 0; i < children.length; i++)
 147:       {
 148:         result.minimum += children[i].minimum;
 149:         result.preferred += children[i].preferred;
 150:         result.maximum += children[i].maximum;
 151:       }
 152:     return result;
 153:   }
 154: 
 155:   /**
 156:    * Calculates how much space is nessecary to place a set of components
 157:    * aligned according to their alignment value.
 158:    * The size requirements of the components is specified in
 159:    * <code>children</code>.
 160:    *
 161:    * @param children the SizeRequirements of each of the components
 162:    *
 163:    * @return the SizeRequirements that describe how much space is needed
 164:    *     to place the components aligned
 165:    */
 166:   public static SizeRequirements
 167:   getAlignedSizeRequirements(SizeRequirements[] children)
 168:   {
 169:     float minLeft = 0;
 170:     float minRight = 0;
 171:     float prefLeft = 0;
 172:     float prefRight = 0;
 173:     float maxLeft = 0;
 174:     float maxRight = 0;
 175:     for (int i = 0; i < children.length; i++)
 176:       {
 177:         float myMinLeft = children[i].minimum * children[i].alignment;
 178:         float myMinRight = children[i].minimum - myMinLeft;
 179:         minLeft = Math.max(myMinLeft, minLeft);
 180:         minRight = Math.max(myMinRight, minRight);
 181:         float myPrefLeft = children[i].preferred * children[i].alignment;
 182:         float myPrefRight = children[i].preferred - myPrefLeft;
 183:         prefLeft = Math.max(myPrefLeft, prefLeft);
 184:         prefRight = Math.max(myPrefRight, prefRight);
 185:         float myMaxLeft = children[i].maximum * children[i].alignment;
 186:         float myMaxRight = children[i].maximum - myMaxLeft;
 187:         maxLeft = Math.max(myMaxLeft, maxLeft);
 188:         maxRight = Math.max(myMaxRight, maxRight);
 189:       }
 190:     int minSize = (int) (minLeft + minRight);
 191:     int prefSize = (int) (prefLeft + prefRight);
 192:     int maxSize = (int) (maxLeft + maxRight);
 193:     float align = prefLeft / (prefRight + prefLeft);
 194:     if (Float.isNaN(align))
 195:       align = 0;
 196:     return new SizeRequirements(minSize, prefSize, maxSize, align);
 197:   }
 198: 
 199:   /**
 200:    * Calculate the offsets and spans of the components, when they should
 201:    * be placed end-to-end.
 202:    *
 203:    * You must specify the amount of allocated space in
 204:    * <code>allocated</code>, the total size requirements of the set of
 205:    * components in <code>total</code> (this can be calculated using
 206:    * {@link #getTiledSizeRequirements} and the size requirements of the
 207:    * components in <code>children</code>.
 208:    *
 209:    * The calculated offset and span values for each component are then
 210:    * stored in the arrays <code>offsets</code> and <code>spans</code>.
 211:    *
 212:    * The components are placed in the forward direction, beginning with
 213:    * an offset of 0.
 214:    *
 215:    * @param allocated the amount of allocated space
 216:    * @param total the total size requirements of the components
 217:    * @param children the size requirement of each component
 218:    * @param offsets will hold the offset values for each component
 219:    * @param spans will hold the span values for each component
 220:    */
 221:   public static void calculateTiledPositions(int allocated,
 222:                                              SizeRequirements total,
 223:                                              SizeRequirements[] children,
 224:                                              int[] offsets, int[] spans)
 225:   {
 226:     calculateTiledPositions(allocated, total, children, offsets, spans, true);
 227:   }
 228: 
 229:   /**
 230:    * Calculate the offsets and spans of the components, when they should
 231:    * be placed end-to-end.
 232:    *
 233:    * You must specify the amount of allocated space in
 234:    * <code>allocated</code>, the total size requirements of the set of
 235:    * components in <code>total</code> (this can be calculated using
 236:    * {@link #getTiledSizeRequirements} and the size requirements of the
 237:    * components in <code>children</code>.
 238:    *
 239:    * The calculated offset and span values for each component are then
 240:    * stored in the arrays <code>offsets</code> and <code>spans</code>.
 241:    *
 242:    * Depending on the value of <code>forward</code> the components are
 243:    * placed in the forward direction (left-right or top-bottom), where
 244:    * the offsets begin with 0, or in the reverse direction
 245:    * (right-left or bottom-top).
 246:    *
 247:    * @param allocated the amount of allocated space
 248:    * @param total the total size requirements of the components
 249:    * @param children the size requirement of each component
 250:    * @param offsets will hold the offset values for each component
 251:    * @param spans will hold the span values for each component
 252:    * @param forward whether the components should be placed in the forward
 253:    *     direction (left-right or top-bottom) or reverse direction
 254:    *     (right-left or bottom-top)
 255:    */
 256:   public static void calculateTiledPositions(int allocated,
 257:                                              SizeRequirements total,
 258:                                              SizeRequirements[] children,
 259:                                              int[] offsets, int[] spans,
 260:                                              boolean forward)
 261:   {
 262:     int span = 0;
 263:     if (forward)
 264:       {
 265:         int offset = 0;
 266:         for (int i = 0; i < children.length; i++)
 267:           {
 268:             offsets[i] = offset;
 269:             spans[i] = children[i].preferred;
 270:             span += spans[i];
 271:             offset += children[i].preferred;
 272:           }
 273:       }
 274:     else
 275:       {
 276:         int offset = allocated;
 277:         for (int i = 0; i < children.length; i++)
 278:           {
 279:             offset -= children[i].preferred;
 280:             offsets[i] = offset;
 281:             span += spans[i];
 282:             spans[i] = children[i].preferred;
 283:           }
 284:       }
 285:     // Adjust spans so that we exactly fill the allocated region. If
 286:     if (span > allocated)
 287:       adjustSmaller(allocated, children, spans, span);
 288:     else if (span < allocated)
 289:       adjustGreater(allocated, children, spans, span);
 290: 
 291:     // Adjust offsets.
 292:     if (forward)
 293:       {
 294:         int offset = 0;
 295:         for (int i = 0; i < children.length; i++)
 296:           {
 297:             offsets[i] = offset;
 298:             offset += spans[i];
 299:           }
 300:       }
 301:     else
 302:       {
 303:         int offset = allocated;
 304:         for (int i = 0; i < children.length; i++)
 305:           {
 306:             offset -= spans[i];
 307:             offsets[i] = offset;
 308:           }
 309:       }
 310:   }
 311: 
 312:   private static void adjustSmaller(int allocated, SizeRequirements[] children,
 313:                                     int[] spans, int span)
 314:   {
 315:     // Sum up (prefSize - minSize) over all children
 316:     int sumDelta = 0;
 317:     for (int i = 0; i < children.length; i++)
 318:       sumDelta += children[i].preferred - children[i].minimum;
 319: 
 320:     // If we have sumDelta == 0, then all components have prefSize == maxSize
 321:     // and we can't do anything about it.
 322:     if (sumDelta == 0)
 323:       return;
 324: 
 325:     // Adjust all sizes according to their preferred and minimum sizes.
 326:     for (int i = 0; i < children.length; i++)
 327:       {
 328:         double factor = ((double) (children[i].preferred - children[i].minimum))
 329:                         / ((double) sumDelta);
 330:         // In case we have a sumDelta of 0, the factor should also be 0.
 331:         if (Double.isNaN(factor))
 332:           factor = 0;
 333:         spans[i] -= factor * (span - allocated);
 334:       }
 335:   }
 336: 
 337:   private static void adjustGreater(int allocated, SizeRequirements[] children,
 338:                                     int[] spans, int span)
 339:   {
 340:     // Sum up (maxSize - prefSize) over all children
 341:     int sumDelta = 0;
 342:     for (int i = 0; i < children.length; i++)
 343:       {
 344:         sumDelta += children[i].maximum - children[i].preferred;
 345:         if (sumDelta < 0)
 346:           sumDelta = Integer.MAX_VALUE;
 347:       }
 348: 
 349:     // If we have sumDelta == 0, then all components have prefSize == maxSize
 350:     // and we can't do anything about it.
 351:     if (sumDelta == 0)
 352:       return;
 353: 
 354:     // Adjust all sizes according to their preferred and minimum sizes.
 355:     for (int i = 0; i < children.length; i++)
 356:       {
 357:         double factor = ((double) (children[i].maximum - children[i].preferred))
 358:                         / ((double) sumDelta);
 359:         spans[i] -= factor * (span - allocated);
 360:       }
 361:   }
 362: 
 363:   /**
 364:    * Calculate the offsets and spans of the components, when they should
 365:    * be placed end-to-end.
 366:    *
 367:    * You must specify the amount of allocated space in
 368:    * <code>allocated</code>, the total size requirements of the set of
 369:    * components in <code>total</code> (this can be calculated using
 370:    * {@link #getTiledSizeRequirements} and the size requirements of the
 371:    * components in <code>children</code>.
 372:    *
 373:    * The calculated offset and span values for each component are then
 374:    * stored in the arrays <code>offsets</code> and <code>spans</code>.
 375:    *
 376:    * The components are tiled in the forward direction, beginning with
 377:    * an offset of 0.
 378:    * 
 379:    * @param allocated the amount of allocated space
 380:    * @param total the total size requirements of the components
 381:    * @param children the size requirement of each component
 382:    * @param offsets will hold the offset values for each component
 383:    * @param spans will hold the span values for each component
 384:    */
 385:   public static void calculateAlignedPositions(int allocated,
 386:                                                SizeRequirements total,
 387:                                                SizeRequirements[] children,
 388:                                                int[] offsets, int[] spans)
 389:   {
 390:     calculateAlignedPositions(allocated, total, children, offsets, spans,
 391:                               true);
 392:   }
 393: 
 394:   /**
 395:    * Calculate the offsets and spans of the components, when they should
 396:    * be placed end-to-end.
 397:    *
 398:    * You must specify the amount of allocated space in
 399:    * <code>allocated</code>, the total size requirements of the set of
 400:    * components in <code>total</code> (this can be calculated using
 401:    * {@link #getTiledSizeRequirements} and the size requirements of the
 402:    * components in <code>children</code>.
 403:    *
 404:    * The calculated offset and span values for each component are then
 405:    * stored in the arrays <code>offsets</code> and <code>spans</code>.
 406:    *
 407:    * Depending on the value of <code>forward</code> the components are
 408:    * placed in the forward direction (left-right or top-bottom), where
 409:    * the offsets begin with 0, or in the reverse direction
 410:    * (right-left or bottom-top).
 411:    *
 412:    * @param allocated the amount of allocated space
 413:    * @param total the total size requirements of the components
 414:    * @param children the size requirement of each component
 415:    * @param spans will hold the span values for each component
 416:    * @param forward whether the components should be placed in the forward
 417:    *     direction (left-right or top-bottom) or reverse direction
 418:    *     (right-left or bottom-top)
 419:    */
 420:   public static void calculateAlignedPositions(int allocated,
 421:                                                SizeRequirements total,
 422:                                                SizeRequirements[] children,
 423:                                                int[] offset, int[] spans,
 424:                                                boolean forward)
 425:   {
 426:     // First we compute the position of the baseline.
 427:     float baseline = allocated * total.alignment;
 428: 
 429:     // Now we can layout the components along the baseline.
 430:     for (int i = 0; i < children.length; i++)
 431:       {
 432:         float align = children[i].alignment;
 433:         // Try to fit the component into the available space.
 434:         int[] spanAndOffset = new int[2];
 435:         if (align < .5F || baseline == 0)
 436:           adjustFromRight(children[i], baseline, allocated, spanAndOffset);
 437:         else
 438:           adjustFromLeft(children[i], baseline, allocated, spanAndOffset);
 439:         spans[i] = spanAndOffset[0];
 440:         offset[i] = spanAndOffset[1];
 441:       }
 442:   }
 443: 
 444:   /**
 445:    * Adjusts the span and offset of a component for the aligned layout.
 446:    *
 447:    * @param reqs
 448:    * @param baseline
 449:    * @param allocated
 450:    * @param spanAndOffset
 451:    */
 452:   private static void adjustFromRight(SizeRequirements reqs, float baseline,
 453:                                       int allocated, int[] spanAndOffset)
 454:   {
 455:     float right = allocated - baseline;
 456:     // If the resulting span exceeds the maximum of the component, then adjust
 457:     // accordingly.
 458:     float maxRight = ((float) reqs.maximum) * (1.F - reqs.alignment);
 459:     if (right / (1.F - reqs.alignment) > reqs.maximum)
 460:       right = maxRight;
 461:     // If we have not enough space on the left side, then adjust accordingly.
 462:     if (right / (1.F - reqs.alignment) * reqs.alignment > allocated - baseline)
 463:       right = ((float) (allocated - baseline))
 464:              / reqs.alignment * (1.F - reqs.alignment);
 465: 
 466:     spanAndOffset[0] = (int) (right / (1.F - reqs.alignment));
 467:     spanAndOffset[1] = (int) (baseline - spanAndOffset[0] * reqs.alignment);
 468:   }
 469: 
 470:   /**
 471:    * Adjusts the span and offset of a component for the aligned layout.
 472:    *
 473:    * @param reqs
 474:    * @param baseline
 475:    * @param allocated
 476:    * @param spanAndOffset
 477:    */
 478:   private static void adjustFromLeft(SizeRequirements reqs, float baseline,
 479:                                      int allocated, int[] spanAndOffset)
 480:   {
 481:     float left = baseline;
 482:     // If the resulting span exceeds the maximum of the component, then adjust
 483:     // accordingly.
 484:     float maxLeft = ((float) reqs.maximum) * reqs.alignment;
 485:     if (left / reqs.alignment > reqs.maximum)
 486:       left = maxLeft;
 487:     // If we have not enough space on the right side, then adjust accordingly.
 488:     if (left / reqs.alignment * (1.F - reqs.alignment) > allocated - baseline)
 489:       left = ((float) (allocated - baseline))
 490:              / (1.F - reqs.alignment) * reqs.alignment;
 491: 
 492:     spanAndOffset[0] = (int) (left / reqs.alignment);
 493:     spanAndOffset[1] = (int) (baseline - spanAndOffset[0] * reqs.alignment);
 494:   }
 495: 
 496:   /**
 497:    * Returns an array of new preferred sizes for the children based on
 498:    * <code>delta</code>. <code>delta</code> specifies a change in the
 499:    * allocated space. The sizes of the children will be shortened or
 500:    * lengthened to accomodate the new allocation.
 501:    *
 502:    * @param delta the change of the size of the total allocation for
 503:    *     the components
 504:    * @param children the size requirements of each component
 505:    *
 506:    * @return the new preferred sizes for each component
 507:    */
 508:   public static int[] adjustSizes(int delta, SizeRequirements[] children)
 509:   {
 510:     return null; // TODO
 511:   }
 512: }