DeleteResourceAction.java :  » IDE-Eclipse » ui-ide » org » eclipse » ui » actions » Java Open Source

Java Open Source » IDE Eclipse » ui ide 
ui ide » org » eclipse » ui » actions » DeleteResourceAction.java
/*******************************************************************************
 * Copyright (c) 2000, 2007 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
 *     Benjamin Muskalla <b.muskalla@gmx.net>
 *     - Fix for bug 172574 - [IDE] DeleteProjectDialog inconsequent selection behavior

 *******************************************************************************/
package org.eclipse.ui.actions;

import java.util.List;

import org.eclipse.core.commands.ExecutionException;
import org.eclipse.core.resources.IProject;
import org.eclipse.core.resources.IResource;
import org.eclipse.core.runtime.CoreException;
import org.eclipse.core.runtime.IProgressMonitor;
import org.eclipse.core.runtime.IStatus;
import org.eclipse.core.runtime.Status;
import org.eclipse.core.runtime.jobs.Job;
import org.eclipse.jface.dialogs.Dialog;
import org.eclipse.jface.dialogs.IDialogConstants;
import org.eclipse.jface.dialogs.MessageDialog;
import org.eclipse.jface.viewers.IStructuredSelection;
import org.eclipse.jface.window.Window;
import org.eclipse.osgi.util.NLS;
import org.eclipse.swt.SWT;
import org.eclipse.swt.events.MouseAdapter;
import org.eclipse.swt.events.MouseEvent;
import org.eclipse.swt.events.SelectionAdapter;
import org.eclipse.swt.events.SelectionEvent;
import org.eclipse.swt.events.SelectionListener;
import org.eclipse.swt.graphics.FontMetrics;
import org.eclipse.swt.graphics.GC;
import org.eclipse.swt.layout.GridData;
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.Label;
import org.eclipse.swt.widgets.Shell;
import org.eclipse.ui.PlatformUI;
import org.eclipse.ui.ide.undo.DeleteResourcesOperation;
import org.eclipse.ui.ide.undo.WorkspaceUndoUtil;
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.progress.WorkbenchJob;

/**
 * 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 IResource[] projects;

    private boolean deleteContent = false;

    /**
     * Control testing mode. In testing mode, it returns true to delete
     * contents and does not pop up the dialog.
     */
    private boolean fIsTesting = false;

    private Button radio1;

    private Button radio2;

    DeleteProjectDialog(Shell parentShell, IResource[] 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(IResource[] projects) {
      if (projects.length == 1) {
        return IDEWorkbenchMessages.DeleteResourceAction_titleProject1;
      }
      return IDEWorkbenchMessages.DeleteResourceAction_titleProjectN;
    }

    static String getMessage(IResource[] projects) {
      if (projects.length == 1) {
        IProject project = (IProject) projects[0];
        return NLS
            .bind(
                IDEWorkbenchMessages.DeleteResourceAction_confirmProject1,
                project.getName());
      }
      return NLS.bind(
          IDEWorkbenchMessages.DeleteResourceAction_confirmProjectN,
          new Integer(projects.length));
    }

    /*
     * (non-Javadoc) Method declared on Window.
     */
    protected void configureShell(Shell newShell) {
      super.configureShell(newShell);
      PlatformUI.getWorkbench().getHelpSystem().setHelp(newShell,
          IIDEHelpContextIds.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.length == 1) {
        IProject project = (IProject) projects[0];
        if (project == null || project.getLocation() == null) {
          text1 = IDEWorkbenchMessages.DeleteResourceAction_deleteContentsN;
        } else {
          text1 = NLS
              .bind(
                  IDEWorkbenchMessages.DeleteResourceAction_deleteContents1,
                  project.getLocation().toOSString());
        }
      } else {
        text1 = IDEWorkbenchMessages.DeleteResourceAction_deleteContentsN;
      }
      radio1.setText(text1);
      radio1.setFont(parent.getFont());
      
      // Add explanatory label that the action cannot be undone.
      // We can't put multi-line formatted text in a radio button,
      // so we have to create a separate label.
      Label detailsLabel = new Label(composite, SWT.LEFT);
      detailsLabel.setText(IDEWorkbenchMessages.DeleteResourceAction_deleteContentsDetails);
      detailsLabel.setFont(parent.getFont());
      // indent the explanatory label
      GC gc = new GC(detailsLabel);
      gc.setFont(detailsLabel.getParent().getFont());
      FontMetrics fontMetrics = gc.getFontMetrics();
      gc.dispose();
      GridData data = new GridData();
      data.horizontalIndent = Dialog.convertHorizontalDLUsToPixels(fontMetrics, IDialogConstants.INDENT);
      detailsLabel.setLayoutData(data);
      // add a listener so that clicking on the label selects the
      // corresponding radio button.
      // see https://bugs.eclipse.org/bugs/show_bug.cgi?id=172574
      detailsLabel.addMouseListener(new MouseAdapter() {
        public void mouseUp(MouseEvent e) {
          deleteContent = true;
          radio1.setSelection(deleteContent);
          radio2.setSelection(!deleteContent);
        }
      });
      // Add a spacer label
      new Label(composite, SWT.LEFT);

      radio2 = new Button(composite, SWT.RADIO);
      radio2.addSelectionListener(selectionListener);
      String text2 = IDEWorkbenchMessages.DeleteResourceAction_doNotDeleteContents;
      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);
        }
      }
    };

    boolean getDeleteContent() {
      return deleteContent;
    }

    /*
     * (non-Javadoc)
     * 
     * @see org.eclipse.jface.window.Window#open()
     */
    public int open() {
      // Override Window#open() to allow for non-interactive testing.
      if (fIsTesting) {
        deleteContent = true;
        return Window.OK;
      }
      return super.open();
    }

    /**
     * Set this delete dialog into testing mode. It won't pop up, and it
     * returns true for deleteContent.
     * 
     * @param t
     *            the testing mode
     */
    void setTestingMode(boolean t) {
      fIsTesting = t;
    }
  }

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

  /**
   * Flag that allows testing mode ... it won't pop up the project delete
   * dialog, and will return "delete all content".
   */
  protected boolean fTestingMode = false;

  private String[] modelProviderIds;

  /**
   * Creates a new delete resource action.
   * 
   * @param shell
   *            the shell for any dialogs
   */
  public DeleteResourceAction(Shell shell) {
    super(IDEWorkbenchMessages.DeleteResourceAction_text);
    setToolTipText(IDEWorkbenchMessages.DeleteResourceAction_toolTip);
    PlatformUI.getWorkbench().getHelpSystem().setHelp(this,
        IIDEHelpContextIds.DELETE_RESOURCE_ACTION);
    setId(ID);
    if (shell == null) {
      throw new IllegalArgumentException();
    }
    this.shell = shell;
  }

  /**
   * Returns whether delete can be performed on the current selection.
   * 
   * @param resources
   *            the selected resources
   * @return <code>true</code> if the resources can be deleted, and
   *         <code>false</code> if the selection contains non-resources or
   *         phantom resources
   */
  private boolean canDelete(IResource[] resources) {
    // allow only projects or only non-projects to be selected;
    // note that the selection may contain multiple types of resource
    if (!(containsOnlyProjects(resources) || containsOnlyNonProjects(resources))) {
      return false;
    }

    if (resources.length == 0) {
      return false;
    }
    // Return true if everything in the selection exists.
    for (int i = 0; i < resources.length; i++) {
      IResource resource = resources[i];
      if (resource.isPhantom()) {
        return false;
      }
    }
    return true;
  }

  /**
   * Returns whether the selection contains linked resources.
   * 
   * @param resources
   *            the selected resources
   * @return <code>true</code> if the resources contain linked resources,
   *         and <code>false</code> otherwise
   */
  private boolean containsLinkedResource(IResource[] resources) {
    for (int i = 0; i < resources.length; i++) {
      IResource resource = resources[i];
      if (resource.isLinked()) {
        return true;
      }
    }
    return false;
  }

  /**
   * Returns whether the selection contains only non-projects.
   * 
   * @param resources
   *            the selected resources
   * @return <code>true</code> if the resources contains only non-projects,
   *         and <code>false</code> otherwise
   */
  private boolean containsOnlyNonProjects(IResource[] resources) {
    int types = getSelectedResourceTypes(resources);
    // 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.
   * 
   * @param resources
   *            the selected resources
   * @return <code>true</code> if the resources contains only projects, and
   *         <code>false</code> otherwise
   */
  private boolean containsOnlyProjects(IResource[] resources) {
    int types = getSelectedResourceTypes(resources);
    // note that the selection may contain multiple types of resource
    return types == IResource.PROJECT;
  }

  /**
   * Asks the user to confirm a delete operation.
   * 
   * @param resources
   *            the selected resources
   * @return <code>true</code> if the user says to go ahead, and
   *         <code>false</code> if the deletion should be abandoned
   */
  private boolean confirmDelete(IResource[] resources) {
    if (containsOnlyProjects(resources)) {
      return confirmDeleteProjects(resources);
    }
    return confirmDeleteNonProjects(resources);
    
  }

  /**
   * Asks the user to confirm a delete operation, where the selection contains
   * no projects.
   * 
   * @param resources
   *            the selected resources
   * @return <code>true</code> if the user says to go ahead, and
   *         <code>false</code> if the deletion should be abandoned
   */
  private boolean confirmDeleteNonProjects(IResource[] resources) {
    String title;
    String msg;
    if (resources.length == 1) {
      title = IDEWorkbenchMessages.DeleteResourceAction_title1;
      IResource resource = resources[0];
      if (resource.isLinked()) {
        msg = NLS
            .bind(
                IDEWorkbenchMessages.DeleteResourceAction_confirmLinkedResource1,
                resource.getName());
      } else {
        msg = NLS.bind(
            IDEWorkbenchMessages.DeleteResourceAction_confirm1,
            resource.getName());
      }
    } else {
      title = IDEWorkbenchMessages.DeleteResourceAction_titleN;
      if (containsLinkedResource(resources)) {
        msg = NLS
            .bind(
                IDEWorkbenchMessages.DeleteResourceAction_confirmLinkedResourceN,
                new Integer(resources.length));
      } else {
        msg = NLS.bind(
            IDEWorkbenchMessages.DeleteResourceAction_confirmN,
            new Integer(resources.length));
      }
    }
    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.
   * 
   * @param resources
   *            the selected resources
   * @return <code>true</code> if the user says to go ahead, and
   *         <code>false</code> if the deletion should be abandoned
   */
  private boolean confirmDeleteProjects(IResource[] resources) {
    DeleteProjectDialog dialog = new DeleteProjectDialog(shell, resources);
    dialog.setTestingMode(fTestingMode);
    int code = dialog.open();
    deleteContent = dialog.getDeleteContent();
    return code == 0; // YES
  }




  /**
   * Return an array of the currently selected resources.
   * 
   * @return the selected resources
   */
  private IResource[] getSelectedResourcesArray() {
    List selection = getSelectedResources();
    IResource[] resources = new IResource[selection.size()];
    selection.toArray(resources);
    return resources;
  }

  /**
   * Returns a bit-mask containing the types of resources in the selection.
   * 
   * @param resources
   *            the selected resources
   */
  private int getSelectedResourceTypes(IResource[] resources) {
    int types = 0;
    for (int i = 0; i < resources.length; i++) {
      types |= resources[i].getType();
    }
    return types;
  }

  /*
   * (non-Javadoc) Method declared on IAction.
   */
  public void run() {
    final IResource[] resources = getSelectedResourcesArray();
    // WARNING: do not query the selected resources more than once
    // since the selection may change during the run,
    // e.g. due to window activation when the prompt dialog is dismissed.
    // For more details, see Bug 60606 [Navigator] (data loss) Navigator
    // deletes/moves the wrong file
    if (!confirmDelete(resources)) {
      return;
    }

    Job deletionCheckJob = new Job(IDEWorkbenchMessages.DeleteResourceAction_checkJobName) {

      /*
       * (non-Javadoc)
       * 
       * @see org.eclipse.core.runtime.jobs.Job#run(org.eclipse.core.runtime.IProgressMonitor)
       */
      protected IStatus run(IProgressMonitor monitor) {
        if (resources.length == 0)
          return Status.CANCEL_STATUS;
        scheduleDeleteJob(resources);
        return Status.OK_STATUS;
      }
      
      /*
       * (non-Javadoc)
       * 
       * @see org.eclipse.core.runtime.jobs.Job#belongsTo(java.lang.Object)
       */
      public boolean belongsTo(Object family) {
        if (IDEWorkbenchMessages.DeleteResourceAction_jobName
            .equals(family)) {
          return true;
        }
        return super.belongsTo(family);
      }
    };

    deletionCheckJob.schedule();

  }

  /**
   * Schedule a job to delete the resources to delete.
   * 
   * @param resourcesToDelete
   */
  private void scheduleDeleteJob(final IResource[] resourcesToDelete) {
    // use a non-workspace job with a runnable inside so we can avoid
    // periodic updates
    Job deleteJob = new Job(
        IDEWorkbenchMessages.DeleteResourceAction_jobName) {
      public IStatus run(final IProgressMonitor monitor) {
        try {
          final DeleteResourcesOperation op = 
            new DeleteResourcesOperation(resourcesToDelete, IDEWorkbenchMessages.DeleteResourceAction_operationLabel, deleteContent);
          op.setModelProviderIds(getModelProviderIds());
          // If we are deleting projects and their content, do not
          // execute the operation in the undo history, since it cannot be
          // properly restored.  Just execute it directly so it won't be
          // added to the undo history.
          if (deleteContent && containsOnlyProjects(resourcesToDelete)) {
            // We must compute the execution status first so that any user prompting
            // or validation checking occurs.  Do it in a syncExec because
            // we are calling this from a Job.
            WorkbenchJob statusJob = new WorkbenchJob("Status checking"){ //$NON-NLS-1$
              /* (non-Javadoc)
               * @see org.eclipse.ui.progress.UIJob#runInUIThread(org.eclipse.core.runtime.IProgressMonitor)
               */
              public IStatus runInUIThread(
                  IProgressMonitor monitor) {
                return op.computeExecutionStatus(monitor);
              }
              
            };
            
            statusJob.setSystem(true);
            statusJob.schedule();
            try {//block until the status is ready
              statusJob.join();
            } catch (InterruptedException e) {
              //Do nothing as status will be a cancel
            }
            
            if (statusJob.getResult().isOK()) {
              return op.execute(monitor, WorkspaceUndoUtil.getUIInfoAdapter(shell));
            } 
            return statusJob.getResult();
          }
          return PlatformUI.getWorkbench().getOperationSupport()
              .getOperationHistory().execute(op, monitor, 
              WorkspaceUndoUtil.getUIInfoAdapter(shell));
        } catch (ExecutionException e) {
          if (e.getCause() instanceof CoreException) {
            return ((CoreException)e.getCause()).getStatus();
          } 
          return new Status(IStatus.ERROR, IDEWorkbenchPlugin.IDE_WORKBENCH, e.getMessage(),e);
        }
      }

      /*
       * (non-Javadoc)
       * 
       * @see org.eclipse.core.runtime.jobs.Job#belongsTo(java.lang.Object)
       */
      public boolean belongsTo(Object family) {
        if (IDEWorkbenchMessages.DeleteResourceAction_jobName
            .equals(family)) {
          return true;
        }
        return super.belongsTo(family);
      }

    };
    deleteJob.setUser(true);
    deleteJob.schedule();
  }

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

  /**
   * Returns the model provider ids that are known to the client that
   * instantiated this operation.
   * 
   * @return the model provider ids that are known to the client that
   *         instantiated this operation.
   * @since 3.2
   */
  public String[] getModelProviderIds() {
    return modelProviderIds;
  }

  /**
   * Sets the model provider ids that are known to the client that
   * instantiated this operation. Any potential side effects reported by these
   * models during validation will be ignored.
   * 
   * @param modelProviderIds
   *            the model providers known to the client who is using this
   *            operation.
   * @since 3.2
   */
  public void setModelProviderIds(String[] modelProviderIds) {
    this.modelProviderIds = modelProviderIds;
  }
}
java2s.com  | Contact Us | Privacy Policy
Copyright 2009 - 12 Demo Source and Support. All rights reserved.
All other trademarks are property of their respective owners.