407 lines
9.6 KiB
C#
407 lines
9.6 KiB
C#
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
|
|
{
|
|
/// <summary>Provides common methods of mouse navigation around viewport</summary>
|
|
public class MouseNavigation : NavigationBase
|
|
{
|
|
/// <summary>
|
|
/// Initializes a new instance of the <see cref="MouseNavigation"/> class.
|
|
/// </summary>
|
|
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);
|
|
}
|
|
}
|
|
}
|