You can not select more than 25 topics Topics must start with a letter or number, can include dashes ('-') and can be up to 35 characters long.
kmplayer/src/kmplayerpartbase.cpp

2055 lines
73 KiB

/**
* Copyright (C) 2002-2003 by Koos Vriezen <koos.vriezen@gmail.com>
*
* This library is free software; you can redistribute it and/or
* modify it under the terms of the GNU Library General Public
* License version 2 as published by the Free Software Foundation.
*
* This library is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
* Library General Public License for more details.
*
* You should have received a copy of the GNU Library General Public License
* along with this library; see the file COPYING.LIB. If not, write to
* the Free Software Foundation, Inc., 51 Franklin Steet, Fifth Floor,
* Boston, MA 02110-1301, USA.
**/
#ifdef KDE_USE_FINAL
#undef Always
#endif
#include <config.h>
#include <math.h>
#include <tqapplication.h>
#include <tqcstring.h>
#include <tqcursor.h>
#include <tqtimer.h>
#include <tqpair.h>
#include <tqpushbutton.h>
#include <tqpopupmenu.h>
#include <tqslider.h>
#include <tqfile.h>
#include <tqregexp.h>
#include <tqtextstream.h>
#include <kmessagebox.h>
#include <kaboutdata.h>
#include <kdebug.h>
#include <kbookmarkmenu.h>
#include <kbookmarkmanager.h>
#include <tdeconfig.h>
#include <ksimpleconfig.h>
#include <kaction.h>
#include <kprocess.h>
#include <kstandarddirs.h>
#include <kmimetype.h>
#include <kprotocolinfo.h>
#include <kapplication.h>
#include <kstaticdeleter.h>
#include <tdeio/job.h>
#include <tdeio/jobclasses.h>
#include "kmplayerpartbase.h"
#include "kmplayerview.h"
#include "playlistview.h"
#include "viewarea.h"
#include "kmplayercontrolpanel.h"
#include "kmplayerconfig.h"
#include "kmplayerprocess.h"
#include "kmplayer_smil.h"
namespace KMPlayer {
class KMPLAYER_NO_EXPORT BookmarkOwner : public KBookmarkOwner {
public:
BookmarkOwner (PartBase *);
KDE_NO_CDTOR_EXPORT virtual ~BookmarkOwner () {}
void openBookmarkURL(const TQString& _url);
TQString currentTitle() const;
TQString currentURL() const;
private:
PartBase * m_player;
};
class KMPLAYER_NO_EXPORT BookmarkManager : public KBookmarkManager {
public:
BookmarkManager (const TQString &);
};
} // namespace
using namespace KMPlayer;
KDE_NO_CDTOR_EXPORT BookmarkOwner::BookmarkOwner (PartBase * player)
: m_player (player) {}
KDE_NO_EXPORT void BookmarkOwner::openBookmarkURL (const TQString & url) {
m_player->openURL (KURL (url));
}
KDE_NO_EXPORT TQString BookmarkOwner::currentTitle () const {
return m_player->source ()->prettyName ();
}
KDE_NO_EXPORT TQString BookmarkOwner::currentURL () const {
return m_player->source ()->url ().url ();
}
inline BookmarkManager::BookmarkManager(const TQString & bmfile)
: KBookmarkManager (bmfile, false) {
}
//-----------------------------------------------------------------------------
PartBase::PartBase (TQWidget * wparent, const char *wname,
TQObject * parent, const char *name, TDEConfig * config)
: KMediaPlayer::Player (wparent, wname ? wname : "kde_kmplayer_view", parent, name ? name : "kde_kmplayer_part"),
m_config (config),
m_view (new View (wparent, wname ? wname : "kde_kmplayer_view")),
m_settings (new Settings (this, config)),
m_recorder (0L),
m_source (0L),
m_bookmark_menu (0L),
m_record_timer (0),
m_update_tree_timer (0),
m_noresize (false),
m_auto_controls (true),
m_bPosSliderPressed (false),
m_in_update_tree (false)
{
MPlayer *mplayer = new MPlayer (this, m_settings);
m_players ["mplayer"] = mplayer;
m_process = mplayer;
Xine * xine = new Xine (this, m_settings);
m_players ["xine"] = xine;
m_players ["gstreamer"] = new GStreamer (this, m_settings);
m_recorders ["mencoder"] = new MEncoder (this, m_settings);
m_recorders ["mplayerdumpstream"] = new MPlayerDumpstream(this, m_settings);
m_recorders ["ffmpeg"] = new FFMpeg (this, m_settings);
m_recorders ["xine"] = xine;
m_sources ["urlsource"] = new URLSource (this);
TQString bmfile = locate ("data", "kmplayer/bookmarks.xml");
TQString localbmfile = locateLocal ("data", "kmplayer/bookmarks.xml");
if (localbmfile != bmfile) {
kdDebug () << "cp " << bmfile << " " << localbmfile << endl;
TDEProcess p;
p << "/bin/cp" << TQString(TQFile::encodeName (bmfile)) << TQString(TQFile::encodeName (localbmfile));
p.start (TDEProcess::Block);
}
m_bookmark_manager = new BookmarkManager (localbmfile);
m_bookmark_owner = new BookmarkOwner (this);
}
void PartBase::showConfigDialog () {
m_settings->show ("URLPage");
}
KDE_NO_EXPORT void PartBase::showPlayListWindow () {
// note, this is also the slot of application's view_playlist action, but
// anyhow, actions don't work for the fullscreen out-of-the-box, so ...
if (m_view->viewArea ()->isFullScreen ())
fullScreen ();
else if (m_view->viewArea ()->isMinimalMode ())
; //done by app: m_view->viewArea ()->minimalMode ();
else
m_view->toggleShowPlaylist ();
}
KDE_NO_EXPORT void PartBase::addBookMark (const TQString & t, const TQString & url) {
KBookmarkGroup b = m_bookmark_manager->root ();
b.addBookmark (m_bookmark_manager, t, KURL (url));
m_bookmark_manager->emitChanged (b);
}
void PartBase::init (TDEActionCollection * action_collection) {
KParts::Part::setWidget (m_view);
m_view->init (action_collection);
#ifdef HAVE_NSPR
m_players ["npp"] = new NpPlayer (this, m_settings, m_service);
#endif
connect(m_settings, TQT_SIGNAL(configChanged()), this, TQT_SLOT(settingsChanged()));
m_settings->readConfig ();
m_settings->applyColorSetting (false);
m_bookmark_menu = new KBookmarkMenu (m_bookmark_manager, m_bookmark_owner, m_view->controlPanel ()->bookmarkMenu (), action_collection, true, true);
connect (m_view, TQT_SIGNAL (urlDropped (const KURL::List &)), this, TQT_SLOT (openURL (const KURL::List &)));
connectPlaylist (m_view->playList ());
connectInfoPanel (m_view->infoPanel ());
new TDEAction (i18n ("Edit playlist &item"), 0, 0, TQT_TQOBJECT(m_view->playList ()), TQT_SLOT (editCurrent ()), action_collection, "edit_playlist_item");
}
void PartBase::connectPanel (ControlPanel * panel) {
panel->contrastSlider ()->setValue (m_settings->contrast);
panel->brightnessSlider ()->setValue (m_settings->brightness);
panel->hueSlider ()->setValue (m_settings->hue);
panel->saturationSlider ()->setValue (m_settings->saturation);
panel->volumeBar ()->setValue (m_settings->volume);
connect (panel->button (ControlPanel::button_playlist), TQT_SIGNAL (clicked ()), this, TQT_SLOT (showPlayListWindow ()));
connect (panel->button (ControlPanel::button_back), TQT_SIGNAL (clicked ()), this, TQT_SLOT (back ()));
connect (panel->button (ControlPanel::button_play), TQT_SIGNAL (clicked ()), this, TQT_SLOT (play ()));
connect (panel->button (ControlPanel::button_forward), TQT_SIGNAL (clicked ()), this, TQT_SLOT (forward ()));
connect (panel->button (ControlPanel::button_pause), TQT_SIGNAL (clicked ()), this, TQT_SLOT (pause ()));
connect (panel->button (ControlPanel::button_stop), TQT_SIGNAL (clicked ()), this, TQT_SLOT (stop ()));
connect (panel->button (ControlPanel::button_record), TQT_SIGNAL (clicked()), this, TQT_SLOT (record()));
connect (panel->volumeBar (), TQT_SIGNAL (volumeChanged (int)), this, TQT_SLOT (volumeChanged (int)));
connect (panel->positionSlider (), TQT_SIGNAL (valueChanged (int)), this, TQT_SLOT (positionValueChanged (int)));
connect (panel->positionSlider (), TQT_SIGNAL (sliderPressed()), this, TQT_SLOT (posSliderPressed()));
connect (panel->positionSlider (), TQT_SIGNAL (sliderReleased()), this, TQT_SLOT (posSliderReleased()));
connect (this, TQT_SIGNAL (positioned (int, int)), panel, TQT_SLOT (setPlayingProgress (int, int)));
connect (this, TQT_SIGNAL (loading(int)), panel, TQT_SLOT(setLoadingProgress(int)));
connect (panel->contrastSlider (), TQT_SIGNAL (valueChanged(int)), this, TQT_SLOT (contrastValueChanged(int)));
connect (panel->brightnessSlider (), TQT_SIGNAL (valueChanged(int)), this, TQT_SLOT (brightnessValueChanged(int)));
connect (panel->hueSlider (), TQT_SIGNAL (valueChanged(int)), this, TQT_SLOT (hueValueChanged(int)));
connect (panel->saturationSlider (), TQT_SIGNAL (valueChanged(int)), this, TQT_SLOT (saturationValueChanged(int)));
connect (this, TQT_SIGNAL (languagesUpdated(const TQStringList &, const TQStringList &)), panel, TQT_SLOT (setLanguages (const TQStringList &, const TQStringList &)));
connect (panel->audioMenu (), TQT_SIGNAL (activated (int)), this, TQT_SLOT (audioSelected (int)));
connect (panel->subtitleMenu (), TQT_SIGNAL (activated (int)), this, TQT_SLOT (subtitleSelected (int)));
connect (this, TQT_SIGNAL (audioIsSelected (int)), panel, TQT_SLOT (selectAudioLanguage (int)));
connect (this, TQT_SIGNAL (subtitleIsSelected (int)), panel, TQT_SLOT (selectSubtitle (int)));
panel->popupMenu()->connectItem (ControlPanel::menu_fullscreen, this, TQT_SLOT (fullScreen ()));
panel->popupMenu ()->connectItem (ControlPanel::menu_config,
this, TQT_SLOT (showConfigDialog ()));
panel->popupMenu ()->connectItem (ControlPanel::menu_video,
m_view, TQT_SLOT(toggleVideoConsoleWindow()));
panel->popupMenu ()->connectItem (ControlPanel::menu_playlist,
m_view, TQT_SLOT (toggleShowPlaylist ()));
panel->popupMenu ()->connectItem (ControlPanel::menu_minimal,
this, TQT_SLOT (minimalMode ()));
connect (this, TQT_SIGNAL (statusUpdated (const TQString &)),
panel->view (), TQT_SLOT (setStatusMessage (const TQString &)));
//connect (panel (), TQT_SIGNAL (clicked ()), m_settings, TQT_SLOT (show ()));
}
void PartBase::connectPlaylist (PlayListView * playlist) {
connect (playlist, TQT_SIGNAL (addBookMark (const TQString &, const TQString &)),
this, TQT_SLOT (addBookMark (const TQString &, const TQString &)));
connect (playlist, TQT_SIGNAL (executed (TQListViewItem *)),
this, TQT_SLOT (playListItemExecuted (TQListViewItem *)));
connect (playlist, TQT_SIGNAL (clicked (TQListViewItem *)),
this, TQT_SLOT (playListItemClicked (TQListViewItem *)));
connect (this, TQT_SIGNAL (treeChanged (int, NodePtr, NodePtr, bool, bool)),
playlist, TQT_SLOT (updateTree (int, NodePtr, NodePtr, bool, bool)));
connect (this, TQT_SIGNAL (treeUpdated ()),
playlist, TQT_SLOT (triggerUpdate ()));
}
void PartBase::connectInfoPanel (InfoWindow * infopanel) {
connect (this, TQT_SIGNAL (infoUpdated (const TQString &)),
infopanel->view (), TQT_SLOT (setInfoMessage (const TQString &)));
}
PartBase::~PartBase () {
kdDebug() << "PartBase::~PartBase" << endl;
m_view = (View*) 0;
stop ();
if (m_source)
m_source->deactivate ();
delete m_settings;
delete m_bookmark_menu;
delete m_bookmark_manager;
delete m_bookmark_owner;
}
void PartBase::settingsChanged () {
if (!m_view)
return;
if (m_settings->showcnfbutton)
m_view->controlPanel()->button (ControlPanel::button_config)->show();
else
m_view->controlPanel()->button (ControlPanel::button_config)->hide();
m_view->controlPanel()->enableRecordButtons (m_settings->showrecordbutton);
if (m_settings->showplaylistbutton)
m_view->controlPanel()->button (ControlPanel::button_playlist)->show();
else
m_view->controlPanel()->button (ControlPanel::button_playlist)->hide();
if (!m_settings->showbroadcastbutton)
m_view->controlPanel ()->broadcastButton ()->hide ();
keepMovieAspect (m_settings->sizeratio);
m_settings->applyColorSetting (true);
}
KMediaPlayer::View* PartBase::view () {
return m_view;
}
extern const char * strGeneralGroup;
bool PartBase::setProcess (Mrl *mrl) {
// determine backend, start with temp_backends
TQString p = temp_backends [m_source->name()];
bool remember_backend = p.isEmpty ();
bool changed = false;
if (p.isEmpty ()) {
// next try to find mimetype match from kmplayerrc
if (!mrl->mimetype.isEmpty ()) {
m_config->setGroup (mrl->mimetype);
p = m_config->readEntry ("player", "" );
remember_backend = !(!p.isEmpty () &&
m_players.contains (p) &&
m_players [p]->supports (m_source->name ()));
}
}
if (p.isEmpty ())
// try source match from kmplayerrc
p = m_settings->backends [m_source->name()];
if (p.isEmpty ()) {
// try source match from kmplayerrc by re-reading
m_config->setGroup (strGeneralGroup);
p = m_config->readEntry (m_source->name (), "");
}
if (p.isEmpty () ||
!m_players.contains (p) ||
!m_players [p]->supports (m_source->name ())) {
// finally find first supported player
p.truncate (0);
if (!m_process || !m_process->supports (m_source->name ())) {
ProcessMap::const_iterator i, e = m_players.end();
for (i = m_players.begin(); i != e; ++i)
if (i.data ()->supports (m_source->name ())) {
p = TQString (i.data ()->name ());
break;
}
} else
p = TQString (m_process->name ());
}
if (!p.isEmpty ()) {
if (!m_process || p != m_process->name ()) {
setProcess (p.ascii ());
updatePlayerMenu (m_view->controlPanel ());
changed = true;
}
if (remember_backend)
m_settings->backends [m_source->name()] = m_process->name ();
else
temp_backends.remove (m_source->name());
}
return changed;
}
void PartBase::setProcess (const char * name) {
Process * process = name ? m_players [name] : 0L;
if (m_process == process)
return;
if (!m_source)
m_source = m_sources ["urlsource"];
Process * old_process = m_process;
m_process = process;
if (old_process && old_process->state () > Process::NotRunning)
old_process->quit ();
if (!m_process)
return;
m_process->setSource (m_source);
if (m_process->playing ()) {
m_view->controlPanel ()->setPlaying (true);
m_view->controlPanel ()->showPositionSlider (!!m_source->length ());
m_view->controlPanel ()->enableSeekButtons (m_source->isSeekable ());
}
emit processChanged (name);
}
void PartBase::setRecorder (const char * name) {
Process * recorder = name ? m_recorders [name] : 0L;
if (m_recorder == recorder)
return;
if (m_recorder)
m_recorder->quit ();
m_recorder = recorder;
}
KDE_NO_EXPORT void PartBase::slotPlayerMenu (int id) {
bool playing = m_process->playing ();
const char * srcname = m_source->name ();
TQPopupMenu * menu = m_view->controlPanel ()->playerMenu ();
ProcessMap::const_iterator pi = m_players.begin(), e = m_players.end();
unsigned i = 0;
for (; pi != e && i < menu->count(); ++pi) {
Process * proc = pi.data ();
if (!proc->supports (srcname))
continue;
int menuid = menu->idAt (i);
menu->setItemChecked (menuid, menuid == id);
if (menuid == id) {
if (proc->name () != TQString ("npp"))
m_settings->backends [srcname] = proc->name ();
temp_backends [srcname] = proc->name ();
if (playing && strcmp (m_process->name (), proc->name ()))
m_process->quit ();
setProcess (proc->name ());
}
++i;
}
if (playing)
setSource (m_source); // re-activate
}
void PartBase::updatePlayerMenu (ControlPanel * panel) {
if (!m_view || !m_process)
return;
TQPopupMenu * menu = panel->playerMenu ();
menu->clear ();
if (!m_source)
return;
const ProcessMap::const_iterator e = m_players.end();
int id = 0; // if multiple parts, id's should be the same for all menu's
for (ProcessMap::const_iterator i = m_players.begin(); i != e; ++i) {
Process * p = i.data ();
if (p->supports (m_source->name ())) {
menu->insertItem (p->menuName (), this, TQT_SLOT (slotPlayerMenu (int)), 0, id++);
if (i.data() == m_process)
menu->setItemChecked (id-1, true);
}
}
}
void PartBase::connectSource (Source * old_source, Source * source) {
if (old_source) {
disconnect (old_source, TQT_SIGNAL(endOfPlayItems ()), this, TQT_SLOT(stop ()));
disconnect (old_source, TQT_SIGNAL (dimensionsChanged ()),
this, TQT_SLOT (sourceHasChangedAspects ()));
disconnect (old_source, TQT_SIGNAL (startPlaying ()),
this, TQT_SLOT (playingStarted ()));
disconnect (old_source, TQT_SIGNAL (stopPlaying ()),
this, TQT_SLOT (playingStopped ()));
}
if (source) {
connect (source, TQT_SIGNAL (endOfPlayItems ()), this, TQT_SLOT (stop ()));
connect (source, TQT_SIGNAL (dimensionsChanged ()),
this, TQT_SLOT (sourceHasChangedAspects ()));
connect (source, TQT_SIGNAL (startPlaying()), this, TQT_SLOT(playingStarted()));
connect (source, TQT_SIGNAL (stopPlaying ()), this, TQT_SLOT(playingStopped()));
}
}
void PartBase::setSource (Source * _source) {
Source * old_source = m_source;
if (m_source) {
m_source->deactivate ();
stop ();
if (m_view) {
m_view->reset ();
emit infoUpdated (TQString ());
}
disconnect (m_source, TQT_SIGNAL (startRecording ()),
this, TQT_SLOT (recordingStarted ()));
disconnect (this, TQT_SIGNAL (audioIsSelected (int)),
m_source, TQT_SLOT (setAudioLang (int)));
disconnect (this, TQT_SIGNAL (subtitleIsSelected (int)),
m_source, TQT_SLOT (setSubtitle (int)));
}
if (m_view) {
if (m_auto_controls)
m_view->controlPanel ()->setAutoControls (m_auto_controls);
m_view->controlPanel ()->enableRecordButtons (m_settings->showrecordbutton);
if (!m_settings->showcnfbutton)
m_view->controlPanel()->button(ControlPanel::button_config)->hide();
if (!m_settings->showplaylistbutton)
m_view->controlPanel()->button(ControlPanel::button_playlist)->hide();
}
m_source = _source;
connectSource (old_source, m_source);
m_process->setSource (m_source);
connect (m_source, TQT_SIGNAL(startRecording()), this,TQT_SLOT(recordingStarted()));
connect (this, TQT_SIGNAL (audioIsSelected (int)),
m_source, TQT_SLOT (setAudioLang (int)));
connect (this, TQT_SIGNAL (subtitleIsSelected (int)),
m_source, TQT_SLOT (setSubtitle (int)));
m_source->init ();
m_source->setIdentified (false);
if (m_view && m_view->viewer ()) {
updatePlayerMenu (m_view->controlPanel ());
m_view->viewer ()->setAspect (0.0);
}
if (m_source) TQTimer::singleShot (0, m_source, TQT_SLOT (activate ()));
updateTree (true, true);
emit sourceChanged (old_source, m_source);
}
KDE_NO_EXPORT void PartBase::changeURL (const TQString & url) {
emit urlChanged (url);
}
bool PartBase::isSeekable (void) const {
return m_source ? m_source->isSeekable () : false;
}
bool PartBase::hasLength () const {
return m_source ? m_source->hasLength () : false;
}
unsigned long PartBase::length () const {
return m_source ? m_source->length () : 0;
}
bool PartBase::openURL (const KURL & url) {
kdDebug () << "PartBase::openURL " << url.url() << url.isValid () << endl;
if (!m_view) return false;
stop ();
Source * src = (url.isEmpty () ? m_sources ["urlsource"] : (!url.protocol ().compare ("kmplayer") && m_sources.contains (url.host ()) ? m_sources [url.host ()] : m_sources ["urlsource"]));
src->setSubURL (KURL ());
src->setURL (url);
setSource (src);
return true;
}
bool PartBase::openURL (const KURL::List & urls) {
if (urls.size () == 1) {
openURL (urls[0]);
} else {
openURL (KURL ());
NodePtr d = m_source->document ();
if (d)
for (unsigned int i = 0; i < urls.size (); i++)
d->appendChild (new GenericURL (d, KURL::decode_string (urls [i].url ())));
}
return true;
}
bool PartBase::closeURL () {
stop ();
if (m_view) {
m_view->viewer ()->setAspect (0.0);
m_view->reset ();
}
return true;
}
bool PartBase::openFile () {
return false;
}
void PartBase::keepMovieAspect (bool b) {
if (m_view) {
m_view->setKeepSizeRatio (b);
if (m_source)
m_view->viewer ()->setAspect (b ? m_source->aspect () : 0.0);
}
}
void PartBase::recordingStarted () {
if (m_settings->replayoption == Settings::ReplayAfter)
m_record_timer = startTimer (1000 * m_settings->replaytime);
}
void PartBase::recordingStopped () {
killTimer (m_record_timer);
m_record_timer = 0;
Recorder * rec = dynamic_cast <Recorder*> (m_recorder);
if (rec) {
if (m_settings->replayoption == Settings::ReplayFinished ||
(m_settings->replayoption == Settings::ReplayAfter && !playing ()))
openURL (rec->recordURL ());
rec->setURL (KURL ());
}
setRecorder ("mencoder"); //FIXME see PartBase::record() checking playing()
}
void PartBase::timerEvent (TQTimerEvent * e) {
if (e->timerId () == m_record_timer) {
kdDebug () << "record timer event" << (m_recorder->playing () && !playing ()) << endl;
m_record_timer = 0;
if (m_recorder->playing () && !playing ()) {
Recorder * rec = dynamic_cast <Recorder*> (m_recorder);
if (rec) {
openURL (rec->recordURL ());
rec->setURL (KURL ());
}
}
} else if (e->timerId () == m_update_tree_timer) {
m_update_tree_timer = 0;
updateTree (m_update_tree_full, true);
}
killTimer (e->timerId ());
}
void PartBase::playingStarted () {
//m_view->viewer ()->setAspect (m_source->aspect ());
if (m_view) {
m_view->controlPanel ()->setPlaying (true);
m_view->controlPanel ()->showPositionSlider (!!m_source->length ());
m_view->controlPanel ()->enableSeekButtons (m_source->isSeekable ());
if (m_settings->autoadjustvolume && m_process)
m_process->volume(m_view->controlPanel()->volumeBar()->value(),true);
}
emit loading (100);
}
void PartBase::playingStopped () {
kdDebug () << "playingStopped " << this << endl;
if (m_view) {
m_view->controlPanel ()->setPlaying (false);
m_view->reset ();
}
m_bPosSliderPressed = false;
}
KDE_NO_EXPORT void PartBase::setPosition (int position, int length) {
if (m_view && !m_bPosSliderPressed)
emit positioned (position, length);
}
void PartBase::setLoaded (int percentage) {
emit loading (percentage);
}
unsigned long PartBase::position () const {
return m_source ? 100 * m_source->position () : 0;
}
void PartBase::pause () {
NodePtr doc = m_source ? m_source->document () : 0L;
if (doc) {
if (doc->state == Node::state_deferred)
doc->undefer ();
else
doc->defer ();
}
}
void PartBase::back () {
m_source->backward ();
}
void PartBase::forward () {
m_source->forward ();
}
KDE_NO_EXPORT void PartBase::playListItemClicked (TQListViewItem * item) {
if (!item)
return;
PlayListItem * vi = static_cast <PlayListItem *> (item);
RootPlayListItem * ri = vi->playListView ()->rootItem (item);
if (ri == item && vi->node) {
TQString src = ri->source;
//kdDebug() << "playListItemClicked " << src << " " << vi->node->nodeName() << endl;
Source * source = src.isEmpty() ? m_source : m_sources[src.ascii()];
if (vi->node->isPlayable ()) {
source->jump (vi->node); //may become !isPlayable by lazy loading
if (!vi->node->isPlayable ())
emit treeChanged (ri->id, vi->node, 0, false, true);
} else if (vi->firstChild ())
vi->listView ()->setOpen (vi, !vi->isOpen ());
} else if (!vi->node && !vi->m_attr)
updateTree (); // items already deleted
}
KDE_NO_EXPORT void PartBase::playListItemExecuted (TQListViewItem * item) {
if (m_in_update_tree) return;
if (m_view->editMode ()) return;
PlayListItem * vi = static_cast <PlayListItem *> (item);
RootPlayListItem * ri = vi->playListView ()->rootItem (item);
if (ri == item)
return; // both null or handled by playListItemClicked
if (vi->node) {
TQString src = ri->source;
//kdDebug() << "playListItemExecuted " << src << " " << vi->node->nodeName() << endl;
Source * source = src.isEmpty() ? m_source : m_sources[src.ascii()];
if (vi->node->isPlayable ()) {
source->jump (vi->node); //may become !isPlayable by lazy loading
if (!vi->node->isPlayable ())
emit treeChanged (ri->id, vi->node, 0, false, true);
} else if (vi->firstChild ())
vi->listView ()->setOpen (vi, !vi->isOpen ());
} else if (vi->m_attr) {
if (vi->m_attr->name () == StringPool::attr_src ||
vi->m_attr->name () == StringPool::attr_href ||
vi->m_attr->name () == StringPool::attr_url ||
vi->m_attr->name () == StringPool::attr_value ||
vi->m_attr->name () == "data") {
TQString src (vi->m_attr->value ());
if (!src.isEmpty ()) {
PlayListItem * pi = static_cast <PlayListItem*>(item->parent());
if (pi) {
for (NodePtr e = pi->node; e; e = e->parentNode ()) {
Mrl * mrl = e->mrl ();
if (mrl)
src = KURL (mrl->absolutePath (), src).url ();
}
KURL url (src);
if (url.isValid ())
openURL (url);
}
}
}
} else
emit treeChanged (ri->id, ri->node, 0L, false, false);
if (m_view)
m_view->viewArea ()->setFocus ();
}
void PartBase::updateTree (bool full, bool force) {
if (force) {
m_in_update_tree = true;
if (m_update_tree_full) {
if (m_source)
emit treeChanged (0, m_source->root (), m_source->current (), true, false);
} else
emit treeUpdated ();
m_in_update_tree = false;
if (m_update_tree_timer) {
killTimer (m_update_tree_timer);
m_update_tree_timer = 0;
}
} else if (!m_update_tree_timer) {
m_update_tree_timer = startTimer (100);
m_update_tree_full = full;
} else
m_update_tree_full |= full;
}
void PartBase::updateInfo (const TQString & msg) {
emit infoUpdated (msg);
}
void PartBase::updateStatus (const TQString & msg) {
emit statusUpdated (msg);
}
void PartBase::setLanguages (const TQStringList & al, const TQStringList & sl) {
emit languagesUpdated (al, sl);
}
KDE_NO_EXPORT void PartBase::audioSelected (int id) {
emit audioIsSelected (id);
}
KDE_NO_EXPORT void PartBase::subtitleSelected (int id) {
emit subtitleIsSelected (id);
}
void PartBase::record () {
if (m_view) m_view->setCursor (TQCursor (TQt::WaitCursor));
if (m_recorder->playing ()) {
m_recorder->stop ();
} else {
m_settings->show ("RecordPage");
m_view->controlPanel ()->setRecording (false);
}
if (m_view) m_view->setCursor (TQCursor (TQt::ArrowCursor));
}
void PartBase::play () {
if (!m_process || !m_view) return;
TQPushButton * pb = ::tqqt_cast <TQPushButton *> (sender ());
if (pb && !pb->isOn ()) {
stop ();
return;
}
if (m_update_tree_timer) {
killTimer (m_update_tree_timer);
m_update_tree_timer = 0;
}
if (m_process->state () == Process::NotRunning) {
PlayListItem * lvi = m_view->playList ()->currentPlayListItem ();
if (lvi) { // make sure it's in the first tree
TQListViewItem * pitem = lvi;
while (pitem->parent())
pitem = pitem->parent();
if (pitem != m_view->playList ()->firstChild ())
lvi = 0L;
}
if (!lvi)
lvi = static_cast<PlayListItem*>(m_view->playList()->firstChild());
if (lvi)
for (NodePtr n = lvi->node; n; n = n->parentNode ()) {
if (n->isPlayable ()) {
m_source->setCurrent (n);
break;
}
}
m_process->ready (m_view->viewer ());
} else if (m_process->state () == Process::Ready) {
m_source->playCurrent ();
} else
m_process->play (m_source, m_source->current ());
}
bool PartBase::playing () const {
return m_process && m_process->state () > Process::Ready;
}
void PartBase::stop () {
TQPushButton * b = m_view ? m_view->controlPanel ()->button (ControlPanel::button_stop) : 0L;
if (b) {
if (!b->isOn ())
b->toggle ();
m_view->setCursor (TQCursor (TQt::WaitCursor));
}
if (m_process)
m_process->quit ();
if (m_source)
m_source->reset ();
if (m_view) {
m_view->setCursor (TQCursor (TQt::ArrowCursor));
if (b->isOn ())
b->toggle ();
m_view->controlPanel ()->setPlaying (false);
setLoaded (100);
}
}
void PartBase::seek (unsigned long msec) {
if (m_process)
m_process->seek (msec/100, true);
}
void PartBase::adjustVolume (int incdec) {
m_process->volume (incdec, false);
}
void PartBase::increaseVolume () {
if (m_view)
m_view->controlPanel ()->volumeBar ()->setValue (m_view->controlPanel ()->volumeBar ()->value () + 2);
}
void PartBase::decreaseVolume () {
if (m_view)
m_view->controlPanel ()->volumeBar ()->setValue (m_view->controlPanel ()->volumeBar ()->value () - 2);
}
KDE_NO_EXPORT void PartBase::posSliderPressed () {
m_bPosSliderPressed=true;
}
KDE_NO_EXPORT void PartBase::posSliderReleased () {
m_bPosSliderPressed=false;
const TQSlider * posSlider = ::tqqt_cast<const TQSlider *> (sender ());
if (posSlider)
m_process->seek (posSlider->value(), true);
}
KDE_NO_EXPORT void PartBase::volumeChanged (int val) {
if (m_process) {
m_settings->volume = val;
m_process->volume (val, true);
}
}
KDE_NO_EXPORT void PartBase::contrastValueChanged (int val) {
m_settings->contrast = val;
m_process->contrast (val, true);
}
KDE_NO_EXPORT void PartBase::brightnessValueChanged (int val) {
m_settings->brightness = val;
m_process->brightness (val, true);
}
KDE_NO_EXPORT void PartBase::hueValueChanged (int val) {
m_settings->hue = val;
m_process->hue (val, true);
}
KDE_NO_EXPORT void PartBase::saturationValueChanged (int val) {
m_settings->saturation = val;
m_process->saturation (val, true);
}
KDE_NO_EXPORT void PartBase::sourceHasChangedAspects () {
if (m_view && m_source) {
//kdDebug () << "sourceHasChangedAspects " << m_source->aspect () << endl;
m_view->viewer ()->setAspect (m_source->aspect ());
m_view->updateLayout ();
}
emit sourceDimensionChanged ();
}
KDE_NO_EXPORT void PartBase::positionValueChanged (int pos) {
TQSlider * slider = ::tqqt_cast <TQSlider *> (sender ());
if (slider && slider->isEnabled ())
m_process->seek (pos, true);
}
KDE_NO_EXPORT void PartBase::fullScreen () {
if (m_view)
m_view->fullScreen ();
}
KDE_NO_EXPORT void PartBase::toggleFullScreen () {
m_view->fullScreen ();
}
KDE_NO_EXPORT void PartBase::minimalMode () {
emit toggleMinimalMode ();
}
KDE_NO_EXPORT bool PartBase::isPlaying () {
return playing ();
}
KDE_NO_EXPORT bool PartBase::isPaused () {
NodePtr doc = m_source ? m_source->document () : 0L;
return doc && doc->state == Node::state_deferred;
}
TDEAboutData* PartBase::createAboutData () {
KMessageBox::error(0L, "createAboutData", "KMPlayer");
return 0;
}
//-----------------------------------------------------------------------------
Source::Source (const TQString & name, PartBase * player, const char * n)
: TQObject (player, n),
m_name (name), m_player (player), m_identified (false), m_auto_play (true),
m_frequency (0), m_xvport (0), m_xvencoding (-1), m_doc_timer (0) {
init ();
}
Source::~Source () {
if (m_document)
m_document->document ()->dispose ();
m_document = 0L;
Q_ASSERT (m_current.ptr () == 0L);
}
void Source::init () {
//setDimensions (320, 240);
m_width = 0;
m_height = 0;
m_aspect = 0.0;
m_length = 0;
m_position = 0;
setLength (m_document, 0);
m_recordcmd.truncate (0);
}
KDE_NO_EXPORT void Source::setLanguages (const TQStringList & alang, const TQStringList & slang) {
m_player->setLanguages (alang, slang);
}
void Source::setDimensions (NodePtr node, int w, int h) {
Mrl * mrl = node ? node->mrl () : 0L;
if (mrl && mrl->view_mode == Mrl::WindowMode) {
mrl->width = w;
mrl->height = h;
float a = h > 0 ? 1.0 * w / h : 0.0;
mrl->aspect = a;
if (m_player->view ()) {
static_cast <View *> (m_player->view())->viewer()->setAspect(a);
static_cast <View *> (m_player->view ())->updateLayout ();
}
} else if (m_aspect < 0.001 || m_width != w || m_height != h) {
bool ev = (w > 0 && h > 0) ||
(h == 0 && m_height > 0) ||
(w == 0 && m_width > 0);
m_width = w;
m_height = h;
if (m_aspect < 0.001)
setAspect (node, h > 0 ? 1.0 * w / h : 0.0);
//kdDebug () << "setDimensions " << w << "x" << h << " a:" << m_aspect << endl;
if (ev)
emit dimensionsChanged ();
}
}
void Source::setAspect (NodePtr node, float a) {
//kdDebug () << "setAspect " << a << endl;
Mrl * mrl = node ? node->mrl () : 0L;
bool changed = false;
if (mrl) {
if (mrl->view_mode == Mrl::WindowMode)
changed |= (fabs (mrl->aspect - a) > 0.001);
mrl->aspect = a;
}
if (!mrl || mrl->view_mode == Mrl::SingleMode) {
changed |= (fabs (m_aspect - a) > 0.001);
m_aspect = a;
}
if (changed)
emit dimensionsChanged ();
}
void Source::setLength (NodePtr, int len) {
m_length = len;
m_player->setPosition (m_position, m_length);
}
KDE_NO_EXPORT void Source::setPosition (int pos) {
m_position = pos;
m_player->setPosition (pos, m_length);
}
KDE_NO_EXPORT void Source::setLoading (int percentage) {
m_player->setLoaded (percentage);
}
/*
static void printTree (NodePtr root, TQString off=TQString()) {
if (!root) {
kdDebug() << off << "[null]" << endl;
return;
}
kdDebug() << off << root->nodeName() << " " << (Element*)root << (root->isPlayable() ? root->mrl ()->src : TQString ("-")) << endl;
off += TQString (" ");
for (NodePtr e = root->firstChild(); e; e = e->nextSibling())
printTree(e, off);
}*/
void Source::setURL (const KURL & url) {
m_url = url;
m_back_request = 0L;
if (m_document && !m_document->hasChildNodes () &&
(m_document->mrl()->src.isEmpty () ||
m_document->mrl()->src == url.url ()))
// special case, mime is set first by plugin FIXME v
m_document->mrl()->src = url.url ();
else {
if (m_document)
m_document->document ()->dispose ();
m_document = new Document (url.url (), this);
}
if (m_player->process () && m_player->source () == this)
m_player->updateTree ();
//kdDebug() << name() << " setURL " << url << endl;
m_current = m_document;
}
void Source::setTitle (const TQString & title) {
emit titleChanged (title);
}
KDE_NO_EXPORT void Source::setAudioLang (int id) {
View * v = static_cast <View *> (m_player->view());
if (v && m_player->process ())
m_player->process ()->setAudioLang (id, v->controlPanel ()->audioMenu ()->text (id));
}
KDE_NO_EXPORT void Source::setSubtitle (int id) {
View * v = static_cast <View *> (m_player->view());
if (v && m_player->process ())
m_player->process ()->setSubtitle (id, v->controlPanel ()->subtitleMenu ()->text (id));
}
void Source::reset () {
if (m_document) {
//kdDebug() << "Source::first" << endl;
m_current = NodePtr ();
m_document->reset ();
m_player->updateTree ();
}
init ();
}
TQString Source::currentMrl () {
Mrl * mrl = m_current ? m_current->mrl () : 0L;
kdDebug() << "Source::currentMrl " << (m_current ? m_current->nodeName():"") << " src:" << (mrl ? mrl->absolutePath () : TQString ()) << endl;
return mrl ? mrl->absolutePath () : TQString ();
}
void Source::playCurrent () {
TQString url = currentMrl ();
m_player->changeURL (url);
m_width = m_height = 0;
m_aspect = 0.0;
if (m_player->view ())
static_cast <View *> (m_player->view ())->playingStop ();//show controls
if (m_document && !m_document->active ()) {
if (!m_current)
m_document->activate ();
else { // ugly code duplicate w/ back_request
for (NodePtr p = m_current->parentNode(); p; p = p->parentNode())
p->state = Element::state_activated;
m_current->activate ();
}
} else if (!m_current) {
emit endOfPlayItems ();
} else if (m_current->state == Element::state_deferred) {
// m_current->undefer ();
} else if (m_player->process ()->state () == Process::NotRunning) {
m_player->process ()->ready (static_cast <View *> (m_player->view ())->viewer ());
} else if (m_player->process ()) {
Mrl * mrl = m_back_request ? m_back_request->mrl () : m_current->mrl ();
if (mrl->view_mode == Mrl::SingleMode) {
// don't reset the dimensions if we have any
m_width = mrl->width;
m_height = mrl->height;
m_aspect = mrl->aspect;
}
m_back_request = 0L;
m_player->process ()->play (this, mrl->linkNode ());
}
//kdDebug () << "Source::playCurrent " << (m_current ? m_current->nodeName():" doc act:") << (m_document && !m_document->active ()) << " cur:" << (!m_current) << " cur act:" << (m_current && !m_current->active ()) << endl;
m_player->updateTree ();
emit dimensionsChanged ();
}
static NodePtr findDepthFirst (NodePtr elm) {
if (!elm)
return NodePtr ();
NodePtr tmp = elm;
for ( ; tmp; tmp = tmp->nextSibling ()) {
if (tmp->isPlayable ())
return tmp;
NodePtr tmp2 = findDepthFirst (tmp->firstChild ());
if (tmp2)
return tmp2;
}
return NodePtr ();
}
bool Source::requestPlayURL (NodePtr mrl) {
//kdDebug() << "Source::requestPlayURL " << mrl->mrl ()->src << endl;
if (m_player->process ()->state () > Process::Ready) {
if (m_player->process ()->mrl () == mrl->mrl ()->linkNode ())
return true;
m_back_request = mrl; // still playing, schedule it
m_player->process ()->stop ();
} else {
if (mrl->mrl ()->view_mode == Mrl::SingleMode)
m_current = mrl;
else
m_back_request = mrl;
m_player->updateTree ();
TQTimer::singleShot (0, this, TQT_SLOT (playCurrent ()));
}
m_player->setProcess (mrl->mrl ());
return true;
}
bool Source::resolveURL (NodePtr) {
return true;
}
void Source::setTimeout (int ms) {
//kdDebug () << "Source::setTimeout " << ms << endl;
if (m_doc_timer)
killTimer (m_doc_timer);
m_doc_timer = ms > -1 ? startTimer (ms) : 0;
}
void Source::timerEvent (TQTimerEvent * e) {
if (e->timerId () == m_doc_timer && m_document && m_document->active ())
m_document->document ()->timer (); // will call setTimeout()
else
killTimer (e->timerId ());
}
bool Source::setCurrent (NodePtr mrl) {
m_current = mrl;
return true;
}
void Source::stateElementChanged (Node * elm, Node::State os, Node::State ns) {
//kdDebug() << "Source::stateElementChanged " << elm->nodeName () << " state:" << (int) elm->state << " cur isPlayable:" << (m_current && m_current->isPlayable ()) << " elm==linkNode:" << (m_current && elm == m_current->mrl ()->linkNode ()) << " p state:" << m_player->process ()->state () << endl;
if (ns == Node::state_deactivated && elm == m_document && !m_back_request) {
emit endOfPlayItems (); // played all items
} else if ((ns == Node::state_deactivated || ns == Node::state_finished) &&
m_player->process ()->mrl() &&
elm == m_player->process ()->mrl ()->mrl ()->linkNode ()) {
if (m_player->process ()->state () > Process::Ready)
//a SMIL movies stopped by SMIL events rather than movie just ending
m_player->process ()->stop ();
if (m_player->view ()) // move away the video widget
TQTimer::singleShot (0, m_player->view (), TQT_SLOT (updateLayout ()));
} else if ((ns == Node::state_deferred ||
(os == Node::state_deferred && ns > Node::state_deferred)) &&
elm == m_document) {
m_player->process ()->pause ();
} else if (ns == Node::state_activated &&
elm->isPlayable () &&
elm->mrl ()->view_mode == Mrl::SingleMode) {
Node *p = elm->parentNode();
if (!p || !p->mrl () || p->mrl ()->view_mode == Mrl::SingleMode)
// make sure we don't set current to nested document
m_current = elm;
}
if (elm->expose ()) {
if (ns == Node::state_activated || ns == Node::state_deactivated)
m_player->updateTree ();
else if (ns == Node::state_began || os == Node::state_began)
m_player->updateTree (false);
}
}
SurfacePtr Source::getSurface (NodePtr n) {
if (m_player->view ())
return static_cast <View*>(m_player->view())->viewArea()->getSurface(n);
return 0L;
}
void Source::setInfoMessage (const TQString & msg) {
m_player->updateInfo (msg);
}
void Source::bitRates (int & preferred, int & maximal) {
preferred = 1024 * m_player->settings ()->prefbitrate;
maximal= 1024 * m_player->settings ()->maxbitrate;
}
void Source::insertURL (NodePtr node, const TQString & mrl, const TQString & title) {
if (!node || !node->mrl ()) // this should always be false
return;
TQString cur_url = node->mrl ()->absolutePath ();
KURL url (cur_url, mrl);
kdDebug() << "Source::insertURL " << KURL (cur_url) << " " << url << endl;
if (!url.isValid ())
kdError () << "try to append non-valid url" << endl;
else if (KURL (cur_url) == url)
kdError () << "try to append url to itself" << endl;
else {
int depth = 0; // cache this?
for (NodePtr e = node; e->parentNode (); e = e->parentNode ())
++depth;
if (depth < 40) {
node->appendChild (new GenericURL (m_document, KURL::decode_string (url.url ()), title.isEmpty() ? KURL::decode_string (mrl) : title));
m_player->updateTree ();
} else
kdError () << "insertURL exceeds depth limit" << endl;
}
}
void Source::play () {
m_player->updateTree ();
TQTimer::singleShot (0, m_player, TQT_SLOT (play ()));
//printTree (m_document);
}
void Source::backward () {
if (m_document->hasChildNodes ()) {
m_back_request = m_current;
if (!m_back_request || m_back_request == m_document) {
m_back_request = m_document->lastChild ();
while (m_back_request->lastChild () && !m_back_request->isPlayable ())
m_back_request = m_back_request->lastChild ();
if (m_back_request->isPlayable ())
return;
}
while (m_back_request && m_back_request != m_document) {
if (m_back_request->previousSibling ()) {
m_back_request = m_back_request->previousSibling ();
NodePtr e = findDepthFirst (m_back_request); // lastDepth..
if (e) {
m_back_request = e;
if (m_player->playing ())
m_player->process ()->stop ();
else if (m_current) {
m_document->reset ();
m_current = e;
TQTimer::singleShot (0, this, TQT_SLOT (playCurrent ()));
}
return;
}
} else
m_back_request = m_back_request->parentNode ();
}
m_back_request = 0L;
} else
m_player->process ()->seek (-1 * m_player->settings ()->seektime * 10, false);
}
void Source::forward () {
if (m_document->hasChildNodes ()) {
if (m_player->playing ())
m_player->process ()->stop ();
else if (m_current)
m_current->finish ();
} else
m_player->process ()->seek (m_player->settings()->seektime * 10, false);
}
void Source::jump (NodePtr e) {
if (e->isPlayable ()) {
if (m_player->playing ()) {
m_back_request = e;
m_player->process ()->stop ();
} else {
if (m_current)
m_document->reset ();
m_current = e;
TQTimer::singleShot (0, this, TQT_SLOT (playCurrent ()));
}
} else
m_player->updateTree ();
}
NodePtr Source::document () {
if (!m_document)
m_document = new Document (TQString (), this);
return m_document;
}
NodePtr Source::root () {
return document ();
}
bool Source::processOutput (const TQString &) {
return false;
}
TQString Source::filterOptions () {
Settings* m_settings = m_player->settings ();
TQString PPargs ("");
if (m_settings->postprocessing)
{
if (m_settings->pp_default)
PPargs = "-vf pp=de";
else if (m_settings->pp_fast)
PPargs = "-vf pp=fa";
else if (m_settings->pp_custom) {
PPargs = "-vf pp=";
if (m_settings->pp_custom_hz) {
PPargs += "hb";
if (m_settings->pp_custom_hz_aq && \
m_settings->pp_custom_hz_ch)
PPargs += ":ac";
else if (m_settings->pp_custom_hz_aq)
PPargs += ":a";
else if (m_settings->pp_custom_hz_ch)
PPargs += ":c";
PPargs += '/';
}
if (m_settings->pp_custom_vt) {
PPargs += "vb";
if (m_settings->pp_custom_vt_aq && \
m_settings->pp_custom_vt_ch)
PPargs += ":ac";
else if (m_settings->pp_custom_vt_aq)
PPargs += ":a";
else if (m_settings->pp_custom_vt_ch)
PPargs += ":c";
PPargs += '/';
}
if (m_settings->pp_custom_dr) {
PPargs += "dr";
if (m_settings->pp_custom_dr_aq && \
m_settings->pp_custom_dr_ch)
PPargs += ":ac";
else if (m_settings->pp_custom_dr_aq)
PPargs += ":a";
else if (m_settings->pp_custom_dr_ch)
PPargs += ":c";
PPargs += '/';
}
if (m_settings->pp_custom_al) {
PPargs += "al";
if (m_settings->pp_custom_al_f)
PPargs += ":f";
PPargs += '/';
}
if (m_settings->pp_custom_tn) {
PPargs += "tn";
/*if (1 <= m_settings->pp_custom_tn_s <= 3){
PPargs += ":";
PPargs += m_settings->pp_custom_tn_s;
}*/ //disabled 'cos this is wrong
PPargs += '/';
}
if (m_settings->pp_lin_blend_int) {
PPargs += "lb";
PPargs += '/';
}
if (m_settings->pp_lin_int) {
PPargs += "li";
PPargs += '/';
}
if (m_settings->pp_cub_int) {
PPargs += "ci";
PPargs += '/';
}
if (m_settings->pp_med_int) {
PPargs += "md";
PPargs += '/';
}
if (m_settings->pp_ffmpeg_int) {
PPargs += "fd";
PPargs += '/';
}
}
if (PPargs.endsWith("/"))
PPargs.truncate(PPargs.length()-1);
}
return PPargs;
}
bool Source::hasLength () {
return true;
}
bool Source::isSeekable () {
return true;
}
void Source::setIdentified (bool b) {
//kdDebug () << "Source::setIdentified " << m_identified << b <<endl;
m_identified = b;
}
static const TQString statemap [] = {
i18n ("Not Running"), i18n ("Ready"), i18n ("Buffering"), i18n ("Playing")
};
void Source::stateChange(Process *p, Process::State olds, Process::State news) {
if (!p || !p->viewer ()) return;
Recorder *rec = dynamic_cast <Recorder *> (p);
if (rec && !rec->recordURL ().isEmpty ()) {
kdDebug () << "recordState " << statemap[olds] << " -> " << statemap[news] << endl;
m_player->updateStatus (i18n ("Recorder %1 %2").arg (p->name ()).arg (statemap[news]));
p->viewer ()->view ()->controlPanel ()->setRecording (news > Process::Ready);
if (news == Process::Ready) {
if (olds > Process::Ready) {
p->quit ();
} else {
NodePtr n = current ();
if (!n)
n = document ();
p->play (this, n);
}
} else if (news > Process::Ready) {
emit startRecording ();
} else if (news == Process::NotRunning)
emit stopRecording ();
} else {
p->viewer()->view()->controlPanel()->setPlaying(news > Process::Ready);
kdDebug () << "processState " << statemap[olds] << " -> " << statemap[news] << endl;
m_player->updateStatus (i18n ("Player %1 %2").arg (p->name ()).arg (statemap[news]));
if (!p->mrl () && news > Process::Ready) {
p->stop (); // reschedule for Ready state
} else if (news == Process::Playing) {
if (p->mrl ()->state == Element::state_deferred)
p->mrl ()->undefer ();
p->viewer ()->view ()->playingStart ();
emit startPlaying ();
} else if (news == Process::NotRunning) {
if (hasLength () && position () > length ())
setLength (m_document, position ());
setPosition (0);
if (p == m_player->process ())
emit stopPlaying ();
// else changed process
} else if (news == Process::Ready) {
if (olds > Process::Ready) {
NodePtr node = p->mrl (); // p->mrl is weak, needs check
Mrl * mrl = node ? node->mrl () : 0L;
if (m_back_request && m_back_request->isPlayable ()) {
if (m_back_request->mrl ()->view_mode == Mrl::SingleMode)
// jump in pl
m_current = m_back_request;
else if (mrl)
// overlapping SMIL audio/video
mrl->endOfFile ();
if (m_current->id >= SMIL::id_node_first &&
m_current->id < SMIL::id_node_last) {
playCurrent (); // just play back_request
} else {
// sanitize pl having all parents of current activated
m_document->reset (); // deactivate everything
for (NodePtr p = m_current->parentNode(); p; p = p->parentNode())
p->state = Element::state_activated;
m_current->activate (); // calls requestPlayUrl
}
m_back_request = 0L;
} else if(mrl)
{
mrl->endOfFile (); // set node to finished
}
if (m_player->view() &&
(!mrl || mrl->view_mode != Mrl::WindowMode))
static_cast<View*>(m_player->view())->viewArea()->repaint();
} else
TQTimer::singleShot (0, this, TQT_SLOT (playCurrent ()));
} else if (news == Process::Buffering) {
if (p->mrl ()->mrl ()->view_mode != Mrl::SingleMode)
p->mrl ()->defer (); // paused the SMIL
}
}
}
TQString Source::plugin (const TQString &mime) const {
m_player->config ()->setGroup (mime);
return m_player->config ()->readEntry ("plugin", "" );
}
TQString Source::prettyName () {
return i18n ("Unknown");
}
//-----------------------------------------------------------------------------
URLSource::URLSource (PartBase * player, const KURL & url)
: Source (i18n ("URL"), player, "urlsource"), activated (false) {
setURL (url);
//kdDebug () << "URLSource::URLSource" << endl;
}
URLSource::~URLSource () {
//kdDebug () << "URLSource::~URLSource" << endl;
}
void URLSource::init () {
Source::init ();
}
void URLSource::dimensions (int & w, int & h) {
if (!m_player->mayResize () && m_player->view ()) {
w = static_cast <View *> (m_player->view ())->viewer ()->width ();
h = static_cast <View *> (m_player->view ())->viewer ()->height ();
} else
Source::dimensions (w, h);
}
bool URLSource::hasLength () {
return !!length ();
}
KDE_NO_EXPORT void URLSource::activate () {
if (activated)
return;
activated = true;
if (url ().isEmpty () && (!m_document || !m_document->hasChildNodes ())) {
m_player->updateTree ();
return;
}
if (m_auto_play)
play ();
}
KDE_NO_EXPORT void URLSource::stopResolving () {
if (m_resolve_info) {
for (SharedPtr <ResolveInfo> ri = m_resolve_info; ri; ri = ri->next)
ri->job->kill ();
m_resolve_info = 0L;
m_player->updateStatus (i18n ("Disconnected"));
m_player->setLoaded (100);
}
}
void URLSource::reset () {
stopResolving ();
Source::reset ();
}
void URLSource::forward () {
stopResolving ();
Source::forward ();
}
void URLSource::backward () {
stopResolving ();
Source::backward ();
}
void URLSource::jump (NodePtr e) {
stopResolving ();
Source::jump (e);
}
void URLSource::deactivate () {
activated = false;
reset ();
getSurface (0L);
}
TQString URLSource::prettyName () {
if (m_url.isEmpty ())
return i18n ("URL");
if (m_url.url ().length () > 50) {
TQString newurl = m_url.protocol () + TQString ("://");
if (m_url.hasHost ())
newurl += m_url.host ();
if (m_url.port ())
newurl += TQString (":%1").arg (m_url.port ());
TQString file = m_url.fileName ();
int len = newurl.length () + file.length ();
KURL path = KURL (m_url.directory ());
bool modified = false;
while (path.url ().length () + len > 50 && path != path.upURL ()) {
path = path.upURL ();
modified = true;
}
TQString dir = path.directory ();
if (!dir.endsWith (TQString ("/")))
dir += '/';
if (modified)
dir += TQString (".../");
newurl += dir + file;
return i18n ("URL - %1").arg (newurl);
}
return i18n ("URL - %1").arg (m_url.prettyURL ());
}
static bool isPlayListMime (const TQString & mime) {
TQString m (mime);
int plugin_pos = m.find ("-plugin");
if (plugin_pos > 0)
m.truncate (plugin_pos);
const char * mimestr = m.ascii ();
return mimestr && (!strcmp (mimestr, "audio/mpegurl") ||
!strcmp (mimestr, "audio/x-mpegurl") ||
!strncmp (mimestr, "video/x-ms", 10) ||
!strncmp (mimestr, "audio/x-ms", 10) ||
//!strcmp (mimestr, "video/x-ms-wmp") ||
//!strcmp (mimestr, "video/x-ms-asf") ||
//!strcmp (mimestr, "video/x-ms-wmv") ||
//!strcmp (mimestr, "video/x-ms-wvx") ||
//!strcmp (mimestr, "video/x-msvideo") ||
!strcmp (mimestr, "audio/x-scpls") ||
!strcmp (mimestr, "audio/x-pn-realaudio") ||
!strcmp (mimestr, "audio/vnd.rn-realaudio") ||
!strcmp (mimestr, "audio/m3u") ||
!strcmp (mimestr, "audio/x-m3u") ||
!strncmp (mimestr, "text/", 5) ||
(!strncmp (mimestr, "application/", 12) &&
strstr (mimestr + 12,"+xml")) ||
!strncasecmp (mimestr, "application/smil", 16) ||
!strncasecmp (mimestr, "application/xml", 15) ||
//!strcmp (mimestr, "application/rss+xml") ||
//!strcmp (mimestr, "application/atom+xml") ||
!strcmp (mimestr, "application/x-mplayer2"));
}
KDE_NO_EXPORT void URLSource::read (NodePtr root, TQTextStream & textstream) {
TQString line;
do {
line = textstream.readLine ();
} while (!line.isNull () && line.stripWhiteSpace ().isEmpty ());
if (!line.isNull ()) {
NodePtr cur_elm = root;
if (cur_elm->isPlayable ())
cur_elm = cur_elm->mrl ()->linkNode ();
if (cur_elm->mrl ()->mimetype == TQString ("audio/x-scpls")) {
bool groupfound = false;
int nr = -1;
struct Entry {
TQString url, title;
} * entries = 0L;
do {
line = line.stripWhiteSpace ();
if (!line.isEmpty ()) {
if (line.startsWith (TQString ("[")) && line.endsWith (TQString ("]"))) {
if (!groupfound && line.mid (1, line.length () - 2).stripWhiteSpace () == TQString ("playlist"))
groupfound = true;
else
break;
kdDebug () << "Group found: " << line << endl;
} else if (groupfound) {
int eq_pos = line.find (TQChar ('='));
if (eq_pos > 0) {
if (line.lower ().startsWith (TQString ("numberofentries"))) {
nr = line.mid (eq_pos + 1).stripWhiteSpace ().toInt ();
kdDebug () << "numberofentries : " << nr << endl;
if (nr > 0 && nr < 1024)
entries = new Entry[nr];
else
nr = 0;
} else if (nr > 0) {
TQString ll = line.lower ();
if (ll.startsWith (TQString ("file"))) {
int i = line.mid (4, eq_pos-4).toInt ();
if (i > 0 && i <= nr)
entries[i-1].url = line.mid (eq_pos + 1).stripWhiteSpace ();
} else if (ll.startsWith (TQString ("title"))) {
int i = line.mid (5, eq_pos-5).toInt ();
if (i > 0 && i <= nr)
entries[i-1].title = line.mid (eq_pos + 1).stripWhiteSpace ();
}
}
}
}
}
line = textstream.readLine ();
} while (!line.isNull ());
for (int i = 0; i < nr; i++)
if (!entries[i].url.isEmpty ())
cur_elm->appendChild (new GenericURL (m_document, KURL::decode_string (entries[i].url), entries[i].title));
delete [] entries;
} else if (line.stripWhiteSpace ().startsWith (TQChar ('<'))) {
readXML (cur_elm, textstream, line);
//cur_elm->normalize ();
if (m_document && m_document->firstChild ()) {
// SMIL documents have set its size of root-layout
Mrl * mrl = m_document->firstChild ()->mrl ();
if (mrl)
Source::setDimensions (m_document->firstChild (), mrl->width, mrl->height);
}
} else if (line.lower () != TQString ("[reference]")) do {
TQString mrl = line.stripWhiteSpace ();
if (line == TQString ("--stop--"))
break;
if (mrl.lower ().startsWith (TQString ("asf ")))
mrl = mrl.mid (4).stripWhiteSpace ();
if (!mrl.isEmpty () && !mrl.startsWith (TQChar ('#')))
cur_elm->appendChild (new GenericURL (m_document, mrl));
line = textstream.readLine ();
} while (!line.isNull ()); /* TODO && m_document.size () < 1024 / * support 1k entries * /);*/
}
}
KDE_NO_EXPORT void URLSource::kioData (TDEIO::Job * job, const TQByteArray & d) {
SharedPtr <ResolveInfo> rinfo = m_resolve_info;
while (rinfo && rinfo->job != job)
rinfo = rinfo->next;
if (!rinfo) {
kdWarning () << "Spurious kioData" << endl;
return;
}
int size = rinfo->data.size ();
int newsize = size + d.size ();
if (!size) { // first data
int accuraty = 0;
KMimeType::Ptr mime = KMimeType::findByContent (d, &accuraty);
if (!mime ||
!mime->name ().startsWith (TQString ("text/")) ||
(newsize > 4 && !strncmp (d.data (), "RIFF", 4))) {
newsize = 0;
kdDebug () << "URLSource::kioData: " << mime->name () << accuraty << endl;
}
}
//kdDebug () << "URLSource::kioData: " << newsize << endl;
if (newsize <= 0 || newsize > 200000) {
rinfo->data.resize (0);
rinfo->job->kill (false);
m_player->setLoaded (100);
} else {
rinfo->data.resize (newsize);
memcpy (rinfo->data.data () + size, d.data (), newsize - size);
m_player->setLoaded (++rinfo->progress);
}
}
KDE_NO_EXPORT void URLSource::kioMimetype (TDEIO::Job * job, const TQString & mimestr) {
SharedPtr <ResolveInfo> rinfo = m_resolve_info;
while (rinfo && rinfo->job != job)
rinfo = rinfo->next;
if (!rinfo) {
kdWarning () << "Spurious kioData" << endl;
return;
}
if (rinfo->resolving_mrl)
rinfo->resolving_mrl->mrl ()->mimetype = mimestr;
if (!rinfo->resolving_mrl || !isPlayListMime (mimestr))
job->kill (false);
}
KDE_NO_EXPORT void URLSource::kioResult (TDEIO::Job * job) {
SharedPtr <ResolveInfo> previnfo, rinfo = m_resolve_info;
while (rinfo && rinfo->job != job) {
previnfo = rinfo;
rinfo = rinfo->next;
}
if (!rinfo) {
kdWarning () << "Spurious kioData" << endl;
return;
}
m_player->updateStatus ("");
m_player->setLoaded (100);
if (previnfo)
previnfo->next = rinfo->next;
else
m_resolve_info = rinfo->next;
TQTextStream textstream (rinfo->data, IO_ReadOnly);
if (rinfo->resolving_mrl) {
if (isPlayListMime (rinfo->resolving_mrl->mrl ()->mimetype))
read (rinfo->resolving_mrl, textstream);
rinfo->resolving_mrl->mrl ()->resolved = true;
rinfo->resolving_mrl->undefer ();
}
static_cast <View *> (m_player->view())->controlPanel()->setPlaying (false);
}
void URLSource::playCurrent () {
Mrl *mrl = m_back_request
? m_back_request->mrl ()
: m_current ? m_current->mrl () : NULL;
if (mrl && mrl->active () && (!mrl->isPlayable () || !mrl->resolved))
// an async playCurrent() call (eg. backend is up & running), ignore
return;
Source::playCurrent ();
}
void URLSource::play () {
Source::play ();
}
bool URLSource::requestPlayURL (NodePtr mrl) {
if (m_document.ptr () != mrl->mrl ()->linkNode ()) {
KURL base = m_document->mrl ()->src;
KURL dest = mrl->mrl ()->linkNode ()->absolutePath ();
// check if some remote playlist tries to open something local, but
// do ignore unknown protocols because there are so many and we only
// want to cache local ones.
if (
#if 0
!KProtocolInfo::protocolClass (dest.protocol ()).isEmpty () &&
#else
dest.isLocalFile () &&
#endif
!kapp->authorizeURLAction ("redirect", base, dest)) {
kdWarning () << "requestPlayURL from document " << base << " to play " << dest << " is not allowed" << endl;
return false;
}
}
return Source::requestPlayURL (mrl);
}
void URLSource::setURL (const KURL & url) {
Source::setURL (url);
Mrl *mrl = document ()->mrl ();
if (!url.isEmpty () && url.isLocalFile () && mrl->mimetype.isEmpty ()) {
KMimeType::Ptr mimeptr = KMimeType::findByURL (url);
if (mimeptr)
mrl->mimetype = mimeptr->name ();
}
}
bool URLSource::resolveURL (NodePtr m) {
Mrl * mrl = m->mrl ();
if (!mrl || mrl->src.isEmpty ())
return true;
int depth = 0;
for (NodePtr e = m->parentNode (); e; e = e->parentNode ())
++depth;
if (depth > 40)
return true;
KURL url (mrl->absolutePath ());
TQString mimestr = mrl->mimetype;
if (mimestr == "application/x-shockwave-flash" ||
mimestr == "application/futuresplash")
return true; // FIXME
bool maybe_playlist = isPlayListMime (mimestr);
kdDebug () << "resolveURL " << mrl->absolutePath () << " " << mimestr << endl;
if (url.isLocalFile ()) {
TQFile file (url.path ());
if (!file.exists ()) {
kdWarning () << "resolveURL " << url.path() << " not found" << endl;
return true;
}
if (mimestr.isEmpty ()) {
KMimeType::Ptr mimeptr = KMimeType::findByURL (url);
if (mimeptr) {
mrl->mimetype = mimeptr->name ();
maybe_playlist = isPlayListMime (mrl->mimetype); // get new mime
}
}
if (maybe_playlist && file.size () < 2000000 && file.open (IO_ReadOnly)) {
char databuf [512];
int nr_bytes = file.readBlock (databuf, 512);
if (nr_bytes > 3) {
int accuraty = 0;
KMimeType::Ptr mime = KMimeType::findByContent (TQCString (databuf, nr_bytes), &accuraty);
if ((mime && !mime->name().startsWith (TQString("text/"))) ||
!strncmp (databuf, "RIFF", 4)) {
return true;
}
kdDebug () << "mime: " << (mime ? mime->name (): TQString("null")) << endl;
}
file.reset ();
TQTextStream textstream (&file);
read (m, textstream);
}
} else if ((maybe_playlist &&
url.protocol ().compare (TQString ("mms")) &&
url.protocol ().compare (TQString ("rtsp")) &&
url.protocol ().compare (TQString ("rtp"))) ||
(mimestr.isEmpty () &&
(url.protocol ().startsWith (TQString ("http")) ||
url.protocol () == TQString::fromLatin1 ("media") ||
url.protocol () == TQString::fromLatin1 ("remote")))) {
TDEIO::Job * job = TDEIO::get (url, false, false);
job->addMetaData ("PropagateHttpHeader", "true");
job->addMetaData ("errorPage", "false");
m_resolve_info = new ResolveInfo (m, job, m_resolve_info);
connect (m_resolve_info->job, TQT_SIGNAL(data(TDEIO::Job*,const TQByteArray&)),
this, TQT_SLOT (kioData (TDEIO::Job *, const TQByteArray &)));
//connect( m_job, TQT_SIGNAL(connected(TDEIO::Job*)),
// this, TQT_SLOT(slotConnected(TDEIO::Job*)));
connect(m_resolve_info->job, TQT_SIGNAL(mimetype(TDEIO::Job*,const TQString&)),
this, TQT_SLOT (kioMimetype (TDEIO::Job *, const TQString &)));
connect (m_resolve_info->job, TQT_SIGNAL (result (TDEIO::Job *)),
this, TQT_SLOT (kioResult (TDEIO::Job *)));
static_cast <View *> (m_player->view ())->controlPanel ()->setPlaying (true);
m_player->updateStatus (i18n ("Connecting"));
m_player->setLoaded (0);
return false; // wait for result ..
}
return true;
}
//-----------------------------------------------------------------------------
namespace KMPlayer {
static KStaticDeleter <DataCache> dataCacheDeleter;
static DataCache * memory_cache;
}
void DataCache::add (const TQString & url, const TQByteArray & data) {
TQByteArray bytes;
bytes.duplicate (data);
cache_map.insert (url, bytes);
preserve_map.erase (url);
emit preserveRemoved (url);
}
bool DataCache::get (const TQString & url, TQByteArray & data) {
DataMap::const_iterator it = cache_map.find (url);
if (it != cache_map.end ()) {
data.duplicate (it.data ());
return true;
}
return false;
}
bool DataCache::preserve (const TQString & url) {
PreserveMap::const_iterator it = preserve_map.find (url);
if (it == preserve_map.end ()) {
preserve_map.insert (url, true);
return true;
}
return false;
}
bool DataCache::isPreserved (const TQString & url) {
return preserve_map.find (url) != preserve_map.end ();
}
bool DataCache::unpreserve (const TQString & url) {
const PreserveMap::iterator it = preserve_map.find (url);
if (it == preserve_map.end ())
return false;
preserve_map.erase (it);
emit preserveRemoved (url);
return true;
}
RemoteObjectPrivate::RemoteObjectPrivate (RemoteObject * r)
: job (0L), remote_object (r), preserve_wait (false) {
if (!memory_cache)
dataCacheDeleter.setObject (memory_cache, new DataCache);
}
RemoteObjectPrivate::~RemoteObjectPrivate () {
clear ();
}
KDE_NO_EXPORT bool RemoteObjectPrivate::download (const TQString & str) {
url = str;
KURL kurl (str);
if (kurl.isLocalFile ()) {
TQFile file (kurl.path ());
if (file.exists () && file.open (IO_ReadOnly)) {
data = file.readAll ();
file.close ();
}
remote_object->remoteReady (data);
return true;
}
if (memory_cache->get (str, data)) {
//kdDebug () << "download found in cache " << str << endl;
remote_object->remoteReady (data);
return true;
}
if (memory_cache->preserve (str)) {
//kdDebug () << "downloading " << str << endl;
job = TDEIO::get (kurl, false, false);
connect (job, TQT_SIGNAL (data (TDEIO::Job *, const TQByteArray &)),
this, TQT_SLOT (slotData (TDEIO::Job *, const TQByteArray &)));
connect (job, TQT_SIGNAL (result (TDEIO::Job *)),
this, TQT_SLOT (slotResult (TDEIO::Job *)));
connect (job, TQT_SIGNAL (mimetype (TDEIO::Job *, const TQString &)),
this, TQT_SLOT (slotMimetype (TDEIO::Job *, const TQString &)));
} else {
//kdDebug () << "download preserved " << str << endl;
connect (memory_cache, TQT_SIGNAL (preserveRemoved (const TQString &)),
this, TQT_SLOT (cachePreserveRemoved (const TQString &)));
preserve_wait = true;
}
return false;
}
KDE_NO_EXPORT void RemoteObjectPrivate::clear () {
if (job) {
job->kill (); // quiet, no result signal
job = 0L;
memory_cache->unpreserve (url);
} else if (preserve_wait) {
disconnect (memory_cache, TQT_SIGNAL (preserveRemoved (const TQString &)),
this, TQT_SLOT (cachePreserveRemoved (const TQString &)));
preserve_wait = false;
}
}
KDE_NO_EXPORT void RemoteObjectPrivate::slotResult (TDEIO::Job * kjob) {
if (!kjob->error ())
memory_cache->add (url, data);
else
data.resize (0);
job = 0L; // signal TDEIO::Job::result deletes itself
remote_object->remoteReady (data);
}
KDE_NO_EXPORT
void RemoteObjectPrivate::cachePreserveRemoved (const TQString & str) {
if (str == url && !memory_cache->isPreserved (str)) {
preserve_wait = false;
disconnect (memory_cache, TQT_SIGNAL (preserveRemoved (const TQString &)),
this, TQT_SLOT (cachePreserveRemoved (const TQString &)));
download (str);
}
}
KDE_NO_EXPORT
void RemoteObjectPrivate::slotData (TDEIO::Job*, const TQByteArray& qb) {
if (qb.size ()) {
int old_size = data.size ();
data.resize (old_size + qb.size ());
memcpy (data.data () + old_size, qb.data (), qb.size ());
}
}
KDE_NO_EXPORT
void RemoteObjectPrivate::slotMimetype (TDEIO::Job *, const TQString & m) {
mime = m;
}
KDE_NO_CDTOR_EXPORT RemoteObject::RemoteObject ()
: d (new RemoteObjectPrivate (this)) {}
KDE_NO_CDTOR_EXPORT RemoteObject::~RemoteObject () {
delete d;
}
/**
* abort previous wget job
*/
KDE_NO_EXPORT void RemoteObject::killWGet () {
d->clear (); // assume data is invalid
}
/**
* Gets contents from url and puts it in m_data
*/
KDE_NO_EXPORT bool RemoteObject::wget (const TQString & url) {
clear ();
return d->download (url);
}
KDE_NO_EXPORT TQString RemoteObject::mimetype () {
if (d->data.size () > 0 && d->mime.isEmpty ()) {
int accuraty;
KMimeType::Ptr mime = KMimeType::findByContent (d->data, &accuraty);
if (mime)
d->mime = mime->name ();
}
return d->mime;
}
KDE_NO_EXPORT void RemoteObject::clear () {
killWGet ();
d->url.truncate (0);
d->mime.truncate (0);
d->data.resize (0);
}
KDE_NO_EXPORT bool RemoteObject::downloading () const {
return !!d->job;
}
//-----------------------------------------------------------------------------
#include "kmplayerpartbase.moc"
#include "kmplayersource.moc"