/* * VFSBrowser.java - VFS browser * :tabSize=4:indentSize=4:noTabs=false: * :folding=explicit:collapseFolds=1: * * Copyright (C) 2000, 2003 Slava Pestov * * This program is free software; you can redistribute it and/or * modify it under the terms of the GNU General Public License * as published by the Free Software Foundation; either version 2 * of the License, or any later version. * * This program is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the * GNU General Public License for more details. * * You should have received a copy of the GNU General Public License * along with this program; if not, write to the Free Software * Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA. */ package org.gjt.sp.jedit.browser; //{{{ Imports import org.gjt.sp.jedit.EditBus.EBHandler; import org.gjt.sp.jedit.bsh.*; import javax.swing.border.EmptyBorder; import javax.swing.event.*; import javax.swing.*; import java.awt.datatransfer.DataFlavor; import java.awt.datatransfer.Transferable; import java.awt.datatransfer.UnsupportedFlavorException; import java.awt.event.*; import java.awt.*; import java.io.File; import java.io.IOException; import java.util.*; import java.util.List; import java.util.concurrent.CountDownLatch; import org.gjt.sp.jedit.datatransfer.ListVFSFileTransferable; import org.gjt.sp.jedit.io.*; import org.gjt.sp.jedit.gui.*; import org.gjt.sp.jedit.msg.*; import org.gjt.sp.jedit.search.*; import org.gjt.sp.jedit.*; import org.gjt.sp.jedit.buffer.JEditBuffer; import org.gjt.sp.util.*; import org.gjt.sp.jedit.menu.MenuItemTextComparator; //}}} /** * The main class of the VFS browser. * Used as dockable, and also embedded inside the * VFSFileChooserDialog. * * @author Slava Pestov * @version $Id: VFSBrowser.java 23224 2013-09-30 20:51:42Z shlomy $ */ public class VFSBrowser extends JPanel implements DefaultFocusComponent, DockableWindow { public static final String NAME = "vfs.browser"; //{{{ Browser modes /** * Open file dialog mode. Equals JFileChooser.OPEN_DIALOG for * backwards compatibility. */ public static final int OPEN_DIALOG = 0; /** * Save file dialog mode. Equals JFileChooser.SAVE_DIALOG for * backwards compatibility. */ public static final int SAVE_DIALOG = 1; /** * File Open Dialog with extra context menu actions like the BROWSER mode. */ public static final int BROWSER_DIALOG = 4; /** * Choose directory dialog mode. */ public static final int CHOOSE_DIRECTORY_DIALOG = 3; /** * Stand-alone dockable browser mode. */ public static final int BROWSER = 2; //}}} //{{{ browseDirectoryInNewWindow() method /** * Opens the specified directory in a new, floating, file system browser. * @param view The view * @param path The directory's path * @since jEdit 4.1pre2 */ public static void browseDirectoryInNewWindow(View view, String path) { DockableWindowManager wm = view.getDockableWindowManager(); if(path != null) { // this is such a bad way of doing it, but oh well... jEdit.setTemporaryProperty("vfs.browser.path.tmp",path); } wm.floatDockableWindow("vfs.browser"); jEdit.unsetProperty("vfs.browser.path.tmp"); } //}}} //{{{ browseDirectory() method /** * Opens the specified directory in a file system browser. * @param view The view * @param path The directory's path * @since jEdit 4.0pre3 */ public static void browseDirectory(View view, String path) { DockableWindowManager wm = view.getDockableWindowManager(); VFSBrowser browser = (VFSBrowser)wm.getDockable(NAME); if(browser != null) { wm.showDockableWindow(NAME); browser.setDirectory(path); } else { if(path != null) { // this is such a bad way of doing it, but oh well... jEdit.setTemporaryProperty("vfs.browser.path.tmp",path); } wm.addDockableWindow("vfs.browser"); jEdit.unsetProperty("vfs.browser.path.tmp"); } } //}}} //{{{ getActionContext() method /** * Returns the browser action context. * @since jEdit 4.2pre1 */ public static ActionContext getActionContext() { return actionContext; } //}}} //{{{ VFSBrowser constructor /** * Creates a new VFS browser. * @param view The view to open buffers in by default */ public VFSBrowser(View view, String position) { this(view,null,BROWSER,true,position); } //}}} //{{{ VFSBrowser constructor /** * Creates a new VFS browser. * @param view The view to open buffers in by default * @param path The path to display * @param mode The browser mode * @param multipleSelection True if multiple selection should be allowed * @param position Where the browser is located * @since jEdit 4.2pre1 */ public VFSBrowser(View view, String path, int mode, boolean multipleSelection, String position) { super(new BorderLayout()); listenerList = new EventListenerList(); this.mode = mode; this.multipleSelection = multipleSelection; this.view = view; currentEncoding = null; autoDetectEncoding = jEdit.getBooleanProperty( "buffer.encodingAutodetect"); ActionHandler actionHandler = new ActionHandler(); topBox = new Box(BoxLayout.Y_AXIS); horizontalLayout = mode != BROWSER || DockableWindowManager.TOP.equals(position) || DockableWindowManager.BOTTOM.equals(position); toolbarBox = new Box(horizontalLayout ? BoxLayout.X_AXIS : BoxLayout.Y_AXIS); topBox.add(toolbarBox); GridBagLayout layout = new GridBagLayout(); pathAndFilterPanel = new JPanel(layout); if(isHorizontalLayout()) pathAndFilterPanel.setBorder(new EmptyBorder(12,12,12,12)); GridBagConstraints cons = new GridBagConstraints(); cons.gridwidth = cons.gridheight = 1; cons.gridx = cons.gridy = 0; cons.fill = GridBagConstraints.BOTH; cons.anchor = GridBagConstraints.EAST; JLabel label = new JLabel(jEdit.getProperty("vfs.browser.path"), SwingConstants.RIGHT); label.setBorder(new EmptyBorder(0,0,0,12)); layout.setConstraints(label,cons); pathAndFilterPanel.add(label); pathField = new HistoryTextField("vfs.browser.path"); pathField.setName("path"); pathField.setInstantPopups(true); pathField.setEnterAddsToHistory(false); pathField.setSelectAllOnFocus(true); // because its preferred size can be quite wide, we // don't want it to make the browser way too big, // so set the preferred width to 0. Dimension prefSize = pathField.getPreferredSize(); prefSize.width = 0; pathField.setPreferredSize(prefSize); pathField.addActionListener(actionHandler); cons.gridx = 1; cons.weightx = 1.0; cons.gridwidth = GridBagConstraints.REMAINDER; layout.setConstraints(pathField,cons); pathAndFilterPanel.add(pathField); filterCheckbox = new JCheckBox(jEdit.getProperty("vfs.browser.filter")); filterCheckbox.setMargin(new Insets(0,0,0,0)); // filterCheckbox.setRequestFocusEnabled(false); filterCheckbox.setBorder(new EmptyBorder(0,0,0,12)); filterCheckbox.setSelected(jEdit.getBooleanProperty( "vfs.browser.filter-enabled")); filterCheckbox.addActionListener(actionHandler); filterCheckbox.setName("filter-checkbox"); if(mode != CHOOSE_DIRECTORY_DIALOG) { cons.gridwidth = 1; cons.gridx = 0; cons.weightx = 0.0; cons.gridy = 1; layout.setConstraints(filterCheckbox,cons); pathAndFilterPanel.add(filterCheckbox); } filterField = new JComboBox(); filterEditor = new HistoryComboBoxEditor("vfs.browser.filter"); filterEditor.setToolTipText(jEdit.getProperty("glob.tooltip")); filterEditor.setInstantPopups(true); filterEditor.setSelectAllOnFocus(true); filterEditor.addActionListener(actionHandler); filterField.setName("filter-field"); if (mode == BROWSER) { DockableWindowManager dwm = view.getDockableWindowManager(); KeyListener keyListener = dwm.closeListener(NAME); filterCheckbox.addKeyListener(keyListener); addKeyListener(keyListener); filterEditor.addKeyListener(keyListener); pathField.addKeyListener(keyListener); // save the location on close of dockable. pathField.addKeyListener(new KeyAdapter() { @Override public void keyReleased(KeyEvent e) { if (e.getKeyCode() == KeyEvent.VK_ESCAPE) { pathField.setText(VFSBrowser.this.path); } } }); } String filter; if(mode == BROWSER || !jEdit.getBooleanProperty( "vfs.browser.currentBufferFilter")) { filter = jEdit.getProperty("vfs.browser.last-filter"); if(filter == null) filter = jEdit.getProperty("vfs.browser.default-filter"); } else { String ext = MiscUtilities.getFileExtension( view.getBuffer().getName()); if(ext.length() == 0) filter = jEdit.getProperty("vfs.browser.default-filter"); else filter = '*' + ext; } // filterField.getEditor().setItem(new GlobVFSFileFilter(filter)); // filterField.addItem(filterField.getEditor().getItem()); filterEditor.setItem(new GlobVFSFileFilter(filter)); filterField.addItem(filterEditor.getItem()); filterField.addItemListener(actionHandler); filterField.setRenderer(new VFSFileFilterRenderer()); // loads the registered VFSFileFilter services. String[] _filters = ServiceManager.getServiceNames(VFSFileFilter.SERVICE_NAME); for (int i = 0; i < _filters.length; i++) { VFSFileFilter _filter = (VFSFileFilter) ServiceManager.getService(VFSFileFilter.SERVICE_NAME, _filters[i]); filterField.addItem(_filter); } if(mode != CHOOSE_DIRECTORY_DIALOG) { cons.gridwidth = GridBagConstraints.REMAINDER; cons.fill = GridBagConstraints.HORIZONTAL; cons.gridx = 1; cons.weightx = 1.0; if (filterField.getItemCount() > 1) { filterField.setEditor(filterEditor); filterField.setEditable(true); layout.setConstraints(filterField,cons); pathAndFilterPanel.add(filterField); } else { layout.setConstraints(filterEditor,cons); pathAndFilterPanel.add(filterEditor); } } topBox.add(pathAndFilterPanel); add(BorderLayout.NORTH,topBox); add(BorderLayout.CENTER,browserView = new BrowserView(this)); if(isHorizontalLayout()) browserView.setBorder(new EmptyBorder(0,12,0,12)); defaultFocusComponent = browserView.getTable(); propertiesChanged(); updateFilterEnabled(); setFocusTraversalPolicy(new LayoutFocusTraversalPolicy()); // see VFSBrowser.browseDirectory() if(path == null) path = jEdit.getProperty("vfs.browser.path.tmp"); if(path == null || path.isEmpty()) { String userHome = System.getProperty("user.home"); String defaultPath = jEdit.getProperty("vfs.browser.defaultPath"); if("home".equals(defaultPath)) path = userHome; else if("working".equals(defaultPath)) path = System.getProperty("user.dir"); else if("buffer".equals(defaultPath)) { Buffer buffer = view.getBuffer(); boolean browseable = (buffer.getVFS().getCapabilities() & VFS.BROWSE_CAP) != 0; if (browseable) path = buffer.getDirectory(); } else if("last".equals(defaultPath)) { path = getLastVisitedPath(); if (path == null) path = "~"; } else if("favorites".equals(defaultPath)) path = "favorites:"; if (path == null || path.isEmpty()) { // unknown value??!!! path = userHome; } } final String _path = path; ThreadUtilities.runInDispatchThread(new Runnable() { @Override public void run() { setDirectory(_path); } }); } //}}} //{{{ focusOnDefaultComponent() method @Override public void focusOnDefaultComponent() { // pathField.requestFocus(); defaultFocusComponent.requestFocus(); } //}}} // {{{ setDefaultFocusComponent() /** Only used by VFSFileChooserDialog, since it embeds this in a dialog */ void setDefaultFocusComponent(JComponent c) { defaultFocusComponent = c; }// }}} //{{{ addNotify() method @Override public void addNotify() { super.addNotify(); EditBus.addToBus(this); } //}}} //{{{ removeNotify() method @Override public void removeNotify() { super.removeNotify(); jEdit.setBooleanProperty("vfs.browser.filter-enabled", filterCheckbox.isSelected()); if(mode == BROWSER || !jEdit.getBooleanProperty( "vfs.browser.currentBufferFilter")) { VFSFileFilter selectedFilter = (VFSFileFilter) filterField.getSelectedItem(); if (selectedFilter instanceof GlobVFSFileFilter) jEdit.setProperty("vfs.browser.last-filter", ((GlobVFSFileFilter)selectedFilter).getGlob()); } EditBus.removeFromBus(this); } //}}} //{{{ handlePropertiesChanged() method @EBHandler public void handlePropertiesChanged(PropertiesChanged msg) { propertiesChanged(); } //}}} //{{{ handleBufferUpdate() method @EBHandler public void handleBufferUpdate(BufferUpdate bmsg) { if (bmsg.getWhat() == BufferUpdate.CREATED || bmsg.getWhat() == BufferUpdate.CLOSED) { browserView.updateFileView(); } } //}}} //{{{ handlePluginUpdate() method @EBHandler public void handlePluginUpdate(PluginUpdate pmsg) { if((pmsg.getWhat() == PluginUpdate.LOADED || pmsg.getWhat() == PluginUpdate.UNLOADED) && plugins != null /* plugins can be null if the VFSBrowser menu bar is hidden */) { plugins.updatePopupMenu(); } } //}}} //{{{ handleVFSUpdate() method @EBHandler public void handleVFSUpdate(VFSUpdate msg) { maybeReloadDirectory(msg.getPath()); } //}}} //{{{ getView() method public View getView() { return view; } //}}} //{{{ getMode() method public int getMode() { return mode; } //}}} //{{{ isMultipleSelectionEnabled() method public boolean isMultipleSelectionEnabled() { return multipleSelection; } //}}} //{{{ isHorizontalLayout() method public boolean isHorizontalLayout() { return horizontalLayout; } //}}} //{{{ getShowHiddenFiles() method public boolean getShowHiddenFiles() { return showHiddenFiles; } //}}} //{{{ setShowHiddenFiles() method public void setShowHiddenFiles(boolean showHiddenFiles) { this.showHiddenFiles = showHiddenFiles; } //}}} //{{{ getVFSFileFilter() method /** * Returns the currently active VFSFileFilter. * * @since jEdit 4.3pre7 */ public VFSFileFilter getVFSFileFilter() { if (mode == CHOOSE_DIRECTORY_DIALOG) return new DirectoriesOnlyFilter(); return (VFSFileFilter) filterField.getSelectedItem(); } //}}} //{{{ addVFSFileFilter() method /** * Adds a file filter to the browser. * * @since jEdit 4.3pre7 */ public void addVFSFileFilter(VFSFileFilter filter) { filterField.addItem(filter); if (filterField.getItemCount() == 2) { filterField.setEditor(filterEditor); filterField.setEditable(true); GridBagLayout layout = (GridBagLayout) pathAndFilterPanel.getLayout(); GridBagConstraints cons =layout.getConstraints(filterEditor); cons.gridwidth = GridBagConstraints.REMAINDER; cons.fill = GridBagConstraints.HORIZONTAL; cons.gridx = 1; cons.weightx = 1; pathAndFilterPanel.remove(filterEditor); layout.setConstraints(filterField, cons); pathAndFilterPanel.add(filterField); pathAndFilterPanel.validate(); pathAndFilterPanel.repaint(); } } //}}} //{{{ setFilenameFilter() method public void setFilenameFilter(String filter) { if(filter == null || filter.length() == 0 || "*".equals(filter)) filterCheckbox.setSelected(false); else { filterCheckbox.setSelected(true); filterEditor.setItem(new GlobVFSFileFilter(filter)); } } //}}} //{{{ getDirectoryField() method public HistoryTextField getDirectoryField() { return pathField; } //}}} //{{{ getDirectory() method public String getDirectory() { return path; } //}}} // {{{ Directory Stack operations /** * @since jedit 4.3pre15 */ public void previousDirectory() { if (historyStack.size() > 1) { historyStack.pop(); nextDirectoryStack.push(path); setDirectory(historyStack.peek()); historyStack.pop(); } } /** * @since jEdit 4.3pre15 */ public void nextDirectory() { if (!nextDirectoryStack.isEmpty()) { setDirectory(nextDirectoryStack.pop()); } } // }}} //{{{ getLastVisitedPath() method /** * Returns the last path visited by VFSBrowser. If no path was ever * visited, returns <code>null</code>, * @since 5.1 */ public static String getLastVisitedPath() { HistoryModel pathModel = HistoryModel.getModel("vfs.browser.path"); if(pathModel.getSize() == 0) return null; else return pathModel.getItem(0); } //}}} //{{{ setDirectory() method public void setDirectory(String path) { if(path.startsWith("file:")) path = path.substring(5); path = MiscUtilities.expandVariables(path); pathField.setText(path); if(!startRequest()) return; historyStack.push(path); browserView.saveExpansionState(); Runnable delayedAWTRequest = new DelayedEndRequest(); browserView.loadDirectory(null,path,true, delayedAWTRequest); this.path = path; } //}}} //{{{ getRootDirectory() method public static String getRootDirectory() { if(OperatingSystem.isMacOS() || OperatingSystem.isWindows()) return FileRootsVFS.PROTOCOL + ':'; else return "/"; } //}}} //{{{ rootDirectory() method /** * Goes to the local drives directory. * @since jEdit 4.0pre4 */ public void rootDirectory() { setDirectory(getRootDirectory()); } //}}} //{{{ reloadDirectory() method public void reloadDirectory() { // used by FTP plugin to clear directory cache VFSManager.getVFSForPath(path).reloadDirectory(path); browserView.saveExpansionState(); browserView.loadDirectory(null,path,false); } //}}} //{{{ delete() method /** * Note that all files must be on the same VFS. * @since jEdit 4.3pre2 */ public void delete(VFSFile[] files) { String dialogType; if(MiscUtilities.isURL(files[0].getDeletePath()) && FavoritesVFS.PROTOCOL.equals( MiscUtilities.getProtocolOfURL(files[0].getDeletePath()))) { dialogType = "vfs.browser.delete-favorites"; } else { dialogType = "vfs.browser.delete-confirm"; } String typeStr = "files"; for (VFSFile file : files) { if (file.getType() == VFSFile.DIRECTORY) { typeStr = "directories and their contents"; break; } } // In the previous version the first argument was the file list, now it is a list so the file list is not // created anymore. But for compatibility an empty string is used. Object[] args = { "", typeStr }; JList list = new JList(files); list.setVisibleRowCount(10); JPanel panel = new JPanel(new BorderLayout()); panel.add(new JScrollPane(list)); panel.add(new JLabel(jEdit.getProperty(dialogType+".message", args)), BorderLayout.PAGE_START); int result = JOptionPane .showConfirmDialog(this, panel, jEdit.getProperty(dialogType+".title"), JOptionPane.OK_CANCEL_OPTION, JOptionPane.QUESTION_MESSAGE); if(result != JOptionPane.YES_OPTION) return; VFS vfs = VFSManager.getVFSForPath(files[0].getDeletePath()); if(!startRequest()) return; final CountDownLatch latch = new CountDownLatch(files.length); for(int i = 0; i < files.length; i++) { Object session = vfs.createVFSSession(files[i].getDeletePath(),this); if(session == null) { latch.countDown(); continue; } final Task task = new DeleteBrowserTask(this, session, vfs, files[i].getDeletePath()); TaskManager.instance.addTaskListener(new TaskAdapter() { @Override public void done(Task t) { if (task == t) { latch.countDown(); TaskManager.instance.removeTaskListener(this); } } }); ThreadUtilities.runInBackground(task); } try { latch.await(); } catch (InterruptedException e) { Log.log(Log.ERROR, this, e, e); } Runnable delayedAWTRequest = new DelayedEndRequest(); EventQueue.invokeLater(delayedAWTRequest); } //}}} //{{{ rename() methods /** * Rename a file. * It will prompt for the new name. * @param from the file to rename * @since jEdit 4.5pre1 */ public void rename(VFSFile from) { String filename; if (from instanceof FavoritesVFS.Favorite) { FavoritesVFS.Favorite favorite = (FavoritesVFS.Favorite) from; filename = favorite.getLabel(); } else { filename = from.getName(); } String[] args = { filename }; String to = GUIUtilities.input(this,"vfs.browser.rename", args,filename); if (to == null) return; rename(from.getVFS(), from.getPath(), to); } /** * Rename a file. * It will prompt for the new name. * @param from the file to rename * @param to the target name * @since jEdit 4.5pre1 */ public void rename(VFSFile from, String to) { rename(from.getVFS(), from.getPath(), to); } public void rename(String from) { VFS vfs = VFSManager.getVFSForPath(from); String filename = vfs.getFileName(from); String[] args = { filename }; String to = GUIUtilities.input(this,"vfs.browser.rename", args,filename); if (to == null) return; rename(from, to); } /** * Rename a file * @param vfs the VFS. It may be strange to give the VFS, but in * case of FavoriteVFS we cannot know that it is favorite. * @param from the full path name of the file to be renamed * @param newname the new name (only filename, not full path) * @since jEdit 4.5pre1 */ private void rename(VFS vfs, String from, String newname) { String filename = vfs.getFileName(from); String to = newname; if(to == null) return; if (!(vfs instanceof FavoritesVFS)) { if (filename.equals(newname)) return; to = MiscUtilities.constructPath(vfs.getParentOfPath(from),to); } Object session = vfs.createVFSSession(from,this); if(session == null) return; if(!startRequest()) return; Runnable delayedAWTRequest = new DelayedEndRequest(); Task renameTask = new RenameBrowserTask(this, session, vfs, from, to, delayedAWTRequest); ThreadUtilities.runInBackground(renameTask); } /** * Rename a file * @param from the full path name of the file to be renamed * @param newname the new name (only filename, not full path) */ public void rename(String from, String newname) { VFS vfs = VFSManager.getVFSForPath(from); rename(vfs, from, newname); } //}}} //{{{ mkdir() method public void mkdir() { String newDirectory = GUIUtilities.input(this,"vfs.browser.mkdir",null); if(newDirectory == null) return; // if a directory is selected, create new dir in there. // if a file is selected, create new dir inside its parent. final VFSFile[] selected = getSelectedFiles(); String parent; if(selected.length == 0) parent = path; else if(selected[0].getType() == VFSFile.FILE) { parent = selected[0].getPath(); parent = VFSManager.getVFSForPath(parent) .getParentOfPath(parent); } else parent = selected[0].getPath(); VFS vfs = VFSManager.getVFSForPath(parent); // path is the currently viewed directory in the browser newDirectory = MiscUtilities.constructPath(parent,newDirectory); Object session = vfs.createVFSSession(newDirectory,this); if(session == null) return; if(!startRequest()) return; Runnable runnable = new Runnable() { @Override public void run() { endRequest(); if (selected.length != 0 && selected[0].getType() != VFSFile.FILE) { VFSDirectoryEntryTable directoryEntryTable = browserView.getTable(); int selectedRow = directoryEntryTable.getSelectedRow(); VFSDirectoryEntryTableModel model = (VFSDirectoryEntryTableModel) directoryEntryTable.getModel(); VFSDirectoryEntryTableModel.Entry entry = model.files[selectedRow]; if (!entry.expanded) { browserView.clearExpansionState(); browserView.loadDirectory(entry,entry.dirEntry.getPath(), false); } } } }; Task mkdirTask = new MkDirBrowserTask(this, session, vfs, newDirectory, runnable); ThreadUtilities.runInBackground(mkdirTask); } //}}} //{{{ newFile() method /** * Creates a new file in the current directory. * @since jEdit 4.0pre2 */ public void newFile() { VFSFile[] selected = getSelectedFiles(); if(selected.length >= 1) { VFSFile file = selected[0]; if(file.getType() == VFSFile.DIRECTORY) jEdit.newFile(view,file.getPath()); else { VFS vfs = VFSManager.getVFSForPath(file.getPath()); jEdit.newFile(view,vfs.getParentOfPath(file.getPath())); } } else jEdit.newFile(view,path); } //}}} //{{{ fileProperties() method /** * Show selected file's properties. */ public void fileProperties(VFSFile[] files) { new FilePropertiesDialog(view, this, files); } //}}} //{{{ searchInDirectory() method /** * Opens a directory search in the current directory. * @since jEdit 4.0pre2 */ public void searchInDirectory() { VFSFile[] selected = getSelectedFiles(); if(selected.length >= 1) { VFSFile file = selected[0]; searchInDirectory(file.getPath(),file.getType() != VFSFile.FILE); } else { searchInDirectory(path,true); } } //}}} //{{{ searchInDirectory() method /** * Opens a directory search in the specified directory. * @param path The path name * @param directory True if the path is a directory, false if it is a file * @since jEdit 4.2pre1 */ public void searchInDirectory(String path, boolean directory) { String filter; VFSFileFilter vfsff = getVFSFileFilter(); if (vfsff instanceof GlobVFSFileFilter) filter = ((GlobVFSFileFilter)vfsff).getGlob(); else filter = "*"; if (!directory) { String name = MiscUtilities.getFileName(path); String ext = MiscUtilities.getFileExtension(name); filter = ext == null || ext.length() == 0 ? filter : '*' + ext; path = MiscUtilities.getParentOfPath(path); } SearchAndReplace.setSearchFileSet(new DirectoryListSet( path,filter,true)); SearchDialog.showSearchDialog(view,null,SearchDialog.DIRECTORY); } //}}} //{{{ getBrowserView() method BrowserView getBrowserView() { return browserView; } //}}} //{{{ getSelectedFiles() method /** * Return the selected files in the lower browser tree. * @since jEdit 4.3pre2 */ public VFSFile[] getSelectedFiles() { return browserView.getSelectedFiles(); } //}}} //{{{ getSelectedFiles() method /** * Return the selected files from the point of view of the * given component. This may be the selected directory from the * upper tree component of the browser (directory tree) or * the selected files in the bottom tree component. * This method is to be used by code running inside VFSBrowser * such as a DynamicMenuProvider. Use the other method otherwise. * The main difference is this function searches the component * hierarchy for a {@link BrowserView.ParentDirectoryList} to get * the list of currently selected files from there. Otherwise, it * returns what {@link #getSelectedFiles()} would return. * @param source the source component to start from when * navigating the component hierarchy * @since jEdit 4.4pre1 */ public VFSFile[] getSelectedFiles(Component source) { if(GUIUtilities.getComponentParent(source, BrowserView.ParentDirectoryList.class) != null) { Object[] selected = getBrowserView() .getParentDirectoryList() .getSelectedValues(); VFSFile[] returnValue = new VFSFile[ selected.length]; System.arraycopy(selected,0,returnValue,0, selected.length); return returnValue; } else { return getSelectedFiles(); } } //}}} //{{{ paste() method /** * Paste the file contained in the clipboard. * If the clipboard do not contains files, nothing happens. * @param file the target, it can be a file, in that case it will be pasted to * the parent directory, or a directory. */ public void paste(VFSFile file) throws IOException, UnsupportedFlavorException { if (file == null) throw new IllegalArgumentException("file cannot be null"); String targetPath = null; switch (file.getType()) { case VFSFile.FILESYSTEM: return; case VFSFile.FILE: targetPath = MiscUtilities.getParentOfPath(file.getPath()); break; case VFSFile.DIRECTORY: targetPath = file.getPath(); break; default: Log.log(Log.ERROR, this, "Unknown file type " + file.getType()); return; } VFS vfs = VFSManager.getVFSForPath(targetPath); Object vfsSession = null; try { vfsSession = vfs.createVFSSession(targetPath, this); if (vfsSession == null) { Log.log(Log.ERROR, this, "Unable to create session for " + targetPath); return; } Transferable transferable = Registers.getRegister('$').getTransferable(); List<String> sources = new ArrayList<String>(); if (transferable.isDataFlavorSupported(ListVFSFileTransferable.jEditFileList)) { Iterable<VFSFile> copiedFiles = (Iterable<VFSFile>) transferable.getTransferData(ListVFSFileTransferable.jEditFileList); for (VFSFile copiedFile : copiedFiles) { sources.add(copiedFile.getPath()); } } else if (transferable.isDataFlavorSupported(DataFlavor.javaFileListFlavor)) { Iterable<File> copiedFiles = (Iterable<File>) transferable.getTransferData(DataFlavor.javaFileListFlavor); for (File copiedFile : copiedFiles) { sources.add(copiedFile.getAbsolutePath()); } } CopyFileWorker worker = new CopyFileWorker(this, sources, targetPath, CopyFileWorker.Behavior.RENAME); ThreadUtilities.runInBackground(worker); } finally { if (vfsSession != null) vfs._endVFSSession(vfsSession, this); } } //}}} //{{{ locateFile() method /** * Goes to the given file's directory and selects the file in the list. * @param path The file * @since jEdit 4.2pre2 */ public void locateFile(final String path) { VFSFileFilter filter = getVFSFileFilter(); if(!filter.accept(MiscUtilities.getFileName(path))) setFilenameFilter(null); setDirectory(MiscUtilities.getParentOfPath(path)); // Do not change this until all VFS Browser tasks are // done in ThreadUtilities AwtRunnableQueue.INSTANCE.runAfterIoTasks(new Runnable() { public void run() { browserView.getTable().selectFile(path); } }); } //}}} //{{{ createPluginsMenu() method public JComponent createPluginsMenu(JComponent pluginMenu, boolean showManagerOptions) { if(showManagerOptions && getMode() == BROWSER) { pluginMenu.add(GUIUtilities.loadMenuItem("plugin-manager",false)); pluginMenu.add(GUIUtilities.loadMenuItem("plugin-options",false)); if (pluginMenu instanceof JMenu) ((JMenu)pluginMenu).addSeparator(); else if (pluginMenu instanceof JPopupMenu) ((JPopupMenu)pluginMenu).addSeparator(); } else /* we're in a modal dialog */; List<JMenuItem> vec = new ArrayList<JMenuItem>(); //{{{ new API EditPlugin[] plugins = jEdit.getPlugins(); for (EditPlugin plugin : plugins) { JMenuItem menuItem = plugin.createBrowserMenuItems(); if (menuItem != null) vec.add(menuItem); } //}}} if (!vec.isEmpty()) { Collections.sort(vec,new MenuItemTextComparator()); for (JMenuItem item : vec) pluginMenu.add(item); } else { JMenuItem mi = new JMenuItem(jEdit.getProperty( "vfs.browser.plugins.no-plugins.label")); mi.setEnabled(false); pluginMenu.add(mi); } return pluginMenu; } //}}} //{{{ addBrowserListener() method public void addBrowserListener(BrowserListener l) { listenerList.add(BrowserListener.class,l); } //}}} //{{{ removeBrowserListener() method public void removeBrowserListener(BrowserListener l) { listenerList.remove(BrowserListener.class,l); } //}}} //{{{ filesActivated() method // canDoubleClickClose set to false when ENTER pressed public static final int M_OPEN = 0; public static final int M_OPEN_NEW_VIEW = 1; public static final int M_OPEN_NEW_PLAIN_VIEW = 2; public static final int M_OPEN_NEW_SPLIT = 3; public static final int M_INSERT = 4; /** * This method does the "double-click" handling. It is public so that * <code>browser.actions.xml</code> can bind to it. * @since jEdit 4.2pre2 */ public void filesActivated(int mode, boolean canDoubleClickClose) { VFSFile[] selectedFiles = browserView.getSelectedFiles(); Buffer buffer = null; check_selected: for (VFSFile file : selectedFiles) { if (file.getType() == VFSFile.DIRECTORY || file.getType() == VFSFile.FILESYSTEM) { if (mode == M_OPEN_NEW_VIEW && this.mode == BROWSER) browseDirectoryInNewWindow(view, file.getPath()); else if (selectedFiles.length == 1) setDirectory(file.getPath()); } else if (this.mode == BROWSER || this.mode == BROWSER_DIALOG) { if (mode == M_INSERT) { view.getBuffer().insertFile(view, file.getPath()); continue check_selected; } Buffer _buffer = jEdit.getBuffer(file.getPath()); if (_buffer == null) { Hashtable<String, Object> props = new Hashtable<String, Object>(); if (currentEncoding != null) { props.put(JEditBuffer.ENCODING, currentEncoding); } props.put(Buffer.ENCODING_AUTODETECT, autoDetectEncoding); _buffer = jEdit.openFile(view, null, file.getPath(), false, props); } else if (doubleClickClose && canDoubleClickClose && this.mode != BROWSER_DIALOG && selectedFiles.length == 1) { // close if this buffer is currently // visible in the view. EditPane[] editPanes = view.getEditPanes(); for (EditPane editPane : editPanes) { if (editPane.getBuffer() == _buffer) { jEdit.closeBuffer(view, _buffer); return; } } } if (_buffer != null) buffer = _buffer; } else { // if a file is selected in OPEN_DIALOG or // SAVE_DIALOG mode, just let the listener(s) // handle it } } if(buffer != null) { switch(mode) { case M_OPEN: view.setBuffer(buffer); break; case M_OPEN_NEW_VIEW: jEdit.newView(view,buffer,false); break; case M_OPEN_NEW_PLAIN_VIEW: jEdit.newView(view,buffer,true); break; case M_OPEN_NEW_SPLIT: view.splitHorizontally().setBuffer(buffer); break; } } Object[] listeners = listenerList.getListenerList(); for(int i = 0; i < listeners.length; i++) { if(listeners[i] == BrowserListener.class) { BrowserListener l = (BrowserListener)listeners[i+1]; l.filesActivated(this,selectedFiles); } } } //}}} //{{{ dispose() method /** Disposes the browser, regardless of whether it is a dialog or a dockable */ public void dispose() { if (mode == BROWSER) { view.getDockableWindowManager().hideDockableWindow(NAME); } else { GUIUtilities.getParentDialog(this).dispose(); } }//}}} //{{{ move() method @Override public void move(String newPosition) { boolean horz = mode != BROWSER || DockableWindowManager.TOP.equals(newPosition) || DockableWindowManager.BOTTOM.equals(newPosition); if (horz == horizontalLayout) return; horizontalLayout = horz; topBox.remove(toolbarBox); toolbarBox = new Box(horizontalLayout ? BoxLayout.X_AXIS : BoxLayout.Y_AXIS); topBox.add(toolbarBox, 0); propertiesChanged(); } //}}} //{{{ Package-private members // This can be null untill an user explicitly selects an encoding // so that this don't overwrite more accurate encoding information // like buffer histories. String currentEncoding; boolean autoDetectEncoding; //{{{ directoryLoaded() method void directoryLoaded(Object node, Object[] loadInfo, boolean addToHistory) { String path = (String)loadInfo[0]; if(path == null) { // there was an error return; } VFSFile[] list = (VFSFile[])loadInfo[1]; if(node == null) { // This is the new, canonical path VFSBrowser.this.path = path; if(!pathField.getText().equals(path)) pathField.setText(path); if(path.endsWith("/") || path.endsWith(File.separator)) { // ensure consistent history; // eg we don't want both // foo/ and foo path = path.substring(0, path.length() - 1); } if(addToHistory) { HistoryModel.getModel("vfs.browser.path") .addItem(path); } } boolean filterEnabled = filterCheckbox.isSelected(); List<VFSFile> directoryList = new ArrayList<VFSFile>(); int directories = 0; int files = 0; int invisible = 0; if(list != null) { VFSFileFilter filter = getVFSFileFilter(); for (VFSFile file : list) { if (file.isHidden() && !showHiddenFiles) { invisible++; continue; } if (filter != null && (filterEnabled || filter instanceof DirectoriesOnlyFilter) && !filter.accept(file)) { invisible++; continue; } if (file.getType() == VFSFile.FILE) files++; else directories++; directoryList.add(file); } Collections.sort(directoryList, new VFS.DirectoryEntryCompare( sortMixFilesAndDirs, sortIgnoreCase)); } browserView.directoryLoaded(node,path, directoryList); // to notify listeners that any existing // selection has been deactivated // turns out under some circumstances this // method can switch the current buffer in // BROWSER mode. // in any case, this is only needed for the // directory chooser (why?), so we add a // check. otherwise poor Rick will go insane. if(mode == CHOOSE_DIRECTORY_DIALOG) filesSelected(); } //}}} //{{{ filesSelected() method void filesSelected() { VFSFile[] selectedFiles = browserView.getSelectedFiles(); if(mode == BROWSER) { for (VFSFile file : selectedFiles) { Buffer buffer = jEdit.getBuffer(file.getPath()); if (buffer != null && view != null) view.setBuffer(buffer); } } Object[] listeners = listenerList.getListenerList(); for(int i = 0; i < listeners.length; i++) { if(listeners[i] == BrowserListener.class) { BrowserListener l = (BrowserListener)listeners[i+1]; l.filesSelected(this,selectedFiles); } } } //}}} //{{{ endRequest() method void endRequest() { requestRunning = false; } //}}} //}}} //{{{ Private members private static final ActionContext actionContext; static { actionContext = new BrowserActionContext(); ActionSet builtInActionSet = new ActionSet(null,null,null, jEdit.class.getResource("browser.actions.xml")); builtInActionSet.setLabel(jEdit.getProperty("action-set.browser")); builtInActionSet.load(); actionContext.addActionSet(builtInActionSet); } //{{{ Instance variables private EventListenerList listenerList; private View view; private boolean horizontalLayout; private String path; private JPanel pathAndFilterPanel; private HistoryTextField pathField; private JComponent defaultFocusComponent; private JCheckBox filterCheckbox; private HistoryComboBoxEditor filterEditor; private JComboBox filterField; private Box toolbarBox; private Box topBox; private FavoritesMenuButton favorites; private PluginsMenuButton plugins; private BrowserView browserView; private int mode; private boolean multipleSelection; private boolean showHiddenFiles; private boolean sortMixFilesAndDirs; private boolean sortIgnoreCase; private boolean doubleClickClose; private boolean requestRunning; private boolean maybeReloadRequestRunning; private final Stack<String> historyStack = new Stack<String>(); private final Stack<String> nextDirectoryStack = new Stack<String>(); //}}} //{{{ createMenuBar() method private Container createMenuBar() { JToolBar menuBar = new JToolBar(); menuBar.setFloatable(false); menuBar.add(new CommandsMenuButton()); menuBar.add(Box.createHorizontalStrut(3)); menuBar.add(plugins = new PluginsMenuButton()); menuBar.add(Box.createHorizontalStrut(3)); menuBar.add(favorites = new FavoritesMenuButton()); return menuBar; } //}}} //{{{ createToolBar() method private Container createToolBar() { if(mode == BROWSER) return GUIUtilities.loadToolBar(actionContext, "vfs.browser.toolbar-browser"); else return GUIUtilities.loadToolBar(actionContext, "vfs.browser.toolbar-dialog"); } //}}} //{{{ propertiesChanged() method private void propertiesChanged() { showHiddenFiles = jEdit.getBooleanProperty("vfs.browser.showHiddenFiles"); sortMixFilesAndDirs = jEdit.getBooleanProperty("vfs.browser.sortMixFilesAndDirs"); sortIgnoreCase = jEdit.getBooleanProperty("vfs.browser.sortIgnoreCase"); doubleClickClose = jEdit.getBooleanProperty("vfs.browser.doubleClickClose"); browserView.propertiesChanged(); toolbarBox.removeAll(); if(jEdit.getBooleanProperty("vfs.browser.showToolbar")) { Container toolbar = createToolBar(); toolbarBox.add(toolbar); } if(jEdit.getBooleanProperty("vfs.browser.showMenubar")) { Container menubar = createMenuBar(); if(horizontalLayout) { toolbarBox.add(menubar,0); } else { menubar.add(Box.createGlue()); toolbarBox.add(menubar); } } else { plugins = null; favorites = null; } revalidate(); if(path != null) reloadDirectory(); } //}}} /* We do this stuff because the browser is not able to handle * more than one request yet */ //{{{ startRequest() method private boolean startRequest() { if(requestRunning) { // dump stack trace for debugging purposes Log.log(Log.DEBUG,this,new Throwable("For debugging purposes")); GUIUtilities.error(this,"browser-multiple-io",null); return false; } else { requestRunning = true; return true; } } //}}} //{{{ updateFilterEnabled() method private void updateFilterEnabled() { filterField.setEnabled(filterCheckbox.isSelected()); filterEditor.setEnabled(filterCheckbox.isSelected()); } //}}} //{{{ maybeReloadDirectory() method private void maybeReloadDirectory(String dir) { if(MiscUtilities.isURL(dir) && MiscUtilities.getProtocolOfURL(dir).equals( FavoritesVFS.PROTOCOL)) { if(favorites != null) favorites.popup = null; } // this is a dirty hack and it relies on the fact // that updates for parents are sent before updates // for the changed nodes themselves (if this was not // the case, the browser wouldn't be updated properly // on delete, etc). // // to avoid causing '> 1 request' errors, don't reload // directory if request already active if(maybeReloadRequestRunning) { //Log.log(Log.WARNING,this,"VFS update: request already in progress"); return; } // save a file -> sends vfs update. if a VFS file dialog box // is shown from the same event frame as the save, the // VFSUpdate will be delivered before the directory is loaded, // and before the path is set. if(path != null) { try { maybeReloadRequestRunning = true; browserView.maybeReloadDirectory(dir); } finally { // Do not change this until all VFS Browser tasks are // done in ThreadUtilities AwtRunnableQueue.INSTANCE.runAfterIoTasks(new Runnable() { public void run() { maybeReloadRequestRunning = false; } }); } } } //}}} //}}} //{{{ Inner classes //{{{ ActionHandler class class ActionHandler implements ActionListener, ItemListener { @Override public void actionPerformed(ActionEvent evt) { if (isProcessingEvent) return; Object source = evt.getSource(); if (source == pathField || source == filterCheckbox) { isProcessingEvent = true; resetLater(); updateFilterEnabled(); String p = pathField.getText(); if(p != null) setDirectory(p); browserView.focusOnFileView(); } else if (source == filterField.getEditor()) { // force the editor to refresh. filterField.getEditor().setItem( filterField.getEditor().getItem()); } // depending on Swing look & feel, filterField.getEditor() // returns some ComboBoxUI else if (source == filterEditor) { // force the editor to refresh. filterEditor.setItem( filterEditor.getItem()); filterField.setSelectedItem( filterEditor.getItem()); // ### ugly: // itemStateChanged does not seem to get fired itemStateChanged(new ItemEvent(filterField, ItemEvent.ITEM_STATE_CHANGED, filterEditor.getItem(), ItemEvent.SELECTED)); } } @Override public void itemStateChanged(ItemEvent e) { if (isProcessingEvent) return; if (e.getStateChange() != ItemEvent.SELECTED) return; isProcessingEvent = true; resetLater(); filterField.setEditable(e.getItem() instanceof GlobVFSFileFilter); updateFilterEnabled(); String path = pathField.getText(); if(path != null) setDirectory(path); browserView.focusOnFileView(); } /** * Why this method exists: since both actionPerformed() * and itemStateChanged() above can change the combo box, * executing one of them can cause a chain reaction causing * the other method to be called. This would cause the * VFS subsystem to be called several times, which would * cause a warning to show up if the first operation is * still in progress, or cause a second operation to happen * which is not really wanted especially if we're talking * about a remove VFS. So the methods set a flag saying * that something is going on, and this method resets * the flag after the AWT thread is done with the * current events. */ private void resetLater() { EventQueue.invokeLater(new Runnable() { @Override public void run() { isProcessingEvent = false; } }); } private boolean isProcessingEvent; } //}}} // {{{ MenuButton (abstract class) @SuppressWarnings("serial") static abstract class MenuButton extends RolloverButton implements KeyListener { JPopupMenu popup; //{{{ MenuButton constructor MenuButton() { setIcon(GUIUtilities.loadIcon(jEdit.getProperty("dropdown-arrow.icon"))); setHorizontalTextPosition(SwingConstants.LEADING); // setRequestFocusEnabled(false); setMargin(new Insets(1,1,1,1)); addMouseListener(new MouseHandler()); addKeyListener(this); if(OperatingSystem.isMacOSLF()) putClientProperty("JButton.buttonType","toolbar"); setAction(new Action()); } //}}} public void keyReleased(KeyEvent e) {} public void keyTyped(KeyEvent e) {} public void keyPressed(KeyEvent e) { if ((e.getKeyCode() == KeyEvent.VK_DOWN) || (e.getKeyCode() == KeyEvent.VK_ENTER )) { doPopup(); e.consume(); return; } } abstract void doPopup(); //{{{ MouseHandler class class MouseHandler extends MouseAdapter { @Override public void mousePressed(MouseEvent evt) { if(popup == null || !popup.isVisible()) { doPopup(); } else { popup.setVisible(false); } } } //}}} //{{{ Action class class Action extends AbstractAction { public void actionPerformed(ActionEvent ae) { doPopup(); } } //}}} } //}}} //{{{ CommandsMenuButton class class CommandsMenuButton extends MenuButton { //{{{ CommandsMenuButton constructor CommandsMenuButton() { setText(jEdit.getProperty("vfs.browser.commands.label")); GUIUtilities.setAutoMnemonic(this); setName("commands"); popup = new BrowserCommandsMenu(VFSBrowser.this, null); } //}}} // BrowserCommandsMenu popup; void doPopup() { ((BrowserCommandsMenu) popup).update(); GUIUtilities.showPopupMenu( popup, this, 0, getHeight(), false); } } //}}} //{{{ PluginsMenuButton class class PluginsMenuButton extends MenuButton { //{{{ PluginsMenuButton constructor PluginsMenuButton() { setText(jEdit.getProperty("vfs.browser.plugins.label")); GUIUtilities.setAutoMnemonic(this); setName("plugins"); setMargin(new Insets(1,1,1,1)); popup = null; createPopupMenu(); } //}}} //{{{ updatePopupMenu() method void updatePopupMenu() { popup = null; } //}}} void doPopup() { if (popup == null) createPopupMenu(); GUIUtilities.showPopupMenu(popup, this, 0, getHeight(), false); } //{{{ createPopupMenu() method private void createPopupMenu() { if(popup != null) return; popup = (JPopupMenu)createPluginsMenu(new JPopupMenu(),true); } //}}} } //}}} //{{{ FavoritesMenuButton class class FavoritesMenuButton extends MenuButton { //{{{ FavoritesMenuButton constructor FavoritesMenuButton() { setText(jEdit.getProperty("vfs.browser.favorites.label")); GUIUtilities.setAutoMnemonic(this); setName("favorites"); createPopupMenu(); } //}}} void doPopup() { if (popup==null) createPopupMenu(); GUIUtilities.showPopupMenu(popup, this, 0, getHeight(), false); } //{{{ createPopupMenu() method void createPopupMenu() { popup = new JPopupMenu(); ActionHandler actionHandler = new ActionHandler(); JMenuItem mi = new JMenuItem( jEdit.getProperty( "vfs.browser.favorites.add-to-favorites.label")); mi.setActionCommand("add-to-favorites"); mi.addActionListener(actionHandler); popup.add(mi); mi = new JMenuItem(jEdit.getProperty( "vfs.browser.favorites.edit-favorites.label")); mi.setActionCommand("dir@favorites:"); mi.addActionListener(actionHandler); popup.add(mi); popup.addSeparator(); VFSFile[] favorites = FavoritesVFS.getFavorites(); if(favorites.length == 0) { mi = new JMenuItem(jEdit.getProperty( "vfs.browser.favorites.no-favorites.label")); mi.setEnabled(false); popup.add(mi); } else { Arrays.sort(favorites, new VFS.DirectoryEntryCompare( sortMixFilesAndDirs, sortIgnoreCase)); for(int i = 0; i < favorites.length; i++) { FavoritesVFS.Favorite favorite = (FavoritesVFS.Favorite) favorites[i]; mi = new JMenuItem(favorite.getLabel()); mi.setIcon(FileCellRenderer.getIconForFile( favorite,false)); String cmd = (favorite.getType() == VFSFile.FILE ? "file@" : "dir@") + favorite.getPath(); mi.setActionCommand(cmd); mi.addActionListener(actionHandler); popup.add(mi); } } } //}}} //{{{ ActionHandler class class ActionHandler implements ActionListener { @Override public void actionPerformed(ActionEvent evt) { String actionCommand = evt.getActionCommand(); if("add-to-favorites".equals(actionCommand)) { // if any directories are selected, add // them, otherwise add current directory VFSFile[] selected = getSelectedFiles(); if(selected == null || selected.length == 0) { if(path.equals(FavoritesVFS.PROTOCOL + ':')) { GUIUtilities.error(VFSBrowser.this, "vfs.browser.recurse-favorites", null); } else { FavoritesVFS.addToFavorites(path, VFSFile.DIRECTORY); } } else { for (VFSFile file : selected) { FavoritesVFS.addToFavorites( file.getPath(), file.getType()); } } } else if(actionCommand.startsWith("dir@")) { setDirectory(actionCommand.substring(4)); } else if(actionCommand.startsWith("file@")) { switch(getMode()) { case BROWSER: jEdit.openFile(view,actionCommand.substring(5)); break; default: locateFile(actionCommand.substring(5)); break; } } } } //}}} } //}}} //{{{ BrowserActionContext class static class BrowserActionContext extends ActionContext { @Override public void invokeAction(EventObject evt, EditAction action) { Component source = (Component)evt.getSource(); VFSBrowser browser = (VFSBrowser) GUIUtilities.getComponentParent( source, VFSBrowser.class); VFSFile[] files = browser.getSelectedFiles(source); // in the future we will want something better, // eg. having an 'evt' object passed to // EditAction.invoke(). // for now, since all browser actions are // written in beanshell we set the 'browser' // variable directly. NameSpace global = BeanShell.getNameSpace(); try { global.setVariable("browser",browser); global.setVariable("files",files); View view = browser.getView(); // I guess ideally all browsers // should have views, but since they // don't, we just use the active view // in that case, since some actions // depend on a view being there and // I don't want to add checks to // them all if(view == null) view = jEdit.getActiveView(); action.invoke(view); } catch(UtilEvalError err) { Log.log(Log.ERROR,this,err); } finally { try { global.setVariable("browser",null); global.setVariable("files",null); } catch(UtilEvalError err) { Log.log(Log.ERROR,this,err); } } } } //}}} //{{{ HistoryComboBoxEditor class private static class HistoryComboBoxEditor extends HistoryTextField implements ComboBoxEditor { HistoryComboBoxEditor(String key) { super(key); } @Override public Object getItem() { if (current == null) { current = new GlobVFSFileFilter(getText()); } if (!current.getGlob().equals(getText())) { current.setGlob(getText()); } return current; } @Override public void setItem(Object item) { if (item == current) { // if we keep the same object, swing // will cause an event to be fired // on the default button of the dialog, // causing a beep since no file is // selected... if (item != null) { GlobVFSFileFilter filter = (GlobVFSFileFilter) item; current = new GlobVFSFileFilter(filter.getGlob()); setText(current.getGlob()); } return; } if (item != null) { // this happens when changing the selected item // in the combo; the combo has not yet fired an // itemStateChanged() event, so it's not put into // non-editable mode by the handler above. if (!(item instanceof GlobVFSFileFilter)) return; GlobVFSFileFilter filter = (GlobVFSFileFilter) item; filter = new GlobVFSFileFilter(filter.getGlob()); setText(filter.getGlob()); addCurrentToHistory(); current = filter; } else { setText("*"); current = new GlobVFSFileFilter("*"); } } @Override protected void processFocusEvent(FocusEvent e) { // AWT will call setItem() when the editor loses // focus; that can cause weird and unwanted things // to happen, so ignore lost focus events. if (e.getID() != FocusEvent.FOCUS_LOST) super.processFocusEvent(e); else { setCaretPosition(0); getCaret().setVisible(false); } } @Override public Component getEditorComponent() { return this; } private GlobVFSFileFilter current; } //}}} //{{{ VFSFileFilterRenderer class private static class VFSFileFilterRenderer extends DefaultListCellRenderer { @Override public Component getListCellRendererComponent(JList list, Object value, int index, boolean isSelected, boolean cellHasFocus) { assert value instanceof VFSFileFilter : "Filter is not a VFSFileFilter"; super.getListCellRendererComponent( list, value, index, isSelected, cellHasFocus); setText(((VFSFileFilter)value).getDescription()); return this; } } //}}} //{{{ DirectoriesOnlyFilter class public static class DirectoriesOnlyFilter implements VFSFileFilter { @Override public boolean accept(VFSFile file) { return file.getType() == VFSFile.DIRECTORY || file.getType() == VFSFile.FILESYSTEM; } @Override public boolean accept(String url) { return false; } @Override public String getDescription() { return jEdit.getProperty("vfs.browser.file_filter.dir_only"); } } //}}} //{{{ DelayedEndRequest class private class DelayedEndRequest implements Runnable { @Override public void run() { endRequest(); } } //}}} //}}} }