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