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();
 | 
						|
      }
 | 
						|
  }
 | 
						|
}
 |