KickJava   Java API By Example, From Geeks To Geeks.

Java > Open Source Codes > org > eclipse > swt > layout > FormLayout


1 /*******************************************************************************
2  * Copyright (c) 2000, 2007 IBM Corporation and others.
3  * All rights reserved. This program and the accompanying materials
4  * are made available under the terms of the Eclipse Public License v1.0
5  * which accompanies this distribution, and is available at
6  * http://www.eclipse.org/legal/epl-v10.html
7  *
8  * Contributors:
9  * IBM Corporation - initial API and implementation
10  *******************************************************************************/

11 package org.eclipse.swt.layout;
12
13 import org.eclipse.swt.*;
14 import org.eclipse.swt.graphics.*;
15 import org.eclipse.swt.widgets.*;
16
17 /**
18  * Instances of this class control the position and size of the
19  * children of a composite control by using <code>FormAttachments</code>
20  * to optionally configure the left, top, right and bottom edges of
21  * each child.
22  * <p>
23  * The following example code creates a <code>FormLayout</code> and then sets
24  * it into a <code>Shell</code>:
25  * <pre>
26  * Display display = new Display ();
27  * Shell shell = new Shell(display);
28  * FormLayout layout = new FormLayout();
29  * layout.marginWidth = 3;
30  * layout.marginHeight = 3;
31  * shell.setLayout(layout);
32  * </pre>
33  * </p>
34  * <p>
35  * To use a <code>FormLayout</code>, create a <code>FormData</code> with
36  * <code>FormAttachment</code> for each child of <code>Composite</code>.
37  * The following example code attaches <code>button1</code> to the top
38  * and left edge of the composite and <code>button2</code> to the right
39  * edge of <code>button1</code> and the top and right edges of the
40  * composite:
41  * <pre>
42  * FormData data1 = new FormData();
43  * data1.left = new FormAttachment(0, 0);
44  * data1.top = new FormAttachment(0, 0);
45  * button1.setLayoutData(data1);
46  * FormData data2 = new FormData();
47  * data2.left = new FormAttachment(button1);
48  * data2.top = new FormAttachment(0, 0);
49  * data2.right = new FormAttachment(100, 0);
50  * button2.setLayoutData(data2);
51  * </pre>
52  * </p>
53  * <p>
54  * Each side of a child control can be attached to a position in the parent
55  * composite, or to other controls within the <code>Composite</code> by
56  * creating instances of <code>FormAttachment</code> and setting them into
57  * the top, bottom, left, and right fields of the child's <code>FormData</code>.
58  * </p>
59  * <p>
60  * If a side is not given an attachment, it is defined as not being attached
61  * to anything, causing the child to remain at its preferred size. If a child
62  * is given no attachment on either the left or the right or top or bottom, it is
63  * automatically attached to the left and top of the composite respectively.
64  * The following code positions <code>button1</code> and <code>button2</code>
65  * but relies on default attachments:
66  * <pre>
67  * FormData data2 = new FormData();
68  * data2.left = new FormAttachment(button1);
69  * data2.right = new FormAttachment(100, 0);
70  * button2.setLayoutData(data2);
71  * </pre>
72  * </p>
73  * <p>
74  * IMPORTANT: Do not define circular attachments. For example, do not attach
75  * the right edge of <code>button1</code> to the left edge of <code>button2</code>
76  * and then attach the left edge of <code>button2</code> to the right edge of
77  * <code>button1</code>. This will over constrain the layout, causing undefined
78  * behavior. The algorithm will terminate, but the results are undefined.
79  * </p>
80  *
81  * @see FormData
82  * @see FormAttachment
83  *
84  * @since 2.0
85  *
86  */

87 public final class FormLayout extends Layout {
88     
89     /**
90      * marginWidth specifies the number of pixels of horizontal margin
91      * that will be placed along the left and right edges of the layout.
92      *
93      * The default value is 0.
94      */

95     public int marginWidth = 0;
96     
97     /**
98      * marginHeight specifies the number of pixels of vertical margin
99      * that will be placed along the top and bottom edges of the layout.
100      *
101      * The default value is 0.
102      */

103     public int marginHeight = 0;
104  
105
106     /**
107      * marginLeft specifies the number of pixels of horizontal margin
108      * that will be placed along the left edge of the layout.
109      *
110      * The default value is 0.
111      *
112      * @since 3.1
113      */

114     public int marginLeft = 0;
115
116     /**
117      * marginTop specifies the number of pixels of vertical margin
118      * that will be placed along the top edge of the layout.
119      *
120      * The default value is 0.
121      *
122      * @since 3.1
123      */

124     public int marginTop = 0;
125
126     /**
127      * marginRight specifies the number of pixels of horizontal margin
128      * that will be placed along the right edge of the layout.
129      *
130      * The default value is 0.
131      *
132      * @since 3.1
133      */

134     public int marginRight = 0;
135
136     /**
137      * marginBottom specifies the number of pixels of vertical margin
138      * that will be placed along the bottom edge of the layout.
139      *
140      * The default value is 0.
141      *
142      * @since 3.1
143      */

144     public int marginBottom = 0;
145
146     /**
147      * spacing specifies the number of pixels between the edge of one control
148      * and the edge of its neighbouring control.
149      *
150      * The default value is 0.
151      *
152      * @since 3.0
153      */

154     public int spacing = 0;
155     
156 /**
157  * Constructs a new instance of this class.
158  */

159 public FormLayout () {
160 }
161
162 /*
163  * Computes the preferred height of the form with
164  * respect to the preferred height of the control.
165  *
166  * Given that the equations for top (T) and bottom (B)
167  * of the control in terms of the height of the form (X)
168  * are:
169  * T = AX + B
170  * B = CX + D
171  *
172  * The equation for the height of the control (H)
173  * is bottom (B) minus top (T) or (H = B - T) or:
174  *
175  * H = (CX + D) - (AX + B)
176  *
177  * Solving for (X), the height of the form, we get:
178  *
179  * X = (H + B - D) / (C - A)
180  *
181  * When (A = C), (C - A = 0) and the equation has no
182  * solution for X. This is a special case meaning that
183  * the control does not constrain the height of the
184  * form. In this case, we need to arbitrarily define
185  * the height of the form (X):
186  *
187  * Case 1: A = C, A = 0, C = 0
188  *
189  * Let X = D, the distance from the top of the form
190  * to the bottom edge of the control. In this case,
191  * the control was attached to the top of the form
192  * and the form needs to be large enough to show the
193  * bottom edge of the control.
194  *
195  * Case 2: A = C, A = 1, C = 1
196  *
197  * Let X = -B, the distance from the bottom of the
198  * form to the top edge of the control. In this case,
199  * the control was attached to the bottom of the form
200  * and the only way that the control would be visible
201  * is if the offset is negative. If the offset is
202  * positive, there is no possible height for the form
203  * that will show the control as it will always be
204  * below the bottom edge of the form.
205  *
206  * Case 3: A = C, A != 0, C != 0 and A != 1, C != 0
207  *
208  * Let X = D / (1 - C), the distance from the top of the
209  * form to the bottom edge of the control. In this case,
210  * since C is not 0 or 1, it must be a fraction, U / V.
211  * The offset D is the distance from CX to the bottom edge
212  * of the control. This represents a fraction of the form
213  * (1 - C)X. Since the height of a fraction of the form is
214  * known, the height of the entire form can be found by setting
215  * (1 - C)X = D. We solve this equation for X in terms of U
216  * and V, giving us X = (U * D) / (U - V). Similarly, if the
217  * offset D is negative, the control is positioned above CX.
218  * The offset -B is the distance from the top edge of the control
219  * to CX. We can find the height of the entire form by setting
220  * CX = -B. Solving in terms of U and V gives us X = (-B * V) / U.
221  */

222 int computeHeight (Control control, FormData data, boolean flushCache) {
223     FormAttachment top = data.getTopAttachment (control, spacing, flushCache);
224     FormAttachment bottom = data.getBottomAttachment (control, spacing, flushCache);
225     FormAttachment height = bottom.minus (top);
226     if (height.numerator == 0) {
227         if (bottom.numerator == 0) return bottom.offset;
228         if (bottom.numerator == bottom.denominator) return -top.offset;
229         if (bottom.offset <= 0) {
230             return -top.offset * top.denominator / bottom.numerator;
231         }
232         int divider = bottom.denominator - bottom.numerator;
233         return bottom.denominator * bottom.offset / divider;
234     }
235     return height.solveY (data.getHeight (control, flushCache));
236 }
237
238 protected Point computeSize (Composite composite, int wHint, int hHint, boolean flushCache) {
239     Point size = layout (composite, false, 0, 0, wHint, hHint, flushCache);
240     if (wHint != SWT.DEFAULT) size.x = wHint;
241     if (hHint != SWT.DEFAULT) size.y = hHint;
242     return size;
243 }
244
245 protected boolean flushCache (Control control) {
246     Object JavaDoc data = control.getLayoutData ();
247     if (data != null) ((FormData) data).flushCache ();
248     return true;
249 }
250
251 String JavaDoc getName () {
252     String JavaDoc string = getClass ().getName ();
253     int index = string.lastIndexOf ('.');
254     if (index == -1) return string;
255     return string.substring (index + 1, string.length ());
256 }
257
258 /*
259  * Computes the preferred height of the form with
260  * respect to the preferred height of the control.
261  */

262 int computeWidth (Control control, FormData data, boolean flushCache) {
263     FormAttachment left = data.getLeftAttachment (control, spacing, flushCache);
264     FormAttachment right = data.getRightAttachment (control, spacing, flushCache);
265     FormAttachment width = right.minus (left);
266     if (width.numerator == 0) {
267         if (right.numerator == 0) return right.offset;
268         if (right.numerator == right.denominator) return -left.offset;
269         if (right.offset <= 0) {
270             return -left.offset * left.denominator / left.numerator;
271         }
272         int divider = right.denominator - right.numerator;
273         return right.denominator * right.offset / divider;
274     }
275     return width.solveY (data.getWidth (control, flushCache));
276 }
277
278 protected void layout (Composite composite, boolean flushCache) {
279     Rectangle rect = composite.getClientArea ();
280     int x = rect.x + marginLeft + marginWidth;
281     int y = rect.y + marginTop + marginHeight;
282     int width = Math.max (0, rect.width - marginLeft - 2 * marginWidth - marginRight);
283     int height = Math.max (0, rect.height - marginTop - 2 * marginHeight - marginBottom);
284     layout (composite, true, x, y, width, height, flushCache);
285 }
286
287 Point layout (Composite composite, boolean move, int x, int y, int width, int height, boolean flushCache) {
288     Control [] children = composite.getChildren ();
289     for (int i=0; i<children.length; i++) {
290         Control child = children [i];
291         FormData data = (FormData) child.getLayoutData ();
292         if (data == null) child.setLayoutData (data = new FormData ());
293         if (flushCache) data.flushCache ();
294         data.cacheLeft = data.cacheRight = data.cacheTop = data.cacheBottom = null;
295     }
296     boolean [] flush = null;
297     Rectangle [] bounds = null;
298     int w = 0, h = 0;
299     for (int i=0; i<children.length; i++) {
300         Control child = children [i];
301         FormData data = (FormData) child.getLayoutData ();
302         if (width != SWT.DEFAULT) {
303             data.needed = false;
304             FormAttachment left = data.getLeftAttachment (child, spacing, flushCache);
305             FormAttachment right = data.getRightAttachment (child, spacing, flushCache);
306             int x1 = left.solveX (width), x2 = right.solveX (width);
307             if (data.height == SWT.DEFAULT && !data.needed) {
308                 int trim = 0;
309                 //TEMPORARY CODE
310
if (child instanceof Scrollable) {
311                     Rectangle rect = ((Scrollable) child).computeTrim (0, 0, 0, 0);
312                     trim = rect.width;
313                 } else {
314                     trim = child.getBorderWidth () * 2;
315                 }
316                 data.cacheWidth = data.cacheHeight = -1;
317                 int currentWidth = Math.max (0, x2 - x1 - trim);
318                 data.computeSize (child, currentWidth, data.height, flushCache);
319                 if (flush == null) flush = new boolean [children.length];
320                 flush [i] = true;
321             }
322             w = Math.max (x2, w);
323             if (move) {
324                 if (bounds == null) bounds = new Rectangle [children.length];
325                 bounds [i] = new Rectangle (0, 0, 0, 0);
326                 bounds [i].x = x + x1;
327                 bounds [i].width = x2 - x1;
328             }
329         } else {
330             w = Math.max (computeWidth (child, data, flushCache), w);
331         }
332     }
333     for (int i=0; i<children.length; i++) {
334         Control child = children [i];
335         FormData data = (FormData) child.getLayoutData ();
336         if (height != SWT.DEFAULT) {
337             int y1 = data.getTopAttachment (child, spacing, flushCache).solveX (height);
338             int y2 = data.getBottomAttachment (child, spacing, flushCache).solveX (height);
339             h = Math.max (y2, h);
340             if (move) {
341                 bounds [i].y = y + y1;
342                 bounds [i].height = y2 - y1;
343             }
344         } else {
345             h = Math.max (computeHeight (child, data, flushCache), h);
346         }
347     }
348     for (int i=0; i<children.length; i++) {
349         Control child = children [i];
350         FormData data = (FormData) child.getLayoutData ();
351         if (flush != null && flush [i]) data.cacheWidth = data.cacheHeight = -1;
352         data.cacheLeft = data.cacheRight = data.cacheTop = data.cacheBottom = null;
353     }
354     if (move) {
355         for (int i=0; i<children.length; i++) {
356             children [i].setBounds (bounds [i]);
357         }
358     }
359     w += marginLeft + marginWidth * 2 + marginRight;
360     h += marginTop + marginHeight * 2 + marginBottom;
361     return new Point (w, h);
362 }
363
364 /**
365  * Returns a string containing a concise, human-readable
366  * description of the receiver.
367  *
368  * @return a string representation of the layout
369  */

370 public String JavaDoc toString () {
371     String JavaDoc string = getName ()+" {";
372     if (marginWidth != 0) string += "marginWidth="+marginWidth+" ";
373     if (marginHeight != 0) string += "marginHeight="+marginHeight+" ";
374     if (marginLeft != 0) string += "marginLeft="+marginLeft+" ";
375     if (marginRight != 0) string += "marginRight="+marginRight+" ";
376     if (marginTop != 0) string += "marginTop="+marginTop+" ";
377     if (marginBottom != 0) string += "marginBottom="+marginBottom+" ";
378     if (spacing != 0) string += "spacing="+spacing+" ";
379     string = string.trim();
380     string += "}";
381     return string;
382 }
383 }
384
Popular Tags