File indexing completed on 2024-04-21 04:54:03

0001 /*
0002     SPDX-FileCopyrightText: 2005-2007 Koos Vriezen <koos.vriezen@gmail.com>
0003 
0004     SPDX-License-Identifier: LGPL-2.0-only
0005 */
0006 
0007 #include "config-kmplayer.h"
0008 
0009 #include <cstdlib>
0010 
0011 #include <QTextStream>
0012 #include <QColor>
0013 #include <QFont>
0014 #include <QApplication>
0015 #include <QRegExp>
0016 #include <QTimer>
0017 #include <QBuffer>
0018 
0019 #include <KIO/Job>
0020 
0021 #include "kmplayercommon_log.h"
0022 #include "kmplayer_smil.h"
0023 #include "kmplayer_rp.h"
0024 #include "expression.h"
0025 #include "mediaobject.h"
0026 
0027 using namespace KMPlayer;
0028 
0029 namespace KMPlayer {
0030 
0031 static const unsigned int begin_timer_id = (unsigned int) 3;
0032 static const unsigned int dur_timer_id = (unsigned int) 4;
0033 static const unsigned int anim_timer_id = (unsigned int) 5;
0034 static const unsigned int trans_timer_id = (unsigned int) 6;
0035 static const unsigned int trans_out_timer_id = (unsigned int) 7;
0036 
0037 }
0038 
0039 /* Intrinsic duration
0040  *  DurTime         |    EndTime     |
0041  *  =======================================================================
0042  *    DurMedia      |   DurMedia     | wait for event
0043  *        0         |   DurMedia     | only wait for child elements
0044  *    DurMedia      |       0        | intrinsic duration finished
0045  */
0046 //-----------------------------------------------------------------------------
0047 
0048 bool KMPlayer::parseTime (const QString & vl, int & dur) {
0049     QByteArray ba = vl.toLatin1 ();
0050     const char *cval = ba.constData ();
0051     if (!cval) {
0052         dur = 0;
0053         return false;
0054     }
0055     int sign = 1;
0056     bool fp_seen = false;
0057     QString num;
0058     const char * p = cval;
0059     for ( ; *p; p++ ) {
0060         if (*p == '+') {
0061             if (!num.isEmpty ())
0062                 break;
0063             else
0064                 sign = 1;
0065         } else if (*p == '-') {
0066             if (!num.isEmpty ())
0067                 break;
0068             else
0069                 sign = -1;
0070         } else if (*p >= '0' && *p <= '9') {
0071             num += QChar (*p);
0072         } else if (*p == '.') {
0073             if (fp_seen)
0074                 break;
0075             else
0076                 num += QChar (*p);
0077             fp_seen = true;
0078         } else if (*p == ' ') {
0079             if (!num.isEmpty ())
0080                 break;
0081         } else if (*p == ':') {
0082             dur = Mrl::parseTimeString (vl);
0083             return dur != 0;
0084         } else
0085             break;
0086     }
0087     bool ok = false;
0088     double t;
0089     if (!num.isEmpty ())
0090         t = sign * num.toDouble (&ok);
0091     if (ok) {
0092         for ( ; *p; p++ ) {
0093             if (*p == 'm') {
0094                 t *= 60;
0095                 break;
0096             } else if (*p == 'h') {
0097                 t *= 60 * 60;
0098                 break;
0099             } else if (*p != ' ')
0100                 break;
0101         }
0102         dur = (int) (100 * t);
0103     } else {
0104         dur = 0;
0105         return false;
0106     }
0107     return true;
0108 }
0109 
0110 static SMIL::Region *findRegion2 (Node *p, const QString &id) {
0111     TrieString regionname_attr ("regionName");
0112     for (Node *c = p->firstChild (); c; c = c->nextSibling ()) {
0113         if (c->id == SMIL::id_node_region) {
0114             SMIL::Region *r = static_cast <SMIL::Region *> (c);
0115             QString a = r->getAttribute (regionname_attr);
0116             if (a.isEmpty ())
0117                 a = r->getAttribute (Ids::attr_id);
0118             if ((a.isEmpty () && id.isEmpty ()) || a == id)
0119                 return r;
0120         }
0121         SMIL::Region * r = findRegion2 (c, id);
0122         if (r)
0123             return r;
0124     }
0125     return nullptr;
0126 }
0127 
0128 static SMIL::RegionBase *findRegion (Node *n, const QString &id) {
0129     SMIL::RegionBase *region = nullptr;
0130     SMIL::Smil *smil = SMIL::Smil::findSmilNode (n);
0131     if (smil) {
0132         SMIL::Layout *layout = convertNode <SMIL::Layout> (smil->layout_node);
0133         region = findRegion2 (layout, id);
0134         if (!region)
0135             region = convertNode <SMIL::RegionBase> (layout->root_layout);
0136     }
0137     return region;
0138 }
0139 
0140 static Node *findHeadNode (SMIL::Smil *s)
0141 {
0142     for (Node *h = s ? s->firstChild () : nullptr; h; h = h->nextSibling ())
0143         if (SMIL::id_node_head == h->id)
0144             return h;
0145     return nullptr;
0146 }
0147 
0148 static SMIL::Transition *findTransition (Node *n, const QString &id)
0149 {
0150     Node *head = findHeadNode (SMIL::Smil::findSmilNode (n));
0151     if (head)
0152         for (Node *c = head->firstChild (); c; c = c->nextSibling())
0153             if (c->id == SMIL::id_node_transition &&
0154                     id == static_cast <Element *> (c)->
0155                     getAttribute (Ids::attr_id))
0156                 return static_cast <SMIL::Transition *> (c);
0157     return nullptr;
0158 }
0159 
0160 static bool parseTransitionParam (Node *n, TransitionModule &m, Runtime *r,
0161         const TrieString &para, const QString &val) {
0162     if (para == "transIn") {
0163         SMIL::Transition *t = findTransition (n, val);
0164         if (t) {
0165             m.trans_in = t;
0166             r->trans_in_dur = t->dur;
0167         } else {
0168             qCWarning(LOG_KMPLAYER_COMMON) << "Transition " << val << " not found in head";
0169         }
0170     } else if (para == "transOut") {
0171         m.trans_out = findTransition (n, val);
0172         if (!m.trans_out)
0173             qCWarning(LOG_KMPLAYER_COMMON) << "Transition " << val << " not found in head";
0174     } else {
0175         return false;
0176     }
0177     return true;
0178 }
0179 
0180 static Node *findLocalNodeById (Node *n, const QString & id) {
0181     SMIL::Smil *s = SMIL::Smil::findSmilNode (n);
0182     if (s)
0183         return s->document ()->getElementById (s, id, false);
0184     return nullptr;
0185 }
0186 
0187 static Fit parseFit (const char *cval) {
0188     Fit fit;
0189     if (!cval)
0190         fit = fit_hidden;
0191     else if (!strcmp (cval, "fill"))
0192         fit = fit_fill;
0193     else if (!strcmp (cval, "hidden"))
0194         fit = fit_hidden;
0195     else if (!strcmp (cval, "meet"))
0196         fit = fit_meet;
0197     else if (!strcmp (cval, "scroll"))
0198         fit = fit_scroll;
0199     else if (!strcmp (cval, "slice"))
0200         fit = fit_slice;
0201     else
0202         fit = fit_default;
0203     return fit;
0204 }
0205 
0206 //-----------------------------------------------------------------------------
0207 
0208 PostponedEvent::PostponedEvent (bool postponed)
0209  : is_postponed (postponed) {}
0210 
0211 //-----------------------------------------------------------------------------
0212 
0213 Runtime::DurationItem::DurationItem ()
0214     : durval (DurTimer), offset (0), next (nullptr) {}
0215 
0216 Runtime::DurationItem &
0217 Runtime::DurationItem::operator = (const Runtime::DurationItem &d) {
0218     durval = d.durval;
0219     offset = d.offset;
0220     payload = d.payload;
0221     connection.assign (&d.connection);
0222     return *this;
0223 }
0224 
0225 bool Runtime::DurationItem::matches (const Duration dur, const Posting *post) {
0226     return dur == durval &&
0227         connection.signaler () == post->source.ptr () &&
0228         ((Duration) MsgStateChanged != durval || post->payload == payload);
0229 }
0230 
0231 void Runtime::DurationItem::clear() {
0232     durval = DurTimer;
0233     offset = 0;
0234     payload = nullptr;
0235     connection.disconnect ();
0236     if (next) {
0237         next->clear ();
0238         delete next;
0239         next = nullptr;
0240     }
0241 }
0242 
0243 static Runtime::Fill getDefaultFill (NodePtr n) {
0244     for (NodePtr p = n->parentNode (); p; p = p->parentNode ()) {
0245         Runtime *rt = static_cast <Runtime *> (p->role (RoleTiming));
0246         if (rt) {
0247             if (rt->fill_def != Runtime::fill_inherit)
0248                 return rt->fill_def;
0249             else if (rt->fill == Runtime::fill_default)
0250                 return rt->fill_active;//assume parent figured out this
0251         } else if (p->id == SMIL::id_node_smil)
0252             break;
0253     }
0254     return Runtime::fill_auto;
0255 }
0256 
0257 Runtime::Runtime (Element *e)
0258  : begin_timer (nullptr),
0259    duration_timer (nullptr),
0260    started_timer (nullptr),
0261    stopped_timer (nullptr),
0262    fill_active (fill_auto),
0263    element (nullptr) {
0264     init();
0265     element = e;
0266 }
0267 
0268 
0269 Runtime::~Runtime () {
0270     if (begin_timer)
0271         element->document ()->cancelPosting (begin_timer);
0272     if (duration_timer)
0273         element->document ()->cancelPosting (duration_timer);
0274     element = nullptr;
0275     init ();
0276 }
0277 
0278 void Runtime::init ()
0279 {
0280     if (element && begin_timer) {
0281         element->document ()->cancelPosting (begin_timer);
0282         begin_timer = nullptr;
0283     }
0284     if (element && duration_timer) {
0285         element->document ()->cancelPosting (duration_timer);
0286         duration_timer = nullptr;
0287     }
0288     repeat = repeat_count = 1;
0289     trans_in_dur = 0;
0290     timingstate = TimingsInit;
0291     for (int i = 0; i < (int) DurTimeLast; i++)
0292         durations [i].clear ();
0293     endTime ().durval = DurMedia;
0294     start_time = finish_time = 0;
0295     fill = fill_default;
0296     fill_def = fill_inherit;
0297 }
0298 
0299 void Runtime::initialize ()
0300 {
0301     if (fill == fill_default) {
0302         if (fill_def == fill_inherit)
0303             fill_active = getDefaultFill (element);
0304         else
0305             fill_active = fill_def;
0306     }
0307     timingstate = Runtime::TimingsInitialized;
0308 }
0309 
0310 static
0311 void setDurationItem (Node *n, const QString &val, Runtime::DurationItem *itm) {
0312     int dur = -2; // also 0 for 'media' duration, so it will not update then
0313     QString vs = val.trimmed ();
0314     QString vl = vs.toLower ();
0315     QByteArray ba = vl.toLatin1 ();
0316     const char *cval = ba.constData ();
0317     int offset = 0;
0318     VirtualVoid *payload = nullptr;
0319     if (cval && cval[0]) {
0320         QString idref;
0321         const char * p = cval;
0322         if (parseTime (vl, offset)) {
0323             dur = Runtime::DurTimer;
0324         } else if (!strncmp (cval, "id(", 3)) {
0325             p = strchr (cval + 3, ')');
0326             if (p) {
0327                 idref = vs.mid (3, p - cval - 3);
0328                 p++;
0329             }
0330             if (*p) {
0331                 const char *q = strchr (p, '(');
0332                 if (q)
0333                     p = q;
0334             }
0335         } else if (!strncmp (cval, "indefinite", 10)) {
0336             offset = -1;
0337             dur = Runtime::DurIndefinite;
0338         } else if (!strncmp (cval, "media", 5)) {
0339             dur = Runtime::DurMedia;
0340         }
0341         if (dur == -2) {
0342             NodePtr target;
0343             const char * q = p;
0344             if (idref.isEmpty ()) {
0345                 bool last_esc = false;
0346                 for ( ; *q; q++) {
0347                     if (*q == '\\') {
0348                         if (last_esc) {
0349                             idref += QChar ('\\');
0350                             last_esc = false;
0351                         } else
0352                             last_esc = true;
0353                     } else if (*q == '.' && !last_esc) {
0354                         break;
0355                     } else
0356                         idref += QChar (*q);
0357                 }
0358                 if (!*q)
0359                     idref = vs.mid (p - cval);
0360                 else
0361                     idref = vs.mid (p - cval, q - p);
0362             }
0363             if (*q) {
0364                 ++q;
0365                 if (!idref.isEmpty ()) {
0366                     target = findLocalNodeById (n, idref);
0367                     if (!target)
0368                         q = p;
0369                 }
0370             } else {
0371                 q = p;
0372             }
0373             if (parseTime (vl.mid (q-cval), offset)) {
0374                 dur = Runtime::DurStart;
0375             } else if (*q && !strncmp (q, "end", 3)) {
0376                 dur = Runtime::DurEnd;
0377                 parseTime (vl.mid (q + 3 - cval), offset);
0378             } else if (*q && !strncmp (q, "begin", 5)) {
0379                 dur = Runtime::DurStart;
0380                 parseTime (vl.mid (q + 5 - cval), offset);
0381             } else if (*q && !strncmp (q, "activateevent", 13)) {
0382                 dur = Runtime::DurActivated;
0383                 parseTime (vl.mid (q + 13 - cval), offset);
0384             } else if (*q && !strncmp (q, "inboundsevent", 13)) {
0385                 dur = Runtime::DurInBounds;
0386                 parseTime (vl.mid (q + 13 - cval), offset);
0387             } else if (*q && !strncmp (q, "outofboundsevent", 16)) {
0388                 dur = Runtime::DurOutBounds;
0389                 parseTime (vl.mid (q + 16 - cval), offset);
0390             } else if (*q && !strncmp (q, "statechange", 11)) {
0391                 int op = vl.indexOf ('(', 11);
0392                 if (op > -1) {
0393                     int cp = vl.indexOf (')', op + 1);
0394                     if (cp > -1) {
0395                         payload = evaluateExpr(vl.mid(op + 1, cp - op - 1).toUtf8(), "data");
0396                         dur = Runtime::DurStateChanged;
0397                     }
0398                 }
0399             } else if (*q && !strncmp (q, "accesskey", 9)) {
0400                 int op = vl.indexOf ('(', 9);
0401                 if (op > -1) {
0402                     int cp = vl.indexOf (')', op + 1);
0403                     if (cp > -1) {
0404                         QString ch = vl.mid (op + 1, cp - op - 1);
0405                         if (!ch.isEmpty ()) {
0406                             payload = new KeyLoad (ch[0].unicode ());
0407                             dur = Runtime::DurAccessKey;
0408                             target = n->document ();
0409                         }
0410                     }
0411                 }
0412             } else
0413                 qCWarning(LOG_KMPLAYER_COMMON) << "setDuration no match " << cval;
0414             if (!target &&
0415                    dur >= Runtime::DurActivated && dur <= Runtime::DurOutBounds)
0416                 target = n;
0417             if (target && dur != Runtime::DurTimer)
0418                 itm->connection.connect (target, (MessageType)dur, n, payload);
0419         }
0420     }
0421     itm->durval = (Runtime::Duration) dur;
0422     itm->offset = offset;
0423     itm->payload = payload;
0424 }
0425 
0426 static
0427 void setDurationItems (Node *n, const QString &s, Runtime::DurationItem *item) {
0428     item->clear ();
0429     Runtime::DurationItem *last = item;
0430     QStringList list = s.split (QChar (';'));
0431     bool timer_set = false;
0432     for (int i = 0; i < list.count(); ++i) {
0433         QString val = list[i].trimmed();
0434         if (!val.isEmpty ()) {
0435             Runtime::DurationItem di;
0436             setDurationItem (n, val, &di);
0437             switch (di.durval) {
0438             case Runtime::DurTimer:
0439             case Runtime::DurIndefinite:
0440             case Runtime::DurMedia:
0441                 *item = di;
0442                 timer_set = true;
0443                 break;
0444             default:
0445                 last = last->next = new Runtime::DurationItem;
0446                 *last = di;
0447             }
0448         }
0449     }
0450     if (item->next && !timer_set)
0451         item->durval = Runtime::DurIndefinite;
0452 }
0453 
0454 static bool disabledByExpr (Runtime *rt) {
0455     bool b = false;
0456     if (!rt->expr.isEmpty ()) {
0457         Expression* res = evaluateExpr(rt->expr.toUtf8(), "data");
0458         if (res) {
0459             SMIL::Smil *smil = SMIL::Smil::findSmilNode (rt->element);
0460             res->setRoot (smil ? smil->state_node.ptr() : nullptr);
0461             b = !res->toBool ();
0462             delete res;
0463         }
0464     }
0465     return b;
0466 }
0467 
0468 /**
0469  * start, or restart in case of re-use, the durations
0470  */
0471 void Runtime::start () {
0472     if (begin_timer || duration_timer)
0473         element->init ();
0474     timingstate = timings_began;
0475 
0476     int offset = 0;
0477     bool stop = true;
0478     for (DurationItem *dur = durations + (int)BeginTime; dur; dur = dur->next)
0479         switch (dur->durval) {
0480         case DurStart: { // check started/finished
0481             Node *sender = dur->connection.signaler ();
0482             if (sender && sender->state >= Node::state_began) {
0483                 offset = dur->offset;
0484                 Runtime *rt = (Runtime*)sender->role (RoleTiming);
0485                 if (rt)
0486                     offset -= element->document()->last_event_time/10 - rt->start_time;
0487                 stop = false;
0488                 qCWarning(LOG_KMPLAYER_COMMON) << "start trigger on started element";
0489             } // else wait for start event
0490             break;
0491         }
0492         case DurEnd: { // check finished
0493             Node *sender = dur->connection.signaler ();
0494             if (sender && sender->state >= Node::state_finished) {
0495                 int offset = dur->offset;
0496                 Runtime *rt = (Runtime*)sender->role (RoleTiming);
0497                 if (rt)
0498                     offset -= element->document()->last_event_time/10 - rt->finish_time;
0499                 stop = false;
0500                 qCWarning(LOG_KMPLAYER_COMMON) << "start trigger on finished element";
0501             } // else wait for end event
0502             break;
0503         }
0504         case DurTimer:
0505             offset = dur->offset;
0506             stop = false;
0507             break;
0508         default:
0509             break;
0510     }
0511     if (stop)   // wait for event
0512         tryFinish ();
0513     else if (offset > 0)               // start timer
0514         begin_timer = element->document ()->post (element,
0515                 new TimerPosting (10 * offset, begin_timer_id));
0516     else                               // start now
0517         propagateStart ();
0518 }
0519 
0520 void Runtime::finish () {
0521     if (started () || timingstate == timings_began) {
0522         doFinish (); // reschedule through Runtime::stopped
0523     } else {
0524         finish_time = element->document ()->last_event_time/10;
0525         repeat_count = repeat;
0526         NodePtrW guard = element;
0527         element->Node::finish ();
0528         if (guard && element->document ()->active ()) { // check for reset
0529             Posting event (element, MsgEventStopped);
0530             element->deliver (MsgEventStopped, &event);
0531         }
0532     }
0533 }
0534 
0535 void Runtime::startAndBeginNode () {
0536     if (begin_timer || duration_timer)
0537         element->init ();
0538     timingstate = timings_began;
0539     propagateStart ();
0540 }
0541 
0542 bool Runtime::parseParam (const TrieString & name, const QString & val) {
0543     if (name == Ids::attr_begin) {
0544         setDurationItems (element, val, durations + (int) BeginTime);
0545         if ((timingstate == timings_began && !begin_timer) ||
0546                 timingstate >= timings_stopped) {
0547             if (beginTime ().offset > 0) { // create a timer for start
0548                 if (begin_timer) {
0549                     element->document ()->cancelPosting (begin_timer);
0550                     begin_timer = nullptr;
0551                 }
0552                 if (beginTime ().durval == DurTimer)
0553                     begin_timer = element->document ()->post (element,
0554                             new TimerPosting (10 * beginTime ().offset, begin_timer_id));
0555             } else {                              // start now
0556                 propagateStart ();
0557             }
0558         }
0559     } else if (name == Ids::attr_dur) {
0560         setDurationItems (element, val, durations + (int) DurTime);
0561     } else if (name == Ids::attr_end) {
0562         setDurationItems (element, val, durations + (int) EndTime);
0563     } else if (name.startsWith (Ids::attr_fill)) {
0564         Fill * f = &fill;
0565         if (name != Ids::attr_fill) {
0566             f = &fill_def;
0567             *f = fill_inherit;
0568         } else
0569             *f = fill_default;
0570         fill_active = fill_auto;
0571         if (val == "freeze")
0572             *f = fill_freeze;
0573         else if (val == "hold")
0574             *f = fill_hold;
0575         else if (val == "auto")
0576             *f = fill_auto;
0577         else if (val == "remove")
0578             *f = fill_remove;
0579         else if (val == "transition")
0580             *f = fill_transition;
0581         if (fill == fill_default) {
0582             if (fill_def == fill_inherit)
0583                 fill_active = getDefaultFill (element);
0584             else
0585                 fill_active = fill_def;
0586         } else
0587             fill_active = fill;
0588     } else if (name == Ids::attr_title) {
0589         Mrl *mrl = element->mrl ();
0590         if (mrl)
0591             mrl->title = val;
0592     } else if (name == "endsync") {
0593         if ((durTime ().durval == DurMedia || durTime ().durval == 0) &&
0594                 endTime ().durval == DurMedia) {
0595             Node *e = findLocalNodeById (element, val);
0596             if (e) {
0597                 durations [(int) EndTime].connection.connect (
0598                     e, MsgEventStopped, element);
0599                 durations [(int) EndTime].durval = (Duration) MsgEventStopped;
0600             }
0601         }
0602     } else if (name.startsWith ("repeat")) {
0603         if (val.indexOf ("indefinite") > -1)
0604             repeat = repeat_count = DurIndefinite;
0605         else
0606             repeat = repeat_count = val.toInt ();
0607     } else if (name.startsWith ("expr")) {
0608         expr = val;
0609     } else // TODO restart/restartDefault
0610         return false;
0611     return true;
0612 }
0613 
0614 void Runtime::message (MessageType msg, void *content) {
0615     switch (msg) {
0616         case MsgEventTimer: {
0617             TimerPosting *te = static_cast <TimerPosting *> (content);
0618             if (te->event_id == begin_timer_id) {
0619                 begin_timer = nullptr;
0620                 propagateStart ();
0621             } else if (te->event_id == dur_timer_id) {
0622                 duration_timer = nullptr;
0623                 doFinish ();
0624             } else {
0625                 qCWarning(LOG_KMPLAYER_COMMON) << "unhandled timer event";
0626             }
0627             return;
0628         }
0629         case MsgEventStarted: {
0630             Posting *event = static_cast <Posting *> (content);
0631             if (event->source.ptr () == element) {
0632                 started_timer = nullptr;
0633                 start_time = element->document ()->last_event_time/10;
0634                 setDuration ();
0635                 NodePtrW guard = element;
0636                 element->deliver (MsgEventStarted, event);
0637                 if (guard) {
0638                     element->begin ();
0639                     if (!element->document ()->postponed ())
0640                         tryFinish ();
0641                 }
0642                 return;
0643             }
0644             break;
0645         }
0646         case MsgEventStopped: {
0647             Posting *event = static_cast <Posting *> (content);
0648             if (event->source.ptr () == element) {
0649                 stopped_timer = nullptr;
0650                 stopped ();
0651                 return;
0652             }
0653             break;
0654         }
0655         default:
0656             break;
0657     }
0658     if ((int) msg >= (int) DurLastDuration)
0659         return;
0660 
0661     Posting *event = static_cast <Posting *> (content);
0662     for (DurationItem *dur = beginTime ().next; dur; dur = dur->next) {
0663         if (dur->matches ((Duration) msg, event)) {
0664             if (started ())
0665                 element->message (MsgStateRewind);
0666             else
0667                 element->activate ();
0668             if (element && dur->offset > 0) {
0669                 if (begin_timer)
0670                     element->document ()->cancelPosting (begin_timer);
0671                 begin_timer = element->document ()->post (element,
0672                         new TimerPosting(10 * dur->offset, begin_timer_id));
0673             } else { //FIXME neg. offsets
0674                 propagateStart ();
0675             }
0676             if (element->state == Node::state_finished)
0677                 element->state = Node::state_activated;//rewind to activated
0678             return;
0679         }
0680     }
0681     if (started ()) {
0682         Posting *event = static_cast <Posting *> (content);
0683         for (DurationItem *dur = endTime ().next; dur; dur = dur->next)
0684             if (dur->matches ((Duration) msg, event)) {
0685                 if (element && dur->offset > 0) {
0686                     if (duration_timer)
0687                         element->document ()->cancelPosting (duration_timer);
0688                     duration_timer = element->document ()->post (element,
0689                             new TimerPosting (10 * dur->offset, dur_timer_id));
0690                 } else {
0691                     doFinish ();
0692                 }
0693                 break;
0694             }
0695     }
0696 }
0697 
0698 void *Runtime::role (RoleType msg, void *content) {
0699     switch (msg) {
0700     case RoleReceivers: {
0701         switch ((MessageType) (long) content) {
0702         case MsgEventStopped:
0703             return &m_StoppedListeners;
0704         case MsgEventStarted:
0705             return &m_StartedListeners;
0706         case MsgEventStarting:
0707             return &m_StartListeners;
0708         case MsgChildTransformedIn:
0709             break;
0710         default:
0711             qCWarning(LOG_KMPLAYER_COMMON) << "unknown event requested " << (int)msg;
0712         }
0713         return nullptr;
0714     }
0715     default:
0716         break;
0717     }
0718     return MsgUnhandled;
0719 }
0720 
0721 void Runtime::propagateStop (bool forced) {
0722     if (state() == TimingsInit || state() >= timings_stopped)
0723         return; // nothing to stop
0724     if (!forced) {
0725         if ((durTime ().durval == DurMedia ||
0726                     durTime ().durval == DurTransition ) &&
0727                 endTime ().durval == DurMedia)
0728             return; // wait for external eof
0729         if (endTime ().durval != DurTimer && endTime ().durval != DurMedia &&
0730                 (started () || beginTime().durval == DurTimer))
0731             return; // wait for event
0732         if (durTime ().durval == DurIndefinite)
0733             return; // this may take a while :-)
0734         if (duration_timer)
0735             return; // timerEvent will call us with forced=true
0736         // bail out if a child still running
0737         for (Node *c = element->firstChild (); c; c = c->nextSibling ())
0738             if (c->unfinished () || Node::state_deferred == c->state)
0739                 return; // a child still running
0740     }
0741     bool was_started (started ());
0742     timingstate = timings_freezed;
0743     if (begin_timer) {
0744         element->document ()->cancelPosting (begin_timer);
0745         begin_timer = nullptr;
0746     }
0747     if (duration_timer) {
0748         element->document ()->cancelPosting (duration_timer);
0749         duration_timer = nullptr;
0750     }
0751     if (was_started && element->document ()->active ())
0752         stopped_timer = element->document()->post (
0753                 element, new Posting (element, MsgEventStopped));
0754     else if (element->unfinished ())
0755         element->finish ();
0756 }
0757 
0758 void Runtime::propagateStart () {
0759     if (begin_timer) {
0760         element->document ()->cancelPosting (begin_timer);
0761         begin_timer = nullptr;
0762     }
0763     if (disabledByExpr (this)) {
0764         if (timings_freezed == timingstate)
0765             element->message (MsgStateFreeze);
0766         timingstate = TimingsDisabled;
0767         return;
0768     }
0769     timingstate = trans_in_dur ? TimingsTransIn : timings_started;
0770     element->deliver (MsgEventStarting, element);
0771     started_timer = element->document()->post (
0772             element, new Posting (element, MsgEventStarted));
0773 }
0774 
0775 /**
0776  * begin_timer timer expired
0777  */
0778 void Runtime::setDuration () {
0779     if (begin_timer) {
0780         element->document ()->cancelPosting (begin_timer);
0781         begin_timer = nullptr;
0782     }
0783     if (duration_timer) {
0784         element->document ()->cancelPosting (duration_timer);
0785         duration_timer = nullptr;
0786     }
0787     int duration = 0;
0788     if (durTime ().durval == DurTimer) {
0789         duration = durTime ().offset;
0790         if (endTime ().durval == DurTimer &&
0791                 (!duration || endTime().offset - beginTime().offset < duration))
0792             duration = endTime ().offset - beginTime ().offset;
0793     } else if (endTime ().durval == DurTimer) {
0794         duration = endTime ().offset;
0795     }
0796     if (duration > 0)
0797         duration_timer = element->document ()->post (element,
0798                 new TimerPosting (10 * duration, dur_timer_id));
0799 }
0800 
0801 bool Runtime::started () const {
0802     return timingstate >= timings_started && timingstate < timings_stopped;
0803 }
0804 
0805 static bool runtimeBegan (Runtime *r) {
0806     return r->timingstate >= Runtime::timings_began &&
0807         r->timingstate < Runtime::timings_stopped;
0808 }
0809 
0810 /**
0811  * duration_timer timer expired or no duration set after started
0812  */
0813 void Runtime::stopped () {
0814     if (element->active ()) {
0815         if (repeat_count == DurIndefinite || 0 < --repeat_count) {
0816             element->message (MsgStateRewind);
0817             beginTime ().offset  = 0;
0818             beginTime ().durval = DurTimer;
0819             if (begin_timer)
0820                 element->document ()->cancelPosting (begin_timer);
0821             propagateStart ();
0822         } else {
0823             repeat_count = repeat;
0824             element->finish ();
0825         }
0826     }
0827 }
0828 
0829 //-----------------------------------------------------------------------------
0830 
0831 SizeType::SizeType () {
0832     reset ();
0833 }
0834 
0835 SizeType::SizeType (const QString & s, bool perc)
0836  : perc_size (perc ? -100 : 0) {
0837     *this = s;
0838 }
0839 
0840 void SizeType::reset () {
0841     perc_size = 0;
0842     abs_size = 0;
0843     isset = false;
0844     has_percentage = false;
0845 }
0846 
0847 SizeType & SizeType::operator = (const QString & s) {
0848     QString strval (s);
0849     int p = strval.indexOf (QChar ('%'));
0850     if (p > -1) {
0851         strval.truncate (p);
0852         has_percentage = true;
0853     }
0854     int px = strval.indexOf (QChar ('p')); // strip px
0855     if (px > -1)
0856         strval.truncate (px);
0857     double d = strval.toDouble (&isset);
0858     if (isset) {
0859         if (p > -1)
0860             perc_size = d;
0861         else if (perc_size < 0)
0862             perc_size = 100 * d;
0863         else
0864             abs_size = d;
0865     }
0866     return *this;
0867 }
0868 
0869 SizeType & SizeType::operator = (Single d) {
0870     reset ();
0871     abs_size = d;
0872     return *this;
0873 }
0874 
0875 SizeType & SizeType::operator += (const SizeType & s) {
0876     perc_size += s.perc_size;
0877     abs_size += s.abs_size;
0878     return *this;
0879 }
0880 
0881 SizeType & SizeType::operator -= (const SizeType & s) {
0882     perc_size -= s.perc_size;
0883     abs_size -= s.abs_size;
0884     return *this;
0885 }
0886 
0887 Single SizeType::size (Single relative_to) const {
0888     Single s = abs_size;
0889     s += perc_size * relative_to / 100;
0890     return s;
0891 }
0892 
0893 QString SizeType::toString () const {
0894     if (isset) {
0895         if (has_percentage)
0896             return QString ("%1%").arg ((int) size (100));
0897         return QString::number ((double) size (100));
0898     }
0899     return QString ();
0900 }
0901 
0902 //-----------------%<----------------------------------------------------------
0903 
0904 template<>
0905 IRect IRect::intersect (const IRect & r) const {
0906     int a (point.x < r.point.x ? r.point.x : point.x);
0907     int b (point.y < r.point.y ? r.point.y : point.y);
0908     return IRect (a, b,
0909             ((point.x + size.width < r.point.x + r.size.width)
0910              ? point.x + size.width : r.point.x + r.size.width) - a,
0911             ((point.y + size.height < r.point.y + r.size.height)
0912              ? point.y + size.height : r.point.y + r.size.height) - b);
0913 }
0914 
0915 //-----------------------------------------------------------------------------
0916 
0917 void CalculatedSizer::resetSizes () {
0918     left.reset ();
0919     top.reset ();
0920     width.reset ();
0921     height.reset ();
0922     right.reset ();
0923     bottom.reset ();
0924     reg_point.truncate (0);
0925     reg_align = QString::fromLatin1 ("topLeft");
0926 }
0927 
0928 static bool regPoints (const QString & str, Single & x, Single & y) {
0929     QByteArray ba = str.toLower ().toLatin1 ();
0930     const char *rp = ba.constData ();
0931     if (!rp)
0932         return false;
0933     if (!strcmp (rp, "center")) {
0934         x = 50;
0935         y = 50;
0936     } else {
0937         if (!strncmp (rp, "top", 3)) {
0938             y = 0;
0939             rp += 3;
0940         } else if (!strncmp (rp, "mid", 3)) {
0941             y = 50;
0942             rp += 3;
0943         } else if (!strncmp (rp, "bottom", 6)) {
0944             y = 100;
0945             rp += 6;
0946         } else
0947             return false;
0948         if (!strcmp (rp, "left")) {
0949             x = 0;
0950         } else if (!strcmp (rp, "mid")) {
0951             x = 50;
0952         } else if (!strcmp (rp, "right")) {
0953             x = 100;
0954         } else
0955             return false;
0956     }
0957     return true;
0958 }
0959 
0960 bool CalculatedSizer::applyRegPoints (Node * node,
0961         CalculatedSizer *region_sizes, Single w, Single h,
0962         Single & xoff, Single & yoff, Single & w1, Single & h1) {
0963     QString rp = reg_point;
0964     if (rp.isEmpty () && region_sizes)
0965         rp = region_sizes->reg_point;
0966     if (rp.isEmpty ())
0967         return false;
0968     Single rpx, rpy, rax, ray;
0969     if (!regPoints (rp, rpx, rpy)) {
0970         node = SMIL::Smil::findSmilNode (node);
0971         if (!node)
0972             return false;
0973         node = static_cast <SMIL::Smil *> (node)->layout_node.ptr ();
0974         if (!node)
0975             return false;
0976         Node *c = node->firstChild ();
0977         for (; c; c = c->nextSibling ())
0978             if (c->id == SMIL::id_node_regpoint &&
0979                     static_cast<Element*>(c)->getAttribute (Ids::attr_id)
0980                         == rp) {
0981                 Single i1, i2; // dummies
0982                 SMIL::RegPoint *rp_elm = static_cast <SMIL::RegPoint *> (c);
0983                 rp_elm->sizes.calcSizes (nullptr, nullptr, 100, 100, rpx, rpy, i1, i2);
0984                 QString ra = rp_elm->getAttribute ("regAlign");
0985                 if (!ra.isEmpty () && reg_align.isEmpty ())
0986                     reg_align = ra;
0987                 break;
0988             }
0989         if (!c)
0990             return false; // not found
0991     }
0992     QString ra = reg_align;
0993     if (ra.isEmpty () && region_sizes)
0994         ra = region_sizes->reg_align;
0995     if (!regPoints (ra, rax, ray))
0996         rax = ray = 0; // default back to topLeft
0997     if (!(int)w1 || !(int)h1) {
0998         xoff = w * (rpx - rax) / 100;
0999         yoff = h * (rpy - ray) / 100;
1000         w1 = w - w * (rpx > rax ? (rpx - rax) : (rax - rpx)) / 100;
1001         h1 = h - h * (rpy > ray ? (rpy - ray) : (ray - rpy)) / 100;
1002     } else {
1003         xoff = (w * rpx - w1 * rax) / 100;
1004         yoff = (h * rpy - h1 * ray) / 100;
1005     }
1006     return true; // success getting sizes based on regPoint
1007 }
1008 
1009 void CalculatedSizer::calcSizes (Node * node,
1010         CalculatedSizer *region_sz, Single w, Single h,
1011         Single & xoff, Single & yoff, Single & w1, Single & h1) {
1012     if (region_sz && applyRegPoints (node, region_sz, w, h, xoff, yoff, w1, h1))
1013         return;
1014     if (left.isSet ())
1015         xoff = left.size (w);
1016     else if (width.isSet ()) {
1017         if (right.isSet ())
1018             xoff = w - width.size (w) - right.size (w);
1019         else
1020             xoff = 0;
1021     } else
1022         xoff = 0;
1023     if (top.isSet ())
1024         yoff = top.size (h);
1025     else if (height.isSet ()) {
1026         if (bottom.isSet ())
1027             yoff = h - height.size (h) - bottom.size (h);
1028         else
1029             yoff = 0;
1030     } else
1031         yoff = 0;
1032     if (width.isSet ())
1033         w1 = width.size (w);
1034     else if (right.isSet ())
1035         w1 = w - xoff - right.size (w);
1036     else
1037         w1 = w - xoff;
1038     if (w1 < 0)
1039         w1 = 0;
1040     if (height.isSet ())
1041         h1 = height.size (h);
1042     else if (bottom.isSet ())
1043         h1 = h - yoff - bottom.size (h);
1044     else
1045         h1 = h - yoff;
1046     if (h1 < 0)
1047         h1 = 0;
1048 }
1049 
1050 bool CalculatedSizer::setSizeParam(const TrieString &name, const QString &val) {
1051     if (name == Ids::attr_left) {
1052         left = val;
1053     } else if (name == Ids::attr_top) {
1054         top = val;
1055     } else if (name == Ids::attr_width) {
1056         width = val;
1057     } else if (name == Ids::attr_height) {
1058         height = val;
1059     } else if (name == Ids::attr_right) {
1060         right = val;
1061     } else if (name == Ids::attr_bottom) {
1062         bottom = val;
1063     } else if (name == "regPoint") {
1064         reg_point = val;
1065     } else if (name == "regAlign") {
1066         reg_align = val;
1067     } else if (name == "mediaAlign") {
1068         reg_point = val;
1069         reg_align = val;
1070     } else
1071         return false;
1072     return true;
1073 }
1074 
1075 void
1076 CalculatedSizer::move (const SizeType &x, const SizeType &y) {
1077     if (left.isSet ()) {
1078         if (right.isSet ()) {
1079             right += x;
1080             right -= left;
1081         }
1082         left = x;
1083     } else if (right.isSet ()) {
1084         right = x;
1085     } else {
1086         left = x;
1087     }
1088     if (top.isSet ()) {
1089         if (bottom.isSet ()) {
1090             bottom += y;
1091             bottom -= top;
1092         }
1093         top = y;
1094     } else if (bottom.isSet ()) {
1095             bottom = y;
1096     } else {
1097         top = y;
1098     }
1099 }
1100 
1101 //-----------------------------------------------------------------------------
1102 
1103 MouseListeners::MouseListeners () {}
1104 
1105 ConnectionList *MouseListeners::receivers (MessageType eid) {
1106     switch (eid) {
1107         case MsgEventClicked:
1108             return &m_ActionListeners;
1109         case MsgEventPointerInBounds:
1110             return &m_InBoundsListeners;
1111         case MsgEventPointerOutBounds:
1112             return &m_OutOfBoundsListeners;
1113         default:
1114             break;
1115     }
1116     return nullptr;
1117 }
1118 
1119 //-----------------------------------------------------------------------------
1120 
1121 static Element * fromScheduleGroup (NodePtr & d, const QString & tag) {
1122     QByteArray ba = tag.toLatin1 ();
1123     const char *ctag = ba.constData ();
1124     if (!strcmp (ctag, "par"))
1125         return new SMIL::Par (d);
1126     else if (!strcmp (ctag, "seq"))
1127         return new SMIL::Seq (d);
1128     else if (!strcmp (ctag, "excl"))
1129         return new SMIL::Excl (d);
1130     return nullptr;
1131 }
1132 
1133 static Element * fromParamGroup (NodePtr & d, const QString & tag) {
1134     QByteArray ba = tag.toLatin1 ();
1135     const char *ctag = ba.constData ();
1136     if (!strcmp (ctag, "param"))
1137         return new SMIL::Param (d);
1138     else if (!strcmp (ctag, "area") || !strcmp (ctag, "anchor"))
1139         return new SMIL::Area (d, tag);
1140     return nullptr;
1141 }
1142 
1143 static Element * fromAnimateGroup (NodePtr & d, const QString & tag) {
1144     QByteArray ba = tag.toLatin1 ();
1145     const char *ctag = ba.constData ();
1146     if (!strcmp (ctag, "set"))
1147         return new SMIL::Set (d);
1148     else if (!strcmp (ctag, "animate"))
1149         return new SMIL::Animate (d);
1150     else if (!strcmp (ctag, "animateColor"))
1151         return new SMIL::AnimateColor (d);
1152     else if (!strcmp (ctag, "animateMotion"))
1153         return new SMIL::AnimateMotion (d);
1154     else if (!strcmp (ctag, "newvalue"))
1155         return new SMIL::NewValue (d);
1156     else if (!strcmp (ctag, "setvalue"))
1157         return new SMIL::SetValue (d);
1158     else if (!strcmp (ctag, "delvalue"))
1159         return new SMIL::DelValue (d);
1160     else if (!strcmp (ctag, "send"))
1161         return new SMIL::Send (d);
1162     return nullptr;
1163 }
1164 
1165 static Element * fromMediaContentGroup (NodePtr & d, const QString & tag) {
1166     QByteArray ba = tag.toLatin1 ();
1167     const char *taglatin = ba.constData ();
1168     if (!strcmp (taglatin, "video") ||
1169             !strcmp (taglatin, "audio") ||
1170             !strcmp (taglatin, "img") ||
1171             !strcmp (taglatin, "animation") ||
1172             !strcmp (taglatin, "textstream") ||
1173             !strcmp (taglatin, "ref"))
1174         return new SMIL::RefMediaType (d, ba);
1175     else if (!strcmp (taglatin, "text"))
1176         return new SMIL::TextMediaType (d);
1177     else if (!strcmp (taglatin, "brush"))
1178         return new SMIL::Brush (d);
1179     else if (!strcmp (taglatin, "a"))
1180         return new SMIL::Anchor (d);
1181     else if (!strcmp (taglatin, "smilText"))
1182         return new SMIL::SmilText (d);
1183     // animation, textstream
1184     return nullptr;
1185 }
1186 
1187 static Element * fromContentControlGroup (NodePtr & d, const QString & tag) {
1188     if (!strcmp (tag.toLatin1 ().constData (), "switch"))
1189         return new SMIL::Switch (d);
1190     return nullptr;
1191 }
1192 
1193 static Element *fromTextFlowGroup (NodePtr &d, const QString &tag) {
1194     QByteArray ba = tag.toLatin1 ();
1195     const char *taglatin = ba.constData ();
1196     if (!strcmp (taglatin, "div"))
1197         return new SMIL::TextFlow (d, SMIL::id_node_div, tag.toUtf8 ());
1198     if (!strcmp (taglatin, "span"))
1199         return new SMIL::TextFlow (d, SMIL::id_node_span, tag.toUtf8 ());
1200     if (!strcmp (taglatin, "p"))
1201         return new SMIL::TextFlow (d, SMIL::id_node_p, tag.toUtf8 ());
1202     if (!strcmp (taglatin, "br"))
1203         return new SMIL::TextFlow (d, SMIL::id_node_br, tag.toUtf8 ());
1204     return nullptr;
1205 }
1206 
1207 static unsigned int setRGBA (unsigned int color, int opacity) {
1208     int a = ((color >> 24) & 0xff) * opacity / 100;
1209     return (a << 24) | (color & 0xffffff);
1210 }
1211 
1212 void SmilColorProperty::init ()
1213 {
1214     color = 0;
1215     opacity = 100;
1216 }
1217 
1218 static unsigned int rgbFromValue (const QString& val) {
1219     SmilColorProperty p;
1220     p.init();
1221     p.setColor (val);
1222     return 0xffffff & p.color;
1223 }
1224 
1225 void SmilColorProperty::setColor (const QString &val)
1226 {
1227     if (val.isEmpty () || val == "transparent")
1228         color = 0;
1229     else if (val.startsWith (QChar ('#')) && val.length() == 9)
1230         color = val.mid (1).toUInt (nullptr, 16);
1231     else
1232         color = setRGBA (QColor (val).rgba (), opacity);
1233 }
1234 
1235 void SmilColorProperty::setOpacity (const QString &val)
1236 {
1237     opacity = SizeType (val, true).size (100);
1238     color = setRGBA (color, opacity);
1239 }
1240 
1241 static bool parseBackgroundParam (SmilColorProperty &p, const TrieString &name, const QString &val)
1242 {
1243     if (name == "background-color" || name == "backgroundColor")
1244         p.setColor (val);
1245     else if (name == "backgroundOpacity")
1246         p.setOpacity (val);
1247     else
1248         return false;
1249     return true;
1250 }
1251 
1252 void MediaOpacity::init () {
1253     bg_opacity = opacity = 100;
1254 }
1255 
1256 static bool parseMediaOpacityParam (MediaOpacity &p, const TrieString &name, const QString &val) {
1257     if (name == "mediaOpacity")
1258         p.opacity = (int) SizeType (val, true).size (100);
1259     else if (name == "mediaBackgroundOpacity")
1260         p.bg_opacity = (int) SizeType (val, true).size (100);
1261     else
1262         return false;
1263     return true;
1264 }
1265 
1266 void TransitionModule::init () {
1267     trans_out_active = false;
1268     trans_start_time = 0;
1269 }
1270 
1271 void TransitionModule::cancelTimer (Node *n) {
1272     if (trans_out_timer) {
1273         n->document ()->cancelPosting (trans_out_timer);
1274         trans_out_timer = nullptr;
1275     }
1276 }
1277 
1278 void TransitionModule::begin (Node *n, Runtime *runtime)
1279 {
1280     SMIL::Transition *trans = convertNode <SMIL::Transition> (trans_in);
1281     if (trans && trans->supported ()) {
1282         active_trans = trans_in;
1283         runtime->timingstate = Runtime::TimingsTransIn;
1284         trans_gain = 0.0;
1285         transition_updater.connect (n->document (), MsgSurfaceUpdate, n);
1286         trans_start_time = n->document ()->last_event_time;
1287         trans_end_time = trans_start_time + 10 * trans->dur;
1288         if (Runtime::DurTimer == runtime->durTime ().durval &&
1289                 0 == runtime->durTime ().offset &&
1290                 Runtime::DurMedia == runtime->endTime ().durval)
1291             runtime->durTime ().durval = Runtime::DurTransition;
1292     }
1293     if (Runtime::DurTimer == runtime->durTime().durval &&
1294             runtime->durTime().offset > 0) {
1295         // FIXME: also account for fill duration
1296         trans = convertNode <SMIL::Transition> (trans_out);
1297         if (trans && trans->supported () &&
1298                 (int) trans->dur < runtime->durTime().offset)
1299             trans_out_timer = n->document()->post (n,
1300                     new TimerPosting ((runtime->durTime().offset - trans->dur) * 10,
1301                         trans_out_timer_id));
1302     }
1303 }
1304 
1305 bool TransitionModule::handleMessage (Node *n, Runtime *runtime, Surface *s,
1306         MessageType msg, void *content) {
1307     switch (msg) {
1308     case MsgSurfaceUpdate: {
1309         UpdateEvent *ue = static_cast <UpdateEvent *> (content);
1310 
1311         trans_start_time += ue->skipped_time;
1312         trans_end_time += ue->skipped_time;
1313 
1314         trans_gain = 1.0 * (ue->cur_event_time - trans_start_time) /
1315             (trans_end_time - trans_start_time);
1316         if (trans_gain > 0.9999) {
1317             transition_updater.disconnect ();
1318             if (active_trans == trans_in) {
1319                 runtime->timingstate = Runtime::timings_started;
1320                 n->deliver (MsgChildTransformedIn, n);
1321             }
1322             if (!trans_out_active)
1323                 active_trans = nullptr;
1324             trans_gain = 1.0;
1325             if (Runtime::DurTransition == runtime->durTime ().durval) {
1326                 runtime->durTime ().durval = Runtime::DurTimer;
1327                 runtime->tryFinish ();
1328             }
1329         }
1330         if (s && s->parentNode())
1331             s->parentNode()->repaint (s->bounds);
1332         return true;
1333     }
1334 
1335     case MsgEventTimer: {
1336         TimerPosting *te = static_cast <TimerPosting *> (content);
1337         if (te->event_id == trans_out_timer_id) {
1338             if (active_trans)
1339                 transition_updater.disconnect ();
1340             trans_out_timer = nullptr;
1341             active_trans = trans_out;
1342             SMIL::Transition *trans = convertNode<SMIL::Transition> (trans_out);
1343             if (trans) {
1344                 trans_gain = 0.0;
1345                 transition_updater.connect (n->document(), MsgSurfaceUpdate, n);
1346                 trans_start_time = n->document ()->last_event_time;
1347                 trans_end_time = trans_start_time + 10 * trans->dur;
1348                 trans_out_active = true;
1349                 if (s)
1350                     s->repaint ();
1351             }
1352             return true;
1353         }
1354         break;
1355     }
1356     default:
1357         break;
1358     }
1359     return false;
1360 }
1361 
1362 void TransitionModule::finish (Node *n) {
1363     transition_updater.disconnect ();
1364     cancelTimer (n);
1365 }
1366 
1367 //-----------------------------------------------------------------------------
1368 
1369 Node *SMIL::Smil::childFromTag (const QString & tag) {
1370     QByteArray ba = tag.toLatin1 ();
1371     const char *ctag = ba.constData ();
1372     if (!strcmp (ctag, "body"))
1373         return new SMIL::Body (m_doc);
1374     else if (!strcmp (ctag, "head"))
1375         return new SMIL::Head (m_doc);
1376     return nullptr;
1377 }
1378 
1379 void SMIL::Smil::activate () {
1380     resolved = true;
1381     if (layout_node)
1382         Element::activate ();
1383     else
1384         Element::deactivate(); // some unfortunate reset in parent doc
1385 }
1386 
1387 void SMIL::Smil::deactivate () {
1388     Mrl::deactivate ();
1389 }
1390 
1391 void SMIL::Smil::message (MessageType msg, void *content) {
1392     switch (msg) {
1393 
1394     case MsgChildFinished: {
1395         Posting *post = (Posting *) content;
1396         if (unfinished ()) {
1397             if (post->source->nextSibling ())
1398                 post->source->nextSibling ()->activate ();
1399             else {
1400                 for (NodePtr e = firstChild (); e; e = e->nextSibling ())
1401                     if (e->active ())
1402                         e->deactivate ();
1403                 finish ();
1404             }
1405         }
1406         break;
1407     }
1408 
1409     case MsgSurfaceBoundsUpdate: {
1410         Layout *layout = convertNode <SMIL::Layout> (layout_node);
1411         if (layout && layout->root_layout)
1412             layout->root_layout->message (msg, content);
1413         return;
1414     }
1415 
1416     default:
1417         Mrl::message (msg, content);
1418     }
1419 }
1420 
1421 void SMIL::Smil::closed () {
1422     Node *head = findHeadNode (this);
1423     if (!head) {
1424         head = new SMIL::Head (m_doc);
1425         insertBefore (head, firstChild ());
1426         head->setAuxiliaryNode (true);
1427         head->closed ();
1428     }
1429     for (Node *e = head->firstChild (); e; e = e->nextSibling ()) {
1430         if (e->id == id_node_layout) {
1431             layout_node = e;
1432         } else if (e->id == id_node_title) {
1433             QString str = e->innerText ();
1434             title = str.left (str.indexOf (QChar ('\n')));
1435         } else if (e->id == id_node_state) {
1436             state_node = e;
1437         } else if (e->id == id_node_meta) {
1438             Element *elm = static_cast <Element *> (e);
1439             const QString name = elm->getAttribute (Ids::attr_name);
1440             if (name == QString::fromLatin1 ("title"))
1441                 title = elm->getAttribute ("content");
1442             else if (name == QString::fromLatin1 ("base"))
1443                 src = elm->getAttribute ("content");
1444         }
1445     }
1446     Mrl::closed ();
1447 }
1448 
1449 void *SMIL::Smil::role (RoleType msg, void *content)
1450 {
1451     if (RolePlaylist == msg)
1452         return !title.isEmpty () || //return false if no title and only one
1453             previousSibling () || nextSibling () ? (PlaylistRole *) this : nullptr;
1454     return Mrl::role (msg, content);
1455 }
1456 
1457 void SMIL::Smil::jump (const QString & id) {
1458     Node *n = document ()->getElementById (this, id, false);
1459     if (n) {
1460         if (n->unfinished ())
1461             qCDebug(LOG_KMPLAYER_COMMON) << "Smil::jump node is unfinished " << id;
1462         else {
1463             for (Node *p = n; p; p = p->parentNode ()) {
1464                 if (p->unfinished () &&
1465                         p->id >= id_node_first_group &&
1466                         p->id <= id_node_last_group) {
1467                     static_cast <GroupBase *> (p)->setJumpNode (n);
1468                     break;
1469                 }
1470                 if (n->id == id_node_body || n->id == id_node_smil) {
1471                     qCCritical(LOG_KMPLAYER_COMMON) << "Smil::jump node passed body for " <<id<< endl;
1472                     break;
1473                 }
1474             }
1475         }
1476     }
1477 }
1478 
1479 SMIL::Smil * SMIL::Smil::findSmilNode (Node * node) {
1480     for (Node * e = node; e; e = e->parentNode ())
1481         if (e->id == SMIL::id_node_smil)
1482             return static_cast <SMIL::Smil *> (e);
1483     return nullptr;
1484 }
1485 
1486 static QString exprStringValue (Node *node, const QString &str) {
1487     Expression* res = evaluateExpr(str.toUtf8(), "data");
1488     if (res) {
1489         SMIL::Smil *smil = SMIL::Smil::findSmilNode (node);
1490         res->setRoot (smil ? smil->state_node.ptr() : nullptr);
1491         QString s = res->toString();
1492         delete res;
1493         return s;
1494     }
1495     return str;
1496 }
1497 
1498 static QString applySubstitution (Node *n, const QString &str, int p, int q) {
1499     QString s = exprStringValue (n, str.mid (p + 1, q - p - 1));
1500     return str.left (p) + s + str.mid (q + 1 );
1501 }
1502 
1503 //-----------------------------------------------------------------------------
1504 
1505 static void headChildDone (Node *node, Node *child) {
1506     if (node->unfinished ()) {
1507         if (child && child->nextSibling ())
1508             child->nextSibling ()->activate ();
1509         else
1510             node->finish (); // we're done
1511     }
1512 }
1513 
1514 Node *SMIL::Head::childFromTag (const QString & tag) {
1515     QByteArray ba = tag.toLatin1 ();
1516     const char *ctag = ba.constData ();
1517     if (!strcmp (ctag, "layout"))
1518         return new SMIL::Layout (m_doc);
1519     else if (!strcmp (ctag, "title"))
1520         return new DarkNode (m_doc, ctag, id_node_title);
1521     else if (!strcmp (ctag, "meta"))
1522         return new DarkNode (m_doc, ctag, id_node_meta);
1523     else if (!strcmp (ctag, "state"))
1524         return new SMIL::State (m_doc);
1525     else if (!strcmp (ctag, "transition"))
1526         return new SMIL::Transition (m_doc);
1527     return nullptr;
1528 }
1529 
1530 void SMIL::Head::closed () {
1531     for (Node *e = firstChild (); e; e = e->nextSibling ())
1532         if (e->id == id_node_layout)
1533             return;
1534     SMIL::Layout * layout = new SMIL::Layout (m_doc);
1535     appendChild (layout);
1536     layout->setAuxiliaryNode (true);
1537     layout->closed (); // add root-layout and a region
1538     Element::closed ();
1539 }
1540 
1541 void SMIL::Head::message (MessageType msg, void *content) {
1542     if (MsgChildFinished == msg) {
1543         headChildDone (this, ((Posting *) content)->source.ptr ());
1544         return;
1545     }
1546     Element::message (msg, content);
1547 }
1548 
1549 //-----------------------------------------------------------------------------
1550 
1551 SMIL::State::State (NodePtr &d)
1552  : Element (d, id_node_state), media_info (nullptr) {}
1553 
1554 Node *SMIL::State::childFromTag (const QString &tag) {
1555     if (tag == "data")
1556         return new DarkNode (m_doc, tag.toUtf8 (), SMIL::id_node_state_data);
1557     return nullptr;
1558 }
1559 
1560 void SMIL::State::closed () {
1561     if (!firstChild ()) {
1562         appendChild (new DarkNode (m_doc, "data", SMIL::id_node_state_data));
1563         firstChild ()->setAuxiliaryNode (true);
1564     }
1565 }
1566 
1567 void SMIL::State::activate () {
1568     init ();
1569     Element::activate ();
1570 }
1571 
1572 void SMIL::State::parseParam (const TrieString &name, const QString &val) {
1573     if (name == Ids::attr_src) {
1574         Smil *s = val.isEmpty () ? nullptr : SMIL::Smil::findSmilNode (this);
1575         if (s) {
1576             m_url.clear ();
1577             if (!media_info)
1578                 media_info = new MediaInfo (this, MediaManager::Text);
1579             Mrl *mrl = s->parentNode () ? s->parentNode ()->mrl () : nullptr;
1580             QString url = mrl ? QUrl(mrl->absolutePath()).resolved(QUrl(val)).url() : val;
1581             postpone_lock = document ()->postpone ();
1582             media_info->wget (url, domain ());
1583             m_url = url;
1584         }
1585     } else {
1586         Element::parseParam (name, val);
1587     }
1588 }
1589 
1590 void SMIL::State::deactivate () {
1591     delete media_info;
1592     media_info = nullptr;
1593     postpone_lock = nullptr;
1594     Element::deactivate ();
1595     m_url.clear ();
1596 }
1597 
1598 QString SMIL::State::domain () {
1599     QString s = m_url;
1600     if (s.isEmpty ()) {
1601         for (Node *p = parentNode (); p; p = p->parentNode ()) {
1602             Mrl *m = p->mrl ();
1603             if (m && !m->src.isEmpty () && m->src != "Playlist://") {
1604                 s = m->absolutePath ();
1605                 break;
1606             }
1607         }
1608     }
1609     const QUrl url = QUrl::fromUserInput(s);
1610     if (url.isLocalFile ())
1611         return QString ();
1612     return url.scheme() + "://" + url.host ();
1613 }
1614 
1615 void SMIL::State::stateChanged (Node *ref) {
1616     Connection *c = m_StateChangeListeners.first ();
1617     for (; c; c = m_StateChangeListeners.next ()) {
1618         if (c->payload && c->connecter) {
1619             Expression *expr = (Expression *) c->payload;
1620             expr->setRoot (this);
1621             Expression::iterator it, e = expr->end();
1622             for (it = expr->begin(); it != e; ++it) {
1623                 if (it->node == ref)
1624                     document()->post (c->connecter,
1625                                      new Posting (this, MsgStateChanged, expr));
1626             }
1627         }
1628     }
1629 }
1630 
1631 void SMIL::State::setValue (Node *ref, const QString &value) {
1632     const QString old = ref->nodeValue ();
1633     const QString s = exprStringValue (this, value);
1634     ref->clearChildren ();
1635     if (!s.isEmpty ())
1636         ref->appendChild (new TextNode (m_doc, s));
1637     if (s != old)
1638         stateChanged (ref);
1639 }
1640 
1641 void SMIL::State::newValue (Node *ref, Where where,
1642         const QString &name, const QString &value) {
1643     NodePtr n = new DarkNode (m_doc, name.toUtf8 ());
1644     switch (where) {
1645         case before:
1646             ref->parentNode ()->insertBefore (n, ref);
1647             break;
1648         case after:
1649             ref->parentNode ()->insertBefore (n, ref->nextSibling ());
1650             break;
1651         default:
1652             ref->appendChild (n);
1653     }
1654     if (!value.isEmpty ()) {
1655         const QString s = exprStringValue (this, value);
1656         n->appendChild (new TextNode (m_doc, s));
1657         stateChanged (ref);
1658     }
1659 }
1660 
1661 void SMIL::State::message (MessageType msg, void *content) {
1662     switch (msg) {
1663 
1664     case MsgMediaReady:
1665         if (media_info && media_info->media) {
1666             if (firstChild ())
1667                 removeChild (firstChild ());
1668             QTextStream in (&((TextMedia *)media_info->media)->text);
1669             readXML (this, in, QString ());
1670             if (firstChild ())
1671                 stateChanged (firstChild ());
1672         }
1673         delete media_info;
1674         media_info = nullptr;
1675         postpone_lock = nullptr;
1676         return;
1677 
1678     default:
1679         break;
1680     }
1681     Element::message (msg, content);
1682 }
1683 
1684 void *SMIL::State::role (RoleType msg, void *content) {
1685     if (MsgStateChanged == (MessageType) (long) content)
1686         return &m_StateChangeListeners;
1687     return Element::role (msg, content);
1688 }
1689 
1690 //-----------------------------------------------------------------------------
1691 
1692 SMIL::Layout::Layout (NodePtr & d)
1693  : Element (d, id_node_layout) {}
1694 
1695 Node *SMIL::Layout::childFromTag (const QString & tag) {
1696     QByteArray ba = tag.toLatin1 ();
1697     const char *ctag = ba.constData ();
1698     if (!strcmp (ctag, "root-layout")) {
1699         Node *e = new SMIL::RootLayout (m_doc);
1700         root_layout = e;
1701         return e;
1702     } else if (!strcmp (ctag, "region"))
1703         return new SMIL::Region (m_doc);
1704     else if (!strcmp (ctag, "regPoint"))
1705         return new SMIL::RegPoint (m_doc);
1706     return nullptr;
1707 }
1708 
1709 void SMIL::Layout::closed () {
1710     if (!root_layout) { // just add one if none there
1711         root_layout = new SMIL::RootLayout (m_doc);
1712         root_layout->setAuxiliaryNode (true);
1713         insertBefore (root_layout, firstChild ());
1714         root_layout->closed ();
1715     } else if (root_layout.ptr () != firstChild ()) {
1716         NodePtr rl = root_layout;
1717         removeChild (root_layout);
1718         insertBefore (root_layout, firstChild ());
1719     }
1720     Element::closed ();
1721 }
1722 
1723 void SMIL::Layout::message (MessageType msg, void *content) {
1724     if (MsgChildFinished == msg) {
1725         headChildDone (this, ((Posting *) content)->source.ptr ());
1726         if (state_finished == state && root_layout)
1727             root_layout->message (MsgSurfaceBoundsUpdate, (void *) true);
1728         return;
1729     }
1730     Element::message (msg, content);
1731 }
1732 
1733 //-----------------------------------------------------------------------------
1734 
1735 SMIL::RegionBase::RegionBase (NodePtr & d, short id)
1736  : Element (d, id),
1737    media_info (nullptr),
1738    z_order (0)
1739 {}
1740 
1741 SMIL::RegionBase::~RegionBase () {
1742     if (region_surface)
1743         region_surface->remove ();
1744 }
1745 
1746 void SMIL::RegionBase::activate () {
1747     show_background = ShowAlways;
1748     background_color.init ();
1749     media_opacity.init ();
1750     bg_repeat = BgRepeat;
1751     fit = fit_default;
1752     Node *n = parentNode ();
1753     if (n && SMIL::id_node_layout == n->id)
1754         n = n->firstChild ();
1755     state = state_deferred;
1756     role (RoleDisplay);
1757     font_props.init ();
1758     init ();
1759     Element::activate ();
1760 }
1761 
1762 void SMIL::RegionBase::deactivate () {
1763     show_background = ShowAlways;
1764     background_color.init ();
1765     background_image.truncate (0);
1766     if (media_info) {
1767         delete media_info;
1768         media_info = nullptr;
1769     }
1770     postpone_lock = nullptr;
1771     sizes.resetSizes ();
1772     Element::deactivate ();
1773 }
1774 
1775 void SMIL::RegionBase::dataArrived () {
1776     ImageMedia *im = media_info ? (ImageMedia *)media_info->media : nullptr;
1777     if (im && !im->isEmpty () && region_surface) {
1778         region_surface->markDirty ();
1779         region_surface->repaint ();
1780     }
1781     postpone_lock = nullptr;
1782 }
1783 
1784 void SMIL::RegionBase::repaint () {
1785     Surface *s = (Surface *) role (RoleDisplay);
1786     if (s)
1787         s->repaint ();
1788 }
1789 
1790 void SMIL::RegionBase::repaint (const SRect & rect) {
1791     Surface *s = (Surface *) role (RoleDisplay);
1792     if (s)
1793         s->repaint (SRect (0, 0, s->bounds.size).intersect (rect));
1794 }
1795 
1796 static void updateSurfaceSort (SMIL::RegionBase *rb) {
1797     Surface *rs = rb->region_surface.ptr ();
1798     Surface *prs = rs->parentNode ();
1799     Surface *next = nullptr;
1800     if (!prs)
1801         return;
1802     for (Surface *s = prs->firstChild (); s; s = s->nextSibling ())
1803         if (s != rs && s->node) {
1804             if (SMIL::id_node_region == s->node->id) {
1805                 SMIL::Region *r = static_cast <SMIL::Region *> (s->node.ptr ());
1806                 if (r->z_order > rb->z_order) {
1807                     next = s;
1808                     break;
1809                 } else if (r->z_order == rb->z_order) {
1810                     next = s;
1811                     // now take region order into account
1812                     Node *n = rb->previousSibling();
1813                     for (; n; n = n->previousSibling())
1814                         if (n->id == SMIL::id_node_region) {
1815                             r = static_cast <SMIL::Region *> (n);
1816                             if (r->region_surface && r->z_order == rb->z_order) {
1817                                 next = r->region_surface->nextSibling ();
1818                                 if (rs == next)
1819                                     next = next->nextSibling ();
1820                                 break;
1821                             }
1822                         }
1823                     break;
1824                 }
1825             } else if (SMIL::id_node_root_layout != s->node->id) {
1826                 // break at attached media types
1827                 Surface *m = (Surface *) s->node->role (RoleDisplay);
1828                 if (m) {
1829                     next = m;
1830                     break;
1831                 }
1832             }
1833         }
1834     if (rs->nextSibling () == next) {
1835         return;
1836     }
1837     SurfacePtr protect (rs);
1838     prs->removeChild (rs);
1839     prs->insertBefore (rs, next);
1840 }
1841 
1842 void SMIL::RegionBase::parseParam (const TrieString & name, const QString & val) {
1843     bool need_repaint = false;
1844     if (name == Ids::attr_fit) {
1845         Fit ft = parseFit (val.toLatin1 ().constData ());
1846         if (ft != fit) {
1847             fit = ft;
1848             if (region_surface)
1849                 region_surface->scroll = fit_scroll == fit;
1850             need_repaint = true;
1851         }
1852     } else if (parseBackgroundParam (background_color, name, val) ||
1853             parseMediaOpacityParam (media_opacity, name, val)) {
1854     } else if (name == "z-index") {
1855         z_order = val.toInt ();
1856         if (region_surface)
1857             updateSurfaceSort (this);
1858         need_repaint = true;
1859     } else if (sizes.setSizeParam (name, val)) {
1860         if (state_finished == state && region_surface)
1861             message (MsgSurfaceBoundsUpdate);
1862     } else if (name == "showBackground") {
1863         if (val == "whenActive")
1864             show_background = ShowWhenActive;
1865         else
1866             show_background = ShowAlways;
1867         need_repaint = true;
1868     } else if (name == "backgroundRepeat") {
1869         if (val == "noRepeat")
1870             bg_repeat = BgNoRepeat;
1871         else if (val == "repeatX")
1872             bg_repeat = BgRepeatX;
1873         else if (val == "repeatY")
1874             bg_repeat = BgRepeatY;
1875         else if (val == "inherit")
1876             bg_repeat = BgInherit;
1877         else
1878             bg_repeat = BgRepeat;
1879     } else if (name == "backgroundImage") {
1880         if (val.isEmpty () || val == "none" || val == "inherit") {
1881             need_repaint = !background_image.isEmpty () &&
1882                 background_image != val;
1883             background_image = val;
1884             if (media_info) {
1885                 delete media_info;
1886                 media_info = nullptr;
1887                 postpone_lock = nullptr;
1888             }
1889         } else if (background_image != val) {
1890             background_image = val;
1891             Smil *s = val.isEmpty () ? nullptr : SMIL::Smil::findSmilNode (this);
1892             if (s) {
1893                 if (!media_info)
1894                     media_info = new MediaInfo (this, MediaManager::Image);
1895                 Mrl *mrl = s->parentNode () ? s->parentNode ()->mrl () : nullptr;
1896                 QString url = mrl ? QUrl(mrl->absolutePath()).resolved(QUrl(val)).url() : val;
1897                 postpone_lock = document ()->postpone ();
1898                 media_info->wget (url);
1899             }
1900         }
1901     } else {
1902         font_props.parseParam (name, val);
1903     }
1904     if (active ()) {
1905         Surface *s = (Surface *) role (RoleDisplay);
1906         if (s && s->background_color != background_color.color){
1907             s->setBackgroundColor (background_color.color);
1908             need_repaint = true;
1909         }
1910         if (need_repaint && s)
1911             s->repaint ();
1912     }
1913     Element::parseParam (name, val);
1914 }
1915 
1916 void SMIL::RegionBase::message (MessageType msg, void *content) {
1917     switch (msg) {
1918 
1919     case MsgMediaReady:
1920         if (media_info)
1921             dataArrived ();
1922         return;
1923 
1924     case MsgChildFinished:
1925         headChildDone (this, ((Posting *) content)->source.ptr ());
1926         return;
1927 
1928     default:
1929         break;
1930     }
1931     Element::message (msg, content);
1932 }
1933 
1934 void *SMIL::RegionBase::role (RoleType msg, void *content) {
1935     switch (msg) {
1936 
1937     case RoleSizer:
1938         return &sizes;
1939 
1940     case RoleReceivers:
1941         if (MsgSurfaceAttach == (MessageType) (long) content)
1942             return &m_AttachedMediaTypes;
1943         // fall through
1944 
1945     default:
1946         break;
1947     }
1948     return Element::role (msg, content);
1949 }
1950 
1951 
1952 //--------------------------%<-------------------------------------------------
1953 
1954 void SMIL::RootLayout::closed () {
1955     QString width = getAttribute (Ids::attr_width);
1956     QString height = getAttribute (Ids::attr_height);
1957     if (!width.isEmpty () && !height.isEmpty ()) {
1958         Smil *s = Smil::findSmilNode (this);
1959         if (s) {
1960             s->size.width = width.toDouble ();
1961             s->size.height = height.toDouble();
1962         }
1963     }
1964     Element::closed ();
1965 }
1966 
1967 SMIL::RootLayout::~RootLayout() {
1968 }
1969 
1970 void SMIL::RootLayout::deactivate () {
1971     SMIL::Smil *s = Smil::findSmilNode (this);
1972     if (s)
1973         s->role (RoleChildDisplay, nullptr);
1974     if (region_surface) {
1975         region_surface->remove ();
1976         region_surface = nullptr;
1977     }
1978     RegionBase::deactivate ();
1979 }
1980 
1981 void SMIL::RootLayout::message (MessageType msg, void *content) {
1982     switch (msg) {
1983 
1984     case MsgSurfaceBoundsUpdate:
1985         if (region_surface) {
1986             Surface *surface = region_surface.ptr ();
1987             Surface *ps = surface->parentNode ();
1988             Single x, y, w, h, pw, ph;
1989             if (ps && auxiliaryNode ()) {
1990                 w = ps->bounds.width ();
1991                 h = ps->bounds.height ();
1992                 sizes.width = QString::number ((int) w);
1993                 sizes.height = QString::number ((int) h);
1994             } else {
1995                 w = sizes.width.size ();
1996                 h = sizes.height.size ();
1997                 if (ps) {
1998                     pw = ps->bounds.width ();
1999                     ph = ps->bounds.height ();
2000                     double pasp = (double) pw / ph;
2001                     double asp = (double) w / h;
2002                     if (pasp > asp) {
2003                         ps->xscale = ps->yscale = 1.0 * ph / h;
2004                         x += (Single (pw/ps->yscale) -  w) / 2;
2005                     } else {
2006                         ps->xscale = ps->yscale = 1.0 * pw / w;
2007                         y += (Single (ph/ps->xscale) - h) / 2;
2008                     }
2009                 }
2010             }
2011             if (content || surface->bounds.size != SSize (w, h)) {
2012                 surface->bounds = SRect (x, y, w, h);
2013                 if (!auxiliaryNode ()) {
2014                     SMIL::Smil *s = Smil::findSmilNode (this);
2015                     s->size = surface->bounds.size;
2016                 }
2017                 if (content)
2018                     surface->resize (surface->bounds, true);
2019                 else
2020                     surface->updateChildren (!!content);
2021             }
2022         }
2023         return;
2024 
2025     default:
2026         break;
2027     }
2028     RegionBase::message (msg, content);
2029 }
2030 
2031 void *SMIL::RootLayout::role (RoleType msg, void *content) {
2032     switch (msg) {
2033 
2034     case RoleDisplay:
2035         if (!region_surface && active ()) {
2036             SMIL::Smil *s = Smil::findSmilNode (this);
2037             if (s && s->active ()) {
2038                 Surface *surface = (Surface *)s->role (RoleChildDisplay, s);
2039                 if (surface) {
2040                     region_surface = surface->createSurface (this, SRect ());
2041                     // FIXME, silly heuristic to allow transparency in nesting
2042                     if (!background_color.color
2043                             && (!s->parentNode ()
2044                                 || s->parentNode()->id < id_node_smil))
2045                         background_color.color = 0xFFFFFAFA; // snow
2046                 }
2047             }
2048         }
2049         return region_surface.ptr ();
2050 
2051     default:
2052         break;
2053     }
2054     return RegionBase::role (msg, content);
2055 }
2056 
2057 //--------------------------%<-------------------------------------------------
2058 
2059 SMIL::Region::Region (NodePtr & d)
2060  : RegionBase (d, id_node_region) {}
2061 
2062 SMIL::Region::~Region () {
2063 }
2064 
2065 void SMIL::Region::deactivate () {
2066     if (region_surface)
2067         region_surface->remove ();
2068     RegionBase::deactivate ();
2069 }
2070 
2071 Node *SMIL::Region::childFromTag (const QString & tag) {
2072     if (!strcmp (tag.toLatin1 ().constData (), "region"))
2073         return new SMIL::Region (m_doc);
2074     return nullptr;
2075 }
2076 
2077 void SMIL::Region::message (MessageType msg, void *content) {
2078     switch (msg) {
2079 
2080     case MsgSurfaceBoundsUpdate:
2081         if (region_surface && state == state_finished) {
2082             Surface *ps = region_surface->parentNode ();
2083             if (ps) {
2084                 SSize dim = ps->bounds.size;
2085                 Single x, y, w, h;
2086                 sizes.calcSizes (this, nullptr, dim.width, dim.height, x, y, w, h);
2087                 region_surface->resize (SRect (x, y, w, h), !!content);
2088             }
2089         }
2090         return;
2091 
2092     default:
2093         break;
2094     }
2095     RegionBase::message (msg, content);
2096 }
2097 
2098 void *SMIL::Region::role (RoleType msg, void *content) {
2099     switch (msg) {
2100 
2101     case RoleDisplay:
2102         if (!region_surface && active ()) {
2103             Node *n = parentNode ();
2104             if (n && SMIL::id_node_layout == n->id)
2105                 n = n->firstChild ();
2106             Surface *s = (Surface *) n->role (RoleDisplay);
2107             if (s) {
2108                 region_surface = s->createSurface (this, SRect ());
2109                 region_surface->background_color = background_color.color;
2110                 updateSurfaceSort (this);
2111             }
2112         }
2113         return region_surface.ptr ();
2114 
2115     default: {
2116         ConnectionList *l = mouse_listeners.receivers ((MessageType)(long)content);
2117         if (l)
2118             return l;
2119     }
2120     }
2121     return RegionBase::role (msg, content);
2122 }
2123 
2124 
2125 //-----------------------------------------------------------------------------
2126 
2127 void SMIL::RegPoint::parseParam (const TrieString & p, const QString & v) {
2128     sizes.setSizeParam (p, v); // TODO: if dynamic, make sure to repaint
2129     Element::parseParam (p, v);
2130 }
2131 
2132 //-----------------------------------------------------------------------------
2133 
2134 static struct TransTypeInfo {
2135     const char *name;
2136     SMIL::Transition::TransType type;
2137     short sub_types;
2138     SMIL::Transition::TransSubType sub_type[8];
2139 } transition_type_info[] = {
2140 #include "transitions.txt"
2141 };
2142 
2143 static struct SubTransTypeInfo {
2144     const char *name;
2145     SMIL::Transition::TransSubType sub_type;
2146 } sub_transition_type_info[] = {
2147 #include "subtrans.txt"
2148 };
2149 
2150 static TransTypeInfo *transInfoFromString (const char *t) {
2151     // TODO binary search
2152     for (int i = 0; transition_type_info[i].name; ++i)
2153         if (!strcmp (t, transition_type_info[i].name))
2154             return transition_type_info + i;
2155     return nullptr;
2156 }
2157 
2158 static
2159 SMIL::Transition::TransSubType subTransInfoFromString (const char *s) {
2160     for (int i = 0; sub_transition_type_info[i].name; ++i)
2161         if (!strcmp (s, sub_transition_type_info[i].name))
2162             return sub_transition_type_info[i].sub_type;
2163     return SMIL::Transition::SubTransTypeNone;
2164 }
2165 
2166 SMIL::Transition::Transition (NodePtr & d)
2167  : Element (d, id_node_transition),
2168    type_info (nullptr), direction (dir_forward), dur (100), fade_color (0) {}
2169 
2170 void SMIL::Transition::activate () {
2171     type = TransTypeNone;
2172     sub_type = SubTransTypeNone;
2173     start_progress = 0.0;
2174     end_progress = 1.0;
2175     type_info = nullptr;
2176     init ();
2177     Element::activate ();
2178 }
2179 
2180 void SMIL::Transition::parseParam (const TrieString & para, const QString & val) {
2181     if (para == Ids::attr_type) {
2182         type_info = transInfoFromString (val.toLatin1 ().constData ());
2183         if (type_info) {
2184             type = type_info->type;
2185             if (SubTransTypeNone != sub_type) {
2186                 for (int i = 0; i < type_info->sub_types; ++i)
2187                     if (type_info->sub_type[i] == sub_type)
2188                         return;
2189             }
2190             if (type_info->sub_types > 0)
2191                 sub_type = type_info->sub_type[0];
2192         }
2193     } else if (para == Ids::attr_dur) {
2194         parseTime (val, dur);
2195     } else if (para == "subtype") {
2196         sub_type = subTransInfoFromString (val.toLatin1 ().constData ());
2197         if (type_info) {
2198             if (SubTransTypeNone != sub_type) {
2199                 for (int i = 0; i < type_info->sub_types; ++i)
2200                     if (type_info->sub_type[i] == sub_type)
2201                         return;
2202             }
2203             if (type_info->sub_types > 0)
2204                 sub_type = type_info->sub_type[0];
2205         }
2206     } else if (para == "fadeColor") {
2207         fade_color = QColor (getAttribute (val)).rgb ();
2208     } else if (para == "direction") {
2209         direction = val == "reverse" ? dir_reverse : dir_forward;
2210     } else if (para == "startProgress") {
2211         start_progress = val.toDouble();
2212         if (start_progress < 0.0)
2213             start_progress = 0.0;
2214         else if (start_progress > 1.0)
2215             start_progress = 1.0;
2216     } else if (para == "endProgress") {
2217         end_progress = val.toDouble();
2218         if (end_progress < start_progress)
2219             end_progress = start_progress;
2220         else if (end_progress > 1.0)
2221             end_progress = 1.0;
2222     } else {
2223         Element::parseParam (para, val);
2224     }
2225 }
2226 
2227 bool SMIL::Transition::supported () {
2228     switch (type) {
2229         case Fade:
2230         case BarWipe:
2231         case BowTieWipe:
2232         case PushWipe:
2233         case IrisWipe:
2234         case ClockWipe:
2235         case EllipseWipe:
2236             return true;
2237         default:
2238             return false;
2239     }
2240 }
2241 
2242 //-----------------------------------------------------------------------------
2243 
2244 SMIL::GroupBase::GroupBase (NodePtr & d, short id)
2245  : Element (d, id),
2246    runtime (new Runtime (this)) {}
2247 
2248 SMIL::GroupBase::~GroupBase () {
2249     delete runtime;
2250 }
2251 
2252 Node *SMIL::GroupBase::childFromTag (const QString & tag) {
2253     Element * elm = fromScheduleGroup (m_doc, tag);
2254     if (!elm) elm = fromMediaContentGroup (m_doc, tag);
2255     if (!elm) elm = fromContentControlGroup (m_doc, tag);
2256     if (!elm) elm = fromAnimateGroup (m_doc, tag);
2257     if (elm)
2258         return elm;
2259     return nullptr;
2260 }
2261 
2262 void SMIL::GroupBase::init () {
2263     if (Runtime::TimingsInitialized > runtime->timingstate) {
2264         Element::init ();
2265         runtime->initialize ();
2266     }
2267 }
2268 
2269 void SMIL::GroupBase::finish () {
2270     setState (state_finished); // avoid recurstion through childDone
2271     for (NodePtr e = firstChild (); e; e = e->nextSibling ())
2272         if (e->unfinished ())
2273             e->finish ();
2274     runtime->finish ();
2275 }
2276 
2277 namespace {
2278 
2279 class GroupBaseInitVisitor : public Visitor {
2280 public:
2281     using Visitor::visit;
2282 
2283     bool ready;
2284 
2285     GroupBaseInitVisitor () : ready (true) {
2286     }
2287 
2288     void visit (Node *node) override {
2289         node->message (MsgMediaPrefetch, MsgBool (1));
2290     }
2291     void visit (SMIL::PriorityClass *pc) override {
2292         for (NodePtr n = pc->firstChild (); n; n = n->nextSibling ())
2293             n->accept (this);
2294     }
2295     void visit (SMIL::Seq *seq) override {
2296         for (Node *n = seq->firstChild (); n; n = n->nextSibling ())
2297             if (n->role (RoleTiming)) {
2298                 seq->firstChild ()->accept (this);
2299                 ready = !!seq->firstChild ()->role (RoleReady);
2300                 break;
2301             }
2302     }
2303     void visit (SMIL::Switch *s) override {
2304         Node *n = s->chosenOne ();
2305         if (n)
2306              n->accept (this);
2307     }
2308     void visit (SMIL::Anchor *a) override {
2309         if (a->firstChild ())
2310              a->firstChild ()->accept (this);
2311     }
2312     void visit (SMIL::Par *par) override {
2313         for (NodePtr n = par->firstChild (); n; n = n->nextSibling ()) {
2314             n->accept (this);
2315             if (ready)
2316                 ready = !!n->role (RoleReady);
2317         }
2318     }
2319 };
2320 
2321 class FreezeStateUpdater : public Visitor {
2322 
2323     bool initial_node;
2324     bool freeze;
2325 
2326     void setFreezeState (Runtime *rt) {
2327         bool auto_freeze = (Runtime::DurTimer == rt->durTime ().durval &&
2328                     0 == rt->durTime ().offset &&
2329                     Runtime::DurMedia == rt->endTime ().durval) &&
2330             rt->fill_active != Runtime::fill_remove;
2331         bool cfg_freeze = rt->fill_active == Runtime::fill_freeze ||
2332             rt->fill_active == Runtime::fill_hold ||
2333             rt->fill_active == Runtime::fill_transition;
2334 
2335         bool do_freeze = freeze && (auto_freeze || cfg_freeze);
2336         if (do_freeze && rt->timingstate == Runtime::timings_stopped) {
2337             rt->timingstate = Runtime::timings_freezed;
2338             rt->element->message (MsgStateFreeze);
2339         } else if (!do_freeze && rt->timingstate == Runtime::timings_freezed) {
2340             rt->timingstate = Runtime::timings_stopped;
2341             rt->element->message (MsgStateFreeze);
2342         }
2343     }
2344     void updateNode (Node *n) {
2345         if (initial_node) {
2346             initial_node = false;
2347         } else {
2348             Runtime *rt = (Runtime *) n->role (RoleTiming);
2349             if (rt && rt->timingstate >= Runtime::timings_stopped)
2350                 setFreezeState (rt);
2351         }
2352     }
2353 public:
2354     using Visitor::visit;
2355 
2356     FreezeStateUpdater () : initial_node (true), freeze (true) {}
2357 
2358     void visit (Element *elm) override {
2359         updateNode (elm);
2360     }
2361     void visit (SMIL::PriorityClass *pc) override {
2362         for (NodePtr n = pc->firstChild (); n; n = n->nextSibling ())
2363             n->accept (this);
2364     }
2365     void visit (SMIL::Seq *seq) override {
2366         bool old_freeze = freeze;
2367 
2368         updateNode (seq);
2369         freeze = freeze && seq->runtime->active ();
2370 
2371         Runtime *prev = nullptr;
2372         for (NodePtr n = seq->firstChild (); n; n = n->nextSibling ()) {
2373             if (n->active ()) {
2374                 Runtime *rt = (Runtime *) n->role (RoleTiming);
2375                 if (rt) {
2376                     bool prev_freeze = prev && freeze &&
2377                         (prev->fill_active == Runtime::fill_hold ||
2378                          (prev->fill_active == Runtime::fill_transition &&
2379                           Runtime::TimingsTransIn == rt->state ()));
2380                     if (rt->timingstate < Runtime::timings_started) {
2381                         break;
2382                     } else if (rt->timingstate < Runtime::timings_stopped) {
2383                         freeze = prev_freeze;
2384                         break;
2385                     }
2386                     if (prev_freeze)
2387                         prev->element->accept (this);
2388                     if (prev &&
2389                             (!prev_freeze ||
2390                              prev->timingstate == Runtime::timings_stopped))
2391                         prev->element->deactivate();
2392                     prev = rt;
2393                 }
2394             }
2395         }
2396         if (prev) {
2397             prev->element->accept (this);
2398             if (prev->timingstate == Runtime::timings_stopped)
2399                 prev->element->deactivate();
2400         }
2401 
2402         freeze = old_freeze;
2403     }
2404     void visit (SMIL::Anchor *a) override {
2405         if (a->firstChild ())
2406             a->firstChild ()->accept (this);
2407     }
2408     void visit (SMIL::Par *par) override {
2409         bool old_freeze = freeze;
2410 
2411         updateNode (par);
2412         freeze = freeze && par->runtime->active ();
2413 
2414         for (NodePtr n = par->firstChild (); n; n = n->nextSibling ())
2415             n->accept (this);
2416 
2417         freeze = old_freeze;
2418     }
2419     void visit (SMIL::Excl *excl) override {
2420         bool old_freeze = freeze;
2421 
2422         updateNode (excl);
2423         bool new_freeze = freeze && excl->runtime->active ();
2424 
2425         Node *cur = excl->cur_node.ptr ();
2426         for (NodePtr n = excl->firstChild (); n; n = n->nextSibling ()) {
2427             freeze = new_freeze && n.ptr () == cur;
2428             n->accept (this);
2429         }
2430 
2431         freeze = old_freeze;
2432     }
2433     void visit (SMIL::Switch *s) override {
2434         bool old_freeze = freeze;
2435 
2436         updateNode (s);
2437         freeze &= s->runtime->active ();
2438 
2439         Node *cur = s->chosenOne ();
2440         if (cur)
2441             cur->accept (this);
2442 
2443         freeze = old_freeze;
2444     }
2445 };
2446 
2447 }
2448 
2449 void SMIL::GroupBase::activate () {
2450     init ();
2451     GroupBaseInitVisitor visitor;
2452     accept (&visitor);
2453     setState (state_activated);
2454     if (visitor.ready)
2455         runtime->start ();
2456     else
2457         state = state_deferred;
2458 }
2459 
2460 void SMIL::GroupBase::parseParam (const TrieString &para, const QString &val) {
2461     if (!runtime->parseParam (para, val))
2462         Element::parseParam (para, val);
2463 }
2464 
2465 void SMIL::GroupBase::message (MessageType msg, void *content) {
2466     switch (msg) {
2467 
2468     case MsgStateRewind:
2469         if (active ()) {
2470             State old = state;
2471             state = state_deactivated;
2472             for (NodePtr e = firstChild (); e; e = e->nextSibling ())
2473                 e->reset ();
2474             state = old;
2475             GroupBaseInitVisitor visitor;
2476             accept (&visitor);
2477         }
2478         return;
2479 
2480     default:
2481         break;
2482     }
2483     if ((int) msg >= (int) Runtime::DurLastDuration)
2484         Element::message (msg, content);
2485     else
2486         runtime->message (msg, content);
2487 }
2488 
2489 void *SMIL::GroupBase::role (RoleType msg, void *content) {
2490     switch (msg) {
2491 
2492     case RoleTiming:
2493         if (Runtime::TimingsInitialized > runtime->timingstate)
2494             init ();
2495         return runtime;
2496 
2497     default:
2498         break;
2499     }
2500     void *response = runtime->role (msg, content);
2501     if (response == MsgUnhandled)
2502         return Element::role (msg, content);
2503     return response;
2504 }
2505 
2506 
2507 void SMIL::GroupBase::deactivate () {
2508     bool need_finish (unfinished ());
2509     setState (state_deactivated); // avoid recurstion through childDone
2510     for (NodePtr e = firstChild (); e; e = e->nextSibling ())
2511         if (e->active ())
2512             e->deactivate ();
2513         else
2514             e->message (MsgMediaPrefetch, MsgBool (0));
2515     if (need_finish)
2516         finish ();
2517     runtime->init ();
2518     Element::deactivate ();
2519 }
2520 
2521 void SMIL::GroupBase::reset () {
2522     Element::reset ();
2523     runtime->init ();
2524 }
2525 
2526 void SMIL::GroupBase::setJumpNode (NodePtr n) {
2527     NodePtr child = n;
2528     if (state > state_init) {
2529         state = state_deferred;
2530         for (NodePtr c = firstChild (); c; c = c->nextSibling ())
2531             if (c->active ())
2532                 c->reset ();
2533         for (Node *c = n->parentNode (); c; c = c->parentNode ()) {
2534             if (c == this || c->id == id_node_body)
2535                 break;
2536             if (c->id >= id_node_first_group && c->id <= id_node_last_group)
2537                 static_cast <SMIL::GroupBase *> (c)->jump_node = child;
2538             child = c;
2539         }
2540     }
2541     jump_node = child;
2542     state = state_activated;
2543     init ();
2544     for (NodePtr n = firstChild (); n; n = n->nextSibling ())
2545         if (n->role (RoleTiming))
2546             convertNode <Element> (n)->init ();
2547     runtime->startAndBeginNode (); // undefer through begin()
2548 }
2549 
2550 //-----------------------------------------------------------------------------
2551 
2552 // SMIL::Body was here
2553 
2554 //-----------------------------------------------------------------------------
2555 
2556 void SMIL::Par::begin () {
2557     jump_node = nullptr; // TODO: adjust timings
2558     setState (state_began);
2559     for (NodePtr e = firstChild (); e; e = e->nextSibling ())
2560         e->activate ();
2561 }
2562 
2563 void SMIL::Par::reset () {
2564     GroupBase::reset ();
2565     for (NodePtr e = firstChild (); e; e = e->nextSibling ())
2566         e->reset ();
2567 }
2568 
2569 static bool childrenReady (Node *node) {
2570     for (Node *n = node->firstChild (); n; n = n->nextSibling ())
2571         if (!n->role (RoleReady))
2572             return false;
2573     return true;
2574 }
2575 
2576 void SMIL::Par::message (MessageType msg, void *content) {
2577     switch (msg) {
2578 
2579     case MsgChildReady:
2580         if (childrenReady (this)) {
2581             const int cur_state = state;
2582             if (state == state_deferred) {
2583                 state = state_activated;
2584                 runtime->start ();
2585             }
2586             if (cur_state == state_init && parentNode ())
2587                 parentNode ()->message (MsgChildReady, this);
2588         }
2589         return;
2590 
2591     case MsgChildFinished: {
2592         if (unfinished ()) {
2593             FreezeStateUpdater visitor;
2594             accept (&visitor);
2595             runtime->tryFinish ();
2596         }
2597         return;
2598     }
2599     default:
2600         break;
2601     }
2602     GroupBase::message (msg, content);
2603 }
2604 
2605 void *SMIL::Par::role (RoleType msg, void *content) {
2606     switch (msg) {
2607     case RoleReady:
2608         return MsgBool (childrenReady (this));
2609     default:
2610         break;
2611     }
2612     return GroupBase::role (msg, content);
2613 }
2614 
2615 
2616 //-----------------------------------------------------------------------------
2617 
2618 void SMIL::Seq::begin () {
2619     setState (state_began);
2620     if (jump_node) {
2621         starting_connection.disconnect ();
2622         trans_connection.disconnect ();
2623         for (NodePtr c = firstChild (); c; c = c->nextSibling ())
2624             if (c == jump_node) {
2625                 jump_node = nullptr;
2626                 c->activate ();
2627                 break;
2628             } else {
2629                 c->state = state_activated; // TODO: ..
2630                 if (c->isElementNode ())
2631                     convertNode <Element> (c)->init ();
2632                 c->state = state_finished; // TODO: ..
2633                 Runtime *rt = (Runtime *) c->role (RoleTiming);
2634                 if (rt)
2635                     rt->timingstate = Runtime::timings_stopped; //TODO fill_hold
2636             }
2637     } else if (firstChild ()) {
2638         if (firstChild ()->nextSibling()) {
2639             GroupBaseInitVisitor visitor;
2640             firstChild ()->nextSibling ()->accept (&visitor);
2641         }
2642         starting_connection.connect (firstChild (), MsgEventStarted, this);
2643         firstChild ()->activate ();
2644     }
2645 }
2646 
2647 void SMIL::Seq::message (MessageType msg, void *content) {
2648     switch (msg) {
2649 
2650         case MsgChildReady:
2651             if (firstChild () == (Node *) content) {
2652                 if (state == state_deferred) {
2653                     state = state_activated;
2654                     runtime->start ();
2655                 }
2656                 if (state == state_init && parentNode ())
2657                     parentNode ()->message (MsgChildReady, this);
2658             } else if (unfinished ()) {
2659                 FreezeStateUpdater visitor;
2660                 accept (&visitor);
2661             }
2662             return;
2663 
2664         case MsgChildFinished: {
2665             if (unfinished ()) {
2666                 Posting *post = (Posting *) content;
2667                 if (state != state_deferred) {
2668                     Node *next = post->source
2669                         ? post->source->nextSibling ()
2670                         : nullptr;
2671                     if (next) {
2672                         if (next->nextSibling()) {
2673                             GroupBaseInitVisitor visitor;
2674                             next->nextSibling ()->accept (&visitor);
2675                         }
2676                         starting_connection.connect(next, MsgEventStarted,this);
2677                         trans_connection.connect (
2678                                 next, MsgChildTransformedIn, this);
2679                         next->activate ();
2680                     } else {
2681                         starting_connection.disconnect ();
2682                         trans_connection.disconnect ();
2683                         runtime->tryFinish ();
2684                     }
2685                     FreezeStateUpdater visitor;
2686                     accept (&visitor);
2687                 } else if (jump_node) {
2688                     finish ();
2689                 }
2690             }
2691             return;
2692         }
2693 
2694         case MsgEventStarted: {
2695             Posting *event = static_cast <Posting *> (content);
2696             Node *source = event->source;
2697             if (source != this && source->previousSibling ()) {
2698                 FreezeStateUpdater visitor;
2699                 starting_connection.disconnect ();
2700                 accept (&visitor);
2701             }
2702             break;
2703         }
2704 
2705         case MsgChildTransformedIn: {
2706             Node *source = (Node *) content;
2707             if (source != this && source->previousSibling ()) {
2708                 FreezeStateUpdater visitor;
2709                 starting_connection.disconnect ();
2710                 accept (&visitor);
2711             }
2712             break;
2713         }
2714 
2715         default:
2716             break;
2717     }
2718     GroupBase::message (msg, content);
2719 }
2720 
2721 void *SMIL::Seq::role (RoleType msg, void *content) {
2722     switch (msg) {
2723     case RoleReady:
2724         return MsgBool (!firstChild () || firstChild ()->role (RoleReady));
2725     default:
2726         break;
2727     }
2728     return GroupBase::role (msg, content);
2729 }
2730 
2731 
2732 //-----------------------------------------------------------------------------
2733 
2734 Node *SMIL::Excl::childFromTag (const QString &tag) {
2735     if (tag == "priorityClass")
2736         return new PriorityClass (m_doc);
2737     return GroupBase::childFromTag (tag);
2738 }
2739 
2740 namespace {
2741 
2742 class ExclActivateVisitor : public Visitor {
2743     SMIL::Excl *excl;
2744 public:
2745     ExclActivateVisitor (SMIL::Excl *ex) : excl (ex) {}
2746 
2747     using Visitor::visit;
2748 
2749     void visit (Node *n) override {
2750         Node *s = n->nextSibling ();
2751         if (s)
2752             s->accept (this);
2753     }
2754     void visit (Element *elm) override {
2755         if (elm->role (RoleTiming)) {
2756             // make aboutToStart connection with Timing
2757             excl->started_event_list =
2758                 new SMIL::Excl::ConnectionItem (excl->started_event_list);
2759             excl->started_event_list->link.connect (elm, MsgEventStarting, excl);
2760             elm->activate ();
2761         }
2762         visit (static_cast <Node *> (elm));
2763     }
2764     void visit (SMIL::PriorityClass *pc) override {
2765         pc->init ();
2766         pc->state = Node::state_activated;
2767         Node *n = pc->firstChild ();
2768         if (n)
2769             n->accept (this);
2770         visit (static_cast <Node *> (pc));
2771     }
2772 };
2773 
2774 class ExclPauseVisitor : public Visitor {
2775     bool pause;
2776     Node *paused_by;
2777     unsigned int cur_time;
2778 
2779     void updatePauseStateEvent (Posting *event, int pause_time) {
2780         if (event) {
2781             if (pause)
2782                 paused_by->document ()->pausePosting (event);
2783             else
2784                 paused_by->document ()->unpausePosting (event, (cur_time-pause_time)*10);
2785         }
2786     }
2787     static Posting *activeEvent (Runtime *r) {
2788         Posting *event = nullptr;
2789         if (r->begin_timer)
2790             event = r->begin_timer;
2791         else if (r->started_timer)
2792             event = r->started_timer;
2793         else if (r->duration_timer)
2794             event = r->duration_timer;
2795         else if (r->stopped_timer)
2796             event = r->stopped_timer;
2797         return event;
2798     }
2799 
2800 public:
2801     ExclPauseVisitor (bool p, Node *pb, unsigned int pt)
2802         : pause(p), paused_by (pb), cur_time (pt) {}
2803     ~ExclPauseVisitor () override {
2804         paused_by->document ()->updateTimeout ();
2805     }
2806 
2807     using Visitor::visit;
2808 
2809     void visit (Node *node) override {
2810         for (Node *c = node->firstChild (); c; c = c->nextSibling ())
2811             c->accept (this);
2812     }
2813     void visit (Element *elm) override {
2814         if (!elm->active ())
2815             return; // nothing to do
2816         Runtime *rt = (Runtime *) elm->role (RoleTiming);
2817         if (rt) {
2818             if (pause) {
2819                 rt->paused_time = cur_time;
2820                 rt->paused_by = paused_by;
2821                 rt->unpaused_state = rt->timingstate;
2822                 rt->timingstate = Runtime::timings_paused;
2823             } else {
2824                 rt->paused_by = nullptr;
2825                 rt->timingstate = rt->unpaused_state;
2826                 rt->start_time += cur_time;
2827             }
2828             updatePauseStateEvent (activeEvent (rt), rt->paused_time);
2829         }
2830         visit (static_cast <Node *> (elm));
2831     }
2832     void visit (SMIL::MediaType *mt) override {
2833         if (mt->media_info && mt->media_info->media) {
2834             if (pause)
2835                 mt->media_info->media->pause ();
2836             else
2837                 mt->media_info->media->unpause ();
2838             Surface *s = mt->surface ();
2839             if (s)
2840                 s->repaint ();
2841         }
2842 
2843         Posting *event = nullptr;
2844         if (mt->transition.trans_out_timer)
2845             event = mt->transition.trans_out_timer;
2846         updatePauseStateEvent (event, mt->runtime->paused_time);
2847 
2848         visit (static_cast <Element *> (mt));
2849     }
2850     void visit (SMIL::AnimateBase *an) override {
2851         updatePauseStateEvent(an->anim_timer, an->runtime->paused_time);
2852         visit (static_cast <Element *> (an));
2853     }
2854     void visit (SMIL::Smil *s) override {
2855         for (Node *c = s->firstChild (); c; c = c->nextSibling ())
2856             if (SMIL::id_node_body == c->id)
2857                 c->accept (this);
2858     }
2859 };
2860 
2861 }
2862 
2863 static void clearList (SMIL::Excl::ConnectionItem **pitem) {
2864     SMIL::Excl::ConnectionItem *item = *pitem;
2865     while (item) {
2866         SMIL::Excl::ConnectionItem *tmp = item;
2867         item = item->next;
2868         delete tmp;
2869     }
2870     *pitem = nullptr;
2871 }
2872 
2873 SMIL::Excl::Excl (NodePtr & d)
2874     : GroupBase (d, id_node_excl), started_event_list (nullptr) {}
2875 
2876 SMIL::Excl::~Excl () {
2877     clearList (&started_event_list);
2878 }
2879 
2880 void SMIL::Excl::begin () {
2881     Node *n = firstChild ();
2882     if (n) {
2883         ExclActivateVisitor visitor (this);
2884         n->accept (&visitor);
2885     }
2886 }
2887 
2888 void SMIL::Excl::deactivate () {
2889     clearList (&started_event_list);
2890     priority_queue.clear ();
2891     stopped_connection.disconnect ();
2892     GroupBase::deactivate ();
2893 }
2894 
2895 void SMIL::Excl::message (MessageType msg, void *content) {
2896     switch (msg) {
2897         case MsgEventStarting: {
2898             Node *source = (Node *) content;
2899             NodePtr n = cur_node;
2900             if (source == n.ptr ())
2901                 return; // eg. repeating
2902             cur_node = source;
2903             stopped_connection.connect (cur_node, MsgEventStopped, this);
2904             if (n) {
2905                 if (SMIL::id_node_priorityclass == cur_node->parentNode ()->id) {
2906                     switch (static_cast <SMIL::PriorityClass *>
2907                             (cur_node->parentNode ())->peers) {
2908                         case PriorityClass::PeersPause: {
2909                             ExclPauseVisitor visitor (
2910                                   true, this, document ()->last_event_time/10);
2911                             n->accept (&visitor);
2912                             priority_queue.insertBefore (
2913                                   new NodeRefItem (n), priority_queue.first ());
2914                             return;
2915                         }
2916                         default:
2917                             break; //TODO
2918                     }
2919                 }
2920                 ((Runtime*)n->role (RoleTiming))->doFinish ();
2921             }
2922             return;
2923         }
2924         case MsgChildFinished: {
2925             Posting *event = static_cast <Posting *> (content);
2926             FreezeStateUpdater visitor;
2927             accept (&visitor);
2928             if (event->source == cur_node) {
2929                 Runtime* rt = (Runtime*)cur_node->role (RoleTiming);
2930                 if (rt && rt->timingstate == Runtime::timings_stopped) {
2931                     cur_node = nullptr;
2932                     stopped_connection.disconnect ();
2933                 }
2934                 runtime->tryFinish ();
2935             }
2936             return;
2937         }
2938         case MsgEventStopped: {
2939             Posting *event = static_cast <Posting *> (content);
2940             if (event->source == cur_node) {
2941 
2942                 NodeRefItemPtr ref = priority_queue.first ();
2943                 while (ref && (!ref->data || !ref->data->active ())) {
2944                     // should not happen, but consider a backend crash or so
2945                     priority_queue.remove (ref);
2946                     ref = priority_queue.first ();
2947                 }
2948                 if (ref) {
2949                     cur_node = ref->data;
2950                     priority_queue.remove (ref);
2951                     stopped_connection.connect (cur_node, MsgEventStopped, this);
2952                     ExclPauseVisitor visitor (false, this, document()->last_event_time/10);
2953                     cur_node->accept (&visitor);
2954                     // else TODO
2955                 }
2956             }
2957             break;
2958         }
2959         default:
2960             break;
2961     }
2962     GroupBase::message (msg, content);
2963 }
2964 
2965 //-----------------------------------------------------------------------------
2966 
2967 Node *SMIL::PriorityClass::childFromTag (const QString &tag) {
2968     Element * elm = fromScheduleGroup (m_doc, tag);
2969     if (!elm) elm = fromMediaContentGroup (m_doc, tag);
2970     if (!elm) elm = fromContentControlGroup (m_doc, tag);
2971     if (!elm) elm = fromAnimateGroup (m_doc, tag);
2972     if (elm)
2973         return elm;
2974     return nullptr;
2975 }
2976 
2977 void
2978 SMIL::PriorityClass::parseParam (const TrieString &name, const QString &val) {
2979     if (name == "peers") {
2980         if (val == "pause")
2981             peers = PeersPause;
2982         else if (val == "defer")
2983             peers = PeersDefer;
2984         else if (val == "never")
2985             peers = PeersNever;
2986         else
2987             peers = PeersStop;
2988     } else if (name == "higher") {
2989         if (val == "stop")
2990             higher = HigherStop;
2991         else
2992             higher = HigherPause;
2993     } else if (name == "lower") {
2994         if (val == "never")
2995             lower = LowerNever;
2996         else
2997             lower = LowerDefer;
2998     } else if (name == "pauseDisplay") {
2999         if (val == "disable")
3000             pause_display = PauseDisplayDisable;
3001         else if (val == "hide")
3002             pause_display = PauseDisplayHide;
3003         else
3004             pause_display = PauseDisplayShow;
3005     }
3006 }
3007 
3008 void SMIL::PriorityClass::init () {
3009     peers = PeersStop;
3010     higher = HigherPause;
3011     lower = LowerDefer;
3012     pause_display = PauseDisplayShow;
3013     Element::init ();
3014 }
3015 
3016 void SMIL::PriorityClass::message (MessageType msg, void *data) {
3017     if (MsgChildFinished == msg)
3018         // do nothing
3019         return;
3020     Element::message (msg, data);
3021 }
3022 
3023 //-----------------------------------------------------------------------------
3024 
3025 void SMIL::Switch::init () {
3026     Node *n = chosen_one.ptr ();
3027     chosen_one = nullptr;
3028     if (n && n->active ())
3029         n->deactivate ();
3030     GroupBase::init ();
3031 }
3032 
3033 Node *SMIL::Switch::chosenOne () {
3034     if (!chosen_one && firstChild ()) {
3035         PlayListNotify * n = document()->notify_listener;
3036         int pref = 0, max = 0x7fffffff, currate = 0;
3037         if (n)
3038             n->bitRates (pref, max);
3039         if (firstChild ()) {
3040             Node *fallback = nullptr;
3041             for (Node *e = firstChild (); e; e = e->nextSibling ())
3042                 if (e->isElementNode ()) {
3043                     Element *elm = static_cast <Element *> (e);
3044                     Runtime *rt = (Runtime *) e->role (RoleTiming);
3045                     if (rt) {
3046                         if (rt->state () < Runtime::TimingsInitialized)
3047                             elm->init ();
3048                         if (!disabledByExpr (rt)) {
3049                             QString lang = elm->getAttribute ("systemLanguage");
3050                             if (!lang.isEmpty ()) {
3051                                 lang = lang.replace (QChar ('-'), QChar ('_'));
3052                                 char *clang = getenv ("LANG");
3053                                 if (!clang) {
3054                                     if (!fallback)
3055                                         fallback = e;
3056                                 } else if (QString (clang).toLower ().startsWith (lang)) {
3057                                     chosen_one = e;
3058                                 } else if (!fallback) {
3059                                     fallback = e->nextSibling ();
3060                                 }
3061                             }
3062                             if (e->id == id_node_ref) {
3063                                 SMIL::MediaType * mt = static_cast<SMIL::MediaType*>(e);
3064                                 if (!chosen_one) {
3065                                     chosen_one = e;
3066                                     currate = mt->bitrate;
3067                                 } else if (int (mt->bitrate) <= max) {
3068                                     int delta1 = pref > currate ? pref-currate : currate-pref;
3069                                     int delta2 = pref > int (mt->bitrate) ? pref-mt->bitrate : mt->bitrate-pref;
3070                                     if (delta2 < delta1) {
3071                                         chosen_one = e;
3072                                         currate = mt->bitrate;
3073                                     }
3074                                 }
3075                             } else if (!fallback)
3076                                 fallback = e;
3077                         }
3078                     }
3079                 }
3080             if (!chosen_one)
3081                 chosen_one = (fallback ? fallback : firstChild ());
3082         }
3083     }
3084     return chosen_one.ptr ();
3085 }
3086 
3087 void SMIL::Switch::begin () {
3088     Node *n = chosenOne ();
3089     if (n)
3090         n->activate ();
3091     else
3092         runtime->tryFinish ();
3093 }
3094 
3095 void SMIL::Switch::deactivate () {
3096     chosen_one = nullptr;
3097     GroupBase::deactivate ();
3098 }
3099 
3100 void SMIL::Switch::reset () {
3101     GroupBase::reset ();
3102     for (NodePtr e = firstChild (); e; e = e->nextSibling ()) {
3103         if (e->state != state_init)
3104             e->reset ();
3105     }
3106 }
3107 
3108 void SMIL::Switch::message (MessageType msg, void *content) {
3109     switch (msg) {
3110     case MsgChildFinished: {
3111         Posting *post = (Posting *) content;
3112         if (unfinished () && post->source == chosen_one) {
3113             runtime->tryFinish ();
3114             FreezeStateUpdater visitor;
3115             accept (&visitor);
3116         }
3117         return;
3118     }
3119     case MsgStateRewind:
3120         chosen_one = nullptr;
3121         break;
3122     default:
3123         break;
3124     }
3125     GroupBase::message (msg, content);
3126 }
3127 
3128 //-----------------------------------------------------------------------------
3129 
3130 SMIL::LinkingBase::LinkingBase (NodePtr & d, short id)
3131  : Element(d, id), show (show_replace) {}
3132 
3133 void SMIL::LinkingBase::deactivate () {
3134     mediatype_attach.disconnect ();
3135     Element::deactivate ();
3136 }
3137 
3138 void SMIL::LinkingBase::parseParam(const TrieString &para, const QString &val) {
3139     if (para == Ids::attr_href) {
3140         href = val;
3141     } else if (para == Ids::attr_target) {
3142         target = val;
3143     }
3144 }
3145 
3146 //-----------------------------------------------------------------------------
3147 
3148 SMIL::Anchor::Anchor (NodePtr & d)
3149  : LinkingBase (d, id_node_anchor) {}
3150 
3151 void SMIL::Anchor::activate () {
3152     init ();
3153     for (Node *c = firstChild(); c; c = c->nextSibling ())
3154         if (nodeMessageReceivers (c, MsgEventClicked)) {
3155             mediatype_attach.connect (c, MsgSurfaceAttach, this);
3156             break;
3157         }
3158     Element::activate ();
3159 }
3160 
3161 void SMIL::Anchor::message (MessageType msg, void *content) {
3162     switch (msg) {
3163 
3164         case MsgChildReady:
3165             if (parentNode ())
3166                 parentNode ()->message (MsgChildReady, this);
3167             return;
3168 
3169         case MsgChildFinished: {
3170             Posting *post = (Posting *) content;
3171             if (unfinished ()) {
3172                 if (post->source->nextSibling ())
3173                     post->source->nextSibling ()->activate ();
3174                 else
3175                     finish ();
3176             }
3177             return;
3178         }
3179 
3180         default:
3181             break;
3182     }
3183     LinkingBase::message (msg, content);
3184 }
3185 
3186 Node *SMIL::Anchor::childFromTag (const QString & tag) {
3187     return fromMediaContentGroup (m_doc, tag);
3188 }
3189 
3190 void *SMIL::Anchor::role (RoleType msg, void *content) {
3191     switch (msg) {
3192     case RoleReady:
3193         return MsgBool (childrenReady (this));
3194     default:
3195         break;
3196     }
3197     return LinkingBase::role (msg, content);
3198 }
3199 
3200 
3201 //-----------------------------------------------------------------------------
3202 
3203 SMIL::Area::Area (NodePtr & d, const QString & t)
3204  : LinkingBase (d, id_node_area), coords (nullptr), nr_coords (0), tag (t.toUtf8()) {}
3205 
3206 SMIL::Area::~Area () {
3207     delete [] coords;
3208 }
3209 
3210 void SMIL::Area::activate () {
3211     init ();
3212     if (parentNode () &&
3213             parentNode ()->id >= id_node_first_mediatype &&
3214             parentNode ()->id <= id_node_last_mediatype) {
3215         mediatype_attach.connect (parentNode (), MsgSurfaceAttach, this);
3216     }
3217     Element::activate ();
3218 }
3219 
3220 void SMIL::Area::parseParam (const TrieString & para, const QString & val) {
3221     if (para == "coords") {
3222         delete [] coords;
3223         QStringList clist = val.split (QChar (','));
3224         nr_coords = clist.count ();
3225         coords = new SizeType [nr_coords];
3226         for (int i = 0; i < nr_coords; ++i)
3227             coords[i] = clist[i];
3228     } else
3229         LinkingBase::parseParam (para, val);
3230 }
3231 
3232 void *SMIL::Area::role (RoleType msg, void *content) {
3233     ConnectionList *l = mouse_listeners.receivers ((MessageType) (long) content);
3234     if (l)
3235         return l;
3236     return Element::role (msg, content);
3237 }
3238 
3239 //-----------------------------------------------------------------------------
3240 
3241 SMIL::MediaType::MediaType (NodePtr &d, const QByteArray &t, short id)
3242  : Mrl (d, id),
3243    runtime (new Runtime (this)),
3244    m_type (t),
3245    pan_zoom (nullptr),
3246    bitrate (0),
3247    sensitivity (sens_opaque) {
3248     view_mode = Mrl::WindowMode;
3249 }
3250 
3251 SMIL::MediaType::~MediaType () {
3252     delete runtime;
3253     delete pan_zoom;
3254 }
3255 
3256 Node *SMIL::MediaType::childFromTag (const QString & tag) {
3257     Element * elm = fromContentControlGroup (m_doc, tag);
3258     if (!elm) elm = fromParamGroup (m_doc, tag);
3259     if (!elm) elm = fromAnimateGroup (m_doc, tag);
3260     if (elm)
3261         return elm;
3262     return nullptr;
3263 }
3264 
3265 static NodePtr findExternalTree (Mrl *mrl) {
3266     for (Node *c = mrl->firstChild (); c; c = c->nextSibling ()) {
3267         Mrl * m = c->mrl ();
3268         if (m && (m->opener.ptr () == mrl ||
3269                     m->id == SMIL::id_node_smil ||
3270                     m->id == RP::id_node_imfl))
3271             return c;
3272     }
3273     return nullptr;
3274 }
3275 
3276 void SMIL::MediaType::closed () {
3277     external_tree = findExternalTree (this);
3278     Mrl *mrl = external_tree ? external_tree->mrl () : nullptr;
3279     if (mrl)
3280         size = mrl->size;
3281     title = getAttribute (Ids::attr_title);
3282     Mrl::closed ();
3283 }
3284 
3285 void SMIL::MediaType::prefetch () {
3286 }
3287 
3288 void SMIL::MediaType::parseParam (const TrieString &para, const QString & val) {
3289     if (para == Ids::attr_src) {
3290         if (src != val) {
3291             src = val;
3292             if (external_tree)
3293                 removeChild (external_tree);
3294             delete media_info;
3295             media_info = nullptr;
3296             if (!val.isEmpty () && runtimeBegan (runtime))
3297                 prefetch ();
3298             if (state == state_began && resolved)
3299                 clipStart ();
3300         }
3301     } else if (para == Ids::attr_fit) {
3302         fit = parseFit (val.toLatin1 ().constData ());
3303         if (fit != effective_fit)
3304             message (MsgSurfaceBoundsUpdate);
3305     } else if (para == Ids::attr_type) {
3306         mimetype = val;
3307     } else if (para == "panZoom") {
3308         QStringList coords = val.split (QChar (','));
3309         if (coords.size () < 4) {
3310             qCWarning(LOG_KMPLAYER_COMMON) << "panZoom less then four nubmers";
3311             return;
3312         }
3313         if (!pan_zoom)
3314             pan_zoom = new CalculatedSizer;
3315         pan_zoom->left = coords[0];
3316         pan_zoom->top = coords[1];
3317         pan_zoom->width = coords[2];
3318         pan_zoom->height = coords[3];
3319     } else if (parseBackgroundParam (background_color, para, val) ||
3320             parseMediaOpacityParam (media_opacity, para, val)) {
3321     } else if (para == "system-bitrate") {
3322         bitrate = val.toInt ();
3323     } else if (parseTransitionParam (this, transition, runtime, para, val)) {
3324     } else if (para == "sensitivity") {
3325         if (val == "transparent")
3326             sensitivity = sens_transparent;
3327         //else if (val == "percentage") // TODO
3328         //    sensitivity = sens_percentage;
3329         else
3330             sensitivity = sens_opaque;
3331     } else if (sizes.setSizeParam (para, val)) {
3332         message (MsgSurfaceBoundsUpdate);
3333     } else if (!runtime->parseParam (para, val)) {
3334         Mrl::parseParam (para, val);
3335     }
3336     if (sub_surface) {
3337         sub_surface->markDirty ();
3338         sub_surface->setBackgroundColor (background_color.color);
3339         sub_surface->repaint ();
3340     }
3341 }
3342 
3343 void SMIL::MediaType::init () {
3344     if (Runtime::TimingsInitialized > runtime->timingstate) {
3345         fit = fit_default;
3346         effective_fit = fit_default;
3347         background_color.init ();
3348         media_opacity.init ();
3349         transition.init ();
3350         QString pg = getAttribute ("paramGroup");
3351         if (!pg.isEmpty ()) {
3352             Node *head = findHeadNode (SMIL::Smil::findSmilNode(this));
3353             if (head) {
3354                 Expression *expr = evaluateExpr(QString("/paramGroup[@id='" + pg + "']/param").toUtf8());
3355                 if (expr) {
3356                     expr->setRoot (head);
3357                     Expression::iterator it, e = expr->end();
3358                     for (it = expr->begin(); it != e; ++it) {
3359                         if (it->node->isElementNode()) {
3360                             Element *e = static_cast <Element*>(it->node);
3361                             QString n = e->getAttribute (Ids::attr_name);
3362                             if (!n.isEmpty ())
3363                                 parseParam (n, e->getAttribute (Ids::attr_value));
3364                         }
3365                     }
3366                     delete expr;
3367                 }
3368             }
3369         }
3370         Mrl::init (); // sets all attributes
3371         for (NodePtr c = firstChild (); c; c = c->nextSibling ())
3372             if (SMIL::id_node_param == c->id)
3373                 c->activate (); // activate param children
3374         runtime->initialize ();
3375     }
3376 }
3377 
3378 void SMIL::MediaType::activate () {
3379     init (); // sets all attributes
3380     setState (state_activated);
3381     for (Attribute *a = attributes ().first (); a; a = a->nextSibling ()) {
3382         QString v = a->value ();
3383         int p = v.indexOf ('{');
3384         if (p > -1) {
3385             int q = v.indexOf ('}', p + 1);
3386             if (q > -1)
3387                 parseParam (a->name (), applySubstitution (this, v, p, q));
3388         }
3389     }
3390     if (!runtime->started ())
3391         runtime->start ();
3392 }
3393 
3394 void SMIL::MediaType::deactivate () {
3395     region_attach.disconnect ();
3396     if (region_node)
3397         convertNode <SMIL::RegionBase> (region_node)->repaint ();
3398     transition.finish (this);
3399     runtime->init ();
3400     Mrl::deactivate ();
3401     (void) surface ();
3402     region_node = nullptr;
3403     postpone_lock = nullptr;
3404 }
3405 
3406 void SMIL::MediaType::defer () {
3407     if (media_info) {
3408         //media_info->pause ();
3409         bool running = unfinished ();
3410         setState (state_deferred);
3411         if (running)
3412             postpone_lock = document ()->postpone ();
3413     }
3414 }
3415 
3416 void SMIL::MediaType::undefer () {
3417     if (runtime->started ()) {
3418         setState (state_began);
3419         if (media_info && media_info->media)
3420             media_info->media->unpause ();
3421         Surface *s = surface ();
3422         if (s)
3423             s->repaint ();
3424     } else {
3425         setState (state_activated);
3426     }
3427     postpone_lock = nullptr;
3428 }
3429 
3430 void SMIL::MediaType::begin () {
3431     if (!src.isEmpty () && !media_info)
3432         prefetch ();
3433     if (media_info && media_info->downloading ()) {
3434         postpone_lock = document ()->postpone ();
3435         state = state_began;
3436         return; // wait for MsgMediaReady
3437     }
3438 
3439     SMIL::RegionBase *r = findRegion (this, param (Ids::attr_region));
3440     transition.cancelTimer (this); // eg transOut and we're repeating
3441     for (NodePtr c = firstChild (); c; c = c->nextSibling ())
3442         if (SMIL::id_node_param != c->id && c != external_tree)
3443             c->activate (); // activate set/animate.. children
3444     if (r) {
3445         region_node = r;
3446         region_attach.connect (r, MsgSurfaceAttach, this);
3447         r->repaint ();
3448         clipStart ();
3449         transition.begin (this, runtime);
3450     } else {
3451         qCWarning(LOG_KMPLAYER_COMMON) << nodeName() << "::begin " << src << " region '" <<
3452             param (Ids::attr_region) << "' not found" << endl;
3453     }
3454     Element::begin ();
3455 }
3456 
3457 void SMIL::MediaType::clipStart () {
3458     if (region_node && region_node->role (RoleDisplay)) {
3459         if (external_tree)
3460             external_tree->activate ();
3461         else if (media_info && media_info->media)
3462             media_info->media->play ();
3463     }
3464 }
3465 
3466 void SMIL::MediaType::clipStop () {
3467     if (runtime->timingstate == Runtime::timings_stopped) {
3468         region_attach.disconnect ();
3469         if (media_info && media_info->media)
3470             media_info->media->stop ();
3471         if (external_tree && external_tree->active ())
3472             external_tree->deactivate ();
3473     }
3474     if (sub_surface)
3475         sub_surface->repaint ();
3476     document_postponed.disconnect ();
3477 }
3478 
3479 void SMIL::MediaType::finish () {
3480     transition.transition_updater.disconnect ();
3481     if (media_info && media_info->media)
3482         media_info->media->pause ();
3483     postpone_lock = nullptr;
3484 
3485     Surface *s = surface ();
3486     if (s)
3487         s->repaint ();
3488     runtime->finish ();
3489 }
3490 
3491 void SMIL::MediaType::reset () {
3492     Mrl::reset ();
3493     runtime->init ();
3494 }
3495 
3496 SRect SMIL::MediaType::calculateBounds () {
3497     SMIL::RegionBase *rb = convertNode <SMIL::RegionBase> (region_node);
3498     if (rb && rb->role (RoleDisplay)) {
3499         SRect rr = rb->region_surface->bounds;
3500         Single x, y, w = size.width, h = size.height;
3501         sizes.calcSizes (this, &rb->sizes, rr.width(), rr.height(), x, y, w, h);
3502         if (fit_default != fit)
3503             effective_fit = fit;
3504         ImageMedia *im;
3505         switch (effective_fit) {
3506             case fit_scroll:
3507             case fit_default:
3508             case fit_hidden:
3509                 if (media_info &&
3510                         (MediaManager::AudioVideo == media_info->type ||
3511                         (MediaManager::Image == media_info->type &&
3512                          (im = static_cast <ImageMedia *>(media_info->media)) &&
3513                          !im->isEmpty () &&
3514                          im->cached_img->flags & ImageData::ImageScalable)))
3515                     effective_fit = fit_meet;
3516                 break;
3517             default:
3518                 break;
3519         }
3520 
3521         if (!size.isEmpty () && w > 0 && h > 0)
3522             switch (effective_fit) {
3523                 case fit_meet: {
3524                     float iasp = 1.0 * size.width / size.height;
3525                     float rasp = 1.0 * w / h;
3526                     if (iasp > rasp)
3527                         h = size.height * w / size.width;
3528                     else
3529                         w = size.width * h / size.height;
3530                     break;
3531                 }
3532                 case fit_scroll:
3533                 case fit_default:
3534                 case fit_hidden:
3535                      w = size.width;
3536                      h = size.height;
3537                      break;
3538                 case fit_slice: {
3539                     float iasp = 1.0 * size.width / size.height;
3540                     float rasp = 1.0 * w / h;
3541                     if (iasp > rasp)
3542                         w = size.width * h / size.height;
3543                     else
3544                         h = size.height * w / size.width;
3545                     break;
3546                 }
3547                 default: {} // fit_fill
3548             }
3549         return SRect (x, y, w, h);
3550     }
3551     return SRect ();
3552 }
3553 
3554 void SMIL::MediaType::message (MessageType msg, void *content) {
3555     switch (msg) {
3556 
3557         case MsgEventPostponed: {
3558             PostponedEvent *pe = static_cast <PostponedEvent *> (content);
3559             if (media_info) {
3560                 if (pe->is_postponed) {
3561                     if (unfinished ()) {
3562                         setState (state_deferred);
3563                         if (media_info->media)
3564                             media_info->media->pause ();
3565                     }
3566                 } else if (state == Node::state_deferred) {
3567                     setState (state_began);
3568                     if (media_info->media)
3569                         media_info->media->unpause ();
3570                 }
3571             }
3572             return;
3573         }
3574 
3575         case MsgSurfaceBoundsUpdate:
3576             if (sub_surface)
3577                 sub_surface->resize (calculateBounds (), !!content);
3578             return;
3579 
3580         case MsgStateFreeze:
3581             clipStop ();
3582             return;
3583 
3584         case MsgChildFinished: {
3585             Posting *post = (Posting *) content;
3586             if (post->source->mrl () &&
3587                     post->source->mrl ()->opener.ptr () == this) {
3588                 post->source->deactivate (); // should only if fill not is freeze or hold
3589             } else if (active ()) {
3590                 if (runtime->state () < Runtime::timings_stopped) {
3591                     if (runtime->started ())
3592                         runtime->tryFinish (); // what about repeat_count ..
3593                     return; // still running, wait for runtime to finish
3594                 }
3595             }
3596             if (active ())
3597                 finish ();
3598             return;
3599         }
3600 
3601         case MsgStateRewind:
3602             if (external_tree) {
3603                 State old = state;
3604                 state = state_deactivated;
3605                 external_tree->reset ();
3606                 state = old;
3607             }
3608             return;
3609 
3610         case MsgMediaPrefetch:
3611             if (content) {
3612                 init ();
3613                 if (!src.isEmpty () && !media_info)
3614                     prefetch ();
3615             } else if (media_info) {
3616                 delete media_info;
3617                 media_info = nullptr;
3618             }
3619             return;
3620 
3621         case MsgMediaReady: {
3622             resolved = true;
3623             Mrl *mrl = external_tree ? external_tree->mrl () : nullptr;
3624             if (mrl)
3625                 size = mrl->size;
3626             postpone_lock = nullptr;
3627             message (MsgSurfaceBoundsUpdate, (void *) true);
3628             if (state == state_began) {
3629                 begin ();
3630                 runtime->tryFinish ();
3631             } else if (state < state_began && parentNode ()) {
3632                 parentNode ()->message (MsgChildReady, this);
3633             }
3634             return;
3635         }
3636 
3637         case MsgMediaFinished:
3638             if (state_deferred == state && postpone_lock)
3639                 state = state_began;
3640             if (unfinished ()) {
3641                 if (runtime->durTime ().durval == Runtime::DurMedia)
3642                     runtime->durTime ().durval = Runtime::DurTimer;
3643                 if (media_info) {
3644                     delete media_info;
3645                     media_info = nullptr;
3646                 }
3647                 postpone_lock = nullptr;
3648                 runtime->tryFinish ();
3649             }
3650             return;
3651 
3652         default:
3653             break;
3654     }
3655     if (!transition.handleMessage (this, runtime, surface (), msg, content)) {
3656         if ((int) msg >= (int) Runtime::DurLastDuration)
3657             Mrl::message (msg, content);
3658         else
3659             runtime->message (msg, content);
3660     }
3661 }
3662 
3663 void *SMIL::MediaType::role (RoleType msg, void *content) {
3664     switch (msg) {
3665 
3666     case RoleReady:
3667         return MsgBool (!media_info || !media_info->downloading ());
3668 
3669     case RoleTiming:
3670         return runtime;
3671 
3672     case RoleDisplay:
3673         return surface ();
3674 
3675     case RoleSizer:
3676         return &sizes;
3677 
3678     case RoleChildDisplay: {
3679         Surface *s = nullptr;
3680         Mrl *mrl = (Mrl *) content;
3681         if (mrl) {
3682             size = mrl->size;
3683             message (MsgSurfaceBoundsUpdate);
3684             s = surface ();
3685         }
3686         return s;
3687     }
3688 
3689     case RolePlaylist:
3690         return nullptr;
3691 
3692     case RoleReceivers: {
3693         MessageType m = (MessageType) (long) content;
3694         ConnectionList *l = mouse_listeners.receivers (m);
3695         if (l)
3696             return l;
3697         if (MsgSurfaceAttach == m)
3698             return &m_MediaAttached;
3699         if (MsgChildTransformedIn == m)
3700             return &transition.m_TransformedIn;
3701     } // fall through
3702 
3703     default:
3704         break;
3705     }
3706     void *response = runtime->role (msg, content);
3707     if (response == MsgUnhandled)
3708         return Mrl::role (msg, content);
3709     return response;
3710 }
3711 
3712 
3713 Surface *SMIL::MediaType::surface () {
3714     if (!runtime->active ()) {
3715         if (sub_surface)
3716             sub_surface->remove ();
3717         sub_surface = nullptr;
3718     } else if (!sub_surface && region_node) {
3719         Surface *rs = (Surface *) region_node->role (RoleDisplay);
3720         if (rs) {
3721             sub_surface = rs->createSurface (this, SRect ());
3722             sub_surface->setBackgroundColor (background_color.color);
3723             message (MsgSurfaceBoundsUpdate);
3724         }
3725     }
3726     return sub_surface.ptr ();
3727 }
3728 
3729 //-----------------------------------------------------------------------------
3730 
3731 namespace {
3732     class SvgElement : public Element {
3733         QByteArray tag;
3734         NodePtrW image;
3735 
3736     public:
3737         SvgElement (NodePtr &doc, Node *img, const QByteArray &t, short id=0)
3738             : Element (doc, id), tag (t), image (img) {}
3739 
3740         void parseParam (const TrieString &name, const QString &val) override {
3741             setAttribute (name, val);
3742             Mrl *mrl = image ? image->mrl () : nullptr;
3743             if (mrl && mrl->media_info &&
3744                     MediaManager::Image == mrl->media_info->type) {
3745                 ImageMedia *im=static_cast<ImageMedia*>(mrl->media_info->media);
3746                 if (im)
3747                     im->updateRender ();
3748             }
3749         }
3750 
3751         Node *childFromTag (const QString & tag) override {
3752             return new SvgElement (m_doc, image.ptr (), tag.toLatin1());
3753         }
3754 
3755         const char *nodeName () const override {
3756             return tag.constData ();
3757         }
3758     };
3759 }
3760 
3761 SMIL::RefMediaType::RefMediaType (NodePtr &d, const QByteArray &t)
3762  : SMIL::MediaType (d, t, id_node_ref) {}
3763 
3764 Node *SMIL::RefMediaType::childFromTag (const QString & tag) {
3765     QByteArray ba = tag.toLatin1 ();
3766     const char *taglatin = ba.constData ();
3767     if (!strcmp (taglatin, "imfl"))
3768         return new RP::Imfl (m_doc);
3769     else if (!strcmp (taglatin, "svg"))
3770         return new SvgElement (m_doc, this, ba, id_node_svg);
3771     Node *n = fromXMLDocumentTag (m_doc, tag);
3772     if (n)
3773         return n;
3774     return SMIL::MediaType::childFromTag (tag);
3775 }
3776 
3777 void SMIL::RefMediaType::prefetch () {
3778     if (!src.isEmpty ()) {
3779         Node *n = findChildWithId (this, id_node_svg);
3780         if (n)
3781             removeChild (n);
3782         if (!media_info)
3783             media_info = new MediaInfo (this, MediaManager::Any);
3784         resolved = media_info->wget (absolutePath ());
3785     }
3786 }
3787 
3788 void SMIL::RefMediaType::activate () {
3789     MediaType::activate ();
3790 
3791     if (src.isEmpty () && (!media_info || !media_info->media)) {
3792         Node *n = findChildWithId (this, id_node_svg);
3793         if (n) {
3794             if (!media_info)
3795                 media_info = new MediaInfo (this, MediaManager::Image);
3796             media_info->media = new ImageMedia (this);
3797             message (MsgMediaReady);
3798         }
3799     }
3800 }
3801 
3802 void SMIL::RefMediaType::clipStart () {
3803     if (media_info && media_info->media &&
3804             media_info->media->type () != MediaManager::Image &&
3805             region_node && !external_tree && !src.isEmpty()) {
3806         repeat = runtime->repeat_count == Runtime::DurIndefinite
3807             ? 9998 : runtime->repeat_count;
3808         runtime->repeat_count = 1;
3809         document_postponed.connect (document(), MsgEventPostponed, this);
3810     }
3811     MediaType::clipStart ();
3812 }
3813 
3814 void SMIL::RefMediaType::finish () {
3815     if (media_info && media_info->media &&
3816             media_info->media->type () != MediaManager::Image &&
3817             runtime->durTime ().durval == Runtime::DurMedia)
3818         runtime->durTime ().durval = Runtime::DurTimer;//reset to make this finish
3819     MediaType::finish ();
3820 }
3821 
3822 void SMIL::RefMediaType::begin () {
3823     MediaType::begin ();
3824     if (media_info && media_info->media &&
3825             media_info->media->type () != MediaManager::Image &&
3826             0 == runtime->durTime ().offset &&
3827             Runtime::DurMedia == runtime->endTime ().durval)
3828         runtime->durTime ().durval = Runtime::DurMedia; // duration of clip
3829 }
3830 
3831 void SMIL::RefMediaType::accept (Visitor * v) {
3832     v->visit (this);
3833 }
3834 
3835 void SMIL::RefMediaType::message (MessageType msg, void *content) {
3836     if (media_info &&
3837             media_info->media &&
3838             media_info->media->type () == MediaManager::Image) {
3839         switch (msg) {
3840 
3841         case MsgMediaUpdated: {
3842             Surface *s = surface ();
3843             if (s) {
3844                 s->markDirty ();
3845                 s->repaint ();
3846             }
3847             if (state >= state_finished)
3848                 clipStop ();
3849             return;
3850         }
3851 
3852         case MsgChildFinished:
3853             if (id_node_svg == ((Posting *) content)->source->id)
3854                 return;
3855 
3856         case MsgMediaReady:
3857             if (media_info) {
3858                 ImageMedia *im = static_cast <ImageMedia *> (media_info->media);
3859                 if (im && !im->isEmpty ())
3860                     im->sizes (size);
3861             }
3862             break;
3863 
3864         default:
3865             break;
3866         }
3867     }
3868     MediaType::message (msg, content);
3869 }
3870 
3871 void *SMIL::RefMediaType::role (RoleType msg, void *content)
3872 {
3873     if (RolePlaylist == msg) {
3874         if (caption ().isEmpty () &&
3875                 !src.isEmpty () &&
3876                 !external_tree &&
3877                 (m_type == "video" || m_type == "audio"))
3878             setCaption (src);
3879         return !caption ().isEmpty () ? (PlaylistRole *) this : nullptr;
3880     }
3881     return MediaType::role (msg, content);
3882 }
3883 
3884 Node::PlayType SMIL::RefMediaType::playType ()
3885 {
3886     if (media_info && media_info->media)
3887         switch (media_info->media->type ()) {
3888         case MediaManager::AudioVideo:
3889             return play_type_video;
3890         case MediaManager::Image:
3891             return play_type_image;
3892         default:
3893             break;
3894         }
3895     return play_type_unknown;
3896 }
3897 
3898 //-----------------------------------------------------------------------------
3899 
3900 SMIL::TextMediaType::TextMediaType (NodePtr & d)
3901     : SMIL::MediaType (d, "text", id_node_text) {}
3902 
3903 void SMIL::TextMediaType::init () {
3904     if (Runtime::TimingsInitialized > runtime->timingstate) {
3905         font_size = TextMedia::defaultFontSize ();
3906         font_color = 0;
3907         font_name = "sans";
3908         halign = align_left;
3909 
3910         MediaType::init ();
3911     }
3912 }
3913 
3914 void SMIL::TextMediaType::prefetch () {
3915     if (!media_info) {
3916         media_info = new MediaInfo (this, MediaManager::Text);
3917         media_info->wget (absolutePath ());
3918     }
3919 }
3920 
3921 void
3922 SMIL::TextMediaType::parseParam (const TrieString &name, const QString &val) {
3923     if (name == "color" || name == "fontColor") {
3924         font_color = val.isEmpty () ? 0 : rgbFromValue (val);
3925     } else if (name == "fontFace") {
3926         if (val.toLower ().indexOf ("sans" ) < 0)
3927             font_name = "serif";
3928     } else if (name == "font-size" || name == "fontPtSize") {
3929         font_size = val.isEmpty() ? TextMedia::defaultFontSize() : (int)SizeType (val).size ();
3930     } else if (name == "fontSize") {
3931         font_size += val.isEmpty() ? TextMedia::defaultFontSize() : (int)SizeType (val).size ();
3932     } else if (name == "hAlign") {
3933         QByteArray ba = val.toLatin1 ();
3934         const char *cval = ba.constData ();
3935         if (!cval)
3936             halign = align_left;
3937         else if (!strcmp (cval, "center"))
3938             halign = align_center;
3939         else if (!strcmp (cval, "right"))
3940             halign = align_right;
3941         else
3942             halign = align_left;
3943     // TODO: expandTabs fontBackgroundColor fontSize fontStyle fontWeight hAlig vAlign wordWrap
3944     } else {
3945         MediaType::parseParam (name, val);
3946         return;
3947     }
3948     if (sub_surface) {
3949         size = SSize ();
3950         sub_surface->resize (calculateBounds (), true);
3951     }
3952 }
3953 
3954 void SMIL::TextMediaType::accept (Visitor * v) {
3955     v->visit (this);
3956 }
3957 
3958 //-----------------------------------------------------------------------------
3959 
3960 SMIL::Brush::Brush (NodePtr & d)
3961     : SMIL::MediaType (d, "brush", id_node_brush) {}
3962 
3963 void SMIL::Brush::init () {
3964     if (Runtime::TimingsInitialized > runtime->timingstate)
3965         color.init ();
3966     MediaType::init ();
3967 }
3968 
3969 void SMIL::Brush::accept (Visitor * v) {
3970     v->visit (this);
3971 }
3972 
3973 void SMIL::Brush::parseParam (const TrieString &param, const QString &val) {
3974     if (param == "color") {
3975         color.setColor (val);
3976         Surface *s = surface ();
3977         if (s)
3978             s->repaint ();
3979     } else {
3980         MediaType::parseParam (param, val);
3981     }
3982 }
3983 
3984 //-----------------------------------------------------------------------------
3985 
3986 SMIL::SmilText::SmilText (NodePtr &d)
3987  : Element (d, id_node_smil_text),
3988    runtime (new Runtime (this)) {}
3989 
3990 SMIL::SmilText::~SmilText () {
3991     delete runtime;
3992 }
3993 
3994 void SMIL::SmilText::init () {
3995     if (Runtime::TimingsInitialized > runtime->timingstate) {
3996         background_color.init ();
3997         transition.init ();
3998         props.init ();
3999         RegionBase *rb = static_cast<SMIL::RegionBase *> (region_node.ptr ());
4000         if (rb) {
4001             props.mask (rb->font_props);
4002             media_opacity = rb->media_opacity;
4003         } else {
4004             media_opacity.init ();
4005         }
4006         Element::init ();
4007         runtime->initialize ();
4008     }
4009 }
4010 
4011 void SMIL::SmilText::activate () {
4012     SMIL::RegionBase *r = findRegion (this, param (Ids::attr_region));
4013     if (r)
4014         region_node = r;
4015     init (); // sets all attributes
4016     setState (state_activated);
4017     runtime->start ();
4018 }
4019 
4020 void SMIL::SmilText::begin () {
4021     RegionBase *rb = static_cast<SMIL::RegionBase *> (region_node.ptr ());
4022     transition.cancelTimer (this); // eg transOut and we're repeating
4023     if (rb) {
4024         region_attach.connect (rb, MsgSurfaceAttach, this);
4025         rb->repaint ();
4026         transition.begin (this, runtime);
4027     }
4028     setState (state_began);
4029     for (NodePtr c = firstChild (); c; c = c->nextSibling ())
4030         c->activate ();
4031 
4032 }
4033 
4034 void SMIL::SmilText::finish () {
4035     transition.transition_updater.disconnect ();
4036     runtime->finish ();
4037 }
4038 
4039 void SMIL::SmilText::deactivate () {
4040     transition.finish (this);
4041     region_attach.disconnect ();
4042     if (text_surface) {
4043         text_surface->repaint ();
4044         text_surface->remove ();
4045         text_surface = nullptr;
4046     }
4047     sizes.resetSizes ();
4048     runtime->init ();
4049     Element::deactivate ();
4050 }
4051 
4052 void SMIL::SmilText::reset () {
4053     runtime->init ();
4054     Element::reset ();
4055 }
4056 
4057 Node *SMIL::SmilText::childFromTag (const QString &tag) {
4058     QByteArray ba = tag.toLatin1 ();
4059     const char *ctag = ba.constData ();
4060     if (!strcmp (ctag, "tev"))
4061         return new TemporalMoment (m_doc, id_node_tev, ba);
4062     if (!strcmp (ctag, "clear"))
4063         return new TemporalMoment (m_doc, id_node_clear, ba);
4064     return fromTextFlowGroup (m_doc, tag);
4065 }
4066 
4067 void SMIL::SmilText::parseParam (const TrieString &name, const QString &value) {
4068     if (props.parseParam (name, value)
4069             || sizes.setSizeParam (name, value)
4070             || parseBackgroundParam (background_color, name, value)
4071             || parseMediaOpacityParam (media_opacity, name, value)) {
4072         message (MsgMediaUpdated);
4073     } else if (!runtime->parseParam (name, value)
4074             && !parseTransitionParam (this, transition, runtime, name, value)) {
4075         Element::parseParam (name, value);
4076     }
4077 }
4078 
4079 void SMIL::SmilText::updateBounds (bool remove) {
4080     if (text_surface) {
4081         SMIL::RegionBase *rb = convertNode <SMIL::RegionBase> (region_node);
4082         Surface *rs = (Surface *) region_node->role (RoleDisplay);
4083         if (rs) {
4084             SRect b = rs->bounds;
4085             Single x, y, w = size.width, h = size.height;
4086             sizes.calcSizes (this, &rb->sizes, b.width(), b.height(), x, y, w, h);
4087             if (!size.isEmpty () && w > 0 && h > 0) {
4088                 w = size.width;
4089                 h = size.height;
4090             }
4091             text_surface->resize (SRect (x, y, w, h), remove);
4092         }
4093     }
4094 }
4095 
4096 void SMIL::SmilText::message (MessageType msg, void *content) {
4097     switch (msg) {
4098 
4099         case MsgSurfaceBoundsUpdate:
4100             updateBounds (!!content);
4101             return;
4102 
4103         case MsgStateFreeze:
4104             if (!runtime->active () && text_surface) {
4105                 text_surface->repaint ();
4106                 text_surface->remove ();
4107                 text_surface = nullptr;
4108             }
4109             return;
4110 
4111         case MsgChildFinished:
4112             if (unfinished ())
4113                 runtime->tryFinish ();
4114             return;
4115 
4116         case MsgMediaUpdated:
4117             if (surface ()) {
4118                 text_surface->parentNode ()->repaint ();
4119                 text_surface->remove ();
4120                 text_surface = nullptr;
4121             }
4122             return;
4123 
4124         default:
4125             break;
4126     }
4127     if (!transition.handleMessage (this, runtime, surface (), msg, content)) {
4128         if ((int) msg >= (int) Runtime::DurLastDuration)
4129             Element::message (msg, content);
4130         else
4131             runtime->message (msg, content);
4132     }
4133 }
4134 
4135 void *SMIL::SmilText::role (RoleType msg, void *content) {
4136     switch (msg) {
4137 
4138     case RoleTiming:
4139         return runtime;
4140 
4141     case RoleDisplay:
4142         return surface ();
4143 
4144     case RoleSizer:
4145         return &sizes;
4146 
4147     case RoleReceivers: {
4148         MessageType msgt = (MessageType) (long) content;
4149         ConnectionList *l = mouse_listeners.receivers (msgt);
4150         if (l)
4151             return l;
4152         if (MsgSurfaceAttach == msgt)
4153             return &media_attached;
4154         if (MsgChildTransformedIn == msgt)
4155             return &transition.m_TransformedIn;
4156     } // fall through
4157 
4158     default:
4159         break;
4160     }
4161     void *response = runtime->role (msg, content);
4162     if (response == MsgUnhandled)
4163         return Element::role (msg, content);
4164     return response;
4165 }
4166 
4167 Surface *SMIL::SmilText::surface () {
4168     if (!runtime->active ()) {
4169         if (text_surface) {
4170             text_surface->remove ();
4171             text_surface = nullptr;
4172         }
4173     } else if (region_node && !text_surface) {
4174         Surface *rs = (Surface *) region_node->role (RoleDisplay);
4175         if (rs) {
4176             text_surface = rs->createSurface (this, SRect ());
4177             text_surface->setBackgroundColor (background_color.color);
4178             size = SSize ();
4179             updateBounds (false);
4180         }
4181     }
4182     return text_surface.ptr ();
4183 }
4184 
4185 //-----------------------------------------------------------------------------
4186 
4187 void SmilTextProperties::init () {
4188     font_color = -1;
4189     background_color = -1;
4190     text_direction = DirInherit;
4191     font_family = "sans";
4192     font_size = -1;
4193     font_style = StyleInherit;
4194     font_weight = WeightInherit;
4195     text_mode = ModeInherit;
4196     text_place = PlaceInherit;
4197     text_style = "";
4198     text_wrap = WrapInherit;
4199     space = SpaceDefault;
4200     text_writing = WritingLrTb;
4201     text_align = AlignInherit;
4202 }
4203 
4204 bool SmilTextProperties::parseParam(const TrieString &name, const QString &val) {
4205     if (name == "textWrap") {
4206         // { Wrap, NoWrap, WrapInherit } text_wrap;
4207     } else if (name == "space" /*xml:space*/) {
4208         // { SpaceDefault, SpacePreserve } space;
4209     } else if (name == "textAlign") {
4210         if (val == "left")
4211             text_align = AlignLeft;
4212         else if (val == "center")
4213             text_align = AlignCenter;
4214         else if (val == "right")
4215             text_align = AlignRight;
4216         // start, end
4217         else
4218             text_align = AlignInherit;
4219     } else if (name == "textBackgroundColor") {
4220         background_color = rgbFromValue (val);
4221     } else if (name == "textColor") {
4222         font_color = rgbFromValue (val);
4223     } else if (name == "textDirection") {
4224         if (val == "ltr")
4225             text_direction = DirLtr;
4226         else if (val == "rtl")
4227             text_direction = DirRtl;
4228         else
4229             text_direction = DirInherit;
4230         //  DirLtro, DirRtlo
4231     } else if (name == "textFontFamily") {
4232         font_family = val;
4233     } else if (name == "textFontSize") {
4234         font_size = SizeType (val);
4235     } else if (name == "textFontStyle") {
4236         if (val == "normal")
4237             font_style = StyleNormal;
4238         else if (val == "italic")
4239             font_style = StyleItalic;
4240         else if (val == "oblique")
4241             font_style = StyleOblique;
4242         else if (val == "reverseOblique")
4243             font_style = StyleRevOblique;
4244         else
4245             font_style = StyleInherit;
4246     } else if (name == "textFontWeight") {
4247         if (val == "normal")
4248             font_weight = WeightNormal;
4249         else if (val == "bold")
4250             font_weight = WeightBold;
4251         else
4252             font_weight = WeightInherit;
4253     } else if (name == "textMode") {
4254         // { ModeAppend, ModeReplace, ModeInherit } text_mode;
4255     } else if (name == "textPlace") {
4256         //enum { PlaceStart, PlaceCenter, PlaceEnd, PlaceInherit } text_place;
4257     } else if (name == "textStyle") {
4258         text_style = val;
4259     } else if (name == "textWritingMode") {
4260         // { WritingLrTb, WritingRlTb, WritingTbLr, WritingTbRl } text_writing;
4261     } else {
4262         return false;
4263     }
4264     return true;
4265 }
4266 
4267 void SmilTextProperties::mask (const SmilTextProperties &props) {
4268     if ((float)props.font_size.size () > 0.1)
4269         font_size = props.font_size;
4270     if (props.font_color > -1)
4271         font_color = props.font_color;
4272     if (props.background_color > -1)
4273         background_color = props.background_color;
4274     if (StyleInherit != props.font_style)
4275         font_style = props.font_style;
4276     if (WeightInherit != props.font_weight)
4277         font_weight = props.font_weight;
4278     if (AlignInherit != props.text_align)
4279         text_align = props.text_align;
4280     font_family = props.font_family;
4281 }
4282 
4283 SMIL::TextFlow::TextFlow (NodePtr &doc, short id, const QByteArray &t)
4284  : Element (doc, id), tag (t) {}
4285 
4286 SMIL::TextFlow::~TextFlow () {}
4287 
4288 void SMIL::TextFlow::init () {
4289     props.init ();
4290     Element::init ();
4291 }
4292 
4293 void SMIL::TextFlow::activate () {
4294     init ();
4295     Element::activate ();
4296 }
4297 
4298 Node *SMIL::TextFlow::childFromTag (const QString &tag) {
4299     return fromTextFlowGroup (m_doc, tag);
4300 }
4301 
4302 void SMIL::TextFlow::parseParam(const TrieString &name, const QString &val) {
4303     if (!props.parseParam (name, val))
4304         Element::parseParam (name, val);
4305 }
4306 
4307 SMIL::TemporalMoment::TemporalMoment (NodePtr &doc, short id, const QByteArray &t)
4308  : Element (doc, id),
4309    runtime (new Runtime (this)),
4310    tag (t) {}
4311 
4312 SMIL::TemporalMoment::~TemporalMoment () {
4313     delete runtime;
4314 }
4315 
4316 void SMIL::TemporalMoment::init () {
4317     if (Runtime::TimingsInitialized > runtime->timingstate) {
4318         Element::init ();
4319         runtime->initialize ();
4320     }
4321 }
4322 
4323 void SMIL::TemporalMoment::activate () {
4324     init ();
4325     setState (state_activated);
4326     runtime->start ();
4327 }
4328 
4329 void SMIL::TemporalMoment::begin () {
4330     parentNode ()->message (MsgMediaUpdated);
4331     Element::begin ();
4332 }
4333 
4334 void SMIL::TemporalMoment::deactivate () {
4335     runtime->init ();
4336     Element::deactivate ();
4337 }
4338 
4339 Node *SMIL::TemporalMoment::childFromTag (const QString & tag) {
4340     return fromTextFlowGroup (m_doc, tag);
4341 }
4342 
4343 void SMIL::TemporalMoment::parseParam (const TrieString &name, const QString &value) {
4344     // TODO: next
4345     if (!runtime->parseParam (name, value))
4346         Element::parseParam (name, value);
4347 }
4348 
4349 void SMIL::TemporalMoment::message (MessageType msg, void *content) {
4350     if ((int) msg >= (int) Runtime::DurLastDuration)
4351         Element::message (msg, content);
4352     else
4353         runtime->message (msg, content);
4354 }
4355 
4356 void *SMIL::TemporalMoment::role (RoleType msg, void *content) {
4357     if (RoleTiming == msg)
4358         return runtime;
4359     void *response = runtime->role (msg, content);
4360     if (response == MsgUnhandled)
4361         return Element::role (msg, content);
4362     return response;
4363 }
4364 
4365 //-----------------------------------------------------------------------------
4366 
4367 SMIL::StateValue::StateValue (NodePtr &d, short _id)
4368  : Element (d, _id), ref (nullptr), runtime (new Runtime (this)) {
4369 }
4370 
4371 SMIL::StateValue::~StateValue () {
4372     delete runtime;
4373     delete ref;
4374 }
4375 
4376 void SMIL::StateValue::init () {
4377     if (Runtime::TimingsInitialized > runtime->timingstate) {
4378         SMIL::Smil *smil = SMIL::Smil::findSmilNode (this);
4379         if (smil)
4380             state = smil->state_node.ptr ();
4381         Element::init ();
4382         runtime->initialize ();
4383     }
4384 }
4385 
4386 void SMIL::StateValue::activate () {
4387     init ();
4388     setState (state_activated);
4389     for (Attribute *a = attributes ().first (); a; a = a->nextSibling ()) {
4390         QString v = a->value ();
4391         int p = v.indexOf ('{');
4392         if (p > -1) {
4393             int q = v.indexOf ('}', p + 1);
4394             if (q > -1)
4395                 parseParam (a->name (), applySubstitution (this, v, p, q));
4396         }
4397     }
4398     runtime->start ();
4399 }
4400 
4401 void SMIL::StateValue::finish () {
4402     runtime->finish ();
4403 }
4404 
4405 void SMIL::StateValue::deactivate () {
4406     if (unfinished ())
4407         finish ();
4408     delete ref;
4409     ref = nullptr;
4410     runtime->init ();
4411     Element::deactivate ();
4412 }
4413 
4414 void SMIL::StateValue::reset () {
4415     runtime->init ();
4416     Element::reset ();
4417 }
4418 
4419 void SMIL::StateValue::parseParam (const TrieString &para, const QString &val) {
4420     if (para == Ids::attr_value) {
4421         value = val;
4422     } else if (para == "ref") {
4423         delete ref;
4424         if (state)
4425             ref = evaluateExpr(val.toUtf8(), "data");
4426         else
4427             ref = nullptr;
4428     } else if (!runtime->parseParam (para, val)) {
4429         Element::parseParam (para, val);
4430     }
4431 }
4432 
4433 void SMIL::StateValue::message (MessageType msg, void *data) {
4434     if ((int) msg >= (int) Runtime::DurLastDuration)
4435         Element::message (msg, data);
4436     else
4437         runtime->message (msg, data);
4438 }
4439 
4440 void *SMIL::StateValue::role (RoleType msg, void *data) {
4441     switch (msg) {
4442 
4443     case RoleTiming:
4444         return runtime;
4445 
4446     default:
4447         break;
4448     }
4449     void *response = runtime->role (msg, data);
4450     if (response == MsgUnhandled)
4451         return Element::role (msg, data);
4452     return response;
4453 }
4454 
4455 //-----------------------------------------------------------------------------
4456 
4457 void SMIL::NewValue::init () {
4458     where = SMIL::State::child;
4459     StateValue::init ();
4460 }
4461 
4462 void SMIL::NewValue::begin () {
4463     SMIL::State *st = static_cast <SMIL::State *> (state.ptr ());
4464     if (name.isEmpty () || !st) {
4465         qCWarning(LOG_KMPLAYER_COMMON) << "name is empty or no state";
4466     } else {
4467         if (!ref)
4468             ref = evaluateExpr ("/data");
4469         ref->setRoot (st);
4470         Expression::iterator it = ref->begin(), e = ref->end();
4471         if (it != e && it->node) {
4472             if (name.startsWith(QChar('@')) && it->node->isElementNode())
4473                 static_cast<Element*>(it->node)->setAttribute(name.mid(1), value);
4474             else
4475                 st->newValue(it->node, where, name, value);
4476         }
4477     }
4478 }
4479 
4480 void SMIL::NewValue::parseParam (const TrieString &para, const QString &val) {
4481     if (para == Ids::attr_name)
4482         name = val;
4483     else if (para == "where") {
4484         if (val == "before")
4485             where = SMIL::State::before;
4486         else if (val == "after")
4487             where = SMIL::State::after;
4488         else
4489             where = SMIL::State::child;
4490     } else {
4491         StateValue::parseParam (para, val);
4492     }
4493 }
4494 
4495 //-----------------------------------------------------------------------------
4496 
4497 void SMIL::SetValue::begin () {
4498     SMIL::State *st = static_cast <SMIL::State *> (state.ptr ());
4499     if (!ref || !st) {
4500         qCWarning(LOG_KMPLAYER_COMMON) << "ref is empty or no state";
4501     } else {
4502         ref->setRoot (st);
4503         Expression::iterator it = ref->begin(), e = ref->end();
4504         if (it != e && it->node) {
4505             if (it->attr && it->node->isElementNode ())
4506                 static_cast<Element*>(it->node)->setAttribute(it->attr->name(), value);
4507             else
4508                 st->setValue(it->node, value);
4509         }
4510     }
4511 }
4512 
4513 //-----------------------------------------------------------------------------
4514 
4515 void SMIL::DelValue::begin () {
4516     SMIL::State *st = static_cast <SMIL::State *> (state.ptr ());
4517     if (!ref || !st) {
4518         qCWarning(LOG_KMPLAYER_COMMON) << "ref is empty or no state";
4519     } else {
4520         ref->setRoot (st);
4521         Expression::iterator it = ref->begin(), e = ref->end();
4522         while (it != e) {
4523             if (it->attr && it->node->isElementNode ())
4524                 static_cast<Element*>(it->node)->setAttribute(it->attr->name(), QString());
4525             else
4526                 it->node->parentNode()->removeChild(it->node);
4527             ref->setRoot (st);
4528             it = ref->begin();
4529         }
4530     }
4531 }
4532 
4533 //-----------------------------------------------------------------------------
4534 
4535 void SMIL::Send::init () {
4536     method = SMIL::State::get;
4537     replace = SMIL::State::instance;
4538     StateValue::init ();
4539 }
4540 
4541 void SMIL::Send::begin () {
4542     SMIL::State *st = static_cast <SMIL::State *> (state.ptr ());
4543     if (action.isEmpty () || !st) {
4544         qCWarning(LOG_KMPLAYER_COMMON) << "action is empty or no state";
4545     } else {
4546         Smil *s = SMIL::Smil::findSmilNode (this);
4547         if (s) {
4548             delete media_info;
4549             media_info = new MediaInfo (this, MediaManager::Text);
4550             Mrl *mrl = s->parentNode () ? s->parentNode ()->mrl () : nullptr;
4551             QString url = mrl ? QUrl(mrl->absolutePath()).resolved(QUrl(action)).url() : action;
4552             if (SMIL::State::get == method && replace == SMIL::State::instance) {
4553                 // TODO compose GET url
4554                 media_info->wget (url, st->domain ());
4555             } else // TODO ..
4556                 qCDebug(LOG_KMPLAYER_COMMON, "unsupported method %d replace %d", method, replace);
4557         }
4558     }
4559 }
4560 
4561 void SMIL::Send::deactivate () {
4562     delete media_info;
4563     media_info = nullptr;
4564     action.clear ();
4565     StateValue::deactivate ();
4566 }
4567 
4568 void SMIL::Send::parseParam (const TrieString &para, const QString &val) {
4569     if (para == "action") {
4570         action = val;
4571     } else if (para == "method") {
4572         if (val == "put")
4573             method = SMIL::State::put;
4574         else
4575             method = SMIL::State::get;
4576     } else if (para == "replace") {
4577         if (val == "all")
4578             replace = SMIL::State::all;
4579         else if (val == "none")
4580             replace = SMIL::State::none;
4581         else
4582             replace = SMIL::State::instance;
4583     } else if (para == "target") {
4584         delete ref;
4585         if (state)
4586             ref = evaluateExpr(val.toUtf8(), "data");
4587         else
4588             ref = nullptr;
4589     } else {
4590         StateValue::parseParam (para, val);
4591     }
4592 }
4593 
4594 void SMIL::Send::message (MessageType msg, void *content) {
4595     SMIL::State *st = static_cast <SMIL::State *> (state.ptr ());
4596     switch (msg) {
4597 
4598     case MsgMediaReady: {
4599         Node *target = nullptr;
4600         if (!ref && SMIL::State::instance == replace)
4601             ref = evaluateExpr ("/data");
4602         if (ref) {
4603             ref->setRoot (st);
4604             Expression::iterator it = ref->begin(), e = ref->end();
4605             if (it != e)
4606                 target = it->node;
4607         }
4608         if (target) {
4609             Node *parent = target->parentNode ();
4610             Node *next = target->nextSibling ();
4611             bool changed = target->firstChild ();
4612             target->clearChildren ();
4613             if (media_info && media_info->media) {
4614                 QTextStream in (&((TextMedia *)media_info->media)->text);
4615                 readXML (target, in, QString ());
4616                 if (target->firstChild ()) {
4617                     NodePtr store = target->firstChild ();
4618                     parent->removeChild (target);
4619                     parent->insertBefore (store, next);
4620                     target = store;
4621                     changed = true;
4622                 }
4623             }
4624             if (changed)
4625                 st->stateChanged (target);
4626         }
4627         delete media_info;
4628         media_info = nullptr;
4629         return;
4630     }
4631     default:
4632         break;
4633     }
4634     StateValue::message (msg, content);
4635 }
4636 
4637 //-----------------------------------------------------------------------------
4638 
4639 SMIL::AnimateGroup::AnimateGroup (NodePtr &d, short _id)
4640  : Element (d, _id),
4641    runtime (new Runtime (this)),
4642    modification_id (-1) {}
4643 
4644 SMIL::AnimateGroup::~AnimateGroup () {
4645     delete runtime;
4646 }
4647 
4648 void SMIL::AnimateGroup::parseParam (const TrieString &name, const QString &val) {
4649     if (name == Ids::attr_target || name == "targetElement") {
4650         target_id = val;
4651     } else if (name == "attribute" || name == "attributeName") {
4652         changed_attribute = TrieString (val);
4653     } else if (name == "to") {
4654         change_to = val;
4655     } else if (!runtime->parseParam (name, val)) {
4656         Element::parseParam (name, val);
4657     }
4658 }
4659 
4660 void SMIL::AnimateGroup::init () {
4661     if (Runtime::TimingsInitialized > runtime->timingstate) {
4662         Element::init ();
4663         runtime->initialize ();
4664     }
4665 }
4666 
4667 void SMIL::AnimateGroup::activate () {
4668     init ();
4669     setState (state_activated);
4670     runtime->start ();
4671 }
4672 
4673 /**
4674  * animation finished
4675  */
4676 void SMIL::AnimateGroup::finish () {
4677     runtime->finish ();
4678 }
4679 
4680 void SMIL::AnimateGroup::reset () {
4681     Element::reset ();
4682     target_id.truncate (0);
4683     runtime->init ();
4684 }
4685 
4686 void SMIL::AnimateGroup::deactivate () {
4687     restoreModification ();
4688     if (unfinished ())
4689         finish ();
4690     runtime->init ();
4691     Element::deactivate ();
4692 }
4693 
4694 void SMIL::AnimateGroup::message (MessageType msg, void *data) {
4695     switch (msg) {
4696 
4697         case MsgStateFreeze:
4698             if (!runtime->active ())
4699                 restoreModification ();
4700             return;
4701 
4702         case MsgStateRewind:
4703             restoreModification ();
4704             return;
4705 
4706         default:
4707             break;
4708     }
4709     if ((int) msg >= (int) Runtime::DurLastDuration)
4710         Element::message (msg, data);
4711     else
4712         runtime->message (msg, data);
4713 }
4714 
4715 void *SMIL::AnimateGroup::role (RoleType msg, void *data) {
4716     switch (msg) {
4717 
4718     case RoleTiming:
4719         return runtime;
4720 
4721     default:
4722         break;
4723     }
4724     void *response = runtime->role (msg, data);
4725     if (response == MsgUnhandled)
4726         return Element::role (msg, data);
4727     return response;
4728 }
4729 
4730 
4731 void SMIL::AnimateGroup::restoreModification () {
4732     if (modification_id > -1 && target_element &&
4733             target_element->state > Node::state_init) {
4734         convertNode <Element> (target_element)->resetParam (
4735                 changed_attribute, modification_id);
4736     }
4737     modification_id = -1;
4738 }
4739 
4740 Node *SMIL::AnimateGroup::targetElement () {
4741     if (target_id.isEmpty ()) {
4742         for (Node *p = parentNode(); p; p =p->parentNode())
4743             if (SMIL::id_node_first_mediatype <= p->id &&
4744                     SMIL::id_node_last_mediatype >= p->id) {
4745                 target_element = p;
4746                 break;
4747             }
4748     } else {
4749         target_element = findLocalNodeById (this, target_id);
4750     }
4751     return target_element.ptr ();
4752 }
4753 
4754 //-----------------------------------------------------------------------------
4755 
4756 void SMIL::Set::begin () {
4757     restoreModification ();
4758     Element *target = static_cast <Element *> (targetElement ());
4759     if (target)
4760         target->setParam (changed_attribute, change_to, &modification_id);
4761     else
4762         qCWarning(LOG_KMPLAYER_COMMON) << "target element not found" << endl;
4763     AnimateGroup::begin ();
4764 }
4765 
4766 //-----------------------------------------------------------------------------
4767 /*
4768 //http://en.wikipedia.org/wiki/B%C3%A9zier_curve
4769 typedef struct {
4770     float x;
4771     float y;
4772 } Point2D;
4773 
4774 static Point2D PointOnCubicBezier (Point2D *cp, float t) {
4775     float   ax, bx, cx;
4776     float   ay, by, cy;
4777     float   tSquared, tCubed;
4778     Point2D result;
4779 
4780     // calculate the polynomial coefficients
4781 
4782     cx = 3.0 * (cp[1].x - cp[0].x);
4783     bx = 3.0 * (cp[2].x - cp[1].x) - cx;
4784     ax = cp[3].x - cp[0].x - cx - bx;
4785 
4786     cy = 3.0 * (cp[1].y - cp[0].y);
4787     by = 3.0 * (cp[2].y - cp[1].y) - cy;
4788     ay = cp[3].y - cp[0].y - cy - by;
4789 
4790     // calculate the curve point at parameter value t
4791 
4792     tSquared = t * t;
4793     tCubed = tSquared * t;
4794 
4795     result.x = (ax * tCubed) + (bx * tSquared) + (cx * t) + cp[0].x;
4796     result.y = (ay * tCubed) + (by * tSquared) + (cy * t) + cp[0].y;
4797 
4798     return result;
4799 }
4800 */
4801 
4802 SMIL::AnimateBase::AnimateBase (NodePtr &d, short id)
4803  : AnimateGroup (d, id),
4804    anim_timer (nullptr),
4805    keytimes (nullptr),
4806    spline_table (nullptr),
4807    keytime_count (0) {}
4808 
4809 SMIL::AnimateBase::~AnimateBase () {
4810     if (keytimes)
4811         free (keytimes);
4812     if (spline_table)
4813         free (spline_table);
4814 }
4815 
4816 void SMIL::AnimateBase::init () {
4817     if (Runtime::TimingsInitialized > runtime->timingstate) {
4818         if (anim_timer) {
4819             document ()->cancelPosting (anim_timer);
4820             anim_timer = nullptr;
4821         }
4822         accumulate = acc_none;
4823         additive = add_replace;
4824         calcMode = calc_linear;
4825         change_from.truncate (0);
4826         change_by.truncate (0);
4827         values.clear ();
4828         if (keytimes)
4829             free (keytimes);
4830         keytimes = nullptr;
4831         keytime_count = 0;
4832         if (spline_table)
4833             free (spline_table);
4834         spline_table = nullptr;
4835         splines.clear ();
4836         AnimateGroup::init ();
4837     }
4838 }
4839 
4840 void SMIL::AnimateBase::begin () {
4841     interval = 0;
4842     if (!setInterval ())
4843         return;
4844     applyStep ();
4845     if (calc_discrete != calcMode)
4846         change_updater.connect (m_doc, MsgSurfaceUpdate, this);
4847     AnimateGroup::begin ();
4848 }
4849 
4850 void SMIL::AnimateBase::finish () {
4851     if (anim_timer) { // make sure timers are stopped
4852         document ()->cancelPosting (anim_timer);
4853         anim_timer = nullptr;
4854     }
4855     change_updater.disconnect ();
4856     AnimateGroup::finish ();
4857 }
4858 
4859 void SMIL::AnimateBase::deactivate () {
4860     if (anim_timer) {
4861         document ()->cancelPosting (anim_timer);
4862         anim_timer = nullptr;
4863     } else {
4864         change_updater.disconnect ();
4865     }
4866     if (spline_table)
4867         free (spline_table);
4868     spline_table = nullptr;
4869     AnimateGroup::deactivate ();
4870 }
4871 
4872 void SMIL::AnimateBase::message (MessageType msg, void *data) {
4873     switch (msg) {
4874         case MsgEventTimer: {
4875             TimerPosting *te = static_cast <TimerPosting *> (data);
4876             if (te->event_id == anim_timer_id) {
4877                 anim_timer = nullptr;
4878                 timerTick (0);
4879                 return;
4880             }
4881             break;
4882         }
4883         case MsgSurfaceUpdate: {
4884             UpdateEvent *ue = static_cast <UpdateEvent *> (data);
4885             interval_start_time += ue->skipped_time;
4886             interval_end_time += ue->skipped_time;
4887             timerTick (ue->cur_event_time);
4888             return;
4889         }
4890         case MsgStateRewind:
4891             restoreModification ();
4892             if (anim_timer) {
4893                 document ()->cancelPosting (anim_timer);
4894                 anim_timer = nullptr;
4895             } else {
4896                 change_updater.disconnect ();
4897             }
4898             break;
4899         default:
4900             break;
4901     }
4902     AnimateGroup::message (msg, data);
4903 }
4904 
4905 void SMIL::AnimateBase::parseParam (const TrieString &name, const QString &val) {
4906     if (name == "from") {
4907         change_from = val;
4908     } else if (name == "by" || name == "change_by") {
4909         change_by = val;
4910     } else if (name == "values") {
4911         values = val.split (QChar (';'));
4912     } else if (name == "keyTimes") {
4913         QStringList kts = val.split (QChar (';'));
4914         if (keytimes)
4915             free (keytimes);
4916         keytime_count = kts.size ();
4917         if (0 == keytime_count) {
4918             keytimes = nullptr;
4919             return;
4920         }
4921         keytimes = (float *) malloc (sizeof (float) * keytime_count);
4922         for (unsigned int i = 0; i < keytime_count; i++) {
4923             keytimes[i] = kts[i].trimmed().toDouble();
4924             if (keytimes[i] < 0.0 || keytimes[i] > 1.0)
4925                 qCWarning(LOG_KMPLAYER_COMMON) << "animateMotion wrong keyTimes values";
4926             else if (i == 0 && keytimes[i] > 0.01)
4927                 qCWarning(LOG_KMPLAYER_COMMON) << "animateMotion first keyTimes value not 0";
4928             else
4929                 continue;
4930             free (keytimes);
4931             keytimes = nullptr;
4932             keytime_count = 0;
4933             return;
4934         }
4935     } else if (name == "keySplines") {
4936         splines = val.split (QChar (';'));
4937     } else if (name == "calcMode") {
4938         if (val == QString::fromLatin1 ("discrete"))
4939             calcMode = calc_discrete;
4940         else if (val == QString::fromLatin1 ("linear"))
4941             calcMode = calc_linear;
4942         else if (val == QString::fromLatin1 ("paced"))
4943             calcMode = calc_paced;
4944         else if (val == QString::fromLatin1 ("spline"))
4945             calcMode = calc_spline;
4946     } else
4947         AnimateGroup::parseParam (name, val);
4948 }
4949 
4950 static SMIL::AnimateBase::Point2D cubicBezier (float ax, float bx, float cx,
4951         float ay, float by, float cy, float t) {
4952     float   tSquared, tCubed;
4953     SMIL::AnimateBase::Point2D result;
4954 
4955     /* calculate the curve point at parameter value t */
4956 
4957     tSquared = t * t;
4958     tCubed = tSquared * t;
4959 
4960     result.x = (ax * tCubed) + (bx * tSquared) + (cx * t);
4961     result.y = (ay * tCubed) + (by * tSquared) + (cy * t);
4962 
4963     return result;
4964 }
4965 
4966 static
4967 float cubicBezier (SMIL::AnimateBase::Point2D *table, int a, int b, float x) {
4968     if (b > a + 1) {
4969         int mid = (a + b) / 2;
4970         if (table[mid].x > x)
4971             return cubicBezier (table, a, mid, x);
4972         else
4973             return cubicBezier (table, mid, b, x);
4974     }
4975     return table[a].y + (x - table[a].x) / (table[b].x - table[a].x) * (table[b].y - table[a].y);
4976 }
4977 
4978 
4979 bool SMIL::AnimateBase::setInterval () {
4980     int cs = runtime->durTime ().offset;
4981     if (keytime_count > interval + 1)
4982         cs = (int) (cs * (keytimes[interval+1] - keytimes[interval]));
4983     else if (keytime_count > interval && calc_discrete == calcMode)
4984         cs = (int) (cs * (1.0 - keytimes[interval]));
4985     else if (values.size () > 0 && calc_discrete == calcMode)
4986         cs /= values.size ();
4987     else if (values.size () > 1)
4988         cs /= values.size () - 1;
4989     if (cs < 0) {
4990         qCWarning(LOG_KMPLAYER_COMMON) << "animateMotion has no valid duration interval " <<
4991             interval << endl;
4992         runtime->doFinish ();
4993         return false;
4994     }
4995     interval_start_time = document ()->last_event_time;
4996     interval_end_time = interval_start_time + 10 * cs;
4997     switch (calcMode) {
4998         case calc_paced: // FIXME
4999         case calc_linear:
5000             break;
5001         case calc_spline:
5002             if (splines.size () > (int)interval) {
5003                 QStringList kss = splines[interval].split (QChar (' '));
5004                 control_point[0] = control_point[1] = 0;
5005                 control_point[2] = control_point[3] = 1;
5006                 if (kss.size () == 4) {
5007                     for (int i = 0; i < 4; ++i) {
5008                         control_point[i] = kss[i].toDouble();
5009                         if (control_point[i] < 0 || control_point[i] > 1) {
5010                             qCWarning(LOG_KMPLAYER_COMMON) << "keySplines values not between 0-1"
5011                                 << endl;
5012                             control_point[i] = i > 1 ? 1 : 0;
5013                             break;
5014                         }
5015                     }
5016                     if (spline_table)
5017                         free (spline_table);
5018                     spline_table = (Point2D *) malloc (100 * sizeof (Point2D));
5019 
5020                     /* calculate the polynomial coefficients */
5021                     float ax, bx, cx;
5022                     float ay, by, cy;
5023                     cx = 3.0 * control_point[0];
5024                     bx = 3.0 * (control_point[2] - control_point[0]) - cx;
5025                     ax = 1.0 - cx - bx;
5026 
5027                     cy = 3.0 * control_point[1];
5028                     by = 3.0 * (control_point[3] - control_point[1]) - cy;
5029                     ay = 1.0 - cy - by;
5030 
5031                     for (int i = 0; i < 100; ++i)
5032                         spline_table[i] = cubicBezier (ax, bx, cx, ay, by, cy, 1.0*i/100);
5033                 } else {
5034                     qCWarning(LOG_KMPLAYER_COMMON) << "keySplines " << interval <<
5035                         " has not 4 values" << endl;
5036                 }
5037             }
5038             break;
5039         case calc_discrete:
5040             anim_timer = document ()->post (this,
5041                     new TimerPosting (10 * cs, anim_timer_id));
5042             break;
5043         default:
5044             break;
5045     }
5046     //qCDebug(LOG_KMPLAYER_COMMON) << "setInterval " << steps << " " <<
5047     //    cur_x.size () << "," << cur_y.size () << "=>"
5048     //    << end_x.size () << "," << end_y.size () << " d:" << 
5049     //    delta_x.size () << "," << delta_y.size () << endl;
5050     return true;
5051 }
5052 
5053 //-----------------------------------------------------------------------------
5054 
5055 SMIL::Animate::Animate (NodePtr &doc)
5056  : AnimateBase (doc, id_node_animate),
5057    num_count (0), begin_(nullptr), cur (nullptr), delta (nullptr), end (nullptr) {
5058 }
5059 
5060 void SMIL::Animate::init () {
5061     if (Runtime::TimingsInitialized > runtime->timingstate) {
5062         cleanUp ();
5063         AnimateBase::init ();
5064     }
5065 }
5066 
5067 void SMIL::Animate::cleanUp () {
5068     if (anim_timer) {
5069         document ()->cancelPosting (anim_timer);
5070         anim_timer = nullptr;
5071     }
5072     delete [] begin_;
5073     delete [] cur;
5074     delete [] delta;
5075     delete [] end;
5076     begin_ = cur = delta = end = nullptr;
5077     num_count = 0;
5078 }
5079 
5080 void SMIL::Animate::deactivate () {
5081     cleanUp ();
5082     AnimateBase::deactivate ();
5083 }
5084 
5085 void SMIL::Animate::begin () {
5086     restoreModification ();
5087     cleanUp (); // FIXME: repeating doesn't reinit
5088 
5089     NodePtr protect = target_element;
5090     Element *target = static_cast <Element *> (targetElement ());
5091     if (!target) {
5092         qCWarning(LOG_KMPLAYER_COMMON) << "target element not found";
5093         runtime->doFinish ();
5094         return;
5095     }
5096     if (values.size () < 2) {
5097         values.push_front (change_from.isEmpty ()
5098                 ? target->param (changed_attribute)
5099                 : change_from);
5100         if (!change_to.isEmpty ()) {
5101             values.push_back (change_to);
5102         } else if (!change_by.isEmpty ()) {
5103             SizeType b (values[0]);
5104             b += SizeType (change_by);
5105             values.push_back (b.toString ());
5106         }
5107     }
5108     if (values.size () < 2) {
5109         qCWarning(LOG_KMPLAYER_COMMON) << "could not determine change values";
5110         runtime->doFinish ();
5111         return;
5112     }
5113     if (calcMode != calc_discrete) {
5114         QStringList bnums = values[0].split (QString (","));
5115         QStringList enums = values[1].split (QString (","));
5116         num_count = bnums.size ();
5117         if (num_count) {
5118             begin_ = new SizeType [num_count];
5119             end = new SizeType [num_count];
5120             cur = new SizeType [num_count];
5121             delta = new SizeType [num_count];
5122             for (int i = 0; i < num_count; ++i) {
5123                 begin_[i] = bnums[i];
5124                 end[i] = i < enums.size () ? enums[i] : bnums[i];
5125                 cur[i] = begin_[i];
5126                 delta[i] = end[i];
5127                 delta[i] -= begin_[i];
5128             }
5129         }
5130     }
5131     AnimateBase::begin ();
5132 }
5133 
5134 void SMIL::Animate::finish () {
5135     if (active () && calc_discrete != calcMode)
5136         for (int i = 0; i < num_count; ++i)
5137             if (cur[i].size () != end[i].size ()) {
5138                 for (int j = 0; j < num_count; ++j)
5139                     cur[j] = end[j];
5140                 applyStep (); // we lost some steps ..
5141                 break;
5142             }
5143     AnimateBase::finish ();
5144 }
5145 
5146 void SMIL::Animate::applyStep () {
5147     Element *target = convertNode <Element> (target_element);
5148     if (target) {
5149         if (calcMode != calc_discrete) {
5150             if (num_count) {
5151                 QString val (cur[0].toString ());
5152                 for (int i = 1; i < num_count; ++i)
5153                     val += QChar (',') + cur[i].toString ();
5154                 target->setParam (changed_attribute, val, &modification_id);
5155             }
5156         } else if ((int)interval < values.size ()) {
5157             target->setParam (changed_attribute,
5158                     values[interval], &modification_id);
5159         }
5160     }
5161 }
5162 
5163 bool SMIL::Animate::timerTick (unsigned int cur_time) {
5164     if (cur_time && cur_time <= interval_end_time) {
5165         float gain = 1.0 * (cur_time - interval_start_time) /
5166                            (interval_end_time - interval_start_time);
5167         if (gain > 1.0) {
5168             change_updater.disconnect ();
5169             gain = 1.0;
5170         }
5171         switch (calcMode) {
5172             case calc_paced: // FIXME
5173             case calc_linear:
5174                 break;
5175             case calc_spline:
5176                 if (spline_table)
5177                     gain = cubicBezier (spline_table, 0, 99, gain);
5178                 break;
5179             case calc_discrete:
5180                 return false; // shouldn't come here
5181         }
5182         for (int i = 0; i < num_count; ++i) {
5183             cur[i] = delta[i];
5184             cur[i] *= gain;
5185             cur[i] += begin_[i];
5186         }
5187         applyStep ();
5188         return true;
5189     } else if (values.size () > (int) ++interval) {
5190         if (calc_discrete != calcMode) {
5191             if (values.size () <= (int) interval + 1)
5192                 return false;
5193             QStringList enums = values[interval+1].split (QString (","));
5194             for (int i = 0; i < num_count; ++i) {
5195                 begin_[i] = end[i];
5196                 if (i < enums.size ())
5197                     end[i] = enums[i];
5198                 cur[i] = begin_[i];
5199                 delta[i] = end[i];
5200                 delta[i] -= begin_[i];
5201             }
5202         }
5203         if (setInterval ()) {
5204             applyStep ();
5205             return true;
5206         }
5207     }
5208     return false;
5209 }
5210 
5211 //-----------------------------------------------------------------------------
5212 
5213 static
5214 bool getMotionCoordinates (const QString &coord, SizeType &x, SizeType &y) {
5215     int p = coord.indexOf (QChar (','));
5216     if (p < 0)
5217         p = coord.indexOf (QChar (' '));
5218     if (p > 0) {
5219         x = coord.left (p).trimmed ();
5220         y = coord.mid (p + 1).trimmed ();
5221         return true;
5222     }
5223     return false;
5224 }
5225 
5226 void SMIL::AnimateMotion::init () {
5227     cur_x = cur_y = delta_x = delta_y = SizeType();
5228     AnimateBase::init ();
5229 }
5230 
5231 void SMIL::AnimateMotion::begin () {
5232     Node *t = targetElement ();
5233     CalculatedSizer *sizes = t ? (CalculatedSizer *) t->role (RoleSizer) : nullptr;
5234     if (!sizes)
5235         return;
5236     old_sizes = *sizes;
5237     if (anim_timer) {
5238         document ()->cancelPosting (anim_timer);
5239         anim_timer = nullptr;
5240     }
5241     if (change_from.isEmpty ()) {
5242         if (values.size () > 1) {
5243             getMotionCoordinates (values[0], begin_x, begin_y);
5244             getMotionCoordinates (values[1], end_x, end_y);
5245         } else {
5246             if (sizes->left.isSet ()) {
5247                 begin_x = sizes->left;
5248             } else if (sizes->right.isSet() && sizes->width.isSet ()) {
5249                 begin_x = sizes->right;
5250                 begin_x -= sizes->width;
5251             } else {
5252                 begin_x = "0";
5253             }
5254             if (sizes->top.isSet ()) {
5255                 begin_y = sizes->top;
5256             } else if (sizes->bottom.isSet() && sizes->height.isSet ()) {
5257                 begin_y = sizes->bottom;
5258                 begin_y -= sizes->height;
5259             } else {
5260                 begin_y = "0";
5261             }
5262         }
5263     } else {
5264         getMotionCoordinates (change_from, begin_x, begin_y);
5265     }
5266     if (!change_by.isEmpty ()) {
5267         getMotionCoordinates (change_by, delta_x, delta_y);
5268         end_x = begin_x;
5269         end_y = begin_y;
5270         end_x += delta_x;
5271         end_y += delta_y;
5272     } else if (!change_to.isEmpty ()) {
5273         getMotionCoordinates (change_to, end_x, end_y);
5274     }
5275     cur_x = begin_x;
5276     cur_y = begin_y;
5277     delta_x = end_x;
5278     delta_x -= begin_x;
5279     delta_y = end_y;
5280     delta_y -= begin_y;
5281     AnimateBase::begin ();
5282 }
5283 
5284 void SMIL::AnimateMotion::finish () {
5285     if (active ()) {
5286         if (calcMode != calc_discrete &&
5287                 (cur_x.size () != end_x.size () ||
5288                  cur_y.size () != end_y.size ())) {
5289             cur_x = end_x;
5290             cur_y = end_y;
5291             applyStep (); // we lost some steps ..
5292         }
5293     }
5294     AnimateBase::finish ();
5295 }
5296 
5297 void SMIL::AnimateMotion::restoreModification () {
5298     Node *n = target_element.ptr ();
5299     CalculatedSizer *sizes = n ? (CalculatedSizer *) n->role (RoleSizer) : nullptr;
5300     if (sizes) {
5301         *sizes = old_sizes;
5302         n->message (MsgSurfaceBoundsUpdate);
5303     }
5304 }
5305 
5306 void SMIL::AnimateMotion::applyStep () {
5307     Node *n = target_element.ptr ();
5308     CalculatedSizer *sizes = n ? (CalculatedSizer *) n->role (RoleSizer) : nullptr;
5309     if (n->role (RoleDisplay)) {
5310         sizes->move (cur_x, cur_y);
5311         n->message (MsgSurfaceBoundsUpdate);
5312     }
5313 }
5314 
5315 bool SMIL::AnimateMotion::timerTick (unsigned int cur_time) {
5316     if (cur_time && cur_time <= interval_end_time) {
5317         float gain = 1.0 * (cur_time - interval_start_time) /
5318                            (interval_end_time - interval_start_time);
5319         if (gain > 1.0) {
5320             change_updater.disconnect ();
5321             gain = 1.0;
5322         }
5323         switch (calcMode) {
5324             case calc_paced: // FIXME
5325             case calc_linear:
5326                 break;
5327             case calc_spline:
5328                 if (spline_table)
5329                     gain = cubicBezier (spline_table, 0, 99, gain);
5330                 break;
5331             case calc_discrete:
5332                 return false; // shouldn't come here
5333         }
5334         cur_x = delta_x;
5335         cur_y = delta_y;
5336         cur_x *= gain;
5337         cur_y *= gain;
5338         cur_x += begin_x;
5339         cur_y += begin_y;
5340         applyStep ();
5341         return true;
5342     } else if (values.size () > (int) ++interval) {
5343         getMotionCoordinates (values[interval], begin_x, begin_y);
5344         cur_x = begin_x;
5345         cur_y = begin_y;
5346         if (calcMode != calc_discrete && values.size () > (int) interval + 1) {
5347             getMotionCoordinates (values[interval+1], end_x, end_y);
5348             delta_x = end_x;
5349             delta_x -= begin_x;
5350             delta_y = end_y;
5351             delta_y -= begin_y;
5352         }
5353         if (setInterval ()) {
5354             applyStep ();
5355             return true;
5356         }
5357     }
5358     return false;
5359 }
5360 
5361 //-----------------------------------------------------------------------------
5362 
5363 static bool getAnimateColor (unsigned int val, SMIL::AnimateColor::Channels &c) {
5364     c.alpha = val >> 24;
5365     c.red = (val >> 16) & 0xff;
5366     c.green = (val >> 8) & 0xff;
5367     c.blue = val & 0xff;
5368     return true;
5369 }
5370 
5371 static bool getAnimateColor (const QString &val, SMIL::AnimateColor::Channels &c) {
5372     if (val.isEmpty ())
5373         return getAnimateColor (0, c);
5374     QColor color (val);
5375     return getAnimateColor (color.rgba (), c);
5376 }
5377 
5378 static short colorNormalise (int c) {
5379     if (c > 255)
5380         return 255;
5381     if (c < -255)
5382         return -255;
5383     return c;
5384 }
5385 
5386 SMIL::AnimateColor::Channels &SMIL::AnimateColor::Channels::operator *= (const float f) {
5387     alpha = colorNormalise ((int) (f * alpha));
5388     red = colorNormalise ((int) (f * red));
5389     green = colorNormalise ((int) (f * green));
5390     blue = colorNormalise ((int) (f * blue));
5391     return *this;
5392 }
5393 
5394 SMIL::AnimateColor::Channels &SMIL::AnimateColor::Channels::operator += (const Channels &c) {
5395     alpha = colorNormalise ((int) alpha + c.alpha);
5396     red = colorNormalise ((int) red + c.red);
5397     green = colorNormalise ((int) green + c.green);
5398     blue = colorNormalise ((int) blue + c.blue);
5399     return *this;
5400 }
5401 
5402 SMIL::AnimateColor::Channels &SMIL::AnimateColor::Channels::operator -= (const Channels &c) {
5403     alpha = colorNormalise ((int) alpha - c.alpha);
5404     red = colorNormalise ((int) red - c.red);
5405     green = colorNormalise ((int) green - c.green);
5406     blue = colorNormalise ((int) blue - c.blue);
5407     return *this;
5408 }
5409 
5410 unsigned int SMIL::AnimateColor::Channels::argb () {
5411     unsigned int v =
5412         (0xff000000 & ((unsigned)(alpha < 0 ? 0 : alpha) << 24)) |
5413         (0x00ff0000 & ((unsigned)(red < 0 ? 0 : red) << 16)) |
5414         (0x0000ff00 & ((unsigned)(green < 0 ? 0 : green) << 8)) |
5415         (0x000000ff & (blue < 0 ? 0 : blue));
5416     return v;
5417 }
5418 
5419 void SMIL::AnimateColor::Channels::clear () {
5420     alpha = red = blue = green = 0;
5421 }
5422 
5423 void SMIL::AnimateColor::init () {
5424     cur_c.clear ();
5425     delta_c.clear ();
5426     changed_attribute = "background-color";
5427     AnimateBase::init ();
5428 }
5429 
5430 void SMIL::AnimateColor::begin () {
5431     Element *target = static_cast <Element *> (targetElement ());
5432     if (!target)
5433         return;
5434     if (anim_timer) {
5435         document ()->cancelPosting (anim_timer);
5436         anim_timer = nullptr;
5437     }
5438     if (change_from.isEmpty ()) {
5439         if (values.size () > 1) {
5440             getAnimateColor (values[0], begin_c);
5441             getAnimateColor (values[1], end_c);
5442         } else {
5443             getAnimateColor (target->param (changed_attribute), begin_c);
5444         }
5445     } else {
5446         getAnimateColor (change_from, begin_c);
5447     }
5448     if (!change_by.isEmpty ()) {
5449         getAnimateColor (change_by, delta_c);
5450         end_c = begin_c;
5451         end_c += delta_c;
5452     } else if (!change_to.isEmpty ()) {
5453         getAnimateColor (change_to, end_c);
5454     }
5455     cur_c = begin_c;
5456     delta_c = end_c;
5457     delta_c -= begin_c;
5458     AnimateBase::begin ();
5459 }
5460 
5461 void SMIL::AnimateColor::finish () {
5462     if (active ()) {
5463         if (calcMode != calc_discrete && cur_c.argb () != end_c.argb ()) {
5464             cur_c = end_c;
5465             applyStep (); // we lost some steps ..
5466         }
5467     }
5468     AnimateBase::finish ();
5469 }
5470 
5471 void SMIL::AnimateColor::applyStep () {
5472     Node *target = target_element.ptr ();
5473     if (target) {
5474         // TODO make more efficient
5475         const  QString val = QString::asprintf ("#%08x", cur_c.argb ());
5476         static_cast <Element *> (target)->setParam (changed_attribute, val);
5477     }
5478 }
5479 
5480 bool SMIL::AnimateColor::timerTick (unsigned int cur_time) {
5481     if (cur_time && cur_time <= interval_end_time) {
5482         float gain = 1.0 * (cur_time - interval_start_time) /
5483                            (interval_end_time - interval_start_time);
5484         if (gain > 1.0) {
5485             change_updater.disconnect ();
5486             gain = 1.0;
5487         }
5488         switch (calcMode) {
5489             case calc_paced: // FIXME
5490             case calc_linear:
5491                 break;
5492             case calc_spline:
5493                 if (spline_table)
5494                     gain = cubicBezier (spline_table, 0, 99, gain);
5495                 break;
5496             case calc_discrete:
5497                 return true; // shouldn't come here
5498         }
5499         cur_c = delta_c;
5500         cur_c *= gain;
5501         cur_c += begin_c;
5502         applyStep ();
5503         return true;
5504     } else if (values.size () > (int) ++interval) {
5505         getAnimateColor (values[interval], begin_c);
5506         cur_c = begin_c;
5507         if (calcMode != calc_discrete && values.size () > (int) interval + 1) {
5508             getAnimateColor (values[interval+1], end_c);
5509             delta_c = end_c;
5510             delta_c -= begin_c;
5511         }
5512         if (setInterval ()) {
5513             applyStep ();
5514             return true;
5515         }
5516     }
5517     return false;
5518 }
5519 
5520 //-----------------------------------------------------------------------------
5521 
5522 void SMIL::Param::activate () {
5523     setState (state_activated);
5524     QString name = getAttribute (Ids::attr_name);
5525     Node * parent = parentNode ();
5526     if (!name.isEmpty () && parent && parent->isElementNode ())
5527         static_cast<Element*>(parent)->setParam (name,
5528                 getAttribute (Ids::attr_value));
5529     Element::activate (); //finish (); // no livetime of itself, will deactivate
5530 }
5531 
5532 //-----------------------------------------------------------------------------
5533 
5534 void Visitor::visit (SMIL::Layout *n) {
5535     visit (static_cast <Element *> (n));
5536 }
5537 
5538 void Visitor::visit (SMIL::RegionBase *n) {
5539     visit (static_cast <Element *> (n));
5540 }
5541 
5542 void Visitor::visit (SMIL::Seq *n) {
5543     visit (static_cast <SMIL::GroupBase *> (n));
5544 }
5545 
5546 void Visitor::visit (SMIL::Switch *n) {
5547     visit (static_cast <SMIL::GroupBase *> (n));
5548 }
5549 
5550 void Visitor::visit (SMIL::Par *n) {
5551     visit (static_cast <SMIL::GroupBase *> (n));
5552 }
5553 
5554 void Visitor::visit (SMIL::Excl *n) {
5555     visit (static_cast <SMIL::GroupBase *> (n));
5556 }
5557 
5558 void Visitor::visit (SMIL::Transition * n) {
5559     visit (static_cast <Element *> (n));
5560 }
5561 
5562 void Visitor::visit (SMIL::AnimateBase * n) {
5563     visit (static_cast <SMIL::AnimateGroup *> (n));
5564 }
5565 
5566 void Visitor::visit (SMIL::PriorityClass * n) {
5567     visit (static_cast <Element *> (n));
5568 }
5569 
5570 void Visitor::visit (SMIL::MediaType * n) {
5571     visit (static_cast <Mrl *> (n));
5572 }
5573 
5574 void Visitor::visit (SMIL::TextMediaType * n) {
5575     visit (static_cast <SMIL::MediaType *> (n));
5576 }
5577 
5578 void Visitor::visit (SMIL::RefMediaType * n) {
5579     visit (static_cast <SMIL::MediaType *> (n));
5580 }
5581 
5582 void Visitor::visit (SMIL::Brush * n) {
5583     visit (static_cast <SMIL::MediaType *> (n));
5584 }
5585 
5586 void Visitor::visit (SMIL::SmilText *n) {
5587     visit (static_cast <Element *> (n));
5588 }
5589 
5590 void Visitor::visit (SMIL::TextFlow *n) {
5591     visit (static_cast <Element *> (n));
5592 }
5593 
5594 void Visitor::visit (SMIL::TemporalMoment *n) {
5595     visit (static_cast <Element *> (n));
5596 }
5597 
5598 void Visitor::visit (SMIL::Anchor * n) {
5599     visit (static_cast <SMIL::LinkingBase *> (n));
5600 }
5601 
5602 void Visitor::visit (SMIL::Area * n) {
5603     visit (static_cast <SMIL::LinkingBase *> (n));
5604 }