1   
28  package demo.view.mindmap;
29  
30  import y.base.DataProvider;
31  import y.base.Edge;
32  import y.base.EdgeCursor;
33  import y.base.EdgeList;
34  import y.base.ListCell;
35  import y.base.Node;
36  import y.base.NodeCursor;
37  import y.util.DataProviderAdapter;
38  import y.view.Graph2D;
39  import y.view.Graph2DView;
40  import y.view.Graph2DViewActions;
41  import y.view.HitInfo;
42  import y.view.NodeLabel;
43  import y.view.ViewMode;
44  import y.view.YLabel;
45  
46  import javax.swing.AbstractAction;
47  import javax.swing.Action;
48  import javax.swing.ActionMap;
49  import javax.swing.InputMap;
50  import javax.swing.JComponent;
51  import javax.swing.KeyStroke;
52  import java.awt.event.ActionEvent;
53  import java.awt.event.KeyEvent;
54  
55  
59  class KeyboardHandling {
60    
63    private KeyboardHandling() {
64    }
65  
66  
67    static void setKeyActions( final Graph2DView view ) {
68      final ActionMap actionMap = view.getActionMap();
69      final InputMap inputMap = view.getInputMap();
70          actionMap.put("INSERT_CHILD", new AddChildAction(view));
72      actionMap.put("INSERT_SIBLING", new AddSiblingAction(view));
73      actionMap.put("RIGHT", new CursorAction(CursorAction.RIGHT, view));
74      actionMap.put("LEFT", new CursorAction(CursorAction.LEFT, view));
75      actionMap.put("UP", new CursorAction(CursorAction.UP, view));
76      actionMap.put("DOWN", new CursorAction(CursorAction.DOWN, view));
77      actionMap.put("DELETE", new DeleteSelection(view.getGraph2D()));
78      actionMap.put("PLUS", new CollapseExpandAction(true, view.getGraph2D()));
79      actionMap.put("MINUS", new CollapseExpandAction(false, view.getGraph2D()));
80      actionMap.put("EDIT", new EditAction(view));
81          inputMap.put(KeyStroke.getKeyStroke(KeyEvent.VK_INSERT, 0), "INSERT_CHILD");
83      inputMap.put(KeyStroke.getKeyStroke(KeyEvent.VK_ENTER, 0), "INSERT_SIBLING");
84      inputMap.put(KeyStroke.getKeyStroke(KeyEvent.VK_RIGHT, 0), "RIGHT");
85      inputMap.put(KeyStroke.getKeyStroke(KeyEvent.VK_LEFT, 0), "LEFT");
86      inputMap.put(KeyStroke.getKeyStroke(KeyEvent.VK_UP, 0), "UP");
87      inputMap.put(KeyStroke.getKeyStroke(KeyEvent.VK_DOWN, 0), "DOWN");
88      inputMap.put(KeyStroke.getKeyStroke(KeyEvent.VK_DELETE, 0), "DELETE");
89      inputMap.put(KeyStroke.getKeyStroke(KeyEvent.VK_BACK_SPACE,0),"DELETE");
90      inputMap.put(KeyStroke.getKeyStroke(KeyEvent.VK_PLUS, 0), "PLUS");
91      inputMap.put(KeyStroke.getKeyStroke(KeyEvent.VK_ADD,0), "PLUS");
92      inputMap.put(KeyStroke.getKeyStroke(KeyEvent.VK_MINUS, 0), "MINUS");
93      inputMap.put(KeyStroke.getKeyStroke(KeyEvent.VK_SUBTRACT, 0), "MINUS");
94      inputMap.put(KeyStroke.getKeyStroke(KeyEvent.VK_SPACE, 0), "EDIT");
95      inputMap.put(KeyStroke.getKeyStroke(KeyEvent.VK_F2,0), "EDIT");
96      view.getCanvasComponent().setInputMap(JComponent.WHEN_FOCUSED, inputMap);
97      view.getCanvasComponent().setActionMap(actionMap);
98    }
99  
100   
105   static Action createDeleteSelectionAction( final Graph2DView view ) {
106     final DeleteSelection ds = new DeleteSelection(view.getGraph2D());
107     final ActionMap actionMap = view.getCanvasComponent().getActionMap();
108     actionMap.put(Graph2DViewActions.DELETE_SELECTION, ds);
109     return ds;
110   }
111 
112   private static class DeleteSelection extends AbstractAction {
113     private final Graph2D graph2D;
114 
115     public DeleteSelection( final Graph2D graph2D ) {
116       super("Delete Selection");
117       this.graph2D = graph2D;
118       this.putValue(Action.SMALL_ICON, MindMapUtil.getIcon("delete.png"));
119       this.putValue(Action.SHORT_DESCRIPTION, "Delete Selection");
120     }
121 
122     public void actionPerformed(final ActionEvent e) {
123             if (graph2D.selectedEdges().size() > 0) {
125         final Edge edge = graph2D.selectedEdges().edge();
126         if (ViewModel.instance.isCrossReference(edge)) {
127           graph2D.removeEdge(edge);
128           graph2D.getCurrentView().updateView();
129         }
130             } else if (!graph2D.isSelectionEmpty()) {
132         final Node node = graph2D.selectedNodes().node();
133         if (!ViewModel.instance.isRoot(node)) {
134           graph2D.firePreEvent();
135           final Edge inEdge = MindMapUtil.inEdge(node);
136           MindMapUtil.removeSubtree(graph2D, node);
137           if (inEdge != null) {
138             graph2D.setSelected(inEdge.source(), true);
139           }
140           LayoutUtil.layout(graph2D);
141           graph2D.firePostEvent();
142         }
143       }
144     }
145   }
146 
147   
150   private static class EditAction extends Graph2DViewActions.EditLabelAction {
151     EditAction( final Graph2DView view ) {
152       super(view);
153     }
154 
155     
160     protected YLabel findLabel( final Graph2DView view ) {
161       final Graph2D graph = view.getGraph2D();
162       final NodeCursor nc = graph.selectedNodes();
163       if (nc.ok()) {
164         return graph.getRealizer(nc.node()).getLabel();
165       } else {
166         for (EdgeCursor ec = graph.selectedEdges(); ec.ok(); ec.next()) {
167           final Edge edge = ec.edge();
168           if (ViewModel.instance.isCrossReference(edge)) {
169             return graph.getRealizer(edge).getLabel();
170           }
171         }
172       }
173       return null;
174     }
175 
176     
184     protected void setText( final YLabel label, final String text ) {
185       if (label instanceof NodeLabel) {
186         final NodeLabel nl = (NodeLabel) label;
187         final Graph2D graph = nl.getGraph2D();
188                                 graph.backupRealizers();
192         nl.setText(text);
193         MindMapUtil.updateWidth(graph, nl.getNode());
194         LayoutUtil.layout(graph);
195       } else {
196         label.setText(text);
197       }
198     }
199   }
200 
201   
205   static class LabelChangeViewMode extends ViewMode {
206     
212     public void mouseClicked( final double x, final double y ) {
213       if (lastClickEvent != null && lastClickEvent.getClickCount() == 2) {
214         final HitInfo hitInfo = getHitInfo(x, y);
215         if (hitInfo.hasHitNodeLabels()) {
216           editLabel(hitInfo.getHitNodeLabel());
217         } else if (hitInfo.hasHitNodes()) {
218           final Graph2D graph2D = getGraph2D();
219           editLabel(graph2D.getRealizer(hitInfo.getHitNode()).getLabel());
220         } else if (hitInfo.hasHitEdges()) {
221           final Edge edge = hitInfo.getHitEdge();
222                               if (ViewModel.instance.isCrossReference(edge)) {
225             final Graph2D graph2D = getGraph2D();
226             editLabel(graph2D.getRealizer(edge).getLabel());
227           }
228         } else if (hitInfo.hasHitEdgeLabels()) {
229           editLabel(hitInfo.getHitEdgeLabel());
230         }
231       }
232     }
233 
234     private void editLabel( final YLabel label ) {
235       KeyboardHandling.editLabel(view, label);
236     }
237   }
238 
239   
244   static void editLabel( final Graph2DView view, final YLabel label ) {
245     final EditAction helper = new EditAction(view) {
246       protected YLabel findLabel( final Graph2DView view ) {
247         return label;
248       }
249     };
250     helper.editLabel(view);
251   }
252 
253   
256   private static class CollapseExpandAction extends AbstractAction {
257     
260     private final boolean expand;
261     private final Graph2D graph2D;
262 
263     public CollapseExpandAction(final boolean expand, final Graph2D graph2D) {
264       this.expand = expand;
265       this.graph2D = graph2D;
266     }
267 
268     public void actionPerformed(ActionEvent e) {
269       final NodeCursor nodeCursor = graph2D.selectedNodes();
270       if (nodeCursor.size() > 0) {
271         graph2D.firePreEvent();
272         final Node n = nodeCursor.node();
273         if (expand) {
274           MindMapUtil.expandNode(graph2D, n);
275         } else {
276           MindMapUtil.collapseNode(graph2D, n);
277         }
278         LayoutUtil.layout(graph2D);
279         graph2D.firePostEvent();
280       }
281     }
282   }
283 
284   
289   private static class AddSiblingAction extends AbstractAction {
290     private final Graph2DView view;
291 
292     AddSiblingAction( final Graph2DView view ) {
293       this.view = view;
294     }
295 
296     public void actionPerformed(final ActionEvent e) {
297       final NodeCursor nc = view.getGraph2D().selectedNodes();
298       if (nc.ok()) {
299         final Node node = nc.node();
300         if (ViewModel.instance.isRoot(node)) {
301           MindMapUtil.addNode(view, node);
302         } else {
303           final Node parent = MindMapUtil.inEdge(node).source();
304           MindMapUtil.addNode(view, parent, ViewModel.instance.isLeft(node));
305         }
306       }
307     }
308   }
309 
310   
313   private static class AddChildAction extends AbstractAction {
314     private final Graph2DView view;
315 
316     AddChildAction( final Graph2DView view ) {
317       this.view = view;
318     }
319 
320     public void actionPerformed( final ActionEvent e ) {
321       final NodeCursor nc = view.getGraph2D().selectedNodes();
322       if (nc.ok()) {
323         MindMapUtil.addNode(view, nc.node());
324       }
325     }
326   }
327 
328   
331   private static class CursorAction extends AbstractAction {
332     private final int cursorMode;
333     private final Graph2DView view;
334 
335     static final int UP = 1;
336     static final int DOWN = 2;
337     static final int LEFT = 3;
338     static final int RIGHT = 4;
339 
340     public CursorAction(final int cursorMode, final Graph2DView view) {
341       this.cursorMode = cursorMode;
342       this.view = view;
343     }
344 
345     public void actionPerformed(ActionEvent e) {
346       final Graph2D graph2D = view.getGraph2D();
347       final NodeCursor nodeCursor = graph2D.selectedNodes();
348       if (nodeCursor.size() > 0) {
349         final Node node = nodeCursor.node();
350         Node target = null;
351         final ViewModel model = ViewModel.instance;
352         switch (cursorMode) {
353           case DOWN:
354             if (!model.isRoot(node)) {
355               final Node parent = MindMapUtil.inEdge(node).source();
356                             final boolean side = model.isLeft(node);
358               final EdgeList outEdges = new EdgeList(parent.outEdges(), getSameSidePredicate(side, model));
359                             outEdges.sort(new LayoutUtil.YCoordComparator());
361                             final ListCell currentCell = outEdges.findCell(MindMapUtil.inEdge(node));
363               final ListCell succCell = outEdges.cyclicSucc(currentCell);
364               target = ((Edge) succCell.getInfo()).target();
365             }
366             break;
367           case UP:
368             if (!model.isRoot(node)) {
369               final Node parent = MindMapUtil.inEdge(node).source();
370                             final boolean side = model.isLeft(node);
372               final EdgeList outEdges = new EdgeList(parent.outEdges(), getSameSidePredicate(side, model));
373                             outEdges.sort(new LayoutUtil.YCoordComparator());
375                             final ListCell currentCell = outEdges.findCell(MindMapUtil.inEdge(node));
377               final ListCell succCell = outEdges.cyclicPred(currentCell);
378               target = ((Edge) succCell.getInfo()).target();
379             }
380             break;
381           case LEFT:
382             if (!model.isRoot(node)) {
383                             if (model.isLeft(node)) {
385                 final EdgeList edgeList = MindMapUtil.outEdges(node);
386                 if (!edgeList.isEmpty()) {
387                   target = edgeList.popEdge().target();
388                 }
389               } else {
390                 target = MindMapUtil.inEdge(node).source();
391               }
392             } else {
393                             if (node.outDegree() > 0) {
395                 for (EdgeList edges = MindMapUtil.outEdges(node);!edges.isEmpty();) {
396                   final Edge edge = edges.popEdge();
397                   if (model.isLeft(edge.target())) {
398                     target = edge.target();
399                     break;
400                   }
401                 }
402               }
403             }
404             break;
405           case RIGHT:
406             if (!model.isRoot(node)) {
407                             if (model.isLeft(node)) {
409                 target = MindMapUtil.inEdge(node).source();
410               } else {
411                 final EdgeList edgeList = MindMapUtil.outEdges(node);
412                 if (!edgeList.isEmpty()) {
413                   target = edgeList.popEdge().target();
414                 }
415               }
416             } else {
417                             if (node.outDegree() > 0) {
419                 for (EdgeList edges = MindMapUtil.outEdges(node); !edges.isEmpty(); ) {
420                   final Edge edge = edges.popEdge();
421                   if (!model.isLeft(edge.target())) {
422                     target = edge.target();
423                     break;
424                   }
425                 }
426               }
427             }
428             break;
429         }
430         if (target != null) {
431           graph2D.setSelected(node, false);
432           view.updateView();
433           graph2D.setSelected(target, true);
434         }
435       }
436     }
437 
438     
446     private DataProvider getSameSidePredicate(final boolean side, final ViewModel model) {
447       return new DataProviderAdapter() {
448         public boolean getBool(Object dataHolder) {
449           final Edge edge = (Edge) dataHolder;
450           return side == model.isLeft(edge.target()) && !model.isCrossReference(edge);
451         }
452       };
453     }
454   }
455 }
456