ccbcb0d08b582293d05dd34d43b7631c0aff2923
[WebKit-https.git] / WebCore / ksvg2 / svg / SVGAnimationElement.cpp
1 /*
2     Copyright (C) 2004, 2005 Nikolas Zimmermann <wildfox@kde.org>
3                   2004, 2005 Rob Buis <buis@kde.org>
4
5     This file is part of the KDE project
6
7     This library is free software; you can redistribute it and/or
8     modify it under the terms of the GNU Library General Public
9     License as published by the Free Software Foundation; either
10     version 2 of the License, or (at your option) any later version.
11
12     This library is distributed in the hope that it will be useful,
13     but WITHOUT ANY WARRANTY; without even the implied warranty of
14     MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
15     Library General Public License for more details.
16
17     You should have received a copy of the GNU Library General Public License
18     along with this library; see the file COPYING.LIB.  If not, write to
19     the Free Software Foundation, Inc., 59 Temple Place - Suite 330,
20     Boston, MA 02111-1307, USA.
21 */
22
23 #include "config.h"
24 #ifdef SVG_SUPPORT
25 #include "SVGAnimationElement.h"
26
27 #include "Attr.h"
28 #include "CSSPropertyNames.h"
29 #include "Document.h"
30 #include "DOMImplementation.h"
31 #include "ksvgcssproperties.h"
32 #include "KSVGTimeScheduler.h"
33 #include "PlatformString.h"
34 #include "SVGDocumentExtensions.h"
35 #include "SVGHelper.h"
36 #include "SVGStyledElement.h"
37 #include "SVGSVGElement.h"
38 #include "SVGURIReference.h"
39 #include "XLinkNames.h"
40 #include <float.h>
41 #include <math.h>
42 #include <wtf/Vector.h>
43
44 using namespace std;
45
46 namespace WebCore {
47
48 SVGAnimationElement::SVGAnimationElement(const QualifiedName& tagName, Document* doc)
49     : SVGElement(tagName, doc)
50     , SVGTests()
51     , SVGExternalResourcesRequired()
52     , m_targetElement(0)
53     , m_connected(false)
54     , m_currentTime(0.0)
55     , m_simpleDuration(0.0)
56     , m_fill(FILL_REMOVE)
57     , m_restart(RESTART_ALWAYS)
58     , m_calcMode(CALCMODE_LINEAR)
59     , m_additive(ADDITIVE_REPLACE)
60     , m_accumulate(ACCUMULATE_NONE)
61     , m_attributeType(ATTRIBUTETYPE_AUTO)
62     , m_max(0.0)
63     , m_min(0.0)
64     , m_end(0.0)
65     , m_begin(0.0)
66     , m_repeations(0)
67     , m_repeatCount(0)
68 {
69
70 }
71
72 SVGAnimationElement::~SVGAnimationElement()
73 {
74 }
75
76 SVGElement *SVGAnimationElement::targetElement() const
77 {
78     if (!m_targetElement) {
79         if (!m_href.isEmpty()) {
80             Element *element = ownerDocument()->getElementById(SVGURIReference::getTarget(m_href));
81             m_targetElement = svg_dynamic_cast(element);
82         } else if (parentNode()) {
83             Node *target = parentNode();
84             while(target != 0) {
85                 if (target->nodeType() != ELEMENT_NODE)
86                     target = target->parentNode();
87                 else
88                     break;
89             }
90             m_targetElement = svg_dynamic_cast(target);
91         }
92     }
93                         
94     return m_targetElement;
95 }
96
97 double SVGAnimationElement::getEndTime() const
98 {
99     return m_end;
100 }
101
102 double SVGAnimationElement::getStartTime() const
103 {
104     return m_begin;
105 }
106
107 double SVGAnimationElement::getCurrentTime() const
108 {
109     return m_currentTime;
110 }
111
112 double SVGAnimationElement::getSimpleDuration(ExceptionCode&) const
113 {
114     return m_simpleDuration;
115 }
116
117 void SVGAnimationElement::parseMappedAttribute(MappedAttribute *attr)
118 {
119     const String& value = attr->value();
120     if (attr->name().matches(XLinkNames::hrefAttr))
121         m_href = value;
122     else if (attr->name() == SVGNames::attributeNameAttr)
123         m_attributeName = value;
124     else if (attr->name() == SVGNames::attributeTypeAttr)
125     {
126         if (value == "CSS")
127             m_attributeType = ATTRIBUTETYPE_CSS;
128         else if (value == "XML")
129             m_attributeType = ATTRIBUTETYPE_XML;
130         else if (value == "auto")
131             m_attributeType = ATTRIBUTETYPE_AUTO;
132     }
133     else if (attr->name() == SVGNames::beginAttr || attr->name() == SVGNames::endAttr)
134     {
135         // Create list
136         RefPtr<SVGStringList> temp = new SVGStringList();
137
138         // Feed data into list
139         SVGHelper::parseSeparatedList(temp.get(), value, ';');
140
141         ExceptionCode ec = 0;
142
143         // Parse data
144         for (unsigned int i = 0; i < temp->numberOfItems(); i++) {
145             String current = temp->getItem(i, ec);
146
147             if (current.startsWith("accessKey")) {
148                 // Register keyDownEventListener for the character
149                 String character = current.substring(current.length() - 2, 1);
150                 // FIXME: Not implemented! Supposed to register accessKey character
151             } else if (current.startsWith("wallclock")) {
152                 int firstBrace = current.find('(');
153                 int secondBrace = current.find(')');
154
155                 String wallclockValue = current.substring(firstBrace + 1, secondBrace - firstBrace - 2);
156                 // FIXME: Not implemented! Supposed to use wallClock value
157             } else if (current.contains('.')) {
158                 int dotPosition = current.find('.');
159
160                 String element = current.substring(0, dotPosition);
161                 String clockValue;
162                 if (current.contains("begin"))
163                     clockValue = current.substring(dotPosition + 6);
164                 else if (current.contains("end"))
165                     clockValue = current.substring(dotPosition + 4);
166                 else if (current.contains("repeat"))
167                     clockValue = current.substring(dotPosition + 7);
168                 else // DOM2 Event Reference
169                 {
170                     int plusMinusPosition = -1;
171
172                     if (current.contains('+'))
173                         plusMinusPosition = current.find('+');
174                     else if (current.contains('-'))
175                         plusMinusPosition = current.find('-');
176
177                     String event = current.substring(dotPosition + 1, plusMinusPosition - dotPosition - 1);
178                     clockValue = current.substring(dotPosition + event.length() + 1);
179                     // FIXME: supposed to use DOM Event
180                 }
181             } else {
182                 if (attr->name() == SVGNames::beginAttr) {
183                     m_begin = parseClockValue(current);
184                     if (!isIndefinite(m_begin))
185                         m_begin *= 1000.0;
186                     // FIXME: supposed to set begin time
187                 } else {
188                     m_end = parseClockValue(current);
189                     if (!isIndefinite(m_end))
190                         m_end *= 1000.0;
191                     // FIXME: supposed to set end time
192                 }
193             }
194         }
195     }
196     else if (attr->name() == SVGNames::durAttr)
197     {
198         m_simpleDuration = parseClockValue(value);
199         if (!isIndefinite(m_simpleDuration))
200             m_simpleDuration *= 1000.0;
201     }
202     else if (attr->name() == SVGNames::minAttr)
203     {
204         m_min = parseClockValue(value);
205         if (!isIndefinite(m_min))
206             m_min *= 1000.0;
207     }
208     else if (attr->name() == SVGNames::maxAttr)
209     {
210         m_max = parseClockValue(value);
211         if (!isIndefinite(m_max))
212             m_max *= 1000.0;
213     }
214     else if (attr->name() == SVGNames::restartAttr)
215     {
216         if (value == "whenNotActive")
217             m_restart = RESTART_WHENNOTACTIVE;
218         else if (value == "never")
219             m_restart = RESTART_NEVER;
220         else if (value == "always")
221             m_restart = RESTART_ALWAYS;
222     }
223     else if (attr->name() == SVGNames::repeatCountAttr)
224     {
225         if (value == "indefinite")
226             m_repeatCount = DBL_MAX;
227         else
228             m_repeatCount = value.toDouble();
229     }
230     else if (attr->name() == SVGNames::repeatDurAttr)
231         m_repeatDur = value;
232     else if (attr->name() == SVGNames::fillAttr)
233     {
234         if (value == "freeze")
235             m_fill = FILL_FREEZE;
236         else if (value == "remove")
237             m_fill = FILL_REMOVE;
238     }
239     else if (attr->name() == SVGNames::calcModeAttr)
240     {
241         if (value == "discrete")
242             m_calcMode = CALCMODE_DISCRETE;
243         else if (value == "linear")
244             m_calcMode = CALCMODE_LINEAR;
245         else if (value == "spline")
246             m_calcMode = CALCMODE_SPLINE;
247         else if (value == "paced")
248             m_calcMode = CALCMODE_PACED;
249     }
250     else if (attr->name() == SVGNames::valuesAttr)
251     {
252         m_values = new SVGStringList();
253         SVGHelper::parseSeparatedList(m_values.get(), value, ';');
254     }
255     else if (attr->name() == SVGNames::keyTimesAttr)
256     {
257         m_keyTimes = new SVGStringList();
258         SVGHelper::parseSeparatedList(m_keyTimes.get(), value, ';');
259     }
260     else if (attr->name() == SVGNames::keySplinesAttr)
261     {
262         m_keySplines = new SVGStringList();
263         SVGHelper::parseSeparatedList(m_keySplines.get(), value, ';');
264     }
265     else if (attr->name() == SVGNames::fromAttr)
266         m_from = value;
267     else if (attr->name() == SVGNames::toAttr)
268         m_to = value;
269     else if (attr->name() == SVGNames::byAttr)
270         m_by = value;
271     else if (attr->name() == SVGNames::additiveAttr)
272     {
273         if (value == "sum")
274             m_additive = ADDITIVE_SUM;
275         else if (value == "replace")
276             m_additive = ADDITIVE_REPLACE;
277     }
278     else if (attr->name() == SVGNames::accumulateAttr)
279     {
280         if (value == "sum")
281             m_accumulate = ACCUMULATE_SUM;
282         else if (value == "none")
283             m_accumulate = ACCUMULATE_NONE;
284     }
285     else
286     {
287         if (SVGTests::parseMappedAttribute(attr)) return;
288         if (SVGExternalResourcesRequired::parseMappedAttribute(attr)) return;
289         
290         SVGElement::parseMappedAttribute(attr);
291     }
292 }
293
294 double SVGAnimationElement::parseClockValue(const String& data) const
295 {
296     DeprecatedString parse = data.deprecatedString().stripWhiteSpace();
297     
298     if (parse == "indefinite") // Saves some time...
299         return DBL_MAX;
300
301     double result;
302
303     int doublePointOne = parse.find(':');
304     int doublePointTwo = parse.find(':', doublePointOne + 1);
305
306     if (doublePointOne != -1 && doublePointTwo != -1) // Spec: "Full clock values"
307     {
308         unsigned int hours = parse.mid(0, 2).toUInt();
309         unsigned int minutes = parse.mid(3, 2).toUInt();
310         unsigned int seconds = parse.mid(6, 2).toUInt();
311         unsigned int milliseconds = 0;
312
313         result = (3600 * hours) + (60 * minutes) + seconds;
314
315         if (parse.find('.') != -1)
316         {
317             DeprecatedString temp = parse.mid(9, 2);
318             milliseconds = temp.toUInt();
319             result += (milliseconds * (1 / pow(10.0, int(temp.length()))));
320         }
321     }
322     else if (doublePointOne != -1 && doublePointTwo == -1) // Spec: "Partial clock values"
323     {
324         unsigned int minutes = parse.mid(0, 2).toUInt();
325         unsigned int seconds = parse.mid(3, 2).toUInt();
326         unsigned int milliseconds = 0;
327
328         result = (60 * minutes) + seconds;
329
330         if (parse.find('.') != -1)
331         {
332             DeprecatedString temp = parse.mid(6, 2);
333             milliseconds = temp.toUInt();
334             result += (milliseconds * (1 / pow(10.0, int(temp.length()))));
335         }
336     }
337     else // Spec: "Timecount values"
338     {
339         int dotPosition = parse.find('.');
340
341         if (parse.endsWith("h"))
342         {
343             if (dotPosition == -1)
344                 result = parse.mid(0, parse.length() - 1).toUInt() * 3600;
345             else
346             {
347                 result = parse.mid(0, dotPosition).toUInt() * 3600;
348                 DeprecatedString temp = parse.mid(dotPosition + 1, parse.length() - dotPosition - 2);
349                 result += (3600.0 * temp.toUInt()) * (1 / pow(10.0, int(temp.length())));
350             }
351         }
352         else if (parse.endsWith("min"))
353         {
354             if (dotPosition == -1)
355                 result = parse.mid(0, parse.length() - 3).toUInt() * 60;
356             else
357             {
358                 result = parse.mid(0, dotPosition).toUInt() * 60;
359                 DeprecatedString temp = parse.mid(dotPosition + 1, parse.length() - dotPosition - 4);
360                 result += (60.0 * temp.toUInt()) * (1 / pow(10.0, int(temp.length())));
361             }
362         }
363         else if (parse.endsWith("ms"))
364         {
365             if (dotPosition == -1)
366                 result = parse.mid(0, parse.length() - 2).toUInt() / 1000.0;
367             else
368             {
369                 result = parse.mid(0, dotPosition).toUInt() / 1000.0;
370                 DeprecatedString temp = parse.mid(dotPosition + 1, parse.length() - dotPosition - 3);
371                 result += (temp.toUInt() / 1000.0) * (1 / pow(10.0, int(temp.length())));
372             }
373         }
374         else if (parse.endsWith("s"))
375         {
376             if (dotPosition == -1)
377                 result = parse.mid(0, parse.length() - 1).toUInt();
378             else
379             {
380                 result = parse.mid(0, dotPosition).toUInt();
381                 DeprecatedString temp = parse.mid(dotPosition + 1, parse.length() - dotPosition - 2);
382                 result += temp.toUInt() * (1 / pow(10.0, int(temp.length())));
383             }
384         }
385         else
386             result = parse.toDouble();
387     }
388
389     return result;
390 }
391
392 void SVGAnimationElement::closeRenderer()
393 {
394     ownerSVGElement()->timeScheduler()->addTimer(this, lround(getStartTime()));
395     SVGElement::closeRenderer();
396 }
397
398 String SVGAnimationElement::targetAttribute() const
399 {
400     if (!targetElement())
401         return String();
402     
403     SVGElement *target = targetElement();
404     SVGStyledElement *styled = NULL;
405     if (target && target->isStyled())
406         styled = static_cast<SVGStyledElement *>(target);
407     
408     String ret;
409
410     EAttributeType attributeType = static_cast<EAttributeType>(m_attributeType);
411     if (attributeType == ATTRIBUTETYPE_AUTO) {
412         attributeType = ATTRIBUTETYPE_XML;
413
414         // Spec: The implementation should match the attributeName to an attribute
415         // for the target element. The implementation must first search through the
416         // list of CSS properties for a matching property name, and if none is found,
417         // search the default XML namespace for the element.
418         if (styled && styled->style()) {
419             if (styled->style()->getPropertyCSSValue(m_attributeName))
420                 attributeType = ATTRIBUTETYPE_CSS;
421         }
422     }
423     
424     if (attributeType == ATTRIBUTETYPE_CSS) {
425         if (styled && styled->style())
426             ret = styled->style()->getPropertyValue(m_attributeName);
427     }
428
429     if (attributeType == ATTRIBUTETYPE_XML || ret.isEmpty())
430         ret = targetElement()->getAttribute(m_attributeName);
431
432     return ret;
433 }
434
435 void SVGAnimationElement::setTargetAttribute(const String& value)
436 {
437     SVGAnimationElement::setTargetAttribute(targetElement(), m_attributeName, value, static_cast<EAttributeType>(m_attributeType));
438 }
439
440 void SVGAnimationElement::setTargetAttribute(SVGElement* target, const String& name, const String& value, EAttributeType type)
441 {
442     if (!target || name.isNull() || value.isNull())
443         return;
444     
445     SVGStyledElement* styled = (target && target->isStyled()) ? static_cast<SVGStyledElement*>(target) : 0;
446
447     EAttributeType attributeType = type;
448     if (type == ATTRIBUTETYPE_AUTO) {
449         // Spec: The implementation should match the attributeName to an attribute
450         // for the target element. The implementation must first search through the
451         // list of CSS properties for a matching property name, and if none is found,
452         // search the default XML namespace for the element.
453         if (styled && styled->style() && styled->style()->getPropertyCSSValue(name))
454             attributeType = ATTRIBUTETYPE_CSS;
455         else
456             attributeType = ATTRIBUTETYPE_XML;
457     }
458     ExceptionCode ec = 0;
459     if (attributeType == ATTRIBUTETYPE_CSS && styled && styled->style())
460         styled->style()->setProperty(name, value, "", ec);
461     else if (attributeType == ATTRIBUTETYPE_XML)
462         target->setAttribute(name, value, ec);
463 }
464
465 String SVGAnimationElement::attributeName() const
466 {
467     return m_attributeName;
468 }
469
470 bool SVGAnimationElement::connected() const
471 {
472     return m_connected;
473 }
474
475 bool SVGAnimationElement::isFrozen() const
476 {
477     return (m_fill == FILL_FREEZE);
478 }
479
480 bool SVGAnimationElement::isAdditive() const
481 {
482     return (m_additive == ADDITIVE_SUM) ||
483            (detectAnimationMode() == BY_ANIMATION);
484 }
485
486 bool SVGAnimationElement::isAccumulated() const
487 {
488     return (m_accumulate == ACCUMULATE_SUM) &&
489            (detectAnimationMode() != TO_ANIMATION);
490 }
491
492 EAnimationMode SVGAnimationElement::detectAnimationMode() const
493 {
494     if ((!m_from.isEmpty() && !m_to.isEmpty()) || (!m_to.isEmpty())) // to/from-to animation
495     {
496         if (!m_from.isEmpty()) // from-to animation
497             return FROM_TO_ANIMATION;
498         else
499             return TO_ANIMATION;
500     }
501     else if ((m_from.isEmpty() && m_to.isEmpty() && !m_by.isEmpty()) ||
502             (!m_from.isEmpty() && !m_by.isEmpty())) // by/from-by animation
503     {
504         if (!m_from.isEmpty()) // from-by animation
505             return FROM_BY_ANIMATION;
506         else
507             return BY_ANIMATION;
508     }
509     else if (m_values)
510         return VALUES_ANIMATION;
511
512     return NO_ANIMATION;
513 }
514
515 int SVGAnimationElement::calculateCurrentValueItem(double timePercentage)
516 {
517     if (!m_values)
518         return -1;
519     
520     unsigned long items = m_values->numberOfItems();
521
522     // Calculate the relative time percentages for each 'fade'.
523     Vector<double> startTimes(items);
524     startTimes[0] = 0.0;
525     for (unsigned i = 1; i < items; ++i)
526         startTimes[i] = (((2.0 * i)) / (items - 1)) / 2.0;
527
528     int itemByPercentage = -1;
529     for (unsigned i = 0; i < items - 1; ++i) {
530         if (timePercentage >= startTimes[i] && timePercentage <= startTimes[i + 1]) {
531             itemByPercentage = i;
532             break;
533         }
534     }
535
536     return itemByPercentage;
537 }
538
539 double SVGAnimationElement::calculateRelativeTimePercentage(double timePercentage, int currentItem)
540 {
541     if (currentItem == -1 || !m_values)
542         return 0.0;
543
544     unsigned long items = m_values->numberOfItems();
545
546     // Calculate the relative time percentages for each 'fade'.
547     Vector<double> startTimes(items);
548     startTimes[0] = 0.0;
549     for (unsigned i = 1; i < items; ++i)
550         startTimes[i] = (((2.0 * i)) / (items - 1)) / 2.0;
551
552     double beginTimePercentage = startTimes[currentItem];
553     double endTimePercentage = startTimes[currentItem + 1];
554
555     if ((endTimePercentage - beginTimePercentage) == 0.0)
556         return 0.0;
557
558     return ((timePercentage - beginTimePercentage) /
559             (endTimePercentage - beginTimePercentage));
560 }
561
562 double SVGAnimationElement::repeations() const
563 {
564     return m_repeations;
565 }
566
567 bool SVGAnimationElement::isIndefinite(double value) const
568 {
569     return (value == DBL_MAX);
570 }
571
572 }
573
574 // vim:ts=4:noet
575 #endif // SVG_SUPPORT
576