SvgElement.cs 31.1 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
85
86
87
88
89
90
91
92
            get
            {
            	return _content;
            }
            set
            {
            	if(_content != null)
            	{
            		var oldVal = _content;
            		_content = value;
            		if(_content != oldVal)
            			OnAttributeChanged(new AttributeEventArgs{ Attribute = "", Value = value });
            	}
            	else
            	{
            		_content = value;
            		OnAttributeChanged(new AttributeEventArgs{ Attribute = "", Value = value });
            	}
            }
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
        {
Tebjan Halm's avatar
Tebjan Halm committed
241
242
243
244
245
246
247
248
249
            get { return (this.Attributes.GetAttribute<SvgTransformCollection>("Transforms")); }
            set 
            { 
            	var old = this.Transforms;
            	if(old != null)
            		old.TransformChanged -= Attributes_AttributeChanged;
            	value.TransformChanged += Attributes_AttributeChanged;
            	this.Attributes["Transforms"] = value; 
            }
davescriven's avatar
davescriven committed
250
251
252
253
254
255
256
257
258
259
260
261
262
263
        }

        /// <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
        {
            get { return this.Attributes.GetAttribute<string>("ID"); }
            set
            {
                // Don't do anything if it hasn't changed
                if (string.Compare(this.ID, value) == 0)
264
                {
davescriven's avatar
davescriven committed
265
                    return;
266
                }
davescriven's avatar
davescriven committed
267
268
269
270
271
272
273
274
275
276
277
278
279
280
281

                if (this.OwnerDocument != null)
                {
                    this.OwnerDocument.IdManager.Remove(this);
                }

                this.Attributes["ID"] = value;

                if (this.OwnerDocument != null)
                {
                    this.OwnerDocument.IdManager.Add(this);
                }
            }
        }

282
283
284
285
286
287
        /// <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>
288
        protected virtual void AddElement(SvgElement child, int index)
davescriven's avatar
davescriven committed
289
290
291
        {
        }

292
293
294
295
296
        /// <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
297
298
        internal void OnElementAdded(SvgElement child, int index)
        {
299
            this.AddElement(child, index);
davescriven's avatar
davescriven committed
300
301
        }

302
303
304
305
306
        /// <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>
307
        protected virtual void RemoveElement(SvgElement child)
davescriven's avatar
davescriven committed
308
309
310
        {
        }

311
312
313
314
        /// <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
315
316
        internal void OnElementRemoved(SvgElement child)
        {
317
            this.RemoveElement(child);
davescriven's avatar
davescriven committed
318
319
320
321
322
        }

        /// <summary>
        /// Initializes a new instance of the <see cref="SvgElement"/> class.
        /// </summary>
323
        public SvgElement()
davescriven's avatar
davescriven committed
324
325
326
327
        {
            this._children = new SvgElementCollection(this);
            this._eventHandlers = new EventHandlerList();
            this._elementName = string.Empty;
328
329
            this._customAttributes = new SvgCustomAttributeCollection(this);
            
Tebjan Halm's avatar
Tebjan Halm committed
330
331
            Transforms = new SvgTransformCollection();
            
332
333
334
            //subscribe to attribute events
            Attributes.AttributeChanged += Attributes_AttributeChanged;
            CustomAttributes.AttributeChanged += Attributes_AttributeChanged;
335

336
337
338
339
340
            //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
341

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

347
        }
348

349
350
351
352
353
354
        //dispatch attribute event
        void Attributes_AttributeChanged(object sender, AttributeEventArgs e)
        {
        	OnAttributeChanged(e);
        }

355
356
		public virtual void InitialiseFromXML(XmlTextReader reader, SvgDocument document)
		{
357
            throw new NotImplementedException();
358
359
360
		}


361
362
363
364
365
        /// <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
366
        {
367
            this.Render(renderer);
davescriven's avatar
davescriven committed
368
369
370
371
        }

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

davescriven's avatar
davescriven committed
376
            this.Write(writer);
Tebjan Halm's avatar
Tebjan Halm committed
377
378
379

            //Switch culture back
            Thread.CurrentThread.CurrentCulture = previousCulture;
davescriven's avatar
davescriven committed
380
381
382
383
384
385
386
        }

        protected virtual void WriteStartElement(XmlTextWriter writer)
        {
            if (this.ElementName != String.Empty)
            {
                writer.WriteStartElement(this.ElementName);
Tebjan Halm's avatar
Tebjan Halm committed
387
388
                if (this.ElementName == "svg")
                {
389
390
391
392
393
394
395
396
397
					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
398
399
400
401
402
403
404
405
406
407
408
409
410
411
            }
            this.WriteAttributes(writer);
        }

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

        protected virtual void WriteAttributes(XmlTextWriter writer)
        {
412
413
            //properties
            foreach (var attr in _svgPropertyAttributes)
414
415
416
417
418
            {
                if (attr.Property.Converter.CanConvertTo(typeof(string)))
                {
                    object propertyValue = attr.Property.GetValue(this);

419
420
421
                    var forceWrite = false;
                    if ((attr.Attribute.Name == "fill") && (Parent != null))
                    {
422
                    	if(propertyValue == SvgColourServer.NotSet) continue;
423
                    	
424
425
                        object parentValue;
                        if (TryResolveParentAttributeValue(attr.Attribute.Name, out parentValue))
426
                        {
427
428
                            if ((parentValue == propertyValue) 
                                || ((parentValue != null) &&  parentValue.Equals(propertyValue)))
429
                                continue;
430
                            
431
432
433
434
                            forceWrite = true;
                        }
                    }

435
436
                    if (propertyValue != null)
                    {
Tebjan Halm's avatar
Tebjan Halm committed
437
                        var type = propertyValue.GetType();
438
                        string value = (string)attr.Property.Converter.ConvertTo(propertyValue, typeof(string));
Tebjan Halm's avatar
Tebjan Halm committed
439

440
                        if (!SvgDefaults.IsDefault(attr.Attribute.Name, value) || forceWrite)
Tebjan Halm's avatar
Tebjan Halm committed
441
                        {
442
                            writer.WriteAttributeString(attr.Attribute.NamespaceAndName, value);
Tebjan Halm's avatar
Tebjan Halm committed
443
444
445
446
                        }
                    }
                    else if(attr.Attribute.Name == "fill") //if fill equals null, write 'none'
                    {
447
448
                        string value = (string)attr.Property.Converter.ConvertTo(propertyValue, typeof(string));
                        writer.WriteAttributeString(attr.Attribute.NamespaceAndName, value);
449
450
451
                    }
                }
            }
452

Tebjan Halm's avatar
Tebjan Halm committed
453
            
454
            //events
Tebjan Halm's avatar
Tebjan Halm committed
455
            if(AutoPublishEvents)
456
            {
457
458
459
460
	            foreach (var attr in _svgEventAttributes)
	            {
	                var evt = attr.Event.GetValue(this);
	                
461
	                //if someone has registered publish the attribute
Tebjan Halm's avatar
Tebjan Halm committed
462
	                if (evt != null && !string.IsNullOrEmpty(this.ID))
463
464
465
466
	                {
	                    writer.WriteAttributeString(attr.Attribute.Name, this.ID + "/" + attr.Attribute.Name);
	                }
	            }
467
468
            }

469
470
471
472
473
            //add the custom attributes
            foreach (var item in this._customAttributes)
            {
                writer.WriteAttributeString(item.Key, item.Value);
            }
474
        }
Tebjan Halm's avatar
Tebjan Halm committed
475
476
        
        public bool AutoPublishEvents = true;
477

478
        private bool TryResolveParentAttributeValue(string attributeKey, out object parentAttributeValue)
479
        {
480
            parentAttributeValue = null;
481

482
            attributeKey = char.ToUpper(attributeKey[0]) + attributeKey.Substring(1);
483
484

            var currentParent = Parent;
485
            var resolved = false;
486
487
488
489
            while (currentParent != null)
            {
                if (currentParent.Attributes.ContainsKey(attributeKey))
                {
490
491
492
                    resolved = true;
                    parentAttributeValue = currentParent.Attributes[attributeKey];
                    if (parentAttributeValue != null)
493
494
495
496
                        break;
                }
                currentParent = currentParent.Parent;
            }
497

498
            return resolved;
davescriven's avatar
davescriven committed
499
500
501
502
503
504
505
506
507
508
509
510
511
512
        }

        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
513
514
515
516
517
            //write the content
            if(!String.IsNullOrEmpty(this.Content))
                writer.WriteString(this.Content);

            //write all children
davescriven's avatar
davescriven committed
518
519
520
521
522
523
524
            foreach (SvgElement child in this.Children)
            {
                child.Write(writer);
            }
        }

        /// <summary>
525
        /// Renders the <see cref="SvgElement"/> and contents to the specified <see cref="SvgRenderer"/> object.
davescriven's avatar
davescriven committed
526
        /// </summary>
527
528
        /// <param name="renderer">The <see cref="SvgRenderer"/> object to render to.</param>
        protected virtual void Render(SvgRenderer renderer)
davescriven's avatar
davescriven committed
529
        {
530
531
532
            this.PushTransforms(renderer);
            this.RenderChildren(renderer);
            this.PopTransforms(renderer);
davescriven's avatar
davescriven committed
533
534
        }

535
536
537
538
539
        /// <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
540
541
542
        {
            foreach (SvgElement element in this.Children)
            {
543
                element.Render(renderer);
davescriven's avatar
davescriven committed
544
545
546
            }
        }

547
548
549
550
551
        /// <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
552
        {
553
            this.Render(renderer);
davescriven's avatar
davescriven committed
554
        }
Tebjan Halm's avatar
Tebjan Halm committed
555
556
557
558
559
560
561
562
        
        /// <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
563
        	foreach(var child in elem.Children)
Tebjan Halm's avatar
Tebjan Halm committed
564
        	{
Tebjan Halm's avatar
Tebjan Halm committed
565
        		if (child is SvgVisualElement)
Tebjan Halm's avatar
Tebjan Halm committed
566
        		{
Tebjan Halm's avatar
Tebjan Halm committed
567
        			if(!(child is SvgGroup))
568
        			{
Tebjan Halm's avatar
Tebjan Halm committed
569
        				var childPath = ((SvgVisualElement)child).Path;
570
        				
Tebjan Halm's avatar
Tebjan Halm committed
571
572
573
574
575
576
577
578
        				if (childPath != null)
        				{
        					childPath = (GraphicsPath)childPath.Clone();
        					if(child.Transforms != null)
        						childPath.Transform(child.Transforms.GetMatrix());
        					
        					path.AddPath(childPath, false);
        				}
579
        			}
Tebjan Halm's avatar
Tebjan Halm committed
580
        		}
Tebjan Halm's avatar
Tebjan Halm committed
581
        			
Tebjan Halm's avatar
Tebjan Halm committed
582
        		AddPaths(child, path);
Tebjan Halm's avatar
Tebjan Halm committed
583
        	}
Tebjan Halm's avatar
Tebjan Halm committed
584
585
586
587
588
589
590
591
592
593
594
595
596
597
598
599
600
601
602
603
604
605
606
607
608
609
610
611
612
613
614
615
616
617
618
619
620
        }
        
        /// <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
621
        	
Tebjan Halm's avatar
Tebjan Halm committed
622
        	return ret;
Tebjan Halm's avatar
Tebjan Halm committed
623
        }
davescriven's avatar
davescriven committed
624

625
626
627
628
629
630
        /// <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
631
632
633
634
        public virtual object Clone()
        {
            return this.MemberwiseClone();
        }
635
636
637
638
639
640

    	public abstract SvgElement DeepCopy();

		public virtual SvgElement DeepCopy<T>() where T : SvgElement, new()
		{
			var newObj = new T();
641
			newObj.ID = this.ID;
642
643
			newObj.Content = this.Content;
			newObj.ElementName = this.ElementName;
644
			
645
646
647
648
649
650
651
652
653
654
655
656
657
658
//			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());
			}
659
			
660
661
662
663
664
665
666
667
668
669
670
671
672
673
674
675
676
677
678
			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
679
680
					else if (attr.Event.Name == "Click")
						newObj.Click += delegate {  };
681
682
683
				}
			}
			
684
685
686
687
688
689
690
			if(this._customAttributes.Count > 0)
			{
				foreach (var element in _customAttributes) 
				{
					newObj.CustomAttributes.Add(element.Key, element.Value);
				}
			}
691
692
				
			return newObj;
Tebjan Halm's avatar
Tebjan Halm committed
693
        }
694
695
696
697
698
699
700
701
702
703
704
705
706
707
		
		/// <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 Halm's avatar
Tebjan Halm committed
708
709
710
711
712
713
714

        #region graphical EVENTS

        /*  
            onfocusin = "<anything>"
            onfocusout = "<anything>"
            onactivate = "<anything>"
715
716
717
718
719
                onclick = "<anything>"
                onmousedown = "<anything>"
                onmouseup = "<anything>"
                onmouseover = "<anything>"
                onmousemove = "<anything>"
720
            	onmouseout = "<anything>" 
Tebjan Halm's avatar
Tebjan Halm committed
721
722
         */

723
724
725
726
727
728
729
        /// <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
730
            if (caller != null && !string.IsNullOrEmpty(this.ID))
731
732
733
            {
                var rpcID = this.ID + "/";

Tebjan Halm's avatar
Tebjan Halm committed
734
735
                caller.RegisterAction<float, float, int, int>(rpcID + "onclick", OnClick);
                caller.RegisterAction<float, float, int, int>(rpcID + "onmousedown", OnMouseDown);
736
                caller.RegisterAction<float, float, int>(rpcID + "onmouseup", OnMouseUp);
737
                caller.RegisterAction<float, float>(rpcID + "onmousemove", OnMouseMove);
joreg's avatar
joreg committed
738
                caller.RegisterAction<float>(rpcID + "onmousescroll", OnMouseScroll);
739
740
                caller.RegisterAction(rpcID + "onmouseover", OnMouseOver);
                caller.RegisterAction(rpcID + "onmouseout", OnMouseOut);
741
742
            }
        }
743
744
745
746
747
748
749
        
        /// <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
750
        	if (caller != null && !string.IsNullOrEmpty(this.ID))
751
752
753
754
755
756
757
        	{
        		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
758
        		caller.UnregisterAction(rpcID + "onmousescroll");
759
760
761
762
        		caller.UnregisterAction(rpcID + "onmouseover");
        		caller.UnregisterAction(rpcID + "onmouseout");
        	}
        }
763

Tebjan Halm's avatar
Tebjan Halm committed
764
765
766
        [SvgAttribute("onclick")]
        public event EventHandler<MouseArg> Click;

767
768
769
770
771
        [SvgAttribute("onmousedown")]
        public event EventHandler<MouseArg> MouseDown;

        [SvgAttribute("onmouseup")]
        public event EventHandler<MouseArg> MouseUp;
772
773
774
        
        [SvgAttribute("onmousemove")]
        public event EventHandler<PointArg> MouseMove;
775

joreg's avatar
joreg committed
776
777
778
        [SvgAttribute("onmousescroll")]
        public event EventHandler<PointArg> MouseScroll;
        
779
780
781
782
783
784
        [SvgAttribute("onmouseover")]
        public event EventHandler MouseOver;

        [SvgAttribute("onmouseout")]
        public event EventHandler MouseOut;

785
        //click
786
        protected void OnClick(float x, float y, int button, int clickCount)
Tebjan Halm's avatar
Tebjan Halm committed
787
        {
788
789
790
791
792
793
794
795
796
        	RaiseMouseClick(this, new MouseArg { x = x, y = y, Button = button, ClickCount = clickCount});
        }
        
        protected void RaiseMouseClick(object sender, MouseArg e)
        {
        	var handler = Click;
        	if (handler != null)
        	{
        		handler(sender, e);
797
798
799
            }
        }

800
        //down
801
        protected void OnMouseDown(float x, float y, int button, int clickCount)
802
        {
803
        	RaiseMouseDown(this, new MouseArg { x = x, y = y, Button = button, ClickCount = clickCount});
Tebjan Halm's avatar
Tebjan Halm committed
804
805
806
807
808
        }
        
        protected void RaiseMouseDown(object sender, MouseArg e)
        {
        	var handler = MouseDown;
809
810
            if (handler != null)
            {
Tebjan Halm's avatar
Tebjan Halm committed
811
                handler(sender, e);
812
813
814
            }
        }

815
        //up
816
817
        protected void OnMouseUp(float x, float y, int button)
        {
818
819
820
821
822
823
        	RaiseMouseUp(this, new MouseArg { x = x, y = y, Button = button});
        }
        
        protected void RaiseMouseUp(object sender, MouseArg e)
        {
        	var handler = MouseUp;
824
825
            if (handler != null)
            {
826
                handler(sender, e);
827
828
            }
        }
829
830
831
        
        //move
        protected void OnMouseMove(float x, float y)
832
        {
833
834
835
836
837
838
        	RaiseMouseMove(this, new PointArg { x = x, y = y});
        }
        
        protected void RaiseMouseMove(object sender, PointArg e)
        {
        	var handler = MouseMove;
839
840
            if (handler != null)
            {
841
                handler(sender, e);
842
843
            }
        }
joreg's avatar
joreg committed
844
845
846
847
848
849
850
851
852
853
854
855
856
857
858
        
        //scroll
        protected void OnMouseScroll(float y)
        {
        	RaiseMouseScroll(this, new PointArg { x = 0, y = y});
        }
        
        protected void RaiseMouseScroll(object sender, PointArg e)
        {
        	var handler = MouseScroll;
            if (handler != null)
            {
                handler(sender, e);
            }
        }
859

860
861
		//over        
        protected void OnMouseOver()
862
        {
863
864
865
866
867
868
        	RaiseMouseOver(this);
        }
        
        protected void RaiseMouseOver(object sender)
        {
        	var handler = MouseOver;
869
870
            if (handler != null)
            {
871
                handler(sender, new EventArgs());
Tebjan Halm's avatar
Tebjan Halm committed
872
873
874
            }
        }

875
876
877
878
879
880
881
        //out
        protected void OnMouseOut()
        {
        	RaiseMouseOut(this);
        }
        
        protected void RaiseMouseOut(object sender)
882
        {
883
        	var handler = MouseOut;
884
885
            if (handler != null)
            {
886
                handler(sender, new EventArgs());
887
888
            }
        }
Tebjan Halm's avatar
Tebjan Halm committed
889
890
891
892

        #endregion graphical EVENTS

    }
893
894
895
896
897
898
899
900
901
    
    /// <summary>
    /// Describes the Attribute which was set
    /// </summary>
    public class AttributeEventArgs : EventArgs
    {
    	public string Attribute;
    	public object Value;
    }
Tebjan Halm's avatar
Tebjan Halm committed
902

903
904
905
    //deriving class registers event actions and calls the actions if the event occurs
    public interface ISvgEventCaller
    {
906
        void RegisterAction(string rpcID, Action action);
907
908
909
910
        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);
911
        void UnregisterAction(string rpcID);
912
913
    }

Tebjan Halm's avatar
Tebjan Halm committed
914
915
916
917
918
919
920
921
922
    /// <summary>
    /// Represents the state of the mouse at the moment the event occured.
    /// </summary>
    public class MouseArg : EventArgs
    {
        public float x;
        public float y;

        /// <summary>
923
        /// 1 = left, 2 = middle, 3 = right
Tebjan Halm's avatar
Tebjan Halm committed
924
        /// </summary>
925
        public int Button;
926
927
        
        public int ClickCount = -1;
davescriven's avatar
davescriven committed
928
929
    }

930
931
932
933
934
935
936
937
938
    /// <summary>
    /// Represents the mouse position at the moment the event occured.
    /// </summary>
    public class PointArg : EventArgs
    {
        public float x;
        public float y;
    }

davescriven's avatar
davescriven committed
939
940
    internal interface ISvgElement
    {
941
942
943
		SvgElement Parent {get;}
		SvgElementCollection Children { get; }

944
        void Render(SvgRenderer renderer);
davescriven's avatar
davescriven committed
945
946
    }
}