From 4fc32fefc5bd05dd9f41e879fce4cfe2e601af71 Mon Sep 17 00:00:00 2001 From: Tuukka Lehtonen Date: Thu, 18 Oct 2018 13:49:37 +0300 Subject: [PATCH 1/1] Fixed context menu popup location for HiDPI displays with display zoom gitlab #152 Change-Id: If7ca761cf305daa4ecfd837af75c93eae285df1a --- .../org.simantics.charts/META-INF/MANIFEST.MF | 1 - .../charts/editor/TimeSeriesEditor.java | 14 +- .../charts/editor/e4/ChartCopyHandler.java | 222 ---- .../charts/editor/e4/MoveHairlineHandler.java | 81 -- .../charts/editor/e4/TimeSeriesEditor.java | 1148 ----------------- 5 files changed, 7 insertions(+), 1459 deletions(-) delete mode 100644 bundles/org.simantics.charts/src/org/simantics/charts/editor/e4/ChartCopyHandler.java delete mode 100644 bundles/org.simantics.charts/src/org/simantics/charts/editor/e4/MoveHairlineHandler.java delete mode 100644 bundles/org.simantics.charts/src/org/simantics/charts/editor/e4/TimeSeriesEditor.java diff --git a/bundles/org.simantics.charts/META-INF/MANIFEST.MF b/bundles/org.simantics.charts/META-INF/MANIFEST.MF index 2b3484be8..46f215bd3 100644 --- a/bundles/org.simantics.charts/META-INF/MANIFEST.MF +++ b/bundles/org.simantics.charts/META-INF/MANIFEST.MF @@ -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 diff --git a/bundles/org.simantics.charts/src/org/simantics/charts/editor/TimeSeriesEditor.java b/bundles/org.simantics.charts/src/org/simantics/charts/editor/TimeSeriesEditor.java index cadb5bd4b..bffd4c8a2 100644 --- a/bundles/org.simantics.charts/src/org/simantics/charts/editor/TimeSeriesEditor.java +++ b/bundles/org.simantics.charts/src/org/simantics/charts/editor/TimeSeriesEditor.java @@ -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 index 9d10ad313..000000000 --- a/bundles/org.simantics.charts/src/org/simantics/charts/editor/e4/ChartCopyHandler.java +++ /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 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 index 62d7955a2..000000000 --- a/bundles/org.simantics.charts/src/org/simantics/charts/editor/e4/MoveHairlineHandler.java +++ /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 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 index 4c58595a1..000000000 --- a/bundles/org.simantics.charts/src/org/simantics/charts/editor/e4/TimeSeriesEditor.java +++ /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 - * @author Tuukka Lehtonen - */ -public class TimeSeriesEditor extends E4ResourceEditorBase implements IAdaptable { - - ParametrizedRead INPUT_VALIDATOR = - Combinators.compose( - InputValidationCombinators.hasURI(), - InputValidationCombinators.extractInputResource() - ); - - protected ParametrizedRead 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 findMAppElementById(List 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() { - @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 { - @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 { - @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 { - 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 getAdapter(Class 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 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; -// } -} -- 2.43.2