| /******************************************************************************* |
| * Copyright (c) 2000, 2003 IBM Corporation and others. |
| * All rights reserved. This program and the accompanying materials |
| * are made available under the terms of the Common Public License v1.0 |
| * which accompanies this distribution, and is available at |
| * http://www.eclipse.org/legal/cpl-v10.html |
| * |
| * Contributors: |
| * IBM Corporation - initial API and implementation |
| *******************************************************************************/ |
| package org.eclipse.ui.actions; |
| |
| import java.lang.reflect.InvocationTargetException; |
| import java.text.MessageFormat; |
| import java.util.ArrayList; |
| import java.util.Iterator; |
| import java.util.List; |
| |
| import org.eclipse.core.resources.IProject; |
| import org.eclipse.core.resources.IResource; |
| import org.eclipse.core.resources.IResourceStatus; |
| import org.eclipse.core.runtime.CoreException; |
| import org.eclipse.core.runtime.IProgressMonitor; |
| import org.eclipse.core.runtime.IStatus; |
| import org.eclipse.core.runtime.MultiStatus; |
| import org.eclipse.core.runtime.OperationCanceledException; |
| import org.eclipse.core.runtime.Status; |
| import org.eclipse.core.runtime.SubProgressMonitor; |
| |
| import org.eclipse.swt.SWT; |
| import org.eclipse.swt.events.SelectionAdapter; |
| import org.eclipse.swt.events.SelectionEvent; |
| import org.eclipse.swt.events.SelectionListener; |
| import org.eclipse.swt.layout.GridLayout; |
| import org.eclipse.swt.widgets.Button; |
| import org.eclipse.swt.widgets.Composite; |
| import org.eclipse.swt.widgets.Control; |
| import org.eclipse.swt.widgets.Shell; |
| |
| import org.eclipse.jface.dialogs.ErrorDialog; |
| import org.eclipse.jface.dialogs.IDialogConstants; |
| import org.eclipse.jface.dialogs.MessageDialog; |
| import org.eclipse.jface.dialogs.ProgressMonitorDialog; |
| import org.eclipse.jface.viewers.IStructuredSelection; |
| |
| import org.eclipse.ui.PlatformUI; |
| import org.eclipse.ui.help.WorkbenchHelp; |
| |
| import org.eclipse.ui.internal.ide.IDEWorkbenchMessages; |
| import org.eclipse.ui.internal.ide.IDEWorkbenchPlugin; |
| import org.eclipse.ui.internal.ide.IHelpContextIds; |
| |
| /** |
| * Standard action for deleting the currently selected resources. |
| * <p> |
| * This class may be instantiated; it is not intended to be subclassed. |
| * </p> |
| */ |
| public class DeleteResourceAction extends SelectionListenerAction { |
| |
| static class DeleteProjectDialog extends MessageDialog { |
| |
| private List projects; |
| private boolean deleteContent = false; |
| private Button radio1; |
| private Button radio2; |
| |
| DeleteProjectDialog(Shell parentShell, List projects) { |
| super( |
| parentShell, |
| getTitle(projects), |
| null, // accept the default window icon |
| getMessage(projects), |
| MessageDialog.QUESTION, |
| new String[] {IDialogConstants.YES_LABEL, IDialogConstants.NO_LABEL}, |
| 0); // yes is the default |
| this.projects = projects; |
| } |
| |
| static String getTitle(List projects) { |
| if (projects.size() == 1) |
| return IDEWorkbenchMessages.getString("DeleteResourceAction.titleProject1"); //$NON-NLS-1$ |
| else |
| return IDEWorkbenchMessages.getString("DeleteResourceAction.titleProjectN"); //$NON-NLS-1$ |
| } |
| |
| static String getMessage(List projects) { |
| if (projects.size() == 1) { |
| IProject project = (IProject) projects.get(0); |
| return IDEWorkbenchMessages.format("DeleteResourceAction.confirmProject1", new Object[] { project.getName() }); //$NON-NLS-1$ |
| } |
| else { |
| return IDEWorkbenchMessages.format("DeleteResourceAction.confirmProjectN", new Object[] { new Integer(projects.size()) }); //$NON-NLS-1$ |
| } |
| } |
| |
| /* (non-Javadoc) |
| * Method declared on Window. |
| */ |
| protected void configureShell(Shell newShell) { |
| super.configureShell(newShell); |
| WorkbenchHelp.setHelp(newShell, IHelpContextIds.DELETE_PROJECT_DIALOG); |
| } |
| |
| protected Control createCustomArea(Composite parent) { |
| Composite composite = new Composite(parent, SWT.NONE); |
| composite.setLayout(new GridLayout()); |
| radio1 = new Button(composite, SWT.RADIO); |
| radio1.addSelectionListener(selectionListener); |
| String text1; |
| if (projects.size() == 1) { |
| IProject project = (IProject) projects.get(0); |
| if(project == null || project.getLocation() == null) |
| text1 = IDEWorkbenchMessages.getString("DeleteResourceAction.deleteContentsN"); //$NON-NLS-1$ |
| else |
| text1 = IDEWorkbenchMessages.format("DeleteResourceAction.deleteContents1", new Object[] { project.getLocation().toOSString() }); //$NON-NLS-1$ |
| } else { |
| text1 = IDEWorkbenchMessages.getString("DeleteResourceAction.deleteContentsN"); //$NON-NLS-1$ |
| } |
| radio1.setText(text1); |
| radio1.setFont(parent.getFont()); |
| |
| radio2 = new Button(composite, SWT.RADIO); |
| radio2.addSelectionListener(selectionListener); |
| String text2 = IDEWorkbenchMessages.getString("DeleteResourceAction.doNotDeleteContents"); //$NON-NLS-1$ |
| radio2.setText(text2); |
| radio2.setFont(parent.getFont()); |
| |
| // set initial state |
| radio1.setSelection(deleteContent); |
| radio2.setSelection(!deleteContent); |
| |
| return composite; |
| } |
| |
| private SelectionListener selectionListener = new SelectionAdapter() { |
| public void widgetSelected(SelectionEvent e) { |
| Button button = (Button) e.widget; |
| if (button.getSelection()) { |
| deleteContent = (button == radio1); |
| } |
| } |
| }; |
| |
| public boolean getDeleteContent() { |
| return deleteContent; |
| } |
| } |
| |
| /** |
| * The id of this action. |
| */ |
| public static final String ID = PlatformUI.PLUGIN_ID + ".DeleteResourceAction";//$NON-NLS-1$ |
| |
| /** |
| * The shell in which to show any dialogs. |
| */ |
| private Shell shell; |
| |
| /** |
| * Whether or not we are deleting content for projects. |
| */ |
| private boolean deleteContent = false; |
| |
| /** |
| * Whether or not to automatically delete out of sync resources |
| */ |
| private boolean forceOutOfSyncDelete = false; |
| /** |
| * Creates a new delete resource action. |
| * |
| * @param shell the shell for any dialogs |
| */ |
| public DeleteResourceAction(Shell shell) { |
| super(IDEWorkbenchMessages.getString("DeleteResourceAction.text")); //$NON-NLS-1$ |
| setToolTipText(IDEWorkbenchMessages.getString("DeleteResourceAction.toolTip")); //$NON-NLS-1$ |
| WorkbenchHelp.setHelp(this, IHelpContextIds.DELETE_RESOURCE_ACTION); |
| setId(ID); |
| if (shell == null) { |
| throw new IllegalArgumentException(); |
| } |
| this.shell = shell; |
| } |
| /** |
| * Returns whether delete can be performed on the current selection. |
| * |
| * @return <code>true</code> if the resources can be deleted, and |
| * <code>false</code> if the selection contains non-resources or phantom |
| * resources |
| */ |
| boolean canDelete() { |
| // allow only projects or only non-projects to be selected; |
| // note that the selection may contain multiple types of resource |
| if (!(containsOnlyProjects() || containsOnlyNonProjects())) { |
| return false; |
| } |
| |
| List resources = getSelectedResources(); |
| if (resources.size() == 0) return false; |
| // Return true if everything in the selection exists. |
| for (Iterator e = resources.iterator(); e.hasNext();) { |
| IResource next = (IResource)e.next(); |
| if (next.isPhantom()) { |
| return false; |
| } |
| } |
| return true; |
| } |
| /** |
| * Returns whether the selection contains linked resources. |
| * |
| * @return <code>true</code> if the resources contain linked |
| * resources, and <code>false</code> otherwise |
| */ |
| boolean containsLinkedResource() { |
| Iterator iterator = getSelectedResources().iterator(); |
| while (iterator.hasNext()) { |
| IResource resource = (IResource) iterator.next(); |
| if (resource.isLinked()) return true; |
| } |
| return false; |
| } |
| /** |
| * Returns whether the selection contains only non-projects. |
| * |
| * @return <code>true</code> if the resources contains only non-projects, and |
| * <code>false</code> otherwise |
| */ |
| boolean containsOnlyNonProjects() { |
| if (getSelectedNonResources().size() > 0) return false; |
| int types = getSelectedResourceTypes(); |
| // check for empty selection |
| if (types == 0) return false; |
| // note that the selection may contain multiple types of resource |
| return (types & IResource.PROJECT) == 0; |
| } |
| /** |
| * Returns whether the selection contains only projects. |
| * |
| * @return <code>true</code> if the resources contains only projects, and |
| * <code>false</code> otherwise |
| */ |
| boolean containsOnlyProjects() { |
| if (getSelectedNonResources().size() > 0) return false; |
| int types = getSelectedResourceTypes(); |
| // note that the selection may contain multiple types of resource |
| return types == IResource.PROJECT; |
| } |
| |
| /** |
| * Asks the user to confirm a delete operation. |
| * |
| * @return <code>true</code> if the user says to go ahead, and <code>false</code> |
| * if the deletion should be abandoned |
| */ |
| boolean confirmDelete() { |
| if (containsOnlyProjects()) { |
| return confirmDeleteProjects(); |
| } |
| else { |
| return confirmDeleteNonProjects(); |
| } |
| } |
| /** |
| * Asks the user to confirm a delete operation, |
| * where the selection contains no projects. |
| * |
| * @return <code>true</code> if the user says to go ahead, and <code>false</code> |
| * if the deletion should be abandoned |
| */ |
| boolean confirmDeleteNonProjects() { |
| List resources = getSelectedResources(); |
| String title; |
| String msg; |
| if (resources.size() == 1) { |
| title = IDEWorkbenchMessages.getString("DeleteResourceAction.title1"); //$NON-NLS-1$ |
| IResource resource = (IResource) resources.get(0); |
| if (resource.isLinked()) |
| msg = IDEWorkbenchMessages.format("DeleteResourceAction.confirmLinkedResource1", new Object[] { resource.getName() }); //$NON-NLS-1$ |
| else |
| msg = IDEWorkbenchMessages.format("DeleteResourceAction.confirm1", new Object[] { resource.getName() }); //$NON-NLS-1$ |
| } |
| else { |
| title = IDEWorkbenchMessages.getString("DeleteResourceAction.titleN"); //$NON-NLS-1$ |
| if (containsLinkedResource()) |
| msg = IDEWorkbenchMessages.format("DeleteResourceAction.confirmLinkedResourceN", new Object[] { new Integer(resources.size()) }); //$NON-NLS-1$ |
| else |
| msg = IDEWorkbenchMessages.format("DeleteResourceAction.confirmN", new Object[] { new Integer(resources.size()) }); //$NON-NLS-1$ |
| } |
| return MessageDialog.openQuestion(shell, title, msg); |
| } |
| /** |
| * Asks the user to confirm a delete operation, |
| * where the selection contains only projects. |
| * Also remembers whether project content should be deleted. |
| * |
| * @return <code>true</code> if the user says to go ahead, and <code>false</code> |
| * if the deletion should be abandoned |
| */ |
| boolean confirmDeleteProjects() { |
| List resources = getSelectedResources(); |
| DeleteProjectDialog dialog = new DeleteProjectDialog(shell, resources); |
| int code = dialog.open(); |
| deleteContent = dialog.getDeleteContent(); |
| return code == 0; // YES |
| } |
| /** |
| * Deletes the given resources. |
| */ |
| void delete(IResource[] resourcesToDelete, IProgressMonitor monitor) throws CoreException { |
| final List exceptions = new ArrayList(); |
| forceOutOfSyncDelete = false; |
| monitor.beginTask("", resourcesToDelete.length); //$NON-NLS-1$ |
| for (int i = 0; i < resourcesToDelete.length; ++i) { |
| if (monitor.isCanceled()) { |
| throw new OperationCanceledException(); |
| } |
| try { |
| delete(resourcesToDelete[i], new SubProgressMonitor(monitor, 1, SubProgressMonitor.PREPEND_MAIN_LABEL_TO_SUBTASK)); |
| } catch (CoreException e) { |
| exceptions.add(e); |
| } |
| } |
| |
| // Check to see if any problems occurred during processing. |
| final int exceptionCount = exceptions.size(); |
| if (exceptionCount == 1) { |
| throw (CoreException) exceptions.get(0); |
| } else if (exceptionCount > 1) { |
| final MultiStatus multi = new MultiStatus(IDEWorkbenchPlugin.IDE_WORKBENCH, 0, IDEWorkbenchMessages.getString("DeleteResourceAction.deletionExceptionMessage"), new Exception()); //$NON-NLS-1$ |
| for (int i = 0; i < exceptionCount; i++) { |
| CoreException exception = (CoreException) exceptions.get(0); |
| IStatus status = exception.getStatus(); |
| multi.add(new Status(status.getSeverity(), status.getPlugin(), status.getCode(), status.getMessage(), exception)); |
| } |
| throw new CoreException(multi); |
| } |
| |
| // Signal that the job has completed successfully. |
| monitor.done(); |
| } |
| /** |
| * Deletes the given resource. |
| */ |
| void delete(IResource resourceToDelete, IProgressMonitor monitor) throws CoreException { |
| boolean force = false; // don't force deletion of out-of-sync resources |
| try { |
| if (resourceToDelete.getType() == IResource.PROJECT) { |
| // if it's a project, ask whether content should be deleted too |
| IProject project = (IProject) resourceToDelete; |
| project.delete(deleteContent, force, monitor); |
| } |
| else { |
| // if it's not a project, just delete it |
| resourceToDelete.delete(IResource.KEEP_HISTORY, monitor); |
| } |
| } |
| catch (CoreException exception) { |
| if (resourceToDelete.getType() == IResource.FILE) { |
| IStatus[] children = exception.getStatus().getChildren(); |
| |
| if (children.length == 1 && |
| children[0].getCode() == IResourceStatus.OUT_OF_SYNC_LOCAL) { |
| if (forceOutOfSyncDelete) { |
| resourceToDelete.delete(IResource.KEEP_HISTORY | IResource.FORCE, monitor); |
| } |
| else { |
| int result = queryDeleteOutOfSync(resourceToDelete); |
| |
| if (result == IDialogConstants.YES_ID) { |
| resourceToDelete.delete(IResource.KEEP_HISTORY | IResource.FORCE, monitor); |
| } |
| else |
| if (result == IDialogConstants.YES_TO_ALL_ID) { |
| forceOutOfSyncDelete = true; |
| resourceToDelete.delete(IResource.KEEP_HISTORY | IResource.FORCE, monitor); |
| } |
| else |
| if (result == IDialogConstants.CANCEL_ID) { |
| throw new OperationCanceledException(); |
| } |
| } |
| } |
| else { |
| throw exception; |
| } |
| } |
| else { |
| throw exception; |
| } |
| } |
| } |
| /** |
| * Return an array of the currently selected resources. |
| * |
| * @return the list of selected resources |
| */ |
| IResource[] getSelectedResourcesArray() { |
| List selection = getSelectedResources(); |
| IResource[] resources = new IResource[selection.size()]; |
| int i = 0; |
| for (Iterator e = selection.iterator(); e.hasNext();) |
| resources[i++] = (IResource) e.next(); |
| return resources; |
| } |
| /** |
| * Returns a bit-mask containing the types of resources in the selection. |
| */ |
| int getSelectedResourceTypes() { |
| int types = 0; |
| for (Iterator i = getSelectedResources().iterator(); i.hasNext();) { |
| IResource r = (IResource) i.next(); |
| types |= r.getType(); |
| } |
| return types; |
| } |
| /* (non-Javadoc) |
| * Method declared on IAction. |
| */ |
| public void run() { |
| if (!confirmDelete()) |
| return; |
| final IResource[] resourcesToDelete = getResourcesToDelete(); |
| |
| if (resourcesToDelete.length == 0) |
| return; |
| try { |
| WorkspaceModifyOperation op = new WorkspaceModifyOperation() { |
| protected void execute(IProgressMonitor monitor) throws CoreException { |
| delete(resourcesToDelete, monitor); |
| } |
| }; |
| new ProgressMonitorDialog(shell).run(true, true, op); |
| } catch (InvocationTargetException e) { |
| Throwable t = e.getTargetException(); |
| if (t instanceof CoreException) { |
| CoreException exception = (CoreException) t; |
| IStatus status = exception.getStatus(); |
| IStatus[] children = status.getChildren(); |
| boolean outOfSyncError = false; |
| |
| for (int i = 0; i < children.length; i++) { |
| if (children[i].getCode() == IResourceStatus.OUT_OF_SYNC_LOCAL) { |
| outOfSyncError = true; |
| break; |
| } |
| } |
| IDEWorkbenchPlugin.log(MessageFormat.format("Exception in {0}.run: {1}", new Object[] {getClass().getName(), t}), status);//$NON-NLS-1$ |
| if (outOfSyncError) { |
| ErrorDialog.openError( |
| shell, |
| IDEWorkbenchMessages.getString("DeleteResourceAction.errorTitle"), //$NON-NLS-1$ |
| IDEWorkbenchMessages.getString("DeleteResourceAction.outOfSyncError"), //$NON-NLS-1$ |
| status); |
| } |
| else { |
| ErrorDialog.openError( |
| shell, |
| IDEWorkbenchMessages.getString("DeleteResourceAction.errorTitle"), // no special message //$NON-NLS-1$ |
| null, status); |
| } |
| } |
| else { |
| // CoreExceptions are collected above, but unexpected runtime exceptions and errors may still occur. |
| IDEWorkbenchPlugin.log(MessageFormat.format("Exception in {0}.run: {1}", new Object[] {getClass().getName(), t}));//$NON-NLS-1$ |
| MessageDialog.openError( |
| shell, |
| IDEWorkbenchMessages.getString("DeleteResourceAction.messageTitle"), //$NON-NLS-1$ |
| IDEWorkbenchMessages.format("DeleteResourceAction.internalError", new Object[] {t.getMessage()})); //$NON-NLS-1$ |
| } |
| } catch (InterruptedException e) { |
| // just return |
| } |
| } |
| /** |
| * Returns the resources to delete based on the selection and their read-only status. |
| * |
| * @return the resources to delete |
| */ |
| IResource[] getResourcesToDelete() { |
| IResource[] selectedResources = getSelectedResourcesArray(); |
| |
| if (containsOnlyProjects() && !deleteContent) { |
| // We can just return the selection |
| return selectedResources; |
| } |
| |
| ReadOnlyStateChecker checker = |
| new ReadOnlyStateChecker( |
| this.shell, |
| IDEWorkbenchMessages.getString("DeleteResourceAction.title1"), //$NON-NLS-1$ |
| IDEWorkbenchMessages.getString("DeleteResourceAction.readOnlyQuestion")); //$NON-NLS-1$ |
| |
| return checker.checkReadOnlyResources(selectedResources); |
| } |
| /** |
| * The <code>DeleteResourceAction</code> implementation of this |
| * <code>SelectionListenerAction</code> method disables the action |
| * if the selection contains phantom resources or non-resources |
| */ |
| protected boolean updateSelection(IStructuredSelection selection) { |
| return super.updateSelection(selection) && canDelete(); |
| } |
| |
| /** |
| * Ask the user whether the given resource should be deleted |
| * despite being out of sync with the file system. |
| * @param resource the out of sync resource |
| * @return One of the IDialogConstants constants indicating which |
| * of the Yes, Yes to All, No, Cancel options has been selected by |
| * the user. |
| */ |
| private int queryDeleteOutOfSync(IResource resource) { |
| final MessageDialog dialog = |
| new MessageDialog( |
| shell, |
| IDEWorkbenchMessages.getString("DeleteResourceAction.messageTitle"), //$NON-NLS-1$ |
| null, |
| IDEWorkbenchMessages.format("DeleteResourceAction.outOfSyncQuestion", new Object[] {resource.getName()}), //$NON-NLS-1$ |
| MessageDialog.QUESTION, |
| new String[] { |
| IDialogConstants.YES_LABEL, |
| IDialogConstants.YES_TO_ALL_LABEL, |
| IDialogConstants.NO_LABEL, |
| IDialogConstants.CANCEL_LABEL }, |
| 0); |
| shell.getDisplay().syncExec(new Runnable() { |
| public void run() { |
| dialog.open(); |
| } |
| }); |
| int result = dialog.getReturnCode(); |
| if (result == 0) |
| return IDialogConstants.YES_ID; |
| if (result == 1) |
| return IDialogConstants.YES_TO_ALL_ID; |
| if (result == 2) |
| return IDialogConstants.NO_ID; |
| return IDialogConstants.CANCEL_ID; |
| } |
| } |