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