--- /dev/null
+<?xml version="1.0" encoding="UTF-8"?>\r
+<classpath>\r
+ <classpathentry kind="con" path="org.eclipse.jdt.launching.JRE_CONTAINER/org.eclipse.jdt.internal.debug.ui.launcher.StandardVMType/JavaSE-1.6"/>\r
+ <classpathentry kind="con" path="org.eclipse.pde.core.requiredPlugins"/>\r
+ <classpathentry kind="src" path="src"/>\r
+ <classpathentry kind="output" path="bin"/>\r
+</classpath>\r
--- /dev/null
+syntax: regexp\r
+^bin/\r
+\r
+syntax: glob\r
+*.svn/*
\ No newline at end of file
--- /dev/null
+<?xml version="1.0" encoding="UTF-8"?>\r
+<projectDescription>\r
+ <name>org.simantics.sysdyn.ui</name>\r
+ <comment></comment>\r
+ <projects>\r
+ </projects>\r
+ <buildSpec>\r
+ <buildCommand>\r
+ <name>org.eclipse.jdt.core.javabuilder</name>\r
+ <arguments>\r
+ </arguments>\r
+ </buildCommand>\r
+ <buildCommand>\r
+ <name>org.eclipse.pde.ManifestBuilder</name>\r
+ <arguments>\r
+ </arguments>\r
+ </buildCommand>\r
+ <buildCommand>\r
+ <name>org.eclipse.pde.SchemaBuilder</name>\r
+ <arguments>\r
+ </arguments>\r
+ </buildCommand>\r
+ </buildSpec>\r
+ <natures>\r
+ <nature>org.eclipse.pde.PluginNature</nature>\r
+ <nature>org.eclipse.jdt.core.javanature</nature>\r
+ </natures>\r
+</projectDescription>\r
--- /dev/null
+#Tue Nov 10 13:35:16 EET 2009\r
+eclipse.preferences.version=1\r
+org.eclipse.jdt.core.compiler.codegen.inlineJsrBytecode=enabled\r
+org.eclipse.jdt.core.compiler.codegen.targetPlatform=1.6\r
+org.eclipse.jdt.core.compiler.compliance=1.6\r
+org.eclipse.jdt.core.compiler.problem.assertIdentifier=error\r
+org.eclipse.jdt.core.compiler.problem.enumIdentifier=error\r
+org.eclipse.jdt.core.compiler.source=1.6\r
--- /dev/null
+Manifest-Version: 1.0
+Bundle-ManifestVersion: 2
+Bundle-Name: Ui
+Bundle-SymbolicName: org.simantics.sysdyn.ui;singleton:=true
+Bundle-Version: 1.0.0.qualifier
+Bundle-RequiredExecutionEnvironment: JavaSE-1.6
+Require-Bundle: org.simantics.h2d;bundle-version="1.0.0",
+ org.simantics.db;bundle-version="0.6.2",
+ org.simantics.db.management;bundle-version="0.6.2",
+ org.simantics.layer0.utils;bundle-version="0.6.2",
+ org.simantics.scenegraph;bundle-version="1.0.0",
+ org.junit4;bundle-version="4.5.0",
+ org.simantics.ui;bundle-version="1.0.0",
+ org.eclipse.ui;bundle-version="3.5.0",
+ org.eclipse.core.runtime;bundle-version="3.5.0",
+ org.simantics.objmap;bundle-version="0.1.0",
+ org.simantics.sysdyn;bundle-version="1.0.0",
+ org.simantics.project;bundle-version="1.0.0",
+ org.apache.log4j;bundle-version="1.2.15"
+Bundle-Activator: org.simantics.sysdyn.ui.Activator
+Bundle-ActivationPolicy: lazy
--- /dev/null
+<?xml version="1.0" encoding="UTF-8"?>\r
+<adapters>\r
+ <target\r
+ interface="org.simantics.project.features.IProjectFeature">\r
+ <resource\r
+ uri="http://www.simantics.org/Sysdyn#SysdynProject"\r
+ class="org.simantics.sysdyn.ui.project.SysdynProject" />\r
+ </target>\r
+</adapters>
\ No newline at end of file
--- /dev/null
+source.. = src/\r
+output.. = bin/\r
+bin.includes = META-INF/,\\r
+ .,\\r
+ plugin.xml\r
--- /dev/null
+Possible connections between sysdyn elements:\r
+\r
+{| border="1"\r
+| \r
+| \r
+| colspan="4", align="center" | To\r
+|-\r
+| \r
+| \r
+| Auxiliary\r
+| Stock\r
+| Valve\r
+| Cloud\r
+|-\r
+| rowspan="4" | From\r
+| Auxiliary\r
+| dependency\r
+| -\r
+| dependency\r
+| -\r
+|-\r
+| Stock\r
+| dependency\r
+| -\r
+| flow / dependency\r
+| -\r
+|-\r
+| Valve\r
+| dependency\r
+| flow\r
+| dependency (?)\r
+| flow\r
+|-\r
+| Cloud\r
+| -\r
+| -\r
+| flow\r
+| -\r
+|}\r
+\r
+Connection actions between sysdyn elements:\r
+\r
+{| border="1"\r
+| \r
+| \r
+| colspan="4", align="center" | To\r
+|-\r
+| \r
+| \r
+| Auxiliary\r
+| Stock\r
+| Valve\r
+| Cloud\r
+|-\r
+| rowspan="4" | From\r
+| Auxiliary\r
+| dependency\r
+| -\r
+| dependency\r
+| -\r
+|-\r
+| Stock\r
+| dependency\r
+| valve + 2 x flow\r
+| flow / dependency\r
+| valve + 2 x flow\r
+|-\r
+| Valve\r
+| dependency\r
+| flow\r
+| dependency (?)\r
+| flow\r
+|-\r
+| Cloud\r
+| -\r
+| valve + 2 x flow\r
+| flow\r
+| -\r
+|}
\ No newline at end of file
--- /dev/null
+<?xml version="1.0" encoding="UTF-8"?>\r
+<?eclipse version="3.4"?>\r
+<plugin>\r
+ <extension\r
+ point="org.eclipse.ui.editors">\r
+ <editor\r
+ class="org.simantics.sysdyn.ui.editor.SysdynDiagramEditor"\r
+ default="false"\r
+ id="org.simantics.sysdyn.ui.diagramEditor"\r
+ name="Sysdyn diagram editor">\r
+ </editor>\r
+ </extension>\r
+ <extension\r
+ point="org.simantics.ui.resourceEditorAdapter">\r
+ <adapter\r
+ editorId="org.simantics.sysdyn.ui.diagramEditor"\r
+ priority="3"\r
+ type_uris="http://www.simantics.org/Sysdyn#Configuration">\r
+ </adapter>\r
+ </extension>\r
+ <extension\r
+ point="org.eclipse.ui.views">\r
+ <category\r
+ id="org.simantics.sysdyn.ui.category"\r
+ name="System Dynamics">\r
+ </category>\r
+ <view\r
+ category="org.simantics.sysdyn.ui.category"\r
+ class="org.simantics.sysdyn.ui.modelica.ModelicaView"\r
+ id="org.simantics.sysdyn.ui.modelica.view"\r
+ name="Modelica View"\r
+ restorable="true">\r
+ </view>\r
+ <view\r
+ category="org.simantics.sysdyn.ui.category"\r
+ class="org.simantics.sysdyn.ui.equation.EquationView"\r
+ id="org.simantics.sysdyn.ui.equation.view"\r
+ name="Equation View"\r
+ restorable="true">\r
+ </view>\r
+ </extension>\r
+ <extension\r
+ point="org.eclipse.ui.perspectives">\r
+ <perspective\r
+ class="org.simantics.sysdyn.ui.project.SysdynPerspectiveFactory"\r
+ id="org.simantics.sysdyn.ui.perspective"\r
+ name="System dynamics">\r
+ </perspective>\r
+ </extension>\r
+\r
+</plugin>\r
--- /dev/null
+package org.simantics.sysdyn.ui;\r
+\r
+import org.apache.log4j.BasicConfigurator;\r
+import org.apache.log4j.ConsoleAppender;\r
+import org.apache.log4j.SimpleLayout;\r
+import org.eclipse.ui.plugin.AbstractUIPlugin;\r
+import org.osgi.framework.BundleContext;\r
+\r
+public class Activator extends AbstractUIPlugin {\r
+\r
+ @Override\r
+ public void start(BundleContext context) throws Exception {\r
+ super.start(context);\r
+ ConsoleAppender appender = \r
+ new ConsoleAppender(new SimpleLayout());\r
+ BasicConfigurator.configure(appender);\r
+ }\r
+\r
+}\r
--- /dev/null
+package org.simantics.sysdyn.ui;\r
+\r
+import javax.swing.JFrame;\r
+\r
+import org.simantics.h2d.canvas.EditorCanvas;\r
+import org.simantics.h2d.diagram.Diagram;\r
+import org.simantics.h2d.editor.impl.DiagramEditor;\r
+import org.simantics.h2d.event.handler.DefaultEventHandlers;\r
+import org.simantics.sysdyn.ui.actions.Connect;\r
+import org.simantics.sysdyn.ui.actions.CreateAuxiliary;\r
+import org.simantics.sysdyn.ui.actions.CreateCloud;\r
+import org.simantics.sysdyn.ui.actions.CreateStock;\r
+import org.simantics.sysdyn.ui.actions.CreateValve;\r
+\r
+public class TestDiagramEditor extends JFrame {\r
+\r
+ public TestDiagramEditor() {\r
+ super("Test Canvas");\r
+ setDefaultCloseOperation(JFrame.EXIT_ON_CLOSE);\r
+ \r
+ Diagram diagram = new Diagram();\r
+ final DiagramEditor editor = new DiagramEditor(getRootPane(), diagram);\r
+ \r
+ DefaultEventHandlers.configure(editor);\r
+ editor.addEventHandler(1, "key(A)", new CreateAuxiliary());\r
+ editor.addEventHandler(1, "key(S)", new CreateStock());\r
+ editor.addEventHandler(1, "key(V)", new CreateValve());\r
+ editor.addEventHandler(1, "key(C)", new CreateCloud());\r
+ editor.addEventHandler(1, "drag(alt+left)", new Connect());\r
+ \r
+ getContentPane().add(new EditorCanvas(editor));\r
+\r
+ setSize(800, 600);\r
+ }\r
+ \r
+ public static void main(String[] args) {\r
+ javax.swing.SwingUtilities.invokeLater(new Runnable() {\r
+ public void run() {\r
+ new TestDiagramEditor().setVisible(true);\r
+ }\r
+ }); \r
+ }\r
+ \r
+}\r
--- /dev/null
+package org.simantics.sysdyn.ui.actions;\r
+\r
+import java.awt.geom.Point2D;\r
+import java.awt.geom.Rectangle2D;\r
+\r
+import org.simantics.h2d.editor.IDiagramEditor;\r
+import org.simantics.h2d.element.IElement;\r
+import org.simantics.h2d.element.handler.Connectable;\r
+import org.simantics.h2d.event.DragEvent;\r
+import org.simantics.h2d.event.handler.DragEventHandler;\r
+import org.simantics.h2d.node.RectangleNode;\r
+import org.simantics.scenegraph.g2d.G2DParentNode;\r
+import org.simantics.sysdyn.ui.elements.AuxiliaryElement;\r
+import org.simantics.sysdyn.ui.elements.CloudElement;\r
+import org.simantics.sysdyn.ui.elements.DependencyElement;\r
+import org.simantics.sysdyn.ui.elements.FlowElement;\r
+import org.simantics.sysdyn.ui.elements.StockElement;\r
+import org.simantics.sysdyn.ui.elements.ValveElement;\r
+\r
+public class Connect extends DragEventHandler {\r
+\r
+ Connectable from;\r
+ Connectable to;\r
+ \r
+ RectangleNode fromNode = new RectangleNode(); \r
+ RectangleNode toNode = new RectangleNode();\r
+ \r
+ @Override\r
+ protected boolean begin(IDiagramEditor editor, DragEvent event) {\r
+ for(IElement element : event.pickedElements) {\r
+ Connectable connectable = element.getInterface(Connectable.class);\r
+ if(connectable != null) {\r
+ this.from = connectable;\r
+ return true;\r
+ }\r
+ }\r
+ return false;\r
+ }\r
+ \r
+ @Override\r
+ protected void update(IDiagramEditor editor, DragEvent event) {\r
+ for(IElement element : editor.pickElements(event.current)) {\r
+ Connectable connectable = element.getInterface(Connectable.class); \r
+ if(connectable != null && connectable != from) {\r
+ if(connectable == to)\r
+ return;\r
+ to = connectable;\r
+ \r
+ Rectangle2D bounds = new Rectangle2D.Double(); \r
+ to.getBounds(bounds);\r
+ bounds.setFrame(\r
+ bounds.getX()-2.0, \r
+ bounds.getY()-2.0, \r
+ bounds.getWidth()+4.0, \r
+ bounds.getHeight()+4.0); \r
+ toNode.init(bounds);\r
+ \r
+ editor.requestRepaint(); \r
+ return;\r
+ }\r
+ }\r
+ to = null;\r
+ toNode.init(fromNode.getBounds());\r
+ editor.requestRepaint();\r
+ }\r
+ \r
+ @Override\r
+ protected void end(IDiagramEditor editor, DragEvent event) {\r
+ if(to != null) {\r
+ IElement newElement;\r
+ if(from instanceof ValveElement && to instanceof StockElement)\r
+ newElement = new FlowElement(from, to);\r
+ else if(from instanceof StockElement && to instanceof ValveElement)\r
+ newElement = new FlowElement(from, to);\r
+ else if( (from instanceof StockElement || from instanceof CloudElement) \r
+ && (to instanceof StockElement || to instanceof CloudElement)) {\r
+ Point2D fromOrigo = from.getOrigo();\r
+ Point2D toOrigo = to.getOrigo();\r
+ newElement = new ValveElement(\r
+ 0.5 * (fromOrigo.getX() + toOrigo.getX()),\r
+ 0.5 * (fromOrigo.getY() + toOrigo.getY())\r
+ );\r
+ editor.getDiagram().addElement(new FlowElement(from, (ValveElement)newElement));\r
+ editor.getDiagram().addElement(new FlowElement(to, (ValveElement)newElement));\r
+ }\r
+ else if(from instanceof ValveElement && to instanceof AuxiliaryElement)\r
+ newElement = new DependencyElement(to, from);\r
+ else if(from instanceof AuxiliaryElement && to instanceof StockElement)\r
+ newElement = new DependencyElement(to, from);\r
+ else \r
+ newElement = new DependencyElement(from, to);\r
+ editor.getDiagram().addElement(newElement);\r
+ }\r
+ \r
+ from = null;\r
+ to = null;\r
+ }\r
+ \r
+ @Override\r
+ public void init(G2DParentNode parent) {\r
+ fromNode = parent.addNode(RectangleNode.class);\r
+ toNode = parent.addNode(RectangleNode.class);\r
+ \r
+ Rectangle2D bounds = new Rectangle2D.Double(); \r
+ from.getBounds(bounds);\r
+ bounds.setFrame(\r
+ bounds.getX()-2.0, \r
+ bounds.getY()-2.0, \r
+ bounds.getWidth()+4.0, \r
+ bounds.getHeight()+4.0);\r
+ \r
+ fromNode.init(bounds);\r
+ toNode.init(bounds);\r
+ }\r
+ \r
+ @Override\r
+ public void remove() {\r
+ fromNode.remove();\r
+ toNode.remove();\r
+ }\r
+\r
+}\r
--- /dev/null
+package org.simantics.sysdyn.ui.actions;\r
+\r
+import org.simantics.h2d.editor.IDiagramEditor;\r
+import org.simantics.h2d.event.IEvent;\r
+import org.simantics.h2d.event.ILocatableEvent;\r
+import org.simantics.h2d.event.handler.IEventHandler;\r
+import org.simantics.sysdyn.ui.elements.AuxiliaryElement;\r
+import org.simantics.sysdyn.ui.elements.StockElement;\r
+\r
+public class CreateAuxiliary implements IEventHandler {\r
+\r
+ @Override\r
+ public boolean handle(IDiagramEditor editor, IEvent _event) {\r
+ ILocatableEvent event = (ILocatableEvent)_event;\r
+ AuxiliaryElement element = new AuxiliaryElement("Auxiliary", \r
+ event.getLocation().getX(),\r
+ event.getLocation().getY());\r
+ editor.getDiagram().addElement(element);\r
+ element.beginRenameAction(editor);\r
+ editor.requestRepaint();\r
+ return true;\r
+ }\r
+\r
+}\r
--- /dev/null
+package org.simantics.sysdyn.ui.actions;\r
+\r
+import org.simantics.h2d.editor.IDiagramEditor;\r
+import org.simantics.h2d.event.IEvent;\r
+import org.simantics.h2d.event.ILocatableEvent;\r
+import org.simantics.h2d.event.handler.IEventHandler;\r
+import org.simantics.sysdyn.ui.elements.CloudElement;\r
+\r
+public class CreateCloud implements IEventHandler {\r
+\r
+ @Override\r
+ public boolean handle(IDiagramEditor editor, IEvent _event) {\r
+ ILocatableEvent event = (ILocatableEvent)_event;\r
+ CloudElement element = new CloudElement( \r
+ event.getLocation().getX(),\r
+ event.getLocation().getY());\r
+ editor.getDiagram().addElement(element); \r
+ editor.requestRepaint();\r
+ return true;\r
+ }\r
+\r
+}\r
--- /dev/null
+package org.simantics.sysdyn.ui.actions;\r
+\r
+import org.simantics.h2d.editor.IDiagramEditor;\r
+import org.simantics.h2d.event.IEvent;\r
+import org.simantics.h2d.event.ILocatableEvent;\r
+import org.simantics.h2d.event.handler.IEventHandler;\r
+import org.simantics.sysdyn.ui.elements.StockElement;\r
+\r
+public class CreateStock implements IEventHandler {\r
+\r
+ @Override\r
+ public boolean handle(IDiagramEditor editor, IEvent _event) {\r
+ ILocatableEvent event = (ILocatableEvent)_event;\r
+ StockElement element = new StockElement("Stock", \r
+ event.getLocation().getX(),\r
+ event.getLocation().getY());\r
+ editor.getDiagram().addElement(element);\r
+ element.beginRenameAction(editor);\r
+ editor.requestRepaint();\r
+ return true;\r
+ }\r
+\r
+}\r
--- /dev/null
+package org.simantics.sysdyn.ui.actions;\r
+\r
+import org.simantics.h2d.editor.IDiagramEditor;\r
+import org.simantics.h2d.event.IEvent;\r
+import org.simantics.h2d.event.ILocatableEvent;\r
+import org.simantics.h2d.event.handler.IEventHandler;\r
+import org.simantics.sysdyn.ui.elements.ValveElement;\r
+\r
+public class CreateValve implements IEventHandler {\r
+\r
+ @Override\r
+ public boolean handle(IDiagramEditor editor, IEvent _event) {\r
+ ILocatableEvent event = (ILocatableEvent)_event;\r
+ editor.getDiagram().addElement(new ValveElement( \r
+ event.getLocation().getX(),\r
+ event.getLocation().getY()));\r
+ editor.requestRepaint();\r
+ return true;\r
+ }\r
+\r
+}\r
--- /dev/null
+package org.simantics.sysdyn.ui.editor;\r
+\r
+import java.awt.Frame;\r
+import java.util.ArrayList;\r
+\r
+import javax.swing.SwingUtilities;\r
+\r
+import org.eclipse.jface.viewers.StructuredSelection;\r
+import org.eclipse.swt.SWT;\r
+import org.eclipse.swt.awt.SWT_AWT;\r
+import org.eclipse.swt.widgets.Composite;\r
+import org.eclipse.ui.IEditorInput;\r
+import org.eclipse.ui.IEditorSite;\r
+import org.eclipse.ui.PartInitException;\r
+import org.simantics.db.ReadGraph;\r
+import org.simantics.db.Resource;\r
+import org.simantics.db.Session;\r
+import org.simantics.db.WriteGraph;\r
+import org.simantics.db.common.request.ReadRequest;\r
+import org.simantics.db.common.request.WriteRequest;\r
+import org.simantics.db.exception.DatabaseException;\r
+import org.simantics.h2d.canvas.EditorCanvas;\r
+import org.simantics.h2d.diagram.IDiagram;\r
+import org.simantics.h2d.diagram.IDiagramListener;\r
+import org.simantics.h2d.editor.ISelection;\r
+import org.simantics.h2d.editor.ISelectionListener;\r
+import org.simantics.h2d.editor.impl.DiagramEditor;\r
+import org.simantics.h2d.element.IElement;\r
+import org.simantics.h2d.element.IElementListener;\r
+import org.simantics.h2d.event.handler.DefaultEventHandlers;\r
+import org.simantics.objmap.IMapping;\r
+import org.simantics.objmap.IMappingListener;\r
+import org.simantics.objmap.MappingException;\r
+import org.simantics.objmap.Mappings;\r
+import org.simantics.sysdyn.ui.actions.Connect;\r
+import org.simantics.sysdyn.ui.actions.CreateAuxiliary;\r
+import org.simantics.sysdyn.ui.actions.CreateCloud;\r
+import org.simantics.sysdyn.ui.actions.CreateStock;\r
+import org.simantics.sysdyn.ui.actions.CreateValve;\r
+import org.simantics.ui.SimanticsUI;\r
+import org.simantics.ui.workbench.ResourceEditorPart;\r
+import org.simantics.utils.ui.jface.ActiveSelectionProvider;\r
+\r
+public class SysdynDiagramEditor extends ResourceEditorPart {\r
+\r
+ EditorCanvas canvas;\r
+ IDiagram diagram;\r
+ IMapping mapping;\r
+ Frame frame;\r
+ \r
+ IElementListener elementUpdateListener = new IElementListener() {\r
+ \r
+ @Override\r
+ public void elementUpdated(IElement element) {\r
+ mapping.rangeModified(element);\r
+ }\r
+ \r
+ @Override\r
+ public void elementRemoved(IElement element) {\r
+ }\r
+ };\r
+ \r
+ protected void readDiagram(ReadGraph g) throws DatabaseException {\r
+ SysdynDiagramSchema schema = new SysdynDiagramSchema(g);\r
+ mapping = Mappings.createWithListening(schema);\r
+ \r
+ try {\r
+ diagram = (IDiagram)mapping.map(g, getInputResource());\r
+ } catch (MappingException e) {\r
+ // TODO Auto-generated catch block\r
+ e.printStackTrace();\r
+ }\r
+ \r
+ final Session session = g.getSession();\r
+ mapping.addMappingListener(new IMappingListener() {\r
+ \r
+ @Override\r
+ public void rangeModified() {\r
+ session.asyncRequest(new WriteRequest() {\r
+ \r
+ @Override\r
+ public void perform(WriteGraph graph)\r
+ throws DatabaseException {\r
+ try {\r
+ mapping.updateDomain(graph);\r
+ } catch(Exception e) {\r
+ e.printStackTrace();\r
+ }\r
+ }\r
+ \r
+ });\r
+ }\r
+ \r
+ @Override\r
+ public void domainModified() {\r
+ session.asyncRequest(new ReadRequest() {\r
+ \r
+ @Override\r
+ public void run(ReadGraph graph) throws DatabaseException {\r
+ try {\r
+ mapping.updateRange(graph);\r
+ } catch(Exception e) {\r
+ e.printStackTrace();\r
+ }\r
+ }\r
+ }); \r
+ }\r
+ \r
+ });\r
+ \r
+ diagram.addDiagramListener(new IDiagramListener() {\r
+ \r
+ @Override\r
+ public void elementAdded(IElement element) {\r
+ mapping.rangeModified(diagram); \r
+ element.addListener(elementUpdateListener);\r
+ }\r
+ \r
+ @Override\r
+ public void elementRemoved(IElement element) {\r
+ mapping.rangeModified(diagram); \r
+ }\r
+ \r
+ });\r
+ }\r
+ \r
+ @Override\r
+ public void init(IEditorSite site, IEditorInput input)\r
+ throws PartInitException {\r
+ super.init(site, input);\r
+ \r
+ try {\r
+ SimanticsUI.getSession().syncRequest(new ReadRequest() {\r
+ \r
+ @Override\r
+ public void run(ReadGraph g) throws DatabaseException {\r
+ readDiagram(g); \r
+ }\r
+ \r
+ });\r
+ } catch (DatabaseException e) {\r
+ // TODO Auto-generated catch block\r
+ e.printStackTrace();\r
+ } \r
+ }\r
+ \r
+ @Override\r
+ public void createPartControl(Composite parent) {\r
+ final Composite composite = new Composite(parent, \r
+ SWT.NO_BACKGROUND | SWT.EMBEDDED);\r
+ frame = SWT_AWT.new_Frame(composite);\r
+ \r
+ final ActiveSelectionProvider selectionProvider = new ActiveSelectionProvider();\r
+ getSite().setSelectionProvider(selectionProvider);\r
+ \r
+ SwingUtilities.invokeLater(new Runnable() {\r
+\r
+ @Override\r
+ public void run() { \r
+ DiagramEditor editor = new DiagramEditor(null, diagram); \r
+ \r
+ DefaultEventHandlers.configure(editor);\r
+ editor.addEventHandler(1, "key(A)", new CreateAuxiliary());\r
+ editor.addEventHandler(1, "key(S)", new CreateStock());\r
+ editor.addEventHandler(1, "key(V)", new CreateValve());\r
+ editor.addEventHandler(1, "key(C)", new CreateCloud());\r
+ editor.addEventHandler(1, "drag(alt+left)", new Connect());\r
+ \r
+ canvas = new EditorCanvas(editor);\r
+ frame.add(canvas);\r
+ \r
+ canvas.requestFocus(); \r
+ \r
+ editor.getSelection().addSelectionListener(new ISelectionListener() {\r
+ \r
+ @Override\r
+ public void selectionChanged(ISelection selection) {\r
+ final ArrayList<Resource> resources = new ArrayList<Resource>(selection.size());\r
+ for(IElement element : selection)\r
+ resources.add(mapping.inverseGet(element));\r
+ composite.getDisplay().asyncExec(new Runnable() {\r
+\r
+ @Override\r
+ public void run() {\r
+ selectionProvider.setSelection(new StructuredSelection(resources));\r
+ }\r
+ \r
+ });\r
+ }\r
+ \r
+ });\r
+ }\r
+ \r
+ });\r
+ }\r
+\r
+ @Override\r
+ public void setFocus() {\r
+ if(canvas != null)\r
+ canvas.requestFocus(); \r
+ }\r
+ \r
+ @Override\r
+ public void dispose() {\r
+ mapping.dispose();\r
+ frame.dispose();\r
+ \r
+ }\r
+\r
+}\r
--- /dev/null
+package org.simantics.sysdyn.ui.editor;\r
+\r
+import java.util.Collection;\r
+\r
+import org.simantics.db.Builtins;\r
+import org.simantics.db.ReadGraph;\r
+import org.simantics.db.exception.DatabaseException;\r
+import org.simantics.h2d.diagram.Diagram;\r
+import org.simantics.objmap.rules.MappedElementsRule;\r
+import org.simantics.objmap.rules.domain.RelatedObjectsAccessor;\r
+import org.simantics.objmap.rules.range.FieldAccessor;\r
+import org.simantics.objmap.schema.MappingSchemas;\r
+import org.simantics.objmap.schema.SimpleLinkType;\r
+import org.simantics.objmap.schema.SimpleSchema;\r
+import org.simantics.sysdyn.SysdynResource;\r
+import org.simantics.sysdyn.ui.elements.AuxiliaryElement;\r
+import org.simantics.sysdyn.ui.elements.CloudElement;\r
+import org.simantics.sysdyn.ui.elements.DependencyElement;\r
+import org.simantics.sysdyn.ui.elements.FlowElement;\r
+import org.simantics.sysdyn.ui.elements.StockElement;\r
+import org.simantics.sysdyn.ui.elements.ValveElement;\r
+\r
+public class SysdynDiagramSchema extends SimpleSchema {\r
+\r
+ public SysdynDiagramSchema(ReadGraph g) throws DatabaseException {\r
+ Builtins b = g.getBuiltins();\r
+ SysdynResource sr = SysdynResource.getInstance(g);\r
+ try {\r
+ {\r
+ SimpleLinkType linkType = \r
+ new SimpleLinkType(sr.Configuration, Diagram.class);\r
+ linkType.addRule(new MappedElementsRule(\r
+ new RelatedObjectsAccessor(b.ConsistsOf),\r
+ new FieldAccessor<Collection<Object>>(Diagram.class.getField("elements"))\r
+ ));\r
+ addLinkType(linkType);\r
+ } \r
+ \r
+ addLinkType(MappingSchemas.fromAnnotations(g, AuxiliaryElement.class)); \r
+ addLinkType(MappingSchemas.fromAnnotations(g, StockElement.class));\r
+ addLinkType(MappingSchemas.fromAnnotations(g, CloudElement.class));\r
+ addLinkType(MappingSchemas.fromAnnotations(g, ValveElement.class));\r
+ addLinkType(MappingSchemas.fromAnnotations(g, DependencyElement.class));\r
+ addLinkType(MappingSchemas.fromAnnotations(g, FlowElement.class));\r
+ } catch(NoSuchFieldException e) {\r
+ e.printStackTrace();\r
+ } catch (InstantiationException e) {\r
+ e.printStackTrace();\r
+ } catch (IllegalAccessException e) {\r
+ e.printStackTrace();\r
+ }\r
+ }\r
+ \r
+}\r
--- /dev/null
+package org.simantics.sysdyn.ui.elements;\r
+\r
+import java.awt.geom.Rectangle2D;\r
+\r
+public class Arcs {\r
+\r
+ public static final double PI2 = Math.PI*2.0;\r
+ \r
+ /**\r
+ * Returns angle + 2PI * n such that the\r
+ * result is between -PI and PI.\r
+ */\r
+ public static double normalizeAngle(double angle) {\r
+ return Math.IEEEremainder(angle, PI2);\r
+ }\r
+ \r
+ /**\r
+ * Returns true, if three normalized angles are clockwise oriented.\r
+ */\r
+ public static boolean areClockwiseOrdered(double angle1, double angle2, double angle3) {\r
+ //System.out.println(angle1 + " " + angle2 + " " + angle3);\r
+ return angle1 < angle2 \r
+ ? (angle2 < angle3 || angle3 < angle1)\r
+ : (angle2 < angle3 && angle3 < angle1)\r
+ ;\r
+ }\r
+ \r
+ /**\r
+ * Returns an angle in radians between straight line from (x0,y0) to (x2,y2)\r
+ * and an arc from (x0,y0) to (x2,y2) thru (x1,y1). The angle\r
+ * is measured at (x0,y0) and is between -PI and PI.\r
+ */\r
+ public static double angleOfArc(\r
+ double x0, double y0, \r
+ double x1, double y1,\r
+ double x2, double y2) {\r
+ double dx0 = x1-x0;\r
+ double dy0 = y1-y0;\r
+ double dx1 = x1-x2;\r
+ double dy1 = y1-y2;\r
+ double dx = x2-x0;\r
+ double dy = y2-y0;\r
+ // Length of cross product (p1-p0)x(p2-p0)\r
+ double dd = dx0*dy - dy0*dx; \r
+ \r
+ if(Math.abs(dd) < 1e-6) // Points are (almost) collinear\r
+ return 0.0;\r
+ else { \r
+ // (p1-p0)*(p1-p2) / dd\r
+ double offset = (dx0*dx1 + dy0*dy1) / dd;\r
+ double angle = Math.PI*0.5 - Math.atan(offset);\r
+ if(dd > 0.0)\r
+ angle = angle-Math.PI;\r
+ return angle;\r
+ \r
+ }\r
+ }\r
+ \r
+ private static double updateBestNextAngle(double curAngle, double bestAngle, double newAngle) {\r
+ if(newAngle < curAngle)\r
+ newAngle += PI2;\r
+ if(newAngle < bestAngle)\r
+ return newAngle;\r
+ return bestAngle; \r
+ } \r
+ \r
+ private static double updateBestPrevAngle(double curAngle, double bestAngle, double newAngle) {\r
+ if(newAngle > curAngle)\r
+ newAngle -= PI2;\r
+ if(newAngle > bestAngle)\r
+ return newAngle;\r
+ return bestAngle; \r
+ } \r
+ \r
+ public static double nextIntersectingAngle(double cx, double cy, double r,\r
+ double curAngle, Rectangle2D rect, boolean dir) {\r
+ if(!dir) {\r
+ double bestAngle = curAngle + PI2;\r
+ {\r
+ double dx = rect.getMinX() - cx;\r
+ if(Math.abs(dx) < r) {\r
+ double angle = normalizeAngle(Math.acos(dx / r));\r
+ bestAngle = updateBestNextAngle(curAngle, bestAngle, angle);\r
+ bestAngle = updateBestNextAngle(curAngle, bestAngle, -angle);\r
+ }\r
+ }\r
+ {\r
+ double dx = rect.getMaxX() - cx;\r
+ if(Math.abs(dx) < r) {\r
+ double angle = normalizeAngle(Math.acos(dx / r));\r
+ bestAngle = updateBestNextAngle(curAngle, bestAngle, angle);\r
+ bestAngle = updateBestNextAngle(curAngle, bestAngle, -angle);\r
+ }\r
+ }\r
+ {\r
+ double dy = cy - rect.getMinY();\r
+ if(Math.abs(dy) < r) {\r
+ double angle = Math.asin(dy / r);\r
+ bestAngle = updateBestNextAngle(curAngle, bestAngle, angle);\r
+ bestAngle = updateBestNextAngle(curAngle, bestAngle, \r
+ normalizeAngle(Math.PI-angle));\r
+ }\r
+ }\r
+ {\r
+ double dy = cy - rect.getMaxY();\r
+ if(Math.abs(dy) < r) {\r
+ double angle = Math.asin(dy / r);\r
+ bestAngle = updateBestNextAngle(curAngle, bestAngle, angle);\r
+ bestAngle = updateBestNextAngle(curAngle, bestAngle, \r
+ normalizeAngle(Math.PI-angle));\r
+ }\r
+ }\r
+ return normalizeAngle(bestAngle);\r
+ } \r
+ else {\r
+ double bestAngle = curAngle - PI2;\r
+ {\r
+ double dx = rect.getMinX() - cx;\r
+ if(Math.abs(dx) < r) {\r
+ double angle = normalizeAngle(Math.acos(dx / r));\r
+ bestAngle = updateBestPrevAngle(curAngle, bestAngle, angle);\r
+ bestAngle = updateBestPrevAngle(curAngle, bestAngle, -angle);\r
+ }\r
+ }\r
+ {\r
+ double dx = rect.getMaxX() - cx;\r
+ if(Math.abs(dx) < r) {\r
+ double angle = normalizeAngle(Math.acos(dx / r));\r
+ bestAngle = updateBestPrevAngle(curAngle, bestAngle, angle);\r
+ bestAngle = updateBestPrevAngle(curAngle, bestAngle, -angle);\r
+ }\r
+ }\r
+ {\r
+ double dy = cy - rect.getMinY();\r
+ if(Math.abs(dy) < r) {\r
+ double angle = Math.asin(dy / r);\r
+ bestAngle = updateBestPrevAngle(curAngle, bestAngle, angle);\r
+ bestAngle = updateBestPrevAngle(curAngle, bestAngle, \r
+ normalizeAngle(Math.PI-angle));\r
+ }\r
+ }\r
+ {\r
+ double dy = cy - rect.getMaxY();\r
+ if(Math.abs(dy) < r) {\r
+ double angle = Math.asin(dy / r);\r
+ bestAngle = updateBestPrevAngle(curAngle, bestAngle, angle);\r
+ bestAngle = updateBestPrevAngle(curAngle, bestAngle, \r
+ normalizeAngle(Math.PI-angle));\r
+ }\r
+ }\r
+ return normalizeAngle(bestAngle);\r
+ }\r
+ }\r
+}\r
--- /dev/null
+package org.simantics.sysdyn.ui.elements;\r
+\r
+import org.simantics.objmap.annotations.GraphType;\r
+\r
+@GraphType("http://www.simantics.org/Sysdyn#Auxiliary")\r
+public class AuxiliaryElement extends TextElement {\r
+ \r
+ public AuxiliaryElement() { \r
+ }\r
+ \r
+ public AuxiliaryElement(String label, double x, double y) {\r
+ super(label, x, y);\r
+ }\r
+ \r
+}\r
--- /dev/null
+package org.simantics.sysdyn.ui.elements;\r
+\r
+import java.awt.BasicStroke;\r
+import java.awt.Color;\r
+import java.awt.geom.Path2D;\r
+\r
+import org.simantics.h2d.element.handler.Connectable;\r
+import org.simantics.h2d.node.ShapeNode;\r
+import org.simantics.objmap.annotations.GraphType;\r
+import org.simantics.scenegraph.g2d.G2DParentNode;\r
+\r
+@GraphType("http://www.simantics.org/Sysdyn#Cloud")\r
+public class CloudElement extends RectangularElement implements Connectable {\r
+ \r
+ public static final double CLOUD_SIZE_X = 5.0;\r
+ public static final double CLOUD_SIZE_Y = 3.0;\r
+ \r
+ public static final double CLOUD_CURVES = 7;\r
+ \r
+ ShapeNode cloudNode;\r
+ boolean rotated = false;\r
+ \r
+ public CloudElement() { \r
+ }\r
+ \r
+ public CloudElement(double x, double y) {\r
+ this.posX = x;\r
+ this.posY = y; \r
+ }\r
+\r
+ @Override\r
+ public void init(G2DParentNode parent) {\r
+ cloudNode = parent.addNode(ShapeNode.class);\r
+ cloudNode.setColor(Color.BLACK);\r
+ cloudNode.setScaleStroke(true);\r
+ cloudNode.setStroke(new BasicStroke(1));\r
+ update();\r
+ }\r
+\r
+ @Override\r
+ public void remove() {\r
+ cloudNode.remove();\r
+ }\r
+ \r
+ @Override\r
+ protected void update() {\r
+ //bounds.setFrame(posX-CLOUD_SIZE_X, posY-CLOUD_SIZE_Y, 2.0*CLOUD_SIZE_X, 2.0*CLOUD_SIZE_Y);\r
+ Path2D path = new Path2D.Double(); \r
+ double ox = CLOUD_SIZE_X;\r
+ double oy = 0.0;\r
+ path.moveTo(posX+ox, posY+oy); \r
+ for(int i=1;i<CLOUD_CURVES+1;++i) {\r
+ double angle = (Math.PI * 2.0 / CLOUD_CURVES) * i;\r
+ double x = Math.cos(angle) * CLOUD_SIZE_X;\r
+ double y = Math.sin(angle) * CLOUD_SIZE_Y;\r
+ path.curveTo(\r
+ posX+(2*ox + x)*0.5, posY+(2*oy + y)*0.5,\r
+ posX+(ox + 2*x)*0.5, posY+(oy + 2*y)*0.5,\r
+ posX + x, posY + y);\r
+ ox = x;\r
+ oy = y;\r
+ }\r
+ cloudNode.setShape(path);\r
+ bounds = path.getBounds2D();\r
+ super.update();\r
+ }\r
+\r
+ public boolean isRotated() {\r
+ return rotated;\r
+ }\r
+}\r
--- /dev/null
+package org.simantics.sysdyn.ui.elements;\r
+\r
+import java.awt.BasicStroke;\r
+import java.awt.Shape;\r
+import java.awt.geom.Arc2D;\r
+import java.awt.geom.Path2D;\r
+import java.awt.geom.Rectangle2D;\r
+\r
+import org.simantics.h2d.editor.IDiagramEditor;\r
+import org.simantics.h2d.element.Element;\r
+import org.simantics.h2d.element.IElement;\r
+import org.simantics.h2d.element.IElementListener;\r
+import org.simantics.h2d.element.handler.Connectable;\r
+import org.simantics.h2d.event.DragEvent;\r
+import org.simantics.h2d.event.handler.DragEventHandler;\r
+import org.simantics.h2d.event.handler.IEventHandler;\r
+import org.simantics.h2d.node.FilledShapeNode;\r
+import org.simantics.h2d.node.ShapeNode;\r
+import org.simantics.objmap.annotations.GraphType;\r
+import org.simantics.objmap.annotations.RelatedElement;\r
+import org.simantics.objmap.annotations.RelatedValue;\r
+import org.simantics.scenegraph.g2d.G2DParentNode;\r
+\r
+@GraphType("http://www.simantics.org/Sysdyn#Dependency")\r
+public class DependencyElement extends Element implements IElementListener {\r
+ \r
+ /*\r
+ * Total length of the arrow is ARROW_LENGTH1 + ARROW_LENGTH2\r
+ */\r
+ public static double ARROW_LENGTH1 = 0.2;\r
+ public static double ARROW_LENGTH2 = 3.0;\r
+ public static double ARROW_WIDTH = 1.8;\r
+ \r
+ @RelatedElement("http://www.simantics.org/Sysdyn#HasTail")\r
+ Connectable tail;\r
+ @RelatedElement("http://www.simantics.org/Sysdyn#HasHead")\r
+ Connectable head;\r
+ @RelatedValue("http://www.simantics.org/Sysdyn#HasAngle")\r
+ double angle = 0.1;\r
+\r
+ // Auxiliary \r
+ double angle0;\r
+ double angle1;\r
+ double cx;\r
+ double cy;\r
+ double r;\r
+ \r
+ // Scene graph\r
+ ShapeNode arcNode;\r
+ FilledShapeNode arrowNode;\r
+ \r
+ public DependencyElement() { \r
+ }\r
+ \r
+ public DependencyElement(Connectable tail, Connectable head) {\r
+ super();\r
+ this.tail = tail;\r
+ this.head = head;\r
+ }\r
+ \r
+ @Override\r
+ public void elementUpdated(IElement element) {\r
+ update(); \r
+ } \r
+\r
+ @Override\r
+ public void remove() {\r
+ arcNode.remove();\r
+ arrowNode.remove();\r
+ tail.removeListener(this);\r
+ head.removeListener(this);\r
+ super.remove();\r
+ }\r
+\r
+ @Override\r
+ public void init(G2DParentNode parent) {\r
+ tail.addListener(this);\r
+ head.addListener(this);\r
+ \r
+ arcNode = parent.addNode(ShapeNode.class);\r
+ arcNode.setScaleStroke(true);\r
+ arcNode.setStroke(new BasicStroke(1));\r
+ arrowNode = parent.addNode(FilledShapeNode.class);\r
+ update();\r
+ }\r
+ \r
+ protected Shape createArrow(double x, double y, double dx, double dy) {\r
+ Path2D path = new Path2D.Double();\r
+ path.moveTo(x+ARROW_LENGTH1*dx, y+ARROW_LENGTH1*dy);\r
+ x -= ARROW_LENGTH2*dx;\r
+ y -= ARROW_LENGTH2*dy;\r
+ path.lineTo(x-ARROW_WIDTH*dy, y+ARROW_WIDTH*dx);\r
+ path.lineTo(x+ARROW_WIDTH*dy, y-ARROW_WIDTH*dx);\r
+ path.closePath();\r
+ return path;\r
+ }\r
+ \r
+ protected void updateSceneGraph() {\r
+ double x0 = tail.getOrigo().getX();\r
+ double y0 = tail.getOrigo().getY();\r
+ double x1 = head.getOrigo().getX();\r
+ double y1 = head.getOrigo().getY();\r
+ \r
+ double offset = \r
+ Math.abs(angle) < 1.0e-6\r
+ ? 1e3 * Math.signum(angle)\r
+ : Math.tan(Math.PI*0.5-angle)*0.5;\r
+ cx = 0.5*(x0+x1) + offset * (y1-y0);\r
+ cy = 0.5*(y0+y1) + offset * (x0-x1);\r
+ double dx0 = x0 - cx;\r
+ double dy0 = y0 - cy;\r
+ double dx1 = x1 - cx;\r
+ double dy1 = y1 - cy;\r
+ r = Math.sqrt(dx0*dx0 + dy0*dy0);\r
+ Rectangle2D bounds = new Rectangle2D.Double();\r
+ tail.getBounds(bounds);\r
+ angle0 = Arcs.nextIntersectingAngle(cx, cy, r, \r
+ Math.atan2(-dy0, dx0), bounds, angle < 0.0);\r
+ head.getBounds(bounds);\r
+ angle1 = Arcs.nextIntersectingAngle(cx, cy, r, \r
+ Math.atan2(-dy1, dx1), bounds, angle > 0.0);\r
+ double extent = angle1-angle0;\r
+ //double arcAngle = angle0;\r
+ if(angle < 0.0) {\r
+ double temp = angle0; \r
+ angle0 = angle1;\r
+ angle1 = temp;\r
+ extent = -extent;\r
+ } \r
+ if(extent < 0)\r
+ extent += Math.PI*2.0;\r
+ else if(extent >= 360.0)\r
+ extent -= Math.PI*2.0;\r
+ Shape shape = new Arc2D.Double(cx-r, cy-r, 2*r, 2*r, \r
+ Math.toDegrees(angle0), \r
+ Math.toDegrees(extent), \r
+ Arc2D.OPEN); \r
+ arcNode.setShape(shape);\r
+ \r
+ double xx = Math.cos(angle > 0.0 ? angle1 : angle0);\r
+ double yy = -Math.sin(angle > 0.0 ? angle1 : angle0);\r
+ Shape arrowShape = createArrow(cx + r*xx, cy + r*yy, \r
+ angle < 0.0 ? -yy : yy, \r
+ angle > 0.0 ? -xx : xx);\r
+ arrowNode.setShape(arrowShape);\r
+ }\r
+ \r
+ public void update() {\r
+ if(arcNode != null)\r
+ updateSceneGraph();\r
+ super.update();\r
+ }\r
+ \r
+ @Override\r
+ public void getBounds(Rectangle2D bounds) {\r
+ bounds.setFrame(arcNode.getBounds());\r
+ }\r
+\r
+ @Override\r
+ public boolean hitTest(double x, double y, double tolerance) {\r
+ double dx = x-cx;\r
+ double dy = y-cy;\r
+ double dist = dx*dx + dy*dy;\r
+ if(dist < (r+tolerance)*(r+tolerance) &&\r
+ dist > (r-tolerance)*(r-tolerance)) {\r
+ double angle = Arcs.normalizeAngle(Math.atan2(-dy, dx));\r
+ if(Arcs.areClockwiseOrdered(angle0, angle, angle1))\r
+ return true;\r
+ }\r
+ return false;\r
+ }\r
+ \r
+ class EventHandler extends DragEventHandler {\r
+ @Override\r
+ protected boolean begin(IDiagramEditor editor, DragEvent event) {\r
+ return event.startModifiers.equals("left");\r
+ }\r
+ \r
+ @Override\r
+ protected void update(IDiagramEditor editor, DragEvent event) {\r
+ angle = Arcs.angleOfArc(\r
+ tail.getOrigo().getX(), tail.getOrigo().getY(), \r
+ event.current.getX(), event.current.getY(), \r
+ head.getOrigo().getX(), head.getOrigo().getY()\r
+ );\r
+ DependencyElement.this.update();\r
+ editor.requestRepaint();\r
+ }\r
+ }\r
+\r
+ @Override\r
+ public <T> T getInterface(Class<T> clazz) {\r
+ if(clazz == IEventHandler.class) \r
+ return (T)new EventHandler();\r
+ return super.getInterface(clazz);\r
+ }\r
+\r
+ @Override\r
+ public void elementRemoved(IElement element) {\r
+ remove(); \r
+ }\r
+ \r
+}\r
--- /dev/null
+package org.simantics.sysdyn.ui.elements;\r
+\r
+import java.awt.Shape;\r
+import java.awt.geom.Path2D;\r
+import java.awt.geom.Rectangle2D;\r
+\r
+import org.simantics.h2d.element.Element;\r
+import org.simantics.h2d.element.IElement;\r
+import org.simantics.h2d.element.IElementListener;\r
+import org.simantics.h2d.element.handler.Connectable;\r
+import org.simantics.h2d.node.FilledShapeNode;\r
+import org.simantics.h2d.node.ShapeNode;\r
+import org.simantics.objmap.annotations.GraphType;\r
+import org.simantics.objmap.annotations.RelatedElement;\r
+import org.simantics.scenegraph.g2d.G2DParentNode;\r
+\r
+@GraphType("http://www.simantics.org/Sysdyn#Flow")\r
+public class FlowElement extends Element implements IElementListener {\r
+\r
+ public static double ARROW_LENGTH1 = 0.2;\r
+ public static double ARROW_LENGTH2 = 3.0;\r
+ public static double ARROW_WIDTH = 1.8;\r
+ \r
+ static final double OFFSET = 1.5;\r
+ \r
+ /*\r
+ * Total length of the arrow is ARROW_LENGTH1 + ARROW_LENGTH2\r
+ */ \r
+ @RelatedElement("http://www.simantics.org/Sysdyn#HasTail")\r
+ Connectable tail;\r
+ @RelatedElement("http://www.simantics.org/Sysdyn#HasHead")\r
+ Connectable head;\r
+ double angle = 0.1;\r
+\r
+ // Auxiliary \r
+ double angle0;\r
+ double angle1;\r
+ double cx;\r
+ double cy;\r
+ double r;\r
+ \r
+ // Scene graph\r
+ ShapeNode lineNode1;\r
+ ShapeNode lineNode2;\r
+ FilledShapeNode arrowNode;\r
+ \r
+ public FlowElement() { \r
+ }\r
+ \r
+ public FlowElement(Connectable tail, Connectable head) {\r
+ super();\r
+ this.tail = tail;\r
+ this.head = head;\r
+ }\r
+ \r
+ @Override\r
+ public void elementUpdated(IElement element) {\r
+ update(); \r
+ } \r
+\r
+ @Override\r
+ public void remove() {\r
+ lineNode1.remove();\r
+ lineNode2.remove();\r
+ tail.removeListener(this);\r
+ head.removeListener(this);\r
+ }\r
+\r
+ @Override\r
+ public void init(G2DParentNode parent) {\r
+ tail.addListener(this);\r
+ head.addListener(this);\r
+ \r
+ lineNode1 = parent.addNode(ShapeNode.class);\r
+ lineNode1.setScaleStroke(true);\r
+ lineNode2 = parent.addNode(ShapeNode.class);\r
+ lineNode2.setScaleStroke(true);\r
+ \r
+ //arrowNode = parent.addNode(FilledShapeNode.class);\r
+ update();\r
+ }\r
+ \r
+ protected Shape createArrow(double x, double y, double dx, double dy) {\r
+ Path2D path = new Path2D.Double();\r
+ path.moveTo(x+ARROW_LENGTH1*dx, y+ARROW_LENGTH1*dy);\r
+ x -= ARROW_LENGTH2*dx;\r
+ y -= ARROW_LENGTH2*dy;\r
+ path.lineTo(x-ARROW_WIDTH*dy, y+ARROW_WIDTH*dx);\r
+ path.lineTo(x+ARROW_WIDTH*dy, y-ARROW_WIDTH*dx);\r
+ path.closePath();\r
+ return path;\r
+ }\r
+ \r
+ // TODO\r
+ \r
+ private void draw(boolean vertical, double ... coordinates) {\r
+ lineNode1.setShape(Flows.createOffsetLine(vertical, OFFSET, coordinates));\r
+ lineNode2.setShape(Flows.createOffsetLine(vertical, -OFFSET, coordinates));\r
+ }\r
+\r
+ private void draw(Connectable valve, Connectable node) {\r
+ double x0 = valve.getOrigo().getX();\r
+ double y0 = valve.getOrigo().getY(); \r
+ double x1 = node.getOrigo().getX();\r
+ double y1 = node.getOrigo().getY();\r
+\r
+ Rectangle2D rect = new Rectangle2D.Double();\r
+ node.getBounds(rect);\r
+ if( ((ValveElement)valve).rotated ) {\r
+ if(y1 > y0)\r
+ y0 += OFFSET;\r
+ else\r
+ y0 -= OFFSET;\r
+ if(rect.getMinX() <= x0 && rect.getMaxX() >= x0) {\r
+ if(y1 > y0)\r
+ draw(true, y0, x0, rect.getMinY());\r
+ else\r
+ draw(true, y0, x0, rect.getMaxY());\r
+ }\r
+ else {\r
+ if(x1 > x0)\r
+ draw(true, y0, x0, y1, rect.getMinX());\r
+ else\r
+ draw(true, y0, x0, y1, rect.getMaxX());\r
+ }\r
+ }\r
+ else {\r
+ if(x1 > x0)\r
+ x0 += OFFSET;\r
+ else\r
+ x0 -= OFFSET;\r
+ if(rect.getMinY() <= y0 && rect.getMaxY() >= y0) {\r
+ if(x1 > x0)\r
+ draw(false, x0, y0, rect.getMinX());\r
+ else\r
+ draw(false, x0, y0, rect.getMaxX());\r
+ }\r
+ else {\r
+ if(y1 > y0)\r
+ draw(false, x0, y0, x1, rect.getMinY());\r
+ else\r
+ draw(false, x0, y0, x1, rect.getMaxY());\r
+ }\r
+ }\r
+ }\r
+\r
+ protected void updateSceneGraph() {\r
+ if(tail instanceof ValveElement)\r
+ draw(tail, head);\r
+ else if(head instanceof ValveElement)\r
+ draw(head, tail);\r
+ }\r
+ \r
+ // TODO\r
+ \r
+ public void update() {\r
+ if(lineNode1 != null)\r
+ updateSceneGraph();\r
+ super.update();\r
+ }\r
+ \r
+ @Override\r
+ public void getBounds(Rectangle2D bounds) {\r
+ bounds.setFrame(lineNode1.getBounds());\r
+ }\r
+\r
+ @Override\r
+ public boolean hitTest(double x, double y, double tolerance) {\r
+ // TODO\r
+ return false;\r
+ }\r
+\r
+ @Override\r
+ public <T> T getInterface(Class<T> clazz) { \r
+ return super.getInterface(clazz);\r
+ }\r
+\r
+ @Override\r
+ public void elementRemoved(IElement element) {\r
+ remove(); \r
+ }\r
+ \r
+}\r
--- /dev/null
+package org.simantics.sysdyn.ui.elements;\r
+\r
+import java.awt.geom.Path2D;\r
+\r
+public class Flows {\r
+ public static Path2D createLine(boolean vertical, double ... coordinates) {\r
+ Path2D path = new Path2D.Double();\r
+ if(vertical)\r
+ path.moveTo(coordinates[1], coordinates[0]);\r
+ else\r
+ path.moveTo(coordinates[0], coordinates[1]);\r
+ for(int i=2;i<coordinates.length;++i, vertical = !vertical) {\r
+ if(vertical)\r
+ path.lineTo(coordinates[i-1], coordinates[i]);\r
+ else\r
+ path.lineTo(coordinates[i], coordinates[i-1]);\r
+ }\r
+ return path;\r
+ }\r
+ \r
+ public static Path2D createOffsetLine(boolean vertical, double offset, double ... coordinates) {\r
+ double[] newCoordinats = new double[coordinates.length];\r
+ newCoordinats[0] = coordinates[0];\r
+ newCoordinats[coordinates.length-1] = coordinates[coordinates.length-1];\r
+ for(int i=1;i<coordinates.length-1;++i) {\r
+ if(coordinates[i-1] < coordinates[i+1] ^ (i&1)==1)\r
+ newCoordinats[i] = coordinates[i]+offset;\r
+ else\r
+ newCoordinats[i] = coordinates[i]-offset;\r
+ }\r
+ return createLine(vertical, newCoordinats);\r
+ }\r
+}
\ No newline at end of file
--- /dev/null
+package org.simantics.sysdyn.ui.elements;\r
+\r
+import java.awt.geom.Point2D;\r
+import java.awt.geom.Rectangle2D;\r
+\r
+import org.simantics.h2d.element.Element;\r
+import org.simantics.h2d.element.handler.Movable;\r
+import org.simantics.objmap.annotations.RelatedValue;\r
+\r
+/**\r
+ * Base class of elements that have rectangular shape (for hit testing)\r
+ * and which can be moved.\r
+ * @author Hannu Niemistö\r
+ */\r
+public abstract class RectangularElement extends Element implements Movable {\r
+ \r
+ protected Rectangle2D bounds = new Rectangle2D.Double();\r
+ \r
+ @RelatedValue("http://www.simantics.org/Sysdyn#HasX")\r
+ protected double posX;\r
+ @RelatedValue("http://www.simantics.org/Sysdyn#HasY")\r
+ protected double posY; \r
+ \r
+ public RectangularElement() { \r
+ }\r
+ \r
+ @Override\r
+ public void getBounds(Rectangle2D bounds) {\r
+ bounds.setFrame(this.bounds);\r
+ }\r
+\r
+ @Override\r
+ public boolean hitTest(double x, double y, double tolerance) {\r
+ return bounds.intersects(x-tolerance, y-tolerance, 2.0*tolerance, 2.0*tolerance);\r
+ } \r
+ \r
+ @Override\r
+ public void move(double deltaX, double deltaY) {\r
+ posX += deltaX;\r
+ posY += deltaY;\r
+ update();\r
+ }\r
+ \r
+ public Point2D getOrigo() {\r
+ return new Point2D.Double(posX, posY);\r
+ }\r
+ \r
+}\r
--- /dev/null
+package org.simantics.sysdyn.ui.elements;\r
+\r
+import org.simantics.h2d.node.RectangleNode;\r
+import org.simantics.objmap.annotations.GraphType;\r
+import org.simantics.scenegraph.g2d.G2DParentNode;\r
+\r
+@GraphType("http://www.simantics.org/Sysdyn#Stock")\r
+public class StockElement extends TextElement {\r
+ \r
+ RectangleNode rectangleNode;\r
+ \r
+ public StockElement() { \r
+ }\r
+ \r
+ public StockElement(String label, double x, double y) {\r
+ super(label, x, y);\r
+ }\r
+\r
+ @Override\r
+ public void init(G2DParentNode parent) {\r
+ rectangleNode = parent.addNode(RectangleNode.class);\r
+ super.init(parent);\r
+ }\r
+ \r
+ @Override\r
+ public void remove() {\r
+ rectangleNode.remove();\r
+ super.remove();\r
+ }\r
+\r
+ @Override\r
+ protected void update() {\r
+ super.update();\r
+ if(rectangleNode != null)\r
+ rectangleNode.init(bounds);\r
+ }\r
+\r
+}\r
--- /dev/null
+package org.simantics.sysdyn.ui.elements;\r
+\r
+import java.awt.Color;\r
+import java.awt.Font;\r
+import java.awt.font.FontRenderContext;\r
+import java.awt.geom.AffineTransform;\r
+import java.awt.geom.Rectangle2D;\r
+\r
+import org.simantics.h2d.action.IAction;\r
+import org.simantics.h2d.editor.IDiagramEditor;\r
+import org.simantics.h2d.element.handler.Connectable;\r
+import org.simantics.h2d.element.handler.Movable;\r
+import org.simantics.h2d.event.ClickEvent;\r
+import org.simantics.h2d.event.IEvent;\r
+import org.simantics.h2d.event.KeyboardEvent;\r
+import org.simantics.h2d.event.handler.IEventHandler;\r
+import org.simantics.h2d.node.ITextListener;\r
+import org.simantics.h2d.node.TextNode;\r
+import org.simantics.objmap.annotations.GraphType;\r
+import org.simantics.objmap.annotations.RelatedValue;\r
+import org.simantics.scenegraph.g2d.G2DParentNode;\r
+\r
+@GraphType("http://www.simantics.org/Sysdyn#Auxiliary")\r
+public class TextElement extends RectangularElement implements Movable, Connectable, IEventHandler {\r
+ \r
+ static final Font FONT = new Font("sans-serif", Font.PLAIN, 12);\r
+ static final double FONT_SCALE = 0.3;\r
+ static final AffineTransform FONT_TRANSFORM = new AffineTransform(FONT_SCALE, 0.0, 0.0, FONT_SCALE, 0.0, 0.0);\r
+ \r
+ static final double XPADDING = 4.0;\r
+ static final double YPADDING = 2.0;\r
+ static final FontRenderContext FRC = new FontRenderContext(FONT_TRANSFORM, true, true);\r
+\r
+ // Properties\r
+ @RelatedValue("http://www.vtt.fi/Simantics/Layer0/1.0/Relations#HasName")\r
+ public String label = "Unnamed";\r
+ \r
+ // Auxiliary fields\r
+ double textX;\r
+ double textY;\r
+ \r
+ TextNode textNode;\r
+ \r
+ public TextElement() { \r
+ }\r
+ \r
+ public TextElement(String label, double x, double y) {\r
+ this.label = label;\r
+ this.posX = x;\r
+ this.posY = y;\r
+ }\r
+\r
+ @Override\r
+ public void remove() {\r
+ textNode.remove();\r
+ }\r
+\r
+ @Override\r
+ public void init(G2DParentNode parent) {\r
+ textNode = parent.addNode(TextNode.class);\r
+ update();\r
+ }\r
+ \r
+ @Override\r
+ protected void update() {\r
+ Rectangle2D textBounds = FONT.getStringBounds(label, FRC);\r
+ textBounds.setFrame(\r
+ textBounds.getX()*FONT_SCALE,\r
+ textBounds.getY()*FONT_SCALE,\r
+ textBounds.getWidth()*FONT_SCALE,\r
+ textBounds.getHeight()*FONT_SCALE\r
+ );\r
+ \r
+ textX = posX-textBounds.getCenterX();\r
+ textY = posY-textBounds.getCenterY();\r
+ bounds.setFrame(\r
+ posX-textBounds.getWidth()*0.5-XPADDING,\r
+ posY-textBounds.getHeight()*0.5-YPADDING,\r
+ textBounds.getWidth()+XPADDING*2,\r
+ textBounds.getHeight()+YPADDING*2\r
+ ); \r
+ \r
+ if(textNode != null)\r
+ textNode.init(label, FONT, Color.BLACK, textX, textY, FONT_SCALE);\r
+ \r
+ super.update();\r
+ }\r
+\r
+ class TextEditingAction implements IAction, ITextListener {\r
+ \r
+ String oldText;\r
+ IDiagramEditor editor;\r
+\r
+ public TextEditingAction(IDiagramEditor editor) {\r
+ super();\r
+ this.editor = editor;\r
+ }\r
+\r
+ @Override\r
+ public void init(G2DParentNode parent) {\r
+ oldText = textNode.getText();\r
+ textNode.setEditMode(true); \r
+ textNode.setTextListener(this);\r
+ /*textFieldNode = parent.addNode(TextFieldNode.class);\r
+ textFieldNode.init(10.0, 10.0);*/\r
+ }\r
+\r
+ @Override\r
+ public void remove() {\r
+ textNode.setEditMode(false);\r
+ textNode.setTextListener(null);\r
+ //textFieldNode.remove(); \r
+ }\r
+\r
+ @Override\r
+ public boolean handle(IDiagramEditor editor, IEvent _event) {\r
+ if(_event instanceof KeyboardEvent) {\r
+ KeyboardEvent event = (KeyboardEvent)_event;\r
+ if(event.key.equals("Escape")) {\r
+ label = oldText;\r
+ update();\r
+ editor.removeAction(this); \r
+ editor.requestRepaint(); \r
+ }\r
+ else if(event.key.equals("Enter"))\r
+ editor.removeAction(this);\r
+ return true;\r
+ }\r
+ else if(_event instanceof ClickEvent) {\r
+ editor.removeAction(this);\r
+ }\r
+ return false;\r
+ }\r
+\r
+ @Override\r
+ public void textChanged() {\r
+ label = textNode.getText();\r
+ update();\r
+ editor.requestRepaint();\r
+ }\r
+ \r
+ }\r
+ \r
+ public void beginRenameAction(IDiagramEditor editor) {\r
+ editor.addAction(new TextEditingAction(editor));\r
+ }\r
+\r
+ @Override\r
+ public boolean handle(IDiagramEditor editor, IEvent _event) {\r
+ if(_event instanceof KeyboardEvent) {\r
+ KeyboardEvent event = (KeyboardEvent)_event;\r
+ if(event.key.equals("F3")) {\r
+ beginRenameAction(editor);\r
+ return true;\r
+ } \r
+ }\r
+ return false;\r
+ } \r
+ \r
+}\r
--- /dev/null
+package org.simantics.sysdyn.ui.elements;\r
+\r
+import java.awt.BasicStroke;\r
+import java.awt.Color;\r
+import java.awt.geom.Path2D;\r
+\r
+import org.simantics.h2d.element.handler.Connectable;\r
+import org.simantics.h2d.element.handler.Rotatable;\r
+import org.simantics.h2d.node.ShapeNode;\r
+import org.simantics.objmap.annotations.GraphType;\r
+import org.simantics.scenegraph.g2d.G2DParentNode;\r
+\r
+@GraphType("http://www.simantics.org/Sysdyn#Valve")\r
+public class ValveElement extends RectangularElement implements Connectable, Rotatable {\r
+ \r
+ public static final double VALVE_SIZE = 5.0;\r
+ \r
+ ShapeNode valveNode;\r
+ boolean rotated = false;\r
+ \r
+ public ValveElement() { \r
+ }\r
+ \r
+ public ValveElement(double x, double y) {\r
+ this.posX = x;\r
+ this.posY = y; \r
+ }\r
+\r
+ @Override\r
+ public void init(G2DParentNode parent) {\r
+ valveNode = parent.addNode(ShapeNode.class);\r
+ valveNode.setColor(Color.BLACK);\r
+ valveNode.setScaleStroke(true);\r
+ valveNode.setStroke(new BasicStroke(1));\r
+ update();\r
+ }\r
+\r
+ @Override\r
+ public void remove() {\r
+ valveNode.remove();\r
+ }\r
+ \r
+ @Override\r
+ protected void update() {\r
+ bounds.setFrame(posX-VALVE_SIZE, posY-VALVE_SIZE, 2.0*VALVE_SIZE, 2.0*VALVE_SIZE);\r
+ Path2D path = new Path2D.Double();\r
+ path.moveTo(posX-VALVE_SIZE, posY-VALVE_SIZE);\r
+ if(rotated) {\r
+ path.lineTo(posX-VALVE_SIZE, posY+VALVE_SIZE);\r
+ path.lineTo(posX+VALVE_SIZE, posY-VALVE_SIZE);\r
+ }\r
+ else { \r
+ path.lineTo(posX+VALVE_SIZE, posY-VALVE_SIZE);\r
+ path.lineTo(posX-VALVE_SIZE, posY+VALVE_SIZE); \r
+ }\r
+ path.lineTo(posX+VALVE_SIZE, posY+VALVE_SIZE);\r
+ path.closePath();\r
+ valveNode.setShape(path);\r
+ super.update();\r
+ }\r
+\r
+ @Override\r
+ public void rotate(int amount) {\r
+ if((amount & 1) == 1) {\r
+ rotated = !rotated;\r
+ update();\r
+ }\r
+ }\r
+\r
+ public boolean isRotated() {\r
+ return rotated;\r
+ }\r
+}\r
--- /dev/null
+package org.simantics.sysdyn.ui.equation;\r
+\r
+import org.eclipse.jface.viewers.ISelection;\r
+import org.eclipse.jface.viewers.IStructuredSelection;\r
+import org.eclipse.swt.SWT;\r
+import org.eclipse.swt.events.FocusAdapter;\r
+import org.eclipse.swt.events.FocusEvent;\r
+import org.eclipse.swt.events.KeyAdapter;\r
+import org.eclipse.swt.events.KeyEvent;\r
+import org.eclipse.swt.graphics.Font;\r
+import org.eclipse.swt.widgets.Composite;\r
+import org.eclipse.swt.widgets.Text;\r
+import org.eclipse.ui.ISelectionListener;\r
+import org.eclipse.ui.IWorkbenchPart;\r
+import org.eclipse.ui.part.ViewPart;\r
+import org.simantics.db.Builtins;\r
+import org.simantics.db.ReadGraph;\r
+import org.simantics.db.Resource;\r
+import org.simantics.db.WriteGraph;\r
+import org.simantics.db.common.request.ReadRequest;\r
+import org.simantics.db.common.request.WriteRequest;\r
+import org.simantics.db.exception.DatabaseException;\r
+import org.simantics.sysdyn.SysdynResource;\r
+import org.simantics.ui.SimanticsUI;\r
+\r
+public class EquationView extends ViewPart implements ISelectionListener {\r
+ \r
+ static final Font FONT = new Font(null, "Courier New", 12, SWT.NORMAL);\r
+ \r
+ Text text;\r
+ Resource auxiliary;\r
+ String originalText;\r
+ \r
+ @Override \r
+ public void createPartControl(Composite parent) {\r
+ getSite().getPage().addPostSelectionListener(this);\r
+ text = new Text(parent, SWT.MULTI);\r
+ text.setFont(FONT);\r
+ text.addFocusListener(new FocusAdapter() {\r
+ \r
+ @Override\r
+ public void focusLost(FocusEvent e) {\r
+ final String currentText = text.getText();\r
+ if(originalText != null && !originalText.equals(currentText)) {\r
+ SimanticsUI.getSession().asyncRequest(new WriteRequest() {\r
+\r
+ @Override\r
+ public void perform(WriteGraph g)\r
+ throws DatabaseException {\r
+ SysdynResource sr = SysdynResource.getInstance(g);\r
+ g.claimValue(auxiliary, sr.HasValue, currentText);\r
+ }\r
+ \r
+ });\r
+ }\r
+ } \r
+ \r
+ });\r
+ text.addKeyListener(new KeyAdapter() { \r
+ \r
+ @Override\r
+ public void keyPressed(KeyEvent e) {\r
+ if(e.keyCode == SWT.ESC) {\r
+ text.setText(originalText);\r
+ } \r
+ }\r
+ \r
+ });\r
+ } \r
+\r
+ class UpdateViewRequest extends ReadRequest {\r
+\r
+ Resource resource;\r
+ \r
+ public UpdateViewRequest(Resource resource) {\r
+ this.resource = resource;\r
+ }\r
+ \r
+ @Override\r
+ public void run(ReadGraph g) throws DatabaseException {\r
+ SysdynResource sr = SysdynResource.getInstance(g);\r
+ Builtins b = g.getBuiltins();\r
+ if(g.isInstanceOf(resource, sr.Auxiliary) || g.isInstanceOf(resource, sr.Valve)) {\r
+ auxiliary = resource;\r
+ final String name = g.getRelatedValue(resource, b.HasName);\r
+ final String value = g.getPossibleRelatedValue(resource, sr.HasValue);\r
+ text.getDisplay().asyncExec(new Runnable() {\r
+\r
+ @Override\r
+ public void run() {\r
+ if(value == null)\r
+ originalText = "";\r
+ else\r
+ originalText = value;\r
+ text.setText(originalText);\r
+ setPartName("Value of " + name);\r
+ }\r
+ \r
+ });\r
+ }\r
+ else if(g.isInstanceOf(resource, sr.Stock)) {\r
+ auxiliary = resource;\r
+ final String name = g.getRelatedValue(resource, b.HasName);\r
+ final String value = g.getPossibleRelatedValue(resource, sr.HasValue);\r
+ text.getDisplay().asyncExec(new Runnable() {\r
+\r
+ @Override\r
+ public void run() {\r
+ if(value == null)\r
+ originalText = "";\r
+ else\r
+ originalText = value;\r
+ text.setText(originalText);\r
+ setPartName("Initial value of " + name);\r
+ }\r
+ \r
+ });\r
+ }\r
+ }\r
+ \r
+ }\r
+ \r
+ @Override\r
+ public void selectionChanged(IWorkbenchPart part, ISelection selection) {\r
+ if(selection instanceof IStructuredSelection) {\r
+ IStructuredSelection structuredSelection = \r
+ (IStructuredSelection)selection;\r
+ if(structuredSelection.size() == 1) {\r
+ Object element = structuredSelection.getFirstElement();\r
+ if(element instanceof Resource)\r
+ SimanticsUI.getSession().asyncRequest(new UpdateViewRequest((Resource)element));\r
+ }\r
+ }\r
+ }\r
+\r
+ @Override\r
+ public void setFocus() {\r
+ text.setFocus();\r
+ }\r
+\r
+}\r
--- /dev/null
+package org.simantics.sysdyn.ui.modelica;\r
+\r
+import org.eclipse.jface.viewers.ISelection;\r
+import org.eclipse.swt.SWT;\r
+import org.eclipse.swt.widgets.Composite;\r
+import org.eclipse.swt.widgets.Label;\r
+import org.eclipse.ui.ISelectionListener;\r
+import org.eclipse.ui.IWorkbenchPart;\r
+import org.eclipse.ui.part.ViewPart;\r
+\r
+public class ModelicaView extends ViewPart implements ISelectionListener {\r
+ \r
+ @Override \r
+ public void createPartControl(Composite parent) {\r
+ getSite().getPage().addPostSelectionListener(this);\r
+ new Label(parent, SWT.NONE).setText("Hello World!");\r
+ } \r
+\r
+ @Override\r
+ public void setFocus() {\r
+ // TODO Auto-generated method stub\r
+\r
+ }\r
+\r
+ @Override\r
+ public void selectionChanged(IWorkbenchPart part, ISelection selection) {\r
+ System.out.println("selection changed");\r
+ }\r
+\r
+}\r
--- /dev/null
+package org.simantics.sysdyn.ui.project;\r
+\r
+import org.eclipse.ui.IFolderLayout;\r
+import org.eclipse.ui.IPageLayout;\r
+import org.eclipse.ui.IPerspectiveFactory;\r
+\r
+public class SysdynPerspectiveFactory implements IPerspectiveFactory {\r
+\r
+ @Override\r
+ public void createInitialLayout(IPageLayout layout) {\r
+ layout.setEditorAreaVisible(true);\r
+ String editorArea = layout.getEditorArea();\r
+\r
+ IFolderLayout left = layout.createFolder("left", IPageLayout.LEFT, 0.25f, editorArea);\r
+ left.addView("org.simantics.ode.views.ontologyBrowser");\r
+ \r
+ IFolderLayout bottom = layout.createFolder("bottom", IPageLayout.BOTTOM, 0.75f, editorArea);\r
+ bottom.addView("org.simantics.sysdyn.ui.equation.view");\r
+ }\r
+\r
+}\r
--- /dev/null
+package org.simantics.sysdyn.ui.project;\r
+\r
+import org.simantics.project.ProjectKeys;\r
+import org.simantics.project.exception.ProjectException;\r
+import org.simantics.project.features.AbstractProjectFeature;\r
+\r
+public class SysdynProject extends AbstractProjectFeature {\r
+ private static final String DEFAULT_PERSPECTIVE = "org.simantics.sysdyn.ui.perspective";\r
+ \r
+ @Override\r
+ public void configure() throws ProjectException {\r
+ addToCollectionHint(ProjectKeys.PERSPECTIVES, DEFAULT_PERSPECTIVE);\r
+ addToCollectionHint(ProjectKeys.OPEN_PERSPECTIVES, DEFAULT_PERSPECTIVE);\r
+ getProjectElement().setHint(ProjectKeys.DEFAULT_PERSPECTIVE, DEFAULT_PERSPECTIVE);\r
+ }\r
+\r
+ @Override\r
+ public void deconfigure() throws ProjectException {\r
+ }\r
+\r
+}\r