f@0
|
1 /*
|
f@0
|
2 CCmI Editor - A Collaborative Cross-Modal Diagram Editing Tool
|
f@0
|
3
|
f@0
|
4 Copyright (C) 2011 Queen Mary University of London (http://ccmi.eecs.qmul.ac.uk/)
|
f@0
|
5
|
f@0
|
6 This program is free software: you can redistribute it and/or modify
|
f@0
|
7 it under the terms of the GNU General Public License as published by
|
f@0
|
8 the Free Software Foundation, either version 3 of the License, or
|
f@0
|
9 (at your option) any later version.
|
f@0
|
10
|
f@0
|
11 This program is distributed in the hope that it will be useful,
|
f@0
|
12 but WITHOUT ANY WARRANTY; without even the implied warranty of
|
f@0
|
13 MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
f@0
|
14 GNU General Public License for more details.
|
f@0
|
15
|
f@0
|
16 You should have received a copy of the GNU General Public License
|
f@0
|
17 along with this program. If not, see <http://www.gnu.org/licenses/>.
|
f@0
|
18 */
|
f@0
|
19
|
f@0
|
20 package uk.ac.qmul.eecs.ccmi.gui.filechooser;
|
f@0
|
21
|
f@0
|
22 import java.awt.Component;
|
f@0
|
23 import java.awt.event.ActionEvent;
|
f@0
|
24 import java.awt.event.InputEvent;
|
f@0
|
25 import java.awt.event.KeyEvent;
|
f@0
|
26 import java.awt.event.MouseEvent;
|
f@0
|
27 import java.io.File;
|
f@0
|
28 import java.io.IOException;
|
f@0
|
29 import java.util.LinkedList;
|
f@0
|
30 import java.util.ResourceBundle;
|
f@0
|
31
|
f@0
|
32 import javax.swing.AbstractAction;
|
f@0
|
33 import javax.swing.JTree;
|
f@0
|
34 import javax.swing.KeyStroke;
|
f@0
|
35 import javax.swing.event.TreeSelectionEvent;
|
f@0
|
36 import javax.swing.event.TreeSelectionListener;
|
f@0
|
37 import javax.swing.filechooser.FileFilter;
|
f@0
|
38 import javax.swing.tree.DefaultTreeModel;
|
f@0
|
39 import javax.swing.tree.TreeCellRenderer;
|
f@0
|
40 import javax.swing.tree.TreeNode;
|
f@0
|
41 import javax.swing.tree.TreePath;
|
f@0
|
42 import javax.swing.tree.TreeSelectionModel;
|
f@0
|
43
|
f@0
|
44 import uk.ac.qmul.eecs.ccmi.sound.PlayerListener;
|
f@0
|
45 import uk.ac.qmul.eecs.ccmi.sound.SoundEvent;
|
f@0
|
46 import uk.ac.qmul.eecs.ccmi.sound.SoundFactory;
|
f@0
|
47 import uk.ac.qmul.eecs.ccmi.speech.NarratorFactory;
|
f@0
|
48
|
f@0
|
49 /*
|
f@0
|
50 *
|
f@0
|
51 * A JTree displaying the content for the local file system.
|
f@0
|
52 *
|
f@0
|
53 *
|
f@0
|
54 */
|
f@0
|
55 @SuppressWarnings("serial")
|
f@0
|
56 class FileSystemTree extends JTree {
|
f@0
|
57 FileSystemTree(FileFilter filter){
|
f@0
|
58 super(new DefaultTreeModel(FileSystemTreeNode.getRootNode(filter)));
|
f@0
|
59 getAccessibleContext().setAccessibleName(ResourceBundle.getBundle(SpeechFileChooser.class.getName()).getString("tree.accessible_name"));
|
f@0
|
60 getSelectionModel().setSelectionMode(TreeSelectionModel.SINGLE_TREE_SELECTION);
|
f@0
|
61 setCellRenderer(new FileSystemTreeCellRendered(getCellRenderer()));
|
f@0
|
62
|
f@0
|
63 setSelectionPath(new TreePath(getModel().getRoot()));
|
f@0
|
64 overwriteTreeKeystrokes();
|
f@0
|
65 this.addTreeSelectionListener(new TreeSelectionListener(){
|
f@0
|
66 @Override
|
f@0
|
67 public void valueChanged(TreeSelectionEvent evt) {
|
f@0
|
68 if(treeSelectionListenerGateOpen){
|
f@0
|
69 FileSystemTreeNode treeNode = (FileSystemTreeNode)evt.getPath().getLastPathComponent();
|
f@0
|
70 NarratorFactory.getInstance().speak(treeNode.spokenText());
|
f@0
|
71 }
|
f@0
|
72 }
|
f@0
|
73 });
|
f@0
|
74 }
|
f@0
|
75
|
f@0
|
76 @Override
|
f@0
|
77 public void setSelectionPath(TreePath path){
|
f@0
|
78 super.setSelectionPath(path);
|
f@0
|
79 scrollPathToVisible(path);
|
f@0
|
80 getSelectionPath();
|
f@0
|
81 }
|
f@0
|
82
|
f@0
|
83 public void setSelectionPath(File file){
|
f@0
|
84 if(file == null)
|
f@0
|
85 return;
|
f@0
|
86
|
f@0
|
87 try {
|
f@0
|
88 file = file.getCanonicalFile();
|
f@0
|
89 } catch (IOException e) {
|
f@0
|
90 setSelectionPath(new TreePath(getModel().getRoot()));
|
f@0
|
91 return;
|
f@0
|
92 }
|
f@0
|
93 /* make a file path: a list of file's each one representing a directory of file's path */
|
f@0
|
94 LinkedList<File> filePath = new LinkedList<File>();
|
f@0
|
95 filePath.add(file);
|
f@0
|
96 File parent = file.getParentFile();
|
f@0
|
97 while(parent != null){
|
f@0
|
98 filePath.add(0, parent);
|
f@0
|
99 parent = parent.getParentFile();
|
f@0
|
100 }
|
f@0
|
101 /* make a TreePath out of the file path */
|
f@0
|
102 FileSystemTreeNode currentNode = (FileSystemTreeNode)getModel().getRoot();
|
f@0
|
103 TreePath treePath = new TreePath(currentNode);
|
f@0
|
104 for(File f : filePath){
|
f@0
|
105 boolean found = false;
|
f@0
|
106 for(int i=0;i<currentNode.getChildCount();i++){
|
f@0
|
107 if(currentNode.getChildAt(i).getFile().equals(f)){
|
f@0
|
108 currentNode = currentNode.getChildAt(i);
|
f@0
|
109 treePath = treePath.pathByAddingChild(currentNode);
|
f@0
|
110 found = true;
|
f@0
|
111 break;
|
f@0
|
112 }
|
f@0
|
113 }
|
f@0
|
114 if(!found)
|
f@0
|
115 break;
|
f@0
|
116 }
|
f@0
|
117 treeSelectionListenerGateOpen = false;
|
f@0
|
118 setSelectionPath(treePath);
|
f@0
|
119 treeSelectionListenerGateOpen = true;
|
f@0
|
120 }
|
f@0
|
121
|
f@0
|
122 public void applyFilter(FileFilter filter){
|
f@0
|
123 FileSystemTreeNode selectedNode = (FileSystemTreeNode)getSelectionPath().getLastPathComponent();
|
f@0
|
124 File file = selectedNode.getFile();
|
f@0
|
125 treeSelectionListenerGateOpen = false;
|
f@0
|
126 ((DefaultTreeModel)getModel()).setRoot(FileSystemTreeNode.getRootNode(filter));
|
f@0
|
127 treeSelectionListenerGateOpen = true;
|
f@0
|
128 if(file == null)
|
f@0
|
129 setSelectionPath(new TreePath(getModel().getRoot()));
|
f@0
|
130 else
|
f@0
|
131 setSelectionPath(file);
|
f@0
|
132 }
|
f@0
|
133
|
f@0
|
134 @Override
|
f@0
|
135 protected void processMouseEvent(MouseEvent e){
|
f@0
|
136 //do nothing as the tree does not have to be editable with mouse
|
f@0
|
137 }
|
f@0
|
138
|
f@0
|
139 private void overwriteTreeKeystrokes() {
|
f@0
|
140 /* overwrite the keys. up and down arrow are overwritten so that it loops when the top and the */
|
f@0
|
141 /* bottom are reached rather than getting stuck */
|
f@0
|
142
|
f@0
|
143 /* Overwrite keystrokes up,down,left,right arrows and space, shift, ctrl */
|
f@0
|
144 getInputMap().put(KeyStroke.getKeyStroke(KeyEvent.VK_DOWN,0),"down");
|
f@0
|
145 getActionMap().put("down", new AbstractAction(){
|
f@0
|
146 @Override
|
f@0
|
147 public void actionPerformed(ActionEvent evt) {
|
f@0
|
148 FileSystemTreeNode treeNode = (FileSystemTreeNode)getLastSelectedPathComponent();
|
f@0
|
149 /* look if we've got a sibling node after (we are not at the bottom) */
|
f@0
|
150 FileSystemTreeNode nextTreeNode = treeNode.getNextSibling();
|
f@0
|
151 SoundEvent loop = null;
|
f@0
|
152 if(nextTreeNode == null){
|
f@0
|
153 TreeNode parent = treeNode.getParent();
|
f@0
|
154 if(parent == null) /* root node, just stay there */
|
f@0
|
155 nextTreeNode = treeNode;
|
f@0
|
156 else /* loop = go to first child of own parent */
|
f@0
|
157 nextTreeNode = (FileSystemTreeNode)parent.getChildAt(0);
|
f@0
|
158 loop = SoundEvent.LIST_BOTTOM_REACHED;
|
f@0
|
159 }
|
f@0
|
160
|
f@0
|
161 final String speech = nextTreeNode.spokenText();
|
f@0
|
162 treeSelectionListenerGateOpen = false;
|
f@0
|
163 setSelectionPath(new TreePath(nextTreeNode.getPath()));
|
f@0
|
164 treeSelectionListenerGateOpen = true;
|
f@0
|
165 SoundFactory.getInstance().play(loop, new PlayerListener(){
|
f@0
|
166 public void playEnded() {
|
f@0
|
167 NarratorFactory.getInstance().speak(speech);
|
f@0
|
168 }
|
f@0
|
169 });
|
f@0
|
170 }});
|
f@0
|
171
|
f@0
|
172 /* Overwrite keystrokes up,down,left,right arrows and space, shift, ctrl */
|
f@0
|
173 getInputMap().put(KeyStroke.getKeyStroke(KeyEvent.VK_UP,0),"up");
|
f@0
|
174 getActionMap().put("up", new AbstractAction(){
|
f@0
|
175 @Override
|
f@0
|
176 public void actionPerformed(ActionEvent evt) {
|
f@0
|
177 FileSystemTreeNode treeNode = (FileSystemTreeNode)getLastSelectedPathComponent();
|
f@0
|
178 /* look if we've got a sibling node after (we are not at the bottom) */
|
f@0
|
179 FileSystemTreeNode peviousTreeNode = treeNode.getPreviousSibling();
|
f@0
|
180 SoundEvent loop = null;
|
f@0
|
181 if(peviousTreeNode == null){
|
f@0
|
182 TreeNode parent = treeNode.getParent();
|
f@0
|
183 if(parent == null) /* root node, just stay there */
|
f@0
|
184 peviousTreeNode = treeNode;
|
f@0
|
185 else /* loop = go to first child of own parent */
|
f@0
|
186 peviousTreeNode = (FileSystemTreeNode)parent.getChildAt(parent.getChildCount()-1);
|
f@0
|
187 loop = SoundEvent.LIST_TOP_REACHED;
|
f@0
|
188 }
|
f@0
|
189
|
f@0
|
190 final String speech = peviousTreeNode.spokenText();
|
f@0
|
191 treeSelectionListenerGateOpen = false;
|
f@0
|
192 setSelectionPath(new TreePath(peviousTreeNode.getPath()));
|
f@0
|
193 treeSelectionListenerGateOpen = true;
|
f@0
|
194 SoundFactory.getInstance().play(loop, new PlayerListener(){
|
f@0
|
195 public void playEnded() {
|
f@0
|
196 NarratorFactory.getInstance().speak(speech);
|
f@0
|
197 }
|
f@0
|
198 });
|
f@0
|
199 }});
|
f@0
|
200
|
f@0
|
201 getInputMap().put(KeyStroke.getKeyStroke(KeyEvent.VK_LEFT,0),"left");
|
f@0
|
202 getActionMap().put("left", new AbstractAction(){
|
f@0
|
203 @Override
|
f@0
|
204 public void actionPerformed(ActionEvent evt) {
|
f@0
|
205 TreePath path = getSelectionPath();
|
f@0
|
206 TreeNode treeNode = (TreeNode)path.getLastPathComponent();
|
f@0
|
207 final FileSystemTreeNode parent = (FileSystemTreeNode)treeNode.getParent();
|
f@0
|
208 if(parent == null){/* root node */
|
f@0
|
209 SoundFactory.getInstance().play(SoundEvent.ERROR);
|
f@0
|
210 }
|
f@0
|
211 else{
|
f@0
|
212 TreePath newPath = new TreePath(parent.getPath());
|
f@0
|
213 treeSelectionListenerGateOpen = false;
|
f@0
|
214 setSelectionPath(newPath);
|
f@0
|
215 collapsePath(newPath);
|
f@0
|
216 treeSelectionListenerGateOpen = true;
|
f@0
|
217 SoundFactory.getInstance().play(SoundEvent.TREE_NODE_COLLAPSE,new PlayerListener(){
|
f@0
|
218 @Override
|
f@0
|
219 public void playEnded() {
|
f@0
|
220 NarratorFactory.getInstance().speak(parent.spokenText());
|
f@0
|
221 }
|
f@0
|
222 });
|
f@0
|
223 }
|
f@0
|
224 }
|
f@0
|
225 });
|
f@0
|
226
|
f@0
|
227 getInputMap().put(KeyStroke.getKeyStroke(KeyEvent.VK_RIGHT,0),"right");
|
f@0
|
228 getActionMap().put("right", new AbstractAction(){
|
f@0
|
229 @Override
|
f@0
|
230 public void actionPerformed(ActionEvent evt) {
|
f@0
|
231 TreePath path = getSelectionPath();
|
f@0
|
232 TreeNode treeNode = (TreeNode)path.getLastPathComponent();
|
f@0
|
233 if(treeNode.isLeaf()){/* leaf node */
|
f@0
|
234 SoundFactory.getInstance().play(SoundEvent.ERROR);
|
f@0
|
235 }
|
f@0
|
236 else{
|
f@0
|
237 expandPath(path);
|
f@0
|
238 final FileSystemTreeNode firstChild = (FileSystemTreeNode)treeNode.getChildAt(0);
|
f@0
|
239 treeSelectionListenerGateOpen = false;
|
f@0
|
240 setSelectionPath(new TreePath(firstChild.getPath()));
|
f@0
|
241 treeSelectionListenerGateOpen = true;
|
f@0
|
242 SoundFactory.getInstance().play(SoundEvent.TREE_NODE_EXPAND,new PlayerListener(){
|
f@0
|
243 @Override
|
f@0
|
244 public void playEnded() {
|
f@0
|
245 NarratorFactory.getInstance().speak(firstChild.spokenText());
|
f@0
|
246 }
|
f@0
|
247 });
|
f@0
|
248 }
|
f@0
|
249 }
|
f@0
|
250 });
|
f@0
|
251
|
f@0
|
252 getInputMap().put(KeyStroke.getKeyStroke(KeyEvent.VK_SPACE,0),"space");
|
f@0
|
253 getActionMap().put("space",new AbstractAction(){
|
f@0
|
254 @Override
|
f@0
|
255 public void actionPerformed(ActionEvent evt) {
|
f@0
|
256 TreePath path = getSelectionPath();
|
f@0
|
257 FileSystemTreeNode treeNode = (FileSystemTreeNode)path.getLastPathComponent();
|
f@0
|
258 NarratorFactory.getInstance().speak(treeNode.toString());
|
f@0
|
259 }
|
f@0
|
260 });
|
f@0
|
261
|
f@0
|
262 getInputMap().put(KeyStroke.getKeyStroke(KeyEvent.VK_SPACE,InputEvent.CTRL_DOWN_MASK), "ctrl_space");
|
f@0
|
263 getActionMap().put("ctrl_space", new AbstractAction(){
|
f@0
|
264 @Override
|
f@0
|
265 public void actionPerformed(ActionEvent evt) {
|
f@0
|
266 TreePath path = getSelectionPath();
|
f@0
|
267 FileSystemTreeNode treeNode = (FileSystemTreeNode)path.getLastPathComponent();
|
f@0
|
268 NarratorFactory.getInstance().speak(treeNode.getFile().getPath());
|
f@0
|
269 }
|
f@0
|
270 });
|
f@0
|
271 /* make the tree ignore the page up and page down keys */
|
f@0
|
272 getInputMap().put(KeyStroke.getKeyStroke(KeyEvent.VK_PAGE_UP,0),"none");
|
f@0
|
273 getInputMap().put(KeyStroke.getKeyStroke(KeyEvent.VK_PAGE_DOWN,0),"none");
|
f@0
|
274 }
|
f@0
|
275 private boolean treeSelectionListenerGateOpen;
|
f@0
|
276 }
|
f@0
|
277
|
f@0
|
278 /**
|
f@0
|
279 * This class overwrites the default cell renderer in order to always render directories with a
|
f@0
|
280 * directory-icon regardless whether they are a leaf node or not.
|
f@0
|
281 *
|
f@0
|
282 */
|
f@0
|
283 class FileSystemTreeCellRendered implements TreeCellRenderer {
|
f@0
|
284 FileSystemTreeCellRendered(TreeCellRenderer delegate){
|
f@0
|
285 this.delegate = delegate;
|
f@0
|
286 }
|
f@0
|
287
|
f@0
|
288 @Override
|
f@0
|
289 public Component getTreeCellRendererComponent(JTree tree, Object value, boolean selected,
|
f@0
|
290 boolean expanded, boolean leaf, int row, boolean hasFocus) {
|
f@0
|
291 if(leaf && ((FileSystemTreeNode)value).getFile().isDirectory() )
|
f@0
|
292 return delegate.getTreeCellRendererComponent(tree, value, selected, expanded, false, row, hasFocus);
|
f@0
|
293 return delegate.getTreeCellRendererComponent(tree, value, selected, expanded, leaf, row, hasFocus);
|
f@0
|
294 }
|
f@0
|
295
|
f@0
|
296 TreeCellRenderer delegate;
|
f@0
|
297 }
|