Free cookie consent management tool by TermsFeed Policy Generator

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

Last change on this file since 1993 was 1993, checked in by mstoeger, 15 years ago

added many new persisted properties.
removed useless comments.
added XmlSupport class since the code for setting xml attributes is always the same.
#639

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