using System; using System.Windows; using System.Windows.Documents; using System.Windows.Input; using System.Windows.Media; using Microsoft.Research.DynamicDataDisplay.Common; using Microsoft.Research.DynamicDataDisplay.Common.Auxiliary; using System.Diagnostics; namespace Microsoft.Research.DynamicDataDisplay.Navigation { /// Provides common methods of mouse navigation around viewport public class MouseNavigation : NavigationBase { /// /// Initializes a new instance of the class. /// public MouseNavigation() { } private AdornerLayer adornerLayer; protected AdornerLayer AdornerLayer { get { if (adornerLayer == null) { adornerLayer = AdornerLayer.GetAdornerLayer(this); if (adornerLayer != null) { adornerLayer.IsHitTestVisible = false; } } return adornerLayer; } } protected override void OnPlotterAttached(Plotter plotter) { base.OnPlotterAttached(plotter); Mouse.AddMouseDownHandler(Parent, OnMouseDown); Mouse.AddMouseMoveHandler(Parent, OnMouseMove); Mouse.AddMouseUpHandler(Parent, OnMouseUp); Mouse.AddMouseWheelHandler(Parent, OnMouseWheel); plotter.KeyDown += new KeyEventHandler(OnParentKeyDown); } protected override void OnPlotterDetaching(Plotter plotter) { plotter.KeyDown -= new KeyEventHandler(OnParentKeyDown); Mouse.RemoveMouseDownHandler(Parent, OnMouseDown); Mouse.RemoveMouseMoveHandler(Parent, OnMouseMove); Mouse.RemoveMouseUpHandler(Parent, OnMouseUp); Mouse.RemoveMouseWheelHandler(Parent, OnMouseWheel); base.OnPlotterDetaching(plotter); } private void OnParentKeyDown(object sender, KeyEventArgs e) { if (e.Key == Key.Escape || e.Key == Key.Back) { if (isZooming) { isZooming = false; zoomRect = null; ReleaseMouseCapture(); RemoveSelectionAdorner(); e.Handled = true; } } } private void OnMouseWheel(object sender, MouseWheelEventArgs e) { if (!e.Handled) { Point mousePos = e.GetPosition(this); int delta = -e.Delta; MouseWheelZoom(mousePos, delta); e.Handled = true; } } #if DEBUG public override string ToString() { if (!String.IsNullOrEmpty(Name)) { return Name; } return base.ToString(); } #endif bool adornerAdded; RectangleSelectionAdorner selectionAdorner; private void AddSelectionAdorner() { if (!adornerAdded) { AdornerLayer layer = AdornerLayer; if (layer != null) { selectionAdorner = new RectangleSelectionAdorner(this) { Border = zoomRect }; layer.Add(selectionAdorner); adornerAdded = true; } } } private void RemoveSelectionAdorner() { AdornerLayer layer = AdornerLayer; if (layer != null) { layer.Remove(selectionAdorner); adornerAdded = false; } } private void UpdateSelectionAdorner() { selectionAdorner.Border = zoomRect; selectionAdorner.InvalidateVisual(); } Rect? zoomRect = null; private const double wheelZoomSpeed = 1.2; private bool shouldKeepRatioWhileZooming; private bool isZooming = false; protected bool IsZooming { get { return isZooming; } } private bool isPanning = false; protected bool IsPanning { get { return isPanning; } } private Point panningStartPointInViewport; protected Point PanningStartPointInViewport { get { return panningStartPointInViewport; } } private Point zoomStartPoint; private static bool IsShiftOrCtrl { get { ModifierKeys currKeys = Keyboard.Modifiers; return (currKeys | ModifierKeys.Shift) == currKeys || (currKeys | ModifierKeys.Control) == currKeys; } } protected virtual bool ShouldStartPanning(MouseButtonEventArgs e) { return e.ChangedButton == MouseButton.Left && Keyboard.Modifiers == ModifierKeys.None; } protected virtual bool ShouldStartZoom(MouseButtonEventArgs e) { return e.ChangedButton == MouseButton.Left && IsShiftOrCtrl; } Point panningStartPointInScreen; protected virtual void StartPanning(MouseButtonEventArgs e) { panningStartPointInScreen = e.GetPosition(this); panningStartPointInViewport = panningStartPointInScreen.ScreenToViewport(Viewport.Transform); Plotter2D.UndoProvider.CaptureOldValue(Viewport, Viewport2D.VisibleProperty, Viewport.Visible); isPanning = true; // not capturing mouse because this made some tools like PointSelector not // receive MouseUp events on markers; // Mouse will be captured later, in the first MouseMove handler call. // CaptureMouse(); Viewport.PanningState = Viewport2DPanningState.Panning; //e.Handled = true; } protected virtual void StartZoom(MouseButtonEventArgs e) { zoomStartPoint = e.GetPosition(this); if (Viewport.Output.Contains(zoomStartPoint)) { isZooming = true; AddSelectionAdorner(); CaptureMouse(); shouldKeepRatioWhileZooming = Keyboard.Modifiers == ModifierKeys.Shift; e.Handled = true; } } private void OnMouseDown(object sender, MouseButtonEventArgs e) { // dragging bool shouldStartDrag = ShouldStartPanning(e); if (shouldStartDrag) StartPanning(e); // zooming bool shouldStartZoom = ShouldStartZoom(e); if (shouldStartZoom) StartZoom(e); if (!Plotter.IsFocused) { //var window = Window.GetWindow(Plotter); //var focusWithinWindow = FocusManager.GetFocusedElement(window) != null; Plotter.Focus(); //if (!focusWithinWindow) //{ // this is done to prevent other tools like PointSelector from getting mouse click event when clicking on plotter // to activate window it's contained within e.Handled = true; //} } } private void OnMouseMove(object sender, MouseEventArgs e) { if (!isPanning && !isZooming) return; // dragging if (isPanning && e.LeftButton == MouseButtonState.Pressed) { if (!IsMouseCaptured) { CaptureMouse(); } Point endPoint = e.GetPosition(this).ScreenToViewport(Viewport.Transform); Point loc = Viewport.Visible.Location; Vector shift = panningStartPointInViewport - endPoint; loc += shift; // preventing unnecessary changes, if actually visible hasn't change. if (shift.X != 0 || shift.Y != 0) { Cursor = Cursors.ScrollAll; DataRect visible = Viewport.Visible; visible.Location = loc; Viewport.Visible = visible; } e.Handled = true; } // zooming else if (isZooming && e.LeftButton == MouseButtonState.Pressed) { Point zoomEndPoint = e.GetPosition(this); UpdateZoomRect(zoomEndPoint); e.Handled = true; } } private static bool IsShiftPressed() { return Keyboard.IsKeyDown(Key.LeftShift) || Keyboard.IsKeyDown(Key.RightShift); } private void UpdateZoomRect(Point zoomEndPoint) { Rect output = Viewport.Output; Rect tmpZoomRect = new Rect(zoomStartPoint, zoomEndPoint); tmpZoomRect = Rect.Intersect(tmpZoomRect, output); shouldKeepRatioWhileZooming = IsShiftPressed(); if (shouldKeepRatioWhileZooming) { double currZoomRatio = tmpZoomRect.Width / tmpZoomRect.Height; double zoomRatio = output.Width / output.Height; if (currZoomRatio < zoomRatio) { double oldHeight = tmpZoomRect.Height; double height = tmpZoomRect.Width / zoomRatio; tmpZoomRect.Height = height; if (!tmpZoomRect.Contains(zoomStartPoint)) { tmpZoomRect.Offset(0, oldHeight - height); } } else { double oldWidth = tmpZoomRect.Width; double width = tmpZoomRect.Height * zoomRatio; tmpZoomRect.Width = width; if (!tmpZoomRect.Contains(zoomStartPoint)) { tmpZoomRect.Offset(oldWidth - width, 0); } } } zoomRect = tmpZoomRect; UpdateSelectionAdorner(); } private void OnMouseUp(object sender, MouseButtonEventArgs e) { OnParentMouseUp(e); } protected virtual void OnParentMouseUp(MouseButtonEventArgs e) { if (isPanning && e.ChangedButton == MouseButton.Left) { isPanning = false; StopPanning(e); } else if (isZooming && e.ChangedButton == MouseButton.Left) { isZooming = false; StopZooming(); } } protected virtual void StopZooming() { if (zoomRect.HasValue) { Rect output = Viewport.Output; Point p1 = zoomRect.Value.TopLeft.ScreenToViewport(Viewport.Transform); Point p2 = zoomRect.Value.BottomRight.ScreenToViewport(Viewport.Transform); DataRect newVisible = new DataRect(p1, p2); Viewport.Visible = newVisible; zoomRect = null; ReleaseMouseCapture(); RemoveSelectionAdorner(); } } protected virtual void StopPanning(MouseButtonEventArgs e) { Plotter2D.UndoProvider.CaptureNewValue(Plotter2D.Viewport, Viewport2D.VisibleProperty, Viewport.Visible); if (!Plotter.IsFocused) { Plotter2D.Focus(); } Plotter2D.Viewport.PanningState = Viewport2DPanningState.NotPanning; ReleaseMouseCapture(); ClearValue(CursorProperty); } protected override void OnLostFocus(RoutedEventArgs e) { if (isZooming) { RemoveSelectionAdorner(); isZooming = false; } if (isPanning) { Plotter2D.Viewport.PanningState = Viewport2DPanningState.NotPanning; isPanning = false; } ReleaseMouseCapture(); base.OnLostFocus(e); } private void MouseWheelZoom(Point mousePos, double wheelRotationDelta) { Point zoomTo = mousePos.ScreenToViewport(Viewport.Transform); double zoomSpeed = Math.Abs(wheelRotationDelta / Mouse.MouseWheelDeltaForOneLine); zoomSpeed *= wheelZoomSpeed; if (wheelRotationDelta < 0) { zoomSpeed = 1 / zoomSpeed; } Viewport.Visible = Viewport.Visible.Zoom(zoomTo, zoomSpeed); } } }