Package org.eclipse.ui.operations

Source Code of org.eclipse.ui.operations.NonLocalUndoUserApprover

/*******************************************************************************
* Copyright (c) 2005, 2006 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
*******************************************************************************/
package org.eclipse.ui.operations;

import java.util.ArrayList;

import org.eclipse.core.commands.operations.IAdvancedUndoableOperation;
import org.eclipse.core.commands.operations.IOperationApprover;
import org.eclipse.core.commands.operations.IOperationHistory;
import org.eclipse.core.commands.operations.IUndoContext;
import org.eclipse.core.commands.operations.IUndoableOperation;
import org.eclipse.core.runtime.IAdaptable;
import org.eclipse.core.runtime.IStatus;
import org.eclipse.core.runtime.Status;
import org.eclipse.jface.dialogs.IDialogConstants;
import org.eclipse.jface.dialogs.MessageDialog;
import org.eclipse.osgi.util.NLS;
import org.eclipse.ui.IEditorPart;
import org.eclipse.ui.internal.Workbench;
import org.eclipse.ui.internal.WorkbenchMessages;
import org.eclipse.ui.internal.util.Util;

/**
* <p>
* An operation approver that prompts the user to see if a non-local undo should
* proceed inside an editor. A non-local undo is detected when an operation
* being undone or redone affects elements other than those described by the
* editor itself. Clients can optionally specify a class, the preferred
* comparison class, that should be used when comparing objects affected by the
* editor with objects affected by an undo or redo operation. Comparisons
* between the affected objects inside the editor and those described by the
* operation will first be done by simply performing an equality check, using
* {@link java.lang.Object#equals(java.lang.Object)}. If an object described by
* an operation is not equal to one of the objects affected by the editor, and
* if it is not an instance of the preferred comparison class, but is an
* instance of {@link org.eclipse.core.runtime.IAdaptable}, then the operation
* approver will also attempt to retrieve an adapter on that object for the
* preferred comparison class and perform a second equality check using the
* adapter.
* </p>
* <p>
* This class may be instantiated by clients.
* </p>
*
*
* @since 3.1
*/
public final class NonLocalUndoUserApprover implements IOperationApprover {

  private IUndoContext context;

  private IEditorPart part;

  private Object[] elements;

  private Class affectedObjectsClass;

  private ArrayList elementsAndAdapters;

  /**
   * Create a NonLocalUndoUserApprover associated with the specified editor
   * and undo context
   *
   * @param context
   *            the undo context of operations in question.
   * @param part
   *            the editor part that is displaying the element
   * @param affectedObjects
   *            the objects that are affected by the editor and considered to
   *            be objects local to the editor. The objects are typically
   *            instances of the preferredComparisonClass or else provide
   *            adapters for the preferredComparisonClass, although this is
   *            not required.
   * @param preferredComparisonClass
   *            the preferred class to be used when comparing the editor's
   *            affectedObjects with those provided by the undoable operation
   *            using
   *            {@link org.eclipse.core.commands.operations.IAdvancedUndoableOperation#getAffectedObjects()}.
   *            If the operation's affected objects are not instances of the
   *            specified class, but are instances of
   *            {@link org.eclipse.core.runtime.IAdaptable}, then an adapter
   *            for this class will be requested. The preferredComparisonClass
   *            may be <code>null</code>, which indicates that there is no
   *            expected class or adapter necessary for the comparison.
   */
  public NonLocalUndoUserApprover(IUndoContext context, IEditorPart part,
      Object[] affectedObjects, Class preferredComparisonClass) {
    super();
    this.context = context;
    this.part = part;
    this.affectedObjectsClass = preferredComparisonClass;
    this.elements = affectedObjects;
  }

  /*
   * (non-Javadoc)
   *
   * @see org.eclipse.core.commands.operations.IOperationApprover#proceedRedoing(org.eclipse.core.commands.operations.IUndoableOperation,
   *      org.eclipse.core.commands.operations.IOperationHistory,
   *      org.eclipse.core.runtime.IAdaptable)
   */
  public IStatus proceedRedoing(IUndoableOperation operation,
      IOperationHistory history, IAdaptable uiInfo) {

    // return immediately if the operation is not relevant
    if (!requiresApproval(operation, uiInfo)) {
      return Status.OK_STATUS;
    }

    String message = NLS.bind(
        WorkbenchMessages.Operations_nonLocalRedoWarning, operation
            .getLabel(), part.getEditorInput().getName());
    return proceedWithOperation(operation, message, WorkbenchMessages.Operations_discardRedo);
  }

  /*
   * (non-Javadoc)
   *
   * @see org.eclipse.core.commands.operations.IOperationApprover#proceedUndoing(org.eclipse.core.commands.operations.IUndoableOperation,
   *      org.eclipse.core.commands.operations.IOperationHistory,
   *      org.eclipse.core.runtime.IAdaptable)
   */
  public IStatus proceedUndoing(IUndoableOperation operation,
      IOperationHistory history, IAdaptable uiInfo) {

    // return immediately if the operation is not relevant
    if (!requiresApproval(operation, uiInfo)) {
      return Status.OK_STATUS;
    }

    String message = NLS.bind(
        WorkbenchMessages.Operations_nonLocalUndoWarning, operation
            .getLabel(), part.getEditorInput().getName());
    return proceedWithOperation(operation, message, WorkbenchMessages.Operations_discardUndo);

  }

  /*
   * Determine whether the operation in question affects elements outside of
   * the editor. If this can be determined and it does affect other elements,
   * prompt the user as to whether the operation should proceed.
   */
  private IStatus proceedWithOperation(IUndoableOperation operation,
      final String message, final String discardButton) {

    // if the operation cannot tell us about its modified elements, there's
    // nothing we can do.
    if (!(operation instanceof IAdvancedUndoableOperation)) {
      return Status.OK_STATUS;
    }

    // Obtain the operation's affected objects.
    Object[] modifiedElements = ((IAdvancedUndoableOperation) operation)
        .getAffectedObjects();

    // Since the operation participates in describing its affected objects,
    // we assume for the rest of this method that an inability to
    // determine a match implies that a non-local operation is occurring.
    // This is a conservative assumption that provides more user prompting.

    boolean local;
    if (modifiedElements == null) {
      // The operation could not determine which elements are affected.
      // Consider the operation non-local.
      local = false;
    } else {
      // The operation answered some array of affected objects. Consider
      // the operation local until a non-match is found. Note that an
      // empty
      // array of affected objects is considered a local change.
      local = true;
      for (int i = 0; i < modifiedElements.length; i++) {
        Object modifiedElement = modifiedElements[i];
        if (!elementsContains(modifiedElement)) {
          // the modified element is not known by the editor
          local = false;
          // one last try - try to adapt the modified element if a
          // preferred
          // comparison class has been provided.
          if (affectedObjectsClass != null) {
            Object adapter = Util.getAdapter(modifiedElement,
                affectedObjectsClass);
            if (adapter != null && elementsContains(adapter)) {
              local = true;
            }
          }
          // if the element did not match the affected objects, no
          // need to check any others.
          if (!local) {
            break;
          }
        }
      }
    }
    if (local) {
      return Status.OK_STATUS;
    }

    // The operation affects more than just our element.  Find out if
    // we should proceed, cancel, or discard the undo.  Must be done in
    // a syncExec because operation approval notifications may come from
    // a background thread.
    final int[] answer = new int[1];
    Workbench.getInstance().getDisplay().syncExec(new Runnable() {
      public void run() {
        MessageDialog dialog = new MessageDialog(part.getSite().getShell(), part.getEditorInput().getName(),
            null, message, MessageDialog.QUESTION, new String[] { IDialogConstants.OK_LABEL,
                            discardButton, IDialogConstants.CANCEL_LABEL }, 0); // yes is the default
            answer[0] = dialog.open();
    }});
    switch (answer[0]) {
    case 0:
      return Status.OK_STATUS;
    case 1:
      return IOperationHistory.OPERATION_INVALID_STATUS;
    default:
      // Cancel by default to include ESC key and shell close,
      // which return SWT.DEFAULT, and any other unexpected return codes
      return Status.CANCEL_STATUS;
    }
  }

  /*
   * Answer whether this operation is relevant enough to this operation
   * approver that it should be examined in detail.
   */
  private boolean requiresApproval(IUndoableOperation operation,
      IAdaptable uiInfo) {
    // no approval is required if the operation doesn't have our undo
    // context
    if (!(operation.hasContext(context))) {
      return false;
    }

    // no approval is required if the operation only has our context
    if (operation.getContexts().length == 1) {
      return false;
    }

    // no approval is required if we can ascertain that the operation did
    // not originate
    // in our context.
    if (uiInfo != null) {
      IUndoContext originatingContext = (IUndoContext) Util.getAdapter(uiInfo,
          IUndoContext.class);
      if (originatingContext != null
          && !(originatingContext.matches(context))) {
        return false;
      }
    }

    return true;
  }

  /*
   * Return whether or not the collection of editor elements plus any of their
   * adapters contains the specified object.
   */
  private boolean elementsContains(Object someObject) {
    if (elements == null) {
      return false;
    }
    if (elementsAndAdapters == null) {
      // Compute a list of not just the elements, but any adapters they
      // may provide on the preferred class if they are not instances of
      // the preferred class. This is done only once.
      elementsAndAdapters = new ArrayList(elements.length);
      for (int i = 0; i < elements.length; i++) {
        Object element = elements[i];
        elementsAndAdapters.add(element);
        if (affectedObjectsClass != null
            && !affectedObjectsClass.isInstance(element)) {
          Object adapter = Util.getAdapter(element, affectedObjectsClass);
          if (adapter != null) {
            elementsAndAdapters.add(adapter);
          }
        }
      }
    }
    for (int i = 0; i < elementsAndAdapters.size(); i++) {
      if (elementsAndAdapters.get(i).equals(someObject)) {
        return true;
      }
    }
    return false;
  }
}
TOP

Related Classes of org.eclipse.ui.operations.NonLocalUndoUserApprover

TOP
Copyright © 2018 www.massapi.com. All rights reserved.
All source code are property of their respective owners. Java is a trademark of Sun Microsystems, Inc and owned by ORACLE Inc. Contact coftware#gmail.com.