1 package org.simantics.district.selection.ui.parts;
3 import java.util.ArrayList;
4 import java.util.Collection;
5 import java.util.Collections;
6 import java.util.Comparator;
7 import java.util.HashMap;
8 import java.util.HashSet;
12 import java.util.UUID;
13 import java.util.function.Consumer;
15 import javax.inject.Inject;
17 import org.eclipse.jface.dialogs.Dialog;
18 import org.eclipse.jface.dialogs.MessageDialog;
19 import org.eclipse.jface.layout.GridDataFactory;
20 import org.eclipse.jface.layout.GridLayoutFactory;
21 import org.eclipse.jface.layout.RowDataFactory;
22 import org.eclipse.jface.layout.RowLayoutFactory;
23 import org.eclipse.jface.resource.ImageDescriptor;
24 import org.eclipse.jface.resource.JFaceResources;
25 import org.eclipse.jface.resource.LocalResourceManager;
26 import org.eclipse.jface.resource.ResourceLocator;
27 import org.eclipse.jface.window.Window;
28 import org.eclipse.nebula.widgets.tablecombo.TableCombo;
29 import org.eclipse.swt.SWT;
30 import org.eclipse.swt.events.SelectionAdapter;
31 import org.eclipse.swt.events.SelectionEvent;
32 import org.eclipse.swt.widgets.Button;
33 import org.eclipse.swt.widgets.Combo;
34 import org.eclipse.swt.widgets.Composite;
35 import org.eclipse.swt.widgets.Control;
36 import org.eclipse.swt.widgets.Display;
37 import org.eclipse.swt.widgets.Label;
38 import org.eclipse.swt.widgets.Shell;
39 import org.eclipse.swt.widgets.Table;
40 import org.eclipse.swt.widgets.TableItem;
41 import org.eclipse.swt.widgets.Text;
42 import org.eclipse.swt.widgets.Widget;
43 import org.simantics.Simantics;
44 import org.simantics.databoard.Bindings;
45 import org.simantics.db.ReadGraph;
46 import org.simantics.db.Resource;
47 import org.simantics.db.WriteGraph;
48 import org.simantics.db.common.request.IndexRoot;
49 import org.simantics.db.common.request.ReadRequest;
50 import org.simantics.db.common.request.WriteRequest;
51 import org.simantics.db.exception.DatabaseException;
52 import org.simantics.db.exception.RuntimeDatabaseException;
53 import org.simantics.db.layer0.QueryIndexUtils;
54 import org.simantics.db.layer0.request.ActiveModels;
55 import org.simantics.db.layer0.request.PropertyInfo;
56 import org.simantics.db.layer0.request.PropertyInfoRequest;
57 import org.simantics.db.layer0.util.Layer0Utils;
58 import org.simantics.diagram.stubs.DiagramResource;
59 import org.simantics.diagram.stubs.G2DResource;
60 import org.simantics.district.network.ontology.DistrictNetworkResource;
61 import org.simantics.district.region.ontology.DiagramRegionsResource;
62 import org.simantics.district.route.ontology.RouteResource;
63 import org.simantics.district.selection.ElementSelectionResource;
64 import org.simantics.district.selection.ElementSelectionUtils;
65 import org.simantics.district.selection.ElementSelector;
66 import org.simantics.district.selection.ElementSelector.AggregateCondition;
67 import org.simantics.district.selection.ElementSelector.AggregateCondition.Type;
68 import org.simantics.district.selection.ElementSelector.All;
69 import org.simantics.district.selection.ElementSelector.Condition;
70 import org.simantics.district.selection.ElementSelector.DiagramGenerator;
71 import org.simantics.district.selection.ElementSelector.ExplicitGenerator;
72 import org.simantics.district.selection.ElementSelector.Generator;
73 import org.simantics.district.selection.ElementSelector.ModelGenerator;
74 import org.simantics.district.selection.ElementSelector.PropertyCondition;
75 import org.simantics.district.selection.ElementSelector.PropertySelector;
76 import org.simantics.district.selection.ElementSelector.RegionCondition;
77 import org.simantics.district.selection.ElementSelector.RouteCondition;
78 import org.simantics.district.selection.ElementSelector.Selector;
79 import org.simantics.district.selection.ui.ElementSelectionTools;
80 import org.simantics.layer0.Layer0;
81 import org.simantics.layer0.utils.direct.GraphUtils;
82 import org.simantics.modeling.ModelingResources;
83 import org.simantics.structural.stubs.StructuralResource2;
84 import org.simantics.utils.datastructures.Arrays;
85 import org.simantics.utils.datastructures.Pair;
86 import org.simantics.utils.ui.color.Color;
87 import org.slf4j.Logger;
88 import org.slf4j.LoggerFactory;
90 public class EditSelectorDialog extends Dialog {
92 private static final RowLayoutFactory ROW_LAYOUT = RowLayoutFactory.fillDefaults().wrap(false);
93 private static final ImageDescriptor CROSS_IMAGE = ResourceLocator.imageDescriptorFromBundle("com.famfamfam.silk", "icons/cross.png").get();
94 private static final ImageDescriptor PLUS_IMAGE = ResourceLocator.imageDescriptorFromBundle("com.famfamfam.silk", "icons/add.png").get();
96 private static Logger LOGGER = LoggerFactory.getLogger(EditSelectorDialog.class);
98 private ElementSelector elementSelector;
100 // Currently selected elements
101 Collection<Resource> currentSelection;
103 // Data for comboboxes
104 private List<Resource> diagrams;
105 private ArrayList<String> diagramNames;
107 private String[] regionNames;
108 private Resource[] regionResources;
110 private String[] routeNames;
111 private Resource[] routeResources;
113 private List<Resource> componentTypes;
114 private List<String> componentTypeNames;
116 private List<String> propertyNames;
117 private List<String> propertyLabels;
119 private Composite conditionPanel;
122 private int generatorIndex;
123 private Combo sourceField;
126 private Text nameField;
129 private TableCombo colorCombo;
131 private Resource diagram;
132 private Combo diagramField;
134 private int selectorIndex;
135 private Combo selectorField;
137 private Resource componentType;
138 private Combo componentTypeField;
140 private String propertyName;
141 private Combo propertyField;
143 private int numberOfItems;
146 private Condition condition;
148 // Dialog area component
149 private Composite content;
151 private int diagramIndex;
153 private LocalResourceManager resourceManager;
155 static class ValidationException extends Exception {
156 private static final long serialVersionUID = 1L;
158 public ValidationException(String message) {
163 // Function type for updating condition objects with optional validation
164 static interface Updater {
165 // If 'validate' is true, a runtime exception may be thrown for invalid values
166 void update(boolean validate) throws ValidationException;
169 final static Updater NULL_UPDATE = validate -> {};
171 // Called to read values from controls into conditions
172 Updater updater = NULL_UPDATE;
174 public EditSelectorDialog(Shell shell, ElementSelector elementSelector, Collection<Resource> currentSelection) {
177 this.elementSelector = elementSelector;
178 if (elementSelector != null) {
180 Simantics.getSession().sync(new ReadRequest() {
182 public void run(ReadGraph graph) throws DatabaseException {
183 elementSelector.buildSelection(graph);
186 } catch (DatabaseException e1) {
187 LOGGER.error("Failed to read element selector resource " + elementSelector.getResource(), e1);
188 throw new RuntimeDatabaseException(e1);
192 this.currentSelection = currentSelection;
194 Map<Resource, String> diagramMap = ElementSelector.findDiagrams();
195 diagrams = new ArrayList<Resource>(diagramMap.size());
196 diagramNames = new ArrayList<String>(diagramMap.size());
197 diagramMap.entrySet()
199 .sorted(Comparator.comparing(e -> e.getValue()))
200 .forEachOrdered(e -> {
201 diagrams.add(e.getKey());
202 diagramNames.add(e.getValue());
205 name = elementSelector != null ? elementSelector.getName() : "";
213 if (elementSelector != null) {
214 float[] color = elementSelector.getColor();
216 this.color = ElementSelectionTools.literalToColor(color);
219 Generator generator = elementSelector.getGenerator();
220 if (generator instanceof ModelGenerator) {
223 else if (generator instanceof DiagramGenerator) {
225 diagram = ((DiagramGenerator)generator).diagram;
227 else if (generator instanceof ExplicitGenerator) {
231 throw new IllegalStateException("Unknown generator type " + generator.getClass().getName());
234 Selector selector = elementSelector.getSelector();
235 if (selector instanceof All) {
238 else if (selector instanceof PropertySelector) {
239 PropertySelector propertySelector = (PropertySelector)selector;
240 selectorIndex = propertySelector.smallest ? 1 : 2;
241 propertyName = propertySelector.propertyName;
242 numberOfItems = propertySelector.resultCount;
245 throw new IllegalStateException("Unknwon selector type " + selector.getClass().getName());
248 condition = elementSelector.getCondition();
250 componentType = elementSelector.getSelector().componentType;
253 readRegions(diagram);
255 readComponentTypes();
256 updatePropertyList();
259 private void readComponentTypes() {
261 Simantics.getSession().syncRequest(new ReadRequest() {
263 public void run(ReadGraph graph) throws DatabaseException {
264 Layer0 L0 = Layer0.getInstance(graph);
265 List<Resource> types = findComponentTypes(graph);
267 componentTypes = new ArrayList<>(types.size() + 1);
268 componentTypeNames = new ArrayList<>(types.size() + 1);
270 componentTypes.add(null);
271 componentTypeNames.add("Any type");
272 componentTypes.addAll(types);
273 for (Resource t : types) {
274 componentTypeNames.add(graph.getValue2(t, L0.HasName));
278 } catch (DatabaseException e) {
279 LOGGER.error("Failed to read district component types", e);
283 private void readRoutes() {
284 final Map<Resource, String> routes = new HashMap<>();
287 Simantics.getSession().syncRequest(new ReadRequest() {
289 public void run(ReadGraph graph) throws DatabaseException {
290 Layer0 L0 = Layer0.getInstance(graph);
291 RouteResource ROUTE = RouteResource.getInstance(graph);
293 Resource model = ActiveModels.getPossibleActiveModel(graph, Simantics.getProjectResource());
294 List<Resource> routeCollection = QueryIndexUtils.searchByType(graph, model, ROUTE.Route);
295 for (Resource r : routeCollection) {
296 String name = graph.getRelatedValue(r, L0.HasLabel);
301 } catch (DatabaseException e) {
302 LOGGER.error("Failed to read routes in the model", e);
305 routeNames = routes.values().toArray(new String[routes.size()]);
306 routeResources = routes.keySet().toArray(new Resource[routes.size()]);
309 private void readRegions(Resource diagram) {
310 final Map<Resource, String> regions = new HashMap<>();
313 Simantics.getSession().syncRequest(new ReadRequest() {
315 public void run(ReadGraph graph) throws DatabaseException {
316 Layer0 L0 = Layer0.getInstance(graph);
317 ModelingResources MOD = ModelingResources.getInstance(graph);
318 DiagramRegionsResource DR = DiagramRegionsResource.getInstance(graph);
320 // If a specific diagram is given, use that
321 Collection<Resource> ds = diagram != null ? Collections.singleton(diagram) : diagrams;
323 for (Resource composite : ds) {
324 Resource diagram = graph.getSingleObject(composite, MOD.CompositeToDiagram);
325 for (Resource r : graph.getObjects(diagram, DR.hasRegion)) {
326 if (!graph.isInstanceOf(r, DR.Region))
328 String name = graph.getRelatedValue(r, L0.HasLabel);
329 regions.put(r, name);
334 } catch (DatabaseException e) {
335 LOGGER.error("Failed to read regions in the model", e);
338 regionNames = regions.values().toArray(new String[regions.size()]);
339 regionResources = regions.keySet().toArray(new Resource[regions.size()]);
342 private void updateDialog() {
344 updater.update(false);
345 } catch (ValidationException e) {
346 // Should not happend with argument false
350 updater = updateConditionPanel();
352 content.layout(true, true);
357 protected void okPressed() {
359 generatorIndex = sourceField.getSelectionIndex();
360 if (generatorIndex == 1) {
361 int selectionIndex = diagramField.getSelectionIndex();
362 if (selectionIndex < 0) {
363 diagramField.setFocus();
364 throw new ValidationException("Please select a diagram");
367 diagram = diagrams.get(selectionIndex);
370 name = nameField.getText();
371 if (name.isEmpty()) {
372 nameField.setFocus();
373 throw new ValidationException("Please enter a name");
377 int index = colorCombo.getSelectionIndex();
379 color = new Color(colorCombo.getTable().getItem(index).getBackground().getRGB());
383 componentType = componentTypes.get(componentTypeField.getSelectionIndex());
385 selectorIndex = selectorField.getSelectionIndex();
386 if (selectorIndex != 0) {
387 int propertyIndex = propertyField.getSelectionIndex();
388 propertyName = propertyIndex >= 0 ? propertyNames.get(propertyIndex) : propertyField.getText();
389 if (propertyName.isEmpty()) {
390 propertyField.setFocus();
391 throw new ValidationException("Please select a property");
395 // Try to parse number of items
396 if (useNumberOfItems()) {
398 numberOfItems = Integer.parseInt(nField.getText());
399 if (numberOfItems <= 0) {
402 throw new ValidationException("Number of elements must be positive");
404 } catch (NumberFormatException e) {
407 throw new ValidationException("Please enter a valid number of elements");
411 // To to update condition definitions
412 updater.update(true);
413 } catch (ValidationException e) {
414 MessageDialog.openError(this.getShell(), "Missing data", e.getMessage());
421 public void writeSelection() throws DatabaseException {
422 Simantics.getSession().syncRequest(new WriteRequest() {
424 public void perform(WriteGraph graph) throws DatabaseException {
425 Layer0 L0 = Layer0.getInstance(graph);
426 ElementSelectionResource ES = ElementSelectionResource.getInstance(graph);
427 ModelingResources MOD = ModelingResources.getInstance(graph);
428 DiagramResource DIA = DiagramResource.getInstance(graph);
430 graph.markUndoPoint();
431 Layer0Utils.addCommentMetadata(graph, "Created new element selection");
433 Resource lib = ElementSelectionUtils.ensureSelectionLibrary(graph);
437 if (elementSelector != null) {
438 selection = elementSelector.getResource();
439 graph.deny(selection);
442 selection = graph.newResource();
445 graph.claim(selection, L0.InstanceOf, ES.Selection);
446 graph.claimLiteral(selection, L0.HasName, L0.String, UUID.randomUUID().toString());
447 graph.claimLiteral(selection, L0.HasLabel, L0.String, name);
448 graph.claim(selection, L0.PartOf, lib);
451 float[] value = ElementSelectionTools.colorToLiteral(color);
452 graph.claimLiteral(selection, ES.Selection_HasHighlightColor, G2DResource.getInstance(graph).Color, value, Bindings.FLOAT_ARRAY);
456 Resource generator = graph.newResource();
457 Resource generatorType;
458 switch (generatorIndex) {
460 generatorType = ES.Generator_Model;
463 generatorType = ES.Generator_Diagram;
464 Resource composite = graph.getPossibleObject(diagram, MOD.DiagramToComposite);
465 graph.claim(generator, ES.Generator_HasDiagram, composite != null ? composite : diagram);
468 generatorType = ES.Generator_Explicit;
469 for (Resource r : currentSelection) {
471 if (graph.isInstanceOf(r, DIA.Connection))
473 if (!graph.isInstanceOf(r, DIA.Element)) {
474 if (!graph.hasStatement(r, MOD.ComponentToElement))
477 r = graph.getPossibleObject(r, MOD.ComponentToElement);
482 graph.claim(generator, ES.Generator_HasSelectedElement, r);
485 default: throw new IllegalStateException("Invalid source index " + generatorIndex);
487 graph.claim(generator, L0.InstanceOf, generatorType);
488 graph.claim(selection, ES.Selection_HasGenerator, generator);
491 Resource selector = graph.newResource();
492 Resource selectorType;
493 switch (selectorIndex) {
494 case 0: selectorType = ES.Selector_All; break;
495 case 1: selectorType = ES.Selector_NLowest; break;
496 case 2: selectorType = ES.Selector_NHighest; break;
497 default: throw new IllegalStateException("Invalid selector index " + selectorIndex);
499 graph.claim(selector, L0.InstanceOf, selectorType);
500 graph.claim(selection, ES.Selection_HasSelector, selector);
501 graph.deny(selector, ES.Selector_HasMapping);
502 if (componentType != null)
503 graph.claim(selector, ES.Selector_HasMapping, componentType);
505 if (selectorIndex > 0) {
506 graph.claimLiteral(selector, ES.PropertySelector_HasSelectionPropertyName, L0.String, propertyName);
507 graph.claimLiteral(selector, ES.PropertySelector_HasResultCount, L0.Integer, numberOfItems);
511 if (condition != null) {
512 Resource conditionResource = condition.update(graph);
513 graph.claim(selection, ES.Selection_HasCondition, conditionResource);
519 private boolean isDiagramFieldVisible() {
520 return generatorIndex == 1;
523 private boolean useNumberOfItems() {
524 return selectorIndex != 0;
528 protected Control createDialogArea(Composite parent) {
529 this.resourceManager = new LocalResourceManager(JFaceResources.getResources(), parent);
532 getShell().setText("Edit element selector");
534 content = new Composite(parent, SWT.NONE);
535 GridLayoutFactory.swtDefaults().numColumns(2).applyTo(content);
538 Label nameLabel = new Label(content, SWT.NONE);
539 nameLabel.setText("Name");
540 GridDataFactory.swtDefaults().applyTo(nameLabel);
542 nameField = new Text(content, SWT.BORDER);
543 nameField.setEditable(true);
544 nameField.setText(name);
545 GridDataFactory.swtDefaults().hint(200, SWT.DEFAULT).applyTo(nameField);
548 Label label = new Label(content, SWT.NONE);
549 GridDataFactory.swtDefaults().applyTo(label);
550 label.setText("Highlight color");
552 colorCombo = new TableCombo(content, SWT.BORDER | SWT.READ_ONLY);
553 GridDataFactory.swtDefaults().hint(80, SWT.DEFAULT).applyTo(colorCombo);
554 Table colorTable = colorCombo.getTable();
555 colorCombo.getTextControl().setEnabled(false);
556 Display display = getShell().getDisplay();
557 colorTable.setFont(display.getSystemFont());
558 createColorItem(colorTable, "Red", SWT.COLOR_RED);
559 createColorItem(colorTable, "Green", SWT.COLOR_GREEN);
560 createColorItem(colorTable, "Blue", SWT.COLOR_BLUE);
561 createColorItem(colorTable, "Yellow", SWT.COLOR_YELLOW);
562 createColorItem(colorTable, "Cyan", SWT.COLOR_CYAN);
563 createColorItem(colorTable, "Magenta", SWT.COLOR_MAGENTA);
564 TableItem other = new TableItem(colorTable, SWT.NONE);
565 other.setText("Other...");
569 for (i = 0; i < 6; i++) {
570 if (colorTable.getItem(i).getBackground().getRGB().equals(color.getRgb())) {
571 colorCombo.select(i);
577 other.setBackground(resourceManager.createColor(color.getRgb()));
578 colorCombo.select(6);
582 colorCombo.addSelectionListener(new SelectionAdapter() {
584 public void widgetSelected(SelectionEvent e) {
585 int index = colorCombo.getSelectionIndex();
587 org.simantics.utils.ui.color.ColorDialog colorDialog = new org.simantics.utils.ui.workbench.dialogs.ColorDialog(getParentShell());
588 int result = colorDialog.open();
589 if (result == Window.OK) {
590 other.setBackground(resourceManager.createColor(colorDialog.getColor().getRgb()));
591 colorCombo.clearSelection();
592 colorCombo.select(6);
600 Label selectorLabel = new Label(content, SWT.NONE);
601 selectorLabel.setText("Select");
602 GridDataFactory.swtDefaults().applyTo(selectorLabel);
604 Composite selectorComposite = new Composite(content, SWT.NONE);
605 GridDataFactory.swtDefaults().applyTo(selectorComposite);
606 RowLayoutFactory.fillDefaults().applyTo(selectorComposite);
608 nField = new Text(selectorComposite, SWT.BORDER);
609 RowDataFactory.swtDefaults().hint(40, SWT.DEFAULT).applyTo(nField);
610 if (useNumberOfItems())
611 nField.setText(Integer.toString(numberOfItems));
612 nField.setEnabled(useNumberOfItems());
614 componentTypeField = new Combo(selectorComposite, SWT.READ_ONLY);
615 RowDataFactory.swtDefaults().applyTo(componentTypeField);
616 componentTypeField.setItems(componentTypeNames.toArray(new String[] {}));
618 int index = componentTypes.indexOf(componentType);
619 componentTypeField.select(index >= 0 ? index : 0);
622 // Update property selection controls when component type changes
623 componentTypeField.addSelectionListener(new SelectionAdapter() {
625 public void widgetSelected(SelectionEvent e) {
626 int index = componentTypeField.getSelectionIndex();
627 componentType = index >= 0 ? componentTypes.get(index) : null;
628 updatePropertyList();
629 propertyField.setItems(propertyLabels.toArray(new String[] {}));
635 new Label(selectorComposite, SWT.NONE).setText("with");
637 selectorField = new Combo(selectorComposite, SWT.BORDER | SWT.READ_ONLY);
638 selectorField.setItems("All", "Lowest", "Highest");
639 selectorField.select(selectorIndex);
640 RowDataFactory.swtDefaults().hint(40, SWT.DEFAULT).applyTo(selectorField);
642 propertyField = new Combo(selectorComposite, SWT.NONE);
643 RowDataFactory.swtDefaults().hint(120, SWT.DEFAULT).applyTo(propertyField);
644 propertyField.setItems(propertyLabels.toArray(new String[] {}));
646 int index = propertyName != null ? propertyNames.indexOf(propertyName) : -1;
648 propertyField.select(index);
650 propertyField.setText(propertyName != null ? propertyName : "");
652 propertyField.setEnabled(useNumberOfItems());
654 selectorField.addSelectionListener(new SelectionAdapter() {
656 public void widgetSelected(SelectionEvent e) {
657 selectorIndex = selectorField.getSelectionIndex();
659 boolean enable = useNumberOfItems();
660 nField.setEnabled(enable);
661 propertyField.setEnabled(enable);
663 nField.setText(enable ? Integer.toString(numberOfItems) : "");
668 Label sourceLabel = new Label(content, SWT.NONE);
669 sourceLabel.setText("from");
670 GridDataFactory.swtDefaults().align(SWT.BEGINNING, SWT.CENTER).applyTo(sourceLabel);
672 Composite sourceComposite = new Composite(content, SWT.NONE);
673 GridDataFactory.swtDefaults().applyTo(sourceComposite);
674 RowLayoutFactory.fillDefaults().applyTo(sourceComposite);
676 sourceField = new Combo(sourceComposite, SWT.BORDER | SWT.READ_ONLY);
677 RowDataFactory.swtDefaults().applyTo(sourceField);
678 sourceField.setItems("Whole model", "Diagram", "Current selection");
679 sourceField.select(generatorIndex);
681 diagramField = new Combo(sourceComposite, SWT.BORDER | SWT.READ_ONLY);
682 RowDataFactory.swtDefaults().hint(120, SWT.DEFAULT).applyTo(diagramField);
683 diagramField.setItems(diagramNames.toArray(new String[diagramNames.size()]));
684 diagramField.setEnabled(isDiagramFieldVisible());
686 diagramIndex = diagram != null ? diagrams.indexOf(diagram) : -1;
687 diagramField.select(diagramIndex);
689 sourceField.addSelectionListener(new SelectionAdapter() {
691 public void widgetSelected(SelectionEvent e) {
692 generatorIndex = sourceField.getSelectionIndex();
693 boolean enabled = isDiagramFieldVisible();
695 diagramIndex = diagramField.getSelectionIndex();
696 diagramField.clearSelection();
698 if (diagramIndex >= 0)
699 diagramField.select(diagramIndex);
701 diagramField.clearSelection();
703 diagramField.setEnabled(enabled);
705 // Refresh list of regions for current diagram
706 diagram = enabled ? (diagramIndex >= 0 ? diagrams.get(diagramIndex) : null) : null;
707 readRegions(diagram);
712 sourceField.select(generatorIndex);
715 label = new Label(content, SWT.NONE);
716 GridDataFactory.swtDefaults().align(SWT.BEGINNING, SWT.CENTER).applyTo(label);
717 label.setText("where");
719 conditionPanel = new Composite(content, SWT.NONE);
720 GridDataFactory.swtDefaults().minSize(400, SWT.DEFAULT).grab(true, false).applyTo(conditionPanel);
721 GridLayoutFactory.fillDefaults().numColumns(2).applyTo(conditionPanel);
723 updater = updateConditionPanel();
728 private TableItem createColorItem(Table colorTable, String name, int color) {
729 TableItem item = new TableItem(colorTable, SWT.BORDER);
731 item.setBackground(colorTable.getDisplay().getSystemColor(color));
735 private Updater updateConditionPanel() {
737 for (Widget c : conditionPanel.getChildren())
740 return createConditionPanelFor(conditionPanel, condition, cond -> condition = cond);
743 private Updater createConditionPanelFor(final Composite parent, final Condition condition, final Consumer<Condition> consumer) {
744 // Create new contents
745 Button notCheck = new Button(parent, SWT.CHECK);
746 GridDataFactory.swtDefaults().align(SWT.BEGINNING, SWT.BEGINNING).applyTo(notCheck);
747 notCheck.setText("not");
748 notCheck.setSelection(condition != null && condition.isInverse);
750 Composite conditionComp = new Composite(parent, SWT.NONE);
751 GridDataFactory.fillDefaults().applyTo(conditionComp);
753 Combo typeCombo = new Combo(conditionComp, SWT.BORDER | SWT.READ_ONLY);
763 typeCombo.addSelectionListener(new ConditionTypeSelectionListener(typeCombo, consumer, condition));
765 final Updater updater;
766 if (condition instanceof PropertyCondition) {
768 updater = createPropertyConditionPanel(conditionComp, (PropertyCondition)condition);
769 } else if (condition instanceof RegionCondition) {
771 updater = createRegionConditionPanel(conditionComp, (RegionCondition)condition);
772 } else if (condition instanceof RouteCondition) {
774 updater = createRouteConditionPanel(conditionComp, (RouteCondition)condition);
775 } else if (condition instanceof AggregateCondition) {
776 AggregateCondition cond = (AggregateCondition) condition;
777 typeCombo.select(cond.type.equals(Type.CONJUNCTION) ? 4 : 5);
778 updater = createAggregateConditionPanel(conditionComp, cond);
780 ROW_LAYOUT.applyTo(conditionComp);
781 notCheck.setEnabled(false);
787 updater.update(validate);
788 condition.isInverse = notCheck.getSelection();
792 private final class ConditionTypeSelectionListener extends SelectionAdapter {
793 private final Combo typeCombo;
794 private final Consumer<Condition> consumer;
795 private final Condition finalCondition;
797 private ConditionTypeSelectionListener(Combo typeCombo, Consumer<Condition> consumer, Condition finalCondition) {
798 this.typeCombo = typeCombo;
799 this.consumer = consumer;
800 this.finalCondition = finalCondition;
804 public void widgetSelected(SelectionEvent e) {
805 int index = typeCombo.getSelectionIndex();
806 Condition newCondition = finalCondition;
812 newCondition = createPropertyCondition("", null, null);
815 newCondition = createRegionCondition(null);
818 newCondition = createRouteCondition(null);
821 if (newCondition instanceof AggregateCondition)
822 ((AggregateCondition)newCondition).type = Type.CONJUNCTION;
824 newCondition = createAggregateCondition(null, new ArrayList<>(), true, false);
827 if (newCondition instanceof AggregateCondition)
828 ((AggregateCondition)newCondition).type = Type.DISJUNCTION;
830 newCondition = createAggregateCondition(null, new ArrayList<>(), false, false);
834 consumer.accept(newCondition);
840 private Updater createAggregateConditionPanel(Composite conditionComp, AggregateCondition cond) {
841 GridLayoutFactory.fillDefaults().numColumns(2).applyTo(conditionComp);
842 new Label(conditionComp, SWT.NONE); // Eat extra column
844 int n = cond.conditions.size();
845 final Updater[] updates = new Updater[n];
846 for (int i = 0; i < n; i++) {
847 updates[i] = createConditionRowPanel(conditionComp, cond, i);
850 Button addButton = new Button(conditionComp, SWT.PUSH);
851 GridDataFactory.swtDefaults().applyTo(addButton);
852 addButton.setImage(resourceManager.createImage(PLUS_IMAGE));
854 addButton.addSelectionListener(new SelectionAdapter() {
856 public void widgetSelected(SelectionEvent e) {
857 cond.conditions.add(createPropertyCondition("", null, null));
863 for (Updater updater : updates)
864 updater.update(validate);
868 private Updater createConditionRowPanel(Composite parent, AggregateCondition parentCondition, final int i) {
869 GridLayoutFactory conditionLayout = GridLayoutFactory.fillDefaults().numColumns(2);
870 GridDataFactory swtDefaults = GridDataFactory.swtDefaults();
872 Condition c = parentCondition.conditions.get(i);
874 Composite row = new Composite(parent, SWT.NONE);
875 conditionLayout.applyTo(row);
876 swtDefaults.applyTo(row);
878 Consumer<Condition> update = cd -> {
880 parentCondition.conditions.set(i, cd);
882 parentCondition.conditions.remove(i);
885 Updater updater = createConditionPanelFor(row, c, update);
887 Button removeButton = new Button(parent, SWT.PUSH);
888 swtDefaults.align(SWT.BEGINNING, SWT.BEGINNING).applyTo(removeButton);
889 removeButton.setImage(resourceManager.createImage(CROSS_IMAGE));
891 removeButton.addSelectionListener(new SelectionAdapter() {
893 public void widgetSelected(SelectionEvent e) {
894 parentCondition.conditions.remove(i);
902 private Updater createRouteConditionPanel(Composite conditionComp, RouteCondition condition) {
903 ROW_LAYOUT.applyTo(conditionComp);
906 Combo routeCombo = new Combo(conditionComp, SWT.READ_ONLY);
907 RowDataFactory.swtDefaults().hint(120, SWT.DEFAULT).applyTo(routeCombo);
908 routeCombo.setItems(routeNames);
910 // Set current selection
911 int index = Arrays.indexOf(routeResources, condition.routeResource);
913 routeCombo.select(index);
917 int i = routeCombo.getSelectionIndex();
918 if (validate && i < 0) {
919 routeCombo.forceFocus();
920 throw new RuntimeException("Must select a route");
923 condition.routeResource = i >= 0 ? routeResources[i] : null;
927 private Updater createRegionConditionPanel(Composite conditionComp, RegionCondition condition) {
928 ROW_LAYOUT.applyTo(conditionComp);
931 Combo regionCombo = new Combo(conditionComp, SWT.READ_ONLY);
932 RowDataFactory.swtDefaults().hint(120, SWT.DEFAULT).applyTo(regionCombo);
933 regionCombo.setItems(regionNames);
935 // Set current selection
936 int index = Arrays.indexOf(regionResources, condition.regionResource);
938 regionCombo.select(index);
942 int i = regionCombo.getSelectionIndex();
943 if (validate && i < 0) {
944 regionCombo.forceFocus();
945 throw new ValidationException("Please select a region");
948 condition.regionResource = i >= 0 ? regionResources[i] : null;
952 private Updater createPropertyConditionPanel(Composite conditionComp, PropertyCondition condition) {
953 ROW_LAYOUT.applyTo(conditionComp);
955 Text lowerLimitText = new Text(conditionComp, SWT.BORDER);
956 RowDataFactory.swtDefaults().hint(40, SWT.DEFAULT).applyTo(lowerLimitText);
957 lowerLimitText.setText(condition.lowerLimit != null ? Double.toString(condition.lowerLimit) : "");
959 new Label(conditionComp, SWT.NONE).setText("\u2264");
961 Combo propertyNameText = new Combo(conditionComp, SWT.NONE);
962 RowDataFactory.swtDefaults().hint(120, SWT.DEFAULT).applyTo(propertyNameText);
963 propertyNameText.setItems(propertyLabels.toArray(new String[] {}));
964 int index = propertyNames.indexOf(condition.propertyName);
966 propertyNameText.select(index);
968 propertyNameText.setText(condition.propertyName);
970 new Label(conditionComp, SWT.NONE).setText("\u2264");
972 Text upperLimitText = new Text(conditionComp, SWT.BORDER);
973 RowDataFactory.swtDefaults().hint(40, SWT.DEFAULT).applyTo(upperLimitText);
974 upperLimitText.setText(condition.upperLimit != null ? Double.toString(condition.upperLimit) : "");
979 String text = lowerLimitText.getText();
980 condition.lowerLimit = text.isEmpty() ? null : Double.parseDouble(text);
981 } catch (NumberFormatException e) {
983 lowerLimitText.selectAll();
984 lowerLimitText.forceFocus();
985 throw new ValidationException("Please enter a valid lower limit");
990 String text = upperLimitText.getText();
991 condition.upperLimit = text.isEmpty() ? null : Double.parseDouble(text);
992 } catch (NumberFormatException e) {
994 upperLimitText.selectAll();
995 upperLimitText.forceFocus();
996 throw new ValidationException("Please enter a valid upper limit");
1000 int ind = propertyNameText.getSelectionIndex();
1003 name = propertyNames.get(ind);
1005 name = propertyNameText.getText();
1007 if (validate && name.isEmpty()) {
1008 propertyNameText.forceFocus();
1009 throw new ValidationException("Please select a property");
1011 condition.propertyName = name;
1016 private static Condition createPropertyCondition(String propertyName, Double lowerLimit, Double upperLimit) {
1017 return new PropertyCondition(null, propertyName, lowerLimit, upperLimit);
1020 private static Condition createRegionCondition(Resource regionResource) {
1021 return new RegionCondition(null, regionResource, null);
1024 private static Condition createRouteCondition(Resource route) {
1025 return new RouteCondition(null, route, null);
1028 private static Condition createAggregateCondition(Resource existingResource, List<Condition> subConditions, boolean isConjunction, boolean isInverse) {
1029 Type type = isConjunction ? Type.CONJUNCTION : Type.DISJUNCTION;
1030 AggregateCondition condition = new AggregateCondition(null, type, subConditions);
1031 condition.isInverse = isInverse;
1035 static List<Resource> findComponentTypes(ReadGraph graph) throws DatabaseException {
1036 DistrictNetworkResource DN = DistrictNetworkResource.getInstance(graph);
1037 Resource project = Simantics.getProjectResource();
1038 Resource model = ActiveModels.getPossibleActiveModel(graph, project);
1040 return QueryIndexUtils.searchByType(graph, model, DN.Mapping_Base);
1043 void updatePropertyList() {
1044 propertyNames = new ArrayList<>();
1045 propertyLabels = new ArrayList<>();
1047 Collection<Resource> types = componentType != null ? Collections.singleton(componentType) : componentTypes;
1048 Set<Pair<String, String>> properties = new HashSet<>();
1051 Simantics.getSession().syncRequest(new ReadRequest() {
1053 public void run(ReadGraph graph) throws DatabaseException {
1054 Layer0 L0 = Layer0.getInstance(graph);
1056 for (Resource type : types) {
1060 Resource ct = graph.getPossibleObject(type, DistrictNetworkResource.getInstance(graph).Mapping_ComponentType);
1064 if (graph.isInstanceOf(ct, L0.String)) {
1065 Resource indexRoot = graph.syncRequest(new IndexRoot(type));
1066 String name = graph.getValue(ct);
1067 ct = GraphUtils.getPossibleChild(graph, indexRoot, name);
1072 for (Resource prop : graph.getObjects(ct, L0.DomainOf)) {
1073 if (!graph.isInstanceOf(prop, StructuralResource2.getInstance(graph).Property))
1076 // Filter only numeric properties
1077 PropertyInfo info = graph.syncRequest(new PropertyInfoRequest(prop));
1078 if (info != null && info.requiredValueType != null && !isNumericValueType(info.requiredValueType))
1081 String name = graph.getRelatedValue2(prop, L0.HasName);
1082 String label = graph.getPossibleRelatedValue2(prop, L0.HasLabel);
1083 if (label == null) label = name;
1085 properties.add(Pair.make(label, name));
1090 } catch (DatabaseException e) {
1091 LOGGER.error("Failed to read district component properties", e);
1094 propertyNames.clear();
1095 propertyLabels.clear();
1096 properties.stream().sorted(Comparator.comparing(p -> p.first)).forEachOrdered(p -> {
1097 propertyLabels.add(p.first);
1098 propertyNames.add(p.second);
1102 static boolean isNumericValueType(String requiredValueType) {
1103 switch (requiredValueType) {