• Skip to content
  • Skip to link menu
KDE 4.2 API Reference
  • KDE API Reference
  • kdelibs
  • Sitemap
  • Contact Us
 

KHTML

SVGSMILElement.cpp

Go to the documentation of this file.
00001 /*
00002  * Copyright (C) 2008 Apple Inc. All Rights Reserved.
00003  *
00004  * Redistribution and use in source and binary forms, with or without
00005  * modification, are permitted provided that the following conditions
00006  * are met:
00007  * 1. Redistributions of source code must retain the above copyright
00008  *    notice, this list of conditions and the following disclaimer.
00009  * 2. Redistributions in binary form must reproduce the above copyright
00010  *    notice, this list of conditions and the following disclaimer in the
00011  *    documentation and/or other materials provided with the distribution.
00012  *
00013  * THIS SOFTWARE IS PROVIDED BY APPLE INC. ``AS IS'' AND ANY
00014  * EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
00015  * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR
00016  * PURPOSE ARE DISCLAIMED.  IN NO EVENT SHALL APPLE INC. OR
00017  * CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL,
00018  * EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO,
00019  * PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR
00020  * PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY
00021  * OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
00022  * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
00023  * OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. 
00024  */
00025 
00026 #include "config.h"
00027 #if ENABLE(SVG_ANIMATION)
00028 #include "SVGSMILElement.h"
00029 
00030 #include "CSSPropertyNames.h"
00031 #include "Document.h"
00032 #include "Event.h"
00033 #include "EventListener.h"
00034 #include "FloatConversion.h"
00035 #include "FrameView.h"
00036 #include "HTMLNames.h"
00037 #include "SVGNames.h"
00038 #include "SVGParserUtilities.h"
00039 #include "SVGSVGElement.h"
00040 #include "SVGURIReference.h"
00041 #include "SMILTimeContainer.h"
00042 #include "XLinkNames.h"
00043 #include <math.h>
00044 #include <wtf/MathExtras.h>
00045 #include <wtf/Vector.h>
00046 
00047 using namespace std;
00048 
00049 namespace WebCore {
00050     
00051 // This is used for duration type time values that can't be negative.
00052 static const double invalidCachedTime = -1.;
00053     
00054 class ConditionEventListener : public EventListener {
00055 public:
00056     ConditionEventListener(SVGSMILElement* animation, Element* eventBase, SVGSMILElement::Condition* condition) 
00057         : m_animation(animation)
00058         , m_condition(condition) 
00059         , m_eventBase(eventBase)
00060     {
00061         m_eventBase->addEventListener(m_condition->m_name, this, false);
00062     }
00063 
00064     void unregister()
00065     {
00066         // If this has only one ref then the event base is dead already and we don't need to remove ourself.
00067         if (!hasOneRef())
00068             m_eventBase->removeEventListener(m_condition->m_name, this, false);
00069     }
00070 
00071     virtual void handleEvent(Event* event, bool isWindowEvent) 
00072     {
00073         m_animation->handleConditionEvent(event, m_condition);
00074     }
00075 private:
00076     SVGSMILElement* m_animation;
00077     SVGSMILElement::Condition* m_condition;
00078     Element* m_eventBase;
00079 };
00080     
00081 SVGSMILElement::Condition::Condition(Type type, BeginOrEnd beginOrEnd, const String& baseID, const String& name, SMILTime offset, int repeats)
00082     : m_type(type)
00083     , m_beginOrEnd(beginOrEnd)
00084     , m_baseID(baseID)
00085     , m_name(name)
00086     , m_offset(offset)
00087     , m_repeats(repeats) 
00088 {
00089 }
00090     
00091 SVGSMILElement::SVGSMILElement(const QualifiedName& tagName, Document* doc)
00092     : SVGElement(tagName, doc)
00093     , m_conditionsConnected(false)
00094     , m_hasEndEventConditions(false)
00095     , m_intervalBegin(SMILTime::unresolved())
00096     , m_intervalEnd(SMILTime::unresolved())
00097     , m_previousIntervalBegin(SMILTime::unresolved())
00098     , m_isWaitingForFirstInterval(true)
00099     , m_activeState(Inactive)
00100     , m_lastPercent(0)
00101     , m_lastRepeat(0)
00102     , m_nextProgressTime(0)
00103     , m_documentOrderIndex(0)
00104     , m_cachedDur(invalidCachedTime)
00105     , m_cachedRepeatDur(invalidCachedTime)
00106     , m_cachedRepeatCount(invalidCachedTime)
00107     , m_cachedMin(invalidCachedTime)
00108     , m_cachedMax(invalidCachedTime)
00109 {
00110 }
00111 
00112 SVGSMILElement::~SVGSMILElement()
00113 {
00114     disconnectConditions();
00115     if (m_timeContainer)
00116         m_timeContainer->unschedule(this);
00117 }
00118     
00119 void SVGSMILElement::insertedIntoDocument()
00120 {
00121     SVGElement::insertedIntoDocument();
00122 #ifndef NDEBUG
00123     // Verify we are not in <use> instance tree.
00124     for (Node* n = this; n; n = n->parent())
00125         ASSERT(!n->isShadowNode());
00126 #endif
00127     SVGSVGElement* owner = ownerSVGElement();
00128     if (!owner)
00129         return;
00130     m_timeContainer = owner->timeContainer();
00131     ASSERT(m_timeContainer);
00132     m_timeContainer->setDocumentOrderIndexesDirty();
00133     reschedule();
00134 }
00135 
00136 void SVGSMILElement::removedFromDocument()
00137 {
00138     if (m_timeContainer) {
00139         m_timeContainer->unschedule(this);
00140         m_timeContainer = 0;
00141     }
00142     // Calling disconnectConditions() may kill us if there are syncbase conditions.
00143     // OK, but we don't want to die inside the call.
00144     RefPtr<SVGSMILElement> keepAlive(this);
00145     disconnectConditions();
00146     SVGElement::removedFromDocument();
00147 }
00148    
00149 void SVGSMILElement::finishParsingChildren()
00150 {
00151     SVGElement::finishParsingChildren();
00152 
00153     // "If no attribute is present, the default begin value (an offset-value of 0) must be evaluated."
00154     if (!hasAttribute(SVGNames::beginAttr))
00155         m_beginTimes.append(0);
00156 
00157     if (m_isWaitingForFirstInterval) {
00158         resolveFirstInterval();
00159         reschedule();
00160     }
00161 }
00162 
00163 SMILTime SVGSMILElement::parseOffsetValue(const String& data)
00164 {
00165     bool ok;
00166     double result = 0;
00167     String parse = data.stripWhiteSpace();
00168     if (parse.endsWith("h"))
00169         result = parse.left(parse.length() - 1).toDouble(&ok) * 60 * 60;
00170     else if (parse.endsWith("min"))
00171         result = parse.left(parse.length() - 3).toDouble(&ok) * 60;
00172     else if (parse.endsWith("ms"))
00173         result = parse.left(parse.length() - 2).toDouble(&ok) / 1000;
00174     else if (parse.endsWith("s"))
00175         result = parse.left(parse.length() - 1).toDouble(&ok);
00176     else
00177         result = parse.toDouble(&ok);
00178     if (!ok)
00179         return SMILTime::unresolved();
00180     return result;
00181 }
00182     
00183 SMILTime SVGSMILElement::parseClockValue(const String& data)
00184 {
00185     if (data.isNull())
00186         return SMILTime::unresolved();
00187     
00188     String parse = data.stripWhiteSpace();
00189 
00190     static const AtomicString indefiniteValue("indefinite");
00191     if (parse == indefiniteValue)
00192         return SMILTime::indefinite();
00193 
00194     double result = 0;
00195     bool ok;
00196     int doublePointOne = parse.find(':');
00197     int doublePointTwo = parse.find(':', doublePointOne + 1);
00198     if (doublePointOne == 2 && doublePointTwo == 5 && parse.length() >= 8) { 
00199         result += parse.substring(0, 2).toUIntStrict(&ok) * 60 * 60;
00200         if (!ok)
00201             return SMILTime::unresolved();
00202         result += parse.substring(3, 2).toUIntStrict(&ok) * 60;
00203         if (!ok)
00204             return SMILTime::unresolved();
00205         result += parse.substring(6).toDouble(&ok);
00206     } else if (doublePointOne == 2 && doublePointTwo == -1 && parse.length() >= 5) { 
00207         result += parse.substring(0, 2).toUIntStrict(&ok) * 60;
00208         if (!ok)
00209             return SMILTime::unresolved();
00210         result += parse.substring(3).toDouble(&ok);
00211     } else
00212         return parseOffsetValue(parse);
00213 
00214     if (!ok)
00215         return SMILTime::unresolved();
00216     return result;
00217 }
00218     
00219 static void sortTimeList(Vector<SMILTime>& timeList)
00220 {
00221     std::sort(timeList.begin(), timeList.end());
00222 }
00223     
00224 bool SVGSMILElement::parseCondition(const String& value, BeginOrEnd beginOrEnd)
00225 {
00226     String parseString = value.stripWhiteSpace();
00227     
00228     double sign = 1.;
00229     bool ok;
00230     int pos = parseString.find('+');
00231     if (pos == -1) {
00232         pos = parseString.find('-');
00233         if (pos != -1)
00234             sign = -1.;
00235     }
00236     String conditionString;
00237     SMILTime offset = 0;
00238     if (pos == -1)
00239         conditionString = parseString;
00240     else {
00241         conditionString = parseString.left(pos).stripWhiteSpace();
00242         String offsetString = parseString.substring(pos + 1).stripWhiteSpace();
00243         offset = parseOffsetValue(offsetString);
00244         if (offset.isUnresolved())
00245             return false;
00246         offset = offset * sign;
00247     }
00248     if (conditionString.isEmpty())
00249         return false;
00250     pos = conditionString.find('.');
00251     
00252     String baseID;
00253     String nameString;
00254     if (pos == -1)
00255         nameString = conditionString;
00256     else {
00257         baseID = conditionString.left(pos);
00258         nameString = conditionString.substring(pos + 1);
00259     }
00260     if (nameString.isEmpty())
00261         return false;
00262 
00263     Condition::Type type;
00264     int repeats = -1;
00265     if (nameString.startsWith("repeat(") && nameString.endsWith(")")) {
00266         // FIXME: For repeat events we just need to add the data carrying TimeEvent class and 
00267         // fire the events at appropriate times.
00268         repeats = nameString.substring(7, nameString.length() - 8).toUIntStrict(&ok);
00269         if (!ok)
00270             return false;
00271         nameString = "repeat";
00272         type = Condition::EventBase;
00273     } else if (nameString == "begin" || nameString == "end") {
00274         if (baseID.isEmpty())
00275             return false;
00276         type = Condition::Syncbase;
00277     } else if (nameString.startsWith("accesskey(")) {
00278         // FIXME: accesskey() support.
00279         type = Condition::AccessKey;
00280     } else
00281         type = Condition::EventBase;
00282     
00283     m_conditions.append(Condition(type, beginOrEnd, baseID, nameString, offset, repeats));
00284 
00285     if (type == Condition::EventBase && beginOrEnd == End)
00286         m_hasEndEventConditions = true;
00287 
00288     return true;
00289 }
00290 
00291 bool SVGSMILElement::isSMILElement(Node* node)
00292 {
00293     if (!node)
00294         return false;
00295     return node->hasTagName(SVGNames::setTag) || node->hasTagName(SVGNames::animateTag) || node->hasTagName(SVGNames::animateMotionTag)
00296             || node->hasTagName(SVGNames::animateTransformTag) || node->hasTagName(SVGNames::animateColorTag);
00297 }
00298     
00299 void SVGSMILElement::parseBeginOrEnd(const String& parseString, BeginOrEnd beginOrEnd)
00300 {
00301     Vector<SMILTime>& timeList = beginOrEnd == Begin ? m_beginTimes : m_endTimes;
00302     if (beginOrEnd == End)
00303         m_hasEndEventConditions = false;
00304     HashSet<double> existing;
00305     for (unsigned n = 0; n < timeList.size(); ++n)
00306         existing.add(timeList[n].value());
00307     Vector<String> splitString;
00308     parseString.split(';', splitString);
00309     for (unsigned n = 0; n < splitString.size(); ++n) {
00310         SMILTime value = parseClockValue(splitString[n]);
00311         if (value.isUnresolved())
00312             parseCondition(splitString[n], beginOrEnd);
00313         else if (!existing.contains(value.value()))
00314             timeList.append(value);
00315     }
00316     sortTimeList(timeList);
00317 }
00318 
00319 void SVGSMILElement::parseMappedAttribute(MappedAttribute* attr)
00320 {
00321     if (attr->name() == SVGNames::beginAttr) {
00322         if (!m_conditions.isEmpty()) {
00323             disconnectConditions();
00324             m_conditions.clear();
00325             parseBeginOrEnd(getAttribute(SVGNames::endAttr), End);
00326         }
00327         parseBeginOrEnd(attr->value().string(), Begin);
00328         if (inDocument())
00329             connectConditions();
00330     } else if (attr->name() == SVGNames::endAttr) {
00331         if (!m_conditions.isEmpty()) {
00332             disconnectConditions();
00333             m_conditions.clear();
00334             parseBeginOrEnd(getAttribute(SVGNames::beginAttr), Begin);
00335         }
00336         parseBeginOrEnd(attr->value().string(), End);
00337         if (inDocument())
00338             connectConditions();
00339     } else
00340         SVGElement::parseMappedAttribute(attr);
00341 }
00342 
00343 void SVGSMILElement::attributeChanged(Attribute* attr, bool preserveDecls)
00344 {
00345     SVGElement::attributeChanged(attr, preserveDecls);
00346     
00347     const QualifiedName& attrName = attr->name();
00348     if (attrName == SVGNames::durAttr)
00349         m_cachedDur = invalidCachedTime;
00350     else if (attrName == SVGNames::repeatDurAttr)
00351         m_cachedRepeatDur = invalidCachedTime;
00352     else if (attrName == SVGNames::repeatCountAttr)
00353         m_cachedRepeatCount = invalidCachedTime;
00354     else if (attrName == SVGNames::minAttr)
00355         m_cachedMin = invalidCachedTime;
00356     else if (attrName == SVGNames::maxAttr)
00357         m_cachedMax = invalidCachedTime;
00358     
00359     if (inDocument()) {
00360         if (attrName == SVGNames::beginAttr)
00361             beginListChanged();
00362         else if (attrName == SVGNames::endAttr)
00363             endListChanged();
00364     }
00365 }
00366 
00367 void SVGSMILElement::connectConditions()
00368 {
00369     if (m_conditionsConnected)
00370         disconnectConditions();
00371     m_conditionsConnected = true;
00372     for (unsigned n = 0; n < m_conditions.size(); ++n) {
00373         Condition& condition = m_conditions[n];
00374         if (condition.m_type == Condition::EventBase) {
00375             ASSERT(!condition.m_syncbase);
00376             Element* eventBase = condition.m_baseID.isEmpty() ? targetElement() : document()->getElementById(condition.m_baseID);
00377             if (!eventBase)
00378                 continue;
00379             ASSERT(!condition.m_eventListener);
00380             condition.m_eventListener = new ConditionEventListener(this, eventBase, &condition);
00381         } else if (condition.m_type == Condition::Syncbase) {
00382             ASSERT(!condition.m_baseID.isEmpty());
00383             condition.m_syncbase = document()->getElementById(condition.m_baseID);
00384             if (!isSMILElement(condition.m_syncbase.get())) {
00385                 condition.m_syncbase = 0;
00386                 continue;
00387             }
00388             SVGSMILElement* syncbase = static_cast<SVGSMILElement*>(condition.m_syncbase.get());
00389             syncbase->addTimeDependent(this);
00390         }
00391     }
00392 }
00393 
00394 void SVGSMILElement::disconnectConditions()
00395 {
00396     if (!m_conditionsConnected)
00397         return;
00398     m_conditionsConnected = false;
00399     for (unsigned n = 0; n < m_conditions.size(); ++n) {
00400         Condition& condition = m_conditions[n];
00401         if (condition.m_type == Condition::EventBase) {
00402             ASSERT(!condition.m_syncbase);
00403             if (condition.m_eventListener) {
00404                 condition.m_eventListener->unregister();
00405                 condition.m_eventListener = 0;
00406             }
00407         } else if (condition.m_type == Condition::Syncbase) {
00408             if (condition.m_syncbase) {
00409                 ASSERT(isSMILElement(condition.m_syncbase.get()));
00410                 static_cast<SVGSMILElement*>(condition.m_syncbase.get())->removeTimeDependent(this);
00411             }
00412         }
00413         condition.m_syncbase = 0;
00414     }
00415 }
00416 
00417 void SVGSMILElement::reschedule()
00418 {
00419     if (m_timeContainer)
00420         m_timeContainer->schedule(this);
00421 }
00422 
00423 SVGElement* SVGSMILElement::targetElement() const
00424 {
00425     String href = xlinkHref();
00426     Node* target = href.isEmpty() ? parentNode() : document()->getElementById(SVGURIReference::getTarget(href));
00427     if (target && target->isSVGElement())
00428         return static_cast<SVGElement*>(target);
00429     return 0;
00430 }
00431     
00432 String SVGSMILElement::attributeName() const
00433 {    
00434     return getAttribute(SVGNames::attributeNameAttr).string().stripWhiteSpace();
00435 }
00436     
00437 SMILTime SVGSMILElement::elapsed() const 
00438 {
00439     return m_timeContainer ? m_timeContainer->elapsed() : 0;
00440 } 
00441     
00442 bool SVGSMILElement::isInactive() const
00443 {
00444      return m_activeState == Inactive;
00445 }
00446 
00447 bool SVGSMILElement::isFrozen() const
00448 {
00449     return m_activeState == Frozen;
00450 }
00451     
00452 SVGSMILElement::Restart SVGSMILElement::restart() const
00453 {    
00454     static const AtomicString never("never");
00455     static const AtomicString whenNotActive("whenNotActive");
00456     const AtomicString& value = getAttribute(SVGNames::restartAttr);
00457     if (value == never)
00458         return RestartNever;
00459     if (value == whenNotActive)
00460         return RestartWhenNotActive;
00461     return RestartAlways;
00462 }
00463     
00464 SVGSMILElement::FillMode SVGSMILElement::fill() const
00465 {   
00466     static const AtomicString freeze("freeze");
00467     const AtomicString& value = getAttribute(SVGNames::fillAttr);
00468     return value == freeze ? FillFreeze : FillRemove;
00469 }
00470     
00471 String SVGSMILElement::xlinkHref() const
00472 {    
00473     return getAttribute(XLinkNames::hrefAttr);
00474 }
00475     
00476 SMILTime SVGSMILElement::dur() const
00477 {   
00478     if (m_cachedDur != invalidCachedTime)
00479         return m_cachedDur;
00480     const AtomicString& value = getAttribute(SVGNames::durAttr);
00481     SMILTime clockValue = parseClockValue(value);
00482     return m_cachedDur = clockValue <= 0 ? SMILTime::unresolved() : clockValue;
00483 }
00484 
00485 SMILTime SVGSMILElement::repeatDur() const
00486 {    
00487     if (m_cachedRepeatDur != invalidCachedTime)
00488         return m_cachedRepeatDur;
00489     const AtomicString& value = getAttribute(SVGNames::repeatDurAttr);
00490     SMILTime clockValue = parseClockValue(value);
00491     return m_cachedRepeatDur = clockValue < 0 ? SMILTime::unresolved() : clockValue;
00492 }
00493     
00494 // So a count is not really a time but let just all pretend we did not notice.
00495 SMILTime SVGSMILElement::repeatCount() const
00496 {    
00497     if (m_cachedRepeatCount != invalidCachedTime)
00498         return m_cachedRepeatCount;
00499     const AtomicString& value = getAttribute(SVGNames::repeatCountAttr);
00500     if (value.isNull())
00501         return SMILTime::unresolved();
00502 
00503     static const AtomicString indefiniteValue("indefinite");
00504     if (value == indefiniteValue)
00505         return SMILTime::indefinite();
00506     bool ok;
00507     double result = value.string().toDouble(&ok);
00508     return m_cachedRepeatCount = ok && result > 0 ? result : SMILTime::unresolved();
00509 }
00510 
00511 SMILTime SVGSMILElement::maxValue() const
00512 {    
00513     if (m_cachedMax != invalidCachedTime)
00514         return m_cachedMax;
00515     const AtomicString& value = getAttribute(SVGNames::maxAttr);
00516     SMILTime result = parseClockValue(value);
00517     return m_cachedMax = (result.isUnresolved() || result < 0) ? SMILTime::indefinite() : result;
00518 }
00519     
00520 SMILTime SVGSMILElement::minValue() const
00521 {    
00522     if (m_cachedMin != invalidCachedTime)
00523         return m_cachedMin;
00524     const AtomicString& value = getAttribute(SVGNames::minAttr);
00525     SMILTime result = parseClockValue(value);
00526     return m_cachedMin = (result.isUnresolved() || result < 0) ? 0 : result;
00527 }
00528                  
00529 SMILTime SVGSMILElement::simpleDuration() const
00530 {
00531     return min(dur(), SMILTime::indefinite());
00532 }
00533     
00534 void SVGSMILElement::addBeginTime(SMILTime time)
00535 {
00536     m_beginTimes.append(time);
00537     sortTimeList(m_beginTimes);
00538     beginListChanged();
00539 }
00540     
00541 void SVGSMILElement::addEndTime(SMILTime time)
00542 {
00543     m_endTimes.append(time);
00544     sortTimeList(m_endTimes);
00545     endListChanged();
00546 }
00547     
00548 SMILTime SVGSMILElement::findInstanceTime(BeginOrEnd beginOrEnd, SMILTime minimumTime, bool equalsMinimumOK) const
00549 {
00550     // FIXME: This searches from the beginning which is inefficient. The list is usually not long
00551     // (one entry in common cases) but you can construct a case where it does grow.
00552     const Vector<SMILTime>& list = beginOrEnd == Begin ? m_beginTimes : m_endTimes;
00553     for (unsigned n = 0; n < list.size(); ++n) {
00554         SMILTime time = list[n];
00555         ASSERT(!time.isUnresolved());
00556         if (time.isIndefinite() && beginOrEnd == Begin) {
00557             // "The special value "indefinite" does not yield an instance time in the begin list."
00558             continue;
00559         }
00560         if (equalsMinimumOK) {
00561             if (time >= minimumTime)
00562                 return time;
00563         } else if (time > minimumTime)
00564             return time;
00565     }
00566     return SMILTime::unresolved();
00567 }
00568 
00569 SMILTime SVGSMILElement::repeatingDuration() const
00570 {
00571     // Computing the active duration
00572     // http://www.w3.org/TR/SMIL2/smil-timing.html#Timing-ComputingActiveDur
00573     SMILTime repeatCount = this->repeatCount();
00574     SMILTime repeatDur = this->repeatDur();
00575     SMILTime simpleDuration = this->simpleDuration();
00576     if (simpleDuration == 0 || (repeatDur.isUnresolved() && repeatCount.isUnresolved()))
00577         return simpleDuration;
00578     SMILTime repeatCountDuration = simpleDuration * repeatCount;
00579     return min(repeatCountDuration, min(repeatDur, SMILTime::indefinite()));
00580 }
00581 
00582 SMILTime SVGSMILElement::resolveActiveEnd(SMILTime resolvedBegin, SMILTime resolvedEnd) const
00583 {
00584     // Computing the active duration
00585     // http://www.w3.org/TR/SMIL2/smil-timing.html#Timing-ComputingActiveDur
00586     SMILTime preliminaryActiveDuration;
00587     if (!resolvedEnd.isUnresolved() && dur().isUnresolved() && repeatDur().isUnresolved() && repeatCount().isUnresolved())
00588         preliminaryActiveDuration = resolvedEnd - resolvedBegin;
00589     else if (!resolvedEnd.isFinite())
00590         preliminaryActiveDuration = repeatingDuration();
00591     else
00592         preliminaryActiveDuration = min(repeatingDuration(), resolvedEnd - resolvedBegin);
00593     
00594     SMILTime minValue = this->minValue();
00595     SMILTime maxValue = this->maxValue();
00596     if (minValue > maxValue) {
00597         // Ignore both.
00598         // http://www.w3.org/TR/2001/REC-smil-animation-20010904/#MinMax
00599         minValue = 0;
00600         maxValue = SMILTime::indefinite();
00601     }
00602     return resolvedBegin + min(maxValue, max(minValue, preliminaryActiveDuration));
00603 }
00604 
00605 void SVGSMILElement::resolveInterval(bool first, SMILTime& beginResult, SMILTime& endResult) const
00606 {
00607     // See the pseudocode in
00608     // http://www.w3.org/TR/2001/REC-smil-animation-20010904/#Timing-BeginEnd-LifeCycle
00609     SMILTime beginAfter = first ? -numeric_limits<double>::infinity() : m_intervalEnd;
00610     SMILTime lastIntervalTempEnd = numeric_limits<double>::infinity();
00611     while (true) {
00612         SMILTime tempBegin = findInstanceTime(Begin, beginAfter, true);
00613         if (tempBegin.isUnresolved())
00614             break;
00615         SMILTime tempEnd;
00616         if (m_endTimes.isEmpty())
00617             tempEnd = resolveActiveEnd(tempBegin, SMILTime::indefinite());
00618         else {
00619             tempEnd = findInstanceTime(End, tempBegin, true);
00620             if ((first && tempBegin == tempEnd && tempEnd == lastIntervalTempEnd) || (!first && tempEnd == m_intervalEnd)) 
00621                 tempEnd = findInstanceTime(End, tempBegin, false);    
00622             if (tempEnd.isUnresolved()) {
00623                 if (!m_endTimes.isEmpty() && !m_hasEndEventConditions)
00624                     break;
00625             }
00626             tempEnd = resolveActiveEnd(tempBegin, tempEnd);
00627         }
00628         if (tempEnd > 0 || !first) {
00629             beginResult = tempBegin;
00630             endResult = tempEnd;
00631             return;
00632         } else if (restart() == RestartNever)
00633             break;
00634         else
00635             beginAfter = tempEnd;
00636         lastIntervalTempEnd = tempEnd;
00637     }
00638     beginResult = SMILTime::unresolved();
00639     endResult = SMILTime::unresolved();
00640 }
00641     
00642 void SVGSMILElement::resolveFirstInterval()
00643 {
00644     SMILTime begin;
00645     SMILTime end;
00646     resolveInterval(true, begin, end);
00647     ASSERT(!begin.isIndefinite());
00648 
00649     if (!begin.isUnresolved() && (begin != m_intervalBegin || end != m_intervalEnd)) {   
00650         bool wasUnresolved = m_intervalBegin.isUnresolved();
00651         m_intervalBegin = begin;
00652         m_intervalEnd = end;
00653         notifyDependentsIntervalChanged(wasUnresolved ? NewInterval : ExistingInterval);
00654         m_nextProgressTime = min(m_nextProgressTime, m_intervalBegin);
00655         reschedule();
00656     }
00657 }
00658 
00659 void SVGSMILElement::resolveNextInterval()
00660 {
00661     SMILTime begin;
00662     SMILTime end;
00663     resolveInterval(false, begin, end);
00664     ASSERT(!begin.isIndefinite());
00665     
00666     if (!begin.isUnresolved() && begin != m_intervalBegin) {
00667         m_intervalBegin = begin;
00668         m_intervalEnd = end;
00669         notifyDependentsIntervalChanged(NewInterval);
00670         m_nextProgressTime = min(m_nextProgressTime, m_intervalBegin);
00671     }
00672 }
00673 
00674 SMILTime SVGSMILElement::nextProgressTime() const
00675 {
00676     return m_nextProgressTime;
00677 }
00678     
00679 void SVGSMILElement::beginListChanged()
00680 {
00681     SMILTime elapsed = this->elapsed();
00682     if (m_isWaitingForFirstInterval)
00683         resolveFirstInterval();
00684     else if (elapsed < m_intervalBegin) {
00685         SMILTime newBegin = findInstanceTime(Begin, elapsed, false);
00686         if (newBegin < m_intervalBegin) {
00687             // Begin time changed, re-resolve the interval.
00688             SMILTime oldBegin = m_intervalBegin;
00689             m_intervalBegin = elapsed;
00690             resolveInterval(false, m_intervalBegin, m_intervalEnd);  
00691             ASSERT(!m_intervalBegin.isUnresolved());
00692             if (m_intervalBegin != oldBegin)
00693                 notifyDependentsIntervalChanged(ExistingInterval);
00694         }
00695     }
00696     m_nextProgressTime = elapsed;
00697     reschedule();
00698 }
00699 
00700 void SVGSMILElement::endListChanged()
00701 {
00702     SMILTime elapsed = this->elapsed();
00703     if (m_isWaitingForFirstInterval)
00704         resolveFirstInterval();
00705     else if (elapsed < m_intervalEnd && m_intervalBegin.isFinite()) {
00706         SMILTime newEnd = findInstanceTime(End, m_intervalBegin, false);
00707         if (newEnd < m_intervalEnd) {
00708             newEnd = resolveActiveEnd(m_intervalBegin, newEnd);
00709             if (newEnd != m_intervalEnd) {
00710                 m_intervalEnd = newEnd;
00711                 notifyDependentsIntervalChanged(ExistingInterval);
00712             }
00713         }
00714     }
00715     m_nextProgressTime = elapsed;
00716     reschedule();
00717 }
00718     
00719 void SVGSMILElement::checkRestart(SMILTime elapsed)
00720 {
00721     ASSERT(!m_isWaitingForFirstInterval);
00722     ASSERT(elapsed >= m_intervalBegin);
00723     
00724     Restart restart = this->restart();
00725     if (restart == RestartNever)
00726         return;
00727     
00728     if (elapsed < m_intervalEnd) {
00729         if (restart != RestartAlways)
00730             return;
00731         SMILTime nextBegin = findInstanceTime(Begin, m_intervalBegin, false);
00732         if (nextBegin < m_intervalEnd) { 
00733             m_intervalEnd = nextBegin;
00734             notifyDependentsIntervalChanged(ExistingInterval);
00735         }
00736     } 
00737     if (elapsed >= m_intervalEnd)
00738         resolveNextInterval();
00739 }
00740     
00741 float SVGSMILElement::calculateAnimationPercentAndRepeat(SMILTime elapsed, unsigned& repeat) const
00742 {
00743     SMILTime simpleDuration = this->simpleDuration();
00744     repeat = 0;
00745     if (simpleDuration.isIndefinite()) {
00746         repeat = 0;
00747         return 0.f;
00748     }
00749     if (simpleDuration == 0) {
00750         repeat = 0;
00751         return 1.f;
00752     }
00753     ASSERT(m_intervalBegin.isFinite());
00754     ASSERT(simpleDuration.isFinite());
00755     SMILTime activeTime = elapsed - m_intervalBegin;
00756     SMILTime repeatingDuration = this->repeatingDuration();
00757     if (elapsed >= m_intervalEnd || activeTime > repeatingDuration) {
00758         repeat = static_cast<unsigned>(repeatingDuration.value() / simpleDuration.value());
00759         if (fmod(repeatingDuration.value(), simpleDuration.value() == 0.))
00760             repeat--;
00761         return 1.f;
00762     }
00763     repeat = static_cast<unsigned>(activeTime.value() / simpleDuration.value());
00764     SMILTime simpleTime = fmod(activeTime.value(), simpleDuration.value());
00765     return narrowPrecisionToFloat(simpleTime.value() / simpleDuration.value());
00766 }
00767     
00768 SMILTime SVGSMILElement::calculateNextProgressTime(SMILTime elapsed) const
00769 {
00770     if (m_activeState == Active) {
00771         // If duration is indefinite the value does not actually change over time. Same is true for <set>.
00772         SMILTime simpleDuration = this->simpleDuration();
00773         if (simpleDuration.isIndefinite() || hasTagName(SVGNames::setTag)) {
00774             SMILTime repeatCount = this->repeatCount();
00775             SMILTime repeatingDurationEnd = m_intervalBegin + repeatingDuration();
00776             // We are supposed to do freeze semantics when repeating ends, even if the element is still active. 
00777             // Take care that we get a timer callback at that point.
00778             if (elapsed < repeatingDurationEnd && repeatingDurationEnd < m_intervalEnd && repeatingDurationEnd.isFinite())
00779                 return repeatingDurationEnd;
00780             return m_intervalEnd;
00781         } 
00782         return elapsed + 0.025;
00783     }
00784     return m_intervalBegin >= elapsed ? m_intervalBegin : SMILTime::unresolved();
00785 }
00786     
00787 SVGSMILElement::ActiveState SVGSMILElement::determineActiveState(SMILTime elapsed) const
00788 {
00789     if (elapsed >= m_intervalBegin && elapsed < m_intervalEnd)
00790         return Active;
00791 
00792     if (m_activeState == Active)
00793         return fill() == FillFreeze ? Frozen : Inactive;
00794 
00795     return m_activeState;
00796 }
00797     
00798 bool SVGSMILElement::isContributing(SMILTime elapsed) const 
00799 { 
00800     // Animation does not contribute during the active time if it is past its repeating duration and has fill=remove.
00801     return (m_activeState == Active && (fill() == FillFreeze || elapsed <= m_intervalBegin + repeatingDuration())) || m_activeState == Frozen;
00802 }
00803     
00804 void SVGSMILElement::progress(SMILTime elapsed, SVGSMILElement* resultElement)
00805 {
00806     ASSERT(m_timeContainer);
00807     ASSERT(m_isWaitingForFirstInterval || m_intervalBegin.isFinite());
00808     
00809     if (!m_conditionsConnected)
00810         connectConditions();
00811     
00812     if (!m_intervalBegin.isFinite()) {
00813         ASSERT(m_activeState == Inactive);
00814         m_nextProgressTime = SMILTime::unresolved();
00815         return;
00816     }
00817     
00818     if (elapsed < m_intervalBegin) {
00819         ASSERT(m_activeState != Active);
00820         if (m_activeState == Frozen && resultElement)
00821             updateAnimation(m_lastPercent, m_lastRepeat, resultElement);
00822         m_nextProgressTime = m_intervalBegin;
00823         return;
00824     }
00825     
00826     m_previousIntervalBegin = m_intervalBegin;
00827     
00828     if (m_activeState == Inactive) {
00829         m_isWaitingForFirstInterval = false;
00830         m_activeState = Active;
00831         startedActiveInterval();
00832     }
00833     
00834     unsigned repeat;
00835     float percent = calculateAnimationPercentAndRepeat(elapsed, repeat);
00836 
00837     checkRestart(elapsed);
00838 
00839     ActiveState oldActiveState = m_activeState;
00840     m_activeState = determineActiveState(elapsed);
00841     
00842     if (isContributing(elapsed)) {
00843         if (resultElement)
00844             updateAnimation(percent, repeat, resultElement);
00845         m_lastPercent = percent;
00846         m_lastRepeat = repeat;
00847     }
00848 
00849     if (oldActiveState == Active && m_activeState != Active)
00850         endedActiveInterval();
00851 
00852     m_nextProgressTime = calculateNextProgressTime(elapsed);
00853 }
00854     
00855 void SVGSMILElement::notifyDependentsIntervalChanged(NewOrExistingInterval newOrExisting)
00856 {
00857     ASSERT(m_intervalBegin.isFinite());
00858     static HashSet<SVGSMILElement*> loopBreaker;
00859     if (loopBreaker.contains(this))
00860         return;
00861     loopBreaker.add(this);
00862     
00863     TimeDependentSet::iterator end = m_timeDependents.end();
00864     for (TimeDependentSet::iterator it = m_timeDependents.begin(); it != end; ++it) {
00865         SVGSMILElement* dependent = *it;
00866         dependent->createInstanceTimesFromSyncbase(this, newOrExisting);
00867     }
00868 
00869     loopBreaker.remove(this);
00870 }
00871     
00872 void SVGSMILElement::createInstanceTimesFromSyncbase(SVGSMILElement* syncbase, NewOrExistingInterval newOrExisting)
00873 {
00874     // FIXME: To be really correct, this should handle updating exising interval by changing 
00875     // the associated times instead of creating new ones.
00876     for (unsigned n = 0; n < m_conditions.size(); ++n) {
00877         Condition& condition = m_conditions[n];
00878         if (condition.m_type == Condition::Syncbase && condition.m_syncbase == syncbase) {
00879             ASSERT(condition.m_name == "begin" || condition.m_name == "end");
00880             // No nested time containers in SVG, no need for crazy time space conversions. Phew!
00881             SMILTime time = 0;
00882             if (condition.m_name == "begin")
00883                 time = syncbase->m_intervalBegin + condition.m_offset;
00884             else
00885                 time = syncbase->m_intervalEnd + condition.m_offset;
00886             ASSERT(time.isFinite());
00887             if (condition.m_beginOrEnd == Begin)
00888                 addBeginTime(time);
00889             else
00890                 addEndTime(time);
00891         }
00892     }
00893 }
00894     
00895 void SVGSMILElement::addTimeDependent(SVGSMILElement* animation)
00896 {
00897     m_timeDependents.add(animation);
00898     if (m_intervalBegin.isFinite())
00899         animation->createInstanceTimesFromSyncbase(this, NewInterval);
00900 }
00901     
00902 void SVGSMILElement::removeTimeDependent(SVGSMILElement* animation)
00903 {
00904     m_timeDependents.remove(animation);
00905 }
00906     
00907 void SVGSMILElement::handleConditionEvent(Event* event, Condition* condition)
00908 {
00909     if (condition->m_beginOrEnd == Begin)
00910         addBeginTime(elapsed() + condition->m_offset);
00911     else
00912         addEndTime(elapsed() + condition->m_offset);
00913 }
00914 
00915 void SVGSMILElement::beginByLinkActivation()
00916 {
00917     addBeginTime(elapsed());
00918 }
00919     
00920 }
00921 
00922 #endif
00923 

KHTML

Skip menu "KHTML"
  • Main Page
  • Namespace List
  • Class Hierarchy
  • Alphabetical List
  • Class List
  • File List
  • Namespace Members
  • Class Members
  • Related Pages

kdelibs

Skip menu "kdelibs"
  • DNSSD
  • Interfaces
  •   KHexEdit
  •   KMediaPlayer
  •   KSpeech
  •   KTextEditor
  • Kate
  • kconf_update
  • KDE3Support
  •   KUnitTest
  • KDECore
  • KDED
  • KDEsu
  • KDEUI
  • KDocTools
  • KFile
  • KHTML
  • KImgIO
  • KInit
  • kio
  • KIOSlave
  • KJS
  •   KJS-API
  •   WTF
  • kjsembed
  • KNewStuff
  • KParts
  • Kross
  • KUtils
  • Nepomuk
  • Plasma
  • Solid
  • Sonnet
  • ThreadWeaver
Generated for kdelibs by doxygen 1.5.7
This website is maintained by Adriaan de Groot and Allen Winter.
KDE® and the K Desktop Environment® logo are registered trademarks of KDE e.V. | Legal