1 /*
   2  * Copyright (c) 2002, 2014, Oracle and/or its affiliates. All rights reserved.
   3  * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER.
   4  *
   5  * This code is free software; you can redistribute it and/or modify it
   6  * under the terms of the GNU General Public License version 2 only, as
   7  * published by the Free Software Foundation.  Oracle designates this
   8  * particular file as subject to the "Classpath" exception as provided
   9  * by Oracle in the LICENSE file that accompanied this code.
  10  *
  11  * This code is distributed in the hope that it will be useful, but WITHOUT
  12  * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
  13  * FITNESS FOR A PARTICULAR PURPOSE.  See the GNU General Public License
  14  * version 2 for more details (a copy is included in the LICENSE file that
  15  * accompanied this code).
  16  *
  17  * You should have received a copy of the GNU General Public License version
  18  * 2 along with this work; if not, write to the Free Software Foundation,
  19  * Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA.
  20  *
  21  * Please contact Oracle, 500 Oracle Parkway, Redwood Shores, CA 94065 USA
  22  * or visit www.oracle.com if you need additional information or have any
  23  * questions.
  24  */
  25 package com.sun.java.swing.plaf.gtk;
  26 
  27 import java.awt.*;
  28 import java.awt.event.*;
  29 import java.beans.*;
  30 import java.io.File;
  31 import java.io.IOException;
  32 import java.text.MessageFormat;
  33 import java.util.*;
  34 
  35 import javax.swing.*;
  36 import javax.swing.border.*;
  37 import javax.swing.filechooser.*;
  38 import javax.swing.event.*;
  39 import javax.swing.plaf.*;
  40 import javax.swing.plaf.basic.BasicDirectoryModel;
  41 import javax.swing.table.*;
  42 import javax.accessibility.*;
  43 
  44 import sun.swing.SwingUtilities2;
  45 
  46 import sun.swing.plaf.synth.*;
  47 import sun.swing.FilePane;
  48 import sun.awt.shell.ShellFolder;
  49 
  50 /**
  51  * GTK FileChooserUI.
  52  *
  53  * @author Leif Samuelsson
  54  * @author Jeff Dinkins
  55  */
  56 class GTKFileChooserUI extends SynthFileChooserUI {
  57 
  58     // The accessoryPanel is a container to place the JFileChooser accessory component
  59     private JPanel accessoryPanel = null;
  60 
  61     private String newFolderButtonText = null;
  62     private String newFolderErrorSeparator = null;
  63     private String newFolderErrorText = null;
  64     private String newFolderDialogText = null;
  65     private String newFolderNoDirectoryErrorTitleText = null;
  66     private String newFolderNoDirectoryErrorText = null;
  67 
  68     private String deleteFileButtonText = null;
  69     private String renameFileButtonText = null;
  70 
  71     private String newFolderButtonToolTipText = null;
  72     private String deleteFileButtonToolTipText = null;
  73     private String renameFileButtonToolTipText = null;
  74 
  75     private int newFolderButtonMnemonic = 0;
  76     private int deleteFileButtonMnemonic = 0;
  77     private int renameFileButtonMnemonic = 0;
  78     private int foldersLabelMnemonic = 0;
  79     private int filesLabelMnemonic = 0;
  80 
  81     private String renameFileDialogText = null;
  82     private String renameFileErrorTitle = null;
  83     private String renameFileErrorText = null;
  84 
  85     private JComboBox<FileFilter> filterComboBox;
  86     private FilterComboBoxModel filterComboBoxModel;
  87 
  88     // From Motif
  89 
  90     private JPanel rightPanel;
  91     private JList<File> directoryList;
  92     private JList<File> fileList;
  93 
  94     private JLabel pathField;
  95     private JTextField fileNameTextField;
  96 
  97     private static final Dimension hstrut3 = new Dimension(3, 1);
  98     private static final Dimension vstrut10 = new Dimension(1, 10);
  99 
 100     private static Dimension prefListSize = new Dimension(75, 150);
 101 
 102     private static Dimension PREF_SIZE = new Dimension(435, 360);
 103     private static Dimension MIN_SIZE = new Dimension(200, 300);
 104 
 105     private static Dimension ZERO_ACC_SIZE = new Dimension(1, 1);
 106 
 107     private static Dimension MAX_SIZE = new Dimension(Short.MAX_VALUE, Short.MAX_VALUE);
 108 
 109     private static final Insets buttonMargin = new Insets(3, 3, 3, 3);
 110 
 111     private String filesLabelText = null;
 112     private String foldersLabelText = null;
 113     private String pathLabelText = null;
 114     private String filterLabelText = null;
 115 
 116     private int pathLabelMnemonic = 0;
 117     private int filterLabelMnemonic = 0;
 118 
 119     private JComboBox<File> directoryComboBox;
 120     private DirectoryComboBoxModel directoryComboBoxModel;
 121     private Action directoryComboBoxAction = new DirectoryComboBoxAction();
 122     private JPanel bottomButtonPanel;
 123     private GTKDirectoryModel model = null;
 124     private Action newFolderAction;
 125     private boolean readOnly;
 126     private boolean showDirectoryIcons;
 127     private boolean showFileIcons;
 128     private GTKFileView fileView = new GTKFileView();
 129     private PropertyChangeListener gtkFCPropertyChangeListener;
 130     private Action approveSelectionAction = new GTKApproveSelectionAction();
 131     private GTKDirectoryListModel directoryListModel;
 132 
 133     public GTKFileChooserUI(JFileChooser filechooser) {
 134         super(filechooser);
 135     }
 136 
 137     protected ActionMap createActionMap() {
 138         ActionMap map = new ActionMapUIResource();
 139         map.put("approveSelection", getApproveSelectionAction());
 140         map.put("cancelSelection", getCancelSelectionAction());
 141         map.put("Go Up", getChangeToParentDirectoryAction());
 142         map.put("fileNameCompletion", getFileNameCompletionAction());
 143         return map;
 144     }
 145 
 146     public String getFileName() {
 147         JFileChooser fc = getFileChooser();
 148         String typedInName = fileNameTextField != null ?
 149             fileNameTextField.getText() : null;
 150 
 151         if (!fc.isMultiSelectionEnabled()) {
 152             return typedInName;
 153         }
 154 
 155         int mode = fc.getFileSelectionMode();
 156         JList<File> list = mode == JFileChooser.DIRECTORIES_ONLY ?
 157             directoryList : fileList;
 158         Object[] files = list.getSelectedValues();
 159         int len = files.length;
 160         Vector<String> result = new Vector<String>(len + 1);
 161 
 162         // we return all selected file names
 163         for (int i = 0; i < len; i++) {
 164             File file = (File)files[i];
 165             result.add(file.getName());
 166         }
 167         // plus the file name typed into the text field, if not already there
 168         if (typedInName != null && !result.contains(typedInName)) {
 169             result.add(typedInName);
 170         }
 171 
 172         StringBuilder sb = new StringBuilder();
 173         len = result.size();
 174 
 175         // construct the resulting string
 176         for (int i=0; i<len; i++) {
 177             if (i > 0) {
 178                 sb.append(" ");
 179             }
 180             if (len > 1) {
 181                 sb.append("\"");
 182             }
 183             sb.append(result.get(i));
 184             if (len > 1) {
 185                 sb.append("\"");
 186             }
 187         }
 188         return sb.toString();
 189     }
 190 
 191     public void setFileName(String fileName) {
 192         if (fileNameTextField != null) {
 193             fileNameTextField.setText(fileName);
 194         }
 195     }
 196 
 197 //     public String getDirectoryName() {
 198 //      return pathField.getText();
 199 //     }
 200 
 201     public void setDirectoryName(String dirname) {
 202         pathField.setText(dirname);
 203     }
 204 
 205     public void ensureFileIsVisible(JFileChooser fc, File f) {
 206         // PENDING
 207     }
 208 
 209     public void rescanCurrentDirectory(JFileChooser fc) {
 210         getModel().validateFileCache();
 211     }
 212 
 213     public JPanel getAccessoryPanel() {
 214         return accessoryPanel;
 215     }
 216 
 217     // ***********************
 218     // * FileView operations *
 219     // ***********************
 220 
 221     public FileView getFileView(JFileChooser fc) {
 222         return fileView;
 223     }
 224 
 225     private class GTKFileView extends BasicFileView {
 226         public GTKFileView() {
 227             iconCache = null;
 228         }
 229 
 230         public void clearIconCache() {
 231         }
 232 
 233         public Icon getCachedIcon(File f) {
 234             return null;
 235         }
 236 
 237         public void cacheIcon(File f, Icon i) {
 238         }
 239 
 240         public Icon getIcon(File f) {
 241             return (f != null && f.isDirectory()) ? directoryIcon : fileIcon;
 242         }
 243     }
 244 
 245 
 246     private void updateDefaultButton() {
 247         JFileChooser filechooser = getFileChooser();
 248         JRootPane root = SwingUtilities.getRootPane(filechooser);
 249         if (root == null) {
 250             return;
 251         }
 252 
 253         if (filechooser.getControlButtonsAreShown()) {
 254             if (root.getDefaultButton() == null) {
 255                 root.setDefaultButton(getApproveButton(filechooser));
 256                 getCancelButton(filechooser).setDefaultCapable(false);
 257             }
 258         } else {
 259             if (root.getDefaultButton() == getApproveButton(filechooser)) {
 260                 root.setDefaultButton(null);
 261             }
 262         }
 263     }
 264 
 265     protected void doSelectedFileChanged(PropertyChangeEvent e) {
 266         super.doSelectedFileChanged(e);
 267         File f = (File) e.getNewValue();
 268         if (f != null) {
 269             setFileName(getFileChooser().getName(f));
 270         }
 271     }
 272 
 273     protected void doDirectoryChanged(PropertyChangeEvent e) {
 274         directoryList.clearSelection();
 275         ListSelectionModel sm = directoryList.getSelectionModel();
 276         if (sm instanceof DefaultListSelectionModel) {
 277             ((DefaultListSelectionModel)sm).moveLeadSelectionIndex(0);
 278             sm.setAnchorSelectionIndex(0);
 279         }
 280         fileList.clearSelection();
 281         sm = fileList.getSelectionModel();
 282         if (sm instanceof DefaultListSelectionModel) {
 283             ((DefaultListSelectionModel)sm).moveLeadSelectionIndex(0);
 284             sm.setAnchorSelectionIndex(0);
 285         }
 286 
 287         File currentDirectory = getFileChooser().getCurrentDirectory();
 288         if (currentDirectory != null) {
 289             try {
 290                 setDirectoryName(ShellFolder.getNormalizedFile((File)e.getNewValue()).getPath());
 291             } catch (IOException ioe) {
 292                 setDirectoryName(((File)e.getNewValue()).getAbsolutePath());
 293             }
 294             if ((getFileChooser().getFileSelectionMode() == JFileChooser.DIRECTORIES_ONLY) && !getFileChooser().isMultiSelectionEnabled()) {
 295                 setFileName(pathField.getText());
 296             }
 297             directoryComboBoxModel.addItem(currentDirectory);
 298             directoryListModel.directoryChanged();
 299         }
 300         super.doDirectoryChanged(e);
 301     }
 302 
 303     protected void doAccessoryChanged(PropertyChangeEvent e) {
 304         if (getAccessoryPanel() != null) {
 305             if (e.getOldValue() != null) {
 306                 getAccessoryPanel().remove((JComponent)e.getOldValue());
 307             }
 308             JComponent accessory = (JComponent)e.getNewValue();
 309             if (accessory != null) {
 310                 getAccessoryPanel().add(accessory, BorderLayout.CENTER);
 311                 getAccessoryPanel().setPreferredSize(accessory.getPreferredSize());
 312                 getAccessoryPanel().setMaximumSize(MAX_SIZE);
 313             } else {
 314                 getAccessoryPanel().setPreferredSize(ZERO_ACC_SIZE);
 315                 getAccessoryPanel().setMaximumSize(ZERO_ACC_SIZE);
 316             }
 317         }
 318     }
 319 
 320     protected void doFileSelectionModeChanged(PropertyChangeEvent e) {
 321         directoryList.clearSelection();
 322         rightPanel.setVisible(((Integer)e.getNewValue()).intValue() != JFileChooser.DIRECTORIES_ONLY);
 323 
 324         super.doFileSelectionModeChanged(e);
 325     }
 326 
 327     protected void doMultiSelectionChanged(PropertyChangeEvent e) {
 328         if (getFileChooser().isMultiSelectionEnabled()) {
 329             fileList.setSelectionMode(ListSelectionModel.MULTIPLE_INTERVAL_SELECTION);
 330         } else {
 331             fileList.setSelectionMode(ListSelectionModel.SINGLE_SELECTION);
 332             fileList.clearSelection();
 333         }
 334 
 335         super.doMultiSelectionChanged(e);
 336     }
 337 
 338     protected void doControlButtonsChanged(PropertyChangeEvent e) {
 339         super.doControlButtonsChanged(e);
 340 
 341         JFileChooser filechooser = getFileChooser();
 342         if (filechooser.getControlButtonsAreShown()) {
 343             filechooser.add(bottomButtonPanel, BorderLayout.SOUTH);
 344         } else {
 345             filechooser.remove(bottomButtonPanel);
 346         }
 347         updateDefaultButton();
 348     }
 349 
 350     protected void doAncestorChanged(PropertyChangeEvent e) {
 351         if (e.getOldValue() == null && e.getNewValue() != null) {
 352             // Ancestor was added, set initial focus
 353             fileNameTextField.selectAll();
 354             fileNameTextField.requestFocus();
 355             updateDefaultButton();
 356         }
 357 
 358         super.doAncestorChanged(e);
 359     }
 360 
 361 
 362 
 363     // ********************************************
 364     // ************ Create Listeners **************
 365     // ********************************************
 366 
 367     public ListSelectionListener createListSelectionListener(JFileChooser fc) {
 368         return new SelectionListener();
 369     }
 370 
 371     class DoubleClickListener extends MouseAdapter {
 372         JList<?> list;
 373         public  DoubleClickListener(JList<?> list) {
 374             this.list = list;
 375         }
 376 
 377         public void mouseClicked(MouseEvent e) {
 378             if (SwingUtilities.isLeftMouseButton(e) && e.getClickCount() == 2) {
 379                 int index = list.locationToIndex(e.getPoint());
 380                 if (index >= 0) {
 381                     File f = (File) list.getModel().getElementAt(index);
 382                     try {
 383                         // Strip trailing ".."
 384                         f = ShellFolder.getNormalizedFile(f);
 385                     } catch (IOException ex) {
 386                         // That's ok, we'll use f as is
 387                     }
 388                     if (getFileChooser().isTraversable(f)) {
 389                         list.clearSelection();
 390                         if (getFileChooser().getCurrentDirectory().equals(f)){
 391                             rescanCurrentDirectory(getFileChooser());
 392                         } else {
 393                             getFileChooser().setCurrentDirectory(f);
 394                         }
 395                     } else {
 396                         getFileChooser().approveSelection();
 397                     }
 398                 }
 399             }
 400         }
 401 
 402         public void mouseEntered(MouseEvent evt) {
 403             if (list != null) {
 404                 TransferHandler th1 = getFileChooser().getTransferHandler();
 405                 TransferHandler th2 = list.getTransferHandler();
 406                 if (th1 != th2) {
 407                     list.setTransferHandler(th1);
 408                 }
 409                 if (getFileChooser().getDragEnabled() != list.getDragEnabled()) {
 410                     list.setDragEnabled(getFileChooser().getDragEnabled());
 411                 }
 412             }
 413         }
 414     }
 415 
 416     protected MouseListener createDoubleClickListener(JFileChooser fc, JList<?> list) {
 417         return new DoubleClickListener(list);
 418     }
 419 
 420 
 421 
 422     protected class SelectionListener implements ListSelectionListener {
 423         public void valueChanged(ListSelectionEvent e) {
 424             if (!e.getValueIsAdjusting()) {
 425                 JFileChooser chooser = getFileChooser();
 426                 JList<?> list = (JList) e.getSource();
 427 
 428                 if (chooser.isMultiSelectionEnabled()) {
 429                     File[] files = null;
 430                     Object[] objects = list.getSelectedValues();
 431                     if (objects != null) {
 432                         if (objects.length == 1
 433                             && ((File)objects[0]).isDirectory()
 434                             && chooser.isTraversable(((File)objects[0]))
 435                             && (chooser.getFileSelectionMode() != JFileChooser.DIRECTORIES_ONLY
 436                                 || !chooser.getFileSystemView().isFileSystem(((File)objects[0])))) {
 437                             setDirectorySelected(true);
 438                             setDirectory(((File)objects[0]));
 439                         } else {
 440                             ArrayList<File> fList = new ArrayList<File>(objects.length);
 441                             for (Object object : objects) {
 442                                 File f = (File) object;
 443                                 if ((chooser.isFileSelectionEnabled() && f.isFile())
 444                                     || (chooser.isDirectorySelectionEnabled() && f.isDirectory())) {
 445                                     fList.add(f);
 446                                 }
 447                             }
 448                             if (fList.size() > 0) {
 449                                 files = fList.toArray(new File[fList.size()]);
 450                             }
 451                             setDirectorySelected(false);
 452                         }
 453                     }
 454                     chooser.setSelectedFiles(files);
 455                 } else {
 456                     File file = (File)list.getSelectedValue();
 457                     if (file != null
 458                         && file.isDirectory()
 459                         && chooser.isTraversable(file)
 460                         && (chooser.getFileSelectionMode() == JFileChooser.FILES_ONLY
 461                             || !chooser.getFileSystemView().isFileSystem(file))) {
 462 
 463                         setDirectorySelected(true);
 464                         setDirectory(file);
 465                     } else {
 466                         setDirectorySelected(false);
 467                         if (file != null) {
 468                             chooser.setSelectedFile(file);
 469                         }
 470                     }
 471                 }
 472             }
 473         }
 474     }
 475 
 476 
 477     //
 478     // ComponentUI Interface Implementation methods
 479     //
 480     public static ComponentUI createUI(JComponent c) {
 481         return new GTKFileChooserUI((JFileChooser)c);
 482     }
 483 
 484     public void installUI(JComponent c) {
 485         accessoryPanel = new JPanel(new BorderLayout(10, 10));
 486         accessoryPanel.setName("GTKFileChooser.accessoryPanel");
 487 
 488         super.installUI(c);
 489     }
 490 
 491     public void uninstallUI(JComponent c) {
 492         c.removePropertyChangeListener(filterComboBoxModel);
 493         super.uninstallUI(c);
 494 
 495         if (accessoryPanel != null) {
 496             accessoryPanel.removeAll();
 497         }
 498         accessoryPanel = null;
 499         getFileChooser().removeAll();
 500     }
 501 
 502     public void installComponents(JFileChooser fc) {
 503         super.installComponents(fc);
 504 
 505         boolean leftToRight = fc.getComponentOrientation().isLeftToRight();
 506 
 507         fc.setLayout(new BorderLayout());
 508         fc.setAlignmentX(JComponent.CENTER_ALIGNMENT);
 509 
 510         // Top row of buttons
 511         JPanel topButtonPanel = new JPanel(new FlowLayout(FlowLayout.LEADING, 0, 0));
 512         topButtonPanel.setBorder(new EmptyBorder(10, 10, 0, 10));
 513         topButtonPanel.setName("GTKFileChooser.topButtonPanel");
 514 
 515         if (!UIManager.getBoolean("FileChooser.readOnly")) {
 516             JButton newFolderButton = new JButton(getNewFolderAction());
 517             newFolderButton.setName("GTKFileChooser.newFolderButton");
 518             newFolderButton.setMnemonic(newFolderButtonMnemonic);
 519             newFolderButton.setToolTipText(newFolderButtonToolTipText);
 520             newFolderButton.setText(newFolderButtonText);
 521             topButtonPanel.add(newFolderButton);
 522         }
 523         JButton deleteFileButton = new JButton(deleteFileButtonText);
 524         deleteFileButton.setName("GTKFileChooser.deleteFileButton");
 525         deleteFileButton.setMnemonic(deleteFileButtonMnemonic);
 526         deleteFileButton.setToolTipText(deleteFileButtonToolTipText);
 527         deleteFileButton.setEnabled(false);
 528         topButtonPanel.add(deleteFileButton);
 529 
 530         RenameFileAction rfa = new RenameFileAction();
 531         JButton renameFileButton = new JButton(rfa);
 532         if (readOnly) {
 533             rfa.setEnabled(false);
 534         }
 535         renameFileButton.setText(renameFileButtonText);
 536         renameFileButton.setName("GTKFileChooser.renameFileButton");
 537         renameFileButton.setMnemonic(renameFileButtonMnemonic);
 538         renameFileButton.setToolTipText(renameFileButtonToolTipText);
 539         topButtonPanel.add(renameFileButton);
 540 
 541         fc.add(topButtonPanel, BorderLayout.NORTH);
 542 
 543 
 544         JPanel interior = new JPanel();
 545         interior.setBorder(new EmptyBorder(0, 10, 10, 10));
 546         interior.setName("GTKFileChooser.interiorPanel");
 547         align(interior);
 548         interior.setLayout(new BoxLayout(interior, BoxLayout.PAGE_AXIS));
 549 
 550         fc.add(interior, BorderLayout.CENTER);
 551 
 552         @SuppressWarnings("serial") // anonymous class
 553         JPanel comboBoxPanel = new JPanel(new FlowLayout(FlowLayout.CENTER,
 554                                                          0, 0) {
 555             public void layoutContainer(Container target) {
 556                 super.layoutContainer(target);
 557                 JComboBox<?> comboBox = directoryComboBox;
 558                 if (comboBox.getWidth() > target.getWidth()) {
 559                     comboBox.setBounds(0, comboBox.getY(), target.getWidth(),
 560                                        comboBox.getHeight());
 561                 }
 562             }
 563         });
 564         comboBoxPanel.setBorder(new EmptyBorder(0, 0, 4, 0));
 565         comboBoxPanel.setName("GTKFileChooser.directoryComboBoxPanel");
 566         // CurrentDir ComboBox
 567         directoryComboBoxModel = createDirectoryComboBoxModel(fc);
 568         directoryComboBox = new JComboBox<>(directoryComboBoxModel);
 569         directoryComboBox.setName("GTKFileChooser.directoryComboBox");
 570         directoryComboBox.putClientProperty( "JComboBox.lightweightKeyboardNavigation", "Lightweight" );
 571         directoryComboBox.addActionListener(directoryComboBoxAction);
 572         directoryComboBox.setMaximumRowCount(8);
 573         comboBoxPanel.add(directoryComboBox);
 574         interior.add(comboBoxPanel);
 575 
 576 
 577         // CENTER: left, right, accessory
 578         JPanel centerPanel = new JPanel(new BorderLayout());
 579         centerPanel.setName("GTKFileChooser.centerPanel");
 580 
 581         // SPLIT PANEL: left, right
 582         JSplitPane splitPanel = new JSplitPane();
 583         splitPanel.setName("GTKFileChooser.splitPanel");
 584         splitPanel.setDividerLocation((PREF_SIZE.width-8)/2);
 585 
 586         // left panel - Filter & directoryList
 587         JPanel leftPanel = new JPanel(new GridBagLayout());
 588         leftPanel.setName("GTKFileChooser.directoryListPanel");
 589 
 590         // Add the Directory List
 591         // Create a label that looks like button (should be a table header)
 592         TableCellRenderer headerRenderer = new JTableHeader().getDefaultRenderer();
 593         JLabel directoryListLabel =
 594             (JLabel)headerRenderer.getTableCellRendererComponent(null, foldersLabelText,
 595                                                                      false, false, 0, 0);
 596         directoryListLabel.setName("GTKFileChooser.directoryListLabel");
 597         leftPanel.add(directoryListLabel, new GridBagConstraints(
 598                           0, 0, 1, 1, 1, 0, GridBagConstraints.WEST,
 599                           GridBagConstraints.HORIZONTAL,
 600                           new Insets(0, 0, 0, 0), 0, 0));
 601         leftPanel.add(createDirectoryList(), new GridBagConstraints(
 602                           0, 1, 1, 1, 1, 1, GridBagConstraints.EAST,
 603                           GridBagConstraints.BOTH,
 604                           new Insets(0, 0, 0, 0), 0, 0));
 605         directoryListLabel.setDisplayedMnemonic(foldersLabelMnemonic);
 606         directoryListLabel.setLabelFor(directoryList);
 607 
 608         // create files list
 609         rightPanel = new JPanel(new GridBagLayout());
 610         rightPanel.setName("GTKFileChooser.fileListPanel");
 611 
 612         headerRenderer = new JTableHeader().getDefaultRenderer();
 613         JLabel fileListLabel =
 614             (JLabel)headerRenderer.getTableCellRendererComponent(null, filesLabelText,
 615                                                                      false, false, 0, 0);
 616         fileListLabel.setName("GTKFileChooser.fileListLabel");
 617         rightPanel.add(fileListLabel, new GridBagConstraints(
 618                           0, 0, 1, 1, 1, 0, GridBagConstraints.WEST,
 619                           GridBagConstraints.HORIZONTAL,
 620                           new Insets(0, 0, 0, 0), 0, 0));
 621         rightPanel.add(createFilesList(), new GridBagConstraints(
 622                           0, 1, 1, 1, 1, 1, GridBagConstraints.EAST,
 623                           GridBagConstraints.BOTH,
 624                           new Insets(0, 0, 0, 0), 0, 0));
 625         fileListLabel.setDisplayedMnemonic(filesLabelMnemonic);
 626         fileListLabel.setLabelFor(fileList);
 627 
 628         splitPanel.add(leftPanel,  leftToRight ? JSplitPane.LEFT : JSplitPane.RIGHT);
 629         splitPanel.add(rightPanel, leftToRight ? JSplitPane.RIGHT : JSplitPane.LEFT);
 630         centerPanel.add(splitPanel, BorderLayout.CENTER);
 631 
 632         JComponent accessoryPanel = getAccessoryPanel();
 633         JComponent accessory = fc.getAccessory();
 634         if (accessoryPanel != null) {
 635             if (accessory == null) {
 636                 accessoryPanel.setPreferredSize(ZERO_ACC_SIZE);
 637                 accessoryPanel.setMaximumSize(ZERO_ACC_SIZE);
 638             } else {
 639                 getAccessoryPanel().add(accessory, BorderLayout.CENTER);
 640                 accessoryPanel.setPreferredSize(accessory.getPreferredSize());
 641                 accessoryPanel.setMaximumSize(MAX_SIZE);
 642             }
 643             align(accessoryPanel);
 644             centerPanel.add(accessoryPanel, BorderLayout.AFTER_LINE_ENDS);
 645         }
 646         interior.add(centerPanel);
 647         interior.add(Box.createRigidArea(vstrut10));
 648 
 649         JPanel pathFieldPanel = new JPanel(new FlowLayout(FlowLayout.LEADING,
 650                                                           0, 0));
 651         pathFieldPanel.setBorder(new EmptyBorder(0, 0, 4, 0));
 652         JLabel pathFieldLabel = new JLabel(pathLabelText);
 653         pathFieldLabel.setName("GTKFileChooser.pathFieldLabel");
 654         pathFieldLabel.setDisplayedMnemonic(pathLabelMnemonic);
 655         align(pathFieldLabel);
 656         pathFieldPanel.add(pathFieldLabel);
 657         pathFieldPanel.add(Box.createRigidArea(hstrut3));
 658 
 659         File currentDirectory = fc.getCurrentDirectory();
 660         String curDirName = null;
 661         if (currentDirectory != null) {
 662             curDirName = currentDirectory.getPath();
 663         }
 664         @SuppressWarnings("serial") // anonymous class
 665         JLabel tmp = new JLabel(curDirName) {
 666             public Dimension getMaximumSize() {
 667                 Dimension d = super.getMaximumSize();
 668                 d.height = getPreferredSize().height;
 669                 return d;
 670             }
 671         };
 672         pathField =  tmp;
 673         pathField.setName("GTKFileChooser.pathField");
 674         align(pathField);
 675         pathFieldPanel.add(pathField);
 676         interior.add(pathFieldPanel);
 677 
 678         // add the fileName field
 679         @SuppressWarnings("serial") // anonymous class
 680         JTextField tmp2 = new JTextField() {
 681             public Dimension getMaximumSize() {
 682                 Dimension d = super.getMaximumSize();
 683                 d.height = getPreferredSize().height;
 684                 return d;
 685             }
 686         };
 687         fileNameTextField = tmp2;
 688 
 689         pathFieldLabel.setLabelFor(fileNameTextField);
 690 
 691         Set<AWTKeyStroke> forwardTraversalKeys = fileNameTextField.getFocusTraversalKeys(
 692             KeyboardFocusManager.FORWARD_TRAVERSAL_KEYS);
 693         forwardTraversalKeys = new HashSet<AWTKeyStroke>(forwardTraversalKeys);
 694         forwardTraversalKeys.remove(KeyStroke.getKeyStroke(KeyEvent.VK_TAB, 0));
 695         fileNameTextField.setFocusTraversalKeys(KeyboardFocusManager.FORWARD_TRAVERSAL_KEYS, forwardTraversalKeys);
 696 
 697         fileNameTextField.setName("GTKFileChooser.fileNameTextField");
 698         fileNameTextField.getActionMap().put("fileNameCompletionAction", getFileNameCompletionAction());
 699         fileNameTextField.getInputMap().put(KeyStroke.getKeyStroke(KeyEvent.VK_TAB, 0), "fileNameCompletionAction");
 700         interior.add(fileNameTextField);
 701 
 702         // Add the filter combo box
 703         JPanel panel = new JPanel();
 704         panel.setLayout(new FlowLayout(FlowLayout.LEADING, 0, 0));
 705         panel.setBorder(new EmptyBorder(0, 0, 4, 0));
 706         JLabel filterLabel = new JLabel(filterLabelText);
 707         filterLabel.setName("GTKFileChooser.filterLabel");
 708         filterLabel.setDisplayedMnemonic(filterLabelMnemonic);
 709         panel.add(filterLabel);
 710 
 711         filterComboBoxModel = createFilterComboBoxModel();
 712         fc.addPropertyChangeListener(filterComboBoxModel);
 713         filterComboBox = new JComboBox<>(filterComboBoxModel);
 714         filterComboBox.setRenderer(createFilterComboBoxRenderer());
 715         filterLabel.setLabelFor(filterComboBox);
 716 
 717         interior.add(Box.createRigidArea(vstrut10));
 718         interior.add(panel);
 719         interior.add(filterComboBox);
 720 
 721         // Add buttons
 722         bottomButtonPanel = new JPanel(new FlowLayout(FlowLayout.TRAILING));
 723         bottomButtonPanel.setName("GTKFileChooser.bottomButtonPanel");
 724         align(bottomButtonPanel);
 725 
 726         JPanel pnButtons = new JPanel(new GridLayout(1, 2, 5, 0));
 727 
 728         JButton cancelButton = getCancelButton(fc);
 729         align(cancelButton);
 730         cancelButton.setMargin(buttonMargin);
 731         pnButtons.add(cancelButton);
 732 
 733         JButton approveButton = getApproveButton(fc);
 734         align(approveButton);
 735         approveButton.setMargin(buttonMargin);
 736         pnButtons.add(approveButton);
 737 
 738         bottomButtonPanel.add(pnButtons);
 739 
 740         if (fc.getControlButtonsAreShown()) {
 741             fc.add(bottomButtonPanel, BorderLayout.SOUTH);
 742         }
 743     }
 744 
 745     protected void installListeners(JFileChooser fc) {
 746         super.installListeners(fc);
 747 
 748         gtkFCPropertyChangeListener = new GTKFCPropertyChangeListener();
 749         fc.addPropertyChangeListener(gtkFCPropertyChangeListener);
 750     }
 751 
 752     private int getMnemonic(String key, Locale l) {
 753         return SwingUtilities2.getUIDefaultsInt(key, l);
 754     }
 755 
 756     protected void uninstallListeners(JFileChooser fc) {
 757         super.uninstallListeners(fc);
 758 
 759         if (gtkFCPropertyChangeListener != null) {
 760             fc.removePropertyChangeListener(gtkFCPropertyChangeListener);
 761         }
 762     }
 763 
 764     private class GTKFCPropertyChangeListener implements PropertyChangeListener {
 765         public void propertyChange(PropertyChangeEvent e) {
 766             String prop = e.getPropertyName();
 767             if (prop.equals("GTKFileChooser.showDirectoryIcons")) {
 768                 showDirectoryIcons = Boolean.TRUE.equals(e.getNewValue());
 769             } else if (prop.equals("GTKFileChooser.showFileIcons")) {
 770                 showFileIcons      = Boolean.TRUE.equals(e.getNewValue());
 771             }
 772         }
 773     }
 774 
 775     protected void installDefaults(JFileChooser fc) {
 776         super.installDefaults(fc);
 777         readOnly = UIManager.getBoolean("FileChooser.readOnly");
 778         showDirectoryIcons =
 779             Boolean.TRUE.equals(fc.getClientProperty("GTKFileChooser.showDirectoryIcons"));
 780         showFileIcons =
 781             Boolean.TRUE.equals(fc.getClientProperty("GTKFileChooser.showFileIcons"));
 782     }
 783 
 784     protected void installIcons(JFileChooser fc) {
 785         directoryIcon    = UIManager.getIcon("FileView.directoryIcon");
 786         fileIcon         = UIManager.getIcon("FileView.fileIcon");
 787     }
 788 
 789     protected void installStrings(JFileChooser fc) {
 790         super.installStrings(fc);
 791 
 792         Locale l = fc.getLocale();
 793 
 794         newFolderDialogText = UIManager.getString("FileChooser.newFolderDialogText", l);
 795         newFolderErrorText = UIManager.getString("FileChooser.newFolderErrorText",l);
 796         newFolderErrorSeparator = UIManager.getString("FileChooser.newFolderErrorSeparator",l);
 797         newFolderButtonText = UIManager.getString("FileChooser.newFolderButtonText", l);
 798         newFolderNoDirectoryErrorTitleText = UIManager.getString("FileChooser.newFolderNoDirectoryErrorTitleText", l);
 799         newFolderNoDirectoryErrorText = UIManager.getString("FileChooser.newFolderNoDirectoryErrorText", l);
 800         deleteFileButtonText = UIManager.getString("FileChooser.deleteFileButtonText", l);
 801         renameFileButtonText = UIManager.getString("FileChooser.renameFileButtonText", l);
 802 
 803         newFolderButtonMnemonic = getMnemonic("FileChooser.newFolderButtonMnemonic", l);
 804         deleteFileButtonMnemonic = getMnemonic("FileChooser.deleteFileButtonMnemonic", l);
 805         renameFileButtonMnemonic = getMnemonic("FileChooser.renameFileButtonMnemonic", l);
 806 
 807         newFolderButtonToolTipText = UIManager.getString("FileChooser.newFolderButtonToolTipText", l);
 808         deleteFileButtonToolTipText = UIManager.getString("FileChooser.deleteFileButtonToolTipText", l);
 809         renameFileButtonToolTipText = UIManager.getString("FileChooser.renameFileButtonToolTipText", l);
 810 
 811         renameFileDialogText = UIManager.getString("FileChooser.renameFileDialogText", l);
 812         renameFileErrorTitle = UIManager.getString("FileChooser.renameFileErrorTitle", l);
 813         renameFileErrorText = UIManager.getString("FileChooser.renameFileErrorText", l);
 814 
 815         foldersLabelText = UIManager.getString("FileChooser.foldersLabelText",l);
 816         foldersLabelMnemonic = getMnemonic("FileChooser.foldersLabelMnemonic", l);
 817 
 818         filesLabelText = UIManager.getString("FileChooser.filesLabelText",l);
 819         filesLabelMnemonic = getMnemonic("FileChooser.filesLabelMnemonic", l);
 820 
 821         pathLabelText = UIManager.getString("FileChooser.pathLabelText",l);
 822         pathLabelMnemonic = getMnemonic("FileChooser.pathLabelMnemonic", l);
 823 
 824         filterLabelText = UIManager.getString("FileChooser.filterLabelText", l);
 825         filterLabelMnemonic = UIManager.getInt("FileChooser.filterLabelMnemonic");
 826     }
 827 
 828     protected void uninstallStrings(JFileChooser fc) {
 829         super.uninstallStrings(fc);
 830 
 831         newFolderButtonText = null;
 832         deleteFileButtonText = null;
 833         renameFileButtonText = null;
 834 
 835         newFolderButtonToolTipText = null;
 836         deleteFileButtonToolTipText = null;
 837         renameFileButtonToolTipText = null;
 838 
 839         renameFileDialogText = null;
 840         renameFileErrorTitle = null;
 841         renameFileErrorText = null;
 842 
 843         foldersLabelText = null;
 844         filesLabelText = null;
 845 
 846         pathLabelText = null;
 847 
 848         newFolderDialogText = null;
 849         newFolderErrorText = null;
 850         newFolderErrorSeparator = null;
 851     }
 852 
 853     protected JScrollPane createFilesList() {
 854         fileList = new JList<>();
 855         fileList.setName("GTKFileChooser.fileList");
 856         fileList.putClientProperty(AccessibleContext.ACCESSIBLE_NAME_PROPERTY, filesLabelText);
 857 
 858         if (getFileChooser().isMultiSelectionEnabled()) {
 859             fileList.setSelectionMode(ListSelectionModel.MULTIPLE_INTERVAL_SELECTION);
 860         } else {
 861             fileList.setSelectionMode(ListSelectionModel.SINGLE_SELECTION);
 862         }
 863 
 864         fileList.setModel(new GTKFileListModel());
 865         fileList.getSelectionModel().removeSelectionInterval(0, 0);
 866         fileList.setCellRenderer(new FileCellRenderer());
 867         fileList.addListSelectionListener(createListSelectionListener(getFileChooser()));
 868         fileList.addMouseListener(createDoubleClickListener(getFileChooser(), fileList));
 869         align(fileList);
 870         JScrollPane scrollpane = new JScrollPane(fileList);
 871     scrollpane.setVerticalScrollBarPolicy(JScrollPane.VERTICAL_SCROLLBAR_ALWAYS);
 872         scrollpane.setName("GTKFileChooser.fileListScrollPane");
 873         scrollpane.setPreferredSize(prefListSize);
 874         scrollpane.setMaximumSize(MAX_SIZE);
 875         align(scrollpane);
 876         return scrollpane;
 877     }
 878 
 879     protected JScrollPane createDirectoryList() {
 880         directoryList = new JList<>();
 881         directoryList.setName("GTKFileChooser.directoryList");
 882         directoryList.putClientProperty(AccessibleContext.ACCESSIBLE_NAME_PROPERTY, foldersLabelText);
 883         align(directoryList);
 884 
 885         directoryList.setCellRenderer(new DirectoryCellRenderer());
 886         directoryListModel = new GTKDirectoryListModel();
 887         directoryList.getSelectionModel().removeSelectionInterval(0, 0);
 888         directoryList.setModel(directoryListModel);
 889         directoryList.addMouseListener(createDoubleClickListener(getFileChooser(), directoryList));
 890         directoryList.addListSelectionListener(createListSelectionListener(getFileChooser()));
 891 
 892         JScrollPane scrollpane = new JScrollPane(directoryList);
 893     scrollpane.setVerticalScrollBarPolicy(JScrollPane.VERTICAL_SCROLLBAR_ALWAYS);
 894         scrollpane.setName("GTKFileChooser.directoryListScrollPane");
 895         scrollpane.setMaximumSize(MAX_SIZE);
 896         scrollpane.setPreferredSize(prefListSize);
 897         align(scrollpane);
 898         return scrollpane;
 899     }
 900 
 901     protected void createModel() {
 902         model = new GTKDirectoryModel();
 903     }
 904 
 905     public BasicDirectoryModel getModel() {
 906         return model;
 907     }
 908 
 909     public Action getApproveSelectionAction() {
 910         return approveSelectionAction;
 911     }
 912 
 913     @SuppressWarnings("serial") // Superclass is not serializable across versions
 914     private class GTKDirectoryModel extends BasicDirectoryModel {
 915         FileSystemView fsv;
 916         private Comparator<File> fileComparator = new Comparator<File>() {
 917             public int compare(File o, File o1) {
 918                 return fsv.getSystemDisplayName(o).compareTo(fsv.getSystemDisplayName(o1));
 919             }
 920         };
 921 
 922         public GTKDirectoryModel() {
 923             super(getFileChooser());
 924         }
 925 
 926         protected void sort(Vector<? extends File> v) {
 927             fsv = getFileChooser().getFileSystemView();
 928             Collections.sort(v, fileComparator);
 929         }
 930     }
 931 
 932     @SuppressWarnings("serial") // Superclass is not serializable across versions
 933     protected class GTKDirectoryListModel extends AbstractListModel<File> implements ListDataListener {
 934         File curDir;
 935         public GTKDirectoryListModel() {
 936             getModel().addListDataListener(this);
 937             directoryChanged();
 938         }
 939 
 940         public int getSize() {
 941             return getModel().getDirectories().size() + 1;
 942         }
 943 
 944         @Override
 945         public File getElementAt(int index) {
 946             return index > 0 ? getModel().getDirectories().elementAt(index - 1):
 947                     curDir;
 948         }
 949 
 950         public void intervalAdded(ListDataEvent e) {
 951             fireIntervalAdded(this, e.getIndex0(), e.getIndex1());
 952         }
 953 
 954         public void intervalRemoved(ListDataEvent e) {
 955             fireIntervalRemoved(this, e.getIndex0(), e.getIndex1());
 956         }
 957 
 958         // PENDING - this is inefficient - should sent out
 959         // incremental adjustment values instead of saying that the
 960         // whole list has changed.
 961         public void fireContentsChanged() {
 962             fireContentsChanged(this, 0, getModel().getDirectories().size()-1);
 963         }
 964 
 965         // PENDING - fire the correct interval changed - currently sending
 966         // out that everything has changed
 967         public void contentsChanged(ListDataEvent e) {
 968             fireContentsChanged();
 969         }
 970 
 971         private void directoryChanged() {
 972             curDir = getFileChooser().getFileSystemView().createFileObject(
 973                     getFileChooser().getCurrentDirectory(), ".");
 974         }
 975     }
 976 
 977     @SuppressWarnings("serial") // Superclass is not serializable across versions
 978     protected class GTKFileListModel extends AbstractListModel<File> implements ListDataListener {
 979         public GTKFileListModel() {
 980             getModel().addListDataListener(this);
 981         }
 982 
 983         public int getSize() {
 984             return getModel().getFiles().size();
 985         }
 986 
 987         public boolean contains(Object o) {
 988             return getModel().getFiles().contains(o);
 989         }
 990 
 991         public int indexOf(Object o) {
 992             return getModel().getFiles().indexOf(o);
 993         }
 994 
 995         @Override
 996         public File getElementAt(int index) {
 997             return getModel().getFiles().elementAt(index);
 998         }
 999 
1000         public void intervalAdded(ListDataEvent e) {
1001             fireIntervalAdded(this, e.getIndex0(), e.getIndex1());
1002         }
1003 
1004         public void intervalRemoved(ListDataEvent e) {
1005             fireIntervalRemoved(this, e.getIndex0(), e.getIndex1());
1006         }
1007 
1008         // PENDING - this is inefficient - should sent out
1009         // incremental adjustment values instead of saying that the
1010         // whole list has changed.
1011         public void fireContentsChanged() {
1012             fireContentsChanged(this, 0, getModel().getFiles().size()-1);
1013         }
1014 
1015         // PENDING - fire the interval changed
1016         public void contentsChanged(ListDataEvent e) {
1017             fireContentsChanged();
1018         }
1019     }
1020 
1021 
1022     @SuppressWarnings("serial") // Superclass is not serializable across versions
1023     protected class FileCellRenderer extends DefaultListCellRenderer  {
1024         public Component getListCellRendererComponent(JList<?> list, Object value, int index,
1025                                                       boolean isSelected, boolean cellHasFocus) {
1026 
1027             super.getListCellRendererComponent(list, value, index, isSelected, cellHasFocus);
1028             setText(getFileChooser().getName((File) value));
1029             if (showFileIcons) {
1030                 setIcon(getFileChooser().getIcon((File)value));
1031             }
1032             return this;
1033         }
1034     }
1035 
1036     @SuppressWarnings("serial") // Superclass is not serializable across versions
1037     protected class DirectoryCellRenderer extends DefaultListCellRenderer  {
1038         public Component getListCellRendererComponent(JList<?> list, Object value, int index,
1039                                                       boolean isSelected, boolean cellHasFocus) {
1040 
1041             super.getListCellRendererComponent(list, value, index, isSelected, cellHasFocus);
1042 
1043             if (showDirectoryIcons) {
1044                 setIcon(getFileChooser().getIcon((File)value));
1045                 setText(getFileChooser().getName((File)value));
1046             } else {
1047                 setText(getFileChooser().getName((File)value) + "/");
1048             }
1049             return this;
1050         }
1051     }
1052 
1053     public Dimension getPreferredSize(JComponent c) {
1054         Dimension prefSize = new Dimension(PREF_SIZE);
1055         JComponent accessory = getFileChooser().getAccessory();
1056         if (accessory != null) {
1057             prefSize.width += accessory.getPreferredSize().width + 20;
1058         }
1059         Dimension d = c.getLayout().preferredLayoutSize(c);
1060         if (d != null) {
1061             return new Dimension(d.width < prefSize.width ? prefSize.width : d.width,
1062                                  d.height < prefSize.height ? prefSize.height : d.height);
1063         } else {
1064             return prefSize;
1065         }
1066     }
1067 
1068     public Dimension getMinimumSize(JComponent x)  {
1069         return new Dimension(MIN_SIZE);
1070     }
1071 
1072     public Dimension getMaximumSize(JComponent x) {
1073         return new Dimension(Integer.MAX_VALUE, Integer.MAX_VALUE);
1074     }
1075 
1076     protected void align(JComponent c) {
1077         c.setAlignmentX(JComponent.LEFT_ALIGNMENT);
1078         c.setAlignmentY(JComponent.TOP_ALIGNMENT);
1079     }
1080 
1081     public Action getNewFolderAction() {
1082         if (newFolderAction == null) {
1083             newFolderAction = new NewFolderAction();
1084             newFolderAction.setEnabled(!readOnly);
1085         }
1086         return newFolderAction;
1087     }
1088 
1089     //
1090     // DataModel for DirectoryComboxbox
1091     //
1092     protected DirectoryComboBoxModel createDirectoryComboBoxModel(JFileChooser fc) {
1093         return new DirectoryComboBoxModel();
1094     }
1095 
1096     /**
1097      * Data model for a type-face selection combo-box.
1098      */
1099     @SuppressWarnings("serial") // Superclass is not serializable across versions
1100     protected class DirectoryComboBoxModel extends AbstractListModel<File> implements ComboBoxModel<File> {
1101         Vector<File> directories = new Vector<File>();
1102         File selectedDirectory = null;
1103         JFileChooser chooser = getFileChooser();
1104         FileSystemView fsv = chooser.getFileSystemView();
1105 
1106         public DirectoryComboBoxModel() {
1107             // Add the current directory to the model, and make it the
1108             // selectedDirectory
1109             File dir = getFileChooser().getCurrentDirectory();
1110             if (dir != null) {
1111                 addItem(dir);
1112             }
1113         }
1114 
1115         /**
1116          * Adds the directory to the model and sets it to be selected,
1117          * additionally clears out the previous selected directory and
1118          * the paths leading up to it, if any.
1119          */
1120         private void addItem(File directory) {
1121 
1122             if (directory == null) {
1123                 return;
1124             }
1125 
1126             int oldSize = directories.size();
1127             directories.clear();
1128             if (oldSize > 0) {
1129                 fireIntervalRemoved(this, 0, oldSize);
1130             }
1131 
1132             // Get the canonical (full) path. This has the side
1133             // benefit of removing extraneous chars from the path,
1134             // for example /foo/bar/ becomes /foo/bar
1135             File canonical;
1136             try {
1137                 canonical = fsv.createFileObject(ShellFolder.getNormalizedFile(directory).getPath());
1138             } catch (IOException e) {
1139                 // Maybe drive is not ready. Can't abort here.
1140                 canonical = directory;
1141             }
1142 
1143             // create File instances of each directory leading up to the top
1144             File f = canonical;
1145             do {
1146                 directories.add(f);
1147             } while ((f = f.getParentFile()) != null);
1148             int newSize = directories.size();
1149             if (newSize > 0) {
1150                 fireIntervalAdded(this, 0, newSize);
1151             }
1152             setSelectedItem(canonical);
1153         }
1154 
1155         public void setSelectedItem(Object selectedDirectory) {
1156             this.selectedDirectory = (File)selectedDirectory;
1157             fireContentsChanged(this, -1, -1);
1158         }
1159 
1160         public Object getSelectedItem() {
1161             return selectedDirectory;
1162         }
1163 
1164         public int getSize() {
1165             return directories.size();
1166         }
1167 
1168         @Override
1169         public File getElementAt(int index) {
1170             return directories.elementAt(index);
1171         }
1172     }
1173 
1174     /**
1175      * Acts when DirectoryComboBox has changed the selected item.
1176      */
1177     @SuppressWarnings("serial") // Superclass is not serializable across versions
1178     protected class DirectoryComboBoxAction extends AbstractAction {
1179         protected DirectoryComboBoxAction() {
1180             super("DirectoryComboBoxAction");
1181         }
1182 
1183         public void actionPerformed(ActionEvent e) {
1184             File f = (File)directoryComboBox.getSelectedItem();
1185             getFileChooser().setCurrentDirectory(f);
1186         }
1187     }
1188 
1189     /**
1190      * Creates a new folder.
1191      */
1192     @SuppressWarnings("serial") // Superclass is not serializable across versions
1193     private class NewFolderAction extends AbstractAction {
1194         protected NewFolderAction() {
1195             super(FilePane.ACTION_NEW_FOLDER);
1196         }
1197         public void actionPerformed(ActionEvent e) {
1198             if (readOnly) {
1199                 return;
1200             }
1201             JFileChooser fc = getFileChooser();
1202             File currentDirectory = fc.getCurrentDirectory();
1203             String dirName = JOptionPane.showInputDialog(fc,
1204                     newFolderDialogText, newFolderButtonText,
1205                     JOptionPane.PLAIN_MESSAGE);
1206 
1207             if (dirName != null) {
1208                 if (!currentDirectory.exists()) {
1209                     JOptionPane.showMessageDialog(fc,
1210                             MessageFormat.format(newFolderNoDirectoryErrorText, dirName),
1211                             newFolderNoDirectoryErrorTitleText, JOptionPane.ERROR_MESSAGE);
1212                     return;
1213                 }
1214 
1215                 File newDir = fc.getFileSystemView().createFileObject
1216                         (currentDirectory, dirName);
1217                 if (newDir == null || !newDir.mkdir()) {
1218                     JOptionPane.showMessageDialog(fc,
1219                             newFolderErrorText + newFolderErrorSeparator + " \"" +
1220                             dirName + "\"",
1221                             newFolderErrorText, JOptionPane.ERROR_MESSAGE);
1222                 }
1223                 fc.rescanCurrentDirectory();
1224             }
1225         }
1226     }
1227 
1228     @SuppressWarnings("serial") // Superclass is not serializable across versions
1229     private class GTKApproveSelectionAction extends ApproveSelectionAction {
1230         public void actionPerformed(ActionEvent e) {
1231             if (isDirectorySelected()) {
1232                 File dir = getDirectory();
1233                 try {
1234                     // Strip trailing ".."
1235                     if (dir != null) {
1236                         dir = ShellFolder.getNormalizedFile(dir);
1237                     }
1238                 } catch (IOException ex) {
1239                     // Ok, use f as is
1240                 }
1241                 if (getFileChooser().getCurrentDirectory().equals(dir)) {
1242                     directoryList.clearSelection();
1243                     fileList.clearSelection();
1244                     ListSelectionModel sm = fileList.getSelectionModel();
1245                     if (sm instanceof DefaultListSelectionModel) {
1246                         ((DefaultListSelectionModel)sm).moveLeadSelectionIndex(0);
1247                         sm.setAnchorSelectionIndex(0);
1248                     }
1249                     rescanCurrentDirectory(getFileChooser());
1250                     return;
1251                 }
1252             }
1253             super.actionPerformed(e);
1254         }
1255     }
1256 
1257     /**
1258      * Renames file
1259      */
1260     @SuppressWarnings("serial") // Superclass is not serializable across versions
1261     private class RenameFileAction extends AbstractAction {
1262         protected RenameFileAction() {
1263             super(FilePane.ACTION_EDIT_FILE_NAME);
1264         }
1265         public void actionPerformed(ActionEvent e) {
1266             if (getFileName().equals("")) {
1267                 return;
1268             }
1269             JFileChooser fc = getFileChooser();
1270             File currentDirectory = fc.getCurrentDirectory();
1271             String newFileName = (String) JOptionPane.showInputDialog
1272                    (fc, new MessageFormat(renameFileDialogText).format
1273                            (new Object[] { getFileName() }),
1274                            renameFileButtonText, JOptionPane.PLAIN_MESSAGE, null, null,
1275                            getFileName());
1276 
1277             if (newFileName != null) {
1278                 File oldFile = fc.getFileSystemView().createFileObject
1279                         (currentDirectory, getFileName());
1280                 File newFile = fc.getFileSystemView().createFileObject
1281                         (currentDirectory, newFileName);
1282                 if (oldFile == null || newFile == null ||
1283                         !getModel().renameFile(oldFile, newFile)) {
1284                     JOptionPane.showMessageDialog(fc,
1285                             new MessageFormat(renameFileErrorText).
1286                             format(new Object[] { getFileName(), newFileName}),
1287                             renameFileErrorTitle, JOptionPane.ERROR_MESSAGE);
1288                 } else {
1289                     setFileName(getFileChooser().getName(newFile));
1290                     fc.rescanCurrentDirectory();
1291                 }
1292             }
1293         }
1294     }
1295 
1296     //
1297     // Renderer for Filter ComboBox
1298     //
1299     protected FilterComboBoxRenderer createFilterComboBoxRenderer() {
1300         return new FilterComboBoxRenderer();
1301     }
1302 
1303     /**
1304      * Render different filters
1305      */
1306     @SuppressWarnings("serial") // Superclass is not serializable across versions
1307     public class FilterComboBoxRenderer extends DefaultListCellRenderer implements UIResource {
1308         public String getName() {
1309             // As SynthComboBoxRenderer's are asked for a size BEFORE they
1310             // are parented getName is overriden to force the name to be
1311             // ComboBox.renderer if it isn't set. If we didn't do this the
1312             // wrong style could be used for size calculations.
1313             String name = super.getName();
1314             if (name == null) {
1315                 return "ComboBox.renderer";
1316             }
1317             return name;
1318         }
1319 
1320         public Component getListCellRendererComponent(JList<?> list, Object value,
1321                                                       int index, boolean isSelected,
1322                                                       boolean cellHasFocus) {
1323 
1324             super.getListCellRendererComponent(list, value, index, isSelected, cellHasFocus);
1325 
1326             setName("ComboBox.listRenderer");
1327 
1328             if (value != null) {
1329                 if (value instanceof FileFilter) {
1330                     setText(((FileFilter) value).getDescription());
1331                 }
1332             } else {
1333                 setText("");
1334             }
1335 
1336             return this;
1337         }
1338     }
1339 
1340     //
1341     // DataModel for Filter Combobox
1342     //
1343     protected FilterComboBoxModel createFilterComboBoxModel() {
1344         return new FilterComboBoxModel();
1345     }
1346 
1347     /**
1348      * Data model for filter combo-box.
1349      */
1350     @SuppressWarnings("serial") // JDK implementation class
1351     protected class FilterComboBoxModel extends AbstractListModel<FileFilter>
1352             implements ComboBoxModel<FileFilter>, PropertyChangeListener {
1353         protected FileFilter[] filters;
1354 
1355         protected FilterComboBoxModel() {
1356             super();
1357             filters = getFileChooser().getChoosableFileFilters();
1358         }
1359 
1360         public void propertyChange(PropertyChangeEvent e) {
1361             String prop = e.getPropertyName();
1362             if (prop == JFileChooser.CHOOSABLE_FILE_FILTER_CHANGED_PROPERTY) {
1363                 filters = (FileFilter[]) e.getNewValue();
1364                 fireContentsChanged(this, -1, -1);
1365             } else if (prop == JFileChooser.FILE_FILTER_CHANGED_PROPERTY) {
1366                 fireContentsChanged(this, -1, -1);
1367             }
1368         }
1369 
1370         public void setSelectedItem(Object filter) {
1371             if (filter != null) {
1372                 getFileChooser().setFileFilter((FileFilter) filter);
1373                 fireContentsChanged(this, -1, -1);
1374             }
1375         }
1376 
1377         public Object getSelectedItem() {
1378             // Ensure that the current filter is in the list.
1379             // NOTE: we shouldnt' have to do this, since JFileChooser adds
1380             // the filter to the choosable filters list when the filter
1381             // is set. Lets be paranoid just in case someone overrides
1382             // setFileFilter in JFileChooser.
1383             FileFilter currentFilter = getFileChooser().getFileFilter();
1384             boolean found = false;
1385             if (currentFilter != null) {
1386                 for (FileFilter filter : filters) {
1387                     if (filter == currentFilter) {
1388                         found = true;
1389                     }
1390                 }
1391                 if (found == false) {
1392                     getFileChooser().addChoosableFileFilter(currentFilter);
1393                 }
1394             }
1395             return getFileChooser().getFileFilter();
1396         }
1397 
1398         public int getSize() {
1399             if (filters != null) {
1400                 return filters.length;
1401             } else {
1402                 return 0;
1403             }
1404         }
1405 
1406         @Override
1407         public FileFilter getElementAt(int index) {
1408             if (index > getSize() - 1) {
1409                 // This shouldn't happen. Try to recover gracefully.
1410                 return getFileChooser().getFileFilter();
1411             }
1412             if (filters != null) {
1413                 return filters[index];
1414             } else {
1415                 return null;
1416             }
1417         }
1418     }
1419 }