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 */
24package com.sun.hotspot.igv.controlflow;
25
26import com.sun.hotspot.igv.data.InputBlockEdge;
27import com.sun.hotspot.igv.data.InputBlock;
28import com.sun.hotspot.igv.data.InputGraph;
29import com.sun.hotspot.igv.data.services.InputGraphProvider;
30import com.sun.hotspot.igv.data.InputNode;
31import com.sun.hotspot.igv.util.LookupHistory;
32import java.awt.Color;
33import java.awt.Point;
34import java.awt.Rectangle;
35import java.util.ArrayList;
36import java.util.HashSet;
37import java.util.Set;
38import javax.swing.BorderFactory;
39import org.netbeans.api.visual.action.ActionFactory;
40import org.netbeans.api.visual.action.MoveProvider;
41import org.netbeans.api.visual.action.RectangularSelectDecorator;
42import org.netbeans.api.visual.action.RectangularSelectProvider;
43import org.netbeans.api.visual.action.SelectProvider;
44import org.netbeans.api.visual.action.WidgetAction;
45import org.netbeans.api.visual.anchor.AnchorFactory;
46import org.netbeans.api.visual.anchor.AnchorShape;
47import org.netbeans.api.visual.router.RouterFactory;
48import org.netbeans.api.visual.widget.LayerWidget;
49import org.netbeans.api.visual.widget.Widget;
50import org.netbeans.api.visual.graph.GraphScene;
51import org.netbeans.api.visual.graph.layout.GraphLayout;
52import org.netbeans.api.visual.layout.LayoutFactory;
53import org.netbeans.api.visual.layout.SceneLayout;
54import org.netbeans.api.visual.widget.ConnectionWidget;
55
56/**
57 *
58 * @author Thomas Wuerthinger
59 */
60public 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
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
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
127    public void clearSelection() {
128        for (BlockWidget w : selection) {
129            w.setState(w.getState().deriveSelected(false));
130        }
131        selection.clear();
132        selectionChanged();
133    }
134
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
146    public void addToSelection(BlockWidget widget) {
147        widget.setState(widget.getState().deriveSelected(true));
148        selection.add(widget);
149        selectionChanged();
150    }
151
152    public void removeFromSelection(BlockWidget widget) {
153        widget.setState(widget.getState().deriveSelected(false));
154        selection.remove(widget);
155        selectionChanged();
156    }
157
158    public boolean isAimingAllowed(Widget widget, Point point, boolean b) {
159        return false;
160    }
161
162    public boolean isSelectionAllowed(Widget widget, Point point, boolean b) {
163        return true;
164    }
165
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
188    public void movementStarted(Widget widget) {
189    }
190
191    public void movementFinished(Widget widget) {
192    }
193
194    public Point getOriginalLocation(Widget widget) {
195        return widget.getPreferredLocation();
196    }
197
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
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
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
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
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
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
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