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.networkmonitoring;
29  
30  import y.base.DataMap;
31  import y.base.Edge;
32  import y.base.Node;
33  import y.view.EdgeLabel;
34  import y.view.Graph2D;
35  import y.view.Graph2DTraversal;
36  import y.view.HitInfo;
37  import y.view.NodeLabel;
38  import y.view.NodeRealizer;
39  import y.view.ViewMode;
40  
41  import java.awt.Cursor;
42  
43  /**
44   * {@link ViewMode} to disable, enable or repair network nodes and edges and to show/hide info labels.
45   */
46  public class NetworkInteractionMode extends ViewMode {
47    private final DataMap ids;
48    private final NetworkModel model;
49  
50    public NetworkInteractionMode(NetworkModel model, DataMap ids) {
51      this.model = model;
52      this.ids = ids;
53    }
54  
55    /**
56     * Overwritten to change the show/hide info labels when a nodes was clicked or to change the state of network elements
57     * according to the buttons that where clicked.
58     *
59     * @param x the x-coordinate of the mouse event in world coordinates.
60     * @param y the y-coordinate of the mouse event in world coordinates.
61     */
62    public void mouseClicked(double x, double y) {
63      final Graph2D graph = getGraph2D();
64      final HitInfo hit = getHitInfo(x, y);
65      if (hitErrorSign(hit, x, y)) {
66        // hit sign was clicked so repair the belonging network node or connection respectively.
67        final Node hitNode = hit.getHitNode();
68        if (hitNode != null) {
69          final NetworkData data = NetworkMonitoringFactory.getNetworkData(graph.getRealizer(hitNode));
70          if (data.isBroken()) {
71            model.repairNetworkNode(ids.get(hitNode));
72          }
73        }
74        final EdgeLabel hitLabel = hit.getHitEdgeLabel();
75        if (hitLabel != null) {
76          final Edge edge = hitLabel.getEdge();
77          final NetworkData data = NetworkMonitoringFactory.getNetworkData(graph.getRealizer(edge));
78          if (data.isBroken()) {
79            model.repairEdge(ids.get(edge));
80          }
81        }
82      } else if (view.getZoom() > NetworkMonitoringDemo.LABEL_HIDE_ZOOM_LEVEL) {
83        // info labels are visible
84        if (hit.hasHitNodeLabels()) {
85          final NodeLabel hitNodeLabel = hit.getHitNodeLabel();
86          if (NetworkInfoLabelPainter.hitsCloseIcon(hitNodeLabel, x, y)) {
87            // close icon got hit => hide info label
88            hitNodeLabel.setVisible(false);
89          } else if (NetworkInfoLabelPainter.hitsStateChangeIcon(hitNodeLabel, x, y)) {
90            // state change icon got hit => update the state of the according network node
91            final Node node = hitNodeLabel.getNode();
92            final NetworkData data = NetworkMonitoringFactory.getNetworkData(graph.getRealizer(node));
93            if (data.isBroken()) {
94              model.repairNetworkNode(ids.get(node));
95            } else if (data.isDisabled()) {
96              model.enableNetworkNode(ids.get(node));
97            } else if (data.isOK()) {
98              model.disableNetworkNode(ids.get(node));
99            }
100         }
101       } else if (hit.hasHitNodes()) {
102         // node was hit => toggle visibility of the belonging info label
103         final Node hitNode = hit.getHitNode();
104         final NodeRealizer realizer = graph.getRealizer(hitNode);
105         final NodeLabel infoLabel = realizer.getLabel();
106         infoLabel.setVisible(!infoLabel.isVisible());
107       }
108     }
109   }
110 
111   /**
112    * Overwritten to indicate a possible interaction when moving the mouse over a node, an edge or an edge label by
113    * changing the mouse cursor visualization.
114    *
115    * @param x the x-coordinate of the mouse event in world coordinates.
116    * @param y the y-coordinate of the mouse event in world coordinates.
117    */
118   public void mouseMoved(double x, double y) {
119     changeCursor(x, y, Cursor.getDefaultCursor());
120   }
121 
122   /**
123    * Overwritten to show panning cursor when moving the view port by changing the mouse cursor visualization.
124    *
125    * @param x the x-coordinate of the mouse event in world coordinates.
126    * @param y the y-coordinate of the mouse event in world coordinates.
127    */
128   public void mouseDraggedLeft(double x, double y) {
129     view.setViewCursor(Cursor.getPredefinedCursor(Cursor.MOVE_CURSOR));
130   }
131 
132   /**
133    * Overwritten to avoid panning cursor when clicking on a node, an edge or an edge label by changing the mouse cursor
134    * visualization. Sadly, there is no closed hand cursor in AWT, so the normal hand is used
135    *
136    * @param x the x-coordinate of the mouse event in world coordinates.
137    * @param y the y-coordinate of the mouse event in world coordinates.
138    */
139   public void mousePressedLeft(double x, double y) {
140     changeCursor(x, y, Cursor.getPredefinedCursor(Cursor.MOVE_CURSOR));
141   }
142 
143   /**
144    * Overwritten to indicate a possible interaction when mouse is still over a node, an edge or an edge label by
145    * changing the mouse cursor visualization.
146    *
147    * @param x the x-coordinate of the mouse event in world coordinates.
148    * @param y the y-coordinate of the mouse event in world coordinates.
149    */
150   public void mouseReleasedLeft(double x, double y) {
151     changeCursor(x, y, Cursor.getDefaultCursor());
152   }
153 
154   private void changeCursor(double x, double y, Cursor defaultCursor) {
155     final HitInfo hit = getHitInfo(x, y);
156     if (hitsButton(hit, x, y) || hit.hasHitNodes()) {
157       view.setViewCursor(Cursor.getPredefinedCursor(Cursor.HAND_CURSOR));
158     } else {
159       view.setViewCursor(defaultCursor);
160     }
161   }
162 
163   /**
164    * Determines whether or not any kind of button is hit by the given coordinates.
165    *
166    * @param hit current hit information.
167    * @param x   x-coordinate of the location to be checked in world-coordinates.
168    * @param y   y-coordinate of the location to be checked in world-coordinates.
169    *
170    * @return <code>true</code> when a button is hit, <code>false</code> otherwise.
171    */
172   private boolean hitsButton(HitInfo hit, double x, double y) {
173     if (hitErrorSign(hit, x, y)) {
174       return true;
175     } else if (hit.hasHitNodeLabels() && view.getZoom() > NetworkMonitoringDemo.LABEL_HIDE_ZOOM_LEVEL) {
176       final NodeLabel hitNodeLabel = hit.getHitNodeLabel();
177       if (NetworkInfoLabelPainter.hitsCloseIcon(hitNodeLabel, x, y)
178             || NetworkInfoLabelPainter.hitsStateChangeIcon(hitNodeLabel, x, y)) {
179         return true;
180       }
181     }
182     return false;
183   }
184 
185   /**
186    * Determines whether or not a warning sign either on a network node or connection is hit by the given coordinates.
187    *
188    * @param hit current hit information.
189    * @param x   x-coordinate of the location to be checked in world-coordinates.
190    * @param y   y-coordinate of the location to be checked in world-coordinates.
191    *
192    * @return <code>true</code> when a warning sign is hit, <code>false</code> otherwise.
193    */
194   private boolean hitErrorSign(HitInfo hit, double x, double y) {
195     if (view.getZoom() > view.getPaintDetailThreshold()) {
196       if (hit.hasHitNodes()) {
197         final NodeRealizer realizer = getGraph2D().getRealizer(hit.getHitNode());
198         return NetworkNodePainter.hitWarningSign(realizer, x, y);
199       } else if (hit.hasHitEdgeLabels()) {
200         return true;
201       }
202     }
203     return false;
204   }
205 
206   /**
207    * Overwritten to get a {@link HitInfo} with all possible graph elements at the given coordinates.
208    */
209   protected HitInfo getHitInfo(double x, double y) {
210     return view.getHitInfoFactory().createHitInfo(x, y, Graph2DTraversal.ALL, false);
211   }
212 }
213