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