private ScopeAction StartScope(string name)
        {
            if (recursion != 0)
            {
                return(null);
            }

            ScopeAction scopeAction = new ScopeAction(name);

            recursion = 1;

            try
            {
                Run(scopeAction);
            }
            catch
            {
                recursion = 0;
                state     = UndoRedoManagerState.Idle;

                throw;
            }

            return(scopeAction);
        }
        private void UndoCurrent()
        {
            state = UndoRedoManagerState.ProcessUndo;

            try
            {
                for (int i = current.list.Count; i-- > 0;)
                {
                    current.list[i].Run(this, ActionType.Undo);
                }
            }
            finally
            {
                state = UndoRedoManagerState.ProcessDo;
            }
        }
        /// <summary>
        /// Roes last undoed action.
        /// </summary>
        public void Redo()
        {
            if (!CanRedo)
            {
                throw new InvalidOperationException();
            }

            state = UndoRedoManagerState.ProcessRedo;

            try
            {
                RunAction(ActionType.Redo);
            }
            finally
            {
                state = UndoRedoManagerState.Idle;
            }

            OnChanged();
        }
        /// <summary>
        /// Runs an action and puts it in the undo list.
        /// </summary>
        /// <param name="action">action to run</param>
        public void Run(IAction action)
        {
            if (action == null)
            {
                throw new ArgumentNullException("action");
            }

            bool changed = false;

            recursion++;

            try
            {
                switch (state)
                {
                case UndoRedoManagerState.Idle:
                    changed       = InternalClearRedo();
                    state         = UndoRedoManagerState.ProcessDo;
                    current.prime = null;
                    current.list  = new List <IAction>();

                    break;

                case UndoRedoManagerState.ProcessDo:
                    break;

                default:
                    throw new InvalidOperationException();
                }

                ScopeAction scope = action as ScopeAction;

                if (action.Run(this, ActionType.Do))
                {
                    IAction item = action;

                    Merge(current.list, ref item);

                    if ((item != null) && !IsSecondary(item))
                    {
                        if ((current.prime == null) || (recursion == 1) && (scope == null))
                        {
                            current.prime = item;
                        }
                    }
                }

                if (recursion != 1)
                {
                    return;
                }

                if (current.prime == null)
                {
                    // There is no prime action.
                    // We can cancel whole action.
                    UndoCurrent();

                    return;
                }

                bool hasPrimary = false;

                for (int i = 0, c = current.list.Count; i < c; i++)
                {
                    IAction item = current.list[i];

                    if ((current.prime != item) && !IsSecondary(item))
                    {
                        hasPrimary = true;

                        break;
                    }
                }

                // End of scope.
                if (!hasPrimary && (scope != null))
                {
                    UndoCurrent();

                    return;
                }

                changed = true;

                bool addAction = true;

                if (!hasPrimary && (actions.Count != 0))
                {
                    // Merge previous action with a new one.
                    Action previousAction = actions[actions.Count - 1];

                    IMergeableAction mergeableAction = previousAction.prime as IMergeableAction;

                    if (mergeableAction != null)
                    {
                        IAction mergedAction;

                        mergeList.Add(current.prime);

                        try
                        {
                            mergedAction = mergeableAction.Merge(this, mergeList);
                            addAction    = mergeList.Count != 0;

                            int index = current.list.IndexOf(current.prime);

                            if (addAction)
                            {
                                current.prime = mergeList[0];

                                if (index != -1)
                                {
                                    current.list[index] = current.prime;
                                }
                            }
                            else
                            {
                                current.prime = mergedAction;
                                current.list.RemoveAt(index);
                            }
                        }
                        finally
                        {
                            mergeList.Clear();
                        }

                        if (!addAction)
                        {
                            if (previousAction.list != null)
                            {
                                Merge(previousAction.list, current.list);
                                current.list = previousAction.list;
                            }

                            if (current.prime == null)
                            {
                                UndoCurrent();
                                actions.RemoveAt(actions.Count - 1);
                            }
                            else
                            {
                                if (current.list.Count < 2)
                                {
                                    current.list = null;
                                }

                                actions[actions.Count - 1] = current;
                            }

                            return;
                        }
                    }
                }

                if (addAction)
                {
                    if (current.list.Count < 2)
                    {
                        current.list = null;
                    }

                    actions.Add(current);
                }
            }
            finally
            {
                if (--recursion == 0)
                {
                    state        = UndoRedoManagerState.Idle;
                    redoPosition = actions.Count;

                    if (CheckUndoListSize())
                    {
                        changed = true;
                    }

                    if (changed)
                    {
                        OnChanged();
                    }
                }
            }
        }