1 /*
   2  * Copyright (c) 2003, 2010, 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 package sun.swing.plaf.synth;
  26 
  27 import java.awt.*;
  28 import java.awt.event.*;
  29 import java.beans.*;
  30 import java.io.*;
  31 import java.util.*;
  32 import java.security.AccessController;
  33 import java.security.PrivilegedAction;
  34 
  35 import javax.swing.*;
  36 import javax.swing.event.*;
  37 import javax.swing.filechooser.*;
  38 import javax.swing.filechooser.FileFilter;
  39 import javax.swing.plaf.basic.*;
  40 import javax.swing.plaf.synth.*;
  41 import javax.swing.plaf.ActionMapUIResource;
  42 
  43 import sun.awt.shell.ShellFolder;
  44 import sun.swing.*;
  45 
  46 /**
  47  * Synth FileChooserUI implementation.
  48  * <p>
  49  * Note that the classes in the com.sun.java.swing.plaf.synth
  50  * package are not
  51  * part of the core Java APIs. They are a part of Sun's JDK and JRE
  52  * distributions. Although other licensees may choose to distribute
  53  * these classes, developers cannot depend on their availability in
  54  * non-Sun implementations. Additionally this API may change in
  55  * incompatible ways between releases. While this class is public, it
  56  * shoud be considered an implementation detail, and subject to change.
  57  *
  58  * @author Leif Samuelsson
  59  * @author Jeff Dinkins
  60  */
  61 public class SynthFileChooserUIImpl extends SynthFileChooserUI {
  62     private JLabel lookInLabel;
  63     private JComboBox<File> directoryComboBox;
  64     private DirectoryComboBoxModel directoryComboBoxModel;
  65     private Action directoryComboBoxAction = new DirectoryComboBoxAction();
  66 
  67     private FilterComboBoxModel filterComboBoxModel;
  68 
  69     private JTextField fileNameTextField;
  70 
  71     private FilePane filePane;
  72     private JToggleButton listViewButton;
  73     private JToggleButton detailsViewButton;
  74 
  75     private boolean readOnly;
  76 
  77     private JPanel buttonPanel;
  78     private JPanel bottomPanel;
  79 
  80     private JComboBox<FileFilter> filterComboBox;
  81 
  82     private static final Dimension hstrut5 = new Dimension(5, 1);
  83 
  84     private static final Insets shrinkwrap = new Insets(0,0,0,0);
  85 
  86     // Preferred and Minimum sizes for the dialog box
  87     private static Dimension LIST_PREF_SIZE = new Dimension(405, 135);
  88 
  89     // Labels, mnemonics, and tooltips (oh my!)
  90     private int    lookInLabelMnemonic = 0;
  91     private String lookInLabelText = null;
  92     private String saveInLabelText = null;
  93 
  94     private int    fileNameLabelMnemonic = 0;
  95     private String fileNameLabelText = null;
  96     private int    folderNameLabelMnemonic = 0;
  97     private String folderNameLabelText = null;
  98 
  99     private int    filesOfTypeLabelMnemonic = 0;
 100     private String filesOfTypeLabelText = null;
 101 
 102     private String upFolderToolTipText = null;
 103     private String upFolderAccessibleName = null;
 104 
 105     private String homeFolderToolTipText = null;
 106     private String homeFolderAccessibleName = null;
 107 
 108     private String newFolderToolTipText = null;
 109     private String newFolderAccessibleName = null;
 110 
 111     private String listViewButtonToolTipText = null;
 112     private String listViewButtonAccessibleName = null;
 113 
 114     private String detailsViewButtonToolTipText = null;
 115     private String detailsViewButtonAccessibleName = null;
 116 
 117     private AlignedLabel fileNameLabel;
 118     private final PropertyChangeListener modeListener = new PropertyChangeListener() {
 119         public void propertyChange(PropertyChangeEvent event) {
 120             if (fileNameLabel != null) {
 121                 populateFileNameLabel();
 122             }
 123         }
 124     };
 125 
 126     private void populateFileNameLabel() {
 127         if (getFileChooser().getFileSelectionMode() == JFileChooser.DIRECTORIES_ONLY) {
 128             fileNameLabel.setText(folderNameLabelText);
 129             fileNameLabel.setDisplayedMnemonic(folderNameLabelMnemonic);
 130         } else {
 131             fileNameLabel.setText(fileNameLabelText);
 132             fileNameLabel.setDisplayedMnemonic(fileNameLabelMnemonic);
 133         }
 134     }
 135 
 136     public SynthFileChooserUIImpl(JFileChooser b) {
 137         super(b);
 138     }
 139 
 140 
 141     private class SynthFileChooserUIAccessor implements FilePane.FileChooserUIAccessor {
 142         public JFileChooser getFileChooser() {
 143             return SynthFileChooserUIImpl.this.getFileChooser();
 144         }
 145 
 146         public BasicDirectoryModel getModel() {
 147             return SynthFileChooserUIImpl.this.getModel();
 148         }
 149 
 150         public JPanel createList() {
 151             return null;
 152         }
 153 
 154         public JPanel createDetailsView() {
 155             return null;
 156         }
 157 
 158         public boolean isDirectorySelected() {
 159             return SynthFileChooserUIImpl.this.isDirectorySelected();
 160         }
 161 
 162         public File getDirectory() {
 163             return SynthFileChooserUIImpl.this.getDirectory();
 164         }
 165 
 166         public Action getChangeToParentDirectoryAction() {
 167             return SynthFileChooserUIImpl.this.getChangeToParentDirectoryAction();
 168         }
 169 
 170         public Action getApproveSelectionAction() {
 171             return SynthFileChooserUIImpl.this.getApproveSelectionAction();
 172         }
 173 
 174         public Action getNewFolderAction() {
 175             return SynthFileChooserUIImpl.this.getNewFolderAction();
 176         }
 177 
 178         public MouseListener createDoubleClickListener(JList list) {
 179             return SynthFileChooserUIImpl.this.createDoubleClickListener(getFileChooser(),
 180                                                                      list);
 181         }
 182 
 183         public ListSelectionListener createListSelectionListener() {
 184             return SynthFileChooserUIImpl.this.createListSelectionListener(getFileChooser());
 185         }
 186     }
 187 
 188     protected void installDefaults(JFileChooser fc) {
 189         super.installDefaults(fc);
 190         readOnly = UIManager.getBoolean("FileChooser.readOnly");
 191     }
 192 
 193     public void installComponents(JFileChooser fc) {
 194         super.installComponents(fc);
 195 
 196         SynthContext context = getContext(fc, ENABLED);
 197 
 198         fc.setLayout(new BorderLayout(0, 11));
 199 
 200         // ********************************* //
 201         // **** Construct the top panel **** //
 202         // ********************************* //
 203 
 204         // Directory manipulation buttons
 205         JPanel topPanel = new JPanel(new BorderLayout(11, 0));
 206     JPanel topButtonPanel = new JPanel();
 207     topButtonPanel.setLayout(new BoxLayout(topButtonPanel, BoxLayout.LINE_AXIS));
 208     topPanel.add(topButtonPanel, BorderLayout.AFTER_LINE_ENDS);
 209 
 210         // Add the top panel to the fileChooser
 211         fc.add(topPanel, BorderLayout.NORTH);
 212 
 213         // ComboBox Label
 214         lookInLabel = new JLabel(lookInLabelText);
 215         lookInLabel.setDisplayedMnemonic(lookInLabelMnemonic);
 216         topPanel.add(lookInLabel, BorderLayout.BEFORE_LINE_BEGINS);
 217 
 218         // CurrentDir ComboBox
 219         directoryComboBox = new JComboBox<File>();
 220         directoryComboBox.getAccessibleContext().setAccessibleDescription(lookInLabelText);
 221         directoryComboBox.putClientProperty( "JComboBox.isTableCellEditor", Boolean.TRUE );
 222         lookInLabel.setLabelFor(directoryComboBox);
 223         directoryComboBoxModel = createDirectoryComboBoxModel(fc);
 224         directoryComboBox.setModel(directoryComboBoxModel);
 225         directoryComboBox.addActionListener(directoryComboBoxAction);
 226         directoryComboBox.setRenderer(createDirectoryComboBoxRenderer(fc));
 227         directoryComboBox.setAlignmentX(JComponent.LEFT_ALIGNMENT);
 228         directoryComboBox.setAlignmentY(JComponent.TOP_ALIGNMENT);
 229         directoryComboBox.setMaximumRowCount(8);
 230         topPanel.add(directoryComboBox, BorderLayout.CENTER);
 231 
 232         filePane = new FilePane(new SynthFileChooserUIAccessor());
 233         fc.addPropertyChangeListener(filePane);
 234 
 235         // Add 'Go Up' to context menu, plus 'Go Home' if on Unix
 236         JPopupMenu contextMenu = filePane.getComponentPopupMenu();
 237         if (contextMenu != null) {
 238             contextMenu.insert(getChangeToParentDirectoryAction(), 0);
 239             if (File.separatorChar == '/') {
 240                 contextMenu.insert(getGoHomeAction(), 1);
 241             }
 242         }
 243 
 244     FileSystemView fsv = fc.getFileSystemView();
 245 
 246     // Up Button
 247     JButton upFolderButton = new JButton(getChangeToParentDirectoryAction());
 248     upFolderButton.setText(null);
 249     upFolderButton.setIcon(upFolderIcon);
 250     upFolderButton.setToolTipText(upFolderToolTipText);
 251     upFolderButton.getAccessibleContext().setAccessibleName(upFolderAccessibleName);
 252     upFolderButton.setAlignmentX(JComponent.LEFT_ALIGNMENT);
 253     upFolderButton.setAlignmentY(JComponent.CENTER_ALIGNMENT);
 254     upFolderButton.setMargin(shrinkwrap);
 255 
 256     topButtonPanel.add(upFolderButton);
 257     topButtonPanel.add(Box.createRigidArea(hstrut5));
 258 
 259     // Home Button
 260     File homeDir = fsv.getHomeDirectory();
 261     String toolTipText = homeFolderToolTipText;
 262     if (fsv.isRoot(homeDir)) {
 263         toolTipText = getFileView(fc).getName(homeDir); // Probably "Desktop".
 264     }
 265 
 266     JButton b = new JButton(homeFolderIcon);
 267     b.setToolTipText(toolTipText);
 268     b.getAccessibleContext().setAccessibleName(homeFolderAccessibleName);
 269     b.setAlignmentX(JComponent.LEFT_ALIGNMENT);
 270     b.setAlignmentY(JComponent.CENTER_ALIGNMENT);
 271     b.setMargin(shrinkwrap);
 272 
 273     b.addActionListener(getGoHomeAction());
 274     topButtonPanel.add(b);
 275     topButtonPanel.add(Box.createRigidArea(hstrut5));
 276 
 277     // New Directory Button
 278     if (!readOnly) {
 279         b = new JButton(filePane.getNewFolderAction());
 280         b.setText(null);
 281         b.setIcon(newFolderIcon);
 282         b.setToolTipText(newFolderToolTipText);
 283         b.getAccessibleContext().setAccessibleName(newFolderAccessibleName);
 284         b.setAlignmentX(JComponent.LEFT_ALIGNMENT);
 285         b.setAlignmentY(JComponent.CENTER_ALIGNMENT);
 286         b.setMargin(shrinkwrap);
 287         topButtonPanel.add(b);
 288         topButtonPanel.add(Box.createRigidArea(hstrut5));
 289     }
 290 
 291     // View button group
 292     ButtonGroup viewButtonGroup = new ButtonGroup();
 293 
 294     // List Button
 295     listViewButton = new JToggleButton(listViewIcon);
 296     listViewButton.setToolTipText(listViewButtonToolTipText);
 297     listViewButton.getAccessibleContext().setAccessibleName(listViewButtonAccessibleName);
 298     listViewButton.setSelected(true);
 299     listViewButton.setAlignmentX(JComponent.LEFT_ALIGNMENT);
 300     listViewButton.setAlignmentY(JComponent.CENTER_ALIGNMENT);
 301     listViewButton.setMargin(shrinkwrap);
 302     listViewButton.addActionListener(filePane.getViewTypeAction(FilePane.VIEWTYPE_LIST));
 303     topButtonPanel.add(listViewButton);
 304     viewButtonGroup.add(listViewButton);
 305 
 306     // Details Button
 307     detailsViewButton = new JToggleButton(detailsViewIcon);
 308     detailsViewButton.setToolTipText(detailsViewButtonToolTipText);
 309     detailsViewButton.getAccessibleContext().setAccessibleName(detailsViewButtonAccessibleName);
 310     detailsViewButton.setAlignmentX(JComponent.LEFT_ALIGNMENT);
 311     detailsViewButton.setAlignmentY(JComponent.CENTER_ALIGNMENT);
 312     detailsViewButton.setMargin(shrinkwrap);
 313     detailsViewButton.addActionListener(filePane.getViewTypeAction(FilePane.VIEWTYPE_DETAILS));
 314     topButtonPanel.add(detailsViewButton);
 315     viewButtonGroup.add(detailsViewButton);
 316 
 317     filePane.addPropertyChangeListener(new PropertyChangeListener() {
 318         public void propertyChange(PropertyChangeEvent e) {
 319             if ("viewType".equals(e.getPropertyName())) {
 320                 int viewType = filePane.getViewType();
 321                 switch (viewType) {
 322                     case FilePane.VIEWTYPE_LIST:
 323                         listViewButton.setSelected(true);
 324                         break;
 325                     case FilePane.VIEWTYPE_DETAILS:
 326                         detailsViewButton.setSelected(true);
 327                         break;
 328                 }
 329             }
 330         }
 331     });
 332 
 333         // ************************************** //
 334         // ******* Add the directory pane ******* //
 335         // ************************************** //
 336         fc.add(getAccessoryPanel(), BorderLayout.AFTER_LINE_ENDS);
 337         JComponent accessory = fc.getAccessory();
 338         if (accessory != null) {
 339             getAccessoryPanel().add(accessory);
 340         }
 341         filePane.setPreferredSize(LIST_PREF_SIZE);
 342         fc.add(filePane, BorderLayout.CENTER);
 343 
 344 
 345         // ********************************** //
 346         // **** Construct the bottom panel ** //
 347         // ********************************** //
 348         bottomPanel = new JPanel();
 349         bottomPanel.setLayout(new BoxLayout(bottomPanel, BoxLayout.Y_AXIS));
 350         fc.add(bottomPanel, BorderLayout.SOUTH);
 351 
 352         // FileName label and textfield
 353         JPanel fileNamePanel = new JPanel();
 354         fileNamePanel.setLayout(new BoxLayout(fileNamePanel, BoxLayout.LINE_AXIS));
 355         bottomPanel.add(fileNamePanel);
 356         bottomPanel.add(Box.createRigidArea(new Dimension(1, 5)));
 357 
 358         fileNameLabel = new AlignedLabel();
 359         populateFileNameLabel();
 360         fileNamePanel.add(fileNameLabel);
 361 
 362         fileNameTextField = new JTextField(35) {
 363             public Dimension getMaximumSize() {
 364                 return new Dimension(Short.MAX_VALUE, super.getPreferredSize().height);
 365             }
 366         };
 367         fileNamePanel.add(fileNameTextField);
 368         fileNameLabel.setLabelFor(fileNameTextField);
 369         fileNameTextField.addFocusListener(
 370             new FocusAdapter() {
 371                 public void focusGained(FocusEvent e) {
 372                     if (!getFileChooser().isMultiSelectionEnabled()) {
 373                         filePane.clearSelection();
 374                     }
 375                 }
 376             }
 377         );
 378         if (fc.isMultiSelectionEnabled()) {
 379             setFileName(fileNameString(fc.getSelectedFiles()));
 380         } else {
 381             setFileName(fileNameString(fc.getSelectedFile()));
 382         }
 383 
 384 
 385         // Filetype label and combobox
 386         JPanel filesOfTypePanel = new JPanel();
 387         filesOfTypePanel.setLayout(new BoxLayout(filesOfTypePanel, BoxLayout.LINE_AXIS));
 388         bottomPanel.add(filesOfTypePanel);
 389 
 390         AlignedLabel filesOfTypeLabel = new AlignedLabel(filesOfTypeLabelText);
 391         filesOfTypeLabel.setDisplayedMnemonic(filesOfTypeLabelMnemonic);
 392         filesOfTypePanel.add(filesOfTypeLabel);
 393 
 394         filterComboBoxModel = createFilterComboBoxModel();
 395         fc.addPropertyChangeListener(filterComboBoxModel);
 396         filterComboBox = new JComboBox<FileFilter>(filterComboBoxModel);
 397         filterComboBox.getAccessibleContext().setAccessibleDescription(filesOfTypeLabelText);
 398         filesOfTypeLabel.setLabelFor(filterComboBox);
 399         filterComboBox.setRenderer(createFilterComboBoxRenderer());
 400         filesOfTypePanel.add(filterComboBox);
 401 
 402 
 403         // buttons
 404         buttonPanel = new JPanel();
 405         buttonPanel.setLayout(new ButtonAreaLayout());
 406 
 407         buttonPanel.add(getApproveButton(fc));
 408         buttonPanel.add(getCancelButton(fc));
 409 
 410         if (fc.getControlButtonsAreShown()) {
 411             addControlButtons();
 412         }
 413 
 414         groupLabels(new AlignedLabel[] { fileNameLabel, filesOfTypeLabel });
 415     }
 416 
 417     protected void installListeners(JFileChooser fc) {
 418         super.installListeners(fc);
 419         fc.addPropertyChangeListener(JFileChooser.FILE_SELECTION_MODE_CHANGED_PROPERTY, modeListener);
 420     }
 421 
 422     protected void uninstallListeners(JFileChooser fc) {
 423         fc.removePropertyChangeListener(JFileChooser.FILE_SELECTION_MODE_CHANGED_PROPERTY, modeListener);
 424         super.uninstallListeners(fc);
 425     }
 426 
 427     private String fileNameString(File file) {
 428         if (file == null) {
 429             return null;
 430         } else {
 431             JFileChooser fc = getFileChooser();
 432             if (fc.isDirectorySelectionEnabled() && !fc.isFileSelectionEnabled()) {
 433                 return file.getPath();
 434             } else {
 435                 return file.getName();
 436             }
 437         }
 438     }
 439 
 440     private String fileNameString(File[] files) {
 441         StringBuffer buf = new StringBuffer();
 442         for (int i = 0; files != null && i < files.length; i++) {
 443             if (i > 0) {
 444                 buf.append(" ");
 445             }
 446             if (files.length > 1) {
 447                 buf.append("\"");
 448             }
 449             buf.append(fileNameString(files[i]));
 450             if (files.length > 1) {
 451                 buf.append("\"");
 452             }
 453         }
 454         return buf.toString();
 455     }
 456 
 457     public void uninstallUI(JComponent c) {
 458         // Remove listeners
 459         c.removePropertyChangeListener(filterComboBoxModel);
 460         c.removePropertyChangeListener(filePane);
 461 
 462         if (filePane != null) {
 463             filePane.uninstallUI();
 464             filePane = null;
 465         }
 466 
 467         super.uninstallUI(c);
 468     }
 469 
 470     protected void installStrings(JFileChooser fc) {
 471         super.installStrings(fc);
 472 
 473         Locale l = fc.getLocale();
 474 
 475         lookInLabelMnemonic = getMnemonic("FileChooser.lookInLabelMnemonic", l);
 476         lookInLabelText = UIManager.getString("FileChooser.lookInLabelText", l);
 477         saveInLabelText = UIManager.getString("FileChooser.saveInLabelText", l);
 478 
 479         fileNameLabelMnemonic = getMnemonic("FileChooser.fileNameLabelMnemonic", l);
 480         fileNameLabelText = UIManager.getString("FileChooser.fileNameLabelText", l);
 481         folderNameLabelMnemonic = getMnemonic("FileChooser.folderNameLabelMnemonic", l);
 482         folderNameLabelText = UIManager.getString("FileChooser.folderNameLabelText", l);
 483 
 484         filesOfTypeLabelMnemonic = getMnemonic("FileChooser.filesOfTypeLabelMnemonic", l);
 485         filesOfTypeLabelText = UIManager.getString("FileChooser.filesOfTypeLabelText", l);
 486 
 487     upFolderToolTipText =  UIManager.getString("FileChooser.upFolderToolTipText",l);
 488     upFolderAccessibleName = UIManager.getString("FileChooser.upFolderAccessibleName",l);
 489 
 490     homeFolderToolTipText =  UIManager.getString("FileChooser.homeFolderToolTipText",l);
 491     homeFolderAccessibleName = UIManager.getString("FileChooser.homeFolderAccessibleName",l);
 492 
 493     newFolderToolTipText = UIManager.getString("FileChooser.newFolderToolTipText",l);
 494     newFolderAccessibleName = UIManager.getString("FileChooser.newFolderAccessibleName",l);
 495 
 496     listViewButtonToolTipText = UIManager.getString("FileChooser.listViewButtonToolTipText",l);
 497     listViewButtonAccessibleName = UIManager.getString("FileChooser.listViewButtonAccessibleName",l);
 498 
 499     detailsViewButtonToolTipText = UIManager.getString("FileChooser.detailsViewButtonToolTipText",l);
 500     detailsViewButtonAccessibleName = UIManager.getString("FileChooser.detailsViewButtonAccessibleName",l);
 501     }
 502 
 503     private int getMnemonic(String key, Locale l) {
 504         return SwingUtilities2.getUIDefaultsInt(key, l);
 505     }
 506 
 507 
 508     public String getFileName() {
 509         if (fileNameTextField != null) {
 510             return fileNameTextField.getText();
 511         } else {
 512             return null;
 513         }
 514     }
 515 
 516     public void setFileName(String fileName) {
 517         if (fileNameTextField != null) {
 518             fileNameTextField.setText(fileName);
 519         }
 520     }
 521 
 522     @Override public void rescanCurrentDirectory(JFileChooser fc) {
 523         filePane.rescanCurrentDirectory();
 524     }
 525 
 526     protected void doSelectedFileChanged(PropertyChangeEvent e) {
 527         super.doSelectedFileChanged(e);
 528 
 529         File f = (File) e.getNewValue();
 530         JFileChooser fc = getFileChooser();
 531         if (f != null
 532             && ((fc.isFileSelectionEnabled() && !f.isDirectory())
 533                 || (f.isDirectory() && fc.isDirectorySelectionEnabled()))) {
 534 
 535             setFileName(fileNameString(f));
 536         }
 537     }
 538 
 539     protected void doSelectedFilesChanged(PropertyChangeEvent e) {
 540         super.doSelectedFilesChanged(e);
 541 
 542         File[] files = (File[]) e.getNewValue();
 543         JFileChooser fc = getFileChooser();
 544         if (files != null
 545             && files.length > 0
 546             && (files.length > 1 || fc.isDirectorySelectionEnabled() || !files[0].isDirectory())) {
 547             setFileName(fileNameString(files));
 548         }
 549     }
 550 
 551     protected void doDirectoryChanged(PropertyChangeEvent e) {
 552         super.doDirectoryChanged(e);
 553 
 554         JFileChooser fc = getFileChooser();
 555         FileSystemView fsv = fc.getFileSystemView();
 556         File currentDirectory = fc.getCurrentDirectory();
 557 
 558         if (!readOnly && currentDirectory != null) {
 559             getNewFolderAction().setEnabled(filePane.canWrite(currentDirectory));
 560         }
 561 
 562         if (currentDirectory != null) {
 563             JComponent cb = getDirectoryComboBox();
 564             if (cb instanceof JComboBox) {
 565                 ComboBoxModel model = ((JComboBox)cb).getModel();
 566                 if (model instanceof DirectoryComboBoxModel) {
 567                     ((DirectoryComboBoxModel)model).addItem(currentDirectory);
 568                 }
 569             }
 570 
 571             if (fc.isDirectorySelectionEnabled() && !fc.isFileSelectionEnabled()) {
 572                 if (fsv.isFileSystem(currentDirectory)) {
 573                     setFileName(currentDirectory.getPath());
 574                 } else {
 575                     setFileName(null);
 576                 }
 577             }
 578         }
 579     }
 580 
 581 
 582     protected void doFileSelectionModeChanged(PropertyChangeEvent e) {
 583         super.doFileSelectionModeChanged(e);
 584 
 585         JFileChooser fc = getFileChooser();
 586         File currentDirectory = fc.getCurrentDirectory();
 587         if (currentDirectory != null
 588             && fc.isDirectorySelectionEnabled()
 589             && !fc.isFileSelectionEnabled()
 590             && fc.getFileSystemView().isFileSystem(currentDirectory)) {
 591 
 592             setFileName(currentDirectory.getPath());
 593         } else {
 594             setFileName(null);
 595         }
 596     }
 597 
 598     protected void doAccessoryChanged(PropertyChangeEvent e) {
 599         if (getAccessoryPanel() != null) {
 600             if (e.getOldValue() != null) {
 601                 getAccessoryPanel().remove((JComponent)e.getOldValue());
 602             }
 603             JComponent accessory = (JComponent)e.getNewValue();
 604             if (accessory != null) {
 605                 getAccessoryPanel().add(accessory, BorderLayout.CENTER);
 606             }
 607         }
 608     }
 609 
 610     protected void doControlButtonsChanged(PropertyChangeEvent e) {
 611         super.doControlButtonsChanged(e);
 612 
 613         if (getFileChooser().getControlButtonsAreShown()) {
 614             addControlButtons();
 615         } else {
 616             removeControlButtons();
 617         }
 618     }
 619 
 620     protected void addControlButtons() {
 621         if (bottomPanel != null) {
 622             bottomPanel.add(buttonPanel);
 623         }
 624     }
 625 
 626     protected void removeControlButtons() {
 627         if (bottomPanel != null) {
 628             bottomPanel.remove(buttonPanel);
 629         }
 630     }
 631 
 632 
 633 
 634 
 635     // *******************************************************
 636     // ************ FileChooser UI PLAF methods **************
 637     // *******************************************************
 638 
 639     protected ActionMap createActionMap() {
 640         ActionMap map = new ActionMapUIResource();
 641         // add standard actions
 642         FilePane.addActionsToMap(map, filePane.getActions());
 643         // add synth only actions
 644         map.put("fileNameCompletion", getFileNameCompletionAction());
 645         return map;
 646     }
 647 
 648     // *****************************
 649     // ***** Directory Actions *****
 650     // *****************************
 651 
 652     protected JComponent getDirectoryComboBox() {
 653         return directoryComboBox;
 654     }
 655 
 656     protected Action getDirectoryComboBoxAction() {
 657         return directoryComboBoxAction;
 658     }
 659 
 660     protected DirectoryComboBoxRenderer createDirectoryComboBoxRenderer(JFileChooser fc) {
 661         return new DirectoryComboBoxRenderer(directoryComboBox.getRenderer());
 662     }
 663 
 664     //
 665     // Renderer for DirectoryComboBox
 666     //
 667     // Synth has some odd behavior with regards to renderers. Renderers are styled
 668     // in a specific manner by the SynthComboBoxUI. If we extend DefaultListCellRenderer
 669     // here, then we get none of those benefits or behaviors, leading to poor
 670     // looking combo boxes.
 671     // So what we do here is delegate most jobs to the "real" or original renderer,
 672     // and simply monkey with the icon and text of the renderer.
 673     private class DirectoryComboBoxRenderer implements ListCellRenderer<File> {
 674         private ListCellRenderer<? super File> delegate;
 675         IndentIcon ii = new IndentIcon();
 676 
 677         private DirectoryComboBoxRenderer(ListCellRenderer<? super File> delegate) {
 678             this.delegate = delegate;
 679         }
 680 
 681         @Override
 682         public Component getListCellRendererComponent(JList<? extends File> list, File value, int index, boolean isSelected, boolean cellHasFocus) {
 683             Component c = delegate.getListCellRendererComponent(list, value, index, isSelected, cellHasFocus);
 684 
 685             assert c instanceof JLabel;
 686             JLabel label = (JLabel)c;
 687             if (value == null) {
 688                 label.setText("");
 689                 return label;
 690             }
 691             label.setText(getFileChooser().getName(value));
 692             Icon icon = getFileChooser().getIcon(value);
 693             ii.icon = icon;
 694             ii.depth = directoryComboBoxModel.getDepth(index);
 695             label.setIcon(ii);
 696 
 697             return label;
 698         }
 699     }
 700 
 701     final static int space = 10;
 702     class IndentIcon implements Icon {
 703 
 704         Icon icon = null;
 705         int depth = 0;
 706 
 707         public void paintIcon(Component c, Graphics g, int x, int y) {
 708             if (icon != null) {
 709                 if (c.getComponentOrientation().isLeftToRight()) {
 710                     icon.paintIcon(c, g, x+depth*space, y);
 711                 } else {
 712                     icon.paintIcon(c, g, x, y);
 713                 }
 714             }
 715         }
 716 
 717         public int getIconWidth() {
 718             return ((icon != null) ? icon.getIconWidth() : 0) + depth*space;
 719         }
 720 
 721         public int getIconHeight() {
 722             return (icon != null) ? icon.getIconHeight() : 0;
 723         }
 724 
 725     }
 726 
 727     //
 728     // DataModel for DirectoryComboxbox
 729     //
 730     protected DirectoryComboBoxModel createDirectoryComboBoxModel(JFileChooser fc) {
 731         return new DirectoryComboBoxModel();
 732     }
 733 
 734     /**
 735      * Data model for a type-face selection combo-box.
 736      */
 737     protected class DirectoryComboBoxModel extends AbstractListModel<File> implements ComboBoxModel<File> {
 738         Vector<File> directories = new Vector<File>();
 739         int[] depths = null;
 740         File selectedDirectory = null;
 741         JFileChooser chooser = getFileChooser();
 742         FileSystemView fsv = chooser.getFileSystemView();
 743 
 744         public DirectoryComboBoxModel() {
 745             // Add the current directory to the model, and make it the
 746             // selectedDirectory
 747             File dir = getFileChooser().getCurrentDirectory();
 748             if (dir != null) {
 749                 addItem(dir);
 750             }
 751         }
 752 
 753         /**
 754          * Adds the directory to the model and sets it to be selected,
 755          * additionally clears out the previous selected directory and
 756          * the paths leading up to it, if any.
 757          */
 758         public void addItem(File directory) {
 759 
 760             if (directory == null) {
 761                 return;
 762             }
 763 
 764             boolean useShellFolder = FilePane.usesShellFolder(chooser);
 765 
 766             int oldSize = directories.size();
 767             directories.clear();
 768             if (oldSize > 0) {
 769                 fireIntervalRemoved(this, 0, oldSize);
 770             }
 771 
 772             File[] baseFolders;
 773             if (useShellFolder) {
 774                 baseFolders = AccessController.doPrivileged(new PrivilegedAction<File[]>() {
 775                     public File[] run() {
 776                         return (File[]) ShellFolder.get("fileChooserComboBoxFolders");
 777                     }
 778                 });
 779             } else {
 780                 baseFolders = fsv.getRoots();
 781             }
 782             directories.addAll(Arrays.asList(baseFolders));
 783 
 784             // Get the canonical (full) path. This has the side
 785             // benefit of removing extraneous chars from the path,
 786             // for example /foo/bar/ becomes /foo/bar
 787             File canonical;
 788             try {
 789                 canonical = ShellFolder.getNormalizedFile(directory);
 790             } catch (IOException e) {
 791                 // Maybe drive is not ready. Can't abort here.
 792                 canonical = directory;
 793             }
 794 
 795             // create File instances of each directory leading up to the top
 796             try {
 797                 File sf = useShellFolder ? ShellFolder.getShellFolder(canonical)
 798                                          : canonical;
 799                 File f = sf;
 800                 Vector<File> path = new Vector<File>(10);
 801                 do {
 802                     path.addElement(f);
 803                 } while ((f = f.getParentFile()) != null);
 804 
 805                 int pathCount = path.size();
 806                 // Insert chain at appropriate place in vector
 807                 for (int i = 0; i < pathCount; i++) {
 808                     f = path.get(i);
 809                     if (directories.contains(f)) {
 810                         int topIndex = directories.indexOf(f);
 811                         for (int j = i-1; j >= 0; j--) {
 812                             directories.insertElementAt(path.get(j), topIndex+i-j);
 813                         }
 814                         break;
 815                     }
 816                 }
 817                 calculateDepths();
 818                 setSelectedItem(sf);
 819             } catch (FileNotFoundException ex) {
 820                 calculateDepths();
 821             }
 822         }
 823 
 824         private void calculateDepths() {
 825             depths = new int[directories.size()];
 826             for (int i = 0; i < depths.length; i++) {
 827                 File dir = directories.get(i);
 828                 File parent = dir.getParentFile();
 829                 depths[i] = 0;
 830                 if (parent != null) {
 831                     for (int j = i-1; j >= 0; j--) {
 832                         if (parent.equals(directories.get(j))) {
 833                             depths[i] = depths[j] + 1;
 834                             break;
 835                         }
 836                     }
 837                 }
 838             }
 839         }
 840 
 841         public int getDepth(int i) {
 842             return (depths != null && i >= 0 && i < depths.length) ? depths[i] : 0;
 843         }
 844 
 845         public void setSelectedItem(Object selectedDirectory) {
 846             this.selectedDirectory = (File)selectedDirectory;
 847             fireContentsChanged(this, -1, -1);
 848         }
 849 
 850         public Object getSelectedItem() {
 851             return selectedDirectory;
 852         }
 853 
 854         public int getSize() {
 855             return directories.size();
 856         }
 857 
 858         public File getElementAt(int index) {
 859             return directories.elementAt(index);
 860         }
 861     }
 862 
 863     /**
 864      * Acts when DirectoryComboBox has changed the selected item.
 865      */
 866     protected class DirectoryComboBoxAction extends AbstractAction {
 867         protected DirectoryComboBoxAction() {
 868             super("DirectoryComboBoxAction");
 869         }
 870 
 871         public void actionPerformed(ActionEvent e) {
 872             directoryComboBox.hidePopup();
 873             JComponent cb = getDirectoryComboBox();
 874             if (cb instanceof JComboBox) {
 875                 File f = (File)((JComboBox)cb).getSelectedItem();
 876                 getFileChooser().setCurrentDirectory(f);
 877             }
 878         }
 879     }
 880 
 881     //
 882     // Renderer for Types ComboBox
 883     //
 884     protected FilterComboBoxRenderer createFilterComboBoxRenderer() {
 885         return new FilterComboBoxRenderer(filterComboBox.getRenderer());
 886     }
 887 
 888     /**
 889      * Render different type sizes and styles.
 890      */
 891     public class FilterComboBoxRenderer implements ListCellRenderer<FileFilter> {
 892         private ListCellRenderer<? super FileFilter> delegate;
 893         private FilterComboBoxRenderer(ListCellRenderer<? super FileFilter> delegate) {
 894             this.delegate = delegate;
 895         }
 896 
 897         public Component getListCellRendererComponent(JList<? extends FileFilter> list, FileFilter value, int index,
 898                                                       boolean isSelected, boolean cellHasFocus) {
 899             Component c = delegate.getListCellRendererComponent(list, value, index, isSelected, cellHasFocus);
 900 
 901             String text = null;
 902             if (value != null) {
 903                 text = value.getDescription();
 904             }
 905 
 906             //this should always be true, since SynthComboBoxUI's SynthComboBoxRenderer
 907             //extends JLabel
 908             assert c instanceof JLabel;
 909             if (text != null) {
 910                 ((JLabel)c).setText(text);
 911             }
 912             return c;
 913         }
 914     }
 915 
 916     //
 917     // DataModel for Types Comboxbox
 918     //
 919     protected FilterComboBoxModel createFilterComboBoxModel() {
 920         return new FilterComboBoxModel();
 921     }
 922 
 923     /**
 924      * Data model for a type-face selection combo-box.
 925      */
 926     protected class FilterComboBoxModel extends AbstractFilterComboBoxModel {
 927         protected JFileChooser getFileChooser() {
 928             return SynthFileChooserUIImpl.this.getFileChooser();
 929         }
 930     }
 931 
 932 
 933 
 934     /**
 935      * <code>ButtonAreaLayout</code> behaves in a similar manner to
 936      * <code>FlowLayout</code>. It lays out all components from left to
 937      * right, flushed right. The widths of all components will be set
 938      * to the largest preferred size width.
 939      */
 940     private static class ButtonAreaLayout implements LayoutManager {
 941         private int hGap = 5;
 942         private int topMargin = 17;
 943 
 944         public void addLayoutComponent(String string, Component comp) {
 945         }
 946 
 947         public void layoutContainer(Container container) {
 948             Component[] children = container.getComponents();
 949 
 950             if (children != null && children.length > 0) {
 951                 int         numChildren = children.length;
 952                 Dimension[] sizes = new Dimension[numChildren];
 953                 Insets      insets = container.getInsets();
 954                 int         yLocation = insets.top + topMargin;
 955                 int         maxWidth = 0;
 956 
 957                 for (int counter = 0; counter < numChildren; counter++) {
 958                     sizes[counter] = children[counter].getPreferredSize();
 959                     maxWidth = Math.max(maxWidth, sizes[counter].width);
 960                 }
 961                 int xLocation, xOffset;
 962                 if (container.getComponentOrientation().isLeftToRight()) {
 963                     xLocation = container.getSize().width - insets.left - maxWidth;
 964                     xOffset = hGap + maxWidth;
 965                 } else {
 966                     xLocation = insets.left;
 967                     xOffset = -(hGap + maxWidth);
 968                 }
 969                 for (int counter = numChildren - 1; counter >= 0; counter--) {
 970                     children[counter].setBounds(xLocation, yLocation,
 971                                                 maxWidth, sizes[counter].height);
 972                     xLocation -= xOffset;
 973                 }
 974             }
 975         }
 976 
 977         public Dimension minimumLayoutSize(Container c) {
 978             if (c != null) {
 979                 Component[] children = c.getComponents();
 980 
 981                 if (children != null && children.length > 0) {
 982                     int       numChildren = children.length;
 983                     int       height = 0;
 984                     Insets    cInsets = c.getInsets();
 985                     int       extraHeight = topMargin + cInsets.top + cInsets.bottom;
 986                     int       extraWidth = cInsets.left + cInsets.right;
 987                     int       maxWidth = 0;
 988 
 989                     for (int counter = 0; counter < numChildren; counter++) {
 990                         Dimension aSize = children[counter].getPreferredSize();
 991                         height = Math.max(height, aSize.height);
 992                         maxWidth = Math.max(maxWidth, aSize.width);
 993                     }
 994                     return new Dimension(extraWidth + numChildren * maxWidth +
 995                                          (numChildren - 1) * hGap,
 996                                          extraHeight + height);
 997                 }
 998             }
 999             return new Dimension(0, 0);
1000         }
1001 
1002         public Dimension preferredLayoutSize(Container c) {
1003             return minimumLayoutSize(c);
1004         }
1005 
1006         public void removeLayoutComponent(Component c) { }
1007     }
1008 
1009     private static void groupLabels(AlignedLabel[] group) {
1010         for (int i = 0; i < group.length; i++) {
1011             group[i].group = group;
1012         }
1013     }
1014 
1015     private class AlignedLabel extends JLabel {
1016         private AlignedLabel[] group;
1017         private int maxWidth = 0;
1018 
1019         AlignedLabel() {
1020             super();
1021             setAlignmentX(JComponent.LEFT_ALIGNMENT);
1022         }
1023 
1024         AlignedLabel(String text) {
1025             super(text);
1026             setAlignmentX(JComponent.LEFT_ALIGNMENT);
1027         }
1028 
1029         public Dimension getPreferredSize() {
1030             Dimension d = super.getPreferredSize();
1031             // Align the width with all other labels in group.
1032             return new Dimension(getMaxWidth() + 11, d.height);
1033         }
1034 
1035         private int getMaxWidth() {
1036             if (maxWidth == 0 && group != null) {
1037                 int max = 0;
1038                 for (int i = 0; i < group.length; i++) {
1039                     max = Math.max(group[i].getSuperPreferredWidth(), max);
1040                 }
1041                 for (int i = 0; i < group.length; i++) {
1042                     group[i].maxWidth = max;
1043                 }
1044             }
1045             return maxWidth;
1046         }
1047 
1048         private int getSuperPreferredWidth() {
1049             return super.getPreferredSize().width;
1050         }
1051     }
1052 }