1. /*
  2. * @(#)BasicFileChooserUI.java 1.61 04/06/15
  3. *
  4. * Copyright 2004 Sun Microsystems, Inc. All rights reserved.
  5. * SUN PROPRIETARY/CONFIDENTIAL. Use is subject to license terms.
  6. */
  7. package javax.swing.plaf.basic;
  8. import javax.swing.*;
  9. import javax.swing.filechooser.*;
  10. import javax.swing.filechooser.FileFilter;
  11. import javax.swing.event.*;
  12. import javax.swing.plaf.*;
  13. import java.awt.*;
  14. import java.awt.event.*;
  15. import java.awt.datatransfer.*;
  16. import java.beans.*;
  17. import java.io.*;
  18. import java.util.*;
  19. import java.util.regex.*;
  20. import sun.awt.shell.ShellFolder;
  21. import sun.swing.*;
  22. import com.sun.java.swing.SwingUtilities2;
  23. /**
  24. * Basic L&F implementation of a FileChooser.
  25. *
  26. * @version %i% %g%
  27. * @author Jeff Dinkins
  28. */
  29. public class BasicFileChooserUI extends FileChooserUI {
  30. /* FileView icons */
  31. protected Icon directoryIcon = null;
  32. protected Icon fileIcon = null;
  33. protected Icon computerIcon = null;
  34. protected Icon hardDriveIcon = null;
  35. protected Icon floppyDriveIcon = null;
  36. protected Icon newFolderIcon = null;
  37. protected Icon upFolderIcon = null;
  38. protected Icon homeFolderIcon = null;
  39. protected Icon listViewIcon = null;
  40. protected Icon detailsViewIcon = null;
  41. protected int saveButtonMnemonic = 0;
  42. protected int openButtonMnemonic = 0;
  43. protected int cancelButtonMnemonic = 0;
  44. protected int updateButtonMnemonic = 0;
  45. protected int helpButtonMnemonic = 0;
  46. /**
  47. * The mnemonic keycode used for the approve button when a directory
  48. * is selected and the current selection mode is not DIRECTORIES_ONLY.
  49. *
  50. * @since 1.4
  51. */
  52. protected int directoryOpenButtonMnemonic = 0;
  53. protected String saveButtonText = null;
  54. protected String openButtonText = null;
  55. protected String cancelButtonText = null;
  56. protected String updateButtonText = null;
  57. protected String helpButtonText = null;
  58. /**
  59. * The label text displayed on the approve button when a directory
  60. * is selected and the current selection mode is not DIRECTORIES_ONLY.
  61. *
  62. * @since 1.4
  63. */
  64. protected String directoryOpenButtonText = null;
  65. private String openDialogTitleText = null;
  66. private String saveDialogTitleText = null;
  67. protected String saveButtonToolTipText = null;
  68. protected String openButtonToolTipText = null;
  69. protected String cancelButtonToolTipText = null;
  70. protected String updateButtonToolTipText = null;
  71. protected String helpButtonToolTipText = null;
  72. /**
  73. * The tooltip text displayed on the approve button when a directory
  74. * is selected and the current selection mode is not DIRECTORIES_ONLY.
  75. *
  76. * @since 1.4
  77. */
  78. protected String directoryOpenButtonToolTipText = null;
  79. // Some generic FileChooser functions
  80. private Action approveSelectionAction = new ApproveSelectionAction();
  81. private Action cancelSelectionAction = new CancelSelectionAction();
  82. private Action updateAction = new UpdateAction();
  83. private Action newFolderAction;
  84. private Action goHomeAction = new GoHomeAction();
  85. private Action changeToParentDirectoryAction = new ChangeToParentDirectoryAction();
  86. private String newFolderErrorSeparator = null;
  87. private String newFolderErrorText = null;
  88. private String fileDescriptionText = null;
  89. private String directoryDescriptionText = null;
  90. private JFileChooser filechooser = null;
  91. private boolean directorySelected = false;
  92. private File directory = null;
  93. private PropertyChangeListener propertyChangeListener = null;
  94. private AcceptAllFileFilter acceptAllFileFilter = new AcceptAllFileFilter();
  95. private FileFilter actualFileFilter = null;
  96. private GlobFilter globFilter = null;
  97. private BasicDirectoryModel model = null;
  98. private BasicFileView fileView = new BasicFileView();
  99. private boolean usesSingleFilePane;
  100. private boolean readOnly;
  101. // The accessoryPanel is a container to place the JFileChooser accessory component
  102. private JPanel accessoryPanel = null;
  103. private Handler handler;
  104. public BasicFileChooserUI(JFileChooser b) {
  105. }
  106. public void installUI(JComponent c) {
  107. accessoryPanel = new JPanel(new BorderLayout());
  108. filechooser = (JFileChooser) c;
  109. createModel();
  110. clearIconCache();
  111. installDefaults(filechooser);
  112. installComponents(filechooser);
  113. installListeners(filechooser);
  114. filechooser.applyComponentOrientation(filechooser.getComponentOrientation());
  115. }
  116. public void uninstallUI(JComponent c) {
  117. uninstallListeners((JFileChooser) filechooser);
  118. uninstallComponents((JFileChooser) filechooser);
  119. uninstallDefaults((JFileChooser) filechooser);
  120. if(accessoryPanel != null) {
  121. accessoryPanel.removeAll();
  122. }
  123. accessoryPanel = null;
  124. getFileChooser().removeAll();
  125. handler = null;
  126. }
  127. public void installComponents(JFileChooser fc) {
  128. }
  129. public void uninstallComponents(JFileChooser fc) {
  130. }
  131. protected void installListeners(JFileChooser fc) {
  132. propertyChangeListener = createPropertyChangeListener(fc);
  133. if(propertyChangeListener != null) {
  134. fc.addPropertyChangeListener(propertyChangeListener);
  135. }
  136. fc.addPropertyChangeListener(getModel());
  137. InputMap inputMap = getInputMap(JComponent.
  138. WHEN_ANCESTOR_OF_FOCUSED_COMPONENT);
  139. SwingUtilities.replaceUIInputMap(fc, JComponent.
  140. WHEN_ANCESTOR_OF_FOCUSED_COMPONENT, inputMap);
  141. ActionMap actionMap = getActionMap();
  142. SwingUtilities.replaceUIActionMap(fc, actionMap);
  143. }
  144. InputMap getInputMap(int condition) {
  145. if (condition == JComponent.WHEN_ANCESTOR_OF_FOCUSED_COMPONENT) {
  146. return (InputMap)DefaultLookup.get(getFileChooser(), this,
  147. "FileChooser.ancestorInputMap");
  148. }
  149. return null;
  150. }
  151. ActionMap getActionMap() {
  152. return createActionMap();
  153. }
  154. ActionMap createActionMap() {
  155. ActionMap map = new ActionMapUIResource();
  156. Action refreshAction = new UIAction(FilePane.ACTION_REFRESH) {
  157. public void actionPerformed(ActionEvent evt) {
  158. getFileChooser().rescanCurrentDirectory();
  159. }
  160. };
  161. map.put(FilePane.ACTION_APPROVE_SELECTION, getApproveSelectionAction());
  162. map.put(FilePane.ACTION_CANCEL, getCancelSelectionAction());
  163. map.put(FilePane.ACTION_REFRESH, refreshAction);
  164. map.put(FilePane.ACTION_CHANGE_TO_PARENT_DIRECTORY,
  165. getChangeToParentDirectoryAction());
  166. return map;
  167. }
  168. protected void uninstallListeners(JFileChooser fc) {
  169. if(propertyChangeListener != null) {
  170. fc.removePropertyChangeListener(propertyChangeListener);
  171. }
  172. fc.removePropertyChangeListener(getModel());
  173. SwingUtilities.replaceUIInputMap(fc, JComponent.
  174. WHEN_ANCESTOR_OF_FOCUSED_COMPONENT, null);
  175. SwingUtilities.replaceUIActionMap(fc, null);
  176. }
  177. protected void installDefaults(JFileChooser fc) {
  178. installIcons(fc);
  179. installStrings(fc);
  180. usesSingleFilePane = UIManager.getBoolean("FileChooser.usesSingleFilePane");
  181. readOnly = UIManager.getBoolean("FileChooser.readOnly");
  182. TransferHandler th = fc.getTransferHandler();
  183. if (th == null || th instanceof UIResource) {
  184. fc.setTransferHandler(defaultTransferHandler);
  185. }
  186. LookAndFeel.installProperty(fc, "opaque", Boolean.FALSE);
  187. }
  188. protected void installIcons(JFileChooser fc) {
  189. directoryIcon = UIManager.getIcon("FileView.directoryIcon");
  190. fileIcon = UIManager.getIcon("FileView.fileIcon");
  191. computerIcon = UIManager.getIcon("FileView.computerIcon");
  192. hardDriveIcon = UIManager.getIcon("FileView.hardDriveIcon");
  193. floppyDriveIcon = UIManager.getIcon("FileView.floppyDriveIcon");
  194. newFolderIcon = UIManager.getIcon("FileChooser.newFolderIcon");
  195. upFolderIcon = UIManager.getIcon("FileChooser.upFolderIcon");
  196. homeFolderIcon = UIManager.getIcon("FileChooser.homeFolderIcon");
  197. detailsViewIcon = UIManager.getIcon("FileChooser.detailsViewIcon");
  198. listViewIcon = UIManager.getIcon("FileChooser.listViewIcon");
  199. }
  200. protected void installStrings(JFileChooser fc) {
  201. Locale l = fc.getLocale();
  202. newFolderErrorText = UIManager.getString("FileChooser.newFolderErrorText",l);
  203. newFolderErrorSeparator = UIManager.getString("FileChooser.newFolderErrorSeparator",l);
  204. fileDescriptionText = UIManager.getString("FileChooser.fileDescriptionText",l);
  205. directoryDescriptionText = UIManager.getString("FileChooser.directoryDescriptionText",l);
  206. saveButtonText = UIManager.getString("FileChooser.saveButtonText",l);
  207. openButtonText = UIManager.getString("FileChooser.openButtonText",l);
  208. saveDialogTitleText = UIManager.getString("FileChooser.saveDialogTitleText",l);
  209. openDialogTitleText = UIManager.getString("FileChooser.openDialogTitleText",l);
  210. cancelButtonText = UIManager.getString("FileChooser.cancelButtonText",l);
  211. updateButtonText = UIManager.getString("FileChooser.updateButtonText",l);
  212. helpButtonText = UIManager.getString("FileChooser.helpButtonText",l);
  213. directoryOpenButtonText = UIManager.getString("FileChooser.directoryOpenButtonText",l);
  214. saveButtonMnemonic = getMnemonic("FileChooser.saveButtonMnemonic", l);
  215. openButtonMnemonic = getMnemonic("FileChooser.openButtonMnemonic", l);
  216. cancelButtonMnemonic = getMnemonic("FileChooser.cancelButtonMnemonic", l);
  217. updateButtonMnemonic = getMnemonic("FileChooser.updateButtonMnemonic", l);
  218. helpButtonMnemonic = getMnemonic("FileChooser.helpButtonMnemonic", l);
  219. directoryOpenButtonMnemonic = getMnemonic("FileChooser.directoryOpenButtonMnemonic", l);
  220. saveButtonToolTipText = UIManager.getString("FileChooser.saveButtonToolTipText",l);
  221. openButtonToolTipText = UIManager.getString("FileChooser.openButtonToolTipText",l);
  222. cancelButtonToolTipText = UIManager.getString("FileChooser.cancelButtonToolTipText",l);
  223. updateButtonToolTipText = UIManager.getString("FileChooser.updateButtonToolTipText",l);
  224. helpButtonToolTipText = UIManager.getString("FileChooser.helpButtonToolTipText",l);
  225. directoryOpenButtonToolTipText = UIManager.getString("FileChooser.directoryOpenButtonToolTipText",l);
  226. }
  227. protected void uninstallDefaults(JFileChooser fc) {
  228. uninstallIcons(fc);
  229. uninstallStrings(fc);
  230. if (fc.getTransferHandler() instanceof UIResource) {
  231. fc.setTransferHandler(null);
  232. }
  233. }
  234. protected void uninstallIcons(JFileChooser fc) {
  235. directoryIcon = null;
  236. fileIcon = null;
  237. computerIcon = null;
  238. hardDriveIcon = null;
  239. floppyDriveIcon = null;
  240. newFolderIcon = null;
  241. upFolderIcon = null;
  242. homeFolderIcon = null;
  243. detailsViewIcon = null;
  244. listViewIcon = null;
  245. }
  246. protected void uninstallStrings(JFileChooser fc) {
  247. saveButtonText = null;
  248. openButtonText = null;
  249. cancelButtonText = null;
  250. updateButtonText = null;
  251. helpButtonText = null;
  252. directoryOpenButtonText = null;
  253. saveButtonToolTipText = null;
  254. openButtonToolTipText = null;
  255. cancelButtonToolTipText = null;
  256. updateButtonToolTipText = null;
  257. helpButtonToolTipText = null;
  258. directoryOpenButtonToolTipText = null;
  259. }
  260. protected void createModel() {
  261. model = new BasicDirectoryModel(getFileChooser());
  262. }
  263. public BasicDirectoryModel getModel() {
  264. return model;
  265. }
  266. public PropertyChangeListener createPropertyChangeListener(JFileChooser fc) {
  267. return null;
  268. }
  269. public String getFileName() {
  270. return null;
  271. }
  272. public String getDirectoryName() {
  273. return null;
  274. }
  275. public void setFileName(String filename) {
  276. }
  277. public void setDirectoryName(String dirname) {
  278. }
  279. public void rescanCurrentDirectory(JFileChooser fc) {
  280. }
  281. public void ensureFileIsVisible(JFileChooser fc, File f) {
  282. }
  283. public JFileChooser getFileChooser() {
  284. return filechooser;
  285. }
  286. public JPanel getAccessoryPanel() {
  287. return accessoryPanel;
  288. }
  289. protected JButton getApproveButton(JFileChooser fc) {
  290. return null;
  291. }
  292. public String getApproveButtonToolTipText(JFileChooser fc) {
  293. String tooltipText = fc.getApproveButtonToolTipText();
  294. if(tooltipText != null) {
  295. return tooltipText;
  296. }
  297. if(fc.getDialogType() == JFileChooser.OPEN_DIALOG) {
  298. return openButtonToolTipText;
  299. } else if(fc.getDialogType() == JFileChooser.SAVE_DIALOG) {
  300. return saveButtonToolTipText;
  301. }
  302. return null;
  303. }
  304. public void clearIconCache() {
  305. fileView.clearIconCache();
  306. }
  307. // ********************************************
  308. // ************ Create Listeners **************
  309. // ********************************************
  310. private Handler getHandler() {
  311. if (handler == null) {
  312. handler = new Handler();
  313. }
  314. return handler;
  315. }
  316. protected MouseListener createDoubleClickListener(JFileChooser fc,
  317. JList list) {
  318. return new Handler(list);
  319. }
  320. public ListSelectionListener createListSelectionListener(JFileChooser fc) {
  321. return getHandler();
  322. }
  323. private class Handler implements MouseListener, ListSelectionListener {
  324. JList list;
  325. Handler() {
  326. }
  327. Handler(JList list) {
  328. this.list = list;
  329. }
  330. public void mouseClicked(MouseEvent evt) {
  331. // Note: we can't depend on evt.getSource() because of backward
  332. // compatability
  333. if (list != null &&
  334. SwingUtilities.isLeftMouseButton(evt) &&
  335. evt.getClickCount() == 2) {
  336. int index = SwingUtilities2.loc2IndexFileList(list, evt.getPoint());
  337. if (index >= 0) {
  338. File f = (File)list.getModel().getElementAt(index);
  339. try {
  340. // Strip trailing ".."
  341. f = f.getCanonicalFile();
  342. } catch (IOException ex) {
  343. // That's ok, we'll use f as is
  344. }
  345. if(getFileChooser().isTraversable(f)) {
  346. list.clearSelection();
  347. changeDirectory(f);
  348. } else {
  349. getFileChooser().approveSelection();
  350. }
  351. }
  352. }
  353. }
  354. public void mouseEntered(MouseEvent evt) {
  355. if (list != null) {
  356. TransferHandler th1 = getFileChooser().getTransferHandler();
  357. TransferHandler th2 = list.getTransferHandler();
  358. if (th1 != th2) {
  359. list.setTransferHandler(th1);
  360. }
  361. if (getFileChooser().getDragEnabled() != list.getDragEnabled()) {
  362. list.setDragEnabled(getFileChooser().getDragEnabled());
  363. }
  364. }
  365. }
  366. public void mouseExited(MouseEvent evt) {
  367. }
  368. public void mousePressed(MouseEvent evt) {
  369. }
  370. public void mouseReleased(MouseEvent evt) {
  371. }
  372. public void valueChanged(ListSelectionEvent evt) {
  373. if(!evt.getValueIsAdjusting()) {
  374. JFileChooser chooser = getFileChooser();
  375. FileSystemView fsv = chooser.getFileSystemView();
  376. JList list = (JList)evt.getSource();
  377. int fsm = chooser.getFileSelectionMode();
  378. boolean useSetDirectory =
  379. usesSingleFilePane ? (fsm == JFileChooser.FILES_ONLY)
  380. : (fsm != JFileChooser.DIRECTORIES_ONLY);
  381. if (chooser.isMultiSelectionEnabled()) {
  382. File[] files = null;
  383. Object[] objects = list.getSelectedValues();
  384. if (objects != null) {
  385. if (objects.length == 1
  386. && ((File)objects[0]).isDirectory()
  387. && chooser.isTraversable(((File)objects[0]))
  388. && (useSetDirectory || !fsv.isFileSystem(((File)objects[0])))) {
  389. setDirectorySelected(true);
  390. setDirectory(((File)objects[0]));
  391. } else {
  392. ArrayList fList = new ArrayList(objects.length);
  393. for (int i = 0; i < objects.length; i++) {
  394. File f = (File)objects[i];
  395. boolean isDir = f.isDirectory();
  396. if ((chooser.isFileSelectionEnabled() && !isDir)
  397. || (chooser.isDirectorySelectionEnabled()
  398. && fsv.isFileSystem(f)
  399. && isDir)) {
  400. fList.add(f);
  401. }
  402. }
  403. if (fList.size() > 0) {
  404. files = (File[])fList.toArray(new File[fList.size()]);
  405. }
  406. setDirectorySelected(false);
  407. }
  408. }
  409. chooser.setSelectedFiles(files);
  410. } else {
  411. File file = (File)list.getSelectedValue();
  412. if (file != null
  413. && file.isDirectory()
  414. && chooser.isTraversable(file)
  415. && (useSetDirectory || !fsv.isFileSystem(file))) {
  416. setDirectorySelected(true);
  417. setDirectory(file);
  418. if (usesSingleFilePane) {
  419. chooser.setSelectedFile(null);
  420. }
  421. } else {
  422. setDirectorySelected(false);
  423. if (file != null) {
  424. chooser.setSelectedFile(file);
  425. }
  426. }
  427. }
  428. }
  429. }
  430. }
  431. protected class DoubleClickListener extends MouseAdapter {
  432. // NOTE: This class exists only for backward compatability. All
  433. // its functionality has been moved into Handler. If you need to add
  434. // new functionality add it to the Handler, but make sure this
  435. // class calls into the Handler.
  436. Handler handler;
  437. public DoubleClickListener(JList list) {
  438. handler = new Handler(list);
  439. }
  440. /**
  441. * The JList used for representing the files is created by subclasses, but the
  442. * selection is monitored in this class. The TransferHandler installed in the
  443. * JFileChooser is also installed in the file list as it is used as the actual
  444. * transfer source. The list is updated on a mouse enter to reflect the current
  445. * data transfer state of the file chooser.
  446. */
  447. public void mouseEntered(MouseEvent e) {
  448. handler.mouseEntered(e);
  449. }
  450. public void mouseClicked(MouseEvent e) {
  451. handler.mouseClicked(e);
  452. }
  453. }
  454. protected class SelectionListener implements ListSelectionListener {
  455. // NOTE: This class exists only for backward compatability. All
  456. // its functionality has been moved into Handler. If you need to add
  457. // new functionality add it to the Handler, but make sure this
  458. // class calls into the Handler.
  459. public void valueChanged(ListSelectionEvent e) {
  460. getHandler().valueChanged(e);
  461. }
  462. }
  463. /**
  464. * Property to remember whether a directory is currently selected in the UI.
  465. *
  466. * @return <code>true</code> iff a directory is currently selected.
  467. * @since 1.4
  468. */
  469. protected boolean isDirectorySelected() {
  470. return directorySelected;
  471. }
  472. /**
  473. * Property to remember whether a directory is currently selected in the UI.
  474. * This is normally called by the UI on a selection event.
  475. *
  476. * @param b iff a directory is currently selected.
  477. * @since 1.4
  478. */
  479. protected void setDirectorySelected(boolean b) {
  480. directorySelected = b;
  481. }
  482. /**
  483. * Property to remember the directory that is currently selected in the UI.
  484. *
  485. * @return the value of the <code>directory</code> property
  486. * @see #setDirectory
  487. * @since 1.4
  488. */
  489. protected File getDirectory() {
  490. return directory;
  491. }
  492. /**
  493. * Property to remember the directory that is currently selected in the UI.
  494. * This is normally called by the UI on a selection event.
  495. *
  496. * @param f the <code>File</code> object representing the directory that is
  497. * currently selected
  498. * @since 1.4
  499. */
  500. protected void setDirectory(File f) {
  501. directory = f;
  502. }
  503. /**
  504. * Returns the mnemonic for the given key.
  505. */
  506. private int getMnemonic(String key, Locale l) {
  507. Object value = UIManager.get(key, l);
  508. if (value instanceof Integer) {
  509. return (Integer)value;
  510. }
  511. if (value instanceof String) {
  512. try {
  513. return Integer.parseInt((String)value);
  514. } catch (NumberFormatException nfe) { }
  515. }
  516. return 0;
  517. }
  518. // *******************************************************
  519. // ************ FileChooser UI PLAF methods **************
  520. // *******************************************************
  521. /**
  522. * Returns the default accept all file filter
  523. */
  524. public FileFilter getAcceptAllFileFilter(JFileChooser fc) {
  525. return acceptAllFileFilter;
  526. }
  527. public FileView getFileView(JFileChooser fc) {
  528. return fileView;
  529. }
  530. /**
  531. * Returns the title of this dialog
  532. */
  533. public String getDialogTitle(JFileChooser fc) {
  534. String dialogTitle = fc.getDialogTitle();
  535. if (dialogTitle != null) {
  536. return dialogTitle;
  537. } else if (fc.getDialogType() == JFileChooser.OPEN_DIALOG) {
  538. return openDialogTitleText;
  539. } else if (fc.getDialogType() == JFileChooser.SAVE_DIALOG) {
  540. return saveDialogTitleText;
  541. } else {
  542. return getApproveButtonText(fc);
  543. }
  544. }
  545. public int getApproveButtonMnemonic(JFileChooser fc) {
  546. int mnemonic = fc.getApproveButtonMnemonic();
  547. if (mnemonic > 0) {
  548. return mnemonic;
  549. } else if (fc.getDialogType() == JFileChooser.OPEN_DIALOG) {
  550. return openButtonMnemonic;
  551. } else if (fc.getDialogType() == JFileChooser.SAVE_DIALOG) {
  552. return saveButtonMnemonic;
  553. } else {
  554. return mnemonic;
  555. }
  556. }
  557. public String getApproveButtonText(JFileChooser fc) {
  558. String buttonText = fc.getApproveButtonText();
  559. if (buttonText != null) {
  560. return buttonText;
  561. } else if (fc.getDialogType() == JFileChooser.OPEN_DIALOG) {
  562. return openButtonText;
  563. } else if (fc.getDialogType() == JFileChooser.SAVE_DIALOG) {
  564. return saveButtonText;
  565. } else {
  566. return null;
  567. }
  568. }
  569. // *****************************
  570. // ***** Directory Actions *****
  571. // *****************************
  572. public Action getNewFolderAction() {
  573. if (newFolderAction == null) {
  574. newFolderAction = new NewFolderAction();
  575. // Note: Don't return null for readOnly, it might
  576. // break older apps.
  577. if (readOnly) {
  578. newFolderAction.setEnabled(false);
  579. }
  580. }
  581. return newFolderAction;
  582. }
  583. public Action getGoHomeAction() {
  584. return goHomeAction;
  585. }
  586. public Action getChangeToParentDirectoryAction() {
  587. return changeToParentDirectoryAction;
  588. }
  589. public Action getApproveSelectionAction() {
  590. return approveSelectionAction;
  591. }
  592. public Action getCancelSelectionAction() {
  593. return cancelSelectionAction;
  594. }
  595. public Action getUpdateAction() {
  596. return updateAction;
  597. }
  598. /**
  599. * Creates a new folder.
  600. */
  601. protected class NewFolderAction extends AbstractAction {
  602. protected NewFolderAction() {
  603. super(FilePane.ACTION_NEW_FOLDER);
  604. }
  605. public void actionPerformed(ActionEvent e) {
  606. if (readOnly) {
  607. return;
  608. }
  609. JFileChooser fc = getFileChooser();
  610. File currentDirectory = fc.getCurrentDirectory();
  611. File newFolder = null;
  612. try {
  613. newFolder = fc.getFileSystemView().createNewFolder(currentDirectory);
  614. if (fc.isMultiSelectionEnabled()) {
  615. fc.setSelectedFiles(new File[] { newFolder });
  616. } else {
  617. fc.setSelectedFile(newFolder);
  618. }
  619. } catch (IOException exc) {
  620. JOptionPane.showMessageDialog(
  621. fc,
  622. newFolderErrorText + newFolderErrorSeparator + exc,
  623. newFolderErrorText, JOptionPane.ERROR_MESSAGE);
  624. return;
  625. }
  626. fc.rescanCurrentDirectory();
  627. }
  628. }
  629. /**
  630. * Acts on the "home" key event or equivalent event.
  631. */
  632. protected class GoHomeAction extends AbstractAction {
  633. protected GoHomeAction() {
  634. super("Go Home");
  635. }
  636. public void actionPerformed(ActionEvent e) {
  637. JFileChooser fc = getFileChooser();
  638. changeDirectory(fc.getFileSystemView().getHomeDirectory());
  639. }
  640. }
  641. protected class ChangeToParentDirectoryAction extends AbstractAction {
  642. protected ChangeToParentDirectoryAction() {
  643. super("Go Up");
  644. putValue(Action.ACTION_COMMAND_KEY, FilePane.ACTION_CHANGE_TO_PARENT_DIRECTORY);
  645. }
  646. public void actionPerformed(ActionEvent e) {
  647. Component focusOwner = KeyboardFocusManager.getCurrentKeyboardFocusManager().getFocusOwner();
  648. if (focusOwner == null || !(focusOwner instanceof javax.swing.text.JTextComponent)) {
  649. getFileChooser().changeToParentDirectory();
  650. }
  651. }
  652. }
  653. /**
  654. * Responds to an Open or Save request
  655. */
  656. protected class ApproveSelectionAction extends AbstractAction {
  657. protected ApproveSelectionAction() {
  658. super(FilePane.ACTION_APPROVE_SELECTION);
  659. }
  660. public void actionPerformed(ActionEvent e) {
  661. if (isDirectorySelected()) {
  662. File dir = getDirectory();
  663. if (dir != null) {
  664. try {
  665. // Strip trailing ".."
  666. dir = dir.getCanonicalFile();
  667. } catch (IOException ex) {
  668. // Ok, use f as is
  669. }
  670. changeDirectory(dir);
  671. return;
  672. }
  673. }
  674. JFileChooser chooser = getFileChooser();
  675. String filename = getFileName();
  676. FileSystemView fs = chooser.getFileSystemView();
  677. File dir = chooser.getCurrentDirectory();
  678. if (filename != null) {
  679. // Remove whitespace from beginning and end of filename
  680. filename = filename.trim();
  681. }
  682. if (filename == null || filename.equals("")) {
  683. // no file selected, multiple selection off, therefore cancel the approve action
  684. resetGlobFilter();
  685. return;
  686. }
  687. File selectedFile = null;
  688. File[] selectedFiles = null;
  689. if (filename != null && !filename.equals("")) {
  690. // Unix: Resolve '~' to user's home directory
  691. if (File.separatorChar == '/') {
  692. if (filename.startsWith("~/")) {
  693. filename = System.getProperty("user.home") + filename.substring(1);
  694. } else if (filename.equals("~")) {
  695. filename = System.getProperty("user.home");
  696. }
  697. }
  698. if (chooser.isMultiSelectionEnabled() && filename.startsWith("\"")) {
  699. ArrayList fList = new ArrayList();
  700. filename = filename.substring(1);
  701. if (filename.endsWith("\"")) {
  702. filename = filename.substring(0, filename.length()-1);
  703. }
  704. File[] children = null;
  705. int childIndex = 0;
  706. do {
  707. String str;
  708. int i = filename.indexOf("\" \"");
  709. if (i > 0) {
  710. str = filename.substring(0, i);
  711. filename = filename.substring(i+3);
  712. } else {
  713. str = filename;
  714. filename = "";
  715. }
  716. File file = fs.createFileObject(str);
  717. if (!file.isAbsolute()) {
  718. if (children == null) {
  719. children = fs.getFiles(dir, false);
  720. Arrays.sort(children);
  721. }
  722. for (int k = 0; k < children.length; k++) {
  723. int l = (childIndex + k) % children.length;
  724. if (children[l].getName().equals(str)) {
  725. file = children[l];
  726. childIndex = l + 1;
  727. break;
  728. }
  729. }
  730. }
  731. fList.add(file);
  732. } while (filename.length() > 0);
  733. if (fList.size() > 0) {
  734. selectedFiles = (File[])fList.toArray(new File[fList.size()]);
  735. }
  736. resetGlobFilter();
  737. } else {
  738. selectedFile = fs.createFileObject(filename);
  739. if(!selectedFile.isAbsolute()) {
  740. selectedFile = fs.getChild(dir, filename);
  741. }
  742. // check for wildcard pattern
  743. FileFilter currentFilter = chooser.getFileFilter();
  744. if (!selectedFile.exists() && isGlobPattern(filename)) {
  745. if (globFilter == null) {
  746. globFilter = new GlobFilter();
  747. }
  748. try {
  749. globFilter.setPattern(filename);
  750. if (!(currentFilter instanceof GlobFilter)) {
  751. actualFileFilter = currentFilter;
  752. }
  753. chooser.setFileFilter(null);
  754. chooser.setFileFilter(globFilter);
  755. return;
  756. } catch (PatternSyntaxException pse) {
  757. // Not a valid glob pattern. Abandon filter.
  758. }
  759. }
  760. resetGlobFilter();
  761. // Check for directory change action
  762. boolean isDir = (selectedFile != null && selectedFile.isDirectory());
  763. boolean isTrav = (selectedFile != null && chooser.isTraversable(selectedFile));
  764. boolean isDirSelEnabled = chooser.isDirectorySelectionEnabled();
  765. boolean isFileSelEnabled = chooser.isFileSelectionEnabled();
  766. if (isDir && isTrav && !isDirSelEnabled) {
  767. changeDirectory(selectedFile);
  768. return;
  769. } else if ((isDir || !isFileSelEnabled)
  770. && (!isDir || !isDirSelEnabled)
  771. && (!isDirSelEnabled || selectedFile.exists())) {
  772. selectedFile = null;
  773. }
  774. }
  775. }
  776. if (selectedFiles != null || selectedFile != null) {
  777. if (selectedFiles != null || chooser.isMultiSelectionEnabled()) {
  778. if (selectedFiles == null) {
  779. selectedFiles = new File[] { selectedFile };
  780. }
  781. chooser.setSelectedFiles(selectedFiles);
  782. // Do it again. This is a fix for bug 4949273 to force the
  783. // selected value in case the ListSelectionModel clears it
  784. // for non-existing file names.
  785. chooser.setSelectedFiles(selectedFiles);
  786. } else {
  787. chooser.setSelectedFile(selectedFile);
  788. }
  789. chooser.approveSelection();
  790. } else {
  791. if (chooser.isMultiSelectionEnabled()) {
  792. chooser.setSelectedFiles(null);
  793. } else {
  794. chooser.setSelectedFile(null);
  795. }
  796. chooser.cancelSelection();
  797. }
  798. }
  799. }
  800. private void resetGlobFilter() {
  801. if (actualFileFilter != null) {
  802. JFileChooser chooser = getFileChooser();
  803. FileFilter currentFilter = chooser.getFileFilter();
  804. if (currentFilter != null && currentFilter.equals(globFilter)) {
  805. chooser.setFileFilter(actualFileFilter);
  806. chooser.removeChoosableFileFilter(globFilter);
  807. }
  808. actualFileFilter = null;
  809. }
  810. }
  811. private static boolean isGlobPattern(String filename) {
  812. return ((File.separatorChar == '\\' && (filename.indexOf('*') >= 0
  813. || filename.indexOf('?') >= 0))
  814. || (File.separatorChar == '/' && (filename.indexOf('*') >= 0
  815. || filename.indexOf('?') >= 0
  816. || filename.indexOf('[') >= 0)));
  817. }
  818. /* A file filter which accepts file patterns containing
  819. * the special wildcards *? on Windows and *?[] on Unix.
  820. */
  821. class GlobFilter extends FileFilter {
  822. Pattern pattern;
  823. String globPattern;
  824. public void setPattern(String globPattern) {
  825. char[] gPat = globPattern.toCharArray();
  826. char[] rPat = new char[gPat.length * 2];
  827. boolean isWin32 = (File.separatorChar == '\\');
  828. boolean inBrackets = false;
  829. StringBuffer buf = new StringBuffer();
  830. int j = 0;
  831. this.globPattern = globPattern;
  832. if (isWin32) {
  833. // On windows, a pattern ending with *.* is equal to ending with *
  834. int len = gPat.length;
  835. if (globPattern.endsWith("*.*")) {
  836. len -= 2;
  837. }
  838. for (int i = 0; i < len; i++) {
  839. switch(gPat[i]) {
  840. case '*':
  841. rPat[j++] = '.';
  842. rPat[j++] = '*';
  843. break;
  844. case '?':
  845. rPat[j++] = '.';
  846. break;
  847. case '\\':
  848. rPat[j++] = '\\';
  849. rPat[j++] = '\\';
  850. break;
  851. default:
  852. if ("+()^$.{}[]".indexOf(gPat[i]) >= 0) {
  853. rPat[j++] = '\\';
  854. }
  855. rPat[j++] = gPat[i];
  856. break;
  857. }
  858. }
  859. } else {
  860. for (int i = 0; i < gPat.length; i++) {
  861. switch(gPat[i]) {
  862. case '*':
  863. if (!inBrackets) {
  864. rPat[j++] = '.';
  865. }
  866. rPat[j++] = '*';
  867. break;
  868. case '?':
  869. rPat[j++] = inBrackets ? '?' : '.';
  870. break;
  871. case '[':
  872. inBrackets = true;
  873. rPat[j++] = gPat[i];
  874. if (i < gPat.length - 1) {
  875. switch (gPat[i+1]) {
  876. case '!':
  877. case '^':
  878. rPat[j++] = '^';
  879. i++;
  880. break;
  881. case ']':
  882. rPat[j++] = gPat[++i];
  883. break;
  884. }
  885. }
  886. break;
  887. case ']':
  888. rPat[j++] = gPat[i];
  889. inBrackets = false;
  890. break;
  891. case '\\':
  892. if (i == 0 && gPat.length > 1 && gPat[1] == '~') {
  893. rPat[j++] = gPat[++i];
  894. } else {
  895. rPat[j++] = '\\';
  896. if (i < gPat.length - 1 && "*?[]".indexOf(gPat[i+1]) >= 0) {
  897. rPat[j++] = gPat[++i];
  898. } else {
  899. rPat[j++] = '\\';
  900. }
  901. }
  902. break;
  903. default:
  904. //if ("+()|^$.{}<>".indexOf(gPat[i]) >= 0) {
  905. if (!Character.isLetterOrDigit(gPat[i])) {
  906. rPat[j++] = '\\';
  907. }
  908. rPat[j++] = gPat[i];
  909. break;
  910. }
  911. }
  912. }
  913. this.pattern = Pattern.compile(new String(rPat, 0, j), Pattern.CASE_INSENSITIVE);
  914. }
  915. public boolean accept(File f) {
  916. if (f == null) {
  917. return false;
  918. }
  919. if (f.isDirectory()) {
  920. return true;
  921. }
  922. return pattern.matcher(f.getName()).matches();
  923. }
  924. public String getDescription() {
  925. return globPattern;
  926. }
  927. }
  928. /**
  929. * Responds to a cancel request.
  930. */
  931. protected class CancelSelectionAction extends AbstractAction {
  932. public void actionPerformed(ActionEvent e) {
  933. getFileChooser().cancelSelection();
  934. }
  935. }
  936. /**
  937. * Rescans the files in the current directory
  938. */
  939. protected class UpdateAction extends AbstractAction {
  940. public void actionPerformed(ActionEvent e) {
  941. JFileChooser fc = getFileChooser();
  942. fc.setCurrentDirectory(fc.getFileSystemView().createFileObject(getDirectoryName()));
  943. fc.rescanCurrentDirectory();
  944. }
  945. }
  946. private void changeDirectory(File dir) {
  947. JFileChooser fc = getFileChooser();
  948. // Traverse shortcuts on Windows
  949. if (dir != null && File.separatorChar == '\\' && dir.getPath().endsWith(".lnk")) {
  950. try {
  951. File linkedTo = ShellFolder.getShellFolder(dir).getLinkLocation();
  952. if (linkedTo != null && fc.isTraversable(linkedTo)) {
  953. dir = linkedTo;
  954. } else {
  955. return;
  956. }
  957. } catch (FileNotFoundException ex) {
  958. return;
  959. }
  960. }
  961. fc.setCurrentDirectory(dir);
  962. }
  963. // *****************************************
  964. // ***** default AcceptAll file filter *****
  965. // *****************************************
  966. protected class AcceptAllFileFilter extends FileFilter {
  967. public AcceptAllFileFilter() {
  968. }
  969. public boolean accept(File f) {
  970. return true;
  971. }
  972. public String getDescription() {
  973. return UIManager.getString("FileChooser.acceptAllFileFilterText");
  974. }
  975. }
  976. // ***********************
  977. // * FileView operations *
  978. // ***********************
  979. protected class BasicFileView extends FileView {
  980. /* FileView type descriptions */
  981. // PENDING(jeff) - pass in the icon cache size
  982. protected Hashtable<File,Icon> iconCache = new Hashtable<File,Icon>();
  983. public BasicFileView() {
  984. }
  985. public void clearIconCache() {
  986. iconCache = new Hashtable<File,Icon>();
  987. }
  988. public String getName(File f) {
  989. // Note: Returns display name rather than file name
  990. String fileName = null;
  991. if(f != null) {
  992. fileName = getFileChooser().getFileSystemView().getSystemDisplayName(f);
  993. }
  994. return fileName;
  995. }
  996. public String getDescription(File f) {
  997. return f.getName();
  998. }
  999. public String getTypeDescription(File f) {
  1000. String type = getFileChooser().getFileSystemView().getSystemTypeDescription(f);
  1001. if (type == null) {
  1002. if (f.isDirectory()) {
  1003. type = directoryDescriptionText;
  1004. } else {
  1005. type = fileDescriptionText;
  1006. }
  1007. }
  1008. return type;
  1009. }
  1010. public Icon getCachedIcon(File f) {
  1011. return (Icon) iconCache.get(f);
  1012. }
  1013. public void cacheIcon(File f, Icon i) {
  1014. if(f == null || i == null) {
  1015. return;
  1016. }
  1017. iconCache.put(f, i);
  1018. }
  1019. public Icon getIcon(File f) {
  1020. Icon icon = getCachedIcon(f);
  1021. if(icon != null) {
  1022. return icon;
  1023. }
  1024. icon = fileIcon;
  1025. if (f != null) {
  1026. FileSystemView fsv = getFileChooser().getFileSystemView();
  1027. if (fsv.isFloppyDrive(f)) {
  1028. icon = floppyDriveIcon;
  1029. } else if (fsv.isDrive(f)) {
  1030. icon = hardDriveIcon;
  1031. } else if (fsv.isComputerNode(f)) {
  1032. icon = computerIcon;
  1033. } else if (f.isDirectory()) {
  1034. icon = directoryIcon;
  1035. }
  1036. }
  1037. cacheIcon(f, icon);
  1038. return icon;
  1039. }
  1040. public Boolean isHidden(File f) {
  1041. String name = f.getName();
  1042. if(name != null && name.charAt(0) == '.') {
  1043. return Boolean.TRUE;
  1044. } else {
  1045. return Boolean.FALSE;
  1046. }
  1047. }
  1048. }
  1049. private static final TransferHandler defaultTransferHandler = new FileTransferHandler();
  1050. /**
  1051. * Data transfer support for the file chooser. Since files are currently presented
  1052. * as a list, the list support is reused with the added flavor of DataFlavor.javaFileListFlavor
  1053. */
  1054. static class FileTransferHandler extends TransferHandler implements UIResource {
  1055. /**
  1056. * Create a Transferable to use as the source for a data transfer.
  1057. *
  1058. * @param c The component holding the data to be transfered. This
  1059. * argument is provided to enable sharing of TransferHandlers by
  1060. * multiple components.
  1061. * @return The representation of the data to be transfered.
  1062. *
  1063. */
  1064. protected Transferable createTransferable(JComponent c) {
  1065. Object[] values = null;
  1066. if (c instanceof JList) {
  1067. values = ((JList)c).getSelectedValues();
  1068. } else if (c instanceof JTable) {
  1069. JTable table = (JTable)c;
  1070. int[] rows = table.getSelectedRows();
  1071. if (rows != null) {
  1072. values = new Object[rows.length];
  1073. for (int i=0; i<rows.length; i++) {
  1074. values[i] = table.getValueAt(rows[i], 0);
  1075. }
  1076. }
  1077. }
  1078. if (values == null || values.length == 0) {
  1079. return null;
  1080. }
  1081. StringBuffer plainBuf = new StringBuffer();
  1082. StringBuffer htmlBuf = new StringBuffer();
  1083. htmlBuf.append("<html>\n<body>\n<ul>\n");
  1084. for (int i = 0; i < values.length; i++) {
  1085. Object obj = values[i];
  1086. String val = ((obj == null) ? "" : obj.toString());
  1087. plainBuf.append(val + "\n");
  1088. htmlBuf.append(" <li>" + val + "\n");
  1089. }
  1090. // remove the last newline
  1091. plainBuf.deleteCharAt(plainBuf.length() - 1);
  1092. htmlBuf.append("</ul>\n</body>\n</html>");
  1093. return new FileTransferable(plainBuf.toString(), htmlBuf.toString(), values);
  1094. }
  1095. public int getSourceActions(JComponent c) {
  1096. return COPY;
  1097. }
  1098. static class FileTransferable extends BasicTransferable {
  1099. Object[] fileData;
  1100. FileTransferable(String plainData, String htmlData, Object[] fileData) {
  1101. super(plainData, htmlData);
  1102. this.fileData = fileData;
  1103. }
  1104. /**
  1105. * Best format of the file chooser is DataFlavor.javaFileListFlavor.
  1106. */
  1107. protected DataFlavor[] getRicherFlavors() {
  1108. DataFlavor[] flavors = new DataFlavor[1];
  1109. flavors[0] = DataFlavor.javaFileListFlavor;
  1110. return flavors;
  1111. }
  1112. /**
  1113. * The only richer format supported is the file list flavor
  1114. */
  1115. protected Object getRicherData(DataFlavor flavor) {
  1116. if (DataFlavor.javaFileListFlavor.equals(flavor)) {
  1117. ArrayList files = new ArrayList();
  1118. for (int i = 0; i < fileData.length; i++) {
  1119. files.add(fileData[i]);
  1120. }
  1121. return files;
  1122. }
  1123. return null;
  1124. }
  1125. }
  1126. }
  1127. }