package org.openlca.app.util.tables; import java.util.List; import java.util.Timer; import java.util.TimerTask; import java.util.function.Consumer; import org.eclipse.jface.viewers.ArrayContentProvider; import org.eclipse.jface.viewers.CellLabelProvider; import org.eclipse.jface.viewers.ColumnViewerToolTipSupport; import org.eclipse.jface.viewers.IBaseLabelProvider; import org.eclipse.jface.viewers.TableViewer; import org.eclipse.jface.viewers.TableViewerColumn; import org.eclipse.jface.window.ToolTip; import org.eclipse.swt.SWT; import org.eclipse.swt.dnd.DND; import org.eclipse.swt.dnd.DropTarget; import org.eclipse.swt.dnd.DropTargetAdapter; import org.eclipse.swt.dnd.DropTargetEvent; import org.eclipse.swt.dnd.Transfer; import org.eclipse.swt.events.ControlAdapter; import org.eclipse.swt.events.ControlEvent; import org.eclipse.swt.events.MouseAdapter; import org.eclipse.swt.events.MouseEvent; import org.eclipse.swt.events.SelectionAdapter; import org.eclipse.swt.events.SelectionEvent; import org.eclipse.swt.graphics.Point; import org.eclipse.swt.layout.GridData; import org.eclipse.swt.widgets.Composite; import org.eclipse.swt.widgets.Event; import org.eclipse.swt.widgets.Table; import org.eclipse.swt.widgets.TableColumn; import org.eclipse.swt.widgets.TableItem; import org.openlca.app.components.IModelDropHandler; import org.openlca.app.components.ModelTransfer; import org.openlca.app.util.UI; import org.openlca.app.util.viewers.Sorter; import org.openlca.core.model.descriptors.BaseDescriptor; /** * A helper class for creating tables, table viewers and related resources. */ public class Tables { public static TableViewer createViewer(Composite parent, String... properties) { return createViewer(parent, properties, null); } /** * Creates a default table viewer with the given properties. The properties * are also used to create columns where each column label is the respective * property of this column. The viewer is configured in the following way: * <ul> * <li>content provider = {@link ArrayContentProvider} * <li>lines and header are visible * <li>grid data with horizontal and vertical fill * */ public static TableViewer createViewer(Composite parent, String[] properties, IBaseLabelProvider labelProvider) { TableViewer viewer = new TableViewer(parent, SWT.BORDER | SWT.FULL_SELECTION | SWT.VIRTUAL | SWT.MULTI); viewer.setContentProvider(new ArrayContentProvider()); boolean hasColumns = properties != null && properties.length > 0; Table table = viewer.getTable(); table.setLinesVisible(hasColumns); table.setHeaderVisible(hasColumns); if (hasColumns) { createColumns(viewer, properties, labelProvider); } if (labelProvider != null) { viewer.setLabelProvider(labelProvider); } GridData data = UI.gridData(table, true, true); data.minimumHeight = 150; return viewer; } private static void createColumns(TableViewer viewer, String[] labels, IBaseLabelProvider labelProvider) { if (labelProvider instanceof CellLabelProvider) { ColumnViewerToolTipSupport.enableFor(viewer, ToolTip.NO_RECREATE); } viewer.setColumnProperties(labels); for (String label : labels) { TableViewerColumn c = new TableViewerColumn(viewer, SWT.NULL); c.getColumn().setText(label); if (labelProvider instanceof CellLabelProvider) { c.setLabelProvider((CellLabelProvider) labelProvider); } } for (TableColumn c : viewer.getTable().getColumns()) c.pack(); } public static void addDropSupport(TableViewer table, IModelDropHandler handler) { final Transfer transfer = ModelTransfer.getInstance(); DropTarget dropTarget = new DropTarget(table.getTable(), DND.DROP_COPY | DND.DROP_MOVE | DND.DROP_DEFAULT); dropTarget.setTransfer(new Transfer[] { transfer }); dropTarget.addDropListener(new DropTargetAdapter() { @Override public void drop(DropTargetEvent event) { if (!transfer.isSupportedType(event.currentDataType)) return; List<BaseDescriptor> list = ModelTransfer .getBaseDescriptors(event.data); handler.handleDrop(list); } }); } public static void bindColumnWidths(TableViewer viewer, double... percents) { bindColumnWidths(viewer.getTable(), percents); } public static void bindColumnWidths(TableViewer viewer, int minimum, double... percents) { bindColumnWidths(viewer.getTable(), minimum, percents); } /** * Binds the given percentage values (values between 0 and 1) to the column * widths of the given table */ public static void bindColumnWidths(Table table, double... percents) { bindColumnWidths(table, 0, percents); } public static void bindColumnWidths(Table table, int minimum, double... percents) { if (table == null || percents == null) return; TableResizeListener tableListener = new TableResizeListener(table, percents, minimum); // see resize listener declaration for comment on why this is done ColumnResizeListener columnListener = new ColumnResizeListener(tableListener); for (TableColumn column : table.getColumns()) column.addControlListener(columnListener); table.addControlListener(tableListener); } /** Add an event handler for double clicks on the given table viewer. */ public static void onDoubleClick(TableViewer viewer, Consumer<MouseEvent> handler) { if (viewer == null || viewer.getTable() == null || handler == null) return; viewer.getTable().addMouseListener(new MouseAdapter() { @Override public void mouseDoubleClick(MouseEvent e) { handler.accept(e); } }); } /** * Get the table item where the given event occurred. Returns null if the * event occurred in the empty table area. */ public static TableItem getItem(TableViewer viewer, MouseEvent event) { if (viewer == null || event == null) return null; Table table = viewer.getTable(); if (table == null) return null; return table.getItem(new Point(event.x, event.y)); } public static void onDeletePressed(TableViewer viewer, Consumer<Event> handler) { if (viewer == null || viewer.getTable() == null || handler == null) return; viewer.getTable().addListener(SWT.KeyUp, (event) -> { if (event.keyCode == SWT.DEL) { handler.accept(event); } }); } public static void addSorter(TableViewer viewer, Sorter<?> sorter) { Table table = viewer.getTable(); if (sorter.column >= table.getColumnCount()) return; TableColumn column = table.getColumn(sorter.column); column.addSelectionListener(new SelectionAdapter() { @Override public void widgetSelected(SelectionEvent e) { TableColumn current = table.getSortColumn(); if (column == current) sorter.ascending = !sorter.ascending; else sorter.ascending = true; int direction = sorter.ascending ? SWT.UP : SWT.DOWN; table.setSortDirection(direction); table.setSortColumn(column); viewer.setSorter(sorter); viewer.refresh(); } }); } // In order to be able to resize columns manually, we must know if a column // was resized before, and in those cases, don't resize the columns // automatically. private static class ColumnResizeListener extends ControlAdapter { private TableResizeListener depending; private boolean enabled = true; private boolean initialized; private ColumnResizeListener(TableResizeListener depending) { this.depending = depending; } @Override public void controlResized(ControlEvent e) { if (!enabled) return; if (!initialized) { initialized = true; return; } depending.enabled = false; enabled = false; Timer t = new Timer(); t.schedule(new TimerTask() { @Override public void run() { depending.enabled = true; enabled = true; } }, 100); } } private static class TableResizeListener extends ControlAdapter { private Table table; private double[] percents; private int minimum = 0; private boolean enabled = true; private TableResizeListener(Table table, double[] percents, int mininmum) { this.table = table; this.percents = percents; this.minimum = mininmum; } @Override public void controlResized(ControlEvent e) { if (!enabled) return; double width = table.getSize().x - 25; TableColumn[] columns = table.getColumns(); int longest = -1; double max = 0; double additional = 0; for (int i = 0; i < columns.length; i++) { if (i >= percents.length) break; double colWidth = percents[i] * width; if (max < colWidth) { max = colWidth; longest = i; } if (minimum > 0 && colWidth < minimum) { additional += minimum - colWidth; colWidth = minimum; } columns[i].setWidth((int) colWidth); } if (additional == 0 || longest == -1) return; columns[longest].setWidth((int) (percents[longest] * width - additional)); } } }