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.layout.router;
29  
30  import demo.view.advanced.EdgeConnectorDemo;
31  import y.base.Edge;
32  import y.base.Node;
33  import y.geom.YPoint;
34  import y.view.CreateEdgeMode;
35  import y.view.EdgeRealizer;
36  import y.view.Graph2D;
37  import y.view.Graph2DTraversal;
38  import y.view.HitInfo;
39  import y.view.NodeRealizer;
40  
41  import java.awt.Color;
42  
43  /**
44   * Extends {@link y.view.CreateEdgeMode} to introduce new hubs automatically at existing edges. This is similar to
45   * {@link demo.view.advanced.EdgeConnectorDemo.CreateEdgeConnectorMode}
46   * <p/>
47   * Each new edge is colored in the bus' color if the source of an edge creation is a hub.
48   * <p/>
49   * This class prevents the creation of edges which would connect hubs of the same bus.
50   * <p/>
51   * Usage: to create an edge that starts at another edge, shift-press on the edge to initiate the edge creation
52   * gesture, then drag the mouse. To create an edge that ends at another edge, shift-release the mouse on the edge.
53   */
54  public class HubCreateEdgeMode extends CreateEdgeMode {
55    private Node sourceNode;
56    private Node targetNode;
57    private Edge sourceSplitEdge;
58    private Edge targetSplitEdge;
59    private Color color;
60    private final NodeRealizer hubRealizer;
61  
62    public HubCreateEdgeMode(NodeRealizer hubRealizer) {
63      this.hubRealizer = hubRealizer;
64      allowSelfloopCreation(false);
65      setIndicatingTargetNode(true);
66    }
67  
68    public NodeRealizer getHubRealizer() {
69      return hubRealizer;
70    }
71  
72    public void mouseShiftPressedLeft(double x, double y) {
73      if (isEditing()) {
74        super.mouseShiftPressedLeft(x, y);
75      } else {
76        final Graph2D graph = getGraph2D();
77        final Edge edge = getHitInfo(x, y).getHitEdge();
78  
79        if (edge != null) {
80          final EdgeRealizer edgeRealizer = graph.getRealizer(edge);
81          final double[] pathPoint = EdgeConnectorDemo.PointPathProjector.calculateClosestPathPoint(
82              edgeRealizer.getPath(), x, y);
83          color = edgeRealizer.getLineColor();
84  
85          final NodeRealizer sourceRealizer = hubRealizer.createCopy();
86          sourceRealizer.setCenter(pathPoint[0], pathPoint[1]);
87          sourceRealizer.setFillColor(color);
88  
89          sourceNode = graph.createNode(sourceRealizer);
90          sourceSplitEdge = edge;
91          view.updateView();
92          super.mouseShiftPressedLeft(pathPoint[0], pathPoint[1]);
93        } else {
94          sourceNode = null;
95          super.mouseShiftPressedLeft(x, y);
96        }
97      }
98    }
99  
100   public void mouseShiftReleasedLeft(double x, double y) {
101     final Graph2D graph = getGraph2D();
102     final Edge edge = getHitInfo(x, y).getHitEdge();
103 
104     if (edge != null) {
105       final double[] pathPoint = EdgeConnectorDemo.PointPathProjector.calculateClosestPathPoint(
106           graph.getRealizer(edge).getPath(), x, y);
107 
108       final NodeRealizer targetRealizer = hubRealizer.createCopy();
109       targetRealizer.setCenter(pathPoint[0], pathPoint[1]);
110       targetRealizer.setFillColor(color);
111 
112       targetNode = graph.createNode(targetRealizer);
113       targetSplitEdge = edge;
114       view.updateView();
115       super.mouseShiftReleasedLeft(pathPoint[0], pathPoint[1]);
116     } else {
117       super.mouseShiftReleasedLeft(x, y);
118     }
119   }
120 
121   public HitInfo getHitInfo(double x, double y) {
122     final HitInfo info = view.getHitInfoFactory().createHitInfo(x, y, Graph2DTraversal.ALL, false);
123     setLastHitInfo(info);
124     return info;
125   }
126 
127   protected void edgeCreated(Edge edge) {
128     if (sourceNode != null) {
129       splitEdge(sourceSplitEdge, sourceNode);
130     }
131     if (targetNode != null) {
132       splitEdge(targetSplitEdge, targetNode);
133     }
134     super.edgeCreated(edge);
135   }
136 
137   /**
138    * Splits the edge into two parts by introducing the given node as intermediate.
139    */
140   private void splitEdge(Edge edge, Node node) {
141     final Graph2D graph = getGraph2D();
142     final Node oldSource = edge.source();
143     final Node oldTarget = edge.target();
144 
145     graph.firePreEvent();
146     // Notes: - pre and post events should frame the node creation also
147     //        - the following works only if the edge is straight-line;
148     //          otherwise we have to split its path and distribute the bends correctly
149 
150     // Do not change the edge which connects to a regular node since for example a NodePort is bound to it.
151     // Note that at least one of oldSource and oldTarget is a hub since otherwise this method is not called.
152     final EdgeRealizer originalRealizer = graph.getRealizer(edge).createCopy();
153     if (!BusRouterDemo.isHub(oldSource)) {
154       graph.changeEdge(edge, oldSource, node);
155 
156       final Edge edgeToTarget = graph.createEdge(node, oldTarget);
157       graph.setRealizer(edgeToTarget, originalRealizer);
158       graph.setSourcePointRel(edgeToTarget, YPoint.ORIGIN);
159     } else {
160       graph.changeEdge(edge, node, oldTarget);
161 
162       final Edge edgeFromSource = graph.createEdge(oldSource, node);
163       graph.setRealizer(edgeFromSource, originalRealizer);
164       graph.setTargetPointRel(edgeFromSource, YPoint.ORIGIN);
165     }
166 
167     graph.firePostEvent();
168   }
169 
170   protected void cancelEdgeCreation() {
171     if (sourceNode != null) {
172       Node tmp = sourceNode;
173       sourceNode = null;
174       getGraph2D().removeNode(tmp);
175     }
176     if (targetNode != null) {
177       Node tmp = targetNode;
178       targetNode = null;
179       getGraph2D().removeNode(tmp);
180     }
181 
182     super.cancelEdgeCreation();
183   }
184 
185   public void setEditing(boolean editing) {
186     if (!editing) {
187       sourceNode = null;
188       sourceSplitEdge = null;
189       targetNode = null;
190       targetSplitEdge = null;
191       color = null;
192     }
193 
194     super.setEditing(editing);
195   }
196 
197   /**
198    * Accepts a source node if it is a hub or if it is a regular node and has node ports. Additionally, queries the bus
199    * color from a hub node if not already set.
200    */
201   protected boolean acceptSourceNode(Node source, double x, double y) {
202     if (color == null) {
203       color = BusRouterDemo.isHub(source) ? view.getGraph2D().getRealizer(source).getFillColor() : Color.BLACK;
204     }
205     return BusRouterDemo.isHub(source) || super.acceptSourceNode(source, x, y);
206   }
207 
208   /**
209    * Accepts a target node if it is a hub of a different bus or if it is a regular node with node ports.
210    */
211   protected boolean acceptTargetNode(Node target, double x, double y) {
212     return (BusRouterDemo.isHub(target) && color != null && !color.equals(
213         getGraph2D().getRealizer(target).getFillColor())) || super.acceptTargetNode(target, x, y);
214   }
215 
216 
217   /**
218    * Returns a dummy EdgeRealizer which has the current bus color set.
219    */
220   protected EdgeRealizer getDummyEdgeRealizer() {
221     final EdgeRealizer realizer = super.getDummyEdgeRealizer();
222     if (color != null) {
223       realizer.setLineColor(color);
224     }
225     return realizer;
226   }
227 }
228