From bbc0593318256b4d59163970285ad8be4bd0a0ba Mon Sep 17 00:00:00 2001 From: lempinen Date: Tue, 22 Nov 2011 13:35:43 +0000 Subject: [PATCH] First implementation of jfreechart charts git-svn-id: https://www.simantics.org/svn/simantics/sysdyn/trunk@23325 ac1ea38d-2e2b-0410-8846-a27921b304fc --- org.simantics.sysdyn.feature/feature.xml | 7 + .../META-INF/MANIFEST.MF | 3 +- org.simantics.sysdyn.ontology/graph.tg | Bin 72223 -> 72865 bytes .../ChartAxisAndVariablesViewpoint.pgraph | 25 + .../graph/Sysdyn.pgraph | 6 +- .../org/simantics/sysdyn/SysdynResource.java | 12 + org.simantics.sysdyn.ui/META-INF/MANIFEST.MF | 4 +- org.simantics.sysdyn.ui/adapters.xml | 2 +- org.simantics.sysdyn.ui/plugin.xml | 43 +- .../ui/browser/contributions/ChartImager.java | 33 ++ .../browser/contributions/ChartLabeler.java | 34 ++ .../ui/browser/contributions/Charts.java | 47 ++ .../browser/contributions/ChartsImager.java | 33 ++ .../browser/contributions/ChartsLabeler.java | 31 + .../ui/browser/contributions/Model.java | 9 +- .../sysdyn/ui/browser/nodes/ChartNode.java | 94 +++ .../sysdyn/ui/browser/nodes/ChartsFolder.java | 36 ++ .../profiles/SimulationPlaybackStyle.java | 64 ++- .../newComponents/NewChartHandler.java | 97 ++++ .../sysdyn/ui/project/DefaultVariable.java | 88 --- .../sysdyn/ui/project/HistoryVariable.java | 113 ---- .../sysdyn/ui/properties/ModelTab.java | 25 - .../ResourceSelectionProcessor.java | 534 ++++++++++-------- .../simantics/sysdyn/ui/trend/ChartPanel.java | 241 +++++--- .../sysdyn/ui/trend/ChartPanelElement.java | 113 ++-- .../sysdyn/ui/trend/ChartPanelHeader.java | 97 +++- .../sysdyn/ui/trend/ChartPanelSeparator.java | 27 +- .../simantics/sysdyn/ui/trend/TrendView.java | 26 +- .../sysdyn/ui/trend/chart/ChartComposite.java | 137 +++++ .../sysdyn/ui/trend/chart/IAxis.java | 33 ++ .../sysdyn/ui/trend/chart/IDataset.java | 39 ++ .../sysdyn/ui/trend/chart/IJFreeChart.java | 32 ++ .../ui/trend/chart/IJFreeChartComponent.java | 33 ++ .../sysdyn/ui/trend/chart/IPlot.java | 33 ++ .../sysdyn/ui/trend/chart/ITitle.java | 33 ++ .../sysdyn/ui/trend/chart/JFreeChart.java | 111 ++++ .../sysdyn/ui/trend/chart/NumberAxis.java | 87 +++ .../sysdyn/ui/trend/chart/TextTitle.java | 94 +++ .../sysdyn/ui/trend/chart/XYDataset.java | 160 ++++++ .../sysdyn/ui/trend/chart/XYPlot.java | 153 +++++ .../chart/graphexplorer/AxisChildRule.java | 63 +++ .../graphexplorer/VariableChildRule.java | 69 +++ .../properties/AxisPropertyComposite.java | 111 ++++ .../properties/BooleanPropertyFactory.java | 130 +++++ .../properties/BooleanSelectionListener.java | 82 +++ .../properties/ChartAxisAndVariablesTab.java | 192 +++++++ .../ui/trend/chart/properties/ChartTab.java | 46 ++ .../trend/chart/properties/ColorPicker.java | 375 ++++++++++++ .../properties/GeneralChartPropertiesTab.java | 225 ++++++++ .../JFreeChartPropertyColorProvider.java | 57 ++ .../properties/SeriesPropertyComposite.java | 170 ++++++ .../chart/properties/TrackedSpinner.java | 179 ++++++ .../simantics/sysdyn/ui/values/ValueView.java | 77 ++- .../sysdyn/adapter/HistoryVariable.java | 21 +- 54 files changed, 3888 insertions(+), 698 deletions(-) create mode 100644 org.simantics.sysdyn.ontology/graph/ChartAxisAndVariablesViewpoint.pgraph create mode 100644 org.simantics.sysdyn.ui/src/org/simantics/sysdyn/ui/browser/contributions/ChartImager.java create mode 100644 org.simantics.sysdyn.ui/src/org/simantics/sysdyn/ui/browser/contributions/ChartLabeler.java create mode 100644 org.simantics.sysdyn.ui/src/org/simantics/sysdyn/ui/browser/contributions/Charts.java create mode 100644 org.simantics.sysdyn.ui/src/org/simantics/sysdyn/ui/browser/contributions/ChartsImager.java create mode 100644 org.simantics.sysdyn.ui/src/org/simantics/sysdyn/ui/browser/contributions/ChartsLabeler.java create mode 100644 org.simantics.sysdyn.ui/src/org/simantics/sysdyn/ui/browser/nodes/ChartNode.java create mode 100644 org.simantics.sysdyn.ui/src/org/simantics/sysdyn/ui/browser/nodes/ChartsFolder.java create mode 100644 org.simantics.sysdyn.ui/src/org/simantics/sysdyn/ui/handlers/newComponents/NewChartHandler.java delete mode 100644 org.simantics.sysdyn.ui/src/org/simantics/sysdyn/ui/project/DefaultVariable.java delete mode 100644 org.simantics.sysdyn.ui/src/org/simantics/sysdyn/ui/project/HistoryVariable.java delete mode 100644 org.simantics.sysdyn.ui/src/org/simantics/sysdyn/ui/properties/ModelTab.java create mode 100644 org.simantics.sysdyn.ui/src/org/simantics/sysdyn/ui/trend/chart/ChartComposite.java create mode 100644 org.simantics.sysdyn.ui/src/org/simantics/sysdyn/ui/trend/chart/IAxis.java create mode 100644 org.simantics.sysdyn.ui/src/org/simantics/sysdyn/ui/trend/chart/IDataset.java create mode 100644 org.simantics.sysdyn.ui/src/org/simantics/sysdyn/ui/trend/chart/IJFreeChart.java create mode 100644 org.simantics.sysdyn.ui/src/org/simantics/sysdyn/ui/trend/chart/IJFreeChartComponent.java create mode 100644 org.simantics.sysdyn.ui/src/org/simantics/sysdyn/ui/trend/chart/IPlot.java create mode 100644 org.simantics.sysdyn.ui/src/org/simantics/sysdyn/ui/trend/chart/ITitle.java create mode 100644 org.simantics.sysdyn.ui/src/org/simantics/sysdyn/ui/trend/chart/JFreeChart.java create mode 100644 org.simantics.sysdyn.ui/src/org/simantics/sysdyn/ui/trend/chart/NumberAxis.java create mode 100644 org.simantics.sysdyn.ui/src/org/simantics/sysdyn/ui/trend/chart/TextTitle.java create mode 100644 org.simantics.sysdyn.ui/src/org/simantics/sysdyn/ui/trend/chart/XYDataset.java create mode 100644 org.simantics.sysdyn.ui/src/org/simantics/sysdyn/ui/trend/chart/XYPlot.java create mode 100644 org.simantics.sysdyn.ui/src/org/simantics/sysdyn/ui/trend/chart/graphexplorer/AxisChildRule.java create mode 100644 org.simantics.sysdyn.ui/src/org/simantics/sysdyn/ui/trend/chart/graphexplorer/VariableChildRule.java create mode 100644 org.simantics.sysdyn.ui/src/org/simantics/sysdyn/ui/trend/chart/properties/AxisPropertyComposite.java create mode 100644 org.simantics.sysdyn.ui/src/org/simantics/sysdyn/ui/trend/chart/properties/BooleanPropertyFactory.java create mode 100644 org.simantics.sysdyn.ui/src/org/simantics/sysdyn/ui/trend/chart/properties/BooleanSelectionListener.java create mode 100644 org.simantics.sysdyn.ui/src/org/simantics/sysdyn/ui/trend/chart/properties/ChartAxisAndVariablesTab.java create mode 100644 org.simantics.sysdyn.ui/src/org/simantics/sysdyn/ui/trend/chart/properties/ChartTab.java create mode 100644 org.simantics.sysdyn.ui/src/org/simantics/sysdyn/ui/trend/chart/properties/ColorPicker.java create mode 100644 org.simantics.sysdyn.ui/src/org/simantics/sysdyn/ui/trend/chart/properties/GeneralChartPropertiesTab.java create mode 100644 org.simantics.sysdyn.ui/src/org/simantics/sysdyn/ui/trend/chart/properties/JFreeChartPropertyColorProvider.java create mode 100644 org.simantics.sysdyn.ui/src/org/simantics/sysdyn/ui/trend/chart/properties/SeriesPropertyComposite.java create mode 100644 org.simantics.sysdyn.ui/src/org/simantics/sysdyn/ui/trend/chart/properties/TrackedSpinner.java diff --git a/org.simantics.sysdyn.feature/feature.xml b/org.simantics.sysdyn.feature/feature.xml index 66b609d2..d5e62a54 100644 --- a/org.simantics.sysdyn.feature/feature.xml +++ b/org.simantics.sysdyn.feature/feature.xml @@ -121,4 +121,11 @@ install-size="0" version="0.0.0"/> + + diff --git a/org.simantics.sysdyn.ontology/META-INF/MANIFEST.MF b/org.simantics.sysdyn.ontology/META-INF/MANIFEST.MF index be16f573..c12c356d 100644 --- a/org.simantics.sysdyn.ontology/META-INF/MANIFEST.MF +++ b/org.simantics.sysdyn.ontology/META-INF/MANIFEST.MF @@ -11,7 +11,8 @@ Require-Bundle: org.simantics.layer0, org.simantics.project.ontology;bundle-version="1.0.0", org.simantics.viewpoint.ontology;bundle-version="1.0.0", org.simantics.layer0x.ontology;bundle-version="1.0.0", - org.simantics.issues.ontology;bundle-version="1.1.0" + org.simantics.issues.ontology;bundle-version="1.1.0", + org.simantics.jfreechart.ontology;bundle-version="0.1.0" Bundle-RequiredExecutionEnvironment: JavaSE-1.6 Export-Package: org.simantics.sysdyn Bundle-Vendor: VTT Technical Reserarch Centre of Finland diff --git a/org.simantics.sysdyn.ontology/graph.tg b/org.simantics.sysdyn.ontology/graph.tg index eaa15267e151cd2190bea2f1d2c155d5ac4bed0e..2a4147273fc6214aadda96669484067899db53e2 100644 GIT binary patch literal 72865 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 UG7qKiniKnbYHj#TCOqu=2CP_~DF6Tf literal 72223 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 diff --git a/org.simantics.sysdyn.ontology/graph/ChartAxisAndVariablesViewpoint.pgraph b/org.simantics.sysdyn.ontology/graph/ChartAxisAndVariablesViewpoint.pgraph new file mode 100644 index 00000000..d07ed227 --- /dev/null +++ b/org.simantics.sysdyn.ontology/graph/ChartAxisAndVariablesViewpoint.pgraph @@ -0,0 +1,25 @@ +L0 = +VP = +PROJECT = +MOD = +IMAGE = +COLOR = +ACT = +JFREE = +SYSDYN = + +CBC = SYSDYN.ChartAxisAndVariablesBrowseContext : VP.BrowseContext +CAC = SYSDYN.ChartAxisAndVariablesActionContext : VP.BrowseContext + +CBC.AxisChildRule : VP.ChildRule +CBC.VariableChildRule : VP.ChildRule + +CBC + @VP.customChildRule JFREE.Chart CBC.AxisChildRule + JFREE.Axis : VP.ResourceNodeType + @VP.customChildRule JFREE.Axis CBC.VariableChildRule + JFREE.Series : VP.ResourceNodeType + +CBC + @VP.customLabelRule JFREE.Axis VP.ResourceLabelLabelRule + @VP.customLabelRule JFREE.Series VP.ResourceLabelLabelRule \ No newline at end of file diff --git a/org.simantics.sysdyn.ontology/graph/Sysdyn.pgraph b/org.simantics.sysdyn.ontology/graph/Sysdyn.pgraph index a00b29db..e3ed74d5 100644 --- a/org.simantics.sysdyn.ontology/graph/Sysdyn.pgraph +++ b/org.simantics.sysdyn.ontology/graph/Sysdyn.pgraph @@ -6,6 +6,7 @@ DIA = SIMU = MOD = PROJ = +JFREE = //##################################################################### // Defines ontology and attaches it to SimanticsDomain @@ -371,9 +372,8 @@ SYSDYN.HasParameterFile - none 0 fill 1 + none 0 fill 1 \ No newline at end of file diff --git a/org.simantics.sysdyn.ui/plugin.xml b/org.simantics.sysdyn.ui/plugin.xml index 2eedf70d..dc462d7c 100644 --- a/org.simantics.sysdyn.ui/plugin.xml +++ b/org.simantics.sysdyn.ui/plugin.xml @@ -563,6 +563,22 @@ + + + + + + + + + + @@ -1281,6 +1302,10 @@ class="org.simantics.sysdyn.ui.browser.contributions.Book" preference="2.0"> + + @@ -1445,6 +1470,14 @@ class="org.simantics.sysdyn.ui.browser.contributions.SheetLabeler" preference="2.0"> + + + + @@ -1549,6 +1582,14 @@ class="org.simantics.sysdyn.ui.browser.contributions.SimulationResultImager" preference="2.0"> + + + + @@ -1631,7 +1672,7 @@ diff --git a/org.simantics.sysdyn.ui/src/org/simantics/sysdyn/ui/browser/contributions/ChartImager.java b/org.simantics.sysdyn.ui/src/org/simantics/sysdyn/ui/browser/contributions/ChartImager.java new file mode 100644 index 00000000..6cc2a412 --- /dev/null +++ b/org.simantics.sysdyn.ui/src/org/simantics/sysdyn/ui/browser/contributions/ChartImager.java @@ -0,0 +1,33 @@ +/******************************************************************************* + * 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.sysdyn.ui.browser.contributions; + +import org.eclipse.jface.resource.ImageDescriptor; +import org.simantics.browsing.ui.swt.ImagerContributor; +import org.simantics.db.ReadGraph; +import org.simantics.db.exception.DatabaseException; +import org.simantics.sysdyn.ui.Activator; +import org.simantics.sysdyn.ui.browser.nodes.ChartNode; + +/** + * Provides image for {@link ChartNode} in model browser + * @author Teemu Lempinen + * + */ +public class ChartImager extends ImagerContributor { + + @Override + public ImageDescriptor getDescriptor(ReadGraph graph, ChartNode input) throws DatabaseException { + return ImageDescriptor.createFromURL(Activator.getDefault().getBundle().getResource("icons/chart_organisation.png")); + } + +} diff --git a/org.simantics.sysdyn.ui/src/org/simantics/sysdyn/ui/browser/contributions/ChartLabeler.java b/org.simantics.sysdyn.ui/src/org/simantics/sysdyn/ui/browser/contributions/ChartLabeler.java new file mode 100644 index 00000000..55936833 --- /dev/null +++ b/org.simantics.sysdyn.ui/src/org/simantics/sysdyn/ui/browser/contributions/ChartLabeler.java @@ -0,0 +1,34 @@ +/******************************************************************************* + * 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.sysdyn.ui.browser.contributions; + +import org.simantics.browsing.ui.graph.contributor.labeler.LabelerContributor; +import org.simantics.db.ReadGraph; +import org.simantics.db.exception.DatabaseException; +import org.simantics.layer0.Layer0; +import org.simantics.sysdyn.ui.browser.nodes.ChartNode; + +/** + * Provides label for {@link ChartNode} in model browser + * @author Teemu Lempinen + * + */ +public class ChartLabeler extends LabelerContributor { + + @Override + public String getLabel(ReadGraph graph, ChartNode chart) throws DatabaseException { + String name = graph.getPossibleRelatedValue(chart.data, Layer0.getInstance(graph).HasName); + return name == null ? "Chart (no name)" : name; + } + + +} diff --git a/org.simantics.sysdyn.ui/src/org/simantics/sysdyn/ui/browser/contributions/Charts.java b/org.simantics.sysdyn.ui/src/org/simantics/sysdyn/ui/browser/contributions/Charts.java new file mode 100644 index 00000000..766aec6b --- /dev/null +++ b/org.simantics.sysdyn.ui/src/org/simantics/sysdyn/ui/browser/contributions/Charts.java @@ -0,0 +1,47 @@ +/******************************************************************************* + * 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.sysdyn.ui.browser.contributions; + +import java.util.ArrayList; +import java.util.Collection; + +import org.simantics.browsing.ui.common.node.AbstractNode; +import org.simantics.browsing.ui.graph.contributor.viewpoint.ViewpointContributor; +import org.simantics.db.ReadGraph; +import org.simantics.db.Resource; +import org.simantics.db.common.request.ObjectsWithType; +import org.simantics.db.exception.DatabaseException; +import org.simantics.layer0.Layer0; +import org.simantics.sysdyn.JFreeChartResource; +import org.simantics.sysdyn.ui.browser.nodes.ChartNode; +import org.simantics.sysdyn.ui.browser.nodes.ChartsFolder; + +/** + * Class for creating chart nodes for model browser + * @author Teemu Lempinen + * + */ +public class Charts extends ViewpointContributor { + + @Override + public Collection getContribution(ReadGraph graph, ChartsFolder folder) throws DatabaseException { + ArrayList> result = new ArrayList>(); + for(Resource chart : graph.syncRequest( + new ObjectsWithType(folder.data, + Layer0.getInstance(graph).ConsistsOf, + JFreeChartResource.getInstance(graph).Chart))) { + result.add(new ChartNode(chart)); + } + return result; + } + +} diff --git a/org.simantics.sysdyn.ui/src/org/simantics/sysdyn/ui/browser/contributions/ChartsImager.java b/org.simantics.sysdyn.ui/src/org/simantics/sysdyn/ui/browser/contributions/ChartsImager.java new file mode 100644 index 00000000..e51b8e74 --- /dev/null +++ b/org.simantics.sysdyn.ui/src/org/simantics/sysdyn/ui/browser/contributions/ChartsImager.java @@ -0,0 +1,33 @@ +/******************************************************************************* + * 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.sysdyn.ui.browser.contributions; + +import org.eclipse.jface.resource.ImageDescriptor; +import org.simantics.browsing.ui.swt.ImagerContributor; +import org.simantics.db.ReadGraph; +import org.simantics.db.exception.DatabaseException; +import org.simantics.sysdyn.ui.Activator; +import org.simantics.sysdyn.ui.browser.nodes.ChartsFolder; + +/** + * Provides image for {@link ChartsFolder} in model browser + * @author Teemu Lempinen + * + */ +public class ChartsImager extends ImagerContributor { + + @Override + public ImageDescriptor getDescriptor(ReadGraph graph, ChartsFolder chartNode) throws DatabaseException { + return ImageDescriptor.createFromURL(Activator.getDefault().getBundle().getResource("icons/folder.png")); + } + +} diff --git a/org.simantics.sysdyn.ui/src/org/simantics/sysdyn/ui/browser/contributions/ChartsLabeler.java b/org.simantics.sysdyn.ui/src/org/simantics/sysdyn/ui/browser/contributions/ChartsLabeler.java new file mode 100644 index 00000000..fe718b2c --- /dev/null +++ b/org.simantics.sysdyn.ui/src/org/simantics/sysdyn/ui/browser/contributions/ChartsLabeler.java @@ -0,0 +1,31 @@ +/******************************************************************************* + * 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.sysdyn.ui.browser.contributions; + +import org.simantics.browsing.ui.graph.contributor.labeler.LabelerContributor; +import org.simantics.db.ReadGraph; +import org.simantics.db.exception.DatabaseException; +import org.simantics.sysdyn.ui.browser.nodes.ChartsFolder; + +/** + * Provides label for {@link ChartsFolder} in model browser + * @author Teemu Lempinen + * + */ +public class ChartsLabeler extends LabelerContributor { + + @Override + public String getLabel(ReadGraph graph, ChartsFolder input) throws DatabaseException { + return "Charts"; + } + +} diff --git a/org.simantics.sysdyn.ui/src/org/simantics/sysdyn/ui/browser/contributions/Model.java b/org.simantics.sysdyn.ui/src/org/simantics/sysdyn/ui/browser/contributions/Model.java index 1d50d2f1..01c21fb1 100644 --- a/org.simantics.sysdyn.ui/src/org/simantics/sysdyn/ui/browser/contributions/Model.java +++ b/org.simantics.sysdyn.ui/src/org/simantics/sysdyn/ui/browser/contributions/Model.java @@ -1,5 +1,5 @@ /******************************************************************************* - * Copyright (c) 2010 Association for Decentralized Information Management in + * Copyright (c) 2010, 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 @@ -23,12 +23,18 @@ import org.simantics.db.exception.DatabaseException; import org.simantics.db.layer0.variable.Variable; import org.simantics.db.layer0.variable.Variables; import org.simantics.operation.Layer0X; +import org.simantics.sysdyn.ui.browser.nodes.ChartsFolder; import org.simantics.sysdyn.ui.browser.nodes.ConfigurationNode; import org.simantics.sysdyn.ui.browser.nodes.ExperimentsFolder; import org.simantics.sysdyn.ui.browser.nodes.FunctionsFolder; import org.simantics.sysdyn.ui.browser.nodes.ModelNode; import org.simantics.sysdyn.ui.browser.nodes.ModulesNode; +/** + * Provides children for a model in model browser + * @author Teemu Lempinen + * + */ public class Model extends ViewpointContributor { @Override @@ -49,6 +55,7 @@ public class Model extends ViewpointContributor { result.add(new ExperimentsFolder(model.data)); result.add(new ModulesNode(model.data)); result.add(new FunctionsFolder(model.data)); + result.add(new ChartsFolder(model.data)); return result; } diff --git a/org.simantics.sysdyn.ui/src/org/simantics/sysdyn/ui/browser/nodes/ChartNode.java b/org.simantics.sysdyn.ui/src/org/simantics/sysdyn/ui/browser/nodes/ChartNode.java new file mode 100644 index 00000000..a51a9e4c --- /dev/null +++ b/org.simantics.sysdyn.ui/src/org/simantics/sysdyn/ui/browser/nodes/ChartNode.java @@ -0,0 +1,94 @@ +/******************************************************************************* + * 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.sysdyn.ui.browser.nodes; + +import java.util.Iterator; + +import org.eclipse.core.runtime.IAdaptable; +import org.eclipse.jface.viewers.IStructuredSelection; +import org.simantics.browsing.ui.common.node.AbstractNode; +import org.simantics.browsing.ui.common.node.IDropTargetNode; +import org.simantics.databoard.Bindings; +import org.simantics.db.Resource; +import org.simantics.db.WriteGraph; +import org.simantics.db.common.request.SingleObjectWithType; +import org.simantics.db.common.request.WriteRequest; +import org.simantics.db.exception.DatabaseException; +import org.simantics.db.layer0.variable.Variable; +import org.simantics.db.layer0.variable.Variables; +import org.simantics.layer0.Layer0; +import org.simantics.layer0.utils.direct.GraphUtils; +import org.simantics.sysdyn.JFreeChartResource; +import org.simantics.ui.SimanticsUI; + +/** + * Node representing a chart + * @author Teemu Lempinen + * + */ +public class ChartNode extends AbstractNode implements IDropTargetNode { + + public ChartNode(Resource data) { + super(data); + } + + /** + * Add variable to this chart, if the dropped element(s) can be adapted to a {@link Variable} + */ + @Override + public void drop(Object data) { + IStructuredSelection selection = (IStructuredSelection)data; + Iterator iterator = selection.iterator(); + while(iterator.hasNext()) { + Object o = iterator.next(); + if(o instanceof IAdaptable) { + Variable v = (Variable) ((IAdaptable)o).getAdapter(Variable.class); + if(v != null) { + addVariableToChart(v); + } + } + } + } + + + /** + * Adds a variable to this chart + * @param variable + */ + private void addVariableToChart(final Variable variable) { + + SimanticsUI.getSession().asyncRequest(new WriteRequest() { + + @Override + public void perform(WriteGraph graph) throws DatabaseException { + Layer0 l0 = Layer0.getInstance(graph); + JFreeChartResource jfree = JFreeChartResource.getInstance(graph); + + Resource plot = graph.syncRequest(new SingleObjectWithType(data, l0.ConsistsOf, jfree.Plot)); + + Resource dataset = graph.syncRequest(new SingleObjectWithType(plot, l0.ConsistsOf, jfree.Dataset)); //FIXME: Support multiple datasets + + String rvi = Variables.getRVI(graph, variable); + Resource type = l0.String; + Resource literal = graph.newResource(); + graph.claim(literal, l0.InstanceOf, null, type); + graph.claimValue(literal, rvi, Bindings.STRING); + + GraphUtils.create2(graph, jfree.Series, + l0.HasLabel, rvi, + jfree.HasVariableRVI, literal, + l0.PartOf, dataset); + } + }); + } + +} diff --git a/org.simantics.sysdyn.ui/src/org/simantics/sysdyn/ui/browser/nodes/ChartsFolder.java b/org.simantics.sysdyn.ui/src/org/simantics/sysdyn/ui/browser/nodes/ChartsFolder.java new file mode 100644 index 00000000..d75b01ba --- /dev/null +++ b/org.simantics.sysdyn.ui/src/org/simantics/sysdyn/ui/browser/nodes/ChartsFolder.java @@ -0,0 +1,36 @@ +/******************************************************************************* + * 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.sysdyn.ui.browser.nodes; + +import org.simantics.browsing.ui.common.node.AbstractNode; +import org.simantics.db.Resource; + +/** + * Folder containing all sysdyn charts + * @author Teemu Lempinen + * + */ +public class ChartsFolder extends AbstractNode { + + public ChartsFolder(Resource resource) { + super(resource); + } + + @SuppressWarnings("rawtypes") + @Override + public Object getAdapter(Class adapter) { + if(clazz == adapter) // There is no resource for this node.. + return null; + return super.getAdapter(adapter); + } + +} diff --git a/org.simantics.sysdyn.ui/src/org/simantics/sysdyn/ui/elements2/profiles/SimulationPlaybackStyle.java b/org.simantics.sysdyn.ui/src/org/simantics/sysdyn/ui/elements2/profiles/SimulationPlaybackStyle.java index 66c36622..91084b6c 100644 --- a/org.simantics.sysdyn.ui/src/org/simantics/sysdyn/ui/elements2/profiles/SimulationPlaybackStyle.java +++ b/org.simantics.sysdyn.ui/src/org/simantics/sysdyn/ui/elements2/profiles/SimulationPlaybackStyle.java @@ -11,7 +11,6 @@ *******************************************************************************/ package org.simantics.sysdyn.ui.elements2.profiles; -//import java.awt.Color; import java.awt.geom.AffineTransform; import java.awt.geom.Rectangle2D; import java.util.ArrayList; @@ -48,15 +47,27 @@ import org.simantics.utils.ui.color.Color; import org.simantics.utils.ui.color.ColorGradient; import org.simantics.utils.ui.color.ColorValue; +/** + * Profile style definition for simulation playback mode. Works only with SimulationPlaybackExperiment + * + * @author Teemu Lempinen + * + */ public class SimulationPlaybackStyle extends StyleBase> { Resource gradientResource; ColorGradient cg; byte[] gradient; + /** + * Determine if style needs to be redrawn and return objects that are needed to redraw the style. + * + * @return All necessary components that are needed to draw this style + */ @Override public Triple calculateStyle(ReadGraph graph, Resource runtimeDiagram, Resource entry, Resource element, Variable configuration) throws DatabaseException { + // Find SimulationPlaybackExperiment IProject project = SimanticsUI.getProject(); IExperimentManager em = project.getHint(IExperimentManager.KEY_EXPERIMENT_MANAGER); IExperiment experiment = em.getActiveExperiment(); @@ -74,6 +85,7 @@ public class SimulationPlaybackStyle extends StyleBase max) max = d; } - + + // Find the index in time and value arrays for this time + // (time steps may vary, we need to loop to find the correct time) int index = 0; if(ta[ta.length - 1] - time > ta[ta.length / 2] ) { index = ta.length - 1; @@ -121,10 +139,14 @@ public class SimulationPlaybackStyle extends StyleBase result) { Double multiplier; if (result != null && (multiplier = result.second) != null && !multiplier.isNaN()) { - + // Create a node that will show the style effect A node = ProfileVariables.claimChild(_node, "", "playbackColour", A.class, observer); if (node == null) return; @@ -174,28 +199,34 @@ public class SimulationPlaybackStyle extends StyleBase T getValue(ReadGraph graph) throws DatabaseException { - Layer0X L0X = Layer0X.getInstance(graph); - SysdynResource sr = SysdynResource.getInstance(graph); - Resource represents = graph.getPossibleObject(resource, L0X.Represents); - if(represents == null) return null; - //FIXME: doesn't support multiple expressions - Resource expressions = graph.getPossibleObject(represents, sr.HasExpressions); - if(expressions == null) return null; - List expressionList = OrderedSetUtils.toList(graph, expressions); - Resource expression = expressionList.get(0); - if(expression == null) return null; - if(!graph.isInstanceOf(expression, sr.ParameterExpression)) return null; - String text = graph.getPossibleRelatedValue(expression, sr.HasEquation); - if(text == null) return null; - Double value = Double.parseDouble(text); - return (T)value; - } - - @Override - public T getValue(ReadGraph graph, Binding binding) throws DatabaseException { - if(!Bindings.DOUBLE.equals(binding)) return null; - return getValue(graph); - } - - @Override - public void setValue(WriteGraph graph, Object object, Binding binding) throws DatabaseException { - Layer0 l0 = Layer0.getInstance(graph); - Layer0X L0X = Layer0X.getInstance(graph); - SysdynResource sr = SysdynResource.getInstance(graph); -// if(!Bindings.DOUBLE.equals(binding)) return; -// if(!MutableDoubleBinding.INSTANCE.equals(binding)) return; - Resource represents = graph.getPossibleObject(resource, L0X.Represents); - if(represents == null) return; - //FIXME: doesn't support multiple expressions - Resource expressions = graph.getPossibleObject(represents, sr.HasExpressions); - if(expressions == null) return; - List expressionList = OrderedSetUtils.toList(graph, expressions); - Resource expression = expressionList.get(0); - if(expression == null) return; - if(!graph.isInstanceOf(expression, sr.ParameterExpression)) return; - graph.claimLiteral(expression, sr.HasEquation, object.toString(), Bindings.STRING); - } - - @SuppressWarnings("unchecked") - @Override - public T getInterface(ReadGraph graph, Class clazz) - throws DatabaseException { - if(Datatype.class.equals(clazz)) return (T)Datatypes.DOUBLE; - return super.getInterface(graph, clazz); - } -} diff --git a/org.simantics.sysdyn.ui/src/org/simantics/sysdyn/ui/project/HistoryVariable.java b/org.simantics.sysdyn.ui/src/org/simantics/sysdyn/ui/project/HistoryVariable.java deleted file mode 100644 index b7b8539f..00000000 --- a/org.simantics.sysdyn.ui/src/org/simantics/sysdyn/ui/project/HistoryVariable.java +++ /dev/null @@ -1,113 +0,0 @@ -/******************************************************************************* - * Copyright (c) 2010 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.sysdyn.ui.project; - -import java.util.ArrayList; - -import org.simantics.databoard.Accessors; -import org.simantics.databoard.Bindings; -import org.simantics.databoard.accessor.Accessor; -import org.simantics.databoard.accessor.RecordAccessor; -import org.simantics.databoard.accessor.error.AccessorConstructionException; -import org.simantics.databoard.accessor.error.AccessorException; -import org.simantics.databoard.binding.error.RuntimeBindingConstructionException; -import org.simantics.db.ReadGraph; -import org.simantics.db.Resource; -import org.simantics.db.exception.DatabaseException; -import org.simantics.db.layer0.variable.ResourceVariable; -import org.simantics.db.layer0.variable.Variables; -import org.simantics.simulation.ontology.SimulationResource; -import org.simantics.sysdyn.manager.SysdynDataSet; -import org.simantics.sysdyn.manager.SysdynModel; -import org.simantics.sysdyn.manager.SysdynModelManager; -import org.simantics.sysdyn.manager.SysdynResult; - -public class HistoryVariable extends ResourceVariable { - - public HistoryVariable(Resource resource) { - super(resource); - } - -// @Override -// public T getInterface(ReadGraph graph, Class clazz) throws DatabaseException { -// if(Accessor.class.equals(clazz)) { -// -// SimulationResource SIMU = SimulationResource.getInstance(graph); -// Resource model = Variables.getModel(graph, this); -// Resource configuration = graph.getPossibleObject(model, SIMU.HasConfiguration); -// SysdynModel sm = SysdynModelManager.getInstance(graph.getSession()).getModel(graph, configuration); -// SysdynResult sr = sm.getSysdynResult(); -// String rvi = Variables.getRVI(graph, this); -// System.out.println("HistoryVariable rvi='" + rvi + "'"); -// rvi = rvi.substring(1).replace("/", "."); -// SysdynDataSet ds = sr.getDataSet(rvi); -// -// try { -// return (T)Accessors.getAccessor(Bindings.getBindingUnchecked(SysdynDataSet.class), ds); -// } catch (RuntimeBindingConstructionException e) { -// e.printStackTrace(); -// } catch (AccessorConstructionException e) { -// e.printStackTrace(); -// } -// return null; -// -// } -// return super.getInterface(graph, clazz); -// } - - @SuppressWarnings("unchecked") - @Override - public T getInterface(ReadGraph graph, Class clazz) throws DatabaseException { - if(RecordAccessor.class.equals(clazz) || Accessor.class.equals(clazz)) { - SimulationResource SIMU = SimulationResource.getInstance(graph); - Resource model = Variables.getModel(graph, this); - Resource configuration = graph.getPossibleObject(model, SIMU.HasConfiguration); - final SysdynModel sm = SysdynModelManager.getInstance(graph.getSession()).getModel(graph, configuration); - SysdynResult sr = new SysdynResult(sm.getSimulationResult()); // TODO: copy or not to copy ... - - String tmp = Variables.getRVI(graph, this); - System.out.println("HistoryVariable rvi='" + tmp + "'"); - final String rvi = tmp.substring(1).replace("/", "."); - SysdynDataSet ds = sr.getDataSet(rvi); - if(ds == null) ds = new SysdynDataSet("", "", new ArrayList(), new ArrayList()); // We need a dataset, so if not set, create it - try { - final RecordAccessor ac = (RecordAccessor)Accessors.getAccessor(Bindings.getBindingUnchecked(SysdynDataSet.class), ds); - - sm.addResultListener(new Runnable() { // FIXME: remove listener at some point.. - @Override - public void run() { - SysdynResult sr = new SysdynResult(sm.getSimulationResult()); - SysdynDataSet ds = sr.getDataSet(rvi); - if(ds == null) return; - try { - if(ds.result == null) ds.result = ""; - ac.setValue(Bindings.getBindingUnchecked(SysdynDataSet.class), ds); - } catch (RuntimeBindingConstructionException e) { - // TODO Auto-generated catch block - e.printStackTrace(); - } catch (AccessorException e) { - // TODO Auto-generated catch block - e.printStackTrace(); - } - }}); - return (T)ac; - } catch (RuntimeBindingConstructionException e) { - e.printStackTrace(); - } catch (AccessorConstructionException e) { - e.printStackTrace(); - } - return null; - - } - return super.getInterface(graph, clazz); - } -} diff --git a/org.simantics.sysdyn.ui/src/org/simantics/sysdyn/ui/properties/ModelTab.java b/org.simantics.sysdyn.ui/src/org/simantics/sysdyn/ui/properties/ModelTab.java deleted file mode 100644 index 77ea180a..00000000 --- a/org.simantics.sysdyn.ui/src/org/simantics/sysdyn/ui/properties/ModelTab.java +++ /dev/null @@ -1,25 +0,0 @@ -/******************************************************************************* - * Copyright (c) 2010 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.sysdyn.ui.properties; - -import org.eclipse.swt.widgets.Composite; -import org.eclipse.ui.IWorkbenchSite; -import org.simantics.browsing.ui.swt.widgets.impl.WidgetSupport; -import org.simantics.db.management.ISessionContext; - -public class ModelTab extends LabelPropertyTabContributor { - - @Override - public void createControls(Composite body, IWorkbenchSite site, ISessionContext context, WidgetSupport support) { - - } -} diff --git a/org.simantics.sysdyn.ui/src/org/simantics/sysdyn/ui/properties/ResourceSelectionProcessor.java b/org.simantics.sysdyn.ui/src/org/simantics/sysdyn/ui/properties/ResourceSelectionProcessor.java index c453dd79..d0f4851d 100644 --- a/org.simantics.sysdyn.ui/src/org/simantics/sysdyn/ui/properties/ResourceSelectionProcessor.java +++ b/org.simantics.sysdyn.ui/src/org/simantics/sysdyn/ui/properties/ResourceSelectionProcessor.java @@ -29,263 +29,313 @@ import org.simantics.modeling.ModelingResources; import org.simantics.selectionview.ComparableTabContributor; import org.simantics.selectionview.SelectionProcessor; import org.simantics.simulation.ontology.SimulationResource; +import org.simantics.sysdyn.JFreeChartResource; import org.simantics.sysdyn.SysdynResource; import org.simantics.sysdyn.ui.browser.nodes.SharedFunctionsFolder; +import org.simantics.sysdyn.ui.trend.chart.properties.ChartAxisAndVariablesTab; +import org.simantics.sysdyn.ui.trend.chart.properties.ChartTab; +import org.simantics.sysdyn.ui.trend.chart.properties.GeneralChartPropertiesTab; import org.simantics.ui.utils.AdaptionUtils; +/** + * SelectionProcessor for processing selections for property view in system dynamics + * + * @author Teemu Lempinen + * + */ public class ResourceSelectionProcessor implements SelectionProcessor { - @Override - public Collection process(Object selection, ReadGraph backend) { - Collection tabs = new ArrayList(); - SysdynResource sr = SysdynResource.getInstance(backend); - DiagramResource dr = DiagramResource.getInstance(backend); - ModelingResources mr = ModelingResources.getInstance(backend); - SimulationResource simu = SimulationResource.getInstance(backend); + @Override + public Collection process(Object selection, ReadGraph backend) { + Collection tabs = new ArrayList(); + SysdynResource sr = SysdynResource.getInstance(backend); + DiagramResource dr = DiagramResource.getInstance(backend); + ModelingResources mr = ModelingResources.getInstance(backend); + SimulationResource simu = SimulationResource.getInstance(backend); + JFreeChartResource jfree = JFreeChartResource.getInstance(backend); + + try { + // Many elements + if (selection instanceof ArrayList && ((ArrayList) selection).size() > 1) { + List independentVariables = new ArrayList(); + Resource model = null; + for(Object o : (ArrayList)selection) { + Resource r = AdaptionUtils.adaptToSingle(o, Resource.class); + if(r != null && backend.isInstanceOf(r, sr.IndependentVariable)) { + if(model == null) + model = backend.getSingleObject(r, Layer0.getInstance(backend).PartOf); + if(model.equals( backend.getSingleObject(r, Layer0.getInstance(backend).PartOf))) + independentVariables.add(r); + } + } + + tabs.add(new ComparableTabContributor( + new ArrayIndexesTab(), + 1, + independentVariables, + "Indexes")); + + return tabs; + } + + + // Single element + Resource r = AdaptionUtils.adaptToSingle(selection, Resource.class); + + if(r == null) { + // SharedFunctionsFolder has properties but no adapted resource + SharedFunctionsFolder sff = AdaptionUtils.adaptToSingle(selection, SharedFunctionsFolder.class); + if (sff != null) { + return Collections.singleton(new ComparableTabContributor( + new SharedFunctionLibrariesTab(), + 2, + sff.data, + "Shared Functions")); + } - try { - // Many elements - if (selection instanceof ArrayList && ((ArrayList) selection).size() > 1) { - List independentVariables = new ArrayList(); - Resource model = null; - for(Object o : (ArrayList)selection) { - Resource r = AdaptionUtils.adaptToSingle(o, Resource.class); - if(r != null && backend.isInstanceOf(r, sr.IndependentVariable)) { - if(model == null) - model = backend.getSingleObject(r, Layer0.getInstance(backend).PartOf); - if(model.equals( backend.getSingleObject(r, Layer0.getInstance(backend).PartOf))) - independentVariables.add(r); - } - } + return Collections.emptyList(); + } - tabs.add(new ComparableTabContributor( - new ArrayIndexesTab(), - 1, - independentVariables, - "Indexes")); - - return tabs; - } + // if r == diagram element, change it to component + if (backend.isInstanceOf(r, dr.Element)) { + Resource component = backend.getPossibleObject(r, mr.ElementToComponent); + if (component != null) { + r = component; + } else { + Resource connection = backend.getPossibleObject(r, mr.DiagramConnectionToConnection); + if(connection != null) + r = connection; + } + } + + // Independent variable + if (backend.isInstanceOf(r, sr.IndependentVariable)) { + Resource activeExpression = backend.getPossibleObject(r, sr.HasActiveExpression); + Resource expression = null; + if(activeExpression != null) + // if variable has active expression, display it + expression = activeExpression; + else if (backend.hasStatement(r, sr.HasExpressions)){ + // else display the first expression of the variable + Resource expressions = backend.getPossibleObject(r, sr.HasExpressions); + List expressionList = OrderedSetUtils.toList(backend, expressions); + if(expressionList.isEmpty()) { + System.err.println("expressionList is empty for " + r); + return Collections.emptyList(); + } + expression = expressionList.get(0); + } + tabs.add(new ComparableTabContributor( + new EquationTab(), + 3, + r, + "Equation")); + if(expression != null && backend.isInstanceOf(expression, sr.WithLookupExpression)) { + // WithLookupExpression has its own extra tab for visual configuration + tabs.add(new ComparableTabContributor( + new LookupTableTab(), + 2, + expression, + "Lookup Table")); + } - // Single element - Resource r = AdaptionUtils.adaptToSingle(selection, Resource.class); - - if(r == null) { - SharedFunctionsFolder sff = AdaptionUtils.adaptToSingle(selection, SharedFunctionsFolder.class); - if (sff != null) { - return Collections.singleton(new ComparableTabContributor( - new SharedFunctionLibrariesTab(), - 2, - sff.data, - "Shared Functions")); - } - - return Collections.emptyList(); - } + tabs.add(new ComparableTabContributor( + new ArrayIndexesTab(), + 1, + r, + "Indexes")); + tabs.add(new ComparableTabContributor( + new VariableInformationTab(), + 0, + r, + "Additional Information")); + return tabs; + } + + // Input variable + if (backend.isInstanceOf(r, sr.Input)) { + tabs.add(new ComparableTabContributor( + new InputVariableTab(), + 2, + r, + "Input")); - if (backend.isInstanceOf(r, dr.Element)) { - Resource component = backend.getPossibleObject(r, mr.ElementToComponent); - if (component != null) { - r = component; - } else { - Resource connection = backend.getPossibleObject(r, mr.DiagramConnectionToConnection); - if(connection != null) - r = connection; - } - } - if (backend.isInstanceOf(r, sr.IndependentVariable)) { + tabs.add(new ComparableTabContributor( + new ArrayIndexesTab(), + 1, + r, + "Indexes")); + tabs.add(new ComparableTabContributor( + new VariableInformationTab(), + 0, + r, + "Additional Information")); + return tabs; + } + + // Enumeration + if (backend.isInstanceOf(r, sr.Enumeration)) { + Object s = AdaptionUtils.adaptToSingle(selection, ISelection.class); + if(s == null) + s = r; + // give either variable or the actual resource + return Collections.singleton(new ComparableTabContributor( + new EnumerationTab(), + 2, + s, + "Enumeration")); + } + + // Configuration and model. They both show the properties of the configuration + if ( backend.isInstanceOf(r, sr.Configuration) || backend.isInstanceOf(r, sr.SysdynModel)) { + if(!backend.isInstanceOf(r, sr.SysdynModel)) + r = backend.getPossibleObject(r, SimulationResource.getInstance(backend).IsConfigurationOf); + if (r != null) + return Collections.singleton( + new ComparableTabContributor( + new ConfigurationTab(), + 0, + r, + "Model Properties")); + } + + // Module + if (backend.isInstanceOf(r, sr.Module)){ + tabs.add(new ComparableTabContributor( + new ModuleTab(), + 3, + r, + "Module Properties")); + tabs.add(new ComparableTabContributor( + new ModuleInputTab(), + 2, + r, + "Inputs")); + tabs.add(new ComparableTabContributor( + new ModuleOutputTab(), + 1, + r, + "Outputs")); + return tabs; + } + + // Playback experiment + if (backend.isInstanceOf(r, sr.PlaybackExperiment)) + return Collections.singleton( + new ComparableTabContributor( + new PlaybackExperimentTab(), + 0, + r, + "Experiment Properties")); + + // Default experiment + if (backend.isInstanceOf(r, simu.Experiment)) + return Collections.singleton( + new ComparableTabContributor( + new ExperimentTab(), + 0, + r, + "Experiment Properties")); + + // Saved simulation result + if (backend.isInstanceOf(r, sr.Result)) + return Collections.singleton( + new ComparableTabContributor( + new ResultTab(), + 0, + r, + "Result Properties")); + + // Dependency + if (backend.isInstanceOf(r, sr.Dependency)) + if (backend.hasStatement(r, sr.RefersTo)) + return Collections.singleton( + new ComparableTabContributor( + new ReferenceDependencyTab(), + 0, + r, + "Reference Properties")); + else { + Resource diaConnection = backend.getPossibleObject(r, ModelingResources.getInstance(backend).ConnectionToDiagramConnection); + return Collections.singleton( + new ComparableTabContributor( + new DependencyTab(), + 0, + diaConnection, + "Dependency Properties")); + } + + // Module symbol. Modules in modules-folder are actually symbol resources + if (backend.isInheritedFrom(r, sr.ModuleSymbol)) { + // Find the component resource + r = backend.getPossibleObject(r, mr.SymbolToComponentType); + if(r != null) + return Collections.singleton( + new ComparableTabContributor( + new ModuleTypeTab(), + 0, + r, + "Module Type Properties")); + } - Resource activeExpression = backend.getPossibleObject(r, sr.HasActiveExpression); - Resource expression = null; - if(activeExpression != null) - expression = activeExpression; - else if (backend.hasStatement(r, sr.HasExpressions)){ - Resource expressions = backend.getPossibleObject(r, sr.HasExpressions); - List expressionList = OrderedSetUtils.toList(backend, expressions); - if(expressionList.isEmpty()) { - System.err.println("expressionList is empty for " + r); - return Collections.emptyList(); - } - expression = expressionList.get(0); - } - tabs.add(new ComparableTabContributor( - new EquationTab(), - 3, - r, - "Equation")); - if(expression != null && backend.isInstanceOf(expression, sr.WithLookupExpression)) { - tabs.add(new ComparableTabContributor( - new LookupTableTab(), - 2, - expression, - "Lookup Table")); - } + // Function + if (backend.isInstanceOf(r, sr.SysdynModelicaFunction)) { + tabs.add(new ComparableTabContributor( + new FunctionTab(), + 2, + r, + "Function")); + tabs.add(new ComparableTabContributor( + new ExternalFilesTab(), + 1, + r, + "External files")); + return tabs; + } - tabs.add(new ComparableTabContributor( - new ArrayIndexesTab(), - 1, - r, - "Indexes")); + // Function library + if (backend.isInstanceOf(r, sr.SysdynModelicaFunctionLibrary)) { + Object s = AdaptionUtils.adaptToSingle(selection, ISelection.class); + if(s == null) + s = r; + // give either variable or the actual resource + return Collections.singleton(new ComparableTabContributor( + new FunctionLibraryTab(), + 2, + s, + "Function library")); + } - tabs.add(new ComparableTabContributor( - new VariableInformationTab(), - 0, - r, - "Additional Information")); - return tabs; - } - if (backend.isInstanceOf(r, sr.Input)) { - tabs.add(new ComparableTabContributor( - new InputVariableTab(), - 2, - r, - "Input")); - - tabs.add(new ComparableTabContributor( - new ArrayIndexesTab(), - 1, - r, - "Indexes")); + // Chart + if (backend.isInstanceOf(r, jfree.Chart)) { + tabs.add(new ComparableTabContributor( + new GeneralChartPropertiesTab(), + 10, + r, + "General")); + tabs.add(new ComparableTabContributor( + new ChartAxisAndVariablesTab(), + 9, + r, + "Axis and Variables")); + tabs.add(new ComparableTabContributor( + new ChartTab(), + 1, + r, + "Chart")); + return tabs; + } - tabs.add(new ComparableTabContributor( - new VariableInformationTab(), - 0, - r, - "Additional Information")); - return tabs; - } - if (backend.isInstanceOf(r, sr.Enumeration)) { - Object s = AdaptionUtils.adaptToSingle(selection, ISelection.class); - if(s == null) - s = r; - // give either variable or the actual resource - return Collections.singleton(new ComparableTabContributor( - new EnumerationTab(), - 2, - s, - "Enumeration")); - } - if ( backend.isInstanceOf(r, sr.Configuration) || backend.isInstanceOf(r, sr.SysdynModel)) { - if(!backend.isInstanceOf(r, sr.SysdynModel)) - r = backend.getPossibleObject(r, SimulationResource.getInstance(backend).IsConfigurationOf); - if (r != null) - return Collections.singleton( - new ComparableTabContributor( - new ConfigurationTab(), - 0, - r, - "Model Properties")); - } - if (backend.isInstanceOf(r, sr.Module)){ - tabs.add(new ComparableTabContributor( - new ModuleTab(), - 3, - r, - "Module Properties")); - tabs.add(new ComparableTabContributor( - new ModuleInputTab(), - 2, - r, - "Inputs")); - tabs.add(new ComparableTabContributor( - new ModuleOutputTab(), - 1, - r, - "Outputs")); - return tabs; - } - if (backend.isInstanceOf(r, sr.PlaybackExperiment)) - return Collections.singleton( - new ComparableTabContributor( - new PlaybackExperimentTab(), - 0, - r, - "Experiment Properties")); - if (backend.isInstanceOf(r, simu.Experiment)) - return Collections.singleton( - new ComparableTabContributor( - new ExperimentTab(), - 0, - r, - "Experiment Properties")); - if (backend.isInstanceOf(r, sr.SysdynModel)) - return Collections.singleton( - new ComparableTabContributor( - new ModelTab(), - 0, - r, - "Model Properties")); - if (backend.isInstanceOf(r, sr.Result)) - return Collections.singleton( - new ComparableTabContributor( - new ResultTab(), - 0, - r, - "Result Properties")); - if (backend.isInstanceOf(r, sr.Dependency)) - if (backend.hasStatement(r, sr.RefersTo)) - return Collections.singleton( - new ComparableTabContributor( - new ReferenceDependencyTab(), - 0, - r, - "Reference Properties")); - else { - Resource diaConnection = backend.getPossibleObject(r, ModelingResources.getInstance(backend).ConnectionToDiagramConnection); - return Collections.singleton( - new ComparableTabContributor( - new DependencyTab(), - 0, - diaConnection, - "Dependency Properties")); - } - if (backend.isInheritedFrom(r, sr.ModuleSymbol)) { - r = backend.getPossibleObject(r, mr.SymbolToComponentType); - if(r != null) - return Collections.singleton( - new ComparableTabContributor( - new ModuleTypeTab(), - 0, - r, - "Module Type Properties")); - } - - if (backend.isInstanceOf(r, sr.SysdynModelicaFunction)) { - tabs.add(new ComparableTabContributor( - new FunctionTab(), - 2, - r, - "Function")); - tabs.add(new ComparableTabContributor( - new ExternalFilesTab(), - 1, - r, - "External files")); - return tabs; - } - - if (backend.isInstanceOf(r, sr.SysdynModelicaFunctionLibrary)) { - Object s = AdaptionUtils.adaptToSingle(selection, ISelection.class); - if(s == null) - s = r; - // give either variable or the actual resource - return Collections.singleton(new ComparableTabContributor( - new FunctionLibraryTab(), - 2, - s, - "Function library")); - } - - } catch (ServiceException e) { - e.printStackTrace(); - } catch (ManyObjectsForFunctionalRelationException e) { - e.printStackTrace(); - } catch (DatabaseException e) { - e.printStackTrace(); - } - return Collections.emptyList(); - } + } catch (ServiceException e) { + e.printStackTrace(); + } catch (ManyObjectsForFunctionalRelationException e) { + e.printStackTrace(); + } catch (DatabaseException e) { + e.printStackTrace(); + } + return Collections.emptyList(); + } } diff --git a/org.simantics.sysdyn.ui/src/org/simantics/sysdyn/ui/trend/ChartPanel.java b/org.simantics.sysdyn.ui/src/org/simantics/sysdyn/ui/trend/ChartPanel.java index 1bf12a70..08a5c629 100644 --- a/org.simantics.sysdyn.ui/src/org/simantics/sysdyn/ui/trend/ChartPanel.java +++ b/org.simantics.sysdyn.ui/src/org/simantics/sysdyn/ui/trend/ChartPanel.java @@ -23,8 +23,6 @@ import org.eclipse.swt.dnd.DropTarget; import org.eclipse.swt.dnd.DropTargetAdapter; import org.eclipse.swt.dnd.DropTargetEvent; import org.eclipse.swt.dnd.Transfer; -import org.eclipse.swt.events.MouseAdapter; -import org.eclipse.swt.events.MouseEvent; import org.eclipse.swt.graphics.Point; import org.eclipse.swt.graphics.Rectangle; import org.eclipse.swt.widgets.Composite; @@ -33,7 +31,13 @@ import org.eclipse.ui.IMemento; import org.eclipse.ui.IViewSite; import org.eclipse.ui.PartInitException; import org.eclipse.ui.part.ViewPart; +import org.simantics.db.ReadGraph; +import org.simantics.db.Resource; +import org.simantics.db.common.request.ReadRequest; +import org.simantics.db.exception.DatabaseException; +import org.simantics.sysdyn.JFreeChartResource; import org.simantics.sysdyn.ui.Activator; +import org.simantics.ui.SimanticsUI; import org.simantics.ui.dnd.LocalObjectTransfer; /** @@ -48,22 +52,19 @@ public class ChartPanel extends ViewPart { private Composite body; private ScrolledComposite sc; - + private IDialogSettings settings; - private ArrayList expandedCharts; - private ArrayList minimizedCharts; - -// private ArrayList elements; -// private ArrayList