Free cookie consent management tool by TermsFeed Policy Generator

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

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

more "zoom level too high" fixes. the error also occured for empty charts and for empty data rows. #498

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