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