PageRenderTime 69ms CodeModel.GetById 34ms app.highlight 28ms RepoModel.GetById 1ms app.codeStats 0ms

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

#
Java | 910 lines | 672 code | 117 blank | 121 comment | 136 complexity | 248a446be5d150b1466e118c2385d679 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 4310 2002-08-16 18:02:18Z spestov $
 44 */
 45class BrowserView extends JPanel
 46{
 47	//{{{ BrowserView constructor
 48	public BrowserView(VFSBrowser browser, final boolean splitHorizontally)
 49	{
 50		this.browser = browser;
 51		this.splitHorizontally = splitHorizontally;
 52
 53		parentDirectories = new JList();
 54
 55		parentDirectories.getSelectionModel().setSelectionMode(
 56			TreeSelectionModel.SINGLE_TREE_SELECTION);
 57
 58		parentDirectories.setCellRenderer(new ParentDirectoryRenderer());
 59		parentDirectories.setVisibleRowCount(5);
 60		parentDirectories.addMouseListener(new MouseHandler());
 61
 62		rootNode = new DefaultMutableTreeNode(null,true);
 63		model = new DefaultTreeModel(rootNode,true);
 64
 65		tree = new BrowserJTree(model);
 66		tree.setCellRenderer(renderer);
 67		tree.setEditable(false);
 68		tree.addTreeExpansionListener(new TreeHandler());
 69
 70		// looks bad with the OS X L&F, apparently...
 71		if(!OperatingSystem.isMacOSLF())
 72			tree.putClientProperty("JTree.lineStyle", "Angled");
 73
 74		tree.setRootVisible(false);
 75		tree.setShowsRootHandles(true);
 76		tree.setVisibleRowCount(12);
 77
 78		final JScrollPane parentScroller = new JScrollPane(parentDirectories);
 79		parentScroller.setMinimumSize(new Dimension(0,0));
 80		JScrollPane treeScroller = new JScrollPane(tree);
 81		treeScroller.setMinimumSize(new Dimension(0,0));
 82		splitPane = new JSplitPane(
 83			splitHorizontally ? JSplitPane.HORIZONTAL_SPLIT : JSplitPane.VERTICAL_SPLIT,
 84			parentScroller,treeScroller);
 85		splitPane.setOneTouchExpandable(true);
 86
 87		SwingUtilities.invokeLater(new Runnable()
 88		{
 89			public void run()
 90			{
 91				String prop = splitHorizontally ? "vfs.browser.horizontalSplitter" : "vfs.browser.splitter";
 92				int loc = jEdit.getIntegerProperty(prop,-1);
 93				if(loc == -1)
 94					loc = parentScroller.getPreferredSize().height;
 95
 96				splitPane.setDividerLocation(loc);
 97				parentDirectories.ensureIndexIsVisible(
 98					parentDirectories.getModel()
 99					.getSize());
100			}
101		});
102
103		tmpExpanded = new Hashtable();
104
105		if(browser.isMultipleSelectionEnabled())
106			tree.getSelectionModel().setSelectionMode(
107				TreeSelectionModel.DISCONTIGUOUS_TREE_SELECTION);
108		else
109			tree.getSelectionModel().setSelectionMode(
110				TreeSelectionModel.SINGLE_TREE_SELECTION);
111
112		setLayout(new BorderLayout());
113
114		add(BorderLayout.CENTER,splitPane);
115
116		propertiesChanged();
117	} //}}}
118
119	//{{{ focusOnFileView() method
120	public void focusOnFileView()
121	{
122		tree.requestFocus();
123	} //}}}
124
125	//{{{ removeNotify() method
126	public void removeNotify()
127	{
128		String prop = splitHorizontally ? "vfs.browser.horizontalSplitter" : "vfs.browser.splitter";
129		jEdit.setIntegerProperty(prop,splitPane.getDividerLocation());
130
131		super.removeNotify();
132	} //}}}
133
134	//{{{ getSelectedFiles() method
135	public VFS.DirectoryEntry[] getSelectedFiles()
136	{
137		Vector selected = new Vector(tree.getSelectionCount());
138		TreePath[] paths = tree.getSelectionPaths();
139		if(paths == null)
140			return new VFS.DirectoryEntry[0];
141
142		for(int i = 0; i < paths.length; i++)
143		{
144			DefaultMutableTreeNode treeNode = (DefaultMutableTreeNode)
145				paths[i].getLastPathComponent();
146			Object obj = treeNode.getUserObject();
147			if(obj instanceof VFS.DirectoryEntry)
148				selected.addElement(obj);
149		}
150
151		VFS.DirectoryEntry[] retVal = new VFS.DirectoryEntry[selected.size()];
152		selected.copyInto(retVal);
153		return retVal;
154	} //}}}
155
156	//{{{ selectNone() method
157	public void selectNone()
158	{
159		tree.setSelectionPaths(new TreePath[0]);
160	} //}}}
161
162	//{{{ loadDirectory() method
163	public void loadDirectory(String path)
164	{
165		// called by VFSBrowser.setDirectory()
166		tmpExpanded.clear();
167		loadDirectory(rootNode,path,false);
168	} //}}}
169
170	//{{{ directoryLoaded() method
171	public void directoryLoaded(DefaultMutableTreeNode node,
172		String path, Vector directory)
173	{
174		if(node == rootNode)
175		{
176			DefaultListModel parentList = new DefaultListModel();
177
178			String parent = path;
179
180			if(parent.length() != 1 && (parent.endsWith("/")
181				|| parent.endsWith(File.separator)))
182				parent = parent.substring(0,parent.length() - 1);
183
184			for(;;)
185			{
186				VFS _vfs = VFSManager.getVFSForPath(
187					parent);
188				// create a DirectoryEntry manually
189				// instead of using _vfs._getDirectoryEntry()
190				// since so many VFS's have broken
191				// implementations of this method
192				parentList.insertElementAt(new VFS.DirectoryEntry(
193					_vfs.getFileName(parent),
194					parent,parent,
195					VFS.DirectoryEntry.DIRECTORY,
196					0L,false),0);
197				String newParent = _vfs.getParentOfPath(parent);
198				if(newParent.length() != 1 && (newParent.endsWith("/")
199					|| newParent.endsWith(File.separator)))
200					newParent = newParent.substring(0,newParent.length() - 1);
201
202				if(newParent == null || parent.equals(newParent))
203					break;
204				else
205					parent = newParent;
206			}
207
208			parentDirectories.setModel(parentList);
209			int index = parentList.getSize() - 1;
210			parentDirectories.setSelectedIndex(index);
211			parentDirectories.ensureIndexIsVisible(index);
212		}
213
214		node.removeAllChildren();
215
216		Vector toExpand = new Vector();
217
218		if(directory != null)
219		{
220			for(int i = 0; i < directory.size(); i++)
221			{
222				VFS.DirectoryEntry file = (VFS.DirectoryEntry)
223					directory.elementAt(i);
224				boolean allowsChildren = (file.type != VFS.DirectoryEntry.FILE);
225				DefaultMutableTreeNode newNode = new DefaultMutableTreeNode(file,allowsChildren);
226				node.add(newNode);
227				if(tmpExpanded.get(file.path) != null)
228				{
229					tmpExpanded.remove(file.path);
230					toExpand.addElement(new TreePath(newNode.getPath()));
231				}
232			}
233		}
234
235		// fire events
236		model.reload(node);
237		tree.expandPath(new TreePath(node.getPath()));
238
239		// expand branches that were expanded before
240		for(int i = 0; i < toExpand.size(); i++)
241		{
242			TreePath treePath = (TreePath)toExpand.elementAt(i);
243			tree.expandPath(treePath);
244		}
245
246		timer.stop();
247		typeSelectBuffer.setLength(0);
248	} //}}}
249
250	//{{{ updateFileView() method
251	public void updateFileView()
252	{
253		tree.repaint();
254	} //}}}
255
256	//{{{ maybeReloadDirectory() method
257	public void maybeReloadDirectory(String path)
258	{
259		tmpExpanded.clear();
260
261		// because this method is called for *every* VFS update,
262		// we don't want to scan the tree all the time. So we
263		// use the following algorithm to determine if the path
264		// might be part of the tree:
265		// - if the path starts with the browser's current directory,
266		//   we do the tree scan
267		// - if the browser's directory is 'favorites:' -- we have to
268		//   do the tree scan, as every path can appear under the
269		//   favorites list
270		// - if the browser's directory is 'roots:' and path is on
271		//   the local filesystem, do a tree scan
272		String browserDir = browser.getDirectory();
273		if(browserDir.startsWith(FavoritesVFS.PROTOCOL))
274			maybeReloadDirectory(rootNode,path);
275		else if(browserDir.startsWith(FileRootsVFS.PROTOCOL))
276		{
277			if(!MiscUtilities.isURL(path) || MiscUtilities.getProtocolOfURL(path)
278				.equals("file"))
279				maybeReloadDirectory(rootNode,path);
280		}
281		else if(path.startsWith(browserDir))
282			maybeReloadDirectory(rootNode,path);
283	} //}}}
284
285	//{{{ getDefaultFocusComponent() method
286	public Component getDefaultFocusComponent()
287	{
288		return tree;
289	} //}}}
290
291	//{{{ propertiesChanged() method
292	public void propertiesChanged()
293	{
294		showIcons = jEdit.getBooleanProperty("vfs.browser.showIcons");
295		renderer.propertiesChanged();
296
297		tree.setRowHeight(renderer.getTreeCellRendererComponent(
298			tree,new DefaultMutableTreeNode("foo"),
299			false,false,false,0,false).getSize().height);
300
301		splitPane.setBorder(null);
302	} //}}}
303
304	//{{{ getTree() method
305	public BrowserJTree getTree()
306	{
307		return tree;
308	} //}}}
309
310	//{{{ Private members
311
312	//{{{ Instance variables
313	private VFSBrowser browser;
314
315	private JSplitPane splitPane;
316	private JList parentDirectories;
317	private BrowserJTree tree;
318	private Hashtable tmpExpanded;
319	private DefaultTreeModel model;
320	private DefaultMutableTreeNode rootNode;
321	private BrowserCommandsMenu popup;
322	private boolean showIcons;
323	private boolean splitHorizontally;
324
325	private FileCellRenderer renderer = new FileCellRenderer();
326
327	private StringBuffer typeSelectBuffer = new StringBuffer();
328	private Timer timer = new Timer(0,new ClearTypeSelect());
329	//}}}
330
331	//{{{ maybeReloadDirectory() method
332	private boolean maybeReloadDirectory(DefaultMutableTreeNode node, String path)
333	{
334		// nodes which are not expanded need not be checked
335		if(!tree.isExpanded(new TreePath(node.getPath())))
336			return false;
337
338		if(node == rootNode && path.equals(browser.getDirectory()))
339		{
340			loadDirectory(rootNode,path,false);
341			return true;
342		}
343
344		Object userObject = node.getUserObject();
345		if(userObject instanceof VFS.DirectoryEntry)
346		{
347			VFS.DirectoryEntry file = (VFS.DirectoryEntry)userObject;
348
349			// we don't need to do anything with files!
350			if(file.type == VFS.DirectoryEntry.FILE)
351				return false;
352
353			if(path.equals(file.path))
354			{
355				loadDirectory(node,path,false);
356				return true;
357			}
358		}
359
360		if(node.getChildCount() != 0)
361		{
362			Enumeration children = node.children();
363			while(children.hasMoreElements())
364			{
365				DefaultMutableTreeNode child = (DefaultMutableTreeNode)
366					children.nextElement();
367				if(maybeReloadDirectory(child,path))
368					return true;
369			}
370		}
371
372		return false;
373	} //}}}
374
375	//{{{ loadDirectory() method
376	private void loadDirectory(DefaultMutableTreeNode node, String path,
377		boolean showLoading)
378	{
379		saveExpansionState(node);
380
381		path = MiscUtilities.constructPath(browser.getDirectory(),path);
382		VFS vfs = VFSManager.getVFSForPath(path);
383
384		Object session = vfs.createVFSSession(path,this);
385		if(session == null)
386			return;
387
388		if(node == rootNode)
389		{
390			setListModel(parentDirectories,new Object[] {
391				new LoadingPlaceholder() });
392		}
393
394		if(showLoading)
395		{
396			node.removeAllChildren();
397			node.add(new DefaultMutableTreeNode(new LoadingPlaceholder(),false));
398			model.reload(node);
399		}
400
401		VFSManager.runInWorkThread(new BrowserIORequest(
402			BrowserIORequest.LIST_DIRECTORY,browser,
403			session,vfs,path,null,node,node == rootNode));
404	} //}}}
405
406	//{{{ saveExpansionState() method
407	private void saveExpansionState(DefaultMutableTreeNode node)
408	{
409		for(int i = 0; i < node.getChildCount(); i++)
410		{
411			DefaultMutableTreeNode child = (DefaultMutableTreeNode)
412				node.getChildAt(i);
413
414			TreePath treePath = new TreePath(child.getPath());
415
416			if(tree.isExpanded(treePath))
417			{
418				VFS.DirectoryEntry file = ((VFS.DirectoryEntry)
419					child.getUserObject());
420
421				tmpExpanded.put(file.path,file.path);
422
423				if(file.type != VFS.DirectoryEntry.FILE)
424					saveExpansionState(child);
425			}
426		}
427	} //}}}
428
429	//{{{ showFilePopup() method
430	private void showFilePopup(VFS.DirectoryEntry[] files, Component comp,
431		Point point)
432	{
433		popup = new BrowserCommandsMenu(browser,files);
434		// for the parent directory right-click; on the click we select
435		// the clicked item, but when the popup goes away we select the
436		// currently showing directory.
437		popup.addPopupMenuListener(new PopupMenuListener()
438		{
439			public void popupMenuCanceled(PopupMenuEvent e) {}
440
441			public void popupMenuWillBecomeVisible(PopupMenuEvent e) {}
442
443			public void popupMenuWillBecomeInvisible(PopupMenuEvent e)
444			{
445				int index = parentDirectories.getModel().getSize() - 1;
446				parentDirectories.setSelectedIndex(index);
447			}
448		});
449		GUIUtilities.showPopupMenu(popup,comp,point.x,point.y);
450	} //}}}
451
452	//{{{ setListModel() method
453	/**
454	 * This should be in the JDK API.
455	 */
456	private void setListModel(JList list, final Object[] model)
457	{
458		list.setModel(new AbstractListModel()
459		{
460			public int getSize() { return model.length; }
461			public Object getElementAt(int i) { return model[i]; }
462		});
463	} //}}}
464
465	//}}}
466
467	//{{{ Inner classes
468
469	//{{{ ClearTypeSelect
470	class ClearTypeSelect implements ActionListener
471	{
472		public void actionPerformed(ActionEvent evt)
473		{
474			typeSelectBuffer.setLength(0);
475			browser.filesSelected();
476		}
477	} //}}}
478
479	//{{{ ParentDirectoryRenderer class
480	class ParentDirectoryRenderer extends DefaultListCellRenderer
481	{
482		Font plainFont, boldFont;
483
484		ParentDirectoryRenderer()
485		{
486			plainFont = UIManager.getFont("Tree.font");
487			boldFont = new Font(plainFont.getName(),Font.BOLD,plainFont.getSize());
488		}
489
490		public Component getListCellRendererComponent(
491			JList list,
492			Object value,
493			int index,
494			boolean isSelected,
495			boolean cellHasFocus)
496		{
497			super.getListCellRendererComponent(list,value,index,
498				isSelected,cellHasFocus);
499
500			ParentDirectoryRenderer.this.setBorder(new EmptyBorder(
501				1,index * 5 + 1,1,1));
502
503			if(value instanceof LoadingPlaceholder)
504			{
505				ParentDirectoryRenderer.this.setFont(plainFont);
506
507				setIcon(showIcons ? FileCellRenderer.loadingIcon : null);
508				setText(jEdit.getProperty("vfs.browser.tree.loading"));
509			}
510			else if(value instanceof VFS.DirectoryEntry)
511			{
512				VFS.DirectoryEntry dirEntry = (VFS.DirectoryEntry)value;
513				ParentDirectoryRenderer.this.setFont(boldFont);
514
515				setIcon(showIcons ? FileCellRenderer.getIconForFile(dirEntry,true)
516					: null);
517				setText(dirEntry.name);
518			}
519			else if(value == null)
520				setText("VFS does not follow VFS API");
521
522			return this;
523		}
524	} //}}}
525
526	//{{{ MouseHandler class
527	class MouseHandler extends MouseAdapter
528	{
529		public void mousePressed(MouseEvent evt)
530		{
531			int row = parentDirectories.locationToIndex(evt.getPoint());
532			if(row != -1)
533			{
534				Object obj = parentDirectories.getModel()
535					.getElementAt(row);
536				if(obj instanceof VFS.DirectoryEntry)
537				{
538					VFS.DirectoryEntry dirEntry = ((VFS.DirectoryEntry)obj);
539					if(GUIUtilities.isPopupTrigger(evt))
540					{
541						if(popup != null && popup.isVisible())
542						{
543							popup.setVisible(false);
544							popup = null;
545						}
546						else
547						{
548							parentDirectories.setSelectedIndex(row);
549							showFilePopup(new VFS.DirectoryEntry[] {
550								dirEntry },parentDirectories,
551								evt.getPoint());
552						}
553					}
554				}
555			}
556		}
557
558		public void mouseReleased(MouseEvent evt)
559		{
560			// ignore double clicks
561			if(evt.getClickCount() % 2 == 0)
562				return;
563
564			int row = parentDirectories.locationToIndex(evt.getPoint());
565			if(row != -1)
566			{
567				Object obj = parentDirectories.getModel()
568					.getElementAt(row);
569				if(obj instanceof VFS.DirectoryEntry)
570				{
571					VFS.DirectoryEntry dirEntry = ((VFS.DirectoryEntry)obj);
572					if(!GUIUtilities.isPopupTrigger(evt))
573					{
574						browser.setDirectory(dirEntry.path);
575						focusOnFileView();
576					}
577				}
578			}
579		}
580	} //}}}
581
582	//{{{ BrowserJTree class
583	class BrowserJTree extends JTree
584	{
585		//{{{ BrowserJTree constructor
586		BrowserJTree(TreeModel model)
587		{
588			super(model);
589			ToolTipManager.sharedInstance().registerComponent(this);
590		} //}}}
591
592		//{{{ getToolTipText() method
593		public final String getToolTipText(MouseEvent evt)
594		{
595			TreePath path = getPathForLocation(evt.getX(), evt.getY());
596			if(path != null)
597			{
598				Rectangle cellRect = getPathBounds(path);
599				if(cellRect != null && !cellRectIsVisible(cellRect))
600					return path.getLastPathComponent().toString();
601			}
602			return null;
603		} //}}}
604
605		//{{{ getToolTipLocation() method
606		public final Point getToolTipLocation(MouseEvent evt)
607		{
608			TreePath path = getPathForLocation(evt.getX(), evt.getY());
609			if(path != null)
610			{
611				Rectangle cellRect = getPathBounds(path);
612				if(cellRect != null && !cellRectIsVisible(cellRect))
613				{
614					return new Point(cellRect.x + (showIcons ? 14 : - 4),
615						cellRect.y);
616				}
617			}
618			return null;
619		} //}}}
620
621		//{{{ processKeyEvent() method
622		public void processKeyEvent(KeyEvent evt)
623		{
624			// could make things somewhat easier...
625			// ... but KeyEventWorkaround 'output contract' will
626			// change in 4.1, so not a good idea
627			//evt = KeyEventWorkaround.processKeyEvent(evt);
628			//if(evt == null)
629			//	return;
630
631			if(evt.getID() == KeyEvent.KEY_PRESSED)
632			{
633				switch(evt.getKeyCode())
634				{
635				case KeyEvent.VK_UP:
636				case KeyEvent.VK_DOWN:
637					super.processKeyEvent(evt);
638					if(browser.getMode() != VFSBrowser.BROWSER)
639						browser.filesSelected();
640					break;
641				case KeyEvent.VK_ENTER:
642					browser.filesActivated((evt.isShiftDown()
643						? VFSBrowser.M_OPEN_NEW_VIEW
644						: VFSBrowser.M_OPEN),false);
645					evt.consume();
646					break;
647				case KeyEvent.VK_LEFT:
648					String directory = browser.getDirectory();
649					browser.setDirectory(MiscUtilities
650						.getParentOfPath(directory));
651					evt.consume();
652					break;
653				}
654			}
655			else if(evt.getID() == KeyEvent.KEY_TYPED)
656			{
657				if(evt.isControlDown() || evt.isAltDown()
658					|| evt.isMetaDown())
659				{
660					return;
661				}
662
663				// hack...
664				if(evt.isShiftDown() && evt.getKeyChar() == '\n')
665					return;
666
667				switch(evt.getKeyChar())
668				{
669				case '~':
670					browser.setDirectory(System.getProperty("user.home"));
671					break;
672				case '/':
673					browser.rootDirectory();
674					break;
675				case '-':
676					View view = browser.getView();
677					Buffer buffer = view.getBuffer();
678					browser.setDirectory(MiscUtilities.getParentOfPath(
679						buffer.getPath()));
680					break;
681				default:
682					typeSelectBuffer.append(evt.getKeyChar());
683					doTypeSelect(typeSelectBuffer.toString(),true);
684
685					timer.stop();
686					timer.setInitialDelay(750);
687					timer.setRepeats(false);
688					timer.start();
689					break;
690				}
691
692				return;
693			}
694
695			if(!evt.isConsumed())
696				super.processKeyEvent(evt);
697		} //}}}
698
699		//{{{ processMouseEvent() method
700		protected void processMouseEvent(MouseEvent evt)
701		{
702			ToolTipManager ttm = ToolTipManager.sharedInstance();
703
704			TreePath path = getPathForLocation(evt.getX(),evt.getY());
705
706			switch(evt.getID())
707			{
708			//{{{ MOUSE_ENTERED...
709			case MouseEvent.MOUSE_ENTERED:
710				toolTipInitialDelay = ttm.getInitialDelay();
711				toolTipReshowDelay = ttm.getReshowDelay();
712				ttm.setInitialDelay(200);
713				ttm.setReshowDelay(0);
714				super.processMouseEvent(evt);
715				break; //}}}
716			//{{{ MOUSE_EXITED...
717			case MouseEvent.MOUSE_EXITED:
718				ttm.setInitialDelay(toolTipInitialDelay);
719				ttm.setReshowDelay(toolTipReshowDelay);
720				super.processMouseEvent(evt);
721				break; //}}}
722			//{{{ MOUSE_CLICKED...
723			case MouseEvent.MOUSE_CLICKED:
724				if(path != null)
725				{
726					if((evt.getModifiers() & MouseEvent.BUTTON1_MASK) != 0)
727					{
728						// A double click is not only when clickCount == 2
729						// because every other click can open a new directory
730						if(evt.getClickCount() % 2 == 0)
731						{
732							setSelectionPath(path);
733
734							// don't pass double-clicks to tree, otherwise
735							// directory nodes will be expanded and we don't
736							// want that
737							browser.filesActivated((evt.isShiftDown()
738								? VFSBrowser.M_OPEN_NEW_VIEW
739								: VFSBrowser.M_OPEN),true);
740							break;
741						}
742						else
743						{
744							if(!isPathSelected(path))
745								setSelectionPath(path);
746						}
747					}
748					if((evt.getModifiers() & MouseEvent.BUTTON2_MASK) != 0)
749					{
750						setSelectionPath(path);
751						browser.filesActivated((evt.isShiftDown()
752							? VFSBrowser.M_OPEN_NEW_VIEW
753							: VFSBrowser.M_OPEN),true);
754					}
755
756					super.processMouseEvent(evt);
757					break;
758				}
759				else if(GUIUtilities.isPopupTrigger(evt))
760					break;
761			//}}}
762			//{{{ MOUSE_PRESSED...
763			case MouseEvent.MOUSE_PRESSED:
764				if((evt.getModifiers() & MouseEvent.BUTTON1_MASK) != 0)
765				{
766					if(evt.getClickCount() % 2 == 0)
767						break;
768				}
769				else if(GUIUtilities.isPopupTrigger(evt))
770				{
771					if(popup != null && popup.isVisible())
772					{
773						popup.setVisible(false);
774						popup = null;
775						break;
776					}
777
778					if(path == null)
779						showFilePopup(null,this,evt.getPoint());
780					else
781					{
782						if(!isPathSelected(path))
783							setSelectionPath(path);
784
785						showFilePopup(getSelectedFiles(),this,evt.getPoint());
786					}
787
788					break;
789				}
790
791				super.processMouseEvent(evt);
792				break;
793			//}}}
794			//{{{ MOUSE_RELEASED...
795			case MouseEvent.MOUSE_RELEASED:
796				if(!GUIUtilities.isPopupTrigger(evt)
797					&& path != null)
798				{
799					browser.filesSelected();
800				}
801
802				if(evt.getClickCount() % 2 != 0)
803					super.processMouseEvent(evt);
804				break;
805			//}}}
806			default:
807				super.processMouseEvent(evt);
808				break;
809			}
810		} //}}}
811
812		//{{{ Private members
813		private int toolTipInitialDelay = -1;
814		private int toolTipReshowDelay = -1;
815
816		//{{{ cellRectIsVisible() method
817		private boolean cellRectIsVisible(Rectangle cellRect)
818		{
819			Rectangle vr = BrowserJTree.this.getVisibleRect();
820			return vr.contains(cellRect.x,cellRect.y) &&
821				vr.contains(cellRect.x + cellRect.width,
822				cellRect.y + cellRect.height);
823		} //}}}
824
825		//{{{ doTypeSelect() method
826		void doTypeSelect(String str, boolean ignoreCase)
827		{
828			if(getSelectionCount() == 0)
829				doTypeSelect(str,0,getRowCount(),ignoreCase);
830			else
831			{
832				int start = getMaxSelectionRow();
833				boolean retVal = doTypeSelect(str,start,getRowCount(),
834					ignoreCase);
835
836				if(!retVal)
837				{
838					// scan from selection to end failed, so
839					// scan from start to selection
840					doTypeSelect(str,0,start,ignoreCase);
841				}
842			}
843		} //}}}
844
845		//{{{ doTypeSelect() method
846		private boolean doTypeSelect(String str, int start, int end,
847			boolean ignoreCase)
848		{
849			for(int i = start; i < end; i++)
850			{
851				DefaultMutableTreeNode treeNode = (DefaultMutableTreeNode)
852					getPathForRow(i).getLastPathComponent();
853				Object obj = treeNode.getUserObject();
854				if(obj instanceof VFS.DirectoryEntry)
855				{
856					VFS.DirectoryEntry file = (VFS.DirectoryEntry)obj;
857					if(file.name.regionMatches(ignoreCase,
858						0,str,0,str.length()))
859					{
860						setSelectionRow(i);
861						scrollRowToVisible(i);
862						return true;
863					}
864				}
865			}
866
867			return false;
868		} //}}}
869
870		//}}}
871	} //}}}
872
873	//{{{ TreeHandler class
874	class TreeHandler implements TreeExpansionListener
875	{
876		//{{{ treeExpanded() method
877		public void treeExpanded(TreeExpansionEvent evt)
878		{
879			TreePath path = evt.getPath();
880			DefaultMutableTreeNode treeNode = (DefaultMutableTreeNode)
881				path.getLastPathComponent();
882			Object userObject = treeNode.getUserObject();
883			if(userObject instanceof VFS.DirectoryEntry)
884			{
885				loadDirectory(treeNode,((VFS.DirectoryEntry)
886					userObject).path,true);
887			}
888		} //}}}
889
890		//{{{ treeCollapsed() method
891		public void treeCollapsed(TreeExpansionEvent evt)
892		{
893			TreePath path = evt.getPath();
894			DefaultMutableTreeNode treeNode = (DefaultMutableTreeNode)
895				path.getLastPathComponent();
896			if(treeNode.getUserObject() instanceof VFS.DirectoryEntry)
897			{
898				// we add the placeholder so that the node has
899				// 1 child (otherwise the user won't be able to
900				// expand it again)
901				treeNode.removeAllChildren();
902				treeNode.add(new DefaultMutableTreeNode(new LoadingPlaceholder(),false));
903				model.reload(treeNode);
904			}
905		} //}}}
906	} //}}}
907
908	static class LoadingPlaceholder {}
909	//}}}
910}