]> gerrit.simantics Code Review - simantics/platform.git/blobdiff - bundles/org.simantics.charts/src/org/simantics/charts/ui/ChartAndSubscriptionItemDialog.java
Fixed all line endings of the repository
[simantics/platform.git] / bundles / org.simantics.charts / src / org / simantics / charts / ui / ChartAndSubscriptionItemDialog.java
index e7b11047eab74cb8839c1a2147d05e5579c30127..8bb8163455687825feca20161c86d06887bc655f 100644 (file)
-/*******************************************************************************\r
- * Copyright (c) 2007, 2011 Association for Decentralized Information Management in\r
- * Industry THTH ry.\r
- * All rights reserved. This program and the accompanying materials\r
- * are made available under the terms of the Eclipse Public License v1.0\r
- * which accompanies this distribution, and is available at\r
- * http://www.eclipse.org/legal/epl-v10.html\r
- *\r
- * Contributors:\r
- *     VTT Technical Research Centre of Finland - initial API and implementation\r
- *******************************************************************************/\r
-package org.simantics.charts.ui;\r
-\r
-import java.awt.Color;\r
-\r
-import org.eclipse.core.runtime.preferences.IEclipsePreferences;\r
-import org.eclipse.core.runtime.preferences.InstanceScope;\r
-import org.eclipse.jface.dialogs.Dialog;\r
-import org.eclipse.jface.fieldassist.ControlDecoration;\r
-import org.eclipse.jface.fieldassist.FieldDecorationRegistry;\r
-import org.eclipse.jface.layout.GridDataFactory;\r
-import org.eclipse.jface.layout.GridLayoutFactory;\r
-import org.eclipse.jface.preference.ColorSelector;\r
-import org.eclipse.jface.util.IPropertyChangeListener;\r
-import org.eclipse.jface.util.PropertyChangeEvent;\r
-import org.eclipse.swt.SWT;\r
-import org.eclipse.swt.events.ModifyEvent;\r
-import org.eclipse.swt.events.ModifyListener;\r
-import org.eclipse.swt.events.SelectionAdapter;\r
-import org.eclipse.swt.events.SelectionEvent;\r
-import org.eclipse.swt.graphics.Image;\r
-import org.eclipse.swt.graphics.RGB;\r
-import org.eclipse.swt.widgets.Button;\r
-import org.eclipse.swt.widgets.Combo;\r
-import org.eclipse.swt.widgets.Composite;\r
-import org.eclipse.swt.widgets.Control;\r
-import org.eclipse.swt.widgets.Group;\r
-import org.eclipse.swt.widgets.Label;\r
-import org.eclipse.swt.widgets.Shell;\r
-import org.eclipse.swt.widgets.Spinner;\r
-import org.eclipse.swt.widgets.Text;\r
-import org.simantics.charts.preference.ChartPreferences;\r
-import org.simantics.charts.query.ChartAndSubscriptionItemData;\r
-import org.simantics.databoard.util.URIStringUtils;\r
-import org.simantics.modeling.preferences.SubscriptionPreferences;\r
-import org.simantics.trend.configuration.Scale;\r
-import org.simantics.trend.configuration.TrendItem.DrawMode;\r
-import org.simantics.trend.impl.JarisPaints;\r
-\r
-/**\r
- * ChartAndSubscriptionItemDialog is a dialog that has the following property\r
- * queries:\r
- *   Chart name  (Read only)\r
- *   Variable    (Read only)\r
- *   Label\r
- *   Draw Mode   (Enumeration)\r
- *   Scale, min, max ( Enum, int, int)\r
- *   Subscription ( Text )\r
- *   Deadband, Interval, Gain, Bias, Unit (Double)\r
- *   Stroke settings (width, color)\r
- * \r
- * If the dialog is opened in binary mode the content is more stripped.\r
- *   Chart name\r
- *   Variable\r
- *   Label\r
- *   Subscription\r
- *   Stroke settings\r
- * \r
- * @author toni.kalajainen\r
- */\r
-public class ChartAndSubscriptionItemDialog extends Dialog {\r
-\r
-    private static final String MANUAL = "Manual";\r
-    private static final String AUTO = "Auto";\r
-\r
-    // UI\r
-    Label lChart, lRVI, lLabel, lDrawMode, lScale, lMin, lMax, lSubscription, lDeadband, lInterval, lGain, lBias, lUnit;\r
-    Text tRVI, tLabel, tDeadband, tInterval, tGain, tBias, tUnit;\r
-    Combo cSubscription;\r
-    Button saveAsPrefs;\r
-\r
-    // chart specific, not initialized, if includeChartSpecificProperties=false\r
-    Text tChart, tMin, tMax;\r
-    Combo cDrawMode, cScale;\r
-    Spinner sStrokeWidth;\r
-    ColorSelector colorSelector;\r
-\r
-    // Input error decorations\r
-    ControlDecoration tMinDecor;\r
-    ControlDecoration tMaxDecor;\r
-    ControlDecoration tDeadbandDecor;\r
-    ControlDecoration tIntervalDecor;\r
-    ControlDecoration tGainDecor;\r
-    ControlDecoration tBiasDecor;\r
-\r
-    //\r
-    ChartAndSubscriptionItemData baseData = new ChartAndSubscriptionItemData();\r
-    ChartAndSubscriptionItemData data;\r
-\r
-    final Runnable applyAction;\r
-\r
-    IEclipsePreferences chartnode; // chart specific, not initialized, if includeChartSpecificProperties=false\r
-    IEclipsePreferences subscriptionnode;\r
-    boolean includeChartSpecificProperties;\r
-\r
-    public ChartAndSubscriptionItemDialog(Shell parentShell, ChartAndSubscriptionItemData data, boolean includeChartSpecificProperties) {\r
-        this(parentShell, data, includeChartSpecificProperties, null);\r
-    }\r
-\r
-    public ChartAndSubscriptionItemDialog(Shell parentShell, ChartAndSubscriptionItemData data, boolean includeChartSpecificProperties, Runnable applyAction) {\r
-        super(parentShell);\r
-        this.data = data;\r
-        this.baseData.readFrom(data);\r
-        this.includeChartSpecificProperties = includeChartSpecificProperties;\r
-        this.applyAction = applyAction;\r
-\r
-        if(includeChartSpecificProperties)\r
-            chartnode = InstanceScope.INSTANCE.getNode( ChartPreferences.P_NODE );\r
-        subscriptionnode = InstanceScope.INSTANCE.getNode( SubscriptionPreferences.P_NODE );\r
-\r
-        setShellStyle(SWT.RESIZE | SWT.TITLE | SWT.CLOSE | SWT.BORDER);\r
-    }\r
-\r
-    @Override\r
-    protected Control createContents(Composite parent) {\r
-        Control c = super.createContents(parent);\r
-        validate();\r
-        return c;\r
-    }\r
-\r
-    @Override\r
-    protected void createButtonsForButtonBar(Composite parent) {\r
-        if (applyAction != null)\r
-            createButton(parent, ChartDialogConstants.APPLY_ID, ChartDialogConstants.APPLY_LABEL, true);\r
-        super.createButtonsForButtonBar(parent);\r
-    }\r
-\r
-    public void setInput(ChartAndSubscriptionItemData data) {\r
-        // TODO: implement so that this reinitializes the UI of the dialog according to the new specified data\r
-    }\r
-\r
-    protected void disposeDialogAreaContents() {\r
-        Composite area = (Composite) dialogArea;\r
-        for (Control child : area.getChildren())\r
-            child.dispose();\r
-    }\r
-\r
-    @Override\r
-    protected Control createDialogArea(Composite parent) {\r
-        Composite c = (Composite) super.createDialogArea(parent);\r
-        GridLayoutFactory.fillDefaults().margins(8, 8).numColumns(9).applyTo(c);\r
-        createDialogAreaContents(c);\r
-        return c;\r
-    }\r
-\r
-    protected void createDialogAreaContents(Composite c) {\r
-        GridDataFactory gd1 = GridDataFactory.fillDefaults().align(SWT.FILL, SWT.CENTER).span(1, 1);\r
-        GridDataFactory gd2 = GridDataFactory.fillDefaults().grab(true, false).indent(5, 0).span(8, 1);\r
-\r
-        // 1st line - Chart: [chart name]\r
-        if(includeChartSpecificProperties) {\r
-            lChart = new Label(c, 0);\r
-            lChart.setText("Chart:");\r
-            gd1.applyTo(lChart);\r
-            tChart = new Text(c, SWT.BORDER | SWT.READ_ONLY);\r
-            //tChart.setEnabled( false );\r
-            if ( data.chartName != null ) tChart.setText( data.chartName );\r
-            gd2.applyTo(tChart);\r
-        }\r
-\r
-        // 2nd line - Variable Reference: [chart item label]\r
-        lRVI = new Label(c, 0);\r
-        lRVI.setText("Variable: ");\r
-        gd1.applyTo(lRVI);\r
-        tRVI = new Text(c, SWT.BORDER | SWT.READ_ONLY);\r
-        //tRVI.setEnabled(false);\r
-        if ( data.variableReference!=null ) tRVI.setText(URIStringUtils.unescape( data.variableReference ));\r
-        gd2.applyTo(tRVI);\r
-\r
-        // 3rd line - Label: [chart item label]\r
-        lLabel = new Label(c, 0);\r
-        lLabel.setText("&Label: ");\r
-        gd1.applyTo(lLabel);\r
-        tLabel = new Text(c, SWT.BORDER);\r
-        if ( data.label!=null ) tLabel.setText(data.label);\r
-        gd2.applyTo(tLabel);\r
-\r
-//        if (!data.binaryMode) {\r
-//             // 4th line - DrawMode: [ drop-down-list V ]\r
-//             lDrawMode = new Label(c, 0);\r
-//             lDrawMode.setText("DrawMode :");\r
-//             gd1.applyTo(lDrawMode);\r
-//             cDrawMode = new Combo(c, SWT.DROP_DOWN | SWT.BORDER | SWT.READ_ONLY);\r
-//             for (DrawMode dm : DrawMode.values()) cDrawMode.add( dm.name() );\r
-//             String _drawmode = data.drawmode!=null ? data.drawmode.toString() : chartnode.get( ChartPreferences.P_DRAWMODE, ChartPreferences.DEFAULT_DRAWMODE ); \r
-//             cDrawMode.setText( _drawmode );\r
-//             gd2.applyTo(cDrawMode);\r
-//        }\r
-\r
-        if (!data.binaryMode && includeChartSpecificProperties) {\r
-               // 5th line - Scale: [ Auto/Manual ] Min: [ ## ] Max: [ ## ]\r
-               lScale = new Label(c, 0);\r
-               lScale.setText("&Scale: ");\r
-               gd1.applyTo(lScale);\r
-\r
-               Composite scaleComposite = new Composite(c, 0);\r
-               gd2.applyTo(scaleComposite);\r
-               GridLayoutFactory.fillDefaults().numColumns(8).applyTo(scaleComposite);\r
-\r
-               cScale = new Combo(scaleComposite, SWT.DROP_DOWN | SWT.BORDER | SWT.READ_ONLY);\r
-               cScale.add(AUTO);\r
-               cScale.add(MANUAL);\r
-               String _scale = data.scale != null ? data.scale.toString() : chartnode.get( ChartPreferences.P_SCALEMODE, ChartPreferences.DEFAULT_SCALEMODE ); \r
-               cScale.setText( _scale );\r
-               GridDataFactory.fillDefaults().span(2, 1).applyTo(cScale);\r
-               \r
-               lMin = new Label(scaleComposite, 0);\r
-               lMin.setText("&Min: ");\r
-               GridDataFactory.fillDefaults().span(1, 1).align(SWT.FILL, SWT.CENTER).applyTo(lMin);\r
-               tMin = new Text(scaleComposite, SWT.BORDER);\r
-               Double _min = data.min;\r
-               if ( _min == null ) _min = chartnode.getDouble( ChartPreferences.P_SCALE_MIN, 0.0);\r
-               tMin.setText( Double.toString(_min) );\r
-               GridDataFactory.fillDefaults().hint(80, SWT.DEFAULT).indent(5, 0).grab(true, false).span(2, 1).applyTo(tMin);\r
-               tMin.addModifyListener(new ModifyListener() {\r
-                   @Override\r
-                   public void modifyText(ModifyEvent e) {\r
-                       data.min = parseDouble(tMin, false, 0.0);\r
-                    if (data.min != null)\r
-                        cScale.setText(MANUAL);\r
-                       validate();\r
-                   }\r
-               });\r
-\r
-               lMax = new Label(scaleComposite, 0);\r
-               lMax.setText("Ma&x: ");\r
-               GridDataFactory.fillDefaults().span(1, 1).align(SWT.FILL, SWT.CENTER).applyTo(lMax);\r
-               tMax = new Text(scaleComposite, SWT.BORDER);\r
-               Double _max = data.max;\r
-               if ( _max==null ) _max = chartnode.getDouble( ChartPreferences.P_SCALE_MAX, 100.0);\r
-               tMax.setText( Double.toString(_max) );\r
-               GridDataFactory.fillDefaults().hint(80, SWT.DEFAULT).indent(5, 0).grab(true, false).span(2, 1).applyTo(tMax);\r
-            tMax.addModifyListener(new ModifyListener() {\r
-                @Override\r
-                public void modifyText(ModifyEvent e) {\r
-                    data.max = parseDouble(tMax, false, 100.0);\r
-                    if (data.max != null)\r
-                        cScale.setText(MANUAL);\r
-                    validate();\r
-                }\r
-            });\r
-        }\r
-\r
-        // 6th line - Subscription: [ Default / others ] [New] \r
-        lSubscription = new Label(c, 0);\r
-        lSubscription.setText("S&ubscription: ");\r
-        gd1.applyTo(lSubscription);\r
-        cSubscription = new Combo(c, SWT.BORDER);\r
-        for (String s : data.subscriptions) cSubscription.add(s);\r
-        gd2.applyTo(cSubscription);\r
-        String _subscription = data.subscription;\r
-        if ( _subscription == null ) _subscription = subscriptionnode.get( SubscriptionPreferences.P_SUBSCRIPTION_DEFAULT, data.subscriptions.length==0 ? "Default" : cSubscription.getItem(0) );\r
-        cSubscription.setText( _subscription );\r
-\r
-        if (!data.binaryMode) {\r
-               // 7th line - Deadband: [deadband] Interval: [interval]\r
-               lDeadband = new Label(c, 0);\r
-               lDeadband.setText("&Deadband: ");\r
-               gd1.applyTo(lDeadband);\r
-               tDeadband = new Text(c, SWT.BORDER);\r
-               Double _deadband = data.deadband;\r
-               if ( _deadband == null ) _deadband = subscriptionnode.getDouble( SubscriptionPreferences.P_SUBSCRIPTION_DEADBAND, SubscriptionPreferences.DEFAULT_SUBSCRIPTION_DEADBAND );\r
-               tDeadband.setText( Double.toString(_deadband) );\r
-               tDeadband.setEditable(data.mutableCollectionSettings);\r
-               GridDataFactory.fillDefaults().hint(110, SWT.DEFAULT).indent(5, 0).span(3, 1).applyTo(tDeadband);\r
-            tDeadband.addModifyListener(new ModifyListener() {\r
-                @Override\r
-                public void modifyText(ModifyEvent e) {\r
-                    data.deadband = parseDouble(tDeadband, false, 0.0);\r
-                    validate();\r
-                }\r
-            });\r
-\r
-               lInterval = new Label(c, 0);\r
-               lInterval.setText("&Interval: ");\r
-               lInterval.setToolTipText("Sampling Interval");\r
-               GridDataFactory.fillDefaults().span(2, 1).applyTo(lInterval);\r
-               tInterval = new Text(c, SWT.BORDER);\r
-               Double _interval = data.interval;\r
-               if ( _interval == null ) _interval = subscriptionnode.getDouble( SubscriptionPreferences.P_SUBSCRIPTION_INTERVAL, SubscriptionPreferences.DEFAULT_SUBSCRIPTION_INTERVAL );\r
-               tInterval.setText( Double.toString(_interval) );\r
-               tInterval.setEditable(data.mutableCollectionSettings);\r
-               GridDataFactory.fillDefaults().hint(110, SWT.DEFAULT).indent(5, 0).span(3, 1).applyTo(tInterval);\r
-            tInterval.addModifyListener(new ModifyListener() {\r
-                @Override\r
-                public void modifyText(ModifyEvent e) {\r
-                    data.interval = parseDouble(tInterval, false, 0.0);\r
-                    validate();\r
-                }\r
-            });\r
-        }\r
-\r
-        // 8th line - Gain: [gain] Bias: [bias]\r
-        if (!data.binaryMode) {\r
-               lGain = new Label(c, 0);\r
-               lGain.setText("&Gain: ");\r
-               gd1.applyTo(lGain);\r
-               tGain = new Text(c, SWT.BORDER);\r
-               Double _gain = data.gain;\r
-               if (_gain == null) _gain = subscriptionnode.getDouble( SubscriptionPreferences.P_SUBSCRIPTION_GAIN, 1.0);\r
-               tGain.setText( Double.toString(_gain) );\r
-               GridDataFactory.fillDefaults().hint(110, SWT.DEFAULT).indent(5, 0).span(3, 1).applyTo(tGain);\r
-            tGain.addModifyListener(new ModifyListener() {\r
-                @Override\r
-                public void modifyText(ModifyEvent e) {\r
-                    data.gain = parseDouble(tGain, false, 1.0);\r
-                    validate();\r
-                }\r
-            });\r
-\r
-               lBias = new Label(c, 0);\r
-               lBias.setText("&Bias: ");\r
-               GridDataFactory.fillDefaults().span(2, 1).applyTo(lBias);\r
-               tBias = new Text(c, SWT.BORDER);\r
-               Double _bias = data.bias;\r
-               if (_bias == null) _bias = subscriptionnode.getDouble( SubscriptionPreferences.P_SUBSCRIPTION_BIAS, 0.0);\r
-               tBias.setText( Double.toString(_bias) );\r
-               GridDataFactory.fillDefaults().hint(110, SWT.DEFAULT).indent(5, 0).span(3, 1).applyTo(tBias);\r
-            tBias.addModifyListener(new ModifyListener() {\r
-                @Override\r
-                public void modifyText(ModifyEvent e) {\r
-                    data.bias = parseDouble(tBias, false, 0.0);\r
-                    validate();\r
-                }\r
-            });\r
-        }\r
-\r
-        // 9th line - Unit: [unit]\r
-        if (!data.binaryMode) {\r
-               lUnit = new Label(c, 0);\r
-               lUnit.setText("U&nit: ");\r
-               gd1.applyTo(lUnit);\r
-               tUnit = new Text(c, SWT.BORDER);\r
-               String _unit = data.unit;\r
-               if (_unit==null) _unit = subscriptionnode.get( SubscriptionPreferences.P_SUBSCRIPTION_UNIT, "");\r
-               tUnit.setText( _unit );\r
-               GridDataFactory.fillDefaults().hint(100, SWT.DEFAULT).indent(5, 0).span(3, 1).applyTo(tUnit);\r
-               // Fill line\r
-            GridDataFactory.fillDefaults().span(5, 1).applyTo(new Label(c, 0));\r
-        }\r
-\r
-        // Disable fields that edit subscription item\r
-        if (!data.hasSubscriptionItem) {\r
-               tLabel.setEnabled( false );\r
-               cSubscription.setEnabled( false );\r
-               tDeadband.setEnabled( false );\r
-               tInterval.setEnabled( false );\r
-               tGain.setEnabled( false );\r
-               tBias.setEnabled( false );\r
-               tUnit.setEnabled( false );\r
-        }\r
-\r
-        // 10th line - Color stroke / color\r
-        if (includeChartSpecificProperties) {\r
-            createStrokeGroup(c);\r
-        }\r
-\r
-        saveAsPrefs = new Button(c, SWT.CHECK);\r
-        saveAsPrefs.setText("Sav&e as new defaults");\r
-        saveAsPrefs.setToolTipText("Sav&e these settings as new defaults");\r
-        // Always unchecked by default.\r
-        saveAsPrefs.setSelection(false);\r
-        GridDataFactory.fillDefaults().hint(100, SWT.DEFAULT).span(9, 1).applyTo(saveAsPrefs);\r
-\r
-        // Error decorations\r
-        Image image = FieldDecorationRegistry.getDefault()\r
-                .getFieldDecoration(FieldDecorationRegistry.DEC_ERROR)\r
-                .getImage();\r
-\r
-        if (!data.binaryMode) {\r
-            if (includeChartSpecificProperties) {\r
-                tMinDecor = createDecoration(tMin, SWT.LEFT | SWT.CENTER, image);\r
-                tMaxDecor = createDecoration(tMax, SWT.LEFT | SWT.CENTER, image);\r
-            }\r
-            tDeadbandDecor = createDecoration(tDeadband, SWT.LEFT | SWT.CENTER, image);\r
-            tIntervalDecor = createDecoration(tInterval, SWT.LEFT | SWT.CENTER, image);\r
-            tGainDecor = createDecoration(tGain, SWT.LEFT | SWT.CENTER, image);\r
-            tBiasDecor = createDecoration(tBias, SWT.LEFT | SWT.CENTER, image);\r
-        }\r
-\r
-        // Ensure that the data structure contains all the values that were\r
-        // initialized into the UI.\r
-        parseUiToData();\r
-\r
-        tLabel.setFocus();\r
-    }\r
-\r
-    private void createStrokeGroup(Composite c) {\r
-        GridDataFactory gd1 = GridDataFactory.fillDefaults().indent(10, 0).align(SWT.FILL, SWT.CENTER).span(1, 1);\r
-\r
-        Group gStroke = new Group(c, 0);\r
-        gStroke.setText("Trend Line Stroke");\r
-        GridLayoutFactory.fillDefaults().margins(8, 8).spacing(10, 10).numColumns(5).applyTo(gStroke);\r
-        GridDataFactory.fillDefaults().span(9, 1).applyTo(gStroke);\r
-\r
-        if (!data.binaryMode) {\r
-            // Custom stroke\r
-            Label lStrokeWidth = new Label(gStroke, 0);\r
-            lStrokeWidth.setText("&Width:");\r
-            gd1.applyTo(lStrokeWidth);\r
-            sStrokeWidth = new Spinner(gStroke, SWT.BORDER);\r
-            sStrokeWidth.setDigits(1);\r
-            sStrokeWidth.setIncrement(5);\r
-            sStrokeWidth.setMinimum(10);\r
-            sStrokeWidth.setMaximum(50);\r
-            GridDataFactory.fillDefaults().indent(10, 0).grab(false, false).span(1, 1).applyTo(sStrokeWidth);\r
-\r
-            if (data.strokeWidth != null) {\r
-                sStrokeWidth.setSelection((int) (data.strokeWidth * 10));\r
-            }\r
-\r
-            sStrokeWidth.addSelectionListener(new SelectionAdapter() {\r
-                @Override\r
-                public void widgetSelected(SelectionEvent e) {\r
-                    float newWidth = ((float) sStrokeWidth.getSelection()) * 0.1f;\r
-                    data.strokeWidth = newWidth;\r
-                }\r
-            });\r
-        }\r
-\r
-        Label lColor = new Label(gStroke, 0);\r
-        lColor.setText("&Color: ");\r
-        gd1.applyTo(lColor);\r
-        colorSelector = new ColorSelector(gStroke);\r
-        GridDataFactory.fillDefaults().indent(10, 0).span(1, 1).grab(false, false).applyTo(colorSelector.getButton());\r
-        final RGB indexColor = toRGB(JarisPaints.getColor(data.index));\r
-        RGB customColor = data.color != null ? toRGB(data.color) : indexColor;\r
-        colorSelector.setColorValue(customColor);\r
-\r
-        final Button bResetColor = new Button(gStroke, SWT.PUSH);\r
-        bResetColor.setText("Reset Color");\r
-        bResetColor.setToolTipText("Reset Color to Item Index Default");\r
-        bResetColor.setEnabled(!indexColor.equals(customColor));\r
-\r
-        colorSelector.addListener(new IPropertyChangeListener() {\r
-            @Override\r
-            public void propertyChange(PropertyChangeEvent event) {\r
-                RGB rgb = colorSelector.getColorValue();\r
-                data.color = new Color(rgb.red, rgb.green, rgb.blue).getColorComponents(new float[4]);\r
-                data.color[3] = 1;\r
-                bResetColor.setEnabled(!indexColor.equals(rgb));\r
-            }\r
-        });\r
-\r
-        bResetColor.addSelectionListener(new SelectionAdapter() {\r
-            @Override\r
-            public void widgetSelected(SelectionEvent e) {\r
-                data.color = null;\r
-                colorSelector.setColorValue(toRGB(JarisPaints.getColor(data.index)));\r
-                bResetColor.setEnabled(false);\r
-            }\r
-        });\r
-    }\r
-\r
-    protected RGB toRGB(float[] color) {\r
-        float r = color[0];\r
-        float g = color[1];\r
-        float b = color[2];\r
-        return new RGB( (int) (r*255+0.5), (int) (g*255+0.5), (int) (b*255+0.5) );\r
-    }\r
-\r
-    protected RGB toRGB(Color color) {\r
-        return new RGB(color.getRed(), color.getGreen(), color.getBlue());\r
-    }\r
-\r
-    protected ControlDecoration createDecoration(Control control, int position, Image image) {\r
-        ControlDecoration d = new ControlDecoration(control, position);\r
-        d.setMarginWidth(2);\r
-        d.setImage(image);\r
-        d.hide();\r
-        return d;\r
-    }\r
-\r
-    protected Double parseDouble(Text text, boolean returnDefault, double defaultValue) {\r
-        if (text == null)\r
-            return null;\r
-        try {\r
-            String s = text.getText();\r
-            s = s.replace(',', '.');\r
-            return Double.valueOf(s);\r
-        } catch ( NumberFormatException nfe ) {\r
-            return returnDefault ? defaultValue : null;\r
-        }\r
-    }\r
-\r
-    protected void parseUiToData() {\r
-        data.label = tLabel.getText();\r
-        if (!data.binaryMode) {\r
-            if (includeChartSpecificProperties) {\r
-                data.min = parseDouble(tMin, true, 0.0);\r
-                data.max = parseDouble(tMax, true, 100.0);\r
-            }\r
-            data.interval = parseDouble(tInterval, true, 0.0);\r
-            data.deadband = parseDouble(tDeadband, true, 0.0);\r
-            data.gain = parseDouble(tGain, true, 1.0);\r
-            data.bias = parseDouble(tBias, true, 0.0);\r
-            data.unit = tUnit.getText();\r
-\r
-            if (includeChartSpecificProperties) {\r
-//                data.drawmode = DrawMode.valueOf( cDrawMode.getText() );\r
-                data.drawmode = DrawMode.Line;\r
-\r
-                if (cScale.getText().equals("Manual")) {\r
-                    data.scale = new Scale.Manual(data.min, data.max);\r
-                } else if (cScale.getText().equals("Auto")) {\r
-                    data.scale = new Scale.Auto();\r
-                }\r
-            }\r
-        }\r
-        data.subscription = cSubscription.getText();\r
-    }\r
-\r
-    @Override\r
-    protected void buttonPressed(int buttonId) {\r
-        if (ChartDialogConstants.APPLY_ID == buttonId) {\r
-            applyPressed();\r
-        } else {\r
-            super.buttonPressed(buttonId);\r
-        }\r
-    }\r
-\r
-    private void applyPressed() {\r
-        if (applyAction != null) {\r
-            parseUiToData();\r
-            if (!baseData.equals(data)) {\r
-                baseData.readFrom(data);\r
-                applyAction.run();\r
-            }\r
-        }\r
-    }\r
-\r
-    @Override\r
-    protected void okPressed() {\r
-        boolean savePrefs = saveAsPrefs.getSelection();\r
-        parseUiToData();\r
-\r
-        if (!data.binaryMode && savePrefs) {\r
-            if (includeChartSpecificProperties) {\r
-                chartnode.put( ChartPreferences.P_DRAWMODE, data.drawmode.name() );\r
-                chartnode.put( ChartPreferences.P_SCALEMODE, data.scale instanceof Scale.Manual ? "Manual" : "Auto" );\r
-                chartnode.putDouble( ChartPreferences.P_SCALE_MIN, data.min );\r
-                chartnode.putDouble( ChartPreferences.P_SCALE_MAX, data.max );\r
-            }\r
-            subscriptionnode.putDouble( SubscriptionPreferences.P_SUBSCRIPTION_DEADBAND, data.deadband );\r
-            subscriptionnode.putDouble( SubscriptionPreferences.P_SUBSCRIPTION_INTERVAL, data.interval );\r
-            subscriptionnode.putDouble( SubscriptionPreferences.P_SUBSCRIPTION_GAIN, data.gain );\r
-            subscriptionnode.putDouble( SubscriptionPreferences.P_SUBSCRIPTION_BIAS, data.bias );\r
-            subscriptionnode.put( SubscriptionPreferences.P_SUBSCRIPTION_UNIT, data.unit );\r
-        }\r
-\r
-        if (savePrefs)\r
-            subscriptionnode.put( SubscriptionPreferences.P_SUBSCRIPTION_DEFAULT, data.subscription );\r
-\r
-        if (applyAction != null && !baseData.equals(data))\r
-            applyAction.run();\r
-        super.okPressed();\r
-    }\r
-\r
-    @Override\r
-    protected void configureShell(Shell newShell) {\r
-        super.configureShell(newShell);\r
-        newShell.setText(\r
-                includeChartSpecificProperties ? "Edit Chart Item"\r
-                                               : "Edit Subscription Item"\r
-                );\r
-    }\r
-\r
-    public void validate() {\r
-        boolean ok = true;\r
-        if (!data.binaryMode) {\r
-            if (includeChartSpecificProperties) {\r
-                setDecoration(tMinDecor, null);\r
-                setDecoration(tMaxDecor, null);\r
-                if (tMin != null && tMax != null) {\r
-                    if (data.min == null || data.max == null) {\r
-                        ok = false;\r
-                        if (data.min == null)\r
-                            setDecoration(tMinDecor, "Invalid non-numeric value");\r
-                        if (data.max == null)\r
-                            setDecoration(tMaxDecor, "Invalid non-numeric value");\r
-                    } else {\r
-                        if (data.min >= data.max) {\r
-                            setDecoration(tMinDecor, "Minimum value must be smaller than maximum value");\r
-                            setDecoration(tMaxDecor, "Maximum value must be larger than minimum value");\r
-                            ok = false;\r
-                        }\r
-                    }\r
-                }\r
-            }\r
-\r
-            ok &= data.interval != null && data.deadband != null && data.bias != null && data.gain != null;\r
-            setDecoration(tIntervalDecor, data.interval == null ? "Invalid non-numeric value" : null);\r
-            if (data.interval != null && data.interval < 0)\r
-                setDecoration(tIntervalDecor, "Sampling interval cannot be negative");\r
-            setDecoration(tDeadbandDecor, data.deadband == null ? "Invalid non-numeric value" : null);\r
-            if (data.deadband != null && data.deadband < 0)\r
-                setDecoration(tDeadbandDecor, "Deadband cannot be negative");\r
-            setDecoration(tGainDecor, data.gain == null ? "Invalid non-numeric value" : null);\r
-            setDecoration(tBiasDecor, data.bias == null ? "Invalid non-numeric value" : null);\r
-        }\r
-        setFinishable(ok);\r
-    }\r
-\r
-    private void setDecoration(ControlDecoration decor, String desc) {\r
-        if (decor == null)\r
-            return;\r
-        if (desc != null) {\r
-            decor.setDescriptionText(desc);\r
-            decor.show();\r
-        } else {\r
-            decor.hide();\r
-        }\r
-    }\r
-\r
-    protected void setFinishable(boolean ok) {\r
-        getButton(OK).setEnabled(ok);\r
-    }\r
-\r
-}\r
+/*******************************************************************************
+ * Copyright (c) 2007, 2011 Association for Decentralized Information Management in
+ * Industry THTH ry.
+ * All rights reserved. This program and the accompanying materials
+ * are made available under the terms of the Eclipse Public License v1.0
+ * which accompanies this distribution, and is available at
+ * http://www.eclipse.org/legal/epl-v10.html
+ *
+ * Contributors:
+ *     VTT Technical Research Centre of Finland - initial API and implementation
+ *******************************************************************************/
+package org.simantics.charts.ui;
+
+import java.awt.Color;
+
+import org.eclipse.core.runtime.preferences.IEclipsePreferences;
+import org.eclipse.core.runtime.preferences.InstanceScope;
+import org.eclipse.jface.dialogs.Dialog;
+import org.eclipse.jface.fieldassist.ControlDecoration;
+import org.eclipse.jface.fieldassist.FieldDecorationRegistry;
+import org.eclipse.jface.layout.GridDataFactory;
+import org.eclipse.jface.layout.GridLayoutFactory;
+import org.eclipse.jface.preference.ColorSelector;
+import org.eclipse.jface.util.IPropertyChangeListener;
+import org.eclipse.jface.util.PropertyChangeEvent;
+import org.eclipse.swt.SWT;
+import org.eclipse.swt.events.ModifyEvent;
+import org.eclipse.swt.events.ModifyListener;
+import org.eclipse.swt.events.SelectionAdapter;
+import org.eclipse.swt.events.SelectionEvent;
+import org.eclipse.swt.graphics.Image;
+import org.eclipse.swt.graphics.RGB;
+import org.eclipse.swt.widgets.Button;
+import org.eclipse.swt.widgets.Combo;
+import org.eclipse.swt.widgets.Composite;
+import org.eclipse.swt.widgets.Control;
+import org.eclipse.swt.widgets.Group;
+import org.eclipse.swt.widgets.Label;
+import org.eclipse.swt.widgets.Shell;
+import org.eclipse.swt.widgets.Spinner;
+import org.eclipse.swt.widgets.Text;
+import org.simantics.charts.preference.ChartPreferences;
+import org.simantics.charts.query.ChartAndSubscriptionItemData;
+import org.simantics.databoard.util.URIStringUtils;
+import org.simantics.modeling.preferences.SubscriptionPreferences;
+import org.simantics.trend.configuration.Scale;
+import org.simantics.trend.configuration.TrendItem.DrawMode;
+import org.simantics.trend.impl.JarisPaints;
+
+/**
+ * ChartAndSubscriptionItemDialog is a dialog that has the following property
+ * queries:
+ *   Chart name  (Read only)
+ *   Variable    (Read only)
+ *   Label
+ *   Draw Mode   (Enumeration)
+ *   Scale, min, max ( Enum, int, int)
+ *   Subscription ( Text )
+ *   Deadband, Interval, Gain, Bias, Unit (Double)
+ *   Stroke settings (width, color)
+ * 
+ * If the dialog is opened in binary mode the content is more stripped.
+ *   Chart name
+ *   Variable
+ *   Label
+ *   Subscription
+ *   Stroke settings
+ * 
+ * @author toni.kalajainen
+ */
+public class ChartAndSubscriptionItemDialog extends Dialog {
+
+    private static final String MANUAL = "Manual";
+    private static final String AUTO = "Auto";
+
+    // UI
+    Label lChart, lRVI, lLabel, lDrawMode, lScale, lMin, lMax, lSubscription, lDeadband, lInterval, lGain, lBias, lUnit;
+    Text tRVI, tLabel, tDeadband, tInterval, tGain, tBias, tUnit;
+    Combo cSubscription;
+    Button saveAsPrefs;
+
+    // chart specific, not initialized, if includeChartSpecificProperties=false
+    Text tChart, tMin, tMax;
+    Combo cDrawMode, cScale;
+    Spinner sStrokeWidth;
+    ColorSelector colorSelector;
+
+    // Input error decorations
+    ControlDecoration tMinDecor;
+    ControlDecoration tMaxDecor;
+    ControlDecoration tDeadbandDecor;
+    ControlDecoration tIntervalDecor;
+    ControlDecoration tGainDecor;
+    ControlDecoration tBiasDecor;
+
+    //
+    ChartAndSubscriptionItemData baseData = new ChartAndSubscriptionItemData();
+    ChartAndSubscriptionItemData data;
+
+    final Runnable applyAction;
+
+    IEclipsePreferences chartnode; // chart specific, not initialized, if includeChartSpecificProperties=false
+    IEclipsePreferences subscriptionnode;
+    boolean includeChartSpecificProperties;
+
+    public ChartAndSubscriptionItemDialog(Shell parentShell, ChartAndSubscriptionItemData data, boolean includeChartSpecificProperties) {
+        this(parentShell, data, includeChartSpecificProperties, null);
+    }
+
+    public ChartAndSubscriptionItemDialog(Shell parentShell, ChartAndSubscriptionItemData data, boolean includeChartSpecificProperties, Runnable applyAction) {
+        super(parentShell);
+        this.data = data;
+        this.baseData.readFrom(data);
+        this.includeChartSpecificProperties = includeChartSpecificProperties;
+        this.applyAction = applyAction;
+
+        if(includeChartSpecificProperties)
+            chartnode = InstanceScope.INSTANCE.getNode( ChartPreferences.P_NODE );
+        subscriptionnode = InstanceScope.INSTANCE.getNode( SubscriptionPreferences.P_NODE );
+
+        setShellStyle(SWT.RESIZE | SWT.TITLE | SWT.CLOSE | SWT.BORDER);
+    }
+
+    @Override
+    protected Control createContents(Composite parent) {
+        Control c = super.createContents(parent);
+        validate();
+        return c;
+    }
+
+    @Override
+    protected void createButtonsForButtonBar(Composite parent) {
+        if (applyAction != null)
+            createButton(parent, ChartDialogConstants.APPLY_ID, ChartDialogConstants.APPLY_LABEL, true);
+        super.createButtonsForButtonBar(parent);
+    }
+
+    public void setInput(ChartAndSubscriptionItemData data) {
+        // TODO: implement so that this reinitializes the UI of the dialog according to the new specified data
+    }
+
+    protected void disposeDialogAreaContents() {
+        Composite area = (Composite) dialogArea;
+        for (Control child : area.getChildren())
+            child.dispose();
+    }
+
+    @Override
+    protected Control createDialogArea(Composite parent) {
+        Composite c = (Composite) super.createDialogArea(parent);
+        GridLayoutFactory.fillDefaults().margins(8, 8).numColumns(9).applyTo(c);
+        createDialogAreaContents(c);
+        return c;
+    }
+
+    protected void createDialogAreaContents(Composite c) {
+        GridDataFactory gd1 = GridDataFactory.fillDefaults().align(SWT.FILL, SWT.CENTER).span(1, 1);
+        GridDataFactory gd2 = GridDataFactory.fillDefaults().grab(true, false).indent(5, 0).span(8, 1);
+
+        // 1st line - Chart: [chart name]
+        if(includeChartSpecificProperties) {
+            lChart = new Label(c, 0);
+            lChart.setText("Chart:");
+            gd1.applyTo(lChart);
+            tChart = new Text(c, SWT.BORDER | SWT.READ_ONLY);
+            //tChart.setEnabled( false );
+            if ( data.chartName != null ) tChart.setText( data.chartName );
+            gd2.applyTo(tChart);
+        }
+
+        // 2nd line - Variable Reference: [chart item label]
+        lRVI = new Label(c, 0);
+        lRVI.setText("Variable: ");
+        gd1.applyTo(lRVI);
+        tRVI = new Text(c, SWT.BORDER | SWT.READ_ONLY);
+        //tRVI.setEnabled(false);
+        if ( data.variableReference!=null ) tRVI.setText(URIStringUtils.unescape( data.variableReference ));
+        gd2.applyTo(tRVI);
+
+        // 3rd line - Label: [chart item label]
+        lLabel = new Label(c, 0);
+        lLabel.setText("&Label: ");
+        gd1.applyTo(lLabel);
+        tLabel = new Text(c, SWT.BORDER);
+        if ( data.label!=null ) tLabel.setText(data.label);
+        gd2.applyTo(tLabel);
+
+//        if (!data.binaryMode) {
+//             // 4th line - DrawMode: [ drop-down-list V ]
+//             lDrawMode = new Label(c, 0);
+//             lDrawMode.setText("DrawMode :");
+//             gd1.applyTo(lDrawMode);
+//             cDrawMode = new Combo(c, SWT.DROP_DOWN | SWT.BORDER | SWT.READ_ONLY);
+//             for (DrawMode dm : DrawMode.values()) cDrawMode.add( dm.name() );
+//             String _drawmode = data.drawmode!=null ? data.drawmode.toString() : chartnode.get( ChartPreferences.P_DRAWMODE, ChartPreferences.DEFAULT_DRAWMODE ); 
+//             cDrawMode.setText( _drawmode );
+//             gd2.applyTo(cDrawMode);
+//        }
+
+        if (!data.binaryMode && includeChartSpecificProperties) {
+               // 5th line - Scale: [ Auto/Manual ] Min: [ ## ] Max: [ ## ]
+               lScale = new Label(c, 0);
+               lScale.setText("&Scale: ");
+               gd1.applyTo(lScale);
+
+               Composite scaleComposite = new Composite(c, 0);
+               gd2.applyTo(scaleComposite);
+               GridLayoutFactory.fillDefaults().numColumns(8).applyTo(scaleComposite);
+
+               cScale = new Combo(scaleComposite, SWT.DROP_DOWN | SWT.BORDER | SWT.READ_ONLY);
+               cScale.add(AUTO);
+               cScale.add(MANUAL);
+               String _scale = data.scale != null ? data.scale.toString() : chartnode.get( ChartPreferences.P_SCALEMODE, ChartPreferences.DEFAULT_SCALEMODE ); 
+               cScale.setText( _scale );
+               GridDataFactory.fillDefaults().span(2, 1).applyTo(cScale);
+               
+               lMin = new Label(scaleComposite, 0);
+               lMin.setText("&Min: ");
+               GridDataFactory.fillDefaults().span(1, 1).align(SWT.FILL, SWT.CENTER).applyTo(lMin);
+               tMin = new Text(scaleComposite, SWT.BORDER);
+               Double _min = data.min;
+               if ( _min == null ) _min = chartnode.getDouble( ChartPreferences.P_SCALE_MIN, 0.0);
+               tMin.setText( Double.toString(_min) );
+               GridDataFactory.fillDefaults().hint(80, SWT.DEFAULT).indent(5, 0).grab(true, false).span(2, 1).applyTo(tMin);
+               tMin.addModifyListener(new ModifyListener() {
+                   @Override
+                   public void modifyText(ModifyEvent e) {
+                       data.min = parseDouble(tMin, false, 0.0);
+                    if (data.min != null)
+                        cScale.setText(MANUAL);
+                       validate();
+                   }
+               });
+
+               lMax = new Label(scaleComposite, 0);
+               lMax.setText("Ma&x: ");
+               GridDataFactory.fillDefaults().span(1, 1).align(SWT.FILL, SWT.CENTER).applyTo(lMax);
+               tMax = new Text(scaleComposite, SWT.BORDER);
+               Double _max = data.max;
+               if ( _max==null ) _max = chartnode.getDouble( ChartPreferences.P_SCALE_MAX, 100.0);
+               tMax.setText( Double.toString(_max) );
+               GridDataFactory.fillDefaults().hint(80, SWT.DEFAULT).indent(5, 0).grab(true, false).span(2, 1).applyTo(tMax);
+            tMax.addModifyListener(new ModifyListener() {
+                @Override
+                public void modifyText(ModifyEvent e) {
+                    data.max = parseDouble(tMax, false, 100.0);
+                    if (data.max != null)
+                        cScale.setText(MANUAL);
+                    validate();
+                }
+            });
+        }
+
+        // 6th line - Subscription: [ Default / others ] [New] 
+        lSubscription = new Label(c, 0);
+        lSubscription.setText("S&ubscription: ");
+        gd1.applyTo(lSubscription);
+        cSubscription = new Combo(c, SWT.BORDER);
+        for (String s : data.subscriptions) cSubscription.add(s);
+        gd2.applyTo(cSubscription);
+        String _subscription = data.subscription;
+        if ( _subscription == null ) _subscription = subscriptionnode.get( SubscriptionPreferences.P_SUBSCRIPTION_DEFAULT, data.subscriptions.length==0 ? "Default" : cSubscription.getItem(0) );
+        cSubscription.setText( _subscription );
+
+        if (!data.binaryMode) {
+               // 7th line - Deadband: [deadband] Interval: [interval]
+               lDeadband = new Label(c, 0);
+               lDeadband.setText("&Deadband: ");
+               gd1.applyTo(lDeadband);
+               tDeadband = new Text(c, SWT.BORDER);
+               Double _deadband = data.deadband;
+               if ( _deadband == null ) _deadband = subscriptionnode.getDouble( SubscriptionPreferences.P_SUBSCRIPTION_DEADBAND, SubscriptionPreferences.DEFAULT_SUBSCRIPTION_DEADBAND );
+               tDeadband.setText( Double.toString(_deadband) );
+               tDeadband.setEditable(data.mutableCollectionSettings);
+               GridDataFactory.fillDefaults().hint(110, SWT.DEFAULT).indent(5, 0).span(3, 1).applyTo(tDeadband);
+            tDeadband.addModifyListener(new ModifyListener() {
+                @Override
+                public void modifyText(ModifyEvent e) {
+                    data.deadband = parseDouble(tDeadband, false, 0.0);
+                    validate();
+                }
+            });
+
+               lInterval = new Label(c, 0);
+               lInterval.setText("&Interval: ");
+               lInterval.setToolTipText("Sampling Interval");
+               GridDataFactory.fillDefaults().span(2, 1).applyTo(lInterval);
+               tInterval = new Text(c, SWT.BORDER);
+               Double _interval = data.interval;
+               if ( _interval == null ) _interval = subscriptionnode.getDouble( SubscriptionPreferences.P_SUBSCRIPTION_INTERVAL, SubscriptionPreferences.DEFAULT_SUBSCRIPTION_INTERVAL );
+               tInterval.setText( Double.toString(_interval) );
+               tInterval.setEditable(data.mutableCollectionSettings);
+               GridDataFactory.fillDefaults().hint(110, SWT.DEFAULT).indent(5, 0).span(3, 1).applyTo(tInterval);
+            tInterval.addModifyListener(new ModifyListener() {
+                @Override
+                public void modifyText(ModifyEvent e) {
+                    data.interval = parseDouble(tInterval, false, 0.0);
+                    validate();
+                }
+            });
+        }
+
+        // 8th line - Gain: [gain] Bias: [bias]
+        if (!data.binaryMode) {
+               lGain = new Label(c, 0);
+               lGain.setText("&Gain: ");
+               gd1.applyTo(lGain);
+               tGain = new Text(c, SWT.BORDER);
+               Double _gain = data.gain;
+               if (_gain == null) _gain = subscriptionnode.getDouble( SubscriptionPreferences.P_SUBSCRIPTION_GAIN, 1.0);
+               tGain.setText( Double.toString(_gain) );
+               GridDataFactory.fillDefaults().hint(110, SWT.DEFAULT).indent(5, 0).span(3, 1).applyTo(tGain);
+            tGain.addModifyListener(new ModifyListener() {
+                @Override
+                public void modifyText(ModifyEvent e) {
+                    data.gain = parseDouble(tGain, false, 1.0);
+                    validate();
+                }
+            });
+
+               lBias = new Label(c, 0);
+               lBias.setText("&Bias: ");
+               GridDataFactory.fillDefaults().span(2, 1).applyTo(lBias);
+               tBias = new Text(c, SWT.BORDER);
+               Double _bias = data.bias;
+               if (_bias == null) _bias = subscriptionnode.getDouble( SubscriptionPreferences.P_SUBSCRIPTION_BIAS, 0.0);
+               tBias.setText( Double.toString(_bias) );
+               GridDataFactory.fillDefaults().hint(110, SWT.DEFAULT).indent(5, 0).span(3, 1).applyTo(tBias);
+            tBias.addModifyListener(new ModifyListener() {
+                @Override
+                public void modifyText(ModifyEvent e) {
+                    data.bias = parseDouble(tBias, false, 0.0);
+                    validate();
+                }
+            });
+        }
+
+        // 9th line - Unit: [unit]
+        if (!data.binaryMode) {
+               lUnit = new Label(c, 0);
+               lUnit.setText("U&nit: ");
+               gd1.applyTo(lUnit);
+               tUnit = new Text(c, SWT.BORDER);
+               String _unit = data.unit;
+               if (_unit==null) _unit = subscriptionnode.get( SubscriptionPreferences.P_SUBSCRIPTION_UNIT, "");
+               tUnit.setText( _unit );
+               GridDataFactory.fillDefaults().hint(100, SWT.DEFAULT).indent(5, 0).span(3, 1).applyTo(tUnit);
+               // Fill line
+            GridDataFactory.fillDefaults().span(5, 1).applyTo(new Label(c, 0));
+        }
+
+        // Disable fields that edit subscription item
+        if (!data.hasSubscriptionItem) {
+               tLabel.setEnabled( false );
+               cSubscription.setEnabled( false );
+               tDeadband.setEnabled( false );
+               tInterval.setEnabled( false );
+               tGain.setEnabled( false );
+               tBias.setEnabled( false );
+               tUnit.setEnabled( false );
+        }
+
+        // 10th line - Color stroke / color
+        if (includeChartSpecificProperties) {
+            createStrokeGroup(c);
+        }
+
+        saveAsPrefs = new Button(c, SWT.CHECK);
+        saveAsPrefs.setText("Sav&e as new defaults");
+        saveAsPrefs.setToolTipText("Sav&e these settings as new defaults");
+        // Always unchecked by default.
+        saveAsPrefs.setSelection(false);
+        GridDataFactory.fillDefaults().hint(100, SWT.DEFAULT).span(9, 1).applyTo(saveAsPrefs);
+
+        // Error decorations
+        Image image = FieldDecorationRegistry.getDefault()
+                .getFieldDecoration(FieldDecorationRegistry.DEC_ERROR)
+                .getImage();
+
+        if (!data.binaryMode) {
+            if (includeChartSpecificProperties) {
+                tMinDecor = createDecoration(tMin, SWT.LEFT | SWT.CENTER, image);
+                tMaxDecor = createDecoration(tMax, SWT.LEFT | SWT.CENTER, image);
+            }
+            tDeadbandDecor = createDecoration(tDeadband, SWT.LEFT | SWT.CENTER, image);
+            tIntervalDecor = createDecoration(tInterval, SWT.LEFT | SWT.CENTER, image);
+            tGainDecor = createDecoration(tGain, SWT.LEFT | SWT.CENTER, image);
+            tBiasDecor = createDecoration(tBias, SWT.LEFT | SWT.CENTER, image);
+        }
+
+        // Ensure that the data structure contains all the values that were
+        // initialized into the UI.
+        parseUiToData();
+
+        tLabel.setFocus();
+    }
+
+    private void createStrokeGroup(Composite c) {
+        GridDataFactory gd1 = GridDataFactory.fillDefaults().indent(10, 0).align(SWT.FILL, SWT.CENTER).span(1, 1);
+
+        Group gStroke = new Group(c, 0);
+        gStroke.setText("Trend Line Stroke");
+        GridLayoutFactory.fillDefaults().margins(8, 8).spacing(10, 10).numColumns(5).applyTo(gStroke);
+        GridDataFactory.fillDefaults().span(9, 1).applyTo(gStroke);
+
+        if (!data.binaryMode) {
+            // Custom stroke
+            Label lStrokeWidth = new Label(gStroke, 0);
+            lStrokeWidth.setText("&Width:");
+            gd1.applyTo(lStrokeWidth);
+            sStrokeWidth = new Spinner(gStroke, SWT.BORDER);
+            sStrokeWidth.setDigits(1);
+            sStrokeWidth.setIncrement(5);
+            sStrokeWidth.setMinimum(10);
+            sStrokeWidth.setMaximum(50);
+            GridDataFactory.fillDefaults().indent(10, 0).grab(false, false).span(1, 1).applyTo(sStrokeWidth);
+
+            if (data.strokeWidth != null) {
+                sStrokeWidth.setSelection((int) (data.strokeWidth * 10));
+            }
+
+            sStrokeWidth.addSelectionListener(new SelectionAdapter() {
+                @Override
+                public void widgetSelected(SelectionEvent e) {
+                    float newWidth = ((float) sStrokeWidth.getSelection()) * 0.1f;
+                    data.strokeWidth = newWidth;
+                }
+            });
+        }
+
+        Label lColor = new Label(gStroke, 0);
+        lColor.setText("&Color: ");
+        gd1.applyTo(lColor);
+        colorSelector = new ColorSelector(gStroke);
+        GridDataFactory.fillDefaults().indent(10, 0).span(1, 1).grab(false, false).applyTo(colorSelector.getButton());
+        final RGB indexColor = toRGB(JarisPaints.getColor(data.index));
+        RGB customColor = data.color != null ? toRGB(data.color) : indexColor;
+        colorSelector.setColorValue(customColor);
+
+        final Button bResetColor = new Button(gStroke, SWT.PUSH);
+        bResetColor.setText("Reset Color");
+        bResetColor.setToolTipText("Reset Color to Item Index Default");
+        bResetColor.setEnabled(!indexColor.equals(customColor));
+
+        colorSelector.addListener(new IPropertyChangeListener() {
+            @Override
+            public void propertyChange(PropertyChangeEvent event) {
+                RGB rgb = colorSelector.getColorValue();
+                data.color = new Color(rgb.red, rgb.green, rgb.blue).getColorComponents(new float[4]);
+                data.color[3] = 1;
+                bResetColor.setEnabled(!indexColor.equals(rgb));
+            }
+        });
+
+        bResetColor.addSelectionListener(new SelectionAdapter() {
+            @Override
+            public void widgetSelected(SelectionEvent e) {
+                data.color = null;
+                colorSelector.setColorValue(toRGB(JarisPaints.getColor(data.index)));
+                bResetColor.setEnabled(false);
+            }
+        });
+    }
+
+    protected RGB toRGB(float[] color) {
+        float r = color[0];
+        float g = color[1];
+        float b = color[2];
+        return new RGB( (int) (r*255+0.5), (int) (g*255+0.5), (int) (b*255+0.5) );
+    }
+
+    protected RGB toRGB(Color color) {
+        return new RGB(color.getRed(), color.getGreen(), color.getBlue());
+    }
+
+    protected ControlDecoration createDecoration(Control control, int position, Image image) {
+        ControlDecoration d = new ControlDecoration(control, position);
+        d.setMarginWidth(2);
+        d.setImage(image);
+        d.hide();
+        return d;
+    }
+
+    protected Double parseDouble(Text text, boolean returnDefault, double defaultValue) {
+        if (text == null)
+            return null;
+        try {
+            String s = text.getText();
+            s = s.replace(',', '.');
+            return Double.valueOf(s);
+        } catch ( NumberFormatException nfe ) {
+            return returnDefault ? defaultValue : null;
+        }
+    }
+
+    protected void parseUiToData() {
+        data.label = tLabel.getText();
+        if (!data.binaryMode) {
+            if (includeChartSpecificProperties) {
+                data.min = parseDouble(tMin, true, 0.0);
+                data.max = parseDouble(tMax, true, 100.0);
+            }
+            data.interval = parseDouble(tInterval, true, 0.0);
+            data.deadband = parseDouble(tDeadband, true, 0.0);
+            data.gain = parseDouble(tGain, true, 1.0);
+            data.bias = parseDouble(tBias, true, 0.0);
+            data.unit = tUnit.getText();
+
+            if (includeChartSpecificProperties) {
+//                data.drawmode = DrawMode.valueOf( cDrawMode.getText() );
+                data.drawmode = DrawMode.Line;
+
+                if (cScale.getText().equals("Manual")) {
+                    data.scale = new Scale.Manual(data.min, data.max);
+                } else if (cScale.getText().equals("Auto")) {
+                    data.scale = new Scale.Auto();
+                }
+            }
+        }
+        data.subscription = cSubscription.getText();
+    }
+
+    @Override
+    protected void buttonPressed(int buttonId) {
+        if (ChartDialogConstants.APPLY_ID == buttonId) {
+            applyPressed();
+        } else {
+            super.buttonPressed(buttonId);
+        }
+    }
+
+    private void applyPressed() {
+        if (applyAction != null) {
+            parseUiToData();
+            if (!baseData.equals(data)) {
+                baseData.readFrom(data);
+                applyAction.run();
+            }
+        }
+    }
+
+    @Override
+    protected void okPressed() {
+        boolean savePrefs = saveAsPrefs.getSelection();
+        parseUiToData();
+
+        if (!data.binaryMode && savePrefs) {
+            if (includeChartSpecificProperties) {
+                chartnode.put( ChartPreferences.P_DRAWMODE, data.drawmode.name() );
+                chartnode.put( ChartPreferences.P_SCALEMODE, data.scale instanceof Scale.Manual ? "Manual" : "Auto" );
+                chartnode.putDouble( ChartPreferences.P_SCALE_MIN, data.min );
+                chartnode.putDouble( ChartPreferences.P_SCALE_MAX, data.max );
+            }
+            subscriptionnode.putDouble( SubscriptionPreferences.P_SUBSCRIPTION_DEADBAND, data.deadband );
+            subscriptionnode.putDouble( SubscriptionPreferences.P_SUBSCRIPTION_INTERVAL, data.interval );
+            subscriptionnode.putDouble( SubscriptionPreferences.P_SUBSCRIPTION_GAIN, data.gain );
+            subscriptionnode.putDouble( SubscriptionPreferences.P_SUBSCRIPTION_BIAS, data.bias );
+            subscriptionnode.put( SubscriptionPreferences.P_SUBSCRIPTION_UNIT, data.unit );
+        }
+
+        if (savePrefs)
+            subscriptionnode.put( SubscriptionPreferences.P_SUBSCRIPTION_DEFAULT, data.subscription );
+
+        if (applyAction != null && !baseData.equals(data))
+            applyAction.run();
+        super.okPressed();
+    }
+
+    @Override
+    protected void configureShell(Shell newShell) {
+        super.configureShell(newShell);
+        newShell.setText(
+                includeChartSpecificProperties ? "Edit Chart Item"
+                                               : "Edit Subscription Item"
+                );
+    }
+
+    public void validate() {
+        boolean ok = true;
+        if (!data.binaryMode) {
+            if (includeChartSpecificProperties) {
+                setDecoration(tMinDecor, null);
+                setDecoration(tMaxDecor, null);
+                if (tMin != null && tMax != null) {
+                    if (data.min == null || data.max == null) {
+                        ok = false;
+                        if (data.min == null)
+                            setDecoration(tMinDecor, "Invalid non-numeric value");
+                        if (data.max == null)
+                            setDecoration(tMaxDecor, "Invalid non-numeric value");
+                    } else {
+                        if (data.min >= data.max) {
+                            setDecoration(tMinDecor, "Minimum value must be smaller than maximum value");
+                            setDecoration(tMaxDecor, "Maximum value must be larger than minimum value");
+                            ok = false;
+                        }
+                    }
+                }
+            }
+
+            ok &= data.interval != null && data.deadband != null && data.bias != null && data.gain != null;
+            setDecoration(tIntervalDecor, data.interval == null ? "Invalid non-numeric value" : null);
+            if (data.interval != null && data.interval < 0)
+                setDecoration(tIntervalDecor, "Sampling interval cannot be negative");
+            setDecoration(tDeadbandDecor, data.deadband == null ? "Invalid non-numeric value" : null);
+            if (data.deadband != null && data.deadband < 0)
+                setDecoration(tDeadbandDecor, "Deadband cannot be negative");
+            setDecoration(tGainDecor, data.gain == null ? "Invalid non-numeric value" : null);
+            setDecoration(tBiasDecor, data.bias == null ? "Invalid non-numeric value" : null);
+        }
+        setFinishable(ok);
+    }
+
+    private void setDecoration(ControlDecoration decor, String desc) {
+        if (decor == null)
+            return;
+        if (desc != null) {
+            decor.setDescriptionText(desc);
+            decor.show();
+        } else {
+            decor.hide();
+        }
+    }
+
+    protected void setFinishable(boolean ok) {
+        getButton(OK).setEnabled(ok);
+    }
+
+}