External/OxyPlot/OxyPlot.WindowsForms/Plot.cs
author StephaneLenclud
Mon, 02 Feb 2015 20:58:28 +0100
branchMiniDisplay
changeset 448 10c04c79527e
parent 391 5be8f2773237
permissions -rw-r--r--
Sync: Adding shamap and documentation to enable easier sync next time OHM is updated.
     1 // --------------------------------------------------------------------------------------------------------------------
     2 // <copyright file="Plot.cs" company="OxyPlot">
     3 //   The MIT License (MIT)
     4 //
     5 //   Copyright (c) 2012 Oystein Bjorke
     6 //
     7 //   Permission is hereby granted, free of charge, to any person obtaining a
     8 //   copy of this software and associated documentation files (the
     9 //   "Software"), to deal in the Software without restriction, including
    10 //   without limitation the rights to use, copy, modify, merge, publish,
    11 //   distribute, sublicense, and/or sell copies of the Software, and to
    12 //   permit persons to whom the Software is furnished to do so, subject to
    13 //   the following conditions:
    14 //
    15 //   The above copyright notice and this permission notice shall be included
    16 //   in all copies or substantial portions of the Software.
    17 //
    18 //   THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS
    19 //   OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF
    20 //   MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT.
    21 //   IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY
    22 //   CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT,
    23 //   TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE
    24 //   SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
    25 // </copyright>
    26 // <summary>
    27 //   Represents a control that displays a plot.
    28 // </summary>
    29 // --------------------------------------------------------------------------------------------------------------------
    30 namespace OxyPlot.WindowsForms
    31 {
    32     using System;
    33     using System.ComponentModel;
    34     using System.Diagnostics;
    35     using System.Drawing;
    36     using System.Runtime.InteropServices;
    37     using System.Windows.Forms;
    38 
    39     using OxyPlot.Axes;
    40     using OxyPlot.Series;
    41 
    42     /// <summary>
    43     /// Represents a control that displays a plot.
    44     /// </summary>
    45     [Serializable]
    46     public class Plot : Control, IPlotControl
    47     {
    48         /// <summary>
    49         /// The category for the properties of this control.
    50         /// </summary>
    51         private const string OxyPlotCategory = "OxyPlot";
    52 
    53         /// <summary>
    54         /// The invalidate lock.
    55         /// </summary>
    56         private readonly object invalidateLock = new object();
    57 
    58         /// <summary>
    59         /// The model lock.
    60         /// </summary>
    61         private readonly object modelLock = new object();
    62 
    63         /// <summary>
    64         /// The rendering lock.
    65         /// </summary>
    66         private readonly object renderingLock = new object();
    67 
    68         /// <summary>
    69         /// The current model (holding a reference to this plot control).
    70         /// </summary>
    71         [NonSerialized]
    72         private PlotModel currentModel;
    73 
    74         /// <summary>
    75         /// The is model invalidated.
    76         /// </summary>
    77         private bool isModelInvalidated;
    78 
    79         /// <summary>
    80         /// The model.
    81         /// </summary>
    82         private PlotModel model;
    83 
    84         /// <summary>
    85         /// The mouse manipulator.
    86         /// </summary>
    87         [NonSerialized]
    88         private ManipulatorBase mouseManipulator;
    89 
    90         /// <summary>
    91         /// The update data flag.
    92         /// </summary>
    93         private bool updateDataFlag = true;
    94 
    95         /// <summary>
    96         /// The zoom rectangle.
    97         /// </summary>
    98         private Rectangle zoomRectangle;
    99 
   100         /// <summary>
   101         /// The render context.
   102         /// </summary>
   103         private GraphicsRenderContext renderContext;
   104 
   105         /// <summary>
   106         /// Initializes a new instance of the <see cref="Plot"/> class.
   107         /// </summary>
   108         public Plot()
   109         {
   110             this.renderContext = new GraphicsRenderContext();
   111 
   112             // ReSharper disable DoNotCallOverridableMethodsInConstructor
   113             this.DoubleBuffered = true;
   114             // ReSharper restore DoNotCallOverridableMethodsInConstructor
   115             this.KeyboardPanHorizontalStep = 0.1;
   116             this.KeyboardPanVerticalStep = 0.1;
   117             this.PanCursor = Cursors.Hand;
   118             this.ZoomRectangleCursor = Cursors.SizeNWSE;
   119             this.ZoomHorizontalCursor = Cursors.SizeWE;
   120             this.ZoomVerticalCursor = Cursors.SizeNS;
   121         }
   122 
   123         /// <summary>
   124         /// Gets the actual model.
   125         /// </summary>
   126         /// <value> The actual model. </value>
   127         public PlotModel ActualModel
   128         {
   129             get
   130             {
   131                 return this.Model;
   132             }
   133         }
   134 
   135         /// <summary>
   136         /// Gets or sets the keyboard pan horizontal step.
   137         /// </summary>
   138         /// <value> The keyboard pan horizontal step. </value>
   139         [Category(OxyPlotCategory)]
   140         public double KeyboardPanHorizontalStep { get; set; }
   141 
   142         /// <summary>
   143         /// Gets or sets the keyboard pan vertical step.
   144         /// </summary>
   145         /// <value> The keyboard pan vertical step. </value>
   146         [Category(OxyPlotCategory)]
   147         public double KeyboardPanVerticalStep { get; set; }
   148 
   149         /// <summary>
   150         /// Gets or sets the model.
   151         /// </summary>
   152         [Browsable(false)]
   153         [DefaultValue(null)]
   154         [Category(OxyPlotCategory)]
   155         public PlotModel Model
   156         {
   157             get
   158             {
   159                 return this.model;
   160             }
   161 
   162             set
   163             {
   164                 if (this.model != value)
   165                 {
   166                     this.model = value;
   167                     this.OnModelChanged();
   168                 }
   169             }
   170         }
   171 
   172         /// <summary>
   173         /// Gets or sets the pan cursor.
   174         /// </summary>
   175         [Category(OxyPlotCategory)]
   176         public Cursor PanCursor { get; set; }
   177 
   178         /// <summary>
   179         /// Gets or sets the horizontal zoom cursor.
   180         /// </summary>
   181         [Category(OxyPlotCategory)]
   182         public Cursor ZoomHorizontalCursor { get; set; }
   183 
   184         /// <summary>
   185         /// Gets or sets the rectangle zoom cursor.
   186         /// </summary>
   187         [Category(OxyPlotCategory)]
   188         public Cursor ZoomRectangleCursor { get; set; }
   189 
   190         /// <summary>
   191         /// Gets or sets vertical zoom cursor.
   192         /// </summary>
   193         [Category(OxyPlotCategory)]
   194         public Cursor ZoomVerticalCursor { get; set; }
   195 
   196         /// <summary>
   197         /// Get the axes from a point.
   198         /// </summary>
   199         /// <param name="pt">
   200         /// The point.
   201         /// </param>
   202         /// <param name="xaxis">
   203         /// The x axis.
   204         /// </param>
   205         /// <param name="yaxis">
   206         /// The y axis.
   207         /// </param>
   208         public void GetAxesFromPoint(ScreenPoint pt, out Axis xaxis, out Axis yaxis)
   209         {
   210             if (this.Model == null)
   211             {
   212                 xaxis = null;
   213                 yaxis = null;
   214                 return;
   215             }
   216 
   217             this.Model.GetAxesFromPoint(pt, out xaxis, out yaxis);
   218         }
   219 
   220         /// <summary>
   221         /// Get the series from a point.
   222         /// </summary>
   223         /// <param name="pt">
   224         /// The point (screen coordinates).
   225         /// </param>
   226         /// <param name="limit">
   227         /// The limit.
   228         /// </param>
   229         /// <returns>
   230         /// The series.
   231         /// </returns>
   232         public Series GetSeriesFromPoint(ScreenPoint pt, double limit)
   233         {
   234             if (this.Model == null)
   235             {
   236                 return null;
   237             }
   238 
   239             return this.Model.GetSeriesFromPoint(pt, limit);
   240         }
   241 
   242         /// <summary>
   243         /// The hide tracker.
   244         /// </summary>
   245         public void HideTracker()
   246         {
   247         }
   248 
   249         /// <summary>
   250         /// The hide zoom rectangle.
   251         /// </summary>
   252         public void HideZoomRectangle()
   253         {
   254             this.zoomRectangle = Rectangle.Empty;
   255             this.Invalidate();
   256         }
   257 
   258         /// <summary>
   259         /// The invalidate plot.
   260         /// </summary>
   261         /// <param name="updateData">
   262         /// The update data.
   263         /// </param>
   264         public void InvalidatePlot(bool updateData)
   265         {
   266             lock (this.invalidateLock)
   267             {
   268                 this.isModelInvalidated = true;
   269                 this.updateDataFlag = this.updateDataFlag || updateData;
   270             }
   271 
   272             this.Invalidate();
   273         }
   274 
   275         /// <summary>
   276         /// Called when the Model property has been changed.
   277         /// </summary>
   278         public void OnModelChanged()
   279         {
   280             lock (this.modelLock)
   281             {
   282                 if (this.currentModel != null)
   283                 {
   284                     this.currentModel.AttachPlotControl(null);
   285                 }
   286 
   287                 if (this.Model != null)
   288                 {
   289                     if (this.Model.PlotControl != null)
   290                     {
   291                         throw new InvalidOperationException(
   292                             "This PlotModel is already in use by some other plot control.");
   293                     }
   294 
   295                     this.Model.AttachPlotControl(this);
   296                     this.currentModel = this.Model;
   297                 }
   298             }
   299 
   300             this.InvalidatePlot(true);
   301         }
   302 
   303         /// <summary>
   304         /// The pan.
   305         /// </summary>
   306         /// <param name="axis">
   307         /// The axis.
   308         /// </param>
   309         /// <param name="x0">
   310         /// The x 0.
   311         /// </param>
   312         /// <param name="x1">
   313         /// The x 1.
   314         /// </param>
   315         public void Pan(Axis axis, ScreenPoint x0, ScreenPoint x1)
   316         {
   317             axis.Pan(x0, x1);
   318             this.InvalidatePlot(false);
   319         }
   320 
   321         /// <summary>
   322         /// Pans all axes.
   323         /// </summary>
   324         /// <param name="deltax">
   325         /// The horizontal delta.
   326         /// </param>
   327         /// <param name="deltay">
   328         /// The vertical delta.
   329         /// </param>
   330         public void PanAll(double deltax, double deltay)
   331         {
   332             foreach (var a in this.ActualModel.Axes)
   333             {
   334                 a.Pan(a.IsHorizontal() ? deltax : deltay);
   335             }
   336 
   337             this.InvalidatePlot(false);
   338         }
   339 
   340         /// <summary>
   341         /// The refresh plot.
   342         /// </summary>
   343         /// <param name="updateData">
   344         /// The update data.
   345         /// </param>
   346         public void RefreshPlot(bool updateData)
   347         {
   348             lock (this.invalidateLock)
   349             {
   350                 this.isModelInvalidated = true;
   351                 this.updateDataFlag = this.updateDataFlag || updateData;
   352             }
   353 
   354             this.Refresh();
   355         }
   356 
   357         /// <summary>
   358         /// The reset.
   359         /// </summary>
   360         /// <param name="axis">
   361         /// The axis.
   362         /// </param>
   363         public void Reset(Axis axis)
   364         {
   365             axis.Reset();
   366             this.InvalidatePlot(false);
   367         }
   368 
   369         /// <summary>
   370         /// Sets the cursor type.
   371         /// </summary>
   372         /// <param name="cursorType">
   373         /// The cursor type.
   374         /// </param>
   375         public void SetCursorType(CursorType cursorType)
   376         {
   377             switch (cursorType)
   378             {
   379                 case CursorType.Pan:
   380                     this.Cursor = this.PanCursor;
   381                     break;
   382                 case CursorType.ZoomRectangle:
   383                     this.Cursor = this.ZoomRectangleCursor;
   384                     break;
   385                 case CursorType.ZoomHorizontal:
   386                     this.Cursor = this.ZoomHorizontalCursor;
   387                     break;
   388                 case CursorType.ZoomVertical:
   389                     this.Cursor = this.ZoomVerticalCursor;
   390                     break;
   391                 default:
   392                     this.Cursor = Cursors.Arrow;
   393                     break;
   394             }
   395         }
   396 
   397         /// <summary>
   398         /// The show tracker.
   399         /// </summary>
   400         /// <param name="data">
   401         /// The data.
   402         /// </param>
   403         public void ShowTracker(TrackerHitResult data)
   404         {
   405             // not implemented for WindowsForms
   406         }
   407 
   408         /// <summary>
   409         /// The show zoom rectangle.
   410         /// </summary>
   411         /// <param name="r">
   412         /// The r.
   413         /// </param>
   414         public void ShowZoomRectangle(OxyRect r)
   415         {
   416             this.zoomRectangle = new Rectangle((int)r.Left, (int)r.Top, (int)r.Width, (int)r.Height);
   417             this.Invalidate();
   418         }
   419 
   420         /// <summary>
   421         /// The zoom.
   422         /// </summary>
   423         /// <param name="axis">
   424         /// The axis.
   425         /// </param>
   426         /// <param name="p1">
   427         /// The p 1.
   428         /// </param>
   429         /// <param name="p2">
   430         /// The p 2.
   431         /// </param>
   432         public void Zoom(Axis axis, double p1, double p2)
   433         {
   434             axis.Zoom(p1, p2);
   435             this.InvalidatePlot(false);
   436         }
   437 
   438         /// <summary>
   439         /// The zoom all.
   440         /// </summary>
   441         public void ZoomAll()
   442         {
   443             foreach (var a in this.Model.Axes)
   444             {
   445                 a.Reset();
   446             }
   447 
   448             this.InvalidatePlot(false);
   449         }
   450 
   451         /// <summary>
   452         /// Zooms all axes.
   453         /// </summary>
   454         /// <param name="delta">
   455         /// The delta.
   456         /// </param>
   457         public void ZoomAllAxes(double delta)
   458         {
   459             foreach (var a in this.ActualModel.Axes)
   460             {
   461                 this.ZoomAt(a, delta);
   462             }
   463 
   464             this.RefreshPlot(false);
   465         }
   466 
   467         /// <summary>
   468         /// The zoom at.
   469         /// </summary>
   470         /// <param name="axis">
   471         /// The axis.
   472         /// </param>
   473         /// <param name="factor">
   474         /// The factor.
   475         /// </param>
   476         /// <param name="x">
   477         /// The x.
   478         /// </param>
   479         public void ZoomAt(Axis axis, double factor, double x = double.NaN)
   480         {
   481             if (double.IsNaN(x))
   482             {
   483                 double sx = (axis.Transform(axis.ActualMaximum) + axis.Transform(axis.ActualMinimum)) * 0.5;
   484                 x = axis.InverseTransform(sx);
   485             }
   486 
   487             axis.ZoomAt(factor, x);
   488             this.InvalidatePlot(false);
   489         }
   490 
   491         /// <summary>
   492         /// The on mouse down.
   493         /// </summary>
   494         /// <param name="e">
   495         /// The e.
   496         /// </param>
   497         protected override void OnMouseDown(MouseEventArgs e)
   498         {
   499             base.OnMouseDown(e);
   500 
   501             if (this.mouseManipulator != null)
   502             {
   503                 return;
   504             }
   505 
   506             this.Focus();
   507             this.Capture = true;
   508 
   509             if (this.ActualModel != null)
   510             {
   511                 var args = this.CreateMouseEventArgs(e);
   512                 this.ActualModel.HandleMouseDown(this, args);
   513                 if (args.Handled)
   514                 {
   515                     return;
   516                 }
   517             }
   518 
   519             this.mouseManipulator = this.GetManipulator(e);
   520 
   521             if (this.mouseManipulator != null)
   522             {
   523                 this.mouseManipulator.Started(this.CreateManipulationEventArgs(e));
   524             }
   525         }
   526 
   527         /// <summary>
   528         /// The on mouse move.
   529         /// </summary>
   530         /// <param name="e">
   531         /// The e.
   532         /// </param>
   533         protected override void OnMouseMove(MouseEventArgs e)
   534         {
   535             base.OnMouseMove(e);
   536 
   537             if (this.ActualModel != null)
   538             {
   539                 var args = this.CreateMouseEventArgs(e);
   540                 this.ActualModel.HandleMouseMove(this, args);
   541                 if (args.Handled)
   542                 {
   543                     return;
   544                 }
   545             }
   546 
   547             if (this.mouseManipulator != null)
   548             {
   549                 this.mouseManipulator.Delta(this.CreateManipulationEventArgs(e));
   550             }
   551         }
   552 
   553         /// <summary>
   554         /// Raises the <see cref="E:System.Windows.Forms.Control.MouseUp"/> event.
   555         /// </summary>
   556         /// <param name="e">
   557         /// A <see cref="T:System.Windows.Forms.MouseEventArgs"/> that contains the event data.
   558         /// </param>
   559         protected override void OnMouseUp(MouseEventArgs e)
   560         {
   561             base.OnMouseUp(e);
   562             this.Capture = false;
   563 
   564             if (this.ActualModel != null)
   565             {
   566                 var args = this.CreateMouseEventArgs(e);
   567                 this.ActualModel.HandleMouseUp(this, args);
   568                 if (args.Handled)
   569                 {
   570                     return;
   571                 }
   572             }
   573 
   574             if (this.mouseManipulator != null)
   575             {
   576                 this.mouseManipulator.Completed(this.CreateManipulationEventArgs(e));
   577             }
   578 
   579             this.mouseManipulator = null;
   580         }
   581 
   582         /// <summary>
   583         /// Raises the <see cref="E:System.Windows.Forms.Control.MouseWheel"/> event.
   584         /// </summary>
   585         /// <param name="e">
   586         /// A <see cref="T:System.Windows.Forms.MouseEventArgs"/> that contains the event data.
   587         /// </param>
   588         protected override void OnMouseWheel(MouseEventArgs e)
   589         {
   590             base.OnMouseWheel(e);
   591             bool isControlDown = ModifierKeys == Keys.Control;
   592             var m = new ZoomStepManipulator(this, e.Delta * 0.001, isControlDown);
   593             m.Started(new ManipulationEventArgs(e.Location.ToScreenPoint()));
   594         }
   595 
   596         /// <summary>
   597         /// Raises the <see cref="E:System.Windows.Forms.Control.Paint"/> event.
   598         /// </summary>
   599         /// <param name="e">
   600         /// A <see cref="T:System.Windows.Forms.PaintEventArgs"/> that contains the event data.
   601         /// </param>
   602         protected override void OnPaint(PaintEventArgs e)
   603         {
   604             base.OnPaint(e);
   605             try
   606             {
   607                 lock (this.invalidateLock)
   608                 {
   609                     if (this.isModelInvalidated)
   610                     {
   611                         if (this.model != null)
   612                         {
   613                             this.model.Update(this.updateDataFlag);
   614                             this.updateDataFlag = false;
   615                         }
   616 
   617                         this.isModelInvalidated = false;
   618                     }
   619                 }
   620 
   621                 lock (this.renderingLock)
   622                 {
   623                     this.renderContext.SetGraphicsTarget(e.Graphics);
   624                     if (this.model != null)
   625                     {
   626                         this.model.Render(this.renderContext, this.Width, this.Height);
   627                     }
   628 
   629                     if (this.zoomRectangle != Rectangle.Empty)
   630                     {
   631                         using (var zoomBrush = new SolidBrush(Color.FromArgb(0x40, 0xFF, 0xFF, 0x00)))
   632                         using (var zoomPen = new Pen(Color.Black))
   633                         {
   634                             zoomPen.DashPattern = new float[] { 3, 1 };
   635                             e.Graphics.FillRectangle(zoomBrush, this.zoomRectangle);
   636                             e.Graphics.DrawRectangle(zoomPen, this.zoomRectangle);
   637                         }
   638                     }
   639                 }
   640             }
   641             catch (Exception paintException)
   642             {
   643                 var trace = new StackTrace(paintException);
   644                 Debug.WriteLine(paintException);
   645                 Debug.WriteLine(trace);
   646                 using (var font = new Font("Arial", 10))
   647                 {
   648                     e.Graphics.DrawString(
   649                         "OxyPlot paint exception: " + paintException.Message, font, Brushes.Red, 10, 10);
   650                 }
   651             }
   652         }
   653 
   654         /// <summary>
   655         /// Raises the <see cref="E:System.Windows.Forms.Control.PreviewKeyDown"/> event.
   656         /// </summary>
   657         /// <param name="e">
   658         /// A <see cref="T:System.Windows.Forms.PreviewKeyDownEventArgs"/> that contains the event data.
   659         /// </param>
   660         protected override void OnPreviewKeyDown(PreviewKeyDownEventArgs e)
   661         {
   662             base.OnPreviewKeyDown(e);
   663             if (e.KeyCode == Keys.A)
   664             {
   665                 this.ZoomAll();
   666             }
   667 
   668             bool control = (e.Modifiers & Keys.Control) == Keys.Control;
   669             bool alt = (e.Modifiers & Keys.Alt) == Keys.Alt;
   670 
   671             double deltax = 0;
   672             double deltay = 0;
   673             double zoom = 0;
   674             switch (e.KeyCode)
   675             {
   676                 case Keys.Up:
   677                     deltay = -1;
   678                     break;
   679                 case Keys.Down:
   680                     deltay = 1;
   681                     break;
   682                 case Keys.Left:
   683                     deltax = -1;
   684                     break;
   685                 case Keys.Right:
   686                     deltax = 1;
   687                     break;
   688                 case Keys.Add:
   689                 case Keys.Oemplus:
   690                 case Keys.PageUp:
   691                     zoom = 1;
   692                     break;
   693                 case Keys.Subtract:
   694                 case Keys.OemMinus:
   695                 case Keys.PageDown:
   696                     zoom = -1;
   697                     break;
   698             }
   699 
   700             if ((deltax * deltax) + (deltay * deltay) > 0)
   701             {
   702                 deltax = deltax * this.ActualModel.PlotArea.Width * this.KeyboardPanHorizontalStep;
   703                 deltay = deltay * this.ActualModel.PlotArea.Height * this.KeyboardPanVerticalStep;
   704 
   705                 // small steps if the user is pressing control
   706                 if (control)
   707                 {
   708                     deltax *= 0.2;
   709                     deltay *= 0.2;
   710                 }
   711 
   712                 this.PanAll(deltax, deltay);
   713 
   714                 // e.Handled = true;
   715             }
   716 
   717             if (Math.Abs(zoom) > 1e-8)
   718             {
   719                 if (control)
   720                 {
   721                     zoom *= 0.2;
   722                 }
   723 
   724                 this.ZoomAllAxes(1 + (zoom * 0.12));
   725 
   726                 // e.Handled = true;
   727             }
   728 
   729             if (control && alt && this.ActualModel != null)
   730             {
   731                 switch (e.KeyCode)
   732                 {
   733                     case Keys.R:
   734                         this.SetClipboardText(this.ActualModel.CreateTextReport());
   735                         break;
   736                     case Keys.C:
   737                         this.SetClipboardText(this.ActualModel.ToCode());
   738                         break;
   739                     case Keys.X:
   740 
   741                         // this.SetClipboardText(this.ActualModel.ToXml());
   742                         break;
   743                 }
   744             }
   745         }
   746 
   747         /// <summary>
   748         /// Raises the <see cref="E:System.Windows.Forms.Control.Resize"/> event.
   749         /// </summary>
   750         /// <param name="e">
   751         /// An <see cref="T:System.EventArgs"/> that contains the event data.
   752         /// </param>
   753         protected override void OnResize(EventArgs e)
   754         {
   755             base.OnResize(e);
   756             this.InvalidatePlot(false);
   757         }
   758 
   759         /// <summary>
   760         /// Converts the changed button.
   761         /// </summary>
   762         /// <param name="e">
   763         /// The <see cref="System.Windows.Forms.MouseEventArgs"/> instance containing the event data.
   764         /// </param>
   765         /// <returns>
   766         /// The mouse button.
   767         /// </returns>
   768         private static OxyMouseButton ConvertChangedButton(MouseEventArgs e)
   769         {
   770             switch (e.Button)
   771             {
   772                 case MouseButtons.Left:
   773                     return OxyMouseButton.Left;
   774                 case MouseButtons.Middle:
   775                     return OxyMouseButton.Middle;
   776                 case MouseButtons.Right:
   777                     return OxyMouseButton.Right;
   778                 case MouseButtons.XButton1:
   779                     return OxyMouseButton.XButton1;
   780                 case MouseButtons.XButton2:
   781                     return OxyMouseButton.XButton2;
   782             }
   783 
   784             return OxyMouseButton.Left;
   785         }
   786 
   787         /// <summary>
   788         /// Creates the mouse event arguments.
   789         /// </summary>
   790         /// <param name="e">
   791         /// The <see cref="System.Windows.Forms.MouseEventArgs"/> instance containing the event data.
   792         /// </param>
   793         /// <returns>
   794         /// Mouse event arguments.
   795         /// </returns>
   796         private OxyMouseEventArgs CreateMouseEventArgs(MouseEventArgs e)
   797         {
   798             return new OxyMouseEventArgs
   799             {
   800                 ChangedButton = ConvertChangedButton(e),
   801                 Position = new ScreenPoint(e.Location.X, e.Location.Y),
   802                 IsShiftDown = (ModifierKeys & Keys.Shift) == Keys.Shift,
   803                 IsControlDown = (ModifierKeys & Keys.Control) == Keys.Control,
   804                 IsAltDown = (ModifierKeys & Keys.Alt) == Keys.Alt,
   805             };
   806         }
   807 
   808         /// <summary>
   809         /// Creates the manipulation event args.
   810         /// </summary>
   811         /// <param name="e">
   812         /// The MouseEventArgs instance containing the event data.
   813         /// </param>
   814         /// <returns>
   815         /// A manipulation event args object.
   816         /// </returns>
   817         private ManipulationEventArgs CreateManipulationEventArgs(MouseEventArgs e)
   818         {
   819             return new ManipulationEventArgs(e.Location.ToScreenPoint());
   820         }
   821 
   822         /// <summary>
   823         /// Gets the manipulator for the current mouse button and modifier keys.
   824         /// </summary>
   825         /// <param name="e">
   826         /// The event args.
   827         /// </param>
   828         /// <returns>
   829         /// A manipulator or null if no gesture was recognized.
   830         /// </returns>
   831         private ManipulatorBase GetManipulator(MouseEventArgs e)
   832         {
   833             bool control = (ModifierKeys & Keys.Control) == Keys.Control;
   834             bool shift = (ModifierKeys & Keys.Shift) == Keys.Shift;
   835             bool alt = (ModifierKeys & Keys.Alt) == Keys.Alt;
   836 
   837             bool lmb = e.Button == MouseButtons.Left;
   838             bool rmb = e.Button == MouseButtons.Right;
   839             bool mmb = e.Button == MouseButtons.Middle;
   840             bool xb1 = e.Button == MouseButtons.XButton1;
   841             bool xb2 = e.Button == MouseButtons.XButton2;
   842 
   843             // MMB / control RMB / control+alt LMB
   844             if (mmb || (control && lmb) || (control && alt && rmb))
   845             {
   846                 return new ZoomRectangleManipulator(this);
   847             }
   848 
   849             // Right mouse button / alt+left mouse button
   850             if (lmb || (rmb && alt))
   851             {
   852                 if (e.Clicks == 2)
   853                 {
   854                     return new ResetManipulator(this);
   855                 }
   856                 return new PanManipulator(this);
   857             }
   858 
   859             // Left mouse button
   860             if (rmb)
   861             {
   862                 return new TrackerManipulator(this) { Snap = !control, PointsOnly = shift };
   863             }
   864 
   865             // XButtons are zoom-stepping
   866             if (xb1 || xb2)
   867             {
   868                 double d = xb1 ? 0.05 : -0.05;
   869                 return new ZoomStepManipulator(this, d, control);
   870             }
   871 
   872             return null;
   873         }
   874 
   875         /// <summary>
   876         /// The set clipboard text.
   877         /// </summary>
   878         /// <param name="text">
   879         /// The text.
   880         /// </param>
   881         private void SetClipboardText(string text)
   882         {
   883             try
   884             {
   885                 // todo: can't get the following solution to work
   886                 // http://stackoverflow.com/questions/5707990/requested-clipboard-operation-did-not-succeed
   887                 Clipboard.SetText(text);
   888             }
   889             catch (ExternalException ee)
   890             {
   891                 // Requested Clipboard operation did not succeed.
   892                 MessageBox.Show(this, ee.Message, "OxyPlot");
   893             }
   894         }
   895     }
   896 }