001/* Spring.java --
002   Copyright (C) 2004 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.awt.Component;
041import java.awt.Dimension;
042
043/**
044 * Calculates the space between component edges, that are layed out by
045 * {@link SpringLayout}.
046 * <p>
047 * A Spring defines a minimum, preferred and maximum distance for each edge
048 * (north, east, south, west) of a component.
049 * </p>
050 * However, springs are not static, their actual values are computed at
051 * runtime. That means, if a Spring C is defined as the sum of Spring A and
052 * Spring B, then the values (min, pref and max) are not calculated at
053 * creation of Spring C, but instead always when {@link #getValue} is
054 * called. So, when Spring A or Spring B changes, this is reflected in
055 * Spring C.
056 *
057 * @author Roman Kennke (roman@ontographics.com)
058 */
059public abstract class Spring
060{
061
062  /** Indicates a not-set value. **/
063  public static final int UNSET = Integer.MIN_VALUE;
064
065  /**
066   * Creates a new Spring object. This constructor is used by the static
067   * methods which create Springs.
068   */
069  protected Spring()
070  {
071    // Nothing to do here.
072  }
073
074  /**
075   * Creates a Spring which min, pref and max values are all the same.
076   * These kind of Springs are 'struts'.
077   *
078   * @param val the constant for min, pref and max values.
079   * @return a Spring object with constant values for min, pref and max.
080   */
081  public static Spring constant(int val)
082  {
083    return new SimpleSpring(val, val, val);
084  }
085
086  /** Creates a Spring which min, pref and max values are constants.
087   * @param min the constant for the minimum value.
088   * @param pref the constant for the preferred value.
089   * @param max the constant for the maximum value.
090   * @return a Spring object with constant values for min, pref and max.
091   */
092  public static Spring constant(int min, int pref, int max)
093  {
094    return new SimpleSpring(min, pref, max);
095  }
096
097  /**
098   * Returns the maximum value of the Spring.
099   *
100   * @return the maximum value.
101   */
102  public abstract int getMaximumValue();
103
104  /**
105   * Returns the minimum value of this Spring.
106   *
107   * @return the minimum value.
108   */
109  public abstract int getMinimumValue();
110
111  /**
112   * Return the preferred value of this Spring.
113   *
114   * @return the preferred value.
115   */
116  public abstract int getPreferredValue();
117
118  /**
119   * Return the actual value of this Spring.
120   *
121   * @return the actual value of this Spring.
122   */
123  public abstract int getValue();
124
125  /**
126   * Creates and returns a Spring, which always has the maximum values
127   * min = max(min_s1, min_s2), pref = max(pref_s1, pref_s2), max =
128   * max(max_s1, max_s2).
129   *
130   * @param s1 the first summand of the max Spring.
131   * @param s2 the second summand of the max Spring.
132   * @return a Spring which is max(s1, s2).
133   */
134  public static Spring max(Spring s1, Spring s2)
135  {
136    return new MaxSpring(s1, s2);
137  }
138
139  /**
140   * Creates and returns a Spring, which is always the negation of s.
141   * min = -min_s, pref = -pref_s, max = -max_pref.
142   *
143   * @param s the Spring to be negated.
144   * @return the negative of <code>s</code>.
145   */
146  public static Spring minus(Spring s)
147  {
148    return new MinusSpring(s);
149  }
150
151  /**
152   * Sets the actual value. If <code>value</code> is out of the (min, max)
153   * bounds, then the value is adjusted, so that is inside these bounds.
154   *
155   * @param value the value to be set.
156   */
157  public abstract void setValue(int value);
158
159  private int getShrinkRange()
160  {
161    return (getPreferredValue() - getMinimumValue());
162  }
163
164  private int getExpandRange()
165  {
166    return (getMaximumValue() - getPreferredValue());
167  }
168
169  double getStrain()
170  {
171    int v = getValue();
172    int p = getPreferredValue();
173    int r = (v < p) ? getShrinkRange() : getExpandRange();
174    if (r == 0)
175      r = 1;
176    return (double)(v - p) / r;
177  }
178
179  void setStrain(double strain)
180  {
181    int r = (strain < 0) ? getShrinkRange() : getExpandRange();
182    int v = (getPreferredValue() + (int)(strain * r));
183    setValue(v);
184  }
185
186  /**
187   * Creates and returns a Spring, which is always the sum of s1 and s2.
188   * min_sum = min_s1 + min_s2, pref_sum = pref_s1 + pref_s2, max_sum =
189   * max_s1 + max_s2.
190   *
191   * @param s1 the 1st summand of the sum Spring.
192   * @param s2 the 2nd summand of the sum Spring.
193   * @return a sum which is <code>s1 + s2</code>.
194   */
195  public static Spring sum(Spring s1, Spring s2)
196  {
197    return new AddSpring(s1, s2);
198  }
199
200  /**
201   * Return a new Spring which computes its values by scaling
202   * the values of another spring by a constant factor.  If the
203   * factor is negative, the minimum and maximum values of
204   * the argument spring will be interchanged.
205   * @param spring the spring to track
206   * @param factor the factor by which to scale
207   * @return a new multiplicative Spring
208   * @since 1.5
209   */
210  public static Spring scale(final Spring spring, final float factor)
211  {
212    if (spring == null)
213      throw new NullPointerException("spring argument is null");
214    return new Spring()
215    {
216      public int getMaximumValue()
217      {
218        return (int) ((factor < 0 ? spring.getMinimumValue()
219                            : spring.getMaximumValue())
220                      * factor);
221      }
222
223      public int getMinimumValue()
224      {
225        return (int) ((factor < 0 ? spring.getMaximumValue()
226                                  : spring.getMinimumValue())
227                            * factor);
228      }
229
230      public int getPreferredValue()
231      {
232        return (int) (spring.getPreferredValue() * factor);
233      }
234
235      public int getValue()
236      {
237        return (int) (spring.getValue() * factor);
238      }
239
240      public void setValue(int value)
241      {
242        spring.setValue((int) (value / factor));
243      }
244    };
245  }
246
247  /**
248   * Return a new Spring which takes its values from the specified
249   * Component.  In particular, the maximum value is taken from
250   * the maximumSize, the minimum value is taken from the minimumSize,
251   * the preferred value is taken from the preferredSize, and the
252   * value is taken from the component's current size.  These values
253   * change as the component changes size.
254   * @param component the component
255   * @return a new Spring which tracks the component's width
256   * @since 1.5
257   */
258  public static Spring width(final Component component)
259  {
260    return new Spring()
261    {
262      public int getMaximumValue()
263      {
264        return component.getMaximumSize().width;
265      }
266
267      public int getMinimumValue()
268      {
269        return component.getMinimumSize().width;
270      }
271
272      public int getPreferredValue()
273      {
274        return component.getPreferredSize().width;
275      }
276
277      public int getValue()
278      {
279        return component.getSize().width;
280      }
281
282      public void setValue(int value)
283      {
284        Dimension d = component.getSize();
285        component.setSize(value, d.height);
286      }
287    };
288  }
289
290  /**
291   * Return a new Spring which takes its values from the specified
292   * Component.  In particular, the maximum value is taken from
293   * the maximumSize, the minimum value is taken from the minimumSize,
294   * the preferred value is taken from the preferredSize, and the
295   * value is taken from the component's current size.  These values
296   * change as the component changes size.
297   * @param component the component
298   * @return a new Spring which tracks the component's height
299   * @since 1.5
300   */
301  public static Spring height(final Component component)
302  {
303    return new Spring()
304    {
305      public int getMaximumValue()
306      {
307        return component.getMaximumSize().height;
308      }
309
310      public int getMinimumValue()
311      {
312        return component.getMinimumSize().height;
313      }
314
315      public int getPreferredValue()
316      {
317        return component.getPreferredSize().height;
318      }
319
320      public int getValue()
321      {
322        return component.getSize().height;
323      }
324
325      public void setValue(int value)
326      {
327        Dimension d = component.getSize();
328        component.setSize(d.width, value);
329      }
330    };
331  }
332
333  /**
334   * A simple Spring, that holds constant values for min, pref and max.
335   *
336   * @author Roman Kennke (roman@ontographics.com)
337   */
338  private static final class SimpleSpring extends Spring
339  {
340
341    /** The constant value for min. */
342    private final int min;
343
344    /** The constant value for pref. */
345    private final int pref;
346
347    /** The constant value for max. */
348    private final int max;
349
350    /** The actual value of the spring. */
351    private int value;
352
353    public String toString()
354    {
355      return "SimpleSpring of " + value;
356    }
357
358    /**
359     * Creates a new SimpleSpring object.
360     *
361     * @param newMin the constant minimum value.
362     * @param newPref the constant preferred value.
363     * @param newMax the constant maximum value.
364     */
365    public SimpleSpring(int newMin, int newPref, int newMax)
366    {
367      min = newMin;
368      pref = newPref;
369      max = newMax;
370      value = newPref;
371    }
372
373    /**
374     * Returns the maximum value of this Spring.
375     *
376     * @return the maximum value.
377     */
378    public int getMaximumValue()
379    {
380      return max;
381    }
382
383    /**
384     * Returns the minimum value of this Spring.
385     *
386     * @return the minimum value.
387     */
388    public int getMinimumValue()
389    {
390      return min;
391    }
392
393    /**
394     * Returns the preferred value of this Spring.
395     *
396     * @return the preferred value.
397     */
398    public int getPreferredValue()
399    {
400      return pref;
401    }
402
403    /**
404     * Return the actual current value of this Spring.
405     *
406     * @return the current value.
407     */
408    public int getValue()
409    {
410      if (value == Spring.UNSET)
411          return pref;
412      return value;
413    }
414
415    /**
416     * Sets the current value.
417     *
418     * @param val the value to be set.
419     */
420    public void setValue(int val)
421    {
422      value = val;
423    }
424  }
425
426
427  /**
428   * A Spring, that is the sum of two other Springs.
429   *
430   * @author Roman Kennke (roman@ontographics.com)
431   */
432  private static final class AddSpring extends Spring
433  {
434
435    /** The springs, that are the 'operands' of this Spring. */
436    private final Spring s1;
437    private final Spring s2;
438
439    /** The current value for this Spring. */
440    private int value;
441
442    public String toString()
443    {
444      return "AddSpring of " + s1 + " and " + s2;
445    }
446
447    /**
448     * Creates a new AddSpring object.
449     *
450     * @param s1 the first operand.
451     * @param s2 the second operand.
452     */
453    protected AddSpring(Spring s1, Spring s2)
454    {
455      super();
456      this.s1 = s1;
457      this.s2 = s2;
458      value = Spring.UNSET;
459    }
460
461    /**
462     * Returns the maximum value of this Spring.
463     *
464     * @return the maximum value.
465     */
466    public int getMaximumValue()
467    {
468      int max1 = s1.getMaximumValue();
469      int max2 = s2.getMaximumValue();
470      return max1 + max2;
471    }
472
473    /**
474     * Return the minimum value of this Spring.
475     *
476     * @return the minimum value.
477     */
478    public int getMinimumValue()
479    {
480      int min1 = s1.getMinimumValue();
481      int min2 = s2.getMinimumValue();
482      return min1 + min2;
483    }
484
485    /**
486     * Returns the preferred value of this Spring.
487     *
488     * @return the preferred value.
489     */
490    public int getPreferredValue()
491    {
492      int pref1 = s1.getPreferredValue();
493      int pref2 = s2.getPreferredValue();
494      return pref1 + pref2;
495    }
496
497    /**
498     * Returns the actual current value of this Spring.
499     *
500     * @return the current value of this Spring.
501     */
502    public int getValue()
503    {
504      if (value == Spring.UNSET)
505        {
506          int val1 = s1.getValue();
507          int val2 = s2.getValue();
508          value = val1 + val2;
509        }
510      return value;
511    }
512
513    /**
514     * Sets the current value.
515     *
516     * @param val the value to be set.
517     */
518    public void setValue(int val)
519    {
520      if (val == Spring.UNSET)
521      {
522        if (value != Spring.UNSET)
523        {
524          s1.setValue(Spring.UNSET);
525          s2.setValue(Spring.UNSET);
526        }
527        value = Spring.UNSET;
528        return;
529      }
530
531      value = val;
532
533      //Spead the value over the two components
534      double fStrain = getStrain();
535      s1.setStrain(fStrain);
536      int remainder = val - s1.getValue();
537      s2.setValue(remainder);
538    }
539
540  }
541
542
543  /**
544   * A Spring that is calculated as the negation of another Spring.
545   *
546   * @author Roman Kennke (roman@ontographics.com)
547   */
548  private static final class MinusSpring extends Spring
549  {
550
551    /** The Spring from which to calculate the negation. */
552    private final Spring s;
553
554    public String toString()
555    {
556      return "MinusSpring of " + s;
557    }
558
559    /**
560     * Creates a new MinusSpring object.
561     * @param s the Spring from which to calculate the negation.
562     */
563    protected MinusSpring(Spring s)
564    {
565      super();
566      this.s = s;
567    }
568
569    /** Returns the maximum value of this Spring.
570     *
571     * @return the maximum value.
572     */
573    public int getMaximumValue()
574    {
575      return -s.getMinimumValue();
576    }
577
578    /**
579     * Returns the minimum value of this Spring.
580     *
581     * @return the minimum value.
582     */
583    public int getMinimumValue()
584    {
585      return -s.getMaximumValue();
586    }
587
588    /**
589     * Returns the preferred value of this Spring.
590     *
591     * @return the preferred value.
592     */
593    public int getPreferredValue()
594    {
595      return -s.getPreferredValue();
596    }
597
598    /**
599     * Returns the current value of this Spring.
600     *
601     * @return the current value.
602     */
603    public int getValue()
604    {
605      return -s.getValue();
606    }
607
608    /**
609     * Sets the current value.
610     *
611     * @param val the value to be set.
612     */
613    public void setValue(int val)
614    {
615      if (val == Spring.UNSET)
616        s.setValue(Spring.UNSET);
617      else
618        s.setValue(-val);
619    }
620  }
621
622
623  /**
624   * A Spring, that is calculated as the maximum of two Springs.
625   *
626   * @author Roman Kennke (roman@ontographics.com)
627   */
628  private static final class MaxSpring extends Spring
629  {
630
631    /** The two other Springs from which to calculate the maximum. */
632    private final Spring s1;
633    private final Spring s2;
634
635    public String toString()
636    {
637      return "MaxSpring of " + s1 + " and " + s2;
638    }
639
640    /** The current value of this Spring. */
641    private int value;
642
643    /**
644     * Creates a new MaxSpring object.
645     *
646     * @param s1 the 1st operand.
647     * @param s2 the 2nd operand.
648     */
649    protected MaxSpring(Spring s1, Spring s2)
650    {
651      super();
652      this.s1 = s1;
653      this.s2 = s2;
654      value = Spring.UNSET;
655    }
656
657
658    /**
659     * Returns the maximum value of this Spring.
660     *
661     * @return the maximum value.
662     */
663    public int getMaximumValue()
664    {
665      int max1 = s1.getMaximumValue();
666      int max2 = s2.getMaximumValue();
667      return Math.max(max1, max2);
668    }
669
670    /**
671     * Returns the minimum value of this Spring.
672     *
673     * @return the minimum value.
674     */
675    public int getMinimumValue()
676    {
677      int min1 = s1.getMinimumValue();
678      int min2 = s2.getMinimumValue();
679      return Math.max(min1, min2);
680    }
681
682    /**
683     * Returns the preferred value of this Spring.
684     *
685     * @return the preferred value.
686     */
687    public int getPreferredValue()
688    {
689      int pref1 = s1.getPreferredValue();
690      int pref2 = s2.getPreferredValue();
691      return Math.max(pref1, pref2);
692    }
693
694    /**
695     * Returns the actual value of this Spring.
696     *
697     * @return the current value.
698     */
699    public int getValue()
700    {
701      if (value == Spring.UNSET)
702      {
703          int val1 = s1.getValue();
704          int val2 = s2.getValue();
705          value = Math.max(val1, val2);
706      }
707      return value;
708    }
709
710    /**
711     * Sets the current value.
712     *
713     * @param val the value to be set.
714     */
715    public void setValue(int val)
716    {
717      if (val == Spring.UNSET)
718      {
719        if (value != Spring.UNSET)
720        {
721          s1.setValue(Spring.UNSET);
722          s2.setValue(Spring.UNSET);
723        }
724        value = Spring.UNSET;
725        return;
726      }
727
728      value = val;
729
730      int p1 = s1.getPreferredValue();
731      int p2 = s2.getPreferredValue();
732
733      if (p1 < p2)
734      {
735        s1.setValue(Math.min(val, p1));
736        s2.setValue(val);
737      }
738      else
739      {
740        s1.setValue(val);
741        s2.setValue(Math.min(val, p2));
742      }
743    }
744  }
745}