]> gerrit.simantics Code Review - simantics/district.git/blob - org.simantics.district.network.ui/src/org/simantics/district/network/ui/NetworkDrawingParticipant.java
Initial commit of simantics/district
[simantics/district.git] / org.simantics.district.network.ui / src / org / simantics / district / network / ui / NetworkDrawingParticipant.java
1 \r
2 package org.simantics.district.network.ui;\r
3 \r
4 import java.awt.Color;\r
5 import java.awt.Graphics2D;\r
6 import java.awt.Shape;\r
7 import java.awt.geom.Path2D;\r
8 import java.awt.geom.Point2D;\r
9 import java.awt.geom.Rectangle2D;\r
10 import java.util.ArrayList;\r
11 import java.util.Collections;\r
12 import java.util.HashSet;\r
13 import java.util.Iterator;\r
14 import java.util.List;\r
15 import java.util.Set;\r
16 \r
17 import org.simantics.Simantics;\r
18 import org.simantics.db.Resource;\r
19 import org.simantics.db.WriteGraph;\r
20 import org.simantics.db.common.request.WriteRequest;\r
21 import org.simantics.db.exception.DatabaseException;\r
22 import org.simantics.diagram.ui.DiagramModelHints;\r
23 import org.simantics.g2d.canvas.impl.DependencyReflection.Dependency;\r
24 import org.simantics.g2d.canvas.impl.SGNodeReflection.SGInit;\r
25 import org.simantics.g2d.diagram.IDiagram;\r
26 import org.simantics.g2d.diagram.handler.PickContext;\r
27 import org.simantics.g2d.diagram.handler.PickRequest;\r
28 import org.simantics.g2d.diagram.handler.PickRequest.PickPolicy;\r
29 import org.simantics.g2d.diagram.participant.AbstractDiagramParticipant;\r
30 import org.simantics.g2d.diagram.participant.Selection;\r
31 import org.simantics.g2d.element.IElement;\r
32 import org.simantics.g2d.participant.TransformUtil;\r
33 import org.simantics.g2d.utils.GeometryUtils;\r
34 import org.simantics.scenegraph.g2d.G2DNode;\r
35 import org.simantics.scenegraph.g2d.G2DParentNode;\r
36 import org.simantics.scenegraph.g2d.events.EventTypes;\r
37 import org.simantics.scenegraph.g2d.events.MouseEvent;\r
38 import org.simantics.scenegraph.g2d.events.EventHandlerReflection.EventHandler;\r
39 import org.simantics.scenegraph.g2d.events.MouseEvent.MouseClickEvent;\r
40 import org.simantics.scenegraph.g2d.events.MouseEvent.MouseDoubleClickedEvent;\r
41 import org.simantics.scenegraph.g2d.events.MouseEvent.MouseMovedEvent;\r
42 import org.simantics.scenegraph.utils.NodeUtil;\r
43 import org.simantics.utils.datastructures.hints.IHintContext.Key;\r
44 import org.simantics.utils.datastructures.hints.IHintContext.KeyOf;\r
45 \r
46 public class NetworkDrawingParticipant extends AbstractDiagramParticipant {\r
47 \r
48     /**\r
49      * A hint key for terminal pick distance in control pixels.\r
50      * @see #PICK_DIST\r
51      */\r
52     public static final Key KEY_PICK_DISTANCE = new KeyOf(Double.class, "PICK_DISTANCE");\r
53 \r
54     /**\r
55      * Default terminal pick distance in control pixels.\r
56      * @see #DEFAULT_PICK_DISTANCE\r
57      */\r
58     public static final double PICK_DIST = 10;\r
59     \r
60     private NetworkDrawingNode node;\r
61     \r
62     @Dependency Selection selection;\r
63     @Dependency TransformUtil util;\r
64     @Dependency PickContext pickContext;\r
65     \r
66     @SGInit\r
67     public void initSG(G2DParentNode parent) {\r
68         node = parent.addNode("networkDrawingNode", NetworkDrawingNode.class);\r
69     }\r
70     \r
71     @Override\r
72     protected void onDiagramSet(IDiagram newDiagram, IDiagram oldDiagram) {\r
73         node.setDiagram(newDiagram);\r
74     }\r
75     \r
76     @EventHandler(priority = 1 << 21)\r
77     public boolean handleClick(MouseClickEvent me) {\r
78         \r
79         boolean isLeft = me.button == MouseEvent.LEFT_BUTTON;\r
80         boolean isRight = me.button == MouseEvent.RIGHT_BUTTON;\r
81         if (!isLeft && !isRight)\r
82             return false;\r
83         boolean isShiftPressed = me.hasAllModifiers(MouseEvent.SHIFT_MASK);\r
84         \r
85         int selectionId = me.mouseId;\r
86         \r
87         double pickDist = getPickDistance();\r
88         Rectangle2D controlPickRect = new Rectangle2D.Double(me.controlPosition.getX()-pickDist, me.controlPosition.getY()-pickDist, pickDist*2+1, pickDist*2+1);\r
89         Shape canvasPickRect  = GeometryUtils.transformShape(controlPickRect, util.getInverseTransform());\r
90         \r
91         PickRequest req = new PickRequest(canvasPickRect);\r
92         req.pickPolicy = PickPolicy.PICK_INTERSECTING_OBJECTS;\r
93         //req.pickSorter = PickRequest.PickSorter.CONNECTIONS_LAST;\r
94         List<IElement> pickables = new ArrayList<IElement>();\r
95         pickContext.pick(diagram, req, pickables);\r
96         \r
97         Set<IElement> currentSelection = selection.getSelection(selectionId);\r
98         \r
99         if (!pickables.isEmpty()) {\r
100             /*\r
101              * Select the one object the mouse points to. If multiple object\r
102              * are picked, select the one that is after the earliest by\r
103              * index of the current selection when shift is pressed. Otherwise\r
104              * always pick the topmost element.\r
105              */\r
106             IElement selectedPick = isShiftPressed\r
107                     ? rotatingPick(currentSelection, pickables)\r
108                             : pickables.get(pickables.size() - 1);\r
109     \r
110             // Only select when\r
111             // 1. the selection would actually change\r
112             // AND\r
113             //   2.1. left button was pressed\r
114             //   OR\r
115             //   2.2. right button was pressed and the element to-be-selected\r
116             //        is NOT a part of the current selection\r
117             if (!Collections.singleton(selectedPick).equals(currentSelection)\r
118                     && (isLeft || (isRight && !currentSelection.contains(selectedPick)))) {\r
119                 selection.setSelection(selectionId, selectedPick);\r
120             }\r
121         }\r
122         \r
123         return false;\r
124     }\r
125     \r
126     private double getPickDistance() {\r
127         Double pickDistance = getHint(KEY_PICK_DISTANCE);\r
128         return pickDistance == null ? PICK_DIST : Math.max(pickDistance, 0);\r
129     }\r
130     \r
131     private IElement rotatingPick(int selectionId, List<IElement> pickables) {\r
132         Set<IElement> sel = selection.getSelection(selectionId);\r
133         return rotatingPick(sel, pickables);\r
134     }\r
135 \r
136     private IElement rotatingPick(Set<IElement> sel, List<IElement> pickables) {\r
137         int earliestIndex = pickables.size();\r
138         for (int i = pickables.size() - 1; i >= 0; --i) {\r
139             if (sel.contains(pickables.get(i))) {\r
140                 earliestIndex = i;\r
141                 break;\r
142             }\r
143         }\r
144         if (earliestIndex == 0)\r
145             earliestIndex = pickables.size();\r
146         IElement selectedPick = pickables.get(earliestIndex - 1);\r
147         return selectedPick;\r
148     }\r
149 \r
150     public static class NetworkDrawingNode extends G2DNode {\r
151 \r
152         private static final long serialVersionUID = -3475301184009620573L;\r
153         \r
154         private List<Point2D> nodes = new ArrayList<>();\r
155 \r
156         private Rectangle2D rect = new Rectangle2D.Double(10, 10, 10, 10);\r
157 \r
158         private Set<Path2D> paths = new HashSet<>();\r
159 \r
160         private Resource diagramResource;\r
161 \r
162         private boolean committed;\r
163         \r
164         @Override\r
165         public void init() {\r
166             super.init();\r
167             addEventHandler(this);\r
168         }\r
169         \r
170         public void setDiagram(IDiagram diagram) {\r
171             if (diagram != null)\r
172                 this.diagramResource = diagram.getHint(DiagramModelHints.KEY_DIAGRAM_RESOURCE);\r
173         }\r
174 \r
175         @Override\r
176         public void render(Graphics2D g2d) {\r
177             Color old = g2d.getColor();\r
178             g2d.setColor(Color.BLUE);\r
179             \r
180             paths.forEach(p -> {\r
181                 g2d.draw(p);\r
182             });\r
183             \r
184             g2d.setColor(old);\r
185         }\r
186 \r
187         @Override\r
188         public Rectangle2D getBoundsInLocal() {\r
189             return rect.getBounds2D();\r
190         }\r
191         \r
192         @Override\r
193         public int getEventMask() {\r
194             return EventTypes.MouseMask;\r
195         }\r
196         \r
197         @Override\r
198         protected boolean mouseDoubleClicked(MouseDoubleClickedEvent e) {\r
199             // nodes to path2d\r
200             Point2D start = null;\r
201             Point2D end = null;\r
202             Iterator<Point2D> nodeIter = nodes.iterator();\r
203             while (nodeIter.hasNext()) {\r
204                 if (end == null) {\r
205                     start = nodeIter.next();\r
206                 } else {\r
207                     start = end;\r
208                 }\r
209                 end = nodeIter.next();\r
210                 \r
211                 createEdge(start, end);\r
212             }\r
213             \r
214             nodes.clear();\r
215             committed = true;\r
216             \r
217             repaint();\r
218             \r
219             return true;\r
220         }\r
221         \r
222         private void createEdge(Point2D start, Point2D end) {\r
223             double[] startCoords = new double[] { start.getX(), start.getY() };\r
224             double[] endCoords = new double[] { end.getX(), end.getY() };\r
225             \r
226             DNEdgeBuilder builder = new DNEdgeBuilder(diagramResource);\r
227             Simantics.getSession().asyncRequest(new WriteRequest() {\r
228                 \r
229                 @Override\r
230                 public void perform(WriteGraph graph) throws DatabaseException {\r
231                     builder.create(graph, startCoords, endCoords);\r
232                 }\r
233             });\r
234             \r
235         }\r
236         \r
237         @Override\r
238         protected boolean mouseClicked(MouseClickEvent e) {\r
239             if (committed) {\r
240                 committed = false;\r
241                 return false;\r
242             }\r
243             Point2D localPos = NodeUtil.worldToLocal(this, e.controlPosition, new Point2D.Double());\r
244             nodes.add(new Point2D.Double(localPos.getX(), localPos.getY()));\r
245             return super.mouseClicked(e);\r
246         }\r
247         \r
248         @Override\r
249         protected boolean mouseMoved(MouseMovedEvent e) {\r
250             return super.mouseMoved(e);\r
251         }\r
252     }\r
253 }\r