| UmlCreateEdgeMode.java |
1 /****************************************************************************
2 * This demo file is part of yFiles for Java 2.14.
3 * Copyright (c) 2000-2017 by yWorks GmbH, Vor dem Kreuzberg 28,
4 * 72070 Tuebingen, Germany. All rights reserved.
5 *
6 * yFiles demo files exhibit yFiles for Java functionalities. Any redistribution
7 * of demo files in source code or binary form, with or without
8 * modification, is not permitted.
9 *
10 * Owners of a valid software license for a yFiles for Java version that this
11 * demo is shipped with are allowed to use the demo source code as basis
12 * for their own yFiles for Java powered applications. Use of such programs is
13 * governed by the rights and conditions as set out in the yFiles for Java
14 * license agreement.
15 *
16 * THIS SOFTWARE IS PROVIDED ''AS IS'' AND ANY EXPRESS OR IMPLIED
17 * WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF
18 * MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN
19 * NO EVENT SHALL yWorks BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
20 * SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED
21 * TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR
22 * PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF
23 * LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING
24 * NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS
25 * SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
26 *
27 ***************************************************************************/
28 package demo.view.uml;
29
30 import y.base.Edge;
31 import y.base.Node;
32 import y.base.NodeCursor;
33 import y.base.NodeList;
34 import y.layout.router.polyline.EdgeRouter;
35 import y.util.DataProviderAdapter;
36 import y.view.Drawable;
37 import y.view.Graph2D;
38 import y.view.Graph2DLayoutExecutor;
39 import y.view.MoveSelectionMode;
40 import y.view.NodeRealizer;
41
42 import java.awt.Graphics2D;
43 import java.awt.Rectangle;
44 import java.awt.geom.Rectangle2D;
45
46 /**
47 * A {@link y.view.ViewMode} that creates an edge with its own target node if a source node exists.
48 * The target node can be dragged around to a desired position. When its center lies within another node at the time,
49 * the edge is connected to that node and the target node is deleted.
50 */
51 class UmlCreateEdgeMode extends MoveSelectionMode {
52 private final EdgeRouter edgeRouter;
53 private Node sourceNode;
54 private Node targetNode;
55
56 private Node draggedNode;
57 private Drawable targetNodeIndicator;
58
59 UmlCreateEdgeMode(final EdgeRouter edgeRouter) {
60 this.edgeRouter = edgeRouter;
61 // create a new drawable for the target node
62 targetNodeIndicator = new Drawable() {
63 public void paint(Graphics2D graphics) {
64 if (targetNode != null) {
65 drawTargetNodeIndicator(graphics, getGraph2D().getRealizer(targetNode));
66 }
67 }
68
69 public Rectangle getBounds() {
70 if (targetNode != null) {
71 return getTargetNodeIndicatorBounds(getGraph2D().getRealizer(targetNode)).getBounds();
72 } else {
73 return new Rectangle(0, 0, -1, -1);
74 }
75 }
76 };
77 }
78
79 /**
80 * Overwritten to add an edge with a new target node to the current graph when a drag starts. Afterwards, this target
81 * node will be dragged around.
82 */
83 public void mousePressedLeft(double x, double y) {
84 if (sourceNode != null) {
85 final Graph2D graph = getGraph2D();
86 graph.firePreEvent();
87
88 // create the target node and the new edge
89 // the target node will be dragged around until a mouse release
90 draggedNode = graph.createNode();
91 UmlRealizerFactory.setNodeOpacity(graph.getRealizer(draggedNode), UmlRealizerFactory.TRANSPARENT);
92 graph.setCenter(draggedNode, x, y);
93 graph.createEdge(sourceNode, draggedNode);
94
95 super.mousePressedLeft(x, y);
96 }
97 }
98
99 /**
100 * Overwritten to just move the target node of the new edge.
101 *
102 * @return a {@link NodeList} with the target node as the only element.
103 */
104 protected NodeList getNodesToBeMoved() {
105 final NodeList nodesToBeMoved = new NodeList();
106 if (draggedNode != null) {
107 nodesToBeMoved.add(draggedNode);
108 }
109 return nodesToBeMoved;
110 }
111
112 /**
113 * Overwritten to keep track of the nodes over which the new target node moves. Nodes that contain the center of the
114 * target node are possible new target nodes for the edge and will be marked.
115 */
116 public void mouseDraggedLeft(final double x, final double y) {
117 if (sourceNode != null) {
118 // if the center of the dragged node lies within other nodes, the node in front gets selected as target node
119 updateTargetNode();
120
121 super.mouseDraggedLeft(x, y);
122 }
123 }
124
125 /**
126 * Updates the target node when the currently dragged node moves over other nodes. Nodes that contain the center of
127 * the target node are possible new target nodes for the edge and will be marked.
128 */
129 private void updateTargetNode() {
130 final Graph2D graph = getGraph2D();
131
132 // find a node that contains the center of the dragged node
133 // if there are several nodes the node in front is taken
134 Node newTargetNode = null;
135 for (NodeCursor nc = graph.nodes(); nc.ok(); nc.next()) {
136 final Node node = nc.node();
137 if (node != draggedNode) {
138 final NodeRealizer realizer = graph.getRealizer(node);
139 final Rectangle2D.Double bounds = realizer.getBoundingBox();
140 if (bounds.contains(graph.getCenterX(draggedNode), graph.getCenterY(draggedNode))
141 && (newTargetNode == null || newTargetNode.index() < node.index())) {
142 newTargetNode = node;
143 }
144 }
145 }
146
147 // if the target node changed, the target node indicator gets updated
148 if (newTargetNode != targetNode) {
149 targetNode = newTargetNode;
150 updateTargetNodeIndicator();
151 }
152 }
153
154 /**
155 * Updates the drawable that displays indication marks for the current target node.
156 */
157 private void updateTargetNodeIndicator() {
158 view.removeDrawable(targetNodeIndicator);
159 if (targetNode != null) {
160 view.addDrawable(targetNodeIndicator);
161 }
162 }
163
164 /**
165 * Overwritten to properly end edge creation. If the node was dragged onto another node, this other node becomes the
166 * new target node for the edge and the former target node gets removed.
167 */
168 public void mouseReleasedLeft(double x, double y) {
169 if (sourceNode != null) {
170 final Graph2D graph = getGraph2D();
171 final Edge draggedEdge = draggedNode.lastInEdge();
172 if (targetNode != null) {
173 // if the dragged node is dropped on another node, the other node becomes the new target and the dragged node
174 // is removed
175 graph.changeEdge(draggedEdge, draggedEdge.source(), targetNode);
176 graph.removeNode(draggedNode);
177 } else {
178 UmlRealizerFactory.setNodeOpacity(graph.getRealizer(draggedNode), UmlRealizerFactory.OPAQUE);
179 }
180
181 // clean up target node indicator
182 view.removeDrawable(targetNodeIndicator);
183
184 // end edge creation step for undo/redo
185 graph.firePostEvent();
186
187 edgeCreated(draggedEdge);
188
189 draggedNode = null;
190 targetNode = null;
191 sourceNode = null;
192
193 super.mouseReleasedLeft(x, y);
194 }
195 }
196
197 /**
198 * Overwritten to disable event handling from the right mouse button.
199 */
200 public void mouseDraggedRight(double x, double y) {
201 }
202
203 /**
204 * Overwritten to disable event handling from the right mouse button.
205 */
206 public void mouseReleasedRight(double x, double y) {
207 }
208
209 /**
210 * Overwritten to clean up this {@link y.view.ViewMode} in case edge creation was aborted.
211 */
212 public void cancelEditing() throws UnsupportedOperationException {
213 // resets all nodes and drawables
214 if (draggedNode != null) {
215 getGraph2D().removeNode(draggedNode);
216 }
217 draggedNode = null;
218 targetNode = null;
219 sourceNode = null;
220 view.removeDrawable(targetNodeIndicator);
221
222 super.cancelEditing();
223 }
224
225 /**
226 * Sets the source node for the newly created edge.
227 */
228 public void setSourceNode(Node sourceNode) {
229 this.sourceNode = sourceNode;
230 }
231
232 /**
233 * Callback to be able to react when edge creation is finished.
234 */
235 protected void edgeCreated(final Edge edge) {
236 final Graph2D graph = getGraph2D();
237 final DataProviderAdapter selectedEdges = new DataProviderAdapter() {
238 public boolean getBool(Object dataHolder) {
239 return dataHolder == edge;
240 }
241 };
242 edgeRouter.setSphereOfAction(EdgeRouter.ROUTE_SELECTED_EDGES);
243 graph.addDataProvider(EdgeRouter.SELECTED_EDGES, selectedEdges);
244 try {
245 final Graph2DLayoutExecutor executor = new Graph2DLayoutExecutor();
246 executor.getLayoutMorpher().setKeepZoomFactor(true);
247 executor.doLayout(view, edgeRouter);
248 } finally {
249 graph.removeDataProvider(EdgeRouter.SELECTED_EDGES);
250 }
251 }
252 }
253