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 {@literal 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 
  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 }