123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432433434435436437438439440441442443444445446447448449450451452453454455456457458459460461462463464465466467468469470471472473474475476477478479480481482483484485486487488489490491492493494495496497498499500501502503504505506507508509510511512513514515516517518519520521522523524525526527528529530531532533534535536537538539540541542543544545546547548549550551552553554555556557558559560561562563564565566567568569570571572573574575576577578579580581582583584585586587588589590591592593594595596597598599600601602603604605606607608609610611612613614615616617618619620621622623624625626627628629630631632633634635636637638639640641642643644645646647648649650651652653654655656657658659660661662663664665666667668669670671672673674675676677678679680681682683684685686687688689690691692693694695696697698699700701702703704705706707708709710711712713714715716717718719720721722723724725726727728729730731732733734735736737738739740741742743744745746747748749750751752753754755756757758759760761762763764765766767768769770771772773774775776777778779780781782783784785786787 |
- // Licensed to the .NET Foundation under one or more agreements.
- // The .NET Foundation licenses this file to you under the MIT license.
- // See the LICENSE file in the project root for more information.
- //
- // Purpose: Collection of annotation objects.
- //
- using System;
- using System.Windows.Forms;
- using System.Diagnostics.CodeAnalysis;
- using System.Drawing;
- namespace FastReport.DataVisualization.Charting
- {
- /// <summary>
- /// <b>AnnotationCollection</b> is a collection that stores chart annotation objects.
- /// <seealso cref="Charting.Chart.Annotations"/>
- /// </summary>
- /// <remarks>
- /// All chart annotations are stored in this collection. It is exposed as
- /// a <see cref="Charting.Chart.Annotations"/> property of the chart. It is also used to
- /// store annotations inside the <see cref="AnnotationGroup"/> class.
- /// <para>
- /// This class includes methods for adding, inserting, iterating and removing annotations.
- /// </para>
- /// </remarks>
- [
- SRDescription("DescriptionAttributeAnnotations3"),
- ]
- public class AnnotationCollection : ChartNamedElementCollection<Annotation>
- {
- #region Fields
- /// <summary>
- /// Group this collection belongs too
- /// </summary>
- internal AnnotationGroup AnnotationGroup { get; set; }
- // Annotation object that was last clicked on
- internal Annotation lastClickedAnnotation = null;
- // Start point of annotation moving or resizing
- private PointF _movingResizingStartPoint = PointF.Empty;
- // Current resizing mode
- private ResizingMode _resizingMode = ResizingMode.None;
-
- // Annotation object which is currently placed on the chart
- internal Annotation placingAnnotation = null;
- #endregion
- #region Construction and Initialization
- /// <summary>
- /// Initializes a new instance of the <see cref="AnnotationCollection"/> class.
- /// </summary>
- /// <param name="parent">The parent chart element.</param>
- internal AnnotationCollection(IChartElement parent) : base(parent)
- {
- }
- #endregion
- #region Items Inserting and Removing Notification methods
- /// <summary>
- /// Initializes the specified item.
- /// </summary>
- /// <param name="item">The item.</param>
- internal override void Initialize(Annotation item)
- {
- if (item != null)
- {
- TextAnnotation textAnnotation = item as TextAnnotation;
- if (textAnnotation != null && string.IsNullOrEmpty(textAnnotation.Text) && Chart != null && Chart.IsDesignMode())
- {
- textAnnotation.Text = item.Name;
- }
- //If the collection belongs to annotation group we need to pass a ref to this group to all the child annotations
- if (this.AnnotationGroup != null)
- {
- item.annotationGroup = this.AnnotationGroup;
- }
- item.ResetCurrentRelativePosition();
- }
- base.Initialize(item);
- }
- /// <summary>
- /// Deinitializes the specified item.
- /// </summary>
- /// <param name="item">The item.</param>
- internal override void Deinitialize(Annotation item)
- {
- if (item != null)
- {
- item.annotationGroup = null;
- item.ResetCurrentRelativePosition();
- }
- base.Deinitialize(item);
- }
- /// <summary>
- /// Finds an annotation in the collection by name.
- /// </summary>
- /// <param name="name">
- /// Name of the annotation to find.
- /// </param>
- /// <returns>
- /// <see cref="Annotation"/> object, or null (or nothing) if it does not exist.
- /// </returns>
- public override Annotation FindByName(string name)
- {
- foreach(Annotation annotation in this)
- {
- // Compare annotation name
- if(annotation.Name == name)
- {
- return annotation;
- }
- // Check if annotation is a group
- AnnotationGroup annotationGroup = annotation as AnnotationGroup;
- if(annotationGroup != null)
- {
- Annotation result = annotationGroup.Annotations.FindByName(name);
- if(result != null)
- {
- return result;
- }
- }
- }
- return null;
- }
- #endregion
- #region Painting
- /// <summary>
- /// Paints all annotation objects in the collection.
- /// </summary>
- /// <param name="chartGraph">Chart graphics used for painting.</param>
- /// <param name="drawAnnotationOnly">Indicates that only annotation objects are redrawn.</param>
- [SuppressMessage("Microsoft.Usage", "CA1801:ReviewUnusedParameters", Justification="This parameter is used when compiling for the WinForms version of Chart")]
- internal void Paint(ChartGraphics chartGraph, bool drawAnnotationOnly)
- {
- ChartPicture chartPicture = this.Chart.chartPicture;
- // Restore previous background using double buffered bitmap
- if(!chartPicture.isSelectionMode &&
- this.Count > 0 /*&&
- !this.Chart.chartPicture.isPrinting*/)
- {
- chartPicture.backgroundRestored = true;
- Rectangle chartPosition = new Rectangle(0, 0, chartPicture.Width, chartPicture.Height);
- if(chartPicture.nonTopLevelChartBuffer == null || !drawAnnotationOnly)
- {
- // Dispose previous bitmap
- if(chartPicture.nonTopLevelChartBuffer != null)
- {
- chartPicture.nonTopLevelChartBuffer.Dispose();
- chartPicture.nonTopLevelChartBuffer = null;
- }
- // Copy chart area plotting rectangle from the chart's dubble buffer image into area dubble buffer image
- if (this.Chart.paintBufferBitmap != null &&
- this.Chart.paintBufferBitmap.Size.Width >= chartPosition.Size.Width &&
- this.Chart.paintBufferBitmap.Size.Height >= chartPosition.Size.Height)
- {
- chartPicture.nonTopLevelChartBuffer = this.Chart.paintBufferBitmap.Clone(
- chartPosition, this.Chart.paintBufferBitmap.PixelFormat);
- }
- }
- else if(drawAnnotationOnly && chartPicture.nonTopLevelChartBuffer != null)
- {
- // Restore previous background
- this.Chart.paintBufferBitmapGraphics.DrawImageUnscaled(
- chartPicture.nonTopLevelChartBuffer,
- chartPosition);
- }
- }
- // Draw all annotation objects
- foreach(Annotation annotation in this)
- {
- // Reset calculated relative position
- annotation.ResetCurrentRelativePosition();
- if(annotation.IsVisible())
- {
- bool resetClip = false;
- // Check if anchor point assosiated with plot area is inside the scaleView
- if(annotation.IsAnchorVisible())
- {
- // Set annotation object clipping
- if(annotation.ClipToChartArea.Length > 0 &&
- annotation.ClipToChartArea != Constants.NotSetValue &&
- Chart != null)
- {
- int areaIndex = Chart.ChartAreas.IndexOf(annotation.ClipToChartArea);
- if( areaIndex >= 0 )
- {
- // Get chart area object
- ChartArea chartArea = Chart.ChartAreas[areaIndex];
- chartGraph.SetClip(chartArea.PlotAreaPosition.ToRectangleF());
- resetClip = true;
- }
- }
- // Start Svg Selection mode
- string url = String.Empty;
- chartGraph.StartHotRegion(
- annotation.ReplaceKeywords(url),
- annotation.ReplaceKeywords(annotation.ToolTip) );
- // Draw annotation object
- annotation.Paint(Chart, chartGraph);
- // End Svg Selection mode
- chartGraph.EndHotRegion( );
- // Reset clipping region
- if(resetClip)
- {
- chartGraph.ResetClip();
- }
- }
- }
- }
- }
- #endregion
- #region Mouse Events Handlers
- /// <summary>
- /// Mouse was double clicked.
- /// </summary>
- internal void OnDoubleClick()
- {
- if(lastClickedAnnotation != null &&
- lastClickedAnnotation.AllowTextEditing)
- {
- TextAnnotation textAnnotation = lastClickedAnnotation as TextAnnotation;
- if(textAnnotation == null)
- {
- AnnotationGroup group = lastClickedAnnotation as AnnotationGroup;
- if (group != null)
- {
- // Try to edit text annotation in the group
- foreach (Annotation annot in group.Annotations)
- {
- TextAnnotation groupAnnot = annot as TextAnnotation;
- if (groupAnnot != null &&
- groupAnnot.AllowTextEditing)
- {
- // Get annotation position in relative coordinates
- PointF firstPoint = PointF.Empty;
- PointF anchorPoint = PointF.Empty;
- SizeF size = SizeF.Empty;
- groupAnnot.GetRelativePosition(out firstPoint, out size, out anchorPoint);
- RectangleF textPosition = new RectangleF(firstPoint, size);
- // Check if last clicked coordinate is inside this text annotation
- if (groupAnnot.GetGraphics() != null &&
- textPosition.Contains(groupAnnot.GetGraphics().GetRelativePoint(this._movingResizingStartPoint)))
- {
- textAnnotation = groupAnnot;
- lastClickedAnnotation = textAnnotation;
- break;
- }
- }
- }
- }
- }
- if(textAnnotation != null)
- {
- // Start annotation text editing
- textAnnotation.BeginTextEditing();
- }
- }
- }
- /// <summary>
- /// Checks if specified point is contained by any of the selection handles.
- /// </summary>
- /// <param name="point">Point which is tested in pixel coordinates.</param>
- /// <param name="resizingMode">Handle containing the point or None.</param>
- /// <returns>Annotation that contains the point or Null.</returns>
- internal Annotation HitTestSelectionHandles(PointF point, ref ResizingMode resizingMode)
- {
- Annotation annotation = null;
- if( Common != null &&
- Common.graph != null)
- {
- PointF pointRel = Common.graph.GetRelativePoint(point);
- foreach(Annotation annot in this)
- {
- // Reset selcted path point
- annot.currentPathPointIndex = -1;
- // Check if annotation is selected
- if(annot.IsSelected)
- {
- if(annot.selectionRects != null)
- {
- for(int index = 0; index < annot.selectionRects.Length; index++)
- {
- if(!annot.selectionRects[index].IsEmpty &&
- annot.selectionRects[index].Contains(pointRel))
- {
- annotation = annot;
- if(index > (int)ResizingMode.AnchorHandle)
- {
- resizingMode = ResizingMode.MovingPathPoints;
- annot.currentPathPointIndex = index - 9;
- }
- else
- {
- resizingMode = (ResizingMode)index;
- }
- }
- }
- }
- }
- }
- }
- return annotation;
- }
- /// <summary>
- /// Mouse button pressed in the control.
- /// </summary>
- /// <param name="e">Event arguments.</param>
- /// <param name="isHandled">Returns true if event is handled and no further processing required.</param>
- internal void OnMouseDown(MouseEventArgs e, ref bool isHandled)
- {
- // Reset last clicked annotation object and stop text editing
- if(lastClickedAnnotation != null)
- {
- TextAnnotation textAnnotation = lastClickedAnnotation as TextAnnotation;
- if(textAnnotation != null)
- {
- // Stop annotation text editing
- textAnnotation.StopTextEditing();
- }
- lastClickedAnnotation = null;
- }
- // Check if in annotation placement mode
- if( this.placingAnnotation != null)
- {
- // Process mouse down
- this.placingAnnotation.PlacementMouseDown(new PointF(e.X, e.Y), e.Button);
- // Set handled flag
- isHandled = true;
- return;
- }
- // Process only left mouse buttons
- if(e.Button == MouseButtons.Left)
- {
- bool updateRequired = false;
- this._resizingMode = ResizingMode.None;
- // Check if mouse buton was pressed in any selection handles areas
- Annotation annotation =
- HitTestSelectionHandles(new PointF(e.X, e.Y), ref this._resizingMode);
- // Check if mouse button was pressed over one of the annotation objects
- if(annotation == null && this.Count > 0)
- {
- HitTestResult result = this.Chart.HitTest(e.X, e.Y, ChartElementType.Annotation);
- if(result != null && result.ChartElementType == ChartElementType.Annotation)
- {
- annotation = (Annotation)result.Object;
- }
- }
- // Unselect all annotations if mouse clicked outside any annotations
- if(annotation == null || !annotation.IsSelected)
- {
- if((Control.ModifierKeys & Keys.Control) != Keys.Control &&
- (Control.ModifierKeys & Keys.Shift) != Keys.Shift)
- {
- foreach (Annotation annot in this.Chart.Annotations)
- {
- if(annot != annotation && annot.IsSelected)
- {
- annot.IsSelected = false;
- updateRequired = true;
- // Call selection changed notification
- if (this.Chart != null)
- {
- this.Chart.OnAnnotationSelectionChanged(annot);
- }
- }
- }
- }
- }
- // Process mouse action in the annotation object
- if(annotation != null)
- {
- // Mouse down event handled
- isHandled = true;
- // Select/Unselect annotation
- Annotation selectableAnnotation = annotation;
- if(annotation.AnnotationGroup != null)
- {
- // Select annotation group when click on any child annotations
- selectableAnnotation = annotation.AnnotationGroup;
- }
- if(!selectableAnnotation.IsSelected && selectableAnnotation.AllowSelecting)
- {
- selectableAnnotation.IsSelected = true;
- updateRequired = true;
- // Call selection changed notification
- if (this.Chart != null)
- {
- this.Chart.OnAnnotationSelectionChanged(selectableAnnotation);
- }
- }
- else if((Control.ModifierKeys & Keys.Control) == Keys.Control ||
- (Control.ModifierKeys & Keys.Shift) == Keys.Shift)
- {
- selectableAnnotation.IsSelected = false;
- updateRequired = true;
- // Call selection changed notification
- if (this.Chart != null)
- {
- this.Chart.OnAnnotationSelectionChanged(selectableAnnotation);
- }
- }
- // Remember last clicked and selected annotation
- lastClickedAnnotation = annotation;
- // Rember mouse position
- this._movingResizingStartPoint = new PointF(e.X, e.Y);
- // Start moving, repositioning or resizing of annotation
- if(annotation.IsSelected)
- {
- // Check if one of selection handles was clicked on
- this._resizingMode = annotation.GetSelectionHandle(this._movingResizingStartPoint);
- if(!annotation.AllowResizing &&
- this._resizingMode >= ResizingMode.TopLeftHandle &&
- this._resizingMode <= ResizingMode.LeftHandle)
- {
- this._resizingMode = ResizingMode.None;
- }
- if(!annotation.AllowAnchorMoving &&
- this._resizingMode == ResizingMode.AnchorHandle)
- {
- this._resizingMode = ResizingMode.None;
- }
- if(this._resizingMode == ResizingMode.None && annotation.AllowMoving)
- {
- // Annotation moving mode
- this._resizingMode = ResizingMode.Moving;
- }
- }
- else
- {
- if(this._resizingMode == ResizingMode.None && annotation.AllowMoving)
- {
- // Do not allow moving child annotations inside the group.
- // Only the whole group can be selected, resized or repositioned.
- if (annotation.AnnotationGroup != null)
- {
- // Move the group instead
- lastClickedAnnotation = annotation.AnnotationGroup;
- }
-
- // Annotation moving mode
- this._resizingMode = ResizingMode.Moving;
- }
- }
- }
- // Update chart
- if(updateRequired)
- {
- // Invalidate and update the chart
- this.Chart.Invalidate(true);
- this.Chart.UpdateAnnotations();
- }
- }
- }
- /// <summary>
- /// Mouse button released in the control.
- /// </summary>
- /// <param name="e">Event arguments.</param>
- internal void OnMouseUp(MouseEventArgs e)
- {
- // Check if in annotation placement mode
- if( this.placingAnnotation != null)
- {
- if(!this.placingAnnotation.PlacementMouseUp(new PointF(e.X, e.Y), e.Button))
- {
- return;
- }
- }
- if(e.Button == MouseButtons.Left)
- {
- // Reset moving sizing start point
- this._movingResizingStartPoint = PointF.Empty;
- this._resizingMode = ResizingMode.None;
- }
- // Loop through all annotation objects
- for(int index = 0; index < this.Count; index++)
- {
- Annotation annotation = this[index];
- // NOTE: Automatic deleting feature was disabled. -AG.
- /*
- // Delete all annotation objects moved outside clipping region
- if( annotation.outsideClipRegion )
- {
- this.List.RemoveAt(index);
- --index;
- }
- */
- // Reset start position/location fields
- annotation.startMovePositionRel = RectangleF.Empty;
- annotation.startMoveAnchorLocationRel = PointF.Empty;
- if(annotation.startMovePathRel != null)
- {
- annotation.startMovePathRel.Dispose();
- annotation.startMovePathRel = null;
- }
- // Fire position changed event
- if( annotation.positionChanged )
- {
- annotation.positionChanged = false;
- if (this.Chart != null)
- {
- this.Chart.OnAnnotationPositionChanged(annotation);
- }
- }
- }
- }
- /// <summary>
- /// Mouse moved in the control.
- /// </summary>
- /// <param name="e">Event arguments.</param>
- internal void OnMouseMove(MouseEventArgs e)
- {
- // Check if in annotation placement mode
- if(this.placingAnnotation != null)
- {
- System.Windows.Forms.Cursor newCursor = this.Chart.Cursor;
- if(this.placingAnnotation.IsValidPlacementPosition(e.X, e.Y))
- {
- newCursor = Cursors.Cross;
- }
- else
- {
- newCursor = this.Chart.defaultCursor;
- }
- // Set current chart cursor
- if (newCursor != this.Chart.Cursor)
- {
- System.Windows.Forms.Cursor tmpCursor = this.Chart.defaultCursor;
- this.Chart.Cursor = newCursor;
- this.Chart.defaultCursor = tmpCursor;
- }
- this.placingAnnotation.PlacementMouseMove(new PointF(e.X, e.Y));
-
- return;
- }
- // Check if currently resizing/moving annotation
- if(!this._movingResizingStartPoint.IsEmpty &&
- this._resizingMode != ResizingMode.None)
- {
- // Calculate how far the mouse was moved
- SizeF moveDistance = new SizeF(
- this._movingResizingStartPoint.X - e.X,
- this._movingResizingStartPoint.Y - e.Y );
- // Update location of all selected annotation objects
- foreach(Annotation annot in this)
- {
- if(annot.IsSelected &&
- ( (this._resizingMode == ResizingMode.MovingPathPoints && annot.AllowPathEditing) ||
- (this._resizingMode == ResizingMode.Moving && annot.AllowMoving) ||
- (this._resizingMode == ResizingMode.AnchorHandle && annot.AllowAnchorMoving) ||
- (this._resizingMode >= ResizingMode.TopLeftHandle && this._resizingMode <= ResizingMode.LeftHandle && annot.AllowResizing) ) )
- {
- annot.AdjustLocationSize(moveDistance, this._resizingMode, true, true);
- }
- }
- // Move last clicked non-selected annotation
- if(lastClickedAnnotation != null &&
- !lastClickedAnnotation.IsSelected)
- {
- if(this._resizingMode == ResizingMode.Moving &&
- lastClickedAnnotation.AllowMoving)
- {
- lastClickedAnnotation.AdjustLocationSize(moveDistance, this._resizingMode, true, true);
- }
- }
- // Invalidate and update the chart
- this.Chart.Invalidate(true);
- this.Chart.UpdateAnnotations();
- }
- else if(this.Count > 0)
- {
- // Check if currently placing annotation from the UserInterface
- bool process = true;
- if(process)
- {
- // Check if mouse pointer is over the annotation selection handle
- ResizingMode currentResizingMode = ResizingMode.None;
- Annotation annotation =
- HitTestSelectionHandles(new PointF(e.X, e.Y), ref currentResizingMode);
- // Check if mouse pointer over the annotation object movable area
- if(annotation == null)
- {
- HitTestResult result = this.Chart.HitTest(e.X, e.Y, ChartElementType.Annotation);
- if(result != null && result.ChartElementType == ChartElementType.Annotation)
- {
- annotation = (Annotation)result.Object;
- if(annotation != null)
- {
- // Check if annotation is in the collection
- if(this.Contains(annotation))
- {
- currentResizingMode = ResizingMode.Moving;
- if(annotation.AllowMoving == false)
- {
- // Movement is not allowed
- annotation = null;
- currentResizingMode = ResizingMode.None;
- }
- }
- }
- }
- }
- // Set mouse cursor
- SetResizingCursor(annotation, currentResizingMode);
- }
- }
- }
- /// <summary>
- /// Sets mouse cursor shape.
- /// </summary>
- /// <param name="annotation">Annotation object.</param>
- /// <param name="currentResizingMode">Resizing mode.</param>
- private void SetResizingCursor(Annotation annotation, ResizingMode currentResizingMode)
- {
- // Change current cursor
- if(this.Chart != null)
- {
- System.Windows.Forms.Cursor newCursor = this.Chart.Cursor;
- if(annotation != null)
- {
- if(currentResizingMode == ResizingMode.MovingPathPoints &&
- annotation.AllowPathEditing)
- {
- newCursor = Cursors.Cross;
- }
- if(currentResizingMode == ResizingMode.Moving &&
- annotation.AllowMoving)
- {
- newCursor = Cursors.SizeAll;
- }
- if(currentResizingMode == ResizingMode.AnchorHandle &&
- annotation.AllowAnchorMoving)
- {
- newCursor = Cursors.Cross;
- }
- if(currentResizingMode != ResizingMode.Moving &&
- annotation.AllowResizing)
- {
- if(annotation.SelectionPointsStyle == SelectionPointsStyle.TwoPoints)
- {
- if(currentResizingMode == ResizingMode.TopLeftHandle ||
- currentResizingMode == ResizingMode.BottomRightHandle)
- {
- newCursor = Cursors.Cross;
- }
- }
- else
- {
- if(currentResizingMode == ResizingMode.TopLeftHandle ||
- currentResizingMode == ResizingMode.BottomRightHandle)
- {
- newCursor = Cursors.SizeNWSE;
- }
- else if(currentResizingMode == ResizingMode.TopRightHandle ||
- currentResizingMode == ResizingMode.BottomLeftHandle)
- {
- newCursor = Cursors.SizeNESW;
- }
- else if(currentResizingMode == ResizingMode.TopHandle ||
- currentResizingMode == ResizingMode.BottomHandle)
- {
- newCursor = Cursors.SizeNS;
- }
- else if(currentResizingMode == ResizingMode.LeftHandle ||
- currentResizingMode == ResizingMode.RightHandle)
- {
- newCursor = Cursors.SizeWE;
- }
- }
- }
- }
- else
- {
- newCursor = this.Chart.defaultCursor;
- }
- // Set current chart cursor
- if (newCursor != this.Chart.Cursor)
- {
- System.Windows.Forms.Cursor tmpCursor = this.Chart.defaultCursor;
- this.Chart.Cursor = newCursor;
- this.Chart.defaultCursor = tmpCursor;
- }
- }
- }
- #endregion
- #region Event handlers
- internal void ChartAreaNameReferenceChanged(object sender, NameReferenceChangedEventArgs e)
- {
- // If all the chart areas are removed and then a new one is inserted - Annotations don't get bound to it by default
- if (e.OldElement == null)
- return;
- foreach (Annotation annotation in this)
- {
- if (annotation.ClipToChartArea == e.OldName)
- annotation.ClipToChartArea = e.NewName;
- AnnotationGroup group = annotation as AnnotationGroup;
- if (group != null)
- {
- group.Annotations.ChartAreaNameReferenceChanged(sender, e);
- }
- }
- }
- #endregion
- }
- }
|