root/plot/Pybrary.Plot/PlotControl.cs

Revision 746, 14.4 kB (checked in by mfenniak, 2 years ago)

--

Line 
1 using System;
2 using System.Collections.Generic;
3 using System.Text;
4 using System.Windows.Forms;
5 using System.Drawing;
6 using System.Drawing.Drawing2D;
7
8 namespace Pybrary.Plot
9 {
10     public class PlotControl : Control
11     {
12         private BrushDescription selectionBrush =
13             new BrushDescription(Color.FromArgb(100, Color.FromKnownColor(KnownColor.Highlight)));
14
15         private Plot plot = null;
16         private EventHandler drawHandler;
17         private IList<ToolStripMenuItem> scaleMenuItems = new List<ToolStripMenuItem>();
18
19         public PlotControl()
20         {
21             drawHandler = delegate(object sender, EventArgs args) { Invalidate(); };
22             this.ResizeRedraw = true;
23             this.SetStyle(
24                 ControlStyles.UserPaint |
25                 ControlStyles.AllPaintingInWmPaint |
26                 ControlStyles.OptimizedDoubleBuffer, true);
27
28             this.MouseDown += new System.Windows.Forms.MouseEventHandler(this.PlotControl_MouseDown);
29             this.MouseMove += new System.Windows.Forms.MouseEventHandler(this.PlotControl_MouseMove);
30             this.MouseUp += new System.Windows.Forms.MouseEventHandler(this.PlotControl_MouseUp);
31             this.LostFocus += new System.EventHandler(this.PlotControl_LostFocus);
32         }
33
34         protected override void OnPaint(PaintEventArgs pe)
35         {
36             if (plot == null)
37             {
38                 using (SolidBrush white = new SolidBrush(Color.White))
39                     pe.Graphics.FillRectangle(white, pe.ClipRectangle);
40             }
41             else
42             {
43                 plot.PaintOn(pe.Graphics, this.ClientRectangle);
44                 if (selectionBr != null)
45                 {
46                     GraphicsState _s = pe.Graphics.Save();
47                     pe.Graphics.PageUnit = GraphicsUnit.Inch;
48                     pe.Graphics.FillRectangle(selectionBr, selectionRect.Rect);
49                     pe.Graphics.Restore(_s);
50                 }
51                 RepopulateScaleMenus();
52             }
53         }
54
55         public Plot Plot
56         {
57             get
58             {
59                 return plot;
60             }
61             set
62             {
63                 if (plot != null)
64                     plot.OnChanged -= drawHandler;
65                 plot = value;
66                 if (plot != null)
67                     plot.OnChanged += drawHandler;
68                 Invalidate();
69             }
70         }
71
72        
73
74         public ToolStripMenuItem CreatePlotMenu()
75         {
76             ToolStripMenuItem plotMenu = new ToolStripMenuItem("Plot");
77
78             ToolStripMenuItem manualScale = new ToolStripMenuItem("Set Scale...");
79             scaleMenuItems.Add(manualScale);
80             RepopulateScaleMenus();
81             plotMenu.DropDownItems.Add(manualScale);
82
83             ToolStripMenuItem zoom = new ToolStripMenuItem("Zoom");
84             zoom.Click += delegate(object sender, EventArgs e) { enableZoom(); };
85             zoom.Paint += delegate(object sender, PaintEventArgs e) { zoom.Enabled = (plot != null); };
86             plotMenu.DropDownItems.Add(zoom);
87
88             ToolStripMenuItem zoomReset = new ToolStripMenuItem("Reset Zoom");
89             zoomReset.Click += delegate(object sender, EventArgs e) { resetZoom(); };
90             zoomReset.Paint += new PaintEventHandler(ResetPaintHandler);
91             plotMenu.DropDownItems.Add(zoomReset);
92
93             plotMenu.DropDownItems.Add(new ToolStripSeparator());
94
95             ToolStripMenuItem clip = new ToolStripMenuItem("Copy to Clipboard");
96             clip.Click += delegate(object sender, EventArgs e) { copyToClipboard(); };
97             clip.Paint += delegate(object sender, PaintEventArgs e) { zoom.Enabled = (plot != null); };
98             plotMenu.DropDownItems.Add(clip);
99
100             return plotMenu;
101         }
102
103         private void RepopulateScaleMenus()
104         {
105             foreach (ToolStripMenuItem item in scaleMenuItems)
106             {
107                 item.DropDownItems.Clear();
108                 if (plot == null)
109                 {
110                     item.Enabled = false;
111                     return;
112                 }
113                 else
114                     item.Enabled = true;
115
116                 foreach (KeyValuePair<string, NumericYAxis> kv in plot.YAxes)
117                 {
118                     NumericAxis axis = kv.Value;
119                     if (axis.Visible)
120                     {
121                         ToolStripMenuItem newitem = new ToolStripMenuItem(axis.Title);
122                         newitem.Click += delegate(object sender, EventArgs e) { ManuallyScale(axis); };
123                         item.DropDownItems.Add(newitem);
124                     }
125                 }
126             }
127         }
128
129         private void ManuallyScale(NumericAxis axis)
130         {
131             ManualNumericAxisForm form = new ManualNumericAxisForm(axis);
132             form.ShowDialog();
133         }
134
135         private void ResetPaintHandler(object sender, PaintEventArgs e)
136         {
137             ToolStripMenuItem zoomReset = sender as ToolStripMenuItem;
138             bool enabled = false;
139             if (plot != null)
140             {
141                 enabled = enabled || plot.XAxis.ZoomedMinimum.HasValue || plot.XAxis.ZoomedMaximum.HasValue;
142                 foreach (KeyValuePair<string, NumericYAxis> kv in plot.YAxes)
143                 {
144                     NumericYAxis y = kv.Value;
145                     enabled = enabled || y.ZoomedMinimum.HasValue || y.ZoomedMaximum.HasValue;
146                 }
147             }
148             zoomReset.Enabled = enabled;
149         }
150
151         private void copyToClipboard()
152         {
153             if (plot == null)
154                 return;
155
156             DataObject o = new DataObject();
157
158             using (Bitmap bmp = new Bitmap(800, 600))
159             {
160                 // Bitmap
161                 using (Graphics g = Graphics.FromImage(bmp))
162                 {
163                     g.PageUnit = GraphicsUnit.Pixel;
164                     plot.PaintOn(g, new Rectangle(0, 0, bmp.Width, bmp.Height));
165                     g.Flush();
166
167                     o.SetData(DataFormats.Bitmap, true, bmp);
168                 }
169
170                 o.SetData(DataFormats.Text, true, plot.Series.TextExport());
171
172                 Clipboard.SetDataObject(o, true);
173             }
174         }
175
176         private bool mouseDown = false;
177         private PointF mouseDownLocation;
178
179         private bool zoomEnabled = false;
180         private Cursor savedCursor = null;
181
182         private bool zoomingPlotArea;
183         private bool zoomingXAxis;
184         private YAxis zoomingYAxis;
185
186         private void enableZoom()
187         {
188             if (zoomEnabled)
189                 return;
190             savedCursor = this.Cursor;
191             //this.Cursor = Cursors.Cross;
192             zoomEnabled = true;
193         }
194
195         private void disableZoom()
196         {
197             if (zoomEnabled)
198             {
199                 this.Cursor = savedCursor;
200                 zoomingPlotArea = zoomingXAxis = false;
201                 zoomingYAxis = null;
202                 savedCursor = null;
203                 zoomEnabled = false;
204             }
205         }
206
207         private void resetZoom()
208         {
209             using (plot.SuspendEvents())
210             {
211                 plot.XAxis.ZoomedMinimum = null;
212                 plot.XAxis.ZoomedMaximum = null;
213                 foreach (KeyValuePair<string, NumericYAxis> kv in plot.YAxes)
214                 {
215                     NumericYAxis y = kv.Value;
216                     y.ZoomedMinimum = y.ZoomedMaximum = null;
217                 }
218             }
219         }
220
221         protected override bool ProcessCmdKey(ref Message msg, Keys keyData)
222         {
223             if (msg.WParam.ToInt32() == Convert.ToInt32(Keys.Escape))
224             {
225                 disableZoom();
226                 return true;
227             }
228
229             return base.ProcessCmdKey(ref msg, keyData);
230         }
231
232         private void PlotControl_MouseDown(object sender, MouseEventArgs e)
233         {
234             if (this.plot != null)
235             {
236                 if (e.Button == MouseButtons.Left)
237                 {
238                     mouseDown = true;
239                     mouseDownLocation = screenToInches(e.Location);
240                 }
241             }
242         }
243
244         private void PlotControl_MouseUp(object sender, MouseEventArgs e)
245         {
246             mouseDown = false;
247             finishMouseDragging();
248         }
249
250         private void PlotControl_MouseMove(object sender, MouseEventArgs e)
251         {
252             if (plot == null)
253                 return;
254
255             PointF cursor = screenToInches(e.Location);
256             if (!mouseDragging && mouseDown && zoomEnabled)
257             {
258                 // Dragging the mouse - ignore small drags
259                 if (Math.Abs(cursor.X - mouseDownLocation.X) < 0.05 &&
260                     Math.Abs(cursor.Y - mouseDownLocation.Y) < 0.05)
261                     return;
262                 beginMouseDragging(e.Location);
263             }
264
265             if (mouseDragging)
266                 duringMouseDragging(e.Location);
267
268             if (!mouseDragging && zoomEnabled)
269             {
270                 // otherwise, let's try to find where we are...
271                 if (plot.DataArea.Rect.Contains(cursor))
272                     this.Cursor = Cursors.Cross;
273                 else if (plot.XAxis.DrawArea.HasValue && plot.XAxis.DrawArea.Value.Rect.Contains(cursor))
274                     this.Cursor = Cursors.SizeWE;
275                 else if (plot.YAxes.FindAxisAt(cursor) != null)
276                     this.Cursor = Cursors.SizeNS;
277                 else
278                     this.Cursor = Cursors.Default;
279             }
280         }
281
282         private void PlotControl_LostFocus(object sender, EventArgs e)
283         {
284             mouseDown = false;
285             finishMouseDragging();
286         }
287
288
289         private bool mouseDragging = false;
290         private AdvancedRect selectionRect;
291         private Brush selectionBr;
292
293         private void beginMouseDragging(Point location)
294         {
295             PointF cursor = screenToInches(location);
296
297             Console.WriteLine("Begin dragging.");
298             mouseDragging = true;
299             selectionBr = selectionBrush.CreateBrush();
300
301             // otherwise, let's try to find where we are...
302             if (plot.DataArea.Rect.Contains(cursor))
303                 zoomingPlotArea = true;
304             else if (plot.XAxis.DrawArea.HasValue && plot.XAxis.DrawArea.Value.Rect.Contains(cursor))
305                 zoomingXAxis = true;
306             else
307             {
308                 zoomingYAxis = plot.YAxes.FindAxisAt(cursor);
309                 if (zoomingYAxis == null)
310                     finishMouseDragging();
311             }
312         }
313
314         private void duringMouseDragging(Point cursor)
315         {
316             PointF realCursor = screenToInches(cursor);
317             if (zoomingPlotArea)
318             {
319                 selectionRect = new AdvancedRect(
320                     new PointF(
321                         Math.Max(plot.DataArea.TopLeft.X, Math.Min(realCursor.X, mouseDownLocation.X)),
322                         Math.Max(plot.DataArea.TopLeft.Y, Math.Min(realCursor.Y, mouseDownLocation.Y))),
323                     new PointF(
324                         Math.Min(plot.DataArea.BottomRight.X, Math.Max(realCursor.X, mouseDownLocation.X)),
325                         Math.Min(plot.DataArea.BottomRight.Y, Math.Max(realCursor.Y, mouseDownLocation.Y)))
326                 );
327             }
328             else if (zoomingXAxis)
329             {
330                 selectionRect = plot.DataArea;
331                 selectionRect.TopLeft.X = Math.Max(selectionRect.TopLeft.X, Math.Min(realCursor.X, mouseDownLocation.X));
332                 selectionRect.BottomRight.X = Math.Min(selectionRect.BottomRight.X, Math.Max(realCursor.X, mouseDownLocation.X));
333             }
334             else if (zoomingYAxis != null)
335             {
336                 selectionRect = plot.DataArea;
337                 selectionRect.TopLeft.Y = Math.Max(selectionRect.TopLeft.Y, Math.Min(realCursor.Y, mouseDownLocation.Y));
338                 selectionRect.BottomRight.Y = Math.Min(selectionRect.BottomRight.Y, Math.Max(realCursor.Y, mouseDownLocation.Y));
339             }
340
341             Invalidate();
342         }
343
344         private PointF screenToInches(Point x)
345         {
346             using (Graphics g = this.CreateGraphics())
347             {
348                 GraphicsState _s = g.Save();
349                 g.PageUnit = GraphicsUnit.Inch;
350                 PointF[] arr = new PointF[] { new PointF(x.X, x.Y) };
351                 g.TransformPoints(CoordinateSpace.Page, CoordinateSpace.Device, arr);
352                 g.Restore(_s);
353                 return arr[0];
354             }
355         }
356
357         private void finishMouseDragging()
358         {
359             if (!mouseDragging)
360                 return;
361
362             using (plot.SuspendEvents())
363             {
364                 AdvancedRect dataArea = plot.DataArea;
365
366                 if (zoomingPlotArea || zoomingXAxis)
367                 {
368                     double newMin = plot.XAxis.CoordinateToData(selectionRect.TopLeft.X, dataArea);
369                     double newMax = plot.XAxis.CoordinateToData(selectionRect.BottomRight.X, dataArea);
370                     plot.XAxis.ZoomedMinimum = newMin;
371                     plot.XAxis.ZoomedMaximum = newMax;
372
373                     if (zoomingPlotArea)
374                     {
375                         foreach (KeyValuePair<string, NumericYAxis> kv in plot.YAxes)
376                         {
377                             NumericYAxis y = kv.Value;
378                             newMax = y.CoordinateToData(selectionRect.TopLeft.Y, dataArea);
379                             newMin = y.CoordinateToData(selectionRect.BottomRight.Y, dataArea);
380                             y.ZoomedMinimum = newMin;
381                             y.ZoomedMaximum = newMax;
382                         }
383                     }
384                 }
385                 else if (zoomingYAxis != null)
386                 {
387                     double newMax = zoomingYAxis.CoordinateToData(selectionRect.TopLeft.Y, dataArea);
388                     double newMin = zoomingYAxis.CoordinateToData(selectionRect.BottomRight.Y, dataArea);
389                     zoomingYAxis.ZoomedMinimum = newMin;
390                     zoomingYAxis.ZoomedMaximum = newMax;
391                 }
392
393                 selectionBr.Dispose();
394                 selectionBr = null;
395                 mouseDragging = false;
396
397                 disableZoom();
398             }
399         }
400     }
401 }
Note: See TracBrowser for help on using the browser.