]> gerrit.simantics Code Review - simantics/district.git/blob
46bea63f2a14032bff8e708aa424c44cf9025a02
[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         addSelectionListener(variableCombo);
414         
415         GridDataFactory.fillDefaults().align(SWT.CENTER, SWT.CENTER).applyTo(variableCombo);
416         
417         Text gainText = new Text(parent, SWT.BORDER);
418         GridDataFactory.fillDefaults().align(SWT.CENTER, SWT.CENTER).applyTo(gainText);
419         addSelectionListener(gainText);
420         
421         Text biasText = new Text(parent, SWT.BORDER);
422         GridDataFactory.fillDefaults().align(SWT.CENTER, SWT.CENTER).applyTo(biasText);
423         addSelectionListener(biasText);
424         
425         Button defaultButton = new Button(parent, SWT.CHECK);
426         GridDataFactory.fillDefaults().align(SWT.CENTER, SWT.CENTER).applyTo(defaultButton);
427         addSelectionListener(defaultButton);
428         defaultButton.addSelectionListener(new SelectionAdapter() {
429             
430             @Override
431             public void widgetSelected(SelectionEvent e) {
432                 int index = variableCombo.getSelectionIndex();
433                 if (index >= 0) {
434                     String key = variableCombo.getItem(index);
435                     DynamicArrowContribution cont = arrowContributions.get(key);
436                     
437                     gainText.setText(Double.toString(cont.getDefaultGain()));
438                     gainText.setEnabled(!defaultButton.getSelection());
439                     biasText.setText(Double.toString(cont.getDefaultBias()));
440                     biasText.setEnabled(!defaultButton.getSelection());
441                     
442                 }
443             }
444         });
445         
446         variableCombo.addSelectionListener(new SelectionAdapter() {
447             
448             @Override
449             public void widgetSelected(SelectionEvent e) {
450                 // handle update for others
451                 String key = variableCombo.getItem(variableCombo.getSelectionIndex());
452                 DynamicArrowContribution cont = arrowContributions.get(key);
453                 
454                 if (gainText.getText().isEmpty()) {
455                     gainText.setText(Double.toString(cont.getDefaultGain()));
456                 }
457                 if (biasText.getText().isEmpty()) {
458                     biasText.setText(Double.toString(cont.getDefaultBias()));
459                 }
460                 
461                 defaultButton.setSelection(true);
462             }
463         });
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         addSelectionListener(variableCombo);
870         
871         GridDataFactory.fillDefaults().align(SWT.CENTER, SWT.CENTER).applyTo(variableCombo);
872         
873         Text minText = new Text(parent, SWT.BORDER);
874         GridDataFactory.fillDefaults().grab(true, false).hint(150, SWT.DEFAULT).align(SWT.CENTER, SWT.CENTER).applyTo(minText);
875         addSelectionListener(minText);
876         
877         Text maxText = new Text(parent, SWT.BORDER);
878         GridDataFactory.fillDefaults().grab(true, false).hint(150, SWT.DEFAULT).align(SWT.CENTER, SWT.CENTER).applyTo(maxText);
879         addSelectionListener(maxText);
880         
881         Text unit = new Text(parent, SWT.READ_ONLY);
882         GridDataFactory.fillDefaults().grab(true, false).align(SWT.CENTER, SWT.CENTER).applyTo(unit);
883         
884         Combo colorMapCombo = new Combo(parent, SWT.READ_ONLY);
885         GridDataFactory.fillDefaults().align(SWT.CENTER, SWT.CENTER).applyTo(colorMapCombo);
886         colorMapCombo.setItems(colorMaps.keySet().toArray(new String[colorMaps.keySet().size()]));
887         addSelectionListener(colorMapCombo);
888         
889         Button defaultButton = new Button(parent, SWT.CHECK);
890         GridDataFactory.fillDefaults().align(SWT.CENTER, SWT.CENTER).applyTo(defaultButton);
891         addSelectionListener(defaultButton);
892         defaultButton.addSelectionListener(new SelectionAdapter() {
893             
894             @Override
895             public void widgetSelected(SelectionEvent e) {
896                 int index = variableCombo.getSelectionIndex();
897                 if (index >= 0) {
898                     String key = variableCombo.getItem(index);
899                     DynamicColorContribution cont = colorContributions.get(key);
900                     
901                     minText.setText(Double.toString(cont.getDefaultMin()));
902                     minText.setEnabled(!defaultButton.getSelection());
903                     maxText.setText(Double.toString(cont.getDefaultMax()));
904                     maxText.setEnabled(!defaultButton.getSelection());
905                     unit.setText(cont.getUnit());
906                     
907                     String[] items = colorMapCombo.getItems();
908                     for (int i = 0; i < items.length; i++) {
909                         String item = items[i];
910                         if (item.equals(cont.getDefaultColorMap().getLabel())) {
911                             colorMapCombo.select(i);
912                             break;
913                         }
914                     }
915                     colorMapCombo.setEnabled(!defaultButton.getSelection());
916                 }
917             }
918         });
919         
920         variableCombo.addSelectionListener(new SelectionAdapter() {
921             
922             @Override
923             public void widgetSelected(SelectionEvent e) {
924                 // handle update for others
925                 int index = variableCombo.getSelectionIndex();
926                 if (index >= 0) {
927                     String key = variableCombo.getItem(index);
928                     DynamicColorContribution cont = colorContributions.get(key);
929                     
930                     if (minText.getText().isEmpty()) {
931                         minText.setText(Double.toString(cont.getDefaultMin()));
932                     }
933                     if (maxText.getText().isEmpty()) {
934                         maxText.setText(Double.toString(cont.getDefaultMax()));
935                     }
936                     unit.setText(cont.getUnit());
937                     
938                     String[] items = colorMapCombo.getItems();
939                     for (int i = 0; i < items.length; i++) {
940                         String item = items[i];
941                         if (item.equals(cont.getDefaultColorMap().getLabel())) {
942                             colorMapCombo.select(i);
943                             break;
944                         }
945                     }
946                     
947                     defaultButton.setSelection(true);
948                 }
949             }
950         });
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                         String colorMap = colorMapCombo.getItem(colorMapCombo.getSelectionIndex());
964                         try {
965                             Map<String, DynamicColorMap> colorMaps = Simantics.getSession().syncRequest(new UniqueRead<Map<String, DynamicColorMap>>() {
966         
967                                 @Override
968                                 public Map<String, DynamicColorMap> perform(ReadGraph graph) throws DatabaseException {
969                                     return DynamicVisualisationsContributions.dynamicColorMaps(graph);
970                                 }
971                             });
972                             DynamicColorMap dColorMap = colorMaps.get(colorMap);
973                             String label = variableCombo.getItem(variableCombo.getSelectionIndex());
974                             
975                             DynamicColorContribution dcc = new DynamicColorContribution(label, cont.getModuleName(), cont.getAttributeName(), unit.getText(), cont.getVariableGain(), cont.getVariableBias(), dColorMap, Double.parseDouble(minText.getText()), Double.parseDouble(maxText.getText()));
976                             dcc.setUsed(usedButton.getSelection());
977                             dcc.setUseDefault(defaultButton.getSelection());
978                             
979                             return Pair.make(object.getColoringObject().getName(), dcc);
980                         } catch (DatabaseException e) {
981                             LOGGER.error("Could not get DynamicColorContribution", e);
982                         }
983                     }
984                 }
985                 return null;
986             }
987         };
988     }
989     
990     private void createSizingObjectHeaderRow(Composite parent) {
991
992         Label label = new Label(parent, SWT.NONE);
993         label.setText("Label");
994         GridDataFactory.fillDefaults().grab(true, false).align(SWT.CENTER, SWT.CENTER).applyTo(label);
995         
996         label = new Label(parent, SWT.NONE);
997         label.setText("Used");
998         GridDataFactory.fillDefaults().grab(true, false).align(SWT.CENTER, SWT.CENTER).applyTo(label);
999         
1000         label = new Label(parent, SWT.NONE);
1001         label.setText("Variable");
1002         GridDataFactory.fillDefaults().grab(true, false).align(SWT.CENTER, SWT.CENTER).applyTo(label);
1003         
1004         label = new Label(parent, SWT.NONE);
1005         label.setText("Min");
1006         GridDataFactory.fillDefaults().grab(true, false).align(SWT.CENTER, SWT.CENTER).applyTo(label);
1007         
1008         label = new Label(parent, SWT.NONE);
1009         label.setText("Max");
1010         GridDataFactory.fillDefaults().grab(true, false).align(SWT.CENTER, SWT.CENTER).applyTo(label);
1011         
1012         label = new Label(parent, SWT.NONE);
1013         label.setText("Unit");
1014         GridDataFactory.fillDefaults().grab(true, false).align(SWT.CENTER, SWT.CENTER).applyTo(label);
1015         
1016         label = new Label(parent, SWT.NONE);
1017         label.setText("SizeMap");
1018         GridDataFactory.fillDefaults().grab(true, false).align(SWT.CENTER, SWT.CENTER).applyTo(label);
1019         
1020         label = new Label(parent, SWT.NONE);
1021         label.setText("Default");
1022         GridDataFactory.fillDefaults().grab(true, false).align(SWT.CENTER, SWT.CENTER).applyTo(label);
1023     }
1024
1025     private Supplier<Pair<String, DynamicSizeContribution>> createSizingObjectRow(Composite parent, DynamicSizingObject object, Map<String, DynamicSizeMap> sizeMaps) {
1026         Label label = new Label(parent, SWT.NONE);
1027         label.setText(object.getSizingObject().getName());
1028         GridDataFactory.fillDefaults().align(SWT.CENTER, SWT.CENTER).applyTo(label);
1029         
1030         Map<String, DynamicSizeContribution> sizeContributions = object.getSizeContributions();
1031         
1032         Button usedButton = new Button(parent, SWT.CHECK);
1033         GridDataFactory.fillDefaults().align(SWT.CENTER, SWT.CENTER).applyTo(usedButton);
1034         addSelectionListener(usedButton);
1035         
1036         Combo variableCombo = new Combo(parent, SWT.READ_ONLY);
1037         variableCombo.setItems(sizeContributions.keySet().toArray(new String[sizeContributions.size()]));
1038         addSelectionListener(variableCombo);
1039         
1040         GridDataFactory.fillDefaults().align(SWT.CENTER, SWT.CENTER).applyTo(variableCombo);
1041         
1042         Text minText = new Text(parent, SWT.BORDER);
1043         GridDataFactory.fillDefaults().align(SWT.CENTER, SWT.CENTER).applyTo(minText);
1044         addSelectionListener(minText);
1045         
1046         Text maxText = new Text(parent, SWT.BORDER);
1047         GridDataFactory.fillDefaults().align(SWT.CENTER, SWT.CENTER).applyTo(maxText);
1048         addSelectionListener(maxText);
1049         
1050         Label unit = new Label(parent, SWT.NONE);
1051         GridDataFactory.fillDefaults().grab(true, false).align(SWT.CENTER, SWT.CENTER).applyTo(unit);
1052         
1053         Combo sizeMapCombo = new Combo(parent, SWT.READ_ONLY);
1054         GridDataFactory.fillDefaults().align(SWT.CENTER, SWT.CENTER).applyTo(sizeMapCombo);
1055         sizeMapCombo.setItems(sizeMaps.keySet().toArray(new String[sizeMaps.keySet().size()]));
1056         addSelectionListener(sizeMapCombo);
1057         
1058         Button defaultButton = new Button(parent, SWT.CHECK);
1059         GridDataFactory.fillDefaults().align(SWT.CENTER, SWT.CENTER).applyTo(defaultButton);
1060         addSelectionListener(defaultButton);
1061         defaultButton.addSelectionListener(new SelectionAdapter() {
1062             
1063             @Override
1064             public void widgetSelected(SelectionEvent e) {
1065                 int index = variableCombo.getSelectionIndex();
1066                 if (index >= 0) {
1067                     String key = variableCombo.getItem(index);
1068                     DynamicSizeContribution cont = sizeContributions.get(key);
1069                     
1070                     minText.setText(Double.toString(cont.getDefaultMin()));
1071                     minText.setEnabled(!defaultButton.getSelection());
1072                     maxText.setText(Double.toString(cont.getDefaultMax()));
1073                     maxText.setEnabled(!defaultButton.getSelection());
1074                     unit.setText(cont.getUnit());
1075                     
1076                     String[] items = sizeMapCombo.getItems();
1077                     for (int i = 0; i < items.length; i++) {
1078                         String item = items[i];
1079                         if (item.equals(cont.getDefaultSizeMap().getLabel())) {
1080                             sizeMapCombo.select(i);
1081                             break;
1082                         }
1083                     }
1084                     sizeMapCombo.setEnabled(!defaultButton.getSelection());
1085                 }
1086             }
1087         });
1088         
1089         variableCombo.addSelectionListener(new SelectionAdapter() {
1090             
1091             @Override
1092             public void widgetSelected(SelectionEvent e) {
1093                 // handle update for others
1094                 String key = variableCombo.getItem(variableCombo.getSelectionIndex());
1095                 DynamicSizeContribution cont = sizeContributions.get(key);
1096                 
1097                 if (minText.getText().isEmpty()) {
1098                     minText.setText(Double.toString(cont.getDefaultMin()));
1099                 }
1100                 if (maxText.getText().isEmpty()) {
1101                     maxText.setText(Double.toString(cont.getDefaultMax()));
1102                 }
1103                 unit.setText(cont.getUnit());
1104                 
1105                 String[] items = sizeMapCombo.getItems();
1106                 for (int i = 0; i < items.length; i++) {
1107                     String item = items[i];
1108                     if (item.equals(cont.getDefaultSizeMap().getLabel())) {
1109                         sizeMapCombo.select(i);
1110                         break;
1111                     }
1112                 }
1113                 
1114                 defaultButton.setSelection(true);
1115             }
1116         });
1117         
1118         sizingRows.put(object.getSizingObject().getName(), new SizingObjectRow(label, usedButton, variableCombo, minText, maxText, unit, sizeMapCombo, defaultButton));
1119         
1120         return new Supplier<Pair<String, DynamicSizeContribution>>() {
1121
1122             @Override
1123             public Pair<String, DynamicSizeContribution> get() {
1124                 int selectionIndex = variableCombo.getSelectionIndex();
1125                 if (selectionIndex >= 0) {
1126                     String key = variableCombo.getItem(selectionIndex);
1127                     DynamicSizeContribution cont = sizeContributions.get(key);
1128                     if (cont != null) {
1129                         String sizeMap = sizeMapCombo.getItem(sizeMapCombo.getSelectionIndex());
1130                         try {
1131                             Map<String, DynamicSizeMap> sizeMaps = Simantics.getSession().syncRequest(new UniqueRead<Map<String, DynamicSizeMap>>() {
1132         
1133                                 @Override
1134                                 public Map<String, DynamicSizeMap> perform(ReadGraph graph) throws DatabaseException {
1135                                     return DynamicVisualisationsContributions.dynamicSizeMaps(graph);
1136                                 }
1137                             });
1138                             DynamicSizeMap dColorMap = sizeMaps.get(sizeMap);
1139                             String label = variableCombo.getItem(variableCombo.getSelectionIndex());
1140                             
1141                             DynamicSizeContribution dsc = new DynamicSizeContribution(label, cont.getModuleName(), cont.getAttributeName(), unit.getText(), cont.getVariableGain(), cont.getVariableBias(), dColorMap, Double.parseDouble(minText.getText()), Double.parseDouble(maxText.getText()));
1142                             dsc.setUsed(usedButton.getSelection());
1143                             dsc.setUseDefault(defaultButton.getSelection());
1144                             
1145                             return Pair.make(object.getSizingObject().getName(), dsc);
1146                         } catch (DatabaseException e) {
1147                             LOGGER.error("Could not get DynamicColorContribution", e);
1148                         }
1149                     }
1150                 }
1151                 return null;
1152             }
1153         };
1154     }
1155     
1156     private void initializeColorBars(Composite parent) {
1157         Group group = new Group(parent, SWT.NONE);
1158         group.setText("Color Bars");
1159         GridDataFactory.fillDefaults().grab(true, false).applyTo(group);
1160         GridLayoutFactory.fillDefaults().numColumns(8).margins(5, 5).applyTo(group);
1161         
1162         createColorBars(group);
1163     }
1164     
1165     private void createColorBars(Composite parent) {
1166         
1167         showColorButton = new Button(parent, SWT.CHECK);
1168         showColorButton.setText("Show");
1169         addSelectionListener(showColorButton);
1170         
1171         colorTicksButton = new Button(parent, SWT.CHECK);
1172         colorTicksButton.setText("Ticks");
1173         addSelectionListener(colorTicksButton);
1174         
1175         colorGradientButton = new Button(parent, SWT.CHECK);
1176         colorGradientButton.setText("Gradients");
1177         addSelectionListener(colorGradientButton);
1178         
1179         Label label = new Label(parent, SWT.NONE);
1180         label.setText("Location");
1181         colorLocationCombo = new Combo(parent, SWT.READ_ONLY);
1182         String[] colorLocationItems = Stream.of(ColorBarsLocation.values()).map(size -> size.toString()).toArray(String[]::new);
1183         colorLocationCombo.setItems(colorLocationItems);
1184         if (colorLocationItems.length > 0) {
1185             colorLocationCombo.select(0);
1186         }
1187         addSelectionListener(colorLocationCombo);
1188         
1189         label = new Label(parent, SWT.NONE);
1190         label.setText("Size");
1191         colorSizeCombo = new Combo(parent, SWT.READ_ONLY);
1192         String[] colorSizeItems = Stream.of(ColorBarsSize.values()).map(size -> size.toString()).toArray(String[]::new);
1193         colorSizeCombo.setItems(colorSizeItems);
1194         if (colorSizeItems.length > 0) {
1195             colorSizeCombo.select(0);
1196         }
1197         addSelectionListener(colorSizeCombo);
1198     }
1199
1200     private void initializeObjectSizes(Composite parent) {
1201         Group group = new Group(parent, SWT.NONE);
1202         group.setText("Object Sizes");
1203         GridDataFactory.fillDefaults().grab(true, false).applyTo(group);
1204         GridLayoutFactory.fillDefaults().numColumns(8).margins(5, 5).applyTo(group);
1205         
1206         {
1207             createSizingObjectHeaderRow(group);
1208             createObjectSizes(group);
1209         }
1210     }
1211     
1212     private void createObjectSizes(Composite parent) {
1213         
1214         sizeSuppliers = new ArrayList<>(); 
1215         try {
1216             Pair<Collection<DynamicSizingObject>, Map<String, DynamicSizeMap>> resultSizing = Simantics.getSession().syncRequest(new UniqueRead<Pair<Collection<DynamicSizingObject>, Map<String, DynamicSizeMap>>>() {
1217
1218                 @Override
1219                 public Pair<Collection<DynamicSizingObject>, Map<String, DynamicSizeMap>> perform(ReadGraph graph) throws DatabaseException {
1220                     Map<String, DynamicSizeMap> dynamicSizeMaps = DynamicVisualisationsContributions.dynamicSizeMaps(graph);
1221                     return Pair.make(DynamicVisualisationsContributions.dynamicSizingObjects(graph), dynamicSizeMaps);
1222                 }
1223             });
1224             
1225             for (DynamicSizingObject object : resultSizing.first) {
1226                 sizeSuppliers.add(createSizingObjectRow(parent, object, resultSizing.second));
1227             }
1228         } catch (DatabaseException e) {
1229             LOGGER.error("Could not create object sizes", e);
1230         }
1231     }
1232
1233     private void initializeSizeBars(Composite parent) {
1234         Group group = new Group(parent, SWT.NONE);
1235         group.setText("Size Bars");
1236         GridDataFactory.fillDefaults().grab(true, false).applyTo(group);
1237         GridLayoutFactory.fillDefaults().numColumns(8).margins(5, 5).applyTo(group);
1238         
1239         createSizeBars(group);
1240     }
1241
1242     private void createSizeBars(Composite parent) {
1243         showSizeButton = new Button(parent, SWT.CHECK);
1244         showSizeButton.setText("Show");
1245         addSelectionListener(showSizeButton);
1246         
1247         sizeTicksButton = new Button(parent, SWT.CHECK);
1248         sizeTicksButton.setText("Ticks");
1249         addSelectionListener(sizeTicksButton);
1250         
1251         sizeGradientButton = new Button(parent, SWT.CHECK);
1252         sizeGradientButton.setText("Gradient");
1253         addSelectionListener(sizeGradientButton);
1254         
1255         Label label = new Label(parent, SWT.NONE);
1256         label.setText("Location");
1257         sizeLocationCombo = new Combo(parent, SWT.READ_ONLY);
1258         String[] sizeLocationItems = Stream.of(SizeBarsLocation.values()).map(size -> size.toString()).toArray(String[]::new);
1259         sizeLocationCombo.setItems(sizeLocationItems);
1260         if (sizeLocationItems.length > 0) {
1261             sizeLocationCombo.select(0);
1262         }
1263         addSelectionListener(sizeLocationCombo);
1264         
1265         label = new Label(parent, SWT.NONE);
1266         label.setText("Size");
1267         sizeSizeCombo = new Combo(parent, SWT.READ_ONLY);
1268         String[] sizeSizeItems = Stream.of(SizeBarsSize.values()).map(size -> size.toString()).toArray(String[]::new);
1269         sizeSizeCombo.setItems(sizeSizeItems);
1270         if (sizeSizeItems.length > 0) {
1271             sizeSizeCombo.select(0);
1272         }
1273         addSelectionListener(sizeSizeCombo);
1274     }
1275
1276     private void addSelectionListener(Widget widget) {
1277         if (widget instanceof Button) {
1278             ((Button) widget).addSelectionListener(new SelectionAdapter() {
1279
1280                 @Override
1281                 public void widgetSelected(SelectionEvent e) {
1282                     persistCurrentVisualisationTemplateIfAvailable();
1283                 }
1284             });
1285         } else if (widget instanceof Combo) {
1286             ((Combo) widget).addSelectionListener(new SelectionAdapter() {
1287
1288                 @Override
1289                 public void widgetSelected(SelectionEvent e) {
1290                     persistCurrentVisualisationTemplateIfAvailable();
1291                 }
1292             });
1293         } else if (widget instanceof Text) {
1294             ((Text) widget).addFocusListener(new FocusAdapter() {
1295                 
1296                 @Override
1297                 public void focusLost(FocusEvent e) {
1298                     persistCurrentVisualisationTemplateIfAvailable();
1299                 }
1300             });
1301             ((Text) widget).addKeyListener(new KeyAdapter() {
1302                 
1303                 @Override
1304                 public void keyReleased(KeyEvent e) {
1305                     if(e.keyCode == SWT.CR || e.keyCode == SWT.LF) {
1306                         persistCurrentVisualisationTemplateIfAvailable();
1307                     }
1308                 }
1309             });
1310         }
1311     }
1312
1313     public void setParentResource(Resource parentResource) {
1314         if (this.parentResource != parentResource) {
1315             this.parentResource = parentResource;
1316             updateListening();
1317         }
1318         saveVisualisationTemplateAsButton.setEnabled(parentResource != null);
1319     }
1320
1321     private void updateListening() {
1322         if (visualisationsListener != null)
1323             visualisationsListener.dispose();
1324         visualisationsListener = new VisualisationsListener(this);
1325         Simantics.getSession().asyncRequest(new DynamicVisualisationsRequest(parentResource), visualisationsListener);
1326         
1327         if (listener != null)
1328             listener.dispose();
1329         listener = new VisualisationListener(this);
1330         Simantics.getSession().asyncRequest(new ActiveDynamicVisualisationsRequest(parentResource), listener);
1331     }
1332
1333     private static class VisualisationsListener implements Listener<Collection<NamedResource>> {
1334
1335         private static final Logger LOGGER = LoggerFactory.getLogger(VisualisationsListener.class);
1336
1337         private boolean disposed;
1338         private DynamicVisualisationsUI ui;
1339         
1340         public VisualisationsListener(DynamicVisualisationsUI ui) {
1341             this.ui = ui;
1342         }
1343
1344         @Override
1345         public void execute(Collection<NamedResource> result) {
1346             ui.updateVisualisations(result);
1347         }
1348
1349         @Override
1350         public void exception(Throwable t) {
1351             LOGGER.error("Could not listen visualisation", t);
1352         }
1353
1354         @Override
1355         public boolean isDisposed() {
1356             return disposed || ui.getParent().isDisposed();
1357         }
1358
1359         public void dispose() {
1360             this.disposed = true;
1361         }
1362     }
1363     
1364     private static class VisualisationListener implements Listener<DynamicVisualisation> {
1365
1366         private static final Logger LOGGER = LoggerFactory.getLogger(VisualisationListener.class);
1367
1368         private boolean disposed;
1369         private DynamicVisualisationsUI ui;
1370         
1371         public VisualisationListener(DynamicVisualisationsUI ui) {
1372             this.ui = ui;
1373         }
1374
1375         @Override
1376         public void execute(DynamicVisualisation result) {
1377             ui.updateVisualisation(result);
1378         }
1379
1380         @Override
1381         public void exception(Throwable t) {
1382             LOGGER.error("Could not listen visualisation", t);
1383         }
1384
1385         @Override
1386         public boolean isDisposed() {
1387             return disposed ||ui.getParent().isDisposed();
1388         }
1389
1390         public void dispose() {
1391             this.disposed = true;
1392         }
1393     }
1394
1395     public void updateVisualisation(DynamicVisualisation result) {
1396         this.visualisation = result;
1397         Display.getDefault().asyncExec(() -> {
1398             if (getParent().isDisposed())
1399                 return;
1400             
1401             removeVisualisationTemplateButton.setEnabled(visualisation != null && visualisation.getVisualisationResource() != null);
1402             
1403             if (visualisation != null) {
1404                 String[] items = templateSelectionCombo.getItems();
1405                 for (int i = 0; i < items.length; i++) {
1406                     if (visualisation.getName().equals(items[i])) {
1407                         templateSelectionCombo.select(i);
1408                         break;
1409                     }
1410                 }
1411                 
1412                 Map<String, DynamicColorContribution> colorContributions = visualisation.getColorContributions();
1413                 for (Entry<String, DynamicColorContribution> entry : colorContributions.entrySet()) {
1414                     
1415                     ColoringObjectRow coloringObjectRow = coloringRows.get(entry.getKey());
1416                     if (coloringObjectRow != null) {
1417                         
1418                         coloringObjectRow.update(entry.getValue());
1419                         
1420                     } else {
1421                         LOGGER.info("No coloring object visualisation row for key {}", entry.getKey());
1422                     }
1423                 }
1424                 ColorBarOptions colorOptions = visualisation.getColorBarOptions();
1425                 showColorButton.setSelection(colorOptions.isShowColorBars());
1426                 colorTicksButton.setSelection(colorOptions.isShowColorBarsTicks());
1427                 colorGradientButton.setSelection(colorOptions.isUseGradients());
1428                 for (int i = 0; i < colorLocationCombo.getItems().length; i++) {
1429                     String item = colorLocationCombo.getItem(i);
1430                     if (item.equals(colorOptions.getLocation().toString())) {
1431                         colorLocationCombo.select(i);
1432                         break;
1433                     }
1434                 }
1435                 for (int i = 0; i < colorSizeCombo.getItems().length; i++) {
1436                     String item = colorSizeCombo.getItem(i);
1437                     if (item.equals(colorOptions.getSize().toString())) {
1438                         colorSizeCombo.select(i);
1439                         break;
1440                     }
1441                 }
1442                 
1443                 Map<String, DynamicSizeContribution> sizeContributions = visualisation.getSizeContributions();
1444                 for (Entry<String, DynamicSizeContribution> entry : sizeContributions.entrySet()) {
1445                     
1446                     SizingObjectRow sizingObjectRow = sizingRows.get(entry.getKey());
1447                     if (sizingObjectRow != null) {
1448                         
1449                         sizingObjectRow.update(entry.getValue());
1450                         
1451                     } else {
1452                         LOGGER.info("No sizing object visualisation row for key {}", entry.getKey());
1453                     }
1454                 }
1455                 SizeBarOptions sizeOptions = visualisation.getSizeBarOptions();
1456                 showSizeButton.setSelection(sizeOptions.isShowSizeBars());
1457                 sizeTicksButton.setSelection(sizeOptions.isShowSizeBarsTicks());
1458                 sizeGradientButton.setSelection(sizeOptions.isUseGradients());
1459                 for (int i = 0; i < sizeLocationCombo.getItems().length; i++) {
1460                     String item = sizeLocationCombo.getItem(i);
1461                     if (item.equals(sizeOptions.getLocation().toString())) {
1462                         sizeLocationCombo.select(i);
1463                         break;
1464                     }
1465                 }
1466                 for (int i = 0; i < sizeSizeCombo.getItems().length; i++) {
1467                     String item = sizeSizeCombo.getItem(i);
1468                     if (item.equals(sizeOptions.getSize().toString())) {
1469                         sizeSizeCombo.select(i);
1470                         break;
1471                     }
1472                 }
1473             }
1474         });
1475     }
1476
1477     public void updateVisualisations(Collection<NamedResource> result) {
1478         this.visualisations = result;
1479         
1480         Display.getDefault().asyncExec(() -> {
1481             if (getParent().isDisposed())
1482                 return;
1483             templateSelectionCombo.setItems(visualisations.stream().map(NamedResource::getName).collect(Collectors.toList()).toArray(new String[visualisations.size()]));
1484             
1485             if (visualisation != null) {
1486                 String[] items = templateSelectionCombo.getItems();
1487                 for (int i = 0; i < items.length; i++) {
1488                     if (visualisation.getName().equals(items[i])) {
1489                         templateSelectionCombo.select(i);
1490                         break;
1491                     }
1492                 }
1493             }
1494             
1495         });
1496     }
1497     
1498     public Composite getParent() {
1499         return parent;
1500     }
1501 }