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