1 /**
2  * Copyright (C) 2006 by Koos Vriezen <koos.vriezen@gmail.com>
3  *
4  *  This library is free software; you can redistribute it and/or
5  *  modify it under the terms of the GNU Library General Public
6  *  License version 2 as published by the Free Software Foundation.
7  *
8  *  This library is distributed in the hope that it will be useful,
9  *  but WITHOUT ANY WARRANTY; without even the implied warranty of
10  *  MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
11  *  Library General Public License for more details.
12  *
13  *  You should have received a copy of the GNU Library General Public License
14  *  along with this library; see the file COPYING.LIB.  If not, write to
15  *  the Free Software Foundation, Inc., 51 Franklin Steet, Fifth Floor,
16  *  Boston, MA 02110-1301, USA.
17  **/
18 
19 #include "config-kmplayer.h"
20 
21 #include <qcolor.h>
22 #include <qtimer.h>
23 
24 #include <kdebug.h>
25 
26 #include "kmplayer_rp.h"
27 #include "kmplayer_smil.h"
28 #include "mediaobject.h"
29 
30 using namespace KMPlayer;
31 
32 
Imfl(NodePtr & d)33 KDE_NO_CDTOR_EXPORT RP::Imfl::Imfl (NodePtr & d)
34   : Mrl (d, id_node_imfl),
35     fit (fit_hidden),
36     duration (0),
37     duration_timer (NULL),
38     needs_scene_img (0) {}
39 
~Imfl()40 KDE_NO_CDTOR_EXPORT RP::Imfl::~Imfl () {
41 }
42 
closed()43 KDE_NO_EXPORT void RP::Imfl::closed () {
44     for (Node *n = firstChild (); n; n = n->nextSibling ())
45         if (RP::id_node_head == n->id) {
46             Attribute *a = static_cast <Element *> (n)->attributes ().first ();
47             for (; a; a = a->nextSibling ()) {
48                 if (Ids::attr_width == a->name ()) {
49                     size.width = a->value ().toInt ();
50                 } else if (Ids::attr_height == a->name ()) {
51                     size.height = a->value ().toInt ();
52                 } else if (a->name () == "duration") {
53                     int dur;
54                     parseTime (a->value ().toLower (), dur);
55                     duration = dur;
56                 }
57             }
58         }
59     Mrl::closed ();
60 }
61 
defer()62 KDE_NO_EXPORT void RP::Imfl::defer () {
63     kDebug () << "RP::Imfl::defer ";
64     setState (state_deferred);
65     for (Node *n = firstChild (); n; n = n->nextSibling ())
66         if (n->id == RP::id_node_image && !n->active ())
67             n->activate ();
68 }
69 
activate()70 KDE_NO_EXPORT void RP::Imfl::activate () {
71     kDebug () << "RP::Imfl::activate ";
72     resolved = true;
73     setState (state_activated);
74     int timings_count = 0;
75     for (Node *n = firstChild (); n; n = n->nextSibling ())
76         switch (n->id) {
77             case RP::id_node_crossfade:
78             case RP::id_node_fadein:
79             case RP::id_node_fadeout:
80             case RP::id_node_fill:
81             case RP::id_node_wipe:
82             case RP::id_node_viewchange:
83                 n->activate (); // set their start timers
84                 timings_count++;
85                 break;
86             case RP::id_node_image:
87                 if (!n->active ())
88                     n->activate ();
89                 break;
90         }
91     if (duration > 0)
92         duration_timer = document ()->post (this,
93                 new TimerPosting (duration * 10));
94     else if (!timings_count)
95         finish ();
96 }
97 
finish()98 KDE_NO_EXPORT void RP::Imfl::finish () {
99     kDebug () << "RP::Imfl::finish ";
100     Mrl::finish ();
101     if (duration_timer) {
102         document ()->cancelPosting (duration_timer);
103         duration_timer = 0;
104     }
105     for (NodePtr n = firstChild (); n; n = n->nextSibling ())
106         if (n->unfinished ())
107             n->finish ();
108 }
109 
deactivate()110 KDE_NO_EXPORT void RP::Imfl::deactivate () {
111     kDebug () << "RP::Imfl::deactivate ";
112     if (unfinished ())
113         finish ();
114     else if (duration_timer) {
115         document ()->cancelPosting (duration_timer);
116         duration_timer = 0;
117     }
118     if (!active ())
119         return; // calling finish might call deactivate() as well
120     setState (state_deactivated);
121     for (NodePtr n = firstChild (); n; n = n->nextSibling ())
122         if (n->active ())
123             n->deactivate ();
124     rp_surface = (Surface *) role (RoleChildDisplay, NULL);
125 }
126 
message(MessageType msg,void * content)127 KDE_NO_EXPORT void RP::Imfl::message (MessageType msg, void *content) {
128     switch (msg) {
129         case MsgEventTimer:
130             duration_timer = 0;
131             if (unfinished ())
132                 finish ();
133             return;
134         case MsgChildFinished:
135             if (unfinished () && !duration_timer) {
136                 for (Node *n = firstChild (); n; n = n->nextSibling ())
137                     switch (n->id) {
138                         case RP::id_node_crossfade:
139                         case RP::id_node_fadein:
140                         case RP::id_node_fadeout:
141                         case RP::id_node_fill:
142                             if (n->unfinished ())
143                                 return;
144                     }
145                 finish ();
146             }
147             return;
148         default:
149             break;
150     }
151     Mrl::message (msg, content);
152 }
153 
accept(Visitor * v)154 KDE_NO_EXPORT void RP::Imfl::accept (Visitor * v) {
155     v->visit (this);
156 }
157 
surface()158 KDE_NO_EXPORT Surface *RP::Imfl::surface () {
159     if (!rp_surface) {
160         rp_surface = (Surface *) Mrl::role (RoleChildDisplay, this);
161         if (rp_surface && size.isEmpty ())
162             size = rp_surface->bounds.size;
163     }
164     return rp_surface.ptr ();
165 }
166 
childFromTag(const QString & tag)167 KDE_NO_EXPORT Node *RP::Imfl::childFromTag (const QString & tag) {
168     QByteArray ba = tag.toLatin1 ();
169     const char *ctag = ba.constData ();
170     if (!strcmp (ctag, "head"))
171         return new DarkNode (m_doc, "head", RP::id_node_head);
172     else if (!strcmp (ctag, "image"))
173         return new RP::Image (m_doc);
174     else if (!strcmp (ctag, "fill"))
175         return new RP::Fill (m_doc);
176     else if (!strcmp (ctag, "wipe"))
177         return new RP::Wipe (m_doc);
178     else if (!strcmp (ctag, "viewchange"))
179         return new RP::ViewChange (m_doc);
180     else if (!strcmp (ctag, "crossfade"))
181         return new RP::Crossfade (m_doc);
182     else if (!strcmp (ctag, "fadein"))
183         return new RP::Fadein (m_doc);
184     else if (!strcmp (ctag, "fadeout"))
185         return new RP::Fadeout (m_doc);
186     return NULL;
187 }
188 
repaint()189 KDE_NO_EXPORT void RP::Imfl::repaint () {
190     if (!active ()) {
191         kWarning () << "Spurious Imfl repaint";
192     } else if (surface () && !size.isEmpty ()) {
193         rp_surface->markDirty ();
194         rp_surface->repaint (SRect (0, 0, size));
195     }
196 }
197 
Image(NodePtr & doc)198 KDE_NO_CDTOR_EXPORT RP::Image::Image (NodePtr & doc)
199  : Mrl (doc, id_node_image) {
200     view_mode = WindowMode;
201 }
202 
~Image()203 KDE_NO_CDTOR_EXPORT RP::Image::~Image () {
204 }
205 
closed()206 KDE_NO_EXPORT void RP::Image::closed () {
207     src = getAttribute (Ids::attr_name);
208     Mrl::closed ();
209 }
210 
activate()211 KDE_NO_EXPORT void RP::Image::activate () {
212     kDebug () << "RP::Image::activate";
213     setState (state_activated);
214     isPlayable (); // update src attribute
215     if (!media_info)
216         media_info = new MediaInfo (this, MediaManager::Image);
217     media_info->wget (absolutePath ());
218 }
219 
begin()220 KDE_NO_EXPORT void RP::Image::begin () {
221     Node::begin ();
222 }
223 
deactivate()224 KDE_NO_EXPORT void RP::Image::deactivate () {
225     if (img_surface) {
226         img_surface->remove ();
227         img_surface = NULL;
228     }
229     setState (state_deactivated);
230     postpone_lock = 0L;
231     delete media_info;
232     media_info = NULL;
233 }
234 
message(MessageType msg,void * content)235 KDE_NO_EXPORT void RP::Image::message (MessageType msg, void *content) {
236     if (msg == MsgMediaReady) {
237         if (media_info)
238             dataArrived ();
239     } else {
240         Mrl::message (msg, content);
241     }
242 }
243 
dataArrived()244 KDE_NO_EXPORT void RP::Image::dataArrived () {
245     kDebug () << "RP::Image::remoteReady";
246     ImageMedia *im = media_info->media ? (ImageMedia *)media_info->media : NULL;
247     if (im && !im->isEmpty ()) {
248         size.width = im->cached_img->width;
249         size.height = im->cached_img->height;
250     }
251     postpone_lock = 0L;
252 }
253 
isReady(bool postpone_if_not)254 KDE_NO_EXPORT bool RP::Image::isReady (bool postpone_if_not) {
255     if (media_info->downloading () && postpone_if_not)
256         postpone_lock = document ()->postpone ();
257     return !media_info->downloading ();
258 }
259 
surface()260 KDE_NO_EXPORT Surface *RP::Image::surface () {
261     ImageMedia *im = media_info && media_info->media
262         ? (ImageMedia *)media_info->media : NULL;
263     if (im && !img_surface && !im->isEmpty ()) {
264         Node * p = parentNode ();
265         if (p && p->id == RP::id_node_imfl) {
266             Surface *ps = static_cast <RP::Imfl *> (p)->surface ();
267             if (ps)
268                 img_surface = ps->createSurface (this,
269                         SRect (0, 0, size));
270         }
271     }
272     return img_surface;
273 }
274 
TimingsBase(NodePtr & d,const short i)275 KDE_NO_CDTOR_EXPORT RP::TimingsBase::TimingsBase (NodePtr & d, const short i)
276  : Element (d, i), x (0), y (0), w (0), h (0), start (0), duration (0),
277    start_timer (NULL), duration_timer (NULL), update_timer (NULL) {}
278 
activate()279 KDE_NO_EXPORT void RP::TimingsBase::activate () {
280     setState (state_activated);
281     x = y = w = h = 0;
282     srcx = srcy = srcw = srch = 0;
283     for (Attribute *a = attributes ().first (); a; a = a->nextSibling ()) {
284         if (a->name () == Ids::attr_target) {
285             for (Node *n = parentNode()->firstChild(); n; n= n->nextSibling())
286                 if (static_cast <Element *> (n)->
287                         getAttribute ("handle") == a->value ())
288                     target = n;
289         } else if (a->name () == "start") {
290             int dur;
291             parseTime (a->value ().toLower (), dur);
292             start = dur;
293         } else if (a->name () == "duration") {
294             int dur;
295             parseTime (a->value ().toLower (), dur);
296             duration = dur;
297         } else if (a->name () == "dstx") {
298             x = a->value ().toInt ();
299         } else if (a->name () == "dsty") {
300             y = a->value ().toInt ();
301         } else if (a->name () == "dstw") {
302             w = a->value ().toInt ();
303         } else if (a->name () == "dsth") {
304             h = a->value ().toInt ();
305         } else if (a->name () == "srcx") {
306             srcx = a->value ().toInt ();
307         } else if (a->name () == "srcy") {
308             srcy = a->value ().toInt ();
309         } else if (a->name () == "srcw") {
310             srcw = a->value ().toInt ();
311         } else if (a->name () == "srch") {
312             srch = a->value ().toInt ();
313         }
314     }
315     start_timer = document ()->post (this, new TimerPosting (start *10));
316 }
317 
deactivate()318 KDE_NO_EXPORT void RP::TimingsBase::deactivate () {
319     if (unfinished ())
320         finish ();
321     else
322         cancelTimers ();
323     setState (state_deactivated);
324 }
325 
message(MessageType msg,void * content)326 KDE_NO_EXPORT void RP::TimingsBase::message (MessageType msg, void *content) {
327     switch (msg) {
328         case MsgEventTimer: {
329             TimerPosting *te = static_cast <TimerPosting *> (content);
330             if (te == update_timer && duration > 0) {
331                 update (100 * 10 * ++curr_step / duration);
332                 te->interval = true;
333             } else if (te == start_timer) {
334                 start_timer = 0;
335                 duration_timer = document()->post (this,
336                         new TimerPosting (duration * 10));
337                 begin ();
338             } else if (te == duration_timer) {
339                 duration_timer = 0;
340                 update (100);
341                 finish ();
342             }
343             break;
344         }
345         case MsgEventPostponed: {
346             PostponedEvent *pe = static_cast <PostponedEvent *> (content);
347             if (!pe->is_postponed) {
348                 document_postponed.disconnect ();
349                 update (duration > 0 ? 0 : 100);
350             }
351             break;
352         }
353         default:
354             Element::message (msg, content);
355     }
356 }
357 
begin()358 KDE_NO_EXPORT void RP::TimingsBase::begin () {
359     progress = 0;
360     setState (state_began);
361     if (target)
362         target->begin ();
363     if (duration > 0) {
364         steps = duration / 10; // 10/s updates
365         update_timer = document ()->post (this, new TimerPosting (100)); // 50ms
366         curr_step = 1;
367     }
368 }
369 
update(int percentage)370 KDE_NO_EXPORT void RP::TimingsBase::update (int percentage) {
371     progress = percentage;
372     Node *p = parentNode ();
373     if (p->id == RP::id_node_imfl)
374         static_cast <RP::Imfl *> (p)->repaint ();
375 }
376 
finish()377 KDE_NO_EXPORT void RP::TimingsBase::finish () {
378     progress = 100;
379     cancelTimers ();
380     document_postponed.disconnect ();
381     Element::finish ();
382 }
383 
cancelTimers()384 KDE_NO_EXPORT void RP::TimingsBase::cancelTimers () {
385     if (start_timer) {
386         document ()->cancelPosting (start_timer);
387         start_timer = 0;
388     } else if (duration_timer) {
389         document ()->cancelPosting (duration_timer);
390         duration_timer = 0;
391     }
392     if (update_timer) {
393         document ()->cancelPosting (update_timer);
394         update_timer = 0;
395     }
396 }
397 
activate()398 KDE_NO_EXPORT void RP::Crossfade::activate () {
399     TimingsBase::activate ();
400 }
401 
begin()402 KDE_NO_EXPORT void RP::Crossfade::begin () {
403     //kDebug () << "RP::Crossfade::begin";
404     TimingsBase::begin ();
405     if (target && target->id == id_node_image) {
406         RP::Image * img = static_cast <RP::Image *> (target.ptr ());
407         if (!img->isReady (true))
408             document_postponed.connect (document(), MsgEventPostponed, this);
409         else
410             update (duration > 0 ? 0 : 100);
411     }
412 }
413 
accept(Visitor * v)414 KDE_NO_EXPORT void RP::Crossfade::accept (Visitor * v) {
415     v->visit (this);
416 }
417 
activate()418 KDE_NO_EXPORT void RP::Fadein::activate () {
419     // pickup color from Fill that should be declared before this node
420     from_color = 0;
421     TimingsBase::activate ();
422 }
423 
begin()424 KDE_NO_EXPORT void RP::Fadein::begin () {
425     //kDebug () << "RP::Fadein::begin";
426     TimingsBase::begin ();
427     if (target && target->id == id_node_image) {
428         RP::Image * img = static_cast <RP::Image *> (target.ptr ());
429         if (!img->isReady (true))
430             document_postponed.connect (document(), MsgEventPostponed, this);
431         else
432             update (duration > 0 ? 0 : 100);
433     }
434 }
435 
accept(Visitor * v)436 KDE_NO_EXPORT void RP::Fadein::accept (Visitor * v) {
437     v->visit (this);
438 }
439 
activate()440 KDE_NO_EXPORT void RP::Fadeout::activate () {
441     to_color = QColor (getAttribute ("color")).rgb ();
442     TimingsBase::activate ();
443 }
444 
begin()445 KDE_NO_EXPORT void RP::Fadeout::begin () {
446     //kDebug () << "RP::Fadeout::begin";
447     TimingsBase::begin ();
448 }
449 
accept(Visitor * v)450 KDE_NO_EXPORT void RP::Fadeout::accept (Visitor * v) {
451     v->visit (this);
452 }
453 
activate()454 KDE_NO_EXPORT void RP::Fill::activate () {
455     color = QColor (getAttribute ("color")).rgb ();
456     TimingsBase::activate ();
457 }
458 
begin()459 KDE_NO_EXPORT void RP::Fill::begin () {
460     setState (state_began);
461     update (0);
462 }
463 
accept(Visitor * v)464 KDE_NO_EXPORT void RP::Fill::accept (Visitor * v) {
465     v->visit (this);
466 }
467 
activate()468 KDE_NO_EXPORT void RP::Wipe::activate () {
469     //TODO implement 'type="push"'
470     QString dir = getAttribute ("direction").toLower ();
471     direction = dir_right;
472     if (dir == QString::fromLatin1 ("left"))
473         direction = dir_left;
474     else if (dir == QString::fromLatin1 ("up"))
475         direction = dir_up;
476     else if (dir == QString::fromLatin1 ("down"))
477         direction = dir_down;
478     TimingsBase::activate ();
479 }
480 
begin()481 KDE_NO_EXPORT void RP::Wipe::begin () {
482     //kDebug () << "RP::Wipe::begin";
483     TimingsBase::begin ();
484     if (target && target->id == id_node_image) {
485         RP::Image * img = static_cast <RP::Image *> (target.ptr ());
486         if (!img->isReady (true))
487             document_postponed.connect (document(), MsgEventPostponed, this);
488         else
489             update (duration > 0 ? 0 : 100);
490     }
491 }
492 
accept(Visitor * v)493 KDE_NO_EXPORT void RP::Wipe::accept (Visitor * v) {
494     v->visit (this);
495 }
496 
activate()497 KDE_NO_EXPORT void RP::ViewChange::activate () {
498     TimingsBase::activate ();
499 }
500 
begin()501 KDE_NO_EXPORT void RP::ViewChange::begin () {
502     kDebug () << "RP::ViewChange::begin";
503     setState (state_began);
504     Node *p = parentNode ();
505     if (p->id == RP::id_node_imfl)
506         static_cast <RP::Imfl *> (p)->needs_scene_img++;
507     update (0);
508 }
509 
finish()510 KDE_NO_EXPORT void RP::ViewChange::finish () {
511     Node *p = parentNode ();
512     if (p && p->id == RP::id_node_imfl)
513         static_cast <RP::Imfl *> (p)->needs_scene_img--;
514     TimingsBase::finish ();
515 }
516 
accept(Visitor * v)517 KDE_NO_EXPORT void RP::ViewChange::accept (Visitor * v) {
518     v->visit (this);
519 }
520