SvgElement.cs 34.2 KB
Newer Older
davescriven's avatar
davescriven committed
1
2
3
4
5
6
using System;
using System.Collections.Generic;
using System.ComponentModel;
using System.Drawing;
using System.Drawing.Drawing2D;
using System.Xml;
7
using System.Linq;
davescriven's avatar
davescriven committed
8
using Svg.Transforms;
9
using System.Reflection;
Tebjan Halm's avatar
Tebjan Halm committed
10
11
using System.Threading;
using System.Globalization;
davescriven's avatar
davescriven committed
12
13
14
15
16
17
18
19

namespace Svg
{
    /// <summary>
    /// The base class of which all SVG elements are derived from.
    /// </summary>
    public abstract class SvgElement : ISvgElement, ISvgTransformable, ICloneable
    {
20
        //optimization
21
        protected class PropertyAttributeTuple
22
        {
23
            public PropertyDescriptor Property;
24
25
            public SvgAttributeAttribute Attribute;
        }
26
27
28
29
30
31
32

        protected class EventAttributeTuple
        {
            public FieldInfo Event;
            public SvgAttributeAttribute Attribute;
        }

Tebjan Halm's avatar
Tebjan Halm committed
33
        //reflection cache
34
35
        private IEnumerable<PropertyAttributeTuple> _svgPropertyAttributes;
        private IEnumerable<EventAttributeTuple> _svgEventAttributes;
36

davescriven's avatar
davescriven committed
37
38
39
40
41
42
43
        internal SvgElement _parent;
        private string _elementName;
        private SvgAttributeCollection _attributes;
        private EventHandlerList _eventHandlers;
        private SvgElementCollection _children;
        private static readonly object _loadEventKey = new object();
        private Matrix _graphicsMatrix;
44
        private SvgCustomAttributeCollection _customAttributes;
davescriven's avatar
davescriven committed
45
46
47
48

        /// <summary>
        /// Gets the name of the element.
        /// </summary>
49
        protected internal string ElementName
davescriven's avatar
davescriven committed
50
        {
51
52
53
54
55
56
57
58
59
60
61
62
63
64
            get
            {
                if (string.IsNullOrEmpty(this._elementName))
                {
                    var attr = TypeDescriptor.GetAttributes(this).OfType<SvgElementAttribute>().SingleOrDefault();

                    if (attr != null)
                    {
                        this._elementName = attr.ElementName;
                    }
                }

                return this._elementName;
            }
65
            internal set { this._elementName = value; }
davescriven's avatar
davescriven committed
66
67
68
69
70
        }

        /// <summary>
        /// Gets or sets the content of the element.
        /// </summary>
Tebjan Halm's avatar
Tebjan Halm committed
71
        private string _content;
davescriven's avatar
davescriven committed
72
73
        public virtual string Content
        {
Tebjan Halm's avatar
Tebjan Halm committed
74
75
76
77
78
79
80
81
82
83
84
            get
            {
            	return _content;
            }
            set
            {
            	if(_content != null)
            	{
            		var oldVal = _content;
            		_content = value;
            		if(_content != oldVal)
tebjan's avatar
tebjan committed
85
            			OnContentChanged(new ContentEventArgs{ Content = value });
Tebjan Halm's avatar
Tebjan Halm committed
86
87
88
89
            	}
            	else
            	{
            		_content = value;
tebjan's avatar
tebjan committed
90
            		OnContentChanged(new ContentEventArgs{ Content = value });
Tebjan Halm's avatar
Tebjan Halm committed
91
92
            	}
            }
davescriven's avatar
davescriven committed
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
        }

        /// <summary>
        /// Gets an <see cref="EventHandlerList"/> of all events belonging to the element.
        /// </summary>
        protected virtual EventHandlerList Events
        {
            get { return this._eventHandlers; }
        }

        /// <summary>
        /// Occurs when the element is loaded.
        /// </summary>
        public event EventHandler Load
        {
            add { this.Events.AddHandler(_loadEventKey, value); }
            remove { this.Events.RemoveHandler(_loadEventKey, value); }
        }

        /// <summary>
        /// Gets a collection of all child <see cref="SvgElements"/>.
        /// </summary>
        public virtual SvgElementCollection Children
        {
            get { return this._children; }
        }

        /// <summary>
        /// Gets a value to determine whether the element has children.
        /// </summary>
        public virtual bool HasChildren()
        {
            return (this.Children.Count > 0);
        }

        /// <summary>
        /// Gets the parent <see cref="SvgElement"/>.
        /// </summary>
        /// <value>An <see cref="SvgElement"/> if one exists; otherwise null.</value>
        public virtual SvgElement Parent
        {
            get { return this._parent; }
        }

        /// <summary>
        /// Gets the owner <see cref="SvgDocument"/>.
        /// </summary>
        public virtual SvgDocument OwnerDocument
        {
142
143
144
145
146
147
148
149
150
151
152
153
154
155
        	get
        	{
        		if (this is SvgDocument)
        		{
        			return this as SvgDocument;
        		}
        		else
        		{
        			if(this.Parent != null)
        				return Parent.OwnerDocument;
        			else
        				return null;
        		}
        	}
davescriven's avatar
davescriven committed
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
        }

        /// <summary>
        /// Gets a collection of element attributes.
        /// </summary>
        protected internal virtual SvgAttributeCollection Attributes
        {
            get
            {
                if (this._attributes == null)
                {
                    this._attributes = new SvgAttributeCollection(this);
                }

                return this._attributes;
            }
        }

174
175
176
177
        /// <summary>
        /// Gets a collection of custom attributes
        /// </summary>
        public SvgCustomAttributeCollection CustomAttributes
178
179
180
181
        {
            get { return this._customAttributes; }
        }

182
183
184
185
186
        /// <summary>
        /// Applies the required transforms to <see cref="SvgRenderer"/>.
        /// </summary>
        /// <param name="renderer">The <see cref="SvgRenderer"/> to be transformed.</param>
        protected internal virtual void PushTransforms(SvgRenderer renderer)
davescriven's avatar
davescriven committed
187
        {
188
            _graphicsMatrix = renderer.Transform;
189

davescriven's avatar
davescriven committed
190
191
192
193
194
195
            // Return if there are no transforms
            if (this.Transforms == null || this.Transforms.Count == 0)
            {
                return;
            }

196
            Matrix transformMatrix = renderer.Transform;
davescriven's avatar
davescriven committed
197
198
199

            foreach (SvgTransform transformation in this.Transforms)
            {
200
                transformMatrix.Multiply(transformation.Matrix);
davescriven's avatar
davescriven committed
201
202
            }

203
            renderer.Transform = transformMatrix;
davescriven's avatar
davescriven committed
204
205
        }

206
207
208
209
210
        /// <summary>
        /// Removes any previously applied transforms from the specified <see cref="SvgRenderer"/>.
        /// </summary>
        /// <param name="renderer">The <see cref="SvgRenderer"/> that should have transforms removed.</param>
        protected internal virtual void PopTransforms(SvgRenderer renderer)
davescriven's avatar
davescriven committed
211
        {
212
            renderer.Transform = _graphicsMatrix;
davescriven's avatar
davescriven committed
213
214
215
            _graphicsMatrix = null;
        }

216
217
218
219
220
        /// <summary>
        /// Applies the required transforms to <see cref="SvgRenderer"/>.
        /// </summary>
        /// <param name="renderer">The <see cref="SvgRenderer"/> to be transformed.</param>
        void ISvgTransformable.PushTransforms(SvgRenderer renderer)
davescriven's avatar
davescriven committed
221
        {
222
            this.PushTransforms(renderer);
davescriven's avatar
davescriven committed
223
224
        }

225
226
227
228
229
        /// <summary>
        /// Removes any previously applied transforms from the specified <see cref="SvgRenderer"/>.
        /// </summary>
        /// <param name="renderer">The <see cref="SvgRenderer"/> that should have transforms removed.</param>
        void ISvgTransformable.PopTransforms(SvgRenderer renderer)
davescriven's avatar
davescriven committed
230
        {
231
            this.PopTransforms(renderer);
davescriven's avatar
davescriven committed
232
233
234
235
236
237
238
239
240
        }

        /// <summary>
        /// Gets or sets the element transforms.
        /// </summary>
        /// <value>The transforms.</value>
        [SvgAttribute("transform")]
        public SvgTransformCollection Transforms
        {
241
            get { return (this.Attributes.GetAttribute<SvgTransformCollection>("transform")); }
Tebjan Halm's avatar
Tebjan Halm committed
242
243
244
245
246
247
            set 
            { 
            	var old = this.Transforms;
            	if(old != null)
            		old.TransformChanged -= Attributes_AttributeChanged;
            	value.TransformChanged += Attributes_AttributeChanged;
248
            	this.Attributes["transform"] = value; 
Tebjan Halm's avatar
Tebjan Halm committed
249
            }
davescriven's avatar
davescriven committed
250
251
252
253
254
255
256
257
258
        }

        /// <summary>
        /// Gets or sets the ID of the element.
        /// </summary>
        /// <exception cref="SvgException">The ID is already used within the <see cref="SvgDocument"/>.</exception>
        [SvgAttribute("id")]
        public string ID
        {
259
            get { return this.Attributes.GetAttribute<string>("id"); }
davescriven's avatar
davescriven committed
260
261
            set
            {
262
263
264
                SetAndFixID(value, false);
            }
        }
davescriven's avatar
davescriven committed
265

266
267
268
269
270
271
272
        public void SetAndFixID(string value, bool autoFixID = true, Action<SvgElement, string, string> logElementOldIDNewID = null)
        {
            // Don't do anything if it hasn't changed
            if (string.Compare(this.ID, value) == 0)
            {
                return;
            }
davescriven's avatar
davescriven committed
273

274
275
276
277
            if (this.OwnerDocument != null)
            {
                this.OwnerDocument.IdManager.Remove(this);
            }
davescriven's avatar
davescriven committed
278

279
            this.Attributes["id"] = value;
280
281
282

            if (this.OwnerDocument != null)
            {
283
                this.OwnerDocument.IdManager.AddAndFixID(this, null, autoFixID, logElementOldIDNewID);
davescriven's avatar
davescriven committed
284
285
286
            }
        }

287
288
289
290
291
292
        /// <summary>
        /// Only used by the ID Manager
        /// </summary>
        /// <param name="newID"></param>
        internal void FixID(string newID)
        {
293
            this.Attributes["id"] = newID;
294
295
        }

296
297
298
299
300
301
        /// <summary>
        /// Called by the underlying <see cref="SvgElement"/> when an element has been added to the
        /// <see cref="Children"/> collection.
        /// </summary>
        /// <param name="child">The <see cref="SvgElement"/> that has been added.</param>
        /// <param name="index">An <see cref="int"/> representing the index where the element was added to the collection.</param>
302
        protected virtual void AddElement(SvgElement child, int index)
davescriven's avatar
davescriven committed
303
304
        {
        }
tebjan's avatar
tebjan committed
305
306
307
308
309
        
        /// <summary>
        /// Fired when an Element was added to the children of this Element
        /// </summary>
		public event EventHandler<ChildAddedEventArgs> ChildAdded;
davescriven's avatar
davescriven committed
310

311
312
313
314
315
        /// <summary>
        /// Calls the <see cref="AddElement"/> method with the specified parameters.
        /// </summary>
        /// <param name="child">The <see cref="SvgElement"/> that has been added.</param>
        /// <param name="index">An <see cref="int"/> representing the index where the element was added to the collection.</param>
davescriven's avatar
davescriven committed
316
317
        internal void OnElementAdded(SvgElement child, int index)
        {
318
            this.AddElement(child, index);
319
320
321
322
323
            SvgElement sibling = null;
            if(index < (Children.Count - 1))
            {
            	sibling = Children[index + 1];
            }
tebjan's avatar
tebjan committed
324
325
326
            var handler = ChildAdded;
            if(handler != null)
            {
327
            	handler(this, new ChildAddedEventArgs { NewChild = child, BeforeSibling = sibling });
tebjan's avatar
tebjan committed
328
            }
davescriven's avatar
davescriven committed
329
330
        }

331
332
333
334
335
        /// <summary>
        /// Called by the underlying <see cref="SvgElement"/> when an element has been removed from the
        /// <see cref="Children"/> collection.
        /// </summary>
        /// <param name="child">The <see cref="SvgElement"/> that has been removed.</param>
336
        protected virtual void RemoveElement(SvgElement child)
davescriven's avatar
davescriven committed
337
338
339
        {
        }

340
341
342
343
        /// <summary>
        /// Calls the <see cref="RemoveElement"/> method with the specified <see cref="SvgElement"/> as the parameter.
        /// </summary>
        /// <param name="child">The <see cref="SvgElement"/> that has been removed.</param>
davescriven's avatar
davescriven committed
344
345
        internal void OnElementRemoved(SvgElement child)
        {
346
            this.RemoveElement(child);
davescriven's avatar
davescriven committed
347
348
349
350
351
        }

        /// <summary>
        /// Initializes a new instance of the <see cref="SvgElement"/> class.
        /// </summary>
352
        public SvgElement()
davescriven's avatar
davescriven committed
353
354
355
356
        {
            this._children = new SvgElementCollection(this);
            this._eventHandlers = new EventHandlerList();
            this._elementName = string.Empty;
357
358
            this._customAttributes = new SvgCustomAttributeCollection(this);
            
Tebjan Halm's avatar
Tebjan Halm committed
359
360
            Transforms = new SvgTransformCollection();
            
361
362
363
            //subscribe to attribute events
            Attributes.AttributeChanged += Attributes_AttributeChanged;
            CustomAttributes.AttributeChanged += Attributes_AttributeChanged;
364

365
366
367
368
369
            //find svg attribute descriptions
            _svgPropertyAttributes = from PropertyDescriptor a in TypeDescriptor.GetProperties(this)
                            let attribute = a.Attributes[typeof(SvgAttributeAttribute)] as SvgAttributeAttribute
                            where attribute != null
                            select new PropertyAttributeTuple { Property = a, Attribute = attribute };
Tebjan Halm's avatar
Tebjan Halm committed
370

371
            _svgEventAttributes = from EventDescriptor a in TypeDescriptor.GetEvents(this)
372
373
                            let attribute = a.Attributes[typeof(SvgAttributeAttribute)] as SvgAttributeAttribute
                            where attribute != null
374
                            select new EventAttributeTuple { Event = a.ComponentType.GetField(a.Name, BindingFlags.Instance | BindingFlags.NonPublic), Attribute = attribute };
davescriven's avatar
davescriven committed
375

376
        }
377

378
379
380
381
382
383
        //dispatch attribute event
        void Attributes_AttributeChanged(object sender, AttributeEventArgs e)
        {
        	OnAttributeChanged(e);
        }

384
385
		public virtual void InitialiseFromXML(XmlTextReader reader, SvgDocument document)
		{
386
            throw new NotImplementedException();
387
388
389
		}


390
391
392
393
394
        /// <summary>
        /// Renders this element to the <see cref="SvgRenderer"/>.
        /// </summary>
        /// <param name="renderer">The <see cref="SvgRenderer"/> that the element should use to render itself.</param>
        public void RenderElement(SvgRenderer renderer)
davescriven's avatar
davescriven committed
395
        {
396
            this.Render(renderer);
davescriven's avatar
davescriven committed
397
398
399
400
        }

        public void WriteElement(XmlTextWriter writer)
        {
Tebjan Halm's avatar
Tebjan Halm committed
401
402
403
404
            //Save previous culture and switch to invariant for writing
            var previousCulture = Thread.CurrentThread.CurrentCulture;
            Thread.CurrentThread.CurrentCulture = CultureInfo.InvariantCulture;

davescriven's avatar
davescriven committed
405
            this.Write(writer);
Tebjan Halm's avatar
Tebjan Halm committed
406
407
408

            //Switch culture back
            Thread.CurrentThread.CurrentCulture = previousCulture;
davescriven's avatar
davescriven committed
409
410
411
412
413
414
415
        }

        protected virtual void WriteStartElement(XmlTextWriter writer)
        {
            if (this.ElementName != String.Empty)
            {
                writer.WriteStartElement(this.ElementName);
Tebjan Halm's avatar
Tebjan Halm committed
416
417
                if (this.ElementName == "svg")
                {
418
419
420
421
422
423
424
425
426
					foreach (var ns in SvgAttributeAttribute.Namespaces)
					{
						if (string.IsNullOrEmpty(ns.Key))
							writer.WriteAttributeString("xmlns", ns.Value);
						else
							writer.WriteAttributeString("xmlns:" + ns.Key, ns.Value);
					}
					writer.WriteAttributeString("version", "1.1");
				}
davescriven's avatar
davescriven committed
427
428
429
430
431
432
433
434
435
436
437
438
439
440
            }
            this.WriteAttributes(writer);
        }

        protected virtual void WriteEndElement(XmlTextWriter writer)
        {
            if (this.ElementName != String.Empty)
            {
                writer.WriteEndElement();
            }
        }

        protected virtual void WriteAttributes(XmlTextWriter writer)
        {
441
442
            //properties
            foreach (var attr in _svgPropertyAttributes)
443
444
445
446
447
            {
                if (attr.Property.Converter.CanConvertTo(typeof(string)))
                {
                    object propertyValue = attr.Property.GetValue(this);

448
449
450
                    var forceWrite = false;
                    if ((attr.Attribute.Name == "fill") && (Parent != null))
                    {
451
                    	if(propertyValue == SvgColourServer.NotSet) continue;
452
                    	
453
454
                        object parentValue;
                        if (TryResolveParentAttributeValue(attr.Attribute.Name, out parentValue))
455
                        {
456
457
                            if ((parentValue == propertyValue) 
                                || ((parentValue != null) &&  parentValue.Equals(propertyValue)))
458
                                continue;
459
                            
460
461
462
463
                            forceWrite = true;
                        }
                    }

464
465
                    if (propertyValue != null)
                    {
Tebjan Halm's avatar
Tebjan Halm committed
466
                        var type = propertyValue.GetType();
467
                        string value = (string)attr.Property.Converter.ConvertTo(propertyValue, typeof(string));
Tebjan Halm's avatar
Tebjan Halm committed
468

469
                        if (!SvgDefaults.IsDefault(attr.Attribute.Name, value) || forceWrite)
Tebjan Halm's avatar
Tebjan Halm committed
470
                        {
471
                            writer.WriteAttributeString(attr.Attribute.NamespaceAndName, value);
Tebjan Halm's avatar
Tebjan Halm committed
472
473
474
475
                        }
                    }
                    else if(attr.Attribute.Name == "fill") //if fill equals null, write 'none'
                    {
476
477
                        string value = (string)attr.Property.Converter.ConvertTo(propertyValue, typeof(string));
                        writer.WriteAttributeString(attr.Attribute.NamespaceAndName, value);
478
479
480
                    }
                }
            }
481

Tebjan Halm's avatar
Tebjan Halm committed
482
            
483
            //events
Tebjan Halm's avatar
Tebjan Halm committed
484
            if(AutoPublishEvents)
485
            {
486
487
488
489
	            foreach (var attr in _svgEventAttributes)
	            {
	                var evt = attr.Event.GetValue(this);
	                
490
	                //if someone has registered publish the attribute
Tebjan Halm's avatar
Tebjan Halm committed
491
	                if (evt != null && !string.IsNullOrEmpty(this.ID))
492
493
494
495
	                {
	                    writer.WriteAttributeString(attr.Attribute.Name, this.ID + "/" + attr.Attribute.Name);
	                }
	            }
496
497
            }

498
499
500
501
502
            //add the custom attributes
            foreach (var item in this._customAttributes)
            {
                writer.WriteAttributeString(item.Key, item.Value);
            }
503
        }
Tebjan Halm's avatar
Tebjan Halm committed
504
505
        
        public bool AutoPublishEvents = true;
506

507
        private bool TryResolveParentAttributeValue(string attributeKey, out object parentAttributeValue)
508
        {
509
            parentAttributeValue = null;
510

511
            attributeKey = char.ToUpper(attributeKey[0]) + attributeKey.Substring(1);
512
513

            var currentParent = Parent;
514
            var resolved = false;
515
516
517
518
            while (currentParent != null)
            {
                if (currentParent.Attributes.ContainsKey(attributeKey))
                {
519
520
521
                    resolved = true;
                    parentAttributeValue = currentParent.Attributes[attributeKey];
                    if (parentAttributeValue != null)
522
523
524
525
                        break;
                }
                currentParent = currentParent.Parent;
            }
526

527
            return resolved;
davescriven's avatar
davescriven committed
528
529
530
531
532
533
534
535
536
537
538
539
540
541
        }

        protected virtual void Write(XmlTextWriter writer)
        {
            if (this.ElementName != String.Empty)
            {
                this.WriteStartElement(writer);
                this.WriteChildren(writer);
                this.WriteEndElement(writer);
            }
        }

        protected virtual void WriteChildren(XmlTextWriter writer)
        {
Tebjan Halm's avatar
Tebjan Halm committed
542
543
544
545
546
            //write the content
            if(!String.IsNullOrEmpty(this.Content))
                writer.WriteString(this.Content);

            //write all children
davescriven's avatar
davescriven committed
547
548
549
550
551
552
553
            foreach (SvgElement child in this.Children)
            {
                child.Write(writer);
            }
        }

        /// <summary>
554
        /// Renders the <see cref="SvgElement"/> and contents to the specified <see cref="SvgRenderer"/> object.
davescriven's avatar
davescriven committed
555
        /// </summary>
556
557
        /// <param name="renderer">The <see cref="SvgRenderer"/> object to render to.</param>
        protected virtual void Render(SvgRenderer renderer)
davescriven's avatar
davescriven committed
558
        {
559
560
561
            this.PushTransforms(renderer);
            this.RenderChildren(renderer);
            this.PopTransforms(renderer);
davescriven's avatar
davescriven committed
562
563
        }

564
565
566
567
568
        /// <summary>
        /// Renders the children of this <see cref="SvgElement"/>.
        /// </summary>
        /// <param name="renderer">The <see cref="SvgRenderer"/> to render the child <see cref="SvgElement"/>s to.</param>
        protected virtual void RenderChildren(SvgRenderer renderer)
davescriven's avatar
davescriven committed
569
570
571
        {
            foreach (SvgElement element in this.Children)
            {
572
                element.Render(renderer);
davescriven's avatar
davescriven committed
573
574
575
            }
        }

576
577
578
579
580
        /// <summary>
        /// Renders the <see cref="SvgElement"/> and contents to the specified <see cref="SvgRenderer"/> object.
        /// </summary>
        /// <param name="renderer">The <see cref="SvgRenderer"/> object to render to.</param>
        void ISvgElement.Render(SvgRenderer renderer)
davescriven's avatar
davescriven committed
581
        {
582
            this.Render(renderer);
davescriven's avatar
davescriven committed
583
        }
Tebjan Halm's avatar
Tebjan Halm committed
584
585
586
587
588
589
590
591
        
        /// <summary>
        /// Recursive method to add up the paths of all children
        /// </summary>
        /// <param name="elem"></param>
        /// <param name="path"></param>
        protected void AddPaths(SvgElement elem, GraphicsPath path)
        {
Tebjan Halm's avatar
Tebjan Halm committed
592
        	foreach(var child in elem.Children)
Tebjan Halm's avatar
Tebjan Halm committed
593
        	{
Tebjan Halm's avatar
Tebjan Halm committed
594
        		if (child is SvgVisualElement)
Tebjan Halm's avatar
Tebjan Halm committed
595
        		{
Tebjan Halm's avatar
Tebjan Halm committed
596
        			if(!(child is SvgGroup))
597
        			{
Tebjan Halm's avatar
Tebjan Halm committed
598
        				var childPath = ((SvgVisualElement)child).Path;
599
        				
Tebjan Halm's avatar
Tebjan Halm committed
600
601
602
603
604
605
606
607
        				if (childPath != null)
        				{
        					childPath = (GraphicsPath)childPath.Clone();
        					if(child.Transforms != null)
        						childPath.Transform(child.Transforms.GetMatrix());
        					
        					path.AddPath(childPath, false);
        				}
608
        			}
Tebjan Halm's avatar
Tebjan Halm committed
609
        		}
Tebjan Halm's avatar
Tebjan Halm committed
610
        			
Tebjan Halm's avatar
Tebjan Halm committed
611
        		AddPaths(child, path);
Tebjan Halm's avatar
Tebjan Halm committed
612
        	}
Tebjan Halm's avatar
Tebjan Halm committed
613
614
615
616
617
618
619
620
621
622
623
624
625
626
627
628
629
630
631
632
633
634
635
636
637
638
639
640
641
642
643
644
645
646
647
648
649
        }
        
        /// <summary>
        /// Recursive method to add up the paths of all children
        /// </summary>
        /// <param name="elem"></param>
        /// <param name="path"></param>
        protected GraphicsPath GetPaths(SvgElement elem)
        {
        	var ret = new GraphicsPath();
        	
        	foreach(var child in elem.Children)
        	{
        		if (child is SvgVisualElement)
        		{
        			if(!(child is SvgGroup))
        			{
        				var childPath = ((SvgVisualElement)child).Path;
        				
        				if (childPath != null)
        				{
        					childPath = (GraphicsPath)childPath.Clone();
        					if(child.Transforms != null)
        						childPath.Transform(child.Transforms.GetMatrix());
        					
        					ret.AddPath(childPath, false);
        				}
        			}
        			else
        			{
        				var childPath = GetPaths(child);
        				if(child.Transforms != null)
        					childPath.Transform(child.Transforms.GetMatrix());
        			}
        		}
        			
        	}
Tebjan Halm's avatar
Tebjan Halm committed
650
        	
Tebjan Halm's avatar
Tebjan Halm committed
651
        	return ret;
Tebjan Halm's avatar
Tebjan Halm committed
652
        }
davescriven's avatar
davescriven committed
653

654
655
656
657
658
659
        /// <summary>
        /// Creates a new object that is a copy of the current instance.
        /// </summary>
        /// <returns>
        /// A new object that is a copy of this instance.
        /// </returns>
davescriven's avatar
davescriven committed
660
661
662
663
        public virtual object Clone()
        {
            return this.MemberwiseClone();
        }
664
665
666
667
668
669

    	public abstract SvgElement DeepCopy();

		public virtual SvgElement DeepCopy<T>() where T : SvgElement, new()
		{
			var newObj = new T();
670
			newObj.ID = this.ID;
671
672
			newObj.Content = this.Content;
			newObj.ElementName = this.ElementName;
673
			
674
675
676
677
678
679
680
681
682
683
684
685
686
687
//			if (this.Parent != null)
	//			this.Parent.Children.Add(newObj);

			if (this.Transforms != null)
			{
				newObj.Transforms = new SvgTransformCollection();
				foreach (var transform in this.Transforms)
					newObj.Transforms.Add(transform.Clone() as SvgTransform);
			}

			foreach (var child in this.Children)
			{
				newObj.Children.Add(child.DeepCopy());
			}
688
			
689
690
691
692
693
694
695
696
697
698
699
700
701
702
703
704
705
706
707
			foreach (var attr in this._svgEventAttributes)
			{
				var evt = attr.Event.GetValue(this);
				
				//if someone has registered also register here
				if (evt != null)
				{
					if(attr.Event.Name == "MouseDown")
						newObj.MouseDown += delegate {  };
					else if (attr.Event.Name == "MouseUp")
						newObj.MouseUp += delegate {  };
					else if (attr.Event.Name == "MouseOver")
						newObj.MouseOver += delegate {  };
					else if (attr.Event.Name == "MouseOut")
						newObj.MouseOut += delegate {  };
					else if (attr.Event.Name == "MouseMove")
						newObj.MouseMove += delegate {  };
					else if (attr.Event.Name == "MouseScroll")
						newObj.MouseScroll += delegate {  };
Tebjan Halm's avatar
Tebjan Halm committed
708
709
					else if (attr.Event.Name == "Click")
						newObj.Click += delegate {  };
joreg's avatar
joreg committed
710
711
					else if (attr.Event.Name == "Change")
						newObj.Change += delegate {  };
712
713
714
				}
			}
			
715
716
717
718
719
720
721
			if(this._customAttributes.Count > 0)
			{
				foreach (var element in _customAttributes) 
				{
					newObj.CustomAttributes.Add(element.Key, element.Value);
				}
			}
722
723
				
			return newObj;
Tebjan Halm's avatar
Tebjan Halm committed
724
        }
725
726
727
728
729
730
731
732
733
734
735
736
737
738
		
		/// <summary>
        /// Fired when an Atrribute of this Element has changed
        /// </summary>
		public event EventHandler<AttributeEventArgs> AttributeChanged;
		
		protected void OnAttributeChanged(AttributeEventArgs args)
		{
			var handler = AttributeChanged;
			if(handler != null)
			{
				handler(this, args);
			}
		}
tebjan's avatar
tebjan committed
739
740
741
742
743
744
745
746
747
748
749
750
751
752
		
		/// <summary>
        /// Fired when an Atrribute of this Element has changed
        /// </summary>
		public event EventHandler<ContentEventArgs> ContentChanged;
		
		protected void OnContentChanged(ContentEventArgs args)
		{
			var handler = ContentChanged;
			if(handler != null)
			{
				handler(this, args);
			}
		}
Tebjan Halm's avatar
Tebjan Halm committed
753
754
755
756
757
758
759

        #region graphical EVENTS

        /*  
            onfocusin = "<anything>"
            onfocusout = "<anything>"
            onactivate = "<anything>"
760
761
762
763
764
                onclick = "<anything>"
                onmousedown = "<anything>"
                onmouseup = "<anything>"
                onmouseover = "<anything>"
                onmousemove = "<anything>"
765
            	onmouseout = "<anything>" 
Tebjan Halm's avatar
Tebjan Halm committed
766
767
         */

768
769
770
771
772
773
774
        /// <summary>
        /// Use this method to provide your implementation ISvgEventCaller which can register Actions 
        /// and call them if one of the events occurs. Make sure, that your SvgElement has a unique ID.
        /// </summary>
        /// <param name="caller"></param>
        public void RegisterEvents(ISvgEventCaller caller)
        {
Tebjan Halm's avatar
Tebjan Halm committed
775
            if (caller != null && !string.IsNullOrEmpty(this.ID))
776
777
778
            {
                var rpcID = this.ID + "/";

Tebjan Halm's avatar
Tebjan Halm committed
779
780
781
782
783
784
785
786
                caller.RegisterAction<float, float, int, int, string>(rpcID + "onclick", OnClick);
                caller.RegisterAction<float, float, int, int, string>(rpcID + "onmousedown", OnMouseDown);
                caller.RegisterAction<float, float, int, string>(rpcID + "onmouseup", OnMouseUp);
                caller.RegisterAction<float, float, string>(rpcID + "onmousemove", OnMouseMove);
                caller.RegisterAction<float, string>(rpcID + "onmousescroll", OnMouseScroll);
                caller.RegisterAction<string>(rpcID + "onmouseover", OnMouseOver);
                caller.RegisterAction<string>(rpcID + "onmouseout", OnMouseOut);
                caller.RegisterAction<string, string>(rpcID + "onchange", OnChange);
787
788
            }
        }
789
790
791
792
793
794
795
        
        /// <summary>
        /// Use this method to provide your implementation ISvgEventCaller to unregister Actions
        /// </summary>
        /// <param name="caller"></param>
        public void UnregisterEvents(ISvgEventCaller caller)
        {
Tebjan Halm's avatar
Tebjan Halm committed
796
        	if (caller != null && !string.IsNullOrEmpty(this.ID))
797
798
799
800
801
802
803
        	{
        		var rpcID = this.ID + "/";

        		caller.UnregisterAction(rpcID + "onclick");
        		caller.UnregisterAction(rpcID + "onmousedown");
        		caller.UnregisterAction(rpcID + "onmouseup");
        		caller.UnregisterAction(rpcID + "onmousemove");
joreg's avatar
joreg committed
804
        		caller.UnregisterAction(rpcID + "onmousescroll");
805
806
        		caller.UnregisterAction(rpcID + "onmouseover");
        		caller.UnregisterAction(rpcID + "onmouseout");
joreg's avatar
joreg committed
807
        		caller.UnregisterAction(rpcID + "onchange");
808
809
        	}
        }
810

Tebjan Halm's avatar
Tebjan Halm committed
811
812
813
        [SvgAttribute("onclick")]
        public event EventHandler<MouseArg> Click;

814
815
816
817
818
        [SvgAttribute("onmousedown")]
        public event EventHandler<MouseArg> MouseDown;

        [SvgAttribute("onmouseup")]
        public event EventHandler<MouseArg> MouseUp;
819
820
821
        
        [SvgAttribute("onmousemove")]
        public event EventHandler<PointArg> MouseMove;
822

joreg's avatar
joreg committed
823
824
825
        [SvgAttribute("onmousescroll")]
        public event EventHandler<PointArg> MouseScroll;
        
826
        [SvgAttribute("onmouseover")]
Tebjan Halm's avatar
Tebjan Halm committed
827
        public event EventHandler<SVGArg> MouseOver;
828
829

        [SvgAttribute("onmouseout")]
Tebjan Halm's avatar
Tebjan Halm committed
830
        public event EventHandler<SVGArg> MouseOut;
joreg's avatar
joreg committed
831
832
833
        
        [SvgAttribute("onchange")]
        public event EventHandler<StringArg> Change;
834

835
        //click
Tebjan Halm's avatar
Tebjan Halm committed
836
        protected void OnClick(float x, float y, int button, int clickCount, string sessionID)
Tebjan Halm's avatar
Tebjan Halm committed
837
        {
Tebjan Halm's avatar
Tebjan Halm committed
838
        	RaiseMouseClick(this, new MouseArg { x = x, y = y, Button = button, ClickCount = clickCount, SessionID = sessionID });
839
840
841
842
843
844
845
846
        }
        
        protected void RaiseMouseClick(object sender, MouseArg e)
        {
        	var handler = Click;
        	if (handler != null)
        	{
        		handler(sender, e);
847
848
849
            }
        }

850
        //down
Tebjan Halm's avatar
Tebjan Halm committed
851
        protected void OnMouseDown(float x, float y, int button, int clickCount, string sessionID)
852
        {
Tebjan Halm's avatar
Tebjan Halm committed
853
        	RaiseMouseDown(this, new MouseArg { x = x, y = y, Button = button, ClickCount = clickCount, SessionID = sessionID });
Tebjan Halm's avatar
Tebjan Halm committed
854
855
856
857
858
        }
        
        protected void RaiseMouseDown(object sender, MouseArg e)
        {
        	var handler = MouseDown;
859
860
            if (handler != null)
            {
Tebjan Halm's avatar
Tebjan Halm committed
861
                handler(sender, e);
862
863
864
            }
        }

865
        //up
Tebjan Halm's avatar
Tebjan Halm committed
866
        protected void OnMouseUp(float x, float y, int button, string sessionID)
867
        {
Tebjan Halm's avatar
Tebjan Halm committed
868
        	RaiseMouseUp(this, new MouseArg { x = x, y = y, Button = button, SessionID = sessionID });
869
870
871
872
873
        }
        
        protected void RaiseMouseUp(object sender, MouseArg e)
        {
        	var handler = MouseUp;
874
875
            if (handler != null)
            {
876
                handler(sender, e);
877
878
            }
        }
879
880
        
        //move
Tebjan Halm's avatar
Tebjan Halm committed
881
        protected void OnMouseMove(float x, float y, string sessionID)
882
        {
Tebjan Halm's avatar
Tebjan Halm committed
883
        	RaiseMouseMove(this, new PointArg { x = x, y = y, SessionID = sessionID });
884
885
886
887
888
        }
        
        protected void RaiseMouseMove(object sender, PointArg e)
        {
        	var handler = MouseMove;
889
890
            if (handler != null)
            {
891
                handler(sender, e);
892
893
            }
        }
joreg's avatar
joreg committed
894
895
        
        //scroll
Tebjan Halm's avatar
Tebjan Halm committed
896
        protected void OnMouseScroll(float y, string sessionID)
joreg's avatar
joreg committed
897
        {
Tebjan Halm's avatar
Tebjan Halm committed
898
        	RaiseMouseScroll(this, new PointArg { x = 0, y = y, SessionID = sessionID});
joreg's avatar
joreg committed
899
900
901
902
903
904
905
906
907
908
        }
        
        protected void RaiseMouseScroll(object sender, PointArg e)
        {
        	var handler = MouseScroll;
            if (handler != null)
            {
                handler(sender, e);
            }
        }
909

910
		//over        
Tebjan Halm's avatar
Tebjan Halm committed
911
        protected void OnMouseOver(string sessionID)
912
        {
Tebjan Halm's avatar
Tebjan Halm committed
913
        	RaiseMouseOver(this, new SVGArg{ SessionID = sessionID });
914
915
        }
        
Tebjan Halm's avatar
Tebjan Halm committed
916
        protected void RaiseMouseOver(object sender, SVGArg args)
917
918
        {
        	var handler = MouseOver;
919
920
            if (handler != null)
            {
Tebjan Halm's avatar
Tebjan Halm committed
921
                handler(sender, args);
Tebjan Halm's avatar
Tebjan Halm committed
922
923
924
            }
        }

925
        //out
Tebjan Halm's avatar
Tebjan Halm committed
926
        protected void OnMouseOut(string sessionID)
927
        {
Tebjan Halm's avatar
Tebjan Halm committed
928
        	RaiseMouseOut(this, new SVGArg{ SessionID = sessionID });
929
930
        }
        
Tebjan Halm's avatar
Tebjan Halm committed
931
        protected void RaiseMouseOut(object sender, SVGArg args)
932
        {
933
        	var handler = MouseOut;
934
935
            if (handler != null)
            {
Tebjan Halm's avatar
Tebjan Halm committed
936
                handler(sender, args);
937
938
            }
        }
joreg's avatar
joreg committed
939
940
        
        //change
Tebjan Halm's avatar
Tebjan Halm committed
941
        protected void OnChange(string newString, string sessionID)
joreg's avatar
joreg committed
942
        {
Tebjan Halm's avatar
Tebjan Halm committed
943
        	RaiseChange(this, new StringArg {s = newString, SessionID = sessionID});
joreg's avatar
joreg committed
944
945
946
947
948
949
950
951
952
953
        }
        
        protected void RaiseChange(object sender, StringArg s)
        {
        	var handler = Change;
            if (handler != null)
            {
                handler(sender, s);
            }
        }
Tebjan Halm's avatar
Tebjan Halm committed
954
955
956
957

        #endregion graphical EVENTS

    }
958
    
Tebjan Halm's avatar
Tebjan Halm committed
959
960
961
962
963
964
    public class SVGArg : EventArgs
    {
    	public string SessionID;
    }
    	
    
965
966
967
    /// <summary>
    /// Describes the Attribute which was set
    /// </summary>
Tebjan Halm's avatar
Tebjan Halm committed
968
    public class AttributeEventArgs : SVGArg
969
970
971
972
    {
    	public string Attribute;
    	public object Value;
    }
tebjan's avatar
tebjan committed
973
    
tebjan's avatar
tebjan committed
974
975
976
977
978
979
980
981
    /// <summary>
    /// Content of this whas was set
    /// </summary>
    public class ContentEventArgs : SVGArg
    {
    	public string Content;
    }
    
tebjan's avatar
tebjan committed
982
983
984
985
986
987
    /// <summary>
    /// Describes the Attribute which was set
    /// </summary>
    public class ChildAddedEventArgs : SVGArg
    {
    	public SvgElement NewChild;
988
    	public SvgElement BeforeSibling;
tebjan's avatar
tebjan committed
989
    }
Tebjan Halm's avatar
Tebjan Halm committed
990

991
992
993
    //deriving class registers event actions and calls the actions if the event occurs
    public interface ISvgEventCaller
    {
994
        void RegisterAction(string rpcID, Action action);
995
996
997
998
        void RegisterAction<T1>(string rpcID, Action<T1> action);
        void RegisterAction<T1, T2>(string rpcID, Action<T1, T2> action);
        void RegisterAction<T1, T2, T3>(string rpcID, Action<T1, T2, T3> action);
        void RegisterAction<T1, T2, T3, T4>(string rpcID, Action<T1, T2, T3, T4> action);
Tebjan Halm's avatar
Tebjan Halm committed
999
        void RegisterAction<T1, T2, T3, T4, T5>(string rpcID, Action<T1, T2, T3, T4, T5> action);
1000
        void UnregisterAction(string rpcID);