/Users/lyon/j4p/src/gui/tree/FileTree.java

1    package gui.tree; 
2     
3    import javax.swing.*; 
4    import javax.swing.event.TreeExpansionEvent; 
5    import javax.swing.event.TreeExpansionListener; 
6    import javax.swing.tree.DefaultMutableTreeNode; 
7    import javax.swing.tree.DefaultTreeModel; 
8    import javax.swing.tree.TreeModel; 
9    import javax.swing.tree.TreePath; 
10   import java.awt.*; 
11   import java.awt.dnd.Autoscroll; 
12   import java.io.File; 
13   import java.io.FileNotFoundException; 
14   import java.util.ArrayList; 
15   import java.util.Arrays; 
16   import java.util.Comparator; 
17    
18   public class FileTree extends JTree implements Autoscroll { 
19       public static final Insets defaultScrollInsets = new Insets(8, 8, 8, 8); 
20       protected Insets scrollInsets = defaultScrollInsets; 
21    
22       public FileTree(String path) throws FileNotFoundException, SecurityException { 
23           super((TreeModel) null);            // Create the JTree itself 
24    
25           // Use horizontal and vertical lines 
26           putClientProperty("JTree.lineStyle", "Angled"); 
27    
28           // Create the first node 
29           DefaultMutableFileTreeNode rootNode = new DefaultMutableFileTreeNode(null, path); 
30    
31           // Populate the root node with its subdirectories 
32           boolean addedNodes = rootNode.populateDirectories(true); 
33           setModel(new DefaultTreeModel(rootNode)); 
34    
35           // Listen for Tree Selection Events 
36           addTreeExpansionListener(new TreeExpansionHandler()); 
37       } 
38    
39       // Returns the full pathname for a path, or null if not a known path 
40       public String getPathName(TreePath path) { 
41           Object o = path.getLastPathComponent(); 
42           if (o instanceof DefaultMutableFileTreeNode) { 
43               return ((DefaultMutableFileTreeNode) o).fullName; 
44           } 
45           return null; 
46       } 
47    
48       // Adds a new node to the gui.tree after construction. 
49       // Returns the inserted node, or null if the parent 
50       // directory has not been expanded. 
51       public DefaultMutableFileTreeNode addNode(DefaultMutableFileTreeNode parent, String name) { 
52           int index = parent.addNode(name); 
53           if (index != -1) { 
54               ((DefaultTreeModel) getModel()).nodesWereInserted( 
55                       parent, new int[]{index}); 
56               return (DefaultMutableFileTreeNode) parent.getChildAt(index); 
57           } 
58    
59           // No node was created 
60           return null; 
61       } 
62    
63       // Autoscrolling support 
64       public void setScrollInsets(Insets insets) { 
65           this.scrollInsets = insets; 
66       } 
67    
68       public Insets getScrollInsets() { 
69           return scrollInsets; 
70       } 
71    
72       // Implementation of Autoscroll interface 
73       public Insets getAutoscrollInsets() { 
74           Rectangle r = getVisibleRect(); 
75           Dimension size = getSize(); 
76           Insets i = new Insets(r.y + scrollInsets.top, r.x + scrollInsets.left, 
77                   size.height - r.y - r.height + scrollInsets.bottom, 
78                   size.width - r.x - r.width + scrollInsets.right); 
79           return i; 
80       } 
81    
82       public void autoscroll(Point location) { 
83           JScrollPane scroller = 
84                   (JScrollPane) SwingUtilities.getAncestorOfClass(JScrollPane.class, this); 
85           if (scroller != null) { 
86               JScrollBar hBar = scroller.getHorizontalScrollBar(); 
87               JScrollBar vBar = scroller.getVerticalScrollBar(); 
88               Rectangle r = getVisibleRect(); 
89               if (location.x <= r.x + scrollInsets.left) { 
90                   // Need to scroll left 
91                   hBar.setValue(hBar.getValue() - hBar.getUnitIncrement(-1)); 
92               } 
93               if (location.y <= r.y + scrollInsets.top) { 
94                   // Need to scroll up 
95                   vBar.setValue(vBar.getValue() - vBar.getUnitIncrement(-1)); 
96               } 
97               if (location.x >= r.x + r.width - scrollInsets.right) { 
98                   // Need to scroll right 
99                   hBar.setValue(hBar.getValue() + hBar.getUnitIncrement(1)); 
100              } 
101              if (location.y >= r.y + r.height - scrollInsets.bottom) { 
102                  // Need to scroll down 
103                  vBar.setValue(vBar.getValue() + vBar.getUnitIncrement(1)); 
104              } 
105          } 
106      } 
107   
108      // Inner class that represents a node in this file system gui.tree 
109      public static class DefaultMutableFileTreeNode extends DefaultMutableTreeNode { 
110          public DefaultMutableFileTreeNode(String parent, String name) throws SecurityException, 
111                  FileNotFoundException { 
112              this.name = name; 
113   
114              // See if this node exists and whether it is a directory 
115              fullName = parent == null ? name : parent + File.separator + name; 
116   
117              File f = new File(fullName); 
118              if (f.exists() == false) { 
119                  throw new FileNotFoundException("File " + fullName + " does not exist"); 
120              } 
121   
122              isDir = f.isDirectory(); 
123   
124              // Hack for Windows which doesn't consider a drive to be a directory! 
125              if (isDir == false && f.isFile() == false) { 
126                  isDir = true; 
127              } 
128          } 
129   
130          // Override isLeaf to check whether this is a directory 
131          public boolean isLeaf() { 
132              return !isDir; 
133          } 
134   
135          // Override getAllowsChildren to check whether this is a directory 
136          public boolean getAllowsChildren() { 
137              return isDir; 
138          } 
139   
140          // Return whether this is a directory 
141          public boolean isDir() { 
142              return isDir; 
143          } 
144   
145          // Get full path 
146          public String getFullName() { 
147              return fullName; 
148          } 
149   
150          // For display purposes, we return our own name 
151          public String toString() { 
152              return name; 
153          } 
154   
155          // If we are a directory, scan our contents and populate 
156          // with children. In addition, populate those children 
157          // if the "descend" flag is true. We only descend once, 
158          // to avoid recursing the whole subtree. 
159          // Returns true if some nodes were added 
160          boolean populateDirectories(boolean descend) { 
161              boolean addedNodes = false; 
162   
163              // Do this only once 
164              if (populated == false) { 
165                  File f; 
166                  try { 
167                      f = new File(fullName); 
168                  } catch (SecurityException e) { 
169                      populated = true; 
170                      return false; 
171                  } 
172   
173                  if (interim == true) { 
174                      // We have had a quick look here before: 
175                      // remove the dummy node that we added last time 
176                      removeAllChildren(); 
177                      interim = false; 
178                  } 
179   
180                  String fileNames[] = f.list();      // Get list of contents 
181   
182                  // Process the contents 
183                  ArrayList al = new ArrayList(); 
184                  for (int i = 0; i < fileNames.length; i++) { 
185                      String fileName = fileNames[i]; 
186                      File d = new File(fullName, fileName); 
187                      try { 
188                          DefaultMutableFileTreeNode node = 
189                                  new DefaultMutableFileTreeNode(fullName, fileName); 
190                          al.add(node); 
191                          if (descend && d.isDirectory()) { 
192                              node.populateDirectories(false); 
193                          } 
194                          addedNodes = true; 
195                          if (descend == false) { 
196                              // Only add one node if not descending 
197                              break; 
198                          } 
199                      } catch (Throwable t) { 
200                          // Ignore phantoms or access problems 
201                      } 
202                  } 
203   
204                  if (addedNodes == true) { 
205                      // Now sort the list of contained files and directories 
206                      Object[] nodes = al.toArray(); 
207                      Arrays.sort(nodes, new Comparator() { 
208                          public boolean equals(Object o) { 
209                              return false; 
210                          } 
211   
212                          public int compare(Object o1, Object o2) { 
213                              DefaultMutableFileTreeNode node1 = (DefaultMutableFileTreeNode) o1; 
214                              DefaultMutableFileTreeNode node2 = (DefaultMutableFileTreeNode) o2; 
215   
216                              // Directories come first 
217                              if (node1.isDir != node2.isDir) { 
218                                  return node1.isDir ? -1 : +1; 
219                              } 
220   
221                              // Both directories or both files - 
222                              // compare based on pathname 
223                              return node1.fullName.compareTo(node2.fullName); 
224                          } 
225                      }); 
226   
227                      // Add sorted items as children of this node 
228                      for (int j = 0; j < nodes.length; j++) { 
229                          this.add((DefaultMutableFileTreeNode) nodes[j]); 
230                      } 
231                  } 
232   
233                  // If we were scanning to get all subdirectories, 
234                  // or if we found no content, there is no 
235                  // reason to look at this directory again, so 
236                  // set populated to true. Otherwise, we set interim 
237                  // so that we look again in the future if we need to 
238                  if (descend == true || addedNodes == false) { 
239                      populated = true; 
240                  } else { 
241                      // Just set interim state 
242                      interim = true; 
243                  } 
244              } 
245              return addedNodes; 
246          } 
247   
248          // Adding a new file or directory after 
249          // constructing the FileTree. Returns 
250          // the index of the inserted node. 
251          public int addNode(String name) { 
252              // If not populated yet, do nothing 
253              if (populated == true) { 
254                  // Do not add a new node if 
255                  // the required node is already there 
256                  int childCount = getChildCount(); 
257                  for (int i = 0; i < childCount; i++) { 
258                      DefaultMutableFileTreeNode node = (DefaultMutableFileTreeNode) getChildAt(i); 
259                      if (node.name.equals(name)) { 
260                          // Already exists - ensure 
261                          // we repopulate 
262                          if (node.isDir()) { 
263                              node.interim = true; 
264                              node.populated = false; 
265                          } 
266                          return -1; 
267                      } 
268                  } 
269   
270                  // Add a new node 
271                  try { 
272                      DefaultMutableFileTreeNode node = new DefaultMutableFileTreeNode(fullName, name); 
273                      add(node); 
274                      return childCount; 
275                  } catch (Exception e) { 
276                  } 
277              } 
278              return -1; 
279          } 
280   
281          protected String name;      // Name of this component 
282          protected String fullName;  // Full pathname 
283          protected boolean populated;// true if we have been populated 
284          protected boolean interim;  // true if we are in interim state 
285          protected boolean isDir;    // true if this is a directory 
286      } 
287   
288      // Inner class that handles Tree Expansion Events 
289      protected class TreeExpansionHandler implements TreeExpansionListener { 
290          public void treeExpanded(TreeExpansionEvent evt) { 
291              TreePath path = evt.getPath();          // The expanded path 
292              JTree tree = (JTree) evt.getSource();   // The gui.tree 
293   
294              // Get the last component of the path and 
295              // arrange to have it fully populated. 
296              DefaultMutableFileTreeNode node = (DefaultMutableFileTreeNode) path.getLastPathComponent(); 
297              if (node.populateDirectories(true)) { 
298                  ((DefaultTreeModel) tree.getModel()).nodeStructureChanged(node); 
299              } 
300          } 
301   
302          public void treeCollapsed(TreeExpansionEvent evt) { 
303              // Nothing to do 
304          } 
305      } 
306  }