/******************************************************************************* * Copyright (c) 2012-2017 Codenvy, S.A. * All rights reserved. This program and the accompanying materials * are made available under the terms of the Eclipse Public License v1.0 * which accompanies this distribution, and is available at * http://www.eclipse.org/legal/epl-v10.html * * Contributors: * Codenvy, S.A. - initial API and implementation *******************************************************************************/ package org.eclipse.che.ide.part.editor.recent; import com.google.inject.Inject; import com.google.inject.Singleton; import com.google.web.bindery.event.shared.EventBus; import org.eclipse.che.ide.api.action.ActionManager; import org.eclipse.che.ide.api.action.DefaultActionGroup; import org.eclipse.che.ide.api.action.IdeActions; import org.eclipse.che.ide.api.event.FileEvent; import org.eclipse.che.ide.api.resources.File; import org.eclipse.che.ide.api.resources.Resource; import org.eclipse.che.ide.api.resources.ResourceChangedEvent; import org.eclipse.che.ide.api.resources.VirtualFile; import org.eclipse.che.ide.util.Pair; import org.eclipse.che.ide.util.loging.Log; import java.util.Iterator; import java.util.LinkedList; import java.util.List; import static com.google.common.collect.Lists.newLinkedList; import static org.eclipse.che.ide.api.constraints.Constraints.FIRST; import static org.eclipse.che.ide.api.event.FileEvent.FileOperation.OPEN; import static org.eclipse.che.ide.api.resources.ResourceDelta.REMOVED; /** * Default implementation of Recent File List. * * @author Vlad Zhukovskiy */ @Singleton public class RecentFileStore implements RecentFileList { public static final int MAX_FILES_IN_STACK = 25; public static final int MAX_PATH_LENGTH_TO_DISPLAY = 50; public static final String RECENT_GROUP_ID = "Recent"; private final OpenRecentFilesPresenter openRecentFilesPresenter; private final ActionManager actionManager; private final RecentFileActionFactory recentFileActionFactory; private DefaultActionGroup recentGroup; private LinkedList<File> recentStorage = newLinkedList(); private LinkedList<Pair<File, RecentFileAction>> fileToAction = newLinkedList(); @Inject public RecentFileStore(EventBus eventBus, OpenRecentFilesPresenter openRecentFilesPresenter, ActionManager actionManager, RecentFileActionFactory recentFileActionFactory) { this.openRecentFilesPresenter = openRecentFilesPresenter; this.actionManager = actionManager; this.recentFileActionFactory = recentFileActionFactory; eventBus.addHandler(FileEvent.TYPE, event -> { if (event.getOperationType() == OPEN) { VirtualFile file = event.getFile(); if (file instanceof File) { add((File)file); } } }); eventBus.addHandler(ResourceChangedEvent.getType(), event -> { if (event.getDelta().getKind() != REMOVED) { return; } final Resource resource = event.getDelta().getResource(); if (!resource.isFile()) { return; } if (recentStorage.contains(resource.asFile())) { if (!remove(resource.asFile())) { Log.warn(getClass(), "File has not been removed from recent list"); } } }); } private void ensureGroupExist() { if (recentGroup == null) { recentGroup = (DefaultActionGroup)actionManager.getAction(IdeActions.GROUP_RECENT_FILES); } } /** {@inheritDoc} */ @Override public boolean isEmpty() { return recentStorage.isEmpty(); } /** {@inheritDoc} */ @Override public boolean add(final File item) { ensureGroupExist(); if (item == null || recentGroup == null) { return false; } //initial precondition if (recentStorage.size() == MAX_FILES_IN_STACK) { remove(recentStorage.getLast()); } remove(item); recentStorage.addFirst(item); openRecentFilesPresenter.setRecentFiles(getAll()); //register recent item action RecentFileAction action = recentFileActionFactory.newRecentFileAction(item); fileToAction.add(Pair.of(item, action)); actionManager.registerAction(action.getId(), action); recentGroup.add(action, FIRST); return true; } /** {@inheritDoc} */ @Override public boolean remove(File item) { if (recentGroup == null) { return false; } recentStorage.remove(item); openRecentFilesPresenter.setRecentFiles(getAll()); //with one cycle de-register action and remove it from recent group Iterator<Pair<File, RecentFileAction>> iterator = fileToAction.iterator(); while (iterator.hasNext()) { Pair<File, RecentFileAction> pair = iterator.next(); if (pair.getFirst().equals(item)) { recentGroup.remove(pair.getSecond()); actionManager.unregisterAction(pair.getSecond().getId()); iterator.remove(); return true; } } return false; } /** {@inheritDoc} */ @Override public boolean contains(File item) { return recentStorage.contains(item); } /** {@inheritDoc} */ @Override public List<File> getAll() { return recentStorage; } /** {@inheritDoc} */ @Override public void clear() { if (recentGroup == null) { return; } openRecentFilesPresenter.clearRecentFiles(); recentStorage.clear(); //de-register all previously registered actions for (Pair<File, RecentFileAction> pair : fileToAction) { actionManager.unregisterAction(pair.getSecond().getId()); recentGroup.remove(pair.getSecond()); } fileToAction.clear(); } /** {@inheritDoc} */ @Override public OpenRecentFilesPresenter getRecentViewDialog() { return openRecentFilesPresenter; } /** * Split path if it more then 50 characters. Otherwise, if path is less then 50 characters then it returns as is. * * @param path * path to check * @return path to display */ static String getShortPath(String path) { if (path.length() < MAX_PATH_LENGTH_TO_DISPLAY) { return path; } int bIndex = path.length() - MAX_PATH_LENGTH_TO_DISPLAY; String raw = path.substring(bIndex); if (raw.indexOf('/') == -1) { return raw; } raw = raw.substring(raw.indexOf('/')); raw = "..." + raw; return raw; } }