Example #1
0
 /// ------------------------------------------------------------------------------------
 /// <summary>
 /// Clears all marks and bundles.
 /// </summary>
 /// ------------------------------------------------------------------------------------
 private void Clear()
 {
     m_currentBundle = null;
     ClearAllMarks();
     m_undoBundles.Clear();
     m_redoBundles.Clear();
 }
Example #2
0
        /// <summary>
        /// A change is affected by redoing another unit of work if it affects any of the same objects,
        /// or if it removes a reference to an item which the other change deletes (and so redoing the
        /// other change first would delete the target without first removing the reference).
        /// </summary>
        internal bool IsAffectedByRedoing(FdoUnitOfWork other)
        {
            if (AffectsSameObjects(other))
            {
                return(true);
            }

            //// We have a problem if the other change creates a reference to an object, and this change
            //// creates the object. If we redo the other change first, the reference will exist without a target.
            //if (m_newObjects.Count != 0)
            //{
            //    var guids = new HashSet<Guid>(from obj in m_newObjects select obj.Id.Guid);
            //    foreach (var item in other.m_changes)
            //    {
            //        var change = item as FdoStateChangeBase;
            //        if (change != null && change.RefersToAfterChange(guids))
            //            return true;
            //    }
            //}
            //// We have a problem if this change deletes a reference to an object, and the other change
            //// deletes the object. If we redo the other change first, the object will be deleted, but not
            //// the reference to it.
            //if (other.m_deletedObjects.Count == 0)
            //    return false;
            //var deletedGuids = new HashSet<Guid>(from obj in other.m_deletedObjects select obj.Guid);
            //foreach (var item in m_changes)
            //{
            //    var change = item as FdoStateChangeBase;
            //    if (change != null && change.RefersToBeforeChange(deletedGuids))
            //        return true;
            //}

            return(false);
        }
Example #3
0
 public UndoStack(UnitOfWorkService uowService)
 {
     m_uowService    = uowService;
     m_undoBundles   = new Stack <FdoUnitOfWork>();
     m_redoBundles   = new Stack <FdoUnitOfWork>();
     m_currentBundle = null;
 }
Example #4
0
        /// <summary>
        /// Rollback the current UOW.
        ///</summary>
        /// <param name='nDepth'>[Not used.]</param>
        /// <exception cref="InvalidOperationException">
        /// Thrown if not in the right state to do
        /// a rollback (in the data change phase of the UOW).
        /// </exception>
        public void Rollback(int nDepth)
        {
            if (this != m_uowService.ActiveUndoStack)
            {
                m_uowService.ActiveUndoStack.Rollback(nDepth);
                return;
            }
            if (m_uowService.CurrentProcessingState != UnitOfWorkService.FdoBusinessTransactionState.ProcessingDataChanges)
            {
                throw new InvalidOperationException("Rollback not supported in the current state.");
            }

            Debug.Assert(m_uowService.m_lock.IsWriteLockHeld, "Trying Rollback without write lock!");
            if (m_uowService.m_lock.IsWriteLockHeld)
            {
                m_uowService.m_lock.ExitWriteLock();
            }
            else
            {
                Logger.WriteEvent("Trying to rollback without write lock!");
            }

            m_currentBundle.Rollback();
            m_currentBundle = null;
            m_actionsToDoAtEndOfPropChanged.Clear();             // don't do them on some subsequent task
            m_uowService.CurrentProcessingState = UnitOfWorkService.FdoBusinessTransactionState.ReadyForBeginTask;
        }
Example #5
0
        /// ------------------------------------------------------------------------------------
        /// <summary>
        /// Raises the prop changed completed event.
        /// </summary>
        /// <param name="uow">The unit of work we are doing, undoing, or redoing.</param>
        /// <param name="fromUndoRedo">True if the method was called for an undo or
        /// redo, false for the original action.</param>
        /// ------------------------------------------------------------------------------------
        private void DoTasksForEndOfPropChanged(FdoUnitOfWork uow, bool fromUndoRedo)
        {
            var tasks = new List <Action>();

            if (!fromUndoRedo)
            {
                // These notifications must be sent only once.
                // In particular they must not be sent again if one of the tasks in the list makes a new UOW.
                // Since this method will execute at the end of such a task, we must make sure that there is
                // a fresh m_actionsToDoAtEndOfPropChanged for any such UOW so it will not see our list.
                tasks.AddRange(m_actionsToDoAtEndOfPropChanged);
                m_actionsToDoAtEndOfPropChanged.Clear();
            }
            tasks.AddRange(uow.ActionsToDoAtEndOfPropChanged);
            if (tasks.Count > 0)
            {
                m_ui.SynchronizeInvoke.Invoke(() =>
                {
                    foreach (Action task in tasks)
                    {
                        task();
                    }
                });
            }
        }
Example #6
0
 /// ------------------------------------------------------------------------------------
 /// <summary>
 /// Inserts the actions from another unit of work (merge) into this UOW at the beginning
 /// of this change list. This method handles the deleted/created objects correctly.
 /// </summary>
 /// <param name="uow">The unit of work.</param>
 /// ------------------------------------------------------------------------------------
 internal void InsertActionsFrom(FdoUnitOfWork uow)
 {
     m_changes.InsertRange(0, uow.m_changes);
     foreach (Guid guid in uow.m_newGuids)
     {
         m_newGuids.Add(guid);
     }
     m_newObjects.UnionWith(uow.m_newObjects);
     m_dirtyObjects.UnionWith(uow.m_dirtyObjects);
     m_deletedObjects.UnionWith(uow.m_deletedObjects);
 }
Example #7
0
 /// <summary>
 /// Begin a sequence of non-undoable actions.
 ///</summary>
 public void BeginNonUndoableTask()
 {
     if (this != m_uowService.ActiveUndoStack)
     {
         m_uowService.ActiveUndoStack.BeginNonUndoableTask();
         return;
     }
     CheckNotProcessingDataChanges("Nested tasks are not supported.");
     CheckNotBroadcastingPropChanges("Can't start new task, while broadcasting PropChanges.");
     CheckNotInUndoRedo();
     m_uowService.CurrentProcessingState = UnitOfWorkService.FdoBusinessTransactionState.ProcessingDataChanges;
     m_uowService.m_lock.EnterWriteLock();
     m_currentBundle = new FdoNonUndoableUnitOfWork(m_uowService);
 }
Example #8
0
 /// <summary>
 /// This undo stack has a change that conflicts with itemToUndo if it has a redoable item with an earlier
 /// sequence number that is affected by redoing itemToRedo.
 /// </summary>
 internal bool HasConflictingRedoChanges(FdoUnitOfWork itemToRedo)
 {
     // Stack enumeration is in reverse order, so we get the first change we might redo first.
     foreach (var other in m_redoBundles)
     {
         // If it's equal we're comparing with ourself, which should not be considered a conflict!
         if (other.Sequence >= itemToRedo.Sequence)
         {
             return(false);                    // this and therefore all remaining changes are later than the one we are testing
         }
         if (other.IsAffectedByRedoing(itemToRedo))
         {
             return(true);
         }
     }
     return(false);
 }
Example #9
0
        /// <summary>
        /// Rollback the current UOW.
        ///</summary>
        /// <param name='nDepth'>[Not used.]</param>
        /// <exception cref="InvalidOperationException">
        /// Thrown if not in the right state to do
        /// a rollback (in the data change phase of the UOW).
        /// </exception>
        public void Rollback(int nDepth)
        {
            if (this != m_uowService.ActiveUndoStack)
            {
                m_uowService.ActiveUndoStack.Rollback(nDepth);
                return;
            }
            if (m_uowService.CurrentProcessingState != UnitOfWorkService.FdoBusinessTransactionState.ProcessingDataChanges)
            {
                throw new InvalidOperationException("Rollback not supported in the current state.");
            }

            m_uowService.m_lock.ExitWriteLock();

            m_currentBundle.Rollback();
            m_currentBundle = null;
            RaisePropChangedCompleted(true);
            m_uowService.CurrentProcessingState = UnitOfWorkService.FdoBusinessTransactionState.ReadyForBeginTask;
        }
Example #10
0
        /// <summary>
        /// Two units of work have conflicting changes if they create, modify, or delete any of the same objects.
        /// In addition, since this is considered the change which must not be harmed by undoing (or redoing)
        /// </summary>
        internal bool AffectsSameObjects(FdoUnitOfWork other)
        {
            var itsChangedObjects = new HashSet <ICmObjectId>(other.m_deletedObjects);

            foreach (var item in other.m_dirtyObjects)
            {
                itsChangedObjects.Add(item.Id);
            }
            foreach (var item in other.m_newObjects)
            {
                itsChangedObjects.Add(item);
            }
            foreach (var item in m_dirtyObjects)
            {
                if (itsChangedObjects.Contains(item.Id))
                {
                    return(true);
                }
            }
            foreach (var item in m_newObjects)
            {
                if (itsChangedObjects.Contains(item))
                {
                    return(true);
                }
            }
            foreach (var item in m_deletedObjects)
            {
                if (itsChangedObjects.Contains(item))
                {
                    return(true);
                }
            }

            return(false);
        }
Example #11
0
 private void PushUowOnUndoStack(FdoUnitOfWork newUow)
 {
     m_undoBundles.Push(newUow);
     Debug.Assert(m_countUnsavedBundles >= 0);
     m_countUnsavedBundles++;
 }
Example #12
0
        private void EndUndoTaskCommon(bool updateDateModified)
        {
            if (m_uowService.CurrentProcessingState != UnitOfWorkService.FdoBusinessTransactionState.ProcessingDataChanges)
            {
                throw new InvalidOperationException("Cannot end task that has not been started.");
            }

            if (updateDateModified)
            {
                // A generic side effect of all changes is to update DateModified.
                // Collect the objects we want to record the modify time on. Don't do each as found:
                // Updating them will add new dirtballs, which will mess up the DirtyObjects iterator.
                // Also, we don't need the overhead of updating an object repeatedly if it has several changes.
                var collector = new HashSet <ICmObjectInternal>();
                foreach (var item in m_currentBundle.DirtyObjects)
                {
                    item.CollectDateModifiedObject(collector);
                }
                // Don't update the modify time on new objects, it should be near enough, and Undo will fail
                // trying to restore the modify time on the deleted object.
                var newObjects = m_currentBundle.NewObjects;
                foreach (var dmObj in collector)
                {
                    if (!newObjects.Contains(dmObj.Id) && !m_currentBundle.IsDateModifiedExplicitly(dmObj))
                    {
                        dmObj.UpdateDateModified();
                    }
                }
                // Update the project DateModified, but only once every 2 minutes at most.
                if (m_currentBundle.DirtyObjects.Count() > 0)
                {
                    LangProject proj = m_currentBundle.DirtyObjects.ElementAt(0).Cache.LangProject as LangProject;
                    TimeSpan    span = new TimeSpan(DateTime.Now.Ticks - proj.DateModified.Ticks);
                    if (span.Minutes >= 2 || m_currentBundle.DirtyObjects.Contains(proj))
                    {
                        proj.UpdateDateModifiedInternal();
                    }
                }
            }

            m_uowService.m_lock.ExitWriteLock();

            m_uowService.CurrentProcessingState = UnitOfWorkService.FdoBusinessTransactionState.BroadcastingPropChanges;

            if (m_currentBundle.HasDataChange)
            {
                // Can't redo these now.
                // If m_currentBundle can't be finished well,
                // then it can all be rolled back
                ClearRedoStack();
                PushUowOnUndoStack(m_currentBundle);
                m_currentBundle.SetAfterXml();

                m_uowService.SuppressSelections = true;

                try
                {
                    // Handle Step 2.B (PropChanged calls) here.
                    // Do them here because we may not commit yet.
                    // 2.B can be moved after the optional save, but then rethink the states.
                    m_uowService.SendPropChangedNotifications(m_currentBundle.GetPropChangeInformation(false));
                }
                finally
                {
                    m_uowService.SuppressSelections = false;
                }
            }

            m_currentBundle = null;

            m_uowService.CurrentProcessingState = UnitOfWorkService.FdoBusinessTransactionState.ReadyForBeginTask;

            // Do this after we are back in a safe state to do a new UOW, if necessary.
            RaisePropChangedCompleted(false);
        }