001: /*
002: * @(#)BasicFolderChooserUI.java 4/12/2006
003: *
004: * Copyright 2002 - 2006 JIDE Software Inc. All rights reserved.
005: */
006: package com.jidesoft.plaf.basic;
007:
008: import com.jidesoft.dialog.ButtonPanel;
009: import com.jidesoft.plaf.FolderChooserUI;
010: import com.jidesoft.swing.FolderChooser;
011: import com.jidesoft.utils.SystemInfo;
012: import sun.awt.shell.ShellFolder;
013:
014: import javax.swing.*;
015: import javax.swing.event.TreeSelectionEvent;
016: import javax.swing.event.TreeSelectionListener;
017: import javax.swing.filechooser.FileSystemView;
018: import javax.swing.plaf.ComponentUI;
019: import javax.swing.plaf.basic.BasicFileChooserUI;
020: import javax.swing.tree.MutableTreeNode;
021: import javax.swing.tree.TreePath;
022: import javax.swing.tree.TreeSelectionModel;
023: import java.awt.*;
024: import java.awt.event.ActionEvent;
025: import java.beans.PropertyChangeEvent;
026: import java.beans.PropertyChangeListener;
027: import java.io.File;
028: import java.text.MessageFormat;
029: import java.util.*;
030: import java.util.List;
031:
032: public class BasicFolderChooserUI extends BasicFileChooserUI implements
033: FolderChooserUI {
034: private FolderChooser _folderChooser;
035:
036: private FolderToolBar _toolbar;
037: private JTree _fileSystemTree;
038: private JScrollPane _treeScrollPane;
039:
040: private JButton _approveButton;
041: private JButton _cancelButton;
042: private JPanel _buttonPanel;
043:
044: private Action _approveSelectionAction = new ApproveSelectionAction();
045: public BasicFolderChooserUI.FolderChooserSelectionListener _selectionListener;
046:
047: public BasicFolderChooserUI(FolderChooser chooser) {
048: super (chooser);
049: BasicFileSystemTreeNode.clearCache();
050: }
051:
052: public static ComponentUI createUI(JComponent c) {
053: return new BasicFolderChooserUI((FolderChooser) c);
054: }
055:
056: @Override
057: public void installComponents(JFileChooser chooser) {
058: _folderChooser = (FolderChooser) chooser;
059:
060: JPanel panel = new JPanel(new BorderLayout(6, 6));
061: panel.add(createFileSystemTreePanel(), BorderLayout.CENTER);
062: panel.add(createToolbar(), BorderLayout.BEFORE_FIRST_LINE);
063: panel.setBorder(BorderFactory.createEmptyBorder(6, 6, 6, 6));
064: chooser.add(panel);
065:
066: chooser.setLayout(new BorderLayout());
067:
068: chooser.setFileSelectionMode(JFileChooser.DIRECTORIES_ONLY);
069:
070: Component accessory = chooser.getAccessory();
071: if (accessory != null) {
072: chooser.add(chooser.getAccessory(),
073: BorderLayout.BEFORE_FIRST_LINE);
074: }
075:
076: chooser.add(panel, BorderLayout.CENTER);
077: chooser.add(_buttonPanel = createButtonPanel(),
078: BorderLayout.AFTER_LAST_LINE);
079:
080: updateView(chooser);
081:
082: Runnable runnable = new Runnable() {
083: public void run() {
084: _fileSystemTree.requestFocusInWindow();
085: }
086: };
087: SwingUtilities.invokeLater(runnable);
088: }
089:
090: protected JPanel createButtonPanel() {
091: _approveButton = new JButton();
092: _approveButton.setAction(getApproveSelectionAction());
093:
094: _cancelButton = new JButton();
095: _cancelButton.addActionListener(getCancelSelectionAction());
096:
097: ButtonPanel buttonPanel = new ButtonPanel();
098: buttonPanel.setBorder(BorderFactory.createEmptyBorder(0, 6, 6,
099: 6));
100: buttonPanel.addButton(_approveButton,
101: ButtonPanel.AFFIRMATIVE_BUTTON);
102: buttonPanel.addButton(_cancelButton, ButtonPanel.CANCEL_BUTTON);
103: return buttonPanel;
104: }
105:
106: @Override
107: public void rescanCurrentDirectory(JFileChooser fc) {
108: super .rescanCurrentDirectory(fc);
109: }
110:
111: @Override
112: public void ensureFileIsVisible(JFileChooser fc, File f) {
113: super .ensureFileIsVisible(fc, f);
114: ensureFileIsVisible(f, true);
115: }
116:
117: protected JComponent createToolbar() {
118: _toolbar = new FolderToolBar(true, _folderChooser
119: .getRecentList());
120: _toolbar.addListener(new FolderToolBarListener() {
121: // ------------------------------------------------------------------------------
122: // Implementation of FolderToolBarListener
123: // ------------------------------------------------------------------------------
124:
125: public void deleteFolderButtonClicked() {
126: // make sure user really wants to do this
127: String text, header;
128: TreePath path = _fileSystemTree.getSelectionPaths()[0];
129: java.util.List selection = getSelectedFolders(new TreePath[] { path });
130:
131: final ResourceBundle resourceBundle = FolderChooserResource
132: .getResourceBundle(Locale.getDefault());
133: if (selection.size() > 1) {
134: text = MessageFormat
135: .format(
136: resourceBundle
137: .getString("FolderChooser.delete.message2"),
138: selection.size());
139: } else {
140: text = resourceBundle
141: .getString("FolderChooser.delete.message1");
142: }
143: final String title = resourceBundle
144: .getString("FolderChooser.delete.title");
145:
146: int result = JOptionPane.showConfirmDialog(
147: _folderChooser, text, title,
148: JOptionPane.OK_CANCEL_OPTION,
149: JOptionPane.WARNING_MESSAGE);
150: if (result == JOptionPane.OK_OPTION) {
151: TreePath parentPath = path.getParentPath();
152: Object parentObject = parentPath
153: .getLastPathComponent();
154: Object deletedObject = path.getLastPathComponent();
155: int index = _fileSystemTree.getModel()
156: .getIndexOfChild(parentObject,
157: deletedObject);
158: for (int i = 0; i < selection.size(); i++) {
159: File f = (File) selection.get(i);
160: recursiveDelete(f);
161: }
162: ((BasicFileSystemTreeModel) _fileSystemTree
163: .getModel()).removePath(path, index,
164: deletedObject);
165: TreePath pathToSelect = parentPath;
166: if (index >= ((MutableTreeNode) parentObject)
167: .getChildCount()) {
168: index = ((MutableTreeNode) parentObject)
169: .getChildCount() - 1;
170: }
171: if (index > 0) {
172: pathToSelect = parentPath
173: .pathByAddingChild(((MutableTreeNode) parentObject)
174: .getChildAt(index));
175: }
176: _fileSystemTree.setSelectionPath(pathToSelect);
177: _fileSystemTree.scrollPathToVisible(pathToSelect);
178: }
179:
180: }
181:
182: /**
183: * Recursively deletes a file/directory.
184: *
185: * @param file The file/folder to delete
186: * @return <code>true</code> only if the file and all children were successfully deleted.
187: */
188: public final boolean recursiveDelete(File file) {
189: if (isFileSystem(file) && file.isDirectory()) {
190: // delete all children first
191: File[] children = FileSystemView
192: .getFileSystemView().getFiles(file, false);
193: for (int i = 0; i < children.length; i++) {
194: File f = children[i];
195: if (!recursiveDelete(f)) {
196: return false;
197: }
198: }
199: // delete this file.
200: return file.delete();
201: } else {
202: return false;
203: }
204: }
205:
206: public void newFolderButtonClicked() {
207: // get the selected folder
208: TreePath[] paths = _fileSystemTree.getSelectionPaths();
209: java.util.List selection = getSelectedFolders(paths);
210: if (selection.size() > 1 || selection.size() == 0)
211: return; // should never happen
212:
213: File parent = (File) selection.get(0);
214:
215: final ResourceBundle resourceBundle = FolderChooserResource
216: .getResourceBundle(Locale.getDefault());
217: String folderName = JOptionPane
218: .showInputDialog(
219: _folderChooser,
220: resourceBundle
221: .getString("FolderChooser.new.folderName"),
222: resourceBundle
223: .getString("FolderChooser.new.title"),
224: JOptionPane.OK_CANCEL_OPTION
225: | JOptionPane.QUESTION_MESSAGE);
226:
227: if (folderName != null) {
228: File newFolder = new File(parent, folderName);
229: boolean success = newFolder.mkdir();
230:
231: TreePath parentPath = paths[0];
232: boolean isExpanded = _fileSystemTree
233: .isExpanded(parentPath);
234: if (!isExpanded) { // expand it first
235: _fileSystemTree.expandPath(parentPath);
236: }
237:
238: LazyMutableTreeNode parentTreeNode = (LazyMutableTreeNode) parentPath
239: .getLastPathComponent();
240: BasicFileSystemTreeNode child = BasicFileSystemTreeNode
241: .createFileSystemTreeNode(newFolder,
242: _folderChooser);
243: // child.setParent(parentTreeNode);
244: if (success) {
245: parentTreeNode.clear();
246: int insertIndex = _fileSystemTree.getModel()
247: .getIndexOfChild(parentTreeNode, child);
248: if (insertIndex != -1) {
249: // ((BasicFileSystemTreeModel) _fileSystemTree.getModel()).insertNodeInto(child, parentTreeNode, insertIndex);
250: ((BasicFileSystemTreeModel) _fileSystemTree
251: .getModel())
252: .nodeStructureChanged(parentTreeNode);
253: // ((BasicFileSystemTreeModel) _fileSystemTree.getModel()).addPath(parentPath, insertIndex, child);
254: }
255: }
256: TreePath newPath = parentPath
257: .pathByAddingChild(child);
258: _fileSystemTree.setSelectionPath(newPath);
259: _fileSystemTree.scrollPathToVisible(newPath);
260: }
261: }
262:
263: public void myDocumentsButtonClicked() {
264: File myDocuments = FileSystemView.getFileSystemView()
265: .getDefaultDirectory();
266: ensureFileIsVisible(myDocuments, true);
267: }
268:
269: public void desktopButtonClicked() {
270: File desktop = FileSystemView.getFileSystemView()
271: .getHomeDirectory();
272: ensureFileIsVisible(desktop, true);
273: }
274:
275: public void recentFolderSelected(final File file) {
276: new Thread(new Runnable() {
277: public void run() {
278: setWaitCursor(true);
279: try {
280: ensureFileIsVisible(file, true);
281: } finally {
282: setWaitCursor(false);
283: }
284: }
285: }).start();
286: }
287:
288: private Cursor m_oldCursor;
289:
290: private void setWaitCursor(boolean isWait) {
291: Window parentWindow = SwingUtilities
292: .getWindowAncestor(_folderChooser);
293: if (isWait) {
294: Cursor hourglassCursor = new Cursor(
295: Cursor.WAIT_CURSOR);
296: m_oldCursor = parentWindow.getCursor();
297: parentWindow.setCursor(hourglassCursor);
298: } else {
299: if (m_oldCursor != null) {
300: parentWindow.setCursor(m_oldCursor);
301: m_oldCursor = null;
302: }
303: }
304: }
305:
306: public java.util.List getSelectedFolders() {
307: TreePath[] paths = _fileSystemTree.getSelectionPaths();
308: return getSelectedFolders(paths);
309: }
310:
311: public java.util.List getSelectedFolders(TreePath[] paths) {
312: if (paths == null || paths.length == 0)
313: return new ArrayList();
314:
315: List folders = new ArrayList(paths.length);
316: for (int i = 0; i < paths.length; i++) {
317: TreePath path = paths[i];
318: BasicFileSystemTreeNode f = (BasicFileSystemTreeNode) path
319: .getLastPathComponent();
320: folders.add(f.getFile());
321: }
322: return folders;
323: }
324:
325: });
326: updateToolbarButtons();
327: return _toolbar;
328: }
329:
330: /**
331: * Updates toolbar button status depending on current selection status
332: */
333: protected void updateToolbarButtons() {
334: // delete folder button
335: TreePath[] selectedFiles = _fileSystemTree == null ? new TreePath[0]
336: : _fileSystemTree.getSelectionPaths();
337: // System.out.println("selectedFiles.length = " + selectedFiles == null ? 0 : selectedFiles.length);
338: if (selectedFiles != null && selectedFiles.length > 0) {
339: _toolbar.enableDelete();
340: } else {
341: _toolbar.disableDelete();
342: }
343:
344: // new folder button (only enable if exactly one folder selected
345: if (selectedFiles != null && selectedFiles.length == 1) {
346: _toolbar.enableNewFolder();
347: } else {
348: _toolbar.disableNewFolder();
349: }
350: }
351:
352: private JComponent createFileSystemTreePanel() {
353: JPanel panel = new JPanel(new BorderLayout());
354: _fileSystemTree = new BasicFileSystemTree(_folderChooser);
355: updateMultiSelectionEnabled();
356: _treeScrollPane = new JScrollPane(_fileSystemTree);
357: panel.add(_treeScrollPane);
358: return panel;
359: }
360:
361: private void updateMultiSelectionEnabled() {
362: if (_folderChooser.isMultiSelectionEnabled()) {
363: _fileSystemTree.getSelectionModel().setSelectionMode(
364: TreeSelectionModel.DISCONTIGUOUS_TREE_SELECTION);
365: } else {
366: _fileSystemTree.getSelectionModel().setSelectionMode(
367: TreeSelectionModel.SINGLE_TREE_SELECTION);
368: }
369: }
370:
371: @Override
372: public void uninstallComponents(JFileChooser chooser) {
373: chooser.remove(_treeScrollPane);
374: chooser.remove(_buttonPanel);
375: }
376:
377: @Override
378: protected void installListeners(JFileChooser fc) {
379: super .installListeners(fc);
380: _selectionListener = new FolderChooserSelectionListener();
381: _fileSystemTree.addTreeSelectionListener(_selectionListener);
382: }
383:
384: @Override
385: protected void uninstallListeners(JFileChooser fc) {
386: super .uninstallListeners(fc);
387: _fileSystemTree.removeTreeSelectionListener(_selectionListener);
388: }
389:
390: @Override
391: public PropertyChangeListener createPropertyChangeListener(
392: JFileChooser fc) {
393: return new FolderChooserPropertyChangeListener();
394: }
395:
396: private void updateView(JFileChooser chooser) {
397: if (chooser.getApproveButtonText() != null) {
398: _approveButton.setText(chooser.getApproveButtonText());
399: _approveButton.setMnemonic(chooser
400: .getApproveButtonMnemonic());
401: } else {
402: if (JFileChooser.OPEN_DIALOG == chooser.getDialogType()) {
403: _approveButton.setText(openButtonText);
404: _approveButton.setToolTipText(openButtonToolTipText);
405: _approveButton.setMnemonic(openButtonMnemonic);
406: } else {
407: _approveButton.setText(saveButtonText);
408: _approveButton.setToolTipText(saveButtonToolTipText);
409: _approveButton.setMnemonic(saveButtonMnemonic);
410: }
411: }
412:
413: _cancelButton.setText(cancelButtonText);
414: _cancelButton.setMnemonic(cancelButtonMnemonic);
415:
416: _buttonPanel.setVisible(chooser.getControlButtonsAreShown());
417: }
418:
419: /**
420: * Checks if <code>f</code> represents a real directory or file as opposed to a
421: * special folder such as <code>"Desktop"</code>. Used by UI classes to decide if
422: * a folder is selectable when doing directory choosing.
423: *
424: * @param f a <code>File</code> object
425: * @return <code>true</code> if <code>f</code> is a real file or directory.
426: */
427: public static boolean isFileSystem(File f) {
428: if (f instanceof ShellFolder) {
429: ShellFolder sf = (ShellFolder) f;
430: // Shortcuts to directories are treated as not being file system objects,
431: // so that they are never returned by JFileChooser.
432: return sf.isFileSystem()
433: && !(sf.isLink() && sf.isDirectory());
434: } else {
435: return true;
436: }
437: }
438:
439: private TreePath getTreePathForFile(File file) {
440: if (!file.isDirectory()) {
441: return null;
442: }
443: Stack stack = new Stack();
444: List list = new ArrayList();
445: list.add(_fileSystemTree.getModel().getRoot());
446: FileSystemView fsv = _folderChooser.getFileSystemView();
447: File[] alternativeRoots = null;
448: File root = null;
449: if (SystemInfo.isWindows()) {
450: File[] roots = fsv.getRoots();
451: root = roots[0];
452: if (isFileSystem(root) && root.isDirectory()) {
453: alternativeRoots = root.listFiles();
454: }
455: }
456: File parent = file;
457: outloop: do {
458: stack.push(parent);
459: if (alternativeRoots != null) {
460: for (int i = 0; i < alternativeRoots.length; i++) {
461: File r = alternativeRoots[i];
462: if (r.equals(parent)) {
463: stack.push(root);
464: break outloop;
465: }
466: }
467: }
468: parent = _folderChooser.getFileSystemView()
469: .getParentDirectory(parent);
470: } while (parent != null);
471:
472: while (!stack.empty()) {
473: list.add(BasicFileSystemTreeNode.createFileSystemTreeNode(
474: (File) stack.pop(), _folderChooser));
475: }
476: return new TreePath(list.toArray());
477: }
478:
479: private void ensureFileIsVisible(File file, boolean scroll) {
480: final TreePath path = file == null ? new TreePath(
481: _fileSystemTree.getModel().getRoot())
482: : getTreePathForFile(file);
483: if (path != null) {
484: _fileSystemTree.setSelectionPath(path);
485: _fileSystemTree.expandPath(path);
486: if (scroll) {
487: Runnable runnable = new Runnable() {
488: public void run() {
489: _fileSystemTree.scrollPathToVisible(path);
490: }
491: };
492: SwingUtilities.invokeLater(runnable);
493: }
494: }
495: // getApproveSelectionAction().setEnabled(_fileSystemTree.getSelectionCount() > 0);
496: }
497:
498: private class FolderChooserPropertyChangeListener implements
499: PropertyChangeListener {
500: public void propertyChange(PropertyChangeEvent evt) {
501: if (FolderChooser.PROPERTY_RECENTLIST.equals(evt
502: .getPropertyName())) {
503: _toolbar.setRecentList((List) evt.getNewValue());
504: } else if (JFileChooser.APPROVE_BUTTON_TEXT_CHANGED_PROPERTY
505: .equals(evt.getPropertyName())) {
506: updateView(_folderChooser);
507: } else if (JFileChooser.DIALOG_TYPE_CHANGED_PROPERTY
508: .equals(evt.getPropertyName())) {
509: updateView(_folderChooser);
510: } else if (JFileChooser.MULTI_SELECTION_ENABLED_CHANGED_PROPERTY
511: .equals(evt.getPropertyName())) {
512: updateMultiSelectionEnabled();
513: } else if (JFileChooser.DIRECTORY_CHANGED_PROPERTY
514: .equals(evt.getPropertyName())) {
515: ensureFileIsVisible(_folderChooser
516: .getCurrentDirectory(), true);
517: } else if (JFileChooser.ACCESSORY_CHANGED_PROPERTY
518: .equals(evt.getPropertyName())) {
519: Component oldValue = (Component) evt.getOldValue();
520: Component newValue = (Component) evt.getNewValue();
521: if (oldValue != null) {
522: _folderChooser.remove(oldValue);
523: }
524: if (newValue != null) {
525: _folderChooser.add(newValue,
526: BorderLayout.BEFORE_FIRST_LINE);
527: }
528: _folderChooser.revalidate();
529: _folderChooser.repaint();
530: } else if (JFileChooser.CONTROL_BUTTONS_ARE_SHOWN_CHANGED_PROPERTY
531: .equals(evt.getPropertyName())) {
532: updateView(_folderChooser);
533: }
534: }
535: }
536:
537: private class FolderChooserSelectionListener implements
538: TreeSelectionListener {
539: public void valueChanged(TreeSelectionEvent e) {
540: getApproveSelectionAction().setEnabled(
541: _fileSystemTree.getSelectionCount() > 0);
542: if (_toolbar != null) {
543: updateToolbarButtons();
544: }
545: }
546: }
547:
548: private void setSelectedFiles() {
549: TreePath[] selectedPaths = _fileSystemTree.getSelectionPaths();
550: if (selectedPaths == null || selectedPaths.length == 0) {
551: _folderChooser.setSelectedFile(null);
552: return;
553: }
554:
555: java.util.List files = new ArrayList();
556: for (int i = 0, c = selectedPaths.length; i < c; i++) {
557: File f = ((BasicFileSystemTreeNode) selectedPaths[i]
558: .getLastPathComponent()).getFile();
559: files.add(f);
560: }
561:
562: _folderChooser.setSelectedFiles((File[]) files
563: .toArray(new File[files.size()]));
564: }
565:
566: @Override
567: public Action getApproveSelectionAction() {
568: return _approveSelectionAction;
569: }
570:
571: private class ApproveSelectionAction extends AbstractAction {
572: public ApproveSelectionAction() {
573: setEnabled(false);
574: }
575:
576: public void actionPerformed(ActionEvent e) {
577: setSelectedFiles();
578: _folderChooser.approveSelection();
579: }
580: }
581: }
|