Logo Search packages:      
Sourcecode: kaffeine version File versions

playlist.cpp

/***************************************************************************
                           playlist.cpp  
                           -------------------
    begin                : Die Apr 22 2003
    revision             : $Revision: 1.81 $
    last modified        : $Date: 2005/02/24 19:01:09 $ by $Author: gillata $
    copyright            : (C) 2003-2005 by J├╝rgen Kofler
    email                : kaffeine@gmx.net
 ***************************************************************************/

/***************************************************************************
 *                                                                         *
 *   This program is free software; you can redistribute it and/or modify  *
 *   it under the terms of the GNU General Public License as published by  *
 *   the Free Software Foundation; either version 2 of the License, or     *
 *   (at your option) any later version.                                   *
 *                                                                         *
 ***************************************************************************/



#include <krandomsequence.h>
#include <kapplication.h>
#include <kglobalsettings.h>
#include <kiconloader.h>
#include <klocale.h>
#include <kfiledialog.h>
#include <kmessagebox.h>
#include <kmimetype.h>
#include <kstandarddirs.h>
#include <kmessagebox.h>
#include <kio/netaccess.h>
#include <kprogress.h>
#include <kfilemetainfo.h>
#include <klineedit.h>
#include <kcombobox.h>
#include <kpushbutton.h>
#include <kdebug.h>
#include <kurl.h>
#include <kaccel.h>

#include <qlabel.h>
#include <qclipboard.h>
#include <qdragobject.h>
#include <qstringlist.h>
#include <qdom.h>
#include <qxml.h>
#include <qregexp.h>
#include <qheader.h>
#include <qlayout.h>
#include <qpixmap.h>
#include <qvbox.h>
#include <qlistbox.h>
#include <qtextcodec.h>
#include <qtooltip.h>

#include "mrl.h"
#include "playlistimport.h"
#include "playlistitem.h"
#include "playlist.h"
#include "playlist.moc"

#define SPECIAL_PLAYLISTS "DVD;VCD;AudioCD"

PlayList::PlayList(KComboBox* playlistSelector, QWidget* parent, const char *name) : QWidget(parent , name),
  m_playlistSelector(playlistSelector), m_playTime(0), m_playTimeVisible(0), m_countVisible(0), m_searchSelection(false), 
  m_metaOnLoading(true), m_sortAscending(true), m_currentEntry(NULL), m_currentRandomListEntry(-1),
  m_endless(false), m_random(false), m_useAlternateEncoding(false), m_alternateEncoding("ISO 8859-1")
{
 
  m_playlistDirectory = locateLocal("appdata", "playlists");
  m_playlistDirectory.append("/");
  KIO::NetAccess::mkdir(m_playlistDirectory, this);
   
  QGridLayout* layout = new QGridLayout(this, 2, 5, 5);

  m_list = new UrlListView(this);
  setAcceptDrops(true);
  m_list->setHScrollBarMode(KListView::AlwaysOff); 
  m_list->setItemMargin(1);
  m_list->setMargin(2);
  m_list->setSelectionMode(QListView::Extended);
  m_list->addColumn("");
  m_list->addColumn(i18n("Title"));
  m_list->addColumn(i18n("Artist"));
  m_list->addColumn(i18n("Album"));
  m_list->addColumn(i18n("Track"));
  m_list->addColumn(i18n("Length"));
  m_list->setShowToolTips(true);
  m_list->setColumnWidthMode(MIME_COLUMN, QListView::Manual);
  m_list->setColumnWidthMode(TITLE_COLUMN, QListView::Manual);
  m_list->setColumnWidthMode(ARTIST_COLUMN, QListView::Manual);
  m_list->setColumnWidthMode(ALBUM_COLUMN, QListView::Manual);
  m_list->setColumnWidthMode(TRACK_COLUMN, QListView::Manual);
  m_list->setColumnWidthMode(LENGTH_COLUMN, QListView::Manual);
  m_list->setResizeMode(QListView::NoColumn);
  m_list->setDragEnabled(true);
  m_list->setAcceptDrops(true);
  m_list->setDropVisualizer(true);
  m_list->setItemsMovable(true);
  m_list->setSorting(-1);
  m_list->setItemsRenameable(true);
  m_list->setRenameable(TITLE_COLUMN);
  m_list->setRenameable(ARTIST_COLUMN);
  m_list->setRenameable(ALBUM_COLUMN);
  m_list->setRenameable(TRACK_COLUMN);
  m_list->setAllColumnsShowFocus(true);
  layout->addMultiCellWidget(m_list, 1, 1, 0, 4);

  m_playlistSelector->setMinimumWidth(160);
  m_playlistSelector->setEditable(true);
  m_playlistSelector->setDuplicatesEnabled(false);
  m_playlistSelector->setFocusPolicy(QWidget::ClickFocus);
  connect(m_playlistSelector, SIGNAL(returnPressed(const QString&)), this, SLOT(slotNewPlaylistName(const QString&)));
  connect(m_playlistSelector, SIGNAL(activated(int)),this, SLOT(slotPlaylistActivated(int)));
  
  QLabel* filterLabel = new QLabel(i18n("Filter") + ":", this);
  layout->addWidget(filterLabel, 0, 0);
  m_playlistFilter = new KLineEdit(this);
  m_playlistFilter->setFocusPolicy(QWidget::ClickFocus);
  connect(m_playlistFilter, SIGNAL(textChanged(const QString&)), this, SLOT(slotFindText(const QString&)));
  layout->addWidget(m_playlistFilter, 0, 1);
  QLabel* playlistLabel = new QLabel(i18n("Playlist") + ":", this);
  layout->addWidget(playlistLabel, 0, 2);
  m_playlistName = new QLabel(this);
  layout->addWidget(m_playlistName, 0, 3);
  
  m_undockButton = new KPushButton(i18n("Undock"), this);
  QToolTip::add(m_undockButton, i18n("Undock Playlist Window"));
  layout->addWidget(m_undockButton, 0, 4);
  connect(m_undockButton, SIGNAL(clicked()), this, SLOT(slotUndockClicked()));
  
  KAccel* accel = new KAccel(this);
  accel->insert("Delete selected", Qt::Key_Delete, this, SLOT(slotRemoveSelected()));
 
  connect(m_list, SIGNAL(dropped(QDropEvent*, QListViewItem*)), this, SLOT(slotDropEvent(QDropEvent*, QListViewItem*)));
  connect(m_list, SIGNAL(doubleClicked(QListViewItem*)), this, SLOT(slotPlayDirect(QListViewItem*)));
  connect(m_list, SIGNAL(returnPressed(QListViewItem*)), this, SLOT(slotPlayDirect(QListViewItem*)));
  connect(m_list, SIGNAL(signalCut()), this, SLOT(slotCut()));
  connect(m_list, SIGNAL(signalCopy()), this, SLOT(slotCopy()));
  connect(m_list, SIGNAL(signalPaste()), this, SLOT(slotPaste()));
  connect(m_list, SIGNAL(signalSelectAll()), this, SLOT(slotSelectAll()));
  connect(m_list, SIGNAL(signalPlayItem(QListViewItem*)), this, SLOT(slotPlayDirect(QListViewItem*)));
  connect(m_list, SIGNAL(signalPlaylistFromSelected()), this, SLOT(slotPlaylistFromSelected()));
  connect(m_list, SIGNAL(signalAddToQueue(MRL)), this, SLOT(slotAddToQueue(MRL)));
  connect(m_list->header(), SIGNAL(clicked(int)), this, SLOT(slotSort(int)));

  m_isCurrentEntry = KGlobal::iconLoader()->loadIcon("player_play", KIcon::Small);
  m_cdPixmap = KGlobal::iconLoader()->loadIcon("cdtrack", KIcon::Small);
}

PlayList::~PlayList()
{  
  delete m_list;
}

void PlayList::loadConfig(KConfig* config)
{
  config->setGroup("Playlist");
  QStringList list;
  list = config->readListEntry("Playlists"); 
  if (!list.count())
  {
    list.append(i18n("Playlist") + "1");
  }  
  int lastRegularPlaylist = list.count();
  list.append(i18n("NEW"));
  list.append("DVD");
  list.append("VCD");
  list.append("AudioCD");
  m_playlistSelector->insertStringList(list);
  m_currentPlaylist = config->readNumEntry("Current", 0);
  if (m_currentPlaylist > lastRegularPlaylist)
    m_currentPlaylist = 0;
  m_playlistSelector->setCurrentItem(m_currentPlaylist);
  m_playlistName->setText(m_playlistSelector->currentText());
  add(m_playlistDirectory + m_playlistSelector->text(m_currentPlaylist) + ".kaffeine", NULL);
  m_nextPlaylistNumber = config->readNumEntry("Next Playlist", 2);
  QString currentEntry = config->readEntry("Current Entry", QString());
  if ((!currentEntry.isEmpty()) && (m_list->childCount()))
  {
    QListViewItem* tmp = findByURL(currentEntry);
    if (tmp)
      setCurrentEntry(tmp, false);
  }  
}

void PlayList::saveConfig(KConfig* config)
{
  saveCurrentPlaylist();
  config->setGroup("Playlist");
  QStringList list;
  QString text;
  for (uint i=0; i < m_playlistSelector->listBox()->count(); i++)
  {
    text = m_playlistSelector->text(i);
    if ((!QString(SPECIAL_PLAYLISTS).contains(text)) && (text != i18n("NEW")))
      list.append(text);
  }    
  config->writeEntry("Playlists", list);  
  config->writeEntry("Current", m_currentPlaylist);
  config->writeEntry("Next Playlist", m_nextPlaylistNumber);
  config->writeEntry("Current Entry", m_currentEntryMRL.url());
}

void PlayList::slotUndockClicked()
{
  if (m_undockButton->text() == i18n("Undock"))
  {
    m_undockButton->setText(i18n("Dock"));
    QToolTip::remove(m_undockButton);
    QToolTip::add(m_undockButton, i18n("Dock Playlist Window"));
  }
  else
  {
    m_undockButton->setText(i18n("Undock"));
    QToolTip::remove(m_undockButton);
    QToolTip::add(m_undockButton, i18n("Undock Playlist Window"));
  }
  
  emit signalToggleDockState();
}

void PlayList::closeEvent(QCloseEvent*)
{
  slotUndockClicked();
}  

/******************************************
 *      get the urls from playlist
 ******************************************/

MRL PlayList::getCurrent()
{
  if (isQueueMode())
  {
    m_queue.clear();
    updateStatus();
  }  
  
  if (m_random)
  {
    if (m_currentRandomListEntry == -1) return MRL();
    setCurrentEntry(m_randomList.at(m_currentRandomListEntry));
    return m_currentEntryMRL;
  }  

  if (!m_currentEntry)
    if (m_list->childCount() > 0)
    {
      if (m_list->firstChild()->isVisible())
         setCurrentEntry(m_list->firstChild());
        else
        {  
          if (m_list->firstChild()->itemBelow())
            setCurrentEntry(m_list->firstChild()->itemBelow());
           else
            return MRL();
        }   
    }
    else
     return MRL();

   setCurrentEntry(m_currentEntry);  
   return m_currentEntryMRL;
}

MRL PlayList::getNext()
{
  if (isQueueMode())
  {
    MRL mrl = m_queue.first();
    m_queue.remove(m_queue.begin());
    updateStatus();
    return mrl;
  }
  
  if (!m_currentEntry) return getCurrent();

  if (m_random)
  {
  if ((m_currentRandomListEntry+1) < (int)m_randomList.count())
      m_currentRandomListEntry += 1;
     else
     {
       if (m_endless)
         m_currentRandomListEntry = 0;
        else
         return MRL();
     }
    setCurrentEntry(m_randomList.at(m_currentRandomListEntry));
    return m_currentEntryMRL;     
  }

  QListViewItem* tmpItem;
  tmpItem = m_currentEntry->itemBelow();
  
  if (tmpItem)
  {
    setCurrentEntry(tmpItem);
    return m_currentEntryMRL;
  }
  else
  {
    if (m_endless)
    {
        setCurrentEntry(m_list->firstChild());
        return m_currentEntryMRL;
    }  
    else
      return MRL();
  }  
}

MRL PlayList::getPrevious() 
{
  if (isQueueMode())
  {
    m_queue.clear();
    updateStatus();
  } 
  
  if (!m_currentEntry) return getCurrent();

  if (m_random)
  {
    if (m_currentRandomListEntry > 0)
      m_currentRandomListEntry -= 1;
     else
     {
       if (m_endless)
         m_currentRandomListEntry = m_randomList.count()-1;
        else
         return MRL();
     }
    setCurrentEntry(m_randomList.at(m_currentRandomListEntry));
    return m_currentEntryMRL;
   }

  QListViewItem* tmpItem;
  tmpItem = m_currentEntry->itemAbove();

  if (tmpItem)
  {
    setCurrentEntry(tmpItem);
    return m_currentEntryMRL;
  }
  else
  {
    if (m_endless)
    {
       setCurrentEntry(getLast()); 
       return m_currentEntryMRL;
    }  
    else
      return MRL();
  }  
}

QListViewItem* PlayList::getLast()
{
  return m_list->lastItem();
}

QListViewItem* PlayList::getFirst()
{
  return m_list->firstChild();
}  

QListViewItem* PlayList::findByURL(const QString& url)
{
  QListViewItemIterator it(m_list);
  while (it.current())
  {
    if (dynamic_cast<PlaylistItem*>(*it)->url() == url)
    {
      return (*it);
    }
    ++it;
  }      
  
  /* fallback */
  return getFirst();
}

/********* set current entry (with play icon) ****************/

void PlayList::setCurrentEntry(QListViewItem* item, bool playIcon)
{
  if (m_currentEntry)
    m_currentEntry->setPixmap(TITLE_COLUMN, QPixmap());
  if (playIcon)
    item->setPixmap(TITLE_COLUMN, m_isCurrentEntry);
  m_currentEntry = item;
  if (m_random)
    m_currentRandomListEntry = m_randomList.find(item);
  PlaylistItem* plItem = dynamic_cast<PlaylistItem*>(item);
  m_currentEntryMRL = plItem->toMRL();
  m_list->setCurrentItem(m_currentEntry);

  m_list->ensureVisible(10, m_list->itemPos(m_currentEntry), 10, 30);
}

QListViewItem* PlayList::insertItem(QListViewItem* after, const MRL& m)
{
  PlaylistItem* tmp = NULL;
  MRL mrl(m);
    
  QListViewItemIterator it(m_list);
  while (it.current())
  {
    if (dynamic_cast<PlaylistItem*>(*it)->url() == mrl.url())
    {
      kdDebug() << "PlayList: Source '" << mrl.url() << "' still exists. Skipped." << endl;
      return after;
    }
    ++it;
  }      
  
  if (mrl.mime().isNull())
  {
     KMimeType::Ptr mime = KMimeType::findByURL(mrl.kurl().path());
     mrl.setMime(mime->name());
  }
     
  tmp = new PlaylistItem(m_list, dynamic_cast<KListViewItem *>(after), mrl);
  if (!tmp) return after;
     
  if ((mrl.mime() == "video/dvd") || (mrl.mime() == "video/vcd") || (mrl.mime() == "audio/cd"))
     tmp->setPixmap(MIME_COLUMN, m_cdPixmap);
    else
     tmp->setPixmap(MIME_COLUMN, KMimeType::mimeType(mrl.mime())->pixmap(KIcon::Small));
      
  if (tmp->length().contains(':'))
    m_playTime += timeStringToMs(tmp->length());
   
   if (m_searchSelection)
   {
     QString text = m_playlistFilter->text(); 
     if ((!tmp->title().contains(text, false)) && (!tmp->url().contains(text, false))
        && (!tmp->artist().contains(text, false)) && (!tmp->album().contains(text, false)))
     {
       tmp->setVisible(false);
     }
   }

   if (tmp->isVisible())
   {
     if (tmp->length().contains(':'));
       m_playTimeVisible += timeStringToMs(tmp->length());
     m_countVisible++;  
   }
       
   return tmp;
}
 
void PlayList::add(const QString& url, QListViewItem* after)
{
  add(QStringList(url), after);
}


void PlayList::add(const QStringList& urlList, QListViewItem* after)
{
  QListViewItem* tmp = NULL;
  QStringList urls(urlList);
  
  QString ext;
  QString subtitleURL;
  QStringList subs;
  KURL tmpKURL;
  bool playlist;
  MRL mrl;
  MRL::List mrlList;
  
  KProgressDialog* progress = NULL;
  progress = new KProgressDialog(this, "importprogress", QString::null, i18n("Importing media resources..."));
  progress->progressBar()->setTotalSteps(urls.count());
  progress->show();
  
  kdDebug() << "PlayList: add " << urls.count() << " items to playlist" << endl;
   
  for (uint i = 0; i < urls.count(); i++)
  {  
    mrl = MRL(urls[i]);
    if (mrl.kurl().isLocalFile())
    {
      if (!QFile::exists(mrl.kurl().path()))
        continue;
      mrl.setTitle(mrl.kurl().fileName());
    }
    else
    {
      mrl.setTitle(mrl.url()); 
    }  
   
    //kdDebug() << "PlayList: Check url " << mrl.url() << endl;
     /*********** determine extension and mime type ************/

    ext = mrl.kurl().fileName();
    ext = ext.remove(0 , ext.findRev('.') +1).lower();
    // kdDebug() << "Extension: " << ext << endl;
     
     //kdDebug() << "PlayList: Try to determine mime of: " << mrl.url() << endl;
    KMimeType::Ptr mime = KMimeType::findByURL(mrl.kurl().path()); /* works only with path() (without protocol)
                                                                      e.g. http://www.somafm.com/indipop.pls
                                                                      path: /indipop.pls */
     //kdDebug() << "Mime: " << mime->name() << endl;
     /*** check for kaffeine/noatun/pls/asx/m3u playlist ***/
    mrl.setMime(mime->name());
             
     /* urls from audiocd kio-slave */
    if (mrl.kurl().protocol() == "audiocd")
    {
      QString audioTrack = QString::number(mrl.kurl().fileName().remove(QRegExp("\\D")).left(2).toUInt());
      mrl = MRL(audioTrack.prepend("cdda:/"));
    }   
    
     /******** some special processing for local files! *******/
    if (mrl.kurl().isLocalFile()) 
    {
      /* playlist ? */ 
       if ((mime->name() == "text/plain") || (mime->name() == "text/xml") || (mime->name() == "application/x-kaffeine")
         || (mime->name() == "audio/x-scpls") || (mime->name() == "audio/x-mpegurl" || (mime->name() == "audio/mpegurl")
         || (ext == "asx") || (ext == "asf") || (ext == "wvx") || (ext == "wax"))) /* windows meta files */
       {
         kdDebug() << "PlayList: Check for kaffeine/noatun/m3u/pls/asx playlist\n";
         
         mrlList.clear();
         playlist = false;
         QFile file(mrl.url());
         file.open(IO_ReadOnly);

         QTextStream stream(&file);
         QString firstLine = stream.readLine();
         QString secondLine = stream.readLine();
         file.close();
          
         if (secondLine.contains("kaffeine", false))
         {
            kdDebug() << "PlayList: Try loading kaffeine playlist\n";
            playlist = PlaylistImport::kaffeine(mrl.url(), mrlList);
           if (!playlist)
             continue;
          }
          if (secondLine.contains("noatun", false))
          {
             kdDebug() << "PlayList: Try loading noatun playlist\n";
             playlist = PlaylistImport::noatun(mrl.url(), mrlList);
          }
          if (firstLine.contains("asx", false))
          {
            kdDebug() << "PlayList: Try loading asx playlist\n";
            playlist = PlaylistImport::asx(mrl.url(), mrlList);
          } 
          if (firstLine.contains("[playlist]", false))
          {
            kdDebug() << "PlayList: Try loading pls playlist\n";
            playlist = PlaylistImport::pls(mrl.url(), mrlList);
          } 
          if (ext == "m3u")  //indentify by extension
          {
            kdDebug() << "PlayList: Try loading m3u playlist\n";
            playlist = PlaylistImport::m3u(mrl.url(), mrlList);
          }
          if (playlist)
          { 
          for (MRL::List::ConstIterator it = mrlList.begin(); it != mrlList.end(); it++)
             after = insertItem(after, *it);
         continue;
          }
         
      }
      
       /* check for ram playlist */
      if ( (ext == "ra") || (ext == "rm") || (ext == "ram") || (ext == "lsc") || (ext == "pl") )
      {
        mrlList.clear();
        kdDebug() << "PlayList: Try loading ram playlist\n";
        if (PlaylistImport::ram(mrl, mrlList, parentWidget()))
        {
          for (MRL::List::ConstIterator it = mrlList.begin(); it != mrlList.end(); it++)
             after = insertItem(after, *it);
          continue;
        } 
      }  
       
       /**** a directory ? ****/
      if (mime->name() == "inode/directory")
      {
        kdDebug() << "PlayList: Add Directory: " << mrl.url() << endl;

        QDir d(mrl.url());
        uint x;

        /* subdirs */
        QStringList entryList = d.entryList(QDir::Dirs, QDir::Name);
        for (x = 0; x < entryList.count(); x++)
        {
          if (entryList[x][0] != '.')
          {
            urls.append(mrl.url() + "/" + entryList[x]);
          }
        }

        /* Media files */
        /* Exclude subtitles because they are examined twice : */
        /* Once for movie and once per subtitle - very annoying*/
        QString fileFilter = m_fileFilter;
        fileFilter.remove("*.srt",false);
        fileFilter.remove("*.ssa",false);
        fileFilter.remove("*.txt",false);
        fileFilter.remove("*.asc",false);
        fileFilter.remove("*.smi",false);
        fileFilter.remove("*.sub",false);
        entryList = d.entryList(fileFilter, QDir::Files, QDir::Name );
        for (x = 0; x < entryList.count(); x++)
        {
          urls.append(mrl.url() + "/" + entryList[x]);
        }

        progress->progressBar()->setTotalSteps(urls.count());
        continue; /* dont add directory to playlist */
      }

      /*** append subtitle files to the movie ***/
      if ((ext == "srt") || (ext == "ssa") || (ext == "txt") ||
          (ext == "asc") || (ext == "smi") || (ext == "sub"))
      {
        QStringList movies;
        QString movieURL;
        QListViewItemIterator it(m_list);
        while (it.current())
        {
          if (dynamic_cast<PlaylistItem*>(*it)->mime().contains("video"))
            movies << dynamic_cast<PlaylistItem*>(*it)->url();
          ++it;
        }
        if (movies.count() == 1)
          movieURL = movies[0];
        else if (movies.count() >= 2)
        {
          MovieChooser *mc = new MovieChooser(movies, mrl.url(), this);
          if(mc->exec() == QDialog::Accepted)
            movieURL = mc->getSelection();
          delete mc;
        }
        it = m_list;
        while (it.current())
        {
          if (dynamic_cast<PlaylistItem*>(*it)->url() == movieURL)
          {
            kdDebug() << "PlayList: adding subtitle to movie " << dynamic_cast<PlaylistItem*>(*it)->url() << endl;
            (dynamic_cast<PlaylistItem*>(*it))->addSubtitle(mrl.url());
            break;
          }
          ++it;
        }
        continue;
      }

       /*** get meta tags ****/
      if (m_metaOnLoading)
        getMetaInfo(mrl, mime->name());
       
       /* Get all suported subs in movie dir, clear out
        * those starting with a different name than the movie,
        * prompt the user to select a sub
        */
      if (mime->name().contains("video"))
      {
        kdDebug() << "PlayList: Check for subtitle files" << endl;
        subtitleURL = QString::null;
        QDir *d = new QDir(mrl.url().section('/',0,-2));
        QString filename = mrl.url().section('/',-1,-1);
        subs = QStringList();
       //Do a case insensitive search for subs
        QStringList dirListing = d->entryList(QDir::Files|QDir::NoSymLinks); //cache directory listing
        bool matches = false;
 
        for(QStringList::Iterator it = dirListing.begin(); it != dirListing.end(); ++it )
        {
          if(startsWith(*it, filename.section('.',0,-2), false)) //If filenames (without extensions) match
          {
            if( endsWith(*it, ".srt", false) || endsWith(*it, ".ssa", false) || 
                endsWith(*it, ".txt", false) || endsWith(*it, ".smi", false) || 
                endsWith(*it, ".asc", false) || endsWith(*it, ".sub", false) )
              matches = true;
    
            if(matches)
             subs.append(*it); //This is a subtitle for our movie
             matches = false;
           }
         }
 
         subs.prepend(i18n("(no subtitles)"));
         subs.append(i18n("Other subtitle..."));
         if((subs.count() >= 3))
         {
           SubtitleChooser *sc = new SubtitleChooser(subs, filename, this);
   
THERE:     if(sc->exec() == QDialog::Accepted)
           { 
             if((sc->getSelection() != i18n("(no subtitles)")) && (sc->getSelection() != i18n("Other subtitle...")))
              subtitleURL = d->path()+"/"+sc->getSelection();
             if((sc->getSelection() == i18n("Other subtitle...")))
             {
               subtitleURL = KFileDialog::getOpenURL(d->path(), i18n("*.smi *.srt *.sub *.txt *.ssa *.asc|Subtitle Files\n*.*|All Files"), 0, i18n("Select a subtitle file")).path();
               if(subtitleURL == "")
               {
                 subtitleURL = QString::null;
                 goto THERE;
               }
               else //The user has selected another sub, so add this to the list
               {
                 subs.clear();
                 subs.append(subtitleURL);
               }
             }
           }
           delete sc;
         }
         subs.remove(i18n("(no subtitles)"));
         subs.remove(i18n("Other subtitle..."));
       //Add the full path to the subtitle name
         for (unsigned int i = 0; i < subs.size(); i++ )
           if(!subs[i].startsWith(d->path()))    
             subs[i] = d->path()+"/"+subs[i];

         mrl.setSubtitleFiles(subs);   
         if (!subtitleURL.isNull())
         {
           kdDebug() << "PlayList: Use subtitle file: " << subtitleURL << " for: " << mrl.url() << endl;
           mrl.setCurrentSubtitle(subs.findIndex(subtitleURL));
         }  
       }  
     } /* if localFile() */

     tmp = insertItem(after, mrl);
     if (tmp)
       after = tmp;

     if ( progress->wasCancelled() )
        break;
     progress->progressBar()->setProgress(i+1);
     progress->setLabel(QString::number(i+1) + " / " + QString::number(progress->progressBar()->totalSteps()) + " " + i18n("Files"));
     KApplication::kApplication()->processEvents();
  }

  delete progress;
  if (m_random) createRandomList();
  updateStatus();
}

void PlayList::add(const MRL::List& mrlList, QListViewItem* after)
{
  for (MRL::List::ConstIterator it = mrlList.begin(); it != mrlList.end(); it++) 
    after = insertItem(after, *it);
  updateStatus();
  if (m_random) createRandomList();
}

void PlayList::slotPlayDirect(QListViewItem* item)
{
  if (!item) return;

  setCurrentEntry(item);
  emit signalPlay(m_currentEntryMRL);
}  
  
void PlayList::setEndless(bool e)
{
  m_endless = e;
}

void PlayList::setRandom(bool r)
{
  m_random = r;
  if (m_random) createRandomList();
}

void PlayList::createRandomList()
{
  kdDebug() << "PlayList: Create a random playlist" << endl;
  m_randomList.clear();
  m_currentRandomListEntry = 0;

  QListViewItem* tmpItem = NULL;
  tmpItem = m_list->firstChild();
  if ( (tmpItem) && (!tmpItem->isVisible()) )
    tmpItem = tmpItem->itemBelow();

  while (tmpItem)
  {
    m_randomList.append(tmpItem);
    tmpItem = tmpItem->itemBelow();
  }

  if (!(m_randomList.count() > 0))
  {
    m_currentRandomListEntry = -1;
    return;
  }  
 
  KRandomSequence r(KApplication::random());
  r.randomize(&m_randomList);
}

void PlayList::clearList()
{
  m_list->clear();
  m_randomList.clear();
  m_playTime = 0;
  m_playTimeVisible = 0;
  m_countVisible = 0;
  if (m_searchSelection)
  {
    m_playlistFilter->clear();
    m_searchSelection = false;
  }  
  updateStatus();
  m_currentEntry = NULL;
  m_currentRandomListEntry = -1;
}

void PlayList::slotDropEvent(QDropEvent* dev, QListViewItem* after)
{
  QStringList urls ,newurls;

  if (QUriDrag::decodeLocalFiles(dev, urls))
  {
    if (urls.count()) 
    {
      for (uint i=0; i < urls.count() ;i++) 
      {
        KURL url(QUriDrag::unicodeUriToUri(urls[i]));
        newurls << url.path(-1);
        kdDebug() << "PlayList: Dropped " << url.path() << endl;
      }
      add(newurls, after);
    }
    else
    {
      QUriDrag::decodeToUnicodeUris(dev, urls);
      if (urls.count())
        add(urls, after);
    } 
  }
  else
  if (strcmp(dev->format(), "text/x-moz-url") == 0)    /* for mozilla drops */
  {
    QByteArray data = dev->encodedData("text/plain");
    QString md(data);
    add(md, after);
  } 
}

void PlayList::slotSetAlternateColor( const QColor& col )
{
  m_list->setAlternateBackground( col );
  m_altCol = col;
  m_list->triggerUpdate();
}

void PlayList::useAlternateEncoding(bool useEncoding)
{
  m_useAlternateEncoding = useEncoding;
}

void PlayList::setAlternateEncoding(const QString& encoding)
{
  m_alternateEncoding = encoding;
}


void PlayList::getMetaInfo(MRL& mrl, const QString& mimeName)
{
  KFileMetaInfo* metaInfo = NULL;
  KFileMetaInfoGroup metaGroup;
  uint m, n;
  
  metaInfo = new KFileMetaInfo(mrl.url(), mimeName);
  QStringList groups = metaInfo->groups();
  QStringList keys;
  QString title;
  QString artist;
  QString album;

  QTextCodec *altCodec;
  QTextCodec *CodecUtf8;

  altCodec = QTextCodec::codecForName(m_alternateEncoding);
  CodecUtf8 = QTextCodec::codecForName("UTF-8");
  //kdDebug() << "playlist: locale " << Codec->name << " index: " << m_alternateEncoding << endl;
  
  for (m = 0; m < groups.count(); m++)
  {
    //kdDebug() << "Metainfo-Group: " << groups[m] << endl;
    metaGroup = metaInfo->group(groups[m]);
    keys = metaGroup.keys();
    for (n = 0; n < keys.count(); n++)
    {
      //kdDebug() << "Metainfo-Key: " << keys[n] << endl;
      if (keys[n] == "Length")
      {
        mrl.setLength(QTime().addSecs(metaGroup.item(keys[n]).value().toUInt())); 
      }

      if (keys[n] == "Title") 
      { 
        title = metaGroup.item(keys[n]).value().toString().simplifyWhiteSpace();
        if ((!title.isEmpty()) && (title.contains(QRegExp("\\w")) > 2) && (title.left(5).lower() != "track"))
      {
         if ((m_useAlternateEncoding) && (CodecUtf8->heuristicContentMatch(title,title.length()) < 0)) 
         {
            mrl.setTitle(altCodec->toUnicode(title));
            //kdDebug() << "playlist: Convert, locale name: " << altCodec->name() << title << endl;
         }
           else
           {
              mrl.setTitle(title);
            //kdDebug() << "playlist: non Convert, locale name: " << CodecUtf8->name() << title << endl;
           }
        }
      }

      if (keys[n] == "Artist") 
      {
      artist = (metaGroup.item(keys[n]).value().toString().simplifyWhiteSpace());
        if (!artist.isEmpty()) 
      {
         if ((m_useAlternateEncoding) && (CodecUtf8->heuristicContentMatch(artist,artist.length()) < 0 )) 
         {
            mrl.setArtist(altCodec->toUnicode(artist));
         }
           else
           {
              mrl.setArtist(artist);
           }
        }
      }
      
      if (keys[n] == "Album") 
      {
      album = (metaGroup.item(keys[n]).value().toString().simplifyWhiteSpace());
        if (!album.isEmpty()) 
      {
         if ((m_useAlternateEncoding) && (CodecUtf8->heuristicContentMatch(album,album.length()) < 0 )) 
         {
            mrl.setAlbum(altCodec->toUnicode(album));
         }
           else
           {
              mrl.setAlbum(album);
           }
      }
      }
      
      if (keys[n] == "Tracknumber")
        mrl.setTrack(metaGroup.item(keys[n]).value().toString().simplifyWhiteSpace());
      if (keys[n] == "Date")
        mrl.setYear(metaGroup.item(keys[n]).value().toString().simplifyWhiteSpace());
      if (keys[n] == "Genre")
        mrl.setGenre(metaGroup.item(keys[n]).value().toString().simplifyWhiteSpace());
    }
  }
  
  delete metaInfo;
}

void PlayList::mergeMeta(const MRL& mrl)
{
  if (!m_currentEntry) return;
  PlaylistItem* tmp = dynamic_cast<PlaylistItem*>(m_currentEntry);
  bool addTime = (tmp->length() == QString("00:00:00"));
  if ((tmp) && (tmp->url() == mrl.url()))
  {
    tmp->setTitle(mrl.title());
    tmp->setArtist(mrl.artist());
    tmp->setAlbum(mrl.album());
    tmp->setTrack(mrl.track());
    tmp->setYear(mrl.year());
    tmp->setGenre(mrl.genre());
    tmp->setLength(mrl.length().toString("h:mm:ss"));
    tmp->setCurrentSubtitle(mrl.currentSubtitle());
    tmp->setSubtitles(mrl.subtitleFiles());
  }
  if (addTime)
  {
    if (tmp->length().contains(':'));
    {
      m_playTime += timeStringToMs(tmp->length());
      if (tmp->isVisible())
        m_playTimeVisible += timeStringToMs(tmp->length());
     
      updateStatus();  
    }
  }
}

/************ sort playlist ******************/

void PlayList::slotSort(int section)
{
  m_list->setSorting(section, m_sortAscending);
  m_list->sort();
  m_list->setSorting(-1);
  m_sortAscending = !m_sortAscending;
}  


/***  remove items ***/

void PlayList::slotRemoveSelected()
{
  QPtrList<QListViewItem> selected;

  if (m_currentEntry)
    if (m_currentEntry->isSelected())
     {
        m_currentEntry = NULL;
        m_currentRandomListEntry = -1;
     }   

  selected = m_list->selectedItems();
  PlaylistItem* item = NULL;

  for(uint i = 0; i<selected.count(); i++)
  {
   // kdDebug() << "Remove " << selected.at(i)->text(TITLE_COLUMN) << "\n";
    item = dynamic_cast<PlaylistItem *>(selected.at(i));
    if (item->length().contains(':'))
    {
      m_playTime -= timeStringToMs(item->length());
      m_playTimeVisible -= timeStringToMs(item->length());
    }  
    
    m_countVisible--;
    delete selected.at(i);
  } 
 
  if (m_random) createRandomList();
  updateStatus();
}

void PlayList::updateStatus()
{
   QString status;
   if (isQueueMode())
   {
     QTime total;
     for (MRL::List::ConstIterator it = m_queue.begin(); it != m_queue.end(); it++)
       total = total.addSecs(QTime().secsTo((*it).length()));
     status = i18n("Queue: %1 Entries, Playtime: %2").arg(m_queue.count()).arg(total.toString("h:mm:ss"));
   }
   else
   {
     //status = i18n("Entries: %1, Playtime: %2  (Total: %3, %4)").arg(QString::number(m_countVisible)).arg(msToTimeString(m_playTimeVisible)).arg(QString::number(m_list->childCount())).arg(msToTimeString(m_playTime));
     
     
     status = i18n("Entries: %1, Playtime: %2").arg(QString::number(m_countVisible)).arg(msToTimeString(m_playTimeVisible));
   }
   emit signalPlaylistStatus(status);
}    

void PlayList::slotNewList()
{
  saveCurrentPlaylist();
  m_playlistSelector->insertItem(i18n("Playlist") + QString::number(m_nextPlaylistNumber), 0);
  m_nextPlaylistNumber++;
  m_playlistSelector->setCurrentItem(0);
  m_currentPlaylist = 0;
  clearList();
}

void PlayList::setPlaylist(const QString& name, bool clear)
{
  saveCurrentPlaylist();
  if (clear)
    clearList();
  int index = 0;
  if (m_playlistSelector->listBox()->findItem(name))
    index = m_playlistSelector->listBox()->index(m_playlistSelector->listBox()->findItem(name)); 
   else 
    m_playlistSelector->insertItem(name, 0);  
  m_playlistSelector->setCurrentItem(index);
  m_currentPlaylist = index;
  m_playlistName->setText(m_playlistSelector->currentText());
}   

void PlayList::slotPlaylistActivated(int index)
{
  kdDebug() << "PlayList: Switch to playlist: " << m_playlistSelector->text(index) << endl;
  QString pl = m_playlistSelector->text(index);
  
  if (QString(SPECIAL_PLAYLISTS).contains(pl))
  {
    if (pl == "DVD")
      emit signalRequestForDVD();
    if (pl == "VCD")
      emit signalRequestForVCD();
    if (pl == "AudioCD")
      emit signalRequestForAudioCD();
    return;  
  }
  saveCurrentPlaylist();
  clearList(); 
  m_currentPlaylist = index;
  m_playlistName->setText(m_playlistSelector->currentText());
  add(m_playlistDirectory + pl + ".kaffeine", NULL);
  if (parentWidget())
    parentWidget()->setFocus();
/*  if (!isQueueMode())
  {
    getCurrent();
    emit signalPlay(m_currentEntryMRL);
  }  */
}

void PlayList::slotNewPlaylistName(const QString& text)
{
  if ((text.isEmpty()) || (text == m_playlistSelector->text(m_currentPlaylist)))
    return;
  if (m_playlistSelector->listBox()->findItem(text))
  {
    kdDebug() << "PlayList: Name still exists!" << endl;
    return;
  }  
  QString oldPl = m_playlistDirectory + m_playlistSelector->text(m_currentPlaylist) + ".kaffeine";
  kdDebug() << "Playlist: removing file: " << oldPl << endl;
  if (!KIO::NetAccess::del(oldPl, this))
    kdError() << "Playlist: " << KIO::NetAccess::lastErrorString() << endl;
  
  kdDebug() << "PlayList: Set playlist name to: " << text << endl;  
  m_playlistSelector->changeItem(text, m_currentPlaylist);
  m_playlistName->setText(m_playlistSelector->currentText());
  saveCurrentPlaylist(); 
  if (parentWidget())
    parentWidget()->setFocus(); 
}

void PlayList::saveCurrentPlaylist()
{
  QString pl = m_playlistSelector->text(m_currentPlaylist);
  if (!QString(SPECIAL_PLAYLISTS).contains(pl))
    savePlaylist(m_playlistDirectory + pl + ".kaffeine");
}  

void PlayList::removeCurrentPlaylist()
{
  int code = KMessageBox::questionYesNo(0, i18n("Remove '%1' from list and from disk?").arg(m_playlistSelector->text(m_currentPlaylist)));
  if (code == KMessageBox::Yes)
  {
    QString pl = m_playlistDirectory + m_playlistSelector->text(m_currentPlaylist) + ".kaffeine";
    if (!KIO::NetAccess::del(pl, this))
      kdError() << "Playlist: " << KIO::NetAccess::lastErrorString() << endl;
        
    m_playlistSelector->removeItem(m_currentPlaylist);
    m_currentPlaylist = m_playlistSelector->currentItem();
    m_playlistName->setText(m_playlistSelector->currentText());
    clearList();
    add(m_playlistDirectory + m_playlistSelector->text(m_currentPlaylist) + ".kaffeine", NULL); 
  }
}

void PlayList::loadPlaylist(const QString& pl)
{
  saveCurrentPlaylist();
  QString plName = KURL(pl).fileName();
  plName = plName.remove(".kaffeine", false);
  m_playlistSelector->insertItem(plName, 0);
  m_playlistSelector->setCurrentItem(0);
  m_playlistName->setText(m_playlistSelector->currentText());
  m_currentPlaylist = 0;
  clearList();
  add(pl, NULL); 
}

/******************************************
 *         save xml playlist
 ******************************************/
 
void PlayList::savePlaylist(const QString& pl)
{
  QDomDocument doc("playlist");
  doc.setContent(QString("<!DOCTYPE XMLPlaylist>"));
  QDomElement root = doc.createElement("playlist");
  root.setAttribute("client", "kaffeine");
  doc.appendChild(root);

  QDomElement entry;
  PlaylistItem * tmp = NULL;

  QListViewItemIterator it(m_list);
  while (it.current())
  {
    tmp = dynamic_cast<PlaylistItem *>(it.current());
   
    entry = doc.createElement("entry");

    entry.setAttribute("title", tmp->title());
    entry.setAttribute("artist", tmp->artist());
    entry.setAttribute("album", tmp->album());
    entry.setAttribute("track", tmp->track());
    entry.setAttribute("year", tmp->year());
    entry.setAttribute("genre", tmp->genre());
    entry.setAttribute("url", tmp->url());
    entry.setAttribute("mime", tmp->mime());
    entry.setAttribute("length", tmp->length());
     
    if(!(tmp->subtitles().isEmpty()))
    {
       QString subList;
       for(unsigned int i=0; i<tmp->subtitles().count(); i++)
         subList += tmp->subtitles()[i] + "&";  
       
       entry.setAttribute("subs", subList); 
    }
    entry.setAttribute("currentSub", QString::number(tmp->currentSubtitle()));
    root.appendChild(entry);
      
    ++it;
  }

  QFile file(pl);
  if (!file.open(IO_WriteOnly)) return;
  QTextStream stream(&file);
  stream.setEncoding(QTextStream::UnicodeUTF8);

  stream << doc.toString();

  file.close();
  m_list->setCleared(false);
}

/**********************/

void PlayList::slotFindText(const QString& text)
{
  if (text == i18n("Filter")) return;

  QListViewItemIterator it(m_list);
  m_playTimeVisible = 0;
  m_countVisible = 0;
  PlaylistItem* tmp = NULL;
  while ( it.current() )
  {
    tmp = dynamic_cast<PlaylistItem *>(it.current());
    if (text.isEmpty() || tmp->title().contains(text, false) || tmp->url().contains(text, false) 
        || tmp->artist().contains(text, false) || tmp->album().contains(text, false) )
    {
      tmp->setVisible(true);
      if (tmp->length().contains(':'))
        m_playTimeVisible += timeStringToMs(tmp->length());
     
      m_countVisible++;
    }
    else
    {
      tmp->setVisible(false);
      if (tmp == m_currentEntry)
      {
        tmp->setPixmap(TITLE_COLUMN, QPixmap());
        m_currentEntry = NULL;
        m_currentRandomListEntry = -1;
      }  
    }
    ++it;
  }

  if (text.isEmpty())
    m_searchSelection = false;
   else
    m_searchSelection = true; 

  if (m_random) createRandomList();
  updateStatus();
}  


/***************** cut/copy/paste *************************/  

void PlayList::slotCut()
{
  slotCopy();
  slotRemoveSelected();
}

void PlayList::slotPaste()
{
  QPtrList<QListViewItem> selected;
  selected = m_list->selectedItems();
  QListViewItem* lastSelected = NULL;
  if (selected.count())
    lastSelected = selected.at(selected.count() - 1);
   else 
    lastSelected = m_list->lastItem();
    
  QStrList list;

  if (QUriDrag::decode(QApplication::clipboard()->data(), list))
  {
    QStringList urls;
    for (QStrListIterator it(list); *it; ++it)
      urls.append(QUriDrag::uriToUnicodeUri(*it));
    add(urls, lastSelected);
    return;
  }
 /** try to decode as text **/
  QString text;
  if (QTextDrag::decode(QApplication::clipboard()->data(), text))
  {
    add(text, lastSelected);
  }  
}

void PlayList::slotCopy()
{
  QPtrList<QListViewItem> selected;
  selected = m_list->selectedItems();

  QStrList urlList;
  
  for (uint i=0; i<selected.count(); i++)
  {
    urlList.append(QUriDrag::unicodeUriToUri(dynamic_cast<PlaylistItem *>(selected.at(i))->url()));
  }

  QApplication::clipboard()->setData(new QUriDrag(urlList));  
}

void PlayList::slotSelectAll()
{
  QListViewItemIterator it(m_list);
  while (it.current())
  {
    if ((*it)->isVisible())
      m_list->setSelected(*it, true);
    ++it;
  }
}

void PlayList::slotPlaylistFromSelected()
{
  QPtrList<QListViewItem> selected;
  selected = m_list->selectedItems();

  MRL::List mrlList;
  
  for (uint i=0; i<selected.count(); i++)
  {
    mrlList.append(dynamic_cast<PlaylistItem *>(selected.at(i))->toMRL());
  }
  
  if (mrlList.count())
  {
    slotNewList();
    add(mrlList, NULL);
  }
}

void PlayList::slotAddToQueue(MRL mrl)
{
  m_queue.append(mrl);
  updateStatus();
}

/**** helper ****/

QString PlayList::msToTimeString(int msec)
{
  /*
  QTime t;
  t = t.addMSecs(msec);
  return t.toString("h:mm:ss");
  */
  /* can be >24h */
  int hours;
  int min;
  int sec;
  int my_msec=msec;
  QString tmp;
  QString t;

  msec = msec/1000;  //sec
  hours = msec/3600;
  my_msec -= hours*3600*1000;
  t = t.setNum(hours);
  t.append(":");

  msec = msec - (hours*3600);
  min = msec / 60;
  my_msec -= min*60*1000;
  tmp = tmp.setNum(min);
  tmp = tmp.rightJustify(2, '0');
  t.append(tmp);
  t.append(":");

  sec = msec - (min*60);
  my_msec -= sec*1000;
  if(my_msec > 500)
   sec++;
  tmp = tmp.setNum(sec);
  tmp = tmp.rightJustify(2, '0');
  t.append(tmp);

  return t;
}

int PlayList::timeStringToMs(const QString& timeString)
{
  int sec = 0;
  QStringList tokens = QStringList::split(':',timeString);
  
  bool ok = false;
  sec += tokens[0].toInt(&ok)*3600; //hours
  sec += tokens[1].toInt(&ok)*60; //minutes
  sec += tokens[2].toInt(&ok); //secs
  
  if (ok)
    return sec*1000; //return millisecs
   else
    return 0;
}

bool PlayList::endsWith(QString s1, QString s2, bool caseSensitive )
{
    if ( s1.isNull() || s2.isNull() )
      return false;
    int startPos = s1.length() - s2.length();

    for (unsigned int i = 0; i < s2.length(); i++ ) 
    {
      if(caseSensitive)
      {
        if ( s1[startPos + i] != s2[i] )
        return false;
      }
      else
      {
        if ( s1[startPos + i].lower() != s2[i].lower() )
        return false;
      }
    }
  
    return true;
}

bool PlayList::startsWith(QString s1, QString s2, bool caseSensitive )
{
    if ( s1.isNull() || s2.isNull() )
      return false;
    
    if(s2.length() > s1.length())
      return false;

    for (unsigned int i = 0; i < s2.length(); i++ ) 
    {
      if(caseSensitive)
      {
        if ( s1[i] != s2[i] )
        return false;
      }
      else
      {
        if ( s1[i].lower() != s2[i].lower() )
        return false;
      }
    }
  
    return true;
}

SubtitleChooser::SubtitleChooser(QStringList values, QString filename,QWidget *parent, const char * name) :
KDialogBase(parent,name,true,i18n("Select a subtitle"), Ok|Cancel, Ok, true)
{
   QVBox *page = makeVBoxMainWidget();
   QLabel *label =  new QLabel(page);
   label->setText("<qt>" + i18n("Media file:") + " <b>" + filename + "</b></qt>");
   table = new QListBox(page);
   this->setMinimumSize(300,200);
  
   table->setFocus();
   table->insertStringList(values);
   table->setSelected(0, true);
}

SubtitleChooser::~SubtitleChooser(){}

QString SubtitleChooser::getSelection()
{
  return table->selectedItem()->text();
}

MovieChooser::MovieChooser(QStringList values, QString filename,QWidget *parent, const char * name) :
KDialogBase(parent,name,true,i18n("Select the movie"), Ok|Cancel, Ok, true)
{
   QVBox *page = makeVBoxMainWidget();
   QLabel *label =  new QLabel(page);
   label->setText("<qt> " + i18n("Subtitle file:") + " <b>" + filename + "</b></qt>");
   table = new QListBox(page);
   this->setMinimumSize(450,200);
  
   table->setFocus();
   table->insertStringList(values);
   table->setSelected(0, true);
}

MovieChooser::~MovieChooser(){}

QString MovieChooser::getSelection()
{
  return table->selectedItem()->text();
}

Generated by  Doxygen 1.6.0   Back to index