1/*
2 * Copyright (C) 2004, 2005, 2006, 2007, 2008 Nikolas Zimmermann <zimmermann@kde.org>
3 * Copyright (C) 2004, 2005, 2006, 2007 Rob Buis <buis@kde.org>
4 * Copyright (C) Research In Motion Limited 2009-2010. All rights reserved.
5 *
6 * This library is free software; you can redistribute it and/or
7 * modify it under the terms of the GNU Library General Public
8 * License as published by the Free Software Foundation; either
9 * version 2 of the License, or (at your option) any later version.
10 *
11 * This library is distributed in the hope that it will be useful,
12 * but WITHOUT ANY WARRANTY; without even the implied warranty of
13 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
14 * Library General Public License for more details.
15 *
16 * You should have received a copy of the GNU Library General Public License
17 * along with this library; see the file COPYING.LIB.  If not, write to
18 * the Free Software Foundation, Inc., 51 Franklin Street, Fifth Floor,
19 * Boston, MA 02110-1301, USA.
20 */
21
22#include "config.h"
23
24#if ENABLE(SVG)
25#include "SVGMarkerElement.h"
26
27#include "Attribute.h"
28#include "RenderSVGResourceMarker.h"
29#include "SVGFitToViewBox.h"
30#include "SVGNames.h"
31#include "SVGSVGElement.h"
32
33namespace WebCore {
34
35// Animated property definitions
36DEFINE_ANIMATED_LENGTH(SVGMarkerElement, SVGNames::refXAttr, RefX, refX)
37DEFINE_ANIMATED_LENGTH(SVGMarkerElement, SVGNames::refYAttr, RefY, refY)
38DEFINE_ANIMATED_LENGTH(SVGMarkerElement, SVGNames::markerWidthAttr, MarkerWidth, markerWidth)
39DEFINE_ANIMATED_LENGTH(SVGMarkerElement, SVGNames::markerHeightAttr, MarkerHeight, markerHeight)
40DEFINE_ANIMATED_ENUMERATION(SVGMarkerElement, SVGNames::markerUnitsAttr, MarkerUnits, markerUnits)
41DEFINE_ANIMATED_ENUMERATION_MULTIPLE_WRAPPERS(SVGMarkerElement, SVGNames::orientAttr, orientTypeIdentifier(), OrientType, orientType)
42DEFINE_ANIMATED_ANGLE_MULTIPLE_WRAPPERS(SVGMarkerElement, SVGNames::orientAttr, orientAngleIdentifier(), OrientAngle, orientAngle)
43DEFINE_ANIMATED_BOOLEAN(SVGMarkerElement, SVGNames::externalResourcesRequiredAttr, ExternalResourcesRequired, externalResourcesRequired)
44DEFINE_ANIMATED_RECT(SVGMarkerElement, SVGNames::viewBoxAttr, ViewBox, viewBox)
45DEFINE_ANIMATED_PRESERVEASPECTRATIO(SVGMarkerElement, SVGNames::preserveAspectRatioAttr, PreserveAspectRatio, preserveAspectRatio)
46
47inline SVGMarkerElement::SVGMarkerElement(const QualifiedName& tagName, Document* document)
48    : SVGStyledElement(tagName, document)
49    , m_refX(LengthModeWidth)
50    , m_refY(LengthModeHeight)
51    , m_markerWidth(LengthModeWidth, "3")
52    , m_markerHeight(LengthModeHeight, "3")
53    , m_markerUnits(SVG_MARKERUNITS_STROKEWIDTH)
54    , m_orientType(SVG_MARKER_ORIENT_ANGLE)
55{
56    // Spec: If the markerWidth/markerHeight attribute is not specified, the effect is as if a value of "3" were specified.
57}
58
59PassRefPtr<SVGMarkerElement> SVGMarkerElement::create(const QualifiedName& tagName, Document* document)
60{
61    return adoptRef(new SVGMarkerElement(tagName, document));
62}
63
64const AtomicString& SVGMarkerElement::orientTypeIdentifier()
65{
66    DEFINE_STATIC_LOCAL(AtomicString, s_identifier, ("SVGOrientType"));
67    return s_identifier;
68}
69
70const AtomicString& SVGMarkerElement::orientAngleIdentifier()
71{
72    DEFINE_STATIC_LOCAL(AtomicString, s_identifier, ("SVGOrientAngle"));
73    return s_identifier;
74}
75
76AffineTransform SVGMarkerElement::viewBoxToViewTransform(float viewWidth, float viewHeight) const
77{
78    return SVGFitToViewBox::viewBoxToViewTransform(viewBox(), preserveAspectRatio(), viewWidth, viewHeight);
79}
80
81void SVGMarkerElement::parseMappedAttribute(Attribute* attr)
82{
83    if (attr->name() == SVGNames::markerUnitsAttr) {
84        if (attr->value() == "userSpaceOnUse")
85            setMarkerUnitsBaseValue(SVG_MARKERUNITS_USERSPACEONUSE);
86        else if (attr->value() == "strokeWidth")
87            setMarkerUnitsBaseValue(SVG_MARKERUNITS_STROKEWIDTH);
88    } else if (attr->name() == SVGNames::refXAttr)
89        setRefXBaseValue(SVGLength(LengthModeWidth, attr->value()));
90    else if (attr->name() == SVGNames::refYAttr)
91        setRefYBaseValue(SVGLength(LengthModeHeight, attr->value()));
92    else if (attr->name() == SVGNames::markerWidthAttr)
93        setMarkerWidthBaseValue(SVGLength(LengthModeWidth, attr->value()));
94    else if (attr->name() == SVGNames::markerHeightAttr)
95        setMarkerHeightBaseValue(SVGLength(LengthModeHeight, attr->value()));
96    else if (attr->name() == SVGNames::orientAttr) {
97        SVGAngle angle;
98
99        if (attr->value() == "auto")
100            setOrientTypeBaseValue(SVG_MARKER_ORIENT_AUTO);
101        else {
102            ExceptionCode ec = 0;
103            angle.setValueAsString(attr->value(), ec);
104            setOrientTypeBaseValue(SVG_MARKER_ORIENT_ANGLE);
105        }
106
107        setOrientAngleBaseValue(angle);
108    } else {
109        if (SVGLangSpace::parseMappedAttribute(attr))
110            return;
111        if (SVGExternalResourcesRequired::parseMappedAttribute(attr))
112            return;
113        if (SVGFitToViewBox::parseMappedAttribute(document(), attr))
114            return;
115
116        SVGStyledElement::parseMappedAttribute(attr);
117    }
118}
119
120void SVGMarkerElement::svgAttributeChanged(const QualifiedName& attrName)
121{
122    SVGStyledElement::svgAttributeChanged(attrName);
123
124    bool invalidateClients = false;
125    if (attrName == SVGNames::refXAttr
126        || attrName == SVGNames::refYAttr
127        || attrName == SVGNames::markerWidthAttr
128        || attrName == SVGNames::markerHeightAttr) {
129        invalidateClients = true;
130        updateRelativeLengthsInformation();
131    }
132
133    RenderObject* object = renderer();
134    if (!object)
135        return;
136
137    if (invalidateClients
138        || attrName == SVGNames::markerUnitsAttr
139        || attrName == SVGNames::orientAttr
140        || SVGLangSpace::isKnownAttribute(attrName)
141        || SVGExternalResourcesRequired::isKnownAttribute(attrName)
142        || SVGFitToViewBox::isKnownAttribute(attrName)
143        || SVGStyledElement::isKnownAttribute(attrName))
144        object->setNeedsLayout(true);
145}
146
147void SVGMarkerElement::synchronizeProperty(const QualifiedName& attrName)
148{
149    SVGStyledElement::synchronizeProperty(attrName);
150
151    if (attrName == anyQName()) {
152        synchronizeMarkerUnits();
153        synchronizeRefX();
154        synchronizeRefY();
155        synchronizeMarkerWidth();
156        synchronizeMarkerHeight();
157        synchronizeOrientAngle();
158        synchronizeOrientType();
159        synchronizeExternalResourcesRequired();
160        synchronizeViewBox();
161        synchronizePreserveAspectRatio();
162        return;
163    }
164
165    if (attrName == SVGNames::markerUnitsAttr)
166        synchronizeMarkerUnits();
167    else if (attrName == SVGNames::refXAttr)
168        synchronizeRefX();
169    else if (attrName == SVGNames::refYAttr)
170        synchronizeRefY();
171    else if (attrName == SVGNames::markerWidthAttr)
172        synchronizeMarkerWidth();
173    else if (attrName == SVGNames::markerHeightAttr)
174        synchronizeMarkerHeight();
175    else if (attrName == SVGNames::orientAttr) {
176        synchronizeOrientAngle();
177        synchronizeOrientType();
178    } else if (SVGExternalResourcesRequired::isKnownAttribute(attrName))
179        synchronizeExternalResourcesRequired();
180    else if (SVGFitToViewBox::isKnownAttribute(attrName)) {
181        synchronizeViewBox();
182        synchronizePreserveAspectRatio();
183    }
184}
185
186AttributeToPropertyTypeMap& SVGMarkerElement::attributeToPropertyTypeMap()
187{
188    DEFINE_STATIC_LOCAL(AttributeToPropertyTypeMap, s_attributeToPropertyTypeMap, ());
189    return s_attributeToPropertyTypeMap;
190}
191
192void SVGMarkerElement::fillAttributeToPropertyTypeMap()
193{
194    AttributeToPropertyTypeMap& attributeToPropertyTypeMap = this->attributeToPropertyTypeMap();
195
196    SVGStyledElement::fillPassedAttributeToPropertyTypeMap(attributeToPropertyTypeMap);
197    attributeToPropertyTypeMap.set(SVGNames::refXAttr, AnimatedLength);
198    attributeToPropertyTypeMap.set(SVGNames::refYAttr, AnimatedLength);
199    attributeToPropertyTypeMap.set(SVGNames::markerWidthAttr, AnimatedLength);
200    attributeToPropertyTypeMap.set(SVGNames::markerHeightAttr, AnimatedLength);
201    attributeToPropertyTypeMap.set(SVGNames::markerUnitsAttr, AnimatedEnumeration);
202    attributeToPropertyTypeMap.set(SVGNames::orientAttr, AnimatedAngle);
203    attributeToPropertyTypeMap.set(SVGNames::viewBoxAttr, AnimatedRect);
204}
205
206void SVGMarkerElement::childrenChanged(bool changedByParser, Node* beforeChange, Node* afterChange, int childCountDelta)
207{
208    SVGStyledElement::childrenChanged(changedByParser, beforeChange, afterChange, childCountDelta);
209
210    if (changedByParser)
211        return;
212
213    if (RenderObject* object = renderer())
214        object->setNeedsLayout(true);
215}
216
217void SVGMarkerElement::setOrientToAuto()
218{
219    setOrientTypeBaseValue(SVG_MARKER_ORIENT_AUTO);
220    setOrientAngleBaseValue(SVGAngle());
221
222    if (RenderObject* object = renderer())
223        object->setNeedsLayout(true);
224}
225
226void SVGMarkerElement::setOrientToAngle(const SVGAngle& angle)
227{
228    setOrientTypeBaseValue(SVG_MARKER_ORIENT_ANGLE);
229    setOrientAngleBaseValue(angle);
230
231    if (RenderObject* object = renderer())
232        object->setNeedsLayout(true);
233}
234
235RenderObject* SVGMarkerElement::createRenderer(RenderArena* arena, RenderStyle*)
236{
237    return new (arena) RenderSVGResourceMarker(this);
238}
239
240bool SVGMarkerElement::selfHasRelativeLengths() const
241{
242    return refX().isRelative()
243        || refY().isRelative()
244        || markerWidth().isRelative()
245        || markerHeight().isRelative();
246}
247
248}
249
250#endif
251