\r
private IHintListener hoverHintListener;\r
\r
- private static final Key NODE = new SceneGraphNodeKey(HoverShapeNode.class, "LOOP_NODE");\r
+ private static final Key NODE = new SceneGraphNodeKey(LoopNode.class, "LOOP_NODE");\r
\r
public LoopImageSceneGraph(Image i) {\r
super(i);\r
super.init(e, parent);\r
\r
// Create new hover shape node for the loop image\r
- final HoverShapeNode node = ElementUtils.getOrCreateNode(e, parent, NODE, "loopHover", HoverShapeNode.class);\r
+ final LoopNode node = ElementUtils.getOrCreateNode(e, parent, NODE, "loopHover", LoopNode.class);\r
\r
// Mirror the image if clockwise is selected.\r
Boolean clockwise = e.getHint(SysdynElementHints.KEY_LOOP_CLOCKWISE);\r
@Override\r
public void hintChanged(IHintObservable sender, Key key, Object oldValue, Object newValue) {\r
IElement e = (IElement)sender;\r
- HoverShapeNode shape = (HoverShapeNode) e.getHint(NODE);\r
+ LoopNode shape = (LoopNode) e.getHint(NODE);\r
if(shape == null) {\r
return;\r
}\r
\r
// Move the text box into (around) the middle of the loop image\r
AffineTransform at = ElementUtils.getTransform(e);\r
- final HoverShapeNode node = ElementUtils.getOrCreateNode(e, parent, NODE, "loopComment", HoverShapeNode.class);\r
+ final LoopNode node = ElementUtils.getOrCreateNode(e, parent, NODE, "loopComment", LoopNode.class);\r
\r
// Unflip the text and image\r
unflipText(e);\r
--- /dev/null
+/*******************************************************************************\r
+ * Copyright (c) 2014 Association for Decentralized Information Management\r
+ * in Industry THTH ry.\r
+ * All rights reserved. This program and the accompanying materials\r
+ * are made available under the terms of the Eclipse Public License v1.0\r
+ * which accompanies this distribution, and is available at\r
+ * http://www.eclipse.org/legal/epl-v10.html\r
+ *\r
+ * Contributors:\r
+ * VTT Technical Research Centre of Finland - initial API and implementation\r
+ *******************************************************************************/\r
+package org.simantics.sysdyn.ui.elements;\r
+\r
+import java.awt.Color;\r
+import java.awt.Graphics2D;\r
+import java.util.ArrayList;\r
+import java.util.Collection;\r
+import java.util.Collections;\r
+import java.util.Iterator;\r
+import java.util.List;\r
+\r
+import org.simantics.db.ReadGraph;\r
+import org.simantics.db.Resource;\r
+import org.simantics.db.common.utils.ListUtils;\r
+import org.simantics.db.exception.DatabaseException;\r
+import org.simantics.db.request.Read;\r
+import org.simantics.diagram.elements.DiagramNodeUtil;\r
+import org.simantics.g2d.element.ElementHints;\r
+import org.simantics.g2d.element.IElement;\r
+import org.simantics.modeling.ModelingResources;\r
+import org.simantics.scenegraph.INode;\r
+import org.simantics.scenegraph.ParentNode;\r
+import org.simantics.scenegraph.g2d.IG2DNode;\r
+import org.simantics.scenegraph.g2d.nodes.ConnectionNode;\r
+import org.simantics.scenegraph.g2d.nodes.spatial.RTreeNode;\r
+import org.simantics.scenegraph.utils.NodeUtil;\r
+import org.simantics.sysdyn.SysdynResource;\r
+import org.simantics.ui.SimanticsUI;\r
+\r
+/**\r
+ * Node for Sysdyn loop elements.\r
+ * \r
+ * @author Tuomas Miettinen\r
+ *\r
+ */\r
+public class LoopNode extends HoverShapeNode {\r
+\r
+ /**\r
+ * Interface for nodes that can be part of loops\r
+ * @author Tuomas Miettinen\r
+ *\r
+ */\r
+ public interface ILoopComponentNode {\r
+ \r
+ /**\r
+ * Sets or resets the loop selected status\r
+ * @param loop The loop which has been selected\r
+ * @param selected true iff the loop is selected\r
+ */\r
+ public void setLoopSelected(LoopNode loop, boolean selected);\r
+ \r
+ }\r
+ \r
+ public static Color HIGHLIGHT_COLOR = Color.decode("#ff5fbf");\r
+ \r
+ private boolean selected = false;\r
+ \r
+ private static final long serialVersionUID = 6173159124691715569L;\r
+ \r
+ @Override\r
+ public void render(Graphics2D g2d) {\r
+ super.render(g2d);\r
+ \r
+ // If the loop is selected, highlight also the elements that belong to the loop.\r
+ boolean selected = NodeUtil.isSelected(this, 1);\r
+ // Do nothing if the selection is and was off.\r
+ if (selected || this.selected != selected) {\r
+ this.selected = selected; \r
+ // Tell all items belonging to the loop that the loop is selected.\r
+ setLoopItemsSelected();\r
+ }\r
+ }\r
+ \r
+ private void setLoopItemsSelected() {\r
+ // Get all variables and dependencies in the loop.\r
+ List<Resource> loopItems = getAllLoopItems();\r
+ \r
+ // Get the diagram where this loop is.\r
+ RTreeNode diagramNode = (RTreeNode)NodeUtil.getNearestParentOfType(this, RTreeNode.class);\r
+ if (diagramNode == null)\r
+ return;\r
+ \r
+ // Go through all elements on the diagram where this loop is.\r
+ Collection<IG2DNode> children = diagramNode.getNodes();\r
+ Iterator<IG2DNode> it = children.iterator();\r
+ while (it.hasNext()) {\r
+ IG2DNode n = it.next();\r
+ \r
+ // Get the respective node \r
+ INode child = getNodeOfPossibleLoopComponentNode(n);\r
+ if (child instanceof ILoopComponentNode) {\r
+ ILoopComponentNode ln = (ILoopComponentNode)child;\r
+ // Get the respective element \r
+ IElement e = DiagramNodeUtil.getElement((IG2DNode)child.getParent());\r
+ // Get the respective resource \r
+ Resource r = e.getHint(ElementHints.KEY_OBJECT);\r
+ // If the node belongs to the loop, tell it that whether the loop is selected or not.\r
+ ln.setLoopSelected(this, NodeUtil.isSelected(this, 1) && loopItems.contains(r));\r
+ }\r
+ }\r
+ }\r
+\r
+ /**\r
+ * Get the ILoopComponentNode under the variable, dependency, or flow. \r
+ * @param n node under which the ILoopComponentNode is sought\r
+ * @return ILoopComponentNode or null, if there is not any.\r
+ */\r
+ private static INode getNodeOfPossibleLoopComponentNode(IG2DNode n) {\r
+ // Get all nodeIds of n's children.\r
+ Collection<String> nodeIds = ((ParentNode<?>)n).getNodeIds();\r
+\r
+ // Flows and SysdynTextNodes\r
+ for (String id : nodeIds) {\r
+ if ("text".equals(id)\r
+ || id.startsWith("flow_")) \r
+ return ((ParentNode<?>)n).getNode(id);\r
+ }\r
+\r
+ // Dependencies\r
+ if (n instanceof ConnectionNode) {\r
+ // See the first (and only) child of n has a DependencyNode as a child.\r
+ n = ((ConnectionNode) n).getNodes().iterator().next();\r
+ if (n instanceof ParentNode<?>) {\r
+ nodeIds = ((ParentNode<?>)n).getNodeIds();\r
+ for (String id : nodeIds) {\r
+ if (id.startsWith("edge_"))\r
+ return ((ParentNode<?>)n).getNode(id);\r
+ }\r
+ }\r
+ }\r
+ \r
+ return null; // n was no variable, dependency, or flow\r
+\r
+ }\r
+\r
+ /**\r
+ * Get all variables and dependencies in the loop.\r
+ * @return A list where the items are, in unspecified order.\r
+ */\r
+ private List<Resource> getAllLoopItems() {\r
+ IElement loopElement = DiagramNodeUtil.getElement(this);\r
+ final Resource loopSymbolResource = loopElement.getHint(ElementHints.KEY_OBJECT);\r
+ List<Resource> loopItems = Collections.emptyList();\r
+ \r
+ try {\r
+ loopItems = SimanticsUI.getSession().syncRequest(new Read<List<Resource>>(){\r
+\r
+ @Override\r
+ public List<Resource> perform(ReadGraph graph) throws DatabaseException {\r
+ ModelingResources mod = ModelingResources.getInstance(graph);\r
+ SysdynResource sr = SysdynResource.getInstance(graph);\r
+ Resource loopComponentResource = graph.getPossibleObject(loopSymbolResource, mod.ElementToComponent);\r
+ if (loopComponentResource == null)\r
+ return Collections.emptyList();\r
+ \r
+ Resource loopResource = graph.getPossibleObject(loopComponentResource, sr.Loop_Items);\r
+ if (loopResource == null)\r
+ return Collections.emptyList();\r
+ \r
+ List<Resource> loopItems = ListUtils.toPossibleList(graph, loopResource);\r
+ if (loopItems == null)\r
+ return Collections.emptyList();\r
+ \r
+ ArrayList<Resource> dependencyItems = new ArrayList<Resource>();\r
+ \r
+ // Add dependencies and flows.\r
+ for (int i = 0; i < loopItems.size(); ++i) {\r
+ boolean skipBackwardFlows = false;\r
+ \r
+ // Go through forward dependencies and flows\r
+ Collection<Resource> forwardDependencies = graph.getObjects(loopItems.get(i), sr.Variable_isTailOf);\r
+ for (Resource dependency : forwardDependencies) {\r
+ Resource dependingVariable = graph.getSingleObject(dependency, sr.Variable_HasHead);\r
+ if (dependingVariable.equals(loopItems.get((i + 1) % loopItems.size()))) {\r
+ if (graph.isInstanceOf(dependency, sr.Flow)\r
+ && graph.isInstanceOf(loopItems.get(i), sr.Stock)) {\r
+ // Flows from stocks don't count. \r
+ continue;\r
+ }\r
+ skipBackwardFlows = true;\r
+ dependencyItems.add(graph.getSingleObject(dependency, mod.ConnectionToDiagramConnection));\r
+ break;\r
+ }\r
+ }\r
+ \r
+ if (skipBackwardFlows)\r
+ continue;\r
+ \r
+ // Backward flows from stocks.\r
+ Collection<Resource> backwardFlows = graph.getObjects(loopItems.get(i), sr.Variable_isHeadOf);\r
+ for (Resource flow : backwardFlows) {\r
+ if (graph.isInstanceOf(flow, sr.Flow)) {\r
+ Resource dependingVariable = graph.getSingleObject(flow, sr.Variable_HasTail);\r
+ if (dependingVariable.equals(loopItems.get((i + 1) % loopItems.size()))\r
+ && graph.isInstanceOf(dependingVariable, sr.Stock)) {\r
+ dependencyItems.add(graph.getSingleObject(flow, mod.ConnectionToDiagramConnection));\r
+ break;\r
+ }\r
+ }\r
+ }\r
+ }\r
+ \r
+ // Convert variables from component to element.\r
+ for (int i = 0; i < loopItems.size(); ++i) {\r
+ loopItems.set(i, graph.getPossibleObject(loopItems.get(i), mod.ComponentToElement));\r
+ }\r
+ // Merge the two lists.\r
+ loopItems.addAll(dependencyItems);\r
+ \r
+ return loopItems;\r
+ }\r
+ \r
+ });\r
+ } catch (DatabaseException e1) {\r
+ // TODO Auto-generated catch block\r
+ e1.printStackTrace();\r
+ }\r
+ \r
+ return loopItems;\r
+ }\r
+ \r
+}\r
/*******************************************************************************\r
- * Copyright (c) 2013 Association for Decentralized Information Management in\r
+ * Copyright (c) 2013-2014 Association for Decentralized Information Management in\r
* Industry THTH ry.\r
* All rights reserved. This program and the accompanying materials\r
* are made available under the terms of the Eclipse Public License v1.0\r
import java.awt.BasicStroke;\r
import java.awt.Color;\r
import java.awt.Graphics2D;\r
+import java.util.HashMap;\r
\r
import org.simantics.diagram.elements.TextEditActivation;\r
import org.simantics.diagram.elements.TextNode;\r
import org.simantics.g2d.element.IElement;\r
import org.simantics.scenegraph.g2d.events.EventTypes;\r
import org.simantics.scenegraph.g2d.events.MouseEvent.MouseDragBegin;\r
+import org.simantics.sysdyn.ui.elements.LoopNode.ILoopComponentNode;\r
import org.simantics.sysdyn.ui.utils.SysdynWorkbenchUtils;\r
\r
/**\r
* 1. Draw borders when hovering\r
* 2. Support Sysdyn's diagram locking\r
* @author Teemu Lempinen\r
+ * @author Tuomas Miettinen\r
*\r
*/\r
-public class SysdynTextNode extends TextNode {\r
+public class SysdynTextNode extends TextNode implements ILoopComponentNode {\r
\r
private static final long serialVersionUID = 5235077104121753251L;\r
+ private HashMap<LoopNode, Boolean> loopSelectionMap = new HashMap<LoopNode, Boolean>();\r
+\r
+ private boolean isLoopSelected() {\r
+ return loopSelectionMap.containsValue(true);\r
+ }\r
\r
- \r
@Override\r
public int getEventMask(){\r
return EventTypes.FocusLostMask | super.getEventMask();\r
g.setColor(oldColor);\r
g.setStroke(oldStroke);\r
\r
+ } else if (isLoopSelected()) {\r
+ BasicStroke oldStroke = (BasicStroke)g.getStroke();\r
+ Color oldColor = g.getColor();\r
+ g.setColor(LoopNode.HIGHLIGHT_COLOR);\r
+ g.setStroke(new BasicStroke((float)(2.0*scale)));\r
+ g.draw(getBoundsInLocal());\r
+ g.setColor(oldColor);\r
+ g.setStroke(oldStroke);\r
+ \r
}\r
- \r
}\r
\r
public TextEditActivation activateEdit(int mouseId, IElement e, ICanvasContext ctx, boolean save) {\r
return super.activateEdit(mouseId, e, ctx);\r
}\r
\r
+ @Override\r
+ public void setLoopSelected(LoopNode loop, boolean selected) {\r
+ Boolean loopSelected = loopSelectionMap.get(loop);\r
+ if (loopSelected == null || loopSelected != selected) {\r
+ loopSelectionMap.put(loop, selected);\r
+ repaint();\r
+ }\r
+ }\r
}\r
/*******************************************************************************\r
- * Copyright (c) 2010, 2012 Association for Decentralized Information Management in\r
+ * Copyright (c) 2010, 2012, 2014 Association for Decentralized Information Management in\r
* Industry THTH ry.\r
* All rights reserved. This program and the accompanying materials\r
* are made available under the terms of the Eclipse Public License v1.0\r
import java.beans.PropertyChangeEvent;\r
import java.beans.PropertyChangeListener;\r
import java.util.Collection;\r
+import java.util.HashMap;\r
\r
import org.simantics.diagram.elements.TextNode;\r
import org.simantics.g2d.utils.Alignment;\r
import org.simantics.scenegraph.g2d.nodes.SingleElementNode;\r
import org.simantics.scenegraph.utils.NodeUtil;\r
import org.simantics.sysdyn.ui.editor.routing.DependencyRouter;\r
+import org.simantics.sysdyn.ui.elements.LoopNode.ILoopComponentNode;\r
+import org.simantics.sysdyn.ui.elements.LoopNode;\r
import org.simantics.sysdyn.ui.elements.SysdynElementHints;\r
import org.simantics.sysdyn.ui.utils.SysdynWorkbenchUtils;\r
import org.simantics.utils.datastructures.Triple;\r
* @author Tuomas Miettinen\r
*\r
*/\r
-public class DependencyNode extends TextNode implements ISelectionPainterNode {\r
+public class DependencyNode extends TextNode implements ISelectionPainterNode, ILoopComponentNode {\r
\r
public static final String INSIDE = "Inside";\r
public static final String OUTSIDE = "Outside";\r
g.fill(shapes.second);\r
}\r
if (delayMark) g.draw(shapes.third);\r
+ } else if (isLoopSelected()) {\r
+ g.setColor(LoopNode.HIGHLIGHT_COLOR);\r
+ if(stroke != null) g.setStroke(stroke);\r
+ g.draw(shapes.first);\r
+ if (arrowHead) {\r
+ g.draw(shapes.second);\r
+ g.fill(shapes.second);\r
+ }\r
+ if (delayMark) g.draw(shapes.third);\r
} else {\r
if(color != null) g.setColor(color);\r
if(stroke != null) g.setStroke(stroke);\r
}\r
\r
boolean pressHit = false;\r
+ private HashMap<LoopNode, Boolean> loopSelectionMap = new HashMap<LoopNode, Boolean>();\r
\r
+ private boolean isLoopSelected() {\r
+ return loopSelectionMap.containsValue(true);\r
+ }\r
+ \r
protected boolean hitTest(org.simantics.scenegraph.g2d.events.MouseEvent event, double tolerance) {\r
if(beginBounds == null || endBounds == null) return false;\r
Point2D localPos = NodeUtil.worldToLocal(this, event.controlPosition, new Point2D.Double());\r
protected boolean isDragging() {\r
return dragging;\r
}\r
+\r
+ @Override\r
+ public void setLoopSelected(LoopNode loop, boolean selected) {\r
+ Boolean loopSelected = loopSelectionMap.get(loop);\r
+ if (loopSelected == null || loopSelected != selected) {\r
+ loopSelectionMap.put(loop, selected);\r
+ repaint();\r
+ }\r
+ }\r
}\r
import org.simantics.db.request.Read;\r
import org.simantics.diagram.connection.rendering.BasicConnectionStyle;\r
import org.simantics.sysdyn.SysdynResource;\r
+import org.simantics.sysdyn.ui.elements.LoopNode;\r
import org.simantics.ui.SimanticsUI;\r
\r
public class FlowConnectionStyle extends BasicConnectionStyle {\r
Stroke lineStroke;\r
\r
private Resource resource;\r
+\r
+ // Is the default color overridden by the loop color\r
+ private boolean loopColorOverride = false;\r
\r
public static final float DEFAULT_LINE_WIDTH = 1.0f;\r
\r
\r
@Override\r
public void drawPath(Graphics2D g, Path2D path, boolean isTransient) {\r
- if (lineColor != null)\r
- g.setColor(lineColor);\r
+ if (lineColor != null) // Highlight the flow if loop where the flow belongs to is selected.\r
+ g.setColor(loopColorOverride ? LoopNode.HIGHLIGHT_COLOR : lineColor);\r
if (lineStroke != null)\r
g.setStroke(lineStroke);\r
\r
public double getDegeneratedLineLength() {\r
return 0;\r
}\r
+\r
+ /**\r
+ * Set if the flow color should be overwritten with loop color\r
+ * @param loopColorOverride \r
+ */\r
+ public void setLoopColorOverride(boolean loopColorOverride) {\r
+ this.loopColorOverride = loopColorOverride;\r
+ }\r
\r
}\r
/*******************************************************************************\r
- * Copyright (c) 2007, 2011 Association for Decentralized Information Management in\r
+ * Copyright (c) 2007, 2011, 2014 Association for Decentralized Information Management in\r
* Industry THTH ry.\r
* All rights reserved. This program and the accompanying materials\r
* are made available under the terms of the Eclipse Public License v1.0\r
if (rg == null || renderer == null) {\r
cleanup(connection);\r
} else {\r
- RouteFlowNode rgn = connection.getHint(KEY_RG_NODE);\r
- if (rgn == null) {\r
- rgn = parent.addNode(ElementUtils.generateNodeId(connection), RouteFlowNode.class);\r
- connection.setHint(KEY_RG_NODE, rgn);\r
- }\r
+ RouteFlowNode rgn = ElementUtils.getOrCreateNode(connection, parent, KEY_RG_NODE, "flow_" + connection.hashCode(), RouteFlowNode.class);\r
rgn.setRouteGraph(rg);\r
rgn.setRenderer(renderer);\r
\r
/*******************************************************************************\r
- * Copyright (c) 2010, 2012 Association for Decentralized Information Management in\r
+ * Copyright (c) 2013-2014 Association for Decentralized Information Management in\r
* Industry THTH ry.\r
* All rights reserved. This program and the accompanying materials\r
* are made available under the terms of the Eclipse Public License v1.0\r
*******************************************************************************/\r
package org.simantics.sysdyn.ui.elements.connections;\r
\r
+import java.util.HashMap;\r
+\r
+import org.simantics.diagram.connection.rendering.ConnectionStyle;\r
+import org.simantics.diagram.connection.rendering.StyledRouteGraphRenderer;\r
import org.simantics.scenegraph.g2d.events.MouseEvent.MouseDragBegin;\r
import org.simantics.scenegraph.g2d.nodes.connection.RouteGraphNode;\r
+import org.simantics.sysdyn.ui.elements.LoopNode;\r
+import org.simantics.sysdyn.ui.elements.LoopNode.ILoopComponentNode;\r
import org.simantics.sysdyn.ui.elements.SysdynElementHints;\r
import org.simantics.sysdyn.ui.utils.SysdynWorkbenchUtils;\r
\r
* @author Tuomas Miettinen\r
*\r
*/\r
-public class RouteFlowNode extends RouteGraphNode {\r
+public class RouteFlowNode extends RouteGraphNode implements ILoopComponentNode {\r
\r
private static final long serialVersionUID = 2576929364910319487L;\r
boolean isLock = false;\r
+ private HashMap<LoopNode, Boolean> loopSelectionMap = new HashMap<LoopNode, Boolean>();\r
\r
+ private boolean isLoopSelected() {\r
+ return loopSelectionMap.containsValue(true);\r
+ }\r
+ \r
@Override\r
protected boolean mouseDragged(MouseDragBegin e) {\r
// Disable dragging if LockSketch is ON\r
else\r
return super.mouseDragged(e);\r
}\r
+\r
+ @Override\r
+ public void setLoopSelected(LoopNode loop, boolean selected) {\r
+ Boolean loopSelected = loopSelectionMap.get(loop);\r
+ if (loopSelected == null || loopSelected != selected) {\r
+ loopSelectionMap.put(loop, selected);\r
+ \r
+ // Here the FlowConnectionStyle takes care of drawing the flow, so\r
+ // find it and tell it to change the color accordingly\r
+ if (!(renderer instanceof StyledRouteGraphRenderer))\r
+ return;\r
+ \r
+ StyledRouteGraphRenderer renderer = (StyledRouteGraphRenderer)this.renderer;\r
+ ConnectionStyle style = renderer.getStyle();\r
+ if (!(style instanceof FlowConnectionStyle))\r
+ return;\r
+ \r
+ FlowConnectionStyle fcs = (FlowConnectionStyle)style;\r
+ \r
+ fcs.setLoopColorOverride(isLoopSelected());\r
+ repaint();\r
+ }\r
+ }\r
+ \r
}\r
import org.simantics.ui.SimanticsUI;\r
\r
/**\r
- * Utils for configurations\r
+ * Utils for loops\r
* @author Tuomas Miettinen\r
*\r
*/\r
oddNumberOfNegativeCausalities = !oddNumberOfNegativeCausalities;\r
break;\r
}\r
- continue;\r
}\r
}\r
}\r