]> gerrit.simantics Code Review - simantics/platform.git/blob
3bff312550d19a2ae40be7c3a0c4429669c2ca1e
[simantics/platform.git] /
1 package org.simantics.browsing.ui.nattable;
2
3 import java.util.HashMap;
4 import java.util.Map;
5
6 import org.eclipse.core.runtime.Assert;
7 import org.eclipse.jface.util.Util;
8 import org.eclipse.jface.viewers.ColumnLayoutData;
9 import org.eclipse.jface.viewers.ColumnPixelData;
10 import org.eclipse.jface.viewers.ColumnWeightData;
11 import org.eclipse.nebula.widgets.nattable.NatTable;
12 import org.eclipse.nebula.widgets.nattable.coordinate.Range;
13 import org.eclipse.nebula.widgets.nattable.grid.layer.DefaultRowHeaderDataLayer;
14 import org.eclipse.nebula.widgets.nattable.layer.ILayerListener;
15 import org.eclipse.nebula.widgets.nattable.layer.event.ColumnDeleteEvent;
16 import org.eclipse.nebula.widgets.nattable.layer.event.ColumnInsertEvent;
17 import org.eclipse.nebula.widgets.nattable.layer.event.ILayerEvent;
18 import org.eclipse.nebula.widgets.nattable.resize.event.ColumnResizeEvent;
19 import org.eclipse.swt.graphics.Point;
20 import org.eclipse.swt.graphics.Rectangle;
21 import org.eclipse.swt.widgets.Composite;
22 import org.eclipse.swt.widgets.Layout;
23 import org.eclipse.swt.widgets.Scrollable;
24
25
26 /**
27  * Modified org.eclipse.jface.layout.AbstractColumnLayout and TreeColumnLayout to NatTable compatible.
28  * 
29  * @author Marko Luukkainen <marko.luukkainen@vtt.fi>
30  *
31  */
32 public class NatTableColumnLayout extends Layout implements ILayerListener{
33         private static int COLUMN_TRIM;
34         static {
35                 if (Util.isWindows()) {
36                         COLUMN_TRIM = 4;
37                 } else if (Util.isMac()) {
38                         COLUMN_TRIM = 24;
39                 } else {
40                         COLUMN_TRIM = 3;
41                 }
42         }
43         
44         NatTable natTable;
45         GEColumnHeaderDataProvider columnHeaderDataProvider;
46         DefaultRowHeaderDataLayer rowHeaderDataLayer;
47         Map<Integer, ColumnLayoutData> layoutDatas = new HashMap<>();
48         
49         private boolean inupdateMode = false;
50
51         private boolean relayout = true;
52         
53         public NatTableColumnLayout(NatTable natTable, GEColumnHeaderDataProvider columnHeaderDataProvider) {
54                 this.natTable = natTable;
55                 this.columnHeaderDataProvider = columnHeaderDataProvider;
56                 this.natTable.addLayerListener(this);
57         }
58         
59         public NatTableColumnLayout(NatTable natTable, GEColumnHeaderDataProvider columnHeaderDataProvider, DefaultRowHeaderDataLayer rowHeaderDataLayer) {
60                 this.natTable = natTable;
61                 this.columnHeaderDataProvider = columnHeaderDataProvider;
62                 this.natTable.addLayerListener(this);
63                 this.rowHeaderDataLayer = rowHeaderDataLayer;
64         }
65         
66         public void setColumnData(int column, ColumnLayoutData data) {
67                 layoutDatas.put(column, data);
68         }
69         
70         protected void setColumnWidths(Scrollable tree, int[] widths) {
71                 for (int i=0; i < widths.length; i++) {
72                         columnHeaderDataProvider.getDataLayer().setColumnWidthByPosition(i, widths[i]);
73                 }
74         }
75         
76         @Override
77         public void handleLayerEvent(ILayerEvent event) {
78                 if (inupdateMode)
79                         return;
80                 if (event instanceof ColumnResizeEvent) {
81                         ColumnResizeEvent evt = (ColumnResizeEvent)event;
82                         for (Range r : evt.getColumnPositionRanges()) {
83                                 int colIndex = evt.getLayer().getColumnIndexByPosition(r.start);
84                                 int w = columnHeaderDataProvider.getDataLayer().getColumnWidthByPosition(colIndex);
85                                 setColumnData(colIndex, new ColumnPixelData(w));
86                         }
87                         update();
88                 } else if (event instanceof ColumnInsertEvent ||
89                            event instanceof ColumnDeleteEvent) {
90                         update();
91                 } 
92         }
93         
94         boolean updateCalled = false;
95         
96         private void update() {
97                 if (updateCalled)
98                         return;
99                 updateCalled = true;
100                 natTable.getDisplay().asyncExec(new Runnable() {
101                         
102                         @Override
103                         public void run() {
104                                 if (!natTable.isDisposed()) {
105                                         natTable.update();
106                                         natTable.getParent().layout();
107                                 }
108                                 updateCalled = false;
109                         }
110                 });
111         }
112
113         
114         @Override
115         protected Point computeSize(Composite composite, int wHint, int hHint, boolean flushCache) {
116                 return computeTableTreeSize(getControl(composite), wHint, hHint);
117         }
118         
119         Scrollable getControl(Composite composite) {
120                 return natTable;
121         }
122         
123         
124         @Override
125         protected void layout(Composite composite, boolean flushCache) {
126                 Rectangle area = composite.getClientArea();
127                 Scrollable table = getControl(composite);
128                 int tableWidth = table.getSize().x;
129                 int trim = computeTrim(area, table, tableWidth);
130                 int width = Math.max(0, area.width - trim);
131                 if (rowHeaderDataLayer != null)
132                         width -= rowHeaderDataLayer.getWidth();
133
134                 if (width > 1)
135                         layoutTableTree(table, width, area, tableWidth < area.width);
136
137                 // For the first time we need to relayout because Scrollbars are not
138                 // calculate appropriately
139                 if (relayout) {
140                         relayout = false;
141                         composite.layout();
142                 }
143                 
144         }
145         
146         protected ColumnLayoutData getLayoutData(Scrollable tableTree,
147                         int columnIndex) {
148                 return layoutDatas.get(columnIndex);
149         }
150         
151         protected int getColumnCount(Scrollable tableTree) {
152                 return columnHeaderDataProvider.getColumnCount();
153         }
154         
155         private Point computeTableTreeSize(Scrollable scrollable, int wHint,
156                         int hHint) {
157                 Point result = scrollable.computeSize(wHint, hHint);
158
159                 int width = 0;
160                 int size = getColumnCount(scrollable);
161                 if (rowHeaderDataLayer != null)
162                         width += rowHeaderDataLayer.getWidth();
163                 for (int i = 0; i < size; ++i) {
164                         ColumnLayoutData layoutData = getLayoutData(scrollable, i);
165                         if (layoutData instanceof ColumnPixelData) {
166                                 ColumnPixelData col = (ColumnPixelData) layoutData;
167                                 width += col.width;
168                                 if (col.addTrim) {
169                                         width += getColumnTrim();
170                                 }
171                         } else if (layoutData instanceof ColumnWeightData) {
172                                 ColumnWeightData col = (ColumnWeightData) layoutData;
173                                 width += col.minimumWidth;
174                         } else {
175                                 Assert.isTrue(false, "Unknown column layout data"); //$NON-NLS-1$
176                         }
177                 }
178                 if (width > result.x)
179                         result.x = width;
180
181                 return result;
182         }
183         
184         private void layoutTableTree(final Scrollable scrollable, final int width,
185                         final Rectangle area, final boolean increase) {
186                 final int numberOfColumns = getColumnCount(scrollable);
187                 final int[] widths = new int[numberOfColumns];
188
189                 final int[] weightColumnIndices = new int[numberOfColumns];
190                 int numberOfWeightColumns = 0;
191
192                 int fixedWidth = 0;
193                 int totalWeight = 0;
194
195                 // First calc space occupied by fixed columns
196                 for (int i = 0; i < numberOfColumns; i++) {
197                         ColumnLayoutData col = getLayoutData(scrollable, i);
198                         if (col instanceof ColumnPixelData) {
199                                 ColumnPixelData cpd = (ColumnPixelData) col;
200                                 int pixels = cpd.width;
201                                 if (cpd.addTrim) {
202                                         pixels += getColumnTrim();
203                                 }
204                                 widths[i] = pixels;
205                                 fixedWidth += pixels;
206                         } else if (col instanceof ColumnWeightData) {
207                                 ColumnWeightData cw = (ColumnWeightData) col;
208                                 weightColumnIndices[numberOfWeightColumns] = i;
209                                 numberOfWeightColumns++;
210                                 totalWeight += cw.weight;
211                         } else {
212                                 Assert.isTrue(false, "Unknown column layout data"); //$NON-NLS-1$
213                         }
214                 }
215
216                 boolean recalculate;
217                 do {
218                         recalculate = false;
219                         for (int i = 0; i < numberOfWeightColumns; i++) {
220                                 int colIndex = weightColumnIndices[i];
221                                 ColumnWeightData cw = (ColumnWeightData) getLayoutData(
222                                                 scrollable, colIndex);
223                                 final int minWidth = cw.minimumWidth;
224                                 final int allowedWidth = totalWeight == 0 ? 0
225                                                 : (width - fixedWidth) * cw.weight / totalWeight;
226                                 if (allowedWidth < minWidth) {
227                                         /*
228                                          * if the width assigned by weight is less than the minimum,
229                                          * then treat this column as fixed, remove it from weight
230                                          * calculations, and recalculate other weights.
231                                          */
232                                         numberOfWeightColumns--;
233                                         totalWeight -= cw.weight;
234                                         fixedWidth += minWidth;
235                                         widths[colIndex] = minWidth;
236                                         System.arraycopy(weightColumnIndices, i + 1,
237                                                         weightColumnIndices, i, numberOfWeightColumns - i);
238                                         recalculate = true;
239                                         break;
240                                 }
241                                 widths[colIndex] = allowedWidth;
242                         }
243                 } while (recalculate);
244
245                 if (increase) {
246                         scrollable.setSize(area.width, area.height);
247                 }
248
249                 inupdateMode = true;
250                 setColumnWidths(scrollable, widths);
251                 scrollable.update();
252                 inupdateMode = false;
253
254                 if (!increase) {
255                         scrollable.setSize(area.width, area.height);
256                 }
257         }
258         
259         private int computeTrim(Rectangle area, Scrollable scrollable,
260                         int currentWidth) {
261                 int trim;
262
263                 if (currentWidth > 1) {
264                         trim = currentWidth - scrollable.getClientArea().width;
265                 } else {
266                         // initially, the table has no extend and no client area - use the
267                         // border with
268                         // plus some padding as educated guess
269                         trim = 2 * scrollable.getBorderWidth() + 1;
270                 }
271
272                 return trim;
273         }
274         
275         protected int getColumnTrim() {
276                 return COLUMN_TRIM;
277         }
278         
279         
280         
281
282 }