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