SvgElement.cs 37.3 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
        internal SvgElement _parent;
        private string _elementName;
        private SvgAttributeCollection _attributes;
        private EventHandlerList _eventHandlers;
        private SvgElementCollection _children;
        private static readonly object _loadEventKey = new object();
43
        private Region _graphicsClip;
davescriven's avatar
davescriven committed
44
        private Matrix _graphicsMatrix;
45
        private SvgCustomAttributeCollection _customAttributes;
46
        private List<ISvgNode> _nodes = new List<ISvgNode>();
davescriven's avatar
davescriven committed
47

Eric Domke's avatar
Eric Domke committed
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70

        private Dictionary<string, SortedDictionary<int, string>> _styles = new Dictionary<string, SortedDictionary<int, string>>();
        
        public void AddStyle(string name, string value, int specificity)
        {
            SortedDictionary<int, string> rules;
            if (!_styles.TryGetValue(name, out rules))
            {
                rules = new SortedDictionary<int, string>();
                _styles[name] = rules;
            }
            while (rules.ContainsKey(specificity)) specificity++;
            rules[specificity] = value;
        }
        public void FlushStyles()
        {
            foreach (var s in _styles)
            {
                SvgElementFactory.SetPropertyValue(this, s.Key, s.Value.Last().Value, this.OwnerDocument);
            }
            _styles = null;
        }

davescriven's avatar
davescriven committed
71
72
73
        /// <summary>
        /// Gets the name of the element.
        /// </summary>
74
        protected internal string ElementName
davescriven's avatar
davescriven committed
75
        {
76
77
78
79
80
81
82
83
84
85
86
87
88
89
            get
            {
                if (string.IsNullOrEmpty(this._elementName))
                {
                    var attr = TypeDescriptor.GetAttributes(this).OfType<SvgElementAttribute>().SingleOrDefault();

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

                return this._elementName;
            }
90
            internal set { this._elementName = value; }
davescriven's avatar
davescriven committed
91
92
        }

93
94
95
96
97
98
99
100
101
102
        /// <summary>
        /// Gets or sets the color <see cref="SvgPaintServer"/> of this element which drives the currentColor property.
        /// </summary>
        [SvgAttribute("color")]
        public virtual SvgPaintServer Color
        {
            get { return (this.Attributes["color"] == null) ? SvgColourServer.NotSet : (SvgPaintServer)this.Attributes["color"]; }
            set { this.Attributes["color"] = value; }
        }

davescriven's avatar
davescriven committed
103
104
105
        /// <summary>
        /// Gets or sets the content of the element.
        /// </summary>
Tebjan Halm's avatar
Tebjan Halm committed
106
        private string _content;
davescriven's avatar
davescriven committed
107
108
        public virtual string Content
        {
Tebjan Halm's avatar
Tebjan Halm committed
109
110
111
112
113
114
115
116
117
118
119
            get
            {
            	return _content;
            }
            set
            {
            	if(_content != null)
            	{
            		var oldVal = _content;
            		_content = value;
            		if(_content != oldVal)
tebjan's avatar
tebjan committed
120
            			OnContentChanged(new ContentEventArgs{ Content = value });
Tebjan Halm's avatar
Tebjan Halm committed
121
122
123
124
            	}
            	else
            	{
            		_content = value;
tebjan's avatar
tebjan committed
125
            		OnContentChanged(new ContentEventArgs{ Content = value });
Tebjan Halm's avatar
Tebjan Halm committed
126
127
            	}
            }
davescriven's avatar
davescriven committed
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
        }

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

155
156
157
158
159
        public IList<ISvgNode> Nodes
        {
            get { return this._nodes; }
        }

Eric Domke's avatar
Eric Domke committed
160
161
162
163
164
165
166
167
168
        public IEnumerable<SvgElement> Descendants()
        {
            return this.AsEnumerable().Descendants();
        }
        private IEnumerable<SvgElement> AsEnumerable()
        {
            yield return this;
        }

davescriven's avatar
davescriven committed
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
        /// <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; }
        }

186
187
188
189
190
191
192
193
194
195
196
197
198
199
200
201
202
203
204
205
206
207
208
209
210
211
        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
212
213
214
215
216
        /// <summary>
        /// Gets the owner <see cref="SvgDocument"/>.
        /// </summary>
        public virtual SvgDocument OwnerDocument
        {
217
218
219
220
221
222
223
224
225
226
227
228
229
230
        	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
231
232
233
234
235
236
237
238
239
240
241
242
243
244
245
246
247
248
        }

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

249
250
251
252
        /// <summary>
        /// Gets a collection of custom attributes
        /// </summary>
        public SvgCustomAttributeCollection CustomAttributes
253
254
255
256
        {
            get { return this._customAttributes; }
        }

257
258
259
260
        /// <summary>
        /// Applies the required transforms to <see cref="SvgRenderer"/>.
        /// </summary>
        /// <param name="renderer">The <see cref="SvgRenderer"/> to be transformed.</param>
261
        protected internal virtual bool PushTransforms(SvgRenderer renderer)
davescriven's avatar
davescriven committed
262
        {
263
            _graphicsMatrix = renderer.Transform;
264
            _graphicsClip = renderer.Clip;
265

davescriven's avatar
davescriven committed
266
267
268
            // Return if there are no transforms
            if (this.Transforms == null || this.Transforms.Count == 0)
            {
269
                return true;
davescriven's avatar
davescriven committed
270
            }
271
            if (this.Transforms.Count == 1 && this.Transforms[0].Matrix.Equals(new Matrix(0, 0, 0, 0, 0, 0))) return false;
davescriven's avatar
davescriven committed
272

273
            Matrix transformMatrix = renderer.Transform;
davescriven's avatar
davescriven committed
274
275
276

            foreach (SvgTransform transformation in this.Transforms)
            {
277
                transformMatrix.Multiply(transformation.Matrix);
davescriven's avatar
davescriven committed
278
279
            }

280
            renderer.Transform = transformMatrix;
281
282

            return true;
davescriven's avatar
davescriven committed
283
284
        }

285
286
287
288
289
        /// <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
290
        {
291
            renderer.Transform = _graphicsMatrix;
davescriven's avatar
davescriven committed
292
            _graphicsMatrix = null;
293
294
            renderer.SetClip(_graphicsClip);
            _graphicsClip = null;
davescriven's avatar
davescriven committed
295
296
        }

297
298
299
300
301
        /// <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
302
        {
303
            this.PushTransforms(renderer);
davescriven's avatar
davescriven committed
304
305
        }

306
307
308
309
310
        /// <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
311
        {
312
            this.PopTransforms(renderer);
davescriven's avatar
davescriven committed
313
314
315
316
317
318
319
320
321
        }

        /// <summary>
        /// Gets or sets the element transforms.
        /// </summary>
        /// <value>The transforms.</value>
        [SvgAttribute("transform")]
        public SvgTransformCollection Transforms
        {
322
            get { return (this.Attributes.GetAttribute<SvgTransformCollection>("transform")); }
Tebjan Halm's avatar
Tebjan Halm committed
323
324
325
326
327
328
            set 
            { 
            	var old = this.Transforms;
            	if(old != null)
            		old.TransformChanged -= Attributes_AttributeChanged;
            	value.TransformChanged += Attributes_AttributeChanged;
329
            	this.Attributes["transform"] = value; 
Tebjan Halm's avatar
Tebjan Halm committed
330
            }
davescriven's avatar
davescriven committed
331
332
333
334
335
336
337
338
339
        }

        /// <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
        {
340
            get { return this.Attributes.GetAttribute<string>("id"); }
davescriven's avatar
davescriven committed
341
342
            set
            {
tebjan's avatar
tebjan committed
343
                SetAndForceUniqueID(value, false);
344
345
            }
        }
davescriven's avatar
davescriven committed
346

tebjan's avatar
tebjan committed
347
        public void SetAndForceUniqueID(string value, bool autoForceUniqueID = true, Action<SvgElement, string, string> logElementOldIDNewID = null)
348
349
350
351
352
353
        {
            // Don't do anything if it hasn't changed
            if (string.Compare(this.ID, value) == 0)
            {
                return;
            }
davescriven's avatar
davescriven committed
354

355
356
357
358
            if (this.OwnerDocument != null)
            {
                this.OwnerDocument.IdManager.Remove(this);
            }
davescriven's avatar
davescriven committed
359

360
            this.Attributes["id"] = value;
361
362
363

            if (this.OwnerDocument != null)
            {
tebjan's avatar
tebjan committed
364
                this.OwnerDocument.IdManager.AddAndForceUniqueID(this, null, autoForceUniqueID, logElementOldIDNewID);
davescriven's avatar
davescriven committed
365
366
367
            }
        }

368
369
370
371
        /// <summary>
        /// Only used by the ID Manager
        /// </summary>
        /// <param name="newID"></param>
tebjan's avatar
tebjan committed
372
        internal void ForceUniqueID(string newID)
373
        {
374
            this.Attributes["id"] = newID;
375
376
        }

377
378
379
380
381
382
        /// <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>
383
        protected virtual void AddElement(SvgElement child, int index)
davescriven's avatar
davescriven committed
384
385
        {
        }
tebjan's avatar
tebjan committed
386
387
388
389
390
        
        /// <summary>
        /// Fired when an Element was added to the children of this Element
        /// </summary>
		public event EventHandler<ChildAddedEventArgs> ChildAdded;
davescriven's avatar
davescriven committed
391

392
393
394
395
396
        /// <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
397
398
        internal void OnElementAdded(SvgElement child, int index)
        {
399
            this.AddElement(child, index);
400
401
402
403
404
            SvgElement sibling = null;
            if(index < (Children.Count - 1))
            {
            	sibling = Children[index + 1];
            }
tebjan's avatar
tebjan committed
405
406
407
            var handler = ChildAdded;
            if(handler != null)
            {
408
            	handler(this, new ChildAddedEventArgs { NewChild = child, BeforeSibling = sibling });
tebjan's avatar
tebjan committed
409
            }
davescriven's avatar
davescriven committed
410
411
        }

412
413
414
415
416
        /// <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>
417
        protected virtual void RemoveElement(SvgElement child)
davescriven's avatar
davescriven committed
418
419
420
        {
        }

421
422
423
424
        /// <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
425
426
        internal void OnElementRemoved(SvgElement child)
        {
427
            this.RemoveElement(child);
davescriven's avatar
davescriven committed
428
429
430
431
432
        }

        /// <summary>
        /// Initializes a new instance of the <see cref="SvgElement"/> class.
        /// </summary>
433
        public SvgElement()
davescriven's avatar
davescriven committed
434
435
436
437
        {
            this._children = new SvgElementCollection(this);
            this._eventHandlers = new EventHandlerList();
            this._elementName = string.Empty;
438
439
            this._customAttributes = new SvgCustomAttributeCollection(this);
            
Tebjan Halm's avatar
Tebjan Halm committed
440
441
            Transforms = new SvgTransformCollection();
            
442
443
444
            //subscribe to attribute events
            Attributes.AttributeChanged += Attributes_AttributeChanged;
            CustomAttributes.AttributeChanged += Attributes_AttributeChanged;
445

446
447
448
449
450
            //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
451

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

457
        }
458

459
460
461
462
463
464
        //dispatch attribute event
        void Attributes_AttributeChanged(object sender, AttributeEventArgs e)
        {
        	OnAttributeChanged(e);
        }

465
466
		public virtual void InitialiseFromXML(XmlTextReader reader, SvgDocument document)
		{
467
            throw new NotImplementedException();
468
469
470
		}


471
472
473
474
475
        /// <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
476
        {
477
            this.Render(renderer);
davescriven's avatar
davescriven committed
478
479
480
481
        }

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

davescriven's avatar
davescriven committed
486
            this.Write(writer);
Tebjan Halm's avatar
Tebjan Halm committed
487
488
489

            //Switch culture back
            Thread.CurrentThread.CurrentCulture = previousCulture;
davescriven's avatar
davescriven committed
490
491
492
493
494
495
496
        }

        protected virtual void WriteStartElement(XmlTextWriter writer)
        {
            if (this.ElementName != String.Empty)
            {
                writer.WriteStartElement(this.ElementName);
Tebjan Halm's avatar
Tebjan Halm committed
497
498
                if (this.ElementName == "svg")
                {
499
500
501
502
503
504
505
506
507
					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
508
509
510
511
512
513
514
515
516
517
518
519
520
521
            }
            this.WriteAttributes(writer);
        }

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

        protected virtual void WriteAttributes(XmlTextWriter writer)
        {
522
523
            //properties
            foreach (var attr in _svgPropertyAttributes)
524
525
526
527
528
            {
                if (attr.Property.Converter.CanConvertTo(typeof(string)))
                {
                    object propertyValue = attr.Property.GetValue(this);

529
530
531
                    var forceWrite = false;
                    if ((attr.Attribute.Name == "fill") && (Parent != null))
                    {
532
                    	if(propertyValue == SvgColourServer.NotSet) continue;
533
                    	
534
535
                        object parentValue;
                        if (TryResolveParentAttributeValue(attr.Attribute.Name, out parentValue))
536
                        {
537
538
                            if ((parentValue == propertyValue) 
                                || ((parentValue != null) &&  parentValue.Equals(propertyValue)))
539
                                continue;
540
                            
541
542
543
544
                            forceWrite = true;
                        }
                    }

545
546
                    if (propertyValue != null)
                    {
Tebjan Halm's avatar
Tebjan Halm committed
547
                        var type = propertyValue.GetType();
548
                        string value = (string)attr.Property.Converter.ConvertTo(propertyValue, typeof(string));
Tebjan Halm's avatar
Tebjan Halm committed
549

550
                        if (!SvgDefaults.IsDefault(attr.Attribute.Name, value) || forceWrite)
Tebjan Halm's avatar
Tebjan Halm committed
551
                        {
552
                            writer.WriteAttributeString(attr.Attribute.NamespaceAndName, value);
Tebjan Halm's avatar
Tebjan Halm committed
553
554
555
556
                        }
                    }
                    else if(attr.Attribute.Name == "fill") //if fill equals null, write 'none'
                    {
557
558
                        string value = (string)attr.Property.Converter.ConvertTo(propertyValue, typeof(string));
                        writer.WriteAttributeString(attr.Attribute.NamespaceAndName, value);
559
560
561
                    }
                }
            }
562

Tebjan Halm's avatar
Tebjan Halm committed
563
            
564
            //events
Tebjan Halm's avatar
Tebjan Halm committed
565
            if(AutoPublishEvents)
566
            {
567
568
569
	            foreach (var attr in _svgEventAttributes)
	            {
	                var evt = attr.Event.GetValue(this);
Eric Domke's avatar
Eric Domke committed
570

571
	                //if someone has registered publish the attribute
Tebjan Halm's avatar
Tebjan Halm committed
572
	                if (evt != null && !string.IsNullOrEmpty(this.ID))
573
574
575
576
	                {
	                    writer.WriteAttributeString(attr.Attribute.Name, this.ID + "/" + attr.Attribute.Name);
	                }
	            }
577
578
            }

579
580
581
582
583
            //add the custom attributes
            foreach (var item in this._customAttributes)
            {
                writer.WriteAttributeString(item.Key, item.Value);
            }
584
        }
Tebjan Halm's avatar
Tebjan Halm committed
585
586
        
        public bool AutoPublishEvents = true;
587

588
        private bool TryResolveParentAttributeValue(string attributeKey, out object parentAttributeValue)
589
        {
590
            parentAttributeValue = null;
591

tebjan's avatar
tebjan committed
592
            //attributeKey = char.ToUpper(attributeKey[0]) + attributeKey.Substring(1);
593
594

            var currentParent = Parent;
595
            var resolved = false;
596
597
598
599
            while (currentParent != null)
            {
                if (currentParent.Attributes.ContainsKey(attributeKey))
                {
600
601
602
                    resolved = true;
                    parentAttributeValue = currentParent.Attributes[attributeKey];
                    if (parentAttributeValue != null)
603
604
605
606
                        break;
                }
                currentParent = currentParent.Parent;
            }
607

608
            return resolved;
davescriven's avatar
davescriven committed
609
610
611
612
613
614
615
616
617
618
619
620
621
622
        }

        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
623
624
625
626
627
            //write the content
            if(!String.IsNullOrEmpty(this.Content))
                writer.WriteString(this.Content);

            //write all children
davescriven's avatar
davescriven committed
628
629
630
631
632
633
634
            foreach (SvgElement child in this.Children)
            {
                child.Write(writer);
            }
        }

        /// <summary>
635
        /// Renders the <see cref="SvgElement"/> and contents to the specified <see cref="SvgRenderer"/> object.
davescriven's avatar
davescriven committed
636
        /// </summary>
637
638
        /// <param name="renderer">The <see cref="SvgRenderer"/> object to render to.</param>
        protected virtual void Render(SvgRenderer renderer)
davescriven's avatar
davescriven committed
639
        {
640
641
642
            this.PushTransforms(renderer);
            this.RenderChildren(renderer);
            this.PopTransforms(renderer);
davescriven's avatar
davescriven committed
643
644
        }

645
646
647
648
649
        /// <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
650
651
652
        {
            foreach (SvgElement element in this.Children)
            {
653
                element.Render(renderer);
davescriven's avatar
davescriven committed
654
655
656
            }
        }

657
658
659
660
661
        /// <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
662
        {
663
            this.Render(renderer);
davescriven's avatar
davescriven committed
664
        }
Tebjan Halm's avatar
Tebjan Halm committed
665
666
667
668
669
670
671
672
        
        /// <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
673
        	foreach(var child in elem.Children)
Tebjan Halm's avatar
Tebjan Halm committed
674
        	{
Tebjan Halm's avatar
Tebjan Halm committed
675
        		if (child is SvgVisualElement)
Tebjan Halm's avatar
Tebjan Halm committed
676
        		{
Tebjan Halm's avatar
Tebjan Halm committed
677
        			if(!(child is SvgGroup))
678
        			{
679
        				var childPath = ((SvgVisualElement)child).Path(null);
680
        				
Tebjan Halm's avatar
Tebjan Halm committed
681
682
683
684
685
        				if (childPath != null)
        				{
        					childPath = (GraphicsPath)childPath.Clone();
        					if(child.Transforms != null)
        						childPath.Transform(child.Transforms.GetMatrix());
686
687

                            if (childPath.PointCount > 0) path.AddPath(childPath, false);
Tebjan Halm's avatar
Tebjan Halm committed
688
        				}
689
        			}
Tebjan Halm's avatar
Tebjan Halm committed
690
        		}
691
692

                if (!(child is SvgPaintServer)) AddPaths(child, path);
Tebjan Halm's avatar
Tebjan Halm committed
693
        	}
Tebjan Halm's avatar
Tebjan Halm committed
694
695
696
697
698
699
700
        }
        
        /// <summary>
        /// Recursive method to add up the paths of all children
        /// </summary>
        /// <param name="elem"></param>
        /// <param name="path"></param>
701
        protected GraphicsPath GetPaths(SvgElement elem, SvgRenderer renderer)
Tebjan Halm's avatar
Tebjan Halm committed
702
703
704
705
706
707
708
709
710
        {
        	var ret = new GraphicsPath();
        	
        	foreach(var child in elem.Children)
        	{
        		if (child is SvgVisualElement)
        		{
        			if(!(child is SvgGroup))
        			{
711
                        var childPath = ((SvgVisualElement)child).Path(renderer);
Tebjan Halm's avatar
Tebjan Halm committed
712
713
714
715
716
717
718
719
720
721
722
723
        				
        				if (childPath != null)
        				{
        					childPath = (GraphicsPath)childPath.Clone();
        					if(child.Transforms != null)
        						childPath.Transform(child.Transforms.GetMatrix());
        					
        					ret.AddPath(childPath, false);
        				}
        			}
        			else
        			{
724
                        var childPath = GetPaths(child, renderer);
Tebjan Halm's avatar
Tebjan Halm committed
725
726
727
728
729
730
        				if(child.Transforms != null)
        					childPath.Transform(child.Transforms.GetMatrix());
        			}
        		}
        			
        	}
Tebjan Halm's avatar
Tebjan Halm committed
731
        	
Tebjan Halm's avatar
Tebjan Halm committed
732
        	return ret;
Tebjan Halm's avatar
Tebjan Halm committed
733
        }
davescriven's avatar
davescriven committed
734

735
736
737
738
739
740
        /// <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
741
742
743
744
        public virtual object Clone()
        {
            return this.MemberwiseClone();
        }
745
746
747
748
749
750

    	public abstract SvgElement DeepCopy();

		public virtual SvgElement DeepCopy<T>() where T : SvgElement, new()
		{
			var newObj = new T();
751
			newObj.ID = this.ID;
752
753
			newObj.Content = this.Content;
			newObj.ElementName = this.ElementName;
Eric Domke's avatar
Eric Domke committed
754

755
756
757
758
759
//			if (this.Parent != null)
	//			this.Parent.Children.Add(newObj);

			if (this.Transforms != null)
			{
760
				newObj.Transforms = this.Transforms.Clone() as SvgTransformCollection;
761
762
763
764
765
766
			}

			foreach (var child in this.Children)
			{
				newObj.Children.Add(child.DeepCopy());
			}
Eric Domke's avatar
Eric Domke committed
767

768
769
770
			foreach (var attr in this._svgEventAttributes)
			{
				var evt = attr.Event.GetValue(this);
Eric Domke's avatar
Eric Domke committed
771

772
773
774
775
776
777
778
779
780
781
782
783
784
785
786
				//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
787
788
					else if (attr.Event.Name == "Click")
						newObj.Click += delegate {  };
789
790
					else if (attr.Event.Name == "Change") //text element
						(newObj as SvgText).Change += delegate {  };
791
792
				}
			}
Eric Domke's avatar
Eric Domke committed
793

794
795
796
797
798
799
800
			if(this._customAttributes.Count > 0)
			{
				foreach (var element in _customAttributes) 
				{
					newObj.CustomAttributes.Add(element.Key, element.Value);
				}
			}
Eric Domke's avatar
Eric Domke committed
801

802
			return newObj;
Tebjan Halm's avatar
Tebjan Halm committed
803
        }
Eric Domke's avatar
Eric Domke committed
804

805
806
807
808
		/// <summary>
        /// Fired when an Atrribute of this Element has changed
        /// </summary>
		public event EventHandler<AttributeEventArgs> AttributeChanged;
Eric Domke's avatar
Eric Domke committed
809

810
811
812
813
814
815
816
817
		protected void OnAttributeChanged(AttributeEventArgs args)
		{
			var handler = AttributeChanged;
			if(handler != null)
			{
				handler(this, args);
			}
		}
Eric Domke's avatar
Eric Domke committed
818

tebjan's avatar
tebjan committed
819
820
821
822
		/// <summary>
        /// Fired when an Atrribute of this Element has changed
        /// </summary>
		public event EventHandler<ContentEventArgs> ContentChanged;
Eric Domke's avatar
Eric Domke committed
823

tebjan's avatar
tebjan committed
824
825
826
827
828
829
830
831
		protected void OnContentChanged(ContentEventArgs args)
		{
			var handler = ContentChanged;
			if(handler != null)
			{
				handler(this, args);
			}
		}
Tebjan Halm's avatar
Tebjan Halm committed
832
833
834
835

        #region graphical EVENTS

        /*  
836
837
838
            	onfocusin = "<anything>"
            	onfocusout = "<anything>"
            	onactivate = "<anything>"
839
840
841
842
843
                onclick = "<anything>"
                onmousedown = "<anything>"
                onmouseup = "<anything>"
                onmouseover = "<anything>"
                onmousemove = "<anything>"
844
            	onmouseout = "<anything>" 
Tebjan Halm's avatar
Tebjan Halm committed
845
846
         */

Eric Domke's avatar
Eric Domke committed
847
#if Net4
848
849
850
        /// <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.
851
        /// The SvgTextElement overwrites this and regsiters the Change event tor its text content.
852
853
        /// </summary>
        /// <param name="caller"></param>
854
        public virtual void RegisterEvents(ISvgEventCaller caller)
855
        {
Tebjan Halm's avatar
Tebjan Halm committed
856
            if (caller != null && !string.IsNullOrEmpty(this.ID))
857
858
859
            {
                var rpcID = this.ID + "/";

860
861
862
863
864
865
                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));
866
                caller.RegisterAction<int, bool, bool, bool, string>(rpcID + "onmousescroll", OnMouseScroll);
867
868
            }
        }
869
870
871
872
873
        
        /// <summary>
        /// Use this method to provide your implementation ISvgEventCaller to unregister Actions
        /// </summary>
        /// <param name="caller"></param>
874
        public virtual void UnregisterEvents(ISvgEventCaller caller)
875
        {
Tebjan Halm's avatar
Tebjan Halm committed
876
        	if (caller != null && !string.IsNullOrEmpty(this.ID))
877
878
879
880
881
882
883
        	{
        		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
884
        		caller.UnregisterAction(rpcID + "onmousescroll");
885
886
887
888
        		caller.UnregisterAction(rpcID + "onmouseover");
        		caller.UnregisterAction(rpcID + "onmouseout");
        	}
        }
Eric Domke's avatar
Eric Domke committed
889
#endif
890

Tebjan Halm's avatar
Tebjan Halm committed
891
892
893
        [SvgAttribute("onclick")]
        public event EventHandler<MouseArg> Click;

894
895
896
897
898
        [SvgAttribute("onmousedown")]
        public event EventHandler<MouseArg> MouseDown;

        [SvgAttribute("onmouseup")]
        public event EventHandler<MouseArg> MouseUp;
899
900
        
        [SvgAttribute("onmousemove")]
901
        public event EventHandler<MouseArg> MouseMove;
902

joreg's avatar
joreg committed
903
        [SvgAttribute("onmousescroll")]
904
        public event EventHandler<MouseScrollArg> MouseScroll;
joreg's avatar
joreg committed
905
        
906
        [SvgAttribute("onmouseover")]
907
        public event EventHandler<MouseArg> MouseOver;
908
909

        [SvgAttribute("onmouseout")]
910
        public event EventHandler<MouseArg> MouseOut;
911
        
Eric Domke's avatar
Eric Domke committed
912
#if Net4
913
        protected Action<float, float, int, int, bool, bool, bool, string> CreateMouseEventAction(Action<object, MouseArg> eventRaiser)
Tebjan Halm's avatar
Tebjan Halm committed
914
        {
915
916
        	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 });
917
        }
Eric Domke's avatar
Eric Domke committed
918
919
#endif

920
        //click
921
922
923
924
925
926
        protected void RaiseMouseClick(object sender, MouseArg e)
        {
        	var handler = Click;
        	if (handler != null)
        	{
        		handler(sender, e);
927
928
929
            }
        }

930
        //down
Tebjan Halm's avatar
Tebjan Halm committed
931
932
933
        protected void RaiseMouseDown(object sender, MouseArg e)
        {
        	var handler = MouseDown;
934
935
            if (handler != null)
            {
Tebjan Halm's avatar
Tebjan Halm committed
936
                handler(sender, e);
937
938
939
            }
        }

940
941
942
943
        //up
        protected void RaiseMouseUp(object sender, MouseArg e)
        {
        	var handler = MouseUp;
944
945
            if (handler != null)
            {
946
                handler(sender, e);
947
948
            }
        }
949
950

        protected void RaiseMouseMove(object sender, MouseArg e)
951
952
        {
        	var handler = MouseMove;
953
954
            if (handler != null)
            {
955
                handler(sender, e);
956
957
            }
        }
joreg's avatar
joreg committed
958
        
959
960
        //over
        protected void RaiseMouseOver(object sender, MouseArg args)
961
962
        {
        	var handler = MouseOver;
963
964
            if (handler != null)
            {
Tebjan Halm's avatar
Tebjan Halm committed
965
                handler(sender, args);
Tebjan Halm's avatar
Tebjan Halm committed
966
967
968
            }
        }

969
        //out
970
        protected void RaiseMouseOut(object sender, MouseArg args)
971
        {
972
        	var handler = MouseOut;
973
974
            if (handler != null)
            {
Tebjan Halm's avatar
Tebjan Halm committed
975
                handler(sender, args);
976
977
            }
        }
joreg's avatar
joreg committed
978
        
979
980
        
        //scroll
981
        protected void OnMouseScroll(int scroll, bool ctrlKey, bool shiftKey, bool altKey, string sessionID)
joreg's avatar
joreg committed
982
        {
983
        	RaiseMouseScroll(this, new MouseScrollArg { Scroll = scroll, AltKey = altKey, ShiftKey = shiftKey, CtrlKey = ctrlKey, SessionID = sessionID});
joreg's avatar
joreg committed
984
985
        }
        
986
        protected void RaiseMouseScroll(object sender, MouseScrollArg e)
joreg's avatar
joreg committed
987
        {
988
        	var handler = MouseScroll;
joreg's avatar
joreg committed
989
990
            if (handler != null)
            {
991
                handler(sender, e);
joreg's avatar
joreg committed
992
993
            }
        }
994
        
Tebjan Halm's avatar
Tebjan Halm committed
995
996
        #endregion graphical EVENTS
    }
997
    
Tebjan Halm's avatar
Tebjan Halm committed
998
999
1000
    public class SVGArg : EventArgs
    {
    	public string SessionID;