PageRenderTime 80ms CodeModel.GetById 45ms app.highlight 29ms RepoModel.GetById 0ms app.codeStats 1ms

/jEdit/tags/jedit-4-0-pre5/org/gjt/sp/jedit/browser/BrowserView.java

#
Java | 784 lines | 588 code | 100 blank | 96 comment | 112 complexity | 1549ee105f7de2915820cd249666d26c MD5 | raw file
  1/*
  2 * BrowserView.java
  3 * :tabSize=8:indentSize=8:noTabs=false:
  4 * :folding=explicit:collapseFolds=1:
  5 *
  6 * Copyright (C) 2000, 2001, 2002 Slava Pestov
  7 *
  8 * This program is free software; you can redistribute it and/or
  9 * modify it under the terms of the GNU General Public License
 10 * as published by the Free Software Foundation; either version 2
 11 * of the License, or any later version.
 12 *
 13 * This program is distributed in the hope that it will be useful,
 14 * but WITHOUT ANY WARRANTY; without even the implied warranty of
 15 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.	See the
 16 * GNU General Public License for more details.
 17 *
 18 * You should have received a copy of the GNU General Public License
 19 * along with this program; if not, write to the Free Software
 20 * Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA  02111-1307, USA.
 21 */
 22
 23package org.gjt.sp.jedit.browser;
 24
 25//{{{ Imports
 26import javax.swing.border.EmptyBorder;
 27import javax.swing.event.*;
 28import javax.swing.tree.*;
 29import javax.swing.*;
 30import java.awt.event.*;
 31import java.awt.*;
 32import java.io.File;
 33import java.util.Enumeration;
 34import java.util.Hashtable;
 35import java.util.Vector;
 36import org.gjt.sp.jedit.io.*;
 37import org.gjt.sp.jedit.*;
 38//}}}
 39
 40/**
 41 * VFS browser tree view.
 42 * @author Slava Pestov
 43 * @version $Id: BrowserView.java 4000 2002-01-30 08:06:41Z spestov $
 44 */
 45public class BrowserView extends JPanel
 46{
 47	//{{{ BrowserView constructor
 48	public BrowserView(VFSBrowser browser, boolean splitHorizontally)
 49	{
 50		this.browser = browser;
 51		this.splitHorizontally = splitHorizontally;
 52
 53		parentModel = new DefaultListModel();
 54		parentDirectories = new JList(parentModel);
 55
 56		parentDirectories.getSelectionModel().setSelectionMode(
 57			TreeSelectionModel.SINGLE_TREE_SELECTION);
 58
 59		parentDirectories.setCellRenderer(new ParentDirectoryRenderer());
 60		parentDirectories.setVisibleRowCount(5);
 61		parentDirectories.addMouseListener(new MouseHandler());
 62
 63		rootNode = new DefaultMutableTreeNode(null,true);
 64		model = new DefaultTreeModel(rootNode,true);
 65
 66		tree = new BrowserJTree(model);
 67		tree.setCellRenderer(renderer);
 68		tree.setEditable(false);
 69		tree.addTreeExpansionListener(new TreeHandler());
 70		tree.putClientProperty("JTree.lineStyle", "Angled");
 71		tree.setRootVisible(false);
 72		tree.setShowsRootHandles(true);
 73		tree.setVisibleRowCount(12);
 74
 75		JScrollPane parentScroller = new JScrollPane(parentDirectories);
 76		parentScroller.setMinimumSize(new Dimension(0,0));
 77		JScrollPane treeScroller = new JScrollPane(tree);
 78		treeScroller.setMinimumSize(new Dimension(0,0));
 79		splitPane = new JSplitPane( 
 80			splitHorizontally ? JSplitPane.HORIZONTAL_SPLIT : JSplitPane.VERTICAL_SPLIT,
 81			parentScroller,treeScroller);
 82
 83		tmpExpanded = new Hashtable();
 84
 85		if(browser.isMultipleSelectionEnabled())
 86			tree.getSelectionModel().setSelectionMode(
 87				TreeSelectionModel.DISCONTIGUOUS_TREE_SELECTION);
 88		else
 89			tree.getSelectionModel().setSelectionMode(
 90				TreeSelectionModel.SINGLE_TREE_SELECTION);
 91
 92		setLayout(new BorderLayout());
 93
 94		add(BorderLayout.CENTER,splitPane);
 95
 96		propertiesChanged();
 97	} //}}}
 98
 99	//{{{ requestDefaultFocus() method
100	public boolean requestDefaultFocus()
101	{
102		tree.requestFocus();
103		return true;
104	} //}}}
105
106	//{{{ addNotify() method
107	public void addNotify()
108	{
109		super.addNotify();
110
111		SwingUtilities.invokeLater(new Runnable()
112		{
113			public void run()
114			{
115				String prop = splitHorizontally ? "vfs.browser.horizontalSplitter" : "vfs.browser.splitter";
116				int loc = jEdit.getIntegerProperty(prop,0);
117				if(loc != 0)
118				{
119					splitPane.setDividerLocation(loc);
120					parentDirectories.ensureIndexIsVisible(
121						parentDirectories.getModel()
122						.getSize());
123				}
124			}
125		});
126	} //}}}
127
128	//{{{ removeNotify() method
129	public void removeNotify()
130	{
131		String prop = splitHorizontally ? "vfs.browser.horizontalSplitter" : "vfs.browser.splitter";
132		jEdit.setIntegerProperty(prop,splitPane.getDividerLocation());
133
134		super.removeNotify();
135	} //}}}
136
137	//{{{ getSelectedFiles() method
138	public VFS.DirectoryEntry[] getSelectedFiles()
139	{
140		Vector selected = new Vector(tree.getSelectionCount());
141		TreePath[] paths = tree.getSelectionPaths();
142		if(paths == null)
143			return new VFS.DirectoryEntry[0];
144
145		for(int i = 0; i < paths.length; i++)
146		{
147			DefaultMutableTreeNode treeNode = (DefaultMutableTreeNode)
148				paths[i].getLastPathComponent();
149			Object obj = treeNode.getUserObject();
150			if(obj instanceof VFS.DirectoryEntry)
151				selected.addElement(obj);
152		}
153
154		VFS.DirectoryEntry[] retVal = new VFS.DirectoryEntry[selected.size()];
155		selected.copyInto(retVal);
156		return retVal;
157	} //}}}
158
159	//{{{ selectNone() method
160	public void selectNone()
161	{
162		tree.setSelectionPaths(new TreePath[0]);
163	} //}}}
164
165	//{{{ directoryLoaded() method
166	public void directoryLoaded(DefaultMutableTreeNode node,
167		String path, Vector directory)
168	{
169		if(node == rootNode)
170		{
171			parentModel.removeAllElements();
172			String parent = path;
173			for(;;)
174			{
175				parentModel.insertElementAt(parent,0);
176				String newParent = MiscUtilities.getParentOfPath(parent);
177				if(newParent.length() != 1 && (newParent.endsWith("/")
178					|| newParent.endsWith(File.separator)))
179					newParent = newParent.substring(0,newParent.length() - 1);
180
181				if(newParent == null || parent.equals(newParent))
182					break;
183				else
184					parent = newParent;
185			}
186
187			int index = parentModel.getSize() - 1;
188			parentDirectories.setSelectedIndex(index);
189			parentDirectories.ensureIndexIsVisible(parentModel.getSize() - 1);
190		}
191
192		node.removeAllChildren();
193
194		Vector toExpand = new Vector();
195
196		if(directory != null)
197		{
198			for(int i = 0; i < directory.size(); i++)
199			{
200				VFS.DirectoryEntry file = (VFS.DirectoryEntry)
201					directory.elementAt(i);
202				boolean allowsChildren = (file.type != VFS.DirectoryEntry.FILE);
203				DefaultMutableTreeNode newNode = new DefaultMutableTreeNode(file,allowsChildren);
204				node.add(newNode);
205				if(tmpExpanded.get(file.path) != null)
206					toExpand.addElement(new TreePath(newNode.getPath()));
207			}
208		}
209
210		// fire events
211		model.reload(node);
212		tree.expandPath(new TreePath(node.getPath()));
213
214		// expand branches that were expanded before
215		for(int i = 0; i < toExpand.size(); i++)
216		{
217			TreePath treePath = (TreePath)toExpand.elementAt(i);
218			tree.expandPath(treePath);
219		}
220
221		timer.stop();
222		typeSelectBuffer.setLength(0);
223	} //}}}
224
225	//{{{ updateFileView() method
226	public void updateFileView()
227	{
228		tree.repaint();
229	} //}}}
230
231	//{{{ loadDirectory() method
232	public void loadDirectory(String path)
233	{
234		// called by VFSBrowser.setDirectory()
235		tmpExpanded.clear();
236		loadDirectory(rootNode,path,false);
237	} //}}}
238
239	//{{{ maybeReloadDirectory() method
240	public void maybeReloadDirectory(String path)
241	{
242		tmpExpanded.clear();
243
244		// because this method is called for *every* VFS update,
245		// we don't want to scan the tree all the time. So we
246		// use the following algorithm to determine if the path
247		// might be part of the tree:
248		// - if the path starts with the browser's current directory,
249		//   we do the tree scan
250		// - if the browser's directory is 'favorites:' -- we have to
251		//   do the tree scan, as every path can appear under the
252		//   favorites list
253		// - if the browser's directory is 'roots:' and path is on
254		//   the local filesystem, do a tree scan
255		String browserDir = browser.getDirectory();
256		if(browserDir.startsWith(FavoritesVFS.PROTOCOL))
257			maybeReloadDirectory(rootNode,path);
258		else if(browserDir.startsWith(FileRootsVFS.PROTOCOL))
259		{
260			if(!MiscUtilities.isURL(path) || MiscUtilities.getProtocolOfURL(path)
261				.equals("file"))
262				maybeReloadDirectory(rootNode,path);
263		}
264		else if(path.startsWith(browserDir))
265			maybeReloadDirectory(rootNode,path);
266	} //}}}
267
268	//{{{ getDefaultFocusComponent() method
269	public Component getDefaultFocusComponent()
270	{
271		return tree;
272	} //}}}
273
274	//{{{ propertiesChanged() method
275	public void propertiesChanged()
276	{
277		showIcons = jEdit.getBooleanProperty("vfs.browser.showIcons");
278		renderer.propertiesChanged();
279
280		tree.setRowHeight(renderer.getTreeCellRendererComponent(
281			tree,new DefaultMutableTreeNode("foo"),
282			false,false,false,0,false).getSize().height);
283
284		splitPane.setBorder(null);
285	} //}}}
286
287	//{{{ Private members
288
289	//{{{ Instance variables
290	private VFSBrowser browser;
291
292	private JSplitPane splitPane;
293	private JList parentDirectories;
294	private DefaultListModel parentModel;
295	private JTree tree;
296	private Hashtable tmpExpanded;
297	private DefaultTreeModel model;
298	private DefaultMutableTreeNode rootNode;
299	private BrowserCommandsMenu popup;
300	private boolean showIcons;
301	private boolean splitHorizontally;
302
303	private FileCellRenderer renderer = new FileCellRenderer();
304
305	private StringBuffer typeSelectBuffer = new StringBuffer();
306	private Timer timer = new Timer(0,new ClearTypeSelect());
307	//}}}
308
309	//{{{ maybeReloadDirectory() method
310	private boolean maybeReloadDirectory(DefaultMutableTreeNode node, String path)
311	{
312		// nodes which are not expanded need not be checked
313		if(!tree.isExpanded(new TreePath(node.getPath())))
314			return false;
315
316		if(node == rootNode && path.equals(browser.getDirectory()))
317		{
318			loadDirectory(rootNode,path,false);
319			return true;
320		}
321
322		Object userObject = node.getUserObject();
323		if(userObject instanceof VFS.DirectoryEntry)
324		{
325			VFS.DirectoryEntry file = (VFS.DirectoryEntry)userObject;
326
327			// we don't need to do anything with files!
328			if(file.type == VFS.DirectoryEntry.FILE)
329				return false;
330
331			if(path.equals(file.path))
332			{
333				loadDirectory(node,path,false);
334				return true;
335			}
336		}
337
338		if(node.getChildCount() != 0)
339		{
340			Enumeration children = node.children();
341			while(children.hasMoreElements())
342			{
343				DefaultMutableTreeNode child = (DefaultMutableTreeNode)
344					children.nextElement();
345				if(maybeReloadDirectory(child,path))
346					return true;
347			}
348		}
349
350		return false;
351	} //}}}
352
353	//{{{ loadDirectory() method
354	private void loadDirectory(DefaultMutableTreeNode node, String path,
355		boolean showLoading)
356	{
357		int rowCount = tree.getRowCount();
358		for(int i = 0; i < rowCount; i++)
359		{
360			TreePath treePath = tree.getPathForRow(i);
361			if(tree.isExpanded(treePath))
362			{
363				DefaultMutableTreeNode _node = (DefaultMutableTreeNode)
364					treePath.getLastPathComponent();
365				VFS.DirectoryEntry file = ((VFS.DirectoryEntry)
366					_node.getUserObject());
367
368				tmpExpanded.put(file.path,file.path);
369			}
370		}
371
372		if(node == rootNode)
373		{
374			parentModel.removeAllElements();
375			parentModel.addElement(new LoadingPlaceholder());
376		}
377
378		if(showLoading)
379		{
380			node.removeAllChildren();
381			node.add(new DefaultMutableTreeNode(new LoadingPlaceholder(),false));
382			model.reload(node);
383		}
384
385		browser.loadDirectory(node,path,node == rootNode);
386	} //}}}
387
388	//{{{ showFilePopup() method
389	private void showFilePopup(VFS.DirectoryEntry file, Point point)
390	{
391		popup = new BrowserCommandsMenu(browser,file);
392		GUIUtilities.showPopupMenu(popup,tree,point.x+1,point.y+1);
393	} //}}}
394
395	//}}}
396
397	//{{{ Inner classes
398
399	//{{{ ClearTypeSelect
400	class ClearTypeSelect implements ActionListener
401	{
402		public void actionPerformed(ActionEvent evt)
403		{
404			typeSelectBuffer.setLength(0);
405			browser.filesSelected();
406		}
407	} //}}}
408
409	//{{{ ParentDirectoryRenderer class
410	class ParentDirectoryRenderer extends DefaultListCellRenderer
411	{
412		Font plainFont, boldFont;
413
414		ParentDirectoryRenderer()
415		{
416			plainFont = UIManager.getFont("Tree.font");
417			boldFont = new Font(plainFont.getName(),Font.BOLD,plainFont.getSize());
418		}
419
420		public Component getListCellRendererComponent(
421			JList list,
422			Object value,
423			int index,
424			boolean isSelected,
425			boolean cellHasFocus)
426		{
427			super.getListCellRendererComponent(list,value,index,
428				isSelected,cellHasFocus);
429
430			ParentDirectoryRenderer.this.setBorder(new EmptyBorder(
431				1,index * 10 + 1,1,1));
432
433			if(value instanceof LoadingPlaceholder)
434			{
435				ParentDirectoryRenderer.this.setFont(plainFont);
436
437				setIcon(showIcons ? FileCellRenderer.loadingIcon : null);
438				setText(jEdit.getProperty("vfs.browser.tree.loading"));
439			}
440			else
441			{
442				ParentDirectoryRenderer.this.setFont(boldFont);
443
444				setIcon(showIcons ? FileCellRenderer.openDirIcon : null);
445				setText(MiscUtilities.getFileName(value.toString()));
446			}
447
448			return this;
449		}
450	} //}}}
451
452	//{{{ MouseHandler class
453	class MouseHandler extends MouseAdapter
454	{
455		public void mouseClicked(MouseEvent evt)
456		{
457			// ignore double clicks
458			if(evt.getClickCount() == 2)
459				return;
460
461			int row = parentDirectories.locationToIndex(evt.getPoint());
462			if(row != -1)
463			{
464				Object obj = parentModel.getElementAt(row);
465				if(obj instanceof String)
466				{
467					browser.setDirectory((String)obj);
468					requestDefaultFocus();
469				}
470			}
471		}
472	} //}}}
473
474	//{{{ BrowserJTree class
475	class BrowserJTree extends JTree
476	{
477		//{{{ BrowserJTree constructor
478		BrowserJTree(TreeModel model)
479		{
480			super(model);
481			ToolTipManager.sharedInstance().registerComponent(this);
482		} //}}}
483
484		//{{{ getToolTipText() method
485		public final String getToolTipText(MouseEvent evt)
486		{
487			TreePath path = getPathForLocation(evt.getX(), evt.getY());
488			if(path != null)
489			{
490				Rectangle cellRect = getPathBounds(path);
491				if(cellRect != null && !cellRectIsVisible(cellRect))
492					return path.getLastPathComponent().toString();
493			}
494			return null;
495		} //}}}
496
497		//{{{ getToolTipLocation() method
498		public final Point getToolTipLocation(MouseEvent evt)
499		{
500			TreePath path = getPathForLocation(evt.getX(), evt.getY());
501			if(path != null)
502			{
503				Rectangle cellRect = getPathBounds(path);
504				if(cellRect != null && !cellRectIsVisible(cellRect))
505				{
506					return new Point(cellRect.x + (showIcons ? 14 : - 4),
507						cellRect.y);
508				}
509			}
510			return null;
511		} //}}}
512
513		//{{{ processKeyEvent() method
514		protected void processKeyEvent(KeyEvent evt)
515		{
516			if(evt.getID() == KeyEvent.KEY_PRESSED)
517			{
518				switch(evt.getKeyCode())
519				{
520				case KeyEvent.VK_ENTER:
521					browser.filesActivated(evt.isShiftDown(),false);
522					evt.consume();
523					break;
524				case KeyEvent.VK_LEFT:
525					String directory = browser.getDirectory();
526					browser.setDirectory(MiscUtilities
527						.getParentOfPath(directory));
528					evt.consume();
529					break;
530				}
531			}
532			else if(evt.getID() == KeyEvent.KEY_TYPED)
533			{
534				switch(evt.getKeyChar())
535				{
536				case '~':
537					browser.setDirectory(System.getProperty("user.home"));
538					break;
539				case '/':
540					browser.rootDirectory();
541					break;
542				case '-':
543					View view = browser.getView();
544					Buffer buffer = view.getBuffer();
545					browser.setDirectory(MiscUtilities.getParentOfPath(
546						buffer.getPath()));
547					break;
548				default:
549					typeSelectBuffer.append(evt.getKeyChar());
550					doTypeSelect(typeSelectBuffer.toString());
551
552					timer.stop();
553					timer.setInitialDelay(750);
554					timer.setRepeats(false);
555					timer.start();
556					break;
557				}
558
559				return;
560			}
561
562			if(!evt.isConsumed())
563				super.processKeyEvent(evt);
564		} //}}}
565
566		//{{{ processMouseEvent() method
567		protected void processMouseEvent(MouseEvent evt)
568		{
569			ToolTipManager ttm = ToolTipManager.sharedInstance();
570
571			switch(evt.getID())
572			{
573			//{{{ MOUSE_ENTERED...
574			case MouseEvent.MOUSE_ENTERED:
575				toolTipInitialDelay = ttm.getInitialDelay();
576				toolTipReshowDelay = ttm.getReshowDelay();
577				ttm.setInitialDelay(200);
578				ttm.setReshowDelay(0);
579				super.processMouseEvent(evt);
580				break; //}}}
581			//{{{ MOUSE_EXITED...
582			case MouseEvent.MOUSE_EXITED:
583				ttm.setInitialDelay(toolTipInitialDelay);
584				ttm.setReshowDelay(toolTipReshowDelay);
585				super.processMouseEvent(evt);
586				break; //}}}
587			//{{{ MOUSE_CLICKED...
588			case MouseEvent.MOUSE_CLICKED:
589				if((evt.getModifiers() & MouseEvent.BUTTON2_MASK) != 0)
590				{
591					TreePath path = getPathForLocation(evt.getX(),evt.getY());
592					if(path == null)
593					{
594						super.processMouseEvent(evt);
595						break;
596					}
597
598					if(!isPathSelected(path))
599						setSelectionPath(path);
600
601					browser.filesActivated(evt.isShiftDown(),true);
602					break;
603				}
604				else if((evt.getModifiers() & MouseEvent.BUTTON1_MASK) != 0)
605				{
606					TreePath path = getPathForLocation(evt.getX(),evt.getY());
607					if(path == null)
608					{
609						super.processMouseEvent(evt);
610						break;
611					}
612
613					if(!isPathSelected(path))
614						setSelectionPath(path);
615
616					if(evt.getClickCount() == 1)
617					{
618						browser.filesSelected();
619						super.processMouseEvent(evt);
620					}
621					if(evt.getClickCount() == 2)
622					{
623						// don't pass double-clicks to tree, otherwise
624						// directory nodes will be expanded and we don't
625						// want that
626						browser.filesActivated(evt.isShiftDown(),true);
627						break;
628					}
629				}
630				else if(GUIUtilities.isPopupTrigger(evt))
631					; // do nothing
632
633				super.processMouseEvent(evt);
634				break; //}}}
635			//{{{ MOUSE_PRESSED...
636			case MouseEvent.MOUSE_PRESSED:
637				if((evt.getModifiers() & MouseEvent.BUTTON1_MASK) != 0)
638				{
639					if(popup != null && popup.isVisible())
640						popup.setVisible(false);
641
642					if(evt.getClickCount() == 2)
643						break;
644				}
645				else if(GUIUtilities.isPopupTrigger(evt))
646				{
647					if(popup != null && popup.isVisible())
648					{
649						popup.setVisible(false);
650						break;
651					}
652
653					TreePath path = getPathForLocation(evt.getX(),evt.getY());
654					if(path == null)
655						showFilePopup(null,evt.getPoint());
656					else
657					{
658						setSelectionPath(path);
659						browser.filesSelected();
660
661						Object userObject = ((DefaultMutableTreeNode)path
662							.getLastPathComponent()).getUserObject();
663						if(userObject instanceof VFS.DirectoryEntry)
664						{
665							VFS.DirectoryEntry file = (VFS.DirectoryEntry)
666								userObject;
667							showFilePopup(file,evt.getPoint());
668						}
669						else
670							showFilePopup(null,evt.getPoint());
671					}
672
673					break;
674				}
675
676				super.processMouseEvent(evt);
677				break; //}}}
678			//{{{ MOUSE_RELEASED...
679			case MouseEvent.MOUSE_RELEASED:
680				if(evt.getClickCount() != 2)
681					super.processMouseEvent(evt);
682				break; //}}}
683			default:
684				super.processMouseEvent(evt);
685				break;
686			}
687		} //}}}
688
689		//{{{ Private members
690		private int toolTipInitialDelay = -1;
691		private int toolTipReshowDelay = -1;
692
693		//{{{ cellRectIsVisible() method
694		private boolean cellRectIsVisible(Rectangle cellRect)
695		{
696			Rectangle vr = BrowserJTree.this.getVisibleRect();
697			return vr.contains(cellRect.x,cellRect.y) &&
698				vr.contains(cellRect.x + cellRect.width,
699				cellRect.y + cellRect.height);
700		} //}}}
701
702		//{{{ doTypeSelect() method
703		private void doTypeSelect(String str)
704		{
705			if(getSelectionCount() == 0)
706				doTypeSelect(str,0,getRowCount());
707			else
708			{
709				int start = getMaxSelectionRow();
710				boolean retVal = doTypeSelect(str,start,getRowCount());
711
712				if(!retVal)
713				{
714					// scan from selection to end failed, so
715					// scan from start to selection
716					doTypeSelect(str,0,start);
717				}
718			}
719		} //}}}
720
721		//{{{ doTypeSelect() method
722		private boolean doTypeSelect(String str, int start, int end)
723		{
724			for(int i = start; i < end; i++)
725			{
726				DefaultMutableTreeNode treeNode = (DefaultMutableTreeNode)
727					getPathForRow(i).getLastPathComponent();
728				Object obj = treeNode.getUserObject();
729				if(obj instanceof VFS.DirectoryEntry)
730				{
731					VFS.DirectoryEntry file = (VFS.DirectoryEntry)obj;
732					if(file.name.regionMatches(true,0,str,0,str.length()))
733					{
734						setSelectionRow(i);
735						scrollRowToVisible(i);
736						return true;
737					}
738				}
739			}
740
741			return false;
742		} //}}}
743
744		//}}}
745	} //}}}
746
747	//{{{ TreeHandler class
748	class TreeHandler implements TreeExpansionListener
749	{
750		//{{{ treeExpanded() method
751		public void treeExpanded(TreeExpansionEvent evt)
752		{
753			TreePath path = evt.getPath();
754			DefaultMutableTreeNode treeNode = (DefaultMutableTreeNode)
755				path.getLastPathComponent();
756			Object userObject = treeNode.getUserObject();
757			if(userObject instanceof VFS.DirectoryEntry)
758			{
759				loadDirectory(treeNode,((VFS.DirectoryEntry)
760					userObject).path,true);
761			}
762		} //}}}
763
764		//{{{ treeCollapsed() method
765		public void treeCollapsed(TreeExpansionEvent evt)
766		{
767			TreePath path = evt.getPath();
768			DefaultMutableTreeNode treeNode = (DefaultMutableTreeNode)
769				path.getLastPathComponent();
770			if(treeNode.getUserObject() instanceof VFS.DirectoryEntry)
771			{
772				// we add the placeholder so that the node has
773				// 1 child (otherwise the user won't be able to
774				// expand it again)
775				treeNode.removeAllChildren();
776				treeNode.add(new DefaultMutableTreeNode(new LoadingPlaceholder(),false));
777				model.reload(treeNode);
778			}
779		} //}}}
780	} //}}}
781
782	static class LoadingPlaceholder {}
783	//}}}
784}