1. /*
  2. * @(#)GTKFileChooserUI.java 1.26 04/06/01
  3. *
  4. * Copyright 2004 Sun Microsystems, Inc. All rights reserved.
  5. * SUN PROPRIETARY/CONFIDENTIAL. Use is subject to license terms.
  6. */
  7. package com.sun.java.swing.plaf.gtk;
  8. import java.awt.*;
  9. import java.awt.event.*;
  10. import java.beans.*;
  11. import java.io.File;
  12. import java.io.FileNotFoundException;
  13. import java.io.IOException;
  14. import java.text.MessageFormat;
  15. import java.util.*;
  16. import javax.swing.plaf.synth.*;
  17. import javax.swing.*;
  18. import javax.swing.border.*;
  19. import javax.swing.filechooser.*;
  20. import javax.swing.event.*;
  21. import javax.swing.plaf.*;
  22. import javax.swing.plaf.basic.BasicDirectoryModel;
  23. import javax.swing.table.*;
  24. import sun.swing.plaf.synth.*;
  25. import sun.swing.FilePane;
  26. /**
  27. * GTK FileChooserUI.
  28. *
  29. * @version 1.36 08/21/02
  30. * @author Leif Samuelsson
  31. * @author Jeff Dinkins
  32. */
  33. class GTKFileChooserUI extends SynthFileChooserUI {
  34. // The accessoryPanel is a container to place the JFileChooser accessory component
  35. private JPanel accessoryPanel = null;
  36. private String newFolderButtonText = null;
  37. private String newFolderErrorSeparator = null;
  38. private String newFolderErrorText = null;
  39. private String newFolderDialogText = null;
  40. private String deleteFileButtonText = null;
  41. private String renameFileButtonText = null;
  42. private String newFolderButtonToolTipText = null;
  43. private String deleteFileButtonToolTipText = null;
  44. private String renameFileButtonToolTipText = null;
  45. private int newFolderButtonMnemonic = 0;
  46. private int deleteFileButtonMnemonic = 0;
  47. private int renameFileButtonMnemonic = 0;
  48. private String renameFileDialogText = null;
  49. private String renameFileErrorTitle = null;
  50. private String renameFileErrorText = null;
  51. private JComboBox filterComboBox;
  52. private FilterComboBoxModel filterComboBoxModel;
  53. // From Motif
  54. private JPanel rightPanel;
  55. private JList directoryList;
  56. private JList fileList;
  57. private JLabel pathField;
  58. private JTextField fileNameTextField;
  59. private static final Dimension hstrut3 = new Dimension(3, 1);
  60. private static final Dimension vstrut10 = new Dimension(1, 10);
  61. private static final Insets insets = new Insets(10, 10, 10, 10);
  62. private static Dimension prefListSize = new Dimension(75, 150);
  63. private static Dimension PREF_SIZE = new Dimension(435, 360);
  64. private static Dimension MIN_SIZE = new Dimension(200, 300);
  65. private static Dimension PREF_ACC_SIZE = new Dimension(10, 10);
  66. private static Dimension ZERO_ACC_SIZE = new Dimension(1, 1);
  67. private static Dimension MAX_SIZE = new Dimension(Short.MAX_VALUE, Short.MAX_VALUE);
  68. private static final Insets buttonMargin = new Insets(3, 3, 3, 3);
  69. private String filesLabelText = null;
  70. private String foldersLabelText = null;
  71. private String pathLabelText = null;
  72. private String filterLabelText = null;
  73. private int pathLabelMnemonic = 0;
  74. private int filterLabelMnemonic = 0;
  75. private JComboBox directoryComboBox;
  76. private DirectoryComboBoxModel directoryComboBoxModel;
  77. private Action directoryComboBoxAction = new DirectoryComboBoxAction();
  78. private JPanel bottomButtonPanel;
  79. private GTKDirectoryModel model = null;
  80. private Action newFolderAction;
  81. private JPanel interior;
  82. private boolean readOnly;
  83. private boolean showDirectoryIcons;
  84. private boolean showFileIcons;
  85. private GTKFileView fileView = new GTKFileView();
  86. private PropertyChangeListener gtkFCPropertyChangeListener;
  87. private Action approveSelectionAction = new GTKApproveSelectionAction();
  88. private GTKDirectoryListModel directoryListModel;
  89. public GTKFileChooserUI(JFileChooser filechooser) {
  90. super(filechooser);
  91. }
  92. public String getFileName() {
  93. JFileChooser fc = getFileChooser();
  94. String typedInName = fileNameTextField != null ?
  95. fileNameTextField.getText() : null;
  96. if (!fc.isMultiSelectionEnabled()) {
  97. return typedInName;
  98. }
  99. int mode = fc.getFileSelectionMode();
  100. JList list = mode == JFileChooser.DIRECTORIES_ONLY ?
  101. directoryList : fileList;
  102. Object[] files = list.getSelectedValues();
  103. int len = files.length;
  104. Vector result = new Vector(len + 1);
  105. // we return all selected file names
  106. for (int i = 0; i < len; i++) {
  107. File file = (File)files[i];
  108. result.add(file.getName());
  109. }
  110. // plus the file name typed into the text field, if not already there
  111. if (typedInName != null && !result.contains(typedInName)) {
  112. result.add(typedInName);
  113. }
  114. StringBuffer buf = new StringBuffer();
  115. len = result.size();
  116. // construct the resulting string
  117. for (int i=0; i<len; i++) {
  118. if (len > 1) {
  119. buf.append(" \"");
  120. }
  121. buf.append(result.get(i));
  122. if (len > 1) {
  123. buf.append("\"");
  124. }
  125. }
  126. return buf.toString();
  127. }
  128. public void setFileName(String fileName) {
  129. if (fileNameTextField != null) {
  130. fileNameTextField.setText(fileName);
  131. }
  132. }
  133. // public String getDirectoryName() {
  134. // return pathField.getText();
  135. // }
  136. public void setDirectoryName(String dirname) {
  137. pathField.setText(dirname);
  138. }
  139. public void ensureFileIsVisible(JFileChooser fc, File f) {
  140. // PENDING
  141. }
  142. public void rescanCurrentDirectory(JFileChooser fc) {
  143. getModel().validateFileCache();
  144. }
  145. public JPanel getAccessoryPanel() {
  146. return accessoryPanel;
  147. }
  148. // ***********************
  149. // * FileView operations *
  150. // ***********************
  151. public FileView getFileView(JFileChooser fc) {
  152. return fileView;
  153. }
  154. private class GTKFileView extends BasicFileView {
  155. public GTKFileView() {
  156. iconCache = null;
  157. }
  158. public void clearIconCache() {
  159. }
  160. public Icon getCachedIcon(File f) {
  161. return null;
  162. }
  163. public void cacheIcon(File f, Icon i) {
  164. }
  165. public Icon getIcon(File f) {
  166. return (f != null && f.isDirectory()) ? directoryIcon : fileIcon;
  167. }
  168. }
  169. private void updateDefaultButton() {
  170. JFileChooser filechooser = getFileChooser();
  171. JRootPane root = SwingUtilities.getRootPane(filechooser);
  172. if (root == null) {
  173. return;
  174. }
  175. if (filechooser.getControlButtonsAreShown()) {
  176. if (root.getDefaultButton() == null) {
  177. root.setDefaultButton(getApproveButton(filechooser));
  178. getCancelButton(filechooser).setDefaultCapable(false);
  179. }
  180. } else {
  181. if (root.getDefaultButton() == getApproveButton(filechooser)) {
  182. root.setDefaultButton(null);
  183. }
  184. }
  185. }
  186. protected void doSelectedFileChanged(PropertyChangeEvent e) {
  187. super.doSelectedFileChanged(e);
  188. File f = (File) e.getNewValue();
  189. if (f != null) {
  190. setFileName(getFileChooser().getName(f));
  191. }
  192. }
  193. protected void doDirectoryChanged(PropertyChangeEvent e) {
  194. directoryList.clearSelection();
  195. fileList.clearSelection();
  196. File currentDirectory = getFileChooser().getCurrentDirectory();
  197. if (currentDirectory != null) {
  198. try {
  199. setDirectoryName(((File)e.getNewValue()).getCanonicalPath());
  200. } catch (IOException ioe) {
  201. setDirectoryName(((File)e.getNewValue()).getAbsolutePath());
  202. }
  203. directoryComboBoxModel.addItem(currentDirectory);
  204. directoryListModel.directoryChanged();
  205. }
  206. super.doDirectoryChanged(e);
  207. }
  208. protected void doAccessoryChanged(PropertyChangeEvent e) {
  209. if (getAccessoryPanel() != null) {
  210. if (e.getOldValue() != null) {
  211. getAccessoryPanel().remove((JComponent)e.getOldValue());
  212. }
  213. JComponent accessory = (JComponent)e.getNewValue();
  214. if (accessory != null) {
  215. getAccessoryPanel().add(accessory, BorderLayout.CENTER);
  216. getAccessoryPanel().setPreferredSize(accessory.getPreferredSize());
  217. getAccessoryPanel().setMaximumSize(MAX_SIZE);
  218. } else {
  219. getAccessoryPanel().setPreferredSize(ZERO_ACC_SIZE);
  220. getAccessoryPanel().setMaximumSize(ZERO_ACC_SIZE);
  221. }
  222. }
  223. }
  224. protected void doFileSelectionModeChanged(PropertyChangeEvent e) {
  225. directoryList.clearSelection();
  226. rightPanel.setVisible(((Integer)e.getNewValue()).intValue() != JFileChooser.DIRECTORIES_ONLY);
  227. super.doFileSelectionModeChanged(e);
  228. }
  229. protected void doMultiSelectionChanged(PropertyChangeEvent e) {
  230. if (getFileChooser().isMultiSelectionEnabled()) {
  231. fileList.setSelectionMode(ListSelectionModel.MULTIPLE_INTERVAL_SELECTION);
  232. } else {
  233. fileList.setSelectionMode(ListSelectionModel.SINGLE_SELECTION);
  234. fileList.clearSelection();
  235. }
  236. super.doMultiSelectionChanged(e);
  237. }
  238. protected void doControlButtonsChanged(PropertyChangeEvent e) {
  239. super.doControlButtonsChanged(e);
  240. JFileChooser filechooser = getFileChooser();
  241. if (filechooser.getControlButtonsAreShown()) {
  242. filechooser.add(bottomButtonPanel, BorderLayout.SOUTH);
  243. } else {
  244. filechooser.remove(bottomButtonPanel);
  245. }
  246. updateDefaultButton();
  247. }
  248. protected void doAncestorChanged(PropertyChangeEvent e) {
  249. if (e.getOldValue() == null && e.getNewValue() != null) {
  250. // Ancestor was added, set initial focus
  251. fileNameTextField.selectAll();
  252. fileNameTextField.requestFocus();
  253. updateDefaultButton();
  254. }
  255. super.doAncestorChanged(e);
  256. }
  257. // ********************************************
  258. // ************ Create Listeners **************
  259. // ********************************************
  260. public ListSelectionListener createListSelectionListener(JFileChooser fc) {
  261. return new SelectionListener();
  262. }
  263. class DoubleClickListener extends MouseAdapter {
  264. JList list;
  265. public DoubleClickListener(JList list) {
  266. this.list = list;
  267. }
  268. public void mouseClicked(MouseEvent e) {
  269. if (SwingUtilities.isLeftMouseButton(e) && e.getClickCount() == 2) {
  270. int index = list.locationToIndex(e.getPoint());
  271. if (index >= 0) {
  272. File f = (File) list.getModel().getElementAt(index);
  273. try {
  274. // Strip trailing ".."
  275. f = f.getCanonicalFile();
  276. } catch (IOException ex) {
  277. // That's ok, we'll use f as is
  278. }
  279. if (getFileChooser().isTraversable(f)) {
  280. list.clearSelection();
  281. if (getFileChooser().getCurrentDirectory().equals(f)){
  282. rescanCurrentDirectory(getFileChooser());
  283. } else {
  284. getFileChooser().setCurrentDirectory(f);
  285. }
  286. } else {
  287. getFileChooser().approveSelection();
  288. }
  289. }
  290. }
  291. }
  292. }
  293. protected MouseListener createDoubleClickListener(JFileChooser fc, JList list) {
  294. return new DoubleClickListener(list);
  295. }
  296. protected class SelectionListener implements ListSelectionListener {
  297. public void valueChanged(ListSelectionEvent e) {
  298. if (!e.getValueIsAdjusting()) {
  299. JFileChooser chooser = getFileChooser();
  300. JList list = (JList) e.getSource();
  301. if (chooser.isMultiSelectionEnabled()) {
  302. File[] files = null;
  303. Object[] objects = list.getSelectedValues();
  304. if (objects != null) {
  305. if (objects.length == 1
  306. && ((File)objects[0]).isDirectory()
  307. && chooser.isTraversable(((File)objects[0]))
  308. && (chooser.getFileSelectionMode() != chooser.DIRECTORIES_ONLY
  309. || !chooser.getFileSystemView().isFileSystem(((File)objects[0])))) {
  310. setDirectorySelected(true);
  311. setDirectory(((File)objects[0]));
  312. } else {
  313. ArrayList fList = new ArrayList(objects.length);
  314. for (int i = 0; i < objects.length; i++) {
  315. File f = (File)objects[i];
  316. if ((chooser.isFileSelectionEnabled() && f.isFile())
  317. || (chooser.isDirectorySelectionEnabled() && f.isDirectory())) {
  318. fList.add(f);
  319. }
  320. }
  321. if (fList.size() > 0) {
  322. files = (File[])fList.toArray(new File[fList.size()]);
  323. }
  324. setDirectorySelected(false);
  325. }
  326. }
  327. chooser.setSelectedFiles(files);
  328. } else {
  329. File file = (File)list.getSelectedValue();
  330. if (file != null
  331. && file.isDirectory()
  332. && chooser.isTraversable(file)
  333. && (chooser.getFileSelectionMode() != chooser.DIRECTORIES_ONLY
  334. || !chooser.getFileSystemView().isFileSystem(file))) {
  335. setDirectorySelected(true);
  336. setDirectory(file);
  337. } else {
  338. setDirectorySelected(false);
  339. if (file != null) {
  340. chooser.setSelectedFile(file);
  341. }
  342. }
  343. }
  344. }
  345. }
  346. }
  347. //
  348. // ComponentUI Interface Implementation methods
  349. //
  350. public static ComponentUI createUI(JComponent c) {
  351. return new GTKFileChooserUI((JFileChooser)c);
  352. }
  353. public void installUI(JComponent c) {
  354. accessoryPanel = new JPanel(new BorderLayout(10, 10));
  355. accessoryPanel.setName("GTKFileChooser.accessoryPanel");
  356. super.installUI(c);
  357. }
  358. public void uninstallUI(JComponent c) {
  359. c.removePropertyChangeListener(filterComboBoxModel);
  360. super.uninstallUI(c);
  361. if (accessoryPanel != null) {
  362. accessoryPanel.removeAll();
  363. }
  364. accessoryPanel = null;
  365. getFileChooser().removeAll();
  366. }
  367. public void installComponents(JFileChooser fc) {
  368. super.installComponents(fc);
  369. boolean leftToRight = fc.getComponentOrientation().isLeftToRight();
  370. fc.setLayout(new BorderLayout());
  371. fc.setAlignmentX(JComponent.CENTER_ALIGNMENT);
  372. // Top row of buttons
  373. JPanel topButtonPanel = new JPanel(new FlowLayout(FlowLayout.LEADING, 0, 0));
  374. topButtonPanel.setBorder(new EmptyBorder(10, 10, 0, 10));
  375. topButtonPanel.setName("GTKFileChooser.topButtonPanel");
  376. if (!UIManager.getBoolean("FileChooser.readOnly")) {
  377. JButton newFolderButton = new JButton(getNewFolderAction());
  378. newFolderButton.setName("GTKFileChooser.newFolderButton");
  379. newFolderButton.setMnemonic(newFolderButtonMnemonic);
  380. newFolderButton.setToolTipText(newFolderButtonToolTipText);
  381. newFolderButton.setText(newFolderButtonText);
  382. topButtonPanel.add(newFolderButton);
  383. }
  384. JButton deleteFileButton = new JButton(deleteFileButtonText);
  385. deleteFileButton.setName("GTKFileChooser.deleteFileButton");
  386. deleteFileButton.setMnemonic(deleteFileButtonMnemonic);
  387. deleteFileButton.setToolTipText(deleteFileButtonToolTipText);
  388. deleteFileButton.setEnabled(false);
  389. topButtonPanel.add(deleteFileButton);
  390. RenameFileAction rfa = new RenameFileAction();
  391. JButton renameFileButton = new JButton(rfa);
  392. if (readOnly) {
  393. rfa.setEnabled(false);
  394. }
  395. renameFileButton.setText(renameFileButtonText);
  396. renameFileButton.setName("GTKFileChooser.renameFileButton");
  397. renameFileButton.setMnemonic(renameFileButtonMnemonic);
  398. renameFileButton.setToolTipText(renameFileButtonToolTipText);
  399. topButtonPanel.add(renameFileButton);
  400. fc.add(topButtonPanel, BorderLayout.NORTH);
  401. interior = new JPanel();
  402. interior.setBorder(new EmptyBorder(0, 10, 10, 10));
  403. interior.setName("GTKFileChooser.interiorPanel");
  404. align(interior);
  405. interior.setLayout(new BoxLayout(interior, BoxLayout.PAGE_AXIS));
  406. fc.add(interior, BorderLayout.CENTER);
  407. JPanel comboBoxPanel = new JPanel(new FlowLayout(FlowLayout.CENTER,
  408. 0, 0) {
  409. public void layoutContainer(Container target) {
  410. super.layoutContainer(target);
  411. JComboBox comboBox = directoryComboBox;
  412. if (comboBox.getWidth() > target.getWidth()) {
  413. comboBox.setBounds(0, comboBox.getY(), target.getWidth(),
  414. comboBox.getHeight());
  415. }
  416. }
  417. });
  418. comboBoxPanel.setBorder(new EmptyBorder(0, 0, 4, 0));
  419. comboBoxPanel.setName("GTKFileChooser.directoryComboBoxPanel");
  420. // CurrentDir ComboBox
  421. directoryComboBoxModel = createDirectoryComboBoxModel(fc);
  422. directoryComboBox = new JComboBox(directoryComboBoxModel);
  423. directoryComboBox.setName("GTKFileChooser.directoryComboBox");
  424. directoryComboBox.putClientProperty( "JComboBox.lightweightKeyboardNavigation", "Lightweight" );
  425. directoryComboBox.addActionListener(directoryComboBoxAction);
  426. directoryComboBox.setMaximumRowCount(8);
  427. comboBoxPanel.add(directoryComboBox);
  428. interior.add(comboBoxPanel);
  429. // CENTER: left, right, accessory
  430. JPanel centerPanel = new JPanel(new BorderLayout());
  431. centerPanel.setName("GTKFileChooser.centerPanel");
  432. // SPLIT PANEL: left, right
  433. JSplitPane splitPanel = new JSplitPane();
  434. splitPanel.setName("GTKFileChooser.splitPanel");
  435. splitPanel.setDividerLocation((PREF_SIZE.width-8)/2);
  436. // left panel - Filter & directoryList
  437. JPanel leftPanel = new JPanel(new GridBagLayout());
  438. leftPanel.setName("GTKFileChooser.directoryListPanel");
  439. // Add the Directory List
  440. // Create a label that looks like button (should be a table header)
  441. TableCellRenderer headerRenderer = new JTableHeader().getDefaultRenderer();
  442. JComponent directoryListLabel =
  443. (JComponent)headerRenderer.getTableCellRendererComponent(null, foldersLabelText,
  444. false, false, 0, 0);
  445. directoryListLabel.setName("GTKFileChooser.directoryListLabel");
  446. leftPanel.add(directoryListLabel, new GridBagConstraints(
  447. 0, 0, 1, 1, 1, 0, GridBagConstraints.WEST,
  448. GridBagConstraints.HORIZONTAL,
  449. new Insets(0, 0, 0, 0), 0, 0));
  450. leftPanel.add(createDirectoryList(), new GridBagConstraints(
  451. 0, 1, 1, 1, 1, 1, GridBagConstraints.EAST,
  452. GridBagConstraints.BOTH,
  453. new Insets(0, 0, 0, 0), 0, 0));
  454. // create files list
  455. rightPanel = new JPanel(new GridBagLayout());
  456. rightPanel.setName("GTKFileChooser.fileListPanel");
  457. headerRenderer = new JTableHeader().getDefaultRenderer();
  458. JComponent fileListLabel =
  459. (JComponent)headerRenderer.getTableCellRendererComponent(null, filesLabelText,
  460. false, false, 0, 0);
  461. fileListLabel.setName("GTKFileChooser.fileListLabel");
  462. rightPanel.add(fileListLabel, new GridBagConstraints(
  463. 0, 0, 1, 1, 1, 0, GridBagConstraints.WEST,
  464. GridBagConstraints.HORIZONTAL,
  465. new Insets(0, 0, 0, 0), 0, 0));
  466. rightPanel.add(createFilesList(), new GridBagConstraints(
  467. 0, 1, 1, 1, 1, 1, GridBagConstraints.EAST,
  468. GridBagConstraints.BOTH,
  469. new Insets(0, 0, 0, 0), 0, 0));
  470. splitPanel.add(leftPanel, leftToRight ? JSplitPane.LEFT : JSplitPane.RIGHT);
  471. splitPanel.add(rightPanel, leftToRight ? JSplitPane.RIGHT : JSplitPane.LEFT);
  472. centerPanel.add(splitPanel, BorderLayout.CENTER);
  473. JComponent accessoryPanel = getAccessoryPanel();
  474. JComponent accessory = fc.getAccessory();
  475. if (accessoryPanel != null) {
  476. if (accessory == null) {
  477. accessoryPanel.setPreferredSize(ZERO_ACC_SIZE);
  478. accessoryPanel.setMaximumSize(ZERO_ACC_SIZE);
  479. } else {
  480. getAccessoryPanel().add(accessory, BorderLayout.CENTER);
  481. accessoryPanel.setPreferredSize(accessory.getPreferredSize());
  482. accessoryPanel.setMaximumSize(MAX_SIZE);
  483. }
  484. align(accessoryPanel);
  485. centerPanel.add(accessoryPanel, BorderLayout.AFTER_LINE_ENDS);
  486. }
  487. interior.add(centerPanel);
  488. interior.add(Box.createRigidArea(vstrut10));
  489. JPanel pathFieldPanel = new JPanel(new FlowLayout(FlowLayout.LEADING,
  490. 0, 0));
  491. pathFieldPanel.setBorder(new EmptyBorder(0, 0, 4, 0));
  492. JLabel pathFieldLabel = new JLabel(pathLabelText);
  493. pathFieldLabel.setName("GTKFileChooser.pathFieldLabel");
  494. pathFieldLabel.setDisplayedMnemonic(pathLabelMnemonic);
  495. align(pathFieldLabel);
  496. pathFieldPanel.add(pathFieldLabel);
  497. pathFieldPanel.add(Box.createRigidArea(hstrut3));
  498. File currentDirectory = fc.getCurrentDirectory();
  499. String curDirName = null;
  500. if (currentDirectory != null) {
  501. curDirName = currentDirectory.getPath();
  502. }
  503. pathField = new JLabel(curDirName) {
  504. public Dimension getMaximumSize() {
  505. Dimension d = super.getMaximumSize();
  506. d.height = getPreferredSize().height;
  507. return d;
  508. }
  509. };
  510. pathField.setName("GTKFileChooser.pathField");
  511. pathFieldLabel.setLabelFor(pathField);
  512. align(pathField);
  513. pathFieldPanel.add(pathField);
  514. interior.add(pathFieldPanel);
  515. // add the fileName field
  516. fileNameTextField = new JTextField() {
  517. public Dimension getMaximumSize() {
  518. Dimension d = super.getMaximumSize();
  519. d.height = getPreferredSize().height;
  520. return d;
  521. }
  522. };
  523. Set forwardTraversalKeys = fileNameTextField.getFocusTraversalKeys(
  524. KeyboardFocusManager.FORWARD_TRAVERSAL_KEYS);
  525. forwardTraversalKeys = new HashSet(forwardTraversalKeys);
  526. forwardTraversalKeys.remove(KeyStroke.getKeyStroke(KeyEvent.VK_TAB, 0));
  527. fileNameTextField.setFocusTraversalKeys(KeyboardFocusManager.FORWARD_TRAVERSAL_KEYS, forwardTraversalKeys);
  528. fileNameTextField.setName("GTKFileChooser.fileNameTextField");
  529. fileNameTextField.getActionMap().put("fileNameCompletionAction", getFileNameCompletionAction());
  530. fileNameTextField.getInputMap().put(KeyStroke.getKeyStroke(KeyEvent.VK_TAB, 0), "fileNameCompletionAction");
  531. interior.add(fileNameTextField);
  532. // Add the filter combo box
  533. JPanel panel = new JPanel();
  534. panel.setLayout(new FlowLayout(FlowLayout.LEADING, 0, 0));
  535. panel.setBorder(new EmptyBorder(0, 0, 4, 0));
  536. JLabel filterLabel = new JLabel(filterLabelText);
  537. filterLabel.setName("GTKFileChooser.filterLabel");
  538. filterLabel.setDisplayedMnemonic(filterLabelMnemonic);
  539. panel.add(filterLabel);
  540. filterComboBoxModel = createFilterComboBoxModel();
  541. fc.addPropertyChangeListener(filterComboBoxModel);
  542. filterComboBox = new JComboBox(filterComboBoxModel);
  543. filterComboBox.setRenderer(createFilterComboBoxRenderer());
  544. filterLabel.setLabelFor(filterComboBox);
  545. interior.add(Box.createRigidArea(vstrut10));
  546. interior.add(panel);
  547. interior.add(filterComboBox);
  548. // Add buttons
  549. bottomButtonPanel = new JPanel(new FlowLayout(FlowLayout.TRAILING));
  550. bottomButtonPanel.setName("GTKFileChooser.bottomButtonPanel");
  551. align(bottomButtonPanel);
  552. JButton cancelButton = getCancelButton(fc);
  553. align(cancelButton);
  554. cancelButton.setMargin(buttonMargin);
  555. bottomButtonPanel.add(cancelButton);
  556. JButton approveButton = getApproveButton(fc);;
  557. align(approveButton);
  558. approveButton.setMargin(buttonMargin);
  559. bottomButtonPanel.add(approveButton);
  560. fc.add(bottomButtonPanel, BorderLayout.SOUTH);
  561. }
  562. protected void installListeners(JFileChooser fc) {
  563. super.installListeners(fc);
  564. gtkFCPropertyChangeListener = new GTKFCPropertyChangeListener();
  565. fc.addPropertyChangeListener(gtkFCPropertyChangeListener);
  566. }
  567. protected void uninstallListeners(JFileChooser fc) {
  568. super.uninstallListeners(fc);
  569. if (gtkFCPropertyChangeListener != null) {
  570. fc.removePropertyChangeListener(gtkFCPropertyChangeListener);
  571. }
  572. }
  573. private class GTKFCPropertyChangeListener implements PropertyChangeListener {
  574. public void propertyChange(PropertyChangeEvent e) {
  575. String prop = e.getPropertyName();
  576. if (prop.equals("GTKFileChooser.showDirectoryIcons")) {
  577. showDirectoryIcons = Boolean.TRUE.equals(e.getNewValue());
  578. } else if (prop.equals("GTKFileChooser.showFileIcons")) {
  579. showFileIcons = Boolean.TRUE.equals(e.getNewValue());
  580. }
  581. }
  582. }
  583. protected void installDefaults(JFileChooser fc) {
  584. super.installDefaults(fc);
  585. readOnly = UIManager.getBoolean("FileChooser.readOnly");
  586. showDirectoryIcons =
  587. Boolean.TRUE.equals(fc.getClientProperty("GTKFileChooser.showDirectoryIcons"));
  588. showFileIcons =
  589. Boolean.TRUE.equals(fc.getClientProperty("GTKFileChooser.showFileIcons"));
  590. }
  591. protected void installIcons(JFileChooser fc) {
  592. directoryIcon = UIManager.getIcon("FileView.directoryIcon");
  593. fileIcon = UIManager.getIcon("FileView.fileIcon");
  594. }
  595. protected void installStrings(JFileChooser fc) {
  596. super.installStrings(fc);
  597. Locale l = fc.getLocale();
  598. newFolderDialogText = UIManager.getString("FileChooser.newFolderDialogText", l);
  599. newFolderErrorText = UIManager.getString("FileChooser.newFolderErrorText",l);
  600. newFolderErrorSeparator = UIManager.getString("FileChooser.newFolderErrorSeparator",l);
  601. newFolderButtonText = UIManager.getString("FileChooser.newFolderButtonText", l);
  602. deleteFileButtonText = UIManager.getString("FileChooser.deleteFileButtonText", l);
  603. renameFileButtonText = UIManager.getString("FileChooser.renameFileButtonText", l);
  604. newFolderButtonMnemonic = UIManager.getInt("FileChooser.newFolderButtonMnemonic", l);
  605. deleteFileButtonMnemonic = UIManager.getInt("FileChooser.deleteFileButtonMnemonic", l);
  606. renameFileButtonMnemonic = UIManager.getInt("FileChooser.renameFileButtonMnemonic", l);
  607. newFolderButtonToolTipText = UIManager.getString("FileChooser.newFolderButtonToolTipText", l);
  608. deleteFileButtonToolTipText = UIManager.getString("FileChooser.deleteFileButtonToolTipText", l);
  609. renameFileButtonToolTipText = UIManager.getString("FileChooser.renameFileButtonToolTipText", l);
  610. renameFileDialogText = UIManager.getString("FileChooser.renameFileDialogText", l);
  611. renameFileErrorTitle = UIManager.getString("FileChooser.renameFileErrorTitle", l);
  612. renameFileErrorText = UIManager.getString("FileChooser.renameFileErrorText", l);
  613. foldersLabelText = UIManager.getString("FileChooser.foldersLabelText",l);
  614. filesLabelText = UIManager.getString("FileChooser.filesLabelText",l);
  615. pathLabelText = UIManager.getString("FileChooser.pathLabelText",l);
  616. pathLabelMnemonic = UIManager.getInt("FileChooser.pathLabelMnemonic");
  617. filterLabelText = UIManager.getString("FileChooser.filterLabelText", l);
  618. filterLabelMnemonic = UIManager.getInt("FileChooser.filterLabelMnemonic");
  619. }
  620. protected void uninstallStrings(JFileChooser fc) {
  621. super.uninstallStrings(fc);
  622. newFolderButtonText = null;
  623. deleteFileButtonText = null;
  624. renameFileButtonText = null;
  625. newFolderButtonToolTipText = null;
  626. deleteFileButtonToolTipText = null;
  627. renameFileButtonToolTipText = null;
  628. renameFileDialogText = null;
  629. renameFileErrorTitle = null;
  630. renameFileErrorText = null;
  631. foldersLabelText = null;
  632. filesLabelText = null;
  633. pathLabelText = null;
  634. newFolderDialogText = null;
  635. newFolderErrorText = null;
  636. newFolderErrorSeparator = null;
  637. }
  638. protected JScrollPane createFilesList() {
  639. fileList = new JList();
  640. fileList.setName("GTKFileChooser.fileList");
  641. if (getFileChooser().isMultiSelectionEnabled()) {
  642. fileList.setSelectionMode(ListSelectionModel.MULTIPLE_INTERVAL_SELECTION);
  643. } else {
  644. fileList.setSelectionMode(ListSelectionModel.SINGLE_SELECTION);
  645. }
  646. fileList.setModel(new GTKFileListModel());
  647. fileList.setCellRenderer(new FileCellRenderer());
  648. fileList.addListSelectionListener(createListSelectionListener(getFileChooser()));
  649. fileList.addMouseListener(createDoubleClickListener(getFileChooser(), fileList));
  650. align(fileList);
  651. JScrollPane scrollpane = new JScrollPane(fileList);
  652. scrollpane.setVerticalScrollBarPolicy(JScrollPane.VERTICAL_SCROLLBAR_ALWAYS);
  653. scrollpane.setName("GTKFileChooser.fileListScrollPane");
  654. scrollpane.setPreferredSize(prefListSize);
  655. scrollpane.setMaximumSize(MAX_SIZE);
  656. align(scrollpane);
  657. return scrollpane;
  658. }
  659. protected JScrollPane createDirectoryList() {
  660. directoryList = new JList();
  661. directoryList.setName("GTKFileChooser.directoryList");
  662. align(directoryList);
  663. directoryList.setCellRenderer(new DirectoryCellRenderer());
  664. directoryListModel = new GTKDirectoryListModel();
  665. directoryList.setModel(directoryListModel);
  666. directoryList.addMouseListener(createDoubleClickListener(getFileChooser(), directoryList));
  667. directoryList.addListSelectionListener(createListSelectionListener(getFileChooser()));
  668. JScrollPane scrollpane = new JScrollPane(directoryList);
  669. scrollpane.setVerticalScrollBarPolicy(JScrollPane.VERTICAL_SCROLLBAR_ALWAYS);
  670. scrollpane.setName("GTKFileChooser.directoryListScrollPane");
  671. scrollpane.setMaximumSize(MAX_SIZE);
  672. scrollpane.setPreferredSize(prefListSize);
  673. align(scrollpane);
  674. return scrollpane;
  675. }
  676. protected void createModel() {
  677. model = new GTKDirectoryModel();
  678. }
  679. public BasicDirectoryModel getModel() {
  680. return model;
  681. }
  682. public Action getApproveSelectionAction() {
  683. return approveSelectionAction;
  684. }
  685. private class GTKDirectoryModel extends BasicDirectoryModel {
  686. FileSystemView fsv;
  687. private Comparator fileComparator = new Comparator() {
  688. public int compare(Object o, Object o1) {
  689. return fsv.getSystemDisplayName((File) o).compareTo
  690. (fsv.getSystemDisplayName((File) o1));
  691. }
  692. };
  693. public GTKDirectoryModel() {
  694. super(getFileChooser());
  695. }
  696. protected void sort(Vector<? extends File> v) {
  697. fsv = getFileChooser().getFileSystemView();
  698. Collections.sort(v, fileComparator);
  699. }
  700. }
  701. protected class GTKDirectoryListModel extends AbstractListModel implements ListDataListener {
  702. File curDir;
  703. public GTKDirectoryListModel() {
  704. getModel().addListDataListener(this);
  705. directoryChanged();
  706. }
  707. public int getSize() {
  708. return getModel().getDirectories().size() + 1;
  709. }
  710. public Object getElementAt(int index) {
  711. return index > 0 ? getModel().getDirectories().elementAt(index - 1):
  712. curDir;
  713. }
  714. public void intervalAdded(ListDataEvent e) {
  715. fireIntervalAdded(this, e.getIndex0(), e.getIndex1());
  716. }
  717. public void intervalRemoved(ListDataEvent e) {
  718. fireIntervalRemoved(this, e.getIndex0(), e.getIndex1());
  719. }
  720. // PENDING - this is inefficient - should sent out
  721. // incremental adjustment values instead of saying that the
  722. // whole list has changed.
  723. public void fireContentsChanged() {
  724. fireContentsChanged(this, 0, getModel().getDirectories().size()-1);
  725. }
  726. // PENDING - fire the correct interval changed - currently sending
  727. // out that everything has changed
  728. public void contentsChanged(ListDataEvent e) {
  729. fireContentsChanged();
  730. }
  731. private void directoryChanged() {
  732. curDir = getFileChooser().getFileSystemView().createFileObject(
  733. getFileChooser().getCurrentDirectory(), ".");
  734. }
  735. }
  736. protected class GTKFileListModel extends AbstractListModel implements ListDataListener {
  737. public GTKFileListModel() {
  738. getModel().addListDataListener(this);
  739. }
  740. public int getSize() {
  741. return getModel().getFiles().size();
  742. }
  743. public boolean contains(Object o) {
  744. return getModel().getFiles().contains(o);
  745. }
  746. public int indexOf(Object o) {
  747. return getModel().getFiles().indexOf(o);
  748. }
  749. public Object getElementAt(int index) {
  750. return getModel().getFiles().elementAt(index);
  751. }
  752. public void intervalAdded(ListDataEvent e) {
  753. fireIntervalAdded(this, e.getIndex0(), e.getIndex1());
  754. }
  755. public void intervalRemoved(ListDataEvent e) {
  756. fireIntervalRemoved(this, e.getIndex0(), e.getIndex1());
  757. }
  758. // PENDING - this is inefficient - should sent out
  759. // incremental adjustment values instead of saying that the
  760. // whole list has changed.
  761. public void fireContentsChanged() {
  762. fireContentsChanged(this, 0, getModel().getFiles().size()-1);
  763. }
  764. // PENDING - fire the interval changed
  765. public void contentsChanged(ListDataEvent e) {
  766. fireContentsChanged();
  767. }
  768. }
  769. protected class FileCellRenderer extends DefaultListCellRenderer {
  770. public Component getListCellRendererComponent(JList list, Object value, int index,
  771. boolean isSelected, boolean cellHasFocus) {
  772. super.getListCellRendererComponent(list, value, index, isSelected, cellHasFocus);
  773. setText(getFileChooser().getName((File) value));
  774. if (showFileIcons) {
  775. setIcon(getFileChooser().getIcon((File)value));
  776. }
  777. return this;
  778. }
  779. }
  780. protected class DirectoryCellRenderer extends DefaultListCellRenderer {
  781. public Component getListCellRendererComponent(JList list, Object value, int index,
  782. boolean isSelected, boolean cellHasFocus) {
  783. super.getListCellRendererComponent(list, value, index, isSelected, cellHasFocus);
  784. if (showDirectoryIcons) {
  785. setIcon(getFileChooser().getIcon((File)value));
  786. setText(getFileChooser().getName((File)value));
  787. } else {
  788. setText(getFileChooser().getName((File)value) + "/");
  789. }
  790. return this;
  791. }
  792. }
  793. public Dimension getPreferredSize(JComponent c) {
  794. Dimension prefSize = new Dimension(PREF_SIZE);
  795. JComponent accessory = getFileChooser().getAccessory();
  796. if (accessory != null) {
  797. prefSize.width += accessory.getPreferredSize().width + 20;
  798. }
  799. Dimension d = c.getLayout().preferredLayoutSize(c);
  800. if (d != null) {
  801. return new Dimension(d.width < prefSize.width ? prefSize.width : d.width,
  802. d.height < prefSize.height ? prefSize.height : d.height);
  803. } else {
  804. return prefSize;
  805. }
  806. }
  807. public Dimension getMinimumSize(JComponent x) {
  808. return new Dimension(MIN_SIZE);
  809. }
  810. public Dimension getMaximumSize(JComponent x) {
  811. return new Dimension(Integer.MAX_VALUE, Integer.MAX_VALUE);
  812. }
  813. protected void align(JComponent c) {
  814. c.setAlignmentX(JComponent.LEFT_ALIGNMENT);
  815. c.setAlignmentY(JComponent.TOP_ALIGNMENT);
  816. }
  817. public Action getNewFolderAction() {
  818. if (newFolderAction == null) {
  819. newFolderAction = new NewFolderAction();
  820. newFolderAction.setEnabled(!readOnly);
  821. }
  822. return newFolderAction;
  823. }
  824. //
  825. // DataModel for DirectoryComboxbox
  826. //
  827. protected DirectoryComboBoxModel createDirectoryComboBoxModel(JFileChooser fc) {
  828. return new DirectoryComboBoxModel();
  829. }
  830. /**
  831. * Data model for a type-face selection combo-box.
  832. */
  833. protected class DirectoryComboBoxModel extends AbstractListModel implements ComboBoxModel {
  834. Vector directories = new Vector();
  835. File selectedDirectory = null;
  836. JFileChooser chooser = getFileChooser();
  837. FileSystemView fsv = chooser.getFileSystemView();
  838. public DirectoryComboBoxModel() {
  839. // Add the current directory to the model, and make it the
  840. // selectedDirectory
  841. File dir = getFileChooser().getCurrentDirectory();
  842. if (dir != null) {
  843. addItem(dir);
  844. }
  845. }
  846. /**
  847. * Adds the directory to the model and sets it to be selected,
  848. * additionally clears out the previous selected directory and
  849. * the paths leading up to it, if any.
  850. */
  851. private void addItem(File directory) {
  852. if (directory == null) {
  853. return;
  854. }
  855. int oldSize = directories.size();
  856. directories.clear();
  857. if (oldSize > 0) {
  858. fireIntervalRemoved(this, 0, oldSize);
  859. }
  860. // Get the canonical (full) path. This has the side
  861. // benefit of removing extraneous chars from the path,
  862. // for example /foo/bar/ becomes /foo/bar
  863. File canonical = null;
  864. try {
  865. canonical = fsv.createFileObject(directory.getCanonicalPath());
  866. } catch (IOException e) {
  867. // Maybe drive is not ready. Can't abort here.
  868. canonical = directory;
  869. }
  870. // create File instances of each directory leading up to the top
  871. File f = canonical;
  872. do {
  873. directories.add(f);
  874. } while ((f = f.getParentFile()) != null);
  875. int newSize = directories.size();
  876. if (newSize > 0) {
  877. fireIntervalAdded(this, 0, newSize);
  878. }
  879. setSelectedItem(canonical);
  880. }
  881. public void setSelectedItem(Object selectedDirectory) {
  882. this.selectedDirectory = (File)selectedDirectory;
  883. fireContentsChanged(this, -1, -1);
  884. }
  885. public Object getSelectedItem() {
  886. return selectedDirectory;
  887. }
  888. public int getSize() {
  889. return directories.size();
  890. }
  891. public Object getElementAt(int index) {
  892. return directories.elementAt(index);
  893. }
  894. }
  895. /**
  896. * Acts when DirectoryComboBox has changed the selected item.
  897. */
  898. protected class DirectoryComboBoxAction extends AbstractAction {
  899. protected DirectoryComboBoxAction() {
  900. super("DirectoryComboBoxAction");
  901. }
  902. public void actionPerformed(ActionEvent e) {
  903. File f = (File)directoryComboBox.getSelectedItem();
  904. getFileChooser().setCurrentDirectory(f);
  905. }
  906. }
  907. /**
  908. * Creates a new folder.
  909. */
  910. private class NewFolderAction extends AbstractAction {
  911. protected NewFolderAction() {
  912. super(FilePane.ACTION_NEW_FOLDER);
  913. }
  914. public void actionPerformed(ActionEvent e) {
  915. if (readOnly) {
  916. return;
  917. }
  918. JFileChooser fc = getFileChooser();
  919. File currentDirectory = fc.getCurrentDirectory();
  920. String dirName = (String) JOptionPane.showInputDialog(fc,
  921. newFolderDialogText, newFolderButtonText,
  922. JOptionPane.PLAIN_MESSAGE);
  923. if (dirName != null) {
  924. File newDir = fc.getFileSystemView().createFileObject
  925. (currentDirectory, dirName);
  926. if (newDir == null || !newDir.mkdir()) {
  927. JOptionPane.showMessageDialog(fc,
  928. newFolderErrorText + newFolderErrorSeparator + " \"" +
  929. dirName + "\"",
  930. newFolderErrorText, JOptionPane.ERROR_MESSAGE);
  931. }
  932. fc.rescanCurrentDirectory();
  933. }
  934. }
  935. }
  936. private class GTKApproveSelectionAction extends ApproveSelectionAction {
  937. public void actionPerformed(ActionEvent e) {
  938. if (isDirectorySelected()) {
  939. File dir = getDirectory();
  940. try {
  941. if (dir != null) {
  942. dir = dir.getCanonicalFile();
  943. }
  944. // Strip trailing ".."
  945. } catch (IOException ex) {
  946. // Ok, use f as is
  947. }
  948. if (getFileChooser().getCurrentDirectory().equals(dir)) {
  949. directoryList.clearSelection();
  950. rescanCurrentDirectory(getFileChooser());
  951. return;
  952. }
  953. }
  954. super.actionPerformed(e);
  955. }
  956. }
  957. /**
  958. * Renames file
  959. */
  960. private class RenameFileAction extends AbstractAction {
  961. protected RenameFileAction() {
  962. super(FilePane.ACTION_EDIT_FILE_NAME);
  963. }
  964. public void actionPerformed(ActionEvent e) {
  965. if (getFileName().equals("")) {
  966. return;
  967. }
  968. JFileChooser fc = getFileChooser();
  969. File currentDirectory = fc.getCurrentDirectory();
  970. String newFileName = (String) JOptionPane.showInputDialog
  971. (fc, new MessageFormat(renameFileDialogText).format
  972. (new Object[] { getFileName() }),
  973. renameFileButtonText, JOptionPane.PLAIN_MESSAGE, null, null,
  974. getFileName());
  975. if (newFileName != null) {
  976. File oldFile = fc.getFileSystemView().createFileObject
  977. (currentDirectory, getFileName());
  978. File newFile = fc.getFileSystemView().createFileObject
  979. (currentDirectory, newFileName);
  980. if (oldFile == null || newFile == null ||
  981. !getModel().renameFile(oldFile, newFile)) {
  982. JOptionPane.showMessageDialog(fc,
  983. new MessageFormat(renameFileErrorText).
  984. format(new Object[] { getFileName(), newFileName}),
  985. renameFileErrorTitle, JOptionPane.ERROR_MESSAGE);
  986. } else {
  987. setFileName(getFileChooser().getName(newFile));
  988. fc.rescanCurrentDirectory();
  989. }
  990. }
  991. }
  992. }
  993. //
  994. // Renderer for Filter ComboBox
  995. //
  996. protected FilterComboBoxRenderer createFilterComboBoxRenderer() {
  997. return new FilterComboBoxRenderer();
  998. }
  999. /**
  1000. * Render different filters
  1001. */
  1002. public class FilterComboBoxRenderer extends DefaultListCellRenderer implements UIResource {
  1003. public String getName() {
  1004. // As SynthComboBoxRenderer's are asked for a size BEFORE they
  1005. // are parented getName is overriden to force the name to be
  1006. // ComboBox.renderer if it isn't set. If we didn't do this the
  1007. // wrong style could be used for size calculations.
  1008. String name = super.getName();
  1009. if (name == null) {
  1010. return "ComboBox.renderer";
  1011. }
  1012. return name;
  1013. }
  1014. public Component getListCellRendererComponent(JList list, Object value,
  1015. int index, boolean isSelected,
  1016. boolean cellHasFocus) {
  1017. super.getListCellRendererComponent(list, value, index, isSelected, cellHasFocus);
  1018. setName("ComboBox.listRenderer");
  1019. if (value != null) {
  1020. if (value instanceof FileFilter) {
  1021. setText(((FileFilter) value).getDescription());
  1022. }
  1023. } else {
  1024. setText("");
  1025. }
  1026. return this;
  1027. }
  1028. }
  1029. //
  1030. // DataModel for Filter Combobox
  1031. //
  1032. protected FilterComboBoxModel createFilterComboBoxModel() {
  1033. return new FilterComboBoxModel();
  1034. }
  1035. /**
  1036. * Data model for filter combo-box.
  1037. */
  1038. protected class FilterComboBoxModel extends AbstractListModel
  1039. implements ComboBoxModel, PropertyChangeListener {
  1040. protected FileFilter[] filters;
  1041. protected FilterComboBoxModel() {
  1042. super();
  1043. filters = getFileChooser().getChoosableFileFilters();
  1044. }
  1045. public void propertyChange(PropertyChangeEvent e) {
  1046. String prop = e.getPropertyName();
  1047. if (prop == JFileChooser.CHOOSABLE_FILE_FILTER_CHANGED_PROPERTY) {
  1048. filters = (FileFilter[]) e.getNewValue();
  1049. fireContentsChanged(this, -1, -1);
  1050. } else if (prop == JFileChooser.FILE_FILTER_CHANGED_PROPERTY) {
  1051. fireContentsChanged(this, -1, -1);
  1052. }
  1053. }
  1054. public void setSelectedItem(Object filter) {
  1055. if (filter != null) {
  1056. getFileChooser().setFileFilter((FileFilter) filter);
  1057. setFileName(null);
  1058. fireContentsChanged(this, -1, -1);
  1059. }
  1060. }
  1061. public Object getSelectedItem() {
  1062. // Ensure that the current filter is in the list.
  1063. // NOTE: we shouldnt' have to do this, since JFileChooser adds
  1064. // the filter to the choosable filters list when the filter
  1065. // is set. Lets be paranoid just in case someone overrides
  1066. // setFileFilter in JFileChooser.
  1067. FileFilter currentFilter = getFileChooser().getFileFilter();
  1068. boolean found = false;
  1069. if (currentFilter != null) {
  1070. for (int i = 0; i < filters.length; i++) {
  1071. if (filters[i] == currentFilter) {
  1072. found = true;
  1073. }
  1074. }
  1075. if (found == false) {
  1076. getFileChooser().addChoosableFileFilter(currentFilter);
  1077. }
  1078. }
  1079. return getFileChooser().getFileFilter();
  1080. }
  1081. public int getSize() {
  1082. if (filters != null) {
  1083. return filters.length;
  1084. } else {
  1085. return 0;
  1086. }
  1087. }
  1088. public Object getElementAt(int index) {
  1089. if (index > getSize() - 1) {
  1090. // This shouldn't happen. Try to recover gracefully.
  1091. return getFileChooser().getFileFilter();
  1092. }
  1093. if (filters != null) {
  1094. return filters[index];
  1095. } else {
  1096. return null;
  1097. }
  1098. }
  1099. }
  1100. }