diff --git a/src/CMakeLists.txt b/src/CMakeLists.txt index 93b1e3e28..9557b3a99 100644 --- a/src/CMakeLists.txt +++ b/src/CMakeLists.txt @@ -305,6 +305,7 @@ set(SOURCES ui/screensaver.cpp ui/settingsdialog.cpp ui/settingspage.cpp + ui/standarditemiconloader.cpp ui/systemtrayicon.cpp ui/trackselectiondialog.cpp ui/windows7thumbbar.cpp @@ -541,6 +542,7 @@ set(HEADERS ui/qtsystemtrayicon.h ui/settingsdialog.h ui/settingspage.h + ui/standarditemiconloader.h ui/systemtrayicon.h ui/trackselectiondialog.h ui/windows7thumbbar.h diff --git a/src/podcasts/podcastdiscoverymodel.cpp b/src/podcasts/podcastdiscoverymodel.cpp index 3faa09d93..e124272db 100644 --- a/src/podcasts/podcastdiscoverymodel.cpp +++ b/src/podcasts/podcastdiscoverymodel.cpp @@ -18,8 +18,8 @@ #include "podcast.h" #include "podcastdiscoverymodel.h" #include "core/application.h" -#include "covers/albumcoverloader.h" #include "ui/iconloader.h" +#include "ui/standarditemiconloader.h" #include #include @@ -27,14 +27,11 @@ PodcastDiscoveryModel::PodcastDiscoveryModel(Application* app, QObject* parent) : QStandardItemModel(parent), app_(app), + icon_loader_(new StandardItemIconLoader(app->album_cover_loader(), this)), is_tree_(false), - default_icon_(":providers/podcast32.png") + default_icon_(":providers/podcast16.png") { - cover_options_.desired_height_ = 32; - - connect(app_->album_cover_loader(), SIGNAL(ImageLoaded(quint64,QImage)), - SLOT(ImageLoaded(quint64,QImage))); - connect(this, SIGNAL(modelAboutToBeReset()), SLOT(CancelPendingImages())); + icon_loader_->SetModel(this); } 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(); if (podcast.image_url().isValid()) { - quint64 id = app_->album_cover_loader()->LoadImageAsync( - cover_options_, podcast.image_url().toString(), QString()); - pending_covers_[id] = item; + icon_loader_->LoadIcon(podcast.image_url().toString(), QString(), 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::fromList(pending_covers_.keys())); - pending_covers_.clear(); -} - QStandardItem* PodcastDiscoveryModel::CreateLoadingIndicator() { QStandardItem* item = new QStandardItem; item->setText(tr("Loading...")); diff --git a/src/podcasts/podcastdiscoverymodel.h b/src/podcasts/podcastdiscoverymodel.h index 861caf861..b72d4acb0 100644 --- a/src/podcasts/podcastdiscoverymodel.h +++ b/src/podcasts/podcastdiscoverymodel.h @@ -24,6 +24,7 @@ class Application; class Podcast; +class StandardItemIconLoader; class PodcastDiscoveryModel : public QStandardItemModel { Q_OBJECT @@ -54,22 +55,17 @@ public: QVariant data(const QModelIndex& index, int role) const; -private slots: - void CancelPendingImages(); - void ImageLoaded(quint64 id, const QImage& image); - private: void LazyLoadImage(const QModelIndex& index); private: Application* app_; + StandardItemIconLoader* icon_loader_; bool is_tree_; - AlbumCoverLoaderOptions cover_options_; QIcon default_icon_; QIcon folder_icon_; - QMap pending_covers_; }; #endif // PODCASTDISCOVERYMODEL_H diff --git a/src/podcasts/podcastservice.cpp b/src/podcasts/podcastservice.cpp index b525cccea..d627e1878 100644 --- a/src/podcasts/podcastservice.cpp +++ b/src/podcasts/podcastservice.cpp @@ -19,8 +19,11 @@ #include "podcastbackend.h" #include "podcastservice.h" #include "core/application.h" +#include "core/logging.h" #include "internet/internetmodel.h" +#include "library/libraryview.h" #include "ui/iconloader.h" +#include "ui/standarditemiconloader.h" #include @@ -29,10 +32,13 @@ const char* PodcastService::kSettingsGroup = "Podcasts"; PodcastService::PodcastService(Application* app, InternetModel* parent) : InternetService(kServiceName, app, parent, parent), + use_pretty_covers_(true), + icon_loader_(new StandardItemIconLoader(app->album_cover_loader(), this)), context_menu_(NULL), root_(NULL), backend_(app->podcast_backend()) { + icon_loader_->SetModel(model()); } PodcastService::~PodcastService() { @@ -53,6 +59,10 @@ void PodcastService::LazyPopulate(QStandardItem* parent) { } void PodcastService::PopulatePodcastList(QStandardItem* parent) { + if (default_icon_.isNull()) { + default_icon_ = QIcon(":providers/podcast16.png"); + } + foreach (const Podcast& podcast, backend_->GetAllSubscriptions()) { const int unlistened_count = podcast.extra("db:unlistened_count").toInt(); QString title = podcast.title(); @@ -70,6 +80,12 @@ void PodcastService::PopulatePodcastList(QStandardItem* parent) { } 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); } @@ -86,6 +102,14 @@ void PodcastService::ShowContextMenu(const QModelIndex& index, 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() { return QModelIndex(); } diff --git a/src/podcasts/podcastservice.h b/src/podcasts/podcastservice.h index 1a2e6447d..c610659a1 100644 --- a/src/podcasts/podcastservice.h +++ b/src/podcasts/podcastservice.h @@ -25,6 +25,7 @@ class AddPodcastDialog; class PodcastBackend; +class StandardItemIconLoader; class PodcastService : public InternetService { Q_OBJECT @@ -46,6 +47,7 @@ public: void LazyPopulate(QStandardItem* parent); void ShowContextMenu(const QModelIndex& index, const QPoint& global_pos); + void ReloadSettings(); protected: QModelIndex GetCurrentIndex(); @@ -57,6 +59,10 @@ private: void PopulatePodcastList(QStandardItem* parent); private: + bool use_pretty_covers_; + QIcon default_icon_; + StandardItemIconLoader* icon_loader_; + QMenu* context_menu_; QStandardItem* root_; diff --git a/src/ui/standarditemiconloader.cpp b/src/ui/standarditemiconloader.cpp new file mode 100644 index 000000000..62efe300e --- /dev/null +++ b/src/ui/standarditemiconloader.cpp @@ -0,0 +1,94 @@ +/* This file is part of Clementine. + Copyright 2012, David Sansome + + 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 . +*/ + +#include "standarditemiconloader.h" +#include "covers/albumcoverloader.h" + +#include +#include +#include + +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::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::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))); + } +} diff --git a/src/ui/standarditemiconloader.h b/src/ui/standarditemiconloader.h new file mode 100644 index 000000000..536c27a88 --- /dev/null +++ b/src/ui/standarditemiconloader.h @@ -0,0 +1,63 @@ +/* This file is part of Clementine. + Copyright 2012, David Sansome + + 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 . +*/ + +#ifndef STANDARDITEMICONLOADER_H +#define STANDARDITEMICONLOADER_H + +#include "covers/albumcoverloaderoptions.h" + +#include +#include + +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 pending_covers_; +}; + +#endif // STANDARDITEMICONLOADER_H