001: /*******************************************************************************
002: * Copyright (c) 2004, 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.ui.internal.layout;
011:
012: import java.util.ArrayList;
013: import java.util.List;
014:
015: import org.eclipse.swt.SWT;
016: import org.eclipse.swt.graphics.Point;
017: import org.eclipse.swt.graphics.Rectangle;
018: import org.eclipse.swt.widgets.Composite;
019: import org.eclipse.swt.widgets.Control;
020: import org.eclipse.swt.widgets.Layout;
021:
022: /**
023: * <p>Instance of this class lay out the control children of a <code>Composite</code>
024: * in a grid, using a simple set of rules and a simple API. This class is
025: * intended to be more predictable than <code>GridLayout</code> and easier to use than
026: * <code>FormLayout</code>, while retaining most of the power of both.</p>
027: *
028: * <p>The power of a <code>CellLayout</code> lies in the ability to control
029: * the size and resizing properties of each row and column. Unlike other layout
030: * classes, complex layouts can be created without attaching any layout data to
031: * individual controls in the layout. </p>
032: *
033: * <p>The various subclasses of <code>IColumnInfo</code>
034: * can be used to create columns with fixed width, columns whose width is computed
035: * from child controls, or width that grows in proportion to the size of other
036: * columns. Layouts can be given a default <code>IColumnInfo</code> that will
037: * be used to set the size of any column whose properties have not been explicitly
038: * set. This is useful for creating layouts where most or all columns have the
039: * same properties. Similarly, the subclasses of <code>IRowInfo</code> can be used to
040: * control the height of individual rows.</p>
041: *
042: * <p>For a finer grain of control, <code>CellData</code> objects can be attached
043: * to individual controls in the layout. These objects serve a similar function as
044: * <code>GridData</code> objects serve for <code>GridLayout</code>. They allow
045: * controls to span multiple rows or columns, set the justification of the control
046: * within its cell, and allow the user to override the preferred size of the control.
047: * </p>
048: *
049: * <p>In many cases, it is not necessary to attach any layout data to controls in
050: * the layout, since the controls can be arranged based on the properties of rows
051: * and columns. However, layout data may be attached to individual controls to
052: * allow them to span multiple columns or to control their justification within
053: * their cell.
054: * </p>
055: *
056: * <p>All the <code>set</code> methods in this class return <code>this</code>, allowing
057: * a layout to be created and initialized in a single line of code. For example: </p>
058: *
059: * <code>
060: * Composite myControl = new Composite(parent, SWT.NONE);
061: * myControl.setLayout(new CellLayout(2).setMargins(10,10).setSpacing(5,5));
062: * </code>
063: *
064: * @since 3.0
065: */
066: public class CellLayout extends Layout {
067:
068: /**
069: * Object used to compute the height of rows whose properties have not been
070: * explicitly set.
071: */
072: private Row defaultRowSettings = new Row(false);
073:
074: /**
075: * Object used to compute the width of columns whose properties have not been
076: * explicitly set.
077: */
078: private Row defaultColSettings = new Row(true);
079:
080: /**
081: * horizontalSpacing specifies the number of pixels between the right
082: * edge of one cell and the left edge of its neighbouring cell to
083: * the right.
084: *
085: * The default value is 5.
086: */
087: int horizontalSpacing = 5;
088:
089: /**
090: * verticalSpacing specifies the number of pixels between the bottom
091: * edge of one cell and the top edge of its neighbouring cell underneath.
092: *
093: * The default value is 5.
094: */
095: int verticalSpacing = 5;
096:
097: /**
098: * marginWidth specifies the number of pixels of horizontal margin
099: * that will be placed along the left and right edges of the layout.
100: *
101: * The default value is 0.
102: */
103: public int marginWidth = 5;
104:
105: /**
106: * marginHeight specifies the number of pixels of vertical margin
107: * that will be placed along the top and bottom edges of the layout.
108: *
109: * The default value is 0.
110: */
111: public int marginHeight = 5;
112:
113: /**
114: * Number of columns in this layout, or 0 indicating that the whole layout
115: * should be on a single row.
116: */
117: private int numCols;
118:
119: /**
120: * List of IColumnInfo. The nth object is used to compute the width of the
121: * nth column, or null indicating that the default column should be used.
122: */
123: private List cols;
124:
125: /**
126: * List of RowInfo. The nth object is used to compute the height of the
127: * nth row, or null indicating that the default row should be used.
128: */
129: private List rows = new ArrayList(16);
130:
131: // Cached information
132: private GridInfo gridInfo = new GridInfo();
133:
134: private int[] cachedRowMin = null;
135:
136: private int[] cachedColMin = null;
137:
138: public static int cacheMisses;
139:
140: public static int cacheHits;
141:
142: private LayoutCache cache = new LayoutCache();
143:
144: // End of cached control sizes
145:
146: /**
147: * Creates the layout
148: *
149: * @param numCols the number of columns in this layout,
150: * or 0 indicating that the whole layout should be on one row.
151: */
152: public CellLayout(int numCols) {
153: super ();
154: this .numCols = numCols;
155: cols = new ArrayList(numCols == 0 ? 3 : numCols);
156: }
157:
158: /**
159: * Sets the amount empty space between cells
160: *
161: * @param newSpacing a point (x,y) corresponding to the number of pixels of
162: * empty space between adjacent columns and rows respectively
163: */
164: public CellLayout setSpacing(int horizontalSpacing,
165: int verticalSpacing) {
166: this .horizontalSpacing = horizontalSpacing;
167: this .verticalSpacing = verticalSpacing;
168:
169: return this ;
170: }
171:
172: /**
173: * Sets the amount empty space between cells
174: *
175: * @param newSpacing a point (x,y) corresponding to the number of pixels of
176: * empty space between adjacent columns and rows respectively
177: */
178: public CellLayout setSpacing(Point newSpacing) {
179: horizontalSpacing = newSpacing.x;
180: verticalSpacing = newSpacing.y;
181: return this ;
182: }
183:
184: /**
185: * Returns the amount of empty space between adjacent cells
186: *
187: * @return a point (x,y) corresponding to the number of pixels of empty
188: * space between adjacent columns and rows respectively
189: */
190: public Point getSpacing() {
191: return new Point(horizontalSpacing, verticalSpacing);
192: }
193:
194: /**
195: * Sets the size of the margin around the outside of the layout.
196: *
197: * @param marginWidth the size of the margin around the top and
198: * bottom of the layout
199: * @param marginHeight the size of the margin on the left and right
200: * of the layout.
201: */
202: public CellLayout setMargins(int marginWidth, int marginHeight) {
203: this .marginWidth = marginWidth;
204: this .marginHeight = marginHeight;
205: return this ;
206: }
207:
208: /**
209: * Sets the size of the margin around the outside of the layout.
210: *
211: * @param newMargins point indicating the size of the horizontal and vertical
212: * margins, in pixels.
213: */
214: public CellLayout setMargins(Point newMargins) {
215: marginWidth = newMargins.x;
216: marginHeight = newMargins.y;
217: return this ;
218: }
219:
220: /**
221: * Returns the size of the margins around the outside of the layout.
222: *
223: * @return the size of the outer margins, in pixels.
224: */
225: public Point getMargins() {
226: return new Point(marginWidth, marginHeight);
227: }
228:
229: /**
230: * Sets the default column settings. All columns will use these settings unless
231: * they have been explicitly assigned custom settings by setColumn.
232: *
233: * @param info the properties of all default columns
234: * @see setColumn
235: */
236: public CellLayout setDefaultColumn(Row info) {
237: defaultColSettings = info;
238: return this ;
239: }
240:
241: /**
242: * Sets the column info for the given column number (the leftmost column is column 0).
243: * This replaces any existing info for the column. Note that more than one column
244: * are allowed to share the same IColumnInfo instance if they have identical properties.
245: *
246: * @param colNum the column number to modify
247: * @param info the properties of the column, or null if this column should use the
248: * default properties
249: */
250: public CellLayout setColumn(int colNum, Row info) {
251: while (cols.size() <= colNum) {
252: cols.add(null);
253: }
254:
255: cols.set(colNum, info);
256:
257: return this ;
258: }
259:
260: /**
261: * Sets the default row settings for this layout. Unless this is overridden
262: * for an individual row, all rows will use the default settings.
263: *
264: * @param info the row info object that should be used to set the size
265: * of rows, by default.
266: */
267: public CellLayout setDefaultRow(Row info) {
268: defaultRowSettings = info;
269:
270: return this ;
271: }
272:
273: /**
274: * Sets the row info for the given rows. The topmost row is row 0. Multiple
275: * rows are allowed to share the same RowInfo instance.
276: *
277: * @param rowNum the row number to set
278: * @param info the row info that will control the sizing of the given row,
279: * or null if the row should use the default settings for this layout.
280: */
281: public CellLayout setRow(int rowNum, Row info) {
282: while (rows.size() <= rowNum) {
283: rows.add(null);
284: }
285:
286: rows.set(rowNum, info);
287:
288: return this ;
289: }
290:
291: /**
292: * Returns the row info that controls the size of the given row. Will return
293: * the default row settings for this layout if no custom row info has been
294: * assigned to the row.
295: *
296: * @param rowNum
297: * @return
298: */
299: private Row getRow(int rowNum, boolean isHorizontal) {
300: if (isHorizontal) {
301: if (rowNum >= rows.size()) {
302: return defaultRowSettings;
303: }
304:
305: Row result = (Row) rows.get(rowNum);
306:
307: if (result == null) {
308: result = defaultRowSettings;
309: }
310:
311: return result;
312: } else {
313: if (rowNum >= cols.size()) {
314: return defaultColSettings;
315: }
316:
317: Row result = (Row) cols.get(rowNum);
318:
319: if (result == null) {
320: result = defaultColSettings;
321: }
322:
323: return result;
324: }
325: }
326:
327: /**
328: * Initializes the gridInfo object.
329: *
330: * @param children controls that are being layed out
331: */
332: private void initGrid(Control[] children) {
333: cache.setControls(children);
334: gridInfo.initGrid(children, this );
335: cachedRowMin = null;
336: cachedColMin = null;
337: }
338:
339: /* (non-Javadoc)
340: * @see org.eclipse.swt.widgets.Layout#computeSize(org.eclipse.swt.widgets.Composite, int, int, boolean)
341: */
342: protected Point computeSize(Composite composite, int wHint,
343: int hHint, boolean flushCache) {
344: Control[] children = composite.getChildren();
345: initGrid(children);
346:
347: if (flushCache) {
348: cache.flush();
349: }
350:
351: // Determine the amount of whitespace (area that cannot be used by controls)
352: Point emptySpace = totalEmptySpace();
353:
354: int[] heightConstraints = computeConstraints(true);
355:
356: int width;
357: if (wHint == SWT.DEFAULT) {
358: width = preferredSize(heightConstraints, false);
359: } else {
360: width = wHint - emptySpace.x;
361: }
362:
363: int height = hHint;
364: if (hHint == SWT.DEFAULT) {
365: height = preferredSize(computeSizes(heightConstraints,
366: width, false), true);
367: } else {
368: height = hHint - emptySpace.y;
369: }
370:
371: Point preferredSize = new Point(width + emptySpace.x, height
372: + emptySpace.y);
373:
374: // At this point we know the layout's preferred size. Now adjust it
375: // if we're smaller than the minimum possible size for the composite.
376:
377: // If exactly one dimension of our preferred size is smaller than
378: // the minimum size of our composite, then set that dimension to
379: // the minimum size and recompute the other dimension (for example,
380: // increasing the width to match a shell's minimum width may reduce
381: // the height allocated for a wrapping text widget). There is no
382: // point in doing this if both dimensions are smaller than the
383: // composite's minimum size, since we're already smaller than
384: // we need to be.
385: Point minimumSize = CellLayoutUtil
386: .computeMinimumSize(composite);
387:
388: boolean wider = (preferredSize.x >= minimumSize.x);
389: boolean taller = (preferredSize.y >= minimumSize.y);
390:
391: if (wider) {
392: if (taller) {
393: // If we're larger in both dimensions, don't adjust the minimum
394: // size.
395: return preferredSize;
396: } else {
397: // If our preferred height is smaller than the minimum height,
398: // recompute the preferred width using the minimum height
399: return computeSize(composite, wHint, minimumSize.y,
400: false);
401: }
402: } else {
403: if (taller) {
404: // If our preferred width is smaller than the minimum width,
405: // recompute the preferred height using the minimum width
406: return computeSize(composite, minimumSize.x, hHint,
407: false);
408: } else {
409: // If both dimensions are smaller than the minimum size,
410: // use the minimum size as our preferred size.
411: return minimumSize;
412: }
413: }
414: }
415:
416: int[] computeSizes(int[] constraints, int availableSpace,
417: boolean computingRows) {
418: int[] result = computeMinSizes(constraints, computingRows);
419:
420: int totalFixed = sumOfSizes(result);
421: int denominator = getResizeDenominator(computingRows);
422: int numRows = gridInfo.getNumRows(computingRows);
423:
424: if (totalFixed < availableSpace) {
425: int remaining = availableSpace - totalFixed;
426:
427: for (int idx = 0; idx < numRows && denominator > 0; idx++) {
428: Row row = getRow(idx, computingRows);
429:
430: if (row.grows) {
431: int greed = row.size;
432: int amount = remaining * greed / denominator;
433:
434: result[idx] += amount;
435: remaining -= amount;
436: denominator -= greed;
437: }
438: }
439: }
440:
441: return result;
442: }
443:
444: /**
445: * Computes one dimension of the preferred size of the layout.
446: *
447: * @param hint contains the result if already known, or SWT.DEFAULT if it needs to be computed
448: * @param constraints contains constraints along the other dimension, or SWT.DEFAULT if none. For
449: * example, if we are computing the preferred row sizes, this would be an array of known column sizes.
450: * @param computingRows if true, this method returns the height (pixels). Otherwise, it returns the
451: * width (pixels).
452: */
453: int preferredSize(int[] constraints, boolean computingRows) {
454: int[] fixedSizes = computeMinSizes(constraints, computingRows);
455:
456: return sumOfSizes(fixedSizes)
457: + getDynamicSize(constraints, fixedSizes, computingRows);
458: }
459:
460: /**
461: * Computes the sum of all integers in the given array. If any of the entries are SWT.DEFAULT,
462: * the result is SWT.DEFAULT.
463: */
464: static int sumOfSizes(int[] input) {
465: return sumOfSizes(input, 0, input.length);
466: }
467:
468: static int sumOfSizes(int[] input, int start, int length) {
469: int sum = 0;
470: for (int idx = start; idx < start + length; idx++) {
471: int next = input[idx];
472:
473: if (next == SWT.DEFAULT) {
474: return SWT.DEFAULT;
475: }
476:
477: sum += next;
478: }
479:
480: return sum;
481: }
482:
483: /**
484: * Returns the preferred dynamic width of the layout
485: *
486: * @param constraints
487: * @param fixedSizes
488: * @param computingRows
489: * @return
490: */
491: int getDynamicSize(int[] constraints, int[] fixedSizes,
492: boolean computingRows) {
493: int result = 0;
494: int numerator = getResizeDenominator(computingRows);
495:
496: // If no resizable columns, return
497: if (numerator == 0) {
498: return 0;
499: }
500:
501: int rowSpacing = computingRows ? verticalSpacing
502: : horizontalSpacing;
503: int colSpacing = computingRows ? horizontalSpacing
504: : verticalSpacing;
505:
506: int numControls = gridInfo.controls.length;
507: for (int idx = 0; idx < numControls; idx++) {
508: int controlRowStart = gridInfo.getStartPos(idx,
509: computingRows);
510: int controlRowSpan = getSpan(idx, computingRows);
511: int controlColStart = gridInfo.getStartPos(idx,
512: !computingRows);
513: int controlColSpan = getSpan(idx, !computingRows);
514:
515: int denominator = getGrowthRatio(controlRowStart,
516: controlRowSpan, computingRows);
517:
518: if (denominator > 0) {
519:
520: int widthHint = sumOfSizes(constraints,
521: controlColStart, controlColSpan);
522: if (widthHint != SWT.DEFAULT) {
523: widthHint += colSpacing * (controlColSpan - 1);
524: }
525:
526: // Compute the total control size
527: int controlSize = computeControlSize(idx, widthHint,
528: computingRows);
529:
530: // Subtract the amount that overlaps fixed-size columns
531: controlSize -= sumOfSizes(fixedSizes, controlRowStart,
532: controlRowSpan);
533:
534: // Subtract the amount that overlaps spacing between cells
535: controlSize -= (rowSpacing * (controlRowSpan - 1));
536:
537: result = Math.max(result, controlSize * numerator
538: / denominator);
539: }
540: }
541:
542: return result;
543: }
544:
545: /**
546: * Computes one dimension of a control's size
547: *
548: * @param control the index of the control being computed
549: * @param constraint the other dimension of the control's size, or SWT.DEFAULT if unknown
550: * @param computingHeight if true, this method returns a height. Else it returns a width
551: * @return the preferred height or width of the control, in pixels
552: */
553: int computeControlSize(int control, int constraint,
554: boolean computingHeight) {
555: CellData data = gridInfo.getCellData(control);
556:
557: // If we're looking for the preferred size of the control (without hints)
558: if (constraint == SWT.DEFAULT) {
559: Point result = data.computeSize(cache.getCache(control),
560: SWT.DEFAULT, SWT.DEFAULT);
561:
562: // Return result
563: if (computingHeight) {
564: return result.y;
565: }
566: return result.x;
567: }
568:
569: // Compute a height
570: if (computingHeight) {
571: return data.computeSize(cache.getCache(control),
572: constraint, SWT.DEFAULT).y;
573: }
574:
575: return data.computeSize(cache.getCache(control), SWT.DEFAULT,
576: constraint).x;
577: }
578:
579: /**
580: * Returns the relative amount that a control starting on the given row and spanning
581: * the given length will contribute
582: *
583: * @param start
584: * @param length
585: * @param computingRows
586: * @return
587: */
588: int getGrowthRatio(int start, int length, boolean computingRows) {
589: boolean willGrow = false;
590: int sum = 0;
591:
592: int end = start + length;
593: for (int idx = start; idx < end; idx++) {
594: Row row = getRow(idx, computingRows);
595:
596: if (row.largerThanChildren && row.grows) {
597: willGrow = true;
598: }
599:
600: sum += row.size;
601: }
602:
603: if (!willGrow) {
604: return 0;
605: }
606:
607: return sum;
608: }
609:
610: int[] computeMinSizes(int[] constraints, boolean computingRows) {
611: // We cache the result of this function since it might be called more than once
612: // for a single size computation
613: int[] result = computingRows ? cachedRowMin : cachedColMin;
614:
615: if (result == null) {
616: int columnSpacing;
617: int rowSpacing;
618:
619: if (computingRows) {
620: columnSpacing = horizontalSpacing;
621: rowSpacing = verticalSpacing;
622: } else {
623: columnSpacing = verticalSpacing;
624: rowSpacing = horizontalSpacing;
625: }
626:
627: int rowCount = gridInfo.getNumRows(computingRows);
628: result = new int[rowCount];
629: int colCount = gridInfo.getNumRows(!computingRows);
630: int[] rowControls = new int[colCount];
631:
632: int lastGrowingRow = -1;
633:
634: for (int idx = 0; idx < rowCount; idx++) {
635: Row row = getRow(idx, computingRows);
636:
637: if (row.grows) {
638: // There is no minimum size for growing rows
639: lastGrowingRow = idx;
640: result[idx] = 0;
641: } else {
642: result[idx] = row.size;
643:
644: if (row.largerThanChildren) {
645: // Determine which controls are in this row
646: gridInfo
647: .getRow(rowControls, idx, computingRows);
648:
649: for (int colIdx = 0; colIdx < rowControls.length; colIdx++) {
650: int control = rowControls[colIdx];
651:
652: // The getRow method will insert -1 into empty cells... skip these.
653: if (control != -1) {
654: int controlStart = gridInfo
655: .getStartPos(control,
656: computingRows);
657: int controlSpan = getSpan(control,
658: computingRows);
659:
660: // If the control ends on this row and does not span any growing rows
661: if (controlStart + controlSpan - 1 == idx
662: && controlStart > lastGrowingRow) {
663: int controlColStart = gridInfo
664: .getStartPos(control,
665: !computingRows);
666: int controlColSpan = getSpan(
667: control, !computingRows);
668: int controlRowSpan = getSpan(
669: control, computingRows);
670:
671: // Compute the width constraint for this control
672: int spannedWidth = sumOfSizes(
673: constraints,
674: controlColStart,
675: controlColSpan);
676: if (spannedWidth != SWT.DEFAULT) {
677: spannedWidth += (columnSpacing * (controlSpan - 1));
678: }
679:
680: int controlHeight = computeControlSize(
681: control, spannedWidth,
682: computingRows);
683:
684: // Determine how much of the control spans already allocated columns
685: int allocatedHeight = sumOfSizes(
686: result, controlColStart,
687: controlRowSpan - 1)
688: + (rowSpacing * (controlRowSpan - 1));
689:
690: result[idx] = Math.max(result[idx],
691: controlHeight
692: - allocatedHeight);
693: }
694: }
695: }
696: }
697: }
698: }
699: }
700:
701: // Cache this result
702: if (computingRows) {
703: cachedRowMin = result;
704: } else {
705: cachedColMin = result;
706: }
707:
708: return result;
709: }
710:
711: /**
712: * Returns the height constraints that should be used when computing column widths. Requires initGrid
713: * to have been called first.
714: *
715: * @param result Will contain the height constraint for row i in the ith position of the array,
716: * or SWT.DEFAULT if there is no constraint on that row.
717: */
718: private int[] computeConstraints(boolean horizontal) {
719: // Initialize the height constraints for each row (basically, these will always be SWT.DEFAULT,
720: // except for rows of type FixedRow, which have a constant height).
721: int numRows = gridInfo.getNumRows(horizontal);
722: int[] result = new int[numRows];
723:
724: for (int idx = 0; idx < numRows; idx++) {
725: Row row = getRow(idx, horizontal);
726:
727: if (!(row.grows || row.largerThanChildren)) {
728: result[idx] = row.size;
729: } else {
730: result[idx] = SWT.DEFAULT;
731: }
732: }
733:
734: return result;
735: }
736:
737: /**
738: * Computes the total greediness of all rows
739: *
740: * @return the total greediness of all rows
741: */
742: private int getResizeDenominator(boolean horizontal) {
743: int result = 0;
744: int numRows = gridInfo.getNumRows(horizontal);
745:
746: for (int idx = 0; idx < numRows; idx++) {
747: Row row = getRow(idx, horizontal);
748:
749: if (row.grows) {
750: result += row.size;
751: }
752: }
753:
754: return result;
755: }
756:
757: // /**
758: // * Computes the total fixed height of all rows
759: // *
760: // * @param widthConstraints array where the nth entry indicates the known width of the
761: // * nth column, or SWT.DEFAULT if the width is still unknown
762: // *
763: // * @return the total fixed height for all rows
764: // */
765: // private int getMinimumSize(int[] constraints, boolean horizontal) {
766: // Control[] controls = new Control[gridInfo.getRows()];
767: // int result = 0;
768: // int numRows = gridInfo.getRows();
769: //
770: // for (int idx = 0; idx < numRows; idx++) {
771: // result += getRow(idx).getFixedHeight(gridInfo, widthConstraints, idx);
772: // }
773: //
774: // return result;
775: // }
776:
777: protected int getSpan(int controlId, boolean isRow) {
778: CellData data = gridInfo.getCellData(controlId);
779:
780: if (isRow) {
781: return data.verticalSpan;
782: }
783: return data.horizontalSpan;
784: }
785:
786: /**
787: * Returns the total space that will be required for margins and spacing between and
788: * around cells. initGrid(...) must have been called first.
789: *
790: * @return
791: */
792: private Point totalEmptySpace() {
793: int numRows = gridInfo.getRows();
794:
795: return new Point((2 * marginWidth)
796: + ((gridInfo.getCols() - 1) * horizontalSpacing),
797: (2 * marginHeight) + ((numRows - 1) * verticalSpacing));
798: }
799:
800: /**
801: * Returns the absolute positions of each row, given the start position, row sizes,
802: * and row spacing
803: *
804: * @param startPos position of the initial row
805: * @param sizes array of row sizes (pixels)
806: * @param spacing space between each row (pixels)
807: * @return array of row positions. The result size is sizes.length + 1. The last entry is
808: * the position of the end of the layout.
809: */
810: private static int[] computeRowPositions(int startPos, int[] sizes,
811: int spacing) {
812: int[] result = new int[sizes.length + 1];
813:
814: result[0] = startPos;
815: for (int idx = 0; idx < sizes.length; idx++) {
816: result[idx + 1] = result[idx] + sizes[idx] + spacing;
817: }
818:
819: return result;
820: }
821:
822: /* (non-Javadoc)
823: * @see org.eclipse.swt.widgets.Layout#layout(org.eclipse.swt.widgets.Composite, boolean)
824: */
825: protected void layout(Composite composite, boolean flushCache) {
826: Control[] children = composite.getChildren();
827:
828: // If there are no children then this is a NO-OP
829: if (children.length == 0)
830: return;
831:
832: initGrid(children);
833:
834: if (flushCache) {
835: cache.flush();
836: }
837:
838: Point emptySpace = totalEmptySpace();
839:
840: // Compute the area actually available for controls (once the margins and spacing is removed)
841: int availableWidth = composite.getClientArea().width
842: - emptySpace.x;
843: int availableHeight = composite.getClientArea().height
844: - emptySpace.y;
845:
846: int[] heights = computeConstraints(true);
847: int[] widths = new int[gridInfo.getCols()];
848:
849: // Compute the actual column widths
850: widths = computeSizes(heights, availableWidth, false);
851:
852: // Compute the actual row heights (based on the actual column widths)
853: heights = computeSizes(widths, availableHeight, true);
854:
855: Rectangle currentCell = new Rectangle(0, 0, 0, 0);
856:
857: int[] starty = computeRowPositions(composite.getClientArea().y
858: + marginHeight, heights, verticalSpacing);
859: int[] startx = computeRowPositions(composite.getClientArea().x
860: + marginWidth, widths, horizontalSpacing);
861:
862: int numChildren = gridInfo.controls.length;
863: for (int controlId = 0; controlId < numChildren; controlId++) {
864: CellData data = gridInfo.getCellData(controlId);
865:
866: int row = gridInfo.controlRow[controlId];
867: int col = gridInfo.controlCol[controlId];
868:
869: currentCell.x = startx[col];
870: currentCell.width = startx[col + data.horizontalSpan]
871: - currentCell.x - horizontalSpacing;
872:
873: currentCell.y = starty[row];
874: currentCell.height = starty[row + data.verticalSpan]
875: - currentCell.y - verticalSpacing;
876:
877: data
878: .positionControl(cache.getCache(controlId),
879: currentCell);
880: }
881: }
882:
883: /**
884: * @return
885: */
886: public int getColumns() {
887: return numCols;
888: }
889:
890: public boolean canGrow(Composite composite, boolean horizontally) {
891: initGrid(composite.getChildren());
892:
893: int numRows = gridInfo.getNumRows(horizontally);
894:
895: for (int idx = 0; idx < numRows; idx++) {
896: Row row = getRow(idx, horizontally);
897:
898: if (row.grows) {
899: return true;
900: }
901: }
902:
903: return false;
904:
905: }
906: }
|