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 }