001    /* SizeRequirements.java --
002       Copyright (C) 2002, 2005 Free Software Foundation, Inc.
003    
004    This file is part of GNU Classpath.
005    
006    GNU Classpath is free software; you can redistribute it and/or modify
007    it under the terms of the GNU General Public License as published by
008    the Free Software Foundation; either version 2, or (at your option)
009    any later version.
010    
011    GNU Classpath is distributed in the hope that it will be useful, but
012    WITHOUT ANY WARRANTY; without even the implied warranty of
013    MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
014    General Public License for more details.
015    
016    You should have received a copy of the GNU General Public License
017    along with GNU Classpath; see the file COPYING.  If not, write to the
018    Free Software Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA
019    02110-1301 USA.
020    
021    Linking this library statically or dynamically with other modules is
022    making a combined work based on this library.  Thus, the terms and
023    conditions of the GNU General Public License cover the whole
024    combination.
025    
026    As a special exception, the copyright holders of this library give you
027    permission to link this library with independent modules to produce an
028    executable, regardless of the license terms of these independent
029    modules, and to copy and distribute the resulting executable under
030    terms of your choice, provided that you also meet, for each linked
031    independent module, the terms and conditions of the license of that
032    module.  An independent module is a module which is not derived from
033    or based on this library.  If you modify this library, you may extend
034    this exception to your version of the library, but you are not
035    obligated to do so.  If you do not wish to do so, delete this
036    exception statement from your version. */
037    
038    package javax.swing;
039    
040    import java.io.Serializable;
041    
042    /**
043     * This class calculates information about the size and position requirements
044     * of components.
045     *
046     * Two types of layout are supported:
047     * <ul>
048     * <li>Tiled: the components are placed at position top-left or bottom-right
049     *    position within their allocated space</li>
050     * <li>Aligned: the components are placed aligned in their allocated space
051     *    according to their alignment value</li>
052     * </ul>
053     *
054     * @author Andrew Selkirk
055     * @author Roman Kennke (roman@kennke.org)
056     */
057    public class SizeRequirements implements Serializable
058    {
059      /**
060       * The serialVersionUID.
061       */
062      private static final long serialVersionUID = 9217749429906736553L;
063    
064      /**
065       * The minimum reasonable width or height of a component.
066       */
067      public int minimum;
068    
069      /**
070       * The preferred width or height of a component.
071       */
072      public int preferred;
073    
074      /**
075       * The maximum reasonable width or height of a component.
076       */
077      public int maximum;
078    
079      /**
080       * The horizontal or vertical alignment of a component.
081       */
082      public float alignment;
083    
084      /**
085       * Creates a SizeRequirements object with minimum, preferred and
086       * maximum size set to zero, and an alignment value of 0.5.
087       */
088      public SizeRequirements()
089      {
090        this (0, 0, 0, 0.5F);
091      }
092    
093      /**
094       * Creates a SizeRequirements object with the specified minimum,
095       * preferred, maximum and alignment values.
096       *
097       * @param min the minimum reasonable size of the component
098       * @param pref the preferred size of the component
099       * @param max the maximum size of the component
100       * @param align the alignment of the component
101       */
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       */
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
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
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       */
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       */
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    
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    
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       */
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       */
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       */
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       */
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       */
519      public static int[] adjustSizes(int delta, SizeRequirements[] children)
520      {
521        return null; // TODO
522      }
523    }