1 /* SizeRequirements.java --
2    Copyright (C) 2002, 2005 Free Software Foundation, Inc.
3 
4 This file is part of GNU Classpath.
5 
6 GNU Classpath is free software; you can redistribute it and/or modify
7 it under the terms of the GNU General Public License as published by
8 the Free Software Foundation; either version 2, or (at your option)
9 any later version.
10 
11 GNU Classpath is distributed in the hope that it will be useful, but
12 WITHOUT ANY WARRANTY; without even the implied warranty of
13 MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
14 General Public License for more details.
15 
16 You should have received a copy of the GNU General Public License
17 along with GNU Classpath; see the file COPYING.  If not, write to the
18 Free Software Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA
19 02110-1301 USA.
20 
21 Linking this library statically or dynamically with other modules is
22 making a combined work based on this library.  Thus, the terms and
23 conditions of the GNU General Public License cover the whole
24 combination.
25 
26 As a special exception, the copyright holders of this library give you
27 permission to link this library with independent modules to produce an
28 executable, regardless of the license terms of these independent
29 modules, and to copy and distribute the resulting executable under
30 terms of your choice, provided that you also meet, for each linked
31 independent module, the terms and conditions of the license of that
32 module.  An independent module is a module which is not derived from
33 or based on this library.  If you modify this library, you may extend
34 this exception to your version of the library, but you are not
35 obligated to do so.  If you do not wish to do so, delete this
36 exception statement from your version. */
37 
38 package javax.swing;
39 
40 import java.io.Serializable;
41 
42 /**
43  * This class calculates information about the size and position requirements
44  * of components.
45  *
46  * Two types of layout are supported:
47  * <ul>
48  * <li>Tiled: the components are placed at position top-left or bottom-right
49  *    position within their allocated space</li>
50  * <li>Aligned: the components are placed aligned in their allocated space
51  *    according to their alignment value</li>
52  * </ul>
53  *
54  * @author Andrew Selkirk
55  * @author Roman Kennke (roman@kennke.org)
56  */
57 public class SizeRequirements implements Serializable
58 {
59   /**
60    * The serialVersionUID.
61    */
62   private static final long serialVersionUID = 9217749429906736553L;
63 
64   /**
65    * The minimum reasonable width or height of a component.
66    */
67   public int minimum;
68 
69   /**
70    * The preferred width or height of a component.
71    */
72   public int preferred;
73 
74   /**
75    * The maximum reasonable width or height of a component.
76    */
77   public int maximum;
78 
79   /**
80    * The horizontal or vertical alignment of a component.
81    */
82   public float alignment;
83 
84   /**
85    * Creates a SizeRequirements object with minimum, preferred and
86    * maximum size set to zero, and an alignment value of 0.5.
87    */
SizeRequirements()88   public SizeRequirements()
89   {
90     this (0, 0, 0, 0.5F);
91   }
92 
93   /**
94    * Creates a SizeRequirements object with the specified minimum,
95    * preferred, maximum and alignment values.
96    *
97    * @param min the minimum reasonable size of the component
98    * @param pref the preferred size of the component
99    * @param max the maximum size of the component
100    * @param align the alignment of the component
101    */
SizeRequirements(int min, int pref, int max, float align)102   public SizeRequirements(int min, int pref, int max, float align)
103   {
104     minimum = min;
105     preferred = pref;
106     maximum = max;
107     alignment = align;
108   }
109 
110   /**
111    * Returns a String representation of this SizeRequirements object,
112    * containing information about the minimum, preferred, maximum and
113    * alignment value.
114    *
115    * @return a String representation of this SizeRequirements object
116    */
toString()117   public String toString()
118   {
119     StringBuilder b = new StringBuilder();
120     b.append("<[");
121     b.append(minimum);
122     b.append(',');
123     b.append(preferred);
124     b.append(',');
125     b.append(maximum);
126     b.append("]@");
127     b.append(alignment);
128     b.append('>');
129     return b.toString();
130   }
131 
132   /**
133    * Calculates how much space is nessecary to place a set of components
134    * end-to-end. The size requirements of the components is specified
135    * in <code>children</code>.
136    *
137    * @param children the SizeRequirements of each of the components
138    *
139    * @return the SizeRequirements that describe how much space is needed
140    *     to place the components end-to-end
141    */
142   public static SizeRequirements
getTiledSizeRequirements(SizeRequirements[] children)143   getTiledSizeRequirements(SizeRequirements[] children)
144   {
145     SizeRequirements result = new SizeRequirements();
146     for (int i = 0; i < children.length; i++)
147       {
148         result.minimum += children[i].minimum;
149         result.preferred += children[i].preferred;
150         result.maximum += children[i].maximum;
151       }
152     return result;
153   }
154 
155   /**
156    * Calculates how much space is nessecary to place a set of components
157    * aligned according to their alignment value.
158    * The size requirements of the components is specified in
159    * <code>children</code>.
160    *
161    * @param children the SizeRequirements of each of the components
162    *
163    * @return the SizeRequirements that describe how much space is needed
164    *     to place the components aligned
165    */
166   public static SizeRequirements
getAlignedSizeRequirements(SizeRequirements[] children)167   getAlignedSizeRequirements(SizeRequirements[] children)
168   {
169     float minLeft = 0;
170     float minRight = 0;
171     float prefLeft = 0;
172     float prefRight = 0;
173     float maxLeft = 0;
174     float maxRight = 0;
175     for (int i = 0; i < children.length; i++)
176       {
177         float myMinLeft = children[i].minimum * children[i].alignment;
178         float myMinRight = children[i].minimum - myMinLeft;
179         minLeft = Math.max(myMinLeft, minLeft);
180         minRight = Math.max(myMinRight, minRight);
181         float myPrefLeft = children[i].preferred * children[i].alignment;
182         float myPrefRight = children[i].preferred - myPrefLeft;
183         prefLeft = Math.max(myPrefLeft, prefLeft);
184         prefRight = Math.max(myPrefRight, prefRight);
185         float myMaxLeft = children[i].maximum * children[i].alignment;
186         float myMaxRight = children[i].maximum - myMaxLeft;
187         maxLeft = Math.max(myMaxLeft, maxLeft);
188         maxRight = Math.max(myMaxRight, maxRight);
189       }
190     int minSize = (int) (minLeft + minRight);
191     int prefSize = (int) (prefLeft + prefRight);
192     int maxSize = (int) (maxLeft + maxRight);
193     float align = prefLeft / (prefRight + prefLeft);
194     if (Float.isNaN(align))
195       align = 0;
196     return new SizeRequirements(minSize, prefSize, maxSize, align);
197   }
198 
199   /**
200    * Calculate the offsets and spans of the components, when they should
201    * be placed end-to-end.
202    *
203    * You must specify the amount of allocated space in
204    * <code>allocated</code>, the total size requirements of the set of
205    * components in <code>total</code> (this can be calculated using
206    * {@link #getTiledSizeRequirements} and the size requirements of the
207    * components in <code>children</code>.
208    *
209    * The calculated offset and span values for each component are then
210    * stored in the arrays <code>offsets</code> and <code>spans</code>.
211    *
212    * The components are placed in the forward direction, beginning with
213    * an offset of 0.
214    *
215    * @param allocated the amount of allocated space
216    * @param total the total size requirements of the components
217    * @param children the size requirement of each component
218    * @param offsets will hold the offset values for each component
219    * @param spans will hold the span values for each component
220    */
calculateTiledPositions(int allocated, SizeRequirements total, SizeRequirements[] children, int[] offsets, int[] spans)221   public static void calculateTiledPositions(int allocated,
222                                              SizeRequirements total,
223                                              SizeRequirements[] children,
224                                              int[] offsets, int[] spans)
225   {
226     calculateTiledPositions(allocated, total, children, offsets, spans, true);
227   }
228 
229   /**
230    * Calculate the offsets and spans of the components, when they should
231    * be placed end-to-end.
232    *
233    * You must specify the amount of allocated space in
234    * <code>allocated</code>, the total size requirements of the set of
235    * components in <code>total</code> (this can be calculated using
236    * {@link #getTiledSizeRequirements} and the size requirements of the
237    * components in <code>children</code>.
238    *
239    * The calculated offset and span values for each component are then
240    * stored in the arrays <code>offsets</code> and <code>spans</code>.
241    *
242    * Depending on the value of <code>forward</code> the components are
243    * placed in the forward direction (left-right or top-bottom), where
244    * the offsets begin with 0, or in the reverse direction
245    * (right-left or bottom-top).
246    *
247    * @param allocated the amount of allocated space
248    * @param total the total size requirements of the components
249    * @param children the size requirement of each component
250    * @param offsets will hold the offset values for each component
251    * @param spans will hold the span values for each component
252    * @param forward whether the components should be placed in the forward
253    *     direction (left-right or top-bottom) or reverse direction
254    *     (right-left or bottom-top)
255    */
calculateTiledPositions(int allocated, SizeRequirements total, SizeRequirements[] children, int[] offsets, int[] spans, boolean forward)256   public static void calculateTiledPositions(int allocated,
257                                              SizeRequirements total,
258                                              SizeRequirements[] children,
259                                              int[] offsets, int[] spans,
260                                              boolean forward)
261   {
262     int span = 0;
263     if (forward)
264       {
265         int offset = 0;
266         for (int i = 0; i < children.length; i++)
267           {
268             offsets[i] = offset;
269             spans[i] = children[i].preferred;
270             span += spans[i];
271             offset += children[i].preferred;
272           }
273       }
274     else
275       {
276         int offset = allocated;
277         for (int i = 0; i < children.length; i++)
278           {
279             offset -= children[i].preferred;
280             offsets[i] = offset;
281             span += spans[i];
282             spans[i] = children[i].preferred;
283           }
284       }
285     // Adjust spans so that we exactly fill the allocated region. If
286     if (span > allocated)
287       adjustSmaller(allocated, children, spans, span);
288     else if (span < allocated)
289       adjustGreater(allocated, children, spans, span);
290 
291     // Adjust offsets.
292     if (forward)
293       {
294         int offset = 0;
295         for (int i = 0; i < children.length; i++)
296           {
297             offsets[i] = offset;
298             offset += spans[i];
299           }
300       }
301     else
302       {
303         int offset = allocated;
304         for (int i = 0; i < children.length; i++)
305           {
306             offset -= spans[i];
307             offsets[i] = offset;
308           }
309       }
310   }
311 
adjustSmaller(int allocated, SizeRequirements[] children, int[] spans, int span)312   private static void adjustSmaller(int allocated, SizeRequirements[] children,
313                                     int[] spans, int span)
314   {
315     // Sum up (prefSize - minSize) over all children
316     int sumDelta = 0;
317     for (int i = 0; i < children.length; i++)
318       sumDelta += children[i].preferred - children[i].minimum;
319 
320     // If we have sumDelta == 0, then all components have prefSize == maxSize
321     // and we can't do anything about it.
322     if (sumDelta == 0)
323       return;
324 
325     // Adjust all sizes according to their preferred and minimum sizes.
326     for (int i = 0; i < children.length; i++)
327       {
328         double factor = ((double) (children[i].preferred - children[i].minimum))
329                         / ((double) sumDelta);
330         // In case we have a sumDelta of 0, the factor should also be 0.
331         if (Double.isNaN(factor))
332           factor = 0;
333         spans[i] -= factor * (span - allocated);
334       }
335   }
336 
adjustGreater(int allocated, SizeRequirements[] children, int[] spans, int span)337   private static void adjustGreater(int allocated, SizeRequirements[] children,
338                                     int[] spans, int span)
339   {
340     // Sum up (maxSize - prefSize) over all children
341     int sumDelta = 0;
342     for (int i = 0; i < children.length; i++)
343       {
344         sumDelta += children[i].maximum - children[i].preferred;
345         if (sumDelta < 0)
346           sumDelta = Integer.MAX_VALUE;
347       }
348 
349     // If we have sumDelta == 0, then all components have prefSize == maxSize
350     // and we can't do anything about it.
351     if (sumDelta == 0)
352       return;
353 
354     // Adjust all sizes according to their preferred and minimum sizes.
355     for (int i = 0; i < children.length; i++)
356       {
357         double factor = ((double) (children[i].maximum - children[i].preferred))
358                         / ((double) sumDelta);
359         spans[i] -= factor * (span - allocated);
360       }
361   }
362 
363   /**
364    * Calculate the offsets and spans of the components, when they should
365    * be placed end-to-end.
366    *
367    * You must specify the amount of allocated space in
368    * <code>allocated</code>, the total size requirements of the set of
369    * components in <code>total</code> (this can be calculated using
370    * {@link #getTiledSizeRequirements} and the size requirements of the
371    * components in <code>children</code>.
372    *
373    * The calculated offset and span values for each component are then
374    * stored in the arrays <code>offsets</code> and <code>spans</code>.
375    *
376    * The components are tiled in the forward direction, beginning with
377    * an offset of 0.
378    *
379    * @param allocated the amount of allocated space
380    * @param total the total size requirements of the components
381    * @param children the size requirement of each component
382    * @param offsets will hold the offset values for each component
383    * @param spans will hold the span values for each component
384    */
calculateAlignedPositions(int allocated, SizeRequirements total, SizeRequirements[] children, int[] offsets, int[] spans)385   public static void calculateAlignedPositions(int allocated,
386                                                SizeRequirements total,
387                                                SizeRequirements[] children,
388                                                int[] offsets, int[] spans)
389   {
390     calculateAlignedPositions(allocated, total, children, offsets, spans,
391                               true);
392   }
393 
394   /**
395    * Calculate the offsets and spans of the components, when they should
396    * be placed end-to-end.
397    *
398    * You must specify the amount of allocated space in
399    * <code>allocated</code>, the total size requirements of the set of
400    * components in <code>total</code> (this can be calculated using
401    * {@link #getTiledSizeRequirements} and the size requirements of the
402    * components in <code>children</code>.
403    *
404    * The calculated offset and span values for each component are then
405    * stored in the arrays <code>offsets</code> and <code>spans</code>.
406    *
407    * Depending on the value of <code>forward</code> the components are
408    * placed in the forward direction (left-right or top-bottom), where
409    * the offsets begin with 0, or in the reverse direction
410    * (right-left or bottom-top).
411    *
412    * @param allocated the amount of allocated space
413    * @param total the total size requirements of the components
414    * @param children the size requirement of each component
415    * @param spans will hold the span values for each component
416    * @param forward whether the components should be placed in the forward
417    *     direction (left-right or top-bottom) or reverse direction
418    *     (right-left or bottom-top)
419    */
calculateAlignedPositions(int allocated, SizeRequirements total, SizeRequirements[] children, int[] offset, int[] spans, boolean forward)420   public static void calculateAlignedPositions(int allocated,
421                                                SizeRequirements total,
422                                                SizeRequirements[] children,
423                                                int[] offset, int[] spans,
424                                                boolean forward)
425   {
426     // First we compute the position of the baseline.
427     float baseline = allocated * total.alignment;
428 
429     // Now we can layout the components along the baseline.
430     for (int i = 0; i < children.length; i++)
431       {
432         float align = children[i].alignment;
433         // Try to fit the component into the available space.
434         int[] spanAndOffset = new int[2];
435         if (align < .5F || baseline == 0)
436           adjustFromRight(children[i], baseline, allocated, spanAndOffset);
437         else
438           adjustFromLeft(children[i], baseline, allocated, spanAndOffset);
439         spans[i] = spanAndOffset[0];
440         offset[i] = spanAndOffset[1];
441       }
442   }
443 
444   /**
445    * Adjusts the span and offset of a component for the aligned layout.
446    *
447    * @param reqs
448    * @param baseline
449    * @param allocated
450    * @param spanAndOffset
451    */
adjustFromRight(SizeRequirements reqs, float baseline, int allocated, int[] spanAndOffset)452   private static void adjustFromRight(SizeRequirements reqs, float baseline,
453                                       int allocated, int[] spanAndOffset)
454   {
455     float right = allocated - baseline;
456     // If the resulting span exceeds the maximum of the component, then adjust
457     // accordingly.
458     float maxRight = ((float) reqs.maximum) * (1.F - reqs.alignment);
459     if (right / (1.F - reqs.alignment) > reqs.maximum)
460       right = maxRight;
461     // If we have not enough space on the left side, then adjust accordingly.
462     if (right / (1.F - reqs.alignment) * reqs.alignment > allocated - baseline)
463       right = ((float) (allocated - baseline))
464              / reqs.alignment * (1.F - reqs.alignment);
465 
466     spanAndOffset[0] = (int) (right / (1.F - reqs.alignment));
467     spanAndOffset[1] = (int) (baseline - spanAndOffset[0] * reqs.alignment);
468   }
469 
470   /**
471    * Adjusts the span and offset of a component for the aligned layout.
472    *
473    * @param reqs
474    * @param baseline
475    * @param allocated
476    * @param spanAndOffset
477    */
adjustFromLeft(SizeRequirements reqs, float baseline, int allocated, int[] spanAndOffset)478   private static void adjustFromLeft(SizeRequirements reqs, float baseline,
479                                      int allocated, int[] spanAndOffset)
480   {
481     float left = baseline;
482     // If the resulting span exceeds the maximum of the component, then adjust
483     // accordingly.
484     float maxLeft = ((float) reqs.maximum) * reqs.alignment;
485     if (left / reqs.alignment > reqs.maximum)
486       left = maxLeft;
487     // If we have not enough space on the right side, then adjust accordingly.
488     if (left / reqs.alignment * (1.F - reqs.alignment) > allocated - baseline)
489       left = ((float) (allocated - baseline))
490              / (1.F - reqs.alignment) * reqs.alignment;
491 
492     spanAndOffset[0] = (int) (left / reqs.alignment);
493     spanAndOffset[1] = (int) (baseline - spanAndOffset[0] * reqs.alignment);
494   }
495 
496   /**
497    * Returns an array of new preferred sizes for the children based on
498    * <code>delta</code>. <code>delta</code> specifies a change in the
499    * allocated space. The sizes of the children will be shortened or
500    * lengthened to accomodate the new allocation.
501    *
502    * @param delta the change of the size of the total allocation for
503    *     the components
504    * @param children the size requirements of each component
505    *
506    * @return the new preferred sizes for each component
507    */
adjustSizes(int delta, SizeRequirements[] children)508   public static int[] adjustSizes(int delta, SizeRequirements[] children)
509   {
510     return null; // TODO
511   }
512 }
513