blob: a1ef56deb1923d2d7065b58eea931f3f25992b49 [file] [log] [blame]
/*******************************************************************************
* Copyright (c) 2006, 2016 IBM Corporation and others.
* 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:
* IBM Corporation - initial API and implementation
* Dina Sayed, dsayed@eg.ibm.com, IBM - bug 269844
* Andrey Loskutov <loskutov@gmx.de> - generified interface, bug 462760
* Mickael Istria (Red Hat Inc.) - Bug 486901
*******************************************************************************/
package org.eclipse.ui.actions;
import java.util.ArrayList;
import java.util.Collections;
import java.util.List;
import org.eclipse.core.resources.IProject;
import org.eclipse.core.resources.IResource;
import org.eclipse.core.resources.IResourceChangeEvent;
import org.eclipse.core.resources.IResourceDelta;
import org.eclipse.core.resources.ResourcesPlugin;
import org.eclipse.core.runtime.CoreException;
import org.eclipse.jface.dialogs.IDialogConstants;
import org.eclipse.jface.dialogs.MessageDialogWithToggle;
import org.eclipse.jface.preference.IPreferenceStore;
import org.eclipse.jface.window.IShellProvider;
import org.eclipse.osgi.util.NLS;
import org.eclipse.swt.widgets.Shell;
import org.eclipse.ui.PlatformUI;
import org.eclipse.ui.ide.IDEActionFactory;
import org.eclipse.ui.internal.ide.IDEInternalPreferences;
import org.eclipse.ui.internal.ide.IDEWorkbenchMessages;
import org.eclipse.ui.internal.ide.IDEWorkbenchPlugin;
import org.eclipse.ui.internal.ide.IIDEHelpContextIds;
import org.eclipse.ui.internal.ide.misc.DisjointSet;
/**
* This action closes all projects that are unrelated to the selected projects. A
* project is unrelated if it is not directly or transitively referenced by one
* of the selected projects, and does not directly or transitively reference
* one of the selected projects.
* <p>
* This class may be instantiated; it is not intended to be subclassed.
* </p>
*
* @see IDEActionFactory#CLOSE_UNRELATED_PROJECTS
* @since 3.3
*/
public class CloseUnrelatedProjectsAction extends CloseResourceAction {
/**
* The id of this action.
*/
public static final String ID = PlatformUI.PLUGIN_ID + ".CloseUnrelatedProjectsAction"; //$NON-NLS-1$
private final List<IResource> projectsToClose = new ArrayList<>();
private boolean selectionDirty = true;
private List<? extends IResource> oldSelection = Collections.emptyList();
/**
* Builds the connected component set for the input projects.
* The result is a DisjointSet where all related projects belong
* to the same set.
*/
private static DisjointSet<IProject> buildConnectedComponents(IProject[] projects) {
//initially each vertex is in a set by itself
DisjointSet<IProject> set = new DisjointSet<>();
for (IProject project : projects) {
set.makeSet(project);
}
for (IProject project : projects) {
try {
IProject[] references = project.getReferencedProjects();
//each reference represents an edge in the project reference
//digraph from projects[i] -> references[j]
for (IProject reference : references) {
IProject setOne = set.findSet(project);
//note that referenced projects may not exist in the workspace
IProject setTwo = set.findSet(reference);
//these two projects are related, so join their sets
if (setOne != null && setTwo != null && setOne != setTwo) {
set.union(setOne, setTwo);
}
}
} catch (CoreException e) {
//assume inaccessible projects have no references
}
}
return set;
}
/**
* Creates this action.
*
* @param shell
* The shell to use for parenting any dialogs created by this
* action.
*
* @deprecated {@link #CloseUnrelatedProjectsAction(IShellProvider)}
*/
@Deprecated
public CloseUnrelatedProjectsAction(Shell shell) {
super(shell, IDEWorkbenchMessages.CloseUnrelatedProjectsAction_text);
initAction();
}
/**
* Creates this action.
*
* @param provider
* The shell to use for parenting any dialogs created by this
* action.
* @since 3.4
*/
public CloseUnrelatedProjectsAction(IShellProvider provider){
super(provider, IDEWorkbenchMessages.CloseUnrelatedProjectsAction_text);
initAction();
}
@Override
public void run() {
if (promptForConfirmation()) {
super.run();
}
}
/**
* Returns whether to close unrelated projects.
* Consults the preference and prompts the user if necessary.
*
* @return <code>true</code> if unrelated projects should be closed, and
* <code>false</code> otherwise.
*/
private boolean promptForConfirmation() {
IPreferenceStore store = IDEWorkbenchPlugin.getDefault().getPreferenceStore();
if (store.getBoolean(IDEInternalPreferences.CLOSE_UNRELATED_PROJECTS)) {
return true;
}
// get first project name
List<? extends IResource> selection = super.getSelectedResources();
int selectionSize = selection.size();
if (selectionSize == 0) {
return true;
}
String message = null;
if (selectionSize == 1) { // if one project is selected then print its name
IResource firstSelected = selection.get(0);
String projectName = null;
if (firstSelected instanceof IProject) {
projectName = ((IProject) firstSelected).getName();
}
message = NLS.bind(IDEWorkbenchMessages.CloseUnrelatedProjectsAction_confirmMsg1, projectName);
} else // if more then one project is selected then print there number
message = NLS.bind(IDEWorkbenchMessages.CloseUnrelatedProjectsAction_confirmMsgN, selectionSize);
MessageDialogWithToggle dialog = MessageDialogWithToggle.openOkCancelConfirm(
getShell(), IDEWorkbenchMessages.CloseUnrelatedProjectsAction_toolTip,
message, IDEWorkbenchMessages.CloseUnrelatedProjectsAction_AlwaysClose,
false, null, null);
if (dialog.getReturnCode() != IDialogConstants.OK_ID) {
return false;
}
store.setValue(IDEInternalPreferences.CLOSE_UNRELATED_PROJECTS, dialog.getToggleState());
return true;
}
/**
* Initializes for the constructor.
*/
private void initAction(){
setId(ID);
setToolTipText(IDEWorkbenchMessages.CloseUnrelatedProjectsAction_toolTip);
PlatformUI.getWorkbench().getHelpSystem().setHelp(this, IIDEHelpContextIds.CLOSE_UNRELATED_PROJECTS_ACTION);
}
@Override
protected void clearCache() {
super.clearCache();
oldSelection = Collections.emptyList();
selectionDirty = true;
}
/**
* Computes the related projects of the selection.
*/
private void computeRelated(List<? extends IResource> selection) {
//build the connected component set for all projects in the workspace
DisjointSet<IProject> set = buildConnectedComponents(ResourcesPlugin.getWorkspace().getRoot().getProjects());
//remove the connected components that the selected projects are in
for (IResource resource : selection) {
set.removeSet(resource);
}
//the remainder of the projects in the disjoint set are unrelated to the selection
projectsToClose.clear();
set.toList(projectsToClose);
}
@Override
protected List<? extends IResource> getSelectedResources() {
if (selectionDirty) {
List<? extends IResource> newSelection = super.getSelectedResources();
if (!oldSelection.equals(newSelection)) {
oldSelection = newSelection;
computeRelated(newSelection);
}
selectionDirty = false;
}
return projectsToClose;
}
/**
* Handles a resource changed event by updating the enablement
* when projects change.
* <p>
* This method overrides the super-type implementation to update
* the selection when the open state or description of any project changes.
*/
@Override
public void resourceChanged(IResourceChangeEvent event) {
// don't bother looking at delta if selection not applicable
if (selectionIsOfType(IResource.PROJECT)) {
IResourceDelta delta = event.getDelta();
if (delta != null) {
IResourceDelta[] projDeltas = delta.getAffectedChildren(IResourceDelta.CHANGED);
for (IResourceDelta projDelta : projDeltas) {
//changing either the description or the open state can affect enablement
if ((projDelta.getFlags() & (IResourceDelta.OPEN | IResourceDelta.DESCRIPTION)) != 0) {
selectionChanged(getStructuredSelection());
return;
}
}
}
}
}
}