Free cookie consent management tool by TermsFeed Policy Generator

source: trunk/sources/HeuristicLab.Visualization/3.2/LineChart.cs @ 3001

Last change on this file since 3001 was 2017, checked in by bspisic, 15 years ago

Refactoring PanListener(#664)

File size: 30.2 KB
Line 
1using System;
2using System.Collections.Generic;
3using System.Drawing;
4using System.Drawing.Drawing2D;
5using System.Windows.Forms;
6using HeuristicLab.Core;
7using HeuristicLab.Visualization.DataExport;
8using HeuristicLab.Visualization.Drawing;
9using HeuristicLab.Visualization.Legend;
10using HeuristicLab.Visualization.Options;
11using HeuristicLab.Visualization.Test;
12
13namespace HeuristicLab.Visualization {
14  public partial class LineChart : ViewBase {
15    private readonly IChartDataRowsModel model;
16    private readonly Canvas canvas;
17
18    private readonly TextShape titleShape = new TextShape("Title");
19    private readonly LegendShape legendShape = new LegendShape();
20    private readonly XAxis xAxis = new XAxis();
21    private readonly XAxisGrid xAxisGrid = new XAxisGrid();
22    private readonly List<RowEntry> rowEntries = new List<RowEntry>();
23
24    private readonly Dictionary<IDataRow, RowEntry> rowToRowEntry = new Dictionary<IDataRow, RowEntry>();
25
26    private readonly WorldShape userInteractionShape = new WorldShape();
27    private readonly RectangleShape rectangleShape = new RectangleShape(0, 0, 0, 0, Color.FromArgb(50, 0, 0, 255));
28    private IMouseEventListener mouseEventListener;
29
30    private const int YAxisWidth = 100;
31    private const int XAxisHeight = 40;
32    private readonly TooltipListener toolTipListener;
33    private readonly ToolTip valueToolTip;
34
35    /// <summary>
36    /// This constructor shouldn't be called. Only required for the designer.
37    /// </summary>
38    public LineChart() {
39      InitializeComponent();
40    }
41
42    /// <summary>
43    /// Initializes the chart.
44    /// </summary>
45    /// <param name="model">Referenz to the model, for data</param>
46    public LineChart(IChartDataRowsModel model) : this() {
47      if (model == null) {
48        throw new NullReferenceException("Model cannot be null.");
49      }
50
51      canvas = canvasUI.Canvas;
52
53      this.model = model;
54      this.model.ViewSettings.OnUpdateSettings += UpdateViewSettings;
55
56      Item = model;
57
58      valueToolTip = new ToolTip();
59      toolTipListener = new TooltipListener();
60      toolTipListener.ShowToolTip += ShowToolTip;
61      mouseEventListener = toolTipListener;
62
63      this.ResizeRedraw = true;
64
65      canvasUI.BeforePaint += delegate { UpdateLayout(); };
66
67      UpdateLayout();
68      ZoomToFullView();
69    }
70
71    public Bitmap Snapshot() {
72      UpdateLayout();
73      Bitmap bmp = new Bitmap(Width, Height);
74      using (Graphics g = Graphics.FromImage(bmp)) {
75        canvas.Draw(g);
76      }
77      return bmp;
78    }
79
80    /// <summary>
81    /// updates the view settings
82    /// </summary>
83    private void UpdateViewSettings() {
84      titleShape.Font = model.ViewSettings.TitleFont;
85      titleShape.Color = model.ViewSettings.TitleColor;
86      titleShape.Text = model.Title;
87
88      legendShape.Font = model.ViewSettings.LegendFont;
89      legendShape.Color = model.ViewSettings.LegendColor;
90
91      xAxis.Font = model.XAxis.Font;
92      xAxis.Color = model.XAxis.Color;
93
94      SetLegendPosition();
95
96      canvasUI.Invalidate();
97    }
98
99    /// <summary>
100    /// Layout management - arranges the inner shapes.
101    /// </summary>
102    private void UpdateLayout() {
103      canvas.ClearShapes();
104
105      titleShape.Text = model.Title;
106
107      if (model.XAxis.ShowGrid) {
108        xAxisGrid.Color = model.XAxis.GridColor;
109        canvas.AddShape(xAxisGrid);
110      }
111
112      foreach (YAxisDescriptor yAxisDescriptor in model.YAxes) {
113        YAxisInfo info = GetYAxisInfo(yAxisDescriptor);
114        if (yAxisDescriptor.ShowGrid) {
115          info.Grid.Color = yAxisDescriptor.GridColor;
116          canvas.AddShape(info.Grid);
117        }
118      }
119
120      foreach (RowEntry rowEntry in rowEntries) {
121        canvas.AddShape(rowEntry.LinesShape);
122      }
123
124      xAxis.ShowLabel = model.XAxis.ShowLabel;
125      xAxis.Label = model.XAxis.Label;
126
127      canvas.AddShape(xAxis);
128
129      int yAxesWidthLeft = 0;
130      int yAxesWidthRight = 0;
131
132      foreach (YAxisDescriptor yAxisDescriptor in model.YAxes) {
133        YAxisInfo info = GetYAxisInfo(yAxisDescriptor);
134        if (yAxisDescriptor.ShowYAxis) {
135          canvas.AddShape(info.YAxis);
136          info.YAxis.ShowLabel = yAxisDescriptor.ShowYAxisLabel;
137          info.YAxis.Label = yAxisDescriptor.Label;
138          info.YAxis.Position = yAxisDescriptor.Position;
139          switch (yAxisDescriptor.Position) {
140            case AxisPosition.Left:
141              yAxesWidthLeft += YAxisWidth;
142              break;
143            case AxisPosition.Right:
144              yAxesWidthRight += YAxisWidth;
145              break;
146            default:
147              throw new NotImplementedException();
148          }
149        }
150      }
151
152      canvas.AddShape(titleShape);
153      canvas.AddShape(legendShape);
154      canvas.AddShape(userInteractionShape);
155
156      titleShape.X = 10;
157      titleShape.Y = canvasUI.Height - 10;
158
159      RectangleD linesAreaBoundingBox = new RectangleD(yAxesWidthLeft,
160                                                       XAxisHeight,
161                                                       canvasUI.Width - yAxesWidthRight,
162                                                       canvasUI.Height);
163
164      foreach (RowEntry rowEntry in rowEntries) {
165        rowEntry.LinesShape.BoundingBox = linesAreaBoundingBox;
166      }
167
168      xAxisGrid.BoundingBox = linesAreaBoundingBox;
169
170      foreach (YAxisDescriptor yAxisDescriptor in model.YAxes) {
171        YAxisInfo info = GetYAxisInfo(yAxisDescriptor);
172        info.Grid.BoundingBox = linesAreaBoundingBox;
173      }
174
175      int yAxisLeft = 0;
176      int yAxisRight = (int) linesAreaBoundingBox.X2;
177
178      foreach (YAxisDescriptor yAxisDescriptor in model.YAxes) {
179        YAxisInfo info = GetYAxisInfo(yAxisDescriptor);
180        if (yAxisDescriptor.ShowYAxis) {
181          switch (yAxisDescriptor.Position) {
182            case AxisPosition.Left:
183              info.YAxis.BoundingBox = new RectangleD(yAxisLeft,
184                                                      linesAreaBoundingBox.Y1,
185                                                      yAxisLeft + YAxisWidth,
186                                                      linesAreaBoundingBox.Y2);
187              yAxisLeft += YAxisWidth;
188              break;
189            case AxisPosition.Right:
190              info.YAxis.BoundingBox = new RectangleD(yAxisRight,
191                                                      linesAreaBoundingBox.Y1,
192                                                      yAxisRight + YAxisWidth,
193                                                      linesAreaBoundingBox.Y2);
194              yAxisRight += YAxisWidth;
195              break;
196            default:
197              throw new NotImplementedException();
198          }
199        }
200      }
201
202      userInteractionShape.BoundingBox = linesAreaBoundingBox;
203      userInteractionShape.ClippingArea = new RectangleD(0, 0, userInteractionShape.BoundingBox.Width,
204                                                         userInteractionShape.BoundingBox.Height);
205
206      xAxis.BoundingBox = new RectangleD(linesAreaBoundingBox.X1,
207                                         0,
208                                         linesAreaBoundingBox.X2,
209                                         linesAreaBoundingBox.Y1);
210
211      SetLegendPosition();
212    }
213
214    private readonly Dictionary<YAxisDescriptor, YAxisInfo> yAxisInfos = new Dictionary<YAxisDescriptor, YAxisInfo>();
215
216    /// <summary>
217    ///
218    /// </summary>
219    /// <param name="yAxisDescriptor"></param>
220    /// <returns></returns>
221    private YAxisInfo GetYAxisInfo(YAxisDescriptor yAxisDescriptor) {
222      YAxisInfo info;
223
224      if (!yAxisInfos.TryGetValue(yAxisDescriptor, out info)) {
225        info = new YAxisInfo();
226        yAxisInfos[yAxisDescriptor] = info;
227      }
228
229      return info;
230    }
231
232
233    #region Legend-specific
234
235    /// <summary>
236    /// sets the legend position
237    /// </summary>
238    private void SetLegendPosition() {
239      switch (model.ViewSettings.LegendPosition) {
240        case LegendPosition.Bottom:
241          SetLegendBottom();
242          break;
243
244        case LegendPosition.Top:
245          SetLegendTop();
246          break;
247
248        case LegendPosition.Left:
249          SetLegendLeft();
250          break;
251
252        case LegendPosition.Right:
253          SetLegendRight();
254          break;
255      }
256    }
257
258    public void SetLegendRight() {
259      legendShape.BoundingBox = new RectangleD(canvasUI.Width - legendShape.GetMaxLabelLength(), 10, canvasUI.Width, canvasUI.Height - 50);
260      legendShape.ClippingArea = new RectangleD(0, 0, legendShape.BoundingBox.Width, legendShape.BoundingBox.Height);
261      legendShape.Row = false;
262      legendShape.CreateLegend();
263    }
264
265    public void SetLegendLeft() {
266      legendShape.BoundingBox = new RectangleD(10, 10, canvasUI.Width, canvasUI.Height - 50);
267      legendShape.ClippingArea = new RectangleD(0, 0, legendShape.BoundingBox.Width, legendShape.BoundingBox.Height);
268      legendShape.Row = false;
269      legendShape.CreateLegend();
270    }
271
272    public void SetLegendTop() {
273      legendShape.BoundingBox = new RectangleD(100, canvasUI.Height - canvasUI.Height, canvasUI.Width, canvasUI.Height - 10);
274      legendShape.ClippingArea = new RectangleD(0, 0, legendShape.BoundingBox.Width, legendShape.BoundingBox.Height);
275      legendShape.Row = true;
276      legendShape.Top = true;
277      legendShape.CreateLegend();
278    }
279
280    public void SetLegendBottom() {
281      legendShape.BoundingBox = new RectangleD(100, 2, canvasUI.Width, canvasUI.Height);
282      legendShape.ClippingArea = new RectangleD(0, 0, legendShape.BoundingBox.Width, legendShape.BoundingBox.Height);
283      legendShape.Row = true;
284      legendShape.Top = false;
285      legendShape.CreateLegend();
286    }
287
288    #endregion
289
290    /// <summary>
291    /// Shows the Tooltip with the real values of a datapoint, if the mousepoint is near to one
292    /// </summary>
293    /// <param name="location"></param>
294    private void ShowToolTip(Point location) {
295      valueToolTip.Hide(this);
296      if (rowEntries.Count > 0) {
297        double dx = Transform.ToWorldX(location.X, this.rowEntries[0].LinesShape.Viewport,
298                                       this.rowEntries[0].LinesShape.ClippingArea);
299        int ix = (int) Math.Round(dx);
300        foreach (var rowEntry in rowEntries) {
301          if ((rowEntry.DataRow.Count > ix) && (ix > 0) && ((rowEntry.DataRow.RowSettings.LineType == DataRowType.Normal) || (rowEntry.DataRow.RowSettings.LineType == DataRowType.Points))) {
302            Point screenDataP = Transform.ToScreen(new PointD(ix, rowEntry.DataRow[ix]), rowEntry.LinesShape.Viewport,
303                                                   rowEntry.LinesShape.ClippingArea);
304            if ((Math.Abs(screenDataP.X - location.X) <= 6) && (Math.Abs(screenDataP.Y - location.Y) <= 6)) {
305              valueToolTip.Show(("\t x:" + ix + " y:" + rowEntry.DataRow[ix]), this, screenDataP.X, screenDataP.Y);
306            }
307          }
308        }
309      }
310    }
311
312
313
314    private void optionsToolStripMenuItem_Click(object sender, EventArgs e) {
315      OptionsDialog optionsdlg = new OptionsDialog(model);
316      optionsdlg.Show();
317      mouseEventListener = toolTipListener;
318    }
319
320    private void exportToolStripMenuItem_Click(object sender, EventArgs e) {
321      ExportDialog exportdlg = new ExportDialog();
322      exportdlg.ShowDialog(this);
323
324      IExporter exporter = exportdlg.SelectedExporter;
325
326      if (exporter != null)
327        exporter.Export(model, this);
328    }
329
330    public void OnDataRowChanged(IDataRow row) {
331      RowEntry rowEntry = rowToRowEntry[row];
332
333      rowEntry.LinesShape.UpdateStyle(row);
334
335      canvasUI.Invalidate();
336    }
337
338    #region Add-/RemoveItemEvents
339
340    protected override void AddItemEvents() {
341      base.AddItemEvents();
342
343      model.DataRowAdded += OnDataRowAdded;
344      model.DataRowRemoved += OnDataRowRemoved;
345      model.ModelChanged += OnModelChanged;
346
347      foreach (IDataRow row in model.Rows) {
348        OnDataRowAdded(row);
349      }
350    }
351
352    protected override void RemoveItemEvents() {
353      base.RemoveItemEvents();
354
355      model.DataRowAdded -= OnDataRowAdded;
356      model.DataRowRemoved -= OnDataRowRemoved;
357      model.ModelChanged -= OnModelChanged;
358    }
359
360    private void OnDataRowAdded(IDataRow row) {
361      row.ValueChanged += OnRowValueChanged;
362      row.ValuesChanged += OnRowValuesChanged;
363      row.DataRowChanged += OnDataRowChanged;
364
365      legendShape.AddLegendItem(new LegendItem(row));
366      legendShape.CreateLegend();
367
368      InitLineShapes(row);
369
370      canvasUI.Invalidate();
371    }
372
373    private void OnDataRowRemoved(IDataRow row) {
374      row.ValueChanged -= OnRowValueChanged;
375      row.ValuesChanged -= OnRowValuesChanged;
376      row.DataRowChanged -= OnDataRowChanged;
377
378      rowToRowEntry.Remove(row);
379      rowEntries.RemoveAll(delegate(RowEntry rowEntry) { return rowEntry.DataRow == row; });
380
381      canvasUI.Invalidate();
382    }
383
384    #endregion
385
386    public void ZoomToFullView() {
387      double xmin, xmax;
388      GetClippingRange(0, model.MaxDataRowValues-1, out xmin, out xmax);
389      SetClipX(xmin, xmax);
390
391      foreach (RowEntry rowEntry in rowEntries) {
392        YAxisDescriptor yAxis = rowEntry.DataRow.YAxis;
393
394        double ymin, ymax;
395        GetClippingRange(yAxis.MinValue, yAxis.MaxValue, out ymin, out ymax);
396        SetClipY(rowEntry, ymin, ymax);
397      }
398
399      canvasUI.Invalidate();
400    }
401
402    /// <summary>
403    /// Calculates the required clipping range such that the specified min/max values
404    /// visible including a small padding.
405    /// </summary>
406    /// <param name="minValue"></param>
407    /// <param name="maxValue"></param>
408    /// <param name="clipFrom"></param>
409    /// <param name="clipTo"></param>
410    private static void GetClippingRange(double minValue, double maxValue, out double clipFrom, out double clipTo) {
411      if (minValue == double.MaxValue || maxValue == double.MinValue) {
412        clipFrom = -0.1;
413        clipTo = 1.1;
414      } else {
415        double padding = (maxValue - minValue)*0.05;
416        clipFrom = minValue - padding;
417        clipTo = maxValue + padding;
418
419        if (Math.Abs(clipTo - clipFrom) < double.Epsilon * 5) {
420          clipFrom -= 0.1;
421          clipTo += 0.1;
422        }
423      }
424    }
425
426    private void SetClipX(double x1, double x2) {
427      xAxisGrid.ClippingArea = new RectangleD(x1,
428                                              xAxisGrid.ClippingArea.Y1,
429                                              x2,
430                                              xAxisGrid.ClippingArea.Y2);
431
432      xAxis.ClippingArea = new RectangleD(x1,
433                                          0,
434                                          x2,
435                                          XAxisHeight);
436
437      foreach (RowEntry rowEntry in rowEntries) {
438        rowEntry.LinesShape.ClippingArea = new RectangleD(x1,
439                                                          rowEntry.LinesShape.ClippingArea.Y1,
440                                                          x2,
441                                                          rowEntry.LinesShape.ClippingArea.Y2);
442      }
443
444      foreach (YAxisDescriptor yAxisDescriptor in model.YAxes) {
445        YAxisInfo info = GetYAxisInfo(yAxisDescriptor);
446        info.Grid.ClippingArea = new RectangleD(x1,
447                                                info.Grid.ClippingArea.Y1,
448                                                x2,
449                                                info.Grid.ClippingArea.Y2);
450        info.YAxis.ClippingArea = new RectangleD(0,
451                                                 info.YAxis.ClippingArea.Y1,
452                                                 YAxisWidth,
453                                                 info.YAxis.ClippingArea.Y2);
454      }
455    }
456
457    private void SetClipY(RowEntry rowEntry, double y1, double y2) {
458      xAxisGrid.ClippingArea = new RectangleD(xAxisGrid.ClippingArea.X1,
459                                              y1,
460                                              xAxisGrid.ClippingArea.X2,
461                                              y2);
462
463      rowEntry.LinesShape.ClippingArea = new RectangleD(rowEntry.LinesShape.ClippingArea.X1,
464                                                        y1,
465                                                        rowEntry.LinesShape.ClippingArea.X2,
466                                                        y2);
467
468      YAxisInfo info = GetYAxisInfo(rowEntry.DataRow.YAxis);
469
470      info.Grid.ClippingArea = new RectangleD(info.Grid.ClippingArea.X1,
471                                              y1,
472                                              info.Grid.ClippingArea.X2,
473                                              y2);
474      info.YAxis.ClippingArea = new RectangleD(info.YAxis.ClippingArea.X1,
475                                               y1,
476                                               info.YAxis.ClippingArea.X2,
477                                               y2);
478    }
479
480    /// <summary>
481    /// Creates the shapes for the data of the given row and stores them.
482    /// </summary>
483    /// <param name="row">Datarow, whose data items should be converted to shapes</param>
484    private void InitLineShapes(IDataRow row) {
485      RowEntry rowEntry = new RowEntry(row);
486      rowEntries.Add(rowEntry);
487      rowToRowEntry[row] = rowEntry;
488
489      if ((row.RowSettings.LineType == DataRowType.SingleValue)) {
490        if (row.Count > 0) {
491          LineShape lineShape = new HorizontalLineShape(0, row[0], double.MaxValue, row[0], row.RowSettings.Color, row.RowSettings.Thickness,
492                                                        row.RowSettings.Style);
493          rowEntry.LinesShape.AddShape(lineShape);
494        }
495      } else if (row.RowSettings.LineType == DataRowType.Points) {
496        rowEntry.ShowMarkers(true);      //no lines, only markers are shown!!
497        for (int i = 0; i < row.Count; i++)
498          rowEntry.LinesShape.AddMarkerShape(new MarkerShape(i, row[i], 8, row.RowSettings.Color));
499      } else if (row.RowSettings.LineType == DataRowType.Normal) {
500        rowEntry.ShowMarkers(row.RowSettings.ShowMarkers);
501        for (int i = 1; i < row.Count; i++) {
502          LineShape lineShape = new LineShape(i - 1, row[i - 1], i, row[i], row.RowSettings.Color, row.RowSettings.Thickness, row.RowSettings.Style);
503          rowEntry.LinesShape.AddShape(lineShape);
504          rowEntry.LinesShape.AddMarkerShape(new MarkerShape(i - 1, row[i - 1], 8, row.RowSettings.Color));
505        }
506        if (row.Count > 0) {
507          rowEntry.LinesShape.AddMarkerShape(new MarkerShape((row.Count - 1), row[(row.Count - 1)], 8, row.RowSettings.Color));
508        }
509      }
510
511      ZoomToFullView();
512    }
513
514    /// <summary>
515    /// Handles the event, when a value of a datarow was changed
516    /// </summary>
517    /// <param name="row">row in which the data was changed</param>
518    /// <param name="value">new value of the data point</param>
519    /// <param name="index">index in the datarow of the changed datapoint</param>
520    /// <param name="action">the performed action (added, modified, deleted)</param>
521    private void OnRowValueChanged(IDataRow row, double value, int index, Action action) {
522      RowEntry rowEntry = rowToRowEntry[row];
523
524      if (row.RowSettings.LineType == DataRowType.SingleValue) {
525        if (action == Action.Added) {
526          LineShape lineShape = new HorizontalLineShape(0, row[0], double.MaxValue, row[0], row.RowSettings.Color, row.RowSettings.Thickness,
527                                                        row.RowSettings.Style);
528          rowEntry.LinesShape.AddShape(lineShape);
529        } else if(action==Action.Deleted) {
530          throw new ArgumentException("It is unwise to delete the only value of the SinglevalueRow!!");
531        }else if(action ==Action.Modified){
532          LineShape lineShape = rowEntry.LinesShape.GetShape(0);
533          lineShape.Y1 = value;
534          lineShape.Y2 = value;
535        }
536      } else if (row.RowSettings.LineType == DataRowType.Points) {
537        if (action == Action.Added) {
538          if(rowEntry.LinesShape.Count==0)
539            rowEntry.LinesShape.AddMarkerShape(new MarkerShape(0, row[0], 8, row.RowSettings.Color));
540          if (index > 0 && index == rowEntry.LinesShape.Count + 1) {
541            LineShape lineShape = new LineShape(index - 1, row[index - 1], index, row[index], row.RowSettings.Color, row.RowSettings.Thickness,
542                                                row.RowSettings.Style);
543            rowEntry.LinesShape.AddShape(lineShape);
544            rowEntry.LinesShape.AddMarkerShape(new MarkerShape(index, row[index], 8, row.RowSettings.Color));
545          } else {
546            throw new ArgumentException("Adding a value is only possible at the end of a row!");
547          }
548        } else if (action == Action.Modified) {
549          // not the first value
550          if (index > 0) {
551            rowEntry.LinesShape.GetShape(index - 1).Y2 = value;
552            ((MarkerShape) rowEntry.LinesShape.markersShape.GetShape(index - 1)).Y = value;
553          }
554
555          // not the last value
556          if (index < row.Count - 1) {
557            rowEntry.LinesShape.GetShape(index).Y1 = value;
558            ((MarkerShape) rowEntry.LinesShape.markersShape.GetShape(index)).Y = value;
559          }
560        } else if(action == Action.Deleted) {
561          if (index == row.Count - 1)
562            rowEntry.LinesShape.RemoveMarkerShape(rowEntry.LinesShape.markersShape.GetShape(index));
563          else
564            throw new NotSupportedException("Deleting of values other than the last one is not supported!");
565        }
566
567      } else if (row.RowSettings.LineType == DataRowType.Normal) {
568        if (index > rowEntry.LinesShape.Count + 1) {
569          throw new NotImplementedException();
570        }
571
572        if (action == Action.Added) {
573          if (rowEntry.LinesShape.Count == 0)
574            rowEntry.LinesShape.AddMarkerShape(new MarkerShape(0, row[0], 8, row.RowSettings.Color));
575          if (index > 0 && index == rowEntry.LinesShape.Count + 1) {
576            LineShape lineShape = new LineShape(index - 1, row[index - 1], index, row[index], row.RowSettings.Color, row.RowSettings.Thickness,
577                                                row.RowSettings.Style);
578            rowEntry.LinesShape.AddShape(lineShape);
579            rowEntry.LinesShape.AddMarkerShape(new MarkerShape(index, row[index], 8, row.RowSettings.Color));
580          }
581        } else if (action == Action.Modified) {
582          // not the first value
583          if (index > 0) {
584            rowEntry.LinesShape.GetShape(index - 1).Y2 = value;
585            ((MarkerShape) rowEntry.LinesShape.markersShape.GetShape(index - 1)).Y = value;
586          }
587
588          // not the last value
589          if (index < row.Count - 1) {
590            rowEntry.LinesShape.GetShape(index).Y1 = value;
591            ((MarkerShape) rowEntry.LinesShape.markersShape.GetShape(index)).Y = value;
592          }
593        } else if (action == Action.Deleted) {
594          if (index == row.Count - 1) {
595            rowEntry.LinesShape.RemoveMarkerShape(rowEntry.LinesShape.markersShape.GetShape(index));
596            rowEntry.LinesShape.RemoveShape(rowEntry.LinesShape.GetShape(index));
597          } else
598            throw new NotSupportedException("Deleting of values other than the last one is not supported!");
599        }
600      }
601
602      ZoomToFullView();
603    }
604
605    private void OnRowValuesChanged(IDataRow row, double[] values, int index, Action action) {
606      foreach (double value in values) {
607        OnRowValueChanged(row, value, index++, action);
608      }
609    }
610
611    private void OnModelChanged() {
612      canvasUI.Invalidate();
613    }
614
615    #region Begin-/EndUpdate
616
617    private int beginUpdateCount;
618
619    public void BeginUpdate() {
620      beginUpdateCount++;
621    }
622
623    public void EndUpdate() {
624      if (beginUpdateCount == 0) {
625        throw new InvalidOperationException("Too many EndUpdates.");
626      }
627
628      beginUpdateCount--;
629
630      if (beginUpdateCount == 0) {
631        canvasUI.Invalidate();
632      }
633    }
634
635    #endregion
636
637    #region Zooming / Panning
638
639    private void canvasUI1_MouseDown(object sender, MouseEventArgs e) {
640      Focus();
641
642      if (e.Button == MouseButtons.Right) {
643        valueToolTip.Hide(this);
644        mouseEventListener = null;
645        contextMenu.Show(PointToScreen(e.Location));
646      } else if (e.Button == MouseButtons.Left) {
647        if (ModifierKeys == Keys.None) {
648          PanListener panListener = new PanListener(e.Location);
649          panListener.Pan += Pan;
650
651          mouseEventListener = panListener;
652        } else if (ModifierKeys == Keys.Control) {
653          ZoomListener zoomListener = new ZoomListener(e.Location);
654          zoomListener.DrawRectangle += DrawRectangle;
655          zoomListener.Zoom += Zoom;
656
657          rectangleShape.Rectangle = RectangleD.Empty;
658          userInteractionShape.AddShape(rectangleShape);
659
660          mouseEventListener = zoomListener;
661        }
662      }
663    }
664
665    private void canvasUI_MouseMove(object sender, MouseEventArgs e) {
666      if (mouseEventListener != null) {
667        mouseEventListener.MouseMove(sender, e);
668      }
669    }
670
671    private void canvasUI_MouseUp(object sender, MouseEventArgs e) {
672      if (mouseEventListener != null) {
673        mouseEventListener.MouseUp(sender, e);
674      }
675
676      mouseEventListener = toolTipListener;
677    }
678
679    private void canvasUI1_MouseWheel(object sender, MouseEventArgs e) {
680      if (ModifierKeys == Keys.Control) {
681        double zoomFactor = (e.Delta > 0) ? 0.7 : 1.3;
682
683        PointD world = Transform.ToWorld(e.Location, xAxis.Viewport, xAxis.ClippingArea);
684
685        double x1 = world.X - (world.X - xAxis.ClippingArea.X1) * zoomFactor;
686        double x2 = world.X + (xAxis.ClippingArea.X2 - world.X) * zoomFactor;
687
688        SetClipX(x1, x2);
689
690        foreach (RowEntry rowEntry in rowEntries) {
691          world = Transform.ToWorld(e.Location, rowEntry.LinesShape.Viewport, rowEntry.LinesShape.ClippingArea);
692
693          double y1 = world.Y - (world.Y - rowEntry.LinesShape.ClippingArea.Y1) * zoomFactor;
694          double y2 = world.Y + (rowEntry.LinesShape.ClippingArea.Y2 - world.Y) * zoomFactor;
695
696          SetClipY(rowEntry, y1, y2);
697        }
698
699        canvasUI.Invalidate();
700      }
701    }
702
703    private void Pan(Point startPoint, Point endPoint) {
704      RectangleD clippingArea = Translate.ClippingArea(startPoint, endPoint, xAxis.ClippingArea, xAxis.Viewport);
705
706      SetClipX(clippingArea.X1, clippingArea.X2);
707
708      foreach (RowEntry rowEntry in rowEntries) {
709        if (rowEntry.DataRow.YAxis.ClipChangeable) {
710          clippingArea = Translate.ClippingArea(startPoint, endPoint, rowEntry.LinesShape.ClippingArea,
711                                                rowEntry.LinesShape.Viewport);
712          SetClipY(rowEntry, clippingArea.Y1, clippingArea.Y2);
713        }
714      }
715
716      canvasUI.Invalidate();
717    }
718
719    private void DrawRectangle(Rectangle rectangle) {
720      rectangleShape.Rectangle = Transform.ToWorld(rectangle, userInteractionShape.Viewport,
721                                                   userInteractionShape.ClippingArea);
722      canvasUI.Invalidate();
723    }
724
725    private void Zoom(Rectangle rectangle) {
726      RectangleD clippingArea = Transform.ToWorld(rectangle, xAxis.Viewport, xAxis.ClippingArea);
727
728      SetClipX(clippingArea.X1, clippingArea.X2);
729
730      foreach (RowEntry rowEntry in rowEntries) {
731        if (rowEntry.DataRow.YAxis.ClipChangeable) {
732          clippingArea = Transform.ToWorld(rectangle, rowEntry.LinesShape.Viewport, rowEntry.LinesShape.ClippingArea);
733
734          SetClipY(rowEntry, clippingArea.Y1, clippingArea.Y2);
735        }
736      }
737
738      userInteractionShape.RemoveShape(rectangleShape);
739      canvasUI.Invalidate();
740    }
741
742    #endregion
743
744    protected override void OnPaint(PaintEventArgs e) {
745      UpdateLayout();
746      base.OnPaint(e);
747    }
748
749    private class LinesShape : WorldShape {
750      public readonly CompositeShape markersShape = new CompositeShape();
751
752      public void UpdateStyle(IDataRow row) {
753        foreach (IShape shape in shapes) {
754          LineShape lineShape = shape as LineShape;
755          if (lineShape != null) {
756            lineShape.LSColor = row.RowSettings.Color;
757            lineShape.LSDrawingStyle = row.RowSettings.Style;
758            lineShape.LSThickness = row.RowSettings.Thickness;
759          }
760        }
761        markersShape.ShowChildShapes = row.RowSettings.ShowMarkers;
762      }
763
764      /// <summary>
765      /// Draws all Shapes in the chart
766      /// </summary>
767      /// <param name="graphics"></param>
768      public override void Draw(Graphics graphics) {
769        GraphicsState gstate = graphics.Save();
770
771        graphics.SetClip(Viewport);
772        foreach (IShape shape in shapes) {
773          // draw child shapes using our own clipping area
774          shape.Draw(graphics);
775        }
776        markersShape.Draw(graphics);
777        graphics.Restore(gstate);
778      }
779
780      public void AddMarkerShape(IShape shape) {
781        shape.Parent = this;
782        markersShape.AddShape(shape);
783      }
784
785      public void RemoveMarkerShape(IShape shape) {
786        shape.Parent = this;
787        markersShape.RemoveShape(shape);
788      }
789
790      public int Count {
791        get { return shapes.Count; }
792      }
793
794      public LineShape GetShape(int index) {
795        return (LineShape) shapes[index]; //shapes[0] is markersShape!!
796      }
797    }
798
799    private class RowEntry {
800      private readonly IDataRow dataRow;
801
802      private readonly LinesShape linesShape = new LinesShape();
803
804      public RowEntry(IDataRow dataRow) {
805        this.dataRow = dataRow;
806        linesShape.markersShape.Parent = linesShape;
807      }
808
809      public IDataRow DataRow {
810        get { return dataRow; }
811      }
812
813      public LinesShape LinesShape {
814        get { return linesShape; }
815      }
816
817      public void ShowMarkers(bool flag) {
818        linesShape.markersShape.ShowChildShapes = flag;
819      }
820    }
821
822    private class YAxisInfo {
823      private readonly YAxisGrid grid = new YAxisGrid();
824      private readonly YAxis yAxis = new YAxis();
825
826      public YAxisGrid Grid {
827        get { return grid; }
828      }
829
830      public YAxis YAxis {
831        get { return yAxis; }
832      }
833    }
834  }
835}
Note: See TracBrowser for help on using the repository browser.