/**
 * 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 <tdemessagebox.h>
#include <tdeaboutdata.h>
#include <kdebug.h>
#include <kbookmarkmenu.h>
#include <kbookmarkmanager.h>
#include <tdeconfig.h>
#include <ksimpleconfig.h>
#include <tdeaction.h>
#include <kprocess.h>
#include <kstandarddirs.h>
#include <kmimetype.h>
#include <kprotocolinfo.h>
#include <tdeapplication.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() << "[01;31mSource::stateElementChanged[00m " << 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"
