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 }