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