1 /*
   2  * Copyright (c) 1997, 2006, Oracle and/or its affiliates. All rights reserved.
   3  * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER.
   4  *
   5  * This code is free software; you can redistribute it and/or modify it
   6  * under the terms of the GNU General Public License version 2 only, as
   7  * published by the Free Software Foundation.  Oracle designates this
   8  * particular file as subject to the "Classpath" exception as provided
   9  * by Oracle in the LICENSE file that accompanied this code.
  10  *
  11  * This code is distributed in the hope that it will be useful, but WITHOUT
  12  * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
  13  * FITNESS FOR A PARTICULAR PURPOSE.  See the GNU General Public License
  14  * version 2 for more details (a copy is included in the LICENSE file that
  15  * accompanied this code).
  16  *
  17  * You should have received a copy of the GNU General Public License version
  18  * 2 along with this work; if not, write to the Free Software Foundation,
  19  * Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA.
  20  *
  21  * Please contact Oracle, 500 Oracle Parkway, Redwood Shores, CA 94065 USA
  22  * or visit www.oracle.com if you need additional information or have any
  23  * questions.
  24  */
  25 package javax.swing;
  26 
  27 
  28 import java.awt.*;
  29 import java.io.Serializable;
  30 
  31 /**
  32  * For the convenience of layout managers,
  33  * calculates information about the size and position of components.
  34  * All size and position calculation methods are class methods
  35  * that take arrays of SizeRequirements as arguments.
  36  * The SizeRequirements class supports two types of layout:
  37  *
  38  * <blockquote>
  39  * <dl>
  40  * <dt> tiled
  41  * <dd> The components are placed end-to-end,
  42  *      starting either at coordinate 0 (the leftmost or topmost position)
  43  *      or at the coordinate representing the end of the allocated span
  44  *      (the rightmost or bottommost position).
  45  *
  46  * <dt> aligned
  47  * <dd> The components are aligned as specified
  48  *      by each component's X or Y alignment value.
  49  * </dl>
  50  * </blockquote>
  51  *
  52  * <p>
  53  *
  54  * Each SizeRequirements object contains information
  55  * about either the width (and X alignment)
  56  * or height (and Y alignment)
  57  * of a single component or a group of components:
  58  *
  59  * <blockquote>
  60  * <dl>
  61  * <dt> <code>minimum</code>
  62  * <dd> The smallest reasonable width/height of the component
  63  *      or component group, in pixels.
  64  *
  65  * <dt> <code>preferred</code>
  66  * <dd> The natural width/height of the component
  67  *      or component group, in pixels.
  68  *
  69  * <dt> <code>maximum</code>
  70  * <dd> The largest reasonable width/height of the component
  71  *      or component group, in pixels.
  72  *
  73  * <dt> <code>alignment</code>
  74  * <dd> The X/Y alignment of the component
  75  *      or component group.
  76  * </dl>
  77  * </blockquote>
  78  * <p>
  79  * <strong>Warning:</strong>
  80  * Serialized objects of this class will not be compatible with
  81  * future Swing releases. The current serialization support is
  82  * appropriate for short term storage or RMI between applications running
  83  * the same version of Swing.  As of 1.4, support for long term storage
  84  * of all JavaBeans&trade;
  85  * has been added to the <code>java.beans</code> package.
  86  * Please see {@link java.beans.XMLEncoder}.
  87  *
  88  * @see Component#getMinimumSize
  89  * @see Component#getPreferredSize
  90  * @see Component#getMaximumSize
  91  * @see Component#getAlignmentX
  92  * @see Component#getAlignmentY
  93  *
  94  * @author Timothy Prinzing
  95  */
  96 public class SizeRequirements implements Serializable {
  97 
  98     /**
  99      * The minimum size required.
 100      * For a component <code>comp</code>, this should be equal to either
 101      * <code>comp.getMinimumSize().width</code> or
 102      * <code>comp.getMinimumSize().height</code>.
 103      */
 104     public int minimum;
 105 
 106     /**
 107      * The preferred (natural) size.
 108      * For a component <code>comp</code>, this should be equal to either
 109      * <code>comp.getPreferredSize().width</code> or
 110      * <code>comp.getPreferredSize().height</code>.
 111      */
 112     public int preferred;
 113 
 114     /**
 115      * The maximum size allowed.
 116      * For a component <code>comp</code>, this should be equal to either
 117      * <code>comp.getMaximumSize().width</code> or
 118      * <code>comp.getMaximumSize().height</code>.
 119      */
 120     public int maximum;
 121 
 122     /**
 123      * The alignment, specified as a value between 0.0 and 1.0,
 124      * inclusive.
 125      * To specify centering, the alignment should be 0.5.
 126      */
 127     public float alignment;
 128 
 129     /**
 130      * Creates a SizeRequirements object with the minimum, preferred,
 131      * and maximum sizes set to zero and an alignment value of 0.5
 132      * (centered).
 133      */
 134     public SizeRequirements() {
 135         minimum = 0;
 136         preferred = 0;
 137         maximum = 0;
 138         alignment = 0.5f;
 139     }
 140 
 141     /**
 142      * Creates a SizeRequirements object with the specified minimum, preferred,
 143      * and maximum sizes and the specified alignment.
 144      *
 145      * @param min the minimum size &gt;= 0
 146      * @param pref the preferred size &gt;= 0
 147      * @param max the maximum size &gt;= 0
 148      * @param a the alignment &gt;= 0.0f &amp;&amp; &lt;= 1.0f
 149      */
 150     public SizeRequirements(int min, int pref, int max, float a) {
 151         minimum = min;
 152         preferred = pref;
 153         maximum = max;
 154         alignment = a > 1.0f ? 1.0f : a < 0.0f ? 0.0f : a;
 155     }
 156 
 157     /**
 158      * Returns a string describing the minimum, preferred, and maximum
 159      * size requirements, along with the alignment.
 160      *
 161      * @return the string
 162      */
 163     public String toString() {
 164         return "[" + minimum + "," + preferred + "," + maximum + "]@" + alignment;
 165     }
 166 
 167     /**
 168      * Determines the total space necessary to
 169      * place a set of components end-to-end.  The needs
 170      * of each component in the set are represented by an entry in the
 171      * passed-in SizeRequirements array.
 172      * The returned SizeRequirements object has an alignment of 0.5
 173      * (centered).  The space requirement is never more than
 174      * Integer.MAX_VALUE.
 175      *
 176      * @param children  the space requirements for a set of components.
 177      *   The vector may be of zero length, which will result in a
 178      *   default SizeRequirements object instance being passed back.
 179      * @return  the total space requirements.
 180      */
 181     public static SizeRequirements getTiledSizeRequirements(SizeRequirements[]
 182                                                             children) {
 183         SizeRequirements total = new SizeRequirements();
 184         for (int i = 0; i < children.length; i++) {
 185             SizeRequirements req = children[i];
 186             total.minimum = (int) Math.min((long) total.minimum + (long) req.minimum, Integer.MAX_VALUE);
 187             total.preferred = (int) Math.min((long) total.preferred + (long) req.preferred, Integer.MAX_VALUE);
 188             total.maximum = (int) Math.min((long) total.maximum + (long) req.maximum, Integer.MAX_VALUE);
 189         }
 190         return total;
 191     }
 192 
 193     /**
 194      * Determines the total space necessary to
 195      * align a set of components.  The needs
 196      * of each component in the set are represented by an entry in the
 197      * passed-in SizeRequirements array.  The total space required will
 198      * never be more than Integer.MAX_VALUE.
 199      *
 200      * @param children  the set of child requirements.  If of zero length,
 201      *  the returns result will be a default instance of SizeRequirements.
 202      * @return  the total space requirements.
 203      */
 204     public static SizeRequirements getAlignedSizeRequirements(SizeRequirements[]
 205                                                               children) {
 206         SizeRequirements totalAscent = new SizeRequirements();
 207         SizeRequirements totalDescent = new SizeRequirements();
 208         for (int i = 0; i < children.length; i++) {
 209             SizeRequirements req = children[i];
 210 
 211             int ascent = (int) (req.alignment * req.minimum);
 212             int descent = req.minimum - ascent;
 213             totalAscent.minimum = Math.max(ascent, totalAscent.minimum);
 214             totalDescent.minimum = Math.max(descent, totalDescent.minimum);
 215 
 216             ascent = (int) (req.alignment * req.preferred);
 217             descent = req.preferred - ascent;
 218             totalAscent.preferred = Math.max(ascent, totalAscent.preferred);
 219             totalDescent.preferred = Math.max(descent, totalDescent.preferred);
 220 
 221             ascent = (int) (req.alignment * req.maximum);
 222             descent = req.maximum - ascent;
 223             totalAscent.maximum = Math.max(ascent, totalAscent.maximum);
 224             totalDescent.maximum = Math.max(descent, totalDescent.maximum);
 225         }
 226         int min = (int) Math.min((long) totalAscent.minimum + (long) totalDescent.minimum, Integer.MAX_VALUE);
 227         int pref = (int) Math.min((long) totalAscent.preferred + (long) totalDescent.preferred, Integer.MAX_VALUE);
 228         int max = (int) Math.min((long) totalAscent.maximum + (long) totalDescent.maximum, Integer.MAX_VALUE);
 229         float alignment = 0.0f;
 230         if (min > 0) {
 231             alignment = (float) totalAscent.minimum / min;
 232             alignment = alignment > 1.0f ? 1.0f : alignment < 0.0f ? 0.0f : alignment;
 233         }
 234         return new SizeRequirements(min, pref, max, alignment);
 235     }
 236 
 237     /**
 238      * Creates a set of offset/span pairs representing how to
 239      * lay out a set of components end-to-end.
 240      * This method requires that you specify
 241      * the total amount of space to be allocated,
 242      * the size requirements for each component to be placed
 243      * (specified as an array of SizeRequirements), and
 244      * the total size requirement of the set of components.
 245      * You can get the total size requirement
 246      * by invoking the getTiledSizeRequirements method.  The components
 247      * will be tiled in the forward direction with offsets increasing from 0.
 248      *
 249      * @param allocated the total span to be allocated &gt;= 0.
 250      * @param total     the total of the children requests.  This argument
 251      *  is optional and may be null.
 252      * @param children  the size requirements for each component.
 253      * @param offsets   the offset from 0 for each child where
 254      *   the spans were allocated (determines placement of the span).
 255      * @param spans     the span allocated for each child to make the
 256      *   total target span.
 257      */
 258     public static void calculateTiledPositions(int allocated,
 259                                                SizeRequirements total,
 260                                                SizeRequirements[] children,
 261                                                int[] offsets,
 262                                                int[] spans) {
 263         calculateTiledPositions(allocated, total, children, offsets, spans, true);
 264     }
 265 
 266     /**
 267      * Creates a set of offset/span pairs representing how to
 268      * lay out a set of components end-to-end.
 269      * This method requires that you specify
 270      * the total amount of space to be allocated,
 271      * the size requirements for each component to be placed
 272      * (specified as an array of SizeRequirements), and
 273      * the total size requirement of the set of components.
 274      * You can get the total size requirement
 275      * by invoking the getTiledSizeRequirements method.
 276      *
 277      * This method also requires a flag indicating whether components
 278      * should be tiled in the forward direction (offsets increasing
 279      * from 0) or reverse direction (offsets decreasing from the end
 280      * of the allocated space).  The forward direction represents
 281      * components tiled from left to right or top to bottom.  The
 282      * reverse direction represents components tiled from right to left
 283      * or bottom to top.
 284      *
 285      * @param allocated the total span to be allocated &gt;= 0.
 286      * @param total     the total of the children requests.  This argument
 287      *  is optional and may be null.
 288      * @param children  the size requirements for each component.
 289      * @param offsets   the offset from 0 for each child where
 290      *   the spans were allocated (determines placement of the span).
 291      * @param spans     the span allocated for each child to make the
 292      *   total target span.
 293      * @param forward   tile with offsets increasing from 0 if true
 294      *   and with offsets decreasing from the end of the allocated space
 295      *   if false.
 296      * @since 1.4
 297      */
 298     public static void calculateTiledPositions(int allocated,
 299                                                SizeRequirements total,
 300                                                SizeRequirements[] children,
 301                                                int[] offsets,
 302                                                int[] spans,
 303                                                boolean forward) {
 304         // The total argument turns out to be a bad idea since the
 305         // total of all the children can overflow the integer used to
 306         // hold the total.  The total must therefore be calculated and
 307         // stored in long variables.
 308         long min = 0;
 309         long pref = 0;
 310         long max = 0;
 311         for (int i = 0; i < children.length; i++) {
 312             min += children[i].minimum;
 313             pref += children[i].preferred;
 314             max += children[i].maximum;
 315         }
 316         if (allocated >= pref) {
 317             expandedTile(allocated, min, pref, max, children, offsets, spans, forward);
 318         } else {
 319             compressedTile(allocated, min, pref, max, children, offsets, spans, forward);
 320         }
 321     }
 322 
 323     private static void compressedTile(int allocated, long min, long pref, long max,
 324                                        SizeRequirements[] request,
 325                                        int[] offsets, int[] spans,
 326                                        boolean forward) {
 327 
 328         // ---- determine what we have to work with ----
 329         float totalPlay = Math.min(pref - allocated, pref - min);
 330         float factor = (pref - min == 0) ? 0.0f : totalPlay / (pref - min);
 331 
 332         // ---- make the adjustments ----
 333         int totalOffset;
 334         if( forward ) {
 335             // lay out with offsets increasing from 0
 336             totalOffset = 0;
 337             for (int i = 0; i < spans.length; i++) {
 338                 offsets[i] = totalOffset;
 339                 SizeRequirements req = request[i];
 340                 float play = factor * (req.preferred - req.minimum);
 341                 spans[i] = (int)(req.preferred - play);
 342                 totalOffset = (int) Math.min((long) totalOffset + (long) spans[i], Integer.MAX_VALUE);
 343             }
 344         } else {
 345             // lay out with offsets decreasing from the end of the allocation
 346             totalOffset = allocated;
 347             for (int i = 0; i < spans.length; i++) {
 348                 SizeRequirements req = request[i];
 349                 float play = factor * (req.preferred - req.minimum);
 350                 spans[i] = (int)(req.preferred - play);
 351                 offsets[i] = totalOffset - spans[i];
 352                 totalOffset = (int) Math.max((long) totalOffset - (long) spans[i], 0);
 353             }
 354         }
 355     }
 356 
 357     private static void expandedTile(int allocated, long min, long pref, long max,
 358                                      SizeRequirements[] request,
 359                                      int[] offsets, int[] spans,
 360                                      boolean forward) {
 361 
 362         // ---- determine what we have to work with ----
 363         float totalPlay = Math.min(allocated - pref, max - pref);
 364         float factor = (max - pref == 0) ? 0.0f : totalPlay / (max - pref);
 365 
 366         // ---- make the adjustments ----
 367         int totalOffset;
 368         if( forward ) {
 369             // lay out with offsets increasing from 0
 370             totalOffset = 0;
 371             for (int i = 0; i < spans.length; i++) {
 372                 offsets[i] = totalOffset;
 373                 SizeRequirements req = request[i];
 374                 int play = (int)(factor * (req.maximum - req.preferred));
 375                 spans[i] = (int) Math.min((long) req.preferred + (long) play, Integer.MAX_VALUE);
 376                 totalOffset = (int) Math.min((long) totalOffset + (long) spans[i], Integer.MAX_VALUE);
 377             }
 378         } else {
 379             // lay out with offsets decreasing from the end of the allocation
 380             totalOffset = allocated;
 381             for (int i = 0; i < spans.length; i++) {
 382                 SizeRequirements req = request[i];
 383                 int play = (int)(factor * (req.maximum - req.preferred));
 384                 spans[i] = (int) Math.min((long) req.preferred + (long) play, Integer.MAX_VALUE);
 385                 offsets[i] = totalOffset - spans[i];
 386                 totalOffset = (int) Math.max((long) totalOffset - (long) spans[i], 0);
 387             }
 388         }
 389     }
 390 
 391     /**
 392      * Creates a bunch of offset/span pairs specifying how to
 393      * lay out a set of components with the specified alignments.
 394      * The resulting span allocations will overlap, with each one
 395      * fitting as well as possible into the given total allocation.
 396      * This method requires that you specify
 397      * the total amount of space to be allocated,
 398      * the size requirements for each component to be placed
 399      * (specified as an array of SizeRequirements), and
 400      * the total size requirements of the set of components
 401      * (only the alignment field of which is actually used).
 402      * You can get the total size requirement by invoking
 403      * getAlignedSizeRequirements.
 404      *
 405      * Normal alignment will be done with an alignment value of 0.0f
 406      * representing the left/top edge of a component.
 407      *
 408      * @param allocated the total span to be allocated &gt;= 0.
 409      * @param total     the total of the children requests.
 410      * @param children  the size requirements for each component.
 411      * @param offsets   the offset from 0 for each child where
 412      *   the spans were allocated (determines placement of the span).
 413      * @param spans     the span allocated for each child to make the
 414      *   total target span.
 415      */
 416     public static void calculateAlignedPositions(int allocated,
 417                                                  SizeRequirements total,
 418                                                  SizeRequirements[] children,
 419                                                  int[] offsets,
 420                                                  int[] spans) {
 421         calculateAlignedPositions( allocated, total, children, offsets, spans, true );
 422     }
 423 
 424     /**
 425      * Creates a set of offset/span pairs specifying how to
 426      * lay out a set of components with the specified alignments.
 427      * The resulting span allocations will overlap, with each one
 428      * fitting as well as possible into the given total allocation.
 429      * This method requires that you specify
 430      * the total amount of space to be allocated,
 431      * the size requirements for each component to be placed
 432      * (specified as an array of SizeRequirements), and
 433      * the total size requirements of the set of components
 434      * (only the alignment field of which is actually used)
 435      * You can get the total size requirement by invoking
 436      * getAlignedSizeRequirements.
 437      *
 438      * This method also requires a flag indicating whether normal or
 439      * reverse alignment should be performed.  With normal alignment
 440      * the value 0.0f represents the left/top edge of the component
 441      * to be aligned.  With reverse alignment, 0.0f represents the
 442      * right/bottom edge.
 443      *
 444      * @param allocated the total span to be allocated &gt;= 0.
 445      * @param total     the total of the children requests.
 446      * @param children  the size requirements for each component.
 447      * @param offsets   the offset from 0 for each child where
 448      *   the spans were allocated (determines placement of the span).
 449      * @param spans     the span allocated for each child to make the
 450      *   total target span.
 451      * @param normal    when true, the alignment value 0.0f means
 452      *   left/top; when false, it means right/bottom.
 453      * @since 1.4
 454      */
 455     public static void calculateAlignedPositions(int allocated,
 456                                                  SizeRequirements total,
 457                                                  SizeRequirements[] children,
 458                                                  int[] offsets,
 459                                                  int[] spans,
 460                                                  boolean normal) {
 461         float totalAlignment = normal ? total.alignment : 1.0f - total.alignment;
 462         int totalAscent = (int)(allocated * totalAlignment);
 463         int totalDescent = allocated - totalAscent;
 464         for (int i = 0; i < children.length; i++) {
 465             SizeRequirements req = children[i];
 466             float alignment = normal ? req.alignment : 1.0f - req.alignment;
 467             int maxAscent = (int)(req.maximum * alignment);
 468             int maxDescent = req.maximum - maxAscent;
 469             int ascent = Math.min(totalAscent, maxAscent);
 470             int descent = Math.min(totalDescent, maxDescent);
 471 
 472             offsets[i] = totalAscent - ascent;
 473             spans[i] = (int) Math.min((long) ascent + (long) descent, Integer.MAX_VALUE);
 474         }
 475     }
 476 
 477     // This method was used by the JTable - which now uses a different technique.
 478     /**
 479      * Adjust a specified array of sizes by a given amount.
 480      *
 481      * @param delta     an int specifying the size difference
 482      * @param children  an array of SizeRequirements objects
 483      * @return an array of ints containing the final size for each item
 484      */
 485     public static int[] adjustSizes(int delta, SizeRequirements[] children) {
 486       return new int[0];
 487     }
 488 }