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 */
25package com.sun.java.swing.plaf.gtk;
26
27import java.awt.*;
28import java.awt.event.*;
29import java.beans.*;
30import java.io.File;
31import java.io.IOException;
32import java.text.MessageFormat;
33import java.util.*;
34
35import javax.swing.*;
36import javax.swing.border.*;
37import javax.swing.filechooser.*;
38import javax.swing.event.*;
39import javax.swing.plaf.*;
40import javax.swing.plaf.basic.BasicDirectoryModel;
41import javax.swing.table.*;
42import javax.accessibility.*;
43
44import sun.swing.SwingUtilities2;
45
46import sun.swing.plaf.synth.*;
47import sun.swing.FilePane;
48import sun.awt.shell.ShellFolder;
49
50/**
51 * GTK FileChooserUI.
52 *
53 * @author Leif Samuelsson
54 * @author Jeff Dinkins
55 */
56class 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}
1426