1
14 package demo.view.orgchart;
15
16 import java.awt.Dimension;
17 import java.awt.event.ActionEvent;
18 import java.awt.event.InputEvent;
19 import java.awt.event.KeyEvent;
20 import java.awt.event.MouseWheelListener;
21 import java.awt.geom.Point2D;
22 import java.beans.PropertyChangeListener;
23 import java.util.HashMap;
24 import java.util.HashSet;
25 import java.util.Iterator;
26 import java.util.List;
27 import java.util.Map;
28
29 import javax.swing.AbstractAction;
30 import javax.swing.Action;
31 import javax.swing.ActionMap;
32 import javax.swing.ComponentInputMap;
33 import javax.swing.InputMap;
34 import javax.swing.JComponent;
35 import javax.swing.KeyStroke;
36 import javax.swing.tree.TreeModel;
37
38 import y.anim.AnimationFactory;
39 import y.anim.AnimationObject;
40 import y.anim.AnimationPlayer;
41 import y.anim.CompositeAnimationObject;
42 import y.base.DataMap;
43 import y.base.DataProvider;
44 import y.base.Edge;
45 import y.base.EdgeCursor;
46 import y.base.EdgeList;
47 import y.base.EdgeMap;
48 import y.base.Node;
49 import y.base.NodeCursor;
50 import y.base.NodeList;
51 import y.base.NodeMap;
52 import y.geom.YInsets;
53 import y.geom.YPoint;
54 import y.geom.YRectangle;
55 import y.layout.GraphLayout;
56 import y.layout.NormalizingGraphElementOrderStage;
57 import y.layout.Layouter;
58 import y.layout.tree.GenericTreeLayouter;
59 import y.util.Maps;
60 import y.view.EdgeRealizer;
61 import y.view.Graph2D;
62 import y.view.Graph2DView;
63 import y.view.Graph2DViewActions;
64 import y.view.Graph2DViewMouseWheelZoomListener;
65 import y.view.HitInfo;
66 import y.view.NavigationMode;
67 import y.view.NodeRealizer;
68 import y.view.Overview;
69 import y.view.Selections;
70 import y.view.ViewAnimationFactory;
71 import y.view.ViewMode;
72 import y.view.Graph2DLayoutExecutor;
73 import y.view.hierarchy.GroupNodeRealizer;
74 import y.view.hierarchy.HierarchyManager;
75
76
79 public class JTreeChart extends Graph2DView {
80
81 private boolean viewLocalHierarchy = false;
82 private boolean siblingViewEnabled = false;
83 private boolean groupViewEnabled = false;
84 private DataProvider groupIdDP;
85 private DataProvider userObjectDP;
86 private TreeModel model;
87 private Map graph2TreeMap;
88 private Map tree2GraphMap;
89 private NodeList allNodes = new NodeList();
90 private EdgeList allEdges = new EdgeList();
91 private HashMap idToGroupNodeMap;
92 private HashMap groupNodeToIdMap;
93 private Object lastUserObject;
94
95
105 public JTreeChart(TreeModel model, DataProvider userObjectDP, DataProvider groupIdDP) {
106 super();
107
108 this.groupIdDP = groupIdDP;
109 this.userObjectDP = userObjectDP;
110 this.model = model;
111
112 new HierarchyManager(getGraph2D());
113
114 setRealizerDefaults();
115 updateChart();
116 addMouseInteraction();
117 addKeyboardInteraction();
118 }
119
120
123 protected void addMouseInteraction() {
124 ViewMode vm = createTreeChartViewMode();
125 if(vm != null) {
126 addViewMode(vm);
127 }
128
129 MouseWheelListener mwl = createMouseWheelListener();
130 if(mwl != null) {
131 getCanvasComponent().addMouseWheelListener(mwl);
132 }
133 }
134
135
138 protected void addKeyboardInteraction() {
139 Graph2DViewActions actions = new Graph2DViewActions(this);
140
141 ActionMap actionMap = actions.createActionMap();
142 actionMap.put(Graph2DViewActions.FOCUS_BOTTOM_NODE, new SelectRootWrapperAction(actionMap.get(Graph2DViewActions.FOCUS_BOTTOM_NODE),this));
143 actionMap.put(Graph2DViewActions.FOCUS_TOP_NODE, new SelectRootWrapperAction(actionMap.get(Graph2DViewActions.FOCUS_TOP_NODE),this));
144 actionMap.put(Graph2DViewActions.FOCUS_LEFT_NODE, new SelectRootWrapperAction(actionMap.get(Graph2DViewActions.FOCUS_LEFT_NODE),this));
145 actionMap.put(Graph2DViewActions.FOCUS_RIGHT_NODE, new SelectRootWrapperAction(actionMap.get(Graph2DViewActions.FOCUS_RIGHT_NODE),this));
146 actionMap.put("NODE_ACTION", new NodeAction());
147
148 final JComponent canvas = getCanvasComponent();
149 InputMap inputMap = new ComponentInputMap(canvas);
150 inputMap.put(KeyStroke.getKeyStroke(KeyEvent.VK_LEFT,InputEvent.CTRL_MASK), Graph2DViewActions.FOCUS_LEFT_NODE);
151 inputMap.put(KeyStroke.getKeyStroke(KeyEvent.VK_RIGHT,InputEvent.CTRL_MASK), Graph2DViewActions.FOCUS_RIGHT_NODE);
152 inputMap.put(KeyStroke.getKeyStroke(KeyEvent.VK_UP,InputEvent.CTRL_MASK), Graph2DViewActions.FOCUS_TOP_NODE);
153 inputMap.put(KeyStroke.getKeyStroke(KeyEvent.VK_DOWN,InputEvent.CTRL_MASK), Graph2DViewActions.FOCUS_BOTTOM_NODE);
154
155 inputMap.put(KeyStroke.getKeyStroke(KeyEvent.VK_ENTER,0), "NODE_ACTION");
156
157 canvas.setActionMap(actionMap);
158 canvas.setInputMap(JComponent.WHEN_IN_FOCUSED_WINDOW, inputMap);
159
160 KeyboardNavigation kNav = new KeyboardNavigation(this);
161 canvas.addKeyListener(kNav.createZoomInKeyListener(KeyEvent.VK_ADD, KeyEvent.VK_PLUS));
162 canvas.addKeyListener(kNav.createZoomOutKeyListener(KeyEvent.VK_SUBTRACT, KeyEvent.VK_MINUS));
163 canvas.addKeyListener(kNav.createMoveViewportUpKeyListener(KeyEvent.VK_UP));
164 canvas.addKeyListener(kNav.createMoveViewportDownKeyListener(KeyEvent.VK_DOWN));
165 canvas.addKeyListener(kNav.createMoveViewportLeftKeyListener(KeyEvent.VK_LEFT));
166 canvas.addKeyListener(kNav.createMoveViewportRightKeyListener(KeyEvent.VK_RIGHT));
167 }
168
169
173 protected MouseWheelListener createMouseWheelListener() {
174 return new Graph2DViewMouseWheelZoomListener();
175 }
176
177
183 protected JTreeChartViewMode createTreeChartViewMode() {
184 return new JTreeChartViewMode();
185 }
186
187 public Action createNodeAction() {
188 return new NodeAction();
189 }
190
191 public Action createZoomInAction() {
192 return new AnimatedZoomAction(true);
193 }
194
195 public Action createZoomOutAction() {
196 return new AnimatedZoomAction(false);
197 }
198
199 public Action createFitContentAction() {
200 return new FitContentAction();
201 }
202
203 public Overview createOverview() {
204 return new Overview(this);
205 }
206
207
212 protected void setRealizerDefaults() {
213 }
214
215
220 protected void configureNodeRealizer(Node n) {
221 }
222
223
235 protected void configureGroupRealizer(Node node, Object groupId, boolean collapsed) {
236 NodeRealizer nr = getGraph2D().getRealizer(node);
237 if(nr instanceof GroupNodeRealizer) {
238 GroupNodeRealizer gnr = (GroupNodeRealizer) nr;
239 gnr.setGroupClosed(collapsed);
240 gnr.setBorderInsets(new YInsets(0,0,0,0));
241 }
242 }
243
244
249 protected void configureEdgeRealizer(Edge e) {
250 }
251
252
256 private void configureRealizers() {
257 Graph2D graph = getGraph2D();
258 HierarchyManager hm = graph.getHierarchyManager();
259 for(NodeCursor nc = graph.nodes(); nc.ok(); nc.next()) {
260 Node n = nc.node();
261 if(groupNodeToIdMap == null || groupNodeToIdMap.get(n) == null) {
262 configureNodeRealizer(n);
263 }
264 else {
265 configureGroupRealizer(n, groupNodeToIdMap.get(n), hm.isFolderNode(n));
266 }
267 }
268 for(EdgeCursor ec = graph.edges(); ec.ok(); ec.next()) {
269 configureEdgeRealizer(ec.edge());
270 }
271 }
272
273
276 private void layoutGraph() {
277 new Graph2DLayoutExecutor(Graph2DLayoutExecutor.BUFFERED).doLayout(getGraph2D(), createLayouter());
278 fitContent();
279 updateView();
280 }
281
282
287 public Object getUserObject(Node node) {
288 Object treeNode = graph2TreeMap.get(node);
289 if(treeNode == null) {
290 return null;
291 } else {
292 return getUserObject(treeNode);
293 }
294 }
295
296
300 public Node getRootNode() {
301 Object treeNode = model.getRoot();
302 Object userObject = getUserObject(treeNode);
303 return getNodeForUserObject(userObject);
304 }
305
306
312 private Object getUserObject(Object treeNode) {
313 return userObjectDP == null ? null : userObjectDP.get(treeNode);
314 }
315
316
327 public Object getGroupId(Object userObject) {
328 return groupIdDP == null ? null : groupIdDP.get(userObject);
329 }
330
331
339 public Node getNodeForUserObject(Object userObject) {
340 for (NodeCursor nc = getGraph2D().nodes(); nc.ok(); nc.next()) {
341 Node n = nc.node();
342 if(getUserObject(n) == userObject) {
343 return n;
344 }
345 }
346 return null;
347 }
348
349
354 public Object getTreeNode(Node node) {
355 return graph2TreeMap.get(node);
356 }
357
358
361 public void showGlobalHierarchy() {
362 viewLocalHierarchy = false;
363
364 final NodeCursor nc = getGraph2D().selectedNodes();
365 final Object selected = nc.ok() ? getUserObject(nc.node()) : null;
366
367 buildGlobalGraph();
368 configureRealizers();
369
370 if (selected != null) {
371 getGraph2D().setSelected(getNodeForUserObject(selected), true);
372 }
373
374 layoutGraph();
375 getGraph2D().updateViews();
376 }
377
378
394 public void showLocalHierarchy(Object userObject) {
395 viewLocalHierarchy = true;
396 Graph2D graph = getGraph2D();
397
398 if (userObject == null) {
399 Node root = null;
400 final NodeCursor selected = graph.selectedNodes();
401 if (selected.ok()) {
402 root = selected.node();
403 } else {
404 for(NodeCursor nc = graph.nodes(); nc.ok(); nc.next()) {
405 if(nc.node().inDegree() == 0) {
406 root = nc.node();
407 break;
408 }
409 }
410 }
411 userObject = getUserObject(root);
412 }
413
414 lastUserObject = userObject;
415
416 boolean incrChange = getNodeForUserObject(userObject) != null;
417
418 final NodeList addedNodes = new NodeList();
419 NodeList removedNodes = new NodeList();
420 EdgeList removedEdges = new EdgeList();
421 final EdgeList addedEdges = new EdgeList();
422
423 buildLocalView(userObject, removedNodes, addedNodes, removedEdges, addedEdges);
424
425 if (!incrChange) {
426 configureRealizers();
427 new Graph2DLayoutExecutor(Graph2DLayoutExecutor.BUFFERED).doLayout(graph, createLayouter());
429 fitContent();
430 } else {
431 for(NodeCursor nc = removedNodes.nodes(); nc.ok(); nc.next()) {
432 graph.reInsertNode(nc.node());
433 }
434 for(EdgeCursor ec = removedEdges.edges(); ec.ok(); ec.next()) {
435 graph.reInsertEdge(ec.edge());
436 }
437
438 configureRealizers();
439
440 for(EdgeCursor ec = addedEdges.edges(); ec.ok(); ec.next()) {
441 graph.removeEdge(ec.edge());
442 }
443 for(NodeCursor nc = addedNodes.nodes(); nc.ok(); nc.next()) {
444 graph.removeNode(nc.node());
445 }
446
447 ViewAnimationFactory factory = new ViewAnimationFactory(this);
448 AnimationPlayer player = factory.createConfiguredPlayer();
449 player.setBlocking(true);
450
451 AnimationObject deleteAnim = createDeleteAnimation(graph, removedNodes, removedEdges, factory, 200);
452
453 player.animate(deleteAnim);
454
455 for(NodeCursor nc = addedNodes.nodes(); nc.ok(); nc.next()) {
456 graph.reInsertNode(nc.node());
457 graph.getRealizer(nc.node()).setVisible(false);
458 }
459
460 for(EdgeCursor ec = addedEdges.edges(); ec.ok(); ec.next()) {
461 graph.reInsertEdge(ec.edge());
462 graph.getRealizer(ec.edge()).setVisible(false);
463 }
464
465 if(isGroupViewEnabled()) {
466 for (NodeCursor nc = graph.nodes(); nc.ok(); nc.next()) {
467 Node n = nc.node();
468 Object obj = getUserObject(n);
469 if(obj != null && getGroupId(obj) != null) {
470 HierarchyManager hm = getGraph2D().getHierarchyManager();
471 Node groupNode = (Node) idToGroupNodeMap.get(getGroupId(obj));
472 if(hm.isNormalNode(groupNode)) {
473 hm.convertToGroupNode(groupNode);
474 }
475 hm.setParentNode(n, groupNode);
476 }
477 }
478 }
479
480 new Graph2DLayoutExecutor(){
481 protected AnimationObject createAnimation(Graph2DView view, Graph2D graph, GraphLayout graphLayout) {
482 for(NodeCursor nc = addedNodes.nodes(); nc.ok(); nc.next()) {
483 graph.getRealizer(nc.node()).setVisible(false);
484 }
485 for(EdgeCursor ec = addedEdges.edges(); ec.ok(); ec.next()) {
486 graph.getRealizer(ec.edge()).setVisible(false);
487 }
488 return super.createAnimation(view, graph, graphLayout);
489 }
490 }.doLayout(this, createLayouter());
491
492 for(NodeCursor nc = addedNodes.nodes(); nc.ok(); nc.next()) {
493 graph.getRealizer(nc.node()).setVisible(true);
494 }
495 for(EdgeCursor ec = addedEdges.edges(); ec.ok(); ec.next()) {
496 graph.getRealizer(ec.edge()).setVisible(true);
497 }
498
499 AnimationObject fadeInAnim = createFadeInAnimation(graph, addedNodes, addedEdges, factory, 500);
500 player.animate(fadeInAnim);
501 }
502
503
504 graph.updateViews();
505 }
506
507 protected Layouter createLayouter() {
508 GenericTreeLayouter layouter = new GenericTreeLayouter();
509
510 return new NormalizingGraphElementOrderStage(layouter);
525 }
526
527
533 public void updateChart() {
534 if(isLocalViewEnabled()) {
535 buildGlobalGraph();
536 showLocalHierarchy(lastUserObject);
537 } else {
538 showGlobalHierarchy();
539 }
540 }
541
542
548 public boolean isSiblingViewEnabled() {
549 return siblingViewEnabled;
550 }
551
552
565 public void setSiblingViewEnabled(boolean siblingViewEnabled) {
566 this.siblingViewEnabled = siblingViewEnabled;
567 }
568
569
575 public boolean isLocalViewEnabled() {
576 return viewLocalHierarchy;
577 }
578
579
583 public boolean isGroupViewEnabled() {
584 return groupViewEnabled && groupIdDP != null;
585 }
586
587
592 public void setGroupViewEnabled(boolean enabled) {
593 groupViewEnabled = enabled;
594 }
595
596
601 public void focusNode(Node node) {
602 YPoint p = getGraph2D().getCenter(node);
603 focusView(getZoom(), new Point2D.Double(p.x, p.y), false);
604 updateView();
605 }
606
607
617 public void performNodeAction(Node node) {
618 if(getGraph2D().getHierarchyManager().isNormalNode(node)) {
619 if(viewLocalHierarchy) {
620 showLocalHierarchy(getUserObject(node));
621 }
622 else {
623 Point2D center = new Point2D.Double(getGraph2D().getCenterX(node), getGraph2D().getCenterY(node));
624 YRectangle nodeSize = getGraph2D().getRectangle(node);
625 Dimension viewSize = getViewSize();
626 double zoom;
627 if(viewSize.width/nodeSize.width < viewSize.height/nodeSize.height) {
628 zoom = viewSize.width/nodeSize.width;
629 } else {
630 zoom = viewSize.height/nodeSize.height;
631 }
632 zoom *= 0.5;
633 focusView(zoom, center, true);
634 }
635 }
636 }
637
638
643 private void collapseGroup(Node groupNode) {
644 Graph2D graph = getGraph2D();
645 HierarchyManager hm = graph.getHierarchyManager();
646 hm.closeGroup(groupNode);
647 configureGroupRealizer(groupNode, groupNodeToIdMap.get(groupNode), true);
648 layoutGraph();
649 }
650
651
656 private void expandGroup(Node folderNode) {
657 Graph2D graph = getGraph2D();
658 HierarchyManager hm = graph.getHierarchyManager();
659 hm.openFolder(folderNode);
660 configureGroupRealizer(folderNode, groupNodeToIdMap.get(folderNode), false);
661 layoutGraph();
662 }
663
664
669 private void buildGlobalGraph() {
670 Graph2D graph = getGraph2D();
671 graph.clear();
672 tree2GraphMap = new HashMap();
673 graph2TreeMap = new HashMap();
674 Object treeNode = model.getRoot();
675 Node graphNode = graph.createNode();
676 tree2GraphMap.put(treeNode, graphNode);
677 graph2TreeMap.put(graphNode, treeNode);
678 buildGraph(treeNode, graphNode, tree2GraphMap, graph2TreeMap);
679
680 if(isGroupViewEnabled()) {
681 addGroupNodes();
682 }
683
684 allNodes = new NodeList(graph.nodes());
685 allEdges = new EdgeList(graph.edges());
686
687 DataMap comparableMap = Maps.createHashedDataMap();
688 NormalizingGraphElementOrderStage.fillComparableMapFromGraph(graph, comparableMap, comparableMap);
689 graph.addDataProvider(NormalizingGraphElementOrderStage.COMPARABLE_EDGE_DPKEY, comparableMap);
690 graph.addDataProvider(NormalizingGraphElementOrderStage.COMPARABLE_NODE_DPKEY, comparableMap);
691 }
692
693
702 private void buildGraph(Object treeNode, Node graphNode, Map tree2GraphMap, Map graph2TreeMap) {
703 Graph2D graph = getGraph2D();
704 int count = model.getChildCount(treeNode);
705 for(int i = 0; i < count; i++) {
706 Object treeChild = model.getChild(treeNode, i);
707 Node graphChild = graph.createNode();
708 tree2GraphMap.put(treeChild, graphChild);
709 graph2TreeMap.put(graphChild, treeChild);
710 graph.createEdge(graphNode, graphChild);
712 buildGraph(treeChild, graphChild, tree2GraphMap, graph2TreeMap);
713 }
714 }
715
716
719 private void addGroupNodes() {
720 Graph2D graph = getGraph2D();
721 idToGroupNodeMap = new HashMap();
722 groupNodeToIdMap = new HashMap();
723 HierarchyManager hm = graph.getHierarchyManager();
724 for(NodeCursor nc = graph.nodes(); nc.ok(); nc.next()) {
725 Node n = nc.node();
726 Object obj = getUserObject(n);
727 if(obj != null && getGroupId(obj) != null) {
728 Node groupNode = (Node) idToGroupNodeMap.get(getGroupId(obj));
729 if(groupNode == null) {
730 groupNode = hm.createGroupNode(graph);
731 idToGroupNodeMap.put(getGroupId(obj), groupNode);
732 groupNodeToIdMap.put(groupNode, getGroupId(obj));
733 }
734 hm.setParentNode(n, groupNode);
735 }
736 }
737 }
738
739
751 private void buildLocalView(Object userObject, NodeList removedNodes, NodeList addedNodes, EdgeList removedEdges, EdgeList addedEdges) {
752 expandAll();
753 Graph2D graph = getGraph2D();
754 NodeMap prevNodeMap = Maps.createHashedNodeMap();
755 for (NodeCursor nc = graph.nodes(); nc.ok(); nc.next()) {
756 Node n = nc.node();
757 prevNodeMap.setBool(n, true);
758 }
759 EdgeMap prevEdgeMap = Maps.createHashedEdgeMap();
760 for (EdgeCursor ec = graph.edges(); ec.ok(); ec.next()) {
761 Edge e = ec.edge();
762 prevEdgeMap.setBool(e, true);
763 }
764
765 rebuildGlobalGraph();
766 for (NodeCursor nc = graph.nodes(); nc.ok(); nc.next()) {
767 Node n = nc.node();
768 if(getUserObject(n).equals(userObject)) {
769 NodeList nodes = new NodeList(n);
770 if(n.inDegree() == 1) {
771 Node parent = n.firstInEdge().source();
772 nodes.add(parent);
773 if(isSiblingViewEnabled()) {
774 nodes.pop();
775 nodes.addAll(parent.successors());
776 }
777 }
778 nodes.addAll(n.successors());
779
780 NodeList nodesToRemove = new NodeList(graph.nodes());
781
782 if(isGroupViewEnabled()) {
783 HashSet requiredGroups = new HashSet();
784 for(NodeCursor ncc = nodes.nodes(); ncc.ok(); ncc.next()) {
786 Node node = ncc.node();
787 requiredGroups.add(graph.getHierarchyManager().getParentNode(node));
788 }
789 nodesToRemove.removeAll(requiredGroups);
790 }
791 nodesToRemove.removeAll(nodes);
792
793 while(!nodesToRemove.isEmpty()) {
794 graph.removeNode(nodesToRemove.popNode());
795 }
796 break;
797 }
798 }
799
800 for(NodeCursor nc = allNodes.nodes(); nc.ok(); nc.next()) {
801 Node n = nc.node();
802 if(n.getGraph() != null) {
803 if(prevNodeMap.getBool(n)) {
805 } else {
807 addedNodes.add(n);
809 }
810 } else {
811 if(prevNodeMap.getBool(n)) {
813 removedNodes.add(n);
815 } else {
816 }
818 }
819 }
820 for(EdgeCursor ec = allEdges.edges(); ec.ok(); ec.next()) {
821 Edge e = ec.edge();
822 if(e.getGraph() != null) {
823 if(prevEdgeMap.getBool(e)) {
825 } else {
827 addedEdges.add(e);
829 }
830 } else {
831 if(prevEdgeMap.getBool(e)) {
833 removedEdges.add(e);
835 } else {
836 }
838 }
839 }
840
841 Node employeeNode = getNodeForUserObject(userObject);
842 if(employeeNode != null) {
843 graph.setSelected(employeeNode, true);
844 }
845 }
846
847
851 private void expandAll() {
852 HierarchyManager hm = getGraph2D().getHierarchyManager();
853 for (NodeCursor nc = allNodes.nodes(); nc.ok(); nc.next()) {
854 Node n = nc.node();
855 if(hm.isFolderNode(n)) {
856 hm.openFolder(n);
857 }
858 }
859 }
860
861 private void rebuildGlobalGraph() {
862 Graph2D graph = getGraph2D();
863 graph.clear();
864 for (NodeCursor nc = allNodes.nodes(); nc.ok(); nc.next()) {
865 Node n = nc.node();
866 graph.reInsertNode(n);
867 }
868 for (EdgeCursor ec = allEdges.edges(); ec.ok(); ec.next()) {
869 Edge e = ec.edge();
870 graph.reInsertEdge(e);
871 }
872
873 if(isGroupViewEnabled()) {
875 for (NodeCursor nc = allNodes.nodes(); nc.ok(); nc.next()) {
876 Node n = nc.node();
877 Object obj = getUserObject(n);
878 if(obj != null && getGroupId(obj) != null) {
879 HierarchyManager hm = getGraph2D().getHierarchyManager();
880 Node groupNode = (Node) idToGroupNodeMap.get(getGroupId(obj));
881 if(hm.isNormalNode(groupNode)) {
882 hm.convertToGroupNode(groupNode);
883 }
884 hm.setParentNode(n, groupNode);
885 }
886 }
887 }
888 }
889
890
898 private AnimationObject createDeleteAnimation(
899 Graph2D graph,
900 final List nodesToBeDeleted,
901 final List edgesToBeDeleted,
902 ViewAnimationFactory factory,
903 long preferredDuration
904 ) {
905 final CompositeAnimationObject deleteEdges = AnimationFactory.createConcurrency();
906 for (Iterator it = edgesToBeDeleted.iterator(); it.hasNext();) {
907 final EdgeRealizer er = graph.getRealizer((Edge) it.next());
908 deleteEdges.addAnimation(factory.fadeOut(er, ViewAnimationFactory.APPLY_EFFECT, preferredDuration));
909 }
910
911 final CompositeAnimationObject deleteNodes = AnimationFactory.createConcurrency();
912 for (Iterator it = nodesToBeDeleted.iterator(); it.hasNext();) {
913 final NodeRealizer nr = graph.getRealizer((Node) it.next());
914 deleteNodes.addAnimation(factory.fadeOut(nr, ViewAnimationFactory.APPLY_EFFECT, preferredDuration));
915 }
916 return AnimationFactory.createSequence(deleteEdges, deleteNodes);
917 }
918
919
926 private AnimationObject createFadeInAnimation(
927 Graph2D graph,
928 final List nodesToBeAdded,
929 final List edgesToBeAdded,
930 ViewAnimationFactory factory,
931 long preferredDuration
932 ) {
933 final CompositeAnimationObject addElems = AnimationFactory.createConcurrency();
934 for (Iterator it = edgesToBeAdded.iterator(); it.hasNext();) {
935 final EdgeRealizer er = graph.getRealizer((Edge) it.next());
936 addElems.addAnimation(factory.fadeIn(
937 er, preferredDuration));
938 }
939
940 for (Iterator it = nodesToBeAdded.iterator(); it.hasNext();) {
941 final NodeRealizer nr = graph.getRealizer((Node) it.next());
942 addElems.addAnimation(factory.fadeIn(nr, preferredDuration));
943 }
944 return addElems;
945 }
946
947
948
961 public static class JTreeChartViewMode extends NavigationMode {
962 public JTreeChart getJTreeChart() {
963 return (JTreeChart) view;
964 }
965
966 public void mouseClicked(double x, double y) {
967 if(lastClickEvent.getClickCount() > 1) {
968 mouseDoubleClicked(x, y);
969 }
970 else {
971 mouseSingleClicked(x,y);
972 }
973 }
974
975
988 protected void mouseSingleClicked(double x, double y) {
989 view.getCanvasComponent().requestFocus();
990 HitInfo info = getHitInfo(x,y);
991 Node node = info.getHitNode();
992 Graph2D graph = getGraph2D();
993 if (node != null && getGraph2D().getHierarchyManager().isNormalNode(node)) {
994 if (!graph.isSelected(node)) {
995 graph.unselectAll();
996 graph.setSelected(node, true);
997 }
998 } else {
999 getGraph2D().unselectAll();
1000 }
1001 getGraph2D().updateViews();
1002 }
1003
1004
1019 protected void mouseDoubleClicked(double x, double y) {
1020 if (lastClickEvent.getClickCount() == 2) {
1021 HitInfo info = getHitInfo(x,y);
1022 Node node = info.getHitNode();
1023 if (node != null) {
1024 if (getGraph2D().getHierarchyManager().isGroupNode(node)) {
1025 getJTreeChart().collapseGroup(node);
1026 } else if (getGraph2D().getHierarchyManager().isFolderNode(node)) {
1027 getJTreeChart().expandGroup(node);
1028 } else {
1029 getJTreeChart().performNodeAction(node);
1030 }
1031 } else {
1032 getJTreeChart().fitContent(true);
1033 }
1034 }
1035 }
1036 }
1037
1038
1046 private static class SelectRootWrapperAction implements Action {
1047 Action delegateAction;
1048 Graph2DView view;
1049
1050 SelectRootWrapperAction(Action delegateAction, Graph2DView view) {
1051 this.delegateAction = delegateAction;
1052 this.view = view;
1053 }
1054
1055 public void addPropertyChangeListener(PropertyChangeListener listener) {
1056 delegateAction.addPropertyChangeListener(listener);
1057 }
1058
1059 public Object getValue(String key) {
1060 return delegateAction.getValue(key);
1061 }
1062
1063 public boolean isEnabled() {
1064 return delegateAction.isEnabled();
1065 }
1066
1067 public void putValue(String key, Object value) {
1068 delegateAction.putValue(key, value);
1069 }
1070
1071 public void removePropertyChangeListener(PropertyChangeListener listener) {
1072 delegateAction.removePropertyChangeListener(listener);
1073 }
1074
1075 public void setEnabled(boolean b) {
1076 delegateAction.setEnabled(b);
1077 }
1078
1079
1087 public void actionPerformed(ActionEvent e) {
1088 Graph2D graph = view.getGraph2D();
1089 boolean selectionEmpty = Selections.isNodeSelectionEmpty(graph);
1090 if(selectionEmpty) {
1091 for (NodeCursor nc = graph.nodes(); nc.ok(); nc.next()) {
1093 Node n = nc.node();
1094 if(n.inDegree() == 0) {
1095 graph.setSelected(n, true);
1096 selectionEmpty = false;
1097 break;
1098 }
1099 }
1100 if(graph.nodeCount() > 0 && selectionEmpty) {
1101 graph.setSelected(graph.firstNode(), true);
1102 }
1103 }
1104 else {
1105 delegateAction.actionPerformed(e);
1106 }
1107 }
1108 }
1109
1110
1114 private class AnimatedZoomAction extends AbstractAction {
1115 private final boolean zoomIn;
1116
1117 private ViewAnimationFactory factory;
1118 private AnimationPlayer player;
1119
1120 AnimatedZoomAction( final boolean zoomIn ) {
1121 this.zoomIn = zoomIn;
1122 }
1123
1124
1128 public void actionPerformed(ActionEvent e) {
1129 if (factory == null) {
1130 factory = new ViewAnimationFactory(JTreeChart.this);
1131 player = factory.createConfiguredPlayer();
1132 }
1133
1134 if (!player.isPlaying()) {
1135 player.animate(AnimationFactory.createEasedAnimation(
1136 factory.zoom(calculateZoom(), ViewAnimationFactory.APPLY_EFFECT, 500)));
1137 }
1138 }
1139
1140
1144 double calculateZoom() {
1145 if (zoomIn) {
1146 return Math.min(4, getZoom()*2);
1147 } else {
1148 Point2D oldP = getViewPoint2D();
1149 double oldZoom = getZoom();
1150 fitContent();
1151 double fitContentZoom = getZoom();
1152 setZoom(oldZoom);
1153 setViewPoint2D(oldP.getX(), oldP.getY());
1154
1155 return Math.max(fitContentZoom, getZoom()*0.5);
1156 }
1157 }
1158 }
1159
1160
1164 private class NodeAction extends AbstractAction {
1165 public void actionPerformed(ActionEvent e) {
1166 if(!Selections.isNodeSelectionEmpty(getGraph2D())) {
1167 performNodeAction(getGraph2D().selectedNodes().node());
1168 }
1169 }
1170 }
1171
1172
1176 private class FitContentAction extends AbstractAction {
1177 public void actionPerformed(ActionEvent e) {
1178 fitContent(true);
1179 }
1180 }
1181}
1182