private void Merge(List <IAction> previous, List <IAction> next) { if (previous == null) { return; } for (int i = previous.Count; i-- > 0;) { if (next.Count == 0) { break; } IAction action = previous[i]; IMergeableAction mergeableAction = action as IMergeableAction; if (mergeableAction == null) { continue; } IAction mergedAction = mergeableAction.Merge(this, next); if (mergedAction == null) { previous.RemoveAt(i); } else if (mergedAction != action) { previous[i] = mergedAction; } } previous.AddRange(next); }
/// <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(); } } } }