Free cookie consent management tool by TermsFeed Policy Generator

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

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

Refactoring PanListener(#664)

File size: 30.2 KB
RevLine 
[683]1using System;
[861]2using System.Collections.Generic;
[928]3using System.Drawing;
[1559]4using System.Drawing.Drawing2D;
[928]5using System.Windows.Forms;
[697]6using HeuristicLab.Core;
[1781]7using HeuristicLab.Visualization.DataExport;
[1964]8using HeuristicLab.Visualization.Drawing;
[1233]9using HeuristicLab.Visualization.Legend;
[1195]10using HeuristicLab.Visualization.Options;
[1457]11using HeuristicLab.Visualization.Test;
[683]12
[861]13namespace HeuristicLab.Visualization {
[1187]14  public partial class LineChart : ViewBase {
[697]15    private readonly IChartDataRowsModel model;
[1240]16    private readonly Canvas canvas;
17
[1285]18    private readonly TextShape titleShape = new TextShape("Title");
19    private readonly LegendShape legendShape = new LegendShape();
20    private readonly XAxis xAxis = new XAxis();
[1876]21    private readonly XAxisGrid xAxisGrid = new XAxisGrid();
[1285]22    private readonly List<RowEntry> rowEntries = new List<RowEntry>();
[684]23
[1285]24    private readonly Dictionary<IDataRow, RowEntry> rowToRowEntry = new Dictionary<IDataRow, RowEntry>();
[1038]25
[1285]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;
[983]29
[1285]30    private const int YAxisWidth = 100;
[1462]31    private const int XAxisHeight = 40;
[1883]32    private readonly TooltipListener toolTipListener;
33    private readonly ToolTip valueToolTip;
[1285]34
[697]35    /// <summary>
36    /// This constructor shouldn't be called. Only required for the designer.
37    /// </summary>
[861]38    public LineChart() {
[684]39      InitializeComponent();
40    }
41
[697]42    /// <summary>
43    /// Initializes the chart.
44    /// </summary>
[754]45    /// <param name="model">Referenz to the model, for data</param>
[861]46    public LineChart(IChartDataRowsModel model) : this() {
[1045]47      if (model == null) {
[697]48        throw new NullReferenceException("Model cannot be null.");
[1045]49      }
[684]50
[1240]51      canvas = canvasUI.Canvas;
[983]52
[1285]53      this.model = model;
[1993]54      this.model.ViewSettings.OnUpdateSettings += UpdateViewSettings;
[1038]55
[983]56      Item = model;
[1038]57
[1883]58      valueToolTip = new ToolTip();
59      toolTipListener = new TooltipListener();
60      toolTipListener.ShowToolTip += ShowToolTip;
61      mouseEventListener = toolTipListener;
62
[1879]63      this.ResizeRedraw = true;
64
[1880]65      canvasUI.BeforePaint += delegate { UpdateLayout(); };
66
[1240]67      UpdateLayout();
[1285]68      ZoomToFullView();
[697]69    }
[684]70
[1979]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
[1346]80    /// <summary>
81    /// updates the view settings
82    /// </summary>
[1342]83    private void UpdateViewSettings() {
[1993]84      titleShape.Font = model.ViewSettings.TitleFont;
85      titleShape.Color = model.ViewSettings.TitleColor;
[1839]86      titleShape.Text = model.Title;
[1337]87
[1993]88      legendShape.Font = model.ViewSettings.LegendFont;
89      legendShape.Color = model.ViewSettings.LegendColor;
[1337]90
[1993]91      xAxis.Font = model.XAxis.Font;
92      xAxis.Color = model.XAxis.Color;
[1337]93
[1345]94      SetLegendPosition();
[1342]95
[1880]96      canvasUI.Invalidate();
[1337]97    }
98
[1038]99    /// <summary>
100    /// Layout management - arranges the inner shapes.
101    /// </summary>
102    private void UpdateLayout() {
[1285]103      canvas.ClearShapes();
104
[1608]105      titleShape.Text = model.Title;
106
[1881]107      if (model.XAxis.ShowGrid) {
[1885]108        xAxisGrid.Color = model.XAxis.GridColor;
[1876]109        canvas.AddShape(xAxisGrid);
110      }
111
[1350]112      foreach (YAxisDescriptor yAxisDescriptor in model.YAxes) {
113        YAxisInfo info = GetYAxisInfo(yAxisDescriptor);
[1458]114        if (yAxisDescriptor.ShowGrid) {
115          info.Grid.Color = yAxisDescriptor.GridColor;
116          canvas.AddShape(info.Grid);
117        }
[1285]118      }
119
120      foreach (RowEntry rowEntry in rowEntries) {
121        canvas.AddShape(rowEntry.LinesShape);
122      }
123
[1881]124      xAxis.ShowLabel = model.XAxis.ShowLabel;
125      xAxis.Label = model.XAxis.Label;
[1462]126
[1285]127      canvas.AddShape(xAxis);
128
[1457]129      int yAxesWidthLeft = 0;
130      int yAxesWidthRight = 0;
[1343]131
[1350]132      foreach (YAxisDescriptor yAxisDescriptor in model.YAxes) {
133        YAxisInfo info = GetYAxisInfo(yAxisDescriptor);
134        if (yAxisDescriptor.ShowYAxis) {
135          canvas.AddShape(info.YAxis);
[1462]136          info.YAxis.ShowLabel = yAxisDescriptor.ShowYAxisLabel;
137          info.YAxis.Label = yAxisDescriptor.Label;
[1457]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          }
[1343]149        }
[1285]150      }
151
152      canvas.AddShape(titleShape);
153      canvas.AddShape(legendShape);
154      canvas.AddShape(userInteractionShape);
155
[1038]156      titleShape.X = 10;
[1240]157      titleShape.Y = canvasUI.Height - 10;
[1038]158
[1457]159      RectangleD linesAreaBoundingBox = new RectangleD(yAxesWidthLeft,
[1285]160                                                       XAxisHeight,
[1457]161                                                       canvasUI.Width - yAxesWidthRight,
[1285]162                                                       canvasUI.Height);
[1240]163
[1285]164      foreach (RowEntry rowEntry in rowEntries) {
165        rowEntry.LinesShape.BoundingBox = linesAreaBoundingBox;
166      }
[1182]167
[1876]168      xAxisGrid.BoundingBox = linesAreaBoundingBox;
169
[1350]170      foreach (YAxisDescriptor yAxisDescriptor in model.YAxes) {
171        YAxisInfo info = GetYAxisInfo(yAxisDescriptor);
172        info.Grid.BoundingBox = linesAreaBoundingBox;
173      }
174
[1285]175      int yAxisLeft = 0;
[1883]176      int yAxisRight = (int) linesAreaBoundingBox.X2;
[1457]177
[1350]178      foreach (YAxisDescriptor yAxisDescriptor in model.YAxes) {
179        YAxisInfo info = GetYAxisInfo(yAxisDescriptor);
180        if (yAxisDescriptor.ShowYAxis) {
[1457]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          }
[1343]199        }
[1285]200      }
[1187]201
[1285]202      userInteractionShape.BoundingBox = linesAreaBoundingBox;
[1883]203      userInteractionShape.ClippingArea = new RectangleD(0, 0, userInteractionShape.BoundingBox.Width,
204                                                         userInteractionShape.BoundingBox.Height);
[1182]205
[1285]206      xAxis.BoundingBox = new RectangleD(linesAreaBoundingBox.X1,
[1038]207                                         0,
[1285]208                                         linesAreaBoundingBox.X2,
209                                         linesAreaBoundingBox.Y1);
[1049]210
[1345]211      SetLegendPosition();
[1342]212    }
213
[1350]214    private readonly Dictionary<YAxisDescriptor, YAxisInfo> yAxisInfos = new Dictionary<YAxisDescriptor, YAxisInfo>();
215
[1883]216    /// <summary>
217    ///
218    /// </summary>
219    /// <param name="yAxisDescriptor"></param>
220    /// <returns></returns>
[1350]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
[1883]232
233    #region Legend-specific
234
[1346]235    /// <summary>
236    /// sets the legend position
237    /// </summary>
[1345]238    private void SetLegendPosition() {
[1993]239      switch (model.ViewSettings.LegendPosition) {
[1345]240        case LegendPosition.Bottom:
[1993]241          SetLegendBottom();
[1345]242          break;
243
244        case LegendPosition.Top:
[1993]245          SetLegendTop();
[1345]246          break;
247
248        case LegendPosition.Left:
[1993]249          SetLegendLeft();
[1345]250          break;
251
252        case LegendPosition.Right:
[1993]253          SetLegendRight();
[1345]254          break;
255      }
256    }
257
[1993]258    public void SetLegendRight() {
259      legendShape.BoundingBox = new RectangleD(canvasUI.Width - legendShape.GetMaxLabelLength(), 10, canvasUI.Width, canvasUI.Height - 50);
[1342]260      legendShape.ClippingArea = new RectangleD(0, 0, legendShape.BoundingBox.Width, legendShape.BoundingBox.Height);
261      legendShape.Row = false;
262      legendShape.CreateLegend();
263    }
264
[1993]265    public void SetLegendLeft() {
[1388]266      legendShape.BoundingBox = new RectangleD(10, 10, canvasUI.Width, canvasUI.Height - 50);
[1342]267      legendShape.ClippingArea = new RectangleD(0, 0, legendShape.BoundingBox.Width, legendShape.BoundingBox.Height);
268      legendShape.Row = false;
269      legendShape.CreateLegend();
[1038]270    }
271
[1993]272    public void SetLegendTop() {
273      legendShape.BoundingBox = new RectangleD(100, canvasUI.Height - canvasUI.Height, canvasUI.Width, canvasUI.Height - 10);
[1342]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
[1993]280    public void SetLegendBottom() {
[1878]281      legendShape.BoundingBox = new RectangleD(100, 2, canvasUI.Width, canvasUI.Height);
[1342]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
[1883]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) {
[1974]301          if ((rowEntry.DataRow.Count > ix) && (ix > 0) && ((rowEntry.DataRow.RowSettings.LineType == DataRowType.Normal) || (rowEntry.DataRow.RowSettings.LineType == DataRowType.Points))) {
[1883]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
[1242]314    private void optionsToolStripMenuItem_Click(object sender, EventArgs e) {
[1341]315      OptionsDialog optionsdlg = new OptionsDialog(model);
[1459]316      optionsdlg.Show();
[1883]317      mouseEventListener = toolTipListener;
[1242]318    }
319
[1781]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)
[1980]327        exporter.Export(model, this);
[1781]328    }
329
[1242]330    public void OnDataRowChanged(IDataRow row) {
[1285]331      RowEntry rowEntry = rowToRowEntry[row];
332
333      rowEntry.LinesShape.UpdateStyle(row);
334
[1880]335      canvasUI.Invalidate();
[1242]336    }
337
[861]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;
[869]346
[1045]347      foreach (IDataRow row in model.Rows) {
[869]348        OnDataRowAdded(row);
[1045]349      }
[683]350    }
[684]351
[861]352    protected override void RemoveItemEvents() {
353      base.RemoveItemEvents();
354
355      model.DataRowAdded -= OnDataRowAdded;
356      model.DataRowRemoved -= OnDataRowRemoved;
357      model.ModelChanged -= OnModelChanged;
[697]358    }
359
[861]360    private void OnDataRowAdded(IDataRow row) {
361      row.ValueChanged += OnRowValueChanged;
362      row.ValuesChanged += OnRowValuesChanged;
[1237]363      row.DataRowChanged += OnDataRowChanged;
364
[1977]365      legendShape.AddLegendItem(new LegendItem(row));
[1049]366      legendShape.CreateLegend();
[1285]367
[987]368      InitLineShapes(row);
[1285]369
[1880]370      canvasUI.Invalidate();
[684]371    }
[697]372
[1240]373    private void OnDataRowRemoved(IDataRow row) {
374      row.ValueChanged -= OnRowValueChanged;
375      row.ValuesChanged -= OnRowValuesChanged;
376      row.DataRowChanged -= OnDataRowChanged;
[1285]377
378      rowToRowEntry.Remove(row);
379      rowEntries.RemoveAll(delegate(RowEntry rowEntry) { return rowEntry.DataRow == row; });
380
[1880]381      canvasUI.Invalidate();
[1240]382    }
383
384    #endregion
385
[1249]386    public void ZoomToFullView() {
[1987]387      double xmin, xmax;
388      GetClippingRange(0, model.MaxDataRowValues-1, out xmin, out xmax);
389      SetClipX(xmin, xmax);
[987]390
[1285]391      foreach (RowEntry rowEntry in rowEntries) {
[1979]392        YAxisDescriptor yAxis = rowEntry.DataRow.YAxis;
[1249]393
[1987]394        double ymin, ymax;
395        GetClippingRange(yAxis.MinValue, yAxis.MaxValue, out ymin, out ymax);
[1979]396        SetClipY(rowEntry, ymin, ymax);
[1285]397      }
398
[1880]399      canvasUI.Invalidate();
[987]400    }
401
[1987]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
[1285]426    private void SetClipX(double x1, double x2) {
[1876]427      xAxisGrid.ClippingArea = new RectangleD(x1,
428                                              xAxisGrid.ClippingArea.Y1,
429                                              x2,
430                                              xAxisGrid.ClippingArea.Y2);
431
[1285]432      xAxis.ClippingArea = new RectangleD(x1,
433                                          0,
434                                          x2,
435                                          XAxisHeight);
[1249]436
[1285]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);
[1249]442      }
[1350]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      }
[1285]455    }
[1249]456
[1350]457    private void SetClipY(RowEntry rowEntry, double y1, double y2) {
[1876]458      xAxisGrid.ClippingArea = new RectangleD(xAxisGrid.ClippingArea.X1,
459                                              y1,
460                                              xAxisGrid.ClippingArea.X2,
461                                              y2);
462
[1285]463      rowEntry.LinesShape.ClippingArea = new RectangleD(rowEntry.LinesShape.ClippingArea.X1,
464                                                        y1,
465                                                        rowEntry.LinesShape.ClippingArea.X2,
466                                                        y2);
[1350]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);
[981]478    }
479
[1883]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>
[987]484    private void InitLineShapes(IDataRow row) {
[1285]485      RowEntry rowEntry = new RowEntry(row);
486      rowEntries.Add(rowEntry);
487      rowToRowEntry[row] = rowEntry;
488
[1974]489      if ((row.RowSettings.LineType == DataRowType.SingleValue)) {
[1242]490        if (row.Count > 0) {
[1962]491          LineShape lineShape = new HorizontalLineShape(0, row[0], double.MaxValue, row[0], row.RowSettings.Color, row.RowSettings.Thickness,
[1974]492                                                        row.RowSettings.Style);
[1285]493          rowEntry.LinesShape.AddShape(lineShape);
[1242]494        }
[1974]495      } else if (row.RowSettings.LineType == DataRowType.Points) {
[1965]496        rowEntry.ShowMarkers(true);      //no lines, only markers are shown!!
[1883]497        for (int i = 0; i < row.Count; i++)
[1962]498          rowEntry.LinesShape.AddMarkerShape(new MarkerShape(i, row[i], 8, row.RowSettings.Color));
[1974]499      } else if (row.RowSettings.LineType == DataRowType.Normal) {
500        rowEntry.ShowMarkers(row.RowSettings.ShowMarkers);
[1242]501        for (int i = 1; i < row.Count; i++) {
[1974]502          LineShape lineShape = new LineShape(i - 1, row[i - 1], i, row[i], row.RowSettings.Color, row.RowSettings.Thickness, row.RowSettings.Style);
[1285]503          rowEntry.LinesShape.AddShape(lineShape);
[1962]504          rowEntry.LinesShape.AddMarkerShape(new MarkerShape(i - 1, row[i - 1], 8, row.RowSettings.Color));
[1242]505        }
[1608]506        if (row.Count > 0) {
[1962]507          rowEntry.LinesShape.AddMarkerShape(new MarkerShape((row.Count - 1), row[(row.Count - 1)], 8, row.RowSettings.Color));
[1608]508        }
[1242]509      }
[1249]510
[981]511      ZoomToFullView();
[697]512    }
513
[1883]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>
[869]521    private void OnRowValueChanged(IDataRow row, double value, int index, Action action) {
[1285]522      RowEntry rowEntry = rowToRowEntry[row];
523
[1974]524      if (row.RowSettings.LineType == DataRowType.SingleValue) {
[1242]525        if (action == Action.Added) {
[1962]526          LineShape lineShape = new HorizontalLineShape(0, row[0], double.MaxValue, row[0], row.RowSettings.Color, row.RowSettings.Thickness,
[1974]527                                                        row.RowSettings.Style);
[1285]528          rowEntry.LinesShape.AddShape(lineShape);
[1883]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){
[1285]532          LineShape lineShape = rowEntry.LinesShape.GetShape(0);
533          lineShape.Y1 = value;
534          lineShape.Y2 = value;
[1242]535        }
[1974]536      } else if (row.RowSettings.LineType == DataRowType.Points) {
[1883]537        if (action == Action.Added) {
538          if(rowEntry.LinesShape.Count==0)
[1962]539            rowEntry.LinesShape.AddMarkerShape(new MarkerShape(0, row[0], 8, row.RowSettings.Color));
[1883]540          if (index > 0 && index == rowEntry.LinesShape.Count + 1) {
[1962]541            LineShape lineShape = new LineShape(index - 1, row[index - 1], index, row[index], row.RowSettings.Color, row.RowSettings.Thickness,
[1974]542                                                row.RowSettings.Style);
[1883]543            rowEntry.LinesShape.AddShape(lineShape);
[1962]544            rowEntry.LinesShape.AddMarkerShape(new MarkerShape(index, row[index], 8, row.RowSettings.Color));
[1883]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        }
[1974]566
567      } else if (row.RowSettings.LineType == DataRowType.Normal) {
[1608]568        if (index > rowEntry.LinesShape.Count + 1) {
[1242]569          throw new NotImplementedException();
570        }
[861]571
[1603]572        if (action == Action.Added) {
[1883]573          if (rowEntry.LinesShape.Count == 0)
[1962]574            rowEntry.LinesShape.AddMarkerShape(new MarkerShape(0, row[0], 8, row.RowSettings.Color));
[1603]575          if (index > 0 && index == rowEntry.LinesShape.Count + 1) {
[1962]576            LineShape lineShape = new LineShape(index - 1, row[index - 1], index, row[index], row.RowSettings.Color, row.RowSettings.Thickness,
[1974]577                                                row.RowSettings.Style);
[1603]578            rowEntry.LinesShape.AddShape(lineShape);
[1962]579            rowEntry.LinesShape.AddMarkerShape(new MarkerShape(index, row[index], 8, row.RowSettings.Color));
[1603]580          }
[1608]581        } else if (action == Action.Modified) {
[1603]582          // not the first value
583          if (index > 0) {
584            rowEntry.LinesShape.GetShape(index - 1).Y2 = value;
[1883]585            ((MarkerShape) rowEntry.LinesShape.markersShape.GetShape(index - 1)).Y = value;
[1603]586          }
[861]587
[1603]588          // not the last value
[1883]589          if (index < row.Count - 1) {
[1603]590            rowEntry.LinesShape.GetShape(index).Y1 = value;
[1883]591            ((MarkerShape) rowEntry.LinesShape.markersShape.GetShape(index)).Y = value;
[1603]592          }
[1883]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!");
[1242]599        }
[1045]600      }
[861]601
[981]602      ZoomToFullView();
[697]603    }
604
[869]605    private void OnRowValuesChanged(IDataRow row, double[] values, int index, Action action) {
[1045]606      foreach (double value in values) {
[869]607        OnRowValueChanged(row, value, index++, action);
[1045]608      }
[861]609    }
[761]610
[1182]611    private void OnModelChanged() {
[1880]612      canvasUI.Invalidate();
[1182]613    }
614
[697]615    #region Begin-/EndUpdate
616
[1242]617    private int beginUpdateCount;
[697]618
[861]619    public void BeginUpdate() {
[697]620      beginUpdateCount++;
621    }
622
[861]623    public void EndUpdate() {
[1045]624      if (beginUpdateCount == 0) {
[697]625        throw new InvalidOperationException("Too many EndUpdates.");
[1045]626      }
[697]627
628      beginUpdateCount--;
629
[1045]630      if (beginUpdateCount == 0) {
[1880]631        canvasUI.Invalidate();
[1045]632      }
[697]633    }
634
635    #endregion
[928]636
[1059]637    #region Zooming / Panning
638
[928]639    private void canvasUI1_MouseDown(object sender, MouseEventArgs e) {
[1058]640      Focus();
[1249]641
[1237]642      if (e.Button == MouseButtons.Right) {
[1883]643        valueToolTip.Hide(this);
644        mouseEventListener = null;
[2015]645        contextMenu.Show(PointToScreen(e.Location));
[1249]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;
[2016]655          zoomListener.Zoom += Zoom;
[1249]656
657          rectangleShape.Rectangle = RectangleD.Empty;
658          userInteractionShape.AddShape(rectangleShape);
659
660          mouseEventListener = zoomListener;
[1187]661        }
662      }
[928]663    }
664
[1249]665    private void canvasUI_MouseMove(object sender, MouseEventArgs e) {
666      if (mouseEventListener != null) {
667        mouseEventListener.MouseMove(sender, e);
668      }
669    }
[1244]670
[1249]671    private void canvasUI_MouseUp(object sender, MouseEventArgs e) {
672      if (mouseEventListener != null) {
673        mouseEventListener.MouseUp(sender, e);
674      }
[1240]675
[1883]676      mouseEventListener = toolTipListener;
[1240]677    }
678
[1058]679    private void canvasUI1_MouseWheel(object sender, MouseEventArgs e) {
680      if (ModifierKeys == Keys.Control) {
[1351]681        double zoomFactor = (e.Delta > 0) ? 0.7 : 1.3;
[1058]682
[2015]683        PointD world = Transform.ToWorld(e.Location, xAxis.Viewport, xAxis.ClippingArea);
[1351]684
[2014]685        double x1 = world.X - (world.X - xAxis.ClippingArea.X1) * zoomFactor;
686        double x2 = world.X + (xAxis.ClippingArea.X2 - world.X) * zoomFactor;
[1351]687
688        SetClipX(x1, x2);
689
[1285]690        foreach (RowEntry rowEntry in rowEntries) {
[1351]691          world = Transform.ToWorld(e.Location, rowEntry.LinesShape.Viewport, rowEntry.LinesShape.ClippingArea);
692
[2014]693          double y1 = world.Y - (world.Y - rowEntry.LinesShape.ClippingArea.Y1) * zoomFactor;
694          double y2 = world.Y + (rowEntry.LinesShape.ClippingArea.Y2 - world.Y) * zoomFactor;
[1351]695
696          SetClipY(rowEntry, y1, y2);
[1285]697        }
[1351]698
[1880]699        canvasUI.Invalidate();
[1058]700      }
701    }
702
[2014]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
[2016]725    private void Zoom(Rectangle rectangle) {
[2014]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
[1249]742    #endregion
[928]743
[1879]744    protected override void OnPaint(PaintEventArgs e) {
745      UpdateLayout();
746      base.OnPaint(e);
747    }
748
[1285]749    private class LinesShape : WorldShape {
[1559]750      public readonly CompositeShape markersShape = new CompositeShape();
751
[1285]752      public void UpdateStyle(IDataRow row) {
753        foreach (IShape shape in shapes) {
754          LineShape lineShape = shape as LineShape;
755          if (lineShape != null) {
[1962]756            lineShape.LSColor = row.RowSettings.Color;
[1974]757            lineShape.LSDrawingStyle = row.RowSettings.Style;
[1962]758            lineShape.LSThickness = row.RowSettings.Thickness;
[1285]759          }
760        }
[1974]761        markersShape.ShowChildShapes = row.RowSettings.ShowMarkers;
[1285]762      }
[928]763
[1883]764      /// <summary>
765      /// Draws all Shapes in the chart
766      /// </summary>
767      /// <param name="graphics"></param>
[1559]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
[1883]785      public void RemoveMarkerShape(IShape shape) {
786        shape.Parent = this;
787        markersShape.RemoveShape(shape);
788      }
789
[1285]790      public int Count {
791        get { return shapes.Count; }
792      }
[928]793
[1285]794      public LineShape GetShape(int index) {
[1883]795        return (LineShape) shapes[index]; //shapes[0] is markersShape!!
[1285]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;
[1559]806        linesShape.markersShape.Parent = linesShape;
[1285]807      }
808
809      public IDataRow DataRow {
810        get { return dataRow; }
811      }
812
[1350]813      public LinesShape LinesShape {
814        get { return linesShape; }
815      }
[1559]816
[1965]817      public void ShowMarkers(bool flag) {
[1561]818        linesShape.markersShape.ShowChildShapes = flag;
[1559]819      }
[1350]820    }
821
[1608]822    private class YAxisInfo {
[1876]823      private readonly YAxisGrid grid = new YAxisGrid();
[1350]824      private readonly YAxis yAxis = new YAxis();
825
[1876]826      public YAxisGrid Grid {
[1285]827        get { return grid; }
828      }
829
830      public YAxis YAxis {
831        get { return yAxis; }
832      }
833    }
[684]834  }
[1608]835}
Note: See TracBrowser for help on using the repository browser.