External/OxyPlot/OxyPlot.WindowsForms/Plot.cs
author StephaneLenclud
Thu, 18 Apr 2013 23:25:10 +0200
branchMiniDisplay
changeset 444 9b09e2ee0968
parent 391 5be8f2773237
permissions -rw-r--r--
Front View plug-in does not init if no sensor added.
Fixing some format to make strings shorter.
Now trying to start SoundGraphAccess.exe process from same directory.
Packed mode now can display three sensors along with the current time.
     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 }