public UndoTransactionImpl(ITextUndoHistory history, ITextUndoTransaction parent, string description) { if (history == null) { throw new ArgumentNullException("history", String.Format(CultureInfo.CurrentUICulture, "Strings.ArgumentCannotBeNull", "UndoTransactionImpl", "history")); } if (String.IsNullOrEmpty(description)) { throw new ArgumentNullException("description", String.Format(CultureInfo.CurrentUICulture, "Strings.ArgumentCannotBeNull", "UndoTransactionImpl", "description")); } this.history = history as UndoHistoryImpl; if (this.history == null) { throw new ArgumentException("Strings.InvalidHistoryInTransaction"); } this.parent = parent as UndoTransactionImpl; if (this.parent == null && parent != null) { throw new ArgumentException("Strings.InvalidParentInTransaction"); } this.description = description; this.state = UndoTransactionState.Open; this.primitives = new List <ITextUndoPrimitive>(); this.mergePolicy = NullMergeUndoTransactionPolicy.Instance; this.IsReadOnly = true; }
public UndoTransactionImpl(ITextUndoHistory history, ITextUndoTransaction parent, string description) { if (history == null) { throw new ArgumentNullException(nameof(history)); } if (string.IsNullOrEmpty(description)) { throw new ArgumentNullException(nameof(description)); } this.history = history as UndoHistoryImpl; if (this.history == null) { throw new ArgumentException("Strings.InvalidHistoryInTransaction"); } this.parent = parent as UndoTransactionImpl; if (this.parent == null && parent != null) { throw new ArgumentException("Strings.InvalidParentInTransaction"); } this.description = description; this.state = UndoTransactionState.Open; this.primitives = new List <ITextUndoPrimitive>(); this.mergePolicy = NullMergeUndoTransactionPolicy.Instance; this.IsReadOnly = true; }
/// <summary> /// Copies all of the primitives from the given transaction, and appends them to the UndoPrimitives list. /// </summary> /// <param name="transaction">The UndoTransactionImpl to copy from.</param> public void CopyPrimitivesFrom(UndoTransactionImpl transaction) { foreach (ITextUndoPrimitive p in transaction.UndoPrimitives) { this.AddUndo(p); } }
/// <summary> /// This does two different things, depending on the MergeUndoTransactionPolicys in question. /// It either simply pushes the current transaction to the undo stack, OR it merges it with /// the most recent item in the stack. /// </summary> private void MergeOrPushToUndoStack(UndoTransactionImpl transaction) { ITextUndoTransaction transactionAdded; TextUndoTransactionCompletionResult transactionResult; UndoTransactionImpl utPrevious = this.undoStack.Count > 0 ? this.undoStack.Peek() as UndoTransactionImpl : null; if (utPrevious != null && ProceedWithMerge(transaction, utPrevious)) { // Temporarily make utPrevious non-read-only, during merge. utPrevious.IsReadOnly = false; try { transaction.MergePolicy.PerformTransactionMerge(utPrevious, transaction); } finally { utPrevious.IsReadOnly = true; } // utPrevious is already on the undo stack, so we don't need to add it; but report // it as the added transaction in the UndoTransactionCompleted event. transactionAdded = utPrevious; transactionResult = TextUndoTransactionCompletionResult.TransactionMerged; } else { this.undoStack.Push(transaction); transactionAdded = transaction; transactionResult = TextUndoTransactionCompletionResult.TransactionAdded; } RaiseUndoTransactionCompleted(transactionAdded, transactionResult); }
/// <summary> /// Creates a new transaction, nests it in the previously current transaction, and marks it current. /// If there is a redo stack, it gets cleared. /// UNDONE: should the redo-clearing happen now or when the new transaction is committed? /// </summary> /// <param name="description">A string description for the transaction.</param> /// <param name="isHidden">The new transaction.</param> /// <returns></returns> public ITextUndoTransaction CreateTransaction(string description) { if (String.IsNullOrEmpty(description)) { throw new ArgumentNullException("description", String.Format(CultureInfo.CurrentUICulture, "Strings.ArgumentCannotBeNull", "CreateTransaction", "description")); } // If there is a pending transaction that has already been completed, we should not be permitted // to open a new transaction, since it cannot later be added to its parent. if ((this.currentTransaction != null) && (this.currentTransaction.State != UndoTransactionState.Open)) { throw new InvalidOperationException("Strings.CannotCreateTransactionWhenCurrentTransactionNotOpen"); } // new transactions that are visible should clear the redo stack. if (this.currentTransaction == null) { foreach (UndoTransactionImpl redoTransaction in this.redoStack) { redoTransaction.Invalidate(); } this.redoStack.Clear(); } UndoTransactionImpl newTransaction = new UndoTransactionImpl(this, this.currentTransaction, description); this.currentTransaction = newTransaction; return(this.currentTransaction); }
private bool ProceedWithMerge(UndoTransactionImpl transaction1, UndoTransactionImpl transaction2) { UndoHistoryRegistryImpl registry = UndoHistoryRegistry; return(transaction1.MergePolicy != null && transaction2.MergePolicy != null && transaction1.MergePolicy.TestCompatiblePolicy(transaction2.MergePolicy) && transaction1.MergePolicy.CanMerge(transaction1, transaction2)); }
public UndoHistoryImpl(UndoHistoryRegistryImpl undoHistoryRegistry) { this.currentTransaction = null; this.UndoHistoryRegistry = undoHistoryRegistry; this.undoStack = new Stack <ITextUndoTransaction>(); this.redoStack = new Stack <ITextUndoTransaction>(); this.activeUndoOperationPrimitive = null; this.state = TextUndoHistoryState.Idle; }
public DelegatedUndoPrimitiveImpl(UndoHistoryImpl history, UndoTransactionImpl parent, UndoableOperationCurried operationCurried) { redoOperations = new Stack <UndoableOperationCurried>(); undoOperations = new Stack <UndoableOperationCurried>(); this.parent = parent; this.history = history; this.state = DelegatedUndoPrimitiveState.Inactive; undoOperations.Push(operationCurried); }
/// <summary> /// This is how the transactions alert their containing history that they have finished /// (likely from the Dispose() method). /// </summary> /// <param name="transaction">This is the transaction that's finishing. It should match the history's current transaction. /// If it does not match, then the current transaction will be discarded and an exception will be thrown.</param> public void EndTransaction(ITextUndoTransaction transaction) { if (this.currentTransaction != transaction) { this.currentTransaction = null; throw new InvalidOperationException("Strings.EndTransactionOutOfOrder"); } // only add completed transactions to their parents (or the stack) if (this.currentTransaction.State == UndoTransactionState.Completed) { if (this.currentTransaction.Parent == null) // stack bottomed out! { MergeOrPushToUndoStack(this.currentTransaction); } } this.currentTransaction = this.currentTransaction.Parent as UndoTransactionImpl; }
/// <summary> /// This is how the transactions alert their containing history that they have finished /// (likely from the Dispose() method). /// </summary> /// <param name="transaction">This is the transaction that's finishing. It should match the history's current transaction. /// If it does not match, then the current transaction will be discarded and an exception will be thrown.</param> public void EndTransaction(ITextUndoTransaction transaction) { if (this.currentTransaction != transaction) { this.currentTransaction = null; throw new InvalidOperationException("Strings.EndTransactionOutOfOrder"); } // Note that the VS undo history actually "pops" the nested undo stack on the Complete/Cancel // (instead of in the Dispose). This shouldn't affect anything but we should consider adapting // this code to follow the model in VS undo. this.currentTransaction = (UndoTransactionImpl)(transaction.Parent); // only add completed transactions to their parents (or the stack) if (transaction.State == UndoTransactionState.Completed) { if (transaction.Parent == null) // stack bottomed out! { MergeOrPushToUndoStack((UndoTransactionImpl)transaction); } } }
/// <summary> /// Performs a redo operation and places the primitives on the redo stack, up until (and /// including) the transaction indicated. This is called by the linked undo transaction that /// is aware of the linking relationship between transactions, and it does not call back into /// the transactions' public Redo(). /// </summary> /// <param name="transaction"></param> public void RedoInIsolation(UndoTransactionImpl transaction) { TextUndoHistoryState originalState = this.state; this.state = TextUndoHistoryState.Redoing; using (new AutoEnclose(delegate { this.state = originalState; })) { if (this.redoStack.Contains(transaction)) { UndoTransactionImpl redone = null; while (redone != transaction) { UndoTransactionImpl ut = this.redoStack.Pop() as UndoTransactionImpl; ut.Do(); this.undoStack.Push(ut); RaiseUndoRedoHappened(this.state, ut); redone = ut; } } } }