PageRenderTime 246ms CodeModel.GetById 129ms app.highlight 103ms RepoModel.GetById 1ms app.codeStats 0ms

/jEdit/tags/jedit-4-2-pre4/org/gjt/sp/jedit/browser/VFSBrowser.java

#
Java | 1626 lines | 1178 code | 204 blank | 244 comment | 228 complexity | a6a4428b5c134c498f74f93253a1f717 MD5 | raw file
   1/*
   2 * VFSBrowser.java - VFS browser
   3 * :tabSize=8:indentSize=8:noTabs=false:
   4 * :folding=explicit:collapseFolds=1:
   5 *
   6 * Copyright (C) 2000, 2003 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 bsh.*;
  27import gnu.regexp.*;
  28import javax.swing.border.EmptyBorder;
  29import javax.swing.event.*;
  30import javax.swing.tree.DefaultMutableTreeNode;
  31import javax.swing.*;
  32import java.awt.event.*;
  33import java.awt.*;
  34import java.io.File;
  35import java.util.*;
  36import org.gjt.sp.jedit.io.*;
  37import org.gjt.sp.jedit.gui.*;
  38import org.gjt.sp.jedit.msg.*;
  39import org.gjt.sp.jedit.search.*;
  40import org.gjt.sp.jedit.*;
  41import org.gjt.sp.util.Log;
  42//}}}
  43
  44/**
  45 * The main class of the VFS browser.
  46 * @author Slava Pestov
  47 * @version $Id: VFSBrowser.java 4842 2003-08-04 00:23:07Z spestov $
  48 */
  49public class VFSBrowser extends JPanel implements EBComponent, DefaultFocusComponent
  50{
  51	public static final String NAME = "vfs.browser";
  52
  53	//{{{ Browser types
  54	/**
  55	 * Open file dialog mode. Equals JFileChooser.OPEN_DIALOG for
  56	 * backwards compatibility.
  57	 */
  58	public static final int OPEN_DIALOG = 0;
  59
  60	/**
  61	 * Save file dialog mode. Equals JFileChooser.SAVE_DIALOG for
  62	 * backwards compatibility.
  63	 */
  64	public static final int SAVE_DIALOG = 1;
  65	/**
  66	 * Choose directory dialog mode.
  67	 */
  68	public static final int BROWSER_DIALOG = 4;
  69	/**
  70	 * Choose directory dialog mode.
  71	 */
  72	public static final int CHOOSE_DIRECTORY_DIALOG = 3;
  73
  74	/**
  75	 * Stand-alone browser mode.
  76	 */
  77	public static final int BROWSER = 2;
  78	//}}}
  79
  80	//{{{ browseDirectoryInNewWindow() method
  81	/**
  82	 * Opens the specified directory in a new, floating, file system browser.
  83	 * @param view The view
  84	 * @param path The directory's path
  85	 * @since jEdit 4.1pre2
  86	 */
  87	public static void browseDirectoryInNewWindow(View view, String path)
  88	{
  89		DockableWindowManager wm = view.getDockableWindowManager();
  90		if(path != null)
  91		{
  92			// this is such a bad way of doing it, but oh well...
  93			jEdit.setTemporaryProperty("vfs.browser.path.tmp",path);
  94		}
  95		wm.floatDockableWindow("vfs.browser");
  96		jEdit.unsetProperty("vfs.browser.path.tmp");
  97	} //}}}
  98
  99	//{{{ browseDirectory() method
 100	/**
 101	 * Opens the specified directory in a file system browser.
 102	 * @param view The view
 103	 * @param path The directory's path
 104	 * @since jEdit 4.0pre3
 105	 */
 106	public static void browseDirectory(View view, String path)
 107	{
 108		DockableWindowManager wm = view.getDockableWindowManager();
 109		VFSBrowser browser = (VFSBrowser)wm.getDockable(NAME);
 110		if(browser != null)
 111		{
 112			wm.showDockableWindow(NAME);
 113			browser.setDirectory(path);
 114		}
 115		else
 116		{
 117			if(path != null)
 118			{
 119				// this is such a bad way of doing it, but oh well...
 120				jEdit.setTemporaryProperty("vfs.browser.path.tmp",path);
 121			}
 122			wm.addDockableWindow("vfs.browser");
 123			jEdit.unsetProperty("vfs.browser.path.tmp");
 124		}
 125	} //}}}
 126
 127	//{{{ getActionContext() method
 128	/**
 129	 * Returns the browser action context.
 130	 * @since jEdit 4.2pre1
 131	 */
 132	public static ActionContext getActionContext()
 133	{
 134		return actionContext;
 135	} //}}}
 136
 137	//{{{ VFSBrowser constructor
 138	/**
 139	 * Creates a new VFS browser.
 140	 * @param view The view to open buffers in by default
 141	 */
 142	public VFSBrowser(View view, String position)
 143	{
 144		this(view,null,BROWSER,true,position);
 145	} //}}}
 146
 147	//{{{ VFSBrowser constructor
 148	/**
 149	 * Creates a new VFS browser.
 150	 * @param view The view to open buffers in by default
 151	 * @param path The path to display
 152	 * @param mode The browser mode
 153	 * @param multipleSelection True if multiple selection should be allowed
 154	 * @param position Where the browser is located
 155	 * @since jEdit 4.2pre1
 156	 */
 157	public VFSBrowser(View view, String path, int mode,
 158		boolean multipleSelection, String position)
 159	{
 160		super(new BorderLayout());
 161
 162		listenerList = new EventListenerList();
 163
 164		this.mode = mode;
 165		this.multipleSelection = multipleSelection;
 166		this.floating = floating;
 167		this.view = view;
 168
 169		currentEncoding = jEdit.getProperty("buffer.encoding",
 170			System.getProperty("file.encoding"));
 171
 172		ActionHandler actionHandler = new ActionHandler();
 173
 174		Box topBox = new Box(BoxLayout.Y_AXIS);
 175
 176		horizontalLayout = (mode != BROWSER
 177			|| DockableWindowManager.TOP.equals(position)
 178			|| DockableWindowManager.BOTTOM.equals(position));
 179
 180		toolbarBox = new Box(horizontalLayout
 181			? BoxLayout.X_AXIS
 182			: BoxLayout.Y_AXIS);
 183
 184		topBox.add(toolbarBox);
 185
 186		GridBagLayout layout = new GridBagLayout();
 187		JPanel pathAndFilterPanel = new JPanel(layout);
 188
 189		GridBagConstraints cons = new GridBagConstraints();
 190		cons.gridwidth = cons.gridheight = 1;
 191		cons.gridx = cons.gridy = 0;
 192		cons.fill = GridBagConstraints.BOTH;
 193		cons.anchor = GridBagConstraints.EAST;
 194		JLabel label = new JLabel(jEdit.getProperty("vfs.browser.path"),
 195			SwingConstants.RIGHT);
 196		label.setBorder(new EmptyBorder(0,0,0,12));
 197		layout.setConstraints(label,cons);
 198		pathAndFilterPanel.add(label);
 199
 200		pathField = new HistoryTextField("vfs.browser.path");
 201		pathField.setInstantPopups(true);
 202		pathField.setEnterAddsToHistory(false);
 203		pathField.setSelectAllOnFocus(true);
 204
 205		if(floating)
 206		{
 207			label.setDisplayedMnemonic(jEdit.getProperty(
 208				"vfs.browser.path.mnemonic").charAt(0));
 209			label.setLabelFor(pathField);
 210		}
 211
 212		// because its preferred size can be quite wide, we
 213		// don't want it to make the browser way too big,
 214		// so set the preferred width to 0.
 215		Dimension prefSize = pathField.getPreferredSize();
 216		prefSize.width = 0;
 217		pathField.setPreferredSize(prefSize);
 218		pathField.addActionListener(actionHandler);
 219		cons.gridx = 1;
 220		cons.weightx = 1.0f;
 221
 222		layout.setConstraints(pathField,cons);
 223		pathAndFilterPanel.add(pathField);
 224
 225		filterCheckbox = new JCheckBox(jEdit.getProperty("vfs.browser.filter"));
 226		filterCheckbox.setMargin(new Insets(0,0,0,0));
 227		filterCheckbox.setRequestFocusEnabled(false);
 228		filterCheckbox.setBorder(new EmptyBorder(0,0,0,12));
 229		filterCheckbox.setSelected(jEdit.getBooleanProperty(
 230			"vfs.browser.filter-enabled"));
 231
 232		filterCheckbox.addActionListener(actionHandler);
 233
 234		if(mode != CHOOSE_DIRECTORY_DIALOG)
 235		{
 236			cons.gridx = 0;
 237			cons.weightx = 0.0f;
 238			cons.gridy = 1;
 239			layout.setConstraints(filterCheckbox,cons);
 240			pathAndFilterPanel.add(filterCheckbox);
 241		}
 242
 243		filterField = new HistoryTextField("vfs.browser.filter");
 244		filterField.setInstantPopups(true);
 245		filterField.setSelectAllOnFocus(true);
 246		filterField.addActionListener(actionHandler);
 247
 248		if(mode != CHOOSE_DIRECTORY_DIALOG)
 249		{
 250			cons.gridx = 1;
 251			cons.weightx = 1.0f;
 252			layout.setConstraints(filterField,cons);
 253			pathAndFilterPanel.add(filterField);
 254		}
 255
 256		topBox.add(pathAndFilterPanel);
 257		add(BorderLayout.NORTH,topBox);
 258
 259		add(BorderLayout.CENTER,browserView = new BrowserView(this));
 260
 261		propertiesChanged();
 262
 263		String filter;
 264		if(mode == BROWSER || !jEdit.getBooleanProperty(
 265			"vfs.browser.currentBufferFilter"))
 266		{
 267			filter = jEdit.getProperty("vfs.browser.last-filter");
 268			if(filter == null)
 269				filter = jEdit.getProperty("vfs.browser.default-filter");
 270		}
 271		else
 272		{
 273			String ext = MiscUtilities.getFileExtension(
 274				view.getBuffer().getName());
 275			if(ext.length() == 0)
 276				filter = jEdit.getProperty("vfs.browser.default-filter");
 277			else
 278				filter = "*" + ext;
 279		}
 280
 281		filterField.setText(filter);
 282		filterField.addCurrentToHistory();
 283
 284		updateFilterEnabled();
 285
 286		// see VFSBrowser.browseDirectory()
 287		if(path == null)
 288			path = jEdit.getProperty("vfs.browser.path.tmp");
 289
 290		if(path == null || path.length() == 0)
 291		{
 292			String userHome = System.getProperty("user.home");
 293			String defaultPath = jEdit.getProperty("vfs.browser.defaultPath");
 294			if(defaultPath.equals("home"))
 295				path = userHome;
 296			else if(defaultPath.equals("working"))
 297				path = System.getProperty("user.dir");
 298			else if(defaultPath.equals("buffer"))
 299			{
 300				if(view != null)
 301				{
 302					Buffer buffer = view.getBuffer();
 303					path = buffer.getDirectory();
 304				}
 305				else
 306					path = userHome;
 307			}
 308			else if(defaultPath.equals("last"))
 309			{
 310				HistoryModel pathModel = HistoryModel.getModel("vfs.browser.path");
 311				if(pathModel.getSize() == 0)
 312					path = "~";
 313				else
 314					path = pathModel.getItem(0);
 315			}
 316			else if(defaultPath.equals("favorites"))
 317				path = "favorites:";
 318			else
 319			{
 320				// unknown value??!!!
 321				path = userHome;
 322			}
 323		}
 324
 325		final String _path = path;
 326
 327		SwingUtilities.invokeLater(new Runnable()
 328		{
 329			public void run()
 330			{
 331				setDirectory(_path);
 332			}
 333		});
 334	} //}}}
 335
 336	//{{{ focusOnDefaultComponent() method
 337	public void focusOnDefaultComponent()
 338	{
 339		browserView.focusOnFileView();
 340	} //}}}
 341
 342	//{{{ addNotify() method
 343	public void addNotify()
 344	{
 345		super.addNotify();
 346		EditBus.addToBus(this);
 347	} //}}}
 348
 349	//{{{ removeNotify() method
 350	public void removeNotify()
 351	{
 352		super.removeNotify();
 353		jEdit.setBooleanProperty("vfs.browser.filter-enabled",
 354			filterCheckbox.isSelected());
 355		if(mode == BROWSER || !jEdit.getBooleanProperty(
 356			"vfs.browser.currentBufferFilter"))
 357		{
 358			jEdit.setProperty("vfs.browser.last-filter",
 359				filterField.getText());
 360		}
 361		EditBus.removeFromBus(this);
 362	} //}}}
 363
 364	//{{{ handleMessage() method
 365	public void handleMessage(EBMessage msg)
 366	{
 367		if(msg instanceof PropertiesChanged)
 368			propertiesChanged();
 369		else if(msg instanceof BufferUpdate)
 370		{
 371			BufferUpdate bmsg = (BufferUpdate)msg;
 372			if(bmsg.getWhat() == BufferUpdate.CREATED
 373				|| bmsg.getWhat() == BufferUpdate.CLOSED)
 374				browserView.updateFileView();
 375			// hacked BufferIORequest to send VFSUpdates in case
 376			// two stage save is off now...
 377			/* else if(bmsg.getWhat() == BufferUpdate.SAVED)
 378			{
 379				maybeReloadDirectory(MiscUtilities.getParentOfPath(
 380					bmsg.getBuffer().getPath()));
 381			} */
 382		}
 383		else if(msg instanceof VFSUpdate)
 384		{
 385			maybeReloadDirectory(((VFSUpdate)msg).getPath());
 386		}
 387	} //}}}
 388
 389	//{{{ getView() method
 390	public View getView()
 391	{
 392		return view;
 393	} //}}}
 394
 395	//{{{ getMode() method
 396	public int getMode()
 397	{
 398		return mode;
 399	} //}}}
 400
 401	//{{{ isMultipleSelectionEnabled() method
 402	public boolean isMultipleSelectionEnabled()
 403	{
 404		return multipleSelection;
 405	} //}}}
 406
 407	//{{{ isHorizontalLayout() method
 408	public boolean isHorizontalLayout()
 409	{
 410		return horizontalLayout;
 411	} //}}}
 412
 413	//{{{ getShowHiddenFiles() method
 414	public boolean getShowHiddenFiles()
 415	{
 416		return showHiddenFiles;
 417	} //}}}
 418
 419	//{{{ setShowHiddenFiles() method
 420	public void setShowHiddenFiles(boolean showHiddenFiles)
 421	{
 422		this.showHiddenFiles = showHiddenFiles;
 423	} //}}}
 424
 425	//{{{ getFilenameFilter() method
 426	/**
 427	 * Returns the file name filter glob.
 428	 * @since jEdit 3.2pre2
 429	 */
 430	public String getFilenameFilter()
 431	{
 432		if(filterCheckbox.isSelected())
 433		{
 434			String filter = filterField.getText();
 435			if(filter.length() == 0)
 436				return "*";
 437			else
 438				return filter;
 439		}
 440		else
 441			return "*";
 442	} //}}}
 443
 444	//{{{ setFilenameFilter() method
 445	public void setFilenameFilter(String filter)
 446	{
 447		if(filter == null || filter.length() == 0 || filter.equals("*"))
 448			filterCheckbox.setSelected(false);
 449		else
 450		{
 451			filterCheckbox.setSelected(true);
 452			filterField.setText(filter);
 453		}
 454	} //}}}
 455
 456	//{{{ getDirectoryField() method
 457	public HistoryTextField getDirectoryField()
 458	{
 459		return pathField;
 460	} //}}}
 461
 462	//{{{ getDirectory() method
 463	public String getDirectory()
 464	{
 465		return path;
 466	} //}}}
 467
 468	//{{{ setDirectory() method
 469	public void setDirectory(String path)
 470	{
 471		if(path.startsWith("file:"))
 472			path = path.substring(5);
 473
 474		pathField.setText(path);
 475
 476		if(!startRequest())
 477			return;
 478
 479		updateFilenameFilter();
 480		browserView.saveExpansionState();
 481		browserView.loadDirectory(null,path);
 482		this.path = path;
 483
 484		VFSManager.runInAWTThread(new Runnable()
 485		{
 486			public void run()
 487			{
 488				endRequest();
 489			}
 490		});
 491	} //}}}
 492
 493	//{{{ rootDirectory() method
 494	/**
 495	 * Goes to the local drives directory.
 496	 * @since jEdit 4.0pre4
 497	 */
 498	public void rootDirectory()
 499	{
 500		if(OperatingSystem.isMacOS() || OperatingSystem.isDOSDerived())
 501			setDirectory(FileRootsVFS.PROTOCOL + ":");
 502		else
 503			setDirectory("/");
 504	} //}}}
 505
 506	//{{{ reloadDirectory() method
 507	public void reloadDirectory()
 508	{
 509		// used by FTP plugin to clear directory cache
 510		VFSManager.getVFSForPath(path).reloadDirectory(path);
 511
 512		updateFilenameFilter();
 513		browserView.saveExpansionState();
 514		browserView.loadDirectory(null,path);
 515	} //}}}
 516
 517	//{{{ delete() method
 518	/**
 519	 * Note that all files must be on the same VFS.
 520	 */
 521	public void delete(VFS.DirectoryEntry[] files)
 522	{
 523		String dialogType;
 524
 525		if(MiscUtilities.isURL(files[0].deletePath)
 526			&& FavoritesVFS.PROTOCOL.equals(
 527			MiscUtilities.getProtocolOfURL(files[0].deletePath)))
 528		{
 529			dialogType = "vfs.browser.delete-favorites";
 530		}
 531		else
 532		{
 533			dialogType = "vfs.browser.delete-confirm";
 534		}
 535
 536		StringBuffer buf = new StringBuffer();
 537		for(int i = 0; i < files.length; i++)
 538		{
 539			buf.append(files[i].path);
 540			buf.append('\n');
 541		}
 542
 543		Object[] args = { buf.toString() };
 544		int result = GUIUtilities.confirm(this,dialogType,args,
 545			JOptionPane.YES_NO_OPTION,
 546			JOptionPane.WARNING_MESSAGE);
 547		if(result != JOptionPane.YES_OPTION)
 548			return;
 549
 550		VFS vfs = VFSManager.getVFSForPath(files[0].deletePath);
 551
 552		if(!startRequest())
 553			return;
 554
 555		for(int i = 0; i < files.length; i++)
 556		{
 557			Object session = vfs.createVFSSession(files[i].deletePath,this);
 558			if(session == null)
 559				continue;
 560
 561			VFSManager.runInWorkThread(new BrowserIORequest(
 562				BrowserIORequest.DELETE,this,
 563				session,vfs,files[i].deletePath,
 564				null,null,null));
 565		}
 566
 567		VFSManager.runInAWTThread(new Runnable()
 568		{
 569			public void run()
 570			{
 571				endRequest();
 572			}
 573		});
 574	} //}}}
 575
 576	//{{{ rename() method
 577	public void rename(String from)
 578	{
 579		VFS vfs = VFSManager.getVFSForPath(from);
 580
 581		String filename = vfs.getFileName(from);
 582		String[] args = { filename };
 583		String to = GUIUtilities.input(this,"vfs.browser.rename",
 584			args,filename);
 585		if(to == null)
 586			return;
 587
 588		to = MiscUtilities.constructPath(vfs.getParentOfPath(from),to);
 589
 590		Object session = vfs.createVFSSession(from,this);
 591		if(session == null)
 592			return;
 593
 594		if(!startRequest())
 595			return;
 596
 597		VFSManager.runInWorkThread(new BrowserIORequest(
 598			BrowserIORequest.RENAME,this,
 599			session,vfs,from,to,null,null));
 600
 601		VFSManager.runInAWTThread(new Runnable()
 602		{
 603			public void run()
 604			{
 605				endRequest();
 606			}
 607		});
 608	} //}}}
 609
 610	//{{{ mkdir() method
 611	public void mkdir()
 612	{
 613		String newDirectory = GUIUtilities.input(this,"vfs.browser.mkdir",null);
 614		if(newDirectory == null)
 615			return;
 616
 617		// if a directory is selected, create new dir in there.
 618		// if a file is selected, create new dir inside its parent.
 619		VFS.DirectoryEntry[] selected = getSelectedFiles();
 620		String parent;
 621		if(selected.length == 0)
 622			parent = path;
 623		else if(selected[0].type == VFS.DirectoryEntry.FILE)
 624		{
 625			parent = selected[0].path;
 626			parent = VFSManager.getVFSForPath(parent)
 627				.getParentOfPath(parent);
 628		}
 629		else
 630			parent = selected[0].path;
 631
 632		VFS vfs = VFSManager.getVFSForPath(parent);
 633
 634		// path is the currently viewed directory in the browser
 635		newDirectory = MiscUtilities.constructPath(parent,newDirectory);
 636
 637		Object session = vfs.createVFSSession(newDirectory,this);
 638		if(session == null)
 639			return;
 640
 641		if(!startRequest())
 642			return;
 643
 644		VFSManager.runInWorkThread(new BrowserIORequest(
 645			BrowserIORequest.MKDIR,this,
 646			session,vfs,newDirectory,null,null,null));
 647
 648		VFSManager.runInAWTThread(new Runnable()
 649		{
 650			public void run()
 651			{
 652				endRequest();
 653			}
 654		});
 655	} //}}}
 656
 657	//{{{ newFile() method
 658	/**
 659	 * Creates a new file in the current directory.
 660	 * @since jEdit 4.0pre2
 661	 */
 662	public void newFile()
 663	{
 664		VFS.DirectoryEntry[] selected = getSelectedFiles();
 665		if(selected.length >= 1)
 666		{
 667			VFS.DirectoryEntry file = selected[0];
 668			if(file.type == VFS.DirectoryEntry.DIRECTORY)
 669				jEdit.newFile(view,file.path);
 670			else
 671			{
 672				VFS vfs = VFSManager.getVFSForPath(file.path);
 673				jEdit.newFile(view,vfs.getParentOfPath(file.path));
 674			}
 675		}
 676		else
 677			jEdit.newFile(view,path);
 678	} //}}}
 679
 680	//{{{ searchInDirectory() method
 681	/**
 682	 * Opens a directory search in the current directory.
 683	 * @since jEdit 4.0pre2
 684	 */
 685	public void searchInDirectory()
 686	{
 687		VFS.DirectoryEntry[] selected = getSelectedFiles();
 688		if(selected.length >= 1)
 689		{
 690			VFS.DirectoryEntry file = selected[0];
 691			searchInDirectory(file.path,file.type != VFS.DirectoryEntry.FILE);
 692		}
 693		else
 694		{
 695			searchInDirectory(this.path,true);
 696		}
 697	} //}}}
 698
 699	//{{{ searchInDirectory() method
 700	/**
 701	 * Opens a directory search in the specified directory.
 702	 * @param path The path name
 703	 * @param directory True if the path is a directory, false if it is a file
 704	 * @since jEdit 4.2pre1
 705	 */
 706	public void searchInDirectory(String path, boolean directory)
 707	{
 708		String filter;
 709
 710		if(directory)
 711		{
 712			filter = getFilenameFilter();
 713		}
 714		else
 715		{
 716			String name = MiscUtilities.getFileName(path);
 717			String ext = MiscUtilities.getFileExtension(name);
 718			filter = (ext == null || ext.length() == 0
 719				? getFilenameFilter()
 720				: "*" + ext);
 721			path = MiscUtilities.getParentOfPath(path);
 722		}
 723
 724		SearchAndReplace.setSearchFileSet(new DirectoryListSet(
 725			path,filter,true));
 726		SearchDialog.showSearchDialog(view,null,SearchDialog.DIRECTORY);
 727	} //}}}
 728
 729	//{{{ getBrowserView() method
 730	public BrowserView getBrowserView()
 731	{
 732		return browserView;
 733	} //}}}
 734
 735	//{{{ getSelectedFiles() method
 736	public VFS.DirectoryEntry[] getSelectedFiles()
 737	{
 738		return browserView.getSelectedFiles();
 739	} //}}}
 740
 741	//{{{ locateFile() method
 742	/**
 743	 * Goes to the given file's directory and selects the file in the list.
 744	 * @param path The file
 745	 * @since jEdit 4.2pre2
 746	 */
 747	public void locateFile(final String path)
 748	{
 749		if(!filenameFilter.isMatch(MiscUtilities.getFileName(path)))
 750			setFilenameFilter(null);
 751
 752		setDirectory(MiscUtilities.getParentOfPath(path));
 753		VFSManager.runInAWTThread(new Runnable()
 754		{
 755			public void run()
 756			{
 757				browserView.getTable().selectFile(path);
 758			}
 759		});
 760	} //}}}
 761
 762	//{{{ addBrowserListener() method
 763	public void addBrowserListener(BrowserListener l)
 764	{
 765		listenerList.add(BrowserListener.class,l);
 766	} //}}}
 767
 768	//{{{ removeBrowserListener() method
 769	public void removeBrowserListener(BrowserListener l)
 770	{
 771		listenerList.remove(BrowserListener.class,l);
 772	} //}}}
 773
 774	//{{{ filesActivated() method
 775	// canDoubleClickClose set to false when ENTER pressed
 776	public static final int M_OPEN = 0;
 777	public static final int M_OPEN_NEW_VIEW = 1;
 778	public static final int M_OPEN_NEW_PLAIN_VIEW = 2;
 779	public static final int M_OPEN_NEW_SPLIT = 3;
 780
 781	/**
 782	 * This method does the "double-click" handling. It is public so that
 783	 * <code>browser.actions.xml</code> can bind to it.
 784	 * @since jEdit 4.2pre2
 785	 */
 786	public void filesActivated(int mode, boolean canDoubleClickClose)
 787	{
 788		VFS.DirectoryEntry[] selectedFiles = browserView.getSelectedFiles();
 789
 790		Buffer buffer = null;
 791
 792check_selected: for(int i = 0; i < selectedFiles.length; i++)
 793		{
 794			VFS.DirectoryEntry file = selectedFiles[i];
 795
 796			if(file.type == VFS.DirectoryEntry.DIRECTORY
 797				|| file.type == VFS.DirectoryEntry.FILESYSTEM)
 798			{
 799				if(mode == M_OPEN_NEW_VIEW && this.mode == BROWSER)
 800					browseDirectoryInNewWindow(view,file.path);
 801				else
 802					setDirectory(file.path);
 803			}
 804			else if(this.mode == BROWSER || this.mode == BROWSER_DIALOG)
 805			{
 806				Buffer _buffer = jEdit.getBuffer(file.path);
 807				if(_buffer == null)
 808				{
 809					Hashtable props = new Hashtable();
 810					props.put(Buffer.ENCODING,currentEncoding);
 811					_buffer = jEdit.openFile(null,null,file.path,
 812						false,props);
 813				}
 814				else if(doubleClickClose && canDoubleClickClose
 815					&& this.mode != BROWSER_DIALOG
 816					&& selectedFiles.length == 1)
 817				{
 818					// close if this buffer is currently
 819					// visible in the view.
 820					EditPane[] editPanes = view.getEditPanes();
 821					for(int j = 0; j < editPanes.length; j++)
 822					{
 823						if(editPanes[j].getBuffer() == _buffer)
 824						{
 825							jEdit.closeBuffer(view,_buffer);
 826							return;
 827						}
 828					}
 829				}
 830
 831				if(_buffer != null)
 832					buffer = _buffer;
 833			}
 834			else
 835			{
 836				// if a file is selected in OPEN_DIALOG or
 837				// SAVE_DIALOG mode, just let the listener(s)
 838				// handle it
 839			}
 840		}
 841
 842		if(buffer != null)
 843		{
 844			switch(mode)
 845			{
 846			case M_OPEN:
 847				view.setBuffer(buffer);
 848				break;
 849			case M_OPEN_NEW_VIEW:
 850				jEdit.newView(view,buffer,false);
 851				break;
 852			case M_OPEN_NEW_PLAIN_VIEW:
 853				jEdit.newView(view,buffer,true);
 854				break;
 855			case M_OPEN_NEW_SPLIT:
 856				view.splitHorizontally().setBuffer(buffer);
 857				break;
 858			}
 859		}
 860
 861		Object[] listeners = listenerList.getListenerList();
 862		for(int i = 0; i < listeners.length; i++)
 863		{
 864			if(listeners[i] == BrowserListener.class)
 865			{
 866				BrowserListener l = (BrowserListener)listeners[i+1];
 867				l.filesActivated(this,selectedFiles);
 868			}
 869		}
 870	} //}}}
 871
 872	//{{{ Package-private members
 873	String currentEncoding;
 874
 875	//{{{ pathsEqual() method
 876	/**
 877	 * This will be made public at some stage, in the io package, but not
 878	 * yet.
 879	 */
 880	static boolean pathsEqual(String p1, String p2)
 881	{
 882		if(p1.endsWith("/") || p1.endsWith(File.separator))
 883			p1 = p1.substring(0,p1.length() - 1);
 884		if(p2.endsWith("/") || p2.endsWith(File.separator))
 885			p2 = p2.substring(0,p2.length() - 1);
 886		return p1.equals(p2);
 887	} //}}}
 888
 889	//{{{ updateFilenameFilter() method
 890	void updateFilenameFilter()
 891	{
 892		try
 893		{
 894			String filter = filterField.getText();
 895			if(filter.length() == 0)
 896				filter = "*";
 897			filenameFilter = new RE(MiscUtilities.globToRE(filter),RE.REG_ICASE);
 898		}
 899		catch(Exception e)
 900		{
 901			Log.log(Log.ERROR,VFSBrowser.this,e);
 902			String[] args = { filterField.getText(),
 903				e.getMessage() };
 904			GUIUtilities.error(this,"vfs.browser.bad-filter",args);
 905		}
 906	} //}}}
 907
 908	//{{{ directoryLoaded() method
 909	void directoryLoaded(Object node, Object[] loadInfo)
 910	{
 911		VFSManager.runInAWTThread(new DirectoryLoadedAWTRequest(
 912			node,loadInfo));
 913	} //}}}
 914
 915	//{{{ filesSelected() method
 916	void filesSelected()
 917	{
 918		VFS.DirectoryEntry[] selectedFiles = browserView.getSelectedFiles();
 919
 920		if(mode == BROWSER)
 921		{
 922			for(int i = 0; i < selectedFiles.length; i++)
 923			{
 924				VFS.DirectoryEntry file = selectedFiles[i];
 925				Buffer buffer = jEdit.getBuffer(file.path);
 926				if(buffer != null && view != null)
 927					view.setBuffer(buffer);
 928			}
 929		}
 930
 931		Object[] listeners = listenerList.getListenerList();
 932		for(int i = 0; i < listeners.length; i++)
 933		{
 934			if(listeners[i] == BrowserListener.class)
 935			{
 936				BrowserListener l = (BrowserListener)listeners[i+1];
 937				l.filesSelected(this,selectedFiles);
 938			}
 939		}
 940	} //}}}
 941
 942	//{{{ endRequest() method
 943	void endRequest()
 944	{
 945		requestRunning = false;
 946	} //}}}
 947
 948	//}}}
 949
 950	//{{{ Private members
 951
 952	private static ActionContext actionContext;
 953
 954	static
 955	{
 956		actionContext = new ActionContext()
 957		{
 958			public void invokeAction(EventObject evt,
 959				EditAction action)
 960			{
 961				VFSBrowser browser = (VFSBrowser)
 962					GUIUtilities.getComponentParent(
 963					(Component)evt.getSource(),
 964					VFSBrowser.class);
 965
 966				// in the future we will want something better,
 967				// eg. having an 'evt' object passed to
 968				// EditAction.invoke().
 969
 970				// for now, since all browser actions are
 971				// written in beanshell we set the 'browser'
 972				// variable directly.
 973				NameSpace global = BeanShell.getNameSpace();
 974				try
 975				{
 976					global.setVariable("browser",
 977						browser);
 978					global.setVariable("files",
 979						browser.getSelectedFiles());
 980
 981					action.invoke(browser.getView());
 982				}
 983				catch(UtilEvalError err)
 984				{
 985					Log.log(Log.ERROR,this,err);
 986				}
 987				finally
 988				{
 989					try
 990					{
 991						global.setVariable("browser",null);
 992						global.setVariable("files",null);
 993					}
 994					catch(UtilEvalError err)
 995					{
 996						Log.log(Log.ERROR,this,err);
 997					}
 998				}
 999			}
1000		};
1001
1002		ActionSet builtInActionSet = new ActionSet(null,null,null,
1003			jEdit.class.getResource("browser.actions.xml"));
1004		builtInActionSet.load();
1005		actionContext.addActionSet(builtInActionSet);
1006	}
1007
1008	//{{{ Instance variables
1009	private EventListenerList listenerList;
1010	private View view;
1011	private boolean floating;
1012	private boolean horizontalLayout;
1013	private String path;
1014	private HistoryTextField pathField;
1015	private JCheckBox filterCheckbox;
1016	private HistoryTextField filterField;
1017	private Box toolbarBox;
1018	private FavoritesMenuButton favorites;
1019	private BrowserView browserView;
1020	private RE filenameFilter;
1021	private int mode;
1022	private boolean multipleSelection;
1023
1024	private boolean showHiddenFiles;
1025	private boolean sortMixFilesAndDirs;
1026	private boolean sortIgnoreCase;
1027	private boolean doubleClickClose;
1028
1029	private boolean requestRunning;
1030	//}}}
1031
1032	//{{{ createMenuBar() method
1033	private JPanel createMenuBar()
1034	{
1035		JPanel menuBar = new JPanel();
1036		menuBar.setLayout(new BoxLayout(menuBar,BoxLayout.X_AXIS));
1037		menuBar.setBorder(new EmptyBorder(0,3,1,0));
1038
1039		menuBar.add(new CommandsMenuButton());
1040		menuBar.add(Box.createHorizontalStrut(3));
1041		menuBar.add(new PluginsMenuButton());
1042		menuBar.add(Box.createHorizontalStrut(3));
1043		menuBar.add(favorites = new FavoritesMenuButton());
1044
1045		return menuBar;
1046	} //}}}
1047
1048	//{{{ createToolBar() method
1049	private Box createToolBar()
1050	{
1051		if(mode == BROWSER)
1052			return GUIUtilities.loadToolBar(actionContext,
1053				"vfs.browser.toolbar-browser");
1054		else
1055			return GUIUtilities.loadToolBar(actionContext,
1056				"vfs.browser.toolbar-dialog");
1057	} //}}}
1058
1059	//{{{ propertiesChanged() method
1060	private void propertiesChanged()
1061	{
1062		showHiddenFiles = jEdit.getBooleanProperty("vfs.browser.showHiddenFiles");
1063		sortMixFilesAndDirs = jEdit.getBooleanProperty("vfs.browser.sortMixFilesAndDirs");
1064		sortIgnoreCase = jEdit.getBooleanProperty("vfs.browser.sortIgnoreCase");
1065		doubleClickClose = jEdit.getBooleanProperty("vfs.browser.doubleClickClose");
1066
1067		browserView.propertiesChanged();
1068
1069		toolbarBox.removeAll();
1070
1071		if(jEdit.getBooleanProperty("vfs.browser.showToolbar"))
1072		{
1073			Box toolbar = createToolBar();
1074			if(horizontalLayout)
1075				toolbarBox.add(toolbar);
1076			else
1077			{
1078				toolbar.add(Box.createGlue());
1079				toolbarBox.add(toolbar);
1080			}
1081		}
1082
1083		if(jEdit.getBooleanProperty("vfs.browser.showMenubar"))
1084		{
1085			JPanel menubar = createMenuBar();
1086			if(horizontalLayout)
1087			{
1088				toolbarBox.add(Box.createHorizontalStrut(6));
1089				toolbarBox.add(menubar,0);
1090			}
1091			else
1092			{
1093				menubar.add(Box.createGlue());
1094				toolbarBox.add(menubar);
1095			}
1096		}
1097		else
1098			favorites = null;
1099
1100		toolbarBox.add(Box.createGlue());
1101
1102		revalidate();
1103
1104		if(path != null)
1105			reloadDirectory();
1106	} //}}}
1107
1108	/* We do this stuff because the browser is not able to handle
1109	 * more than one request yet */
1110
1111	//{{{ startRequest() method
1112	private boolean startRequest()
1113	{
1114		if(requestRunning)
1115		{
1116			// dump stack trace for debugging purposes
1117			Log.log(Log.DEBUG,this,new Throwable("For debugging purposes"));
1118
1119			GUIUtilities.error(this,"browser-multiple-io",null);
1120			return false;
1121		}
1122		else
1123		{
1124			requestRunning = true;
1125			return true;
1126		}
1127	} //}}}
1128
1129	//{{{ updateFilterEnabled() method
1130	private void updateFilterEnabled()
1131	{
1132		filterField.setEnabled(filterCheckbox.isSelected());
1133	} //}}}
1134
1135	//{{{ maybeReloadDirectory() method
1136	private void maybeReloadDirectory(String dir)
1137	{
1138		if(MiscUtilities.isURL(dir)
1139			&& MiscUtilities.getProtocolOfURL(dir).equals(
1140			FavoritesVFS.PROTOCOL))
1141		{
1142			if(favorites != null)
1143				favorites.popup = null;
1144		}
1145
1146		// this is a dirty hack and it relies on the fact
1147		// that updates for parents are sent before updates
1148		// for the changed nodes themselves (if this was not
1149		// the case, the browser wouldn't be updated properly
1150		// on delete, etc).
1151		//
1152		// to avoid causing '> 1 request' errors, don't reload
1153		// directory if request already active
1154		if(requestRunning)
1155			return;
1156
1157		// save a file -> sends vfs update. if a VFS file dialog box
1158		// is shown from the same event frame as the save, the
1159		// VFSUpdate will be delivered before the directory is loaded,
1160		// and before the path is set.
1161		if(path != null)
1162		{
1163			try
1164			{
1165				requestRunning = true;
1166
1167				browserView.maybeReloadDirectory(dir);
1168			}
1169			finally
1170			{
1171				VFSManager.runInAWTThread(new Runnable()
1172				{
1173					public void run()
1174					{
1175						requestRunning = false;
1176					}
1177				});
1178			}
1179		}
1180	} //}}}
1181
1182	//}}}
1183
1184	//{{{ Inner classes
1185
1186	//{{{ ActionHandler class
1187	class ActionHandler implements ActionListener
1188	{
1189		public void actionPerformed(ActionEvent evt)
1190		{
1191			Object source = evt.getSource();
1192			if(source == pathField || source == filterField
1193				|| source == filterCheckbox)
1194			{
1195				updateFilterEnabled();
1196
1197				String path = pathField.getText();
1198				if(path != null)
1199					setDirectory(path);
1200
1201				browserView.focusOnFileView();
1202			}
1203		}
1204	} //}}}
1205
1206	//{{{ CommandsMenuButton class
1207	class CommandsMenuButton extends JButton
1208	{
1209		//{{{ CommandsMenuButton constructor
1210		CommandsMenuButton()
1211		{
1212			setText(jEdit.getProperty("vfs.browser.commands.label"));
1213			setIcon(GUIUtilities.loadIcon("ToolbarMenu.gif"));
1214			setHorizontalTextPosition(SwingConstants.LEADING);
1215
1216			popup = new BrowserCommandsMenu(VFSBrowser.this,null);
1217
1218			CommandsMenuButton.this.setRequestFocusEnabled(false);
1219			setMargin(new Insets(1,1,1,1));
1220			CommandsMenuButton.this.addMouseListener(new MouseHandler());
1221
1222			if(OperatingSystem.isMacOSLF())
1223				CommandsMenuButton.this.putClientProperty("JButton.buttonType","toolbar");
1224		} //}}}
1225
1226		BrowserCommandsMenu popup;
1227
1228		//{{{ MouseHandler class
1229		class MouseHandler extends MouseAdapter
1230		{
1231			public void mousePressed(MouseEvent evt)
1232			{
1233				if(!popup.isVisible())
1234				{
1235					popup.update();
1236
1237					GUIUtilities.showPopupMenu(
1238						popup,CommandsMenuButton.this,0,
1239						CommandsMenuButton.this.getHeight(),
1240						false);
1241				}
1242				else
1243				{
1244					popup.setVisible(false);
1245				}
1246			}
1247		} //}}}
1248	} //}}}
1249
1250	//{{{ PluginsMenuButton class
1251	class PluginsMenuButton extends JButton
1252	{
1253		//{{{ PluginsMenuButton constructor
1254		PluginsMenuButton()
1255		{
1256			setText(jEdit.getProperty("vfs.browser.plugins.label"));
1257			setIcon(GUIUtilities.loadIcon("ToolbarMenu.gif"));
1258			setHorizontalTextPosition(SwingConstants.LEADING);
1259
1260			PluginsMenuButton.this.setRequestFocusEnabled(false);
1261			setMargin(new Insets(1,1,1,1));
1262			PluginsMenuButton.this.addMouseListener(new MouseHandler());
1263
1264			if(OperatingSystem.isMacOSLF())
1265				PluginsMenuButton.this.putClientProperty("JButton.buttonType","toolbar");
1266		} //}}}
1267
1268		JPopupMenu popup;
1269
1270		//{{{ createPopupMenu() method
1271		private void createPopupMenu()
1272		{
1273			if(popup != null)
1274				return;
1275
1276			popup = new JPopupMenu();
1277			ActionHandler actionHandler = new ActionHandler();
1278
1279			if(getMode() == BROWSER)
1280			{
1281				popup.add(GUIUtilities.loadMenuItem("plugin-manager",false));
1282				popup.add(GUIUtilities.loadMenuItem("plugin-options",false));
1283				popup.addSeparator();
1284			}
1285			else
1286				/* we're in a modal dialog */;
1287
1288			ArrayList vec = new ArrayList();
1289
1290			//{{{ old API
1291			Enumeration enum = VFSManager.getFilesystems();
1292
1293			while(enum.hasMoreElements())
1294			{
1295				VFS vfs = (VFS)enum.nextElement();
1296				if((vfs.getCapabilities() & VFS.BROWSE_CAP) == 0)
1297					continue;
1298
1299				JMenuItem menuItem = new JMenuItem(jEdit.getProperty(
1300					"vfs." + vfs.getName() + ".label"));
1301				menuItem.setActionCommand(vfs.getName());
1302				menuItem.addActionListener(actionHandler);
1303				vec.add(menuItem);
1304			} //}}}
1305
1306			//{{{ new API
1307			EditPlugin[] plugins = jEdit.getPlugins();
1308			for(int i = 0; i < plugins.length; i++)
1309			{
1310				JMenuItem menuItem = plugins[i].createBrowserMenuItems();
1311				if(menuItem != null)
1312					vec.add(menuItem);
1313			} //}}}
1314
1315			if(vec.size() != 0)
1316			{
1317				MiscUtilities.quicksort(vec,new MiscUtilities.MenuItemCompare());
1318				for(int i = 0; i < vec.size(); i++)
1319					popup.add((JMenuItem)vec.get(i));
1320			}
1321			else
1322			{
1323				JMenuItem mi = new JMenuItem(jEdit.getProperty(
1324					"vfs.browser.plugins.no-plugins.label"));
1325				mi.setEnabled(false);
1326				popup.add(mi);
1327			}
1328		} //}}}
1329
1330		//{{{ ActionHandler class
1331		class ActionHandler implements ActionListener
1332		{
1333			public void actionPerformed(ActionEvent evt)
1334			{
1335				VFS vfs = VFSManager.getVFSByName(evt.getActionCommand());
1336				String directory = vfs.showBrowseDialog(null,
1337					VFSBrowser.this);
1338				if(directory != null)
1339					setDirectory(directory);
1340			}
1341		} //}}}
1342
1343		//{{{ MouseHandler class
1344		class MouseHandler extends MouseAdapter
1345		{
1346			public void mousePressed(MouseEvent evt)
1347			{
1348				createPopupMenu();
1349
1350				if(!popup.isVisible())
1351				{
1352					GUIUtilities.showPopupMenu(
1353						popup,PluginsMenuButton.this,0,
1354						PluginsMenuButton.this.getHeight(),
1355						false);
1356				}
1357				else
1358				{
1359					popup.setVisible(false);
1360				}
1361			}
1362		} //}}}
1363	} //}}}
1364
1365	//{{{ FavoritesMenuButton class
1366	class FavoritesMenuButton extends JButton
1367	{
1368		//{{{ FavoritesMenuButton constructor
1369		FavoritesMenuButton()
1370		{
1371			setText(jEdit.getProperty("vfs.browser.favorites.label"));
1372			setIcon(GUIUtilities.loadIcon("ToolbarMenu.gif"));
1373			setHorizontalTextPosition(SwingConstants.LEADING);
1374
1375			FavoritesMenuButton.this.setRequestFocusEnabled(false);
1376			setMargin(new Insets(1,1,1,1));
1377			FavoritesMenuButton.this.addMouseListener(new MouseHandler());
1378
1379			if(OperatingSystem.isMacOSLF())
1380				FavoritesMenuButton.this.putClientProperty("JButton.buttonType","toolbar");
1381		} //}}}
1382
1383		JPopupMenu popup;
1384
1385		//{{{ createPopupMenu() method
1386		void createPopupMenu()
1387		{
1388			popup = new JPopupMenu();
1389			ActionHandler actionHandler = new ActionHandler();
1390
1391			JMenuItem mi = new JMenuItem(
1392				jEdit.getProperty(
1393				"vfs.browser.favorites"
1394				+ ".add-to-favorites.label"));
1395			mi.setActionCommand("add-to-favorites");
1396			mi.addActionListener(actionHandler);
1397			popup.add(mi);
1398
1399			mi = new JMenuItem(
1400				jEdit.getProperty(
1401				"vfs.browser.favorites"
1402				+ ".edit-favorites.label"));
1403			mi.setActionCommand("dir@favorites:");
1404			mi.addActionListener(actionHandler);
1405			popup.add(mi);
1406
1407			popup.addSeparator();
1408
1409			VFS.DirectoryEntry[] favorites
1410				= FavoritesVFS.getFavorites();
1411			if(favorites.length == 0)
1412			{
1413				mi = new JMenuItem(
1414					jEdit.getProperty(
1415					"vfs.browser.favorites"
1416					+ ".no-favorites.label"));
1417				mi.setEnabled(false);
1418				popup.add(mi);
1419			}
1420			else
1421			{
1422				MiscUtilities.quicksort(favorites,
1423					new VFS.DirectoryEntryCompare(
1424					sortMixFilesAndDirs,
1425					sortIgnoreCase));
1426				for(int i = 0; i < favorites.length; i++)
1427				{
1428					VFS.DirectoryEntry favorite
1429						= favorites[i];
1430					mi = new JMenuItem(favorite.path);
1431					mi.setIcon(FileCellRenderer
1432						.getIconForFile(
1433						favorite,false));
1434					String cmd = (favorite.type ==
1435						VFS.DirectoryEntry.FILE
1436						? "file@" : "dir@")
1437						+ favorite.path;
1438					mi.setActionCommand(cmd);
1439					mi.addActionListener(actionHandler);
1440					popup.add(mi);
1441				}
1442			}
1443		} //}}}
1444
1445		//{{{ ActionHandler class
1446		class ActionHandler implements ActionListener
1447		{
1448			public void actionPerformed(ActionEvent evt)
1449			{
1450				String actionCommand = evt.getActionCommand();
1451				if(actionCommand.equals("add-to-favorites"))
1452				{
1453					// if any directories are selected, add
1454					// them, otherwise add current directory
1455					ArrayList toAdd = new ArrayList();
1456					VFS.DirectoryEntry[] selected = getSelectedFiles();
1457					if(selected == null || selected.length == 0)
1458					{
1459						if(path.equals(FavoritesVFS.PROTOCOL + ":"))
1460						{
1461							GUIUtilities.error(VFSBrowser.this,
1462								"vfs.browser.recurse-favorites",
1463								null);
1464						}
1465						else
1466						{
1467							FavoritesVFS.addToFavorites(path,
1468								VFS.DirectoryEntry.DIRECTORY);
1469						}
1470					}
1471					else
1472					{
1473						for(int i = 0; i < selected.length; i++)
1474						{
1475							VFS.DirectoryEntry file
1476								= selected[i];
1477							FavoritesVFS.addToFavorites(file.path,file.type);
1478						}
1479					}
1480				}
1481				else if(actionCommand.startsWith("dir@"))
1482				{
1483					setDirectory(actionCommand.substring(4));
1484				}
1485				else if(actionCommand.startsWith("file@"))
1486				{
1487					switch(getMode())
1488					{
1489					case BROWSER:
1490						jEdit.openFile(view,actionCommand.substring(5));
1491						break;
1492					default:
1493						locateFile(actionCommand.substring(5));
1494						break;
1495					}
1496				}
1497			}
1498		} //}}}
1499
1500		//{{{ MouseHandler class
1501		class MouseHandler extends MouseAdapter
1502		{
1503			public void mousePressed(MouseEvent evt)
1504			{
1505				if(popup != null && popup.isVisible())
1506				{
1507					popup.setVisible(false);
1508					return;
1509				}
1510
1511				if(popup == null)
1512					createPopupMenu();
1513
1514				GUIUtilities.showPopupMenu(
1515					popup,FavoritesMenuButton.this,0,
1516					FavoritesMenuButton.this.getHeight(),
1517					false);
1518			}
1519		} //}}}
1520	} //}}}
1521
1522	//{{{ DirectoryLoadedAWTRequest class
1523	class DirectoryLoadedAWTRequest implements Runnable
1524	{
1525		Object node;
1526		Object[] loadInfo;
1527
1528		DirectoryLoadedAWTRequest(Object node, Object[] loadInfo)
1529		{
1530			this.node = node;
1531			this.loadInfo = loadInfo;
1532		}
1533
1534		public void run()
1535		{
1536			String path = (String)loadInfo[0];
1537			VFS.DirectoryEntry[] list = (VFS.DirectoryEntry[])
1538				loadInfo[1];
1539
1540			if(node == null)
1541			{
1542				// This is the new, canonical path
1543				VFSBrowser.this.path = path;
1544				if(!pathField.getText().equals(path))
1545					pathField.setText(path);
1546				if(path.endsWith("/") ||
1547					path.endsWith(File.separator))
1548				{
1549					// ensure consistent history;
1550					// eg we don't want both
1551					// foo/ and foo
1552					path = path.substring(0,
1553						path.length() - 1);
1554				}
1555
1556				HistoryModel.getModel("vfs.browser.path")
1557					.addItem(path);
1558			}
1559
1560			boolean filterEnabled = filterCheckbox.isSelected();
1561
1562			ArrayList directoryVector = new ArrayList();
1563
1564			int directories = 0;
1565			int files = 0;
1566			int invisible = 0;
1567
1568			if(list != null)
1569			{
1570				for(int i = 0; i < list.length; i++)
1571				{
1572					VFS.DirectoryEntry file = list[i];
1573					if(file.hidden && !showHiddenFiles)
1574					{
1575						invisible++;
1576						continue;
1577					}
1578
1579					if(file.type == VFS.DirectoryEntry.FILE
1580						&& filterEnabled
1581						&& filenameFilter != null
1582						&& !filenameFilter.isMatch(file.name))
1583					{
1584						invisible++;
1585						continue;
1586					}
1587
1588					if(file.type == VFS.DirectoryEntry.FILE)
1589						files++;
1590					else
1591						directories++;
1592
1593					directoryVector.add(file);
1594				}
1595
1596				MiscUtilities.quicksort(directoryVector,
1597					new VFS.DirectoryEntryCompare(
1598					sortMixFilesAndDirs,
1599					sortIgnoreCase));
1600			}
1601
1602			browserView.directoryLoaded(node,path,
1603				directoryVector);
1604
1605			// to notify listeners that any existing
1606			// selection has been deactivated
1607
1608			// turns out under some circumstances this
1609			// method can switch the current buffer in
1610			// BROWSER mode.
1611
1612			// in any case, this is only needed for the
1613			// directory chooser (why?), so we add a
1614			// check. otherwise poor Rick will go insane.
1615			if(mode == CHOOSE_DIRECTORY_DIALOG)
1616				filesSelected();
1617		}
1618
1619		public String toString()
1620		{
1621			return (String)loadInfo[0];
1622		}
1623	} //}}}
1624
1625	//}}}
1626}