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