]> gerrit.simantics Code Review - simantics/platform.git/blobdiff - bundles/org.simantics.charts/src/org/simantics/charts/editor/AddMilestoneHandler.java
Fixed all line endings of the repository
[simantics/platform.git] / bundles / org.simantics.charts / src / org / simantics / charts / editor / AddMilestoneHandler.java
index d976caf82f750baab6183dfe411afc8efecdc16d..ea91bd91a391103295a5cbc16ea715df39d5c25f 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.editor;\r
-\r
-import org.eclipse.core.commands.AbstractHandler;\r
-import org.eclipse.core.commands.ExecutionEvent;\r
-import org.eclipse.core.commands.ExecutionException;\r
-import org.eclipse.core.expressions.IEvaluationContext;\r
-import org.eclipse.jface.dialogs.Dialog;\r
-import org.eclipse.jface.dialogs.IDialogSettings;\r
-import org.eclipse.jface.dialogs.IInputValidator;\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.swt.SWT;\r
-import org.eclipse.swt.events.ModifyEvent;\r
-import org.eclipse.swt.events.ModifyListener;\r
-import org.eclipse.swt.graphics.Image;\r
-import org.eclipse.swt.graphics.Point;\r
-import org.eclipse.swt.widgets.Button;\r
-import org.eclipse.swt.widgets.Composite;\r
-import org.eclipse.swt.widgets.Control;\r
-import org.eclipse.swt.widgets.Label;\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.databoard.Bindings;\r
-import org.simantics.db.Resource;\r
-import org.simantics.db.Session;\r
-import org.simantics.db.VirtualGraph;\r
-import org.simantics.db.WriteGraph;\r
-import org.simantics.db.common.request.WriteRequest;\r
-import org.simantics.db.exception.DatabaseException;\r
-import org.simantics.db.service.VirtualGraphSupport;\r
-import org.simantics.event.ontology.EventResource;\r
-import org.simantics.event.ontology.EventViewResource;\r
-import org.simantics.event.util.EventWriteData;\r
-import org.simantics.event.view.handler.CorrectMilestoneLabelsAction;\r
-import org.simantics.layer0.Layer0;\r
-import org.simantics.trend.impl.TrendNode;\r
-import org.simantics.utils.format.TimeFormat;\r
-\r
-/**\r
- * This handler opens a dialog for new milestone.\r
- * \r
- * @author toni.kalajainen\r
- */\r
-public class AddMilestoneHandler extends AbstractHandler {\r
-\r
-    @Override\r
-    public Object execute(ExecutionEvent event) throws ExecutionException {\r
-\r
-        TimeSeriesEditor tse = getTSE(event);\r
-        if (tse == null)\r
-            return null;\r
-        TrendNode tn = tse.trendNode;\r
-        if (tn == null)\r
-            return null;\r
-        final Resource experiment = tse.chartData.run;\r
-        if (experiment == null)\r
-            return null;\r
-\r
-        EventData data = new EventData();\r
-        data.initialTime = tn.lastMouseHoverTime;\r
-        AddMilestoneDialog d = new AddMilestoneDialog( tse.getSite().getShell(), data );\r
-        int ok = d.open();\r
-        if (ok != Dialog.OK)\r
-            return null;\r
-\r
-        Session session = Simantics.getSession();\r
-        VirtualGraph vg = session.getService(VirtualGraphSupport.class).getWorkspacePersistent("experiments");\r
-        session.async( new CreateManualEvent(vg, experiment, data) );\r
-\r
-        return null;\r
-    }\r
-\r
-    TimeSeriesEditor getTSE(ExecutionEvent event) {\r
-        Object eac = event.getApplicationContext();\r
-        if (eac == null || (eac instanceof IEvaluationContext == false)) return null;\r
-        IEvaluationContext ec = (IEvaluationContext) eac;\r
-        Object editor = ec.getVariable("activeEditor");\r
-        if (editor == null || (editor instanceof TimeSeriesEditor == false)) return null;\r
-        TimeSeriesEditor tse = (TimeSeriesEditor) editor;\r
-        return tse;\r
-    }\r
-\r
-    static class CreateManualEvent extends WriteRequest {\r
-\r
-        private final Resource experiment;\r
-        private final EventData eventData;\r
-\r
-        public CreateManualEvent(VirtualGraph vg, Resource experiment, EventData eventData) {\r
-            super(vg);\r
-            this.experiment = experiment;\r
-            this.eventData = eventData;\r
-        }\r
-\r
-        @Override\r
-        public void perform(WriteGraph graph) throws DatabaseException {\r
-            Layer0 L0 = Layer0.getInstance(graph);\r
-            EventResource EVENT = EventResource.getInstance(graph);\r
-            EventViewResource EVENTVIEW = EventViewResource.getInstance(graph);\r
-            Resource eventlog = graph.getPossibleObject(experiment, EVENT.IsEventProducerOf);\r
-            if (eventlog == null) return;\r
-\r
-            EventWriteData data = new EventWriteData(graph, eventlog, getProvider());\r
-            data.prepareToWrite(graph);\r
-\r
-            Resource event = graph.newResource(); \r
-            graph.claim(event, L0.InstanceOf, EVENT.Event);\r
-            graph.claimLiteral(event, L0.HasName, L0.String, ""+data.targetPos, Bindings.STRING);\r
-            graph.claimLiteral(event, L0.HasLabel, L0.HasLabel_Inverse, L0.String, eventData.description, Bindings.STRING);\r
-            graph.claimLiteral(event, L0.HasDescription, L0.HasDescription_Inverse, L0.String, eventData.description, Bindings.STRING);\r
-            graph.claimLiteral(event, EVENT.HasTimestamp, EVENT.HasTimestamp_Inverse, EVENT.TimeStamp, eventData.parsedTime, Bindings.DOUBLE);\r
-            if (eventData.isMilestone)\r
-                graph.claim(event, EVENT.Milestone, event);\r
-            if (eventData.isBaseline) {\r
-                graph.deny(eventlog, EVENT.EventLog_HasBaselineEvent);\r
-                graph.claim(eventlog, EVENT.EventLog_HasBaselineEvent, event);\r
-            }\r
-            graph.claim(event, EVENT.Event_type, null, EVENTVIEW.ManualEventType);\r
-            graph.claimLiteral(event, EVENT.Event_message, EVENT.Event_message_Inverse, L0.String, eventData.message, Bindings.STRING);\r
-            graph.claimLiteral(event, EVENT.Event_tag, EVENT.Event_tag_Inverse, L0.String, eventData.tag, Bindings.STRING);\r
-            graph.claim(event, EVENT.NoReturn, EVENT.NoReturn, event);\r
-            graph.claim(data.targetSlice, L0.ConsistsOf, L0.PartOf, event);\r
-\r
-            data.written();\r
-            data.commit(graph);\r
-\r
-            graph.syncRequest( new CorrectMilestoneLabelsAction(eventlog, getProvider()) );\r
-        }\r
-    }\r
-\r
-}\r
-\r
-\r
-class AddMilestoneDialog extends Dialog {\r
-\r
-    private static final String DIALOG = "AddMilestoneDialog"; //$NON-NLS-1$\r
-\r
-    IInputValidator timeValidator = new IInputValidator() {\r
-        @Override\r
-        public String isValid(String text) {\r
-            if (text.trim().isEmpty())\r
-                return null;\r
-            return TimeInputValidator.INSTANCE.isValid(text);\r
-        }\r
-    }; \r
-\r
-    Label nameLabel, descLabel, tagLabel, timeLabel, baselineLabel;\r
-    Text nameText, descText, tagText, timeText;\r
-    Button baselineButt;\r
-    ControlDecoration timeDecor;\r
-\r
-    EventData data;\r
-\r
-    private IDialogSettings dialogBoundsSettings;\r
-\r
-    protected AddMilestoneDialog(Shell parentShell, EventData data) {\r
-        super(parentShell);\r
-        this.data = data;\r
-\r
-        IDialogSettings settings = Activator.getDefault().getDialogSettings();\r
-        dialogBoundsSettings = settings.getSection(DIALOG);\r
-        if (dialogBoundsSettings == null)\r
-            dialogBoundsSettings = settings.addNewSection(DIALOG);\r
-    }\r
-\r
-    @Override\r
-    protected IDialogSettings getDialogBoundsSettings() {\r
-        return dialogBoundsSettings;\r
-    }\r
-\r
-    @Override\r
-    protected int getShellStyle() {\r
-        return SWT.RESIZE | SWT.MODELESS | SWT.TITLE | SWT.CLOSE | SWT.BORDER;\r
-    }\r
-\r
-    @Override\r
-    protected void configureShell(Shell newShell) {\r
-        super.configureShell(newShell);\r
-        newShell.setText("Add new milestone");\r
-    }\r
-\r
-    @Override\r
-    protected Point getInitialSize() {\r
-        return super.getInitialSize();\r
-    }\r
-\r
-    @Override\r
-    protected Control createDialogArea(Composite parent) \r
-    {\r
-        Composite composite = (Composite) super.createDialogArea(parent);\r
-        GridLayoutFactory.fillDefaults().margins(8, 8).numColumns(4).applyTo(composite);\r
-\r
-        // In this dialog there are fields for:\r
-        //   Message:\r
-        //   Tag:\r
-        //   Description:\r
-        //   Time:\r
-        //   [x] Baseline\r
-\r
-        GridDataFactory gd1 = GridDataFactory.fillDefaults().span(1, 1);\r
-        GridDataFactory gd2 = GridDataFactory.fillDefaults().minSize(300, SWT.DEFAULT).indent(10, 0).span(3, 1).grab(true, false);\r
-\r
-        nameLabel = new Label(composite, 0);\r
-        nameLabel.setText("Message:");\r
-        gd1.applyTo(nameLabel);\r
-        nameText = new Text(composite, SWT.BORDER);\r
-        nameText.setText("My Milestone");\r
-        gd2.applyTo(nameText);\r
-\r
-        tagLabel = new Label(composite, 0);\r
-        tagLabel.setText("Tag:");\r
-        gd1.applyTo(tagLabel);\r
-        tagText = new Text(composite, SWT.BORDER);\r
-        tagText.setText("");\r
-        gd2.applyTo(tagText);\r
-\r
-        descLabel = new Label(composite, 0);\r
-        descLabel.setText("Description:");\r
-        gd1.applyTo(descLabel);\r
-        descText = new Text(composite, SWT.BORDER);\r
-        gd2.applyTo(descText);\r
-\r
-        timeLabel = new Label(composite, 0);\r
-        timeLabel.setText("Time:");\r
-        gd1.applyTo(timeLabel);\r
-        timeText = new Text(composite, SWT.BORDER);\r
-        TimeFormat tf = new TimeFormat(data.initialTime, 3);\r
-        String time = tf.format( data.initialTime );\r
-        timeText.setText( time );\r
-        gd2.applyTo(timeText);\r
-        timeText.addModifyListener(new ModifyListener() {\r
-            @Override\r
-            public void modifyText(ModifyEvent e) {\r
-                validateInput();\r
-            }\r
-        });\r
-\r
-        baselineLabel = new Label(composite, 0);\r
-        baselineLabel.setText("Is baseline:");\r
-        gd1.applyTo(baselineLabel);\r
-        baselineButt = new Button(composite, SWT.CHECK);\r
-        gd2.applyTo(baselineButt);\r
-\r
-        // Error decorations\r
-        Image image = FieldDecorationRegistry.getDefault()\r
-                .getFieldDecoration(FieldDecorationRegistry.DEC_ERROR)\r
-                .getImage();\r
-\r
-        timeDecor = createDecoration(timeText, SWT.LEFT | SWT.CENTER, image);\r
-\r
-        validateInput();\r
-\r
-        return composite;\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 void validateInput() {\r
-        boolean ok = true;\r
-\r
-        // Read data from widgets.\r
-        data.time = timeText.getText();\r
-\r
-        // Validate data.\r
-        Double parsedTime = TimeInputValidator.INSTANCE.parse( data.time );\r
-        if (parsedTime == null) {\r
-            ok = false;\r
-            data.parsedTime = 0.0;\r
-        } else {\r
-            data.parsedTime = parsedTime;\r
-        }\r
-        setDecoration(timeDecor, parsedTime == null ? "Invalid time 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
-    @Override\r
-    protected void okPressed() {\r
-        data.message = nameText.getText();\r
-        data.tag = tagText.getText();\r
-        data.description = descText.getText();\r
-        data.time = timeText.getText();\r
-        data.isBaseline = baselineButt.getSelection();\r
-        data.isMilestone = true;\r
-        super.okPressed();\r
-    }\r
-\r
-    protected void setFinishable(boolean ok) {\r
-        Button b = getButton(OK);\r
-        if (b != null)\r
-            b.setEnabled(ok);\r
-    }\r
-\r
-}\r
-\r
-class EventData {\r
-    double initialTime;\r
-\r
-    String message;\r
-    String tag;\r
-    String description;\r
-    String time;\r
-    double parsedTime;\r
-    boolean isBaseline;\r
-    boolean isMilestone;\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.editor;
+
+import org.eclipse.core.commands.AbstractHandler;
+import org.eclipse.core.commands.ExecutionEvent;
+import org.eclipse.core.commands.ExecutionException;
+import org.eclipse.core.expressions.IEvaluationContext;
+import org.eclipse.jface.dialogs.Dialog;
+import org.eclipse.jface.dialogs.IDialogSettings;
+import org.eclipse.jface.dialogs.IInputValidator;
+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.swt.SWT;
+import org.eclipse.swt.events.ModifyEvent;
+import org.eclipse.swt.events.ModifyListener;
+import org.eclipse.swt.graphics.Image;
+import org.eclipse.swt.graphics.Point;
+import org.eclipse.swt.widgets.Button;
+import org.eclipse.swt.widgets.Composite;
+import org.eclipse.swt.widgets.Control;
+import org.eclipse.swt.widgets.Label;
+import org.eclipse.swt.widgets.Shell;
+import org.eclipse.swt.widgets.Text;
+import org.simantics.Simantics;
+import org.simantics.charts.Activator;
+import org.simantics.databoard.Bindings;
+import org.simantics.db.Resource;
+import org.simantics.db.Session;
+import org.simantics.db.VirtualGraph;
+import org.simantics.db.WriteGraph;
+import org.simantics.db.common.request.WriteRequest;
+import org.simantics.db.exception.DatabaseException;
+import org.simantics.db.service.VirtualGraphSupport;
+import org.simantics.event.ontology.EventResource;
+import org.simantics.event.ontology.EventViewResource;
+import org.simantics.event.util.EventWriteData;
+import org.simantics.event.view.handler.CorrectMilestoneLabelsAction;
+import org.simantics.layer0.Layer0;
+import org.simantics.trend.impl.TrendNode;
+import org.simantics.utils.format.TimeFormat;
+
+/**
+ * This handler opens a dialog for new milestone.
+ * 
+ * @author toni.kalajainen
+ */
+public class AddMilestoneHandler extends AbstractHandler {
+
+    @Override
+    public Object execute(ExecutionEvent event) throws ExecutionException {
+
+        TimeSeriesEditor tse = getTSE(event);
+        if (tse == null)
+            return null;
+        TrendNode tn = tse.trendNode;
+        if (tn == null)
+            return null;
+        final Resource experiment = tse.chartData.run;
+        if (experiment == null)
+            return null;
+
+        EventData data = new EventData();
+        data.initialTime = tn.lastMouseHoverTime;
+        AddMilestoneDialog d = new AddMilestoneDialog( tse.getSite().getShell(), data );
+        int ok = d.open();
+        if (ok != Dialog.OK)
+            return null;
+
+        Session session = Simantics.getSession();
+        VirtualGraph vg = session.getService(VirtualGraphSupport.class).getWorkspacePersistent("experiments");
+        session.async( new CreateManualEvent(vg, experiment, data) );
+
+        return null;
+    }
+
+    TimeSeriesEditor getTSE(ExecutionEvent event) {
+        Object eac = event.getApplicationContext();
+        if (eac == null || (eac instanceof IEvaluationContext == false)) return null;
+        IEvaluationContext ec = (IEvaluationContext) eac;
+        Object editor = ec.getVariable("activeEditor");
+        if (editor == null || (editor instanceof TimeSeriesEditor == false)) return null;
+        TimeSeriesEditor tse = (TimeSeriesEditor) editor;
+        return tse;
+    }
+
+    static class CreateManualEvent extends WriteRequest {
+
+        private final Resource experiment;
+        private final EventData eventData;
+
+        public CreateManualEvent(VirtualGraph vg, Resource experiment, EventData eventData) {
+            super(vg);
+            this.experiment = experiment;
+            this.eventData = eventData;
+        }
+
+        @Override
+        public void perform(WriteGraph graph) throws DatabaseException {
+            Layer0 L0 = Layer0.getInstance(graph);
+            EventResource EVENT = EventResource.getInstance(graph);
+            EventViewResource EVENTVIEW = EventViewResource.getInstance(graph);
+            Resource eventlog = graph.getPossibleObject(experiment, EVENT.IsEventProducerOf);
+            if (eventlog == null) return;
+
+            EventWriteData data = new EventWriteData(graph, eventlog, getProvider());
+            data.prepareToWrite(graph);
+
+            Resource event = graph.newResource(); 
+            graph.claim(event, L0.InstanceOf, EVENT.Event);
+            graph.claimLiteral(event, L0.HasName, L0.String, ""+data.targetPos, Bindings.STRING);
+            graph.claimLiteral(event, L0.HasLabel, L0.HasLabel_Inverse, L0.String, eventData.description, Bindings.STRING);
+            graph.claimLiteral(event, L0.HasDescription, L0.HasDescription_Inverse, L0.String, eventData.description, Bindings.STRING);
+            graph.claimLiteral(event, EVENT.HasTimestamp, EVENT.HasTimestamp_Inverse, EVENT.TimeStamp, eventData.parsedTime, Bindings.DOUBLE);
+            if (eventData.isMilestone)
+                graph.claim(event, EVENT.Milestone, event);
+            if (eventData.isBaseline) {
+                graph.deny(eventlog, EVENT.EventLog_HasBaselineEvent);
+                graph.claim(eventlog, EVENT.EventLog_HasBaselineEvent, event);
+            }
+            graph.claim(event, EVENT.Event_type, null, EVENTVIEW.ManualEventType);
+            graph.claimLiteral(event, EVENT.Event_message, EVENT.Event_message_Inverse, L0.String, eventData.message, Bindings.STRING);
+            graph.claimLiteral(event, EVENT.Event_tag, EVENT.Event_tag_Inverse, L0.String, eventData.tag, Bindings.STRING);
+            graph.claim(event, EVENT.NoReturn, EVENT.NoReturn, event);
+            graph.claim(data.targetSlice, L0.ConsistsOf, L0.PartOf, event);
+
+            data.written();
+            data.commit(graph);
+
+            graph.syncRequest( new CorrectMilestoneLabelsAction(eventlog, getProvider()) );
+        }
+    }
+
+}
+
+
+class AddMilestoneDialog extends Dialog {
+
+    private static final String DIALOG = "AddMilestoneDialog"; //$NON-NLS-1$
+
+    IInputValidator timeValidator = new IInputValidator() {
+        @Override
+        public String isValid(String text) {
+            if (text.trim().isEmpty())
+                return null;
+            return TimeInputValidator.INSTANCE.isValid(text);
+        }
+    }; 
+
+    Label nameLabel, descLabel, tagLabel, timeLabel, baselineLabel;
+    Text nameText, descText, tagText, timeText;
+    Button baselineButt;
+    ControlDecoration timeDecor;
+
+    EventData data;
+
+    private IDialogSettings dialogBoundsSettings;
+
+    protected AddMilestoneDialog(Shell parentShell, EventData data) {
+        super(parentShell);
+        this.data = data;
+
+        IDialogSettings settings = Activator.getDefault().getDialogSettings();
+        dialogBoundsSettings = settings.getSection(DIALOG);
+        if (dialogBoundsSettings == null)
+            dialogBoundsSettings = settings.addNewSection(DIALOG);
+    }
+
+    @Override
+    protected IDialogSettings getDialogBoundsSettings() {
+        return dialogBoundsSettings;
+    }
+
+    @Override
+    protected int getShellStyle() {
+        return SWT.RESIZE | SWT.MODELESS | SWT.TITLE | SWT.CLOSE | SWT.BORDER;
+    }
+
+    @Override
+    protected void configureShell(Shell newShell) {
+        super.configureShell(newShell);
+        newShell.setText("Add new milestone");
+    }
+
+    @Override
+    protected Point getInitialSize() {
+        return super.getInitialSize();
+    }
+
+    @Override
+    protected Control createDialogArea(Composite parent) 
+    {
+        Composite composite = (Composite) super.createDialogArea(parent);
+        GridLayoutFactory.fillDefaults().margins(8, 8).numColumns(4).applyTo(composite);
+
+        // In this dialog there are fields for:
+        //   Message:
+        //   Tag:
+        //   Description:
+        //   Time:
+        //   [x] Baseline
+
+        GridDataFactory gd1 = GridDataFactory.fillDefaults().span(1, 1);
+        GridDataFactory gd2 = GridDataFactory.fillDefaults().minSize(300, SWT.DEFAULT).indent(10, 0).span(3, 1).grab(true, false);
+
+        nameLabel = new Label(composite, 0);
+        nameLabel.setText("Message:");
+        gd1.applyTo(nameLabel);
+        nameText = new Text(composite, SWT.BORDER);
+        nameText.setText("My Milestone");
+        gd2.applyTo(nameText);
+
+        tagLabel = new Label(composite, 0);
+        tagLabel.setText("Tag:");
+        gd1.applyTo(tagLabel);
+        tagText = new Text(composite, SWT.BORDER);
+        tagText.setText("");
+        gd2.applyTo(tagText);
+
+        descLabel = new Label(composite, 0);
+        descLabel.setText("Description:");
+        gd1.applyTo(descLabel);
+        descText = new Text(composite, SWT.BORDER);
+        gd2.applyTo(descText);
+
+        timeLabel = new Label(composite, 0);
+        timeLabel.setText("Time:");
+        gd1.applyTo(timeLabel);
+        timeText = new Text(composite, SWT.BORDER);
+        TimeFormat tf = new TimeFormat(data.initialTime, 3);
+        String time = tf.format( data.initialTime );
+        timeText.setText( time );
+        gd2.applyTo(timeText);
+        timeText.addModifyListener(new ModifyListener() {
+            @Override
+            public void modifyText(ModifyEvent e) {
+                validateInput();
+            }
+        });
+
+        baselineLabel = new Label(composite, 0);
+        baselineLabel.setText("Is baseline:");
+        gd1.applyTo(baselineLabel);
+        baselineButt = new Button(composite, SWT.CHECK);
+        gd2.applyTo(baselineButt);
+
+        // Error decorations
+        Image image = FieldDecorationRegistry.getDefault()
+                .getFieldDecoration(FieldDecorationRegistry.DEC_ERROR)
+                .getImage();
+
+        timeDecor = createDecoration(timeText, SWT.LEFT | SWT.CENTER, image);
+
+        validateInput();
+
+        return composite;
+    }
+
+    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 void validateInput() {
+        boolean ok = true;
+
+        // Read data from widgets.
+        data.time = timeText.getText();
+
+        // Validate data.
+        Double parsedTime = TimeInputValidator.INSTANCE.parse( data.time );
+        if (parsedTime == null) {
+            ok = false;
+            data.parsedTime = 0.0;
+        } else {
+            data.parsedTime = parsedTime;
+        }
+        setDecoration(timeDecor, parsedTime == null ? "Invalid time 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();
+        }
+    }
+
+    @Override
+    protected void okPressed() {
+        data.message = nameText.getText();
+        data.tag = tagText.getText();
+        data.description = descText.getText();
+        data.time = timeText.getText();
+        data.isBaseline = baselineButt.getSelection();
+        data.isMilestone = true;
+        super.okPressed();
+    }
+
+    protected void setFinishable(boolean ok) {
+        Button b = getButton(OK);
+        if (b != null)
+            b.setEnabled(ok);
+    }
+
+}
+
+class EventData {
+    double initialTime;
+
+    String message;
+    String tag;
+    String description;
+    String time;
+    double parsedTime;
+    boolean isBaseline;
+    boolean isMilestone;
+}