// Reparents a list of tracks to a new parent
        //  the new parent cannot be null (has to be track asset or sequence)
        //  the insertAfter can be null (will not reorder)
        internal static bool ReparentTracks(List <TrackAsset> tracksToMove, PlayableAsset targetParent,
                                            TrackAsset insertMarker = null, bool insertBefore = false)
        {
            var targetParentTrack   = targetParent as TrackAsset;
            var targetSequenceTrack = targetParent as TimelineAsset;

            if (tracksToMove == null || tracksToMove.Count == 0 || (targetParentTrack == null && targetSequenceTrack == null))
            {
                return(false);
            }

            // invalid parent type on a track
            if (targetParentTrack != null && tracksToMove.Any(x => !TimelineCreateUtilities.ValidateParentTrack(targetParentTrack, x.GetType())))
            {
                return(false);
            }

            // no valid tracks means this is simply a rearrangement
            var validTracks = tracksToMove.Where(x => x.parent != targetParent).ToList();

            if (insertMarker == null && !validTracks.Any())
            {
                return(false);
            }

            var parents = validTracks.Select(x => x.parent).Where(x => x != null).Distinct().ToList();

            // push the current state of the tracks that will change
            foreach (var p in parents)
            {
                UndoExtensions.RegisterPlayableAsset(p, "Reparent");
            }
            UndoExtensions.RegisterTracks(validTracks, "Reparent");
            UndoExtensions.RegisterPlayableAsset(targetParent, "Reparent");

            // need to reparent tracks first, before moving them.
            foreach (var t in validTracks)
            {
                if (t.parent != targetParent)
                {
                    TrackAsset    toMoveParent   = t.parent as TrackAsset;
                    TimelineAsset toMoveTimeline = t.parent as TimelineAsset;
                    if (toMoveTimeline != null)
                    {
                        toMoveTimeline.RemoveTrack(t);
                    }
                    else if (toMoveParent != null)
                    {
                        toMoveParent.RemoveSubTrack(t);
                    }

                    if (targetParentTrack != null)
                    {
                        targetParentTrack.AddChild(t);
                        targetParentTrack.SetCollapsed(false);
                    }
                    else
                    {
                        targetSequenceTrack.AddTrackInternal(t);
                    }
                }
            }


            if (insertMarker != null)
            {
                // re-ordering track. This is using internal APIs, so invalidation of the tracks must be done manually to avoid
                //  cache mismatches
                var children = targetParentTrack != null ? targetParentTrack.subTracksObjects : targetSequenceTrack.trackObjects;
                TimelineUtility.ReorderTracks(children, tracksToMove, insertMarker, insertBefore);
                if (targetParentTrack != null)
                {
                    targetParentTrack.Invalidate();
                }
                if (insertMarker.timelineAsset != null)
                {
                    insertMarker.timelineAsset.Invalidate();
                }
            }

            return(true);
        }
        public void Grab(IEnumerable <ITimelineItem> items, TrackAsset referenceTrack, Vector2 mousePosition)
        {
            if (items == null)
            {
                return;
            }

            items = items.ToArray(); // Cache enumeration result

            if (!items.Any())
            {
                return;
            }

            m_GrabbedModalUndoGroup = Undo.GetCurrentGroup();

            var trackItems      = items.GroupBy(c => c.parentTrack).ToArray();
            var trackItemsCount = trackItems.Length;
            var tracks          = items.Select(c => c.parentTrack).Where(x => x != null).Distinct();

            movingItems = new MovingItems[trackItemsCount];

            allowTrackSwitch = trackItemsCount == 1 && !trackItems.SelectMany(x => x).Any(x => x is MarkerItem); // For now, track switch is only supported when all items are on the same track and there are no items

            // one push per track handles all the clips on the track
            UndoExtensions.RegisterTracks(tracks, L10n.Tr("Move Items"));
            foreach (var sourceTrack in tracks)
            {
                // push all markers on the track because of ripple
                UndoExtensions.RegisterMarkers(sourceTrack.GetMarkers(), L10n.Tr("Move Items"));
            }

            for (var i = 0; i < trackItemsCount; ++i)
            {
                var track        = trackItems[i].Key;
                var grabbedItems = new MovingItems(m_State, track, trackItems[i].ToArray(), referenceTrack, mousePosition, allowTrackSwitch);
                movingItems[i] = grabbedItems;
            }

            m_LeftMostMovingItems  = null;
            m_RightMostMovingItems = null;

            foreach (var grabbedTrackItems in movingItems)
            {
                if (m_LeftMostMovingItems == null || m_LeftMostMovingItems.start > grabbedTrackItems.start)
                {
                    m_LeftMostMovingItems = grabbedTrackItems;
                }

                if (m_RightMostMovingItems == null || m_RightMostMovingItems.end < grabbedTrackItems.end)
                {
                    m_RightMostMovingItems = grabbedTrackItems;
                }
            }

            m_ItemGUIs   = new HashSet <TimelineItemGUI>();
            m_ItemsGroup = new ItemsGroup(items);

            foreach (var item in items)
            {
                m_ItemGUIs.Add(item.gui);
            }

            targetTrack = referenceTrack;

            EditMode.BeginMove(this);
            m_Grabbing = true;
        }