001: /*
002: * Licensed to the Apache Software Foundation (ASF) under one or more
003: * contributor license agreements. See the NOTICE file distributed with
004: * this work for additional information regarding copyright ownership.
005: * The ASF licenses this file to You under the Apache License, Version 2.0
006: * (the "License"); you may not use this file except in compliance with
007: * the License. You may obtain a copy of the License at
008: *
009: * http://www.apache.org/licenses/LICENSE-2.0
010: *
011: * Unless required by applicable law or agreed to in writing, software
012: * distributed under the License is distributed on an "AS IS" BASIS,
013: * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
014: * See the License for the specific language governing permissions and
015: * limitations under the License.
016: */
017: /**
018: * @author Roman I. Chernyatchik
019: * @version $Revision$
020: */package javax.swing;
021:
022: import java.awt.Component;
023: import java.awt.Container;
024: import java.awt.Dimension;
025: import java.awt.LayoutManager2;
026: import java.util.HashMap;
027: import java.util.HashSet;
028: import java.util.Map;
029: import java.util.Set;
030:
031: public class SpringLayout implements LayoutManager2 {
032: public static class Constraints {
033: private Spring x;
034: private Spring y;
035:
036: private final Spring[] constraintSprings = new Spring[6];
037:
038: private final ConstraintsOrder horizontalConstraintsOrder;
039: private final ConstraintsOrder verticalConstraintsOrder;
040:
041: public Constraints() {
042: this (null, null, null, null);
043: }
044:
045: public Constraints(final Spring x, final Spring y) {
046: this (x, y, null, null);
047: }
048:
049: public Constraints(final Component c) {
050: this (Spring.constant(c.getX()), Spring.constant(c.getY()),
051: Spring.width(c), Spring.height(c));
052: }
053:
054: public Constraints(final Spring x, final Spring y,
055: final Spring width, final Spring height) {
056: horizontalConstraintsOrder = new ConstraintsOrder(
057: EAST_EDGE, WEST_EDGE, WIDTH);
058: constraintSprings[WIDTH] = width;
059: constraintSprings[WEST_EDGE] = x;
060: deriveConstraint(EAST_EDGE);
061:
062: verticalConstraintsOrder = new ConstraintsOrder(SOUTH_EDGE,
063: NORTH_EDGE, HEIGHT);
064: constraintSprings[HEIGHT] = height;
065: constraintSprings[NORTH_EDGE] = y;
066: deriveConstraint(SOUTH_EDGE);
067: }
068:
069: public void setX(final Spring x) {
070: constraintSprings[WEST_EDGE] = x;
071: deriveConstraint(horizontalConstraintsOrder.push(WEST_EDGE));
072: this .x = null;
073: }
074:
075: public Spring getX() {
076: return x == null ? calculateX() : x;
077: }
078:
079: public void setY(final Spring y) {
080: constraintSprings[NORTH_EDGE] = y;
081: deriveConstraint(verticalConstraintsOrder.push(NORTH_EDGE));
082: this .y = null;
083: }
084:
085: public Spring getY() {
086: return y == null ? calculateY() : y;
087: }
088:
089: public void setWidth(final Spring width) {
090: constraintSprings[WIDTH] = width;
091: deriveConstraint(horizontalConstraintsOrder.push(WIDTH));
092: x = null;
093: }
094:
095: public Spring getWidth() {
096: return constraintSprings[WIDTH];
097: }
098:
099: public void setHeight(final Spring height) {
100: constraintSprings[HEIGHT] = height;
101: deriveConstraint(verticalConstraintsOrder.push(HEIGHT));
102: y = null;
103: }
104:
105: public Spring getHeight() {
106: return constraintSprings[HEIGHT];
107: }
108:
109: public void setConstraint(final String edgeName, final Spring s) {
110: final int edge = SpringLayout.getType(edgeName);
111:
112: switch (edge) {
113: case EAST_EDGE:
114: constraintSprings[EAST_EDGE] = s;
115: deriveConstraint(horizontalConstraintsOrder
116: .push(EAST_EDGE));
117: x = null;
118: break;
119: case WEST_EDGE:
120: setX(s);
121: break;
122: case SOUTH_EDGE:
123: constraintSprings[SOUTH_EDGE] = s;
124: deriveConstraint(verticalConstraintsOrder
125: .push(SOUTH_EDGE));
126: y = null;
127: break;
128: case NORTH_EDGE:
129: setY(s);
130: break;
131: default:
132: break;
133: }
134: }
135:
136: public Spring getConstraint(final String edgeName) {
137: final int constraintType = SpringLayout.getType(edgeName);
138: if (constraintType >= 0) {
139: return constraintSprings[constraintType];
140: }
141: return null;
142: }
143:
144: private void deriveConstraint(final byte type) {
145: Spring newValue = null;
146: switch (type) {
147: case WEST_EDGE:
148: if (constraintSprings[EAST_EDGE] != null
149: && getWidth() != null) {
150: newValue = Spring.sum(constraintSprings[EAST_EDGE],
151: Spring.minus(getWidth()));
152: }
153: break;
154: case EAST_EDGE:
155: if (constraintSprings[WEST_EDGE] != null
156: && getWidth() != null) {
157: newValue = Spring.sum(constraintSprings[WEST_EDGE],
158: getWidth());
159: }
160: break;
161: case WIDTH:
162: if (constraintSprings[EAST_EDGE] != null
163: && constraintSprings[WEST_EDGE] != null) {
164:
165: newValue = Spring.sum(constraintSprings[EAST_EDGE],
166: Spring.minus(constraintSprings[WEST_EDGE]));
167: }
168: break;
169: case NORTH_EDGE:
170: if (constraintSprings[SOUTH_EDGE] != null
171: && getHeight() != null) {
172: newValue = Spring.sum(
173: constraintSprings[SOUTH_EDGE], Spring
174: .minus(getHeight()));
175: }
176: break;
177: case SOUTH_EDGE:
178: if (constraintSprings[NORTH_EDGE] != null
179: && getHeight() != null) {
180: newValue = Spring.sum(
181: constraintSprings[NORTH_EDGE], getHeight());
182: }
183: break;
184: case HEIGHT:
185: if (constraintSprings[SOUTH_EDGE] != null
186: && constraintSprings[NORTH_EDGE] != null) {
187:
188: newValue = Spring
189: .sum(
190: constraintSprings[SOUTH_EDGE],
191: Spring
192: .minus(constraintSprings[NORTH_EDGE]));
193: }
194: break;
195: default:
196: return;
197: }
198: constraintSprings[type] = newValue;
199: }
200:
201: private Spring calculateX() {
202: return constraintSprings[WEST_EDGE];
203: }
204:
205: private Spring calculateY() {
206: return constraintSprings[NORTH_EDGE];
207: }
208:
209: private void clearConstraints(final SpringLayout layout) {
210: layout.markedSprings.clear();
211: constraintSprings[WIDTH].setValue(Spring.UNSET);
212: layout.markedSprings.clear();
213: constraintSprings[HEIGHT].setValue(Spring.UNSET);
214:
215: layout.markedSprings.clear();
216: constraintSprings[WEST_EDGE].setValue(Spring.UNSET);
217: layout.markedSprings.clear();
218: constraintSprings[EAST_EDGE].setValue(Spring.UNSET);
219:
220: layout.markedSprings.clear();
221: constraintSprings[NORTH_EDGE].setValue(Spring.UNSET);
222: layout.markedSprings.clear();
223: constraintSprings[SOUTH_EDGE].setValue(Spring.UNSET);
224: }
225: }
226:
227: private static class ConstraintsOrder {
228: private byte[] constraintsOrder = new byte[3];
229: private int offset;
230:
231: public ConstraintsOrder(final byte constraintType1,
232: final byte constrintType2, final byte constrintType3) {
233: push(constraintType1);
234: push(constrintType2);
235: push(constrintType3);
236: }
237:
238: public byte push(final byte constraintType) {
239: final int nextOffset = (offset + 1) % 3;
240: final int prevOffset = (offset + 2) % 3;
241: final byte oldConstraintType = constraintsOrder[nextOffset];
242:
243: if (oldConstraintType == constraintType) {
244: offset = nextOffset;
245: return constraintsOrder[prevOffset];
246: }
247: if (peek() != constraintType) {
248: if (constraintsOrder[prevOffset] == constraintType) {
249: constraintsOrder[prevOffset] = oldConstraintType;
250: }
251: constraintsOrder[nextOffset] = constraintType;
252:
253: offset = nextOffset;
254: }
255: return oldConstraintType;
256: }
257:
258: public byte peek() {
259: return constraintsOrder[offset];
260: }
261: }
262:
263: private static class ProxySpring extends Spring {
264: private final byte edgeType;
265: private final SpringLayout layout;
266: private final Component component;
267:
268: public ProxySpring(final byte edgeType,
269: final Component component, final SpringLayout layout) {
270: this .edgeType = edgeType;
271: this .layout = layout;
272: this .component = component;
273: }
274:
275: @Override
276: public int getMinimumValue() {
277: return getSpring().getMinimumValue();
278: }
279:
280: @Override
281: public int getPreferredValue() {
282: return getSpring().getPreferredValue();
283: }
284:
285: @Override
286: public int getMaximumValue() {
287: return getSpring().getMaximumValue();
288: }
289:
290: @Override
291: public int getValue() {
292: final Spring s = getSpring();
293: if (layout.calculatedSprings.containsKey(s)) {
294: return layout.calculatedSprings.get(s).intValue();
295: }
296: if (layout.markedSprings.contains(s)) {
297: return 0;
298: }
299: layout.markedSprings.add(s);
300: final int value = s.getValue();
301: layout.calculatedSprings.put(s, new Integer(value));
302: return value;
303: }
304:
305: @Override
306: public void setValue(final int value) {
307: final Spring s = getSpring();
308: if (layout.markedSprings.contains(s)) {
309: return;
310: }
311: layout.markedSprings.add(s);
312: s.setValue(value);
313: }
314:
315: @Override
316: public String toString() {
317: String edgeName;
318: switch (edgeType) {
319: case WEST_EDGE:
320: edgeName = "WEST";
321: break;
322: case EAST_EDGE:
323: edgeName = "EAST";
324: break;
325: case NORTH_EDGE:
326: edgeName = "NORTH";
327: break;
328: case SOUTH_EDGE:
329: edgeName = "SOUTH";
330: break;
331: default:
332: edgeName = "";
333: break;
334: }
335: return "[ProxySpring for " + edgeName
336: + " edge of component "
337: + component.getClass().getName() + "]";
338: }
339:
340: private Spring getSpring() {
341: switch (edgeType) {
342: case WEST_EDGE:
343: return layout.getConstraints(component).getX();
344: case EAST_EDGE:
345: return Spring.sum(layout.getConstraints(component)
346: .getX(), layout.getConstraints(component)
347: .getWidth());
348: case NORTH_EDGE:
349: return layout.getConstraints(component).getY();
350:
351: case SOUTH_EDGE:
352: return Spring.sum(layout.getConstraints(component)
353: .getY(), layout.getConstraints(component)
354: .getHeight());
355: default:
356: return null;
357: }
358: }
359: }
360:
361: public static final String WEST = "West";
362: public static final String EAST = "East";
363: public static final String NORTH = "North";
364: public static final String SOUTH = "South";
365:
366: private static final float CENTERED = 0.5f;
367: private static final byte WEST_EDGE = 0;
368: private static final byte EAST_EDGE = 1;
369: private static final byte NORTH_EDGE = 2;
370: private static final byte SOUTH_EDGE = 3;
371: private static final byte WIDTH = 4;
372: private static final byte HEIGHT = 5;
373:
374: private Map<Spring, Integer> calculatedSprings = new HashMap<Spring, Integer>();
375: private Map<Component, Constraints> constraintsMap = new HashMap<Component, Constraints>();
376: private Set<Spring> markedSprings = new HashSet<Spring>();
377:
378: public SpringLayout() {
379: }
380:
381: public void addLayoutComponent(final String name, final Component c) {
382: // Specified by LayoutManager2 but is not used
383: }
384:
385: public void removeLayoutComponent(final Component c) {
386: constraintsMap.remove(c);
387: }
388:
389: public Dimension minimumLayoutSize(final Container container) {
390: Constraints targetConstraints = getConstraints(container);
391: initTargetConstrains(container, targetConstraints);
392:
393: return new Dimension(targetConstraints.getWidth()
394: .getMinimumValue()
395: + container.getInsets().left
396: + container.getInsets().right,
397:
398: targetConstraints.getHeight().getMinimumValue()
399: + container.getInsets().top
400: + container.getInsets().bottom);
401: }
402:
403: public Dimension preferredLayoutSize(final Container container) {
404: Constraints targetConstraints = getConstraints(container);
405: initTargetConstrains(container, targetConstraints);
406: return new Dimension(targetConstraints.getWidth()
407: .getPreferredValue()
408: + container.getInsets().left
409: + container.getInsets().right,
410:
411: targetConstraints.getHeight().getPreferredValue()
412: + container.getInsets().top
413: + container.getInsets().bottom);
414: }
415:
416: public Dimension maximumLayoutSize(final Container container) {
417: Constraints targetConstraints = getConstraints(container);
418: initTargetConstrains(container, targetConstraints);
419:
420: return new Dimension(targetConstraints.getWidth()
421: .getMaximumValue()
422: + container.getInsets().left
423: + container.getInsets().right,
424:
425: targetConstraints.getHeight().getMaximumValue()
426: + container.getInsets().top
427: + container.getInsets().bottom);
428: }
429:
430: public void addLayoutComponent(final Component component,
431: final Object constraints) {
432: if (constraints != null && constraints instanceof Constraints) {
433: constraintsMap.put(component, (Constraints) constraints);
434: }
435: }
436:
437: public float getLayoutAlignmentX(final Container p) {
438: return CENTERED;
439: }
440:
441: public float getLayoutAlignmentY(final Container p) {
442: return CENTERED;
443: }
444:
445: public void invalidateLayout(final Container p) {
446: //Do nothing
447: }
448:
449: public void putConstraint(final String edge1,
450: final Component component1, final int pad,
451: final String edge2, final Component component2) {
452:
453: putConstraint(edge1, component1, Spring.constant(pad), edge2,
454: component2);
455: }
456:
457: public void putConstraint(final String edge1,
458: final Component component1, final Spring pad,
459: final String edge2, final Component component2) {
460:
461: Constraints constraints1 = getConstraints(component1);
462:
463: final byte edge1Type = getType(edge1);
464: final byte edge2Type = getType(edge2);
465:
466: final boolean edge1IsHorizontal = edge1Type == EAST_EDGE
467: || edge1Type == WEST_EDGE;
468: final boolean edge2IsHorizontal = edge2Type == EAST_EDGE
469: || edge2Type == WEST_EDGE;
470:
471: if ((edge1IsHorizontal && edge2IsHorizontal)
472: || (!edge1IsHorizontal && !edge2IsHorizontal)) {
473:
474: constraints1.setConstraint(edge1, Spring.sum(
475: new ProxySpring(edge2Type, component2, this ), pad));
476: }
477: }
478:
479: public Constraints getConstraints(final Component component) {
480: Constraints constraints = constraintsMap.get(component);
481: if (constraints != null) {
482: return constraints;
483: }
484:
485: constraints = new Constraints(Spring.constant(0), Spring
486: .constant(0), Spring.width(component), Spring
487: .height(component));
488: constraintsMap.put(component, constraints);
489: return constraints;
490: }
491:
492: public Spring getConstraint(final String edgeName,
493: final Component component) {
494:
495: return new ProxySpring(getType(edgeName), component, this );
496: }
497:
498: public void layoutContainer(final Container container) {
499: Component component;
500: Constraints constraints;
501:
502: Constraints targetConstraints = getConstraints(container);
503: initTargetConstrains(container, targetConstraints);
504: targetConstraints.clearConstraints(this );
505:
506: if (container.getLayout() != this ) {
507: return;
508: }
509:
510: for (int i = 0; i < container.getComponentCount(); i++) {
511: getConstraints(container.getComponent(i)).clearConstraints(
512: this );
513: }
514: calculatedSprings.clear();
515:
516: targetConstraints.getWidth().setValue(container.getWidth());
517: targetConstraints.getHeight().setValue(container.getHeight());
518:
519: for (int i = 0; i < container.getComponentCount(); i++) {
520: component = container.getComponent(i);
521: constraints = getConstraints(component);
522:
523: component.setBounds(getValue(constraints.getX()),
524: getValue(constraints.getY()), getValue(constraints
525: .getWidth()), getValue(constraints
526: .getHeight()));
527: }
528: }
529:
530: private static byte getType(final String edgeName) {
531: if (EAST.equals(edgeName)) {
532: return EAST_EDGE;
533: } else if (WEST.equals(edgeName)) {
534: return WEST_EDGE;
535: } else if (NORTH.equals(edgeName)) {
536: return NORTH_EDGE;
537: } else if (SOUTH.equals(edgeName)) {
538: return SOUTH_EDGE;
539: }
540: return -1;
541: }
542:
543: private int getValue(final Spring s) {
544: if (!calculatedSprings.containsKey(s)) {
545: markedSprings.clear();
546: final int value = s.getValue();
547: calculatedSprings.put(s, new Integer(value));
548: return value;
549: }
550: return calculatedSprings.get(s).intValue();
551: }
552:
553: private void initTargetConstrains(final Container target,
554: final Constraints targetConstraints) {
555: targetConstraints.setX(Spring.constant(0));
556: targetConstraints.setY(Spring.constant(0));
557:
558: Spring width = targetConstraints.getWidth();
559: if (width instanceof Spring.WidthSpring) {
560: if (((Spring.WidthSpring) width).component == target) {
561: targetConstraints.setWidth(Spring.constant(0, 0,
562: Integer.MAX_VALUE));
563: }
564: }
565:
566: Spring height = targetConstraints.getHeight();
567: if (height instanceof Spring.HeightSpring) {
568: if (((Spring.HeightSpring) height).component == target) {
569: targetConstraints.setHeight(Spring.constant(0, 0,
570: Integer.MAX_VALUE));
571: }
572: }
573: }
574: }
|