1. /*
  2. * @(#)MotifFileChooserUI.java 1.22 00/02/02
  3. *
  4. * Copyright 1997-2000 Sun Microsystems, Inc. All Rights Reserved.
  5. *
  6. * This software is the proprietary information of Sun Microsystems, Inc.
  7. * Use is subject to license terms.
  8. *
  9. */
  10. package com.sun.java.swing.plaf.motif;
  11. import javax.swing.*;
  12. import javax.swing.filechooser.*;
  13. import javax.swing.event.*;
  14. import javax.swing.plaf.*;
  15. import javax.swing.plaf.basic.*;
  16. import java.awt.*;
  17. import java.awt.event.*;
  18. import java.beans.*;
  19. import java.io.File;
  20. import java.io.IOException;
  21. import java.util.*;
  22. /**
  23. * Motif FileChooserUI.
  24. *
  25. * @version 1.22 02/02/00
  26. * @author Jeff Dinkins
  27. */
  28. public class MotifFileChooserUI extends BasicFileChooserUI {
  29. private FilterComboBoxModel filterComboBoxModel;
  30. protected JList directoryList = null;
  31. protected JList fileList = null;
  32. protected JTextField pathField = null;
  33. protected JComboBox filterComboBox = null;
  34. protected JTextField filenameTextField = null;
  35. private static final Dimension hstrut10 = new Dimension(10, 1);
  36. private static final Dimension vstrut10 = new Dimension(1, 10);
  37. private static final Insets insets = new Insets(10, 10, 10, 10);
  38. private static Dimension prefListSize = new Dimension(75, 150);
  39. private static Dimension WITH_ACCELERATOR_PREF_SIZE = new Dimension(650, 450);
  40. private static Dimension PREF_SIZE = new Dimension(350, 450);
  41. private static Dimension MIN_SIZE = new Dimension(200, 300);
  42. private static Dimension PREF_ACC_SIZE = new Dimension(10, 10);
  43. private static Dimension ZERO_ACC_SIZE = new Dimension(1, 1);
  44. private static Dimension MAX_SIZE = new Dimension(Short.MAX_VALUE, Short.MAX_VALUE);
  45. private static final Insets buttonMargin = new Insets(3, 3, 3, 3);
  46. private JPanel directoryPanel = new JPanel();
  47. protected JButton approveButton;
  48. private String enterFileNameLabelText = null;
  49. private int enterFileNameLabelMnemonic = 0;
  50. private String filesLabelText = null;
  51. private int filesLabelMnemonic = 0;
  52. private String foldersLabelText = null;
  53. private int foldersLabelMnemonic = 0;
  54. private String pathLabelText = null;
  55. private int pathLabelMnemonic = 0;
  56. private String filterLabelText = null;
  57. private int filterLabelMnemonic = 0;
  58. public MotifFileChooserUI(JFileChooser filechooser) {
  59. super(filechooser);
  60. }
  61. public String getFileName() {
  62. if(filenameTextField != null) {
  63. return filenameTextField.getText();
  64. } else {
  65. return null;
  66. }
  67. }
  68. public void setFileName(String filename) {
  69. if(filenameTextField != null) {
  70. filenameTextField.setText(filename);
  71. }
  72. }
  73. public String getDirectoryName() {
  74. return pathField.getText();
  75. }
  76. public void setDirectoryName(String dirname) {
  77. pathField.setText(dirname);
  78. }
  79. public void ensureFileIsVisible(JFileChooser fc, File f) {
  80. // PENDING(jeff)
  81. }
  82. public void rescanCurrentDirectory(JFileChooser fc) {
  83. // PENDING(jeff)
  84. }
  85. public PropertyChangeListener createPropertyChangeListener(JFileChooser fc) {
  86. return new PropertyChangeListener() {
  87. public void propertyChange(PropertyChangeEvent e) {
  88. String prop = e.getPropertyName();
  89. if(prop.equals(JFileChooser.SELECTED_FILE_CHANGED_PROPERTY)) {
  90. File f = (File) e.getNewValue();
  91. if(f != null) {
  92. setFileName(getFileChooser().getName(f));
  93. }
  94. } else if(prop.equals(JFileChooser.DIRECTORY_CHANGED_PROPERTY)) {
  95. directoryList.clearSelection();
  96. File currentDirectory = getFileChooser().getCurrentDirectory();
  97. if(currentDirectory != null) {
  98. try {
  99. setDirectoryName(((File)e.getNewValue()).getCanonicalPath());
  100. } catch (IOException ioe) {
  101. setDirectoryName(((File)e.getNewValue()).getAbsolutePath());
  102. }
  103. }
  104. } else if(prop.equals(JFileChooser.FILE_SELECTION_MODE_CHANGED_PROPERTY)) {
  105. directoryList.clearSelection();
  106. } else if(prop == JFileChooser.MULTI_SELECTION_ENABLED_CHANGED_PROPERTY) {
  107. if(getFileChooser().isMultiSelectionEnabled()) {
  108. fileList.setSelectionMode(ListSelectionModel.MULTIPLE_INTERVAL_SELECTION);
  109. } else {
  110. fileList.setSelectionMode(ListSelectionModel.SINGLE_SELECTION);
  111. fileList.clearSelection();
  112. getFileChooser().setSelectedFiles(null);
  113. }
  114. } else if(prop == JFileChooser.ACCESSORY_CHANGED_PROPERTY) {
  115. if(getAccessoryPanel() != null) {
  116. if(e.getOldValue() != null) {
  117. getAccessoryPanel().remove((JComponent) e.getOldValue());
  118. }
  119. JComponent accessory = (JComponent) e.getNewValue();
  120. if(accessory != null) {
  121. getAccessoryPanel().add(accessory, BorderLayout.CENTER);
  122. getAccessoryPanel().setPreferredSize(PREF_ACC_SIZE);
  123. getAccessoryPanel().setMaximumSize(MAX_SIZE);
  124. } else {
  125. getAccessoryPanel().setPreferredSize(ZERO_ACC_SIZE);
  126. getAccessoryPanel().setMaximumSize(ZERO_ACC_SIZE);
  127. }
  128. }
  129. } else if(prop == JFileChooser.APPROVE_BUTTON_TEXT_CHANGED_PROPERTY ||
  130. prop == JFileChooser.DIALOG_TYPE_CHANGED_PROPERTY) {
  131. approveButton.setText(getApproveButtonText(getFileChooser()));
  132. approveButton.setToolTipText(getApproveButtonToolTipText(getFileChooser()));
  133. }
  134. }
  135. };
  136. }
  137. //
  138. // ComponentUI Interface Implementation methods
  139. //
  140. public static ComponentUI createUI(JComponent c) {
  141. return new MotifFileChooserUI((JFileChooser)c);
  142. }
  143. public void installUI(JComponent c) {
  144. super.installUI(c);
  145. }
  146. public void uninstallUI(JComponent c) {
  147. getFileChooser().removeAll();
  148. super.uninstallUI(c);
  149. }
  150. public void installComponents(JFileChooser fc) {
  151. fc.setLayout(new BoxLayout(fc, BoxLayout.Y_AXIS));
  152. fc.add(Box.createRigidArea(vstrut10));
  153. JPanel interior = new JPanel() {
  154. public Insets getInsets() {
  155. return insets;
  156. }
  157. };
  158. align(interior);
  159. interior.setLayout(new BoxLayout(interior, BoxLayout.Y_AXIS));
  160. fc.add(interior);
  161. // PENDING(jeff) - I18N
  162. JLabel l = new JLabel(pathLabelText);
  163. l.setDisplayedMnemonic(pathLabelMnemonic);
  164. align(l);
  165. interior.add(l);
  166. File currentDirectory = fc.getCurrentDirectory();
  167. String curDirName = null;
  168. if(currentDirectory != null) {
  169. curDirName = currentDirectory.getPath();
  170. }
  171. pathField = new JTextField(curDirName);
  172. l.setLabelFor(pathField);
  173. align(pathField);
  174. // Change to folder on return
  175. pathField.addActionListener(getUpdateAction());
  176. interior.add(pathField);
  177. interior.add(Box.createRigidArea(vstrut10));
  178. // CENTER: left, right accessory
  179. JPanel centerPanel = new JPanel();
  180. centerPanel.setPreferredSize(MAX_SIZE);
  181. centerPanel.setLayout(new BoxLayout(centerPanel, BoxLayout.X_AXIS));
  182. align(centerPanel);
  183. // left panel - Filter & folderList
  184. JPanel leftPanel = new JPanel();
  185. leftPanel.setLayout(new BoxLayout(leftPanel, BoxLayout.Y_AXIS));
  186. align(leftPanel);
  187. // add the filter PENDING(jeff) - I18N
  188. l = new JLabel(filterLabelText);
  189. l.setDisplayedMnemonic(filterLabelMnemonic);
  190. align(l);
  191. leftPanel.add(l);
  192. filterComboBox = new JComboBox();
  193. l.setLabelFor(filterComboBox);
  194. filterComboBoxModel = createFilterComboBoxModel();
  195. filterComboBox.setModel(filterComboBoxModel);
  196. filterComboBox.setRenderer(createFilterComboBoxRenderer());
  197. fc.addPropertyChangeListener(filterComboBoxModel);
  198. align(filterComboBox);
  199. leftPanel.add(filterComboBox);
  200. // leftPanel.add(Box.createRigidArea(vstrut10));
  201. // Add the Folder List PENDING(jeff) - I18N
  202. l = new JLabel(foldersLabelText);
  203. l.setDisplayedMnemonic(foldersLabelMnemonic);
  204. align(l);
  205. leftPanel.add(l);
  206. JScrollPane sp = createDirectoryList();
  207. l.setLabelFor(sp);
  208. leftPanel.add(sp);
  209. // create files list
  210. JPanel rightPanel = new JPanel();
  211. align(rightPanel);
  212. rightPanel.setLayout(new BoxLayout(rightPanel, BoxLayout.Y_AXIS));
  213. l = new JLabel(filesLabelText);
  214. l.setDisplayedMnemonic(filesLabelMnemonic);
  215. align(l);
  216. rightPanel.add(l);
  217. sp = createFilesList();
  218. l.setLabelFor(sp);
  219. rightPanel.add(sp);
  220. centerPanel.add(leftPanel);
  221. centerPanel.add(Box.createRigidArea(hstrut10));
  222. centerPanel.add(rightPanel);
  223. JComponent accessoryPanel = getAccessoryPanel();
  224. JComponent accessory = fc.getAccessory();
  225. if(accessoryPanel != null) {
  226. if(accessory == null) {
  227. accessoryPanel.setPreferredSize(ZERO_ACC_SIZE);
  228. accessoryPanel.setMaximumSize(ZERO_ACC_SIZE);
  229. } else {
  230. getAccessoryPanel().add(accessory, BorderLayout.CENTER);
  231. accessoryPanel.setPreferredSize(PREF_ACC_SIZE);
  232. accessoryPanel.setMaximumSize(MAX_SIZE);
  233. }
  234. align(accessoryPanel);
  235. centerPanel.add(accessoryPanel);
  236. }
  237. interior.add(centerPanel);
  238. interior.add(Box.createRigidArea(vstrut10));
  239. // add the filename field PENDING(jeff) - I18N
  240. l = new JLabel(enterFileNameLabelText);
  241. l.setDisplayedMnemonic(enterFileNameLabelMnemonic);
  242. align(l);
  243. interior.add(l);
  244. filenameTextField = new JTextField();
  245. l.setLabelFor(filenameTextField);
  246. filenameTextField.addActionListener(getApproveSelectionAction());
  247. align(filenameTextField);
  248. filenameTextField.setAlignmentX(JComponent.LEFT_ALIGNMENT);
  249. interior.add(filenameTextField);
  250. interior.add(Box.createRigidArea(vstrut10));
  251. fc.add(new JSeparator());
  252. fc.add(Box.createRigidArea(vstrut10));
  253. // Add buttons
  254. JPanel buttonPanel = new JPanel();
  255. align(buttonPanel);
  256. buttonPanel.setLayout(new BoxLayout(buttonPanel, BoxLayout.X_AXIS));
  257. buttonPanel.add(Box.createGlue());
  258. approveButton = new JButton(getApproveButtonText(fc)) {
  259. public Dimension getMaximumSize() {
  260. return new Dimension(MAX_SIZE.width, this.getPreferredSize().height);
  261. }
  262. };
  263. approveButton.setMnemonic(getApproveButtonMnemonic(fc));
  264. approveButton.setToolTipText(getApproveButtonToolTipText(fc));
  265. align(approveButton);
  266. approveButton.setMargin(buttonMargin);
  267. approveButton.addActionListener(getApproveSelectionAction());
  268. buttonPanel.add(approveButton);
  269. buttonPanel.add(Box.createGlue());
  270. JButton updateButton = new JButton(updateButtonText) {
  271. public Dimension getMaximumSize() {
  272. return new Dimension(MAX_SIZE.width, this.getPreferredSize().height);
  273. }
  274. };
  275. updateButton.setMnemonic(updateButtonMnemonic);
  276. updateButton.setToolTipText(updateButtonToolTipText);
  277. align(updateButton);
  278. updateButton.setMargin(buttonMargin);
  279. updateButton.addActionListener(getUpdateAction());
  280. buttonPanel.add(updateButton);
  281. buttonPanel.add(Box.createGlue());
  282. JButton cancelButton = new JButton(cancelButtonText) {
  283. public Dimension getMaximumSize() {
  284. return new Dimension(MAX_SIZE.width, this.getPreferredSize().height);
  285. }
  286. };
  287. cancelButton.setMnemonic(cancelButtonMnemonic);
  288. cancelButton.setToolTipText(cancelButtonToolTipText);
  289. align(cancelButton);
  290. cancelButton.setMargin(buttonMargin);
  291. cancelButton.addActionListener(getCancelSelectionAction());
  292. buttonPanel.add(cancelButton);
  293. buttonPanel.add(Box.createGlue());
  294. JButton helpButton = new JButton(helpButtonText) {
  295. public Dimension getMaximumSize() {
  296. return new Dimension(MAX_SIZE.width, this.getPreferredSize().height);
  297. }
  298. };
  299. helpButton.setMnemonic(helpButtonMnemonic);
  300. helpButton.setToolTipText(helpButtonToolTipText);
  301. align(helpButton);
  302. helpButton.setMargin(buttonMargin);
  303. helpButton.setEnabled(false);
  304. buttonPanel.add(helpButton);
  305. buttonPanel.add(Box.createGlue());
  306. fc.add(buttonPanel);
  307. fc.add(Box.createRigidArea(vstrut10));
  308. fc.add(Box.createGlue());
  309. }
  310. public void uninstallComponents(JFileChooser fc) {
  311. fc.removeAll();
  312. }
  313. protected void installStrings(JFileChooser fc) {
  314. super.installStrings(fc);
  315. enterFileNameLabelText = UIManager.getString("FileChooser.enterFileNameLabelText");
  316. enterFileNameLabelMnemonic = UIManager.getInt("FileChooser.enterFileNameLabelMnemonic");
  317. filesLabelText = UIManager.getString("FileChooser.filesLabelText");
  318. filesLabelMnemonic = UIManager.getInt("FileChooser.filesLabelMnemonic");
  319. foldersLabelText = UIManager.getString("FileChooser.foldersLabelText");
  320. foldersLabelMnemonic = UIManager.getInt("FileChooser.foldersLabelMnemonic");
  321. pathLabelText = UIManager.getString("FileChooser.pathLabelText");
  322. pathLabelMnemonic = UIManager.getInt("FileChooser.pathLabelMnemonic");
  323. filterLabelText = UIManager.getString("FileChooser.filterLabelText");
  324. filterLabelMnemonic = UIManager.getInt("FileChooser.filterLabelMnemonic");
  325. }
  326. protected void installIcons(JFileChooser fc) {
  327. // Since motif doesn't have button icons, leave this empty
  328. // which overrides the supertype icon loading
  329. }
  330. protected void uninstallIcons(JFileChooser fc) {
  331. // Since motif doesn't have button icons, leave this empty
  332. // which overrides the supertype icon loading
  333. }
  334. protected JScrollPane createFilesList() {
  335. fileList = new JList();
  336. if(getFileChooser().isMultiSelectionEnabled()) {
  337. fileList.setSelectionMode(ListSelectionModel.MULTIPLE_INTERVAL_SELECTION);
  338. } else {
  339. fileList.setSelectionMode(ListSelectionModel.SINGLE_SELECTION);
  340. }
  341. fileList.setModel(new MotifFileListModel());
  342. fileList.setCellRenderer(new FileCellRenderer());
  343. fileList.addListSelectionListener(createListSelectionListener(getFileChooser()));
  344. fileList.addMouseListener(createDoubleClickListener(getFileChooser(), fileList));
  345. align(fileList);
  346. JScrollPane scrollpane = new JScrollPane(fileList);
  347. scrollpane.setPreferredSize(prefListSize);
  348. scrollpane.setMaximumSize(MAX_SIZE);
  349. align(scrollpane);
  350. return scrollpane;
  351. }
  352. protected JScrollPane createDirectoryList() {
  353. directoryList = new JList();
  354. align(directoryList);
  355. directoryList.setCellRenderer(new DirectoryCellRenderer());
  356. directoryList.setModel(new MotifDirectoryListModel());
  357. directoryList.addMouseListener(createDoubleClickListener(getFileChooser(), directoryList));
  358. directoryList.addListSelectionListener(createListSelectionListener(getFileChooser()));
  359. JScrollPane scrollpane = new JScrollPane(directoryList);
  360. scrollpane.setMaximumSize(MAX_SIZE);
  361. scrollpane.setPreferredSize(prefListSize);
  362. align(scrollpane);
  363. return scrollpane;
  364. }
  365. public Dimension getPreferredSize(JComponent x) {
  366. if(getFileChooser().getAccessory() != null) {
  367. return WITH_ACCELERATOR_PREF_SIZE;
  368. } else {
  369. return PREF_SIZE;
  370. }
  371. }
  372. public Dimension getMinimumSize(JComponent x) {
  373. return MIN_SIZE;
  374. }
  375. public Dimension getMaximumSize(JComponent x) {
  376. return new Dimension(Integer.MAX_VALUE, Integer.MAX_VALUE);
  377. }
  378. protected void align(JComponent c) {
  379. c.setAlignmentX(JComponent.LEFT_ALIGNMENT);
  380. c.setAlignmentY(JComponent.TOP_ALIGNMENT);
  381. }
  382. protected class FileCellRenderer extends DefaultListCellRenderer {
  383. public Component getListCellRendererComponent(JList list, Object value, int index,
  384. boolean isSelected, boolean cellHasFocus) {
  385. super.getListCellRendererComponent(list, value, index, isSelected, cellHasFocus);
  386. setText(getFileChooser().getName((File) value));
  387. return this;
  388. }
  389. }
  390. protected class DirectoryCellRenderer extends DefaultListCellRenderer {
  391. public Component getListCellRendererComponent(JList list, Object value, int index,
  392. boolean isSelected, boolean cellHasFocus) {
  393. super.getListCellRendererComponent(list, value, index, isSelected, cellHasFocus);
  394. setText(getFileChooser().getName((File) value));
  395. return this;
  396. }
  397. }
  398. protected class MotifDirectoryListModel extends AbstractListModel implements ListDataListener {
  399. public MotifDirectoryListModel() {
  400. getModel().addListDataListener(this);
  401. }
  402. public int getSize() {
  403. return getModel().getDirectories().size();
  404. }
  405. public Object getElementAt(int index) {
  406. return getModel().getDirectories().elementAt(index);
  407. }
  408. public void intervalAdded(ListDataEvent e) {
  409. }
  410. // PENDING(jeff) - implement
  411. public void intervalRemoved(ListDataEvent e) {
  412. }
  413. // PENDING(jeff) - this is inefficient - should sent out
  414. // incremental adjustment values instead of saying that the
  415. // whole list has changed.
  416. public void fireContentsChanged() {
  417. fireContentsChanged(this, 0, getModel().getDirectories().size()-1);
  418. }
  419. // PENDING(jeff) - fire the correct interval changed - currently sending
  420. // out that everything has changed
  421. public void contentsChanged(ListDataEvent e) {
  422. fireContentsChanged();
  423. }
  424. }
  425. protected class MotifFileListModel extends AbstractListModel implements ListDataListener {
  426. public MotifFileListModel() {
  427. getModel().addListDataListener(this);
  428. }
  429. public int getSize() {
  430. return getModel().getFiles().size();
  431. }
  432. public boolean contains(Object o) {
  433. return getModel().getFiles().contains(o);
  434. }
  435. public int indexOf(Object o) {
  436. return getModel().getFiles().indexOf(o);
  437. }
  438. public Object getElementAt(int index) {
  439. return getModel().getFiles().elementAt(index);
  440. }
  441. public void intervalAdded(ListDataEvent e) {
  442. }
  443. // PENDING(jeff) - implement
  444. public void intervalRemoved(ListDataEvent e) {
  445. }
  446. // PENDING(jeff) - this is inefficient - should sent out
  447. // incremental adjustment values instead of saying that the
  448. // whole list has changed.
  449. public void fireContentsChanged() {
  450. fireContentsChanged(this, 0, getModel().getFiles().size()-1);
  451. }
  452. // PENDING(jeff) - fire the interval changed
  453. public void contentsChanged(ListDataEvent e) {
  454. fireContentsChanged();
  455. }
  456. }
  457. //
  458. // DataModel for Types Comboxbox
  459. //
  460. protected FilterComboBoxModel createFilterComboBoxModel() {
  461. return new FilterComboBoxModel();
  462. }
  463. //
  464. // Renderer for Types ComboBox
  465. //
  466. protected FilterComboBoxRenderer createFilterComboBoxRenderer() {
  467. return new FilterComboBoxRenderer();
  468. }
  469. /**
  470. * Render different type sizes and styles.
  471. */
  472. public class FilterComboBoxRenderer extends DefaultListCellRenderer {
  473. public Component getListCellRendererComponent(JList list,
  474. Object value, int index, boolean isSelected,
  475. boolean cellHasFocus) {
  476. super.getListCellRendererComponent(list, value, index, isSelected, cellHasFocus);
  477. FileFilter filter = (FileFilter) value;
  478. if(filter != null) {
  479. setText(filter.getDescription());
  480. }
  481. return this;
  482. }
  483. }
  484. /**
  485. * Data model for a type-face selection combo-box.
  486. */
  487. protected class FilterComboBoxModel extends AbstractListModel implements ComboBoxModel, PropertyChangeListener {
  488. protected FileFilter[] filters;
  489. protected FilterComboBoxModel() {
  490. super();
  491. filters = getFileChooser().getChoosableFileFilters();
  492. }
  493. public void propertyChange(PropertyChangeEvent e) {
  494. String prop = e.getPropertyName();
  495. if(prop == JFileChooser.CHOOSABLE_FILE_FILTER_CHANGED_PROPERTY) {
  496. filters = (FileFilter[]) e.getNewValue();
  497. fireContentsChanged(this, -1, -1);
  498. }
  499. }
  500. public void setSelectedItem(Object filter) {
  501. if(filter != null) {
  502. getFileChooser().setFileFilter((FileFilter) filter);
  503. fireContentsChanged(this, -1, -1);
  504. }
  505. }
  506. public Object getSelectedItem() {
  507. // Ensure that the current filter is in the list.
  508. // NOTE: we shouldnt' have to do this, since JFileChooser adds
  509. // the filter to the choosable filters list when the filter
  510. // is set. Lets be paranoid just in case someone overrides
  511. // setFileFilter in JFileChooser.
  512. FileFilter currentFilter = getFileChooser().getFileFilter();
  513. boolean found = false;
  514. if(currentFilter != null) {
  515. for(int i=0; i < filters.length; i++) {
  516. if(filters[i] == currentFilter) {
  517. found = true;
  518. }
  519. }
  520. if(found == false) {
  521. getFileChooser().addChoosableFileFilter(currentFilter);
  522. }
  523. }
  524. return getFileChooser().getFileFilter();
  525. }
  526. public int getSize() {
  527. if(filters != null) {
  528. return filters.length;
  529. } else {
  530. return 0;
  531. }
  532. }
  533. public Object getElementAt(int index) {
  534. if(index > getSize() - 1) {
  535. // This shouldn't happen. Try to recover gracefully.
  536. return getFileChooser().getFileFilter();
  537. }
  538. if(filters != null) {
  539. return filters[index];
  540. } else {
  541. return null;
  542. }
  543. }
  544. }
  545. protected JButton getApproveButton(JFileChooser fc) {
  546. return approveButton;
  547. }
  548. }