in Sources/Visualization/Microsoft.Psi.Visualization.Windows/VisualizationObjects/Annotations/TimeIntervalAnnotationVisualizationObject.cs [563:688]
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);
}
}