1
2/*
3 * Copyright 2006 The Android Open Source Project
4 *
5 * Use of this source code is governed by a BSD-style license that can be
6 * found in the LICENSE file.
7 */
8
9
10#include "SkDisplayPost.h"
11#include "SkAnimateMaker.h"
12#include "SkAnimator.h"
13#include "SkDisplayMovie.h"
14#include "SkPostParts.h"
15#include "SkScript.h"
16#ifdef SK_DEBUG
17#include "SkDump.h"
18#include "SkTime.h"
19#endif
20
21enum SkPost_Properties {
22    SK_PROPERTY(target),
23    SK_PROPERTY(type)
24};
25
26#if SK_USE_CONDENSED_INFO == 0
27
28const SkMemberInfo SkPost::fInfo[] = {
29    SK_MEMBER(delay, MSec),
30//  SK_MEMBER(initialized, Boolean),
31    SK_MEMBER(mode, EventMode),
32    SK_MEMBER(sink, String),
33    SK_MEMBER_PROPERTY(target, String),
34    SK_MEMBER_PROPERTY(type, String)
35};
36
37#endif
38
39DEFINE_GET_MEMBER(SkPost);
40
41SkPost::SkPost() : delay(0), /*initialized(SkBool(-1)), */ mode(kImmediate), fMaker(NULL),
42    fSinkID(0), fTargetMaker(NULL), fChildHasID(false), fDirty(false) {
43}
44
45SkPost::~SkPost() {
46    for (SkDataInput** part = fParts.begin(); part < fParts.end();  part++)
47        delete *part;
48}
49
50bool SkPost::addChild(SkAnimateMaker& , SkDisplayable* child) {
51    SkASSERT(child && child->isDataInput());
52    SkDataInput* part = (SkDataInput*) child;
53    *fParts.append() = part;
54    return true;
55}
56
57bool SkPost::childrenNeedDisposing() const {
58    return false;
59}
60
61void SkPost::dirty() {
62    fDirty = true;
63}
64
65#ifdef SK_DUMP_ENABLED
66void SkPost::dump(SkAnimateMaker* maker) {
67    dumpBase(maker);
68    SkString* eventType = new SkString();
69    fEvent.getType(eventType);
70    if (eventType->equals("user")) {
71        const char* target = fEvent.findString("id");
72        SkDebugf("target=\"%s\" ", target);
73    }
74    else
75        SkDebugf("type=\"%s\" ", eventType->c_str());
76    delete eventType;
77
78    if (delay > 0) {
79        SkDebugf("delay=\"%g\" ", SkScalarToFloat(SkScalarDiv(delay, 1000)));
80    }
81//  if (initialized == false)
82//      SkDebugf("(uninitialized) ");
83    SkString string;
84    SkDump::GetEnumString(SkType_EventMode, mode, &string);
85    if (!string.equals("immediate"))
86        SkDebugf("mode=\"%s\" ", string.c_str());
87    // !!! could enhance this to search through make hierarchy to show name of sink
88    if (sink.size() > 0) {
89        SkDebugf("sink=\"%s\" sinkID=\"%d\" ", sink.c_str(), fSinkID);
90    } else if (fSinkID != maker->getAnimator()->getSinkID() && fSinkID != 0) {
91        SkDebugf("sinkID=\"%d\" ", fSinkID);
92    }
93    const SkMetaData& meta = fEvent.getMetaData();
94    SkMetaData::Iter iter(meta);
95    SkMetaData::Type    type;
96    int number;
97    const char* name;
98    bool closedYet = false;
99    SkDisplayList::fIndent += 4;
100    //this seems to work, but kinda hacky
101    //for some reason the last part is id, which i don't want
102    //and the parts seem to be in the reverse order from the one in which we find the
103    //data itself
104    //SkDataInput** ptr = fParts.end();
105    //SkDataInput* data;
106    //const char* ID;
107    while ((name = iter.next(&type, &number)) != NULL) {
108        //ptr--;
109        if (strcmp(name, "id") == 0)
110            continue;
111        if (closedYet == false) {
112            SkDebugf(">\n");
113            closedYet = true;
114        }
115        //data = *ptr;
116        //if (data->id)
117        //    ID = data->id;
118        //else
119        //    ID = "";
120        SkDebugf("%*s<data name=\"%s\" ", SkDisplayList::fIndent, "", name);
121        switch (type) {
122            case SkMetaData::kS32_Type: {
123                int32_t s32;
124                meta.findS32(name, &s32);
125                SkDebugf("int=\"%d\" ", s32);
126                } break;
127            case SkMetaData::kScalar_Type: {
128                SkScalar scalar;
129                meta.findScalar(name, &scalar);
130                SkDebugf("float=\"%g\" ", SkScalarToFloat(scalar));
131                } break;
132            case SkMetaData::kString_Type:
133                SkDebugf("string=\"%s\" ", meta.findString(name));
134                break;
135            case SkMetaData::kPtr_Type: {//when do we have a pointer
136                    void* ptr;
137                    meta.findPtr(name, &ptr);
138                    SkDebugf("0x%08x ", ptr);
139                } break;
140            case SkMetaData::kBool_Type: {
141                bool boolean;
142                meta.findBool(name, &boolean);
143                SkDebugf("boolean=\"%s\" ", boolean ? "true " : "false ");
144                } break;
145            default:
146                break;
147        }
148        SkDebugf("/>\n");
149        //ptr++;
150/*      perhaps this should only be done in the case of a pointer?
151        SkDisplayable* displayable;
152        if (maker->find(name, &displayable))
153            displayable->dump(maker);
154        else
155            SkDebugf("\n");*/
156    }
157    SkDisplayList::fIndent -= 4;
158    if (closedYet)
159        dumpEnd(maker);
160    else
161        SkDebugf("/>\n");
162
163}
164#endif
165
166bool SkPost::enable(SkAnimateMaker& maker ) {
167    if (maker.hasError())
168        return true;
169    if (fDirty) {
170        if (sink.size() > 0)
171            findSinkID();
172        if (fChildHasID) {
173            SkString preserveID(fEvent.findString("id"));
174            fEvent.getMetaData().reset();
175            if (preserveID.size() > 0)
176                fEvent.setString("id", preserveID);
177            for (SkDataInput** part = fParts.begin(); part < fParts.end();  part++) {
178                if ((*part)->add())
179                    maker.setErrorCode(SkDisplayXMLParserError::kErrorAddingDataToPost);
180            }
181        }
182        fDirty = false;
183    }
184#ifdef SK_DUMP_ENABLED
185    if (maker.fDumpPosts) {
186        SkDebugf("post enable: ");
187        dump(&maker);
188    }
189#if defined SK_DEBUG_ANIMATION_TIMING
190    SkString debugOut;
191    SkMSec time = maker.getAppTime();
192    debugOut.appendS32(time - maker.fDebugTimeBase);
193    debugOut.append(" post id=");
194    debugOut.append(_id);
195    debugOut.append(" enable=");
196    debugOut.appendS32(maker.fEnableTime - maker.fDebugTimeBase);
197    debugOut.append(" delay=");
198    debugOut.appendS32(delay);
199#endif
200#endif
201//  SkMSec adjustedDelay = maker.adjustDelay(maker.fEnableTime, delay);
202    SkMSec futureTime = maker.fEnableTime + delay;
203    fEvent.setFast32(futureTime);
204#if defined SK_DEBUG && defined SK_DEBUG_ANIMATION_TIMING
205    debugOut.append(" future=");
206    debugOut.appendS32(futureTime - maker.fDebugTimeBase);
207    SkDebugf("%s\n", debugOut.c_str());
208#endif
209    SkEventSinkID targetID = fSinkID;
210    bool isAnimatorEvent = true;
211    SkAnimator* anim = maker.getAnimator();
212    if (targetID == 0) {
213        isAnimatorEvent = fEvent.findString("id") != NULL;
214        if (isAnimatorEvent)
215            targetID = anim->getSinkID();
216        else if (maker.fHostEventSinkID)
217            targetID = maker.fHostEventSinkID;
218        else
219            return true;
220    } else
221        anim = fTargetMaker->getAnimator();
222    if (delay == 0) {
223        if (isAnimatorEvent && mode == kImmediate)
224            fTargetMaker->doEvent(fEvent);
225        else
226            anim->onEventPost(new SkEvent(fEvent), targetID);
227    } else
228        anim->onEventPostTime(new SkEvent(fEvent), targetID, futureTime);
229    return true;
230}
231
232void SkPost::findSinkID() {
233    // get the next delimiter '.' if any
234    fTargetMaker = fMaker;
235    const char* ch = sink.c_str();
236    do {
237        const char* end = strchr(ch, '.');
238        size_t len = end ? (size_t) (end - ch) : strlen(ch);
239        SkDisplayable* displayable = NULL;
240        if (SK_LITERAL_STR_EQUAL("parent", ch, len)) {
241            if (fTargetMaker->fParentMaker)
242                fTargetMaker = fTargetMaker->fParentMaker;
243            else {
244                fTargetMaker->setErrorCode(SkDisplayXMLParserError::kNoParentAvailable);
245                return;
246            }
247        } else {
248            fTargetMaker->find(ch, len, &displayable);
249            if (displayable == NULL || displayable->getType() != SkType_Movie) {
250                fTargetMaker->setErrorCode(SkDisplayXMLParserError::kExpectedMovie);
251                return;
252            }
253            SkDisplayMovie* movie = (SkDisplayMovie*) displayable;
254            fTargetMaker = movie->fMovie.fMaker;
255        }
256        if (end == NULL)
257            break;
258        ch = ++end;
259    } while (true);
260    SkAnimator* anim = fTargetMaker->getAnimator();
261    fSinkID = anim->getSinkID();
262}
263
264bool SkPost::hasEnable() const {
265    return true;
266}
267
268void SkPost::onEndElement(SkAnimateMaker& maker) {
269    fTargetMaker = fMaker = &maker;
270    if (fChildHasID == false) {
271        for (SkDataInput** part = fParts.begin(); part < fParts.end();  part++)
272            delete *part;
273        fParts.reset();
274    }
275}
276
277void SkPost::setChildHasID() {
278    fChildHasID = true;
279}
280
281bool SkPost::setProperty(int index, SkScriptValue& value) {
282    SkASSERT(value.fType == SkType_String);
283    SkString* string = value.fOperand.fString;
284    switch(index) {
285        case SK_PROPERTY(target): {
286            fEvent.setType("user");
287            fEvent.setString("id", *string);
288            mode = kImmediate;
289            } break;
290        case SK_PROPERTY(type):
291            fEvent.setType(*string);
292            break;
293        default:
294            SkASSERT(0);
295            return false;
296    }
297    return true;
298}
299