001: /*
002: #IFNDEF ALT_LICENSE
003: ThinWire(R) RIA Ajax Framework
004: Copyright (C) 2003-2007 Custom Credit Systems
005:
006: This library is free software; you can redistribute it and/or modify it under
007: the terms of the GNU Lesser General Public License as published by the Free
008: Software Foundation; either version 2.1 of the License, or (at your option) any
009: later version.
010:
011: This library is distributed in the hope that it will be useful, but WITHOUT ANY
012: WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS FOR A
013: PARTICULAR PURPOSE. See the GNU Lesser General Public License for more details.
014:
015: You should have received a copy of the GNU Lesser General Public License along
016: with this library; if not, write to the Free Software Foundation, Inc., 59
017: Temple Place, Suite 330, Boston, MA 02111-1307 USA
018:
019: Users who would rather have a commercial license, warranty or support should
020: contact the following company who invented, built and supports the technology:
021:
022: Custom Credit Systems, Richardson, TX 75081, USA.
023: email: info@thinwire.com ph: +1 (888) 644-6405
024: http://www.thinwire.com
025: #ENDIF
026: [ v1.2_RC2 ]
027: */
028: package thinwire.ui.layout;
029:
030: import java.util.List;
031:
032: import thinwire.render.RenderStateEvent;
033: import thinwire.render.RenderStateListener;
034: import thinwire.render.web.WebApplication;
035: import thinwire.ui.*;
036: import thinwire.ui.event.*;
037: import thinwire.ui.style.Style;
038:
039: /**
040: * SplitLayout divides a Container into two sections placing one component on
041: * each side and provides a dragable divider allowing the user to adjust the
042: * split. The split can be either vertical (splitting the Components left and
043: * right) or horizontal (splitting the Components top and bottom).
044: *
045: * <p>
046: * <b>Example:</b> <br>
047: * <img src="doc-files/SplitLayout-1.png"> <br>
048: *
049: * <pre>
050: * Panel p = new Panel();
051: * p.getChildren().add(new Button("Top"));
052: * p.getChildren().add(new Button("Bottom"));
053: * p.setLayout(new SplitLayout(.5));
054: * </pre>
055: *
056: * </p>
057: *
058: * @author Joshua J. Gertzen
059: */
060: public final class SplitLayout extends AbstractLayout {
061: public enum Maximize {
062: NONE, FIRST, SECOND
063: };
064:
065: private WebApplication app;
066:
067: private double split;
068:
069: private boolean splitVertical;
070:
071: private Maximize maximize;
072:
073: private Label spacer;
074:
075: private boolean layoutInProgress;
076:
077: /**
078: * Constructs a horizontal SplitLayout with the first Component having the
079: * specified height, 0px margin and 4px spacing.
080: *
081: * @param split
082: */
083: public SplitLayout(double split) {
084: this (split, false, 0, 4);
085: }
086:
087: /**
088: * Constructs a SplitLayout with the first Component having the specified
089: * size, 0px margin and 4px spacing
090: *
091: * @param split
092: * @param splitVertical
093: */
094: public SplitLayout(double split, boolean splitVertical) {
095: this (split, splitVertical, 0, 4);
096: }
097:
098: /**
099: * Constructs a SplitLayout with the first Component having the specified
100: * size, specified margin and 4px spacing
101: *
102: * @param split
103: * @param splitVertical
104: * @param margin
105: */
106: public SplitLayout(double split, boolean splitVertical, int margin) {
107: this (split, splitVertical, margin, 4);
108: }
109:
110: /**
111: * Constructs a SplitLayout with the first Component having the specified
112: * size, specified margin and specified spacing
113: *
114: * @param split
115: * @param splitVertical
116: * @param margin
117: * @param spacing
118: */
119: public SplitLayout(double split, boolean splitVertical, int margin,
120: int spacing) {
121: app = (WebApplication) Application.current();
122: spacer = new Label();
123: spacer.addPropertyChangeListener(new String[] {
124: Component.PROPERTY_X, Component.PROPERTY_Y },
125: new PropertyChangeListener() {
126: public void propertyChange(PropertyChangeEvent ev) {
127: if (!layoutInProgress
128: && SplitLayout.this .container != null) {
129: double value = (Integer) ev.getNewValue()
130: - SplitLayout.this .margin;
131:
132: if (SplitLayout.this .split < 1) {
133: int contValue = ev.getPropertyName()
134: .equals(Component.PROPERTY_X) ? SplitLayout.this .container
135: .getInnerWidth()
136: : SplitLayout.this .container
137: .getInnerHeight();
138: contValue -= SplitLayout.this .spacing
139: + SplitLayout.this .margin * 2;
140: value = Math.floor(value / contValue
141: * 1000 + .5) / 1000;
142: }
143:
144: SplitLayout.this .setSplit(value);
145: }
146: }
147: });
148:
149: setSplit(split);
150: setSplitVertical(splitVertical);
151: setMargin(margin);
152: setSpacing(spacing);
153: setMaximize(null);
154: setAutoApply(true);
155: }
156:
157: private RenderStateListener spacerListener = new RenderStateListener() {
158: public void renderStateChange(RenderStateEvent ev) {
159: app.clientSideMethodCall("tw_SplitLayout", "newInstance",
160: ev.getId(), margin);
161: }
162: };
163:
164: /**
165: * Associates the specified Container to this layout. (NOTE: This method
166: * should only be called from Container.setLayout())
167: */
168: public void setContainer(Container<Component> container) {
169: if (this .container != null) {
170: app.removeRenderStateListener(spacer, spacerListener);
171: this .container.getChildren().remove(spacer);
172: }
173:
174: if (container != null) {
175: app.addRenderStateListener(spacer, spacerListener);
176: spacer.setVisible(false);
177: container.getChildren().add(spacer);
178: }
179:
180: super .setContainer(container);
181: }
182:
183: /**
184: *
185: * @return the size of the first Component in the Container
186: */
187: public double getSplit() {
188: return split;
189: }
190:
191: /**
192: * Set the size of the first Component in the Container
193: *
194: * @param split
195: */
196: public void setSplit(double split) {
197: this .split = split;
198: if (autoLayout)
199: apply();
200: }
201:
202: /**
203: *
204: * @return true if the split is vertical (Components are left and right)
205: */
206: public boolean isSplitVertical() {
207: return splitVertical;
208: }
209:
210: /**
211: * Sets whether the split is vertical (Components are left and right) or
212: * horizontal (Components are top and bottom)
213: *
214: * @param splitVertical
215: */
216: public void setSplitVertical(boolean splitVertical) {
217: this .splitVertical = splitVertical;
218:
219: if (splitVertical) {
220: this .spacer.setSize(4, 8);
221: } else {
222: this .spacer.setSize(8, 4);
223: }
224:
225: if (autoLayout)
226: apply();
227: }
228:
229: @Override
230: public void setMargin(int margin) {
231: super .setMargin(margin);
232: Integer id = app.getComponentId(spacer);
233: if (id != null)
234: app.clientSideMethodCall("tw_SplitLayout", "setMargin", id,
235: margin);
236: }
237:
238: /**
239: *
240: * @return the Stle object for the spacer
241: */
242: public Style getSpacerStyle() {
243: return spacer.getStyle();
244: }
245:
246: /**
247: *
248: * @return true if one of the components is currently maximized
249: */
250: public boolean isMaximized() {
251: return maximize != Maximize.NONE;
252: }
253:
254: /**
255: * Returns an enum constant telling whether the first, second or neither
256: * component is currently maximized.
257: *
258: * @return
259: */
260: public Maximize getMaximize() {
261: return maximize;
262: }
263:
264: /**
265: * Set either the first or second component to be maximized. Passing
266: * Maximize.NONE or null will restore the layout to the last state.
267: *
268: * @param maximize
269: */
270: public void setMaximize(Maximize maximize) {
271: if (maximize == null)
272: maximize = Maximize.NONE;
273: this .maximize = maximize;
274: if (autoLayout)
275: apply();
276: }
277:
278: protected void update() {
279: if (container == null)
280: return;
281: int innerHeight = container.getInnerHeight();
282: int innerWidth = container.getInnerWidth();
283: if (innerHeight < 10 || innerWidth < 10)
284: return;
285: layoutInProgress = true;
286: int firstSize = (splitVertical ? innerWidth : innerHeight)
287: - margin * 2;
288: int spacing = this .spacing;
289: int secondSize;
290:
291: if (maximize == Maximize.NONE) {
292: firstSize -= spacing;
293: secondSize = firstSize;
294:
295: if (split >= 1) {
296: firstSize = (int) Math.floor(split);
297: } else {
298: firstSize *= split;
299: }
300:
301: secondSize -= firstSize;
302: } else {
303: spacing = 0;
304:
305: if (maximize == Maximize.FIRST) {
306: secondSize = 0;
307: } else {
308: secondSize = firstSize;
309: firstSize = 0;
310: }
311: }
312:
313: List<Component> children = container.getChildren();
314:
315: for (int i = children.size(), cnt = 0; --i >= 0;) {
316: Component c = children.get(i);
317:
318: if (c == spacer) {
319: if (maximize == Maximize.NONE) {
320: if (splitVertical) {
321: c.setBounds(firstSize + margin, margin,
322: spacing, innerHeight - (margin * 2));
323: } else {
324: c.setBounds(margin, firstSize + margin,
325: innerWidth - (margin * 2), spacing);
326: }
327:
328: c.setVisible(true);
329: } else {
330: c.setVisible(false);
331: }
332: } else if (cnt == 0) {
333: if (maximize == Maximize.FIRST) {
334: c.setVisible(false);
335: } else {
336: if (splitVertical) {
337: c.setBounds(firstSize + spacing + margin,
338: margin, secondSize, innerHeight
339: - (margin * 2));
340: } else {
341: c.setBounds(margin, firstSize + spacing
342: + margin, innerWidth - (margin * 2),
343: secondSize);
344: }
345:
346: c.setVisible(true);
347: }
348:
349: cnt++;
350: } else if (cnt == 1) {
351: if (maximize == Maximize.SECOND) {
352: c.setVisible(false);
353: } else {
354: if (splitVertical) {
355: c.setBounds(margin, margin, firstSize,
356: innerHeight - (margin * 2));
357: } else {
358: c.setBounds(margin, margin, innerWidth
359: - (margin * 2), firstSize);
360: }
361:
362: c.setVisible(true);
363: }
364:
365: cnt++;
366: } else {
367: c.setVisible(false);
368: }
369: }
370:
371: layoutInProgress = false;
372: }
373: }
|