]> gerrit.simantics Code Review - simantics/platform.git/commitdiff
Fixed context menu popup location for HiDPI displays with display zoom 31/2331/1
authorTuukka Lehtonen <tuukka.lehtonen@semantum.fi>
Thu, 18 Oct 2018 10:49:37 +0000 (13:49 +0300)
committerTuukka Lehtonen <tuukka.lehtonen@semantum.fi>
Thu, 18 Oct 2018 10:49:37 +0000 (13:49 +0300)
gitlab #152

Change-Id: If7ca761cf305daa4ecfd837af75c93eae285df1a

bundles/org.simantics.charts/META-INF/MANIFEST.MF
bundles/org.simantics.charts/src/org/simantics/charts/editor/TimeSeriesEditor.java
bundles/org.simantics.charts/src/org/simantics/charts/editor/e4/ChartCopyHandler.java [deleted file]
bundles/org.simantics.charts/src/org/simantics/charts/editor/e4/MoveHairlineHandler.java [deleted file]
bundles/org.simantics.charts/src/org/simantics/charts/editor/e4/TimeSeriesEditor.java [deleted file]

index 2b3484be8ada3d5744824178e5626aec0d739e37..46f215bd37e0cb80eabbcae57fbc9ee8854dd81a 100644 (file)
@@ -7,7 +7,6 @@ Bundle-Activator: org.simantics.charts.Activator
 Bundle-Vendor: Semantum Oy
 Export-Package: org.simantics.charts,
  org.simantics.charts.editor,
- org.simantics.charts.editor.e4,
  org.simantics.charts.preference,
  org.simantics.charts.query,
  org.simantics.charts.ui
index cadb5bd4b372e5841479497d672cd1fde938511e..bffd4c8a24952fb60bfae8cd517be91a9d24e36d 100644 (file)
@@ -134,6 +134,7 @@ import org.simantics.utils.threads.ThreadUtils;
 import org.simantics.utils.ui.BundleUtils;
 import org.simantics.utils.ui.ErrorLogger;
 import org.simantics.utils.ui.ExceptionUtils;
+import org.simantics.utils.ui.SWTDPIUtil;
 import org.simantics.utils.ui.SWTUtils;
 import org.simantics.utils.ui.dialogs.ShowMessage;
 import org.simantics.utils.ui.jface.ActiveSelectionProvider;
@@ -567,13 +568,12 @@ public class TimeSeriesEditor extends ResourceEditorPart {
                 if (event.button != MouseEvent.RIGHT_BUTTON)
                     return false;
 
-                final Point p = new Point((int) event.screenPosition.getX(), (int) event.screenPosition.getY());
-                SWTUtils.asyncExec(chassis, new Runnable() {
-                    @Override
-                    public void run() {
-                        if (!canvas.isDisposed())
-                            showPopup(p);
-                    }
+                Point p = new Point(
+                        SWTDPIUtil.downscaleSwt((int) event.screenPosition.getX()),
+                        SWTDPIUtil.downscaleSwt((int) event.screenPosition.getY()));
+                SWTUtils.asyncExec(chassis, () -> {
+                    if (!canvas.isDisposed())
+                        showPopup(p);
                 });
                 return true;
             }
diff --git a/bundles/org.simantics.charts/src/org/simantics/charts/editor/e4/ChartCopyHandler.java b/bundles/org.simantics.charts/src/org/simantics/charts/editor/e4/ChartCopyHandler.java
deleted file mode 100644 (file)
index 9d10ad3..0000000
+++ /dev/null
@@ -1,222 +0,0 @@
-/*******************************************************************************
- * Copyright (c) 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.e4;
-
-import java.awt.Toolkit;
-import java.awt.datatransfer.Clipboard;
-import java.awt.datatransfer.StringSelection;
-import java.io.BufferedWriter;
-import java.io.File;
-import java.io.FileOutputStream;
-import java.io.IOException;
-import java.io.OutputStreamWriter;
-import java.lang.reflect.InvocationTargetException;
-import java.nio.charset.Charset;
-import java.util.Collections;
-import java.util.List;
-import java.util.concurrent.atomic.AtomicBoolean;
-
-import javax.inject.Named;
-
-import org.eclipse.core.commands.ExecutionException;
-import org.eclipse.core.runtime.IProgressMonitor;
-import org.eclipse.core.runtime.preferences.InstanceScope;
-import org.eclipse.e4.core.di.annotations.CanExecute;
-import org.eclipse.e4.core.di.annotations.Execute;
-import org.eclipse.e4.ui.model.application.ui.basic.MPart;
-import org.eclipse.e4.ui.services.IServiceConstants;
-import org.eclipse.jface.action.IStatusLineManager;
-import org.eclipse.jface.operation.IRunnableWithProgress;
-import org.eclipse.jface.preference.IPreferenceStore;
-import org.eclipse.swt.SWT;
-import org.eclipse.swt.widgets.FileDialog;
-import org.eclipse.swt.widgets.Shell;
-import org.eclipse.ui.IWorkbenchPart;
-import org.eclipse.ui.PlatformUI;
-import org.eclipse.ui.preferences.ScopedPreferenceStore;
-import org.simantics.charts.ui.CSVProgressMonitor;
-import org.simantics.databoard.binding.error.BindingException;
-import org.simantics.databoard.parser.StringEscapeUtils;
-import org.simantics.databoard.util.Bean;
-import org.simantics.databoard.util.StreamUtil;
-import org.simantics.history.HistoryException;
-import org.simantics.history.ItemManager;
-import org.simantics.history.csv.CSVFormatter;
-import org.simantics.history.csv.ColumnSeparator;
-import org.simantics.history.csv.DecimalSeparator;
-import org.simantics.history.csv.ExportInterpolation;
-import org.simantics.history.util.subscription.SamplingFormat;
-import org.simantics.modeling.preferences.CSVPreferences;
-import org.simantics.trend.configuration.TrendItem;
-import org.simantics.trend.impl.TrendNode;
-import org.simantics.utils.format.FormattingUtils;
-import org.simantics.utils.ui.ErrorLogger;
-import org.simantics.utils.ui.workbench.WorkbenchUtils;
-
-/**
- * @author Tuukka Lehtonen
- */
-public class ChartCopyHandler {
-
-       String lastFile;
-       
-    @CanExecute
-    public boolean canExecute(@Named(IServiceConstants.ACTIVE_PART) MPart part) {
-        if (part == null)
-            return false;
-        if (part.getObject() instanceof TimeSeriesEditor == false)
-            return false;
-        return true;
-    }
-       
-    @Execute
-    public void execute(@Named(IServiceConstants.ACTIVE_PART) MPart part, @Named(IServiceConstants.ACTIVE_SHELL) Shell shell) throws ExecutionException {
-       if (part.getObject() instanceof TimeSeriesEditor == false)
-           return;
-        TimeSeriesEditor editor = (TimeSeriesEditor) part.getObject();
-        final TrendNode trendNode = editor.trendNode;
-        IWorkbenchPart wbpart = WorkbenchUtils.getActiveWorkbenchPart();
-        IStatusLineManager status = WorkbenchUtils.getStatusLine(wbpart);
-
-        final AtomicBoolean result = new AtomicBoolean(false);
-        try {
-            PlatformUI.getWorkbench().getProgressService().busyCursorWhile(new IRunnableWithProgress() {
-                @Override
-                public void run(IProgressMonitor monitor) throws InvocationTargetException, InterruptedException {
-                    result.set( copyDataToClipboard(monitor, trendNode, Format.JOINED_TIME, shell) );
-                }
-            });
-            if (!result.get()) {
-                status.setMessage("No data to copy");
-            } else {
-                status.setMessage("Copied chart data to clipboard");
-            }
-            status.setErrorMessage(null);
-        } catch (InvocationTargetException e) {
-            ErrorLogger.defaultLogError(e.getCause());
-        } catch (InterruptedException e) {
-            ErrorLogger.defaultLogError(e);
-        }
-    }
-
-    static enum Format {
-        TIME_VALUE_PAIRS,
-        JOINED_TIME
-    }
-
-    public boolean copyDataToClipboard(IProgressMonitor monitor, TrendNode t, Format format, final Shell shell) {
-        Charset UTF8        = Charset.forName("UTF-8");
-        try {
-               // String builder can be really slow when it is extended many times over. 
-               // Instead stream to file with buffering
-//            StringBuilder sb = new StringBuilder();
-               IPreferenceStore csvnode = new ScopedPreferenceStore( InstanceScope.INSTANCE, CSVPreferences.P_NODE );
-               String ext = csvnode.getString(CSVPreferences.P_CSV_FILE_EXTENSION);
-               File tmpFile = File.createTempFile("clipboard", ext);
-               tmpFile.deleteOnExit();
-               FileOutputStream fos = new FileOutputStream(tmpFile); 
-               BufferedWriter w = new BufferedWriter(new OutputStreamWriter(fos, UTF8));
-               try {
-                       ItemManager im = new ItemManager( t.historian.getItems() );
-                   CSVFormatter formatter = new CSVFormatter();            
-                   formatter.setTimeRange(t.horizRuler.from, t.horizRuler.end);
-                   
-                   // Write preferences
-                   formatter.setStartTime( csvnode.getDouble(CSVPreferences.P_CSV_START_TIME) );
-                   formatter.setTimeStep( csvnode.getDouble(CSVPreferences.P_CSV_TIME_STEP) );
-                   formatter.setDecimalSeparator( DecimalSeparator.fromPreference(csvnode.getString(CSVPreferences.P_CSV_DECIMAL_SEPARATOR) ) );
-                   formatter.setColumnSeparator( ColumnSeparator.fromPreference(StringEscapeUtils.unescape( csvnode.getString(CSVPreferences.P_CSV_COLUMN_SEPARATOR) ) ) );
-                   formatter.setResample( csvnode.getBoolean(CSVPreferences.P_CSV_RESAMPLE) );
-                   formatter.setNumberInterpolation( ExportInterpolation.fromPreference (csvnode.getString(CSVPreferences.P_CSV_SAMPLING_MODE) ) );
-                   formatter.setTimeFormat( FormattingUtils.significantDigitFormat( csvnode.getInt(CSVPreferences.P_CSV_TIME_DIGITS) ) );
-                   formatter.setFloatFormat( FormattingUtils.significantDigitFormat( csvnode.getInt(CSVPreferences.P_CSV_FLOAT_DIGITS) ) );
-                   formatter.setNumberFormat( FormattingUtils.significantDigitFormat( csvnode.getInt(CSVPreferences.P_CSV_DOUBLE_DIGITS) ) );
-                   
-                   for (TrendItem i : t.spec.items) {
-                       if (i.hidden) continue;
-                       List<Bean> items = im.search("variableId", i.variableId);
-                       Collections.sort(items, SamplingFormat.INTERVAL_COMPARATOR);
-                       if (items.isEmpty()) continue;
-                       Bean config = items.get(0);
-                       String historyId = (String) config.getFieldUnchecked("id");
-                       formatter.addItem( t.historian, historyId, i.simpleLabel, i.variableReference, i.unit);
-                   }
-                   formatter.sort();
-                   switch (format) {
-                       case TIME_VALUE_PAIRS: 
-//                                             formatter.formulate1(new CSVProgressMonitor(monitor), w);
-                           break;
-                       case JOINED_TIME:
-                           formatter.formulate2(new CSVProgressMonitor(monitor), w);
-                           break;
-                       default:
-                           throw new UnsupportedOperationException("unsupported format " + format);
-                   }
-                   w.flush();
-       
-                   if (tmpFile.length()==0) return false;
-       
-                   Toolkit toolkit = Toolkit.getDefaultToolkit();
-                   Clipboard clipboard = toolkit.getSystemClipboard();
-                   w.flush();
-                   fos.close();
-                   fos = null;
-                       
-                   System.out.println("Exported to "+tmpFile+" size: "+tmpFile.length());
-                   if ( tmpFile.length() > 10*1024*1024 ) {
-//                     String msg = "The data has been written to temporary file:\n"+tmpFile.getCanonicalPath();
-//                     ShowMessage.showInformation( shell.getDisplay(), "Too much data for clipboard.", msg);
-                       final File csvFile = tmpFile;
-                       tmpFile = null;
-                       shell.getDisplay().asyncExec( new Runnable() {
-                                               @Override
-                                               public void run() {
-                                       FileDialog fd = new FileDialog(shell, SWT.SAVE);
-                                       fd.setText("Write CSV to File");
-                                       fd.setFileName( lastFile!=null ? lastFile : csvFile.getAbsolutePath() );
-                                       String newFile = fd.open();
-                                       if ( newFile != null ) {
-                                               lastFile = newFile;
-                                               File ff = new File( newFile );
-                                               ff.delete();
-                                               csvFile.renameTo( ff );
-                                       } else {
-                                               csvFile.delete();
-                                       }
-                                               }} ); 
-                   } else {
-                           String str = StreamUtil.readString(tmpFile, UTF8);
-                           
-                           StringSelection strSel = new StringSelection(str);
-                           clipboard.setContents(strSel, null);
-                   }
-                   
-                       } catch (BindingException e1) {
-                               ErrorLogger.defaultLogError(e1);
-                               return false;
-                       } catch (IOException e) {
-                               ErrorLogger.defaultLogError(e);
-                       } finally {
-                       if ( fos != null ) try { fos.close(); } catch (IOException e) { ErrorLogger.defaultLogError(e); }
-                       if ( tmpFile != null ) tmpFile.delete();
-                       }                       
-
-            return true;
-        } catch (HistoryException e) {
-            ErrorLogger.defaultLogError(e);
-        } catch (IOException e) {
-            ErrorLogger.defaultLogError(e);
-               }
-        return false;
-    }
-
-}
diff --git a/bundles/org.simantics.charts/src/org/simantics/charts/editor/e4/MoveHairlineHandler.java b/bundles/org.simantics.charts/src/org/simantics/charts/editor/e4/MoveHairlineHandler.java
deleted file mode 100644 (file)
index 62d7955..0000000
+++ /dev/null
@@ -1,81 +0,0 @@
-package org.simantics.charts.editor.e4;
-
-import java.util.List;
-
-import javax.inject.Named;
-
-import org.eclipse.e4.core.di.annotations.CanExecute;
-import org.eclipse.e4.core.di.annotations.Execute;
-import org.eclipse.e4.ui.di.AboutToHide;
-import org.eclipse.e4.ui.di.AboutToShow;
-import org.eclipse.e4.ui.model.application.ui.basic.MPart;
-import org.eclipse.e4.ui.model.application.ui.menu.MDirectMenuItem;
-import org.eclipse.e4.ui.model.application.ui.menu.MMenuElement;
-import org.eclipse.e4.ui.model.application.ui.menu.MMenuFactory;
-import org.eclipse.e4.ui.services.IServiceConstants;
-import org.simantics.charts.editor.TrackExperimentTimeAction;
-import org.simantics.trend.configuration.TrendSpec;
-import org.simantics.trend.impl.ItemNode;
-import org.simantics.trend.impl.TrendNode;
-import org.simantics.trend.impl.TrendParticipant;
-
-public class MoveHairlineHandler {
-
-    @AboutToShow
-    public void aboutToShow(List<MMenuElement> items) {
-        MDirectMenuItem menuItem = MMenuFactory.INSTANCE.createDirectMenuItem();
-        menuItem.setContributionURI("bundleclass://org.simantics.charts/org.simantics.charts.editor.e4.MoveHairlineHandler");
-        menuItem.setLabel("Move Hairline Here");
-        items.add(menuItem);
-    }
-    
-    @AboutToHide
-    public void aboutToHide() {
-        
-    }
-    
-    @CanExecute
-    public boolean canExecute(@Named(IServiceConstants.ACTIVE_PART) MPart activePart) {
-        if (activePart != null && activePart.getObject() instanceof TimeSeriesEditor) {
-            TimeSeriesEditor editor = (TimeSeriesEditor) activePart.getObject();
-            
-            TrendNode trendNode = editor.trendNode;
-            TrendParticipant tp = editor.tp;
-            if (trendNode == null || tp == null)
-                return false;
-            
-            TrendSpec trendSpec = trendNode.getTrendSpec();
-            boolean hairlineMovementAllowed =
-                    !(trendSpec.experimentIsRunning &&
-                      trendSpec.viewProfile.trackExperimentTime);
-            
-            return hairlineMovementAllowed;
-        }
-        return false;
-    }
-    
-    @Execute
-    public void execute(@Named(IServiceConstants.ACTIVE_PART) MPart activePart) {
-        if (activePart.getObject() instanceof TimeSeriesEditor) {
-            TimeSeriesEditor editor = (TimeSeriesEditor) activePart.getObject();
-            
-            TrendNode trendNode = editor.trendNode;
-            TrendParticipant tp = editor.tp;
-            if (trendNode == null || tp == null)
-                return;
-
-            TrendSpec trendSpec = trendNode.getTrendSpec();
-            ItemNode hoverItem = tp.hoveringItem;
-            
-            // #TODO Finish this when we are fully in E4 workbench
-            
-//            trend.valueTipTime = time;
-//            trend.repaint();
-//            if (setTrackExperimentTime != null) {
-//                TrackExperimentTimeAction.setTracking(chart, setTrackExperimentTime);
-//            }
-            
-        }
-    }
-
-}
diff --git a/bundles/org.simantics.charts/src/org/simantics/charts/editor/e4/TimeSeriesEditor.java b/bundles/org.simantics.charts/src/org/simantics/charts/editor/e4/TimeSeriesEditor.java
deleted file mode 100644 (file)
index 4c58595..0000000
+++ /dev/null
@@ -1,1148 +0,0 @@
-/*******************************************************************************
- * 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.e4;
-
-import java.awt.geom.Point2D;
-import java.util.Collections;
-import java.util.List;
-import java.util.Set;
-import java.util.UUID;
-import java.util.logging.Level;
-import java.util.logging.Logger;
-
-import javax.inject.Inject;
-
-import org.eclipse.core.runtime.IAdaptable;
-import org.eclipse.core.runtime.IStatus;
-import org.eclipse.core.runtime.Status;
-import org.eclipse.core.runtime.preferences.IEclipsePreferences;
-import org.eclipse.core.runtime.preferences.IEclipsePreferences.IPreferenceChangeListener;
-import org.eclipse.core.runtime.preferences.IEclipsePreferences.PreferenceChangeEvent;
-import org.eclipse.core.runtime.preferences.InstanceScope;
-import org.eclipse.e4.ui.model.application.MApplication;
-import org.eclipse.e4.ui.model.application.MApplicationElement;
-import org.eclipse.e4.ui.model.application.ui.MUIElement;
-import org.eclipse.e4.ui.model.application.ui.basic.MPart;
-import org.eclipse.e4.ui.model.application.ui.menu.MToolBar;
-import org.eclipse.e4.ui.services.EContextService;
-import org.eclipse.e4.ui.workbench.modeling.EModelService;
-import org.eclipse.e4.ui.workbench.modeling.ISelectionListener;
-import org.eclipse.jface.action.IMenuListener;
-import org.eclipse.jface.action.IMenuManager;
-import org.eclipse.jface.action.MenuManager;
-import org.eclipse.jface.action.Separator;
-import org.eclipse.jface.resource.ImageDescriptor;
-import org.eclipse.jface.viewers.StructuredSelection;
-import org.eclipse.swt.SWT;
-import org.eclipse.swt.graphics.Point;
-import org.eclipse.swt.widgets.Composite;
-import org.eclipse.swt.widgets.Display;
-import org.eclipse.swt.widgets.Menu;
-import org.eclipse.swt.widgets.Text;
-import org.eclipse.ui.IEditorInput;
-import org.eclipse.ui.IEditorSite;
-import org.simantics.Simantics;
-import org.simantics.browsing.ui.model.browsecontexts.BrowseContext;
-import org.simantics.charts.Activator;
-import org.simantics.charts.ITrendSupport;
-import org.simantics.charts.editor.ActiveRunQuery;
-import org.simantics.charts.editor.ChartData;
-import org.simantics.charts.editor.ChartKeyBindings;
-import org.simantics.charts.editor.ChartKeys;
-import org.simantics.charts.editor.ChartPasteHandler2;
-import org.simantics.charts.editor.HideItemsAction;
-import org.simantics.charts.editor.MoveHairlineAction;
-import org.simantics.charts.editor.PropertiesAction;
-import org.simantics.charts.editor.SendCommandAction;
-import org.simantics.charts.editor.SubscriptionDropParticipant;
-import org.simantics.charts.editor.TrackExperimentTimeAction;
-import org.simantics.charts.ontology.ChartResource;
-import org.simantics.charts.preference.ChartPreferences;
-import org.simantics.charts.query.FindChartItemForTrendItem;
-import org.simantics.charts.query.MilestoneSpecQuery;
-import org.simantics.charts.query.SetProperty;
-import org.simantics.charts.query.TrendSpecQuery;
-import org.simantics.charts.ui.ChartLinkData;
-import org.simantics.databoard.Bindings;
-import org.simantics.databoard.util.ObjectUtils;
-import org.simantics.db.AsyncReadGraph;
-import org.simantics.db.ReadGraph;
-import org.simantics.db.Resource;
-import org.simantics.db.Session;
-import org.simantics.db.common.request.ParametrizedRead;
-import org.simantics.db.common.request.UniqueRead;
-import org.simantics.db.exception.DatabaseException;
-import org.simantics.db.layer0.request.combinations.Combinators;
-import org.simantics.db.layer0.variable.RVI;
-import org.simantics.db.layer0.variable.RVIBuilder;
-import org.simantics.db.layer0.variable.Variable;
-import org.simantics.db.layer0.variable.Variables;
-import org.simantics.db.layer0.variable.Variables.Role;
-import org.simantics.db.procedure.AsyncListener;
-import org.simantics.db.procedure.SyncListener;
-import org.simantics.diagram.participant.SGFocusParticipant;
-import org.simantics.diagram.participant.e4.ContextUtil;
-import org.simantics.g2d.canvas.ICanvasContext;
-import org.simantics.g2d.canvas.impl.CanvasContext;
-import org.simantics.g2d.chassis.AWTChassis;
-import org.simantics.g2d.chassis.ICanvasChassis;
-import org.simantics.g2d.chassis.IChassisListener;
-import org.simantics.g2d.chassis.SWTChassis;
-import org.simantics.g2d.participant.KeyToCommand;
-import org.simantics.g2d.participant.TimeParticipant;
-import org.simantics.g2d.utils.CanvasUtils;
-import org.simantics.history.Collector;
-import org.simantics.history.HistoryManager;
-import org.simantics.history.impl.FileHistory;
-import org.simantics.project.IProject;
-import org.simantics.scenegraph.INode;
-import org.simantics.scenegraph.g2d.events.Event;
-import org.simantics.scenegraph.g2d.events.EventTypes;
-import org.simantics.scenegraph.g2d.events.IEventHandler;
-import org.simantics.scenegraph.g2d.events.MouseEvent;
-import org.simantics.scenegraph.g2d.events.MouseEvent.MouseButtonReleasedEvent;
-import org.simantics.scenegraph.g2d.events.command.Commands;
-import org.simantics.selectionview.StandardPropertyPage;
-import org.simantics.simulation.data.Datasource;
-import org.simantics.simulation.experiment.ExperimentState;
-import org.simantics.simulation.experiment.IExperiment;
-import org.simantics.simulation.experiment.IExperimentListener;
-import org.simantics.simulation.ontology.SimulationResource;
-import org.simantics.trend.TrendInitializer;
-import org.simantics.trend.TrendInitializer.StepListener;
-import org.simantics.trend.configuration.ItemPlacement;
-import org.simantics.trend.configuration.LineQuality;
-import org.simantics.trend.configuration.TimeFormat;
-import org.simantics.trend.configuration.TrendSpec;
-import org.simantics.trend.impl.HorizRuler;
-import org.simantics.trend.impl.ItemNode;
-import org.simantics.trend.impl.MilestoneSpec;
-import org.simantics.trend.impl.TrendNode;
-import org.simantics.trend.impl.TrendParticipant;
-import org.simantics.ui.workbench.IPropertyPage;
-import org.simantics.ui.workbench.IResourceEditorInput;
-import org.simantics.ui.workbench.ResourceEditorInput;
-import org.simantics.ui.workbench.action.PerformDefaultAction;
-import org.simantics.ui.workbench.e4.E4ResourceEditorBase;
-import org.simantics.ui.workbench.editor.input.InputValidationCombinators;
-import org.simantics.utils.datastructures.hints.HintListenerAdapter;
-import org.simantics.utils.datastructures.hints.IHintContext;
-import org.simantics.utils.datastructures.hints.IHintContext.Key;
-import org.simantics.utils.datastructures.hints.IHintObservable;
-import org.simantics.utils.format.ValueFormat;
-import org.simantics.utils.threads.AWTThread;
-import org.simantics.utils.threads.IThreadWorkQueue;
-import org.simantics.utils.threads.SWTThread;
-import org.simantics.utils.threads.ThreadUtils;
-import org.simantics.utils.ui.BundleUtils;
-import org.simantics.utils.ui.ErrorLogger;
-import org.simantics.utils.ui.ExceptionUtils;
-import org.simantics.utils.ui.SWTUtils;
-import org.simantics.utils.ui.dialogs.ShowMessage;
-import org.simantics.utils.ui.jface.ActiveSelectionProvider;
-
-/**
- * TimeSeriesEditor is an interactive part that draws a time series chart.
- * 
- * The configuration model is {@link TrendSpec} which is read through
- * {@link TrendSpecQuery}. In Simantics Environment the
- * editor input is {@link ResourceEditorInput}.
- * 
- * @author Toni Kalajainen <toni.kalajainen@vtt.fi>
- * @author Tuukka Lehtonen
- */
-public class TimeSeriesEditor extends E4ResourceEditorBase implements IAdaptable {
-
-    ParametrizedRead<IResourceEditorInput, Boolean> INPUT_VALIDATOR =
-        Combinators.compose(
-                InputValidationCombinators.hasURI(),
-                InputValidationCombinators.extractInputResource()
-        );
-
-    protected ParametrizedRead<IResourceEditorInput, Boolean> getInputValidator() {
-        return INPUT_VALIDATOR;
-    }
-
-    /**
-     * The root property browse context of the time series editor. A transitive
-     * closure is calculated for this context.
-     */
-    private static String       ROOT_PROPERTY_BROWSE_CONTEXT = ChartResource.URIs.ChartBrowseContext;
-
-    /**
-     * ID of the this editor part extension.
-     */
-    public static final String  ID                  = "org.simantics.charts.editor.timeseries";
-
-    public static final String CONTRIBUTION_URI     = "bundleclass://org.simantics.charts/org.simantics.charts.editor.e4.TimeSeriesEditor";
-
-    private static final String CONTEXT_MENU_ID     = "#timeSeriesChart";
-
-    private IEclipsePreferences chartPreferenceNode;
-
-    private final ImageDescriptor IMG_ZOOM_TO_FIT = BundleUtils.getImageDescriptorFromPlugin(Activator.PLUGIN_ID, "icons/horizAndVert16.png");
-    private final ImageDescriptor IMG_ZOOM_TO_FIT_HORIZ = BundleUtils.getImageDescriptorFromPlugin(Activator.PLUGIN_ID, "icons/horiz16.png");
-    private final ImageDescriptor IMG_ZOOM_TO_FIT_VERT = BundleUtils.getImageDescriptorFromPlugin(Activator.PLUGIN_ID, "icons/vert16.png");
-    private final ImageDescriptor IMG_AUTOSCALE = BundleUtils.getImageDescriptorFromPlugin(Activator.PLUGIN_ID, "icons/autoscale16.png");
-
-    IPreferenceChangeListener preferenceListener = new IPreferenceChangeListener() {
-
-        @Override
-        public void preferenceChange(PreferenceChangeEvent event) {
-            if (isDisposed()) {
-                System.err.println("Warning: pref change to disposed TimeSeriesEditor");
-                return;
-            }
-
-            if ( event.getKey().equals(ChartPreferences.P_REDRAW_INTERVAL ) || 
-                    event.getKey().equals(ChartPreferences.P_AUTOSCALE_INTERVAL )) {
-                long redraw_interval = chartPreferenceNode.getLong(ChartPreferences.P_REDRAW_INTERVAL, ChartPreferences.DEFAULT_REDRAW_INTERVAL);
-                long autoscale_interval = chartPreferenceNode.getLong(ChartPreferences.P_AUTOSCALE_INTERVAL, ChartPreferences.DEFAULT_AUTOSCALE_INTERVAL);
-                setInterval( redraw_interval, autoscale_interval );
-            }
-            if ( event.getKey().equals(ChartPreferences.P_DRAW_SAMPLES )) {
-                boolean draw_samples = chartPreferenceNode.getBoolean(ChartPreferences.P_DRAW_SAMPLES, ChartPreferences.DEFAULT_DRAW_SAMPLES);
-                setDrawSamples( draw_samples );
-            }
-            if ( event.getKey().equals(ChartPreferences.P_TIMEFORMAT ) ) {
-                String s = chartPreferenceNode.get(ChartPreferences.P_TIMEFORMAT, ChartPreferences.DEFAULT_TIMEFORMAT);
-                TimeFormat tf = TimeFormat.valueOf( s );
-                if (tf!=null) setTimeFormat( tf );
-            }
-            if ( event.getKey().equals(ChartPreferences.P_VALUEFORMAT ) ) {
-                String s = chartPreferenceNode.get(ChartPreferences.P_VALUEFORMAT, ChartPreferences.DEFAULT_VALUEFORMAT);
-                ValueFormat vf = ValueFormat.valueOf( s );
-                if (vf!=null) setValueFormat( vf );
-            }
-            if ( event.getKey().equals(ChartPreferences.P_ITEMPLACEMENT)) {
-                String s = chartPreferenceNode.get(ChartPreferences.P_ITEMPLACEMENT, ChartPreferences.DEFAULT_ITEMPLACEMENT);
-                ItemPlacement ip = ItemPlacement.valueOf(s);
-                if (trendNode!=null) trendNode.itemPlacement = ip;
-            }
-            if ( event.getKey().equals(ChartPreferences.P_TEXTQUALITY) || event.getKey().equals(ChartPreferences.P_LINEQUALITY) ) {
-                String s1 = chartPreferenceNode.get(ChartPreferences.P_TEXTQUALITY, ChartPreferences.DEFAULT_TEXTQUALITY);
-                String s2 = chartPreferenceNode.get(ChartPreferences.P_LINEQUALITY, ChartPreferences.DEFAULT_LINEQUALITY);
-                LineQuality q1 = LineQuality.valueOf(s1);
-                LineQuality q2 = LineQuality.valueOf(s2);
-                if (trendNode!=null) trendNode.quality.textQuality = q1;
-                if (trendNode!=null) trendNode.quality.lineQuality = q2;
-            }
-            
-        }
-    };
-
-    /**
-     * The project which this editor is listening to for changes to
-     * {@link ChartKeys.ChartSourceKey keys}.
-     */
-    IProject                    project;
-
-    /**
-     * The text widget shown only if there is no IProject available at the time
-     * of editor part creation.
-     */
-    Text                        errorText;
-
-    /**
-     * A unique key for making DB requests chart editor specific without binding
-     * the requests to the editor object itself.
-     */
-    UUID                        uniqueChartEditorId = UUID.randomUUID();
-    Logger                      log;
-    Display                     display;
-    SWTChassis                  canvas;
-    CanvasContext               cvsCtx;
-    TrendParticipant            tp;
-    TrendNode                   trendNode;
-    StepListener                stepListener;
-    MilestoneSpecListener       milestoneListener;
-    MilestoneSpecQuery          milestoneQuery;
-
-    /**
-     * The ChartData instance used by this editor for sourcing data at any given
-     * moment. Project hint instances are copied into this instance.
-     */
-    final ChartData             chartData = new ChartData(null, null, null, null, null, null);
-
-    /**
-     * The ChartSourceKey to match the model this editor was opened for.
-     * @see #model
-     * @see #init(IEditorSite, IEditorInput)
-     */
-    ChartKeys.ChartSourceKey    chartDataKey;
-    
-    
-    /**
-     * Context management utils
-     */
-    protected IThreadWorkQueue           swt;
-    protected ContextUtil                contextUtil;
-
-    class ExperimentStateListener implements IExperimentListener {
-        @Override
-        public void stateChanged(ExperimentState state) {
-            TrendSpec spec = trendNode.getTrendSpec();
-            spec.experimentIsRunning = state == ExperimentState.RUNNING;
-            if (spec.experimentIsRunning && spec.viewProfile.trackExperimentTime) {
-                TrendParticipant t = tp;
-                if (t != null)
-                    t.setDirty();
-            }
-        }
-    }
-
-    ExperimentStateListener experimentStateListener = new ExperimentStateListener();
-
-    class ChartDataListener extends HintListenerAdapter implements Runnable {
-        @Override
-        public void hintChanged(IHintObservable sender, Key key, Object oldValue, Object newValue) {
-            if (key.equals(chartDataKey)) {
-                // @Thread any
-                if (!cvsCtx.isDisposed() && cvsCtx.isAlive()) {
-                    cvsCtx.getThreadAccess().asyncExec(this);
-                }
-            }
-        }
-        @Override
-        public void run() {
-            // @Thread AWT
-            if (cvsCtx.isDisposed() || !cvsCtx.isAlive()) return;
-            ChartData data = Simantics.getProject().getHint(chartDataKey);
-            setInput( data, trendNode.getTrendSpec() );
-        }
-    }
-
-    ChartDataListener chartDataListener = new ChartDataListener();
-
-    class ValueTipBoxPositionListener extends HintListenerAdapter {
-        @Override
-        public void hintChanged(IHintObservable sender, Key key, Object oldValue, Object newValue) {
-            if (key.equals(TrendParticipant.KEY_VALUE_TIP_BOX_RELATIVE_POS) && newValue != null) {
-                Session s = Simantics.getSession();
-                ChartResource CHART = s.getService(ChartResource.class);
-                Point2D p = (Point2D) newValue;
-                double[] value = { p.getX(), p.getY() };
-                s.asyncRequest(new SetProperty(getInputResource(), CHART.Chart_valueViewPosition, value, Bindings.DOUBLE_ARRAY));
-            }
-        }
-    }
-
-    ValueTipBoxPositionListener valueTipBoxPositionListener = new ValueTipBoxPositionListener();
-
-    
-    private void handleLinkDataChange() {
-        final ChartLinkData newData = linkData;
-        trendNode.autoscaletime = newData == null || newData.sender == TimeSeriesEditor.this;
-        
-        if ( newData == null || newData.sender==TimeSeriesEditor.this ) return;
-        TrendNode tn = trendNode;
-        HorizRuler hr = tn!=null ? tn.horizRuler : null;
-
-        ChartLinkData oldData = new ChartLinkData();
-        getFromEnd(oldData);
-
-        if ( hr != null && !ObjectUtils.objectEquals(tn.valueTipTime, newData.valueTipTime)) {
-            tn.valueTipTime = newData.valueTipTime;
-            tp.setDirty();
-        }
-        
-        if ( hr != null && (oldData.from!=newData.from || oldData.sx!=newData.sx)) {
-            
-            cvsCtx.getThreadAccess().asyncExec( new Runnable() {
-                @Override
-                public void run() {
-                    boolean b = trendNode.horizRuler.setFromScale(newData.from, newData.sx);
-                    trendNode.horizRuler.autoscroll = false;
-                    if (b) {
-                        trendNode.layout();
-                        tp.setDirty();
-                    }
-                }});
-        }
-    }
-    
-    // Link-Time
-//    State linkTimeState;
-//    IStateListener linkTimeStateListener = new IStateListener() {
-//             @Override
-//             public void handleStateChange(State state, Object oldValue) {
-//                     
-//                     
-//             }
-//    };
-    
-    HorizRuler.TimeWindowListener horizRulerListener = new HorizRuler.TimeWindowListener() {
-               @Override
-               public void onNewWindow(double from, double end, double scalex) {
-                       final ChartLinkData oldData = linkData;
-                       if (oldData != null) {
-                               ChartLinkData data = new ChartLinkData(TimeSeriesEditor.this, from, end, scalex);
-                               data.valueTipTime = trendNode.valueTipTime;
-                               linkData = data;
-                               handleLinkDataChange();
-                       }
-               }
-       };
-
-    class ChassisListener implements IChassisListener {
-        @Override
-        public void chassisClosed(ICanvasChassis sender) {
-            // Prevent deadlock while disposing which using syncExec would result in. 
-            final ICanvasContext ctx = cvsCtx;
-            ThreadUtils.asyncExec(ctx.getThreadAccess(), new Runnable() {
-                @Override
-                public void run() {
-                    if (ctx != null) {
-                        AWTChassis awt = canvas.getAWTComponent();
-                        if (awt != null)
-                            awt.setCanvasContext(null);
-                        ctx.dispose();
-                    }
-                }
-            });
-            canvas.removeChassisListener(ChassisListener.this);
-        }
-    }
-
-    ActiveSelectionProvider     selectionProvider   = new ActiveSelectionProvider();
-    MenuManager                 menuManager;
-
-    @Inject
-    EContextService contextService;
-
-    private ChartLinkData linkData;
-
-    public TimeSeriesEditor() {
-        log = Logger.getLogger( this.getClass().getName() );
-    }
-
-    @Override
-    public void initImpl(MPart part) {
-        EModelService modelService = getContext().get(EModelService.class);
-        MApplication app = getContext().get(MApplication.class);
-        MUIElement element = modelService.cloneSnippet(app, "org.simantics.charts.toolbar.default.snippet", null);
-        if (element != null) {
-            element.setVisible(true);
-            element.setToBeRendered(true);
-            part.setToolbar((MToolBar)element);
-        }
-        
-//        MBindingContext dialogAndWindowContext = findMAppElementById(app.getRootContext(), "org.eclipse.ui.contexts.dialogAndWindow");
-//        MBindingContext windowContext = findMAppElementById(dialogAndWindowContext.getChildren(), "org.eclipse.ui.contexts.window");
-//        MBindingContext chartContext = findMAppElementById(windowContext.getChildren(), "org.simantics.charts.editor.context");
-//        if (chartContext == null) {
-//            chartContext = MCommandsFactory.INSTANCE.createBindingContext();
-//            chartContext.setContributorURI("bundle://test.uri");
-//            chartContext.setElementId("org.simantics.charts.editor.context");
-//            chartContext.setName("In Chart Editor");
-//            chartContext.setDescription("Chart Editor Binding Context");
-//            windowContext.getChildren().add(chartContext);
-//        }
-//        MBindingTable chartBindingTable = findMAppElementById(app.getBindingTables(), "org.simantics.charts.editor.context");
-//        if (chartBindingTable == null) {
-//            chartBindingTable = MCommandsFactory.INSTANCE.createBindingTable();
-//            chartBindingTable.setContributorURI("bundle://test.test.uri");
-//            chartBindingTable.setElementId("org.simantics.charts.editor.context");
-//            chartBindingTable.setBindingContext(chartContext);
-//            
-//            for (MCommand command : app.getCommands()) {
-//                if (command.getElementId().equals("org.simantics.chart.canvasCommandDelegateCommand")) {
-//                    
-//                    MParameter param = MCommandsFactory.INSTANCE.createParameter();
-//                    param.setElementId("id");
-//                    param.setName("org.simantics.charts.commandparameter.canvasCommandDelegateParameter");
-//                    param.setValue("zoomToFit");
-//                    MKeyBinding keyBinding = MCommandsFactory.INSTANCE.createKeyBinding();
-//                    keyBinding.setKeySequence("1");
-//                    keyBinding.setCommand(command);
-//                    keyBinding.getParameters().add(param);
-//                    chartBindingTable.getBindings().add(keyBinding);
-//                    
-//                    break;
-//                }
-//            }
-//            
-//            app.getBindingTables().add(chartBindingTable);
-//        }
-//        if (!part.getBindingContexts().contains(chartContext))
-//            part.getBindingContexts().add(chartContext);
-    }
-
-    private <T extends MApplicationElement> T findMAppElementById(List<T> appElements, String elementId) {
-        for (T element : appElements) {
-            if (element.getElementId() != null && element.getElementId().equals(elementId)) {
-                return element;
-            }
-        }
-        return null;
-    }
-
-
-    /**
-     * Invoke this only from the AWT thread.
-     * @param context
-     */
-    protected void setCanvasContext(final SWTChassis chassis, final ICanvasContext context) {
-        // Cannot directly invoke SWTChassis.setCanvasContext only because it
-        // needs to be invoked in the SWT thread and AWTChassis.setCanvasContext in the
-        // AWT thread, but directly invoking SWTChassis.setCanvasContext would call both
-        // in the SWT thread which would cause synchronous scheduling of AWT
-        // runnables which is always a potential source of deadlocks.
-        chassis.getAWTComponent().setCanvasContext(context);
-        SWTUtils.asyncExec(chassis, new Runnable() {
-            @Override
-            public void run() {
-                if (!chassis.isDisposed())
-                    // For AWT, this is a no-operation.
-                    chassis.setCanvasContext(context);
-            }
-        });
-    }
-
-    @Override
-    public void createPartControl(Composite parent) {
-        this.chartDataKey = ChartKeys.chartSourceKey(assertInputModelResource());
-        display = parent.getDisplay();
-        swt = SWTThread.getThreadAccess(display);
-
-        // Must have a project to attach to, otherwise the editor is useless.
-        project = Simantics.peekProject();
-        if (project == null) {
-            errorText = new Text(parent, SWT.NONE);
-            errorText.setText("No project is open.");
-            errorText.setEditable(false);
-            return;
-        }
-
-        // Create the canvas context here before finishing createPartControl
-        // to give anybody requiring access to this editor's ICanvasContext
-        // a chance to do their work.
-        // The context can be created in SWT thread without scheduling
-        // to the context thread and having potential deadlocks.
-        // The context is locked here and unlocked after it has been
-        // initialized in the AWT thread.
-        IThreadWorkQueue thread = AWTThread.getThreadAccess();
-        cvsCtx = new CanvasContext(thread);
-        cvsCtx.setLocked(true);
-
-        canvas = new SWTChassis(parent, SWT.NONE);
-        canvas.populate(parameter -> {
-            if (!isDisposed()) {
-                canvas.addChassisListener(new ChassisListener());
-                initializeCanvas(canvas, cvsCtx);
-            }
-        });
-
-        // Link time
-//        ICommandService service = (ICommandService) PlatformUI.getWorkbench().getService(ICommandService.class);
-//        Command command = service.getCommand( LinkTimeHandler.COMMAND_ID );
-//        linkTimeState = command.getState( LinkTimeHandler.STATE_ID );
-//        if ( linkTimeState != null ) linkTimeState.addListener( linkTimeStateListener );
-        
-        // TODO: Link time E4 way!
-
-        
-        linkData = new ChartLinkData();
-        
-        addPopupMenu();
-        
-        // Start tracking editor input validity. 
-        activateValidation();
-
-        // Provide input as selection for property page.
-//        selectionProvider.setSelection( new StructuredSelection(getInputResource()) );
-        
-        getSelectionService().setSelection(new StructuredSelection(getInputResource()));
-        getSelectionService().addSelectionListener(getPart().getElementId(), new ISelectionListener() {
-            
-            @Override
-            public void selectionChanged(MPart part, Object selection) {
-                System.out.println("selection changed!");
-            }
-        });
-        
-//        getSite().setSelectionProvider( selectionProvider );
-    }
-
-    protected void initializeCanvas(final SWTChassis chassis, CanvasContext cvsCtx) {
-        // Initialize canvas context
-        TrendSpec nodata = new TrendSpec();
-        nodata.init();
-        cvsCtx = TrendInitializer.defaultInitializeCanvas(cvsCtx, null, null, null, nodata);
-
-        tp = cvsCtx.getAtMostOneItemOfClass(TrendParticipant.class);
-
-        
-//        IContextService contextService = (IContextService) getSite().getService(IContextService.class);
-        contextUtil = new ContextUtil(contextService, swt);
-        
-        
-        cvsCtx.add( new SubscriptionDropParticipant( getInputResource() ) );
-        cvsCtx.add( new KeyToCommand( ChartKeyBindings.DEFAULT_BINDINGS ) );
-        cvsCtx.add( new ChartPasteHandler2(getInputResource()) );
-        cvsCtx.add(contextUtil);
-        
-        // Context management
-        cvsCtx.add(new SGFocusParticipant(canvas, "org.simantics.charts.editor.context"));
-
-        stepListener = new StepListener( tp );
-        trendNode = tp.getTrend();
-        trendNode.titleNode.remove();
-        trendNode.titleNode = null;
-
-        // Link time
-        trendNode.horizRuler.listener = horizRulerListener;
-        
-        final ChartLinkData linkTime = linkData;
-        if (linkTime!=null) trendNode.horizRuler.setFromEnd(linkTime.from, linkTime.sx);
-        
-        // Handle mouse moved event after TrendParticipant.
-        // This handler forwards trend.mouseHoverTime to linkTimeState
-        cvsCtx.getEventHandlerStack().add( new IEventHandler() {
-
-                       @Override
-                       public int getEventMask() {
-                               return EventTypes.MouseMovedMask | EventTypes.MouseClickMask | EventTypes.CommandMask | EventTypes.KeyPressed;
-                       }
-
-                       @Override
-                       public boolean handleEvent(Event e) {
-
-//                             System.out.println("LinkEventHandler: "+e);
-                               ChartLinkData oldData = linkData;
-                               if (oldData!=null) {
-                                       ChartLinkData newData = new ChartLinkData();
-                                       getFromEnd(newData);
-                                       if (!newData.equals(oldData)) {
-//                                             System.out.println("Sending new link-data");
-                                               linkData = newData;
-                                               handleLinkDataChange();
-                                       }
-                               }
-                               return false;
-                       }}, -1);
-
-        // Canvas context is initialized, unlock it now to allow rendering.
-        cvsCtx.setLocked(false);
-
-        setCanvasContext(chassis, cvsCtx);
-
-        cvsCtx.getEventHandlerStack().add(new IEventHandler() {
-            @Override
-            public boolean handleEvent(Event e) {
-                MouseButtonReleasedEvent event = (MouseButtonReleasedEvent) e;
-                if (event.button != MouseEvent.RIGHT_BUTTON)
-                    return false;
-
-                final Point p = new Point((int) event.screenPosition.getX(), (int) event.screenPosition.getY());
-                SWTUtils.asyncExec(chassis, new Runnable() {
-                    @Override
-                    public void run() {
-                        if (!canvas.isDisposed())
-                            showPopup(p);
-                    }
-                });
-                return true;
-            }
-            @Override
-            public int getEventMask() {
-                return EventTypes.MouseButtonReleasedMask;
-            }
-        }, 1000000);
-
-        // Track data source and preinitialize chartData
-        project.addHintListener(chartDataListener);
-        chartData.readFrom( (ChartData) project.getHint( chartDataKey ) );
-
-        if (chartData.run != null) {
-            milestoneListener = new MilestoneSpecListener();
-            milestoneQuery = new MilestoneSpecQuery( chartData.run );
-            Simantics.getSession().asyncRequest( milestoneQuery, milestoneListener );
-        }
-
-        // IMPORTANT: Only after preinitializing chartData, start tracking chart configuration
-        trackChartConfiguration();
-        trackPreferences();
-
-        // Write changes to TrendSpec.viewProfile.valueViewPosition[XY]
-        // back to the graph database.
-        cvsCtx.getHintStack().addHintListener(valueTipBoxPositionListener);
-    }
-
-    private void addPopupMenu() {
-
-//         #TODO: FINISH THIS WHEN E4 WORKBENCH
-//         MPopupMenu popupMenu = MMenuFactory.INSTANCE.createPopupMenu();
-//         popupMenu.setLabel("Time Series Editor");
-//         popupMenu.setElementId(CONTEXT_MENU_ID);
-//         
-//        for (MCommand command : getPart().getContext().get(MApplication.class).getCommands()) {
-//            if (command.getElementId().equals("org.simantics.chart.canvasCommandDelegateCommand")) {
-//                MParameter param = MCommandsFactory.INSTANCE.createParameter();
-//                param.setElementId("id");
-//                param.setName("org.simantics.charts.commandparameter.canvasCommandDelegateParameter");
-//                param.setValue("zoomToFit");
-//                MHandledMenuItem mmenuItem = MMenuFactory.INSTANCE.createHandledMenuItem();
-//                mmenuItem.setLabel("Zoom to Fit");
-//                mmenuItem.setCommand(command);
-//                mmenuItem.getParameters().add(param);
-//            }
-//        }
-//         
-//         getPart().getContext().get(EMenuService.class).registerContextMenu(canvas, CONTEXT_MENU_ID);
-           
-           
-           
-        menuManager = new MenuManager("Time Series Editor", CONTEXT_MENU_ID);
-        menuManager.setRemoveAllWhenShown(true);
-        Menu menu = menuManager.createContextMenu(canvas);
-        canvas.setMenu(menu);
-        // TODO: link to modeled menu in E4 way
-//        getEditorSite().registerContextMenu(menuManager.getId(), menuManager, selectionProvider);
-
-        // Add support for some built-in actions in the context menu.
-        menuManager.addMenuListener(new IMenuListener() {
-            @Override
-            public void menuAboutToShow(IMenuManager manager) {
-                // Not initialized yet, prevent NPE.
-                TrendNode trendNode = TimeSeriesEditor.this.trendNode;
-                TrendParticipant tp = TimeSeriesEditor.this.tp;
-                if (trendNode == null || tp == null)
-                    return;
-
-                TrendSpec trendSpec = trendNode.getTrendSpec();
-                ItemNode hoverItem = tp.hoveringItem;
-                if (hoverItem != null && hoverItem.item != null) {
-                    Resource component = resolveReferencedComponent(hoverItem.item.variableId);
-                    if (component != null) {
-                        manager.add(new PerformDefaultAction("Show Referenced Component", canvas, component));
-                    }
-
-                    Resource chart = TimeSeriesEditor.this.getInputResource();
-                    if ( chart != null ) {
-                        try {
-                            Resource chartItem = Simantics.getSession().sync( new FindChartItemForTrendItem(chart, hoverItem.item) );
-                            if (chartItem != null) {
-                                manager.add(new HideItemsAction("Hide Item", true, Collections.singletonList(chartItem)));
-                                manager.add(new Separator());
-                                manager.add(new PropertiesAction("Item Properties", canvas, chartItem));
-                                manager.add(new PropertiesAction("Chart Properties", canvas, chart));
-                            }
-                        } catch (DatabaseException e) {
-                            Activator.getDefault().getLog().log(new Status(IStatus.ERROR, Activator.PLUGIN_ID, "Failed to resolve context menu items.", e));
-                        }
-                    }
-                } else {
-                    boolean hairlineMovementAllowed =
-                            !(trendSpec.experimentIsRunning &&
-                              trendSpec.viewProfile.trackExperimentTime);
-
-                    Resource chart = TimeSeriesEditor.this.getInputResource();
-                    manager.add(new Separator());
-                    manager.add(new MoveHairlineAction(
-                            "Move Hairline Here",
-                            chart,
-                            hairlineMovementAllowed,
-                            trendNode,
-                            trendNode.mouseHoverTime
-                            ));
-                    manager.add(new MoveHairlineAction(
-                            "Move Hairline To Current Time",
-                            chart,
-                            hairlineMovementAllowed,
-                            trendNode,
-                            trendNode.horizRuler.getItemEndTime(),
-                            Boolean.FALSE
-                            ));
-                    manager.add(new TrackExperimentTimeAction(
-                            "Hairline Tracks Current Time",
-                            chart,
-                            trendSpec.viewProfile.trackExperimentTime));
-                    manager.add(new Separator());
-                    manager.add(new SendCommandAction("Zoom to Fit", IMG_ZOOM_TO_FIT, cvsCtx, Commands.ZOOM_TO_FIT));
-                    manager.add(new SendCommandAction("Zoom to Fit Horizontally", IMG_ZOOM_TO_FIT_HORIZ, cvsCtx, Commands.ZOOM_TO_FIT_HORIZ));
-                    manager.add(new SendCommandAction("Zoom to Fit Vertically", IMG_ZOOM_TO_FIT_VERT, cvsCtx, Commands.ZOOM_TO_FIT_VERT));
-                    manager.add(new SendCommandAction("Autoscale Chart", IMG_AUTOSCALE, cvsCtx, Commands.AUTOSCALE));
-                    manager.add(new Separator());
-                    manager.add(new PropertiesAction("Chart Properties", canvas, chart));
-                }
-            }
-        });
-    }
-
-    protected Resource resolveReferencedComponent(final String variableId) {
-        try {
-            return Simantics.getSession().sync(new UniqueRead<Resource>() {
-                @Override
-                public Resource perform(ReadGraph graph) throws DatabaseException {
-                    Variable configuration = Variables.getConfigurationContext(graph, getInputResource());
-                    RVI rvi = RVI.fromResourceFormat(graph, variableId);
-                    rvi = new RVIBuilder(rvi).removeFromFirstRole(Role.PROPERTY).toRVI();
-                    if (rvi.isEmpty())
-                        return null;
-                    Variable var = rvi.resolve(graph, configuration);
-                    return var.getPossibleRepresents(graph);
-                }
-            });
-        } catch (DatabaseException e) {
-            ErrorLogger.defaultLogError(e);
-        }
-        return null;
-    }
-
-    private void showPopup(Point p) {
-        menuManager.getMenu().setLocation(p);
-        menuManager.getMenu().setVisible(true);
-    }
-
-    private void trackChartConfiguration() {
-        Simantics.getSession().asyncRequest(new TrendSpecQuery( uniqueChartEditorId, getInputResource() ), new TrendSpecListener());
-        Simantics.getSession().asyncRequest(new ActiveRunQuery( uniqueChartEditorId, getInputResource() ), new ActiveRunListener());
-    }
-
-    @Override
-    public void setFocus() {
-        if (errorText != null)
-            errorText.setFocus();
-        else
-            canvas.setFocus();
-    }
-
-    @Override
-    public void dispose() {
-        if (isDisposed() == true) return;
-
-        if (trendNode!=null && trendNode.horizRuler!=null) {
-            trendNode.horizRuler.listener = null;
-        }
-
-//        if ( linkTimeState != null ) linkTimeState.removeListener( linkTimeStateListener );
-
-        canvas.getHintContext().removeHint( SWTChassis.KEY_EDITORPART );
-        canvas.getHintContext().removeHint( SWTChassis.KEY_WORKBENCHPAGE );
-        canvas.getHintContext().removeHint( SWTChassis.KEY_WORKBENCHWINDOW );
-
-        if ( chartPreferenceNode!= null ) {
-            chartPreferenceNode.removePreferenceChangeListener( preferenceListener );
-        }
-
-        MilestoneSpecListener ml = milestoneListener;
-        if (ml!=null) ml.dispose();
-
-        if (project != null) {
-            project.removeHintListener(chartDataListener);
-        }
-
-        if (chartData != null) {
-            if (chartData.datasource!=null)
-                chartData.datasource.removeListener( stepListener );
-            if (chartData.experiment!=null)
-                chartData.experiment.removeListener( experimentStateListener );
-            chartData.readFrom( null );
-        }
-    }
-
-    /**
-     * @param data new data or null
-     * @param newSpec new spec or null
-     * @thread AWT
-     */
-    @SuppressWarnings("unused")
-    public void setInput(ChartData data, TrendSpec newSpec) {
-       boolean doLayout = false;
-
-       // Disregard input if it is not for this chart's containing model.
-       if (data != null && data.model != null && !data.model.equals(assertInputModelResource()))
-           data = null;
-
-       // Accommodate Datasource changes
-       Datasource: {
-               Datasource oldDatasource = chartData==null?null:chartData.datasource;
-               Datasource newDatasource = data==null?null:data.datasource;
-               //if ( !ObjectUtils.objectEquals(oldDatasource, newDatasource) ) 
-               {
-                       if (oldDatasource!=null) oldDatasource.removeListener( stepListener );
-                       if (newDatasource!=null) newDatasource.addListener( stepListener );
-               }
-       }
-
-        Experiment: {
-            IExperiment oldExperiment = chartData==null?null:chartData.experiment;
-            IExperiment newExperiment = data==null?null:data.experiment;
-            //if ( !ObjectUtils.objectEquals(oldExperiment, newExperiment) ) 
-            {
-                if (oldExperiment!=null) oldExperiment.removeListener( experimentStateListener );
-                if (newExperiment!=null) newExperiment.addListener( experimentStateListener );
-            }
-        }
-
-       // Accommodate Historian changes
-       Historian: {
-               HistoryManager oldHistorian = trendNode.historian==null?null:trendNode.historian;
-               HistoryManager newHistorian = data==null?null:data.history;
-               Collector newCollector = data==null?null:data.collector;
-       //      if ( !ObjectUtils.objectEquals(oldHistorian, newHistorian) ) 
-               {
-                       if (newHistorian instanceof FileHistory) {
-                               FileHistory fh = (FileHistory) newHistorian;
-                               System.out.println("History = "+fh.getWorkarea());
-                       }
-                       trendNode.setHistorian( newHistorian, newCollector );
-                       doLayout |= trendNode.autoscale(true, true) | !ObjectUtils.objectEquals(oldHistorian, newHistorian);
-               }
-
-               // Accommodate TrendSpec changes
-               TrendSpec oldSpec = trendNode.getTrendSpec();
-               if ( !newSpec.equals(oldSpec) ) {
-                   trendNode.setTrendSpec( newSpec==null?TrendSpec.EMPTY:newSpec );
-                   doLayout = true;
-               }
-               
-       }
-
-        Resource newExperimentResource = data==null ? null : data.run;
-        Resource oldExperimentResource = this.chartData == null ? null : this.chartData.run;
-       
-        // Track milestones
-        Milestones: {
-               if (!ObjectUtils.objectEquals(oldExperimentResource, newExperimentResource)) {
-
-                       // Dispose old listener & Query
-                       if (milestoneListener!=null) {
-                               milestoneListener.dispose();
-                               milestoneListener = null;
-                       }
-                       if (milestoneQuery!=null) {
-                               milestoneQuery = null;
-                       }
-
-                       trendNode.setMilestones( MilestoneSpec.EMPTY );
-                       
-                       if (newExperimentResource != null) {
-                               milestoneListener = new MilestoneSpecListener();
-                               milestoneQuery = new MilestoneSpecQuery( newExperimentResource );
-                               Simantics.getSession().asyncRequest( milestoneQuery, milestoneListener );
-                       }
-               }
-               
-        }
-
-        if (doLayout) trendNode.layout();
-        this.chartData.readFrom( data );
-        tp.setDirty();
-        
-        if (!ObjectUtils.objectEquals(oldExperimentResource, newExperimentResource)) {
-               resetViewAfterDataChange();
-        }
-        
-    }
-
-    class ActiveRunListener implements SyncListener<Resource> {
-        @Override
-        public void exception(ReadGraph graph, Throwable throwable) {
-            ErrorLogger.defaultLogError(throwable);
-            ShowMessage.showError(throwable.getClass().getSimpleName(), throwable.getMessage());
-        }
-        @Override
-        public void execute(ReadGraph graph, final Resource run) throws DatabaseException {
-               if(run != null) {
-                       SimulationResource SIMU = SimulationResource.getInstance(graph);
-                       Variable var = Variables.getVariable(graph, run);
-                       IExperiment exp = var.getPossiblePropertyValue(graph, SIMU.Run_iExperiment);
-                       ITrendSupport ts = exp.getService(ITrendSupport.class);
-                       ts.setChartData(graph);
-               }
-        }
-        @Override
-        public boolean isDisposed() {
-            return TimeSeriesEditor.this.isDisposed();
-        }
-    }
-    
-    class TrendSpecListener implements AsyncListener<TrendSpec> {
-        @Override
-        public void exception(AsyncReadGraph graph, Throwable throwable) {
-               
-            ErrorLogger.defaultLogError(throwable);
-            ShowMessage.showError(throwable.getClass().getSimpleName(), throwable.getMessage());
-        }
-        @Override
-        public void execute(AsyncReadGraph graph, final TrendSpec result) {
-            if (result == null) {
-                log.log(Level.INFO, "Chart configuration removed");
-            } else {
-                log.log(Level.INFO, "Chart configuration updated: " + result);
-            }
-
-            // Reload chart in AWT Thread
-            AWTThread.getThreadAccess().asyncExec(new Runnable() {
-                @Override
-                public void run() {
-                    if (!isDisposed())
-                        setInput( chartData, result );
-                }
-            });
-        }
-        @Override
-        public boolean isDisposed() {
-            return TimeSeriesEditor.this.isDisposed();
-        }
-    }
-    
-    class MilestoneSpecListener implements AsyncListener<MilestoneSpec> {
-       boolean disposed = false;
-               @Override
-               public void execute(AsyncReadGraph graph, final MilestoneSpec result) {
-                       AWTThread.INSTANCE.asyncExec(new Runnable() {
-                               public void run() {
-                                       trendNode.setMilestones(result);
-                               }});
-               }
-
-               @Override
-               public void exception(AsyncReadGraph graph, Throwable throwable) {
-                       
-               }
-
-               @Override
-               public boolean isDisposed() {
-                       return disposed;
-               }
-               
-               public void dispose() {
-                       disposed = true;
-               }
-       
-    }
-
-    private void trackPreferences() {
-        chartPreferenceNode = InstanceScope.INSTANCE.getNode( "org.simantics.charts" );
-        chartPreferenceNode.addPreferenceChangeListener( preferenceListener );
-        long redrawInterval = chartPreferenceNode.getLong(ChartPreferences.P_REDRAW_INTERVAL, ChartPreferences.DEFAULT_REDRAW_INTERVAL);
-        long autoscaleInterval = chartPreferenceNode.getLong(ChartPreferences.P_AUTOSCALE_INTERVAL, ChartPreferences.DEFAULT_AUTOSCALE_INTERVAL);
-        setInterval(redrawInterval, autoscaleInterval);
-        
-        String timeFormat = chartPreferenceNode.get(ChartPreferences.P_TIMEFORMAT, ChartPreferences.DEFAULT_TIMEFORMAT); 
-        TimeFormat tf = TimeFormat.valueOf( timeFormat );
-        if (tf!=null) setTimeFormat( tf );
-        
-        Boolean drawSamples = chartPreferenceNode.getBoolean(ChartPreferences.P_DRAW_SAMPLES, ChartPreferences.DEFAULT_DRAW_SAMPLES);
-        setDrawSamples(drawSamples);
-
-        String valueFormat = chartPreferenceNode.get(ChartPreferences.P_VALUEFORMAT, ChartPreferences.DEFAULT_VALUEFORMAT);
-        ValueFormat vf = ValueFormat.valueOf( valueFormat );
-        if (vf!=null) setValueFormat( vf );
-        
-       String s = chartPreferenceNode.get(ChartPreferences.P_ITEMPLACEMENT, ChartPreferences.DEFAULT_ITEMPLACEMENT);
-       ItemPlacement ip = ItemPlacement.valueOf(s);
-       if (trendNode!=null) trendNode.itemPlacement = ip;
-       
-        String s1 = chartPreferenceNode.get(ChartPreferences.P_TEXTQUALITY, ChartPreferences.DEFAULT_TEXTQUALITY);
-        String s2 = chartPreferenceNode.get(ChartPreferences.P_LINEQUALITY, ChartPreferences.DEFAULT_LINEQUALITY);
-        LineQuality q1 = LineQuality.valueOf(s1);
-        LineQuality q2 = LineQuality.valueOf(s2);
-        if (trendNode!=null) trendNode.quality.textQuality = q1;
-        if (trendNode!=null) trendNode.quality.lineQuality = q2;
-       
-    }
-
-    private void setInterval(long redrawInterval, long autoscaleInterval) {
-        redrawInterval = Math.max(1, redrawInterval);
-        long pulse = Math.min(50, redrawInterval);
-        pulse = Math.min(pulse, autoscaleInterval);
-        IHintContext h = canvas.getCanvasContext().getDefaultHintContext();
-        h.setHint(TimeParticipant.KEY_TIME_PULSE_INTERVAL, pulse);
-        h.setHint(TrendParticipant.KEY_TREND_DRAW_INTERVAL, redrawInterval);
-        h.setHint(TrendParticipant.KEY_TREND_AUTOSCALE_INTERVAL, autoscaleInterval);        
-    }
-
-    private void setDrawSamples(boolean value) {
-       trendNode.drawSamples = value;
-       trendNode.layout();
-       tp.setDirty();
-    }
-
-    private void setTimeFormat( TimeFormat tf ) {
-       if (trendNode.timeFormat == tf) return;
-       trendNode.timeFormat = tf;
-       trendNode.layout();
-        tp.setDirty();
-    }
-
-    private void setValueFormat( ValueFormat vf ) {
-       if (trendNode.valueFormat == vf) return;
-       trendNode.valueFormat = vf;
-       trendNode.layout();
-        tp.setDirty();
-    }
-
-    @SuppressWarnings("unchecked")
-    @Override
-    public <T> T getAdapter(Class<T> adapter) {
-        if (adapter == INode.class) {
-            ICanvasContext ctx = cvsCtx;
-            if (ctx != null)
-                return (T) ctx.getSceneGraph();
-        }
-        if (adapter == IPropertyPage.class)
-            // TODO: pure e4 property page without sites
-            return (T) new StandardPropertyPage(null, getPropertyPageContexts());
-        if (adapter == ICanvasContext.class)
-            return (T) cvsCtx;
-        return null;
-    }
-
-    protected Set<String> getPropertyPageContexts() {
-        try {
-            return BrowseContext.getBrowseContextClosure(Simantics.getSession(), Collections.singleton(ROOT_PROPERTY_BROWSE_CONTEXT));
-        } catch (DatabaseException e) {
-            ExceptionUtils.logAndShowError("Failed to load modeled browse contexts for property page, see exception for details.", e);
-            return Collections.singleton(ROOT_PROPERTY_BROWSE_CONTEXT);
-        }
-    }
-
-    /**
-     * Add from, end, (scale x) to argument array
-     * @param fromEnd array of 2 or 3
-     */
-    public void getFromEnd(ChartLinkData data) {
-       data.sender = this;
-       TrendNode tn = trendNode;
-       data.valueTipTime = tn.valueTipTime;
-       HorizRuler hr = tn!=null ? tn.horizRuler : null;
-       if ( hr != null ) {
-               data.from = hr.from;
-               data.end = hr.end;
-                       double len = hr.end-hr.from; 
-                       double wid = tn.plot.getWidth();
-                       if ( wid==0.0 ) wid = 0.1;
-                       data.sx = len/wid;
-       }
-    }
-
-    @SuppressWarnings("unused")
-    private static boolean doubleEquals(double a, double b) {
-       if (Double.isNaN(a) && Double.isNaN(b)) return true;
-       return a==b;
-    }
-
-    protected void resetViewAfterDataChange() {
-       
-       CanvasUtils.sendCommand(cvsCtx, Commands.CANCEL);
-       CanvasUtils.sendCommand(cvsCtx, Commands.AUTOSCALE);
-       
-    }
-
-//    private Resource getInputResource() {
-//        return resource;
-//    }
-}