1 /*
   2  * Copyright (c) 1997, 2014, 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 @SuppressWarnings("serial") // Same-version serialization only
  97 public class SizeRequirements implements Serializable {
  98 
  99     /**
 100      * The minimum size required.
 101      * For a component <code>comp</code>, this should be equal to either
 102      * <code>comp.getMinimumSize().width</code> or
 103      * <code>comp.getMinimumSize().height</code>.
 104      */
 105     public int minimum;
 106 
 107     /**
 108      * The preferred (natural) size.
 109      * For a component <code>comp</code>, this should be equal to either
 110      * <code>comp.getPreferredSize().width</code> or
 111      * <code>comp.getPreferredSize().height</code>.
 112      */
 113     public int preferred;
 114 
 115     /**
 116      * The maximum size allowed.
 117      * For a component <code>comp</code>, this should be equal to either
 118      * <code>comp.getMaximumSize().width</code> or
 119      * <code>comp.getMaximumSize().height</code>.
 120      */
 121     public int maximum;
 122 
 123     /**
 124      * The alignment, specified as a value between 0.0 and 1.0,
 125      * inclusive.
 126      * To specify centering, the alignment should be 0.5.
 127      */
 128     public float alignment;
 129 
 130     /**
 131      * Creates a SizeRequirements object with the minimum, preferred,
 132      * and maximum sizes set to zero and an alignment value of 0.5
 133      * (centered).
 134      */
 135     public SizeRequirements() {
 136         minimum = 0;
 137         preferred = 0;
 138         maximum = 0;
 139         alignment = 0.5f;
 140     }
 141 
 142     /**
 143      * Creates a SizeRequirements object with the specified minimum, preferred,
 144      * and maximum sizes and the specified alignment.
 145      *
 146      * @param min the minimum size &gt;= 0
 147      * @param pref the preferred size &gt;= 0
 148      * @param max the maximum size &gt;= 0
 149      * @param a the alignment &gt;= 0.0f &amp;&amp; &lt;= 1.0f
 150      */
 151     public SizeRequirements(int min, int pref, int max, float a) {
 152         minimum = min;
 153         preferred = pref;
 154         maximum = max;
 155         alignment = a > 1.0f ? 1.0f : a < 0.0f ? 0.0f : a;
 156     }
 157 
 158     /**
 159      * Returns a string describing the minimum, preferred, and maximum
 160      * size requirements, along with the alignment.
 161      *
 162      * @return the string
 163      */
 164     public String toString() {
 165         return "[" + minimum + "," + preferred + "," + maximum + "]@" + alignment;
 166     }
 167 
 168     /**
 169      * Determines the total space necessary to
 170      * place a set of components end-to-end.  The needs
 171      * of each component in the set are represented by an entry in the
 172      * passed-in SizeRequirements array.
 173      * The returned SizeRequirements object has an alignment of 0.5
 174      * (centered).  The space requirement is never more than
 175      * Integer.MAX_VALUE.
 176      *
 177      * @param children  the space requirements for a set of components.
 178      *   The vector may be of zero length, which will result in a
 179      *   default SizeRequirements object instance being passed back.
 180      * @return  the total space requirements.
 181      */
 182     public static SizeRequirements getTiledSizeRequirements(SizeRequirements[]
 183                                                             children) {
 184         SizeRequirements total = new SizeRequirements();
 185         for (int i = 0; i < children.length; i++) {
 186             SizeRequirements req = children[i];
 187             total.minimum = (int) Math.min((long) total.minimum + (long) req.minimum, Integer.MAX_VALUE);
 188             total.preferred = (int) Math.min((long) total.preferred + (long) req.preferred, Integer.MAX_VALUE);
 189             total.maximum = (int) Math.min((long) total.maximum + (long) req.maximum, Integer.MAX_VALUE);
 190         }
 191         return total;
 192     }
 193 
 194     /**
 195      * Determines the total space necessary to
 196      * align a set of components.  The needs
 197      * of each component in the set are represented by an entry in the
 198      * passed-in SizeRequirements array.  The total space required will
 199      * never be more than Integer.MAX_VALUE.
 200      *
 201      * @param children  the set of child requirements.  If of zero length,
 202      *  the returns result will be a default instance of SizeRequirements.
 203      * @return  the total space requirements.
 204      */
 205     public static SizeRequirements getAlignedSizeRequirements(SizeRequirements[]
 206                                                               children) {
 207         SizeRequirements totalAscent = new SizeRequirements();
 208         SizeRequirements totalDescent = new SizeRequirements();
 209         for (int i = 0; i < children.length; i++) {
 210             SizeRequirements req = children[i];
 211 
 212             int ascent = (int) (req.alignment * req.minimum);
 213             int descent = req.minimum - ascent;
 214             totalAscent.minimum = Math.max(ascent, totalAscent.minimum);
 215             totalDescent.minimum = Math.max(descent, totalDescent.minimum);
 216 
 217             ascent = (int) (req.alignment * req.preferred);
 218             descent = req.preferred - ascent;
 219             totalAscent.preferred = Math.max(ascent, totalAscent.preferred);
 220             totalDescent.preferred = Math.max(descent, totalDescent.preferred);
 221 
 222             ascent = (int) (req.alignment * req.maximum);
 223             descent = req.maximum - ascent;
 224             totalAscent.maximum = Math.max(ascent, totalAscent.maximum);
 225             totalDescent.maximum = Math.max(descent, totalDescent.maximum);
 226         }
 227         int min = (int) Math.min((long) totalAscent.minimum + (long) totalDescent.minimum, Integer.MAX_VALUE);
 228         int pref = (int) Math.min((long) totalAscent.preferred + (long) totalDescent.preferred, Integer.MAX_VALUE);
 229         int max = (int) Math.min((long) totalAscent.maximum + (long) totalDescent.maximum, Integer.MAX_VALUE);
 230         float alignment = 0.0f;
 231         if (min > 0) {
 232             alignment = (float) totalAscent.minimum / min;
 233             alignment = alignment > 1.0f ? 1.0f : alignment < 0.0f ? 0.0f : alignment;
 234         }
 235         return new SizeRequirements(min, pref, max, alignment);
 236     }
 237 
 238     /**
 239      * Creates a set of offset/span pairs representing how to
 240      * lay out a set of components end-to-end.
 241      * This method requires that you specify
 242      * the total amount of space to be allocated,
 243      * the size requirements for each component to be placed
 244      * (specified as an array of SizeRequirements), and
 245      * the total size requirement of the set of components.
 246      * You can get the total size requirement
 247      * by invoking the getTiledSizeRequirements method.  The components
 248      * will be tiled in the forward direction with offsets increasing from 0.
 249      *
 250      * @param allocated the total span to be allocated &gt;= 0.
 251      * @param total     the total of the children requests.  This argument
 252      *  is optional and may be null.
 253      * @param children  the size requirements for each component.
 254      * @param offsets   the offset from 0 for each child where
 255      *   the spans were allocated (determines placement of the span).
 256      * @param spans     the span allocated for each child to make the
 257      *   total target span.
 258      */
 259     public static void calculateTiledPositions(int allocated,
 260                                                SizeRequirements total,
 261                                                SizeRequirements[] children,
 262                                                int[] offsets,
 263                                                int[] spans) {
 264         calculateTiledPositions(allocated, total, children, offsets, spans, true);
 265     }
 266 
 267     /**
 268      * Creates a set of offset/span pairs representing how to
 269      * lay out a set of components end-to-end.
 270      * This method requires that you specify
 271      * the total amount of space to be allocated,
 272      * the size requirements for each component to be placed
 273      * (specified as an array of SizeRequirements), and
 274      * the total size requirement of the set of components.
 275      * You can get the total size requirement
 276      * by invoking the getTiledSizeRequirements method.
 277      *
 278      * This method also requires a flag indicating whether components
 279      * should be tiled in the forward direction (offsets increasing
 280      * from 0) or reverse direction (offsets decreasing from the end
 281      * of the allocated space).  The forward direction represents
 282      * components tiled from left to right or top to bottom.  The
 283      * reverse direction represents components tiled from right to left
 284      * or bottom to top.
 285      *
 286      * @param allocated the total span to be allocated &gt;= 0.
 287      * @param total     the total of the children requests.  This argument
 288      *  is optional and may be null.
 289      * @param children  the size requirements for each component.
 290      * @param offsets   the offset from 0 for each child where
 291      *   the spans were allocated (determines placement of the span).
 292      * @param spans     the span allocated for each child to make the
 293      *   total target span.
 294      * @param forward   tile with offsets increasing from 0 if true
 295      *   and with offsets decreasing from the end of the allocated space
 296      *   if false.
 297      * @since 1.4
 298      */
 299     public static void calculateTiledPositions(int allocated,
 300                                                SizeRequirements total,
 301                                                SizeRequirements[] children,
 302                                                int[] offsets,
 303                                                int[] spans,
 304                                                boolean forward) {
 305         // The total argument turns out to be a bad idea since the
 306         // total of all the children can overflow the integer used to
 307         // hold the total.  The total must therefore be calculated and
 308         // stored in long variables.
 309         long min = 0;
 310         long pref = 0;
 311         long max = 0;
 312         for (int i = 0; i < children.length; i++) {
 313             min += children[i].minimum;
 314             pref += children[i].preferred;
 315             max += children[i].maximum;
 316         }
 317         if (allocated >= pref) {
 318             expandedTile(allocated, min, pref, max, children, offsets, spans, forward);
 319         } else {
 320             compressedTile(allocated, min, pref, max, children, offsets, spans, forward);
 321         }
 322     }
 323 
 324     private static void compressedTile(int allocated, long min, long pref, long max,
 325                                        SizeRequirements[] request,
 326                                        int[] offsets, int[] spans,
 327                                        boolean forward) {
 328 
 329         // ---- determine what we have to work with ----
 330         float totalPlay = Math.min(pref - allocated, pref - min);
 331         float factor = (pref - min == 0) ? 0.0f : totalPlay / (pref - min);
 332 
 333         // ---- make the adjustments ----
 334         int totalOffset;
 335         if( forward ) {
 336             // lay out with offsets increasing from 0
 337             totalOffset = 0;
 338             for (int i = 0; i < spans.length; i++) {
 339                 offsets[i] = totalOffset;
 340                 SizeRequirements req = request[i];
 341                 float play = factor * (req.preferred - req.minimum);
 342                 spans[i] = (int)(req.preferred - play);
 343                 totalOffset = (int) Math.min((long) totalOffset + (long) spans[i], Integer.MAX_VALUE);
 344             }
 345         } else {
 346             // lay out with offsets decreasing from the end of the allocation
 347             totalOffset = allocated;
 348             for (int i = 0; i < spans.length; i++) {
 349                 SizeRequirements req = request[i];
 350                 float play = factor * (req.preferred - req.minimum);
 351                 spans[i] = (int)(req.preferred - play);
 352                 offsets[i] = totalOffset - spans[i];
 353                 totalOffset = (int) Math.max((long) totalOffset - (long) spans[i], 0);
 354             }
 355         }
 356     }
 357 
 358     private static void expandedTile(int allocated, long min, long pref, long max,
 359                                      SizeRequirements[] request,
 360                                      int[] offsets, int[] spans,
 361                                      boolean forward) {
 362 
 363         // ---- determine what we have to work with ----
 364         float totalPlay = Math.min(allocated - pref, max - pref);
 365         float factor = (max - pref == 0) ? 0.0f : totalPlay / (max - pref);
 366 
 367         // ---- make the adjustments ----
 368         int totalOffset;
 369         if( forward ) {
 370             // lay out with offsets increasing from 0
 371             totalOffset = 0;
 372             for (int i = 0; i < spans.length; i++) {
 373                 offsets[i] = totalOffset;
 374                 SizeRequirements req = request[i];
 375                 int play = (int)(factor * (req.maximum - req.preferred));
 376                 spans[i] = (int) Math.min((long) req.preferred + (long) play, Integer.MAX_VALUE);
 377                 totalOffset = (int) Math.min((long) totalOffset + (long) spans[i], Integer.MAX_VALUE);
 378             }
 379         } else {
 380             // lay out with offsets decreasing from the end of the allocation
 381             totalOffset = allocated;
 382             for (int i = 0; i < spans.length; i++) {
 383                 SizeRequirements req = request[i];
 384                 int play = (int)(factor * (req.maximum - req.preferred));
 385                 spans[i] = (int) Math.min((long) req.preferred + (long) play, Integer.MAX_VALUE);
 386                 offsets[i] = totalOffset - spans[i];
 387                 totalOffset = (int) Math.max((long) totalOffset - (long) spans[i], 0);
 388             }
 389         }
 390     }
 391 
 392     /**
 393      * Creates a bunch of offset/span pairs specifying how to
 394      * lay out a set of components with the specified alignments.
 395      * The resulting span allocations will overlap, with each one
 396      * fitting as well as possible into the given total allocation.
 397      * This method requires that you specify
 398      * the total amount of space to be allocated,
 399      * the size requirements for each component to be placed
 400      * (specified as an array of SizeRequirements), and
 401      * the total size requirements of the set of components
 402      * (only the alignment field of which is actually used).
 403      * You can get the total size requirement by invoking
 404      * getAlignedSizeRequirements.
 405      *
 406      * Normal alignment will be done with an alignment value of 0.0f
 407      * representing the left/top edge of a component.
 408      *
 409      * @param allocated the total span to be allocated &gt;= 0.
 410      * @param total     the total of the children requests.
 411      * @param children  the size requirements for each component.
 412      * @param offsets   the offset from 0 for each child where
 413      *   the spans were allocated (determines placement of the span).
 414      * @param spans     the span allocated for each child to make the
 415      *   total target span.
 416      */
 417     public static void calculateAlignedPositions(int allocated,
 418                                                  SizeRequirements total,
 419                                                  SizeRequirements[] children,
 420                                                  int[] offsets,
 421                                                  int[] spans) {
 422         calculateAlignedPositions( allocated, total, children, offsets, spans, true );
 423     }
 424 
 425     /**
 426      * Creates a set of offset/span pairs specifying how to
 427      * lay out a set of components with the specified alignments.
 428      * The resulting span allocations will overlap, with each one
 429      * fitting as well as possible into the given total allocation.
 430      * This method requires that you specify
 431      * the total amount of space to be allocated,
 432      * the size requirements for each component to be placed
 433      * (specified as an array of SizeRequirements), and
 434      * the total size requirements of the set of components
 435      * (only the alignment field of which is actually used)
 436      * You can get the total size requirement by invoking
 437      * getAlignedSizeRequirements.
 438      *
 439      * This method also requires a flag indicating whether normal or
 440      * reverse alignment should be performed.  With normal alignment
 441      * the value 0.0f represents the left/top edge of the component
 442      * to be aligned.  With reverse alignment, 0.0f represents the
 443      * right/bottom edge.
 444      *
 445      * @param allocated the total span to be allocated &gt;= 0.
 446      * @param total     the total of the children requests.
 447      * @param children  the size requirements for each component.
 448      * @param offsets   the offset from 0 for each child where
 449      *   the spans were allocated (determines placement of the span).
 450      * @param spans     the span allocated for each child to make the
 451      *   total target span.
 452      * @param normal    when true, the alignment value 0.0f means
 453      *   left/top; when false, it means right/bottom.
 454      * @since 1.4
 455      */
 456     public static void calculateAlignedPositions(int allocated,
 457                                                  SizeRequirements total,
 458                                                  SizeRequirements[] children,
 459                                                  int[] offsets,
 460                                                  int[] spans,
 461                                                  boolean normal) {
 462         float totalAlignment = normal ? total.alignment : 1.0f - total.alignment;
 463         int totalAscent = (int)(allocated * totalAlignment);
 464         int totalDescent = allocated - totalAscent;
 465         for (int i = 0; i < children.length; i++) {
 466             SizeRequirements req = children[i];
 467             float alignment = normal ? req.alignment : 1.0f - req.alignment;
 468             int maxAscent = (int)(req.maximum * alignment);
 469             int maxDescent = req.maximum - maxAscent;
 470             int ascent = Math.min(totalAscent, maxAscent);
 471             int descent = Math.min(totalDescent, maxDescent);
 472 
 473             offsets[i] = totalAscent - ascent;
 474             spans[i] = (int) Math.min((long) ascent + (long) descent, Integer.MAX_VALUE);
 475         }
 476     }
 477 
 478     // This method was used by the JTable - which now uses a different technique.
 479     /**
 480      * Adjust a specified array of sizes by a given amount.
 481      *
 482      * @param delta     an int specifying the size difference
 483      * @param children  an array of SizeRequirements objects
 484      * @return an array of ints containing the final size for each item
 485      */
 486     public static int[] adjustSizes(int delta, SizeRequirements[] children) {
 487       return new int[0];
 488     }
 489 }