1 /* 2 * Copyright (c) 1998, 2006, Oracle and/or its affiliates. All rights reserved. 3 * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER. 4 * 5 * This code is free software; you can redistribute it and/or modify it 6 * under the terms of the GNU General Public License version 2 only, as 7 * published by the Free Software Foundation. Oracle designates this 8 * particular file as subject to the "Classpath" exception as provided 9 * by Oracle in the LICENSE file that accompanied this code. 10 * 11 * This code is distributed in the hope that it will be useful, but WITHOUT 12 * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or 13 * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License 14 * version 2 for more details (a copy is included in the LICENSE file that 15 * accompanied this code). 16 * 17 * You should have received a copy of the GNU General Public License version 18 * 2 along with this work; if not, write to the Free Software Foundation, 19 * Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA. 20 * 21 * Please contact Oracle, 500 Oracle Parkway, Redwood Shores, CA 94065 USA 22 * or visit www.oracle.com if you need additional information or have any 23 * questions. 24 */ 25 26 package javax.swing.plaf.metal; 27 28 import javax.swing.*; 29 import javax.swing.border.EmptyBorder; 30 import javax.swing.filechooser.*; 31 import javax.swing.event.*; 32 import javax.swing.plaf.*; 33 import javax.swing.plaf.basic.*; 34 import java.awt.*; 35 import java.awt.event.*; 36 import java.beans.*; 37 import java.io.File; 38 import java.io.FileNotFoundException; 39 import java.io.IOException; 40 import java.util.*; 41 import java.security.AccessController; 42 import java.security.PrivilegedAction; 43 import javax.accessibility.*; 44 45 import sun.awt.shell.ShellFolder; 46 import sun.swing.*; 47 48 /** 49 * Metal L&F implementation of a FileChooser. 50 * 51 * @author Jeff Dinkins 52 */ 53 public class MetalFileChooserUI extends BasicFileChooserUI { 54 55 // Much of the Metal UI for JFilechooser is just a copy of 56 // the windows implementation, but using Metal themed buttons, lists, 57 // icons, etc. We are planning a complete rewrite, and hence we've 58 // made most things in this class private. 59 60 private JLabel lookInLabel; 61 private JComboBox directoryComboBox; 62 private DirectoryComboBoxModel directoryComboBoxModel; 63 private Action directoryComboBoxAction = new DirectoryComboBoxAction(); 64 65 private FilterComboBoxModel filterComboBoxModel; 66 67 private JTextField fileNameTextField; 68 69 private FilePane filePane; 70 private JToggleButton listViewButton; 71 private JToggleButton detailsViewButton; 72 73 private boolean useShellFolder; 74 75 private JButton approveButton; 76 private JButton cancelButton; 77 78 private JPanel buttonPanel; 79 private JPanel bottomPanel; 80 81 private JComboBox filterComboBox; 82 83 private static final Dimension hstrut5 = new Dimension(5, 1); 84 private static final Dimension hstrut11 = new Dimension(11, 1); 85 86 private static final Dimension vstrut5 = new Dimension(1, 5); 87 88 private static final Insets shrinkwrap = new Insets(0,0,0,0); 89 90 // Preferred and Minimum sizes for the dialog box 91 private static int PREF_WIDTH = 500; 92 private static int PREF_HEIGHT = 326; 93 private static Dimension PREF_SIZE = new Dimension(PREF_WIDTH, PREF_HEIGHT); 94 95 private static int MIN_WIDTH = 500; 96 private static int MIN_HEIGHT = 326; 97 private static Dimension MIN_SIZE = new Dimension(MIN_WIDTH, MIN_HEIGHT); 98 99 private static int LIST_PREF_WIDTH = 405; 100 private static int LIST_PREF_HEIGHT = 135; 101 private static Dimension LIST_PREF_SIZE = new Dimension(LIST_PREF_WIDTH, LIST_PREF_HEIGHT); 102 103 // Labels, mnemonics, and tooltips (oh my!) 104 private int lookInLabelMnemonic = 0; 105 private String lookInLabelText = null; 106 private String saveInLabelText = null; 107 108 private int fileNameLabelMnemonic = 0; 109 private String fileNameLabelText = null; 110 private int folderNameLabelMnemonic = 0; 111 private String folderNameLabelText = null; 112 113 private int filesOfTypeLabelMnemonic = 0; 114 private String filesOfTypeLabelText = null; 115 116 private String upFolderToolTipText = null; 117 private String upFolderAccessibleName = null; 118 119 private String homeFolderToolTipText = null; 120 private String homeFolderAccessibleName = null; 121 122 private String newFolderToolTipText = null; 123 private String newFolderAccessibleName = null; 124 125 private String listViewButtonToolTipText = null; 126 private String listViewButtonAccessibleName = null; 127 128 private String detailsViewButtonToolTipText = null; 129 private String detailsViewButtonAccessibleName = null; 130 131 private AlignedLabel fileNameLabel; 132 133 private void populateFileNameLabel() { 134 if (getFileChooser().getFileSelectionMode() == JFileChooser.DIRECTORIES_ONLY) { 135 fileNameLabel.setText(folderNameLabelText); 136 fileNameLabel.setDisplayedMnemonic(folderNameLabelMnemonic); 137 } else { 138 fileNameLabel.setText(fileNameLabelText); 139 fileNameLabel.setDisplayedMnemonic(fileNameLabelMnemonic); 140 } 141 } 142 143 // 144 // ComponentUI Interface Implementation methods 145 // 146 public static ComponentUI createUI(JComponent c) { 147 return new MetalFileChooserUI((JFileChooser) c); 148 } 149 150 public MetalFileChooserUI(JFileChooser filechooser) { 151 super(filechooser); 152 } 153 154 public void installUI(JComponent c) { 155 super.installUI(c); 156 } 157 158 public void uninstallComponents(JFileChooser fc) { 159 fc.removeAll(); 160 bottomPanel = null; 161 buttonPanel = null; 162 } 163 164 private class MetalFileChooserUIAccessor implements FilePane.FileChooserUIAccessor { 165 public JFileChooser getFileChooser() { 166 return MetalFileChooserUI.this.getFileChooser(); 167 } 168 169 public BasicDirectoryModel getModel() { 170 return MetalFileChooserUI.this.getModel(); 171 } 172 173 public JPanel createList() { 174 return MetalFileChooserUI.this.createList(getFileChooser()); 175 } 176 177 public JPanel createDetailsView() { 178 return MetalFileChooserUI.this.createDetailsView(getFileChooser()); 179 } 180 181 public boolean isDirectorySelected() { 182 return MetalFileChooserUI.this.isDirectorySelected(); 183 } 184 185 public File getDirectory() { 186 return MetalFileChooserUI.this.getDirectory(); 187 } 188 189 public Action getChangeToParentDirectoryAction() { 190 return MetalFileChooserUI.this.getChangeToParentDirectoryAction(); 191 } 192 193 public Action getApproveSelectionAction() { 194 return MetalFileChooserUI.this.getApproveSelectionAction(); 195 } 196 197 public Action getNewFolderAction() { 198 return MetalFileChooserUI.this.getNewFolderAction(); 199 } 200 201 public MouseListener createDoubleClickListener(JList list) { 202 return MetalFileChooserUI.this.createDoubleClickListener(getFileChooser(), 203 list); 204 } 205 206 public ListSelectionListener createListSelectionListener() { 207 return MetalFileChooserUI.this.createListSelectionListener(getFileChooser()); 208 } 209 210 public boolean usesShellFolder() { 211 return useShellFolder; 212 } 213 } 214 215 public void installComponents(JFileChooser fc) { 216 FileSystemView fsv = fc.getFileSystemView(); 217 218 fc.setBorder(new EmptyBorder(12, 12, 11, 11)); 219 fc.setLayout(new BorderLayout(0, 11)); 220 221 filePane = new FilePane(new MetalFileChooserUIAccessor()); 222 fc.addPropertyChangeListener(filePane); 223 224 updateUseShellFolder(); 225 226 // ********************************* // 227 // **** Construct the top panel **** // 228 // ********************************* // 229 230 // Directory manipulation buttons 231 JPanel topPanel = new JPanel(new BorderLayout(11, 0)); 232 JPanel topButtonPanel = new JPanel(); 233 topButtonPanel.setLayout(new BoxLayout(topButtonPanel, BoxLayout.LINE_AXIS)); 234 topPanel.add(topButtonPanel, BorderLayout.AFTER_LINE_ENDS); 235 236 // Add the top panel to the fileChooser 237 fc.add(topPanel, BorderLayout.NORTH); 238 239 // ComboBox Label 240 lookInLabel = new JLabel(lookInLabelText); 241 lookInLabel.setDisplayedMnemonic(lookInLabelMnemonic); 242 topPanel.add(lookInLabel, BorderLayout.BEFORE_LINE_BEGINS); 243 244 // CurrentDir ComboBox 245 directoryComboBox = new JComboBox() { 246 public Dimension getPreferredSize() { 247 Dimension d = super.getPreferredSize(); 248 // Must be small enough to not affect total width. 249 d.width = 150; 250 return d; 251 } 252 }; 253 directoryComboBox.putClientProperty(AccessibleContext.ACCESSIBLE_DESCRIPTION_PROPERTY, 254 lookInLabelText); 255 directoryComboBox.putClientProperty( "JComboBox.isTableCellEditor", Boolean.TRUE ); 256 lookInLabel.setLabelFor(directoryComboBox); 257 directoryComboBoxModel = createDirectoryComboBoxModel(fc); 258 directoryComboBox.setModel(directoryComboBoxModel); 259 directoryComboBox.addActionListener(directoryComboBoxAction); 260 directoryComboBox.setRenderer(createDirectoryComboBoxRenderer(fc)); 261 directoryComboBox.setAlignmentX(JComponent.LEFT_ALIGNMENT); 262 directoryComboBox.setAlignmentY(JComponent.TOP_ALIGNMENT); 263 directoryComboBox.setMaximumRowCount(8); 264 265 topPanel.add(directoryComboBox, BorderLayout.CENTER); 266 267 // Up Button 268 JButton upFolderButton = new JButton(getChangeToParentDirectoryAction()); 269 upFolderButton.setText(null); 270 upFolderButton.setIcon(upFolderIcon); 271 upFolderButton.setToolTipText(upFolderToolTipText); 272 upFolderButton.putClientProperty(AccessibleContext.ACCESSIBLE_NAME_PROPERTY, 273 upFolderAccessibleName); 274 upFolderButton.setAlignmentX(JComponent.LEFT_ALIGNMENT); 275 upFolderButton.setAlignmentY(JComponent.CENTER_ALIGNMENT); 276 upFolderButton.setMargin(shrinkwrap); 277 278 topButtonPanel.add(upFolderButton); 279 topButtonPanel.add(Box.createRigidArea(hstrut5)); 280 281 // Home Button 282 File homeDir = fsv.getHomeDirectory(); 283 String toolTipText = homeFolderToolTipText; 284 if (fsv.isRoot(homeDir)) { 285 toolTipText = getFileView(fc).getName(homeDir); // Probably "Desktop". 286 } 287 288 289 290 291 JButton b = new JButton(homeFolderIcon); 292 b.setToolTipText(toolTipText); 293 b.putClientProperty(AccessibleContext.ACCESSIBLE_NAME_PROPERTY, 294 homeFolderAccessibleName); 295 b.setAlignmentX(JComponent.LEFT_ALIGNMENT); 296 b.setAlignmentY(JComponent.CENTER_ALIGNMENT); 297 b.setMargin(shrinkwrap); 298 299 b.addActionListener(getGoHomeAction()); 300 topButtonPanel.add(b); 301 topButtonPanel.add(Box.createRigidArea(hstrut5)); 302 303 // New Directory Button 304 if (!UIManager.getBoolean("FileChooser.readOnly")) { 305 b = new JButton(filePane.getNewFolderAction()); 306 b.setText(null); 307 b.setIcon(newFolderIcon); 308 b.setToolTipText(newFolderToolTipText); 309 b.putClientProperty(AccessibleContext.ACCESSIBLE_NAME_PROPERTY, 310 newFolderAccessibleName); 311 b.setAlignmentX(JComponent.LEFT_ALIGNMENT); 312 b.setAlignmentY(JComponent.CENTER_ALIGNMENT); 313 b.setMargin(shrinkwrap); 314 } 315 topButtonPanel.add(b); 316 topButtonPanel.add(Box.createRigidArea(hstrut5)); 317 318 // View button group 319 ButtonGroup viewButtonGroup = new ButtonGroup(); 320 321 // List Button 322 listViewButton = new JToggleButton(listViewIcon); 323 listViewButton.setToolTipText(listViewButtonToolTipText); 324 listViewButton.putClientProperty(AccessibleContext.ACCESSIBLE_NAME_PROPERTY, 325 listViewButtonAccessibleName); 326 listViewButton.setSelected(true); 327 listViewButton.setAlignmentX(JComponent.LEFT_ALIGNMENT); 328 listViewButton.setAlignmentY(JComponent.CENTER_ALIGNMENT); 329 listViewButton.setMargin(shrinkwrap); 330 listViewButton.addActionListener(filePane.getViewTypeAction(FilePane.VIEWTYPE_LIST)); 331 topButtonPanel.add(listViewButton); 332 viewButtonGroup.add(listViewButton); 333 334 // Details Button 335 detailsViewButton = new JToggleButton(detailsViewIcon); 336 detailsViewButton.setToolTipText(detailsViewButtonToolTipText); 337 detailsViewButton.putClientProperty(AccessibleContext.ACCESSIBLE_NAME_PROPERTY, 338 detailsViewButtonAccessibleName); 339 detailsViewButton.setAlignmentX(JComponent.LEFT_ALIGNMENT); 340 detailsViewButton.setAlignmentY(JComponent.CENTER_ALIGNMENT); 341 detailsViewButton.setMargin(shrinkwrap); 342 detailsViewButton.addActionListener(filePane.getViewTypeAction(FilePane.VIEWTYPE_DETAILS)); 343 topButtonPanel.add(detailsViewButton); 344 viewButtonGroup.add(detailsViewButton); 345 346 filePane.addPropertyChangeListener(new PropertyChangeListener() { 347 public void propertyChange(PropertyChangeEvent e) { 348 if ("viewType".equals(e.getPropertyName())) { 349 int viewType = filePane.getViewType(); 350 switch (viewType) { 351 case FilePane.VIEWTYPE_LIST: 352 listViewButton.setSelected(true); 353 break; 354 355 case FilePane.VIEWTYPE_DETAILS: 356 detailsViewButton.setSelected(true); 357 break; 358 } 359 } 360 } 361 }); 362 363 // ************************************** // 364 // ******* Add the directory pane ******* // 365 // ************************************** // 366 fc.add(getAccessoryPanel(), BorderLayout.AFTER_LINE_ENDS); 367 JComponent accessory = fc.getAccessory(); 368 if(accessory != null) { 369 getAccessoryPanel().add(accessory); 370 } 371 filePane.setPreferredSize(LIST_PREF_SIZE); 372 fc.add(filePane, BorderLayout.CENTER); 373 374 // ********************************** // 375 // **** Construct the bottom panel ** // 376 // ********************************** // 377 JPanel bottomPanel = getBottomPanel(); 378 bottomPanel.setLayout(new BoxLayout(bottomPanel, BoxLayout.Y_AXIS)); 379 fc.add(bottomPanel, BorderLayout.SOUTH); 380 381 // FileName label and textfield 382 JPanel fileNamePanel = new JPanel(); 383 fileNamePanel.setLayout(new BoxLayout(fileNamePanel, BoxLayout.LINE_AXIS)); 384 bottomPanel.add(fileNamePanel); 385 bottomPanel.add(Box.createRigidArea(vstrut5)); 386 387 fileNameLabel = new AlignedLabel(); 388 populateFileNameLabel(); 389 fileNamePanel.add(fileNameLabel); 390 391 fileNameTextField = new JTextField(35) { 392 public Dimension getMaximumSize() { 393 return new Dimension(Short.MAX_VALUE, super.getPreferredSize().height); 394 } 395 }; 396 fileNamePanel.add(fileNameTextField); 397 fileNameLabel.setLabelFor(fileNameTextField); 398 fileNameTextField.addFocusListener( 399 new FocusAdapter() { 400 public void focusGained(FocusEvent e) { 401 if (!getFileChooser().isMultiSelectionEnabled()) { 402 filePane.clearSelection(); 403 } 404 } 405 } 406 ); 407 if (fc.isMultiSelectionEnabled()) { 408 setFileName(fileNameString(fc.getSelectedFiles())); 409 } else { 410 setFileName(fileNameString(fc.getSelectedFile())); 411 } 412 413 414 // Filetype label and combobox 415 JPanel filesOfTypePanel = new JPanel(); 416 filesOfTypePanel.setLayout(new BoxLayout(filesOfTypePanel, BoxLayout.LINE_AXIS)); 417 bottomPanel.add(filesOfTypePanel); 418 419 AlignedLabel filesOfTypeLabel = new AlignedLabel(filesOfTypeLabelText); 420 filesOfTypeLabel.setDisplayedMnemonic(filesOfTypeLabelMnemonic); 421 filesOfTypePanel.add(filesOfTypeLabel); 422 423 filterComboBoxModel = createFilterComboBoxModel(); 424 fc.addPropertyChangeListener(filterComboBoxModel); 425 filterComboBox = new JComboBox(filterComboBoxModel); 426 filterComboBox.putClientProperty(AccessibleContext.ACCESSIBLE_DESCRIPTION_PROPERTY, 427 filesOfTypeLabelText); 428 filesOfTypeLabel.setLabelFor(filterComboBox); 429 filterComboBox.setRenderer(createFilterComboBoxRenderer()); 430 filesOfTypePanel.add(filterComboBox); 431 432 // buttons 433 getButtonPanel().setLayout(new ButtonAreaLayout()); 434 435 approveButton = new JButton(getApproveButtonText(fc)); 436 // Note: Metal does not use mnemonics for approve and cancel 437 approveButton.addActionListener(getApproveSelectionAction()); 438 approveButton.setToolTipText(getApproveButtonToolTipText(fc)); 439 getButtonPanel().add(approveButton); 440 441 cancelButton = new JButton(cancelButtonText); 442 cancelButton.setToolTipText(cancelButtonToolTipText); 443 cancelButton.addActionListener(getCancelSelectionAction()); 444 getButtonPanel().add(cancelButton); 445 446 if(fc.getControlButtonsAreShown()) { 447 addControlButtons(); 448 } 449 450 groupLabels(new AlignedLabel[] { fileNameLabel, filesOfTypeLabel }); 451 } 452 453 private void updateUseShellFolder() { 454 // Decide whether to use the ShellFolder class to populate shortcut 455 // panel and combobox. 456 JFileChooser fc = getFileChooser(); 457 Boolean prop = 458 (Boolean)fc.getClientProperty("FileChooser.useShellFolder"); 459 if (prop != null) { 460 useShellFolder = prop.booleanValue(); 461 } else { 462 useShellFolder = fc.getFileSystemView().equals(FileSystemView.getFileSystemView()); 463 } 464 } 465 466 protected JPanel getButtonPanel() { 467 if (buttonPanel == null) { 468 buttonPanel = new JPanel(); 469 } 470 return buttonPanel; 471 } 472 473 protected JPanel getBottomPanel() { 474 if(bottomPanel == null) { 475 bottomPanel = new JPanel(); 476 } 477 return bottomPanel; 478 } 479 480 protected void installStrings(JFileChooser fc) { 481 super.installStrings(fc); 482 483 Locale l = fc.getLocale(); 484 485 lookInLabelMnemonic = UIManager.getInt("FileChooser.lookInLabelMnemonic"); 486 lookInLabelText = UIManager.getString("FileChooser.lookInLabelText",l); 487 saveInLabelText = UIManager.getString("FileChooser.saveInLabelText",l); 488 489 fileNameLabelMnemonic = UIManager.getInt("FileChooser.fileNameLabelMnemonic"); 490 fileNameLabelText = UIManager.getString("FileChooser.fileNameLabelText",l); 491 folderNameLabelMnemonic = UIManager.getInt("FileChooser.folderNameLabelMnemonic"); 492 folderNameLabelText = UIManager.getString("FileChooser.folderNameLabelText",l); 493 494 filesOfTypeLabelMnemonic = UIManager.getInt("FileChooser.filesOfTypeLabelMnemonic"); 495 filesOfTypeLabelText = UIManager.getString("FileChooser.filesOfTypeLabelText",l); 496 497 upFolderToolTipText = UIManager.getString("FileChooser.upFolderToolTipText",l); 498 upFolderAccessibleName = UIManager.getString("FileChooser.upFolderAccessibleName",l); 499 500 homeFolderToolTipText = UIManager.getString("FileChooser.homeFolderToolTipText",l); 501 homeFolderAccessibleName = UIManager.getString("FileChooser.homeFolderAccessibleName",l); 502 503 newFolderToolTipText = UIManager.getString("FileChooser.newFolderToolTipText",l); 504 newFolderAccessibleName = UIManager.getString("FileChooser.newFolderAccessibleName",l); 505 506 listViewButtonToolTipText = UIManager.getString("FileChooser.listViewButtonToolTipText",l); 507 listViewButtonAccessibleName = UIManager.getString("FileChooser.listViewButtonAccessibleName",l); 508 509 detailsViewButtonToolTipText = UIManager.getString("FileChooser.detailsViewButtonToolTipText",l); 510 detailsViewButtonAccessibleName = UIManager.getString("FileChooser.detailsViewButtonAccessibleName",l); 511 } 512 513 protected void installListeners(JFileChooser fc) { 514 super.installListeners(fc); 515 ActionMap actionMap = getActionMap(); 516 SwingUtilities.replaceUIActionMap(fc, actionMap); 517 } 518 519 protected ActionMap getActionMap() { 520 return createActionMap(); 521 } 522 523 protected ActionMap createActionMap() { 524 ActionMap map = new ActionMapUIResource(); 525 FilePane.addActionsToMap(map, filePane.getActions()); 526 return map; 527 } 528 529 protected JPanel createList(JFileChooser fc) { 530 return filePane.createList(); 531 } 532 533 protected JPanel createDetailsView(JFileChooser fc) { 534 return filePane.createDetailsView(); 535 } 536 537 /** 538 * Creates a selection listener for the list of files and directories. 539 * 540 * @param fc a <code>JFileChooser</code> 541 * @return a <code>ListSelectionListener</code> 542 */ 543 public ListSelectionListener createListSelectionListener(JFileChooser fc) { 544 return super.createListSelectionListener(fc); 545 } 546 547 // Obsolete class, not used in this version. 548 protected class SingleClickListener extends MouseAdapter { 549 public SingleClickListener(JList list) { 550 } 551 } 552 553 // Obsolete class, not used in this version. 554 protected class FileRenderer extends DefaultListCellRenderer { 555 } 556 557 public void uninstallUI(JComponent c) { 558 // Remove listeners 559 c.removePropertyChangeListener(filterComboBoxModel); 560 c.removePropertyChangeListener(filePane); 561 cancelButton.removeActionListener(getCancelSelectionAction()); 562 approveButton.removeActionListener(getApproveSelectionAction()); 563 fileNameTextField.removeActionListener(getApproveSelectionAction()); 564 565 if (filePane != null) { 566 filePane.uninstallUI(); 567 filePane = null; 568 } 569 570 super.uninstallUI(c); 571 } 572 573 /** 574 * Returns the preferred size of the specified 575 * <code>JFileChooser</code>. 576 * The preferred size is at least as large, 577 * in both height and width, 578 * as the preferred size recommended 579 * by the file chooser's layout manager. 580 * 581 * @param c a <code>JFileChooser</code> 582 * @return a <code>Dimension</code> specifying the preferred 583 * width and height of the file chooser 584 */ 585 public Dimension getPreferredSize(JComponent c) { 586 int prefWidth = PREF_SIZE.width; 587 Dimension d = c.getLayout().preferredLayoutSize(c); 588 if (d != null) { 589 return new Dimension(d.width < prefWidth ? prefWidth : d.width, 590 d.height < PREF_SIZE.height ? PREF_SIZE.height : d.height); 591 } else { 592 return new Dimension(prefWidth, PREF_SIZE.height); 593 } 594 } 595 596 /** 597 * Returns the minimum size of the <code>JFileChooser</code>. 598 * 599 * @param c a <code>JFileChooser</code> 600 * @return a <code>Dimension</code> specifying the minimum 601 * width and height of the file chooser 602 */ 603 public Dimension getMinimumSize(JComponent c) { 604 return MIN_SIZE; 605 } 606 607 /** 608 * Returns the maximum size of the <code>JFileChooser</code>. 609 * 610 * @param c a <code>JFileChooser</code> 611 * @return a <code>Dimension</code> specifying the maximum 612 * width and height of the file chooser 613 */ 614 public Dimension getMaximumSize(JComponent c) { 615 return new Dimension(Integer.MAX_VALUE, Integer.MAX_VALUE); 616 } 617 618 private String fileNameString(File file) { 619 if (file == null) { 620 return null; 621 } else { 622 JFileChooser fc = getFileChooser(); 623 if ((fc.isDirectorySelectionEnabled() && !fc.isFileSelectionEnabled()) || 624 (fc.isDirectorySelectionEnabled() && fc.isFileSelectionEnabled() && fc.getFileSystemView().isFileSystemRoot(file))) { 625 return file.getPath(); 626 } else { 627 return file.getName(); 628 } 629 } 630 } 631 632 private String fileNameString(File[] files) { 633 StringBuffer buf = new StringBuffer(); 634 for (int i = 0; files != null && i < files.length; i++) { 635 if (i > 0) { 636 buf.append(" "); 637 } 638 if (files.length > 1) { 639 buf.append("\""); 640 } 641 buf.append(fileNameString(files[i])); 642 if (files.length > 1) { 643 buf.append("\""); 644 } 645 } 646 return buf.toString(); 647 } 648 649 /* The following methods are used by the PropertyChange Listener */ 650 651 private void doSelectedFileChanged(PropertyChangeEvent e) { 652 File f = (File) e.getNewValue(); 653 JFileChooser fc = getFileChooser(); 654 if (f != null 655 && ((fc.isFileSelectionEnabled() && !f.isDirectory()) 656 || (f.isDirectory() && fc.isDirectorySelectionEnabled()))) { 657 658 setFileName(fileNameString(f)); 659 } 660 } 661 662 private void doSelectedFilesChanged(PropertyChangeEvent e) { 663 File[] files = (File[]) e.getNewValue(); 664 JFileChooser fc = getFileChooser(); 665 if (files != null 666 && files.length > 0 667 && (files.length > 1 || fc.isDirectorySelectionEnabled() || !files[0].isDirectory())) { 668 setFileName(fileNameString(files)); 669 } 670 } 671 672 private void doDirectoryChanged(PropertyChangeEvent e) { 673 JFileChooser fc = getFileChooser(); 674 FileSystemView fsv = fc.getFileSystemView(); 675 676 clearIconCache(); 677 File currentDirectory = fc.getCurrentDirectory(); 678 if(currentDirectory != null) { 679 directoryComboBoxModel.addItem(currentDirectory); 680 681 if (fc.isDirectorySelectionEnabled() && !fc.isFileSelectionEnabled()) { 682 if (fsv.isFileSystem(currentDirectory)) { 683 setFileName(currentDirectory.getPath()); 684 } else { 685 setFileName(null); 686 } 687 } 688 } 689 } 690 691 private void doFilterChanged(PropertyChangeEvent e) { 692 clearIconCache(); 693 } 694 695 private void doFileSelectionModeChanged(PropertyChangeEvent e) { 696 if (fileNameLabel != null) { 697 populateFileNameLabel(); 698 } 699 clearIconCache(); 700 701 JFileChooser fc = getFileChooser(); 702 File currentDirectory = fc.getCurrentDirectory(); 703 if (currentDirectory != null 704 && fc.isDirectorySelectionEnabled() 705 && !fc.isFileSelectionEnabled() 706 && fc.getFileSystemView().isFileSystem(currentDirectory)) { 707 708 setFileName(currentDirectory.getPath()); 709 } else { 710 setFileName(null); 711 } 712 } 713 714 private void doAccessoryChanged(PropertyChangeEvent e) { 715 if(getAccessoryPanel() != null) { 716 if(e.getOldValue() != null) { 717 getAccessoryPanel().remove((JComponent) e.getOldValue()); 718 } 719 JComponent accessory = (JComponent) e.getNewValue(); 720 if(accessory != null) { 721 getAccessoryPanel().add(accessory, BorderLayout.CENTER); 722 } 723 } 724 } 725 726 private void doApproveButtonTextChanged(PropertyChangeEvent e) { 727 JFileChooser chooser = getFileChooser(); 728 approveButton.setText(getApproveButtonText(chooser)); 729 approveButton.setToolTipText(getApproveButtonToolTipText(chooser)); 730 } 731 732 private void doDialogTypeChanged(PropertyChangeEvent e) { 733 JFileChooser chooser = getFileChooser(); 734 approveButton.setText(getApproveButtonText(chooser)); 735 approveButton.setToolTipText(getApproveButtonToolTipText(chooser)); 736 if (chooser.getDialogType() == JFileChooser.SAVE_DIALOG) { 737 lookInLabel.setText(saveInLabelText); 738 } else { 739 lookInLabel.setText(lookInLabelText); 740 } 741 } 742 743 private void doApproveButtonMnemonicChanged(PropertyChangeEvent e) { 744 // Note: Metal does not use mnemonics for approve and cancel 745 } 746 747 private void doControlButtonsChanged(PropertyChangeEvent e) { 748 if(getFileChooser().getControlButtonsAreShown()) { 749 addControlButtons(); 750 } else { 751 removeControlButtons(); 752 } 753 } 754 755 /* 756 * Listen for filechooser property changes, such as 757 * the selected file changing, or the type of the dialog changing. 758 */ 759 public PropertyChangeListener createPropertyChangeListener(JFileChooser fc) { 760 return new PropertyChangeListener() { 761 public void propertyChange(PropertyChangeEvent e) { 762 String s = e.getPropertyName(); 763 if(s.equals(JFileChooser.SELECTED_FILE_CHANGED_PROPERTY)) { 764 doSelectedFileChanged(e); 765 } else if (s.equals(JFileChooser.SELECTED_FILES_CHANGED_PROPERTY)) { 766 doSelectedFilesChanged(e); 767 } else if(s.equals(JFileChooser.DIRECTORY_CHANGED_PROPERTY)) { 768 doDirectoryChanged(e); 769 } else if(s.equals(JFileChooser.FILE_FILTER_CHANGED_PROPERTY)) { 770 doFilterChanged(e); 771 } else if(s.equals(JFileChooser.FILE_SELECTION_MODE_CHANGED_PROPERTY)) { 772 doFileSelectionModeChanged(e); 773 } else if(s.equals(JFileChooser.ACCESSORY_CHANGED_PROPERTY)) { 774 doAccessoryChanged(e); 775 } else if (s.equals(JFileChooser.APPROVE_BUTTON_TEXT_CHANGED_PROPERTY) || 776 s.equals(JFileChooser.APPROVE_BUTTON_TOOL_TIP_TEXT_CHANGED_PROPERTY)) { 777 doApproveButtonTextChanged(e); 778 } else if(s.equals(JFileChooser.DIALOG_TYPE_CHANGED_PROPERTY)) { 779 doDialogTypeChanged(e); 780 } else if(s.equals(JFileChooser.APPROVE_BUTTON_MNEMONIC_CHANGED_PROPERTY)) { 781 doApproveButtonMnemonicChanged(e); 782 } else if(s.equals(JFileChooser.CONTROL_BUTTONS_ARE_SHOWN_CHANGED_PROPERTY)) { 783 doControlButtonsChanged(e); 784 } else if (s.equals("componentOrientation")) { 785 ComponentOrientation o = (ComponentOrientation)e.getNewValue(); 786 JFileChooser cc = (JFileChooser)e.getSource(); 787 if (o != (ComponentOrientation)e.getOldValue()) { 788 cc.applyComponentOrientation(o); 789 } 790 } else if (s == "FileChooser.useShellFolder") { 791 updateUseShellFolder(); 792 doDirectoryChanged(e); 793 } else if (s.equals("ancestor")) { 794 if (e.getOldValue() == null && e.getNewValue() != null) { 795 // Ancestor was added, set initial focus 796 fileNameTextField.selectAll(); 797 fileNameTextField.requestFocus(); 798 } 799 } 800 } 801 }; 802 } 803 804 805 protected void removeControlButtons() { 806 getBottomPanel().remove(getButtonPanel()); 807 } 808 809 protected void addControlButtons() { 810 getBottomPanel().add(getButtonPanel()); 811 } 812 813 public void ensureFileIsVisible(JFileChooser fc, File f) { 814 filePane.ensureFileIsVisible(fc, f); 815 } 816 817 public void rescanCurrentDirectory(JFileChooser fc) { 818 filePane.rescanCurrentDirectory(); 819 } 820 821 public String getFileName() { 822 if (fileNameTextField != null) { 823 return fileNameTextField.getText(); 824 } else { 825 return null; 826 } 827 } 828 829 public void setFileName(String filename) { 830 if (fileNameTextField != null) { 831 fileNameTextField.setText(filename); 832 } 833 } 834 835 /** 836 * Property to remember whether a directory is currently selected in the UI. 837 * This is normally called by the UI on a selection event. 838 * 839 * @param directorySelected if a directory is currently selected. 840 * @since 1.4 841 */ 842 protected void setDirectorySelected(boolean directorySelected) { 843 super.setDirectorySelected(directorySelected); 844 JFileChooser chooser = getFileChooser(); 845 if(directorySelected) { 846 if (approveButton != null) { 847 approveButton.setText(directoryOpenButtonText); 848 approveButton.setToolTipText(directoryOpenButtonToolTipText); 849 } 850 } else { 851 if (approveButton != null) { 852 approveButton.setText(getApproveButtonText(chooser)); 853 approveButton.setToolTipText(getApproveButtonToolTipText(chooser)); 854 } 855 } 856 } 857 858 public String getDirectoryName() { 859 // PENDING(jeff) - get the name from the directory combobox 860 return null; 861 } 862 863 public void setDirectoryName(String dirname) { 864 // PENDING(jeff) - set the name in the directory combobox 865 } 866 867 protected DirectoryComboBoxRenderer createDirectoryComboBoxRenderer(JFileChooser fc) { 868 return new DirectoryComboBoxRenderer(); 869 } 870 871 // 872 // Renderer for DirectoryComboBox 873 // 874 class DirectoryComboBoxRenderer extends DefaultListCellRenderer { 875 IndentIcon ii = new IndentIcon(); 876 public Component getListCellRendererComponent(JList list, Object value, 877 int index, boolean isSelected, 878 boolean cellHasFocus) { 879 880 super.getListCellRendererComponent(list, value, index, isSelected, cellHasFocus); 881 882 if (value == null) { 883 setText(""); 884 return this; 885 } 886 File directory = (File)value; 887 setText(getFileChooser().getName(directory)); 888 Icon icon = getFileChooser().getIcon(directory); 889 ii.icon = icon; 890 ii.depth = directoryComboBoxModel.getDepth(index); 891 setIcon(ii); 892 893 return this; 894 } 895 } 896 897 final static int space = 10; 898 class IndentIcon implements Icon { 899 900 Icon icon = null; 901 int depth = 0; 902 903 public void paintIcon(Component c, Graphics g, int x, int y) { 904 if (c.getComponentOrientation().isLeftToRight()) { 905 icon.paintIcon(c, g, x+depth*space, y); 906 } else { 907 icon.paintIcon(c, g, x, y); 908 } 909 } 910 911 public int getIconWidth() { 912 return icon.getIconWidth() + depth*space; 913 } 914 915 public int getIconHeight() { 916 return icon.getIconHeight(); 917 } 918 919 } 920 921 // 922 // DataModel for DirectoryComboxbox 923 // 924 protected DirectoryComboBoxModel createDirectoryComboBoxModel(JFileChooser fc) { 925 return new DirectoryComboBoxModel(); 926 } 927 928 /** 929 * Data model for a type-face selection combo-box. 930 */ 931 protected class DirectoryComboBoxModel extends AbstractListModel implements ComboBoxModel { 932 Vector directories = new Vector(); 933 int[] depths = null; 934 File selectedDirectory = null; 935 JFileChooser chooser = getFileChooser(); 936 FileSystemView fsv = chooser.getFileSystemView(); 937 938 public DirectoryComboBoxModel() { 939 // Add the current directory to the model, and make it the 940 // selectedDirectory 941 File dir = getFileChooser().getCurrentDirectory(); 942 if(dir != null) { 943 addItem(dir); 944 } 945 } 946 947 /** 948 * Adds the directory to the model and sets it to be selected, 949 * additionally clears out the previous selected directory and 950 * the paths leading up to it, if any. 951 */ 952 private void addItem(File directory) { 953 954 if(directory == null) { 955 return; 956 } 957 958 directories.clear(); 959 960 File[] baseFolders = (useShellFolder) 961 ? (File[]) ShellFolder.get("fileChooserComboBoxFolders") 962 : fsv.getRoots(); 963 directories.addAll(Arrays.asList(baseFolders)); 964 965 // Get the canonical (full) path. This has the side 966 // benefit of removing extraneous chars from the path, 967 // for example /foo/bar/ becomes /foo/bar 968 File canonical = null; 969 try { 970 canonical = ShellFolder.getNormalizedFile(directory); 971 } catch (IOException e) { 972 // Maybe drive is not ready. Can't abort here. 973 canonical = directory; 974 } 975 976 // create File instances of each directory leading up to the top 977 try { 978 File sf = useShellFolder ? ShellFolder.getShellFolder(canonical) 979 : canonical; 980 File f = sf; 981 Vector path = new Vector(10); 982 do { 983 path.addElement(f); 984 } while ((f = f.getParentFile()) != null); 985 986 int pathCount = path.size(); 987 // Insert chain at appropriate place in vector 988 for (int i = 0; i < pathCount; i++) { 989 f = (File)path.get(i); 990 if (directories.contains(f)) { 991 int topIndex = directories.indexOf(f); 992 for (int j = i-1; j >= 0; j--) { 993 directories.insertElementAt(path.get(j), topIndex+i-j); 994 } 995 break; 996 } 997 } 998 calculateDepths(); 999 setSelectedItem(sf); 1000 } catch (FileNotFoundException ex) { 1001 calculateDepths(); 1002 } 1003 } 1004 1005 private void calculateDepths() { 1006 depths = new int[directories.size()]; 1007 for (int i = 0; i < depths.length; i++) { 1008 File dir = (File)directories.get(i); 1009 File parent = dir.getParentFile(); 1010 depths[i] = 0; 1011 if (parent != null) { 1012 for (int j = i-1; j >= 0; j--) { 1013 if (parent.equals((File)directories.get(j))) { 1014 depths[i] = depths[j] + 1; 1015 break; 1016 } 1017 } 1018 } 1019 } 1020 } 1021 1022 public int getDepth(int i) { 1023 return (depths != null && i >= 0 && i < depths.length) ? depths[i] : 0; 1024 } 1025 1026 public void setSelectedItem(Object selectedDirectory) { 1027 this.selectedDirectory = (File)selectedDirectory; 1028 fireContentsChanged(this, -1, -1); 1029 } 1030 1031 public Object getSelectedItem() { 1032 return selectedDirectory; 1033 } 1034 1035 public int getSize() { 1036 return directories.size(); 1037 } 1038 1039 public Object getElementAt(int index) { 1040 return directories.elementAt(index); 1041 } 1042 } 1043 1044 // 1045 // Renderer for Types ComboBox 1046 // 1047 protected FilterComboBoxRenderer createFilterComboBoxRenderer() { 1048 return new FilterComboBoxRenderer(); 1049 } 1050 1051 /** 1052 * Render different type sizes and styles. 1053 */ 1054 public class FilterComboBoxRenderer extends DefaultListCellRenderer { 1055 public Component getListCellRendererComponent(JList list, 1056 Object value, int index, boolean isSelected, 1057 boolean cellHasFocus) { 1058 1059 super.getListCellRendererComponent(list, value, index, isSelected, cellHasFocus); 1060 1061 if (value != null && value instanceof FileFilter) { 1062 setText(((FileFilter)value).getDescription()); 1063 } 1064 1065 return this; 1066 } 1067 } 1068 1069 // 1070 // DataModel for Types Comboxbox 1071 // 1072 protected FilterComboBoxModel createFilterComboBoxModel() { 1073 return new FilterComboBoxModel(); 1074 } 1075 1076 /** 1077 * Data model for a type-face selection combo-box. 1078 */ 1079 protected class FilterComboBoxModel extends AbstractListModel implements ComboBoxModel, PropertyChangeListener { 1080 protected FileFilter[] filters; 1081 protected FilterComboBoxModel() { 1082 super(); 1083 filters = getFileChooser().getChoosableFileFilters(); 1084 } 1085 1086 public void propertyChange(PropertyChangeEvent e) { 1087 String prop = e.getPropertyName(); 1088 if(prop == JFileChooser.CHOOSABLE_FILE_FILTER_CHANGED_PROPERTY) { 1089 filters = (FileFilter[]) e.getNewValue(); 1090 fireContentsChanged(this, -1, -1); 1091 } else if (prop == JFileChooser.FILE_FILTER_CHANGED_PROPERTY) { 1092 fireContentsChanged(this, -1, -1); 1093 } 1094 } 1095 1096 public void setSelectedItem(Object filter) { 1097 if(filter != null) { 1098 getFileChooser().setFileFilter((FileFilter) filter); 1099 fireContentsChanged(this, -1, -1); 1100 } 1101 } 1102 1103 public Object getSelectedItem() { 1104 // Ensure that the current filter is in the list. 1105 // NOTE: we shouldnt' have to do this, since JFileChooser adds 1106 // the filter to the choosable filters list when the filter 1107 // is set. Lets be paranoid just in case someone overrides 1108 // setFileFilter in JFileChooser. 1109 FileFilter currentFilter = getFileChooser().getFileFilter(); 1110 boolean found = false; 1111 if(currentFilter != null) { 1112 for(int i=0; i < filters.length; i++) { 1113 if(filters[i] == currentFilter) { 1114 found = true; 1115 } 1116 } 1117 if(found == false) { 1118 getFileChooser().addChoosableFileFilter(currentFilter); 1119 } 1120 } 1121 return getFileChooser().getFileFilter(); 1122 } 1123 1124 public int getSize() { 1125 if(filters != null) { 1126 return filters.length; 1127 } else { 1128 return 0; 1129 } 1130 } 1131 1132 public Object getElementAt(int index) { 1133 if(index > getSize() - 1) { 1134 // This shouldn't happen. Try to recover gracefully. 1135 return getFileChooser().getFileFilter(); 1136 } 1137 if(filters != null) { 1138 return filters[index]; 1139 } else { 1140 return null; 1141 } 1142 } 1143 } 1144 1145 public void valueChanged(ListSelectionEvent e) { 1146 JFileChooser fc = getFileChooser(); 1147 File f = fc.getSelectedFile(); 1148 if (!e.getValueIsAdjusting() && f != null && !getFileChooser().isTraversable(f)) { 1149 setFileName(fileNameString(f)); 1150 } 1151 } 1152 1153 /** 1154 * Acts when DirectoryComboBox has changed the selected item. 1155 */ 1156 protected class DirectoryComboBoxAction extends AbstractAction { 1157 protected DirectoryComboBoxAction() { 1158 super("DirectoryComboBoxAction"); 1159 } 1160 1161 public void actionPerformed(ActionEvent e) { 1162 directoryComboBox.hidePopup(); 1163 File f = (File)directoryComboBox.getSelectedItem(); 1164 if (!getFileChooser().getCurrentDirectory().equals(f)) { 1165 getFileChooser().setCurrentDirectory(f); 1166 } 1167 } 1168 } 1169 1170 protected JButton getApproveButton(JFileChooser fc) { 1171 return approveButton; 1172 } 1173 1174 1175 /** 1176 * <code>ButtonAreaLayout</code> behaves in a similar manner to 1177 * <code>FlowLayout</code>. It lays out all components from left to 1178 * right, flushed right. The widths of all components will be set 1179 * to the largest preferred size width. 1180 */ 1181 private static class ButtonAreaLayout implements LayoutManager { 1182 private int hGap = 5; 1183 private int topMargin = 17; 1184 1185 public void addLayoutComponent(String string, Component comp) { 1186 } 1187 1188 public void layoutContainer(Container container) { 1189 Component[] children = container.getComponents(); 1190 1191 if (children != null && children.length > 0) { 1192 int numChildren = children.length; 1193 Dimension[] sizes = new Dimension[numChildren]; 1194 Insets insets = container.getInsets(); 1195 int yLocation = insets.top + topMargin; 1196 int maxWidth = 0; 1197 1198 for (int counter = 0; counter < numChildren; counter++) { 1199 sizes[counter] = children[counter].getPreferredSize(); 1200 maxWidth = Math.max(maxWidth, sizes[counter].width); 1201 } 1202 int xLocation, xOffset; 1203 if (container.getComponentOrientation().isLeftToRight()) { 1204 xLocation = container.getSize().width - insets.left - maxWidth; 1205 xOffset = hGap + maxWidth; 1206 } else { 1207 xLocation = insets.left; 1208 xOffset = -(hGap + maxWidth); 1209 } 1210 for (int counter = numChildren - 1; counter >= 0; counter--) { 1211 children[counter].setBounds(xLocation, yLocation, 1212 maxWidth, sizes[counter].height); 1213 xLocation -= xOffset; 1214 } 1215 } 1216 } 1217 1218 public Dimension minimumLayoutSize(Container c) { 1219 if (c != null) { 1220 Component[] children = c.getComponents(); 1221 1222 if (children != null && children.length > 0) { 1223 int numChildren = children.length; 1224 int height = 0; 1225 Insets cInsets = c.getInsets(); 1226 int extraHeight = topMargin + cInsets.top + cInsets.bottom; 1227 int extraWidth = cInsets.left + cInsets.right; 1228 int maxWidth = 0; 1229 1230 for (int counter = 0; counter < numChildren; counter++) { 1231 Dimension aSize = children[counter].getPreferredSize(); 1232 height = Math.max(height, aSize.height); 1233 maxWidth = Math.max(maxWidth, aSize.width); 1234 } 1235 return new Dimension(extraWidth + numChildren * maxWidth + 1236 (numChildren - 1) * hGap, 1237 extraHeight + height); 1238 } 1239 } 1240 return new Dimension(0, 0); 1241 } 1242 1243 public Dimension preferredLayoutSize(Container c) { 1244 return minimumLayoutSize(c); 1245 } 1246 1247 public void removeLayoutComponent(Component c) { } 1248 } 1249 1250 private static void groupLabels(AlignedLabel[] group) { 1251 for (int i = 0; i < group.length; i++) { 1252 group[i].group = group; 1253 } 1254 } 1255 1256 private class AlignedLabel extends JLabel { 1257 private AlignedLabel[] group; 1258 private int maxWidth = 0; 1259 1260 AlignedLabel() { 1261 super(); 1262 setAlignmentX(JComponent.LEFT_ALIGNMENT); 1263 } 1264 1265 1266 AlignedLabel(String text) { 1267 super(text); 1268 setAlignmentX(JComponent.LEFT_ALIGNMENT); 1269 } 1270 1271 public Dimension getPreferredSize() { 1272 Dimension d = super.getPreferredSize(); 1273 // Align the width with all other labels in group. 1274 return new Dimension(getMaxWidth() + 11, d.height); 1275 } 1276 1277 private int getMaxWidth() { 1278 if (maxWidth == 0 && group != null) { 1279 int max = 0; 1280 for (int i = 0; i < group.length; i++) { 1281 max = Math.max(group[i].getSuperPreferredWidth(), max); 1282 } 1283 for (int i = 0; i < group.length; i++) { 1284 group[i].maxWidth = max; 1285 } 1286 } 1287 return maxWidth; 1288 } 1289 1290 private int getSuperPreferredWidth() { 1291 return super.getPreferredSize().width; 1292 } 1293 } 1294 }