Docjar: A Java Source and Docuemnt Enginecom.*    java.*    javax.*    org.*    all    new    plug-in

Quick Search    Search Deep

Source code: org/gjt/sp/jedit/browser/VFSBrowser.java


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