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