/* * Copyright 2006 The Android Open Source Project * * Use of this source code is governed by a BSD-style license that can be * found in the LICENSE file. */ #include "SkDisplayPost.h" #include "SkAnimateMaker.h" #include "SkAnimator.h" #include "SkDisplayMovie.h" #include "SkPostParts.h" #include "SkScript.h" #ifdef SK_DEBUG #include "SkDump.h" #include "SkTime.h" #endif enum SkPost_Properties { SK_PROPERTY(target), SK_PROPERTY(type) }; #if SK_USE_CONDENSED_INFO == 0 const SkMemberInfo SkPost::fInfo[] = { SK_MEMBER(delay, MSec), // SK_MEMBER(initialized, Boolean), SK_MEMBER(mode, EventMode), SK_MEMBER(sink, String), SK_MEMBER_PROPERTY(target, String), SK_MEMBER_PROPERTY(type, String) }; #endif DEFINE_GET_MEMBER(SkPost); SkPost::SkPost() : delay(0), /*initialized(SkBool(-1)), */ mode(kImmediate), fMaker(NULL), fSinkID(0), fTargetMaker(NULL), fChildHasID(false), fDirty(false) { } SkPost::~SkPost() { for (SkDataInput** part = fParts.begin(); part < fParts.end(); part++) delete *part; } bool SkPost::add(SkAnimateMaker& , SkDisplayable* child) { SkASSERT(child && child->isDataInput()); SkDataInput* part = (SkDataInput*) child; *fParts.append() = part; return true; } bool SkPost::childrenNeedDisposing() const { return false; } void SkPost::dirty() { fDirty = true; } #ifdef SK_DUMP_ENABLED void SkPost::dump(SkAnimateMaker* maker) { dumpBase(maker); SkString* eventType = new SkString(); fEvent.getType(eventType); if (eventType->equals("user")) { const char* target = fEvent.findString("id"); SkDebugf("target=\"%s\" ", target); } else SkDebugf("type=\"%s\" ", eventType->c_str()); delete eventType; if (delay > 0) { #ifdef SK_CAN_USE_FLOAT SkDebugf("delay=\"%g\" ", SkScalarToFloat(SkScalarDiv(delay, 1000))); #else SkDebugf("delay=\"%x\" ", SkScalarDiv(delay, 1000)); #endif } // if (initialized == false) // SkDebugf("(uninitialized) "); SkString string; SkDump::GetEnumString(SkType_EventMode, mode, &string); if (!string.equals("immediate")) SkDebugf("mode=\"%s\" ", string.c_str()); // !!! could enhance this to search through make hierarchy to show name of sink if (sink.size() > 0) { SkDebugf("sink=\"%s\" sinkID=\"%d\" ", sink.c_str(), fSinkID); } else if (fSinkID != maker->getAnimator()->getSinkID() && fSinkID != 0) { SkDebugf("sinkID=\"%d\" ", fSinkID); } const SkMetaData& meta = fEvent.getMetaData(); SkMetaData::Iter iter(meta); SkMetaData::Type type; int number; const char* name; bool closedYet = false; SkDisplayList::fIndent += 4; //this seems to work, but kinda hacky //for some reason the last part is id, which i don't want //and the parts seem to be in the reverse order from the one in which we find the //data itself //SkDataInput** ptr = fParts.end(); //SkDataInput* data; //const char* ID; while ((name = iter.next(&type, &number)) != NULL) { //ptr--; if (strcmp(name, "id") == 0) continue; if (closedYet == false) { SkDebugf(">\n"); closedYet = true; } //data = *ptr; //if (data->id) // ID = data->id; //else // ID = ""; SkDebugf("%*s\n"); //ptr++; /* perhaps this should only be done in the case of a pointer? SkDisplayable* displayable; if (maker->find(name, &displayable)) displayable->dump(maker); else SkDebugf("\n");*/ } SkDisplayList::fIndent -= 4; if (closedYet) dumpEnd(maker); else SkDebugf("/>\n"); } #endif bool SkPost::enable(SkAnimateMaker& maker ) { if (maker.hasError()) return true; if (fDirty) { if (sink.size() > 0) findSinkID(); if (fChildHasID) { SkString preserveID(fEvent.findString("id")); fEvent.getMetaData().reset(); if (preserveID.size() > 0) fEvent.setString("id", preserveID); for (SkDataInput** part = fParts.begin(); part < fParts.end(); part++) { if ((*part)->add()) maker.setErrorCode(SkDisplayXMLParserError::kErrorAddingDataToPost); } } fDirty = false; } #ifdef SK_DUMP_ENABLED if (maker.fDumpPosts) { SkDebugf("post enable: "); dump(&maker); } #if defined SK_DEBUG_ANIMATION_TIMING SkString debugOut; SkMSec time = maker.getAppTime(); debugOut.appendS32(time - maker.fDebugTimeBase); debugOut.append(" post id="); debugOut.append(_id); debugOut.append(" enable="); debugOut.appendS32(maker.fEnableTime - maker.fDebugTimeBase); debugOut.append(" delay="); debugOut.appendS32(delay); #endif #endif // SkMSec adjustedDelay = maker.adjustDelay(maker.fEnableTime, delay); SkMSec futureTime = maker.fEnableTime + delay; fEvent.setFast32(futureTime); #if defined SK_DEBUG && defined SK_DEBUG_ANIMATION_TIMING debugOut.append(" future="); debugOut.appendS32(futureTime - maker.fDebugTimeBase); SkDebugf("%s\n", debugOut.c_str()); #endif SkEventSinkID targetID = fSinkID; bool isAnimatorEvent = true; SkAnimator* anim = maker.getAnimator(); if (targetID == 0) { isAnimatorEvent = fEvent.findString("id") != NULL; if (isAnimatorEvent) targetID = anim->getSinkID(); else if (maker.fHostEventSinkID) targetID = maker.fHostEventSinkID; else return true; } else anim = fTargetMaker->getAnimator(); if (delay == 0) { if (isAnimatorEvent && mode == kImmediate) fTargetMaker->doEvent(fEvent); else anim->onEventPost(new SkEvent(fEvent), targetID); } else anim->onEventPostTime(new SkEvent(fEvent), targetID, futureTime); return true; } void SkPost::findSinkID() { // get the next delimiter '.' if any fTargetMaker = fMaker; const char* ch = sink.c_str(); do { const char* end = strchr(ch, '.'); size_t len = end ? end - ch : strlen(ch); SkDisplayable* displayable = NULL; if (SK_LITERAL_STR_EQUAL("parent", ch, len)) { if (fTargetMaker->fParentMaker) fTargetMaker = fTargetMaker->fParentMaker; else { fTargetMaker->setErrorCode(SkDisplayXMLParserError::kNoParentAvailable); return; } } else { fTargetMaker->find(ch, len, &displayable); if (displayable == NULL || displayable->getType() != SkType_Movie) { fTargetMaker->setErrorCode(SkDisplayXMLParserError::kExpectedMovie); return; } SkDisplayMovie* movie = (SkDisplayMovie*) displayable; fTargetMaker = movie->fMovie.fMaker; } if (end == NULL) break; ch = ++end; } while (true); SkAnimator* anim = fTargetMaker->getAnimator(); fSinkID = anim->getSinkID(); } bool SkPost::hasEnable() const { return true; } void SkPost::onEndElement(SkAnimateMaker& maker) { fTargetMaker = fMaker = &maker; if (fChildHasID == false) { for (SkDataInput** part = fParts.begin(); part < fParts.end(); part++) delete *part; fParts.reset(); } } void SkPost::setChildHasID() { fChildHasID = true; } bool SkPost::setProperty(int index, SkScriptValue& value) { SkASSERT(value.fType == SkType_String); SkString* string = value.fOperand.fString; switch(index) { case SK_PROPERTY(target): { fEvent.setType("user"); fEvent.setString("id", *string); mode = kImmediate; } break; case SK_PROPERTY(type): fEvent.setType(*string); break; default: SkASSERT(0); return false; } return true; }