PageRenderTime 97ms CodeModel.GetById 20ms app.highlight 62ms RepoModel.GetById 1ms app.codeStats 1ms

/jEdit/tags/jedit-4-5-pre1/org/gjt/sp/jedit/browser/VFSBrowser.java

#
Java | 2155 lines | 1529 code | 257 blank | 369 comment | 291 complexity | 15dc66f0952c8924559cde89524bee3c MD5 | raw file

Large files files are truncated, but you can click here to view the full 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 org.gjt.sp.jedit.EditBus.EBHandler;
  27import org.gjt.sp.jedit.bsh.*;
  28import javax.swing.border.EmptyBorder;
  29import javax.swing.event.*;
  30import javax.swing.*;
  31import java.awt.event.*;
  32import java.awt.*;
  33import java.io.File;
  34import java.util.*;
  35import java.util.List;
  36import java.util.concurrent.CountDownLatch;
  37
  38import org.gjt.sp.jedit.io.*;
  39import org.gjt.sp.jedit.gui.*;
  40import org.gjt.sp.jedit.msg.*;
  41import org.gjt.sp.jedit.search.*;
  42import org.gjt.sp.jedit.*;
  43import org.gjt.sp.jedit.buffer.JEditBuffer;
  44import org.gjt.sp.util.*;
  45import org.gjt.sp.jedit.menu.MenuItemTextComparator;
  46//}}}
  47
  48/**
  49 * The main class of the VFS browser.
  50 * Used as dockable, and also embedded inside the
  51 * VFSFileChooserDialog.
  52 * 
  53 * @author Slava Pestov
  54 * @version $Id: VFSBrowser.java 19949 2011-09-10 08:37:05Z kpouer $
  55 */
  56public class VFSBrowser extends JPanel implements DefaultFocusComponent,
  57	DockableWindow
  58{
  59	public static final String NAME = "vfs.browser";
  60
  61	//{{{ Browser modes
  62	/**
  63	 * Open file dialog mode. Equals JFileChooser.OPEN_DIALOG for
  64	 * backwards compatibility.
  65	 */
  66	public static final int OPEN_DIALOG = 0;
  67
  68	/**
  69	 * Save file dialog mode. Equals JFileChooser.SAVE_DIALOG for
  70	 * backwards compatibility.
  71	 */
  72	public static final int SAVE_DIALOG = 1;
  73	/**
  74	 * Choose directory dialog mode.
  75	 */
  76	public static final int BROWSER_DIALOG = 4;
  77	/**
  78	 * Choose directory dialog mode.
  79	 */
  80	public static final int CHOOSE_DIRECTORY_DIALOG = 3;
  81
  82	/**
  83	 * Stand-alone dockable browser mode.
  84	 */
  85	public static final int BROWSER = 2;
  86	//}}}
  87
  88	//{{{ browseDirectoryInNewWindow() method
  89	/**
  90	 * Opens the specified directory in a new, floating, file system browser.
  91	 * @param view The view
  92	 * @param path The directory's path
  93	 * @since jEdit 4.1pre2
  94	 */
  95	public static void browseDirectoryInNewWindow(View view, String path)
  96	{
  97		DockableWindowManager wm = view.getDockableWindowManager();
  98		if(path != null)
  99		{
 100			// this is such a bad way of doing it, but oh well...
 101			jEdit.setTemporaryProperty("vfs.browser.path.tmp",path);
 102		}
 103		wm.floatDockableWindow("vfs.browser");
 104		jEdit.unsetProperty("vfs.browser.path.tmp");
 105	} //}}}
 106
 107	//{{{ browseDirectory() method
 108	/**
 109	 * Opens the specified directory in a file system browser.
 110	 * @param view The view
 111	 * @param path The directory's path
 112	 * @since jEdit 4.0pre3
 113	 */
 114	public static void browseDirectory(View view, String path)
 115	{
 116		DockableWindowManager wm = view.getDockableWindowManager();
 117		VFSBrowser browser = (VFSBrowser)wm.getDockable(NAME);
 118		if(browser != null)
 119		{
 120			wm.showDockableWindow(NAME);
 121			browser.setDirectory(path);
 122		}
 123		else
 124		{
 125			if(path != null)
 126			{
 127				// this is such a bad way of doing it, but oh well...
 128				jEdit.setTemporaryProperty("vfs.browser.path.tmp",path);
 129			}
 130			wm.addDockableWindow("vfs.browser");
 131			jEdit.unsetProperty("vfs.browser.path.tmp");
 132		}
 133	} //}}}
 134
 135	//{{{ getActionContext() method
 136	/**
 137	 * Returns the browser action context.
 138	 * @since jEdit 4.2pre1
 139	 */
 140	public static ActionContext getActionContext()
 141	{
 142		return actionContext;
 143	} //}}}
 144
 145	//{{{ VFSBrowser constructor
 146	/**
 147	 * Creates a new VFS browser.
 148	 * @param view The view to open buffers in by default
 149	 */
 150	public VFSBrowser(View view, String position)
 151	{
 152		this(view,null,BROWSER,true,position);
 153	} //}}}
 154
 155	//{{{ VFSBrowser constructor
 156	/**
 157	 * Creates a new VFS browser.
 158	 * @param view The view to open buffers in by default
 159	 * @param path The path to display
 160	 * @param mode The browser mode
 161	 * @param multipleSelection True if multiple selection should be allowed
 162	 * @param position Where the browser is located
 163	 * @since jEdit 4.2pre1
 164	 */
 165	public VFSBrowser(View view, String path, int mode,
 166		boolean multipleSelection, String position)
 167	{
 168		super(new BorderLayout());
 169
 170		listenerList = new EventListenerList();
 171
 172		this.mode = mode;
 173		this.multipleSelection = multipleSelection;
 174		this.view = view;
 175
 176		
 177		currentEncoding = null;
 178		autoDetectEncoding = jEdit.getBooleanProperty(
 179			"buffer.encodingAutodetect");
 180
 181		ActionHandler actionHandler = new ActionHandler();
 182
 183		topBox = new Box(BoxLayout.Y_AXIS);
 184		horizontalLayout = mode != BROWSER
 185			|| DockableWindowManager.TOP.equals(position)
 186			|| DockableWindowManager.BOTTOM.equals(position);
 187
 188		toolbarBox = new Box(horizontalLayout
 189			? BoxLayout.X_AXIS
 190			: BoxLayout.Y_AXIS);
 191
 192		topBox.add(toolbarBox);
 193
 194		GridBagLayout layout = new GridBagLayout();
 195		pathAndFilterPanel = new JPanel(layout);
 196		if(isHorizontalLayout())
 197			pathAndFilterPanel.setBorder(new EmptyBorder(12,12,12,12));
 198
 199		GridBagConstraints cons = new GridBagConstraints();
 200		cons.gridwidth = cons.gridheight = 1;
 201		cons.gridx = cons.gridy = 0;
 202		cons.fill = GridBagConstraints.BOTH;
 203		cons.anchor = GridBagConstraints.EAST;
 204		JLabel label = new JLabel(jEdit.getProperty("vfs.browser.path"),
 205			SwingConstants.RIGHT);
 206		label.setBorder(new EmptyBorder(0,0,0,12));
 207		layout.setConstraints(label,cons);
 208		pathAndFilterPanel.add(label);
 209
 210		pathField = new HistoryTextField("vfs.browser.path");
 211		pathField.setName("path");
 212		pathField.setInstantPopups(true);
 213		pathField.setEnterAddsToHistory(false);
 214		pathField.setSelectAllOnFocus(true);
 215		
 216
 217		// because its preferred size can be quite wide, we
 218		// don't want it to make the browser way too big,
 219		// so set the preferred width to 0.
 220		Dimension prefSize = pathField.getPreferredSize();
 221		prefSize.width = 0;
 222		pathField.setPreferredSize(prefSize);
 223		pathField.addActionListener(actionHandler);
 224		cons.gridx = 1;
 225		cons.weightx = 1.0;
 226		cons.gridwidth = GridBagConstraints.REMAINDER;
 227
 228		layout.setConstraints(pathField,cons);
 229		pathAndFilterPanel.add(pathField);
 230
 231		filterCheckbox = new JCheckBox(jEdit.getProperty("vfs.browser.filter"));
 232		filterCheckbox.setMargin(new Insets(0,0,0,0));
 233//		filterCheckbox.setRequestFocusEnabled(false);
 234		filterCheckbox.setBorder(new EmptyBorder(0,0,0,12));
 235		filterCheckbox.setSelected(jEdit.getBooleanProperty(
 236			"vfs.browser.filter-enabled"));
 237
 238		filterCheckbox.addActionListener(actionHandler);
 239		filterCheckbox.setName("filter-checkbox");
 240		if(mode != CHOOSE_DIRECTORY_DIALOG)
 241		{
 242			cons.gridwidth = 1;
 243			cons.gridx = 0;
 244			cons.weightx = 0.0;
 245			cons.gridy = 1;
 246			layout.setConstraints(filterCheckbox,cons);
 247			pathAndFilterPanel.add(filterCheckbox);
 248		}
 249
 250		filterField = new JComboBox();
 251		filterEditor = new HistoryComboBoxEditor("vfs.browser.filter");
 252		filterEditor.setToolTipText(jEdit.getProperty("glob.tooltip"));
 253		filterEditor.setInstantPopups(true);
 254		filterEditor.setSelectAllOnFocus(true);
 255		filterEditor.addActionListener(actionHandler);
 256		filterField.setName("filter-field");
 257		if (mode == BROWSER)
 258		{
 259			DockableWindowManager dwm = view.getDockableWindowManager();
 260			KeyListener keyListener = dwm.closeListener(NAME);
 261			filterCheckbox.addKeyListener(keyListener);	
 262			addKeyListener(keyListener);
 263			filterEditor.addKeyListener(keyListener);
 264			pathField.addKeyListener(keyListener);
 265			// save the location on close of dockable.
 266			pathField.addKeyListener(new KeyAdapter()
 267			{
 268				@Override
 269				public void keyReleased(KeyEvent e)
 270				{
 271					if (e.getKeyCode() == KeyEvent.VK_ESCAPE)
 272					{
 273						pathField.setText(VFSBrowser.this.path);
 274					}
 275				}
 276			});
 277		}
 278
 279		String filter;
 280		if(mode == BROWSER || !jEdit.getBooleanProperty(
 281			"vfs.browser.currentBufferFilter"))
 282		{
 283			filter = jEdit.getProperty("vfs.browser.last-filter");
 284			if(filter == null)
 285				filter = jEdit.getProperty("vfs.browser.default-filter");
 286		}
 287		else
 288		{
 289			String ext = MiscUtilities.getFileExtension(
 290				view.getBuffer().getName());
 291			if(ext.length() == 0)
 292				filter = jEdit.getProperty("vfs.browser.default-filter");
 293			else
 294				filter = '*' + ext;
 295		}
 296
 297		// filterField.getEditor().setItem(new GlobVFSFileFilter(filter));
 298		// filterField.addItem(filterField.getEditor().getItem());
 299		filterEditor.setItem(new GlobVFSFileFilter(filter));
 300		filterField.addItem(filterEditor.getItem());
 301		filterField.addItemListener(actionHandler);
 302		filterField.setRenderer(new VFSFileFilterRenderer());
 303
 304		// loads the registered VFSFileFilter services.
 305		String[] _filters = ServiceManager.getServiceNames(VFSFileFilter.SERVICE_NAME);
 306		for (int i = 0; i < _filters.length; i++)
 307		{
 308			VFSFileFilter _filter = (VFSFileFilter)
 309				ServiceManager.getService(VFSFileFilter.SERVICE_NAME, _filters[i]);
 310			filterField.addItem(_filter);
 311		}
 312
 313		if(mode != CHOOSE_DIRECTORY_DIALOG)
 314		{
 315			cons.gridwidth = GridBagConstraints.REMAINDER;
 316			cons.fill = GridBagConstraints.HORIZONTAL;
 317			cons.gridx = 1;
 318			cons.weightx = 1.0;
 319			if (filterField.getItemCount() > 1)
 320			{
 321				filterField.setEditor(filterEditor);
 322				filterField.setEditable(true);
 323				layout.setConstraints(filterField,cons);
 324				pathAndFilterPanel.add(filterField);
 325			}
 326			else
 327			{
 328				layout.setConstraints(filterEditor,cons);
 329				pathAndFilterPanel.add(filterEditor);
 330			}
 331		}
 332
 333		topBox.add(pathAndFilterPanel);
 334		add(BorderLayout.NORTH,topBox);
 335
 336		add(BorderLayout.CENTER,browserView = new BrowserView(this));
 337		if(isHorizontalLayout())
 338			browserView.setBorder(new EmptyBorder(0,12,0,12));
 339		defaultFocusComponent = browserView.getTable();
 340		propertiesChanged();
 341
 342		updateFilterEnabled();
 343
 344		setFocusTraversalPolicy(new LayoutFocusTraversalPolicy());
 345		// see VFSBrowser.browseDirectory()
 346		if(path == null)
 347			path = jEdit.getProperty("vfs.browser.path.tmp");
 348
 349		if(path == null || path.isEmpty())
 350		{
 351			String userHome = System.getProperty("user.home");
 352			String defaultPath = jEdit.getProperty("vfs.browser.defaultPath");
 353			if("home".equals(defaultPath))
 354				path = userHome;
 355			else if("working".equals(defaultPath))
 356				path = System.getProperty("user.dir");
 357			else if("buffer".equals(defaultPath))
 358			{
 359				Buffer buffer = view.getBuffer();
 360				boolean browseable = (buffer.getVFS().getCapabilities() & VFS.BROWSE_CAP) != 0;
 361				if (browseable)
 362					path = buffer.getDirectory();
 363			}
 364			else if("last".equals(defaultPath))
 365			{
 366				HistoryModel pathModel = HistoryModel.getModel("vfs.browser.path");
 367				if(pathModel.getSize() == 0)
 368					path = "~";
 369				else
 370					path = pathModel.getItem(0);
 371			}
 372			else if("favorites".equals(defaultPath))
 373				path = "favorites:";
 374
 375			if (path == null || path.isEmpty())
 376			{
 377				// unknown value??!!!
 378				path = userHome;
 379			}
 380		}
 381
 382		final String _path = path;
 383
 384		ThreadUtilities.runInDispatchThread(new Runnable()
 385		{
 386			@Override
 387			public void run()
 388			{
 389				setDirectory(_path);
 390			}
 391		});
 392	} //}}}
 393
 394	//{{{ focusOnDefaultComponent() method
 395	@Override
 396	public void focusOnDefaultComponent()
 397	{
 398		// pathField.requestFocus();		
 399		defaultFocusComponent.requestFocus();
 400	} //}}}
 401
 402	// {{{ setDefaultFocusComponent()
 403	/** Only used by VFSFileChooserDialog, since it embeds this in a dialog
 404	 */
 405	void setDefaultFocusComponent(JComponent c) 
 406	{
 407		defaultFocusComponent = c;
 408	}// }}}
 409	
 410	//{{{ addNotify() method
 411	@Override
 412	public void addNotify()
 413	{
 414		super.addNotify();
 415		EditBus.addToBus(this);
 416	} //}}}
 417
 418	//{{{ removeNotify() method
 419	@Override
 420	public void removeNotify()
 421	{
 422		super.removeNotify();
 423		jEdit.setBooleanProperty("vfs.browser.filter-enabled",
 424			filterCheckbox.isSelected());
 425		if(mode == BROWSER || !jEdit.getBooleanProperty(
 426			"vfs.browser.currentBufferFilter"))
 427		{
 428			VFSFileFilter selectedFilter =
 429				(VFSFileFilter) filterField.getSelectedItem();
 430			if (selectedFilter instanceof GlobVFSFileFilter)
 431				jEdit.setProperty("vfs.browser.last-filter",
 432					((GlobVFSFileFilter)selectedFilter).getGlob());
 433		}
 434		EditBus.removeFromBus(this);
 435	} //}}}
 436
 437	//{{{ handlePropertiesChanged() method
 438	@EBHandler
 439	public void handlePropertiesChanged(PropertiesChanged msg)
 440	{
 441		propertiesChanged();
 442	} //}}}
 443
 444	//{{{ handleBufferUpdate() method
 445	@EBHandler
 446	public void handleBufferUpdate(BufferUpdate bmsg)
 447	{
 448		if (bmsg.getWhat() == BufferUpdate.CREATED ||
 449			bmsg.getWhat() == BufferUpdate.CLOSED)
 450		{
 451			browserView.updateFileView();
 452		}
 453	} //}}}
 454
 455	//{{{ handlePluginUpdate() method
 456	@EBHandler
 457	public void handlePluginUpdate(PluginUpdate pmsg)
 458	{
 459		if((pmsg.getWhat() == PluginUpdate.LOADED ||
 460		   pmsg.getWhat() == PluginUpdate.UNLOADED) &&
 461		   plugins != null /* plugins can be null if the VFSBrowser menu bar is hidden */)
 462		{
 463			plugins.updatePopupMenu();
 464		}
 465	} //}}}
 466
 467	//{{{ handleVFSUpdate() method
 468	@EBHandler
 469	public void handleVFSUpdate(VFSUpdate msg)
 470	{
 471		maybeReloadDirectory(msg.getPath());
 472	} //}}}
 473
 474	//{{{ getView() method
 475	public View getView()
 476	{
 477		return view;
 478	} //}}}
 479
 480	//{{{ getMode() method
 481	public int getMode()
 482	{
 483		return mode;
 484	} //}}}
 485
 486	//{{{ isMultipleSelectionEnabled() method
 487	public boolean isMultipleSelectionEnabled()
 488	{
 489		return multipleSelection;
 490	} //}}}
 491
 492	//{{{ isHorizontalLayout() method
 493	public boolean isHorizontalLayout()
 494	{
 495		return horizontalLayout;
 496	} //}}}
 497
 498	//{{{ getShowHiddenFiles() method
 499	public boolean getShowHiddenFiles()
 500	{
 501		return showHiddenFiles;
 502	} //}}}
 503
 504	//{{{ setShowHiddenFiles() method
 505	public void setShowHiddenFiles(boolean showHiddenFiles)
 506	{
 507		this.showHiddenFiles = showHiddenFiles;
 508	} //}}}
 509
 510	//{{{ getVFSFileFilter() method
 511	/**
 512	 * Returns the currently active VFSFileFilter.
 513	 *
 514	 * @since jEdit 4.3pre7
 515	 */
 516	public VFSFileFilter getVFSFileFilter()
 517	{
 518		if (mode == CHOOSE_DIRECTORY_DIALOG)
 519			return new DirectoriesOnlyFilter();
 520		return 	(VFSFileFilter) filterField.getSelectedItem();
 521	} //}}}
 522
 523	//{{{ addVFSFileFilter() method
 524	/**
 525	 * Adds a file filter to the browser.
 526	 *
 527	 * @since jEdit 4.3pre7
 528	 */
 529	public void addVFSFileFilter(VFSFileFilter filter)
 530	{
 531		filterField.addItem(filter);
 532		if (filterField.getItemCount() == 2)
 533		{
 534			filterField.setEditor(filterEditor);
 535			filterField.setEditable(true);
 536
 537			GridBagLayout layout = (GridBagLayout) pathAndFilterPanel.getLayout();
 538			GridBagConstraints cons =layout.getConstraints(filterEditor);
 539			cons.gridwidth = GridBagConstraints.REMAINDER;
 540			cons.fill = GridBagConstraints.HORIZONTAL;
 541			cons.gridx = 1;
 542			cons.weightx = 1;
 543
 544			pathAndFilterPanel.remove(filterEditor);
 545			layout.setConstraints(filterField, cons);
 546			pathAndFilterPanel.add(filterField);
 547			pathAndFilterPanel.validate();
 548			pathAndFilterPanel.repaint();
 549		}
 550	} //}}}
 551
 552	//{{{ setFilenameFilter() method
 553	public void setFilenameFilter(String filter)
 554	{
 555		if(filter == null || filter.length() == 0 || "*".equals(filter))
 556			filterCheckbox.setSelected(false);
 557		else
 558		{
 559			filterCheckbox.setSelected(true);
 560			filterEditor.setItem(new GlobVFSFileFilter(filter));
 561		}
 562	} //}}}
 563
 564	//{{{ getDirectoryField() method
 565	public HistoryTextField getDirectoryField()
 566	{
 567		return pathField;
 568	} //}}}
 569
 570	//{{{ getDirectory() method
 571	public String getDirectory()
 572	{
 573		return path;
 574	} //}}}
 575
 576	// {{{ Directory Stack operations
 577	/**
 578	 * @since jedit 4.3pre15
 579	 */
 580	public void previousDirectory() 
 581	{
 582		if (historyStack.size() > 1)
 583		{
 584			historyStack.pop();
 585			nextDirectoryStack.push(path);
 586			setDirectory(historyStack.peek());
 587			historyStack.pop();
 588		}
 589	}
 590	
 591	
 592	/**
 593	 * @since jEdit 4.3pre15
 594	 */
 595	public void nextDirectory() 
 596	{
 597		if (!nextDirectoryStack.isEmpty())
 598		{
 599			setDirectory(nextDirectoryStack.pop());
 600		}
 601	}
 602	// }}}	
 603	
 604	//{{{ setDirectory() method
 605	public void setDirectory(String path)
 606	{
 607		if(path.startsWith("file:"))
 608			path = path.substring(5);
 609		path = MiscUtilities.expandVariables(path);
 610		pathField.setText(path);
 611
 612		if(!startRequest())
 613			return;
 614
 615		historyStack.push(path);
 616		browserView.saveExpansionState();
 617
 618		Runnable delayedAWTRequest = new DelayedEndRequest();
 619		browserView.loadDirectory(null,path,true, delayedAWTRequest);
 620		this.path = path;
 621	} //}}}
 622
 623	//{{{ getRootDirectory() method
 624	public static String getRootDirectory()
 625	{
 626		if(OperatingSystem.isMacOS() || OperatingSystem.isDOSDerived())
 627			return FileRootsVFS.PROTOCOL + ':';
 628		else
 629			return "/";
 630	} //}}}
 631
 632	//{{{ rootDirectory() method
 633	/**
 634	 * Goes to the local drives directory.
 635	 * @since jEdit 4.0pre4
 636	 */
 637	public void rootDirectory()
 638	{
 639		setDirectory(getRootDirectory());
 640	} //}}}
 641
 642	//{{{ reloadDirectory() method
 643	public void reloadDirectory()
 644	{
 645		// used by FTP plugin to clear directory cache
 646		VFSManager.getVFSForPath(path).reloadDirectory(path);
 647
 648		browserView.saveExpansionState();
 649		browserView.loadDirectory(null,path,false);
 650	} //}}}
 651
 652	//{{{ delete() method
 653	/**
 654	 * Note that all files must be on the same VFS.
 655	 * @since jEdit 4.3pre2
 656	 */
 657	public void delete(VFSFile[] files)
 658	{
 659		String dialogType;
 660
 661		if(MiscUtilities.isURL(files[0].getDeletePath())
 662			&& FavoritesVFS.PROTOCOL.equals(
 663			MiscUtilities.getProtocolOfURL(files[0].getDeletePath())))
 664		{
 665			dialogType = "vfs.browser.delete-favorites";
 666		}
 667		else
 668		{
 669			dialogType = "vfs.browser.delete-confirm";
 670		}
 671
 672		StringBuilder buf = new StringBuilder();
 673		String typeStr = "files";
 674		for(int i = 0; i < files.length; i++)
 675		{
 676			buf.append(files[i].getPath());
 677			buf.append('\n');
 678			if (files[i].getType() == VFSFile.DIRECTORY)
 679				typeStr = "directories and their contents";
 680		}
 681
 682		Object[] args = { buf.toString(), typeStr};
 683		
 684		int result = GUIUtilities.confirm(this,dialogType,args,
 685			JOptionPane.YES_NO_OPTION,
 686			JOptionPane.WARNING_MESSAGE);
 687		if(result != JOptionPane.YES_OPTION)
 688			return;
 689
 690		VFS vfs = VFSManager.getVFSForPath(files[0].getDeletePath());
 691
 692		if(!startRequest())
 693			return;
 694
 695		final CountDownLatch latch = new CountDownLatch(files.length);
 696		for(int i = 0; i < files.length; i++)
 697		{
 698			Object session = vfs.createVFSSession(files[i].getDeletePath(),this);
 699			if(session == null)
 700			{
 701				latch.countDown();
 702				continue;
 703			}
 704
 705			final Task task = new DeleteBrowserTask(this, session, vfs, files[i].getDeletePath());
 706			TaskManager.instance.addTaskListener(new TaskAdapter()
 707			{
 708				@Override
 709				public void done(Task t)
 710				{
 711					if (task == t)
 712					{
 713						latch.countDown();
 714						TaskManager.instance.removeTaskListener(this);
 715					}
 716				}
 717			});
 718			ThreadUtilities.runInBackground(task);
 719		}
 720
 721		try
 722		{
 723			latch.await();
 724		}
 725		catch (InterruptedException e)
 726		{
 727			Log.log(Log.ERROR, this, e, e);
 728		}
 729
 730		Runnable delayedAWTRequest = new DelayedEndRequest();
 731		EventQueue.invokeLater(delayedAWTRequest);
 732	} //}}}
 733
 734	//{{{ rename() methods
 735	/**
 736	 * Rename a file.
 737	 * It will prompt for the new name.
 738	 * @param from the file to rename
 739	 * @since jEdit 4.5pre1
 740	 */
 741	public void rename(VFSFile from)
 742	{
 743		String filename;
 744		if (from instanceof FavoritesVFS.Favorite)
 745		{
 746			FavoritesVFS.Favorite favorite = (FavoritesVFS.Favorite) from;
 747			filename = favorite.getLabel();
 748		}
 749		else
 750		{
 751			filename = from.getName();
 752		}
 753		String[] args = { filename };
 754		String to = GUIUtilities.input(this,"vfs.browser.rename",
 755			args,filename);
 756		if (to == null)
 757			return;
 758		rename(from.getVFS(), from.getPath(), to);
 759	}
 760
 761	/**
 762	 * Rename a file.
 763	 * It will prompt for the new name.
 764	 * @param from the file to rename
 765	 * @param from to the target name
 766	 * @since jEdit 4.5pre1
 767	 */
 768	public void rename(VFSFile from, String to)
 769	{
 770		rename(from.getVFS(), from.getPath(), to);
 771	}
 772
 773	public void rename(String from)
 774	{
 775		VFS vfs = VFSManager.getVFSForPath(from);
 776
 777		String filename = vfs.getFileName(from);
 778		String[] args = { filename };
 779		String to = GUIUtilities.input(this,"vfs.browser.rename",
 780			args,filename);
 781		if (to == null)
 782			return;
 783		rename(from, to);
 784	}
 785
 786	/**
 787	 * Rename a file
 788	 * @param vfs the VFS. It may be strange to give the VFS, but in
 789	 * case of FavoriteVFS we cannot know that it is favorite.
 790	 * @param from the full path name of the file to be renamed
 791	 * @param newname the new name (only filename, not full path)
 792	 * @since jEdit 4.5pre1
 793	 */
 794	private void rename(VFS vfs, String from, String newname)
 795	{
 796		String filename = vfs.getFileName(from);
 797		String to = newname;
 798
 799		if(to == null)
 800			return;
 801
 802		if (!(vfs instanceof FavoritesVFS))
 803		{
 804			if (filename.equals(newname))
 805				return;
 806			to = MiscUtilities.constructPath(vfs.getParentOfPath(from),to);
 807		}
 808
 809		Object session = vfs.createVFSSession(from,this);
 810		if(session == null)
 811			return;
 812
 813		if(!startRequest())
 814			return;
 815
 816		Runnable delayedAWTRequest = new DelayedEndRequest();
 817		Task renameTask = new RenameBrowserTask(this, session, vfs, from, to, delayedAWTRequest);
 818		ThreadUtilities.runInBackground(renameTask);
 819	}
 820
 821	/**
 822	 * Rename a file
 823	 * @param from the full path name of the file to be renamed
 824	 * @param newname the new name (only filename, not full path)
 825	 */
 826	public void rename(String from, String newname)
 827	{
 828		VFS vfs = VFSManager.getVFSForPath(from);
 829		rename(vfs, from, newname);
 830	} //}}}
 831
 832	//{{{ mkdir() method
 833	public void mkdir()
 834	{
 835		String newDirectory = GUIUtilities.input(this,"vfs.browser.mkdir",null);
 836		if(newDirectory == null)
 837			return;
 838
 839		// if a directory is selected, create new dir in there.
 840		// if a file is selected, create new dir inside its parent.
 841		final VFSFile[] selected = getSelectedFiles();
 842		String parent;
 843		if(selected.length == 0)
 844			parent = path;
 845		else if(selected[0].getType() == VFSFile.FILE)
 846		{
 847			parent = selected[0].getPath();
 848			parent = VFSManager.getVFSForPath(parent)
 849				.getParentOfPath(parent);
 850		}
 851		else
 852			parent = selected[0].getPath();
 853
 854		VFS vfs = VFSManager.getVFSForPath(parent);
 855
 856		// path is the currently viewed directory in the browser
 857		newDirectory = MiscUtilities.constructPath(parent,newDirectory);
 858
 859		Object session = vfs.createVFSSession(newDirectory,this);
 860		if(session == null)
 861			return;
 862
 863		if(!startRequest())
 864			return;
 865
 866		Runnable runnable = new Runnable()
 867		{
 868			@Override
 869			public void run()
 870			{
 871				endRequest();
 872				if (selected.length != 0 && selected[0].getType() != VFSFile.FILE)
 873				{
 874					VFSDirectoryEntryTable directoryEntryTable = browserView.getTable();
 875					int selectedRow = directoryEntryTable.getSelectedRow();
 876					VFSDirectoryEntryTableModel model = (VFSDirectoryEntryTableModel) directoryEntryTable.getModel();
 877					VFSDirectoryEntryTableModel.Entry entry = model.files[selectedRow];
 878					if (!entry.expanded)
 879					{
 880						browserView.clearExpansionState();
 881						browserView.loadDirectory(entry,entry.dirEntry.getPath(),
 882							false);
 883					}
 884				}
 885			}
 886		};
 887		Task mkdirTask = new MkDirBrowserTask(this, session, vfs, newDirectory, runnable);
 888		ThreadUtilities.runInBackground(mkdirTask);
 889	} //}}}
 890
 891	//{{{ newFile() method
 892	/**
 893	 * Creates a new file in the current directory.
 894	 * @since jEdit 4.0pre2
 895	 */
 896	public void newFile()
 897	{
 898		VFSFile[] selected = getSelectedFiles();
 899		if(selected.length >= 1)
 900		{
 901			VFSFile file = selected[0];
 902			if(file.getType() == VFSFile.DIRECTORY)
 903				jEdit.newFile(view,file.getPath());
 904			else
 905			{
 906				VFS vfs = VFSManager.getVFSForPath(file.getPath());
 907				jEdit.newFile(view,vfs.getParentOfPath(file.getPath()));
 908			}
 909		}
 910		else
 911			jEdit.newFile(view,path);
 912	} //}}}
 913
 914	//{{{ fileProperties() method
 915	/**
 916	 * Show selected file's properties.
 917	 */
 918	public void fileProperties(VFSFile[] files)
 919	{
 920		new FilePropertiesDialog(view, this, files);
 921	} //}}} 		
 922		
 923	//{{{ searchInDirectory() method
 924	/**
 925	 * Opens a directory search in the current directory.
 926	 * @since jEdit 4.0pre2
 927	 */
 928	public void searchInDirectory()
 929	{
 930		VFSFile[] selected = getSelectedFiles();
 931		if(selected.length >= 1)
 932		{
 933			VFSFile file = selected[0];
 934			searchInDirectory(file.getPath(),file.getType() != VFSFile.FILE);
 935		}
 936		else
 937		{
 938			searchInDirectory(path,true);
 939		}
 940	} //}}}
 941
 942	//{{{ searchInDirectory() method
 943	/**
 944	 * Opens a directory search in the specified directory.
 945	 * @param path The path name
 946	 * @param directory True if the path is a directory, false if it is a file
 947	 * @since jEdit 4.2pre1
 948	 */
 949	public void searchInDirectory(String path, boolean directory)
 950	{
 951		String filter;
 952		VFSFileFilter vfsff = getVFSFileFilter();
 953		if (vfsff instanceof GlobVFSFileFilter)
 954			filter = ((GlobVFSFileFilter)vfsff).getGlob();
 955		else
 956			filter = "*";
 957
 958		if (!directory)
 959		{
 960			String name = MiscUtilities.getFileName(path);
 961			String ext = MiscUtilities.getFileExtension(name);
 962			filter = ext == null || ext.length() == 0
 963				? filter : '*' + ext;
 964			path = MiscUtilities.getParentOfPath(path);
 965		}
 966
 967		SearchAndReplace.setSearchFileSet(new DirectoryListSet(
 968			path,filter,true));
 969		SearchDialog.showSearchDialog(view,null,SearchDialog.DIRECTORY);
 970	} //}}}
 971
 972	//{{{ getBrowserView() method
 973	BrowserView getBrowserView()
 974	{
 975		return browserView;
 976	} //}}}
 977
 978	//{{{ getSelectedFiles() method
 979	/**
 980	 * Return the selected files in the lower browser tree.
 981	 * @since jEdit 4.3pre2
 982	 */
 983	public VFSFile[] getSelectedFiles()
 984	{
 985		return browserView.getSelectedFiles();
 986	} //}}}
 987
 988	//{{{ getSelectedFiles() method
 989	/**
 990	 * Return the selected files from the point of view of the
 991	 * given component. This may be the selected directory from the
 992	 * upper tree component of the browser (directory tree) or
 993	 * the selected files in the bottom tree component.
 994	 * This method is to be used by code running inside VFSBrowser
 995	 * such as a DynamicMenuProvider. Use the other method otherwise.
 996	 * The main difference is this function searches the component
 997	 * hierarchy for a {@link BrowserView.ParentDirectoryList} to get
 998	 * the list of currently selected files from there. Otherwise, it
 999	 * returns what {@link #getSelectedFiles()} would return.
1000	 * @param source the source component to start from when
1001	 * 		navigating the component hierarchy
1002	 * @since jEdit 4.4pre1
1003	 */
1004	public VFSFile[] getSelectedFiles(Component source)
1005	{
1006		if(GUIUtilities.getComponentParent(source, BrowserView.ParentDirectoryList.class)
1007			!= null)
1008		{
1009			Object[] selected = getBrowserView()
1010				.getParentDirectoryList()
1011				.getSelectedValues();
1012			VFSFile[] returnValue = new VFSFile[
1013				selected.length];
1014			System.arraycopy(selected,0,returnValue,0,
1015				selected.length);
1016			return returnValue;
1017		}
1018		else
1019		{
1020			return getSelectedFiles();
1021		}
1022	} //}}}
1023
1024	//{{{ locateFile() method
1025	/**
1026	 * Goes to the given file's directory and selects the file in the list.
1027	 * @param path The file
1028	 * @since jEdit 4.2pre2
1029	 */
1030	public void locateFile(final String path)
1031	{
1032		VFSFileFilter filter = getVFSFileFilter();
1033		if(!filter.accept(MiscUtilities.getFileName(path)))
1034			setFilenameFilter(null);
1035
1036		setDirectory(MiscUtilities.getParentOfPath(path));
1037		// Do not change this until all VFS Browser tasks are
1038		// done in ThreadUtilities
1039		VFSManager.runInAWTThread(new Runnable()
1040		{
1041			public void run()
1042			{
1043				browserView.getTable().selectFile(path);
1044			}
1045		});
1046	} //}}}
1047
1048	//{{{ createPluginsMenu() method
1049	public JComponent createPluginsMenu(JComponent pluginMenu, boolean showManagerOptions)
1050	{
1051		if(showManagerOptions && getMode() == BROWSER)
1052		{
1053			pluginMenu.add(GUIUtilities.loadMenuItem("plugin-manager",false));
1054			pluginMenu.add(GUIUtilities.loadMenuItem("plugin-options",false));
1055			if (pluginMenu instanceof JMenu)
1056				((JMenu)pluginMenu).addSeparator();
1057			else if (pluginMenu instanceof JPopupMenu)
1058				((JPopupMenu)pluginMenu).addSeparator();
1059
1060		}
1061		else
1062			/* we're in a modal dialog */;
1063
1064		List<JMenuItem> vec = new ArrayList<JMenuItem>();
1065
1066		//{{{ new API
1067		EditPlugin[] plugins = jEdit.getPlugins();
1068		for (int i = 0; i < plugins.length; i++)
1069		{
1070			JMenuItem menuItem = plugins[i].createBrowserMenuItems();
1071			if(menuItem != null)
1072				vec.add(menuItem);
1073		} //}}}
1074
1075		if (!vec.isEmpty())
1076		{
1077			Collections.sort(vec,new MenuItemTextComparator());
1078			for(int i = 0; i < vec.size(); i++)
1079				pluginMenu.add(vec.get(i));
1080		}
1081		else
1082		{
1083			JMenuItem mi = new JMenuItem(jEdit.getProperty(
1084					"vfs.browser.plugins.no-plugins.label"));
1085			mi.setEnabled(false);
1086			pluginMenu.add(mi);
1087		}
1088
1089		return pluginMenu;
1090	} //}}}
1091
1092	//{{{ addBrowserListener() method
1093	public void addBrowserListener(BrowserListener l)
1094	{
1095		listenerList.add(BrowserListener.class,l);
1096	} //}}}
1097
1098	//{{{ removeBrowserListener() method
1099	public void removeBrowserListener(BrowserListener l)
1100	{
1101		listenerList.remove(BrowserListener.class,l);
1102	} //}}}
1103
1104	//{{{ filesActivated() method
1105	// canDoubleClickClose set to false when ENTER pressed
1106	public static final int M_OPEN = 0;
1107	public static final int M_OPEN_NEW_VIEW = 1;
1108	public static final int M_OPEN_NEW_PLAIN_VIEW = 2;
1109	public static final int M_OPEN_NEW_SPLIT = 3;
1110	public static final int M_INSERT = 4;
1111
1112	/**
1113	 * This method does the "double-click" handling. It is public so that
1114	 * <code>browser.actions.xml</code> can bind to it.
1115	 * @since jEdit 4.2pre2
1116	 */
1117	public void filesActivated(int mode, boolean canDoubleClickClose)
1118	{
1119		VFSFile[] selectedFiles = browserView.getSelectedFiles();
1120
1121		Buffer buffer = null;
1122
1123check_selected: for(int i = 0; i < selectedFiles.length; i++)
1124		{
1125			VFSFile file = selectedFiles[i];
1126
1127			if(file.getType() == VFSFile.DIRECTORY
1128				|| file.getType() == VFSFile.FILESYSTEM)
1129			{
1130				if(mode == M_OPEN_NEW_VIEW && this.mode == BROWSER)
1131					browseDirectoryInNewWindow(view,file.getPath());
1132				else
1133					setDirectory(file.getPath());
1134			}
1135			else if(this.mode == BROWSER || this.mode == BROWSER_DIALOG)
1136			{
1137				if(mode == M_INSERT)
1138				{
1139					view.getBuffer().insertFile(view,
1140						file.getPath());
1141					continue check_selected;
1142				}
1143
1144				Buffer _buffer = jEdit.getBuffer(file.getPath());
1145				if(_buffer == null)
1146				{
1147					Hashtable<String, Object> props = new Hashtable<String, Object>();
1148					if(currentEncoding != null)
1149					{
1150						props.put(JEditBuffer.ENCODING,currentEncoding);
1151					}
1152					props.put(Buffer.ENCODING_AUTODETECT,
1153						autoDetectEncoding);
1154					_buffer = jEdit.openFile(view, null,
1155						file.getPath(),false,props);
1156				}
1157				else if(doubleClickClose && canDoubleClickClose
1158					&& this.mode != BROWSER_DIALOG
1159					&& selectedFiles.length == 1)
1160				{
1161					// close if this buffer is currently
1162					// visible in the view.
1163					EditPane[] editPanes = view.getEditPanes();
1164					for(int j = 0; j < editPanes.length; j++)
1165					{
1166						if(editPanes[j].getBuffer() == _buffer)
1167						{
1168							jEdit.closeBuffer(view,_buffer);
1169							return;
1170						}
1171					}
1172				}
1173
1174				if(_buffer != null)
1175					buffer = _buffer;
1176			}
1177			else
1178			{
1179				// if a file is selected in OPEN_DIALOG or
1180				// SAVE_DIALOG mode, just let the listener(s)
1181				// handle it
1182			}
1183		}
1184
1185		if(buffer != null)
1186		{
1187			switch(mode)
1188			{
1189			case M_OPEN:
1190				view.setBuffer(buffer);
1191				break;
1192			case M_OPEN_NEW_VIEW:
1193				jEdit.newView(view,buffer,false);
1194				break;
1195			case M_OPEN_NEW_PLAIN_VIEW:
1196				jEdit.newView(view,buffer,true);
1197				break;
1198			case M_OPEN_NEW_SPLIT:
1199				view.splitHorizontally().setBuffer(buffer);
1200				break;
1201			}
1202		}
1203
1204		Object[] listeners = listenerList.getListenerList();
1205		for(int i = 0; i < listeners.length; i++)
1206		{
1207			if(listeners[i] == BrowserListener.class)
1208			{
1209				BrowserListener l = (BrowserListener)listeners[i+1];
1210				l.filesActivated(this,selectedFiles);
1211			}
1212		}
1213	} //}}}
1214
1215	//{{{ dispose() method
1216	/** Disposes the browser, regardless of whether it is a dialog or a dockable
1217	*/
1218	public void dispose()
1219	{
1220		if (mode == BROWSER)
1221		{
1222			view.getDockableWindowManager().hideDockableWindow(NAME);			
1223		}
1224		else
1225		{
1226			GUIUtilities.getParentDialog(this).dispose();
1227		}	
1228	}//}}}
1229
1230	//{{{ move() method
1231	@Override
1232	public void move(String newPosition)
1233	{
1234		boolean horz = mode != BROWSER
1235				|| DockableWindowManager.TOP.equals(newPosition)
1236				|| DockableWindowManager.BOTTOM.equals(newPosition);
1237		if (horz == horizontalLayout)
1238			return;
1239		horizontalLayout = horz;
1240		topBox.remove(toolbarBox);
1241		toolbarBox = new Box(horizontalLayout
1242				? BoxLayout.X_AXIS
1243				: BoxLayout.Y_AXIS);
1244		topBox.add(toolbarBox, 0);
1245		propertiesChanged();
1246	} //}}}
1247	
1248	//{{{ Package-private members
1249
1250	// This can be null untill an user explicitly selects an encoding
1251	// so that this don't overwrite more accurate encoding information
1252	// like buffer histories.
1253	String currentEncoding;
1254
1255	boolean autoDetectEncoding;
1256
1257	//{{{ directoryLoaded() method
1258	void directoryLoaded(Object node, Object[] loadInfo,
1259		boolean addToHistory)
1260	{
1261		String path = (String)loadInfo[0];
1262		if(path == null)
1263		{
1264			// there was an error
1265			return;
1266		}
1267
1268		VFSFile[] list = (VFSFile[])loadInfo[1];
1269
1270		if(node == null)
1271		{
1272			// This is the new, canonical path
1273			VFSBrowser.this.path = path;
1274			if(!pathField.getText().equals(path))
1275				pathField.setText(path);
1276			if(path.endsWith("/") ||
1277				path.endsWith(File.separator))
1278			{
1279				// ensure consistent history;
1280				// eg we don't want both
1281				// foo/ and foo
1282				path = path.substring(0,
1283					path.length() - 1);
1284			}
1285
1286			if(addToHistory)
1287			{
1288				HistoryModel.getModel("vfs.browser.path")
1289					.addItem(path);
1290			}
1291		}
1292
1293		boolean filterEnabled = filterCheckbox.isSelected();
1294
1295		List<VFSFile> directoryList = new ArrayList<VFSFile>();
1296
1297		int directories = 0;
1298		int files = 0;
1299		int invisible = 0;
1300
1301		if(list != null)
1302		{
1303			VFSFileFilter filter = getVFSFileFilter();
1304
1305			for(int i = 0; i < list.length; i++)
1306			{
1307				VFSFile file = list[i];
1308				if(file.isHidden() && !showHiddenFiles)
1309				{
1310					invisible++;
1311					continue;
1312				}
1313
1314				if (filter != null && (filterEnabled || filter instanceof DirectoriesOnlyFilter)
1315				    && !filter.accept(file))
1316				{
1317					invisible++;
1318					continue;
1319				}
1320
1321				if(file.getType() == VFSFile.FILE)
1322					files++;
1323				else
1324					directories++;
1325
1326				directoryList.add(file);
1327			}
1328
1329			Collections.sort(directoryList,
1330				new VFS.DirectoryEntryCompare(
1331				sortMixFilesAndDirs,
1332				sortIgnoreCase));
1333		}
1334
1335		browserView.directoryLoaded(node,path,
1336			directoryList);
1337
1338		// to notify listeners that any existing
1339		// selection has been deactivated
1340
1341		// turns out under some circumstances this
1342		// method can switch the current buffer in
1343		// BROWSER mode.
1344
1345		// in any case, this is only needed for the
1346		// directory chooser (why?), so we add a
1347		// check. otherwise poor Rick will go insane.
1348		if(mode == CHOOSE_DIRECTORY_DIALOG)
1349			filesSelected();
1350	} //}}}
1351
1352	//{{{ filesSelected() method
1353	void filesSelected()
1354	{
1355		VFSFile[] selectedFiles = browserView.getSelectedFiles();
1356
1357		if(mode == BROWSER)
1358		{
1359			for(int i = 0; i < selectedFiles.length; i++)
1360			{
1361				VFSFile file = selectedFiles[i];
1362				Buffer buffer = jEdit.getBuffer(file.getPath());
1363				if(buffer != null && view != null)
1364					view.setBuffer(buffer);
1365			}
1366		}
1367
1368		Object[] listeners = listenerList.getListenerList();
1369		for(int i = 0; i < listeners.length; i++)
1370		{
1371			if(listeners[i] == BrowserListener.class)
1372			{
1373				BrowserListener l = (BrowserListener)listeners[i+1];
1374				l.filesSelected(this,selectedFiles);
1375			}
1376		}
1377	} //}}}
1378
1379	//{{{ endRequest() method
1380	void endRequest()
1381	{
1382		requestRunning = false;
1383	} //}}}
1384
1385	//}}}
1386
1387	//{{{ Private members
1388
1389	private static final ActionContext actionContext;
1390
1391	static
1392	{
1393		actionContext = new BrowserActionContext();
1394
1395		ActionSet builtInActionSet = new ActionSet(null,null,null,
1396			jEdit.class.getResource("browser.actions.xml"));
1397		builtInActionSet.setLabel(jEdit.getProperty("action-set.browser"));
1398		builtInActionSet.load();
1399		actionContext.addActionSet(builtInActionSet);
1400	}
1401
1402	//{{{ Instance variables
1403	private EventListenerList listenerList;
1404	private View view;
1405	private boolean horizontalLayout;
1406	private String path;
1407	private JPanel pathAndFilterPanel;
1408	private HistoryTextField pathField;
1409	private JComponent defaultFocusComponent;
1410	private JCheckBox filterCheckbox;
1411	private HistoryComboBoxEditor filterEditor;
1412	private JComboBox filterField;
1413	private Box toolbarBox;
1414	private Box topBox;
1415	private FavoritesMenuButton favorites;
1416	private PluginsMenuButton plugins;
1417	private BrowserView browserView;
1418	private int mode;
1419	private boolean multipleSelection;
1420
1421	private boolean showHiddenFiles;
1422	private boolean sortMixFilesAndDirs;
1423	private boolean sortIgnoreCase;
1424	private boolean doubleClickClose;
1425
1426	private boolean requestRunning;
1427	private boolean maybeReloadRequestRunning;
1428	
1429	private final Stack<String> historyStack = new Stack<String>();
1430	private final Stack<String> nextDirectoryStack = new Stack<String>();
1431	//}}}
1432
1433	//{{{ createMenuBar() method
1434	private Container createMenuBar()
1435	{
1436		JToolBar menuBar = new JToolBar();
1437		menuBar.setFloatable(false);
1438
1439		menuBar.add(new CommandsMenuButton());
1440		menuBar.add(Box.createHorizontalStrut(3));
1441		menuBar.add(plugins = new PluginsMenuButton());
1442		menuBar.add(Box.createHorizontalStrut(3));
1443		menuBar.add(favorites = new FavoritesMenuButton());
1444
1445		return menuBar;
1446	} //}}}
1447
1448	//{{{ createToolBar() method
1449	private Container createToolBar()
1450	{
1451		if(mode == BROWSER)
1452			return GUIUtilities.loadToolBar(actionContext,
1453				"vfs.browser.toolbar-browser");
1454		else
1455			return GUIUtilities.loadToolBar(actionContext,
1456				"vfs.browser.toolbar-dialog");
1457	} //}}}
1458
1459	//{{{ propertiesChanged() method
1460	private void propertiesChanged()
1461	{
1462		showHiddenFiles = jEdit.getBooleanProperty("vfs.browser.showHiddenFiles");
1463		sortMixFilesAndDirs = jEdit.getBooleanProperty("vfs.browser.sortMixFilesAndDirs");
1464		sortIgnoreCase = jEdit.getBooleanProperty("vfs.browser.sortIgnoreCase");
1465		doubleClickClose = jEdit.getBooleanProperty("vfs.browser.doubleClickClose");
1466
1467		browserView.propertiesChanged();
1468
1469		toolbarBox.removeAll();
1470
1471		if(jEdit.getBooleanProperty("vfs.browser.showToolbar"))
1472		{
1473			Container toolbar = createToolBar();
1474			toolbarBox.add(toolbar);
1475		}
1476
1477		if(jEdit.getBooleanProperty("vfs.browser.showMenubar"))
1478		{
1479			Container menubar = createMenuBar();
1480			if(horizontalLayout)
1481			{
1482				toolbarBox.add(menubar,0);
1483			}
1484			else
1485			{
1486				menubar.add(Box.createGlue());
1487				toolbarBox.add(menubar);
1488			}
1489		}
1490		else
1491		{
1492			plugins = null;
1493			favorites = null;
1494		}
1495
1496		revalidate();
1497
1498		if(path != null)
1499			reloadDirectory();
1500	} //}}}
1501
1502	/* We do this stuff because the browser is not able to handle
1503	 * more than one request yet */
1504
1505	//{{{ startRequest() method
1506	private boolean startRequest()
1507	{
1508		if(requestRunning)
1509		{
1510			// dump stack trace for debugging purposes
1511			Log.log(Log.DEBUG,this,new Throwable("For debugging purposes"));
1512
1513			GUIUtilities.error(this,"browser-multiple-io",null);
1514			return false;
1515		}
1516		else
1517		{
1518			requestRunning = true;
1519			return true;
1520		}
1521	} //}}}
1522
1523	//{{{ updateFilterEnabled() method
1524	private void updateFilterEnabled()
1525	{
1526		filterField.setEnabled(filterCheckbox.isSelected());
1527		filterEditor.setEnabled(filterCheckbox.isSelected());
1528	} //}}}
1529
1530	//{{{ maybeReloadDirectory() method
1531	private void maybeReloadDirectory(String dir)
1532	{
1533		if(MiscUtilities.isURL(dir)
1534			&& MiscUtilities.getProtocolOfURL(dir).equals(
1535			FavoritesVFS.PROTOCOL))
1536		{
1537			if(favorites != null)
1538				favorites.popup = null;
1539		}
1540
1541		// this is a dirty hack and it relies on the fact
1542		// that updates for parents are sent before updates
1543		// for the changed nodes themselves (if this was not
1544		// the case, the browser wouldn't be updated properly
1545		// on delete, etc).
1546		//
1547		// to avoid causing '> 1 request' errors, don't reload
1548		// directory if request already active
1549		if(maybeReloadRequestRunning)
1550		{
1551			//Log.log(Log.WARNING,this,"VFS update: request already in progress");
1552			return;
1553		}
1554
1555		// save a file -> sends vfs update. if a VFS file dialog box
1556		// is shown from the same event frame as the save, the
1557		// VFSUpdate will be delivered before the directory is loaded,
1558		// and before the path is set.
1559		if(path != null)
1560		{
1561			try
1562			{
1563				maybeReloadRequestRunning = true;
1564
1565				browserView.maybeReloadDirectory(dir);
1566			}
1567			finally
1568			{
1569				// Do not change this until all VFS Browser tasks are
1570				// done in ThreadUtilities
1571				VFSManager.runInAWTThread(new Runnable()
1572				{
1573					public void run()
1574					{
1575						maybeReloadRequestRunning = false;
1576					}
1577				});
1578			}
1579		}
1580	} //}}}
1581
1582	//}}}
1583
1584	//{{{ Inner classes
1585
1586	//{{{ ActionHandler class
1587	class ActionHandler implements ActionListener, ItemListener
1588	{
1589		@Override
1590		public void actionPerformed(ActionEvent evt)
1591		{
1592			if (isProcessingEvent)
1593				return;
1594
1595			Object source = evt.getSource();
1596
1597			if (source == pathField
1598			    || source == filterCheckbox)
1599			{
1600				isProcessingEvent = true;
1601				resetLater();
1602
1603				updateFilterEnabled();
1604
1605				String p = pathField.getText();
1606				
1607				if(p != null)
1608					setDirectory(p);
1609				browserView.focusOnFileView();
1610			}
1611
1612			else if (source == filterField.getEditor())
1613			{
1614				// force the editor to refresh.
1615				filterField.getEditor().setItem(
1616					filterField.getEditor().getItem());
1617			}
1618
1619			// depending on Swing look & feel, filterField.getEditor()
1620			// returns some ComboBoxUI
1621			else if (source == filterEditor)
1622			{
1623				// force the editor to refresh.
1624				filterEditor.setItem(
1625					filterEditor.getItem());
1626				filterField.setSelectedItem(
1627					filterEditor.getItem());
1628				// ### ugly: 
1629				// itemStateChanged does not seem to get fired
1630				itemStateChanged(new ItemEvent(filterField,
1631					ItemEvent.ITEM_STATE_CHANGED,
1632					filterEditor.getItem(),
1633					ItemEvent.SELECTED));
1634			}
1635		}
1636
1637		@Override
1638		public void itemStateChanged(ItemEvent e)
1639		{
1640			if (isProcessingEvent)
1641				return;
1642
1643			if (e.getStateChange() != ItemEvent.SELECTED)
1644				return;
1645
1646			isProcessingEvent = true;
1647			resetLater();
1648
1649			filterField.setEditable(e.getItem() instanceof GlobVFSFileFilter);
1650			updateFilterEnabled();
1651			String path = pathField.getText();
1652			if(path != null)
1653				setDirectory(path);
1654
1655			browserView.focusOnFileView();
1656		}
1657
1658		/**
1659		 * Why this method exists: since both actionPerformed()
1660		 * and itemStateChanged() above can change the combo box,
1661		 * executing one of them can cause a chain reaction causing
1662		 * the other method to be called. This would cause the
1663		 * VFS subsystem to be called several times, which would
1664		 * cause a warning to show up if the first operation is
1665		 * still in progress, or cause a second operation to happen
1666		 * which is not really wanted especially if we're talking
1667		 * about a remove VFS. So the methods set a flag saying
1668		 * that something is going on, and this method resets
1669		 * the flag after the AWT thread is done with the
1670		 * current events.
1671		 */
1672		private void resetLater()
1673		{
1674			ThreadUtilities.runInDispatchThread(new Runnable()
1675				{
1676					@Override
1677					public void run()
1678					{
1679						isProcessingEvent = false;
1680					}
1681				}
1682			);
1683		}
1684
1685		private boolean isProcessingEvent;
1686
1687	} //}}}
1688
1689	//{{{ CommandsMenuButton class
1690	class CommandsMenuButton extends RolloverButton
1691	{
1692		//{{{ CommandsMenuButton constructor
1693		CommandsMenuButton()
1694		{
1695			setText(jEdit.getProperty("vfs.browser.commands.label"));
1696			setIcon(GUIUtilities.loadIcon(jEdit.getProperty("dropdown-arrow.icon")));
1697			setHorizontalTextPosition(SwingConstants.LEADING);
1698			setName("commands");
1699			
1700			popup = new BrowserCommandsMenu(VFSBrowser.this,null);
1701
1702			CommandsMenuButton.this.setRequestFocusEnabled(false);
1703			setMargin(new Insets(1,1,1,1));
1704			CommandsMenuButton.this.addMouseListener(new MouseHandler());
1705
1706			if(OperatingSystem.isMacOSLF())
1707				CommandsMenuButton.this.putClientProperty("JButton.buttonType","toolbar");
1708		} //}}}
1709
1710		BrowserCommandsMenu popup;
1711
1712		//{{{ MouseHandler class
1713		class MouseHandler extends MouseAdapter
1714		{
1715			@Override
1716			public void mousePressed(MouseEvent evt)
1717			{
1718				if(!popup.isVisible())
1719				{
1720					popup.update();
1721
1722					GUIUtilities.showPopupMenu(
1723						popup,CommandsMenuButton.this,0,
1724						CommandsMenuButton.this.getHeight(),
1725						false);
1726				}
1727				else
1728				{
1729					popup.setVisible(false);
1730				}
1731			}
1732		} //}}}
1733	} //}}}
1734
1735	//{{{ PluginsMenuButton class
1736	class PluginsMenuButton extends RolloverButton
1737	{
1738		//{{{ PluginsMenuButton constructor
1739		PluginsMenuButton()
1740		{
1741			setText(jEdit.getProperty("vfs.browser.plugins.label"));
1742			setIcon(GUIUtilities.loadIcon(jEdit.getProperty("dropdown-arrow.icon")));
1743			setHorizontalTextPosition(SwingConstants.LEADING);
1744			setName("plugins");
1745			
1746			PluginsMenuButton.this.setRequestFocusEnabled(false);
1747			setMargin(new Insets(1,1,1,1));
1748			PluginsMenuButton.this.addMouseListener(new MouseHandler());
1749
1750			if(OperatingSystem.isMacOSLF())
1751				PluginsMenuButton.this.putClientProperty("JButton.buttonType","toolbar");
1752		} //}}}
1753
1754		JPopupMenu popup;
1755
1756		//{{{ updatePopupMenu() method
1757		void updatePopupMenu()
1758		{
1759			popup = null;
1760		} //}}}
1761
1762		//{{{ createPopupMenu() method
1763		private void createPopupMenu()
1764		{
1765			if(popup != null)
1766				return;
1767
1768			popup = (JPopupMenu)createPluginsMenu(new JPopupMenu(),true);
1769		} //}}}
1770
1771		//{{{ MouseHandler class
1772		class MouseHandler extends MouseAdapter
1773		{
1774			@Override
1775			public void mousePressed(MouseEvent evt)
1776			{
1777				createPopupMenu();
1778
1779				if(!popup.isVisible())
1780				{
1781					GUIUtilities.showPopupMenu(
1782						popup,PluginsMenuButton.this,0,
1783						PluginsMenuButton.this.getHeight(),
1784						false);
1785				}
1786				else
1787				{
1788					popup.setVisible(false);
1789				}
1790			}
1791		} //}}}
1792	} //}}}
1793
1794	//{{{ FavoritesMenuButton class
1795	class FavoritesMenuButton extends RolloverButton
1796	{
1797		//{{{ FavoritesMenuButton constructor
1798		FavoritesMenuButton()
1799		{
1800			setText(jEdit.getProperty("vfs.browser.favorites.label"));
1801			setIcon(GUIUtilities.loadIcon(jEdit.getProperty("dropdown-arrow.icon")));
1802			setHorizontalTextPosition(SwingConstants.LEADING);
1803			setName("favorites");
1804			
1805			FavoritesMenuButton.this.setRequestFocusEnabled(false);
1806			setMargin(new Insets(1,1,1,1));
1807			FavoritesMenuButton.this.addMouseListener(new MouseHandler());
1808
1809			if(OperatingSystem.isMacOSLF())
1810				FavoritesMenuButton.this.putClientProperty("JButton.buttonType","toolbar");
1811		} //}}}
1812
1813		JPopupMenu popup;
1814
1815		//{{{ createPopupMenu() method
1816		void createPopupMenu()
1817		{
1818			popup = new JPopupMenu();
1819			ActionHandler actionHandler = new ActionHandler();
1820
1821			JMenuItem mi = new JMenuItem(
1822				jEdit.getProperty(
1823				"vfs.browser.favorites"
1824				+ ".add-to-favorites.label"));
1825			mi.setActionCommand("add-to-favorites");
1826			mi.addActionListener(actionHandler);
1827			popup.add(mi);
1828
1829			mi = new JMenuItem(
1830				jEdit.getProperty(
1831				"vfs.browser.favorites"
1832				+ ".edit-favorites.label"));
1833			mi.setActionCommand("dir@favorites:");
1834			mi.addActionListener(actionHandler);
1835			popup.add(mi);
1836
1837			popup.addSeparator();
1838
1839			VFSFile[] favorites = FavoritesVFS.getFavorites();
1840			if(favorites.length == 0)
1841			{
1842				mi = new JMenuItem(
1843					jEdit.getProperty(
1844					"vfs.browser.favorites"
1845					+ ".no-favorites.label"));
1846				mi.setEnabled(false);
1847				popup.add(mi);
1848			}
1849			else
1850			{
1851				Arrays.sort(favorites,
1852					new VFS.DirectoryEntryCompare(
1853					sortMixFilesAndDirs,
1854					sortIgnoreCase));
1855				for(int i = 0; i < favorites.length; i++)
1856				{
1857					FavoritesVFS.Favorite favorite = (FavoritesVFS.Favorite) favorites[i];
1858					mi = new JMenuItem(favorite.getLabel());
1859					mi.setIcon(FileCellRenderer
1860						.getIconForFile(
1861						favorite,false));
1862					String cmd = (favorite.getType() ==
1863						VFSFile.FILE
1864						? "file@" : "dir@")
1865						+ favorite.getPath();
1866					mi.setActionCommand(cmd);
1867					mi.addActionListener(actionHandler);
1868					popup.add(mi);
1869				}
1870			}
1871		} //}}}
1872
1873		//{{{ ActionHandler class
1874		class ActionHandler implements ActionListener
1875		{
1876			@Override
1877			public void actionPerformed(ActionEvent evt)
1878			{
1879				String actionCommand = evt.getActionCommand();
1880				if("add-to-favorites".equals(actionCommand))
1881				{
1882					// if any directories are selected, add
1883					// them, otherwise add current directory
1884					VFSFile[] selected = getSelectedFiles();
1885					if(selected == null || selected.length == 0)
1886					{
1887						if(path.equals(FavoritesVFS.PROTOCOL + ':'))
1888						{
1889							GUIUtilities.error(VFSBrowser.this,
1890								"vfs.browser.recurse-favorites",
1891								null);
1892						}
1893						else
1894						{
1895							FavoritesVFS.addToFavorites(path,
1896								VFSFile.DIRECTORY);
1897						}
1898					}
1899					else
1900					{
1901						for(int i = 0; i < selected.length; i++)
1902						{
1903							VFSFile file = selected[i];
1904							FavoritesVFS.addToFavorites(file.getPath(),
1905								file.getType());
1906						}
1907					}
1908				}
1909				else if(actionCommand.startsWith("dir@"))
1910				{
1911					setDirectory(actionCommand.substring(4));
1912				}
1913				else if(actionCommand.startsWith("file@"))
1914				{
1915					switch(getMode())
1916					{
1917					case BROWSER:
1918						jEdit.openFile(view,actionCommand.substring(5));
1919						break;
1920					default:
1921						locateFile(actionCommand.substring(5));
1922						break;
1923					}
1924				}
1925			}
1926		} //}}}
1927
1928		//{{{ MouseHandler class
1929		class MouseHandler extends MouseAdapter
1930		{
1931			@Override
1932			public void mousePressed(MouseEvent evt)
1933			{
1934				if(popup != null && popup.isVisible())
1935				{
1936					popup.setVisible(false);
1937					return;
1938				}
1939
1940				if(popup == null)
1941					createPopupMenu();
1942
1943				GUIUtilities.showPopupMenu(
1944					popup,FavoritesMenuButton.this,0,
1945					FavoritesMenuButton.this.getHeight(),
1946					false);
1947			}
1948		} //}}}
1949	} //}}}
1950
1951	//{{{ BrowserActionContext class
1952	static class BrowserActionContext extends ActionContext
1953	{
1954		@Override
1955		public void invokeAction(EventObject evt, EditAction action)
1956		{
1957			Component source = (Component)evt.getSource();
1958			VFSBrowser browser = (VFSBrowser)
1959				GUIUtilities.getComponentParent(
1960				source,
1961				VFSBrowser.class);
1962
1963			VFSFile[] files = browser.getSelectedFiles(source);
1964
1965			// in the future we will want something better,
1966			// eg. having an 'evt' object passed to
1967			// EditAction.invoke().
1968
1969			// for now, since all browser actions are
1970			// written in beanshell we set the 'browser'
1971			// variable directly.
1972			NameSpace global = BeanShell.getNameSpace();
1973			try
1974			{
1975				global.setVariable("browser",browser);
1976				global.setVariable("files",files);
1977
1978				View view = browser.getView();
1979				// I guess ideally all browsers
1980				// should have views, but since they
1981				// don't, we just use the active view
1982				// in that

Large files files are truncated, but you can click here to view the full file