public void ChangeTrackSegmentEndFrameTest()
        {
            SetupViewModel(useRealUndoService: true);

            SegmentViewModelBase segmentToTest = _viewModel.TimelineSegments[0];

            int newEndFrame      = 15;
            int originalEndFrame = segmentToTest.EndFrame;

            Assert.True(_viewModel.CanChangeTrackSegmentEndFrame(segmentToTest, newEndFrame));

            _viewModel.ChangeTrackSegmentEndFrame(segmentToTest, newEndFrame);
            Assert.Equal(newEndFrame, segmentToTest.EndFrame);
            Assert.Equal(2, segmentToTest.Model.KeyFrames.Count);
            Assert.Equal(2, segmentToTest.KeyFrameViewModels.Count);
            Assert.Equal(segmentToTest.StartFrame, segmentToTest.KeyFrameViewModels[0].FrameNumber);
            Assert.Equal(10, segmentToTest.KeyFrameViewModels[1].FrameNumber);

            Assert.True(_undoRoot.CanUndo);
            _undoRoot.Undo();

            Assert.Equal(originalEndFrame, segmentToTest.EndFrame);
            Assert.Equal(3, segmentToTest.Model.KeyFrames.Count);
            Assert.Equal(3, segmentToTest.KeyFrameViewModels.Count);
            Assert.Equal(segmentToTest.StartFrame, segmentToTest.KeyFrameViewModels[0].FrameNumber);
            Assert.Equal(10, segmentToTest.KeyFrameViewModels[1].FrameNumber);
            Assert.Equal(22, segmentToTest.KeyFrameViewModels[2].FrameNumber);
        }
Esempio n. 2
0
        /// <summary>
        /// Handles the <see cref="UIElement.MouseMove"/> routed event for a <see cref="VideoTimelineSegment"/> element.
        /// </summary>
        /// <remarks>
        /// Can't use Preview Mouse events as the <see cref="VideoTimelineSegment"/> element's border <see cref="Thumb"/>s
        /// have to have the first chance to handle the event.
        /// </remarks>
        /// <inheritdoc cref="MouseEventHandler"/>
        private void OnTimelineSegmentMouseMove(object sender, MouseEventArgs e)
        {
            if (!_isDraggingSegment && e.LeftButton == MouseButtonState.Pressed && _segmentDragOrigin.HasValue)
            {
                Point mousePosition = e.GetPosition(tracksListBox);

                if (Math.Abs(mousePosition.X - _segmentDragOrigin.Value.X) >= SystemParameters.MinimumHorizontalDragDistance ||
                    Math.Abs(mousePosition.Y - _segmentDragOrigin.Value.Y) >= SystemParameters.MinimumVerticalDragDistance)
                {
                    VideoTimelineSegment draggedSegmentElement = sender as VideoTimelineSegment;
                    Debug.Assert(draggedSegmentElement != null);

                    SegmentViewModelBase draggedSegmentViewModel = draggedSegmentElement.DataContext as SegmentViewModelBase;
                    Debug.Assert(draggedSegmentViewModel != null);

                    _isDraggingSegment = true;

                    DataObject dataObject = new DataObject();
                    dataObject.SetData(typeof(SegmentViewModelWeakReference), new SegmentViewModelWeakReference(draggedSegmentViewModel));
                    dataObject.SetData(typeof(Size), draggedSegmentElement.RenderSize);

                    DragDrop.DoDragDrop(tracksListBox, dataObject, DragDropEffects.Copy | DragDropEffects.Move);
                }
            }
        }
        /// <inheritdoc/>
        public override SegmentViewModelBase CreateSegmentModelViewModel(SegmentViewModelBase sourceViewModel, int?trackNumber = null, int?startFrame = null, int?endFrame = null, string name = null, KeyFrameViewModelCollection keyFrameViewModels = null)
        {
            int    newTrackNumber = trackNumber.GetValueOrDefault(sourceViewModel.TrackNumber);
            int    newStartFrame  = startFrame.GetValueOrDefault(sourceViewModel.StartFrame);
            int    newEndFrame    = endFrame.GetValueOrDefault(sourceViewModel.EndFrame);
            string newName        = name ?? sourceViewModel.Name;

            KeyFrameModelCollection keyFrameModels = keyFrameViewModels != null
                                                     ? GetKeyFrameModels(keyFrameViewModels)
                                                     : GetKeyFrameModels(sourceViewModel.KeyFrameViewModels, true, newStartFrame - sourceViewModel.StartFrame);

            return(new MockSegmentViewModel(
                       new MockSegmentModel(newStartFrame, newEndFrame, newTrackNumber, keyFrameModels, newName),
                       _scriptVideoContext, _rootUndoObject, _undoService, _undoChangeFactory, _clipboardService, keyFrameViewModels
                       ));
        }
        public void CanMoveTrackSegmentTest()
        {
            SetupViewModel(useRealUndoService: false);

            SegmentViewModelBase segmentToMove = _timelineSegmentProvidingViewModel.SegmentViewModels[6];

            Assert.True(
                _viewModel.CanMoveTrackSegment(segmentToMove, 2, 0)
                );

            Assert.False(
                _viewModel.CanMoveTrackSegment(segmentToMove, 1, 1)
                );

            segmentToMove = _timelineSegmentProvidingViewModel.SegmentViewModels[5];
            Assert.True(
                _viewModel.CanMoveTrackSegment(segmentToMove, 1, 1)
                );
        }
Esempio n. 5
0
        /// <summary>
        /// Handles the <see cref="VideoTimelineSegment.HorizontalResizeDragCompletedEvent"/> attached event
        /// for a <see cref="VideoTimelineSegment"/> element.
        /// </summary>
        /// <inheritdoc cref="HorizontalResizeDragCompletedEventHandler"/>
        private void OnTrackSegmentHorizontalResizeDragCompleted(object sender, HorizontalResizeDragCompletedEventArgs e)
        {
            if (_isResizingSegment)
            {
                VideoTimelineSegment segmentElement = e.OriginalSource as VideoTimelineSegment;
                Debug.Assert(segmentElement != null);

                VideoTimelineTrack trackElement = segmentElement.ParentTrackElement;

                SegmentViewModelBase segmentViewModel = segmentElement.DataContext as SegmentViewModelBase;
                Debug.Assert(segmentViewModel != null);

                int frameDiff = (int)Math.Round(_segmentResizeAdorner.HorizontalOffset / trackElement.PixelsPerFrame);
                if (frameDiff != 0)
                {
                    if (e.HorizontalSide == HorizontalSide.Left)
                    {
                        int newStartFrame = segmentViewModel.StartFrame + frameDiff;
                        ViewModel.ChangeTrackSegmentStartFrame(segmentViewModel, newStartFrame);
                    }
                    else // e.HorizontalSide == HorizontalSide.Right
                    {
                        int newEndFrame = segmentViewModel.EndFrame + frameDiff;
                        ViewModel.ChangeTrackSegmentEndFrame(segmentViewModel, newEndFrame);
                    }
                }

                segmentElement.Clip = null;

                _segmentResizeAdorner?.Detach();
                _segmentResizeAdorner = null;

                _isResizingSegment = false;
            }

            e.Handled = true;
        }
        [InlineData(20)]   // 'Contract' test
        public void ChangeTrackSegmentStartFrame_SingleKeyFrame_Test(int newStartFrame)
        {
            SetupViewModel(useRealUndoService: true);

            SegmentViewModelBase segmentToTest = _viewModel.TimelineSegments[8];

            int originalStartFrame = segmentToTest.StartFrame;

            Assert.True(_viewModel.CanChangeTrackSegmentStartFrame(segmentToTest, newStartFrame));

            _viewModel.ChangeTrackSegmentStartFrame(segmentToTest, newStartFrame);
            Assert.Equal(newStartFrame, segmentToTest.StartFrame);
            Assert.Single(segmentToTest.Model.KeyFrames);
            Assert.Single(segmentToTest.KeyFrameViewModels);
            Assert.Equal(newStartFrame, segmentToTest.KeyFrameViewModels[0].FrameNumber);

            Assert.True(_undoRoot.CanUndo);
            _undoRoot.Undo();

            Assert.Equal(originalStartFrame, segmentToTest.StartFrame);
            Assert.Single(segmentToTest.Model.KeyFrames);
            Assert.Single(segmentToTest.KeyFrameViewModels);
            Assert.Equal(originalStartFrame, segmentToTest.KeyFrameViewModels[0].FrameNumber);
        }
Esempio n. 7
0
        /// <summary>
        /// Handles the <see cref="UIElement.PreviewDrop"/> routed event for the <see cref="tracksListBox"/>.
        /// </summary>
        /// <remarks>Completes a <see cref="VideoTimelineSegment"/> dragging operation.</remarks>
        /// <inheritdoc cref="DragEventHandler"/>
        private void OnTracksListBoxPreviewDrop(object sender, DragEventArgs e)
        {
            if (e.Effects != DragDropEffects.None)
            {
                bool isAdding  = false;
                bool isCopying = false;
                bool isMoving  = false;

                SegmentViewModelBase segmentViewModel = null;
                Enum addableSegmentType       = null;
                int? draggedItemStartFrame    = null;
                int  draggedItemFrameDuration = 0;

                if (e.Data.GetDataPresent(typeof(SegmentViewModelWeakReference)))
                {
                    if (!(e.Data.GetData(typeof(SegmentViewModelWeakReference)) is SegmentViewModelWeakReference segmentViewModelRef) ||
                        !segmentViewModelRef.TryGetTarget(out segmentViewModel))
                    {
                        throw new InvalidOperationException($"Invalid reference to a {nameof(SegmentViewModelBase)} object.");
                    }

                    draggedItemStartFrame    = segmentViewModel.StartFrame;
                    draggedItemFrameDuration = segmentViewModel.FrameDuration;

                    if (e.Effects.HasFlag(DragDropEffects.Copy) && e.KeyStates.HasFlag(DragDropKeyStates.ControlKey))
                    {
                        isCopying = true;
                    }
                    else if (e.Effects.HasFlag(DragDropEffects.Move))
                    {
                        isMoving = true;
                    }
                }
                else if (e.Data.GetDataPresent(typeof(WeakReference <AddTrackSegmentCommandParameters>)))
                {
                    if (!(e.Data.GetData(typeof(WeakReference <AddTrackSegmentCommandParameters>)) is WeakReference <AddTrackSegmentCommandParameters> addTrackSegmentActionDataRef) ||
                        !addTrackSegmentActionDataRef.TryGetTarget(out AddTrackSegmentCommandParameters addTrackSegmentActionData))
                    {
                        throw new InvalidOperationException($"Invalid reference to a {nameof(AddTrackSegmentCommandParameters)} object.");
                    }

                    addableSegmentType       = addTrackSegmentActionData.SegmentTypeDescriptor;
                    draggedItemStartFrame    = 0;
                    draggedItemFrameDuration = addTrackSegmentActionData.FrameDuration;
                    isAdding = true;
                }

                if (draggedItemStartFrame.HasValue &&
                    TryFindTrackElementAndClosestFrameNumber(e, draggedItemStartFrame.Value,
                                                             out VideoTimelineTrack timelineTrackElement,
                                                             out int closestFrameNumber))
                {
                    if (isAdding)
                    {
                        ViewModel.AddTrackSegment(addableSegmentType, timelineTrackElement.TrackNumber,
                                                  closestFrameNumber, draggedItemFrameDuration);
                    }
                    else if (isCopying)
                    {
                        ViewModel.CopyTrackSegment(segmentViewModel, timelineTrackElement.TrackNumber, closestFrameNumber);
                    }
                    else if (isMoving)
                    {
                        ViewModel.MoveTrackSegment(segmentViewModel, timelineTrackElement.TrackNumber, closestFrameNumber);
                    }
                }
            }

            EndSegmentDrag();

            e.Handled = true;
        }
Esempio n. 8
0
        /// <summary>
        /// Handles the <see cref="UIElement.PreviewDragOver"/> routed event for the <see cref="tracksListBox"/>.
        /// </summary>
        /// <remarks>Performs validation for a <see cref="VideoTimelineSegment"/> dragging operation.</remarks>
        /// <inheritdoc cref="DragEventHandler"/>
        private void OnTracksListBoxPreviewDragOver(object sender, DragEventArgs e)
        {
            e.Effects = DragDropEffects.None;

            if (_isDraggingSegment && _segmentDragOrigin.HasValue)
            {
                SegmentViewModelBase segmentViewModel = null;
                int?draggedItemStartFrame             = null;
                int draggedItemFrameDuration          = 0;

                if (e.Data.GetDataPresent(typeof(SegmentViewModelWeakReference)))
                {
                    if (!(e.Data.GetData(typeof(SegmentViewModelWeakReference)) is SegmentViewModelWeakReference segmentViewModelRef) ||
                        !segmentViewModelRef.TryGetTarget(out segmentViewModel))
                    {
                        throw new InvalidOperationException($"Invalid reference to a {nameof(SegmentViewModelBase)} object.");
                    }

                    draggedItemStartFrame    = segmentViewModel.StartFrame;
                    draggedItemFrameDuration = segmentViewModel.FrameDuration;
                }
                else if (e.Data.GetDataPresent(typeof(WeakReference <AddTrackSegmentCommandParameters>)))
                {
                    if (!(e.Data.GetData(typeof(WeakReference <AddTrackSegmentCommandParameters>)) is WeakReference <AddTrackSegmentCommandParameters> addTrackSegmentActionDataRef) ||
                        !addTrackSegmentActionDataRef.TryGetTarget(out AddTrackSegmentCommandParameters addTrackSegmentActionData))
                    {
                        throw new InvalidOperationException($"Invalid reference to a {nameof(AddTrackSegmentCommandParameters)} object.");
                    }

                    draggedItemStartFrame    = 0;
                    draggedItemFrameDuration = addTrackSegmentActionData.FrameDuration;
                }

                if (draggedItemStartFrame.HasValue &&
                    TryFindTrackElementAndClosestFrameNumber(e, draggedItemStartFrame.Value,
                                                             out VideoTimelineTrack timelineTrackElement,
                                                             out int closestFrameNumber))
                {
                    if (e.AllowedEffects == DragDropEffects.Copy ||
                        (e.AllowedEffects.HasFlag(DragDropEffects.Copy) && e.KeyStates.HasFlag(DragDropKeyStates.ControlKey)))
                    {
                        if (ViewModel.CanAddTrackSegment(timelineTrackElement.TrackNumber, closestFrameNumber, draggedItemFrameDuration))
                        {
                            e.Effects = DragDropEffects.Copy;
                        }
                    }
                    else if (e.AllowedEffects.HasFlag(DragDropEffects.Move) &&
                             ViewModel.CanMoveTrackSegment(segmentViewModel, timelineTrackElement.TrackNumber, closestFrameNumber))
                    {
                        e.Effects = DragDropEffects.Move;
                    }

                    _segmentDragAdorner.PositionOffset = timelineTrackElement.TranslatePoint(new Point(closestFrameNumber * timelineTrackElement.PixelsPerFrame, 0d),
                                                                                             tracksListBox);

                    _segmentDragAdorner.ToolTipData.StartFrame = closestFrameNumber;
                    _segmentDragAdorner.ToolTipData.EndFrame   = closestFrameNumber + draggedItemFrameDuration - 1;
                }

                _segmentDragAdorner.IsDragValid = e.Effects == DragDropEffects.Move || e.Effects == DragDropEffects.Copy;
            }

            e.Handled = true;
        }
Esempio n. 9
0
        /// <summary>
        /// Handles the <see cref="VideoTimelineSegment.HorizontalResizeDraggingEvent"/> attached event
        /// for a <see cref="VideoTimelineSegment"/> element.
        /// </summary>
        /// <inheritdoc cref="HorizontalResizeDraggingEventHandler"/>
        private void OnTrackSegmentHorizontalResizeDragging(object sender, HorizontalResizeDragEventArgs e)
        {
            e.Handled = true;

            VideoTimelineSegment segmentElement = e.OriginalSource as VideoTimelineSegment;

            Debug.Assert(segmentElement != null);

            VideoTimelineTrack trackElement = segmentElement.ParentTrackElement;

            SegmentViewModelBase segmentViewModel = segmentElement.DataContext as SegmentViewModelBase;

            Debug.Assert(segmentViewModel != null);

            int frameDiff = (int)Math.Round(e.HorizontalChange / trackElement.PixelsPerFrame);

            if (frameDiff == 0)
            {
                return;
            }

            int    proposedStartFrame = segmentViewModel.StartFrame;
            int    proposedEndFrame   = segmentViewModel.EndFrame;
            double framePixelOffset   = trackElement.PixelsPerFrame * frameDiff;
            bool   isContracting      = false;
            Rect   segmentElementClipRect;

            if (e.HorizontalSide == HorizontalSide.Left)
            {
                proposedStartFrame += frameDiff;
                if (!ViewModel.CanChangeTrackSegmentStartFrame(segmentViewModel, proposedStartFrame))
                {
                    return;
                }

                if (frameDiff > 0)
                {
                    isContracting          = true;
                    segmentElementClipRect = new Rect(framePixelOffset, 0d, segmentElement.ActualWidth - framePixelOffset, segmentElement.ActualHeight);
                }
            }
            else // e.HorizontalSide == HorizontalSide.Right
            {
                proposedEndFrame += frameDiff;
                if (!ViewModel.CanChangeTrackSegmentEndFrame(segmentViewModel, proposedEndFrame))
                {
                    return;
                }

                if (frameDiff < 0)
                {
                    isContracting = true;

                    segmentElementClipRect = new Rect(
                        new Size(segmentElement.ActualWidth + framePixelOffset, segmentElement.ActualHeight)
                        );
                }
            }

            if (isContracting)
            {
                if (segmentElement.Clip is RectangleGeometry rectangleGeometry)
                {
                    rectangleGeometry.Rect = segmentElementClipRect;
                }
                else
                {
                    segmentElement.Clip = new RectangleGeometry(segmentElementClipRect);
                }
            }
            else
            {
                segmentElement.Clip = null;
            }

            if (_segmentResizeAdorner == null)
            {
                UIElement segmentElementContainer = segmentElement.TemplatedParent as UIElement;
                Debug.Assert(segmentElementContainer != null);

                _segmentResizeAdorner = new TrackSegmentResizeAdorner(adornedElement: trackElement, segmentElement,
                                                                      segmentElementLeftCoordinate: Canvas.GetLeft(segmentElementContainer),
                                                                      attachPosition: e.HorizontalSide);
                _segmentResizeAdorner.Attach();

                _isResizingSegment = true;
            }

            _segmentResizeAdorner.HorizontalOffset       = framePixelOffset;
            _segmentResizeAdorner.ToolTipData.StartFrame = proposedStartFrame;
            _segmentResizeAdorner.ToolTipData.EndFrame   = proposedEndFrame;
        }
        public void MoveTrackSegmentTest(int preTestFrameNumber, int preTestTrackIndex, int segmentViewModelIndex, bool isSelectedSegmentPreMove, int newTrackNumber, int newStartFrame)
        {
            SetupViewModel(useRealUndoService: true);

            IScriptVideoContext svc = _scriptVideoContextMock.Object;

            if (svc.FrameNumber != preTestFrameNumber)
            {
                svc.FrameNumber = preTestFrameNumber;
            }

            _viewModel.SelectedTrack = _viewModel.TimelineTrackCollection[preTestTrackIndex];

            SegmentViewModelBase segmentToMove = _timelineSegmentProvidingViewModel.SegmentViewModels[segmentViewModelIndex];

            Assert.Equal(isSelectedSegmentPreMove, segmentToMove == _viewModel.TimelineSegmentProvidingViewModel.SelectedSegment);

            int originalTrackNumber = segmentToMove.TrackNumber;
            int originalStartFrame  = segmentToMove.StartFrame;
            int originalEndFrame    = segmentToMove.EndFrame;
            int newEndFrame         = newStartFrame + segmentToMove.FrameDuration - 1;

            // Key frame tests
            int startFrameOffset = newStartFrame - originalStartFrame;

            int[] originalKeyFrameNumbers = segmentToMove.KeyFrameViewModels.Select(kfvm => kfvm.FrameNumber).ToArray();

            _viewModel.MoveTrackSegment(segmentToMove, newTrackNumber, newStartFrame);
            Assert.Equal(newTrackNumber, segmentToMove.TrackNumber);
            Assert.Equal(newStartFrame, segmentToMove.StartFrame);
            Assert.Equal(newEndFrame, segmentToMove.EndFrame);

            Assert.Equal(1, _viewModel.TimelineSegments.Count(segment => segment == segmentToMove));

            Assert.DoesNotContain(segmentToMove, _viewModel.TimelineTrackCollection[originalTrackNumber].TrackSegments);
            Assert.Contains(segmentToMove, _viewModel.TimelineTrackCollection[newTrackNumber].TrackSegments);

            // Key frame tests
            for (int i = 0; i < segmentToMove.KeyFrameViewModels.Count; i++)
            {
                Assert.Equal(originalKeyFrameNumbers[i] + startFrameOffset, segmentToMove.KeyFrameViewModels[i].FrameNumber);
            }

            if (isSelectedSegmentPreMove)
            {
                Assert.False(segmentToMove == _viewModel.TimelineSegmentProvidingViewModel.SelectedSegment);
            }

            Assert.True(_undoRoot.CanUndo);
            _undoRoot.Undo();

            Assert.Equal(originalTrackNumber, segmentToMove.TrackNumber);
            Assert.Equal(originalStartFrame, segmentToMove.StartFrame);
            Assert.Equal(originalEndFrame, segmentToMove.EndFrame);

            Assert.Equal(1, _viewModel.TimelineSegments.Count(segment => segment == segmentToMove));

            Assert.DoesNotContain(segmentToMove, _viewModel.TimelineTrackCollection[newTrackNumber].TrackSegments);
            Assert.Contains(segmentToMove, _viewModel.TimelineTrackCollection[originalTrackNumber].TrackSegments);

            // Key frame tests
            for (int i = 0; i < segmentToMove.KeyFrameViewModels.Count; i++)
            {
                Assert.Equal(originalKeyFrameNumbers[i], segmentToMove.KeyFrameViewModels[i].FrameNumber);
            }

            Assert.Equal(isSelectedSegmentPreMove, segmentToMove == _viewModel.TimelineSegmentProvidingViewModel.SelectedSegment);
        }
        public void JoinTrackSegmentRightCommandTest(int frameNumber, int trackIndex, int trackSegmentIndex, bool expectedCanExecute, bool expectedRightSelectedSegment)
        {
            SetupViewModel(useRealUndoService: expectedCanExecute);

            IScriptVideoContext svc = _scriptVideoContextMock.Object;

            if (svc.FrameNumber != frameNumber)
            {
                svc.FrameNumber = frameNumber;
            }

            IVideoTimelineTrackViewModel timelineTrack        = _viewModel.TimelineTrackCollection[trackIndex];
            SegmentViewModelBase         segmentLeftViewModel = timelineTrack.TrackSegments[trackSegmentIndex];
            int segmentLeftEndFrame      = segmentLeftViewModel.EndFrame;
            int segmentLeftKeyFrameCount = segmentLeftViewModel.KeyFrameViewModels.Count;

            SegmentViewModelBase segmentRightViewModel;
            int segmentRightKeyFrameCount;

            Assert.Equal(expectedCanExecute, _viewModel.MergeTrackSegmentRightCommand.CanExecute(segmentLeftViewModel));
            if (expectedCanExecute)
            {
                segmentRightViewModel = timelineTrack.TrackSegments[trackSegmentIndex + 1];
                Assert.Equal(expectedRightSelectedSegment, segmentRightViewModel == _viewModel.TimelineSegmentProvidingViewModel.SelectedSegment);
                segmentRightKeyFrameCount = segmentRightViewModel.KeyFrameViewModels.Count;

                _viewModel.MergeTrackSegmentRightCommand.Execute(segmentLeftViewModel);

                RunPreUndoTests();

                _undoRoot.Undo();
                RunPostUndoTests();

                _undoRoot.Redo();
                RunPreUndoTests();
            }

            void RunPreUndoTests()
            {
                Assert.Empty(segmentRightViewModel.KeyFrameViewModels);
                Assert.Empty(segmentRightViewModel.Model.KeyFrames);
                Assert.DoesNotContain(segmentRightViewModel, _viewModel.TimelineSegments);
                Assert.DoesNotContain(segmentRightViewModel, timelineTrack.TrackSegments);

                Assert.Equal(segmentLeftKeyFrameCount + segmentRightKeyFrameCount, segmentLeftViewModel.KeyFrameViewModels.Count);
                Assert.Equal(segmentLeftKeyFrameCount + segmentRightKeyFrameCount, segmentLeftViewModel.Model.KeyFrames.Count);
                Assert.Equal(segmentRightViewModel.EndFrame, segmentLeftViewModel.EndFrame);

                if (expectedRightSelectedSegment)
                {
                    Assert.False(segmentRightViewModel == _viewModel.TimelineSegmentProvidingViewModel.SelectedSegment);
                    Assert.True(segmentLeftViewModel == _viewModel.TimelineSegmentProvidingViewModel.SelectedSegment);
                }

                Assert.True(_undoRoot.CanUndo);
            }

            void RunPostUndoTests()
            {
                Assert.Contains(segmentRightViewModel, _viewModel.TimelineSegments);
                Assert.Contains(segmentRightViewModel, timelineTrack.TrackSegments);

                Assert.Equal(segmentLeftEndFrame, segmentLeftViewModel.EndFrame);
                Assert.Equal(segmentLeftKeyFrameCount, segmentLeftViewModel.KeyFrameViewModels.Count);
                Assert.Equal(segmentLeftKeyFrameCount, segmentLeftViewModel.Model.KeyFrames.Count);

                Assert.Equal(segmentRightKeyFrameCount, segmentRightViewModel.KeyFrameViewModels.Count);
                Assert.Equal(segmentRightKeyFrameCount, segmentRightViewModel.Model.KeyFrames.Count);

                if (expectedRightSelectedSegment)
                {
                    Assert.False(segmentLeftViewModel == _viewModel.TimelineSegmentProvidingViewModel.SelectedSegment);
                    Assert.True(segmentRightViewModel == _viewModel.TimelineSegmentProvidingViewModel.SelectedSegment);
                }

                Assert.True(_undoRoot.CanRedo);
            }
        }
        public void SplitSelectedTrackSegmentCommandTest(int trackNumber, int frameNumber, int[] expectedToSplitKeyFrameNumbers, int[] expectedSplitKeyFrameNumbers)
        {
            SetupViewModel(useRealUndoService: true);
            IScriptVideoContext svc = _scriptVideoContextMock.Object;

            if (_viewModel.SelectedTrack.TrackNumber != trackNumber)
            {
                _viewModel.SelectedTrack = _viewModel.TimelineTrackCollection[trackNumber];
            }

            if (svc.FrameNumber != frameNumber)
            {
                svc.FrameNumber = frameNumber;
            }

            Assert.NotNull(_viewModel.TimelineSegmentProvidingViewModel.SelectedSegment);
            SegmentViewModelBase segmentViewModelToSplit = _viewModel.TimelineSegmentProvidingViewModel.SelectedSegment;
            int originalEndFrame = segmentViewModelToSplit.EndFrame;

            int[] originalKeyFrameNumbers = segmentViewModelToSplit.KeyFrameViewModels.Select(kfvm => kfvm.FrameNumber).ToArray();

            Assert.True(
                _viewModel.SplitSelectedTrackSegmentCommand.CanExecute()
                );
            _viewModel.SplitSelectedTrackSegmentCommand.Execute();

            SegmentViewModelBase splitSegmentViewModel;

            RunPreUndoTests();

            _undoRoot.Undo();
            RunPostUndoTests();

            _undoRoot.Redo();
            RunPreUndoTests();

            void RunPreUndoTests()
            {
                Assert.NotNull(_viewModel.TimelineSegmentProvidingViewModel.SelectedSegment);
                Assert.NotSame(segmentViewModelToSplit, _viewModel.TimelineSegmentProvidingViewModel.SelectedSegment);

                Assert.False(
                    _viewModel.SplitSelectedTrackSegmentCommand.CanExecute()
                    );

                splitSegmentViewModel = _viewModel.TimelineSegmentProvidingViewModel.SelectedSegment;

                Assert.Equal(frameNumber - 1, segmentViewModelToSplit.EndFrame);
                Assert.Equal(expectedToSplitKeyFrameNumbers.Length, segmentViewModelToSplit.KeyFrameViewModels.Count);
                for (int i = 0; i < expectedToSplitKeyFrameNumbers.Length; i++)
                {
                    Assert.Equal(expectedToSplitKeyFrameNumbers[i], segmentViewModelToSplit.KeyFrameViewModels[i].FrameNumber);
                }

                Assert.Equal(frameNumber, splitSegmentViewModel.StartFrame);
                Assert.Equal(originalEndFrame, splitSegmentViewModel.EndFrame);

                Assert.Equal(expectedSplitKeyFrameNumbers.Length, splitSegmentViewModel.KeyFrameViewModels.Count);
                for (int i = 0; i < expectedSplitKeyFrameNumbers.Length; i++)
                {
                    Assert.Equal(expectedSplitKeyFrameNumbers[i], splitSegmentViewModel.KeyFrameViewModels[i].FrameNumber);
                }

                Assert.True(_undoRoot.CanUndo);
            }

            void RunPostUndoTests()
            {
                Assert.Same(segmentViewModelToSplit, _viewModel.TimelineSegmentProvidingViewModel.SelectedSegment);
                Assert.DoesNotContain(splitSegmentViewModel, _viewModel.TimelineSegments);

                Assert.True(
                    _viewModel.SplitSelectedTrackSegmentCommand.CanExecute()
                    );

                Assert.Equal(originalEndFrame, segmentViewModelToSplit.EndFrame);

                Assert.Equal(originalKeyFrameNumbers.Length, segmentViewModelToSplit.KeyFrameViewModels.Count);
                for (int i = 0; i < originalKeyFrameNumbers.Length; i++)
                {
                    Assert.Equal(originalKeyFrameNumbers[i], segmentViewModelToSplit.KeyFrameViewModels[i].FrameNumber);
                }

                Assert.True(_undoRoot.CanRedo);
            }
        }