SvgElement.cs 34 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);
tebjan's avatar
tebjan committed
319
320
321
322
323
            var handler = ChildAdded;
            if(handler != null)
            {
            	handler(this, new ChildAddedEventArgs { NewChild = child });
            }
davescriven's avatar
davescriven committed
324
325
        }

326
327
328
329
330
        /// <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>
331
        protected virtual void RemoveElement(SvgElement child)
davescriven's avatar
davescriven committed
332
333
334
        {
        }

335
336
337
338
        /// <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
339
340
        internal void OnElementRemoved(SvgElement child)
        {
341
            this.RemoveElement(child);
davescriven's avatar
davescriven committed
342
343
344
345
346
        }

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

360
361
362
363
364
            //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
365

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

371
        }
372

373
374
375
376
377
378
        //dispatch attribute event
        void Attributes_AttributeChanged(object sender, AttributeEventArgs e)
        {
        	OnAttributeChanged(e);
        }

379
380
		public virtual void InitialiseFromXML(XmlTextReader reader, SvgDocument document)
		{
381
            throw new NotImplementedException();
382
383
384
		}


385
386
387
388
389
        /// <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
390
        {
391
            this.Render(renderer);
davescriven's avatar
davescriven committed
392
393
394
395
        }

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

davescriven's avatar
davescriven committed
400
            this.Write(writer);
Tebjan Halm's avatar
Tebjan Halm committed
401
402
403

            //Switch culture back
            Thread.CurrentThread.CurrentCulture = previousCulture;
davescriven's avatar
davescriven committed
404
405
406
407
408
409
410
        }

        protected virtual void WriteStartElement(XmlTextWriter writer)
        {
            if (this.ElementName != String.Empty)
            {
                writer.WriteStartElement(this.ElementName);
Tebjan Halm's avatar
Tebjan Halm committed
411
412
                if (this.ElementName == "svg")
                {
413
414
415
416
417
418
419
420
421
					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
422
423
424
425
426
427
428
429
430
431
432
433
434
435
            }
            this.WriteAttributes(writer);
        }

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

        protected virtual void WriteAttributes(XmlTextWriter writer)
        {
436
437
            //properties
            foreach (var attr in _svgPropertyAttributes)
438
439
440
441
442
            {
                if (attr.Property.Converter.CanConvertTo(typeof(string)))
                {
                    object propertyValue = attr.Property.GetValue(this);

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

459
460
                    if (propertyValue != null)
                    {
Tebjan Halm's avatar
Tebjan Halm committed
461
                        var type = propertyValue.GetType();
462
                        string value = (string)attr.Property.Converter.ConvertTo(propertyValue, typeof(string));
Tebjan Halm's avatar
Tebjan Halm committed
463

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

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

493
494
495
496
497
            //add the custom attributes
            foreach (var item in this._customAttributes)
            {
                writer.WriteAttributeString(item.Key, item.Value);
            }
498
        }
Tebjan Halm's avatar
Tebjan Halm committed
499
500
        
        public bool AutoPublishEvents = true;
501

502
        private bool TryResolveParentAttributeValue(string attributeKey, out object parentAttributeValue)
503
        {
504
            parentAttributeValue = null;
505

506
            attributeKey = char.ToUpper(attributeKey[0]) + attributeKey.Substring(1);
507
508

            var currentParent = Parent;
509
            var resolved = false;
510
511
512
513
            while (currentParent != null)
            {
                if (currentParent.Attributes.ContainsKey(attributeKey))
                {
514
515
516
                    resolved = true;
                    parentAttributeValue = currentParent.Attributes[attributeKey];
                    if (parentAttributeValue != null)
517
518
519
520
                        break;
                }
                currentParent = currentParent.Parent;
            }
521

522
            return resolved;
davescriven's avatar
davescriven committed
523
524
525
526
527
528
529
530
531
532
533
534
535
536
        }

        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
537
538
539
540
541
            //write the content
            if(!String.IsNullOrEmpty(this.Content))
                writer.WriteString(this.Content);

            //write all children
davescriven's avatar
davescriven committed
542
543
544
545
546
547
548
            foreach (SvgElement child in this.Children)
            {
                child.Write(writer);
            }
        }

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

559
560
561
562
563
        /// <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
564
565
566
        {
            foreach (SvgElement element in this.Children)
            {
567
                element.Render(renderer);
davescriven's avatar
davescriven committed
568
569
570
            }
        }

571
572
573
574
575
        /// <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
576
        {
577
            this.Render(renderer);
davescriven's avatar
davescriven committed
578
        }
Tebjan Halm's avatar
Tebjan Halm committed
579
580
581
582
583
584
585
586
        
        /// <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
587
        	foreach(var child in elem.Children)
Tebjan Halm's avatar
Tebjan Halm committed
588
        	{
Tebjan Halm's avatar
Tebjan Halm committed
589
        		if (child is SvgVisualElement)
Tebjan Halm's avatar
Tebjan Halm committed
590
        		{
Tebjan Halm's avatar
Tebjan Halm committed
591
        			if(!(child is SvgGroup))
592
        			{
Tebjan Halm's avatar
Tebjan Halm committed
593
        				var childPath = ((SvgVisualElement)child).Path;
594
        				
Tebjan Halm's avatar
Tebjan Halm committed
595
596
597
598
599
600
601
602
        				if (childPath != null)
        				{
        					childPath = (GraphicsPath)childPath.Clone();
        					if(child.Transforms != null)
        						childPath.Transform(child.Transforms.GetMatrix());
        					
        					path.AddPath(childPath, false);
        				}
603
        			}
Tebjan Halm's avatar
Tebjan Halm committed
604
        		}
Tebjan Halm's avatar
Tebjan Halm committed
605
        			
Tebjan Halm's avatar
Tebjan Halm committed
606
        		AddPaths(child, path);
Tebjan Halm's avatar
Tebjan Halm committed
607
        	}
Tebjan Halm's avatar
Tebjan Halm committed
608
609
610
611
612
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
        }
        
        /// <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
645
        	
Tebjan Halm's avatar
Tebjan Halm committed
646
        	return ret;
Tebjan Halm's avatar
Tebjan Halm committed
647
        }
davescriven's avatar
davescriven committed
648

649
650
651
652
653
654
        /// <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
655
656
657
658
        public virtual object Clone()
        {
            return this.MemberwiseClone();
        }
659
660
661
662
663
664

    	public abstract SvgElement DeepCopy();

		public virtual SvgElement DeepCopy<T>() where T : SvgElement, new()
		{
			var newObj = new T();
665
			newObj.ID = this.ID;
666
667
			newObj.Content = this.Content;
			newObj.ElementName = this.ElementName;
668
			
669
670
671
672
673
674
675
676
677
678
679
680
681
682
//			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());
			}
683
			
684
685
686
687
688
689
690
691
692
693
694
695
696
697
698
699
700
701
702
			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
703
704
					else if (attr.Event.Name == "Click")
						newObj.Click += delegate {  };
joreg's avatar
joreg committed
705
706
					else if (attr.Event.Name == "Change")
						newObj.Change += delegate {  };
707
708
709
				}
			}
			
710
711
712
713
714
715
716
			if(this._customAttributes.Count > 0)
			{
				foreach (var element in _customAttributes) 
				{
					newObj.CustomAttributes.Add(element.Key, element.Value);
				}
			}
717
718
				
			return newObj;
Tebjan Halm's avatar
Tebjan Halm committed
719
        }
720
721
722
723
724
725
726
727
728
729
730
731
732
733
		
		/// <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
734
735
736
737
738
739
740
741
742
743
744
745
746
747
		
		/// <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
748
749
750
751
752
753
754

        #region graphical EVENTS

        /*  
            onfocusin = "<anything>"
            onfocusout = "<anything>"
            onactivate = "<anything>"
755
756
757
758
759
                onclick = "<anything>"
                onmousedown = "<anything>"
                onmouseup = "<anything>"
                onmouseover = "<anything>"
                onmousemove = "<anything>"
760
            	onmouseout = "<anything>" 
Tebjan Halm's avatar
Tebjan Halm committed
761
762
         */

763
764
765
766
767
768
769
        /// <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
770
            if (caller != null && !string.IsNullOrEmpty(this.ID))
771
772
773
            {
                var rpcID = this.ID + "/";

Tebjan Halm's avatar
Tebjan Halm committed
774
775
776
777
778
779
780
781
                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);
782
783
            }
        }
784
785
786
787
788
789
790
        
        /// <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
791
        	if (caller != null && !string.IsNullOrEmpty(this.ID))
792
793
794
795
796
797
798
        	{
        		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
799
        		caller.UnregisterAction(rpcID + "onmousescroll");
800
801
        		caller.UnregisterAction(rpcID + "onmouseover");
        		caller.UnregisterAction(rpcID + "onmouseout");
joreg's avatar
joreg committed
802
        		caller.UnregisterAction(rpcID + "onchange");
803
804
        	}
        }
805

Tebjan Halm's avatar
Tebjan Halm committed
806
807
808
        [SvgAttribute("onclick")]
        public event EventHandler<MouseArg> Click;

809
810
811
812
813
        [SvgAttribute("onmousedown")]
        public event EventHandler<MouseArg> MouseDown;

        [SvgAttribute("onmouseup")]
        public event EventHandler<MouseArg> MouseUp;
814
815
816
        
        [SvgAttribute("onmousemove")]
        public event EventHandler<PointArg> MouseMove;
817

joreg's avatar
joreg committed
818
819
820
        [SvgAttribute("onmousescroll")]
        public event EventHandler<PointArg> MouseScroll;
        
821
        [SvgAttribute("onmouseover")]
Tebjan Halm's avatar
Tebjan Halm committed
822
        public event EventHandler<SVGArg> MouseOver;
823
824

        [SvgAttribute("onmouseout")]
Tebjan Halm's avatar
Tebjan Halm committed
825
        public event EventHandler<SVGArg> MouseOut;
joreg's avatar
joreg committed
826
827
828
        
        [SvgAttribute("onchange")]
        public event EventHandler<StringArg> Change;
829

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

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

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

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

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

        #endregion graphical EVENTS

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

985
986
987
    //deriving class registers event actions and calls the actions if the event occurs
    public interface ISvgEventCaller
    {
988
        void RegisterAction(string rpcID, Action action);
989
990
991
992
        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
993
        void RegisterAction<T1, T2, T3, T4, T5>(string rpcID, Action<T1, T2, T3, T4, T5> action);
994
        void UnregisterAction(string rpcID);
995
996
    }

Tebjan Halm's avatar
Tebjan Halm committed
997
998
999
    /// <summary>
    /// Represents the state of the mouse at the moment the event occured.
    /// </summary>
Tebjan Halm's avatar
Tebjan Halm committed
1000
    public class MouseArg : SVGArg