1. /*
  2. * @(#)MetalFileChooserUI.java 1.71 04/01/13
  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.metal;
  8. import javax.swing.*;
  9. import javax.swing.border.EmptyBorder;
  10. import javax.swing.filechooser.*;
  11. import javax.swing.event.*;
  12. import javax.swing.plaf.*;
  13. import javax.swing.plaf.basic.*;
  14. import javax.swing.table.*;
  15. import javax.swing.text.Position;
  16. import java.awt.*;
  17. import java.awt.event.*;
  18. import java.beans.*;
  19. import java.io.File;
  20. import java.io.FileNotFoundException;
  21. import java.io.IOException;
  22. import java.text.DateFormat;
  23. import java.util.*;
  24. import sun.awt.shell.ShellFolder;
  25. /**
  26. * Metal L&F implementation of a FileChooser.
  27. *
  28. * @version 1.71 01/13/04
  29. * @author Jeff Dinkins
  30. */
  31. public class MetalFileChooserUI extends BasicFileChooserUI {
  32. // Much of the Metal UI for JFilechooser is just a copy of
  33. // the windows implementation, but using Metal themed buttons, lists,
  34. // icons, etc. We are planning a complete rewrite, and hence we've
  35. // made most things in this class private.
  36. private JPanel centerPanel;
  37. private JLabel lookInLabel;
  38. private JComboBox directoryComboBox;
  39. private DirectoryComboBoxModel directoryComboBoxModel;
  40. private Action directoryComboBoxAction = new DirectoryComboBoxAction();
  41. private FilterComboBoxModel filterComboBoxModel;
  42. private JTextField fileNameTextField;
  43. private JToggleButton listViewButton;
  44. private JToggleButton detailsViewButton;
  45. private JPanel listViewPanel;
  46. private JPanel detailsViewPanel;
  47. private JPanel currentViewPanel;
  48. private FocusListener editorFocusListener = new FocusAdapter() {
  49. public void focusLost(FocusEvent e) {
  50. if (! e.isTemporary()) {
  51. applyEdit();
  52. }
  53. }
  54. };
  55. private boolean useShellFolder;
  56. private ListSelectionModel listSelectionModel;
  57. private JList list;
  58. private JTable detailsTable;
  59. private JButton approveButton;
  60. private JButton cancelButton;
  61. private JPanel buttonPanel;
  62. private JPanel bottomPanel;
  63. private JComboBox filterComboBox;
  64. private static final Dimension hstrut5 = new Dimension(5, 1);
  65. private static final Dimension hstrut11 = new Dimension(11, 1);
  66. private static final Dimension vstrut5 = new Dimension(1, 5);
  67. private static final Insets shrinkwrap = new Insets(0,0,0,0);
  68. // Preferred and Minimum sizes for the dialog box
  69. private static int PREF_WIDTH = 500;
  70. private static int PREF_HEIGHT = 326;
  71. private static Dimension PREF_SIZE = new Dimension(PREF_WIDTH, PREF_HEIGHT);
  72. private static int MIN_WIDTH = 500;
  73. private static int MIN_HEIGHT = 326;
  74. private static Dimension MIN_SIZE = new Dimension(MIN_WIDTH, MIN_HEIGHT);
  75. private static int LIST_PREF_WIDTH = 405;
  76. private static int LIST_PREF_HEIGHT = 135;
  77. private static Dimension LIST_PREF_SIZE = new Dimension(LIST_PREF_WIDTH, LIST_PREF_HEIGHT);
  78. private static final int COLUMN_FILENAME = 0;
  79. private static final int COLUMN_FILESIZE = 1;
  80. private static final int COLUMN_FILETYPE = 2;
  81. private static final int COLUMN_FILEDATE = 3;
  82. private static final int COLUMN_FILEATTR = 4;
  83. private static final int COLUMN_COLCOUNT = 5;
  84. private int[] COLUMN_WIDTHS = { 150, 75, 130, 130, 40 };
  85. // Labels, mnemonics, and tooltips (oh my!)
  86. private int lookInLabelMnemonic = 0;
  87. private String lookInLabelText = null;
  88. private String saveInLabelText = null;
  89. private int fileNameLabelMnemonic = 0;
  90. private String fileNameLabelText = null;
  91. private int filesOfTypeLabelMnemonic = 0;
  92. private String filesOfTypeLabelText = null;
  93. private String upFolderToolTipText = null;
  94. private String upFolderAccessibleName = null;
  95. private String homeFolderToolTipText = null;
  96. private String homeFolderAccessibleName = null;
  97. private String newFolderToolTipText = null;
  98. private String newFolderAccessibleName = null;
  99. private String listViewButtonToolTipText = null;
  100. private String listViewButtonAccessibleName = null;
  101. private String detailsViewButtonToolTipText = null;
  102. private String detailsViewButtonAccessibleName = null;
  103. private String fileNameHeaderText = null;
  104. private String fileSizeHeaderText = null;
  105. private String fileTypeHeaderText = null;
  106. private String fileDateHeaderText = null;
  107. private String fileAttrHeaderText = null;
  108. //
  109. // ComponentUI Interface Implementation methods
  110. //
  111. public static ComponentUI createUI(JComponent c) {
  112. return new MetalFileChooserUI((JFileChooser) c);
  113. }
  114. public MetalFileChooserUI(JFileChooser filechooser) {
  115. super(filechooser);
  116. }
  117. public void installUI(JComponent c) {
  118. super.installUI(c);
  119. }
  120. public void uninstallComponents(JFileChooser fc) {
  121. fc.removeAll();
  122. bottomPanel = null;
  123. buttonPanel = null;
  124. }
  125. public void installComponents(JFileChooser fc) {
  126. FileSystemView fsv = fc.getFileSystemView();
  127. fc.setBorder(new EmptyBorder(12, 12, 11, 11));
  128. fc.setLayout(new BorderLayout(0, 11));
  129. // ********************************* //
  130. // **** Construct the top panel **** //
  131. // ********************************* //
  132. // Directory manipulation buttons
  133. JPanel topPanel = new JPanel(new BorderLayout(11, 0));
  134. JPanel topButtonPanel = new JPanel();
  135. topButtonPanel.setLayout(new BoxLayout(topButtonPanel, BoxLayout.LINE_AXIS));
  136. topPanel.add(topButtonPanel, BorderLayout.AFTER_LINE_ENDS);
  137. // Add the top panel to the fileChooser
  138. fc.add(topPanel, BorderLayout.NORTH);
  139. // ComboBox Label
  140. lookInLabel = new JLabel(lookInLabelText);
  141. lookInLabel.setDisplayedMnemonic(lookInLabelMnemonic);
  142. topPanel.add(lookInLabel, BorderLayout.BEFORE_LINE_BEGINS);
  143. // CurrentDir ComboBox
  144. directoryComboBox = new JComboBox();
  145. directoryComboBox.getAccessibleContext().setAccessibleDescription(lookInLabelText);
  146. directoryComboBox.putClientProperty( "JComboBox.lightweightKeyboardNavigation", "Lightweight" );
  147. lookInLabel.setLabelFor(directoryComboBox);
  148. directoryComboBoxModel = createDirectoryComboBoxModel(fc);
  149. directoryComboBox.setModel(directoryComboBoxModel);
  150. directoryComboBox.addActionListener(directoryComboBoxAction);
  151. directoryComboBox.setRenderer(createDirectoryComboBoxRenderer(fc));
  152. directoryComboBox.setAlignmentX(JComponent.LEFT_ALIGNMENT);
  153. directoryComboBox.setAlignmentY(JComponent.TOP_ALIGNMENT);
  154. directoryComboBox.setMaximumRowCount(8);
  155. topPanel.add(directoryComboBox, BorderLayout.CENTER);
  156. // Up Button
  157. JButton upFolderButton = new JButton(getChangeToParentDirectoryAction());
  158. upFolderButton.setText(null);
  159. upFolderButton.setIcon(upFolderIcon);
  160. upFolderButton.setToolTipText(upFolderToolTipText);
  161. upFolderButton.getAccessibleContext().setAccessibleName(upFolderAccessibleName);
  162. upFolderButton.setAlignmentX(JComponent.LEFT_ALIGNMENT);
  163. upFolderButton.setAlignmentY(JComponent.CENTER_ALIGNMENT);
  164. upFolderButton.setMargin(shrinkwrap);
  165. topButtonPanel.add(upFolderButton);
  166. topButtonPanel.add(Box.createRigidArea(hstrut5));
  167. // Home Button
  168. File homeDir = fsv.getHomeDirectory();
  169. String toolTipText = homeFolderToolTipText;
  170. if (fsv.isRoot(homeDir)) {
  171. toolTipText = getFileView(fc).getName(homeDir); // Probably "Desktop".
  172. }
  173. JButton b = new JButton(homeFolderIcon);
  174. b.setToolTipText(toolTipText);
  175. b.getAccessibleContext().setAccessibleName(homeFolderAccessibleName);
  176. b.setAlignmentX(JComponent.LEFT_ALIGNMENT);
  177. b.setAlignmentY(JComponent.CENTER_ALIGNMENT);
  178. b.setMargin(shrinkwrap);
  179. b.addActionListener(getGoHomeAction());
  180. topButtonPanel.add(b);
  181. topButtonPanel.add(Box.createRigidArea(hstrut5));
  182. // New Directory Button
  183. b = new JButton(getNewFolderAction());
  184. b.setText(null);
  185. b.setIcon(newFolderIcon);
  186. b.setToolTipText(newFolderToolTipText);
  187. b.getAccessibleContext().setAccessibleName(newFolderAccessibleName);
  188. b.setAlignmentX(JComponent.LEFT_ALIGNMENT);
  189. b.setAlignmentY(JComponent.CENTER_ALIGNMENT);
  190. b.setMargin(shrinkwrap);
  191. topButtonPanel.add(b);
  192. topButtonPanel.add(Box.createRigidArea(hstrut5));
  193. // View button group
  194. ButtonGroup viewButtonGroup = new ButtonGroup();
  195. class ViewButtonListener implements ActionListener {
  196. JFileChooser fc;
  197. ViewButtonListener(JFileChooser fc) {
  198. this.fc = fc;
  199. }
  200. public void actionPerformed(ActionEvent e) {
  201. JToggleButton b = (JToggleButton)e.getSource();
  202. JPanel oldViewPanel = currentViewPanel;
  203. if (b == detailsViewButton) {
  204. if (detailsViewPanel == null) {
  205. detailsViewPanel = createDetailsView(fc);
  206. detailsViewPanel.setPreferredSize(LIST_PREF_SIZE);
  207. }
  208. currentViewPanel = detailsViewPanel;
  209. } else {
  210. currentViewPanel = listViewPanel;
  211. }
  212. if (currentViewPanel != oldViewPanel) {
  213. centerPanel.remove(oldViewPanel);
  214. centerPanel.add(currentViewPanel, BorderLayout.CENTER);
  215. centerPanel.revalidate();
  216. centerPanel.repaint();
  217. }
  218. }
  219. }
  220. ViewButtonListener viewButtonListener = new ViewButtonListener(fc);
  221. // List Button
  222. listViewButton = new JToggleButton(listViewIcon);
  223. listViewButton.setToolTipText(listViewButtonToolTipText);
  224. listViewButton.getAccessibleContext().setAccessibleName(listViewButtonAccessibleName);
  225. listViewButton.setSelected(true);
  226. listViewButton.setAlignmentX(JComponent.LEFT_ALIGNMENT);
  227. listViewButton.setAlignmentY(JComponent.CENTER_ALIGNMENT);
  228. listViewButton.setMargin(shrinkwrap);
  229. listViewButton.addActionListener(viewButtonListener);
  230. topButtonPanel.add(listViewButton);
  231. viewButtonGroup.add(listViewButton);
  232. // Details Button
  233. detailsViewButton = new JToggleButton(detailsViewIcon);
  234. detailsViewButton.setToolTipText(detailsViewButtonToolTipText);
  235. detailsViewButton.getAccessibleContext().setAccessibleName(detailsViewButtonAccessibleName);
  236. detailsViewButton.setAlignmentX(JComponent.LEFT_ALIGNMENT);
  237. detailsViewButton.setAlignmentY(JComponent.CENTER_ALIGNMENT);
  238. detailsViewButton.setMargin(shrinkwrap);
  239. detailsViewButton.addActionListener(viewButtonListener);
  240. topButtonPanel.add(detailsViewButton);
  241. viewButtonGroup.add(detailsViewButton);
  242. updateUseShellFolder();
  243. // ************************************** //
  244. // ******* Add the directory pane ******* //
  245. // ************************************** //
  246. centerPanel = new JPanel(new BorderLayout());
  247. listViewPanel = createList(fc);
  248. listSelectionModel = list.getSelectionModel();
  249. listViewPanel.setPreferredSize(LIST_PREF_SIZE);
  250. centerPanel.add(listViewPanel, BorderLayout.CENTER);
  251. currentViewPanel = listViewPanel;
  252. centerPanel.add(getAccessoryPanel(), BorderLayout.AFTER_LINE_ENDS);
  253. JComponent accessory = fc.getAccessory();
  254. if(accessory != null) {
  255. getAccessoryPanel().add(accessory);
  256. }
  257. fc.add(centerPanel, BorderLayout.CENTER);
  258. // ********************************** //
  259. // **** Construct the bottom panel ** //
  260. // ********************************** //
  261. JPanel bottomPanel = getBottomPanel();
  262. bottomPanel.setLayout(new BoxLayout(bottomPanel, BoxLayout.Y_AXIS));
  263. fc.add(bottomPanel, BorderLayout.SOUTH);
  264. // FileName label and textfield
  265. JPanel fileNamePanel = new JPanel();
  266. fileNamePanel.setLayout(new BoxLayout(fileNamePanel, BoxLayout.LINE_AXIS));
  267. bottomPanel.add(fileNamePanel);
  268. bottomPanel.add(Box.createRigidArea(vstrut5));
  269. AlignedLabel fileNameLabel = new AlignedLabel(fileNameLabelText);
  270. fileNameLabel.setDisplayedMnemonic(fileNameLabelMnemonic);
  271. fileNamePanel.add(fileNameLabel);
  272. fileNameTextField = new JTextField(35) {
  273. public Dimension getMaximumSize() {
  274. return new Dimension(Short.MAX_VALUE, super.getPreferredSize().height);
  275. }
  276. };
  277. fileNamePanel.add(fileNameTextField);
  278. fileNameLabel.setLabelFor(fileNameTextField);
  279. fileNameTextField.addFocusListener(
  280. new FocusAdapter() {
  281. public void focusGained(FocusEvent e) {
  282. if (!getFileChooser().isMultiSelectionEnabled()) {
  283. listSelectionModel.clearSelection();
  284. }
  285. }
  286. }
  287. );
  288. if (fc.isMultiSelectionEnabled()) {
  289. setFileName(fileNameString(fc.getSelectedFiles()));
  290. } else {
  291. setFileName(fileNameString(fc.getSelectedFile()));
  292. }
  293. // Filetype label and combobox
  294. JPanel filesOfTypePanel = new JPanel();
  295. filesOfTypePanel.setLayout(new BoxLayout(filesOfTypePanel, BoxLayout.LINE_AXIS));
  296. bottomPanel.add(filesOfTypePanel);
  297. AlignedLabel filesOfTypeLabel = new AlignedLabel(filesOfTypeLabelText);
  298. filesOfTypeLabel.setDisplayedMnemonic(filesOfTypeLabelMnemonic);
  299. filesOfTypePanel.add(filesOfTypeLabel);
  300. filterComboBoxModel = createFilterComboBoxModel();
  301. fc.addPropertyChangeListener(filterComboBoxModel);
  302. filterComboBox = new JComboBox(filterComboBoxModel);
  303. filterComboBox.getAccessibleContext().setAccessibleDescription(filesOfTypeLabelText);
  304. filesOfTypeLabel.setLabelFor(filterComboBox);
  305. filterComboBox.setRenderer(createFilterComboBoxRenderer());
  306. filesOfTypePanel.add(filterComboBox);
  307. // buttons
  308. getButtonPanel().setLayout(new ButtonAreaLayout());
  309. approveButton = new JButton(getApproveButtonText(fc));
  310. // Note: Metal does not use mnemonics for approve and cancel
  311. approveButton.addActionListener(getApproveSelectionAction());
  312. approveButton.setToolTipText(getApproveButtonToolTipText(fc));
  313. getButtonPanel().add(approveButton);
  314. cancelButton = new JButton(cancelButtonText);
  315. cancelButton.setToolTipText(cancelButtonToolTipText);
  316. cancelButton.addActionListener(getCancelSelectionAction());
  317. getButtonPanel().add(cancelButton);
  318. if(fc.getControlButtonsAreShown()) {
  319. addControlButtons();
  320. }
  321. groupLabels(new AlignedLabel[] { fileNameLabel, filesOfTypeLabel });
  322. }
  323. private void updateUseShellFolder() {
  324. // Decide whether to use the ShellFolder class to populate shortcut
  325. // panel and combobox.
  326. JFileChooser fc = getFileChooser();
  327. Boolean prop =
  328. (Boolean)fc.getClientProperty("FileChooser.useShellFolder");
  329. if (prop != null) {
  330. useShellFolder = prop.booleanValue();
  331. } else {
  332. // See if FileSystemView.getRoots() returns the desktop folder,
  333. // i.e. the normal Windows hierarchy.
  334. useShellFolder = false;
  335. File[] roots = fc.getFileSystemView().getRoots();
  336. if (roots != null && roots.length == 1) {
  337. File[] cbFolders = (File[])ShellFolder.get("fileChooserComboBoxFolders");
  338. if (cbFolders != null && cbFolders.length > 0 && roots[0] == cbFolders[0]) {
  339. useShellFolder = true;
  340. }
  341. }
  342. }
  343. }
  344. protected JPanel getButtonPanel() {
  345. if (buttonPanel == null) {
  346. buttonPanel = new JPanel();
  347. }
  348. return buttonPanel;
  349. }
  350. protected JPanel getBottomPanel() {
  351. if(bottomPanel == null) {
  352. bottomPanel = new JPanel();
  353. }
  354. return bottomPanel;
  355. }
  356. protected void installStrings(JFileChooser fc) {
  357. super.installStrings(fc);
  358. Locale l = fc.getLocale();
  359. lookInLabelMnemonic = UIManager.getInt("FileChooser.lookInLabelMnemonic");
  360. lookInLabelText = UIManager.getString("FileChooser.lookInLabelText",l);
  361. saveInLabelText = UIManager.getString("FileChooser.saveInLabelText",l);
  362. fileNameLabelMnemonic = UIManager.getInt("FileChooser.fileNameLabelMnemonic");
  363. fileNameLabelText = UIManager.getString("FileChooser.fileNameLabelText",l);
  364. filesOfTypeLabelMnemonic = UIManager.getInt("FileChooser.filesOfTypeLabelMnemonic");
  365. filesOfTypeLabelText = UIManager.getString("FileChooser.filesOfTypeLabelText",l);
  366. upFolderToolTipText = UIManager.getString("FileChooser.upFolderToolTipText",l);
  367. upFolderAccessibleName = UIManager.getString("FileChooser.upFolderAccessibleName",l);
  368. homeFolderToolTipText = UIManager.getString("FileChooser.homeFolderToolTipText",l);
  369. homeFolderAccessibleName = UIManager.getString("FileChooser.homeFolderAccessibleName",l);
  370. newFolderToolTipText = UIManager.getString("FileChooser.newFolderToolTipText",l);
  371. newFolderAccessibleName = UIManager.getString("FileChooser.newFolderAccessibleName",l);
  372. listViewButtonToolTipText = UIManager.getString("FileChooser.listViewButtonToolTipText",l);
  373. listViewButtonAccessibleName = UIManager.getString("FileChooser.listViewButtonAccessibleName",l);
  374. detailsViewButtonToolTipText = UIManager.getString("FileChooser.detailsViewButtonToolTipText",l);
  375. detailsViewButtonAccessibleName = UIManager.getString("FileChooser.detailsViewButtonAccessibleName",l);
  376. fileNameHeaderText = UIManager.getString("FileChooser.fileNameHeaderText",l);
  377. fileSizeHeaderText = UIManager.getString("FileChooser.fileSizeHeaderText",l);
  378. fileTypeHeaderText = UIManager.getString("FileChooser.fileTypeHeaderText",l);
  379. fileDateHeaderText = UIManager.getString("FileChooser.fileDateHeaderText",l);
  380. fileAttrHeaderText = UIManager.getString("FileChooser.fileAttrHeaderText",l);
  381. }
  382. protected void installListeners(JFileChooser fc) {
  383. super.installListeners(fc);
  384. ActionMap actionMap = getActionMap();
  385. SwingUtilities.replaceUIActionMap(fc, actionMap);
  386. }
  387. protected ActionMap getActionMap() {
  388. return createActionMap();
  389. }
  390. protected ActionMap createActionMap() {
  391. AbstractAction escAction = new AbstractAction() {
  392. public void actionPerformed(ActionEvent e) {
  393. if (editFile != null) {
  394. cancelEdit();
  395. } else {
  396. getFileChooser().cancelSelection();
  397. }
  398. }
  399. public boolean isEnabled(){
  400. return getFileChooser().isEnabled();
  401. }
  402. };
  403. ActionMap map = new ActionMapUIResource();
  404. map.put("approveSelection", getApproveSelectionAction());
  405. map.put("cancelSelection", escAction);
  406. map.put("Go Up", getChangeToParentDirectoryAction());
  407. return map;
  408. }
  409. protected JPanel createList(JFileChooser fc) {
  410. JPanel p = new JPanel(new BorderLayout());
  411. final JFileChooser fileChooser = fc;
  412. list = new JList() {
  413. public int getNextMatch(String prefix, int startIndex, Position.Bias bias) {
  414. ListModel model = getModel();
  415. int max = model.getSize();
  416. if (prefix == null || startIndex < 0 || startIndex >= max) {
  417. throw new IllegalArgumentException();
  418. }
  419. // start search from the next element before/after the selected element
  420. boolean backwards = (bias == Position.Bias.Backward);
  421. for (int i = startIndex; backwards ? i >= 0 : i < max; i += (backwards ? -1 : 1)) {
  422. String filename = fileChooser.getName((File)model.getElementAt(i));
  423. if (filename.regionMatches(true, 0, prefix, 0, prefix.length())) {
  424. return i;
  425. }
  426. }
  427. return -1;
  428. }
  429. };
  430. list.setCellRenderer(new FileRenderer());
  431. list.setLayoutOrientation(JList.VERTICAL_WRAP);
  432. list.setVisibleRowCount(-1);
  433. if (fc.isMultiSelectionEnabled()) {
  434. list.setSelectionMode(ListSelectionModel.MULTIPLE_INTERVAL_SELECTION);
  435. } else {
  436. list.setSelectionMode(ListSelectionModel.SINGLE_SELECTION);
  437. }
  438. list.setModel(getModel());
  439. list.addListSelectionListener(createListSelectionListener(fc));
  440. list.addMouseListener(createDoubleClickListener(fc, list));
  441. list.addMouseListener(createSingleClickListener(fc, list));
  442. getModel().addListDataListener(new ListDataListener() {
  443. public void contentsChanged(ListDataEvent e) {
  444. // Update the selection after JList has been updated
  445. new DelayedSelectionUpdater();
  446. }
  447. public void intervalAdded(ListDataEvent e) {
  448. new DelayedSelectionUpdater();
  449. }
  450. public void intervalRemoved(ListDataEvent e) {
  451. }
  452. });
  453. JScrollPane scrollpane = new JScrollPane(list);
  454. p.add(scrollpane, BorderLayout.CENTER);
  455. return p;
  456. }
  457. class DetailsTableModel extends AbstractTableModel implements ListDataListener {
  458. String[] columnNames = {
  459. fileNameHeaderText,
  460. fileSizeHeaderText,
  461. fileTypeHeaderText,
  462. fileDateHeaderText,
  463. fileAttrHeaderText
  464. };
  465. JFileChooser chooser;
  466. ListModel listModel;
  467. DetailsTableModel(JFileChooser fc) {
  468. this.chooser = fc;
  469. listModel = getModel();
  470. listModel.addListDataListener(this);
  471. }
  472. public int getRowCount() {
  473. return listModel.getSize();
  474. }
  475. public int getColumnCount() {
  476. return COLUMN_COLCOUNT;
  477. }
  478. public String getColumnName(int column) {
  479. return columnNames[column];
  480. }
  481. public Class getColumnClass(int column) {
  482. switch (column) {
  483. case COLUMN_FILENAME:
  484. return File.class;
  485. case COLUMN_FILEDATE:
  486. return Date.class;
  487. default:
  488. return super.getColumnClass(column);
  489. }
  490. }
  491. public Object getValueAt(int row, int col) {
  492. // Note: It is very important to avoid getting info on drives, as
  493. // this will trigger "No disk in A:" and similar dialogs.
  494. //
  495. // Use (f.exists() && !chooser.getFileSystemView().isFileSystemRoot(f)) to
  496. // determine if it is safe to call methods directly on f.
  497. File f = (File)listModel.getElementAt(row);
  498. switch (col) {
  499. case COLUMN_FILENAME:
  500. return f;
  501. case COLUMN_FILESIZE:
  502. if (!f.exists() || f.isDirectory()) {
  503. return null;
  504. }
  505. long len = f.length() / 1024L;
  506. if (len < 1024L) {
  507. return ((len == 0L) ? 1L : len) + " KB";
  508. } else {
  509. len /= 1024L;
  510. if (len < 1024L) {
  511. return len + " MB";
  512. } else {
  513. len /= 1024L;
  514. return len + " GB";
  515. }
  516. }
  517. case COLUMN_FILETYPE:
  518. if (!f.exists()) {
  519. return null;
  520. }
  521. return chooser.getFileSystemView().getSystemTypeDescription(f);
  522. case COLUMN_FILEDATE:
  523. if (!f.exists() || chooser.getFileSystemView().isFileSystemRoot(f)) {
  524. return null;
  525. }
  526. long time = f.lastModified();
  527. return (time == 0L) ? null : new Date(time);
  528. case COLUMN_FILEATTR:
  529. if (!f.exists() || chooser.getFileSystemView().isFileSystemRoot(f)) {
  530. return null;
  531. }
  532. String attributes = "";
  533. if (!f.canWrite()) {
  534. attributes += "R";
  535. }
  536. if (f.isHidden()) {
  537. attributes += "H";
  538. }
  539. return attributes;
  540. }
  541. return null;
  542. }
  543. public void setValueAt(Object value, int row, int col) {
  544. if (col == COLUMN_FILENAME) {
  545. JFileChooser chooser = getFileChooser();
  546. File f = (File)getValueAt(row, col);
  547. if (f != null) {
  548. String oldDisplayName = chooser.getName(f);
  549. String oldFileName = f.getName();
  550. String newDisplayName = ((String)value).trim();
  551. String newFileName;
  552. if (!newDisplayName.equals(oldDisplayName)) {
  553. newFileName = newDisplayName;
  554. //Check if extension is hidden from user
  555. int i1 = oldFileName.length();
  556. int i2 = oldDisplayName.length();
  557. if (i1 > i2 && oldFileName.charAt(i2) == '.') {
  558. newFileName = newDisplayName + oldFileName.substring(i2);
  559. }
  560. // rename
  561. FileSystemView fsv = chooser.getFileSystemView();
  562. File f2 = fsv.createFileObject(f.getParentFile(), newFileName);
  563. if (!f2.exists() && MetalFileChooserUI.this.getModel().renameFile(f, f2)) {
  564. if (fsv.isParent(chooser.getCurrentDirectory(), f2)) {
  565. if (chooser.isMultiSelectionEnabled()) {
  566. chooser.setSelectedFiles(new File[] { f2 });
  567. } else {
  568. chooser.setSelectedFile(f2);
  569. }
  570. } else {
  571. //Could be because of delay in updating Desktop folder
  572. //chooser.setSelectedFile(null);
  573. }
  574. } else {
  575. // PENDING(jeff) - show a dialog indicating failure
  576. }
  577. }
  578. }
  579. }
  580. }
  581. public boolean isCellEditable(int row, int column) {
  582. return (column == COLUMN_FILENAME);
  583. }
  584. public void contentsChanged(ListDataEvent e) {
  585. fireTableDataChanged();
  586. }
  587. public void intervalAdded(ListDataEvent e) {
  588. fireTableDataChanged();
  589. }
  590. public void intervalRemoved(ListDataEvent e) {
  591. fireTableDataChanged();
  592. }
  593. }
  594. class DetailsTableCellRenderer extends DefaultTableCellRenderer {
  595. JFileChooser chooser;
  596. DateFormat df;
  597. DetailsTableCellRenderer(JFileChooser chooser) {
  598. this.chooser = chooser;
  599. df = DateFormat.getDateTimeInstance(DateFormat.SHORT, DateFormat.SHORT,
  600. chooser.getLocale());
  601. }
  602. public Component getTableCellRendererComponent(JTable table, Object value,
  603. boolean isSelected, boolean hasFocus, int row, int column) {
  604. if (column == COLUMN_FILESIZE || column == COLUMN_FILEATTR) {
  605. setHorizontalAlignment(SwingConstants.TRAILING);
  606. } else {
  607. setHorizontalAlignment(SwingConstants.LEADING);
  608. }
  609. return super.getTableCellRendererComponent(table, value, isSelected, hasFocus, row, column);
  610. }
  611. public void setValue(Object value) {
  612. setIcon(null);
  613. if (value instanceof File) {
  614. File file = (File)value;
  615. String fileName = chooser.getName(file);
  616. setText(fileName);
  617. Icon icon = chooser.getIcon(file);
  618. setIcon(icon);
  619. } else if (value instanceof Date) {
  620. setText((value == null) ? "" : df.format((Date)value));
  621. } else {
  622. super.setValue(value);
  623. }
  624. }
  625. }
  626. protected JPanel createDetailsView(JFileChooser fc) {
  627. final JFileChooser chooser = fc;
  628. JPanel p = new JPanel(new BorderLayout());
  629. DetailsTableModel detailsTableModel = new DetailsTableModel(chooser);
  630. detailsTable = new JTable(detailsTableModel) {
  631. // Handle Escape key events here
  632. protected boolean processKeyBinding(KeyStroke ks, KeyEvent e, int condition, boolean pressed) {
  633. if (e.getKeyCode() == KeyEvent.VK_ESCAPE && getCellEditor() == null) {
  634. // We are not editing, forward to filechooser.
  635. chooser.dispatchEvent(e);
  636. return true;
  637. }
  638. return super.processKeyBinding(ks, e, condition, pressed);
  639. }
  640. };
  641. detailsTable.setComponentOrientation(chooser.getComponentOrientation());
  642. detailsTable.setAutoResizeMode(JTable.AUTO_RESIZE_OFF);
  643. detailsTable.setShowGrid(false);
  644. detailsTable.setSelectionModel(listSelectionModel);
  645. detailsTable.putClientProperty("JTable.autoStartsEdit", Boolean.FALSE);
  646. Font font = detailsTable.getFont();
  647. detailsTable.setRowHeight(Math.max(font.getSize(), 19)+3);
  648. TableColumnModel columnModel = detailsTable.getColumnModel();
  649. TableColumn[] columns = new TableColumn[COLUMN_COLCOUNT];
  650. for (int i = 0; i < COLUMN_COLCOUNT; i++) {
  651. columns[i] = columnModel.getColumn(i);
  652. columns[i].setPreferredWidth(COLUMN_WIDTHS[i]);
  653. }
  654. if (!System.getProperty("os.name").startsWith("Windows")) {
  655. columnModel.removeColumn(columns[COLUMN_FILETYPE]);
  656. columnModel.removeColumn(columns[COLUMN_FILEATTR]);
  657. }
  658. TableCellRenderer cellRenderer = new DetailsTableCellRenderer(chooser);
  659. detailsTable.setDefaultRenderer(File.class, cellRenderer);
  660. detailsTable.setDefaultRenderer(Date.class, cellRenderer);
  661. detailsTable.setDefaultRenderer(Object.class, cellRenderer);
  662. // Install cell editor for editing file name
  663. final JTextField tf = new JTextField();
  664. tf.addFocusListener(editorFocusListener);
  665. columns[COLUMN_FILENAME].setCellEditor(new DefaultCellEditor(tf) {
  666. public boolean isCellEditable(EventObject e) {
  667. if (e instanceof MouseEvent) {
  668. MouseEvent me = (MouseEvent)e;
  669. int index = detailsTable.rowAtPoint(me.getPoint());
  670. return (me.getClickCount() == 1 && detailsTable.isRowSelected(index));
  671. }
  672. return super.isCellEditable(e);
  673. }
  674. public Component getTableCellEditorComponent(JTable table, Object value,
  675. boolean isSelected, int row, int column) {
  676. Component comp = super.getTableCellEditorComponent(table, value, isSelected, row, column);
  677. if (value instanceof File) {
  678. tf.setText(chooser.getName((File)value));
  679. tf.requestFocus();
  680. tf.selectAll();
  681. }
  682. return comp;
  683. }
  684. });
  685. JList fakeList = new JList(detailsTableModel.listModel) {
  686. JTable table = detailsTable;
  687. public int locationToIndex(Point location) {
  688. return table.rowAtPoint(location);
  689. }
  690. public Rectangle getCellBounds(int index0, int index1) {
  691. Rectangle r0 = table.getCellRect(index0, COLUMN_FILENAME, false);
  692. Rectangle r1 = table.getCellRect(index1, COLUMN_FILENAME, false);
  693. return r0.union(r1);
  694. }
  695. public Object getSelectedValue() {
  696. return table.getValueAt(table.getSelectedRow(), COLUMN_FILENAME);
  697. }
  698. public Component add(Component comp) {
  699. if (comp instanceof JTextField) {
  700. return table.add(comp);
  701. } else {
  702. return super.add(comp);
  703. }
  704. }
  705. public void repaint() {
  706. if (table != null)
  707. table.repaint();
  708. }
  709. public TransferHandler getTransferHandler() {
  710. if (table != null) {
  711. return table.getTransferHandler();
  712. } else {
  713. return super.getTransferHandler();
  714. }
  715. }
  716. public void setTransferHandler(TransferHandler newHandler) {
  717. if (table != null) {
  718. table.setTransferHandler(newHandler);
  719. } else {
  720. super.setTransferHandler(newHandler);
  721. }
  722. }
  723. public boolean getDragEnabled() {
  724. if (table != null) {
  725. return table.getDragEnabled();
  726. } else {
  727. return super.getDragEnabled();
  728. }
  729. }
  730. public void setDragEnabled(boolean b) {
  731. if (table != null) {
  732. table.setDragEnabled(b);
  733. } else {
  734. super.setDragEnabled(b);
  735. }
  736. }
  737. };
  738. fakeList.setSelectionModel(listSelectionModel);
  739. detailsTable.addMouseListener(createDoubleClickListener(chooser, fakeList));
  740. //detailsTable.addMouseListener(createSingleClickListener(chooser, fakeList));
  741. JScrollPane scrollpane = new JScrollPane(detailsTable);
  742. scrollpane.setComponentOrientation(chooser.getComponentOrientation());
  743. LookAndFeel.installColors(scrollpane.getViewport(), "Table.background", "Table.foreground");
  744. // Adjust width of first column so the table fills the viewport when
  745. // first displayed (temporary listener).
  746. scrollpane.addComponentListener(new ComponentAdapter() {
  747. public void componentResized(ComponentEvent e) {
  748. JScrollPane sp = (JScrollPane)e.getComponent();
  749. fixNameColumnWidth(sp.getViewport().getSize().width);
  750. sp.removeComponentListener(this);
  751. }
  752. });
  753. p.add(scrollpane, BorderLayout.CENTER);
  754. return p;
  755. }
  756. private void fixNameColumnWidth(int viewWidth) {
  757. TableColumn nameCol = detailsTable.getColumnModel().getColumn(COLUMN_FILENAME);
  758. int tableWidth = detailsTable.getPreferredSize().width;
  759. if (tableWidth < viewWidth) {
  760. nameCol.setPreferredWidth(nameCol.getPreferredWidth() + viewWidth - tableWidth);
  761. }
  762. }
  763. private class DelayedSelectionUpdater implements Runnable {
  764. DelayedSelectionUpdater() {
  765. SwingUtilities.invokeLater(this);
  766. }
  767. public void run() {
  768. setFileSelected();
  769. }
  770. }
  771. /**
  772. * Creates a selection listener for the list of files and directories.
  773. *
  774. * @param fc a <code>JFileChooser</code>
  775. * @return a <code>ListSelectionListener</code>
  776. */
  777. public ListSelectionListener createListSelectionListener(JFileChooser fc) {
  778. return new SelectionListener() {
  779. public void valueChanged(ListSelectionEvent e) {
  780. if (!e.getValueIsAdjusting()) {
  781. JFileChooser chooser = getFileChooser();
  782. FileSystemView fsv = chooser.getFileSystemView();
  783. JList list = (JList) e.getSource();
  784. if (chooser.isMultiSelectionEnabled()) {
  785. File[] files = null;
  786. Object[] objects = list.getSelectedValues();
  787. if (objects != null) {
  788. if (objects.length == 1
  789. && ((File)objects[0]).isDirectory()
  790. && chooser.isTraversable(((File)objects[0]))
  791. && (chooser.getFileSelectionMode() == chooser.FILES_ONLY
  792. || !fsv.isFileSystem(((File)objects[0])))) {
  793. setDirectorySelected(true);
  794. setDirectory(((File)objects[0]));
  795. } else {
  796. files = new File[objects.length];
  797. int j = 0;
  798. for (int i = 0; i < objects.length; i++) {
  799. File f = (File)objects[i];
  800. boolean isDir = f.isDirectory();
  801. boolean isFile = ShellFolder.disableFileChooserSpeedFix() ? f.isFile() : !isDir;
  802. if ((chooser.isFileSelectionEnabled() && isFile)
  803. || (chooser.isDirectorySelectionEnabled()
  804. && fsv.isFileSystem(f)
  805. && isDir)) {
  806. files[j++] = f;
  807. }
  808. }
  809. if (j == 0) {
  810. files = null;
  811. } else if (j < objects.length) {
  812. File[] tmpFiles = new File[j];
  813. System.arraycopy(files, 0, tmpFiles, 0, j);
  814. files = tmpFiles;
  815. }
  816. setDirectorySelected(false);
  817. }
  818. }
  819. chooser.setSelectedFiles(files);
  820. } else {
  821. File file = (File)list.getSelectedValue();
  822. if (file != null
  823. && file.isDirectory()
  824. && chooser.isTraversable(file)
  825. && (chooser.getFileSelectionMode() == chooser.FILES_ONLY
  826. || !fsv.isFileSystem(file))) {
  827. setDirectorySelected(true);
  828. setDirectory(file);
  829. chooser.setSelectedFile(null);
  830. } else {
  831. setDirectorySelected(false);
  832. if (file != null) {
  833. chooser.setSelectedFile(file);
  834. }
  835. }
  836. }
  837. }
  838. }
  839. };
  840. }
  841. private MouseListener createSingleClickListener(JFileChooser fc, JList list) {
  842. return new SingleClickListener(list);
  843. }
  844. int lastIndex = -1;
  845. File editFile = null;
  846. int editX = 20;
  847. private int getEditIndex() {
  848. return lastIndex;
  849. }
  850. private void setEditIndex(int i) {
  851. lastIndex = i;
  852. }
  853. private void resetEditIndex() {
  854. lastIndex = -1;
  855. }
  856. private void cancelEdit() {
  857. if (editFile != null) {
  858. editFile = null;
  859. list.remove(editCell);
  860. centerPanel.repaint();
  861. } else if (detailsTable != null && detailsTable.isEditing()) {
  862. detailsTable.getCellEditor().cancelCellEditing();
  863. }
  864. }
  865. JTextField editCell = null;
  866. private void editFileName(int index) {
  867. ensureIndexIsVisible(index);
  868. if (listViewPanel.isVisible()) {
  869. editFile = (File)getModel().getElementAt(index);
  870. Rectangle r = list.getCellBounds(index, index);
  871. if (editCell == null) {
  872. editCell = new JTextField();
  873. editCell.addActionListener(new EditActionListener());
  874. editCell.addFocusListener(editorFocusListener);
  875. editCell.setNextFocusableComponent(list);
  876. }
  877. list.add(editCell);
  878. editCell.setText(getFileChooser().getName(editFile));
  879. if (list.getComponentOrientation().isLeftToRight()) {
  880. editCell.setBounds(editX + r.x, r.y, r.width - editX, r.height);
  881. } else {
  882. editCell.setBounds(r.x, r.y, r.width - editX, r.height);
  883. }
  884. editCell.requestFocus();
  885. editCell.selectAll();
  886. } else if (detailsViewPanel.isVisible()) {
  887. detailsTable.editCellAt(index, COLUMN_FILENAME);
  888. }
  889. }
  890. protected class SingleClickListener extends MouseAdapter {
  891. JList list;
  892. public SingleClickListener(JList list) {
  893. this.list = list;
  894. }
  895. public void mouseClicked(MouseEvent e) {
  896. if (SwingUtilities.isLeftMouseButton(e)) {
  897. if (e.getClickCount() == 1) {
  898. JFileChooser fc = getFileChooser();
  899. int index = list.locationToIndex(e.getPoint());
  900. if ((!fc.isMultiSelectionEnabled() || fc.getSelectedFiles().length <= 1)
  901. && index >= 0 && list.isSelectedIndex(index)
  902. && getEditIndex() == index && editFile == null) {
  903. editFileName(index);
  904. } else {
  905. if (index >= 0) {
  906. setEditIndex(index);
  907. } else {
  908. resetEditIndex();
  909. }
  910. }
  911. } else {
  912. // on double click (open or drill down one directory) be
  913. // sure to clear the edit index
  914. resetEditIndex();
  915. }
  916. }
  917. }
  918. }
  919. class EditActionListener implements ActionListener {
  920. public void actionPerformed(ActionEvent e) {
  921. applyEdit();
  922. }
  923. }
  924. private void applyEdit() {
  925. if (editFile != null && editFile.exists()) {
  926. JFileChooser chooser = getFileChooser();
  927. String oldDisplayName = chooser.getName(editFile);
  928. String oldFileName = editFile.getName();
  929. String newDisplayName = editCell.getText().trim();
  930. String newFileName;
  931. if (!newDisplayName.equals(oldDisplayName)) {
  932. newFileName = newDisplayName;
  933. //Check if extension is hidden from user
  934. int i1 = oldFileName.length();
  935. int i2 = oldDisplayName.length();
  936. if (i1 > i2 && oldFileName.charAt(i2) == '.') {
  937. newFileName = newDisplayName + oldFileName.substring(i2);
  938. }
  939. // rename
  940. FileSystemView fsv = chooser.getFileSystemView();
  941. File f2 = fsv.createFileObject(editFile.getParentFile(), newFileName);
  942. if (!f2.exists() && getModel().renameFile(editFile, f2)) {
  943. if (fsv.isParent(chooser.getCurrentDirectory(), f2)) {
  944. if (chooser.isMultiSelectionEnabled()) {
  945. chooser.setSelectedFiles(new File[] { f2 });
  946. } else {
  947. chooser.setSelectedFile(f2);
  948. }
  949. } else {
  950. //Could be because of delay in updating Desktop folder
  951. //chooser.setSelectedFile(null);
  952. }
  953. } else {
  954. // PENDING(jeff) - show a dialog indicating failure
  955. }
  956. }
  957. }
  958. if (detailsTable != null && detailsTable.isEditing()) {
  959. detailsTable.getCellEditor().stopCellEditing();
  960. }
  961. cancelEdit();
  962. }
  963. protected class FileRenderer extends DefaultListCellRenderer {
  964. public Component getListCellRendererComponent(JList list, Object value,
  965. int index, boolean isSelected,
  966. boolean cellHasFocus) {
  967. super.getListCellRendererComponent(list, value, index, isSelected, cellHasFocus);
  968. File file = (File) value;
  969. String fileName = getFileChooser().getName(file);
  970. setText(fileName);
  971. Icon icon = getFileChooser().getIcon(file);
  972. setIcon(icon);
  973. if(isSelected) {
  974. // PENDING(jeff) - grab padding (4) below from defaults table.
  975. editX = icon.getIconWidth() + 4;
  976. }
  977. return this;
  978. }
  979. }
  980. public void uninstallUI(JComponent c) {
  981. // Remove listeners
  982. c.removePropertyChangeListener(filterComboBoxModel);
  983. cancelButton.removeActionListener(getCancelSelectionAction());
  984. approveButton.removeActionListener(getApproveSelectionAction());
  985. fileNameTextField.removeActionListener(getApproveSelectionAction());
  986. super.uninstallUI(c);
  987. }
  988. /**
  989. * Returns the preferred size of the specified
  990. * <code>JFileChooser</code>.
  991. * The preferred size is at least as large,
  992. * in both height and width,
  993. * as the preferred size recommended
  994. * by the file chooser's layout manager.
  995. *
  996. * @param c a <code>JFileChooser</code>
  997. * @return a <code>Dimension</code> specifying the preferred
  998. * width and height of the file chooser
  999. */
  1000. public Dimension getPreferredSize(JComponent c) {
  1001. int prefWidth = PREF_SIZE.width;
  1002. Dimension d = c.getLayout().preferredLayoutSize(c);
  1003. if (d != null) {
  1004. return new Dimension(d.width < prefWidth ? prefWidth : d.width,
  1005. d.height < PREF_SIZE.height ? PREF_SIZE.height : d.height);
  1006. } else {
  1007. return new Dimension(prefWidth, PREF_SIZE.height);
  1008. }
  1009. }
  1010. /**
  1011. * Returns the minimum size of the <code>JFileChooser</code>.
  1012. *
  1013. * @param c a <code>JFileChooser</code>
  1014. * @return a <code>Dimension</code> specifying the minimum
  1015. * width and height of the file chooser
  1016. */
  1017. public Dimension getMinimumSize(JComponent c) {
  1018. return MIN_SIZE;
  1019. }
  1020. /**
  1021. * Returns the maximum size of the <code>JFileChooser</code>.
  1022. *
  1023. * @param c a <code>JFileChooser</code>
  1024. * @return a <code>Dimension</code> specifying the maximum
  1025. * width and height of the file chooser
  1026. */
  1027. public Dimension getMaximumSize(JComponent c) {
  1028. return new Dimension(Integer.MAX_VALUE, Integer.MAX_VALUE);
  1029. }
  1030. void setFileSelected() {
  1031. if (getFileChooser().isMultiSelectionEnabled() && !isDirectorySelected()) {
  1032. File[] files = getFileChooser().getSelectedFiles(); // Should be selected
  1033. Object[] selectedObjects = list.getSelectedValues(); // Are actually selected
  1034. if (ShellFolder.disableFileChooserSpeedFix()) {
  1035. // Remove files that shouldn't be selected
  1036. for (int j = 0; j < selectedObjects.length; j++) {
  1037. boolean found = false;
  1038. for (int i = 0; i < files.length; i++) {
  1039. if (files[i].equals(selectedObjects[j])) {
  1040. found = true;
  1041. break;
  1042. }
  1043. }
  1044. if (!found) {
  1045. int index = getModel().indexOf(selectedObjects[j]);
  1046. if (index >= 0) {
  1047. listSelectionModel.removeSelectionInterval(index, index);
  1048. }
  1049. }
  1050. }
  1051. // Add files that should be selected
  1052. for (int i = 0; i < files.length; i++) {
  1053. boolean found = false;
  1054. for (int j = 0; j < selectedObjects.length; j++) {
  1055. if (files[i].equals(selectedObjects[j])) {
  1056. found = true;
  1057. break;
  1058. }
  1059. }
  1060. if (!found) {
  1061. int index = getModel().indexOf(files[i]);
  1062. if (index >= 0) {
  1063. listSelectionModel.addSelectionInterval(index, index);
  1064. }
  1065. }
  1066. }
  1067. } else {
  1068. listSelectionModel.setValueIsAdjusting(true);
  1069. try {
  1070. Arrays.sort(files);
  1071. Arrays.sort(selectedObjects);
  1072. int shouldIndex = 0;
  1073. int actuallyIndex = 0;
  1074. // Remove files that shouldn't be selected and add files which should be selected
  1075. // Note: Assume files are already sorted in compareTo order.
  1076. while (shouldIndex < files.length &&
  1077. actuallyIndex < selectedObjects.length) {
  1078. int comparison = files[shouldIndex].compareTo(selectedObjects[actuallyIndex]);
  1079. if (comparison < 0) {
  1080. int index = getModel().indexOf(files[shouldIndex]);
  1081. listSelectionModel.addSelectionInterval(index, index);
  1082. shouldIndex++;
  1083. } else if (comparison > 0) {
  1084. int index = getModel().indexOf(selectedObjects[actuallyIndex]);
  1085. listSelectionModel.removeSelectionInterval(index, index);
  1086. actuallyIndex++;
  1087. } else {
  1088. // Do nothing
  1089. shouldIndex++;
  1090. actuallyIndex++;
  1091. }
  1092. }
  1093. while (shouldIndex < files.length) {
  1094. int index = getModel().indexOf(files[shouldIndex]);
  1095. listSelectionModel.addSelectionInterval(index, index);
  1096. shouldIndex++;
  1097. }
  1098. while (actuallyIndex < selectedObjects.length) {
  1099. int index = getModel().indexOf(selectedObjects[actuallyIndex]);
  1100. listSelectionModel.removeSelectionInterval(index, index);
  1101. actuallyIndex++;
  1102. }
  1103. } finally {
  1104. listSelectionModel.setValueIsAdjusting(false);
  1105. }
  1106. }
  1107. } else {
  1108. JFileChooser chooser = getFileChooser();
  1109. File f = null;
  1110. if (isDirectorySelected()) {
  1111. f = getDirectory();
  1112. } else {
  1113. f = chooser.getSelectedFile();
  1114. }
  1115. int i;
  1116. if (f != null && (i = getModel().indexOf(f)) >= 0) {
  1117. listSelectionModel.setSelectionInterval(i, i);
  1118. ensureIndexIsVisible(i);
  1119. } else {
  1120. listSelectionModel.clearSelection();
  1121. }
  1122. }
  1123. }
  1124. private String fileNameString(File file) {
  1125. if (file == null) {
  1126. return null;
  1127. } else {
  1128. JFileChooser fc = getFileChooser();
  1129. if (fc.isDirectorySelectionEnabled() && !fc.isFileSelectionEnabled()) {
  1130. return file.getPath();
  1131. } else {
  1132. return file.getName();
  1133. }
  1134. }
  1135. }
  1136. private String fileNameString(File[] files) {
  1137. StringBuffer buf = new StringBuffer();
  1138. for (int i = 0; files != null && i < files.length; i++) {
  1139. if (i > 0) {
  1140. buf.append(" ");
  1141. }
  1142. if (files.length > 1) {
  1143. buf.append("\"");
  1144. }
  1145. buf.append(fileNameString(files[i]));
  1146. if (files.length > 1) {
  1147. buf.append("\"");
  1148. }
  1149. }
  1150. return buf.toString();
  1151. }
  1152. /* The following methods are used by the PropertyChange Listener */
  1153. private void doSelectedFileChanged(PropertyChangeEvent e) {
  1154. applyEdit();
  1155. File f = (File) e.getNewValue();
  1156. JFileChooser fc = getFileChooser();
  1157. if (f != null
  1158. && ((fc.isFileSelectionEnabled() && !f.isDirectory())
  1159. || (f.isDirectory() && fc.isDirectorySelectionEnabled()))) {
  1160. setFileName(fileNameString(f));
  1161. setFileSelected();
  1162. }
  1163. }
  1164. private void doSelectedFilesChanged(PropertyChangeEvent e) {
  1165. applyEdit();
  1166. File[] files = (File[]) e.getNewValue();
  1167. JFileChooser fc = getFileChooser();
  1168. if (files != null
  1169. && files.length > 0
  1170. && (files.length > 1 || fc.isDirectorySelectionEnabled() || !files[0].isDirectory())) {
  1171. setFileName(fileNameString(files));
  1172. setFileSelected();
  1173. }
  1174. }
  1175. private void doDirectoryChanged(PropertyChangeEvent e) {
  1176. JFileChooser fc = getFileChooser();
  1177. FileSystemView fsv = fc.getFileSystemView();
  1178. applyEdit();
  1179. resetEditIndex();
  1180. clearIconCache();
  1181. listSelectionModel.clearSelection();
  1182. ensureIndexIsVisible(0);
  1183. File currentDirectory = fc.getCurrentDirectory();
  1184. if(currentDirectory != null) {
  1185. directoryComboBoxModel.addItem(currentDirectory);
  1186. getNewFolderAction().setEnabled(currentDirectory.canWrite());
  1187. getChangeToParentDirectoryAction().setEnabled(!fsv.isRoot(currentDirectory));
  1188. if (fc.isDirectorySelectionEnabled() && !fc.isFileSelectionEnabled()) {
  1189. if (fsv.isFileSystem(currentDirectory)) {
  1190. setFileName(currentDirectory.getPath());
  1191. } else {
  1192. setFileName(null);
  1193. }
  1194. }
  1195. }
  1196. }
  1197. private void doFilterChanged(PropertyChangeEvent e) {
  1198. applyEdit();
  1199. resetEditIndex();
  1200. clearIconCache();
  1201. listSelectionModel.clearSelection();
  1202. }
  1203. private void doFileSelectionModeChanged(PropertyChangeEvent e) {
  1204. applyEdit();
  1205. resetEditIndex();
  1206. clearIconCache();
  1207. listSelectionModel.clearSelection();
  1208. JFileChooser fc = getFileChooser();
  1209. File currentDirectory = fc.getCurrentDirectory();
  1210. if (currentDirectory != null
  1211. && fc.isDirectorySelectionEnabled()
  1212. && !fc.isFileSelectionEnabled()
  1213. && fc.getFileSystemView().isFileSystem(currentDirectory)) {
  1214. setFileName(currentDirectory.getPath());
  1215. } else {
  1216. setFileName(null);
  1217. }
  1218. }
  1219. private void doMultiSelectionChanged(PropertyChangeEvent e) {
  1220. if (getFileChooser().isMultiSelectionEnabled()) {
  1221. listSelectionModel.setSelectionMode(ListSelectionModel.MULTIPLE_INTERVAL_SELECTION);
  1222. } else {
  1223. listSelectionModel.setSelectionMode(ListSelectionModel.SINGLE_SELECTION);
  1224. listSelectionModel.clearSelection();
  1225. getFileChooser().setSelectedFiles(null);
  1226. }
  1227. }
  1228. private void doAccessoryChanged(PropertyChangeEvent e) {
  1229. if(getAccessoryPanel() != null) {
  1230. if(e.getOldValue() != null) {
  1231. getAccessoryPanel().remove((JComponent) e.getOldValue());
  1232. }
  1233. JComponent accessory = (JComponent) e.getNewValue();
  1234. if(accessory != null) {
  1235. getAccessoryPanel().add(accessory, BorderLayout.CENTER);
  1236. }
  1237. }
  1238. }
  1239. private void doApproveButtonTextChanged(PropertyChangeEvent e) {
  1240. JFileChooser chooser = getFileChooser();
  1241. approveButton.setText(getApproveButtonText(chooser));
  1242. approveButton.setToolTipText(getApproveButtonToolTipText(chooser));
  1243. }
  1244. private void doDialogTypeChanged(PropertyChangeEvent e) {
  1245. JFileChooser chooser = getFileChooser();
  1246. approveButton.setText(getApproveButtonText(chooser));
  1247. approveButton.setToolTipText(getApproveButtonToolTipText(chooser));
  1248. if (chooser.getDialogType() == JFileChooser.SAVE_DIALOG) {
  1249. lookInLabel.setText(saveInLabelText);
  1250. } else {
  1251. lookInLabel.setText(lookInLabelText);
  1252. }
  1253. }
  1254. private void doApproveButtonMnemonicChanged(PropertyChangeEvent e) {
  1255. // Note: Metal does not use mnemonics for approve and cancel
  1256. }
  1257. private void doControlButtonsChanged(PropertyChangeEvent e) {
  1258. if(getFileChooser().getControlButtonsAreShown()) {
  1259. addControlButtons();
  1260. } else {
  1261. removeControlButtons();
  1262. }
  1263. }
  1264. /*
  1265. * Listen for filechooser property changes, such as
  1266. * the selected file changing, or the type of the dialog changing.
  1267. */
  1268. public PropertyChangeListener createPropertyChangeListener(JFileChooser fc) {
  1269. return new PropertyChangeListener() {
  1270. public void propertyChange(PropertyChangeEvent e) {
  1271. String s = e.getPropertyName();
  1272. if(s.equals(JFileChooser.SELECTED_FILE_CHANGED_PROPERTY)) {
  1273. doSelectedFileChanged(e);
  1274. } else if (s.equals(JFileChooser.SELECTED_FILES_CHANGED_PROPERTY)) {
  1275. doSelectedFilesChanged(e);
  1276. } else if(s.equals(JFileChooser.DIRECTORY_CHANGED_PROPERTY)) {
  1277. doDirectoryChanged(e);
  1278. } else if(s.equals(JFileChooser.FILE_FILTER_CHANGED_PROPERTY)) {
  1279. doFilterChanged(e);
  1280. } else if(s.equals(JFileChooser.FILE_SELECTION_MODE_CHANGED_PROPERTY)) {
  1281. doFileSelectionModeChanged(e);
  1282. } else if(s.equals(JFileChooser.MULTI_SELECTION_ENABLED_CHANGED_PROPERTY)) {
  1283. doMultiSelectionChanged(e);
  1284. } else if(s.equals(JFileChooser.ACCESSORY_CHANGED_PROPERTY)) {
  1285. doAccessoryChanged(e);
  1286. } else if (s.equals(JFileChooser.APPROVE_BUTTON_TEXT_CHANGED_PROPERTY) ||
  1287. s.equals(JFileChooser.APPROVE_BUTTON_TOOL_TIP_TEXT_CHANGED_PROPERTY)) {
  1288. doApproveButtonTextChanged(e);
  1289. } else if(s.equals(JFileChooser.DIALOG_TYPE_CHANGED_PROPERTY)) {
  1290. doDialogTypeChanged(e);
  1291. } else if(s.equals(JFileChooser.APPROVE_BUTTON_MNEMONIC_CHANGED_PROPERTY)) {
  1292. doApproveButtonMnemonicChanged(e);
  1293. } else if(s.equals(JFileChooser.CONTROL_BUTTONS_ARE_SHOWN_CHANGED_PROPERTY)) {
  1294. doControlButtonsChanged(e);
  1295. } else if(s.equals(JFileChooser.CANCEL_SELECTION)) {
  1296. applyEdit();
  1297. } else if (s.equals("componentOrientation")) {
  1298. ComponentOrientation o = (ComponentOrientation)e.getNewValue();
  1299. JFileChooser cc = (JFileChooser)e.getSource();
  1300. if (o != (ComponentOrientation)e.getOldValue()) {
  1301. cc.applyComponentOrientation(o);
  1302. }
  1303. if (detailsTable != null) {
  1304. detailsTable.setComponentOrientation(o);
  1305. detailsTable.getParent().getParent().setComponentOrientation(o);
  1306. }
  1307. } else if (s == "FileChooser.useShellFolder") {
  1308. updateUseShellFolder();
  1309. doDirectoryChanged(e);
  1310. } else if (s.equals("ancestor")) {
  1311. if (e.getOldValue() == null && e.getNewValue() != null) {
  1312. // Ancestor was added, set initial focus
  1313. fileNameTextField.selectAll();
  1314. fileNameTextField.requestFocus();
  1315. }
  1316. }
  1317. }
  1318. };
  1319. }
  1320. protected void removeControlButtons() {
  1321. getBottomPanel().remove(getButtonPanel());
  1322. }
  1323. protected void addControlButtons() {
  1324. getBottomPanel().add(getButtonPanel());
  1325. }
  1326. private void ensureIndexIsVisible(int i) {
  1327. if (i >= 0) {
  1328. list.ensureIndexIsVisible(i);
  1329. if (detailsTable != null) {
  1330. detailsTable.scrollRectToVisible(detailsTable.getCellRect(i, COLUMN_FILENAME, true));
  1331. }
  1332. }
  1333. }
  1334. public void ensureFileIsVisible(JFileChooser fc, File f) {
  1335. ensureIndexIsVisible(getModel().indexOf(f));
  1336. }
  1337. public void rescanCurrentDirectory(JFileChooser fc) {
  1338. getModel().validateFileCache();
  1339. }
  1340. public String getFileName() {
  1341. if (fileNameTextField != null) {
  1342. return fileNameTextField.getText();
  1343. } else {
  1344. return null;
  1345. }
  1346. }
  1347. public void setFileName(String filename) {
  1348. if (fileNameTextField != null) {
  1349. fileNameTextField.setText(filename);
  1350. }
  1351. }
  1352. /**
  1353. * Property to remember whether a directory is currently selected in the UI.
  1354. * This is normally called by the UI on a selection event.
  1355. *
  1356. * @param directorySelected if a directory is currently selected.
  1357. * @since 1.4
  1358. */
  1359. protected void setDirectorySelected(boolean directorySelected) {
  1360. super.setDirectorySelected(directorySelected);
  1361. JFileChooser chooser = getFileChooser();
  1362. if(directorySelected) {
  1363. approveButton.setText(directoryOpenButtonText);
  1364. approveButton.setToolTipText(directoryOpenButtonToolTipText);
  1365. } else {
  1366. approveButton.setText(getApproveButtonText(chooser));
  1367. approveButton.setToolTipText(getApproveButtonToolTipText(chooser));
  1368. }
  1369. }
  1370. public String getDirectoryName() {
  1371. // PENDING(jeff) - get the name from the directory combobox
  1372. return null;
  1373. }
  1374. public void setDirectoryName(String dirname) {
  1375. // PENDING(jeff) - set the name in the directory combobox
  1376. }
  1377. protected DirectoryComboBoxRenderer createDirectoryComboBoxRenderer(JFileChooser fc) {
  1378. return new DirectoryComboBoxRenderer();
  1379. }
  1380. //
  1381. // Renderer for DirectoryComboBox
  1382. //
  1383. class DirectoryComboBoxRenderer extends DefaultListCellRenderer {
  1384. IndentIcon ii = new IndentIcon();
  1385. public Component getListCellRendererComponent(JList list, Object value,
  1386. int index, boolean isSelected,
  1387. boolean cellHasFocus) {
  1388. super.getListCellRendererComponent(list, value, index, isSelected, cellHasFocus);
  1389. if (value == null) {
  1390. setText("");
  1391. return this;
  1392. }
  1393. File directory = (File)value;
  1394. setText(getFileChooser().getName(directory));
  1395. Icon icon = getFileChooser().getIcon(directory);
  1396. ii.icon = icon;
  1397. ii.depth = directoryComboBoxModel.getDepth(index);
  1398. setIcon(ii);
  1399. return this;
  1400. }
  1401. }
  1402. final static int space = 10;
  1403. class IndentIcon implements Icon {
  1404. Icon icon = null;
  1405. int depth = 0;
  1406. public void paintIcon(Component c, Graphics g, int x, int y) {
  1407. if (c.getComponentOrientation().isLeftToRight()) {
  1408. icon.paintIcon(c, g, x+depth*space, y);
  1409. } else {
  1410. icon.paintIcon(c, g, x, y);
  1411. }
  1412. }
  1413. public int getIconWidth() {
  1414. return icon.getIconWidth() + depth*space;
  1415. }
  1416. public int getIconHeight() {
  1417. return icon.getIconHeight();
  1418. }
  1419. }
  1420. //
  1421. // DataModel for DirectoryComboxbox
  1422. //
  1423. protected DirectoryComboBoxModel createDirectoryComboBoxModel(JFileChooser fc) {
  1424. return new DirectoryComboBoxModel();
  1425. }
  1426. /**
  1427. * Data model for a type-face selection combo-box.
  1428. */
  1429. protected class DirectoryComboBoxModel extends AbstractListModel implements ComboBoxModel {
  1430. Vector directories = new Vector();
  1431. int[] depths = null;
  1432. File selectedDirectory = null;
  1433. JFileChooser chooser = getFileChooser();
  1434. FileSystemView fsv = chooser.getFileSystemView();
  1435. public DirectoryComboBoxModel() {
  1436. // Add the current directory to the model, and make it the
  1437. // selectedDirectory
  1438. File dir = getFileChooser().getCurrentDirectory();
  1439. if(dir != null) {
  1440. addItem(dir);
  1441. }
  1442. }
  1443. /**
  1444. * Adds the directory to the model and sets it to be selected,
  1445. * additionally clears out the previous selected directory and
  1446. * the paths leading up to it, if any.
  1447. */
  1448. private void addItem(File directory) {
  1449. if(directory == null) {
  1450. return;
  1451. }
  1452. directories.clear();
  1453. File[] baseFolders;
  1454. if (useShellFolder) {
  1455. baseFolders = (File[])ShellFolder.get("fileChooserComboBoxFolders");
  1456. } else {
  1457. baseFolders = fsv.getRoots();
  1458. }
  1459. directories.addAll(Arrays.asList(baseFolders));
  1460. // Get the canonical (full) path. This has the side
  1461. // benefit of removing extraneous chars from the path,
  1462. // for example /foo/bar/ becomes /foo/bar
  1463. File canonical = null;
  1464. try {
  1465. canonical = directory.getCanonicalFile();
  1466. } catch (IOException e) {
  1467. // Maybe drive is not ready. Can't abort here.
  1468. canonical = directory;
  1469. }
  1470. // create File instances of each directory leading up to the top
  1471. try {
  1472. File sf = useShellFolder ? ShellFolder.getShellFolder(canonical)
  1473. : canonical;
  1474. File f = sf;
  1475. Vector path = new Vector(10);
  1476. do {
  1477. path.addElement(f);
  1478. } while ((f = f.getParentFile()) != null);
  1479. int pathCount = path.size();
  1480. // Insert chain at appropriate place in vector
  1481. for (int i = 0; i < pathCount; i++) {
  1482. f = (File)path.get(i);
  1483. if (directories.contains(f)) {
  1484. int topIndex = directories.indexOf(f);
  1485. for (int j = i-1; j >= 0; j--) {
  1486. directories.insertElementAt(path.get(j), topIndex+i-j);
  1487. }
  1488. break;
  1489. }
  1490. }
  1491. calculateDepths();
  1492. setSelectedItem(sf);
  1493. } catch (FileNotFoundException ex) {
  1494. calculateDepths();
  1495. }
  1496. }
  1497. private void calculateDepths() {
  1498. depths = new int[directories.size()];
  1499. for (int i = 0; i < depths.length; i++) {
  1500. File dir = (File)directories.get(i);
  1501. File parent = dir.getParentFile();
  1502. depths[i] = 0;
  1503. if (parent != null) {
  1504. for (int j = i-1; j >= 0; j--) {
  1505. if (parent.equals((File)directories.get(j))) {
  1506. depths[i] = depths[j] + 1;
  1507. break;
  1508. }
  1509. }
  1510. }
  1511. }
  1512. }
  1513. public int getDepth(int i) {
  1514. return (depths != null && i >= 0 && i < depths.length) ? depths[i] : 0;
  1515. }
  1516. public void setSelectedItem(Object selectedDirectory) {
  1517. this.selectedDirectory = (File)selectedDirectory;
  1518. fireContentsChanged(this, -1, -1);
  1519. }
  1520. public Object getSelectedItem() {
  1521. return selectedDirectory;
  1522. }
  1523. public int getSize() {
  1524. return directories.size();
  1525. }
  1526. public Object getElementAt(int index) {
  1527. return directories.elementAt(index);
  1528. }
  1529. }
  1530. //
  1531. // Renderer for Types ComboBox
  1532. //
  1533. protected FilterComboBoxRenderer createFilterComboBoxRenderer() {
  1534. return new FilterComboBoxRenderer();
  1535. }
  1536. /**
  1537. * Render different type sizes and styles.
  1538. */
  1539. public class FilterComboBoxRenderer extends DefaultListCellRenderer {
  1540. public Component getListCellRendererComponent(JList list,
  1541. Object value, int index, boolean isSelected,
  1542. boolean cellHasFocus) {
  1543. super.getListCellRendererComponent(list, value, index, isSelected, cellHasFocus);
  1544. if (value != null && value instanceof FileFilter) {
  1545. setText(((FileFilter)value).getDescription());
  1546. }
  1547. return this;
  1548. }
  1549. }
  1550. //
  1551. // DataModel for Types Comboxbox
  1552. //
  1553. protected FilterComboBoxModel createFilterComboBoxModel() {
  1554. return new FilterComboBoxModel();
  1555. }
  1556. /**
  1557. * Data model for a type-face selection combo-box.
  1558. */
  1559. protected class FilterComboBoxModel extends AbstractListModel implements ComboBoxModel, PropertyChangeListener {
  1560. protected FileFilter[] filters;
  1561. protected FilterComboBoxModel() {
  1562. super();
  1563. filters = getFileChooser().getChoosableFileFilters();
  1564. }
  1565. public void propertyChange(PropertyChangeEvent e) {
  1566. String prop = e.getPropertyName();
  1567. if(prop == JFileChooser.CHOOSABLE_FILE_FILTER_CHANGED_PROPERTY) {
  1568. filters = (FileFilter[]) e.getNewValue();
  1569. fireContentsChanged(this, -1, -1);
  1570. } else if (prop == JFileChooser.FILE_FILTER_CHANGED_PROPERTY) {
  1571. fireContentsChanged(this, -1, -1);
  1572. }
  1573. }
  1574. public void setSelectedItem(Object filter) {
  1575. if(filter != null) {
  1576. getFileChooser().setFileFilter((FileFilter) filter);
  1577. setFileName(null);
  1578. fireContentsChanged(this, -1, -1);
  1579. }
  1580. }
  1581. public Object getSelectedItem() {
  1582. // Ensure that the current filter is in the list.
  1583. // NOTE: we shouldnt' have to do this, since JFileChooser adds
  1584. // the filter to the choosable filters list when the filter
  1585. // is set. Lets be paranoid just in case someone overrides
  1586. // setFileFilter in JFileChooser.
  1587. FileFilter currentFilter = getFileChooser().getFileFilter();
  1588. boolean found = false;
  1589. if(currentFilter != null) {
  1590. for(int i=0; i < filters.length; i++) {
  1591. if(filters[i] == currentFilter) {
  1592. found = true;
  1593. }
  1594. }
  1595. if(found == false) {
  1596. getFileChooser().addChoosableFileFilter(currentFilter);
  1597. }
  1598. }
  1599. return getFileChooser().getFileFilter();
  1600. }
  1601. public int getSize() {
  1602. if(filters != null) {
  1603. return filters.length;
  1604. } else {
  1605. return 0;
  1606. }
  1607. }
  1608. public Object getElementAt(int index) {
  1609. if(index > getSize() - 1) {
  1610. // This shouldn't happen. Try to recover gracefully.
  1611. return getFileChooser().getFileFilter();
  1612. }
  1613. if(filters != null) {
  1614. return filters[index];
  1615. } else {
  1616. return null;
  1617. }
  1618. }
  1619. }
  1620. public void valueChanged(ListSelectionEvent e) {
  1621. JFileChooser fc = getFileChooser();
  1622. File f = fc.getSelectedFile();
  1623. if (!e.getValueIsAdjusting() && f != null && !getFileChooser().isTraversable(f)) {
  1624. setFileName(fileNameString(f));
  1625. }
  1626. }
  1627. /**
  1628. * Acts when DirectoryComboBox has changed the selected item.
  1629. */
  1630. protected class DirectoryComboBoxAction extends AbstractAction {
  1631. protected DirectoryComboBoxAction() {
  1632. super("DirectoryComboBoxAction");
  1633. }
  1634. public void actionPerformed(ActionEvent e) {
  1635. File f = (File)directoryComboBox.getSelectedItem();
  1636. getFileChooser().setCurrentDirectory(f);
  1637. }
  1638. }
  1639. protected JButton getApproveButton(JFileChooser fc) {
  1640. return approveButton;
  1641. }
  1642. /**
  1643. * <code>ButtonAreaLayout</code> behaves in a similar manner to
  1644. * <code>FlowLayout</code>. It lays out all components from left to
  1645. * right, flushed right. The widths of all components will be set
  1646. * to the largest preferred size width.
  1647. */
  1648. private static class ButtonAreaLayout implements LayoutManager {
  1649. private int hGap = 5;
  1650. private int topMargin = 17;
  1651. public void addLayoutComponent(String string, Component comp) {
  1652. }
  1653. public void layoutContainer(Container container) {
  1654. Component[] children = container.getComponents();
  1655. if (children != null && children.length > 0) {
  1656. int numChildren = children.length;
  1657. Dimension[] sizes = new Dimension[numChildren];
  1658. Insets insets = container.getInsets();
  1659. int yLocation = insets.top + topMargin;
  1660. int maxWidth = 0;
  1661. for (int counter = 0; counter < numChildren; counter++) {
  1662. sizes[counter] = children[counter].getPreferredSize();
  1663. maxWidth = Math.max(maxWidth, sizes[counter].width);
  1664. }
  1665. int xLocation, xOffset;
  1666. if (container.getComponentOrientation().isLeftToRight()) {
  1667. xLocation = container.getSize().width - insets.left - maxWidth;
  1668. xOffset = hGap + maxWidth;
  1669. } else {
  1670. xLocation = insets.left;
  1671. xOffset = -(hGap + maxWidth);
  1672. }
  1673. for (int counter = numChildren - 1; counter >= 0; counter--) {
  1674. children[counter].setBounds(xLocation, yLocation,
  1675. maxWidth, sizes[counter].height);
  1676. xLocation -= xOffset;
  1677. }
  1678. }
  1679. }
  1680. public Dimension minimumLayoutSize(Container c) {
  1681. if (c != null) {
  1682. Component[] children = c.getComponents();
  1683. if (children != null && children.length > 0) {
  1684. int numChildren = children.length;
  1685. int height = 0;
  1686. Insets cInsets = c.getInsets();
  1687. int extraHeight = topMargin + cInsets.top + cInsets.bottom;
  1688. int extraWidth = cInsets.left + cInsets.right;
  1689. int maxWidth = 0;
  1690. for (int counter = 0; counter < numChildren; counter++) {
  1691. Dimension aSize = children[counter].getPreferredSize();
  1692. height = Math.max(height, aSize.height);
  1693. maxWidth = Math.max(maxWidth, aSize.width);
  1694. }
  1695. return new Dimension(extraWidth + numChildren * maxWidth +
  1696. (numChildren - 1) * hGap,
  1697. extraHeight + height);
  1698. }
  1699. }
  1700. return new Dimension(0, 0);
  1701. }
  1702. public Dimension preferredLayoutSize(Container c) {
  1703. return minimumLayoutSize(c);
  1704. }
  1705. public void removeLayoutComponent(Component c) { }
  1706. }
  1707. private static void groupLabels(AlignedLabel[] group) {
  1708. for (int i = 0; i < group.length; i++) {
  1709. group[i].group = group;
  1710. }
  1711. }
  1712. private class AlignedLabel extends JLabel {
  1713. private AlignedLabel[] group;
  1714. private int maxWidth = 0;
  1715. AlignedLabel(String text) {
  1716. super(text);
  1717. setAlignmentX(JComponent.LEFT_ALIGNMENT);
  1718. }
  1719. public Dimension getPreferredSize() {
  1720. Dimension d = super.getPreferredSize();
  1721. // Align the width with all other labels in group.
  1722. return new Dimension(getMaxWidth() + 11, d.height);
  1723. }
  1724. private int getMaxWidth() {
  1725. if (maxWidth == 0 && group != null) {
  1726. int max = 0;
  1727. for (int i = 0; i < group.length; i++) {
  1728. max = Math.max(group[i].getSuperPreferredWidth(), max);
  1729. }
  1730. for (int i = 0; i < group.length; i++) {
  1731. group[i].maxWidth = max;
  1732. }
  1733. }
  1734. return maxWidth;
  1735. }
  1736. private int getSuperPreferredWidth() {
  1737. return super.getPreferredSize().width;
  1738. }
  1739. }
  1740. }