| /******************************************************************************* |
| * 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; |
| } |
| } |
| } |
| } |
| } |
| } |