]> gerrit.simantics Code Review - simantics/platform.git/blob - bundles/org.simantics.charts/src/org/simantics/charts/editor/AddMilestoneHandler.java
Fixed context menu popup location for HiDPI displays with display zoom
[simantics/platform.git] / bundles / org.simantics.charts / src / org / simantics / charts / editor / AddMilestoneHandler.java
1 /*******************************************************************************
2  * Copyright (c) 2007, 2011 Association for Decentralized Information Management in
3  * Industry THTH ry.
4  * All rights reserved. This program and the accompanying materials
5  * are made available under the terms of the Eclipse Public License v1.0
6  * which accompanies this distribution, and is available at
7  * http://www.eclipse.org/legal/epl-v10.html
8  *
9  * Contributors:
10  *     VTT Technical Research Centre of Finland - initial API and implementation
11  *******************************************************************************/
12 package org.simantics.charts.editor;
13
14 import org.eclipse.core.commands.AbstractHandler;
15 import org.eclipse.core.commands.ExecutionEvent;
16 import org.eclipse.core.commands.ExecutionException;
17 import org.eclipse.core.expressions.IEvaluationContext;
18 import org.eclipse.jface.dialogs.Dialog;
19 import org.eclipse.jface.dialogs.IDialogSettings;
20 import org.eclipse.jface.dialogs.IInputValidator;
21 import org.eclipse.jface.fieldassist.ControlDecoration;
22 import org.eclipse.jface.fieldassist.FieldDecorationRegistry;
23 import org.eclipse.jface.layout.GridDataFactory;
24 import org.eclipse.jface.layout.GridLayoutFactory;
25 import org.eclipse.swt.SWT;
26 import org.eclipse.swt.events.ModifyEvent;
27 import org.eclipse.swt.events.ModifyListener;
28 import org.eclipse.swt.graphics.Image;
29 import org.eclipse.swt.graphics.Point;
30 import org.eclipse.swt.widgets.Button;
31 import org.eclipse.swt.widgets.Composite;
32 import org.eclipse.swt.widgets.Control;
33 import org.eclipse.swt.widgets.Label;
34 import org.eclipse.swt.widgets.Shell;
35 import org.eclipse.swt.widgets.Text;
36 import org.simantics.Simantics;
37 import org.simantics.charts.Activator;
38 import org.simantics.databoard.Bindings;
39 import org.simantics.db.Resource;
40 import org.simantics.db.Session;
41 import org.simantics.db.VirtualGraph;
42 import org.simantics.db.WriteGraph;
43 import org.simantics.db.common.request.WriteRequest;
44 import org.simantics.db.exception.DatabaseException;
45 import org.simantics.db.service.VirtualGraphSupport;
46 import org.simantics.event.ontology.EventResource;
47 import org.simantics.event.ontology.EventViewResource;
48 import org.simantics.event.util.EventWriteData;
49 import org.simantics.event.view.handler.CorrectMilestoneLabelsAction;
50 import org.simantics.layer0.Layer0;
51 import org.simantics.trend.impl.TrendNode;
52 import org.simantics.utils.format.TimeFormat;
53
54 /**
55  * This handler opens a dialog for new milestone.
56  * 
57  * @author toni.kalajainen
58  */
59 public class AddMilestoneHandler extends AbstractHandler {
60
61     @Override
62     public Object execute(ExecutionEvent event) throws ExecutionException {
63
64         TimeSeriesEditor tse = getTSE(event);
65         if (tse == null)
66             return null;
67         TrendNode tn = tse.trendNode;
68         if (tn == null)
69             return null;
70         final Resource experiment = tse.chartData.run;
71         if (experiment == null)
72             return null;
73
74         EventData data = new EventData();
75         data.initialTime = tn.lastMouseHoverTime;
76         AddMilestoneDialog d = new AddMilestoneDialog( tse.getSite().getShell(), data );
77         int ok = d.open();
78         if (ok != Dialog.OK)
79             return null;
80
81         Session session = Simantics.getSession();
82         VirtualGraph vg = session.getService(VirtualGraphSupport.class).getWorkspacePersistent("experiments");
83         session.async( new CreateManualEvent(vg, experiment, data) );
84
85         return null;
86     }
87
88     TimeSeriesEditor getTSE(ExecutionEvent event) {
89         Object eac = event.getApplicationContext();
90         if (eac == null || (eac instanceof IEvaluationContext == false)) return null;
91         IEvaluationContext ec = (IEvaluationContext) eac;
92         Object editor = ec.getVariable("activeEditor");
93         if (editor == null || (editor instanceof TimeSeriesEditor == false)) return null;
94         TimeSeriesEditor tse = (TimeSeriesEditor) editor;
95         return tse;
96     }
97
98     static class CreateManualEvent extends WriteRequest {
99
100         private final Resource experiment;
101         private final EventData eventData;
102
103         public CreateManualEvent(VirtualGraph vg, Resource experiment, EventData eventData) {
104             super(vg);
105             this.experiment = experiment;
106             this.eventData = eventData;
107         }
108
109         @Override
110         public void perform(WriteGraph graph) throws DatabaseException {
111             Layer0 L0 = Layer0.getInstance(graph);
112             EventResource EVENT = EventResource.getInstance(graph);
113             EventViewResource EVENTVIEW = EventViewResource.getInstance(graph);
114             Resource eventlog = graph.getPossibleObject(experiment, EVENT.IsEventProducerOf);
115             if (eventlog == null) return;
116
117             EventWriteData data = new EventWriteData(graph, eventlog, getProvider());
118             data.prepareToWrite(graph);
119
120             Resource event = graph.newResource(); 
121             graph.claim(event, L0.InstanceOf, EVENT.Event);
122             graph.claimLiteral(event, L0.HasName, L0.String, ""+data.targetPos, Bindings.STRING);
123             graph.claimLiteral(event, L0.HasLabel, L0.HasLabel_Inverse, L0.String, eventData.description, Bindings.STRING);
124             graph.claimLiteral(event, L0.HasDescription, L0.HasDescription_Inverse, L0.String, eventData.description, Bindings.STRING);
125             graph.claimLiteral(event, EVENT.HasTimestamp, EVENT.HasTimestamp_Inverse, EVENT.TimeStamp, eventData.parsedTime, Bindings.DOUBLE);
126             if (eventData.isMilestone)
127                 graph.claim(event, EVENT.Milestone, event);
128             if (eventData.isBaseline) {
129                 graph.deny(eventlog, EVENT.EventLog_HasBaselineEvent);
130                 graph.claim(eventlog, EVENT.EventLog_HasBaselineEvent, event);
131             }
132             graph.claim(event, EVENT.Event_type, null, EVENTVIEW.ManualEventType);
133             graph.claimLiteral(event, EVENT.Event_message, EVENT.Event_message_Inverse, L0.String, eventData.message, Bindings.STRING);
134             graph.claimLiteral(event, EVENT.Event_tag, EVENT.Event_tag_Inverse, L0.String, eventData.tag, Bindings.STRING);
135             graph.claim(event, EVENT.NoReturn, EVENT.NoReturn, event);
136             graph.claim(data.targetSlice, L0.ConsistsOf, L0.PartOf, event);
137
138             data.written();
139             data.commit(graph);
140
141             graph.syncRequest( new CorrectMilestoneLabelsAction(eventlog, getProvider()) );
142         }
143     }
144
145 }
146
147
148 class AddMilestoneDialog extends Dialog {
149
150     private static final String DIALOG = "AddMilestoneDialog"; //$NON-NLS-1$
151
152     IInputValidator timeValidator = new IInputValidator() {
153         @Override
154         public String isValid(String text) {
155             if (text.trim().isEmpty())
156                 return null;
157             return TimeInputValidator.INSTANCE.isValid(text);
158         }
159     };  
160
161     Label nameLabel, descLabel, tagLabel, timeLabel, baselineLabel;
162     Text nameText, descText, tagText, timeText;
163     Button baselineButt;
164     ControlDecoration timeDecor;
165
166     EventData data;
167
168     private IDialogSettings dialogBoundsSettings;
169
170     protected AddMilestoneDialog(Shell parentShell, EventData data) {
171         super(parentShell);
172         this.data = data;
173
174         IDialogSettings settings = Activator.getDefault().getDialogSettings();
175         dialogBoundsSettings = settings.getSection(DIALOG);
176         if (dialogBoundsSettings == null)
177             dialogBoundsSettings = settings.addNewSection(DIALOG);
178     }
179
180     @Override
181     protected IDialogSettings getDialogBoundsSettings() {
182         return dialogBoundsSettings;
183     }
184
185     @Override
186     protected int getShellStyle() {
187         return SWT.RESIZE | SWT.MODELESS | SWT.TITLE | SWT.CLOSE | SWT.BORDER;
188     }
189
190     @Override
191     protected void configureShell(Shell newShell) {
192         super.configureShell(newShell);
193         newShell.setText("Add new milestone");
194     }
195
196     @Override
197     protected Point getInitialSize() {
198         return super.getInitialSize();
199     }
200
201     @Override
202     protected Control createDialogArea(Composite parent) 
203     {
204         Composite composite = (Composite) super.createDialogArea(parent);
205         GridLayoutFactory.fillDefaults().margins(8, 8).numColumns(4).applyTo(composite);
206
207         // In this dialog there are fields for:
208         //   Message:
209         //   Tag:
210         //   Description:
211         //   Time:
212         //   [x] Baseline
213
214         GridDataFactory gd1 = GridDataFactory.fillDefaults().span(1, 1);
215         GridDataFactory gd2 = GridDataFactory.fillDefaults().minSize(300, SWT.DEFAULT).indent(10, 0).span(3, 1).grab(true, false);
216
217         nameLabel = new Label(composite, 0);
218         nameLabel.setText("Message:");
219         gd1.applyTo(nameLabel);
220         nameText = new Text(composite, SWT.BORDER);
221         nameText.setText("My Milestone");
222         gd2.applyTo(nameText);
223
224         tagLabel = new Label(composite, 0);
225         tagLabel.setText("Tag:");
226         gd1.applyTo(tagLabel);
227         tagText = new Text(composite, SWT.BORDER);
228         tagText.setText("");
229         gd2.applyTo(tagText);
230
231         descLabel = new Label(composite, 0);
232         descLabel.setText("Description:");
233         gd1.applyTo(descLabel);
234         descText = new Text(composite, SWT.BORDER);
235         gd2.applyTo(descText);
236
237         timeLabel = new Label(composite, 0);
238         timeLabel.setText("Time:");
239         gd1.applyTo(timeLabel);
240         timeText = new Text(composite, SWT.BORDER);
241         TimeFormat tf = new TimeFormat(data.initialTime, 3);
242         String time = tf.format( data.initialTime );
243         timeText.setText( time );
244         gd2.applyTo(timeText);
245         timeText.addModifyListener(new ModifyListener() {
246             @Override
247             public void modifyText(ModifyEvent e) {
248                 validateInput();
249             }
250         });
251
252         baselineLabel = new Label(composite, 0);
253         baselineLabel.setText("Is baseline:");
254         gd1.applyTo(baselineLabel);
255         baselineButt = new Button(composite, SWT.CHECK);
256         gd2.applyTo(baselineButt);
257
258         // Error decorations
259         Image image = FieldDecorationRegistry.getDefault()
260                 .getFieldDecoration(FieldDecorationRegistry.DEC_ERROR)
261                 .getImage();
262
263         timeDecor = createDecoration(timeText, SWT.LEFT | SWT.CENTER, image);
264
265         validateInput();
266
267         return composite;
268     }
269
270     protected ControlDecoration createDecoration(Control control, int position, Image image) {
271         ControlDecoration d = new ControlDecoration(control, position);
272         d.setMarginWidth(2);
273         d.setImage(image);
274         d.hide();
275         return d;
276     }
277
278     protected void validateInput() {
279         boolean ok = true;
280
281         // Read data from widgets.
282         data.time = timeText.getText();
283
284         // Validate data.
285         Double parsedTime = TimeInputValidator.INSTANCE.parse( data.time );
286         if (parsedTime == null) {
287             ok = false;
288             data.parsedTime = 0.0;
289         } else {
290             data.parsedTime = parsedTime;
291         }
292         setDecoration(timeDecor, parsedTime == null ? "Invalid time value" : null);
293
294         setFinishable(ok);
295     }
296
297     private void setDecoration(ControlDecoration decor, String desc) {
298         if (decor == null)
299             return;
300         if (desc != null) {
301             decor.setDescriptionText(desc);
302             decor.show();
303         } else {
304             decor.hide();
305         }
306     }
307
308     @Override
309     protected void okPressed() {
310         data.message = nameText.getText();
311         data.tag = tagText.getText();
312         data.description = descText.getText();
313         data.time = timeText.getText();
314         data.isBaseline = baselineButt.getSelection();
315         data.isMilestone = true;
316         super.okPressed();
317     }
318
319     protected void setFinishable(boolean ok) {
320         Button b = getButton(OK);
321         if (b != null)
322             b.setEnabled(ok);
323     }
324
325 }
326
327 class EventData {
328     double initialTime;
329
330     String message;
331     String tag;
332     String description;
333     String time;
334     double parsedTime;
335     boolean isBaseline;
336     boolean isMilestone;
337 }