1 /* $Id$ */
2 /***************************************************************************
3  *                   (C) Copyright 2003-2010 - Stendhal                    *
4  ***************************************************************************
5  ***************************************************************************
6  *                                                                         *
7  *   This program is free software; you can redistribute it and/or modify  *
8  *   it under the terms of the GNU General Public License as published by  *
9  *   the Free Software Foundation; either version 2 of the License, or     *
10  *   (at your option) any later version.                                   *
11  *                                                                         *
12  ***************************************************************************/
13 package data.sprites.monsters;
14 
15 /**
16  *
17  */
18 
19 import java.io.File;
20 import java.io.FileNotFoundException;
21 import java.util.Arrays;
22 
23 import javax.swing.JTree;
24 import javax.swing.event.TreeExpansionEvent;
25 import javax.swing.event.TreeExpansionListener;
26 import javax.swing.tree.DefaultMutableTreeNode;
27 import javax.swing.tree.DefaultTreeModel;
28 import javax.swing.tree.TreeModel;
29 import javax.swing.tree.TreePath;
30 
31 public class FileTree extends JTree {
32 	/**
33 	 *
34 	 */
35 	private static final long serialVersionUID = 1L;
36 
FileTree(final String path)37 	public FileTree(final String path) throws FileNotFoundException {
38 		// Create the JTree itself
39 		super((TreeModel) null);
40 
41 		// Use horizontal and vertical lines
42 		putClientProperty("JTree.lineStyle", "Angled");
43 
44 		// Create the first node
45 		final FileTreeNode rootNode = new FileTreeNode(null, path);
46 
47 		// Populate the root node with its subdirectories
48 		rootNode.populateDirectories(true);
49 		setModel(new DefaultTreeModel(rootNode));
50 
51 		// Listen for Tree Selection Events
52 		addTreeExpansionListener(new TreeExpansionHandler());
53 	}
54 
55 	// Returns the full pathname for a path, or null
56 	// if not a known path
getPathName(final TreePath path)57 	public String getPathName(final TreePath path) {
58 		final Object o = path.getLastPathComponent();
59 		if (o instanceof FileTreeNode) {
60 			return ((FileTreeNode) o).file.getAbsolutePath();
61 		}
62 		return null;
63 	}
64 
65 	// Returns the File for a path, or null if not a known path
getFile(final TreePath path)66 	public File getFile(final TreePath path) {
67 		final Object o = path.getLastPathComponent();
68 		if (o instanceof FileTreeNode) {
69 			return ((FileTreeNode) o).file;
70 		}
71 		return null;
72 	}
73 
74 	// Inner class that represents a node in this
75 	// file system tree
76 	protected class FileTreeNode extends DefaultMutableTreeNode {
77 
78 		private static final long serialVersionUID = 3223106240309250204L;
79 
80 		/**
81 		 * File object for this node.
82 		 */
83 		protected File file;
84 
85 		/**
86 		 * Name of this node.
87 		 */
88 		protected String name;
89 		/**
90 		 * true if we have been populated.
91 		 */
92 		protected boolean populated;
93 		/**
94 		 * true if we are in interim state.
95 		 */
96 
97 		protected boolean interim;
98 		/**
99 		 * true if this is a directory.
100 		 */
101 
102 		protected boolean isDir;
103 
104 
105 
106 
FileTreeNode(final File parent, final String name)107 		public FileTreeNode(final File parent, final String name) throws FileNotFoundException {
108 			this.name = name;
109 
110 			// See if this node exists and whether it
111 			// is a directory
112 			file = new File(parent, name);
113 			if (!file.exists()) {
114 				throw new FileNotFoundException("File " + name + " does not exist");
115 			}
116 
117 			isDir = file.isDirectory();
118 
119 			// Hold the File as the user object.
120 			setUserObject(file);
121 
122 		}
123 
124 		// Override isLeaf to check whether this is a directory
125 		@Override
isLeaf()126 		public boolean isLeaf() {
127 			return !isDir;
128 		}
129 
130 		// Override getAllowsChildren to check whether
131 		// this is a directory
132 		@Override
getAllowsChildren()133 		public boolean getAllowsChildren() {
134 			return isDir;
135 		}
136 
137 		// For display purposes, we return our own name public String toString()
138 		// { return name; }
139 		// If we are a directory, scan our contents and populate
140 		// with children. In addition, populate those children
141 		// if the "descend" flag is true. We only descend once,
142 		// to avoid recursing the whole subtree.
143 		// Returns true if some nodes were added
populateDirectories(final boolean descend)144 		boolean populateDirectories(final boolean descend) {
145 			boolean addedNodes = false;
146 			// Do this only once
147 			if (!populated) {
148 				if (interim) {
149 					// We have had a quick look here before:
150 					// remove the dummy node that we added last time
151 					removeAllChildren();
152 					interim = false;
153 				}
154 				// Get list of contents
155 				final String[] names = file.list();
156 				Arrays.sort(names);
157 
158 				// Process the directories
159 				for (int i = 0; i < names.length; i++) {
160 					final String nameTemp = names[i];
161 					try {
162 						// if (d.isDirectory()) {
163 						final FileTreeNode node = new FileTreeNode(file, nameTemp);
164 						this.add(node);
165 						if (descend) {
166 							node.populateDirectories(false);
167 						}
168 						addedNodes = true;
169 						if (!descend) {
170 							// Only add one node if not descending
171 							break;
172 						}
173 						// }
174 						// else{
175 
176 						// }
177 					} catch (final Throwable t) {
178 						// Ignore phantoms or access problems
179 					}
180 				}
181 
182 				// If we were scanning to get all subdirectories,
183 				// or if we found no subdirectories, there is no
184 				// reason to look at this directory again, so
185 				// set populated to true. Otherwise, we set interim
186 				// so that we look again in the future if we need to
187 				if (descend || !addedNodes) {
188 					populated = true;
189 				} else {
190 					// Just set interim state
191 					interim = true;
192 				}
193 			}
194 			return addedNodes;
195 		}
196 
197 
198 	}
199 
200 	// Inner class that handles Tree Expansion Events
201 	protected static class TreeExpansionHandler implements TreeExpansionListener {
202 		@Override
treeExpanded(final TreeExpansionEvent evt)203 		public void treeExpanded(final TreeExpansionEvent evt) {
204 			// The expanded path
205 			final TreePath path = evt.getPath();
206 			// The tree
207 			final JTree tree = (JTree) evt.getSource();
208 
209 			// Get the last component of the path and
210 			// arrange to have it fully populated.
211 			final FileTreeNode node = (FileTreeNode) path.getLastPathComponent();
212 			if (node.populateDirectories(true)) {
213 				((DefaultTreeModel) tree.getModel()).nodeStructureChanged(node);
214 			}
215 		}
216 
217 		@Override
treeCollapsed(final TreeExpansionEvent evt)218 		public void treeCollapsed(final TreeExpansionEvent evt) {
219 			// Nothing to do
220 		}
221 	}
222 }
223