/// <summary> /// Constructor that permanently attaches to the given timeline control by subscribing to its /// events.</summary> /// <param name="owner">The timeline control that we are permanently attached to</param> public D2dSelectionManipulator(D2dTimelineControl owner) { Owner = owner; Owner.MouseDownPicked += Owner_MouseDownPicked; Owner.KeyDown += Owner_KeyDown; Owner.MouseUp += Owner_MouseUp; }
/// <summary> /// Counts the number of references to this document by all TimelineDocuments</summary> /// <param name="document">Document</param> /// <returns>Number references to document</returns> private int NumReferences(ITimelineDocument document) { int count = 0; foreach (IDocument topDoc in m_documentRegistry.Documents) { ITimelineDocument topTimelineDoc = topDoc as ITimelineDocument; if (topTimelineDoc != null) { if (document == topTimelineDoc) { count++; } foreach (TimelinePath path in D2dTimelineControl.GetHierarchy(topTimelineDoc.Timeline)) { IHierarchicalTimeline target = ((ITimelineReference)path.Last).Target; if (document == target.As <ITimelineDocument>()) { count++; } } } } return(count); }
// private because this may be moved to TimelineControl private bool Overlaps(TimelinePath path, float beginTime, float endTime) { IEvent e = (IEvent)path.Last; float start, length; using (Matrix localToWorld = D2dTimelineControl.CalculateLocalToWorld(path)) { start = GdiUtil.Transform(localToWorld, e.Start); length = GdiUtil.TransformVector(localToWorld, e.Length); } // If the length is zero, then count an exact match with beginTime or endTime as // being an overlap. if (length == 0) { return(!( start > endTime || start + length < beginTime)); } // Otherwise, don't count an exact match. return(!( start >= endTime || start + length <= beginTime)); }
/// <summary> /// Constructor that permanently attaches to the given TimelineControl by subscribing to its /// events</summary> /// <param name="owner">The TimelineControl whose events we permanently listen to</param> public D2dSplitManipulator(D2dTimelineControl owner) { m_owner = owner; m_owner.MouseDownPicked += owner_MouseDownPicked; m_owner.MouseMovePicked += owner_MouseMovePicked; m_owner.KeyDown += owner_KeyDown; }
/// <summary> /// Notifies the client that its Control has been activated. Activation occurs when /// the Control gets focus, or a parent "host" Control gets focus.</summary> /// <param name="control">Client Control that was activated</param> /// <remarks>This method is only called by IControlHostService if the Control was previously /// registered for this IControlHostClient.</remarks> public void Activate(Control control) { D2dTimelineControl timelineControl = (D2dTimelineControl)control; TimelineDocument timelineDocument = (TimelineDocument)timelineControl.TimelineDocument; m_contextRegistry.ActiveContext = timelineDocument; m_documentRegistry.ActiveDocument = timelineDocument; }
/// <summary> /// Constructor that permanently attaches to the given timeline control by subscribing to its /// events</summary> /// <param name="owner">The timeline control whose events we permanently listen to</param> public D2dScaleManipulator(D2dTimelineControl owner) { Owner = owner; Owner.MouseDownPicked += owner_MouseDownPicked; Owner.MouseMovePicked += owner_MouseMovePicked; Owner.Picking += owner_Picking; Owner.MouseUp += owner_MouseUp; Owner.DrawingD2d += owner_DrawingD2d; }
/// <summary> /// Constructor that permanently attaches to the given TimelineControl by subscribing to its /// events</summary> /// <param name="owner">The TimelineControl whose events we permanently listen to</param> public D2dSnapManipulator(D2dTimelineControl owner) { m_owner = owner; m_owner.GetSnapOffset = GetSnapOffset; m_owner.DrawingD2d += owner_DrawingD2d; m_owner.MouseUp += owner_MouseUp; m_owner.MouseDown += owner_MouseDown; m_owner.KeyDown += owner_KeyDown; }
/// <summary> /// Constructor that permanently attaches to the given TimelineControl by subscribing to its /// events</summary> /// <param name="owner">The TimelineControl whose events we permanently listen to</param> public D2dSnapManipulator(D2dTimelineControl owner) { m_owner = owner; m_owner.GetSnapOffset = new D2dTimelineControl.SnapOffsetFinder(GetSnapOffset); m_owner.DrawingD2d += owner_DrawingD2d; m_owner.MouseUp += owner_MouseUp; m_owner.MouseDown += owner_MouseDown; m_owner.KeyDown += owner_KeyDown; }
/// <summary> /// Constructor that permanently attaches to the given timeline control by subscribing to its /// events</summary> /// <param name="owner">The timeline control whose events we permanently listen to</param> public D2dScrubberManipulator(D2dTimelineControl owner) { Owner = owner; Owner.MouseDownPicked += owner_MouseDownPicked; Owner.MouseMovePicked += owner_MouseMovePicked; Owner.Picking += owner_Picking; Owner.MouseMove += MouseMoveHandler; Owner.MouseUp += owner_MouseUp; Owner.DrawingD2d += owner_DrawingD2d; Owner.BoundingRectUpdating += owner_BoundingRectUpdating; }
/// <summary> /// Requests permission to close the client's Control</summary> /// <param name="control">Client Control to be closed</param> /// <returns>True if the Control can close, or false to cancel</returns> /// <remarks> /// 1. This method is only called by IControlHostService if the Control was previously /// registered for this IControlHostClient. /// 2. If true is returned, the IControlHostService calls its own /// UnregisterControl. The IControlHostClient has to call RegisterControl again /// if it wants to re-register this Control.</remarks> public bool Close(Control control) { D2dTimelineControl timelineControl = (D2dTimelineControl)control; TimelineDocument timelineDocument = (TimelineDocument)timelineControl.TimelineDocument; if (timelineDocument != null) { return(m_documentService.Close(timelineDocument)); } return(true); }
private void owner_MouseMovePicked(object sender, HitEventArgs e) { string toolTipText = null; if (m_active) { e.Handled = true; if (e.HitRecord.Type == HitType.Interval && e.MouseEvent.Button == MouseButtons.None && !m_owner.IsUsingMouse && m_owner.IsEditable(e.HitRecord.HitPath)) { SetCursor(); TimelinePath hitPath = e.HitRecord.HitPath; IInterval hitObject = (IInterval)e.HitRecord.HitTimelineObject; float worldX = GdiUtil.InverseTransform(m_owner.Transform, e.MouseEvent.Location.X); //Make sure the snap-to indicator line is drawn. float delta = m_owner.GetSnapOffset(new[] { worldX }, s_snapOptions); worldX += delta; worldX = m_owner.ConstrainFrameOffset(worldX); using (Matrix localToWorld = D2dTimelineControl.CalculateLocalToWorld(hitPath)) { if (worldX <= GdiUtil.Transform(localToWorld, hitObject.Start) || worldX >= GdiUtil.Transform(localToWorld, hitObject.Start + hitObject.Length)) { // Clear the results since a split is impossible. m_owner.GetSnapOffset(new float[] {}, s_snapOptions); } else { toolTipText = worldX.ToString(); } } } } if (toolTipText != null) { m_toolTip.Show(toolTipText, m_owner, e.MouseEvent.Location); } else { m_toolTip.Hide(m_owner); } }
/// <summary> /// Constructor that permanently attaches to the given TimelineControl by subscribing to its /// events</summary> /// <param name="owner">The timeline control that we are permanently attached to</param> public D2dMoveManipulator(D2dTimelineControl owner) { m_owner = owner; m_owner.MouseDownPicked += owner_MouseDownPicked; m_owner.MouseMovePicked += owner_MouseMovePicked; m_owner.MouseMove += owner_MouseMove; m_owner.MouseDown += owner_MouseDown; m_owner.MouseUp += owner_MouseUp; m_owner.DrawingD2d += owner_DrawingD2d; m_owner.KeyDown += owner_KeyDown; m_owner.DragOver += owner_DragOver; m_owner.DragEnter += owner_DragEnter; m_owner.DragLeave += owner_DragLeave; m_owner.DragDrop += owner_DragDrop; }
private void owner_DrawingD2d(object sender, EventArgs e) { // Test if anything is visible. m_visibleManipulator = D2dTimelineControl.CalculateRange(Owner.EditableSelection, out m_worldMin, out m_worldMax); if (!m_visibleManipulator) { return; } D2dGraphics g = Owner.D2dGraphics; Matrix worldToView = Owner.Transform; // Make the TimelineRenderer draw the ghosts, if necessary. Must happen before // the manipulator is drawn so that snap-to info is created. if (IsScaling) { TimelineLayout layout = Owner.GetLayout(); GhostInfo[] ghosts = m_resizer.CreateGhostInfo( layout, Owner.GetDragOffset().X, worldToView); Owner.Renderer.DrawGhosts( ghosts, DraggedSide, worldToView, Owner.ClientRectangle); } // Tighten clipping region. Has to occur after DrawGhosts because the TimelineRenderer // assumes that it has to shrink the current Graphics.Clip region by the header width. try { g.PushAxisAlignedClip(Owner.VisibleClientRectangle); // Draw the manipulator, giving client code a customization point. DrawManipulator(g, out m_leftHandle, out m_rightHandle); } finally { g.PopAxisAlignedClip(); } }
private void owner_MouseDownPicked(object sender, HitEventArgs e) { TimelinePath hitPath = e.HitRecord.HitPath; IInterval hitInterval = e.HitRecord.HitTimelineObject as IInterval; if (m_active && e.MouseEvent.Button == MouseButtons.Left && !m_owner.IsUsingMouse && m_owner.IsEditable(hitPath)) { if (hitInterval != null && e.HitRecord.Type == HitType.Interval) { PointF mouseLocation = e.MouseEvent.Location; float worldX = Sce.Atf.GdiUtil.InverseTransform(m_owner.Transform, mouseLocation.X); worldX += m_owner.GetSnapOffset(new[] { worldX }, s_snapOptions); float fraction; using (Matrix localToWorld = D2dTimelineControl.CalculateLocalToWorld(hitPath)) { fraction = (worldX - GdiUtil.Transform(localToWorld, hitInterval.Start)) / GdiUtil.TransformVector(localToWorld, hitInterval.Length); } if (m_owner.Selection.SelectionContains(hitInterval)) { SplitSelectedIntervals(fraction); } else { SplitUnselectedInterval(hitInterval, fraction); } } Active = false; e.Handled = true; //don't let subsequent listeners get this event } }
/// <summary> /// Constructor for a single scaling or resizing operation</summary> /// <param name="side">Side of the interval or manipulator is being dragged</param> /// <param name="mode">Scaling mode</param> /// <param name="boundary">World coordinate starting position for the drag operation</param> /// <param name="owner">The timeline control that this manipulator is attached to</param> internal Resizer(Side side, ScaleMode mode, float boundary, D2dTimelineControl owner) { m_intervalSide = side; Mode = mode; m_originalBoundary = boundary; m_owner = owner; if (Mode == ScaleMode.InPlace) { m_selection = new HashSet <TimelinePath>(GetEditableEvents <IInterval>()); } else { m_selection = new HashSet <TimelinePath>(GetEditableEvents <IEvent>()); } if (m_selection.Count == 0) { throw new InvalidTransactionException("only IEvents can be resized"); } m_initialMin = float.MaxValue; m_initialMax = float.MinValue; if (Mode == ScaleMode.TimePeriod) { foreach (TimelinePath path in m_selection) { IEvent curr = (IEvent)path.Last; if (curr.Start < m_initialMin) { m_initialMin = curr.Start; } if (m_initialMax < curr.Start + curr.Length) { m_initialMax = curr.Start + curr.Length; } } } DragOffsetWithSnap = m_owner.GetDragOffset().X; }
/// <summary> /// Gets the offset from one of the world snap points to the closest non-selected object's edge</summary> /// <param name="movingPoints">The x-coordinates to snap "from", in world coordinates</param> /// <param name="options">The options to control the behavior. If null, the defaults are used.</param> /// <returns>The value to be added, to GetDragOffset().X, for example. Is in world coordinates.</returns> private float GetSnapOffset(IEnumerable <float> movingPoints, D2dTimelineControl.SnapOptions options) { //we want to recalculate m_snapInfo and reflect changes to the snap-to lines m_snapInfo.Clear(); m_owner.Invalidate(); if (options == null) { options = new D2dTimelineControl.SnapOptions(); } // Check for user-forced snapping and no-snapping. if (options.CheckModifierKeys) { Keys modKeys = Control.ModifierKeys; if (s_deactivatorKeys != Keys.None && (modKeys & s_deactivatorKeys) == s_deactivatorKeys) { return(0.0f); } if (s_activatorKeys != Keys.None && (modKeys & s_activatorKeys) != s_activatorKeys) { return(0.0f); } } // Prepare helper object on each moving point. foreach (float snapping in movingPoints) { m_snapInfo.Add(new SnapOffsetInfo(snapping)); } if (m_snapInfo.Count == 0) { return(0.0f); } // Find the closest IEvent. float worldSnapTolerance = GdiUtil.InverseTransformVector(m_owner.Transform, s_snapTolerance); List <TimelinePath> events = new List <TimelinePath>( D2dTimelineControl.GetObjects <IEvent>(m_owner.Timeline)); // Allow for snapping to a scrubber manipulator. if (m_scrubber != null && options.IncludeScrubber) { events.Add(new TimelinePath(m_scrubber)); } foreach (TimelinePath path in events) { if (options.IncludeSelected || !m_owner.Selection.SelectionContains(path)) { IEvent snapToEvent = (IEvent)path.Last; if (options.Filter == null || options.Filter(snapToEvent, options)) { Matrix localToWorld = D2dTimelineControl.CalculateLocalToWorld(path); float start, length; GetEventDimensions(snapToEvent, localToWorld, out start, out length); foreach (SnapOffsetInfo info in m_snapInfo) { info.Update(start, snapToEvent, worldSnapTolerance); if (length > 0) { info.Update(start + length, snapToEvent, worldSnapTolerance); } } } } } // Keep only the shortest distance snap-to points. Could be multiple in case of tie. SnapOffsetInfo.RemoveInvalid(m_snapInfo); if (m_snapInfo.Count == 0) { return(0.0f); } SnapOffsetInfo topInfo = m_snapInfo[0]; return(topInfo.Offset); }
// 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 = 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 = D2dTimelineControl.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 D2dTimelineControl.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); // Pretend that drag-drop objects are in the TimelineLayout object if (m_owner.DragDropObjects != null) { foreach (ITimelineObject dragDrop in m_owner.DragDropObjects) { layout[dragDrop] = GetDragDropBounds(dragDrop); } } 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; valid &= m_owner.Constraints.IsStartValid(interval, ref start); valid &= m_owner.Constraints.IsLengthValid(interval, ref length); if (valid) { ITrack intervalTrack = interval.Track; if (intervalTrack != null) { yOffset = layout[target].Y - layout[interval.Track].Y; } else { yOffset = layout[target].Y - bounds.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; valid &= m_owner.Constraints.IsStartValid(key, ref start); if (valid) { ITrack keyTrack = key.Track; if (keyTrack != null) { yOffset = layout[targetTrack].Y - layout[key.Track].Y; } else { yOffset = layout[targetTrack].Y - bounds.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 = (DragDelta.Y < 0) ? GetLastTrack() : GetFirstTrack(); } } else if (group != null) { xOffset = 0; if (target == null) { IList <IGroup> groups = m_owner.TimelineDocument.Timeline.Groups; target = (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); }
private void owner_BoundingRectUpdating(object sender, D2dTimelineControl.BoundingRectEventArgs e) { e.NewClientRect = m_handleRect; }
/// <summary> /// Gets the offset from one of the world snap points to the closest non-selected object's edge</summary> /// <param name="movingPoints">The x-coordinates to snap "from", in world coordinates</param> /// <param name="options">The options to control the behavior. If null, the defaults are used.</param> /// <returns>The value to be added, to GetDragOffset().X, for example. Is in world coordinates.</returns> private float GetSnapOffset(IEnumerable<float> movingPoints, D2dTimelineControl.SnapOptions options) { //we want to recalculate m_snapInfo and reflect changes to the snap-to lines m_snapInfo.Clear(); m_owner.Invalidate(); if (options == null) options = new D2dTimelineControl.SnapOptions(); // Check for user-forced snapping and no-snapping. if (options.CheckModifierKeys) { Keys modKeys = Control.ModifierKeys; if (s_deactivatorKeys != Keys.None && (modKeys & s_deactivatorKeys) == s_deactivatorKeys) return 0.0f; if (s_activatorKeys != Keys.None && (modKeys & s_activatorKeys) != s_activatorKeys) return 0.0f; } // Prepare helper object on each moving point. foreach (float snapping in movingPoints) { m_snapInfo.Add(new SnapOffsetInfo(snapping)); } if (m_snapInfo.Count == 0) return 0.0f; // Find the closest IEvent. float worldSnapTolerance = GdiUtil.InverseTransformVector(m_owner.Transform, s_snapTolerance); List<TimelinePath> events = new List<TimelinePath>( D2dTimelineControl.GetObjects<IEvent>(m_owner.Timeline)); // Allow for snapping to a scrubber manipulator. if (m_scrubber != null && options.IncludeScrubber) events.Add(new TimelinePath(m_scrubber)); foreach (TimelinePath path in events) { if (options.IncludeSelected || !m_owner.Selection.SelectionContains(path)) { IEvent snapToEvent = (IEvent)path.Last; if (options.Filter == null || options.Filter(snapToEvent, options)) { Matrix localToWorld = D2dTimelineControl.CalculateLocalToWorld(path); float start, length; GetEventDimensions(snapToEvent, localToWorld, out start, out length); foreach (SnapOffsetInfo info in m_snapInfo) { info.Update(start, snapToEvent, worldSnapTolerance); if (length > 0) info.Update(start + length, snapToEvent, worldSnapTolerance); } } } } // Keep only the shortest distance snap-to points. Could be multiple in case of tie. SnapOffsetInfo.RemoveInvalid(m_snapInfo); if (m_snapInfo.Count == 0) return 0.0f; SnapOffsetInfo topInfo = m_snapInfo[0]; return topInfo.Offset; }
/// <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>()); }
private bool MoveSnapFilter(IEvent testEvent, D2dTimelineControl.SnapOptions options) { ITimelineReference movingReference = options.FilterContext as ITimelineReference; if (movingReference == null) return true; IHierarchicalTimeline movingTimeline = movingReference.Target; if (movingTimeline == null) return true; ITimeline owningTimeline = null; if (testEvent is ITimelineReference) owningTimeline = ((ITimelineReference)testEvent).Parent; else if (testEvent is IInterval) owningTimeline = ((IInterval)testEvent).Track.Group.Timeline; else if (testEvent is IKey) owningTimeline = ((IKey)testEvent).Track.Group.Timeline; else if (testEvent is IMarker) owningTimeline = ((IMarker)testEvent).Timeline; // Is the reference being compared to an object that it owns? Never snap! if (owningTimeline == movingTimeline) return false; // Is the reference being compared against a sibling object? Snap away. if (owningTimeline == movingReference.Parent) return true; // to-do: support a true hierarchy. We can't support directed acyclic graphs because // the Layout implementation uses a Dictionary of ITimelineObjects and the same object // can't have multiple layout rectangles. We could support a tree hierarchy, though. //// The test object may be a grandchild of the moving reference. Look for a parent. //owningTimeline = owningTimeline.Parent; return true; }
/// <summary> /// Gets the range of events that intersect the rectangle that encloses 'begin' and 'end'</summary> /// <param name="begin">Beginning timeline path</param> /// <param name="end">Ending timeline path</param> /// <returns>Enumeration of timeline paths that intersect the rectangle. /// A timeline path is a sequence of objects in timelines, e.g., groups, tracks, events.</returns> protected virtual IEnumerable <TimelinePath> GetRangeOfEvents(TimelinePath begin, TimelinePath end) { // If the two paths are the same, then the rectangle is just a point. // This check fixes a bug where the 'rectangle' becomes the whole interval // and then falsely selects an adjacent zero-length object due to Overlaps(). if (begin.Equals(end)) { return new TimelinePath[] { begin } } ; // Get the range of tracks between these two events. bool searchMarkers = false; System.Collections.IEnumerable rangeOfTracks; if (begin.Last is IMarker || end.Last is IMarker) { rangeOfTracks = Owner.AllTracks; searchMarkers = true; } else { TimelinePath beginTrack = GetOwningTrack(begin); TimelinePath endTrack = GetOwningTrack(end); rangeOfTracks = GetRangeOfTracks(beginTrack, endTrack); } // Get the range of times to look for. float beginStart, beginEnd; D2dTimelineControl.CalculateRange(begin, out beginStart, out beginEnd); float endStart, endEnd; D2dTimelineControl.CalculateRange(end, out endStart, out endEnd); float beginTime = Math.Min(beginStart, endStart); float endTime = Math.Max(beginEnd, endEnd); // Look through all the IEvents of these tracks. List <TimelinePath> range = new List <TimelinePath>(); foreach (TimelinePath testPath in rangeOfTracks) { ITrack track = (ITrack)testPath.Last; foreach (IKey key in track.Keys) { testPath.Last = key; if (Overlaps(testPath, beginTime, endTime)) { range.Add(new TimelinePath(testPath)); } } foreach (IInterval interval in track.Intervals) { testPath.Last = interval; if (Overlaps(testPath, beginTime, endTime)) { range.Add(new TimelinePath(testPath)); } } } // Look for markers? if (searchMarkers) { foreach (TimelinePath testPath in Owner.AllMarkers) { if (Overlaps(testPath, beginTime, endTime)) { range.Add(testPath); } } } return(range); }