/jEdit/tags/jedit-4-3-pre15/org/gjt/sp/jedit/browser/VFSBrowser.java

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