001: /**
002: * L2FProd.com Common Components 7.3 License.
003: *
004: * Copyright 2005-2007 L2FProd.com
005: *
006: * Licensed under the Apache License, Version 2.0 (the "License");
007: * you may not use this file except in compliance with the License.
008: * You may obtain a copy of the License at
009: *
010: * http://www.apache.org/licenses/LICENSE-2.0
011: *
012: * Unless required by applicable law or agreed to in writing, software
013: * distributed under the License is distributed on an "AS IS" BASIS,
014: * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
015: * See the License for the specific language governing permissions and
016: * limitations under the License.
017: */package com.l2fprod.common.swing.plaf.windows;
018:
019: import com.l2fprod.common.swing.JDirectoryChooser;
020: import com.l2fprod.common.swing.LookAndFeelTweaks;
021: import com.l2fprod.common.swing.plaf.DirectoryChooserUI;
022: import com.l2fprod.common.swing.tree.LazyMutableTreeNode;
023: import com.l2fprod.common.util.OS;
024:
025: import java.awt.Component;
026: import java.awt.Dimension;
027: import java.awt.GridBagConstraints;
028: import java.awt.GridBagLayout;
029: import java.awt.Insets;
030: import java.awt.event.ActionEvent;
031: import java.awt.event.MouseEvent;
032: import java.beans.PropertyChangeEvent;
033: import java.beans.PropertyChangeListener;
034: import java.io.File;
035: import java.lang.reflect.InvocationTargetException;
036: import java.util.ArrayList;
037: import java.util.Arrays;
038: import java.util.Enumeration;
039: import java.util.List;
040: import java.util.Stack;
041:
042: import javax.swing.AbstractAction;
043: import javax.swing.Action;
044: import javax.swing.Box;
045: import javax.swing.Icon;
046: import javax.swing.JButton;
047: import javax.swing.JComponent;
048: import javax.swing.JFileChooser;
049: import javax.swing.JOptionPane;
050: import javax.swing.JPanel;
051: import javax.swing.JScrollPane;
052: import javax.swing.JTree;
053: import javax.swing.KeyStroke;
054: import javax.swing.SwingUtilities;
055: import javax.swing.UIManager;
056: import javax.swing.event.TreeExpansionEvent;
057: import javax.swing.event.TreeExpansionListener;
058: import javax.swing.event.TreeSelectionEvent;
059: import javax.swing.event.TreeSelectionListener;
060: import javax.swing.filechooser.FileSystemView;
061: import javax.swing.filechooser.FileView;
062: import javax.swing.plaf.ComponentUI;
063: import javax.swing.plaf.basic.BasicFileChooserUI;
064: import javax.swing.tree.DefaultMutableTreeNode;
065: import javax.swing.tree.DefaultTreeCellRenderer;
066: import javax.swing.tree.DefaultTreeModel;
067: import javax.swing.tree.TreePath;
068: import javax.swing.tree.TreeSelectionModel;
069:
070: /**
071: * WindowsDirectoryChooserUI. <br>
072: *
073: */
074: public class WindowsDirectoryChooserUI extends BasicFileChooserUI
075: implements DirectoryChooserUI {
076:
077: public static ComponentUI createUI(JComponent c) {
078: return new WindowsDirectoryChooserUI((JFileChooser) c);
079: }
080:
081: private static Queue nodeQueue;
082:
083: private JFileChooser chooser;
084: private JTree tree;
085: private JScrollPane treeScroll;
086: private JButton approveButton;
087: private JButton cancelButton;
088: private JPanel buttonPanel;
089: private BasicFileView fileView = new WindowsFileView();
090: private Action approveSelectionAction = new ApproveSelectionAction();
091: private boolean useNodeQueue;
092:
093: private JButton newFolderButton;
094: private Action newFolderAction = new NewFolderAction();
095: private String newFolderText = null;
096: private String newFolderToolTipText = null;
097:
098: public WindowsDirectoryChooserUI(JFileChooser chooser) {
099: super (chooser);
100: }
101:
102: public void rescanCurrentDirectory(JFileChooser fc) {
103: super .rescanCurrentDirectory(fc);
104: findFile(chooser.getSelectedFile() == null ? chooser
105: .getCurrentDirectory() : chooser.getSelectedFile(),
106: true, true);
107: }
108:
109: public void ensureFileIsVisible(JFileChooser fc, File f) {
110: super .ensureFileIsVisible(fc, f);
111: File selectedFile = fc.getSelectedFile();
112: boolean select = selectedFile != null && selectedFile.equals(f);
113: findFile(f, select, false);
114: }
115:
116: protected String getToolTipText(MouseEvent event) {
117: TreePath path = tree.getPathForLocation(event.getX(), event
118: .getY());
119: if (path != null
120: && path.getLastPathComponent() instanceof FileTreeNode) {
121: FileTreeNode node = (FileTreeNode) path
122: .getLastPathComponent();
123: String typeDescription = getFileView(chooser)
124: .getTypeDescription(node.getFile());
125: if (typeDescription == null
126: || typeDescription.length() == 0) {
127: return node.toString();
128: } else {
129: return node.toString() + " - " + typeDescription;
130: }
131: } else {
132: return null;
133: }
134: }
135:
136: public void installComponents(JFileChooser chooser) {
137: this .chooser = chooser;
138:
139: chooser.setLayout(LookAndFeelTweaks.createBorderLayout());
140: chooser.setFileSelectionMode(JFileChooser.DIRECTORIES_ONLY);
141:
142: Component accessory = chooser.getAccessory();
143: if (accessory != null) {
144: chooser.add("North", chooser.getAccessory());
145: }
146:
147: tree = new JTree() {
148:
149: public String getToolTipText(MouseEvent event) {
150: String tip = WindowsDirectoryChooserUI.this
151: .getToolTipText(event);
152: if (tip == null) {
153: return super .getToolTipText(event);
154: } else {
155: return tip;
156: }
157: }
158: };
159: tree.addTreeExpansionListener(new TreeExpansion());
160:
161: tree.setModel(new FileSystemTreeModel(chooser
162: .getFileSystemView()));
163: tree.setRootVisible(false);
164: tree.setShowsRootHandles(false);
165: tree.setCellRenderer(new FileSystemTreeRenderer());
166: tree.setToolTipText("");
167:
168: chooser.add("Center", treeScroll = new JScrollPane(tree));
169: treeScroll.setPreferredSize(new Dimension(300, 300));
170:
171: approveButton = new JButton();
172: approveButton.setAction(getApproveSelectionAction());
173:
174: cancelButton = new JButton();
175: cancelButton.setAction(getCancelSelectionAction());
176: cancelButton.setDefaultCapable(true);
177:
178: newFolderButton = new JButton();
179: newFolderButton.setAction(getNewFolderAction());
180:
181: buttonPanel = new JPanel(new GridBagLayout());
182:
183: GridBagConstraints gridBagConstraints = new GridBagConstraints();
184: gridBagConstraints.insets = new Insets(0, 0, 0, 25);
185: gridBagConstraints.anchor = GridBagConstraints.EAST;
186: gridBagConstraints.weightx = 1;
187: gridBagConstraints.gridy = 0;
188: gridBagConstraints.gridx = 0;
189: buttonPanel.add(Box.createHorizontalStrut(0),
190: gridBagConstraints);
191: buttonPanel.add(newFolderButton, gridBagConstraints);
192:
193: gridBagConstraints = new GridBagConstraints();
194: gridBagConstraints.insets = new Insets(0, 0, 0, 6);
195: gridBagConstraints.weightx = 0;
196: gridBagConstraints.gridy = 0;
197: gridBagConstraints.gridx = 1;
198: buttonPanel.add(approveButton, gridBagConstraints);
199:
200: gridBagConstraints = new GridBagConstraints();
201: gridBagConstraints.weightx = 0;
202: gridBagConstraints.gridy = 0;
203: gridBagConstraints.gridx = 2;
204: buttonPanel.add(cancelButton, gridBagConstraints);
205: chooser.add("South", buttonPanel);
206:
207: updateView(chooser);
208: }
209:
210: public Action getNewFolderAction() {
211: return newFolderAction;
212: }
213:
214: protected void installStrings(JFileChooser fc) {
215: super .installStrings(fc);
216:
217: saveButtonToolTipText = UIManager
218: .getString("DirectoryChooser.saveButtonToolTipText");
219: openButtonToolTipText = UIManager
220: .getString("DirectoryChooser.openButtonToolTipText");
221: cancelButtonToolTipText = UIManager
222: .getString("DirectoryChooser.cancelButtonToolTipText");
223:
224: newFolderText = UIManager
225: .getString("DirectoryChooser.newFolderButtonText");
226: newFolderToolTipText = UIManager
227: .getString("DirectoryChooser.newFolderButtonToolTipText");
228: }
229:
230: protected void uninstallStrings(JFileChooser fc) {
231: super .uninstallStrings(fc);
232:
233: newFolderText = null;
234: newFolderToolTipText = null;
235: }
236:
237: public void uninstallComponents(JFileChooser chooser) {
238: chooser.remove(treeScroll);
239: chooser.remove(buttonPanel);
240: }
241:
242: public FileView getFileView(JFileChooser fc) {
243: return fileView;
244: }
245:
246: protected void installListeners(JFileChooser fc) {
247: super .installListeners(fc);
248:
249: tree.addTreeSelectionListener(new SelectionListener());
250:
251: fc.getActionMap().put("refreshTree", new UpdateAction());
252: fc.getInputMap(JComponent.WHEN_ANCESTOR_OF_FOCUSED_COMPONENT)
253: .put(KeyStroke.getKeyStroke("F5"), "refreshTree");
254: }
255:
256: private class UpdateAction extends AbstractAction {
257: public void actionPerformed(ActionEvent e) {
258: JFileChooser fc = getFileChooser();
259: fc.rescanCurrentDirectory();
260: }
261: }
262:
263: protected void uninstallListeners(JFileChooser fc) {
264: super .uninstallListeners(fc);
265: }
266:
267: public PropertyChangeListener createPropertyChangeListener(
268: JFileChooser fc) {
269: return new ChangeListener();
270: }
271:
272: private void updateView(JFileChooser chooser) {
273: if (chooser.getApproveButtonText() != null) {
274: approveButton.setText(chooser.getApproveButtonText());
275: approveButton.setMnemonic(chooser
276: .getApproveButtonMnemonic());
277: } else {
278: if (JFileChooser.OPEN_DIALOG == chooser.getDialogType()) {
279: approveButton.setText(openButtonText);
280: approveButton.setToolTipText(openButtonToolTipText);
281: approveButton.setMnemonic(openButtonMnemonic);
282: } else {
283: approveButton.setText(saveButtonText);
284: approveButton.setToolTipText(saveButtonToolTipText);
285: approveButton.setMnemonic(saveButtonMnemonic);
286: }
287: }
288:
289: cancelButton.setText(cancelButtonText);
290: cancelButton.setMnemonic(cancelButtonMnemonic);
291:
292: newFolderButton.setText(newFolderText);
293: newFolderButton.setToolTipText(newFolderToolTipText);
294: newFolderButton.setVisible(((JDirectoryChooser) chooser)
295: .isShowingCreateDirectory());
296:
297: buttonPanel.setVisible(chooser.getControlButtonsAreShown());
298:
299: // ensure approve/cancel buttons have the same width
300: approveButton.setPreferredSize(null);
301: cancelButton.setPreferredSize(null);
302:
303: Dimension preferredSize = approveButton.getMinimumSize();
304: preferredSize = new Dimension(Math.max(preferredSize.width,
305: cancelButton.getPreferredSize().width),
306: preferredSize.height);
307: approveButton.setPreferredSize(preferredSize);
308: cancelButton.setPreferredSize(preferredSize);
309: }
310:
311: /**
312: * Ensures the file is visible, tree expanded and optionally
313: * selected
314: *
315: * @param fileToLocate
316: * @param selectFile
317: */
318: private void findFile(File fileToLocate, boolean selectFile,
319: boolean reload) {
320: if (fileToLocate == null || !fileToLocate.isDirectory()) {
321: return;
322: }
323:
324: // build the canonical path so we can navigate the tree model to
325: // find the
326: // node
327: File file = null;
328: try {
329: file = fileToLocate.getCanonicalFile();
330: } catch (Exception e) {
331: return;
332: }
333:
334: // temporarly disable loading nodes in the background
335: useNodeQueue = false;
336: TreePath pathToSelect;
337:
338: try {
339: // split the full path into individual files to locate them in
340: // the tree
341: // model
342: List files = new ArrayList();
343: files.add(file);
344: while ((file = chooser.getFileSystemView()
345: .getParentDirectory(file)) != null) {
346: files.add(0, file);
347: }
348:
349: List path = new ArrayList();
350:
351: // start from the root
352: DefaultMutableTreeNode node = (DefaultMutableTreeNode) tree
353: .getModel().getRoot();
354: path.add(node);
355:
356: DefaultMutableTreeNode current;
357:
358: boolean found = true;
359:
360: // ...and go through the tree model to find the files. Stop as
361: // soon as
362: // path is completely found or if one of the files in the path
363: // is not
364: // found.
365: while (files.size() > 0 && found) {
366: found = false;
367: for (int i = 0, c = node.getChildCount(); i < c; i++) {
368: current = (DefaultMutableTreeNode) node
369: .getChildAt(i);
370: File f = ((FileTreeNode) current).getFile();
371: if (files.get(0).equals(f)) {
372: path.add(current);
373: files.remove(0);
374: node = current;
375: found = true;
376: break;
377: }
378: }
379: }
380:
381: // select the path we found, it may be the file we were looking
382: // for or a
383: // subpath only
384: pathToSelect = new TreePath(path.toArray());
385: if (pathToSelect.getLastPathComponent() instanceof FileTreeNode
386: && reload) {
387: ((FileTreeNode) (pathToSelect.getLastPathComponent()))
388: .clear();
389: enqueueChildren((FileTreeNode) pathToSelect
390: .getLastPathComponent());
391: }
392: } finally {
393: // re-enable background loading
394: useNodeQueue = true;
395: }
396:
397: if (selectFile) {
398: tree.expandPath(pathToSelect);
399: tree.setSelectionPath(pathToSelect);
400: }
401:
402: tree.makeVisible(pathToSelect); //scrollPathToVisible(pathToSelect);
403: }
404:
405: private class ChangeListener implements PropertyChangeListener {
406:
407: public void propertyChange(PropertyChangeEvent evt) {
408: if (JFileChooser.APPROVE_BUTTON_TEXT_CHANGED_PROPERTY
409: .equals(evt.getPropertyName())) {
410: updateView(chooser);
411: }
412:
413: if (JFileChooser.MULTI_SELECTION_ENABLED_CHANGED_PROPERTY
414: .equals(evt.getPropertyName())) {
415: if (chooser.isMultiSelectionEnabled()) {
416: tree
417: .getSelectionModel()
418: .setSelectionMode(
419: TreeSelectionModel.DISCONTIGUOUS_TREE_SELECTION);
420: } else {
421: tree.getSelectionModel().setSelectionMode(
422: TreeSelectionModel.SINGLE_TREE_SELECTION);
423: }
424: }
425:
426: if (JFileChooser.DIRECTORY_CHANGED_PROPERTY.equals(evt
427: .getPropertyName())) {
428: findFile(chooser.getCurrentDirectory(), false, false);
429: }
430:
431: if (JFileChooser.ACCESSORY_CHANGED_PROPERTY.equals(evt
432: .getPropertyName())) {
433: Component oldValue = (Component) evt.getOldValue();
434: Component newValue = (Component) evt.getNewValue();
435: if (oldValue != null) {
436: chooser.remove(oldValue);
437: }
438: if (newValue != null) {
439: chooser.add("North", newValue);
440: }
441: chooser.revalidate();
442: chooser.repaint();
443: }
444:
445: if (JFileChooser.CONTROL_BUTTONS_ARE_SHOWN_CHANGED_PROPERTY
446: .equals(evt.getPropertyName())) {
447: updateView(chooser);
448: }
449:
450: if (JDirectoryChooser.SHOWING_CREATE_DIRECTORY_CHANGED_KEY
451: .equals(evt.getPropertyName())) {
452: updateView(chooser);
453: }
454: }
455: }
456:
457: private class SelectionListener implements TreeSelectionListener {
458:
459: public void valueChanged(TreeSelectionEvent e) {
460: getApproveSelectionAction().setEnabled(
461: tree.getSelectionCount() > 0);
462: setSelectedFiles();
463:
464: // the current directory is the one currently selected
465: TreePath currentDirectoryPath = tree.getSelectionPath();
466: if (currentDirectoryPath != null) {
467: File currentDirectory = ((FileTreeNode) currentDirectoryPath
468: .getLastPathComponent()).getFile();
469: chooser.setCurrentDirectory(currentDirectory);
470: }
471: }
472: }
473:
474: public Action getApproveSelectionAction() {
475: return approveSelectionAction;
476: }
477:
478: private void setSelectedFiles() {
479: TreePath[] selectedPaths = tree.getSelectionPaths();
480: if (selectedPaths == null || selectedPaths.length == 0) {
481: chooser.setSelectedFile(null);
482: return;
483: }
484:
485: List files = new ArrayList();
486: for (int i = 0, c = selectedPaths.length; i < c; i++) {
487: LazyMutableTreeNode node = (LazyMutableTreeNode) selectedPaths[i]
488: .getLastPathComponent();
489: if (node instanceof FileTreeNode) {
490: File f = ((FileTreeNode) node).getFile();
491: files.add(f);
492: }
493: }
494:
495: chooser.setSelectedFiles((File[]) files.toArray(new File[0]));
496: }
497:
498: private class ApproveSelectionAction extends AbstractAction {
499:
500: public ApproveSelectionAction() {
501: setEnabled(false);
502: }
503:
504: public void actionPerformed(ActionEvent e) {
505: setSelectedFiles();
506: chooser.approveSelection();
507: }
508: }
509:
510: /**
511: * Listens on nodes being opened and preload their children to get
512: * better UI experience (GUI should be more responsive and empty
513: * nodes discovered automatically).
514: */
515: private class TreeExpansion implements TreeExpansionListener {
516:
517: public void treeCollapsed(TreeExpansionEvent event) {
518: }
519:
520: public void treeExpanded(TreeExpansionEvent event) {
521: // ensure children gets expanded later
522: if (event.getPath() != null) {
523: Object lastElement = event.getPath()
524: .getLastPathComponent();
525: if (lastElement instanceof FileTreeNode && useNodeQueue) {
526: if (((FileTreeNode) lastElement).isLoaded()) {
527: enqueueChildren((FileTreeNode) lastElement);
528: }
529: }
530: }
531: }
532: }
533:
534: private void enqueueChildren(FileTreeNode node) {
535: for (Enumeration e = node.children(); e.hasMoreElements();) {
536: addToQueue((FileTreeNode) e.nextElement(), tree);
537: }
538: }
539:
540: private class FileSystemTreeRenderer extends
541: DefaultTreeCellRenderer {
542:
543: public Component getTreeCellRendererComponent(JTree tree,
544: Object value, boolean sel, boolean expanded,
545: boolean leaf, int row, boolean hasFocus) {
546: super .getTreeCellRendererComponent(tree, value, sel,
547: expanded, false,
548: // even "leaf" folders should look like other folders
549: row, hasFocus);
550:
551: if (value instanceof FileTreeNode) {
552: FileTreeNode node = (FileTreeNode) value;
553: setText(getFileView(chooser).getName(node.getFile()));
554: // @PMD:REVIEWED:EmptyIfStmt: by fred on 14/08/04 17:25
555: if (OS.isMacOSX()
556: && UIManager.getLookAndFeel()
557: .isNativeLookAndFeel()) {
558: // do not set icon for MacOSX when native look is used, it
559: // seems the
560: // Tree.icons set by the
561: // look and feel are not that good or Apple is doing
562: // something I
563: // can't figure.
564: // setIcon only if not running in MacOSX
565: } else {
566: setIcon(getFileView(chooser)
567: .getIcon(node.getFile()));
568: }
569: }
570:
571: return this ;
572: }
573: }
574:
575: private class NewFolderAction extends AbstractAction {
576: public void actionPerformed(ActionEvent e) {
577: // get the currently selected folder
578: JFileChooser fc = getFileChooser();
579: File currentDirectory = fc.getCurrentDirectory();
580:
581: if (!currentDirectory.canWrite()) {
582: JOptionPane
583: .showMessageDialog(
584: fc,
585: UIManager
586: .getString("DirectoryChooser.cantCreateFolderHere"),
587: UIManager
588: .getString("DirectoryChooser.cantCreateFolderHere.title"),
589: JOptionPane.ERROR_MESSAGE);
590: return;
591: }
592:
593: String newFolderName = JOptionPane
594: .showInputDialog(
595: fc,
596: UIManager
597: .getString("DirectoryChooser.enterFolderName"),
598: newFolderText, JOptionPane.QUESTION_MESSAGE);
599: if (newFolderName != null) {
600: File newFolder = new File(currentDirectory,
601: newFolderName);
602: if (newFolder.mkdir()) {
603: if (fc.isMultiSelectionEnabled()) {
604: fc.setSelectedFiles(new File[] { newFolder });
605: } else {
606: fc.setSelectedFile(newFolder);
607: }
608: fc.rescanCurrentDirectory();
609: } else {
610: JOptionPane
611: .showMessageDialog(
612: fc,
613: UIManager
614: .getString("DirectoryChooser.createFolderFailed"),
615: UIManager
616: .getString("DirectoryChooser.createFolderFailed.title"),
617: JOptionPane.ERROR_MESSAGE);
618: }
619: }
620: }
621: }
622:
623: private class FileSystemTreeModel extends DefaultTreeModel {
624:
625: public FileSystemTreeModel(FileSystemView fsv) {
626: super (new MyComputerTreeNode(fsv), false);
627: }
628: }
629:
630: private class MyComputerTreeNode extends LazyMutableTreeNode {
631:
632: public MyComputerTreeNode(FileSystemView fsv) {
633: super (fsv);
634: }
635:
636: protected void loadChildren() {
637: FileSystemView fsv = (FileSystemView) getUserObject();
638: File[] roots = fsv.getRoots();
639: if (roots != null) {
640: Arrays.sort(roots);
641: for (int i = 0, c = roots.length; i < c; i++) {
642: add(new FileTreeNode(roots[i]));
643: }
644: }
645: }
646:
647: public String toString() {
648: return "/";
649: }
650: }
651:
652: private class FileTreeNode extends LazyMutableTreeNode implements
653: Comparable {
654:
655: public FileTreeNode(File file) {
656: super (file);
657: }
658:
659: public boolean canEnqueue() {
660: return !isLoaded()
661: && !chooser.getFileSystemView().isFloppyDrive(
662: getFile())
663: && !chooser.getFileSystemView().isFileSystemRoot(
664: getFile());
665: }
666:
667: public boolean isLeaf() {
668: if (!isLoaded()) {
669: return false;
670: } else {
671: return super .isLeaf();
672: }
673: }
674:
675: protected void loadChildren() {
676: FileTreeNode[] nodes = getChildren();
677: for (int i = 0, c = nodes.length; i < c; i++) {
678: add(nodes[i]);
679: }
680: }
681:
682: private FileTreeNode[] getChildren() {
683: File[] files = chooser.getFileSystemView().getFiles(
684: getFile(), chooser.isFileHidingEnabled());
685: ArrayList nodes = new ArrayList();
686: // keep only directories, no "file" in the tree.
687: if (files != null) {
688: for (int i = 0, c = files.length; i < c; i++) {
689: if (files[i].isDirectory()) {
690: nodes.add(new FileTreeNode(files[i]));
691: }
692: }
693: }
694: // sort directories, FileTreeNode implements Comparable
695: FileTreeNode[] result = (FileTreeNode[]) nodes
696: .toArray(new FileTreeNode[0]);
697: Arrays.sort(result);
698: return result;
699: }
700:
701: public File getFile() {
702: return (File) getUserObject();
703: }
704:
705: public String toString() {
706: return chooser.getFileSystemView().getSystemDisplayName(
707: (File) getUserObject());
708: }
709:
710: public int compareTo(Object o) {
711: if (!(o instanceof FileTreeNode)) {
712: return 1;
713: }
714: return getFile().compareTo(((FileTreeNode) o).getFile());
715: }
716:
717: public void clear() {
718: super .clear();
719: ((DefaultTreeModel) tree.getModel())
720: .nodeStructureChanged(this );
721: }
722: }
723:
724: /**
725: * From WindowsFileChooserUI
726: */
727: protected class WindowsFileView extends BasicFileView {
728:
729: public Icon getIcon(File f) {
730: Icon icon = getCachedIcon(f);
731: if (icon != null) {
732: return icon;
733: }
734: if (f != null) {
735: icon = getFileChooser().getFileSystemView()
736: .getSystemIcon(f);
737: }
738: if (icon == null) {
739: icon = super .getIcon(f);
740: }
741: cacheIcon(f, icon);
742: return icon;
743: }
744: }
745:
746: private static synchronized void addToQueue(FileTreeNode node,
747: JTree tree) {
748: if (nodeQueue == null || !nodeQueue.isAlive()) {
749: nodeQueue = new Queue();
750: nodeQueue.start();
751: }
752: if (node.canEnqueue()) {
753: nodeQueue.add(node, tree);
754: }
755: }
756:
757: /**
758: * This queue takes care of loading nodes in the background.
759: */
760: private static final class Queue extends Thread {
761:
762: private volatile Stack nodes = new Stack();
763:
764: private Object lock = new Object();
765:
766: private volatile boolean running = true;
767:
768: public Queue() {
769: super ("DirectoryChooser-BackgroundLoader");
770: setDaemon(true);
771: }
772:
773: public void add(WindowsDirectoryChooserUI.FileTreeNode node,
774: JTree tree) {
775: if (!isAlive()) {
776: throw new IllegalArgumentException(
777: "Queue is no longer alive");
778: }
779:
780: synchronized (lock) {
781: if (running) {
782: nodes.addElement(new QueueItem(node, tree));
783: lock.notifyAll();
784: }
785: }
786: }
787:
788: public void run() {
789: while (running) {
790: while (nodes.size() > 0) {
791: final QueueItem item = (QueueItem) nodes.pop();
792: final WindowsDirectoryChooserUI.FileTreeNode node = item.node;
793: final JTree tree = item.tree;
794:
795: // ask how many items we got
796: node.getChildCount();
797:
798: Runnable runnable = new Runnable() {
799:
800: public void run() {
801: ((DefaultTreeModel) tree.getModel())
802: .nodeChanged(node);
803: tree.repaint();
804: }
805: };
806: try {
807: SwingUtilities.invokeAndWait(runnable);
808: } catch (InterruptedException e) {
809: e.printStackTrace();
810: } catch (InvocationTargetException e) {
811: e.printStackTrace();
812: }
813: }
814:
815: // wait for 5 seconds for someone to use the queue, else just
816: // ends this
817: // queue
818: try {
819: synchronized (lock) {
820: lock.wait(5000);
821: }
822:
823: if (nodes.size() == 0) {
824: running = false;
825: }
826:
827: } catch (InterruptedException e) {
828: e.printStackTrace();
829: }
830: }
831: }
832: }
833:
834: /**
835: * An entry in the queue.
836: */
837: private static final class QueueItem {
838: WindowsDirectoryChooserUI.FileTreeNode node;
839: JTree tree;
840:
841: public QueueItem(WindowsDirectoryChooserUI.FileTreeNode node,
842: JTree tree) {
843: this.node = node;
844: this.tree = tree;
845: }
846: }
847:
848: }
|