PageRenderTime 207ms CodeModel.GetById 152ms app.highlight 47ms RepoModel.GetById 1ms app.codeStats 0ms

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

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