blob: 9a2a5a8313697f9950c05dc862f51a0f1a9e6707 [file] [log] [blame]
/*******************************************************************************
* Copyright (c) 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.internal.ide.undo;
import org.eclipse.core.commands.operations.IOperationHistory;
import org.eclipse.core.commands.operations.IOperationHistoryListener;
import org.eclipse.core.commands.operations.IUndoableOperation;
import org.eclipse.core.commands.operations.OperationHistoryEvent;
import org.eclipse.core.resources.IResourceChangeEvent;
import org.eclipse.core.resources.IResourceChangeListener;
import org.eclipse.core.resources.ResourcesPlugin;
import org.eclipse.core.runtime.IStatus;
import org.eclipse.ui.PlatformUI;
import org.eclipse.ui.ide.undo.AbstractWorkspaceOperation;
import org.eclipse.ui.ide.undo.WorkspaceUndoUtil;
import org.eclipse.ui.internal.ide.Policy;
/**
* WorkspaceUndoMonitor monitors the workspace for resource changes and
* periodically checks the undo history to make sure it is valid.
*
* This class is not intended to be instantiated or used by clients.
*
* @since 3.3
*
*/
public class WorkspaceUndoMonitor {
/**
* Singleton instance.
*/
private static WorkspaceUndoMonitor instance;
/**
* Number of workspace changes that will cause validation of undo history
*/
private static int CHANGE_THRESHHOLD = 10;
/**
* Prefix to use on debug info
*/
private static String DEBUG_PREFIX = "Workspace Undo Monitor: "; //$NON-NLS-1$
/**
* Get the singleton instance of this class.
*
* @return the singleton instance of this class.
*/
public static WorkspaceUndoMonitor getInstance() {
if (instance == null) {
instance = new WorkspaceUndoMonitor();
}
return instance;
}
/**
* Number of workspace changes that have occurred since the last undoable
* operation was executed, undone, or redone.
*/
private int numChanges = 0;
/**
* The IUndoableOperation in progress, or <code>null</code> if there is
* none in progress.
*/
private IUndoableOperation operationInProgress = null;
/**
* Resource listener used to determine how often to validate the workspace
* undo history.
*/
private IResourceChangeListener resourceListener;
/**
* Operation history listener used to determine whether there is an undoable
* operation in progress.
*/
private IOperationHistoryListener historyListener;
/**
* Construct an instance. Should only be called by {@link #getInstance()}
*/
private WorkspaceUndoMonitor() {
if (Policy.DEBUG_UNDOMONITOR) {
System.out.println(DEBUG_PREFIX + "Installing listeners"); //$NON-NLS-1$
}
resourceListener = getResourceChangeListener();
ResourcesPlugin.getWorkspace().addResourceChangeListener(
resourceListener);
historyListener = getOperationHistoryListener();
getOperationHistory().addOperationHistoryListener(historyListener);
}
/**
* Get a change listener for listening to resource changes.
*
* @return the resource change listeners
*/
private IResourceChangeListener getResourceChangeListener() {
return new IResourceChangeListener() {
/*
* (non-Javadoc)
*
* @see org.eclipse.core.resources.IResourceChangeListener#resourceChanged(org.eclipse.core.resources.IResourceChangeEvent)
*/
public void resourceChanged(IResourceChangeEvent event) {
// If there is an operation in progress, this event is to be
// ignored.
if (operationInProgress != null) {
return;
}
if (event.getType() == IResourceChangeEvent.POST_CHANGE
|| event.getType() == IResourceChangeEvent.POST_BUILD) {
// For now, we consider any change a change worth tracking.
// We can be more specific later if warranted.
incrementChangeCount();
if (numChanges >= CHANGE_THRESHHOLD) {
checkOperationHistory();
}
}
}
};
}
/**
* Get a change listener for listening to operation history changes.
*
* @return the resource change listeners
*/
private IOperationHistoryListener getOperationHistoryListener() {
return new IOperationHistoryListener() {
/*
* (non-Javadoc)
*
* @see org.eclipse.core.commands.operations.IOperationHistoryListener#historyNotification(org.eclipse.core.commands.operations.OperationHistoryEvent)
*/
public void historyNotification(OperationHistoryEvent event) {
switch (event.getEventType()) {
case OperationHistoryEvent.ABOUT_TO_EXECUTE:
case OperationHistoryEvent.ABOUT_TO_UNDO:
case OperationHistoryEvent.ABOUT_TO_REDO:
operationInProgress = event.getOperation();
break;
case OperationHistoryEvent.DONE:
case OperationHistoryEvent.UNDONE:
case OperationHistoryEvent.REDONE:
resetChangeCount();
operationInProgress = null;
break;
case OperationHistoryEvent.OPERATION_NOT_OK:
operationInProgress = null;
break;
}
}
};
}
/**
* Shutdown the workspace undo monitor. Unhooks the listeners.
*/
public void shutdown() {
if (Policy.DEBUG_UNDOMONITOR) {
System.out.println(DEBUG_PREFIX + "Shutting Down"); //$NON-NLS-1$
}
if (resourceListener != null) {
ResourcesPlugin.getWorkspace().removeResourceChangeListener(
resourceListener);
}
if (historyListener != null) {
getOperationHistory().removeOperationHistoryListener(
historyListener);
}
}
/**
* Get the operation history.
*/
private IOperationHistory getOperationHistory() {
return PlatformUI.getWorkbench().getOperationSupport()
.getOperationHistory();
}
/**
* Check the pending undoable operation to see if it is still valid.
*/
private void checkOperationHistory() {
if (Policy.DEBUG_UNDOMONITOR) {
System.out.println(DEBUG_PREFIX + "Checking Operation History..."); //$NON-NLS-1$
}
IUndoableOperation currentOp = getOperationHistory().getUndoOperation(
WorkspaceUndoUtil.getWorkspaceUndoContext());
// If there is no pending op, nothing to do.
if (currentOp == null) {
resetChangeCount();
return;
}
// First try the simple check
if (!currentOp.canUndo()) {
flushWorkspaceHistory(currentOp);
return;
}
// Now try a more advanced check. If the undoable status is definitely
// an error, flush the history. Anything less than an error status
// should be left alone so that the user can be prompted as to what
// should be done when an undo is actually attempted.
if (currentOp instanceof AbstractWorkspaceOperation) {
AbstractWorkspaceOperation op = (AbstractWorkspaceOperation) currentOp;
op.setQuietCompute(true);
IStatus status = op.computeUndoableStatus(null);
op.setQuietCompute(false);
if (status.getSeverity() == IStatus.ERROR) {
flushWorkspaceHistory(currentOp);
}
}
resetChangeCount();
}
/**
* Flush the undo and redo history for the workspace undo context.
*/
private void flushWorkspaceHistory(IUndoableOperation op) {
if (Policy.DEBUG_UNDOMONITOR) {
System.out.println(DEBUG_PREFIX + "Flushing undo history due to "+op); //$NON-NLS-1$
}
getOperationHistory().dispose(
WorkspaceUndoUtil.getWorkspaceUndoContext(), true, true, false);
}
/**
* Reset the workspace change count
*/
private void resetChangeCount() {
numChanges = 0;
if (Policy.DEBUG_UNDOMONITOR) {
System.out.println(DEBUG_PREFIX + "Resetting change count to 0"); //$NON-NLS-1$
}
}
/**
* Increment the workspace change count
*/
private void incrementChangeCount() {
numChanges++;
if (Policy.DEBUG_UNDOMONITOR) {
System.out.println(DEBUG_PREFIX + "Incrementing workspace change count. Count = "+numChanges); //$NON-NLS-1$
}
}
}