]> gerrit.simantics Code Review - simantics/platform.git/blobdiff - bundles/org.simantics.charts/src/org/simantics/charts/ui/ChartDialog.java
Fixed all line endings of the repository
[simantics/platform.git] / bundles / org.simantics.charts / src / org / simantics / charts / ui / ChartDialog.java
index 8615828da36fc850de782a93370c51d52fc7a6d3..f703f96bc8f57415692e7a89489532d21d17c896 100644 (file)
-package org.simantics.charts.ui;\r
-\r
-import java.awt.Color;\r
-import java.text.Format;\r
-import java.util.ArrayList;\r
-import java.util.List;\r
-import java.util.Set;\r
-\r
-import org.eclipse.core.runtime.IStatus;\r
-import org.eclipse.core.runtime.Status;\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.viewers.ArrayContentProvider;\r
-import org.eclipse.jface.viewers.DoubleClickEvent;\r
-import org.eclipse.jface.viewers.IDoubleClickListener;\r
-import org.eclipse.jface.viewers.ISelection;\r
-import org.eclipse.jface.viewers.LabelProvider;\r
-import org.eclipse.jface.viewers.ListViewer;\r
-import org.eclipse.swt.SWT;\r
-import org.eclipse.swt.events.KeyAdapter;\r
-import org.eclipse.swt.events.KeyEvent;\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.Scale;\r
-import org.eclipse.swt.widgets.Shell;\r
-import org.eclipse.swt.widgets.Text;\r
-import org.simantics.Simantics;\r
-import org.simantics.charts.Activator;\r
-import org.simantics.charts.query.ChartAndSubscriptionItemData;\r
-import org.simantics.charts.query.ChartAndSubscriptionItemReadQuery;\r
-import org.simantics.charts.ui.ChartData.ItemKey;\r
-import org.simantics.db.exception.DatabaseException;\r
-import org.simantics.modeling.ui.chart.property.ChartComposite;\r
-import org.simantics.trend.configuration.TrendItem;\r
-import org.simantics.trend.configuration.YAxisMode;\r
-import org.simantics.trend.impl.Plot;\r
-import org.simantics.utils.format.TimeFormat;\r
-import org.simantics.utils.ui.ISelectionUtils;\r
-\r
-/**\r
- * Dialog for chart properties:\r
- * \r
- * <pre>\r
- * Name:             [ Process ]\r
- * Time Start:       [   ]\r
- * Length:           [   ]\r
- * Scroll Increment: [--|--] n%\r
- * Axis Mode:        [Single/Multi]\r
- * [ ] Hairline Tracks Experiment Time\r
- * \r
- * --- Styling ------------------------------------------\r
- * | [ ] Background Vertical Gradient {color1} {color2} |\r
- * | [ ] Show Grid                    {grid color}      |\r
- * | [ ] Show Milestones                                |\r
- * ------------------------------------------------------\r
- * \r
- * --- Item visibility ---------------\r
- * | Visible          Hidden         |\r
- * | -----------      -------------- |\r
- * | |         | HIDE |            | |\r
- * | |         | SHOW |            | |\r
- * | -----------      -------------- |\r
- * -----------------------------------\r
- * </pre>\r
- * \r
- * @author toni.kalajainen@semantum.fi\r
- * @author Tuukka Lehtonen\r
- */\r
-public class ChartDialog extends Dialog {\r
-\r
-    private static final RGB DEFAULT_BG_COLOR1 = toRGB(Plot.PLOT_AREA_BG_GRADIENT_COLOR_BOTTOM.getColorComponents(new float[3]), null);\r
-    private static final RGB DEFAULT_BG_COLOR2 = toRGB(Plot.PLOT_AREA_BG_GRADIENT_COLOR_TOP.getColorComponents(new float[3]), null);\r
-    private static final RGB DEFAULT_GRID_COLOR = toRGB(Plot.GRID_LINE_COLOR.getColorComponents(new float[3]), null);\r
-\r
-    Format timeFormat = new TimeFormat(100, 3);\r
-\r
-    // UI\r
-    Label lName, lStartTime, lLength, lScrollIncrement, lAxisMode;\r
-    Text tName, tStartTime, tLength;\r
-    Scale sIncrement;\r
-    Combo cAxisMode;\r
-    Button bBackgroundGradient;\r
-    ColorSelector backgroundColor1;\r
-    ColorSelector backgroundColor2;\r
-    Button bShowGrid;\r
-    ColorSelector gridColor;\r
-    Button bMilestones;\r
-    Button bTrackExperimentTime;\r
-    ListViewer lVisibleItems;\r
-    ListViewer lHiddenItems;\r
-\r
-    ControlDecoration tStartTimeDecor;\r
-    ControlDecoration tLengthDecor;\r
-\r
-    final ChartData baseData = new ChartData();\r
-    final ChartData data;\r
-    final Runnable applyAction;\r
-\r
-    /**\r
-     * Set when a chart item dialog has previously been opened by this dialog.\r
-     */\r
-    ChartAndSubscriptionItemDialog currentItemDialog = null;\r
-\r
-    public ChartDialog(Shell parentShell, ChartData data, Runnable applyAction) {\r
-        super(parentShell);\r
-        this.data = data;\r
-        this.baseData.readFrom(data);\r
-        this.applyAction = applyAction;\r
-        setShellStyle(SWT.RESIZE | SWT.TITLE | SWT.CLOSE | SWT.BORDER);\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
-    @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().indent(5, 0).grab(true, false).span(8, 1);\r
-\r
-        // 1st line - Chart: [chart name]\r
-        lName = new Label(c, 0);\r
-        lName.setText("&Name:");\r
-        gd1.applyTo(lName);\r
-        tName = new Text(c, SWT.BORDER);\r
-        tName.setEnabled( true );\r
-        if ( data.name != null ) tName.setText( data.name );\r
-        gd2.applyTo(tName);\r
-\r
-        // 2nd line - start time + length\r
-        Label l = new Label(c, 0);\r
-        l.setText("Chart Time Window Preference (Auto-scale)");\r
-        GridDataFactory.fillDefaults().span(9, 1).applyTo(l);\r
-\r
-        lStartTime = new Label(c, 0);\r
-        lStartTime.setText("&Time Start:");\r
-        gd1.applyTo(lStartTime);\r
-        tStartTime = new Text(c, SWT.BORDER);\r
-        tStartTime.setToolTipText("Chart Window Fixed Start Time in Seconds or Yy Dd HH:mm:ss.ddd");\r
-        if ( data.timeStart!=null ) {\r
-            String str = timeFormat.format(data.timeStart);\r
-            tStartTime.setText(str);\r
-        }\r
-        gd2.applyTo(tStartTime);\r
-        tStartTime.addModifyListener(new ModifyListener() {\r
-            @Override\r
-            public void modifyText(ModifyEvent e) {\r
-                data.timeStart = parseTime(tStartTime, false, 0.0);\r
-                validate();\r
-            }\r
-        });\r
-\r
-        lLength = new Label(c, 0);\r
-        lLength.setText("&Length:");\r
-        gd1.applyTo(lLength);\r
-        tLength = new Text(c, SWT.BORDER);\r
-        tLength.setToolTipText("Chart Window Fixed Time Axis Length in Seconds or Yy Dd HH:mm:ss.ddd");\r
-        if ( data.timeLength!=null ) {\r
-            String str = timeFormat.format(data.timeLength);\r
-            tLength.setText(str);\r
-        }\r
-        gd2.applyTo(tLength);\r
-        tLength.addModifyListener(new ModifyListener() {\r
-            @Override\r
-            public void modifyText(ModifyEvent e) {\r
-                data.timeLength = parseTime(tLength, false, 0.0);\r
-                validate();\r
-            }\r
-        });\r
-\r
-        // 3rd line - Increment scrollbar\r
-        lScrollIncrement = new Label(c, 0);\r
-        lScrollIncrement.setText("Scroll Increment:");\r
-        gd1.applyTo(lScrollIncrement);\r
-        sIncrement = new Scale(c, SWT.HORIZONTAL);\r
-        sIncrement.setMinimum(1);\r
-        sIncrement.setMaximum(100);\r
-        sIncrement.setIncrement(10);\r
-        sIncrement.setPageIncrement(10);\r
-        if (data.timeIncrement!=null) {\r
-            sIncrement.setSelection( (int) (double) data.timeIncrement );\r
-        }\r
-        gd2.copy().span(7, 1).applyTo(sIncrement);\r
-        final Text sIncrementText = new Text(c, SWT.READ_ONLY | SWT.RIGHT);\r
-        gd1.copy().hint(35, SWT.DEFAULT).applyTo(sIncrementText);\r
-        sIncrement.addSelectionListener(new SelectionAdapter() {\r
-            @Override\r
-            public void widgetSelected(SelectionEvent e) {\r
-                sIncrementText.setText(sIncrement.getSelection() + " %");\r
-            }\r
-        });\r
-        sIncrementText.setText(sIncrement.getSelection() + " %");\r
-\r
-        // 4th line -- AxisMode + Milestone\r
-        lAxisMode = new Label(c, 0);\r
-        lAxisMode.setText("A&xis Mode:");\r
-        gd1.applyTo(lAxisMode);\r
-        cAxisMode = new Combo(c, SWT.DROP_DOWN | SWT.BORDER | SWT.READ_ONLY);\r
-        GridDataFactory.fillDefaults().indent(5, 0).span(1, 1).applyTo(cAxisMode);\r
-        cAxisMode.add("Single");\r
-        cAxisMode.add("Multi");\r
-        String _mode = data.axisMode==YAxisMode.SingleAxis ? "Single" : "Multi"; \r
-        cAxisMode.setText( _mode );\r
-        // Filler\r
-        GridDataFactory.fillDefaults().span(7, 1).applyTo(new Label(c, 0));\r
-\r
-        bTrackExperimentTime = new Button(c, SWT.CHECK);\r
-        bTrackExperimentTime.setText("Ha&irline Tracks Experiment Time");\r
-        bTrackExperimentTime.setSelection( data.trackExperimentTime );\r
-        GridDataFactory.fillDefaults().span(9, 1).applyTo(bTrackExperimentTime);\r
-\r
-        // Styling group begins\r
-\r
-        Group gStyling = new Group(c, 0);\r
-        gStyling.setText("Styling");\r
-        GridDataFactory.fillDefaults().indent(0, 5).grab(true, true).span(9, 1).applyTo(gStyling);\r
-        GridLayoutFactory.fillDefaults().margins(8, 8).numColumns(4).applyTo(gStyling);\r
-\r
-        // Background color settings\r
-        {\r
-            bBackgroundGradient = new Button(gStyling, SWT.CHECK);\r
-            bBackgroundGradient.setText("&Background Vertical Gradient");\r
-            bBackgroundGradient.setToolTipText("Solid Color or Vertical Gradient");\r
-            bBackgroundGradient.setSelection(data.backgroundGradient);\r
-            bBackgroundGradient.addSelectionListener(new SelectionAdapter() {\r
-                @Override\r
-                public void widgetSelected(SelectionEvent e) {\r
-                    data.backgroundGradient = bBackgroundGradient.getSelection();\r
-                    setBackgroundGradient(data.backgroundGradient);\r
-                }\r
-            });\r
-            GridDataFactory.fillDefaults().indent(0, 0).span(1, 1).grab(false, false).applyTo(bBackgroundGradient);\r
-\r
-            backgroundColor1 = new ColorSelector(gStyling);\r
-            backgroundColor2 = new ColorSelector(gStyling);\r
-            backgroundColor1.getButton().setToolTipText("Select Gradient Bottom Color");\r
-            backgroundColor2.getButton().setToolTipText("Select Gradient Top Color");\r
-            GridDataFactory.fillDefaults().indent(0, 0).span(1, 1).grab(false, false).applyTo(backgroundColor1.getButton());\r
-            GridDataFactory.fillDefaults().indent(0, 0).span(1, 1).grab(false, false).applyTo(backgroundColor2.getButton());\r
-            backgroundColor1.setColorValue(toRGB(data.backgroundColor1, DEFAULT_BG_COLOR1));\r
-            backgroundColor2.setColorValue(toRGB(data.backgroundColor2, DEFAULT_BG_COLOR2));\r
-            setBackgroundGradient(data.backgroundGradient);\r
-\r
-            Button bResetBackgroundColors = new Button(gStyling, SWT.PUSH);\r
-            bResetBackgroundColors.setText("Reset");\r
-            bResetBackgroundColors.setToolTipText("Reset Background Color to Default");\r
-            bResetBackgroundColors.addSelectionListener(new SelectionAdapter() {\r
-                public void widgetSelected(SelectionEvent e) {\r
-                    backgroundColor1.setColorValue(toRGB(null, DEFAULT_BG_COLOR1));\r
-                    backgroundColor2.setColorValue(toRGB(null, DEFAULT_BG_COLOR2));\r
-                }\r
-            });\r
-        }\r
-\r
-        // Grid settings\r
-        {\r
-            bShowGrid = new Button(gStyling, SWT.CHECK);\r
-            bShowGrid.setText("Show &Grid");\r
-            bShowGrid.setToolTipText("Toggle Background Grid Visibility");\r
-            bShowGrid.setSelection(data.showGrid);\r
-            GridDataFactory.fillDefaults().indent(0, 0).span(1, 1).grab(false, false).applyTo(bShowGrid);\r
-\r
-            gridColor = new ColorSelector(gStyling);\r
-            gridColor.getButton().setToolTipText("Select Grid Color");\r
-            GridDataFactory.fillDefaults().indent(0, 0).span(1, 1).grab(false, false).applyTo(gridColor.getButton());\r
-            gridColor.setColorValue(toRGB(data.gridColor, DEFAULT_GRID_COLOR));\r
-\r
-            Button bResetGridColor = new Button(gStyling, SWT.PUSH);\r
-            bResetGridColor.setText("Reset");\r
-            bResetGridColor.setToolTipText("Reset Grid Color to Default");\r
-            bResetGridColor.addSelectionListener(new SelectionAdapter() {\r
-                public void widgetSelected(SelectionEvent e) {\r
-                    gridColor.setColorValue(toRGB(null, DEFAULT_GRID_COLOR));\r
-                }\r
-            });\r
-        }\r
-\r
-        // Grid settings\r
-        {\r
-            GridDataFactory.fillDefaults().span(1, 1).applyTo(new Label(gStyling, 0));\r
-            bMilestones = new Button(gStyling, SWT.CHECK);\r
-            bMilestones.setText("Show &Milestones");\r
-            bMilestones.setToolTipText("Toggle Milestone Event Visibility");\r
-            bMilestones.setSelection( data.showMilestones );\r
-            GridDataFactory.fillDefaults().indent(0, 0).span(4, 1).applyTo(bMilestones);\r
-        }\r
-\r
-        // Item visibility group begins\r
-\r
-        Group gItemVisibility = new Group(c, 0);\r
-        gItemVisibility.setText("Item Visibility");\r
-        GridDataFactory.fillDefaults().indent(0, 5).grab(true, true).span(9, 1).applyTo(gItemVisibility);\r
-        GridLayoutFactory.fillDefaults().margins(8, 8).numColumns(3).applyTo(gItemVisibility);\r
-\r
-        Label visible = new Label(gItemVisibility, 0);\r
-        visible.setText("Visible");\r
-        GridDataFactory.fillDefaults().grab(true, false).applyTo(visible);\r
-        new Label(gItemVisibility, 0);\r
-        Label hidden = new Label(gItemVisibility, 0);\r
-        hidden.setText("Hidden");\r
-        GridDataFactory.fillDefaults().grab(true, false).applyTo(hidden);\r
-\r
-        lVisibleItems = new ListViewer(gItemVisibility, SWT.MULTI | SWT.BORDER | SWT.V_SCROLL | SWT.H_SCROLL);\r
-        GridDataFactory.fillDefaults().hint(200, 200).grab(true, true).applyTo(lVisibleItems.getControl());\r
-        Composite addRemoveButtons = new Composite(gItemVisibility, 0);\r
-        GridLayoutFactory.fillDefaults().numColumns(1).applyTo(addRemoveButtons);\r
-        GridDataFactory.fillDefaults().grab(false, true).span(1, 1).applyTo(addRemoveButtons);\r
-        Button hideButton = new Button(addRemoveButtons, SWT.PUSH);\r
-        hideButton.setText("&Hide \u2192");\r
-        GridDataFactory.fillDefaults().grab(true, false).span(1, 1).applyTo(hideButton);\r
-        Button showButton = new Button(addRemoveButtons, SWT.PUSH);\r
-        showButton.setText("\u2190 &Show");\r
-        GridDataFactory.fillDefaults().grab(true, false).span(1, 1).applyTo(showButton);\r
-        lHiddenItems = new ListViewer(gItemVisibility, SWT.MULTI | SWT.BORDER | SWT.V_SCROLL | SWT.H_SCROLL);\r
-        GridDataFactory.fillDefaults().hint(200, 200).grab(true, true).applyTo(lHiddenItems.getControl());\r
-\r
-        lVisibleItems.addDoubleClickListener(new OpenChartItemProperties());\r
-        lVisibleItems.setContentProvider(new ItemContentProvider(false));\r
-        lHiddenItems.addDoubleClickListener(new OpenChartItemProperties());\r
-        lHiddenItems.setContentProvider(new ItemContentProvider(true));\r
-        lVisibleItems.setLabelProvider(new ItemLabeler());\r
-        lHiddenItems.setLabelProvider(new ItemLabeler());\r
-        lVisibleItems.setInput(data);\r
-        lHiddenItems.setInput(data);\r
-\r
-        lVisibleItems.getList().addKeyListener(new SelectAllListener(lVisibleItems));\r
-        lHiddenItems.getList().addKeyListener(new SelectAllListener(lHiddenItems));\r
-        showButton.addSelectionListener(new ShowHideListener(false));\r
-        hideButton.addSelectionListener(new ShowHideListener(true));\r
-\r
-        Image image = FieldDecorationRegistry.getDefault()\r
-                .getFieldDecoration(FieldDecorationRegistry.DEC_ERROR)\r
-                .getImage();\r
-\r
-        tStartTimeDecor = createDecoration(tStartTime, SWT.LEFT | SWT.CENTER, image);\r
-        tLengthDecor = createDecoration(tLength, SWT.LEFT | SWT.CENTER, image);\r
-    }\r
-\r
-    class ShowHideListener extends SelectionAdapter {\r
-        private boolean hide;\r
-\r
-        public ShowHideListener(boolean hide) {\r
-            this.hide = hide;\r
-        }\r
-\r
-        @Override\r
-        public void widgetSelected(SelectionEvent e) {\r
-            ISelection selection = hide ? lVisibleItems.getSelection() : lHiddenItems.getSelection();\r
-            Set<ItemKey> selected = ISelectionUtils.filterSetSelection(selection, ItemKey.class);\r
-            if (!selected.isEmpty()) {\r
-                if (hide) {\r
-                    data.hiddenItems.addAll(selected);\r
-                } else {\r
-                    data.hiddenItems.removeAll(selected);\r
-                }\r
-                lVisibleItems.refresh();\r
-                lHiddenItems.refresh();\r
-            }\r
-        }\r
-    }\r
-\r
-    static class SelectAllListener extends KeyAdapter {\r
-        private ListViewer list;\r
-\r
-        public SelectAllListener(ListViewer list) {\r
-            this.list = list;\r
-        }\r
-\r
-        @Override\r
-        public void keyPressed(KeyEvent e) {\r
-            if (e.stateMask == SWT.CTRL && e.keyCode == 'a') {\r
-                list.getList().selectAll();\r
-            }\r
-        }\r
-    };\r
-\r
-    class ItemContentProvider extends ArrayContentProvider {\r
-        private final boolean expectedHidden;\r
-\r
-        public ItemContentProvider(boolean expectedHidden) {\r
-            this.expectedHidden = expectedHidden;\r
-        }\r
-\r
-        @Override\r
-        public Object[] getElements(Object inputElement) {\r
-            List<Object> result = new ArrayList<>();\r
-            for (ItemKey item : data.allItems.keySet())\r
-                if (data.hiddenItems.contains(item) == expectedHidden)\r
-                    result.add(item);\r
-            return result.toArray();\r
-        }\r
-    }\r
-\r
-    private void setBackgroundGradient(boolean backgroundGradient) {\r
-        backgroundColor2.setEnabled(backgroundGradient);\r
-        backgroundColor1.getButton().setToolTipText(backgroundGradient ? "Select Gradient Bottom Color" : "Select Solid Color");\r
-    }\r
-\r
-    protected Double parseTime(Text text, boolean returnDefault, double defaultValue) {\r
-        if (text == null)\r
-            return null;\r
-        String s = text.getText();\r
-        Double time = ChartComposite.START_VALIDATOR.parse(s);\r
-        if (time == null)\r
-            return returnDefault ? defaultValue : null;\r
-        return time;\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
-    class ItemLabeler extends LabelProvider {\r
-        @Override\r
-        public String getText(Object element) {\r
-            ItemKey item = (ItemKey) element;\r
-            TrendItem ti = data.allItems.get(item);\r
-            return ti.label;\r
-        }\r
-    }\r
-\r
-    protected void parseUiToData() {\r
-        data.name = tName.getText();\r
-        data.backgroundColor1 = toColorComponents( backgroundColor1.getColorValue() );\r
-        data.backgroundColor2 = toColorComponents( backgroundColor2.getColorValue() );\r
-        data.showGrid = bShowGrid.getSelection();\r
-        data.gridColor = toColorComponents( gridColor.getColorValue() );\r
-        data.showMilestones = bMilestones.getSelection();\r
-        data.trackExperimentTime = bTrackExperimentTime.getSelection();\r
-        data.axisMode = cAxisMode.getSelectionIndex()==0?YAxisMode.SingleAxis:YAxisMode.MultiAxis;\r
-        data.timeIncrement = (double) sIncrement.getSelection();\r
-        data.timeLength = ChartComposite.LENGTH_VALIDATOR.parse(tLength.getText());\r
-        data.timeStart = ChartComposite.START_VALIDATOR.parse(tStartTime.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
-        parseUiToData();\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("Edit Chart");\r
-    }\r
-\r
-    public void validate() {\r
-        String startTimeError = ChartComposite.START_VALIDATOR.isValid(tStartTime.getText());\r
-        String lengthError = ChartComposite.LENGTH_VALIDATOR.isValid(tLength.getText());\r
-        setDecoration(tStartTimeDecor, startTimeError);\r
-        setDecoration(tLengthDecor, lengthError);\r
-        boolean ok = startTimeError == null && lengthError == null;\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
-    private static RGB toRGB(float[] components, RGB defaultValue) {\r
-        if (components == null || components.length < 3)\r
-            return defaultValue;\r
-        Color c = new Color(components[0], components[1], components[2]);\r
-        return new RGB(c.getRed(), c.getGreen(), c.getBlue());\r
-    }\r
-\r
-    private static float[] toColorComponents(RGB rgb) {\r
-        return new Color(rgb.red, rgb.green, rgb.blue).getColorComponents(new float[3]);\r
-    }\r
-\r
-    class OpenChartItemProperties implements IDoubleClickListener {\r
-        @Override\r
-        public void doubleClick(DoubleClickEvent event) {\r
-            final ItemKey item = ISelectionUtils.filterSingleSelection(event.getSelection(), ItemKey.class);\r
-            if (item == null)\r
-                return;\r
-            try {\r
-                final ChartAndSubscriptionItemData data = Simantics.getSession().syncRequest(\r
-                        new ChartAndSubscriptionItemReadQuery(item.resource.getResource()));\r
-                if (currentItemDialog != null && currentItemDialog.getShell() != null && !currentItemDialog.getShell().isDisposed()) {\r
-                    // TODO: reinitialize existing dialog with the specified data.\r
-                    // Close current dialog before opening new one.\r
-                    currentItemDialog.close();\r
-                }\r
-                currentItemDialog = ChartDoubleClickHandler.openChartItemPropertiesDialog(\r
-                        event.getViewer().getControl().getShell(),\r
-                        item.resource.getResource(),\r
-                        data,\r
-                        false);\r
-            } catch (DatabaseException e) {\r
-                Activator.getDefault().getLog().log(new Status(IStatus.ERROR, Activator.PLUGIN_ID, "Problems opening chart item property dialog."));\r
-            }\r
-        }\r
-    }\r
-\r
-}\r
+package org.simantics.charts.ui;
+
+import java.awt.Color;
+import java.text.Format;
+import java.util.ArrayList;
+import java.util.List;
+import java.util.Set;
+
+import org.eclipse.core.runtime.IStatus;
+import org.eclipse.core.runtime.Status;
+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.viewers.ArrayContentProvider;
+import org.eclipse.jface.viewers.DoubleClickEvent;
+import org.eclipse.jface.viewers.IDoubleClickListener;
+import org.eclipse.jface.viewers.ISelection;
+import org.eclipse.jface.viewers.LabelProvider;
+import org.eclipse.jface.viewers.ListViewer;
+import org.eclipse.swt.SWT;
+import org.eclipse.swt.events.KeyAdapter;
+import org.eclipse.swt.events.KeyEvent;
+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.Scale;
+import org.eclipse.swt.widgets.Shell;
+import org.eclipse.swt.widgets.Text;
+import org.simantics.Simantics;
+import org.simantics.charts.Activator;
+import org.simantics.charts.query.ChartAndSubscriptionItemData;
+import org.simantics.charts.query.ChartAndSubscriptionItemReadQuery;
+import org.simantics.charts.ui.ChartData.ItemKey;
+import org.simantics.db.exception.DatabaseException;
+import org.simantics.modeling.ui.chart.property.ChartComposite;
+import org.simantics.trend.configuration.TrendItem;
+import org.simantics.trend.configuration.YAxisMode;
+import org.simantics.trend.impl.Plot;
+import org.simantics.utils.format.TimeFormat;
+import org.simantics.utils.ui.ISelectionUtils;
+
+/**
+ * Dialog for chart properties:
+ * 
+ * <pre>
+ * Name:             [ Process ]
+ * Time Start:       [   ]
+ * Length:           [   ]
+ * Scroll Increment: [--|--] n%
+ * Axis Mode:        [Single/Multi]
+ * [ ] Hairline Tracks Experiment Time
+ * 
+ * --- Styling ------------------------------------------
+ * | [ ] Background Vertical Gradient {color1} {color2} |
+ * | [ ] Show Grid                    {grid color}      |
+ * | [ ] Show Milestones                                |
+ * ------------------------------------------------------
+ * 
+ * --- Item visibility ---------------
+ * | Visible          Hidden         |
+ * | -----------      -------------- |
+ * | |         | HIDE |            | |
+ * | |         | SHOW |            | |
+ * | -----------      -------------- |
+ * -----------------------------------
+ * </pre>
+ * 
+ * @author toni.kalajainen@semantum.fi
+ * @author Tuukka Lehtonen
+ */
+public class ChartDialog extends Dialog {
+
+    private static final RGB DEFAULT_BG_COLOR1 = toRGB(Plot.PLOT_AREA_BG_GRADIENT_COLOR_BOTTOM.getColorComponents(new float[3]), null);
+    private static final RGB DEFAULT_BG_COLOR2 = toRGB(Plot.PLOT_AREA_BG_GRADIENT_COLOR_TOP.getColorComponents(new float[3]), null);
+    private static final RGB DEFAULT_GRID_COLOR = toRGB(Plot.GRID_LINE_COLOR.getColorComponents(new float[3]), null);
+
+    Format timeFormat = new TimeFormat(100, 3);
+
+    // UI
+    Label lName, lStartTime, lLength, lScrollIncrement, lAxisMode;
+    Text tName, tStartTime, tLength;
+    Scale sIncrement;
+    Combo cAxisMode;
+    Button bBackgroundGradient;
+    ColorSelector backgroundColor1;
+    ColorSelector backgroundColor2;
+    Button bShowGrid;
+    ColorSelector gridColor;
+    Button bMilestones;
+    Button bTrackExperimentTime;
+    ListViewer lVisibleItems;
+    ListViewer lHiddenItems;
+
+    ControlDecoration tStartTimeDecor;
+    ControlDecoration tLengthDecor;
+
+    final ChartData baseData = new ChartData();
+    final ChartData data;
+    final Runnable applyAction;
+
+    /**
+     * Set when a chart item dialog has previously been opened by this dialog.
+     */
+    ChartAndSubscriptionItemDialog currentItemDialog = null;
+
+    public ChartDialog(Shell parentShell, ChartData data, Runnable applyAction) {
+        super(parentShell);
+        this.data = data;
+        this.baseData.readFrom(data);
+        this.applyAction = applyAction;
+        setShellStyle(SWT.RESIZE | SWT.TITLE | SWT.CLOSE | SWT.BORDER);
+    }
+
+    @Override
+    protected void createButtonsForButtonBar(Composite parent) {
+        if (applyAction != null)
+            createButton(parent, ChartDialogConstants.APPLY_ID, ChartDialogConstants.APPLY_LABEL, true);
+        super.createButtonsForButtonBar(parent);
+    }
+
+    @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().indent(5, 0).grab(true, false).span(8, 1);
+
+        // 1st line - Chart: [chart name]
+        lName = new Label(c, 0);
+        lName.setText("&Name:");
+        gd1.applyTo(lName);
+        tName = new Text(c, SWT.BORDER);
+        tName.setEnabled( true );
+        if ( data.name != null ) tName.setText( data.name );
+        gd2.applyTo(tName);
+
+        // 2nd line - start time + length
+        Label l = new Label(c, 0);
+        l.setText("Chart Time Window Preference (Auto-scale)");
+        GridDataFactory.fillDefaults().span(9, 1).applyTo(l);
+
+        lStartTime = new Label(c, 0);
+        lStartTime.setText("&Time Start:");
+        gd1.applyTo(lStartTime);
+        tStartTime = new Text(c, SWT.BORDER);
+        tStartTime.setToolTipText("Chart Window Fixed Start Time in Seconds or Yy Dd HH:mm:ss.ddd");
+        if ( data.timeStart!=null ) {
+            String str = timeFormat.format(data.timeStart);
+            tStartTime.setText(str);
+        }
+        gd2.applyTo(tStartTime);
+        tStartTime.addModifyListener(new ModifyListener() {
+            @Override
+            public void modifyText(ModifyEvent e) {
+                data.timeStart = parseTime(tStartTime, false, 0.0);
+                validate();
+            }
+        });
+
+        lLength = new Label(c, 0);
+        lLength.setText("&Length:");
+        gd1.applyTo(lLength);
+        tLength = new Text(c, SWT.BORDER);
+        tLength.setToolTipText("Chart Window Fixed Time Axis Length in Seconds or Yy Dd HH:mm:ss.ddd");
+        if ( data.timeLength!=null ) {
+            String str = timeFormat.format(data.timeLength);
+            tLength.setText(str);
+        }
+        gd2.applyTo(tLength);
+        tLength.addModifyListener(new ModifyListener() {
+            @Override
+            public void modifyText(ModifyEvent e) {
+                data.timeLength = parseTime(tLength, false, 0.0);
+                validate();
+            }
+        });
+
+        // 3rd line - Increment scrollbar
+        lScrollIncrement = new Label(c, 0);
+        lScrollIncrement.setText("Scroll Increment:");
+        gd1.applyTo(lScrollIncrement);
+        sIncrement = new Scale(c, SWT.HORIZONTAL);
+        sIncrement.setMinimum(1);
+        sIncrement.setMaximum(100);
+        sIncrement.setIncrement(10);
+        sIncrement.setPageIncrement(10);
+        if (data.timeIncrement!=null) {
+            sIncrement.setSelection( (int) (double) data.timeIncrement );
+        }
+        gd2.copy().span(7, 1).applyTo(sIncrement);
+        final Text sIncrementText = new Text(c, SWT.READ_ONLY | SWT.RIGHT);
+        gd1.copy().hint(35, SWT.DEFAULT).applyTo(sIncrementText);
+        sIncrement.addSelectionListener(new SelectionAdapter() {
+            @Override
+            public void widgetSelected(SelectionEvent e) {
+                sIncrementText.setText(sIncrement.getSelection() + " %");
+            }
+        });
+        sIncrementText.setText(sIncrement.getSelection() + " %");
+
+        // 4th line -- AxisMode + Milestone
+        lAxisMode = new Label(c, 0);
+        lAxisMode.setText("A&xis Mode:");
+        gd1.applyTo(lAxisMode);
+        cAxisMode = new Combo(c, SWT.DROP_DOWN | SWT.BORDER | SWT.READ_ONLY);
+        GridDataFactory.fillDefaults().indent(5, 0).span(1, 1).applyTo(cAxisMode);
+        cAxisMode.add("Single");
+        cAxisMode.add("Multi");
+        String _mode = data.axisMode==YAxisMode.SingleAxis ? "Single" : "Multi"; 
+        cAxisMode.setText( _mode );
+        // Filler
+        GridDataFactory.fillDefaults().span(7, 1).applyTo(new Label(c, 0));
+
+        bTrackExperimentTime = new Button(c, SWT.CHECK);
+        bTrackExperimentTime.setText("Ha&irline Tracks Experiment Time");
+        bTrackExperimentTime.setSelection( data.trackExperimentTime );
+        GridDataFactory.fillDefaults().span(9, 1).applyTo(bTrackExperimentTime);
+
+        // Styling group begins
+
+        Group gStyling = new Group(c, 0);
+        gStyling.setText("Styling");
+        GridDataFactory.fillDefaults().indent(0, 5).grab(true, true).span(9, 1).applyTo(gStyling);
+        GridLayoutFactory.fillDefaults().margins(8, 8).numColumns(4).applyTo(gStyling);
+
+        // Background color settings
+        {
+            bBackgroundGradient = new Button(gStyling, SWT.CHECK);
+            bBackgroundGradient.setText("&Background Vertical Gradient");
+            bBackgroundGradient.setToolTipText("Solid Color or Vertical Gradient");
+            bBackgroundGradient.setSelection(data.backgroundGradient);
+            bBackgroundGradient.addSelectionListener(new SelectionAdapter() {
+                @Override
+                public void widgetSelected(SelectionEvent e) {
+                    data.backgroundGradient = bBackgroundGradient.getSelection();
+                    setBackgroundGradient(data.backgroundGradient);
+                }
+            });
+            GridDataFactory.fillDefaults().indent(0, 0).span(1, 1).grab(false, false).applyTo(bBackgroundGradient);
+
+            backgroundColor1 = new ColorSelector(gStyling);
+            backgroundColor2 = new ColorSelector(gStyling);
+            backgroundColor1.getButton().setToolTipText("Select Gradient Bottom Color");
+            backgroundColor2.getButton().setToolTipText("Select Gradient Top Color");
+            GridDataFactory.fillDefaults().indent(0, 0).span(1, 1).grab(false, false).applyTo(backgroundColor1.getButton());
+            GridDataFactory.fillDefaults().indent(0, 0).span(1, 1).grab(false, false).applyTo(backgroundColor2.getButton());
+            backgroundColor1.setColorValue(toRGB(data.backgroundColor1, DEFAULT_BG_COLOR1));
+            backgroundColor2.setColorValue(toRGB(data.backgroundColor2, DEFAULT_BG_COLOR2));
+            setBackgroundGradient(data.backgroundGradient);
+
+            Button bResetBackgroundColors = new Button(gStyling, SWT.PUSH);
+            bResetBackgroundColors.setText("Reset");
+            bResetBackgroundColors.setToolTipText("Reset Background Color to Default");
+            bResetBackgroundColors.addSelectionListener(new SelectionAdapter() {
+                public void widgetSelected(SelectionEvent e) {
+                    backgroundColor1.setColorValue(toRGB(null, DEFAULT_BG_COLOR1));
+                    backgroundColor2.setColorValue(toRGB(null, DEFAULT_BG_COLOR2));
+                }
+            });
+        }
+
+        // Grid settings
+        {
+            bShowGrid = new Button(gStyling, SWT.CHECK);
+            bShowGrid.setText("Show &Grid");
+            bShowGrid.setToolTipText("Toggle Background Grid Visibility");
+            bShowGrid.setSelection(data.showGrid);
+            GridDataFactory.fillDefaults().indent(0, 0).span(1, 1).grab(false, false).applyTo(bShowGrid);
+
+            gridColor = new ColorSelector(gStyling);
+            gridColor.getButton().setToolTipText("Select Grid Color");
+            GridDataFactory.fillDefaults().indent(0, 0).span(1, 1).grab(false, false).applyTo(gridColor.getButton());
+            gridColor.setColorValue(toRGB(data.gridColor, DEFAULT_GRID_COLOR));
+
+            Button bResetGridColor = new Button(gStyling, SWT.PUSH);
+            bResetGridColor.setText("Reset");
+            bResetGridColor.setToolTipText("Reset Grid Color to Default");
+            bResetGridColor.addSelectionListener(new SelectionAdapter() {
+                public void widgetSelected(SelectionEvent e) {
+                    gridColor.setColorValue(toRGB(null, DEFAULT_GRID_COLOR));
+                }
+            });
+        }
+
+        // Grid settings
+        {
+            GridDataFactory.fillDefaults().span(1, 1).applyTo(new Label(gStyling, 0));
+            bMilestones = new Button(gStyling, SWT.CHECK);
+            bMilestones.setText("Show &Milestones");
+            bMilestones.setToolTipText("Toggle Milestone Event Visibility");
+            bMilestones.setSelection( data.showMilestones );
+            GridDataFactory.fillDefaults().indent(0, 0).span(4, 1).applyTo(bMilestones);
+        }
+
+        // Item visibility group begins
+
+        Group gItemVisibility = new Group(c, 0);
+        gItemVisibility.setText("Item Visibility");
+        GridDataFactory.fillDefaults().indent(0, 5).grab(true, true).span(9, 1).applyTo(gItemVisibility);
+        GridLayoutFactory.fillDefaults().margins(8, 8).numColumns(3).applyTo(gItemVisibility);
+
+        Label visible = new Label(gItemVisibility, 0);
+        visible.setText("Visible");
+        GridDataFactory.fillDefaults().grab(true, false).applyTo(visible);
+        new Label(gItemVisibility, 0);
+        Label hidden = new Label(gItemVisibility, 0);
+        hidden.setText("Hidden");
+        GridDataFactory.fillDefaults().grab(true, false).applyTo(hidden);
+
+        lVisibleItems = new ListViewer(gItemVisibility, SWT.MULTI | SWT.BORDER | SWT.V_SCROLL | SWT.H_SCROLL);
+        GridDataFactory.fillDefaults().hint(200, 200).grab(true, true).applyTo(lVisibleItems.getControl());
+        Composite addRemoveButtons = new Composite(gItemVisibility, 0);
+        GridLayoutFactory.fillDefaults().numColumns(1).applyTo(addRemoveButtons);
+        GridDataFactory.fillDefaults().grab(false, true).span(1, 1).applyTo(addRemoveButtons);
+        Button hideButton = new Button(addRemoveButtons, SWT.PUSH);
+        hideButton.setText("&Hide \u2192");
+        GridDataFactory.fillDefaults().grab(true, false).span(1, 1).applyTo(hideButton);
+        Button showButton = new Button(addRemoveButtons, SWT.PUSH);
+        showButton.setText("\u2190 &Show");
+        GridDataFactory.fillDefaults().grab(true, false).span(1, 1).applyTo(showButton);
+        lHiddenItems = new ListViewer(gItemVisibility, SWT.MULTI | SWT.BORDER | SWT.V_SCROLL | SWT.H_SCROLL);
+        GridDataFactory.fillDefaults().hint(200, 200).grab(true, true).applyTo(lHiddenItems.getControl());
+
+        lVisibleItems.addDoubleClickListener(new OpenChartItemProperties());
+        lVisibleItems.setContentProvider(new ItemContentProvider(false));
+        lHiddenItems.addDoubleClickListener(new OpenChartItemProperties());
+        lHiddenItems.setContentProvider(new ItemContentProvider(true));
+        lVisibleItems.setLabelProvider(new ItemLabeler());
+        lHiddenItems.setLabelProvider(new ItemLabeler());
+        lVisibleItems.setInput(data);
+        lHiddenItems.setInput(data);
+
+        lVisibleItems.getList().addKeyListener(new SelectAllListener(lVisibleItems));
+        lHiddenItems.getList().addKeyListener(new SelectAllListener(lHiddenItems));
+        showButton.addSelectionListener(new ShowHideListener(false));
+        hideButton.addSelectionListener(new ShowHideListener(true));
+
+        Image image = FieldDecorationRegistry.getDefault()
+                .getFieldDecoration(FieldDecorationRegistry.DEC_ERROR)
+                .getImage();
+
+        tStartTimeDecor = createDecoration(tStartTime, SWT.LEFT | SWT.CENTER, image);
+        tLengthDecor = createDecoration(tLength, SWT.LEFT | SWT.CENTER, image);
+    }
+
+    class ShowHideListener extends SelectionAdapter {
+        private boolean hide;
+
+        public ShowHideListener(boolean hide) {
+            this.hide = hide;
+        }
+
+        @Override
+        public void widgetSelected(SelectionEvent e) {
+            ISelection selection = hide ? lVisibleItems.getSelection() : lHiddenItems.getSelection();
+            Set<ItemKey> selected = ISelectionUtils.filterSetSelection(selection, ItemKey.class);
+            if (!selected.isEmpty()) {
+                if (hide) {
+                    data.hiddenItems.addAll(selected);
+                } else {
+                    data.hiddenItems.removeAll(selected);
+                }
+                lVisibleItems.refresh();
+                lHiddenItems.refresh();
+            }
+        }
+    }
+
+    static class SelectAllListener extends KeyAdapter {
+        private ListViewer list;
+
+        public SelectAllListener(ListViewer list) {
+            this.list = list;
+        }
+
+        @Override
+        public void keyPressed(KeyEvent e) {
+            if (e.stateMask == SWT.CTRL && e.keyCode == 'a') {
+                list.getList().selectAll();
+            }
+        }
+    };
+
+    class ItemContentProvider extends ArrayContentProvider {
+        private final boolean expectedHidden;
+
+        public ItemContentProvider(boolean expectedHidden) {
+            this.expectedHidden = expectedHidden;
+        }
+
+        @Override
+        public Object[] getElements(Object inputElement) {
+            List<Object> result = new ArrayList<>();
+            for (ItemKey item : data.allItems.keySet())
+                if (data.hiddenItems.contains(item) == expectedHidden)
+                    result.add(item);
+            return result.toArray();
+        }
+    }
+
+    private void setBackgroundGradient(boolean backgroundGradient) {
+        backgroundColor2.setEnabled(backgroundGradient);
+        backgroundColor1.getButton().setToolTipText(backgroundGradient ? "Select Gradient Bottom Color" : "Select Solid Color");
+    }
+
+    protected Double parseTime(Text text, boolean returnDefault, double defaultValue) {
+        if (text == null)
+            return null;
+        String s = text.getText();
+        Double time = ChartComposite.START_VALIDATOR.parse(s);
+        if (time == null)
+            return returnDefault ? defaultValue : null;
+        return time;
+    }
+
+    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;
+    }
+
+    class ItemLabeler extends LabelProvider {
+        @Override
+        public String getText(Object element) {
+            ItemKey item = (ItemKey) element;
+            TrendItem ti = data.allItems.get(item);
+            return ti.label;
+        }
+    }
+
+    protected void parseUiToData() {
+        data.name = tName.getText();
+        data.backgroundColor1 = toColorComponents( backgroundColor1.getColorValue() );
+        data.backgroundColor2 = toColorComponents( backgroundColor2.getColorValue() );
+        data.showGrid = bShowGrid.getSelection();
+        data.gridColor = toColorComponents( gridColor.getColorValue() );
+        data.showMilestones = bMilestones.getSelection();
+        data.trackExperimentTime = bTrackExperimentTime.getSelection();
+        data.axisMode = cAxisMode.getSelectionIndex()==0?YAxisMode.SingleAxis:YAxisMode.MultiAxis;
+        data.timeIncrement = (double) sIncrement.getSelection();
+        data.timeLength = ChartComposite.LENGTH_VALIDATOR.parse(tLength.getText());
+        data.timeStart = ChartComposite.START_VALIDATOR.parse(tStartTime.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() {
+        parseUiToData();
+        if (applyAction != null && !baseData.equals(data))
+            applyAction.run();
+        super.okPressed();
+    }
+
+    @Override
+    protected void configureShell(Shell newShell) {
+        super.configureShell(newShell);
+        newShell.setText("Edit Chart");
+    }
+
+    public void validate() {
+        String startTimeError = ChartComposite.START_VALIDATOR.isValid(tStartTime.getText());
+        String lengthError = ChartComposite.LENGTH_VALIDATOR.isValid(tLength.getText());
+        setDecoration(tStartTimeDecor, startTimeError);
+        setDecoration(tLengthDecor, lengthError);
+        boolean ok = startTimeError == null && lengthError == 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);
+    }
+
+    private static RGB toRGB(float[] components, RGB defaultValue) {
+        if (components == null || components.length < 3)
+            return defaultValue;
+        Color c = new Color(components[0], components[1], components[2]);
+        return new RGB(c.getRed(), c.getGreen(), c.getBlue());
+    }
+
+    private static float[] toColorComponents(RGB rgb) {
+        return new Color(rgb.red, rgb.green, rgb.blue).getColorComponents(new float[3]);
+    }
+
+    class OpenChartItemProperties implements IDoubleClickListener {
+        @Override
+        public void doubleClick(DoubleClickEvent event) {
+            final ItemKey item = ISelectionUtils.filterSingleSelection(event.getSelection(), ItemKey.class);
+            if (item == null)
+                return;
+            try {
+                final ChartAndSubscriptionItemData data = Simantics.getSession().syncRequest(
+                        new ChartAndSubscriptionItemReadQuery(item.resource.getResource()));
+                if (currentItemDialog != null && currentItemDialog.getShell() != null && !currentItemDialog.getShell().isDisposed()) {
+                    // TODO: reinitialize existing dialog with the specified data.
+                    // Close current dialog before opening new one.
+                    currentItemDialog.close();
+                }
+                currentItemDialog = ChartDoubleClickHandler.openChartItemPropertiesDialog(
+                        event.getViewer().getControl().getShell(),
+                        item.resource.getResource(),
+                        data,
+                        false);
+            } catch (DatabaseException e) {
+                Activator.getDefault().getLog().log(new Status(IStatus.ERROR, Activator.PLUGIN_ID, "Problems opening chart item property dialog."));
+            }
+        }
+    }
+
+}