Mercurial > projects > dwt2
comparison org.eclipse.ui.forms/src/org/eclipse/ui/forms/widgets/TableWrapLayout.d @ 12:bc29606a740c
Added dwt-addons in original directory structure of eclipse.org
author | Frank Benoit <benoit@tionex.de> |
---|---|
date | Sat, 14 Mar 2009 18:23:29 +0100 |
parents | |
children | dbfb303e8fb0 |
comparison
equal
deleted
inserted
replaced
11:43904fec5dca | 12:bc29606a740c |
---|---|
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 org.eclipse.ui.forms.widgets.TableWrapLayout; | |
14 | |
15 import org.eclipse.ui.forms.widgets.TableWrapData; | |
16 import org.eclipse.ui.forms.widgets.ILayoutExtension; | |
17 import org.eclipse.ui.forms.widgets.LayoutCache; | |
18 import org.eclipse.ui.forms.widgets.SizeCache; | |
19 | |
20 import org.eclipse.swt.SWT; | |
21 import org.eclipse.swt.graphics.Point; | |
22 import org.eclipse.swt.graphics.Rectangle; | |
23 import org.eclipse.swt.widgets.Composite; | |
24 import org.eclipse.swt.widgets.Control; | |
25 import org.eclipse.swt.widgets.Layout; | |
26 | |
27 import java.lang.all; | |
28 import java.util.Enumeration; | |
29 import java.util.Set; | |
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 Vector grid = null; | |
105 | |
106 private Hashtable 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 Vector(); | |
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 Vector(); | |
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 Vector(); | |
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 = arrayFromObject!(TableWrapData)( grid.elementAt(i)); | |
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 = arrayFromObject!(TableWrapData)( grid.elementAt(i)); | |
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 SWT.DEFAULT) { | |
339 size = new Point(size.x, td.heightHint); | |
340 } | |
341 td.compSize = size; | |
342 RowSpan rowspan = cast(RowSpan) rowspans.get(child); | |
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 for (Enumeration enm = rowspans.elements(); enm.hasMoreElements();) { | |
351 RowSpan rowspan = cast(RowSpan) enm.nextElement(); | |
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 = SWT.DEFAULT; | |
416 Point size = controlCache.computeSize(widthArg, SWT.DEFAULT); | |
417 if (maxWidth !is SWT.DEFAULT) | |
418 size.x = Math.min(size.x, maxWidth); | |
419 if (maxHeight !is SWT.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]; | |
434 RowSpan rowspan = cast(RowSpan) rowspans.get(control); | |
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 Vector growingCols = new Vector(); | |
467 Vector growingRows = new Vector(); | |
468 rowspans = new Hashtable(); | |
469 // | |
470 children = composite.getChildren(); | |
471 if (children.length is 0) | |
472 return; | |
473 // | |
474 grid.addElement( new ArrayWrapperObject(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 (arrayFromObject!(TableWrapData)( grid.elementAt(row))[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.addElement(new ArrayWrapperObject(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.addElement(new ArrayWrapperObject(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.put(child, new RowSpan(child, column, row)); | |
506 } | |
507 for (int j = 2; j <= spec.rowspan; j++) { | |
508 if (row + j > grid.size()) { | |
509 grid.addElement(new ArrayWrapperObject(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(ArrayWrapperObject) grid.elementAt(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(ArrayWrapperObject) grid.elementAt(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(ArrayWrapperObject) grid.elementAt(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(ArrayWrapperObject) grid.elementAt(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(ArrayWrapperObject) grid.elementAt(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(Vector 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.add(new Integer(affectedColumn)); | |
575 } | |
576 | |
577 private void updateGrowingRows(Vector 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.add(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 Vector(); | |
619 createGrid(parent); | |
620 } | |
621 resetColumnWidths(); | |
622 int minWidth = internalGetMinimumWidth(parent, changed); | |
623 int maxWidth = internalGetMaximumWidth(parent, changed); | |
624 | |
625 if (wHint is SWT.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 = arrayFromObject!(TableWrapData)( grid.elementAt(i)); | |
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 SWT.DEFAULT) { | |
687 Point size = computeSize(td.childIndex, cwidth, td.indent, td.maxWidth, td.maxHeight); | |
688 cy = size.y; | |
689 } | |
690 RowSpan rowspan = cast(RowSpan) rowspans.get(child); | |
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.isEmpty()) | |
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 for (Enumeration enm = rowspans.elements(); enm.hasMoreElements();) { | |
714 RowSpan rowspan = cast(RowSpan) enm.nextElement(); | |
715 rowspan.update(row, rowHeight); | |
716 } | |
717 } | |
718 | |
719 private int compensateForRowSpans(int totalHeight) { | |
720 for (Enumeration enm = rowspans.elements(); enm.hasMoreElements();) { | |
721 RowSpan rowspan = cast(RowSpan) enm.nextElement(); | |
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 = arrayFromObject!(TableWrapData)( grid.elementAt(i)); | |
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 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) return; | |
808 | |
809 // Second pass for controls with multi-column horizontal span | |
810 for (int i = 0; i < grid.size(); i++) { | |
811 TableWrapData[] row = arrayFromObject!(TableWrapData)( grid.elementAt(i)); | |
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 SWT.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() & SWT.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 } |