1 /**
2   This file belong to the KMPlayer project, a movie player plugin for Konqueror
3   Copyright (C) 2009  Koos Vriezen <koos.vriezen@gmail.com>
4 
5   This library is free software; you can redistribute it and/or
6   modify it under the terms of the GNU Lesser General Public
7   License as published by the Free Software Foundation; either
8   version 2 of the License, or (at your option) any later version.
9 
10   This library is distributed in the hope that it will be useful,
11   but WITHOUT ANY WARRANTY; without even the implied warranty of
12   MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
13   Lesser General Public License for more details.
14 
15   You should have received a copy of the GNU Lesser General Public
16   License along with this library; if not, write to the Free Software
17   Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA  02110-1301  USA
18 **/
19 
20 #include <qfile.h>
21 #include <qurl.h>
22 #include <qtextstream.h>
23 #include <qbytearray.h>
24 #include <qinputdialog.h>
25 #include <QStandardPaths>
26 
27 #include <kfiledialog.h>
28 #include <ksharedconfig.h>
29 #include <klocale.h>
30 #include <kdebug.h>
31 
32 #include "kmplayer_lists.h"
33 #include "kmplayer.h"
34 #include "mediaobject.h"
35 
36 
play(KMPlayer::Mrl * mrl)37 KDE_NO_EXPORT void ListsSource::play (KMPlayer::Mrl *mrl) {
38     if (m_player->source () == this)
39         Source::play (mrl);
40     else if (mrl)
41         mrl->activate ();
42 }
43 
activate()44 KDE_NO_EXPORT void ListsSource::activate () {
45     activated = true;
46     play (m_current ? m_current->mrl () : NULL);
47 }
48 
prettyName()49 QString ListsSource::prettyName ()
50 {
51     return ((KMPlayer::PlaylistRole *)m_document->role (KMPlayer::RolePlaylist))->caption ();
52 }
53 
FileDocument(short i,const QString & s,KMPlayer::Source * src)54 KDE_NO_CDTOR_EXPORT FileDocument::FileDocument (short i, const QString &s, KMPlayer::Source *src)
55  : KMPlayer::SourceDocument (src, s), load_tree_version ((unsigned int)-1) {
56     id = i;
57 }
58 
childFromTag(const QString & tag)59 KDE_NO_EXPORT KMPlayer::Node *FileDocument::childFromTag(const QString &tag) {
60     if (tag == QString::fromLatin1 (nodeName ()))
61         return this;
62     return 0L;
63 }
64 
readFromFile(const QString & fn)65 void FileDocument::readFromFile (const QString & fn) {
66     QFile file (fn);
67     kDebug () << "readFromFile " << fn;
68     if (QFileInfo (file).exists ()) {
69         file.open (QIODevice::ReadOnly);
70         QTextStream inxml (&file);
71         inxml.setCodec("UTF-8");
72         KMPlayer::readXML (this, inxml, QString (), false);
73         normalize ();
74     }
75     load_tree_version = m_tree_version;
76 }
77 
writeToFile(const QString & fn)78 void FileDocument::writeToFile (const QString & fn) {
79     QFile file (fn);
80     kDebug () << "writeToFile " << fn;
81     file.open (QIODevice::WriteOnly | QIODevice::Truncate);
82     file.write (outerXML ().toUtf8 ());
83     load_tree_version = m_tree_version;
84 }
85 
sync(const QString & fn)86 void FileDocument::sync (const QString &fn)
87 {
88     if (resolved && load_tree_version != m_tree_version)
89         writeToFile (fn);
90 }
91 
Recents(KMPlayerApp * a)92 KDE_NO_CDTOR_EXPORT Recents::Recents (KMPlayerApp *a)
93     : FileDocument (id_node_recent_document, "recents://"),
94       app(a) {
95     title = i18n ("Most Recent");
96     bookmarkable = false;
97 }
98 
activate()99 KDE_NO_EXPORT void Recents::activate () {
100     if (!resolved)
101         defer ();
102 }
103 
defer()104 KDE_NO_EXPORT void Recents::defer () {
105     if (!resolved) {
106         resolved = true;
107         readFromFile(QStandardPaths::writableLocation(QStandardPaths::GenericDataLocation) + "/kmplayer/recent.xml");
108     }
109 }
110 
childFromTag(const QString & tag)111 KDE_NO_EXPORT KMPlayer::Node *Recents::childFromTag (const QString & tag) {
112     // kDebug () << nodeName () << " childFromTag " << tag;
113     if (tag == QString::fromLatin1 ("item"))
114         return new Recent (m_doc, app);
115     else if (tag == QString::fromLatin1 ("group"))
116         return new Group (m_doc, app);
117     return FileDocument::childFromTag (tag);
118 }
119 
message(KMPlayer::MessageType msg,void * data)120 KDE_NO_EXPORT void Recents::message (KMPlayer::MessageType msg, void *data) {
121     if (KMPlayer::MsgChildFinished == msg)
122         finish ();
123     else
124         FileDocument::message (msg, data);
125 }
126 
127 KDE_NO_CDTOR_EXPORT
Recent(KMPlayer::NodePtr & doc,KMPlayerApp * a,const QString & url)128 Recent::Recent (KMPlayer::NodePtr & doc, KMPlayerApp * a, const QString &url)
129   : KMPlayer::Mrl (doc, id_node_recent_node), app (a) {
130     src = url;
131     setAttribute (KMPlayer::Ids::attr_url, url);
132 }
133 
closed()134 KDE_NO_EXPORT void Recent::closed () {
135     src = getAttribute (KMPlayer::Ids::attr_url);
136     Mrl::closed ();
137 }
138 
activate()139 KDE_NO_EXPORT void Recent::activate () {
140     app->openDocumentFile (KUrl (src));
141 }
142 
143 KDE_NO_CDTOR_EXPORT
Group(KMPlayer::NodePtr & doc,KMPlayerApp * a,const QString & pn)144 Group::Group (KMPlayer::NodePtr & doc, KMPlayerApp * a, const QString & pn)
145   : KMPlayer::Element (doc, KMPlayer::id_node_group_node), app (a) {
146     title = pn;
147     if (!pn.isEmpty ())
148         setAttribute (KMPlayer::Ids::attr_title, pn);
149 }
150 
childFromTag(const QString & tag)151 KDE_NO_EXPORT KMPlayer::Node *Group::childFromTag (const QString & tag) {
152     if (tag == QString::fromLatin1 ("item"))
153         return new Recent (m_doc, app);
154     else if (tag == QString::fromLatin1 ("group"))
155         return new Group (m_doc, app);
156     return 0L;
157 }
158 
closed()159 KDE_NO_EXPORT void Group::closed () {
160     title = getAttribute (KMPlayer::Ids::attr_title);
161     Element::closed ();
162 }
163 
role(KMPlayer::RoleType msg,void * content)164 void *Group::role (KMPlayer::RoleType msg, void *content)
165 {
166     if (KMPlayer::RolePlaylist == msg)
167         return (KMPlayer::PlaylistRole *) this ;
168     return Element::role (msg, content);
169 }
170 
defer()171 KDE_NO_EXPORT void Playlist::defer () {
172     if (playmode) {
173         KMPlayer::Document::defer ();
174         // Hack: Node::undefer will restart first item when state=init
175         if (firstChild() && KMPlayer::Node::state_init == firstChild()->state)
176             firstChild()->state = KMPlayer::Node::state_activated;
177     } else if (!resolved) {
178         resolved = true;
179         readFromFile(QStandardPaths::writableLocation(QStandardPaths::GenericDataLocation) + "/kmplayer/playlist.xml");
180     }
181 }
182 
activate()183 KDE_NO_EXPORT void Playlist::activate () {
184     if (playmode)
185         KMPlayer::Document::activate ();
186     else if (!resolved)
187         defer ();
188 }
189 
Playlist(KMPlayerApp * a,KMPlayer::Source * s,bool plmode)190 KDE_NO_CDTOR_EXPORT Playlist::Playlist (KMPlayerApp *a, KMPlayer::Source *s, bool plmode)
191     : FileDocument (KMPlayer::id_node_playlist_document, "Playlist://", s),
192       app(a),
193       playmode (plmode) {
194     title = i18n ("Persistent Playlists");
195     bookmarkable = false;
196 }
197 
childFromTag(const QString & tag)198 KDE_NO_EXPORT KMPlayer::Node *Playlist::childFromTag (const QString & tag) {
199     // kDebug () << nodeName () << " childFromTag " << tag;
200     QByteArray ba = tag.toUtf8 ();
201     const char *name = ba.constData ();
202     if (!strcmp (name, "item"))
203         return new PlaylistItem (m_doc, app, playmode);
204     else if (!strcmp (name, "group"))
205         return new PlaylistGroup (m_doc, app, playmode);
206     else if (!strcmp (name, "object"))
207         return new HtmlObject (m_doc, app, playmode);
208     return FileDocument::childFromTag (tag);
209 }
210 
message(KMPlayer::MessageType msg,void * data)211 KDE_NO_EXPORT void Playlist::message (KMPlayer::MessageType msg, void *data) {
212     if (KMPlayer::MsgChildFinished == msg && !playmode)
213         finish ();
214     else
215         FileDocument::message (msg, data);
216 }
217 
218 KDE_NO_CDTOR_EXPORT
PlaylistItemBase(KMPlayer::NodePtr & d,short i,KMPlayerApp * a,bool pm)219 PlaylistItemBase::PlaylistItemBase (KMPlayer::NodePtr &d, short i, KMPlayerApp *a, bool pm)
220     : KMPlayer::Mrl (d, i), app (a), playmode (pm) {
221     editable = !pm;
222 }
223 
activate()224 KDE_NO_EXPORT void PlaylistItemBase::activate () {
225     if (playmode) {
226         Mrl::activate ();
227     } else {
228         ListsSource * source = static_cast <ListsSource *> (app->player ()->sources () ["listssource"]);
229         Playlist *pl = new Playlist (app, source, true);
230         KMPlayer::NodePtr n = pl;
231         pl->src.clear ();
232         QString data;
233         QString pn;
234         if (parentNode ()->id == KMPlayer::id_node_group_node) {
235             data = QString ("<playlist>") +
236                 parentNode ()->innerXML () +
237                 QString ("</playlist>");
238             pn = ((KMPlayer::PlaylistRole *)parentNode ()->role (KMPlayer::RolePlaylist))->caption ();
239         } else {
240             data = outerXML ();
241             pn = title.isEmpty () ? src : title;
242         }
243         pl->setCaption (pn);
244         //kDebug () << "cloning to " << data;
245         QTextStream inxml (&data, QIODevice::ReadOnly);
246         KMPlayer::readXML (pl, inxml, QString (), false);
247         pl->normalize ();
248         KMPlayer::Node *cur = pl->firstChild ();
249         pl->mrl ()->resolved = !!cur;
250         if (parentNode ()->id == KMPlayer::id_node_group_node && cur) {
251             KMPlayer::Node *sister = parentNode ()->firstChild ();
252             while (sister && cur && sister != this) {
253                 sister = sister->nextSibling ();
254                 cur = cur->nextSibling ();
255             }
256         }
257         bool reset_only = source == app->player ()->source ();
258         if (reset_only)
259             app->player ()->stop ();
260         source->setDocument (pl, cur);
261         if (reset_only) {
262             source->activate ();
263             app->setCaption (pn);
264         } else
265             app->player ()->setSource (source);
266     }
267 }
268 
closed()269 void PlaylistItemBase::closed () {
270     title = getAttribute (KMPlayer::Ids::attr_title);
271     Mrl::closed ();
272 }
273 
274 KDE_NO_CDTOR_EXPORT
PlaylistItem(KMPlayer::NodePtr & doc,KMPlayerApp * a,bool pm,const QString & url)275 PlaylistItem::PlaylistItem (KMPlayer::NodePtr & doc, KMPlayerApp *a, bool pm, const QString &url)
276  : PlaylistItemBase (doc, KMPlayer::id_node_playlist_item, a, pm) {
277     src = url;
278     setAttribute (KMPlayer::Ids::attr_url, url);
279 }
280 
closed()281 KDE_NO_EXPORT void PlaylistItem::closed () {
282     src = getAttribute (KMPlayer::Ids::attr_url);
283     PlaylistItemBase::closed ();
284 }
285 
begin()286 KDE_NO_EXPORT void PlaylistItem::begin () {
287     if (playmode && firstChild ())
288         firstChild ()->activate ();
289     else
290         Mrl::begin ();
291 }
292 
setNodeName(const QString & s)293 KDE_NO_EXPORT void PlaylistItem::setNodeName (const QString & s) {
294     bool uri = s.startsWith (QChar ('/'));
295     if (!uri) {
296         int p = s.indexOf ("://");
297         uri = p > 0 && p < 10;
298     }
299     if (uri) {
300         if (title.isEmpty () || title == src)
301             title = s;
302         src = s;
303         setAttribute (KMPlayer::Ids::attr_url, s);
304     } else {
305         title = s;
306         setAttribute (KMPlayer::Ids::attr_title, s);
307     }
308 }
309 
310 KDE_NO_CDTOR_EXPORT
PlaylistGroup(KMPlayer::NodePtr & doc,KMPlayerApp * a,const QString & pn)311 PlaylistGroup::PlaylistGroup (KMPlayer::NodePtr &doc, KMPlayerApp *a, const QString &pn)
312   : KMPlayer::Element (doc, KMPlayer::id_node_group_node), app (a), playmode (false) {
313     title = pn;
314     editable = true;
315     if (!pn.isEmpty ())
316         setAttribute (KMPlayer::Ids::attr_title, pn);
317 }
318 
319 KDE_NO_CDTOR_EXPORT
PlaylistGroup(KMPlayer::NodePtr & doc,KMPlayerApp * a,bool lm)320 PlaylistGroup::PlaylistGroup (KMPlayer::NodePtr &doc, KMPlayerApp *a, bool lm)
321   : KMPlayer::Element (doc, KMPlayer::id_node_group_node), app (a), playmode (lm) {
322     editable = !lm;
323 }
324 
childFromTag(const QString & tag)325 KDE_NO_EXPORT KMPlayer::Node *PlaylistGroup::childFromTag (const QString &tag) {
326     QByteArray ba = tag.toUtf8 ();
327     const char *name = ba.constData ();
328     if (!strcmp (name, "item"))
329         return new PlaylistItem (m_doc, app, playmode);
330     else if (!strcmp (name, "group"))
331         return new PlaylistGroup (m_doc, app, playmode);
332     else if (!strcmp (name, "object"))
333         return new HtmlObject (m_doc, app, playmode);
334     return 0L;
335 }
336 
closed()337 KDE_NO_EXPORT void PlaylistGroup::closed () {
338     title = getAttribute (KMPlayer::Ids::attr_title);
339     Element::closed ();
340 }
341 
setNodeName(const QString & t)342 KDE_NO_EXPORT void PlaylistGroup::setNodeName (const QString &t) {
343     title = t;
344     setAttribute (KMPlayer::Ids::attr_title, t);
345 }
346 
role(KMPlayer::RoleType msg,void * content)347 void *PlaylistGroup::role (KMPlayer::RoleType msg, void *content)
348 {
349     if (KMPlayer::RolePlaylist == msg)
350         return (KMPlayer::PlaylistRole *) this ;
351     return Element::role (msg, content);
352 }
353 
354 KDE_NO_CDTOR_EXPORT
HtmlObject(KMPlayer::NodePtr & doc,KMPlayerApp * a,bool pm)355 HtmlObject::HtmlObject (KMPlayer::NodePtr &doc, KMPlayerApp *a, bool pm)
356   : PlaylistItemBase (doc, KMPlayer::id_node_html_object, a, pm) {}
357 
activate()358 KDE_NO_EXPORT void HtmlObject::activate () {
359     if (playmode)
360         KMPlayer::Mrl::activate ();
361     else
362         PlaylistItemBase::activate ();
363 }
364 
closed()365 KDE_NO_EXPORT void HtmlObject::closed () {
366     for (Node *n = firstChild (); n; n = n->nextSibling ()) {
367         if (n->id == KMPlayer::id_node_param) {
368             KMPlayer::Element *e = static_cast <KMPlayer::Element *> (n);
369             QString name = e->getAttribute (KMPlayer::Ids::attr_name);
370             if (name == "type")
371                 mimetype = e->getAttribute (KMPlayer::Ids::attr_value);
372             else if (name == "movie")
373                 src = e->getAttribute (KMPlayer::Ids::attr_value);
374         } else if (n->id == KMPlayer::id_node_html_embed) {
375             KMPlayer::Element *e = static_cast <KMPlayer::Element *> (n);
376             QString type = e->getAttribute (KMPlayer::Ids::attr_type);
377             if (!type.isEmpty ())
378                 mimetype = type;
379             QString asrc = e->getAttribute (KMPlayer::Ids::attr_src);
380             if (!asrc.isEmpty ())
381                 src = asrc;
382         }
383     }
384     PlaylistItemBase::closed ();
385 }
386 
childFromTag(const QString & tag)387 KDE_NO_EXPORT KMPlayer::Node *HtmlObject::childFromTag (const QString & tag) {
388     QByteArray ba = tag.toUtf8 ();
389     const char *name = ba.constData ();
390     if (!strcasecmp (name, "param"))
391         return new KMPlayer::DarkNode (m_doc, name, KMPlayer::id_node_param);
392     else if (!strcasecmp (name, "embed"))
393         return new KMPlayer::DarkNode(m_doc, name,KMPlayer::id_node_html_embed);
394     return NULL;
395 }
396 
Generator(KMPlayerApp * a)397 Generator::Generator (KMPlayerApp *a)
398  : FileDocument (id_node_gen_document, QString (),
399             a->player ()->sources () ["listssource"]),
400    app (a), qprocess (NULL), data (NULL)
401 {}
402 
childFromTag(const QString & tag)403 KMPlayer::Node *Generator::childFromTag (const QString &tag) {
404     QByteArray ba = tag.toUtf8();
405     const char *ctag = ba.constData ();
406     if (!strcmp (ctag, "generator"))
407         return new GeneratorElement (m_doc, tag, id_node_gen_generator);
408     return NULL;
409 }
410 
genReadAsk(KMPlayer::Node * n)411 QString Generator::genReadAsk (KMPlayer::Node *n) {
412     QString title;
413     QString desc;
414     QString type = static_cast <Element *> (n)->getAttribute (
415             KMPlayer::Ids::attr_type);
416     QString key = static_cast<Element*>(n)->getAttribute ("key");
417     QString def = static_cast<Element*>(n)->getAttribute ("default");
418     QString input;
419     KConfigGroup cfg(KSharedConfig::openConfig(), "Generator Defaults");
420     if (!key.isEmpty ())
421         def = cfg.readEntry (key, def);
422     if (type == "file") {
423         input = KFileDialog::getOpenFileName (KUrl (def), QString(), app);
424     } else if (type == "dir") {
425         input = KFileDialog::getExistingDirectoryUrl (KUrl (def), app).toLocalFile ();
426         if (!input.isEmpty ())
427             input += QChar ('/');
428     } else {
429         for (KMPlayer::Node *c = n->firstChild (); c; c = c->nextSibling ())
430             switch (c->id) {
431                 case id_node_gen_title:
432                     title = c->innerText ().simplified ();
433                     break;
434                 case id_node_gen_description:
435                     desc = c->innerText ().simplified ();
436                     break;
437             }
438         input = QInputDialog::getText(0, title, desc, QLineEdit::Normal, def);
439     }
440     if (input.isNull ())
441         canceled = true;
442     else if (!key.isEmpty ())
443         cfg.writeEntry (key, input);
444     return input;
445 }
446 
genReadUriGet(KMPlayer::Node * n)447 QString Generator::genReadUriGet (KMPlayer::Node *n) {
448     QString str;
449     bool first = true;
450     for (KMPlayer::Node *c = n->firstChild (); c && !canceled; c = c->nextSibling ()) {
451         QString key;
452         QString val;
453         switch (c->id) {
454         case id_node_gen_http_key_value: {
455             KMPlayer::Node *q = c->firstChild ();
456             if (q) {
457                 key = genReadString (q);
458                 q = q->nextSibling ();
459                 if (q && !canceled)
460                     val = genReadString (q);
461             }
462             break;
463         }
464         default:
465             key = genReadString (c);
466             break;
467         }
468         if (!key.isEmpty ()) {
469             if (first) {
470                 str += QChar ('?');
471                 first = false;
472             } else {
473                 str += QChar ('&');
474             }
475             str += QUrl::toPercentEncoding (key);
476             if (!val.isEmpty ())
477                 str += QChar ('=') + QString (QUrl::toPercentEncoding (val));
478         }
479     }
480     return str;
481 }
482 
genReadString(KMPlayer::Node * n)483 QString Generator::genReadString (KMPlayer::Node *n) {
484     QString str;
485     bool need_quote = quote;
486     bool find_resource = false;
487     quote = false;
488     for (KMPlayer::Node *c = n->firstChild (); c && !canceled; c = c->nextSibling ())
489         switch (c->id) {
490         case id_node_gen_uri:
491         case id_node_gen_sequence:
492             str += genReadString (c);
493             break;
494         case id_node_gen_literal:
495             str += c->innerText ().simplified ();
496             break;
497         case id_node_gen_predefined: {
498             QString val = static_cast <Element *>(c)->getAttribute ("key");
499             if (val == "data" || val == "sysdata") {
500                 str += "kmplayer";
501                 find_resource = true;
502             }
503             break;
504         }
505         case id_node_gen_http_get:
506             str += genReadUriGet (c);
507             break;
508         case id_node_gen_ask:
509             str += genReadAsk (c);
510             break;
511         case KMPlayer::id_node_text:
512              str += c->nodeValue ().simplified ();
513         }
514     if (find_resource)
515         str = QStandardPaths::locate(QStandardPaths::GenericDataLocation, str);
516     if (!static_cast <Element *>(n)->getAttribute ("encoding").isEmpty ())
517         str = QUrl::toPercentEncoding (str);
518     if (need_quote) {
519         //from QProcess' parseCombinedArgString
520         str.replace (QChar ('"'), QString ("\"\"\""));
521         str = QChar ('"') + str + QChar ('"');
522         quote = true;
523     }
524     return str;
525 }
526 
genReadInput(KMPlayer::Node * n)527 QString Generator::genReadInput (KMPlayer::Node *n) {
528     quote = false;
529     return genReadString (n);
530 }
531 
genReadProcess(KMPlayer::Node * n)532 QString Generator::genReadProcess (KMPlayer::Node *n) {
533     QString process;
534     QString str;
535     quote = true;
536     for (KMPlayer::Node *c = n->firstChild (); c && !canceled; c = c->nextSibling ())
537         switch (c->id) {
538         case id_node_gen_program:
539             process = QString (genReadString (c));
540             break;
541         case id_node_gen_argument:
542             process += QChar (' ') + genReadString (c);
543             break;
544         }
545     return process;
546 }
547 
activate()548 void Generator::activate () {
549     QString input;
550     canceled = false;
551     KMPlayer::Node *n = firstChild ();
552     if (n && n->id == id_node_gen_generator) {
553         title = static_cast<Element *>(n)->getAttribute (
554                 KMPlayer::Ids::attr_name);
555         for (KMPlayer::Node *c = n->firstChild (); c && !canceled; c = c->nextSibling ())
556             switch (c->id) {
557             case id_node_gen_input:
558                 input = genReadInput (c);
559                 break;
560             case id_node_gen_process:
561                 process = genReadProcess (c);
562             }
563     }
564     if (canceled)
565         return;
566     if (!input.isEmpty () && process.isEmpty ()) {
567         message (KMPlayer::MsgInfoString, &input);
568         //openFile (m_control->m_app, input);
569     } else if (!process.isEmpty ()) {
570         data = new QTextStream (&buffer);
571         if (input.isEmpty ()) {
572             message (KMPlayer::MsgInfoString, &process);
573             begin ();
574         } else {
575             QString cmdline (input + " | " + process);
576             message (KMPlayer::MsgInfoString, &cmdline);
577             if (!media_info)
578                 media_info = new KMPlayer::MediaInfo (
579                         this, KMPlayer::MediaManager::Data);
580             state = state_activated;
581             media_info->wget (input);
582         }
583     }
584 }
585 
begin()586 void Generator::begin () {
587     if (!qprocess) {
588         qprocess = new QProcess (app);
589         connect (qprocess, SIGNAL (started ()),
590                  this, SLOT (started ()));
591         connect (qprocess, SIGNAL (error (QProcess::ProcessError)),
592                  this, SLOT (error (QProcess::ProcessError)));
593         connect (qprocess, SIGNAL (finished (int, QProcess::ExitStatus)),
594                  this, SLOT (finished ()));
595         connect (qprocess, SIGNAL (readyReadStandardOutput ()),
596                  this, SLOT (readyRead ()));
597     }
598     QString info;
599     if (media_info)
600         info = QString ("Input data ") +
601             QString::number (media_info->rawData ().size () / 1024.0) + "kb | ";
602     info += process;
603     message (KMPlayer::MsgInfoString, &info);
604     kDebug() << process;
605     qprocess->start (process);
606     state = state_began;
607 }
608 
deactivate()609 void Generator::deactivate () {
610     if (qprocess) {
611         disconnect (qprocess, SIGNAL (started ()),
612                     this, SLOT (started ()));
613         disconnect (qprocess, SIGNAL (error (QProcess::ProcessError)),
614                     this, SLOT (error (QProcess::ProcessError)));
615         disconnect (qprocess, SIGNAL (finished (int, QProcess::ExitStatus)),
616                     this, SLOT (finished ()));
617         disconnect (qprocess, SIGNAL (readyReadStandardOutput ()),
618                     this, SLOT (readyRead ()));
619         qprocess->kill ();
620         qprocess->deleteLater ();
621     }
622     qprocess = NULL;
623     delete data;
624     data = NULL;
625     buffer.clear ();
626     FileDocument::deactivate ();
627 }
628 
message(KMPlayer::MessageType msg,void * content)629 void Generator::message (KMPlayer::MessageType msg, void *content) {
630     if (KMPlayer::MsgMediaReady == msg) {
631         if (!media_info->rawData ().size ()) {
632             QString err ("No input data received");
633             message (KMPlayer::MsgInfoString, &err);
634             deactivate ();
635         } else {
636             begin ();
637         }
638     } else {
639         FileDocument::message (msg, content);
640     }
641 }
642 
readyRead()643 void Generator::readyRead () {
644     if (qprocess->bytesAvailable ())
645         *data << qprocess->readAll();
646     if (qprocess->state () == QProcess::NotRunning) {
647         if (!buffer.isEmpty ()) {
648             Playlist *pl = new Playlist (app, m_source, true);
649             KMPlayer::NodePtr n = pl;
650             pl->src.clear ();
651             QTextStream stream (&buffer, QIODevice::ReadOnly);
652             KMPlayer::readXML (pl, stream, QString (), false);
653             pl->title = title;
654             pl->normalize ();
655             message (KMPlayer::MsgInfoString, NULL);
656             bool reset_only = m_source == app->player ()->source ();
657             if (reset_only)
658                 app->player ()->stop ();
659             m_source->setDocument (pl, pl);
660             if (reset_only) {
661                 m_source->activate ();
662                 app->setCaption (getAttribute(KMPlayer::Ids::attr_name));
663             } else {
664                 app->player ()->setSource (m_source);
665             }
666         } else {
667             QString err ("No data received");
668             message (KMPlayer::MsgInfoString, &err);
669         }
670         deactivate ();
671     }
672 }
673 
started()674 void Generator::started () {
675     if (media_info) {
676         QByteArray &ba = media_info->rawData ();
677         // TODO validate utf8
678         if (ba.size ())
679             qprocess->write (ba);
680         qprocess->closeWriteChannel ();
681         return;
682     }
683     message (KMPlayer::MsgInfoString, &process);
684 }
685 
error(QProcess::ProcessError err)686 void Generator::error (QProcess::ProcessError err) {
687     kDebug () << (int)err;
688     QString msg ("Couldn't start process");
689     message (KMPlayer::MsgInfoString, &msg);
690     deactivate ();
691 }
692 
finished()693 void Generator::finished () {
694     if (active () && state_deferred != state)
695         readyRead ();
696 }
697 
698 struct GeneratorTag {
699     const char *tag;
700     short id;
701 } gen_tags[] = {
702     { "input", id_node_gen_input },
703     { "process", id_node_gen_process },
704     { "uri", id_node_gen_uri },
705     { "literal", id_node_gen_literal },
706     { "ask", id_node_gen_ask },
707     { "title", id_node_gen_title },
708     { "description", id_node_gen_description },
709     { "process", id_node_gen_process },
710     { "program", id_node_gen_program },
711     { "argument", id_node_gen_argument },
712     { "predefined", id_node_gen_predefined },
713     { "http-get", id_node_gen_http_get },
714     { "key-value", id_node_gen_http_key_value },
715     { "key", id_node_gen_sequence },
716     { "value", id_node_gen_sequence },
717     { "sequence", id_node_gen_sequence },
718     { NULL, -1 }
719 };
720 
childFromTag(const QString & tag)721 KMPlayer::Node *GeneratorElement::childFromTag (const QString &tag) {
722     QByteArray ba = tag.toUtf8();
723     const char *ctag = ba.constData ();
724     for (GeneratorTag *t = gen_tags; t->tag; ++t)
725         if (!strcmp (ctag, t->tag))
726             return new GeneratorElement (m_doc, tag, t->id);
727     return NULL;
728 }
729 
730