]> gerrit.simantics Code Review - simantics/district.git/commitdiff
UI for diagram element selection 38/2638/5
authorReino Ruusu <reino.ruusu@semantum.fi>
Fri, 1 Feb 2019 15:56:17 +0000 (17:56 +0200)
committerReino Ruusu <reino.ruusu@semantum.fi>
Wed, 13 Feb 2019 09:01:45 +0000 (11:01 +0200)
gitlab #28

Change-Id: Id89372d9101dc7004b9b71b8ed6769b555b7b510

22 files changed:
org.simantics.district.selection.ui/.classpath [new file with mode: 0644]
org.simantics.district.selection.ui/.project [new file with mode: 0644]
org.simantics.district.selection.ui/META-INF/MANIFEST.MF [new file with mode: 0644]
org.simantics.district.selection.ui/build.properties [new file with mode: 0644]
org.simantics.district.selection.ui/fragment.e4xmi [new file with mode: 0644]
org.simantics.district.selection.ui/plugin.xml [new file with mode: 0644]
org.simantics.district.selection.ui/pom.xml [new file with mode: 0644]
org.simantics.district.selection.ui/src/org/simantics/district/selection/ui/ElementSelectorTableUI.java [new file with mode: 0644]
org.simantics.district.selection.ui/src/org/simantics/district/selection/ui/handlers/CreateNewElementSelector.java [new file with mode: 0644]
org.simantics.district.selection.ui/src/org/simantics/district/selection/ui/handlers/DeleteElementSelector.java [new file with mode: 0644]
org.simantics.district.selection.ui/src/org/simantics/district/selection/ui/handlers/EditElementSelector.java [new file with mode: 0644]
org.simantics.district.selection.ui/src/org/simantics/district/selection/ui/handlers/OpenElementSelectorTable.java [new file with mode: 0644]
org.simantics.district.selection.ui/src/org/simantics/district/selection/ui/parts/EditSelectorDialog.java [new file with mode: 0644]
org.simantics.district.selection.ui/src/org/simantics/district/selection/ui/parts/ElementSelectionView.java [new file with mode: 0644]
org.simantics.district.selection/graph.tg
org.simantics.district.selection/graph/DiagramElementSelection.pgraph
org.simantics.district.selection/src/org/simantics/district/selection/ElementSelectionResource.java
org.simantics.district.selection/src/org/simantics/district/selection/ElementSelectionUtils.java [new file with mode: 0644]
org.simantics.district.selection/src/org/simantics/district/selection/ElementSelector.java
org.simantics.district.selection/src/org/simantics/district/selection/ElementSelectorTest.java [new file with mode: 0644]
org.simantics.district.ui.feature/feature.xml
pom.xml

diff --git a/org.simantics.district.selection.ui/.classpath b/org.simantics.district.selection.ui/.classpath
new file mode 100644 (file)
index 0000000..eca7bdb
--- /dev/null
@@ -0,0 +1,7 @@
+<?xml version="1.0" encoding="UTF-8"?>
+<classpath>
+       <classpathentry kind="con" path="org.eclipse.jdt.launching.JRE_CONTAINER/org.eclipse.jdt.internal.debug.ui.launcher.StandardVMType/JavaSE-1.8"/>
+       <classpathentry kind="con" path="org.eclipse.pde.core.requiredPlugins"/>
+       <classpathentry kind="src" path="src"/>
+       <classpathentry kind="output" path="bin"/>
+</classpath>
diff --git a/org.simantics.district.selection.ui/.project b/org.simantics.district.selection.ui/.project
new file mode 100644 (file)
index 0000000..491a0fb
--- /dev/null
@@ -0,0 +1,28 @@
+<?xml version="1.0" encoding="UTF-8"?>
+<projectDescription>
+       <name>org.simantics.district.selection.ui</name>
+       <comment></comment>
+       <projects>
+       </projects>
+       <buildSpec>
+               <buildCommand>
+                       <name>org.eclipse.jdt.core.javabuilder</name>
+                       <arguments>
+                       </arguments>
+               </buildCommand>
+               <buildCommand>
+                       <name>org.eclipse.pde.ManifestBuilder</name>
+                       <arguments>
+                       </arguments>
+               </buildCommand>
+               <buildCommand>
+                       <name>org.eclipse.pde.SchemaBuilder</name>
+                       <arguments>
+                       </arguments>
+               </buildCommand>
+       </buildSpec>
+       <natures>
+               <nature>org.eclipse.pde.PluginNature</nature>
+               <nature>org.eclipse.jdt.core.javanature</nature>
+       </natures>
+</projectDescription>
diff --git a/org.simantics.district.selection.ui/META-INF/MANIFEST.MF b/org.simantics.district.selection.ui/META-INF/MANIFEST.MF
new file mode 100644 (file)
index 0000000..ee82114
--- /dev/null
@@ -0,0 +1,21 @@
+Manifest-Version: 1.0
+Automatic-Module-Name: org.simantics.district.selection.ui
+Bundle-SymbolicName: org.simantics.district.selection.ui;singleton:=tr
+ ue
+Bundle-Name: Diagram element selection UI
+Bundle-Version: 1.0.0.qualifier
+Require-Bundle: javax.inject,org.eclipse.osgi,org.eclipse.jface,org.ec
+ lipse.e4.ui.model.workbench,org.eclipse.e4.ui.di,org.eclipse.e4.ui.se
+ rvices,org.eclipse.e4.core.di.annotations,org.eclipse.e4.ui.workbench
+ ,org.slf4j.api,org.simantics,org.simantics.district.region,org.simant
+ ics.district.selection,org.simantics.db.common,org.simantics.ui,com.f
+ amfamfam.silk;bundle-version="1.3.0",org.simantics.browsing.ui.common
+ ,org.eclipse.e4.core.contexts,org.eclipse.e4.core.di;bundle-version="
+ 1.6.100",org.simantics.district.region.ontology,org.simantics.distric
+ t.route.ontology,org.simantics.modeling.ontology,org.simantics.silk.o
+ ntology;bundle-version=1.1.0
+Bundle-ManifestVersion: 2
+Bundle-RequiredExecutionEnvironment: JavaSE-1.8
+Bundle-Vendor: Semantum oy
+Import-Package: javax.annotation;version="1.2.0"
+
diff --git a/org.simantics.district.selection.ui/build.properties b/org.simantics.district.selection.ui/build.properties
new file mode 100644 (file)
index 0000000..c8d752e
--- /dev/null
@@ -0,0 +1,6 @@
+source.. = src/
+output.. = bin/
+bin.includes = plugin.xml,\
+               META-INF/,\
+               .,\
+               fragment.e4xmi
diff --git a/org.simantics.district.selection.ui/fragment.e4xmi b/org.simantics.district.selection.ui/fragment.e4xmi
new file mode 100644 (file)
index 0000000..123c997
--- /dev/null
@@ -0,0 +1,31 @@
+<?xml version="1.0" encoding="ASCII"?>
+<fragment:ModelFragments xmi:version="2.0" xmlns:xmi="http://www.omg.org/XMI" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" xmlns:basic="http://www.eclipse.org/ui/2010/UIModel/application/descriptor/basic" xmlns:commands="http://www.eclipse.org/ui/2010/UIModel/application/commands" xmlns:fragment="http://www.eclipse.org/ui/2010/UIModel/fragment" xmlns:menu="http://www.eclipse.org/ui/2010/UIModel/application/ui/menu" xmi:id="_BxaXACerEeWxCPrV0pAZQQ">
+  <fragments xsi:type="fragment:StringModelFragment" xmi:id="_EI7oACerEeWxCPrV0pAZQQ" featurename="descriptors" parentElementId="xpath:/">
+    <elements xsi:type="basic:PartDescriptor" xmi:id="_KMklcCerEeWxCPrV0pAZQQ" elementId="org.simantics.district.selection.ui.parts.elementSelectionView" label="Diagram Element Selections" iconURI="platform:/plugin/org.simantics.silk.ontology/graph/images/table_go.png" tooltip="" category="District Network" contributionURI="bundleclass://org.simantics.district.selection.ui/org.simantics.district.selection.ui.parts.ElementSelectionView">
+      <tags>View</tags>
+      <tags>categoryTag:District</tags>
+    </elements>
+  </fragments>
+  <fragments xsi:type="fragment:StringModelFragment" xmi:id="_aD4EkCVaEem-kMBxWCWPIg" featurename="menuContributions" parentElementId="xpath:/">
+    <elements xsi:type="menu:MenuContribution" xmi:id="_zFdSsCVaEem-kMBxWCWPIg" elementId="org.simantics.district.selection.ui.menucontribution.openview" parentId="popup:#AprosDiagramPopup">
+      <children xsi:type="menu:HandledMenuItem" xmi:id="_403skCVaEem-kMBxWCWPIg" elementId="org.simantics.district.selection.ui.handledmanuitem.openview" label="Open Diagram Element Selection View" iconURI="platform:/plugin/com.famfamfam.silk/icons/table_key.png" command="_OdOfoCVcEem-kMBxWCWPIg"/>
+    </elements>
+    <elements xsi:type="menu:MenuContribution" xmi:id="_QOvjwCVgEem-kMBxWCWPIg" elementId="org.simantics.district.selection.ui.menucontribution.selectionView.contextMenu" parentId="org.simantics.district.selection.ui.contextMenu">
+      <children xsi:type="menu:HandledMenuItem" xmi:id="_ZKWM0CVgEem-kMBxWCWPIg" elementId="org.simantics.district.selection.ui.contextMenu.createNewSelection" label="Create New Selection" iconURI="platform:/plugin/com.famfamfam.silk/icons/table_add.png" command="_rH7MsCVgEem-kMBxWCWPIg"/>
+      <children xsi:type="menu:HandledMenuItem" xmi:id="_Gl5QAC9tEem4UIS68e_fsw" elementId="org.simantics.district.selection.ui.contextMenu.editSelection" label="Edit Element Selection" iconURI="platform:/plugin/org.simantics.silk.ontology/graph/images/pencil.png" command="_dL1xIC7IEem4UIS68e_fsw"/>
+      <children xsi:type="menu:HandledMenuItem" xmi:id="_bVDmsC9tEem4UIS68e_fsw" elementId="org.simantics.district.selection.ui.contextMenu.deleteSelection" label="Delete Element Selection" iconURI="platform:/plugin/org.simantics.silk.ontology/graph/images/cross.png" command="_RfOU0C7PEem4UIS68e_fsw"/>
+    </elements>
+  </fragments>
+  <fragments xsi:type="fragment:StringModelFragment" xmi:id="_Sy-NUCVbEem-kMBxWCWPIg" featurename="commands" parentElementId="xpath:/">
+    <elements xsi:type="commands:Command" xmi:id="_OdOfoCVcEem-kMBxWCWPIg" elementId="org.simantics.district.selection.ui.command.openview" commandName="Open Diagram Element Selection Table"/>
+    <elements xsi:type="commands:Command" xmi:id="_rH7MsCVgEem-kMBxWCWPIg" elementId="org.simantics.district.selection.ui.command.createNewSelection" commandName="Create New Selection"/>
+    <elements xsi:type="commands:Command" xmi:id="_dL1xIC7IEem4UIS68e_fsw" elementId="org.simantics.district.selection.ui.command.editElementSelector" commandName="Edit Element Selection"/>
+    <elements xsi:type="commands:Command" xmi:id="_RfOU0C7PEem4UIS68e_fsw" elementId="org.simantics.district.selection.ui.command.deleteElementSelector" commandName="Delete Element Selection"/>
+  </fragments>
+  <fragments xsi:type="fragment:StringModelFragment" xmi:id="_icxXQCVcEem-kMBxWCWPIg" featurename="handlers" parentElementId="xpath:/">
+    <elements xsi:type="commands:Handler" xmi:id="_k6qWACVcEem-kMBxWCWPIg" elementId="org.simantics.district.selection.ui.handler.openselectiontableview" contributionURI="bundleclass://org.simantics.district.selection.ui/org.simantics.district.selection.ui.handlers.OpenElementSelectorTable" command="_OdOfoCVcEem-kMBxWCWPIg"/>
+    <elements xsi:type="commands:Handler" xmi:id="_0q9pMCVgEem-kMBxWCWPIg" elementId="org.simantics.district.selection.ui.handler.createNewSelection" contributionURI="bundleclass://org.simantics.district.selection.ui/org.simantics.district.selection.ui.handlers.CreateNewElementSelector" command="_rH7MsCVgEem-kMBxWCWPIg"/>
+    <elements xsi:type="commands:Handler" xmi:id="_YSif4C7IEem4UIS68e_fsw" elementId="org.simantics.district.selection.ui.handler.editElementSelector" contributionURI="bundleclass://org.simantics.district.selection.ui/org.simantics.district.selection.ui.handlers.EditElementSelector" command="_dL1xIC7IEem4UIS68e_fsw"/>
+    <elements xsi:type="commands:Handler" xmi:id="_U7VPUC7PEem4UIS68e_fsw" elementId="org.simantics.district.selection.ui.handler.deleteElementSelector" contributionURI="bundleclass://org.simantics.district.selection.ui/org.simantics.district.selection.ui.handlers.DeleteElementSelector" command="_RfOU0C7PEem4UIS68e_fsw"/>
+  </fragments>
+</fragment:ModelFragments>
diff --git a/org.simantics.district.selection.ui/plugin.xml b/org.simantics.district.selection.ui/plugin.xml
new file mode 100644 (file)
index 0000000..ebc4555
--- /dev/null
@@ -0,0 +1,14 @@
+<?xml version="1.0" encoding="UTF-8"?>
+<?eclipse version="3.4"?>
+<plugin>
+
+   <extension
+         id="org.simantics.district.selection.ui.fragment"
+         point="org.eclipse.e4.workbench.model">
+      <fragment
+            apply="notexists"
+            uri="fragment.e4xmi">
+      </fragment>
+   </extension>
+
+</plugin>
diff --git a/org.simantics.district.selection.ui/pom.xml b/org.simantics.district.selection.ui/pom.xml
new file mode 100644 (file)
index 0000000..28f0dd0
--- /dev/null
@@ -0,0 +1,17 @@
+<?xml version="1.0" encoding="UTF-8"?>
+<project
+       xsi:schemaLocation="http://maven.apache.org/POM/4.0.0 http://maven.apache.org/xsd/maven-4.0.0.xsd"
+       xmlns="http://maven.apache.org/POM/4.0.0" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance">
+       <modelVersion>4.0.0</modelVersion>
+
+       <parent>
+               <groupId>org.simantics.district</groupId>
+               <artifactId>org.simantics.district.root</artifactId>
+               <version>1.0.0-SNAPSHOT</version>
+       </parent>
+
+       <artifactId>org.simantics.district.selection.ui</artifactId>
+       <packaging>eclipse-plugin</packaging>
+       <version>1.0.0-SNAPSHOT</version>
+
+</project>
diff --git a/org.simantics.district.selection.ui/src/org/simantics/district/selection/ui/ElementSelectorTableUI.java b/org.simantics.district.selection.ui/src/org/simantics/district/selection/ui/ElementSelectorTableUI.java
new file mode 100644 (file)
index 0000000..0786ce5
--- /dev/null
@@ -0,0 +1,253 @@
+package org.simantics.district.selection.ui;
+
+import java.util.ArrayList;
+import java.util.Collection;
+import java.util.Collections;
+import java.util.List;
+
+import org.eclipse.e4.ui.workbench.modeling.ESelectionService;
+import org.eclipse.jface.viewers.ColumnLabelProvider;
+import org.eclipse.jface.viewers.ColumnViewerToolTipSupport;
+import org.eclipse.jface.viewers.DoubleClickEvent;
+import org.eclipse.jface.viewers.IDoubleClickListener;
+import org.eclipse.jface.viewers.ISelectionChangedListener;
+import org.eclipse.jface.viewers.IStructuredSelection;
+import org.eclipse.jface.viewers.ITreeContentProvider;
+import org.eclipse.jface.viewers.SelectionChangedEvent;
+import org.eclipse.jface.viewers.StructuredSelection;
+import org.eclipse.jface.viewers.TreeSelection;
+import org.eclipse.jface.viewers.TreeViewer;
+import org.eclipse.jface.viewers.TreeViewerColumn;
+import org.eclipse.jface.viewers.Viewer;
+import org.eclipse.swt.SWT;
+import org.eclipse.swt.graphics.Image;
+import org.eclipse.swt.layout.FillLayout;
+import org.eclipse.swt.widgets.Composite;
+import org.eclipse.swt.widgets.Tree;
+import org.simantics.Simantics;
+import org.simantics.browsing.ui.common.AdaptableHintContext;
+import org.simantics.db.ReadGraph;
+import org.simantics.db.Resource;
+import org.simantics.db.common.procedure.adapter.SyncListenerAdapter;
+import org.simantics.db.exception.DatabaseException;
+import org.simantics.db.exception.RuntimeDatabaseException;
+import org.simantics.db.layer0.QueryIndexUtils;
+import org.simantics.db.layer0.SelectionHints;
+import org.simantics.db.layer0.request.ActiveModels;
+import org.simantics.db.request.Read;
+import org.simantics.district.selection.ElementSelectionResource;
+import org.simantics.district.selection.ElementSelectionUtils;
+import org.simantics.district.selection.ElementSelector;
+import org.simantics.layer0.Layer0;
+import org.simantics.scl.runtime.Lists;
+import org.simantics.scl.runtime.function.FunctionImpl1;
+import org.simantics.ui.selection.AnyResource;
+import org.simantics.ui.selection.AnyVariable;
+import org.simantics.ui.selection.WorkbenchSelectionContentType;
+import org.slf4j.Logger;
+import org.slf4j.LoggerFactory;
+
+public class ElementSelectorTableUI extends Composite {
+
+       private static final Logger LOGGER = LoggerFactory.getLogger(ElementSelectorTableUI.class);
+
+       private TreeViewer viewer;
+       private TreeViewerColumn column1;
+       private TreeViewerColumn column2;
+
+       public ElementSelectorTableUI(ESelectionService selectionService, Composite parent, int style) {
+               super(parent, style);
+               parent.setLayout(new FillLayout());
+               //        GridDataFactory.fillDefaults().grab(true, true).applyTo(this);
+               //        GridLayoutFactory.fillDefaults().numColumns(1).applyTo(this);
+               this.setLayout(new FillLayout());
+
+               viewer = new TreeViewer(this, SWT.FULL_SELECTION);
+               viewer.addDoubleClickListener(new IDoubleClickListener() {
+                       @Override
+                       public void doubleClick(DoubleClickEvent event) {
+                               TreeSelection selection = (TreeSelection) viewer.getSelection();
+                               ElementSelector query = (ElementSelector) selection.getFirstElement();
+                               try {
+                                       List<AdaptableHintContext> elements = Simantics.getSession().syncRequest(new Read<List<AdaptableHintContext>>() {
+                                               @SuppressWarnings("unchecked")
+                                               @Override
+                                               public List<AdaptableHintContext> perform(ReadGraph graph) throws DatabaseException {
+                                                       Resource model = ActiveModels.getPossibleActiveModel(graph, Simantics.getProjectResource());
+                                                       if (model == null) {
+                                                               LOGGER.warn("No active model");
+                                                               return Collections.emptyList();
+                                                       }
+                                                       
+                                                       List<Resource> result = query.selectElementsFrom(graph, model);
+                                                       return Lists.map(new FunctionImpl1<Resource, AdaptableHintContext>() {
+                                                               public AdaptableHintContext apply(Resource p0) {
+                                                                       AdaptableHintContext selectionElement = new SelectionElement(SelectionHints.STD_KEYS, graph);
+                                                                       selectionElement.setHint(SelectionHints.KEY_MAIN, p0);
+                                                                       selectionElement.setHint(SelectionHints.KEY_MODEL, model);
+                                                                       return selectionElement;
+                                                               }
+                                                       }, result);
+                                               }
+                                       });
+                                       
+                                       selectionService.setPostSelection(new StructuredSelection(elements));
+                               } catch (DatabaseException e) {
+                                       LOGGER.error("Element selection query failed", e);
+                               }
+                       }
+               });
+               
+               viewer.setContentProvider(new ITreeContentProvider() {
+                       @Override
+                       public boolean hasChildren(Object element) {
+                               return false;
+                       }
+
+                       @Override
+                       public Object getParent(Object element) {
+                               return null;
+                       }
+
+                       @Override
+                       public Object[] getElements(Object inputElement) {
+                               if (inputElement == null || !(inputElement instanceof Collection))
+                                       return new Object[0];
+                               
+                               return ((Collection<?>)inputElement).toArray();
+                       }
+
+                       @Override
+                       public Object[] getChildren(Object parentElement) {
+                               return null;
+                       }
+
+                       @Override
+                       public void inputChanged(Viewer viewer, Object oldInput, Object newInput) {
+                               ITreeContentProvider.super.inputChanged(viewer, oldInput, newInput);
+                       }
+               });
+               
+               Simantics.getSession().asyncRequest(new SelectionsRequest(), new SyncListenerAdapter<Collection<ElementSelector>>() {
+                       public void execute(ReadGraph graph, Collection<ElementSelector> result) {
+                               parent.getDisplay().asyncExec(() -> {
+                                       viewer.setInput(result);
+                               });
+                       }
+
+                       @Override
+                       public void exception(ReadGraph graph, Throwable t) throws DatabaseException {
+                               LOGGER.error("Error getting element selector list", t);
+                       }
+
+                       @Override
+                       public boolean isDisposed() {
+                               return ElementSelectorTableUI.this.isDisposed();
+                       }
+               });
+
+               ColumnViewerToolTipSupport.enableFor(viewer);
+
+               Tree table = viewer.getTree();
+               table.setHeaderVisible(true);
+               table.setLinesVisible(true);
+
+               column1 = new TreeViewerColumn(viewer, SWT.NONE);
+               column1.getColumn().setText("Name");
+               column1.getColumn().setWidth(200);
+               column1.getColumn().setResizable(true);
+               column1.setLabelProvider(new ColumnLabelProvider() {
+                       @Override
+                       public String getText(Object element) {
+                               ElementSelector selector = (ElementSelector) element;
+                               return selector.getName();
+                       }
+                       
+                       @Override
+                       public Image getImage(Object element) {
+                               return null;
+                       }
+               });
+
+               column2 = new TreeViewerColumn(viewer, SWT.NONE);
+               column2.getColumn().setText("Query");
+               column2.getColumn().setWidth(600);
+               column2.getColumn().setResizable(true);
+               column2.setLabelProvider(new ColumnLabelProvider() {
+                       @Override
+                       public String getText(Object element) {
+                               ElementSelector selector = (ElementSelector) element;
+                               return selector.getExpression();
+                       }
+                       
+                       @Override
+                       public Image getImage(Object element) {
+                               return null;
+                       }
+               });
+       }
+
+       public Tree getTree() {
+               return viewer.getTree();
+       }
+
+       public ElementSelector getSelectedItem() {
+               IStructuredSelection selection = (IStructuredSelection) viewer.getSelection();
+               return selection != null ? (ElementSelector) selection.getFirstElement() : null;
+       }
+
+       private final class SelectionElement extends AdaptableHintContext {
+               private final ReadGraph graph;
+
+               private SelectionElement(Key[] keys, ReadGraph graph) {
+                       super(keys);
+                       this.graph = graph;
+               }
+
+               @SuppressWarnings("unchecked")
+               public <T> T getContent(WorkbenchSelectionContentType<T> contentType) {
+                       Resource element = getHint(SelectionHints.KEY_MAIN);
+                       if (contentType instanceof AnyResource) {
+                               return (T)element;
+                       }
+                       else if (contentType instanceof AnyVariable) {
+                               try {
+                                       return (T)ElementSelector.getVariableForElement(graph, element);
+                               } catch (DatabaseException e) {
+                                       return null;
+                               }
+                       }
+                       
+                       return null;
+               }
+       }
+
+       public static class SelectionsRequest implements Read<Collection<ElementSelector>> {
+               @Override
+               public Collection<ElementSelector> perform(ReadGraph graph) throws DatabaseException {
+                       Layer0 L0 = Layer0.getInstance(graph);
+                       ElementSelectionResource ES = ElementSelectionResource.getInstance(graph);
+                       
+                       Resource model = ActiveModels.getPossibleActiveModel(graph, Simantics.getProjectResource());
+                       if (model == null) {
+                               return Collections.emptyList();
+                       }
+                       
+                       List<Resource> libs = QueryIndexUtils.searchByType(graph, model, ES.SelectionLibrary);
+                       if (libs.isEmpty())
+                               return Collections.emptyList();
+                       
+                       Resource lib = libs.get(0);
+                       
+                       List<ElementSelector> result = new ArrayList<>();
+                       for (Resource selection : graph.getObjects(lib, L0.ConsistsOf)) {
+                               if (!graph.isInstanceOf(selection, ES.Selection))
+                                       continue;
+                               
+                               result.add(ElementSelector.getSelector(graph, selection));
+                       }
+                       
+                       return result;
+               }
+       }
+}
diff --git a/org.simantics.district.selection.ui/src/org/simantics/district/selection/ui/handlers/CreateNewElementSelector.java b/org.simantics.district.selection.ui/src/org/simantics/district/selection/ui/handlers/CreateNewElementSelector.java
new file mode 100644 (file)
index 0000000..b9df8b8
--- /dev/null
@@ -0,0 +1,39 @@
+package org.simantics.district.selection.ui.handlers;
+
+import org.eclipse.e4.core.contexts.IEclipseContext;
+import org.eclipse.e4.core.di.annotations.CanExecute;
+import org.eclipse.e4.core.di.annotations.Execute;
+import org.eclipse.e4.ui.workbench.IWorkbench;
+import org.eclipse.e4.ui.workbench.modeling.ESelectionService;
+import org.eclipse.jface.window.Window;
+import org.eclipse.swt.widgets.Shell;
+import org.simantics.db.exception.DatabaseException;
+import org.simantics.district.selection.ui.parts.EditSelectorDialog;
+import org.slf4j.Logger;
+import org.slf4j.LoggerFactory;
+
+public class CreateNewElementSelector {
+
+       private static final Logger LOGGER = LoggerFactory.getLogger(CreateNewElementSelector.class);
+
+       @CanExecute
+       public boolean canExecute(ESelectionService selectionService) {
+               return true;
+       }
+       
+       @Execute
+       public void createNewElementSelector(IEclipseContext context, IWorkbench workbench) {
+               Shell shell = context.getActive(Shell.class);
+               EditSelectorDialog dialog = new EditSelectorDialog(shell, null);
+               LOGGER.debug("Opening dialog");
+               int result = dialog.open();
+               LOGGER.debug("Dialog closed with result code " + result);
+               if (result == Window.OK) {
+                       try {
+                               dialog.writeSelection();
+                       } catch (DatabaseException e) {
+                               LOGGER.error("Writing new element selection failed", e);
+                       }
+               }
+       }
+}
diff --git a/org.simantics.district.selection.ui/src/org/simantics/district/selection/ui/handlers/DeleteElementSelector.java b/org.simantics.district.selection.ui/src/org/simantics/district/selection/ui/handlers/DeleteElementSelector.java
new file mode 100644 (file)
index 0000000..4382e70
--- /dev/null
@@ -0,0 +1,64 @@
+package org.simantics.district.selection.ui.handlers;
+
+import javax.inject.Inject;
+
+import org.eclipse.e4.core.contexts.IEclipseContext;
+import org.eclipse.e4.core.di.annotations.CanExecute;
+import org.eclipse.e4.core.di.annotations.Execute;
+import org.eclipse.e4.ui.model.application.ui.basic.MPart;
+import org.eclipse.e4.ui.workbench.IWorkbench;
+import org.eclipse.e4.ui.workbench.modeling.EPartService;
+import org.eclipse.e4.ui.workbench.modeling.ESelectionService;
+import org.simantics.Simantics;
+import org.simantics.db.WriteGraph;
+import org.simantics.db.common.request.WriteRequest;
+import org.simantics.db.exception.DatabaseException;
+import org.simantics.district.selection.ElementSelector;
+import org.simantics.district.selection.ui.parts.ElementSelectionView;
+import org.slf4j.Logger;
+import org.slf4j.LoggerFactory;
+
+public class DeleteElementSelector {
+
+       private static final Logger LOGGER = LoggerFactory.getLogger(DeleteElementSelector.class);
+
+       @Inject
+       EPartService partService;
+       
+       @CanExecute
+       public boolean canExecute(ESelectionService selectionService) {
+               MPart part = partService.getActivePart();
+               if (part == null) return false;
+               
+               Object object = part.getObject();
+               if (object == null || !(object instanceof ElementSelectionView))
+                       return false;
+               
+               ElementSelectionView view = (ElementSelectionView)object;
+               return view.getSelectedItem() != null;
+       }
+
+       @Execute
+       public void editElementSelector(IEclipseContext context, IWorkbench workbench) {
+               MPart part = partService.getActivePart();
+               if (part == null) return;
+               
+               Object object = part.getObject();
+               if (object == null || !(object instanceof ElementSelectionView))
+                       return;
+               
+               ElementSelectionView view = (ElementSelectionView)object;
+               ElementSelector selectedItem = view.getSelectedItem();
+               
+               if (selectedItem == null)
+                       return;
+               
+               Simantics.getSession().async(new WriteRequest() {
+                       @Override
+                       public void perform(WriteGraph graph) throws DatabaseException {
+                               graph.markUndoPoint();
+                               graph.deny(selectedItem.getResource());
+                       }
+               });
+       }
+}
diff --git a/org.simantics.district.selection.ui/src/org/simantics/district/selection/ui/handlers/EditElementSelector.java b/org.simantics.district.selection.ui/src/org/simantics/district/selection/ui/handlers/EditElementSelector.java
new file mode 100644 (file)
index 0000000..f914d16
--- /dev/null
@@ -0,0 +1,66 @@
+package org.simantics.district.selection.ui.handlers;
+
+import javax.inject.Inject;
+
+import org.eclipse.e4.core.contexts.ContextInjectionFactory;
+import org.eclipse.e4.core.contexts.IEclipseContext;
+import org.eclipse.e4.core.di.annotations.CanExecute;
+import org.eclipse.e4.core.di.annotations.Execute;
+import org.eclipse.e4.ui.model.application.ui.basic.MPart;
+import org.eclipse.e4.ui.workbench.IWorkbench;
+import org.eclipse.e4.ui.workbench.modeling.EPartService;
+import org.eclipse.e4.ui.workbench.modeling.ESelectionService;
+import org.eclipse.jface.window.Window;
+import org.eclipse.swt.widgets.Shell;
+import org.simantics.db.exception.DatabaseException;
+import org.simantics.district.selection.ui.parts.EditSelectorDialog;
+import org.simantics.district.selection.ui.parts.ElementSelectionView;
+import org.slf4j.Logger;
+import org.slf4j.LoggerFactory;
+
+public class EditElementSelector {
+
+       private static final Logger LOGGER = LoggerFactory.getLogger(EditElementSelector.class);
+
+       @Inject
+       EPartService partService;
+       
+       @CanExecute
+       public boolean canExecute(ESelectionService selectionService) {
+               MPart part = partService.getActivePart();
+               if (part == null) return false;
+               
+               Object object = part.getObject();
+               if (object == null || !(object instanceof ElementSelectionView))
+                       return false;
+               
+               ElementSelectionView view = (ElementSelectionView)object;
+               return view.getSelectedItem() != null;
+       }
+
+       @Execute
+       public void editElementSelector(IEclipseContext context, IWorkbench workbench) {
+               MPart part = partService.getActivePart();
+               if (part == null) return;
+               
+               Object object = part.getObject();
+               if (object == null || !(object instanceof ElementSelectionView))
+                       return;
+               
+               ElementSelectionView view = (ElementSelectionView)object;
+               
+               Shell shell = context.getActive(Shell.class);
+               EditSelectorDialog dialog = new EditSelectorDialog(shell, view.getSelectedItem());
+               
+               LOGGER.debug("Opening dialog");
+               int result = dialog.open();
+               LOGGER.debug("Dialog closed with result code " + result);
+               if (result == Window.OK) {
+                       try {
+                               dialog.writeSelection();
+                       } catch (DatabaseException e) {
+                               LOGGER.error("Writing new element selection failed", e);
+                       }
+               }
+       }
+}
diff --git a/org.simantics.district.selection.ui/src/org/simantics/district/selection/ui/handlers/OpenElementSelectorTable.java b/org.simantics.district.selection.ui/src/org/simantics/district/selection/ui/handlers/OpenElementSelectorTable.java
new file mode 100644 (file)
index 0000000..4a9bd29
--- /dev/null
@@ -0,0 +1,22 @@
+package org.simantics.district.selection.ui.handlers;
+
+import org.eclipse.e4.core.di.annotations.CanExecute;
+import org.eclipse.e4.core.di.annotations.Execute;
+import org.eclipse.e4.ui.workbench.modeling.ESelectionService;
+import org.simantics.ui.workbench.e4.E4WorkbenchUtils;
+
+public class OpenElementSelectorTable {
+
+       private static final String VIEW_ID = "org.simantics.diagram.selection.ui.elementSelectionView";
+
+       @CanExecute
+       public boolean canExecute(ESelectionService selectionService) {
+               return true;
+       }
+
+       @Execute
+       public void openPropertyTable(ESelectionService selectionService) {
+               E4WorkbenchUtils.openAndShowPart(VIEW_ID);
+       }
+
+}
diff --git a/org.simantics.district.selection.ui/src/org/simantics/district/selection/ui/parts/EditSelectorDialog.java b/org.simantics.district.selection.ui/src/org/simantics/district/selection/ui/parts/EditSelectorDialog.java
new file mode 100644 (file)
index 0000000..42270da
--- /dev/null
@@ -0,0 +1,909 @@
+package org.simantics.district.selection.ui.parts;
+
+import java.util.ArrayList;
+import java.util.HashMap;
+import java.util.List;
+import java.util.Map;
+import java.util.UUID;
+
+import javax.inject.Inject;
+
+import org.eclipse.core.runtime.IStatus;
+import org.eclipse.core.runtime.Status;
+import org.eclipse.jface.dialogs.Dialog;
+import org.eclipse.jface.dialogs.ErrorDialog;
+import org.eclipse.jface.layout.GridDataFactory;
+import org.eclipse.jface.layout.GridLayoutFactory;
+import org.eclipse.jface.layout.RowLayoutFactory;
+import org.eclipse.jface.window.Window;
+import org.eclipse.swt.SWT;
+import org.eclipse.swt.custom.StackLayout;
+import org.eclipse.swt.events.SelectionAdapter;
+import org.eclipse.swt.events.SelectionEvent;
+import org.eclipse.swt.widgets.Button;
+import org.eclipse.swt.widgets.Combo;
+import org.eclipse.swt.widgets.Composite;
+import org.eclipse.swt.widgets.Control;
+import org.eclipse.swt.widgets.Label;
+import org.eclipse.swt.widgets.Shell;
+import org.eclipse.swt.widgets.Text;
+import org.simantics.Simantics;
+import org.simantics.db.ReadGraph;
+import org.simantics.db.Resource;
+import org.simantics.db.Session;
+import org.simantics.db.WriteGraph;
+import org.simantics.db.common.request.ReadRequest;
+import org.simantics.db.common.request.WriteRequest;
+import org.simantics.db.exception.DatabaseException;
+import org.simantics.db.exception.RuntimeDatabaseException;
+import org.simantics.db.layer0.QueryIndexUtils;
+import org.simantics.db.layer0.request.ActiveModels;
+import org.simantics.db.request.Read;
+import org.simantics.db.request.WriteResult;
+import org.simantics.district.region.ontology.DiagramRegionsResource;
+import org.simantics.district.route.ontology.RouteResource;
+import org.simantics.district.selection.ElementSelectionResource;
+import org.simantics.district.selection.ElementSelectionUtils;
+import org.simantics.district.selection.ElementSelector;
+import org.simantics.district.selection.ElementSelector.AggregateCondition;
+import org.simantics.district.selection.ElementSelector.AggregateCondition.Type;
+import org.simantics.district.selection.ElementSelector.All;
+import org.simantics.district.selection.ElementSelector.Condition;
+import org.simantics.district.selection.ElementSelector.DiagramGenerator;
+import org.simantics.district.selection.ElementSelector.ExplicitGenerator;
+import org.simantics.district.selection.ElementSelector.Generator;
+import org.simantics.district.selection.ElementSelector.ModelGenerator;
+import org.simantics.district.selection.ElementSelector.PropertyCondition;
+import org.simantics.district.selection.ElementSelector.PropertySelector;
+import org.simantics.district.selection.ElementSelector.RegionCondition;
+import org.simantics.district.selection.ElementSelector.RouteCondition;
+import org.simantics.district.selection.ElementSelector.Selector;
+import org.simantics.layer0.Layer0;
+import org.simantics.modeling.ModelingResources;
+import org.simantics.utils.datastructures.Arrays;
+import org.slf4j.Logger;
+import org.slf4j.LoggerFactory;
+
+public class EditSelectorDialog extends Dialog {
+
+       private static Logger LOGGER = LoggerFactory.getLogger(EditSelectorDialog.class);
+       
+       private ElementSelector elementSelector;
+
+       // Data for comboboxes
+       Map<Resource, String> diagrams;
+       
+       private String[] regionNames;
+       private Resource[] regionResources;
+
+       private String[] routeNames;
+       private Resource[] routeResources;
+
+       // Dialog fields
+       private int generatorIndex;
+       private Combo sourceField;
+
+       private String name;
+       private Text nameField;
+
+       private Resource diagram;
+       private Combo diagramField;
+
+       private int selectorIndex;
+       private Combo selectorField;
+
+       private String propertyName;
+       private Text propertyField;
+
+       private int numberOfItems;
+       private Text nField;
+
+       private Condition condition;
+       private Button removeConditionButton;
+       private Text conditionLabel;
+
+       // Dialog area component
+       private Composite content;
+
+       @Inject
+       public EditSelectorDialog(Shell shell, ElementSelector elementSelector) {
+               super(shell);
+               
+               this.elementSelector = elementSelector;
+               if (elementSelector != null) {
+                       try {
+                               Simantics.getSession().sync(new ReadRequest() {
+                                       @Override
+                                       public void run(ReadGraph graph) throws DatabaseException {
+                                               elementSelector.buildSelection(graph);
+                                       }
+                               });
+                       } catch (DatabaseException e1) {
+                               LOGGER.error("Failed to read element selector resource " + elementSelector.getResource(), e1);
+                               throw new RuntimeDatabaseException(e1);
+                       }
+               }
+               
+               final Map<Resource, String> regions = new HashMap<>();
+               final Map<Resource, String> routes = new HashMap<>();
+               
+               try {
+                       Simantics.getSession().syncRequest(new Read<Void>() {
+                               @Override
+                               public Void perform(ReadGraph graph) throws DatabaseException {
+                                       Resource model = ActiveModels.getPossibleActiveModel(graph, Simantics.getProjectResource());
+                                       List<Resource> regionCollection = QueryIndexUtils.searchByType(graph, model, DiagramRegionsResource.getInstance(graph).Region);
+                                       for (Resource r : regionCollection) {
+                                               String name = graph.getRelatedValue(r, Layer0.getInstance(graph).HasName);
+                                               regions.put(r, name);
+                                       }
+                                       
+                                       List<Resource> routeCollection = QueryIndexUtils.searchByType(graph, model, RouteResource.getInstance(graph).Route);
+                                       for (Resource r : routeCollection) {
+                                               String name = graph.getRelatedValue(r, Layer0.getInstance(graph).HasName);
+                                               routes.put(r, name);
+                                       }
+                                       return null;
+                               }
+                       });
+               } catch (DatabaseException e) {
+                       LOGGER.error("Failed to read routes and/or regions in the model", e);
+               }
+               
+               regionNames = regions.values().toArray(new String[regions.size()]);
+               regionResources = regions.keySet().toArray(new Resource[regions.size()]);
+               
+               routeNames = routes.values().toArray(new String[routes.size()]);
+               routeResources = routes.keySet().toArray(new Resource[routes.size()]);
+       
+               name = elementSelector != null ? elementSelector.getName() : "";
+               propertyName = "";
+               numberOfItems = 1;
+               generatorIndex = 0;
+               selectorIndex = 0;
+               diagram = null;
+               condition = null;
+               
+               if (elementSelector != null) {
+                       Generator generator = elementSelector.getGenerator();
+                       if (generator instanceof ModelGenerator) {
+                               generatorIndex = 0;
+                       }
+                       else if (generator instanceof DiagramGenerator) {
+                               generatorIndex = 1;
+                               diagram = ((DiagramGenerator)generator).diagram;
+                       }
+                       else if (generator instanceof ExplicitGenerator) {
+                               generatorIndex = 2;
+                               // TODO: Management of explicit lists of elements
+                       }
+                       else {
+                               throw new IllegalStateException("Unknown generator type " + generator.getClass().getName());
+                       }
+                       
+                       Selector selector = elementSelector.getSelector();
+                       if (selector instanceof All) {
+                               selectorIndex = 0;
+                       }
+                       else if (selector instanceof PropertySelector) {
+                               PropertySelector propertySelector = (PropertySelector)selector;
+                               selectorIndex = propertySelector.smallest ? 1 : 2;
+                               propertyName = propertySelector.propertyName;
+                               numberOfItems = propertySelector.resultCount;
+                       }
+                       else {
+                               throw new IllegalStateException("Unknwon selector type " + selector.getClass().getName());
+                       }
+                       
+                       condition = elementSelector.getCondition();
+               }
+       }
+       
+       @Override
+       protected void okPressed() {
+               generatorIndex = sourceField.getSelectionIndex();
+               if (generatorIndex == 1) {
+                       int selectionIndex = diagramField.getSelectionIndex();
+                       if (selectionIndex < 0) {
+                               ErrorDialog.openError(getShell(), "Error", "Please select a diagram", new Status(IStatus.ERROR, "org.simantics.district.selection.ui", "No diagram selected"));
+                               return;
+                       }
+                       
+                       diagram = new ArrayList<Resource>(diagrams.keySet()).get(selectionIndex);
+               }
+               
+               name = nameField.getText();
+               
+               selectorIndex = selectorField.getSelectionIndex();
+               
+               propertyName = propertyField.getText();
+               String text = nField.getText();
+               numberOfItems = "".equals(text) ? 0 : Integer.parseInt(text);
+               
+               super.okPressed();
+       }
+       
+       public void writeSelection() throws DatabaseException {
+               Simantics.getSession().syncRequest(new WriteRequest() {
+                       @Override
+                       public void perform(WriteGraph graph) throws DatabaseException {
+                               Layer0 L0 = Layer0.getInstance(graph);
+                               ElementSelectionResource ES = ElementSelectionResource.getInstance(graph);
+                               
+                               graph.markUndoPoint();
+                               
+                               Resource lib = ElementSelectionUtils.ensureSelectionLibrary(graph);
+                               
+                               // Selection
+                               Resource selection;
+                               if (elementSelector != null) {
+                                       selection = elementSelector.getResource();
+                                       graph.deny(selection);
+                               }
+                               else {
+                                       selection = graph.newResource();
+                               }
+                               
+                               graph.claim(selection, L0.InstanceOf, ES.Selection);
+                               graph.claimLiteral(selection, L0.HasName, L0.String, UUID.randomUUID().toString());
+                               graph.claimLiteral(selection, L0.HasLabel, L0.String, name);
+                               graph.claim(selection, L0.PartOf, lib);
+                               
+                               // Generator
+                               Resource generator = graph.newResource();
+                               Resource generatorType;
+                               switch (generatorIndex) {
+                               case 0:
+                                       generatorType = ES.Generator_Model;
+                                       break;
+                               case 1:
+                                       generatorType = ES.Generator_Diagram;
+                                       Resource composite = graph.getPossibleObject(diagram, ModelingResources.getInstance(graph).DiagramToComposite);
+                                       graph.claim(generator, ES.Generator_HasDiagram, composite != null ? composite : diagram);
+                                       break;
+                               case 2:
+                                       generatorType = ES.Generator_Explicit;
+                                       // TODO: Claim relations to current selection elements
+                                       break;
+                               default: throw new IllegalStateException("Invalid source index " + generatorIndex);
+                               }
+                               graph.claim(generator, L0.InstanceOf, generatorType);
+                               graph.claim(selection, ES.Selection_HasGenerator, generator);
+                               
+                               // Selector
+                               Resource selector = graph.newResource();
+                               Resource selectorType;
+                               switch (selectorIndex) {
+                               case 0: selectorType = ES.Selector_All; break;
+                               case 1: selectorType = ES.Selector_NLowest; break;
+                               case 2: selectorType = ES.Selector_NHighest; break;
+                               default: throw new IllegalStateException("Invalid selector index " + selectorIndex);
+                               }
+                               graph.claim(selector, L0.InstanceOf, selectorType);
+                               graph.claim(selection, ES.Selection_HasSelector, selector);
+                               
+                               if (selectorIndex > 0) {
+                                       graph.claimLiteral(selector, ES.PropertySelector_HasSelectionPropertyName, L0.String, propertyName);
+                                       graph.claimLiteral(selector, ES.PropertySelector_HasResultCount, L0.Integer, numberOfItems);
+                               }
+                               
+                               // Condition
+                               if (condition != null) {
+                                       graph.claim(selection, ES.Selection_HasCondition, condition.resource);
+                               }
+                       }
+               });
+       }
+
+       @Override
+       protected Control createDialogArea(Composite parent) {
+               // Set dialog title
+               getShell().setText("Edit element selector");
+               
+               content = new Composite(parent, SWT.NONE);
+               GridLayoutFactory.swtDefaults().numColumns(3).applyTo(content);
+               
+               // Name
+               Label nameLabel = new Label(content, SWT.NONE);
+               nameLabel.setText("Name");
+               GridDataFactory.swtDefaults().applyTo(nameLabel);
+               
+               nameField = new Text(content, SWT.BORDER);
+               nameField.setEditable(true);
+               nameField.setText(name);
+               GridDataFactory.swtDefaults().span(2, 1).hint(200, SWT.DEFAULT).applyTo(nameField);
+               
+               // Source
+               Label sourceLabel = new Label(content, SWT.NONE);
+               sourceLabel.setText("Source");
+               GridDataFactory.swtDefaults().applyTo(sourceLabel);
+               
+               sourceField = new Combo(content, SWT.BORDER | SWT.READ_ONLY);
+               sourceField.setItems("Model", "Diagram", "Current selection");
+               sourceField.select(generatorIndex);
+               GridDataFactory.swtDefaults().span(1, 1).applyTo(sourceField);
+               
+               diagramField = new Combo(content, SWT.BORDER | SWT.READ_ONLY);
+               GridDataFactory.swtDefaults().span(1, 1).applyTo(diagramField);
+               diagrams = ElementSelector.findDiagrams();
+               diagramField.setItems(diagrams.values().toArray(new String[diagrams.size()]));
+               
+               int diagramIndex = diagram != null ? new ArrayList<>(diagrams.keySet()).indexOf(diagram) : -1;
+               diagramField.select(diagramIndex);
+               
+               sourceField.addSelectionListener(new SelectionAdapter() {
+                       @Override
+                       public void widgetSelected(SelectionEvent e) {
+                               generatorIndex = sourceField.getSelectionIndex();
+                               diagramField.setVisible(isDiagramFieldVisible());
+                       }
+               });
+               
+               sourceField.select(generatorIndex);
+               diagramField.setVisible(isDiagramFieldVisible());
+               
+               // Selector
+               Label selectorLabel = new Label(content, SWT.NONE);
+               selectorLabel.setText("Select");
+               GridDataFactory.swtDefaults().span(1, 1).applyTo(selectorLabel);
+               
+               selectorField = new Combo(content, SWT.BORDER | SWT.READ_ONLY);
+               selectorField.setItems("All", "N lowest", "N highest");
+               selectorField.select(selectorIndex);
+               GridDataFactory.swtDefaults().span(1, 1).applyTo(selectorField);
+               
+               Composite selectorComposite = new Composite(content, SWT.NONE);
+               GridDataFactory.swtDefaults().span(1, 1).applyTo(selectorComposite);
+               GridLayoutFactory.swtDefaults().numColumns(2).applyTo(selectorComposite);
+               
+               Label propertyLabel = new Label(selectorComposite, SWT.NONE);
+               propertyLabel.setText("Property name");
+               GridDataFactory.swtDefaults().applyTo(propertyLabel);
+               
+               propertyField = new Text(selectorComposite, SWT.BORDER);
+               propertyField.setText(propertyName);
+               GridDataFactory.swtDefaults().hint(80, SWT.DEFAULT).applyTo(propertyField);
+               
+               Label nLabel = new Label(selectorComposite, SWT.NONE);
+               nLabel.setText("Number of elements");
+               GridDataFactory.swtDefaults().applyTo(nLabel);
+               
+               nField = new Text(selectorComposite, SWT.BORDER);
+               nField.setText(Integer.toString(numberOfItems));
+               GridDataFactory.swtDefaults().hint(40, SWT.DEFAULT).applyTo(nField);
+               
+               selectorField.addSelectionListener(new SelectionAdapter() {
+                       @Override
+                       public void widgetSelected(SelectionEvent e) {
+                               selectorIndex = selectorField.getSelectionIndex();
+                               selectorComposite.setVisible(isSelectorCompositeVisible());
+                       }
+               });
+               
+               selectorField.select(selectorIndex);
+               selectorComposite.setVisible(isSelectorCompositeVisible());
+               
+               // Condition
+               new Label(content, SWT.NONE).setText("Condition");
+               conditionLabel = new Text(content, SWT.READ_ONLY);
+               GridDataFactory.swtDefaults().span(2, 1).applyTo(conditionLabel);
+               
+               new Label(content, SWT.NONE);
+               Composite conditionPanel = new Composite(content, SWT.NONE);
+               GridDataFactory.swtDefaults().span(2, 1).applyTo(conditionPanel);
+               GridLayoutFactory.swtDefaults().margins(0, 0).numColumns(2).applyTo(conditionPanel);
+               Button conditionButton = new Button(conditionPanel, SWT.PUSH);
+               conditionButton.setText("Edit...");
+               GridDataFactory.swtDefaults().span(1, 1).applyTo(conditionButton);
+               removeConditionButton = new Button(conditionPanel, SWT.PUSH);
+               removeConditionButton.setText("Remove");
+               GridDataFactory.swtDefaults().span(1, 1).applyTo(removeConditionButton);
+               
+               updateCondition();
+               
+               conditionButton.addSelectionListener(new SelectionAdapter() {
+                       @Override
+                       public void widgetSelected(SelectionEvent e) {
+                               ConditionDialog dialog = new ConditionDialog(getShell(), condition);
+                               if (dialog.open() == Window.OK) {
+                                       try {
+                                               condition = dialog.createCondition();
+                                       } catch (DatabaseException e1) {
+                                               LOGGER.error("Creating a condition object failed", e1);
+                                       }
+                                       
+                                       updateCondition();
+                               }
+                       }
+               });
+               
+               removeConditionButton.addSelectionListener(new SelectionAdapter() {
+                       @Override
+                       public void widgetSelected(SelectionEvent e) {
+                               condition = null;
+                               updateCondition();
+                       }
+               });
+               
+               return content;
+       }
+       
+       private void updateCondition() {
+               if (condition != null) {
+                       removeConditionButton.setEnabled(true);
+                       try {
+                               conditionLabel.setText(ElementSelector.getExpression(Simantics.getSession(), condition.resource));
+                       } catch (DatabaseException e) {
+                               LOGGER.error("Error getting expression string for " + condition.resource);
+                       }
+               }
+               else {
+                       conditionLabel.setText("No condition");
+                       removeConditionButton.setEnabled(false);
+               }
+               
+               content.layout();
+       }
+
+       private boolean isDiagramFieldVisible() {
+               return generatorIndex == 1;
+       }
+
+       private boolean isSelectorCompositeVisible() {
+               return selectorIndex != 0;
+       }
+
+       class ConditionDialog extends Dialog {
+               // Resource of the edited condition
+               private Resource existingResource;
+               
+               // Inverse condition button
+               private boolean isInverse;
+               private Button inverseField;
+
+               // Condition type
+               private int typeIndex;
+               private Combo typeField;
+               
+               // Type-specific control panels under a stack layout
+               private Composite stackPanel;
+               private StackLayout stack;
+               
+               private Composite propertyPanel;
+               private Composite regionPanel;
+               private Composite routePanel;
+               private Composite aggregatePanel;
+               
+               // Property condition
+               private Double lowerLimit;
+               private Double upperLimit;
+               private String propertyName;
+       
+               private Text propertyNameField;
+               private Text lowerLimitField;
+               private Text upperLimitField;
+               
+               // Region condition
+               private Resource region;
+               private Combo regionField;
+               
+               // Route condition
+               private Resource route;
+               private Combo routeField;
+               
+               // Aggregate condition
+               private List<Condition> subConditions;
+               private boolean isConjunction;
+               
+               private org.eclipse.swt.widgets.List subConditionField;
+               private Combo operatorField;
+               
+               public ConditionDialog(Shell shell, Condition condition) {
+                       super(shell);
+                       
+                       typeIndex = 0;
+                       isInverse = false;
+                       propertyName = "";
+                       upperLimit = null;
+                       lowerLimit = null;
+                       subConditions = new ArrayList<>();
+                       
+                       existingResource = condition != null ? condition.resource : null;
+
+                       if (condition != null) {
+                               if (condition instanceof PropertyCondition) {
+                                       typeIndex = 0;
+                                       PropertyCondition propertyCondition = (PropertyCondition)condition;
+                                       propertyName = propertyCondition.propertyName;
+                                       upperLimit = propertyCondition.upperLimit;
+                                       lowerLimit = propertyCondition.lowerLimit;
+                               }
+                               else if (condition instanceof RegionCondition) {
+                                       typeIndex = 1;
+                                       region = ((RegionCondition)condition).regionResource;
+                               }
+                               else if (condition instanceof RouteCondition) {
+                                       typeIndex = 2;
+                                       route = ((RouteCondition)condition).routeResource;
+                               }
+                               else if (condition instanceof AggregateCondition) {
+                                       typeIndex = 3;
+                                       subConditions = new ArrayList<>(((AggregateCondition)condition).conditions);
+                                       isConjunction = ((AggregateCondition)condition).type == Type.CONJUNCTION;
+                                       isInverse = ((AggregateCondition)condition).type == Type.NEGATION;
+                               }
+                       }
+               }
+               
+               @Override
+               protected Control createDialogArea(Composite parent) {
+                       getShell().setText("Edit selector condition");
+                       
+                       Composite content = (Composite)super.createDialogArea(parent);
+                       GridLayoutFactory.swtDefaults().numColumns(1).applyTo(content);
+                       
+                       GridDataFactory defaultWidth = GridDataFactory.swtDefaults().hint(200, SWT.DEFAULT);
+                       
+                       // Is inverse
+                       inverseField = new Button(content, SWT.CHECK);
+                       inverseField.setText("Is inverse");
+                       inverseField.setSelection(isInverse);
+                       
+                       // Condition type
+                       typeField = new Combo(content, SWT.BORDER | SWT.READ_ONLY);
+                       typeField.setItems("Property value", "In region", "On route", "Combination");
+                       typeField.select(typeIndex);
+                       
+                       // Type-dependent stacked panels
+                       stackPanel = new Composite(content, SWT.NONE);
+                       stack = new StackLayout();
+                       stackPanel.setLayout(stack);
+                       
+                       // Property condition panel
+                       propertyPanel = new Composite(stackPanel, SWT.NONE);
+                       GridLayoutFactory.swtDefaults().numColumns(2).applyTo(propertyPanel);
+                       
+                       new Label(propertyPanel, SWT.NONE).setText("Property name");
+                       propertyNameField = new Text(propertyPanel, SWT.BORDER);
+                       propertyNameField.setText(propertyName);
+                       defaultWidth.applyTo(propertyNameField);
+                       
+                       new Label(propertyPanel, SWT.NONE).setText("Lower limit");
+                       lowerLimitField = new Text(propertyPanel, SWT.BORDER);
+                       defaultWidth.applyTo(lowerLimitField);
+                       if (lowerLimit != null) lowerLimitField.setText(lowerLimit.toString());
+                       
+                       new Label(propertyPanel, SWT.NONE).setText("Upper limit");
+                       upperLimitField = new Text(propertyPanel, SWT.BORDER);
+                       defaultWidth.applyTo(upperLimitField);
+                       if (upperLimit != null) upperLimitField.setText(upperLimit.toString());
+                       
+                       // Region condition panel
+                       regionPanel = new Composite(stackPanel, SWT.NONE);
+                       GridLayoutFactory.swtDefaults().numColumns(2).applyTo(regionPanel);
+                       
+                       new Label(regionPanel, SWT.NONE).setText("Region");
+                       regionField = new Combo(regionPanel, SWT.BORDER | SWT.READ_ONLY);
+                       regionField.setItems(regionNames);
+                       defaultWidth.applyTo(regionField);
+                       
+                       if (region != null) {
+                               int regionIndex = Arrays.indexOf(regionResources, region);
+                               regionField.select(regionIndex);
+                       }
+                       else {
+                               regionField.select(0);
+                       }
+                       
+                       // Route condition panel
+                       routePanel = new Composite(stackPanel, SWT.NONE);
+                       GridLayoutFactory.swtDefaults().numColumns(2).applyTo(routePanel);
+                       
+                       new Label(routePanel, SWT.NONE).setText("Route");
+                       routeField = new Combo(routePanel, SWT.BORDER | SWT.READ_ONLY);
+                       routeField.setItems(routeNames);
+                       defaultWidth.applyTo(routeField);
+                       
+                       if (route != null) {
+                               int routeIndex = Arrays.indexOf(routeResources, route);
+                               routeField.select(routeIndex);
+                       }
+                       else {
+                               routeField.select(0);
+                       }
+                       
+                       // Aggregate condition panel
+                       aggregatePanel = new Composite(stackPanel, SWT.NONE);
+                       GridLayoutFactory.swtDefaults().numColumns(2).applyTo(aggregatePanel);
+                       
+                       new Label(aggregatePanel, SWT.NONE).setText("Operator");
+                       operatorField = new Combo(aggregatePanel, SWT.READ_ONLY);
+                       operatorField.setItems("And", "Or");
+                       operatorField.select(isConjunction ? 0 : 1);
+                       
+                       new Label(aggregatePanel, SWT.NONE).setText("Sub-conditions");
+                       Composite buttons = new Composite(aggregatePanel, SWT.NONE);
+                       RowLayoutFactory.swtDefaults().justify(true).fill(true).extendedMargins(0, 0, 0, 0).type(SWT.HORIZONTAL).applyTo(buttons);
+                       
+                       Button addButton = new Button(buttons, SWT.PUSH);
+                       addButton.setText("Add");
+                       Button removeButton = new Button(buttons, SWT.PUSH);
+                       removeButton.setText("Remove");
+                       Button editButton = new Button(buttons, SWT.PUSH);
+                       editButton.setText("Edit");
+
+                       new Label(aggregatePanel, SWT.NONE);
+                       subConditionField = new org.eclipse.swt.widgets.List(aggregatePanel, SWT.BORDER);
+                       GridDataFactory.swtDefaults().hint(200, 150).applyTo(subConditionField);
+                       if (subConditions != null) {
+                               Session session = Simantics.getSession();
+                               List<String> items = new ArrayList<>();
+                               for (Condition c : subConditions) {
+                                       try {
+                                               items.add(ElementSelector.getExpression(session, c.resource));
+                                       } catch (DatabaseException e1) {
+                                               LOGGER.error("Condition expression read failed", e1);
+                                               items.add("<Unknown expression>");
+                                       }
+                               }
+                               
+                               subConditionField.setItems(items.toArray(new String[items.size()]));
+                       }
+                       
+                       addButton.addSelectionListener(new SelectionAdapter() {
+                               @Override
+                               public void widgetSelected(SelectionEvent e) {
+                                       ConditionDialog conditionDialog = new ConditionDialog(getShell(), null);
+                                       if (conditionDialog.open() == Window.OK) {
+                                               Condition condition;
+                                               try {
+                                                       condition = conditionDialog.createCondition();
+                                                       subConditions.add(condition);
+                                                       
+                                                       try {
+                                                               subConditionField.add(ElementSelector.getExpression(Simantics.getSession(), condition.resource));
+                                                       } catch (DatabaseException e1) {
+                                                               LOGGER.error("Condition expression read failed", e1);
+                                                               subConditionField.add("<Unknown expression>");
+                                                       }
+                                               } catch (DatabaseException e2) {
+                                                       LOGGER.error("Create condition failed", e2);
+                                               }
+                                       }
+                               }
+                       });
+                       
+                       removeButton.addSelectionListener(new SelectionAdapter() {
+                               @Override
+                               public void widgetSelected(SelectionEvent e) {
+                                       int index = subConditionField.getSelectionIndex();
+                                       if (index >= 0) {
+                                               subConditionField.deselectAll();
+                                               subConditionField.remove(index);
+                                               subConditions.remove(index);
+                                               
+                                               if (index < subConditions.size())
+                                                       subConditionField.setSelection(index);
+                                       }
+                                       
+                                       boolean selected = subConditionField.getSelectionIndex() >= 0;
+                                       removeButton.setEnabled(selected);
+                                       editButton.setEnabled(selected);
+                               }
+                       });
+                       
+                       editButton.addSelectionListener(new SelectionAdapter() {
+                               @Override
+                               public void widgetSelected(SelectionEvent e) {
+                                       int index = subConditionField.getSelectionIndex();
+                                       if (index >= 0) {
+                                               Condition condition = subConditions.get(index);
+                                               ConditionDialog conditionDialog = new ConditionDialog(getShell(), condition);
+                                               if (conditionDialog.open() == Window.OK) {
+                                                       try {
+                                                               condition = conditionDialog.createCondition();
+                                                               subConditions.set(index, condition);
+                                                               
+                                                               try {
+                                                                       subConditionField.setItem(index, ElementSelector.getExpression(Simantics.getSession(), condition.resource));
+                                                               } catch (DatabaseException e1) {
+                                                                       LOGGER.error("Condition expression read failed", e1);
+                                                                       subConditionField.setItem(index, "<Unknown expression>");
+                                                               }
+                                                       } catch (DatabaseException e2) {
+                                                               LOGGER.error("Create condition failed", e2);
+                                                       }
+                                               }
+                                       }
+                               }
+                       });
+                       
+                       subConditionField.addSelectionListener(new SelectionAdapter() {
+                               @Override
+                               public void widgetSelected(SelectionEvent e) {
+                                       boolean selected = subConditionField.getSelectionIndex() >= 0;
+                                       removeButton.setEnabled(selected);
+                                       editButton.setEnabled(selected);
+                               }
+                       });
+
+                       // Stack layout update
+                       typeField.addSelectionListener(new SelectionAdapter() {
+                               @Override
+                               public void widgetSelected(SelectionEvent e) {
+                                       updateStack();
+                               }
+                       });
+                       
+                       updateStack();
+                       
+                       return content;
+               }
+               
+               @Override
+               protected void okPressed() {
+                       isInverse = inverseField.getSelection();
+                       
+                       switch (typeIndex) {
+                       case 0: // Property condition
+                               propertyName = propertyNameField.getText();
+                               try {
+                                       String lowerLimitText = lowerLimitField.getText();
+                                       lowerLimit = lowerLimitText.equals("") ? null : Double.valueOf(lowerLimitText);
+                                       String upperLimitText = upperLimitField.getText();
+                                       upperLimit = upperLimitText.equals("") ? null : Double.valueOf(upperLimitText);
+                               }
+                               catch (NumberFormatException e) {
+                                       ErrorDialog.openError(getShell(), "Error", "Invalid numeric value: " + e.getMessage(), new Status(OK, "org.simantics.district.selection.ui", e.getMessage()));
+                                       return;
+                               }
+                               break;
+                       case 1: { // Region condition
+                               int selectionIndex = regionField.getSelectionIndex();
+                               if (selectionIndex < 0) {
+                                       ErrorDialog.openError(getShell(), "Error", "Please select a region", new Status(OK, "org.simantics.district.selection.ui", "No region selection"));
+                                       return;
+                               }
+                               region = regionResources[selectionIndex];
+                               break;
+                       }
+                       case 2: // Route condition
+                               route = routeResources[routeField.getSelectionIndex()];
+                               break;
+                       case 3: // Aggregate condition
+                               isConjunction = operatorField.getSelectionIndex() == 0;
+                               break;
+                       }
+                       
+                       super.okPressed();
+               }
+       
+               protected Condition createCondition() throws DatabaseException {
+                       if (isInverse && !(typeIndex == 3 && !isConjunction)) {
+                               Resource resource0 = createCondition0();
+                               
+                               // Create a negation
+                               Resource resource = Simantics.getSession().syncRequest(new WriteResult<Resource>() {
+                                       @Override
+                                       public Resource perform(WriteGraph graph) throws DatabaseException {
+                                               ElementSelectionResource ES = ElementSelectionResource.getInstance(graph);
+                                               Layer0 L0 = Layer0.getInstance(graph);
+                                               
+                                               Resource r = graph.newResource();
+                                               graph.claim(r, L0.InstanceOf, ES.Negation);
+                                               graph.claim(r, ES.HasSubcondition, resource0);
+                                               return r;
+                                       }
+                               });
+                               
+                               return ElementSelector.getCondition(Simantics.getSession(), resource);
+                       }
+                       else {
+                               return ElementSelector.getCondition(Simantics.getSession(), createCondition0());
+                       }
+               }
+       
+               private Resource createCondition0() throws DatabaseException {
+                       switch (typeIndex) {
+                       case 0: return createPropertyCondition();
+                       case 1: return createRegionCondition();
+                       case 2: return createRouteCondition();
+                       case 3: return createAggregateCondition();
+                       default: throw new IllegalStateException("Invalid condition type code " + typeIndex);
+                       }
+               }
+       
+               private Resource createPropertyCondition() throws DatabaseException {
+                       return Simantics.getSession().syncRequest(new WriteResult<Resource>() {
+                               @Override
+                               public Resource perform(WriteGraph graph) throws DatabaseException {
+                                       ElementSelectionResource ES = ElementSelectionResource.getInstance(graph);
+                                       Layer0 L0 = Layer0.getInstance(graph);
+                                       
+                                       Resource r = graph.newResource();
+                                       graph.claim(r, L0.InstanceOf, ES.PropertyCondition);
+                                       graph.claimLiteral(r, ES.PropertyCondition_HasPropertyName, propertyName);
+                                       if (lowerLimit != null)
+                                               graph.claimLiteral(r, ES.PropertyCondition_HasLowerLimit, L0.Double, lowerLimit);
+                                       if (upperLimit != null)
+                                               graph.claimLiteral(r, ES.PropertyCondition_HasUpperLimit, L0.Double, upperLimit);
+                                       
+                                       return r;
+                               }
+                       });
+               }
+               
+               private Resource createAggregateCondition() throws DatabaseException {
+                       return Simantics.getSession().syncRequest(new WriteResult<Resource>() {
+                               @Override
+                               public Resource perform(WriteGraph graph) throws DatabaseException {
+                                       ElementSelectionResource ES = ElementSelectionResource.getInstance(graph);
+                                       Layer0 L0 = Layer0.getInstance(graph);
+                                       
+                                       Resource r;
+                                       if (existingResource != null) {
+                                               // Reuse existing resource
+                                               r = existingResource;
+                                               // Clear any previous statements
+                                               graph.deny(existingResource);
+                                       }
+                                       else {
+                                               r = graph.newResource();
+                                       }
+                                       
+                                       Resource type;
+                                       type = isConjunction ? ES.Conjunction : isInverse ? ES.Negation : ES.Disjunction;
+                                       
+                                       graph.claim(r, L0.InstanceOf, type);
+                                       for (Condition c : subConditions) {
+                                               graph.claim(r, ES.HasSubcondition, c.resource);
+                                       }
+                                       
+                                       return r;
+                               }
+                       });
+               }
+       
+               private Resource createRouteCondition() throws DatabaseException {
+                       return Simantics.getSession().syncRequest(new WriteResult<Resource>() {
+                               @Override
+                               public Resource perform(WriteGraph graph) throws DatabaseException {
+                                       ElementSelectionResource ES = ElementSelectionResource.getInstance(graph);
+                                       Layer0 L0 = Layer0.getInstance(graph);
+                                       
+                                       Resource r = graph.newResource();
+                                       graph.claim(r, L0.InstanceOf, ES.RouteCondition);
+                                       graph.claim(r, ES.RouteCondition_HasRoute, route);
+                                       return r;
+                               }
+                       });
+               }
+       
+               private Resource createRegionCondition() throws DatabaseException {
+                       return Simantics.getSession().syncRequest(new WriteResult<Resource>() {
+                               @Override
+                               public Resource perform(WriteGraph graph) throws DatabaseException {
+                                       ElementSelectionResource ES = ElementSelectionResource.getInstance(graph);
+                                       Layer0 L0 = Layer0.getInstance(graph);
+                                       
+                                       Resource r = graph.newResource();
+                                       graph.claim(r, L0.InstanceOf, ES.RegionCondition);
+                                       graph.claim(r, ES.RegionCondition_HasRegion, region);
+                                       return r;
+                               }
+                       });
+               }
+
+               private void updateStack() {
+                       typeIndex = typeField.getSelectionIndex();
+                       switch (typeIndex) {
+                       case 0: stack.topControl = propertyPanel; break;
+                       case 1: stack.topControl = regionPanel; break;
+                       case 2: stack.topControl = routePanel; break;
+                       case 3: stack.topControl = aggregatePanel; break;
+                       }
+                       
+                       stackPanel.layout();
+               }
+       }
+}
diff --git a/org.simantics.district.selection.ui/src/org/simantics/district/selection/ui/parts/ElementSelectionView.java b/org.simantics.district.selection.ui/src/org/simantics/district/selection/ui/parts/ElementSelectionView.java
new file mode 100644 (file)
index 0000000..1cddde9
--- /dev/null
@@ -0,0 +1,100 @@
+package org.simantics.district.selection.ui.parts;
+
+import java.util.List;
+
+import javax.annotation.PostConstruct;
+import javax.inject.Inject;
+
+import org.eclipse.e4.ui.di.Focus;
+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.MMenuContribution;
+import org.eclipse.e4.ui.model.application.ui.menu.MMenuFactory;
+import org.eclipse.e4.ui.model.application.ui.menu.MPopupMenu;
+import org.eclipse.e4.ui.model.application.ui.menu.MToolBar;
+import org.eclipse.e4.ui.model.application.ui.menu.MToolBarElement;
+import org.eclipse.e4.ui.services.EMenuService;
+import org.eclipse.e4.ui.workbench.modeling.ESelectionService;
+import org.eclipse.swt.SWT;
+import org.eclipse.swt.widgets.Composite;
+import org.simantics.district.selection.ElementSelector;
+import org.simantics.district.selection.ui.ElementSelectorTableUI;
+import org.slf4j.Logger;
+import org.slf4j.LoggerFactory;
+
+public class ElementSelectionView {
+       private static final String CONTEXT_MENU_ID = "org.simantics.district.selection.ui.contextMenu";
+       private static final String POPUP_ID = "org.simantics.district.selection.ui.selectiontable.popup";
+       private static final String CREATE_NEW_ID = "org.simantics.district.selection.ui.command.createNewSelection";
+       private static final String CREATE_NEW_LABEL = "Create New Element Selection Query";
+       private static final String CREATE_NEW_ICON = "platform:/plugin/com.famfamfam.silk/icons/add.png";
+       private static final String EDIT_ID = "org.simantics.district.selection.ui.command.editElementSelector";
+       private static final String EDIT_LABEL = "Edit Element Selection Query";
+       private static final String EDIT_ICON = "platform:/plugin/com.famfamfam.silk/icons/pencil.png";
+       private static final String DELETE_ID = "org.simantics.district.selection.ui.command.deleteElementSelector";
+       private static final String DELETE_LABEL = "Delete Element Selection Query";
+       private static final String DELETE_ICON = "platform:/plugin/com.famfamfam.silk/icons/cross.png";
+       
+       private static final Logger LOGGER = LoggerFactory.getLogger(ElementSelectionView.class);
+
+       private ElementSelectorTableUI table;
+
+       @Inject
+       private ESelectionService selectionService;
+
+       @Inject
+       public void init(MPart part, MApplication app) {
+               // Command is contributed via fragment
+               MHandledToolItem createItem = MMenuFactory.INSTANCE.createHandledToolItem();
+               createItem.setCommand(app.getCommand(CREATE_NEW_ID));
+               createItem.setLabel(CREATE_NEW_LABEL);
+               createItem.setIconURI(CREATE_NEW_ICON);
+               MHandledToolItem editItem = MMenuFactory.INSTANCE.createHandledToolItem();
+               editItem.setCommand(app.getCommand(EDIT_ID));
+               editItem.setLabel(EDIT_LABEL);
+               editItem.setIconURI(EDIT_ICON);
+               MHandledToolItem deleteItem = MMenuFactory.INSTANCE.createHandledToolItem();
+               deleteItem.setCommand(app.getCommand(DELETE_ID));
+               deleteItem.setLabel(DELETE_LABEL);
+               deleteItem.setIconURI(DELETE_ICON);
+               
+               MToolBar toolBar = MMenuFactory.INSTANCE.createToolBar();
+               toolBar.setToBeRendered(true);
+               
+               List<MToolBarElement> children = toolBar.getChildren();
+               children.add(createItem);
+               children.add(editItem);
+               children.add(deleteItem);
+               
+               part.setToolbar(toolBar);
+
+               MPopupMenu popupMenu = MMenuFactory.INSTANCE.createPopupMenu();
+               popupMenu.setElementId(POPUP_ID);
+
+               List<MMenuContribution> menuContributions = app.getMenuContributions();
+               for (MMenuContribution menuContribution : menuContributions) {
+                       if (CONTEXT_MENU_ID.equals(menuContribution.getParentId())) {
+                               popupMenu.getChildren().addAll(menuContribution.getChildren());
+                       }
+               }
+
+               part.getMenus().add(popupMenu);
+       }
+
+       @PostConstruct
+       public void createPartControl(Composite parent, EMenuService menuService) {
+               table = new ElementSelectorTableUI(selectionService, parent, SWT.BORDER);
+               if (!(menuService.registerContextMenu(this.table.getTree(), POPUP_ID)))
+                       LOGGER.warn("Could not register context menu {}", POPUP_ID);
+       }
+
+       @Focus
+       public void setFocus() {
+               table.setFocus();
+       }
+
+       public ElementSelector getSelectedItem() {
+               return table.getSelectedItem();
+       }
+}
index d7f0261ef11e2a8ce4cc90b8c2d9e311f8bb8a43..fb07142bc714247bbed0721a0f8eaae6345e7b08 100644 (file)
Binary files a/org.simantics.district.selection/graph.tg and b/org.simantics.district.selection/graph.tg differ
index 5a510864d5c72e397e136c40880d272904d9504e..55e019585124ebb8bfd587373a5dd297050918ba 100644 (file)
@@ -8,6 +8,8 @@ ES = <http://www.simantics.org/ElementSelection-1.0> : L0.Ontology
     L0.Ontology.global true
     L0.HasResourceClass "org.simantics.district.selection.ElementSelectionResource"
 
+ES.SelectionLibrary <T L0.Library
+
 ES.Selection <T L0.Entity
     >-- ES.Selection.HasGenerator --> ES.Generator <R L0.HasProperty : L0.FunctionalRelation
     >-- ES.Selection.HasCondition --> ES.Condition <R L0.HasProperty : L0.FunctionalRelation
index e75673dda009b16f0c0db6151d6d940dba406351..7cd7a7fac4292b337c6bd4b2d0d99a4f6b44f579 100644 (file)
@@ -47,6 +47,7 @@ public class ElementSelectionResource {
     public final Resource RouteCondition_HasRoute;
     public final Resource RouteCondition_HasRoute_Inverse;
     public final Resource Selection;
+    public final Resource SelectionLibrary;
     public final Resource Selection_HasCondition;
     public final Resource Selection_HasCondition_Inverse;
     public final Resource Selection_HasGenerator;
@@ -96,6 +97,7 @@ public class ElementSelectionResource {
         public static final String RouteCondition_HasRoute = "http://www.simantics.org/ElementSelection-1.0/RouteCondition/HasRoute";
         public static final String RouteCondition_HasRoute_Inverse = "http://www.simantics.org/ElementSelection-1.0/RouteCondition/HasRoute/Inverse";
         public static final String Selection = "http://www.simantics.org/ElementSelection-1.0/Selection";
+        public static final String SelectionLibrary = "http://www.simantics.org/ElementSelection-1.0/SelectionLibrary";
         public static final String Selection_HasCondition = "http://www.simantics.org/ElementSelection-1.0/Selection/HasCondition";
         public static final String Selection_HasCondition_Inverse = "http://www.simantics.org/ElementSelection-1.0/Selection/HasCondition/Inverse";
         public static final String Selection_HasGenerator = "http://www.simantics.org/ElementSelection-1.0/Selection/HasGenerator";
@@ -155,6 +157,7 @@ public class ElementSelectionResource {
         RouteCondition_HasRoute = getResourceOrNull(graph, URIs.RouteCondition_HasRoute);
         RouteCondition_HasRoute_Inverse = getResourceOrNull(graph, URIs.RouteCondition_HasRoute_Inverse);
         Selection = getResourceOrNull(graph, URIs.Selection);
+        SelectionLibrary = getResourceOrNull(graph, URIs.SelectionLibrary);
         Selection_HasCondition = getResourceOrNull(graph, URIs.Selection_HasCondition);
         Selection_HasCondition_Inverse = getResourceOrNull(graph, URIs.Selection_HasCondition_Inverse);
         Selection_HasGenerator = getResourceOrNull(graph, URIs.Selection_HasGenerator);
diff --git a/org.simantics.district.selection/src/org/simantics/district/selection/ElementSelectionUtils.java b/org.simantics.district.selection/src/org/simantics/district/selection/ElementSelectionUtils.java
new file mode 100644 (file)
index 0000000..4075e9b
--- /dev/null
@@ -0,0 +1,43 @@
+package org.simantics.district.selection;
+
+import java.util.List;
+import java.util.UUID;
+
+import org.simantics.Simantics;
+import org.simantics.databoard.Bindings;
+import org.simantics.db.RequestProcessor;
+import org.simantics.db.Resource;
+import org.simantics.db.WriteGraph;
+import org.simantics.db.exception.DatabaseException;
+import org.simantics.db.layer0.QueryIndexUtils;
+import org.simantics.db.layer0.request.PossibleActiveModel;
+import org.simantics.db.request.WriteResult;
+import org.simantics.layer0.Layer0;
+
+public class ElementSelectionUtils {
+       public static Resource ensureSelectionLibrary(RequestProcessor graph) throws DatabaseException {
+               return graph.syncRequest(new WriteResult<Resource>() {
+                       @Override
+                       public Resource perform(WriteGraph graph) throws DatabaseException {
+                               ElementSelectionResource ES = ElementSelectionResource.getInstance(graph);
+                               
+                               Resource model = graph.sync(new PossibleActiveModel(Simantics.getProjectResource()));
+                               if (model == null) return null;
+                               
+                               List<Resource> libs = QueryIndexUtils.searchByTypeShallow(graph, model, ES.SelectionLibrary);
+                               if (libs.isEmpty()) {
+                                       Resource lib = graph.newResource();
+                                       Layer0 L0 = Layer0.getInstance(graph);
+                                       graph.claim(lib, L0.InstanceOf, ES.SelectionLibrary);
+                                       graph.claim(model, L0.ConsistsOf, lib);
+                                       graph.claimLiteral(lib, L0.HasName, L0.String, UUID.randomUUID().toString(), Bindings.STRING);
+                                       return lib;
+                               }
+                               else {
+                                       return libs.get(0);
+                               }
+                       }
+               });
+       }
+
+}
index 7d429e4e04a5669667b11d35d678851f0af06874..74eaa227096a06b011e230d47089328c210bacab 100644 (file)
@@ -3,22 +3,29 @@ package org.simantics.district.selection;
 import java.awt.geom.Path2D;
 import java.util.ArrayList;
 import java.util.Collection;
+import java.util.Collections;
+import java.util.HashMap;
 import java.util.HashSet;
 import java.util.List;
+import java.util.Map;
 import java.util.Set;
 
 import org.simantics.Simantics;
 import org.simantics.db.ReadGraph;
+import org.simantics.db.RequestProcessor;
 import org.simantics.db.Resource;
 import org.simantics.db.common.request.ResourceRead;
 import org.simantics.db.common.utils.ListUtils;
 import org.simantics.db.exception.DatabaseException;
+import org.simantics.db.layer0.QueryIndexUtils;
 import org.simantics.db.layer0.request.ActiveModels;
 import org.simantics.db.layer0.request.ActiveRuns;
 import org.simantics.db.layer0.request.Configuration;
+import org.simantics.db.layer0.request.PossibleActiveModel;
 import org.simantics.db.layer0.variable.RVI;
 import org.simantics.db.layer0.variable.Variable;
 import org.simantics.db.layer0.variable.Variables;
+import org.simantics.db.request.Read;
 import org.simantics.diagram.stubs.DiagramResource;
 import org.simantics.district.network.ontology.DistrictNetworkResource;
 import org.simantics.district.region.ontology.DiagramRegionsResource;
@@ -35,7 +42,7 @@ import org.slf4j.LoggerFactory;
 public class ElementSelector {
        String name;
        String expression;
-       Integer count;
+       
        Resource resource;
        
        Generator generator;
@@ -51,6 +58,8 @@ public class ElementSelector {
        static DiagramResource DIA;
        static DistrictNetworkResource DN;
        
+       private static Logger LOGGER = LoggerFactory.getLogger(ElementSelector.class);
+       
        ElementSelector(ReadGraph graph, Resource resource) throws DatabaseException {
                super();
                
@@ -62,32 +71,169 @@ public class ElementSelector {
                DN = DistrictNetworkResource.getInstance(graph);
                
                this.resource = resource;
-               this.count = -1;
                try {
                        this.name = graph.getRelatedValue(resource, L0.HasLabel);
-                       this.expression = buildExpression(graph, resource);
+                       this.expression = getExpression(graph, resource);
                } catch (DatabaseException e) {
                        LOG.error("Error reading element selector", e);
                        throw e;
                }
        }
        
-       public static ElementSelector getSelector(ReadGraph graph, Resource resource) throws DatabaseException {
+       /**
+        * Instantiate an element selector object for an ES.Selection resource.
+        */
+       public static ElementSelector getSelector(RequestProcessor graph, Resource resource) throws DatabaseException {
                return graph.syncRequest(new ElementSelectorQuery(resource));
        }
+       
+       /**
+        * Get an SQL-like textual description of an ES.Selection resource.
+        */
+       public static String getExpression(RequestProcessor graph, Resource resource) throws DatabaseException {
+               return graph.syncRequest(new SelectionExpressionRequest(resource));
+       }
+
+       /**
+        * Get a Java object representation of an ES.Condition resource.
+        */
+       public static Condition getCondition(RequestProcessor graph, Resource condition) throws DatabaseException {
+               return graph.syncRequest(new SelectionConditionRequest(condition));
+       }
 
+       /**
+        * Get the name of the element selector.
+        */
        public String getName() {
                return name;
        }
 
+       /**
+        * Get a textual SQL-like description of the element selector.
+        */
        public String getExpression() {
                return expression;
        }
 
+       /**
+        * Get the resource that this selector represents. 
+        */
        public Resource getResource() {
                return resource;
        }
 
+       /**
+        * Get the generator component. Use {@link #buildSelection(ReadGraph)} to make it available first.
+        */
+       public Generator getGenerator() {
+               return generator;
+       }
+
+       /**
+        * Get the selector component. Use {@link #buildSelection(ReadGraph)} to make it available first.
+        */
+       public Selector getSelector() {
+               return selector;
+       }
+
+       /**
+        * Get the condition component. Use {@link #buildSelection(ReadGraph)} to make it available first.
+        */
+       public Condition getCondition() {
+               return condition;
+       }
+
+       /**
+        * 
+        * @param graph
+        * @throws DatabaseException
+        */
+       public void buildSelection(ReadGraph graph) throws DatabaseException {
+               Resource selector = graph.getSingleObject(resource, ES.Selection_HasSelector);
+               Resource generator = graph.getSingleObject(resource, ES.Selection_HasGenerator);
+               Resource condition = graph.getPossibleObject(resource, ES.Selection_HasCondition);
+               
+               this.selector = buildSelector(graph, selector);
+               this.generator = buildGenerator(graph, generator);
+               this.condition = buildCondition(graph, condition);
+       }
+
+       public static Map<Resource, String> findDiagrams() {
+               try {
+                       return Simantics.getSession().syncRequest(new Read<Map<Resource, String>>() {
+                               @Override
+                               public Map<Resource, String> perform(ReadGraph graph) throws DatabaseException {
+                                       Map<Resource, String> result = new HashMap<>();
+                                       Resource model = graph.syncRequest(new PossibleActiveModel(Simantics.getProjectResource()));
+                                       List<Resource> composites = QueryIndexUtils.searchByType(graph, model, StructuralResource2.getInstance(graph).Composite);
+                                       for (Resource r : composites) {
+                                               // Get diagram
+                                               Resource diagram = graph.getPossibleObject(r, ModelingResources.getInstance(graph).CompositeToDiagram);
+                                               if (diagram == null) continue;
+                                               
+                                               // Filter out user component diagrams
+                                               Resource parent = graph.getPossibleObject(r, Layer0.getInstance(graph).PartOf);
+                                               if (parent == null || graph.isInheritedFrom(parent, StructuralResource2.getInstance(graph).Component))
+                                                       continue;
+                                               
+                                               result.put(r, graph.getRelatedValue(r, Layer0.getInstance(graph).HasName));
+                                       }
+                                       
+                                       return result;
+                               }
+                       });
+               } catch (DatabaseException e) {
+                       LOGGER.error("Query for model diagrams failed", e);
+                       return Collections.emptyMap();
+               }
+       }
+
+       public static Variable getVariableForElement(ReadGraph graph, Resource element) throws DatabaseException {
+               Resource component = graph.getPossibleObject(element, MOD.ElementToComponent);
+               if (component != null) {
+                       Variable var = Variables.getVariable(graph, component);
+                       RVI realRvi = var.getRVI(graph);
+                       
+                       for (Resource activeModel : graph.syncRequest(new ActiveModels(Simantics.getProjectResource()))) {
+                               for (Variable run : graph.syncRequest(new ActiveRuns(activeModel))) {
+                                       Variable v = realRvi.resolvePossible(graph, run);
+                                       if (v != null) {
+                                               return v;
+                                       }
+                               }
+                               Variable configuration = Variables.getPossibleConfigurationContext(graph, activeModel);
+                               if (configuration != null) {
+                                       Variable v = realRvi.resolvePossible(graph, configuration);
+                                       if (v != null) {
+                                               return v;
+                                       }
+                               }
+                       }
+               }
+               
+               return null;
+       }
+
+       public static Double getPropertyValue(ReadGraph graph, Resource element, String propertyName) {
+               try {
+                       Variable v = getVariableForElement(graph, element);
+                       if (v != null) {
+                               Number value = v.getPossiblePropertyValue(graph, propertyName);
+                               if (value != null)
+                                       return value.doubleValue();
+                       }
+                       
+                       // No property found - try possible mapped element property as well
+                       Resource mappedElement = graph.getPossibleObject(element, DN.MappedComponent);
+                       if (mappedElement != null)
+                               return getPropertyValue(graph, mappedElement, propertyName);
+               }
+               catch (DatabaseException e) {
+               }
+               
+               return null;
+       }
+
        public List<Resource> selectElementsFrom(ReadGraph graph, Resource model) throws DatabaseException {
                if (selector == null) {
                        buildSelection(graph);
@@ -147,19 +293,20 @@ public class ElementSelector {
                        String propertyName = graph.getRelatedValue(resource, ES.PropertyCondition_HasPropertyName);
                        Double lowerLimit = graph.getPossibleRelatedValue(resource, ES.PropertyCondition_HasLowerLimit);
                        Double upperLimit = graph.getPossibleRelatedValue(resource, ES.PropertyCondition_HasUpperLimit);
-                       cond = new PropertyCondition(propertyName, lowerLimit, upperLimit);
+                       cond = new PropertyCondition(resource, propertyName, lowerLimit, upperLimit);
                }
                else if (graph.isInstanceOf(resource, ES.RegionCondition)) {
                        DiagramRegionsResource DR = DiagramRegionsResource.getInstance(graph);
-                       double[] region = graph.getRelatedValue(graph.getSingleObject(resource, ES.RegionCondition_HasRegion), DR.Region_area);
-                       cond = new RegionCondition(region);
+                       Resource regionResource = graph.getSingleObject(resource, ES.RegionCondition_HasRegion);
+                       double[] region = graph.getRelatedValue(regionResource, DR.Region_area);
+                       cond = new RegionCondition(resource, regionResource, region);
                }
                else if (graph.isInstanceOf(resource, ES.RouteCondition)) {
-                       Set<Resource> routePoints = new HashSet<>(ListUtils.toList(graph, graph.getSingleObject(resource, ES.RouteCondition_HasRoute)));
-                       cond = new RouteCondition(routePoints);
+                       Resource routeResource = graph.getSingleObject(resource, ES.RouteCondition_HasRoute);
+                       Set<Resource> routePoints = new HashSet<>(ListUtils.toList(graph, routeResource));
+                       cond = new RouteCondition(resource, routeResource, routePoints);
                }
                else if (graph.isInstanceOf(resource, ES.AggregateCondition)) {
-                       boolean isDisjunction = graph.isInstanceOf(resource, ES.Disjunction);
                        Collection<Resource> conditionResources = graph.getObjects(resource, ES.HasSubcondition);
                        List<Condition> conditions = new ArrayList<>(conditionResources.size());
                        for (Resource c : conditionResources) {
@@ -175,23 +322,22 @@ public class ElementSelector {
                        else
                                throw new IllegalArgumentException("Unknown aggreate condition type " + graph.getURI(graph.getSingleType(resource)));
                                
-                       cond = new AggregateCondition(type, conditions);
+                       cond = new AggregateCondition(resource, type, conditions);
                }
                else {
                        throw new IllegalArgumentException("Unknown condition type " + graph.getURI(graph.getSingleType(resource)));
                }
                
-               cond.isInverse = graph.hasStatement(resource, ES.Condition_IsInverse, resource);
                return cond;
        }
 
        private static String buildExpression(ReadGraph graph, Resource r) throws DatabaseException {
                if (graph.isInstanceOf(r, ES.Selection)) {
-                       String exp = "select " + buildExpression(graph, graph.getSingleObject(r, ES.Selection_HasSelector)) + 
-                                    " from " + buildExpression(graph, graph.getSingleObject(r, ES.Selection_HasGenerator));
+                       String exp = "select " + getExpression(graph, graph.getSingleObject(r, ES.Selection_HasSelector)) + 
+                                    " from " + getExpression(graph, graph.getSingleObject(r, ES.Selection_HasGenerator));
                        
                        Resource cond = graph.getPossibleObject(r, ES.Selection_HasCondition);
-                       return cond != null ? exp + " where {" + buildExpression(graph, cond) + "}" : exp;
+                       return cond != null ? exp + " where {" + getExpression(graph, cond) + "}" : exp;
                }
                else if (graph.isInstanceOf(r, ES.Condition)) {
                        if (graph.isInstanceOf(r, ES.PropertyCondition)) {
@@ -212,7 +358,7 @@ public class ElementSelector {
                                List<String> exps = new ArrayList<>();
                                Collection<Resource> objects = graph.getObjects(r, ES.HasSubcondition);
                                for (Resource c : objects) {
-                                       String exp = buildExpression(graph, c);
+                                       String exp = getExpression(graph, c);
                                        exps.add(objects.size() > 1 ? "{" + exp + "}" : exp);
                                }
                                String result = String.join(op, exps);
@@ -300,16 +446,6 @@ public class ElementSelector {
                return result;
        }
 
-       private void buildSelection(ReadGraph graph) throws DatabaseException {
-               Resource selector = graph.getSingleObject(resource, ES.Selection_HasSelector);
-               Resource generator = graph.getSingleObject(resource, ES.Selection_HasGenerator);
-               Resource condition = graph.getPossibleObject(resource, ES.Selection_HasCondition);
-               
-               this.selector = buildSelector(graph, selector);
-               this.generator = buildGenerator(graph, generator);
-               this.condition = buildCondition(graph, condition);
-       }
-
        private Collection<Resource> filterElementsFrom(ReadGraph graph, Collection<Resource> elements) throws DatabaseException {
                if (condition == null) return elements;
                
@@ -326,59 +462,13 @@ public class ElementSelector {
                return selector.select(graph, filterElementsFrom(graph, generator.generate(graph, model)));
        }
        
-       static Variable getVariableForElement(ReadGraph graph, Resource element) throws DatabaseException {
-               Resource component = graph.getPossibleObject(element, MOD.ElementToComponent);
-               if (component != null) {
-                       Variable var = Variables.getVariable(graph, component);
-                       RVI realRvi = var.getRVI(graph);
-                       
-                       for (Resource activeModel : graph.syncRequest(new ActiveModels(Simantics.getProjectResource()))) {
-                               for (Variable run : graph.syncRequest(new ActiveRuns(activeModel))) {
-                                       Variable v = realRvi.resolvePossible(graph, run);
-                                       if (v != null) {
-                                               return v;
-                                       }
-                               }
-                               Variable configuration = Variables.getPossibleConfigurationContext(graph, activeModel);
-                               if (configuration != null) {
-                                       Variable v = realRvi.resolvePossible(graph, configuration);
-                                       if (v != null) {
-                                               return v;
-                                       }
-                               }
-                       }
-               }
-               
-               return null;
-       }
-       
-       static Double getPropertyValue(ReadGraph graph, Resource element, String propertyName) {
-               try {
-                       Variable v = getVariableForElement(graph, element);
-                       if (v != null) {
-                               Number value = v.getPossiblePropertyValue(graph, propertyName);
-                               if (value != null)
-                                       return value.doubleValue();
-                       }
-                       
-                       // No property found - try possible mapped element property as well
-                       Resource mappedElement = graph.getPossibleObject(element, DN.MappedComponent);
-                       if (mappedElement != null)
-                               return getPropertyValue(graph, mappedElement, propertyName);
-               }
-               catch (DatabaseException e) {
-               }
-               
-               return null;
-       }
-       
        // Generators
        
-       static abstract class Generator {
+       public static abstract class Generator {
                abstract Collection<Resource> generate(ReadGraph graph, Resource model) throws DatabaseException;
        }
        
-       static class ModelGenerator extends Generator {
+       public static class ModelGenerator extends Generator {
                @Override
                Collection<Resource> generate(ReadGraph graph, Resource model) throws DatabaseException {
                        Resource conf = graph.syncRequest(new Configuration(model));
@@ -397,8 +487,8 @@ public class ElementSelector {
                }
        }
        
-       static class DiagramGenerator extends Generator {
-               Resource diagram;
+       public static class DiagramGenerator extends Generator {
+               public Resource diagram;
                
                public DiagramGenerator(Resource diagram) {
                        this.diagram = diagram;
@@ -410,12 +500,12 @@ public class ElementSelector {
                }
        }
        
-       static class ExplicitGenerator extends Generator {
+       public static class ExplicitGenerator extends Generator {
                public ExplicitGenerator(Collection<Resource> elements) {
                        this.elements = elements;
                }
 
-               Collection<Resource> elements;
+               public Collection<Resource> elements;
 
                @Override
                Collection<Resource> generate(ReadGraph graph, Resource model) {
@@ -425,11 +515,11 @@ public class ElementSelector {
        
        // Selectors
        
-       static abstract class Selector {
+       public static abstract class Selector {
                abstract Collection<Resource> select(ReadGraph graph, Collection<Resource> elements);
        }
        
-       static class All extends Selector {
+       public static class All extends Selector {
                public All() {
                }
 
@@ -439,16 +529,16 @@ public class ElementSelector {
                }
        }
        
-       static class PropertySelector extends Selector {
+       public static class PropertySelector extends Selector {
                public PropertySelector(boolean smallest, String propertyName, int resultCount) {
                        this.smallest = smallest;
                        this.propertyName = propertyName;
                        this.resultCount = resultCount;
                }
                
-               boolean smallest;
-               String propertyName;
-               int resultCount;
+               public boolean smallest;
+               public String propertyName;
+               public int resultCount;
                
                @SuppressWarnings("unchecked")
                @Override
@@ -486,22 +576,28 @@ public class ElementSelector {
        
        // Conditions
        
-       static abstract class Condition {
-               boolean isInverse;
+       public static abstract class Condition {
+               public Resource resource;
+               
+               Condition(Resource r) {
+                       resource = r;
+               }
+               
                abstract boolean match(ReadGraph graph, Resource r) throws DatabaseException;
        }
        
-       static class PropertyCondition extends Condition {
-               public PropertyCondition(String propertyName, Double lowerLimit, Double upperLimit) {
-                       super();
+       public static class PropertyCondition extends Condition {
+               public PropertyCondition(Resource r, String propertyName, Double lowerLimit, Double upperLimit) {
+                       super(r);
+                       
                        this.propertyName = propertyName;
                        this.lowerLimit = lowerLimit;
                        this.upperLimit = upperLimit;
                }
                
-               String propertyName;
-               Double lowerLimit;
-               Double upperLimit;
+               public String propertyName;
+               public Double lowerLimit;
+               public Double upperLimit;
                
                @Override
                boolean match(ReadGraph graph, Resource r) {
@@ -510,9 +606,9 @@ public class ElementSelector {
                }
        }
        
-       static class RegionCondition extends Condition {
-               public RegionCondition(double[] region) {
-                       super();
+       public static class RegionCondition extends Condition {
+               public RegionCondition(Resource r, Resource regionResoruce, double[] region) {
+                       super(r);
                        this.region = region;
                        
                        Path2D path = new Path2D.Double();
@@ -524,8 +620,10 @@ public class ElementSelector {
                        path.closePath();
 
                        this.path = path;
+                       this.regionResource = regionResoruce;
                }
 
+               public Resource regionResource;
                double[] region;
                Path2D path;
 
@@ -538,12 +636,14 @@ public class ElementSelector {
                } 
        }
        
-       static class RouteCondition extends Condition {
-               public RouteCondition(Set<Resource> routePoints) {
-                       super();
+       public static class RouteCondition extends Condition {
+               public RouteCondition(Resource r, Resource routeResource, Set<Resource> routePoints) {
+                       super(r);
                        this.routePoints = routePoints;
+                       this.routeResource = routeResource;
                }
 
+               public Resource routeResource;
                Set<Resource> routePoints;
 
                @Override
@@ -552,31 +652,17 @@ public class ElementSelector {
                }
        }
        
-       static class DiagramCondition extends Condition {
-               public DiagramCondition(Resource diagram) {
-                       super();
-                       this.diagram = diagram;
-               }
-
-               Resource diagram;
-
-               @Override
-               boolean match(ReadGraph graph, Resource r) throws DatabaseException {
-                       return graph.getSingleObject(r, L0.PartOf).equals(diagram);
-               }
-       }
-       
-       static class AggregateCondition extends Condition {
-               static enum Type { DISJUNCTION, CONJUNCTION, NEGATION };
+       public static class AggregateCondition extends Condition {
+               public static enum Type { DISJUNCTION, CONJUNCTION, NEGATION };
                
-               public AggregateCondition(Type type, List<Condition> conditions) {
-                       super();
+               public AggregateCondition(Resource r, Type type, List<Condition> conditions) {
+                       super(r);
                        this.type = type;
                        this.conditions = conditions;
                }
                
-               Type type;
-               List<Condition> conditions;
+               public Type type;
+               public List<Condition> conditions;
                
                @Override
                boolean match(ReadGraph graph, Resource r) throws DatabaseException {
@@ -610,4 +696,26 @@ public class ElementSelector {
                        return new ElementSelector(graph, resource);
                }
        }
+
+       public final static class SelectionExpressionRequest extends ResourceRead<String> {
+               public SelectionExpressionRequest(Resource condition) {
+                       super(condition);
+               }
+       
+               @Override
+               public String perform(ReadGraph graph) throws DatabaseException {
+                       return ElementSelector.buildExpression(graph, resource);
+               }
+       }
+
+       public static final class SelectionConditionRequest extends ResourceRead<Condition> {
+               public SelectionConditionRequest(Resource resource) {
+                       super(resource);
+               }
+       
+               @Override
+               public Condition perform(ReadGraph graph) throws DatabaseException {
+                       return buildCondition(graph, resource);
+               }
+       }
 }
diff --git a/org.simantics.district.selection/src/org/simantics/district/selection/ElementSelectorTest.java b/org.simantics.district.selection/src/org/simantics/district/selection/ElementSelectorTest.java
new file mode 100644 (file)
index 0000000..9dc771a
--- /dev/null
@@ -0,0 +1,283 @@
+package org.simantics.district.selection;
+
+//import static org.junit.Assert.*;
+//
+//import org.eclipse.core.runtime.NullProgressMonitor;
+//import org.junit.AfterClass;
+//import org.junit.Before;
+//import org.junit.BeforeClass;
+//import org.junit.Test;
+//import org.simantics.Simantics;
+//import org.simantics.db.Resource;
+//import org.simantics.db.WriteGraph;
+//import org.simantics.db.common.request.WriteRequest;
+//import org.simantics.db.exception.DatabaseException;
+//import org.simantics.db.testing.common.AcornTests;
+//import org.simantics.layer0.Layer0;
+//
+//public class ElementSelectorTest {
+//
+//     Resource selection;
+//     Resource generator;
+//     Resource selector;
+//     
+//     @Before
+//     public void setUp() throws DatabaseException {
+//             Simantics.sync(new WriteRequest() {
+//                     @Override
+//                     public void perform(WriteGraph graph) throws DatabaseException {
+//                             Layer0 L0 = Layer0.getInstance(graph);
+//                             ElementSelectionResource ES = ElementSelectionResource.getInstance(graph);
+//                             
+//                             selection = graph.newResource();
+//                             graph.claim(selection, L0.InstanceOf, ES.Selection);
+//                             graph.claimLiteral(selection, L0.HasLabel, L0.String, "My Selection");
+//                             generator = graph.newResource();
+//                             graph.claim(selection, ES.Selection_HasGenerator, generator);
+//                             selector = graph.newResource();
+//                             graph.claim(selection, ES.Selection_HasSelector, selector);
+//                     }
+//             });
+//     }
+//     
+//     @BeforeClass
+//     public static void initialize0() throws Exception {
+//             AcornTests.newSimanticsWorkspace(null, null);
+//     }
+//
+//     @AfterClass
+//     public static void deinitialize0() throws Exception {
+//             Simantics.shutdown(new NullProgressMonitor());
+//     }
+//     
+//     @Test
+//     public void testExpressionGeneration1() throws DatabaseException {
+//             
+//             Simantics.sync(new WriteRequest() {
+//                     @Override
+//                     public void perform(WriteGraph graph) throws DatabaseException {
+//                             Layer0 L0 = Layer0.getInstance(graph);
+//                             ElementSelectionResource ES = ElementSelectionResource.getInstance(graph);
+//                             
+//                             graph.claim(generator, L0.InstanceOf, ES.Generator_Model);
+//                             graph.claim(selector, L0.InstanceOf, ES.Selector_NHighest);
+//                             graph.claimLiteral(selector, ES.PropertySelector_HasSelectionPropertyName, L0.String, "myProperty");
+//                             graph.claimLiteral(selector, ES.PropertySelector_HasResultCount, L0.Integer, 10);
+//                     }
+//             });
+//             
+//             ElementSelector sel = Simantics.sync(new ElementSelector.ElementSelectorQuery(selection));
+//             assertNotNull(sel);
+//             String expression = sel.expression;
+//             assertEquals("select top 10 of myProperty from model", expression);
+//             System.err.println(expression);
+//     }
+//     
+//     @Test
+//     public void testExpressionGeneration2() throws DatabaseException {
+//             
+//             Simantics.sync(new WriteRequest() {
+//                     @Override
+//                     public void perform(WriteGraph graph) throws DatabaseException {
+//                             Layer0 L0 = Layer0.getInstance(graph);
+//                             ElementSelectionResource ES = ElementSelectionResource.getInstance(graph);
+//                             
+//                             graph.claim(generator, L0.InstanceOf, ES.Generator_Model);
+//                             graph.claim(selector, L0.InstanceOf, ES.Selector_NLowest);
+//                             graph.claimLiteral(selector, ES.PropertySelector_HasSelectionPropertyName, L0.String, "criteria1");
+//                             graph.claimLiteral(selector, ES.PropertySelector_HasResultCount, L0.Integer, 20);
+//                     }
+//             });
+//             
+//             ElementSelector sel = Simantics.sync(new ElementSelector.ElementSelectorQuery(selection));
+//             assertNotNull(sel);
+//             String expression = sel.expression;
+//             assertEquals("select bottom 20 of criteria1 from model", expression);
+//             System.err.println(expression);
+//     }
+//     
+//     @Test
+//     public void testExpressionGeneration3() throws DatabaseException {
+//             
+//             Simantics.sync(new WriteRequest() {
+//                     @Override
+//                     public void perform(WriteGraph graph) throws DatabaseException {
+//                             Layer0 L0 = Layer0.getInstance(graph);
+//                             ElementSelectionResource ES = ElementSelectionResource.getInstance(graph);
+//                             
+//                             graph.claim(generator, L0.InstanceOf, ES.Generator_Model);
+//                             graph.claim(selector, L0.InstanceOf, ES.Selector_All);
+//                     }
+//             });
+//             
+//             ElementSelector sel = Simantics.sync(new ElementSelector.ElementSelectorQuery(selection));
+//             assertNotNull(sel);
+//             String expression = sel.expression;
+//             assertEquals("select all from model", expression);
+//             System.err.println(expression);
+//     }
+//     
+//     @Test
+//     public void testExpressionGeneration4() throws DatabaseException {
+//             
+//             Simantics.sync(new WriteRequest() {
+//                     @Override
+//                     public void perform(WriteGraph graph) throws DatabaseException {
+//                             Layer0 L0 = Layer0.getInstance(graph);
+//                             ElementSelectionResource ES = ElementSelectionResource.getInstance(graph);
+//                             
+//                             graph.claim(generator, L0.InstanceOf, ES.Generator_Model);
+//                             graph.claim(selector, L0.InstanceOf, ES.Selector_All);
+//                             Resource condition = graph.newResource();
+//                             graph.claim(condition, L0.InstanceOf, ES.PropertyCondition);
+//                             graph.claim(condition, ES.Selection_HasCondition_Inverse, selection);
+//                             graph.claimLiteral(condition, ES.PropertyCondition_HasPropertyName, L0.String, "myProperty");
+//                             graph.claimLiteral(condition, ES.PropertyCondition_HasLowerLimit, L0.Double, 20.0);
+//                     }
+//             });
+//             
+//             ElementSelector sel = Simantics.sync(new ElementSelector.ElementSelectorQuery(selection));
+//             assertNotNull(sel);
+//             String expression = sel.expression;
+//             assertEquals("select all from model where {20.0 < myProperty}", expression);
+//             System.err.println(expression);
+//     }
+//     
+//     @Test
+//     public void testExpressionGeneration5() throws DatabaseException {
+//             
+//             Simantics.sync(new WriteRequest() {
+//                     @Override
+//                     public void perform(WriteGraph graph) throws DatabaseException {
+//                             Layer0 L0 = Layer0.getInstance(graph);
+//                             ElementSelectionResource ES = ElementSelectionResource.getInstance(graph);
+//                             
+//                             graph.claim(generator, L0.InstanceOf, ES.Generator_Model);
+//                             graph.claim(selector, L0.InstanceOf, ES.Selector_NLowest);
+//                             graph.claimLiteral(selector, ES.PropertySelector_HasSelectionPropertyName, L0.String, "criteria1");
+//                             graph.claimLiteral(selector, ES.PropertySelector_HasResultCount, L0.Integer, 20);
+//                             Resource condition = graph.newResource();
+//                             graph.claim(condition, L0.InstanceOf, ES.PropertyCondition);
+//                             graph.claim(condition, ES.Selection_HasCondition_Inverse, selection);
+//                             graph.claimLiteral(condition, ES.PropertyCondition_HasPropertyName, L0.String, "myProperty");
+//                             graph.claimLiteral(condition, ES.PropertyCondition_HasLowerLimit, L0.Double, 20.0);
+//                     }
+//             });
+//             
+//             ElementSelector sel = Simantics.sync(new ElementSelector.ElementSelectorQuery(selection));
+//             assertNotNull(sel);
+//             String expression = sel.expression;
+//             assertEquals("select bottom 20 of criteria1 from model where {20.0 < myProperty}", expression);
+//             System.err.println(expression);
+//     }
+//     
+//     @Test
+//     public void testExpressionGeneration6() throws DatabaseException {
+//             
+//             Simantics.sync(new WriteRequest() {
+//                     @Override
+//                     public void perform(WriteGraph graph) throws DatabaseException {
+//                             Layer0 L0 = Layer0.getInstance(graph);
+//                             ElementSelectionResource ES = ElementSelectionResource.getInstance(graph);
+//                             
+//                             graph.claim(generator, L0.InstanceOf, ES.Generator_Model);
+//                             graph.claim(selector, L0.InstanceOf, ES.Selector_NLowest);
+//                             graph.claimLiteral(selector, ES.PropertySelector_HasSelectionPropertyName, L0.String, "criteria1");
+//                             graph.claimLiteral(selector, ES.PropertySelector_HasResultCount, L0.Integer, 20);
+//                             Resource condition = graph.newResource();
+//                             graph.claim(condition, L0.InstanceOf, ES.Disjunction);
+//                             graph.claim(condition, ES.Selection_HasCondition_Inverse, selection);
+//                             Resource condition1 = graph.newResource();
+//                             graph.claim(condition1, L0.InstanceOf, ES.PropertyCondition);
+//                             graph.claim(condition1, ES.HasSubcondition_Inverse, condition);
+//                             graph.claimLiteral(condition1, ES.PropertyCondition_HasPropertyName, L0.String, "myProperty1");
+//                             graph.claimLiteral(condition1, ES.PropertyCondition_HasLowerLimit, L0.Double, 20.0);
+//                             Resource condition2 = graph.newResource();
+//                             graph.claim(condition2, L0.InstanceOf, ES.PropertyCondition);
+//                             graph.claim(condition2, ES.HasSubcondition_Inverse, condition);
+//                             graph.claimLiteral(condition2, ES.PropertyCondition_HasPropertyName, L0.String, "myProperty2");
+//                             graph.claimLiteral(condition2, ES.PropertyCondition_HasLowerLimit, L0.Double, 0.0);
+//                             graph.claimLiteral(condition2, ES.PropertyCondition_HasUpperLimit, L0.Double, 100.0);
+//                     }
+//             });
+//             
+//             ElementSelector sel = Simantics.sync(new ElementSelector.ElementSelectorQuery(selection));
+//             assertNotNull(sel);
+//             String expression = sel.expression;
+//             assertEquals("select bottom 20 of criteria1 from model where {{20.0 < myProperty1} or {0.0 < myProperty2 < 100.0}}", expression);
+//             System.err.println(expression);
+//     }
+//     
+//     @Test
+//     public void testExpressionGeneration7() throws DatabaseException {
+//             
+//             Simantics.sync(new WriteRequest() {
+//                     @Override
+//                     public void perform(WriteGraph graph) throws DatabaseException {
+//                             Layer0 L0 = Layer0.getInstance(graph);
+//                             ElementSelectionResource ES = ElementSelectionResource.getInstance(graph);
+//                             
+//                             graph.claim(generator, L0.InstanceOf, ES.Generator_Explicit);
+//                             for (int i = 0; i < 15; i++) {
+//                                     graph.claim(generator, ES.Generator_HasSelectedElement, graph.newResource());
+//                             }
+//                             graph.claim(selector, L0.InstanceOf, ES.Selector_All);
+//                             Resource condition = graph.newResource();
+//                             graph.claim(condition, L0.InstanceOf, ES.Disjunction);
+//                             graph.claim(condition, ES.Selection_HasCondition_Inverse, selection);
+//                             Resource condition1 = graph.newResource();
+//                             graph.claim(condition1, L0.InstanceOf, ES.PropertyCondition);
+//                             graph.claim(condition1, ES.HasSubcondition_Inverse, condition);
+//                             graph.claimLiteral(condition1, ES.PropertyCondition_HasPropertyName, L0.String, "myProperty1");
+//                             graph.claimLiteral(condition1, ES.PropertyCondition_HasLowerLimit, L0.Double, 20.0);
+//                             Resource condition2 = graph.newResource();
+//                             graph.claim(condition2, L0.InstanceOf, ES.PropertyCondition);
+//                             graph.claim(condition2, ES.HasSubcondition_Inverse, condition);
+//                             graph.claimLiteral(condition2, ES.PropertyCondition_HasPropertyName, L0.String, "myProperty2");
+//                             graph.claimLiteral(condition2, ES.PropertyCondition_HasLowerLimit, L0.Double, 0.0);
+//                             graph.claimLiteral(condition2, ES.PropertyCondition_HasUpperLimit, L0.Double, 100.0);
+//                     }
+//             });
+//             
+//             ElementSelector sel = Simantics.sync(new ElementSelector.ElementSelectorQuery(selection));
+//             assertNotNull(sel);
+//             String expression = sel.expression;
+//             assertEquals("select all from <list of 15 elements> where {{20.0 < myProperty1} or {0.0 < myProperty2 < 100.0}}", expression);
+//             System.err.println(expression);
+//     }
+//     
+//     @Test
+//     public void testExpressionGeneration8() throws DatabaseException {
+//             
+//             Simantics.sync(new WriteRequest() {
+//                     @Override
+//                     public void perform(WriteGraph graph) throws DatabaseException {
+//                             Layer0 L0 = Layer0.getInstance(graph);
+//                             ElementSelectionResource ES = ElementSelectionResource.getInstance(graph);
+//                             
+//                             graph.claim(generator, L0.InstanceOf, ES.Generator_Model);
+//                             graph.claim(selector, L0.InstanceOf, ES.Selector_All);
+//                             Resource condition = graph.newResource();
+//                             graph.claim(condition, L0.InstanceOf, ES.Conjunction);
+//                             graph.claim(condition, ES.Selection_HasCondition_Inverse, selection);
+//                             Resource condition1 = graph.newResource();
+//                             graph.claim(condition1, L0.InstanceOf, ES.PropertyCondition);
+//                             graph.claim(condition1, ES.HasSubcondition_Inverse, condition);
+//                             graph.claimLiteral(condition1, ES.PropertyCondition_HasPropertyName, L0.String, "myProperty1");
+//                             Resource condition2 = graph.newResource();
+//                             graph.claim(condition2, L0.InstanceOf, ES.RegionCondition);
+//                             graph.claim(condition2, ES.HasSubcondition_Inverse, condition);
+//                             Resource region = graph.newResource();
+//                             graph.claim(condition2, ES.RegionCondition_HasRegion, region);
+//                             graph.claim(region, L0.InstanceOf, L0.Entity);
+//                             graph.claimLiteral(region, L0.HasLabel, L0.String, "region1");
+//                     }
+//             });
+//             
+//             ElementSelector sel = Simantics.sync(new ElementSelector.ElementSelectorQuery(selection));
+//             assertNotNull(sel);
+//             String expression = sel.expression;
+//             assertEquals("select all from model where {{has property myProperty1} and {in region region1}}", expression);
+//             System.err.println(expression);
+//     }
+//}
index d4f241b240e24abc59be73492c54eb81bca45322..d563d637eb4eba3ddbe470f3ebff97572a52e73c 100644 (file)
          version="0.0.0"
          unpack="false"/>
 
+   <plugin
+         id="org.simantics.district.selection.ui"
+         download-size="0"
+         install-size="0"
+         version="0.0.0"
+         unpack="false"/>
+
 </feature>
diff --git a/pom.xml b/pom.xml
index 50cb2866f8026feb71141d10465dd6db442ac721..ac49440b0c09b244b53c186142862f1ac910b4c0 100644 (file)
--- a/pom.xml
+++ b/pom.xml
@@ -96,6 +96,7 @@
                <module>org.simantics.district.route.ontology</module>
                <module>org.simantics.district.route.ui</module>
                <module>org.simantics.district.selection</module>
+               <module>org.simantics.district.selection.ui</module>
                <module>org.simantics.maps.server</module>
                <module>org.simantics.maps.server.ui</module>