]> gerrit.simantics Code Review - simantics/district.git/blobdiff - org.simantics.district.network.ui/src/org/simantics/district/network/ui/visualisations/DynamicVisualisationsUI.java
Dynamic visualisations interval and disable support
[simantics/district.git] / org.simantics.district.network.ui / src / org / simantics / district / network / ui / visualisations / DynamicVisualisationsUI.java
index 031e502963f544b057c14e71c09a277c191d3d97..c4afb80e5090be6f69b85521fbfe808cb2ceedb3 100644 (file)
@@ -18,8 +18,15 @@ import org.eclipse.jface.dialogs.InputDialog;
 import org.eclipse.jface.layout.GridDataFactory;
 import org.eclipse.jface.layout.GridLayoutFactory;
 import org.eclipse.swt.SWT;
+import org.eclipse.swt.custom.ScrolledComposite;
+import org.eclipse.swt.events.FocusAdapter;
+import org.eclipse.swt.events.FocusEvent;
+import org.eclipse.swt.events.KeyAdapter;
+import org.eclipse.swt.events.KeyEvent;
 import org.eclipse.swt.events.SelectionAdapter;
 import org.eclipse.swt.events.SelectionEvent;
+import org.eclipse.swt.layout.GridData;
+import org.eclipse.swt.layout.GridLayout;
 import org.eclipse.swt.widgets.Button;
 import org.eclipse.swt.widgets.Combo;
 import org.eclipse.swt.widgets.Composite;
@@ -28,7 +35,7 @@ import org.eclipse.swt.widgets.Group;
 import org.eclipse.swt.widgets.Label;
 import org.eclipse.swt.widgets.Shell;
 import org.eclipse.swt.widgets.Text;
-import org.eclipse.ui.IEditorPart;
+import org.eclipse.swt.widgets.Widget;
 import org.simantics.Simantics;
 import org.simantics.db.ReadGraph;
 import org.simantics.db.Resource;
@@ -37,17 +44,19 @@ import org.simantics.db.common.NamedResource;
 import org.simantics.db.common.request.UniqueRead;
 import org.simantics.db.common.request.WriteRequest;
 import org.simantics.db.exception.DatabaseException;
+import org.simantics.db.layer0.util.RemoverUtil;
 import org.simantics.db.procedure.Listener;
-import org.simantics.district.network.DistrictNetworkUtil;
-import org.simantics.district.network.ontology.DistrictNetworkResource;
 import org.simantics.district.network.profile.ActiveDynamicVisualisationsRequest;
 import org.simantics.district.network.profile.DynamicVisualisationsRequest;
+import org.simantics.district.network.visualisations.DynamicVisualisations;
 import org.simantics.district.network.visualisations.DynamicVisualisationsContributions;
+import org.simantics.district.network.visualisations.DynamicVisualisationsContributions.DynamicArrowObject;
 import org.simantics.district.network.visualisations.DynamicVisualisationsContributions.DynamicColoringObject;
 import org.simantics.district.network.visualisations.DynamicVisualisationsContributions.DynamicSizingObject;
 import org.simantics.district.network.visualisations.model.ColorBarOptions;
 import org.simantics.district.network.visualisations.model.ColorBarOptions.ColorBarsLocation;
 import org.simantics.district.network.visualisations.model.ColorBarOptions.ColorBarsSize;
+import org.simantics.district.network.visualisations.model.DynamicArrowContribution;
 import org.simantics.district.network.visualisations.model.DynamicColorContribution;
 import org.simantics.district.network.visualisations.model.DynamicColorMap;
 import org.simantics.district.network.visualisations.model.DynamicSizeContribution;
@@ -56,26 +65,27 @@ import org.simantics.district.network.visualisations.model.DynamicVisualisation;
 import org.simantics.district.network.visualisations.model.SizeBarOptions;
 import org.simantics.district.network.visualisations.model.SizeBarOptions.SizeBarsLocation;
 import org.simantics.district.network.visualisations.model.SizeBarOptions.SizeBarsSize;
-import org.simantics.ui.workbench.IResourceEditorPart;
 import org.simantics.utils.datastructures.Pair;
-import org.simantics.utils.ui.workbench.WorkbenchUtils;
+import org.simantics.utils.ui.dialogs.ShowError;
 import org.slf4j.Logger;
 import org.slf4j.LoggerFactory;
 
-public class DynamicVisualisationsUI extends Composite {
+public class DynamicVisualisationsUI {
 
     private static final Logger LOGGER = LoggerFactory.getLogger(DynamicVisualisationsUI.class);
 
-    private Resource diagramResource;
+    private Resource parentResource;
     private VisualisationListener listener;
     private DynamicVisualisation visualisation;
 
     private Button showSizeButton;
     private Button sizeTicksButton;
+    private Button sizeGradientButton;
     private Combo sizeLocationCombo;
     private Combo sizeSizeCombo;
     private Button showColorButton;
     private Button colorTicksButton;
+    private Button colorGradientButton;
     private Combo colorLocationCombo;
     private Combo colorSizeCombo;
 
@@ -83,17 +93,58 @@ public class DynamicVisualisationsUI extends Composite {
 
     private List<Supplier<Pair<String, DynamicColorContribution>>> colorSuppliers;
 
-    public DynamicVisualisationsUI(Composite parent, int style) {
-        super(parent, style);
+    private Button removeVisualisationTemplateButton;
 
-        defaultInitializeUI();
+    private Composite parent;
+
+    private Button disableUpdatesButton;
+
+    private List<Supplier<Pair<String, DynamicArrowContribution>>> edgeArrowSuppliers;
+
+    private Button saveVisualisationTemplateAsButton;
+
+    private Button hideEdgesButton;
+    private Button hidePointsButton;
+    private Button hideConsumersButton;
+    private Button hideProducersButton;
+    private Button hideValvesButton;
+    private Button hidePumpingStationsButton;
+
+    private Button networkBranchesStaticPropertiesButton;
+    private Button pointsStaticPropertiesButton;
+    private Button consumersStaticPropertiesButton;
+    
+    private Button dynamicSymbolsEdgesButton;
+    private Button dynamicSymbolsProducersButton;
+    private Button dynamicSymbolsValvesButton;
+    private Button dynamicSymbolsPumpingStationsButton;
+
+    private Text intervalText;
+
+    public DynamicVisualisationsUI(Composite parent) {
+        this.parent = parent;
+        ScrolledComposite scrolledComposite = new ScrolledComposite(parent, SWT.V_SCROLL | SWT.H_SCROLL);
+        scrolledComposite.setLayout(new GridLayout(1, false));
+        scrolledComposite.setLayoutData(new GridData(SWT.FILL, SWT.FILL, true, true));
+
+        Composite firstContent = new Composite(scrolledComposite, SWT.NONE);
+        firstContent.setLayout(new GridLayout(1, false));
+        firstContent.setLayoutData(new GridData(SWT.FILL, SWT.FILL, true, true));
+        
+        defaultInitializeUI(firstContent);
+        
+        scrolledComposite.setContent(firstContent);
+        scrolledComposite.setExpandHorizontal(true);
+        scrolledComposite.setExpandVertical(true);
+        scrolledComposite.setMinSize(firstContent.computeSize(SWT.DEFAULT, SWT.DEFAULT));
     }
 
-    private void defaultInitializeUI() {
-        GridDataFactory.fillDefaults().grab(true, true).applyTo(this);
-        GridLayoutFactory.fillDefaults().numColumns(1).margins(5, 5).applyTo(this);
+    private void defaultInitializeUI(Composite parent) {
+        
+        GridDataFactory.fillDefaults().grab(true, true).applyTo(parent);
+        GridLayoutFactory.fillDefaults().numColumns(1).margins(5, 5).applyTo(parent);
         
-        Composite selectionComposite = new Composite(this, SWT.NONE);
+        Composite selectionComposite = new Composite(parent, SWT.NONE);
         GridDataFactory.fillDefaults().grab(true, false).applyTo(selectionComposite);
         GridLayoutFactory.fillDefaults().numColumns(2).margins(5, 5).applyTo(selectionComposite);
         
@@ -112,7 +163,8 @@ public class DynamicVisualisationsUI extends Composite {
                             
                             @Override
                             public void perform(WriteGraph graph) throws DatabaseException {
-                                DistrictNetworkUtil.setActiveVisualisation(graph, diagramResource, template.getResource());
+                                Resource vf = DynamicVisualisations.getVisualisationFolder(graph, parentResource);
+                                DynamicVisualisations.setActiveVisualisation(graph, vf, template.getResource());
                             }
                         });
                         break;
@@ -121,49 +173,363 @@ public class DynamicVisualisationsUI extends Composite {
             }
         });
         
-        Composite coloringObjectsComposite = new Composite(this, SWT.NONE);
+        Composite intervalElementsComposite = new Composite(parent, SWT.NONE);
+        GridDataFactory.fillDefaults().grab(true, false).applyTo(intervalElementsComposite);
+        GridLayoutFactory.fillDefaults().numColumns(1).applyTo(intervalElementsComposite);
+        initializeIntervalElements(intervalElementsComposite);
+        
+        Composite hideElementsComposite = new Composite(parent, SWT.NONE);
+        GridDataFactory.fillDefaults().grab(true, false).applyTo(hideElementsComposite);
+        GridLayoutFactory.fillDefaults().numColumns(1).applyTo(hideElementsComposite);
+        initializeHideElements(hideElementsComposite);
+        
+        Composite staticPropertiesComposite = new Composite(parent, SWT.NONE);
+        GridDataFactory.fillDefaults().grab(true, false).applyTo(staticPropertiesComposite);
+        GridLayoutFactory.fillDefaults().numColumns(1).applyTo(staticPropertiesComposite);
+        initializeStaticProperties(staticPropertiesComposite);
+
+        Composite dynamicSymbolsComposite = new Composite(parent, SWT.NONE);
+        GridDataFactory.fillDefaults().grab(true, false).applyTo(dynamicSymbolsComposite);
+        GridLayoutFactory.fillDefaults().numColumns(1).applyTo(dynamicSymbolsComposite);
+        initializeDynamicSymbols(dynamicSymbolsComposite);
+
+        Composite coloringObjectsComposite = new Composite(parent, SWT.NONE);
         GridDataFactory.fillDefaults().grab(true, false).applyTo(coloringObjectsComposite);
         GridLayoutFactory.fillDefaults().numColumns(1).applyTo(coloringObjectsComposite);
         initializeColoringObjects(coloringObjectsComposite);
         
-        Composite colorBarsComposite = new Composite(this, SWT.NONE);
+        Composite colorBarsComposite = new Composite(parent, SWT.NONE);
         GridDataFactory.fillDefaults().grab(true, false).applyTo(colorBarsComposite);
         GridLayoutFactory.fillDefaults().numColumns(1).applyTo(colorBarsComposite);
         initializeColorBars(colorBarsComposite);
         
-        Composite objectSizesComposite = new Composite(this, SWT.NONE);
+        Composite objectSizesComposite = new Composite(parent, SWT.NONE);
         GridDataFactory.fillDefaults().grab(true, false).applyTo(objectSizesComposite);
         GridLayoutFactory.fillDefaults().numColumns(1).applyTo(objectSizesComposite);
         initializeObjectSizes(objectSizesComposite);
         
-        Composite sizeBarsComposite = new Composite(this, SWT.NONE);
+        Composite sizeBarsComposite = new Composite(parent, SWT.NONE);
         GridDataFactory.fillDefaults().grab(true, false).applyTo(sizeBarsComposite);
         GridLayoutFactory.fillDefaults().numColumns(1).applyTo(sizeBarsComposite);
         initializeSizeBars(sizeBarsComposite);
         
-        Button saveVisualisationTemplateButton = new Button(this, SWT.NONE);
-        saveVisualisationTemplateButton.setText("Save");
-        saveVisualisationTemplateButton.setEnabled(visualisation == null || visualisation.getVisualisationResource() != null);
-        saveVisualisationTemplateButton.addSelectionListener(new SelectionAdapter() {
+        Composite edgeArrowsComposite = new Composite(parent, SWT.NONE);
+        GridDataFactory.fillDefaults().grab(true, false).applyTo(edgeArrowsComposite);
+        GridLayoutFactory.fillDefaults().numColumns(1).applyTo(edgeArrowsComposite);
+        initializeEdgeArrows(edgeArrowsComposite);
+        
+        Composite buttonBarsComposite = new Composite(parent, SWT.NONE);
+        GridDataFactory.fillDefaults().grab(true, false).applyTo(buttonBarsComposite);
+        GridLayoutFactory.fillDefaults().numColumns(3).applyTo(buttonBarsComposite);
+        
+        saveVisualisationTemplateAsButton = new Button(buttonBarsComposite, SWT.NONE);
+        saveVisualisationTemplateAsButton.setText("Save as visualisation template");
+        saveVisualisationTemplateAsButton.addSelectionListener(new SelectionAdapter() {
             
             @Override
             public void widgetSelected(SelectionEvent e) {
-                persistVisualisationTemplate(visualisation.getName(), Optional.of(visualisation.getVisualisationResource()));
+                showSaveVisualisationTemplateDialog(e.widget.getDisplay().getActiveShell());
             }
         });
         
-        Button saveVisualisationTemplateAsButton = new Button(this, SWT.NONE);
-        saveVisualisationTemplateAsButton.setText("Save as visualisation template");
-        saveVisualisationTemplateAsButton.addSelectionListener(new SelectionAdapter() {
+        removeVisualisationTemplateButton = new Button(buttonBarsComposite, SWT.NONE);
+        removeVisualisationTemplateButton.setText("Remove");
+        removeVisualisationTemplateButton.setEnabled(visualisation != null && visualisation.getVisualisationResource() != null);
+        removeVisualisationTemplateButton.addSelectionListener(new SelectionAdapter() {
             
             @Override
             public void widgetSelected(SelectionEvent e) {
-                showSaveVisualisationTemplateDialog(e.widget.getDisplay().getActiveShell());
+                removeVisualisationTemplate(visualisation.getName(), Optional.of(visualisation.getVisualisationResource()));
             }
         });
+    }
+    
+    private void initializeIntervalElements(Composite parent) {
+        Group group = new Group(parent, SWT.NONE);
+        group.setText("Interval");
+        GridDataFactory.fillDefaults().grab(true, false).applyTo(group);
+        GridLayoutFactory.fillDefaults().numColumns(4).margins(5, 5).applyTo(group);
+        
+        createIntervalElements(group);
+    }
+    
+    private void createIntervalElements(Composite parent) {
+
+        Label label = new Label(parent, SWT.NONE);
+        label.setText("Generic");
+        intervalText = new Text(parent, SWT.BORDER);
+        addSelectionListener(intervalText);
+        
+        disableUpdatesButton = new Button(parent, SWT.CHECK);
+        disableUpdatesButton.setText("Disable updates");
+        addSelectionListener(disableUpdatesButton);
+
+    }
+    
+    private void initializeHideElements(Composite parent) {
+        Group group = new Group(parent, SWT.NONE);
+        group.setText("Hide Elements");
+        GridDataFactory.fillDefaults().grab(true, false).applyTo(group);
+        GridLayoutFactory.fillDefaults().numColumns(8).margins(5, 5).applyTo(group);
+        
+        createHideElements(group);
+    }
+    
+    private void createHideElements(Composite parent) {
+        
+        hideEdgesButton = new Button(parent, SWT.CHECK);
+        hideEdgesButton.setText("Edges");
+        addSelectionListener(hideEdgesButton);
+        
+        hidePointsButton = new Button(parent, SWT.CHECK);
+        hidePointsButton.setText("Points");
+        addSelectionListener(hidePointsButton);
+        
+        hideConsumersButton = new Button(parent, SWT.CHECK);
+        hideConsumersButton.setText("Consumers");
+        addSelectionListener(hideConsumersButton);
+        
+        hideProducersButton = new Button(parent, SWT.CHECK);
+        hideProducersButton.setText("Producers");
+        addSelectionListener(hideProducersButton);
+        
+        hideValvesButton = new Button(parent, SWT.CHECK);
+        hideValvesButton.setText("Valves");
+        addSelectionListener(hideValvesButton);
+
+        hidePumpingStationsButton = new Button(parent, SWT.CHECK);
+        hidePumpingStationsButton.setText("Pumping Stations");
+        addSelectionListener(hidePumpingStationsButton);
+    }
+
+    private void initializeStaticProperties(Composite parent) {
+        Group group = new Group(parent, SWT.NONE);
+        group.setText("Static Properties");
+        GridDataFactory.fillDefaults().grab(true, false).applyTo(group);
+        GridLayoutFactory.fillDefaults().numColumns(8).margins(5, 5).applyTo(group);
+        
+        createStaticProperties(group);
+    }
+    
+    private void createStaticProperties(Composite parent) {
+        
+        networkBranchesStaticPropertiesButton = new Button(parent, SWT.CHECK);
+        networkBranchesStaticPropertiesButton.setText("Network Branches");
+        addSelectionListener(networkBranchesStaticPropertiesButton);
+        
+        pointsStaticPropertiesButton = new Button(parent, SWT.CHECK);
+        pointsStaticPropertiesButton.setText("Points");
+        addSelectionListener(pointsStaticPropertiesButton);
+        
+        consumersStaticPropertiesButton = new Button(parent, SWT.CHECK);
+        consumersStaticPropertiesButton.setText("Consumers");
+        addSelectionListener(consumersStaticPropertiesButton);
+
+    }
+
+    private void initializeDynamicSymbols(Composite parent) {
+        Group group = new Group(parent, SWT.NONE);
+        group.setText("Dynamic Symbols");
+        GridDataFactory.fillDefaults().grab(true, false).applyTo(group);
+        GridLayoutFactory.fillDefaults().numColumns(8).margins(5, 5).applyTo(group);
+        
+        createDynamicSymbols(group);
+    }
+    
+    private void createDynamicSymbols(Composite parent) {
+        
+        dynamicSymbolsEdgesButton = new Button(parent, SWT.CHECK);
+        dynamicSymbolsEdgesButton.setText("Shutoff Valves in Pipes");
+        addSelectionListener(dynamicSymbolsEdgesButton);
+        
+        dynamicSymbolsProducersButton = new Button(parent, SWT.CHECK);
+        dynamicSymbolsProducersButton.setText("Producers");
+        addSelectionListener(dynamicSymbolsProducersButton);
+        
+        dynamicSymbolsValvesButton = new Button(parent, SWT.CHECK);
+        dynamicSymbolsValvesButton.setText("Valves");
+        addSelectionListener(dynamicSymbolsValvesButton);
+
+        dynamicSymbolsPumpingStationsButton = new Button(parent, SWT.CHECK);
+        dynamicSymbolsPumpingStationsButton.setText("Pumping Stations");
+        addSelectionListener(dynamicSymbolsPumpingStationsButton);
+    }
+
+    private void initializeEdgeArrows(Composite parent) {
+        Group group = new Group(parent, SWT.NONE);
+        group.setText("Edge Arrows");
+        GridDataFactory.fillDefaults().grab(true, false).applyTo(group);
+        GridLayoutFactory.fillDefaults().numColumns(6).margins(5, 5).applyTo(group);
+        
+        createEdgeArrowsHeaderRow(group);
+        
+        edgeArrowSuppliers = new ArrayList<>();
+        {
+            try {
+                Collection<DynamicArrowObject> result = Simantics.getSession().syncRequest(new UniqueRead<Collection<DynamicArrowObject>>() {
+
+                    @Override
+                    public Collection<DynamicArrowObject> perform(ReadGraph graph) throws DatabaseException {
+                        return DynamicVisualisationsContributions.dynamicEdgeArrowObjects(graph);
+                    }
+                });
+                
+                for (DynamicArrowObject object : result) {
+                    edgeArrowSuppliers.add(createEdgeArrowRow(group, object));
+                }
+            } catch (DatabaseException e) {
+                LOGGER.error("Could not create coloring objecst", e);
+            }
+        }
+    }
+
+    
+    private void createEdgeArrowsHeaderRow(Composite parent) {
+
+        Label label = new Label(parent, SWT.NONE);
+        label.setText("Label");
+        GridDataFactory.fillDefaults().grab(true, false).align(SWT.CENTER, SWT.CENTER).applyTo(label);
+        
+        label = new Label(parent, SWT.NONE);
+        label.setText("Used");
+        GridDataFactory.fillDefaults().grab(true, false).align(SWT.CENTER, SWT.CENTER).applyTo(label);
+        
+        label = new Label(parent, SWT.NONE);
+        label.setText("Variable");
+        GridDataFactory.fillDefaults().grab(true, false).align(SWT.CENTER, SWT.CENTER).applyTo(label);
+        
+        label = new Label(parent, SWT.NONE);
+        label.setText("Gain");
+        GridDataFactory.fillDefaults().grab(true, false).align(SWT.CENTER, SWT.CENTER).applyTo(label);
         
+        label = new Label(parent, SWT.NONE);
+        label.setText("Bias");
+        GridDataFactory.fillDefaults().grab(true, false).align(SWT.CENTER, SWT.CENTER).applyTo(label);
+
+        label = new Label(parent, SWT.NONE);
+        label.setText("Default");
+        GridDataFactory.fillDefaults().grab(true, false).align(SWT.CENTER, SWT.CENTER).applyTo(label);
     }
     
+    private Supplier<Pair<String, DynamicArrowContribution>> createEdgeArrowRow(Composite parent, DynamicArrowObject object) {
+
+        Map<String, DynamicArrowContribution> arrowContributions = object.getArrowContributions();
+        if (arrowContributions.isEmpty()) {
+            // ok, no point in showing empty combo boxes
+            return null;
+        }
+        
+        Label label = new Label(parent, SWT.NONE);
+        label.setText(object.getArrowObject().getName());
+        GridDataFactory.fillDefaults().align(SWT.CENTER, SWT.CENTER).applyTo(label);
+        
+        Button usedButton = new Button(parent, SWT.CHECK);
+        GridDataFactory.fillDefaults().align(SWT.CENTER, SWT.CENTER).applyTo(usedButton);
+        addSelectionListener(usedButton);
+        
+        Combo variableCombo = new Combo(parent, SWT.READ_ONLY);
+        variableCombo.setItems(arrowContributions.keySet().toArray(new String[arrowContributions.size()]));
+        
+        GridDataFactory.fillDefaults().align(SWT.CENTER, SWT.CENTER).applyTo(variableCombo);
+        
+        Text gainText = new Text(parent, SWT.BORDER);
+        GridDataFactory.fillDefaults().align(SWT.CENTER, SWT.CENTER).applyTo(gainText);
+        addSelectionListener(gainText);
+        
+        Text biasText = new Text(parent, SWT.BORDER);
+        GridDataFactory.fillDefaults().align(SWT.CENTER, SWT.CENTER).applyTo(biasText);
+        addSelectionListener(biasText);
+        
+        Button defaultButton = new Button(parent, SWT.CHECK);
+        GridDataFactory.fillDefaults().align(SWT.CENTER, SWT.CENTER).applyTo(defaultButton);
+        defaultButton.addSelectionListener(new SelectionAdapter() {
+            
+            @Override
+            public void widgetSelected(SelectionEvent e) {
+                int index = variableCombo.getSelectionIndex();
+                if (index >= 0) {
+                    String key = variableCombo.getItem(index);
+                    DynamicArrowContribution cont = arrowContributions.get(key);
+                    
+                    gainText.setText(Double.toString(cont.getDefaultGain()));
+                    gainText.setEnabled(!defaultButton.getSelection());
+                    biasText.setText(Double.toString(cont.getDefaultBias()));
+                    biasText.setEnabled(!defaultButton.getSelection());
+                    
+                }
+            }
+        });
+        addSelectionListener(defaultButton);
+        
+        variableCombo.addSelectionListener(new SelectionAdapter() {
+            
+            @Override
+            public void widgetSelected(SelectionEvent e) {
+                // handle update for others
+                String key = variableCombo.getItem(variableCombo.getSelectionIndex());
+                DynamicArrowContribution cont = arrowContributions.get(key);
+                
+                if (gainText.getText().isEmpty()) {
+                    gainText.setText(Double.toString(cont.getDefaultGain()));
+                }
+                if (biasText.getText().isEmpty()) {
+                    biasText.setText(Double.toString(cont.getDefaultBias()));
+                }
+                
+                defaultButton.setSelection(true);
+            }
+        });
+        addSelectionListener(variableCombo);
+        
+        arrowRows.put(object.getArrowObject().getName(), new ArrowObjectRow(label, usedButton, variableCombo, gainText, biasText, defaultButton));
+        
+        return new Supplier<Pair<String, DynamicArrowContribution>>() {
+
+            @Override
+            public Pair<String, DynamicArrowContribution> get() {
+                int selectionIndex = variableCombo.getSelectionIndex();
+                if (selectionIndex >= 0) {
+                    String key = variableCombo.getItem(selectionIndex);
+                    DynamicArrowContribution cont = arrowContributions.get(key);
+                    if (cont != null) {
+                        String label = variableCombo.getItem(variableCombo.getSelectionIndex());
+                        
+                        double gain = cont.getDefaultGain();
+                        String gainS = gainText.getText();
+                        if (gainS != null && !gainS.isEmpty()) {
+                            gain = Double.parseDouble(gainS);
+                        }
+                        double bias = cont.getDefaultBias();
+                        String biasS = biasText.getText();
+                        if (biasS != null && !biasS.isEmpty()) {
+                            bias = Double.parseDouble(biasText.getText());
+                        }
+                        
+                        DynamicArrowContribution dsc = new DynamicArrowContribution(label, cont.getModuleName(), cont.getAttributeName(), gain, bias);
+                        dsc.setUsed(usedButton.getSelection());
+                        dsc.setUseDefault(defaultButton.getSelection());
+                        
+                        return Pair.make(object.getArrowObject().getName(), dsc);
+                    }
+                }
+                return null;
+            }
+        };
+    }
+
+    protected void removeVisualisationTemplate(String name, Optional<Resource> of) {
+        if (of.isPresent()) {
+            Resource visualisation = of.get();
+            Simantics.getSession().asyncRequest(new WriteRequest() {
+                
+                @Override
+                public void perform(WriteGraph graph) throws DatabaseException {
+                    RemoverUtil.remove(graph, visualisation);
+                }
+            });
+        }
+    }
+
     private void showSaveVisualisationTemplateDialog(Shell shell) {
         
         InputDialog dialog = new InputDialog(shell, "Save visualisation template", "Give template a name", "", new IInputValidator() {
@@ -178,13 +544,31 @@ public class DynamicVisualisationsUI extends Composite {
         
         if (dialog.open() == Dialog.OK) {
             String name = dialog.getValue();
-            persistVisualisationTemplate(name, Optional.empty());
+            try {
+                persistVisualisationTemplate(name, Optional.empty());
+            } catch (Exception e) {
+                LOGGER.error("Could not persist visualisation template", e);
+                ShowError.showError("Could not persist visualisation template", e.getMessage(), e);
+            }
         }
     }
 
-    private void persistVisualisationTemplate(String templateName, Optional<Resource> existing) {
+    private void persistCurrentVisualisationTemplateIfAvailable() {
+        if (visualisation != null) {
+            try {
+                persistVisualisationTemplate(visualisation.getName(), Optional.of(visualisation.getVisualisationResource()));
+            } catch (Exception e1) {
+                LOGGER.error("Could not persist visualisation template", e1);
+                ShowError.showError("Could not persist visualisation template", e1.getMessage(), e1);
+            }
+        } else {
+            LOGGER.info("No current visualisation template selected for saving");
+        }
+    }
+    
+    private void persistVisualisationTemplate(String templateName, Optional<Resource> existing) throws Exception {
         
-        List<Pair<String, DynamicColorContribution>> colorCollect = colorSuppliers.stream().map(s -> s.get()).filter(Objects::nonNull).collect(Collectors.toList());
+        List<Pair<String, DynamicColorContribution>> colorCollect = colorSuppliers.stream().filter(Objects::nonNull).map(s -> s.get()).filter(Objects::nonNull).collect(Collectors.toList());
 
         String colorLocation = colorLocationCombo.getItem(colorLocationCombo.getSelectionIndex());
         String colorSize = colorSizeCombo.getItem(colorSizeCombo.getSelectionIndex());
@@ -192,37 +576,87 @@ public class DynamicVisualisationsUI extends Composite {
         ColorBarOptions colorBarOptions = new ColorBarOptions()
                 .showColorBars(showColorButton.getSelection())
                 .showColorBarsTicks(colorTicksButton.getSelection())
+                .useGradients(colorGradientButton.getSelection())
                 .withLocation(ColorBarsLocation.valueOf(colorLocation))
                 .withSize(ColorBarsSize.valueOf(colorSize));
         
-        List<Pair<String, DynamicSizeContribution>> sizeCollect = sizeSuppliers.stream().map(s -> s.get()).filter(Objects::nonNull).collect(Collectors.toList());
+        List<Pair<String, DynamicSizeContribution>> sizeCollect = sizeSuppliers.stream().filter(Objects::nonNull).map(s -> s.get()).filter(Objects::nonNull).collect(Collectors.toList());
         
         String sizeLocation = sizeLocationCombo.getItem(sizeLocationCombo.getSelectionIndex());
         String sizeSize = sizeSizeCombo.getItem(sizeSizeCombo.getSelectionIndex());
         
-        SizeBarOptions sizeBarOptions = new SizeBarOptions()
+        final SizeBarOptions sizeBarOptions = new SizeBarOptions()
                 .showSizeBars(showSizeButton.getSelection())
                 .showSizeBarsTicks(sizeTicksButton.getSelection())
+                .useGradients(sizeGradientButton.getSelection())
                 .withLocation(SizeBarsLocation.valueOf(sizeLocation))
                 .withSize(SizeBarsSize.valueOf(sizeSize));
         
+        List<Pair<String, DynamicArrowContribution>> edgeArrowCollect = edgeArrowSuppliers.stream().filter(Objects::nonNull).map(s -> s.get()).filter(Objects::nonNull).collect(Collectors.toList());
+        
+        boolean hideEdges = hideEdgesButton.getSelection();
+        boolean hidePoints = hidePointsButton.getSelection();
+        boolean hideConsumers = hideConsumersButton.getSelection();
+        boolean hideProducers = hideProducersButton.getSelection();
+        boolean hideValves = hideValvesButton.getSelection();
+        boolean hidePumpingStations = hidePumpingStationsButton.getSelection();
+        
+        boolean networkBranchesStaticProperties = networkBranchesStaticPropertiesButton.getSelection();
+        boolean pointsStaticProperties = pointsStaticPropertiesButton.getSelection();
+        boolean consumersStaticProperties = consumersStaticPropertiesButton.getSelection();
+        
+        boolean dynamicSymbolsEdges = dynamicSymbolsEdgesButton.getSelection();
+        boolean dynamicSymbolsProducers = dynamicSymbolsProducersButton.getSelection();
+        boolean dynamicSymbolsValves = dynamicSymbolsValvesButton.getSelection();
+        boolean dynamicSymbolsPumpingStations = dynamicSymbolsPumpingStationsButton.getSelection();
+        
+        boolean disabled = disableUpdatesButton.getSelection();
+        Long interval;
+        try {
+            interval = Long.parseLong(intervalText.getText());
+        } catch (NumberFormatException e) {
+            // ignore
+            interval = 2000L;
+        }
+        long ii = interval;
+        
         Simantics.getSession().asyncRequest(new WriteRequest() {
             
             @Override
             public void perform(WriteGraph graph) throws DatabaseException {
-                DistrictNetworkResource DN = DistrictNetworkResource.getInstance(graph);
                 Resource exist;
                 if (existing.isPresent()) {
                     exist = existing.get();
                 } else {
-                    exist = DistrictNetworkUtil.createVisualisation(graph, diagramResource, templateName);
+                    exist = DynamicVisualisations.createVisualisation(graph, parentResource, templateName);
                 }
+                DynamicVisualisations.setIntervalAndDisabled(graph, exist, ii, disabled);
+                DynamicVisualisations.setColorContributions(graph, exist, colorCollect);
+                DynamicVisualisations.setColorBarOptions(graph, exist, colorBarOptions);
+                DynamicVisualisations.setSizeContributions(graph, exist, sizeCollect);
+                DynamicVisualisations.setSizeBarOptions(graph, exist, sizeBarOptions);
+                DynamicVisualisations.setEdgeArrowContributions(graph, exist, edgeArrowCollect);
+                DynamicVisualisations.setHideElements(graph, exist,
+                        hideEdges,
+                        hidePoints,
+                        hideConsumers,
+                        hideProducers,
+                        hideValves,
+                        hidePumpingStations
+                    );
                 
-                DistrictNetworkUtil.setColorContributions(graph, exist, colorCollect);
+                DynamicVisualisations.setStaticProperties(graph, exist,
+                        networkBranchesStaticProperties,
+                        pointsStaticProperties,
+                        consumersStaticProperties
+                    );
                 
-                DistrictNetworkUtil.setColorBarOptions(graph, exist, colorBarOptions);
-                DistrictNetworkUtil.setSizeContributions(graph, exist, sizeCollect);
-                DistrictNetworkUtil.setSizeBarOptions(graph, exist, sizeBarOptions);
+                DynamicVisualisations.setDynamicSymbols(graph, exist,
+                        dynamicSymbolsEdges,
+                        dynamicSymbolsProducers,
+                        dynamicSymbolsValves,
+                        dynamicSymbolsPumpingStations
+                    );
             }
         });
     }
@@ -239,40 +673,23 @@ public class DynamicVisualisationsUI extends Composite {
         colorSuppliers = new ArrayList<>();
         {
             try {
-                Collection<DynamicColoringObject> result = Simantics.getSession().syncRequest(new UniqueRead<Collection<DynamicColoringObject>>() {
+                Pair<Collection<DynamicColoringObject>, Map<String, DynamicColorMap>> result = Simantics.getSession().syncRequest(new UniqueRead<Pair<Collection<DynamicColoringObject>, Map<String, DynamicColorMap>>>() {
 
                     @Override
-                    public Collection<DynamicColoringObject> perform(ReadGraph graph) throws DatabaseException {
-                        return DynamicVisualisationsContributions.dynamicColoringObjects(graph);
+                    public Pair<Collection<DynamicColoringObject>, Map<String, DynamicColorMap>> perform(ReadGraph graph) throws DatabaseException {
+                        Map<String, DynamicColorMap> dynamicColorMaps = DynamicVisualisationsContributions.dynamicColorMaps(graph);
+                        return Pair.make(DynamicVisualisationsContributions.dynamicColoringObjects(graph), dynamicColorMaps);
                     }
                 });
                 
-                for (DynamicColoringObject object : result) {
-                    colorSuppliers.add(createColoringObjectRow(group, object));
+                for (DynamicColoringObject object : result.first) {
+                    colorSuppliers.add(createColoringObjectRow(group, object, result.second));
                 }
 
             } catch (DatabaseException e) {
                 LOGGER.error("Could not create coloring objecst", e);
             }
         }
-        {
-            Button applyButton = new Button(group, SWT.NONE);
-            applyButton.setText("Apply");
-            applyButton.addSelectionListener(new SelectionAdapter() {
-                
-                @Override
-                public void widgetSelected(SelectionEvent e) {
-                    List<Pair<String, DynamicColorContribution>> collect = colorSuppliers.stream().map(s -> s.get()).filter(Objects::nonNull).collect(Collectors.toList());
-                    Simantics.getSession().asyncRequest(new WriteRequest() {
-                        
-                        @Override
-                        public void perform(WriteGraph graph) throws DatabaseException {
-                            DistrictNetworkUtil.setColorContributions(graph, visualisation.getVisualisationResource(), collect);
-                        }
-                    });
-                }
-            });
-        }
     }
     
     private void createColoringObjectHeaderRow(Composite parent) {
@@ -312,6 +729,7 @@ public class DynamicVisualisationsUI extends Composite {
 
     private Map<String, ColoringObjectRow> coloringRows = new HashMap<>();
     private Map<String, SizingObjectRow> sizingRows = new HashMap<>();
+    private Map<String, ArrowObjectRow> arrowRows = new HashMap<>();
 
     private VisualisationsListener visualisationsListener;
 
@@ -326,11 +744,11 @@ public class DynamicVisualisationsUI extends Composite {
         private final Combo variableCombo;
         private final Text minText;
         private final Text maxText;
-        private final Label unit;
+        private final Text unit;
         private final Combo colorMapCombo;
         private final Button defaultButton;
         
-        public ColoringObjectRow(Label label, Button usedButton, Combo variableCombo, Text minText, Text maxText, Label unit,
+        public ColoringObjectRow(Label label, Button usedButton, Combo variableCombo, Text minText, Text maxText, Text unit,
                 Combo colorMapCombo, Button defaultButton) {
             super();
             this.label = label;
@@ -355,17 +773,14 @@ public class DynamicVisualisationsUI extends Composite {
             maxText.setText(Double.toString(colorContribution.getDefaultMax()));
             unit.setText(colorContribution.getUnit());
             
-            // color map only supports single for now
-            colorMapCombo.setItems(colorContribution.getDefaultColorMap().getLabel());
-            colorMapCombo.select(0);
-//            String[] colorItems = colorMapCombo.getItems();
-//            for (int i = 0; i < colorItems.length; i++) {
-//                
-//                if (colorContribution.getDefaultColorMap().getLabel().equals(colorItems[i])) {
-//                    colorMapCombo.select(i);
-//                    break;
-//                }
-//            }
+            String[] colorItems = colorMapCombo.getItems();
+            for (int i = 0; i < colorItems.length; i++) {
+                
+                if (colorContribution.getDefaultColorMap().getLabel().equals(colorItems[i])) {
+                    colorMapCombo.select(i);
+                    break;
+                }
+            }
             usedButton.setSelection(colorContribution.isUsed());
             defaultButton.setSelection(colorContribution.isUseDefault());
             
@@ -411,21 +826,60 @@ public class DynamicVisualisationsUI extends Composite {
             maxText.setText(Double.toString(sizeContribution.getDefaultMax()));
             unit.setText(sizeContribution.getUnit());
             
-            // color map only supports single for now
-            sizeMapCombo.setItems(sizeContribution.getDefaultSizeMap().getLabel());
-            sizeMapCombo.select(0);
-//            String[] colorItems = colorMapCombo.getItems();
-//            for (int i = 0; i < colorItems.length; i++) {
-//                
-//                if (colorContribution.getDefaultColorMap().getLabel().equals(colorItems[i])) {
-//                    colorMapCombo.select(i);
-//                    break;
-//                }
-//            }
+            String[] sizeItems = sizeMapCombo.getItems();
+            for (int i = 0; i < sizeItems.length; i++) {
+                if (sizeContribution.getDefaultSizeMap().getLabel().equals(sizeItems[i])) {
+                    sizeMapCombo.select(i);
+                    break;
+                }
+            }
+            usedButton.setSelection(sizeContribution.isUsed());
+            defaultButton.setSelection(sizeContribution.isUseDefault());
+            
+            minText.setEnabled(!sizeContribution.isUseDefault());
+            maxText.setEnabled(!sizeContribution.isUseDefault());
+            sizeMapCombo.setEnabled(!sizeContribution.isUseDefault());
         }
     }
 
-    private Supplier<Pair<String, DynamicColorContribution>> createColoringObjectRow(Composite parent, DynamicColoringObject object) {
+    private static class ArrowObjectRow {
+        
+        private final Label label;
+        private final Button usedButton;
+        private final Combo variableCombo;
+        private final Text gainText;
+        private final Text biasText;
+        private final Button defaultButton;
+        
+        public ArrowObjectRow(Label label, Button usedButton, Combo variableCombo, Text gainText, Text biasText, Button defaultButton) {
+            this.label = label;
+            this.usedButton = usedButton;
+            this.variableCombo = variableCombo;
+            this.gainText = gainText;
+            this.biasText = biasText;
+            this.defaultButton = defaultButton;
+        }
+
+        public void update(DynamicColorContribution colorContribution) {
+            String[] items = variableCombo.getItems();
+            for (int i = 0; i < items.length; i++) {
+                if (colorContribution.getLabel().equals(items[i])) {
+                    variableCombo.select(i);
+                    break;
+                }
+            }
+            gainText.setText(Double.toString(colorContribution.getDefaultMin()));
+            biasText.setText(Double.toString(colorContribution.getDefaultMax()));
+            
+            usedButton.setSelection(colorContribution.isUsed());
+            defaultButton.setSelection(colorContribution.isUseDefault());
+            
+            gainText.setEnabled(!colorContribution.isUseDefault());
+            biasText.setEnabled(!colorContribution.isUseDefault());
+        }
+    }
+
+    private Supplier<Pair<String, DynamicColorContribution>> createColoringObjectRow(Composite parent, DynamicColoringObject object, Map<String, DynamicColorMap> colorMaps) {
         Label label = new Label(parent, SWT.NONE);
         label.setText(object.getColoringObject().getName());
         GridDataFactory.fillDefaults().align(SWT.CENTER, SWT.CENTER).applyTo(label);
@@ -434,6 +888,7 @@ public class DynamicVisualisationsUI extends Composite {
         
         Button usedButton = new Button(parent, SWT.CHECK);
         GridDataFactory.fillDefaults().align(SWT.CENTER, SWT.CENTER).applyTo(usedButton);
+        addSelectionListener(usedButton);
         
         Combo variableCombo = new Combo(parent, SWT.READ_ONLY);
         variableCombo.setItems(colorContributions.keySet().toArray(new String[colorContributions.size()]));
@@ -442,16 +897,19 @@ public class DynamicVisualisationsUI extends Composite {
         
         Text minText = new Text(parent, SWT.BORDER);
         GridDataFactory.fillDefaults().grab(true, false).hint(150, SWT.DEFAULT).align(SWT.CENTER, SWT.CENTER).applyTo(minText);
+        addSelectionListener(minText);
         
         Text maxText = new Text(parent, SWT.BORDER);
         GridDataFactory.fillDefaults().grab(true, false).hint(150, SWT.DEFAULT).align(SWT.CENTER, SWT.CENTER).applyTo(maxText);
+        addSelectionListener(maxText);
         
-        Label unit = new Label(parent, SWT.NONE);
-        unit.setText("");
-        GridDataFactory.fillDefaults().align(SWT.CENTER, SWT.CENTER).applyTo(unit);
+        Text unit = new Text(parent, SWT.READ_ONLY);
+        GridDataFactory.fillDefaults().grab(true, false).align(SWT.CENTER, SWT.CENTER).applyTo(unit);
         
         Combo colorMapCombo = new Combo(parent, SWT.READ_ONLY);
         GridDataFactory.fillDefaults().align(SWT.CENTER, SWT.CENTER).applyTo(colorMapCombo);
+        colorMapCombo.setItems(colorMaps.keySet().toArray(new String[colorMaps.keySet().size()]));
+        addSelectionListener(colorMapCombo);
         
         Button defaultButton = new Button(parent, SWT.CHECK);
         GridDataFactory.fillDefaults().align(SWT.CENTER, SWT.CENTER).applyTo(defaultButton);
@@ -470,12 +928,19 @@ public class DynamicVisualisationsUI extends Composite {
                     maxText.setEnabled(!defaultButton.getSelection());
                     unit.setText(cont.getUnit());
                     
-                    colorMapCombo.setItems(cont.getDefaultColorMap().getLabel());
-                    colorMapCombo.select(0);
+                    String[] items = colorMapCombo.getItems();
+                    for (int i = 0; i < items.length; i++) {
+                        String item = items[i];
+                        if (item.equals(cont.getDefaultColorMap().getLabel())) {
+                            colorMapCombo.select(i);
+                            break;
+                        }
+                    }
                     colorMapCombo.setEnabled(!defaultButton.getSelection());
                 }
             }
         });
+        addSelectionListener(defaultButton);
         
         variableCombo.addSelectionListener(new SelectionAdapter() {
             
@@ -495,13 +960,20 @@ public class DynamicVisualisationsUI extends Composite {
                     }
                     unit.setText(cont.getUnit());
                     
-                    colorMapCombo.setItems(cont.getDefaultColorMap().getLabel());
-                    colorMapCombo.select(0);
+                    String[] items = colorMapCombo.getItems();
+                    for (int i = 0; i < items.length; i++) {
+                        String item = items[i];
+                        if (item.equals(cont.getDefaultColorMap().getLabel())) {
+                            colorMapCombo.select(i);
+                            break;
+                        }
+                    }
                     
                     defaultButton.setSelection(true);
                 }
             }
         });
+        addSelectionListener(variableCombo);
         
         coloringRows.put(object.getColoringObject().getName(), new ColoringObjectRow(label, usedButton, variableCombo, minText, maxText, unit, colorMapCombo, defaultButton));
 
@@ -514,26 +986,20 @@ public class DynamicVisualisationsUI extends Composite {
                     String key = variableCombo.getItem(selectionIndex);
                     DynamicColorContribution cont = colorContributions.get(key);
                     if (cont != null) {
-                        String colorMap = colorMapCombo.getItem(colorMapCombo.getSelectionIndex());
-                        try {
-                            Map<String, DynamicColorMap> colorMaps = Simantics.getSession().syncRequest(new UniqueRead<Map<String, DynamicColorMap>>() {
-        
-                                @Override
-                                public Map<String, DynamicColorMap> perform(ReadGraph graph) throws DatabaseException {
-                                    return DynamicVisualisationsContributions.dynamicColorMaps(graph);
-                                }
-                            });
+                        
+                        String label = variableCombo.getItem(variableCombo.getSelectionIndex());
+                        DynamicColorContribution dcc;
+                        if (colorMapCombo.getSelectionIndex() > -1) {
+                            String colorMap = colorMapCombo.getItem(colorMapCombo.getSelectionIndex());
                             DynamicColorMap dColorMap = colorMaps.get(colorMap);
-                            String label = variableCombo.getItem(variableCombo.getSelectionIndex());
-                            
-                            DynamicColorContribution dcc = new DynamicColorContribution(label, cont.getModuleName(), cont.getAttributeName(), unit.getText(), cont.getVariableGain(), cont.getVariableBias(), dColorMap, Double.parseDouble(minText.getText()), Double.parseDouble(maxText.getText()));
-                            dcc.setUsed(usedButton.getSelection());
-                            dcc.setUseDefault(defaultButton.getSelection());
-                            
-                            return Pair.make(object.getColoringObject().getName(), dcc);
-                        } catch (DatabaseException e) {
-                            LOGGER.error("Could not get DynamicColorContribution", e);
+                            dcc = new DynamicColorContribution(label, cont.getModuleName(), cont.getAttributeName(), unit.getText(), cont.getVariableGain(), cont.getVariableBias(), dColorMap, Double.parseDouble(minText.getText()), Double.parseDouble(maxText.getText()));
+                        } else {
+                            dcc = colorContributions.get(label);
                         }
+                        dcc.setUsed(usedButton.getSelection());
+                        dcc.setUseDefault(defaultButton.getSelection());
+                        
+                        return Pair.make(object.getColoringObject().getName(), dcc);
                     }
                 }
                 return null;
@@ -576,7 +1042,7 @@ public class DynamicVisualisationsUI extends Composite {
         GridDataFactory.fillDefaults().grab(true, false).align(SWT.CENTER, SWT.CENTER).applyTo(label);
     }
 
-    private Supplier<Pair<String, DynamicSizeContribution>> createSizingObjectRow(Composite parent, DynamicSizingObject object) {
+    private Supplier<Pair<String, DynamicSizeContribution>> createSizingObjectRow(Composite parent, DynamicSizingObject object, Map<String, DynamicSizeMap> sizeMaps) {
         Label label = new Label(parent, SWT.NONE);
         label.setText(object.getSizingObject().getName());
         GridDataFactory.fillDefaults().align(SWT.CENTER, SWT.CENTER).applyTo(label);
@@ -585,6 +1051,7 @@ public class DynamicVisualisationsUI extends Composite {
         
         Button usedButton = new Button(parent, SWT.CHECK);
         GridDataFactory.fillDefaults().align(SWT.CENTER, SWT.CENTER).applyTo(usedButton);
+        addSelectionListener(usedButton);
         
         Combo variableCombo = new Combo(parent, SWT.READ_ONLY);
         variableCombo.setItems(sizeContributions.keySet().toArray(new String[sizeContributions.size()]));
@@ -593,19 +1060,50 @@ public class DynamicVisualisationsUI extends Composite {
         
         Text minText = new Text(parent, SWT.BORDER);
         GridDataFactory.fillDefaults().align(SWT.CENTER, SWT.CENTER).applyTo(minText);
+        addSelectionListener(minText);
         
         Text maxText = new Text(parent, SWT.BORDER);
         GridDataFactory.fillDefaults().align(SWT.CENTER, SWT.CENTER).applyTo(maxText);
+        addSelectionListener(maxText);
         
         Label unit = new Label(parent, SWT.NONE);
-        unit.setText("");
-        GridDataFactory.fillDefaults().align(SWT.CENTER, SWT.CENTER).applyTo(unit);
+        GridDataFactory.fillDefaults().grab(true, false).align(SWT.CENTER, SWT.CENTER).applyTo(unit);
         
         Combo sizeMapCombo = new Combo(parent, SWT.READ_ONLY);
         GridDataFactory.fillDefaults().align(SWT.CENTER, SWT.CENTER).applyTo(sizeMapCombo);
+        sizeMapCombo.setItems(sizeMaps.keySet().toArray(new String[sizeMaps.keySet().size()]));
+        addSelectionListener(sizeMapCombo);
         
         Button defaultButton = new Button(parent, SWT.CHECK);
         GridDataFactory.fillDefaults().align(SWT.CENTER, SWT.CENTER).applyTo(defaultButton);
+        defaultButton.addSelectionListener(new SelectionAdapter() {
+            
+            @Override
+            public void widgetSelected(SelectionEvent e) {
+                int index = variableCombo.getSelectionIndex();
+                if (index >= 0) {
+                    String key = variableCombo.getItem(index);
+                    DynamicSizeContribution cont = sizeContributions.get(key);
+                    
+                    minText.setText(Double.toString(cont.getDefaultMin()));
+                    minText.setEnabled(!defaultButton.getSelection());
+                    maxText.setText(Double.toString(cont.getDefaultMax()));
+                    maxText.setEnabled(!defaultButton.getSelection());
+                    unit.setText(cont.getUnit());
+                    
+                    String[] items = sizeMapCombo.getItems();
+                    for (int i = 0; i < items.length; i++) {
+                        String item = items[i];
+                        if (item.equals(cont.getDefaultSizeMap().getLabel())) {
+                            sizeMapCombo.select(i);
+                            break;
+                        }
+                    }
+                    sizeMapCombo.setEnabled(!defaultButton.getSelection());
+                }
+            }
+        });
+        addSelectionListener(defaultButton);
         
         variableCombo.addSelectionListener(new SelectionAdapter() {
             
@@ -623,12 +1121,19 @@ public class DynamicVisualisationsUI extends Composite {
                 }
                 unit.setText(cont.getUnit());
                 
-                sizeMapCombo.setItems(cont.getDefaultSizeMap().getLabel());
-                sizeMapCombo.select(0);
+                String[] items = sizeMapCombo.getItems();
+                for (int i = 0; i < items.length; i++) {
+                    String item = items[i];
+                    if (item.equals(cont.getDefaultSizeMap().getLabel())) {
+                        sizeMapCombo.select(i);
+                        break;
+                    }
+                }
                 
                 defaultButton.setSelection(true);
             }
         });
+        addSelectionListener(variableCombo);
         
         sizingRows.put(object.getSizingObject().getName(), new SizingObjectRow(label, usedButton, variableCombo, minText, maxText, unit, sizeMapCombo, defaultButton));
         
@@ -641,26 +1146,20 @@ public class DynamicVisualisationsUI extends Composite {
                     String key = variableCombo.getItem(selectionIndex);
                     DynamicSizeContribution cont = sizeContributions.get(key);
                     if (cont != null) {
-                        String sizeMap = sizeMapCombo.getItem(sizeMapCombo.getSelectionIndex());
-                        try {
-                            Map<String, DynamicSizeMap> sizeMaps = Simantics.getSession().syncRequest(new UniqueRead<Map<String, DynamicSizeMap>>() {
-        
-                                @Override
-                                public Map<String, DynamicSizeMap> perform(ReadGraph graph) throws DatabaseException {
-                                    return DynamicVisualisationsContributions.dynamicSizeMaps(graph);
-                                }
-                            });
-                            DynamicSizeMap dColorMap = sizeMaps.get(sizeMap);
-                            String label = variableCombo.getItem(variableCombo.getSelectionIndex());
-                            
-                            DynamicSizeContribution dsc = new DynamicSizeContribution(label, cont.getModuleName(), cont.getAttributeName(), unit.getText(), cont.getVariableGain(), cont.getVariableBias(), dColorMap, Double.parseDouble(minText.getText()), Double.parseDouble(maxText.getText()));
-                            dsc.setUsed(usedButton.getSelection());
-                            dsc.setUseDefault(defaultButton.getSelection());
-                            
-                            return Pair.make(object.getSizingObject().getName(), dsc);
-                        } catch (DatabaseException e) {
-                            LOGGER.error("Could not get DynamicColorContribution", e);
+
+                        String label = variableCombo.getItem(variableCombo.getSelectionIndex());
+                        DynamicSizeContribution dsc;
+                        if (sizeMapCombo.getSelectionIndex() > -1) {
+                            String sizeMap = sizeMapCombo.getItem(sizeMapCombo.getSelectionIndex());
+                            DynamicSizeMap dSizeMap = sizeMaps.get(sizeMap);
+                            dsc = new DynamicSizeContribution(label, cont.getModuleName(), cont.getAttributeName(), unit.getText(), cont.getVariableGain(), cont.getVariableBias(), dSizeMap, Double.parseDouble(minText.getText()), Double.parseDouble(maxText.getText()));
+                        } else {
+                            dsc = sizeContributions.get(label);
                         }
+                        dsc.setUsed(usedButton.getSelection());
+                        dsc.setUseDefault(defaultButton.getSelection());
+                        
+                        return Pair.make(object.getSizingObject().getName(), dsc);
                     }
                 }
                 return null;
@@ -672,7 +1171,7 @@ public class DynamicVisualisationsUI extends Composite {
         Group group = new Group(parent, SWT.NONE);
         group.setText("Color Bars");
         GridDataFactory.fillDefaults().grab(true, false).applyTo(group);
-        GridLayoutFactory.fillDefaults().numColumns(2).margins(5, 5).applyTo(group);
+        GridLayoutFactory.fillDefaults().numColumns(8).margins(5, 5).applyTo(group);
         
         createColorBars(group);
     }
@@ -681,50 +1180,35 @@ public class DynamicVisualisationsUI extends Composite {
         
         showColorButton = new Button(parent, SWT.CHECK);
         showColorButton.setText("Show");
+        addSelectionListener(showColorButton);
         
         colorTicksButton = new Button(parent, SWT.CHECK);
         colorTicksButton.setText("Ticks");
+        addSelectionListener(colorTicksButton);
+        
+        colorGradientButton = new Button(parent, SWT.CHECK);
+        colorGradientButton.setText("Gradients");
+        addSelectionListener(colorGradientButton);
         
         Label label = new Label(parent, SWT.NONE);
         label.setText("Location");
         colorLocationCombo = new Combo(parent, SWT.READ_ONLY);
-        colorLocationCombo.setItems(Stream.of(ColorBarsLocation.values()).map(size -> size.toString()).toArray(String[]::new));
+        String[] colorLocationItems = Stream.of(ColorBarsLocation.values()).map(size -> size.toString()).toArray(String[]::new);
+        colorLocationCombo.setItems(colorLocationItems);
+        if (colorLocationItems.length > 0) {
+            colorLocationCombo.select(0);
+        }
+        addSelectionListener(colorLocationCombo);
         
         label = new Label(parent, SWT.NONE);
         label.setText("Size");
         colorSizeCombo = new Combo(parent, SWT.READ_ONLY);
-        colorSizeCombo.setItems(Stream.of(ColorBarsSize.values()).map(size -> size.toString()).toArray(String[]::new));
-        
-        Button applyButton = new Button(parent, SWT.NONE);
-        applyButton.setText("Apply");
-        
-        applyButton.addSelectionListener(new SelectionAdapter() {
-            
-            @Override
-            public void widgetSelected(SelectionEvent e) {
-                // persist changes
-                IEditorPart activeEditor = WorkbenchUtils.getActiveEditor();
-                if (activeEditor instanceof IResourceEditorPart) {
-                    
-                    String colorLocation = colorLocationCombo.getItem(colorLocationCombo.getSelectionIndex());
-                    String colorSize = colorSizeCombo.getItem(colorSizeCombo.getSelectionIndex());
-                    
-                    ColorBarOptions options = new ColorBarOptions()
-                            .showColorBars(showColorButton.getSelection())
-                            .showColorBarsTicks(colorTicksButton.getSelection())
-                            .withLocation(ColorBarsLocation.valueOf(colorLocation))
-                            .withSize(ColorBarsSize.valueOf(colorSize));
-
-                    Simantics.getSession().asyncRequest(new WriteRequest() {
-                        
-                        @Override
-                        public void perform(WriteGraph graph) throws DatabaseException {
-                            DistrictNetworkUtil.setColorBarOptions(graph, visualisation.getVisualisationResource(), options);
-                        }
-                    });
-                }
-            }
-        });
+        String[] colorSizeItems = Stream.of(ColorBarsSize.values()).map(size -> size.toString()).toArray(String[]::new);
+        colorSizeCombo.setItems(colorSizeItems);
+        if (colorSizeItems.length > 0) {
+            colorSizeCombo.select(0);
+        }
+        addSelectionListener(colorSizeCombo);
     }
 
     private void initializeObjectSizes(Composite parent) {
@@ -743,46 +1227,28 @@ public class DynamicVisualisationsUI extends Composite {
         
         sizeSuppliers = new ArrayList<>(); 
         try {
-            Collection<DynamicSizingObject> resultSizing = Simantics.getSession().syncRequest(new UniqueRead<Collection<DynamicSizingObject>>() {
+            Pair<Collection<DynamicSizingObject>, Map<String, DynamicSizeMap>> resultSizing = Simantics.getSession().syncRequest(new UniqueRead<Pair<Collection<DynamicSizingObject>, Map<String, DynamicSizeMap>>>() {
 
                 @Override
-                public Collection<DynamicSizingObject> perform(ReadGraph graph) throws DatabaseException {
-                    return DynamicVisualisationsContributions.dynamicSizingObjects(graph);
+                public Pair<Collection<DynamicSizingObject>, Map<String, DynamicSizeMap>> perform(ReadGraph graph) throws DatabaseException {
+                    Map<String, DynamicSizeMap> dynamicSizeMaps = DynamicVisualisationsContributions.dynamicSizeMaps(graph);
+                    return Pair.make(DynamicVisualisationsContributions.dynamicSizingObjects(graph), dynamicSizeMaps);
                 }
             });
             
-            for (DynamicSizingObject object : resultSizing) {
-                sizeSuppliers.add(createSizingObjectRow(parent, object));
+            for (DynamicSizingObject object : resultSizing.first) {
+                sizeSuppliers.add(createSizingObjectRow(parent, object, resultSizing.second));
             }
         } catch (DatabaseException e) {
             LOGGER.error("Could not create object sizes", e);
         }
-        
-        {
-            Button applyButton = new Button(parent, SWT.NONE);
-            applyButton.setText("Apply");
-            applyButton.addSelectionListener(new SelectionAdapter() {
-                
-                @Override
-                public void widgetSelected(SelectionEvent e) {
-                    List<Pair<String, DynamicSizeContribution>> collect = sizeSuppliers.stream().map(s -> s.get()).filter(Objects::nonNull).collect(Collectors.toList());
-                    Simantics.getSession().asyncRequest(new WriteRequest() {
-                        
-                        @Override
-                        public void perform(WriteGraph graph) throws DatabaseException {
-                            DistrictNetworkUtil.setSizeContributions(graph, visualisation.getVisualisationResource(), collect);
-                        }
-                    });
-                }
-            });
-        }
     }
 
     private void initializeSizeBars(Composite parent) {
         Group group = new Group(parent, SWT.NONE);
         group.setText("Size Bars");
         GridDataFactory.fillDefaults().grab(true, false).applyTo(group);
-        GridLayoutFactory.fillDefaults().numColumns(2).margins(5, 5).applyTo(group);
+        GridLayoutFactory.fillDefaults().numColumns(8).margins(5, 5).applyTo(group);
         
         createSizeBars(group);
     }
@@ -790,69 +1256,92 @@ public class DynamicVisualisationsUI extends Composite {
     private void createSizeBars(Composite parent) {
         showSizeButton = new Button(parent, SWT.CHECK);
         showSizeButton.setText("Show");
+        addSelectionListener(showSizeButton);
         
         sizeTicksButton = new Button(parent, SWT.CHECK);
         sizeTicksButton.setText("Ticks");
+        addSelectionListener(sizeTicksButton);
+        
+        sizeGradientButton = new Button(parent, SWT.CHECK);
+        sizeGradientButton.setText("Gradient");
+        addSelectionListener(sizeGradientButton);
         
         Label label = new Label(parent, SWT.NONE);
         label.setText("Location");
         sizeLocationCombo = new Combo(parent, SWT.READ_ONLY);
-        sizeLocationCombo.setItems(Stream.of(SizeBarsLocation.values()).map(size -> size.toString()).toArray(String[]::new));
+        String[] sizeLocationItems = Stream.of(SizeBarsLocation.values()).map(size -> size.toString()).toArray(String[]::new);
+        sizeLocationCombo.setItems(sizeLocationItems);
+        if (sizeLocationItems.length > 0) {
+            sizeLocationCombo.select(0);
+        }
+        addSelectionListener(sizeLocationCombo);
         
         label = new Label(parent, SWT.NONE);
         label.setText("Size");
         sizeSizeCombo = new Combo(parent, SWT.READ_ONLY);
-        sizeSizeCombo.setItems(Stream.of(SizeBarsSize.values()).map(size -> size.toString()).toArray(String[]::new));
-        
-        Button applyButton = new Button(parent, SWT.READ_ONLY);
-        applyButton.setText("Apply");
-        
-        applyButton.addSelectionListener(new SelectionAdapter() {
-            
-            @Override
-            public void widgetSelected(SelectionEvent e) {
-                // persist changes
-                IEditorPart activeEditor = WorkbenchUtils.getActiveEditor();
-                if (activeEditor instanceof IResourceEditorPart) {
-                    
-                    String sizeLocation = sizeLocationCombo.getItem(sizeLocationCombo.getSelectionIndex());
-                    String sizeSize = sizeSizeCombo.getItem(sizeSizeCombo.getSelectionIndex());
-                    
-                    SizeBarOptions options = new SizeBarOptions()
-                            .showSizeBars(showSizeButton.getSelection())
-                            .showSizeBarsTicks(sizeTicksButton.getSelection())
-                            .withLocation(SizeBarsLocation.valueOf(sizeLocation))
-                            .withSize(SizeBarsSize.valueOf(sizeSize));
+        String[] sizeSizeItems = Stream.of(SizeBarsSize.values()).map(size -> size.toString()).toArray(String[]::new);
+        sizeSizeCombo.setItems(sizeSizeItems);
+        if (sizeSizeItems.length > 0) {
+            sizeSizeCombo.select(0);
+        }
+        addSelectionListener(sizeSizeCombo);
+    }
 
-                    Simantics.getSession().asyncRequest(new WriteRequest() {
-                        
-                        @Override
-                        public void perform(WriteGraph graph) throws DatabaseException {
-                            DistrictNetworkUtil.setSizeBarOptions(graph, visualisation.getVisualisationResource(), options);
-                        }
-                    });
+    private void addSelectionListener(Widget widget) {
+        if (widget instanceof Button) {
+            ((Button) widget).addSelectionListener(new SelectionAdapter() {
+
+                @Override
+                public void widgetSelected(SelectionEvent e) {
+                    persistCurrentVisualisationTemplateIfAvailable();
                 }
-            }
-        });
+            });
+        } else if (widget instanceof Combo) {
+            ((Combo) widget).addSelectionListener(new SelectionAdapter() {
+
+                @Override
+                public void widgetSelected(SelectionEvent e) {
+                    persistCurrentVisualisationTemplateIfAvailable();
+                }
+            });
+        } else if (widget instanceof Text) {
+            ((Text) widget).addFocusListener(new FocusAdapter() {
+                
+                @Override
+                public void focusLost(FocusEvent e) {
+                    persistCurrentVisualisationTemplateIfAvailable();
+                }
+            });
+            ((Text) widget).addKeyListener(new KeyAdapter() {
+                
+                @Override
+                public void keyReleased(KeyEvent e) {
+                    if(e.keyCode == SWT.CR || e.keyCode == SWT.LF) {
+                        persistCurrentVisualisationTemplateIfAvailable();
+                    }
+                }
+            });
+        }
     }
 
-    public void setDiagramResource(Resource diagramResource) {
-        if (this.diagramResource != diagramResource) {
-            this.diagramResource = diagramResource;
+    public void setParentResource(Resource parentResource) {
+        if (this.parentResource != parentResource) {
+            this.parentResource = parentResource;
             updateListening();
         }
+        saveVisualisationTemplateAsButton.setEnabled(parentResource != null);
     }
 
     private void updateListening() {
         if (visualisationsListener != null)
             visualisationsListener.dispose();
         visualisationsListener = new VisualisationsListener(this);
-        Simantics.getSession().asyncRequest(new DynamicVisualisationsRequest(diagramResource), visualisationsListener);
+        Simantics.getSession().asyncRequest(new DynamicVisualisationsRequest(parentResource), visualisationsListener);
         
         if (listener != null)
             listener.dispose();
         listener = new VisualisationListener(this);
-        Simantics.getSession().asyncRequest(new ActiveDynamicVisualisationsRequest(diagramResource), listener);
+        Simantics.getSession().asyncRequest(new ActiveDynamicVisualisationsRequest(parentResource), listener);
     }
 
     private static class VisualisationsListener implements Listener<Collection<NamedResource>> {
@@ -878,7 +1367,7 @@ public class DynamicVisualisationsUI extends Composite {
 
         @Override
         public boolean isDisposed() {
-            return disposed || ui.isDisposed();
+            return disposed || ui.getParent().isDisposed();
         }
 
         public void dispose() {
@@ -909,7 +1398,7 @@ public class DynamicVisualisationsUI extends Composite {
 
         @Override
         public boolean isDisposed() {
-            return disposed || ui.isDisposed();
+            return disposed ||ui.getParent().isDisposed();
         }
 
         public void dispose() {
@@ -919,12 +1408,13 @@ public class DynamicVisualisationsUI extends Composite {
 
     public void updateVisualisation(DynamicVisualisation result) {
         this.visualisation = result;
-        if (visualisation != null) {
-            Display.getDefault().asyncExec(() -> {
-                if (getParent().isDisposed())
-                    return;
-                
-                
+        Display.getDefault().asyncExec(() -> {
+            if (getParent().isDisposed())
+                return;
+            
+            removeVisualisationTemplateButton.setEnabled(visualisation != null && visualisation.getVisualisationResource() != null);
+            
+            if (visualisation != null) {
                 String[] items = templateSelectionCombo.getItems();
                 for (int i = 0; i < items.length; i++) {
                     if (visualisation.getName().equals(items[i])) {
@@ -933,6 +1423,9 @@ public class DynamicVisualisationsUI extends Composite {
                     }
                 }
                 
+                intervalText.setText(Long.toString(visualisation.getInterval()));
+                disableUpdatesButton.setSelection(visualisation.disabled());
+                
                 Map<String, DynamicColorContribution> colorContributions = visualisation.getColorContributions();
                 for (Entry<String, DynamicColorContribution> entry : colorContributions.entrySet()) {
                     
@@ -948,6 +1441,7 @@ public class DynamicVisualisationsUI extends Composite {
                 ColorBarOptions colorOptions = visualisation.getColorBarOptions();
                 showColorButton.setSelection(colorOptions.isShowColorBars());
                 colorTicksButton.setSelection(colorOptions.isShowColorBarsTicks());
+                colorGradientButton.setSelection(colorOptions.isUseGradients());
                 for (int i = 0; i < colorLocationCombo.getItems().length; i++) {
                     String item = colorLocationCombo.getItem(i);
                     if (item.equals(colorOptions.getLocation().toString())) {
@@ -978,6 +1472,7 @@ public class DynamicVisualisationsUI extends Composite {
                 SizeBarOptions sizeOptions = visualisation.getSizeBarOptions();
                 showSizeButton.setSelection(sizeOptions.isShowSizeBars());
                 sizeTicksButton.setSelection(sizeOptions.isShowSizeBarsTicks());
+                sizeGradientButton.setSelection(sizeOptions.isUseGradients());
                 for (int i = 0; i < sizeLocationCombo.getItems().length; i++) {
                     String item = sizeLocationCombo.getItem(i);
                     if (item.equals(sizeOptions.getLocation().toString())) {
@@ -992,8 +1487,8 @@ public class DynamicVisualisationsUI extends Composite {
                         break;
                     }
                 }
-            });
-        }
+            }
+        });
     }
 
     public void updateVisualisations(Collection<NamedResource> result) {
@@ -1003,6 +1498,21 @@ public class DynamicVisualisationsUI extends Composite {
             if (getParent().isDisposed())
                 return;
             templateSelectionCombo.setItems(visualisations.stream().map(NamedResource::getName).collect(Collectors.toList()).toArray(new String[visualisations.size()]));
+            
+            if (visualisation != null) {
+                String[] items = templateSelectionCombo.getItems();
+                for (int i = 0; i < items.length; i++) {
+                    if (visualisation.getName().equals(items[i])) {
+                        templateSelectionCombo.select(i);
+                        break;
+                    }
+                }
+            }
+            
         });
     }
+    
+    public Composite getParent() {
+        return parent;
+    }
 }