]> gerrit.simantics Code Review - simantics/district.git/blob
14a8f00fa61c0646d84e60107674f78dd1a0cb26
[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.events.SelectionAdapter;
22 import org.eclipse.swt.events.SelectionEvent;
23 import org.eclipse.swt.widgets.Button;
24 import org.eclipse.swt.widgets.Combo;
25 import org.eclipse.swt.widgets.Composite;
26 import org.eclipse.swt.widgets.Display;
27 import org.eclipse.swt.widgets.Group;
28 import org.eclipse.swt.widgets.Label;
29 import org.eclipse.swt.widgets.Shell;
30 import org.eclipse.swt.widgets.Text;
31 import org.simantics.Simantics;
32 import org.simantics.db.ReadGraph;
33 import org.simantics.db.Resource;
34 import org.simantics.db.WriteGraph;
35 import org.simantics.db.common.NamedResource;
36 import org.simantics.db.common.request.UniqueRead;
37 import org.simantics.db.common.request.WriteRequest;
38 import org.simantics.db.exception.DatabaseException;
39 import org.simantics.db.layer0.util.RemoverUtil;
40 import org.simantics.db.procedure.Listener;
41 import org.simantics.district.network.DistrictNetworkUtil;
42 import org.simantics.district.network.profile.ActiveDynamicVisualisationsRequest;
43 import org.simantics.district.network.profile.DynamicVisualisationsRequest;
44 import org.simantics.district.network.visualisations.DynamicVisualisationsContributions;
45 import org.simantics.district.network.visualisations.DynamicVisualisationsContributions.DynamicColoringObject;
46 import org.simantics.district.network.visualisations.DynamicVisualisationsContributions.DynamicSizingObject;
47 import org.simantics.district.network.visualisations.model.ColorBarOptions;
48 import org.simantics.district.network.visualisations.model.ColorBarOptions.ColorBarsLocation;
49 import org.simantics.district.network.visualisations.model.ColorBarOptions.ColorBarsSize;
50 import org.simantics.district.network.visualisations.model.DynamicColorContribution;
51 import org.simantics.district.network.visualisations.model.DynamicColorMap;
52 import org.simantics.district.network.visualisations.model.DynamicSizeContribution;
53 import org.simantics.district.network.visualisations.model.DynamicSizeMap;
54 import org.simantics.district.network.visualisations.model.DynamicVisualisation;
55 import org.simantics.district.network.visualisations.model.SizeBarOptions;
56 import org.simantics.district.network.visualisations.model.SizeBarOptions.SizeBarsLocation;
57 import org.simantics.district.network.visualisations.model.SizeBarOptions.SizeBarsSize;
58 import org.simantics.utils.datastructures.Pair;
59 import org.simantics.utils.ui.dialogs.ShowError;
60 import org.slf4j.Logger;
61 import org.slf4j.LoggerFactory;
62
63 public class DynamicVisualisationsUI extends Composite {
64
65     private static final Logger LOGGER = LoggerFactory.getLogger(DynamicVisualisationsUI.class);
66
67     private Resource parentResource;
68     private VisualisationListener listener;
69     private DynamicVisualisation visualisation;
70
71     private Button showSizeButton;
72     private Button sizeTicksButton;
73     private Button sizeGradientButton;
74     private Combo sizeLocationCombo;
75     private Combo sizeSizeCombo;
76     private Button showColorButton;
77     private Button colorTicksButton;
78     private Button colorGradientButton;
79     private Combo colorLocationCombo;
80     private Combo colorSizeCombo;
81
82     private Combo templateSelectionCombo;
83
84     private List<Supplier<Pair<String, DynamicColorContribution>>> colorSuppliers;
85
86     private Button removeVisualisationTemplateButton;
87
88         private Button applyButton;
89
90     public DynamicVisualisationsUI(Composite parent, int style) {
91         super(parent, style);
92 //        ScrolledComposite scrolledComposite = new ScrolledComposite(this, SWT.V_SCROLL);
93 //        scrolledComposite.setLayout(new GridLayout(1, false));
94 //        scrolledComposite.setLayoutData(new GridData(SWT.FILL, SWT.FILL, true, true));
95 //
96 //        Composite firstContent = new Composite(scrolledComposite, SWT.NONE);
97 //        firstContent.setLayout(new GridLayout(1, false));
98 //        firstContent.setLayoutData(new GridData(SWT.FILL, SWT.FILL, true, true));
99         
100         defaultInitializeUI(this);
101         
102 //        scrolledComposite.setContent(firstContent);
103 //        scrolledComposite.setExpandHorizontal(true);
104 //        scrolledComposite.setExpandVertical(true);
105 //        scrolledComposite.setMinSize(firstContent.computeSize(SWT.DEFAULT, SWT.DEFAULT));
106         
107     }
108
109     private void defaultInitializeUI(Composite parent) {
110         
111         GridDataFactory.fillDefaults().grab(true, true).applyTo(parent);
112         GridLayoutFactory.fillDefaults().numColumns(1).margins(5, 5).applyTo(parent);
113         
114         Composite selectionComposite = new Composite(parent, SWT.NONE);
115         GridDataFactory.fillDefaults().grab(true, false).applyTo(selectionComposite);
116         GridLayoutFactory.fillDefaults().numColumns(2).margins(5, 5).applyTo(selectionComposite);
117         
118         Label templateNameLabel = new Label(selectionComposite, SWT.NONE);
119         templateNameLabel.setText("Visualisation template");
120         templateSelectionCombo = new Combo(selectionComposite, SWT.READ_ONLY);
121         GridDataFactory.fillDefaults().grab(true, false).applyTo(templateSelectionCombo);
122         templateSelectionCombo.addSelectionListener(new SelectionAdapter() {
123             
124             @Override
125             public void widgetSelected(SelectionEvent e) {
126                 String item = templateSelectionCombo.getItem(templateSelectionCombo.getSelectionIndex());
127                 for (NamedResource template : visualisations) {
128                     if (item.equals(template.getName())) {
129                         Simantics.getSession().asyncRequest(new WriteRequest() {
130                             
131                             @Override
132                             public void perform(WriteGraph graph) throws DatabaseException {
133                                 Resource vf = DistrictNetworkUtil.getVisualisationFolder(graph, parentResource);
134                                 DistrictNetworkUtil.setActiveVisualisation(graph, vf, template.getResource());
135                             }
136                         });
137                         break;
138                     }
139                 }
140             }
141         });
142         
143         Composite coloringObjectsComposite = new Composite(parent, SWT.NONE);
144         GridDataFactory.fillDefaults().grab(true, false).applyTo(coloringObjectsComposite);
145         GridLayoutFactory.fillDefaults().numColumns(1).applyTo(coloringObjectsComposite);
146         initializeColoringObjects(coloringObjectsComposite);
147         
148         Composite colorBarsComposite = new Composite(parent, SWT.NONE);
149         GridDataFactory.fillDefaults().grab(true, false).applyTo(colorBarsComposite);
150         GridLayoutFactory.fillDefaults().numColumns(1).applyTo(colorBarsComposite);
151         initializeColorBars(colorBarsComposite);
152         
153         Composite objectSizesComposite = new Composite(parent, SWT.NONE);
154         GridDataFactory.fillDefaults().grab(true, false).applyTo(objectSizesComposite);
155         GridLayoutFactory.fillDefaults().numColumns(1).applyTo(objectSizesComposite);
156         initializeObjectSizes(objectSizesComposite);
157         
158         Composite sizeBarsComposite = new Composite(parent, SWT.NONE);
159         GridDataFactory.fillDefaults().grab(true, false).applyTo(sizeBarsComposite);
160         GridLayoutFactory.fillDefaults().numColumns(1).applyTo(sizeBarsComposite);
161         initializeSizeBars(sizeBarsComposite);
162         
163         Composite buttonBarsComposite = new Composite(parent, SWT.NONE);
164         GridDataFactory.fillDefaults().grab(true, false).applyTo(buttonBarsComposite);
165         GridLayoutFactory.fillDefaults().numColumns(3).applyTo(buttonBarsComposite);
166         
167         Button saveVisualisationTemplateAsButton = new Button(buttonBarsComposite, SWT.NONE);
168         saveVisualisationTemplateAsButton.setText("Save as visualisation template");
169         saveVisualisationTemplateAsButton.addSelectionListener(new SelectionAdapter() {
170             
171             @Override
172             public void widgetSelected(SelectionEvent e) {
173                 showSaveVisualisationTemplateDialog(e.widget.getDisplay().getActiveShell());
174             }
175         });
176         
177         applyButton = new Button(buttonBarsComposite, SWT.NONE);
178         applyButton.setText("Apply");
179         applyButton.setEnabled(visualisation != null);
180         applyButton.addSelectionListener(new SelectionAdapter() {
181             
182             @Override
183             public void widgetSelected(SelectionEvent e) {
184                 try {
185                     persistVisualisationTemplate(visualisation.getName(), Optional.of(visualisation.getVisualisationResource()));
186                 } catch (Exception e1) {
187                     LOGGER.error("Could not persist visualisation template", e1);
188                     ShowError.showError("Could not persist visualisation template", e1.getMessage(), e1);
189                 }
190             }
191         });
192         
193         removeVisualisationTemplateButton = new Button(buttonBarsComposite, SWT.NONE);
194         removeVisualisationTemplateButton.setText("Remove");
195         removeVisualisationTemplateButton.setEnabled(visualisation != null && visualisation.getVisualisationResource() != null);
196         removeVisualisationTemplateButton.addSelectionListener(new SelectionAdapter() {
197             
198             @Override
199             public void widgetSelected(SelectionEvent e) {
200                 removeVisualisationTemplate(visualisation.getName(), Optional.of(visualisation.getVisualisationResource()));
201             }
202         });
203         
204     }
205     
206     protected void removeVisualisationTemplate(String name, Optional<Resource> of) {
207         if (of.isPresent()) {
208             Resource visualisation = of.get();
209             Simantics.getSession().asyncRequest(new WriteRequest() {
210                 
211                 @Override
212                 public void perform(WriteGraph graph) throws DatabaseException {
213                     RemoverUtil.remove(graph, visualisation);
214                 }
215             });
216         }
217     }
218
219     private void showSaveVisualisationTemplateDialog(Shell shell) {
220         
221         InputDialog dialog = new InputDialog(shell, "Save visualisation template", "Give template a name", "", new IInputValidator() {
222             
223             @Override
224             public String isValid(String newText) {
225                 if (newText == null || newText.isEmpty())
226                     return "Name cannot be empty";
227                 return null;
228             }
229         });
230         
231         if (dialog.open() == Dialog.OK) {
232             String name = dialog.getValue();
233             try {
234                 persistVisualisationTemplate(name, Optional.empty());
235             } catch (Exception e) {
236                 LOGGER.error("Could not persist visualisation template", e);
237                 ShowError.showError("Could not persist visualisation template", e.getMessage(), e);
238             }
239         }
240     }
241
242     private void persistVisualisationTemplate(String templateName, Optional<Resource> existing) throws Exception {
243         
244         List<Pair<String, DynamicColorContribution>> colorCollect = colorSuppliers.stream().map(s -> s.get()).filter(Objects::nonNull).collect(Collectors.toList());
245
246         String colorLocation = colorLocationCombo.getItem(colorLocationCombo.getSelectionIndex());
247         String colorSize = colorSizeCombo.getItem(colorSizeCombo.getSelectionIndex());
248         
249         ColorBarOptions colorBarOptions = new ColorBarOptions()
250                 .showColorBars(showColorButton.getSelection())
251                 .showColorBarsTicks(colorTicksButton.getSelection())
252                 .useGradients(colorGradientButton.getSelection())
253                 .withLocation(ColorBarsLocation.valueOf(colorLocation))
254                 .withSize(ColorBarsSize.valueOf(colorSize));
255         
256         List<Pair<String, DynamicSizeContribution>> sizeCollect = sizeSuppliers.stream().map(s -> s.get()).filter(Objects::nonNull).collect(Collectors.toList());
257         
258         String sizeLocation = sizeLocationCombo.getItem(sizeLocationCombo.getSelectionIndex());
259         String sizeSize = sizeSizeCombo.getItem(sizeSizeCombo.getSelectionIndex());
260         
261         SizeBarOptions sizeBarOptions = new SizeBarOptions()
262                 .showSizeBars(showSizeButton.getSelection())
263                 .showSizeBarsTicks(sizeTicksButton.getSelection())
264                 .useGradients(sizeGradientButton.getSelection())
265                 .withLocation(SizeBarsLocation.valueOf(sizeLocation))
266                 .withSize(SizeBarsSize.valueOf(sizeSize));
267         
268         Simantics.getSession().asyncRequest(new WriteRequest() {
269             
270             @Override
271             public void perform(WriteGraph graph) throws DatabaseException {
272                 Resource exist;
273                 if (existing.isPresent()) {
274                     exist = existing.get();
275                 } else {
276                     exist = DistrictNetworkUtil.createVisualisation(graph, parentResource, templateName);
277                 }
278                 
279                 DistrictNetworkUtil.setColorContributions(graph, exist, colorCollect);
280                 
281                 DistrictNetworkUtil.setColorBarOptions(graph, exist, colorBarOptions);
282                 DistrictNetworkUtil.setSizeContributions(graph, exist, sizeCollect);
283                 DistrictNetworkUtil.setSizeBarOptions(graph, exist, sizeBarOptions);
284             }
285         });
286     }
287
288     private void initializeColoringObjects(Composite parent) {
289         Group group = new Group(parent, SWT.NONE);
290         group.setText("Coloring Objects");
291         GridDataFactory.fillDefaults().grab(true, false).applyTo(group);
292         GridLayoutFactory.fillDefaults().numColumns(8).margins(5, 5).applyTo(group);
293         
294         {
295             createColoringObjectHeaderRow(group);
296         }
297         colorSuppliers = new ArrayList<>();
298         {
299             try {
300                 Pair<Collection<DynamicColoringObject>, Map<String, DynamicColorMap>> result = Simantics.getSession().syncRequest(new UniqueRead<Pair<Collection<DynamicColoringObject>, Map<String, DynamicColorMap>>>() {
301
302                     @Override
303                     public Pair<Collection<DynamicColoringObject>, Map<String, DynamicColorMap>> perform(ReadGraph graph) throws DatabaseException {
304                         Map<String, DynamicColorMap> dynamicColorMaps = DynamicVisualisationsContributions.dynamicColorMaps(graph);
305                         return Pair.make(DynamicVisualisationsContributions.dynamicColoringObjects(graph), dynamicColorMaps);
306                     }
307                 });
308                 
309                 for (DynamicColoringObject object : result.first) {
310                     colorSuppliers.add(createColoringObjectRow(group, object, result.second));
311                 }
312
313             } catch (DatabaseException e) {
314                 LOGGER.error("Could not create coloring objecst", e);
315             }
316         }
317     }
318     
319     private void createColoringObjectHeaderRow(Composite parent) {
320
321         Label label = new Label(parent, SWT.NONE);
322         label.setText("Label");
323         GridDataFactory.fillDefaults().grab(true, false).align(SWT.CENTER, SWT.CENTER).applyTo(label);
324         
325         label = new Label(parent, SWT.NONE);
326         label.setText("Used");
327         GridDataFactory.fillDefaults().grab(true, false).align(SWT.CENTER, SWT.CENTER).applyTo(label);
328         
329         label = new Label(parent, SWT.NONE);
330         label.setText("Variable");
331         GridDataFactory.fillDefaults().grab(true, false).align(SWT.CENTER, SWT.CENTER).applyTo(label);
332         
333         label = new Label(parent, SWT.NONE);
334         label.setText("Min");
335         GridDataFactory.fillDefaults().grab(true, false).align(SWT.CENTER, SWT.CENTER).applyTo(label);
336         
337         label = new Label(parent, SWT.NONE);
338         label.setText("Max");
339         GridDataFactory.fillDefaults().grab(true, false).align(SWT.CENTER, SWT.CENTER).applyTo(label);
340         
341         label = new Label(parent, SWT.NONE);
342         label.setText("Unit");
343         GridDataFactory.fillDefaults().grab(true, false).align(SWT.CENTER, SWT.CENTER).applyTo(label);
344         
345         label = new Label(parent, SWT.NONE);
346         label.setText("ColorMap");
347         GridDataFactory.fillDefaults().grab(true, false).align(SWT.CENTER, SWT.CENTER).applyTo(label);
348         
349         label = new Label(parent, SWT.NONE);
350         label.setText("Default");
351         GridDataFactory.fillDefaults().grab(true, false).align(SWT.CENTER, SWT.CENTER).applyTo(label);
352     }
353
354     private Map<String, ColoringObjectRow> coloringRows = new HashMap<>();
355     private Map<String, SizingObjectRow> sizingRows = new HashMap<>();
356
357     private VisualisationsListener visualisationsListener;
358
359     private Collection<NamedResource> visualisations;
360
361     private List<Supplier<Pair<String, DynamicSizeContribution>>> sizeSuppliers;
362
363     private static class ColoringObjectRow {
364         
365         private final Label label;
366         private final Button usedButton;
367         private final Combo variableCombo;
368         private final Text minText;
369         private final Text maxText;
370         private final Text unit;
371         private final Combo colorMapCombo;
372         private final Button defaultButton;
373         
374         public ColoringObjectRow(Label label, Button usedButton, Combo variableCombo, Text minText, Text maxText, Text unit,
375                 Combo colorMapCombo, Button defaultButton) {
376             super();
377             this.label = label;
378             this.usedButton = usedButton;
379             this.variableCombo = variableCombo;
380             this.minText = minText;
381             this.maxText = maxText;
382             this.unit = unit;
383             this.colorMapCombo = colorMapCombo;
384             this.defaultButton = defaultButton;
385         }
386
387         public void update(DynamicColorContribution colorContribution) {
388             String[] items = variableCombo.getItems();
389             for (int i = 0; i < items.length; i++) {
390                 if (colorContribution.getLabel().equals(items[i])) {
391                     variableCombo.select(i);
392                     break;
393                 }
394             }
395             minText.setText(Double.toString(colorContribution.getDefaultMin()));
396             maxText.setText(Double.toString(colorContribution.getDefaultMax()));
397             unit.setText(colorContribution.getUnit());
398             
399             String[] colorItems = colorMapCombo.getItems();
400             for (int i = 0; i < colorItems.length; i++) {
401                 
402                 if (colorContribution.getDefaultColorMap().getLabel().equals(colorItems[i])) {
403                     colorMapCombo.select(i);
404                     break;
405                 }
406             }
407             usedButton.setSelection(colorContribution.isUsed());
408             defaultButton.setSelection(colorContribution.isUseDefault());
409             
410             minText.setEnabled(!colorContribution.isUseDefault());
411             maxText.setEnabled(!colorContribution.isUseDefault());
412             colorMapCombo.setEnabled(!colorContribution.isUseDefault());
413         }
414     }
415
416     private static class SizingObjectRow {
417         
418         private final Label label;
419         private final Button usedButton;
420         private final Combo variableCombo;
421         private final Text minText;
422         private final Text maxText;
423         private final Label unit;
424         private final Combo sizeMapCombo;
425         private final Button defaultButton;
426         
427         public SizingObjectRow(Label label, Button usedButton, Combo variableCombo, Text minText, Text maxText, Label unit,
428                 Combo sizeMapCombo, Button defaultButton) {
429             super();
430             this.label = label;
431             this.usedButton = usedButton;
432             this.variableCombo = variableCombo;
433             this.minText = minText;
434             this.maxText = maxText;
435             this.unit = unit;
436             this.sizeMapCombo = sizeMapCombo;
437             this.defaultButton = defaultButton;
438         }
439
440         public void update(DynamicSizeContribution sizeContribution) {
441             String[] items = variableCombo.getItems();
442             for (int i = 0; i < items.length; i++) {
443                 if (sizeContribution.getLabel().equals(items[i])) {
444                     variableCombo.select(i);
445                     break;
446                 }
447             }
448             minText.setText(Double.toString(sizeContribution.getDefaultMin()));
449             maxText.setText(Double.toString(sizeContribution.getDefaultMax()));
450             unit.setText(sizeContribution.getUnit());
451             
452             String[] sizeItems = sizeMapCombo.getItems();
453             for (int i = 0; i < sizeItems.length; i++) {
454                 if (sizeContribution.getDefaultSizeMap().getLabel().equals(sizeItems[i])) {
455                     sizeMapCombo.select(i);
456                     break;
457                 }
458             }
459             usedButton.setSelection(sizeContribution.isUsed());
460             defaultButton.setSelection(sizeContribution.isUseDefault());
461             
462             minText.setEnabled(!sizeContribution.isUseDefault());
463             maxText.setEnabled(!sizeContribution.isUseDefault());
464             sizeMapCombo.setEnabled(!sizeContribution.isUseDefault());
465         }
466     }
467
468     private Supplier<Pair<String, DynamicColorContribution>> createColoringObjectRow(Composite parent, DynamicColoringObject object, Map<String, DynamicColorMap> colorMaps) {
469         Label label = new Label(parent, SWT.NONE);
470         label.setText(object.getColoringObject().getName());
471         GridDataFactory.fillDefaults().align(SWT.CENTER, SWT.CENTER).applyTo(label);
472         
473         Map<String, DynamicColorContribution> colorContributions = object.getColorContributions();
474         
475         Button usedButton = new Button(parent, SWT.CHECK);
476         GridDataFactory.fillDefaults().align(SWT.CENTER, SWT.CENTER).applyTo(usedButton);
477         
478         Combo variableCombo = new Combo(parent, SWT.READ_ONLY);
479         variableCombo.setItems(colorContributions.keySet().toArray(new String[colorContributions.size()]));
480         
481         GridDataFactory.fillDefaults().align(SWT.CENTER, SWT.CENTER).applyTo(variableCombo);
482         
483         Text minText = new Text(parent, SWT.BORDER);
484         GridDataFactory.fillDefaults().grab(true, false).hint(150, SWT.DEFAULT).align(SWT.CENTER, SWT.CENTER).applyTo(minText);
485         
486         Text maxText = new Text(parent, SWT.BORDER);
487         GridDataFactory.fillDefaults().grab(true, false).hint(150, SWT.DEFAULT).align(SWT.CENTER, SWT.CENTER).applyTo(maxText);
488         
489         Text unit = new Text(parent, SWT.READ_ONLY);
490         GridDataFactory.fillDefaults().grab(true, false).align(SWT.CENTER, SWT.CENTER).applyTo(unit);
491         
492         Combo colorMapCombo = new Combo(parent, SWT.READ_ONLY);
493         GridDataFactory.fillDefaults().align(SWT.CENTER, SWT.CENTER).applyTo(colorMapCombo);
494         colorMapCombo.setItems(colorMaps.keySet().toArray(new String[colorMaps.keySet().size()]));
495         
496         Button defaultButton = new Button(parent, SWT.CHECK);
497         GridDataFactory.fillDefaults().align(SWT.CENTER, SWT.CENTER).applyTo(defaultButton);
498         defaultButton.addSelectionListener(new SelectionAdapter() {
499             
500             @Override
501             public void widgetSelected(SelectionEvent e) {
502                 int index = variableCombo.getSelectionIndex();
503                 if (index >= 0) {
504                     String key = variableCombo.getItem(index);
505                     DynamicColorContribution cont = colorContributions.get(key);
506                     
507                     minText.setText(Double.toString(cont.getDefaultMin()));
508                     minText.setEnabled(!defaultButton.getSelection());
509                     maxText.setText(Double.toString(cont.getDefaultMax()));
510                     maxText.setEnabled(!defaultButton.getSelection());
511                     unit.setText(cont.getUnit());
512                     
513                     String[] items = colorMapCombo.getItems();
514                     for (int i = 0; i < items.length; i++) {
515                         String item = items[i];
516                         if (item.equals(cont.getDefaultColorMap().getLabel())) {
517                             colorMapCombo.select(i);
518                             break;
519                         }
520                     }
521                     colorMapCombo.setEnabled(!defaultButton.getSelection());
522                 }
523             }
524         });
525         
526         variableCombo.addSelectionListener(new SelectionAdapter() {
527             
528             @Override
529             public void widgetSelected(SelectionEvent e) {
530                 // handle update for others
531                 int index = variableCombo.getSelectionIndex();
532                 if (index >= 0) {
533                     String key = variableCombo.getItem(index);
534                     DynamicColorContribution cont = colorContributions.get(key);
535                     
536                     if (minText.getText().isEmpty()) {
537                         minText.setText(Double.toString(cont.getDefaultMin()));
538                     }
539                     if (maxText.getText().isEmpty()) {
540                         maxText.setText(Double.toString(cont.getDefaultMax()));
541                     }
542                     unit.setText(cont.getUnit());
543                     
544                     String[] items = colorMapCombo.getItems();
545                     for (int i = 0; i < items.length; i++) {
546                         String item = items[i];
547                         if (item.equals(cont.getDefaultColorMap().getLabel())) {
548                             colorMapCombo.select(i);
549                             break;
550                         }
551                     }
552                     
553                     defaultButton.setSelection(true);
554                 }
555             }
556         });
557         
558         coloringRows.put(object.getColoringObject().getName(), new ColoringObjectRow(label, usedButton, variableCombo, minText, maxText, unit, colorMapCombo, defaultButton));
559
560         return new Supplier<Pair<String, DynamicColorContribution>>() {
561
562             @Override
563             public Pair<String, DynamicColorContribution> get() {
564                 int selectionIndex = variableCombo.getSelectionIndex();
565                 if (selectionIndex >= 0) {
566                     String key = variableCombo.getItem(selectionIndex);
567                     DynamicColorContribution cont = colorContributions.get(key);
568                     if (cont != null) {
569                         String colorMap = colorMapCombo.getItem(colorMapCombo.getSelectionIndex());
570                         try {
571                             Map<String, DynamicColorMap> colorMaps = Simantics.getSession().syncRequest(new UniqueRead<Map<String, DynamicColorMap>>() {
572         
573                                 @Override
574                                 public Map<String, DynamicColorMap> perform(ReadGraph graph) throws DatabaseException {
575                                     return DynamicVisualisationsContributions.dynamicColorMaps(graph);
576                                 }
577                             });
578                             DynamicColorMap dColorMap = colorMaps.get(colorMap);
579                             String label = variableCombo.getItem(variableCombo.getSelectionIndex());
580                             
581                             DynamicColorContribution dcc = new DynamicColorContribution(label, cont.getModuleName(), cont.getAttributeName(), unit.getText(), cont.getVariableGain(), cont.getVariableBias(), dColorMap, Double.parseDouble(minText.getText()), Double.parseDouble(maxText.getText()));
582                             dcc.setUsed(usedButton.getSelection());
583                             dcc.setUseDefault(defaultButton.getSelection());
584                             
585                             return Pair.make(object.getColoringObject().getName(), dcc);
586                         } catch (DatabaseException e) {
587                             LOGGER.error("Could not get DynamicColorContribution", e);
588                         }
589                     }
590                 }
591                 return null;
592             }
593         };
594     }
595     
596     private void createSizingObjectHeaderRow(Composite parent) {
597
598         Label label = new Label(parent, SWT.NONE);
599         label.setText("Label");
600         GridDataFactory.fillDefaults().grab(true, false).align(SWT.CENTER, SWT.CENTER).applyTo(label);
601         
602         label = new Label(parent, SWT.NONE);
603         label.setText("Used");
604         GridDataFactory.fillDefaults().grab(true, false).align(SWT.CENTER, SWT.CENTER).applyTo(label);
605         
606         label = new Label(parent, SWT.NONE);
607         label.setText("Variable");
608         GridDataFactory.fillDefaults().grab(true, false).align(SWT.CENTER, SWT.CENTER).applyTo(label);
609         
610         label = new Label(parent, SWT.NONE);
611         label.setText("Min");
612         GridDataFactory.fillDefaults().grab(true, false).align(SWT.CENTER, SWT.CENTER).applyTo(label);
613         
614         label = new Label(parent, SWT.NONE);
615         label.setText("Max");
616         GridDataFactory.fillDefaults().grab(true, false).align(SWT.CENTER, SWT.CENTER).applyTo(label);
617         
618         label = new Label(parent, SWT.NONE);
619         label.setText("Unit");
620         GridDataFactory.fillDefaults().grab(true, false).align(SWT.CENTER, SWT.CENTER).applyTo(label);
621         
622         label = new Label(parent, SWT.NONE);
623         label.setText("SizeMap");
624         GridDataFactory.fillDefaults().grab(true, false).align(SWT.CENTER, SWT.CENTER).applyTo(label);
625         
626         label = new Label(parent, SWT.NONE);
627         label.setText("Default");
628         GridDataFactory.fillDefaults().grab(true, false).align(SWT.CENTER, SWT.CENTER).applyTo(label);
629     }
630
631     private Supplier<Pair<String, DynamicSizeContribution>> createSizingObjectRow(Composite parent, DynamicSizingObject object, Map<String, DynamicSizeMap> sizeMaps) {
632         Label label = new Label(parent, SWT.NONE);
633         label.setText(object.getSizingObject().getName());
634         GridDataFactory.fillDefaults().align(SWT.CENTER, SWT.CENTER).applyTo(label);
635         
636         Map<String, DynamicSizeContribution> sizeContributions = object.getSizeContributions();
637         
638         Button usedButton = new Button(parent, SWT.CHECK);
639         GridDataFactory.fillDefaults().align(SWT.CENTER, SWT.CENTER).applyTo(usedButton);
640         
641         Combo variableCombo = new Combo(parent, SWT.READ_ONLY);
642         variableCombo.setItems(sizeContributions.keySet().toArray(new String[sizeContributions.size()]));
643         
644         GridDataFactory.fillDefaults().align(SWT.CENTER, SWT.CENTER).applyTo(variableCombo);
645         
646         Text minText = new Text(parent, SWT.BORDER);
647         GridDataFactory.fillDefaults().align(SWT.CENTER, SWT.CENTER).applyTo(minText);
648         
649         Text maxText = new Text(parent, SWT.BORDER);
650         GridDataFactory.fillDefaults().align(SWT.CENTER, SWT.CENTER).applyTo(maxText);
651         
652         Label unit = new Label(parent, SWT.NONE);
653         GridDataFactory.fillDefaults().grab(true, false).align(SWT.CENTER, SWT.CENTER).applyTo(unit);
654         
655         Combo sizeMapCombo = new Combo(parent, SWT.READ_ONLY);
656         GridDataFactory.fillDefaults().align(SWT.CENTER, SWT.CENTER).applyTo(sizeMapCombo);
657         sizeMapCombo.setItems(sizeMaps.keySet().toArray(new String[sizeMaps.keySet().size()]));
658         
659         Button defaultButton = new Button(parent, SWT.CHECK);
660         GridDataFactory.fillDefaults().align(SWT.CENTER, SWT.CENTER).applyTo(defaultButton);
661         defaultButton.addSelectionListener(new SelectionAdapter() {
662             
663             @Override
664             public void widgetSelected(SelectionEvent e) {
665                 int index = variableCombo.getSelectionIndex();
666                 if (index >= 0) {
667                     String key = variableCombo.getItem(index);
668                     DynamicSizeContribution cont = sizeContributions.get(key);
669                     
670                     minText.setText(Double.toString(cont.getDefaultMin()));
671                     minText.setEnabled(!defaultButton.getSelection());
672                     maxText.setText(Double.toString(cont.getDefaultMax()));
673                     maxText.setEnabled(!defaultButton.getSelection());
674                     unit.setText(cont.getUnit());
675                     
676                     String[] items = sizeMapCombo.getItems();
677                     for (int i = 0; i < items.length; i++) {
678                         String item = items[i];
679                         if (item.equals(cont.getDefaultSizeMap().getLabel())) {
680                             sizeMapCombo.select(i);
681                             break;
682                         }
683                     }
684                     sizeMapCombo.setEnabled(!defaultButton.getSelection());
685                 }
686             }
687         });
688         
689         variableCombo.addSelectionListener(new SelectionAdapter() {
690             
691             @Override
692             public void widgetSelected(SelectionEvent e) {
693                 // handle update for others
694                 String key = variableCombo.getItem(variableCombo.getSelectionIndex());
695                 DynamicSizeContribution cont = sizeContributions.get(key);
696                 
697                 if (minText.getText().isEmpty()) {
698                     minText.setText(Double.toString(cont.getDefaultMin()));
699                 }
700                 if (maxText.getText().isEmpty()) {
701                     maxText.setText(Double.toString(cont.getDefaultMax()));
702                 }
703                 unit.setText(cont.getUnit());
704                 
705                 String[] items = sizeMapCombo.getItems();
706                 for (int i = 0; i < items.length; i++) {
707                     String item = items[i];
708                     if (item.equals(cont.getDefaultSizeMap().getLabel())) {
709                         sizeMapCombo.select(i);
710                         break;
711                     }
712                 }
713                 
714                 defaultButton.setSelection(true);
715             }
716         });
717         
718         sizingRows.put(object.getSizingObject().getName(), new SizingObjectRow(label, usedButton, variableCombo, minText, maxText, unit, sizeMapCombo, defaultButton));
719         
720         return new Supplier<Pair<String, DynamicSizeContribution>>() {
721
722             @Override
723             public Pair<String, DynamicSizeContribution> get() {
724                 int selectionIndex = variableCombo.getSelectionIndex();
725                 if (selectionIndex >= 0) {
726                     String key = variableCombo.getItem(selectionIndex);
727                     DynamicSizeContribution cont = sizeContributions.get(key);
728                     if (cont != null) {
729                         String sizeMap = sizeMapCombo.getItem(sizeMapCombo.getSelectionIndex());
730                         try {
731                             Map<String, DynamicSizeMap> sizeMaps = Simantics.getSession().syncRequest(new UniqueRead<Map<String, DynamicSizeMap>>() {
732         
733                                 @Override
734                                 public Map<String, DynamicSizeMap> perform(ReadGraph graph) throws DatabaseException {
735                                     return DynamicVisualisationsContributions.dynamicSizeMaps(graph);
736                                 }
737                             });
738                             DynamicSizeMap dColorMap = sizeMaps.get(sizeMap);
739                             String label = variableCombo.getItem(variableCombo.getSelectionIndex());
740                             
741                             DynamicSizeContribution dsc = new DynamicSizeContribution(label, cont.getModuleName(), cont.getAttributeName(), unit.getText(), cont.getVariableGain(), cont.getVariableBias(), dColorMap, Double.parseDouble(minText.getText()), Double.parseDouble(maxText.getText()));
742                             dsc.setUsed(usedButton.getSelection());
743                             dsc.setUseDefault(defaultButton.getSelection());
744                             
745                             return Pair.make(object.getSizingObject().getName(), dsc);
746                         } catch (DatabaseException e) {
747                             LOGGER.error("Could not get DynamicColorContribution", e);
748                         }
749                     }
750                 }
751                 return null;
752             }
753         };
754     }
755     
756     private void initializeColorBars(Composite parent) {
757         Group group = new Group(parent, SWT.NONE);
758         group.setText("Color Bars");
759         GridDataFactory.fillDefaults().grab(true, false).applyTo(group);
760         GridLayoutFactory.fillDefaults().numColumns(8).margins(5, 5).applyTo(group);
761         
762         createColorBars(group);
763     }
764     
765     private void createColorBars(Composite parent) {
766         
767         showColorButton = new Button(parent, SWT.CHECK);
768         showColorButton.setText("Show");
769         
770         colorTicksButton = new Button(parent, SWT.CHECK);
771         colorTicksButton.setText("Ticks");
772         
773         colorGradientButton = new Button(parent, SWT.CHECK);
774         colorGradientButton.setText("Gradients");
775         
776         Label label = new Label(parent, SWT.NONE);
777         label.setText("Location");
778         colorLocationCombo = new Combo(parent, SWT.READ_ONLY);
779         String[] colorLocationItems = Stream.of(ColorBarsLocation.values()).map(size -> size.toString()).toArray(String[]::new);
780         colorLocationCombo.setItems(colorLocationItems);
781         if (colorLocationItems.length > 0) {
782             colorLocationCombo.select(0);
783         }
784         
785         label = new Label(parent, SWT.NONE);
786         label.setText("Size");
787         colorSizeCombo = new Combo(parent, SWT.READ_ONLY);
788         String[] colorSizeItems = Stream.of(ColorBarsSize.values()).map(size -> size.toString()).toArray(String[]::new);
789         colorSizeCombo.setItems(colorSizeItems);
790         if (colorSizeItems.length > 0) {
791             colorSizeCombo.select(0);
792         }
793     }
794
795     private void initializeObjectSizes(Composite parent) {
796         Group group = new Group(parent, SWT.NONE);
797         group.setText("Object Sizes");
798         GridDataFactory.fillDefaults().grab(true, false).applyTo(group);
799         GridLayoutFactory.fillDefaults().numColumns(8).margins(5, 5).applyTo(group);
800         
801         {
802             createSizingObjectHeaderRow(group);
803             createObjectSizes(group);
804         }
805     }
806     
807     private void createObjectSizes(Composite parent) {
808         
809         sizeSuppliers = new ArrayList<>(); 
810         try {
811             Pair<Collection<DynamicSizingObject>, Map<String, DynamicSizeMap>> resultSizing = Simantics.getSession().syncRequest(new UniqueRead<Pair<Collection<DynamicSizingObject>, Map<String, DynamicSizeMap>>>() {
812
813                 @Override
814                 public Pair<Collection<DynamicSizingObject>, Map<String, DynamicSizeMap>> perform(ReadGraph graph) throws DatabaseException {
815                     Map<String, DynamicSizeMap> dynamicSizeMaps = DynamicVisualisationsContributions.dynamicSizeMaps(graph);
816                     return Pair.make(DynamicVisualisationsContributions.dynamicSizingObjects(graph), dynamicSizeMaps);
817                 }
818             });
819             
820             for (DynamicSizingObject object : resultSizing.first) {
821                 sizeSuppliers.add(createSizingObjectRow(parent, object, resultSizing.second));
822             }
823         } catch (DatabaseException e) {
824             LOGGER.error("Could not create object sizes", e);
825         }
826     }
827
828     private void initializeSizeBars(Composite parent) {
829         Group group = new Group(parent, SWT.NONE);
830         group.setText("Size Bars");
831         GridDataFactory.fillDefaults().grab(true, false).applyTo(group);
832         GridLayoutFactory.fillDefaults().numColumns(8).margins(5, 5).applyTo(group);
833         
834         createSizeBars(group);
835     }
836
837     private void createSizeBars(Composite parent) {
838         showSizeButton = new Button(parent, SWT.CHECK);
839         showSizeButton.setText("Show");
840         
841         sizeTicksButton = new Button(parent, SWT.CHECK);
842         sizeTicksButton.setText("Ticks");
843         
844         sizeGradientButton = new Button(parent, SWT.CHECK);
845         sizeGradientButton.setText("Gradient");
846         
847         Label label = new Label(parent, SWT.NONE);
848         label.setText("Location");
849         sizeLocationCombo = new Combo(parent, SWT.READ_ONLY);
850         String[] sizeLocationItems = Stream.of(SizeBarsLocation.values()).map(size -> size.toString()).toArray(String[]::new);
851         sizeLocationCombo.setItems(sizeLocationItems);
852         if (sizeLocationItems.length > 0) {
853             sizeLocationCombo.select(0);
854         }
855         
856         label = new Label(parent, SWT.NONE);
857         label.setText("Size");
858         sizeSizeCombo = new Combo(parent, SWT.READ_ONLY);
859         String[] sizeSizeItems = Stream.of(SizeBarsSize.values()).map(size -> size.toString()).toArray(String[]::new);
860         sizeSizeCombo.setItems(sizeSizeItems);
861         if (sizeSizeItems.length > 0) {
862             sizeSizeCombo.select(0);
863         }
864     }
865
866     public void setParentResource(Resource parentResource) {
867         if (this.parentResource != parentResource) {
868             this.parentResource = parentResource;
869             updateListening();
870         }
871     }
872
873     private void updateListening() {
874         if (visualisationsListener != null)
875             visualisationsListener.dispose();
876         visualisationsListener = new VisualisationsListener(this);
877         Simantics.getSession().asyncRequest(new DynamicVisualisationsRequest(parentResource), visualisationsListener);
878         
879         if (listener != null)
880             listener.dispose();
881         listener = new VisualisationListener(this);
882         Simantics.getSession().asyncRequest(new ActiveDynamicVisualisationsRequest(parentResource), listener);
883     }
884
885     private static class VisualisationsListener implements Listener<Collection<NamedResource>> {
886
887         private static final Logger LOGGER = LoggerFactory.getLogger(VisualisationsListener.class);
888
889         private boolean disposed;
890         private DynamicVisualisationsUI ui;
891         
892         public VisualisationsListener(DynamicVisualisationsUI ui) {
893             this.ui = ui;
894         }
895
896         @Override
897         public void execute(Collection<NamedResource> result) {
898             ui.updateVisualisations(result);
899         }
900
901         @Override
902         public void exception(Throwable t) {
903             LOGGER.error("Could not listen visualisation", t);
904         }
905
906         @Override
907         public boolean isDisposed() {
908             return disposed || ui.isDisposed();
909         }
910
911         public void dispose() {
912             this.disposed = true;
913         }
914     }
915     
916     private static class VisualisationListener implements Listener<DynamicVisualisation> {
917
918         private static final Logger LOGGER = LoggerFactory.getLogger(VisualisationListener.class);
919
920         private boolean disposed;
921         private DynamicVisualisationsUI ui;
922         
923         public VisualisationListener(DynamicVisualisationsUI ui) {
924             this.ui = ui;
925         }
926
927         @Override
928         public void execute(DynamicVisualisation result) {
929             ui.updateVisualisation(result);
930         }
931
932         @Override
933         public void exception(Throwable t) {
934             LOGGER.error("Could not listen visualisation", t);
935         }
936
937         @Override
938         public boolean isDisposed() {
939             return disposed || ui.isDisposed();
940         }
941
942         public void dispose() {
943             this.disposed = true;
944         }
945     }
946
947     public void updateVisualisation(DynamicVisualisation result) {
948         this.visualisation = result;
949         Display.getDefault().asyncExec(() -> {
950             if (getParent().isDisposed())
951                 return;
952             
953             applyButton.setEnabled(visualisation != null && visualisation.getVisualisationResource() != null);
954             removeVisualisationTemplateButton.setEnabled(visualisation != null && visualisation.getVisualisationResource() != null);
955             
956             if (visualisation != null) {
957                 String[] items = templateSelectionCombo.getItems();
958                 for (int i = 0; i < items.length; i++) {
959                     if (visualisation.getName().equals(items[i])) {
960                         templateSelectionCombo.select(i);
961                         break;
962                     }
963                 }
964                 
965                 Map<String, DynamicColorContribution> colorContributions = visualisation.getColorContributions();
966                 for (Entry<String, DynamicColorContribution> entry : colorContributions.entrySet()) {
967                     
968                     ColoringObjectRow coloringObjectRow = coloringRows.get(entry.getKey());
969                     if (coloringObjectRow != null) {
970                         
971                         coloringObjectRow.update(entry.getValue());
972                         
973                     } else {
974                         LOGGER.info("No coloring object visualisation row for key {}", entry.getKey());
975                     }
976                 }
977                 ColorBarOptions colorOptions = visualisation.getColorBarOptions();
978                 showColorButton.setSelection(colorOptions.isShowColorBars());
979                 colorTicksButton.setSelection(colorOptions.isShowColorBarsTicks());
980                 colorGradientButton.setSelection(colorOptions.isUseGradients());
981                 for (int i = 0; i < colorLocationCombo.getItems().length; i++) {
982                     String item = colorLocationCombo.getItem(i);
983                     if (item.equals(colorOptions.getLocation().toString())) {
984                         colorLocationCombo.select(i);
985                         break;
986                     }
987                 }
988                 for (int i = 0; i < colorSizeCombo.getItems().length; i++) {
989                     String item = colorSizeCombo.getItem(i);
990                     if (item.equals(colorOptions.getSize().toString())) {
991                         colorSizeCombo.select(i);
992                         break;
993                     }
994                 }
995                 
996                 Map<String, DynamicSizeContribution> sizeContributions = visualisation.getSizeContributions();
997                 for (Entry<String, DynamicSizeContribution> entry : sizeContributions.entrySet()) {
998                     
999                     SizingObjectRow sizingObjectRow = sizingRows.get(entry.getKey());
1000                     if (sizingObjectRow != null) {
1001                         
1002                         sizingObjectRow.update(entry.getValue());
1003                         
1004                     } else {
1005                         LOGGER.info("No sizing object visualisation row for key {}", entry.getKey());
1006                     }
1007                 }
1008                 SizeBarOptions sizeOptions = visualisation.getSizeBarOptions();
1009                 showSizeButton.setSelection(sizeOptions.isShowSizeBars());
1010                 sizeTicksButton.setSelection(sizeOptions.isShowSizeBarsTicks());
1011                 sizeGradientButton.setSelection(sizeOptions.isUseGradients());
1012                 for (int i = 0; i < sizeLocationCombo.getItems().length; i++) {
1013                     String item = sizeLocationCombo.getItem(i);
1014                     if (item.equals(sizeOptions.getLocation().toString())) {
1015                         sizeLocationCombo.select(i);
1016                         break;
1017                     }
1018                 }
1019                 for (int i = 0; i < sizeSizeCombo.getItems().length; i++) {
1020                     String item = sizeSizeCombo.getItem(i);
1021                     if (item.equals(sizeOptions.getSize().toString())) {
1022                         sizeSizeCombo.select(i);
1023                         break;
1024                     }
1025                 }
1026             }
1027         });
1028     }
1029
1030     public void updateVisualisations(Collection<NamedResource> result) {
1031         this.visualisations = result;
1032         
1033         Display.getDefault().asyncExec(() -> {
1034             if (getParent().isDisposed())
1035                 return;
1036             templateSelectionCombo.setItems(visualisations.stream().map(NamedResource::getName).collect(Collectors.toList()).toArray(new String[visualisations.size()]));
1037             
1038             if (visualisation != null) {
1039                 String[] items = templateSelectionCombo.getItems();
1040                 for (int i = 0; i < items.length; i++) {
1041                     if (visualisation.getName().equals(items[i])) {
1042                         templateSelectionCombo.select(i);
1043                         break;
1044                     }
1045                 }
1046             }
1047             
1048         });
1049     }
1050 }