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