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