26 package javax.swing.plaf.basic;
27
28 import java.io.File;
29 import java.util.*;
30 import javax.swing.*;
31 import javax.swing.filechooser.*;
32 import javax.swing.event.*;
33 import java.beans.*;
34
35 import sun.awt.shell.ShellFolder;
36
37 /**
38 * Basic implementation of a file list.
39 *
40 * @author Jeff Dinkins
41 */
42 public class BasicDirectoryModel extends AbstractListModel implements PropertyChangeListener {
43
44 private JFileChooser filechooser = null;
45 // PENDING(jeff) pick the size more sensibly
46 private Vector fileCache = new Vector(50);
47 private LoadFilesThread loadThread = null;
48 private Vector files = null;
49 private Vector directories = null;
50 private int fetchID = 0;
51
52 private PropertyChangeSupport changeSupport;
53
54 private boolean busy = false;
55
56 public BasicDirectoryModel(JFileChooser filechooser) {
57 this.filechooser = filechooser;
58 validateFileCache();
59 }
60
61 public void propertyChange(PropertyChangeEvent e) {
62 String prop = e.getPropertyName();
63 if(prop == JFileChooser.DIRECTORY_CHANGED_PROPERTY ||
64 prop == JFileChooser.FILE_VIEW_CHANGED_PROPERTY ||
65 prop == JFileChooser.FILE_FILTER_CHANGED_PROPERTY ||
66 prop == JFileChooser.FILE_HIDING_CHANGED_PROPERTY ||
67 prop == JFileChooser.FILE_SELECTION_MODE_CHANGED_PROPERTY) {
68 validateFileCache();
69 } else if ("UI".equals(prop)) {
89 loadThread.cancelRunnables();
90 loadThread = null;
91 }
92 }
93
94 public Vector<File> getDirectories() {
95 synchronized(fileCache) {
96 if (directories != null) {
97 return directories;
98 }
99 Vector fls = getFiles();
100 return directories;
101 }
102 }
103
104 public Vector<File> getFiles() {
105 synchronized(fileCache) {
106 if (files != null) {
107 return files;
108 }
109 files = new Vector();
110 directories = new Vector();
111 directories.addElement(filechooser.getFileSystemView().createFileObject(
112 filechooser.getCurrentDirectory(), "..")
113 );
114
115 for (int i = 0; i < getSize(); i++) {
116 File f = (File)fileCache.get(i);
117 if (filechooser.isTraversable(f)) {
118 directories.add(f);
119 } else {
120 files.add(f);
121 }
122 }
123 return files;
124 }
125 }
126
127 public void validateFileCache() {
128 File currentDirectory = filechooser.getCurrentDirectory();
129 if (currentDirectory == null) {
130 return;
131 }
132 if (loadThread != null) {
133 loadThread.interrupt();
134 loadThread.cancelRunnables();
135 }
136
198 protected void sort(Vector<? extends File> v){
199 ShellFolder.sortFiles(v);
200 }
201
202 // Obsolete - not used
203 protected boolean lt(File a, File b) {
204 // First ignore case when comparing
205 int diff = a.getName().toLowerCase().compareTo(b.getName().toLowerCase());
206 if (diff != 0) {
207 return diff < 0;
208 } else {
209 // May differ in case (e.g. "mail" vs. "Mail")
210 return a.getName().compareTo(b.getName()) < 0;
211 }
212 }
213
214
215 class LoadFilesThread extends Thread {
216 File currentDirectory = null;
217 int fid;
218 Vector runnables = new Vector(10);
219
220 public LoadFilesThread(File currentDirectory, int fid) {
221 super("Basic L&F File Loading Thread");
222 this.currentDirectory = currentDirectory;
223 this.fid = fid;
224 }
225
226 private void invokeLater(Runnable runnable) {
227 runnables.addElement(runnable);
228 SwingUtilities.invokeLater(runnable);
229 }
230
231 public void run() {
232 run0();
233 setBusy(false, fid);
234 }
235
236 public void run0() {
237 try {
238 FileSystemView fileSystem = filechooser.getFileSystemView();
239
240 File[] list = fileSystem.getFiles(currentDirectory, filechooser.isFileHidingEnabled());
241
242 Vector<File> acceptsList = new Vector<File>();
243
244 if (isInterrupted()) {
245 return;
246 }
247
248 // run through the file list, add directories and selectable files to fileCache
249 for (int i = 0; i < list.length; i++) {
250 if(filechooser.accept(list[i])) {
251 acceptsList.addElement(list[i]);
252 }
253 }
254
255 if (isInterrupted()) {
256 return;
257 }
258
259 // First sort alphabetically by filename
260 sort(acceptsList);
261
262 Vector newDirectories = new Vector(50);
263 Vector newFiles = new Vector();
264 // run through list grabbing directories in chunks of ten
265 for(int i = 0; i < acceptsList.size(); i++) {
266 File f = (File) acceptsList.elementAt(i);
267 boolean isTraversable = filechooser.isTraversable(f);
268 if (isTraversable) {
269 newDirectories.addElement(f);
270 } else if (!isTraversable && filechooser.isFileSelectionEnabled()) {
271 newFiles.addElement(f);
272 }
273 if(isInterrupted()) {
274 return;
275 }
276 }
277
278 Vector newFileCache = new Vector(newDirectories);
279 newFileCache.addAll(newFiles);
280
281 int newSize = newFileCache.size();
282 int oldSize = fileCache.size();
283
284 if (newSize > oldSize) {
285 //see if interval is added
286 int start = oldSize;
287 int end = newSize;
288 for (int i = 0; i < oldSize; i++) {
289 if (!newFileCache.get(i).equals(fileCache.get(i))) {
290 start = i;
291 for (int j = i; j < newSize; j++) {
292 if (newFileCache.get(j).equals(fileCache.get(i))) {
293 end = j;
294 break;
295 }
296 }
297 break;
298 }
304 }
305 invokeLater(new DoChangeContents(newFileCache.subList(start, end), start, null, 0, fid));
306 newFileCache = null;
307 }
308 } else if (newSize < oldSize) {
309 //see if interval is removed
310 int start = -1;
311 int end = -1;
312 for (int i = 0; i < newSize; i++) {
313 if (!newFileCache.get(i).equals(fileCache.get(i))) {
314 start = i;
315 end = i + oldSize - newSize;
316 break;
317 }
318 }
319 if (start >= 0 && end > start
320 && fileCache.subList(end, oldSize).equals(newFileCache.subList(start, newSize))) {
321 if(isInterrupted()) {
322 return;
323 }
324 invokeLater(new DoChangeContents(null, 0, new Vector(fileCache.subList(start, end)),
325 start, fid));
326 newFileCache = null;
327 }
328 }
329 if (newFileCache != null && !fileCache.equals(newFileCache)) {
330 if (isInterrupted()) {
331 cancelRunnables(runnables);
332 }
333 invokeLater(new DoChangeContents(newFileCache, 0, fileCache, 0, fid));
334 }
335 } catch (RuntimeException e) {
336 if (!(e.getCause() instanceof InterruptedException /* just exit on interruption */)) {
337 throw e;
338 }
339 }
340 }
341
342
343 public void cancelRunnables(Vector runnables) {
344 for(int i = 0; i < runnables.size(); i++) {
345 ((DoChangeContents)runnables.elementAt(i)).cancel();
346 }
347 }
348
349 public void cancelRunnables() {
350 cancelRunnables(runnables);
351 }
352 }
353
354
355 /**
356 * Adds a PropertyChangeListener to the listener list. The listener is
357 * registered for all bound properties of this class.
358 * <p>
359 * If <code>listener</code> is <code>null</code>,
360 * no exception is thrown and no action is performed.
361 *
362 * @param listener the property change listener to be added
363 *
364 * @see #removePropertyChangeListener
365 * @see #getPropertyChangeListeners
438 * busy when it is running a separate (interruptable)
439 * thread in order to load the contents of a directory.
440 */
441 private synchronized void setBusy(final boolean busy, int fid) {
442 if (fid == fetchID) {
443 boolean oldValue = this.busy;
444 this.busy = busy;
445
446 if (changeSupport != null && busy != oldValue) {
447 SwingUtilities.invokeLater(new Runnable() {
448 public void run() {
449 firePropertyChange("busy", !busy, busy);
450 }
451 });
452 }
453 }
454 }
455
456
457 class DoChangeContents implements Runnable {
458 private List addFiles;
459 private List remFiles;
460 private boolean doFire = true;
461 private int fid;
462 private int addStart = 0;
463 private int remStart = 0;
464 private int change;
465
466 public DoChangeContents(List addFiles, int addStart, List remFiles, int remStart, int fid) {
467 this.addFiles = addFiles;
468 this.addStart = addStart;
469 this.remFiles = remFiles;
470 this.remStart = remStart;
471 this.fid = fid;
472 }
473
474 synchronized void cancel() {
475 doFire = false;
476 }
477
478 public synchronized void run() {
479 if (fetchID == fid && doFire) {
480 int remSize = (remFiles == null) ? 0 : remFiles.size();
481 int addSize = (addFiles == null) ? 0 : addFiles.size();
482 synchronized(fileCache) {
483 if (remSize > 0) {
484 fileCache.removeAll(remFiles);
485 }
486 if (addSize > 0) {
|
26 package javax.swing.plaf.basic;
27
28 import java.io.File;
29 import java.util.*;
30 import javax.swing.*;
31 import javax.swing.filechooser.*;
32 import javax.swing.event.*;
33 import java.beans.*;
34
35 import sun.awt.shell.ShellFolder;
36
37 /**
38 * Basic implementation of a file list.
39 *
40 * @author Jeff Dinkins
41 */
42 public class BasicDirectoryModel extends AbstractListModel implements PropertyChangeListener {
43
44 private JFileChooser filechooser = null;
45 // PENDING(jeff) pick the size more sensibly
46 private Vector<File> fileCache = new Vector<File>(50);
47 private LoadFilesThread loadThread = null;
48 private Vector<File> files = null;
49 private Vector<File> directories = null;
50 private int fetchID = 0;
51
52 private PropertyChangeSupport changeSupport;
53
54 private boolean busy = false;
55
56 public BasicDirectoryModel(JFileChooser filechooser) {
57 this.filechooser = filechooser;
58 validateFileCache();
59 }
60
61 public void propertyChange(PropertyChangeEvent e) {
62 String prop = e.getPropertyName();
63 if(prop == JFileChooser.DIRECTORY_CHANGED_PROPERTY ||
64 prop == JFileChooser.FILE_VIEW_CHANGED_PROPERTY ||
65 prop == JFileChooser.FILE_FILTER_CHANGED_PROPERTY ||
66 prop == JFileChooser.FILE_HIDING_CHANGED_PROPERTY ||
67 prop == JFileChooser.FILE_SELECTION_MODE_CHANGED_PROPERTY) {
68 validateFileCache();
69 } else if ("UI".equals(prop)) {
89 loadThread.cancelRunnables();
90 loadThread = null;
91 }
92 }
93
94 public Vector<File> getDirectories() {
95 synchronized(fileCache) {
96 if (directories != null) {
97 return directories;
98 }
99 Vector fls = getFiles();
100 return directories;
101 }
102 }
103
104 public Vector<File> getFiles() {
105 synchronized(fileCache) {
106 if (files != null) {
107 return files;
108 }
109 files = new Vector<File>();
110 directories = new Vector<File>();
111 directories.addElement(filechooser.getFileSystemView().createFileObject(
112 filechooser.getCurrentDirectory(), "..")
113 );
114
115 for (int i = 0; i < getSize(); i++) {
116 File f = fileCache.get(i);
117 if (filechooser.isTraversable(f)) {
118 directories.add(f);
119 } else {
120 files.add(f);
121 }
122 }
123 return files;
124 }
125 }
126
127 public void validateFileCache() {
128 File currentDirectory = filechooser.getCurrentDirectory();
129 if (currentDirectory == null) {
130 return;
131 }
132 if (loadThread != null) {
133 loadThread.interrupt();
134 loadThread.cancelRunnables();
135 }
136
198 protected void sort(Vector<? extends File> v){
199 ShellFolder.sortFiles(v);
200 }
201
202 // Obsolete - not used
203 protected boolean lt(File a, File b) {
204 // First ignore case when comparing
205 int diff = a.getName().toLowerCase().compareTo(b.getName().toLowerCase());
206 if (diff != 0) {
207 return diff < 0;
208 } else {
209 // May differ in case (e.g. "mail" vs. "Mail")
210 return a.getName().compareTo(b.getName()) < 0;
211 }
212 }
213
214
215 class LoadFilesThread extends Thread {
216 File currentDirectory = null;
217 int fid;
218 Vector<DoChangeContents> runnables = new Vector<DoChangeContents>(10);
219
220 public LoadFilesThread(File currentDirectory, int fid) {
221 super("Basic L&F File Loading Thread");
222 this.currentDirectory = currentDirectory;
223 this.fid = fid;
224 }
225
226 private void invokeLater(DoChangeContents runnable) {
227 runnables.addElement(runnable);
228 SwingUtilities.invokeLater(runnable);
229 }
230
231 public void run() {
232 run0();
233 setBusy(false, fid);
234 }
235
236 public void run0() {
237 try {
238 FileSystemView fileSystem = filechooser.getFileSystemView();
239
240 File[] list = fileSystem.getFiles(currentDirectory, filechooser.isFileHidingEnabled());
241
242 Vector<File> acceptsList = new Vector<File>();
243
244 if (isInterrupted()) {
245 return;
246 }
247
248 // run through the file list, add directories and selectable files to fileCache
249 for (File file : list) {
250 if (filechooser.accept(file)) {
251 acceptsList.addElement(file);
252 }
253 }
254
255 if (isInterrupted()) {
256 return;
257 }
258
259 // First sort alphabetically by filename
260 sort(acceptsList);
261
262 Vector<File> newDirectories = new Vector<File>(50);
263 Vector<File> newFiles = new Vector<File>();
264 // run through list grabbing directories in chunks of ten
265 for(int i = 0; i < acceptsList.size(); i++) {
266 File f = acceptsList.elementAt(i);
267 boolean isTraversable = filechooser.isTraversable(f);
268 if (isTraversable) {
269 newDirectories.addElement(f);
270 } else if (!isTraversable && filechooser.isFileSelectionEnabled()) {
271 newFiles.addElement(f);
272 }
273 if(isInterrupted()) {
274 return;
275 }
276 }
277
278 Vector<File> newFileCache = new Vector<File>(newDirectories);
279 newFileCache.addAll(newFiles);
280
281 int newSize = newFileCache.size();
282 int oldSize = fileCache.size();
283
284 if (newSize > oldSize) {
285 //see if interval is added
286 int start = oldSize;
287 int end = newSize;
288 for (int i = 0; i < oldSize; i++) {
289 if (!newFileCache.get(i).equals(fileCache.get(i))) {
290 start = i;
291 for (int j = i; j < newSize; j++) {
292 if (newFileCache.get(j).equals(fileCache.get(i))) {
293 end = j;
294 break;
295 }
296 }
297 break;
298 }
304 }
305 invokeLater(new DoChangeContents(newFileCache.subList(start, end), start, null, 0, fid));
306 newFileCache = null;
307 }
308 } else if (newSize < oldSize) {
309 //see if interval is removed
310 int start = -1;
311 int end = -1;
312 for (int i = 0; i < newSize; i++) {
313 if (!newFileCache.get(i).equals(fileCache.get(i))) {
314 start = i;
315 end = i + oldSize - newSize;
316 break;
317 }
318 }
319 if (start >= 0 && end > start
320 && fileCache.subList(end, oldSize).equals(newFileCache.subList(start, newSize))) {
321 if(isInterrupted()) {
322 return;
323 }
324 invokeLater(new DoChangeContents(null, 0, new Vector<File>(fileCache.subList(start, end)),
325 start, fid));
326 newFileCache = null;
327 }
328 }
329 if (newFileCache != null && !fileCache.equals(newFileCache)) {
330 if (isInterrupted()) {
331 cancelRunnables(runnables);
332 }
333 invokeLater(new DoChangeContents(newFileCache, 0, fileCache, 0, fid));
334 }
335 } catch (RuntimeException e) {
336 if (!(e.getCause() instanceof InterruptedException /* just exit on interruption */)) {
337 throw e;
338 }
339 }
340 }
341
342
343 public void cancelRunnables(Vector<DoChangeContents> runnables) {
344 for (DoChangeContents runnable : runnables) {
345 runnable.cancel();
346 }
347 }
348
349 public void cancelRunnables() {
350 cancelRunnables(runnables);
351 }
352 }
353
354
355 /**
356 * Adds a PropertyChangeListener to the listener list. The listener is
357 * registered for all bound properties of this class.
358 * <p>
359 * If <code>listener</code> is <code>null</code>,
360 * no exception is thrown and no action is performed.
361 *
362 * @param listener the property change listener to be added
363 *
364 * @see #removePropertyChangeListener
365 * @see #getPropertyChangeListeners
438 * busy when it is running a separate (interruptable)
439 * thread in order to load the contents of a directory.
440 */
441 private synchronized void setBusy(final boolean busy, int fid) {
442 if (fid == fetchID) {
443 boolean oldValue = this.busy;
444 this.busy = busy;
445
446 if (changeSupport != null && busy != oldValue) {
447 SwingUtilities.invokeLater(new Runnable() {
448 public void run() {
449 firePropertyChange("busy", !busy, busy);
450 }
451 });
452 }
453 }
454 }
455
456
457 class DoChangeContents implements Runnable {
458 private List<File> addFiles;
459 private List<File> remFiles;
460 private boolean doFire = true;
461 private int fid;
462 private int addStart = 0;
463 private int remStart = 0;
464
465 public DoChangeContents(List<File> addFiles, int addStart, List<File> remFiles, int remStart, int fid) {
466 this.addFiles = addFiles;
467 this.addStart = addStart;
468 this.remFiles = remFiles;
469 this.remStart = remStart;
470 this.fid = fid;
471 }
472
473 synchronized void cancel() {
474 doFire = false;
475 }
476
477 public synchronized void run() {
478 if (fetchID == fid && doFire) {
479 int remSize = (remFiles == null) ? 0 : remFiles.size();
480 int addSize = (addFiles == null) ? 0 : addFiles.size();
481 synchronized(fileCache) {
482 if (remSize > 0) {
483 fileCache.removeAll(remFiles);
484 }
485 if (addSize > 0) {
|