Merge branch 'master' into drive

Conflicts:
	data/data.qrc
This commit is contained in:
John Maguire 2012-07-25 15:36:57 +02:00
commit 517252ccc1
102 changed files with 23729 additions and 22827 deletions

View File

@ -21,7 +21,6 @@ else()
qprogressindicatorspinning_nonmac.cpp
)
set(RESOURCES
qsearchfield_nonmac.qrc
qprogressindicatorspinning_nonmac.qrc
)
qt4_add_resources(RESOURCES_SOURCES ${RESOURCES})

View File

@ -3,18 +3,6 @@
#include <QWidget>
#include <QPointer>
#include <QtGlobal>
#ifndef Q_OS_MAC
// Currently the old LineEdit is better than the current qsearchfield_nonmac
// IMHO: add this (ugly) hack to force non Mac systems to use the old LineEdit.
// TODO: Fix this in a better way (improve qsearchfield_nonmac definitely?)
class LineEdit;
typedef LineEdit QSearchField;
#else // Q_OS_MAC
class QSearchFieldPrivate;
class QSearchField : public QWidget
@ -25,12 +13,14 @@ public:
QString text() const;
QString placeholderText() const;
void setFocus(Qt::FocusReason reason);
public slots:
void setText(const QString &text);
void setPlaceholderText(const QString &text);
void clear();
void selectAll();
void setFocus();
signals:
void textChanged(const QString &text);
@ -48,6 +38,4 @@ private:
Q_PROPERTY(QString placeholderText READ placeholderText WRITE setPlaceholderText);
};
#endif // Q_OS_MAC
#endif // QSEARCHFIELD_H

View File

@ -28,6 +28,9 @@ THE SOFTWARE.
#import "Foundation/NSNotification.h"
#import "AppKit/NSSearchField.h"
#include <QApplication>
#include <QClipboard>
class QSearchFieldPrivate : public QObject
{
public:
@ -82,11 +85,59 @@ public:
}
@end
namespace {
static const unsigned short kKeycodeA = 0;
static const unsigned short kKeycodeX = 7;
static const unsigned short kKeycodeC = 8;
static const unsigned short kKeycodeV = 9;
} // namespace
@interface QocoaSearchField : NSSearchField
-(BOOL)performKeyEquivalent:(NSEvent*)event;
@end
@implementation QocoaSearchField
-(BOOL)performKeyEquivalent:(NSEvent*)event {
if ([event type] == NSKeyDown && [event modifierFlags] & NSCommandKeyMask)
{
const unsigned short keyCode = [event keyCode];
if (keyCode == kKeycodeA) // Cmd+a
{
[self performSelector:@selector(selectText:)];
return YES;
}
else if (keyCode == kKeycodeC) // Cmd+c
{
QClipboard* clipboard = QApplication::clipboard();
clipboard->setText(toQString([self stringValue]));
return YES;
}
else if (keyCode == kKeycodeV) // Cmd+v
{
QClipboard* clipboard = QApplication::clipboard();
[self setStringValue:fromQString(clipboard->text())];
return YES;
}
else if (keyCode == kKeycodeX) // Cmd+x
{
QClipboard* clipboard = QApplication::clipboard();
clipboard->setText(toQString([self stringValue]));
[self setStringValue:@""];
return YES;
}
}
return NO;
}
@end
QSearchField::QSearchField(QWidget *parent) : QWidget(parent)
{
NSAutoreleasePool *pool = [[NSAutoreleasePool alloc] init];
NSSearchField *search = [[NSSearchField alloc] init];
NSSearchField *search = [[QocoaSearchField alloc] init];
QSearchFieldDelegate *delegate = [[QSearchFieldDelegate alloc] init];
pimpl = delegate->pimpl = new QSearchFieldPrivate(this, search);
@ -130,6 +181,21 @@ QString QSearchField::placeholderText() const {
return toQString(placeholder);
}
void QSearchField::setFocus(Qt::FocusReason reason)
{
Q_ASSERT(pimpl);
if (!pimpl)
return;
if ([pimpl->nsSearchField acceptsFirstResponder])
[[pimpl->nsSearchField window] makeFirstResponder: pimpl->nsSearchField];
}
void QSearchField::setFocus()
{
setFocus(Qt::OtherFocusReason);
}
void QSearchField::clear()
{
Q_ASSERT(pimpl);

View File

@ -21,6 +21,7 @@ THE SOFTWARE.
*/
#include "qsearchfield.h"
#include "../../src/ui/iconloader.h"
#include <QApplication>
#include <QEvent>
@ -32,9 +33,6 @@ THE SOFTWARE.
#include <QDir>
#include <QDebug>
#if 0
// All the code below isn't used currently (see qsearchfield.h)
class QSearchFieldPrivate : public QObject
{
public:
@ -65,20 +63,22 @@ QSearchField::QSearchField(QWidget *parent) : QWidget(parent)
connect(lineEdit, SIGNAL(textChanged(QString)),
this, SLOT(setText(QString)));
QIcon clearIcon(IconLoader::Load("edit-clear-locationbar-ltr"));
QToolButton *clearButton = new QToolButton(this);
QPixmap clearIcon(QString(":/Qocoa/qsearchfield_nonmac.png"));
clearButton->setIcon(QIcon(clearIcon));
clearButton->setIconSize(clearIcon.size());
clearButton->setFixedSize(clearIcon.size());
clearButton->setStyleSheet("border: none;");
clearButton->hide();
clearButton->setIcon(clearIcon);
clearButton->setIconSize(QSize(16, 16));
clearButton->setStyleSheet("border: none; padding: 0px;");
clearButton->resize(clearButton->sizeHint());
connect(clearButton, SIGNAL(clicked()), this, SLOT(clear()));
pimpl = new QSearchFieldPrivate(this, lineEdit, clearButton);
lineEdit->setStyleSheet(QString("QLineEdit { padding-right: %1px; } ").arg(pimpl->clearButtonPaddedWidth()));
const int width = qMax(lineEdit->minimumSizeHint().width(), pimpl->clearButtonPaddedWidth());
const int height = qMax(lineEdit->minimumSizeHint().height(), pimpl->clearButtonPaddedHeight());
const int frame_width = lineEdit->style()->pixelMetric(QStyle::PM_DefaultFrameWidth);
lineEdit->setStyleSheet(QString("QLineEdit { padding-left: %1px; } ").arg(clearButton->width()));
const int width = frame_width + qMax(lineEdit->minimumSizeHint().width(), pimpl->clearButtonPaddedWidth());
const int height = frame_width + qMax(lineEdit->minimumSizeHint().height(), pimpl->clearButtonPaddedHeight());
lineEdit->setMinimumSize(width, height);
QVBoxLayout *layout = new QVBoxLayout(this);
@ -94,8 +94,6 @@ void QSearchField::setText(const QString &text)
if (!(pimpl && pimpl->clearButton && pimpl->lineEdit))
return;
pimpl->clearButton->setVisible(!text.isEmpty());
if (text != this->text())
pimpl->lineEdit->setText(text);
}
@ -119,6 +117,18 @@ QString QSearchField::placeholderText() const {
#endif
}
void QSearchField::setFocus(Qt::FocusReason reason)
{
Q_ASSERT(pimpl && pimpl->lineEdit);
if (pimpl && pimpl->lineEdit)
pimpl->lineEdit->setFocus(reason);
}
void QSearchField::setFocus()
{
setFocus(Qt::OtherFocusReason);
}
void QSearchField::clear()
{
Q_ASSERT(pimpl && pimpl->lineEdit);
@ -153,7 +163,7 @@ void QSearchField::resizeEvent(QResizeEvent *resizeEvent)
return;
QWidget::resizeEvent(resizeEvent);
const int x = width() - pimpl->clearButtonPaddedWidth();
const int x = pimpl->lineEditFrameWidth();
const int y = (height() - pimpl->clearButton->height())/2;
pimpl->clearButton->move(x, y);
}
@ -172,5 +182,3 @@ bool QSearchField::eventFilter(QObject *o, QEvent *e)
}
return QWidget::eventFilter(o, e);
}
#endif // 0

Binary file not shown.

Before

Width:  |  Height:  |  Size: 644 B

View File

@ -1,5 +0,0 @@
<RCC>
<qresource prefix="/Qocoa">
<file>qsearchfield_nonmac.png</file>
</qresource>
</RCC>

View File

@ -1,73 +1,42 @@
<RCC>
<qresource prefix="/">
<file>mainwindow.css</file>
<file>songinfo.css</file>
<file>now_playing_tooltip.txt</file>
<file>schema/schema.sql</file>
<file>volumeslider-handle_glow.png</file>
<file>volumeslider-handle.png</file>
<file>volumeslider-inset.png</file>
<file>volumeslider-gradient.png</file>
<file>logo.png</file>
<file>icon.png</file>
<file>icon_large.png</file>
<file>icon_large_grey.png</file>
<file>blank.ttf</file>
<file>clementine-spotify-public.pem</file>
<file>currenttrack_bar_left.png</file>
<file>currenttrack_bar_mid.png</file>
<file>currenttrack_bar_right.png</file>
<file>currenttrack_pause.png</file>
<file>currenttrack_play.png</file>
<file>nomusic.png</file>
<file>last.fm/as.png</file>
<file>last.fm/lastfm.png</file>
<file>last.fm/loved_radio.png</file>
<file>last.fm/neighbour_radio.png</file>
<file>last.fm/personal_radio.png</file>
<file>last.fm/recommended_radio.png</file>
<file>spinner.gif</file>
<file>last.fm/ban.png</file>
<file>last.fm/love.png</file>
<file>last.fm/icon_radio.png</file>
<file>last.fm/icon_tag.png</file>
<file>last.fm/icon_user.png</file>
<file>last.fm/my_friends.png</file>
<file>last.fm/my_neighbours.png</file>
<file>last.fm/user_purple.png</file>
<file>providers/somafm.png</file>
<file>schema/schema-1.sql</file>
<file>schema/schema-2.sql</file>
<file>nocover.png</file>
<file>schema/schema-3.sql</file>
<file>schema/schema-4.sql</file>
<file>schema/schema-5.sql</file>
<file>osd_shadow_corner.png</file>
<file>osd_shadow_edge.png</file>
<file>schema/schema-6.sql</file>
<file>schema/schema-7.sql</file>
<file>tiny-pause.png</file>
<file>tiny-start.png</file>
<file>providers/magnatune.png</file>
<file>schema/schema-8.sql</file>
<file>schema/schema-9.sql</file>
<file>icons/22x22/spotify.png</file>
<file>icons/32x32/spotify.png</file>
<file>icons/48x48/spotify.png</file>
<file>globalsearch.css</file>
<file>grooveshark-valicert-ca.pem</file>
<file>hypnotoad.gif</file>
<file>icon_large_grey.png</file>
<file>icon_large.png</file>
<file>icon.png</file>
<file>icons/22x22/application-exit.png</file>
<file>icons/22x22/applications-internet.png</file>
<file>icons/22x22/configure.png</file>
<file>icons/22x22/document-open.png</file>
<file>icons/22x22/dialog-ok-apply.png</file>
<file>icons/22x22/dialog-warning.png</file>
<file>icons/22x22/document-new.png</file>
<file>icons/22x22/document-open-folder.png</file>
<file>icons/22x22/document-open.png</file>
<file>icons/22x22/document-open-remote.png</file>
<file>icons/22x22/document-save.png</file>
<file>icons/22x22/download.png</file>
<file>icons/22x22/drive-removable-media-usb-pendrive.png</file>
<file>icons/22x22/edit-clear-list.png</file>
<file>icons/22x22/edit-clear-locationbar-ltr.png</file>
<file>icons/22x22/edit-copy.png</file>
<file>icons/22x22/edit-delete.png</file>
<file>icons/22x22/edit-find.png</file>
<file>icons/22x22/edit-redo.png</file>
<file>icons/22x22/edit-rename.png</file>
<file>icons/22x22/edit-undo.png</file>
<file>icons/22x22/folder-new.png</file>
<file>icons/22x22/folder.png</file>
<file>icons/22x22/folder-sound.png</file>
<file>icons/22x22/go-down.png</file>
<file>icons/22x22/go-home.png</file>
<file>icons/22x22/go-jump.png</file>
<file>icons/22x22/go-next.png</file>
@ -75,8 +44,12 @@
<file>icons/22x22/go-up.png</file>
<file>icons/22x22/help-about.png</file>
<file>icons/22x22/help-hint.png</file>
<file>icons/22x22/input-keyboard.png</file>
<file>icons/22x22/ipodtouchicon.png</file>
<file>icons/22x22/list-add.png</file>
<file>icons/22x22/list-remove.png</file>
<file>icons/22x22/mail-message.png</file>
<file>icons/22x22/media-eject.png</file>
<file>icons/22x22/media-playback-pause.png</file>
<file>icons/22x22/media-playback-start.png</file>
<file>icons/22x22/media-playback-stop.png</file>
@ -84,31 +57,62 @@
<file>icons/22x22/media-playlist-shuffle.png</file>
<file>icons/22x22/media-skip-backward.png</file>
<file>icons/22x22/media-skip-forward.png</file>
<file>icons/22x22/multimedia-player-ipod-mini-blue.png</file>
<file>icons/22x22/multimedia-player-ipod-mini-gold.png</file>
<file>icons/22x22/multimedia-player-ipod-mini-green.png</file>
<file>icons/22x22/multimedia-player-ipod-mini-pink.png</file>
<file>icons/22x22/multimedia-player-ipod-mini-silver.png</file>
<file>icons/22x22/multimedia-player-ipod-nano-black.png</file>
<file>icons/22x22/multimedia-player-ipod-nano-green.png</file>
<file>icons/22x22/multimedia-player-ipod-nano-white.png</file>
<file>icons/22x22/multimedia-player-ipod-shuffle.png</file>
<file>icons/22x22/multimedia-player-ipod-standard-color.png</file>
<file>icons/22x22/multimedia-player-ipod-standard-monochrome.png</file>
<file>icons/22x22/multimedia-player-ipod-U2-color.png</file>
<file>icons/22x22/multimedia-player-ipod-U2-monochrome.png</file>
<file>icons/22x22/network-server.png</file>
<file>icons/22x22/phone-google-nexus-one.png</file>
<file>icons/22x22/phone-htc-g1-white.png</file>
<file>icons/22x22/phone-nokia-n900.png</file>
<file>icons/22x22/phone-palm-pre.png</file>
<file>icons/22x22/phone.png</file>
<file>icons/22x22/spotify.png</file>
<file>icons/22x22/user-away.png</file>
<file>icons/22x22/view-choose.png</file>
<file>icons/22x22/view-fullscreen.png</file>
<file>icons/22x22/view-media-equalizer.png</file>
<file>icons/22x22/view-media-lyrics.png</file>
<file>icons/22x22/view-media-playlist.png</file>
<file>icons/22x22/view-media-visualization.png</file>
<file>icons/22x22/view-refresh.png</file>
<file>icons/22x22/weather-showers-scattered.png</file>
<file>icons/22x22/x-clementine-album.png</file>
<file>icons/22x22/x-clementine-albums.png</file>
<file>icons/22x22/x-clementine-artist.png</file>
<file>icons/22x22/x-clementine-shuffle.png</file>
<file>icons/22x22/zoom-in.png</file>
<file>icons/32x32/application-exit.png</file>
<file>icons/32x32/applications-internet.png</file>
<file>icons/32x32/configure.png</file>
<file>icons/32x32/document-open.png</file>
<file>icons/32x32/document-new.png</file>
<file>icons/32x32/document-open-folder.png</file>
<file>icons/32x32/document-open.png</file>
<file>icons/32x32/document-open-remote.png</file>
<file>icons/32x32/document-save.png</file>
<file>icons/32x32/download.png</file>
<file>icons/32x32/drive-removable-media-usb-pendrive.png</file>
<file>icons/32x32/edit-clear-list.png</file>
<file>icons/32x32/edit-clear-locationbar-ltr.png</file>
<file>icons/32x32/edit-copy.png</file>
<file>icons/32x32/edit-delete.png</file>
<file>icons/32x32/edit-find.png</file>
<file>icons/32x32/edit-redo.png</file>
<file>icons/32x32/edit-rename.png</file>
<file>icons/32x32/edit-undo.png</file>
<file>icons/32x32/folder-new.png</file>
<file>icons/32x32/folder.png</file>
<file>icons/32x32/folder-sound.png</file>
<file>icons/32x32/go-down.png</file>
<file>icons/32x32/go-home.png</file>
<file>icons/32x32/go-jump.png</file>
<file>icons/32x32/go-next.png</file>
@ -116,8 +120,12 @@
<file>icons/32x32/go-up.png</file>
<file>icons/32x32/help-about.png</file>
<file>icons/32x32/help-hint.png</file>
<file>icons/32x32/input-keyboard.png</file>
<file>icons/32x32/ipodtouchicon.png</file>
<file>icons/32x32/list-add.png</file>
<file>icons/32x32/list-remove.png</file>
<file>icons/32x32/mail-message.png</file>
<file>icons/32x32/media-eject.png</file>
<file>icons/32x32/media-playback-pause.png</file>
<file>icons/32x32/media-playback-start.png</file>
<file>icons/32x32/media-playback-stop.png</file>
@ -125,31 +133,62 @@
<file>icons/32x32/media-playlist-shuffle.png</file>
<file>icons/32x32/media-skip-backward.png</file>
<file>icons/32x32/media-skip-forward.png</file>
<file>icons/32x32/multimedia-player-ipod-mini-blue.png</file>
<file>icons/32x32/multimedia-player-ipod-mini-gold.png</file>
<file>icons/32x32/multimedia-player-ipod-mini-green.png</file>
<file>icons/32x32/multimedia-player-ipod-mini-pink.png</file>
<file>icons/32x32/multimedia-player-ipod-mini-silver.png</file>
<file>icons/32x32/multimedia-player-ipod-nano-black.png</file>
<file>icons/32x32/multimedia-player-ipod-nano-green.png</file>
<file>icons/32x32/multimedia-player-ipod-nano-white.png</file>
<file>icons/32x32/multimedia-player-ipod-shuffle.png</file>
<file>icons/32x32/multimedia-player-ipod-standard-color.png</file>
<file>icons/32x32/multimedia-player-ipod-standard-monochrome.png</file>
<file>icons/32x32/multimedia-player-ipod-U2-color.png</file>
<file>icons/32x32/multimedia-player-ipod-U2-monochrome.png</file>
<file>icons/32x32/network-server.png</file>
<file>icons/32x32/phone-google-nexus-one.png</file>
<file>icons/32x32/phone-htc-g1-white.png</file>
<file>icons/32x32/phone-nokia-n900.png</file>
<file>icons/32x32/phone-palm-pre.png</file>
<file>icons/32x32/phone.png</file>
<file>icons/32x32/search.png</file>
<file>icons/32x32/spotify.png</file>
<file>icons/32x32/tools-wizard.png</file>
<file>icons/32x32/view-choose.png</file>
<file>icons/32x32/view-fullscreen.png</file>
<file>icons/32x32/view-media-equalizer.png</file>
<file>icons/32x32/view-media-lyrics.png</file>
<file>icons/32x32/view-media-playlist.png</file>
<file>icons/32x32/view-media-visualization.png</file>
<file>icons/32x32/view-refresh.png</file>
<file>icons/32x32/weather-showers-scattered.png</file>
<file>icons/32x32/wiimotedev.png</file>
<file>icons/32x32/x-clementine-artist.png</file>
<file>icons/32x32/x-clementine-shuffle.png</file>
<file>icons/32x32/zoom-in.png</file>
<file>icons/48x48/application-exit.png</file>
<file>icons/48x48/applications-internet.png</file>
<file>icons/48x48/configure.png</file>
<file>icons/48x48/document-open.png</file>
<file>icons/48x48/document-new.png</file>
<file>icons/48x48/document-open-folder.png</file>
<file>icons/48x48/document-open.png</file>
<file>icons/48x48/document-open-remote.png</file>
<file>icons/48x48/document-save.png</file>
<file>icons/48x48/download.png</file>
<file>icons/48x48/drive-removable-media-usb-pendrive.png</file>
<file>icons/48x48/edit-clear-list.png</file>
<file>icons/48x48/edit-clear-locationbar-ltr.png</file>
<file>icons/48x48/edit-copy.png</file>
<file>icons/48x48/edit-delete.png</file>
<file>icons/48x48/edit-find.png</file>
<file>icons/48x48/edit-redo.png</file>
<file>icons/48x48/edit-rename.png</file>
<file>icons/48x48/edit-undo.png</file>
<file>icons/48x48/folder-new.png</file>
<file>icons/48x48/folder.png</file>
<file>icons/48x48/folder-sound.png</file>
<file>icons/48x48/go-down.png</file>
<file>icons/48x48/go-home.png</file>
<file>icons/48x48/go-jump.png</file>
<file>icons/48x48/go-next.png</file>
@ -157,8 +196,12 @@
<file>icons/48x48/go-up.png</file>
<file>icons/48x48/help-about.png</file>
<file>icons/48x48/help-hint.png</file>
<file>icons/48x48/input-keyboard.png</file>
<file>icons/48x48/ipodtouchicon.png</file>
<file>icons/48x48/list-add.png</file>
<file>icons/48x48/list-remove.png</file>
<file>icons/48x48/mail-message.png</file>
<file>icons/48x48/media-eject.png</file>
<file>icons/48x48/media-optical.png</file>
<file>icons/48x48/media-playback-pause.png</file>
<file>icons/48x48/media-playback-start.png</file>
@ -167,185 +210,142 @@
<file>icons/48x48/media-playlist-shuffle.png</file>
<file>icons/48x48/media-skip-backward.png</file>
<file>icons/48x48/media-skip-forward.png</file>
<file>icons/48x48/view-choose.png</file>
<file>icons/48x48/view-media-equalizer.png</file>
<file>icons/48x48/view-media-lyrics.png</file>
<file>icons/48x48/view-media-playlist.png</file>
<file>icons/48x48/view-media-visualization.png</file>
<file>icons/48x48/view-refresh.png</file>
<file>icons/48x48/weather-showers-scattered.png</file>
<file>icons/48x48/x-clementine-shuffle.png</file>
<file>icons/48x48/zoom-in.png</file>
<file>icons/22x22/x-clementine-album.png</file>
<file>icons/22x22/x-clementine-albums.png</file>
<file>icons/22x22/x-clementine-artist.png</file>
<file>icons/32x32/x-clementine-artist.png</file>
<file>icons/48x48/x-clementine-artist.png</file>
<file>icons/22x22/document-new.png</file>
<file>icons/32x32/document-new.png</file>
<file>icons/48x48/document-new.png</file>
<file>schema/schema-10.sql</file>
<file>schema/schema-11.sql</file>
<file>osd_background.png</file>
<file>icons/22x22/view-fullscreen.png</file>
<file>icons/32x32/view-fullscreen.png</file>
<file>icons/48x48/view-fullscreen.png</file>
<file>schema/schema-12.sql</file>
<file>schema/schema-13.sql</file>
<file>icons/22x22/input-keyboard.png</file>
<file>icons/32x32/input-keyboard.png</file>
<file>icons/48x48/input-keyboard.png</file>
<file>icons/22x22/edit-delete.png</file>
<file>icons/32x32/edit-delete.png</file>
<file>icons/48x48/edit-delete.png</file>
<file>schema/schema-14.sql</file>
<file>icons/22x22/multimedia-player-ipod-mini-blue.png</file>
<file>icons/22x22/multimedia-player-ipod-mini-gold.png</file>
<file>icons/22x22/multimedia-player-ipod-mini-green.png</file>
<file>icons/22x22/multimedia-player-ipod-mini-pink.png</file>
<file>icons/22x22/multimedia-player-ipod-mini-silver.png</file>
<file>icons/22x22/multimedia-player-ipod-nano-black.png</file>
<file>icons/22x22/multimedia-player-ipod-nano-white.png</file>
<file>icons/22x22/multimedia-player-ipod-nano-green.png</file>
<file>icons/22x22/multimedia-player-ipod-shuffle.png</file>
<file>icons/22x22/multimedia-player-ipod-standard-color.png</file>
<file>icons/22x22/multimedia-player-ipod-standard-monochrome.png</file>
<file>icons/22x22/multimedia-player-ipod-U2-color.png</file>
<file>icons/22x22/multimedia-player-ipod-U2-monochrome.png</file>
<file>icons/32x32/multimedia-player-ipod-mini-blue.png</file>
<file>icons/32x32/multimedia-player-ipod-mini-gold.png</file>
<file>icons/32x32/multimedia-player-ipod-mini-green.png</file>
<file>icons/32x32/multimedia-player-ipod-mini-pink.png</file>
<file>icons/32x32/multimedia-player-ipod-mini-silver.png</file>
<file>icons/32x32/multimedia-player-ipod-nano-black.png</file>
<file>icons/32x32/multimedia-player-ipod-nano-white.png</file>
<file>icons/32x32/multimedia-player-ipod-nano-green.png</file>
<file>icons/32x32/multimedia-player-ipod-shuffle.png</file>
<file>icons/32x32/multimedia-player-ipod-standard-color.png</file>
<file>icons/32x32/multimedia-player-ipod-standard-monochrome.png</file>
<file>icons/32x32/multimedia-player-ipod-U2-color.png</file>
<file>icons/32x32/multimedia-player-ipod-U2-monochrome.png</file>
<file>icons/48x48/multimedia-player-ipod-mini-blue.png</file>
<file>icons/48x48/multimedia-player-ipod-mini-gold.png</file>
<file>icons/48x48/multimedia-player-ipod-mini-green.png</file>
<file>icons/48x48/multimedia-player-ipod-mini-pink.png</file>
<file>icons/48x48/multimedia-player-ipod-mini-silver.png</file>
<file>icons/48x48/multimedia-player-ipod-nano-black.png</file>
<file>icons/48x48/multimedia-player-ipod-nano-white.png</file>
<file>icons/48x48/multimedia-player-ipod-nano-green.png</file>
<file>icons/48x48/multimedia-player-ipod-nano-white.png</file>
<file>icons/48x48/multimedia-player-ipod-shuffle.png</file>
<file>icons/48x48/multimedia-player-ipod-standard-color.png</file>
<file>icons/48x48/multimedia-player-ipod-standard-monochrome.png</file>
<file>icons/48x48/multimedia-player-ipod-U2-color.png</file>
<file>icons/48x48/multimedia-player-ipod-U2-monochrome.png</file>
<file>icons/22x22/phone-google-nexus-one.png</file>
<file>icons/22x22/phone-htc-g1-white.png</file>
<file>icons/22x22/phone-nokia-n900.png</file>
<file>icons/22x22/phone-palm-pre.png</file>
<file>icons/32x32/phone-google-nexus-one.png</file>
<file>icons/32x32/phone-htc-g1-white.png</file>
<file>icons/32x32/phone-nokia-n900.png</file>
<file>icons/32x32/phone-palm-pre.png</file>
<file>icons/48x48/network-server.png</file>
<file>icons/48x48/phone-google-nexus-one.png</file>
<file>icons/48x48/phone-htc-g1-white.png</file>
<file>icons/48x48/phone-nokia-n900.png</file>
<file>icons/48x48/phone-palm-pre.png</file>
<file>icons/22x22/phone.png</file>
<file>icons/32x32/phone.png</file>
<file>icons/48x48/phone.png</file>
<file>icons/22x22/drive-removable-media-usb-pendrive.png</file>
<file>icons/32x32/drive-removable-media-usb-pendrive.png</file>
<file>icons/48x48/drive-removable-media-usb-pendrive.png</file>
<file>schema/schema-15.sql</file>
<file>schema/device-schema.sql</file>
<file>icons/22x22/go-down.png</file>
<file>icons/32x32/go-down.png</file>
<file>icons/48x48/go-down.png</file>
<file>hypnotoad.gif</file>
<file>blank.ttf</file>
<file>schema/schema-16.sql</file>
<file>icons/22x22/media-eject.png</file>
<file>icons/32x32/media-eject.png</file>
<file>icons/48x48/media-eject.png</file>
<file>icons/22x22/ipodtouchicon.png</file>
<file>icons/32x32/ipodtouchicon.png</file>
<file>icons/48x48/ipodtouchicon.png</file>
<file>icons/32x32/wiimotedev.png</file>
<file>schema/schema-17.sql</file>
<file>icons/48x48/spotify.png</file>
<file>icons/48x48/view-choose.png</file>
<file>icons/48x48/view-fullscreen.png</file>
<file>icons/48x48/view-media-equalizer.png</file>
<file>icons/48x48/view-media-lyrics.png</file>
<file>icons/48x48/view-media-playlist.png</file>
<file>icons/48x48/view-media-visualization.png</file>
<file>icons/48x48/view-refresh.png</file>
<file>icons/48x48/weather-showers-scattered.png</file>
<file>icons/48x48/x-clementine-artist.png</file>
<file>icons/48x48/x-clementine-shuffle.png</file>
<file>icons/48x48/zoom-in.png</file>
<file>last.fm/as_disabled.png</file>
<file>last.fm/as_light.png</file>
<file>last.fm/as.png</file>
<file>last.fm/ban.png</file>
<file>last.fm/icon_radio.png</file>
<file>last.fm/icon_tag.png</file>
<file>last.fm/icon_user.png</file>
<file>last.fm/lastfm.png</file>
<file>last.fm/loved_radio.png</file>
<file>last.fm/love.png</file>
<file>last.fm/my_friends.png</file>
<file>last.fm/my_neighbours.png</file>
<file>last.fm/neighbour_radio.png</file>
<file>last.fm/personal_radio.png</file>
<file>last.fm/recommended_radio.png</file>
<file>last.fm/user_purple.png</file>
<file>logo.png</file>
<file>lumberjacksong.txt</file>
<file>lyrics/ultimate_providers.xml</file>
<file>sidebar_background.png</file>
<file>providers/wikipedia.png</file>
<file>providers/aol.png</file>
<file>mainwindow.css</file>
<file>nocover.png</file>
<file>nomusic.png</file>
<file>now_playing_tooltip.txt</file>
<file>nyancat.png</file>
<file>osd_background.png</file>
<file>osd_shadow_corner.png</file>
<file>osd_shadow_edge.png</file>
<file>providers/amazon.png</file>
<file>providers/myspace.png</file>
<file>providers/aol.png</file>
<file>providers/bbc.png</file>
<file>providers/cdbaby.png</file>
<file>providers/digitallyimported-32.png</file>
<file>providers/digitallyimported.png</file>
<file>providers/echonest.png</file>
<file>providers/googledrive.png</file>
<file>providers/grooveshark.png</file>
<file>providers/itunes.png</file>
<file>providers/jamendo.png</file>
<file>providers/magnatune.png</file>
<file>providers/mog.png</file>
<file>providers/mtvmusic.png</file>
<file>providers/cdbaby.png</file>
<file>providers/echonest.png</file>
<file>providers/musicbrainz.png</file>
<file>providers/mygpo32.png</file>
<file>providers/myspace.png</file>
<file>providers/podcast16.png</file>
<file>providers/podcast32.png</file>
<file>providers/skyfm.png</file>
<file>providers/somafm.png</file>
<file>providers/songkick.png</file>
<file>providers/twitter.png</file>
<file>providers/googledrive.png</file>
<file>lumberjacksong.txt</file>
<file>providers/wikipedia.png</file>
<file>sample.mood</file>
<file>schema/device-schema.sql</file>
<file>schema/jamendo.sql</file>
<file>schema/schema-10.sql</file>
<file>schema/schema-11.sql</file>
<file>schema/schema-12.sql</file>
<file>schema/schema-13.sql</file>
<file>schema/schema-14.sql</file>
<file>schema/schema-15.sql</file>
<file>schema/schema-16.sql</file>
<file>schema/schema-17.sql</file>
<file>schema/schema-18.sql</file>
<file>star-off.png</file>
<file>star-on.png</file>
<file>icons/22x22/folder.png</file>
<file>icons/32x32/folder.png</file>
<file>icons/48x48/folder.png</file>
<file>smartplaylistsearchterm.css</file>
<file>schema/schema-19.sql</file>
<file>schema/schema-1.sql</file>
<file>schema/schema-20.sql</file>
<file>schema/schema-21.sql</file>
<file>providers/jamendo.png</file>
<file>schema/schema-22.sql</file>
<file>schema/jamendo.sql</file>
<file>schema/schema-23.sql</file>
<file>schema/schema-24.sql</file>
<file>schema/schema-25.sql</file>
<file>schema/schema-26.sql</file>
<file>schema/schema-27.sql</file>
<file>schema/schema-28.sql</file>
<file>icons/22x22/network-server.png</file>
<file>icons/32x32/network-server.png</file>
<file>icons/48x48/network-server.png</file>
<file>providers/musicbrainz.png</file>
<file>last.fm/as_disabled.png</file>
<file>last.fm/as_light.png</file>
<file>icons/32x32/tools-wizard.png</file>
<file>icons/22x22/mail-message.png</file>
<file>icons/32x32/mail-message.png</file>
<file>icons/48x48/mail-message.png</file>
<file>schema/schema-29.sql</file>
<file>schema/schema-2.sql</file>
<file>schema/schema-30.sql</file>
<file>schema/schema-31.sql</file>
<file>schema/schema-32.sql</file>
<file>icons/22x22/edit-find.png</file>
<file>icons/32x32/edit-find.png</file>
<file>icons/48x48/edit-find.png</file>
<file>schema/schema-33.sql</file>
<file>spotify-core-logo-128x128.png</file>
<file>icons/22x22/dialog-warning.png</file>
<file>icons/22x22/dialog-ok-apply.png</file>
<file>schema/schema-34.sql</file>
<file>nyancat.png</file>
<file>providers/digitallyimported.png</file>
<file>providers/skyfm.png</file>
<file>providers/digitallyimported-32.png</file>
<file>providers/grooveshark.png</file>
<file>globalsearch.css</file>
<file>clementine-spotify-public.pem</file>
<file>icons/22x22/user-away.png</file>
<file>icons/32x32/search.png</file>
<file>schema/schema-35.sql</file>
<file>schema/schema-36.sql</file>
<file>grooveshark-valicert-ca.pem</file>
<file>schema/schema-37.sql</file>
<file>providers/podcast16.png</file>
<file>providers/podcast32.png</file>
<file>providers/mygpo32.png</file>
<file>providers/itunes.png</file>
<file>providers/bbc.png</file>
<file>sample.mood</file>
<file>schema/schema-3.sql</file>
<file>schema/schema-4.sql</file>
<file>schema/schema-5.sql</file>
<file>schema/schema-6.sql</file>
<file>schema/schema-7.sql</file>
<file>schema/schema-8.sql</file>
<file>schema/schema-9.sql</file>
<file>schema/schema.sql</file>
<file>sidebar_background.png</file>
<file>smartplaylistsearchterm.css</file>
<file>songinfo.css</file>
<file>spinner.gif</file>
<file>spotify-core-logo-128x128.png</file>
<file>star-off.png</file>
<file>star-on.png</file>
<file>tiny-pause.png</file>
<file>tiny-start.png</file>
<file>twitter.css</file>
<file>volumeslider-gradient.png</file>
<file>volumeslider-handle_glow.png</file>
<file>volumeslider-handle.png</file>
<file>volumeslider-inset.png</file>
</qresource>
</RCC>

View File

@ -21,8 +21,9 @@ darwin QMenu {
}
QToolButton {
border: none;
padding: 2px;
border: 2px solid transparent;
border-radius: 3px;
padding: 1px;
}
QToolButton::menu-button {
@ -37,8 +38,6 @@ QToolButton[popupMode="1"] {
QToolButton:hover {
border: 2px solid %palette-highlight;
background-color: %palette-highlight-lighter;
border-radius: 3px;
padding: 1px;
}
QToolButton:hover[popupMode="1"] {
@ -48,8 +47,6 @@ QToolButton:hover[popupMode="1"] {
QToolButton:pressed {
border: 2px solid %palette-highlight-darker;
background-color: %palette-highlight-lighter;
border-radius: 3px;
padding: 1px;
}
QToolButton:pressed[popupMode="1"] {

View File

@ -5,7 +5,6 @@ include_directories(${CMAKE_SOURCE_DIR}/src)
set(SOURCES
core/closure.cpp
core/concurrentrun.cpp
core/encoding.cpp
core/logging.cpp
core/messagehandler.cpp

View File

@ -1,11 +0,0 @@
#include "concurrentrun.h"
namespace ConcurrentRun {
void Run(
QThreadPool* threadpool,
std::tr1::function<void ()> function) {
(new ThreadFunctorVoid(function))->Start(threadpool);
}
} // namespace ConcurrentRun

View File

@ -20,6 +20,9 @@
#include <tr1/functional>
#include <boost/type_traits.hpp>
#include <boost/utility.hpp>
#include <QFuture>
#include <QRunnable>
#include <QThreadPool>
@ -40,11 +43,18 @@
Run() functions are used for convenience: to directly create a new
ThreadFunctor object and start it.
Currently, only functions with one or two arguments and a return value are
Currently, only functions with zero, one, two or three arguments are
supported, but other might be added easily for X arguments by defining a new
ThreadFunctorBasX class, and add new Run() function.
ThreadFunctorX class (and eventually a second class for handling functions
which return void: see existing classes for examples), and add new Run()
function.
*/
/*
Base abstract classes ThreadFunctorBase and ThreadFunctor (for void and
non-void result):
*/
template<typename ReturnType>
class ThreadFunctorBase : public QFutureInterface<ReturnType>, public QRunnable {
public:
@ -62,6 +72,15 @@ class ThreadFunctorBase : public QFutureInterface<ReturnType>, public QRunnable
}
virtual void run() = 0;
};
// Base implemenation for functions having a result to be returned
template<typename ReturnType, class Enabled = void>
class ThreadFunctor : public ThreadFunctorBase<ReturnType> {
public:
ThreadFunctor() {}
virtual void run() = 0;
void End() {
this->reportResult(result_);
@ -72,24 +91,64 @@ class ThreadFunctorBase : public QFutureInterface<ReturnType>, public QRunnable
ReturnType result_;
};
// Use bool as a placeholder result.
class ThreadFunctorVoid : public ThreadFunctorBase<bool> {
// Base implementation for functions with void result
template<typename ReturnType>
class ThreadFunctor<ReturnType, typename boost::enable_if<boost::is_void<ReturnType> >::type>
: public ThreadFunctorBase<ReturnType> {
public:
ThreadFunctorVoid(std::tr1::function<void ()> function)
ThreadFunctor() {}
virtual void run() = 0;
void End() {
this->reportFinished();
}
};
/*
ThreadFunctor with no arguments:
*/
// Non-void result
template<typename ReturnType, class Enabled = void>
class ThreadFunctor0 : public ThreadFunctor<ReturnType> {
public:
ThreadFunctor0(std::tr1::function<ReturnType ()> function)
: function_(function)
{ }
void run() {
this->result_ = function_();
ThreadFunctor<ReturnType>::End();
}
private:
std::tr1::function<ReturnType ()> function_;
};
// Void result
template<typename ReturnType>
class ThreadFunctor0<ReturnType, typename boost::enable_if<boost::is_void<ReturnType> >::type>
: public ThreadFunctor<ReturnType> {
public:
ThreadFunctor0(std::tr1::function<ReturnType ()> function)
: function_(function)
{ }
void run() {
function_();
ThreadFunctorBase<bool>::End();
ThreadFunctor<ReturnType>::End();
}
private:
std::tr1::function<void ()> function_;
private:
std::tr1::function<ReturnType ()> function_;
};
template<typename ReturnType, typename Arg>
class ThreadFunctor1 : public ThreadFunctorBase<ReturnType> {
/*
ThreadFunctor with one argument:
*/
// Non-void result
template<typename ReturnType, typename Arg, class Enabled = void>
class ThreadFunctor1 : public ThreadFunctor<ReturnType> {
public:
ThreadFunctor1(std::tr1::function<ReturnType (Arg)> function,
const Arg& arg)
@ -99,7 +158,7 @@ public:
void run() {
this->result_ = function_(arg_);
ThreadFunctorBase<ReturnType>::End();
ThreadFunctor<ReturnType>::End();
}
private:
@ -107,8 +166,33 @@ private:
Arg arg_;
};
template<typename ReturnType, typename Arg1, typename Arg2>
class ThreadFunctor2 : public ThreadFunctorBase<ReturnType> {
// Void result
template<typename ReturnType, typename Arg>
class ThreadFunctor1<ReturnType, Arg, typename boost::enable_if<boost::is_void<ReturnType> >::type>
: public ThreadFunctor<ReturnType> {
public:
ThreadFunctor1(std::tr1::function<ReturnType (Arg)> function,
const Arg& arg)
: function_(function),
arg_(arg)
{ }
void run() {
function_(arg_);
ThreadFunctor<ReturnType>::End();
}
private:
std::tr1::function<ReturnType (Arg)> function_;
Arg arg_;
};
/*
ThreadFunctor with two arguments:
*/
// Non-void result
template<typename ReturnType, typename Arg1, typename Arg2, class Enabled = void>
class ThreadFunctor2 : public ThreadFunctor<ReturnType> {
public:
ThreadFunctor2(std::tr1::function<ReturnType (Arg1, Arg2)> function,
const Arg1& arg1, const Arg2& arg2)
@ -119,7 +203,7 @@ public:
void run() {
this->result_ = function_(arg1_, arg2_);
ThreadFunctorBase<ReturnType>::End();
ThreadFunctor<ReturnType>::End();
}
private:
@ -128,10 +212,94 @@ private:
Arg2 arg2_;
};
// Void result
template<typename ReturnType, typename Arg1, typename Arg2>
class ThreadFunctor2<ReturnType, Arg1, Arg2, typename boost::enable_if<boost::is_void<ReturnType> >::type>
: public ThreadFunctor<ReturnType> {
public:
ThreadFunctor2(std::tr1::function<ReturnType (Arg1, Arg2)> function,
const Arg1& arg1, const Arg2& arg2)
: function_(function),
arg1_(arg1),
arg2_(arg2)
{ }
void run() {
function_(arg1_, arg2_);
ThreadFunctor<ReturnType>::End();
}
private:
std::tr1::function<ReturnType (Arg1, Arg2)> function_;
Arg1 arg1_;
Arg2 arg2_;
};
/*
ThreadFunctor with three arguments:
*/
// Non-void result
template<typename ReturnType, typename Arg1, typename Arg2, typename Arg3, class Enabled = void>
class ThreadFunctor3 : public ThreadFunctor<ReturnType> {
public:
ThreadFunctor3(std::tr1::function<ReturnType (Arg1, Arg2, Arg3)> function,
const Arg1& arg1, const Arg2& arg2, const Arg3& arg3)
: function_(function),
arg1_(arg1),
arg2_(arg2),
arg3_(arg3)
{ }
void run() {
this->result_ = function_(arg1_, arg2_, arg3_);
ThreadFunctor<ReturnType>::End();
}
private:
std::tr1::function<ReturnType (Arg1, Arg2, Arg3)> function_;
Arg1 arg1_;
Arg2 arg2_;
Arg3 arg3_;
};
// Void result
template<typename ReturnType, typename Arg1, typename Arg2, typename Arg3>
class ThreadFunctor3<ReturnType, Arg1, Arg2, Arg3, typename boost::enable_if<boost::is_void<ReturnType> >::type>
: public ThreadFunctor<ReturnType> {
public:
ThreadFunctor3(std::tr1::function<ReturnType (Arg1, Arg2, Arg3)> function,
const Arg1& arg1, const Arg2& arg2, const Arg3& arg3)
: function_(function),
arg1_(arg1),
arg2_(arg2),
arg3_(arg3)
{ }
void run() {
function_(arg1_, arg2_, arg3_);
ThreadFunctor<ReturnType>::End();
}
private:
std::tr1::function<ReturnType (Arg1, Arg2, Arg3)> function_;
Arg1 arg1_;
Arg2 arg2_;
Arg3 arg3_;
};
/*
Run functions
*/
namespace ConcurrentRun {
void Run(
template<typename ReturnType>
QFuture<ReturnType> Run(
QThreadPool* threadpool,
std::tr1::function<void ()> function);
std::tr1::function<ReturnType ()> function) {
return (new ThreadFunctor0<ReturnType>(function))->Start(threadpool);
}
template<typename ReturnType, typename Arg>
QFuture<ReturnType> Run(
@ -150,6 +318,15 @@ namespace ConcurrentRun {
return (new ThreadFunctor2<ReturnType, Arg1, Arg2>(function, arg1, arg2))->Start(threadpool);
}
template<typename ReturnType, typename Arg1, typename Arg2, typename Arg3>
QFuture<ReturnType> Run(
QThreadPool* threadpool,
std::tr1::function<ReturnType (Arg1, Arg2, Arg3)> function,
const Arg1& arg1, const Arg2& arg2, const Arg3& arg3) {
return (new ThreadFunctor3<ReturnType, Arg1, Arg2, Arg3>(function, arg1, arg2, arg3))->Start(threadpool);
}
}
#endif // CONCURRENTRUN_H

View File

@ -162,7 +162,6 @@ set(SOURCES
internet/geolocator.cpp
internet/googledriveservice.cpp
internet/groovesharkradio.cpp
internet/groovesharksearchplaylisttype.cpp
internet/groovesharkservice.cpp
internet/groovesharksettingspage.cpp
internet/groovesharkurlhandler.cpp
@ -776,7 +775,6 @@ optional_source(HAVE_SPOTIFY
SOURCES
globalsearch/spotifysearchprovider.cpp
internet/spotifyblobdownloader.cpp
internet/spotifysearchplaylisttype.cpp
internet/spotifyserver.cpp
internet/spotifyservice.cpp
internet/spotifysettingspage.cpp

View File

@ -349,7 +349,7 @@ void Player::SeekTo(int seconds) {
// If the length is 0 then either there is no song playing, or the song isn't
// seekable.
if (length_nanosec == 0) {
if (length_nanosec <= 0) {
return;
}

View File

@ -17,6 +17,7 @@
#include "config.h"
#include "songloader.h"
#include "core/concurrentrun.h"
#include "core/logging.h"
#include "core/song.h"
#include "core/signalchecker.h"
@ -38,7 +39,6 @@
#include <QFileInfo>
#include <QTimer>
#include <QUrl>
#include <QtConcurrentRun>
#include <QtDebug>
#include <boost/bind.hpp>
@ -229,7 +229,8 @@ SongLoader::Result SongLoader::LoadLocal(const QString& filename, bool block,
// inside right away.
if (QFileInfo(filename).isDir()) {
if (!block) {
QtConcurrent::run(this, &SongLoader::LoadLocalDirectoryAndEmit, filename);
ConcurrentRun::Run<void>(&thread_pool_,
boost::bind(&SongLoader::LoadLocalDirectoryAndEmit, this, filename));
return WillLoadAsync;
} else {
LoadLocalDirectory(filename);
@ -261,7 +262,8 @@ SongLoader::Result SongLoader::LoadLocal(const QString& filename, bool block,
// It's a playlist!
if (!block) {
QtConcurrent::run(this, &SongLoader::LoadPlaylistAndEmit, parser, filename);
ConcurrentRun::Run<void>(&thread_pool_,
boost::bind(&SongLoader::LoadPlaylistAndEmit, this, parser, filename));
return WillLoadAsync;
} else {
LoadPlaylist(parser, filename);
@ -315,22 +317,27 @@ SongLoader::Result SongLoader::LoadLocal(const QString& filename, bool block,
void SongLoader::EffectiveSongsLoad() {
for (int i = 0; i < songs_.size(); i++) {
Song& song = songs_[i];
EffectiveSongLoad(&songs_[i]);
}
}
if (song.filetype() != Song::Type_Unknown) {
// Maybe we loaded the metadata already, for example from a cuesheet.
continue;
}
void SongLoader::EffectiveSongLoad(Song* song) {
if (!song)
return;
// First, try to get the song from the library
Song library_song = library_->GetSongByUrl(song.url());
if (library_song.is_valid()) {
song = library_song;
} else {
// it's a normal media file
QString filename = song.url().toLocalFile();
TagReaderClient::Instance()->ReadFileBlocking(filename, &song);
}
if (song->filetype() != Song::Type_Unknown) {
// Maybe we loaded the metadata already, for example from a cuesheet.
return;
}
// First, try to get the song from the library
Song library_song = library_->GetSongByUrl(song->url());
if (library_song.is_valid()) {
*song = library_song;
} else {
// it's a normal media file
QString filename = song->url().toLocalFile();
TagReaderClient::Instance()->ReadFileBlocking(filename, song);
}
}
@ -372,6 +379,13 @@ void SongLoader::LoadLocalDirectory(const QString& filename) {
}
qStableSort(songs_.begin(), songs_.end(), CompareSongs);
// Load the first song: all songs will be loaded async, but we want the first
// one in our list to be fully loaded, so if the user has the "Start playing
// when adding to playlist" preference behaviour set, it can enjoy the first
// song being played (seek it, have moodbar, etc.)
if (!songs_.isEmpty())
EffectiveSongLoad(&(*songs_.begin()));
}
void SongLoader::AddAsRawStream() {

View File

@ -19,6 +19,7 @@
#define SONGLOADER_H
#include <QObject>
#include <QThreadPool>
#include <QUrl>
#include "song.h"
@ -64,6 +65,7 @@ public:
// playlist and replace the partially-loaded items by the new ones, fully
// loaded.
void EffectiveSongsLoad();
void EffectiveSongLoad(Song* song);
Result LoadAudioCD();
signals:
@ -128,6 +130,8 @@ private:
LibraryBackendInterface* library_;
boost::shared_ptr<GstElement> pipeline_;
QThreadPool thread_pool_;
};
#endif // SONGLOADER_H

View File

@ -187,7 +187,7 @@ DeviceManager::DeviceManager(Application* app, QObject *parent)
// This reads from the database and contends on the database mutex, which can
// be very slow on startup.
ConcurrentRun::Run(&thread_pool_, bind(&DeviceManager::LoadAllDevices, this));
ConcurrentRun::Run<void>(&thread_pool_, bind(&DeviceManager::LoadAllDevices, this));
// This proxy model only shows connected devices
connected_devices_model_ = new DeviceStateFilterModel(this);

View File

@ -33,8 +33,6 @@
# include "internet/spotifyservice.h"
#endif
#include <QtConcurrentRun>
const int GstEnginePipeline::kGstStateTimeoutNanosecs = 10000000;
const int GstEnginePipeline::kFaderFudgeMsec = 2000;

View File

@ -16,6 +16,12 @@
*/
#include "spotifysearchprovider.h"
#include <ctime>
#include <boost/random/mersenne_twister.hpp>
#include <boost/random/uniform_int.hpp>
#include "core/logging.h"
#include "internet/internetmodel.h"
#include "internet/spotifyserver.h"
@ -29,7 +35,7 @@ SpotifySearchProvider::SpotifySearchProvider(Application* app, QObject* parent)
{
Init("Spotify", "spotify", QIcon(":icons/32x32/spotify.png"),
WantsDelayedQueries | WantsSerialisedArtQueries | ArtIsProbablyRemote |
CanShowConfig);
CanShowConfig | CanGiveSuggestions);
}
SpotifyServer* SpotifySearchProvider::server() {
@ -48,6 +54,10 @@ SpotifyServer* SpotifySearchProvider::server() {
connect(server_, SIGNAL(ImageLoaded(QString,QImage)),
SLOT(ArtLoadedSlot(QString,QImage)));
connect(server_, SIGNAL(destroyed()), SLOT(ServerDestroyed()));
connect(server_, SIGNAL(StarredLoaded(pb::spotify::LoadPlaylistResponse)),
SLOT(SuggestionsLoaded(pb::spotify::LoadPlaylistResponse)));
connect(server_, SIGNAL(ToplistBrowseResults(pb::spotify::BrowseToplistResponse)),
SLOT(SuggestionsLoaded(pb::spotify::BrowseToplistResponse)));
return server_;
}
@ -143,3 +153,72 @@ void SpotifySearchProvider::ShowConfig() {
return service_->ShowConfig();
}
}
void SpotifySearchProvider::AddSuggestionFromTrack(
const pb::spotify::Track& track) {
if (!track.title().empty()) {
suggestions_.insert(QString::fromUtf8(track.title().c_str()));
}
for (int j = 0; j < track.artist_size(); ++j) {
if (!track.artist(j).empty()) {
suggestions_.insert(QString::fromUtf8(track.artist(j).c_str()));
}
}
if (!track.album().empty()) {
suggestions_.insert(QString::fromUtf8(track.album().c_str()));
}
}
void SpotifySearchProvider::AddSuggestionFromAlbum(
const pb::spotify::Album& album) {
AddSuggestionFromTrack(album.metadata());
for (int i = 0; i < album.track_size(); ++i) {
AddSuggestionFromTrack(album.track(i));
}
}
void SpotifySearchProvider::SuggestionsLoaded(
const pb::spotify::LoadPlaylistResponse& playlist) {
for (int i = 0; i < playlist.track_size(); ++i) {
AddSuggestionFromTrack(playlist.track(i));
}
}
void SpotifySearchProvider::SuggestionsLoaded(
const pb::spotify::BrowseToplistResponse& response) {
for (int i = 0; i < response.track_size(); ++i) {
AddSuggestionFromTrack(response.track(i));
}
for (int i = 0; i < response.album_size(); ++i) {
AddSuggestionFromAlbum(response.album(i));
}
}
void SpotifySearchProvider::LoadSuggestions() {
if (!server()) {
return;
}
server()->LoadStarred();
server()->LoadToplist();
}
QStringList SpotifySearchProvider::GetSuggestions(int count) {
if (suggestions_.empty()) {
LoadSuggestions();
return QStringList();
}
QStringList all_suggestions = suggestions_.toList();
boost::mt19937 gen(std::time(0));
boost::uniform_int<> random(0, all_suggestions.size() - 1);
QSet<QString> candidates;
const int max = qMin(count, all_suggestions.size());
while (candidates.size() < max) {
const int index = random(gen);
candidates.insert(all_suggestions[index]);
}
return candidates.toList();
}

View File

@ -33,6 +33,7 @@ public:
void SearchAsync(int id, const QString& query);
void LoadArtAsync(int id, const Result& result);
QStringList GetSuggestions(int count);
bool IsLoggedIn();
void ShowConfig();
@ -41,10 +42,16 @@ private slots:
void ServerDestroyed();
void SearchFinishedSlot(const pb::spotify::SearchResponse& response);
void ArtLoadedSlot(const QString& id, const QImage& image);
void SuggestionsLoaded(const pb::spotify::LoadPlaylistResponse& response);
void SuggestionsLoaded(const pb::spotify::BrowseToplistResponse& response);
private:
SpotifyServer* server();
void LoadSuggestions();
void AddSuggestionFromTrack(const pb::spotify::Track& track);
void AddSuggestionFromAlbum(const pb::spotify::Album& album);
private:
SpotifyServer* server_;
SpotifyService* service_;
@ -52,6 +59,8 @@ private:
QMap<QString, PendingState> queries_;
QMap<QString, int> pending_art_;
QMap<QString, int> pending_tracks_;
QSet<QString> suggestions_;
};
#endif // SPOTIFYSEARCHPROVIDER_H

View File

@ -1,46 +0,0 @@
/* This file is part of Clementine.
Copyright 2010, David Sansome <me@davidsansome.com>
Clementine 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 3 of the License, or
(at your option) any later version.
Clementine 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 Clementine. If not, see <http://www.gnu.org/licenses/>.
*/
#include "groovesharksearchplaylisttype.h"
#include "groovesharkservice.h"
const char* GroovesharkSearchPlaylistType::kName = "grooveshark-search";
GroovesharkSearchPlaylistType::GroovesharkSearchPlaylistType(GroovesharkService* service)
: service_(service) {
}
QIcon GroovesharkSearchPlaylistType::icon(Playlist* playlist) const {
return QIcon(":providers/grooveshark.png");
}
QString GroovesharkSearchPlaylistType::search_hint_text(Playlist* playlist) const {
return QObject::tr("Search Grooveshark");
}
QString GroovesharkSearchPlaylistType::empty_playlist_text(Playlist* playlist) const {
return QObject::tr("Start typing in the search box above to find music on %1.").arg("Grooveshark");
}
bool GroovesharkSearchPlaylistType::has_special_search_behaviour(Playlist* playlist) const {
return true;
}
void GroovesharkSearchPlaylistType::Search(const QString& text, Playlist* playlist) {
service_->Search(text, playlist);
}

View File

@ -1,43 +0,0 @@
/* This file is part of Clementine.
Copyright 2010, David Sansome <me@davidsansome.com>
Clementine 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 3 of the License, or
(at your option) any later version.
Clementine 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 Clementine. If not, see <http://www.gnu.org/licenses/>.
*/
#ifndef GROOVESHARKSEARCHPLAYLISTTYPE_H
#define GROOVESHARKSEARCHPLAYLISTTYPE_H
#include "playlist/specialplaylisttype.h"
class GroovesharkService;
class GroovesharkSearchPlaylistType : public SpecialPlaylistType {
public:
GroovesharkSearchPlaylistType(GroovesharkService* service);
static const char* kName;
virtual QString name() const { return kName; }
virtual QIcon icon(Playlist* playlist) const;
virtual QString search_hint_text(Playlist* playlist) const;
virtual QString empty_playlist_text(Playlist* playlist) const;
virtual bool has_special_search_behaviour(Playlist* playlist) const;
virtual void Search(const QString& text, Playlist* playlist);
private:
GroovesharkService* service_;
};
#endif // GROOVESHARKSEARCHPLAYLISTTYPE_H

View File

@ -38,7 +38,6 @@
#include "internetmodel.h"
#include "groovesharkradio.h"
#include "groovesharksearchplaylisttype.h"
#include "groovesharkurlhandler.h"
#include "searchboxwidget.h"
@ -93,6 +92,7 @@ GroovesharkService::GroovesharkService(Application* app, InternetModel *parent)
stations_(NULL),
grooveshark_radio_(NULL),
favorites_(NULL),
library_(NULL),
playlists_parent_(NULL),
subscribed_playlists_parent_(NULL),
network_(new NetworkAccessManager(this)),
@ -102,6 +102,7 @@ GroovesharkService::GroovesharkService(Application* app, InternetModel *parent)
rename_playlist_(NULL),
remove_from_playlist_(NULL),
remove_from_favorites_(NULL),
remove_from_library_(NULL),
get_url_to_share_song_(NULL),
get_url_to_share_playlist_(NULL),
search_box_(new SearchBoxWidget(this)),
@ -114,7 +115,6 @@ GroovesharkService::GroovesharkService(Application* app, InternetModel *parent)
task_search_id_(0) {
app_->player()->RegisterUrlHandler(url_handler_);
app_->playlist_manager()->RegisterSpecialPlaylistType(new GroovesharkSearchPlaylistType(this));
search_delay_->setInterval(kSearchDelayMsec);
search_delay_->setSingleShot(true);
@ -166,7 +166,9 @@ void GroovesharkService::ShowConfig() {
}
QWidget* GroovesharkService::HeaderWidget() const {
return search_box_;
if (IsLoggedIn())
return search_box_;
return NULL;
}
void GroovesharkService::Search(const QString& text, bool now) {
@ -440,6 +442,7 @@ void GroovesharkService::RemoveItems() {
search_ = NULL;
popular_month_ = NULL;
popular_today_ = NULL;
library_ = NULL;
favorites_ = NULL;
subscribed_playlists_parent_ = NULL;
stations_ = NULL;
@ -465,6 +468,7 @@ void GroovesharkService::ShowContextMenu(const QPoint& global_pos) {
bool display_delete_playlist_action = false,
display_remove_from_playlist_action = false,
display_remove_from_favorites_action = false,
display_remove_from_library_action = false,
display_share_song_url = false,
display_share_playlist_url = false;
@ -481,6 +485,8 @@ void GroovesharkService::ShowContextMenu(const QPoint& global_pos) {
int parent_playlist_type = index.parent().data(Role_PlaylistType).toInt();
if (parent_playlist_type == UserFavorites)
display_remove_from_favorites_action = true;
else if (parent_playlist_type == UserLibrary)
display_remove_from_library_action = true;
else if (parent_playlist_type == UserPlaylist)
display_remove_from_playlist_action = true;
}
@ -489,6 +495,7 @@ void GroovesharkService::ShowContextMenu(const QPoint& global_pos) {
rename_playlist_->setVisible(display_delete_playlist_action);
remove_from_playlist_->setVisible(display_remove_from_playlist_action);
remove_from_favorites_->setVisible(display_remove_from_favorites_action);
remove_from_library_->setVisible(display_remove_from_library_action);
// Check if we can display actions to get URL for sharing songs/playlists:
// - share song
@ -536,6 +543,9 @@ void GroovesharkService::EnsureMenuCreated() {
remove_from_favorites_ = context_menu_->addAction(
IconLoader::Load("list-remove"), tr("Remove from favorites"),
this, SLOT(RemoveCurrentFromFavorites()));
remove_from_library_ = context_menu_->addAction(
IconLoader::Load("list-remove"), tr("Remove from My Music"),
this, SLOT(RemoveCurrentFromLibrary()));
get_url_to_share_song_ = context_menu_->addAction(
tr("Get a URL to share this Grooveshark song"),
this, SLOT(GetCurrentSongUrlToShare()));
@ -606,6 +616,15 @@ void GroovesharkService::EnsureItemsCreated() {
grooveshark_radio_->setData(InternetModel::Type_SmartPlaylist, InternetModel::Role_Type);
radios_divider->appendRow(grooveshark_radio_);
library_ = new QStandardItem(IconLoader::Load("folder-sound"), tr("My Music"));
library_->setData(InternetModel::Type_UserPlaylist, InternetModel::Role_Type);
library_->setData(UserLibrary, Role_PlaylistType);
library_->setData(true, InternetModel::Role_CanLazyLoad);
library_->setData(true, InternetModel::Role_CanBeModified);
library_->setData(InternetModel::PlayBehaviour_SingleItem,
InternetModel::Role_PlayBehaviour);
root_->appendRow(library_);
favorites_ = new QStandardItem(QIcon(":/last.fm/love.png"), tr("Favorites"));
favorites_->setData(InternetModel::Type_UserPlaylist, InternetModel::Role_Type);
favorites_->setData(UserFavorites, Role_PlaylistType);
@ -622,6 +641,7 @@ void GroovesharkService::EnsureItemsCreated() {
root_->appendRow(subscribed_playlists_parent_);
RetrieveUserFavorites();
RetrieveUserLibrarySongs();
RetrieveUserPlaylists();
RetrieveSubscribedPlaylists();
RetrieveAutoplayTags();
@ -731,6 +751,7 @@ void GroovesharkService::RetrieveUserFavorites() {
void GroovesharkService::UserFavoritesRetrieved(QNetworkReply* reply, int task_id) {
reply->deleteLater();
app_->task_manager()->SetTaskFinished(task_id);
if (!favorites_) {
// The use probably logged out before the response arrived.
@ -749,7 +770,38 @@ void GroovesharkService::UserFavoritesRetrieved(QNetworkReply* reply, int task_i
favorites_->appendRow(child);
}
}
void GroovesharkService::RetrieveUserLibrarySongs() {
int task_id =
app_->task_manager()->StartTask(tr("Retrieving Grooveshark My Music songs"));
QNetworkReply* reply = CreateRequest("getUserLibrarySongs", QList<Param>());
NewClosure(reply, SIGNAL(finished()),
this, SLOT(UserLibrarySongsRetrieved(QNetworkReply*, int)), reply, task_id);
}
void GroovesharkService::UserLibrarySongsRetrieved(QNetworkReply* reply, int task_id) {
reply->deleteLater();
app_->task_manager()->SetTaskFinished(task_id);
if (!library_) {
// The use probably logged out before the response arrived.
return;
}
library_->removeRows(0, library_->rowCount());
QVariantMap result = ExtractResult(reply);
SongList songs = ExtractSongs(result);
Song::SortSongsListAlphabetically(&songs);
foreach (const Song& song, songs) {
QStandardItem* child = CreateSongItem(song);
child->setData(true, InternetModel::Role_CanBeModified);
library_->appendRow(child);
}
}
void GroovesharkService::RetrievePopularSongs() {
@ -1023,6 +1075,12 @@ void GroovesharkService::DropMimeData(const QMimeData* data, const QModelIndex&
foreach (int song_id, data_songs_ids) {
AddUserFavoriteSong(song_id);
}
} else if (playlist_type == UserLibrary || parent_playlist_type == UserLibrary) {
// FIXME: Adding songs to user libray doesn't work atm, but the problem
// seems to be on Grooveshark server side, as it returns success=true
// when calling addUserLibrarySongs with a valid song id.
// So this code is deactivated for now to not mislead user
//AddUserLibrarySongs(data_songs_ids);
} else { // Dropped on a normal playlist
// Get the playlist
int playlist_id = index.data(Role_UserPlaylistId).toInt();
@ -1055,6 +1113,12 @@ QList<QAction*> GroovesharkService::playlistitem_actions(const Song& song) {
connect(add_to_favorites, SIGNAL(triggered()), SLOT(AddCurrentSongToUserFavorites()));
playlistitem_actions_.append(add_to_favorites);
// FIXME: as explained above, adding songs to library doesn't work currently
//QAction* add_to_library = new QAction(IconLoader::Load("folder-sound"),
// tr("Add to Grooveshark My Music"), this);
//connect(add_to_library, SIGNAL(triggered()), SLOT(AddCurrentSongToUserLibrary()));
//playlistitem_actions_.append(add_to_library);
// Create a menu with 'add to playlist' actions for each Grooveshark playlist
QAction* add_to_playlists = new QAction(IconLoader::Load("list-add"),
tr("Add to Grooveshark playlists"), this);
@ -1352,6 +1416,42 @@ void GroovesharkService::UserFavoriteSongAdded(QNetworkReply* reply, int task_id
RetrieveUserFavorites();
}
void GroovesharkService::AddUserLibrarySongs(const QList<int>& songs_ids) {
int task_id = app_->task_manager()->StartTask(tr("Adding song to My Music"));
QList<Param> parameters;
// Convert songs ids to QVariant
QVariantList songs_ids_qvariant;
foreach (int song_id, songs_ids) {
songs_ids_qvariant << QVariant(song_id);
}
QVariantList albums_ids_qvariant;
QVariantList artists_ids_qvariant;
parameters << Param("songIDs", songs_ids_qvariant);
// We do not support albums and artist parameters for now, but they are
// required
parameters << Param("albumIDs", albums_ids_qvariant);
parameters << Param("artistIDs", artists_ids_qvariant);
QNetworkReply* reply = CreateRequest("addUserLibrarySongs", parameters);
NewClosure(reply, SIGNAL(finished()),
this, SLOT(UserLibrarySongAdded(QNetworkReply*, int)),
reply, task_id);
}
void GroovesharkService::UserLibrarySongAdded(QNetworkReply* reply, int task_id) {
reply->deleteLater();
app_->task_manager()->SetTaskFinished(task_id);
QVariantMap result = ExtractResult(reply);
if (!result["success"].toBool()) {
qLog(Warning) << "Grooveshark addUserLibrarySongs failed";
return;
}
// Refresh user's library list
RetrieveUserLibrarySongs();
}
void GroovesharkService::RemoveCurrentFromPlaylist() {
const QModelIndexList& indexes(model()->selected_indexes());
QMap<int, QList<int> > playlists_songs_ids;
@ -1412,7 +1512,7 @@ void GroovesharkService::RemoveFromFavorites(const QList<int>& songs_ids_to_remo
if (songs_ids_to_remove.isEmpty())
return;
int task_id = app_->task_manager()->StartTask(tr("Removing song from favorites"));
int task_id = app_->task_manager()->StartTask(tr("Removing songs from favorites"));
QList<Param> parameters;
// Convert song ids to QVariant
@ -1424,10 +1524,10 @@ void GroovesharkService::RemoveFromFavorites(const QList<int>& songs_ids_to_remo
parameters << Param("songIDs", songs_ids_qvariant);
QNetworkReply* reply = CreateRequest("removeUserFavoriteSongs", parameters);
NewClosure(reply, SIGNAL(finished()), this,
SLOT(SongRemovedFromFavorites(QNetworkReply*, int)), reply, task_id);
SLOT(SongsRemovedFromFavorites(QNetworkReply*, int)), reply, task_id);
}
void GroovesharkService::SongRemovedFromFavorites(QNetworkReply* reply, int task_id) {
void GroovesharkService::SongsRemovedFromFavorites(QNetworkReply* reply, int task_id) {
app_->task_manager()->SetTaskFinished(task_id);
reply->deleteLater();
@ -1439,8 +1539,68 @@ void GroovesharkService::SongRemovedFromFavorites(QNetworkReply* reply, int task
RetrieveUserFavorites();
}
QNetworkReply* GroovesharkService::CreateRequest(const QString& method_name, QList<Param> params,
bool use_https) {
void GroovesharkService::RemoveCurrentFromLibrary() {
const QModelIndexList& indexes(model()->selected_indexes());
QList<int> songs_ids;
foreach (const QModelIndex& index, indexes) {
if (index.parent().data(Role_PlaylistType).toInt() != UserLibrary) {
continue;
}
int song_id = ExtractSongId(index.data(InternetModel::Role_Url).toUrl());
if (song_id) {
songs_ids << song_id;
}
}
RemoveFromLibrary(songs_ids);
}
void GroovesharkService::RemoveFromLibrary(const QList<int>& songs_ids_to_remove) {
if (songs_ids_to_remove.isEmpty())
return;
int task_id = app_->task_manager()->StartTask(tr("Removing songs from My Music"));
QList<Param> parameters;
// Convert song ids to QVariant
QVariantList songs_ids_qvariant;
foreach (const int song_id, songs_ids_to_remove) {
songs_ids_qvariant << QVariant(song_id);
}
QVariantList albums_ids_qvariant;
QVariantList artists_ids_qvariant;
parameters << Param("songIDs", songs_ids_qvariant);
// We do not support albums and artist parameters for now, but they are
// required
parameters << Param("albumIDs", albums_ids_qvariant);
parameters << Param("artistIDs", artists_ids_qvariant);
QNetworkReply* reply = CreateRequest("removeUserLibrarySongs", parameters);
NewClosure(reply, SIGNAL(finished()), this,
SLOT(SongsRemovedFromLibrary(QNetworkReply*, int)), reply, task_id);
}
void GroovesharkService::SongsRemovedFromLibrary(QNetworkReply* reply, int task_id) {
app_->task_manager()->SetTaskFinished(task_id);
reply->deleteLater();
QVariantMap result = ExtractResult(reply);
if (!result["success"].toBool()) {
qLog(Warning) << "Grooveshark removeUserLibrarySongs failed";
return;
}
RetrieveUserLibrarySongs();
}
QNetworkReply* GroovesharkService::CreateRequest(
const QString& method_name,
const QList<Param>& params,
bool use_https) {
QVariantMap request_params;
request_params.insert("method", method_name);

View File

@ -46,9 +46,10 @@ class GroovesharkService : public InternetService {
enum PlaylistType {
UserPlaylist = Qt::UserRole,
// Favorites list is like a playlist, but we want to do special treatments
// in some cases
// Favorites and Library list are like playlists, but we want to do special
// treatments in some cases
UserFavorites,
UserLibrary,
SubscribedPlaylist
};
@ -80,6 +81,7 @@ class GroovesharkService : public InternetService {
bool IsLoggedIn() const { return !session_id_.isEmpty(); }
void RetrieveUserPlaylists();
void RetrieveUserFavorites();
void RetrieveUserLibrarySongs();
void RetrievePopularSongs();
void RetrievePopularSongsMonth();
void RetrievePopularSongsToday();
@ -93,6 +95,8 @@ class GroovesharkService : public InternetService {
void RenamePlaylist(int playlist_id);
void AddUserFavoriteSong(int song_id);
void RemoveFromFavorites(const QList<int>& songs_ids_to_remove);
void AddUserLibrarySongs(const QList<int>& songs_ids);
void RemoveFromLibrary(const QList<int>& songs_ids_to_remove);
void GetSongUrlToShare(int song_id);
void GetPlaylistUrlToShare(int playlist_id);
// Start autoplay for the given tag_id, fill the autoplay_state, returns a
@ -156,6 +160,7 @@ class GroovesharkService : public InternetService {
void Authenticated();
void UserPlaylistsRetrieved();
void UserFavoritesRetrieved(QNetworkReply* reply, int task_id);
void UserLibrarySongsRetrieved(QNetworkReply* reply, int task_id);
void PopularSongsMonthRetrieved(QNetworkReply* reply);
void PopularSongsTodayRetrieved(QNetworkReply* reply);
void SubscribedPlaylistsRetrieved(QNetworkReply* reply);
@ -168,16 +173,20 @@ class GroovesharkService : public InternetService {
void RenameCurrentPlaylist();
void PlaylistDeleted(QNetworkReply* reply, int playlist_id);
void PlaylistRenamed(QNetworkReply* reply, int playlist_id, const QString& new_name);
void AddCurrentSongToUserFavorites() { AddUserFavoriteSong(current_song_id_); }
void AddCurrentSongToUserFavorites() { AddUserFavoriteSong(current_song_id_); }
void AddCurrentSongToUserLibrary() { AddUserLibrarySongs(QList<int>() << current_song_id_); }
void AddCurrentSongToPlaylist(QAction* action);
void UserFavoriteSongAdded(QNetworkReply* reply, int task_id);
void UserLibrarySongAdded(QNetworkReply* reply, int task_id);
void GetCurrentSongUrlToShare();
void SongUrlToShareReceived(QNetworkReply* reply);
void GetCurrentPlaylistUrlToShare();
void PlaylistUrlToShareReceived(QNetworkReply* reply);
void RemoveCurrentFromPlaylist();
void RemoveCurrentFromFavorites();
void SongRemovedFromFavorites(QNetworkReply* reply, int task_id);
void RemoveCurrentFromLibrary();
void SongsRemovedFromFavorites(QNetworkReply* reply, int task_id);
void SongsRemovedFromLibrary(QNetworkReply* reply, int task_id);
void StreamMarked();
void SongMarkedAsComplete();
@ -204,8 +213,10 @@ class GroovesharkService : public InternetService {
// Create a request for the given method, with the given params.
// If need_authentication is true, add session_id to params.
// Returns the reply object created
QNetworkReply* CreateRequest(const QString& method_name, const QList<QPair<QString, QVariant> > params,
bool use_https = false);
QNetworkReply* CreateRequest(
const QString& method_name,
const QList<QPair<QString, QVariant> >& params,
bool use_https = false);
// Convenient function which block until 'reply' replies, or timeout after 10
// seconds. Returns false if reply has timeouted
bool WaitForReply(QNetworkReply* reply);
@ -249,6 +260,10 @@ class GroovesharkService : public InternetService {
QStandardItem* stations_;
QStandardItem* grooveshark_radio_;
QStandardItem* favorites_;
// Grooveshark Library (corresponds to Grooveshark 'MyMusic' actually, but
// called 'Library' in the API).
// Nothing to do with Clementine's local library
QStandardItem* library_;
QStandardItem* playlists_parent_;
QStandardItem* subscribed_playlists_parent_;
@ -265,6 +280,7 @@ class GroovesharkService : public InternetService {
QAction* rename_playlist_;
QAction* remove_from_playlist_;
QAction* remove_from_favorites_;
QAction* remove_from_library_;
QAction* get_url_to_share_song_;
QAction* get_url_to_share_playlist_;
QList<QAction*> playlistitem_actions_;

View File

@ -22,8 +22,6 @@
#include <QWidget>
#include "widgets/lineedit.h"
class LineEditInterface;
class Ui_IcecastFilterWidget;

View File

@ -20,8 +20,6 @@
#include <QWidget>
#include "widgets/lineedit.h"
class InternetService;
class DidYouMean;
class Ui_SearchBoxWidget;

View File

@ -1,51 +0,0 @@
/* This file is part of Clementine.
Copyright 2010, David Sansome <me@davidsansome.com>
Clementine 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 3 of the License, or
(at your option) any later version.
Clementine 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 Clementine. If not, see <http://www.gnu.org/licenses/>.
*/
#include "spotifysearchplaylisttype.h"
#include "spotifyservice.h"
const char* SpotifySearchPlaylistType::kName = "spotify-search";
SpotifySearchPlaylistType::SpotifySearchPlaylistType(SpotifyService* service)
: service_(service) {
}
QIcon SpotifySearchPlaylistType::icon(Playlist* playlist) const {
return QIcon(":icons/32x32/spotify.png");
}
QString SpotifySearchPlaylistType::search_hint_text(Playlist* playlist) const {
return QObject::tr("Search Spotify");
}
QString SpotifySearchPlaylistType::empty_playlist_text(Playlist* playlist) const {
return QObject::tr("Start typing in the search box above to find music on %1.").arg("Spotify");
}
bool SpotifySearchPlaylistType::has_special_search_behaviour(Playlist* playlist) const {
return true;
}
void SpotifySearchPlaylistType::Search(const QString& text, Playlist* playlist) {
service_->Search(text, playlist);
}
void SpotifySearchPlaylistType::DidYouMeanClicked(const QString& text, Playlist* playlist) {
// TODO Dead-code now: we will probably remove the entire class later, if the
// new search looks pretty enough for everyone
//service_->Search(text, playlist, true);
}

View File

@ -1,44 +0,0 @@
/* This file is part of Clementine.
Copyright 2010, David Sansome <me@davidsansome.com>
Clementine 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 3 of the License, or
(at your option) any later version.
Clementine 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 Clementine. If not, see <http://www.gnu.org/licenses/>.
*/
#ifndef SPOTIFYSEARCHPLAYLISTTYPE_H
#define SPOTIFYSEARCHPLAYLISTTYPE_H
#include "playlist/specialplaylisttype.h"
class SpotifyService;
class SpotifySearchPlaylistType : public SpecialPlaylistType {
public:
SpotifySearchPlaylistType(SpotifyService* service);
static const char* kName;
virtual QString name() const { return kName; }
virtual QIcon icon(Playlist* playlist) const;
virtual QString search_hint_text(Playlist* playlist) const;
virtual QString empty_playlist_text(Playlist* playlist) const;
virtual bool has_special_search_behaviour(Playlist* playlist) const;
virtual void Search(const QString& text, Playlist* playlist);
virtual void DidYouMeanClicked(const QString& text, Playlist* playlist);
private:
SpotifyService* service_;
};
#endif // SPOTIFYSEARCHPLAYLISTTYPE_H

View File

@ -4,7 +4,6 @@
#include "spotifyblobdownloader.h"
#include "spotifyserver.h"
#include "spotifyservice.h"
#include "spotifysearchplaylisttype.h"
#include "core/application.h"
#include "core/database.h"
#include "core/logging.h"
@ -75,9 +74,6 @@ SpotifyService::SpotifyService(Application* app, InternetModel* parent)
qLog(Debug) << "Spotify system blob path:" << system_blob_path_;
qLog(Debug) << "Spotify local blob path:" << local_blob_path_;
app_->playlist_manager()->RegisterSpecialPlaylistType(
new SpotifySearchPlaylistType(this));
app_->global_search()->AddProvider(new SpotifySearchProvider(app_, this));
search_delay_->setInterval(kSearchDelayMsec);
@ -420,15 +416,21 @@ bool SpotifyService::DoPlaylistsDiffer(const pb::spotify::Playlists& response) c
}
void SpotifyService::InboxLoaded(const pb::spotify::LoadPlaylistResponse& response) {
FillPlaylist(inbox_, response);
if (inbox_) {
FillPlaylist(inbox_, response);
}
}
void SpotifyService::StarredLoaded(const pb::spotify::LoadPlaylistResponse& response) {
FillPlaylist(starred_, response);
if (starred_) {
FillPlaylist(starred_, response);
}
}
void SpotifyService::ToplistLoaded(const pb::spotify::BrowseToplistResponse& response) {
FillPlaylist(toplist_, response.track());
if (toplist_) {
FillPlaylist(toplist_, response.track());
}
}
QStandardItem* SpotifyService::PlaylistBySpotifyIndex(int index) const {
@ -501,7 +503,9 @@ PlaylistItem::Options SpotifyService::playlistitem_options() const {
}
QWidget* SpotifyService::HeaderWidget() const {
return search_box_;
if (IsLoggedIn())
return search_box_;
return NULL;
}
void SpotifyService::EnsureMenuCreated() {

View File

@ -23,7 +23,6 @@
#include <boost/scoped_ptr.hpp>
#include "librarymodel.h"
#include "widgets/lineedit.h"
class GroupByDialog;
class SettingsDialog;

View File

@ -40,7 +40,7 @@ MoodbarLoader::MoodbarLoader(Application* app, QObject* parent)
save_alongside_originals_(false)
{
cache_->setCacheDirectory(Utilities::GetConfigPath(Utilities::Path_MoodbarCache));
cache_->setMaximumCacheSize(10 * 1024 * 1024); // 10MB - enough for 3333 moodbars
cache_->setMaximumCacheSize(60 * 1024 * 1024); // 60MB - enough for 20,000 moodbars
connect(app, SIGNAL(SettingsChanged()), SLOT(ReloadSettings()));
ReloadSettings();

View File

@ -296,6 +296,12 @@ QVariant Playlist::data(const QModelIndex& index, int role) const {
return QVariant(column_alignments_.value(index.column(), (Qt::AlignLeft | Qt::AlignVCenter)));
case Qt::ForegroundRole:
if (data(index, Role_IsCurrent).toBool()) {
// Ignore any custom colours for the currently playing item - they might
// clash with the glowing current track indicator.
return QVariant();
}
if (items_[index.row()]->HasCurrentForegroundColor()) {
return QBrush(items_[index.row()]->GetCurrentForegroundColor());
}
@ -305,6 +311,12 @@ QVariant Playlist::data(const QModelIndex& index, int role) const {
return QVariant();
case Qt::BackgroundRole:
if (data(index, Role_IsCurrent).toBool()) {
// Ignore any custom colours for the currently playing item - they might
// clash with the glowing current track indicator.
return QVariant();
}
if (items_[index.row()]->HasCurrentBackgroundColor()) {
return QBrush(items_[index.row()]->GetCurrentBackgroundColor());
}
@ -695,9 +707,9 @@ bool Playlist::dropMimeData(const QMimeData* data, Qt::DropAction action, int ro
stream.readRawData(reinterpret_cast<char*>(&source_playlist), sizeof(source_playlist));
stream >> source_rows;
if (!stream.atEnd()) {
stream.readRawData((char*)&pid, sizeof(pid));
stream.readRawData((char*)&pid, sizeof(pid));
} else {
pid = ! own_pid;
pid = ! own_pid;
}
qStableSort(source_rows); // Make sure we take them in order
@ -779,6 +791,10 @@ void Playlist::MoveItemsWithoutUndo(const QList<int>& source_rows, int pos) {
layoutAboutToBeChanged();
PlaylistItemList moved_items;
if (pos < 0) {
pos = items_.count();
}
// Take the items out of the list first, keeping track of whether the
// insertion point changes
int offset = 0;
@ -790,10 +806,6 @@ void Playlist::MoveItemsWithoutUndo(const QList<int>& source_rows, int pos) {
}
offset++;
}
if (pos < 0) {
pos = items_.count();
}
// Put the items back in
for (int i=start ; i<start+moved_items.count() ; ++i) {

View File

@ -421,7 +421,11 @@ void PlaylistContainer::resizeEvent(QResizeEvent* e) {
void PlaylistContainer::FocusOnFilter(QKeyEvent *event) {
ui_->filter->setFocus();
QApplication::sendEvent(ui_->filter, event);
if (event->key() == Qt::Key_Escape) {
ui_->filter->clear();
} else {
ui_->filter->setText(ui_->filter->text() + event->text());
}
}
void PlaylistContainer::RepositionNoMatchesLabel(bool force) {

View File

@ -21,8 +21,6 @@
#include <QWidget>
#include <QSettings>
#include "widgets/lineedit.h"
class Ui_PlaylistContainer;
class DidYouMean;

File diff suppressed because it is too large Load Diff

File diff suppressed because it is too large Load Diff

File diff suppressed because it is too large Load Diff

File diff suppressed because it is too large Load Diff

File diff suppressed because it is too large Load Diff

File diff suppressed because it is too large Load Diff

File diff suppressed because it is too large Load Diff

File diff suppressed because it is too large Load Diff

File diff suppressed because it is too large Load Diff

File diff suppressed because it is too large Load Diff

File diff suppressed because it is too large Load Diff

File diff suppressed because it is too large Load Diff

File diff suppressed because it is too large Load Diff

File diff suppressed because it is too large Load Diff

File diff suppressed because it is too large Load Diff

File diff suppressed because it is too large Load Diff

File diff suppressed because it is too large Load Diff

File diff suppressed because it is too large Load Diff

File diff suppressed because it is too large Load Diff

File diff suppressed because it is too large Load Diff

File diff suppressed because it is too large Load Diff

File diff suppressed because it is too large Load Diff

File diff suppressed because it is too large Load Diff

File diff suppressed because it is too large Load Diff

File diff suppressed because it is too large Load Diff

File diff suppressed because it is too large Load Diff

File diff suppressed because it is too large Load Diff

File diff suppressed because it is too large Load Diff

File diff suppressed because it is too large Load Diff

File diff suppressed because it is too large Load Diff

File diff suppressed because it is too large Load Diff

File diff suppressed because it is too large Load Diff

File diff suppressed because it is too large Load Diff

File diff suppressed because it is too large Load Diff

File diff suppressed because it is too large Load Diff

File diff suppressed because it is too large Load Diff

File diff suppressed because it is too large Load Diff

File diff suppressed because it is too large Load Diff

File diff suppressed because it is too large Load Diff

File diff suppressed because it is too large Load Diff

File diff suppressed because it is too large Load Diff

File diff suppressed because it is too large Load Diff

File diff suppressed because it is too large Load Diff

File diff suppressed because it is too large Load Diff

File diff suppressed because it is too large Load Diff

File diff suppressed because it is too large Load Diff

File diff suppressed because it is too large Load Diff

File diff suppressed because it is too large Load Diff

File diff suppressed because it is too large Load Diff

File diff suppressed because it is too large Load Diff

File diff suppressed because it is too large Load Diff

File diff suppressed because it is too large Load Diff

File diff suppressed because it is too large Load Diff

File diff suppressed because it is too large Load Diff

File diff suppressed because it is too large Load Diff

File diff suppressed because it is too large Load Diff

File diff suppressed because it is too large Load Diff

File diff suppressed because it is too large Load Diff

File diff suppressed because it is too large Load Diff

File diff suppressed because it is too large Load Diff

File diff suppressed because it is too large Load Diff

File diff suppressed because it is too large Load Diff

File diff suppressed because it is too large Load Diff

View File

@ -27,7 +27,6 @@
#include "core/song.h"
#include "covers/albumcoverloaderoptions.h"
#include "covers/coversearchstatistics.h"
#include "widgets/lineedit.h"
class AlbumCoverChoiceController;
class AlbumCoverFetcher;

View File

@ -20,7 +20,6 @@
#include "covers/albumcoverfetcher.h"
#include "covers/albumcoverloaderoptions.h"
#include "widgets/lineedit.h"
#include <QDialog>
#include <QIcon>

View File

@ -32,7 +32,8 @@ ForceScrollPerPixel::ForceScrollPerPixel(QAbstractItemView* item_view, QObject*
bool ForceScrollPerPixel::eventFilter(QObject* object, QEvent* event) {
if (object == item_view_ &&
event->type() != QEvent::Destroy &&
event->type() != QEvent::WinIdChange) {
event->type() != QEvent::WinIdChange &&
event->type() != QEvent::AccessibilityPrepare) {
item_view_->setVerticalScrollMode(QAbstractItemView::ScrollPerPixel);
item_view_->verticalScrollBar()->setSingleStep(20);
}

View File

@ -104,8 +104,6 @@ public:
QString text() const { return QLineEdit::text(); }
void set_text(const QString& text) { QLineEdit::setText(text); }
void set_enabled(bool enabled) { QLineEdit::setEnabled(enabled); }
void clear() { LineEditInterface::clear(); }
void setPlaceholderText(const QString& text) { set_hint(text); }
protected:
void paintEvent(QPaintEvent*);

Some files were not shown because too many files have changed in this diff Show More