/jEdit/trunk/org/gjt/sp/jedit/browser/VFSBrowser.java

# · Java · 2198 lines · 1558 code · 262 blank · 378 comment · 302 complexity · 3a884d1cac6d36bf6a8adff2ca76096f MD5 · raw file

Large files are truncated click here to view the full file

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