]> gerrit.simantics Code Review - simantics/platform.git/blob - bundles/org.simantics.browsing.ui.nattable/src/org/simantics/browsing/ui/nattable/NatTableColumnLayout.java
UI locking fixes for GraphExplorer implementations
[simantics/platform.git] / bundles / org.simantics.browsing.ui.nattable / src / org / simantics / browsing / ui / nattable / NatTableColumnLayout.java
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                 // NatTable HiDPI workaround
72                 double displayScale = NatTableGraphExplorer.getDisplayScale();
73                 if (displayScale != 1.0) {
74                         for (int i=0; i < widths.length; i++) {
75                                 widths[i] = (int)Math.floor(((double)widths[i]/ displayScale));
76                         }
77                 }
78                 for (int i=0; i < widths.length; i++) {
79                         columnHeaderDataProvider.getDataLayer().setColumnWidthByPosition(i, widths[i]);
80                 }
81         }
82         
83         @Override
84         public void handleLayerEvent(ILayerEvent event) {
85                 if (inupdateMode)
86                         return;
87                 if (event instanceof ColumnResizeEvent) {
88                         ColumnResizeEvent evt = (ColumnResizeEvent)event;
89                         for (Range r : evt.getColumnPositionRanges()) {
90                                 int colIndex = evt.getLayer().getColumnIndexByPosition(r.start);
91                                 int w = columnHeaderDataProvider.getDataLayer().getColumnWidthByPosition(colIndex);
92                                 setColumnData(colIndex, new ColumnPixelData(w));
93                         }
94                         update();
95                 } else if (event instanceof ColumnInsertEvent ||
96                            event instanceof ColumnDeleteEvent) {
97                         update();
98                 } 
99         }
100         
101         boolean updateCalled = false;
102         
103         private void update() {
104                 if (updateCalled)
105                         return;
106                 updateCalled = true;
107                 natTable.getDisplay().asyncExec(new Runnable() {
108                         
109                         @Override
110                         public void run() {
111                                 if (!natTable.isDisposed()) {
112                                         natTable.update();
113                                         natTable.getParent().layout();
114                                 }
115                                 updateCalled = false;
116                         }
117                 });
118         }
119
120         
121         @Override
122         protected Point computeSize(Composite composite, int wHint, int hHint, boolean flushCache) {
123                 return computeTableTreeSize(getControl(composite), wHint, hHint);
124         }
125         
126         Scrollable getControl(Composite composite) {
127                 return natTable;
128         }
129         
130         
131         @Override
132         protected void layout(Composite composite, boolean flushCache) {
133                 Rectangle area = composite.getClientArea();
134                 Scrollable table = getControl(composite);
135                 int tableWidth = table.getSize().x;
136                 int trim = computeTrim(area, table, tableWidth);
137                 int width = Math.max(0, area.width - trim);
138                 if (rowHeaderDataLayer != null)
139                         width -= rowHeaderDataLayer.getWidth();
140
141                 if (width > 1)
142                         layoutTableTree(table, width, area, tableWidth < area.width);
143
144                 // For the first time we need to relayout because Scrollbars are not
145                 // calculate appropriately
146                 if (relayout) {
147                         relayout = false;
148                         composite.layout();
149                 }
150                 
151         }
152         
153         protected ColumnLayoutData getLayoutData(Scrollable tableTree,
154                         int columnIndex) {
155                 return layoutDatas.get(columnIndex);
156         }
157         
158         protected int getColumnCount(Scrollable tableTree) {
159                 return columnHeaderDataProvider.getColumnCount();
160         }
161         
162         private Point computeTableTreeSize(Scrollable scrollable, int wHint,
163                         int hHint) {
164                 Point result = scrollable.computeSize(wHint, hHint);
165
166                 int width = 0;
167                 int size = getColumnCount(scrollable);
168                 if (rowHeaderDataLayer != null)
169                         width += rowHeaderDataLayer.getWidth();
170                 for (int i = 0; i < size; ++i) {
171                         ColumnLayoutData layoutData = getLayoutData(scrollable, i);
172                         if (layoutData instanceof ColumnPixelData) {
173                                 ColumnPixelData col = (ColumnPixelData) layoutData;
174                                 width += col.width;
175                                 if (col.addTrim) {
176                                         width += getColumnTrim();
177                                 }
178                         } else if (layoutData instanceof ColumnWeightData) {
179                                 ColumnWeightData col = (ColumnWeightData) layoutData;
180                                 width += col.minimumWidth;
181                         } else {
182                                 Assert.isTrue(false, "Unknown column layout data"); //$NON-NLS-1$
183                         }
184                 }
185                 if (width > result.x)
186                         result.x = width;
187
188                 return result;
189         }
190         
191         private void layoutTableTree(final Scrollable scrollable, final int width,
192                         final Rectangle area, final boolean increase) {
193                 final int numberOfColumns = getColumnCount(scrollable);
194                 final int[] widths = new int[numberOfColumns];
195
196                 final int[] weightColumnIndices = new int[numberOfColumns];
197                 int numberOfWeightColumns = 0;
198
199                 int fixedWidth = 0;
200                 int totalWeight = 0;
201
202                 // First calc space occupied by fixed columns
203                 for (int i = 0; i < numberOfColumns; i++) {
204                         ColumnLayoutData col = getLayoutData(scrollable, i);
205                         if (col instanceof ColumnPixelData) {
206                                 ColumnPixelData cpd = (ColumnPixelData) col;
207                                 int pixels = cpd.width;
208                                 if (cpd.addTrim) {
209                                         pixels += getColumnTrim();
210                                 }
211                                 widths[i] = pixels;
212                                 fixedWidth += pixels;
213                         } else if (col instanceof ColumnWeightData) {
214                                 ColumnWeightData cw = (ColumnWeightData) col;
215                                 weightColumnIndices[numberOfWeightColumns] = i;
216                                 numberOfWeightColumns++;
217                                 totalWeight += cw.weight;
218                         } else {
219                                 Assert.isTrue(false, "Unknown column layout data"); //$NON-NLS-1$
220                         }
221                 }
222
223                 boolean recalculate;
224                 do {
225                         recalculate = false;
226                         for (int i = 0; i < numberOfWeightColumns; i++) {
227                                 int colIndex = weightColumnIndices[i];
228                                 ColumnWeightData cw = (ColumnWeightData) getLayoutData(
229                                                 scrollable, colIndex);
230                                 final int minWidth = cw.minimumWidth;
231                                 final int allowedWidth = totalWeight == 0 ? 0
232                                                 : (width - fixedWidth) * cw.weight / totalWeight;
233                                 if (allowedWidth < minWidth) {
234                                         /*
235                                          * if the width assigned by weight is less than the minimum,
236                                          * then treat this column as fixed, remove it from weight
237                                          * calculations, and recalculate other weights.
238                                          */
239                                         numberOfWeightColumns--;
240                                         totalWeight -= cw.weight;
241                                         fixedWidth += minWidth;
242                                         widths[colIndex] = minWidth;
243                                         System.arraycopy(weightColumnIndices, i + 1,
244                                                         weightColumnIndices, i, numberOfWeightColumns - i);
245                                         recalculate = true;
246                                         break;
247                                 }
248                                 widths[colIndex] = allowedWidth;
249                         }
250                 } while (recalculate);
251
252                 if (increase) {
253                         scrollable.setSize(area.width, area.height);
254                 }
255
256                 inupdateMode = true;
257                 setColumnWidths(scrollable, widths);
258                 scrollable.update();
259                 inupdateMode = false;
260
261                 if (!increase) {
262                         scrollable.setSize(area.width, area.height);
263                 }
264         }
265         
266         private int computeTrim(Rectangle area, Scrollable scrollable,
267                         int currentWidth) {
268                 int trim;
269
270                 if (currentWidth > 1) {
271                         trim = currentWidth - scrollable.getClientArea().width;
272                 } else {
273                         // initially, the table has no extend and no client area - use the
274                         // border with
275                         // plus some padding as educated guess
276                         trim = 2 * scrollable.getBorderWidth() + 1;
277                 }
278
279                 return trim;
280         }
281         
282         protected int getColumnTrim() {
283                 return COLUMN_TRIM;
284         }
285         
286         
287         
288
289 }