diff options
author | tpearson <tpearson@283d02a7-25f6-0310-bc7c-ecb5cbfe19da> | 2010-02-10 00:59:09 +0000 |
---|---|---|
committer | tpearson <tpearson@283d02a7-25f6-0310-bc7c-ecb5cbfe19da> | 2010-02-10 00:59:09 +0000 |
commit | 7f66b9e8ba186fb14a2db598a87dfa8de7b5a47b (patch) | |
tree | b5e07812e82f00c780d2c035dca102b8e364f447 /kaffeine/src/input/audiobrowser | |
download | kaffeine-7f66b9e8ba186fb14a2db598a87dfa8de7b5a47b.tar.gz kaffeine-7f66b9e8ba186fb14a2db598a87dfa8de7b5a47b.zip |
Added old abandoned KDE3 version of Kaffeine
git-svn-id: svn://anonsvn.kde.org/home/kde/branches/trinity/applications/kaffeine@1088031 283d02a7-25f6-0310-bc7c-ecb5cbfe19da
Diffstat (limited to 'kaffeine/src/input/audiobrowser')
-rw-r--r-- | kaffeine/src/input/audiobrowser/Makefile.am | 28 | ||||
-rw-r--r-- | kaffeine/src/input/audiobrowser/googlefetcher.cpp | 282 | ||||
-rw-r--r-- | kaffeine/src/input/audiobrowser/googlefetcher.h | 91 | ||||
-rw-r--r-- | kaffeine/src/input/audiobrowser/googlefetcherdialog.cpp | 254 | ||||
-rw-r--r-- | kaffeine/src/input/audiobrowser/googlefetcherdialog.h | 98 | ||||
-rw-r--r-- | kaffeine/src/input/audiobrowser/kaffeineplaylist.rc | 17 | ||||
-rw-r--r-- | kaffeine/src/input/audiobrowser/playlist.cpp | 2365 | ||||
-rw-r--r-- | kaffeine/src/input/audiobrowser/playlist.h | 336 | ||||
-rw-r--r-- | kaffeine/src/input/audiobrowser/playlistitem.cpp | 268 | ||||
-rw-r--r-- | kaffeine/src/input/audiobrowser/playlistitem.h | 93 | ||||
-rw-r--r-- | kaffeine/src/input/audiobrowser/urllistview.cpp | 347 | ||||
-rw-r--r-- | kaffeine/src/input/audiobrowser/urllistview.h | 92 |
12 files changed, 4271 insertions, 0 deletions
diff --git a/kaffeine/src/input/audiobrowser/Makefile.am b/kaffeine/src/input/audiobrowser/Makefile.am new file mode 100644 index 0000000..b743ccd --- /dev/null +++ b/kaffeine/src/input/audiobrowser/Makefile.am @@ -0,0 +1,28 @@ +METASOURCES = AUTO + +noinst_LTLIBRARIES = libkaffeineaudiobrowser.la + +INCLUDES = -I$(top_srcdir)/kaffeine/src/input/ \ + -I$(top_srcdir)/kaffeine/src/ \ + $(all_includes) \ + -I$(top_srcdir)/kaffeine/src/player-parts/kaffeine-part + +libkaffeineaudiobrowser_la_SOURCES = playlist.cpp \ + playlistitem.cpp \ + urllistview.cpp \ + googlefetcher.cpp \ + googlefetcherdialog.cpp + +KDE_CXXFLAGS = $(USE_EXCEPTIONS) + +libkaffeineaudiobrowser_la_LDFLAGS = $(KDE_RPATH) \ + $(all_libraries) \ + -L$(top_srcdir)/kaffeine/src/input + +libkaffeineaudiobrowser_la_LIBADD = $(top_builddir)/kaffeine/src/input/libkaffeineinput.la \ + $(top_builddir)/kaffeine/src/player-parts/kaffeine-part/libkaffeinepart.la \ + $(LIB_KHTML) + +# this is where the XML-GUI resource file goes +shellrcdir = $(kde_datadir)/kaffeine +shellrc_DATA = kaffeineplaylist.rc diff --git a/kaffeine/src/input/audiobrowser/googlefetcher.cpp b/kaffeine/src/input/audiobrowser/googlefetcher.cpp new file mode 100644 index 0000000..fe88d14 --- /dev/null +++ b/kaffeine/src/input/audiobrowser/googlefetcher.cpp @@ -0,0 +1,282 @@ +/* + * googlefetcher.cpp + * + * Copyright (C) 2004 Nathan Toone <nathan@toonetown.com> + * Copyright (C) 2006 Christophe Thommeret <hftom@free.fr> + * + * 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. + * + * This program 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 General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program; if not, write to the Free Software + * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA + */ + +#include <dom/html_document.h> +#include <dom/html_misc.h> +#include <dom/html_table.h> +#include <dom/dom_exception.h> +#include <dom/dom2_traversal.h> + +#include <khtml_part.h> + +#include <klocale.h> +#include <kinputdialog.h> +#include <kurl.h> +#include <kio/netaccess.h> + +#include "googlefetcher.h" +#include "googlefetcherdialog.h" + +GoogleImage::GoogleImage(QString thumbURL, QString size) +{ + // thumbURL is in the following format - and we can regex the imageURL + // images?q=tbn:hKSEWNB8aNcJ:www.styxnet.com/deyoung/styx/stygians/cp_portrait.jpg + m_thumbURL = thumbURL.replace("\"",""); + m_imageURL = thumbURL.remove(QRegExp("^.*q=tbn:[^:]*:")).replace("\"",""); + m_size = size.replace("pixels - ", "\n(") + ")"; +} + + +GoogleFetcher::GoogleFetcher( QString artist, QString album, bool gallery ) +{ + m_artist = artist; + m_album = album; + m_searchString = artist+" "+album; + encoding = 0; + galleryImage=galleryPage=0; + galleryMode = gallery; +} + +void GoogleFetcher::slotLoadImageURLs(GoogleFetcher::ImageSize size) +{ + if(m_loadedQuery == m_searchString && m_loadedSize == size) + return; + + m_imageList.clear(); + + KURL url("http://images.google.com/images"); + if ( encoding ) + url.addQueryItem("q", m_searchString,encoding); + else + url.addQueryItem("q", m_searchString); + + switch (size) { + case XLarge: + url.addQueryItem("imgsz", "xlarge|xxlarge"); + break; + case Large: + url.addQueryItem("imgsz", "large"); + break; + case Medium: + url.addQueryItem("imgsz", "medium"); + break; + case Small: + url.addQueryItem("imgsz", "small"); + break; + case Icon: + url.addQueryItem("imgsz", "icon"); + break; + default: + break; + } + + m_loadedQuery = m_searchString; + m_loadedSize = size; + + // We don't normally like exceptions but missing DOMException will kill + // JuK rather abruptly whether we like it or not so we don't really have a + // choice if we're going to screen-scrape Google. + try { + + KHTMLPart part; + + // Create empty document. + + part.begin(); + part.end(); + + DOM::Document search = part.document(); + search.setAsync(false); // Grab the document before proceeding. + + kdDebug() << "Performing Google Search: " << url << endl; + + search.load(url.url()+QString("&start=%1&sa=N").arg(galleryPage*20) ); + + QString text = search.toString().string(); + + if ( !hasImageResults(text) ) { + kdDebug() << "Search returned no results.\n"; + emit signalNewSearch(m_imageList); + return; + } + + // Go through each of the top (table) nodes + + int pos = text.find("/imgres?imgurl"); + int tpos; + QString s, c; + while ( pos>-1 ) { + text = text.right( text.length()-pos-14 ); + tpos = text.find("&h="); + text = text.right( text.length()-tpos-3 ); + tpos = text.find("&w="); + c = "pixels - " + text.left( tpos ); + text = text.right( text.length()-tpos-3 ); + tpos = text.find("&sz="); + c = c + "x" + text.left( tpos ); + tpos = text.find("src="); + text = text.right( text.length()-tpos-4 ); + if ( (tpos=text.find("width="))==-1 ) + break; + s = text.left( tpos-1 ); + text = text.right( text.length()-tpos ); + if ( (tpos=text.find("></a>"))==-1 ) + break; + m_imageList.append(GoogleImage(s, c)); + pos = text.find("/imgres?imgurl"); + } + } // try + catch (DOM::DOMException &e) + { + kdError() << "Caught DOM Exception: " << e.code << endl; + } + catch (...) + { + kdError() << "Caught unknown exception.\n"; + } + + emit signalNewSearch(m_imageList); +} + +QImage GoogleFetcher::galleryNext( bool &end ) +{ + QString file; + QImage image; + m_loadedSize = All; + int tries = 0; + end = false; + + while ( tries<3 ) { + if( m_imageList.isEmpty() ) { + if ( tries==0 ) { + encoding = 0; + m_loadedQuery = ""; + ++tries; + displayWaitMessage(); + } + else if ( tries==1 ) { + encoding = 4; // try iso8859-1 + m_loadedQuery = ""; + ++tries; + kdDebug() << "Trying iso8859-1" << endl; + displayWaitMessage(); + } + else { + end = true; + return QImage(); + } + } + else { + if(KIO::NetAccess::download( m_imageList[galleryImage].imageURL(), file, 0)) + image = QImage(file); + else + image = QImage(); + ++galleryImage; + if ( galleryImage==(int)m_imageList.count() ) { + m_imageList.clear(); + galleryImage = 0; + ++galleryPage; + } + KIO::NetAccess::removeTempFile(file); + return image; + } + } + return QImage(); +} + +QPixmap GoogleFetcher::pixmap( bool forceFetch ) +{ + bool chosen = false; + m_loadedSize = All; + int tries = 0; + + encoding = 0; + displayWaitMessage(); + + QPixmap pixmap; + + while(!chosen) { + + if(m_imageList.isEmpty()) + switch(tries) { + case 0: + encoding = 4; // try iso8859-1 + m_loadedQuery = ""; + ++tries; + kdDebug() << "Trying iso8859-1" << endl; + displayWaitMessage(); + break; + default : + if ( forceFetch == true ) + chosen = !requestNewSearchTerms(true); + else + chosen = true; + } + else { + GoogleFetcherDialog dialog("google", m_imageList, m_artist, m_album, 0); + connect(&dialog, SIGNAL(sizeChanged(GoogleFetcher::ImageSize)), + this, SLOT(slotLoadImageURLs(GoogleFetcher::ImageSize))); + connect(this, SIGNAL(signalNewSearch(GoogleImageList &)), + &dialog, SLOT(refreshScreen(GoogleImageList &))); + dialog.exec(); + pixmap = dialog.result(); + chosen = dialog.takeIt(); + + if(dialog.newSearch()) + requestNewSearchTerms(); + } + } + return pixmap; +} + +void GoogleFetcher::displayWaitMessage() +{ + //KStatusBar *statusBar = static_cast<KMainWindow *>(kapp->mainWidget())->statusBar(); + //statusBar->message(i18n("Searching for Images. Please Wait...")); + slotLoadImageURLs(); + //statusBar->clear();*/ +} + +bool GoogleFetcher::requestNewSearchTerms(bool noResults) +{ + bool ok; + m_searchString = KInputDialog::getText(i18n("Cover Downloader"), + noResults ? + i18n("No matching images found, please enter new search terms:") : + i18n("Enter new search terms:"), + m_searchString, &ok); + if(ok && !m_searchString.isEmpty()) + displayWaitMessage(); + else + m_searchString = m_loadedQuery; + + return ok; +} + +bool GoogleFetcher::hasImageResults( QString &doc ) +{ + if ( !doc.contains( "/imgres?imgurl" ) ) + return false; + + return true; +} + +#include "googlefetcher.moc" diff --git a/kaffeine/src/input/audiobrowser/googlefetcher.h b/kaffeine/src/input/audiobrowser/googlefetcher.h new file mode 100644 index 0000000..7c90091 --- /dev/null +++ b/kaffeine/src/input/audiobrowser/googlefetcher.h @@ -0,0 +1,91 @@ +/* + * googlefetcher.h + * + * Copyright (C) 2004 Nathan Toone <nathan@toonetown.com> + * Copyright (C) 2006 Christophe Thommeret <hftom@free.fr> + * + * 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. + * + * This program 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 General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program; if not, write to the Free Software + * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA + */ + +#ifndef GOOGLEFETCHER_H +#define GOOGLEFETCHER_H + +#include <kdialogbase.h> + +#include <qpixmap.h> +#include <qstringlist.h> +#include <qregexp.h> + +namespace DOM { + class HTMLDocument; +} + +class KURL; + +class GoogleImage +{ +public: + GoogleImage(QString thumbURL = QString::null, QString size = QString::null); + + QString imageURL() const { return m_imageURL; } + QString thumbURL() const { return m_thumbURL; } + QString size() const { return m_size; } + +private: + QString m_imageURL; + QString m_thumbURL; + QString m_size; +}; + +typedef QValueList<GoogleImage> GoogleImageList; + +class GoogleFetcher : public QObject +{ + Q_OBJECT + +public: + enum ImageSize { All, Icon, Small, Medium, Large, XLarge }; + + GoogleFetcher( QString artist, QString album, bool gallery=false ); + QPixmap pixmap( bool forceFetch = false ); + QImage galleryNext( bool &end ); + +signals: + void signalNewSearch(GoogleImageList &images); + +private: + void displayWaitMessage(); + bool requestNewSearchTerms(bool noResults = false); + + // Returns true if there are results in the search, otherwise returns false. + bool hasImageResults( QString &doc ); + +private slots: + void slotLoadImageURLs(GoogleFetcher::ImageSize size = All); + +private: + QString m_artist, m_album; + QString m_searchString; + QString m_loadedQuery; + ImageSize m_loadedSize; + GoogleImageList m_imageList; + uint m_selectedIndex; + int encoding; + int galleryImage; + int galleryPage; + bool galleryMode; +}; + +#endif /* GOOGLEFETCHER_H */ diff --git a/kaffeine/src/input/audiobrowser/googlefetcherdialog.cpp b/kaffeine/src/input/audiobrowser/googlefetcherdialog.cpp new file mode 100644 index 0000000..bda3853 --- /dev/null +++ b/kaffeine/src/input/audiobrowser/googlefetcherdialog.cpp @@ -0,0 +1,254 @@ +/* + * googlefetcherdialog.cpp + * + * Copyright (C) 2004 Nathan Toone <nathan@toonetown.com> + * + * 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. + * + * This program 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 General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program; if not, write to the Free Software + * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA + */ + +#include <kapplication.h> +#include <kio/netaccess.h> +#include <klocale.h> +#include <kdebug.h> +#include <kmessagebox.h> +#include <kcombobox.h> + +#include <qhbox.h> +#include <qimage.h> +#include <qlabel.h> +#include <qpushbutton.h> +#include <qeventloop.h> + +#include "googlefetcherdialog.h" + +GoogleFetcherDialog::GoogleFetcherDialog(const QString &name, + const GoogleImageList &imageList, + const QString &artist, + const QString &album, + QWidget *parent) : + KDialogBase(parent, name.latin1(), true, QString::null, + Ok | Cancel | User1 , NoDefault, true), + m_pixmap(QPixmap()), + m_imageList(imageList), + m_takeIt(false), + m_newSearch(false) +{ + m_artist = artist; + m_album = album; + disableResize(); + + QHBox *mainBox = new QHBox(this); + m_iconWidget = new KIconView(mainBox); + m_iconWidget->setResizeMode(QIconView::Adjust); + m_iconWidget->setSelectionMode(QIconView::Extended); + m_iconWidget->setSpacing(10); + m_iconWidget->setMode(KIconView::Execute); + m_iconWidget->setFixedSize(500,550); + m_iconWidget->arrangeItemsInGrid(); + m_iconWidget->setItemsMovable(FALSE); + + QHBox *imgSize = new QHBox(actionButton(User1)->parentWidget()); + //QLabel *label = new QLabel(imgSize); + //label->setText(i18n("Image size:")); + + KComboBox *combo = new KComboBox(imgSize); + combo->insertItem(i18n("All Sizes")); + combo->insertItem(i18n("Very Small")); + combo->insertItem(i18n("Small")); + combo->insertItem(i18n("Medium")); + combo->insertItem(i18n("Large")); + combo->insertItem(i18n("Very Large")); + combo->setCurrentItem(0); + connect(combo, SIGNAL(activated(int)), this, SLOT(imgSizeChanged(int))); + connect(m_iconWidget, SIGNAL( executed(QIconViewItem*) ), this, SLOT(slotOk())); + + imgSize->adjustSize(); + setMainWidget(mainBox); + setButtonText(User1, i18n("New Search")); +} + +GoogleFetcherDialog::~GoogleFetcherDialog() +{ + +} + +void GoogleFetcherDialog::setLayout() +{ + setCaption(QString("%1 - %2 (%3)") + .arg(m_artist) + .arg(m_album) + .arg(m_imageList.size())); + + m_iconWidget->clear(); + for(uint i = 0; i < m_imageList.size(); i++) + new CoverIconViewItem(m_iconWidget, m_imageList[i]); + + adjustSize(); +} + +void GoogleFetcherDialog::setImageList(const GoogleImageList &imageList) +{ + m_imageList=imageList; +} + +//////////////////////////////////////////////////////////////////////////////// +// public slots +//////////////////////////////////////////////////////////////////////////////// + +void GoogleFetcherDialog::refreshScreen(GoogleImageList &imageList) +{ + setImageList(imageList); + setLayout(); +} + +int GoogleFetcherDialog::exec() +{ + setLayout(); + return KDialogBase::exec(); +} + +void GoogleFetcherDialog::slotOk() +{ + uint selectedIndex = m_iconWidget->index(m_iconWidget->currentItem()); + m_pixmap = pixmapFromURL(m_imageList[selectedIndex].imageURL()); + + if(m_pixmap.isNull()) { + KMessageBox::sorry(this, + i18n("The cover you have selected is unavailable. Please select another."), + i18n("Cover Unavailable")); + QPixmap blankPix; + blankPix.resize(80, 80); + blankPix.fill(); + m_iconWidget->currentItem()->setPixmap(blankPix, true, true); + return; + } + + m_takeIt = true; + m_newSearch = false; + hide(); +} + +void GoogleFetcherDialog::slotCancel() +{ + m_takeIt = true; + m_newSearch = false; + m_pixmap = QPixmap(); + hide(); +} + +void GoogleFetcherDialog::slotUser1() +{ + m_takeIt = false; + m_newSearch = true; + m_pixmap = QPixmap(); + hide(); +} + +void GoogleFetcherDialog::imgSizeChanged(int index) +{ + GoogleFetcher::ImageSize imageSize = GoogleFetcher::All; + switch (index) { + case 1: + imageSize = GoogleFetcher::Icon; + break; + case 2: + imageSize = GoogleFetcher::Small; + break; + case 3: + imageSize = GoogleFetcher::Medium; + break; + case 4: + imageSize=GoogleFetcher::Large; + break; + case 5: + imageSize=GoogleFetcher::XLarge; + break; + default: + break; + } + emit sizeChanged(imageSize); +} + +QPixmap GoogleFetcherDialog::fetchedImage(uint index) const +{ + return (index > m_imageList.count()) ? QPixmap() : pixmapFromURL(m_imageList[index].imageURL()); +} + +QPixmap GoogleFetcherDialog::pixmapFromURL(const KURL &url) const +{ + QString file; + + if(KIO::NetAccess::download(url, file, 0)) { + QPixmap pixmap = QPixmap(file); + KIO::NetAccess::removeTempFile(file); + return pixmap; + } + KIO::NetAccess::removeTempFile(file); + return QPixmap(); +} + +//////////////////////////////////////////////////////////////////////////////// +// CoverIconViewItem +//////////////////////////////////////////////////////////////////////////////// + +CoverIconViewItem::CoverIconViewItem(QIconView *parent, const GoogleImage &image) : + QObject(parent), KIconViewItem(parent, parent->lastItem(), image.size()), m_job(0) +{ + // Set up the iconViewItem + + QPixmap mainMap; + mainMap.resize(80, 80); + mainMap.fill(); + setPixmap(mainMap, true, true); + + // Start downloading the image. + + m_job = KIO::get(image.thumbURL(), false, false); + connect(m_job, SIGNAL(result(KIO::Job *)), this, SLOT(imageResult(KIO::Job *))); + connect(m_job, SIGNAL(data(KIO::Job *, const QByteArray &)), + this, SLOT(imageData(KIO::Job *, const QByteArray &))); +} + +CoverIconViewItem::~CoverIconViewItem() +{ + if(m_job) { + m_job->kill(); + + // Drain results issued by KIO before being deleted, + // and before deleting the job. + kapp->eventLoop()->processEvents(QEventLoop::ExcludeUserInput); + + delete m_job; + } +} + +void CoverIconViewItem::imageData(KIO::Job *, const QByteArray &data) +{ + int currentSize = m_buffer.size(); + m_buffer.resize(currentSize + data.size(), QGArray::SpeedOptim); + memcpy(&(m_buffer.data()[currentSize]), data.data(), data.size()); +} + +void CoverIconViewItem::imageResult(KIO::Job *job) +{ + if(job->error()) + return; + + QPixmap iconImage(m_buffer); + iconImage = QImage(iconImage.convertToImage()).smoothScale(80, 80); + setPixmap(iconImage, true, true); +} + +#include "googlefetcherdialog.moc" diff --git a/kaffeine/src/input/audiobrowser/googlefetcherdialog.h b/kaffeine/src/input/audiobrowser/googlefetcherdialog.h new file mode 100644 index 0000000..48ec70c --- /dev/null +++ b/kaffeine/src/input/audiobrowser/googlefetcherdialog.h @@ -0,0 +1,98 @@ +/* + * googlefetcherdialog.h + * + * Copyright (C) 2004 Nathan Toone <nathan@toonetown.com> + * + * 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. + * + * This program 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 General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program; if not, write to the Free Software + * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA + */ + +#ifndef GOOGLEFETCHERDIALOG_H +#define GOOGLEFETCHERDIALOG_H + +#include <kiconview.h> +#include <kio/job.h> + +#include "googlefetcher.h" + +class KURL; + +class GoogleFetcherDialog : public KDialogBase +{ + Q_OBJECT + +public: + GoogleFetcherDialog(const QString &name, + const GoogleImageList &urlList, + const QString &artist, + const QString &album, + QWidget *parent = 0); + + virtual ~GoogleFetcherDialog(); + + QPixmap result() const { return m_pixmap; } + bool takeIt() const { return m_takeIt; } + bool newSearch() const { return m_newSearch; } + + void setLayout(); + void setImageList(const GoogleImageList &urlList); + +public slots: + int exec(); + void refreshScreen(GoogleImageList &list); + +signals: + void sizeChanged(GoogleFetcher::ImageSize); + +protected slots: + void slotOk(); + void slotCancel(); + void slotUser1(); + void imgSizeChanged(int index); + +private: + QPixmap fetchedImage(uint index) const; + QPixmap pixmapFromURL(const KURL &url) const; + + QPixmap m_pixmap; + GoogleImageList m_imageList; + KIconView *m_iconWidget; + bool m_takeIt; + bool m_newSearch; + QString m_artist, m_album; +}; + +namespace KIO +{ + class TransferJob; +} + +class CoverIconViewItem : public QObject, public KIconViewItem +{ + Q_OBJECT + +public: + CoverIconViewItem(QIconView *parent, const GoogleImage &image); + ~CoverIconViewItem(); + +private slots: + void imageData(KIO::Job *job, const QByteArray &data); + void imageResult(KIO::Job* job); + +private: + QByteArray m_buffer; + QGuardedPtr<KIO::TransferJob> m_job; +}; + +#endif /* GOOGLEFETCHERDIALOG_H */ diff --git a/kaffeine/src/input/audiobrowser/kaffeineplaylist.rc b/kaffeine/src/input/audiobrowser/kaffeineplaylist.rc new file mode 100644 index 0000000..a274d74 --- /dev/null +++ b/kaffeine/src/input/audiobrowser/kaffeineplaylist.rc @@ -0,0 +1,17 @@ +<!DOCTYPE kpartgui SYSTEM "kpartgui.dtd"> +<kpartgui name="kaffeineplaylist" version="4"> +<MenuBar> + <Menu name="playlist"><text>Play&list</text> + <Action name="playlist_shuffle"/> + <Action name="playlist_repeat"/> + <Action name="playlist_autocover"/> + <Action name="playlist_showplayer"/> + <Separator/> + <Action name="playlist_clear"/> + <Action name="playlist_new"/> + <Action name="playlist_load"/> + <Action name="playlist_save_as"/> + <Action name="playlist_remove"/> + </Menu> +</MenuBar> +</kpartgui> diff --git a/kaffeine/src/input/audiobrowser/playlist.cpp b/kaffeine/src/input/audiobrowser/playlist.cpp new file mode 100644 index 0000000..480d70b --- /dev/null +++ b/kaffeine/src/input/audiobrowser/playlist.cpp @@ -0,0 +1,2365 @@ +/* + * playlist.cpp + * + * Copyright (C) 2003-2005 Jürgen Kofler <kaffeine@gmx.net> + * Copyright (C) 2005-2006 Christophe Thommeret <hftom@free.fr> + * + * 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. + * + * This program 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 General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program; if not, write to the Free Software + * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA + */ + +#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 <kio/job.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 <dcopref.h> +#include <ktoolbar.h> +#include <kcolordialog.h> +#include <kurlcombobox.h> +#include <kurlcompletion.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 <qinputdialog.h> +#include <qtimer.h> +#include <qwhatsthis.h> +#include <qpopupmenu.h> + +#include "googlefetcher.h" +#include "mrl.h" +#include "playlistimport.h" +#include "playlistitem.h" +#include "playlist.h" +#include "playlist.moc" + + + +RollTitle::RollTitle( QWidget *parent ) : QLabel( "I", parent ) +{ + QColorGroup cg = parentWidget()->colorGroup(); + QColor base = cg.base(); + QColor selection = cg.highlight(); + int r = (base.red() + selection.red()) / 2; + int b = (base.blue() + selection.blue()) / 2; + int g = (base.green() + selection.green()) / 2; + back = QColor(r, g, b); + fore = parentWidget()->colorGroup().text(); + setPaletteBackgroundColor( back ); + setPaletteForegroundColor( fore ); + setFrameStyle( QFrame::StyledPanel | QFrame::Sunken ); + title = " ##### "; + titleOffset = 0; + setTitle( title ); + connect( &titleTimer, SIGNAL( timeout() ), this, SLOT( rollTitle() ) ); + titleTimer.start( 50 ); +} + +void RollTitle::setTitle( MRL mrl ) +{ + title = " ##### "; + QString s1 = mrl.artist().stripWhiteSpace(); + QString s2 = mrl.album().stripWhiteSpace(); + QString s3 = mrl.title().stripWhiteSpace(); + if ( !s1.isEmpty() ) + title+= s1+" - "; + if ( !s2.isEmpty() ) + title+= s2+" - "; + if ( s3.isEmpty() ) + title+= mrl.url(); + else + title+= s3; + titleOffset = 0; + setTitle( title ); +} + +void RollTitle::setTitle( QString t ) +{ + QLabel *lab = new QLabel( t, this ); + lab->setFrameStyle( QFrame::StyledPanel | QFrame::Sunken ); + lab->resize( lab->sizeHint() ); + int x = 2*lab->frameWidth(); + titlePix = QPixmap( lab->width()-x, height()-x, -1, QPixmap::BestOptim ); + delete lab; + QPainter p( &titlePix ); + p.fillRect( 0, 0, titlePix.width(), titlePix.height(), back ); + p.setPen( fore ); + p.setFont( font() ); + p.drawText( QRect(0,(titlePix.height()-QFontInfo(font()).pointSize())/2,titlePix.width(),QFontInfo(font()).pointSize()*2), Qt::AlignAuto|Qt::SingleLine, t ); + p.end(); +} + +void RollTitle::rollTitle() +{ + int w = 2*frameWidth(); + QPixmap pix( width()-w, height()-w, -1, QPixmap::BestOptim ); + QPainter p1( &pix ); + p1.fillRect( 0, 0, pix.width(), pix.height(), back ); + p1.end(); + int x = titlePix.width()-titleOffset; + bitBlt( &pix, 0, 0, &titlePix, titleOffset, 0, x, titlePix.height(), CopyROP ); + while ( x<pix.width() ) { + bitBlt( &pix, x, 0, &titlePix, 0, 0, titlePix.width(), titlePix.height(), CopyROP ); + x+= titlePix.width(); + } + ++titleOffset; + if ( titleOffset>titlePix.width() ) + titleOffset = 0; + bitBlt( this, w/2, w/2, &pix, 0, 0, width()-w, height()-w, CopyROP ); +} + +void RollTitle::paintEvent( QPaintEvent *pe ) +{ + QLabel::paintEvent( pe ); + QColorGroup cg = parentWidget()->colorGroup(); + QColor base = cg.base(); + QColor selection = cg.highlight(); + int r = (base.red() + selection.red()) / 2; + int b = (base.blue() + selection.blue()) / 2; + int g = (base.green() + selection.green()) / 2; + back=QColor(r,g,b); + setPaletteBackgroundColor( back ); + fore = parentWidget()->colorGroup().text(); + setPaletteForegroundColor( fore ); + setTitle( title ); +} + + + +struct CoverPopup : public QWidget +{ + CoverPopup(const QPixmap &image, const QPoint &p) : + QWidget(0, 0, WDestructiveClose | WX11BypassWM) + { + QHBoxLayout *layout = new QHBoxLayout(this); + QLabel *label = new QLabel(this); + + layout->addWidget(label); + label->setFrameStyle(QFrame::Box | QFrame::Raised); + label->setLineWidth(1); + label->setPixmap(image); + + setGeometry(p.x(), p.y(), label->width(), label->height()); + show(); + } + virtual void leaveEvent(QEvent *) { close(); } + virtual void mouseReleaseEvent(QMouseEvent *) { close(); } +}; + +CoverFrame::CoverFrame( QWidget *parent ) : QFrame( parent ) +{ + imagePath = ""; + QString path = locate("appdata", "nocover.png"); + if ( !path ) + path = locate("data", "kaffeine/nocover.png"); + noCoverPix.convertFromImage( QImage(path) ); + setPaletteBackgroundPixmap( noCoverPix ); +} + +CoverFrame::~CoverFrame() +{ +} + +void CoverFrame::setCoverPixmap( const QString &path ) +{ + QPixmap pix; + QImage img; + + img = QImage( path ); + if ( !img.isNull() ) { + img = img.smoothScale( 80, 80 ); + pix.convertFromImage( img ); + setPaletteBackgroundPixmap( pix ); + imagePath = path; + } + else { + setPaletteBackgroundPixmap( noCoverPix ); + imagePath = ""; + } +} + +void CoverFrame::popup( const QPixmap &pix ) const +{ + QPoint mouse = QCursor::pos(); + QRect desktop = KApplication::desktop()->screenGeometry(mouse); + int x = mouse.x(); + int y = mouse.y(); + int height = pix.height() + 4; + int width = pix.width() + 4; + + // Detect the right direction to pop up (always towards the center of the + // screen), try to pop up with the mouse pointer 10 pixels into the image in + // both directions. If we're too close to the screen border for this margin, + // show it at the screen edge, accounting for the four pixels (two on each + // side) for the window border. + + if(x - desktop.x() < desktop.width() / 2) + x = (x - desktop.x() < 10) ? desktop.x() : (x - 10); + else + x = (x - desktop.x() > desktop.width() - 10) ? desktop.width() - width +desktop.x() : (x - width + 10); + + if(y - desktop.y() < desktop.height() / 2) + y = (y - desktop.y() < 10) ? desktop.y() : (y - 10); + else + y = (y - desktop.y() > desktop.height() - 10) ? desktop.height() - height + desktop.y() : (y - height + 10); + + new CoverPopup( pix, QPoint(x, y) ); +} + +void CoverFrame::mousePressEvent( QMouseEvent *e ) +{ + int i; + QPixmap pix; + + switch ( e->button() ) { + case LeftButton: { + if ( imagePath.isEmpty() ) + break; + pix.convertFromImage( QImage( imagePath ) ); + if ( !pix.isNull() ) + popup( pix ); + break; + } + case RightButton: { + QPopupMenu pop( this ); + pop.insertItem( i18n("Choose a Cover..."), 1 ); + pop.insertItem( i18n("Gallery..."), 2 ); + i = pop.exec( QCursor::pos() ); + if ( i==1 ) + emit changeCurrentCover(); + else if ( i==2 ) + emit gallery(); + break; + } + default: + break; + } +} + + +PlayList::PlayList( QWidget* parent, QObject *objParent, const char *name ) : KaffeineInput(objParent , name), + 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") +{ + google = NULL; + + mainWidget = new QVBox( parent ); + //mainWidget->setSizePolicy( QSizePolicy (QSizePolicy::Preferred, QSizePolicy::Preferred) ); + hSplit = new QSplitter( mainWidget ); + hSplit->setOpaqueResize( true ); + panel = new QVBox( hSplit ); + playlist = new QWidget( hSplit ); + hSplit->moveToFirst( panel ); + hSplit->setResizeMode( panel, QSplitter::KeepSize ); + + vSplit = new QSplitter( QSplitter::Vertical, panel ); + vSplit->setOpaqueResize( true ); + QWidget *vb = new QWidget( vSplit ); + KToolBar *tb = new KToolBar( vb ); + QVBoxLayout *v = new QVBoxLayout( vb, 0, 0 ); + v->addWidget( tb ); + tb->setIconSize( 16 ); + + browserComb = new KURLComboBox( KURLComboBox::Directories, true, vb ); + browserComb->setCompletionObject( new KURLCompletion( KURLCompletion::DirCompletion ) ); + browserComb->setAutoDeleteCompletionObject( true ); + browserComb->setMaxItems( 10 ); + browserComb->setFocusPolicy(QWidget::ClickFocus); + + + /* if (!m_medium) + m_combo->lineEdit()->setText( location->path() ); + else + m_combo->lineEdit()->setText( "/" );*/ + + QVBoxLayout *v1 = new QVBoxLayout( 0, 3, 3 ); + v1->addWidget( browserComb ); + fileBrowser = new KDirOperator( KURL("/"), vb ); + fileBrowser->setupMenu( KDirOperator::SortActions|KDirOperator::NavActions|KDirOperator::ViewActions ); + view = new KFileIconView( fileBrowser, "view" ); + view->setSelectionMode( KFile::Multi ); + fileBrowser->setMode( KFile::Files ); + fileBrowser->setView( view ); + fileBrowser->setViewConfig( KGlobal::config(), "PlaylistFileBrowser" ); + fileBrowser->setOnlyDoubleClickSelectsFiles( true ); + v1->addWidget( fileBrowser ); + v->addLayout( v1 ); + + KActionCollection *col = fileBrowser->actionCollection(); + col->action( "up" )->plug( tb ); + col->action( "back" )->plug( tb ); + col->action( "forward" )->plug( tb ); + col->action( "reload" )->plug( tb ); + col->action( "home" )->plug( tb ); + + vSplit->moveToFirst( vb ); + playerBox = new QVBox( vSplit ); + playerBox->setMinimumHeight( 50 ); + playerBox->setMinimumWidth( 200 ); + vSplit->moveToLast( playerBox ); + + m_playlistDirectory = locateLocal("appdata", "playlists"); + m_playlistDirectory.append("/"); + KIO::NetAccess::mkdir(m_playlistDirectory, mainWidget); + kdDebug() << "PLAYLIST" << endl; + + QGridLayout* layout = new QGridLayout( playlist, 4, 2, 3 ); + + m_list = new UrlListView(playlist); + mainWidget->setAcceptDrops(true); + m_list->setHScrollBarMode(KListView::AlwaysOff); + m_list->setItemMargin(2); + m_list->setMargin(0); + 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); + KConfig* config = kapp->config(); + m_list->restoreLayout(config, "Playlist Layout"); + m_list->setFullWidth(true); + m_list->setSorting(-1); + m_list->setDragEnabled(true); + m_list->setAcceptDrops(true); + m_list->setDropVisualizer(true); + m_list->setItemsMovable(true); + 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); + m_list->setShowSortIndicator(true); + layout->addMultiCellWidget(m_list, 3, 3, 0, 1); + + coverFrame = new CoverFrame( playlist ); + coverFrame->setFixedWidth( 80 ); + coverFrame->setFixedHeight( 80 ); + layout->addMultiCellWidget(coverFrame, 0, 2, 0, 0); + + QHBoxLayout *h1 = new QHBoxLayout(); + searchBtn = new QToolButton( playlist ); + searchBtn->setAutoRaise( true ); + QToolTip::add( searchBtn, i18n("Search")); + searchBtn->setIconSet( KGlobal::iconLoader()->loadIconSet("locationbar_erase", KIcon::Small) ); + h1->addWidget( searchBtn ); + QLabel* filterLabel = new QLabel(i18n("Filter") + ":", playlist); + h1->addWidget(filterLabel); + m_playlistFilter = new KLineEdit(playlist); + m_playlistFilter->setFocusPolicy(QWidget::ClickFocus); + h1->addWidget(m_playlistFilter); + layout->addLayout( h1, 2, 1 ); + + QHBoxLayout *h2 = new QHBoxLayout(); + QLabel* playlistLabel = new QLabel(i18n("Playlist:"), playlist); + h2->addWidget(playlistLabel); + m_playlistSelector = new KComboBox( playlist ); + m_playlistSelector->setMinimumWidth(10); + m_playlistSelector->setSizePolicy( QSizePolicy (QSizePolicy::MinimumExpanding, QSizePolicy::Preferred) ); + m_playlistSelector->setEditable(true); + m_playlistSelector->setDuplicatesEnabled(false); + m_playlistSelector->setFocusPolicy(QWidget::ClickFocus); + QToolTip::add(m_playlistSelector, i18n("Select the active playlist. To change playlist name edit it and confirm with 'Return'.")); + h2->addWidget(m_playlistSelector); + layout->addLayout( h2, 1, 1 ); + + roller = new RollTitle( playlist ); + layout->addWidget( roller, 0, 1 ); + + KAccel* accel = new KAccel(mainWidget); + accel->insert("Delete selected", Qt::Key_Delete, this, SLOT(slotRemoveSelected())); + + m_isCurrentEntry = UserIcon("playing"); + m_cdPixmap = KGlobal::iconLoader()->loadIcon("cdtrack", KIcon::Small); + + setXMLFile("kaffeineplaylist.rc"); + setupActions(); + + QStrList formats = QImageIO::outputFormats(); + coverImageFormat = "PNG"; + for (int i=0; i<(int)formats.count(); i++ ) { + if ( QString(formats.at(i))=="JPEG" ) { + coverImageFormat = "JPEG"; + break; + } + } + + loadConfig( KGlobal::config() ); + + connect(coverFrame, SIGNAL(changeCurrentCover()), this, SLOT(chooseCurrentCover())); + connect(coverFrame, SIGNAL(gallery()), this, SLOT(showGallery())); + connect(browserComb, SIGNAL(urlActivated( const KURL& )), this, SLOT(setBrowserURL( const KURL& ))); + connect(browserComb, SIGNAL(returnPressed( const QString& )), this, SLOT(setBrowserURL( const QString& ))); + connect(fileBrowser, SIGNAL(urlEntered( const KURL& )), this, SLOT(browserURLChanged( const KURL& ))); + connect(fileBrowser, SIGNAL(fileSelected(const KFileItem*)), this, SLOT(fileSelected(const KFileItem*))); + connect(m_playlistFilter, SIGNAL(textChanged(const QString&)), this, SLOT(slotFindText(const QString&))); + connect( searchBtn, SIGNAL(clicked()), this, SLOT(resetSearch()) ); + connect(m_playlistSelector, SIGNAL(returnPressed(const QString&)), this, SLOT(slotNewPlaylistName(const QString&))); + connect(m_playlistSelector, SIGNAL(activated(int)),this, SLOT(slotPlaylistActivated(int))); + connect(m_list, SIGNAL(dropped(QDropEvent*, QListViewItem*)), this, SLOT(slotDropEvent(QDropEvent*, QListViewItem*))); + connect(m_list, SIGNAL(aboutToMove()), this, SLOT(slotPreDropEvent())); + 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))); +} + +void PlayList::togglePanel() +{ + if ( panel->isHidden() ) + panel->show(); + else + panel->hide(); +} + +void PlayList::getTargets( QStringList &uiNames, QStringList &iconNames, QStringList &targetNames ) +{ + uiNames.append( i18n("Play Playlist") ); + iconNames.append( "view_text" ); + targetNames.append( "PLAYLIST" ); +} + +bool PlayList::execTarget( const QString &target ) +{ + if ( target=="PLAYLIST" ) { + emit showMe( this ); + QTimer::singleShot( 100, this, SLOT(startPlaylist()) ); + return true; + } + return false; +} + +void PlayList::setFileFilter( const QString& filter ) +{ + m_fileFilter = filter; + fileBrowser->setNameFilter( m_fileFilter ); +} + +PlayList::~PlayList() +{ + delete m_list; +} + +void PlayList::setupActions() +{ + m_repeat = new KToggleAction(i18n("&Repeat"), 0 , CTRL|Key_E, this, SLOT(slotRepeat()), actionCollection(),"playlist_repeat"); + m_repeat->setStatusText(i18n("Loop playlist")); + m_shuffle = new KToggleAction(i18n("Sh&uffle"), 0 , CTRL|Key_R, this, SLOT(slotShuffle()), actionCollection(),"playlist_shuffle"); + m_shuffle->setStatusText(i18n("Play items in random order")); + m_autoCover = new KToggleAction(i18n("Autodownload covers"), 0 , 0, this, SLOT(slotAutoCover()), actionCollection(),"playlist_autocover"); + m_autoCover->setStatusText(i18n("Automatic dowloading of covers")); + m_showPlayer = new KToggleAction(i18n("Don't switch to player window"), 0 , 0, this, SLOT(slotShowPlayer()), actionCollection(),"playlist_showplayer"); + m_showPlayer->setStatusText(i18n("Don't switch automatically to player window")); + new KAction(i18n("&Clear Current Playlist"), "eraser", 0, this, SLOT(slotClearList()), actionCollection(), "playlist_clear"); + new KAction(i18n("Ne&w Playlist"), "filenew", 0, this, SLOT(slotNewList()), actionCollection(), "playlist_new"); + new KAction(i18n("&Import Playlist..."), "project_open", 0, this, SLOT(slotPlaylistLoad()), actionCollection(), "playlist_load"); + new KAction(i18n("&Save Current Playlist As..."), "filesaveas", 0, this, SLOT(slotPlaylistSaveAs()), actionCollection(), "playlist_save_as"); + new KAction(i18n("Re&move Current Playlist"), "fileclose", 0, this, SLOT(slotPlaylistRemove()), actionCollection(), "playlist_remove"); +} + +void PlayList::slotClearList() +{ + clearList(); +} + +void PlayList::slotRepeat() +{ + setEndless(m_repeat->isChecked()); +} + +void PlayList::slotShuffle() +{ + setRandom(m_shuffle->isChecked()); +} + +void PlayList::slotAutoCover() +{ + m_cover = m_autoCover->isChecked(); +} + +void PlayList::slotShowPlayer() +{ +} + +void PlayList::slotToggleShuffle() +{ + m_shuffle->setChecked(!m_shuffle->isChecked()); + slotShuffle(); +} + +void PlayList::slotPlaylistLoad() +{ + QString path = KFileDialog::getOpenFileName(":kaffeine_openPlaylist", QString("*.kaffeine|") + i18n("Kaffeine Playlists") + "\n*.*|" + i18n("All Files"), 0, i18n("Open Playlist")); + if (path.isEmpty() || (!path.contains(".kaffeine", false))) + return; + + loadPlaylist(path); +} + +void PlayList::slotPlaylistSaveAs() +{ + QString startPath = ":kaffeine_savePlaylist"; + //if (!lastPlaylist.isEmpty()) startPath = lastPlaylist; + QString path = KFileDialog::getSaveFileName(startPath, + QString("*.kaffeine|") + i18n("Kaffeine Playlists") + "\n" + + QString("*.m3u|") + i18n("M3U Playlists") + "\n" + + QString("*.pls|") + i18n("PLS Playlists") + "\n" + + "*.*|" + i18n("All Files"), 0, i18n("Save Playlist")); + + if ( path.isEmpty() ) + return; + if ( path.right(4).lower() == ".m3u" ) { + exportM3UPlaylist(path); + } else if ( path.right(4).lower() == ".pls" ) { + exportPLSPlaylist(path); + } else if ( path.right(9).lower() == ".kaffeine" ) { + savePlaylist(path); + } else { + path.append(".kaffeine"); + savePlaylist(path); + } +} + +void PlayList::slotPlaylistRemove() +{ + removeCurrentPlaylist(); +} + +QWidget* PlayList::wantPlayerWindow() +{ + return playerBox; +} + +QWidget* PlayList::inputMainWidget() +{ + return mainWidget; +} + +void PlayList::loadConfig(KConfig* config) +{ + QValueList<int> sl; + bool b; + + config->setGroup("Playlist"); + b = config->readBoolEntry("Endless Mode", false); + m_repeat->setChecked(b); + setEndless(b); + b = config->readBoolEntry("Random Mode", false); + m_shuffle->setChecked(b); + setRandom(b); + b = config->readBoolEntry("AutoCover", false); + m_autoCover->setChecked(b); + m_cover = b; + b = config->readBoolEntry("DontShowPlayer", false); + m_showPlayer->setChecked(b); + + sl = config->readIntListEntry("HSplitSizes"); + if ( sl.count() ) + hSplit->setSizes( sl ); + else { + sl.append( 200 ); + sl.append( 200 ); + hSplit->setSizes( sl ); + } + vSplit->setSizes( config->readIntListEntry("VSplitSizes") ); + QStringList list; + list = config->readListEntry("Playlists"); + if (!list.count()) + { + list.append(i18n("Playlist") + "1"); + } + int lastRegularPlaylist = list.count(); + list.append(i18n("NEW")); + m_playlistSelector->insertStringList(list); + m_currentPlaylist = config->readNumEntry("Current", 0); + if (m_currentPlaylist > lastRegularPlaylist) + m_currentPlaylist = 0; + m_playlistSelector->setCurrentItem(m_currentPlaylist); + 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); + } + fileBrowser->readConfig( config, "PlaylistFileBrowser" ); + fileBrowser->setURL( KURL(config->readEntry( "FileBrowserURL", "/" )), true ); + browserComb->setURL( KURL(config->readEntry( "FileBrowserURL", "/" )) ); +} + +void PlayList::saveConfig() +{ + saveConfig( KGlobal::config() ); +} + +void PlayList::saveConfig(KConfig* config) +{ + saveCurrentPlaylist(); + config->setGroup("Playlist"); + config->writeEntry("Endless Mode", m_repeat->isChecked()); + config->writeEntry("Random Mode", m_shuffle->isChecked()); + config->writeEntry("AutoCover", m_autoCover->isChecked()); + config->writeEntry("DontShowPlayer", m_showPlayer->isChecked()); + config->writeEntry( "HSplitSizes", hSplit->sizes() ); + config->writeEntry( "VSplitSizes", vSplit->sizes() ); + QStringList list; + QString text; + for (uint i=0; i < m_playlistSelector->listBox()->count(); i++) + { + text = m_playlistSelector->text(i); + if ( 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()); + m_list->saveLayout(config, "Playlist Layout"); + fileBrowser->writeConfig( config, "PlaylistFileBrowser" ); + config->writeEntry( "FileBrowserURL", fileBrowser->url().url() ); +} + +void PlayList::closeEvent(QCloseEvent* ce) +{ + kdDebug() << "Playlist: close Event" << endl; + ce->ignore(); +} + +/****************************************** + * get the urls from playlist + ******************************************/ +void PlayList::startPlaylist() +{ + MRL mrl; + if ( trackNumber( 1, mrl ) ) + emit play( mrl, this ); +} + +bool PlayList::currentTrack( MRL &mrl ) +{ + mrl = getCurrent(); + if ( !mrl.isEmpty() ) + return true; + else + return false; +} + +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; +} + +bool PlayList::playbackFinished( MRL &mrl ) +{ + return nextTrack( mrl ); +} + +bool PlayList::nextTrack( MRL &mrl ) +{ + if (isQueueMode()) + { + mrl = m_queue.first(); + m_queue.remove(m_queue.begin()); + updateStatus(); + return true; + } + + if (!m_currentEntry) { + mrl = getCurrent(); + if ( !mrl.isEmpty() ) + return true; + else + return false; + } + + if (m_random) + { + if ((m_currentRandomListEntry+1) < (int)m_randomList.count()) + m_currentRandomListEntry += 1; + else + { + if (m_endless) + m_currentRandomListEntry = 0; + else + return false; + } + setCurrentEntry(m_randomList.at(m_currentRandomListEntry)); + mrl = m_currentEntryMRL; + return true; + } + + QListViewItem* tmpItem; + tmpItem = m_currentEntry->itemBelow(); + + if (tmpItem) + { + setCurrentEntry(tmpItem); + mrl = m_currentEntryMRL; + return true; + } + else + { + if (m_endless) + { + setCurrentEntry(m_list->firstChild()); + mrl = m_currentEntryMRL; + return true; + } + else + return false; + } +} + +bool PlayList::previousTrack( MRL &mrl ) +{ + if (isQueueMode()) + { + m_queue.clear(); + updateStatus(); + } + + if (!m_currentEntry) { + mrl = getCurrent(); + if ( !mrl.isEmpty() ) + return true; + else + return false; + } + + if (m_random) { + if (m_currentRandomListEntry > 0) + m_currentRandomListEntry -= 1; + else { + if (m_endless) + m_currentRandomListEntry = m_randomList.count()-1; + else + return false; + } + setCurrentEntry(m_randomList.at(m_currentRandomListEntry)); + mrl = m_currentEntryMRL; + return true; + } + + QListViewItem* tmpItem; + tmpItem = m_currentEntry->itemAbove(); + + if (tmpItem) { + setCurrentEntry(tmpItem); + mrl = m_currentEntryMRL; + return true; + } + else { + if (m_endless) { + setCurrentEntry(getLast()); + mrl = m_currentEntryMRL; + return true; + } + else + return false; + } +} + +bool PlayList::trackNumber( int number, MRL &mrl ) +{ + if (number > m_list->childCount()) + return false; + + QListViewItem * item = m_list->firstChild(); + for (int i = 1; i < number; i++) + item = item->nextSibling(); + + setCurrentEntry(item); + mrl = m_currentEntryMRL; + return true; +} + +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) +{ + PlaylistItem* newItem = dynamic_cast<PlaylistItem*>(item); + PlaylistItem* oldItem = dynamic_cast<PlaylistItem*>(m_currentEntry); + if (m_currentEntry) + { + oldItem->setPlaying(false); + m_currentEntry->setPixmap(TITLE_COLUMN, QPixmap()); + } + if (playIcon) + { + newItem->setPlaying(true); + item->setPixmap(TITLE_COLUMN, m_isCurrentEntry); + } + m_currentEntry = item; + if (m_random) + m_currentRandomListEntry = m_randomList.find(item); + m_currentEntryMRL = newItem->toMRL(); + roller->setTitle( m_currentEntryMRL ); + 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); + + m_list->setSorting(-1); + 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::fileSelected( const KFileItem *item ) +{ + if ( !item ) + return; + add( QStringList( item->url().path() ), NULL ); + QListViewItem* tmp = findByURL(item->url().path()); + if (tmp) { + setCurrentEntry(tmp); + emit play(m_currentEntryMRL, this); + } +} + +void PlayList::setBrowserURL( const QString &path ) +{ + fileBrowser->setURL( KURL(path), true ); +} + +void PlayList::setBrowserURL( const KURL &path ) +{ + fileBrowser->setURL( path, true ); +} + +void PlayList::browserURLChanged( const KURL &u ) +{ + QString url; + + if ( u.isLocalFile() ) + url = u.path(); + else + url = u.prettyURL(); + + QStringList urls = browserComb->urls(); + urls.remove( url ); // do not duplicate + urls.prepend( url ); + browserComb->setURLs( urls, KURLComboBox::RemoveBottom ); +} + +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; + QValueList<MRL> mrlList; + QString mediadevice=""; + + KProgressDialog* progress = NULL; + progress = new KProgressDialog(mainWidget, "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]); + QString url(mrl.kurl().prettyURL()); + + if (mrl.kurl().protocol() == "media" || url.startsWith("system:/media/")) + { + QString mediaName; + if (url.startsWith("system:/media/")) + mediaName = url.mid(14); + else + mediaName = url.mid(7); + int slash = mediaName.find("/"); + QString filePath(mediaName.mid(slash)); + if (filePath == "/") + filePath = ""; + mediaName = mediaName.left(slash); + + DCOPRef mediamanager("kded","mediamanager"); + DCOPReply reply = mediamanager.call("properties(QString)",mediaName); + if (reply.isValid()) + { + QStringList properties = reply; + if (properties.isEmpty()) break; + url = properties[6] + filePath; + if (filePath.isEmpty()) + { + if (properties[10] == "media/audiocd") + { + mediadevice = properties[5]; + delete progress; + emit signalRequestForAudioCD(mediadevice); + return; + } + if (properties[10] == "media/dvdvideo") + { + mediadevice = properties[5]; + delete progress; + emit signalRequestForDVD(mediadevice); + return; + } + if ((properties[10] == "media/vcd") || (properties[10] == "media/svcd")) + { + mediadevice = properties[5]; + delete progress; + emit signalRequestForVCD(mediadevice); + return; + } + } + mrl = MRL(url); + } + } + + if (mrl.kurl().isLocalFile()) + { + if (!QFile::exists(mrl.kurl().path())) + continue; + mrl.setTitle(mrl.kurl().fileName()); + mrl.setURL(mrl.kurl().path()); + } + 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()); + + /******** 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)) || (ext == "pls") ) + { + kdDebug() << "PlayList: Try loading pls playlist\n"; + playlist = PlaylistImport::pls(mrl.url(), mrlList); + } + if (ext == "m3u") //identify by extension + { + kdDebug() << "PlayList: Try loading m3u playlist\n"; + playlist = PlaylistImport::m3u(mrl.url(), mrlList); + } + if (playlist) + { + QValueList<MRL>::ConstIterator end(mrlList.end()); + for (QValueList<MRL>::ConstIterator it = mrlList.begin(); it != 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, mainWidget->parentWidget())) + { + QValueList<MRL>::ConstIterator end(mrlList.end()); + for (QValueList<MRL>::ConstIterator it = mrlList.begin(); it != 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(), mainWidget); + 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; + QStringList::Iterator end(dirListing.end()); + for(QStringList::Iterator it = dirListing.begin(); it != 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; + } + } + + if((subs.count() > 1)) { + subs.prepend(i18n("(no subtitles)")); + subs.append(i18n("Other subtitle...")); + subtitleURL = QString::null; + SubtitleChooser *sc = new SubtitleChooser(subs, filename, mainWidget); + + 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 Subtitle File")).path(); + if(subtitleURL.isEmpty()) + { + 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 ( subs.count()==1 ) + subtitleURL = subs[0]; // Use this one. + 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 QValueList<MRL>& mrlList, QListViewItem* after) +{ + QValueList<MRL>::ConstIterator end(mrlList.end()); + for (QValueList<MRL>::ConstIterator it = mrlList.begin(); it != end; ++it) + after = insertItem(after, *it); + updateStatus(); + if (m_random) createRandomList(); +} + +void PlayList::slotPlayDirect(QListViewItem* item) +{ + if (!item) return; + + setCurrentEntry(item); + emit play(m_currentEntryMRL, this); +} + +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; + + m_list->setSorting(-1); + 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; + newurls << urls[i]; + kdDebug() << "PlayList: Dropped " << urls[i] << 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::slotPreDropEvent() +{ + m_list->setSorting(-1); +} + +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.ascii()); + 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.ascii(), title.length()) < 0)) + { + mrl.setTitle(altCodec->toUnicode(title.ascii())); + //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.ascii(),artist.length()) < 0 )) + { + mrl.setArtist(altCodec->toUnicode(artist.ascii())); + } + 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.ascii(),album.length()) < 0 )) + { + mrl.setAlbum(altCodec->toUnicode(album.ascii())); + } + 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(); + } + } + + roller->setTitle( mrl ); + setCover( mrl.artist().stripWhiteSpace(), mrl.album().stripWhiteSpace() ); +} + +GalleryDialog::GalleryDialog( QWidget *parent, GoogleFetcher *goog ) + : KDialogBase( parent, QString(i18n("Gallery")).latin1(), true, QString::null, Ok, NoDefault, true ) +{ + google = goog; + hbox = new QHBox(this); + imageFrame = new QFrame( hbox ); + imageFrame->setFixedSize(500,500); + setMainWidget(hbox); + connect( &next, SIGNAL(timeout()), this, SLOT(slotNext()) ); + next.start(1000,true); +} + +void GalleryDialog::slotNext() +{ + QPixmap pix; + bool end; + + actionButton(Ok)->setEnabled(false); +loop: + QImage img = google->galleryNext( end ); + if ( end ) { + slotOk(); + return; + } + if ( !img.isNull() ) { + if ( img.width()>img.height() ) + imageFrame->setFixedSize(500, 500*img.height()/img.width()); + else + imageFrame->setFixedSize(500*img.width()/img.height(), 500); + img = img.smoothScale(imageFrame->width(),imageFrame->height()); + pix.convertFromImage( img ); + imageFrame->setPaletteBackgroundPixmap( pix ); + } + else + goto loop; + + actionButton(Ok)->setEnabled(true); + next.start(5000,true); +} + +void GalleryDialog::slotOk() +{ + if ( next.isActive() ) + next.stop(); + hide(); +} + +void PlayList::showGallery() +{ + if ( google ) + return; + QString s = getCurrent().artist().stripWhiteSpace(); + if ( s.isEmpty() ) + return; + google = new GoogleFetcher( s, "", true ); + GalleryDialog dlg( 0, google ); + dlg.exec(); + delete google; + google = NULL; +} + +void PlayList::setCover( QString s1, QString s2, bool forceFetch ) +{ + if ( s1.isEmpty() || s2.isEmpty() ) { + coverFrame->setCoverPixmap( "" ); + return; + } + QString s = locateLocal("appdata", ""); + QImage img; + QPixmap pix; + QString fname = s1+"_"+s2; + fname = fname.replace( QChar( '/' ), "_" ); + fname = fname.upper(); + fname = s+"covers/"+fname; + if ( !forceFetch && QFile( fname ).exists() ) + coverFrame->setCoverPixmap( fname ); + else { + if ( !forceFetch ) + coverFrame->setCoverPixmap( "" ); + if ( google ) + return; + if ( m_cover || forceFetch ) { + google = new GoogleFetcher( s1, s2 ); + pix = google->pixmap( forceFetch ); + delete google; + google = NULL; + } + if ( !QDir( s+"covers" ).exists() ) + QDir().mkdir( s+"covers" ); + if ( !pix.isNull() ) { + img = pix.convertToImage(); + img.save( fname, coverImageFormat.latin1() ); + MRL mrl = getCurrent(); + if ( s1==mrl.artist().stripWhiteSpace() && s2==mrl.album().stripWhiteSpace() ) + coverFrame->setCoverPixmap( fname ); + } + else if ( !forceFetch && m_cover ) { + QString path = locate("appdata", "nocover.png"); + if ( !path ) + path = locate("data", "kaffeine/nocover.png"); + KIO::file_copy( KURL::fromPathOrURL( path ), KURL::fromPathOrURL( fname ), -1, true ); + } + } +} + +void PlayList::chooseCurrentCover() +{ + MRL mrl = getCurrent(); + setCover( mrl.artist().stripWhiteSpace(), mrl.album().stripWhiteSpace(), true ); +} + +/************ sort playlist ******************/ + +void PlayList::slotSort(int section) +{ + m_list->setSorting(section, m_sortAscending); + m_list->sort(); + 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; + QValueList<MRL>::ConstIterator end(m_queue.end()); + for (QValueList<MRL>::ConstIterator it = m_queue.begin(); it != 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 statusBarMessage(status); +} + +void PlayList::slotNewList() +{ + m_list->setSorting(-1); + 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; +} + +void PlayList::nextPlaylist() +{ + int nextPlaylist = m_playlistSelector->currentItem(); + QString pl = NULL; + do + { + nextPlaylist++; + if (nextPlaylist == m_playlistSelector->count()) + nextPlaylist = 0; + pl = m_playlistSelector->text(nextPlaylist); + } + while (nextPlaylist != m_playlistSelector->currentItem()); + + saveCurrentPlaylist(); + clearList(); + m_playlistSelector->setCurrentItem(nextPlaylist); + m_currentPlaylist = nextPlaylist; + add(m_playlistDirectory + pl + ".kaffeine", NULL); + if (mainWidget->parentWidget()) + mainWidget->parentWidget()->setFocus(); +} + +void PlayList::slotPlaylistActivated(int index) +{ + kdDebug() << "PlayList: Switch to playlist: " << m_playlistSelector->text(index) << endl; + QString pl = m_playlistSelector->text(index); + + saveCurrentPlaylist(); + clearList(); + m_currentPlaylist = index; + add(m_playlistDirectory + pl + ".kaffeine", NULL); + if (mainWidget->parentWidget()) + mainWidget->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, mainWidget)) + kdError() << "Playlist: " << KIO::NetAccess::lastErrorString() << endl; + + kdDebug() << "PlayList: Set playlist name to: " << text << endl; + m_playlistSelector->changeItem(text, m_currentPlaylist); + saveCurrentPlaylist(); + if (mainWidget->parentWidget()) + mainWidget->parentWidget()->setFocus(); +} + +QString PlayList::queryCurrentPlaylist() +{ + QString pl = m_playlistSelector->text(m_currentPlaylist); + return (m_playlistDirectory + pl + ".kaffeine"); +} + +void PlayList::saveCurrentPlaylist() +{ + QString pl = m_playlistSelector->text(m_currentPlaylist); + savePlaylist(m_playlistDirectory + pl + ".kaffeine"); +} + +void PlayList::removeCurrentPlaylist() +{ + int code = KMessageBox::warningContinueCancel(0, i18n("Remove '%1' from list and from disk?").arg(m_playlistSelector->text(m_currentPlaylist)),QString::null,KStdGuiItem::del()); + if (code == KMessageBox::Continue) + { + QString pl = m_playlistDirectory + m_playlistSelector->text(m_currentPlaylist) + ".kaffeine"; + if (!KIO::NetAccess::del(pl, mainWidget)) + kdError() << "Playlist: " << KIO::NetAccess::lastErrorString() << endl; + + m_playlistSelector->removeItem(m_currentPlaylist); + m_currentPlaylist = m_playlistSelector->currentItem(); + 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); + if (m_playlistSelector->listBox()->findItem(plName)) + { + QString plNewName = NULL; + while (true) + { + bool ok; + plNewName = QInputDialog::getText(i18n("Playlist Name Already Exists"), + i18n("Enter different playlist name:"), QLineEdit::Normal, plName, &ok); + if ((ok) && (!plNewName.isEmpty())) + { + if (m_playlistSelector->listBox()->findItem(plNewName)) + continue; + else + break; + } + else + { + kdDebug() << "Playlist: Import aborted, playlist not added" << endl; + return; + } + } + plName = plNewName; + } + m_playlistSelector->insertItem(plName, 0); + m_playlistSelector->setCurrentItem(0); + 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::exportM3UPlaylist(const QString& pl) +{ + PlaylistItem * tmp = NULL; + + QListViewItemIterator it(m_list); + + int length = -1; + QString m3uList = "#EXTM3U\n"; + + while (it.current()) + { + tmp = dynamic_cast<PlaylistItem *>(it.current()); + + length = timeStringToMs(tmp->length()) / 1000; + if ( length == 0 ) + length = -1; + + m3uList.append("#EXTINF:" + QString::number(length) + "," + tmp->title()); + if (tmp->artist().isEmpty() ) + m3uList.append("\n"); + else + m3uList.append(" - " + tmp->artist() + "\n"); + m3uList.append(tmp->url() + "\n"); + ++it; + } + + QFile file(pl); + if (!file.open(IO_WriteOnly)) return; + QTextStream stream(&file); + stream.setEncoding(QTextStream::UnicodeUTF8); + + stream << m3uList; + + file.close(); +} + +void PlayList::exportPLSPlaylist(const QString& pl) +{ + PlaylistItem * tmp = NULL; + + QListViewItemIterator it(m_list); + + int counter = 0; + int length = -1; + + QString plsList = "[playlist]\n"; + + while (it.current()) + { + tmp = dynamic_cast<PlaylistItem *>(it.current()); + ++counter; + + length = timeStringToMs(tmp->length()) / 1000; + + if ( length == 0 ) { + length = -1; + } + + plsList.append("File" + QString::number(counter) + "=" + tmp->url() + "\n"); + plsList.append("Title" + QString::number(counter) + "=" + tmp->title()); + if (tmp->artist().isEmpty() ) + plsList.append("\n"); + else + plsList.append(" - " + tmp->artist() + "\n"); + plsList.append("Length" + QString::number(counter) + "=" + QString::number(length) + "\n"); + ++it; + + } + + plsList.append("NumberOfEntries=" + QString::number(counter) + "\n"); + plsList.append("Version=2"); + + QFile file(pl); + if (!file.open(IO_WriteOnly)) return; + QTextStream stream(&file); + stream.setEncoding(QTextStream::UnicodeUTF8); + + stream << plsList; + + file.close(); +} + +void PlayList::resetSearch() +{ + m_playlistFilter->clear(); + slotFindText( "" ); +} + +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(); + + QValueList<MRL> 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 + if (!ok) + return 0; + sec += tokens[1].toInt(&ok)*60; //minutes + if (!ok) + return 0; + 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 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 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(); +} diff --git a/kaffeine/src/input/audiobrowser/playlist.h b/kaffeine/src/input/audiobrowser/playlist.h new file mode 100644 index 0000000..41df120 --- /dev/null +++ b/kaffeine/src/input/audiobrowser/playlist.h @@ -0,0 +1,336 @@ +/* + * playlist.h + * + * Copyright (C) 2003-2005 Jürgen Kofler <kaffeine@gmx.net> + * Copyright (C) 2005-2006 Christophe Thommeret <hftom@free.fr> + * + * 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. + * + * This program 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 General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program; if not, write to the Free Software + * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA + */ + +#ifndef PLAYLIST_H +#define PLAYLIST_H + +#include <kdialogbase.h> +#include <kdiroperator.h> +#include <kfileiconview.h> +#include <kaction.h> +#include <kstdaction.h> + +#include <qwidget.h> +#include <qptrlist.h> +#include <qvbox.h> +#include <qsplitter.h> +#include <qpainter.h> +#include <qtoolbutton.h> + +#include "urllistview.h" +#include "kaffeineinput.h" + +class UrlListView; +class MRL; +class KURL; +class QListViewItem; +class QString; +class QLabel; +class QColor; +class QDropEvent; +class QPixmap; +class KLineEdit; +class KComboBox; +class KURLComboBox; +class KConfig; +class KPushButton; +class GoogleFetcher; + + + +class GalleryDialog : public KDialogBase +{ + Q_OBJECT +public: + GalleryDialog( QWidget *parent, GoogleFetcher *goog ); +protected slots: + void slotOk(); + void slotNext(); +private: + QFrame *imageFrame; + GoogleFetcher *google; + QTimer next; + QHBox *hbox; +}; + + + +class RollTitle : public QLabel +{ + Q_OBJECT + +public: + RollTitle( QWidget *parent ); + void setTitle( MRL mrl ); + +private: + void setTitle( QString t ); + QTimer titleTimer; + QPixmap titlePix; + QColor back, fore; + int titleOffset; + QString title; + +protected: + void paintEvent ( QPaintEvent * ); + +private slots: + void rollTitle(); +}; + + + +class CoverFrame : public QFrame +{ + Q_OBJECT + +public: + CoverFrame( QWidget *parent ); + ~CoverFrame(); + void setCoverPixmap( const QString &path ); + +protected: + void mousePressEvent( QMouseEvent *e ); + +private: + void popup( const QPixmap &pix ) const; + QString imagePath; + QPixmap noCoverPix; + +signals: + void changeCurrentCover(); + void gallery(); +}; + + + +class PlayList : public KaffeineInput +{ + Q_OBJECT + +public: + PlayList(QWidget *parent, QObject *objParent, const char *name=0); + ~PlayList(); + + // Reimplemented from KaffeineInput +public: + QWidget *wantPlayerWindow(); + QWidget *inputMainWidget(); + void mergeMeta(const MRL&); + bool nextTrack( MRL& ); + bool previousTrack( MRL& ); + bool currentTrack( MRL& ); + bool trackNumber( int, MRL& ); + bool playbackFinished( MRL& ); + void getTargets( QStringList &uiNames, QStringList &iconNames, QStringList &targetNames ); + void togglePanel(); + bool execTarget( const QString& ); + void saveConfig(); + bool showPlayer() {return !m_showPlayer->isChecked();} + //*************************************** + +public: + MRL getCurrent(); /* get current playlist entry */ + MRL getNext(); + MRL getEntryWithNumber(int number); + void setCurrentEntry(QListViewItem*, bool playIcon = true); + + QListViewItem* getLast(); + QListViewItem* getFirst(); + QListViewItem* findByURL(const QString&); + + /* insert a KURL(list) after a specific item */ + void add(const QString& url, QListViewItem* after); + void add(const QStringList& urls, QListViewItem* after); + void add(const QValueList<MRL>&, QListViewItem* after); + + bool isQueueMode() { return m_queue.count(); } + + void setPlaylist(const QString& name, bool clear = false); + void nextPlaylist(); + void removeCurrentPlaylist(); + void saveCurrentPlaylist(); + QString queryCurrentPlaylist(); + void loadConfig(KConfig*); + void saveConfig(KConfig*); + void clearList(); + + void setEndless(bool); + void setRandom(bool); + + void useAlternateEncoding(bool); + void setAlternateEncoding(const QString&); + + void setFileFilter(const QString& filter); + void savePlaylist(const QString&); + void loadPlaylist(const QString&); + + void exportM3UPlaylist(const QString&); + void exportPLSPlaylist(const QString&); + + const bool getReadMetaOnLoading() const { return m_metaOnLoading; } + + QVBox *mainWidget; + QVBox *playerBox; + +public slots: + void slotToggleShuffle(); + +protected: + void closeEvent(QCloseEvent*); + +private slots: + void slotNewList(); + void slotSetReadMetaOnLoading(bool read) { m_metaOnLoading = read; } + void slotSetAlternateColor(const QColor&); + void slotPlayDirect(QListViewItem* item); /* doubleclick */ + void slotDropEvent(QDropEvent*, QListViewItem*); + void slotPreDropEvent(); + void slotCut(); + void slotPaste(); + void slotCopy(); + void slotSelectAll(); + void slotPlaylistFromSelected(); + void slotAddToQueue(MRL); + void slotRemoveSelected(); + void slotFindText(const QString&); + void slotSort(int); + void slotPlaylistActivated(int); + void slotNewPlaylistName(const QString&); + void slotRepeat(); + void slotShuffle(); + void slotAutoCover(); + void slotShowPlayer(); + void slotPlaylistLoad(); + void slotPlaylistSaveAs(); + void slotPlaylistRemove(); + void setCover( QString s1, QString s2, bool forceFetch=false ); + void chooseCurrentCover(); + void showGallery(); + void startPlaylist(); + void fileSelected( const KFileItem* ); + void setBrowserURL( const QString& ); + void setBrowserURL( const KURL& ); + void browserURLChanged( const KURL& ); + void slotClearList(); + void resetSearch(); + +private: + QListViewItem* insertItem(QListViewItem* after, const MRL&); + void updateStatus(); + void createRandomList(); + void getMetaInfo(MRL& mrl, const QString& mimeName); + void setupActions(); + /* helpers */ + static QString msToTimeString(int); + static int timeStringToMs(const QString&); + static bool endsWith(QString, QString, bool); + static bool startsWith(QString, QString, bool); + +private: + QColor m_altCol; + KComboBox* m_playlistSelector; + QToolButton *searchBtn; + KLineEdit* m_playlistFilter; + int m_nextPlaylistNumber; + QString m_playlistDirectory; + int m_currentPlaylist; + KURLComboBox *browserComb; + + QSplitter *hSplit, *vSplit; + QVBox *panel; + QWidget *playlist; + KDirOperator *fileBrowser; + KFileIconView *view; + CoverFrame *coverFrame; + GoogleFetcher *google; + QString coverImageFormat; + RollTitle *roller; + + KToggleAction *m_repeat, *m_shuffle, *m_autoCover, *m_showPlayer; + + QValueList<MRL> m_queue; + + uint m_playTime; + uint m_playTimeVisible; + uint m_countVisible; + + bool m_searchSelection; + bool m_metaOnLoading; + bool m_sortAscending; + + UrlListView* m_list; + QListViewItem* m_currentEntry; + MRL m_currentEntryMRL; + + QString m_fileFilter; + QString m_metaInfoString; + QString m_lastPlaylist; + + QPtrList<QListViewItem> m_randomList; + int m_currentRandomListEntry; + + QPixmap m_isCurrentEntry; + QPixmap m_cdPixmap; + + bool m_endless; + bool m_random; + bool m_cover; + + bool m_useAlternateEncoding; + QString m_alternateEncoding; + +signals: + void signalRequestForAudioCD(const QString&); + void signalRequestForDVD(const QString&); + void signalRequestForVCD(const QString&); +}; + +class QListBox; +class QStringList; + +class SubtitleChooser : public KDialogBase +{ + Q_OBJECT +public: + SubtitleChooser(QStringList, QString, QWidget *parent=0, const char *name = 0); + virtual ~SubtitleChooser(); + + QString getSelection(); + +private: + QListBox *table; +}; + +class MovieChooser : public KDialogBase +{ + Q_OBJECT +public: + MovieChooser(QStringList, QString, QWidget *parent=0, const char *name = 0); + virtual ~MovieChooser(); + + QString getSelection(); + +private: + QListBox *table; +}; + +#endif /* PLAYLIST_H */ diff --git a/kaffeine/src/input/audiobrowser/playlistitem.cpp b/kaffeine/src/input/audiobrowser/playlistitem.cpp new file mode 100644 index 0000000..3b5fc05 --- /dev/null +++ b/kaffeine/src/input/audiobrowser/playlistitem.cpp @@ -0,0 +1,268 @@ +/* + * playlistitem.cpp - a self-displayable item for kaffeine's playlist + * + * Copyright (C) 2004-2005 Giorgos Gousios + * Copyright (C) 2004-2005 Jürgen Kofler <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. + * + * This program 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 General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program; if not, write to the Free Software + * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA + */ + +#include <qstringlist.h> + +#include <kdebug.h> + +#include "mrl.h" +#include "playlistitem.h" + +PlaylistItem::PlaylistItem(KListView *list, KListViewItem *after, const QString& url, const QString& mime, const QString& title, + const QString& length, const QString& artist, const QString& album, const QString& year, const QString& genre, + const QString& track, const QStringList& subtitles, int currentSubtitle) : + KListViewItem(list, after, mime, title, artist, album, track, length), + m_url(url), m_year(year), m_genre(genre), m_subtitles(subtitles), m_currentSubtitle(currentSubtitle), isCurrent(false) +{} + +PlaylistItem::PlaylistItem(KListView* list, KListViewItem* after, const MRL& mrl) : + KListViewItem(list, after, mrl.mime(), mrl.title(), mrl.artist(), mrl.album(), + mrl.track(), mrl.length().toString("h:mm:ss")), m_url(mrl.url()), + m_year(mrl.year()), m_genre(mrl.genre()), + m_subtitles(mrl.subtitleFiles()), m_currentSubtitle(mrl.currentSubtitle()), isCurrent(false) +{} + +PlaylistItem::~PlaylistItem() {} + +MRL PlaylistItem::toMRL() const +{ + return MRL(url(), title(), stringToTime(length()), mime(), artist(), album(), track(), year(), genre(), QString::null, subtitles(), currentSubtitle()); +} + +const QString& PlaylistItem::url() const +{ + return m_url; +} + +QString PlaylistItem::mime() const +{ + return this->text(MIME_COLUMN); +} + +QString PlaylistItem::title() const +{ + return this->text(TITLE_COLUMN); +} + +QString PlaylistItem::artist() const +{ + return this->text(ARTIST_COLUMN); +} + +QString PlaylistItem::album() const +{ + return this->text(ALBUM_COLUMN); +} + +QString PlaylistItem::track() const +{ + return this->text(TRACK_COLUMN); +} + +QString PlaylistItem::year() const +{ + return m_year; +} + +QString PlaylistItem::genre() const +{ + return m_genre; +} + +QString PlaylistItem::length() const +{ + return this->text(LENGTH_COLUMN); +} + +const QStringList& PlaylistItem::subtitles() const +{ + return m_subtitles; +} + +int PlaylistItem::currentSubtitle() const +{ + return m_currentSubtitle; +} + +void PlaylistItem::setUrl(const QString& url) +{ + m_url = url; +} + +void PlaylistItem::setMime(const QString& mime) +{ + this->setText(MIME_COLUMN, mime); +} + +void PlaylistItem::setTitle(const QString& title) +{ + this->setText(TITLE_COLUMN, title); +} + +void PlaylistItem::setArtist(const QString& artist) +{ + this->setText(ARTIST_COLUMN, artist); +} + +void PlaylistItem::setAlbum(const QString& album) +{ + this->setText(ALBUM_COLUMN, album); +} + +void PlaylistItem::setTrack(const QString& track) +{ + this->setText(TRACK_COLUMN, track); +} + +void PlaylistItem::setYear(const QString& year) +{ + m_year = year; +} + +void PlaylistItem::setGenre(const QString& genre) +{ + m_genre = genre; +} + +void PlaylistItem::setLength(const QString& length) +{ + this->setText(LENGTH_COLUMN, length); +} + +void PlaylistItem::setSubtitles(const QStringList& subs) +{ + m_subtitles = subs; +} + +void PlaylistItem::addSubtitle(const QString& sub) +{ + /* This will add subtitle & set it to the current one */ + for (uint i=0; i < m_subtitles.count(); i++) { + if (m_subtitles[i] == sub) { + m_currentSubtitle = i; + return; + } + } + m_subtitles.append(sub); + m_currentSubtitle = m_subtitles.count()-1; +} + +void PlaylistItem::setCurrentSubtitle(int sub) +{ + m_currentSubtitle = sub; +} + +QTime PlaylistItem::stringToTime(const QString& timeString) +{ + int sec = 0; + bool ok = false; + QStringList tokens = QStringList::split(':',timeString); + + sec += tokens[0].toInt(&ok)*3600; //hours + sec += tokens[1].toInt(&ok)*60; //minutes + sec += tokens[2].toInt(&ok); //secs + + if (ok) + return QTime().addSecs(sec); + else + return QTime(); +} + +int PlaylistItem::compare(QListViewItem *i, int col, bool ascending ) const +{ + PlaylistItem* p = dynamic_cast<PlaylistItem*>(i); + if (!p) + return QListViewItem::compare(i, col, ascending); + + //if both strings are empty, sort by filename + if (text(col).isEmpty() && i->text(col).isEmpty()) + return -url().compare(p->url()); + + bool ok; + int track1, track2 = 0; + track1 = track().toInt(&ok); + if (ok) + track2 = p->track().toInt(&ok); + + if (col == TRACK_COLUMN) + { + if (ok) + return (track2 - track1); + else + return QListViewItem::compare(i, col, ascending); + } + else + { + int result = QListViewItem::compare(i, col, ascending); + + //if artists are equal, compare albums + if ((col == ARTIST_COLUMN) && (result == 0)) + { + result = QListViewItem::compare(i, ALBUM_COLUMN, ascending); + if (!ascending) + result = -result; + } + + //if strings are equal, sort by track number or filename + if (result == 0) + { + int diff; + if (ok) + diff = track1 - track2; + else + diff = url().compare(p->url()); + + if (ascending) + return diff; + else + return -diff; + } + else + return result; + } +} + +void PlaylistItem::setPlaying(bool playing) +{ + isCurrent = playing; +} + +void PlaylistItem::paintCell(QPainter *p, const QColorGroup &cg, int column, int width, int align) +{ + if (isCurrent) + { + QColorGroup colorGroup = cg; + + QColor base = colorGroup.base(); + QColor selection = colorGroup.highlight(); + + int r = (base.red() + selection.red()) / 2; + int b = (base.blue() + selection.blue()) / 2; + int g = (base.green() + selection.green()) / 2; + + QColor c(r, g, b); + + colorGroup.setColor(QColorGroup::Base, c); + QListViewItem::paintCell(p, colorGroup, column, width, align); + } + else + return KListViewItem::paintCell(p, cg, column, width, align); +} diff --git a/kaffeine/src/input/audiobrowser/playlistitem.h b/kaffeine/src/input/audiobrowser/playlistitem.h new file mode 100644 index 0000000..9b1b9f5 --- /dev/null +++ b/kaffeine/src/input/audiobrowser/playlistitem.h @@ -0,0 +1,93 @@ +/* + * playlistitem.h - a self-displayable item for kaffeine's playlist + * + * Copyright (C) 2004-2005 Giorgos Gousios + * Copyright (C) 2004-2005 Jürgen Kofler <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. + * + * This program 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 General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program; if not, write to the Free Software + * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA + */ + +#ifndef PLAYLISTITEM_H +#define PLAYLISTITEM_H + +#include <klistview.h> + +#define MIME_COLUMN 0 +#define TITLE_COLUMN 1 +#define ARTIST_COLUMN 2 +#define ALBUM_COLUMN 3 +#define TRACK_COLUMN 4 +#define LENGTH_COLUMN 5 + +class QTime; +class QString; +class QStringList; +class MRL; + +class PlaylistItem : public KListViewItem +{ + +public: + PlaylistItem(KListView *list, KListViewItem *after, const QString& url, const QString& mime, const QString& title, + const QString& length = QString::null, const QString& artist = QString::null, const QString& album = QString::null, + const QString& track = QString::null, const QString& year = QString::null, const QString& genre = QString::null, + const QStringList& subtitles = QStringList(), int currentSubtitle = -1); + PlaylistItem(KListView* list, KListViewItem* after, const MRL&); + virtual ~PlaylistItem(); + + MRL toMRL() const; + + const QString& url() const; + QString mime() const; + QString title() const; + QString artist() const; + QString album() const; + QString track() const; + QString year() const; + QString genre() const; + QString length() const; + const QStringList& subtitles() const; + int currentSubtitle() const; + + void setUrl(const QString&); + void setMime(const QString&); + void setTitle(const QString&); + void setArtist(const QString&); + void setAlbum(const QString&); + void setTrack(const QString&); + void setYear(const QString&); + void setGenre(const QString&); + void setLength(const QString&); + void setSubtitles(const QStringList&); + void addSubtitle(const QString&); + void setCurrentSubtitle(int); + + //reimplement to fix track order + int compare( QListViewItem *i, int col, bool ascending ) const; + + void setPlaying(bool playing); + virtual void paintCell(QPainter *p, const QColorGroup &cg, int column, int width, int align); + +private: + static QTime stringToTime(const QString&); + QString m_url; + QString m_year; + QString m_genre; + QStringList m_subtitles; + int m_currentSubtitle; + bool isCurrent; +}; + +#endif /* PLAYLISTITEM_H */ diff --git a/kaffeine/src/input/audiobrowser/urllistview.cpp b/kaffeine/src/input/audiobrowser/urllistview.cpp new file mode 100644 index 0000000..49cfc3c --- /dev/null +++ b/kaffeine/src/input/audiobrowser/urllistview.cpp @@ -0,0 +1,347 @@ +/* + * urllistview.cpp + * + * Copyright (C) 2003-2005 Jürgen Kofler <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. + * + * This program 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 General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program; if not, write to the Free Software + * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA + */ + +#include <kglobal.h> +#include <kiconloader.h> +#include <klocale.h> +#include <kmessagebox.h> +#include <kdebug.h> +#include <kurl.h> +#include <kglobalsettings.h> +#include <kfiledialog.h> +#include <kpopupmenu.h> + +#include <qfontmetrics.h> +#include <qdragobject.h> + +#include "mrl.h" +#include "playlistitem.h" +#include "urllistview.h" +#include "urllistview.moc" + + +UrlListView::UrlListView(QWidget *parent, const char *name ) : KListView(parent,name), + m_listCleared(true), m_itemOfContextMenu(NULL) +{ + m_contextMenu = new KPopupMenu(this); + m_contextMenu->insertItem(KGlobal::iconLoader()->loadIconSet("player_play", KIcon::Small), i18n("Play"), this, SLOT(slotPlayItem())); + m_contextMenu->insertItem(i18n("Play Next/Add to Queue"), this, SLOT(slotPlayNext())); + m_contextMenu->insertSeparator(); + + m_contextMenu->insertItem(KGlobal::iconLoader()->loadIconSet("editcut", KIcon::Small), i18n("C&ut"), this, SIGNAL(signalCut()), CTRL+Key_X); + m_contextMenu->insertItem(KGlobal::iconLoader()->loadIconSet("editcopy", KIcon::Small), i18n("&Copy"), this, SIGNAL(signalCopy()), CTRL+Key_C); + m_contextMenu->insertItem(KGlobal::iconLoader()->loadIconSet("editpaste", KIcon::Small), i18n("&Paste"), this, SIGNAL(signalPaste()), CTRL+Key_V); + m_contextMenu->insertItem(i18n("Select &All"), this, SIGNAL(signalSelectAll()), CTRL+Key_A); + m_contextMenu->insertItem(i18n("Create Playlist From Selected"), this, SIGNAL(signalPlaylistFromSelected())); + m_contextMenu->insertSeparator(); + m_contextMenu->insertItem(KGlobal::iconLoader()->loadIconSet("indent", KIcon::Small), i18n("Add Sub&title..."), this, SLOT(slotAddSubtitle()),QKeySequence(),100 ); + m_contextMenu->insertSeparator(); + m_contextMenu->insertItem(KGlobal::iconLoader()->loadIconSet("edit", KIcon::Small), i18n("&Edit Title"), this, SLOT(slotEditTitle())); + m_contextMenu->insertItem(KGlobal::iconLoader()->loadIconSet("info", KIcon::Small), i18n("&Info"), this, SLOT(slotShowInfo())); + +/* width of the "length"-column */ + QFontMetrics met(KGlobalSettings::generalFont()); + int w1 = met.width(i18n("Length")); + int w2 = met.width("5:55:55") + 2; + + m_column5Width = w1 > w2 ? w1 : w2; + m_column5Width += 30; + +/* width of the "track"-column */ + w1 = met.width(i18n("Track")); + w2 = met.width("9999") + 2; + + m_column4Track = w1 > w2 ? w1 : w2; + m_column4Track += 36; + + connect(this, SIGNAL(contextMenuRequested(QListViewItem*, const QPoint&, int)), + this, SLOT(slotShowContextMenu(QListViewItem*, const QPoint&, int))); + connect(this, SIGNAL(currentChanged(QListViewItem*)), + this, SLOT(slotCurrentChanged(QListViewItem*))); + connect(this, SIGNAL(clicked( QListViewItem*, const QPoint&, int )), + this, SLOT(slotClicked( QListViewItem*, const QPoint&, int ))); +} + +UrlListView::~UrlListView() +{ +} + +bool UrlListView::acceptDrag(QDropEvent* event) const +{ + return QUriDrag::canDecode(event) || QTextDrag::canDecode(event) || KListView::acceptDrag(event); +} + +QDragObject* UrlListView::dragObject() +{ + // get selected items + QPtrList<QListViewItem> selected; + QStrList urlList; + + selected = selectedItems(); + for (uint i = 0; i<selected.count(); i++) + { + urlList.append(dynamic_cast<PlaylistItem*>(selected.at(i))->url().ascii()); + } + + return new QUriDrag(urlList, viewport()); +} + +void UrlListView::resizeEvent(QResizeEvent* rev) +{ + int width = contentsRect().width() - m_column5Width - m_column4Track - 69; + setColumnWidth(0, 20); /* mime */ + setColumnWidth(1, ((width * 5 / 12) + 50)); /* title */ + setColumnWidth(2, (width * 3 / 12)); /* artist */ + setColumnWidth(3, (width * 4 / 12)); /* album */ + setColumnWidth(4, m_column4Track); /* track */ + setColumnWidth(5, m_column5Width); /* width of "length" column */ + + KListView::resizeEvent(rev); +} + +void UrlListView::clear() +{ + m_listCleared = true; + m_itemOfContextMenu = NULL; + setSorting(-1); + KListView::clear(); +} + +void UrlListView::setCleared(bool cl) +{ + m_listCleared = cl; +} + +bool UrlListView::getCleared() const /* was playlist cleared ? */ +{ + return m_listCleared; +} + +/********** context menu **********/ + +void UrlListView::slotShowContextMenu(QListViewItem* item, const QPoint& pos, int) +{ + if (!item) + { + m_itemOfContextMenu = NULL; + disableSubEntry(); + } + else + { + m_itemOfContextMenu = dynamic_cast<PlaylistItem *>(item); + if (m_itemOfContextMenu->mime().contains("video")) + enableSubEntry(); + else + disableSubEntry(); + } + + m_contextMenu->popup(pos); +} + +void UrlListView::slotPlayItem() +{ + if (m_itemOfContextMenu) + emit signalPlayItem(m_itemOfContextMenu); +} + +void UrlListView::slotPlayNext() +{ + if (m_itemOfContextMenu) + emit signalAddToQueue(m_itemOfContextMenu->toMRL()); +} + +void UrlListView::slotEditTitle() +{ + if (m_itemOfContextMenu) + { + m_itemOfContextMenu->setRenameEnabled(1, true); + m_itemOfContextMenu->startRename(1); + m_itemOfContextMenu->setRenameEnabled(1, false); + } +} + +//Pretty print item info +void UrlListView::slotShowInfo() +{ + if (!m_itemOfContextMenu) + return; + + QString num; + num = num.setNum(childCount()); + QString info = "<qt><table width=\"90%\">"; + info = info + "<tr><td colspan=\"2\"><center><b>" + m_itemOfContextMenu->title() + "</b></center></td></tr>"; + info = info + "<tr><td><b>" + i18n("URL")+ ":</b></td><td>" + m_itemOfContextMenu->url() + "</td></tr>"; + info = info + "<tr><td><b>" + i18n("Artist") + ":</b></td><td>" + m_itemOfContextMenu->artist() + "</td></td>"; + info = info + "<tr><td><b>" + i18n("Album") + ":</b></td><td>" + m_itemOfContextMenu->album() + "</td></td>"; + info = info + "<tr><td><b>" + i18n("Track") + ":</b></td><td>" + m_itemOfContextMenu->track() + "</td></td>"; + info = info + "<tr><td><b>" + i18n("Year") + ":</b></td><td>" + m_itemOfContextMenu->year() + "</td></td>"; + info = info + "<tr><td><b>" + i18n("Genre") + ":</b></td><td>" + m_itemOfContextMenu->genre() + "</td></td>"; + info = info + "<tr><td><b>" + i18n("Length") + ":</b></td><td>" + m_itemOfContextMenu->length() + "</td></tr>"; + if(!m_itemOfContextMenu->subtitles().isEmpty()) + { + info = info + "<tr><td><b>" + i18n("Subtitles") + ":</b></td><td>"; + + for(uint i = 0; i < m_itemOfContextMenu->subtitles().count(); i++ ) + { + info = info + ""+m_itemOfContextMenu->subtitles()[i]; + if(m_itemOfContextMenu->currentSubtitle() == (int)i) + info = info + "<small> ("+i18n("in use")+")</small>"; + info = info + "<br>"; + } + info = info + "</ul></td></tr></table></qt>"; + } + + KMessageBox::information(this, info); +} + +void UrlListView::slotClicked(QListViewItem*, const QPoint&, int) +{ + /*if ( (item) && (col == 3) ) + { + m_itemOfContextMenu = dynamic_cast<PlaylistItem *>(item); + if (!m_itemOfContextMenu) return; + slotShowInfo(); + } */ +} + +#include <qdom.h> +#include <kio/job.h> + +void UrlListView::slotAddSubtitle() +{ + /*QDomDocument reqdoc; + + QDomElement methodCall = reqdoc.createElement( "methodCall" ); + QDomElement methodName = reqdoc.createElement( "methodName" ); + QDomText methodNameValue = reqdoc.createTextNode( "LogIn" ); + methodName.appendChild( methodNameValue ); + methodCall.appendChild( methodName ); + reqdoc.appendChild( methodCall ); + + QDomElement params = reqdoc.createElement( "params" ); + + QDomElement param1 = reqdoc.createElement( "param" ); + QDomElement value1 = reqdoc.createElement( "value" ); + QDomElement type1 = reqdoc.createElement( "string" ); + QDomText param1Value = reqdoc.createTextNode( "" ); + type1.appendChild( param1Value ); + value1.appendChild( type1 ); + param1.appendChild( value1 ); + params.appendChild( param1 ); + + QDomElement param2 = reqdoc.createElement( "param" ); + QDomElement value2 = reqdoc.createElement( "value" ); + QDomElement type2 = reqdoc.createElement( "string" ); + QDomText param2Value = reqdoc.createTextNode( "" ); + type2.appendChild( param2Value ); + value2.appendChild( type2 ); + param2.appendChild( value2 ); + params.appendChild( param2 ); + + QDomElement param3 = reqdoc.createElement( "param" ); + QDomElement value3 = reqdoc.createElement( "value" ); + QDomElement type3 = reqdoc.createElement( "string" ); + QDomText param3Value = reqdoc.createTextNode( "en" ); + type3.appendChild( param3Value ); + value3.appendChild( type3 ); + param3.appendChild( value3 ); + params.appendChild( param3 ); + + methodCall.appendChild( params ); + + const QString xmlRequest = "<?xml version=\"1.0\"?>\n" + reqdoc.toString(); + + QByteArray postData; + QDataStream stream( postData, IO_WriteOnly ); + stream.writeRawBytes( xmlRequest.utf8(), xmlRequest.utf8().length() ); + + openSubJobBuf = QByteArray(); + + openSubJob = KIO::http_post( "http://test.opensubtitles.org/xml-rpc", postData, false ); + openSubJob->addMetaData( "content-type", "Content-Type: text/xml" ); + + connect( openSubJob, SIGNAL(result(KIO::Job*)), this, SLOT(openSubResult(KIO::Job*)) ); + connect( openSubJob, SIGNAL(data(KIO::Job*,const QByteArray&) ), this, SLOT(openSubData(KIO::Job*, + const QByteArray&)) ); + + kdDebug() << "\n\n" << xmlRequest << "\n\n" << endl;*/ + + + if (!m_itemOfContextMenu) + return; + + QString openURL = m_itemOfContextMenu->url(); + QString subtitleURL = KFileDialog::getOpenURL(openURL, + i18n("*.smi *.srt *.sub *.txt *.ssa *.asc|Subtitle Files\n*.*|All Files"), + 0, i18n("Select Subtitle File")).path(); + if(!(subtitleURL.isEmpty()) && !(m_itemOfContextMenu->subtitles().contains(subtitleURL)) && QFile::exists(subtitleURL)) + { + m_itemOfContextMenu->addSubtitle(subtitleURL); + emit signalPlayItem(m_itemOfContextMenu); + } + +} + +void UrlListView::openSubResult( KIO::Job* job ) +{ + if ( openSubJob!=job ) + return; + if ( job->error() ) { + kdDebug() << "OpenSubtiltes error : " << job->error() << endl; + return; + } + + QDomDocument document; + if ( !document.setContent( openSubJobBuf ) ) { + kdDebug() << "Couldn't read similar artists response" << endl; + return; + } + kdDebug() << "\n\n" << document.toString() << "\n\n" << endl; + openSubJob = 0; +} + +void UrlListView::openSubData( KIO::Job* job, const QByteArray& data ) +{ + if ( openSubJob != job ) + return; + + unsigned int bufsize = openSubJobBuf.size(); + openSubJobBuf.resize( bufsize + data.size() ); + memcpy( openSubJobBuf.data()+bufsize, data.data(), data.size() ); +} + + +void UrlListView::slotCurrentChanged(QListViewItem * item) +{ + if(item == 0) //All items deleted + m_itemOfContextMenu = NULL; + else + m_itemOfContextMenu = dynamic_cast<PlaylistItem *>(item); +} + +void UrlListView::enableSubEntry() +{ + m_contextMenu->setItemEnabled(100, true); +} + +void UrlListView::disableSubEntry() +{ + m_contextMenu->setItemEnabled(100, false); +} diff --git a/kaffeine/src/input/audiobrowser/urllistview.h b/kaffeine/src/input/audiobrowser/urllistview.h new file mode 100644 index 0000000..964b743 --- /dev/null +++ b/kaffeine/src/input/audiobrowser/urllistview.h @@ -0,0 +1,92 @@ +/* + * urllistview.h + * + * Copyright (C) 2003-2005 Jürgen Kofler <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. + * + * This program 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 General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program; if not, write to the Free Software + * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA + */ + +#ifndef URLLISTVIEW_H +#define URLLISTVIEW_H + +#ifdef HAVE_CONFIG_H +#include <config.h> +#endif + +#include <klistview.h> + +class KPopupMenu; +class QWidget; +class QDragObject; +class PlaylistItem; +class MRL; +class KIO::Job; + +class UrlListView : public KListView +{ + Q_OBJECT +public: + UrlListView(QWidget *parent=0, const char *name=0); + ~UrlListView(); + + void setCleared(bool); + bool getCleared() const; /* was the playlist cleared or should we save it? */ + +public slots: + virtual void clear(); /* reimplement slot */ + +signals: + void signalPlayItem(QListViewItem* ); /* play selected in context menu */ + void signalCut(); + void signalCopy(); + void signalPaste(); + void signalSelectAll(); + void signalAddToQueue(MRL); + void signalPlaylistFromSelected(); + +private slots: + void slotShowContextMenu(QListViewItem*, const QPoint&, int); + void slotCurrentChanged(QListViewItem *); + void slotAddSubtitle(); + void slotShowInfo(); + void slotEditTitle(); + void slotPlayItem(); + void slotClicked(QListViewItem*, const QPoint&, int); + void slotPlayNext(); + void openSubResult( KIO::Job* ); + void openSubData( KIO::Job*, const QByteArray& ); + +protected: + virtual bool acceptDrag(QDropEvent* event) const; + virtual void resizeEvent(QResizeEvent*); + virtual QDragObject* dragObject(); + +private: + void enableSubEntry(); + void disableSubEntry(); + +private: + bool m_listCleared; + int m_column5Width; /* width of the fifth column */ + int m_column4Track; /* width of the fourth column */ + + PlaylistItem* m_itemOfContextMenu; + KPopupMenu* m_contextMenu; + KIO::Job *openSubJob; + QByteArray openSubJobBuf; + +}; + +#endif /* URLLISTVIEW_H */ |