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