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

0001 /*
0002     SPDX-FileCopyrightText: 2002-2003 Koos Vriezen <koos.vriezen@gmail.com>
0003 
0004     SPDX-License-Identifier: LGPL-2.0-only
0005 */
0006 
0007 #include "kmplayer_part.h"
0008 
0009 #include <list>
0010 #include <algorithm>
0011 
0012 #include <QLabel>
0013 #include <QMenu>
0014 #include <QTimer>
0015 #include <QPushButton>
0016 #include <QSlider>
0017 #include <QStatusBar>
0018 #include <QFile>
0019 #include <QMetaObject>
0020 #include <QStandardPaths>
0021 
0022 class KXMLGUIClient; // workaround for kde3.3 on sarge with gcc4, kactioncollection.h does not forward declare KXMLGUIClient
0023 #include <KConfig>
0024 #include <KUrlAuthorized>
0025 #include <KIconLoader>
0026 #include <KLocalizedString>
0027 #if KCOREADDONS_VERSION >= QT_VERSION_CHECK(5, 77, 0)
0028 #include <KPluginMetaData>
0029 #endif
0030 #include <KIO/Job>
0031 
0032 #include "kmplayerpart_log.h"
0033 #include "kmplayerview.h"
0034 #include "playlistview.h"
0035 #include "kmplayercontrolpanel.h"
0036 #include "kmplayerconfig.h"
0037 #include "kmplayerprocess.h"
0038 #include "viewarea.h"
0039 
0040 #include <cstdlib>
0041 #include <unistd.h> // unlink, getpid
0042 
0043 using namespace KMPlayer;
0044 
0045 typedef std::list <KMPlayerPart *> KMPlayerPartList;
0046 
0047 class KMPlayerPartStatic : public GlobalShared<KMPlayerPartStatic>
0048 {
0049 public:
0050     KMPlayerPartStatic (KMPlayerPartStatic **);
0051     ~KMPlayerPartStatic () override;
0052     KMPlayerPartList partlist;
0053     int counter;
0054 };
0055 
0056 static KMPlayerPartStatic * kmplayerpart_static = nullptr;
0057 
0058 KMPlayerPartStatic::KMPlayerPartStatic (KMPlayerPartStatic **glob) : GlobalShared<KMPlayerPartStatic> (glob), counter (0) {
0059     Ids::init ();
0060 }
0061 
0062 KMPlayerPartStatic::~KMPlayerPartStatic () {
0063     kmplayerpart_static = nullptr;
0064     Ids::reset ();
0065     // delete map content
0066 }
0067 
0068 struct GroupPredicate
0069 {
0070     const KMPlayerPart * m_part;
0071     const QString & m_group;
0072     bool m_get_any;
0073     GroupPredicate(const KMPlayerPart *part, const QString &group, bool b=false)
0074         : m_part (part), m_group (group), m_get_any (b) {}
0075     bool operator () (const KMPlayerPart * part) const {
0076         return ((m_get_any && part != m_part &&
0077                     !part->master () && !part->url ().isEmpty ()) ||
0078                 (m_part->allowRedir (part->docBase ()) &&
0079                  (part->m_group == m_group ||
0080                   part->m_group == QString::fromLatin1("_master") ||
0081                   m_group == QString::fromLatin1("_master")) &&
0082                  (part->m_features & KMPlayerPart::Feat_Viewer) !=
0083                  (m_part->m_features & KMPlayerPart::Feat_Viewer)));
0084     }
0085 };
0086 
0087 //-----------------------------------------------------------------------------
0088 
0089 #if KCOREADDONS_VERSION < QT_VERSION_CHECK(5, 77, 0)
0090 KAboutData* KMPlayerFactory::s_about = nullptr;
0091 #endif
0092 
0093 KMPlayerFactory::KMPlayerFactory () {
0094 }
0095 
0096 KMPlayerFactory::~KMPlayerFactory () {
0097 }
0098 
0099 #if KCOREADDONS_VERSION < QT_VERSION_CHECK(5, 77, 0)
0100 KAboutData& KMPlayerFactory::aboutData() {
0101     if (!s_about) {
0102         s_about = new KAboutData("kmplayer", i18n("KMPlayer"), QStringLiteral(KMPLAYER_VERSION_STRING),
0103                 i18n("Embedded MPlayer by KDE"),
0104                 KAboutLicense::LGPL);
0105         s_about->addAuthor(QStringLiteral("Koos Vriezen"), QString(), "koos.vriezen@gmail.com");
0106     }
0107     return *s_about;
0108 }
0109 #endif
0110 
0111 QObject* KMPlayerFactory::create(const char *iface, QWidget* parentWidget, QObject* parent,
0112         const QVariantList& args, const QString&)
0113 {
0114     Q_UNUSED(iface)
0115 #if KCOREADDONS_VERSION >= QT_VERSION_CHECK(5, 77, 0)
0116     return new KMPlayerPart(parentWidget, parent, metaData(), args);
0117 #else
0118     return new KMPlayerPart(parentWidget, parent, args);
0119 #endif
0120 }
0121 
0122 //-----------------------------------------------------------------------------
0123 
0124 GrabDocument::GrabDocument (KMPlayerPart *part, const QString &url,
0125         const QString &file, PlayListNotify *)
0126  : SourceDocument (part->sources () ["urlsource"], url),
0127    m_grab_file (file),
0128    m_part (part) {
0129      id = id_node_grab_document;
0130      resolved = true;
0131 }
0132 
0133 void GrabDocument::activate () {
0134     media_info = new MediaInfo (this, MediaManager::AudioVideo);
0135     media_info->create ();
0136     qCDebug(LOG_KMPLAYER_PART) << src;
0137     Mrl::activate ();
0138 }
0139 
0140 void GrabDocument::undefer () {
0141     begin ();
0142 }
0143 
0144 void GrabDocument::begin () {
0145     setState (state_began);
0146     AudioVideoMedia *av = static_cast <AudioVideoMedia *> (media_info->media);
0147     qCDebug(LOG_KMPLAYER_PART) << m_grab_file;
0148     av->grabPicture (m_grab_file, 0);
0149 }
0150 
0151 void GrabDocument::message (MessageType msg, void *content) {
0152     if (MsgMediaFinished == msg) {
0153         state = state_finished;
0154         m_part->startUrl (QUrl (), m_grab_file);
0155         // deleted here by Source::reset
0156     } else {
0157         SourceDocument::message (msg, content);
0158     }
0159 }
0160 
0161 //-----------------------------------------------------------------------------
0162 
0163 static bool getBoolValue (const QString & value) {
0164     return (value.toLower() != QString::fromLatin1("false") &&
0165             value.toLower() != QString::fromLatin1("off") &&
0166             value.toLower() != QString::fromLatin1("0"));
0167 }
0168 
0169 #define SET_FEAT_ON(f) { m_features |= f; turned_off_features &= ~f; }
0170 #define SET_FEAT_OFF(f) { m_features &= ~f; turned_off_features |= f; }
0171 
0172 #if KCOREADDONS_VERSION >= QT_VERSION_CHECK(5, 77, 0)
0173     KMPlayerPart::KMPlayerPart(QWidget* wparent, QObject* ppart, const KPluginMetaData& metaData, const QVariantList &args)
0174 #else
0175 KMPlayerPart::KMPlayerPart (QWidget *wparent,
0176                     QObject* ppart, const QVariantList& args)
0177 #endif
0178  : PartBase (wparent, ppart, KSharedConfig::openConfig ("kmplayerrc")),
0179    m_master (nullptr),
0180    m_browserextension (new KMPlayerBrowserExtension (this)),
0181    m_liveconnectextension (new KMPlayerLiveConnectExtension (this)),
0182    m_playtime_info(nullptr),
0183    m_expected_view_width (0),
0184    m_expected_view_height (0),
0185    m_features (Feat_Unknown),
0186    m_started_emited (false),
0187    m_wait_npp_loaded (false)
0188 {
0189 #if KCOREADDONS_VERSION >= QT_VERSION_CHECK(5, 77, 0)
0190     setMetaData(metaData);
0191 #else
0192     setComponentData(KMPlayerFactory::aboutData());
0193 #endif
0194     qCDebug(LOG_KMPLAYER_PART) << "KMPlayerPart(" << this << ")::KMPlayerPart ()";
0195     bool show_fullscreen = false;
0196     if (!kmplayerpart_static)
0197         (void) new KMPlayerPartStatic (&kmplayerpart_static);
0198     else
0199         kmplayerpart_static->ref ();
0200     init (actionCollection (),
0201          QString ("/KMPlayerPart%1").arg(kmplayerpart_static->counter++), true);
0202     createBookmarkMenu (m_view->controlPanel ()->bookmarkMenu, actionCollection ());
0203     m_view->controlPanel ()->bookmarkAction->setVisible (true);
0204     ///*KAction *playact =*/ new KAction(i18n("P&lay"), QString ("player_play"), KShortcut (), this, SLOT(play ()), actionCollection (), "play");
0205     ///*KAction *pauseact =*/ new KAction(i18n("&Pause"), QString ("player_pause"), KShortcut (), this, SLOT(pause ()), actionCollection (), "pause");
0206     ///*KAction *stopact =*/ new KAction(i18n("&Stop"), QString ("player_stop"), KShortcut (), this, SLOT(stop ()), actionCollection (), "stop");
0207     //new KAction (i18n ("Increase Volume"), QString ("player_volume"), KShortcut (), this, SLOT (increaseVolume ()), actionCollection (), "edit_volume_up");
0208     //new KAction (i18n ("Decrease Volume"), QString ("player_volume"), KShortcut (), this, SLOT (decreaseVolume ()), actionCollection (), "edit_volume_down");
0209     Source *source = m_sources ["urlsource"];
0210     KMPlayer::ControlPanel * panel = m_view->controlPanel ();
0211     QVariantList::const_iterator it = args.begin ();
0212     QVariantList::const_iterator end = args.end ();
0213     int turned_off_features = 0;
0214     for ( ; it != end; ++it) {
0215         QString arg = (*it).toString();
0216         int equalPos = arg.indexOf("=");
0217         if (equalPos > 0) {
0218             QString name = arg.left(equalPos).toLower();
0219             QString value = arg.right(arg.size() - equalPos - 1);
0220             if (value.at(0)=='\"')
0221                 value = value.right (value.size () - 1);
0222             if (value.at (value.size () - 1) == '\"')
0223                 value.truncate (value.size () - 1);
0224             qCDebug(LOG_KMPLAYER_PART) << "name=" << name << " value=" << value;
0225             if (name == "href") {
0226                 m_href_url = value;
0227             } else if (name == QString::fromLatin1("target")) {
0228                 m_target = value;
0229             } else if (name == QString::fromLatin1("width")) {
0230                 m_noresize = true;
0231                 m_expected_view_width = value.toInt ();
0232             } else if (name == QString::fromLatin1("height")) {
0233                 m_noresize = true;
0234                 m_expected_view_height = value.toInt ();
0235             } else if (name == QString::fromLatin1("controls")) {
0236                 //http://service.real.com/help/library/guides/production8/realpgd.htm?src=noref,rnhmpg_080301,rnhmtn,nosrc
0237                 //http://service.real.com/help/library/guides/production8/htmfiles/control.htm
0238                 QStringList sl = value.split(QChar (','));
0239                 const QStringList::const_iterator e = sl.constEnd ();
0240                 for (QStringList::const_iterator i = sl.constBegin (); i != e; ++i) {
0241                     QString val_lower ((*i).toLower ());
0242                     if (val_lower == QString::fromLatin1("imagewindow")) {
0243                         SET_FEAT_ON (Feat_ImageWindow | Feat_Viewer)
0244                     } else if (val_lower == QString::fromLatin1("all")) {
0245                         m_features = (Feat_Controls | Feat_StatusBar);
0246                     } else if (val_lower == QString::fromLatin1("tacctrl")) {
0247                         SET_FEAT_ON (Feat_Label)
0248                     } else if (val_lower == QString::fromLatin1("controlpanel")) {
0249                         SET_FEAT_ON (Feat_Controls)
0250                     } else if (val_lower == QString::fromLatin1("infovolumepanel")){
0251                         SET_FEAT_ON (Feat_Controls) // TODO
0252                     } else if (val_lower == QString::fromLatin1("positionfield") ||
0253                             val_lower == QString::fromLatin1("positionslider")) {
0254                         setAutoControls (false);
0255                         panel->positionSlider ()->show ();
0256                         SET_FEAT_ON (Feat_Controls)
0257                     } else if ( val_lower == QString::fromLatin1("homectrl")) {
0258                         setAutoControls (false);
0259                         panel->button (KMPlayer::ControlPanel::button_config)->show();
0260                     } else if (val_lower == QString::fromLatin1("mutectrl") ||
0261                             val_lower == QString::fromLatin1("mutevolume")) {
0262                         setAutoControls (false);
0263                         panel->volumeBar()->setMinimumSize (QSize (20, panel->volumeBar()->minimumSize ().height ()));
0264                         panel->volumeBar()->show ();
0265                         SET_FEAT_ON (Feat_Controls)
0266                     } else if (val_lower == QString::fromLatin1("rwctrl")) {
0267                         setAutoControls (false);
0268                         panel->button (KMPlayer::ControlPanel::button_back)->show (); // rewind ?
0269                         SET_FEAT_ON (Feat_Controls)
0270                     } else if ( val_lower == QString::fromLatin1("ffctrl")) {
0271                         setAutoControls (false);
0272                         panel->button(KMPlayer::ControlPanel::button_forward)->show();
0273                         m_features = Feat_Controls;
0274                     } else if ( val_lower ==QString::fromLatin1("stopbutton")) {
0275                         setAutoControls (false);
0276                         panel->button (KMPlayer::ControlPanel::button_stop)->show ();
0277                         SET_FEAT_ON (Feat_Controls)
0278                     } else if (val_lower == QString::fromLatin1("playbutton") ||
0279                             val_lower ==QString::fromLatin1("playonlybutton")) {
0280                         setAutoControls (false);
0281                         panel->button (KMPlayer::ControlPanel::button_play)->show ();
0282                         SET_FEAT_ON (Feat_Controls)
0283                     } else if (val_lower ==QString::fromLatin1("pausebutton")) {
0284                         setAutoControls (false);
0285                         panel->button (KMPlayer::ControlPanel::button_pause)->show ();
0286                         SET_FEAT_ON (Feat_Controls)
0287                     } else if (val_lower == QString::fromLatin1("statusbar") ||
0288                             val_lower == QString::fromLatin1("statusfield")) {
0289                         SET_FEAT_ON (Feat_StatusBar)
0290                     } else if (val_lower == QString::fromLatin1("infopanel")) {
0291                         SET_FEAT_ON (Feat_InfoPanel)
0292                     } else if (val_lower == QString::fromLatin1("playlist")) {
0293                         SET_FEAT_ON (Feat_PlayList)
0294                     } else if (val_lower==QString::fromLatin1("volumeslider")) {
0295                         SET_FEAT_ON (Feat_VolumeSlider)
0296                         setAutoControls (false);
0297                         panel->volumeBar()->show ();
0298                         panel->volumeBar()->setMinimumSize (QSize (20, panel->volumeBar()->minimumSize ().height ()));
0299                     }
0300                 }
0301             } else if (name == QString::fromLatin1("uimode")) {
0302                 QString val_lower (value.toLower ());
0303                 if (val_lower == QString::fromLatin1("full"))
0304                     SET_FEAT_ON (Feat_All & ~(Feat_PlayList | Feat_ImageWindow))
0305                 // TODO: invisible, none, mini
0306             } else if (name == QString::fromLatin1("nolabels")) {
0307                 SET_FEAT_OFF (Feat_Label)
0308             } else if (name == QString::fromLatin1("nocontrols")) {
0309                 SET_FEAT_OFF (Feat_Controls | Feat_VolumeSlider)
0310             } else if (name == QString::fromLatin1("showdisplay")) {
0311                 // the author name, the clip name, and the copyright information
0312                 if (getBoolValue (value))
0313                     SET_FEAT_ON (Feat_InfoPanel)
0314                 else
0315                     SET_FEAT_OFF (Feat_InfoPanel)
0316             } else if (name == QString::fromLatin1("showcontrols") ||
0317                     name == QString::fromLatin1("controller")) {
0318                 if (getBoolValue (value))
0319                     SET_FEAT_ON (Feat_Viewer | Feat_Controls)
0320                 else
0321                     SET_FEAT_OFF (Feat_Controls | Feat_VolumeSlider)
0322             } else if (name == QString::fromLatin1("showstatusbar")) {
0323                 if (getBoolValue (value))
0324                     SET_FEAT_ON (Feat_Viewer | Feat_StatusBar)
0325                 else
0326                     SET_FEAT_OFF (Feat_StatusBar)
0327             // else showcaptioning/showgotobar/showpositioncontrols/showtracker
0328             } else if (name == QString::fromLatin1("console")) {
0329                 m_group = value.isEmpty() ? QString::fromLatin1("_anonymous") : value;
0330             } else if (name == QString::fromLatin1("__khtml__pluginbaseurl")) {
0331                 m_docbase = QUrl (value);
0332             } else if (name == QString::fromLatin1("src")) {
0333                 m_src_url = value;
0334             } else if (name == QString::fromLatin1("filename")) {
0335                 m_file_name = value;
0336             } else if (name == QString::fromLatin1 ("fullscreenmode")) {
0337                 show_fullscreen = getBoolValue (value);
0338             } else if (name == QString::fromLatin1 ("autostart")) {
0339                 source->setAutoPlay (getBoolValue (value));
0340         }
0341             // volume/clicktoplay/transparentatstart/animationatstart
0342             // autorewind/displaysize/border
0343             if (!name.startsWith (QString::fromLatin1 ("__khtml__")))
0344                 convertNode <KMPlayer::Element> (source->document ())->setAttribute (name, value);
0345         }
0346     }
0347     if (turned_off_features) {
0348         if (m_features == Feat_Unknown)
0349             m_features = (Feat_All & ~(Feat_PlayList | Feat_ImageWindow | Feat_StatusBar));
0350         m_features &= ~turned_off_features;
0351     }
0352     //KParts::Part::setWidget (m_view);
0353     setXMLFile("kmplayerpartui.rc");
0354     /*connect (panel->zoom50Action, SIGNAL (triggered (bool)),
0355             this, SLOT (setMenuZoom (bool)));
0356     panel->zoomMenu ()->connectItem (KMPlayer::ControlPanel::menu_zoom100,
0357                                       this, SLOT (setMenuZoom (int)));
0358     panel->zoomMenu ()->connectItem (KMPlayer::ControlPanel::menu_zoom150,
0359                                       this, SLOT (setMenuZoom (int)));*/
0360 
0361     m_view->setNoInfoMessages (m_features != Feat_InfoPanel);
0362     if (m_features == Feat_InfoPanel) {
0363         m_view->initDock (m_view->infoPanel ());
0364     } else if (m_features == Feat_PlayList) {
0365         m_view->initDock (m_view->playList ());
0366     } else if (m_features == Feat_StatusBar) {
0367         m_view->initDock (m_view->statusBar ());
0368     } else {
0369         m_view->initDock (m_view->viewArea ());
0370         if (m_features & Feat_StatusBar) {
0371             m_view->setStatusBarMode (KMPlayer::View::SB_Show);
0372             m_expected_view_height -= m_view->statusBar ()->height ();
0373         }
0374         if (m_features & (Feat_Controls | Feat_VolumeSlider)) {
0375             m_view->setControlPanelMode (m_features & Feat_Viewer ? KMPlayer::View::CP_Show : KMPlayer::View::CP_Only);
0376             m_expected_view_height -= m_view->controlPanel ()->height ();
0377         } else if (parent ())
0378             m_view->setControlPanelMode (KMPlayer::View::CP_Hide);
0379         else
0380             m_view->setControlPanelMode (KMPlayer::View::CP_AutoHide);
0381     }
0382     bool group_member = !m_group.isEmpty () && m_group != QString::fromLatin1("_unique") && m_features != Feat_Unknown;
0383     if (!group_member || m_features & Feat_Viewer) {
0384         // not part of a group or we're the viewer
0385         connectPanel (m_view->controlPanel ());
0386         if (m_features & Feat_StatusBar) {
0387             last_time_left = 0;
0388             connect (this, &KMPlayer::PartBase::positioned,
0389                      this, &KMPlayerPart::statusPosition);
0390             m_playtime_info = new QLabel("--:--");
0391             m_view->statusBar()->addPermanentWidget(m_playtime_info);
0392         }
0393     }
0394     if (group_member) {
0395         KMPlayerPartList::iterator i =kmplayerpart_static->partlist.begin ();
0396         KMPlayerPartList::iterator e =kmplayerpart_static->partlist.end ();
0397         GroupPredicate pred (this, m_group);
0398         for (i = std::find_if (i, e, pred);
0399                 i != e;
0400                 i = std::find_if (++i, e, pred)) {
0401             // found viewer and control part, exchange players now
0402             KMPlayerPart * vp = (m_features & Feat_Viewer) ? this : *i;
0403             KMPlayerPart * cp = (m_features & Feat_Viewer) ? *i : this;
0404             cp->connectToPart (vp);
0405         }
0406     } else
0407         m_group.truncate (0);
0408     kmplayerpart_static->partlist.push_back (this);
0409 
0410     if (m_view->isFullScreen () != show_fullscreen)
0411         m_view->fullScreen ();
0412 }
0413 
0414 #undef SET_FEAT_ON
0415 #undef SET_FEAT_OFF
0416 
0417 KMPlayerPart::~KMPlayerPart () {
0418     qCDebug(LOG_KMPLAYER_PART) << "KMPlayerPart::~KMPlayerPart";
0419     //if (!m_group.isEmpty ()) {
0420         KMPlayerPartList::iterator i = std::find (kmplayerpart_static->partlist.begin (), kmplayerpart_static->partlist.end (), this);
0421         if (i != kmplayerpart_static->partlist.end ())
0422             kmplayerpart_static->partlist.erase (i);
0423         else
0424             qCCritical(LOG_KMPLAYER_PART) << "KMPlayerPart::~KMPlayerPart group lost" << endl;
0425     //}
0426     if (!m_grab_file.isEmpty ())
0427         ::unlink (m_grab_file.toLocal8Bit ().data ());
0428     if (m_source)
0429         m_source->deactivate ();
0430     m_config = nullptr;
0431     kmplayerpart_static->unref ();
0432 }
0433 
0434 void KMPlayerPart::processCreated (KMPlayer::Process *p) {
0435 #ifdef KMPLAYER_WITH_NPP
0436     if (KMPlayer::NpPlayer* nppProcess = qobject_cast<KMPlayer::NpPlayer*>(p)) {
0437         if (m_wait_npp_loaded)
0438             connect (nppProcess, &KMPlayer::NpPlayer::loaded, this, &KMPlayerPart::nppLoaded);
0439         connect (nppProcess, &KMPlayer::NpPlayer::evaluateRequested,
0440                 m_liveconnectextension, &KMPlayerLiveConnectExtension::handleEvaluateRequest);
0441         connect (m_liveconnectextension, &KMPlayerLiveConnectExtension::requestGet,
0442                 nppProcess, &KMPlayer::NpPlayer::requestGet);
0443         connect (m_liveconnectextension, &KMPlayerLiveConnectExtension::requestCall,
0444                 nppProcess, &KMPlayer::NpPlayer::requestCall);
0445     }
0446 #endif
0447 }
0448 
0449 bool KMPlayerPart::allowRedir (const QUrl & url) const
0450 {
0451     return KUrlAuthorized::authorizeUrlAction ("redirect", m_docbase, url);
0452 }
0453 
0454 void KMPlayerPart::setAutoControls (bool b) {
0455     m_auto_controls = b;
0456     m_view->controlPanel ()->setAutoControls (b);
0457 }
0458 
0459 void KMPlayerPart::viewerPartDestroyed (QObject * o) {
0460     if (o == m_master)
0461         m_master = nullptr;
0462     qCDebug(LOG_KMPLAYER_PART) << "KMPlayerPart(" << this << ")::viewerPartDestroyed";
0463     const KMPlayerPartList::iterator e =kmplayerpart_static->partlist.end();
0464     KMPlayerPartList::iterator i = std::find_if (kmplayerpart_static->partlist.begin (), e, GroupPredicate (this, m_group));
0465     if (i != e && *i != this)
0466         (*i)->updatePlayerMenu (m_view->controlPanel ());
0467 }
0468 
0469 void KMPlayerPart::viewerPartProcessChanged (const char *) {
0470     const KMPlayerPartList::iterator e =kmplayerpart_static->partlist.end();
0471     KMPlayerPartList::iterator i = std::find_if (kmplayerpart_static->partlist.begin (), e, GroupPredicate (this, m_group));
0472     if (i != e && *i != this)
0473         (*i)->updatePlayerMenu (m_view->controlPanel ());
0474 }
0475 
0476 void KMPlayerPart::viewerPartSourceChanged(Source *o, Source *s) {
0477     qCDebug(LOG_KMPLAYER_PART) << "KMPlayerPart::source changed " << m_master;
0478     if (m_master && m_view) {
0479         connectSource (o, s);
0480         m_master->updatePlayerMenu (m_view->controlPanel ());
0481     }
0482 }
0483 
0484 bool KMPlayerPart::openUrl(const QUrl& _url) {
0485     QUrl url;
0486     KParts::OpenUrlArguments args = arguments ();
0487     Source * urlsource = m_sources ["urlsource"];
0488     KMPlayerPartList::iterator i =kmplayerpart_static->partlist.begin ();
0489     KMPlayerPartList::iterator e =kmplayerpart_static->partlist.end ();
0490     GroupPredicate pred (this, m_group);
0491     bool emit_started = !m_settings->clicktoplay;
0492 
0493     qCDebug(LOG_KMPLAYER_PART) << "KMPlayerPart::openUrl " << _url.url() << " " << args.mimeType();;
0494     if (args.mimeType () == "application/x-shockwave-flash" ||
0495             args.mimeType () == "application/futuresplash") {
0496         m_wait_npp_loaded = true;
0497         urlsource->setAvoidRedirects (true);
0498     }
0499 
0500     if (m_wait_npp_loaded && (_url.isEmpty () || _url == m_docbase)) {
0501         url = QUrl ("about:empty");
0502         m_wait_npp_loaded = emit_started = false;
0503     } else if (!m_file_name.isEmpty () && (_url.isEmpty () || _url == m_docbase)) {
0504         url = m_docbase.resolved(QUrl(m_file_name)); // fix misdetected SRC attr
0505     } else if (_url != m_docbase) {
0506         url = _url;
0507         if (!m_file_name.isEmpty() && _url.url().indexOf(m_file_name) < 0) {
0508             QUrl u = QUrl::fromUserInput(m_file_name);
0509             if ((u.scheme () == QLatin1String("mms")) ||
0510                     _url.scheme().isEmpty()) {
0511                 // see if we somehow have to merge these
0512                 int p = _url.port ();
0513                 if (p > 0)
0514                     u.setPort (p);
0515                 if (u.path ().isEmpty ())
0516                     u.setPath (QChar ('/') + _url.host ());
0517                 if (allowRedir (u)) {
0518                     url = u;
0519                     qCDebug(LOG_KMPLAYER_PART) << "KMPlayerPart::openUrl compose " << m_file_name << " " << _url.url() << " ->" << u.url();
0520                 }
0521             }
0522         }
0523     } else { // if url is the container document, then it's an empty URL
0524         if (m_features & Feat_Viewer) // damn, look in the group
0525             for (i = std::find_if (i, e, pred);
0526                     i != e;
0527                     i = std::find_if (++i, e, pred))
0528                 if (!(*i)->url ().isEmpty ()) {
0529                     url = (*i)->url ();
0530                     break;
0531                 }
0532     }
0533     if (!m_href_url.isEmpty () &&
0534             !KUrlAuthorized::authorizeUrlAction (
0535                 "redirect", url, m_docbase.resolved(QUrl(m_href_url))))
0536            m_href_url.truncate (0);
0537     if (m_href_url.isEmpty ())
0538         setUrl (url.url ());
0539 
0540     if (url.isEmpty ()) {
0541         if (!m_master && !(m_features & Feat_Viewer))
0542             // no master set, wait for a viewer to attach or timeout
0543             QTimer::singleShot (50, this, &KMPlayerPart::waitForImageWindowTimeOut);
0544         return true;
0545     }
0546     m_src_url = url.url ();
0547 
0548     if (!m_group.isEmpty () && !(m_features & Feat_Viewer)) {
0549         // group member, not the image window
0550         for (i = std::find_if (i, e, pred);
0551                 i != e;
0552                 i = std::find_if (++i, e, pred))
0553             if ((*i)->url ().isEmpty ()) // image window created w/o url
0554                 return (*i)->startUrl (_url);
0555         QTimer::singleShot (50, this, &KMPlayerPart::waitForImageWindowTimeOut);
0556         //qCCritical(LOG_KMPLAYER_PART) << "Not the ImageWindow and no ImageWindow found" << endl;
0557         return true;
0558     }
0559     if (!m_view || !url.isValid ())
0560         return false;
0561 
0562     if (!args.mimeType ().isEmpty ())
0563         urlsource->document ()->mrl ()->mimetype = args.mimeType ();
0564 
0565     startUrl (url);
0566 
0567     if (emit_started && urlsource->autoPlay ()) {
0568         Q_EMIT started (nullptr);
0569         m_started_emited = true;
0570     }
0571     return true;
0572 }
0573 
0574 void
0575 KMPlayerPart::openUrl(const QUrl& u, const QString& t, const QString& srv) {
0576     m_browserextension->requestOpenURL (u, t, srv);
0577 }
0578 
0579 bool KMPlayerPart::openNewURL (const QUrl & url) {
0580     m_file_name.truncate (0);
0581     m_href_url.truncate (0);
0582     m_sources ["urlsource"]->setAutoPlay (true);
0583     return openUrl (url);
0584 }
0585 
0586 bool KMPlayerPart::startUrl(const QUrl &uri, const QString &img)
0587 {
0588     Source * src = sources () ["urlsource"];
0589     QUrl url (uri);
0590     qCDebug(LOG_KMPLAYER_PART) << "uri '" << uri << "' img '" << img;
0591     if (url.isEmpty ()) {
0592         url = QUrl::fromUserInput(m_src_url);
0593     } else if (m_settings->grabhref && !m_href_url.isEmpty ()) {
0594         static int counter;
0595         m_href_url = m_docbase.resolved(QUrl(m_href_url)).url ();
0596         m_grab_file = QString ("%1grab-%2-%3.jpg")
0597             .arg (QStandardPaths::writableLocation(QStandardPaths::GenericDataLocation)+ "kmplayer/")
0598             .arg (getpid ())
0599             .arg (counter++);
0600         Node *n = new GrabDocument (this, url.url (), m_grab_file, src);
0601         src->setUrl (url.url ());
0602         m_src_url = m_href_url;
0603         src->setDocument (n, n);
0604         setSource (src);
0605         if (m_source->avoidRedirects ())
0606             m_source->activate();
0607         return true;
0608     }
0609     if ((m_settings->clicktoplay || !m_href_url.isEmpty ()) &&
0610             (m_features & Feat_Viewer ||
0611              Feat_Unknown == m_features) &&
0612             m_expected_view_width > 10 &&
0613             m_expected_view_height > 10 &&
0614             parent () &&
0615             !strcmp ("KHTMLPart", parent ()->metaObject ()->className())) {
0616         QString pic (img);
0617         if (!pic.isEmpty ()) {
0618             QFile file (pic);
0619             if (!file.exists ()) {
0620                 m_grab_file.truncate (0);
0621                 pic.truncate (0);
0622             } else if (!file.size ()) {
0623                 pic.truncate (0);
0624             }
0625         }
0626         QString img = pic.isEmpty ()
0627             ? QUrl::fromLocalFile (KIconLoader::global()->iconPath (
0628                         QString::fromLatin1 ("video-x-generic"),
0629 #ifdef KMPLAYER_WITH_CAIRO
0630                         -128
0631 #else
0632                         -32
0633 #endif
0634                         )).url ()
0635             : pic;
0636 #ifdef KMPLAYER_WITH_CAIRO
0637         QString smil = QString::fromLatin1 (
0638           "<smil><head><layout>"
0639           "<region id='reg1' left='12.5%' top='5%' right='12.5%' bottom='5%' "
0640           "background-color='#202030' showBackground='whenActive'/>"
0641           "<region id='reg2'/>"
0642           "<region id='reg3'/>"
0643           "</layout>"
0644           "<transition id='clockwipe1' dur='1' type='clockWipe'/>"
0645           "</head>"
0646           "<body>"
0647           "<a href='%1'%2>"
0648           "<img id='image1' src='%3' region='reg%4' fit='meet' "
0649           "begin='.5' dur='indefinite' transIn='clockwipe1'/>"
0650           "</a>"
0651           "<video id='video1' region='reg2' fit='meet'/>"
0652           "</body></smil>"
0653           ).arg (m_target.isEmpty ()
0654               ? QString ("#video1")
0655               : m_href_url.isEmpty () ? m_src_url : m_href_url)
0656            .arg (m_target.isEmpty ()
0657                    ? QString ()
0658                    : QString (" target='%1'").arg (m_target))
0659            .arg (img)
0660            .arg (pic.isEmpty () ? "1" : "3");
0661         QByteArray ba = smil.toUtf8 ();
0662         QTextStream ts (&ba, QIODevice::ReadOnly);
0663         if (m_source)
0664             m_source->deactivate ();
0665         NodePtr doc = src->document ();
0666         KMPlayer::readXML (doc, ts, QString (), false);
0667         NodePtr n = doc->document ()->getElementById ("video1");
0668         if (n) {
0669             Mrl *mrl = new GenericURL (doc, url.url ());
0670             n->appendChild (mrl);
0671             mrl->mimetype = doc->document ()->mimetype;
0672             mrl->opener = n;
0673             mrl->setAttributes (convertNode <Element> (doc)->attributes ());
0674             n->closed ();
0675             NodePtr im = doc->document ()->getElementById ("image1");
0676             if (im)
0677                 im->mrl ()->access_granted = true;
0678         }
0679         doc->document ()->resolved = true;
0680         if (m_source) {
0681             m_source->activate();
0682         } else {
0683             setSource (src);
0684             if (m_source->avoidRedirects ())
0685                 m_source->activate();
0686         }
0687 #else
0688         if (m_view->setPicture (img)) {
0689             connect (m_view, &KMPlayer::View::pictureClicked,
0690                      this, &KMPlayerPart::pictureClicked);
0691             Q_EMIT completed ();
0692             m_started_emited = false;
0693         } else {
0694             return PartBase::openUrl(m_href_url.isEmpty() ? url : QUrl::fromUserInput(m_href_url));
0695         }
0696 #endif
0697         return true;
0698     } else
0699         return PartBase::openUrl(m_href_url.isEmpty() ? url : QUrl::fromUserInput(m_href_url));
0700 }
0701 
0702 #ifndef KMPLAYER_WITH_CAIRO
0703 void KMPlayerPart::pictureClicked () {
0704     m_view->setPicture (QString ());
0705     PartBase::openUrl (QUrl::fromUserInput(m_src_url));
0706 }
0707 #endif
0708 
0709 void KMPlayerPart::waitForImageWindowTimeOut () {
0710     if (!m_master) {
0711         // still no ImageWindow attached, eg. audio only
0712         const KMPlayerPartList::iterator e =kmplayerpart_static->partlist.end();
0713         GroupPredicate pred (this, m_group);
0714         KMPlayerPartList::iterator i = std::find_if (kmplayerpart_static->partlist.begin (), e, pred);
0715         bool noattach = (i == e || *i == this);
0716         if (noattach) {
0717             if (!url ().isEmpty ()) {
0718                 m_features |= KMPlayerPart::Feat_Viewer; //hack, become the view
0719                 for (i = std::find_if (kmplayerpart_static->partlist.begin (), e, pred); i != e; i = std::find_if (++i, e, pred))
0720                     (*i)->connectToPart (this);
0721                 PartBase::openUrl (url ());
0722             } else { // see if we can attach to something out there ..
0723                 i = std::find_if (kmplayerpart_static->partlist.begin (), e, GroupPredicate (this, m_group, true));
0724                 noattach = (i == e);
0725             }
0726         }
0727         if (!noattach)
0728             connectToPart (*i);
0729     }
0730 }
0731 
0732 bool KMPlayerPart::closeUrl () {
0733     if (!m_group.isEmpty ()) {
0734         kmplayerpart_static->partlist.remove (this);
0735         m_group.truncate (0);
0736     }
0737     return PartBase::closeUrl ();
0738 }
0739 
0740 void KMPlayerPart::connectToPart (KMPlayerPart * m) {
0741     m_master = m;
0742     m->connectPanel (m_view->controlPanel ());
0743     m->updatePlayerMenu (m_view->controlPanel ());
0744     if (m_features & Feat_PlayList)
0745         m->connectPlaylist (m_view->playList ());
0746     if (m_features & Feat_InfoPanel)
0747         m->connectInfoPanel (m_view->infoPanel ());
0748     connectSource (m_source, m->source ());
0749     connect (m, &QObject::destroyed,
0750             this, &KMPlayerPart::viewerPartDestroyed);
0751     connect (m, &KMPlayer::PartBase::processChanged,
0752             this, &KMPlayerPart::viewerPartProcessChanged);
0753     connect (m, &KMPlayer::PartBase::sourceChanged,
0754             this, &KMPlayerPart::viewerPartSourceChanged);
0755     if (m_features & Feat_StatusBar) {
0756         last_time_left = 0;
0757         connect (m, &KMPlayer::PartBase::positioned,
0758                  this, &KMPlayerPart::statusPosition);
0759         m_playtime_info = new QLabel("--:--");
0760         m_view->statusBar()->addPermanentWidget(m_playtime_info);
0761     }
0762 }
0763 
0764 void KMPlayerPart::setLoaded (int percentage) {
0765     PartBase::setLoaded (percentage);
0766     if (percentage < 100) {
0767         m_browserextension->setLoadingProgress (percentage);
0768         m_browserextension->infoMessage
0769         // xgettext: no-c-format
0770             (QString::number (percentage) + i18n ("% Cache fill"));
0771     }
0772 }
0773 
0774 void KMPlayerPart::playingStarted () {
0775     const KMPlayerPartList::iterator e =kmplayerpart_static->partlist.end();
0776     KMPlayerPartList::iterator i = std::find_if (kmplayerpart_static->partlist.begin (), e, GroupPredicate (this, m_group));
0777     if (i != e && *i != this && m_view && (*i)->source()) {
0778         m_view->controlPanel ()->setPlaying (true);
0779         m_view->controlPanel ()->showPositionSlider(!!(*i)->source()->length ());
0780         m_view->controlPanel()->enableSeekButtons((*i)->source()->isSeekable());
0781         Q_EMIT loading (100);
0782     } else if (m_source)
0783         KMPlayer::PartBase::playingStarted ();
0784     else
0785         return; // ugh
0786     qCDebug(LOG_KMPLAYER_PART) << "KMPlayerPart::processStartedPlaying ";
0787     if (m_settings->sizeratio && !m_noresize && m_source->width() > 0 && m_source->height() > 0)
0788         m_liveconnectextension->setSize (m_source->width(), m_source->height());
0789     m_browserextension->setLoadingProgress (100);
0790     if (m_started_emited && !m_wait_npp_loaded) {
0791         Q_EMIT completed ();
0792         m_started_emited = false;
0793     }
0794     m_liveconnectextension->started ();
0795     m_browserextension->infoMessage (i18n("KMPlayer: Playing"));
0796 }
0797 
0798 void KMPlayerPart::playingStopped () {
0799     KMPlayer::PartBase::playingStopped ();
0800     if (m_started_emited && !m_wait_npp_loaded) {
0801         m_started_emited = false;
0802         m_browserextension->setLoadingProgress (100);
0803         Q_EMIT completed ();
0804     }
0805     m_liveconnectextension->finished ();
0806     m_browserextension->infoMessage (i18n ("KMPlayer: Stop Playing"));
0807     if (m_view)
0808         m_view->controlPanel ()->setPlaying (false);
0809 }
0810 
0811 void KMPlayerPart::nppLoaded () {
0812     if (m_started_emited && m_wait_npp_loaded) {
0813         m_wait_npp_loaded = false;
0814         m_started_emited = false;
0815         m_browserextension->setLoadingProgress (100);
0816         Q_EMIT completed ();
0817     }
0818 }
0819 
0820 void KMPlayerPart::setMenuZoom (int /*id*/) {
0821     /*int w = 0, h = 0;
0822     if (m_source)
0823         m_source->dimensions (w, h);
0824     if (id == KMPlayer::ControlPanel::menu_zoom100) {
0825         m_liveconnectextension->setSize (w, h);
0826         return;
0827     }
0828     float scale = 1.5;
0829     if (id == KMPlayer::ControlPanel::menu_zoom50)
0830         scale = 0.5;
0831     if (m_view)
0832         m_liveconnectextension->setSize (int (scale * m_view->viewArea ()->width ()),
0833                                          int (scale * m_view->viewArea ()->height()));*/
0834 }
0835 
0836 void KMPlayerPart::statusPosition (int pos, int length) {
0837     int left = (length - pos) / 10;
0838     if (left != last_time_left) {
0839         last_time_left = left;
0840         QString text ("--:--");
0841         if (left > 0) {
0842             int h = left / 3600;
0843             int m = (left % 3600) / 60;
0844             int s = left % 60;
0845             if (h > 0)
0846                 text = QString::asprintf ("%d:%02d:%02d", h, m, s);
0847             else
0848                 text = QString::asprintf ("%02d:%02d", m, s);
0849         }
0850         m_playtime_info->setText(text);
0851     }
0852 }
0853 
0854 QString KMPlayerPart::doEvaluate (const QString &script) {
0855     return m_liveconnectextension->evaluate (
0856             QString ("this.__kmplayer__res=" ) + script);
0857 }
0858 
0859 //---------------------------------------------------------------------
0860 
0861 KMPlayerBrowserExtension::KMPlayerBrowserExtension (KMPlayerPart * parent)
0862   : KParts::BrowserExtension (parent) {
0863 }
0864 
0865 void KMPlayerBrowserExtension::urlChanged (const QString & url) {
0866     Q_EMIT setLocationBarUrl (url);
0867 }
0868 
0869 void KMPlayerBrowserExtension::setLoadingProgress (int percentage) {
0870     Q_EMIT loadingProgress (percentage);
0871 }
0872 
0873 void KMPlayerBrowserExtension::saveState (QDataStream & stream) {
0874     stream << static_cast <PartBase *> (parent ())->url ().url ();
0875 }
0876 
0877 void KMPlayerBrowserExtension::restoreState (QDataStream & stream) {
0878     QString url;
0879     stream >> url;
0880     static_cast <PartBase *> (parent ())->openUrl (QUrl(url));
0881 }
0882 
0883 void KMPlayerBrowserExtension::requestOpenURL (const QUrl & url, const QString & target, const QString & service)
0884 {
0885     KParts::OpenUrlArguments args;
0886     KParts::BrowserArguments bargs;
0887     bargs.frameName = target;
0888     args.setMimeType (service);
0889     Q_EMIT openUrlRequest (url, args, bargs);
0890 }
0891 
0892 //---------------------------------------------------------------------
0893 /*
0894  * add
0895  * .error.errorCount
0896  * .error.item(count)
0897  *   .errorDescription
0898  *   .errorCode
0899  * .controls.stop()
0900  * .controls.play()
0901  */
0902 
0903 enum JSCommand {
0904     notsupported,
0905     canpause, canplay, canstop, canseek,
0906     isfullscreen, isloop, isaspect, showcontrolpanel,
0907     length, width, height, playstate, position, source, setsource, protocol,
0908     gotourl, nextentry, jsc_pause, play, preventry, start, stop,
0909     volume, setvolume,
0910     prop_error, prop_source, prop_volume,
0911     prop_qt_status, prop_qt_rate
0912 };
0913 
0914 struct JSCommandEntry
0915 {
0916     const char * name;
0917     JSCommand command;
0918     const char * defaultvalue;
0919     const KParts::LiveConnectExtension::Type rettype;
0920 };
0921 
0922 // keep this list in alphabetic order
0923 // http://service.real.com/help/library/guides/realonescripting/browse/htmfiles/embedmet.htm
0924 static const JSCommandEntry JSCommandList [] = {
0925     { "CanPause", canpause, nullptr, KParts::LiveConnectExtension::TypeBool },
0926     { "CanPlay", canplay, nullptr, KParts::LiveConnectExtension::TypeBool },
0927     { "CanStop", canstop, nullptr, KParts::LiveConnectExtension::TypeBool },
0928     { "DoGotoURL", notsupported, nullptr, KParts::LiveConnectExtension::TypeVoid },
0929     { "DoNextEntry", notsupported, "false", KParts::LiveConnectExtension::TypeBool },
0930     { "DoPause", jsc_pause, "true", KParts::LiveConnectExtension::TypeBool },
0931     { "DoPlay", play, nullptr, KParts::LiveConnectExtension::TypeBool },
0932     { "DoPlayPause", play, nullptr, KParts::LiveConnectExtension::TypeBool },
0933     { "DoPrevEntry", notsupported, "false", KParts::LiveConnectExtension::TypeBool },
0934     { "DoStop", stop, nullptr, KParts::LiveConnectExtension::TypeBool },
0935     { "FileName", prop_source, nullptr, KParts::LiveConnectExtension::TypeString },
0936     { "GetAuthor", notsupported, "noname", KParts::LiveConnectExtension::TypeString },
0937     { "GetAutoGoToURL", notsupported, "true", KParts::LiveConnectExtension::TypeBool },
0938     { "GetBackgroundColor", notsupported, "#ffffff", KParts::LiveConnectExtension::TypeString },
0939     { "GetBandwidthAverage", notsupported, "64", KParts::LiveConnectExtension::TypeNumber },
0940     { "GetBandwidthCurrent", notsupported, "64", KParts::LiveConnectExtension::TypeNumber },
0941     { "GetBufferingTimeElapsed", notsupported, "0", KParts::LiveConnectExtension::TypeNumber },
0942     { "GetBufferingTimeRemaining", notsupported, "0", KParts::LiveConnectExtension::TypeNumber },
0943     { "GetCanSeek", canseek, nullptr, KParts::LiveConnectExtension::TypeBool },
0944     { "GetCenter", notsupported, "true", KParts::LiveConnectExtension::TypeBool },
0945     { "GetClipHeight", height, nullptr, KParts::LiveConnectExtension::TypeNumber },
0946     { "GetClipWidth", width, nullptr, KParts::LiveConnectExtension::TypeNumber },
0947     { "GetConnectionBandwidth", notsupported, "64", KParts::LiveConnectExtension::TypeNumber },
0948     { "GetConsole", notsupported, "unknown", KParts::LiveConnectExtension::TypeString },
0949     { "GetConsoleEvents", notsupported, "false", KParts::LiveConnectExtension::TypeBool },
0950     { "GetControls", notsupported, "buttons", KParts::LiveConnectExtension::TypeString },
0951     { "GetCopyright", notsupported, "(c) whoever", KParts::LiveConnectExtension::TypeString },
0952     { "GetCurrentEntry", notsupported, "1", KParts::LiveConnectExtension::TypeNumber },
0953     { "GetDuration", notsupported, "0", KParts::LiveConnectExtension::TypeNumber },
0954     { "GetDRMInfo", notsupported, "RNBA", KParts::LiveConnectExtension::TypeString },
0955     { "GetDoubleSize", notsupported, "false", KParts::LiveConnectExtension::TypeBool },
0956     { "GetEntryAbstract", notsupported, "abstract", KParts::LiveConnectExtension::TypeString },
0957     { "GetEntryAuthor", notsupported, "noname", KParts::LiveConnectExtension::TypeString },
0958     { "GetEntryCopyright", notsupported, "(c)", KParts::LiveConnectExtension::TypeString },
0959     { "GetEntryTitle", notsupported, "title", KParts::LiveConnectExtension::TypeString },
0960     { "GetFullScreen", isfullscreen, nullptr, KParts::LiveConnectExtension::TypeBool },
0961     { "GetImageStatus", notsupported, "false", KParts::LiveConnectExtension::TypeBool },
0962     { "GetLastErrorMoreInfoURL", notsupported, "no error", KParts::LiveConnectExtension::TypeString },
0963     { "GetLastErrorRMACode", notsupported, "0", KParts::LiveConnectExtension::TypeNumber },
0964     { "GetLastErrorSeverity", notsupported, "6", KParts::LiveConnectExtension::TypeNumber },
0965     { "GetLastErrorUserCode", notsupported, "0", KParts::LiveConnectExtension::TypeNumber },
0966     { "GetLastErrorUserString", notsupported, "no error", KParts::LiveConnectExtension::TypeString },
0967     { "GetLastMessage", notsupported, "no error", KParts::LiveConnectExtension::TypeString },
0968     { "GetLastStatus", notsupported, "no error", KParts::LiveConnectExtension::TypeString },
0969     { "GetLength", length, nullptr, KParts::LiveConnectExtension::TypeNumber },
0970     { "GetLiveState", notsupported, "false", KParts::LiveConnectExtension::TypeBool },
0971     { "GetLoop", isloop, nullptr, KParts::LiveConnectExtension::TypeBool },
0972     { "GetMaintainAspect", isaspect, nullptr, KParts::LiveConnectExtension::TypeBool },
0973     { "GetMute", notsupported, "false", KParts::LiveConnectExtension::TypeBool },
0974     { "GetNumEntries", notsupported, "1", KParts::LiveConnectExtension::TypeNumber },
0975     { "GetNumLoop", notsupported, "0", KParts::LiveConnectExtension::TypeNumber },
0976     { "GetNumSources", notsupported, "1", KParts::LiveConnectExtension::TypeNumber },
0977     { "GetOriginalSize", notsupported, "true", KParts::LiveConnectExtension::TypeBool },
0978     { "GetPacketsEarly", notsupported, "0", KParts::LiveConnectExtension::TypeNumber },
0979     { "GetPacketsLate", notsupported, "0", KParts::LiveConnectExtension::TypeNumber },
0980     { "GetPacketsMissing", notsupported, "0", KParts::LiveConnectExtension::TypeNumber },
0981     { "GetPacketsOutOfOrder", notsupported, "0", KParts::LiveConnectExtension::TypeNumber },
0982     { "GetPacketsReceived", notsupported, "0", KParts::LiveConnectExtension::TypeNumber },
0983     { "GetPacketsTotal", notsupported, "0", KParts::LiveConnectExtension::TypeNumber },
0984     { "GetPlayState", playstate, nullptr, KParts::LiveConnectExtension::TypeNumber },
0985     { "GetPluginStatus", prop_qt_status, nullptr, KParts::LiveConnectExtension::TypeString },
0986     { "GetPosition", position, nullptr, KParts::LiveConnectExtension::TypeNumber },
0987     { "GetPreFetch", notsupported, "false", KParts::LiveConnectExtension::TypeBool },
0988     { "GetRate", prop_qt_rate, nullptr, KParts::LiveConnectExtension::TypeNumber },
0989     { "GetShowAbout", notsupported, "false", KParts::LiveConnectExtension::TypeBool },
0990     { "GetShowPreferences", notsupported, "false", KParts::LiveConnectExtension::TypeBool },
0991     { "GetShowStatistics", notsupported, "false", KParts::LiveConnectExtension::TypeBool },
0992     { "GetShuffle", notsupported, "false", KParts::LiveConnectExtension::TypeBool },
0993     { "GetSource", source, nullptr, KParts::LiveConnectExtension::TypeString },
0994     { "GetSourceTransport", protocol, nullptr, KParts::LiveConnectExtension::TypeString },
0995     { "GetStereoState", notsupported, "true", KParts::LiveConnectExtension::TypeBool },
0996     { "GetTitle", notsupported, "title", KParts::LiveConnectExtension::TypeString },
0997     { "GetVersionInfo", notsupported, "version", KParts::LiveConnectExtension::TypeString },
0998     { "GetVolume", volume, "100", KParts::LiveConnectExtension::TypeNumber },
0999     { "GetWantErrors", notsupported, "false", KParts::LiveConnectExtension::TypeBool },
1000     { "GetWantKeyboardEvents", notsupported, "false", KParts::LiveConnectExtension::TypeBool },
1001     { "GetWantMouseEvents", notsupported, "false", KParts::LiveConnectExtension::TypeBool },
1002     { "HasNextEntry", notsupported, "false", KParts::LiveConnectExtension::TypeBool },
1003     { "Pause", jsc_pause, nullptr, KParts::LiveConnectExtension::TypeBool },
1004     { "Play", play, nullptr, KParts::LiveConnectExtension::TypeBool },
1005     { "SetAuthor", notsupported, "true", KParts::LiveConnectExtension::TypeBool },
1006     { "SetAutoGoToURL", notsupported, "true", KParts::LiveConnectExtension::TypeBool },
1007     { "SetAutoStart", notsupported, "true", KParts::LiveConnectExtension::TypeBool },
1008     { "SetBackgroundColor", notsupported, "true", KParts::LiveConnectExtension::TypeBool },
1009     { "SetCanSeek", notsupported, "true", KParts::LiveConnectExtension::TypeBool },
1010     { "SetCenter", notsupported, "true", KParts::LiveConnectExtension::TypeBool },
1011     { "SetConsole", notsupported, "true", KParts::LiveConnectExtension::TypeBool },
1012     { "SetConsoleEvents", notsupported, "true", KParts::LiveConnectExtension::TypeBool },
1013     { "SetControls", notsupported, "true", KParts::LiveConnectExtension::TypeBool },
1014     { "SetCopyright", notsupported, "true", KParts::LiveConnectExtension::TypeBool },
1015     { "SetCurrentPosition", notsupported, "true", KParts::LiveConnectExtension::TypeBool },
1016     { "SetDoubleSize", notsupported, "true", KParts::LiveConnectExtension::TypeBool },
1017     { "SetFileName", setsource, nullptr, KParts::LiveConnectExtension::TypeBool },
1018     { "SetFullScreen", notsupported, "true", KParts::LiveConnectExtension::TypeBool },
1019     { "SetImageStatus", notsupported, "true", KParts::LiveConnectExtension::TypeBool },
1020     { "SetLoop", notsupported, "true", KParts::LiveConnectExtension::TypeBool },
1021     { "SetMaintainAspect", notsupported, "true", KParts::LiveConnectExtension::TypeBool },
1022     { "SetMute", notsupported, "true", KParts::LiveConnectExtension::TypeBool },
1023     { "SetNumLoop", notsupported, "true", KParts::LiveConnectExtension::TypeBool },
1024     { "SetOriginalSize", notsupported, "true", KParts::LiveConnectExtension::TypeBool },
1025     { "SetPosition", notsupported, "true", KParts::LiveConnectExtension::TypeBool },
1026     { "SetPreFetch", notsupported, "true", KParts::LiveConnectExtension::TypeBool },
1027     { "SetShowAbout", notsupported, "true", KParts::LiveConnectExtension::TypeBool },
1028     { "SetShowPreferences", notsupported, "true", KParts::LiveConnectExtension::TypeBool },
1029     { "SetShowStatistics", notsupported, "true", KParts::LiveConnectExtension::TypeBool },
1030     { "SetShuffle", notsupported, "true", KParts::LiveConnectExtension::TypeBool },
1031     { "SetSource", setsource, nullptr, KParts::LiveConnectExtension::TypeBool },
1032     { "SetTitle", notsupported, "true", KParts::LiveConnectExtension::TypeBool },
1033     { "SetVolume", setvolume, "true", KParts::LiveConnectExtension::TypeBool },
1034     { "SetWantErrors", notsupported, "true", KParts::LiveConnectExtension::TypeBool },
1035     { "SetWantKeyboardEvents", notsupported, "true", KParts::LiveConnectExtension::TypeBool },
1036     { "SetWantMouseEvents", notsupported, "true", KParts::LiveConnectExtension::TypeBool },
1037     { "ShowControls", showcontrolpanel, "true", KParts::LiveConnectExtension::TypeBool },
1038     { "Start", start, nullptr, KParts::LiveConnectExtension::TypeBool },
1039     { "Stop", stop, nullptr, KParts::LiveConnectExtension::TypeBool },
1040     { "Volume", prop_volume, "100", KParts::LiveConnectExtension::TypeNumber },
1041     { "errorCode", prop_error, "0",KParts::LiveConnectExtension::TypeNumber },
1042     { "pause", jsc_pause, nullptr, KParts::LiveConnectExtension::TypeBool },
1043     { "play", play, nullptr, KParts::LiveConnectExtension::TypeBool },
1044     { "put", prop_source, nullptr, KParts::LiveConnectExtension::TypeString },
1045     { "stop", stop, nullptr, KParts::LiveConnectExtension::TypeBool },
1046     { "volume", volume, nullptr, KParts::LiveConnectExtension::TypeBool },
1047 };
1048 
1049 static const JSCommandEntry * getJSCommandEntry (const char * name, int start = 0, int end = sizeof (JSCommandList)/sizeof (JSCommandEntry)) {
1050     if (end - start < 2) {
1051         if (start != end && !strcasecmp (JSCommandList[start].name, name))
1052             return &JSCommandList[start];
1053         return nullptr;
1054     }
1055     int mid = (start + end) / 2;
1056     int cmp = strcasecmp (JSCommandList[mid].name, name);
1057     if (cmp < 0)
1058         return getJSCommandEntry (name, mid + 1, end);
1059     if (cmp > 0)
1060         return getJSCommandEntry (name, start, mid);
1061     return &JSCommandList[mid];
1062 }
1063 
1064 KMPlayerLiveConnectExtension::KMPlayerLiveConnectExtension (KMPlayerPart * parent)
1065   : KParts::LiveConnectExtension (parent), player (parent),
1066     lastJSCommandEntry (nullptr),
1067     object_counter (0),
1068     m_started (false),
1069     m_enablefinish (false),
1070     m_evaluating (false),
1071     m_skip_put (false) {
1072       connect (parent, &KParts::ReadOnlyPart::started, this, &KMPlayerLiveConnectExtension::started);
1073 }
1074 
1075 KMPlayerLiveConnectExtension::~KMPlayerLiveConnectExtension() {
1076     qCDebug(LOG_KMPLAYER_PART) << "KMPlayerLiveConnectExtension::~KMPlayerLiveConnectExtension()";
1077 }
1078 
1079 void KMPlayerLiveConnectExtension::started () {
1080     m_started = true;
1081 }
1082 
1083 void KMPlayerLiveConnectExtension::finished () {
1084     if (m_started && m_enablefinish) {
1085         KParts::LiveConnectExtension::ArgList args;
1086         args.push_back (qMakePair (KParts::LiveConnectExtension::TypeString, QString("if (window.onFinished) onFinished();")));
1087         Q_EMIT partEvent (0, "eval", args);
1088         m_started = true;
1089         m_enablefinish = false;
1090     }
1091 }
1092 
1093 QString KMPlayerLiveConnectExtension::evaluate (const QString &script) {
1094     KParts::LiveConnectExtension::ArgList args;
1095     args.push_back(qMakePair(KParts::LiveConnectExtension::TypeString, script));
1096     script_result.clear ();
1097     Q_EMIT partEvent (0, "eval", args);
1098     //qCDebug(LOG_KMPLAYER_PART) << script << script_result;
1099     return script_result;
1100 }
1101 
1102 void KMPlayerLiveConnectExtension::handleEvaluateRequest (
1103         const QString & scr, bool store, QString & result) {
1104     m_evaluating = true;
1105 
1106     QString script (scr);
1107     script = script.replace ('\\', "\\\\");
1108     script = script.replace ('\n', "\\n");
1109     script = script.replace ('\r', "");
1110     script = script.replace ('"', "\\\"");
1111     QString obj_var = QString ("this.__kmplayer__obj_%1").arg (object_counter);
1112     script = obj_var + QString ("=eval(\"%1\")").arg (script);
1113     QString eval_result = evaluate (script);
1114 
1115     bool clear_result = true;
1116     if (!store) {
1117         result = eval_result;
1118         if (scr.startsWith ("this.__kmplayer__obj_")) {
1119             // TODO add dbus method for this
1120             int p = scr.indexOf ("=null", 21);
1121             if (p > -1) {
1122                 int i = scr.mid (21, p - 21).toInt ();
1123                 if (i == (int)object_counter-1)
1124                     object_counter--; // works most of the time :-)
1125             }
1126         }
1127     } else {
1128         script = QString ("this.__kmplayer__res=typeof(%1)").arg (obj_var);
1129         QString result_type = evaluate (script);
1130 
1131         if (result_type == "string") {
1132             result = QString ("s:") + eval_result;
1133         } else if (result_type == "object" ||
1134                 result_type == "function" ||
1135                 result_type.startsWith ("[")) {
1136             result = QString ("o:") + obj_var;
1137             clear_result = false;
1138             object_counter++;
1139         } else if (result_type == "number") {
1140             result = QString ("n:") + eval_result;
1141         } else if (result_type == "boolean") {
1142             result = QString ("b:") + eval_result;
1143         } else if (result_type == "undefined" || result_type == "null") {
1144             result = QString ("u:") + eval_result;
1145         } else {
1146             result = "error";
1147         }
1148     }
1149     if (clear_result)
1150         evaluate (obj_var + "=null");
1151 
1152     script_result.clear ();
1153 
1154     m_evaluating = false;
1155 }
1156 
1157 static
1158 bool str2LC (const QString s, KParts::LiveConnectExtension::Type &type, QString &rval) {
1159     if (s == "error")
1160         return false;
1161     if (s == "o:function") {
1162         type = KParts::LiveConnectExtension::TypeFunction;
1163     } else if (s.startsWith (QChar ('\'')) && s.endsWith (QChar ('\''))) {
1164         type = KParts::LiveConnectExtension::TypeString;
1165         rval = s.mid (1, s.size () - 2);
1166     } else if (s == "true" || s == "false") {
1167         type = KParts::LiveConnectExtension::TypeBool;
1168         rval = s;
1169     } else {
1170         bool ok;
1171         s.toInt (&ok);
1172         if (!ok)
1173             s.toDouble (&ok);
1174         if (ok) {
1175             type = KParts::LiveConnectExtension::TypeNumber;
1176             rval = s;
1177         } else {
1178             type = KParts::LiveConnectExtension::TypeVoid;
1179             rval = s;
1180         }
1181     }
1182     return true;
1183 }
1184 
1185 bool KMPlayerLiveConnectExtension::get
1186   (const unsigned long id, const QString & name,
1187    KParts::LiveConnectExtension::Type & type,
1188    unsigned long & rid, QString & rval)
1189 {
1190     if (name.startsWith ("__kmplayer__obj_")) {
1191         if (m_evaluating)
1192             return false;
1193         rid = 0;
1194         type = KParts::LiveConnectExtension::TypeString;
1195         rval = "Access denied";
1196         return true;
1197     }
1198     if (name.startsWith ("__kmplayer_func")) {
1199         rid = id;
1200         type = KParts::LiveConnectExtension::TypeFunction;
1201         return true;
1202     }
1203     if (name.startsWith ("__kmplayer_util_") ||
1204             redir_funcs.indexOf(name) >= 0)
1205         return false;
1206     if (name == "__kmplayer_unique_name") {
1207         rval = QString ("__kmplayer__obj_%1").arg (object_counter);
1208         type = KParts::LiveConnectExtension::TypeString;
1209         rid = id;
1210         object_counter++;
1211         m_allow = rval;
1212         return true;
1213     }
1214     rid = id;
1215     QString req_result;
1216     Q_EMIT requestGet (id, name, &req_result);
1217     if (!req_result.isEmpty ()) {
1218         if (str2LC (req_result, type, rval)) {
1219             if (KParts::LiveConnectExtension::TypeFunction == type) {
1220                 m_skip_put = true;
1221                 if (!redir_funcs.size ())
1222                     evaluate (
1223                             "this.__kmplayer_util_make_arg = function(arg) {"
1224                             "  var t = typeof arg;"
1225                             "  if (t == 'number')"
1226                             "    return 'n:' + arg;"
1227                             "  if (t == 'object') {"
1228                             "    var s = this.__kmplayer_unique_name;"
1229                             "    this[s] = arg;"
1230                             "    return 'o:this.' + s;"
1231                             "  }"
1232                             "  if (t == 'function') {"
1233                             "    var s = this.__kmplayer_unique_name;"
1234                             "    this[s] = arg;"
1235                             "    return 'o:this.' + s;"
1236                             "  }"
1237                             "  if (t == 'boolean')"
1238                             "    return 'b:' + arg;"
1239                             "  if (t == 'undefined' || t == null)"
1240                             "    return 'u:' + arg;"
1241                             "  var s = '' + arg;"
1242                             "  s = s.replace('\\\\', '\\\\\\\\');"
1243                             "  s = s.replace('\\n', '\\\\n');"
1244                             "  return 's:' + s;"
1245                             "}");
1246                 evaluate (QString (
1247                             "this.%1=function(){"
1248                             "  var args=[];"
1249                             "  for (var i=0;i<arguments.length;++i)"
1250                             "      args.push (this.__kmplayer_util_make_arg("
1251                             "                                   arguments[i]));"
1252                             "  return this.__kmplayer_func('%2',args.join('\\n'));"
1253                             "}")
1254                         .arg (name)
1255                         .arg (name));
1256                 redir_funcs.push_back (name);
1257                 m_skip_put = false;
1258                 return false;
1259             }
1260             return true;
1261         }
1262     }
1263     qCDebug(LOG_KMPLAYER_PART) << "get " << name;
1264     const JSCommandEntry * entry = getJSCommandEntry (name.toLatin1 ().constData ());
1265     if (!entry)
1266         return false;
1267     type = entry->rettype;
1268     switch (entry->command) {
1269         case prop_source:
1270             type = KParts::LiveConnectExtension::TypeString;
1271             rval = player->url ().url ();
1272             break;
1273         case prop_volume:
1274             if (player->view ())
1275                 rval = QString::number (player->viewWidget ()->controlPanel()->volumeBar()->value());
1276             break;
1277         case prop_error:
1278             type = KParts::LiveConnectExtension::TypeNumber;
1279             rval = QString::number (0);
1280             break;
1281         case prop_qt_status:
1282             rval = player->getStatus ();
1283             break;
1284         case prop_qt_rate:
1285             rval = QString::number (0.0);
1286             if (player->source() && player->source()->document() &&
1287                     player->source()->document()->state !=
1288                         KMPlayer::Node::state_deferred &&
1289                     player->source()->document()->unfinished ())
1290                 rval = QString::number (1.0);
1291             break;
1292         default:
1293             lastJSCommandEntry = entry;
1294             type = KParts::LiveConnectExtension::TypeFunction;
1295     }
1296     return true;
1297 }
1298 
1299 bool KMPlayerLiveConnectExtension::put
1300   (const unsigned long, const QString & name, const QString & val) {
1301     if (m_skip_put)
1302         return false;
1303     if (name == "__kmplayer__res") {
1304         script_result = val;
1305         return true;
1306     }
1307     if (name.startsWith ("__kmplayer__obj_")) {
1308         script_result = val;
1309         if (name == m_allow) {
1310             m_allow.clear ();
1311             return false;
1312         }
1313         return !m_evaluating;
1314     }
1315 
1316     qCDebug(LOG_KMPLAYER_PART) << "put " << name << "=" << val;
1317 
1318     const JSCommandEntry * entry = getJSCommandEntry (name.toLatin1 ().constData ());
1319     if (!entry)
1320         return false;
1321     switch (entry->command) {
1322         case prop_source: {
1323             const QUrl url = QUrl::fromUserInput(val);
1324             if (player->allowRedir (url))
1325                 player->openNewURL (url);
1326             break;
1327         }
1328         case prop_volume:
1329             if (player->view ())
1330                 player->viewWidget ()->controlPanel()->volumeBar()->setValue(val.toInt ());
1331             break;
1332         default:
1333             return false;
1334     }
1335     return true;
1336 }
1337 
1338 static QString unescapeArg (const QString &arg) {
1339     QString s;
1340     bool last_escape = false;
1341     for (int i = 0; i < arg.length (); ++i)
1342         switch (arg[i].unicode ()) {
1343         case '\\':
1344             if (last_escape) {
1345                 s += QChar ('\\');
1346                 last_escape = false;
1347             } else {
1348                 last_escape = true;
1349             }
1350             break;
1351         case 'n':
1352             if (last_escape) {
1353                 s += QChar ('\n');
1354                 last_escape = false;
1355                 break;
1356             } // else fall through
1357         default:
1358             if (last_escape) {
1359                 qCCritical(LOG_KMPLAYER_PART) << "unescape error " << arg;
1360                 last_escape = false;
1361             }
1362             s += arg[i];
1363         }
1364     return s;
1365 }
1366 
1367 bool KMPlayerLiveConnectExtension::call
1368   (const unsigned long id, const QString & name,
1369    const QStringList & args, KParts::LiveConnectExtension::Type & type,
1370    unsigned long & rid, QString & rval) {
1371     QString func = name;
1372     QString req_result;
1373     QStringList arglst;
1374     int oid = id;
1375     QList<QString>::const_iterator it = args.begin ();
1376     if (func == "__kmplayer_func") {
1377         if ( it != args.end ()) {
1378             func = *it;
1379             oid = 0;
1380             if ( ++it != args.end ()) {
1381                 QStringList a = (*it).split ("\n");
1382                 for (QStringList::iterator i = a.begin(); i != a.end (); ++i)
1383                     arglst << ((*i).startsWith ("s:") ? unescapeArg (*i) : *i);
1384             }
1385         } else {
1386             return false;
1387         }
1388     } else {
1389         for (; it != args.end (); ++it) {
1390             bool ok;
1391             int iv = (*it).toInt (&ok);
1392             if (ok) {
1393                 arglst << QString ("n:%1").arg (iv);
1394             } else {
1395                 double dv = (*it).toDouble (&ok);
1396                 if (ok) {
1397                     arglst << QString ("n:%1").arg (dv);
1398                 } else {
1399                     arglst << QString ("s:%1").arg (*it);
1400                 }
1401             }
1402         }
1403     }
1404     rid = oid;
1405     Q_EMIT requestCall (oid, func, arglst, &req_result);
1406     if (!req_result.isEmpty ()) {
1407         if (str2LC (req_result, type, rval))
1408             return true;
1409     }
1410     qCDebug(LOG_KMPLAYER_PART) << "entry " << func;
1411     const JSCommandEntry * entry = lastJSCommandEntry;
1412     const QByteArray latin1 = func.toLatin1 ();
1413     if (!entry || strcmp (entry->name, latin1.constData ()))
1414         entry = getJSCommandEntry (latin1.constData ());
1415     if (!entry)
1416         return false;
1417     for (QStringList::size_type i = 0; i < args.size (); ++i)
1418         qCDebug(LOG_KMPLAYER_PART) << "      " << args[i];
1419     if (!player->view ())
1420         return false;
1421     type = entry->rettype;
1422     switch (entry->command) {
1423         case notsupported:
1424             if (entry->rettype != KParts::LiveConnectExtension::TypeVoid)
1425                 rval = entry->defaultvalue;
1426             break;
1427         case canpause:
1428             rval = (player->playing () && !player->viewWidget ()->controlPanel()->button (KMPlayer::ControlPanel::button_pause)->isChecked ()) ? "true" : "false";
1429             break;
1430         case canplay:
1431             rval = (!player->playing () || player->viewWidget ()->controlPanel()->button (KMPlayer::ControlPanel::button_pause)->isChecked ()) ? "true" : "false";
1432             break;
1433         case canstop:
1434             rval = player->playing () ? "true" : "false";
1435             break;
1436         case canseek:
1437             rval = player->source ()->isSeekable () ? "true" : "false";
1438             break;
1439         case play:
1440             if (args.size ()) {
1441                 const QUrl url = QUrl::fromUserInput(args.first ());
1442                 if (player->allowRedir (url))
1443                     player->openNewURL (url);
1444             } else
1445                 player->play ();
1446             rval = "true";
1447             break;
1448         case start:
1449             player->play ();
1450             rval = "true";
1451             break;
1452         case stop:
1453             player->stop ();
1454             rval = "true";
1455             break;
1456         case showcontrolpanel:
1457             if (args.size () &&
1458                     (args.first () == QString::fromLatin1 ("0") ||
1459                      args.first () == QString::fromLatin1 ("false")))
1460                 player->viewWidget ()->setControlPanelMode (KMPlayer::View::CP_Hide);
1461             else
1462                 player->viewWidget ()->setControlPanelMode (KMPlayer::View::CP_Show);
1463             break;
1464         case jsc_pause:
1465             player->pause ();
1466             rval = "true";
1467             break;
1468         case isloop:
1469             rval = player->settings ()->loop ? "true" : "false";
1470             break;
1471         case isaspect:
1472             rval = player->settings ()->sizeratio ? "true" : "false";
1473             break;
1474         case isfullscreen:
1475             rval = player->viewWidget ()->isFullScreen () ? "true" : "false";
1476             break;
1477         case length:
1478             rval.setNum (player->source ()->length ());
1479             break;
1480         case width:
1481             rval.setNum (player->source ()->width ());
1482             break;
1483         case height:
1484             rval.setNum (player->source ()->height ());
1485             break;
1486         case playstate: // FIXME 0-6
1487             rval = player->playing () ? "3" : "0";
1488             break;
1489         case position:
1490             rval.setNum (player->position ());
1491             break;
1492         case protocol:
1493             rval = player->url ().scheme();
1494             break;
1495         case setsource:
1496             rval ="false";
1497             if (args.size ()) {
1498                 const QUrl url = QUrl::fromUserInput (args.first ());
1499                 if (player->allowRedir (url) && player->openNewURL (url))
1500                     rval = "true";
1501             }
1502             break;
1503         case setvolume:
1504             if (!args.size ())
1505                 return false;
1506             player->viewWidget ()->controlPanel()->volumeBar()->setValue(args.first ().toInt ());
1507             rval = "true";
1508             break;
1509         case source:
1510             rval = player->url ().url ();
1511             break;
1512         case volume:
1513             if (player->view ())
1514                 rval = QString::number (player->viewWidget ()->controlPanel()->volumeBar()->value());
1515             break;
1516         default:
1517             return false;
1518     }
1519     return true;
1520 }
1521 
1522 void KMPlayerLiveConnectExtension::unregister (const unsigned long) {
1523 }
1524 
1525 void KMPlayerLiveConnectExtension::setSize (int w, int h) {
1526     KMPlayer::View * view = static_cast <KMPlayer::View*> (player->view ());
1527     if (view->controlPanelMode () == KMPlayer::View::CP_Show)
1528         h += view->controlPanel()->height();
1529     const QString jscode = QString::asprintf("try { eval(\"this.setAttribute('WIDTH',%d);this.setAttribute('HEIGHT',%d)\"); } catch(e){}", w, h);
1530     KParts::LiveConnectExtension::ArgList args;
1531     args.push_back (qMakePair (KParts::LiveConnectExtension::TypeString, jscode));
1532     Q_EMIT partEvent (0, "eval", args);
1533 }
1534 
1535 #include "moc_kmplayer_part.cpp"