Create and render moodbars in background threads to avoid blocking the UI
This commit is contained in:
parent
638a4b9739
commit
a2feaa61e7
@ -25,6 +25,12 @@
|
|||||||
|
|
||||||
#include <QApplication>
|
#include <QApplication>
|
||||||
#include <QPainter>
|
#include <QPainter>
|
||||||
|
#include <QtConcurrentRun>
|
||||||
|
|
||||||
|
MoodbarItemDelegate::Data::Data()
|
||||||
|
: state_(State_None)
|
||||||
|
{
|
||||||
|
}
|
||||||
|
|
||||||
MoodbarItemDelegate::MoodbarItemDelegate(Application* app, QObject* parent)
|
MoodbarItemDelegate::MoodbarItemDelegate(Application* app, QObject* parent)
|
||||||
: QItemDelegate(parent),
|
: QItemDelegate(parent),
|
||||||
@ -36,73 +42,154 @@ void MoodbarItemDelegate::paint(
|
|||||||
QPainter* painter, const QStyleOptionViewItem& option,
|
QPainter* painter, const QStyleOptionViewItem& option,
|
||||||
const QModelIndex& index) const {
|
const QModelIndex& index) const {
|
||||||
QPixmap pixmap = const_cast<MoodbarItemDelegate*>(this)->PixmapForIndex(
|
QPixmap pixmap = const_cast<MoodbarItemDelegate*>(this)->PixmapForIndex(
|
||||||
index, option.rect.size(), option.palette);
|
index, option.rect.size());
|
||||||
if (!pixmap.isNull()) {
|
if (!pixmap.isNull()) {
|
||||||
painter->drawPixmap(option.rect, pixmap);
|
painter->drawPixmap(option.rect, pixmap);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
QPixmap MoodbarItemDelegate::PixmapForIndex(
|
QPixmap MoodbarItemDelegate::PixmapForIndex(
|
||||||
const QModelIndex& index, const QSize& size, const QPalette& palette) {
|
const QModelIndex& index, const QSize& size) {
|
||||||
// Do we have a pixmap already that's the right size?
|
// Pixmaps are keyed off URL.
|
||||||
QPixmap pixmap = index.data(Playlist::Role_MoodbarPixmap).value<QPixmap>();
|
const QUrl url(index.sibling(index.row(), Playlist::Column_Filename).data().toUrl());
|
||||||
if (!pixmap.isNull() && pixmap.size() == size) {
|
|
||||||
return pixmap;
|
Data* data = data_[url];
|
||||||
|
if (!data) {
|
||||||
|
data = new Data;
|
||||||
|
data_.insert(url, data);
|
||||||
}
|
}
|
||||||
|
|
||||||
// Do we have colors?
|
data->indexes_.insert(index);
|
||||||
ColorVector colors = index.data(Playlist::Role_MoodbarColors).value<ColorVector>();
|
data->desired_size_ = size;
|
||||||
if (colors.isEmpty()) {
|
|
||||||
// Nope - we need to load a mood file for this song and generate some colors
|
|
||||||
// from it.
|
|
||||||
const QUrl url(index.sibling(index.row(), Playlist::Column_Filename).data().toUrl());
|
|
||||||
|
|
||||||
QByteArray data;
|
switch (data->state_) {
|
||||||
MoodbarPipeline* pipeline = NULL;
|
case Data::State_CannotLoad:
|
||||||
switch (app_->moodbar_loader()->Load(url, &data, &pipeline)) {
|
case Data::State_LoadingData:
|
||||||
case MoodbarLoader::CannotLoad:
|
case Data::State_LoadingColors:
|
||||||
return QPixmap();
|
case Data::State_LoadingImage:
|
||||||
|
return data->pixmap_;
|
||||||
|
|
||||||
case MoodbarLoader::Loaded:
|
case Data::State_Loaded:
|
||||||
// Aww yeah
|
// Is the pixmap the right size?
|
||||||
colors = MoodbarRenderer::Colors(data, MoodbarRenderer::Style_Normal, palette);
|
if (data->pixmap_.size() != size) {
|
||||||
break;
|
StartLoadingImage(url, data);
|
||||||
|
|
||||||
case MoodbarLoader::WillLoadAsync:
|
|
||||||
// Maybe in a little while.
|
|
||||||
qLog(Debug) << "Loading" << pipeline;
|
|
||||||
NewClosure(pipeline, SIGNAL(Finished(bool)),
|
|
||||||
this, SLOT(RequestFinished(MoodbarPipeline*,QModelIndex,QUrl)),
|
|
||||||
pipeline, index, url);
|
|
||||||
return QPixmap();
|
|
||||||
}
|
}
|
||||||
|
|
||||||
|
return data->pixmap_;
|
||||||
|
|
||||||
|
case Data::State_None:
|
||||||
|
break;
|
||||||
}
|
}
|
||||||
|
|
||||||
// We've got colors, let's make a pixmap.
|
// We have to start loading the data from scratch.
|
||||||
pixmap = QPixmap(size);
|
data->state_ = Data::State_LoadingData;
|
||||||
QPainter p(&pixmap);
|
|
||||||
MoodbarRenderer::Render(colors, &p, QRect(QPoint(0, 0), size));
|
|
||||||
p.end();
|
|
||||||
|
|
||||||
// Set these on the item so we don't have to look them up again.
|
// Load a mood file for this song and generate some colors from it
|
||||||
QAbstractItemModel* model = const_cast<QAbstractItemModel*>(index.model());
|
QByteArray bytes;
|
||||||
model->setData(index, QVariant::fromValue(colors), Playlist::Role_MoodbarColors);
|
MoodbarPipeline* pipeline = NULL;
|
||||||
model->setData(index, pixmap, Playlist::Role_MoodbarPixmap);
|
switch (app_->moodbar_loader()->Load(url, &bytes, &pipeline)) {
|
||||||
|
case MoodbarLoader::CannotLoad:
|
||||||
|
data->state_ = Data::State_CannotLoad;
|
||||||
|
break;
|
||||||
|
|
||||||
return pixmap;
|
case MoodbarLoader::Loaded:
|
||||||
|
// We got the data immediately.
|
||||||
|
StartLoadingColors(url, bytes, data);
|
||||||
|
break;
|
||||||
|
|
||||||
|
case MoodbarLoader::WillLoadAsync:
|
||||||
|
// Maybe in a little while.
|
||||||
|
NewClosure(pipeline, SIGNAL(Finished(bool)),
|
||||||
|
this, SLOT(DataLoaded(QUrl,MoodbarPipeline*)),
|
||||||
|
url, pipeline);
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
|
||||||
|
return QPixmap();
|
||||||
}
|
}
|
||||||
|
|
||||||
void MoodbarItemDelegate::RequestFinished(
|
void MoodbarItemDelegate::DataLoaded( const QUrl& url, MoodbarPipeline* pipeline) {
|
||||||
MoodbarPipeline* pipeline, const QModelIndex& index, const QUrl& url) {
|
Data* data = data_[url];
|
||||||
qLog(Debug) << "Finished" << pipeline;
|
if (!data) {
|
||||||
// Is this index still valid, and does it still point to the same URL?
|
|
||||||
if (!index.isValid() || index.sibling(index.row(), Playlist::Column_Filename).data().toUrl() != url) {
|
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
|
|
||||||
// It's good. Create the color list and set them on the item.
|
if (!pipeline->success()) {
|
||||||
ColorVector colors = MoodbarRenderer::Colors(
|
data->state_ = Data::State_CannotLoad;
|
||||||
pipeline->data(), MoodbarRenderer::Style_Normal, qApp->palette());
|
return;
|
||||||
QAbstractItemModel* model = const_cast<QAbstractItemModel*>(index.model());
|
}
|
||||||
model->setData(index, QVariant::fromValue(colors), Playlist::Role_MoodbarColors);
|
|
||||||
|
// Load the colors next.
|
||||||
|
StartLoadingColors(url, pipeline->data(), data);
|
||||||
|
}
|
||||||
|
|
||||||
|
void MoodbarItemDelegate::StartLoadingColors(
|
||||||
|
const QUrl& url, const QByteArray& bytes, Data* data) {
|
||||||
|
data->state_ = Data::State_LoadingColors;
|
||||||
|
|
||||||
|
QFutureWatcher<ColorVector>* watcher = new QFutureWatcher<ColorVector>();
|
||||||
|
NewClosure(watcher, SIGNAL(finished()),
|
||||||
|
this, SLOT(ColorsLoaded(QUrl,QFutureWatcher<ColorVector>*)),
|
||||||
|
url, watcher);
|
||||||
|
|
||||||
|
QFuture<ColorVector> future = QtConcurrent::run(MoodbarRenderer::Colors,
|
||||||
|
bytes, MoodbarRenderer::Style_Normal, qApp->palette());
|
||||||
|
watcher->setFuture(future);
|
||||||
|
}
|
||||||
|
|
||||||
|
void MoodbarItemDelegate::ColorsLoaded(
|
||||||
|
const QUrl& url, QFutureWatcher<ColorVector>* watcher) {
|
||||||
|
watcher->deleteLater();
|
||||||
|
|
||||||
|
Data* data = data_[url];
|
||||||
|
if (!data) {
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
data->colors_ = watcher->result();
|
||||||
|
|
||||||
|
// Load the image next.
|
||||||
|
StartLoadingImage(url, data);
|
||||||
|
}
|
||||||
|
|
||||||
|
void MoodbarItemDelegate::StartLoadingImage(const QUrl& url, Data* data) {
|
||||||
|
data->state_ = Data::State_LoadingImage;
|
||||||
|
|
||||||
|
QFutureWatcher<QImage>* watcher = new QFutureWatcher<QImage>();
|
||||||
|
NewClosure(watcher, SIGNAL(finished()),
|
||||||
|
this, SLOT(ImageLoaded(QUrl,QFutureWatcher<QImage>*)),
|
||||||
|
url, watcher);
|
||||||
|
|
||||||
|
QFuture<QImage> future = QtConcurrent::run(MoodbarRenderer::RenderToImage,
|
||||||
|
data->colors_, data->desired_size_);
|
||||||
|
watcher->setFuture(future);
|
||||||
|
}
|
||||||
|
|
||||||
|
void MoodbarItemDelegate::ImageLoaded(const QUrl& url, QFutureWatcher<QImage>* watcher) {
|
||||||
|
watcher->deleteLater();
|
||||||
|
|
||||||
|
Data* data = data_[url];
|
||||||
|
if (!data) {
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
QImage image(watcher->result());
|
||||||
|
|
||||||
|
// If the desired size changed then don't even bother converting the image
|
||||||
|
// to a pixmap, just reload it at the new size.
|
||||||
|
if (!image.isNull() && data->desired_size_ != image.size()) {
|
||||||
|
StartLoadingImage(url, data);
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
data->pixmap_ = QPixmap::fromImage(image);
|
||||||
|
data->state_ = Data::State_Loaded;
|
||||||
|
|
||||||
|
// Update all the indices with the new pixmap.
|
||||||
|
foreach (const QPersistentModelIndex& index, data->indexes_) {
|
||||||
|
if (index.isValid() && index.sibling(index.row(), Playlist::Column_Filename).data().toUrl() == url) {
|
||||||
|
const_cast<Playlist*>(reinterpret_cast<const Playlist*>(index.model()))
|
||||||
|
->MoodbarUpdated(index);
|
||||||
|
}
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
@ -18,7 +18,12 @@
|
|||||||
#ifndef MOODBARITEMDELEGATE_H
|
#ifndef MOODBARITEMDELEGATE_H
|
||||||
#define MOODBARITEMDELEGATE_H
|
#define MOODBARITEMDELEGATE_H
|
||||||
|
|
||||||
|
#include "moodbarrenderer.h"
|
||||||
|
|
||||||
|
#include <QCache>
|
||||||
#include <QItemDelegate>
|
#include <QItemDelegate>
|
||||||
|
#include <QFutureWatcher>
|
||||||
|
#include <QUrl>
|
||||||
|
|
||||||
class Application;
|
class Application;
|
||||||
class MoodbarPipeline;
|
class MoodbarPipeline;
|
||||||
@ -35,15 +40,39 @@ public:
|
|||||||
const QModelIndex& index) const;
|
const QModelIndex& index) const;
|
||||||
|
|
||||||
private slots:
|
private slots:
|
||||||
void RequestFinished(MoodbarPipeline* pipeline, const QModelIndex& index,
|
void DataLoaded(const QUrl& url, MoodbarPipeline* pipeline);
|
||||||
const QUrl& url);
|
void ColorsLoaded(const QUrl& url, QFutureWatcher<ColorVector>* watcher);
|
||||||
|
void ImageLoaded(const QUrl& url, QFutureWatcher<QImage>* watcher);
|
||||||
|
|
||||||
private:
|
private:
|
||||||
QPixmap PixmapForIndex(const QModelIndex& index, const QSize& size,
|
struct Data {
|
||||||
const QPalette& palette);
|
Data();
|
||||||
|
|
||||||
|
enum State {
|
||||||
|
State_None,
|
||||||
|
State_CannotLoad,
|
||||||
|
State_LoadingData,
|
||||||
|
State_LoadingColors,
|
||||||
|
State_LoadingImage,
|
||||||
|
State_Loaded
|
||||||
|
};
|
||||||
|
|
||||||
|
QSet<QPersistentModelIndex> indexes_;
|
||||||
|
|
||||||
|
State state_;
|
||||||
|
ColorVector colors_;
|
||||||
|
QSize desired_size_;
|
||||||
|
QPixmap pixmap_;
|
||||||
|
};
|
||||||
|
|
||||||
|
private:
|
||||||
|
QPixmap PixmapForIndex(const QModelIndex& index, const QSize& size);
|
||||||
|
void StartLoadingColors(const QUrl& url, const QByteArray& bytes, Data* data);
|
||||||
|
void StartLoadingImage(const QUrl& url, Data* data);
|
||||||
|
|
||||||
private:
|
private:
|
||||||
Application* app_;
|
Application* app_;
|
||||||
|
QCache<QUrl, Data> data_;
|
||||||
};
|
};
|
||||||
|
|
||||||
#endif // MOODBARITEMDELEGATE_H
|
#endif // MOODBARITEMDELEGATE_H
|
||||||
|
@ -32,7 +32,7 @@
|
|||||||
MoodbarLoader::MoodbarLoader(QObject* parent)
|
MoodbarLoader::MoodbarLoader(QObject* parent)
|
||||||
: QObject(parent),
|
: QObject(parent),
|
||||||
cache_(new QNetworkDiskCache(this)),
|
cache_(new QNetworkDiskCache(this)),
|
||||||
kMaxActiveRequests(QThread::idealThreadCount()),
|
kMaxActiveRequests(QThread::idealThreadCount() / 2 + 1),
|
||||||
save_alongside_originals_(false)
|
save_alongside_originals_(false)
|
||||||
{
|
{
|
||||||
cache_->setCacheDirectory(Utilities::GetConfigPath(Utilities::Path_MoodbarCache));
|
cache_->setCacheDirectory(Utilities::GetConfigPath(Utilities::Path_MoodbarCache));
|
||||||
@ -62,9 +62,12 @@ MoodbarLoader::Result MoodbarLoader::Load(
|
|||||||
}
|
}
|
||||||
|
|
||||||
// Are we in the middle of loading this moodbar already?
|
// Are we in the middle of loading this moodbar already?
|
||||||
if (active_requests_.contains(url)) {
|
{
|
||||||
*async_pipeline = active_requests_[url];
|
QMutexLocker l(&mutex_);
|
||||||
return WillLoadAsync;
|
if (requests_.contains(url)) {
|
||||||
|
*async_pipeline = requests_[url];
|
||||||
|
return WillLoadAsync;
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
// Check if a mood file exists for this file already
|
// Check if a mood file exists for this file already
|
||||||
@ -93,29 +96,30 @@ MoodbarLoader::Result MoodbarLoader::Load(
|
|||||||
this, SLOT(RequestFinished(MoodbarPipeline*,QUrl)),
|
this, SLOT(RequestFinished(MoodbarPipeline*,QUrl)),
|
||||||
pipeline, url);
|
pipeline, url);
|
||||||
|
|
||||||
active_requests_[url] = pipeline;
|
{
|
||||||
|
QMutexLocker l(&mutex_);
|
||||||
if (active_requests_.count() > kMaxActiveRequests) {
|
requests_[url] = pipeline;
|
||||||
// Just queue this request now, start it later when another request
|
|
||||||
// finishes.
|
|
||||||
queued_requests_ << url;
|
queued_requests_ << url;
|
||||||
} else if (!StartQueuedRequest(url)) {
|
|
||||||
return CannotLoad;
|
|
||||||
}
|
}
|
||||||
|
|
||||||
|
QMetaObject::invokeMethod(this, "MaybeTakeNextRequest", Qt::QueuedConnection);
|
||||||
|
|
||||||
*async_pipeline = pipeline;
|
*async_pipeline = pipeline;
|
||||||
return WillLoadAsync;
|
return WillLoadAsync;
|
||||||
}
|
}
|
||||||
|
|
||||||
bool MoodbarLoader::StartQueuedRequest(const QUrl& url) {
|
void MoodbarLoader::MaybeTakeNextRequest() {
|
||||||
if (!active_requests_[url]->Start()) {
|
QMutexLocker l(&mutex_);
|
||||||
delete active_requests_.take(url);
|
if (active_requests_.count() > kMaxActiveRequests ||
|
||||||
return false;
|
queued_requests_.isEmpty()) {
|
||||||
|
return;
|
||||||
}
|
}
|
||||||
|
|
||||||
qLog(Info) << "Creating moodbar data for" << url.toLocalFile();
|
const QUrl url = queued_requests_.takeFirst();
|
||||||
|
active_requests_ << url;
|
||||||
|
|
||||||
return true;
|
qLog(Info) << "Creating moodbar data for" << url.toLocalFile();
|
||||||
|
requests_[url]->Start();
|
||||||
}
|
}
|
||||||
|
|
||||||
void MoodbarLoader::RequestFinished(MoodbarPipeline* request, const QUrl& url) {
|
void MoodbarLoader::RequestFinished(MoodbarPipeline* request, const QUrl& url) {
|
||||||
@ -145,10 +149,12 @@ void MoodbarLoader::RequestFinished(MoodbarPipeline* request, const QUrl& url) {
|
|||||||
qLog(Debug) << "Deleting" << request;
|
qLog(Debug) << "Deleting" << request;
|
||||||
|
|
||||||
// Remove the request from the active list and delete it
|
// Remove the request from the active list and delete it
|
||||||
active_requests_.take(url);
|
{
|
||||||
|
QMutexLocker l(&mutex_);
|
||||||
|
requests_.remove(url);
|
||||||
|
active_requests_.remove(url);
|
||||||
|
}
|
||||||
QTimer::singleShot(10, request, SLOT(deleteLater()));
|
QTimer::singleShot(10, request, SLOT(deleteLater()));
|
||||||
|
|
||||||
if (!queued_requests_.isEmpty()) {
|
QMetaObject::invokeMethod(this, "MaybeTakeNextRequest", Qt::QueuedConnection);
|
||||||
StartQueuedRequest(queued_requests_.takeFirst());
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
|
@ -19,8 +19,9 @@
|
|||||||
#define MOODBARLOADER_H
|
#define MOODBARLOADER_H
|
||||||
|
|
||||||
#include <QMap>
|
#include <QMap>
|
||||||
|
#include <QMutex>
|
||||||
#include <QObject>
|
#include <QObject>
|
||||||
#include <QPair>
|
#include <QSet>
|
||||||
|
|
||||||
class QNetworkDiskCache;
|
class QNetworkDiskCache;
|
||||||
class QUrl;
|
class QUrl;
|
||||||
@ -51,18 +52,20 @@ public:
|
|||||||
|
|
||||||
private slots:
|
private slots:
|
||||||
void RequestFinished(MoodbarPipeline* request, const QUrl& filename);
|
void RequestFinished(MoodbarPipeline* request, const QUrl& filename);
|
||||||
|
void MaybeTakeNextRequest();
|
||||||
|
|
||||||
private:
|
private:
|
||||||
static QStringList MoodFilenames(const QString& song_filename);
|
static QStringList MoodFilenames(const QString& song_filename);
|
||||||
bool StartQueuedRequest(const QUrl& url);
|
|
||||||
|
|
||||||
private:
|
private:
|
||||||
QNetworkDiskCache* cache_;
|
QNetworkDiskCache* cache_;
|
||||||
|
|
||||||
const int kMaxActiveRequests;
|
const int kMaxActiveRequests;
|
||||||
|
|
||||||
QMap<QUrl, MoodbarPipeline*> active_requests_;
|
QMutex mutex_;
|
||||||
|
QMap<QUrl, MoodbarPipeline*> requests_;
|
||||||
QList<QUrl> queued_requests_;
|
QList<QUrl> queued_requests_;
|
||||||
|
QSet<QUrl> active_requests_;
|
||||||
|
|
||||||
bool save_alongside_originals_;
|
bool save_alongside_originals_;
|
||||||
};
|
};
|
||||||
|
@ -32,7 +32,6 @@ MoodbarPipeline::MoodbarPipeline(const QString& local_filename)
|
|||||||
}
|
}
|
||||||
|
|
||||||
MoodbarPipeline::~MoodbarPipeline() {
|
MoodbarPipeline::~MoodbarPipeline() {
|
||||||
qLog(Debug) << "Actually deleting" << this;
|
|
||||||
Cleanup();
|
Cleanup();
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -84,6 +83,7 @@ bool MoodbarPipeline::Start() {
|
|||||||
|
|
||||||
if (!filesrc || !convert_element_ || !fftwspectrum || !moodbar || !appsink) {
|
if (!filesrc || !convert_element_ || !fftwspectrum || !moodbar || !appsink) {
|
||||||
pipeline_ = NULL;
|
pipeline_ = NULL;
|
||||||
|
emit Finished(false);
|
||||||
return false;
|
return false;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -139,3 +139,11 @@ void MoodbarRenderer::Render(const ColorVector& colors, QPainter* p, const QRect
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
QImage MoodbarRenderer::RenderToImage(const ColorVector& colors, const QSize& size) {
|
||||||
|
QImage image(size, QImage::Format_ARGB32_Premultiplied);
|
||||||
|
QPainter p(&image);
|
||||||
|
Render(colors, &p, image.rect());
|
||||||
|
p.end();
|
||||||
|
return image;
|
||||||
|
}
|
||||||
|
@ -41,6 +41,7 @@ public:
|
|||||||
static ColorVector Colors(const QByteArray& data, MoodbarStyle style,
|
static ColorVector Colors(const QByteArray& data, MoodbarStyle style,
|
||||||
const QPalette& palette);
|
const QPalette& palette);
|
||||||
static void Render(const ColorVector& colors, QPainter* p, const QRect& rect);
|
static void Render(const ColorVector& colors, QPainter* p, const QRect& rect);
|
||||||
|
static QImage RenderToImage(const ColorVector& colors, const QSize& size);
|
||||||
|
|
||||||
private:
|
private:
|
||||||
MoodbarRenderer();
|
MoodbarRenderer();
|
||||||
|
@ -43,7 +43,6 @@
|
|||||||
#include "library/librarybackend.h"
|
#include "library/librarybackend.h"
|
||||||
#include "library/librarymodel.h"
|
#include "library/librarymodel.h"
|
||||||
#include "library/libraryplaylistitem.h"
|
#include "library/libraryplaylistitem.h"
|
||||||
#include "moodbar/moodbarrenderer.h"
|
|
||||||
#include "smartplaylists/generator.h"
|
#include "smartplaylists/generator.h"
|
||||||
#include "smartplaylists/generatorinserter.h"
|
#include "smartplaylists/generatorinserter.h"
|
||||||
#include "smartplaylists/generatormimedata.h"
|
#include "smartplaylists/generatormimedata.h"
|
||||||
@ -244,12 +243,6 @@ QVariant Playlist::data(const QModelIndex& index, int role) const {
|
|||||||
items_[index.row()]->IsLocalLibraryItem() &&
|
items_[index.row()]->IsLocalLibraryItem() &&
|
||||||
items_[index.row()]->Metadata().id() != -1;
|
items_[index.row()]->Metadata().id() != -1;
|
||||||
|
|
||||||
case Role_MoodbarColors:
|
|
||||||
return QVariant::fromValue(items_[index.row()]->MoodbarColors());
|
|
||||||
|
|
||||||
case Role_MoodbarPixmap:
|
|
||||||
return items_[index.row()]->MoodbarPixmap();
|
|
||||||
|
|
||||||
case Qt::EditRole:
|
case Qt::EditRole:
|
||||||
case Qt::ToolTipRole:
|
case Qt::ToolTipRole:
|
||||||
case Qt::DisplayRole: {
|
case Qt::DisplayRole: {
|
||||||
@ -316,21 +309,16 @@ QVariant Playlist::data(const QModelIndex& index, int role) const {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
void Playlist::MoodbarUpdated(const QModelIndex& index) {
|
||||||
|
emit dataChanged(index.sibling(index.row(), Column_Mood),
|
||||||
|
index.sibling(index.row(), Column_Mood));
|
||||||
|
}
|
||||||
|
|
||||||
bool Playlist::setData(const QModelIndex& index, const QVariant& value, int role) {
|
bool Playlist::setData(const QModelIndex& index, const QVariant& value, int role) {
|
||||||
int row = index.row();
|
int row = index.row();
|
||||||
PlaylistItemPtr item = item_at(row);
|
PlaylistItemPtr item = item_at(row);
|
||||||
Song song = item->Metadata();
|
Song song = item->Metadata();
|
||||||
|
|
||||||
if (role == Role_MoodbarColors) {
|
|
||||||
item->SetMoodbarColors(value.value<QVector<QColor> >());
|
|
||||||
emit dataChanged(index.sibling(index.row(), Column_Mood),
|
|
||||||
index.sibling(index.row(), Column_Mood));
|
|
||||||
return true;
|
|
||||||
} else if (role == Role_MoodbarPixmap) {
|
|
||||||
item->SetMoodbarPixmap(value.value<QPixmap>());
|
|
||||||
return true;
|
|
||||||
}
|
|
||||||
|
|
||||||
if (index.data() == value)
|
if (index.data() == value)
|
||||||
return false;
|
return false;
|
||||||
|
|
||||||
|
@ -122,8 +122,6 @@ class Playlist : public QAbstractListModel {
|
|||||||
Role_StopAfter,
|
Role_StopAfter,
|
||||||
Role_QueuePosition,
|
Role_QueuePosition,
|
||||||
Role_CanSetRating,
|
Role_CanSetRating,
|
||||||
Role_MoodbarColors,
|
|
||||||
Role_MoodbarPixmap,
|
|
||||||
};
|
};
|
||||||
|
|
||||||
enum LastFMStatus {
|
enum LastFMStatus {
|
||||||
@ -246,6 +244,9 @@ class Playlist : public QAbstractListModel {
|
|||||||
// Unregisters a SongInsertVetoListener object.
|
// Unregisters a SongInsertVetoListener object.
|
||||||
void RemoveSongInsertVetoListener(SongInsertVetoListener* listener);
|
void RemoveSongInsertVetoListener(SongInsertVetoListener* listener);
|
||||||
|
|
||||||
|
// Just emits the dataChanged() signal so the mood column is repainted.
|
||||||
|
void MoodbarUpdated(const QModelIndex& index);
|
||||||
|
|
||||||
// QAbstractListModel
|
// QAbstractListModel
|
||||||
int rowCount(const QModelIndex& = QModelIndex()) const { return items_.count(); }
|
int rowCount(const QModelIndex& = QModelIndex()) const { return items_.count(); }
|
||||||
int columnCount(const QModelIndex& = QModelIndex()) const { return ColumnCount; }
|
int columnCount(const QModelIndex& = QModelIndex()) const { return ColumnCount; }
|
||||||
|
@ -33,7 +33,6 @@
|
|||||||
|
|
||||||
|
|
||||||
PlaylistItem::~PlaylistItem() {
|
PlaylistItem::~PlaylistItem() {
|
||||||
delete moodbar_pixmap_;
|
|
||||||
}
|
}
|
||||||
|
|
||||||
PlaylistItem* PlaylistItem::NewFromType(const QString& type) {
|
PlaylistItem* PlaylistItem::NewFromType(const QString& type) {
|
||||||
@ -124,17 +123,3 @@ QColor PlaylistItem::GetCurrentForegroundColor() const {
|
|||||||
bool PlaylistItem::HasCurrentForegroundColor() const {
|
bool PlaylistItem::HasCurrentForegroundColor() const {
|
||||||
return !foreground_colors_.isEmpty();
|
return !foreground_colors_.isEmpty();
|
||||||
}
|
}
|
||||||
|
|
||||||
QPixmap PlaylistItem::MoodbarPixmap() const {
|
|
||||||
if (!moodbar_pixmap_) {
|
|
||||||
return QPixmap();
|
|
||||||
}
|
|
||||||
return *moodbar_pixmap_;
|
|
||||||
}
|
|
||||||
|
|
||||||
void PlaylistItem::SetMoodbarPixmap(const QPixmap& pixmap) {
|
|
||||||
if (!moodbar_pixmap_) {
|
|
||||||
moodbar_pixmap_ = new QPixmap;
|
|
||||||
}
|
|
||||||
*moodbar_pixmap_ = pixmap;
|
|
||||||
}
|
|
||||||
|
@ -33,8 +33,7 @@ class SqlRow;
|
|||||||
class PlaylistItem : public boost::enable_shared_from_this<PlaylistItem> {
|
class PlaylistItem : public boost::enable_shared_from_this<PlaylistItem> {
|
||||||
public:
|
public:
|
||||||
PlaylistItem(const QString& type)
|
PlaylistItem(const QString& type)
|
||||||
: type_(type),
|
: type_(type) {}
|
||||||
moodbar_pixmap_(NULL) {}
|
|
||||||
virtual ~PlaylistItem();
|
virtual ~PlaylistItem();
|
||||||
|
|
||||||
static PlaylistItem* NewFromType(const QString& type);
|
static PlaylistItem* NewFromType(const QString& type);
|
||||||
@ -93,12 +92,6 @@ class PlaylistItem : public boost::enable_shared_from_this<PlaylistItem> {
|
|||||||
// before actually using it.
|
// before actually using it.
|
||||||
virtual bool IsLocalLibraryItem() const { return false; }
|
virtual bool IsLocalLibraryItem() const { return false; }
|
||||||
|
|
||||||
// Moodbar accessors. These are lazy-loaded by MoodbarItemDelegate.
|
|
||||||
const QVector<QColor>& MoodbarColors() const { return moodbar_colors_; }
|
|
||||||
void SetMoodbarColors(const QVector<QColor>& colors) { moodbar_colors_ = colors; }
|
|
||||||
QPixmap MoodbarPixmap() const;
|
|
||||||
void SetMoodbarPixmap(const QPixmap& pixmap);
|
|
||||||
|
|
||||||
protected:
|
protected:
|
||||||
enum DatabaseColumn {
|
enum DatabaseColumn {
|
||||||
Column_LibraryId,
|
Column_LibraryId,
|
||||||
@ -115,10 +108,6 @@ class PlaylistItem : public boost::enable_shared_from_this<PlaylistItem> {
|
|||||||
|
|
||||||
QMap<short, QColor> background_colors_;
|
QMap<short, QColor> background_colors_;
|
||||||
QMap<short, QColor> foreground_colors_;
|
QMap<short, QColor> foreground_colors_;
|
||||||
|
|
||||||
private:
|
|
||||||
QVector<QColor> moodbar_colors_;
|
|
||||||
QPixmap* moodbar_pixmap_;
|
|
||||||
};
|
};
|
||||||
typedef boost::shared_ptr<PlaylistItem> PlaylistItemPtr;
|
typedef boost::shared_ptr<PlaylistItem> PlaylistItemPtr;
|
||||||
typedef QList<PlaylistItemPtr> PlaylistItemList;
|
typedef QList<PlaylistItemPtr> PlaylistItemList;
|
||||||
|
Loading…
x
Reference in New Issue
Block a user