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