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