/* * This file is part of muCommander, http://www.mucommander.com * Copyright (C) 2002-2016 Maxence Bernard * * muCommander 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 3 of the License, or * (at your option) any later version. * * muCommander 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 this program. If not, see <http://www.gnu.org/licenses/>. */ package com.mucommander.core; import java.util.List; import java.util.Vector; import org.slf4j.Logger; import org.slf4j.LoggerFactory; import com.mucommander.commons.file.AbstractFile; import com.mucommander.commons.file.FileFactory; import com.mucommander.commons.file.FileURL; import com.mucommander.commons.file.protocol.FileProtocols; import com.mucommander.commons.file.protocol.local.LocalFile; import com.mucommander.ui.main.FolderPanel; /** * This class maintains a history of visited locations for a given tab, and provides methods to go back and go forward * in the history. * * <p>There is a limit to the number of locations the history can contain, defined by {@link #HISTORY_CAPACITY}.</p> * * @author Maxence Bernard, Arik Hadas */ public class LocalLocationHistory { private static final Logger LOGGER = LoggerFactory.getLogger(LocalLocationHistory.class); /** Maximum number of elements the folder history can contain */ private final static int HISTORY_CAPACITY = 100; /** List of visited locations, ordered by last visit date */ private List<FileURL> history = new Vector<FileURL>(HISTORY_CAPACITY+1); /** Index of current folder in history */ private int historyIndex = -1; /** FolderPanel which is being monitored */ private FolderPanel folderPanel; /** Last folder which can be recalled on next startup */ private String lastRecallableFolder; /** * Creates a new FolderHistory instance which will keep track of visited folders in the given FolderPanel. */ public LocalLocationHistory(FolderPanel folderPanel) { this.folderPanel = folderPanel; } /** * Adds the specified folder to history. The folder won't be added if the previous folder is the same. * * <p>This method is called by FolderPanel each time a folder is changed. */ public void tryToAddToHistory(FileURL folderURL) { // Do not add folder to history if new current folder is the same as previous folder if (historyIndex<0 || !folderURL.equals(history.get(historyIndex), false, false)) addToHistory(folderURL); // Save last recallable folder on startup, only if : // - it is a directory on a local filesytem // - it doesn't look like a removable media drive (cd/dvd/floppy), especially in order to prevent // Java from triggering that dreaded 'Drive not ready' popup. LOGGER.trace("folder="+folderURL); if(folderURL.getScheme().equals(FileProtocols.FILE)) { AbstractFile folder = FileFactory.getFile(folderURL); if (folder.isDirectory() && (folder instanceof LocalFile) && !((LocalFile)folder.getRoot()).guessRemovableDrive()) { this.lastRecallableFolder = folder.getAbsolutePath(); LOGGER.trace("lastRecallableFolder= "+lastRecallableFolder); } } } private void addToHistory(FileURL folderURL) { int historySize = history.size(); historyIndex++; // Delete 'forward' history items if any for(int i=historyIndex; i<historySize; i++) { history.remove(historyIndex); } // If capacity is reached, remove first folder if(history.size()>=HISTORY_CAPACITY) { history.remove(0); historyIndex--; } // Add previous folder to history history.add(folderURL); } /** * Changes current folder to be the previous one in folder history. * Does nothing if there is no previous folder in history. */ public synchronized void goBack() { if (historyIndex==0) return; folderPanel.tryChangeCurrentFolder(history.get(--historyIndex)); } /** * Changes current folder to be the next one in folder history. * Does nothing if there is no next folder in history. */ public synchronized void goForward() { if (historyIndex==history.size()-1) return; folderPanel.tryChangeCurrentFolder(history.get(++historyIndex)); } /** * Returns <code>true</code> if there is at least one folder 'back' in the history. */ public boolean hasBackFolder() { return historyIndex>0; } /** * Returns <code>true</code> if there is at least one folder 'forward' in the history. */ public boolean hasForwardFolder() { return historyIndex!=history.size()-1; } /** * Returns a list of 'back' folders, most recently visited folder first. The returned array may be empty if there * currently isn't any 'back' folder in history, but may never be null. */ public FileURL[] getBackFolders() { if(!hasBackFolder()) return new FileURL[0]; int backLen = historyIndex; FileURL urls[] = new FileURL[backLen]; int cur = 0; for(int i=historyIndex-1; i>=0; i--) urls[cur++] = history.get(i); return urls; } /** * Returns a list of 'forward' folders, most recently visited folder first. The returned array may be empty if there * currently isn't any 'forward' folder in history, but may never be null. */ public FileURL[] getForwardFolders() { if(!hasForwardFolder()) return new FileURL[0]; int historySize = history.size(); FileURL urls[] = new FileURL[historySize-historyIndex-1]; int cur = 0; for(int i=historyIndex+1; i<historySize; i++) urls[cur++] = history.get(i); return urls; } /** * Returns true if the folder history contains the given FileURL, either as a back or forward folder, or as the * current folder. */ public boolean historyContains(FileURL folderURL) { return history.contains(folderURL); } /** * Returns the last visited folder that can be saved when the application terminates, and recalled next time * the application is started. * * <p>The returned folder will NOT be a folder on a remote filesystem * which would be likely not to be reachable next time the app is started, or a removable media drive * (cd/dvd/floppy) under Windows, which would trigger a nasty 'drive not ready' popup dialog if the drive * is not available or the media has changed. */ public String getLastRecallableFolder() { return this.lastRecallableFolder; } }