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