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