Updates referencesource to .NET 4.7
[mono.git] / mcs / class / referencesource / System.Web.DataVisualization / Common / General / ImageMap.cs
1 //-------------------------------------------------------------
2 // <copyright company=\92Microsoft Corporation\92>
3 //   Copyright © Microsoft Corporation. All Rights Reserved.
4 // </copyright>
5 //-------------------------------------------------------------
6 // @owner=alexgor, deliant
7 //=================================================================
8 //  File:               ImageMap.cs
9 //
10 //  Namespace:  DataVisualization.Charting
11 //
12 //      Classes:        MapArea, MapAreasCollection
13 //
14 //  Purpose:    Collection of MapArea classes is used to generate 
15 //              Chart image map, which provides functionality like
16 //              tooltip, drilldown and client-side scripting.
17 //
18 //      Reviewed:       AG - Jul 31, 2002
19 //              AG - Microsoft 14, 2007
20 //
21 //===================================================================
22
23
24 #region Used namespaces
25
26 using System;
27 using System.Text;
28 using System.Collections;
29 using System.Collections.Specialized;
30 using System.ComponentModel;
31 using System.ComponentModel.Design;
32 using System.Drawing;
33 using System.Drawing.Drawing2D;
34 using System.Drawing.Design;
35 using System.Collections.ObjectModel;
36 using System.Diagnostics.CodeAnalysis;
37 #if Microsoft_CONTROL
38         using System.Windows.Forms.DataVisualization.Charting;
39         using System.Windows.Forms.DataVisualization.Charting.Data;
40         using System.Windows.Forms.DataVisualization.Charting.ChartTypes;
41         using System.Windows.Forms.DataVisualization.Charting.Utilities;
42         using System.Windows.Forms.DataVisualization.Charting.Borders3D;
43
44 #else
45         using System.Web;
46         using System.Web.UI;
47         using System.Web.UI.DataVisualization.Charting;
48         using System.Web.UI.DataVisualization.Charting.Utilities;
49 using System.Text.RegularExpressions;
50 using System.IO;
51
52 #endif
53
54 #endregion
55
56 #if Microsoft_CONTROL
57
58         namespace System.Windows.Forms.DataVisualization.Charting
59
60 #else
61 namespace System.Web.UI.DataVisualization.Charting
62
63 #endif
64 {
65
66 #if ! Microsoft_CONTROL
67
68         #region Map area shape enumeration
69
70         /// <summary>
71         /// An enumeration of map areas shapes.
72         /// </summary>
73         public enum MapAreaShape
74         {
75                 /// <summary>
76                 /// The shape of the map area is rectangular.
77                 /// </summary>
78                 Rectangle,
79
80                 /// <summary>
81         /// The shape of the map area is circular.
82                 /// </summary>
83                 Circle,
84
85                 /// <summary>
86         /// The shape of the map area is polygonal.
87                 /// </summary>
88                 Polygon
89         }
90
91
92         #endregion
93
94         #region IMapArea interface defenition
95
96         /// <summary>
97         /// Interface which defines common properties for the map area
98         /// </summary>
99 #if ASPPERM_35
100         [AspNetHostingPermission(System.Security.Permissions.SecurityAction.InheritanceDemand, Level = AspNetHostingPermissionLevel.Minimal)]
101     [AspNetHostingPermission(System.Security.Permissions.SecurityAction.LinkDemand, Level = AspNetHostingPermissionLevel.Minimal)]
102 #endif
103     public interface IChartMapArea
104         {
105         /// <summary>
106         /// Map area tooltip
107         /// </summary>
108         /// <value>The tooltip.</value>
109                 string ToolTip
110                 {
111                         set; get;
112                 }
113         /// <summary>
114         /// Map area Href
115         /// </summary>
116         /// <value>The map area Href.</value>
117         [SuppressMessage("Microsoft.Design", "CA1056:UriPropertiesShouldNotBeStrings")]
118                 string Url
119                 {
120                         set; get;
121                 }
122         /// <summary>
123         /// Map area other custom attributes
124         /// </summary>
125         /// <value>The map area attributes.</value>
126                 string MapAreaAttributes
127                 {
128                         set; get;
129         }
130
131         /// <summary>
132         /// Map area custom data
133         /// </summary>
134         /// <value>The tag.</value>
135         object Tag
136         {
137             set;
138             get;
139         }
140
141         /// <summary>
142         /// Map area post back value.
143         /// </summary>
144         /// <value>The post back value.</value>
145         string PostBackValue { get; set; }
146     }
147
148         #endregion
149
150         /// <summary>
151     /// The MapArea class represents an area of the chart with end-user 
152     /// interactivity like tooltip, HREF or custom attributes.
153         /// </summary>
154         [
155         DefaultProperty("ToolTip"),
156         SRDescription("DescriptionAttributeMapArea_MapArea")
157         ]
158 #if ASPPERM_35
159         [AspNetHostingPermission(System.Security.Permissions.SecurityAction.InheritanceDemand, Level = AspNetHostingPermissionLevel.Minimal)]
160     [AspNetHostingPermission(System.Security.Permissions.SecurityAction.LinkDemand, Level = AspNetHostingPermissionLevel.Minimal)]
161 #endif
162     public class MapArea : ChartNamedElement, IChartMapArea
163     {
164
165         #region Member variables
166
167                 private string                  _toolTip = String.Empty;
168         private string          _url = String.Empty;
169         private string          _attributes = String.Empty;
170         private string          _postBackValue = String.Empty;
171                 private bool                    _isCustom = true;
172                 private MapAreaShape    _shape = MapAreaShape.Rectangle;
173         private float[]         _coordinates = new float[4];
174         private static Regex    _mapAttributesRegex;
175         #endregion
176
177         #region Constructors
178
179         /// <summary>
180         /// Initializes a new instance of the <see cref="MapArea"/> class.
181         /// </summary>
182                 public MapArea() 
183             : base()
184                 {
185                 }
186
187         /// <summary>
188         /// Initializes a new instance of the <see cref="MapArea"/> class.
189         /// </summary>
190         /// <param name="url">The destination URL or anchor point of the map area.</param>
191         /// <param name="path">A GraphicsPath object that defines the shape of the map area.</param>
192         [SuppressMessage("Microsoft.Design", "CA1054:UriParametersShouldNotBeStrings", MessageId = "0#")]
193         public MapArea(string url, GraphicsPath path)
194             : this(String.Empty, url, String.Empty, String.Empty, path, null)
195         {
196         }
197
198         /// <summary>
199         /// Initializes a new instance of the <see cref="MapArea"/> class.
200         /// </summary>
201         /// <param name="url">The destination URL or anchor point of the map area.</param>
202         /// <param name="rect">A RectangleF structure that defines shape of the rectangular map area.</param>
203         [SuppressMessage("Microsoft.Design", "CA1054:UriParametersShouldNotBeStrings", MessageId = "0#")]
204         public MapArea(string url, RectangleF rect)
205             : this(String.Empty, url, String.Empty, String.Empty, rect, null)
206         {
207         }
208
209         /// <summary>
210         /// Initializes a new instance of the <see cref="MapArea"/> class.
211         /// </summary>
212         /// <param name="shape">Area shape.</param>
213         /// <param name="url">The destination URL or anchor point of the map area.</param>
214         /// <param name="coordinates">Coordinates array that determines the location of the circle, rectangle or polygon.
215         /// The type of shape that is being used determines the type of coordinates required.</param>
216         [SuppressMessage("Microsoft.Design", "CA1054:UriParametersShouldNotBeStrings", MessageId = "1#")]
217         public MapArea(MapAreaShape shape, string url, float[] coordinates)
218             : this(shape, String.Empty, url, String.Empty, String.Empty, coordinates, null)
219         {
220         }
221
222         /// <summary>
223         /// Initializes a new instance of the <see cref="MapArea"/> class.
224         /// </summary>
225         /// <param name="toolTip">Tool tip.</param>
226         /// <param name="url">Jump URL.</param>
227         /// <param name="attributes">Other area attributes.</param>
228         /// <param name="postBackValue">The postback value.</param>
229         /// <param name="path">Area coordinates as graphic path</param>
230         /// <param name="tag">The tag.</param>
231         [SuppressMessage("Microsoft.Design", "CA1054:UriParametersShouldNotBeStrings", MessageId = "1#")]
232         public MapArea(string toolTip, string url, string attributes, string postBackValue, GraphicsPath path, object tag) 
233             : base()
234                 {
235                         if(path.PointCount == 0)
236                         {
237                 throw new ArgumentException(SR.ExceptionImageMapPolygonShapeInvalid);
238             }
239
240             // Flatten all curved lines
241                         path.Flatten();
242
243                         // Allocate array of floats
244                         PointF[] pathPoints = path.PathPoints;
245                         float[] coord = new float[pathPoints.Length * 2];
246
247                         // Transfer path points
248                         int     index = 0;
249                         foreach(PointF point in pathPoints)
250                         {
251                                 coord[index++] = point.X;
252                                 coord[index++] = point.Y;
253                         }
254
255             // Initiazize area
256             Initialize(MapAreaShape.Polygon, toolTip, url, attributes, postBackValue, coord, tag);
257                 }
258
259         /// <summary>
260         /// Initializes a new instance of the <see cref="MapArea"/> class.
261         /// </summary>
262         /// <param name="toolTip">Tool tip.</param>
263         /// <param name="url">Jump URL.</param>
264         /// <param name="attributes">Other area attributes.</param>
265         /// <param name="postBackValue">The postback value.</param>
266         /// <param name="rect">Rect coordinates</param>
267         /// <param name="tag">The tag.</param>
268         [SuppressMessage("Microsoft.Design", "CA1054:UriParametersShouldNotBeStrings", MessageId = "1#")]
269         public MapArea(string toolTip, string url, string attributes, string postBackValue, RectangleF rect, object tag)
270             : base()
271         {
272             float[] coord = new float[4];
273             coord[0] = rect.X;
274             coord[1] = rect.Y;
275             coord[2] = rect.Right;
276             coord[3] = rect.Bottom;
277
278             Initialize(MapAreaShape.Rectangle, toolTip, url, attributes, postBackValue, coord, tag);
279         }
280
281         /// <summary>
282         /// Initializes a new instance of the <see cref="MapArea"/> class.
283         /// </summary>
284         /// <param name="shape">The shape.</param>
285         /// <param name="toolTip">The tool tip.</param>
286         /// <param name="url">The URL.</param>
287         /// <param name="attributes">The attributes.</param>
288         /// <param name="postBackValue">The postback value.</param>
289         /// <param name="coordinates">The coordinates.</param>
290         /// <param name="tag">The tag.</param>
291         [SuppressMessage("Microsoft.Design", "CA1054:UriParametersShouldNotBeStrings", MessageId = "2#")]
292         public MapArea(MapAreaShape shape, string toolTip, string url, string attributes, string postBackValue, float[] coordinates, object tag) 
293             : base()
294         {
295             Initialize(shape, toolTip, url, attributes, postBackValue, coordinates, tag);
296         }
297
298
299         private void Initialize(MapAreaShape shape, string toolTip, string url, string attributes, string postBackValue, float[] coordinates, object tag)
300         {
301             // Check number of coordinates depending on the area shape
302             if (shape == MapAreaShape.Circle && coordinates.Length != 3)
303             {
304                 throw (new InvalidOperationException(SR.ExceptionImageMapCircleShapeInvalid));
305             }
306             if (shape == MapAreaShape.Rectangle && coordinates.Length != 4)
307             {
308                 throw (new InvalidOperationException(SR.ExceptionImageMapRectangleShapeInvalid));
309             }
310             if (shape == MapAreaShape.Polygon && (coordinates.Length % 2f) != 0f)
311             {
312                 throw (new InvalidOperationException(SR.ExceptionImageMapPolygonShapeInvalid));
313             }
314
315             // Create new area object
316             this._toolTip = toolTip;
317             this._url = url;
318             this._attributes = attributes;
319             this._shape = shape;
320             this._coordinates = new float[coordinates.Length];
321             this._postBackValue = postBackValue;
322             this.Tag = tag;
323             coordinates.CopyTo(this._coordinates, 0);
324         }
325                 #endregion
326
327         #region Map area HTML tag generation methods
328
329         /// <summary>
330         /// Gets the name of the shape.
331         /// </summary>
332         /// <returns></returns>
333         private string GetShapeName()
334         {
335             //*****************************************
336             //** Set shape type
337             //*****************************************
338             if (_shape == MapAreaShape.Circle)
339             {
340                 return "circle";
341             }
342             else if (_shape == MapAreaShape.Rectangle)
343             {
344                 return "rect";
345             }
346             else if (_shape == MapAreaShape.Polygon)
347             {
348                 return "poly";
349             }
350             return String.Empty;
351         }
352
353
354         /// <summary>
355         /// Gets the coordinates.
356         /// </summary>
357         /// <param name="graph">The graph.</param>
358         /// <returns></returns>
359         private string GetCoordinates(ChartGraphics graph)
360         {
361             // Transform coordinates from relative to pixels
362             float[] transformedCoord = new float[this.Coordinates.Length];
363             if (this.Shape == MapAreaShape.Circle)
364             {
365                 PointF p = graph.GetAbsolutePoint(new PointF(this.Coordinates[0], this.Coordinates[1]));
366                 transformedCoord[0] = p.X;
367                 transformedCoord[1] = p.Y;
368                 p = graph.GetAbsolutePoint(new PointF(this.Coordinates[2], this.Coordinates[1]));
369                 transformedCoord[2] = p.X;
370             }
371             else if (this.Shape == MapAreaShape.Rectangle)
372             {
373                 PointF p = graph.GetAbsolutePoint(new PointF(this.Coordinates[0], this.Coordinates[1]));
374                 transformedCoord[0] = p.X;
375                 transformedCoord[1] = p.Y;
376                 p = graph.GetAbsolutePoint(new PointF(this.Coordinates[2], this.Coordinates[3]));
377                 transformedCoord[2] = p.X;
378                 transformedCoord[3] = p.Y;
379
380                 // Check if rectangle has width and height
381                 if ((int)Math.Round(transformedCoord[0]) == (int)Math.Round(transformedCoord[2]))
382                 {
383                     transformedCoord[2] = (float)Math.Round(transformedCoord[2]) + 1;
384                 }
385                 if ((int)Math.Round(transformedCoord[1]) == (int)Math.Round(transformedCoord[3]))
386                 {
387                     transformedCoord[3] = (float)Math.Round(transformedCoord[3]) + 1;
388                 }
389             }
390             else
391             {
392                 PointF pConverted = Point.Empty;
393                 PointF pOriginal = Point.Empty;
394                 for (int index = 0; index < this.Coordinates.Length - 1; index += 2)
395                 {
396                     pOriginal.X = this.Coordinates[index];
397                     pOriginal.Y = this.Coordinates[index + 1];
398                     pConverted = graph.GetAbsolutePoint(pOriginal);
399                     transformedCoord[index] = pConverted.X;
400                     transformedCoord[index + 1] = pConverted.Y;
401                 }
402             }
403             
404             StringBuilder tagStringBuilder = new StringBuilder();
405             // Store transformed coordinates in the string
406             bool firstElement = true;
407             foreach (float f in transformedCoord)
408             {
409                 if (!firstElement)
410                 {
411                     tagStringBuilder.Append(",");
412                 }
413                 firstElement = false;
414                 tagStringBuilder.Append((int)Math.Round(f));
415             }
416
417             return tagStringBuilder.ToString();
418         }
419
420         private static bool IsJavaScript(string value)
421         {
422             string checkValue = value.Trim().Replace("\r", String.Empty).Replace("\n", String.Empty);
423             if (checkValue.StartsWith("javascript:", StringComparison.OrdinalIgnoreCase))
424             {
425                 return true;
426             }
427             return false;
428         }
429
430
431         /// <summary>
432         /// Encodes the value.
433         /// </summary>
434         /// <param name="chart">The chart.</param>
435         /// <param name="name">The name.</param>
436         /// <param name="value">The value.</param>
437         /// <returns></returns>
438         private static string EncodeValue(Chart chart, string name, string value)
439         {
440             if (chart.IsMapAreaAttributesEncoded)
441             {
442                 if (IsJavaScript(value) ||
443                     name.Trim().StartsWith("on", StringComparison.OrdinalIgnoreCase))
444                 {
445                     return HttpUtility.UrlEncode(value);
446                 }
447             }
448             return value;
449         }
450
451         /// <summary>
452         /// Renders the tag.
453         /// </summary>
454         /// <param name="writer">The writer.</param>
455         /// <param name="chart">The chart.</param>
456         [SuppressMessage("Microsoft.Globalization", "CA1308:NormalizeStringsToUppercase", Justification="We use lower case to generate html attributes.")]
457         internal void RenderTag(HtmlTextWriter writer, Chart chart)
458         {
459             StringBuilder excludedAttributes = new StringBuilder();
460
461             writer.WriteLine();
462             
463             writer.AddAttribute(HtmlTextWriterAttribute.Shape, this.GetShapeName(), false);
464             writer.AddAttribute(HtmlTextWriterAttribute.Coords, this.GetCoordinates(chart.chartPicture.ChartGraph));
465
466             if (!String.IsNullOrEmpty(this.ToolTip))
467             {
468                 excludedAttributes.Append("title,");
469                 writer.AddAttribute(HtmlTextWriterAttribute.Title, EncodeValue(chart, "title", this.ToolTip));
470             }
471             
472             bool postbackRendered = false;
473             if (!String.IsNullOrEmpty(this.Url))
474             {
475                 excludedAttributes.Append("href,");
476                 string resolvedUrl = chart.ResolveClientUrl(this.Url);
477                 writer.AddAttribute(HtmlTextWriterAttribute.Href, EncodeValue(chart, "href", resolvedUrl));
478             }
479             else if (!String.IsNullOrEmpty(this.PostBackValue) && chart.Page != null)
480             {
481                 postbackRendered = true;
482                 excludedAttributes.Append("href,");
483                 writer.AddAttribute(HtmlTextWriterAttribute.Href, chart.Page.ClientScript.GetPostBackClientHyperlink(chart, this.PostBackValue));
484             }
485             
486             if (!postbackRendered && !String.IsNullOrEmpty(this.PostBackValue) && chart.Page != null)
487             {
488                 excludedAttributes.Append("onclick,");
489                 writer.AddAttribute(HtmlTextWriterAttribute.Onclick, chart.Page.ClientScript.GetPostBackEventReference(chart, this.PostBackValue));
490             }
491             
492             if (!String.IsNullOrEmpty(this._attributes))
493             {
494                 string excludedAttr = excludedAttributes.ToString();
495                 
496                 // matches name1="value1" name2="value2", don't match name1="val"ue1" or name1="value1" />
497                 if (_mapAttributesRegex == null)
498                 {
499                     _mapAttributesRegex = new Regex(@"\s?(?<name>(\w+))\s?=\s?""(?<value>[^""]+)""\s?", RegexOptions.IgnoreCase | RegexOptions.Singleline | RegexOptions.IgnorePatternWhitespace | RegexOptions.Compiled);
500                 }
501                 
502                 foreach (Match match in _mapAttributesRegex.Matches(this._attributes))
503                 {
504                     Group names = match.Groups["name"];
505                     Group values = match.Groups["value"];
506                     for (int i = 0; i < names.Captures.Count || i < values.Captures.Count; i++)
507                     {
508                         string name = names.Captures[i].Value.ToLowerInvariant();
509                         string value = values.Captures[i].Value;
510                         
511                         // skip already rendered attributes
512                         if (!excludedAttr.Contains(name + ","))  
513                         {
514                             // is it url?
515                             if ("src,href,longdesc,background,".Contains(name + ",") && !IsJavaScript(value))
516                             {
517                                 value = chart.ResolveClientUrl(value);
518                             }
519                             else
520                             {
521                                 value = HttpUtility.HtmlAttributeEncode(value);
522                             }
523                             value = EncodeValue(chart, name, value);
524                             writer.AddAttribute(name, value, false);
525                         }
526                     }
527                 }
528             }
529
530             if (this._attributes.IndexOf(" alt=", StringComparison.OrdinalIgnoreCase) == -1)
531             {
532                 if (!String.IsNullOrEmpty(this.ToolTip))
533                 {
534                     writer.AddAttribute(HtmlTextWriterAttribute.Alt, EncodeValue(chart, "title", this.ToolTip));
535                 }
536                 else
537                 {
538                     writer.AddAttribute(HtmlTextWriterAttribute.Alt, "");
539                 }
540             }
541
542             writer.RenderBeginTag(HtmlTextWriterTag.Area);
543             writer.RenderEndTag();
544         }
545
546                 #endregion
547
548                 #region MapArea Properties
549
550                 /// <summary>
551                 /// Gets or sets a flag which indicates whether the map area is custom.
552                 /// </summary>
553                 [
554                 Browsable(false),
555                 SRDescription("DescriptionAttributeMapArea_Custom"),
556                 DefaultValue(""),
557                 DesignerSerializationVisibilityAttribute(DesignerSerializationVisibility.Hidden),
558                 SerializationVisibilityAttribute(SerializationVisibility.Hidden)
559                 ]
560                 public bool IsCustom
561                 {
562                         get
563                         {
564                                 return _isCustom;
565                         }
566                         internal set
567                         {
568                                 _isCustom = value;
569                         }
570                 }
571
572                 /// <summary>
573         /// Gets or sets the coordinates of of the map area.
574                 /// </summary>
575                 [
576                 SRCategory("CategoryAttributeShape"),
577                 Bindable(true),
578                 SRDescription("DescriptionAttributeMapArea_Coordinates"),
579                 DefaultValue(""),
580                 #if !Microsoft_CONTROL
581                 PersistenceMode(PersistenceMode.Attribute),
582                 #endif
583                 TypeConverter(typeof(MapAreaCoordinatesConverter))
584                 ]
585         [SuppressMessage("Microsoft.Performance", "CA1819:PropertiesShouldNotReturnArrays")]
586                 public float[] Coordinates
587                 {
588                         get
589                         {
590                                 return _coordinates;
591                         }
592                         set
593                         {
594                                 _coordinates = value;
595                         }
596                 }
597
598                 /// <summary>
599         /// Gets or sets the shape of the map area.
600                 /// </summary>
601                 [
602                 SRCategory("CategoryAttributeShape"),
603                 Bindable(true),
604                 SRDescription("DescriptionAttributeMapArea_Shape"),
605                 DefaultValue(typeof(MapAreaShape), "Rectangle"),
606                 #if !Microsoft_CONTROL
607                 PersistenceMode(PersistenceMode.Attribute)
608                 #endif
609                 ]
610                 public MapAreaShape Shape
611                 {
612                         get
613                         {
614                                 return _shape;
615                         }
616                         set
617                         {
618                                 _shape = value;
619                         }
620                 }
621         
622                 /// <summary>
623         /// Gets or sets the name of the map area.
624                 /// </summary>
625                 [
626                 SRCategory("CategoryAttributeData"),
627                 SRDescription("DescriptionAttributeMapArea_Name"),
628                 DefaultValue("Map Area"),
629                 Browsable(false),
630                 DesignerSerializationVisibilityAttribute(DesignerSerializationVisibility.Hidden),
631                 SerializationVisibilityAttribute(SerializationVisibility.Hidden)
632                 ]
633                 public override string Name
634                 {
635                         get
636                         {
637                                 return base.Name;
638                         }
639                         set
640                         {
641                                 base.Name = value;
642                         }
643                 }
644                 #endregion
645
646                 #region IMapAreaAttributesutes Properties implementation
647
648                 /// <summary>
649         /// Gets or sets the tooltip of the map area.
650                 /// </summary>
651                 [
652
653         SRCategory("CategoryAttributeMapArea"),
654                 Bindable(true),
655                 SRDescription("DescriptionAttributeToolTip"),
656                 DefaultValue(""),
657 #if !Microsoft_CONTROL
658                 PersistenceMode(PersistenceMode.Attribute)
659 #endif
660                 ]
661                 public string ToolTip
662                 {
663                         set
664                         {
665                                 _toolTip = value;
666                         }
667                         get
668                         {
669                                 return _toolTip;
670                         }
671                 }
672
673                 /// <summary>
674         /// Gets or sets the URL of the map area.
675                 /// </summary>
676                 [
677                 SRCategory("CategoryAttributeMapArea"),
678                 Bindable(true),
679                 SRDescription("DescriptionAttributeUrl"),
680                 DefaultValue(""),
681 #if !Microsoft_CONTROL
682                 PersistenceMode(PersistenceMode.Attribute),
683         Editor(Editors.UrlValueEditor.Editor, Editors.UrlValueEditor.Base)
684 #endif
685                 ]
686                 public string Url
687                 {
688                         set
689                         {
690                                 _url = value;
691                         }
692                         get
693                         {
694                                 return _url;
695                         }
696                 }
697
698                 /// <summary>
699         /// Gets or sets the attributes of the map area.
700                 /// </summary>
701                 [
702         SRCategory("CategoryAttributeMapArea"),
703                 Bindable(true),
704                 SRDescription("DescriptionAttributeMapAreaAttributes"),
705                 DefaultValue(""),
706 #if !Microsoft_CONTROL
707                 PersistenceMode(PersistenceMode.Attribute)
708 #endif
709                 ]
710                 public string MapAreaAttributes
711                 {
712                         set
713                         {
714                                 _attributes = value;
715                         }
716                         get
717                         {
718                                 return _attributes;
719                         }
720         }
721
722         /// <summary>
723         /// Gets or sets the postback value which can be processed on click event.
724         /// </summary>
725         /// <value>The value which is passed to click event as argument.</value>
726         [DefaultValue("")]
727         [SRCategory(SR.Keys.CategoryAttributeMapArea)]
728         [SRDescription(SR.Keys.DescriptionAttributePostBackValue)]
729         public string PostBackValue 
730         {
731             get
732             {
733                 return this._postBackValue;
734             }
735             set
736             {
737                 this._postBackValue = value;
738             }
739         }
740
741
742         #endregion
743
744     }
745
746
747         /// <summary>
748     /// The MapAreasCollection class is a strongly typed collection of MapAreas.
749         /// </summary>
750         [
751         SRDescription("DescriptionAttributeMapAreasCollection_MapAreasCollection")
752         ]
753 #if ASPPERM_35
754         [AspNetHostingPermission(System.Security.Permissions.SecurityAction.InheritanceDemand, Level = AspNetHostingPermissionLevel.Minimal)]
755     [AspNetHostingPermission(System.Security.Permissions.SecurityAction.LinkDemand, Level = AspNetHostingPermissionLevel.Minimal)]
756 #endif
757     public class MapAreasCollection : ChartElementCollection<MapArea>
758         {
759
760         #region Constructors
761
762                 /// <summary>
763                 /// Public constructor.
764                 /// </summary>
765                 public MapAreasCollection()
766             : base(null)
767                 {
768                 }
769
770         #endregion 
771
772         #region Methods
773
774         /// <summary>
775         /// Insert new map area items into the collection.
776         /// </summary>
777         /// <param name="index">Index to insert at.</param>
778         /// <param name="toolTip">Tool tip.</param>
779         /// <param name="url">Jump URL.</param>
780         /// <param name="attributes">Other area attributes.</param>
781         /// <param name="postBackValue">The post back value associated with this item.</param>
782         /// <param name="path">Area coordinates as graphics path.</param>
783         /// <param name="absCoordinates">Absolute coordinates in the graphics path.</param>
784         /// <param name="graph">Chart graphics object.</param>
785                 internal void InsertPath(
786                         int index, 
787                         string toolTip, 
788                         string url,
789             string attributes,
790             string postBackValue, 
791                         GraphicsPath path, 
792                         bool absCoordinates,
793                         ChartGraphics graph) 
794                 {
795
796                         // If there is more then one graphical path split them and create 
797                         // image maps for every graphical path separately.
798                         GraphicsPathIterator iterator = new GraphicsPathIterator(path);
799
800                         // There is more then one path.
801                         if( iterator.SubpathCount > 1 )
802                         {
803                                 GraphicsPath subPath = new GraphicsPath();
804                                 while(iterator.NextMarker(subPath) > 0)
805                                 {
806                     InsertSubpath(index, toolTip, url, attributes, postBackValue, subPath, absCoordinates, graph);
807                                         subPath.Reset();
808                                 }
809                         }
810                         // There is only one path
811                         else
812                         {
813                 InsertSubpath(index, toolTip, url, attributes, postBackValue, path, absCoordinates, graph);
814                         }
815                 }
816
817         /// <summary>
818         /// Insert new map area item into the collection.
819         /// </summary>
820         /// <param name="index">Index to insert at.</param>
821         /// <param name="toolTip">Tool tip.</param>
822         /// <param name="url">Jump URL.</param>
823         /// <param name="attributes">Other area attributes.</param>
824         /// <param name="postBackValue">The post back value associated with this item.</param>
825         /// <param name="path">Area coordinates as graphics path.</param>
826         /// <param name="absCoordinates">Absolute coordinates in the graphics path.</param>
827         /// <param name="graph">Chart graphics object.</param>
828                 private void InsertSubpath(
829                         int index, 
830                         string toolTip, 
831                         string url,
832             string attributes,
833             string postBackValue, 
834                         GraphicsPath path, 
835                         bool absCoordinates,
836                         ChartGraphics graph)
837                 {
838                         if(path.PointCount > 0)
839                         {
840                                 // Flatten all curved lines
841                                 path.Flatten();
842
843                                 // Allocate array of floats
844                                 PointF[] pathPoints = path.PathPoints;
845                                 float[] coord = new float[pathPoints.Length * 2];
846
847                                 // Convert absolute coordinates to relative
848                                 if(absCoordinates)
849                                 {
850                                         for(int pointIndex = 0; pointIndex < pathPoints.Length; pointIndex++)
851                                         {
852                                                 pathPoints[pointIndex] = graph.GetRelativePoint( pathPoints[pointIndex] );
853                                         }
854                                 }
855
856                                 // Transfer path points
857                                 int     i = 0;
858                                 foreach(PointF point in pathPoints)
859                                 {
860                                         coord[i++] = point.X;
861                                         coord[i++] = point.Y;
862                                 }
863
864                                 // Add new area
865                 MapArea area = new MapArea(MapAreaShape.Polygon, toolTip, url, attributes, postBackValue, coord, null);
866                 area.IsCustom = false;
867                 this.Insert(index, area);
868                         }
869                 }
870
871         /// <summary>
872                 /// Removes all non custom map areas items from the collection.
873                 /// </summary>
874                 internal void RemoveNonCustom()
875                 {
876                         for(int index = 0; index < this.Count; index++)
877                         {
878                                 // Check the custom flag
879                                 if(!this[index].IsCustom)
880                                 {
881                                         this.RemoveAt(index);
882                                         --index;
883                                 }
884                         }
885                 }
886
887         #endregion
888         }
889
890 #endif
891 }