// Gets the track and group that contains 'target'. Also handles if 'target' is a track or group. private static void GetTrackAndGroup(ITimelineObject target, out ITrack targetTrack, out IGroup targetGroup) { IInterval interval = target as IInterval; if (interval != null) { targetTrack = interval.Track; } else { IKey key = target as IKey; if (key != null) { targetTrack = key.Track; } else { targetTrack = target as ITrack; } } if (targetTrack != null) { targetGroup = targetTrack.Group; } else { targetGroup = target as IGroup; } }
/// <summary> /// Inserts items via drag-and-drop</summary> /// <param name="e">DragEventArgs containing drag and drop event data</param> public void Insert(DragEventArgs e) { IEnumerable <ITimelineObject> itemCopies; if (m_timelineControl.DragDropObjects != null) { itemCopies = m_timelineControl.DragDropObjects; m_timelineControl.DragDropObjects = null; } else { itemCopies = ConvertDrop(e); } Point clientPoint = m_timelineControl.PointToClient(new Point(e.X, e.Y)); PointF mouseLocation = clientPoint; ITimelineObject dropTarget = Pick(mouseLocation); List <TimelinePath> newSelection = new List <TimelinePath>(); foreach (ITimelineObject droppedItem in itemCopies) { Insert(droppedItem, dropTarget); newSelection.Add(new TimelinePath(droppedItem)); } Selection.SetRange(newSelection.AsIEnumerable <object>()); }
private void DomNode_AttributeChanging(object sender, AttributeEventArgs e) { ITimelineObject item = e.DomNode.As <ITimelineObject>(); if (item != null) { if (!IsEditable(item, e.AttributeInfo)) { if (ActiveDocument.Cast <ITransactionContext>().InTransaction) { throw new InvalidTransactionException("timeline object can't be edited"); } else { return; } } // Check if a URI on a timeline reference has changed, so we can unload // old document and load new document. if (e.AttributeInfo.Equivalent(Schema.timelineRefType.refAttribute)) { UnloadSubDocument((Uri)e.OldValue); LoadSubDocument((Uri)e.NewValue); } } }
private bool IsEditable(ITimelineObject item) { var path = new TimelinePath(item); TimelineDocument document = (TimelineDocument)TimelineEditor.TimelineDocumentRegistry.ActiveDocument; if (document != null) { return(document.TimelineControl.IsEditable(path)); } return(false); }
/// <summary> /// Checks whether the given timeline object's attribute is editable for the current /// context and document</summary> /// <param name="item">Timeline object that changed</param> /// <param name="attribute">Attribute on the timeline object that changed</param> /// <returns>True iff this timeline object attribute is editable for the current /// ActiveControl, ActiveContext, and ActiveDocument properties</returns> public virtual bool IsEditable(ITimelineObject item, AttributeInfo attribute) { if (attribute == Schema.groupType.expandedAttribute) { return(true); } TimelinePath path = new TimelinePath(item); return(ActiveControl.IsEditable(path)); }
/// <summary> /// Constructor</summary> /// <param name="obj">Ghosted object</param> /// <param name="target">Target to snap ghosted object to</param> /// <param name="start">Start of ghosted object</param> /// <param name="length">Length of ghosted object</param> /// <param name="bounds">Bounds of ghosted object</param> /// <param name="valid">Whether ghosted object is valid</param> public GhostInfo( ITimelineObject obj, ITimelineObject target, float start, float length, RectangleF bounds, bool valid) { Object = obj; Target = target; Start = start; Length = length; Bounds = bounds; Valid = valid; }
/// <summary> /// Adds new object using a transaction so the history context can be verified. /// Called by automated scripts during testing.</summary> /// <typeparam name="T">Type of object added</typeparam> /// <param name="objectToInsert">Object to add</param> /// <param name="objectToInsertTo">Parent object to add to</param> /// <returns>Object added as type T</returns> public T Insert <T>(DomNode objectToInsert, DomNode objectToInsertTo) where T : class { ITimelineObject source = objectToInsert.As <ITimelineObject>(); ITimelineObject destination = objectToInsertTo.As <ITimelineObject>(); ITransactionContext transactionContext = this.As <ITransactionContext>(); transactionContext.DoTransaction( delegate { Insert(source, destination); }, "Scripted Insert Object"); return(objectToInsert.As <T>()); }
// Determines which tracks to place the targets timeline objects in. Creates new tracks if necessary. // Returns a dictionary mapping target timeline objects to the tracks they should be placed in. private static Dictionary <ITimelineObject, ITrack> CreateTrackMappings( ITimeline timeline, IList <Tuple <ITimelineObject, ITimelineObject> > sourceTargetPairs, ITrack centerTrack, Dictionary <ITimelineObject, ITrack> sourceObjToTrack) { // Gather together all the source tracks. Use the dictionary instead of // the source objects in 'sourceTargetPairs' because the source objects may // no longer be parented to a track. (Because of Cut and Paste.) List <ITrack> sourceTracks = new List <ITrack>(sourceObjToTrack.Values); // Sort the source tracks from top-to-bottom SortTracks(timeline, sourceTracks); // Get or create the target tracks List <ITrack> targetTracks = GetOrCreateOrderedTracks(timeline, sourceTracks.Count, centerTrack); // Prepare a mapping from source track to target track var sourceTrackToTargetTrack = new Dictionary <ITrack, ITrack>(); for (int i = 0; i < targetTracks.Count; i++) { sourceTrackToTargetTrack[sourceTracks[i]] = targetTracks[i]; } // Prepare the mapping from target ITimelineObject to target ITrack Dictionary <ITimelineObject, ITrack> targetObjToTrack = new Dictionary <ITimelineObject, ITrack>(); for (int i = 0; i < sourceTargetPairs.Count; i++) { ITimelineObject source = sourceTargetPairs[i].Item1; ITimelineObject target = sourceTargetPairs[i].Item2; ITrack sourceTrack; sourceObjToTrack.TryGetValue(source, out sourceTrack); if (sourceTrack != null) { ITrack targetTrack = sourceTrackToTargetTrack[sourceTrack]; targetObjToTrack[target] = targetTrack; } } return(targetObjToTrack); }
// Gets the bounding box in client window coordinates private RectangleF GetDragDropBounds(ITimelineObject dragDrop) { var bounds = new RectangleF(); var interval = dragDrop as IInterval; if (interval != null) { bounds = m_owner.Renderer.GetBounds(interval, 0, m_owner.Transform, m_owner.ClientRectangle); } var key = dragDrop as IKey; if (key != null) { bounds = m_owner.Renderer.GetBounds(key, 0, m_owner.Transform, m_owner.ClientRectangle); } bounds = GdiUtil.Transform(m_owner.Transform, bounds); return(bounds); }
private void _MoveSelection(bool tryToCopy, bool createTracksAndGroups) { ITimeline timeline = m_owner.TimelineDocument.Timeline; Dictionary <ITrack, ITrack> newTrackMap = new Dictionary <ITrack, ITrack>(); List <Sce.Atf.Pair <ITrack, IEvent> > toAdd = new List <Sce.Atf.Pair <ITrack, IEvent> >(); for (int i = 0; i < m_ghosts.Length; i++) { GhostInfo ghost = m_ghosts[i]; ITimelineObject ghostCopy = null; if (tryToCopy) { ICloneable cloneable = ghost.Object as ICloneable; if (cloneable != null) { ghostCopy = cloneable.Clone() as ITimelineObject; } } ITimelineReference reference = ghost.Object as ITimelineReference; if (reference != null) { if (ghostCopy != null) { reference = (ITimelineReference)ghostCopy; } reference.Start = ghost.Start; if (ghostCopy != null && timeline is IHierarchicalTimelineList) { ((IHierarchicalTimelineList)timeline).References.Add(reference); } continue; } IInterval interval = ghost.Object as IInterval; if (interval != null) { if (ghostCopy != null) { interval = (IInterval)ghostCopy; } interval.Start = ghost.Start; interval.Length = ghost.Length; ITrack target = (ITrack)ghost.Target; if (target != interval.Track && createTracksAndGroups) { if (target == null) { target = CreateTargetTrack(interval.Track, newTrackMap); } if (ghostCopy == null) { interval.Track.Intervals.Remove(interval); } toAdd.Add(new Sce.Atf.Pair <ITrack, IEvent>(target, interval)); } continue; } IKey key = ghost.Object as IKey; if (key != null) { if (ghostCopy != null) { key = (IKey)ghostCopy; } key.Start = ghost.Start; ITrack target = (ITrack)ghost.Target; if (target != key.Track && createTracksAndGroups) { if (target == null) { target = CreateTargetTrack(key.Track, newTrackMap); } if (ghostCopy == null) { key.Track.Keys.Remove(key); } toAdd.Add(new Sce.Atf.Pair <ITrack, IEvent>(target, key)); } continue; } IMarker marker = ghost.Object as IMarker; if (marker != null) { if (ghost.Valid && marker.Start != ghost.Start) { if (ghostCopy != null) { IMarker markerCopy = (IMarker)ghostCopy; markerCopy.Start = ghost.Start; marker.Timeline.Markers.Add(markerCopy); } else { marker.Start = ghost.Start; } } continue; } ITrack track = ghost.Object as ITrack; if (track != null) { var targetTrack = ghost.Target as ITrack; if (targetTrack != null && targetTrack != track) { if (ghostCopy != null) { track = (ITrack)ghostCopy; } int index = targetTrack.Group.Tracks.IndexOf(targetTrack); if (ghostCopy == null) { track.Group.Tracks.Remove(track); } targetTrack.Group.Tracks.Insert(index, track); } else { var targetGroup = ghost.Target as IGroup; if (targetGroup != null) { // the track was dragged to an empty group. http://tracker.ship.scea.com/jira/browse/WWSATF-1371 if (ghostCopy != null) { track = (ITrack)ghostCopy; } else { track.Group.Tracks.Remove(track); } targetGroup.Tracks.Insert(0, track); } } continue; } IGroup group = ghost.Object as IGroup; if (group != null) { IGroup target = (IGroup)ghost.Target; if (target != null && target != group) { if (ghostCopy != null) { group = (IGroup)ghostCopy; } int index = m_owner.TimelineDocument.Timeline.Groups.IndexOf(target); if (ghostCopy == null) { m_owner.TimelineDocument.Timeline.Groups.Remove(group); } m_owner.TimelineDocument.Timeline.Groups.Insert(index, group); } continue; } } // So that when multiple intervals from multiple tracks are relocated to a different // set of tracks, we need to remove them all and then add them all. If the remove and // adds are done in pairs, one by one, then the events can step on each other. artf32260 foreach (Sce.Atf.Pair <ITrack, IEvent> trackEventPair in toAdd) { if (trackEventPair.Second is IInterval) { trackEventPair.First.Intervals.Add((IInterval)trackEventPair.Second); } else { trackEventPair.First.Keys.Add((IKey)trackEventPair.Second); } } }
/// <summary> /// Checks whether the given timeline object's attribute is editable for the current /// context and document</summary> /// <param name="item">Timeline object that changed</param> /// <param name="attribute">Attribute on the timeline object that changed</param> /// <returns>True iff this timeline object attribute is editable for the current /// ActiveControl, ActiveContext, and ActiveDocument properties</returns> public virtual bool IsEditable(ITimelineObject item, AttributeInfo attribute) { if (attribute == Schema.groupType.expandedAttribute) return true; TimelinePath path = new TimelinePath(item); return ActiveControl.IsEditable(path); }
private bool DoCommand(object commandTag, bool doing) { TimelineContext context = m_contextRegistry.GetActiveContext <TimelineContext>(); if (context == null) { return(false); } TimelineDocument document = context.As <TimelineDocument>(); if (document == null) { return(false); } if (commandTag is Command) { if ((Command)commandTag == Command.ToggleSplitMode) { if (doing && document.SplitManipulator != null) { document.SplitManipulator.Active = !document.SplitManipulator.Active; } return(true); } ITimelineObject target = m_contextRegistry.GetCommandTarget <ITimelineObject>(); if (target == null) { return(false); } IInterval activeInterval = target as IInterval; ITrack activeTrack = (activeInterval != null) ? activeInterval.Track : target.As <ITrack>(); IGroup activeGroup = (activeTrack != null) ? activeTrack.Group : target.As <IGroup>(); ITimeline activeTimeline = (activeGroup != null) ? activeGroup.Timeline : target.As <ITimeline>(); ITransactionContext transactionContext = context.TimelineControl.TransactionContext; switch ((Command)commandTag) { case Command.RemoveGroup: if (activeGroup == null) { return(false); } if (doing) { transactionContext.DoTransaction(delegate { activeGroup.Timeline.Groups.Remove(activeGroup); }, "Remove Group"); } return(true); case Command.RemoveTrack: if (activeTrack == null) { return(false); } if (doing) { transactionContext.DoTransaction(delegate { activeTrack.Group.Tracks.Remove(activeTrack); }, "Remove Track"); } return(true); case Command.RemoveEmptyGroupsAndTracks: if (activeTimeline == null) { return(false); } if (doing) { transactionContext.DoTransaction(delegate { IList <IGroup> groups = activeTimeline.Groups; for (int i = 0; i < groups.Count;) { IList <ITrack> tracks = groups[i].Tracks; for (int j = 0; j < tracks.Count;) { if (tracks[j].Intervals.Count == 0 && tracks[j].Keys.Count == 0) { tracks.RemoveAt(j); } else { j++; } } if (tracks.Count == 0) { groups.RemoveAt(i); } else { i++; } } }, "Remove Empty Groups and Tracks"); } return(true); } } else if (commandTag is StandardCommand) { switch ((StandardCommand)commandTag) { case StandardCommand.ViewZoomExtents: if (doing) { document.TimelineControl.Frame(); } return(true); } } return(false); }
// Gets the bounding box in client window coordinates private RectangleF GetDragDropBounds(ITimelineObject dragDrop) { var bounds = new RectangleF(); var interval = dragDrop as IInterval; if (interval != null) bounds = m_owner.Renderer.GetBounds(interval, 0, m_owner.Transform, m_owner.ClientRectangle); var key = dragDrop as IKey; if (key != null) bounds = m_owner.Renderer.GetBounds(key, 0, m_owner.Transform, m_owner.ClientRectangle); bounds = GdiUtil.Transform(m_owner.Transform, bounds); return bounds; }
private void Insert(ITimelineObject droppedItem, ITimelineObject dropTarget) { if (m_timelineDocument == null) { return; } // TimelineControl maintains target group and track ITimeline targetTimeline = m_timelineDocument.Timeline; IGroup targetGroup = m_timelineControl.TargetGroup != null ? (IGroup)m_timelineControl.TargetGroup.Last : null; ITrack targetTrack = m_timelineControl.TargetTrack != null ? (ITrack)m_timelineControl.TargetTrack.Last : null; // in case of drag and drop, use drop target instead if (dropTarget != null) { GetTrackAndGroup(dropTarget, out targetTrack, out targetGroup); } // Work up from insertion point to get target key/interval, track, group, timeline ITimelineReference reference = droppedItem.As <ITimelineReference>(); if (reference != null) { ((Timeline)targetTimeline).AddReference(reference); return; } IGroup group = droppedItem.As <IGroup>(); if (group != null) { // if this is a new group, add a default track if (group.Tracks.Count == 0) { ITrack emptyTrack = group.CreateTrack(); if (emptyTrack != null) { group.Tracks.Add(emptyTrack); } } targetTimeline.Groups.Add(group); return; } IMarker marker = droppedItem.As <IMarker>(); if (marker != null) { targetTimeline.Markers.Add(marker); return; } // Must be track or interval; get the target group, or create one. ITrack track = droppedItem.As <ITrack>(); IEvent ev = droppedItem.As <IEvent>(); if (track != null || ev != null) { IList <IGroup> groups = targetTimeline.Groups; if (targetGroup == null) { targetGroup = targetTimeline.CreateGroup(); groups.Add(targetGroup); } if (track != null) { targetGroup.Tracks.Add(track); } else //if (ev != null) { // interval or key; get the target track or create one if (targetTrack == null) { targetTrack = targetGroup.CreateTrack(); targetGroup.Tracks.Add(targetTrack); } IInterval interval = droppedItem.As <IInterval>(); if (interval != null) { targetTrack.Intervals.Add(interval); } else { IKey key = droppedItem.As <IKey>(); if (key != null) { targetTrack.Keys.Add(key); } } } } }
/// <summary> /// Gets or sets the bounding rectangle, in screen space, of a timeline object that is /// in the main timeline document and so has no parent ITimelineReference objects in /// its path. A TimelinePath is created around this timeline object when setting.</summary> /// <param name="obj">Timeline object</param> public RectangleF this[ITimelineObject obj] { get { return(m_paths[new TimelinePath(obj)]); } set { m_paths[new TimelinePath(obj)] = value; } }
// gets move information, for drawing ghosts and for performing actual move operation private GhostInfo[] GetMoveGhostInfo(Matrix worldToView, TimelineLayout layout) { // get start and y offsets in timeline space PointF dragOffset = m_owner.GetDragOffset(); // Get snapping points along the timeline (in world coordinates). List <float> movingPoints = new List <float>(2); TimelinePath snapperPath; if (m_mouseMoveHitRecord != null) { snapperPath = m_mouseMoveHitRecord.HitPath;//use the last clicked event (interval, key or marker) } else { snapperPath = m_owner.Selection.LastSelected as TimelinePath;//moving a group or track, for example } IEvent snapperEvent = snapperPath != null ? snapperPath.Last as IEvent : null; if (snapperEvent != null) { Matrix localToWorld = TimelineControl.CalculateLocalToWorld(snapperPath); float worldStart = GdiUtil.Transform(localToWorld, snapperEvent.Start + dragOffset.X); movingPoints.Add(worldStart); if (snapperEvent.Length > 0.0f) { movingPoints.Add(GdiUtil.Transform(localToWorld, snapperEvent.Start + dragOffset.X + snapperEvent.Length)); } } // Get the offset from one of the world snap points to the closest non-selected object. float snapOffset; try { s_snapOptions.FilterContext = snapperEvent; s_snapOptions.Filter = new TimelineControl.SnapFilter(MoveSnapFilter); snapOffset = m_owner.GetSnapOffset(movingPoints, s_snapOptions); } finally { s_snapOptions.FilterContext = null; s_snapOptions.Filter = null; } // adjust dragOffset to "snap-to" nearest event dragOffset.X += snapOffset; // get offsets in client space float xOffset = dragOffset.X * worldToView.Elements[0]; float yOffset = dragOffset.Y * worldToView.Elements[3]; TimelinePath[] targets = GetMoveTargets(layout); GhostInfo[] ghosts = new GhostInfo[targets.Length]; int i = -1; foreach (TimelinePath path in m_owner.Selection.Selection) { i++; ITimelineObject timelineObject = path.Last; RectangleF bounds = layout[path]; TimelinePath targetPath = targets[i]; ITimelineObject target = targetPath != null ? targetPath.Last : null; float start = 0; float length = 0; bool valid = true; IInterval interval = timelineObject as IInterval; IKey key = timelineObject as IKey; IMarker marker = timelineObject as IMarker; ITrack track = timelineObject as ITrack; IGroup group = timelineObject as IGroup; ITimelineReference reference = timelineObject as ITimelineReference; if (interval != null) { ITrack targetTrack = target as ITrack; start = interval.Start + dragOffset.X; length = interval.Length; valid = targetTrack != null && m_owner.Constraints.IsStartValid(interval, ref start) && m_owner.Constraints.IsLengthValid(interval, ref length); if (valid) { yOffset = layout[target].Y - layout[interval.Track].Y; TimelinePath testPath = new TimelinePath(targetPath); foreach (IInterval other in targetTrack.Intervals) { // skip selected intervals, since they are moving too testPath.Last = other; if (m_owner.Selection.SelectionContains(testPath)) { continue; } if (!m_owner.Constraints.IsIntervalValid(interval, ref start, ref length, other)) { valid = false; break; } } } } else if (reference != null) { // don't allow for vertical repositioning yet start = reference.Start + dragOffset.X; valid = true; } else if (key != null) { start = key.Start + dragOffset.X; ITrack targetTrack = target as ITrack; valid = targetTrack != null && m_owner.Constraints.IsStartValid(key, ref start); if (valid) { yOffset = layout[targetTrack].Y - layout[key.Track].Y; } } else if (marker != null) { start = marker.Start + dragOffset.X; yOffset = 0; valid = m_owner.Constraints.IsStartValid(marker, ref start); } else if (track != null) { xOffset = 0; if (target == null) { target = (m_owner.DragDelta.Y < 0) ? GetLastTrack() : GetFirstTrack(); } } else if (group != null) { xOffset = 0; if (target == null) { IList <IGroup> groups = m_owner.TimelineDocument.Timeline.Groups; target = (m_owner.DragDelta.Y < 0) ? groups[0] : groups[groups.Count - 1]; } } bounds.Offset(xOffset, yOffset); ghosts[i] = new GhostInfo(timelineObject, target, start, length, bounds, valid); } return(ghosts); }
/// <summary> /// Inserts the data object into the context. /// Generic insert via IInstancingContext. Called from, for example, the standard paste command.</summary> /// <param name="insertingObject">Data to insert; e.g., System.Windows.Forms.IDataObject</param> public void Insert(object insertingObject) { IDataObject dataObject = (IDataObject)insertingObject; // use current document control to center the elements object[] items = dataObject.GetData(typeof(object[])) as object[]; if (items == null) { return; } IEnumerable <DomNode> sourceDomNodes = PathsToDomNodes(items); DomNode[] nodeCopies = DomNode.Copy(sourceDomNodes); var itemSources = new List <ITimelineObject>(sourceDomNodes.AsIEnumerable <ITimelineObject>()); var itemCopies = new List <ITimelineObject>(nodeCopies.AsIEnumerable <ITimelineObject>()); TimelineDocument document = DomNode.Cast <TimelineDocument>(); D2dTimelineControl timelineControl = document.TimelineControl; Rectangle clientRect = m_timelineControl.VisibleClientRectangle; Point clientPoint = new Point( clientRect.Left + clientRect.Width / 2, clientRect.Top + clientRect.Height / 2); CenterEvents(itemCopies.AsIEnumerable <IEvent>(), clientPoint, timelineControl.Transform); var sourceTargetPairs = new List <Tuple <ITimelineObject, ITimelineObject> >(itemSources.Count); for (int i = 0; i < itemSources.Count; i++) { sourceTargetPairs.Add(new Tuple <ITimelineObject, ITimelineObject>(itemSources[i], itemCopies[i])); } // Prepare the mapping of source objects to their tracks. These may be known already (from // a previous Copy operation), but we can't be sure, so let's augment m_copyObjToTrack if // possible. if (m_copyObjToTrack == null) { m_copyObjToTrack = new Dictionary <ITimelineObject, ITrack>(); } foreach (var source in itemSources) { IGroup group; ITrack track; GetTrackAndGroup(source, out track, out group); if (track != null) { m_copyObjToTrack[source] = track; } } // Guess where the user wants to paste. Priority: // 1. The timeline control's target (currently selected) track. ITrack targetTrack = timelineControl.TargetTrack != null ? (ITrack)m_timelineControl.TargetTrack.Last : null; // But only if it's visible. if (targetTrack != null) { if (!IsTrackVisible(targetTrack)) { targetTrack = null; } } // 2. The track in the center of the view. if (targetTrack == null) { ITimelineObject centerObject = Pick(clientPoint); IGroup centerGroup; GetTrackAndGroup(centerObject, out targetTrack, out centerGroup); } // 3. The first visible track if (targetTrack == null) { foreach (IGroup group in Timeline.Groups) { foreach (ITrack track in group.Tracks) { if (IsTrackVisible(track)) { targetTrack = track; break; } } if (targetTrack != null) { break; } } } Dictionary <ITimelineObject, ITrack> copiesToTracks = CreateTrackMappings( m_timelineDocument.Timeline, sourceTargetPairs, targetTrack, m_copyObjToTrack); foreach (ITimelineObject item in itemCopies) { // Not all items will have a track. The item could be a group, for example. ITrack dropTarget; copiesToTracks.TryGetValue(item, out dropTarget); Insert(item, dropTarget); } List <TimelinePath> newSelection = new List <TimelinePath>(); foreach (ITimelineObject copy in itemCopies) { // Would need a way to get the path of ITimelineReference objects.... if (TimelineControl.GetOwningTimeline(copy) != Timeline) { throw new NotImplementedException("We haven't implemented the ability to insert timeline objects into a sub-document"); } newSelection.Add(new TimelinePath(copy)); } Selection.SetRange(newSelection.AsIEnumerable <object>()); }
/// <summary> /// Moves the objects represented by the current ghosts to be at the position of /// the ghosts</summary> protected virtual void MoveSelection() { // To avoid setting the dirty bit in custom applications. // http://sf.ship.scea.com/sf/go/artf22506 PointF dragOffset = m_owner.GetDragOffset(); if ((dragOffset.X == 0.0f) && (dragOffset.Y == 0.0f)) { return; } // If we're dragging up, then don't create new tracks if (dragOffset.Y < 0) { foreach (GhostInfo ghost in m_ghosts) { if (!ghost.Valid) { return; } } } // If Control key is being held down, then we're in copy mode. bool tryToCopy = (Control.ModifierKeys == Keys.Control); m_owner.TransactionContext.DoTransaction(delegate { ITimeline timeline = m_owner.TimelineDocument.Timeline; Dictionary <ITrack, ITrack> newTrackMap = new Dictionary <ITrack, ITrack>(); List <Sce.Atf.Pair <ITrack, IEvent> > toAdd = new List <Sce.Atf.Pair <ITrack, IEvent> >(); for (int i = 0; i < m_ghosts.Length; i++) { GhostInfo ghost = m_ghosts[i]; ITimelineObject ghostCopy = null; if (tryToCopy) { ICloneable cloneable = ghost.Object as ICloneable; if (cloneable != null) { ghostCopy = cloneable.Clone() as ITimelineObject; } } ITimelineReference reference = ghost.Object as ITimelineReference; if (reference != null) { if (ghostCopy != null) { reference = (ITimelineReference)ghostCopy; } reference.Start = ghost.Start; if (ghostCopy != null && timeline is IHierarchicalTimelineList) { ((IHierarchicalTimelineList)timeline).References.Add(reference); } continue; } IInterval interval = ghost.Object as IInterval; if (interval != null) { if (ghostCopy != null) { interval = (IInterval)ghostCopy; } interval.Start = ghost.Start; interval.Length = ghost.Length; ITrack target = (ITrack)ghost.Target; if (target != interval.Track) { if (target == null) { target = CreateTargetTrack(interval.Track, newTrackMap); } if (ghostCopy == null) { interval.Track.Intervals.Remove(interval); } toAdd.Add(new Sce.Atf.Pair <ITrack, IEvent>(target, interval)); } continue; } IKey key = ghost.Object as IKey; if (key != null) { if (ghostCopy != null) { key = (IKey)ghostCopy; } key.Start = ghost.Start; ITrack target = (ITrack)ghost.Target; if (target != key.Track) { if (target == null) { target = CreateTargetTrack(key.Track, newTrackMap); } if (ghostCopy == null) { key.Track.Keys.Remove(key); } toAdd.Add(new Sce.Atf.Pair <ITrack, IEvent>(target, key)); } continue; } IMarker marker = ghost.Object as IMarker; if (marker != null) { if (ghost.Valid && marker.Start != ghost.Start) { if (ghostCopy != null) { IMarker markerCopy = (IMarker)ghostCopy; markerCopy.Start = ghost.Start; marker.Timeline.Markers.Add(markerCopy); } else { marker.Start = ghost.Start; } } continue; } ITrack track = ghost.Object as ITrack; if (track != null) { ITrack target = (ITrack)ghost.Target; if (target != null && target != track) { if (ghostCopy != null) { track = (ITrack)ghostCopy; } int index = target.Group.Tracks.IndexOf(target); if (ghostCopy == null) { track.Group.Tracks.Remove(track); } target.Group.Tracks.Insert(index, track); } continue; } IGroup group = ghost.Object as IGroup; if (group != null) { IGroup target = (IGroup)ghost.Target; if (target != null && target != group) { if (ghostCopy != null) { group = (IGroup)ghostCopy; } int index = m_owner.TimelineDocument.Timeline.Groups.IndexOf(target); if (ghostCopy == null) { m_owner.TimelineDocument.Timeline.Groups.Remove(group); } m_owner.TimelineDocument.Timeline.Groups.Insert(index, group); } continue; } } // So that when multiple intervals from multiple tracks are relocated to a different // set of tracks, we need to remove them all and then add them all. If the remove and // adds are done in pairs, one by one, then the events can step on each other. artf32260 foreach (Sce.Atf.Pair <ITrack, IEvent> trackEventPair in toAdd) { if (trackEventPair.Second is IInterval) { trackEventPair.First.Intervals.Add((IInterval)trackEventPair.Second); } else { trackEventPair.First.Keys.Add((IKey)trackEventPair.Second); } } }, "Move Events".Localize("Move Manipulator's undo / redo description for moving timeline events")); }