1 /*
2  * Copyright (c) 2008, 2015, Oracle and/or its affiliates. All rights reserved.
3  * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER.
4  *
5  * This code is free software; you can redistribute it and/or modify it
6  * under the terms of the GNU General Public License version 2 only, as
7  * published by the Free Software Foundation.
8  *
9  * This code is distributed in the hope that it will be useful, but WITHOUT
10  * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
11  * FITNESS FOR A PARTICULAR PURPOSE.  See the GNU General Public License
12  * version 2 for more details (a copy is included in the LICENSE file that
13  * accompanied this code).
14  *
15  * You should have received a copy of the GNU General Public License version
16  * 2 along with this work; if not, write to the Free Software Foundation,
17  * Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA.
18  *
19  * Please contact Oracle, 500 Oracle Parkway, Redwood Shores, CA 94065 USA
20  * or visit www.oracle.com if you need additional information or have any
21  * questions.
22  *
23  */
24 package com.sun.hotspot.igv.controlflow;
25 
26 import com.sun.hotspot.igv.data.InputBlockEdge;
27 import com.sun.hotspot.igv.data.InputBlock;
28 import com.sun.hotspot.igv.data.InputGraph;
29 import com.sun.hotspot.igv.data.services.InputGraphProvider;
30 import com.sun.hotspot.igv.data.InputNode;
31 import com.sun.hotspot.igv.util.LookupHistory;
32 import java.awt.Color;
33 import java.awt.Point;
34 import java.awt.Rectangle;
35 import java.util.ArrayList;
36 import java.util.HashSet;
37 import java.util.Set;
38 import javax.swing.BorderFactory;
39 import org.netbeans.api.visual.action.ActionFactory;
40 import org.netbeans.api.visual.action.MoveProvider;
41 import org.netbeans.api.visual.action.RectangularSelectDecorator;
42 import org.netbeans.api.visual.action.RectangularSelectProvider;
43 import org.netbeans.api.visual.action.SelectProvider;
44 import org.netbeans.api.visual.action.WidgetAction;
45 import org.netbeans.api.visual.anchor.AnchorFactory;
46 import org.netbeans.api.visual.anchor.AnchorShape;
47 import org.netbeans.api.visual.router.RouterFactory;
48 import org.netbeans.api.visual.widget.LayerWidget;
49 import org.netbeans.api.visual.widget.Widget;
50 import org.netbeans.api.visual.graph.GraphScene;
51 import org.netbeans.api.visual.graph.layout.GraphLayout;
52 import org.netbeans.api.visual.layout.LayoutFactory;
53 import org.netbeans.api.visual.layout.SceneLayout;
54 import org.netbeans.api.visual.widget.ConnectionWidget;
55 
56 /**
57  *
58  * @author Thomas Wuerthinger
59  */
60 public class ControlFlowScene extends GraphScene<InputBlock, InputBlockEdge> implements SelectProvider, MoveProvider, RectangularSelectDecorator, RectangularSelectProvider {
61 
62     private HashSet<BlockWidget> selection;
63     private InputGraph oldGraph;
64     private LayerWidget edgeLayer;
65     private LayerWidget mainLayer;
66     private LayerWidget selectLayer;
67     private WidgetAction hoverAction = this.createWidgetHoverAction();
68     private WidgetAction selectAction = new DoubleClickSelectAction(this);
69     private WidgetAction moveAction = ActionFactory.createMoveAction(null, this);
70 
ControlFlowScene()71     public ControlFlowScene() {
72         selection = new HashSet<BlockWidget>();
73 
74         this.getInputBindings().setZoomActionModifiers(0);
75         this.setLayout(LayoutFactory.createAbsoluteLayout());
76 
77         mainLayer = new LayerWidget(this);
78         this.addChild(mainLayer);
79 
80         edgeLayer = new LayerWidget(this);
81         this.addChild(edgeLayer);
82 
83         selectLayer = new LayerWidget(this);
84         this.addChild(selectLayer);
85 
86         this.getActions().addAction(hoverAction);
87         this.getActions().addAction(selectAction);
88         this.getActions().addAction(ActionFactory.createRectangularSelectAction(this, selectLayer, this));
89         this.getActions().addAction(ActionFactory.createMouseCenteredZoomAction(1.1));
90     }
91 
setGraph(InputGraph g)92     public void setGraph(InputGraph g) {
93         if (g == oldGraph) {
94             return;
95         }
96         oldGraph = g;
97 
98         ArrayList<InputBlock> blocks = new ArrayList<InputBlock>(this.getNodes());
99         for (InputBlock b : blocks) {
100             removeNode(b);
101         }
102 
103         ArrayList<InputBlockEdge> edges = new ArrayList<InputBlockEdge>(this.getEdges());
104         for (InputBlockEdge e : edges) {
105             removeEdge(e);
106         }
107 
108         for (InputBlock b : g.getBlocks()) {
109             addNode(b);
110         }
111 
112         for (InputBlockEdge e : g.getBlockEdges()) {
113             addEdge(e);
114             assert g.getBlocks().contains(e.getFrom());
115             assert g.getBlocks().contains(e.getTo());
116             this.setEdgeSource(e, e.getFrom());
117             this.setEdgeTarget(e, e.getTo());
118         }
119 
120         GraphLayout<InputBlock, InputBlockEdge> layout = new HierarchicalGraphLayout<InputBlock, InputBlockEdge>();//GridGraphLayout();
121         SceneLayout sceneLayout = LayoutFactory.createSceneGraphLayout(this, layout);
122         sceneLayout.invokeLayout();
123 
124         this.validate();
125     }
126 
clearSelection()127     public void clearSelection() {
128         for (BlockWidget w : selection) {
129             w.setState(w.getState().deriveSelected(false));
130         }
131         selection.clear();
132         selectionChanged();
133     }
134 
selectionChanged()135     public void selectionChanged() {
136         InputGraphProvider p = LookupHistory.getLast(InputGraphProvider.class);//)Utilities.actionsGlobalContext().lookup(InputGraphProvider.class);
137         if (p != null) {
138             Set<InputNode> inputNodes = new HashSet<InputNode>();
139             for (BlockWidget w : selection) {
140                 inputNodes.addAll(w.getBlock().getNodes());
141             }
142             p.setSelectedNodes(inputNodes);
143         }
144     }
145 
addToSelection(BlockWidget widget)146     public void addToSelection(BlockWidget widget) {
147         widget.setState(widget.getState().deriveSelected(true));
148         selection.add(widget);
149         selectionChanged();
150     }
151 
removeFromSelection(BlockWidget widget)152     public void removeFromSelection(BlockWidget widget) {
153         widget.setState(widget.getState().deriveSelected(false));
154         selection.remove(widget);
155         selectionChanged();
156     }
157 
isAimingAllowed(Widget widget, Point point, boolean b)158     public boolean isAimingAllowed(Widget widget, Point point, boolean b) {
159         return false;
160     }
161 
isSelectionAllowed(Widget widget, Point point, boolean b)162     public boolean isSelectionAllowed(Widget widget, Point point, boolean b) {
163         return true;
164     }
165 
select(Widget widget, Point point, boolean change)166     public void select(Widget widget, Point point, boolean change) {
167         if (widget == this) {
168             clearSelection();
169         } else {
170 
171             assert widget instanceof BlockWidget;
172             BlockWidget bw = (BlockWidget) widget;
173             if (change) {
174                 if (selection.contains(bw)) {
175                     removeFromSelection(bw);
176                 } else {
177                     addToSelection(bw);
178                 }
179             } else {
180                 if (!selection.contains(bw)) {
181                     clearSelection();
182                     addToSelection(bw);
183                 }
184             }
185         }
186     }
187 
movementStarted(Widget widget)188     public void movementStarted(Widget widget) {
189     }
190 
movementFinished(Widget widget)191     public void movementFinished(Widget widget) {
192     }
193 
getOriginalLocation(Widget widget)194     public Point getOriginalLocation(Widget widget) {
195         return widget.getPreferredLocation();
196     }
197 
setNewLocation(Widget widget, Point location)198     public void setNewLocation(Widget widget, Point location) {
199         if (selection.contains(widget)) {
200             // move entire selection
201             Point originalLocation = getOriginalLocation(widget);
202             int xOffset = location.x - originalLocation.x;
203             int yOffset = location.y - originalLocation.y;
204             for (Widget w : selection) {
205                 Point p = new Point(w.getPreferredLocation());
206                 p.translate(xOffset, yOffset);
207                 w.setPreferredLocation(p);
208             }
209         } else {
210             widget.setPreferredLocation(location);
211         }
212     }
213 
createSelectionWidget()214     public Widget createSelectionWidget() {
215         Widget widget = new Widget(this);
216         widget.setOpaque(false);
217         widget.setBorder(BorderFactory.createLineBorder(Color.black, 2));
218         widget.setForeground(Color.red);
219         return widget;
220     }
221 
performSelection(Rectangle rectangle)222     public void performSelection(Rectangle rectangle) {
223 
224         if (rectangle.width < 0) {
225             rectangle.x += rectangle.width;
226             rectangle.width *= -1;
227         }
228 
229         if (rectangle.height < 0) {
230             rectangle.y += rectangle.height;
231             rectangle.height *= -1;
232         }
233 
234         boolean changed = false;
235         for (InputBlock b : this.getNodes()) {
236             BlockWidget w = (BlockWidget) findWidget(b);
237             Rectangle r = new Rectangle(w.getBounds());
238             r.setLocation(w.getLocation());
239             if (r.intersects(rectangle)) {
240                 if (!selection.contains(w)) {
241                     changed = true;
242                     selection.add(w);
243                     w.setState(w.getState().deriveSelected(true));
244                 }
245             } else {
246                 if (selection.contains(w)) {
247                     changed = true;
248                     selection.remove(w);
249                     w.setState(w.getState().deriveSelected(false));
250                 }
251             }
252         }
253 
254         if (changed) {
255             selectionChanged();
256         }
257 
258     }
259 
attachNodeWidget(InputBlock node)260     protected Widget attachNodeWidget(InputBlock node) {
261         BlockWidget w = new BlockWidget(this, node);
262         mainLayer.addChild(w);
263         w.getActions().addAction(hoverAction);
264         w.getActions().addAction(selectAction);
265         w.getActions().addAction(moveAction);
266         return w;
267     }
268 
attachEdgeWidget(InputBlockEdge edge)269     protected Widget attachEdgeWidget(InputBlockEdge edge) {
270         BlockConnectionWidget w = new BlockConnectionWidget(this, edge);
271         switch (edge.getState()) {
272             case NEW:
273                 w.setBold(true);
274                 break;
275             case DELETED:
276                 w.setDashed(true);
277                 break;
278         }
279         w.setRouter(RouterFactory.createDirectRouter());
280         w.setTargetAnchorShape(AnchorShape.TRIANGLE_FILLED);
281         edgeLayer.addChild(w);
282         return w;
283     }
284 
attachEdgeSourceAnchor(InputBlockEdge edge, InputBlock oldSourceNode, InputBlock sourceNode)285     protected void attachEdgeSourceAnchor(InputBlockEdge edge, InputBlock oldSourceNode, InputBlock sourceNode) {
286         Widget w = this.findWidget(edge);
287         assert w instanceof ConnectionWidget;
288         ConnectionWidget cw = (ConnectionWidget) w;
289         cw.setSourceAnchor(AnchorFactory.createRectangularAnchor(findWidget(sourceNode)));
290 
291     }
292 
attachEdgeTargetAnchor(InputBlockEdge edge, InputBlock oldTargetNode, InputBlock targetNode)293     protected void attachEdgeTargetAnchor(InputBlockEdge edge, InputBlock oldTargetNode, InputBlock targetNode) {
294         Widget w = this.findWidget(edge);
295         assert w instanceof ConnectionWidget;
296         ConnectionWidget cw = (ConnectionWidget) w;
297         cw.setTargetAnchor(AnchorFactory.createRectangularAnchor(findWidget(targetNode)));
298     }
299 }
300