SvgElement.cs 35 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

namespace Svg
{
    /// <summary>
    /// The base class of which all SVG elements are derived from.
    /// </summary>
18
    public abstract class SvgElement : ISvgElement, ISvgTransformable, ICloneable, ISvgNode
davescriven's avatar
davescriven committed
19
    {
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;
45
        private List<ISvgNode> _nodes = new List<ISvgNode>();
davescriven's avatar
davescriven committed
46
47
48
49

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

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

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

        /// <summary>
        /// Gets or sets the content of the element.
        /// </summary>
Tebjan Halm's avatar
Tebjan Halm committed
72
        private string _content;
davescriven's avatar
davescriven committed
73
74
        public virtual string Content
        {
Tebjan Halm's avatar
Tebjan Halm committed
75
76
77
78
79
80
81
82
83
84
85
            get
            {
            	return _content;
            }
            set
            {
            	if(_content != null)
            	{
            		var oldVal = _content;
            		_content = value;
            		if(_content != oldVal)
tebjan's avatar
tebjan committed
86
            			OnContentChanged(new ContentEventArgs{ Content = value });
Tebjan Halm's avatar
Tebjan Halm committed
87
88
89
90
            	}
            	else
            	{
            		_content = value;
tebjan's avatar
tebjan committed
91
            		OnContentChanged(new ContentEventArgs{ Content = value });
Tebjan Halm's avatar
Tebjan Halm committed
92
93
            	}
            }
davescriven's avatar
davescriven committed
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
        }

        /// <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; }
        }

121
122
123
124
125
        public IList<ISvgNode> Nodes
        {
            get { return this._nodes; }
        }

Eric Domke's avatar
Eric Domke committed
126
127
128
129
130
131
132
133
134
        public IEnumerable<SvgElement> Descendants()
        {
            return this.AsEnumerable().Descendants();
        }
        private IEnumerable<SvgElement> AsEnumerable()
        {
            yield return this;
        }

davescriven's avatar
davescriven committed
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
        /// <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
        {
157
158
159
160
161
162
163
164
165
166
167
168
169
170
        	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
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
        }

        /// <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;
            }
        }

189
190
191
192
        /// <summary>
        /// Gets a collection of custom attributes
        /// </summary>
        public SvgCustomAttributeCollection CustomAttributes
193
194
195
196
        {
            get { return this._customAttributes; }
        }

197
198
199
200
201
        /// <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
202
        {
203
            _graphicsMatrix = renderer.Transform;
204

davescriven's avatar
davescriven committed
205
206
207
208
209
210
            // Return if there are no transforms
            if (this.Transforms == null || this.Transforms.Count == 0)
            {
                return;
            }

211
            Matrix transformMatrix = renderer.Transform;
davescriven's avatar
davescriven committed
212
213
214

            foreach (SvgTransform transformation in this.Transforms)
            {
215
                transformMatrix.Multiply(transformation.Matrix);
davescriven's avatar
davescriven committed
216
217
            }

218
            renderer.Transform = transformMatrix;
davescriven's avatar
davescriven committed
219
220
        }

221
222
223
224
225
        /// <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
226
        {
227
            renderer.Transform = _graphicsMatrix;
davescriven's avatar
davescriven committed
228
229
230
            _graphicsMatrix = null;
        }

231
232
233
234
235
        /// <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
236
        {
237
            this.PushTransforms(renderer);
davescriven's avatar
davescriven committed
238
239
        }

240
241
242
243
244
        /// <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
245
        {
246
            this.PopTransforms(renderer);
davescriven's avatar
davescriven committed
247
248
249
250
251
252
253
254
255
        }

        /// <summary>
        /// Gets or sets the element transforms.
        /// </summary>
        /// <value>The transforms.</value>
        [SvgAttribute("transform")]
        public SvgTransformCollection Transforms
        {
256
            get { return (this.Attributes.GetAttribute<SvgTransformCollection>("transform")); }
Tebjan Halm's avatar
Tebjan Halm committed
257
258
259
260
261
262
            set 
            { 
            	var old = this.Transforms;
            	if(old != null)
            		old.TransformChanged -= Attributes_AttributeChanged;
            	value.TransformChanged += Attributes_AttributeChanged;
263
            	this.Attributes["transform"] = value; 
Tebjan Halm's avatar
Tebjan Halm committed
264
            }
davescriven's avatar
davescriven committed
265
266
267
268
269
270
271
272
273
        }

        /// <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
        {
274
            get { return this.Attributes.GetAttribute<string>("id"); }
davescriven's avatar
davescriven committed
275
276
            set
            {
tebjan's avatar
tebjan committed
277
                SetAndForceUniqueID(value, false);
278
279
            }
        }
davescriven's avatar
davescriven committed
280

tebjan's avatar
tebjan committed
281
        public void SetAndForceUniqueID(string value, bool autoForceUniqueID = true, Action<SvgElement, string, string> logElementOldIDNewID = null)
282
283
284
285
286
287
        {
            // Don't do anything if it hasn't changed
            if (string.Compare(this.ID, value) == 0)
            {
                return;
            }
davescriven's avatar
davescriven committed
288

289
290
291
292
            if (this.OwnerDocument != null)
            {
                this.OwnerDocument.IdManager.Remove(this);
            }
davescriven's avatar
davescriven committed
293

294
            this.Attributes["id"] = value;
295
296
297

            if (this.OwnerDocument != null)
            {
tebjan's avatar
tebjan committed
298
                this.OwnerDocument.IdManager.AddAndForceUniqueID(this, null, autoForceUniqueID, logElementOldIDNewID);
davescriven's avatar
davescriven committed
299
300
301
            }
        }

302
303
304
305
        /// <summary>
        /// Only used by the ID Manager
        /// </summary>
        /// <param name="newID"></param>
tebjan's avatar
tebjan committed
306
        internal void ForceUniqueID(string newID)
307
        {
308
            this.Attributes["id"] = newID;
309
310
        }

311
312
313
314
315
316
        /// <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>
317
        protected virtual void AddElement(SvgElement child, int index)
davescriven's avatar
davescriven committed
318
319
        {
        }
tebjan's avatar
tebjan committed
320
321
322
323
324
        
        /// <summary>
        /// Fired when an Element was added to the children of this Element
        /// </summary>
		public event EventHandler<ChildAddedEventArgs> ChildAdded;
davescriven's avatar
davescriven committed
325

326
327
328
329
330
        /// <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
331
332
        internal void OnElementAdded(SvgElement child, int index)
        {
333
            this.AddElement(child, index);
334
335
336
337
338
            SvgElement sibling = null;
            if(index < (Children.Count - 1))
            {
            	sibling = Children[index + 1];
            }
tebjan's avatar
tebjan committed
339
340
341
            var handler = ChildAdded;
            if(handler != null)
            {
342
            	handler(this, new ChildAddedEventArgs { NewChild = child, BeforeSibling = sibling });
tebjan's avatar
tebjan committed
343
            }
davescriven's avatar
davescriven committed
344
345
        }

346
347
348
349
350
        /// <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>
351
        protected virtual void RemoveElement(SvgElement child)
davescriven's avatar
davescriven committed
352
353
354
        {
        }

355
356
357
358
        /// <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
359
360
        internal void OnElementRemoved(SvgElement child)
        {
361
            this.RemoveElement(child);
davescriven's avatar
davescriven committed
362
363
364
365
366
        }

        /// <summary>
        /// Initializes a new instance of the <see cref="SvgElement"/> class.
        /// </summary>
367
        public SvgElement()
davescriven's avatar
davescriven committed
368
369
370
371
        {
            this._children = new SvgElementCollection(this);
            this._eventHandlers = new EventHandlerList();
            this._elementName = string.Empty;
372
373
            this._customAttributes = new SvgCustomAttributeCollection(this);
            
Tebjan Halm's avatar
Tebjan Halm committed
374
375
            Transforms = new SvgTransformCollection();
            
376
377
378
            //subscribe to attribute events
            Attributes.AttributeChanged += Attributes_AttributeChanged;
            CustomAttributes.AttributeChanged += Attributes_AttributeChanged;
379

380
381
382
383
384
            //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
385

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

391
        }
392

393
394
395
396
397
398
        //dispatch attribute event
        void Attributes_AttributeChanged(object sender, AttributeEventArgs e)
        {
        	OnAttributeChanged(e);
        }

399
400
		public virtual void InitialiseFromXML(XmlTextReader reader, SvgDocument document)
		{
401
            throw new NotImplementedException();
402
403
404
		}


405
406
407
408
409
        /// <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
410
        {
411
            this.Render(renderer);
davescriven's avatar
davescriven committed
412
413
414
415
        }

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

davescriven's avatar
davescriven committed
420
            this.Write(writer);
Tebjan Halm's avatar
Tebjan Halm committed
421
422
423

            //Switch culture back
            Thread.CurrentThread.CurrentCulture = previousCulture;
davescriven's avatar
davescriven committed
424
425
426
427
428
429
430
        }

        protected virtual void WriteStartElement(XmlTextWriter writer)
        {
            if (this.ElementName != String.Empty)
            {
                writer.WriteStartElement(this.ElementName);
Tebjan Halm's avatar
Tebjan Halm committed
431
432
                if (this.ElementName == "svg")
                {
433
434
435
436
437
438
439
440
441
					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
442
443
444
445
446
447
448
449
450
451
452
453
454
455
            }
            this.WriteAttributes(writer);
        }

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

        protected virtual void WriteAttributes(XmlTextWriter writer)
        {
456
457
            //properties
            foreach (var attr in _svgPropertyAttributes)
458
459
460
461
462
            {
                if (attr.Property.Converter.CanConvertTo(typeof(string)))
                {
                    object propertyValue = attr.Property.GetValue(this);

463
464
465
                    var forceWrite = false;
                    if ((attr.Attribute.Name == "fill") && (Parent != null))
                    {
466
                    	if(propertyValue == SvgColourServer.NotSet) continue;
467
                    	
468
469
                        object parentValue;
                        if (TryResolveParentAttributeValue(attr.Attribute.Name, out parentValue))
470
                        {
471
472
                            if ((parentValue == propertyValue) 
                                || ((parentValue != null) &&  parentValue.Equals(propertyValue)))
473
                                continue;
474
                            
475
476
477
478
                            forceWrite = true;
                        }
                    }

479
480
                    if (propertyValue != null)
                    {
Tebjan Halm's avatar
Tebjan Halm committed
481
                        var type = propertyValue.GetType();
482
                        string value = (string)attr.Property.Converter.ConvertTo(propertyValue, typeof(string));
Tebjan Halm's avatar
Tebjan Halm committed
483

484
                        if (!SvgDefaults.IsDefault(attr.Attribute.Name, value) || forceWrite)
Tebjan Halm's avatar
Tebjan Halm committed
485
                        {
486
                            writer.WriteAttributeString(attr.Attribute.NamespaceAndName, value);
Tebjan Halm's avatar
Tebjan Halm committed
487
488
489
490
                        }
                    }
                    else if(attr.Attribute.Name == "fill") //if fill equals null, write 'none'
                    {
491
492
                        string value = (string)attr.Property.Converter.ConvertTo(propertyValue, typeof(string));
                        writer.WriteAttributeString(attr.Attribute.NamespaceAndName, value);
493
494
495
                    }
                }
            }
496

Tebjan Halm's avatar
Tebjan Halm committed
497
            
498
            //events
Tebjan Halm's avatar
Tebjan Halm committed
499
            if(AutoPublishEvents)
500
            {
501
502
503
504
	            foreach (var attr in _svgEventAttributes)
	            {
	                var evt = attr.Event.GetValue(this);
	                
505
	                //if someone has registered publish the attribute
Tebjan Halm's avatar
Tebjan Halm committed
506
	                if (evt != null && !string.IsNullOrEmpty(this.ID))
507
508
509
510
	                {
	                    writer.WriteAttributeString(attr.Attribute.Name, this.ID + "/" + attr.Attribute.Name);
	                }
	            }
511
512
            }

513
514
515
516
517
            //add the custom attributes
            foreach (var item in this._customAttributes)
            {
                writer.WriteAttributeString(item.Key, item.Value);
            }
518
        }
Tebjan Halm's avatar
Tebjan Halm committed
519
520
        
        public bool AutoPublishEvents = true;
521

522
        private bool TryResolveParentAttributeValue(string attributeKey, out object parentAttributeValue)
523
        {
524
            parentAttributeValue = null;
525

tebjan's avatar
tebjan committed
526
            //attributeKey = char.ToUpper(attributeKey[0]) + attributeKey.Substring(1);
527
528

            var currentParent = Parent;
529
            var resolved = false;
530
531
532
533
            while (currentParent != null)
            {
                if (currentParent.Attributes.ContainsKey(attributeKey))
                {
534
535
536
                    resolved = true;
                    parentAttributeValue = currentParent.Attributes[attributeKey];
                    if (parentAttributeValue != null)
537
538
539
540
                        break;
                }
                currentParent = currentParent.Parent;
            }
541

542
            return resolved;
davescriven's avatar
davescriven committed
543
544
545
546
547
548
549
550
551
552
553
554
555
556
        }

        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
557
558
559
560
561
            //write the content
            if(!String.IsNullOrEmpty(this.Content))
                writer.WriteString(this.Content);

            //write all children
davescriven's avatar
davescriven committed
562
563
564
565
566
567
568
            foreach (SvgElement child in this.Children)
            {
                child.Write(writer);
            }
        }

        /// <summary>
569
        /// Renders the <see cref="SvgElement"/> and contents to the specified <see cref="SvgRenderer"/> object.
davescriven's avatar
davescriven committed
570
        /// </summary>
571
572
        /// <param name="renderer">The <see cref="SvgRenderer"/> object to render to.</param>
        protected virtual void Render(SvgRenderer renderer)
davescriven's avatar
davescriven committed
573
        {
574
575
576
            this.PushTransforms(renderer);
            this.RenderChildren(renderer);
            this.PopTransforms(renderer);
davescriven's avatar
davescriven committed
577
578
        }

579
580
581
582
583
        /// <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
584
585
586
        {
            foreach (SvgElement element in this.Children)
            {
587
                element.Render(renderer);
davescriven's avatar
davescriven committed
588
589
590
            }
        }

591
592
593
594
595
        /// <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
596
        {
597
            this.Render(renderer);
davescriven's avatar
davescriven committed
598
        }
Tebjan Halm's avatar
Tebjan Halm committed
599
600
601
602
603
604
605
606
        
        /// <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
607
        	foreach(var child in elem.Children)
Tebjan Halm's avatar
Tebjan Halm committed
608
        	{
Tebjan Halm's avatar
Tebjan Halm committed
609
        		if (child is SvgVisualElement)
Tebjan Halm's avatar
Tebjan Halm committed
610
        		{
Tebjan Halm's avatar
Tebjan Halm committed
611
        			if(!(child is SvgGroup))
612
        			{
Tebjan Halm's avatar
Tebjan Halm committed
613
        				var childPath = ((SvgVisualElement)child).Path;
614
        				
Tebjan Halm's avatar
Tebjan Halm committed
615
616
617
618
619
        				if (childPath != null)
        				{
        					childPath = (GraphicsPath)childPath.Clone();
        					if(child.Transforms != null)
        						childPath.Transform(child.Transforms.GetMatrix());
620
621

                            if (childPath.PointCount > 0) path.AddPath(childPath, false);
Tebjan Halm's avatar
Tebjan Halm committed
622
        				}
623
        			}
Tebjan Halm's avatar
Tebjan Halm committed
624
        		}
Tebjan Halm's avatar
Tebjan Halm committed
625
        			
Tebjan Halm's avatar
Tebjan Halm committed
626
        		AddPaths(child, path);
Tebjan Halm's avatar
Tebjan Halm committed
627
        	}
Tebjan Halm's avatar
Tebjan Halm committed
628
629
630
631
632
633
634
635
636
637
638
639
640
641
642
643
644
645
646
647
648
649
650
651
652
653
654
655
656
657
658
659
660
661
662
663
664
        }
        
        /// <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
665
        	
Tebjan Halm's avatar
Tebjan Halm committed
666
        	return ret;
Tebjan Halm's avatar
Tebjan Halm committed
667
        }
davescriven's avatar
davescriven committed
668

669
670
671
672
673
674
        /// <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
675
676
677
678
        public virtual object Clone()
        {
            return this.MemberwiseClone();
        }
679
680
681
682
683
684

    	public abstract SvgElement DeepCopy();

		public virtual SvgElement DeepCopy<T>() where T : SvgElement, new()
		{
			var newObj = new T();
685
			newObj.ID = this.ID;
686
687
			newObj.Content = this.Content;
			newObj.ElementName = this.ElementName;
688
			
689
690
691
692
693
//			if (this.Parent != null)
	//			this.Parent.Children.Add(newObj);

			if (this.Transforms != null)
			{
694
				newObj.Transforms = this.Transforms.Clone() as SvgTransformCollection;
695
696
697
698
699
700
			}

			foreach (var child in this.Children)
			{
				newObj.Children.Add(child.DeepCopy());
			}
701
			
702
703
704
705
706
707
708
709
710
711
712
713
714
715
716
717
718
719
720
			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
721
722
					else if (attr.Event.Name == "Click")
						newObj.Click += delegate {  };
723
724
					else if (attr.Event.Name == "Change") //text element
						(newObj as SvgText).Change += delegate {  };
725
726
727
				}
			}
			
728
729
730
731
732
733
734
			if(this._customAttributes.Count > 0)
			{
				foreach (var element in _customAttributes) 
				{
					newObj.CustomAttributes.Add(element.Key, element.Value);
				}
			}
735
736
				
			return newObj;
Tebjan Halm's avatar
Tebjan Halm committed
737
        }
738
739
740
741
742
743
744
745
746
747
748
749
750
751
		
		/// <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
752
753
754
755
756
757
758
759
760
761
762
763
764
765
		
		/// <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
766
767
768
769

        #region graphical EVENTS

        /*  
770
771
772
            	onfocusin = "<anything>"
            	onfocusout = "<anything>"
            	onactivate = "<anything>"
773
774
775
776
777
                onclick = "<anything>"
                onmousedown = "<anything>"
                onmouseup = "<anything>"
                onmouseover = "<anything>"
                onmousemove = "<anything>"
778
            	onmouseout = "<anything>" 
Tebjan Halm's avatar
Tebjan Halm committed
779
780
         */

Eric Domke's avatar
Eric Domke committed
781
#if Net4
782
783
784
        /// <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.
785
        /// The SvgTextElement overwrites this and regsiters the Change event tor its text content.
786
787
        /// </summary>
        /// <param name="caller"></param>
788
        public virtual void RegisterEvents(ISvgEventCaller caller)
789
        {
Tebjan Halm's avatar
Tebjan Halm committed
790
            if (caller != null && !string.IsNullOrEmpty(this.ID))
791
792
793
            {
                var rpcID = this.ID + "/";

794
795
796
797
798
799
                caller.RegisterAction<float, float, int, int, bool, bool, bool, string>(rpcID + "onclick", CreateMouseEventAction(RaiseMouseClick));
                caller.RegisterAction<float, float, int, int, bool, bool, bool, string>(rpcID + "onmousedown", CreateMouseEventAction(RaiseMouseDown));
                caller.RegisterAction<float, float, int, int, bool, bool, bool, string>(rpcID + "onmouseup", CreateMouseEventAction(RaiseMouseUp));
                caller.RegisterAction<float, float, int, int, bool, bool, bool, string>(rpcID + "onmousemove", CreateMouseEventAction(RaiseMouseMove));
                caller.RegisterAction<float, float, int, int, bool, bool, bool, string>(rpcID + "onmouseover", CreateMouseEventAction(RaiseMouseOver));
                caller.RegisterAction<float, float, int, int, bool, bool, bool, string>(rpcID + "onmouseout", CreateMouseEventAction(RaiseMouseOut));
800
                caller.RegisterAction<int, bool, bool, bool, string>(rpcID + "onmousescroll", OnMouseScroll);
801
802
            }
        }
803
804
805
806
807
        
        /// <summary>
        /// Use this method to provide your implementation ISvgEventCaller to unregister Actions
        /// </summary>
        /// <param name="caller"></param>
808
        public virtual void UnregisterEvents(ISvgEventCaller caller)
809
        {
Tebjan Halm's avatar
Tebjan Halm committed
810
        	if (caller != null && !string.IsNullOrEmpty(this.ID))
811
812
813
814
815
816
817
        	{
        		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
818
        		caller.UnregisterAction(rpcID + "onmousescroll");
819
820
821
822
        		caller.UnregisterAction(rpcID + "onmouseover");
        		caller.UnregisterAction(rpcID + "onmouseout");
        	}
        }
Eric Domke's avatar
Eric Domke committed
823
#endif
824

Tebjan Halm's avatar
Tebjan Halm committed
825
826
827
        [SvgAttribute("onclick")]
        public event EventHandler<MouseArg> Click;

828
829
830
831
832
        [SvgAttribute("onmousedown")]
        public event EventHandler<MouseArg> MouseDown;

        [SvgAttribute("onmouseup")]
        public event EventHandler<MouseArg> MouseUp;
833
834
        
        [SvgAttribute("onmousemove")]
835
        public event EventHandler<MouseArg> MouseMove;
836

joreg's avatar
joreg committed
837
        [SvgAttribute("onmousescroll")]
838
        public event EventHandler<MouseScrollArg> MouseScroll;
joreg's avatar
joreg committed
839
        
840
        [SvgAttribute("onmouseover")]
841
        public event EventHandler<MouseArg> MouseOver;
842
843

        [SvgAttribute("onmouseout")]
844
        public event EventHandler<MouseArg> MouseOut;
845
        
Eric Domke's avatar
Eric Domke committed
846
#if Net4
847
        protected Action<float, float, int, int, bool, bool, bool, string> CreateMouseEventAction(Action<object, MouseArg> eventRaiser)
Tebjan Halm's avatar
Tebjan Halm committed
848
        {
849
850
        	return (x, y, button, clickCount, altKey, shiftKey, ctrlKey, sessionID) =>
        		eventRaiser(this, new MouseArg { x = x, y = y, Button = button, ClickCount = clickCount, AltKey = altKey, ShiftKey = shiftKey, CtrlKey = ctrlKey, SessionID = sessionID });
851
        }
Eric Domke's avatar
Eric Domke committed
852
853
#endif

854
        //click
855
856
857
858
859
860
        protected void RaiseMouseClick(object sender, MouseArg e)
        {
        	var handler = Click;
        	if (handler != null)
        	{
        		handler(sender, e);
861
862
863
            }
        }

864
        //down
Tebjan Halm's avatar
Tebjan Halm committed
865
866
867
        protected void RaiseMouseDown(object sender, MouseArg e)
        {
        	var handler = MouseDown;
868
869
            if (handler != null)
            {
Tebjan Halm's avatar
Tebjan Halm committed
870
                handler(sender, e);
871
872
873
            }
        }

874
875
876
877
        //up
        protected void RaiseMouseUp(object sender, MouseArg e)
        {
        	var handler = MouseUp;
878
879
            if (handler != null)
            {
880
                handler(sender, e);
881
882
            }
        }
883
884

        protected void RaiseMouseMove(object sender, MouseArg e)
885
886
        {
        	var handler = MouseMove;
887
888
            if (handler != null)
            {
889
                handler(sender, e);
890
891
            }
        }
joreg's avatar
joreg committed
892
        
893
894
        //over
        protected void RaiseMouseOver(object sender, MouseArg args)
895
896
        {
        	var handler = MouseOver;
897
898
            if (handler != null)
            {
Tebjan Halm's avatar
Tebjan Halm committed
899
                handler(sender, args);
Tebjan Halm's avatar
Tebjan Halm committed
900
901
902
            }
        }

903
        //out
904
        protected void RaiseMouseOut(object sender, MouseArg args)
905
        {
906
        	var handler = MouseOut;
907
908
            if (handler != null)
            {
Tebjan Halm's avatar
Tebjan Halm committed
909
                handler(sender, args);
910
911
            }
        }
joreg's avatar
joreg committed
912
        
913
914
        
        //scroll
915
        protected void OnMouseScroll(int scroll, bool ctrlKey, bool shiftKey, bool altKey, string sessionID)
joreg's avatar
joreg committed
916
        {
917
        	RaiseMouseScroll(this, new MouseScrollArg { Scroll = scroll, AltKey = altKey, ShiftKey = shiftKey, CtrlKey = ctrlKey, SessionID = sessionID});
joreg's avatar
joreg committed
918
919
        }
        
920
        protected void RaiseMouseScroll(object sender, MouseScrollArg e)
joreg's avatar
joreg committed
921
        {
922
        	var handler = MouseScroll;
joreg's avatar
joreg committed
923
924
            if (handler != null)
            {
925
                handler(sender, e);
joreg's avatar
joreg committed
926
927
            }
        }
928
        
Tebjan Halm's avatar
Tebjan Halm committed
929
930
        #endregion graphical EVENTS
    }
931
    
Tebjan Halm's avatar
Tebjan Halm committed
932
933
934
935
936
937
    public class SVGArg : EventArgs
    {
    	public string SessionID;
    }
    	
    
938
939
940
    /// <summary>
    /// Describes the Attribute which was set
    /// </summary>
Tebjan Halm's avatar
Tebjan Halm committed
941
    public class AttributeEventArgs : SVGArg
942
943
944
945
    {
    	public string Attribute;
    	public object Value;
    }
tebjan's avatar
tebjan committed
946
    
tebjan's avatar
tebjan committed
947
948
949
950
951
952
953
954
    /// <summary>
    /// Content of this whas was set
    /// </summary>
    public class ContentEventArgs : SVGArg
    {
    	public string Content;
    }
    
tebjan's avatar
tebjan committed
955
956
957
958
959
960
    /// <summary>
    /// Describes the Attribute which was set
    /// </summary>
    public class ChildAddedEventArgs : SVGArg
    {
    	public SvgElement NewChild;
961
    	public SvgElement BeforeSibling;
tebjan's avatar
tebjan committed
962
    }
Tebjan Halm's avatar
Tebjan Halm committed
963

Eric Domke's avatar
Eric Domke committed
964
#if Net4
965
966
967
    //deriving class registers event actions and calls the actions if the event occurs
    public interface ISvgEventCaller
    {
968
        void RegisterAction(string rpcID, Action action);
969
970
971
972
        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
973
        void RegisterAction<T1, T2, T3, T4, T5>(string rpcID, Action<T1, T2, T3, T4, T5> action);
974
975
976
        void RegisterAction<T1, T2, T3, T4, T5, T6>(string rpcID, Action<T1, T2, T3, T4, T5, T6> action);
        void RegisterAction<T1, T2, T3, T4, T5, T6, T7>(string rpcID, Action<T1, T2, T3, T4, T5, T6, T7> action);
        void RegisterAction<T1, T2, T3, T4, T5, T6, T7, T8>(string rpcID, Action<T1, T2, T3, T4, T5, T6, T7, T8> action);
977
        void UnregisterAction(string rpcID);
978
    }
Eric Domke's avatar
Eric Domke committed
979
#endif
980

Tebjan Halm's avatar
Tebjan Halm committed
981
982
983
    /// <summary>
    /// Represents the state of the mouse at the moment the event occured.
    /// </summary>
Tebjan Halm's avatar
Tebjan Halm committed
984
    public class MouseArg : SVGArg
Tebjan Halm's avatar
Tebjan Halm committed
985
986
987
988
989
    {
        public float x;
        public float y;

        /// <summary>
990
        /// 1 = left, 2 = middle, 3 = right
Tebjan Halm's avatar
Tebjan Halm committed
991
        /// </summary>
992
        public int Button;
993
        
994
995
996
        /// <summary>
        /// Amount of mouse clicks, e.g. 2 for double click
        /// </summary>
997
        public int ClickCount = -1;
998
999
1000
        
        /// <summary>
        /// Alt modifier key pressed