001: /*******************************************************************************
002: * Copyright (c) 2000, 2007 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.forms.widgets;
011:
012: import java.util.Enumeration;
013: import java.util.Hashtable;
014: import java.util.Vector;
015:
016: import org.eclipse.swt.SWT;
017: import org.eclipse.swt.graphics.*;
018: import org.eclipse.swt.widgets.*;
019:
020: /**
021: * This implementation of the layout algorithm attempts to position controls in
022: * the composite using a two-pass autolayout HTML table altorithm recommeded by
023: * HTML 4.01 W3C specification (see
024: * http://www.w3.org/TR/html4/appendix/notes.html#h-B.5.2.2). The main
025: * differences with GridLayout is that it has two passes and that width and
026: * height are not calculated in the same pass.
027: * <p>
028: * The advantage of the algorithm over GridLayout is that it is capable of
029: * flowing text controls capable of line wrap. These controls do not have
030: * natural 'preferred size'. Instead, they are capable of providing the required
031: * height if the width is set. Consequently, this algorithm first calculates the
032: * widths that will be assigned to columns, and then passes those widths to the
033: * controls to calculate the height. When a composite with this layout is a
034: * child of the scrolling composite, they should interact in such a way that
035: * reduction in the scrolling composite width results in the reflow and increase
036: * of the overall height.
037: * <p>
038: * If none of the columns contain expandable and wrappable controls, the
039: * end-result will be similar to the one provided by GridLayout. The difference
040: * will show up for layouts that contain controls whose minimum and maximum
041: * widths are not the same.
042: *
043: * @see TableWrapData
044: * @since 3.0
045: */
046: public final class TableWrapLayout extends Layout implements
047: ILayoutExtension {
048: /**
049: * Number of columns to use when positioning children (default is 1).
050: */
051: public int numColumns = 1;
052:
053: /**
054: * Left margin variable (default is 5).
055: */
056: public int leftMargin = 5;
057:
058: /**
059: * Right margin variable (default is 5).
060: */
061: public int rightMargin = 5;
062:
063: /**
064: * Top margin variable (default is 5).
065: */
066: public int topMargin = 5;
067:
068: /**
069: * Botom margin variable (default is 5).
070: */
071: public int bottomMargin = 5;
072:
073: /**
074: * Horizontal spacing (default is 5).
075: */
076: public int horizontalSpacing = 5;
077:
078: /**
079: * Vertical spacing (default is 5).
080: */
081: public int verticalSpacing = 5;
082:
083: /**
084: * If set to <code>true</code>, all the columns will have the same width.
085: * Otherwise, column widths will be computed based on controls in them and
086: * their layout data (default is <code>false</code>).
087: */
088: public boolean makeColumnsEqualWidth = false;
089:
090: private boolean initialLayout = true;
091:
092: private Vector grid = null;
093:
094: private Hashtable rowspans;
095:
096: private int[] minColumnWidths, maxColumnWidths;
097:
098: private int widestColumnWidth;
099:
100: private int[] growingColumns;
101:
102: private int[] growingRows;
103:
104: private LayoutCache cache = new LayoutCache();
105:
106: private class RowSpan {
107: Control child;
108:
109: int row;
110:
111: int column;
112:
113: int height;
114:
115: int totalHeight;
116:
117: public RowSpan(Control child, int column, int row) {
118: this .child = child;
119: this .column = column;
120: this .row = row;
121: }
122:
123: /*
124: * Updates this row span's height with the given one if it is within
125: * this span.
126: */
127: public void update(int currentRow, int rowHeight) {
128: TableWrapData td = (TableWrapData) child.getLayoutData();
129: // is currentRow within this span?
130: if (currentRow >= row && currentRow < row + td.rowspan) {
131: totalHeight += rowHeight;
132: if (currentRow > row)
133: totalHeight += verticalSpacing;
134: }
135: }
136:
137: public int getRequiredHeightIncrease() {
138: if (totalHeight < height)
139: return height - totalHeight;
140: return 0;
141: }
142: }
143:
144: /**
145: * Implements ILayoutExtension. Should not be called directly.
146: *
147: * @see ILayoutExtension
148: */
149: public int computeMinimumWidth(Composite parent, boolean changed) {
150:
151: Control[] children = parent.getChildren();
152: if (changed) {
153: cache.flush();
154: }
155:
156: cache.setControls(children);
157:
158: changed = true;
159: initializeIfNeeded(parent, changed);
160: if (initialLayout) {
161: changed = true;
162: initialLayout = false;
163: }
164: if (grid == null || changed) {
165: changed = true;
166: grid = new Vector();
167: createGrid(parent);
168: }
169: if (minColumnWidths == null)
170: minColumnWidths = new int[numColumns];
171: for (int i = 0; i < numColumns; i++) {
172: minColumnWidths[i] = 0;
173: }
174: return internalGetMinimumWidth(parent, changed);
175: }
176:
177: /**
178: * Implements ILayoutExtension. Should not be called directly.
179: *
180: * @see ILayoutExtension
181: */
182: public int computeMaximumWidth(Composite parent, boolean changed) {
183: Control[] children = parent.getChildren();
184: if (changed) {
185: cache.flush();
186: }
187:
188: cache.setControls(children);
189:
190: changed = true;
191: initializeIfNeeded(parent, changed);
192: if (initialLayout) {
193: changed = true;
194: initialLayout = false;
195: }
196: if (grid == null || changed) {
197: changed = true;
198: grid = new Vector();
199: createGrid(parent);
200: }
201: if (maxColumnWidths == null)
202: maxColumnWidths = new int[numColumns];
203: for (int i = 0; i < numColumns; i++) {
204: maxColumnWidths[i] = 0;
205: }
206: return internalGetMaximumWidth(parent, changed);
207: }
208:
209: /**
210: * @see Layout#layout(Composite, boolean)
211: */
212: protected void layout(Composite parent, boolean changed) {
213:
214: Rectangle clientArea = parent.getClientArea();
215: Control[] children = parent.getChildren();
216: if (changed) {
217: cache.flush();
218: }
219:
220: if (children.length == 0)
221: return;
222:
223: cache.setControls(children);
224:
225: int parentWidth = clientArea.width;
226: changed = true;
227: initializeIfNeeded(parent, changed);
228: if (initialLayout) {
229: changed = true;
230: initialLayout = false;
231: }
232: if (grid == null || changed) {
233: changed = true;
234: grid = new Vector();
235: createGrid(parent);
236: }
237: resetColumnWidths();
238: int minWidth = internalGetMinimumWidth(parent, changed);
239: int maxWidth = internalGetMaximumWidth(parent, changed);
240: int tableWidth = parentWidth;
241: int[] columnWidths;
242: if (parentWidth <= minWidth) {
243: tableWidth = minWidth;
244: if (makeColumnsEqualWidth) {
245: columnWidths = new int[numColumns];
246: for (int i = 0; i < numColumns; i++) {
247: columnWidths[i] = widestColumnWidth;
248: }
249: } else
250: columnWidths = minColumnWidths;
251: } else if (parentWidth > maxWidth) {
252: if (growingColumns.length == 0) {
253: tableWidth = maxWidth;
254: columnWidths = maxColumnWidths;
255: } else {
256: columnWidths = new int[numColumns];
257: int colSpace = tableWidth - leftMargin - rightMargin;
258: colSpace -= (numColumns - 1) * horizontalSpacing;
259: int extra = parentWidth - maxWidth;
260: int colExtra = extra / growingColumns.length;
261: for (int i = 0; i < numColumns; i++) {
262: columnWidths[i] = maxColumnWidths[i];
263: if (isGrowingColumn(i)) {
264: columnWidths[i] += colExtra;
265: }
266: }
267: }
268: } else {
269: columnWidths = new int[numColumns];
270: if (makeColumnsEqualWidth) {
271: int colSpace = tableWidth - leftMargin - rightMargin;
272: colSpace -= (numColumns - 1) * horizontalSpacing;
273: int col = colSpace / numColumns;
274: for (int i = 0; i < numColumns; i++) {
275: columnWidths[i] = col;
276: }
277: } else {
278: columnWidths = assignExtraSpace(tableWidth, maxWidth,
279: minWidth);
280: }
281: }
282: int y = topMargin + clientArea.y;
283: int[] rowHeights = computeRowHeights(children, columnWidths,
284: changed);
285: for (int i = 0; i < grid.size(); i++) {
286: int rowHeight = rowHeights[i];
287: int x = leftMargin + clientArea.x;
288: TableWrapData[] row = (TableWrapData[]) grid.elementAt(i);
289: for (int j = 0; j < numColumns; j++) {
290: TableWrapData td = row[j];
291: if (td.isItemData) {
292: Control child = children[td.childIndex];
293: placeControl(child, td, x, y, rowHeights, i);
294: }
295: x += columnWidths[j];
296: if (j < numColumns - 1)
297: x += horizontalSpacing;
298: }
299: y += rowHeight + verticalSpacing;
300: }
301: }
302:
303: int[] computeRowHeights(Control[] children, int[] columnWidths,
304: boolean changed) {
305: int[] rowHeights = new int[grid.size()];
306: for (int i = 0; i < grid.size(); i++) {
307: TableWrapData[] row = (TableWrapData[]) grid.elementAt(i);
308: rowHeights[i] = 0;
309: for (int j = 0; j < numColumns; j++) {
310: TableWrapData td = row[j];
311: if (td.isItemData == false) {
312: continue;
313: }
314: Control child = children[td.childIndex];
315: int span = td.colspan;
316: int cwidth = 0;
317: for (int k = j; k < j + span; k++) {
318: cwidth += columnWidths[k];
319: if (k < j + span - 1)
320: cwidth += horizontalSpacing;
321: }
322: Point size = computeSize(td.childIndex, cwidth,
323: td.indent, td.maxWidth, td.maxHeight);
324: td.compWidth = cwidth;
325: if (td.heightHint != SWT.DEFAULT) {
326: size = new Point(size.x, td.heightHint);
327: }
328: td.compSize = size;
329: RowSpan rowspan = (RowSpan) rowspans.get(child);
330: if (rowspan == null) {
331: rowHeights[i] = Math.max(rowHeights[i], size.y);
332: } else
333: rowspan.height = size.y;
334: }
335: updateRowSpans(i, rowHeights[i]);
336: }
337: for (Enumeration enm = rowspans.elements(); enm
338: .hasMoreElements();) {
339: RowSpan rowspan = (RowSpan) enm.nextElement();
340: int increase = rowspan.getRequiredHeightIncrease();
341: if (increase == 0)
342: continue;
343: TableWrapData td = (TableWrapData) rowspan.child
344: .getLayoutData();
345: int ngrowing = 0;
346: int[] affectedRows = new int[grid.size()];
347: for (int i = 0; i < growingRows.length; i++) {
348: int growingRow = growingRows[i];
349: if (growingRow >= rowspan.row
350: && growingRow < rowspan.row + td.rowspan) {
351: affectedRows[ngrowing++] = growingRow;
352: }
353: }
354: if (ngrowing == 0) {
355: ngrowing = 1;
356: affectedRows[0] = rowspan.row + td.rowspan - 1;
357: }
358: increase += increase % ngrowing;
359: int perRowIncrease = increase / ngrowing;
360: for (int i = 0; i < ngrowing; i++) {
361: int growingRow = affectedRows[i];
362: rowHeights[growingRow] += perRowIncrease;
363: }
364: }
365: return rowHeights;
366: }
367:
368: boolean isGrowingColumn(int col) {
369: if (growingColumns == null)
370: return false;
371: for (int i = 0; i < growingColumns.length; i++) {
372: if (col == growingColumns[i])
373: return true;
374: }
375: return false;
376: }
377:
378: int[] assignExtraSpace(int tableWidth, int maxWidth, int minWidth) {
379: int fixedPart = leftMargin + rightMargin + (numColumns - 1)
380: * horizontalSpacing;
381: int D = maxWidth - minWidth;
382: int W = tableWidth - fixedPart - minWidth;
383: int widths[] = new int[numColumns];
384: int rem = 0;
385: for (int i = 0; i < numColumns; i++) {
386: int cmin = minColumnWidths[i];
387: int cmax = maxColumnWidths[i];
388: int d = cmax - cmin;
389: int extra = D != 0 ? (d * W) / D : 0;
390: if (i < numColumns - 1) {
391: widths[i] = cmin + extra;
392: rem += widths[i];
393: } else {
394: widths[i] = tableWidth - fixedPart - rem;
395: }
396: }
397: return widths;
398: }
399:
400: Point computeSize(int childIndex, int width, int indent,
401: int maxWidth, int maxHeight) {
402: int widthArg = width - indent;
403: SizeCache controlCache = cache.getCache(childIndex);
404: if (!isWrap(controlCache.getControl()))
405: widthArg = SWT.DEFAULT;
406: Point size = controlCache.computeSize(widthArg, SWT.DEFAULT);
407: if (maxWidth != SWT.DEFAULT)
408: size.x = Math.min(size.x, maxWidth);
409: if (maxHeight != SWT.DEFAULT)
410: size.y = Math.min(size.y, maxHeight);
411: size.x += indent;
412: return size;
413: }
414:
415: void placeControl(Control control, TableWrapData td, int x, int y,
416: int[] rowHeights, int row) {
417: int xloc = x + td.indent;
418: int yloc = y;
419: int height = td.compSize.y;
420: int colWidth = td.compWidth - td.indent;
421: int width = td.compSize.x - td.indent;
422: width = Math.min(width, colWidth);
423: int slotHeight = rowHeights[row];
424: RowSpan rowspan = (RowSpan) rowspans.get(control);
425: if (rowspan != null) {
426: slotHeight = 0;
427: for (int i = row; i < row + td.rowspan; i++) {
428: if (i > row)
429: slotHeight += verticalSpacing;
430: slotHeight += rowHeights[i];
431: }
432: }
433: // align horizontally
434: if (td.align == TableWrapData.CENTER) {
435: xloc = x + colWidth / 2 - width / 2;
436: } else if (td.align == TableWrapData.RIGHT) {
437: xloc = x + colWidth - width;
438: } else if (td.align == TableWrapData.FILL) {
439: width = colWidth;
440: }
441: // align vertically
442: if (td.valign == TableWrapData.MIDDLE) {
443: yloc = y + slotHeight / 2 - height / 2;
444: } else if (td.valign == TableWrapData.BOTTOM) {
445: yloc = y + slotHeight - height;
446: } else if (td.valign == TableWrapData.FILL) {
447: height = slotHeight;
448: }
449: control.setBounds(xloc, yloc, width, height);
450: }
451:
452: void createGrid(Composite composite) {
453: int row, column, rowFill, columnFill;
454: Control[] children;
455: TableWrapData spacerSpec;
456: Vector growingCols = new Vector();
457: Vector growingRows = new Vector();
458: rowspans = new Hashtable();
459: //
460: children = composite.getChildren();
461: if (children.length == 0)
462: return;
463: //
464: grid.addElement(createEmptyRow());
465: row = 0;
466: column = 0;
467: // Loop through the children and place their associated layout specs in
468: // the
469: // grid. Placement occurs left to right, top to bottom (i.e., by row).
470: for (int i = 0; i < children.length; i++) {
471: // Find the first available spot in the grid.
472: Control child = children[i];
473: TableWrapData spec = (TableWrapData) child.getLayoutData();
474: while (((TableWrapData[]) grid.elementAt(row))[column] != null) {
475: column = column + 1;
476: if (column >= numColumns) {
477: row = row + 1;
478: column = 0;
479: if (row >= grid.size()) {
480: grid.addElement(createEmptyRow());
481: }
482: }
483: }
484: // See if the place will support the widget's horizontal span. If
485: // not, go to the
486: // next row.
487: if (column + spec.colspan - 1 >= numColumns) {
488: grid.addElement(createEmptyRow());
489: row = row + 1;
490: column = 0;
491: }
492: // The vertical span for the item will be at least 1. If it is > 1,
493: // add other rows to the grid.
494: if (spec.rowspan > 1) {
495: rowspans.put(child, new RowSpan(child, column, row));
496: }
497: for (int j = 2; j <= spec.rowspan; j++) {
498: if (row + j > grid.size()) {
499: grid.addElement(createEmptyRow());
500: }
501: }
502: // Store the layout spec. Also cache the childIndex. NOTE: That we
503: // assume the children of a
504: // composite are maintained in the order in which they are created
505: // and added to the composite.
506: ((TableWrapData[]) grid.elementAt(row))[column] = spec;
507: spec.childIndex = i;
508: if (spec.grabHorizontal) {
509: updateGrowingColumns(growingCols, spec, column);
510: }
511: if (spec.grabVertical) {
512: updateGrowingRows(growingRows, spec, row);
513: }
514: // Put spacers in the grid to account for the item's vertical and
515: // horizontal
516: // span.
517: rowFill = spec.rowspan - 1;
518: columnFill = spec.colspan - 1;
519: for (int r = 1; r <= rowFill; r++) {
520: for (int c = 0; c < spec.colspan; c++) {
521: spacerSpec = new TableWrapData();
522: spacerSpec.isItemData = false;
523: ((TableWrapData[]) grid.elementAt(row + r))[column
524: + c] = spacerSpec;
525: }
526: }
527: for (int c = 1; c <= columnFill; c++) {
528: for (int r = 0; r < spec.rowspan; r++) {
529: spacerSpec = new TableWrapData();
530: spacerSpec.isItemData = false;
531: ((TableWrapData[]) grid.elementAt(row + r))[column
532: + c] = spacerSpec;
533: }
534: }
535: column = column + spec.colspan - 1;
536: }
537: // Fill out empty grid cells with spacers.
538: for (int k = column + 1; k < numColumns; k++) {
539: spacerSpec = new TableWrapData();
540: spacerSpec.isItemData = false;
541: ((TableWrapData[]) grid.elementAt(row))[k] = spacerSpec;
542: }
543: for (int k = row + 1; k < grid.size(); k++) {
544: spacerSpec = new TableWrapData();
545: spacerSpec.isItemData = false;
546: ((TableWrapData[]) grid.elementAt(k))[column] = spacerSpec;
547: }
548: growingColumns = new int[growingCols.size()];
549: for (int i = 0; i < growingCols.size(); i++) {
550: growingColumns[i] = ((Integer) growingCols.get(i))
551: .intValue();
552: }
553: this .growingRows = new int[growingRows.size()];
554: for (int i = 0; i < growingRows.size(); i++) {
555: this .growingRows[i] = ((Integer) growingRows.get(i))
556: .intValue();
557: }
558: }
559:
560: private void updateGrowingColumns(Vector growingColumns,
561: TableWrapData spec, int column) {
562: int affectedColumn = column + spec.colspan - 1;
563: for (int i = 0; i < growingColumns.size(); i++) {
564: Integer col = (Integer) growingColumns.get(i);
565: if (col.intValue() == affectedColumn)
566: return;
567: }
568: growingColumns.add(new Integer(affectedColumn));
569: }
570:
571: private void updateGrowingRows(Vector growingRows,
572: TableWrapData spec, int row) {
573: int affectedRow = row + spec.rowspan - 1;
574: for (int i = 0; i < growingRows.size(); i++) {
575: Integer irow = (Integer) growingRows.get(i);
576: if (irow.intValue() == affectedRow)
577: return;
578: }
579: growingRows.add(new Integer(affectedRow));
580: }
581:
582: private TableWrapData[] createEmptyRow() {
583: TableWrapData[] row = new TableWrapData[numColumns];
584: for (int i = 0; i < numColumns; i++)
585: row[i] = null;
586: return row;
587: }
588:
589: /**
590: * @see Layout#computeSize(Composite, int, int, boolean)
591: */
592: protected Point computeSize(Composite parent, int wHint, int hHint,
593: boolean changed) {
594: Control[] children = parent.getChildren();
595: if (changed) {
596: cache.flush();
597: }
598: if (children.length == 0) {
599: return new Point(0, 0);
600: }
601: cache.setControls(children);
602:
603: int parentWidth = wHint;
604: changed = true;
605: initializeIfNeeded(parent, changed);
606: if (initialLayout) {
607: changed = true;
608: initialLayout = false;
609: }
610: if (grid == null || changed) {
611: changed = true;
612: grid = new Vector();
613: createGrid(parent);
614: }
615: resetColumnWidths();
616: int minWidth = internalGetMinimumWidth(parent, changed);
617: int maxWidth = internalGetMaximumWidth(parent, changed);
618:
619: if (wHint == SWT.DEFAULT)
620: parentWidth = maxWidth;
621:
622: int tableWidth = parentWidth;
623: int[] columnWidths;
624: if (parentWidth <= minWidth) {
625: tableWidth = minWidth;
626: if (makeColumnsEqualWidth) {
627: columnWidths = new int[numColumns];
628: for (int i = 0; i < numColumns; i++) {
629: columnWidths[i] = widestColumnWidth;
630: }
631: } else
632: columnWidths = minColumnWidths;
633: } else if (parentWidth >= maxWidth) {
634: if (makeColumnsEqualWidth) {
635: columnWidths = new int[numColumns];
636: int colSpace = parentWidth - leftMargin - rightMargin;
637: colSpace -= (numColumns - 1) * horizontalSpacing;
638: int col = colSpace / numColumns;
639: for (int i = 0; i < numColumns; i++) {
640: columnWidths[i] = col;
641: }
642: } else {
643: tableWidth = maxWidth;
644: columnWidths = maxColumnWidths;
645: }
646: } else {
647: columnWidths = new int[numColumns];
648: if (makeColumnsEqualWidth) {
649: int colSpace = tableWidth - leftMargin - rightMargin;
650: colSpace -= (numColumns - 1) * horizontalSpacing;
651: int col = colSpace / numColumns;
652: for (int i = 0; i < numColumns; i++) {
653: columnWidths[i] = col;
654: }
655: } else {
656: columnWidths = assignExtraSpace(tableWidth, maxWidth,
657: minWidth);
658: }
659: }
660: int totalHeight = 0;
661: int innerHeight = 0;
662: // compute widths
663: for (int i = 0; i < grid.size(); i++) {
664: TableWrapData[] row = (TableWrapData[]) grid.elementAt(i);
665: // assign widths, calculate heights
666: int rowHeight = 0;
667: for (int j = 0; j < numColumns; j++) {
668: TableWrapData td = row[j];
669: if (td.isItemData == false) {
670: continue;
671: }
672: Control child = children[td.childIndex];
673: int span = td.colspan;
674: int cwidth = 0;
675: for (int k = j; k < j + span; k++) {
676: if (k > j)
677: cwidth += horizontalSpacing;
678: cwidth += columnWidths[k];
679: }
680: int cy = td.heightHint;
681: if (cy == SWT.DEFAULT) {
682: Point size = computeSize(td.childIndex, cwidth,
683: td.indent, td.maxWidth, td.maxHeight);
684: cy = size.y;
685: }
686: RowSpan rowspan = (RowSpan) rowspans.get(child);
687: if (rowspan != null) {
688: // don't take the height of this child into acount
689: // because it spans multiple rows
690: rowspan.height = cy;
691: } else {
692: rowHeight = Math.max(rowHeight, cy);
693: }
694: }
695: updateRowSpans(i, rowHeight);
696: if (i > 0)
697: innerHeight += verticalSpacing;
698: innerHeight += rowHeight;
699: }
700: if (!rowspans.isEmpty())
701: innerHeight = compensateForRowSpans(innerHeight);
702: totalHeight = topMargin + innerHeight + bottomMargin;
703: return new Point(tableWidth, totalHeight);
704: }
705:
706: private void updateRowSpans(int row, int rowHeight) {
707: if (rowspans == null || rowspans.size() == 0)
708: return;
709: for (Enumeration enm = rowspans.elements(); enm
710: .hasMoreElements();) {
711: RowSpan rowspan = (RowSpan) enm.nextElement();
712: rowspan.update(row, rowHeight);
713: }
714: }
715:
716: private int compensateForRowSpans(int totalHeight) {
717: for (Enumeration enm = rowspans.elements(); enm
718: .hasMoreElements();) {
719: RowSpan rowspan = (RowSpan) enm.nextElement();
720: totalHeight += rowspan.getRequiredHeightIncrease();
721: }
722: return totalHeight;
723: }
724:
725: int internalGetMinimumWidth(Composite parent, boolean changed) {
726: if (changed)
727: //calculateMinimumColumnWidths(parent, true);
728: calculateColumnWidths(parent, minColumnWidths, false, true);
729: int minimumWidth = 0;
730: widestColumnWidth = 0;
731: if (makeColumnsEqualWidth) {
732: for (int i = 0; i < numColumns; i++) {
733: widestColumnWidth = Math.max(widestColumnWidth,
734: minColumnWidths[i]);
735: }
736: }
737: for (int i = 0; i < numColumns; i++) {
738: if (i > 0)
739: minimumWidth += horizontalSpacing;
740: if (makeColumnsEqualWidth)
741: minimumWidth += widestColumnWidth;
742: else
743: minimumWidth += minColumnWidths[i];
744: }
745: // add margins
746: minimumWidth += leftMargin + rightMargin;
747: return minimumWidth;
748: }
749:
750: int internalGetMaximumWidth(Composite parent, boolean changed) {
751: if (changed)
752: //calculateMaximumColumnWidths(parent, true);
753: calculateColumnWidths(parent, maxColumnWidths, true, true);
754: int maximumWidth = 0;
755: for (int i = 0; i < numColumns; i++) {
756: if (i > 0)
757: maximumWidth += horizontalSpacing;
758: maximumWidth += maxColumnWidths[i];
759: }
760: // add margins
761: maximumWidth += leftMargin + rightMargin;
762: return maximumWidth;
763: }
764:
765: void resetColumnWidths() {
766: if (minColumnWidths == null)
767: minColumnWidths = new int[numColumns];
768: if (maxColumnWidths == null)
769: maxColumnWidths = new int[numColumns];
770: for (int i = 0; i < numColumns; i++) {
771: minColumnWidths[i] = 0;
772: }
773: for (int i = 0; i < numColumns; i++) {
774: maxColumnWidths[i] = 0;
775: }
776: }
777:
778: void calculateColumnWidths(Composite parent, int[] columnWidths,
779: boolean max, boolean changed) {
780: boolean secondPassNeeded = false;
781: for (int i = 0; i < grid.size(); i++) {
782: TableWrapData[] row = (TableWrapData[]) grid.elementAt(i);
783: for (int j = 0; j < numColumns; j++) {
784: TableWrapData td = row[j];
785: if (td.isItemData == false)
786: continue;
787:
788: if (td.colspan > 1) {
789: // we will not do controls with multiple column span
790: // here - increment and continue
791: secondPassNeeded = true;
792: j += td.colspan - 1;
793: continue;
794: }
795:
796: SizeCache childCache = cache.getCache(td.childIndex);
797: // !!
798: int width = max ? childCache.computeMaximumWidth()
799: : childCache.computeMinimumWidth();
800: if (td.maxWidth != SWT.DEFAULT)
801: width = Math.min(width, td.maxWidth);
802:
803: width += td.indent;
804: columnWidths[j] = Math.max(columnWidths[j], width);
805: }
806: }
807: if (!secondPassNeeded)
808: return;
809:
810: // Second pass for controls with multi-column horizontal span
811: for (int i = 0; i < grid.size(); i++) {
812: TableWrapData[] row = (TableWrapData[]) grid.elementAt(i);
813: for (int j = 0; j < numColumns; j++) {
814: TableWrapData td = row[j];
815: if (td.isItemData == false || td.colspan == 1)
816: continue;
817:
818: SizeCache childCache = cache.getCache(td.childIndex);
819: int width = max ? childCache.computeMaximumWidth()
820: : childCache.computeMinimumWidth();
821: if (td.maxWidth != SWT.DEFAULT)
822: width = Math.min(width, td.maxWidth);
823:
824: width += td.indent;
825: // check if the current width is enough to
826: // support the control; if not, add the delta to
827: // the last column or to all the growing columns, if present
828: int current = 0;
829: for (int k = j; k < j + td.colspan; k++) {
830: if (k > j)
831: current += horizontalSpacing;
832: current += columnWidths[k];
833: }
834: if (width <= current) {
835: // we are ok - nothing to do here
836: } else {
837: int ndiv = 0;
838: if (growingColumns != null) {
839: for (int k = j; k < j + td.colspan; k++) {
840: if (isGrowingColumn(k)) {
841: ndiv++;
842: }
843: }
844: }
845: if (ndiv == 0) {
846: // add the delta to the last column
847: columnWidths[j + td.colspan - 1] += width
848: - current;
849: } else {
850: // distribute the delta to the growing
851: // columns
852: int percolumn = (width - current) / ndiv;
853: if ((width - current) % ndiv > 0)
854: percolumn++;
855: for (int k = j; k < j + td.colspan; k++) {
856: if (isGrowingColumn(k))
857: columnWidths[k] += percolumn;
858: }
859: }
860: }
861: }
862: }
863: }
864:
865: boolean isWrap(Control control) {
866: if (control instanceof Composite
867: && ((Composite) control).getLayout() instanceof ILayoutExtension)
868: return true;
869: return (control.getStyle() & SWT.WRAP) != 0;
870: }
871:
872: private void initializeIfNeeded(Composite parent, boolean changed) {
873: if (changed)
874: initialLayout = true;
875: if (initialLayout) {
876: initializeLayoutData(parent);
877: initialLayout = false;
878: }
879: }
880:
881: void initializeLayoutData(Composite composite) {
882: Control[] children = composite.getChildren();
883: for (int i = 0; i < children.length; i++) {
884: Control child = children[i];
885: if (child.getLayoutData() == null) {
886: child.setLayoutData(new TableWrapData());
887: }
888: }
889: }
890: }
|