001/* SizeRequirements.java --
002   Copyright (C) 2002, 2005 Free Software Foundation, Inc.
003
004This file is part of GNU Classpath.
005
006GNU Classpath is free software; you can redistribute it and/or modify
007it under the terms of the GNU General Public License as published by
008the Free Software Foundation; either version 2, or (at your option)
009any later version.
010
011GNU Classpath is distributed in the hope that it will be useful, but
012WITHOUT ANY WARRANTY; without even the implied warranty of
013MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
014General Public License for more details.
015
016You should have received a copy of the GNU General Public License
017along with GNU Classpath; see the file COPYING.  If not, write to the
018Free Software Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA
01902110-1301 USA.
020
021Linking this library statically or dynamically with other modules is
022making a combined work based on this library.  Thus, the terms and
023conditions of the GNU General Public License cover the whole
024combination.
025
026As a special exception, the copyright holders of this library give you
027permission to link this library with independent modules to produce an
028executable, regardless of the license terms of these independent
029modules, and to copy and distribute the resulting executable under
030terms of your choice, provided that you also meet, for each linked
031independent module, the terms and conditions of the license of that
032module.  An independent module is a module which is not derived from
033or based on this library.  If you modify this library, you may extend
034this exception to your version of the library, but you are not
035obligated to do so.  If you do not wish to do so, delete this
036exception statement from your version. */
037
038package javax.swing;
039
040import 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 */
057public 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}