2 package org.simantics.district.network.ui;
\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
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
46 public class NetworkDrawingParticipant extends AbstractDiagramParticipant {
\r
49 * A hint key for terminal pick distance in control pixels.
\r
52 public static final Key KEY_PICK_DISTANCE = new KeyOf(Double.class, "PICK_DISTANCE");
\r
55 * Default terminal pick distance in control pixels.
\r
56 * @see #DEFAULT_PICK_DISTANCE
\r
58 public static final double PICK_DIST = 10;
\r
60 private NetworkDrawingNode node;
\r
62 @Dependency Selection selection;
\r
63 @Dependency TransformUtil util;
\r
64 @Dependency PickContext pickContext;
\r
67 public void initSG(G2DParentNode parent) {
\r
68 node = parent.addNode("networkDrawingNode", NetworkDrawingNode.class);
\r
72 protected void onDiagramSet(IDiagram newDiagram, IDiagram oldDiagram) {
\r
73 node.setDiagram(newDiagram);
\r
76 @EventHandler(priority = 1 << 21)
\r
77 public boolean handleClick(MouseClickEvent me) {
\r
79 boolean isLeft = me.button == MouseEvent.LEFT_BUTTON;
\r
80 boolean isRight = me.button == MouseEvent.RIGHT_BUTTON;
\r
81 if (!isLeft && !isRight)
\r
83 boolean isShiftPressed = me.hasAllModifiers(MouseEvent.SHIFT_MASK);
\r
85 int selectionId = me.mouseId;
\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
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
97 Set<IElement> currentSelection = selection.getSelection(selectionId);
\r
99 if (!pickables.isEmpty()) {
\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
106 IElement selectedPick = isShiftPressed
\r
107 ? rotatingPick(currentSelection, pickables)
\r
108 : pickables.get(pickables.size() - 1);
\r
110 // Only select when
\r
111 // 1. the selection would actually change
\r
113 // 2.1. left button was pressed
\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
126 private double getPickDistance() {
\r
127 Double pickDistance = getHint(KEY_PICK_DISTANCE);
\r
128 return pickDistance == null ? PICK_DIST : Math.max(pickDistance, 0);
\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
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
144 if (earliestIndex == 0)
\r
145 earliestIndex = pickables.size();
\r
146 IElement selectedPick = pickables.get(earliestIndex - 1);
\r
147 return selectedPick;
\r
150 public static class NetworkDrawingNode extends G2DNode {
\r
152 private static final long serialVersionUID = -3475301184009620573L;
\r
154 private List<Point2D> nodes = new ArrayList<>();
\r
156 private Rectangle2D rect = new Rectangle2D.Double(10, 10, 10, 10);
\r
158 private Set<Path2D> paths = new HashSet<>();
\r
160 private Resource diagramResource;
\r
162 private boolean committed;
\r
165 public void init() {
\r
167 addEventHandler(this);
\r
170 public void setDiagram(IDiagram diagram) {
\r
171 if (diagram != null)
\r
172 this.diagramResource = diagram.getHint(DiagramModelHints.KEY_DIAGRAM_RESOURCE);
\r
176 public void render(Graphics2D g2d) {
\r
177 Color old = g2d.getColor();
\r
178 g2d.setColor(Color.BLUE);
\r
180 paths.forEach(p -> {
\r
188 public Rectangle2D getBoundsInLocal() {
\r
189 return rect.getBounds2D();
\r
193 public int getEventMask() {
\r
194 return EventTypes.MouseMask;
\r
198 protected boolean mouseDoubleClicked(MouseDoubleClickedEvent e) {
\r
200 Point2D start = null;
\r
201 Point2D end = null;
\r
202 Iterator<Point2D> nodeIter = nodes.iterator();
\r
203 while (nodeIter.hasNext()) {
\r
205 start = nodeIter.next();
\r
209 end = nodeIter.next();
\r
211 createEdge(start, end);
\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
226 DNEdgeBuilder builder = new DNEdgeBuilder(diagramResource);
\r
227 Simantics.getSession().asyncRequest(new WriteRequest() {
\r
230 public void perform(WriteGraph graph) throws DatabaseException {
\r
231 builder.create(graph, startCoords, endCoords);
\r
238 protected boolean mouseClicked(MouseClickEvent e) {
\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
249 protected boolean mouseMoved(MouseMovedEvent e) {
\r
250 return super.mouseMoved(e);
\r