/******************************************************************************* * 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; }