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+
26+package javax.swing.plaf.basic;
27+
28+import java.io.File;
29+import java.util.*;
30+import java.util.concurrent.Callable;
31+import javax.swing.*;
32+import javax.swing.filechooser.*;
33+import javax.swing.event.*;
34+import java.beans.*;
35+
36+import sun.awt.shell.ShellFolder;
37+
38+/**
39+ * Basic implementation of a file list.
40+ *
41+ * @author Jeff Dinkins
42+ */
43+public 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+}