Commit adca2adf authored by Scott Wheeler's avatar Scott Wheeler

Ok, huge patch from Nathan Toone to add a cover manager and such. There

are still quite a few rough edges, but interface and codewise, but it's
close enough that those can be worked out in the relatively near future.

FEATURE:60462

svn path=/trunk/kdemultimedia/juk/; revision=361330
parent 76ada175
......@@ -8,6 +8,7 @@ juk_SOURCES = \
cache.cpp \
categoryreaderinterface.cpp \
collectionlist.cpp \
coverinfo.cpp \
deletedialog.cpp \
deletedialogbase.ui \
directorylist.cpp \
......@@ -23,6 +24,8 @@ juk_SOURCES = \
filerenameroptionsbase.ui \
filerenamerconfigdlg.cpp \
gstreamerplayer.cpp \
googlefetcher.cpp \
googlefetcherdialog.cpp \
historyplaylist.cpp \
juk.cpp \
jukIface.skel \
......@@ -31,6 +34,7 @@ juk_SOURCES = \
main.cpp \
mediafiles.cpp \
musicbrainzquery.cpp \
nowplaying.cpp \
painteater.cpp \
playermanager.cpp \
playlist.cpp \
......@@ -77,7 +81,7 @@ mblibs = -lmusicbrainz -ltunepimp
endif
##################################################
juk_LDADD = $(LDADD_GST) $(mblibs) -lartskde $(LIB_KIO) $(taglib_libs)
juk_LDADD = $(LDADD_GST) $(mblibs) -lartskde $(LIB_KIO) $(taglib_libs) $(LIB_KHTML)
juk_LDFLAGS = $(all_libraries) $(KDE_RPATH) $(LDFLAGS_GST)
tagguessertest_LDADD = $(LIB_KDECORE)
......
......@@ -258,7 +258,10 @@ void CollectionList::contentsDropEvent(QDropEvent *e)
if(e->source() == this)
return;
else
{
tryCoverSet(e);
decode(e);
}
}
void CollectionList::contentsDragMoveEvent(QDragMoveEvent *e)
......
/***************************************************************************
begin : Sun Feb 17 2002
copyright : (C) 2002 - 2004 by Scott Wheeler
email : wheeler@kde.org
***************************************************************************/
/***************************************************************************
* *
* This program is free software; you can redistribute it and/or modify *
* it under the terms of the GNU General Public License as published by *
* the Free Software Foundation; either version 2 of the License, or *
* (at your option) any later version. *
* *
***************************************************************************/
#include <kglobal.h>
#include <kapplication.h>
#include <kstandarddirs.h>
#include <kdebug.h>
#include <qimage.h>
#include <qvbox.h>
#include <qwidget.h>
#include <qnamespace.h>
#include "coverinfo.h"
////////////////////////////////////////////////////////////////////////////////
// public members
////////////////////////////////////////////////////////////////////////////////
CoverInfo::CoverInfo(const Tag &tag) :
m_tag(tag)
{
}
QPixmap *CoverInfo::coverPixmap()
{
QPixmap *coverThumb = getPixmap(false);
if (!coverThumb->isNull())
return coverThumb;
// If the file doesn't exists, try to create the thumbnail from
// the large image
QPixmap *largeCover = largeCoverPixmap();
if (!largeCover->isNull()) {
QImage img(largeCover->convertToImage());
img.smoothScale(80, 80).save(coverLocation(false), "PNG");
}
return getPixmap(false);
}
QPixmap *CoverInfo::largeCoverPixmap()
{
return getPixmap(true);
}
QPixmap *CoverInfo::getPixmap(bool large)
{
if (m_tag.artist().isEmpty() || m_tag.album().isEmpty())
return new QPixmap();
return new QPixmap(coverLocation(large));
}
QString CoverInfo::coverLocation(bool large)
{
QString fileName (QFile::encodeName(m_tag.artist() + " - " + m_tag.album()));
fileName.replace(" ", "_").replace("/", "_").replace("?", "_").append(".png");
QString fileLocation = KGlobal::dirs()->saveLocation("data", kapp->instanceName() + "/") +
"covers/" +
(large ? "large/" : "") +
fileName.lower();
return fileLocation;
}
void CoverInfo::popupLargeCover()
{
QPixmap *largeCover = largeCoverPixmap();;
if (largeCover->isNull()){
return;
}
QVBox *container = new QVBox(0, 0, Qt::WDestructiveClose);
container->setCaption(kapp->makeStdCaption(m_tag.artist() + " - " +m_tag.album()));
QWidget *widget = new QWidget(container);
widget->setPaletteBackgroundPixmap(*largeCover);
widget->setFixedSize(largeCover->size());
container->adjustSize();
container->setFixedSize(container->size());
container->show();
}
/***************************************************************************
begin : Sun Feb 17 2002
copyright : (C) 2002 - 2004 by Scott Wheeler
email : wheeler@kde.org
***************************************************************************/
/***************************************************************************
* *
* 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. *
* *
***************************************************************************/
#ifndef COVERINFO_H
#define COVERINFO_H
#include <qpixmap.h>
#include <qfile.h>
#include "tag.h"
class CoverInfo
{
friend class FileHandle;
public:
CoverInfo(const Tag &tag);
QPixmap *coverPixmap();
bool hasCover() { return (QFile(coverLocation(false)).exists() || QFile(coverLocation(true)).exists()); }
QPixmap *getPixmap(bool large);
QPixmap *largeCoverPixmap();
QString coverLocation(bool large);
void popupLargeCover();
private:
Tag m_tag;
};
#endif
......@@ -67,14 +67,17 @@ class FileHandle::FileHandlePrivate : public RefCounter
{
public:
FileHandlePrivate() :
tag(0) {}
tag(0),
coverInfo(0) {}
~FileHandlePrivate()
{
delete tag;
delete coverInfo;
}
mutable Tag *tag;
mutable CoverInfo *coverInfo;
mutable QString absFilePath;
QFileInfo fileInfo;
QDateTime modificationTime;
......@@ -151,6 +154,14 @@ Tag *FileHandle::tag() const
return d->tag;
}
CoverInfo *FileHandle::coverInfo() const
{
if(!d->coverInfo)
d->coverInfo = new CoverInfo(*tag());
return d->coverInfo;
}
QString FileHandle::absFilePath() const
{
if(d->absFilePath.isNull())
......
......@@ -18,6 +18,8 @@
#include <qstringlist.h>
#include "coverinfo.h"
class QFileInfo;
class QDateTime;
class QDataStream;
......@@ -47,6 +49,7 @@ public:
void setFile(const QString &path);
Tag *tag() const;
CoverInfo *coverInfo() const;
QString absFilePath() const;
const QFileInfo &fileInfo() const;
......
/***************************************************************************
copyright : (C) 2004 Nathan Toone
email : 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. *
* *
***************************************************************************/
#include <dom/html_document.h>
#include <dom/dom_string.h>
#include <dom/html_misc.h>
#include <dom/dom_node.h>
#include <kio/netaccess.h>
#include <kapplication.h>
#include <kpushbutton.h>
#include <klocale.h>
#include <qinputdialog.h>
#include <qfile.h>
#include <qvbox.h>
#include <qlayout.h>
#include <qwidget.h>
#include <qlabel.h>
#include <qdialog.h>
#include "googlefetcher.h"
#include "googlefetcherdialog.h"
GoogleFetcher::GoogleFetcher(const Tag *tag)
: m_tag(tag),
m_searchString(m_tag->artist() + " " + m_tag->album())
{
}
void GoogleFetcher::loadImageURLs()
{
if(m_loadedQuery == m_searchString)
return;
m_urlList.clear();
KURL url = "http://images.google.com/images?q="+m_searchString;
DOM::HTMLDocument *search = new DOM::HTMLDocument;
search->setAsync(false);
search->load(url.url());
DOM::HTMLCollection col = search->links();
for(uint i = 0; i < col.length(); i++) {
DOM::Node result = col.item(i).attributes().getNamedItem("href");
if(!result.isNull()) {
QString href = result.nodeValue().string();
if(!href.isNull() && href.startsWith("/imgres")) {
DOM::HTMLDocument *resdoc = new DOM::HTMLDocument;
resdoc->setAsync(false);
resdoc->load("http://images.google.com" + href + "&frame=small");
DOM::HTMLCollection finalcol=resdoc->links();
// The right link is always the first in the collection.
m_urlList.append(finalcol.item(0).attributes().getNamedItem("href").nodeValue().string());
}
}
}
m_loadedQuery = m_searchString;
}
QPixmap GoogleFetcher::getPixmap()
{
m_chosen = false;
m_selectedIndex = 0;
displayWaitBox();
while(!m_chosen) {
if(m_urlList.size() == 0) {
bool ok;
m_searchString =
QInputDialog::getText(i18n("Cover Downloader"),
i18n("No covers found. Enter new search terms:"),
QLineEdit::Normal, m_searchString, &ok, 0);
if(ok && !m_searchString.isEmpty())
displayWaitBox();
else {
m_currentPixmap = QPixmap();
m_chosen = true;
}
}
else {
GoogleFetcherDialog dialog("google", m_urlList, m_selectedIndex, m_tag, 0);
dialog.exec();
m_currentPixmap = dialog.result();
m_chosen = dialog.takeIt();
if(dialog.newSearch()) {
bool ok;
m_searchString = QInputDialog::getText(i18n("Cover Downloader"),
i18n("Enter new search terms:"),
QLineEdit::Normal, m_searchString, &ok, 0);
if(ok && !m_searchString.isEmpty())
displayWaitBox();
else
m_searchString = m_loadedQuery;
}
}
}
return m_currentPixmap;
}
void GoogleFetcher::displayWaitBox()
{
m_container = new QVBox(0, 0, Qt::WDestructiveClose);
m_container->setCaption(kapp->makeStdCaption(m_tag->artist() + " - " +m_tag->album()));
QWidget *widget = new QWidget(m_container);
QLabel *pleaseWait = new QLabel(widget);
pleaseWait->setText(i18n("Searching for Images. Please Wait..."));
pleaseWait->adjustSize();
m_container->setFixedSize(pleaseWait->size());
m_container->show();
loadImageURLs();
// check that it wasn't closed
if(pleaseWait->size().height() <= 0 || pleaseWait->size().width() <= 0) {
m_chosen = true;
return;
}
delete m_container;
}
/***************************************************************************
copyright : (C) 2004 Nathan Toone
email : 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. *
* *
***************************************************************************/
#ifndef GOOGLEFETCHER_H
#define GOOGLEFETCHER_H
#include <qpixmap.h>
#include <qstring.h>
#include <qstringlist.h>
class KURL;
class Tag;
class GoogleFetcher
{
public:
GoogleFetcher(const Tag *tag);
QPixmap getPixmap();
private slots:
void cancel();
void editSearch();
void saveCover();
void previous();
void next();
private:
QPixmap getPixmapFromURL(const KURL &url);
QPixmap fetchedImage(uint index);
void loadImageURLs();
void displayWaitBox();
void buildBox();
const Tag *m_tag;
QString m_searchString;
QString m_loadedQuery;
QStringList m_urlList;
bool m_chosen;
uint m_selectedIndex;
QVBox *m_container;
QPixmap m_currentPixmap;
};
#endif
/***************************************************************************
copyright : (C) 2004 Nathan Toone
email : 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. *
* *
***************************************************************************/
#include <klocale.h>
#include <dom/html_document.h>
#include <dom/dom_string.h>
#include <dom/html_misc.h>
#include <dom/dom_node.h>
#include <kio/netaccess.h>
#include <kapplication.h>
#include <qhbox.h>
#include <qimage.h>
#include "googlefetcherdialog.h"
GoogleFetcherDialog::GoogleFetcherDialog(const QString &name,
const QValueList<QString> &urlList,
uint selectedIndex,
const Tag *tag,
QWidget *parent) :
KDialogBase(parent, name.latin1(), true, QString::null, Ok | Cancel | User1 | User2 | User3, NoDefault, true),
m_urlList(urlList),
m_pixmap(QPixmap()),
m_takeIt(false),
m_newSearch(false),
m_index(selectedIndex),
m_tag(tag)
{
QHBox *mainBox = new QHBox(this);
m_pixWidget = new QWidget(mainBox);
setMainWidget(mainBox);
setButtonText(User3, i18n("Previous"));
setButtonText(User2, i18n("Next"));
setButtonText(User1, i18n("New Search"));
}
GoogleFetcherDialog::~GoogleFetcherDialog()
{
}
void GoogleFetcherDialog::setLayout()
{
m_pixmap = fetchedImage(m_index);
if(m_pixmap.size().width() > 500 || m_pixmap.size().height() > 500)
m_pixmap = QImage(m_pixmap.convertToImage()).smoothScale(500, 500);
setCaption(QString("(%1/%2) %3 - %4").arg(m_index+1).arg(m_urlList.size())
.arg(m_tag->artist()).arg(m_tag->album()));
m_pixWidget->setPaletteBackgroundPixmap(m_pixmap);
m_pixWidget->setFixedSize(m_pixmap.size());
adjustSize();
}
////////////////////////////////////////////////////////////////////////////////
// public slots
////////////////////////////////////////////////////////////////////////////////
int GoogleFetcherDialog::exec()
{
setLayout();
return KDialogBase::exec();
}
void GoogleFetcherDialog::slotOk()
{
m_takeIt = true;
m_newSearch = false;
hide();
}
void GoogleFetcherDialog::slotCancel()
{
m_takeIt = true;
m_newSearch = false;
m_pixmap=QPixmap();
hide();
}
void GoogleFetcherDialog::slotUser3()
{
m_takeIt = false;
m_newSearch = false;
if(m_index == 0)
m_index = m_urlList.size() - 1;
else
m_index = m_index-1;
setLayout();
}
void GoogleFetcherDialog::slotUser2()
{
m_takeIt = false;
m_newSearch = false;
if(m_index >= m_urlList.size()-1)
m_index = 0;
else
m_index = m_index+1;
setLayout();
}
void GoogleFetcherDialog::slotUser1()
{
m_takeIt = false;
m_newSearch = true;
m_pixmap = QPixmap();
hide();
}
QPixmap GoogleFetcherDialog::fetchedImage(uint index) const
{
if (index>m_urlList.count())
return QPixmap();
QValueListConstIterator<QString> returnVal = m_urlList.at(index);
return getPixmapFromURL(*returnVal);
}
QPixmap GoogleFetcherDialog::getPixmapFromURL(KURL url) const
{
kdDebug(65432) << "imageURL: " << url << endl;
QString tmpFile;
if(KIO::NetAccess::download(url, tmpFile, 0)) {
QPixmap returnVal=QPixmap(tmpFile);
QFile(tmpFile).remove();
return returnVal;
}
return QPixmap();
}
#include "googlefetcherdialog.moc"
/***************************************************************************
copyright : (C) 2004 Nathan Toone
email : 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. *
* *
***************************************************************************/
#ifndef GOOGLEFETCHERDIALOG_H
#define GOOGLEFETCHERDIALOG_H
#include <kdialogbase.h>
#include <kurl.h>
#include <qpixmap.h>
#include "tag.h"
class GoogleFetcherDialog : public KDialogBase
{
Q_OBJECT
public:
GoogleFetcherDialog(const QString &name,
const QValueList<QString> &urlList,
uint selectedIndex,
const Tag *tag,
QWidget *parent = 0);
virtual ~GoogleFetcherDialog();
QPixmap result() const { return m_pixmap; }
bool takeIt() const { return m_takeIt; }
bool newSearch() const { return m_newSearch; }
public slots:
int exec();
protected slots:
void slotOk();
void slotCancel();
void slotUser3();
void slotUser2();
void slotUser1();
private:
void setLayout();
QPixmap fetchedImage(uint index) const;
QPixmap getPixmapFromURL(KURL url) const;
QValueList<QString> m_urlList;
QPixmap m_pixmap;
QWidget *m_pixWidget;
bool m_takeIt;
bool m_newSearch;
uint m_index;
const Tag *m_tag;
};
#endif
......@@ -19,6 +19,7 @@
#include <kstatusbar.h>
#include <kdebug.h>
#include <kmessagebox.h>
#include <kstandarddirs.h>
#include "juk.h"
......@@ -67,6 +68,7 @@ JuK::JuK(QWidget *parent, const char *name) :
setupSystemTray();
setupGlobalAccels();
processArgs();
createDirs();
SplashScreen::finishedLoading();
QTimer::singleShot(0, CollectionList::instance(), SLOT(slotCheckCache()));
......@@ -174,6 +176,9 @@ void JuK::setupActions()
m_togglePopupsAction =
new KToggleAction(i18n("Popup &Track Announcement"),
KShortcut(), this, 0, actions(), "togglePopups");
m_toggleCoversAction =
new KToggleAction(i18n("Show Cover Images"),
KShortcut(), this, 0, actions(), "showCovers");
new KToggleAction(i18n("Save &Play Queue on Exit"),
KShortcut(), this, 0, actions(), "saveUpcomingTracks");
......@@ -254,6 +259,16 @@ void JuK::processArgs()
// m_splitter->open(files);
}
void JuK::createDirs()
{
QDir dir(KGlobal::dirs()->saveLocation("data", kapp->instanceName() + '/'));
if (!dir.exists("covers", false))
dir.mkdir("covers", false);
dir.cd("covers");
if (!dir.exists("large", false))
dir.mkdir("large", false);
}
void JuK::keyPressEvent(QKeyEvent *e)
{
if (e->key() >= Qt::Key_Back && e->key() <= Qt::Key_MediaLast)
......@@ -310,6 +325,9 @@ void JuK::readConfig()
bool showPopups = settingsConfig.readBoolEntry("TrackPopup", false);
m_togglePopupsAction->setChecked(showPopups);
bool showCovers = settingsConfig.readBoolEntry("ShowCovers", false);
m_toggleCoversAction->setChecked(showCovers);
if(m_outputSelectAction)
m_outputSelectAction->setCurrentItem(settingsConfig.readNumEntry("MediaSystem", 0));
......@@ -348,6 +366,7 @@ void JuK::saveConfig()
settingsConfig.writeEntry("DockInSystemTray", m_toggleSystemTrayAction->isChecked());
settingsConfig.writeEntry("DockOnClose", m_toggleDockOnCloseAction->isChecked());
settingsConfig.writeEntry("TrackPopup", m_togglePopupsAction->isChecked());
settingsConfig.writeEntry("ShowCovers", m_toggleCoversAction->isChecked());
if(m_outputSelectAction)
settingsConfig.writeEntry("MediaSystem", m_outputSelectAction->currentItem());
......
......@@ -49,6 +49,7 @@ private:
void setupSystemTray();
void setupGlobalAccels();
void processArgs();
void createDirs();
void keyPressEvent(QKeyEvent *);
......@@ -82,6 +83,7 @@ private:
KToggleAction *m_toggleSystemTrayAction;
KToggleAction *m_toggleDockOnCloseAction;
KToggleAction *m_togglePopupsAction;
KToggleAction *m_toggleCoversAction;
KToggleAction *m_toggleSplashAction;
KToggleAction *m_loopPlaylistAction;
KSelectAction *m_outputSelectAction;
......
......@@ -26,6 +26,7 @@
<Menu name="view" noMerge="1"><text>&amp;View</text>
<Action name="showSearch"/>
<Action name="showEditor"/>
<Action name="showNowPlaying"/>
<Action name="showHistory"/>
<Action name="showUpcoming"/>
<Action name="showColumns"/>
......@@ -56,6 +57,7 @@
<Separator/>
<Action name="guessTag"/>
<Action name="coverManager"/>
<Action name="renameFile"/>
</Menu>
<Menu name="settings"><text>&amp;Settings</text>
......@@ -63,6 +65,7 @@
<Action name="toggleSystemTray" append="show_merge"/>
<Action name="dockOnClose" append="show_merge"/>
<Action name="togglePopups" append="show_merge"/>
<Action name="showCovers" append="show_merge"/>
<Action name="saveUpcomingTracks" append="show_merge"/>