/**
   * Returns <code>true</code> for regular files (not directories) with an <code>exe</code>
   * extension (case-insensitive comparison).
   *
   * @param file the file to test
   * @return <code>true</code> for regular files (not directories) with an <code>exe</code>
   *     extension (case-insensitive comparison).
   */
  @Override
  public boolean isApplication(AbstractFile file) {
    String extension = file.getExtension();

    // the isDirectory() test comes last as it is I/O bound
    return extension != null && extension.equalsIgnoreCase("exe") && !file.isDirectory();
  }
  /**
   * Empty the trash
   *
   * <p><b>Implementation notes:</b><br>
   * Simply free the <code>TRASH_PATH</code> directory
   *
   * @return True if everything went well
   */
  @Override
  public boolean empty() {
    // Abort if there is no usable trash folder
    if (TRASH_FOLDER == null) return false;

    FileSet filesToDelete = new FileSet(TRASH_FOLDER);

    try {
      // delete real files
      filesToDelete.addAll(TRASH_FILES_SUBFOLDER.ls());
      // delete spec files
      filesToDelete.addAll(TRASH_INFO_SUBFOLDER.ls());
    } catch (java.io.IOException ex) {
      LOGGER.debug("Failed to list files", ex);
      return false;
    }

    if (filesToDelete.size() > 0) {
      // Starts deleting files
      MainFrame mainFrame = WindowManager.getCurrentMainFrame();
      ProgressDialog progressDialog =
          new ProgressDialog(mainFrame, Translator.get("delete_dialog.deleting"));
      DeleteJob deleteJob = new DeleteJob(progressDialog, mainFrame, filesToDelete, false);
      progressDialog.start(deleteJob);
    }

    return true;
  }
 /**
  * Return <code>true</code> if the specified file is a Xfce Trash folder, i.e. is a directory and
  * has two subdirectories named "info" and "files".
  *
  * @param file the file to test
  * @return <code>true</code> if the specified file is a Xfce Trash folder
  */
 private static boolean isTrashFolder(AbstractFile file) {
   try {
     return file.isDirectory()
         && file.getChild("info").isDirectory()
         && file.getChild("files").isDirectory();
   } catch (IOException e) {
     return false;
   }
 }
 /**
  * Resolves the user Trash folder and its "info" and "files" subfolders once and for all. The
  * trash folder is created if it doesn't already exist.
  */
 static {
   TRASH_FOLDER = getTrashFolder();
   if (TRASH_FOLDER != null) {
     TRASH_INFO_SUBFOLDER = TRASH_FOLDER.getChildSilently("info");
     TRASH_FILES_SUBFOLDER = TRASH_FOLDER.getChildSilently("files");
     TRASH_VOLUME = TRASH_FOLDER.getVolume();
   } else {
     TRASH_INFO_SUBFOLDER = null;
     TRASH_FILES_SUBFOLDER = null;
     TRASH_VOLUME = null;
   }
 }
  /**
   * Forces this monitor to update current folder information. This method should be called when a
   * folder has been manually refreshed, so that this monitor doesn't detect changes and try to
   * refresh the table again.
   *
   * @param folder the new current folder
   */
  private void updateFolderInfo(AbstractFile folder) {
    this.currentFolder = folder;
    this.currentFolderDate = currentFolder.getDate();

    // Reset time average
    totalCheckTime = 0;
    nbSamples = 0;
  }
  public Vector<String> getPossibleCompletions(String path) {
    Vector<String> result = new Vector<String>();
    int index = Math.max(path.lastIndexOf('\\'), path.lastIndexOf('/'));
    if (index != -1) {
      String currentDirectoryName = path.substring(0, index + 1);

      AbstractFile currentDirectory = FileFactory.getFile(currentDirectoryName);
      if (currentDirectory != null && currentDirectory.exists()) {
        long currentDirectoryDate = currentDirectory.getDate();
        if (cachedDirectoryName == null
            || !cachedDirectoryName.equals(currentDirectoryName)
            || currentDirectoryDate != cachedDirectoryDate) {
          AbstractFile[] currentDirectoryFiles;
          try {
            currentDirectoryFiles = getFiles(currentDirectory);
          } catch (IOException e) {
            LOGGER.debug("Caught exception", e);
            return new Vector<String>();
          }

          int nbCurrentDirectoryFiles = currentDirectoryFiles.length;
          cachedDirectoryFileNames = new String[nbCurrentDirectoryFiles];

          for (int i = 0; i < nbCurrentDirectoryFiles; i++) {
            AbstractFile abstractFileI = currentDirectoryFiles[i];
            cachedDirectoryFileNames[i] =
                abstractFileI.getName()
                    + (abstractFileI.isDirectory() ? abstractFileI.getSeparator() : "");
          }

          Arrays.sort(cachedDirectoryFileNames, String.CASE_INSENSITIVE_ORDER);

          cachedDirectoryName =
              currentDirectory.getAbsolutePath()
                  + (currentDirectory.isDirectory() ? "" : currentDirectory.getSeparator());
          cachedDirectoryDate = currentDirectoryDate;
        }

        final String prefix =
            index == path.length() - 1 ? null : path.substring(index + 1).toLowerCase();
        result = PrefixFilter.createPrefixFilter(prefix).filter(cachedDirectoryFileNames);
      }
    }
    return result;
  }
  /**
   * Checks if current file table's folder has changed and if it hasn't, checks if current folder's
   * date has changed and if it has, refresh the file table.
   *
   * @return <code>true</code> if the folder was refreshed.
   */
  private synchronized boolean checkAndRefresh() {
    if (paused || disableAutoRefreshFilter.match(currentFolder)) {
      return false;
    }

    // Update time average next loop
    long timeStamp = System.currentTimeMillis();

    // Check folder's date
    long date = currentFolder.getDate();

    totalCheckTime += System.currentTimeMillis() - timeStamp;
    nbSamples++;

    // Has date changed ?
    // Note that date will be 0 if the folder is no longer available, and thus yield a refresh: this
    // is exactly
    // what we want (the folder will be changed to a 'workable' folder).
    if (date != currentFolderDate) {
      LOGGER.debug(
          this
              + " ("
              + currentFolder.getName()
              + ") Detected changes in current folder, refreshing table!");

      // Try and refresh current folder in a separate thread as to not lock monitor thread
      folderPanel.tryRefreshCurrentFolder();
    }

    if (!forceRefreshFilePath.isEmpty()) {
      synchronized (forceRefreshFilePath) {
        String folderPath = currentFolder.getAbsolutePath();
        for (String path : forceRefreshFilePath) {
          if (path.startsWith(folderPath)) {
            forceRefreshFilePath.remove(path);
            folderPanel.tryRefreshCurrentFolder();
            break;
          }
        }
      }
    }

    return false;
  }
 /**
  * Make a content of .trashinfo file
  *
  * @param file File for which the content is built
  * @return Final content
  */
 private String getFileInfoContent(AbstractFile file) {
   synchronized (INFO_DATE_FORMAT) { // SimpleDateFormat is not thread safe
     return "[Trash Info]\n"
         + "Path="
         + file.getAbsolutePath()
         + "\n"
         + "DeletionDate="
         + INFO_DATE_FORMAT.format(new Date());
   }
 }
  /**
   * Creates a returns a temporary local file/directory with the same extension as the specified
   * file/directory (guaranteed), and the same filename as much as possible (best effort). This
   * method returns <code>null</code> if the temporary file/directory could not be created.
   *
   * @param nonLocalFile the non-local file for which to create a temporary file.
   * @return a temporary local file/directory with the same extension as the specified
   *     file/directory
   */
  protected LocalFile createTempLocalFile(AbstractFile nonLocalFile) {
    try {
      // Note: the returned temporary file may be an AbstractArchiveFile if the filename's extension
      // corresponds
      // to a registered archive format
      LocalFile tempFile =
          FileFactory.getTemporaryFile(nonLocalFile.getName(), false).getAncestor(LocalFile.class);

      // create a directory
      if (nonLocalFile.isDirectory()) {
        tempFile.mkdir();
      } else { // create a regular file
        tempFile.getOutputStream().close();
      }
      return tempFile;
    } catch (IOException e) {
      return null;
    }
  }
  /**
   * Return trash files count
   *
   * <p>We assume the count of items in trash equals the count of files in <code>
   * TRASH_PATH + "/info"</code> folder.
   *
   * @return Count of files in trash
   */
  @Override
  public int getItemCount() {
    // Abort if there is no usable trash folder
    if (TRASH_FOLDER == null) return -1;

    try {
      return TRASH_INFO_SUBFOLDER.ls().length;
    } catch (java.io.IOException ex) {
      // can't access trash folder
      return -1;
    }
  }
Exemple #11
0
  /**
   * Updates this window's title to show currently active folder and window number. This method is
   * called by this class and WindowManager.
   */
  public void updateWindowTitle() {
    // Update window title
    String title = activeTable.getFolderPanel().getCurrentFolder().getAbsolutePath();

    // Add the application name to window title on all OSs except MAC
    if (!OsFamily.MAC_OS_X.isCurrent()) title += " - muCommander";

    java.util.List<MainFrame> mainFrames = WindowManager.getMainFrames();
    if (mainFrames.size() > 1) title += " [" + (mainFrames.indexOf(this) + 1) + "]";
    setTitle(title);

    // Use new Window decorations introduced in Mac OS X 10.5 (Leopard)
    if (OsFamily.MAC_OS_X.isCurrent() && OsVersion.MAC_OS_X_10_5.isCurrentOrHigher()) {
      // Displays the document icon in the window title bar, works only for local files
      AbstractFile currentFolder = activeTable.getFolderPanel().getCurrentFolder();
      Object javaIoFile;
      if (currentFolder.getURL().getScheme().equals(FileProtocols.FILE)) {
        // If the current folder is an archive entry, display the archive file, this is the closest
        // we can get
        // with a java.io.File
        if (currentFolder.hasAncestor(AbstractArchiveEntryFile.class))
          javaIoFile = currentFolder.getParentArchive().getUnderlyingFileObject();
        else javaIoFile = currentFolder.getUnderlyingFileObject();
      } else {
        // If the current folder is not a local file, use the special /Network directory which is
        // sort of
        // 'Network Neighborhood'.
        javaIoFile = new java.io.File("/Network");
      }

      // Note that for some strange reason (looks like a bug), setting the property to null won't
      // remove
      // the previous icon.
      getRootPane().putClientProperty("Window.documentFile", javaIoFile);
    }
  }
  @Override
  public final boolean isFileOperationSupported(FileOperation op) {
    Class<? extends AbstractFile> thisClass = getClass();
    Method opMethod = op.getCorrespondingMethod(thisClass);
    // If the method corresponding to the file operation has been overridden by this class (a
    // ProxyFile subclass),
    // check the presence of the UnsupportedFileOperation annotation in this class.
    try {
      if (!thisClass
          .getMethod(opMethod.getName(), opMethod.getParameterTypes())
          .getDeclaringClass()
          .equals(ProxyFile.class)) {
        return AbstractFile.isFileOperationSupported(op, thisClass);
      }
    } catch (Exception e) {
      // Should never happen, unless AbstractFile method signatures have changed and FileOperation
      // has not been updated
      LOGGER.warn("Exception caught, this should not have happened", e);
    }

    // Otherwise, check for the presence of the UnsupportedFileOperation annotation in the wrapped
    // AbstractFile.
    return file.isFileOperationSupported(op);
  }
  public FolderChangeMonitor(FolderPanel folderPanel) {
    this.folderPanel = folderPanel;

    // Listen to folder changes to know when a folder is being / has been changed
    folderPanel.getLocationManager().addLocationListener(this);

    this.currentFolder = folderPanel.getCurrentFolder();
    this.currentFolderDate = currentFolder.getDate();

    // Folder contents is up-to-date let's wait before checking it for changes
    this.lastCheckTimestamp = System.currentTimeMillis();
    this.waitBeforeCheckTime = waitAfterRefresh;

    folderPanel.getMainFrame().addWindowListener(this);

    instances.add(this);

    // create and start the monitor thread on first FolderChangeMonitor instance
    if (monitorThread == null && checkPeriod >= 0) {
      monitorThread = new Thread(this, getClass().getName());
      monitorThread.setDaemon(true);
      monitorThread.start();
    }
  }
  public Icon getFileIcon(AbstractFile originalFile, Dimension preferredResolution) {
    // Specified file is a LocalFile or a ProxyFile proxying a LocalFile (e.g. an archive file):
    // let's simply get
    // the icon using #getLocalFileIcon(LocalFile)
    AbstractFile topFile = originalFile.getTopAncestor();
    Icon icon;

    if (topFile instanceof LocalFile) {
      icon = getLocalFileIcon((LocalFile) topFile, originalFile, preferredResolution);
    }
    // File is a remote file: create a temporary local file (or directory) with the same extension
    // to grab the icon
    // and then delete the file. This operation is I/O bound and thus expensive, so an LRU is used
    // to cache
    // frequently-accessed file extensions.
    else {
      // create the temporary, local file
      LocalFile tempFile = createTempLocalFile(topFile);
      if (tempFile == null) {
        // No temp file, no icon!
        return null;
      }

      // Get the file icon
      icon = getLocalFileIcon(tempFile, originalFile, preferredResolution);

      // Delete the temporary file
      try {
        tempFile.delete();
      } catch (IOException e) {
        // Not much to do
      }
    }

    return icon;
  }
  /**
   * It is possible to add several files with same name to the Trash. These files are distinguished
   * by _N appended to the name, where _N is rising int number. <br>
   * This method tries to find first empty <code>filename_N.ext</code>.
   *
   * @param file File to be deleted
   * @return Suitable filename in trash (without .trashinfo extension)
   */
  private String getUniqueFilename(AbstractFile file) throws IOException {
    // try if no previous file in trash exists
    if (!TRASH_FILES_SUBFOLDER.getChild(file.getName()).exists()) return file.getName();

    String rawName = file.getNameWithoutExtension();
    String extension = file.getExtension();

    // find first empty filename in format filename_N.ext
    String filename;
    int count = 1;
    while (true) {
      filename = rawName + "_" + count++;
      if (extension != null) {
        filename += "." + extension;
      }

      if (!TRASH_FILES_SUBFOLDER.getChild(filename).exists()) return filename;
    }
  }
  /**
   * Tries to find an existing user Trash folder and returns it. If no existing Trash folder was
   * found, creates the standard Xfce user Trash folder and returns it.
   *
   * @return the user Trash folder, <code>null</code> if no user trash folder could be found or
   *     created
   */
  private static AbstractFile getTrashFolder() {
    AbstractFile userHome = LocalFile.getUserHome();

    AbstractFile trashDir = userHome.getChildSilently(".local/share/Trash/");
    if (isTrashFolder(trashDir)) {
      return trashDir;
    }

    // No existing user trash was found: create the folder, only if it doesn't already exist.
    if (!trashDir.exists()) {
      try {
        trashDir.mkdirs();
        trashDir.getChild("info").mkdir();
        trashDir.getChild("files").mkdir();

        return trashDir;
      } catch (IOException e) {
        // Will return null
      }
    }

    return null;
  }
 public boolean equals(Object f) {
   return file.equals(f);
 }
Exemple #18
0
  /**
   * Copies recursively the given file or folder.
   *
   * @param file the file or folder to move
   * @param recurseParams destination folder where the given file will be copied (null for top level
   *     files)
   * @return <code>true</code> if the file has been copied.
   */
  @Override
  protected boolean processFile(AbstractFile file, Object recurseParams) {
    // Stop if interrupted
    if (getState() == INTERRUPTED) return false;

    // Destination folder
    AbstractFile destFolder = recurseParams == null ? baseDestFolder : (AbstractFile) recurseParams;

    // Is current file in base folder ?
    boolean isFileInBaseFolder = files.indexOf(file) != -1;

    // Determine filename in destination
    String destFileName;
    if (isFileInBaseFolder && newName != null) destFileName = newName;
    else destFileName = file.getName();

    // Create destination AbstractFile instance
    AbstractFile destFile = createDestinationFile(destFolder, destFileName);
    if (destFile == null) return false;
    currentDestFile = destFile;

    // Do nothing if file is a symlink (skip file and return)
    if (file.isSymlink()) return true;

    destFile = checkForCollision(file, destFolder, destFile, false);
    if (destFile == null) return false;

    // Copy directory recursively
    if (file.isDirectory()) {
      // Create the folder in the destination folder if it doesn't exist
      if (!(destFile.exists() && destFile.isDirectory())) {
        // Loop for retry
        do {
          try {
            destFile.mkdir();
          } catch (IOException e) {
            // Unable to create folder
            int ret =
                showErrorDialog(
                    errorDialogTitle, Translator.get("cannot_create_folder", destFileName));
            // Retry loops
            if (ret == RETRY_ACTION) continue;
            // Cancel or close dialog return false
            return false;
            // Skip continues
          }
          break;
        } while (true);
      }

      // and copy each file in this folder recursively
      do { // Loop for retry
        try {
          // for each file in folder...
          AbstractFile subFiles[] = file.ls();
          // filesDiscovered(subFiles);
          for (int i = 0; i < subFiles.length && getState() != INTERRUPTED; i++) {
            // Notify job that we're starting to process this file (needed for recursive calls to
            // processFile)
            nextFile(subFiles[i]);
            processFile(subFiles[i], destFile);
          }

          // Set currentDestFile back to the enclosing folder in case an overridden processFile
          // method
          // needs to work with the folder after calling super.processFile.
          currentDestFile = destFile;

          // Only when finished with folder, set destination folder's date to match the original
          // folder one
          if (destFile.isFileOperationSupported(FileOperation.CHANGE_DATE)) {
            try {
              destFile.changeDate(file.getDate());
            } catch (IOException e) {
              AppLogger.fine("failed to change the date of " + destFile, e);
              // Fail silently
            }
          }

          return true;
        } catch (IOException e) {
          // file.ls() failed
          int ret =
              showErrorDialog(
                  errorDialogTitle, Translator.get("cannot_read_folder", file.getName()));
          // Retry loops
          if (ret == RETRY_ACTION) continue;
          // Cancel, skip or close dialog returns false
          return false;
        }
      } while (true);
    }
    // File is a regular file, copy it
    else {
      // Copy the file
      return tryCopyFile(file, destFile, append, errorDialogTitle);
    }
  }
 @Override
 public AbstractFile getParent() {
   return file.getParent();
 }
 @Override
 public long getSize() {
   return file.getSize();
 }
 @Override
 public void changeDate(long lastModified) throws IOException {
   file.changeDate(lastModified);
 }
 @Override
 public long getDate() {
   return file.getDate();
 }
 /**
  * Creates a new ProxyFile using the given file to delegate AbstractFile method calls to.
  *
  * @param file the file to be proxied
  */
 public ProxyFile(AbstractFile file) {
   super(file.getURL());
   this.file = file;
 }
 @Override
 public void changeReplication(short replication) throws IOException {
   file.changeReplication(replication);
 }
 @Override
 public long getBlocksize() throws UnsupportedFileOperationException {
   return file.getBlocksize();
 }
 @Override
 public short getReplication() throws UnsupportedFileOperationException {
   return file.getReplication();
 }
 public String toString() {
   return file.toString();
 }
 public int hashCode() {
   return file.hashCode();
 }
 @Override
 public boolean equalsCanonical(Object f) {
   return file.equalsCanonical(f);
 }
 @Override
 public void deleteRecursively() throws IOException {
   file.deleteRecursively();
 }