package org.simantics.browsing.ui.nattable; import java.util.HashMap; import java.util.Map; import org.eclipse.core.runtime.Assert; import org.eclipse.jface.util.Util; import org.eclipse.jface.viewers.ColumnLayoutData; import org.eclipse.jface.viewers.ColumnPixelData; import org.eclipse.jface.viewers.ColumnWeightData; import org.eclipse.nebula.widgets.nattable.NatTable; import org.eclipse.nebula.widgets.nattable.coordinate.Range; import org.eclipse.nebula.widgets.nattable.grid.layer.DefaultRowHeaderDataLayer; import org.eclipse.nebula.widgets.nattable.layer.ILayerListener; import org.eclipse.nebula.widgets.nattable.layer.event.ColumnDeleteEvent; import org.eclipse.nebula.widgets.nattable.layer.event.ColumnInsertEvent; import org.eclipse.nebula.widgets.nattable.layer.event.ILayerEvent; import org.eclipse.nebula.widgets.nattable.resize.event.ColumnResizeEvent; import org.eclipse.swt.graphics.Point; import org.eclipse.swt.graphics.Rectangle; import org.eclipse.swt.widgets.Composite; import org.eclipse.swt.widgets.Layout; import org.eclipse.swt.widgets.Scrollable; /** * Modified org.eclipse.jface.layout.AbstractColumnLayout and TreeColumnLayout to NatTable compatible. * * @author Marko Luukkainen * */ public class NatTableColumnLayout extends Layout implements ILayerListener{ private static int COLUMN_TRIM; static { if (Util.isWindows()) { COLUMN_TRIM = 4; } else if (Util.isMac()) { COLUMN_TRIM = 24; } else { COLUMN_TRIM = 3; } } NatTable natTable; GEColumnHeaderDataProvider columnHeaderDataProvider; DefaultRowHeaderDataLayer rowHeaderDataLayer; Map layoutDatas = new HashMap<>(); private boolean inupdateMode = false; private boolean relayout = true; public NatTableColumnLayout(NatTable natTable, GEColumnHeaderDataProvider columnHeaderDataProvider) { this.natTable = natTable; this.columnHeaderDataProvider = columnHeaderDataProvider; this.natTable.addLayerListener(this); } public NatTableColumnLayout(NatTable natTable, GEColumnHeaderDataProvider columnHeaderDataProvider, DefaultRowHeaderDataLayer rowHeaderDataLayer) { this.natTable = natTable; this.columnHeaderDataProvider = columnHeaderDataProvider; this.natTable.addLayerListener(this); this.rowHeaderDataLayer = rowHeaderDataLayer; } public void setColumnData(int column, ColumnLayoutData data) { layoutDatas.put(column, data); } protected void setColumnWidths(Scrollable tree, int[] widths) { for (int i=0; i < widths.length; i++) { columnHeaderDataProvider.getDataLayer().setColumnWidthByPosition(i, widths[i]); } } @Override public void handleLayerEvent(ILayerEvent event) { if (inupdateMode) return; if (event instanceof ColumnResizeEvent) { ColumnResizeEvent evt = (ColumnResizeEvent)event; for (Range r : evt.getColumnPositionRanges()) { int colIndex = evt.getLayer().getColumnIndexByPosition(r.start); int w = columnHeaderDataProvider.getDataLayer().getColumnWidthByPosition(colIndex); setColumnData(colIndex, new ColumnPixelData(w)); } update(); } else if (event instanceof ColumnInsertEvent || event instanceof ColumnDeleteEvent) { update(); } } boolean updateCalled = false; private void update() { if (updateCalled) return; updateCalled = true; natTable.getDisplay().asyncExec(new Runnable() { @Override public void run() { if (!natTable.isDisposed()) { natTable.update(); natTable.getParent().layout(); } updateCalled = false; } }); } @Override protected Point computeSize(Composite composite, int wHint, int hHint, boolean flushCache) { return computeTableTreeSize(getControl(composite), wHint, hHint); } Scrollable getControl(Composite composite) { return natTable; } @Override protected void layout(Composite composite, boolean flushCache) { Rectangle area = composite.getClientArea(); Scrollable table = getControl(composite); int tableWidth = table.getSize().x; int trim = computeTrim(area, table, tableWidth); int width = Math.max(0, area.width - trim); if (rowHeaderDataLayer != null) width -= rowHeaderDataLayer.getWidth(); if (width > 1) layoutTableTree(table, width, area, tableWidth < area.width); // For the first time we need to relayout because Scrollbars are not // calculate appropriately if (relayout) { relayout = false; composite.layout(); } } protected ColumnLayoutData getLayoutData(Scrollable tableTree, int columnIndex) { return layoutDatas.get(columnIndex); } protected int getColumnCount(Scrollable tableTree) { return columnHeaderDataProvider.getColumnCount(); } private Point computeTableTreeSize(Scrollable scrollable, int wHint, int hHint) { Point result = scrollable.computeSize(wHint, hHint); int width = 0; int size = getColumnCount(scrollable); if (rowHeaderDataLayer != null) width += rowHeaderDataLayer.getWidth(); for (int i = 0; i < size; ++i) { ColumnLayoutData layoutData = getLayoutData(scrollable, i); if (layoutData instanceof ColumnPixelData) { ColumnPixelData col = (ColumnPixelData) layoutData; width += col.width; if (col.addTrim) { width += getColumnTrim(); } } else if (layoutData instanceof ColumnWeightData) { ColumnWeightData col = (ColumnWeightData) layoutData; width += col.minimumWidth; } else { Assert.isTrue(false, "Unknown column layout data"); //$NON-NLS-1$ } } if (width > result.x) result.x = width; return result; } private void layoutTableTree(final Scrollable scrollable, final int width, final Rectangle area, final boolean increase) { final int numberOfColumns = getColumnCount(scrollable); final int[] widths = new int[numberOfColumns]; final int[] weightColumnIndices = new int[numberOfColumns]; int numberOfWeightColumns = 0; int fixedWidth = 0; int totalWeight = 0; // First calc space occupied by fixed columns for (int i = 0; i < numberOfColumns; i++) { ColumnLayoutData col = getLayoutData(scrollable, i); if (col instanceof ColumnPixelData) { ColumnPixelData cpd = (ColumnPixelData) col; int pixels = cpd.width; if (cpd.addTrim) { pixels += getColumnTrim(); } widths[i] = pixels; fixedWidth += pixels; } else if (col instanceof ColumnWeightData) { ColumnWeightData cw = (ColumnWeightData) col; weightColumnIndices[numberOfWeightColumns] = i; numberOfWeightColumns++; totalWeight += cw.weight; } else { Assert.isTrue(false, "Unknown column layout data"); //$NON-NLS-1$ } } boolean recalculate; do { recalculate = false; for (int i = 0; i < numberOfWeightColumns; i++) { int colIndex = weightColumnIndices[i]; ColumnWeightData cw = (ColumnWeightData) getLayoutData( scrollable, colIndex); final int minWidth = cw.minimumWidth; final int allowedWidth = totalWeight == 0 ? 0 : (width - fixedWidth) * cw.weight / totalWeight; if (allowedWidth < minWidth) { /* * if the width assigned by weight is less than the minimum, * then treat this column as fixed, remove it from weight * calculations, and recalculate other weights. */ numberOfWeightColumns--; totalWeight -= cw.weight; fixedWidth += minWidth; widths[colIndex] = minWidth; System.arraycopy(weightColumnIndices, i + 1, weightColumnIndices, i, numberOfWeightColumns - i); recalculate = true; break; } widths[colIndex] = allowedWidth; } } while (recalculate); if (increase) { scrollable.setSize(area.width, area.height); } inupdateMode = true; setColumnWidths(scrollable, widths); scrollable.update(); inupdateMode = false; if (!increase) { scrollable.setSize(area.width, area.height); } } private int computeTrim(Rectangle area, Scrollable scrollable, int currentWidth) { int trim; if (currentWidth > 1) { trim = currentWidth - scrollable.getClientArea().width; } else { // initially, the table has no extend and no client area - use the // border with // plus some padding as educated guess trim = 2 * scrollable.getBorderWidth() + 1; } return trim; } protected int getColumnTrim() { return COLUMN_TRIM; } }