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