]> gerrit.simantics Code Review - simantics/district.git/commitdiff
Add CSV table view for copy/pasting consumer information before creation
authorjsimomaa <jani.simomaa@gmail.com>
Fri, 7 Jun 2019 07:34:01 +0000 (10:34 +0300)
committerTuukka Lehtonen <tuukka.lehtonen@semantum.fi>
Sat, 31 Aug 2019 20:08:43 +0000 (23:08 +0300)
gitlab #51
APROS-15209

Change-Id: I5307000c29fd93292cb6c41ad527a83abc8f9f6f

org.simantics.district.network.ui/src/org/simantics/district/network/ui/table/ColumnHeaderTableDataProvider.java [new file with mode: 0644]
org.simantics.district.network.ui/src/org/simantics/district/network/ui/table/CustomPasteDataAction.java [new file with mode: 0644]
org.simantics.district.network.ui/src/org/simantics/district/network/ui/table/CustomPasteDataCommand.java [new file with mode: 0644]
org.simantics.district.network.ui/src/org/simantics/district/network/ui/table/DistrictCSVTable.java [new file with mode: 0644]
org.simantics.district.network.ui/src/org/simantics/district/network/ui/table/DistrictCSVTableUI.java [new file with mode: 0644]
org.simantics.district.network.ui/src/org/simantics/district/network/ui/table/DistrictCSVTableView.java [new file with mode: 0644]
org.simantics.district.network.ui/src/org/simantics/district/network/ui/table/EditingSupportConfiguration.java [new file with mode: 0644]
org.simantics.district.network.ui/src/org/simantics/district/network/ui/table/ImportCSVHandler.java [new file with mode: 0644]
org.simantics.district.network.ui/src/org/simantics/district/network/ui/table/PasteDataCommandHandler.java [new file with mode: 0644]
org.simantics.district.network.ui/src/org/simantics/district/network/ui/table/RowHeaderTableDataProvider.java [new file with mode: 0644]
org.simantics.district.network.ui/src/org/simantics/district/network/ui/table/TableDataProvider.java [new file with mode: 0644]

diff --git a/org.simantics.district.network.ui/src/org/simantics/district/network/ui/table/ColumnHeaderTableDataProvider.java b/org.simantics.district.network.ui/src/org/simantics/district/network/ui/table/ColumnHeaderTableDataProvider.java
new file mode 100644 (file)
index 0000000..530870b
--- /dev/null
@@ -0,0 +1,31 @@
+package org.simantics.district.network.ui.table;
+
+import org.eclipse.nebula.widgets.nattable.data.IDataProvider;
+
+public class ColumnHeaderTableDataProvider implements IDataProvider {
+
+       @Override
+       public Object getDataValue(int columnIndex, int rowIndex) {
+               // TODO Auto-generated method stub
+               return null;
+       }
+
+       @Override
+       public void setDataValue(int columnIndex, int rowIndex, Object newValue) {
+               // TODO Auto-generated method stub
+               
+       }
+
+       @Override
+       public int getColumnCount() {
+               // TODO Auto-generated method stub
+               return 0;
+       }
+
+       @Override
+       public int getRowCount() {
+               // TODO Auto-generated method stub
+               return 0;
+       }
+
+}
diff --git a/org.simantics.district.network.ui/src/org/simantics/district/network/ui/table/CustomPasteDataAction.java b/org.simantics.district.network.ui/src/org/simantics/district/network/ui/table/CustomPasteDataAction.java
new file mode 100644 (file)
index 0000000..dc8c684
--- /dev/null
@@ -0,0 +1,80 @@
+package org.simantics.district.network.ui.table;
+
+import java.util.Set;
+
+import org.eclipse.nebula.widgets.nattable.NatTable;
+import org.eclipse.nebula.widgets.nattable.coordinate.Range;
+import org.eclipse.nebula.widgets.nattable.ui.action.IKeyAction;
+import org.eclipse.swt.dnd.TextTransfer;
+import org.eclipse.swt.events.KeyEvent;
+
+public class CustomPasteDataAction implements IKeyAction {
+
+    private DistrictCSVTable table;
+
+    public CustomPasteDataAction(DistrictCSVTable table) {
+        this.table = table;
+    }
+
+    @Override
+    public void run(NatTable natTable, KeyEvent event) {
+        Object data = table.cpb.getContents(TextTransfer.getInstance());
+        if (data instanceof String) {
+            String textData = (String) data;
+            String[][] fullData = computeData(textData);
+            
+            int[] cols = table.selectionLayer.getSelectedColumnPositions();
+            int firstCol = cols[0];
+            int column = table.selectionLayer.getColumnIndexByPosition(firstCol);
+            Set<Range> ranges = table.selectionLayer.getSelectedRowPositions();
+            if (!ranges.isEmpty()) {
+                int rowPosition = ranges.iterator().next().start;
+                int[] columns = new int[fullData.length];
+                columns[0] = column;
+                for (int i = 1; i < fullData.length; i++)
+                    columns[i] = table.selectionLayer.getColumnIndexByPosition(firstCol + i);
+                table.bodyDataLayer.doCommand(new CustomPasteDataCommand(table.bodyDataLayer, columns, rowPosition, fullData));
+            }
+        }
+    }
+    
+    private static String[][] computeData(String textData) {
+        String separator;
+        if (textData.contains(",") && !textData.contains(";")) {
+            separator = ",";
+        } else if (textData.contains(";") && !textData.contains("\t")) {
+            separator = ";";
+        } else {
+            separator = "\\t";
+        }
+        
+        textData = textData.replaceAll("\\r", "");
+         
+        String[] rows = textData.split("\\n");
+        
+        String[][] cells = new String[rows.length][];
+        for(int i=0;i<rows.length;++i)
+            cells[i] = rows[i].split(separator, -1);
+        
+        String[][] fullData = cells; //transpose(cells);
+        
+        return fullData;
+    }
+    
+    private static String[][] transpose(String[][] cells) {
+        int rowCount = 0;
+        for(String[] cols : cells)
+            rowCount = Math.max(rowCount, cols.length);
+        String[][] result = new String[rowCount][cells.length];
+        for(int i=0;i<cells.length;++i) {
+            String[] cols = cells[i];
+            int j;
+            for(j=0;j<cols.length;++j)
+                result[j][i] = cols[j];
+            for(;j<rowCount;++j)
+                result[j][i] = "";
+        }
+        return result;
+
+    }
+}
\ No newline at end of file
diff --git a/org.simantics.district.network.ui/src/org/simantics/district/network/ui/table/CustomPasteDataCommand.java b/org.simantics.district.network.ui/src/org/simantics/district/network/ui/table/CustomPasteDataCommand.java
new file mode 100644 (file)
index 0000000..2ddc0a4
--- /dev/null
@@ -0,0 +1,28 @@
+package org.simantics.district.network.ui.table;
+
+import org.eclipse.nebula.widgets.nattable.command.AbstractMultiColumnCommand;
+import org.eclipse.nebula.widgets.nattable.command.ILayerCommand;
+import org.eclipse.nebula.widgets.nattable.layer.ILayer;
+
+public class CustomPasteDataCommand extends AbstractMultiColumnCommand {
+
+    int pasteRow;
+    String[][] data;
+
+    protected CustomPasteDataCommand(ILayer layer, int[] columnPositions, int pasteRow, String[][] data) {
+        super(layer, columnPositions);
+        this.pasteRow = pasteRow;
+        this.data = data;
+    }
+
+    protected CustomPasteDataCommand(CustomPasteDataCommand pasteDataCommand) {
+        super(pasteDataCommand);
+        this.pasteRow = pasteDataCommand.pasteRow;
+        this.data = pasteDataCommand.data;
+    }
+    @Override
+    public ILayerCommand cloneCommand() {
+        return new CustomPasteDataCommand(this);
+    }
+    
+}
diff --git a/org.simantics.district.network.ui/src/org/simantics/district/network/ui/table/DistrictCSVTable.java b/org.simantics.district.network.ui/src/org/simantics/district/network/ui/table/DistrictCSVTable.java
new file mode 100644 (file)
index 0000000..2f811c7
--- /dev/null
@@ -0,0 +1,160 @@
+package org.simantics.district.network.ui.table;
+
+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.AbstractRegistryConfiguration;
+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.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.SelectionLayer;
+import org.eclipse.nebula.widgets.nattable.ui.binding.UiBindingRegistry;
+import org.eclipse.nebula.widgets.nattable.ui.matcher.KeyEventMatcher;
+import org.eclipse.nebula.widgets.nattable.viewport.ViewportLayer;
+import org.eclipse.swt.SWT;
+import org.eclipse.swt.dnd.Clipboard;
+import org.eclipse.swt.widgets.Composite;
+
+public class DistrictCSVTable extends Composite {
+
+       NatTable table;
+       private TableDataProvider 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 ColumnHeaderTableDataProvider columnHeaderDataProvider;
+       Clipboard cpb;
+       public SelectionLayer selectionLayer;
+
+       public DistrictCSVTable(Composite parent, int style) {
+               super(parent, style);
+               defaultInitializeUI();
+       }
+
+       private void defaultInitializeUI() {
+               GridDataFactory.fillDefaults().grab(true, true).applyTo(this);
+               GridLayoutFactory.fillDefaults().numColumns(1).applyTo(this);
+               createTable(this);
+       }
+
+       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 TableDataProvider();
+               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 ColumnHeaderTableDataProvider();
+               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);
+
+               // build the row header layer
+               IDataProvider rowHeaderDataProvider = new RowHeaderTableDataProvider(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, columnGroupHeaderLayer);
+
+               // build the grid layer
+               GridLayer gridLayer = new GridLayer(compositeFreezeLayer, columnGroupHeaderLayer, 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.addConfiguration(new AbstractRegistryConfiguration() {
+                       
+                       @Override
+                       public void configureRegistry(IConfigRegistry configRegistry) {
+                       }
+
+                       @Override
+                       public void configureUiBindings(UiBindingRegistry uiBindingRegistry) {
+                               super.configureUiBindings(uiBindingRegistry);
+                               // ui binding to perform a paste action on pressing CTRL+V
+                               uiBindingRegistry.registerFirstKeyBinding(new KeyEventMatcher(SWT.MOD1, 'v'), new CustomPasteDataAction(DistrictCSVTable.this));
+                       }
+               });
+               
+               table.configure();
+       }
+       
+       @Override
+       public void dispose() {
+               cpb.dispose();
+               super.dispose();
+       }
+
+       public String[][] getCurrentData() {
+               return bodyDataProvider.getCurrentData();
+       }
+
+}
diff --git a/org.simantics.district.network.ui/src/org/simantics/district/network/ui/table/DistrictCSVTableUI.java b/org.simantics.district.network.ui/src/org/simantics/district/network/ui/table/DistrictCSVTableUI.java
new file mode 100644 (file)
index 0000000..e2d3d6e
--- /dev/null
@@ -0,0 +1,11 @@
+package org.simantics.district.network.ui.table;
+
+import org.eclipse.swt.widgets.Composite;
+
+public class DistrictCSVTableUI extends Composite {
+
+       public DistrictCSVTableUI(Composite parent, int style) {
+               super(parent, style);
+       }
+
+}
diff --git a/org.simantics.district.network.ui/src/org/simantics/district/network/ui/table/DistrictCSVTableView.java b/org.simantics.district.network.ui/src/org/simantics/district/network/ui/table/DistrictCSVTableView.java
new file mode 100644 (file)
index 0000000..b7d4f5b
--- /dev/null
@@ -0,0 +1,53 @@
+package org.simantics.district.network.ui.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.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 DistrictCSVTableView {
+
+       @Inject ESelectionService selectionService;
+
+       private DistrictCSVTable 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
+               createHandledToolItem.setCommand(app.getCommand("org.simantics.district.network.ui.command.importcsv")); //$NON-NLS-1$
+               createHandledToolItem.setLabel("Import CSV");
+               createHandledToolItem.setIconURI("platform:/plugin/com.famfamfam.silk/icons/table_edit.png"); //$NON-NLS-1$
+               return createHandledToolItem;
+       }
+
+       @PostConstruct
+       public void postConstruct(Composite parent) {
+               table = new DistrictCSVTable(parent, 0);
+               
+       }
+       
+       @PreDestroy
+       public void dispose() {
+               table.dispose();
+               table = null;
+       }
+
+       public String[][] getCurrentData() {
+               return table.getCurrentData();
+       }
+}
diff --git a/org.simantics.district.network.ui/src/org/simantics/district/network/ui/table/EditingSupportConfiguration.java b/org.simantics.district.network.ui/src/org/simantics/district/network/ui/table/EditingSupportConfiguration.java
new file mode 100644 (file)
index 0000000..a798bc6
--- /dev/null
@@ -0,0 +1,32 @@
+package org.simantics.district.network.ui.table;
+
+import org.eclipse.nebula.widgets.nattable.config.AbstractRegistryConfiguration;
+import org.eclipse.nebula.widgets.nattable.config.IConfigRegistry;
+import org.eclipse.nebula.widgets.nattable.config.IEditableRule;
+import org.eclipse.nebula.widgets.nattable.edit.EditConfigAttributes;
+import org.eclipse.nebula.widgets.nattable.layer.cell.ILayerCell;
+
+public class EditingSupportConfiguration extends AbstractRegistryConfiguration {
+
+    private TableDataProvider bodyDataProvider;
+
+    public EditingSupportConfiguration(TableDataProvider bodyDataProvider) {
+        this.bodyDataProvider = bodyDataProvider;
+    }
+
+    @Override
+    public void configureRegistry(IConfigRegistry configRegistry) {
+        configRegistry.registerConfigAttribute(EditConfigAttributes.CELL_EDITABLE_RULE, new IEditableRule() {
+            @Override
+            public boolean isEditable(ILayerCell cell, IConfigRegistry configRegistry) {
+                return bodyDataProvider.isEditable(cell.getColumnIndex(), cell.getRowIndex());
+            }
+
+            @Override
+            public boolean isEditable(int columnIndex, int rowIndex) {
+                return bodyDataProvider.isEditable(columnIndex, rowIndex);
+            }
+        });
+    }
+
+}
diff --git a/org.simantics.district.network.ui/src/org/simantics/district/network/ui/table/ImportCSVHandler.java b/org.simantics.district.network.ui/src/org/simantics/district/network/ui/table/ImportCSVHandler.java
new file mode 100644 (file)
index 0000000..a94d56a
--- /dev/null
@@ -0,0 +1,286 @@
+package org.simantics.district.network.ui.table;
+
+import java.util.Collection;
+import java.util.HashMap;
+import java.util.Map;
+import java.util.Set;
+
+import javax.inject.Inject;
+import javax.inject.Named;
+
+import org.eclipse.e4.core.di.annotations.Execute;
+import org.eclipse.e4.ui.model.application.ui.basic.MPart;
+import org.eclipse.e4.ui.services.IServiceConstants;
+import org.eclipse.e4.ui.workbench.modeling.EPartService;
+import org.eclipse.jface.dialogs.Dialog;
+import org.eclipse.jface.layout.GridDataFactory;
+import org.eclipse.swt.SWT;
+import org.eclipse.swt.events.ModifyEvent;
+import org.eclipse.swt.events.ModifyListener;
+import org.eclipse.swt.layout.GridData;
+import org.eclipse.swt.layout.GridLayout;
+import org.eclipse.swt.widgets.Combo;
+import org.eclipse.swt.widgets.Composite;
+import org.eclipse.swt.widgets.Control;
+import org.eclipse.swt.widgets.Group;
+import org.eclipse.swt.widgets.Label;
+import org.eclipse.swt.widgets.Shell;
+import org.eclipse.ui.dialogs.SelectionStatusDialog;
+import org.geotools.geometry.DirectPosition2D;
+import org.geotools.referencing.CRS;
+import org.opengis.geometry.DirectPosition;
+import org.opengis.referencing.crs.CoordinateReferenceSystem;
+import org.opengis.referencing.operation.MathTransform;
+import org.simantics.Simantics;
+import org.simantics.db.ReadGraph;
+import org.simantics.db.Resource;
+import org.simantics.db.WriteGraph;
+import org.simantics.db.common.request.IndexRoot;
+import org.simantics.db.common.request.ReadRequest;
+import org.simantics.db.common.request.WriteRequest;
+import org.simantics.db.exception.DatabaseException;
+import org.simantics.db.layer0.request.PossibleActiveModel;
+import org.simantics.district.network.DistrictNetworkUtil;
+import org.simantics.district.network.ui.function.Functions;
+import org.simantics.modeling.ModelingResources;
+import org.slf4j.Logger;
+import org.slf4j.LoggerFactory;
+
+public class ImportCSVHandler {
+
+    private static final Logger LOGGER = LoggerFactory.getLogger(ImportCSVHandler.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
+               MPart activePart = partService.getActivePart();
+               Object object = activePart.getObject();
+               // this object is the part wrapped by e4part
+               DistrictCSVTableView view = (DistrictCSVTableView) object;
+               String[][] data = view.getCurrentData();
+
+               SelectionDialog d = new SelectionDialog(s);
+               
+               if (d.open() == Dialog.CANCEL)
+                   return;
+               
+               String sourceEPSGCRS =  d.getSourceCRS();//"EPSG:3067";
+               
+               Resource targetDiagram = d.getTargetDiagram();
+               Resource mappingType = d.getMappingType();
+
+               try {
+                       MathTransform transform = null;
+                       boolean doTransform = false;
+                       // if sourceEPSGCRS is empty || null then ignore transformation
+                       if (sourceEPSGCRS != null && !sourceEPSGCRS.isEmpty()) {
+                               CoordinateReferenceSystem sourceCRS = CRS.decode(sourceEPSGCRS);
+                               CoordinateReferenceSystem targetCRS = CRS.decode("EPSG:4326");
+                               transform = CRS.findMathTransform(sourceCRS, targetCRS, true);
+                               doTransform = true;
+                       }
+                       
+                       final MathTransform finalTransform = transform;
+                       
+                       int x = xColumn(data);
+                       int y = yColumn(data);
+                       int z = zColumn(data);
+                       
+                       Simantics.getSession().asyncRequest(new WriteRequest() {
+                
+                @Override
+                public void perform(WriteGraph graph) throws DatabaseException {
+                    for (int i = 1; i < data.length; i++) {
+                        try {
+                            String[] rows = data[i];
+                            String xCoords = rows[x];
+                            String yCoords = rows[y];
+                            double xCoord = Double.parseDouble(xCoords);
+                            double yCoord = Double.parseDouble(yCoords);
+                            DirectPosition2D targetPos = new DirectPosition2D();
+                            DirectPosition2D sourcePos = new DirectPosition2D(xCoord, yCoord);
+                            DirectPosition res = finalTransform.transform(sourcePos, targetPos);
+                            double[] coords = res.getCoordinate();
+                            flipAxes(coords);
+                            Resource vertex = DistrictNetworkUtil.createVertex(graph, targetDiagram, coords, z, mappingType);
+                        } catch (Exception e) {
+                            LOGGER.error("", e);
+                        }
+                    }
+                }
+            });
+               } catch (Exception e) {
+                       LOGGER.error("", e);
+               }
+               
+               
+       }
+
+    private static void flipAxes(double[] coords) {
+        double tmp = coords[0];
+        coords[0] = coords[1];
+        coords[1] = tmp;
+    }
+
+       
+       private int zColumn(String[][] data) {
+               return column("z", data);
+       }
+       
+       private int yColumn(String[][] data) {
+               return column("y", data);
+       }
+
+       private int xColumn(String[][] data) {
+               return column("x", data);
+       }
+
+       private int column(String expected, String[][] data) {
+               String[] columns = data[0]; // first row should be headers
+               for (int i = 0; i < columns.length; i++) {
+                       String col = columns[i];
+                       if (col.equals(expected)) {
+                               return i;
+                       }
+               }
+               return -1;
+       }
+       
+       private static class SelectionDialog extends SelectionStatusDialog {
+
+        private Map<String, Resource> diagrams = new HashMap<>();
+        private Map<String, Resource> vertexMappings = new HashMap<>();
+
+        private Composite composite;
+        private Combo networkDiagramSelectionCombo;
+        private Combo sourceCRSCombo;
+        private Combo vertexMappingCombo;
+        private String sourceCRS;
+        private Resource diagram;
+        private Resource mapping;
+
+        public SelectionDialog(Shell parent) {
+            super(parent);
+        }
+
+        public Resource getMappingType() {
+            return mapping;
+        }
+
+        public Resource getTargetDiagram() {
+            return diagram;
+        }
+
+        public String getSourceCRS() {
+            return "EPSG:" + sourceCRS;
+        }
+
+        @Override
+        protected Control createDialogArea(Composite parent) {
+            composite = (Composite) super.createDialogArea(parent);
+            createMappingsGroup(composite);
+            computeContent();
+            return composite;
+        }
+        
+        private void computeContent() {
+            Simantics.getSession().asyncRequest(new ReadRequest() {
+                
+                @Override
+                public void run(ReadGraph graph) throws DatabaseException {
+                    
+                    Resource indexRoot = graph.sync(new IndexRoot(graph.sync(new PossibleActiveModel(Simantics.getProjectResource()))));
+                    vertexMappings = Functions.getVertexMappings(graph, indexRoot);
+                    
+                    Collection<Resource> diagramss = Functions.getDistrictDiagrams(graph);
+                    ModelingResources MOD = ModelingResources.getInstance(graph);
+                    Resource projectResource = Simantics.getProjectResource();
+                    String projectURI = graph.getURI(projectResource);
+                    for (Resource diagram : diagramss) {
+                        Resource composite = graph.getSingleObject(diagram, MOD.DiagramToComposite);
+                        String compositeURI = graph.getURI(composite);
+                        String path = compositeURI.replace(projectURI, "");
+                        diagrams.put(path, diagram);
+                    }
+                    
+                    composite.getDisplay().asyncExec(() -> {
+                        
+                        vertexMappingCombo.setItems(vertexMappings.keySet().toArray(new String[vertexMappings.size()]));
+                        
+                        networkDiagramSelectionCombo.setItems(diagrams.keySet().toArray(new String[diagrams.size()]));
+                        if (diagrams.size() > 0) {
+                            networkDiagramSelectionCombo.select(0);
+                        }
+                        Set<String> codes = CRS.getSupportedCodes("EPSG");
+                        sourceCRSCombo.setItems(codes.toArray(new String[codes.size()]));
+                        sourceCRSCombo.addModifyListener(new ModifyListener() {
+                            
+                            @Override
+                            public void modifyText(ModifyEvent e) {
+                                String currentText = sourceCRSCombo.getText();
+                                if (codes.contains(currentText)) {
+                                    // Select this
+                                    String[] items = sourceCRSCombo.getItems();
+                                    int i;
+                                    for (i = 0; i < items.length; i++) {
+                                        String item = items[i];
+                                        if (currentText.equals(item)) {
+                                            break;
+                                        }
+                                    }
+                                    if (i != 0) {
+                                        sourceCRSCombo.select(i);
+                                    } else {
+                                        System.err.println("Should not happen");
+                                    }
+                                }
+                            }
+                        });
+                    });
+                }
+            });
+        }
+
+        private void createMappingsGroup(Composite parent) {
+            Group group= new Group(parent, SWT.NONE);
+            group.setFont(parent.getFont());
+            group.setText("Select Diagram & CRS");
+            GridDataFactory.fillDefaults().grab(true, false).applyTo(group);
+            group.setLayout(new GridLayout(1, false));
+            
+            Composite cmposite = new Composite(group, SWT.NONE);
+            cmposite.setLayoutData(new GridData(SWT.FILL, SWT.TOP, true, false));
+            cmposite.setLayout(new GridLayout(2, false));
+            
+            Label vertexMappingLabel = new Label(cmposite, SWT.NONE);
+            vertexMappingLabel.setText("Select Vertex Mapping");
+
+            vertexMappingCombo = new Combo(cmposite, SWT.READ_ONLY | SWT.BORDER);
+            GridDataFactory.fillDefaults().grab(true, false).applyTo(vertexMappingCombo);
+            
+            Label selectNetworkDiagramLabel = new Label(cmposite, SWT.NONE);
+            selectNetworkDiagramLabel.setText("Select Network Diagram");
+
+            networkDiagramSelectionCombo = new Combo(cmposite, SWT.READ_ONLY | SWT.BORDER);
+            GridDataFactory.fillDefaults().grab(true, false).applyTo(networkDiagramSelectionCombo);
+            
+            Label label = new Label(cmposite, SWT.NONE);
+            label.setText("Select Source Coordinate Reference System");
+            
+            sourceCRSCombo = new Combo(cmposite, SWT.NONE);
+            sourceCRSCombo.setToolTipText("Select the coordinate reference system that is used in the source material for possible transformation to target coordinate reference system (EPSG:4326)");
+            
+            GridDataFactory.fillDefaults().grab(true, false).applyTo(sourceCRSCombo);
+        }
+        
+        @Override
+        protected void computeResult() {
+            mapping = vertexMappings.get(vertexMappingCombo.getItem(vertexMappingCombo.getSelectionIndex()));
+            diagram = diagrams.get(networkDiagramSelectionCombo.getItem(networkDiagramSelectionCombo.getSelectionIndex()));
+            sourceCRS = sourceCRSCombo.getItem(sourceCRSCombo.getSelectionIndex());
+        }
+       }
+}
diff --git a/org.simantics.district.network.ui/src/org/simantics/district/network/ui/table/PasteDataCommandHandler.java b/org.simantics.district.network.ui/src/org/simantics/district/network/ui/table/PasteDataCommandHandler.java
new file mode 100644 (file)
index 0000000..06e9fbd
--- /dev/null
@@ -0,0 +1,47 @@
+package org.simantics.district.network.ui.table;
+
+import java.util.Collection;
+
+import org.eclipse.nebula.widgets.nattable.command.AbstractLayerCommandHandler;
+import org.eclipse.nebula.widgets.nattable.layer.DataLayer;
+import org.eclipse.nebula.widgets.nattable.layer.event.ColumnVisualUpdateEvent;
+import org.eclipse.nebula.widgets.nattable.layer.event.StructuralRefreshEvent;
+import org.eclipse.nebula.widgets.nattable.selection.SelectionLayer;
+import org.eclipse.swt.dnd.Clipboard;
+import org.slf4j.Logger;
+import org.slf4j.LoggerFactory;
+
+public class PasteDataCommandHandler extends AbstractLayerCommandHandler<CustomPasteDataCommand> {
+
+       private static final Logger LOGGER = LoggerFactory.getLogger(PasteDataCommandHandler.class);
+
+       private final SelectionLayer selectionLayer;
+       private final DataLayer dataLayer;
+       private final Clipboard cpb;
+       private final TableDataProvider provider;
+
+       public PasteDataCommandHandler(TableDataProvider provider, DataLayer dataLayer, SelectionLayer selectionLayer, Clipboard cpb) {
+               this.provider = provider;
+               this.dataLayer = dataLayer;
+               this.selectionLayer = selectionLayer;
+               this.cpb = cpb;
+       }
+
+    @Override
+    public Class<CustomPasteDataCommand> getCommandClass() {
+        return CustomPasteDataCommand.class;
+    }
+
+    @Override
+    protected boolean doCommand(CustomPasteDataCommand command) {
+        String[][] fullData = command.data;
+        int pasteRow = command.pasteRow;
+        Collection<Integer> pasteColumn = command.getColumnPositions();
+        if (pasteRow > -1) {
+            provider.setDataValues(pasteColumn, pasteRow, fullData);
+            dataLayer.fireLayerEvent(new StructuralRefreshEvent(dataLayer));
+        }
+        return true;
+    }
+}
+
diff --git a/org.simantics.district.network.ui/src/org/simantics/district/network/ui/table/RowHeaderTableDataProvider.java b/org.simantics.district.network.ui/src/org/simantics/district/network/ui/table/RowHeaderTableDataProvider.java
new file mode 100644 (file)
index 0000000..b85f6e3
--- /dev/null
@@ -0,0 +1,33 @@
+package org.simantics.district.network.ui.table;
+
+import org.eclipse.nebula.widgets.nattable.data.IDataProvider;
+
+public class RowHeaderTableDataProvider implements IDataProvider {
+
+    protected final IDataProvider bodyDataProvider;
+
+    public RowHeaderTableDataProvider(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/table/TableDataProvider.java b/org.simantics.district.network.ui/src/org/simantics/district/network/ui/table/TableDataProvider.java
new file mode 100644 (file)
index 0000000..a8e2a01
--- /dev/null
@@ -0,0 +1,56 @@
+package org.simantics.district.network.ui.table;
+
+import java.util.Collection;
+
+import org.eclipse.nebula.widgets.nattable.data.IDataProvider;
+
+public class TableDataProvider implements IDataProvider {
+
+       private String[][] data = null;
+
+       @Override
+       public Object getDataValue(int columnIndex, int rowIndex) {
+               if (data == null) {
+                       return null;
+               } else {
+                       return data[rowIndex][columnIndex];
+               }
+       }
+
+       @Override
+       public void setDataValue(int columnIndex, int rowIndex, Object newValue) {
+               
+       }
+
+       @Override
+       public int getColumnCount() {
+               if (data == null) {
+                       return 10;
+               } else {
+                       return data[0].length;
+               }
+       }
+
+       @Override
+       public int getRowCount() {
+               if (data == null) {
+                       return 10;
+               } else {
+                       return data.length;
+               }
+       }
+
+       public boolean isEditable(int columnIndex, int rowIndex) {
+               return false;
+       }
+
+       public void setDataValues(Collection<Integer> pasteColumn, int pasteRow, String[][] fullData) {
+               // start always from row index 0 and column index 0
+               this.data = fullData;
+       }
+
+       public String[][] getCurrentData() {
+               return data;
+       }
+
+}