1 package org.simantics.browsing.ui.nattable;
3 import java.util.HashMap;
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;
27 * Modified org.eclipse.jface.layout.AbstractColumnLayout and TreeColumnLayout to NatTable compatible.
29 * @author Marko Luukkainen <marko.luukkainen@vtt.fi>
32 public class NatTableColumnLayout extends Layout implements ILayerListener{
33 private static int COLUMN_TRIM;
35 if (Util.isWindows()) {
37 } else if (Util.isMac()) {
45 GEColumnHeaderDataProvider columnHeaderDataProvider;
46 DefaultRowHeaderDataLayer rowHeaderDataLayer;
47 Map<Integer, ColumnLayoutData> layoutDatas = new HashMap<>();
49 private boolean inupdateMode = false;
51 private boolean relayout = true;
53 public NatTableColumnLayout(NatTable natTable, GEColumnHeaderDataProvider columnHeaderDataProvider) {
54 this.natTable = natTable;
55 this.columnHeaderDataProvider = columnHeaderDataProvider;
56 this.natTable.addLayerListener(this);
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;
66 public void setColumnData(int column, ColumnLayoutData data) {
67 layoutDatas.put(column, data);
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));
78 for (int i=0; i < widths.length; i++) {
79 columnHeaderDataProvider.getDataLayer().setColumnWidthByPosition(i, widths[i]);
84 public void handleLayerEvent(ILayerEvent event) {
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));
95 } else if (event instanceof ColumnInsertEvent ||
96 event instanceof ColumnDeleteEvent) {
101 boolean updateCalled = false;
103 private void update() {
107 natTable.getDisplay().asyncExec(new Runnable() {
111 if (!natTable.isDisposed()) {
113 natTable.getParent().layout();
115 updateCalled = false;
122 protected Point computeSize(Composite composite, int wHint, int hHint, boolean flushCache) {
123 return computeTableTreeSize(getControl(composite), wHint, hHint);
126 Scrollable getControl(Composite composite) {
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();
142 layoutTableTree(table, width, area, tableWidth < area.width);
144 // For the first time we need to relayout because Scrollbars are not
145 // calculate appropriately
153 protected ColumnLayoutData getLayoutData(Scrollable tableTree,
155 return layoutDatas.get(columnIndex);
158 protected int getColumnCount(Scrollable tableTree) {
159 return columnHeaderDataProvider.getColumnCount();
162 private Point computeTableTreeSize(Scrollable scrollable, int wHint,
164 Point result = scrollable.computeSize(wHint, hHint);
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;
176 width += getColumnTrim();
178 } else if (layoutData instanceof ColumnWeightData) {
179 ColumnWeightData col = (ColumnWeightData) layoutData;
180 width += col.minimumWidth;
182 Assert.isTrue(false, "Unknown column layout data"); //$NON-NLS-1$
185 if (width > result.x)
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];
196 final int[] weightColumnIndices = new int[numberOfColumns];
197 int numberOfWeightColumns = 0;
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;
209 pixels += getColumnTrim();
212 fixedWidth += pixels;
213 } else if (col instanceof ColumnWeightData) {
214 ColumnWeightData cw = (ColumnWeightData) col;
215 weightColumnIndices[numberOfWeightColumns] = i;
216 numberOfWeightColumns++;
217 totalWeight += cw.weight;
219 Assert.isTrue(false, "Unknown column layout data"); //$NON-NLS-1$
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) {
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.
239 numberOfWeightColumns--;
240 totalWeight -= cw.weight;
241 fixedWidth += minWidth;
242 widths[colIndex] = minWidth;
243 System.arraycopy(weightColumnIndices, i + 1,
244 weightColumnIndices, i, numberOfWeightColumns - i);
248 widths[colIndex] = allowedWidth;
250 } while (recalculate);
253 scrollable.setSize(area.width, area.height);
257 setColumnWidths(scrollable, widths);
259 inupdateMode = false;
262 scrollable.setSize(area.width, area.height);
266 private int computeTrim(Rectangle area, Scrollable scrollable,
270 if (currentWidth > 1) {
271 trim = currentWidth - scrollable.getClientArea().width;
273 // initially, the table has no extend and no client area - use the
275 // plus some padding as educated guess
276 trim = 2 * scrollable.getBorderWidth() + 1;
282 protected int getColumnTrim() {