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      */
SizeRequirements()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      */
SizeRequirements(int min, int pref, int max, float a)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 }
491