Mercurial > hg > ccmieditor
comparison java/src/uk/ac/qmul/eecs/ccmi/gui/DiagramTree.java @ 3:9e67171477bc
PHANTOM Omni Heptic device release
author | Fiore Martin <fiore@eecs.qmul.ac.uk> |
---|---|
date | Wed, 25 Apr 2012 17:09:09 +0100 |
parents | 4b2f975e35fa |
children | d66dd5880081 |
comparison
equal
deleted
inserted
replaced
2:4b2f975e35fa | 3:9e67171477bc |
---|---|
38 import javax.swing.tree.TreeNode; | 38 import javax.swing.tree.TreeNode; |
39 import javax.swing.tree.TreePath; | 39 import javax.swing.tree.TreePath; |
40 import javax.swing.tree.TreeSelectionModel; | 40 import javax.swing.tree.TreeSelectionModel; |
41 | 41 |
42 import uk.ac.qmul.eecs.ccmi.diagrammodel.DiagramElement; | 42 import uk.ac.qmul.eecs.ccmi.diagrammodel.DiagramElement; |
43 import uk.ac.qmul.eecs.ccmi.diagrammodel.DiagramModelTreeNode; | 43 import uk.ac.qmul.eecs.ccmi.diagrammodel.DiagramTreeNode; |
44 import uk.ac.qmul.eecs.ccmi.diagrammodel.DiagramNode; | 44 import uk.ac.qmul.eecs.ccmi.diagrammodel.DiagramNode; |
45 import uk.ac.qmul.eecs.ccmi.diagrammodel.EdgeReferenceMutableTreeNode; | 45 import uk.ac.qmul.eecs.ccmi.diagrammodel.EdgeReferenceMutableTreeNode; |
46 import uk.ac.qmul.eecs.ccmi.diagrammodel.NodeReferenceMutableTreeNode; | 46 import uk.ac.qmul.eecs.ccmi.diagrammodel.NodeReferenceMutableTreeNode; |
47 import uk.ac.qmul.eecs.ccmi.diagrammodel.TreeModel; | 47 import uk.ac.qmul.eecs.ccmi.diagrammodel.TreeModel; |
48 import uk.ac.qmul.eecs.ccmi.diagrammodel.TypeMutableTreeNode; | 48 import uk.ac.qmul.eecs.ccmi.diagrammodel.TypeMutableTreeNode; |
49 import uk.ac.qmul.eecs.ccmi.network.Command; | |
50 import uk.ac.qmul.eecs.ccmi.network.DiagramEventActionSource; | |
49 import uk.ac.qmul.eecs.ccmi.sound.PlayerListener; | 51 import uk.ac.qmul.eecs.ccmi.sound.PlayerListener; |
50 import uk.ac.qmul.eecs.ccmi.sound.SoundEvent; | 52 import uk.ac.qmul.eecs.ccmi.sound.SoundEvent; |
51 import uk.ac.qmul.eecs.ccmi.sound.SoundFactory; | 53 import uk.ac.qmul.eecs.ccmi.sound.SoundFactory; |
52 import uk.ac.qmul.eecs.ccmi.speech.Narrator; | 54 import uk.ac.qmul.eecs.ccmi.speech.Narrator; |
53 import uk.ac.qmul.eecs.ccmi.speech.NarratorFactory; | 55 import uk.ac.qmul.eecs.ccmi.speech.NarratorFactory; |
54 import uk.ac.qmul.eecs.ccmi.speech.SpeechUtilities; | 56 import uk.ac.qmul.eecs.ccmi.speech.SpeechUtilities; |
55 import uk.ac.qmul.eecs.ccmi.utils.InteractionLog; | 57 import uk.ac.qmul.eecs.ccmi.utils.InteractionLog; |
56 | 58 |
59 | |
57 @SuppressWarnings("serial") | 60 @SuppressWarnings("serial") |
58 public class DiagramTree extends JTree { | 61 public class DiagramTree extends JTree { |
62 /** | |
63 * Creates a new diagram tree. The model of this tree is set to the tree model | |
64 * held by the instance of {@code Diagram} passed as argument. The model is retrieved by a call | |
65 * to {@code getTreeModel()} on the diagram. | |
66 * <p> | |
67 * The tree doesn't allow interaction via the mouse. It can be navigated via the keyboard using | |
68 * the arrow keys. When a node is selected, cursoring up and down allows the user to go through | |
69 * all the sibling of the selected node. Cursoring right will expand the selected node (if it has children) | |
70 * and select its first child. Cursoring left will collapse a node and select its father. All the motions | |
71 * trigger a text to speech utterance (possibly accompanied by sound) about the new selected node. | |
72 * | |
73 * @param diagram a reference to the diagram holding the tree model for this tree. | |
74 */ | |
59 public DiagramTree(Diagram diagram){ | 75 public DiagramTree(Diagram diagram){ |
60 super(diagram.getTreeModel()); | 76 super(diagram.getTreeModel()); |
61 this.diagram = diagram; | 77 this.diagram = diagram; |
62 resources = ResourceBundle.getBundle(EditorFrame.class.getName()); | 78 resources = ResourceBundle.getBundle(EditorFrame.class.getName()); |
63 | 79 |
64 TreePath rootPath = new TreePath((DiagramModelTreeNode)diagram.getTreeModel().getRoot()); | 80 TreePath rootPath = new TreePath((DiagramTreeNode)diagram.getTreeModel().getRoot()); |
65 setSelectionPath(rootPath); | 81 setSelectionPath(rootPath); |
66 collapsePath(rootPath); | 82 collapsePath(rootPath); |
67 selectedNodes = new ArrayList<Node>(); | 83 selectedNodes = new ArrayList<Node>(); |
68 setEditable(false); | 84 setEditable(false); |
69 getSelectionModel().setSelectionMode(TreeSelectionModel.SINGLE_TREE_SELECTION); | 85 getSelectionModel().setSelectionMode(TreeSelectionModel.SINGLE_TREE_SELECTION); |
70 overwriteTreeKeystrokes(); | 86 overwriteTreeKeystrokes(); |
71 /* don't use the swing focus system as we provide one on our own */ | 87 /* don't use the swing focus system as we provide one on our own */ |
72 setFocusTraversalKeysEnabled(false); | 88 setFocusTraversalKeysEnabled(false); |
73 getAccessibleContext().setAccessibleName(""); | 89 getAccessibleContext().setAccessibleName("tree"); |
74 } | 90 } |
75 | 91 |
76 @SuppressWarnings("unchecked") | 92 @SuppressWarnings("unchecked") |
77 @Override | 93 @Override |
78 public TreeModel<Node,Edge> getModel(){ | 94 public TreeModel<Node,Edge> getModel(){ |
79 return (TreeModel<Node,Edge>)super.getModel(); | 95 return (TreeModel<Node,Edge>)super.getModel(); |
80 } | 96 } |
81 | 97 |
98 /** | |
99 * @see javax.swing.JTree#setModel(javax.swing.tree.TreeModel) | |
100 * | |
101 * @param newModel the new mnodel for this tree | |
102 */ | |
82 public void setModel(TreeModel<Node,Edge> newModel){ | 103 public void setModel(TreeModel<Node,Edge> newModel){ |
83 DiagramModelTreeNode selectedTreeNode = (DiagramModelTreeNode)getSelectionPath().getLastPathComponent(); | 104 DiagramTreeNode selectedTreeNode = (DiagramTreeNode)getSelectionPath().getLastPathComponent(); |
84 super.setModel(newModel); | 105 super.setModel(newModel); |
85 collapseRow(0); | 106 collapseRow(0); |
86 setSelectionPath(new TreePath(selectedTreeNode.getPath())); | 107 setSelectionPath(new TreePath(selectedTreeNode.getPath())); |
87 } | 108 } |
88 | 109 |
110 /** | |
111 * Set a new diagram for this tree. As a result of this call the tree model | |
112 * of this tree will be set to the model return by {@code diagram.getTreeModel()} | |
113 * | |
114 * @param diagram the new diagram for this tree | |
115 */ | |
89 public void setDiagram(Diagram diagram){ | 116 public void setDiagram(Diagram diagram){ |
90 this.diagram = diagram; | 117 this.diagram = diagram; |
91 setModel(diagram.getTreeModel()); | 118 setModel(diagram.getTreeModel()); |
92 } | 119 } |
93 | 120 |
94 public void selectNode(final Node n){ | 121 private void selectNode(final Node n){ |
95 selectedNodes.add(n); | 122 selectedNodes.add(n); |
96 treeModel.valueForPathChanged(new TreePath(n.getPath()),n.getName()); | 123 treeModel.valueForPathChanged(new TreePath(n.getPath()),n.getName()); |
97 | 124 |
98 SoundFactory.getInstance().play(SoundEvent.OK,new PlayerListener(){ | 125 SoundFactory.getInstance().play(SoundEvent.OK,new PlayerListener(){ |
99 @Override | 126 @Override |
102 } | 129 } |
103 }); | 130 }); |
104 InteractionLog.log(INTERACTIONLOG_SOURCE,"node selected for edge",n.getName()); | 131 InteractionLog.log(INTERACTIONLOG_SOURCE,"node selected for edge",n.getName()); |
105 } | 132 } |
106 | 133 |
107 public void unselectNode(final Node n){ | 134 private void unselectNode(final Node n){ |
108 selectedNodes.remove(n); | 135 selectedNodes.remove(n); |
109 treeModel.valueForPathChanged(new TreePath(n.getPath()),n.getName()); | 136 treeModel.valueForPathChanged(new TreePath(n.getPath()),n.getName()); |
110 | 137 |
111 SoundFactory.getInstance().play(SoundEvent.OK,new PlayerListener(){ | 138 SoundFactory.getInstance().play(SoundEvent.OK,new PlayerListener(){ |
112 @Override | 139 @Override |
115 } | 142 } |
116 }); | 143 }); |
117 InteractionLog.log(INTERACTIONLOG_SOURCE,"node unselected for edge",DiagramElement.toLogString(n)); | 144 InteractionLog.log(INTERACTIONLOG_SOURCE,"node unselected for edge",DiagramElement.toLogString(n)); |
118 } | 145 } |
119 | 146 |
147 /** | |
148 * Returns an array containing the references to all the nodes that have so far been selected | |
149 * for edge creation. A new array is created each time this method is called. | |
150 * | |
151 * @return an array of nodes | |
152 */ | |
120 public DiagramNode[] getSelectedNodes(){ | 153 public DiagramNode[] getSelectedNodes(){ |
121 DiagramNode[] array = new DiagramNode[selectedNodes.size()]; | 154 DiagramNode[] array = new DiagramNode[selectedNodes.size()]; |
122 return selectedNodes.toArray(array); | 155 return selectedNodes.toArray(array); |
123 } | 156 } |
124 | 157 |
158 /** | |
159 * Makes all the nodes selected for edge creation unselected. This method should | |
160 * be called after an edge has been created, to get the user restart | |
161 * go over the selection process again. | |
162 * | |
163 */ | |
125 public void clearNodeSelections(){ | 164 public void clearNodeSelections(){ |
126 ArrayList<Node> tempList = new ArrayList<Node>(selectedNodes); | 165 ArrayList<Node> tempList = new ArrayList<Node>(selectedNodes); |
127 selectedNodes.clear(); | 166 selectedNodes.clear(); |
128 for(Node n : tempList){ | 167 for(Node n : tempList){ |
129 treeModel.valueForPathChanged(new TreePath(n.getPath()),n.getName()); | 168 treeModel.valueForPathChanged(new TreePath(n.getPath()),n.getName()); |
130 diagram.getModelUpdater().yieldLock(n, Lock.MUST_EXIST); | 169 diagram.getModelUpdater().yieldLock(n, Lock.MUST_EXIST, new DiagramEventActionSource(DiagramEventSource.TREE,Command.Name.INSERT_EDGE,n.getId(),n.getName())); |
131 } | 170 } |
132 } | 171 } |
133 | 172 |
173 /** | |
174 * Returns a string for a text to speech synthesizer, describing the currently selected | |
175 * tree node. The one that is at the end of the current selection path. | |
176 * | |
177 * @return a description string suitable for text to speech synthesis | |
178 */ | |
134 public String currentPathSpeech(){ | 179 public String currentPathSpeech(){ |
135 TreePath path = getSelectionPath(); | 180 TreePath path = getSelectionPath(); |
136 DiagramModelTreeNode selectedPathTreeNode = (DiagramModelTreeNode)path.getLastPathComponent(); | 181 DiagramTreeNode selectedPathTreeNode = (DiagramTreeNode)path.getLastPathComponent(); |
137 if(selectedNodes.contains(selectedPathTreeNode)) | 182 if(selectedNodes.contains(selectedPathTreeNode)) |
138 /* add information about the fact that the node is selected */ | 183 /* add information about the fact that the node is selected */ |
139 return MessageFormat.format(resources.getString("speech.node_selected"), selectedPathTreeNode.spokenText()); | 184 return MessageFormat.format(resources.getString("speech.node_selected"), selectedPathTreeNode.spokenText()); |
140 else | 185 else |
141 return selectedPathTreeNode.spokenText(); | 186 return selectedPathTreeNode.spokenText(); |
142 } | 187 } |
143 | 188 |
144 /** | 189 /** |
145 * this method changes the selected tree node from an edge/node reference | 190 * Changes the selected tree path from the current to one defined by |
146 * to the related edge/node itself | 191 * the {@code JumpTo enum} |
192 * | |
193 * @see JumpTo | |
194 * | |
195 * @param jumpTo a {@code JumpTo enum} | |
147 */ | 196 */ |
148 public void jump(JumpTo jumpTo){ | 197 public void jump(JumpTo jumpTo){ |
149 final Narrator narrator = NarratorFactory.getInstance(); | 198 final Narrator narrator = NarratorFactory.getInstance(); |
150 TreePath oldPath; | 199 TreePath oldPath; |
151 switch(jumpTo){ | 200 switch(jumpTo){ |
152 case REFERENCE : | 201 case REFERENCE : |
153 oldPath = getSelectionPath(); | 202 oldPath = getSelectionPath(); |
154 DiagramModelTreeNode selectedTreeNode = (DiagramModelTreeNode)oldPath.getLastPathComponent(); | 203 DiagramTreeNode selectedTreeNode = (DiagramTreeNode)oldPath.getLastPathComponent(); |
155 if(selectedTreeNode instanceof NodeReferenceMutableTreeNode){ | 204 if(selectedTreeNode instanceof NodeReferenceMutableTreeNode){ |
156 final Node n = (Node)((NodeReferenceMutableTreeNode)selectedTreeNode).getNode(); | 205 final Node n = (Node)((NodeReferenceMutableTreeNode)selectedTreeNode).getNode(); |
157 setSelectionPath(new TreePath(n.getPath())); | 206 setSelectionPath(new TreePath(n.getPath())); |
158 SoundFactory.getInstance().setPlayerListener(new PlayerListener(){ | 207 SoundFactory.getInstance().play(SoundEvent.JUMP, new PlayerListener(){ |
159 @Override | 208 @Override |
160 public void playEnded() { | 209 public void playEnded() { |
161 narrator.speak(MessageFormat.format(resources.getString("speech.jump"),n.spokenText())); | 210 narrator.speak(MessageFormat.format(resources.getString("speech.jump"),n.spokenText())); |
162 } | 211 } |
163 }, SoundEvent.JUMP); | 212 }); |
164 }else if(selectedTreeNode instanceof EdgeReferenceMutableTreeNode){ | 213 }else if(selectedTreeNode instanceof EdgeReferenceMutableTreeNode){ |
165 final Edge e = (Edge)((EdgeReferenceMutableTreeNode)selectedTreeNode).getEdge(); | 214 final Edge e = (Edge)((EdgeReferenceMutableTreeNode)selectedTreeNode).getEdge(); |
166 setSelectionPath(new TreePath(e.getPath())); | 215 setSelectionPath(new TreePath(e.getPath())); |
167 SoundFactory.getInstance().setPlayerListener(new PlayerListener(){ | 216 SoundFactory.getInstance().play(SoundEvent.JUMP,new PlayerListener(){ |
168 @Override | 217 @Override |
169 public void playEnded() { | 218 public void playEnded() { |
170 narrator.speak(MessageFormat.format(resources.getString("speech.jump"),e.spokenText())); | 219 narrator.speak(MessageFormat.format(resources.getString("speech.jump"),e.spokenText())); |
171 } | 220 } |
172 }, SoundEvent.JUMP); | 221 }); |
173 } | 222 } |
174 /* assume the referee has only root in common with the reference and collapse everything up to the root (excluded) */ | 223 /* assume the referee has only root in common with the reference and collapse everything up to the root (excluded) */ |
175 collapseAll(selectedTreeNode, (DiagramModelTreeNode)selectedTreeNode.getPath()[1]); | 224 collapseAll(selectedTreeNode, (DiagramTreeNode)selectedTreeNode.getPath()[1]); |
176 break; | 225 break; |
177 case ROOT : | 226 case ROOT : |
178 final DiagramModelTreeNode from =(DiagramModelTreeNode)getSelectionPath().getLastPathComponent(); | 227 final DiagramTreeNode from =(DiagramTreeNode)getSelectionPath().getLastPathComponent(); |
179 setSelectionRow(0); | 228 setSelectionRow(0); |
180 collapseAll(from,from.getRoot()); | 229 collapseAll(from,from.getRoot()); |
181 SoundFactory.getInstance().setPlayerListener(new PlayerListener(){ | 230 SoundFactory.getInstance().play(SoundEvent.JUMP, new PlayerListener(){ |
182 @Override | 231 @Override |
183 public void playEnded() { | 232 public void playEnded() { |
184 narrator.speak(MessageFormat.format(resources.getString("speech.jump"),from.getRoot().spokenText())); | 233 narrator.speak(MessageFormat.format(resources.getString("speech.jump"),from.getRoot().spokenText())); |
185 } | 234 } |
186 }, SoundEvent.JUMP); | 235 }); |
187 break; | 236 break; |
188 case TYPE : // jumps to the ancestor type node of the current node, never used | 237 // case TYPE : // jumps to the ancestor type node of the current node, never used |
189 oldPath = getSelectionPath(); | 238 // oldPath = getSelectionPath(); |
190 int index = 0; | 239 // int index = 0; |
191 Object[] pathComponents = oldPath.getPath(); | 240 // Object[] pathComponents = oldPath.getPath(); |
192 for(int i=0;i<pathComponents.length;i++){ | 241 // for(int i=0;i<pathComponents.length;i++){ |
193 if(pathComponents[i] instanceof TypeMutableTreeNode){ | 242 // if(pathComponents[i] instanceof TypeMutableTreeNode){ |
194 index=i; | 243 // index=i; |
195 break; | 244 // break; |
196 } | 245 // } |
197 } | 246 // } |
198 final DiagramModelTreeNode typeTreeNode = (DiagramModelTreeNode)oldPath.getPathComponent(index); | 247 // final DiagramTreeNode typeTreeNode = (DiagramTreeNode)oldPath.getPathComponent(index); |
199 setSelectionPath(new TreePath(typeTreeNode.getPath())); | 248 // setSelectionPath(new TreePath(typeTreeNode.getPath())); |
200 collapseAll((DiagramModelTreeNode)oldPath.getLastPathComponent(),typeTreeNode); | 249 // collapseAll((DiagramTreeNode)oldPath.getLastPathComponent(),typeTreeNode); |
201 SoundFactory.getInstance().setPlayerListener(new PlayerListener(){ | 250 // SoundFactory.getInstance().play(SoundEvent.JUMP, new PlayerListener(){ |
202 @Override | 251 // @Override |
203 public void playEnded() { | 252 // public void playEnded() { |
204 narrator.speak(MessageFormat.format(resources.getString("speech.jump"),typeTreeNode.spokenText())); | 253 // narrator.speak(MessageFormat.format(resources.getString("speech.jump"),typeTreeNode.spokenText())); |
205 } | 254 // } |
206 }, SoundEvent.JUMP); | 255 // }); |
207 break; | 256 // break; |
208 case SELECTED_TYPE : | 257 case SELECTED_TYPE : |
209 DiagramModelTreeNode root = (DiagramModelTreeNode)getModel().getRoot(); | 258 DiagramTreeNode root = (DiagramTreeNode)getModel().getRoot(); |
210 Object[] types = new Object[root.getChildCount()]; | 259 Object[] types = new Object[root.getChildCount()]; |
211 for(int i=0; i< root.getChildCount();i++) | 260 for(int i=0; i< root.getChildCount();i++) |
212 types[i] = ((TypeMutableTreeNode)root.getChildAt(i)).getName();//not to spokenText as it would be too long | 261 types[i] = ((TypeMutableTreeNode)root.getChildAt(i)).getName();//not to spokenText as it would be too long |
213 oldPath = getSelectionPath(); | 262 oldPath = getSelectionPath(); |
214 /* initial value is the type node whose branch node is currently selected */ | 263 /* initial value is the type node whose branch node is currently selected */ |
240 break; | 289 break; |
241 } | 290 } |
242 } | 291 } |
243 setSelectionPath(new TreePath(typeNode.getPath())); | 292 setSelectionPath(new TreePath(typeNode.getPath())); |
244 if(oldPath.getPath().length >= 2) | 293 if(oldPath.getPath().length >= 2) |
245 collapseAll((DiagramModelTreeNode)oldPath.getLastPathComponent(), (DiagramModelTreeNode)initialValue); | 294 collapseAll((DiagramTreeNode)oldPath.getLastPathComponent(), (DiagramTreeNode)initialValue); |
246 SoundFactory.getInstance().setPlayerListener(new PlayerListener(){ | 295 SoundFactory.getInstance().play(SoundEvent.JUMP, new PlayerListener(){ |
247 @Override | 296 @Override |
248 public void playEnded() { | 297 public void playEnded() { |
249 narrator.speak(MessageFormat.format(resources.getString("speech.jump"),selectedValue)); | 298 narrator.speak(MessageFormat.format(resources.getString("speech.jump"),selectedValue)); |
250 } | 299 } |
251 }, SoundEvent.JUMP); | 300 }); |
252 break; | 301 break; |
253 case BOOKMARK : | 302 case BOOKMARK : |
254 TreeModel<Node,Edge> treeModel = getModel(); | 303 TreeModel<Node,Edge> treeModel = getModel(); |
255 | 304 |
256 if(treeModel.getBookmarks().size() == 0){ | 305 if(treeModel.getBookmarks().size() == 0){ |
275 bookmarkArray[0] | 324 bookmarkArray[0] |
276 ); | 325 ); |
277 | 326 |
278 if(bookmark != null){ | 327 if(bookmark != null){ |
279 oldPath = getSelectionPath(); | 328 oldPath = getSelectionPath(); |
280 DiagramModelTreeNode treeNode = treeModel.getBookmarkedTreeNode(bookmark); | 329 DiagramTreeNode treeNode = treeModel.getBookmarkedTreeNode(bookmark); |
281 collapseAll((DiagramModelTreeNode)oldPath.getLastPathComponent(), (DiagramModelTreeNode)treeModel.getRoot()); | 330 collapseAll((DiagramTreeNode)oldPath.getLastPathComponent(), (DiagramTreeNode)treeModel.getRoot()); |
282 setSelectionPath(new TreePath(treeNode.getPath())); | 331 setSelectionPath(new TreePath(treeNode.getPath())); |
283 final String currentPathSpeech = currentPathSpeech(); | 332 final String currentPathSpeech = currentPathSpeech(); |
284 SoundFactory.getInstance().setPlayerListener(new PlayerListener(){ | 333 SoundFactory.getInstance().play(SoundEvent.JUMP, new PlayerListener(){ |
285 @Override | 334 @Override |
286 public void playEnded() { | 335 public void playEnded() { |
287 narrator.speak(currentPathSpeech); | 336 narrator.speak(currentPathSpeech); |
288 } | 337 } |
289 }, SoundEvent.JUMP); | 338 }); |
290 InteractionLog.log(INTERACTIONLOG_SOURCE,"bookmark selected",bookmark); | 339 InteractionLog.log(INTERACTIONLOG_SOURCE,"bookmark selected",bookmark); |
291 }else{ | 340 }else{ |
292 /* it speaks anyway, as we set up the speech in the EditorFrame class. no need to use the narrator then */ | 341 /* it speaks anyway, as we set up the speech in the EditorFrame class. no need to use the narrator then */ |
293 SoundFactory.getInstance().play(SoundEvent.CANCEL); | 342 SoundFactory.getInstance().play(SoundEvent.CANCEL); |
294 InteractionLog.log(INTERACTIONLOG_SOURCE,"cancel select bookmark dialog",""); | 343 InteractionLog.log(INTERACTIONLOG_SOURCE,"cancel select bookmark dialog",""); |
295 return; | 344 return; |
296 } | 345 } |
297 break; | 346 break; |
298 | 347 |
299 } | 348 } |
300 InteractionLog.log(INTERACTIONLOG_SOURCE,"jumped to "+jumpTo.toString(),((DiagramModelTreeNode)getSelectionPath().getLastPathComponent()).getName()); | 349 InteractionLog.log(INTERACTIONLOG_SOURCE,"jumped to "+jumpTo.toString(),((DiagramTreeNode)getSelectionPath().getLastPathComponent()).getName()); |
301 SoundFactory.getInstance().play(SoundEvent.JUMP); | 350 } |
302 } | 351 |
303 | 352 /** |
353 * Changes the selected tree path from the current to the one from the root | |
354 * to the {@code Diagramelement} passed as argument. Note that a {@code Diagramelement} | |
355 * is also an instance of {@code DuagramTreeNode} and it's placed in a {@code TreeModel} | |
356 * when it's inserted into a {@code DiagramModel} | |
357 * | |
358 * @param de the diagram element to be selected on the tree | |
359 */ | |
304 public void jumpTo(final DiagramElement de){ | 360 public void jumpTo(final DiagramElement de){ |
305 TreePath oldPath = getSelectionPath(); | 361 TreePath oldPath = getSelectionPath(); |
306 collapseAll((DiagramModelTreeNode)oldPath.getLastPathComponent(),de); | 362 collapseAll((DiagramTreeNode)oldPath.getLastPathComponent(),de); |
307 setSelectionPath(new TreePath(de.getPath())); | 363 setSelectionPath(new TreePath(de.getPath())); |
308 SoundFactory.getInstance().play( SoundEvent.JUMP, new PlayerListener(){ | 364 SoundFactory.getInstance().play( SoundEvent.JUMP, new PlayerListener(){ |
309 @Override | 365 @Override |
310 public void playEnded() { | 366 public void playEnded() { |
311 NarratorFactory.getInstance().speak(MessageFormat.format(resources.getString("speech.jump"),de.spokenText())); | 367 NarratorFactory.getInstance().speak(MessageFormat.format(resources.getString("speech.jump"),de.spokenText())); |
312 } | 368 } |
313 }); | 369 }); |
314 } | 370 } |
315 | 371 |
316 /* collapse all the nodes in the path from "from" to "to" upwards(with the same direction as going from a leaf to the root)*/ | 372 /* collapse all the nodes in the path from "from" to "to" upwards(with the same direction as going from a leaf to the root)*/ |
317 private void collapseAll(DiagramModelTreeNode from, DiagramModelTreeNode to){ | 373 private void collapseAll(DiagramTreeNode from, DiagramTreeNode to){ |
318 DiagramModelTreeNode currentNode = from; | 374 DiagramTreeNode currentNode = from; |
319 while(currentNode.getParent() != null && currentNode != to){ | 375 while(currentNode.getParent() != null && currentNode != to){ |
320 currentNode = currentNode.getParent(); | 376 currentNode = currentNode.getParent(); |
321 collapsePath(new TreePath(currentNode.getPath())); | 377 collapsePath(new TreePath(currentNode.getPath())); |
322 } | 378 } |
323 } | 379 } |
324 | 380 |
381 /** | |
382 * Mouse events are ignored by this tree. This is just a blank method. | |
383 * | |
384 * @param e a mouse event | |
385 */ | |
325 @Override | 386 @Override |
326 protected void processMouseEvent(MouseEvent e){ | 387 protected void processMouseEvent(MouseEvent e){ |
327 //do nothing as the tree does not have to be editable with mouse | 388 //do nothing as the tree does not have to be editable with mouse |
328 } | 389 } |
329 | 390 |
391 /** | |
392 * Allows only cursor keys, tab key, delete, and actions (CTRL+something) | |
393 * | |
394 * @param e a key event | |
395 * | |
396 */ | |
330 @Override | 397 @Override |
331 protected void processKeyEvent(KeyEvent e){ | 398 protected void processKeyEvent(KeyEvent e){ |
332 /* allow only cursor keys, tab key, delete, and actions (CTRL+something) */ | 399 /* allow only cursor keys, tab key, delete, and actions (CTRL+something) */ |
333 if(e.getKeyChar() == KeyEvent.CHAR_UNDEFINED | 400 if(e.getKeyChar() == KeyEvent.CHAR_UNDEFINED |
334 || e.getKeyCode() == KeyEvent.VK_TAB | 401 || e.getKeyCode() == KeyEvent.VK_TAB |
335 || e.getKeyCode() == KeyEvent.VK_SPACE | 402 || e.getKeyCode() == KeyEvent.VK_SPACE |
336 || e.getKeyCode() == KeyEvent.VK_DELETE | |
337 || e.isControlDown() | 403 || e.isControlDown() |
338 || e.isAltDown()) | 404 || e.isAltDown()) |
339 super.processKeyEvent(e); | 405 super.processKeyEvent(e); |
340 } | 406 } |
341 | 407 |
346 /* Overwrite keystrokes up,down,left,right arrows and space, shift, ctrl */ | 412 /* Overwrite keystrokes up,down,left,right arrows and space, shift, ctrl */ |
347 getInputMap().put(KeyStroke.getKeyStroke(KeyEvent.VK_DOWN,0),"down"); | 413 getInputMap().put(KeyStroke.getKeyStroke(KeyEvent.VK_DOWN,0),"down"); |
348 getActionMap().put("down", new AbstractAction(){ | 414 getActionMap().put("down", new AbstractAction(){ |
349 @Override | 415 @Override |
350 public void actionPerformed(ActionEvent evt) { | 416 public void actionPerformed(ActionEvent evt) { |
351 DiagramModelTreeNode treeNode = (DiagramModelTreeNode)getLastSelectedPathComponent(); | 417 DiagramTreeNode treeNode = (DiagramTreeNode)getLastSelectedPathComponent(); |
352 /* look if we've got a sibling node after (we are not at the bottom) */ | 418 /* look if we've got a sibling node after (we are not at the bottom) */ |
353 DiagramModelTreeNode nextTreeNode = treeNode.getNextSibling(); | 419 DiagramTreeNode nextTreeNode = treeNode.getNextSibling(); |
354 SoundEvent loop = null; | 420 SoundEvent loop = null; |
355 if(nextTreeNode == null){ | 421 if(nextTreeNode == null){ |
356 DiagramModelTreeNode parent = treeNode.getParent(); | 422 DiagramTreeNode parent = treeNode.getParent(); |
357 if(parent == null) /* root node, just stay there */ | 423 if(parent == null) /* root node, just stay there */ |
358 nextTreeNode = treeNode; | 424 nextTreeNode = treeNode; |
359 else /* loop = go to first child of own parent */ | 425 else /* loop = go to first child of own parent */ |
360 nextTreeNode = (DiagramModelTreeNode)parent.getFirstChild(); | 426 nextTreeNode = (DiagramTreeNode)parent.getFirstChild(); |
361 loop = SoundEvent.LIST_BOTTOM_REACHED; | 427 loop = SoundEvent.LIST_BOTTOM_REACHED; |
362 } | 428 } |
363 setSelectionPath(new TreePath(nextTreeNode.getPath())); | 429 setSelectionPath(new TreePath(nextTreeNode.getPath())); |
364 final InputStream finalSound = getTreeNodeSound(nextTreeNode); | 430 final InputStream finalSound = getTreeNodeSound(nextTreeNode); |
365 final String currentPathSpeech = currentPathSpeech(); | 431 final String currentPathSpeech = currentPathSpeech(); |
374 | 440 |
375 getInputMap().put(KeyStroke.getKeyStroke(KeyEvent.VK_UP,0),"up"); | 441 getInputMap().put(KeyStroke.getKeyStroke(KeyEvent.VK_UP,0),"up"); |
376 getActionMap().put("up", new AbstractAction(){ | 442 getActionMap().put("up", new AbstractAction(){ |
377 @Override | 443 @Override |
378 public void actionPerformed(ActionEvent evt) { | 444 public void actionPerformed(ActionEvent evt) { |
379 DiagramModelTreeNode treeNode = (DiagramModelTreeNode)getLastSelectedPathComponent(); | 445 DiagramTreeNode treeNode = (DiagramTreeNode)getLastSelectedPathComponent(); |
380 DiagramModelTreeNode previousTreeNode = treeNode.getPreviousSibling(); | 446 DiagramTreeNode previousTreeNode = treeNode.getPreviousSibling(); |
381 SoundEvent loop = null; | 447 SoundEvent loop = null; |
382 if(previousTreeNode == null){ | 448 if(previousTreeNode == null){ |
383 DiagramModelTreeNode parent = treeNode.getParent(); | 449 DiagramTreeNode parent = treeNode.getParent(); |
384 if(parent == null) /* root node */ | 450 if(parent == null) /* root node */ |
385 previousTreeNode = treeNode; | 451 previousTreeNode = treeNode; |
386 else | 452 else |
387 previousTreeNode = (DiagramModelTreeNode)parent.getLastChild(); | 453 previousTreeNode = (DiagramTreeNode)parent.getLastChild(); |
388 loop = SoundEvent.LIST_TOP_REACHED; | 454 loop = SoundEvent.LIST_TOP_REACHED; |
389 } | 455 } |
390 setSelectionPath(new TreePath(previousTreeNode.getPath())); | 456 setSelectionPath(new TreePath(previousTreeNode.getPath())); |
391 final InputStream finalSound = getTreeNodeSound(previousTreeNode); | 457 final InputStream finalSound = getTreeNodeSound(previousTreeNode); |
392 final String currentPathSpeech = currentPathSpeech(); | 458 final String currentPathSpeech = currentPathSpeech(); |
402 getInputMap().put(KeyStroke.getKeyStroke(KeyEvent.VK_RIGHT,0),"right"); | 468 getInputMap().put(KeyStroke.getKeyStroke(KeyEvent.VK_RIGHT,0),"right"); |
403 getActionMap().put("right", new AbstractAction(){ | 469 getActionMap().put("right", new AbstractAction(){ |
404 @Override | 470 @Override |
405 public void actionPerformed(ActionEvent evt) { | 471 public void actionPerformed(ActionEvent evt) { |
406 TreePath path = getSelectionPath(); | 472 TreePath path = getSelectionPath(); |
407 DiagramModelTreeNode treeNode = (DiagramModelTreeNode)path.getLastPathComponent(); | 473 DiagramTreeNode treeNode = (DiagramTreeNode)path.getLastPathComponent(); |
408 if(treeNode.isLeaf()){ | 474 if(treeNode.isLeaf()){ |
409 notifyBorderReached(treeNode); | 475 notifyBorderReached(treeNode); |
410 InteractionLog.log(INTERACTIONLOG_SOURCE,"move right","border reached"); | 476 InteractionLog.log(INTERACTIONLOG_SOURCE,"move right","border reached"); |
411 } | 477 } |
412 else{ | 478 else{ |
413 expandPath(path); | 479 expandPath(path); |
414 setSelectionPath(new TreePath(((DiagramModelTreeNode)treeNode.getFirstChild()).getPath())); | 480 setSelectionPath(new TreePath(((DiagramTreeNode)treeNode.getFirstChild()).getPath())); |
415 final String currentPathSpeech = currentPathSpeech(); | 481 final String currentPathSpeech = currentPathSpeech(); |
416 SoundFactory.getInstance().play(SoundEvent.TREE_NODE_EXPAND,new PlayerListener(){ | 482 SoundFactory.getInstance().play(SoundEvent.TREE_NODE_EXPAND,new PlayerListener(){ |
417 @Override | 483 @Override |
418 public void playEnded() { | 484 public void playEnded() { |
419 NarratorFactory.getInstance().speak(currentPathSpeech); | 485 NarratorFactory.getInstance().speak(currentPathSpeech); |
420 } | 486 } |
421 }); | 487 }); |
422 InteractionLog.log(INTERACTIONLOG_SOURCE,"move right",((DiagramModelTreeNode)treeNode.getFirstChild()).toString()); | 488 InteractionLog.log(INTERACTIONLOG_SOURCE,"move right",((DiagramTreeNode)treeNode.getFirstChild()).toString()); |
423 } | 489 } |
424 } | 490 } |
425 }); | 491 }); |
426 | 492 |
427 getInputMap().put(KeyStroke.getKeyStroke(KeyEvent.VK_LEFT,0),"left"); | 493 getInputMap().put(KeyStroke.getKeyStroke(KeyEvent.VK_LEFT,0),"left"); |
428 getActionMap().put("left", new AbstractAction(){ | 494 getActionMap().put("left", new AbstractAction(){ |
429 @Override | 495 @Override |
430 public void actionPerformed(ActionEvent evt) { | 496 public void actionPerformed(ActionEvent evt) { |
431 TreePath path = getSelectionPath(); | 497 TreePath path = getSelectionPath(); |
432 DiagramModelTreeNode treeNode = (DiagramModelTreeNode)path.getLastPathComponent(); | 498 DiagramTreeNode treeNode = (DiagramTreeNode)path.getLastPathComponent(); |
433 DiagramModelTreeNode parent = treeNode.getParent(); | 499 DiagramTreeNode parent = treeNode.getParent(); |
434 if(parent == null){/* root node */ | 500 if(parent == null){/* root node */ |
435 notifyBorderReached(treeNode); | 501 notifyBorderReached(treeNode); |
436 InteractionLog.log(INTERACTIONLOG_SOURCE,"move left","border reached"); | 502 InteractionLog.log(INTERACTIONLOG_SOURCE,"move left","border reached"); |
437 } | 503 } |
438 else{ | 504 else{ |
439 TreePath newPath = new TreePath(((DiagramModelTreeNode)parent).getPath()); | 505 TreePath newPath = new TreePath(((DiagramTreeNode)parent).getPath()); |
440 setSelectionPath(newPath); | 506 setSelectionPath(newPath); |
441 collapsePath(newPath); | 507 collapsePath(newPath); |
442 final String currentPathSpeech = currentPathSpeech(); | 508 final String currentPathSpeech = currentPathSpeech(); |
443 SoundFactory.getInstance().play(SoundEvent.TREE_NODE_COLLAPSE,new PlayerListener(){ | 509 SoundFactory.getInstance().play(SoundEvent.TREE_NODE_COLLAPSE,new PlayerListener(){ |
444 @Override | 510 @Override |
445 public void playEnded() { | 511 public void playEnded() { |
446 NarratorFactory.getInstance().speak(currentPathSpeech); | 512 NarratorFactory.getInstance().speak(currentPathSpeech); |
447 } | 513 } |
448 }); | 514 }); |
449 InteractionLog.log(INTERACTIONLOG_SOURCE,"move left",((DiagramModelTreeNode)parent).toString()); | 515 InteractionLog.log(INTERACTIONLOG_SOURCE,"move left",((DiagramTreeNode)parent).toString()); |
450 } | 516 } |
451 } | 517 } |
452 }); | 518 }); |
453 | 519 |
454 getInputMap().put(KeyStroke.getKeyStroke(KeyEvent.VK_SPACE,0),"space"); | 520 getInputMap().put(KeyStroke.getKeyStroke(KeyEvent.VK_SPACE,0),"space"); |
472 * builder.append(", "); | 538 * builder.append(", "); |
473 * } | 539 * } |
474 * Narrator.getInstance().speak(builder.toString(), null); | 540 * Narrator.getInstance().speak(builder.toString(), null); |
475 */ | 541 */ |
476 TreePath path = getSelectionPath(); | 542 TreePath path = getSelectionPath(); |
477 DiagramModelTreeNode treeNode = (DiagramModelTreeNode)path.getLastPathComponent(); | 543 DiagramTreeNode treeNode = (DiagramTreeNode)path.getLastPathComponent(); |
478 NarratorFactory.getInstance().speak(treeNode.detailedSpokenText()); | 544 NarratorFactory.getInstance().speak(treeNode.detailedSpokenText()); |
479 InteractionLog.log(INTERACTIONLOG_SOURCE,"detailed info requested",""); | 545 InteractionLog.log(INTERACTIONLOG_SOURCE,"detailed info requested",""); |
480 } | 546 } |
481 }); | 547 }); |
482 | 548 |
484 getActionMap().put("shift",new AbstractAction(){ | 550 getActionMap().put("shift",new AbstractAction(){ |
485 @Override | 551 @Override |
486 public void actionPerformed(ActionEvent evt) { | 552 public void actionPerformed(ActionEvent evt) { |
487 if(getSelectionPath().getLastPathComponent() instanceof Node){ | 553 if(getSelectionPath().getLastPathComponent() instanceof Node){ |
488 Node node = (Node)getSelectionPath().getLastPathComponent(); | 554 Node node = (Node)getSelectionPath().getLastPathComponent(); |
489 | |
490 | |
491 if(selectedNodes.contains(node)){ | 555 if(selectedNodes.contains(node)){ |
492 unselectNode(node); | 556 unselectNode(node); |
493 diagram.getModelUpdater().yieldLock(node, Lock.MUST_EXIST); | 557 diagram.getModelUpdater().yieldLock(node, Lock.MUST_EXIST,new DiagramEventActionSource(DiagramEventSource.TREE,Command.Name.UNSELECT_NODE_FOR_EDGE_CREATION,node.getId(),node.getName())); |
494 } | 558 }else{ |
495 else{ | 559 if(!diagram.getModelUpdater().getLock(node, Lock.MUST_EXIST,new DiagramEventActionSource(DiagramEventSource.TREE,Command.Name.SELECT_NODE_FOR_EDGE_CREATION,node.getId(),node.getName()))){ |
496 if(!diagram.getModelUpdater().getLock(node, Lock.MUST_EXIST)){ | |
497 InteractionLog.log(INTERACTIONLOG_SOURCE,"Could not get lock on node fro edge creation selection",DiagramElement.toLogString(node)); | 560 InteractionLog.log(INTERACTIONLOG_SOURCE,"Could not get lock on node fro edge creation selection",DiagramElement.toLogString(node)); |
498 SpeechOptionPane.showMessageDialog( | 561 SpeechOptionPane.showMessageDialog( |
499 SpeechOptionPane.getFrameForComponent(DiagramTree.this), | 562 SpeechOptionPane.getFrameForComponent(DiagramTree.this), |
500 resources.getString("dialog.lock_failure.must_exist"), | 563 resources.getString("dialog.lock_failure.must_exist"), |
501 SpeechOptionPane.INFORMATION_MESSAGE); | 564 SpeechOptionPane.INFORMATION_MESSAGE); |
519 /* make the tree ignore the page up and page down keys */ | 582 /* make the tree ignore the page up and page down keys */ |
520 getInputMap().put(KeyStroke.getKeyStroke(KeyEvent.VK_PAGE_UP,0),"none"); | 583 getInputMap().put(KeyStroke.getKeyStroke(KeyEvent.VK_PAGE_UP,0),"none"); |
521 getInputMap().put(KeyStroke.getKeyStroke(KeyEvent.VK_PAGE_DOWN,0),"none"); | 584 getInputMap().put(KeyStroke.getKeyStroke(KeyEvent.VK_PAGE_DOWN,0),"none"); |
522 } | 585 } |
523 | 586 |
524 private static InputStream getTreeNodeSound(DiagramModelTreeNode node){ | 587 private static InputStream getTreeNodeSound(DiagramTreeNode node){ |
525 InputStream sound = null; | 588 InputStream sound = null; |
526 TreeNode[] newPath = node.getPath(); | 589 TreeNode[] newPath = node.getPath(); |
527 if(!node.isRoot()){ | 590 if(!node.isRoot()){ |
528 if(node.getChildCount() > 0){ // check whether it's the folder containing Node/Edge references | 591 if(node.getChildCount() > 0){ // check whether it's the folder containing Node/Edge references |
529 if(node.getChildAt(0) instanceof EdgeReferenceMutableTreeNode){ | 592 if(node.getChildAt(0) instanceof EdgeReferenceMutableTreeNode){ |
548 public void setSelectionPath(TreePath path){ | 611 public void setSelectionPath(TreePath path){ |
549 super.setSelectionPath(path); | 612 super.setSelectionPath(path); |
550 scrollPathToVisible(path); | 613 scrollPathToVisible(path); |
551 } | 614 } |
552 | 615 |
553 private void notifyBorderReached(DiagramModelTreeNode n) { | 616 private void notifyBorderReached(DiagramTreeNode n) { |
554 SoundFactory.getInstance().play(SoundEvent.ERROR); | 617 SoundFactory.getInstance().play(SoundEvent.ERROR); |
555 } | 618 } |
556 | 619 |
557 @Override | 620 @Override |
558 public String convertValueToText(Object value, | 621 public String convertValueToText(Object value, |
580 private Diagram diagram; | 643 private Diagram diagram; |
581 private ResourceBundle resources; | 644 private ResourceBundle resources; |
582 private static final char SELECTED_NODE_MARK_BEGIN = '<'; | 645 private static final char SELECTED_NODE_MARK_BEGIN = '<'; |
583 private static final char SELECTED_NODE_MARK_END = '>'; | 646 private static final char SELECTED_NODE_MARK_END = '>'; |
584 private static final String INTERACTIONLOG_SOURCE = "TREE"; | 647 private static final String INTERACTIONLOG_SOURCE = "TREE"; |
585 static enum JumpTo {REFERENCE, ROOT, TYPE, SELECTED_TYPE, BOOKMARK} | 648 /** |
649 * A list of possible destination for a jump (a change of the selected path without | |
650 * using the navigation arrow keys) | |
651 */ | |
652 public static enum JumpTo { | |
653 /** | |
654 * if the current selection is a edge/node reference tree node, the jump destination | |
655 * is the referee tree node (see {@link uk.ac.qmul.eecs.ccmi.diagrammodel.NodeReferenceMutableTreeNode} and | |
656 * {@link uk.ac.qmul.eecs.ccmi.diagrammodel.EdgeReferenceMutableTreeNode }) | |
657 */ | |
658 REFERENCE, | |
659 /** | |
660 * the destination is the root of the diagram | |
661 */ | |
662 ROOT, | |
663 /** | |
664 * the destination will be a node or edge type selected | |
665 * (via a selection dialog) by the user | |
666 */ | |
667 SELECTED_TYPE, | |
668 /** | |
669 * the destination will be a bookmark selected (via a selection dialog) by the user | |
670 */ | |
671 BOOKMARK} | |
586 | 672 |
587 /* the methods of the TreeModelHandler are overwritten in order to provide a consistent way | 673 /* the methods of the TreeModelHandler are overwritten in order to provide a consistent way |
588 * of updating the tree selection upon tree change. Bear in mind that the tree can possibly be changed | 674 * of updating the tree selection upon tree change. Bear in mind that the tree can possibly be changed |
589 * by another peer on a network, and therefore not only as a response to a user's action. | 675 * by another peer on a network, and therefore not only as a response to a user's action. |
590 * The algorithm works as follows (being A the tree node selected before any handler method M being called): | 676 * The algorithm works as follows (being A the tree node selected before any handler method M being called): |
612 Object[] pathArray = getSelectionPath().getPath(); | 698 Object[] pathArray = getSelectionPath().getPath(); |
613 DefaultMutableTreeNode root = (DefaultMutableTreeNode)getModel().getRoot(); | 699 DefaultMutableTreeNode root = (DefaultMutableTreeNode)getModel().getRoot(); |
614 /* go along the path from the selected node to the root looking for a node * | 700 /* go along the path from the selected node to the root looking for a node * |
615 * attached to the tree or with sibling nodes attached to the tree */ | 701 * attached to the tree or with sibling nodes attached to the tree */ |
616 for(int i=pathArray.length-1;i>=0;i--){ | 702 for(int i=pathArray.length-1;i>=0;i--){ |
617 DiagramModelTreeNode onPathTreeNode = (DiagramModelTreeNode)pathArray[i]; | 703 DiagramTreeNode onPathTreeNode = (DiagramTreeNode)pathArray[i]; |
618 if(onPathTreeNode.isNodeRelated(root)){// if can reach the root from here a.k.a. the node is still part of the tree | 704 if(onPathTreeNode.isNodeRelated(root)){// if can reach the root from here a.k.a. the node is still part of the tree |
619 super.treeStructureChanged(e); | 705 super.treeStructureChanged(e); |
620 setSelectionPath(new TreePath(onPathTreeNode.getPath())); | 706 setSelectionPath(new TreePath(onPathTreeNode.getPath())); |
621 break; | 707 break; |
622 }else{ | 708 }else{ |
623 /* check sibling nodes*/ | 709 /* check sibling nodes*/ |
624 DefaultMutableTreeNode parent = (DiagramModelTreeNode)pathArray[i-1]; | 710 DefaultMutableTreeNode parent = (DiagramTreeNode)pathArray[i-1]; |
625 if(parent.isNodeRelated(root) && parent.getChildCount() > 0){ | 711 if(parent.isNodeRelated(root) && parent.getChildCount() > 0){ |
626 super.treeStructureChanged(e); | 712 super.treeStructureChanged(e); |
627 setSelectionPath(new TreePath(((DefaultMutableTreeNode)parent.getLastChild()).getPath())); | 713 setSelectionPath(new TreePath(((DefaultMutableTreeNode)parent.getLastChild()).getPath())); |
628 break; | 714 break; |
629 } | 715 } |
643 | 729 |
644 @Override | 730 @Override |
645 public void treeNodesRemoved(final TreeModelEvent e){ | 731 public void treeNodesRemoved(final TreeModelEvent e){ |
646 /* check first if what we're removing is in the selecton path */ | 732 /* check first if what we're removing is in the selecton path */ |
647 TreePath path = e.getTreePath(); | 733 TreePath path = e.getTreePath(); |
648 DiagramModelTreeNode removedTreeNode = (DiagramModelTreeNode)e.getChildren()[0]; | 734 DiagramTreeNode removedTreeNode = (DiagramTreeNode)e.getChildren()[0]; |
649 boolean isInSelectionPath = false; | 735 boolean isInSelectionPath = false; |
650 for(Object t : getSelectionPath().getPath()){ | 736 for(Object t : getSelectionPath().getPath()){ |
651 if(removedTreeNode == t){ | 737 if(removedTreeNode == t){ |
652 isInSelectionPath = true; | 738 isInSelectionPath = true; |
653 break; | 739 break; |
654 } | 740 } |
655 } | 741 } |
656 DiagramModelTreeNode parentTreeNode = (DiagramModelTreeNode)path.getLastPathComponent(); | 742 DiagramTreeNode parentTreeNode = (DiagramTreeNode)path.getLastPathComponent(); |
657 /* update the selection only if the tree node involved is in the selection path * | 743 /* update the selection only if the tree node involved is in the selection path * |
658 * this always holds true for tree nodes deleted from the tree */ | 744 * this always holds true for tree nodes deleted from the tree */ |
659 if(isInSelectionPath){ | 745 if(isInSelectionPath){ |
660 if(e.getSource() instanceof TreeModel){ | 746 if(e.getSource() instanceof TreeModel){ |
661 /* update the path only if the node has been removed from the tree or * | 747 /* update the path only if the node has been removed from the tree or * |
668 /* if we deleted from another source, then select the first non null node in the path * | 754 /* if we deleted from another source, then select the first non null node in the path * |
669 * including the deleted node. E.g. if we're deleting the first child of a parent * | 755 * including the deleted node. E.g. if we're deleting the first child of a parent * |
670 * and the node has siblings than the new first sibling will be selected */ | 756 * and the node has siblings than the new first sibling will be selected */ |
671 int limitForParentDeletion = (parentTreeNode instanceof Edge) ? 1 : 0; // an edge with one node is to be deleted | 757 int limitForParentDeletion = (parentTreeNode instanceof Edge) ? 1 : 0; // an edge with one node is to be deleted |
672 if(parentTreeNode.getChildCount() > limitForParentDeletion){ | 758 if(parentTreeNode.getChildCount() > limitForParentDeletion){ |
673 setSelectionPath(new TreePath(((DiagramModelTreeNode)parentTreeNode.getChildAt( | 759 setSelectionPath(new TreePath(((DiagramTreeNode)parentTreeNode.getChildAt( |
674 /* select the n-th sibling node (see algorithm description above or the highest index sibling node */ | 760 /* select the n-th sibling node (see algorithm description above or the highest index sibling node */ |
675 Math.min(e.getChildIndices()[0],parentTreeNode.getChildCount()-1) | 761 Math.min(e.getChildIndices()[0],parentTreeNode.getChildCount()-1) |
676 )).getPath())); | 762 )).getPath())); |
677 }else{ | 763 }else{ |
678 /* the deleted node had no siblings, thus select the node checking from the parent up in the path to the first still existing node */ | 764 /* the deleted node had no siblings, thus select the node checking from the parent up in the path to the first still existing node */ |
679 Object[] pathArray = path.getPath(); | 765 Object[] pathArray = path.getPath(); |
680 for(int i=path.getPathCount()-1;i>=0;i--){ | 766 for(int i=path.getPathCount()-1;i>=0;i--){ |
681 DiagramModelTreeNode itr = (DiagramModelTreeNode)pathArray[i]; | 767 DiagramTreeNode itr = (DiagramTreeNode)pathArray[i]; |
682 if(itr.getPath()[0] == getModel().getRoot()){ | 768 if(itr.getPath()[0] == getModel().getRoot()){ |
683 TreePath newPath = new TreePath(itr.getPath()); | 769 TreePath newPath = new TreePath(itr.getPath()); |
684 setSelectionPath(newPath); | 770 setSelectionPath(newPath); |
685 collapsePath(newPath); | 771 collapsePath(newPath); |
686 break; | 772 break; |
690 } | 776 } |
691 }else | 777 }else |
692 super.treeNodesRemoved(e); | 778 super.treeNodesRemoved(e); |
693 | 779 |
694 /* if the node was selected for edge creation, then remove it from the list */ | 780 /* if the node was selected for edge creation, then remove it from the list */ |
695 DiagramModelTreeNode removedNode = (DiagramModelTreeNode)e.getChildren()[0]; | 781 DiagramTreeNode removedNode = (DiagramTreeNode)e.getChildren()[0]; |
696 selectedNodes.remove(removedNode); | 782 selectedNodes.remove(removedNode); |
697 } | 783 } |
698 } | 784 } |
699 } | 785 } |