Save This Page
Home » openjdk-7 » javax » swing » plaf » basic » [javadoc | source]
    1   /*
    2    * Copyright 1998-2006 Sun Microsystems, Inc.  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.  Sun designates this
    8    * particular file as subject to the "Classpath" exception as provided
    9    * by Sun 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 Sun Microsystems, Inc., 4150 Network Circle, Santa Clara,
   22    * CA 95054 USA or visit www.sun.com if you need additional information or
   23    * have any questions.
   24    */
   25   
   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)) {
   70               Object old = e.getOldValue();
   71               if (old instanceof BasicFileChooserUI) {
   72                   BasicFileChooserUI ui = (BasicFileChooserUI) old;
   73                   BasicDirectoryModel model = ui.getModel();
   74                   if (model != null) {
   75                       model.invalidateFileCache();
   76                   }
   77               }
   78           } else if ("JFileChooserDialogIsClosingProperty".equals(prop)) {
   79               invalidateFileCache();
   80           }
   81       }
   82   
   83       /**
   84        * This method is used to interrupt file loading thread.
   85        */
   86       public void invalidateFileCache() {
   87           if (loadThread != null) {
   88               loadThread.interrupt();
   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   
  137           setBusy(true, ++fetchID);
  138   
  139           loadThread = new LoadFilesThread(currentDirectory, fetchID);
  140           loadThread.start();
  141       }
  142   
  143       /**
  144        * Renames a file in the underlying file system.
  145        *
  146        * @param oldFile a <code>File</code> object representing
  147        *        the existing file
  148        * @param newFile a <code>File</code> object representing
  149        *        the desired new file name
  150        * @return <code>true</code> if rename succeeded,
  151        *        otherwise <code>false</code>
  152        * @since 1.4
  153        */
  154       public boolean renameFile(File oldFile, File newFile) {
  155           synchronized(fileCache) {
  156               if (oldFile.renameTo(newFile)) {
  157                   validateFileCache();
  158                   return true;
  159               }
  160               return false;
  161           }
  162       }
  163   
  164   
  165       public void fireContentsChanged() {
  166           // System.out.println("BasicDirectoryModel: firecontentschanged");
  167           fireContentsChanged(this, 0, getSize()-1);
  168       }
  169   
  170       public int getSize() {
  171           return fileCache.size();
  172       }
  173   
  174       public boolean contains(Object o) {
  175           return fileCache.contains(o);
  176       }
  177   
  178       public int indexOf(Object o) {
  179           return fileCache.indexOf(o);
  180       }
  181   
  182       public Object getElementAt(int index) {
  183           return fileCache.get(index);
  184       }
  185   
  186       /**
  187        * Obsolete - not used.
  188        */
  189       public void intervalAdded(ListDataEvent e) {
  190       }
  191   
  192       /**
  193        * Obsolete - not used.
  194        */
  195       public void intervalRemoved(ListDataEvent e) {
  196       }
  197   
  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               FileSystemView fileSystem = filechooser.getFileSystemView();
  238   
  239               File[] list = fileSystem.getFiles(currentDirectory, filechooser.isFileHidingEnabled());
  240   
  241               Vector<File> acceptsList = new Vector<File>();
  242   
  243               if (isInterrupted()) {
  244                   return;
  245               }
  246   
  247               // run through the file list, add directories and selectable files to fileCache
  248               for (int i = 0; i < list.length; i++) {
  249                   if(filechooser.accept(list[i])) {
  250                       acceptsList.addElement(list[i]);
  251                   }
  252               }
  253   
  254               if (isInterrupted()) {
  255                   return;
  256               }
  257   
  258               // First sort alphabetically by filename
  259               sort(acceptsList);
  260   
  261               Vector newDirectories = new Vector(50);
  262               Vector newFiles = new Vector();
  263               // run through list grabbing directories in chunks of ten
  264               for(int i = 0; i < acceptsList.size(); i++) {
  265                   File f = (File) acceptsList.elementAt(i);
  266                   boolean isTraversable = filechooser.isTraversable(f);
  267                   if (isTraversable) {
  268                       newDirectories.addElement(f);
  269                   } else if (!isTraversable && filechooser.isFileSelectionEnabled()) {
  270                       newFiles.addElement(f);
  271                   }
  272                   if(isInterrupted()) {
  273                       return;
  274                   }
  275               }
  276   
  277               Vector newFileCache = new Vector(newDirectories);
  278               newFileCache.addAll(newFiles);
  279   
  280               int newSize = newFileCache.size();
  281               int oldSize = fileCache.size();
  282   
  283               if (newSize > oldSize) {
  284                   //see if interval is added
  285                   int start = oldSize;
  286                   int end = newSize;
  287                   for (int i = 0; i < oldSize; i++) {
  288                       if (!newFileCache.get(i).equals(fileCache.get(i))) {
  289                           start = i;
  290                           for (int j = i; j < newSize; j++) {
  291                               if (newFileCache.get(j).equals(fileCache.get(i))) {
  292                                   end = j;
  293                                   break;
  294                               }
  295                           }
  296                           break;
  297                       }
  298                   }
  299                   if (start >= 0 && end > start
  300                       && newFileCache.subList(end, newSize).equals(fileCache.subList(start, oldSize))) {
  301                       if(isInterrupted()) {
  302                           return;
  303                       }
  304                       invokeLater(new DoChangeContents(newFileCache.subList(start, end), start, null, 0, fid));
  305                       newFileCache = null;
  306                   }
  307               } else if (newSize < oldSize) {
  308                   //see if interval is removed
  309                   int start = -1;
  310                   int end = -1;
  311                   for (int i = 0; i < newSize; i++) {
  312                       if (!newFileCache.get(i).equals(fileCache.get(i))) {
  313                           start = i;
  314                           end = i + oldSize - newSize;
  315                           break;
  316                       }
  317                   }
  318                   if (start >= 0 && end > start
  319                       && fileCache.subList(end, oldSize).equals(newFileCache.subList(start, newSize))) {
  320                       if(isInterrupted()) {
  321                           return;
  322                       }
  323                       invokeLater(new DoChangeContents(null, 0, new Vector(fileCache.subList(start, end)),
  324                                                        start, fid));
  325                       newFileCache = null;
  326                   }
  327               }
  328               if (newFileCache != null && !fileCache.equals(newFileCache)) {
  329                   if (isInterrupted()) {
  330                       cancelRunnables(runnables);
  331                   }
  332                   invokeLater(new DoChangeContents(newFileCache, 0, fileCache, 0, fid));
  333               }
  334           }
  335   
  336   
  337           public void cancelRunnables(Vector runnables) {
  338               for(int i = 0; i < runnables.size(); i++) {
  339                   ((DoChangeContents)runnables.elementAt(i)).cancel();
  340               }
  341           }
  342   
  343           public void cancelRunnables() {
  344               cancelRunnables(runnables);
  345           }
  346      }
  347   
  348   
  349       /**
  350        * Adds a PropertyChangeListener to the listener list. The listener is
  351        * registered for all bound properties of this class.
  352        * <p>
  353        * If <code>listener</code> is <code>null</code>,
  354        * no exception is thrown and no action is performed.
  355        *
  356        * @param    listener  the property change listener to be added
  357        *
  358        * @see #removePropertyChangeListener
  359        * @see #getPropertyChangeListeners
  360        *
  361        * @since 1.6
  362        */
  363       public void addPropertyChangeListener(PropertyChangeListener listener) {
  364           if (changeSupport == null) {
  365               changeSupport = new PropertyChangeSupport(this);
  366           }
  367           changeSupport.addPropertyChangeListener(listener);
  368       }
  369   
  370       /**
  371        * Removes a PropertyChangeListener from the listener list.
  372        * <p>
  373        * If listener is null, no exception is thrown and no action is performed.
  374        *
  375        * @param listener the PropertyChangeListener to be removed
  376        *
  377        * @see #addPropertyChangeListener
  378        * @see #getPropertyChangeListeners
  379        *
  380        * @since 1.6
  381        */
  382       public void removePropertyChangeListener(PropertyChangeListener listener) {
  383           if (changeSupport != null) {
  384               changeSupport.removePropertyChangeListener(listener);
  385           }
  386       }
  387   
  388       /**
  389        * Returns an array of all the property change listeners
  390        * registered on this component.
  391        *
  392        * @return all of this component's <code>PropertyChangeListener</code>s
  393        *         or an empty array if no property change
  394        *         listeners are currently registered
  395        *
  396        * @see      #addPropertyChangeListener
  397        * @see      #removePropertyChangeListener
  398        * @see      java.beans.PropertyChangeSupport#getPropertyChangeListeners
  399        *
  400        * @since 1.6
  401        */
  402       public PropertyChangeListener[] getPropertyChangeListeners() {
  403           if (changeSupport == null) {
  404               return new PropertyChangeListener[0];
  405           }
  406           return changeSupport.getPropertyChangeListeners();
  407       }
  408   
  409       /**
  410        * Support for reporting bound property changes for boolean properties.
  411        * This method can be called when a bound property has changed and it will
  412        * send the appropriate PropertyChangeEvent to any registered
  413        * PropertyChangeListeners.
  414        *
  415        * @param propertyName the property whose value has changed
  416        * @param oldValue the property's previous value
  417        * @param newValue the property's new value
  418        *
  419        * @since 1.6
  420        */
  421       protected void firePropertyChange(String propertyName,
  422                                         Object oldValue, Object newValue) {
  423           if (changeSupport != null) {
  424               changeSupport.firePropertyChange(propertyName,
  425                                                oldValue, newValue);
  426           }
  427       }
  428   
  429   
  430       /**
  431        * Set the busy state for the model. The model is considered
  432        * busy when it is running a separate (interruptable)
  433        * thread in order to load the contents of a directory.
  434        */
  435       private synchronized void setBusy(final boolean busy, int fid) {
  436           if (fid == fetchID) {
  437               boolean oldValue = this.busy;
  438               this.busy = busy;
  439   
  440               if (changeSupport != null && busy != oldValue) {
  441                   SwingUtilities.invokeLater(new Runnable() {
  442                       public void run() {
  443                           firePropertyChange("busy", !busy, busy);
  444                       }
  445                   });
  446               }
  447           }
  448       }
  449   
  450   
  451       class DoChangeContents implements Runnable {
  452           private List addFiles;
  453           private List remFiles;
  454           private boolean doFire = true;
  455           private int fid;
  456           private int addStart = 0;
  457           private int remStart = 0;
  458           private int change;
  459   
  460           public DoChangeContents(List addFiles, int addStart, List remFiles, int remStart, int fid) {
  461               this.addFiles = addFiles;
  462               this.addStart = addStart;
  463               this.remFiles = remFiles;
  464               this.remStart = remStart;
  465               this.fid = fid;
  466           }
  467   
  468           synchronized void cancel() {
  469                   doFire = false;
  470           }
  471   
  472           public synchronized void run() {
  473               if (fetchID == fid && doFire) {
  474                   int remSize = (remFiles == null) ? 0 : remFiles.size();
  475                   int addSize = (addFiles == null) ? 0 : addFiles.size();
  476                   synchronized(fileCache) {
  477                       if (remSize > 0) {
  478                           fileCache.removeAll(remFiles);
  479                       }
  480                       if (addSize > 0) {
  481                           fileCache.addAll(addStart, addFiles);
  482                       }
  483                       files = null;
  484                       directories = null;
  485                   }
  486                   if (remSize > 0 && addSize == 0) {
  487                       fireIntervalRemoved(BasicDirectoryModel.this, remStart, remStart + remSize - 1);
  488                   } else if (addSize > 0 && remSize == 0 && fileCache.size() > addSize) {
  489                       fireIntervalAdded(BasicDirectoryModel.this, addStart, addStart + addSize - 1);
  490                   } else {
  491                       fireContentsChanged();
  492                   }
  493               }
  494           }
  495       }
  496   }

Save This Page
Home » openjdk-7 » javax » swing » plaf » basic » [javadoc | source]