001: /*******************************************************************************
002: * Copyright (c) 2000, 2006 IBM Corporation and others.
003: * All rights reserved. This program and the accompanying materials
004: * are made available under the terms of the Eclipse Public License v1.0
005: * which accompanies this distribution, and is available at
006: * http://www.eclipse.org/legal/epl-v10.html
007: *
008: * Contributors:
009: * IBM Corporation - initial API and implementation
010: *******************************************************************************/package org.eclipse.swt.layout;
011:
012: import org.eclipse.swt.*;
013: import org.eclipse.swt.graphics.*;
014: import org.eclipse.swt.widgets.*;
015:
016: /**
017: * Instances of this class determine the size and position of the
018: * children of a <code>Composite</code> by placing them either in
019: * horizontal rows or vertical columns within the parent <code>Composite</code>.
020: * <p>
021: * <code>RowLayout</code> aligns all controls in one row if the
022: * <code>type</code> is set to horizontal, and one column if it is
023: * set to vertical. It has the ability to wrap, and provides configurable
024: * margins and spacing. <code>RowLayout</code> has a number of configuration
025: * fields. In addition, the height and width of each control in a
026: * <code>RowLayout</code> can be specified by setting a <code>RowData</code>
027: * object into the control using <code>setLayoutData ()</code>.
028: * </p>
029: * <p>
030: * The following example code creates a <code>RowLayout</code>, sets all
031: * of its fields to non-default values, and then sets it into a
032: * <code>Shell</code>.
033: * <pre>
034: * RowLayout rowLayout = new RowLayout();
035: * rowLayout.wrap = false;
036: * rowLayout.pack = false;
037: * rowLayout.justify = true;
038: * rowLayout.type = SWT.VERTICAL;
039: * rowLayout.marginLeft = 5;
040: * rowLayout.marginTop = 5;
041: * rowLayout.marginRight = 5;
042: * rowLayout.marginBottom = 5;
043: * rowLayout.spacing = 0;
044: * shell.setLayout(rowLayout);
045: * </pre>
046: * If you are using the default field values, you only need one line of code:
047: * <pre>
048: * shell.setLayout(new RowLayout());
049: * </pre>
050: * </p>
051: *
052: * @see RowData
053: */
054: public final class RowLayout extends Layout {
055:
056: /**
057: * type specifies whether the layout places controls in rows or
058: * columns.
059: *
060: * The default value is HORIZONTAL.
061: *
062: * Possible values are: <ul>
063: * <li>HORIZONTAL: Position the controls horizontally from left to right</li>
064: * <li>VERTICAL: Position the controls vertically from top to bottom</li>
065: * </ul>
066: *
067: * @since 2.0
068: */
069: public int type = SWT.HORIZONTAL;
070:
071: /**
072: * marginWidth specifies the number of pixels of horizontal margin
073: * that will be placed along the left and right edges of the layout.
074: *
075: * The default value is 0.
076: *
077: * @since 3.0
078: */
079: public int marginWidth = 0;
080:
081: /**
082: * marginHeight specifies the number of pixels of vertical margin
083: * that will be placed along the top and bottom edges of the layout.
084: *
085: * The default value is 0.
086: *
087: * @since 3.0
088: */
089: public int marginHeight = 0;
090:
091: /**
092: * spacing specifies the number of pixels between the edge of one cell
093: * and the edge of its neighbouring cell.
094: *
095: * The default value is 3.
096: */
097: public int spacing = 3;
098:
099: /**
100: * wrap specifies whether a control will be wrapped to the next
101: * row if there is insufficient space on the current row.
102: *
103: * The default value is true.
104: */
105: public boolean wrap = true;
106:
107: /**
108: * pack specifies whether all controls in the layout take
109: * their preferred size. If pack is false, all controls will
110: * have the same size which is the size required to accommodate the
111: * largest preferred height and the largest preferred width of all
112: * the controls in the layout.
113: *
114: * The default value is true.
115: */
116: public boolean pack = true;
117:
118: /**
119: * fill specifies whether the controls in a row should be
120: * all the same height for horizontal layouts, or the same
121: * width for vertical layouts.
122: *
123: * The default value is false.
124: *
125: * @since 3.0
126: */
127: public boolean fill = false;
128:
129: /**
130: * justify specifies whether the controls in a row should be
131: * fully justified, with any extra space placed between the controls.
132: *
133: * The default value is false.
134: */
135: public boolean justify = false;
136:
137: /**
138: * marginLeft specifies the number of pixels of horizontal margin
139: * that will be placed along the left edge of the layout.
140: *
141: * The default value is 3.
142: */
143: public int marginLeft = 3;
144:
145: /**
146: * marginTop specifies the number of pixels of vertical margin
147: * that will be placed along the top edge of the layout.
148: *
149: * The default value is 3.
150: */
151: public int marginTop = 3;
152:
153: /**
154: * marginRight specifies the number of pixels of horizontal margin
155: * that will be placed along the right edge of the layout.
156: *
157: * The default value is 3.
158: */
159: public int marginRight = 3;
160:
161: /**
162: * marginBottom specifies the number of pixels of vertical margin
163: * that will be placed along the bottom edge of the layout.
164: *
165: * The default value is 3.
166: */
167: public int marginBottom = 3;
168:
169: /**
170: * Constructs a new instance of this class.
171: */
172: public RowLayout() {
173: }
174:
175: /**
176: * Constructs a new instance of this class given the type.
177: *
178: * @param type the type of row layout
179: *
180: * @since 2.0
181: */
182: public RowLayout(int type) {
183: this .type = type;
184: }
185:
186: protected Point computeSize(Composite composite, int wHint,
187: int hHint, boolean flushCache) {
188: Point extent;
189: if (type == SWT.HORIZONTAL) {
190: extent = layoutHorizontal(composite, false,
191: (wHint != SWT.DEFAULT) && wrap, wHint, flushCache);
192: } else {
193: extent = layoutVertical(composite, false,
194: (hHint != SWT.DEFAULT) && wrap, hHint, flushCache);
195: }
196: if (wHint != SWT.DEFAULT)
197: extent.x = wHint;
198: if (hHint != SWT.DEFAULT)
199: extent.y = hHint;
200: return extent;
201: }
202:
203: Point computeSize(Control control, boolean flushCache) {
204: int wHint = SWT.DEFAULT, hHint = SWT.DEFAULT;
205: RowData data = (RowData) control.getLayoutData();
206: if (data != null) {
207: wHint = data.width;
208: hHint = data.height;
209: }
210: return control.computeSize(wHint, hHint, flushCache);
211: }
212:
213: protected boolean flushCache(Control control) {
214: return true;
215: }
216:
217: String getName() {
218: String string = getClass().getName();
219: int index = string.lastIndexOf('.');
220: if (index == -1)
221: return string;
222: return string.substring(index + 1, string.length());
223: }
224:
225: protected void layout(Composite composite, boolean flushCache) {
226: Rectangle clientArea = composite.getClientArea();
227: if (type == SWT.HORIZONTAL) {
228: layoutHorizontal(composite, true, wrap, clientArea.width,
229: flushCache);
230: } else {
231: layoutVertical(composite, true, wrap, clientArea.height,
232: flushCache);
233: }
234: }
235:
236: Point layoutHorizontal(Composite composite, boolean move,
237: boolean wrap, int width, boolean flushCache) {
238: Control[] children = composite.getChildren();
239: int count = 0;
240: for (int i = 0; i < children.length; i++) {
241: Control control = children[i];
242: RowData data = (RowData) control.getLayoutData();
243: if (data == null || !data.exclude) {
244: children[count++] = children[i];
245: }
246: }
247: if (count == 0) {
248: return new Point(
249: marginLeft + marginWidth * 2 + marginRight,
250: marginTop + marginHeight * 2 + marginBottom);
251: }
252: int childWidth = 0, childHeight = 0, maxHeight = 0;
253: if (!pack) {
254: for (int i = 0; i < count; i++) {
255: Control child = children[i];
256: Point size = computeSize(child, flushCache);
257: childWidth = Math.max(childWidth, size.x);
258: childHeight = Math.max(childHeight, size.y);
259: }
260: maxHeight = childHeight;
261: }
262: int clientX = 0, clientY = 0;
263: if (move) {
264: Rectangle rect = composite.getClientArea();
265: clientX = rect.x;
266: clientY = rect.y;
267: }
268: int[] wraps = null;
269: boolean wrapped = false;
270: Rectangle[] bounds = null;
271: if (move && (justify || fill)) {
272: bounds = new Rectangle[count];
273: wraps = new int[count];
274: }
275: int maxX = 0, x = marginLeft + marginWidth, y = marginTop
276: + marginHeight;
277: for (int i = 0; i < count; i++) {
278: Control child = children[i];
279: if (pack) {
280: Point size = computeSize(child, flushCache);
281: childWidth = size.x;
282: childHeight = size.y;
283: }
284: if (wrap && (i != 0) && (x + childWidth > width)) {
285: wrapped = true;
286: if (move && (justify || fill))
287: wraps[i - 1] = maxHeight;
288: x = marginLeft + marginWidth;
289: y += spacing + maxHeight;
290: if (pack)
291: maxHeight = 0;
292: }
293: if (pack || fill) {
294: maxHeight = Math.max(maxHeight, childHeight);
295: }
296: if (move) {
297: int childX = x + clientX, childY = y + clientY;
298: if (justify || fill) {
299: bounds[i] = new Rectangle(childX, childY,
300: childWidth, childHeight);
301: } else {
302: child.setBounds(childX, childY, childWidth,
303: childHeight);
304: }
305: }
306: x += spacing + childWidth;
307: maxX = Math.max(maxX, x);
308: }
309: maxX = Math.max(clientX + marginLeft + marginWidth, maxX
310: - spacing);
311: if (!wrapped)
312: maxX += marginRight + marginWidth;
313: if (move && (justify || fill)) {
314: int space = 0, margin = 0;
315: if (!wrapped) {
316: space = Math.max(0, (width - maxX) / (count + 1));
317: margin = Math
318: .max(0, ((width - maxX) % (count + 1)) / 2);
319: } else {
320: if (fill || justify) {
321: int last = 0;
322: if (count > 0)
323: wraps[count - 1] = maxHeight;
324: for (int i = 0; i < count; i++) {
325: if (wraps[i] != 0) {
326: int wrapCount = i - last + 1;
327: if (justify) {
328: int wrapX = 0;
329: for (int j = last; j <= i; j++) {
330: wrapX += bounds[j].width + spacing;
331: }
332: space = Math.max(0, (width - wrapX)
333: / (wrapCount + 1));
334: margin = Math
335: .max(
336: 0,
337: ((width - wrapX) % (wrapCount + 1)) / 2);
338: }
339: for (int j = last; j <= i; j++) {
340: if (justify)
341: bounds[j].x += (space * (j - last + 1))
342: + margin;
343: if (fill)
344: bounds[j].height = wraps[i];
345: }
346: last = i + 1;
347: }
348: }
349: }
350: }
351: for (int i = 0; i < count; i++) {
352: if (!wrapped) {
353: if (justify)
354: bounds[i].x += (space * (i + 1)) + margin;
355: if (fill)
356: bounds[i].height = maxHeight;
357: }
358: children[i].setBounds(bounds[i]);
359: }
360: }
361: return new Point(maxX, y + maxHeight + marginBottom
362: + marginHeight);
363: }
364:
365: Point layoutVertical(Composite composite, boolean move,
366: boolean wrap, int height, boolean flushCache) {
367: Control[] children = composite.getChildren();
368: int count = 0;
369: for (int i = 0; i < children.length; i++) {
370: Control control = children[i];
371: RowData data = (RowData) control.getLayoutData();
372: if (data == null || !data.exclude) {
373: children[count++] = children[i];
374: }
375: }
376: if (count == 0) {
377: return new Point(
378: marginLeft + marginWidth * 2 + marginRight,
379: marginTop + marginHeight * 2 + marginBottom);
380: }
381: int childWidth = 0, childHeight = 0, maxWidth = 0;
382: if (!pack) {
383: for (int i = 0; i < count; i++) {
384: Control child = children[i];
385: Point size = computeSize(child, flushCache);
386: childWidth = Math.max(childWidth, size.x);
387: childHeight = Math.max(childHeight, size.y);
388: }
389: maxWidth = childWidth;
390: }
391: int clientX = 0, clientY = 0;
392: if (move) {
393: Rectangle rect = composite.getClientArea();
394: clientX = rect.x;
395: clientY = rect.y;
396: }
397: int[] wraps = null;
398: boolean wrapped = false;
399: Rectangle[] bounds = null;
400: if (move && (justify || fill)) {
401: bounds = new Rectangle[count];
402: wraps = new int[count];
403: }
404: int maxY = 0, x = marginLeft + marginWidth, y = marginTop
405: + marginHeight;
406: for (int i = 0; i < count; i++) {
407: Control child = children[i];
408: if (pack) {
409: Point size = computeSize(child, flushCache);
410: childWidth = size.x;
411: childHeight = size.y;
412: }
413: if (wrap && (i != 0) && (y + childHeight > height)) {
414: wrapped = true;
415: if (move && (justify || fill))
416: wraps[i - 1] = maxWidth;
417: x += spacing + maxWidth;
418: y = marginTop + marginHeight;
419: if (pack)
420: maxWidth = 0;
421: }
422: if (pack || fill) {
423: maxWidth = Math.max(maxWidth, childWidth);
424: }
425: if (move) {
426: int childX = x + clientX, childY = y + clientY;
427: if (justify || fill) {
428: bounds[i] = new Rectangle(childX, childY,
429: childWidth, childHeight);
430: } else {
431: child.setBounds(childX, childY, childWidth,
432: childHeight);
433: }
434: }
435: y += spacing + childHeight;
436: maxY = Math.max(maxY, y);
437: }
438: maxY = Math.max(clientY + marginTop + marginHeight, maxY
439: - spacing);
440: if (!wrapped)
441: maxY += marginBottom + marginHeight;
442: if (move && (justify || fill)) {
443: int space = 0, margin = 0;
444: if (!wrapped) {
445: space = Math.max(0, (height - maxY) / (count + 1));
446: margin = Math.max(0,
447: ((height - maxY) % (count + 1)) / 2);
448: } else {
449: if (fill || justify) {
450: int last = 0;
451: if (count > 0)
452: wraps[count - 1] = maxWidth;
453: for (int i = 0; i < count; i++) {
454: if (wraps[i] != 0) {
455: int wrapCount = i - last + 1;
456: if (justify) {
457: int wrapY = 0;
458: for (int j = last; j <= i; j++) {
459: wrapY += bounds[j].height + spacing;
460: }
461: space = Math.max(0, (height - wrapY)
462: / (wrapCount + 1));
463: margin = Math
464: .max(
465: 0,
466: ((height - wrapY) % (wrapCount + 1)) / 2);
467: }
468: for (int j = last; j <= i; j++) {
469: if (justify)
470: bounds[j].y += (space * (j - last + 1))
471: + margin;
472: if (fill)
473: bounds[j].width = wraps[i];
474: }
475: last = i + 1;
476: }
477: }
478: }
479: }
480: for (int i = 0; i < count; i++) {
481: if (!wrapped) {
482: if (justify)
483: bounds[i].y += (space * (i + 1)) + margin;
484: if (fill)
485: bounds[i].width = maxWidth;
486: }
487: children[i].setBounds(bounds[i]);
488: }
489: }
490: return new Point(x + maxWidth + marginRight + marginWidth, maxY);
491: }
492:
493: /**
494: * Returns a string containing a concise, human-readable
495: * description of the receiver.
496: *
497: * @return a string representation of the layout
498: */
499: public String toString() {
500: String string = getName() + " {";
501: string += "type="
502: + ((type != SWT.HORIZONTAL) ? "SWT.VERTICAL"
503: : "SWT.HORIZONTAL") + " ";
504: if (marginWidth != 0)
505: string += "marginWidth=" + marginWidth + " ";
506: if (marginHeight != 0)
507: string += "marginHeight=" + marginHeight + " ";
508: if (marginLeft != 0)
509: string += "marginLeft=" + marginLeft + " ";
510: if (marginTop != 0)
511: string += "marginTop=" + marginTop + " ";
512: if (marginRight != 0)
513: string += "marginRight=" + marginRight + " ";
514: if (marginBottom != 0)
515: string += "marginBottom=" + marginBottom + " ";
516: if (spacing != 0)
517: string += "spacing=" + spacing + " ";
518: string += "wrap=" + wrap + " ";
519: string += "pack=" + pack + " ";
520: string += "fill=" + fill + " ";
521: string += "justify=" + justify + " ";
522: string = string.trim();
523: string += "}";
524: return string;
525: }
526: }
|