History

My first initial commit with all of mine experimental changes.

Dmitry Kabak (userdima committed 13 years ago

Commit d37b88d1 parent 3f5b46618352 files · +2598093 −-8690Browse files

Showing the changes to java/pkg/javax/swing/plaf/basic/BasicDirectoryModel.java (1 of 18352 files). Show all

Unified · Split

java/pkg/javax/swing/plaf/basic/BasicDirectoryModel.java+497−0
@@ -0,0 +1,497 @@
1/*
2 * Copyright (c) 1998, 2009, Oracle and/or its affiliates. All rights reserved.
3 * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER.
4 *
5 * This code is free software; you can redistribute it and/or modify it
6 * under the terms of the GNU General Public License version 2 only, as
7 * published by the Free Software Foundation. Oracle designates this
8 * particular file as subject to the "Classpath" exception as provided
9 * by Oracle in the LICENSE file that accompanied this code.
10 *
11 * This code is distributed in the hope that it will be useful, but WITHOUT
12 * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
13 * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License
14 * version 2 for more details (a copy is included in the LICENSE file that
15 * accompanied this code).
16 *
17 * You should have received a copy of the GNU General Public License version
18 * 2 along with this work; if not, write to the Free Software Foundation,
19 * Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA.
20 *
21 * Please contact Oracle, 500 Oracle Parkway, Redwood Shores, CA 94065 USA
22 * or visit www.oracle.com if you need additional information or have any
23 * questions.
24 */
25
26package javax.swing.plaf.basic;
27
28import java.io.File;
29import java.util.*;
30import java.util.concurrent.Callable;
31import javax.swing.*;
32import javax.swing.filechooser.*;
33import javax.swing.event.*;
34import java.beans.*;
35
36import sun.awt.shell.ShellFolder;
37
38/**
39 * Basic implementation of a file list.
40 *
41 * @author Jeff Dinkins
42 */
43public class BasicDirectoryModel extends AbstractListModel<Object> implements PropertyChangeListener {
44
45 private JFileChooser filechooser = null;
46 // PENDING(jeff) pick the size more sensibly
47 private Vector<File> fileCache = new Vector<File>(50);
48 private LoadFilesThread loadThread = null;
49 private Vector<File> files = null;
50 private Vector<File> directories = null;
51 private int fetchID = 0;
52
53 private PropertyChangeSupport changeSupport;
54
55 private boolean busy = false;
56
57 public BasicDirectoryModel(JFileChooser filechooser) {
58 this.filechooser = filechooser;
59 validateFileCache();
60 }
61
62 public void propertyChange(PropertyChangeEvent e) {
63 String prop = e.getPropertyName();
64 if(prop == JFileChooser.DIRECTORY_CHANGED_PROPERTY ||
65 prop == JFileChooser.FILE_VIEW_CHANGED_PROPERTY ||
66 prop == JFileChooser.FILE_FILTER_CHANGED_PROPERTY ||
67 prop == JFileChooser.FILE_HIDING_CHANGED_PROPERTY ||
68 prop == JFileChooser.FILE_SELECTION_MODE_CHANGED_PROPERTY) {
69 validateFileCache();
70 } else if ("UI".equals(prop)) {
71 Object old = e.getOldValue();
72 if (old instanceof BasicFileChooserUI) {
73 BasicFileChooserUI ui = (BasicFileChooserUI) old;
74 BasicDirectoryModel model = ui.getModel();
75 if (model != null) {
76 model.invalidateFileCache();
77 }
78 }
79 } else if ("JFileChooserDialogIsClosingProperty".equals(prop)) {
80 invalidateFileCache();
81 }
82 }
83
84 /**
85 * This method is used to interrupt file loading thread.
86 */
87 public void invalidateFileCache() {
88 if (loadThread != null) {
89 loadThread.interrupt();
90 loadThread.cancelRunnables();
91 loadThread = null;
92 }
93 }
94
95 public Vector<File> getDirectories() {
96 synchronized(fileCache) {
97 if (directories != null) {
98 return directories;
99 }
100 Vector fls = getFiles();
101 return directories;
102 }
103 }
104
105 public Vector<File> getFiles() {
106 synchronized(fileCache) {
107 if (files != null) {
108 return files;
109 }
110 files = new Vector<File>();
111 directories = new Vector<File>();
112 directories.addElement(filechooser.getFileSystemView().createFileObject(
113 filechooser.getCurrentDirectory(), "..")
114 );
115
116 for (int i = 0; i < getSize(); i++) {
117 File f = fileCache.get(i);
118 if (filechooser.isTraversable(f)) {
119 directories.add(f);
120 } else {
121 files.add(f);
122 }
123 }
124 return files;
125 }
126 }
127
128 public void validateFileCache() {
129 File currentDirectory = filechooser.getCurrentDirectory();
130 if (currentDirectory == null) {
131 return;
132 }
133 if (loadThread != null) {
134 loadThread.interrupt();
135 loadThread.cancelRunnables();
136 }
137
138 setBusy(true, ++fetchID);
139
140 loadThread = new LoadFilesThread(currentDirectory, fetchID);
141 loadThread.start();
142 }
143
144 /**
145 * Renames a file in the underlying file system.
146 *
147 * @param oldFile a <code>File</code> object representing
148 * the existing file
149 * @param newFile a <code>File</code> object representing
150 * the desired new file name
151 * @return <code>true</code> if rename succeeded,
152 * otherwise <code>false</code>
153 * @since 1.4
154 */
155 public boolean renameFile(File oldFile, File newFile) {
156 synchronized(fileCache) {
157 if (oldFile.renameTo(newFile)) {
158 validateFileCache();
159 return true;
160 }
161 return false;
162 }
163 }
164
165
166 public void fireContentsChanged() {
167 // System.out.println("BasicDirectoryModel: firecontentschanged");
168 fireContentsChanged(this, 0, getSize()-1);
169 }
170
171 public int getSize() {
172 return fileCache.size();
173 }
174
175 public boolean contains(Object o) {
176 return fileCache.contains(o);
177 }
178
179 public int indexOf(Object o) {
180 return fileCache.indexOf(o);
181 }
182
183 public Object getElementAt(int index) {
184 return fileCache.get(index);
185 }
186
187 /**
188 * Obsolete - not used.
189 */
190 public void intervalAdded(ListDataEvent e) {
191 }
192
193 /**
194 * Obsolete - not used.
195 */
196 public void intervalRemoved(ListDataEvent e) {
197 }
198
199 protected void sort(Vector<? extends File> v){
200 ShellFolder.sort(v);
201 }
202
203 // Obsolete - not used
204 protected boolean lt(File a, File b) {
205 // First ignore case when comparing
206 int diff = a.getName().toLowerCase().compareTo(b.getName().toLowerCase());
207 if (diff != 0) {
208 return diff < 0;
209 } else {
210 // May differ in case (e.g. "mail" vs. "Mail")
211 return a.getName().compareTo(b.getName()) < 0;
212 }
213 }
214
215
216 class LoadFilesThread extends Thread {
217 File currentDirectory = null;
218 int fid;
219 Vector<DoChangeContents> runnables = new Vector<DoChangeContents>(10);
220
221 public LoadFilesThread(File currentDirectory, int fid) {
222 super("Basic L&F File Loading Thread");
223 this.currentDirectory = currentDirectory;
224 this.fid = fid;
225 }
226
227 public void run() {
228 run0();
229 setBusy(false, fid);
230 }
231
232 public void run0() {
233 FileSystemView fileSystem = filechooser.getFileSystemView();
234
235 if (isInterrupted()) {
236 return;
237 }
238
239 File[] list = fileSystem.getFiles(currentDirectory, filechooser.isFileHidingEnabled());
240
241 if (isInterrupted()) {
242 return;
243 }
244
245 final Vector<File> newFileCache = new Vector<File>();
246 Vector<File> newFiles = new Vector<File>();
247
248 // run through the file list, add directories and selectable files to fileCache
249 // Note that this block must be OUTSIDE of Invoker thread because of
250 // deadlock possibility with custom synchronized FileSystemView
251 for (File file : list) {
252 if (filechooser.accept(file)) {
253 boolean isTraversable = filechooser.isTraversable(file);
254
255 if (isTraversable) {
256 newFileCache.addElement(file);
257 } else if (filechooser.isFileSelectionEnabled()) {
258 newFiles.addElement(file);
259 }
260
261 if (isInterrupted()) {
262 return;
263 }
264 }
265 }
266
267 // First sort alphabetically by filename
268 sort(newFileCache);
269 sort(newFiles);
270
271 newFileCache.addAll(newFiles);
272
273 // To avoid loads of synchronizations with Invoker and improve performance we
274 // execute the whole block on the COM thread
275 DoChangeContents doChangeContents = ShellFolder.invoke(new Callable<DoChangeContents>() {
276 public DoChangeContents call() {
277 int newSize = newFileCache.size();
278 int oldSize = fileCache.size();
279
280 if (newSize > oldSize) {
281 //see if interval is added
282 int start = oldSize;
283 int end = newSize;
284 for (int i = 0; i < oldSize; i++) {
285 if (!newFileCache.get(i).equals(fileCache.get(i))) {
286 start = i;
287 for (int j = i; j < newSize; j++) {
288 if (newFileCache.get(j).equals(fileCache.get(i))) {
289 end = j;
290 break;
291 }
292 }
293 break;
294 }
295 }
296 if (start >= 0 && end > start
297 && newFileCache.subList(end, newSize).equals(fileCache.subList(start, oldSize))) {
298 if (isInterrupted()) {
299 return null;
300 }
301 return new DoChangeContents(newFileCache.subList(start, end), start, null, 0, fid);
302 }
303 } else if (newSize < oldSize) {
304 //see if interval is removed
305 int start = -1;
306 int end = -1;
307 for (int i = 0; i < newSize; i++) {
308 if (!newFileCache.get(i).equals(fileCache.get(i))) {
309 start = i;
310 end = i + oldSize - newSize;
311 break;
312 }
313 }
314 if (start >= 0 && end > start
315 && fileCache.subList(end, oldSize).equals(newFileCache.subList(start, newSize))) {
316 if (isInterrupted()) {
317 return null;
318 }
319 return new DoChangeContents(null, 0, new Vector(fileCache.subList(start, end)), start, fid);
320 }
321 }
322 if (!fileCache.equals(newFileCache)) {
323 if (isInterrupted()) {
324 cancelRunnables(runnables);
325 }
326 return new DoChangeContents(newFileCache, 0, fileCache, 0, fid);
327 }
328 return null;
329 }
330 });
331
332 if (doChangeContents != null) {
333 runnables.addElement(doChangeContents);
334 SwingUtilities.invokeLater(doChangeContents);
335 }
336 }
337
338
339 public void cancelRunnables(Vector<DoChangeContents> runnables) {
340 for (DoChangeContents runnable : runnables) {
341 runnable.cancel();
342 }
343 }
344
345 public void cancelRunnables() {
346 cancelRunnables(runnables);
347 }
348 }
349
350
351 /**
352 * Adds a PropertyChangeListener to the listener list. The listener is
353 * registered for all bound properties of this class.
354 * <p>
355 * If <code>listener</code> is <code>null</code>,
356 * no exception is thrown and no action is performed.
357 *
358 * @param listener the property change listener to be added
359 *
360 * @see #removePropertyChangeListener
361 * @see #getPropertyChangeListeners
362 *
363 * @since 1.6
364 */
365 public void addPropertyChangeListener(PropertyChangeListener listener) {
366 if (changeSupport == null) {
367 changeSupport = new PropertyChangeSupport(this);
368 }
369 changeSupport.addPropertyChangeListener(listener);
370 }
371
372 /**
373 * Removes a PropertyChangeListener from the listener list.
374 * <p>
375 * If listener is null, no exception is thrown and no action is performed.
376 *
377 * @param listener the PropertyChangeListener to be removed
378 *
379 * @see #addPropertyChangeListener
380 * @see #getPropertyChangeListeners
381 *
382 * @since 1.6
383 */
384 public void removePropertyChangeListener(PropertyChangeListener listener) {
385 if (changeSupport != null) {
386 changeSupport.removePropertyChangeListener(listener);
387 }
388 }
389
390 /**
391 * Returns an array of all the property change listeners
392 * registered on this component.
393 *
394 * @return all of this component's <code>PropertyChangeListener</code>s
395 * or an empty array if no property change
396 * listeners are currently registered
397 *
398 * @see #addPropertyChangeListener
399 * @see #removePropertyChangeListener
400 * @see java.beans.PropertyChangeSupport#getPropertyChangeListeners
401 *
402 * @since 1.6
403 */
404 public PropertyChangeListener[] getPropertyChangeListeners() {
405 if (changeSupport == null) {
406 return new PropertyChangeListener[0];
407 }
408 return changeSupport.getPropertyChangeListeners();
409 }
410
411 /**
412 * Support for reporting bound property changes for boolean properties.
413 * This method can be called when a bound property has changed and it will
414 * send the appropriate PropertyChangeEvent to any registered
415 * PropertyChangeListeners.
416 *
417 * @param propertyName the property whose value has changed
418 * @param oldValue the property's previous value
419 * @param newValue the property's new value
420 *
421 * @since 1.6
422 */
423 protected void firePropertyChange(String propertyName,
424 Object oldValue, Object newValue) {
425 if (changeSupport != null) {
426 changeSupport.firePropertyChange(propertyName,
427 oldValue, newValue);
428 }
429 }
430
431
432 /**
433 * Set the busy state for the model. The model is considered
434 * busy when it is running a separate (interruptable)
435 * thread in order to load the contents of a directory.
436 */
437 private synchronized void setBusy(final boolean busy, int fid) {
438 if (fid == fetchID) {
439 boolean oldValue = this.busy;
440 this.busy = busy;
441
442 if (changeSupport != null && busy != oldValue) {
443 SwingUtilities.invokeLater(new Runnable() {
444 public void run() {
445 firePropertyChange("busy", !busy, busy);
446 }
447 });
448 }
449 }
450 }
451
452
453 class DoChangeContents implements Runnable {
454 private List<File> addFiles;
455 private List<File> remFiles;
456 private boolean doFire = true;
457 private int fid;
458 private int addStart = 0;
459 private int remStart = 0;
460
461 public DoChangeContents(List<File> addFiles, int addStart, List<File> remFiles, int remStart, int fid) {
462 this.addFiles = addFiles;
463 this.addStart = addStart;
464 this.remFiles = remFiles;
465 this.remStart = remStart;
466 this.fid = fid;
467 }
468
469 synchronized void cancel() {
470 doFire = false;
471 }
472
473 public synchronized void run() {
474 if (fetchID == fid && doFire) {
475 int remSize = (remFiles == null) ? 0 : remFiles.size();
476 int addSize = (addFiles == null) ? 0 : addFiles.size();
477 synchronized(fileCache) {
478 if (remSize > 0) {
479 fileCache.removeAll(remFiles);
480 }
481 if (addSize > 0) {
482 fileCache.addAll(addStart, addFiles);
483 }
484 files = null;
485 directories = null;
486 }
487 if (remSize > 0 && addSize == 0) {
488 fireIntervalRemoved(BasicDirectoryModel.this, remStart, remStart + remSize - 1);
489 } else if (addSize > 0 && remSize == 0 && addStart + addSize <= fileCache.size()) {
490 fireIntervalAdded(BasicDirectoryModel.this, addStart, addStart + addSize - 1);
491 } else {
492 fireContentsChanged();
493 }
494 }
495 }
496 }
497}