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