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