From: jsimomaa Date: Wed, 12 Aug 2020 14:35:56 +0000 (+0300) Subject: First testing version of TechTypeTable X-Git-Url: https://gerrit.simantics.org/r/gitweb?a=commitdiff_plain;h=refs%2Fchanges%2F12%2F4412%2F1;p=simantics%2Fdistrict.git First testing version of TechTypeTable gitlab #93 Change-Id: I341d7a7735ee8dbc954a53492563f46a18cb3bb7 --- diff --git a/org.simantics.district.network.ui/src/org/simantics/district/network/ui/table/ImportTechTypeCSVHandler.java b/org.simantics.district.network.ui/src/org/simantics/district/network/ui/table/ImportTechTypeCSVHandler.java new file mode 100644 index 00000000..7ee841b4 --- /dev/null +++ b/org.simantics.district.network.ui/src/org/simantics/district/network/ui/table/ImportTechTypeCSVHandler.java @@ -0,0 +1,53 @@ +package org.simantics.district.network.ui.table; + +import java.nio.file.Files; +import java.nio.file.Path; +import java.nio.file.Paths; +import java.util.Map; + +import javax.inject.Inject; +import javax.inject.Named; + +import org.eclipse.e4.core.di.annotations.Execute; +import org.eclipse.e4.ui.services.IServiceConstants; +import org.eclipse.e4.ui.workbench.modeling.EPartService; +import org.eclipse.swt.widgets.FileDialog; +import org.eclipse.swt.widgets.Shell; +import org.simantics.district.imports.DistrictImportUtils; +import org.simantics.district.network.ui.techtype.table.TechTypeTableView; +import org.simantics.utils.ui.ExceptionUtils; +import org.slf4j.Logger; +import org.slf4j.LoggerFactory; + +public class ImportTechTypeCSVHandler { + + private static final Logger LOGGER = LoggerFactory.getLogger(ImportTechTypeCSVHandler.class); + + @Inject + EPartService partService; + + @Execute + public void execute(@Named(IServiceConstants.ACTIVE_SHELL) Shell s) { + // here we can import based on the current CSV table data + + FileDialog dialog = new FileDialog(s); + String path = dialog.open(); + try { + if (path != null) { + + Path p = Paths.get(path); + if (Files.exists(p)) { + Map readCSVHeader = DistrictImportUtils.readCSVHeader(p, ';', true); + TechTypeTableView.table.setTechTypePath(path); + } else { + LOGGER.warn("Path does not exist even though path != null: {}", p); + } + } else { + LOGGER.error("Invalid file path given {}", path); + } + } catch (Exception e) { + LOGGER.error("Could not read file {}", path, e); + ExceptionUtils.logAndShowError("Could not read file " + path + " : " + e.getMessage(), e); + } + } +} diff --git a/org.simantics.district.network.ui/src/org/simantics/district/network/ui/techtype/table/TechTypeColumnHeaderTableDataProvider.java b/org.simantics.district.network.ui/src/org/simantics/district/network/ui/techtype/table/TechTypeColumnHeaderTableDataProvider.java new file mode 100644 index 00000000..9b632192 --- /dev/null +++ b/org.simantics.district.network.ui/src/org/simantics/district/network/ui/techtype/table/TechTypeColumnHeaderTableDataProvider.java @@ -0,0 +1,33 @@ +package org.simantics.district.network.ui.techtype.table; + +import org.eclipse.nebula.widgets.nattable.data.IDataProvider; + +public class TechTypeColumnHeaderTableDataProvider implements IDataProvider { + + private TechTypeTableDataProvider bodyDataProvider; + + public TechTypeColumnHeaderTableDataProvider(TechTypeTableDataProvider bodyDataProvider) { + this.bodyDataProvider = bodyDataProvider; + } + + @Override + public Object getDataValue(int columnIndex, int rowIndex) { + return bodyDataProvider.getHeaderValue(columnIndex); + } + + @Override + public void setDataValue(int columnIndex, int rowIndex, Object newValue) { + + } + + @Override + public int getColumnCount() { + return bodyDataProvider.getColumnCount(); + } + + @Override + public int getRowCount() { + return 1; + } + +} diff --git a/org.simantics.district.network.ui/src/org/simantics/district/network/ui/techtype/table/TechTypeRowHeaderTableDataProvider.java b/org.simantics.district.network.ui/src/org/simantics/district/network/ui/techtype/table/TechTypeRowHeaderTableDataProvider.java new file mode 100644 index 00000000..4d21bd55 --- /dev/null +++ b/org.simantics.district.network.ui/src/org/simantics/district/network/ui/techtype/table/TechTypeRowHeaderTableDataProvider.java @@ -0,0 +1,33 @@ +package org.simantics.district.network.ui.techtype.table; + +import org.eclipse.nebula.widgets.nattable.data.IDataProvider; + +public class TechTypeRowHeaderTableDataProvider implements IDataProvider { + + protected final IDataProvider bodyDataProvider; + + public TechTypeRowHeaderTableDataProvider(IDataProvider bodyDataProvider) { + this.bodyDataProvider = bodyDataProvider; + } + + @Override + public int getColumnCount() { + return 1; + } + + @Override + public int getRowCount() { + return this.bodyDataProvider.getRowCount(); + } + + @Override + public Object getDataValue(int columnIndex, int rowIndex) { + return Integer.valueOf(rowIndex + 1); + } + + @Override + public void setDataValue(int columnIndex, int rowIndex, Object newValue) { + throw new UnsupportedOperationException(); + } + +} diff --git a/org.simantics.district.network.ui/src/org/simantics/district/network/ui/techtype/table/TechTypeTable.java b/org.simantics.district.network.ui/src/org/simantics/district/network/ui/techtype/table/TechTypeTable.java new file mode 100644 index 00000000..725700f8 --- /dev/null +++ b/org.simantics.district.network.ui/src/org/simantics/district/network/ui/techtype/table/TechTypeTable.java @@ -0,0 +1,181 @@ +package org.simantics.district.network.ui.techtype.table; + +import java.io.Serializable; + +import org.eclipse.jface.layout.GridDataFactory; +import org.eclipse.jface.layout.GridLayoutFactory; +import org.eclipse.nebula.widgets.nattable.NatTable; +import org.eclipse.nebula.widgets.nattable.config.DefaultNatTableStyleConfiguration; +import org.eclipse.nebula.widgets.nattable.config.IConfigRegistry; +import org.eclipse.nebula.widgets.nattable.copy.command.CopyDataCommandHandler; +import org.eclipse.nebula.widgets.nattable.data.IDataProvider; +import org.eclipse.nebula.widgets.nattable.data.IRowIdAccessor; +import org.eclipse.nebula.widgets.nattable.freeze.CompositeFreezeLayer; +import org.eclipse.nebula.widgets.nattable.freeze.FreezeLayer; +import org.eclipse.nebula.widgets.nattable.grid.GridRegion; +import org.eclipse.nebula.widgets.nattable.grid.data.DefaultCornerDataProvider; +import org.eclipse.nebula.widgets.nattable.grid.layer.ColumnHeaderLayer; +import org.eclipse.nebula.widgets.nattable.grid.layer.CornerLayer; +import org.eclipse.nebula.widgets.nattable.grid.layer.DefaultColumnHeaderDataLayer; +import org.eclipse.nebula.widgets.nattable.grid.layer.DefaultRowHeaderDataLayer; +import org.eclipse.nebula.widgets.nattable.grid.layer.GridLayer; +import org.eclipse.nebula.widgets.nattable.grid.layer.RowHeaderLayer; +import org.eclipse.nebula.widgets.nattable.group.ColumnGroupHeaderLayer; +import org.eclipse.nebula.widgets.nattable.group.ColumnGroupModel; +import org.eclipse.nebula.widgets.nattable.hideshow.ColumnHideShowLayer; +import org.eclipse.nebula.widgets.nattable.hover.HoverLayer; +import org.eclipse.nebula.widgets.nattable.hover.config.BodyHoverStylingBindings; +import org.eclipse.nebula.widgets.nattable.layer.DataLayer; +import org.eclipse.nebula.widgets.nattable.layer.ILayer; +import org.eclipse.nebula.widgets.nattable.layer.IUniqueIndexLayer; +import org.eclipse.nebula.widgets.nattable.reorder.RowReorderLayer; +import org.eclipse.nebula.widgets.nattable.selection.RowSelectionModel; +import org.eclipse.nebula.widgets.nattable.selection.SelectionLayer; +import org.eclipse.nebula.widgets.nattable.sort.SortHeaderLayer; +import org.eclipse.nebula.widgets.nattable.viewport.ViewportLayer; +import org.eclipse.swt.SWT; +import org.eclipse.swt.dnd.Clipboard; +import org.eclipse.swt.events.ModifyEvent; +import org.eclipse.swt.events.ModifyListener; +import org.eclipse.swt.widgets.Composite; +import org.eclipse.swt.widgets.Text; + +public class TechTypeTable extends Composite { + + NatTable table; + private TechTypeTableDataProvider bodyDataProvider; + DataLayer bodyDataLayer; + private IConfigRegistry summaryConfigRegistry; + private IUniqueIndexLayer summaryRowLayer; + private ViewportLayer viewportLayer; + private CompositeFreezeLayer compositeFreezeLayer; + private FreezeLayer freezeLayer; + //private TableDataSortModel sortModel; + private ColumnHideShowLayer columnHideShowLayer; + private ColumnGroupModel columnGroupModel = new ColumnGroupModel(); + private TechTypeColumnHeaderTableDataProvider columnHeaderDataProvider; + Clipboard cpb; + public SelectionLayer selectionLayer; + private TechTypeTableSortModel sortModel; + + public TechTypeTable(Composite parent, int style) { + super(parent, style); + defaultInitializeUI(); + } + + private void defaultInitializeUI() { + GridDataFactory.fillDefaults().grab(true, true).applyTo(this); + GridLayoutFactory.fillDefaults().numColumns(1).applyTo(this); + + Composite filterComposite = new Composite(this, SWT.NONE); + GridDataFactory.fillDefaults().grab(true, false).applyTo(filterComposite); + GridLayoutFactory.fillDefaults().numColumns(1).applyTo(filterComposite); + + + createFilterBar(filterComposite); + + Composite tableComposite = new Composite(this, SWT.NONE); + GridDataFactory.fillDefaults().grab(true, true).applyTo(tableComposite); + GridLayoutFactory.fillDefaults().numColumns(1).applyTo(tableComposite); + createTable(tableComposite); + } + + private void createFilterBar(Composite filterComposite) { + + Text filterText = new Text(filterComposite, SWT.BORDER); + GridDataFactory.fillDefaults().grab(true, true).applyTo(filterText); + filterText.addModifyListener(new ModifyListener() { + + @Override + public void modifyText(ModifyEvent e) { + System.out.println("text modified"); + bodyDataProvider.setFilter(filterText.getText()); + table.refresh(true); + } + }); + + } + + private void createTable(Composite parent) { + + // build the body layer stack + // Usually you would create a new layer stack by extending AbstractIndexLayerTransform and + // setting the ViewportLayer as underlying layer. But in this case using the ViewportLayer + // directly as body layer is also working. + bodyDataProvider = new TechTypeTableDataProvider(); + bodyDataLayer = new DataLayer(bodyDataProvider); + + RowReorderLayer rowReorderLayer = + new RowReorderLayer(columnHideShowLayer = new ColumnHideShowLayer(bodyDataLayer)); + + HoverLayer hoverLayer = new HoverLayer(rowReorderLayer, false); + // we need to ensure that the hover styling is removed when the mouse + // cursor moves out of the cell area + hoverLayer.addConfiguration(new BodyHoverStylingBindings(hoverLayer)); + + selectionLayer = new SelectionLayer(hoverLayer); + + viewportLayer = new ViewportLayer(selectionLayer); + viewportLayer.setRegionName(GridRegion.BODY); + freezeLayer = new FreezeLayer(selectionLayer); + compositeFreezeLayer = new CompositeFreezeLayer(freezeLayer, viewportLayer, selectionLayer); + + // build the column header layer + columnHeaderDataProvider = new TechTypeColumnHeaderTableDataProvider(bodyDataProvider); + DataLayer columnHeaderDataLayer = new DefaultColumnHeaderDataLayer(columnHeaderDataProvider); + columnHeaderDataLayer.setRowsResizableByDefault(false); + columnHeaderDataLayer.setColumnsResizableByDefault(true); + ColumnHeaderLayer columnHeaderLayer = new ColumnHeaderLayer(columnHeaderDataLayer, compositeFreezeLayer, selectionLayer); + ColumnGroupHeaderLayer columnGroupHeaderLayer = new ColumnGroupHeaderLayer(columnHeaderLayer, selectionLayer, columnGroupModel); + columnGroupHeaderLayer.setCalculateHeight(true); + SortHeaderLayer columnSortHeaderLayer = new SortHeaderLayer<>(columnGroupHeaderLayer, sortModel = new TechTypeTableSortModel(bodyDataProvider)); + + // build the row header layer + IDataProvider rowHeaderDataProvider = new TechTypeRowHeaderTableDataProvider(bodyDataProvider); + DataLayer rowHeaderDataLayer = new DefaultRowHeaderDataLayer(rowHeaderDataProvider); + rowHeaderDataLayer.setRowsResizableByDefault(false); + rowHeaderDataLayer.setColumnsResizableByDefault(false); + RowHeaderLayer rowHeaderLayer = new RowHeaderLayer(rowHeaderDataLayer, compositeFreezeLayer, selectionLayer); + + // build the corner layer + IDataProvider cornerDataProvider = new DefaultCornerDataProvider(columnHeaderDataProvider, rowHeaderDataProvider); + DataLayer cornerDataLayer = new DataLayer(cornerDataProvider); + ILayer cornerLayer = new CornerLayer(cornerDataLayer, rowHeaderLayer, columnSortHeaderLayer); + + // build the grid layer + GridLayer gridLayer = new GridLayer(compositeFreezeLayer, columnSortHeaderLayer, rowHeaderLayer, cornerLayer); + + table = new NatTable(parent, NatTable.DEFAULT_STYLE_OPTIONS | SWT.BORDER, gridLayer, false); + GridDataFactory.fillDefaults().grab(true, true).applyTo(table); + + // Register a CopyDataCommandHandler that also copies the headers and + // uses the configured IDisplayConverters + CopyDataCommandHandler copyHandler = new CopyDataCommandHandler( + selectionLayer, + columnHeaderDataLayer, + rowHeaderDataLayer); + copyHandler.setCopyFormattedText(true); + gridLayer.registerCommandHandler(copyHandler); + + // initialize paste handler with SWT clipboard + cpb = new Clipboard(getDisplay()); + //PasteDataCommandHandler pasteHandler = new PasteDataCommandHandler(bodyDataProvider, bodyDataLayer, selectionLayer, cpb); + //bodyDataLayer.registerCommandHandler(pasteHandler); + + table.addConfiguration(new DefaultNatTableStyleConfiguration()); + //table.addConfiguration(new EditingSupportConfiguration(bodyDataProvider)); + table.configure(); + } + + @Override + public void dispose() { + cpb.dispose(); + super.dispose(); + } + + public void setTechTypePath(String path) { + bodyDataProvider.setPath(path); + table.refresh(true); + } + +} diff --git a/org.simantics.district.network.ui/src/org/simantics/district/network/ui/techtype/table/TechTypeTableDataProvider.java b/org.simantics.district.network.ui/src/org/simantics/district/network/ui/techtype/table/TechTypeTableDataProvider.java new file mode 100644 index 00000000..534be1da --- /dev/null +++ b/org.simantics.district.network.ui/src/org/simantics/district/network/ui/techtype/table/TechTypeTableDataProvider.java @@ -0,0 +1,90 @@ +package org.simantics.district.network.ui.techtype.table; + +import java.io.IOException; +import java.nio.file.Path; +import java.nio.file.Paths; +import java.util.ArrayList; +import java.util.List; +import java.util.stream.Collectors; + +import org.apache.commons.csv.CSVRecord; +import org.eclipse.nebula.widgets.nattable.data.IDataProvider; +import org.simantics.district.imports.DistrictImportUtils; + +public class TechTypeTableDataProvider implements IDataProvider { + + private List records = new ArrayList<>(); + private List filteredRecords = new ArrayList<>(); + private String filter = ""; + + public TechTypeTableDataProvider() { + // load csv + + setPath("C:\\projektit\\apros\\Semantum_VTT_Fortum portaali 2018-17-12\\järvenpää\\qgis\\TechTypeData.csv"); + } + + public Object getHeaderValue(int columnIndex) { + if (records.isEmpty()) { + return ""; + } + return records.get(0).get(columnIndex); + } + + @Override + public Object getDataValue(int columnIndex, int rowIndex) { + return filteredRecords.get(rowIndex + 1).get(columnIndex); + } + + @Override + public void setDataValue(int columnIndex, int rowIndex, Object newValue) { + + } + + @Override + public int getColumnCount() { + if (records.isEmpty()) { + return 0; + } + return records.get(0).size(); + } + + @Override + public int getRowCount() { + return filteredRecords.size() - 1; + } + + public boolean isEditable(int columnIndex, int rowIndex) { + return false; + } + + public void setFilter(String text) { + this.filter = text.toLowerCase(); + + filteredRecords = records.stream().filter(record -> { + for (int i = 0; i < record.size(); i++) { + String columnContent = record.get(i); + if (columnContent.toLowerCase().contains(filter)) { + return true; + } + } + return false; + }).collect(Collectors.toList()); + } + + public void setPath(String path) { + records.clear(); + filteredRecords.clear(); + Path techTypeCsv = Paths.get(path); + try { + DistrictImportUtils.consumeCSV(techTypeCsv, ';', false, record -> { + records.add(record); + return true; + }); + } catch (IOException e) { + e.printStackTrace(); + } + + setFilter(""); + } + +} diff --git a/org.simantics.district.network.ui/src/org/simantics/district/network/ui/techtype/table/TechTypeTableSortModel.java b/org.simantics.district.network.ui/src/org/simantics/district/network/ui/techtype/table/TechTypeTableSortModel.java new file mode 100644 index 00000000..f6a02f4a --- /dev/null +++ b/org.simantics.district.network.ui/src/org/simantics/district/network/ui/techtype/table/TechTypeTableSortModel.java @@ -0,0 +1,127 @@ +package org.simantics.district.network.ui.techtype.table; + +import java.lang.reflect.Array; +import java.util.ArrayList; +import java.util.Arrays; +import java.util.Collections; +import java.util.Comparator; +import java.util.List; + +import org.eclipse.nebula.widgets.nattable.sort.ISortModel; +import org.eclipse.nebula.widgets.nattable.sort.SortDirectionEnum; +import org.simantics.utils.strings.AlphanumComparator; + +public class TechTypeTableSortModel implements ISortModel { + + private static final SortDirectionEnum[] NO_DIRECTIONS = {}; + private static final boolean[] NO_BOOLEANS = {}; + + /** + * Array that contains the sort direction for every column. + * Needed to access the current sort state of a column. + */ + protected SortDirectionEnum[] sortDirections = NO_DIRECTIONS; + + /** + * Array that contains the sorted flags for every column. + * Needed to access the current sort state of a column. + */ + protected boolean[] sorted = NO_BOOLEANS; + + /** + * As this implementation only supports single column sorting, + * this property contains the the column index of the column that + * is currently used for sorting. + * Initial value = -1 for no sort column + */ + protected int currentSortColumn = -1; + + /** + * As this implementation only supports single column sorting, + * this property contains the current sort direction of the column that + * is currently used for sorting. + */ + protected SortDirectionEnum currentSortDirection = SortDirectionEnum.ASC; + + private TechTypeTableDataProvider bodyDataProvider; + + public TechTypeTableSortModel(TechTypeTableDataProvider bodyDataProvider) { + this.bodyDataProvider = bodyDataProvider; + } + + + @Override + public List getSortedColumnIndexes() { + List indexes = new ArrayList(); + if (currentSortColumn > -1) { + indexes.add(Integer.valueOf(currentSortColumn)); + } + return indexes; + } + + @Override + public boolean isColumnIndexSorted(int columnIndex) { + if (sorted.length <= columnIndex) + return false; + return sorted[columnIndex]; + } + + @Override + public SortDirectionEnum getSortDirection(int columnIndex) { + if (sortDirections.length <= columnIndex) + return SortDirectionEnum.NONE; + return sortDirections[columnIndex]; + } + + @Override + public int getSortOrder(int columnIndex) { + return 0; + } + + @Override + public List getComparatorsForColumnIndex(int columnIndex) { + return Collections.singletonList(AlphanumComparator.COMPARATOR); + } + + @Override + public Comparator getColumnComparator(int columnIndex) { + return AlphanumComparator.COMPARATOR; + } + + @Override + public void sort(int columnIndex, SortDirectionEnum sortDirection, boolean accumulate) { + + if (!isColumnIndexSorted(columnIndex)) { + clear(); + } + int columnCount = bodyDataProvider.getColumnCount(); + sortDirections = ensureArraySize(sortDirections, columnCount, SortDirectionEnum.class, SortDirectionEnum.NONE); + + + sortDirections[columnIndex] = sortDirection; + sorted[columnIndex] = !sortDirection.equals(SortDirectionEnum.NONE); + currentSortColumn = columnIndex; + currentSortDirection = sortDirection; + } + + @Override + public void clear() { + Arrays.fill(this.sortDirections, SortDirectionEnum.NONE); + Arrays.fill(this.sorted, false); + this.currentSortColumn = -1; + this.currentSortDirection = SortDirectionEnum.NONE; + } + + @SuppressWarnings("unchecked") + public static T[] ensureArraySize(T[] array, int l, Class clazz, T defaultValue) { + boolean fill = true; + if (array == null || array.length != l) { + array = (T[]) Array.newInstance(clazz, l); + fill = defaultValue != null; + } + if (fill) + Arrays.fill(array, defaultValue); + return array; + } + +} diff --git a/org.simantics.district.network.ui/src/org/simantics/district/network/ui/techtype/table/TechTypeTableUI.java b/org.simantics.district.network.ui/src/org/simantics/district/network/ui/techtype/table/TechTypeTableUI.java new file mode 100644 index 00000000..7cfa44d1 --- /dev/null +++ b/org.simantics.district.network.ui/src/org/simantics/district/network/ui/techtype/table/TechTypeTableUI.java @@ -0,0 +1,11 @@ +package org.simantics.district.network.ui.techtype.table; + +import org.eclipse.swt.widgets.Composite; + +public class TechTypeTableUI extends Composite { + + public TechTypeTableUI(Composite parent, int style) { + super(parent, style); + } + +} diff --git a/org.simantics.district.network.ui/src/org/simantics/district/network/ui/techtype/table/TechTypeTableView.java b/org.simantics.district.network.ui/src/org/simantics/district/network/ui/techtype/table/TechTypeTableView.java new file mode 100644 index 00000000..cdb502c3 --- /dev/null +++ b/org.simantics.district.network.ui/src/org/simantics/district/network/ui/techtype/table/TechTypeTableView.java @@ -0,0 +1,53 @@ +package org.simantics.district.network.ui.techtype.table; + + +import javax.annotation.PostConstruct; +import javax.annotation.PreDestroy; +import javax.inject.Inject; + +import org.eclipse.e4.ui.model.application.MApplication; +import org.eclipse.e4.ui.model.application.commands.MCommand; +import org.eclipse.e4.ui.model.application.ui.basic.MPart; +import org.eclipse.e4.ui.model.application.ui.menu.MHandledToolItem; +import org.eclipse.e4.ui.model.application.ui.menu.MMenuFactory; +import org.eclipse.e4.ui.model.application.ui.menu.MToolBar; +import org.eclipse.e4.ui.workbench.modeling.ESelectionService; +import org.eclipse.swt.widgets.Composite; + +public class TechTypeTableView { + + @Inject ESelectionService selectionService; + + public static TechTypeTable table; + + @Inject + public void init(MPart part, MApplication app) { + MToolBar toolBar = MMenuFactory.INSTANCE.createToolBar(); + toolBar.setToBeRendered(true); + toolBar.getChildren().add(createImportCSVDataToolItem(app)); + part.setToolbar(toolBar); + } + + private MHandledToolItem createImportCSVDataToolItem(MApplication app) { + MHandledToolItem createHandledToolItem = MMenuFactory.INSTANCE.createHandledToolItem(); + // Command is contributed via fragment + MCommand command = app.getCommand("org.simantics.district.network.ui.command.importtechtypecsv"); + createHandledToolItem.setCommand(command); //$NON-NLS-1$ + createHandledToolItem.setLabel("Import Tech Type"); + createHandledToolItem.setIconURI("platform:/plugin/com.famfamfam.silk/icons/table_edit.png"); //$NON-NLS-1$ + return createHandledToolItem; + } + + @PostConstruct + public void postConstruct(Composite parent) { + table = new TechTypeTable(parent, 0); + + } + + @PreDestroy + public void dispose() { + table.dispose(); + table = null; + } + +}