1
28 package demo.view.orgchart;
29
30 import y.anim.AnimationFactory;
31 import y.anim.AnimationObject;
32 import y.anim.AnimationPlayer;
33 import y.anim.CompositeAnimationObject;
34 import y.base.DataMap;
35 import y.base.DataProvider;
36 import y.base.Edge;
37 import y.base.EdgeCursor;
38 import y.base.EdgeList;
39 import y.base.EdgeMap;
40 import y.base.Node;
41 import y.base.NodeCursor;
42 import y.base.NodeList;
43 import y.base.NodeMap;
44 import y.geom.YInsets;
45 import y.geom.YPoint;
46 import y.geom.YRectangle;
47 import y.layout.FixNodeLayoutStage;
48 import y.layout.GraphLayout;
49 import y.layout.Layouter;
50 import y.layout.NormalizingGraphElementOrderStage;
51 import y.layout.tree.GenericTreeLayouter;
52 import y.util.Maps;
53 import y.view.AutoDragViewMode;
54 import y.view.DefaultGraph2DRenderer;
55 import y.view.EdgeRealizer;
56 import y.view.Graph2D;
57 import y.view.Graph2DLayoutExecutor;
58 import y.view.Graph2DView;
59 import y.view.Graph2DViewActions;
60 import y.view.Graph2DViewMouseWheelZoomListener;
61 import y.view.NodeRealizer;
62 import y.view.Overview;
63 import y.view.Selections;
64 import y.view.ViewAnimationFactory;
65 import y.view.ViewMode;
66 import y.view.hierarchy.GroupNodeRealizer;
67 import y.view.hierarchy.HierarchyManager;
68
69 import javax.swing.AbstractAction;
70 import javax.swing.Action;
71 import javax.swing.ActionMap;
72 import javax.swing.ComponentInputMap;
73 import javax.swing.InputMap;
74 import javax.swing.JComponent;
75 import javax.swing.KeyStroke;
76 import javax.swing.tree.TreeModel;
77 import java.awt.Dimension;
78 import java.awt.event.ActionEvent;
79 import java.awt.event.InputEvent;
80 import java.awt.event.KeyEvent;
81 import java.awt.event.MouseWheelListener;
82 import java.awt.geom.Point2D;
83 import java.beans.PropertyChangeListener;
84 import java.util.HashMap;
85 import java.util.HashSet;
86 import java.util.Iterator;
87 import java.util.List;
88
89
92 public class JTreeChart extends Graph2DView {
93
94 public static final Object GRAPH_2_TREE_MAP_DPKEY =
95 "demo.view.orgchart.JTreeChart.GRAPH_2_TREE_MAP_DPKEY";
96 public static final Object TREE_2_GRAPH_MAP_DPKEY =
97 "demo.view.orgchart.JTreeChart.TREE_2_GRAPH_MAP_DPKEY";
98 static final Object ATOP_DPKEY =
99 "demo.view.orgchart.JTreeChart.ATOP_DPKEY";
100 static final Object FIXED_NODE_DPKEY = FixNodeLayoutStage.FIXED_NODE_DPKEY;
101
104 static final Object MARKED_NODES_DPKEY =
105 "demo.view.orgchart.JTreeChart.MARKED_NODES_DPKEY";
106
107 private boolean viewLocalHierarchy = false;
108 private boolean siblingViewEnabled = false;
109 private boolean groupViewEnabled = false;
110 private final DataProvider groupIdDP;
111 private final DataProvider userObjectDP;
112 private TreeModel model;
113 private DataMap graph2TreeMap;
114 private DataMap tree2GraphMap;
115 private NodeList allNodes = new NodeList();
116 private EdgeList allEdges = new EdgeList();
117 private HashMap idToGroupNodeMap;
118 private HashMap groupNodeToIdMap;
119 private Object lastUserObject;
120
121
131 public JTreeChart(final TreeModel model, final DataProvider userObjectDP, final DataProvider groupIdDP) {
132 super();
133
134 this.groupIdDP = groupIdDP;
135 this.userObjectDP = userObjectDP;
136 this.model = model;
137
138 new HierarchyManager(getGraph2D());
139
140 setRealizerDefaults();
141 updateChart();
142 addMouseInteraction();
143 addKeyboardInteraction();
144
145 final DefaultGraph2DRenderer renderer = new DefaultGraph2DRenderer() {
148 protected int getLayer(final Graph2D graph, final Node node) {
149 final DataProvider dataProvider = graph.getDataProvider(ATOP_DPKEY);
150 if (dataProvider != null) {
152 return dataProvider.getBool(node) ? 1 : 0;
153 } else {
154 return 1;
155 }
156 }
157 };
158 setGraph2DRenderer(renderer);
159 renderer.setLayeredPainting(true);
160 }
161
162 public TreeModel getModel() {
163 return model;
164 }
165
166 public void setModel(final TreeModel model) {
167 this.model = model;
168 }
169
170
173 protected void addMouseInteraction() {
174 final ViewMode vm = createTreeChartViewMode();
175 if(vm != null) {
176 addViewMode(vm);
177 }
178 addViewMode(new AutoDragViewMode());
179 final MouseWheelListener mwl = createMouseWheelListener();
180 if(mwl != null) {
181 getCanvasComponent().addMouseWheelListener(mwl);
182 }
183 }
184
185
188 protected void addKeyboardInteraction() {
189 final Graph2DViewActions actions = new Graph2DViewActions(this);
190
191 final ActionMap actionMap = actions.createActionMap();
192 actionMap.put(Graph2DViewActions.FOCUS_BOTTOM_NODE, new SelectRootWrapperAction(actionMap.get(Graph2DViewActions.FOCUS_BOTTOM_NODE),this));
193 actionMap.put(Graph2DViewActions.FOCUS_TOP_NODE, new SelectRootWrapperAction(actionMap.get(Graph2DViewActions.FOCUS_TOP_NODE),this));
194 actionMap.put(Graph2DViewActions.FOCUS_LEFT_NODE, new SelectRootWrapperAction(actionMap.get(Graph2DViewActions.FOCUS_LEFT_NODE),this));
195 actionMap.put(Graph2DViewActions.FOCUS_RIGHT_NODE, new SelectRootWrapperAction(actionMap.get(Graph2DViewActions.FOCUS_RIGHT_NODE),this));
196 actionMap.put("NODE_ACTION", new NodeAction());
197
198 final JComponent canvas = getCanvasComponent();
199 final InputMap inputMap = new ComponentInputMap(canvas);
200 inputMap.put(KeyStroke.getKeyStroke(KeyEvent.VK_LEFT,InputEvent.CTRL_MASK), Graph2DViewActions.FOCUS_LEFT_NODE);
201 inputMap.put(KeyStroke.getKeyStroke(KeyEvent.VK_RIGHT,InputEvent.CTRL_MASK), Graph2DViewActions.FOCUS_RIGHT_NODE);
202 inputMap.put(KeyStroke.getKeyStroke(KeyEvent.VK_UP,InputEvent.CTRL_MASK), Graph2DViewActions.FOCUS_TOP_NODE);
203 inputMap.put(KeyStroke.getKeyStroke(KeyEvent.VK_DOWN,InputEvent.CTRL_MASK), Graph2DViewActions.FOCUS_BOTTOM_NODE);
204
205 inputMap.put(KeyStroke.getKeyStroke(KeyEvent.VK_ENTER,0), "NODE_ACTION");
206
207 canvas.setActionMap(actionMap);
208 canvas.setInputMap(JComponent.WHEN_IN_FOCUSED_WINDOW, inputMap);
209
210 final KeyboardNavigation kNav = new KeyboardNavigation(this);
211 canvas.addKeyListener(kNav.createZoomInKeyListener(KeyEvent.VK_ADD, KeyEvent.VK_PLUS));
212 canvas.addKeyListener(kNav.createZoomOutKeyListener(KeyEvent.VK_SUBTRACT, KeyEvent.VK_MINUS));
213 canvas.addKeyListener(kNav.createMoveViewportUpKeyListener(KeyEvent.VK_UP));
214 canvas.addKeyListener(kNav.createMoveViewportDownKeyListener(KeyEvent.VK_DOWN));
215 canvas.addKeyListener(kNav.createMoveViewportLeftKeyListener(KeyEvent.VK_LEFT));
216 canvas.addKeyListener(kNav.createMoveViewportRightKeyListener(KeyEvent.VK_RIGHT));
217 }
218
219
223 protected MouseWheelListener createMouseWheelListener() {
224 return new Graph2DViewMouseWheelZoomListener();
225 }
226
227
231 protected ViewMode createTreeChartViewMode() {
232 return new ViewModeFactory.JTreeChartViewMode();
233 }
234
235 public Action createZoomInAction() {
236 return new AnimatedZoomAction(true);
237 }
238
239 public Action createZoomOutAction() {
240 return new AnimatedZoomAction(false);
241 }
242
243 public Action createFitContentAction() {
244 return new FitContentAction();
245 }
246
247 public Overview createOverview() {
248 return new Overview(this);
249 }
250
251
256 protected void setRealizerDefaults() {
257 }
258
259
264 protected void configureNodeRealizer(final Node n) {
265 }
266
267
279 protected void configureGroupRealizer(final Node node, final Object groupId, final boolean collapsed) {
280 final NodeRealizer nr = getGraph2D().getRealizer(node);
281 if(nr instanceof GroupNodeRealizer) {
282 final GroupNodeRealizer gnr = (GroupNodeRealizer) nr;
283 gnr.setGroupClosed(collapsed);
284 gnr.setBorderInsets(new YInsets(0,0,0,0));
285 }
286 }
287
288
293 protected void configureEdgeRealizer(final Edge e) {
294 }
295
296
300 private void configureRealizers() {
301 final Graph2D graph = getGraph2D();
302 final HierarchyManager hm = graph.getHierarchyManager();
303 for(final NodeCursor nc = graph.nodes(); nc.ok(); nc.next()) {
304 final Node n = nc.node();
305 if(groupNodeToIdMap == null || groupNodeToIdMap.get(n) == null) {
306 configureNodeRealizer(n);
307 }
308 else {
309 configureGroupRealizer(n, groupNodeToIdMap.get(n), hm.isFolderNode(n));
310 }
311 }
312 for(final EdgeCursor ec = graph.edges(); ec.ok(); ec.next()) {
313 configureEdgeRealizer(ec.edge());
314 }
315 }
316
317
318
319
322 public void layoutGraph(final boolean animate) {
323 final byte mode = animate
324 ? Graph2DLayoutExecutor.ANIMATED
325 : Graph2DLayoutExecutor.BUFFERED;
326 final Graph2DLayoutExecutor executor = new Graph2DLayoutExecutor(mode);
327 executor.getLayoutMorpher().setPreferredDuration(300);
328 executor.getLayoutMorpher().setEasedExecution(true);
329 executor.getLayoutMorpher().setKeepZoomFactor(true);
330
331 final Graph2D graph = getGraph2D();
332 if (!Selections.isNodeSelectionEmpty(graph)) {
333 final DataProvider dp = Selections.createSelectionDataProvider(graph);
334 graph.addDataProvider(FIXED_NODE_DPKEY, dp);
335 try {
336 executor.doLayout(this, new FixNodeLayoutStage(createLayouter()));
337 } finally {
338 graph.removeDataProvider(FIXED_NODE_DPKEY);
339 }
340 } else {
341 executor.doLayout(this, createLayouter());
342 }
343 }
344
345
350 public Object getUserObject(final Node node) {
351 final Object treeNode = graph2TreeMap.get(node);
352 if(treeNode == null) {
353 return null;
354 } else {
355 return getUserObject(treeNode);
356 }
357 }
358
359
363 public Node getRootNode() {
364 final Object treeNode = model.getRoot();
365 final Object userObject = getUserObject(treeNode);
366 return getNodeForUserObject(userObject);
367 }
368
369
375 private Object getUserObject(final Object treeNode) {
376 return userObjectDP == null ? null : userObjectDP.get(treeNode);
377 }
378
379
390 public Object getGroupId(final Object userObject) {
391 return groupIdDP == null ? null : groupIdDP.get(userObject);
392 }
393
394
402 public Node getNodeForUserObject(final Object userObject) {
403 for (final NodeCursor nc = getGraph2D().nodes(); nc.ok(); nc.next()) {
404 final Node n = nc.node();
405 if(getUserObject(n) == userObject) {
406 return n;
407 }
408 }
409 return null;
410 }
411
412
417 public Object getTreeNode(final Node node) {
418 return graph2TreeMap.get(node);
419 }
420
421
428 void updateUserObject( final Node node, final Object userObject ) {
429 graph2TreeMap.set(node, userObject);
430 tree2GraphMap.set(userObject, node);
431 }
432
433
436 public void showGlobalHierarchy() {
437 viewLocalHierarchy = false;
438
439 final FixState state = getFixState();
440
441 buildGlobalGraph();
442 configureRealizers();
443
444 final Graph2D graph = getGraph2D();
445 final Node node = state == null ? null
446 : getNodeForUserObject(state.focusedUserData);
447 if (node != null) {
448 graph.setSelected(node, true);
452 graph.setCenter(node, state.focusedCenterX, state.focusedCenterY);
453 }
454
455 try {
456 layoutGraph(false);
457 } finally {
458
459 if (node != null && !state.focusedSelected) {
464 graph.setSelected(node, false);
465 }
466 }
467
468 graph.updateViews();
469 }
470
471
482 private FixState getFixState() {
483 final Point2D center = getCenter();
484 final double vcx = center.getX();
485 final double vcy = center.getY();
486
487 Object focusedData = null;
488 double focusedCx = 0;
489 double focusedCy = 0;
490
491 double minDistSqr = Double.POSITIVE_INFINITY;
492 final Graph2D graph = getGraph2D();
493 for (NodeCursor nc = graph.nodes(); nc.ok(); nc.next()) {
494 final Node node = nc.node();
495
496 final Object data = getUserObject(node);
497 if (data == null) {
498 continue;
499 }
500
501 if (graph.isSelected(node)) {
502 return new FixState(data, graph.getCenterX(node), graph.getCenterY(node), true);
503 } else {
504 final double ncx = graph.getCenterX(node);
505 final double ncy = graph.getCenterY(node);
506
507 final double dx = ncx - vcx;
508 final double dy = ncy - vcy;
509 double distSqr = dx * dx + dy * dy;
510 if (minDistSqr > distSqr) {
511 minDistSqr = distSqr;
512 focusedData = data;
513 focusedCx = ncx;
514 focusedCy = ncy;
515 }
516 }
517 }
518
519 if (focusedData == null) {
520 return null;
521 } else {
522 return new FixState(focusedData, focusedCx, focusedCy, false);
523 }
524 }
525
526
545 public void showLocalHierarchy(Object userObject) {
546 viewLocalHierarchy = true;
547 final Graph2D graph = getGraph2D();
548
549 if (userObject == null) {
550 final NodeCursor selected = graph.selectedNodes();
551 if (selected.ok()) {
552 userObject = getUserObject(selected.node());
553 } else {
554 userObject = model.getRoot();
555 }
556 }
557
558 lastUserObject = userObject;
559
560 final boolean incrChange = getNodeForUserObject(userObject) != null;
561
562 final NodeList addedNodes = new NodeList();
563 final NodeList removedNodes = new NodeList();
564 final EdgeList removedEdges = new EdgeList();
565 final EdgeList addedEdges = new EdgeList();
566
567 buildLocalView(userObject, removedNodes, addedNodes, removedEdges, addedEdges);
568
569 if (!incrChange) {
570 configureRealizers();
571 new Graph2DLayoutExecutor(Graph2DLayoutExecutor.BUFFERED).doLayout(graph, createLayouter());
572 fitContent();
573 } else {
574 for(final NodeCursor nc = removedNodes.nodes(); nc.ok(); nc.next()) {
575 graph.reInsertNode(nc.node());
576 }
577 for(final EdgeCursor ec = removedEdges.edges(); ec.ok(); ec.next()) {
578 graph.reInsertEdge(ec.edge());
579 }
580
581 configureRealizers();
582
583 for(final EdgeCursor ec = addedEdges.edges(); ec.ok(); ec.next()) {
584 graph.removeEdge(ec.edge());
585 }
586 for(final NodeCursor nc = addedNodes.nodes(); nc.ok(); nc.next()) {
587 graph.removeNode(nc.node());
588 }
589
590 final ViewAnimationFactory factory = new ViewAnimationFactory(this);
591 final AnimationPlayer player = factory.createConfiguredPlayer();
592 player.setBlocking(true);
593
594 final AnimationObject deleteAnim = createDeleteAnimation(graph, removedNodes, removedEdges, factory, 200);
595
596 player.animate(deleteAnim);
597
598 for(final NodeCursor nc = addedNodes.nodes(); nc.ok(); nc.next()) {
599 graph.reInsertNode(nc.node());
600 graph.getRealizer(nc.node()).setVisible(false);
601 }
602
603 for(final EdgeCursor ec = addedEdges.edges(); ec.ok(); ec.next()) {
604 graph.reInsertEdge(ec.edge());
605 graph.getRealizer(ec.edge()).setVisible(false);
606 }
607
608 if(isGroupViewEnabled()) {
609 for (final NodeCursor nc = graph.nodes(); nc.ok(); nc.next()) {
610 final Node n = nc.node();
611 final Object obj = getUserObject(n);
612 if(obj != null && getGroupId(obj) != null) {
613 final HierarchyManager hm = getGraph2D().getHierarchyManager();
614 final Node groupNode = (Node) idToGroupNodeMap.get(getGroupId(obj));
615 if(hm.isNormalNode(groupNode)) {
616 hm.convertToGroupNode(groupNode);
617 }
618 hm.setParentNode(n, groupNode);
619 }
620 }
621 }
622
623 new Graph2DLayoutExecutor(){
624 protected AnimationObject createAnimation(final Graph2DView view, final Graph2D graph,
625 final GraphLayout graphLayout) {
626 for(final NodeCursor nc = addedNodes.nodes(); nc.ok(); nc.next()) {
627 graph.getRealizer(nc.node()).setVisible(false);
628 }
629 for(final EdgeCursor ec = addedEdges.edges(); ec.ok(); ec.next()) {
630 graph.getRealizer(ec.edge()).setVisible(false);
631 }
632 return super.createAnimation(view, graph, graphLayout);
633 }
634 }.doLayout(this, createLayouter());
635
636 for(final NodeCursor nc = addedNodes.nodes(); nc.ok(); nc.next()) {
637 graph.getRealizer(nc.node()).setVisible(true);
638 }
639 for(final EdgeCursor ec = addedEdges.edges(); ec.ok(); ec.next()) {
640 graph.getRealizer(ec.edge()).setVisible(true);
641 }
642
643 final AnimationObject fadeInAnim = createFadeInAnimation(graph, addedNodes, addedEdges, factory, 500);
644 player.animate(fadeInAnim);
645 }
646
647
648 graph.updateViews();
649 }
650
651 protected Layouter createLayouter() {
652 final GenericTreeLayouter layouter = new GenericTreeLayouter();
653 return new NormalizingGraphElementOrderStage(layouter);
668 }
669
670
676 public void updateChart() {
677 if(isLocalViewEnabled()) {
678 buildGlobalGraph();
679 showLocalHierarchy(lastUserObject);
680 } else {
681 showGlobalHierarchy();
682 }
683 }
684
685
691 public boolean isSiblingViewEnabled() {
692 return siblingViewEnabled;
693 }
694
695
708 public void setSiblingViewEnabled(final boolean siblingViewEnabled) {
709 this.siblingViewEnabled = siblingViewEnabled;
710 }
711
712
718 public boolean isLocalViewEnabled() {
719 return viewLocalHierarchy;
720 }
721
722
726 public boolean isGroupViewEnabled() {
727 return groupViewEnabled && groupIdDP != null;
728 }
729
730
735 public void setGroupViewEnabled(final boolean enabled) {
736 groupViewEnabled = enabled;
737 }
738
739
744 public void focusNode(final Node node) {
745 final YPoint p = getGraph2D().getCenter(node);
746 focusView(getZoom(), new Point2D.Double(p.x, p.y), false);
747 updateView();
748 }
749
750
760 public void performNodeAction(final Node node) {
761 if(getGraph2D().getHierarchyManager().isNormalNode(node)) {
762 if(viewLocalHierarchy) {
763 showLocalHierarchy(getUserObject(node));
764 }
765 else {
766 final Point2D center = new Point2D.Double(getGraph2D().getCenterX(node), getGraph2D().getCenterY(node));
767 final YRectangle nodeSize = getGraph2D().getRectangle(node);
768 final Dimension viewSize = getViewSize();
769 double zoom;
770 if(viewSize.width/nodeSize.width < viewSize.height/nodeSize.height) {
771 zoom = viewSize.width/nodeSize.width;
772 } else {
773 zoom = viewSize.height/nodeSize.height;
774 }
775 zoom *= 0.5;
776 focusView(zoom, center, true);
777 }
778 }
779 }
780
781
786 void collapseGroup(final Node groupNode) {
787 final Graph2D graph = getGraph2D();
788 final HierarchyManager hm = graph.getHierarchyManager();
789 hm.closeGroup(groupNode);
790 configureGroupRealizer(groupNode, groupNodeToIdMap.get(groupNode), true);
791 layoutGraph(false);
792 }
793
794
799 void expandGroup(final Node folderNode) {
800 final Graph2D graph = getGraph2D();
801 final HierarchyManager hm = graph.getHierarchyManager();
802 hm.openFolder(folderNode);
803 configureGroupRealizer(folderNode, groupNodeToIdMap.get(folderNode), false);
804 layoutGraph(false);
805 }
806
807
812 private void buildGlobalGraph() {
813 final Graph2D graph = getGraph2D();
814 graph.clear();
815 tree2GraphMap = Maps.createHashedDataMap();
816 graph2TreeMap = Maps.createHashedDataMap();
817 final Object treeNode = model.getRoot();
818 final Node graphNode = graph.createNode();
819 tree2GraphMap.set(treeNode, graphNode);
820 graph2TreeMap.set(graphNode, treeNode);
821 buildGraph(treeNode, graphNode, tree2GraphMap, graph2TreeMap);
822
823 if(isGroupViewEnabled()) {
824 addGroupNodes();
825 }
826
827 allNodes = new NodeList(graph.nodes());
828 allEdges = new EdgeList(graph.edges());
829
830 final DataMap comparableMap = Maps.createHashedDataMap();
831 NormalizingGraphElementOrderStage.fillComparableMapFromGraph(graph, comparableMap, comparableMap);
832 graph.addDataProvider(NormalizingGraphElementOrderStage.COMPARABLE_EDGE_DPKEY, comparableMap);
833 graph.addDataProvider(NormalizingGraphElementOrderStage.COMPARABLE_NODE_DPKEY, comparableMap);
834 graph.addDataProvider(GRAPH_2_TREE_MAP_DPKEY, graph2TreeMap);
835 graph.addDataProvider(TREE_2_GRAPH_MAP_DPKEY, tree2GraphMap);
836 }
837
838
847 private void buildGraph(final Object treeNode, final Node graphNode, final DataMap tree2GraphMap,
848 final DataMap graph2TreeMap) {
849 final Graph2D graph = getGraph2D();
850 final int count = model.getChildCount(treeNode);
851 for(int i = 0; i < count; i++) {
852 final Object treeChild = model.getChild(treeNode, i);
853 final Node graphChild = graph.createNode();
854 tree2GraphMap.set(treeChild, graphChild);
855 graph2TreeMap.set(graphChild, treeChild);
856 graph.createEdge(graphNode, graphChild);
858 buildGraph(treeChild, graphChild, tree2GraphMap, graph2TreeMap);
859 }
860 }
861
862
865 private void addGroupNodes() {
866 final Graph2D graph = getGraph2D();
867 idToGroupNodeMap = new HashMap();
868 groupNodeToIdMap = new HashMap();
869 final HierarchyManager hm = graph.getHierarchyManager();
870 for(final NodeCursor nc = graph.nodes(); nc.ok(); nc.next()) {
871 final Node n = nc.node();
872 final Object obj = getUserObject(n);
873 if (obj != null) {
874 final Object groupId = getGroupId(obj);
875 if (groupId != null) {
876 Node groupNode = (Node) idToGroupNodeMap.get(groupId);
877 if (groupNode == null) {
878 groupNode = hm.createGroupNode(graph);
879 idToGroupNodeMap.put(groupId, groupNode);
880 groupNodeToIdMap.put(groupNode, groupId);
881 }
882 hm.setParentNode(n, groupNode);
883 }
884 }
885 }
886 }
887
888
900 private void buildLocalView(final Object userObject, final NodeList removedNodes, final NodeList addedNodes,
901 final EdgeList removedEdges, final EdgeList addedEdges) {
902 expandAll();
903 final Graph2D graph = getGraph2D();
904 final NodeMap prevNodeMap = Maps.createHashedNodeMap();
905 for (NodeCursor nc = graph.nodes(); nc.ok(); nc.next()) {
906 final Node n = nc.node();
907 prevNodeMap.setBool(n, true);
908 }
909 final EdgeMap prevEdgeMap = Maps.createHashedEdgeMap();
910 for (final EdgeCursor ec = graph.edges(); ec.ok(); ec.next()) {
911 final Edge e = ec.edge();
912 prevEdgeMap.setBool(e, true);
913 }
914
915 rebuildGlobalGraph();
916 for (final NodeCursor nc = graph.nodes(); nc.ok(); nc.next()) {
917 final Node n = nc.node();
918 if(getUserObject(n).equals(userObject)) {
919 final NodeList nodes = new NodeList(n);
920 if(n.inDegree() == 1) {
921 final Node parent = n.firstInEdge().source();
922 nodes.add(parent);
923 if(isSiblingViewEnabled()) {
924 nodes.pop();
925 nodes.addAll(parent.successors());
926 }
927 }
928 nodes.addAll(n.successors());
929
930 final NodeList nodesToRemove = new NodeList(graph.nodes());
931
932 if(isGroupViewEnabled()) {
933 final HashSet requiredGroups = new HashSet();
934 for(NodeCursor ncc = nodes.nodes(); ncc.ok(); ncc.next()) {
936 final Node node = ncc.node();
937 requiredGroups.add(graph.getHierarchyManager().getParentNode(node));
938 }
939 nodesToRemove.removeAll(requiredGroups);
940 }
941 nodesToRemove.removeAll(nodes);
942
943 while(!nodesToRemove.isEmpty()) {
944 graph.removeNode(nodesToRemove.popNode());
945 }
946 break;
947 }
948 }
949
950 for(final NodeCursor nc = allNodes.nodes(); nc.ok(); nc.next()) {
951 final Node n = nc.node();
952 if(n.getGraph() != null) {
953 if (!prevNodeMap.getBool(n)) {
955 addedNodes.add(n);
957 }
958 } else {
959 if(prevNodeMap.getBool(n)) {
961 removedNodes.add(n);
963 }
964 }
965 }
966 for(final EdgeCursor ec = allEdges.edges(); ec.ok(); ec.next()) {
967 final Edge e = ec.edge();
968 if(e.getGraph() != null) {
969 if (!prevEdgeMap.getBool(e)) {
971 addedEdges.add(e);
973 }
974 } else {
975 if(prevEdgeMap.getBool(e)) {
977 removedEdges.add(e);
979 }
980 }
981 }
982
983 final Node employeeNode = getNodeForUserObject(userObject);
984 if(employeeNode != null) {
985 graph.setSelected(employeeNode, true);
986 }
987 }
988
989
993 private void expandAll() {
994 final HierarchyManager hm = getGraph2D().getHierarchyManager();
995 for (NodeCursor nc = allNodes.nodes(); nc.ok(); nc.next()) {
996 final Node n = nc.node();
997 if(hm.isFolderNode(n)) {
998 hm.openFolder(n);
999 }
1000 }
1001 }
1002
1003 private void rebuildGlobalGraph() {
1004 final Graph2D graph = getGraph2D();
1005 graph.clear();
1006 for (final NodeCursor nc = allNodes.nodes(); nc.ok(); nc.next()) {
1007 final Node n = nc.node();
1008 graph.reInsertNode(n);
1009 }
1010 for (final EdgeCursor ec = allEdges.edges(); ec.ok(); ec.next()) {
1011 final Edge e = ec.edge();
1012 graph.reInsertEdge(e);
1013 }
1014
1015 if(isGroupViewEnabled()) {
1017 for (final NodeCursor nc = allNodes.nodes(); nc.ok(); nc.next()) {
1018 final Node n = nc.node();
1019 final Object obj = getUserObject(n);
1020 if(obj != null && getGroupId(obj) != null) {
1021 final HierarchyManager hm = getGraph2D().getHierarchyManager();
1022 final Node groupNode = (Node) idToGroupNodeMap.get(getGroupId(obj));
1023 if(hm.isNormalNode(groupNode)) {
1024 hm.convertToGroupNode(groupNode);
1025 }
1026 hm.setParentNode(n, groupNode);
1027 }
1028 }
1029 }
1030 }
1031
1032
1040 private AnimationObject createDeleteAnimation(final Graph2D graph,final List nodesToBeDeleted,
1041 final List edgesToBeDeleted, final ViewAnimationFactory factory, final long preferredDuration) {
1042 final CompositeAnimationObject deleteEdges = AnimationFactory.createConcurrency();
1043 for (final Iterator it = edgesToBeDeleted.iterator(); it.hasNext();) {
1044 final EdgeRealizer er = graph.getRealizer((Edge) it.next());
1045 deleteEdges.addAnimation(factory.fadeOut(er, ViewAnimationFactory.APPLY_EFFECT, preferredDuration));
1046 }
1047
1048 final CompositeAnimationObject deleteNodes = AnimationFactory.createConcurrency();
1049 for (final Iterator it = nodesToBeDeleted.iterator(); it.hasNext();) {
1050 final NodeRealizer nr = graph.getRealizer((Node) it.next());
1051 deleteNodes.addAnimation(factory.fadeOut(nr, ViewAnimationFactory.APPLY_EFFECT, preferredDuration));
1052 }
1053 return AnimationFactory.createSequence(deleteEdges, deleteNodes);
1054 }
1055
1056
1063 private AnimationObject createFadeInAnimation(final Graph2D graph, final List nodesToBeAdded,
1064 final List edgesToBeAdded, final ViewAnimationFactory factory, final long preferredDuration) {
1065 final CompositeAnimationObject addElems = AnimationFactory.createConcurrency();
1066 for (final Iterator it = edgesToBeAdded.iterator(); it.hasNext();) {
1067 final EdgeRealizer er = graph.getRealizer((Edge) it.next());
1068 addElems.addAnimation(factory.fadeIn(er, preferredDuration));
1069 }
1070 for (final Iterator it = nodesToBeAdded.iterator(); it.hasNext();) {
1071 final NodeRealizer nr = graph.getRealizer((Node) it.next());
1072 addElems.addAnimation(factory.fadeIn(nr, preferredDuration));
1073 }
1074 return addElems;
1075 }
1076
1077
1078
1086 private static class SelectRootWrapperAction implements Action {
1087 final Action delegateAction;
1088 final Graph2DView view;
1089
1090 SelectRootWrapperAction(final Action delegateAction, final Graph2DView view) {
1091 this.delegateAction = delegateAction;
1092 this.view = view;
1093 }
1094
1095 public void addPropertyChangeListener(final PropertyChangeListener listener) {
1096 delegateAction.addPropertyChangeListener(listener);
1097 }
1098
1099 public Object getValue(final String key) {
1100 return delegateAction.getValue(key);
1101 }
1102
1103 public boolean isEnabled() {
1104 return delegateAction.isEnabled();
1105 }
1106
1107 public void putValue(final String key, final Object value) {
1108 delegateAction.putValue(key, value);
1109 }
1110
1111 public void removePropertyChangeListener(final PropertyChangeListener listener) {
1112 delegateAction.removePropertyChangeListener(listener);
1113 }
1114
1115 public void setEnabled(final boolean b) {
1116 delegateAction.setEnabled(b);
1117 }
1118
1119
1127 public void actionPerformed(final ActionEvent e) {
1128 final Graph2D graph = view.getGraph2D();
1129 boolean selectionEmpty = Selections.isNodeSelectionEmpty(graph);
1130 if(selectionEmpty) {
1131 for (NodeCursor nc = graph.nodes(); nc.ok(); nc.next()) {
1133 final Node n = nc.node();
1134 if(n.inDegree() == 0) {
1135 graph.setSelected(n, true);
1136 selectionEmpty = false;
1137 break;
1138 }
1139 }
1140 if(graph.nodeCount() > 0 && selectionEmpty) {
1141 graph.setSelected(graph.firstNode(), true);
1142 }
1143 }
1144 else {
1145 delegateAction.actionPerformed(e);
1146 }
1147 }
1148 }
1149
1150
1154 private class AnimatedZoomAction extends AbstractAction {
1155 private final boolean zoomIn;
1156
1157 private ViewAnimationFactory factory;
1158 private AnimationPlayer player;
1159
1160 AnimatedZoomAction( final boolean zoomIn ) {
1161 this.zoomIn = zoomIn;
1162 }
1163
1164
1168 public void actionPerformed(final ActionEvent e) {
1169 if (factory == null) {
1170 factory = new ViewAnimationFactory(JTreeChart.this);
1171 player = factory.createConfiguredPlayer();
1172 }
1173
1174 if (!player.isPlaying()) {
1175 player.animate(AnimationFactory.createEasedAnimation(
1176 factory.zoom(calculateZoom(), ViewAnimationFactory.APPLY_EFFECT, 500)));
1177 }
1178 }
1179
1180
1184 double calculateZoom() {
1185 if (zoomIn) {
1186 return Math.min(4, getZoom()*2);
1187 } else {
1188 final Point2D oldP = getViewPoint2D();
1189 final double oldZoom = getZoom();
1190 fitContent();
1191 final double fitContentZoom = getZoom();
1192 setZoom(oldZoom);
1193 setViewPoint2D(oldP.getX(), oldP.getY());
1194
1195 return Math.max(fitContentZoom, getZoom()*0.5);
1196 }
1197 }
1198 }
1199
1200
1204 private class NodeAction extends AbstractAction {
1205 public void actionPerformed(final ActionEvent e) {
1206 if(!Selections.isNodeSelectionEmpty(getGraph2D())) {
1207 performNodeAction(getGraph2D().selectedNodes().node());
1208 }
1209 }
1210 }
1211
1212
1216 private class FitContentAction extends AbstractAction {
1217 public void actionPerformed(final ActionEvent e) {
1218 fitContent(true);
1219 }
1220 }
1221
1222
1223 private static final class FixState {
1224 final Object focusedUserData;
1225 final double focusedCenterX;
1226 final double focusedCenterY;
1227 final boolean focusedSelected;
1228
1229 FixState(
1230 final Object userData,
1231 final double centerX,
1232 final double centerY,
1233 final boolean selected
1234 ) {
1235 this.focusedUserData = userData;
1236 this.focusedCenterX = centerX;
1237 this.focusedCenterY = centerY;
1238 this.focusedSelected = selected;
1239 }
1240 }
1241}
1242