Add a helper class to load icons asynchronously and set them on QStandardItems
This commit is contained in:
parent
f2885c0319
commit
884080684a
|
@ -305,6 +305,7 @@ set(SOURCES
|
||||||
ui/screensaver.cpp
|
ui/screensaver.cpp
|
||||||
ui/settingsdialog.cpp
|
ui/settingsdialog.cpp
|
||||||
ui/settingspage.cpp
|
ui/settingspage.cpp
|
||||||
|
ui/standarditemiconloader.cpp
|
||||||
ui/systemtrayicon.cpp
|
ui/systemtrayicon.cpp
|
||||||
ui/trackselectiondialog.cpp
|
ui/trackselectiondialog.cpp
|
||||||
ui/windows7thumbbar.cpp
|
ui/windows7thumbbar.cpp
|
||||||
|
@ -541,6 +542,7 @@ set(HEADERS
|
||||||
ui/qtsystemtrayicon.h
|
ui/qtsystemtrayicon.h
|
||||||
ui/settingsdialog.h
|
ui/settingsdialog.h
|
||||||
ui/settingspage.h
|
ui/settingspage.h
|
||||||
|
ui/standarditemiconloader.h
|
||||||
ui/systemtrayicon.h
|
ui/systemtrayicon.h
|
||||||
ui/trackselectiondialog.h
|
ui/trackselectiondialog.h
|
||||||
ui/windows7thumbbar.h
|
ui/windows7thumbbar.h
|
||||||
|
|
|
@ -18,8 +18,8 @@
|
||||||
#include "podcast.h"
|
#include "podcast.h"
|
||||||
#include "podcastdiscoverymodel.h"
|
#include "podcastdiscoverymodel.h"
|
||||||
#include "core/application.h"
|
#include "core/application.h"
|
||||||
#include "covers/albumcoverloader.h"
|
|
||||||
#include "ui/iconloader.h"
|
#include "ui/iconloader.h"
|
||||||
|
#include "ui/standarditemiconloader.h"
|
||||||
|
|
||||||
#include <QIcon>
|
#include <QIcon>
|
||||||
#include <QSet>
|
#include <QSet>
|
||||||
|
@ -27,14 +27,11 @@
|
||||||
PodcastDiscoveryModel::PodcastDiscoveryModel(Application* app, QObject* parent)
|
PodcastDiscoveryModel::PodcastDiscoveryModel(Application* app, QObject* parent)
|
||||||
: QStandardItemModel(parent),
|
: QStandardItemModel(parent),
|
||||||
app_(app),
|
app_(app),
|
||||||
|
icon_loader_(new StandardItemIconLoader(app->album_cover_loader(), this)),
|
||||||
is_tree_(false),
|
is_tree_(false),
|
||||||
default_icon_(":providers/podcast32.png")
|
default_icon_(":providers/podcast16.png")
|
||||||
{
|
{
|
||||||
cover_options_.desired_height_ = 32;
|
icon_loader_->SetModel(this);
|
||||||
|
|
||||||
connect(app_->album_cover_loader(), SIGNAL(ImageLoaded(quint64,QImage)),
|
|
||||||
SLOT(ImageLoaded(quint64,QImage)));
|
|
||||||
connect(this, SIGNAL(modelAboutToBeReset()), SLOT(CancelPendingImages()));
|
|
||||||
}
|
}
|
||||||
|
|
||||||
QVariant PodcastDiscoveryModel::data(const QModelIndex& index, int role) const {
|
QVariant PodcastDiscoveryModel::data(const QModelIndex& index, int role) const {
|
||||||
|
@ -76,27 +73,10 @@ void PodcastDiscoveryModel::LazyLoadImage(const QModelIndex& index) {
|
||||||
Podcast podcast = index.data(Role_Podcast).value<Podcast>();
|
Podcast podcast = index.data(Role_Podcast).value<Podcast>();
|
||||||
|
|
||||||
if (podcast.image_url().isValid()) {
|
if (podcast.image_url().isValid()) {
|
||||||
quint64 id = app_->album_cover_loader()->LoadImageAsync(
|
icon_loader_->LoadIcon(podcast.image_url().toString(), QString(), item);
|
||||||
cover_options_, podcast.image_url().toString(), QString());
|
|
||||||
pending_covers_[id] = item;
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
void PodcastDiscoveryModel::ImageLoaded(quint64 id, const QImage& image) {
|
|
||||||
QStandardItem* item = pending_covers_.take(id);
|
|
||||||
if (!item)
|
|
||||||
return;
|
|
||||||
|
|
||||||
if (!image.isNull()) {
|
|
||||||
item->setIcon(QIcon(QPixmap::fromImage(image)));
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
void PodcastDiscoveryModel::CancelPendingImages() {
|
|
||||||
app_->album_cover_loader()->CancelTasks(QSet<quint64>::fromList(pending_covers_.keys()));
|
|
||||||
pending_covers_.clear();
|
|
||||||
}
|
|
||||||
|
|
||||||
QStandardItem* PodcastDiscoveryModel::CreateLoadingIndicator() {
|
QStandardItem* PodcastDiscoveryModel::CreateLoadingIndicator() {
|
||||||
QStandardItem* item = new QStandardItem;
|
QStandardItem* item = new QStandardItem;
|
||||||
item->setText(tr("Loading..."));
|
item->setText(tr("Loading..."));
|
||||||
|
|
|
@ -24,6 +24,7 @@
|
||||||
|
|
||||||
class Application;
|
class Application;
|
||||||
class Podcast;
|
class Podcast;
|
||||||
|
class StandardItemIconLoader;
|
||||||
|
|
||||||
class PodcastDiscoveryModel : public QStandardItemModel {
|
class PodcastDiscoveryModel : public QStandardItemModel {
|
||||||
Q_OBJECT
|
Q_OBJECT
|
||||||
|
@ -54,22 +55,17 @@ public:
|
||||||
|
|
||||||
QVariant data(const QModelIndex& index, int role) const;
|
QVariant data(const QModelIndex& index, int role) const;
|
||||||
|
|
||||||
private slots:
|
|
||||||
void CancelPendingImages();
|
|
||||||
void ImageLoaded(quint64 id, const QImage& image);
|
|
||||||
|
|
||||||
private:
|
private:
|
||||||
void LazyLoadImage(const QModelIndex& index);
|
void LazyLoadImage(const QModelIndex& index);
|
||||||
|
|
||||||
private:
|
private:
|
||||||
Application* app_;
|
Application* app_;
|
||||||
|
StandardItemIconLoader* icon_loader_;
|
||||||
|
|
||||||
bool is_tree_;
|
bool is_tree_;
|
||||||
|
|
||||||
AlbumCoverLoaderOptions cover_options_;
|
|
||||||
QIcon default_icon_;
|
QIcon default_icon_;
|
||||||
QIcon folder_icon_;
|
QIcon folder_icon_;
|
||||||
QMap<quint64, QStandardItem*> pending_covers_;
|
|
||||||
};
|
};
|
||||||
|
|
||||||
#endif // PODCASTDISCOVERYMODEL_H
|
#endif // PODCASTDISCOVERYMODEL_H
|
||||||
|
|
|
@ -19,8 +19,11 @@
|
||||||
#include "podcastbackend.h"
|
#include "podcastbackend.h"
|
||||||
#include "podcastservice.h"
|
#include "podcastservice.h"
|
||||||
#include "core/application.h"
|
#include "core/application.h"
|
||||||
|
#include "core/logging.h"
|
||||||
#include "internet/internetmodel.h"
|
#include "internet/internetmodel.h"
|
||||||
|
#include "library/libraryview.h"
|
||||||
#include "ui/iconloader.h"
|
#include "ui/iconloader.h"
|
||||||
|
#include "ui/standarditemiconloader.h"
|
||||||
|
|
||||||
#include <QMenu>
|
#include <QMenu>
|
||||||
|
|
||||||
|
@ -29,10 +32,13 @@ const char* PodcastService::kSettingsGroup = "Podcasts";
|
||||||
|
|
||||||
PodcastService::PodcastService(Application* app, InternetModel* parent)
|
PodcastService::PodcastService(Application* app, InternetModel* parent)
|
||||||
: InternetService(kServiceName, app, parent, parent),
|
: InternetService(kServiceName, app, parent, parent),
|
||||||
|
use_pretty_covers_(true),
|
||||||
|
icon_loader_(new StandardItemIconLoader(app->album_cover_loader(), this)),
|
||||||
context_menu_(NULL),
|
context_menu_(NULL),
|
||||||
root_(NULL),
|
root_(NULL),
|
||||||
backend_(app->podcast_backend())
|
backend_(app->podcast_backend())
|
||||||
{
|
{
|
||||||
|
icon_loader_->SetModel(model());
|
||||||
}
|
}
|
||||||
|
|
||||||
PodcastService::~PodcastService() {
|
PodcastService::~PodcastService() {
|
||||||
|
@ -53,6 +59,10 @@ void PodcastService::LazyPopulate(QStandardItem* parent) {
|
||||||
}
|
}
|
||||||
|
|
||||||
void PodcastService::PopulatePodcastList(QStandardItem* parent) {
|
void PodcastService::PopulatePodcastList(QStandardItem* parent) {
|
||||||
|
if (default_icon_.isNull()) {
|
||||||
|
default_icon_ = QIcon(":providers/podcast16.png");
|
||||||
|
}
|
||||||
|
|
||||||
foreach (const Podcast& podcast, backend_->GetAllSubscriptions()) {
|
foreach (const Podcast& podcast, backend_->GetAllSubscriptions()) {
|
||||||
const int unlistened_count = podcast.extra("db:unlistened_count").toInt();
|
const int unlistened_count = podcast.extra("db:unlistened_count").toInt();
|
||||||
QString title = podcast.title();
|
QString title = podcast.title();
|
||||||
|
@ -70,6 +80,12 @@ void PodcastService::PopulatePodcastList(QStandardItem* parent) {
|
||||||
}
|
}
|
||||||
|
|
||||||
item->setText(podcast.title());
|
item->setText(podcast.title());
|
||||||
|
item->setIcon(default_icon_);
|
||||||
|
|
||||||
|
// Load the podcast's image if it has one
|
||||||
|
if (podcast.image_url().isValid()) {
|
||||||
|
icon_loader_->LoadIcon(podcast.image_url().toString(), QString(), item);
|
||||||
|
}
|
||||||
|
|
||||||
parent->appendRow(item);
|
parent->appendRow(item);
|
||||||
}
|
}
|
||||||
|
@ -86,6 +102,14 @@ void PodcastService::ShowContextMenu(const QModelIndex& index,
|
||||||
context_menu_->popup(global_pos);
|
context_menu_->popup(global_pos);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
void PodcastService::ReloadSettings() {
|
||||||
|
QSettings s;
|
||||||
|
s.beginGroup(LibraryView::kSettingsGroup);
|
||||||
|
|
||||||
|
use_pretty_covers_ = s.value("pretty_covers", true).toBool();
|
||||||
|
// TODO: reload the podcast icons that are already loaded?
|
||||||
|
}
|
||||||
|
|
||||||
QModelIndex PodcastService::GetCurrentIndex() {
|
QModelIndex PodcastService::GetCurrentIndex() {
|
||||||
return QModelIndex();
|
return QModelIndex();
|
||||||
}
|
}
|
||||||
|
|
|
@ -25,6 +25,7 @@
|
||||||
|
|
||||||
class AddPodcastDialog;
|
class AddPodcastDialog;
|
||||||
class PodcastBackend;
|
class PodcastBackend;
|
||||||
|
class StandardItemIconLoader;
|
||||||
|
|
||||||
class PodcastService : public InternetService {
|
class PodcastService : public InternetService {
|
||||||
Q_OBJECT
|
Q_OBJECT
|
||||||
|
@ -46,6 +47,7 @@ public:
|
||||||
void LazyPopulate(QStandardItem* parent);
|
void LazyPopulate(QStandardItem* parent);
|
||||||
|
|
||||||
void ShowContextMenu(const QModelIndex& index, const QPoint& global_pos);
|
void ShowContextMenu(const QModelIndex& index, const QPoint& global_pos);
|
||||||
|
void ReloadSettings();
|
||||||
|
|
||||||
protected:
|
protected:
|
||||||
QModelIndex GetCurrentIndex();
|
QModelIndex GetCurrentIndex();
|
||||||
|
@ -57,6 +59,10 @@ private:
|
||||||
void PopulatePodcastList(QStandardItem* parent);
|
void PopulatePodcastList(QStandardItem* parent);
|
||||||
|
|
||||||
private:
|
private:
|
||||||
|
bool use_pretty_covers_;
|
||||||
|
QIcon default_icon_;
|
||||||
|
StandardItemIconLoader* icon_loader_;
|
||||||
|
|
||||||
QMenu* context_menu_;
|
QMenu* context_menu_;
|
||||||
QStandardItem* root_;
|
QStandardItem* root_;
|
||||||
|
|
||||||
|
|
|
@ -0,0 +1,94 @@
|
||||||
|
/* This file is part of Clementine.
|
||||||
|
Copyright 2012, 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 "standarditemiconloader.h"
|
||||||
|
#include "covers/albumcoverloader.h"
|
||||||
|
|
||||||
|
#include <QAbstractItemModel>
|
||||||
|
#include <QSet>
|
||||||
|
#include <QStandardItem>
|
||||||
|
|
||||||
|
StandardItemIconLoader::StandardItemIconLoader(AlbumCoverLoader* cover_loader,
|
||||||
|
QObject* parent)
|
||||||
|
: QObject(parent),
|
||||||
|
cover_loader_(cover_loader),
|
||||||
|
model_(NULL)
|
||||||
|
{
|
||||||
|
cover_options_.desired_height_ = 16;
|
||||||
|
|
||||||
|
connect(cover_loader_, SIGNAL(ImageLoaded(quint64,QImage)),
|
||||||
|
SLOT(ImageLoaded(quint64,QImage)));
|
||||||
|
}
|
||||||
|
|
||||||
|
void StandardItemIconLoader::SetModel(QAbstractItemModel* model) {
|
||||||
|
if (model_) {
|
||||||
|
disconnect(model_, SIGNAL(rowsAboutToBeRemoved(QModelIndex,int,int)),
|
||||||
|
this, SLOT(RowsAboutToBeRemoved(QModelIndex,int,int)));
|
||||||
|
}
|
||||||
|
|
||||||
|
model_ = model;
|
||||||
|
|
||||||
|
connect(model_, SIGNAL(rowsAboutToBeRemoved(QModelIndex,int,int)),
|
||||||
|
SLOT(RowsAboutToBeRemoved(QModelIndex,int,int)));
|
||||||
|
connect(model_, SIGNAL(modelAboutToBeReset()),
|
||||||
|
SLOT(ModelReset()));
|
||||||
|
}
|
||||||
|
|
||||||
|
void StandardItemIconLoader::LoadIcon(const QString& art_automatic,
|
||||||
|
const QString& art_manual,
|
||||||
|
QStandardItem* for_item) {
|
||||||
|
const quint64 id = cover_loader_->LoadImageAsync(cover_options_, art_automatic, art_manual);
|
||||||
|
pending_covers_[id] = for_item;
|
||||||
|
}
|
||||||
|
|
||||||
|
void StandardItemIconLoader::LoadIcon(const Song& song, QStandardItem* for_item) {
|
||||||
|
const quint64 id = cover_loader_->LoadImageAsync(cover_options_, song);
|
||||||
|
pending_covers_[id] = for_item;
|
||||||
|
}
|
||||||
|
|
||||||
|
void StandardItemIconLoader::RowsAboutToBeRemoved(const QModelIndex& parent, int begin, int end) {
|
||||||
|
for (QMap<quint64, QStandardItem*>::iterator it = pending_covers_.begin() ;
|
||||||
|
it != pending_covers_.end() ; ) {
|
||||||
|
const QStandardItem* item = it.value();
|
||||||
|
const QStandardItem* item_parent = item->parent();
|
||||||
|
|
||||||
|
if (item_parent &&
|
||||||
|
item_parent->index() == parent &&
|
||||||
|
item->index().row() >= begin &&
|
||||||
|
item->index().row() <= end) {
|
||||||
|
cover_loader_->CancelTask(it.key());
|
||||||
|
it = pending_covers_.erase(it);
|
||||||
|
} else {
|
||||||
|
++ it;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
void StandardItemIconLoader::ModelReset() {
|
||||||
|
cover_loader_->CancelTasks(QSet<quint64>::fromList(pending_covers_.keys()));
|
||||||
|
pending_covers_.clear();
|
||||||
|
}
|
||||||
|
|
||||||
|
void StandardItemIconLoader::ImageLoaded(quint64 id, const QImage& image) {
|
||||||
|
QStandardItem* item = pending_covers_.take(id);
|
||||||
|
if (!item)
|
||||||
|
return;
|
||||||
|
|
||||||
|
if (!image.isNull()) {
|
||||||
|
item->setIcon(QIcon(QPixmap::fromImage(image)));
|
||||||
|
}
|
||||||
|
}
|
|
@ -0,0 +1,63 @@
|
||||||
|
/* This file is part of Clementine.
|
||||||
|
Copyright 2012, 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 STANDARDITEMICONLOADER_H
|
||||||
|
#define STANDARDITEMICONLOADER_H
|
||||||
|
|
||||||
|
#include "covers/albumcoverloaderoptions.h"
|
||||||
|
|
||||||
|
#include <QMap>
|
||||||
|
#include <QObject>
|
||||||
|
|
||||||
|
class AlbumCoverLoader;
|
||||||
|
class Song;
|
||||||
|
|
||||||
|
class QAbstractItemModel;
|
||||||
|
class QModelIndex;
|
||||||
|
class QStandardItem;
|
||||||
|
|
||||||
|
// Uses an AlbumCoverLoader to asynchronously load and set an icon on a
|
||||||
|
// QStandardItem.
|
||||||
|
class StandardItemIconLoader : public QObject {
|
||||||
|
Q_OBJECT
|
||||||
|
|
||||||
|
public:
|
||||||
|
StandardItemIconLoader(AlbumCoverLoader* cover_loader, QObject* parent = 0);
|
||||||
|
|
||||||
|
AlbumCoverLoaderOptions* options() { return &cover_options_; }
|
||||||
|
|
||||||
|
void SetModel(QAbstractItemModel* model);
|
||||||
|
|
||||||
|
void LoadIcon(const QString& art_automatic, const QString& art_manual,
|
||||||
|
QStandardItem* for_item);
|
||||||
|
void LoadIcon(const Song& song, QStandardItem* for_item);
|
||||||
|
|
||||||
|
private slots:
|
||||||
|
void ImageLoaded(quint64 id, const QImage& image);
|
||||||
|
void RowsAboutToBeRemoved(const QModelIndex& parent, int begin, int end);
|
||||||
|
void ModelReset();
|
||||||
|
|
||||||
|
private:
|
||||||
|
AlbumCoverLoader* cover_loader_;
|
||||||
|
AlbumCoverLoaderOptions cover_options_;
|
||||||
|
|
||||||
|
QAbstractItemModel* model_;
|
||||||
|
|
||||||
|
QMap<quint64, QStandardItem*> pending_covers_;
|
||||||
|
};
|
||||||
|
|
||||||
|
#endif // STANDARDITEMICONLOADER_H
|
Loading…
Reference in New Issue