001    /* Spring.java -- 
002       Copyright (C) 2004 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.awt.Component;
041    import 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     */
059    public 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    }