001: /*******************************************************************************
002: * Copyright (c) 2006, 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 (original file org.eclipse.ui.texteditor.templates.ColumnLayout)
010: * Tom Schindl <tom.schindl@bestsolution.at> - refactored to be widget independent (bug 171824)
011: * - fix for bug 178280, 184342, 184045
012: *******************************************************************************/package org.eclipse.jface.layout;
013:
014: import org.eclipse.core.runtime.Assert;
015: import org.eclipse.jface.util.Policy;
016: import org.eclipse.jface.viewers.ColumnLayoutData;
017: import org.eclipse.jface.viewers.ColumnPixelData;
018: import org.eclipse.jface.viewers.ColumnWeightData;
019: import org.eclipse.jface.viewers.TableLayout;
020: import org.eclipse.swt.SWT;
021: import org.eclipse.swt.graphics.Point;
022: import org.eclipse.swt.graphics.Rectangle;
023: import org.eclipse.swt.widgets.Composite;
024: import org.eclipse.swt.widgets.Event;
025: import org.eclipse.swt.widgets.Layout;
026: import org.eclipse.swt.widgets.Listener;
027: import org.eclipse.swt.widgets.Scrollable;
028: import org.eclipse.swt.widgets.Widget;
029:
030: /**
031: * The AbstractColumnLayout is a {@link Layout} used to set the size of a table
032: * in a consistent way even during a resize unlike a {@link TableLayout} which
033: * only sets initial sizes.
034: *
035: * <p><b>You can only add the layout to a container whose
036: * only child is the table/tree control you want the layouts applied to.</b>
037: * </p>
038: *
039: * @since 3.3
040: */
041: abstract class AbstractColumnLayout extends Layout {
042: /**
043: * The number of extra pixels taken as horizontal trim by the table column.
044: * To ensure there are N pixels available for the content of the column,
045: * assign N+COLUMN_TRIM for the column width.
046: *
047: * @since 3.1
048: */
049: private static int COLUMN_TRIM = "carbon".equals(SWT.getPlatform()) ? 24 : 3; //$NON-NLS-1$
050:
051: static final boolean IS_GTK = "gtk".equals(SWT.getPlatform());//$NON-NLS-1$
052:
053: static final String LAYOUT_DATA = Policy.JFACE + ".LAYOUT_DATA"; //$NON-NLS-1$
054:
055: private boolean inupdateMode = false;
056:
057: private boolean relayout = true;
058:
059: private Listener resizeListener = new Listener() {
060:
061: public void handleEvent(Event event) {
062: if (!inupdateMode) {
063: updateColumnData(event.widget);
064: }
065: }
066:
067: };
068:
069: /**
070: * Adds a new column of data to this table layout.
071: *
072: * @param column
073: * the column
074: *
075: * @param data
076: * the column layout data
077: */
078: public void setColumnData(Widget column, ColumnLayoutData data) {
079:
080: if (column.getData(LAYOUT_DATA) == null) {
081: column.addListener(SWT.Resize, resizeListener);
082: }
083:
084: column.setData(LAYOUT_DATA, data);
085: }
086:
087: /**
088: * Compute the size of the table or tree based on the ColumnLayoutData and
089: * the width and height hint.
090: *
091: * @param scrollable
092: * the widget to compute
093: * @param wHint
094: * the width hint
095: * @param hHint
096: * the height hint
097: * @return Point where x is the width and y is the height
098: */
099: private Point computeTableTreeSize(Scrollable scrollable,
100: int wHint, int hHint) {
101: Point result = scrollable.computeSize(wHint, hHint);
102:
103: int width = 0;
104: int size = getColumnCount(scrollable);
105: for (int i = 0; i < size; ++i) {
106: ColumnLayoutData layoutData = getLayoutData(scrollable, i);
107: if (layoutData instanceof ColumnPixelData) {
108: ColumnPixelData col = (ColumnPixelData) layoutData;
109: width += col.width;
110: if (col.addTrim) {
111: width += COLUMN_TRIM;
112: }
113: } else if (layoutData instanceof ColumnWeightData) {
114: ColumnWeightData col = (ColumnWeightData) layoutData;
115: width += col.minimumWidth;
116: } else {
117: Assert.isTrue(false, "Unknown column layout data"); //$NON-NLS-1$
118: }
119: }
120: if (width > result.x)
121: result.x = width;
122:
123: return result;
124: }
125:
126: /**
127: * Layout the scrollable based on the supplied width and area. Only increase
128: * the size of the scrollable if increase is <code>true</code>.
129: *
130: * @param scrollable
131: * @param width
132: * @param area
133: * @param increase
134: */
135: private void layoutTableTree(final Scrollable scrollable,
136: final int width, final Rectangle area,
137: final boolean increase) {
138: final int size = getColumnCount(scrollable);
139: final int[] widths = new int[size];
140:
141: final int[] weightIteration = new int[size];
142: int numberOfWeightColumns = 0;
143:
144: int fixedWidth = 0;
145: int minWeightWidth = 0;
146: int totalWeight = 0;
147:
148: // First calc space occupied by fixed columns
149: for (int i = 0; i < size; i++) {
150: ColumnLayoutData col = getLayoutData(scrollable, i);
151: if (col instanceof ColumnPixelData) {
152: ColumnPixelData cpd = (ColumnPixelData) col;
153: int pixels = cpd.width;
154: if (cpd.addTrim) {
155: pixels += COLUMN_TRIM;
156: }
157: widths[i] = pixels;
158: fixedWidth += pixels;
159: } else if (col instanceof ColumnWeightData) {
160: ColumnWeightData cw = (ColumnWeightData) col;
161: weightIteration[numberOfWeightColumns] = i;
162: numberOfWeightColumns++;
163: totalWeight += cw.weight;
164: minWeightWidth += cw.minimumWidth;
165: widths[i] = cw.minimumWidth;
166: } else {
167: Assert.isTrue(false, "Unknown column layout data"); //$NON-NLS-1$
168: }
169: }
170:
171: // Do we have columns that have a weight?
172: final int restIncludingMinWidths = width - fixedWidth;
173: final int rest = restIncludingMinWidths - minWeightWidth;
174: if (numberOfWeightColumns > 0 && rest > 0) {
175:
176: // Modify the weights to reflect what each column already
177: // has due to its minimum. Otherwise, columns with low
178: // minimums get discriminated.
179: int totalWantedPixels = 0;
180: final int[] wantedPixels = new int[numberOfWeightColumns];
181: for (int i = 0; i < numberOfWeightColumns; i++) {
182: ColumnWeightData cw = (ColumnWeightData) getLayoutData(
183: scrollable, weightIteration[i]);
184: wantedPixels[i] = totalWeight == 0 ? 0 : cw.weight
185: * restIncludingMinWidths / totalWeight;
186: totalWantedPixels += wantedPixels[i];
187: }
188:
189: // Now distribute the rest to the columns with weight.
190: int totalDistributed = 0;
191: for (int i = 0; i < numberOfWeightColumns; ++i) {
192: int pixels = totalWantedPixels == 0 ? 0
193: : wantedPixels[i] * rest / totalWantedPixels;
194: totalDistributed += pixels;
195: widths[weightIteration[i]] += pixels;
196: }
197:
198: // Distribute any remaining pixels to columns with weight.
199: int diff = rest - totalDistributed;
200: for (int i = 0; diff > 0; i = ((i + 1) % numberOfWeightColumns)) {
201: ++widths[weightIteration[i]];
202: --diff;
203: }
204: }
205:
206: if (increase) {
207: scrollable.setSize(area.width, area.height);
208: }
209:
210: inupdateMode = true;
211: setColumnWidths(scrollable, widths);
212: scrollable.update();
213: inupdateMode = false;
214:
215: if (!increase) {
216: scrollable.setSize(area.width, area.height);
217: }
218: }
219:
220: /*
221: * (non-Javadoc)
222: *
223: * @see org.eclipse.swt.widgets.Layout#computeSize(org.eclipse.swt.widgets.Composite,
224: * int, int, boolean)
225: */
226: protected Point computeSize(Composite composite, int wHint,
227: int hHint, boolean flushCache) {
228: return computeTableTreeSize(getControl(composite), wHint, hHint);
229: }
230:
231: /*
232: * (non-Javadoc)
233: *
234: * @see org.eclipse.swt.widgets.Layout#layout(org.eclipse.swt.widgets.Composite,
235: * boolean)
236: */
237: protected void layout(Composite composite, boolean flushCache) {
238: Rectangle area = composite.getClientArea();
239: Scrollable table = getControl(composite);
240: int tableWidth = table.getSize().x;
241: int trim = computeTrim(area, table, tableWidth);
242: int width = Math.max(0, area.width - trim);
243:
244: if (width > 1)
245: layoutTableTree(table, width, area, tableWidth < area.width);
246:
247: // For the first time we need to relayout because Scrollbars are not
248: // calculate appropriately
249: if (relayout) {
250: relayout = false;
251: composite.layout();
252: }
253: }
254:
255: /**
256: * Compute the area required for trim.
257: *
258: * @param area
259: * @param scrollable
260: * @param currentWidth
261: * @return int
262: */
263: private int computeTrim(Rectangle area, Scrollable scrollable,
264: int currentWidth) {
265: int trim;
266:
267: if (currentWidth > 1) {
268: trim = currentWidth - scrollable.getClientArea().width;
269: } else {
270: // initially, the table has no extend and no client area - use the
271: // border with
272: // plus some padding as educated guess
273: trim = 2 * scrollable.getBorderWidth() + 1;
274: }
275:
276: return trim;
277: }
278:
279: /**
280: * Get the control being laid out.
281: *
282: * @param composite
283: * the composite with the layout
284: * @return {@link Scrollable}
285: */
286: Scrollable getControl(Composite composite) {
287: return (Scrollable) composite.getChildren()[0];
288: }
289:
290: /**
291: * Get the number of columns for the receiver.
292: *
293: * @return the number of columns
294: */
295: abstract int getColumnCount(Scrollable tableTree);
296:
297: /**
298: * Set the widths of the columns.
299: *
300: * @param widths
301: */
302: abstract void setColumnWidths(Scrollable tableTree, int[] widths);
303:
304: abstract ColumnLayoutData getLayoutData(Scrollable tableTree,
305: int columnIndex);
306:
307: abstract void updateColumnData(Widget column);
308: }
|