File indexing completed on 2024-04-21 12:25:51

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 "config-kmplayer.h"
0008 
0009 #include <cmath>
0010 
0011 #include <QApplication>
0012 #include <QByteArray>
0013 #include <QCursor>
0014 #include <QTimer>
0015 #include <QPair>
0016 #include <QPushButton>
0017 #include <QStandardPaths>
0018 #include <QSlider>
0019 #include <QFile>
0020 #include <QRegExp>
0021 #include <QProcess>
0022 #include <QDBusConnection>
0023 #include <QDBusMessage>
0024 #include <QMimeDatabase>
0025 #include <QMimeType>
0026 
0027 #include <KMessageBox>
0028 #include <KAboutData>
0029 #include <KBookmarkMenu>
0030 #include <KBookmarkManager>
0031 #include <KBookmark>
0032 #include <KConfig>
0033 #include <KConfigGroup>
0034 #include <KIconLoader>
0035 #include <KLocalizedString>
0036 #include <KProtocolInfo>
0037 #include <KIO/Global>
0038 #include <KIO/Job>
0039 #include <KUrlAuthorized>
0040 
0041 #include "kmplayercommon_log.h"
0042 #include "kmplayerpartbase.h"
0043 #include "kmplayerview.h"
0044 #include "playmodel.h"
0045 #include "playlistview.h"
0046 #include "kmplayerprocess.h"
0047 #include "viewarea.h"
0048 #include "kmplayercontrolpanel.h"
0049 #include "kmplayerconfig.h"
0050 #include "kmplayer_smil.h"
0051 #include "mediaobject.h"
0052 #include "partadaptor.h"
0053 
0054 namespace KMPlayer {
0055 
0056 class BookmarkOwner : public KBookmarkOwner {
0057 public:
0058     BookmarkOwner (PartBase *);
0059     ~BookmarkOwner () override {}
0060     void openBookmark(const KBookmark &bm, Qt::MouseButtons mb, Qt::KeyboardModifiers km) override;
0061     QString currentTitle() const override;
0062     QString currentURL() const;
0063 private:
0064     PartBase * m_player;
0065 };
0066 
0067 } // namespace
0068 
0069 using namespace KMPlayer;
0070 
0071 BookmarkOwner::BookmarkOwner (PartBase * player)
0072     : m_player (player) {}
0073 
0074 void BookmarkOwner::openBookmark(const KBookmark &bm, Qt::MouseButtons, Qt::KeyboardModifiers) {
0075     if (!bm.isNull ())
0076         m_player->openUrl (bm.url ());
0077 }
0078 
0079 QString BookmarkOwner::currentTitle () const {
0080     return m_player->source ()->prettyName ();
0081 }
0082 
0083 QString BookmarkOwner::currentURL () const {
0084     return m_player->source ()->url ().url ();
0085 }
0086 
0087 //-----------------------------------------------------------------------------
0088 
0089 PartBase::PartBase (QWidget * wparent, QObject * parent, KSharedConfigPtr config)
0090  : KMediaPlayer::Player (wparent, "kde_kmplayer_part", parent),
0091    m_config (config),
0092    m_view (new View (wparent)),
0093    m_settings (new Settings (this, config)),
0094    m_media_manager (new MediaManager (this)),
0095    m_play_model (new PlayModel (this, KIconLoader::global ())),
0096    m_source (nullptr),
0097    m_bookmark_menu (nullptr),
0098    m_update_tree_timer (0),
0099    m_rec_timer (0),
0100    m_noresize (false),
0101    m_auto_controls (true),
0102    m_bPosSliderPressed (false),
0103    m_in_update_tree (false),
0104    m_update_tree_full (false)
0105 {
0106     m_sources ["urlsource"] = new URLSource (this);
0107 
0108     QString bmfile = QStandardPaths::locate(QStandardPaths::GenericDataLocation, "kmplayer/bookmarks.xml");
0109     QString localbmfile = QStandardPaths::writableLocation(QStandardPaths::GenericDataLocation) + "/kmplayer/bookmarks.xml";
0110     if (localbmfile != bmfile) {
0111         bool bmfileCopied = QFile(bmfile).copy(localbmfile);
0112         qCDebug(LOG_KMPLAYER_COMMON) << "bookmarks.xml copied successfully?" << bmfileCopied;
0113     }
0114     m_bookmark_manager = KBookmarkManager::managerForFile (localbmfile, "kmplayer");
0115     m_bookmark_owner = new BookmarkOwner (this);
0116 }
0117 
0118 void PartBase::showConfigDialog () {
0119     m_settings->show ("URLPage");
0120 }
0121 
0122 void PartBase::showPlayListWindow () {
0123     // note, this is also the slot of application's view_playlist action, but
0124     // anyhow, actions don't work for the fullscreen out-of-the-box, so ...
0125     if (m_view->viewArea ()->isFullScreen ())
0126         fullScreen ();
0127     else if (m_view->viewArea ()->isMinimalMode ())
0128         ; //done by app: m_view->viewArea ()->minimalMode ();
0129     else
0130         m_view->toggleShowPlaylist ();
0131 }
0132 
0133 void PartBase::addBookMark (const QString & t, const QString & url) {
0134     KBookmarkGroup b = m_bookmark_manager->root ();
0135     b.addBookmark (t, QUrl (url), KIO::iconNameForUrl(QUrl(url)));
0136     m_bookmark_manager->emitChanged (b);
0137 }
0138 
0139 void PartBase::init (KActionCollection * action_collection, const QString &objname, bool transparent) {
0140     KParts::Part::setWidget (m_view);
0141     m_view->init (action_collection, transparent);
0142     connect(m_settings, &Settings::configChanged, this, &PartBase::settingsChanged);
0143     m_settings->readConfig ();
0144     m_settings->applyColorSetting (false);
0145     connect (m_view, &View::urlDropped,
0146              this, QOverload<const QList<QUrl>&>::of(&PartBase::openUrl));
0147     connectPlaylist (m_view->playList ());
0148     connectInfoPanel (m_view->infoPanel ());
0149 
0150     (void) new PartAdaptor (this);
0151     QDBusConnection::sessionBus().registerObject (objname, this);
0152 }
0153 
0154 void PartBase::connectPanel (ControlPanel * panel) {
0155     /*panel->contrastSlider ()->setValue (m_settings->contrast);
0156     panel->brightnessSlider ()->setValue (m_settings->brightness);
0157     panel->hueSlider ()->setValue (m_settings->hue);
0158     panel->saturationSlider ()->setValue (m_settings->saturation);
0159     panel->volumeBar ()->setValue (m_settings->volume);*/
0160     connect (panel->button (ControlPanel::button_playlist), &QPushButton::clicked, this, &PartBase::showPlayListWindow);
0161     connect (panel->button (ControlPanel::button_back), &QPushButton::clicked, this, &PartBase::back);
0162     connect (panel->button (ControlPanel::button_play), &QPushButton::clicked, this, &PartBase::play);
0163     connect (panel->button (ControlPanel::button_forward), &QPushButton::clicked, this, &PartBase::forward);
0164     connect (panel->button (ControlPanel::button_pause), &QPushButton::clicked, this, &PartBase::pause);
0165     connect (panel->button (ControlPanel::button_stop), &QPushButton::clicked, this, &PartBase::stop);
0166     connect (panel->button (ControlPanel::button_record), &QPushButton::clicked, this, QOverload<>::of(&PartBase::record));
0167     connect (panel->volumeBar (), &VolumeBar::volumeChanged, this, &PartBase::volumeChanged);
0168     connect (panel->positionSlider (), &QSlider::valueChanged, this, &PartBase::positionValueChanged);
0169     connect (panel->positionSlider (), &QSlider::sliderPressed, this, &PartBase::posSliderPressed);
0170     connect (panel->positionSlider (), &QSlider::sliderReleased, this, &PartBase::posSliderReleased);
0171     connect (this, &PartBase::positioned, panel, &ControlPanel::setPlayingProgress);
0172     connect (this, &PartBase::loading, panel, &ControlPanel::setLoadingProgress);
0173     /*connect (panel->contrastSlider (), SIGNAL (valueChanged(int)), this, SLOT (contrastValueChanged(int)));
0174     connect (panel->brightnessSlider (), SIGNAL (valueChanged(int)), this, SLOT (brightnessValueChanged(int)));
0175     connect (panel->hueSlider (), SIGNAL (valueChanged(int)), this, SLOT (hueValueChanged(int)));
0176     connect (panel->saturationSlider (), SIGNAL (valueChanged(int)), this, SLOT (saturationValueChanged(int)));*/
0177     connect (this, &PartBase::languagesUpdated,
0178              panel, &ControlPanel::setLanguages);
0179     connect (panel->audioMenu, &QMenu::triggered, this, &PartBase::audioSelected);
0180     connect (panel->subtitleMenu, &QMenu::triggered, this, &PartBase::subtitleSelected);
0181     connect (panel->playerMenu, &QMenu::triggered, this, &PartBase::slotPlayerMenu);
0182     connect (this, &PartBase::panelActionToggled, panel, &ControlPanel::actionToggled);
0183     connect (panel->fullscreenAction, &QAction::triggered,
0184             this, &PartBase::fullScreen);
0185     connect (panel->configureAction, &QAction::triggered,
0186             this, &PartBase::showConfigDialog);
0187     connect (panel->videoConsoleAction, &QAction::triggered,
0188             m_view, &View::toggleVideoConsoleWindow);
0189     connect (panel->playlistAction, &QAction::triggered,
0190             m_view, &View::toggleShowPlaylist);
0191     connect (this, &PartBase::statusUpdated,
0192              panel->view (), &View::setStatusMessage);
0193     //connect (panel (), &QPushButton::clicked, m_settings, SLOT (show ()));
0194 }
0195 
0196 void PartBase::createBookmarkMenu(QMenu *owner, KActionCollection *ac) {
0197     m_bookmark_menu = new KBookmarkMenu (m_bookmark_manager, m_bookmark_owner, owner, ac);
0198 }
0199 
0200 void PartBase::connectPlaylist (PlayListView * playlist) {
0201     playlist->setModel (m_play_model);
0202     connect (m_play_model, &PlayModel::updating,
0203              playlist, &PlayListView::modelUpdating);
0204     connect (m_play_model, &PlayModel::updated,
0205              playlist, &PlayListView::modelUpdated);
0206     connect (playlist->selectionModel(), &QItemSelectionModel::currentChanged,
0207              playlist, &PlayListView::slotCurrentItemChanged);
0208     connect (playlist, QOverload<const QString &, const QString &>::of(&PlayListView::addBookMark),
0209              this, &PartBase::addBookMark);
0210     connect (playlist, &QTreeView::activated,
0211              this, &PartBase::playListItemActivated);
0212     connect (playlist, &QTreeView::clicked,
0213              this, &PartBase::playListItemClicked);
0214     connect (this, &PartBase::treeChanged,
0215              m_play_model, QOverload<int, NodePtr, NodePtr, bool, bool>::of(&PlayModel::updateTree));
0216 }
0217 
0218 void PartBase::connectInfoPanel (InfoWindow * infopanel) {
0219     connect (this, &PartBase::infoUpdated,
0220              infopanel->view (), &View::setInfoMessage);
0221 }
0222 
0223 PartBase::~PartBase () {
0224     qCDebug(LOG_KMPLAYER_COMMON) << "PartBase::~PartBase";
0225     m_view = (View*) nullptr;
0226     stopRecording ();
0227     stop ();
0228     if (m_source)
0229         m_source->deactivate ();
0230     delete m_media_manager;
0231     if (m_record_doc)
0232         m_record_doc->document ()->dispose ();
0233     delete m_settings;
0234     delete m_bookmark_menu;
0235     delete m_sources ["urlsource"];
0236     //delete m_bookmark_manager;
0237     delete m_bookmark_owner;
0238 }
0239 
0240 void PartBase::showControls (bool show) {
0241     viewWidget ()->setControlPanelMode (
0242             show ? View::CP_Show : View::CP_Hide);
0243 }
0244 
0245 QString PartBase::getStatus () {
0246     QString rval = "Waiting";
0247     if (source() && source()->document()) {
0248         if (source()->document()->unfinished ())
0249             rval = "Playable";
0250         else if (source()->document()->state >= Node::state_deactivated)
0251             rval = "Complete";
0252     }
0253     return rval;
0254 }
0255 
0256 QString PartBase::doEvaluate (const QString &) {
0257     return "undefined";
0258 }
0259 
0260 void PartBase::settingsChanged () {
0261     if (!m_view)
0262         return;
0263     if (m_settings->showcnfbutton)
0264         m_view->controlPanel()->button (ControlPanel::button_config)->show();
0265     else
0266         m_view->controlPanel()->button (ControlPanel::button_config)->hide();
0267     m_view->controlPanel()->enableRecordButtons (m_settings->showrecordbutton);
0268     if (m_settings->showplaylistbutton)
0269         m_view->controlPanel()->button (ControlPanel::button_playlist)->show();
0270     else
0271         m_view->controlPanel()->button (ControlPanel::button_playlist)->hide();
0272     if (!m_settings->showbroadcastbutton)
0273         m_view->controlPanel ()->broadcastButton ()->hide ();
0274     keepMovieAspect (m_settings->sizeratio);
0275     m_settings->applyColorSetting (true);
0276 }
0277 
0278 KMediaPlayer::View* PartBase::view () {
0279     return m_view;
0280 }
0281 
0282 extern const char * strGeneralGroup;
0283 
0284 QString PartBase::processName (Mrl *mrl) {
0285     if (id_node_grab_document == mrl->id)
0286         return QString ("mplayer"); //FIXME
0287     // determine backend, start with temp_backends
0288     QString p = temp_backends [m_source->name()];
0289     bool remember_backend = p.isEmpty ();
0290     MediaManager::ProcessInfoMap &pinfos = m_media_manager->processInfos ();
0291     if (p.isEmpty ()) {
0292         // next try to find mimetype match from kmplayerrc
0293         if (!mrl->mimetype.isEmpty ()) {
0294             KConfigGroup mime_cfg (m_config, mrl->mimetype);
0295             p = mime_cfg.readEntry ("player", QString ());
0296             remember_backend = !(!p.isEmpty () &&
0297                     pinfos.contains (p) &&
0298                     pinfos [p]->supports (m_source->name ()));
0299         }
0300     }
0301     if (p.isEmpty ())
0302         // try source match from kmplayerrc
0303         p = m_settings->backends [m_source->name()];
0304     if (p.isEmpty ()) {
0305         // try source match from kmplayerrc by re-reading
0306         p = KConfigGroup(m_config, strGeneralGroup).readEntry(m_source->name(),QString());
0307     }
0308     if (p.isEmpty () ||
0309             !pinfos.contains (p) ||
0310             !pinfos [p]->supports (m_source->name ())) {
0311         // finally find first supported player
0312         p.truncate (0);
0313         const MediaManager::ProcessInfoMap::const_iterator e = pinfos.constEnd();
0314         for (MediaManager::ProcessInfoMap::const_iterator i = pinfos.constBegin(); i != e; ++i)
0315             if (i.value ()->supports (m_source->name ())) {
0316                 p = QString (i.value ()->name);
0317                 break;
0318             }
0319     }
0320     if (!p.isEmpty ()) {
0321         updatePlayerMenu (m_view->controlPanel (), p);
0322         if (remember_backend)
0323             m_settings->backends [m_source->name()] = p;
0324         else
0325             temp_backends [m_source->name()] = QString ();
0326     }
0327     return p;
0328 }
0329 
0330 void PartBase::processCreated (Process*) {}
0331 
0332 void PartBase::slotPlayerMenu (QAction* act) {
0333     Mrl *mrl = m_source->current ();
0334     bool playing = mrl && mrl->active ();
0335     const char * srcname = m_source->name ();
0336     QMenu *player_menu = m_view->controlPanel ()->playerMenu;
0337     MediaManager::ProcessInfoMap &pinfos = m_media_manager->processInfos ();
0338     const MediaManager::ProcessInfoMap::const_iterator e = pinfos.constEnd();
0339     int id = 0;
0340     for (MediaManager::ProcessInfoMap::const_iterator i = pinfos.constBegin();
0341             id < player_menu->actions().count() && i != e;
0342             ++i) {
0343         ProcessInfo *pinfo = i.value ();
0344         if (!pinfo->supports (srcname))
0345             continue;
0346         QAction* menu = player_menu->actions().at (id);
0347         menu->setChecked (menu == act);
0348         if (menu == act) {
0349             if (strcmp (pinfo->name, "npp"))
0350                 m_settings->backends [srcname] = pinfo->name;
0351             temp_backends [srcname] = pinfo->name;
0352         }
0353         id++;
0354     }
0355     if (playing)
0356         m_source->play (mrl);
0357 }
0358 
0359 void PartBase::updatePlayerMenu (ControlPanel *panel, const QString &backend) {
0360     if (!m_view)
0361         return;
0362     QMenu *menu = panel->playerMenu;
0363     menu->clear ();
0364     MediaManager::ProcessInfoMap &pinfos = m_media_manager->processInfos ();
0365     const MediaManager::ProcessInfoMap::const_iterator e = pinfos.constEnd();
0366     for (MediaManager::ProcessInfoMap::const_iterator i = pinfos.constBegin(); i != e; ++i) {
0367         ProcessInfo *p = i.value ();
0368         if (p->supports (m_source ? m_source->name () : "urlsource")) {
0369             QAction* act = menu->addAction (p->label);
0370             act->setCheckable(true);
0371             if (backend == p->name)
0372                 act->setChecked (true);
0373         }
0374     }
0375 }
0376 
0377 void PartBase::connectSource (Source * old_source, Source * source) {
0378     if (old_source) {
0379         disconnect (old_source, &Source::endOfPlayItems,
0380                     this, &PartBase::stop);
0381         disconnect (old_source, &Source::dimensionsChanged,
0382                     this, &PartBase::sourceHasChangedAspects);
0383         disconnect (old_source, &Source::startPlaying,
0384                     this, &PartBase::slotPlayingStarted);
0385         disconnect (old_source, &Source::stopPlaying,
0386                     this, &PartBase::slotPlayingStopped);
0387     }
0388     if (source) {
0389         connect (source, &Source::endOfPlayItems,
0390                  this, &PartBase::stop);
0391         connect (source, &Source::dimensionsChanged,
0392                 this, &PartBase::sourceHasChangedAspects);
0393         connect (source, &Source::startPlaying,
0394                 this, &PartBase::slotPlayingStarted);
0395         connect (source, &Source::stopPlaying,
0396                 this, &PartBase::slotPlayingStopped);
0397     }
0398 }
0399 
0400 void PartBase::setSource (Source * _source) {
0401     Source * old_source = m_source;
0402     if (m_source) {
0403         m_source->deactivate ();
0404         stop ();
0405         if (m_view) {
0406             m_view->reset ();
0407             Q_EMIT infoUpdated (QString ());
0408         }
0409         disconnect (this, &PartBase::audioIsSelected,
0410                     m_source, &Source::setAudioLang);
0411         disconnect (this, &PartBase::subtitleIsSelected,
0412                     m_source, &Source::setSubtitle);
0413     }
0414     if (m_view) {
0415         if (m_auto_controls)
0416             m_view->controlPanel ()->setAutoControls (m_auto_controls);
0417         m_view->controlPanel ()->enableRecordButtons (m_settings->showrecordbutton);
0418         if (!m_settings->showcnfbutton)
0419             m_view->controlPanel()->button(ControlPanel::button_config)->hide();
0420         if (!m_settings->showplaylistbutton)
0421           m_view->controlPanel()->button(ControlPanel::button_playlist)->hide();
0422     }
0423     m_source = _source;
0424     connectSource (old_source, m_source);
0425     connect (this, &PartBase::audioIsSelected,
0426              m_source, &Source::setAudioLang);
0427     connect (this, &PartBase::subtitleIsSelected,
0428              m_source, &Source::setSubtitle);
0429     m_source->init ();
0430     m_source->setIdentified (false);
0431     if (m_view)
0432         updatePlayerMenu (m_view->controlPanel ());
0433     if (m_source && !m_source->avoidRedirects ())
0434         QTimer::singleShot (0, m_source, &Source::slotActivate);
0435     updateTree (true, true);
0436     Q_EMIT sourceChanged (old_source, m_source);
0437 }
0438 
0439 void PartBase::changeURL (const QString & url) {
0440     Q_EMIT urlChanged (url);
0441 }
0442 
0443 bool PartBase::isSeekable (void) const {
0444     return m_source ? m_source->isSeekable () : false;
0445 }
0446 
0447 bool PartBase::hasLength () const {
0448     return m_source ? m_source->hasLength () : false;
0449 }
0450 
0451 qlonglong PartBase::length () const {
0452     return m_source ? m_source->length () : 0;
0453 }
0454 
0455 bool PartBase::openUrl (const QUrl &url) {
0456     qCDebug(LOG_KMPLAYER_COMMON) << "PartBase::openUrl " << url.url() << url.isValid ();
0457     if (!m_view) return false;
0458     stop ();
0459     Source * src = (url.isEmpty () ? m_sources ["urlsource"] : (!url.scheme().compare ("kmplayer") && m_sources.contains (url.host ()) ? m_sources [url.host ()] : m_sources ["urlsource"]));
0460     setSource (src);
0461     src->setSubURL (QUrl ());
0462     src->setUrl (url.isLocalFile () ? url.toLocalFile() : url.url ());
0463     if (src->avoidRedirects ())
0464         src->activate ();
0465     return true;
0466 }
0467 
0468 bool PartBase::openUrl(const QList<QUrl>& urls) {
0469     if (urls.size () == 1) {
0470         openUrl(urls[0]);
0471     } else {
0472         openUrl (QUrl ());
0473         NodePtr d = m_source->document ();
0474         if (d)
0475             for (int i = 0; i < urls.size (); i++) {
0476                 const QUrl &url = urls [i];
0477                 d->appendChild (new GenericURL (d,
0478                             url.isLocalFile() ? url.toLocalFile() : url.toString()));
0479             }
0480     }
0481     return true;
0482 }
0483 
0484 void PartBase::openUrl (const QUrl &u, const QString &t, const QString &srv)
0485 {
0486     qCDebug(LOG_KMPLAYER_COMMON) << u << " " << t << " " << srv;
0487     QDBusMessage msg = QDBusMessage::createMethodCall (
0488             "org.kde.klauncher", "/KLauncher",
0489             "org.kde.KLauncher", "start_service_by_desktop_name");
0490     QStringList urls;
0491     urls << u.url ();
0492     msg << "kfmclient" << urls << QStringList () << QString () << true;
0493     msg.setDelayedReply (false);
0494     QDBusConnection::sessionBus().send (msg);
0495 }
0496 
0497 bool PartBase::closeUrl () {
0498     stop ();
0499     if (m_view)
0500         m_view->reset ();
0501     return true;
0502 }
0503 
0504 bool PartBase::openFile () {
0505     return false;
0506 }
0507 
0508 void PartBase::keepMovieAspect (bool b) {
0509     if (m_view)
0510         m_view->setKeepSizeRatio (b);
0511 }
0512 
0513 void PartBase::timerEvent (QTimerEvent * e) {
0514     if (e->timerId () == m_update_tree_timer) {
0515         m_update_tree_timer = 0;
0516         updateTree (m_update_tree_full, true);
0517     } else if (e->timerId () == m_rec_timer) {
0518         m_rec_timer = 0;
0519         if (m_record_doc)
0520             openUrl(QUrl::fromUserInput(convertNode <RecordDocument> (m_record_doc)->record_file));
0521     }
0522     killTimer (e->timerId ());
0523 }
0524 
0525 void PartBase::playingStarted () {
0526     qCDebug(LOG_KMPLAYER_COMMON) << "playingStarted " << this;
0527     if (m_view) {
0528         m_view->controlPanel ()->setPlaying (true);
0529         m_view->controlPanel ()->showPositionSlider (!!m_source->length ());
0530         m_view->controlPanel ()->enableSeekButtons (m_source->isSeekable ());
0531         m_view->playingStart ();
0532         //if (m_settings->autoadjustvolume && m_process)
0533         //   m_process->volume(m_view->controlPanel()->volumeBar()->value(),true);
0534     }
0535     Q_EMIT loading (100);
0536 }
0537 
0538 void PartBase::slotPlayingStarted () {
0539     playingStarted ();
0540 }
0541 
0542 void PartBase::playingStopped () {
0543     qCDebug(LOG_KMPLAYER_COMMON) << "playingStopped " << this;
0544     if (m_view) {
0545         m_view->controlPanel ()->setPlaying (false);
0546         m_view->playingStop ();
0547         m_view->reset ();
0548     }
0549     m_bPosSliderPressed = false;
0550 }
0551 
0552 void PartBase::slotPlayingStopped () {
0553     playingStarted ();
0554 }
0555 
0556 void PartBase::setPosition (int position, int length) {
0557     if (m_view && !m_bPosSliderPressed) {
0558         if (m_media_manager->processes ().size () > 1)
0559             Q_EMIT positioned (0, 0);
0560         else
0561             Q_EMIT positioned (position, length);
0562     }
0563 }
0564 
0565 void PartBase::setLoaded (int percentage) {
0566     Q_EMIT loading (percentage);
0567 }
0568 
0569 qlonglong PartBase::position () const {
0570     return m_source ? 100 * m_source->position () : 0;
0571 }
0572 
0573 void PartBase::pause () {
0574     NodePtr doc = m_source ? m_source->document () : nullptr;
0575     if (doc) {
0576         Mrl *mrl = nullptr;
0577         NodePtrW cur = m_source->current ();
0578         if (cur) {
0579             mrl = cur->mrl ();
0580             if (mrl && Mrl::WindowMode == mrl->view_mode)
0581                 mrl = nullptr;
0582         }
0583         if (doc->state == Node::state_deferred) {
0584             doc->undefer ();
0585             if (cur && mrl && mrl->state == Node::state_deferred)
0586                 mrl->undefer ();
0587         } else {
0588             doc->defer ();
0589             if (cur && mrl && mrl->unfinished ())
0590                 mrl->defer ();
0591         }
0592     }
0593 }
0594 
0595 void PartBase::back () {
0596     m_source->backward ();
0597 }
0598 
0599 void PartBase::forward () {
0600     m_source->forward ();
0601 }
0602 
0603 void PartBase::playListItemClicked (const QModelIndex& index) {
0604     if (!index.isValid ())
0605         return;
0606     PlayListView *pv = qobject_cast <PlayListView *> (sender ());
0607     if (pv->model ()->rowCount ()) {
0608         if (pv->isExpanded (index))
0609             pv->setExpanded (index, false);
0610         else
0611             pv->setExpanded (index, true);
0612     }
0613 }
0614 
0615 void PartBase::playListItemActivated(const QModelIndex &index) {
0616     if (m_in_update_tree) return;
0617     if (m_view->editMode ()) return;
0618     PlayListView *pv = qobject_cast <PlayListView *> (sender ());
0619     if (!pv->model ()->parent (index).isValid () && index.row ())
0620         return; // handled by playListItemClicked
0621     PlayItem *vi = static_cast <PlayItem *> (index.internalPointer ());
0622     TopPlayItem *ri = vi->rootItem ();
0623     if (vi->node) {
0624         QString src = ri->source;
0625         NodePtrW node = vi->node;
0626         //qCDebug(LOG_KMPLAYER_COMMON) << src << " " << vi->node->nodeName();
0627         Source * source = src.isEmpty() ? m_source : m_sources[src.toLatin1().constData()];
0628         if (node->isPlayable () || id_node_playlist_item == node->id) {
0629             source->play (node->mrl ()); //may become !isPlayable by lazy loading
0630             if (node && !node->isPlayable ())
0631                 Q_EMIT treeChanged (ri->id, node, nullptr, false, true);
0632         } // else if (vi->childCount ()) {handled by playListItemClicked
0633     } else if (vi->attribute) {
0634         if (vi->attribute->name () == Ids::attr_src ||
0635                 vi->attribute->name () == Ids::attr_href ||
0636                 vi->attribute->name () == Ids::attr_url ||
0637                 vi->attribute->name () == Ids::attr_value ||
0638                 vi->attribute->name () == "data") {
0639             QString src (vi->attribute->value ());
0640             if (!src.isEmpty ()) {
0641                 PlayItem *pi = vi->parent ();
0642                 if (pi) {
0643                     for (Node *e = pi->node.ptr (); e; e = e->parentNode ()) {
0644                         Mrl * mrl = e->mrl ();
0645                         if (mrl)
0646                             src = QUrl (mrl->absolutePath ()).resolved(QUrl(src)).url ();
0647                     }
0648                     const QUrl url = QUrl::fromUserInput(src);
0649                     if (url.isValid ())
0650                         openUrl (url);
0651                 }
0652             }
0653         }
0654     } else
0655         Q_EMIT treeChanged (ri->id, ri->node, nullptr, false, false);
0656     if (m_view)
0657         m_view->viewArea ()->setFocus ();
0658 }
0659 
0660 void PartBase::updateTree (bool full, bool force) {
0661     if (force) {
0662         m_in_update_tree = true;
0663         if (m_update_tree_full) {
0664             if (m_source)
0665                 Q_EMIT treeChanged (0, m_source->root (), m_source->current (), true, false);
0666         }
0667         m_in_update_tree = false;
0668         if (m_update_tree_timer) {
0669             killTimer (m_update_tree_timer);
0670             m_update_tree_timer = 0;
0671         }
0672     } else if (!m_update_tree_timer) {
0673         m_update_tree_timer = startTimer (100);
0674         m_update_tree_full = full;
0675     } else
0676         m_update_tree_full |= full;
0677 }
0678 
0679 void PartBase::updateInfo (const QString & msg) {
0680     Q_EMIT infoUpdated (msg);
0681 }
0682 
0683 void PartBase::updateStatus (const QString & msg) {
0684     Q_EMIT statusUpdated (msg);
0685 }
0686 
0687 void PartBase::setLanguages (const QStringList & al, const QStringList & sl) {
0688     Q_EMIT languagesUpdated (al, sl);
0689 }
0690 
0691 void PartBase::audioSelected (QAction* act) {
0692     Q_EMIT panelActionToggled(act);
0693     int i = act->parentWidget()->actions().indexOf(act);
0694     if (i >= 0)
0695         Q_EMIT audioIsSelected (i);
0696 }
0697 
0698 void PartBase::subtitleSelected (QAction* act) {
0699     Q_EMIT panelActionToggled(act);
0700     int i = act->parentWidget()->actions().indexOf(act);
0701     if (i >= 0)
0702         Q_EMIT subtitleIsSelected (i);
0703 }
0704 
0705 void PartBase::recorderPlaying () {
0706     stop ();
0707     m_view->controlPanel ()->setRecording (true);
0708     Q_EMIT recording (true);
0709 }
0710 
0711 void PartBase::recorderStopped () {
0712     stopRecording ();
0713     if (m_view && m_rec_timer < 0 && m_record_doc)
0714         openUrl(QUrl::fromUserInput(convertNode <RecordDocument> (m_record_doc)->record_file));
0715 }
0716 
0717 void PartBase::stopRecording () {
0718     if (m_view) {
0719         m_view->controlPanel ()->setRecording (false);
0720         Q_EMIT recording (false);
0721         if (m_record_doc && m_record_doc->active ()) {
0722             m_record_doc->deactivate ();
0723             if (m_rec_timer > 0)
0724                 killTimer (m_rec_timer);
0725             m_rec_timer = 0;
0726         }
0727     }
0728 }
0729 
0730 bool PartBase::isRecording ()
0731 {
0732     return m_record_doc && m_record_doc->active ();
0733 }
0734 
0735 void PartBase::record () {
0736     if (m_view) m_view->setCursor (QCursor (Qt::WaitCursor));
0737     if (!m_view->controlPanel()->button(ControlPanel::button_record)->isChecked ()) {
0738         stopRecording ();
0739     } else {
0740         m_settings->show  ("RecordPage");
0741         m_view->controlPanel ()->setRecording (false);
0742     }
0743     if (m_view) m_view->setCursor (QCursor (Qt::ArrowCursor));
0744 }
0745 
0746 void PartBase::record (const QString &src, const QString &f, const QString &rec, int auto_start)
0747 {
0748     if (m_record_doc) {
0749         if (m_record_doc->active ())
0750             m_record_doc->reset ();
0751         m_record_doc->document ()->dispose ();
0752     }
0753     m_record_doc = new RecordDocument (src, f, rec, source ());
0754     m_record_doc->activate ();
0755     if (auto_start > 0)
0756         m_rec_timer = startTimer (auto_start);
0757     else
0758         m_rec_timer = auto_start;
0759 }
0760 
0761 void PartBase::play () {
0762     if (!m_view)
0763         return;
0764     QPushButton *pb = ::qobject_cast <QPushButton *> (sender ());
0765     if (pb && !pb->isChecked ()) {
0766         stop ();
0767         return;
0768     }
0769     if (m_update_tree_timer) {
0770         killTimer (m_update_tree_timer);
0771         m_update_tree_timer = 0;
0772     }
0773     if (!playing ()) {
0774         PlayItem *lvi = m_view->playList ()->selectedItem ();
0775         if (lvi) {
0776             TopPlayItem *ri = lvi->rootItem ();
0777             if (ri->id != 0) // make sure it's in the first tree
0778                 lvi = nullptr;
0779         }
0780         if (!lvi) {
0781             QModelIndex index = m_view->playList ()->model ()->index (0, 0);
0782             lvi = static_cast<PlayItem*>(index.internalPointer ());
0783             if (!lvi->node)
0784                 lvi = nullptr;
0785         }
0786         if (lvi) {
0787             Mrl *mrl = nullptr;
0788             for (Node * n = lvi->node.ptr (); n; n = n->parentNode ()) {
0789                 if (n->isPlayable ()) {
0790                     mrl = n->mrl ();
0791                     break;
0792                 }
0793                 if (!mrl && n->mrl () && !n->mrl ()->src.isEmpty ())
0794                     mrl = n->mrl ();
0795             }
0796             if (mrl)
0797                 m_source->play (mrl);
0798         }
0799     } else {
0800         m_source->play (nullptr);
0801     }
0802 }
0803 
0804 bool PartBase::playing () const {
0805     return m_source && m_source->document ()->active ();
0806 }
0807 
0808 void PartBase::stop () {
0809     QPushButton * b = m_view ? m_view->controlPanel ()->button (ControlPanel::button_stop) : nullptr;
0810     if (b) {
0811         if (!b->isChecked ())
0812             b->toggle ();
0813         m_view->setCursor (QCursor (Qt::WaitCursor));
0814     }
0815     if (m_source)
0816         m_source->reset ();
0817     MediaManager::ProcessInfoMap &pi = m_media_manager->processInfos ();
0818     const MediaManager::ProcessInfoMap::const_iterator ie = pi.constEnd();
0819     for (MediaManager::ProcessInfoMap::const_iterator i = pi.constBegin(); i != ie; ++i)
0820         i.value ()->quitProcesses ();
0821     MediaManager::ProcessList &processes = m_media_manager->processes ();
0822     const MediaManager::ProcessList::const_iterator e = processes.constEnd();
0823     for (MediaManager::ProcessList::const_iterator i = processes.constBegin(); i != e; ++i)
0824         (*i)->quit ();
0825     if (m_view) {
0826         m_view->setCursor (QCursor (Qt::ArrowCursor));
0827         if (b->isChecked ())
0828             b->toggle ();
0829         m_view->controlPanel ()->setPlaying (false);
0830         setLoaded (100);
0831         updateStatus (i18n ("Ready"));
0832     }
0833     playingStopped ();
0834 }
0835 
0836 void PartBase::seek (qlonglong msec) {
0837     if (m_media_manager->processes ().size () == 1)
0838         m_media_manager->processes ().first ()->seek (msec/100, true);
0839 }
0840 
0841 void PartBase::adjustVolume (int incdec) {
0842     if (m_media_manager->processes ().size () > 0)
0843         m_media_manager->processes ().first ()->volume (incdec, false);
0844 }
0845 
0846 void PartBase::increaseVolume () {
0847     /*if (m_view)
0848         m_view->controlPanel ()->volumeBar ()->setValue (m_view->controlPanel ()->volumeBar ()->value () + 2);*/
0849 }
0850 
0851 void PartBase::decreaseVolume () {
0852     //if (m_view)
0853     //    m_view->controlPanel ()->volumeBar ()->setValue (m_view->controlPanel ()->volumeBar ()->value () - 2);
0854 }
0855 
0856 void PartBase::posSliderPressed () {
0857     m_bPosSliderPressed=true;
0858 }
0859 
0860 void PartBase::posSliderReleased () {
0861     m_bPosSliderPressed=false;
0862     const QSlider * posSlider = ::qobject_cast<const QSlider *> (sender ());
0863     if (m_media_manager->processes ().size () == 1)
0864         m_media_manager->processes ().first ()->seek (posSlider->value(), true);
0865 }
0866 
0867 void PartBase::volumeChanged (int val) {
0868     if (m_media_manager->processes ().size () > 0) {
0869         m_settings->volume = val;
0870         m_media_manager->processes ().first ()->volume (val, true);
0871     }
0872 }
0873 
0874 void PartBase::contrastValueChanged (int val) {
0875     if (m_media_manager->processes ().size () > 0)
0876         m_media_manager->processes ().first ()->contrast (val, true);
0877 }
0878 
0879 void PartBase::brightnessValueChanged (int val) {
0880     if (m_media_manager->processes ().size () > 0)
0881         m_media_manager->processes ().first ()->brightness (val, true);
0882 }
0883 
0884 void PartBase::hueValueChanged (int val) {
0885     if (m_media_manager->processes ().size () > 0)
0886         m_media_manager->processes ().first ()->hue (val, true);
0887 }
0888 
0889 void PartBase::saturationValueChanged (int val) {
0890     m_settings->saturation = val;
0891     if (m_media_manager->processes ().size () > 0)
0892         m_media_manager->processes ().first ()->saturation (val, true);
0893 }
0894 
0895 void PartBase::sourceHasChangedAspects () {
0896     Q_EMIT sourceDimensionChanged ();
0897 }
0898 
0899 void PartBase::positionValueChanged (int pos) {
0900     QSlider * slider = ::qobject_cast <QSlider *> (sender ());
0901     if (m_media_manager->processes ().size () == 1 &&
0902             slider && slider->isEnabled ())
0903         m_media_manager->processes ().first ()->seek (pos, true);
0904 }
0905 
0906 void PartBase::fullScreen () {
0907     if (m_view)
0908         m_view->fullScreen ();
0909 }
0910 
0911 void PartBase::toggleFullScreen () {
0912     m_view->fullScreen ();
0913 }
0914 
0915 bool PartBase::isPlaying () {
0916     return playing ();
0917 }
0918 
0919 KAboutData* PartBase::createAboutData () {
0920     KMessageBox::error(nullptr, "createAboutData", "KMPlayer");
0921     return nullptr;
0922 }
0923 
0924 //-----------------------------------------------------------------------------
0925 
0926 SourceDocument::SourceDocument (Source *s, const QString &url)
0927     : Document (url, s), m_source (s) {}
0928 
0929 void SourceDocument::message (MessageType msg, void *data) {
0930     switch (msg) {
0931 
0932     case MsgInfoString: {
0933         QString info (data ? *((QString *) data) : QString ());
0934         m_source->player ()->updateInfo (info);
0935         return;
0936     }
0937 
0938     case MsgAccessKey:
0939         for (Connection *c = m_KeyListeners.first(); c; c = m_KeyListeners.next ())
0940             if (c->payload && c->connecter) {
0941                 KeyLoad *load = (KeyLoad *) c->payload;
0942                 if (load->key == (int) (long) data)
0943                     post (c->connecter, new Posting (this, MsgAccessKey));
0944             }
0945         return;
0946 
0947     default:
0948         break;
0949     }
0950     Document::message (msg, data);
0951 }
0952 
0953 void *SourceDocument::role (RoleType msg, void *data) {
0954     switch (msg) {
0955 
0956     case RoleMediaManager:
0957         return m_source->player ()->mediaManager ();
0958 
0959     case RoleChildDisplay: {
0960         PartBase *p = m_source->player ();
0961         if (p->view ())
0962             return p->viewWidget ()->viewArea ()->getSurface ((Mrl *) data);
0963         return nullptr;
0964     }
0965 
0966     case RoleReceivers:
0967 
0968         switch ((MessageType) (long) data) {
0969 
0970         case MsgAccessKey:
0971             return &m_KeyListeners;
0972 
0973         case MsgSurfaceUpdate: {
0974             PartBase *p = m_source->player ();
0975             if (p->view ())
0976                 return p->viewWidget ()->viewArea ()->updaters ();
0977         }
0978         // fall through
0979 
0980         default:
0981             break;
0982         }
0983         // fall through
0984 
0985     default:
0986         break;
0987     }
0988     return Document::role (msg, data);
0989 }
0990 
0991 
0992 Source::Source (const QString&, PartBase * player, const char * n)
0993  : QObject (player),
0994    m_name (n), m_player (player),
0995    m_identified (false), m_auto_play (true), m_avoid_redirects (false),
0996    m_frequency (0), m_xvport (0), m_xvencoding (-1), m_doc_timer (0) {
0997     init ();
0998 }
0999 
1000 Source::~Source () {
1001     if (m_document)
1002         m_document->document ()->dispose ();
1003     m_document = nullptr;
1004 }
1005 
1006 void Source::init () {
1007     //setDimensions (320, 240);
1008     m_width = 0;
1009     m_height = 0;
1010     m_aspect = 0.0;
1011     m_length = 0;
1012     m_audio_id = -1;
1013     m_subtitle_id = -1;
1014     m_position = 0;
1015     setLength (m_document, 0);
1016     m_recordcmd.truncate (0);
1017 }
1018 
1019 void Source::setLanguages (LangInfoPtr audio, LangInfoPtr sub)
1020 {
1021     m_audio_infos = audio;
1022     m_subtitle_infos = sub;
1023 
1024     QStringList alst;
1025     QStringList slst;
1026     for (LangInfoPtr li = audio; li; li = li->next)
1027         alst.push_back (li->name);
1028     for (LangInfoPtr li = sub; li; li = li->next)
1029         slst.push_back (li->name);
1030 
1031     m_player->setLanguages (alst, slst);
1032 }
1033 
1034 void Source::setDimensions (NodePtr node, int w, int h) {
1035     Mrl *mrl = node ? node->mrl () : nullptr;
1036     if (mrl) {
1037         float a = h > 0 ? 1.0 * w / h : 0.0;
1038         mrl->size = SSize (w, h);
1039         mrl->aspect = a;
1040         bool ev = (w > 0 && h > 0) ||
1041             (h == 0 && m_height > 0) ||
1042             (w == 0 && m_width > 0);
1043         if (Mrl::SingleMode == mrl->view_mode) {
1044             m_width = w;
1045             m_height = h;
1046         }
1047         if (Mrl::WindowMode == mrl->view_mode || m_aspect < 0.001)
1048             setAspect (node, h > 0 ? 1.0 * w / h : 0.0);
1049             //qCDebug(LOG_KMPLAYER_COMMON) << "setDimensions " << w << "x" << h << " a:" << m_aspect;
1050         else if (ev)
1051             Q_EMIT dimensionsChanged ();
1052     }
1053 }
1054 
1055 void Source::setAspect (NodePtr node, float a) {
1056     //qCDebug(LOG_KMPLAYER_COMMON) << "setAspect " << a;
1057     Mrl *mrl = node ? node->mrl () : nullptr;
1058     bool changed = false;
1059     if (mrl &&
1060             mrl->media_info &&
1061             mrl->media_info->media &&
1062             MediaManager::AudioVideo == mrl->media_info->type) {
1063         static_cast <AudioVideoMedia*>(mrl->media_info->media)->viewer ()->setAspect(a);
1064         if (mrl->view_mode == Mrl::WindowMode)
1065             changed |= (fabs (mrl->aspect - a) > 0.001);
1066         mrl->aspect = a;
1067     }
1068     if (!mrl || mrl->view_mode == Mrl::SingleMode) {
1069         changed |= (fabs (m_aspect - a) > 0.001);
1070         m_aspect = a;
1071         if (changed && m_player->view ())
1072             m_player->viewWidget ()->viewArea ()->resizeEvent (nullptr);
1073 
1074     } else {
1075        mrl->message (MsgSurfaceBoundsUpdate);
1076     }
1077     if (changed)
1078         Q_EMIT dimensionsChanged ();
1079 }
1080 
1081 void Source::setLength (NodePtr, int len) {
1082     m_length = len;
1083     m_player->setPosition (m_position, m_length);
1084 }
1085 
1086 void Source::setPosition (int pos) {
1087     m_position = pos;
1088     m_player->setPosition (pos, m_length);
1089 }
1090 
1091 void Source::setLoading (int percentage) {
1092     m_player->setLoaded (percentage);
1093 }
1094 
1095 /*
1096 static void printTree (NodePtr root, QString off=QString()) {
1097     if (!root) {
1098         qCDebug(LOG_KMPLAYER_COMMON) << off << "[null]";
1099         return;
1100     }
1101     qCDebug(LOG_KMPLAYER_COMMON) << off << root->nodeName() << " " << (Element*)root << (root->isPlayable() ? root->mrl ()->src : QString ("-"));
1102     off += QString ("  ");
1103     for (NodePtr e = root->firstChild(); e; e = e->nextSibling())
1104         printTree(e, off);
1105 }*/
1106 
1107 void Source::setUrl (const QString &url) {
1108     qCDebug(LOG_KMPLAYER_COMMON) << url;
1109     m_url = QUrl::fromUserInput(url);
1110     if (m_document && !m_document->hasChildNodes () &&
1111             (m_document->mrl()->src.isEmpty () ||
1112              m_document->mrl()->src == url))
1113         // special case, mime is set first by plugin FIXME v
1114         m_document->mrl()->src = url;
1115     else {
1116         if (m_document)
1117             m_document->document ()->dispose ();
1118         m_document = new SourceDocument (this, url);
1119     }
1120     if (m_player->source () == this)
1121         m_player->updateTree ();
1122 
1123     QTimer::singleShot (0, this, &Source::changedUrl);
1124 }
1125 
1126 void Source::changedUrl()
1127 {
1128     Q_EMIT titleChanged (this->prettyName ());
1129 }
1130 
1131 void Source::setTitle (const QString & title) {
1132     Q_EMIT titleChanged (title);
1133 }
1134 
1135 void Source::setAudioLang (int id) {
1136     LangInfoPtr li = m_audio_infos;
1137     for (; id > 0 && li; li = li->next)
1138         id--;
1139     m_audio_id = li ? li->id : -1;
1140     if (m_player->view () && m_player->mediaManager ()->processes ().size ())
1141         m_player->mediaManager ()->processes ().first ()->setAudioLang (m_audio_id);
1142 }
1143 
1144 void Source::setSubtitle (int id) {
1145     LangInfoPtr li = m_subtitle_infos;
1146     for (; id > 0 && li; li = li->next)
1147         id--;
1148     m_subtitle_id = li ? li->id : -1;
1149     if (m_player->view () && m_player->mediaManager ()->processes ().size ())
1150         m_player->mediaManager ()->processes ().first ()->setSubtitle (m_subtitle_id);
1151 }
1152 
1153 void Source::reset () {
1154     if (m_document) {
1155         qCDebug(LOG_KMPLAYER_COMMON) << "Source::reset " << name () << endl;
1156         NodePtr doc = m_document; // avoid recursive calls
1157         m_document = nullptr;
1158         doc->reset ();
1159         m_document = doc;
1160         m_player->updateTree ();
1161     }
1162     init ();
1163 }
1164 
1165 void Source::play (Mrl *mrl) {
1166     if (!mrl)
1167         mrl = document ()->mrl ();
1168     NodePtrW guarded = mrl;
1169     blockSignals (true); //endOfPlayItems, but what is hyperspace?
1170     document ()->reset ();
1171     blockSignals (false);
1172     mrl = guarded ? guarded->mrl () : m_document->mrl ();
1173     if (!mrl)
1174         return;
1175     m_width = m_height = 0;
1176     m_player->changeURL (mrl->src);
1177     for (Node *p = mrl->parentNode(); p; p = p->parentNode())
1178         p->state = Element::state_activated;
1179     mrl->activate ();
1180     m_width = mrl->size.width;
1181     m_height = mrl->size.height;
1182     m_aspect = mrl->aspect;
1183     //qCDebug(LOG_KMPLAYER_COMMON) << "Source::playCurrent " << (m_current ? m_current->nodeName():" doc act:") <<  (m_document && !m_document->active ()) << " cur:" << (!m_current)  << " cur act:" << (m_current && !m_current->active ());
1184     m_player->updateTree ();
1185     Q_EMIT dimensionsChanged ();
1186 }
1187 
1188 bool Source::authoriseUrl (const QString &) {
1189     return true;
1190 }
1191 
1192 void Source::setTimeout (int ms) {
1193     //qCDebug(LOG_KMPLAYER_COMMON) << "Source::setTimeout " << ms;
1194     if (m_doc_timer)
1195         killTimer (m_doc_timer);
1196     m_doc_timer = ms > -1 ? startTimer (ms) : 0;
1197 }
1198 
1199 void Source::timerEvent (QTimerEvent * e) {
1200     if (e->timerId () == m_doc_timer && m_document && m_document->active ())
1201         m_document->document ()->timer (); // will call setTimeout()
1202     else
1203         killTimer (e->timerId ());
1204 }
1205 
1206 void Source::setCurrent (Mrl *mrl) {
1207     m_current = mrl;
1208     m_width = mrl->size.width;
1209     m_height = mrl->size.height;
1210     m_aspect = mrl->aspect;
1211 }
1212 
1213 void Source::stateElementChanged (Node *elm, Node::State os, Node::State ns) {
1214     //qCDebug(LOG_KMPLAYER_COMMON) << "Source::stateElementChanged " << elm->nodeName () << " state:" << (int) elm->state << " cur isPlayable:" << (m_current && m_current->isPlayable ()) << " elm==linkNode:" << (m_current && elm == m_current->mrl ()->linkNode ()) << endl;
1215     if (ns == Node::state_activated &&
1216             elm->mrl ()) {
1217         if (Mrl::WindowMode != elm->mrl ()->view_mode &&
1218                 (!elm->parentNode () ||
1219                  !elm->parentNode ()->mrl () ||
1220                  Mrl::WindowMode != elm->parentNode ()->mrl ()->view_mode))
1221             setCurrent (elm->mrl ());
1222         if (m_current.ptr () == elm)
1223             Q_EMIT startPlaying ();
1224     } else if (ns == Node::state_deactivated) {
1225         if (elm == m_document) {
1226             NodePtrW guard = elm;
1227             Q_EMIT endOfPlayItems (); // played all items FIXME on jumps
1228             if (!guard)
1229                 return;
1230         } else if (m_current.ptr () == elm) {
1231             Q_EMIT stopPlaying ();
1232         }
1233     }
1234     if (elm->role (RolePlaylist)) {
1235         if (ns == Node::state_activated || ns == Node::state_deactivated)
1236             m_player->updateTree ();
1237         else if (ns == Node::state_began || os == Node::state_began)
1238             m_player->updateTree (false);
1239     }
1240 }
1241 
1242 void Source::bitRates (int & preferred, int & maximal) {
1243     preferred = 1024 * m_player->settings ()->prefbitrate;
1244     maximal= 1024 * m_player->settings ()->maxbitrate;
1245 }
1246 
1247 void Source::openUrl (const QUrl &url, const QString &t, const QString &srv) {
1248     m_player->openUrl (url, t, srv);
1249 }
1250 
1251 void Source::enableRepaintUpdaters (bool enable, unsigned int off_time) {
1252     if (m_player->view ())
1253         m_player->viewWidget ()->viewArea()->enableUpdaters (enable, off_time);
1254 }
1255 
1256 void Source::insertURL (NodePtr node, const QString & mrl, const QString & title) {
1257     if (!node || !node->mrl ()) // this should always be false
1258         return;
1259     QString cur_url = node->mrl ()->absolutePath ();
1260     const QUrl url = QUrl(cur_url).resolved(QUrl(mrl));
1261     QString urlstr = QUrl::fromPercentEncoding (url.url ().toUtf8 ());
1262     qCDebug(LOG_KMPLAYER_COMMON) << cur_url << " " << urlstr;
1263     if (!url.isValid ())
1264         qCCritical(LOG_KMPLAYER_COMMON) << "try to append non-valid url" << endl;
1265     else if (QUrl::fromPercentEncoding (cur_url.toUtf8 ()) == urlstr)
1266         qCCritical(LOG_KMPLAYER_COMMON) << "try to append url to itself" << endl;
1267     else {
1268         int depth = 0; // cache this?
1269         for (Node *e = node; e->parentNode (); e = e->parentNode ())
1270             ++depth;
1271         if (depth < 40) {
1272             node->appendChild (new GenericURL (m_document, urlstr, title.isEmpty() ? QUrl::fromPercentEncoding (mrl.toUtf8 ()) : title));
1273             m_player->updateTree ();
1274         } else
1275             qCCritical(LOG_KMPLAYER_COMMON) << "insertURL exceeds depth limit" << endl;
1276     }
1277 }
1278 
1279 void Source::backward () {
1280     Node *back = m_current ? m_current.ptr () : m_document.ptr ();
1281     while (back && back != m_document.ptr ()) {
1282         if (back->previousSibling ()) {
1283             back = back->previousSibling ();
1284             while (!back->isPlayable () && back->lastChild ())
1285                 back = back->lastChild ();
1286             if (back->isPlayable () && !back->active ()) {
1287                 play (back->mrl ());
1288                 break;
1289             }
1290         } else {
1291             back = back->parentNode();
1292         }
1293     }
1294 }
1295 
1296 void Source::forward () {
1297     if (m_current)
1298         m_current->finish ();
1299     if (m_document && !m_document->active ())
1300         play (m_document->mrl ());
1301 }
1302 
1303 void Source::setDocument (KMPlayer::NodePtr doc, KMPlayer::NodePtr cur) {
1304     if (m_document)
1305         m_document->document()->dispose ();
1306     m_document = doc;
1307     setCurrent (cur->mrl ());
1308     //qCDebug(LOG_KMPLAYER_COMMON) << "setDocument: " << m_document->outerXML ();
1309 }
1310 
1311 NodePtr Source::document () {
1312     if (!m_document)
1313         m_document = new SourceDocument (this, QString ());
1314     return m_document;
1315 }
1316 
1317 NodePtr Source::root () {
1318     return document ();
1319 }
1320 
1321 bool Source::processOutput (const QString &) {
1322     return false;
1323 }
1324 
1325 QString Source::filterOptions () {
1326     Settings* m_settings = m_player->settings ();
1327     QString PPargs ("");
1328     if (m_settings->postprocessing)
1329     {
1330         if (m_settings->pp_default)
1331             PPargs = "-vf pp=de";
1332         else if (m_settings->pp_fast)
1333             PPargs = "-vf pp=fa";
1334         else if (m_settings->pp_custom) {
1335             PPargs = "-vf pp=";
1336             if (m_settings->pp_custom_hz) {
1337                 PPargs += "hb";
1338                 if (m_settings->pp_custom_hz_aq && \
1339                         m_settings->pp_custom_hz_ch)
1340                     PPargs += ":ac";
1341                 else if (m_settings->pp_custom_hz_aq)
1342                     PPargs += ":a";
1343                 else if (m_settings->pp_custom_hz_ch)
1344                     PPargs += ":c";
1345                 PPargs += '/';
1346             }
1347             if (m_settings->pp_custom_vt) {
1348                 PPargs += "vb";
1349                 if (m_settings->pp_custom_vt_aq && \
1350                         m_settings->pp_custom_vt_ch)
1351                     PPargs += ":ac";
1352                 else if (m_settings->pp_custom_vt_aq)
1353                     PPargs += ":a";
1354                 else if (m_settings->pp_custom_vt_ch)
1355                     PPargs += ":c";
1356                 PPargs += '/';
1357             }
1358             if (m_settings->pp_custom_dr) {
1359                 PPargs += "dr";
1360                 if (m_settings->pp_custom_dr_aq && \
1361                         m_settings->pp_custom_dr_ch)
1362                     PPargs += ":ac";
1363                 else if (m_settings->pp_custom_dr_aq)
1364                     PPargs += ":a";
1365                 else if (m_settings->pp_custom_dr_ch)
1366                     PPargs += ":c";
1367                 PPargs += '/';
1368             }
1369             if (m_settings->pp_custom_al) {
1370                 PPargs += "al";
1371                 if (m_settings->pp_custom_al_f)
1372                     PPargs += ":f";
1373                 PPargs += '/';
1374             }
1375             if (m_settings->pp_custom_tn) {
1376                 PPargs += "tn";
1377                 /*if (1 <= m_settings->pp_custom_tn_s <= 3){
1378                     PPargs += ":";
1379                     PPargs += m_settings->pp_custom_tn_s;
1380                     }*/ //disabled 'cos this is wrong
1381                 PPargs += '/';
1382             }
1383             if (m_settings->pp_lin_blend_int) {
1384                 PPargs += "lb";
1385                 PPargs += '/';
1386             }
1387             if (m_settings->pp_lin_int) {
1388                 PPargs += "li";
1389                 PPargs += '/';
1390             }
1391             if (m_settings->pp_cub_int) {
1392                 PPargs += "ci";
1393                 PPargs += '/';
1394             }
1395             if (m_settings->pp_med_int) {
1396                 PPargs += "md";
1397                 PPargs += '/';
1398             }
1399             if (m_settings->pp_ffmpeg_int) {
1400                 PPargs += "fd";
1401                 PPargs += '/';
1402             }
1403         }
1404         if (PPargs.endsWith("/"))
1405             PPargs.truncate(PPargs.size ()-1);
1406     }
1407     return PPargs;
1408 }
1409 
1410 bool Source::hasLength () {
1411     return true;
1412 }
1413 
1414 bool Source::isSeekable () {
1415     return true;
1416 }
1417 
1418 void Source::setIdentified (bool b) {
1419     //qCDebug(LOG_KMPLAYER_COMMON) << "Source::setIdentified " << m_identified << b;
1420     m_identified = b;
1421     if (!b) {
1422         m_audio_infos = nullptr;
1423         m_subtitle_infos = nullptr;
1424     }
1425 }
1426 
1427 QString Source::plugin (const QString &mime) const {
1428     return KConfigGroup (m_player->config (), mime).readEntry ("plugin", QString ());
1429 }
1430 
1431 QString Source::prettyName () {
1432     return i18n ("Unknown");
1433 }
1434 
1435 void Source::slotActivate ()
1436 {
1437     activate ();
1438 }
1439 
1440 //-----------------------------------------------------------------------------
1441 
1442 URLSource::URLSource (PartBase * player, const QUrl & url)
1443     : Source (i18n ("URL"), player, "urlsource"), activated (false) {
1444     setUrl (url.url ());
1445     //qCDebug(LOG_KMPLAYER_COMMON) << "URLSource::URLSource";
1446 }
1447 
1448 URLSource::~URLSource () {
1449     //qCDebug(LOG_KMPLAYER_COMMON) << "URLSource::~URLSource";
1450 }
1451 
1452 void URLSource::init () {
1453     Source::init ();
1454 }
1455 
1456 void URLSource::dimensions (int & w, int & h) {
1457     if (!m_player->mayResize () && m_player->view ()) {
1458         w = static_cast <View *> (m_player->view ())->viewArea ()->width ();
1459         h = static_cast <View *> (m_player->view ())->viewArea ()->height ();
1460     } else {
1461         Source::dimensions (w, h);
1462     }
1463 }
1464 
1465 bool URLSource::hasLength () {
1466     return !!length ();
1467 }
1468 
1469 void URLSource::activate () {
1470     if (activated)
1471         return;
1472     activated = true;
1473     if (url ().isEmpty () && (!m_document || !m_document->hasChildNodes ())) {
1474         m_player->updateTree ();
1475         return;
1476     }
1477     if (m_auto_play)
1478         play (nullptr);
1479 }
1480 
1481 void URLSource::reset () {
1482     Source::reset ();
1483 }
1484 
1485 void URLSource::forward () {
1486     Source::forward ();
1487 }
1488 
1489 void URLSource::backward () {
1490     Source::backward ();
1491 }
1492 
1493 void URLSource::play (Mrl *mrl) {
1494     Source::play (mrl);
1495 }
1496 
1497 void URLSource::deactivate () {
1498     if (!activated)
1499         return;
1500     activated = false;
1501     reset ();
1502     if (m_document) {
1503         m_document->document ()->dispose ();
1504         m_document = nullptr;
1505     }
1506     if (m_player->view ())
1507         m_player->viewWidget ()->viewArea ()->getSurface (nullptr);
1508 }
1509 
1510 QString URLSource::prettyName () {
1511     if (m_url.isEmpty ())
1512         return i18n ("URL");
1513     if (m_url.url ().size () > 50) {
1514         QString newurl;
1515         if (!m_url.isLocalFile ()) {
1516             newurl = m_url.scheme() + QString ("://");
1517             if (!m_url.host().isEmpty())
1518                 newurl += m_url.host ();
1519             if (m_url.port () != -1)
1520                 newurl += QString (":%1").arg (m_url.port ());
1521         }
1522         QString file = m_url.fileName ();
1523         int len = newurl.size () + file.size ();
1524         QUrl path = m_url.adjusted(QUrl::RemoveFilename|QUrl::RemoveQuery|QUrl::RemoveFragment);
1525         bool modified = false;
1526         while (path.url ().size () + len > 50) {
1527             const QUrl upPath = KIO::upUrl(path);
1528             if (path == upPath) {
1529                 break;
1530             }
1531             path = upPath;
1532             modified = true;
1533         }
1534         QString dir = path.path ();
1535         if (!dir.endsWith (QString ("/")))
1536             dir += '/';
1537         if (modified)
1538             dir += QString (".../");
1539         newurl += dir + file;
1540         return i18n ("URL - ") + newurl;
1541     }
1542     if (m_url.isLocalFile())
1543         return i18n ("URL - ") + m_url.toLocalFile();
1544     return i18n ("URL - ") + m_url.toDisplayString();
1545 }
1546 
1547 bool URLSource::authoriseUrl (const QString &url) {
1548     const QUrl base = QUrl::fromUserInput(document ()->mrl ()->src);
1549     const QUrl dest = QUrl::fromUserInput(url);
1550     if (base != dest) {
1551         // check if some remote playlist tries to open something local, but
1552         // do ignore unknown protocols because there are so many and we only
1553         // want to cache local ones.
1554         if (
1555 #if 0
1556             !KProtocolInfo::protocolClass (dest.scheme()).isEmpty () &&
1557 #else
1558             dest.isLocalFile () &&
1559 #endif
1560                 !KUrlAuthorized::authorizeUrlAction ("redirect", base, dest)) {
1561             qCWarning(LOG_KMPLAYER_COMMON) << "requestPlayURL from document " << base << " to play " << dest << " is not allowed";
1562             return false;
1563         }
1564     }
1565     return Source::authoriseUrl (url);
1566 }
1567 
1568 void URLSource::setUrl (const QString &url) {
1569     Source::setUrl (url);
1570     Mrl *mrl = document ()->mrl ();
1571     if (!url.isEmpty () && m_url.isLocalFile () && mrl->mimetype.isEmpty ()) {
1572         const QMimeType mimeType = QMimeDatabase().mimeTypeForUrl(m_url);
1573         if (mimeType.isValid())
1574             mrl->mimetype = mimeType.name();
1575     }
1576 }
1577 
1578 //-----------------------------------------------------------------------------
1579 
1580 #include "moc_kmplayerpartbase.cpp"