mirror of git://gcc.gnu.org/git/gcc.git
				
				
				
			
		
			
				
	
	
		
			587 lines
		
	
	
		
			16 KiB
		
	
	
	
		
			Java
		
	
	
	
			
		
		
	
	
			587 lines
		
	
	
		
			16 KiB
		
	
	
	
		
			Java
		
	
	
	
| /* BasicDirectoryModel.java --
 | |
|    Copyright (C) 2005, 2006  Free Software Foundation, Inc.
 | |
| 
 | |
| This file is part of GNU Classpath.
 | |
| 
 | |
| GNU Classpath is free software; you can redistribute it and/or modify
 | |
| it under the terms of the GNU General Public License as published by
 | |
| the Free Software Foundation; either version 2, or (at your option)
 | |
| any later version.
 | |
| 
 | |
| GNU Classpath is distributed in the hope that it will be useful, but
 | |
| WITHOUT ANY WARRANTY; without even the implied warranty of
 | |
| MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
 | |
| General Public License for more details.
 | |
| 
 | |
| You should have received a copy of the GNU General Public License
 | |
| along with GNU Classpath; see the file COPYING.  If not, write to the
 | |
| Free Software Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA
 | |
| 02110-1301 USA.
 | |
| 
 | |
| Linking this library statically or dynamically with other modules is
 | |
| making a combined work based on this library.  Thus, the terms and
 | |
| conditions of the GNU General Public License cover the whole
 | |
| combination.
 | |
| 
 | |
| As a special exception, the copyright holders of this library give you
 | |
| permission to link this library with independent modules to produce an
 | |
| executable, regardless of the license terms of these independent
 | |
| modules, and to copy and distribute the resulting executable under
 | |
| terms of your choice, provided that you also meet, for each linked
 | |
| independent module, the terms and conditions of the license of that
 | |
| module.  An independent module is a module which is not derived from
 | |
| or based on this library.  If you modify this library, you may extend
 | |
| this exception to your version of the library, but you are not
 | |
| obligated to do so.  If you do not wish to do so, delete this
 | |
| exception statement from your version. */
 | |
| 
 | |
| package javax.swing.plaf.basic;
 | |
| 
 | |
| import java.beans.PropertyChangeEvent;
 | |
| import java.beans.PropertyChangeListener;
 | |
| import java.io.File;
 | |
| import java.util.Collections;
 | |
| import java.util.Comparator;
 | |
| import java.util.Iterator;
 | |
| import java.util.List;
 | |
| import java.util.Vector;
 | |
| import javax.swing.AbstractListModel;
 | |
| import javax.swing.JFileChooser;
 | |
| import javax.swing.SwingUtilities;
 | |
| import javax.swing.event.ListDataEvent;
 | |
| import javax.swing.filechooser.FileSystemView;
 | |
| 
 | |
| 
 | |
| /**
 | |
|  * Implements an AbstractListModel for directories where the source
 | |
|  * of the files is a JFileChooser object.
 | |
|  *
 | |
|  * This class is used for sorting and ordering the file list in
 | |
|  * a JFileChooser L&F object.
 | |
|  */
 | |
| public class BasicDirectoryModel extends AbstractListModel
 | |
|   implements PropertyChangeListener
 | |
| {
 | |
|   /** The list of files itself */
 | |
|   private Vector contents;
 | |
| 
 | |
|   /**
 | |
|    * The directories in the list.
 | |
|    */
 | |
|   private Vector directories;
 | |
| 
 | |
|   /**
 | |
|    * The files in the list.
 | |
|    */
 | |
|   private Vector files;
 | |
| 
 | |
|   /** The listing mode of the associated JFileChooser,
 | |
|       either FILES_ONLY, DIRECTORIES_ONLY or FILES_AND_DIRECTORIES */
 | |
|   private int listingMode;
 | |
| 
 | |
|   /** The JFileCooser associated with this model */
 | |
|   private JFileChooser filechooser;
 | |
| 
 | |
|   /**
 | |
|    * The thread that loads the file view.
 | |
|    */
 | |
|   private DirectoryLoadThread loadThread;
 | |
| 
 | |
|   /**
 | |
|    * This thread is responsible for loading file lists from the
 | |
|    * current directory and updating the model.
 | |
|    */
 | |
|   private class DirectoryLoadThread extends Thread
 | |
|   {
 | |
| 
 | |
|     /**
 | |
|      * Updates the Swing list model.
 | |
|      */
 | |
|     private class UpdateSwingRequest
 | |
|       implements Runnable
 | |
|     {
 | |
| 
 | |
|       private List added;
 | |
|       private int addIndex;
 | |
|       private List removed;
 | |
|       private int removeIndex;
 | |
|       private boolean cancel;
 | |
| 
 | |
|       UpdateSwingRequest(List add, int ai, List rem, int ri)
 | |
|       {
 | |
|         added = add;
 | |
|         addIndex = ai;
 | |
|         removed = rem;
 | |
|         removeIndex = ri;
 | |
|         cancel = false;
 | |
|       }
 | |
| 
 | |
|       public void run()
 | |
|       {
 | |
|         if (! cancel)
 | |
|           {
 | |
|             int numRemoved = removed == null ? 0 : removed.size();
 | |
|             int numAdded = added == null ? 0 : added.size();
 | |
|             synchronized (contents)
 | |
|               {
 | |
|                 if (numRemoved > 0)
 | |
|                   contents.removeAll(removed);
 | |
|                 if (numAdded > 0)
 | |
|                   contents.addAll(added);
 | |
| 
 | |
|                 files = null;
 | |
|                 directories = null;
 | |
|               }
 | |
|             if (numRemoved > 0 && numAdded == 0)
 | |
|               fireIntervalRemoved(BasicDirectoryModel.this, removeIndex,
 | |
|                                   removeIndex + numRemoved - 1);
 | |
|             else if (numRemoved == 0 && numAdded > 0)
 | |
|               fireIntervalAdded(BasicDirectoryModel.this, addIndex,
 | |
|                                 addIndex + numAdded - 1);
 | |
|             else
 | |
|               fireContentsChanged();
 | |
|           }
 | |
|       }
 | |
| 
 | |
|       void cancel()
 | |
|       {
 | |
|         cancel = true;
 | |
|       }
 | |
|     }
 | |
| 
 | |
|     /**
 | |
|      * The directory beeing loaded.
 | |
|      */
 | |
|     File directory;
 | |
| 
 | |
|     /**
 | |
|      * Stores all UpdateSwingRequests that are sent to the event queue.
 | |
|      */
 | |
|     private UpdateSwingRequest pending;
 | |
| 
 | |
|     /**
 | |
|      * Creates a new DirectoryLoadThread that loads the specified
 | |
|      * directory.
 | |
|      *
 | |
|      * @param dir the directory to load
 | |
|      */
 | |
|     DirectoryLoadThread(File dir)
 | |
|     {
 | |
|       super("Basic L&F directory loader");
 | |
|       directory = dir;
 | |
|     }
 | |
| 
 | |
|     public void run()
 | |
|     {
 | |
|       FileSystemView fsv = filechooser.getFileSystemView();
 | |
|       File[] files = fsv.getFiles(directory,
 | |
|                                   filechooser.isFileHidingEnabled());
 | |
| 
 | |
|       // Occasional check if we have been interrupted.
 | |
|       if (isInterrupted())
 | |
|         return;
 | |
| 
 | |
|       // Check list for accepted files.
 | |
|       Vector accepted = new Vector();
 | |
|       for (int i = 0; i < files.length; i++)
 | |
|         {
 | |
|           if (filechooser.accept(files[i]))
 | |
|             accepted.add(files[i]);
 | |
|         }
 | |
| 
 | |
|       // Occasional check if we have been interrupted.
 | |
|       if (isInterrupted())
 | |
|         return;
 | |
| 
 | |
|       // Sort list.
 | |
|       sort(accepted);
 | |
| 
 | |
|       // Now split up directories from files so that we get the directories
 | |
|       // listed before the files.
 | |
|       Vector newFiles = new Vector();
 | |
|       Vector newDirectories = new Vector();
 | |
|       for (Iterator i = accepted.iterator(); i.hasNext();)
 | |
|         {
 | |
|           File f = (File) i.next();
 | |
|           boolean traversable = filechooser.isTraversable(f);
 | |
|           if (traversable)
 | |
|             newDirectories.add(f);
 | |
|           else if (! traversable && filechooser.isFileSelectionEnabled())
 | |
|             newFiles.add(f);
 | |
| 
 | |
|           // Occasional check if we have been interrupted.
 | |
|           if (isInterrupted())
 | |
|             return;
 | |
| 
 | |
|         }
 | |
| 
 | |
|       // Build up new file cache. Try to update only the changed elements.
 | |
|       // This will be important for actions like adding new files or
 | |
|       // directories inside a large file list.
 | |
|       Vector newCache = new Vector(newDirectories);
 | |
|       newCache.addAll(newFiles);
 | |
| 
 | |
|       int newSize = newCache.size();
 | |
|       int oldSize = contents.size();
 | |
|       if (newSize < oldSize)
 | |
|         {
 | |
|           // Check for removed interval.
 | |
|           int start = -1;
 | |
|           int end = -1;
 | |
|           boolean found = false;
 | |
|           for (int i = 0; i < newSize && !found; i++)
 | |
|             {
 | |
|               if (! newCache.get(i).equals(contents.get(i)))
 | |
|                 {
 | |
|                   start = i;
 | |
|                   end = i + oldSize - newSize;
 | |
|                   found = true;
 | |
|                 }
 | |
|             }
 | |
|           if (start >= 0 && end > start
 | |
|               && contents.subList(end, oldSize)
 | |
|                                     .equals(newCache.subList(start, newSize)))
 | |
|             {
 | |
|               // Occasional check if we have been interrupted.
 | |
|               if (isInterrupted())
 | |
|                 return;
 | |
| 
 | |
|               Vector removed = new Vector(contents.subList(start, end));
 | |
|               UpdateSwingRequest r = new UpdateSwingRequest(null, 0,
 | |
|                                                             removed, start);
 | |
|               invokeLater(r);
 | |
|               newCache = null;
 | |
|             }
 | |
|         }
 | |
|       else if (newSize > oldSize)
 | |
|         {
 | |
|           // Check for inserted interval.
 | |
|           int start = oldSize;
 | |
|           int end = newSize;
 | |
|           boolean found = false;
 | |
|           for (int i = 0; i < oldSize && ! found; i++)
 | |
|             {
 | |
|               if (! newCache.get(i).equals(contents.get(i)))
 | |
|                 {
 | |
|                   start = i;
 | |
|                   boolean foundEnd = false;
 | |
|                   for (int j = i; j < newSize && ! foundEnd; j++)
 | |
|                     {
 | |
|                       if (newCache.get(j).equals(contents.get(i)))
 | |
|                         {
 | |
|                           end = j;
 | |
|                           foundEnd = true;
 | |
|                         }
 | |
|                     }
 | |
|                   end = i + oldSize - newSize;
 | |
|                 }
 | |
|             }
 | |
|           if (start >= 0 && end > start
 | |
|               && newCache.subList(end, newSize)
 | |
|                                     .equals(contents.subList(start, oldSize)))
 | |
|             {
 | |
|               // Occasional check if we have been interrupted.
 | |
|               if (isInterrupted())
 | |
|                 return;
 | |
| 
 | |
|               List added = newCache.subList(start, end);
 | |
|               UpdateSwingRequest r = new UpdateSwingRequest(added, start,
 | |
|                                                             null, 0);
 | |
|               invokeLater(r);
 | |
|               newCache = null;
 | |
|             }
 | |
|         }
 | |
| 
 | |
|       // Handle complete list changes (newCache != null).
 | |
|       if (newCache != null && ! contents.equals(newCache))
 | |
|         {
 | |
|           // Occasional check if we have been interrupted.
 | |
|           if (isInterrupted())
 | |
|             return;
 | |
|           UpdateSwingRequest r = new UpdateSwingRequest(newCache, 0,
 | |
|                                                         contents, 0);
 | |
|           invokeLater(r);
 | |
|         }
 | |
|     }
 | |
| 
 | |
|     /**
 | |
|      * Wraps SwingUtilities.invokeLater() and stores the request in
 | |
|      * a Vector so that we can still cancel it later.
 | |
|      *
 | |
|      * @param update the request to invoke
 | |
|      */
 | |
|     private void invokeLater(UpdateSwingRequest update)
 | |
|     {
 | |
|       pending = update;
 | |
|       SwingUtilities.invokeLater(update);
 | |
|     }
 | |
| 
 | |
|     /**
 | |
|      * Cancels all pending update requests that might be in the AWT
 | |
|      * event queue.
 | |
|      */
 | |
|     void cancelPending()
 | |
|     {
 | |
|       if (pending != null)
 | |
|         pending.cancel();
 | |
|     }
 | |
|   }
 | |
| 
 | |
|   /** A Comparator class/object for sorting the file list. */
 | |
|   private Comparator comparator = new Comparator()
 | |
|     {
 | |
|       public int compare(Object o1, Object o2)
 | |
|       {
 | |
|         if (lt((File) o1, (File) o2))
 | |
|           return -1;
 | |
|         else
 | |
|           return 1;
 | |
|       }
 | |
|     };
 | |
| 
 | |
|   /**
 | |
|    * Creates a new BasicDirectoryModel object.
 | |
|    *
 | |
|    * @param filechooser DOCUMENT ME!
 | |
|    */
 | |
|   public BasicDirectoryModel(JFileChooser filechooser)
 | |
|   {
 | |
|     this.filechooser = filechooser;
 | |
|     filechooser.addPropertyChangeListener(this);
 | |
|     listingMode = filechooser.getFileSelectionMode();
 | |
|     contents = new Vector();
 | |
|     validateFileCache();
 | |
|   }
 | |
| 
 | |
|   /**
 | |
|    * Returns whether a given (File) object is included in the list.
 | |
|    *
 | |
|    * @param o - The file object to test.
 | |
|    *
 | |
|    * @return <code>true</code> if the list contains the given object.
 | |
|    */
 | |
|   public boolean contains(Object o)
 | |
|   {
 | |
|     return contents.contains(o);
 | |
|   }
 | |
| 
 | |
|   /**
 | |
|    * Fires a content change event.
 | |
|    */
 | |
|   public void fireContentsChanged()
 | |
|   {
 | |
|     fireContentsChanged(this, 0, getSize() - 1);
 | |
|   }
 | |
| 
 | |
|   /**
 | |
|    * Returns a Vector of (java.io.File) objects containing
 | |
|    * the directories in this list.
 | |
|    *
 | |
|    * @return a Vector
 | |
|    */
 | |
|   public Vector<File> getDirectories()
 | |
|   {
 | |
|     // Synchronize this with the UpdateSwingRequest for the case when
 | |
|     // contents is modified.
 | |
|     synchronized (contents)
 | |
|       {
 | |
|         Vector dirs = directories;
 | |
|         if (dirs == null)
 | |
|           {
 | |
|             // Initializes this in getFiles().
 | |
|             getFiles();
 | |
|             dirs = directories;
 | |
|           }
 | |
|         return dirs;
 | |
|       }
 | |
|   }
 | |
| 
 | |
|   /**
 | |
|    * Returns the (java.io.File) object at
 | |
|    * an index in the list.
 | |
|    *
 | |
|    * @param index The list index
 | |
|    * @return a File object
 | |
|    */
 | |
|   public Object getElementAt(int index)
 | |
|   {
 | |
|     if (index > getSize() - 1)
 | |
|       return null;
 | |
|     return contents.elementAt(index);
 | |
|   }
 | |
| 
 | |
|   /**
 | |
|    * Returns a Vector of (java.io.File) objects containing
 | |
|    * the files in this list.
 | |
|    *
 | |
|    * @return a Vector
 | |
|    */
 | |
|   public Vector<File>  getFiles()
 | |
|   {
 | |
|     synchronized (contents)
 | |
|       {
 | |
|         Vector f = files;
 | |
|         if (f == null)
 | |
|           {
 | |
|             f = new Vector();
 | |
|             Vector d = new Vector(); // Directories;
 | |
|             for (Iterator i = contents.iterator(); i.hasNext();)
 | |
|               {
 | |
|                 File file = (File) i.next();
 | |
|                 if (filechooser.isTraversable(file))
 | |
|                   d.add(file);
 | |
|                 else
 | |
|                   f.add(file);
 | |
|               }
 | |
|             files = f;
 | |
|             directories = d;
 | |
|           }
 | |
|         return f;
 | |
|       }
 | |
|   }
 | |
| 
 | |
|   /**
 | |
|    * Returns the size of the list, which only includes directories
 | |
|    * if the JFileChooser is set to DIRECTORIES_ONLY.
 | |
|    *
 | |
|    * Otherwise, both directories and files are included in the count.
 | |
|    *
 | |
|    * @return The size of the list.
 | |
|    */
 | |
|   public int getSize()
 | |
|   {
 | |
|     return contents.size();
 | |
|   }
 | |
| 
 | |
|   /**
 | |
|    * Returns the index of an (java.io.File) object in the list.
 | |
|    *
 | |
|    * @param o The object - normally a File.
 | |
|    *
 | |
|    * @return the index of that object, or -1 if it is not in the list.
 | |
|    */
 | |
|   public int indexOf(Object o)
 | |
|   {
 | |
|     return contents.indexOf(o);
 | |
|   }
 | |
| 
 | |
|   /**
 | |
|    * Obsoleted method which does nothing.
 | |
|    */
 | |
|   public void intervalAdded(ListDataEvent e)
 | |
|   {
 | |
|     // obsoleted
 | |
|   }
 | |
| 
 | |
|   /**
 | |
|    * Obsoleted method which does nothing.
 | |
|    */
 | |
|   public void intervalRemoved(ListDataEvent e)
 | |
|   {
 | |
|     // obsoleted
 | |
|   }
 | |
| 
 | |
|   /**
 | |
|    * Obsoleted method which does nothing.
 | |
|    */
 | |
|   public void invalidateFileCache()
 | |
|   {
 | |
|     // obsoleted
 | |
|   }
 | |
| 
 | |
|   /**
 | |
|    * Less than, determine the relative order in the list of two files
 | |
|    * for sorting purposes.
 | |
|    *
 | |
|    * The order is: directories < files, and thereafter alphabetically,
 | |
|    * using the default locale collation.
 | |
|    *
 | |
|    * @param a the first file
 | |
|    * @param b the second file
 | |
|    *
 | |
|    * @return <code>true</code> if a > b, <code>false</code> if a < b.
 | |
|    */
 | |
|   protected boolean lt(File a, File b)
 | |
|   {
 | |
|     boolean aTrav = filechooser.isTraversable(a);
 | |
|     boolean bTrav = filechooser.isTraversable(b);
 | |
| 
 | |
|     if (aTrav == bTrav)
 | |
|       {
 | |
|         String aname = a.getName().toLowerCase();
 | |
|         String bname = b.getName().toLowerCase();
 | |
|         return (aname.compareTo(bname) < 0) ? true : false;
 | |
|       }
 | |
|     else
 | |
|       {
 | |
|         if (aTrav)
 | |
|           return true;
 | |
|         else
 | |
|           return false;
 | |
|       }
 | |
|   }
 | |
| 
 | |
|   /**
 | |
|    * Listens for a property change; the change in file selection mode of the
 | |
|    * associated JFileChooser. Reloads the file cache on that event.
 | |
|    *
 | |
|    * @param e - A PropertyChangeEvent.
 | |
|    */
 | |
|   public void propertyChange(PropertyChangeEvent e)
 | |
|   {
 | |
|     String property = e.getPropertyName();
 | |
|     if (property.equals(JFileChooser.DIRECTORY_CHANGED_PROPERTY)
 | |
|         || property.equals(JFileChooser.FILE_FILTER_CHANGED_PROPERTY)
 | |
|         || property.equals(JFileChooser.FILE_HIDING_CHANGED_PROPERTY)
 | |
|         || property.equals(JFileChooser.FILE_SELECTION_MODE_CHANGED_PROPERTY)
 | |
|         || property.equals(JFileChooser.FILE_VIEW_CHANGED_PROPERTY)
 | |
|         )
 | |
|       {
 | |
|         validateFileCache();
 | |
|       }
 | |
|   }
 | |
| 
 | |
|   /**
 | |
|    * Renames a file - However, does <I>not</I> re-sort the list
 | |
|    * or replace the old file with the new one in the list.
 | |
|    *
 | |
|    * @param oldFile The old file
 | |
|    * @param newFile The new file name
 | |
|    *
 | |
|    * @return <code>true</code> if the rename succeeded
 | |
|    */
 | |
|   public boolean renameFile(File oldFile, File newFile)
 | |
|   {
 | |
|     return oldFile.renameTo( newFile );
 | |
|   }
 | |
| 
 | |
|   /**
 | |
|    * Sorts a Vector of File objects.
 | |
|    *
 | |
|    * @param v The Vector to sort.
 | |
|    */
 | |
|   protected void sort(Vector<? extends File> v)
 | |
|   {
 | |
|     Collections.sort(v, comparator);
 | |
|   }
 | |
| 
 | |
|   /**
 | |
|    * Re-loads the list of files
 | |
|    */
 | |
|   public void validateFileCache()
 | |
|   {
 | |
|     File dir = filechooser.getCurrentDirectory();
 | |
|     if (dir != null)
 | |
|       {
 | |
|         // Cancel all pending requests.
 | |
|         if (loadThread != null)
 | |
|           {
 | |
|             loadThread.interrupt();
 | |
|             loadThread.cancelPending();
 | |
|           }
 | |
|         loadThread = new DirectoryLoadThread(dir);
 | |
|         loadThread.start();
 | |
|       }
 | |
|   }
 | |
| }
 |