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     long minimum = 0;
146     long preferred = 0;
147     long maximum = 0;
148     for (int i = 0; i < children.length; i++)
149       {
150         minimum += children[i].minimum;
151         preferred += children[i].preferred;
152         maximum += children[i].maximum;
153       }
154     // Overflow check.
155     if (minimum > Integer.MAX_VALUE)
156       minimum = Integer.MAX_VALUE;
157     if (preferred > Integer.MAX_VALUE)
158       preferred = Integer.MAX_VALUE;
159     if (maximum > Integer.MAX_VALUE)
160       maximum = Integer.MAX_VALUE;
161     SizeRequirements result = new SizeRequirements((int) minimum,
162                                                    (int) preferred,
163                                                    (int) maximum,
164                                                    0.5F);
165     return result;
166   }
167 
168   /**
169    * Calculates how much space is nessecary to place a set of components
170    * aligned according to their alignment value.
171    * The size requirements of the components is specified in
172    * <code>children</code>.
173    *
174    * @param children the SizeRequirements of each of the components
175    *
176    * @return the SizeRequirements that describe how much space is needed
177    *     to place the components aligned
178    */
179   public static SizeRequirements
getAlignedSizeRequirements(SizeRequirements[] children)180   getAlignedSizeRequirements(SizeRequirements[] children)
181   {
182     float minLeft = 0;
183     float minRight = 0;
184     float prefLeft = 0;
185     float prefRight = 0;
186     float maxLeft = 0;
187     float maxRight = 0;
188     for (int i = 0; i < children.length; i++)
189       {
190         float myMinLeft = children[i].minimum * children[i].alignment;
191         float myMinRight = children[i].minimum - myMinLeft;
192         minLeft = Math.max(myMinLeft, minLeft);
193         minRight = Math.max(myMinRight, minRight);
194         float myPrefLeft = children[i].preferred * children[i].alignment;
195         float myPrefRight = children[i].preferred - myPrefLeft;
196         prefLeft = Math.max(myPrefLeft, prefLeft);
197         prefRight = Math.max(myPrefRight, prefRight);
198         float myMaxLeft = children[i].maximum * children[i].alignment;
199         float myMaxRight = children[i].maximum - myMaxLeft;
200         maxLeft = Math.max(myMaxLeft, maxLeft);
201         maxRight = Math.max(myMaxRight, maxRight);
202       }
203     int minSize = (int) (minLeft + minRight);
204     int prefSize = (int) (prefLeft + prefRight);
205     int maxSize = (int) (maxLeft + maxRight);
206     float align = prefLeft / (prefRight + prefLeft);
207     if (Float.isNaN(align))
208       align = 0;
209     return new SizeRequirements(minSize, prefSize, maxSize, align);
210   }
211 
212   /**
213    * Calculate the offsets and spans of the components, when they should
214    * be placed end-to-end.
215    *
216    * You must specify the amount of allocated space in
217    * <code>allocated</code>, the total size requirements of the set of
218    * components in <code>total</code> (this can be calculated using
219    * {@link #getTiledSizeRequirements} and the size requirements of the
220    * components in <code>children</code>.
221    *
222    * The calculated offset and span values for each component are then
223    * stored in the arrays <code>offsets</code> and <code>spans</code>.
224    *
225    * The components are placed in the forward direction, beginning with
226    * an offset of 0.
227    *
228    * @param allocated the amount of allocated space
229    * @param total the total size requirements of the components
230    * @param children the size requirement of each component
231    * @param offsets will hold the offset values for each component
232    * @param spans will hold the span values for each component
233    */
calculateTiledPositions(int allocated, SizeRequirements total, SizeRequirements[] children, int[] offsets, int[] spans)234   public static void calculateTiledPositions(int allocated,
235                                              SizeRequirements total,
236                                              SizeRequirements[] children,
237                                              int[] offsets, int[] spans)
238   {
239     calculateTiledPositions(allocated, total, children, offsets, spans, true);
240   }
241 
242   /**
243    * Calculate the offsets and spans of the components, when they should
244    * be placed end-to-end.
245    *
246    * You must specify the amount of allocated space in
247    * <code>allocated</code>, the total size requirements of the set of
248    * components in <code>total</code> (this can be calculated using
249    * {@link #getTiledSizeRequirements} and the size requirements of the
250    * components in <code>children</code>.
251    *
252    * The calculated offset and span values for each component are then
253    * stored in the arrays <code>offsets</code> and <code>spans</code>.
254    *
255    * Depending on the value of <code>forward</code> the components are
256    * placed in the forward direction (left-right or top-bottom), where
257    * the offsets begin with 0, or in the reverse direction
258    * (right-left or bottom-top).
259    *
260    * @param allocated the amount of allocated space
261    * @param total the total size requirements of the components
262    * @param children the size requirement of each component
263    * @param offsets will hold the offset values for each component
264    * @param spans will hold the span values for each component
265    * @param forward whether the components should be placed in the forward
266    *     direction (left-right or top-bottom) or reverse direction
267    *     (right-left or bottom-top)
268    */
calculateTiledPositions(int allocated, SizeRequirements total, SizeRequirements[] children, int[] offsets, int[] spans, boolean forward)269   public static void calculateTiledPositions(int allocated,
270                                              SizeRequirements total,
271                                              SizeRequirements[] children,
272                                              int[] offsets, int[] spans,
273                                              boolean forward)
274   {
275     int span = 0;
276     if (forward)
277       {
278         int offset = 0;
279         for (int i = 0; i < children.length; i++)
280           {
281             offsets[i] = offset;
282             spans[i] = children[i].preferred;
283             span += spans[i];
284             offset += children[i].preferred;
285           }
286       }
287     else
288       {
289         int offset = allocated;
290         for (int i = 0; i < children.length; i++)
291           {
292             offset -= children[i].preferred;
293             offsets[i] = offset;
294             span += spans[i];
295             spans[i] = children[i].preferred;
296           }
297       }
298     // Adjust spans so that we exactly fill the allocated region. If
299     if (span > allocated)
300       adjustSmaller(allocated, children, spans, span);
301     else if (span < allocated)
302       adjustGreater(allocated, children, spans, span);
303 
304     // Adjust offsets.
305     if (forward)
306       {
307         int offset = 0;
308         for (int i = 0; i < children.length; i++)
309           {
310             offsets[i] = offset;
311             offset += spans[i];
312           }
313       }
314     else
315       {
316         int offset = allocated;
317         for (int i = 0; i < children.length; i++)
318           {
319             offset -= spans[i];
320             offsets[i] = offset;
321           }
322       }
323   }
324 
adjustSmaller(int allocated, SizeRequirements[] children, int[] spans, int span)325   private static void adjustSmaller(int allocated, SizeRequirements[] children,
326                                     int[] spans, int span)
327   {
328     // Sum up (prefSize - minSize) over all children
329     int sumDelta = 0;
330     for (int i = 0; i < children.length; i++)
331       sumDelta += children[i].preferred - children[i].minimum;
332 
333     // If we have sumDelta == 0, then all components have prefSize == maxSize
334     // and we can't do anything about it.
335     if (sumDelta == 0)
336       return;
337 
338     // Adjust all sizes according to their preferred and minimum sizes.
339     for (int i = 0; i < children.length; i++)
340       {
341         double factor = ((double) (children[i].preferred - children[i].minimum))
342                         / ((double) sumDelta);
343         // In case we have a sumDelta of 0, the factor should also be 0.
344         if (Double.isNaN(factor))
345           factor = 0;
346         spans[i] -= factor * (span - allocated);
347       }
348   }
349 
adjustGreater(int allocated, SizeRequirements[] children, int[] spans, int span)350   private static void adjustGreater(int allocated, SizeRequirements[] children,
351                                     int[] spans, int span)
352   {
353     // Sum up (maxSize - prefSize) over all children
354     long sumDelta = 0;
355     for (int i = 0; i < children.length; i++)
356       {
357         sumDelta += children[i].maximum - children[i].preferred;
358       }
359 
360     // If we have sumDelta == 0, then all components have prefSize == maxSize
361     // and we can't do anything about it.
362     if (sumDelta == 0)
363       return;
364 
365     // Adjust all sizes according to their preferred and minimum sizes.
366     for (int i = 0; i < children.length; i++)
367       {
368         double factor = ((double) (children[i].maximum - children[i].preferred))
369                         / ((double) sumDelta);
370         spans[i] += factor * (allocated - span);
371       }
372   }
373 
374   /**
375    * Calculate the offsets and spans of the components, when they should
376    * be placed end-to-end.
377    *
378    * You must specify the amount of allocated space in
379    * <code>allocated</code>, the total size requirements of the set of
380    * components in <code>total</code> (this can be calculated using
381    * {@link #getTiledSizeRequirements} and the size requirements of the
382    * components in <code>children</code>.
383    *
384    * The calculated offset and span values for each component are then
385    * stored in the arrays <code>offsets</code> and <code>spans</code>.
386    *
387    * The components are tiled in the forward direction, beginning with
388    * an offset of 0.
389    *
390    * @param allocated the amount of allocated space
391    * @param total the total size requirements of the components
392    * @param children the size requirement of each component
393    * @param offsets will hold the offset values for each component
394    * @param spans will hold the span values for each component
395    */
calculateAlignedPositions(int allocated, SizeRequirements total, SizeRequirements[] children, int[] offsets, int[] spans)396   public static void calculateAlignedPositions(int allocated,
397                                                SizeRequirements total,
398                                                SizeRequirements[] children,
399                                                int[] offsets, int[] spans)
400   {
401     calculateAlignedPositions(allocated, total, children, offsets, spans,
402                               true);
403   }
404 
405   /**
406    * Calculate the offsets and spans of the components, when they should
407    * be placed end-to-end.
408    *
409    * You must specify the amount of allocated space in
410    * <code>allocated</code>, the total size requirements of the set of
411    * components in <code>total</code> (this can be calculated using
412    * {@link #getTiledSizeRequirements} and the size requirements of the
413    * components in <code>children</code>.
414    *
415    * The calculated offset and span values for each component are then
416    * stored in the arrays <code>offsets</code> and <code>spans</code>.
417    *
418    * Depending on the value of <code>forward</code> the components are
419    * placed in the forward direction (left-right or top-bottom), where
420    * the offsets begin with 0, or in the reverse direction
421    * (right-left or bottom-top).
422    *
423    * @param allocated the amount of allocated space
424    * @param total the total size requirements of the components
425    * @param children the size requirement of each component
426    * @param spans will hold the span values for each component
427    * @param forward whether the components should be placed in the forward
428    *     direction (left-right or top-bottom) or reverse direction
429    *     (right-left or bottom-top)
430    */
calculateAlignedPositions(int allocated, SizeRequirements total, SizeRequirements[] children, int[] offset, int[] spans, boolean forward)431   public static void calculateAlignedPositions(int allocated,
432                                                SizeRequirements total,
433                                                SizeRequirements[] children,
434                                                int[] offset, int[] spans,
435                                                boolean forward)
436   {
437     // First we compute the position of the baseline.
438     float baseline = allocated * total.alignment;
439 
440     // Now we can layout the components along the baseline.
441     for (int i = 0; i < children.length; i++)
442       {
443         float align = children[i].alignment;
444         // Try to fit the component into the available space.
445         int[] spanAndOffset = new int[2];
446         if (align < .5F || baseline == 0)
447           adjustFromRight(children[i], baseline, allocated, spanAndOffset);
448         else
449           adjustFromLeft(children[i], baseline, allocated, spanAndOffset);
450         spans[i] = spanAndOffset[0];
451         offset[i] = spanAndOffset[1];
452       }
453   }
454 
455   /**
456    * Adjusts the span and offset of a component for the aligned layout.
457    *
458    * @param reqs
459    * @param baseline
460    * @param allocated
461    * @param spanAndOffset
462    */
adjustFromRight(SizeRequirements reqs, float baseline, int allocated, int[] spanAndOffset)463   private static void adjustFromRight(SizeRequirements reqs, float baseline,
464                                       int allocated, int[] spanAndOffset)
465   {
466     float right = allocated - baseline;
467     // If the resulting span exceeds the maximum of the component, then adjust
468     // accordingly.
469     float maxRight = ((float) reqs.maximum) * (1.F - reqs.alignment);
470     if (right / (1.F - reqs.alignment) > reqs.maximum)
471       right = maxRight;
472     // If we have not enough space on the left side, then adjust accordingly.
473     if (right / (1.F - reqs.alignment) * reqs.alignment > allocated - baseline)
474       right = ((float) (allocated - baseline))
475              / reqs.alignment * (1.F - reqs.alignment);
476 
477     spanAndOffset[0] = (int) (right / (1.F - reqs.alignment));
478     spanAndOffset[1] = (int) (baseline - spanAndOffset[0] * reqs.alignment);
479   }
480 
481   /**
482    * Adjusts the span and offset of a component for the aligned layout.
483    *
484    * @param reqs
485    * @param baseline
486    * @param allocated
487    * @param spanAndOffset
488    */
adjustFromLeft(SizeRequirements reqs, float baseline, int allocated, int[] spanAndOffset)489   private static void adjustFromLeft(SizeRequirements reqs, float baseline,
490                                      int allocated, int[] spanAndOffset)
491   {
492     float left = baseline;
493     // If the resulting span exceeds the maximum of the component, then adjust
494     // accordingly.
495     float maxLeft = ((float) reqs.maximum) * reqs.alignment;
496     if (left / reqs.alignment > reqs.maximum)
497       left = maxLeft;
498     // If we have not enough space on the right side, then adjust accordingly.
499     if (left / reqs.alignment * (1.F - reqs.alignment) > allocated - baseline)
500       left = ((float) (allocated - baseline))
501              / (1.F - reqs.alignment) * reqs.alignment;
502 
503     spanAndOffset[0] = (int) (left / reqs.alignment);
504     spanAndOffset[1] = (int) (baseline - spanAndOffset[0] * reqs.alignment);
505   }
506 
507   /**
508    * Returns an array of new preferred sizes for the children based on
509    * <code>delta</code>. <code>delta</code> specifies a change in the
510    * allocated space. The sizes of the children will be shortened or
511    * lengthened to accomodate the new allocation.
512    *
513    * @param delta the change of the size of the total allocation for
514    *     the components
515    * @param children the size requirements of each component
516    *
517    * @return the new preferred sizes for each component
518    */
adjustSizes(int delta, SizeRequirements[] children)519   public static int[] adjustSizes(int delta, SizeRequirements[] children)
520   {
521     return null; // TODO
522   }
523 }
524