UndoManager.cs source code in C# .NET

Source code for the .NET framework in C#

                        

Code:

/ Net / Net / 3.5.50727.3053 / DEVDIV / depot / DevDiv / releases / Orcas / SP / wpf / src / Framework / MS / Internal / documents / UndoManager.cs / 1 / 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

Network programming in C#, Network Programming in VB.NET, Network Programming in .NET
This book is available now!
Buy at Amazon US or
Buy at Amazon UK