Code:
/ 4.0 / 4.0 / DEVDIV_TFS / Dev10 / Releases / RTMRel / wpf / src / Framework / MS / Internal / documents / UndoManager.cs / 1305600 / UndoManager.cs
//----------------------------------------------------------------------------
//
//
// Copyright (C) Microsoft Corporation. All rights reserved.
//
//
//
// Description:
//
// See spec at http://avalon/uis/Stock%20Services/Undo%20spec.htm
//
// History:
// 07/16/2003 : psarrett ported to WCP tree
// 03/21/2004 : eveselov - code style cleaned
//
//---------------------------------------------------------------------------
using System;
using System.Windows;
using System.Collections;
using System.Collections.Generic;
using System.ComponentModel;
using System.ComponentModel.Design;
using System.Diagnostics;
using MS.Utility;
using System.Windows.Markup;
using System.Windows.Documents;
using System.Windows.Controls.Primitives;
namespace MS.Internal.Documents
{
///
/// Enum for the state of the undo manager
///
///
internal enum UndoState
{
///
/// Ready to accept new undo units; not currently undoing or redoing
///
///
Normal,
///
/// In the process of undoing
///
///
Undo,
///
/// In the process of redoing
///
///
Redo,
///
/// In the process of rolling back an aborted undo unit
///
///
Rollback
};
///
/// Undo Manager
///
internal class UndoManager
{
//-----------------------------------------------------
//
// Constructors
//
//-----------------------------------------------------
#region Constructors
///
/// UndoManager constructor
///
internal UndoManager() : base()
{
_scope = null;
_state = UndoState.Normal;
_isEnabled = false;
_undoStack = new List(4);
_redoStack = new Stack(2);
_topUndoIndex = -1;
_bottomUndoIndex = 0;
_undoLimit = _undoLimitDefaultValue;
}
#endregion Constructors
//------------------------------------------------------
//
// Internal Methods
//
//-----------------------------------------------------
#region Internal Methods
///
/// Defines a given FrameworkElement as a scope for undo service.
/// New instance of UndoManager created and attached to this element.
///
///
/// FrameworkElement to which new instance of UndoManager is attached.
///
///
///
internal static void AttachUndoManager(DependencyObject scope, UndoManager undoManager)
{
if (scope == null)
{
throw new ArgumentNullException("scope");
}
if (undoManager == null)
{
throw new ArgumentNullException("undoManager");
}
if (undoManager is UndoManager && ((UndoManager)undoManager)._scope != null)
{
throw new InvalidOperationException(SR.Get(SRID.UndoManagerAlreadyAttached));
}
// Detach existing instance of undo manager if any
DetachUndoManager(scope);
// Attach the service to the scope via private dependency property
scope.SetValue(UndoManager.UndoManagerInstanceProperty, undoManager);
if (undoManager is UndoManager)
{
Debug.Assert(((UndoManager)undoManager)._scope == null);
((UndoManager)undoManager)._scope = scope;
}
undoManager.IsEnabled = true;
}
///
/// Detaches an undo service from the given FrameworkElement.
///
///
/// A FrameworkElement with UndoManager attached to it.
///
///
/// Throws an exception if the scope does not have undo service attached to it.
///
internal static void DetachUndoManager(DependencyObject scope)
{
UndoManager undoManager;
if (scope == null)
{
throw new ArgumentNullException("scope");
}
// Detach existing undo service if any
undoManager = scope.ReadLocalValue(UndoManager.UndoManagerInstanceProperty) as UndoManager;
if (undoManager != null)
{
// Disable the service while in detached state
undoManager.IsEnabled = false;
// Remove the service from a tre
scope.ClearValue(UndoManager.UndoManagerInstanceProperty);
// Break the linkage to its scope
if (undoManager is UndoManager)
{
Debug.Assert(((UndoManager)undoManager)._scope == scope);
((UndoManager)undoManager)._scope = null;
}
}
}
///
/// Finds the nearest undo service for a given uielement as a target.
///
///
/// A ui element which is a descendant (or self) of an element
/// to which undo service is attached.
///
///
internal static UndoManager GetUndoManager(DependencyObject target)
{
if (target == null)
{
return null;
}
else
{
return target.GetValue(UndoManager.UndoManagerInstanceProperty) as UndoManager;
}
}
///
/// Add the given parent undo unit to the undo manager, making it the OpenedUnit of the
/// innermost open parent unit (or the undo manager itself, if no parents are open).
///
///
/// parent unit to add
///
///
/// Thrown if UndoManager is disabled or passed unit is already open.
///
///
/// Thrown if passed unit is null.
///
internal void Open(IParentUndoUnit unit)
{
IParentUndoUnit deepestOpen;
if (!IsEnabled)
{
throw new InvalidOperationException(SR.Get(SRID.UndoServiceDisabled));
}
if (unit == null)
{
throw new ArgumentNullException("unit");
}
deepestOpen = DeepestOpenUnit;
if (deepestOpen == unit)
{
throw new InvalidOperationException(SR.Get(SRID.UndoUnitCantBeOpenedTwice));
}
if (deepestOpen == null)
{
if (unit != LastUnit)
{
// Don't want to add the unit again if we're just reopening it
Add(unit as IUndoUnit);
SetLastUnit(unit as IUndoUnit);
}
SetOpenedUnit(unit);
unit.Container = this;
}
else
{
unit.Container = deepestOpen;
deepestOpen.Open(unit);
}
}
///
/// Opens a closed undo unit on the top of the stack.
///
///
/// IParentUndoUnit to reopen
///
///
/// Thrown if:
/// UndoManager is disabled
/// another unit is already open
/// the given unit is locked
/// the given unit is not on top of the stack
///
///
/// Thrown if passed unit is null.
///
internal void Reopen(IParentUndoUnit unit)
{
if (!IsEnabled)
{
throw new InvalidOperationException(SR.Get(SRID.UndoServiceDisabled));
}
if (unit == null)
{
throw new ArgumentNullException("unit");
}
if (OpenedUnit != null)
{
throw new InvalidOperationException(SR.Get(SRID.UndoUnitAlreadyOpen));
}
switch (State)
{
case UndoState.Normal :
case UndoState.Redo :
{
if (UndoCount == 0 || PeekUndoStack() != unit)
{
throw new InvalidOperationException(SR.Get(SRID.UndoUnitNotOnTopOfStack));
}
break;
}
case UndoState.Undo :
{
if (RedoStack.Count == 0 || (IParentUndoUnit)RedoStack.Peek() != unit)
{
throw new InvalidOperationException(SR.Get(SRID.UndoUnitNotOnTopOfStack));
}
break;
}
case UndoState.Rollback :
default :
// should only happen if someone changes the UndoState enum or parameter validation
Debug.Assert(false);
break;
}
if (unit.Locked)
{
throw new InvalidOperationException(SR.Get(SRID.UndoUnitLocked));
}
Open(unit);
_lastReopenedUnit = unit;
}
///
/// Closes the current open unit, adding it to the containing unit's undo stack if committed.
///
internal void Close(UndoCloseAction closeAction)
{
Close(OpenedUnit, closeAction);
}
///
/// Closes an open child parent unit, adding it to the containing unit's undo stack if committed.
///
///
/// IParentUndoUnit to close. If NULL, this unit's OpenedUnit is closed.
///
///
///
///
/// Thrown if:
/// UndoManager is disabled
/// no undo unit is currently open
///
///
/// Thrown if unit is null
///
internal void Close(IParentUndoUnit unit, UndoCloseAction closeAction)
{
if (!IsEnabled)
{
throw new InvalidOperationException(SR.Get(SRID.UndoServiceDisabled));
}
if (unit == null)
{
throw new ArgumentNullException("unit");
}
if (OpenedUnit == null)
{
throw new InvalidOperationException(SR.Get(SRID.UndoNoOpenUnit));
}
// find the parent of the given unit
if (OpenedUnit != unit)
{
IParentUndoUnit closeParent;
closeParent = OpenedUnit;
while (closeParent.OpenedUnit != null && closeParent.OpenedUnit != unit)
{
closeParent = closeParent.OpenedUnit;
}
if (closeParent.OpenedUnit == null)
{
throw new ArgumentException(SR.Get(SRID.UndoUnitNotFound), "unit");
}
closeParent.Close(closeAction);
return;
}
//
// Close our open unit
//
if (closeAction != UndoCloseAction.Commit)
{
// discard unit
SetState(UndoState.Rollback);
if (unit.OpenedUnit != null)
{
unit.Close(closeAction);
}
if (closeAction == UndoCloseAction.Rollback)
{
unit.Do();
}
PopUndoStack();
SetOpenedUnit(null);
OnNextDiscard();
SetLastUnit(_topUndoIndex == -1 ? null : PeekUndoStack()); // can be null, which is fine
SetState(UndoState.Normal);
}
else
{
// commit unit
if (unit.OpenedUnit != null)
{
unit.Close(UndoCloseAction.Commit);
}
// flush redo stack
if (State != UndoState.Redo && State != UndoState.Undo && RedoStack.Count > 0)
{
RedoStack.Clear();
}
SetOpenedUnit(null);
}
}
///
/// Adds an undo unit to the undo/redo stack, depending on current state.
///
///
/// IUndoUnit to add
///
///
/// Thrown if:
/// UndoManager is disabled
/// unit is not IParentUndoUnit and there is no open IParentUndoUnit
///
///
/// Thrown if unit is null
///
internal void Add(IUndoUnit unit)
{
IParentUndoUnit parent;
if (!IsEnabled)
{
throw new InvalidOperationException(SR.Get(SRID.UndoServiceDisabled));
}
if (unit == null)
{
throw new ArgumentNullException("unit");
}
parent = DeepestOpenUnit;
if (parent != null)
{
parent.Add(unit);
}
else if (unit is IParentUndoUnit)
{
((IParentUndoUnit)unit).Container = this;
if (LastUnit is IParentUndoUnit)
{
((IParentUndoUnit)LastUnit).OnNextAdd();
}
SetLastUnit(unit);
if (State == UndoState.Normal || State == UndoState.Redo)
{
if (++_topUndoIndex == UndoLimit)
{
_topUndoIndex = 0;
}
if (!(_topUndoIndex < UndoStack.Count && PeekUndoStack() == null) // Non-null topmost stack item
&& (UndoLimit == -1 || UndoStack.Count < UndoLimit))
{
UndoStack.Add(unit);
}
else
{
if (PeekUndoStack() != null)
{
if (++_bottomUndoIndex == UndoLimit)
{
_bottomUndoIndex = 0;
}
}
UndoStack[_topUndoIndex] = unit;
}
}
else if (State == UndoState.Undo)
{
RedoStack.Push(unit);
}
else if (State == UndoState.Rollback)
{
// do nothing, throwing out the unit
}
}
else
{
throw new InvalidOperationException(SR.Get(SRID.UndoNoOpenParentUnit));
}
}
///
/// Clear the undo and redo stacks, as well as LastUnit.
///
///
/// Thrown if UndoManager is disabled
///
internal void Clear()
{
if (!IsEnabled)
{
throw new InvalidOperationException(SR.Get(SRID.UndoServiceDisabled));
}
// In practice, we only clear when the public IsUndoEnabled property is set false.
// We'll check that property again when _imeSupportModeEnabled transitions to false.
// While _imeSupportModeEnabled == true, we must leave undo enabled.
if (!_imeSupportModeEnabled)
{
DoClear();
}
}
///
/// Instructs the undo manager to invoke the given number of undo actions.
///
///
/// Number of undo units to undo
///
///
/// Thrown if UndoManager is disabled
///
///
/// Thrown if count is out of range
///
///
/// Thrown if there's an error performing the undo
///
internal void Undo(int count)
{
if (!IsEnabled)
{
throw new InvalidOperationException(SR.Get(SRID.UndoServiceDisabled));
}
if (count > UndoCount || count <= 0)
{
throw new ArgumentOutOfRangeException("count");
}
if (State != UndoState.Normal)
{
throw new InvalidOperationException(SR.Get(SRID.UndoNotInNormalState));
}
if (OpenedUnit != null)
{
throw new InvalidOperationException(SR.Get(SRID.UndoUnitOpen));
}
Invariant.Assert(UndoCount > _minUndoStackCount);
SetState(UndoState.Undo);
bool exceptionThrown = true;
try
{
while (count > 0)
{
IUndoUnit unit;
unit = PopUndoStack();
unit.Do();
count--;
}
exceptionThrown = false;
}
finally
{
if (exceptionThrown)
{
Clear();
}
}
SetState(UndoState.Normal);
}
///
/// Instructs the undo manager to invoke the given number of redo actions.
///
///
/// Number of redo units to redo
///
///
/// Thrown if UndoManager is disabled
///
///
/// Thrown if count is out of range
///
///
/// Thrown if there's an error performing the redo
///
internal void Redo(int count)
{
if (!IsEnabled)
{
throw new InvalidOperationException(SR.Get(SRID.UndoServiceDisabled));
}
if (count > RedoStack.Count || count <= 0)
{
throw new ArgumentOutOfRangeException("count");
}
if (State != UndoState.Normal)
{
throw new InvalidOperationException(SR.Get(SRID.UndoNotInNormalState));
}
if (OpenedUnit != null)
{
throw new InvalidOperationException(SR.Get(SRID.UndoUnitOpen));
}
SetState(UndoState.Redo);
bool exceptionThrown = true;
try
{
while (count > 0)
{
IUndoUnit unit;
unit = (IUndoUnit)RedoStack.Pop();
unit.Do();
count--;
}
exceptionThrown = false;
}
finally
{
if (exceptionThrown)
{
Clear();
}
}
SetState(UndoState.Normal);
}
///
/// Called when a unit is discarded. Unlocks the most recent unit added before the discarded one.
///
internal virtual void OnNextDiscard()
{
if (UndoCount > 0)
{
IParentUndoUnit lastParent = (IParentUndoUnit)PeekUndoStack();
lastParent.OnNextDiscard();
}
}
// Peek the unit of UndoStack and the return the top unit of UndoStack.
internal IUndoUnit PeekUndoStack()
{
if (_topUndoIndex < 0 || _topUndoIndex == UndoStack.Count)
{
return null;
}
else
{
return UndoStack[_topUndoIndex] as IUndoUnit;
}
}
///
/// Explicitly sets the redo stack.
///
///
/// DANGER! This method is internal (and not private) only so it can be accessed
/// by IMEs. Using this method can result in instability and is strongly discouraged.
///
internal Stack SetRedoStack(Stack value)
{
Stack previousValue = _redoStack;
if (value == null)
{
value = new Stack(2);
}
_redoStack = value;
return previousValue;
}
#endregion Internal Methods
//------------------------------------------------------
//
// Internal Properties
//
//------------------------------------------------------
#region Internal Properties
///
/// IME mode switch.
///
///
/// In the context of IME events (TextInputStart/TextInputUpdated/TextInput)
/// the undo stack is used internally to juggle document state such that
/// the IMEs perceive no reentrancy.
///
/// While IsImeSupportModeEnabled is set by our IME handler code
/// - The undo stack must be enabled.
/// - The undo stack must not be limited in size.
///
internal bool IsImeSupportModeEnabled
{
set
{
if (value != _imeSupportModeEnabled)
{
if (value)
{
// While _imeSupportModeEnabled is in effect, the undo
// stack is not constrained. If necessary, force it
// into the expected infinite stack configuration: growing from index 0.
if (_bottomUndoIndex != 0 && _topUndoIndex >= 0)
{
List undoStack = new List(UndoCount);
int i;
if (_bottomUndoIndex > _topUndoIndex)
{
for (i = _bottomUndoIndex; i < UndoLimit; i++)
{
undoStack.Add(_undoStack[i]);
}
_bottomUndoIndex = 0;
}
for (i=_bottomUndoIndex; i<=_topUndoIndex; i++)
{
undoStack.Add(_undoStack[i]);
}
_undoStack = undoStack;
_bottomUndoIndex = 0;
_topUndoIndex = undoStack.Count - 1;
}
_imeSupportModeEnabled = value;
}
else
{
// Transitioning false.
_imeSupportModeEnabled = value;
if (!this.IsEnabled)
{
// If the stack was originally disabled, remove all content added.
DoClear();
}
else
{
// Free up units that exceed the original undo limit.
//
// NB: we are not clearing the undo stack here if UndoLimit changed
// while _imeSupportMode was true, which is at odds
// with behavior from the UndoLimit setter. It always clears the
// stack when the UndoLimit changes. We're skipping that step
// because supporting it would require adding an additional piece
// of state to this class (to delay the reset from happening
// until we get here).
//
//
if (UndoLimit >= 0 && _topUndoIndex >= UndoLimit)
{
List undoStack = new List(UndoLimit);
for (int i = _topUndoIndex + 1 - UndoLimit; i <= _topUndoIndex; i++)
{
undoStack.Add(_undoStack[i]);
}
_undoStack = undoStack;
_bottomUndoIndex = 0;
_topUndoIndex = UndoLimit - 1;
}
}
}
}
}
}
///
/// Maximum number of undo units allowed on the stack
///
internal int UndoLimit
{
get
{
return _imeSupportModeEnabled ? -1 : _undoLimit;
}
set
{
_undoLimit = value;
// While _imeSupportModeEnabled == true, we must leave undo enabled and
// the undo limit may not be cleared.
if (!_imeSupportModeEnabled)
{
DoClear();
}
}
}
///
/// Returns the current state of the undo manager
///
internal UndoState State
{
get
{
return _state;
}
}
///
/// Whether or not the Undo Manager is enabled. If it isn't, any changes submitted
/// to the undo manager will be ignored.
///
///
/// IsEnabled will evaluate to false even when explicitly set true, if the
/// current UndoLimit is zero.
///
internal bool IsEnabled
{
get
{
return _imeSupportModeEnabled || (_isEnabled && _undoLimit != 0);
}
set
{
_isEnabled = value;
}
}
///
/// Returns the topmost opened ParentUndoUnit on the current stack
///
internal IParentUndoUnit OpenedUnit
{
get
{
return _openedUnit;
}
}
///
/// Readonly access to the last unit added to the IParentUndoUnit
///
internal IUndoUnit LastUnit
{
get
{
return _lastUnit;
}
}
///
/// Readonly access to the most recently reopened unit. Note that this unit is not
/// guaranteed to still exist in the stack-- it's just the last unit to be reopened.
///
///
/// Used by TextBox to determine if a text change causes a new unit to be opened or
/// an existing unit to be reopened.
///
internal IParentUndoUnit LastReopenedUnit
{
get
{
return _lastReopenedUnit;
}
}
///
/// Number of top-level units in the undo stack
///
internal int UndoCount
{
get
{
int count;
if (UndoStack.Count == 0 || _topUndoIndex < 0)
{
count = 0;
}
else if (_topUndoIndex == _bottomUndoIndex - 1 && PeekUndoStack() == null)
{
count = 0;
}
else if (_topUndoIndex >= _bottomUndoIndex)
{
count = _topUndoIndex - _bottomUndoIndex + 1;
}
else
{
count = _topUndoIndex + (UndoLimit - _bottomUndoIndex) + 1;
}
return count;
}
}
///
/// Number of top-level units in the redo stack
///
internal int RedoCount
{
get
{
return RedoStack.Count;
}
}
// Default value for UndoLimitProperty.
internal static int UndoLimitDefaultValue
{
get
{
return _undoLimitDefaultValue;
}
}
///
/// Returns the zero based unit in the undo stack.
///
///
/// DANGER! This method is internal (and not private) only so it can be accessed
/// by IMEs. Using this method can result in instability and is strongly discouraged.
///
/// This method may only be called while ImeSupportModeEnabled == true.
/// It does not handle circular undo stacks (_bottomUndoIndex > _topUndoIndex).
///
internal IUndoUnit GetUndoUnit(int index)
{
Invariant.Assert(index < this.UndoCount);
Invariant.Assert(index >= 0);
Invariant.Assert(_bottomUndoIndex == 0);
Invariant.Assert(_imeSupportModeEnabled);
return _undoStack[index];
}
///
/// Removes a range of units in the undo stack.
///
///
/// DANGER! This method is internal (and not private) only so it can be accessed
/// by IMEs. Using this method can result in instability and is strongly discouraged.
///
/// This method may only be called while ImeSupportModeEnabled == true.
/// It does not handle circular undo stacks (_bottomUndoIndex > _topUndoIndex).
///
internal void RemoveUndoRange(int index, int count)
{
Invariant.Assert(index >= 0);
Invariant.Assert(count >= 0);
Invariant.Assert(count + index <= this.UndoCount);
Invariant.Assert(_bottomUndoIndex == 0);
Invariant.Assert(_imeSupportModeEnabled);
int i;
// Slide following units backward to fill the gap.
for (i = index + count; i <= _topUndoIndex; i++)
{
_undoStack[i - count] = _undoStack[i];
}
// null out old references.
for (i = _topUndoIndex - (count - 1); i <= _topUndoIndex; i++)
{
_undoStack[i] = null;
}
// Decrement the top index.
_topUndoIndex -= count;
}
///
/// The minimum allowed depth of the undo stack.
///
///
/// DANGER! This method is internal (and not private) only so it can be accessed
/// by IMEs. Using this method can result in instability and is strongly discouraged.
///
/// Calling Undo when UndoCount == MinUndoStackCount is an error (and
/// will trigger an Invariant failure).
///
/// This property is set during IME composition handling,
/// to ensure that applications cannot undo IME changes
/// inside the scope of TextInputEvent handlers.
///
internal int MinUndoStackCount
{
get
{
return _minUndoStackCount;
}
set
{
_minUndoStackCount = value;
}
}
#endregion Internal Properties
//-----------------------------------------------------
//
// Protected Methods
//
//------------------------------------------------------
#region Protected Methods
///
/// State of the Undo Service
///
///
/// UndoState to which State is to be set
///
protected void SetState(UndoState value)
{
_state = value;
}
///
/// current opened unit
///
///
/// IParentUndoUnit to which OpenedUnit is to bet set
///
protected void SetOpenedUnit(IParentUndoUnit value)
{
_openedUnit = value;
}
///
/// Set LastUnit
///
///
/// IUndoUnit to which LastUnit is to be set
///
protected void SetLastUnit(IUndoUnit value)
{
_lastUnit = value;
}
///
/// Returns the deepest open parent undo unit contained within this one.
///
protected IParentUndoUnit DeepestOpenUnit
{
get
{
IParentUndoUnit openedUnit;
openedUnit = OpenedUnit;
if (openedUnit != null)
{
while (openedUnit.OpenedUnit != null)
{
openedUnit = openedUnit.OpenedUnit;
}
}
return openedUnit;
}
}
#endregion Protected Methods
//-----------------------------------------------------
//
// Protected Properties
//
//-----------------------------------------------------
#region Protected Properties
///
/// the undo stack
///
protected List UndoStack
{
get
{
return _undoStack;
}
}
///
/// the redo stack
///
protected Stack RedoStack
{
get
{
return _redoStack;
}
}
#endregion Protected Properties
//-----------------------------------------------------
//
// Private Methods
//
//------------------------------------------------------
#region Private Methods
///
/// Performs the work of the clear operation. Called by the public Clear() method,
/// or by UndoManager itself if it wants to clear its stacks without regard to
/// whether or not it's enabled.
///
private void DoClear()
{
Invariant.Assert(!_imeSupportModeEnabled); // We can't clear the undo stack while ime code depends on it.
if (UndoStack.Count > 0)
{
UndoStack.Clear();
UndoStack.TrimExcess();
}
if (RedoStack.Count > 0)
{
RedoStack.Clear();
}
SetLastUnit(null);
SetOpenedUnit(null);
_topUndoIndex = -1;
_bottomUndoIndex = 0;
}
private IUndoUnit PopUndoStack()
{
int undoCount = UndoCount - 1;
IUndoUnit unit = (IUndoUnit)UndoStack[_topUndoIndex];
UndoStack[_topUndoIndex--] = null;
if (_topUndoIndex < 0 && undoCount > 0)
{
Invariant.Assert(UndoLimit > 0);
_topUndoIndex = UndoLimit - 1; // This should never be possible with an unlimited stack
}
return unit;
}
#endregion Private methods
//-----------------------------------------------------
//
// Private Properties
//
//------------------------------------------------------
#region Private Properties
///
/// Property that identifies this service in ui element tree.
///
private static readonly DependencyProperty UndoManagerInstanceProperty = DependencyProperty.RegisterAttached( //
"UndoManagerInstance", typeof(UndoManager), typeof(UndoManager), //
new FrameworkPropertyMetadata(null, FrameworkPropertyMetadataOptions.Inherits));
#endregion Private Properties
//------------------------------------------------------
//
// Private Fields
//
//-----------------------------------------------------
#region Private Fields
private DependencyObject _scope; // an element to which this instance of scope is attached
private IParentUndoUnit _openedUnit;
private IUndoUnit _lastUnit;
private List _undoStack; // stack of undo units
private Stack _redoStack; // stack of redo units
private UndoState _state;
private bool _isEnabled;
private IParentUndoUnit _lastReopenedUnit;
private int _topUndoIndex; // index of the topmost unit in the undo stack
private int _bottomUndoIndex; // index of the bottommost unit in the undo stack
private int _undoLimit; // maximum size of undo stack, -1 means infinite.
private int _minUndoStackCount;
private bool _imeSupportModeEnabled;
// Default value for UndoLimitProperty.
private const int _undoLimitDefaultValue = -1;
#endregion Private Fields
}
}
// File provided for Reference Use Only by Microsoft Corporation (c) 2007.
// Copyright (c) Microsoft Corporation. All rights reserved.
//----------------------------------------------------------------------------
//
//
// Copyright (C) Microsoft Corporation. All rights reserved.
//
//
//
// Description:
//
// See spec at http://avalon/uis/Stock%20Services/Undo%20spec.htm
//
// History:
// 07/16/2003 : psarrett ported to WCP tree
// 03/21/2004 : eveselov - code style cleaned
//
//---------------------------------------------------------------------------
using System;
using System.Windows;
using System.Collections;
using System.Collections.Generic;
using System.ComponentModel;
using System.ComponentModel.Design;
using System.Diagnostics;
using MS.Utility;
using System.Windows.Markup;
using System.Windows.Documents;
using System.Windows.Controls.Primitives;
namespace MS.Internal.Documents
{
///
/// Enum for the state of the undo manager
///
///
internal enum UndoState
{
///
/// Ready to accept new undo units; not currently undoing or redoing
///
///
Normal,
///
/// In the process of undoing
///
///
Undo,
///
/// In the process of redoing
///
///
Redo,
///
/// In the process of rolling back an aborted undo unit
///
///
Rollback
};
///
/// Undo Manager
///
internal class UndoManager
{
//-----------------------------------------------------
//
// Constructors
//
//-----------------------------------------------------
#region Constructors
///
/// UndoManager constructor
///
internal UndoManager() : base()
{
_scope = null;
_state = UndoState.Normal;
_isEnabled = false;
_undoStack = new List(4);
_redoStack = new Stack(2);
_topUndoIndex = -1;
_bottomUndoIndex = 0;
_undoLimit = _undoLimitDefaultValue;
}
#endregion Constructors
//------------------------------------------------------
//
// Internal Methods
//
//-----------------------------------------------------
#region Internal Methods
///
/// Defines a given FrameworkElement as a scope for undo service.
/// New instance of UndoManager created and attached to this element.
///
///
/// FrameworkElement to which new instance of UndoManager is attached.
///
///
///
internal static void AttachUndoManager(DependencyObject scope, UndoManager undoManager)
{
if (scope == null)
{
throw new ArgumentNullException("scope");
}
if (undoManager == null)
{
throw new ArgumentNullException("undoManager");
}
if (undoManager is UndoManager && ((UndoManager)undoManager)._scope != null)
{
throw new InvalidOperationException(SR.Get(SRID.UndoManagerAlreadyAttached));
}
// Detach existing instance of undo manager if any
DetachUndoManager(scope);
// Attach the service to the scope via private dependency property
scope.SetValue(UndoManager.UndoManagerInstanceProperty, undoManager);
if (undoManager is UndoManager)
{
Debug.Assert(((UndoManager)undoManager)._scope == null);
((UndoManager)undoManager)._scope = scope;
}
undoManager.IsEnabled = true;
}
///
/// Detaches an undo service from the given FrameworkElement.
///
///
/// A FrameworkElement with UndoManager attached to it.
///
///
/// Throws an exception if the scope does not have undo service attached to it.
///
internal static void DetachUndoManager(DependencyObject scope)
{
UndoManager undoManager;
if (scope == null)
{
throw new ArgumentNullException("scope");
}
// Detach existing undo service if any
undoManager = scope.ReadLocalValue(UndoManager.UndoManagerInstanceProperty) as UndoManager;
if (undoManager != null)
{
// Disable the service while in detached state
undoManager.IsEnabled = false;
// Remove the service from a tre
scope.ClearValue(UndoManager.UndoManagerInstanceProperty);
// Break the linkage to its scope
if (undoManager is UndoManager)
{
Debug.Assert(((UndoManager)undoManager)._scope == scope);
((UndoManager)undoManager)._scope = null;
}
}
}
///
/// Finds the nearest undo service for a given uielement as a target.
///
///
/// A ui element which is a descendant (or self) of an element
/// to which undo service is attached.
///
///
internal static UndoManager GetUndoManager(DependencyObject target)
{
if (target == null)
{
return null;
}
else
{
return target.GetValue(UndoManager.UndoManagerInstanceProperty) as UndoManager;
}
}
///
/// Add the given parent undo unit to the undo manager, making it the OpenedUnit of the
/// innermost open parent unit (or the undo manager itself, if no parents are open).
///
///
/// parent unit to add
///
///
/// Thrown if UndoManager is disabled or passed unit is already open.
///
///
/// Thrown if passed unit is null.
///
internal void Open(IParentUndoUnit unit)
{
IParentUndoUnit deepestOpen;
if (!IsEnabled)
{
throw new InvalidOperationException(SR.Get(SRID.UndoServiceDisabled));
}
if (unit == null)
{
throw new ArgumentNullException("unit");
}
deepestOpen = DeepestOpenUnit;
if (deepestOpen == unit)
{
throw new InvalidOperationException(SR.Get(SRID.UndoUnitCantBeOpenedTwice));
}
if (deepestOpen == null)
{
if (unit != LastUnit)
{
// Don't want to add the unit again if we're just reopening it
Add(unit as IUndoUnit);
SetLastUnit(unit as IUndoUnit);
}
SetOpenedUnit(unit);
unit.Container = this;
}
else
{
unit.Container = deepestOpen;
deepestOpen.Open(unit);
}
}
///
/// Opens a closed undo unit on the top of the stack.
///
///
/// IParentUndoUnit to reopen
///
///
/// Thrown if:
/// UndoManager is disabled
/// another unit is already open
/// the given unit is locked
/// the given unit is not on top of the stack
///
///
/// Thrown if passed unit is null.
///
internal void Reopen(IParentUndoUnit unit)
{
if (!IsEnabled)
{
throw new InvalidOperationException(SR.Get(SRID.UndoServiceDisabled));
}
if (unit == null)
{
throw new ArgumentNullException("unit");
}
if (OpenedUnit != null)
{
throw new InvalidOperationException(SR.Get(SRID.UndoUnitAlreadyOpen));
}
switch (State)
{
case UndoState.Normal :
case UndoState.Redo :
{
if (UndoCount == 0 || PeekUndoStack() != unit)
{
throw new InvalidOperationException(SR.Get(SRID.UndoUnitNotOnTopOfStack));
}
break;
}
case UndoState.Undo :
{
if (RedoStack.Count == 0 || (IParentUndoUnit)RedoStack.Peek() != unit)
{
throw new InvalidOperationException(SR.Get(SRID.UndoUnitNotOnTopOfStack));
}
break;
}
case UndoState.Rollback :
default :
// should only happen if someone changes the UndoState enum or parameter validation
Debug.Assert(false);
break;
}
if (unit.Locked)
{
throw new InvalidOperationException(SR.Get(SRID.UndoUnitLocked));
}
Open(unit);
_lastReopenedUnit = unit;
}
///
/// Closes the current open unit, adding it to the containing unit's undo stack if committed.
///
internal void Close(UndoCloseAction closeAction)
{
Close(OpenedUnit, closeAction);
}
///
/// Closes an open child parent unit, adding it to the containing unit's undo stack if committed.
///
///
/// IParentUndoUnit to close. If NULL, this unit's OpenedUnit is closed.
///
///
///
///
/// Thrown if:
/// UndoManager is disabled
/// no undo unit is currently open
///
///
/// Thrown if unit is null
///
internal void Close(IParentUndoUnit unit, UndoCloseAction closeAction)
{
if (!IsEnabled)
{
throw new InvalidOperationException(SR.Get(SRID.UndoServiceDisabled));
}
if (unit == null)
{
throw new ArgumentNullException("unit");
}
if (OpenedUnit == null)
{
throw new InvalidOperationException(SR.Get(SRID.UndoNoOpenUnit));
}
// find the parent of the given unit
if (OpenedUnit != unit)
{
IParentUndoUnit closeParent;
closeParent = OpenedUnit;
while (closeParent.OpenedUnit != null && closeParent.OpenedUnit != unit)
{
closeParent = closeParent.OpenedUnit;
}
if (closeParent.OpenedUnit == null)
{
throw new ArgumentException(SR.Get(SRID.UndoUnitNotFound), "unit");
}
closeParent.Close(closeAction);
return;
}
//
// Close our open unit
//
if (closeAction != UndoCloseAction.Commit)
{
// discard unit
SetState(UndoState.Rollback);
if (unit.OpenedUnit != null)
{
unit.Close(closeAction);
}
if (closeAction == UndoCloseAction.Rollback)
{
unit.Do();
}
PopUndoStack();
SetOpenedUnit(null);
OnNextDiscard();
SetLastUnit(_topUndoIndex == -1 ? null : PeekUndoStack()); // can be null, which is fine
SetState(UndoState.Normal);
}
else
{
// commit unit
if (unit.OpenedUnit != null)
{
unit.Close(UndoCloseAction.Commit);
}
// flush redo stack
if (State != UndoState.Redo && State != UndoState.Undo && RedoStack.Count > 0)
{
RedoStack.Clear();
}
SetOpenedUnit(null);
}
}
///
/// Adds an undo unit to the undo/redo stack, depending on current state.
///
///
/// IUndoUnit to add
///
///
/// Thrown if:
/// UndoManager is disabled
/// unit is not IParentUndoUnit and there is no open IParentUndoUnit
///
///
/// Thrown if unit is null
///
internal void Add(IUndoUnit unit)
{
IParentUndoUnit parent;
if (!IsEnabled)
{
throw new InvalidOperationException(SR.Get(SRID.UndoServiceDisabled));
}
if (unit == null)
{
throw new ArgumentNullException("unit");
}
parent = DeepestOpenUnit;
if (parent != null)
{
parent.Add(unit);
}
else if (unit is IParentUndoUnit)
{
((IParentUndoUnit)unit).Container = this;
if (LastUnit is IParentUndoUnit)
{
((IParentUndoUnit)LastUnit).OnNextAdd();
}
SetLastUnit(unit);
if (State == UndoState.Normal || State == UndoState.Redo)
{
if (++_topUndoIndex == UndoLimit)
{
_topUndoIndex = 0;
}
if (!(_topUndoIndex < UndoStack.Count && PeekUndoStack() == null) // Non-null topmost stack item
&& (UndoLimit == -1 || UndoStack.Count < UndoLimit))
{
UndoStack.Add(unit);
}
else
{
if (PeekUndoStack() != null)
{
if (++_bottomUndoIndex == UndoLimit)
{
_bottomUndoIndex = 0;
}
}
UndoStack[_topUndoIndex] = unit;
}
}
else if (State == UndoState.Undo)
{
RedoStack.Push(unit);
}
else if (State == UndoState.Rollback)
{
// do nothing, throwing out the unit
}
}
else
{
throw new InvalidOperationException(SR.Get(SRID.UndoNoOpenParentUnit));
}
}
///
/// Clear the undo and redo stacks, as well as LastUnit.
///
///
/// Thrown if UndoManager is disabled
///
internal void Clear()
{
if (!IsEnabled)
{
throw new InvalidOperationException(SR.Get(SRID.UndoServiceDisabled));
}
// In practice, we only clear when the public IsUndoEnabled property is set false.
// We'll check that property again when _imeSupportModeEnabled transitions to false.
// While _imeSupportModeEnabled == true, we must leave undo enabled.
if (!_imeSupportModeEnabled)
{
DoClear();
}
}
///
/// Instructs the undo manager to invoke the given number of undo actions.
///
///
/// Number of undo units to undo
///
///
/// Thrown if UndoManager is disabled
///
///
/// Thrown if count is out of range
///
///
/// Thrown if there's an error performing the undo
///
internal void Undo(int count)
{
if (!IsEnabled)
{
throw new InvalidOperationException(SR.Get(SRID.UndoServiceDisabled));
}
if (count > UndoCount || count <= 0)
{
throw new ArgumentOutOfRangeException("count");
}
if (State != UndoState.Normal)
{
throw new InvalidOperationException(SR.Get(SRID.UndoNotInNormalState));
}
if (OpenedUnit != null)
{
throw new InvalidOperationException(SR.Get(SRID.UndoUnitOpen));
}
Invariant.Assert(UndoCount > _minUndoStackCount);
SetState(UndoState.Undo);
bool exceptionThrown = true;
try
{
while (count > 0)
{
IUndoUnit unit;
unit = PopUndoStack();
unit.Do();
count--;
}
exceptionThrown = false;
}
finally
{
if (exceptionThrown)
{
Clear();
}
}
SetState(UndoState.Normal);
}
///
/// Instructs the undo manager to invoke the given number of redo actions.
///
///
/// Number of redo units to redo
///
///
/// Thrown if UndoManager is disabled
///
///
/// Thrown if count is out of range
///
///
/// Thrown if there's an error performing the redo
///
internal void Redo(int count)
{
if (!IsEnabled)
{
throw new InvalidOperationException(SR.Get(SRID.UndoServiceDisabled));
}
if (count > RedoStack.Count || count <= 0)
{
throw new ArgumentOutOfRangeException("count");
}
if (State != UndoState.Normal)
{
throw new InvalidOperationException(SR.Get(SRID.UndoNotInNormalState));
}
if (OpenedUnit != null)
{
throw new InvalidOperationException(SR.Get(SRID.UndoUnitOpen));
}
SetState(UndoState.Redo);
bool exceptionThrown = true;
try
{
while (count > 0)
{
IUndoUnit unit;
unit = (IUndoUnit)RedoStack.Pop();
unit.Do();
count--;
}
exceptionThrown = false;
}
finally
{
if (exceptionThrown)
{
Clear();
}
}
SetState(UndoState.Normal);
}
///
/// Called when a unit is discarded. Unlocks the most recent unit added before the discarded one.
///
internal virtual void OnNextDiscard()
{
if (UndoCount > 0)
{
IParentUndoUnit lastParent = (IParentUndoUnit)PeekUndoStack();
lastParent.OnNextDiscard();
}
}
// Peek the unit of UndoStack and the return the top unit of UndoStack.
internal IUndoUnit PeekUndoStack()
{
if (_topUndoIndex < 0 || _topUndoIndex == UndoStack.Count)
{
return null;
}
else
{
return UndoStack[_topUndoIndex] as IUndoUnit;
}
}
///
/// Explicitly sets the redo stack.
///
///
/// DANGER! This method is internal (and not private) only so it can be accessed
/// by IMEs. Using this method can result in instability and is strongly discouraged.
///
internal Stack SetRedoStack(Stack value)
{
Stack previousValue = _redoStack;
if (value == null)
{
value = new Stack(2);
}
_redoStack = value;
return previousValue;
}
#endregion Internal Methods
//------------------------------------------------------
//
// Internal Properties
//
//------------------------------------------------------
#region Internal Properties
///
/// IME mode switch.
///
///
/// In the context of IME events (TextInputStart/TextInputUpdated/TextInput)
/// the undo stack is used internally to juggle document state such that
/// the IMEs perceive no reentrancy.
///
/// While IsImeSupportModeEnabled is set by our IME handler code
/// - The undo stack must be enabled.
/// - The undo stack must not be limited in size.
///
internal bool IsImeSupportModeEnabled
{
set
{
if (value != _imeSupportModeEnabled)
{
if (value)
{
// While _imeSupportModeEnabled is in effect, the undo
// stack is not constrained. If necessary, force it
// into the expected infinite stack configuration: growing from index 0.
if (_bottomUndoIndex != 0 && _topUndoIndex >= 0)
{
List undoStack = new List(UndoCount);
int i;
if (_bottomUndoIndex > _topUndoIndex)
{
for (i = _bottomUndoIndex; i < UndoLimit; i++)
{
undoStack.Add(_undoStack[i]);
}
_bottomUndoIndex = 0;
}
for (i=_bottomUndoIndex; i<=_topUndoIndex; i++)
{
undoStack.Add(_undoStack[i]);
}
_undoStack = undoStack;
_bottomUndoIndex = 0;
_topUndoIndex = undoStack.Count - 1;
}
_imeSupportModeEnabled = value;
}
else
{
// Transitioning false.
_imeSupportModeEnabled = value;
if (!this.IsEnabled)
{
// If the stack was originally disabled, remove all content added.
DoClear();
}
else
{
// Free up units that exceed the original undo limit.
//
// NB: we are not clearing the undo stack here if UndoLimit changed
// while _imeSupportMode was true, which is at odds
// with behavior from the UndoLimit setter. It always clears the
// stack when the UndoLimit changes. We're skipping that step
// because supporting it would require adding an additional piece
// of state to this class (to delay the reset from happening
// until we get here).
//
//
if (UndoLimit >= 0 && _topUndoIndex >= UndoLimit)
{
List undoStack = new List(UndoLimit);
for (int i = _topUndoIndex + 1 - UndoLimit; i <= _topUndoIndex; i++)
{
undoStack.Add(_undoStack[i]);
}
_undoStack = undoStack;
_bottomUndoIndex = 0;
_topUndoIndex = UndoLimit - 1;
}
}
}
}
}
}
///
/// Maximum number of undo units allowed on the stack
///
internal int UndoLimit
{
get
{
return _imeSupportModeEnabled ? -1 : _undoLimit;
}
set
{
_undoLimit = value;
// While _imeSupportModeEnabled == true, we must leave undo enabled and
// the undo limit may not be cleared.
if (!_imeSupportModeEnabled)
{
DoClear();
}
}
}
///
/// Returns the current state of the undo manager
///
internal UndoState State
{
get
{
return _state;
}
}
///
/// Whether or not the Undo Manager is enabled. If it isn't, any changes submitted
/// to the undo manager will be ignored.
///
///
/// IsEnabled will evaluate to false even when explicitly set true, if the
/// current UndoLimit is zero.
///
internal bool IsEnabled
{
get
{
return _imeSupportModeEnabled || (_isEnabled && _undoLimit != 0);
}
set
{
_isEnabled = value;
}
}
///
/// Returns the topmost opened ParentUndoUnit on the current stack
///
internal IParentUndoUnit OpenedUnit
{
get
{
return _openedUnit;
}
}
///
/// Readonly access to the last unit added to the IParentUndoUnit
///
internal IUndoUnit LastUnit
{
get
{
return _lastUnit;
}
}
///
/// Readonly access to the most recently reopened unit. Note that this unit is not
/// guaranteed to still exist in the stack-- it's just the last unit to be reopened.
///
///
/// Used by TextBox to determine if a text change causes a new unit to be opened or
/// an existing unit to be reopened.
///
internal IParentUndoUnit LastReopenedUnit
{
get
{
return _lastReopenedUnit;
}
}
///
/// Number of top-level units in the undo stack
///
internal int UndoCount
{
get
{
int count;
if (UndoStack.Count == 0 || _topUndoIndex < 0)
{
count = 0;
}
else if (_topUndoIndex == _bottomUndoIndex - 1 && PeekUndoStack() == null)
{
count = 0;
}
else if (_topUndoIndex >= _bottomUndoIndex)
{
count = _topUndoIndex - _bottomUndoIndex + 1;
}
else
{
count = _topUndoIndex + (UndoLimit - _bottomUndoIndex) + 1;
}
return count;
}
}
///
/// Number of top-level units in the redo stack
///
internal int RedoCount
{
get
{
return RedoStack.Count;
}
}
// Default value for UndoLimitProperty.
internal static int UndoLimitDefaultValue
{
get
{
return _undoLimitDefaultValue;
}
}
///
/// Returns the zero based unit in the undo stack.
///
///
/// DANGER! This method is internal (and not private) only so it can be accessed
/// by IMEs. Using this method can result in instability and is strongly discouraged.
///
/// This method may only be called while ImeSupportModeEnabled == true.
/// It does not handle circular undo stacks (_bottomUndoIndex > _topUndoIndex).
///
internal IUndoUnit GetUndoUnit(int index)
{
Invariant.Assert(index < this.UndoCount);
Invariant.Assert(index >= 0);
Invariant.Assert(_bottomUndoIndex == 0);
Invariant.Assert(_imeSupportModeEnabled);
return _undoStack[index];
}
///
/// Removes a range of units in the undo stack.
///
///
/// DANGER! This method is internal (and not private) only so it can be accessed
/// by IMEs. Using this method can result in instability and is strongly discouraged.
///
/// This method may only be called while ImeSupportModeEnabled == true.
/// It does not handle circular undo stacks (_bottomUndoIndex > _topUndoIndex).
///
internal void RemoveUndoRange(int index, int count)
{
Invariant.Assert(index >= 0);
Invariant.Assert(count >= 0);
Invariant.Assert(count + index <= this.UndoCount);
Invariant.Assert(_bottomUndoIndex == 0);
Invariant.Assert(_imeSupportModeEnabled);
int i;
// Slide following units backward to fill the gap.
for (i = index + count; i <= _topUndoIndex; i++)
{
_undoStack[i - count] = _undoStack[i];
}
// null out old references.
for (i = _topUndoIndex - (count - 1); i <= _topUndoIndex; i++)
{
_undoStack[i] = null;
}
// Decrement the top index.
_topUndoIndex -= count;
}
///
/// The minimum allowed depth of the undo stack.
///
///
/// DANGER! This method is internal (and not private) only so it can be accessed
/// by IMEs. Using this method can result in instability and is strongly discouraged.
///
/// Calling Undo when UndoCount == MinUndoStackCount is an error (and
/// will trigger an Invariant failure).
///
/// This property is set during IME composition handling,
/// to ensure that applications cannot undo IME changes
/// inside the scope of TextInputEvent handlers.
///
internal int MinUndoStackCount
{
get
{
return _minUndoStackCount;
}
set
{
_minUndoStackCount = value;
}
}
#endregion Internal Properties
//-----------------------------------------------------
//
// Protected Methods
//
//------------------------------------------------------
#region Protected Methods
///
/// State of the Undo Service
///
///
/// UndoState to which State is to be set
///
protected void SetState(UndoState value)
{
_state = value;
}
///
/// current opened unit
///
///
/// IParentUndoUnit to which OpenedUnit is to bet set
///
protected void SetOpenedUnit(IParentUndoUnit value)
{
_openedUnit = value;
}
///
/// Set LastUnit
///
///
/// IUndoUnit to which LastUnit is to be set
///
protected void SetLastUnit(IUndoUnit value)
{
_lastUnit = value;
}
///
/// Returns the deepest open parent undo unit contained within this one.
///
protected IParentUndoUnit DeepestOpenUnit
{
get
{
IParentUndoUnit openedUnit;
openedUnit = OpenedUnit;
if (openedUnit != null)
{
while (openedUnit.OpenedUnit != null)
{
openedUnit = openedUnit.OpenedUnit;
}
}
return openedUnit;
}
}
#endregion Protected Methods
//-----------------------------------------------------
//
// Protected Properties
//
//-----------------------------------------------------
#region Protected Properties
///
/// the undo stack
///
protected List UndoStack
{
get
{
return _undoStack;
}
}
///
/// the redo stack
///
protected Stack RedoStack
{
get
{
return _redoStack;
}
}
#endregion Protected Properties
//-----------------------------------------------------
//
// Private Methods
//
//------------------------------------------------------
#region Private Methods
///
/// Performs the work of the clear operation. Called by the public Clear() method,
/// or by UndoManager itself if it wants to clear its stacks without regard to
/// whether or not it's enabled.
///
private void DoClear()
{
Invariant.Assert(!_imeSupportModeEnabled); // We can't clear the undo stack while ime code depends on it.
if (UndoStack.Count > 0)
{
UndoStack.Clear();
UndoStack.TrimExcess();
}
if (RedoStack.Count > 0)
{
RedoStack.Clear();
}
SetLastUnit(null);
SetOpenedUnit(null);
_topUndoIndex = -1;
_bottomUndoIndex = 0;
}
private IUndoUnit PopUndoStack()
{
int undoCount = UndoCount - 1;
IUndoUnit unit = (IUndoUnit)UndoStack[_topUndoIndex];
UndoStack[_topUndoIndex--] = null;
if (_topUndoIndex < 0 && undoCount > 0)
{
Invariant.Assert(UndoLimit > 0);
_topUndoIndex = UndoLimit - 1; // This should never be possible with an unlimited stack
}
return unit;
}
#endregion Private methods
//-----------------------------------------------------
//
// Private Properties
//
//------------------------------------------------------
#region Private Properties
///
/// Property that identifies this service in ui element tree.
///
private static readonly DependencyProperty UndoManagerInstanceProperty = DependencyProperty.RegisterAttached( //
"UndoManagerInstance", typeof(UndoManager), typeof(UndoManager), //
new FrameworkPropertyMetadata(null, FrameworkPropertyMetadataOptions.Inherits));
#endregion Private Properties
//------------------------------------------------------
//
// Private Fields
//
//-----------------------------------------------------
#region Private Fields
private DependencyObject _scope; // an element to which this instance of scope is attached
private IParentUndoUnit _openedUnit;
private IUndoUnit _lastUnit;
private List _undoStack; // stack of undo units
private Stack _redoStack; // stack of redo units
private UndoState _state;
private bool _isEnabled;
private IParentUndoUnit _lastReopenedUnit;
private int _topUndoIndex; // index of the topmost unit in the undo stack
private int _bottomUndoIndex; // index of the bottommost unit in the undo stack
private int _undoLimit; // maximum size of undo stack, -1 means infinite.
private int _minUndoStackCount;
private bool _imeSupportModeEnabled;
// Default value for UndoLimitProperty.
private const int _undoLimitDefaultValue = -1;
#endregion Private Fields
}
}
// File provided for Reference Use Only by Microsoft Corporation (c) 2007.
// Copyright (c) Microsoft Corporation. All rights reserved.
Link Menu

This book is available now!
Buy at Amazon US or
Buy at Amazon UK
- InkCanvasAutomationPeer.cs
- XPathEmptyIterator.cs
- BuildResult.cs
- SpeechRecognizer.cs
- NavigationWindowAutomationPeer.cs
- UIPropertyMetadata.cs
- WindowsToolbarAsMenu.cs
- EventData.cs
- Subset.cs
- LocalizationParserHooks.cs
- TargetException.cs
- TableLayoutCellPaintEventArgs.cs
- CodeMethodReturnStatement.cs
- QilTypeChecker.cs
- TypeListConverter.cs
- StorageMappingFragment.cs
- StringReader.cs
- TextRenderer.cs
- XsltLoader.cs
- PatternMatcher.cs
- ContainerUtilities.cs
- XmlAttributeProperties.cs
- WindowsProgressbar.cs
- ProcessModelSection.cs
- ResXDataNode.cs
- WebPartEditorApplyVerb.cs
- WebPartVerbsEventArgs.cs
- AssemblyNameProxy.cs
- QilStrConcat.cs
- LineSegment.cs
- Drawing.cs
- GenericRootAutomationPeer.cs
- EntityContainerAssociationSet.cs
- Signature.cs
- RtfControlWordInfo.cs
- ReturnType.cs
- ProcessModule.cs
- RbTree.cs
- ParsedAttributeCollection.cs
- ListBindingHelper.cs
- AddingNewEventArgs.cs
- WsdlWriter.cs
- ApplicationId.cs
- HttpContextServiceHost.cs
- ReadOnlyCollection.cs
- DataAdapter.cs
- HttpCookie.cs
- CharKeyFrameCollection.cs
- Thread.cs
- RuntimeResourceSet.cs
- DbDataAdapter.cs
- _ConnectionGroup.cs
- DrawingContext.cs
- BuildDependencySet.cs
- CurrentChangingEventArgs.cs
- precedingquery.cs
- DefaultTextStoreTextComposition.cs
- NotifyParentPropertyAttribute.cs
- TraceUtils.cs
- PageEventArgs.cs
- WebPartMenuStyle.cs
- EdmToObjectNamespaceMap.cs
- BitmapEffectState.cs
- ScriptBehaviorDescriptor.cs
- DrawingBrush.cs
- Line.cs
- DataGridViewCheckBoxColumn.cs
- ThrowHelper.cs
- PreviousTrackingServiceAttribute.cs
- ZoneButton.cs
- DetailsViewCommandEventArgs.cs
- Subtree.cs
- SimpleWebHandlerParser.cs
- HttpAsyncResult.cs
- ZipIOLocalFileHeader.cs
- DiscoveryMessageSequenceGenerator.cs
- WebPartDisplayModeCancelEventArgs.cs
- TransformedBitmap.cs
- XmlSchemaAnyAttribute.cs
- DeviceContext2.cs
- StylusSystemGestureEventArgs.cs
- FigureParagraph.cs
- ProxyWebPartManager.cs
- BamlRecordWriter.cs
- SafeTokenHandle.cs
- ComponentSerializationService.cs
- BinaryWriter.cs
- AttachmentService.cs
- QuaternionAnimation.cs
- Activator.cs
- EnumerableRowCollection.cs
- IndexedGlyphRun.cs
- RefType.cs
- HelpEvent.cs
- DifferencingCollection.cs
- WinEventTracker.cs
- BufferedReadStream.cs
- ValidationEventArgs.cs
- NullableBoolConverter.cs
- FrameworkRichTextComposition.cs