1 
   2 /*
   3  * Copyright (c) 2003, 2006, Oracle and/or its affiliates. All rights reserved.
   4  * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER.
   5  *
   6  * This code is free software; you can redistribute it and/or modify it
   7  * under the terms of the GNU General Public License version 2 only, as
   8  * published by the Free Software Foundation.  Oracle designates this
   9  * particular file as subject to the "Classpath" exception as provided
  10  * by Oracle in the LICENSE file that accompanied this code.
  11  *
  12  * This code is distributed in the hope that it will be useful, but WITHOUT
  13  * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
  14  * FITNESS FOR A PARTICULAR PURPOSE.  See the GNU General Public License
  15  * version 2 for more details (a copy is included in the LICENSE file that
  16  * accompanied this code).
  17  *
  18  * You should have received a copy of the GNU General Public License version
  19  * 2 along with this work; if not, write to the Free Software Foundation,
  20  * Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA.
  21  *
  22  * Please contact Oracle, 500 Oracle Parkway, Redwood Shores, CA 94065 USA
  23  * or visit www.oracle.com if you need additional information or have any
  24  * questions.
  25  */
  26 package sun.swing;
  27 
  28 import java.awt.*;
  29 import java.awt.event.*;
  30 import java.beans.PropertyChangeEvent;
  31 import java.beans.PropertyChangeListener;
  32 import java.io.*;
  33 import java.text.DateFormat;
  34 import java.text.MessageFormat;
  35 import java.util.*;
  36 import java.util.List;
  37 
  38 import javax.accessibility.AccessibleContext;
  39 import javax.swing.*;
  40 import javax.swing.border.*;
  41 import javax.swing.event.*;
  42 import javax.swing.filechooser.*;
  43 import javax.swing.plaf.basic.*;
  44 import javax.swing.table.*;
  45 import javax.swing.text.*;
  46 
  47 import sun.awt.shell.*;
  48 
  49 /**
  50  * <b>WARNING:</b> This class is an implementation detail and is only
  51  * public so that it can be used by two packages. You should NOT consider
  52  * this public API.
  53  * <p>
  54  * This component is intended to be used in a subclass of
  55  * javax.swing.plaf.basic.BasicFileChooserUI. It realies heavily on the
  56  * implementation of BasicFileChooserUI, and is intended to be API compatible
  57  * with earlier implementations of MetalFileChooserUI and WindowsFileChooserUI.
  58  *
  59  * @author Leif Samuelsson
  60  */
  61 public class FilePane extends JPanel implements PropertyChangeListener {
  62     // Constants for actions. These are used for the actions' ACTION_COMMAND_KEY
  63     // and as keys in the action maps for FilePane and the corresponding UI classes
  64 
  65     public final static String ACTION_APPROVE_SELECTION = "approveSelection";
  66     public final static String ACTION_CANCEL            = "cancelSelection";
  67     public final static String ACTION_EDIT_FILE_NAME    = "editFileName";
  68     public final static String ACTION_REFRESH           = "refresh";
  69     public final static String ACTION_CHANGE_TO_PARENT_DIRECTORY = "Go Up";
  70     public final static String ACTION_NEW_FOLDER        = "New Folder";
  71     public final static String ACTION_VIEW_LIST         = "viewTypeList";
  72     public final static String ACTION_VIEW_DETAILS      = "viewTypeDetails";
  73 
  74     private Action[] actions;
  75 
  76     // "enums" for setViewType()
  77     public  static final int VIEWTYPE_LIST     = 0;
  78     public  static final int VIEWTYPE_DETAILS  = 1;
  79     private static final int VIEWTYPE_COUNT    = 2;
  80 
  81     private int viewType = -1;
  82     private JPanel[] viewPanels = new JPanel[VIEWTYPE_COUNT];
  83     private JPanel currentViewPanel;
  84     private String[] viewTypeActionNames;
  85 
  86     private String filesListAccessibleName = null;
  87     private String filesDetailsAccessibleName = null;
  88 
  89     private JPopupMenu contextMenu;
  90     private JMenu viewMenu;
  91 
  92     private String viewMenuLabelText;
  93     private String refreshActionLabelText;
  94     private String newFolderActionLabelText;
  95 
  96     private String kiloByteString;
  97     private String megaByteString;
  98     private String gigaByteString;
  99 
 100     private String renameErrorTitleText;
 101     private String renameErrorText;
 102 
 103     private static final Cursor waitCursor =
 104         Cursor.getPredefinedCursor(Cursor.WAIT_CURSOR);
 105 
 106     private final KeyListener detailsKeyListener = new KeyAdapter() {
 107         private final long timeFactor;
 108 
 109         private final StringBuilder typedString = new StringBuilder();
 110 
 111         private long lastTime = 1000L;
 112 
 113         {
 114             Long l = (Long) UIManager.get("Table.timeFactor");
 115             timeFactor = (l != null) ? l : 1000L;
 116         }
 117 
 118         /**
 119          * Moves the keyboard focus to the first element whose prefix matches
 120          * the sequence of alphanumeric keys pressed by the user with delay
 121          * less than value of <code>timeFactor</code>. Subsequent same key
 122          * presses move the keyboard focus to the next object that starts with
 123          * the same letter until another key is pressed, then it is treated
 124          * as the prefix with appropriate number of the same letters followed
 125          * by first typed another letter.
 126          */
 127         public void keyTyped(KeyEvent e) {
 128             BasicDirectoryModel model = getModel();
 129             int rowCount = model.getSize();
 130 
 131             if (detailsTable == null || rowCount == 0 ||
 132                     e.isAltDown() || e.isControlDown() || e.isMetaDown()) {
 133                 return;
 134             }
 135 
 136             InputMap inputMap = detailsTable.getInputMap(JComponent.WHEN_ANCESTOR_OF_FOCUSED_COMPONENT);
 137             KeyStroke key = KeyStroke.getKeyStrokeForEvent(e);
 138 
 139             if (inputMap != null && inputMap.get(key) != null) {
 140                 return;
 141             }
 142 
 143             int startIndex = detailsTable.getSelectionModel().getLeadSelectionIndex();
 144 
 145             if (startIndex < 0) {
 146                 startIndex = 0;
 147             }
 148 
 149             if (startIndex >= rowCount) {
 150                 startIndex = rowCount - 1;
 151             }
 152 
 153             char c = e.getKeyChar();
 154 
 155             long time = e.getWhen();
 156 
 157             if (time - lastTime < timeFactor) {
 158                 if (typedString.length() == 1 && typedString.charAt(0) == c) {
 159                     // Subsequent same key presses move the keyboard focus to the next
 160                     // object that starts with the same letter.
 161                     startIndex++;
 162                 } else {
 163                     typedString.append(c);
 164                 }
 165             } else {
 166                 startIndex++;
 167 
 168                 typedString.setLength(0);
 169                 typedString.append(c);
 170             }
 171 
 172             lastTime = time;
 173 
 174             if (startIndex >= rowCount) {
 175                 startIndex = 0;
 176             }
 177 
 178             // Find next file
 179             int index = getNextMatch(startIndex, rowCount - 1);
 180 
 181             if (index < 0 && startIndex > 0) { // wrap
 182                 index = getNextMatch(0, startIndex - 1);
 183             }
 184 
 185             if (index >= 0) {
 186                 detailsTable.getSelectionModel().setSelectionInterval(index, index);
 187 
 188                 Rectangle cellRect = detailsTable.getCellRect(index,
 189                         detailsTable.convertColumnIndexToView(COLUMN_FILENAME), false);
 190                 detailsTable.scrollRectToVisible(cellRect);
 191             }
 192         }
 193 
 194         private int getNextMatch(int startIndex, int finishIndex) {
 195             BasicDirectoryModel model = getModel();
 196             JFileChooser fileChooser = getFileChooser();
 197             DetailsTableRowSorter rowSorter = getRowSorter();
 198 
 199             String prefix = typedString.toString().toLowerCase();
 200 
 201             // Search element
 202             for (int index = startIndex; index <= finishIndex; index++) {
 203                 File file = (File) model.getElementAt(rowSorter.convertRowIndexToModel(index));
 204 
 205                 String fileName = fileChooser.getName(file).toLowerCase();
 206 
 207                 if (fileName.startsWith(prefix)) {
 208                     return index;
 209                 }
 210             }
 211 
 212             return -1;
 213         }
 214     };
 215 
 216     private FocusListener editorFocusListener = new FocusAdapter() {
 217         public void focusLost(FocusEvent e) {
 218             if (! e.isTemporary()) {
 219                 applyEdit();
 220             }
 221         }
 222     };
 223 
 224     private static FocusListener repaintListener = new FocusListener() {
 225         public void focusGained(FocusEvent fe) {
 226             repaintSelection(fe.getSource());
 227         }
 228 
 229         public void focusLost(FocusEvent fe) {
 230             repaintSelection(fe.getSource());
 231         }
 232 
 233         private void repaintSelection(Object source) {
 234             if (source instanceof JList) {
 235                 repaintListSelection((JList)source);
 236             } else if (source instanceof JTable) {
 237                 repaintTableSelection((JTable)source);
 238             }
 239         }
 240 
 241         private void repaintListSelection(JList list) {
 242             int[] indices = list.getSelectedIndices();
 243             for (int i : indices) {
 244                 Rectangle bounds = list.getCellBounds(i, i);
 245                 list.repaint(bounds);
 246             }
 247         }
 248 
 249         private void repaintTableSelection(JTable table) {
 250             int minRow = table.getSelectionModel().getMinSelectionIndex();
 251             int maxRow = table.getSelectionModel().getMaxSelectionIndex();
 252             if (minRow == -1 || maxRow == -1) {
 253                 return;
 254             }
 255 
 256             int col0 = table.convertColumnIndexToView(COLUMN_FILENAME);
 257 
 258             Rectangle first = table.getCellRect(minRow, col0, false);
 259             Rectangle last = table.getCellRect(maxRow, col0, false);
 260             Rectangle dirty = first.union(last);
 261             table.repaint(dirty);
 262         }
 263     };
 264 
 265     private boolean smallIconsView = false;
 266     private Border  listViewBorder;
 267     private Color   listViewBackground;
 268     private boolean listViewWindowsStyle;
 269     private boolean readOnly;
 270     private boolean fullRowSelection = false;
 271 
 272     private ListSelectionModel listSelectionModel;
 273     private JList list;
 274     private JTable detailsTable;
 275 
 276     private static final int COLUMN_FILENAME = 0;
 277 
 278     // Provides a way to recognize a newly created folder, so it can
 279     // be selected when it appears in the model.
 280     private File newFolderFile;
 281 
 282     // Used for accessing methods in the corresponding UI class
 283     private FileChooserUIAccessor fileChooserUIAccessor;
 284     private DetailsTableModel detailsTableModel;
 285     private DetailsTableRowSorter rowSorter;
 286 
 287     public FilePane(FileChooserUIAccessor fileChooserUIAccessor) {
 288         super(new BorderLayout());
 289 
 290         this.fileChooserUIAccessor = fileChooserUIAccessor;
 291 
 292         installDefaults();
 293         createActionMap();
 294     }
 295 
 296     public void uninstallUI() {
 297         if (getModel() != null) {
 298             getModel().removePropertyChangeListener(this);
 299         }
 300     }
 301 
 302     protected JFileChooser getFileChooser() {
 303         return fileChooserUIAccessor.getFileChooser();
 304     }
 305 
 306     protected BasicDirectoryModel getModel() {
 307         return fileChooserUIAccessor.getModel();
 308     }
 309 
 310     public int getViewType() {
 311         return viewType;
 312     }
 313 
 314     public void setViewType(int viewType) {
 315         int oldValue = this.viewType;
 316         if (viewType == oldValue) {
 317             return;
 318         }
 319         this.viewType = viewType;
 320 
 321         switch (viewType) {
 322           case VIEWTYPE_LIST:
 323             if (viewPanels[viewType] == null) {
 324                 JPanel p = fileChooserUIAccessor.createList();
 325                 if (p == null) {
 326                     p = createList();
 327                 }
 328                 setViewPanel(viewType, p);
 329             }
 330             list.setLayoutOrientation(JList.VERTICAL_WRAP);
 331             break;
 332 
 333           case VIEWTYPE_DETAILS:
 334             if (viewPanels[viewType] == null) {
 335                 JPanel p = fileChooserUIAccessor.createDetailsView();
 336                 if (p == null) {
 337                     p = createDetailsView();
 338                 }
 339                 setViewPanel(viewType, p);
 340             }
 341             break;
 342         }
 343         JPanel oldViewPanel = currentViewPanel;
 344         currentViewPanel = viewPanels[viewType];
 345         if (currentViewPanel != oldViewPanel) {
 346             if (oldViewPanel != null) {
 347                 remove(oldViewPanel);
 348             }
 349             add(currentViewPanel, BorderLayout.CENTER);
 350             revalidate();
 351             repaint();
 352         }
 353         updateViewMenu();
 354         firePropertyChange("viewType", oldValue, viewType);
 355     }
 356 
 357     class ViewTypeAction extends AbstractAction {
 358         private int viewType;
 359 
 360         ViewTypeAction(int viewType) {
 361             super(viewTypeActionNames[viewType]);
 362             this.viewType = viewType;
 363 
 364             String cmd;
 365             switch (viewType) {
 366                 case VIEWTYPE_LIST:    cmd = ACTION_VIEW_LIST;    break;
 367                 case VIEWTYPE_DETAILS: cmd = ACTION_VIEW_DETAILS; break;
 368                 default:               cmd = (String)getValue(Action.NAME);
 369             }
 370             putValue(Action.ACTION_COMMAND_KEY, cmd);
 371         }
 372 
 373         public void actionPerformed(ActionEvent e) {
 374             setViewType(viewType);
 375         }
 376     }
 377 
 378     public Action getViewTypeAction(int viewType) {
 379         return new ViewTypeAction(viewType);
 380     }
 381 
 382     private static void recursivelySetInheritsPopupMenu(Container container, boolean b) {
 383         if (container instanceof JComponent) {
 384             ((JComponent)container).setInheritsPopupMenu(b);
 385         }
 386         int n = container.getComponentCount();
 387         for (int i = 0; i < n; i++) {
 388             recursivelySetInheritsPopupMenu((Container)container.getComponent(i), b);
 389         }
 390     }
 391 
 392     public void setViewPanel(int viewType, JPanel viewPanel) {
 393         viewPanels[viewType] = viewPanel;
 394         recursivelySetInheritsPopupMenu(viewPanel, true);
 395 
 396         switch (viewType) {
 397           case VIEWTYPE_LIST:
 398             list = (JList)findChildComponent(viewPanels[viewType], JList.class);
 399             if (listSelectionModel == null) {
 400                 listSelectionModel = list.getSelectionModel();
 401                 if (detailsTable != null) {
 402                     detailsTable.setSelectionModel(listSelectionModel);
 403                 }
 404             } else {
 405                 list.setSelectionModel(listSelectionModel);
 406             }
 407             break;
 408 
 409           case VIEWTYPE_DETAILS:
 410             detailsTable = (JTable)findChildComponent(viewPanels[viewType], JTable.class);
 411             detailsTable.setRowHeight(Math.max(detailsTable.getFont().getSize() + 4, 16+1));
 412             if (listSelectionModel != null) {
 413                 detailsTable.setSelectionModel(listSelectionModel);
 414             }
 415             break;
 416         }
 417         if (this.viewType == viewType) {
 418             if (currentViewPanel != null) {
 419                 remove(currentViewPanel);
 420             }
 421             currentViewPanel = viewPanel;
 422             add(currentViewPanel, BorderLayout.CENTER);
 423             revalidate();
 424             repaint();
 425         }
 426     }
 427 
 428     protected void installDefaults() {
 429         Locale l = getFileChooser().getLocale();
 430 
 431         listViewBorder       = UIManager.getBorder("FileChooser.listViewBorder");
 432         listViewBackground   = UIManager.getColor("FileChooser.listViewBackground");
 433         listViewWindowsStyle = UIManager.getBoolean("FileChooser.listViewWindowsStyle");
 434         readOnly             = UIManager.getBoolean("FileChooser.readOnly");
 435 
 436         // TODO: On windows, get the following localized strings from the OS
 437 
 438         viewMenuLabelText =
 439                         UIManager.getString("FileChooser.viewMenuLabelText", l);
 440         refreshActionLabelText =
 441                         UIManager.getString("FileChooser.refreshActionLabelText", l);
 442         newFolderActionLabelText =
 443                         UIManager.getString("FileChooser.newFolderActionLabelText", l);
 444 
 445         viewTypeActionNames = new String[VIEWTYPE_COUNT];
 446         viewTypeActionNames[VIEWTYPE_LIST] =
 447                         UIManager.getString("FileChooser.listViewActionLabelText", l);
 448         viewTypeActionNames[VIEWTYPE_DETAILS] =
 449                         UIManager.getString("FileChooser.detailsViewActionLabelText", l);
 450 
 451         kiloByteString = UIManager.getString("FileChooser.fileSizeKiloBytes", l);
 452         megaByteString = UIManager.getString("FileChooser.fileSizeMegaBytes", l);
 453         gigaByteString = UIManager.getString("FileChooser.fileSizeGigaBytes", l);
 454         fullRowSelection = UIManager.getBoolean("FileView.fullRowSelection");
 455 
 456         filesListAccessibleName = UIManager.getString("FileChooser.filesListAccessibleName", l);
 457         filesDetailsAccessibleName = UIManager.getString("FileChooser.filesDetailsAccessibleName", l);
 458 
 459         renameErrorTitleText = UIManager.getString("FileChooser.renameErrorTitleText", l);
 460         renameErrorText = UIManager.getString("FileChooser.renameErrorText", l);
 461     }
 462 
 463     /**
 464      * Fetches the command list for the FilePane. These commands
 465      * are useful for binding to events, such as in a keymap.
 466      *
 467      * @return the command list
 468      */
 469     public Action[] getActions() {
 470         if (actions == null) {
 471             class FilePaneAction extends AbstractAction {
 472                 FilePaneAction(String name) {
 473                     this(name, name);
 474                 }
 475 
 476                 FilePaneAction(String name, String cmd) {
 477                     super(name);
 478                     putValue(Action.ACTION_COMMAND_KEY, cmd);
 479                 }
 480 
 481                 public void actionPerformed(ActionEvent e) {
 482                     String cmd = (String)getValue(Action.ACTION_COMMAND_KEY);
 483 
 484                     if (cmd == ACTION_CANCEL) {
 485                         if (editFile != null) {
 486                            cancelEdit();
 487                         } else {
 488                            getFileChooser().cancelSelection();
 489                         }
 490                     } else if (cmd == ACTION_EDIT_FILE_NAME) {
 491                         JFileChooser fc = getFileChooser();
 492                         int index = listSelectionModel.getMinSelectionIndex();
 493                         if (index >= 0 && editFile == null &&
 494                             (!fc.isMultiSelectionEnabled() ||
 495                              fc.getSelectedFiles().length <= 1)) {
 496 
 497                             editFileName(index);
 498                         }
 499                     } else if (cmd == ACTION_REFRESH) {
 500                         getFileChooser().rescanCurrentDirectory();
 501                     }
 502                 }
 503 
 504                 public boolean isEnabled() {
 505                     String cmd = (String)getValue(Action.ACTION_COMMAND_KEY);
 506                     if (cmd == ACTION_CANCEL) {
 507                         return getFileChooser().isEnabled();
 508                     } else if (cmd == ACTION_EDIT_FILE_NAME) {
 509                         return !readOnly && getFileChooser().isEnabled();
 510                     } else {
 511                         return true;
 512                     }
 513                 }
 514             }
 515 
 516             ArrayList<Action> actionList = new ArrayList<Action>(8);
 517             Action action;
 518 
 519             actionList.add(new FilePaneAction(ACTION_CANCEL));
 520             actionList.add(new FilePaneAction(ACTION_EDIT_FILE_NAME));
 521             actionList.add(new FilePaneAction(refreshActionLabelText, ACTION_REFRESH));
 522 
 523             action = fileChooserUIAccessor.getApproveSelectionAction();
 524             if (action != null) {
 525                 actionList.add(action);
 526             }
 527             action = fileChooserUIAccessor.getChangeToParentDirectoryAction();
 528             if (action != null) {
 529                 actionList.add(action);
 530             }
 531             action = getNewFolderAction();
 532             if (action != null) {
 533                 actionList.add(action);
 534             }
 535             action = getViewTypeAction(VIEWTYPE_LIST);
 536             if (action != null) {
 537                 actionList.add(action);
 538             }
 539             action = getViewTypeAction(VIEWTYPE_DETAILS);
 540             if (action != null) {
 541                 actionList.add(action);
 542             }
 543             actions = actionList.toArray(new Action[actionList.size()]);
 544         }
 545 
 546         return actions;
 547     }
 548 
 549     protected void createActionMap() {
 550         addActionsToMap(super.getActionMap(), getActions());
 551     }
 552 
 553 
 554     public static void addActionsToMap(ActionMap map, Action[] actions) {
 555         if (map != null && actions != null) {
 556             for (Action a : actions) {
 557                 String cmd = (String)a.getValue(Action.ACTION_COMMAND_KEY);
 558                 if (cmd == null) {
 559                     cmd = (String)a.getValue(Action.NAME);
 560                 }
 561                 map.put(cmd, a);
 562             }
 563         }
 564     }
 565 
 566 
 567     private void updateListRowCount(JList list) {
 568         if (smallIconsView) {
 569             list.setVisibleRowCount(getModel().getSize() / 3);
 570         } else {
 571             list.setVisibleRowCount(-1);
 572         }
 573     }
 574 
 575     public JPanel createList() {
 576         JPanel p = new JPanel(new BorderLayout());
 577         final JFileChooser fileChooser = getFileChooser();
 578         final JList list = new JList() {
 579             public int getNextMatch(String prefix, int startIndex, Position.Bias bias) {
 580                 ListModel model = getModel();
 581                 int max = model.getSize();
 582                 if (prefix == null || startIndex < 0 || startIndex >= max) {
 583                     throw new IllegalArgumentException();
 584                 }
 585                 // start search from the next element before/after the selected element
 586                 boolean backwards = (bias == Position.Bias.Backward);
 587                 for (int i = startIndex; backwards ? i >= 0 : i < max; i += (backwards ?  -1 : 1)) {
 588                     String filename = fileChooser.getName((File)model.getElementAt(i));
 589                     if (filename.regionMatches(true, 0, prefix, 0, prefix.length())) {
 590                         return i;
 591                     }
 592                 }
 593                 return -1;
 594             }
 595         };
 596         list.setCellRenderer(new FileRenderer());
 597         list.setLayoutOrientation(JList.VERTICAL_WRAP);
 598 
 599         // 4835633 : tell BasicListUI that this is a file list
 600         list.putClientProperty("List.isFileList", Boolean.TRUE);
 601 
 602         if (listViewWindowsStyle) {
 603             list.addFocusListener(repaintListener);
 604         }
 605 
 606         updateListRowCount(list);
 607 
 608         getModel().addListDataListener(new ListDataListener() {
 609             public void intervalAdded(ListDataEvent e) {
 610                 updateListRowCount(list);
 611             }
 612             public void intervalRemoved(ListDataEvent e) {
 613                 updateListRowCount(list);
 614             }
 615             public void contentsChanged(ListDataEvent e) {
 616                 if (isShowing()) {
 617                     clearSelection();
 618                 }
 619                 updateListRowCount(list);
 620             }
 621         });
 622 
 623         getModel().addPropertyChangeListener(this);
 624 
 625         if (fileChooser.isMultiSelectionEnabled()) {
 626             list.setSelectionMode(ListSelectionModel.MULTIPLE_INTERVAL_SELECTION);
 627         } else {
 628             list.setSelectionMode(ListSelectionModel.SINGLE_SELECTION);
 629         }
 630         list.setModel(new SortableListModel());
 631 
 632         list.addListSelectionListener(createListSelectionListener());
 633         list.addMouseListener(getMouseHandler());
 634 
 635         JScrollPane scrollpane = new JScrollPane(list);
 636         if (listViewBackground != null) {
 637             list.setBackground(listViewBackground);
 638         }
 639         if (listViewBorder != null) {
 640             scrollpane.setBorder(listViewBorder);
 641         }
 642 
 643         list.putClientProperty(AccessibleContext.ACCESSIBLE_NAME_PROPERTY, filesListAccessibleName);
 644 
 645         p.add(scrollpane, BorderLayout.CENTER);
 646         return p;
 647     }
 648 
 649     /**
 650      * This model allows for sorting JList
 651      */
 652     private class SortableListModel extends AbstractListModel
 653             implements TableModelListener, RowSorterListener {
 654 
 655         public SortableListModel() {
 656             getDetailsTableModel().addTableModelListener(this);
 657             getRowSorter().addRowSorterListener(this);
 658         }
 659 
 660         public int getSize() {
 661             return getModel().getSize();
 662         }
 663 
 664         public Object getElementAt(int index) {
 665             // JList doesn't support RowSorter so far, so we put it into the list model
 666             return getModel().getElementAt(getRowSorter().convertRowIndexToModel(index));
 667         }
 668 
 669         public void tableChanged(TableModelEvent e) {
 670             fireContentsChanged(this, 0, getSize());
 671         }
 672 
 673         public void sorterChanged(RowSorterEvent e) {
 674             fireContentsChanged(this, 0, getSize());
 675         }
 676     }
 677 
 678     private DetailsTableModel getDetailsTableModel() {
 679         if(detailsTableModel == null) {
 680             detailsTableModel = new DetailsTableModel(getFileChooser());
 681         }
 682         return detailsTableModel;
 683     }
 684 
 685     class DetailsTableModel extends AbstractTableModel implements ListDataListener {
 686         JFileChooser chooser;
 687         BasicDirectoryModel directoryModel;
 688 
 689         ShellFolderColumnInfo[] columns;
 690         int[] columnMap;
 691 
 692         DetailsTableModel(JFileChooser fc) {
 693             this.chooser = fc;
 694             directoryModel = getModel();
 695             directoryModel.addListDataListener(this);
 696 
 697             updateColumnInfo();
 698         }
 699 
 700         void updateColumnInfo() {
 701             File dir = chooser.getCurrentDirectory();
 702             if (dir != null && fileChooserUIAccessor.usesShellFolder()) {
 703                 try {
 704                     dir = ShellFolder.getShellFolder(dir);
 705                 } catch (FileNotFoundException e) {
 706                     // Leave dir without changing
 707                 }
 708             }
 709 
 710             ShellFolderColumnInfo[] allColumns = ShellFolder.getFolderColumns(dir);
 711 
 712             ArrayList<ShellFolderColumnInfo> visibleColumns =
 713                     new ArrayList<ShellFolderColumnInfo>();
 714             columnMap = new int[allColumns.length];
 715             for (int i = 0; i < allColumns.length; i++) {
 716                 ShellFolderColumnInfo column = allColumns[i];
 717                 if (column.isVisible()) {
 718                     columnMap[visibleColumns.size()] = i;
 719                     visibleColumns.add(column);
 720                 }
 721             }
 722 
 723             columns = new ShellFolderColumnInfo[visibleColumns.size()];
 724             visibleColumns.toArray(columns);
 725             columnMap = Arrays.copyOf(columnMap, columns.length);
 726 
 727             List<? extends RowSorter.SortKey> sortKeys =
 728                     (rowSorter == null) ? null : rowSorter.getSortKeys();
 729             fireTableStructureChanged();
 730             restoreSortKeys(sortKeys);
 731         }
 732 
 733         private void restoreSortKeys(List<? extends RowSorter.SortKey> sortKeys) {
 734             if (sortKeys != null) {
 735                 // check if preserved sortKeys are valid for this folder
 736                 for (int i = 0; i < sortKeys.size(); i++) {
 737                     RowSorter.SortKey sortKey = sortKeys.get(i);
 738                     if (sortKey.getColumn() >= columns.length) {
 739                         sortKeys = null;
 740                         break;
 741                     }
 742                 }
 743                 if (sortKeys != null) {
 744                     rowSorter.setSortKeys(sortKeys);
 745                 }
 746             }
 747         }
 748 
 749         public int getRowCount() {
 750             return directoryModel.getSize();
 751         }
 752 
 753         public int getColumnCount() {
 754             return columns.length;
 755         }
 756 
 757         public Object getValueAt(int row, int col) {
 758             // Note: It is very important to avoid getting info on drives, as
 759             // this will trigger "No disk in A:" and similar dialogs.
 760             //
 761             // Use (f.exists() && !chooser.getFileSystemView().isFileSystemRoot(f)) to
 762             // determine if it is safe to call methods directly on f.
 763             return getFileColumnValue((File)directoryModel.getElementAt(row), col);
 764         }
 765 
 766         private Object getFileColumnValue(File f, int col) {
 767             return (col == COLUMN_FILENAME)
 768                     ? f // always return the file itself for the 1st column
 769                     : ShellFolder.getFolderColumnValue(f, columnMap[col]);
 770         }
 771 
 772         public void setValueAt(Object value, int row, int col) {
 773             if (col == COLUMN_FILENAME) {
 774                 JFileChooser chooser = getFileChooser();
 775                 File f = (File)getValueAt(row, col);
 776                 if (f != null) {
 777                     String oldDisplayName = chooser.getName(f);
 778                     String oldFileName = f.getName();
 779                     String newDisplayName = ((String)value).trim();
 780                     String newFileName;
 781 
 782                     if (!newDisplayName.equals(oldDisplayName)) {
 783                         newFileName = newDisplayName;
 784                         //Check if extension is hidden from user
 785                         int i1 = oldFileName.length();
 786                         int i2 = oldDisplayName.length();
 787                         if (i1 > i2 && oldFileName.charAt(i2) == '.') {
 788                             newFileName = newDisplayName + oldFileName.substring(i2);
 789                         }
 790 
 791                         // rename
 792                         FileSystemView fsv = chooser.getFileSystemView();
 793                         File f2 = fsv.createFileObject(f.getParentFile(), newFileName);
 794                         if (!f2.exists()) {
 795                             if (FilePane.this.getModel().renameFile(f, f2)) {
 796                                 if (fsv.isParent(chooser.getCurrentDirectory(), f2)) {
 797                                     if (chooser.isMultiSelectionEnabled()) {
 798                                         chooser.setSelectedFiles(new File[]{f2});
 799                                     } else {
 800                                         chooser.setSelectedFile(f2);
 801                                     }
 802                                 } else {
 803                                     // Could be because of delay in updating Desktop folder
 804                                     // chooser.setSelectedFile(null);
 805                                 }
 806                             } else {
 807                                 JOptionPane.showMessageDialog(chooser, MessageFormat.format(renameErrorText, oldFileName),
 808                                         renameErrorTitleText, JOptionPane.ERROR_MESSAGE);
 809                             }
 810                         }
 811                     }
 812                 }
 813             }
 814         }
 815 
 816         public boolean isCellEditable(int row, int column) {
 817             File currentDirectory = getFileChooser().getCurrentDirectory();
 818             return (!readOnly && column == COLUMN_FILENAME && canWrite(currentDirectory));
 819         }
 820 
 821         public void contentsChanged(ListDataEvent e) {
 822             // Update the selection after the model has been updated
 823             new DelayedSelectionUpdater();
 824             fireTableDataChanged();
 825         }
 826 
 827         public void intervalAdded(ListDataEvent e) {
 828             int i0 = e.getIndex0();
 829             int i1 = e.getIndex1();
 830             if (i0 == i1) {
 831                 File file = (File)getModel().getElementAt(i0);
 832                 if (file.equals(newFolderFile)) {
 833                     new DelayedSelectionUpdater(file);
 834                     newFolderFile = null;
 835                 }
 836             }
 837 
 838             fireTableRowsInserted(e.getIndex0(), e.getIndex1());
 839         }
 840         public void intervalRemoved(ListDataEvent e) {
 841             fireTableRowsDeleted(e.getIndex0(), e.getIndex1());
 842         }
 843 
 844         public ShellFolderColumnInfo[] getColumns() {
 845             return columns;
 846         }
 847     }
 848 
 849 
 850     private void updateDetailsColumnModel(JTable table) {
 851         if (table != null) {
 852             ShellFolderColumnInfo[] columns = detailsTableModel.getColumns();
 853 
 854             TableColumnModel columnModel = new DefaultTableColumnModel();
 855             for (int i = 0; i < columns.length; i++) {
 856                 ShellFolderColumnInfo dataItem = columns[i];
 857                 TableColumn column = new TableColumn(i);
 858 
 859                 String title = dataItem.getTitle();
 860                 if (title != null && title.startsWith("FileChooser.") && title.endsWith("HeaderText")) {
 861                     // the column must have a string resource that we try to get
 862                     String uiTitle = UIManager.getString(title, table.getLocale());
 863                     if (uiTitle != null) {
 864                         title = uiTitle;
 865                     }
 866                 }
 867                 column.setHeaderValue(title);
 868 
 869                 Integer width = dataItem.getWidth();
 870                 if (width != null) {
 871                     column.setPreferredWidth(width);
 872                     // otherwise we let JTable to decide the actual width
 873                 }
 874 
 875                 columnModel.addColumn(column);
 876             }
 877 
 878             // Install cell editor for editing file name
 879             if (!readOnly && columnModel.getColumnCount() > COLUMN_FILENAME) {
 880                 columnModel.getColumn(COLUMN_FILENAME).
 881                         setCellEditor(getDetailsTableCellEditor());
 882             }
 883 
 884             table.setColumnModel(columnModel);
 885         }
 886     }
 887 
 888     private DetailsTableRowSorter getRowSorter() {
 889         if (rowSorter == null) {
 890             rowSorter = new DetailsTableRowSorter();
 891         }
 892         return rowSorter;
 893     }
 894 
 895     private class DetailsTableRowSorter extends TableRowSorter<TableModel> {
 896         public DetailsTableRowSorter() {
 897             setModelWrapper(new SorterModelWrapper());
 898         }
 899 
 900         public void updateComparators(ShellFolderColumnInfo [] columns) {
 901             for (int i = 0; i < columns.length; i++) {
 902                 Comparator c = columns[i].getComparator();
 903                 if (c != null) {
 904                     c = new DirectoriesFirstComparatorWrapper(i, c);
 905                 }
 906                 setComparator(i, c);
 907             }
 908         }
 909 
 910         public void modelStructureChanged() {
 911             super.modelStructureChanged();
 912             updateComparators(detailsTableModel.getColumns());
 913         }
 914 
 915         private class SorterModelWrapper extends ModelWrapper<TableModel, Integer> {
 916             public TableModel getModel() {
 917                 return getDetailsTableModel();
 918             }
 919 
 920             public int getColumnCount() {
 921                 return getDetailsTableModel().getColumnCount();
 922             }
 923 
 924             public int getRowCount() {
 925                 return getDetailsTableModel().getRowCount();
 926             }
 927 
 928             public Object getValueAt(int row, int column) {
 929                 return FilePane.this.getModel().getElementAt(row);
 930             }
 931 
 932             public Integer getIdentifier(int row) {
 933                 return row;
 934             }
 935         }
 936     }
 937 
 938     /**
 939      * This class sorts directories before files, comparing directory to
 940      * directory and file to file using the wrapped comparator.
 941      */
 942     private class DirectoriesFirstComparatorWrapper implements Comparator<File> {
 943         private Comparator comparator;
 944         private int column;
 945 
 946         public DirectoriesFirstComparatorWrapper(int column, Comparator comparator) {
 947             this.column = column;
 948             this.comparator = comparator;
 949         }
 950 
 951         public int compare(File f1, File f2) {
 952             if (f1 != null && f2 != null) {
 953                 boolean traversable1 = getFileChooser().isTraversable(f1);
 954                 boolean traversable2 = getFileChooser().isTraversable(f2);
 955                 // directories go first
 956                 if (traversable1 && !traversable2) {
 957                     return -1;
 958                 }
 959                 if (!traversable1 && traversable2) {
 960                     return 1;
 961                 }
 962             }
 963             if (detailsTableModel.getColumns()[column].isCompareByColumn()) {
 964                 return comparator.compare(
 965                         getDetailsTableModel().getFileColumnValue(f1, column),
 966                         getDetailsTableModel().getFileColumnValue(f2, column)
 967                 );
 968             }
 969             // For this column we need to pass the file itself (not a
 970             // column value) to the comparator
 971             return comparator.compare(f1, f2);
 972         }
 973     }
 974 
 975     private DetailsTableCellEditor tableCellEditor;
 976 
 977     private DetailsTableCellEditor getDetailsTableCellEditor() {
 978         if (tableCellEditor == null) {
 979             tableCellEditor = new DetailsTableCellEditor(new JTextField());
 980         }
 981         return tableCellEditor;
 982     }
 983 
 984     private class DetailsTableCellEditor extends DefaultCellEditor {
 985         private final JTextField tf;
 986 
 987         public DetailsTableCellEditor(JTextField tf) {
 988             super(tf);
 989             this.tf = tf;
 990             tf.setName("Table.editor");
 991             tf.addFocusListener(editorFocusListener);
 992         }
 993 
 994         public Component getTableCellEditorComponent(JTable table, Object value,
 995                                                      boolean isSelected, int row, int column) {
 996             Component comp = super.getTableCellEditorComponent(table, value,
 997                     isSelected, row, column);
 998             if (value instanceof File) {
 999                 tf.setText(getFileChooser().getName((File) value));
1000                 tf.selectAll();
1001             }
1002             return comp;
1003         }
1004     }
1005 
1006 
1007     class DetailsTableCellRenderer extends DefaultTableCellRenderer {
1008         JFileChooser chooser;
1009         DateFormat df;
1010 
1011         DetailsTableCellRenderer(JFileChooser chooser) {
1012             this.chooser = chooser;
1013             df = DateFormat.getDateTimeInstance(DateFormat.SHORT, DateFormat.SHORT,
1014                                                 chooser.getLocale());
1015         }
1016 
1017         public void setBounds(int x, int y, int width, int height) {
1018         if (getHorizontalAlignment() == SwingConstants.LEADING &&
1019                     !fullRowSelection) {
1020                 // Restrict width to actual text
1021                 width = Math.min(width, this.getPreferredSize().width+4);
1022             } else {
1023                 x -= 4;
1024             }
1025             super.setBounds(x, y, width, height);
1026         }
1027 
1028 
1029         public Insets getInsets(Insets i) {
1030             // Provide some space between columns
1031             i = super.getInsets(i);
1032             i.left  += 4;
1033             i.right += 4;
1034             return i;
1035         }
1036 
1037         public Component getTableCellRendererComponent(JTable table, Object value,
1038                               boolean isSelected, boolean hasFocus, int row, int column) {
1039 
1040             if ((table.convertColumnIndexToModel(column) != COLUMN_FILENAME ||
1041                     (listViewWindowsStyle && !table.isFocusOwner())) &&
1042                     !fullRowSelection) {
1043                 isSelected = false;
1044             }
1045 
1046             super.getTableCellRendererComponent(table, value, isSelected,
1047                                                        hasFocus, row, column);
1048 
1049             setIcon(null);
1050 
1051             int modelColumn = table.convertColumnIndexToModel(column);
1052             ShellFolderColumnInfo columnInfo = detailsTableModel.getColumns()[modelColumn];
1053 
1054             Integer alignment = columnInfo.getAlignment();
1055             if (alignment == null) {
1056                 alignment = (value instanceof Number)
1057                         ? SwingConstants.RIGHT
1058                         : SwingConstants.LEADING;
1059             }
1060 
1061             setHorizontalAlignment(alignment);
1062 
1063             // formatting cell text
1064             // TODO: it's rather a temporary trick, to be revised
1065             String text;
1066 
1067             if (value == null) {
1068                 text = "";
1069 
1070             } else if (value instanceof File) {
1071                 File file = (File)value;
1072                 text = chooser.getName(file);
1073                 Icon icon = chooser.getIcon(file);
1074                 setIcon(icon);
1075 
1076             } else if (value instanceof Long) {
1077                 long len = ((Long) value) / 1024L;
1078                 if (listViewWindowsStyle) {
1079                     text = MessageFormat.format(kiloByteString, len + 1);
1080                 } else if (len < 1024L) {
1081                     text = MessageFormat.format(kiloByteString, (len == 0L) ? 1L : len);
1082                 } else {
1083                     len /= 1024L;
1084                     if (len < 1024L) {
1085                         text = MessageFormat.format(megaByteString, len);
1086                     } else {
1087                         len /= 1024L;
1088                         text = MessageFormat.format(gigaByteString, len);
1089                     }
1090                 }
1091 
1092             } else if (value instanceof Date) {
1093                 text = df.format((Date)value);
1094 
1095             } else {
1096                 text = value.toString();
1097             }
1098 
1099             setText(text);
1100 
1101             return this;
1102         }
1103     }
1104 
1105     public JPanel createDetailsView() {
1106         final JFileChooser chooser = getFileChooser();
1107 
1108         JPanel p = new JPanel(new BorderLayout());
1109 
1110         final JTable detailsTable = new JTable(getDetailsTableModel()) {
1111             // Handle Escape key events here
1112             protected boolean processKeyBinding(KeyStroke ks, KeyEvent e, int condition, boolean pressed) {
1113                 if (e.getKeyCode() == KeyEvent.VK_ESCAPE && getCellEditor() == null) {
1114                     // We are not editing, forward to filechooser.
1115                     chooser.dispatchEvent(e);
1116                     return true;
1117                 }
1118                 return super.processKeyBinding(ks, e, condition, pressed);
1119             }
1120 
1121             public void tableChanged(TableModelEvent e) {
1122                 super.tableChanged(e);
1123 
1124                 if (e.getFirstRow() == TableModelEvent.HEADER_ROW) {
1125                     // update header with possibly changed column set
1126                     updateDetailsColumnModel(this);
1127                 }
1128             }
1129         };
1130 
1131         detailsTable.setRowSorter(getRowSorter());
1132         detailsTable.setAutoCreateColumnsFromModel(false);
1133         detailsTable.setComponentOrientation(chooser.getComponentOrientation());
1134         detailsTable.setAutoResizeMode(JTable.AUTO_RESIZE_OFF);
1135         detailsTable.setShowGrid(false);
1136         detailsTable.putClientProperty("JTable.autoStartsEdit", Boolean.FALSE);
1137         detailsTable.addKeyListener(detailsKeyListener);
1138 
1139         Font font = list.getFont();
1140         detailsTable.setFont(font);
1141         detailsTable.setIntercellSpacing(new Dimension(0, 0));
1142 
1143         TableCellRenderer headerRenderer =
1144                 new AlignableTableHeaderRenderer(detailsTable.getTableHeader().getDefaultRenderer());
1145         detailsTable.getTableHeader().setDefaultRenderer(headerRenderer);
1146         TableCellRenderer cellRenderer = new DetailsTableCellRenderer(chooser);
1147         detailsTable.setDefaultRenderer(Object.class, cellRenderer);
1148 
1149         // So that drag can be started on a mouse press
1150         detailsTable.getColumnModel().getSelectionModel().
1151             setSelectionMode(ListSelectionModel.SINGLE_SELECTION);
1152 
1153         detailsTable.addMouseListener(getMouseHandler());
1154         // No need to addListSelectionListener because selections are forwarded
1155         // to our JList.
1156 
1157         // 4835633 : tell BasicTableUI that this is a file list
1158         detailsTable.putClientProperty("Table.isFileList", Boolean.TRUE);
1159 
1160         if (listViewWindowsStyle) {
1161             detailsTable.addFocusListener(repaintListener);
1162         }
1163 
1164         // TAB/SHIFT-TAB should transfer focus and ENTER should select an item.
1165         // We don't want them to navigate within the table
1166         ActionMap am = SwingUtilities.getUIActionMap(detailsTable);
1167         am.remove("selectNextRowCell");
1168         am.remove("selectPreviousRowCell");
1169         am.remove("selectNextColumnCell");
1170         am.remove("selectPreviousColumnCell");
1171         detailsTable.setFocusTraversalKeys(KeyboardFocusManager.FORWARD_TRAVERSAL_KEYS,
1172                      null);
1173         detailsTable.setFocusTraversalKeys(KeyboardFocusManager.BACKWARD_TRAVERSAL_KEYS,
1174                      null);
1175 
1176         JScrollPane scrollpane = new JScrollPane(detailsTable);
1177         scrollpane.setComponentOrientation(chooser.getComponentOrientation());
1178         LookAndFeel.installColors(scrollpane.getViewport(), "Table.background", "Table.foreground");
1179 
1180         // Adjust width of first column so the table fills the viewport when
1181         // first displayed (temporary listener).
1182         scrollpane.addComponentListener(new ComponentAdapter() {
1183             public void componentResized(ComponentEvent e) {
1184                 JScrollPane sp = (JScrollPane)e.getComponent();
1185                 fixNameColumnWidth(sp.getViewport().getSize().width);
1186                 sp.removeComponentListener(this);
1187             }
1188         });
1189 
1190         // 4835633.
1191         // If the mouse is pressed in the area below the Details view table, the
1192         // event is not dispatched to the Table MouseListener but to the
1193         // scrollpane.  Listen for that here so we can clear the selection.
1194         scrollpane.addMouseListener(new MouseAdapter() {
1195             public void mousePressed(MouseEvent e) {
1196                 JScrollPane jsp = ((JScrollPane)e.getComponent());
1197                 JTable table = (JTable)jsp.getViewport().getView();
1198 
1199                 if (!e.isShiftDown() || table.getSelectionModel().getSelectionMode() == ListSelectionModel.SINGLE_SELECTION) {
1200                     clearSelection();
1201                     TableCellEditor tce = table.getCellEditor();
1202                     if (tce != null) {
1203                         tce.stopCellEditing();
1204                     }
1205                 }
1206             }
1207         });
1208 
1209         detailsTable.setForeground(list.getForeground());
1210         detailsTable.setBackground(list.getBackground());
1211 
1212         if (listViewBorder != null) {
1213             scrollpane.setBorder(listViewBorder);
1214         }
1215         p.add(scrollpane, BorderLayout.CENTER);
1216 
1217         detailsTableModel.fireTableStructureChanged();
1218 
1219         detailsTable.putClientProperty(AccessibleContext.ACCESSIBLE_NAME_PROPERTY, filesDetailsAccessibleName);
1220 
1221         return p;
1222     } // createDetailsView
1223 
1224     private class AlignableTableHeaderRenderer implements TableCellRenderer {
1225         TableCellRenderer wrappedRenderer;
1226 
1227         public AlignableTableHeaderRenderer(TableCellRenderer wrappedRenderer) {
1228             this.wrappedRenderer = wrappedRenderer;
1229         }
1230 
1231         public Component getTableCellRendererComponent(
1232                                 JTable table, Object value, boolean isSelected,
1233                                 boolean hasFocus, int row, int column) {
1234 
1235             Component c = wrappedRenderer.getTableCellRendererComponent(
1236                                 table, value, isSelected, hasFocus, row, column);
1237 
1238             int modelColumn = table.convertColumnIndexToModel(column);
1239             ShellFolderColumnInfo columnInfo = detailsTableModel.getColumns()[modelColumn];
1240 
1241             Integer alignment = columnInfo.getAlignment();
1242             if (alignment == null) {
1243                 alignment = SwingConstants.CENTER;
1244             }
1245             if (c instanceof JLabel) {
1246                 ((JLabel) c).setHorizontalAlignment(alignment);
1247             }
1248 
1249             return c;
1250         }
1251     }
1252 
1253     private void fixNameColumnWidth(int viewWidth) {
1254         TableColumn nameCol = detailsTable.getColumnModel().getColumn(COLUMN_FILENAME);
1255         int tableWidth = detailsTable.getPreferredSize().width;
1256 
1257         if (tableWidth < viewWidth) {
1258             nameCol.setPreferredWidth(nameCol.getPreferredWidth() + viewWidth - tableWidth);
1259         }
1260     }
1261 
1262     private class DelayedSelectionUpdater implements Runnable {
1263         File editFile;
1264 
1265         DelayedSelectionUpdater() {
1266             this(null);
1267         }
1268 
1269         DelayedSelectionUpdater(File editFile) {
1270             this.editFile = editFile;
1271             if (isShowing()) {
1272                 SwingUtilities.invokeLater(this);
1273             }
1274         }
1275 
1276         public void run() {
1277             setFileSelected();
1278             if (editFile != null) {
1279                 editFileName(getRowSorter().convertRowIndexToView(
1280                         getModel().indexOf(editFile)));
1281                 editFile = null;
1282             }
1283         }
1284     }
1285 
1286 
1287     /**
1288      * Creates a selection listener for the list of files and directories.
1289      *
1290      * @return a <code>ListSelectionListener</code>
1291      */
1292     public ListSelectionListener createListSelectionListener() {
1293         return fileChooserUIAccessor.createListSelectionListener();
1294     }
1295 
1296     int lastIndex = -1;
1297     File editFile = null;
1298     int editX = 20;
1299 
1300     private int getEditIndex() {
1301         return lastIndex;
1302     }
1303 
1304     private void setEditIndex(int i) {
1305         lastIndex = i;
1306     }
1307 
1308     private void resetEditIndex() {
1309         lastIndex = -1;
1310     }
1311 
1312     private void cancelEdit() {
1313         if (editFile != null) {
1314             editFile = null;
1315             list.remove(editCell);
1316             repaint();
1317         } else if (detailsTable != null && detailsTable.isEditing()) {
1318             detailsTable.getCellEditor().cancelCellEditing();
1319         }
1320     }
1321 
1322     JTextField editCell = null;
1323 
1324     /**
1325      * @param index visual index of the file to be edited
1326      */
1327     private void editFileName(int index) {
1328         File currentDirectory = getFileChooser().getCurrentDirectory();
1329         if (readOnly || !canWrite(currentDirectory)) {
1330             return;
1331         }
1332 
1333         ensureIndexIsVisible(index);
1334         switch (viewType) {
1335           case VIEWTYPE_LIST:
1336             editFile = (File)getModel().getElementAt(getRowSorter().convertRowIndexToModel(index));
1337             Rectangle r = list.getCellBounds(index, index);
1338             if (editCell == null) {
1339                 editCell = new JTextField();
1340                 editCell.setName("Tree.cellEditor");
1341                 editCell.addActionListener(new EditActionListener());
1342                 editCell.addFocusListener(editorFocusListener);
1343                 editCell.setNextFocusableComponent(list);
1344             }
1345             list.add(editCell);
1346             editCell.setText(getFileChooser().getName(editFile));
1347             ComponentOrientation orientation = list.getComponentOrientation();
1348             editCell.setComponentOrientation(orientation);
1349             if (orientation.isLeftToRight()) {
1350                 editCell.setBounds(editX + r.x, r.y, r.width - editX, r.height);
1351             } else {
1352                 editCell.setBounds(r.x, r.y, r.width - editX, r.height);
1353             }
1354             editCell.requestFocus();
1355             editCell.selectAll();
1356             break;
1357 
1358           case VIEWTYPE_DETAILS:
1359             detailsTable.editCellAt(index, COLUMN_FILENAME);
1360             break;
1361         }
1362     }
1363 
1364 
1365     class EditActionListener implements ActionListener {
1366         public void actionPerformed(ActionEvent e) {
1367             applyEdit();
1368         }
1369     }
1370 
1371     private void applyEdit() {
1372         if (editFile != null && editFile.exists()) {
1373             JFileChooser chooser = getFileChooser();
1374             String oldDisplayName = chooser.getName(editFile);
1375             String oldFileName = editFile.getName();
1376             String newDisplayName = editCell.getText().trim();
1377             String newFileName;
1378 
1379             if (!newDisplayName.equals(oldDisplayName)) {
1380                 newFileName = newDisplayName;
1381                 //Check if extension is hidden from user
1382                 int i1 = oldFileName.length();
1383                 int i2 = oldDisplayName.length();
1384                 if (i1 > i2 && oldFileName.charAt(i2) == '.') {
1385                     newFileName = newDisplayName + oldFileName.substring(i2);
1386                 }
1387 
1388                 // rename
1389                 FileSystemView fsv = chooser.getFileSystemView();
1390                 File f2 = fsv.createFileObject(editFile.getParentFile(), newFileName);
1391                 if (!f2.exists()) {
1392                     if (getModel().renameFile(editFile, f2)) {
1393                         if (fsv.isParent(chooser.getCurrentDirectory(), f2)) {
1394                             if (chooser.isMultiSelectionEnabled()) {
1395                                 chooser.setSelectedFiles(new File[]{f2});
1396                             } else {
1397                                 chooser.setSelectedFile(f2);
1398                             }
1399                         } else {
1400                             //Could be because of delay in updating Desktop folder
1401                             //chooser.setSelectedFile(null);
1402                         }
1403                     } else {
1404                         JOptionPane.showMessageDialog(chooser, MessageFormat.format(renameErrorText, oldFileName),
1405                                 renameErrorTitleText, JOptionPane.ERROR_MESSAGE);
1406                     }
1407                 }
1408             }
1409         }
1410         if (detailsTable != null && detailsTable.isEditing()) {
1411             detailsTable.getCellEditor().stopCellEditing();
1412         }
1413         cancelEdit();
1414     }
1415 
1416     protected Action newFolderAction;
1417 
1418     public Action getNewFolderAction() {
1419         if (!readOnly && newFolderAction == null) {
1420             newFolderAction = new AbstractAction(newFolderActionLabelText) {
1421                 private Action basicNewFolderAction;
1422 
1423                 // Initializer
1424                 {
1425                     putValue(Action.ACTION_COMMAND_KEY, FilePane.ACTION_NEW_FOLDER);
1426 
1427                     File currentDirectory = getFileChooser().getCurrentDirectory();
1428                     if (currentDirectory != null) {
1429                         setEnabled(canWrite(currentDirectory));
1430                     }
1431                 }
1432 
1433                 public void actionPerformed(ActionEvent ev) {
1434                     if (basicNewFolderAction == null) {
1435                         basicNewFolderAction = fileChooserUIAccessor.getNewFolderAction();
1436                     }
1437                     JFileChooser fc = getFileChooser();
1438                     File oldFile = fc.getSelectedFile();
1439                     basicNewFolderAction.actionPerformed(ev);
1440                     File newFile = fc.getSelectedFile();
1441                     if (newFile != null && !newFile.equals(oldFile) && newFile.isDirectory()) {
1442                         newFolderFile = newFile;
1443                     }
1444                 }
1445             };
1446         }
1447         return newFolderAction;
1448     }
1449 
1450     protected class FileRenderer extends DefaultListCellRenderer  {
1451 
1452         public Component getListCellRendererComponent(JList list, Object value,
1453                                                       int index, boolean isSelected,
1454                                                       boolean cellHasFocus) {
1455 
1456             if (listViewWindowsStyle && !list.isFocusOwner()) {
1457                 isSelected = false;
1458             }
1459 
1460             super.getListCellRendererComponent(list, value, index, isSelected, cellHasFocus);
1461             File file = (File) value;
1462             String fileName = getFileChooser().getName(file);
1463             setText(fileName);
1464             setFont(list.getFont());
1465 
1466             Icon icon = getFileChooser().getIcon(file);
1467             if (icon != null) {
1468                 setIcon(icon);
1469 
1470                 if (isSelected) {
1471                     // PENDING - grab padding (4) below from defaults table.
1472                     editX = icon.getIconWidth() + 4;
1473                 }
1474             } else {
1475                 if (getFileChooser().getFileSystemView().isTraversable(file)) {
1476                     setText(fileName+File.separator);
1477                 }
1478             }
1479 
1480             return this;
1481         }
1482     }
1483 
1484 
1485     void setFileSelected() {
1486         if (getFileChooser().isMultiSelectionEnabled() && !isDirectorySelected()) {
1487             File[] files = getFileChooser().getSelectedFiles(); // Should be selected
1488             Object[] selectedObjects = list.getSelectedValues(); // Are actually selected
1489 
1490             listSelectionModel.setValueIsAdjusting(true);
1491             try {
1492                 int lead = listSelectionModel.getLeadSelectionIndex();
1493                 int anchor = listSelectionModel.getAnchorSelectionIndex();
1494 
1495                 Arrays.sort(files);
1496                 Arrays.sort(selectedObjects);
1497 
1498                 int shouldIndex = 0;
1499                 int actuallyIndex = 0;
1500 
1501                 // Remove files that shouldn't be selected and add files which should be selected
1502                 // Note: Assume files are already sorted in compareTo order.
1503                 while (shouldIndex < files.length &&
1504                        actuallyIndex < selectedObjects.length) {
1505                     int comparison = files[shouldIndex].compareTo((File)selectedObjects[actuallyIndex]);
1506                     if (comparison < 0) {
1507                         doSelectFile(files[shouldIndex++]);
1508                     } else if (comparison > 0) {
1509                         doDeselectFile(selectedObjects[actuallyIndex++]);
1510                     } else {
1511                         // Do nothing
1512                         shouldIndex++;
1513                         actuallyIndex++;
1514                     }
1515 
1516                 }
1517 
1518                 while (shouldIndex < files.length) {
1519                     doSelectFile(files[shouldIndex++]);
1520                 }
1521 
1522                 while (actuallyIndex < selectedObjects.length) {
1523                     doDeselectFile(selectedObjects[actuallyIndex++]);
1524                 }
1525 
1526                 // restore the anchor and lead
1527                 if (listSelectionModel instanceof DefaultListSelectionModel) {
1528                     ((DefaultListSelectionModel)listSelectionModel).
1529                         moveLeadSelectionIndex(lead);
1530                     listSelectionModel.setAnchorSelectionIndex(anchor);
1531                 }
1532             } finally {
1533                 listSelectionModel.setValueIsAdjusting(false);
1534             }
1535         } else {
1536             JFileChooser chooser = getFileChooser();
1537             File f;
1538             if (isDirectorySelected()) {
1539                 f = getDirectory();
1540             } else {
1541                 f = chooser.getSelectedFile();
1542             }
1543             int i;
1544             if (f != null && (i = getModel().indexOf(f)) >= 0) {
1545                 int viewIndex = getRowSorter().convertRowIndexToView(i);
1546                 listSelectionModel.setSelectionInterval(viewIndex, viewIndex);
1547                 ensureIndexIsVisible(viewIndex);
1548             } else {
1549                 clearSelection();
1550             }
1551         }
1552     }
1553 
1554     private void doSelectFile(File fileToSelect) {
1555         int index = getModel().indexOf(fileToSelect);
1556         // could be missed in the current directory if it changed
1557         if (index >= 0) {
1558             index = getRowSorter().convertRowIndexToView(index);
1559             listSelectionModel.addSelectionInterval(index, index);
1560         }
1561     }
1562 
1563     private void doDeselectFile(Object fileToDeselect) {
1564         int index = getRowSorter().convertRowIndexToView(
1565                                 getModel().indexOf(fileToDeselect));
1566         listSelectionModel.removeSelectionInterval(index, index);
1567     }
1568 
1569     /* The following methods are used by the PropertyChange Listener */
1570 
1571     private void doSelectedFileChanged(PropertyChangeEvent e) {
1572         applyEdit();
1573         File f = (File) e.getNewValue();
1574         JFileChooser fc = getFileChooser();
1575         if (f != null
1576             && ((fc.isFileSelectionEnabled() && !f.isDirectory())
1577                 || (f.isDirectory() && fc.isDirectorySelectionEnabled()))) {
1578 
1579             setFileSelected();
1580         }
1581     }
1582 
1583     private void doSelectedFilesChanged(PropertyChangeEvent e) {
1584         applyEdit();
1585         File[] files = (File[]) e.getNewValue();
1586         JFileChooser fc = getFileChooser();
1587         if (files != null
1588             && files.length > 0
1589             && (files.length > 1 || fc.isDirectorySelectionEnabled() || !files[0].isDirectory())) {
1590             setFileSelected();
1591         }
1592     }
1593 
1594     private void doDirectoryChanged(PropertyChangeEvent e) {
1595         getDetailsTableModel().updateColumnInfo();
1596 
1597         JFileChooser fc = getFileChooser();
1598         FileSystemView fsv = fc.getFileSystemView();
1599 
1600         applyEdit();
1601         resetEditIndex();
1602         ensureIndexIsVisible(0);
1603         File currentDirectory = fc.getCurrentDirectory();
1604         if (currentDirectory != null) {
1605             if (!readOnly) {
1606                 getNewFolderAction().setEnabled(canWrite(currentDirectory));
1607             }
1608             fileChooserUIAccessor.getChangeToParentDirectoryAction().setEnabled(!fsv.isRoot(currentDirectory));
1609         }
1610     }
1611 
1612     private void doFilterChanged(PropertyChangeEvent e) {
1613         applyEdit();
1614         resetEditIndex();
1615         clearSelection();
1616     }
1617 
1618     private void doFileSelectionModeChanged(PropertyChangeEvent e) {
1619         applyEdit();
1620         resetEditIndex();
1621         clearSelection();
1622     }
1623 
1624     private void doMultiSelectionChanged(PropertyChangeEvent e) {
1625         if (getFileChooser().isMultiSelectionEnabled()) {
1626             listSelectionModel.setSelectionMode(ListSelectionModel.MULTIPLE_INTERVAL_SELECTION);
1627         } else {
1628             listSelectionModel.setSelectionMode(ListSelectionModel.SINGLE_SELECTION);
1629             clearSelection();
1630             getFileChooser().setSelectedFiles(null);
1631         }
1632     }
1633 
1634     /*
1635      * Listen for filechooser property changes, such as
1636      * the selected file changing, or the type of the dialog changing.
1637      */
1638     public void propertyChange(PropertyChangeEvent e) {
1639             if (viewType == -1) {
1640                 setViewType(VIEWTYPE_LIST);
1641             }
1642 
1643         String s = e.getPropertyName();
1644         if (s.equals(JFileChooser.SELECTED_FILE_CHANGED_PROPERTY)) {
1645             doSelectedFileChanged(e);
1646         } else if (s.equals(JFileChooser.SELECTED_FILES_CHANGED_PROPERTY)) {
1647             doSelectedFilesChanged(e);
1648         } else if (s.equals(JFileChooser.DIRECTORY_CHANGED_PROPERTY)) {
1649             doDirectoryChanged(e);
1650         } else if (s.equals(JFileChooser.FILE_FILTER_CHANGED_PROPERTY)) {
1651             doFilterChanged(e);
1652         } else if (s.equals(JFileChooser.FILE_SELECTION_MODE_CHANGED_PROPERTY)) {
1653             doFileSelectionModeChanged(e);
1654         } else if (s.equals(JFileChooser.MULTI_SELECTION_ENABLED_CHANGED_PROPERTY)) {
1655             doMultiSelectionChanged(e);
1656         } else if (s.equals(JFileChooser.CANCEL_SELECTION)) {
1657             applyEdit();
1658         } else if (s.equals("busy")) {
1659             setCursor((Boolean)e.getNewValue() ? waitCursor : null);
1660         } else if (s.equals("componentOrientation")) {
1661             ComponentOrientation o = (ComponentOrientation)e.getNewValue();
1662             JFileChooser cc = (JFileChooser)e.getSource();
1663             if (o != e.getOldValue()) {
1664                 cc.applyComponentOrientation(o);
1665             }
1666             if (detailsTable != null) {
1667                 detailsTable.setComponentOrientation(o);
1668                 detailsTable.getParent().getParent().setComponentOrientation(o);
1669             }
1670         }
1671     }
1672 
1673     private void ensureIndexIsVisible(int i) {
1674         if (i >= 0) {
1675             if (list != null) {
1676                 list.ensureIndexIsVisible(i);
1677             }
1678             if (detailsTable != null) {
1679                 detailsTable.scrollRectToVisible(detailsTable.getCellRect(i, COLUMN_FILENAME, true));
1680             }
1681         }
1682     }
1683 
1684     public void ensureFileIsVisible(JFileChooser fc, File f) {
1685         int modelIndex = getModel().indexOf(f);
1686         if (modelIndex >= 0) {
1687             ensureIndexIsVisible(getRowSorter().convertRowIndexToView(modelIndex));
1688         }
1689     }
1690 
1691     public void rescanCurrentDirectory() {
1692         getModel().validateFileCache();
1693     }
1694 
1695     public void clearSelection() {
1696         if (listSelectionModel != null) {
1697             listSelectionModel.clearSelection();
1698             if (listSelectionModel instanceof DefaultListSelectionModel) {
1699                 ((DefaultListSelectionModel)listSelectionModel).moveLeadSelectionIndex(0);
1700                 listSelectionModel.setAnchorSelectionIndex(0);
1701             }
1702         }
1703     }
1704 
1705     public JMenu getViewMenu() {
1706         if (viewMenu == null) {
1707             viewMenu = new JMenu(viewMenuLabelText);
1708             ButtonGroup viewButtonGroup = new ButtonGroup();
1709 
1710             for (int i = 0; i < VIEWTYPE_COUNT; i++) {
1711                 JRadioButtonMenuItem mi =
1712                     new JRadioButtonMenuItem(new ViewTypeAction(i));
1713                 viewButtonGroup.add(mi);
1714                 viewMenu.add(mi);
1715             }
1716             updateViewMenu();
1717         }
1718         return viewMenu;
1719     }
1720 
1721     private void updateViewMenu() {
1722         if (viewMenu != null) {
1723             Component[] comps = viewMenu.getMenuComponents();
1724             for (Component comp : comps) {
1725                 if (comp instanceof JRadioButtonMenuItem) {
1726                     JRadioButtonMenuItem mi = (JRadioButtonMenuItem) comp;
1727                     if (((ViewTypeAction)mi.getAction()).viewType == viewType) {
1728                         mi.setSelected(true);
1729                     }
1730                 }
1731             }
1732         }
1733     }
1734 
1735     public JPopupMenu getComponentPopupMenu() {
1736         JPopupMenu popupMenu = getFileChooser().getComponentPopupMenu();
1737         if (popupMenu != null) {
1738             return popupMenu;
1739         }
1740 
1741         JMenu viewMenu = getViewMenu();
1742         if (contextMenu == null) {
1743             contextMenu = new JPopupMenu();
1744             if (viewMenu != null) {
1745                 contextMenu.add(viewMenu);
1746                 if (listViewWindowsStyle) {
1747                     contextMenu.addSeparator();
1748                 }
1749             }
1750             ActionMap actionMap = getActionMap();
1751             Action refreshAction   = actionMap.get(ACTION_REFRESH);
1752             Action newFolderAction = actionMap.get(ACTION_NEW_FOLDER);
1753             if (refreshAction != null) {
1754                 contextMenu.add(refreshAction);
1755                 if (listViewWindowsStyle && newFolderAction != null) {
1756                     contextMenu.addSeparator();
1757                 }
1758             }
1759             if (newFolderAction != null) {
1760                 contextMenu.add(newFolderAction);
1761             }
1762         }
1763         if (viewMenu != null) {
1764             viewMenu.getPopupMenu().setInvoker(viewMenu);
1765         }
1766         return contextMenu;
1767     }
1768 
1769 
1770     private Handler handler;
1771 
1772     protected Handler getMouseHandler() {
1773         if (handler == null) {
1774             handler = new Handler();
1775         }
1776         return handler;
1777     }
1778 
1779     private class Handler implements MouseListener {
1780         private MouseListener doubleClickListener;
1781 
1782         public void mouseClicked(MouseEvent evt) {
1783             JComponent source = (JComponent)evt.getSource();
1784 
1785             int index;
1786             if (source instanceof JList) {
1787                 index = SwingUtilities2.loc2IndexFileList(list, evt.getPoint());
1788             } else if (source instanceof JTable) {
1789                 JTable table = (JTable)source;
1790                 Point p = evt.getPoint();
1791                 index = table.rowAtPoint(p);
1792 
1793                 boolean pointOutsidePrefSize =
1794                         SwingUtilities2.pointOutsidePrefSize(
1795                             table, index, table.columnAtPoint(p), p);
1796 
1797                 if (pointOutsidePrefSize && !fullRowSelection) {
1798                     return;
1799                 }
1800 
1801                 // Translate point from table to list
1802                 if (index >= 0 && list != null &&
1803                     listSelectionModel.isSelectedIndex(index)) {
1804 
1805                     // Make a new event with the list as source, placing the
1806                     // click in the corresponding list cell.
1807                     Rectangle r = list.getCellBounds(index, index);
1808                     evt = new MouseEvent(list, evt.getID(),
1809                                          evt.getWhen(), evt.getModifiers(),
1810                                          r.x + 1, r.y + r.height/2,
1811                                          evt.getXOnScreen(),
1812                                          evt.getYOnScreen(),
1813                                          evt.getClickCount(), evt.isPopupTrigger(),
1814                                          evt.getButton());
1815                 }
1816             } else {
1817                 return;
1818             }
1819 
1820             if (index >= 0 && SwingUtilities.isLeftMouseButton(evt)) {
1821                 JFileChooser fc = getFileChooser();
1822 
1823                 // For single click, we handle editing file name
1824                 if (evt.getClickCount() == 1 && source instanceof JList) {
1825                     if ((!fc.isMultiSelectionEnabled() || fc.getSelectedFiles().length <= 1)
1826                         && index >= 0 && listSelectionModel.isSelectedIndex(index)
1827                         && getEditIndex() == index && editFile == null) {
1828 
1829                         editFileName(index);
1830                     } else {
1831                         if (index >= 0) {
1832                             setEditIndex(index);
1833                         } else {
1834                             resetEditIndex();
1835                         }
1836                     }
1837                 } else if (evt.getClickCount() == 2) {
1838                     // on double click (open or drill down one directory) be
1839                     // sure to clear the edit index
1840                     resetEditIndex();
1841                 }
1842             }
1843 
1844             // Forward event to Basic
1845             if (getDoubleClickListener() != null) {
1846                 getDoubleClickListener().mouseClicked(evt);
1847             }
1848         }
1849 
1850         public void mouseEntered(MouseEvent evt) {
1851             JComponent source = (JComponent)evt.getSource();
1852             if (source instanceof JTable) {
1853                 JTable table = (JTable)evt.getSource();
1854 
1855                 TransferHandler th1 = getFileChooser().getTransferHandler();
1856                 TransferHandler th2 = table.getTransferHandler();
1857                 if (th1 != th2) {
1858                     table.setTransferHandler(th1);
1859                 }
1860 
1861                 boolean dragEnabled = getFileChooser().getDragEnabled();
1862                 if (dragEnabled != table.getDragEnabled()) {
1863                     table.setDragEnabled(dragEnabled);
1864                 }
1865             } else if (source instanceof JList) {
1866                 // Forward event to Basic
1867                 if (getDoubleClickListener() != null) {
1868                     getDoubleClickListener().mouseEntered(evt);
1869                 }
1870             }
1871         }
1872 
1873         public void mouseExited(MouseEvent evt) {
1874             if (evt.getSource() instanceof JList) {
1875                 // Forward event to Basic
1876                 if (getDoubleClickListener() != null) {
1877                     getDoubleClickListener().mouseExited(evt);
1878                 }
1879             }
1880         }
1881 
1882         public void mousePressed(MouseEvent evt) {
1883             if (evt.getSource() instanceof JList) {
1884                 // Forward event to Basic
1885                 if (getDoubleClickListener() != null) {
1886                     getDoubleClickListener().mousePressed(evt);
1887                 }
1888             }
1889         }
1890 
1891         public void mouseReleased(MouseEvent evt) {
1892             if (evt.getSource() instanceof JList) {
1893                 // Forward event to Basic
1894                 if (getDoubleClickListener() != null) {
1895                     getDoubleClickListener().mouseReleased(evt);
1896                 }
1897             }
1898         }
1899 
1900         private MouseListener getDoubleClickListener() {
1901             // Lazy creation of Basic's listener
1902             if (doubleClickListener == null && list != null) {
1903                 doubleClickListener =
1904                     fileChooserUIAccessor.createDoubleClickListener(list);
1905             }
1906             return doubleClickListener;
1907         }
1908     }
1909 
1910     /**
1911      * Property to remember whether a directory is currently selected in the UI.
1912      *
1913      * @return <code>true</code> iff a directory is currently selected.
1914      */
1915     protected boolean isDirectorySelected() {
1916         return fileChooserUIAccessor.isDirectorySelected();
1917     }
1918 
1919 
1920     /**
1921      * Property to remember the directory that is currently selected in the UI.
1922      *
1923      * @return the value of the <code>directory</code> property
1924      * @see javax.swing.plaf.basic.BasicFileChooserUI#setDirectory
1925      */
1926     protected File getDirectory() {
1927         return fileChooserUIAccessor.getDirectory();
1928     }
1929 
1930     private Component findChildComponent(Container container, Class cls) {
1931         int n = container.getComponentCount();
1932         for (int i = 0; i < n; i++) {
1933             Component comp = container.getComponent(i);
1934             if (cls.isInstance(comp)) {
1935                 return comp;
1936             } else if (comp instanceof Container) {
1937                 Component c = findChildComponent((Container)comp, cls);
1938                 if (c != null) {
1939                     return c;
1940                 }
1941             }
1942         }
1943         return null;
1944     }
1945 
1946     public boolean canWrite(File f) {
1947         // Return false for non FileSystem files or if file doesn't exist.
1948         if (!f.exists()) {
1949             return false;
1950         }
1951 
1952         try {
1953         if (f instanceof ShellFolder) {
1954             return ((ShellFolder) f).isFileSystem();
1955         } else {
1956             if (fileChooserUIAccessor.usesShellFolder()) {
1957                 try {
1958                     return ShellFolder.getShellFolder(f).isFileSystem();
1959                 } catch (FileNotFoundException ex) {
1960                     // File doesn't exist
1961                     return false;
1962                 }
1963             } else {
1964                 // Ordinary file
1965                 return true;
1966             }
1967         }
1968         } catch (SecurityException e) {
1969             return false;
1970         }
1971     }
1972 
1973     // This interface is used to access methods in the FileChooserUI
1974     // that are not public.
1975     public interface FileChooserUIAccessor {
1976         public JFileChooser getFileChooser();
1977         public BasicDirectoryModel getModel();
1978         public JPanel createList();
1979         public JPanel createDetailsView();
1980         public boolean isDirectorySelected();
1981         public File getDirectory();
1982         public Action getApproveSelectionAction();
1983         public Action getChangeToParentDirectoryAction();
1984         public Action getNewFolderAction();
1985         public MouseListener createDoubleClickListener(JList list);
1986         public ListSelectionListener createListSelectionListener();
1987         public boolean usesShellFolder();
1988     }
1989 }