]> gerrit.simantics Code Review - simantics/district.git/blob - org.simantics.district.network.ui/src/org/simantics/district/network/ui/visualisations/DynamicVisualisationsUI.java
Fixed edge arrows to always be rendered above the edge
[simantics/district.git] / org.simantics.district.network.ui / src / org / simantics / district / network / ui / visualisations / DynamicVisualisationsUI.java
1 package org.simantics.district.network.ui.visualisations;
2
3 import java.util.ArrayList;
4 import java.util.Collection;
5 import java.util.HashMap;
6 import java.util.List;
7 import java.util.Map;
8 import java.util.Map.Entry;
9 import java.util.Objects;
10 import java.util.Optional;
11 import java.util.function.Supplier;
12 import java.util.stream.Collectors;
13 import java.util.stream.Stream;
14
15 import org.eclipse.jface.dialogs.Dialog;
16 import org.eclipse.jface.dialogs.IInputValidator;
17 import org.eclipse.jface.dialogs.InputDialog;
18 import org.eclipse.jface.layout.GridDataFactory;
19 import org.eclipse.jface.layout.GridLayoutFactory;
20 import org.eclipse.swt.SWT;
21 import org.eclipse.swt.custom.ScrolledComposite;
22 import org.eclipse.swt.events.FocusAdapter;
23 import org.eclipse.swt.events.FocusEvent;
24 import org.eclipse.swt.events.KeyAdapter;
25 import org.eclipse.swt.events.KeyEvent;
26 import org.eclipse.swt.events.SelectionAdapter;
27 import org.eclipse.swt.events.SelectionEvent;
28 import org.eclipse.swt.layout.GridData;
29 import org.eclipse.swt.layout.GridLayout;
30 import org.eclipse.swt.widgets.Button;
31 import org.eclipse.swt.widgets.Combo;
32 import org.eclipse.swt.widgets.Composite;
33 import org.eclipse.swt.widgets.Display;
34 import org.eclipse.swt.widgets.Group;
35 import org.eclipse.swt.widgets.Label;
36 import org.eclipse.swt.widgets.Shell;
37 import org.eclipse.swt.widgets.Text;
38 import org.eclipse.swt.widgets.Widget;
39 import org.simantics.Simantics;
40 import org.simantics.db.ReadGraph;
41 import org.simantics.db.Resource;
42 import org.simantics.db.WriteGraph;
43 import org.simantics.db.common.NamedResource;
44 import org.simantics.db.common.request.UniqueRead;
45 import org.simantics.db.common.request.WriteRequest;
46 import org.simantics.db.exception.DatabaseException;
47 import org.simantics.db.layer0.util.RemoverUtil;
48 import org.simantics.db.procedure.Listener;
49 import org.simantics.district.network.profile.ActiveDynamicVisualisationsRequest;
50 import org.simantics.district.network.profile.DynamicVisualisationsRequest;
51 import org.simantics.district.network.visualisations.DynamicVisualisations;
52 import org.simantics.district.network.visualisations.DynamicVisualisationsContributions;
53 import org.simantics.district.network.visualisations.DynamicVisualisationsContributions.DynamicArrowObject;
54 import org.simantics.district.network.visualisations.DynamicVisualisationsContributions.DynamicColoringObject;
55 import org.simantics.district.network.visualisations.DynamicVisualisationsContributions.DynamicSizingObject;
56 import org.simantics.district.network.visualisations.model.ColorBarOptions;
57 import org.simantics.district.network.visualisations.model.ColorBarOptions.ColorBarsLocation;
58 import org.simantics.district.network.visualisations.model.ColorBarOptions.ColorBarsSize;
59 import org.simantics.district.network.visualisations.model.DynamicArrowContribution;
60 import org.simantics.district.network.visualisations.model.DynamicColorContribution;
61 import org.simantics.district.network.visualisations.model.DynamicColorMap;
62 import org.simantics.district.network.visualisations.model.DynamicSizeContribution;
63 import org.simantics.district.network.visualisations.model.DynamicSizeMap;
64 import org.simantics.district.network.visualisations.model.DynamicVisualisation;
65 import org.simantics.district.network.visualisations.model.SizeBarOptions;
66 import org.simantics.district.network.visualisations.model.SizeBarOptions.SizeBarsLocation;
67 import org.simantics.district.network.visualisations.model.SizeBarOptions.SizeBarsSize;
68 import org.simantics.utils.datastructures.Pair;
69 import org.simantics.utils.ui.dialogs.ShowError;
70 import org.slf4j.Logger;
71 import org.slf4j.LoggerFactory;
72
73 public class DynamicVisualisationsUI {
74
75     private static final Logger LOGGER = LoggerFactory.getLogger(DynamicVisualisationsUI.class);
76
77     private Resource parentResource;
78     private VisualisationListener listener;
79     private DynamicVisualisation visualisation;
80
81     private Button showSizeButton;
82     private Button sizeTicksButton;
83     private Button sizeGradientButton;
84     private Combo sizeLocationCombo;
85     private Combo sizeSizeCombo;
86     private Button showColorButton;
87     private Button colorTicksButton;
88     private Button colorGradientButton;
89     private Combo colorLocationCombo;
90     private Combo colorSizeCombo;
91
92     private Combo templateSelectionCombo;
93
94     private List<Supplier<Pair<String, DynamicColorContribution>>> colorSuppliers;
95
96     private Button removeVisualisationTemplateButton;
97
98     private Composite parent;
99
100     private Button disableUpdatesButton;
101     private Button resetVisualisationButton;
102     private Button hoveringVertexEnabledButton;
103     private Button hoveringEdgesEnabledButton;
104
105     private List<Supplier<Pair<String, DynamicArrowContribution>>> edgeArrowSuppliers;
106
107     private Button saveVisualisationTemplateAsButton;
108
109     private Button hideEdgesButton;
110     private Button hidePointsButton;
111     private Button hideConsumersButton;
112     private Button hideProducersButton;
113     private Button hideValvesButton;
114     private Button hidePumpingStationsButton;
115
116     private Button networkBranchesStaticPropertiesButton;
117     private Button pointsStaticPropertiesButton;
118     private Button consumersStaticPropertiesButton;
119     
120     private Button dynamicSymbolsEdgesButton;
121     private Button dynamicSymbolsProducersButton;
122     private Button dynamicSymbolsValvesButton;
123     private Button dynamicSymbolsPumpingStationsButton;
124
125     private Text intervalText;
126
127     public DynamicVisualisationsUI(Composite parent) {
128         this.parent = parent;
129         ScrolledComposite scrolledComposite = new ScrolledComposite(parent, SWT.V_SCROLL | SWT.H_SCROLL);
130         scrolledComposite.setLayout(new GridLayout(1, false));
131         scrolledComposite.setLayoutData(new GridData(SWT.FILL, SWT.FILL, true, true));
132
133         Composite firstContent = new Composite(scrolledComposite, SWT.NONE);
134         firstContent.setLayout(new GridLayout(1, false));
135         firstContent.setLayoutData(new GridData(SWT.FILL, SWT.FILL, true, true));
136         
137         defaultInitializeUI(firstContent);
138         
139         scrolledComposite.setContent(firstContent);
140         scrolledComposite.setExpandHorizontal(true);
141         scrolledComposite.setExpandVertical(true);
142         scrolledComposite.setMinSize(firstContent.computeSize(SWT.DEFAULT, SWT.DEFAULT));
143     }
144
145     private void defaultInitializeUI(Composite parent) {
146         
147         GridDataFactory.fillDefaults().grab(true, true).applyTo(parent);
148         GridLayoutFactory.fillDefaults().numColumns(1).margins(5, 5).applyTo(parent);
149         
150         Composite selectionComposite = new Composite(parent, SWT.NONE);
151         GridDataFactory.fillDefaults().grab(true, false).applyTo(selectionComposite);
152         GridLayoutFactory.fillDefaults().numColumns(2).margins(5, 5).applyTo(selectionComposite);
153         
154         Label templateNameLabel = new Label(selectionComposite, SWT.NONE);
155         templateNameLabel.setText("Visualisation template");
156         templateSelectionCombo = new Combo(selectionComposite, SWT.READ_ONLY);
157         GridDataFactory.fillDefaults().grab(true, false).applyTo(templateSelectionCombo);
158         templateSelectionCombo.addSelectionListener(new SelectionAdapter() {
159             
160             @Override
161             public void widgetSelected(SelectionEvent e) {
162                 String item = templateSelectionCombo.getItem(templateSelectionCombo.getSelectionIndex());
163                 for (NamedResource template : visualisations) {
164                     if (item.equals(template.getName())) {
165                         if (parentResource != null) {
166                             Resource res = parentResource;
167                             Simantics.getSession().asyncRequest(new WriteRequest() {
168                                 
169                                 @Override
170                                 public void perform(WriteGraph graph) throws DatabaseException {
171                                     Resource vf = DynamicVisualisations.getVisualisationFolder(graph, res);
172                                     DynamicVisualisations.setActiveVisualisation(graph, vf, template.getResource());
173                                 }
174                             });
175                             break;
176                         }
177                     }
178                 }
179             }
180         });
181         
182         Composite intervalElementsComposite = new Composite(parent, SWT.NONE);
183         GridDataFactory.fillDefaults().grab(true, false).applyTo(intervalElementsComposite);
184         GridLayoutFactory.fillDefaults().numColumns(1).applyTo(intervalElementsComposite);
185         initializeIntervalElements(intervalElementsComposite);
186         
187         Composite hideElementsComposite = new Composite(parent, SWT.NONE);
188         GridDataFactory.fillDefaults().grab(true, false).applyTo(hideElementsComposite);
189         GridLayoutFactory.fillDefaults().numColumns(1).applyTo(hideElementsComposite);
190         initializeHideElements(hideElementsComposite);
191         
192         Composite staticPropertiesComposite = new Composite(parent, SWT.NONE);
193         GridDataFactory.fillDefaults().grab(true, false).applyTo(staticPropertiesComposite);
194         GridLayoutFactory.fillDefaults().numColumns(1).applyTo(staticPropertiesComposite);
195         initializeStaticProperties(staticPropertiesComposite);
196
197         Composite dynamicSymbolsComposite = new Composite(parent, SWT.NONE);
198         GridDataFactory.fillDefaults().grab(true, false).applyTo(dynamicSymbolsComposite);
199         GridLayoutFactory.fillDefaults().numColumns(1).applyTo(dynamicSymbolsComposite);
200         initializeDynamicSymbols(dynamicSymbolsComposite);
201
202         Composite coloringObjectsComposite = new Composite(parent, SWT.NONE);
203         GridDataFactory.fillDefaults().grab(true, false).applyTo(coloringObjectsComposite);
204         GridLayoutFactory.fillDefaults().numColumns(1).applyTo(coloringObjectsComposite);
205         initializeColoringObjects(coloringObjectsComposite);
206         
207         Composite colorBarsComposite = new Composite(parent, SWT.NONE);
208         GridDataFactory.fillDefaults().grab(true, false).applyTo(colorBarsComposite);
209         GridLayoutFactory.fillDefaults().numColumns(1).applyTo(colorBarsComposite);
210         initializeColorBars(colorBarsComposite);
211         
212         Composite objectSizesComposite = new Composite(parent, SWT.NONE);
213         GridDataFactory.fillDefaults().grab(true, false).applyTo(objectSizesComposite);
214         GridLayoutFactory.fillDefaults().numColumns(1).applyTo(objectSizesComposite);
215         initializeObjectSizes(objectSizesComposite);
216         
217         Composite sizeBarsComposite = new Composite(parent, SWT.NONE);
218         GridDataFactory.fillDefaults().grab(true, false).applyTo(sizeBarsComposite);
219         GridLayoutFactory.fillDefaults().numColumns(1).applyTo(sizeBarsComposite);
220         initializeSizeBars(sizeBarsComposite);
221         
222         Composite edgeArrowsComposite = new Composite(parent, SWT.NONE);
223         GridDataFactory.fillDefaults().grab(true, false).applyTo(edgeArrowsComposite);
224         GridLayoutFactory.fillDefaults().numColumns(1).applyTo(edgeArrowsComposite);
225         initializeEdgeArrows(edgeArrowsComposite);
226         
227         Composite buttonBarsComposite = new Composite(parent, SWT.NONE);
228         GridDataFactory.fillDefaults().grab(true, false).applyTo(buttonBarsComposite);
229         GridLayoutFactory.fillDefaults().numColumns(3).applyTo(buttonBarsComposite);
230         
231         saveVisualisationTemplateAsButton = new Button(buttonBarsComposite, SWT.NONE);
232         saveVisualisationTemplateAsButton.setText("Save as visualisation template");
233         saveVisualisationTemplateAsButton.addSelectionListener(new SelectionAdapter() {
234             
235             @Override
236             public void widgetSelected(SelectionEvent e) {
237                 showSaveVisualisationTemplateDialog(e.widget.getDisplay().getActiveShell());
238             }
239         });
240         
241         removeVisualisationTemplateButton = new Button(buttonBarsComposite, SWT.NONE);
242         removeVisualisationTemplateButton.setText("Remove");
243         removeVisualisationTemplateButton.setEnabled(visualisation != null && visualisation.getVisualisationResource() != null);
244         removeVisualisationTemplateButton.addSelectionListener(new SelectionAdapter() {
245             
246             @Override
247             public void widgetSelected(SelectionEvent e) {
248                 removeVisualisationTemplate(visualisation.getName(), Optional.of(visualisation.getVisualisationResource()));
249             }
250         });
251     }
252     
253     private void initializeIntervalElements(Composite parent) {
254         Group group = new Group(parent, SWT.NONE);
255         group.setText("Interval");
256         GridDataFactory.fillDefaults().grab(true, false).applyTo(group);
257         GridLayoutFactory.fillDefaults().numColumns(6).margins(5, 5).applyTo(group);
258         
259         createIntervalElements(group);
260     }
261     
262     private void createIntervalElements(Composite parent) {
263
264         Label label = new Label(parent, SWT.NONE);
265         label.setText("Generic");
266         intervalText = new Text(parent, SWT.BORDER);
267         addSelectionListener(intervalText);
268         
269         disableUpdatesButton = new Button(parent, SWT.CHECK);
270         disableUpdatesButton.setText("Disable updates");
271         addSelectionListener(disableUpdatesButton);
272
273         resetVisualisationButton = new Button(parent, SWT.CHECK);
274         resetVisualisationButton.setText("Reset Visualisation");
275         addSelectionListener(resetVisualisationButton);
276         
277         hoveringVertexEnabledButton = new Button(parent, SWT.CHECK);
278         hoveringVertexEnabledButton.setText("Vertex Key Variables on Hover");
279         addSelectionListener(hoveringVertexEnabledButton);
280         
281         hoveringEdgesEnabledButton = new Button(parent, SWT.CHECK);
282         hoveringEdgesEnabledButton.setText("Edge Key Variables on Hover");
283         addSelectionListener(hoveringEdgesEnabledButton);
284     }
285     
286     private void initializeHideElements(Composite parent) {
287         Group group = new Group(parent, SWT.NONE);
288         group.setText("Hide Elements");
289         GridDataFactory.fillDefaults().grab(true, false).applyTo(group);
290         GridLayoutFactory.fillDefaults().numColumns(8).margins(5, 5).applyTo(group);
291         
292         createHideElements(group);
293     }
294     
295     private void createHideElements(Composite parent) {
296         
297         hideEdgesButton = new Button(parent, SWT.CHECK);
298         hideEdgesButton.setText("Edges");
299         addSelectionListener(hideEdgesButton);
300         
301         hidePointsButton = new Button(parent, SWT.CHECK);
302         hidePointsButton.setText("Points");
303         addSelectionListener(hidePointsButton);
304         
305         hideConsumersButton = new Button(parent, SWT.CHECK);
306         hideConsumersButton.setText("Consumers");
307         addSelectionListener(hideConsumersButton);
308         
309         hideProducersButton = new Button(parent, SWT.CHECK);
310         hideProducersButton.setText("Producers");
311         addSelectionListener(hideProducersButton);
312         
313         hideValvesButton = new Button(parent, SWT.CHECK);
314         hideValvesButton.setText("Valves");
315         addSelectionListener(hideValvesButton);
316
317         hidePumpingStationsButton = new Button(parent, SWT.CHECK);
318         hidePumpingStationsButton.setText("Pumping Stations");
319         addSelectionListener(hidePumpingStationsButton);
320     }
321
322     private void initializeStaticProperties(Composite parent) {
323         Group group = new Group(parent, SWT.NONE);
324         group.setText("Static Properties");
325         GridDataFactory.fillDefaults().grab(true, false).applyTo(group);
326         GridLayoutFactory.fillDefaults().numColumns(8).margins(5, 5).applyTo(group);
327         
328         createStaticProperties(group);
329     }
330     
331     private void createStaticProperties(Composite parent) {
332         
333         networkBranchesStaticPropertiesButton = new Button(parent, SWT.CHECK);
334         networkBranchesStaticPropertiesButton.setText("Network Branches");
335         addSelectionListener(networkBranchesStaticPropertiesButton);
336         
337         pointsStaticPropertiesButton = new Button(parent, SWT.CHECK);
338         pointsStaticPropertiesButton.setText("Points");
339         addSelectionListener(pointsStaticPropertiesButton);
340         
341         consumersStaticPropertiesButton = new Button(parent, SWT.CHECK);
342         consumersStaticPropertiesButton.setText("Consumers");
343         addSelectionListener(consumersStaticPropertiesButton);
344
345     }
346
347     private void initializeDynamicSymbols(Composite parent) {
348         Group group = new Group(parent, SWT.NONE);
349         group.setText("Dynamic Symbols");
350         GridDataFactory.fillDefaults().grab(true, false).applyTo(group);
351         GridLayoutFactory.fillDefaults().numColumns(8).margins(5, 5).applyTo(group);
352         
353         createDynamicSymbols(group);
354     }
355     
356     private void createDynamicSymbols(Composite parent) {
357         
358         dynamicSymbolsEdgesButton = new Button(parent, SWT.CHECK);
359         dynamicSymbolsEdgesButton.setText("Shutoff Valves in Pipes");
360         addSelectionListener(dynamicSymbolsEdgesButton);
361         
362         dynamicSymbolsProducersButton = new Button(parent, SWT.CHECK);
363         dynamicSymbolsProducersButton.setText("Producers");
364         addSelectionListener(dynamicSymbolsProducersButton);
365         
366         dynamicSymbolsValvesButton = new Button(parent, SWT.CHECK);
367         dynamicSymbolsValvesButton.setText("Valves");
368         addSelectionListener(dynamicSymbolsValvesButton);
369
370         dynamicSymbolsPumpingStationsButton = new Button(parent, SWT.CHECK);
371         dynamicSymbolsPumpingStationsButton.setText("Pumping Stations");
372         addSelectionListener(dynamicSymbolsPumpingStationsButton);
373     }
374
375     private void initializeEdgeArrows(Composite parent) {
376         Group group = new Group(parent, SWT.NONE);
377         group.setText("Edge Arrows");
378         GridDataFactory.fillDefaults().grab(true, false).applyTo(group);
379         GridLayoutFactory.fillDefaults().numColumns(6).margins(5, 5).applyTo(group);
380         
381         createEdgeArrowsHeaderRow(group);
382         
383         edgeArrowSuppliers = new ArrayList<>();
384         {
385             try {
386                 Collection<DynamicArrowObject> result = Simantics.getSession().syncRequest(new UniqueRead<Collection<DynamicArrowObject>>() {
387
388                     @Override
389                     public Collection<DynamicArrowObject> perform(ReadGraph graph) throws DatabaseException {
390                         return DynamicVisualisationsContributions.dynamicEdgeArrowObjects(graph);
391                     }
392                 });
393                 
394                 for (DynamicArrowObject object : result) {
395                     edgeArrowSuppliers.add(createEdgeArrowRow(group, object));
396                 }
397             } catch (DatabaseException e) {
398                 LOGGER.error("Could not create coloring objecst", e);
399             }
400         }
401     }
402
403     
404     private void createEdgeArrowsHeaderRow(Composite parent) {
405
406         Label label = new Label(parent, SWT.NONE);
407         label.setText("Label");
408         GridDataFactory.fillDefaults().grab(true, false).align(SWT.CENTER, SWT.CENTER).applyTo(label);
409         
410         label = new Label(parent, SWT.NONE);
411         label.setText("Used");
412         GridDataFactory.fillDefaults().grab(true, false).align(SWT.CENTER, SWT.CENTER).applyTo(label);
413         
414         label = new Label(parent, SWT.NONE);
415         label.setText("Variable");
416         GridDataFactory.fillDefaults().grab(true, false).align(SWT.CENTER, SWT.CENTER).applyTo(label);
417         
418         label = new Label(parent, SWT.NONE);
419         label.setText("Gain");
420         GridDataFactory.fillDefaults().grab(true, false).align(SWT.CENTER, SWT.CENTER).applyTo(label);
421         
422         label = new Label(parent, SWT.NONE);
423         label.setText("Bias");
424         GridDataFactory.fillDefaults().grab(true, false).align(SWT.CENTER, SWT.CENTER).applyTo(label);
425
426         label = new Label(parent, SWT.NONE);
427         label.setText("Default");
428         GridDataFactory.fillDefaults().grab(true, false).align(SWT.CENTER, SWT.CENTER).applyTo(label);
429     }
430     
431     private Supplier<Pair<String, DynamicArrowContribution>> createEdgeArrowRow(Composite parent, DynamicArrowObject object) {
432
433         Map<String, DynamicArrowContribution> arrowContributions = object.getArrowContributions();
434         if (arrowContributions.isEmpty()) {
435             // ok, no point in showing empty combo boxes
436             return null;
437         }
438         
439         Label label = new Label(parent, SWT.NONE);
440         label.setText(object.getArrowObject().getName());
441         GridDataFactory.fillDefaults().align(SWT.CENTER, SWT.CENTER).applyTo(label);
442         
443         Button usedButton = new Button(parent, SWT.CHECK);
444         GridDataFactory.fillDefaults().align(SWT.CENTER, SWT.CENTER).applyTo(usedButton);
445         addSelectionListener(usedButton);
446         
447         Combo variableCombo = new Combo(parent, SWT.READ_ONLY);
448         variableCombo.setItems(arrowContributions.keySet().toArray(new String[arrowContributions.size()]));
449         
450         GridDataFactory.fillDefaults().align(SWT.CENTER, SWT.CENTER).applyTo(variableCombo);
451         
452         Text gainText = new Text(parent, SWT.BORDER);
453         GridDataFactory.fillDefaults().align(SWT.CENTER, SWT.CENTER).applyTo(gainText);
454         addSelectionListener(gainText);
455         
456         Text biasText = new Text(parent, SWT.BORDER);
457         GridDataFactory.fillDefaults().align(SWT.CENTER, SWT.CENTER).applyTo(biasText);
458         addSelectionListener(biasText);
459         
460         Button defaultButton = new Button(parent, SWT.CHECK);
461         GridDataFactory.fillDefaults().align(SWT.CENTER, SWT.CENTER).applyTo(defaultButton);
462         defaultButton.addSelectionListener(new SelectionAdapter() {
463             
464             @Override
465             public void widgetSelected(SelectionEvent e) {
466                 int index = variableCombo.getSelectionIndex();
467                 if (index >= 0) {
468                     String key = variableCombo.getItem(index);
469                     DynamicArrowContribution cont = arrowContributions.get(key);
470                     
471                     gainText.setText(Double.toString(cont.getDefaultGain()));
472                     gainText.setEnabled(!defaultButton.getSelection());
473                     biasText.setText(Double.toString(cont.getDefaultBias()));
474                     biasText.setEnabled(!defaultButton.getSelection());
475                     
476                 }
477             }
478         });
479         addSelectionListener(defaultButton);
480         
481         variableCombo.addSelectionListener(new SelectionAdapter() {
482             
483             @Override
484             public void widgetSelected(SelectionEvent e) {
485                 // handle update for others
486                 String key = variableCombo.getItem(variableCombo.getSelectionIndex());
487                 DynamicArrowContribution cont = arrowContributions.get(key);
488                 
489                 if (gainText.getText().isEmpty()) {
490                     gainText.setText(Double.toString(cont.getDefaultGain()));
491                 }
492                 if (biasText.getText().isEmpty()) {
493                     biasText.setText(Double.toString(cont.getDefaultBias()));
494                 }
495                 
496                 defaultButton.setSelection(true);
497             }
498         });
499         addSelectionListener(variableCombo);
500         
501         arrowRows.put(object.getArrowObject().getName(), new ArrowObjectRow(label, usedButton, variableCombo, gainText, biasText, defaultButton));
502         
503         return new Supplier<Pair<String, DynamicArrowContribution>>() {
504
505             @Override
506             public Pair<String, DynamicArrowContribution> get() {
507                 int selectionIndex = variableCombo.getSelectionIndex();
508                 if (selectionIndex >= 0) {
509                     String key = variableCombo.getItem(selectionIndex);
510                     DynamicArrowContribution cont = arrowContributions.get(key);
511                     if (cont != null) {
512                         String label = variableCombo.getItem(variableCombo.getSelectionIndex());
513                         
514                         double gain = cont.getDefaultGain();
515                         String gainS = gainText.getText();
516                         if (gainS != null && !gainS.isEmpty()) {
517                             gain = Double.parseDouble(gainS);
518                         }
519                         double bias = cont.getDefaultBias();
520                         String biasS = biasText.getText();
521                         if (biasS != null && !biasS.isEmpty()) {
522                             bias = Double.parseDouble(biasText.getText());
523                         }
524                         
525                         DynamicArrowContribution dsc = new DynamicArrowContribution(label, cont.getModuleName(), cont.getAttributeName(), gain, bias);
526                         dsc.setUsed(usedButton.getSelection());
527                         dsc.setUseDefault(defaultButton.getSelection());
528                         
529                         return Pair.make(object.getArrowObject().getName(), dsc);
530                     }
531                 }
532                 return null;
533             }
534         };
535     }
536
537     protected void removeVisualisationTemplate(String name, Optional<Resource> of) {
538         if (of.isPresent()) {
539             Resource visualisation = of.get();
540             Simantics.getSession().asyncRequest(new WriteRequest() {
541                 
542                 @Override
543                 public void perform(WriteGraph graph) throws DatabaseException {
544                     RemoverUtil.remove(graph, visualisation);
545                 }
546             });
547         }
548     }
549
550     private void showSaveVisualisationTemplateDialog(Shell shell) {
551         
552         InputDialog dialog = new InputDialog(shell, "Save visualisation template", "Give template a name", "", new IInputValidator() {
553             
554             @Override
555             public String isValid(String newText) {
556                 if (newText == null || newText.isEmpty())
557                     return "Name cannot be empty";
558                 return null;
559             }
560         });
561         
562         if (dialog.open() == Dialog.OK) {
563             String name = dialog.getValue();
564             try {
565                 persistVisualisationTemplate(name, Optional.empty());
566             } catch (Exception e) {
567                 LOGGER.error("Could not persist visualisation template", e);
568                 ShowError.showError("Could not persist visualisation template", e.getMessage(), e);
569             }
570         }
571     }
572
573     private void persistCurrentVisualisationTemplateIfAvailable() {
574         if (visualisation != null) {
575             try {
576                 persistVisualisationTemplate(visualisation.getName(), Optional.of(visualisation.getVisualisationResource()));
577             } catch (Exception e1) {
578                 LOGGER.error("Could not persist visualisation template", e1);
579                 ShowError.showError("Could not persist visualisation template", e1.getMessage(), e1);
580             }
581         } else {
582             LOGGER.info("No current visualisation template selected for saving");
583         }
584     }
585     
586     private void persistVisualisationTemplate(String templateName, Optional<Resource> existing) throws Exception {
587         
588         List<Pair<String, DynamicColorContribution>> colorCollect = colorSuppliers.stream().filter(Objects::nonNull).map(s -> s.get()).filter(Objects::nonNull).collect(Collectors.toList());
589
590         String colorLocation = colorLocationCombo.getItem(colorLocationCombo.getSelectionIndex());
591         String colorSize = colorSizeCombo.getItem(colorSizeCombo.getSelectionIndex());
592         
593         ColorBarOptions colorBarOptions = new ColorBarOptions()
594                 .showColorBars(showColorButton.getSelection())
595                 .showColorBarsTicks(colorTicksButton.getSelection())
596                 .useGradients(colorGradientButton.getSelection())
597                 .withLocation(ColorBarsLocation.valueOf(colorLocation))
598                 .withSize(ColorBarsSize.valueOf(colorSize));
599         
600         List<Pair<String, DynamicSizeContribution>> sizeCollect = sizeSuppliers.stream().filter(Objects::nonNull).map(s -> s.get()).filter(Objects::nonNull).collect(Collectors.toList());
601         
602         String sizeLocation = sizeLocationCombo.getItem(sizeLocationCombo.getSelectionIndex());
603         String sizeSize = sizeSizeCombo.getItem(sizeSizeCombo.getSelectionIndex());
604         
605         final SizeBarOptions sizeBarOptions = new SizeBarOptions()
606                 .showSizeBars(showSizeButton.getSelection())
607                 .showSizeBarsTicks(sizeTicksButton.getSelection())
608                 .useGradients(sizeGradientButton.getSelection())
609                 .withLocation(SizeBarsLocation.valueOf(sizeLocation))
610                 .withSize(SizeBarsSize.valueOf(sizeSize));
611         
612         List<Pair<String, DynamicArrowContribution>> edgeArrowCollect = edgeArrowSuppliers.stream().filter(Objects::nonNull).map(s -> s.get()).filter(Objects::nonNull).collect(Collectors.toList());
613         
614         boolean hideEdges = hideEdgesButton.getSelection();
615         boolean hidePoints = hidePointsButton.getSelection();
616         boolean hideConsumers = hideConsumersButton.getSelection();
617         boolean hideProducers = hideProducersButton.getSelection();
618         boolean hideValves = hideValvesButton.getSelection();
619         boolean hidePumpingStations = hidePumpingStationsButton.getSelection();
620         
621         boolean networkBranchesStaticProperties = networkBranchesStaticPropertiesButton.getSelection();
622         boolean pointsStaticProperties = pointsStaticPropertiesButton.getSelection();
623         boolean consumersStaticProperties = consumersStaticPropertiesButton.getSelection();
624         
625         boolean dynamicSymbolsEdges = dynamicSymbolsEdgesButton.getSelection();
626         boolean dynamicSymbolsProducers = dynamicSymbolsProducersButton.getSelection();
627         boolean dynamicSymbolsValves = dynamicSymbolsValvesButton.getSelection();
628         boolean dynamicSymbolsPumpingStations = dynamicSymbolsPumpingStationsButton.getSelection();
629         
630         boolean disabled = disableUpdatesButton.getSelection();
631         boolean resetVisualisation = resetVisualisationButton.getSelection();
632         Long interval;
633         try {
634             interval = Long.parseLong(intervalText.getText());
635         } catch (NumberFormatException e) {
636             // ignore
637             interval = 2000L;
638         }
639         final long finalInterval = interval;
640         
641         boolean hoverVertex = hoveringVertexEnabledButton.getSelection();
642         boolean hoverEdges = hoveringEdgesEnabledButton.getSelection();
643         
644         Simantics.getSession().asyncRequest(new WriteRequest() {
645             
646             @Override
647             public void perform(WriteGraph graph) throws DatabaseException {
648                 Resource exist;
649                 if (existing.isPresent()) {
650                     exist = existing.get();
651                 } else {
652                     exist = DynamicVisualisations.createVisualisation(graph, parentResource, templateName);
653                 }
654                 DynamicVisualisations.setIntervalAndDisabled(graph, exist, finalInterval, disabled, resetVisualisation);
655                 DynamicVisualisations.setColorContributions(graph, exist, colorCollect);
656                 DynamicVisualisations.setColorBarOptions(graph, exist, colorBarOptions);
657                 DynamicVisualisations.setSizeContributions(graph, exist, sizeCollect);
658                 DynamicVisualisations.setSizeBarOptions(graph, exist, sizeBarOptions);
659                 DynamicVisualisations.setEdgeArrowContributions(graph, exist, edgeArrowCollect);
660                 DynamicVisualisations.setHideElements(graph, exist,
661                         hideEdges,
662                         hidePoints,
663                         hideConsumers,
664                         hideProducers,
665                         hideValves,
666                         hidePumpingStations
667                     );
668                 
669                 DynamicVisualisations.setStaticProperties(graph, exist,
670                         networkBranchesStaticProperties,
671                         pointsStaticProperties,
672                         consumersStaticProperties
673                     );
674                 
675                 DynamicVisualisations.setDynamicSymbols(graph, exist,
676                         dynamicSymbolsEdges,
677                         dynamicSymbolsProducers,
678                         dynamicSymbolsValves,
679                         dynamicSymbolsPumpingStations
680                     );
681                 DynamicVisualisations.setKeyVariablesHover(graph, exist, hoverVertex, hoverEdges);
682             }
683         });
684     }
685
686     private void initializeColoringObjects(Composite parent) {
687         Group group = new Group(parent, SWT.NONE);
688         group.setText("Coloring Objects");
689         GridDataFactory.fillDefaults().grab(true, false).applyTo(group);
690         GridLayoutFactory.fillDefaults().numColumns(8).margins(5, 5).applyTo(group);
691         
692         {
693             createColoringObjectHeaderRow(group);
694         }
695         colorSuppliers = new ArrayList<>();
696         {
697             try {
698                 Pair<Collection<DynamicColoringObject>, Map<String, DynamicColorMap>> result = Simantics.getSession().syncRequest(new UniqueRead<Pair<Collection<DynamicColoringObject>, Map<String, DynamicColorMap>>>() {
699
700                     @Override
701                     public Pair<Collection<DynamicColoringObject>, Map<String, DynamicColorMap>> perform(ReadGraph graph) throws DatabaseException {
702                         Map<String, DynamicColorMap> dynamicColorMaps = DynamicVisualisationsContributions.dynamicColorMaps(graph);
703                         return Pair.make(DynamicVisualisationsContributions.dynamicColoringObjects(graph), dynamicColorMaps);
704                     }
705                 });
706                 
707                 for (DynamicColoringObject object : result.first) {
708                     colorSuppliers.add(createColoringObjectRow(group, object, result.second));
709                 }
710
711             } catch (DatabaseException e) {
712                 LOGGER.error("Could not create coloring objecst", e);
713             }
714         }
715     }
716     
717     private void createColoringObjectHeaderRow(Composite parent) {
718
719         Label label = new Label(parent, SWT.NONE);
720         label.setText("Label");
721         GridDataFactory.fillDefaults().grab(true, false).align(SWT.CENTER, SWT.CENTER).applyTo(label);
722         
723         label = new Label(parent, SWT.NONE);
724         label.setText("Used");
725         GridDataFactory.fillDefaults().grab(true, false).align(SWT.CENTER, SWT.CENTER).applyTo(label);
726         
727         label = new Label(parent, SWT.NONE);
728         label.setText("Variable");
729         GridDataFactory.fillDefaults().grab(true, false).align(SWT.CENTER, SWT.CENTER).applyTo(label);
730         
731         label = new Label(parent, SWT.NONE);
732         label.setText("Min");
733         GridDataFactory.fillDefaults().grab(true, false).align(SWT.CENTER, SWT.CENTER).applyTo(label);
734         
735         label = new Label(parent, SWT.NONE);
736         label.setText("Max");
737         GridDataFactory.fillDefaults().grab(true, false).align(SWT.CENTER, SWT.CENTER).applyTo(label);
738         
739         label = new Label(parent, SWT.NONE);
740         label.setText("Unit");
741         GridDataFactory.fillDefaults().grab(true, false).align(SWT.CENTER, SWT.CENTER).applyTo(label);
742         
743         label = new Label(parent, SWT.NONE);
744         label.setText("ColorMap");
745         GridDataFactory.fillDefaults().grab(true, false).align(SWT.CENTER, SWT.CENTER).applyTo(label);
746         
747         label = new Label(parent, SWT.NONE);
748         label.setText("Default");
749         GridDataFactory.fillDefaults().grab(true, false).align(SWT.CENTER, SWT.CENTER).applyTo(label);
750     }
751
752     private Map<String, ColoringObjectRow> coloringRows = new HashMap<>();
753     private Map<String, SizingObjectRow> sizingRows = new HashMap<>();
754     private Map<String, ArrowObjectRow> arrowRows = new HashMap<>();
755
756     private VisualisationsListener visualisationsListener;
757
758     private Collection<NamedResource> visualisations;
759
760     private List<Supplier<Pair<String, DynamicSizeContribution>>> sizeSuppliers;
761
762     private static class ColoringObjectRow {
763         
764         private final Label label;
765         private final Button usedButton;
766         private final Combo variableCombo;
767         private final Text minText;
768         private final Text maxText;
769         private final Text unit;
770         private final Combo colorMapCombo;
771         private final Button defaultButton;
772         
773         public ColoringObjectRow(Label label, Button usedButton, Combo variableCombo, Text minText, Text maxText, Text unit,
774                 Combo colorMapCombo, Button defaultButton) {
775             super();
776             this.label = label;
777             this.usedButton = usedButton;
778             this.variableCombo = variableCombo;
779             this.minText = minText;
780             this.maxText = maxText;
781             this.unit = unit;
782             this.colorMapCombo = colorMapCombo;
783             this.defaultButton = defaultButton;
784         }
785
786         public void update(DynamicColorContribution colorContribution) {
787             String[] items = variableCombo.getItems();
788             for (int i = 0; i < items.length; i++) {
789                 if (colorContribution.getLabel().equals(items[i])) {
790                     variableCombo.select(i);
791                     break;
792                 }
793             }
794             minText.setText(Double.toString(colorContribution.getDefaultMin()));
795             maxText.setText(Double.toString(colorContribution.getDefaultMax()));
796             unit.setText(colorContribution.getUnit());
797             
798             String[] colorItems = colorMapCombo.getItems();
799             for (int i = 0; i < colorItems.length; i++) {
800                 
801                 if (colorContribution.getDefaultColorMap().getLabel().equals(colorItems[i])) {
802                     colorMapCombo.select(i);
803                     break;
804                 }
805             }
806             usedButton.setSelection(colorContribution.isUsed());
807             defaultButton.setSelection(colorContribution.isUseDefault());
808             
809             minText.setEnabled(!colorContribution.isUseDefault());
810             maxText.setEnabled(!colorContribution.isUseDefault());
811             colorMapCombo.setEnabled(!colorContribution.isUseDefault());
812         }
813     }
814
815     private static class SizingObjectRow {
816         
817         private final Label label;
818         private final Button usedButton;
819         private final Combo variableCombo;
820         private final Text minText;
821         private final Text maxText;
822         private final Label unit;
823         private final Combo sizeMapCombo;
824         private final Button defaultButton;
825         
826         public SizingObjectRow(Label label, Button usedButton, Combo variableCombo, Text minText, Text maxText, Label unit,
827                 Combo sizeMapCombo, Button defaultButton) {
828             super();
829             this.label = label;
830             this.usedButton = usedButton;
831             this.variableCombo = variableCombo;
832             this.minText = minText;
833             this.maxText = maxText;
834             this.unit = unit;
835             this.sizeMapCombo = sizeMapCombo;
836             this.defaultButton = defaultButton;
837         }
838
839         public void update(DynamicSizeContribution sizeContribution) {
840             String[] items = variableCombo.getItems();
841             for (int i = 0; i < items.length; i++) {
842                 if (sizeContribution.getLabel().equals(items[i])) {
843                     variableCombo.select(i);
844                     break;
845                 }
846             }
847             minText.setText(Double.toString(sizeContribution.getDefaultMin()));
848             maxText.setText(Double.toString(sizeContribution.getDefaultMax()));
849             unit.setText(sizeContribution.getUnit());
850             
851             String[] sizeItems = sizeMapCombo.getItems();
852             for (int i = 0; i < sizeItems.length; i++) {
853                 if (sizeContribution.getDefaultSizeMap().getLabel().equals(sizeItems[i])) {
854                     sizeMapCombo.select(i);
855                     break;
856                 }
857             }
858             usedButton.setSelection(sizeContribution.isUsed());
859             defaultButton.setSelection(sizeContribution.isUseDefault());
860             
861             minText.setEnabled(!sizeContribution.isUseDefault());
862             maxText.setEnabled(!sizeContribution.isUseDefault());
863             sizeMapCombo.setEnabled(!sizeContribution.isUseDefault());
864         }
865     }
866
867     private static class ArrowObjectRow {
868         
869         private final Label label;
870         private final Button usedButton;
871         private final Combo variableCombo;
872         private final Text gainText;
873         private final Text biasText;
874         private final Button defaultButton;
875         
876         public ArrowObjectRow(Label label, Button usedButton, Combo variableCombo, Text gainText, Text biasText, Button defaultButton) {
877             this.label = label;
878             this.usedButton = usedButton;
879             this.variableCombo = variableCombo;
880             this.gainText = gainText;
881             this.biasText = biasText;
882             this.defaultButton = defaultButton;
883         }
884
885         public void update(DynamicColorContribution colorContribution) {
886             String[] items = variableCombo.getItems();
887             for (int i = 0; i < items.length; i++) {
888                 if (colorContribution.getLabel().equals(items[i])) {
889                     variableCombo.select(i);
890                     break;
891                 }
892             }
893             gainText.setText(Double.toString(colorContribution.getDefaultMin()));
894             biasText.setText(Double.toString(colorContribution.getDefaultMax()));
895             
896             usedButton.setSelection(colorContribution.isUsed());
897             defaultButton.setSelection(colorContribution.isUseDefault());
898             
899             gainText.setEnabled(!colorContribution.isUseDefault());
900             biasText.setEnabled(!colorContribution.isUseDefault());
901         }
902     }
903
904     private Supplier<Pair<String, DynamicColorContribution>> createColoringObjectRow(Composite parent, DynamicColoringObject object, Map<String, DynamicColorMap> colorMaps) {
905         Label label = new Label(parent, SWT.NONE);
906         label.setText(object.getColoringObject().getName());
907         GridDataFactory.fillDefaults().align(SWT.CENTER, SWT.CENTER).applyTo(label);
908         
909         Map<String, DynamicColorContribution> colorContributions = object.getColorContributions();
910         
911         Button usedButton = new Button(parent, SWT.CHECK);
912         GridDataFactory.fillDefaults().align(SWT.CENTER, SWT.CENTER).applyTo(usedButton);
913         addSelectionListener(usedButton);
914         
915         Combo variableCombo = new Combo(parent, SWT.READ_ONLY);
916         variableCombo.setItems(colorContributions.keySet().toArray(new String[colorContributions.size()]));
917         
918         GridDataFactory.fillDefaults().align(SWT.CENTER, SWT.CENTER).applyTo(variableCombo);
919         
920         Text minText = new Text(parent, SWT.BORDER);
921         GridDataFactory.fillDefaults().grab(true, false).hint(150, SWT.DEFAULT).align(SWT.CENTER, SWT.CENTER).applyTo(minText);
922         addSelectionListener(minText);
923         
924         Text maxText = new Text(parent, SWT.BORDER);
925         GridDataFactory.fillDefaults().grab(true, false).hint(150, SWT.DEFAULT).align(SWT.CENTER, SWT.CENTER).applyTo(maxText);
926         addSelectionListener(maxText);
927         
928         Text unit = new Text(parent, SWT.READ_ONLY);
929         GridDataFactory.fillDefaults().grab(true, false).align(SWT.CENTER, SWT.CENTER).applyTo(unit);
930         
931         Combo colorMapCombo = new Combo(parent, SWT.READ_ONLY);
932         GridDataFactory.fillDefaults().align(SWT.CENTER, SWT.CENTER).applyTo(colorMapCombo);
933         colorMapCombo.setItems(colorMaps.keySet().toArray(new String[colorMaps.keySet().size()]));
934         addSelectionListener(colorMapCombo);
935         
936         Button defaultButton = new Button(parent, SWT.CHECK);
937         GridDataFactory.fillDefaults().align(SWT.CENTER, SWT.CENTER).applyTo(defaultButton);
938         defaultButton.addSelectionListener(new SelectionAdapter() {
939             
940             @Override
941             public void widgetSelected(SelectionEvent e) {
942                 int index = variableCombo.getSelectionIndex();
943                 if (index >= 0) {
944                     String key = variableCombo.getItem(index);
945                     DynamicColorContribution cont = colorContributions.get(key);
946                     
947                     minText.setText(Double.toString(cont.getDefaultMin()));
948                     minText.setEnabled(!defaultButton.getSelection());
949                     maxText.setText(Double.toString(cont.getDefaultMax()));
950                     maxText.setEnabled(!defaultButton.getSelection());
951                     unit.setText(cont.getUnit());
952                     
953                     String[] items = colorMapCombo.getItems();
954                     for (int i = 0; i < items.length; i++) {
955                         String item = items[i];
956                         if (item.equals(cont.getDefaultColorMap().getLabel())) {
957                             colorMapCombo.select(i);
958                             break;
959                         }
960                     }
961                     colorMapCombo.setEnabled(!defaultButton.getSelection());
962                 }
963             }
964         });
965         addSelectionListener(defaultButton);
966         
967         variableCombo.addSelectionListener(new SelectionAdapter() {
968             
969             @Override
970             public void widgetSelected(SelectionEvent e) {
971                 // handle update for others
972                 int index = variableCombo.getSelectionIndex();
973                 if (index >= 0) {
974                     String key = variableCombo.getItem(index);
975                     DynamicColorContribution cont = colorContributions.get(key);
976                     
977                     if (minText.getText().isEmpty()) {
978                         minText.setText(Double.toString(cont.getDefaultMin()));
979                     }
980                     if (maxText.getText().isEmpty()) {
981                         maxText.setText(Double.toString(cont.getDefaultMax()));
982                     }
983                     unit.setText(cont.getUnit());
984                     
985                     String[] items = colorMapCombo.getItems();
986                     for (int i = 0; i < items.length; i++) {
987                         String item = items[i];
988                         if (item.equals(cont.getDefaultColorMap().getLabel())) {
989                             colorMapCombo.select(i);
990                             break;
991                         }
992                     }
993                     
994                     defaultButton.setSelection(true);
995                 }
996             }
997         });
998         addSelectionListener(variableCombo);
999         
1000         coloringRows.put(object.getColoringObject().getName(), new ColoringObjectRow(label, usedButton, variableCombo, minText, maxText, unit, colorMapCombo, defaultButton));
1001
1002         return new Supplier<Pair<String, DynamicColorContribution>>() {
1003
1004             @Override
1005             public Pair<String, DynamicColorContribution> get() {
1006                 int selectionIndex = variableCombo.getSelectionIndex();
1007                 if (selectionIndex >= 0) {
1008                     String key = variableCombo.getItem(selectionIndex);
1009                     DynamicColorContribution cont = colorContributions.get(key);
1010                     if (cont != null) {
1011                         
1012                         String label = variableCombo.getItem(variableCombo.getSelectionIndex());
1013                         DynamicColorContribution dcc;
1014                         if (colorMapCombo.getSelectionIndex() > -1) {
1015                             String colorMap = colorMapCombo.getItem(colorMapCombo.getSelectionIndex());
1016                             DynamicColorMap dColorMap = colorMaps.get(colorMap);
1017                             dcc = new DynamicColorContribution(label, cont.getModuleName(), cont.getAttributeName(), unit.getText(), cont.getVariableGain(), cont.getVariableBias(), dColorMap, Double.parseDouble(minText.getText()), Double.parseDouble(maxText.getText()));
1018                         } else {
1019                             dcc = colorContributions.get(label);
1020                         }
1021                         dcc.setUsed(usedButton.getSelection());
1022                         dcc.setUseDefault(defaultButton.getSelection());
1023                         
1024                         return Pair.make(object.getColoringObject().getName(), dcc);
1025                     }
1026                 }
1027                 return null;
1028             }
1029         };
1030     }
1031     
1032     private void createSizingObjectHeaderRow(Composite parent) {
1033
1034         Label label = new Label(parent, SWT.NONE);
1035         label.setText("Label");
1036         GridDataFactory.fillDefaults().grab(true, false).align(SWT.CENTER, SWT.CENTER).applyTo(label);
1037         
1038         label = new Label(parent, SWT.NONE);
1039         label.setText("Used");
1040         GridDataFactory.fillDefaults().grab(true, false).align(SWT.CENTER, SWT.CENTER).applyTo(label);
1041         
1042         label = new Label(parent, SWT.NONE);
1043         label.setText("Variable");
1044         GridDataFactory.fillDefaults().grab(true, false).align(SWT.CENTER, SWT.CENTER).applyTo(label);
1045         
1046         label = new Label(parent, SWT.NONE);
1047         label.setText("Min");
1048         GridDataFactory.fillDefaults().grab(true, false).align(SWT.CENTER, SWT.CENTER).applyTo(label);
1049         
1050         label = new Label(parent, SWT.NONE);
1051         label.setText("Max");
1052         GridDataFactory.fillDefaults().grab(true, false).align(SWT.CENTER, SWT.CENTER).applyTo(label);
1053         
1054         label = new Label(parent, SWT.NONE);
1055         label.setText("Unit");
1056         GridDataFactory.fillDefaults().grab(true, false).align(SWT.CENTER, SWT.CENTER).applyTo(label);
1057         
1058         label = new Label(parent, SWT.NONE);
1059         label.setText("SizeMap");
1060         GridDataFactory.fillDefaults().grab(true, false).align(SWT.CENTER, SWT.CENTER).applyTo(label);
1061         
1062         label = new Label(parent, SWT.NONE);
1063         label.setText("Default");
1064         GridDataFactory.fillDefaults().grab(true, false).align(SWT.CENTER, SWT.CENTER).applyTo(label);
1065     }
1066
1067     private Supplier<Pair<String, DynamicSizeContribution>> createSizingObjectRow(Composite parent, DynamicSizingObject object, Map<String, DynamicSizeMap> sizeMaps) {
1068         Label label = new Label(parent, SWT.NONE);
1069         label.setText(object.getSizingObject().getName());
1070         GridDataFactory.fillDefaults().align(SWT.CENTER, SWT.CENTER).applyTo(label);
1071         
1072         Map<String, DynamicSizeContribution> sizeContributions = object.getSizeContributions();
1073         
1074         Button usedButton = new Button(parent, SWT.CHECK);
1075         GridDataFactory.fillDefaults().align(SWT.CENTER, SWT.CENTER).applyTo(usedButton);
1076         addSelectionListener(usedButton);
1077         
1078         Combo variableCombo = new Combo(parent, SWT.READ_ONLY);
1079         variableCombo.setItems(sizeContributions.keySet().toArray(new String[sizeContributions.size()]));
1080         
1081         GridDataFactory.fillDefaults().align(SWT.CENTER, SWT.CENTER).applyTo(variableCombo);
1082         
1083         Text minText = new Text(parent, SWT.BORDER);
1084         GridDataFactory.fillDefaults().align(SWT.CENTER, SWT.CENTER).applyTo(minText);
1085         addSelectionListener(minText);
1086         
1087         Text maxText = new Text(parent, SWT.BORDER);
1088         GridDataFactory.fillDefaults().align(SWT.CENTER, SWT.CENTER).applyTo(maxText);
1089         addSelectionListener(maxText);
1090         
1091         Label unit = new Label(parent, SWT.NONE);
1092         GridDataFactory.fillDefaults().grab(true, false).align(SWT.CENTER, SWT.CENTER).applyTo(unit);
1093         
1094         Combo sizeMapCombo = new Combo(parent, SWT.READ_ONLY);
1095         GridDataFactory.fillDefaults().align(SWT.CENTER, SWT.CENTER).applyTo(sizeMapCombo);
1096         sizeMapCombo.setItems(sizeMaps.keySet().toArray(new String[sizeMaps.keySet().size()]));
1097         addSelectionListener(sizeMapCombo);
1098         
1099         Button defaultButton = new Button(parent, SWT.CHECK);
1100         GridDataFactory.fillDefaults().align(SWT.CENTER, SWT.CENTER).applyTo(defaultButton);
1101         defaultButton.addSelectionListener(new SelectionAdapter() {
1102             
1103             @Override
1104             public void widgetSelected(SelectionEvent e) {
1105                 int index = variableCombo.getSelectionIndex();
1106                 if (index >= 0) {
1107                     String key = variableCombo.getItem(index);
1108                     DynamicSizeContribution cont = sizeContributions.get(key);
1109                     
1110                     minText.setText(Double.toString(cont.getDefaultMin()));
1111                     minText.setEnabled(!defaultButton.getSelection());
1112                     maxText.setText(Double.toString(cont.getDefaultMax()));
1113                     maxText.setEnabled(!defaultButton.getSelection());
1114                     unit.setText(cont.getUnit());
1115                     
1116                     String[] items = sizeMapCombo.getItems();
1117                     for (int i = 0; i < items.length; i++) {
1118                         String item = items[i];
1119                         if (item.equals(cont.getDefaultSizeMap().getLabel())) {
1120                             sizeMapCombo.select(i);
1121                             break;
1122                         }
1123                     }
1124                     sizeMapCombo.setEnabled(!defaultButton.getSelection());
1125                 }
1126             }
1127         });
1128         addSelectionListener(defaultButton);
1129         
1130         variableCombo.addSelectionListener(new SelectionAdapter() {
1131             
1132             @Override
1133             public void widgetSelected(SelectionEvent e) {
1134                 // handle update for others
1135                 String key = variableCombo.getItem(variableCombo.getSelectionIndex());
1136                 DynamicSizeContribution cont = sizeContributions.get(key);
1137                 
1138                 if (minText.getText().isEmpty()) {
1139                     minText.setText(Double.toString(cont.getDefaultMin()));
1140                 }
1141                 if (maxText.getText().isEmpty()) {
1142                     maxText.setText(Double.toString(cont.getDefaultMax()));
1143                 }
1144                 unit.setText(cont.getUnit());
1145                 
1146                 String[] items = sizeMapCombo.getItems();
1147                 for (int i = 0; i < items.length; i++) {
1148                     String item = items[i];
1149                     if (item.equals(cont.getDefaultSizeMap().getLabel())) {
1150                         sizeMapCombo.select(i);
1151                         break;
1152                     }
1153                 }
1154                 
1155                 defaultButton.setSelection(true);
1156             }
1157         });
1158         addSelectionListener(variableCombo);
1159         
1160         sizingRows.put(object.getSizingObject().getName(), new SizingObjectRow(label, usedButton, variableCombo, minText, maxText, unit, sizeMapCombo, defaultButton));
1161         
1162         return new Supplier<Pair<String, DynamicSizeContribution>>() {
1163
1164             @Override
1165             public Pair<String, DynamicSizeContribution> get() {
1166                 int selectionIndex = variableCombo.getSelectionIndex();
1167                 if (selectionIndex >= 0) {
1168                     String key = variableCombo.getItem(selectionIndex);
1169                     DynamicSizeContribution cont = sizeContributions.get(key);
1170                     if (cont != null) {
1171
1172                         String label = variableCombo.getItem(variableCombo.getSelectionIndex());
1173                         DynamicSizeContribution dsc;
1174                         if (sizeMapCombo.getSelectionIndex() > -1) {
1175                             String sizeMap = sizeMapCombo.getItem(sizeMapCombo.getSelectionIndex());
1176                             DynamicSizeMap dSizeMap = sizeMaps.get(sizeMap);
1177                             dsc = new DynamicSizeContribution(label, cont.getModuleName(), cont.getAttributeName(), unit.getText(), cont.getVariableGain(), cont.getVariableBias(), dSizeMap, Double.parseDouble(minText.getText()), Double.parseDouble(maxText.getText()));
1178                         } else {
1179                             dsc = sizeContributions.get(label);
1180                         }
1181                         dsc.setUsed(usedButton.getSelection());
1182                         dsc.setUseDefault(defaultButton.getSelection());
1183                         
1184                         return Pair.make(object.getSizingObject().getName(), dsc);
1185                     }
1186                 }
1187                 return null;
1188             }
1189         };
1190     }
1191     
1192     private void initializeColorBars(Composite parent) {
1193         Group group = new Group(parent, SWT.NONE);
1194         group.setText("Color Bars");
1195         GridDataFactory.fillDefaults().grab(true, false).applyTo(group);
1196         GridLayoutFactory.fillDefaults().numColumns(8).margins(5, 5).applyTo(group);
1197         
1198         createColorBars(group);
1199     }
1200     
1201     private void createColorBars(Composite parent) {
1202         
1203         showColorButton = new Button(parent, SWT.CHECK);
1204         showColorButton.setText("Show");
1205         addSelectionListener(showColorButton);
1206         
1207         colorTicksButton = new Button(parent, SWT.CHECK);
1208         colorTicksButton.setText("Ticks");
1209         addSelectionListener(colorTicksButton);
1210         
1211         colorGradientButton = new Button(parent, SWT.CHECK);
1212         colorGradientButton.setText("Gradients");
1213         addSelectionListener(colorGradientButton);
1214         
1215         Label label = new Label(parent, SWT.NONE);
1216         label.setText("Location");
1217         colorLocationCombo = new Combo(parent, SWT.READ_ONLY);
1218         String[] colorLocationItems = Stream.of(ColorBarsLocation.values()).map(size -> size.toString()).toArray(String[]::new);
1219         colorLocationCombo.setItems(colorLocationItems);
1220         if (colorLocationItems.length > 0) {
1221             colorLocationCombo.select(0);
1222         }
1223         addSelectionListener(colorLocationCombo);
1224         
1225         label = new Label(parent, SWT.NONE);
1226         label.setText("Size");
1227         colorSizeCombo = new Combo(parent, SWT.READ_ONLY);
1228         String[] colorSizeItems = Stream.of(ColorBarsSize.values()).map(size -> size.toString()).toArray(String[]::new);
1229         colorSizeCombo.setItems(colorSizeItems);
1230         if (colorSizeItems.length > 0) {
1231             colorSizeCombo.select(0);
1232         }
1233         addSelectionListener(colorSizeCombo);
1234     }
1235
1236     private void initializeObjectSizes(Composite parent) {
1237         Group group = new Group(parent, SWT.NONE);
1238         group.setText("Object Sizes");
1239         GridDataFactory.fillDefaults().grab(true, false).applyTo(group);
1240         GridLayoutFactory.fillDefaults().numColumns(8).margins(5, 5).applyTo(group);
1241         
1242         {
1243             createSizingObjectHeaderRow(group);
1244             createObjectSizes(group);
1245         }
1246     }
1247     
1248     private void createObjectSizes(Composite parent) {
1249         
1250         sizeSuppliers = new ArrayList<>(); 
1251         try {
1252             Pair<Collection<DynamicSizingObject>, Map<String, DynamicSizeMap>> resultSizing = Simantics.getSession().syncRequest(new UniqueRead<Pair<Collection<DynamicSizingObject>, Map<String, DynamicSizeMap>>>() {
1253
1254                 @Override
1255                 public Pair<Collection<DynamicSizingObject>, Map<String, DynamicSizeMap>> perform(ReadGraph graph) throws DatabaseException {
1256                     Map<String, DynamicSizeMap> dynamicSizeMaps = DynamicVisualisationsContributions.dynamicSizeMaps(graph);
1257                     return Pair.make(DynamicVisualisationsContributions.dynamicSizingObjects(graph), dynamicSizeMaps);
1258                 }
1259             });
1260             
1261             for (DynamicSizingObject object : resultSizing.first) {
1262                 sizeSuppliers.add(createSizingObjectRow(parent, object, resultSizing.second));
1263             }
1264         } catch (DatabaseException e) {
1265             LOGGER.error("Could not create object sizes", e);
1266         }
1267     }
1268
1269     private void initializeSizeBars(Composite parent) {
1270         Group group = new Group(parent, SWT.NONE);
1271         group.setText("Size Bars");
1272         GridDataFactory.fillDefaults().grab(true, false).applyTo(group);
1273         GridLayoutFactory.fillDefaults().numColumns(8).margins(5, 5).applyTo(group);
1274         
1275         createSizeBars(group);
1276     }
1277
1278     private void createSizeBars(Composite parent) {
1279         showSizeButton = new Button(parent, SWT.CHECK);
1280         showSizeButton.setText("Show");
1281         addSelectionListener(showSizeButton);
1282         
1283         sizeTicksButton = new Button(parent, SWT.CHECK);
1284         sizeTicksButton.setText("Ticks");
1285         addSelectionListener(sizeTicksButton);
1286         
1287         sizeGradientButton = new Button(parent, SWT.CHECK);
1288         sizeGradientButton.setText("Gradient");
1289         addSelectionListener(sizeGradientButton);
1290         
1291         Label label = new Label(parent, SWT.NONE);
1292         label.setText("Location");
1293         sizeLocationCombo = new Combo(parent, SWT.READ_ONLY);
1294         String[] sizeLocationItems = Stream.of(SizeBarsLocation.values()).map(size -> size.toString()).toArray(String[]::new);
1295         sizeLocationCombo.setItems(sizeLocationItems);
1296         if (sizeLocationItems.length > 0) {
1297             sizeLocationCombo.select(0);
1298         }
1299         addSelectionListener(sizeLocationCombo);
1300         
1301         label = new Label(parent, SWT.NONE);
1302         label.setText("Size");
1303         sizeSizeCombo = new Combo(parent, SWT.READ_ONLY);
1304         String[] sizeSizeItems = Stream.of(SizeBarsSize.values()).map(size -> size.toString()).toArray(String[]::new);
1305         sizeSizeCombo.setItems(sizeSizeItems);
1306         if (sizeSizeItems.length > 0) {
1307             sizeSizeCombo.select(0);
1308         }
1309         addSelectionListener(sizeSizeCombo);
1310     }
1311
1312     private void addSelectionListener(Widget widget) {
1313         if (widget instanceof Button) {
1314             ((Button) widget).addSelectionListener(new SelectionAdapter() {
1315
1316                 @Override
1317                 public void widgetSelected(SelectionEvent e) {
1318                     persistCurrentVisualisationTemplateIfAvailable();
1319                 }
1320             });
1321         } else if (widget instanceof Combo) {
1322             ((Combo) widget).addSelectionListener(new SelectionAdapter() {
1323
1324                 @Override
1325                 public void widgetSelected(SelectionEvent e) {
1326                     persistCurrentVisualisationTemplateIfAvailable();
1327                 }
1328             });
1329         } else if (widget instanceof Text) {
1330             ((Text) widget).addFocusListener(new FocusAdapter() {
1331                 
1332                 @Override
1333                 public void focusLost(FocusEvent e) {
1334                     persistCurrentVisualisationTemplateIfAvailable();
1335                 }
1336             });
1337             ((Text) widget).addKeyListener(new KeyAdapter() {
1338                 
1339                 @Override
1340                 public void keyReleased(KeyEvent e) {
1341                     if(e.keyCode == SWT.CR || e.keyCode == SWT.LF) {
1342                         persistCurrentVisualisationTemplateIfAvailable();
1343                     }
1344                 }
1345             });
1346         }
1347     }
1348
1349     public void setParentResource(Resource parentResource) {
1350         if (this.parentResource != parentResource) {
1351             this.parentResource = parentResource;
1352             updateListening();
1353         }
1354         saveVisualisationTemplateAsButton.setEnabled(parentResource != null);
1355     }
1356
1357     private void updateListening() {
1358         if (visualisationsListener != null)
1359             visualisationsListener.dispose();
1360         if (parentResource != null) {
1361             visualisationsListener = new VisualisationsListener(this);
1362             Simantics.getSession().asyncRequest(new DynamicVisualisationsRequest(parentResource), visualisationsListener);
1363         }
1364         
1365         if (listener != null)
1366             listener.dispose();
1367         if (parentResource != null) {
1368             listener = new VisualisationListener(this);
1369             Simantics.getSession().asyncRequest(new ActiveDynamicVisualisationsRequest(parentResource), listener);
1370         }
1371     }
1372
1373     private static class VisualisationsListener implements Listener<Collection<NamedResource>> {
1374
1375         private static final Logger LOGGER = LoggerFactory.getLogger(VisualisationsListener.class);
1376
1377         private boolean disposed;
1378         private DynamicVisualisationsUI ui;
1379         
1380         public VisualisationsListener(DynamicVisualisationsUI ui) {
1381             this.ui = ui;
1382         }
1383
1384         @Override
1385         public void execute(Collection<NamedResource> result) {
1386             ui.updateVisualisations(result);
1387         }
1388
1389         @Override
1390         public void exception(Throwable t) {
1391             LOGGER.error("Could not listen visualisation", t);
1392         }
1393
1394         @Override
1395         public boolean isDisposed() {
1396             return disposed || ui.getParent().isDisposed();
1397         }
1398
1399         public void dispose() {
1400             this.disposed = true;
1401         }
1402     }
1403     
1404     private static class VisualisationListener implements Listener<DynamicVisualisation> {
1405
1406         private static final Logger LOGGER = LoggerFactory.getLogger(VisualisationListener.class);
1407
1408         private boolean disposed;
1409         private DynamicVisualisationsUI ui;
1410         
1411         public VisualisationListener(DynamicVisualisationsUI ui) {
1412             this.ui = ui;
1413         }
1414
1415         @Override
1416         public void execute(DynamicVisualisation result) {
1417             ui.updateVisualisation(result);
1418         }
1419
1420         @Override
1421         public void exception(Throwable t) {
1422             LOGGER.error("Could not listen visualisation", t);
1423         }
1424
1425         @Override
1426         public boolean isDisposed() {
1427             return disposed ||ui.getParent().isDisposed();
1428         }
1429
1430         public void dispose() {
1431             this.disposed = true;
1432         }
1433     }
1434
1435     public void updateVisualisation(DynamicVisualisation result) {
1436         this.visualisation = result;
1437         Display.getDefault().asyncExec(() -> {
1438             if (getParent().isDisposed())
1439                 return;
1440             
1441             removeVisualisationTemplateButton.setEnabled(visualisation != null && visualisation.getVisualisationResource() != null);
1442             
1443             if (visualisation != null) {
1444                 String[] items = templateSelectionCombo.getItems();
1445                 for (int i = 0; i < items.length; i++) {
1446                     if (visualisation.getName().equals(items[i])) {
1447                         templateSelectionCombo.select(i);
1448                         break;
1449                     }
1450                 }
1451                 
1452                 intervalText.setText(Long.toString(visualisation.getInterval()));
1453                 disableUpdatesButton.setSelection(visualisation.disabledUpdates());
1454                 
1455                 hoveringVertexEnabledButton.setSelection(visualisation.isKeyVariablesVertexHover());
1456                 hoveringEdgesEnabledButton.setSelection(visualisation.isKeyVariablesEdgesHover());
1457                 
1458                 hideConsumersButton.setSelection(visualisation.isHideConsumers());
1459                 hideEdgesButton.setSelection(visualisation.isHideEdges());
1460                 hideProducersButton.setSelection(visualisation.isHideProducers());
1461                 hideValvesButton.setSelection(visualisation.isHideValves());
1462                 hidePumpingStationsButton.setSelection(visualisation.isHidePumpingStations());
1463                 hidePointsButton.setSelection(visualisation.isHidePoints());
1464                 
1465                 Map<String, DynamicColorContribution> colorContributions = visualisation.getColorContributions();
1466                 for (Entry<String, DynamicColorContribution> entry : colorContributions.entrySet()) {
1467                     
1468                     ColoringObjectRow coloringObjectRow = coloringRows.get(entry.getKey());
1469                     if (coloringObjectRow != null) {
1470                         
1471                         coloringObjectRow.update(entry.getValue());
1472                         
1473                     } else {
1474                         LOGGER.info("No coloring object visualisation row for key {}", entry.getKey());
1475                     }
1476                 }
1477                 ColorBarOptions colorOptions = visualisation.getColorBarOptions();
1478                 showColorButton.setSelection(colorOptions.isShowColorBars());
1479                 colorTicksButton.setSelection(colorOptions.isShowColorBarsTicks());
1480                 colorGradientButton.setSelection(colorOptions.isUseGradients());
1481                 for (int i = 0; i < colorLocationCombo.getItems().length; i++) {
1482                     String item = colorLocationCombo.getItem(i);
1483                     if (item.equals(colorOptions.getLocation().toString())) {
1484                         colorLocationCombo.select(i);
1485                         break;
1486                     }
1487                 }
1488                 for (int i = 0; i < colorSizeCombo.getItems().length; i++) {
1489                     String item = colorSizeCombo.getItem(i);
1490                     if (item.equals(colorOptions.getSize().toString())) {
1491                         colorSizeCombo.select(i);
1492                         break;
1493                     }
1494                 }
1495                 
1496                 Map<String, DynamicSizeContribution> sizeContributions = visualisation.getSizeContributions();
1497                 for (Entry<String, DynamicSizeContribution> entry : sizeContributions.entrySet()) {
1498                     
1499                     SizingObjectRow sizingObjectRow = sizingRows.get(entry.getKey());
1500                     if (sizingObjectRow != null) {
1501                         
1502                         sizingObjectRow.update(entry.getValue());
1503                         
1504                     } else {
1505                         LOGGER.info("No sizing object visualisation row for key {}", entry.getKey());
1506                     }
1507                 }
1508                 SizeBarOptions sizeOptions = visualisation.getSizeBarOptions();
1509                 showSizeButton.setSelection(sizeOptions.isShowSizeBars());
1510                 sizeTicksButton.setSelection(sizeOptions.isShowSizeBarsTicks());
1511                 sizeGradientButton.setSelection(sizeOptions.isUseGradients());
1512                 for (int i = 0; i < sizeLocationCombo.getItems().length; i++) {
1513                     String item = sizeLocationCombo.getItem(i);
1514                     if (item.equals(sizeOptions.getLocation().toString())) {
1515                         sizeLocationCombo.select(i);
1516                         break;
1517                     }
1518                 }
1519                 for (int i = 0; i < sizeSizeCombo.getItems().length; i++) {
1520                     String item = sizeSizeCombo.getItem(i);
1521                     if (item.equals(sizeOptions.getSize().toString())) {
1522                         sizeSizeCombo.select(i);
1523                         break;
1524                     }
1525                 }
1526             }
1527         });
1528     }
1529
1530     public void updateVisualisations(Collection<NamedResource> result) {
1531         this.visualisations = result;
1532         
1533         Display.getDefault().asyncExec(() -> {
1534             if (getParent().isDisposed())
1535                 return;
1536             templateSelectionCombo.setItems(visualisations.stream().map(NamedResource::getName).collect(Collectors.toList()).toArray(new String[visualisations.size()]));
1537             
1538             if (visualisation != null) {
1539                 String[] items = templateSelectionCombo.getItems();
1540                 for (int i = 0; i < items.length; i++) {
1541                     if (visualisation.getName().equals(items[i])) {
1542                         templateSelectionCombo.select(i);
1543                         break;
1544                     }
1545                 }
1546             }
1547             
1548         });
1549     }
1550     
1551     public Composite getParent() {
1552         return parent;
1553     }
1554 }