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 */
25package javax.swing;
26
27
28import java.awt.*;
29import 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
98public 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}
491