1 /* 2 * Copyright (c) 1998, 2015, 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 26 package javax.swing.plaf.basic; 27 28 import javax.swing.*; 29 import javax.swing.filechooser.*; 30 import javax.swing.filechooser.FileFilter; 31 import javax.swing.event.*; 32 import javax.swing.plaf.*; 33 import java.awt.*; 34 import java.awt.event.*; 35 import java.awt.datatransfer.*; 36 import java.awt.image.BufferedImage; 37 import static java.awt.image.BufferedImage.TYPE_INT_ARGB; 38 import java.beans.*; 39 import java.io.*; 40 import java.nio.file.Files; 41 import java.nio.file.Path; 42 import java.util.*; 43 import java.util.List; 44 import java.util.regex.*; 45 import static java.util.stream.Collectors.toList; 46 import javax.swing.tree.TreePath; 47 import sun.awt.shell.ShellFolder; 48 import sun.swing.*; 49 import sun.swing.SwingUtilities2; 50 51 /** 52 * Basic L&F implementation of a FileChooser. 53 * 54 * @author Jeff Dinkins 55 */ 56 public class BasicFileChooserUI extends FileChooserUI { 57 58 /* FileView icons */ 59 /** Directory icon */ 60 protected Icon directoryIcon = null; 61 /** File icon */ 62 protected Icon fileIcon = null; 63 /** Computer icon */ 64 protected Icon computerIcon = null; 65 /** Hard drive icon */ 66 protected Icon hardDriveIcon = null; 67 /** Floppy drive icon */ 68 protected Icon floppyDriveIcon = null; 69 70 /** New folder icon */ 71 protected Icon newFolderIcon = null; 72 /** Up folder icon */ 73 protected Icon upFolderIcon = null; 74 /** Home folder icon */ 75 protected Icon homeFolderIcon = null; 76 /** List view icon */ 77 protected Icon listViewIcon = null; 78 /** Details view icon */ 79 protected Icon detailsViewIcon = null; 80 /** View menu icon */ 81 protected Icon viewMenuIcon = null; 82 83 /** Save button mnemonic */ 84 protected int saveButtonMnemonic = 0; 85 /** Open button mnemonic */ 86 protected int openButtonMnemonic = 0; 87 /** Cancel button mnemonic */ 88 protected int cancelButtonMnemonic = 0; 89 /** Update button mnemonic */ 90 protected int updateButtonMnemonic = 0; 91 /** Help button mnemonic */ 92 protected int helpButtonMnemonic = 0; 93 94 /** 95 * The mnemonic keycode used for the approve button when a directory 96 * is selected and the current selection mode is FILES_ONLY. 97 * 98 * @since 1.4 99 */ 100 protected int directoryOpenButtonMnemonic = 0; 101 102 /** Save button text */ 103 protected String saveButtonText = null; 104 /** Open button text */ 105 protected String openButtonText = null; 106 /** Cancel button text */ 107 protected String cancelButtonText = null; 108 /** Update button text */ 109 protected String updateButtonText = null; 110 /** Help button text */ 111 protected String helpButtonText = null; 112 113 /** 114 * The label text displayed on the approve button when a directory 115 * is selected and the current selection mode is FILES_ONLY. 116 * 117 * @since 1.4 118 */ 119 protected String directoryOpenButtonText = null; 120 121 /** Open dialog title text */ 122 private String openDialogTitleText = null; 123 /** Save dialog title text */ 124 private String saveDialogTitleText = null; 125 126 /** Save button tool tip text */ 127 protected String saveButtonToolTipText = null; 128 /** Open button tool tip text */ 129 protected String openButtonToolTipText = null; 130 /** Cancel button tool tip text */ 131 protected String cancelButtonToolTipText = null; 132 /** Update button tool tip text */ 133 protected String updateButtonToolTipText = null; 134 /** Help button tool tip text */ 135 protected String helpButtonToolTipText = null; 136 137 /** 138 * The tooltip text displayed on the approve button when a directory 139 * is selected and the current selection mode is FILES_ONLY. 140 * 141 * @since 1.4 142 */ 143 protected String directoryOpenButtonToolTipText = null; 144 145 // Some generic FileChooser functions 146 private Action approveSelectionAction = new ApproveSelectionAction(); 147 private Action cancelSelectionAction = new CancelSelectionAction(); 148 private Action updateAction = new UpdateAction(); 149 private Action newFolderAction; 150 private Action goHomeAction = new GoHomeAction(); 151 private Action changeToParentDirectoryAction = new ChangeToParentDirectoryAction(); 152 153 private String newFolderErrorSeparator = null; 154 private String newFolderErrorText = null; 155 private String newFolderParentDoesntExistTitleText = null; 156 private String newFolderParentDoesntExistText = null; 157 private String fileDescriptionText = null; 158 private String directoryDescriptionText = null; 159 160 private JFileChooser filechooser = null; 161 162 private boolean directorySelected = false; 163 private File directory = null; 164 165 private PropertyChangeListener propertyChangeListener = null; 166 private AcceptAllFileFilter acceptAllFileFilter = new AcceptAllFileFilter(); 167 private FileFilter actualFileFilter = null; 168 private GlobFilter globFilter = null; 169 private BasicDirectoryModel model = null; 170 private BasicFileView fileView = new BasicFileView(); 171 private boolean usesSingleFilePane; 172 private boolean readOnly; 173 174 // The accessoryPanel is a container to place the JFileChooser accessory component 175 private JPanel accessoryPanel = null; 176 private Handler handler; 177 178 /** 179 * Creates a {@code BasicFileChooserUI} implementation 180 * for the specified component. By default 181 * the {@code BasicLookAndFeel} class uses 182 * {@code createUI} methods of all basic UIs classes 183 * to instantiate UIs. 184 * 185 * @param c the {@code JFileChooser} which needs a UI 186 * @return the {@code BasicFileChooserUI} object 187 * 188 * @see UIDefaults#getUI(JComponent) 189 * @since 1.7 190 */ 191 public static ComponentUI createUI(JComponent c) { 192 return new BasicFileChooserUI((JFileChooser) c); 193 } 194 195 /** 196 * Constructs a {@code BasicFileChooserUI}. 197 * @param b file chooser 198 */ 199 public BasicFileChooserUI(JFileChooser b) { 200 } 201 202 /** 203 * Installs the UI. 204 * @param c the component 205 */ 206 public void installUI(JComponent c) { 207 accessoryPanel = new JPanel(new BorderLayout()); 208 filechooser = (JFileChooser) c; 209 210 createModel(); 211 212 clearIconCache(); 213 214 installDefaults(filechooser); 215 installComponents(filechooser); 216 installListeners(filechooser); 217 filechooser.applyComponentOrientation(filechooser.getComponentOrientation()); 218 } 219 220 /** 221 * Uninstalls the UI. 222 * @param c the component 223 */ 224 public void uninstallUI(JComponent c) { 225 uninstallListeners(filechooser); 226 uninstallComponents(filechooser); 227 uninstallDefaults(filechooser); 228 229 if(accessoryPanel != null) { 230 accessoryPanel.removeAll(); 231 } 232 233 accessoryPanel = null; 234 getFileChooser().removeAll(); 235 236 handler = null; 237 } 238 239 /** 240 * Installs the components. 241 * @param fc the file chooser 242 */ 243 public void installComponents(JFileChooser fc) { 244 } 245 246 /** 247 * Uninstalls the components. 248 * @param fc the file chooser 249 */ 250 public void uninstallComponents(JFileChooser fc) { 251 } 252 253 /** 254 * Installs the listeners. 255 * @param fc the file chooser 256 */ 257 protected void installListeners(JFileChooser fc) { 258 propertyChangeListener = createPropertyChangeListener(fc); 259 if(propertyChangeListener != null) { 260 fc.addPropertyChangeListener(propertyChangeListener); 261 } 262 fc.addPropertyChangeListener(getModel()); 263 264 InputMap inputMap = getInputMap(JComponent. 265 WHEN_ANCESTOR_OF_FOCUSED_COMPONENT); 266 SwingUtilities.replaceUIInputMap(fc, JComponent. 267 WHEN_ANCESTOR_OF_FOCUSED_COMPONENT, inputMap); 268 ActionMap actionMap = getActionMap(); 269 SwingUtilities.replaceUIActionMap(fc, actionMap); 270 } 271 272 InputMap getInputMap(int condition) { 273 if (condition == JComponent.WHEN_ANCESTOR_OF_FOCUSED_COMPONENT) { 274 return (InputMap)DefaultLookup.get(getFileChooser(), this, 275 "FileChooser.ancestorInputMap"); 276 } 277 return null; 278 } 279 280 ActionMap getActionMap() { 281 return createActionMap(); 282 } 283 284 ActionMap createActionMap() { 285 ActionMap map = new ActionMapUIResource(); 286 287 Action refreshAction = new UIAction(FilePane.ACTION_REFRESH) { 288 public void actionPerformed(ActionEvent evt) { 289 getFileChooser().rescanCurrentDirectory(); 290 } 291 }; 292 293 map.put(FilePane.ACTION_APPROVE_SELECTION, getApproveSelectionAction()); 294 map.put(FilePane.ACTION_CANCEL, getCancelSelectionAction()); 295 map.put(FilePane.ACTION_REFRESH, refreshAction); 296 map.put(FilePane.ACTION_CHANGE_TO_PARENT_DIRECTORY, 297 getChangeToParentDirectoryAction()); 298 return map; 299 } 300 301 302 /** 303 * Uninstalls the listeners. 304 * @param fc the file chooser 305 */ 306 protected void uninstallListeners(JFileChooser fc) { 307 if(propertyChangeListener != null) { 308 fc.removePropertyChangeListener(propertyChangeListener); 309 } 310 fc.removePropertyChangeListener(getModel()); 311 SwingUtilities.replaceUIInputMap(fc, JComponent. 312 WHEN_ANCESTOR_OF_FOCUSED_COMPONENT, null); 313 SwingUtilities.replaceUIActionMap(fc, null); 314 } 315 316 317 /** 318 * Installs the defaults. 319 * @param fc the file chooser 320 */ 321 protected void installDefaults(JFileChooser fc) { 322 installIcons(fc); 323 installStrings(fc); 324 usesSingleFilePane = UIManager.getBoolean("FileChooser.usesSingleFilePane"); 325 readOnly = UIManager.getBoolean("FileChooser.readOnly"); 326 TransferHandler th = fc.getTransferHandler(); 327 if (th == null || th instanceof UIResource) { 328 fc.setTransferHandler(defaultTransferHandler); 329 } 330 LookAndFeel.installProperty(fc, "opaque", Boolean.FALSE); 331 } 332 333 /** 334 * Installs the icons. 335 * @param fc the file chooser 336 */ 337 protected void installIcons(JFileChooser fc) { 338 directoryIcon = UIManager.getIcon("FileView.directoryIcon"); 339 fileIcon = UIManager.getIcon("FileView.fileIcon"); 340 computerIcon = UIManager.getIcon("FileView.computerIcon"); 341 hardDriveIcon = UIManager.getIcon("FileView.hardDriveIcon"); 342 floppyDriveIcon = UIManager.getIcon("FileView.floppyDriveIcon"); 343 344 newFolderIcon = UIManager.getIcon("FileChooser.newFolderIcon"); 345 upFolderIcon = UIManager.getIcon("FileChooser.upFolderIcon"); 346 homeFolderIcon = UIManager.getIcon("FileChooser.homeFolderIcon"); 347 detailsViewIcon = UIManager.getIcon("FileChooser.detailsViewIcon"); 348 listViewIcon = UIManager.getIcon("FileChooser.listViewIcon"); 349 viewMenuIcon = UIManager.getIcon("FileChooser.viewMenuIcon"); 350 } 351 352 /** 353 * Installs the strings. 354 * @param fc the file chooser 355 */ 356 protected void installStrings(JFileChooser fc) { 357 358 Locale l = fc.getLocale(); 359 newFolderErrorText = UIManager.getString("FileChooser.newFolderErrorText",l); 360 newFolderErrorSeparator = UIManager.getString("FileChooser.newFolderErrorSeparator",l); 361 362 newFolderParentDoesntExistTitleText = UIManager.getString("FileChooser.newFolderParentDoesntExistTitleText", l); 363 newFolderParentDoesntExistText = UIManager.getString("FileChooser.newFolderParentDoesntExistText", l); 364 365 fileDescriptionText = UIManager.getString("FileChooser.fileDescriptionText",l); 366 directoryDescriptionText = UIManager.getString("FileChooser.directoryDescriptionText",l); 367 368 saveButtonText = UIManager.getString("FileChooser.saveButtonText",l); 369 openButtonText = UIManager.getString("FileChooser.openButtonText",l); 370 saveDialogTitleText = UIManager.getString("FileChooser.saveDialogTitleText",l); 371 openDialogTitleText = UIManager.getString("FileChooser.openDialogTitleText",l); 372 cancelButtonText = UIManager.getString("FileChooser.cancelButtonText",l); 373 updateButtonText = UIManager.getString("FileChooser.updateButtonText",l); 374 helpButtonText = UIManager.getString("FileChooser.helpButtonText",l); 375 directoryOpenButtonText = UIManager.getString("FileChooser.directoryOpenButtonText",l); 376 377 saveButtonMnemonic = getMnemonic("FileChooser.saveButtonMnemonic", l); 378 openButtonMnemonic = getMnemonic("FileChooser.openButtonMnemonic", l); 379 cancelButtonMnemonic = getMnemonic("FileChooser.cancelButtonMnemonic", l); 380 updateButtonMnemonic = getMnemonic("FileChooser.updateButtonMnemonic", l); 381 helpButtonMnemonic = getMnemonic("FileChooser.helpButtonMnemonic", l); 382 directoryOpenButtonMnemonic = getMnemonic("FileChooser.directoryOpenButtonMnemonic", l); 383 384 saveButtonToolTipText = UIManager.getString("FileChooser.saveButtonToolTipText",l); 385 openButtonToolTipText = UIManager.getString("FileChooser.openButtonToolTipText",l); 386 cancelButtonToolTipText = UIManager.getString("FileChooser.cancelButtonToolTipText",l); 387 updateButtonToolTipText = UIManager.getString("FileChooser.updateButtonToolTipText",l); 388 helpButtonToolTipText = UIManager.getString("FileChooser.helpButtonToolTipText",l); 389 directoryOpenButtonToolTipText = UIManager.getString("FileChooser.directoryOpenButtonToolTipText",l); 390 } 391 392 /** 393 * Uninstalls the defaults. 394 * @param fc the file chooser 395 */ 396 protected void uninstallDefaults(JFileChooser fc) { 397 uninstallIcons(fc); 398 uninstallStrings(fc); 399 if (fc.getTransferHandler() instanceof UIResource) { 400 fc.setTransferHandler(null); 401 } 402 } 403 404 /** 405 * Uninstalls the icons. 406 * @param fc the file chooser 407 */ 408 protected void uninstallIcons(JFileChooser fc) { 409 directoryIcon = null; 410 fileIcon = null; 411 computerIcon = null; 412 hardDriveIcon = null; 413 floppyDriveIcon = null; 414 415 newFolderIcon = null; 416 upFolderIcon = null; 417 homeFolderIcon = null; 418 detailsViewIcon = null; 419 listViewIcon = null; 420 viewMenuIcon = null; 421 } 422 423 /** 424 * Uninstalls the strings. 425 * @param fc the file chooser 426 */ 427 protected void uninstallStrings(JFileChooser fc) { 428 saveButtonText = null; 429 openButtonText = null; 430 cancelButtonText = null; 431 updateButtonText = null; 432 helpButtonText = null; 433 directoryOpenButtonText = null; 434 435 saveButtonToolTipText = null; 436 openButtonToolTipText = null; 437 cancelButtonToolTipText = null; 438 updateButtonToolTipText = null; 439 helpButtonToolTipText = null; 440 directoryOpenButtonToolTipText = null; 441 } 442 443 /** 444 * Creates the model. 445 */ 446 protected void createModel() { 447 if (model != null) { 448 model.invalidateFileCache(); 449 } 450 model = new BasicDirectoryModel(getFileChooser()); 451 } 452 453 /** 454 * Returns the model. 455 * @return the model 456 */ 457 public BasicDirectoryModel getModel() { 458 return model; 459 } 460 461 /** 462 * Creates the property change listener. 463 * @param fc the file chooser 464 * @return the property change listener 465 */ 466 public PropertyChangeListener createPropertyChangeListener(JFileChooser fc) { 467 return null; 468 } 469 470 /** 471 * Returns the file name. 472 * @return the file name 473 */ 474 public String getFileName() { 475 return null; 476 } 477 478 /** 479 * Returns the directory name. 480 * @return the directory name 481 */ 482 public String getDirectoryName() { 483 return null; 484 } 485 486 /** 487 * Sets the file name. 488 * @param filename the file name 489 */ 490 public void setFileName(String filename) { 491 } 492 493 /** 494 * Sets the directory name. 495 * @param dirname the file name 496 */ 497 public void setDirectoryName(String dirname) { 498 } 499 500 /** 501 * {@inheritDoc} 502 */ 503 public void rescanCurrentDirectory(JFileChooser fc) { 504 } 505 506 /** 507 * {@inheritDoc} 508 */ 509 public void ensureFileIsVisible(JFileChooser fc, File f) { 510 } 511 512 /** 513 * Returns the file chooser. 514 * @return the file chooser 515 */ 516 public JFileChooser getFileChooser() { 517 return filechooser; 518 } 519 520 /** 521 * Returns the accessory panel. 522 * @return the accessory panel 523 */ 524 public JPanel getAccessoryPanel() { 525 return accessoryPanel; 526 } 527 528 /** 529 * Returns the approve button. 530 * @param fc the file chooser 531 * @return the approve button 532 */ 533 protected JButton getApproveButton(JFileChooser fc) { 534 return null; 535 } 536 537 /** 538 * {@inheritDoc} 539 */ 540 public JButton getDefaultButton(JFileChooser fc) { 541 return getApproveButton(fc); 542 } 543 544 /** 545 * Returns the approve button tool tip. 546 * @param fc the file chooser 547 * @return the approve button tool tip 548 */ 549 public String getApproveButtonToolTipText(JFileChooser fc) { 550 String tooltipText = fc.getApproveButtonToolTipText(); 551 if(tooltipText != null) { 552 return tooltipText; 553 } 554 555 if(fc.getDialogType() == JFileChooser.OPEN_DIALOG) { 556 return openButtonToolTipText; 557 } else if(fc.getDialogType() == JFileChooser.SAVE_DIALOG) { 558 return saveButtonToolTipText; 559 } 560 return null; 561 } 562 563 /** 564 * Clears the icon cache. 565 */ 566 public void clearIconCache() { 567 fileView.clearIconCache(); 568 } 569 570 571 // ******************************************** 572 // ************ Create Listeners ************** 573 // ******************************************** 574 575 private Handler getHandler() { 576 if (handler == null) { 577 handler = new Handler(); 578 } 579 return handler; 580 } 581 582 /** 583 * Creates a double click listener. 584 * @param fc the file chooser 585 * @param list the list 586 * @return a double click listener 587 */ 588 protected MouseListener createDoubleClickListener(JFileChooser fc, 589 JList<?> list) { 590 return new Handler(list); 591 } 592 593 /** 594 * Creates a list selection listener. 595 * @param fc the file chooser 596 * @return a list selection listener 597 */ 598 public ListSelectionListener createListSelectionListener(JFileChooser fc) { 599 return getHandler(); 600 } 601 602 private class Handler implements MouseListener, ListSelectionListener { 603 JList<?> list; 604 605 Handler() { 606 } 607 608 Handler(JList<?> list) { 609 this.list = list; 610 } 611 612 public void mouseClicked(MouseEvent evt) { 613 // Note: we can't depend on evt.getSource() because of backward 614 // compatibility 615 if (list != null && 616 SwingUtilities.isLeftMouseButton(evt) && 617 (evt.getClickCount()%2 == 0)) { 618 619 int index = SwingUtilities2.loc2IndexFileList(list, evt.getPoint()); 620 if (index >= 0) { 621 File f = (File)list.getModel().getElementAt(index); 622 try { 623 // Strip trailing ".." 624 f = ShellFolder.getNormalizedFile(f); 625 } catch (IOException ex) { 626 // That's ok, we'll use f as is 627 } 628 if(getFileChooser().isTraversable(f)) { 629 list.clearSelection(); 630 changeDirectory(f); 631 } else { 632 getFileChooser().approveSelection(); 633 } 634 } 635 } 636 } 637 638 public void mouseEntered(MouseEvent evt) { 639 if (list != null) { 640 TransferHandler th1 = getFileChooser().getTransferHandler(); 641 TransferHandler th2 = list.getTransferHandler(); 642 if (th1 != th2) { 643 list.setTransferHandler(th1); 644 } 645 if (getFileChooser().getDragEnabled() != list.getDragEnabled()) { 646 list.setDragEnabled(getFileChooser().getDragEnabled()); 647 } 648 } 649 } 650 651 public void mouseExited(MouseEvent evt) { 652 } 653 654 public void mousePressed(MouseEvent evt) { 655 } 656 657 public void mouseReleased(MouseEvent evt) { 658 } 659 660 @SuppressWarnings("deprecation") 661 public void valueChanged(ListSelectionEvent evt) { 662 if(!evt.getValueIsAdjusting()) { 663 JFileChooser chooser = getFileChooser(); 664 FileSystemView fsv = chooser.getFileSystemView(); 665 @SuppressWarnings("unchecked") 666 JList<?> list = (JList)evt.getSource(); 667 668 int fsm = chooser.getFileSelectionMode(); 669 boolean useSetDirectory = usesSingleFilePane && 670 (fsm == JFileChooser.FILES_ONLY); 671 672 if (chooser.isMultiSelectionEnabled()) { 673 File[] files = null; 674 Object[] objects = list.getSelectedValues(); 675 if (objects != null) { 676 if (objects.length == 1 677 && ((File)objects[0]).isDirectory() 678 && chooser.isTraversable(((File)objects[0])) 679 && (useSetDirectory || !fsv.isFileSystem(((File)objects[0])))) { 680 setDirectorySelected(true); 681 setDirectory(((File)objects[0])); 682 } else { 683 ArrayList<File> fList = new ArrayList<File>(objects.length); 684 for (Object object : objects) { 685 File f = (File) object; 686 boolean isDir = f.isDirectory(); 687 if ((chooser.isFileSelectionEnabled() && !isDir) 688 || (chooser.isDirectorySelectionEnabled() 689 && fsv.isFileSystem(f) 690 && isDir)) { 691 fList.add(f); 692 } 693 } 694 if (fList.size() > 0) { 695 files = fList.toArray(new File[fList.size()]); 696 } 697 setDirectorySelected(false); 698 } 699 } 700 chooser.setSelectedFiles(files); 701 } else { 702 File file = (File)list.getSelectedValue(); 703 if (file != null 704 && file.isDirectory() 705 && chooser.isTraversable(file) 706 && (useSetDirectory || !fsv.isFileSystem(file))) { 707 708 setDirectorySelected(true); 709 setDirectory(file); 710 if (usesSingleFilePane) { 711 chooser.setSelectedFile(null); 712 } 713 } else { 714 setDirectorySelected(false); 715 if (file != null) { 716 chooser.setSelectedFile(file); 717 } 718 } 719 } 720 } 721 } 722 } 723 724 /** 725 * A double click listener. 726 */ 727 protected class DoubleClickListener extends MouseAdapter { 728 // NOTE: This class exists only for backward compatibility. All 729 // its functionality has been moved into Handler. If you need to add 730 // new functionality add it to the Handler, but make sure this 731 // class calls into the Handler. 732 Handler handler; 733 /** 734 * Constucts a {@code DoubleClickListener}. 735 * @param list the lsit 736 */ 737 public DoubleClickListener(JList<?> list) { 738 handler = new Handler(list); 739 } 740 741 /** 742 * The JList used for representing the files is created by subclasses, but the 743 * selection is monitored in this class. The TransferHandler installed in the 744 * JFileChooser is also installed in the file list as it is used as the actual 745 * transfer source. The list is updated on a mouse enter to reflect the current 746 * data transfer state of the file chooser. 747 */ 748 public void mouseEntered(MouseEvent e) { 749 handler.mouseEntered(e); 750 } 751 752 /** {@inheritDoc} */ 753 public void mouseClicked(MouseEvent e) { 754 handler.mouseClicked(e); 755 } 756 } 757 758 /** 759 * A selection listener. 760 */ 761 protected class SelectionListener implements ListSelectionListener { 762 // NOTE: This class exists only for backward compatibility. All 763 // its functionality has been moved into Handler. If you need to add 764 // new functionality add it to the Handler, but make sure this 765 // class calls into the Handler. 766 /** {@inheritDoc} */ 767 public void valueChanged(ListSelectionEvent e) { 768 getHandler().valueChanged(e); 769 } 770 } 771 772 /** 773 * Property to remember whether a directory is currently selected in the UI. 774 * 775 * @return <code>true</code> iff a directory is currently selected. 776 * @since 1.4 777 */ 778 protected boolean isDirectorySelected() { 779 return directorySelected; 780 } 781 782 /** 783 * Property to remember whether a directory is currently selected in the UI. 784 * This is normally called by the UI on a selection event. 785 * 786 * @param b iff a directory is currently selected. 787 * @since 1.4 788 */ 789 protected void setDirectorySelected(boolean b) { 790 directorySelected = b; 791 } 792 793 /** 794 * Property to remember the directory that is currently selected in the UI. 795 * 796 * @return the value of the <code>directory</code> property 797 * @see #setDirectory 798 * @since 1.4 799 */ 800 protected File getDirectory() { 801 return directory; 802 } 803 804 /** 805 * Property to remember the directory that is currently selected in the UI. 806 * This is normally called by the UI on a selection event. 807 * 808 * @param f the <code>File</code> object representing the directory that is 809 * currently selected 810 * @since 1.4 811 */ 812 protected void setDirectory(File f) { 813 directory = f; 814 } 815 816 /** 817 * Returns the mnemonic for the given key. 818 */ 819 private int getMnemonic(String key, Locale l) { 820 return SwingUtilities2.getUIDefaultsInt(key, l); 821 } 822 823 // ******************************************************* 824 // ************ FileChooser UI PLAF methods ************** 825 // ******************************************************* 826 827 /** 828 * Returns the default accept all file filter 829 */ 830 public FileFilter getAcceptAllFileFilter(JFileChooser fc) { 831 return acceptAllFileFilter; 832 } 833 834 835 public FileView getFileView(JFileChooser fc) { 836 return fileView; 837 } 838 839 840 /** 841 * Returns the title of this dialog 842 * @param fc the file chooser 843 * @return the title of this dialog 844 */ 845 public String getDialogTitle(JFileChooser fc) { 846 String dialogTitle = fc.getDialogTitle(); 847 if (dialogTitle != null) { 848 return dialogTitle; 849 } else if (fc.getDialogType() == JFileChooser.OPEN_DIALOG) { 850 return openDialogTitleText; 851 } else if (fc.getDialogType() == JFileChooser.SAVE_DIALOG) { 852 return saveDialogTitleText; 853 } else { 854 return getApproveButtonText(fc); 855 } 856 } 857 858 /** 859 * Returns the approve button mnemonic. 860 * @param fc the file chooser 861 * @return the approve button mnemonic 862 */ 863 public int getApproveButtonMnemonic(JFileChooser fc) { 864 int mnemonic = fc.getApproveButtonMnemonic(); 865 if (mnemonic > 0) { 866 return mnemonic; 867 } else if (fc.getDialogType() == JFileChooser.OPEN_DIALOG) { 868 return openButtonMnemonic; 869 } else if (fc.getDialogType() == JFileChooser.SAVE_DIALOG) { 870 return saveButtonMnemonic; 871 } else { 872 return mnemonic; 873 } 874 } 875 876 /** {@inheritDoc} */ 877 public String getApproveButtonText(JFileChooser fc) { 878 String buttonText = fc.getApproveButtonText(); 879 if (buttonText != null) { 880 return buttonText; 881 } else if (fc.getDialogType() == JFileChooser.OPEN_DIALOG) { 882 return openButtonText; 883 } else if (fc.getDialogType() == JFileChooser.SAVE_DIALOG) { 884 return saveButtonText; 885 } else { 886 return null; 887 } 888 } 889 890 891 // ***************************** 892 // ***** Directory Actions ***** 893 // ***************************** 894 895 /** 896 * Returns a new folder action. 897 * @return a new folder action 898 */ 899 public Action getNewFolderAction() { 900 if (newFolderAction == null) { 901 newFolderAction = new NewFolderAction(); 902 // Note: Don't return null for readOnly, it might 903 // break older apps. 904 if (readOnly) { 905 newFolderAction.setEnabled(false); 906 } 907 } 908 return newFolderAction; 909 } 910 911 /** 912 * Returns a go home action. 913 * @return a go home action 914 */ 915 public Action getGoHomeAction() { 916 return goHomeAction; 917 } 918 919 /** 920 * Returns a change to parent directory action. 921 * @return a change to parent directory action 922 */ 923 public Action getChangeToParentDirectoryAction() { 924 return changeToParentDirectoryAction; 925 } 926 927 /** 928 * Returns an approve selection action. 929 * @return an approve selection action 930 */ 931 public Action getApproveSelectionAction() { 932 return approveSelectionAction; 933 } 934 935 /** 936 * Returns a cancel selection action. 937 * @return a cancel selection action 938 */ 939 public Action getCancelSelectionAction() { 940 return cancelSelectionAction; 941 } 942 943 /** 944 * Returns an update action. 945 * @return an update action 946 */ 947 public Action getUpdateAction() { 948 return updateAction; 949 } 950 951 952 /** 953 * Creates a new folder. 954 */ 955 @SuppressWarnings("serial") // Superclass is not serializable across versions 956 protected class NewFolderAction extends AbstractAction { 957 /** Constructs a {@code NewFolderAction}. */ 958 protected NewFolderAction() { 959 super(FilePane.ACTION_NEW_FOLDER); 960 } 961 /** {@inheritDoc} */ 962 public void actionPerformed(ActionEvent e) { 963 if (readOnly) { 964 return; 965 } 966 JFileChooser fc = getFileChooser(); 967 File currentDirectory = fc.getCurrentDirectory(); 968 969 if (!currentDirectory.exists()) { 970 JOptionPane.showMessageDialog( 971 fc, 972 newFolderParentDoesntExistText, 973 newFolderParentDoesntExistTitleText, JOptionPane.WARNING_MESSAGE); 974 return; 975 } 976 977 File newFolder; 978 try { 979 newFolder = fc.getFileSystemView().createNewFolder(currentDirectory); 980 if (fc.isMultiSelectionEnabled()) { 981 fc.setSelectedFiles(new File[] { newFolder }); 982 } else { 983 fc.setSelectedFile(newFolder); 984 } 985 } catch (IOException exc) { 986 JOptionPane.showMessageDialog( 987 fc, 988 newFolderErrorText + newFolderErrorSeparator + exc, 989 newFolderErrorText, JOptionPane.ERROR_MESSAGE); 990 return; 991 } 992 993 fc.rescanCurrentDirectory(); 994 } 995 } 996 997 /** 998 * Acts on the "home" key event or equivalent event. 999 */ 1000 @SuppressWarnings("serial") // Superclass is not serializable across versions 1001 protected class GoHomeAction extends AbstractAction { 1002 /** Constructs a {@code GoHomeAction}. */ 1003 protected GoHomeAction() { 1004 super("Go Home"); 1005 } 1006 public void actionPerformed(ActionEvent e) { 1007 JFileChooser fc = getFileChooser(); 1008 changeDirectory(fc.getFileSystemView().getHomeDirectory()); 1009 } 1010 } 1011 1012 /** 1013 * Change to parent directory action. 1014 */ 1015 @SuppressWarnings("serial") // Superclass is not serializable across versions 1016 protected class ChangeToParentDirectoryAction extends AbstractAction { 1017 /** Constructs a {@code ChangeToParentDirectoryAction}. */ 1018 protected ChangeToParentDirectoryAction() { 1019 super("Go Up"); 1020 putValue(Action.ACTION_COMMAND_KEY, FilePane.ACTION_CHANGE_TO_PARENT_DIRECTORY); 1021 } 1022 /** {@inheritDoc} */ 1023 public void actionPerformed(ActionEvent e) { 1024 getFileChooser().changeToParentDirectory(); 1025 } 1026 } 1027 1028 /** 1029 * Responds to an Open or Save request 1030 */ 1031 @SuppressWarnings("serial") // Superclass is not serializable across versions 1032 protected class ApproveSelectionAction extends AbstractAction { 1033 /** Constructs an {@code ApproveSelectionAction}. */ 1034 protected ApproveSelectionAction() { 1035 super(FilePane.ACTION_APPROVE_SELECTION); 1036 } 1037 /** {@inheritDoc} */ 1038 public void actionPerformed(ActionEvent e) { 1039 if (isDirectorySelected()) { 1040 File dir = getDirectory(); 1041 if (dir != null) { 1042 try { 1043 // Strip trailing ".." 1044 dir = ShellFolder.getNormalizedFile(dir); 1045 } catch (IOException ex) { 1046 // Ok, use f as is 1047 } 1048 changeDirectory(dir); 1049 return; 1050 } 1051 } 1052 1053 JFileChooser chooser = getFileChooser(); 1054 1055 String filename = getFileName(); 1056 FileSystemView fs = chooser.getFileSystemView(); 1057 File dir = chooser.getCurrentDirectory(); 1058 1059 if (filename == null || filename.length() == 0) { 1060 // no file selected, multiple selection off, therefore cancel the approve action 1061 resetGlobFilter(); 1062 return; 1063 } 1064 1065 File selectedFile = null; 1066 File[] selectedFiles = null; 1067 1068 // Unix: Resolve '~' to user's home directory 1069 if (File.separatorChar == '/') { 1070 if (filename.startsWith("~/")) { 1071 filename = System.getProperty("user.home") + filename.substring(1); 1072 } else if (filename.equals("~")) { 1073 filename = System.getProperty("user.home"); 1074 } 1075 } 1076 1077 if (chooser.isMultiSelectionEnabled() && filename.length() > 1 && 1078 filename.charAt(0) == '"' && filename.charAt(filename.length() - 1) == '"') { 1079 List<File> fList = new ArrayList<File>(); 1080 1081 String[] files = filename.substring(1, filename.length() - 1).split("\" \""); 1082 // Optimize searching files by names in "children" array 1083 Arrays.sort(files); 1084 1085 File[] children = null; 1086 int childIndex = 0; 1087 1088 for (String str : files) { 1089 File file = fs.createFileObject(str); 1090 if (!file.isAbsolute()) { 1091 if (children == null) { 1092 children = fs.getFiles(dir, false); 1093 Arrays.sort(children); 1094 } 1095 for (int k = 0; k < children.length; k++) { 1096 int l = (childIndex + k) % children.length; 1097 if (children[l].getName().equals(str)) { 1098 file = children[l]; 1099 childIndex = l + 1; 1100 break; 1101 } 1102 } 1103 } 1104 fList.add(file); 1105 } 1106 1107 if (!fList.isEmpty()) { 1108 selectedFiles = fList.toArray(new File[fList.size()]); 1109 } 1110 resetGlobFilter(); 1111 } else { 1112 selectedFile = fs.createFileObject(filename); 1113 if (!selectedFile.isAbsolute()) { 1114 selectedFile = fs.getChild(dir, filename); 1115 } 1116 // check for wildcard pattern 1117 FileFilter currentFilter = chooser.getFileFilter(); 1118 if (!selectedFile.exists() && isGlobPattern(filename)) { 1119 changeDirectory(selectedFile.getParentFile()); 1120 if (globFilter == null) { 1121 globFilter = new GlobFilter(); 1122 } 1123 try { 1124 globFilter.setPattern(selectedFile.getName()); 1125 if (!(currentFilter instanceof GlobFilter)) { 1126 actualFileFilter = currentFilter; 1127 } 1128 chooser.setFileFilter(null); 1129 chooser.setFileFilter(globFilter); 1130 return; 1131 } catch (PatternSyntaxException pse) { 1132 // Not a valid glob pattern. Abandon filter. 1133 } 1134 } 1135 1136 resetGlobFilter(); 1137 1138 // Check for directory change action 1139 boolean isDir = (selectedFile != null && selectedFile.isDirectory()); 1140 boolean isTrav = (selectedFile != null && chooser.isTraversable(selectedFile)); 1141 boolean isDirSelEnabled = chooser.isDirectorySelectionEnabled(); 1142 boolean isFileSelEnabled = chooser.isFileSelectionEnabled(); 1143 boolean isCtrl = (e != null && (e.getModifiers() & 1144 Toolkit.getDefaultToolkit().getMenuShortcutKeyMask()) != 0); 1145 1146 if (isDir && isTrav && (isCtrl || !isDirSelEnabled)) { 1147 changeDirectory(selectedFile); 1148 return; 1149 } else if ((isDir || !isFileSelEnabled) 1150 && (!isDir || !isDirSelEnabled) 1151 && (!isDirSelEnabled || selectedFile.exists())) { 1152 selectedFile = null; 1153 } 1154 } 1155 1156 if (selectedFiles != null || selectedFile != null) { 1157 if (selectedFiles != null || chooser.isMultiSelectionEnabled()) { 1158 if (selectedFiles == null) { 1159 selectedFiles = new File[] { selectedFile }; 1160 } 1161 chooser.setSelectedFiles(selectedFiles); 1162 // Do it again. This is a fix for bug 4949273 to force the 1163 // selected value in case the ListSelectionModel clears it 1164 // for non-existing file names. 1165 chooser.setSelectedFiles(selectedFiles); 1166 } else { 1167 chooser.setSelectedFile(selectedFile); 1168 } 1169 chooser.approveSelection(); 1170 } else { 1171 if (chooser.isMultiSelectionEnabled()) { 1172 chooser.setSelectedFiles(null); 1173 } else { 1174 chooser.setSelectedFile(null); 1175 } 1176 chooser.cancelSelection(); 1177 } 1178 } 1179 } 1180 1181 1182 private void resetGlobFilter() { 1183 if (actualFileFilter != null) { 1184 JFileChooser chooser = getFileChooser(); 1185 FileFilter currentFilter = chooser.getFileFilter(); 1186 if (currentFilter != null && currentFilter.equals(globFilter)) { 1187 chooser.setFileFilter(actualFileFilter); 1188 chooser.removeChoosableFileFilter(globFilter); 1189 } 1190 actualFileFilter = null; 1191 } 1192 } 1193 1194 private static boolean isGlobPattern(String filename) { 1195 return ((File.separatorChar == '\\' && (filename.indexOf('*') >= 0 1196 || filename.indexOf('?') >= 0)) 1197 || (File.separatorChar == '/' && (filename.indexOf('*') >= 0 1198 || filename.indexOf('?') >= 0 1199 || filename.indexOf('[') >= 0))); 1200 } 1201 1202 1203 /* A file filter which accepts file patterns containing 1204 * the special wildcards *? on Windows and *?[] on Unix. 1205 */ 1206 class GlobFilter extends FileFilter { 1207 Pattern pattern; 1208 String globPattern; 1209 1210 public void setPattern(String globPattern) { 1211 char[] gPat = globPattern.toCharArray(); 1212 char[] rPat = new char[gPat.length * 2]; 1213 boolean isWin32 = (File.separatorChar == '\\'); 1214 boolean inBrackets = false; 1215 int j = 0; 1216 1217 this.globPattern = globPattern; 1218 1219 if (isWin32) { 1220 // On windows, a pattern ending with *.* is equal to ending with * 1221 int len = gPat.length; 1222 if (globPattern.endsWith("*.*")) { 1223 len -= 2; 1224 } 1225 for (int i = 0; i < len; i++) { 1226 switch(gPat[i]) { 1227 case '*': 1228 rPat[j++] = '.'; 1229 rPat[j++] = '*'; 1230 break; 1231 1232 case '?': 1233 rPat[j++] = '.'; 1234 break; 1235 1236 case '\\': 1237 rPat[j++] = '\\'; 1238 rPat[j++] = '\\'; 1239 break; 1240 1241 default: 1242 if ("+()^$.{}[]".indexOf(gPat[i]) >= 0) { 1243 rPat[j++] = '\\'; 1244 } 1245 rPat[j++] = gPat[i]; 1246 break; 1247 } 1248 } 1249 } else { 1250 for (int i = 0; i < gPat.length; i++) { 1251 switch(gPat[i]) { 1252 case '*': 1253 if (!inBrackets) { 1254 rPat[j++] = '.'; 1255 } 1256 rPat[j++] = '*'; 1257 break; 1258 1259 case '?': 1260 rPat[j++] = inBrackets ? '?' : '.'; 1261 break; 1262 1263 case '[': 1264 inBrackets = true; 1265 rPat[j++] = gPat[i]; 1266 1267 if (i < gPat.length - 1) { 1268 switch (gPat[i+1]) { 1269 case '!': 1270 case '^': 1271 rPat[j++] = '^'; 1272 i++; 1273 break; 1274 1275 case ']': 1276 rPat[j++] = gPat[++i]; 1277 break; 1278 } 1279 } 1280 break; 1281 1282 case ']': 1283 rPat[j++] = gPat[i]; 1284 inBrackets = false; 1285 break; 1286 1287 case '\\': 1288 if (i == 0 && gPat.length > 1 && gPat[1] == '~') { 1289 rPat[j++] = gPat[++i]; 1290 } else { 1291 rPat[j++] = '\\'; 1292 if (i < gPat.length - 1 && "*?[]".indexOf(gPat[i+1]) >= 0) { 1293 rPat[j++] = gPat[++i]; 1294 } else { 1295 rPat[j++] = '\\'; 1296 } 1297 } 1298 break; 1299 1300 default: 1301 //if ("+()|^$.{}<>".indexOf(gPat[i]) >= 0) { 1302 if (!Character.isLetterOrDigit(gPat[i])) { 1303 rPat[j++] = '\\'; 1304 } 1305 rPat[j++] = gPat[i]; 1306 break; 1307 } 1308 } 1309 } 1310 this.pattern = Pattern.compile(new String(rPat, 0, j), Pattern.CASE_INSENSITIVE); 1311 } 1312 1313 public boolean accept(File f) { 1314 if (f == null) { 1315 return false; 1316 } 1317 if (f.isDirectory()) { 1318 return true; 1319 } 1320 return pattern.matcher(f.getName()).matches(); 1321 } 1322 1323 public String getDescription() { 1324 return globPattern; 1325 } 1326 } 1327 1328 /** 1329 * Responds to a cancel request. 1330 */ 1331 @SuppressWarnings("serial") // Superclass is not serializable across versions 1332 protected class CancelSelectionAction extends AbstractAction { 1333 /** {@inheritDoc} */ 1334 public void actionPerformed(ActionEvent e) { 1335 getFileChooser().cancelSelection(); 1336 } 1337 } 1338 1339 /** 1340 * Rescans the files in the current directory 1341 */ 1342 @SuppressWarnings("serial") // Superclass is not serializable across versions 1343 protected class UpdateAction extends AbstractAction { 1344 /** {@inheritDoc} */ 1345 public void actionPerformed(ActionEvent e) { 1346 JFileChooser fc = getFileChooser(); 1347 fc.setCurrentDirectory(fc.getFileSystemView().createFileObject(getDirectoryName())); 1348 fc.rescanCurrentDirectory(); 1349 } 1350 } 1351 1352 1353 private void changeDirectory(File dir) { 1354 JFileChooser fc = getFileChooser(); 1355 1356 Cursor prevCursor = fc.getCursor(); 1357 fc.setCursor(Cursor.getPredefinedCursor(Cursor.WAIT_CURSOR)); 1358 1359 // Traverse shortcuts on Windows 1360 if (dir != null && FilePane.usesShellFolder(fc)) { 1361 try { 1362 ShellFolder shellFolder = ShellFolder.getShellFolder(dir); 1363 1364 if (shellFolder.isLink()) { 1365 File linkedTo = shellFolder.getLinkLocation(); 1366 1367 // If linkedTo is null we try to use dir 1368 if (linkedTo != null) { 1369 if (fc.isTraversable(linkedTo)) { 1370 dir = linkedTo; 1371 } else { 1372 return; 1373 } 1374 } else { 1375 dir = shellFolder; 1376 } 1377 } 1378 } catch (FileNotFoundException ex) { 1379 return; 1380 } 1381 } 1382 fc.setCurrentDirectory(dir); 1383 if (fc.getFileSelectionMode() == JFileChooser.FILES_AND_DIRECTORIES && 1384 fc.getFileSystemView().isFileSystem(dir)) { 1385 1386 setFileName(dir.getAbsolutePath()); 1387 } 1388 1389 fc.setCursor(prevCursor); 1390 } 1391 1392 1393 // ***************************************** 1394 // ***** default AcceptAll file filter ***** 1395 // ***************************************** 1396 /** 1397 * Accept all file filter. 1398 */ 1399 protected class AcceptAllFileFilter extends FileFilter { 1400 1401 /** Constructs an {@code AcceptAllFileFilter}. */ 1402 public AcceptAllFileFilter() { 1403 } 1404 1405 /** 1406 * Returns true. 1407 * @param f the file 1408 * @return true 1409 */ 1410 public boolean accept(File f) { 1411 return true; 1412 } 1413 1414 /** 1415 * {@inheritDoc} 1416 */ 1417 public String getDescription() { 1418 return UIManager.getString("FileChooser.acceptAllFileFilterText"); 1419 } 1420 } 1421 1422 1423 // *********************** 1424 // * FileView operations * 1425 // *********************** 1426 /** 1427 * A basic file view. 1428 */ 1429 protected class BasicFileView extends FileView { 1430 /* FileView type descriptions */ 1431 /** The icon cache */ 1432 protected Hashtable<File,Icon> iconCache = new Hashtable<File,Icon>(); 1433 1434 /** Constructs a {@code BasicFileView}. */ 1435 public BasicFileView() { 1436 } 1437 1438 /** 1439 * Clears the icon cache. 1440 */ 1441 public void clearIconCache() { 1442 iconCache = new Hashtable<File,Icon>(); 1443 } 1444 1445 /** {@inheritDoc} */ 1446 public String getName(File f) { 1447 // Note: Returns display name rather than file name 1448 String fileName = null; 1449 if(f != null) { 1450 fileName = getFileChooser().getFileSystemView().getSystemDisplayName(f); 1451 } 1452 return fileName; 1453 } 1454 1455 /** {@inheritDoc} */ 1456 public String getDescription(File f) { 1457 return f.getName(); 1458 } 1459 1460 /** {@inheritDoc} */ 1461 public String getTypeDescription(File f) { 1462 String type = getFileChooser().getFileSystemView().getSystemTypeDescription(f); 1463 if (type == null) { 1464 if (f.isDirectory()) { 1465 type = directoryDescriptionText; 1466 } else { 1467 type = fileDescriptionText; 1468 } 1469 } 1470 return type; 1471 } 1472 1473 /** 1474 * Returns the cached icon for the file. 1475 * @param f the file 1476 * @return the cached icon for the file 1477 */ 1478 public Icon getCachedIcon(File f) { 1479 return iconCache.get(f); 1480 } 1481 1482 /** 1483 * Caches an icon for a file. 1484 * @param f the file 1485 * @param i the icon 1486 */ 1487 public void cacheIcon(File f, Icon i) { 1488 if(f == null || i == null) { 1489 return; 1490 } 1491 iconCache.put(f, i); 1492 } 1493 1494 /** {@inheritDoc} */ 1495 public Icon getIcon(File f) { 1496 Icon icon = getCachedIcon(f); 1497 if(icon != null) { 1498 return icon; 1499 } 1500 icon = fileIcon; 1501 if (f != null) { 1502 FileSystemView fsv = getFileChooser().getFileSystemView(); 1503 1504 if (fsv.isFloppyDrive(f)) { 1505 icon = floppyDriveIcon; 1506 } else if (fsv.isDrive(f)) { 1507 icon = hardDriveIcon; 1508 } else if (fsv.isComputerNode(f)) { 1509 icon = computerIcon; 1510 } else if (f.isDirectory()) { 1511 icon = directoryIcon; 1512 } 1513 } 1514 cacheIcon(f, icon); 1515 return icon; 1516 } 1517 1518 /** 1519 * Returns whether or not a file is hidden. 1520 * @param f the file 1521 * @return whether or not a file is hidden 1522 */ 1523 public Boolean isHidden(File f) { 1524 String name = f.getName(); 1525 if(name != null && name.charAt(0) == '.') { 1526 return Boolean.TRUE; 1527 } else { 1528 return Boolean.FALSE; 1529 } 1530 } 1531 } 1532 1533 private static final TransferHandler defaultTransferHandler = new FileTransferHandler(); 1534 1535 /** 1536 * Data transfer support for the file chooser. Since files are currently presented 1537 * as a list, the list support is reused with the added flavor of DataFlavor.javaFileListFlavor 1538 */ 1539 @SuppressWarnings("serial") // JDK-implementation class 1540 static class FileTransferHandler extends TransferHandler implements UIResource { 1541 1542 /** 1543 * Create a Transferable to use as the source for a data transfer. 1544 * 1545 * @param c The component holding the data to be transfered. This 1546 * argument is provided to enable sharing of TransferHandlers by 1547 * multiple components. 1548 * @return The representation of the data to be transfered. 1549 * 1550 */ 1551 @SuppressWarnings("deprecation") 1552 protected Transferable createTransferable(JComponent c) { 1553 Object[] values = null; 1554 if (c instanceof JList) { 1555 values = ((JList)c).getSelectedValues(); 1556 } else if (c instanceof JTable) { 1557 JTable table = (JTable)c; 1558 int[] rows = table.getSelectedRows(); 1559 if (rows != null) { 1560 values = new Object[rows.length]; 1561 for (int i=0; i<rows.length; i++) { 1562 values[i] = table.getValueAt(rows[i], 0); 1563 } 1564 } 1565 } 1566 if (values == null || values.length == 0) { 1567 return null; 1568 } 1569 1570 try { 1571 BufferedImage img = null; 1572 Graphics2D g = null; 1573 for (Object value : values) { 1574 File f=(File) value; 1575 Image icon = ShellFolder.getShellFolder(f).getIcon(true); 1576 if(img == null) { 1577 img = new BufferedImage(icon.getWidth(null)+20, 1578 icon.getHeight(null)+20, TYPE_INT_ARGB); 1579 g = img.createGraphics(); 1580 1581 int w = img.getWidth()-2; 1582 int h = img.getHeight()-2; 1583 g.setPaint(new GradientPaint( 1584 0, 0, new Color(0, 149, 234, 12), 1585 0, h, new Color(0, 155, 237, 28) 1586 )); 1587 g.fillRoundRect(0, 0, w, h, 4, 4); 1588 1589 g.setColor(new Color(0, 170, 248, 36)); 1590 g.drawRoundRect(0, 0, w, h, 4, 4); 1591 } 1592 g.drawImage(icon, 1593 img.getWidth()/2-icon.getWidth(null)/2, 1594 img.getHeight() / 2-icon.getHeight(null)/2, null); 1595 } 1596 setDragImage(img); 1597 setDragImageOffset(new Point( 1598 img.getWidth()/2, img.getHeight()-5)); 1599 } catch (FileNotFoundException ex) { 1600 ex.printStackTrace(); 1601 setDragImage(null); 1602 } 1603 1604 StringBuilder plainBuf = new StringBuilder(); 1605 StringBuilder htmlBuf = new StringBuilder(); 1606 1607 htmlBuf.append("<html>\n<body>\n<ul>\n"); 1608 1609 for (Object obj : values) { 1610 String val = ((obj == null) ? "" : obj.toString()); 1611 plainBuf.append(val).append('\n'); 1612 htmlBuf.append(" <li>").append(val).append('\n'); 1613 } 1614 1615 // remove the last newline 1616 plainBuf.deleteCharAt(plainBuf.length() - 1); 1617 htmlBuf.append("</ul>\n</body>\n</html>"); 1618 1619 return new FileTransferable(plainBuf.toString(), htmlBuf.toString(), values); 1620 } 1621 1622 public int getSourceActions(JComponent c) { 1623 return COPY|MOVE; 1624 } 1625 1626 static class FileTransferable extends BasicTransferable { 1627 1628 Object[] fileData; 1629 1630 FileTransferable(String plainData, String htmlData, Object[] fileData) { 1631 super(plainData, htmlData); 1632 this.fileData = fileData; 1633 } 1634 1635 /** 1636 * Best format of the file chooser is DataFlavor.javaFileListFlavor. 1637 */ 1638 protected DataFlavor[] getRicherFlavors() { 1639 DataFlavor[] flavors = new DataFlavor[1]; 1640 flavors[0] = DataFlavor.javaFileListFlavor; 1641 return flavors; 1642 } 1643 1644 /** 1645 * The only richer format supported is the file list flavor 1646 */ 1647 protected Object getRicherData(DataFlavor flavor) { 1648 if (DataFlavor.javaFileListFlavor.equals(flavor)) { 1649 ArrayList<Object> files = new ArrayList<Object>(); 1650 for (Object file : this.fileData) { 1651 files.add(file); 1652 } 1653 return files; 1654 } 1655 return null; 1656 } 1657 1658 } 1659 } 1660 }