View Javadoc
1   /*
2    * Copyright (c) 1997, 2013, 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 }