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