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 filterComboBox;
  86     private FilterComboBoxModel filterComboBoxModel;
  87 
  88     // From Motif
  89 
  90     private JPanel rightPanel;
  91     private JList directoryList;
  92     private JList 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 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 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         StringBuffer buf = new StringBuffer();
 173         len = result.size();
 174 
 175         // construct the resulting string
 176         for (int i=0; i<len; i++) {
 177             if (i > 0) {
 178                 buf.append(" ");
 179             }
 180             if (len > 1) {
 181                 buf.append("\"");
 182             }
 183             buf.append(result.get(i));
 184             if (len > 1) {
 185                 buf.append("\"");
 186             }
 187         }
 188         return buf.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 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         public Object getElementAt(int index) {
 945             return index > 0 ? getModel().getDirectories().elementAt(index - 1):
 946                     curDir;
 947         }
 948 
 949         public void intervalAdded(ListDataEvent e) {
 950             fireIntervalAdded(this, e.getIndex0(), e.getIndex1());
 951         }
 952 
 953         public void intervalRemoved(ListDataEvent e) {
 954             fireIntervalRemoved(this, e.getIndex0(), e.getIndex1());
 955         }
 956 
 957         // PENDING - this is inefficient - should sent out
 958         // incremental adjustment values instead of saying that the
 959         // whole list has changed.
 960         public void fireContentsChanged() {
 961             fireContentsChanged(this, 0, getModel().getDirectories().size()-1);
 962         }
 963 
 964         // PENDING - fire the correct interval changed - currently sending
 965         // out that everything has changed
 966         public void contentsChanged(ListDataEvent e) {
 967             fireContentsChanged();
 968         }
 969 
 970         private void directoryChanged() {
 971             curDir = getFileChooser().getFileSystemView().createFileObject(
 972                     getFileChooser().getCurrentDirectory(), ".");
 973         }
 974     }
 975 
 976     @SuppressWarnings("serial") // Superclass is not serializable across versions
 977     protected class GTKFileListModel extends AbstractListModel implements ListDataListener {
 978         public GTKFileListModel() {
 979             getModel().addListDataListener(this);
 980         }
 981 
 982         public int getSize() {
 983             return getModel().getFiles().size();
 984         }
 985 
 986         public boolean contains(Object o) {
 987             return getModel().getFiles().contains(o);
 988         }
 989 
 990         public int indexOf(Object o) {
 991             return getModel().getFiles().indexOf(o);
 992         }
 993 
 994         public Object getElementAt(int index) {
 995             return getModel().getFiles().elementAt(index);
 996         }
 997 
 998         public void intervalAdded(ListDataEvent e) {
 999             fireIntervalAdded(this, e.getIndex0(), e.getIndex1());
1000         }
1001 
1002         public void intervalRemoved(ListDataEvent e) {
1003             fireIntervalRemoved(this, e.getIndex0(), e.getIndex1());
1004         }
1005 
1006         // PENDING - this is inefficient - should sent out
1007         // incremental adjustment values instead of saying that the
1008         // whole list has changed.
1009         public void fireContentsChanged() {
1010             fireContentsChanged(this, 0, getModel().getFiles().size()-1);
1011         }
1012 
1013         // PENDING - fire the interval changed
1014         public void contentsChanged(ListDataEvent e) {
1015             fireContentsChanged();
1016         }
1017     }
1018 
1019 
1020     @SuppressWarnings("serial") // Superclass is not serializable across versions
1021     protected class FileCellRenderer extends DefaultListCellRenderer  {
1022         public Component getListCellRendererComponent(JList list, Object value, int index,
1023                                                       boolean isSelected, boolean cellHasFocus) {
1024 
1025             super.getListCellRendererComponent(list, value, index, isSelected, cellHasFocus);
1026             setText(getFileChooser().getName((File) value));
1027             if (showFileIcons) {
1028                 setIcon(getFileChooser().getIcon((File)value));
1029             }
1030             return this;
1031         }
1032     }
1033 
1034     @SuppressWarnings("serial") // Superclass is not serializable across versions
1035     protected class DirectoryCellRenderer extends DefaultListCellRenderer  {
1036         public Component getListCellRendererComponent(JList list, Object value, int index,
1037                                                       boolean isSelected, boolean cellHasFocus) {
1038 
1039             super.getListCellRendererComponent(list, value, index, isSelected, cellHasFocus);
1040 
1041             if (showDirectoryIcons) {
1042                 setIcon(getFileChooser().getIcon((File)value));
1043                 setText(getFileChooser().getName((File)value));
1044             } else {
1045                 setText(getFileChooser().getName((File)value) + "/");
1046             }
1047             return this;
1048         }
1049     }
1050 
1051     public Dimension getPreferredSize(JComponent c) {
1052         Dimension prefSize = new Dimension(PREF_SIZE);
1053         JComponent accessory = getFileChooser().getAccessory();
1054         if (accessory != null) {
1055             prefSize.width += accessory.getPreferredSize().width + 20;
1056         }
1057         Dimension d = c.getLayout().preferredLayoutSize(c);
1058         if (d != null) {
1059             return new Dimension(d.width < prefSize.width ? prefSize.width : d.width,
1060                                  d.height < prefSize.height ? prefSize.height : d.height);
1061         } else {
1062             return prefSize;
1063         }
1064     }
1065 
1066     public Dimension getMinimumSize(JComponent x)  {
1067         return new Dimension(MIN_SIZE);
1068     }
1069 
1070     public Dimension getMaximumSize(JComponent x) {
1071         return new Dimension(Integer.MAX_VALUE, Integer.MAX_VALUE);
1072     }
1073 
1074     protected void align(JComponent c) {
1075         c.setAlignmentX(JComponent.LEFT_ALIGNMENT);
1076         c.setAlignmentY(JComponent.TOP_ALIGNMENT);
1077     }
1078 
1079     public Action getNewFolderAction() {
1080         if (newFolderAction == null) {
1081             newFolderAction = new NewFolderAction();
1082             newFolderAction.setEnabled(!readOnly);
1083         }
1084         return newFolderAction;
1085     }
1086 
1087     //
1088     // DataModel for DirectoryComboxbox
1089     //
1090     protected DirectoryComboBoxModel createDirectoryComboBoxModel(JFileChooser fc) {
1091         return new DirectoryComboBoxModel();
1092     }
1093 
1094     /**
1095      * Data model for a type-face selection combo-box.
1096      */
1097     @SuppressWarnings("serial") // Superclass is not serializable across versions
1098     protected class DirectoryComboBoxModel extends AbstractListModel implements ComboBoxModel {
1099         Vector<File> directories = new Vector<File>();
1100         File selectedDirectory = null;
1101         JFileChooser chooser = getFileChooser();
1102         FileSystemView fsv = chooser.getFileSystemView();
1103 
1104         public DirectoryComboBoxModel() {
1105             // Add the current directory to the model, and make it the
1106             // selectedDirectory
1107             File dir = getFileChooser().getCurrentDirectory();
1108             if (dir != null) {
1109                 addItem(dir);
1110             }
1111         }
1112 
1113         /**
1114          * Adds the directory to the model and sets it to be selected,
1115          * additionally clears out the previous selected directory and
1116          * the paths leading up to it, if any.
1117          */
1118         private void addItem(File directory) {
1119 
1120             if (directory == null) {
1121                 return;
1122             }
1123 
1124             int oldSize = directories.size();
1125             directories.clear();
1126             if (oldSize > 0) {
1127                 fireIntervalRemoved(this, 0, oldSize);
1128             }
1129 
1130             // Get the canonical (full) path. This has the side
1131             // benefit of removing extraneous chars from the path,
1132             // for example /foo/bar/ becomes /foo/bar
1133             File canonical;
1134             try {
1135                 canonical = fsv.createFileObject(ShellFolder.getNormalizedFile(directory).getPath());
1136             } catch (IOException e) {
1137                 // Maybe drive is not ready. Can't abort here.
1138                 canonical = directory;
1139             }
1140 
1141             // create File instances of each directory leading up to the top
1142             File f = canonical;
1143             do {
1144                 directories.add(f);
1145             } while ((f = f.getParentFile()) != null);
1146             int newSize = directories.size();
1147             if (newSize > 0) {
1148                 fireIntervalAdded(this, 0, newSize);
1149             }
1150             setSelectedItem(canonical);
1151         }
1152 
1153         public void setSelectedItem(Object selectedDirectory) {
1154             this.selectedDirectory = (File)selectedDirectory;
1155             fireContentsChanged(this, -1, -1);
1156         }
1157 
1158         public Object getSelectedItem() {
1159             return selectedDirectory;
1160         }
1161 
1162         public int getSize() {
1163             return directories.size();
1164         }
1165 
1166         public Object getElementAt(int index) {
1167             return directories.elementAt(index);
1168         }
1169     }
1170 
1171     /**
1172      * Acts when DirectoryComboBox has changed the selected item.
1173      */
1174     @SuppressWarnings("serial") // Superclass is not serializable across versions
1175     protected class DirectoryComboBoxAction extends AbstractAction {
1176         protected DirectoryComboBoxAction() {
1177             super("DirectoryComboBoxAction");
1178         }
1179 
1180         public void actionPerformed(ActionEvent e) {
1181             File f = (File)directoryComboBox.getSelectedItem();
1182             getFileChooser().setCurrentDirectory(f);
1183         }
1184     }
1185 
1186     /**
1187      * Creates a new folder.
1188      */
1189     @SuppressWarnings("serial") // Superclass is not serializable across versions
1190     private class NewFolderAction extends AbstractAction {
1191         protected NewFolderAction() {
1192             super(FilePane.ACTION_NEW_FOLDER);
1193         }
1194         public void actionPerformed(ActionEvent e) {
1195             if (readOnly) {
1196                 return;
1197             }
1198             JFileChooser fc = getFileChooser();
1199             File currentDirectory = fc.getCurrentDirectory();
1200             String dirName = JOptionPane.showInputDialog(fc,
1201                     newFolderDialogText, newFolderButtonText,
1202                     JOptionPane.PLAIN_MESSAGE);
1203 
1204             if (dirName != null) {
1205                 if (!currentDirectory.exists()) {
1206                     JOptionPane.showMessageDialog(fc,
1207                             MessageFormat.format(newFolderNoDirectoryErrorText, dirName),
1208                             newFolderNoDirectoryErrorTitleText, JOptionPane.ERROR_MESSAGE);
1209                     return;
1210                 }
1211 
1212                 File newDir = fc.getFileSystemView().createFileObject
1213                         (currentDirectory, dirName);
1214                 if (newDir == null || !newDir.mkdir()) {
1215                     JOptionPane.showMessageDialog(fc,
1216                             newFolderErrorText + newFolderErrorSeparator + " \"" +
1217                             dirName + "\"",
1218                             newFolderErrorText, JOptionPane.ERROR_MESSAGE);
1219                 }
1220                 fc.rescanCurrentDirectory();
1221             }
1222         }
1223     }
1224 
1225     @SuppressWarnings("serial") // Superclass is not serializable across versions
1226     private class GTKApproveSelectionAction extends ApproveSelectionAction {
1227         public void actionPerformed(ActionEvent e) {
1228             if (isDirectorySelected()) {
1229                 File dir = getDirectory();
1230                 try {
1231                     // Strip trailing ".."
1232                     if (dir != null) {
1233                         dir = ShellFolder.getNormalizedFile(dir);
1234                     }
1235                 } catch (IOException ex) {
1236                     // Ok, use f as is
1237                 }
1238                 if (getFileChooser().getCurrentDirectory().equals(dir)) {
1239                     directoryList.clearSelection();
1240                     fileList.clearSelection();
1241                     ListSelectionModel sm = fileList.getSelectionModel();
1242                     if (sm instanceof DefaultListSelectionModel) {
1243                         ((DefaultListSelectionModel)sm).moveLeadSelectionIndex(0);
1244                         sm.setAnchorSelectionIndex(0);
1245                     }
1246                     rescanCurrentDirectory(getFileChooser());
1247                     return;
1248                 }
1249             }
1250             super.actionPerformed(e);
1251         }
1252     }
1253 
1254     /**
1255      * Renames file
1256      */
1257     @SuppressWarnings("serial") // Superclass is not serializable across versions
1258     private class RenameFileAction extends AbstractAction {
1259         protected RenameFileAction() {
1260             super(FilePane.ACTION_EDIT_FILE_NAME);
1261         }
1262         public void actionPerformed(ActionEvent e) {
1263             if (getFileName().equals("")) {
1264                 return;
1265             }
1266             JFileChooser fc = getFileChooser();
1267             File currentDirectory = fc.getCurrentDirectory();
1268             String newFileName = (String) JOptionPane.showInputDialog
1269                    (fc, new MessageFormat(renameFileDialogText).format
1270                            (new Object[] { getFileName() }),
1271                            renameFileButtonText, JOptionPane.PLAIN_MESSAGE, null, null,
1272                            getFileName());
1273 
1274             if (newFileName != null) {
1275                 File oldFile = fc.getFileSystemView().createFileObject
1276                         (currentDirectory, getFileName());
1277                 File newFile = fc.getFileSystemView().createFileObject
1278                         (currentDirectory, newFileName);
1279                 if (oldFile == null || newFile == null ||
1280                         !getModel().renameFile(oldFile, newFile)) {
1281                     JOptionPane.showMessageDialog(fc,
1282                             new MessageFormat(renameFileErrorText).
1283                             format(new Object[] { getFileName(), newFileName}),
1284                             renameFileErrorTitle, JOptionPane.ERROR_MESSAGE);
1285                 } else {
1286                     setFileName(getFileChooser().getName(newFile));
1287                     fc.rescanCurrentDirectory();
1288                 }
1289             }
1290         }
1291     }
1292 
1293     //
1294     // Renderer for Filter ComboBox
1295     //
1296     protected FilterComboBoxRenderer createFilterComboBoxRenderer() {
1297         return new FilterComboBoxRenderer();
1298     }
1299 
1300     /**
1301      * Render different filters
1302      */
1303     @SuppressWarnings("serial") // Superclass is not serializable across versions
1304     public class FilterComboBoxRenderer extends DefaultListCellRenderer implements UIResource {
1305         public String getName() {
1306             // As SynthComboBoxRenderer's are asked for a size BEFORE they
1307             // are parented getName is overriden to force the name to be
1308             // ComboBox.renderer if it isn't set. If we didn't do this the
1309             // wrong style could be used for size calculations.
1310             String name = super.getName();
1311             if (name == null) {
1312                 return "ComboBox.renderer";
1313             }
1314             return name;
1315         }
1316 
1317         public Component getListCellRendererComponent(JList list, Object value,
1318                                                       int index, boolean isSelected,
1319                                                       boolean cellHasFocus) {
1320 
1321             super.getListCellRendererComponent(list, value, index, isSelected, cellHasFocus);
1322 
1323             setName("ComboBox.listRenderer");
1324 
1325             if (value != null) {
1326                 if (value instanceof FileFilter) {
1327                     setText(((FileFilter) value).getDescription());
1328                 }
1329             } else {
1330                 setText("");
1331             }
1332 
1333             return this;
1334         }
1335     }
1336 
1337     //
1338     // DataModel for Filter Combobox
1339     //
1340     protected FilterComboBoxModel createFilterComboBoxModel() {
1341         return new FilterComboBoxModel();
1342     }
1343 
1344     /**
1345      * Data model for filter combo-box.
1346      */
1347     @SuppressWarnings("serial") // JDK implementation class
1348     protected class FilterComboBoxModel extends AbstractListModel
1349             implements ComboBoxModel, PropertyChangeListener {
1350         protected FileFilter[] filters;
1351 
1352         protected FilterComboBoxModel() {
1353             super();
1354             filters = getFileChooser().getChoosableFileFilters();
1355         }
1356 
1357         public void propertyChange(PropertyChangeEvent e) {
1358             String prop = e.getPropertyName();
1359             if (prop == JFileChooser.CHOOSABLE_FILE_FILTER_CHANGED_PROPERTY) {
1360                 filters = (FileFilter[]) e.getNewValue();
1361                 fireContentsChanged(this, -1, -1);
1362             } else if (prop == JFileChooser.FILE_FILTER_CHANGED_PROPERTY) {
1363                 fireContentsChanged(this, -1, -1);
1364             }
1365         }
1366 
1367         public void setSelectedItem(Object filter) {
1368             if (filter != null) {
1369                 getFileChooser().setFileFilter((FileFilter) filter);
1370                 fireContentsChanged(this, -1, -1);
1371             }
1372         }
1373 
1374         public Object getSelectedItem() {
1375             // Ensure that the current filter is in the list.
1376             // NOTE: we shouldnt' have to do this, since JFileChooser adds
1377             // the filter to the choosable filters list when the filter
1378             // is set. Lets be paranoid just in case someone overrides
1379             // setFileFilter in JFileChooser.
1380             FileFilter currentFilter = getFileChooser().getFileFilter();
1381             boolean found = false;
1382             if (currentFilter != null) {
1383                 for (FileFilter filter : filters) {
1384                     if (filter == currentFilter) {
1385                         found = true;
1386                     }
1387                 }
1388                 if (found == false) {
1389                     getFileChooser().addChoosableFileFilter(currentFilter);
1390                 }
1391             }
1392             return getFileChooser().getFileFilter();
1393         }
1394 
1395         public int getSize() {
1396             if (filters != null) {
1397                 return filters.length;
1398             } else {
1399                 return 0;
1400             }
1401         }
1402 
1403         public Object getElementAt(int index) {
1404             if (index > getSize() - 1) {
1405                 // This shouldn't happen. Try to recover gracefully.
1406                 return getFileChooser().getFileFilter();
1407             }
1408             if (filters != null) {
1409                 return filters[index];
1410             } else {
1411                 return null;
1412             }
1413         }
1414     }
1415 }