using System; using System.Collections.Generic; using System.ComponentModel; using System.Drawing; using System.Text; using System.Windows.Forms; #pragma warning disable 1591 namespace PdfiumViewer { public abstract class PanningZoomingScrollControl : CustomScrollControl { public const double DefaultZoomMin = 0.1; public const double DefaultZoomMax = 5; public const double DefaultZoomFactor = 1.2; private static readonly Cursor PanCursor; static PanningZoomingScrollControl() { Application.AddMessageFilter(new WheelFilter()); using (var stream = typeof(PanningZoomingScrollControl).Assembly.GetManifestResourceStream(typeof(PanningZoomingScrollControl).Namespace + ".pan.cur")) { PanCursor = new Cursor(stream); } } private double _zoom = 1; private bool _canPan; private Point _dragStart; private Point _startOffset; private double _zoomMax; private double _zoomMin; public event EventHandler ZoomChanged; protected virtual void OnZoomChanged(EventArgs e) { var ev = ZoomChanged; if (ev != null) ev(this, e); } /// /// Gets or sets the current zoom level. /// [Browsable(false)] [DefaultValue(1.0)] public double Zoom { get { return _zoom; } set { value = Math.Min(Math.Max(value, ZoomMin), ZoomMax); SetZoom(value, null); } } protected virtual void SetZoom(double value, Point? focus) { _zoom = value; OnZoomChanged(EventArgs.Empty); Invalidate(); } [DefaultValue(DefaultZoomFactor)] public double ZoomFactor { get; set; } protected PanningZoomingScrollControl() { ZoomFactor = DefaultZoomFactor; _zoomMin = DefaultZoomMin; _zoomMax = DefaultZoomMax; } [DefaultValue(DefaultZoomMin)] public double ZoomMin { get { return _zoomMin; } set { _zoomMin = value; Zoom = Zoom; } } [DefaultValue(DefaultZoomMax)] public double ZoomMax { get { return _zoomMax; } set { _zoomMax = value; Zoom = Zoom; } } /// /// Zooms the PDF document in one step. /// public void ZoomIn() { Zoom *= ZoomFactor; } /// /// Zooms the PDF document out one step. /// public void ZoomOut() { Zoom /= ZoomFactor; } [DefaultValue(MouseWheelMode.PanAndZoom)] public MouseWheelMode MouseWheelMode { get; set; } /// /// Raises the event. /// /// A that contains the event data. protected override void OnMouseWheel(MouseEventArgs e) { base.OnMouseWheel(e); bool doZoom; switch (MouseWheelMode) { case MouseWheelMode.PanAndZoom: doZoom = (ModifierKeys & Keys.Control) != 0; break; case MouseWheelMode.Zoom: doZoom = true; break; default: doZoom = false; break; } if (doZoom) { double zoom = _zoom; if (e.Delta > 0) zoom *= ZoomFactor; else zoom /= ZoomFactor; zoom = Math.Min(Math.Max(zoom, ZoomMin), ZoomMax); SetZoom(zoom, e.Location); } else { base.OnMouseWheel(e); } } protected abstract Rectangle GetDocumentBounds(); /// /// Determines whether the specified key is a regular input key or a special key that requires preprocessing. /// /// /// true if the specified key is a regular input key; otherwise, false. /// /// One of the values. protected override bool IsInputKey(Keys keyData) { switch ((keyData) & Keys.KeyCode) { case Keys.Up: PerformScroll(ScrollAction.LineUp, Orientation.Vertical); return true; case Keys.Down: PerformScroll(ScrollAction.LineDown, Orientation.Vertical); return true; case Keys.Left: PerformScroll(ScrollAction.LineUp, Orientation.Horizontal); return true; case Keys.Right: PerformScroll(ScrollAction.LineDown, Orientation.Horizontal); return true; case Keys.PageUp: PerformScroll(ScrollAction.PageUp, Orientation.Vertical); return true; case Keys.PageDown: PerformScroll(ScrollAction.PageDown, Orientation.Vertical); return true; case Keys.Add: case Keys.Oemplus: if ((keyData & Keys.Modifiers) == Keys.Control) ZoomIn(); return true; case Keys.Subtract: case Keys.OemMinus: if ((keyData & Keys.Modifiers) == Keys.Control) ZoomOut(); return true; case Keys.Home: PerformScroll(ScrollAction.Home, Orientation.Vertical); return true; case Keys.End: PerformScroll(ScrollAction.End, Orientation.Vertical); return true; default: return base.IsInputKey(keyData); } } protected override void OnSetCursor(SetCursorEventArgs e) { if (_canPan && e.HitTest == HitTest.Client) e.Cursor = PanCursor; base.OnSetCursor(e); } protected override void OnLayout(LayoutEventArgs levent) { _canPan = DisplayRectangle.Width > ClientSize.Width || DisplayRectangle.Height > ClientSize.Height; base.OnLayout(levent); } protected override void OnMouseDown(MouseEventArgs e) { base.OnMouseDown(e); if (e.Button != MouseButtons.Left || !_canPan) return; Capture = true; _dragStart = e.Location; _startOffset = DisplayRectangle.Location; } protected override void OnMouseMove(MouseEventArgs e) { base.OnMouseMove(e); if (!Capture) return; var offset = new Point(e.Location.X - _dragStart.X, e.Location.Y - _dragStart.Y); SetDisplayRectLocation(new Point(_startOffset.X + offset.X, _startOffset.Y + offset.Y)); } protected override void OnMouseUp(MouseEventArgs e) { base.OnMouseUp(e); Capture = false; } private class WheelFilter : IMessageFilter { public bool PreFilterMessage(ref Message m) { if (m.Msg != NativeMethods.WM_MOUSEWHEEL) return false; var control = Control.FromHandle(NativeMethods.WindowFromPoint(Cursor.Position)); while (control != null && !(control is PanningZoomingScrollControl)) { control = control.Parent; } if (control == null) return false; NativeMethods.SendMessage(control.Handle, m.Msg, m.WParam, m.LParam); return true; } } } }