001    package nl.cwi.sen1.gui.plugin;
002    
003    import java.awt.BorderLayout;
004    import java.awt.Color;
005    import java.awt.Point;
006    import java.awt.event.MouseEvent;
007    import java.awt.geom.AffineTransform;
008    import java.awt.geom.NoninvertibleTransformException;
009    import java.util.HashSet;
010    import java.util.Iterator;
011    import java.util.Set;
012    
013    import javax.swing.AbstractAction;
014    import javax.swing.JPanel;
015    
016    import nl.cwi.sen1.graph.types.Attribute;
017    import nl.cwi.sen1.util.Preferences;
018    import prefuse.Constants;
019    import prefuse.Display;
020    import prefuse.Visualization;
021    import prefuse.action.Action;
022    import prefuse.action.ActionList;
023    import prefuse.action.RepaintAction;
024    import prefuse.action.animate.LocationAnimator;
025    import prefuse.action.animate.PolarLocationAnimator;
026    import prefuse.action.assignment.ColorAction;
027    import prefuse.action.assignment.FontAction;
028    import prefuse.action.layout.CircleLayout;
029    import prefuse.action.layout.RandomLayout;
030    import prefuse.action.layout.graph.ForceDirectedLayout;
031    import prefuse.action.layout.graph.FruchtermanReingoldLayout;
032    import prefuse.action.layout.graph.NodeLinkTreeLayout;
033    import prefuse.action.layout.graph.RadialTreeLayout;
034    import prefuse.activity.Activity;
035    import prefuse.activity.SlowInSlowOutPacer;
036    import prefuse.controls.Control;
037    import prefuse.controls.ControlAdapter;
038    import prefuse.controls.DragControl;
039    import prefuse.controls.FocusControl;
040    import prefuse.controls.PanControl;
041    import prefuse.controls.ToolTipControl;
042    import prefuse.controls.ZoomControl;
043    import prefuse.controls.ZoomToFitControl;
044    import prefuse.data.Graph;
045    import prefuse.data.Tuple;
046    import prefuse.data.event.TupleSetListener;
047    import prefuse.data.tuple.TupleSet;
048    import prefuse.render.DefaultRendererFactory;
049    import prefuse.render.EdgeRenderer;
050    import prefuse.render.LabelRenderer;
051    import prefuse.util.ColorLib;
052    import prefuse.util.display.ExportDisplayAction;
053    import prefuse.util.force.DragForce;
054    import prefuse.util.force.ForceSimulator;
055    import prefuse.util.force.NBodyForce;
056    import prefuse.util.force.SpringForce;
057    import prefuse.visual.NodeItem;
058    import prefuse.visual.VisualItem;
059    import prefuse.visual.expression.InGroupPredicate;
060    
061    public class GraphPanel extends JPanel {
062            private String type;
063    
064            private String id;
065    
066            private Display display;
067    
068            private Visualization vis;
069    
070            private GraphPanelListener listener;
071    
072            private Activity currentLayout;
073    
074            private Activity currentAnimation;
075    
076            private Set<String> layouts;
077    
078            private EdgeRenderer edgeRenderer;
079    
080            private ForceSimulator forceSimulator;
081    
082            private Boolean closable;
083    
084            public void setClosable(Boolean closable) {
085                    this.closable = closable;
086            }
087    
088            public Boolean isClosable() {
089                    return closable;
090            }
091    
092            public GraphPanel(String type, String id, final Preferences prefs,
093                            boolean closable) {
094                    this.id = id;
095                    this.type = type;
096                    this.closable = closable;
097                    setLayout(new BorderLayout());
098    
099                    vis = new Visualization();
100    
101                    LabelRenderer lr = new GraphNodeRenderer(GraphConstants.LABEL);
102                    lr.setHorizontalPadding(prefs.getInt(GraphConstants.NODE_BORDER_WIDTH));
103                    lr.setVerticalPadding(prefs.getInt(GraphConstants.NODE_BORDER_HEIGHT));
104                    edgeRenderer = new GraphEdgeRenderer(Constants.EDGE_TYPE_LINE,
105                                    Constants.EDGE_ARROW_FORWARD);
106                    vis.setRendererFactory(new DefaultRendererFactory(lr, edgeRenderer));
107    
108                    ColorAction nStroke = new ColorAction(GraphConstants.NODES,
109                                    VisualItem.STROKECOLOR);
110                    nStroke.setDefaultColor(prefs.getColor(GraphConstants.NODE_FOREGROUND)
111                                    .getRGB());
112                    nStroke.add(new InGroupPredicate(Visualization.FOCUS_ITEMS), prefs
113                                    .getColor(GraphConstants.NODE_SELECTED_FOREGROUND).getRGB());
114    
115                    ColorAction nFill = new GraphColorAction(GraphConstants.NODES,
116                                    GraphConstants.FILLCOLOR, VisualItem.FILLCOLOR);
117                    nFill.setDefaultColor(prefs.getColor(GraphConstants.NODE_BACKGROUND)
118                                    .getRGB());
119                    nFill.add(new InGroupPredicate(Visualization.FOCUS_ITEMS), prefs
120                                    .getColor(GraphConstants.NODE_SELECTED_BACKGROUND).getRGB());
121    
122                    ColorAction eStroke = new ColorAction(GraphConstants.EDGES,
123                                    VisualItem.STROKECOLOR);
124                    eStroke.setDefaultColor(Color.BLACK.getRGB());
125    
126                    ColorAction eFill = new ColorAction(GraphConstants.EDGES,
127                                    VisualItem.FILLCOLOR);
128                    eStroke.setDefaultColor(Color.BLACK.getRGB());
129    
130                    ColorAction text = new ColorAction(GraphConstants.NODES,
131                                    VisualItem.TEXTCOLOR);
132                    text.setDefaultColor(ColorLib.gray(50));
133    
134                    FontAction font = new FontAction(GraphConstants.NODES, prefs
135                                    .getFont(GraphConstants.NODE_FONT));
136    
137                    ActionList draw = new ActionList();
138                    draw.add(text);
139                    draw.add(nStroke);
140                    draw.add(nFill);
141                    draw.add(eStroke);
142                    draw.add(eFill);
143                    draw.add(font);
144                    draw.add(new RepaintAction());
145    
146                    vis.putAction("draw", draw);
147    
148                    display = new Display(vis);
149                    display.setHighQuality(true);
150                    display.addControlListener(new DragControl());
151                    display.addControlListener(new ZoomControl());
152                    ZoomToFitControl ztf = new ZoomToFitControl();
153                    ztf.setZoomOverItem(false);
154                    display.addControlListener(ztf);
155                    display.addControlListener(new PanControl());
156                    display.addControlListener(new FocusControl());
157                    display.addControlListener(new ControlAdapter() {
158                            public void itemEntered(VisualItem item, MouseEvent e) {
159                                    if (item.isInGroup(GraphConstants.NODES)) {
160                                            item.setFillColor(prefs.getColor(
161                                                            GraphConstants.NODE_HOVERED_BACKGROUND).getRGB());
162                                            item.setStrokeColor(prefs.getColor(
163                                                            GraphConstants.NODE_HOVERED_FOREGROUND).getRGB());
164                                            item.getVisualization().repaint();
165                                    }
166                            }
167    
168                            public void itemExited(VisualItem item, MouseEvent e) {
169                                    if (item.isInGroup(GraphConstants.NODES)) {
170                                            if (item.isInGroup(Visualization.FOCUS_ITEMS)) {
171                                                    item.setFillColor(prefs.getColor(
172                                                                    GraphConstants.NODE_SELECTED_BACKGROUND)
173                                                                    .getRGB());
174                                                    item.setStrokeColor(prefs.getColor(
175                                                                    GraphConstants.NODE_SELECTED_FOREGROUND)
176                                                                    .getRGB());
177                                            } else {
178                                                    item
179                                                                    .setFillColor(item
180                                                                                    .getInt(GraphConstants.FILLCOLOR));
181                                                    item.setStrokeColor(prefs.getColor(
182                                                                    GraphConstants.NODE_FOREGROUND).getRGB());
183                                            }
184                                            item.getVisualization().repaint();
185                                    }
186                            }
187    
188                            /*
189                             * public void itemPressed(VisualItem item, MouseEvent e) { if
190                             * (e.isPopupTrigger()) { String id =
191                             * item.getString(GraphConstants.ID); if (id != null) {
192                             * firePopupRequested(id, e); } } } }
193                             */
194                    });
195                    display.addControlListener(new ToolTipControl(GraphConstants.TOOLTIP));
196    
197                    TupleSet focusGroup = vis.getGroup(Visualization.FOCUS_ITEMS);
198                    focusGroup.addTupleSetListener(new TupleSetListener() {
199                            public void tupleSetChanged(TupleSet ts, Tuple[] add, Tuple[] rem) {
200                                    for (Tuple t : add) {
201                                            VisualItem item = (VisualItem) t;
202                                            if (item instanceof NodeItem) {
203                                                    fireNodeSelected(item.getString(GraphConstants.ID));
204                                                    vis.run("draw");
205                                                    vis.repaint();
206                                            }
207                                    }
208                            }
209                    });
210    
211                    createLayouts();
212                    setPolarAnimation();
213    
214                    add(display, BorderLayout.CENTER);
215            }
216    
217            public void addControlListener(Control cl) {
218                    display.addControlListener(cl);
219            }
220    
221            public AbstractAction getSaveImageAction() {
222                    return new ExportDisplayAction(display);
223            }
224    
225            public void setCurvedEdges() {
226                    edgeRenderer.setEdgeType(Constants.EDGE_TYPE_CURVE);
227                    runNow();
228            }
229    
230            public void setStraightEdges() {
231                    edgeRenderer.setEdgeType(Constants.EDGE_TYPE_LINE);
232                    runNow();
233            }
234    
235            public void setNoAnimation() {
236                    ActionList animation = new ActionList(vis);
237                    animation.add(new RepaintAction());
238                    vis.putAction("no-animation", animation);
239                    currentAnimation = animation;
240            }
241    
242            public void setPolarAnimation() {
243                    ActionList animation = new ActionList(vis, 1500);
244                    animation.setPacingFunction(new SlowInSlowOutPacer());
245                    animation.add(new PolarLocationAnimator());
246                    animation.add(new RepaintAction());
247                    vis.putAction("polar-animation", animation);
248                    currentAnimation = animation;
249            }
250    
251            public void setLinearAnimation() {
252                    ActionList animation = new ActionList(vis, 1500);
253                    animation.setPacingFunction(new SlowInSlowOutPacer());
254                    animation.add(new LocationAnimator());
255                    animation.add(new RepaintAction());
256                    vis.putAction("linear-animation", animation);
257                    currentAnimation = animation;
258            }
259    
260            private void createLayouts() {
261                    layouts = new HashSet<String>();
262    
263                    ActionList dot = new ActionList();
264                    dot.add(new GraphDotLayout(GraphConstants.GRAPH));
265                    dot.add(new RepaintAction());
266                    vis.putAction("Dot", dot);
267                    layouts.add("Dot");
268    
269                    forceSimulator = new ForceSimulator();
270                    forceSimulator.addForce(new NBodyForce());
271                    forceSimulator.addForce(new SpringForce());
272                    forceSimulator.addForce(new DragForce());
273    
274                    ActionList contforce = new ActionList(Action.INFINITY);
275                    contforce.add(new ForceDirectedLayout(GraphConstants.GRAPH,
276                                    forceSimulator, false));
277                    contforce.add(new RepaintAction());
278                    vis.putAction("Force", contforce);
279                    layouts.add("Force");
280    
281                    ActionList random = new ActionList();
282                    random.add(new RandomLayout());
283                    random.add(new RepaintAction());
284                    vis.putAction("Random", random);
285                    layouts.add("Random");
286    
287                    ActionList circle = new ActionList();
288                    circle.add(new CircleLayout(GraphConstants.NODES));
289                    circle.add(new RepaintAction());
290                    vis.putAction("Circle", circle);
291                    layouts.add("Circle");
292    
293                    ActionList nodeLink = new ActionList();
294                    nodeLink.add(new NodeLinkTreeLayout(GraphConstants.GRAPH));
295                    nodeLink.add(new RepaintAction());
296                    vis.putAction("NodeLink Horizontal", nodeLink);
297                    layouts.add("NodeLink Horizontal");
298    
299                    ActionList nodeLinkV = new ActionList();
300                    NodeLinkTreeLayout nodeLinkVlayout = new NodeLinkTreeLayout(
301                                    GraphConstants.GRAPH);
302                    nodeLinkVlayout.setOrientation(Constants.ORIENT_TOP_BOTTOM);
303                    nodeLinkV.add(nodeLinkVlayout);
304                    nodeLinkV.add(new RepaintAction());
305                    vis.putAction("NodeLink Vertical", nodeLinkV);
306                    layouts.add("NodeLink Vertical");
307    
308                    ActionList radialTree = new ActionList();
309                    radialTree.add(new RadialTreeLayout(GraphConstants.GRAPH));
310                    radialTree.add(new RepaintAction());
311                    vis.putAction("Radial", radialTree);
312                    layouts.add("Radial");
313    
314                    ActionList funny = new ActionList();
315                    funny.add(new FruchtermanReingoldLayout(GraphConstants.GRAPH));
316                    funny.add(new RepaintAction());
317                    vis.putAction("Funny", funny);
318                    layouts.add("Funny");
319            }
320    
321            public ForceSimulator getForceSimulator() {
322                    return forceSimulator;
323            }
324    
325            public Object[] getLayouts() {
326                    return layouts.toArray();
327            }
328    
329            public void setLayout(String name) {
330                    Action newLayout = vis.getAction(name);
331    
332                    if (newLayout != null) {
333                            cancel();
334                            currentLayout = newLayout;
335                            runNow();
336                    } else {
337                            throw new UnsupportedOperationException("Layout " + name
338                                            + " does not exist");
339                    }
340            }
341    
342            private void runNow() {
343                    if (currentLayout != null) {
344                            vis.run("draw");
345                            currentLayout.run();
346                            vis.repaint();
347                            currentAnimation.run();
348                    } else {
349                            System.err.println("No current layout!");
350                    }
351            }
352    
353            private void cancel() {
354                    if (currentLayout != null) {
355                            currentLayout.cancel();
356                    }
357            }
358    
359            protected void resize() {
360                    setSize(getParent().getWidth(), getParent().getHeight());
361            }
362    
363            void setGraphPanelListener(GraphPanelListener l) {
364                    this.listener = l;
365            }
366    
367            protected void fireNodeSelected(String selectedNodeId) {
368                    listener.nodeSelected(selectedNodeId);
369            }
370    
371            void setGraph(Graph g) {
372                    vis.removeGroup(GraphConstants.GRAPH);
373                    vis.addGraph(GraphConstants.GRAPH, g);
374                    Activity oldAnimation = currentAnimation;
375                    setNoAnimation();
376                    runNow();
377                    currentAnimation = oldAnimation;
378            }
379    
380            public void updateNode(String nodeId, Attribute attr) {
381                    VisualItem node = findNode(nodeId);
382                    if (node != null) {
383                            GraphAdapter.updateNode(node, attr);
384                            node.setValidated(false);
385                            vis.run("draw");
386                            vis.repaint();
387                    }
388            }
389    
390            public void setSelectedNode(String nodeId) {
391                    VisualItem node = findNode(nodeId);
392                    if (node != null) {
393                            TupleSet focusGroup = vis.getGroup(Visualization.FOCUS_ITEMS);
394                            focusGroup.setTuple(node);
395                            vis.run("draw");
396                            vis.repaint();
397                            Point p = new Point((int)node.getX(), (int)node.getY());
398                            display.animatePanToAbs(p, 2000);
399                    }
400    
401            }
402    
403            private VisualItem findNode(String nodeId) {
404                    Graph g = (Graph) vis.getGroup(GraphConstants.GRAPH);
405                    if (g != null) {
406                            Iterator<?> nodeIter = g.nodes();
407                            while (nodeIter.hasNext()) {
408                                    VisualItem node = (VisualItem) nodeIter.next();
409                                    if (node.getString(GraphConstants.ID).equals(nodeId)) {
410                                            return node;
411                                    }
412                            }
413                    }
414    
415                    return null;
416            }
417    
418            public void restoreZoomAndPan() {
419                    try {
420                            display.setTransform(new AffineTransform());
421                            display.repaint();
422                    } catch (NoninvertibleTransformException e) {
423                            // this does not happen
424                    }
425            }
426    
427            public String getId() {
428                    return id;
429            }
430    
431            public String getType() {
432                    return type;
433            }
434    }