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