private TimeSpan PixelsToTimespan(double pixelCount, TimelineScroller rootWindow) { double percent = pixelCount / rootWindow.ActualWidth; var viewRange = this.Navigator.ViewRange; return(TimeSpan.FromTicks((long)((double)viewRange.Duration.Ticks * percent))); }
private DateTime GetTimeAtMousePointer(MouseEventArgs e) { TimelineScroller root = this.FindTimelineScroller(e.Source); if (root != null) { Point point = e.GetPosition(root); double percent = point.X / root.ActualWidth; var viewRange = this.Navigator.ViewRange; DateTime time = viewRange.StartTime + TimeSpan.FromTicks((long)((double)viewRange.Duration.Ticks * percent)); // If we're currently snapping to some Visualization Object, adjust the time to the timestamp of the nearest message DateTime?snappedTime = null; PlotVisualizationObject snapToVisualizationObject = this.Container.SnapToVisualizationObject as PlotVisualizationObject; if (snapToVisualizationObject != null) { snappedTime = snapToVisualizationObject.GetTimeOfNearestMessage(time, snapToVisualizationObject.SummaryData?.Count ?? 0, (idx) => snapToVisualizationObject.SummaryData[idx].OriginatingTime); } return(snappedTime ?? time); } Console.WriteLine("TimelineVisualizationPanel.GetTimeAtMousePointer() - Could not find the TimelineScroller in the tree"); return(DateTime.UtcNow); }
private void DragAnnotationEdge(MouseEventArgs e) { // Get the timeline scroller TimelineScroller timelineScroller = this.FindTimelineScroller(e.Source); // Get the time at the mouse cursor DateTime cursorTime = this.GetTimeAtMousePointer(e.GetPosition(e.Source as IInputElement), timelineScroller, true); // Make sure we stay within bounds if (cursorTime < this.annotationDragInfo.MinimumTime) { cursorTime = this.annotationDragInfo.MinimumTime; } if (cursorTime > this.annotationDragInfo.MaximumTime) { cursorTime = this.annotationDragInfo.MaximumTime; } // Set the new positions of the ends of the annotation(s) if (this.annotationDragInfo.LeftAnnotation != null) { this.annotationDragInfo.LeftAnnotation.Interval = new TimeInterval(this.annotationDragInfo.LeftAnnotation.Interval.Left, cursorTime); } if (this.annotationDragInfo.RightAnnotation != null) { this.annotationDragInfo.RightAnnotation.Interval = new TimeInterval(cursorTime, this.annotationDragInfo.RightAnnotation.Interval.Right); } this.UpdateDisplayData(); VisualizationContext.Instance.VisualizationContainer.Navigator.Cursor = cursorTime; }
private void DoMouseDoubleClick(MouseButtonEventArgs e) { // Get the timeline scroller TimelineScroller timelineScroller = this.FindTimelineScroller(e.Source); // Get the time at the mouse cursor DateTime cursorTime = this.GetTimeAtMousePointer(e.GetPosition(e.Source as IInputElement), timelineScroller, false); // Get the item (if any) that straddles this time int index = this.GetAnnotationIndexByTime(cursorTime); if (index > -1) { // Get the annotation that was hit Message <TimeIntervalAnnotation> annotation = this.Data[index]; // Set the navigator selection to the bounds of the annotation TimeInterval annotationRange = annotation.Data.Interval; VisualizationContext.Instance.VisualizationContainer.Navigator.SelectionRange.SetRange(annotationRange); // If the shift key was down, then also zoom to the annotation (with 10% empty space to the left and right) if (Keyboard.IsKeyDown(Key.LeftShift) || Keyboard.IsKeyDown(Key.RightShift)) { double bufferSeconds = annotationRange.Span.TotalSeconds * 0.1d; VisualizationContext.Instance.VisualizationContainer.Navigator.ViewRange.SetRange(annotationRange.Left.AddSeconds(-bufferSeconds), annotationRange.Right.AddSeconds(bufferSeconds)); } } }
private DateTime GetTimeAtMousePointer(MouseEventArgs e) { TimelineScroller root = this.FindTimelineScroller(e.Source); if (root != null) { Point point = e.GetPosition(root); double percent = point.X / root.ActualWidth; var viewRange = this.Navigator.ViewRange; DateTime time = viewRange.StartTime + TimeSpan.FromTicks((long)((double)viewRange.Duration.Ticks * percent)); return(time); } Console.WriteLine("TimelineVisualizationPanel.GetTimeAtMousePointer() - Could not find the TimelineScroller in the tree"); return(DateTime.UtcNow); }
/// <summary> /// Gets the timeline scroller parent of a framework element. /// </summary> /// <param name="sourceElement">The framework element to search from.</param> /// <returns>The timeline scroller object.</returns> public TimelineScroller GetTimelineScroller(object sourceElement) { if (this.timelineScroller == null) { // Walk up the visual tree until we either find the // Timeline Scroller or fall off the top of the tree DependencyObject target = sourceElement as DependencyObject; while (target != null && !(target is TimelineScroller)) { target = VisualTreeHelper.GetParent(target); } this.timelineScroller = target as TimelineScroller; } return(this.timelineScroller); }
private DateTime GetTimeAtMousePointer(Point point, TimelineScroller root, bool useSnap) { double percent = point.X / root.ActualWidth; var viewRange = this.Navigator.ViewRange; DateTime time = viewRange.StartTime + TimeSpan.FromTicks((long)((double)viewRange.Duration.Ticks * percent)); // If we're currently snapping to some Visualization Object, adjust the time to the timestamp of the nearest message DateTime?snappedTime = null; if (useSnap) { IStreamVisualizationObject snapToVisualizationObject = this.Container.SnapToVisualizationObject as IStreamVisualizationObject; if (snapToVisualizationObject != null) { snappedTime = snapToVisualizationObject.GetSnappedTime(time, SnappingBehavior.Nearest); } } return(snappedTime ?? time); }
private void DoMouseMove(MouseEventArgs e) { if (this.annotationDragInfo != null) { this.DragAnnotationEdge(e); e.Handled = true; } else { // Get the timeline scroller and the canvas TimelineScroller timelineScroller = this.FindTimelineScroller(e.Source); Canvas canvas = this.FindCanvas(e.Source); // Get the time at the mouse cursor DateTime cursorTime = this.GetTimeAtMousePointer(e.GetPosition(e.Source as IInputElement), timelineScroller, false); // Get the item (if any) that straddles this time if (this.EnableAnnotationDrag) { int index = this.GetAnnotationIndexByTime(cursorTime); if (index > -1) { AnnotationEdge annotationEdge = this.MouseOverAnnotationEdge(cursorTime, this.Data[index].Data, timelineScroller); if (annotationEdge != AnnotationEdge.None) { canvas.Cursor = Cursors.SizeWE; } else { canvas.Cursor = Cursors.Arrow; } } else { canvas.Cursor = Cursors.Arrow; } } } }
/// <summary> /// Gets the time at the mouse pointer, optionally adjusting for visualization object snap. /// </summary> /// <param name="mouseEventArgs">A mouse event args object.</param> /// <param name="useSnap">If true, and if a visualization object is currently being snapped to, then adjust the time to the nearest message in the visualization object being snapped to.</param> /// <returns>The time represented by the mouse pointer.</returns> public DateTime GetTimeAtMousePointer(MouseEventArgs mouseEventArgs, bool useSnap) { TimelineScroller root = this.GetTimelineScroller(mouseEventArgs.Source); if (root != null) { Point point = mouseEventArgs.GetPosition(root); double percent = point.X / root.ActualWidth; var viewRange = this.Navigator.ViewRange; DateTime time = viewRange.StartTime + TimeSpan.FromTicks((long)((double)viewRange.Duration.Ticks * percent)); // If we're currently snapping to some Visualization Object, adjust the time to the timestamp of the nearest message DateTime?snappedTime = null; if (useSnap == true && VisualizationContext.Instance.VisualizationContainer.SnapToVisualizationObject is IStreamVisualizationObject snapToVisualizationObject) { snappedTime = DataManager.Instance.GetTimeOfNearestMessage(snapToVisualizationObject.StreamSource, time, NearestMessageType.Nearest); } return(snappedTime ?? time); } return(DateTime.UtcNow); }
private DateTime GetTimeAtMousePointer(MouseEventArgs e) { TimelineScroller root = this.FindTimelineScroller(e.Source); if (root != null) { Point point = e.GetPosition(root); double percent = point.X / root.ActualWidth; var viewRange = this.Navigator.ViewRange; DateTime time = viewRange.StartTime + TimeSpan.FromTicks((long)((double)viewRange.Duration.Ticks * percent)); // If we're currently snapping to some Visualization Object, adjust the time to the timestamp of the nearest message DateTime?snappedTime = null; if (VisualizationContext.Instance.VisualizationContainer.SnapToVisualizationObject is IStreamVisualizationObject snapToVisualizationObject) { snappedTime = snapToVisualizationObject.GetSnappedTime(time); } return(snappedTime ?? time); } Debug.WriteLine("TimelineVisualizationPanel.GetTimeAtMousePointer() - Could not find the TimelineScroller in the tree"); return(DateTime.UtcNow); }
private AnnotationEdge MouseOverAnnotationEdge(DateTime cursorTime, TimeIntervalAnnotation annotation, TimelineScroller timelineScroller) { // Work out what time interval is expressed in 3 pixels at the current zoom double percent = 5.0d / timelineScroller.ActualWidth; var viewRange = this.Navigator.ViewRange; TimeSpan hitTargetWidth = TimeSpan.FromTicks((long)((double)viewRange.Duration.Ticks * percent)); // Check if the mouse cursor is within 3 pixels of the left or right edge of the annoation if (Math.Abs((annotation.Interval.Left - cursorTime).Ticks) <= hitTargetWidth.Ticks) { return(AnnotationEdge.Left); } if (Math.Abs((annotation.Interval.Right - cursorTime).Ticks) <= hitTargetWidth.Ticks) { return(AnnotationEdge.Right); } return(AnnotationEdge.None); }
private void DoMouseLeftButtonDown(MouseButtonEventArgs e) { TimelineVisualizationPanel timelinePanel = this.Panel as TimelineVisualizationPanel; // Get the time at the mouse cursor DateTime cursorTime = timelinePanel.GetTimeAtMousePointer(e, false); Message <TimeIntervalAnnotation> annotation = default; AnnotationEdge annotationEdge = AnnotationEdge.None; // Get the item (if any) that straddles this time int index = this.GetAnnotationIndexByTime(cursorTime); if (index > -1) { // Get the annotation that was hit annotation = this.Data[index]; // Check if the mouse is over an edge of the annotation annotationEdge = this.MouseOverAnnotationEdge(cursorTime, this.Data[index].Data, timelinePanel.GetTimelineScroller(e.Source)); } // If the shift key is down, the user is dropping the start selection marker. If there is no VO currently being snapped // to and the mouse is over an annotation edge, then manually set the selection marker right on the edge. Otherwise // let the event bubble up to the timeline visualization panel which will set the selection marker in the usual fashion. if (Keyboard.IsKeyDown(Key.LeftShift) || Keyboard.IsKeyDown(Key.RightShift)) { if ((VisualizationContext.Instance.VisualizationContainer.SnapToVisualizationObject == null) && (annotationEdge != AnnotationEdge.None)) { DateTime selectionMarkerTime = annotationEdge == AnnotationEdge.Left ? annotation.Data.Interval.Left : annotation.Data.Interval.Right; this.Navigator.SelectionRange.SetRange(selectionMarkerTime, this.Navigator.SelectionRange.EndTime >= selectionMarkerTime ? this.Navigator.SelectionRange.EndTime : DateTime.MaxValue); e.Handled = true; } else { return; } } // If we're over an annotation if (annotation != default) { if (annotationEdge == AnnotationEdge.None) { // We're over an annotation, but not an annotation edge, display the annotation's properties this.SelectDisplayObject(annotation.Data); // Begin annotation edit if it's enabled if (this.EnableAnnotationValueEdit) { // Work out the track number to be edited based on the mouse position TimelineScroller timelineScroller = timelinePanel.GetTimelineScroller(e.Source); Point point = e.GetPosition(timelineScroller); int trackId = (int)(point.Y / timelineScroller.ActualHeight * (double)annotation.Data.Values.Count); // Find the display data object corresponding to the annotation and fire an edit event to the view TimeIntervalAnnotationDisplayData displayObject = this.DisplayData.FirstOrDefault(d => d.Annotation.Data.Interval.Right == annotation.Data.Interval.Right); this.TimeIntervalAnnotationEdit?.Invoke(this, new TimeIntervalAnnotationEditEventArgs(displayObject, trackId)); } } // Check if we're over an edge and annotation drag is enabled. if (annotationEdge != AnnotationEdge.None && this.EnableAnnotationDrag) { // Get the previous and next annotations (if any) and check if they abut the annotation whose edge we're going to drag Message <TimeIntervalAnnotation>?previousAnnotation = index > 0 ? this.Data[index - 1] : (Message <TimeIntervalAnnotation>?)null; bool previousAnnotationAbuts = previousAnnotation != null && previousAnnotation.Value.Data.Interval.Right == annotation.Data.Interval.Left; Message <TimeIntervalAnnotation>?nextAnnotation = index < this.Data.Count - 1 ? this.Data[index + 1] : (Message <TimeIntervalAnnotation>?)null; bool nextAnnotationAbuts = nextAnnotation != null && nextAnnotation.Value.Data.Interval.Left == annotation.Data.Interval.Right; // If the ALT key is down, then we will not try to move the annotation that abuts this one bool moveNeighborAnnotation = !Keyboard.IsKeyDown(Key.LeftAlt) && !Keyboard.IsKeyDown(Key.RightAlt); // Work out the minimum and maximum times we can drag the annotation's edge to DateTime minTime; DateTime maxTime; if (annotationEdge == AnnotationEdge.Left) { maxTime = annotation.Data.Interval.Right; if (previousAnnotation == null) { minTime = this.Navigator.ViewRange.StartTime; } else if (previousAnnotationAbuts && moveNeighborAnnotation) { minTime = previousAnnotation.Value.Data.Interval.Left; } else { minTime = previousAnnotation.Value.Data.Interval.Right; } } else { minTime = annotation.Data.Interval.Left; if (nextAnnotation == null) { maxTime = this.Navigator.ViewRange.EndTime; } else if (nextAnnotationAbuts && moveNeighborAnnotation) { maxTime = nextAnnotation.Value.Data.Interval.Right; } else { maxTime = nextAnnotation.Value.Data.Interval.Left; } } // Create the drag data that specifies which annotation(s) to drag, and the minimum and maximum time we can drag to if (annotationEdge == AnnotationEdge.Right) { this.annotationDragInfo = new TimeIntervalAnnotationDragInfo(annotation, moveNeighborAnnotation && nextAnnotationAbuts ? nextAnnotation : null, minTime, maxTime); } else { this.annotationDragInfo = new TimeIntervalAnnotationDragInfo(moveNeighborAnnotation && previousAnnotationAbuts ? previousAnnotation : null, annotation, minTime, maxTime); } } } else { // We're not over any annotation, cancel any current edit operation in the view and display the VO's properties this.TimeIntervalAnnotationEdit?.Invoke(this, new TimeIntervalAnnotationEditEventArgs(null, 0)); this.SelectDisplayObject(null); } }
private AnnotationEdge MouseOverAnnotationEdge(DateTime cursorTime, TimeIntervalAnnotation annotation, TimelineScroller timelineScroller) { // Work out what time interval is expressed in 3 pixels at the current zoom TimeSpan hitTargetWidth = this.PixelsToTimespan(5.0d, timelineScroller); // Check if the mouse cursor is within 3 pixels of the left or right edge of the annoation if (Math.Abs((annotation.Interval.Left - cursorTime).Ticks) <= hitTargetWidth.Ticks) { return(AnnotationEdge.Left); } if (Math.Abs((annotation.Interval.Right - cursorTime).Ticks) <= hitTargetWidth.Ticks) { return(AnnotationEdge.Right); } return(AnnotationEdge.None); }
private void DoMouseLeftButtonDown(MouseButtonEventArgs e) { // Get the timeline scroller TimelineScroller timelineScroller = this.FindTimelineScroller(e.Source); // Get the time at the mouse cursor DateTime cursorTime = this.GetTimeAtMousePointer(e.GetPosition(e.Source as IInputElement), timelineScroller, false); // Get the item (if any) that straddles this time int index = this.GetAnnotationIndexByTime(cursorTime); if (index > -1) { // Get the annotation that was hit Message <TimeIntervalAnnotation> annotation = this.Data[index]; Canvas canvas = this.FindCanvas(e.Source); // Check if the mouse is over an edge of the annotation AnnotationEdge annotationEdge = this.MouseOverAnnotationEdge(cursorTime, this.Data[index].Data, timelineScroller); if (annotationEdge != AnnotationEdge.None && this.EnableAnnotationDrag) { // Get the previous and next annotations (if any) and check if they abut the annotation whose edge we're going to drag Message <TimeIntervalAnnotation>?previousAnnotation = index > 0 ? this.Data[index - 1] : (Message <TimeIntervalAnnotation>?)null; bool previousAnnotationAbuts = previousAnnotation != null && previousAnnotation.Value.Data.Interval.Right == annotation.Data.Interval.Left; Message <TimeIntervalAnnotation>?nextAnnotation = index < this.Data.Count - 1 ? this.Data[index + 1] : (Message <TimeIntervalAnnotation>?)null; bool nextAnnotationAbuts = nextAnnotation != null && nextAnnotation.Value.Data.Interval.Left == annotation.Data.Interval.Right; // If the ALT key is down, then we will not try to move the annotation that abuts this one bool moveNeighborAnnotation = !Keyboard.IsKeyDown(Key.LeftAlt) && !Keyboard.IsKeyDown(Key.RightAlt); // Work out the minimum and maximum times we can drag the annotation's edge to DateTime minTime; DateTime maxTime; if (annotationEdge == AnnotationEdge.Left) { maxTime = annotation.Data.Interval.Right; if (previousAnnotation == null) { minTime = this.Navigator.ViewRange.StartTime; } else if (previousAnnotationAbuts && moveNeighborAnnotation) { minTime = previousAnnotation.Value.Data.Interval.Left; } else { minTime = previousAnnotation.Value.Data.Interval.Right; } } else { minTime = annotation.Data.Interval.Left; if (nextAnnotation == null) { maxTime = this.Navigator.ViewRange.EndTime; } else if (nextAnnotationAbuts && moveNeighborAnnotation) { maxTime = nextAnnotation.Value.Data.Interval.Right; } else { maxTime = nextAnnotation.Value.Data.Interval.Left; } } // Create the drag data that specifies which annotation(s) to drag, and the minimum and maximum time we can drag to if (annotationEdge == AnnotationEdge.Right) { this.annotationDragInfo = new TimeIntervalAnnotationDragInfo(annotation, moveNeighborAnnotation && nextAnnotationAbuts ? nextAnnotation : null, minTime, maxTime); } else { this.annotationDragInfo = new TimeIntervalAnnotationDragInfo(moveNeighborAnnotation && previousAnnotationAbuts ? previousAnnotation : null, annotation, minTime, maxTime); } } else { this.SelectDisplayObject(annotation.Data); } e.Handled = true; } else { this.SelectDisplayObject(null); } }