SvgElement.cs 35.7 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
        /// <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; }
        }

152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
        public IEnumerable<SvgElement> Parents
        {
            get
            {
                var curr = this;
                while (curr.Parent != null)
                {
                    curr = curr.Parent;
                    yield return curr;
                }
            }
        }
        public IEnumerable<SvgElement> ParentsAndSelf
        {
            get
            {
                var curr = this;
                yield return curr;
                while (curr.Parent != null)
                {
                    curr = curr.Parent;
                    yield return curr;
                }
            }
        }

davescriven's avatar
davescriven committed
178
179
180
181
182
        /// <summary>
        /// Gets the owner <see cref="SvgDocument"/>.
        /// </summary>
        public virtual SvgDocument OwnerDocument
        {
183
184
185
186
187
188
189
190
191
192
193
194
195
196
        	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
197
198
199
200
201
202
203
204
205
206
207
208
209
210
211
212
213
214
        }

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

215
216
217
218
        /// <summary>
        /// Gets a collection of custom attributes
        /// </summary>
        public SvgCustomAttributeCollection CustomAttributes
219
220
221
222
        {
            get { return this._customAttributes; }
        }

223
224
225
226
227
        /// <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
228
        {
229
            _graphicsMatrix = renderer.Transform;
230

davescriven's avatar
davescriven committed
231
232
233
234
235
236
            // Return if there are no transforms
            if (this.Transforms == null || this.Transforms.Count == 0)
            {
                return;
            }

237
            Matrix transformMatrix = renderer.Transform;
davescriven's avatar
davescriven committed
238
239
240

            foreach (SvgTransform transformation in this.Transforms)
            {
241
                transformMatrix.Multiply(transformation.Matrix);
davescriven's avatar
davescriven committed
242
243
            }

244
            renderer.Transform = transformMatrix;
davescriven's avatar
davescriven committed
245
246
        }

247
248
249
250
251
        /// <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
252
        {
253
            renderer.Transform = _graphicsMatrix;
davescriven's avatar
davescriven committed
254
255
256
            _graphicsMatrix = null;
        }

257
258
259
260
261
        /// <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
262
        {
263
            this.PushTransforms(renderer);
davescriven's avatar
davescriven committed
264
265
        }

266
267
268
269
270
        /// <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
271
        {
272
            this.PopTransforms(renderer);
davescriven's avatar
davescriven committed
273
274
275
276
277
278
279
280
281
        }

        /// <summary>
        /// Gets or sets the element transforms.
        /// </summary>
        /// <value>The transforms.</value>
        [SvgAttribute("transform")]
        public SvgTransformCollection Transforms
        {
282
            get { return (this.Attributes.GetAttribute<SvgTransformCollection>("transform")); }
Tebjan Halm's avatar
Tebjan Halm committed
283
284
285
286
287
288
            set 
            { 
            	var old = this.Transforms;
            	if(old != null)
            		old.TransformChanged -= Attributes_AttributeChanged;
            	value.TransformChanged += Attributes_AttributeChanged;
289
            	this.Attributes["transform"] = value; 
Tebjan Halm's avatar
Tebjan Halm committed
290
            }
davescriven's avatar
davescriven committed
291
292
293
294
295
296
297
298
299
        }

        /// <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
        {
300
            get { return this.Attributes.GetAttribute<string>("id"); }
davescriven's avatar
davescriven committed
301
302
            set
            {
tebjan's avatar
tebjan committed
303
                SetAndForceUniqueID(value, false);
304
305
            }
        }
davescriven's avatar
davescriven committed
306

tebjan's avatar
tebjan committed
307
        public void SetAndForceUniqueID(string value, bool autoForceUniqueID = true, Action<SvgElement, string, string> logElementOldIDNewID = null)
308
309
310
311
312
313
        {
            // Don't do anything if it hasn't changed
            if (string.Compare(this.ID, value) == 0)
            {
                return;
            }
davescriven's avatar
davescriven committed
314

315
316
317
318
            if (this.OwnerDocument != null)
            {
                this.OwnerDocument.IdManager.Remove(this);
            }
davescriven's avatar
davescriven committed
319

320
            this.Attributes["id"] = value;
321
322
323

            if (this.OwnerDocument != null)
            {
tebjan's avatar
tebjan committed
324
                this.OwnerDocument.IdManager.AddAndForceUniqueID(this, null, autoForceUniqueID, logElementOldIDNewID);
davescriven's avatar
davescriven committed
325
326
327
            }
        }

328
329
330
331
        /// <summary>
        /// Only used by the ID Manager
        /// </summary>
        /// <param name="newID"></param>
tebjan's avatar
tebjan committed
332
        internal void ForceUniqueID(string newID)
333
        {
334
            this.Attributes["id"] = newID;
335
336
        }

337
338
339
340
341
342
        /// <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>
343
        protected virtual void AddElement(SvgElement child, int index)
davescriven's avatar
davescriven committed
344
345
        {
        }
tebjan's avatar
tebjan committed
346
347
348
349
350
        
        /// <summary>
        /// Fired when an Element was added to the children of this Element
        /// </summary>
		public event EventHandler<ChildAddedEventArgs> ChildAdded;
davescriven's avatar
davescriven committed
351

352
353
354
355
356
        /// <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
357
358
        internal void OnElementAdded(SvgElement child, int index)
        {
359
            this.AddElement(child, index);
360
361
362
363
364
            SvgElement sibling = null;
            if(index < (Children.Count - 1))
            {
            	sibling = Children[index + 1];
            }
tebjan's avatar
tebjan committed
365
366
367
            var handler = ChildAdded;
            if(handler != null)
            {
368
            	handler(this, new ChildAddedEventArgs { NewChild = child, BeforeSibling = sibling });
tebjan's avatar
tebjan committed
369
            }
davescriven's avatar
davescriven committed
370
371
        }

372
373
374
375
376
        /// <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>
377
        protected virtual void RemoveElement(SvgElement child)
davescriven's avatar
davescriven committed
378
379
380
        {
        }

381
382
383
384
        /// <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
385
386
        internal void OnElementRemoved(SvgElement child)
        {
387
            this.RemoveElement(child);
davescriven's avatar
davescriven committed
388
389
390
391
392
        }

        /// <summary>
        /// Initializes a new instance of the <see cref="SvgElement"/> class.
        /// </summary>
393
        public SvgElement()
davescriven's avatar
davescriven committed
394
395
396
397
        {
            this._children = new SvgElementCollection(this);
            this._eventHandlers = new EventHandlerList();
            this._elementName = string.Empty;
398
399
            this._customAttributes = new SvgCustomAttributeCollection(this);
            
Tebjan Halm's avatar
Tebjan Halm committed
400
401
            Transforms = new SvgTransformCollection();
            
402
403
404
            //subscribe to attribute events
            Attributes.AttributeChanged += Attributes_AttributeChanged;
            CustomAttributes.AttributeChanged += Attributes_AttributeChanged;
405

406
407
408
409
410
            //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
411

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

417
        }
418

419
420
421
422
423
424
        //dispatch attribute event
        void Attributes_AttributeChanged(object sender, AttributeEventArgs e)
        {
        	OnAttributeChanged(e);
        }

425
426
		public virtual void InitialiseFromXML(XmlTextReader reader, SvgDocument document)
		{
427
            throw new NotImplementedException();
428
429
430
		}


431
432
433
434
435
        /// <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
436
        {
437
            this.Render(renderer);
davescriven's avatar
davescriven committed
438
439
440
441
        }

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

davescriven's avatar
davescriven committed
446
            this.Write(writer);
Tebjan Halm's avatar
Tebjan Halm committed
447
448
449

            //Switch culture back
            Thread.CurrentThread.CurrentCulture = previousCulture;
davescriven's avatar
davescriven committed
450
451
452
453
454
455
456
        }

        protected virtual void WriteStartElement(XmlTextWriter writer)
        {
            if (this.ElementName != String.Empty)
            {
                writer.WriteStartElement(this.ElementName);
Tebjan Halm's avatar
Tebjan Halm committed
457
458
                if (this.ElementName == "svg")
                {
459
460
461
462
463
464
465
466
467
					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
468
469
470
471
472
473
474
475
476
477
478
479
480
481
            }
            this.WriteAttributes(writer);
        }

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

        protected virtual void WriteAttributes(XmlTextWriter writer)
        {
482
483
            //properties
            foreach (var attr in _svgPropertyAttributes)
484
485
486
487
488
            {
                if (attr.Property.Converter.CanConvertTo(typeof(string)))
                {
                    object propertyValue = attr.Property.GetValue(this);

489
490
491
                    var forceWrite = false;
                    if ((attr.Attribute.Name == "fill") && (Parent != null))
                    {
492
                    	if(propertyValue == SvgColourServer.NotSet) continue;
493
                    	
494
495
                        object parentValue;
                        if (TryResolveParentAttributeValue(attr.Attribute.Name, out parentValue))
496
                        {
497
498
                            if ((parentValue == propertyValue) 
                                || ((parentValue != null) &&  parentValue.Equals(propertyValue)))
499
                                continue;
500
                            
501
502
503
504
                            forceWrite = true;
                        }
                    }

505
506
                    if (propertyValue != null)
                    {
Tebjan Halm's avatar
Tebjan Halm committed
507
                        var type = propertyValue.GetType();
508
                        string value = (string)attr.Property.Converter.ConvertTo(propertyValue, typeof(string));
Tebjan Halm's avatar
Tebjan Halm committed
509

510
                        if (!SvgDefaults.IsDefault(attr.Attribute.Name, value) || forceWrite)
Tebjan Halm's avatar
Tebjan Halm committed
511
                        {
512
                            writer.WriteAttributeString(attr.Attribute.NamespaceAndName, value);
Tebjan Halm's avatar
Tebjan Halm committed
513
514
515
516
                        }
                    }
                    else if(attr.Attribute.Name == "fill") //if fill equals null, write 'none'
                    {
517
518
                        string value = (string)attr.Property.Converter.ConvertTo(propertyValue, typeof(string));
                        writer.WriteAttributeString(attr.Attribute.NamespaceAndName, value);
519
520
521
                    }
                }
            }
522

Tebjan Halm's avatar
Tebjan Halm committed
523
            
524
            //events
Tebjan Halm's avatar
Tebjan Halm committed
525
            if(AutoPublishEvents)
526
            {
527
528
529
530
	            foreach (var attr in _svgEventAttributes)
	            {
	                var evt = attr.Event.GetValue(this);
	                
531
	                //if someone has registered publish the attribute
Tebjan Halm's avatar
Tebjan Halm committed
532
	                if (evt != null && !string.IsNullOrEmpty(this.ID))
533
534
535
536
	                {
	                    writer.WriteAttributeString(attr.Attribute.Name, this.ID + "/" + attr.Attribute.Name);
	                }
	            }
537
538
            }

539
540
541
542
543
            //add the custom attributes
            foreach (var item in this._customAttributes)
            {
                writer.WriteAttributeString(item.Key, item.Value);
            }
544
        }
Tebjan Halm's avatar
Tebjan Halm committed
545
546
        
        public bool AutoPublishEvents = true;
547

548
        private bool TryResolveParentAttributeValue(string attributeKey, out object parentAttributeValue)
549
        {
550
            parentAttributeValue = null;
551

tebjan's avatar
tebjan committed
552
            //attributeKey = char.ToUpper(attributeKey[0]) + attributeKey.Substring(1);
553
554

            var currentParent = Parent;
555
            var resolved = false;
556
557
558
559
            while (currentParent != null)
            {
                if (currentParent.Attributes.ContainsKey(attributeKey))
                {
560
561
562
                    resolved = true;
                    parentAttributeValue = currentParent.Attributes[attributeKey];
                    if (parentAttributeValue != null)
563
564
565
566
                        break;
                }
                currentParent = currentParent.Parent;
            }
567

568
            return resolved;
davescriven's avatar
davescriven committed
569
570
571
572
573
574
575
576
577
578
579
580
581
582
        }

        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
583
584
585
586
587
            //write the content
            if(!String.IsNullOrEmpty(this.Content))
                writer.WriteString(this.Content);

            //write all children
davescriven's avatar
davescriven committed
588
589
590
591
592
593
594
            foreach (SvgElement child in this.Children)
            {
                child.Write(writer);
            }
        }

        /// <summary>
595
        /// Renders the <see cref="SvgElement"/> and contents to the specified <see cref="SvgRenderer"/> object.
davescriven's avatar
davescriven committed
596
        /// </summary>
597
598
        /// <param name="renderer">The <see cref="SvgRenderer"/> object to render to.</param>
        protected virtual void Render(SvgRenderer renderer)
davescriven's avatar
davescriven committed
599
        {
600
601
602
            this.PushTransforms(renderer);
            this.RenderChildren(renderer);
            this.PopTransforms(renderer);
davescriven's avatar
davescriven committed
603
604
        }

605
606
607
608
609
        /// <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
610
611
612
        {
            foreach (SvgElement element in this.Children)
            {
613
                element.Render(renderer);
davescriven's avatar
davescriven committed
614
615
616
            }
        }

617
618
619
620
621
        /// <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
622
        {
623
            this.Render(renderer);
davescriven's avatar
davescriven committed
624
        }
Tebjan Halm's avatar
Tebjan Halm committed
625
626
627
628
629
630
631
632
        
        /// <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
633
        	foreach(var child in elem.Children)
Tebjan Halm's avatar
Tebjan Halm committed
634
        	{
Tebjan Halm's avatar
Tebjan Halm committed
635
        		if (child is SvgVisualElement)
Tebjan Halm's avatar
Tebjan Halm committed
636
        		{
Tebjan Halm's avatar
Tebjan Halm committed
637
        			if(!(child is SvgGroup))
638
        			{
Tebjan Halm's avatar
Tebjan Halm committed
639
        				var childPath = ((SvgVisualElement)child).Path;
640
        				
Tebjan Halm's avatar
Tebjan Halm committed
641
642
643
644
645
        				if (childPath != null)
        				{
        					childPath = (GraphicsPath)childPath.Clone();
        					if(child.Transforms != null)
        						childPath.Transform(child.Transforms.GetMatrix());
646
647

                            if (childPath.PointCount > 0) path.AddPath(childPath, false);
Tebjan Halm's avatar
Tebjan Halm committed
648
        				}
649
        			}
Tebjan Halm's avatar
Tebjan Halm committed
650
        		}
Tebjan Halm's avatar
Tebjan Halm committed
651
        			
Tebjan Halm's avatar
Tebjan Halm committed
652
        		AddPaths(child, path);
Tebjan Halm's avatar
Tebjan Halm committed
653
        	}
Tebjan Halm's avatar
Tebjan Halm committed
654
655
656
657
658
659
660
661
662
663
664
665
666
667
668
669
670
671
672
673
674
675
676
677
678
679
680
681
682
683
684
685
686
687
688
689
690
        }
        
        /// <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
691
        	
Tebjan Halm's avatar
Tebjan Halm committed
692
        	return ret;
Tebjan Halm's avatar
Tebjan Halm committed
693
        }
davescriven's avatar
davescriven committed
694

695
696
697
698
699
700
        /// <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
701
702
703
704
        public virtual object Clone()
        {
            return this.MemberwiseClone();
        }
705
706
707
708
709
710

    	public abstract SvgElement DeepCopy();

		public virtual SvgElement DeepCopy<T>() where T : SvgElement, new()
		{
			var newObj = new T();
711
			newObj.ID = this.ID;
712
713
			newObj.Content = this.Content;
			newObj.ElementName = this.ElementName;
714
			
715
716
717
718
719
//			if (this.Parent != null)
	//			this.Parent.Children.Add(newObj);

			if (this.Transforms != null)
			{
720
				newObj.Transforms = this.Transforms.Clone() as SvgTransformCollection;
721
722
723
724
725
726
			}

			foreach (var child in this.Children)
			{
				newObj.Children.Add(child.DeepCopy());
			}
727
			
728
729
730
731
732
733
734
735
736
737
738
739
740
741
742
743
744
745
746
			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
747
748
					else if (attr.Event.Name == "Click")
						newObj.Click += delegate {  };
749
750
					else if (attr.Event.Name == "Change") //text element
						(newObj as SvgText).Change += delegate {  };
751
752
753
				}
			}
			
754
755
756
757
758
759
760
			if(this._customAttributes.Count > 0)
			{
				foreach (var element in _customAttributes) 
				{
					newObj.CustomAttributes.Add(element.Key, element.Value);
				}
			}
761
762
				
			return newObj;
Tebjan Halm's avatar
Tebjan Halm committed
763
        }
764
765
766
767
768
769
770
771
772
773
774
775
776
777
		
		/// <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
778
779
780
781
782
783
784
785
786
787
788
789
790
791
		
		/// <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
792
793
794
795

        #region graphical EVENTS

        /*  
796
797
798
            	onfocusin = "<anything>"
            	onfocusout = "<anything>"
            	onactivate = "<anything>"
799
800
801
802
803
                onclick = "<anything>"
                onmousedown = "<anything>"
                onmouseup = "<anything>"
                onmouseover = "<anything>"
                onmousemove = "<anything>"
804
            	onmouseout = "<anything>" 
Tebjan Halm's avatar
Tebjan Halm committed
805
806
         */

Eric Domke's avatar
Eric Domke committed
807
#if Net4
808
809
810
        /// <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.
811
        /// The SvgTextElement overwrites this and regsiters the Change event tor its text content.
812
813
        /// </summary>
        /// <param name="caller"></param>
814
        public virtual void RegisterEvents(ISvgEventCaller caller)
815
        {
Tebjan Halm's avatar
Tebjan Halm committed
816
            if (caller != null && !string.IsNullOrEmpty(this.ID))
817
818
819
            {
                var rpcID = this.ID + "/";

820
821
822
823
824
825
                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));
826
                caller.RegisterAction<int, bool, bool, bool, string>(rpcID + "onmousescroll", OnMouseScroll);
827
828
            }
        }
829
830
831
832
833
        
        /// <summary>
        /// Use this method to provide your implementation ISvgEventCaller to unregister Actions
        /// </summary>
        /// <param name="caller"></param>
834
        public virtual void UnregisterEvents(ISvgEventCaller caller)
835
        {
Tebjan Halm's avatar
Tebjan Halm committed
836
        	if (caller != null && !string.IsNullOrEmpty(this.ID))
837
838
839
840
841
842
843
        	{
        		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
844
        		caller.UnregisterAction(rpcID + "onmousescroll");
845
846
847
848
        		caller.UnregisterAction(rpcID + "onmouseover");
        		caller.UnregisterAction(rpcID + "onmouseout");
        	}
        }
Eric Domke's avatar
Eric Domke committed
849
#endif
850

Tebjan Halm's avatar
Tebjan Halm committed
851
852
853
        [SvgAttribute("onclick")]
        public event EventHandler<MouseArg> Click;

854
855
856
857
858
        [SvgAttribute("onmousedown")]
        public event EventHandler<MouseArg> MouseDown;

        [SvgAttribute("onmouseup")]
        public event EventHandler<MouseArg> MouseUp;
859
860
        
        [SvgAttribute("onmousemove")]
861
        public event EventHandler<MouseArg> MouseMove;
862

joreg's avatar
joreg committed
863
        [SvgAttribute("onmousescroll")]
864
        public event EventHandler<MouseScrollArg> MouseScroll;
joreg's avatar
joreg committed
865
        
866
        [SvgAttribute("onmouseover")]
867
        public event EventHandler<MouseArg> MouseOver;
868
869

        [SvgAttribute("onmouseout")]
870
        public event EventHandler<MouseArg> MouseOut;
871
        
Eric Domke's avatar
Eric Domke committed
872
#if Net4
873
        protected Action<float, float, int, int, bool, bool, bool, string> CreateMouseEventAction(Action<object, MouseArg> eventRaiser)
Tebjan Halm's avatar
Tebjan Halm committed
874
        {
875
876
        	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 });
877
        }
Eric Domke's avatar
Eric Domke committed
878
879
#endif

880
        //click
881
882
883
884
885
886
        protected void RaiseMouseClick(object sender, MouseArg e)
        {
        	var handler = Click;
        	if (handler != null)
        	{
        		handler(sender, e);
887
888
889
            }
        }

890
        //down
Tebjan Halm's avatar
Tebjan Halm committed
891
892
893
        protected void RaiseMouseDown(object sender, MouseArg e)
        {
        	var handler = MouseDown;
894
895
            if (handler != null)
            {
Tebjan Halm's avatar
Tebjan Halm committed
896
                handler(sender, e);
897
898
899
            }
        }

900
901
902
903
        //up
        protected void RaiseMouseUp(object sender, MouseArg e)
        {
        	var handler = MouseUp;
904
905
            if (handler != null)
            {
906
                handler(sender, e);
907
908
            }
        }
909
910

        protected void RaiseMouseMove(object sender, MouseArg e)
911
912
        {
        	var handler = MouseMove;
913
914
            if (handler != null)
            {
915
                handler(sender, e);
916
917
            }
        }
joreg's avatar
joreg committed
918
        
919
920
        //over
        protected void RaiseMouseOver(object sender, MouseArg args)
921
922
        {
        	var handler = MouseOver;
923
924
            if (handler != null)
            {
Tebjan Halm's avatar
Tebjan Halm committed
925
                handler(sender, args);
Tebjan Halm's avatar
Tebjan Halm committed
926
927
928
            }
        }

929
        //out
930
        protected void RaiseMouseOut(object sender, MouseArg args)
931
        {
932
        	var handler = MouseOut;
933
934
            if (handler != null)
            {
Tebjan Halm's avatar
Tebjan Halm committed
935
                handler(sender, args);
936
937
            }
        }
joreg's avatar
joreg committed
938
        
939
940
        
        //scroll
941
        protected void OnMouseScroll(int scroll, bool ctrlKey, bool shiftKey, bool altKey, string sessionID)
joreg's avatar
joreg committed
942
        {
943
        	RaiseMouseScroll(this, new MouseScrollArg { Scroll = scroll, AltKey = altKey, ShiftKey = shiftKey, CtrlKey = ctrlKey, SessionID = sessionID});
joreg's avatar
joreg committed
944
945
        }
        
946
        protected void RaiseMouseScroll(object sender, MouseScrollArg e)
joreg's avatar
joreg committed
947
        {
948
        	var handler = MouseScroll;
joreg's avatar
joreg committed
949
950
            if (handler != null)
            {
951
                handler(sender, e);
joreg's avatar
joreg committed
952
953
            }
        }
954
        
Tebjan Halm's avatar
Tebjan Halm committed
955
956
        #endregion graphical EVENTS
    }
957
    
Tebjan Halm's avatar
Tebjan Halm committed
958
959
960
961
962
963
    public class SVGArg : EventArgs
    {
    	public string SessionID;
    }
    	
    
964
965
966
    /// <summary>
    /// Describes the Attribute which was set
    /// </summary>
Tebjan Halm's avatar
Tebjan Halm committed
967
    public class AttributeEventArgs : SVGArg
968
969
970
971
    {
    	public string Attribute;
    	public object Value;
    }
tebjan's avatar
tebjan committed
972
    
tebjan's avatar
tebjan committed
973
974
975
976
977
978
979
980
    /// <summary>
    /// Content of this whas was set
    /// </summary>
    public class ContentEventArgs : SVGArg
    {
    	public string Content;
    }
    
tebjan's avatar
tebjan committed
981
982
983
984
985
986
    /// <summary>
    /// Describes the Attribute which was set
    /// </summary>
    public class ChildAddedEventArgs : SVGArg
    {
    	public SvgElement NewChild;
987
    	public SvgElement BeforeSibling;
tebjan's avatar
tebjan committed
988
    }
Tebjan Halm's avatar
Tebjan Halm committed
989

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