+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();
+ }
+ }
+}