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