From 6b8d6c93f9958288a1a3b0a03373729355148284 Mon Sep 17 00:00:00 2001 From: David Sansome Date: Sun, 9 May 2010 15:51:04 +0000 Subject: [PATCH] Add a MergedProxyModel that lets us merge two models into one... --- src/CMakeLists.txt | 2 + src/lastfmservice.cpp | 2 +- src/lastfmservice.h | 2 +- src/magnatuneservice.cpp | 14 +- src/magnatuneservice.h | 7 +- src/mainwindow.cpp | 5 +- src/mergedproxymodel.cpp | 227 ++++++++++++++++++++++++++++++++ src/mergedproxymodel.h | 92 +++++++++++++ src/radiomodel.cpp | 8 +- src/radiomodel.h | 9 +- src/radioservice.cpp | 6 +- src/radioservice.h | 5 +- src/radioview.cpp | 9 +- src/savedradio.cpp | 2 +- src/savedradio.h | 2 +- src/simpletreeitem.h | 4 + src/somafmservice.cpp | 2 +- src/somafmservice.h | 2 +- tests/CMakeLists.txt | 1 + tests/mergedproxymodel_test.cpp | 84 ++++++++++++ 20 files changed, 467 insertions(+), 18 deletions(-) create mode 100644 src/mergedproxymodel.cpp create mode 100644 src/mergedproxymodel.h create mode 100644 tests/mergedproxymodel_test.cpp diff --git a/src/CMakeLists.txt b/src/CMakeLists.txt index 6473af75e..0c012abe3 100644 --- a/src/CMakeLists.txt +++ b/src/CMakeLists.txt @@ -84,6 +84,7 @@ set(CLEMENTINE-SOURCES database.cpp librarymodel.cpp playlistbackend.cpp + mergedproxymodel.cpp ) # Header files that have Q_OBJECT in @@ -153,6 +154,7 @@ set(CLEMENTINE-MOC-HEADERS librarymodel.h playlistbackend.h database.h + mergedproxymodel.h ) # lists of engine source files diff --git a/src/lastfmservice.cpp b/src/lastfmservice.cpp index 31800fd1e..36135b157 100644 --- a/src/lastfmservice.cpp +++ b/src/lastfmservice.cpp @@ -47,7 +47,7 @@ const char* LastFMService::kAudioscrobblerClientId = "tng"; const char* LastFMService::kApiKey = "75d20fb472be99275392aefa2760ea09"; const char* LastFMService::kSecret = "d3072b60ae626be12be69448f5c46e70"; -LastFMService::LastFMService(QObject* parent) +LastFMService::LastFMService(RadioModel* parent) : RadioService(kServiceName, parent), scrobbler_(NULL), station_dialog_(new LastFMStationDialog), diff --git a/src/lastfmservice.h b/src/lastfmservice.h index 228780121..f4ae83cf7 100644 --- a/src/lastfmservice.h +++ b/src/lastfmservice.h @@ -47,7 +47,7 @@ class LastFMService : public RadioService { Q_OBJECT public: - LastFMService(QObject* parent = 0); + LastFMService(RadioModel* parent); ~LastFMService(); static const char* kServiceName; diff --git a/src/magnatuneservice.cpp b/src/magnatuneservice.cpp index 9c5f9c015..1cf7c4a58 100644 --- a/src/magnatuneservice.cpp +++ b/src/magnatuneservice.cpp @@ -16,6 +16,10 @@ #include "magnatuneservice.h" #include "song.h" +#include "radiomodel.h" +#include "mergedproxymodel.h" +#include "librarymodel.h" +#include "librarybackend.h" #include #include @@ -29,16 +33,24 @@ const char* MagnatuneService::kServiceName = "Magnatune"; const char* MagnatuneService::kDatabaseUrl = "http://magnatune.com/info/song_info2_xml.gz"; -MagnatuneService::MagnatuneService(QObject* parent) +MagnatuneService::MagnatuneService(RadioModel* parent) : RadioService(kServiceName, parent), root_(NULL), + library_backend_(new LibraryBackend(parent->db(), "songs", "", "", this)), + library_model_(new LibraryModel(library_backend_, this)), network_(new QNetworkAccessManager(this)) { + library_model_->Init(); } RadioItem* MagnatuneService::CreateRootItem(RadioItem *parent) { root_ = new RadioItem(this, RadioItem::Type_Service, kServiceName, parent); root_->icon = QIcon(":magnatune.png"); + + model()->merged_model()->AddSubModel( + model()->index(root_->row, 0, model()->ItemToIndex(parent)), + library_model_); + return root_; } diff --git a/src/magnatuneservice.h b/src/magnatuneservice.h index 37c3c04ba..2fa667d7b 100644 --- a/src/magnatuneservice.h +++ b/src/magnatuneservice.h @@ -23,11 +23,14 @@ class QNetworkAccessManager; +class LibraryBackend; +class LibraryModel; + class MagnatuneService : public RadioService { Q_OBJECT public: - MagnatuneService(QObject* parent = 0); + MagnatuneService(RadioModel* parent); static const char* kServiceName; static const char* kDatabaseUrl; @@ -46,6 +49,8 @@ class MagnatuneService : public RadioService { private: RadioItem* root_; + LibraryBackend* library_backend_; + LibraryModel* library_model_; QNetworkAccessManager* network_; }; diff --git a/src/mainwindow.cpp b/src/mainwindow.cpp index a785fd56d..37f939ed1 100644 --- a/src/mainwindow.cpp +++ b/src/mainwindow.cpp @@ -47,6 +47,7 @@ #include "transcodedialog.h" #include "playlistbackend.h" #include "database.h" +#include "mergedproxymodel.h" #include "globalshortcuts/globalshortcuts.h" @@ -93,7 +94,7 @@ MainWindow::MainWindow(QNetworkAccessManager* network, Engine::Type engine, QWid library_config_dialog_(new LibraryConfigDialog), about_dialog_(new About), database_(new Database(this)), - radio_model_(new RadioModel(this)), + radio_model_(new RadioModel(database_, this)), playlist_backend_(new PlaylistBackend(database_, this)), playlist_(new Playlist(playlist_backend_, this)), player_(new Player(playlist_, radio_model_->GetLastFMService(), engine, this)), @@ -143,7 +144,7 @@ MainWindow::MainWindow(QNetworkAccessManager* network, Engine::Type engine, QWid library_config_dialog_->SetModel(library_->model()->directory_model()); settings_dialog_->SetLibraryDirectoryModel(library_->model()->directory_model()); - ui_.radio_view->setModel(radio_model_); + ui_.radio_view->setModel(radio_model_->merged_model()); cover_manager_->Init(); diff --git a/src/mergedproxymodel.cpp b/src/mergedproxymodel.cpp new file mode 100644 index 000000000..a1998bcc4 --- /dev/null +++ b/src/mergedproxymodel.cpp @@ -0,0 +1,227 @@ +/* This file is part of Clementine. + + 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 "mergedproxymodel.h" + +std::size_t hash_value(const QModelIndex& index) { + return qHash(index); +} + +MergedProxyModel::MergedProxyModel(QObject* parent) + : QAbstractProxyModel(parent) +{ +} + +void MergedProxyModel::AddSubModel(const QModelIndex& source_parent, + const QAbstractItemModel* submodel) { + merge_points_.insert(submodel, source_parent); + + connect(submodel, SIGNAL(modelReset()), this, SLOT(SubModelReset())); + connect(submodel, SIGNAL(rowsAboutToBeInserted(QModelIndex,int,int)), + this, SLOT(RowsAboutToBeInserted(QModelIndex,int,int))); + connect(submodel, SIGNAL(rowsAboutToBeRemoved(QModelIndex,int,int)), + this, SLOT(RowsAboutToBeRemoved(QModelIndex,int,int))); + connect(submodel, SIGNAL(rowsInserted(QModelIndex,int,int)), + this, SLOT(RowsInserted(QModelIndex,int,int))); + connect(submodel, SIGNAL(rowsRemoved(QModelIndex,int,int)), + this, SLOT(RowsRemoved(QModelIndex,int,int))); +} + +void MergedProxyModel::setSourceModel(QAbstractItemModel* source_model) { + if (sourceModel()) { + disconnect(sourceModel(), SIGNAL(modelReset()), this, SLOT(SourceModelReset())); + disconnect(sourceModel(), SIGNAL(rowsAboutToBeInserted(QModelIndex,int,int)), + this, SLOT(RowsAboutToBeInserted(QModelIndex,int,int))); + disconnect(sourceModel(), SIGNAL(rowsAboutToBeRemoved(QModelIndex,int,int)), + this, SLOT(RowsAboutToBeRemoved(QModelIndex,int,int))); + disconnect(sourceModel(), SIGNAL(rowsInserted(QModelIndex,int,int)), + this, SLOT(RowsInserted(QModelIndex,int,int))); + disconnect(sourceModel(), SIGNAL(rowsRemoved(QModelIndex,int,int)), + this, SLOT(RowsRemoved(QModelIndex,int,int))); + } + + QAbstractProxyModel::setSourceModel(source_model); + + connect(sourceModel(), SIGNAL(modelReset()), this, SLOT(SourceModelReset())); + connect(sourceModel(), SIGNAL(rowsAboutToBeInserted(QModelIndex,int,int)), + this, SLOT(RowsAboutToBeInserted(QModelIndex,int,int))); + connect(sourceModel(), SIGNAL(rowsAboutToBeRemoved(QModelIndex,int,int)), + this, SLOT(RowsAboutToBeRemoved(QModelIndex,int,int))); + connect(sourceModel(), SIGNAL(rowsInserted(QModelIndex,int,int)), + this, SLOT(RowsInserted(QModelIndex,int,int))); + connect(sourceModel(), SIGNAL(rowsRemoved(QModelIndex,int,int)), + this, SLOT(RowsRemoved(QModelIndex,int,int))); +} + +void MergedProxyModel::SourceModelReset() { + // Delete all mappings + MappingContainer::index::type::iterator begin = + mappings_.get().begin(); + MappingContainer::index::type::iterator end = + mappings_.get().end(); + qDeleteAll(begin, end); + + // Clear the containers + mappings_.clear(); + merge_points_.clear(); + + // Reset the proxy + reset(); +} + +void MergedProxyModel::SubModelReset() { + const QAbstractItemModel* submodel = static_cast(sender()); + + // Delete all the mappings that reference the submodel + MappingContainer::index::type::iterator it = + mappings_.get().begin(); + MappingContainer::index::type::iterator end = + mappings_.get().end(); + + while (it != end) { + if ((*it)->source_index.model() == submodel) { + delete *it; + it = mappings_.get().erase(it); + } else { + ++it; + } + } + + // Reset the proxy + reset(); +} + +QModelIndex MergedProxyModel::GetActualSourceParent(const QModelIndex& source_parent, + const QAbstractItemModel* model) const { + if (!source_parent.isValid() && model != sourceModel()) + return merge_points_.value(model); + return source_parent; +} + +void MergedProxyModel::RowsAboutToBeInserted(const QModelIndex& source_parent, + int start, int end) { + beginInsertRows(GetActualSourceParent( + source_parent, static_cast(sender())), + start, end); +} + +void MergedProxyModel::RowsInserted(const QModelIndex&, int, int) { + endInsertRows(); +} + +void MergedProxyModel::RowsAboutToBeRemoved(const QModelIndex& source_parent, + int start, int end) { + beginRemoveRows(GetActualSourceParent( + source_parent, static_cast(sender())), + start, end); +} + +void MergedProxyModel::RowsRemoved(const QModelIndex&, int, int) { + endRemoveRows(); +} + +QModelIndex MergedProxyModel::mapToSource(const QModelIndex& proxy_index) const { + if (!proxy_index.isValid()) + return QModelIndex(); + + Mapping* mapping = static_cast(proxy_index.internalPointer()); + return mapping->source_index; +} + +QModelIndex MergedProxyModel::mapFromSource(const QModelIndex& source_index) const { + if (!source_index.isValid()) + return QModelIndex(); + + // Add a mapping if we don't have one already + MappingContainer::index::type::iterator it = + mappings_.get().find(source_index); + Mapping* mapping; + if (it != mappings_.get().end()) { + mapping = *it; + } else { + mapping = new Mapping(source_index); + const_cast(this)->mappings_.insert(mapping); + } + + return createIndex(source_index.row(), source_index.column(), mapping); +} + +QModelIndex MergedProxyModel::index(int row, int column, const QModelIndex &parent) const { + QModelIndex source_index; + + if (!parent.isValid()) { + source_index = sourceModel()->index(row, column, QModelIndex()); + } else { + QModelIndex source_parent = mapToSource(parent); + const QAbstractItemModel* child_model = merge_points_.key(source_parent); + + if (child_model) + source_index = child_model->index(row, column, QModelIndex()); + else + source_index = source_parent.model()->index(row, column, source_parent); + } + + return mapFromSource(source_index); +} + +QModelIndex MergedProxyModel::parent(const QModelIndex &child) const { + QModelIndex source_child = mapToSource(child); + if (source_child.model() == sourceModel()) + return mapFromSource(source_child.parent()); + + if (!source_child.parent().isValid()) + return mapFromSource(merge_points_.value(source_child.model())); + return mapFromSource(source_child.parent()); +} + +int MergedProxyModel::rowCount(const QModelIndex &parent) const { + if (!parent.isValid()) + return sourceModel()->rowCount(QModelIndex()); + + QModelIndex source_parent = mapToSource(parent); + const QAbstractItemModel* child_model = merge_points_.key(source_parent); + if (child_model) + return child_model->rowCount(QModelIndex()); + return source_parent.model()->rowCount(source_parent); +} + +int MergedProxyModel::columnCount(const QModelIndex &parent) const { + if (!parent.isValid()) + return sourceModel()->columnCount(QModelIndex()); + + QModelIndex source_parent = mapToSource(parent); + const QAbstractItemModel* child_model = merge_points_.key(source_parent); + if (child_model) + return child_model->columnCount(QModelIndex()); + return source_parent.model()->columnCount(source_parent); +} + +bool MergedProxyModel::hasChildren(const QModelIndex &parent) const { + if (!parent.isValid()) + return sourceModel()->hasChildren(QModelIndex()); + + QModelIndex source_parent = mapToSource(parent); + const QAbstractItemModel* child_model = merge_points_.key(source_parent); + + if (child_model) + return child_model->hasChildren(QModelIndex()); + return source_parent.model()->hasChildren(source_parent); +} + +QVariant MergedProxyModel::data(const QModelIndex &proxyIndex, int role) const { + QModelIndex source_index = mapToSource(proxyIndex); + return source_index.model()->data(source_index, role); +} diff --git a/src/mergedproxymodel.h b/src/mergedproxymodel.h new file mode 100644 index 000000000..7bc3fd867 --- /dev/null +++ b/src/mergedproxymodel.h @@ -0,0 +1,92 @@ +/* This file is part of Clementine. + + 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 MERGEDPROXYMODEL_H +#define MERGEDPROXYMODEL_H + +#include + +#include +#include +#include +#include + +using boost::multi_index::multi_index_container; +using boost::multi_index::indexed_by; +using boost::multi_index::hashed_unique; +using boost::multi_index::ordered_unique; +using boost::multi_index::tag; +using boost::multi_index::member; +using boost::multi_index::identity; + +std::size_t hash_value(const QModelIndex& index); + +class MergedProxyModel : public QAbstractProxyModel { + Q_OBJECT + + public: + MergedProxyModel(QObject* parent = 0); + + void AddSubModel(const QModelIndex& source_parent, const QAbstractItemModel* submodel); + + // QAbstractItemModel + QModelIndex index(int row, int column, const QModelIndex &parent) const; + QModelIndex parent(const QModelIndex &child) const; + int rowCount(const QModelIndex &parent) const; + int columnCount(const QModelIndex &parent) const; + QVariant data(const QModelIndex &proxyIndex, int role = Qt::DisplayRole) const; + bool hasChildren(const QModelIndex &parent) const; + + // QAbstractProxyModel + QModelIndex mapFromSource(const QModelIndex &sourceIndex) const; + QModelIndex mapToSource(const QModelIndex &proxyIndex) const; + void setSourceModel(QAbstractItemModel *sourceModel); + + private slots: + void SourceModelReset(); + void SubModelReset(); + + void RowsAboutToBeInserted(const QModelIndex& source_parent, int start, int end); + void RowsInserted(const QModelIndex& source_parent, int start, int end); + void RowsAboutToBeRemoved(const QModelIndex& source_parent, int start, int end); + void RowsRemoved(const QModelIndex& source_parent, int start, int end); + + private: + QModelIndex GetActualSourceParent(const QModelIndex& source_parent, + const QAbstractItemModel* model) const; + + struct Mapping { + Mapping(const QModelIndex& _source_index) : source_index(_source_index) {} + QModelIndex source_index; + }; + + struct tag_by_source {}; + struct tag_by_pointer {}; + typedef multi_index_container< + Mapping*, + indexed_by< + hashed_unique, + member >, + ordered_unique, + identity > + > + > MappingContainer; + + MappingContainer mappings_; + QMap merge_points_; +}; + +#endif // MERGEDPROXYMODEL_H diff --git a/src/radiomodel.cpp b/src/radiomodel.cpp index de71278fc..8a2e6fcb0 100644 --- a/src/radiomodel.cpp +++ b/src/radiomodel.cpp @@ -21,18 +21,22 @@ #include "radiomimedata.h" #include "savedradio.h" #include "magnatuneservice.h" +#include "mergedproxymodel.h" #include #include QMap RadioModel::sServices; -RadioModel::RadioModel(QObject* parent) - : SimpleTreeModel(new RadioItem(this), parent) +RadioModel::RadioModel(Database* db, QObject* parent) + : SimpleTreeModel(new RadioItem(this), parent), + db_(db), + merged_model_(new MergedProxyModel(this)) { Q_ASSERT(sServices.isEmpty()); root_->lazy_loaded = true; + merged_model_->setSourceModel(this); AddService(new LastFMService(this)); AddService(new SomaFMService(this)); diff --git a/src/radiomodel.h b/src/radiomodel.h index 0f1913fc2..c3069d389 100644 --- a/src/radiomodel.h +++ b/src/radiomodel.h @@ -24,12 +24,14 @@ class RadioService; class LastFMService; class Song; +class MergedProxyModel; +class Database; class RadioModel : public SimpleTreeModel { Q_OBJECT public: - RadioModel(QObject* parent = 0); + RadioModel(Database* db, QObject* parent = 0); enum { Role_Type = Qt::UserRole + 1, @@ -52,6 +54,9 @@ class RadioModel : public SimpleTreeModel { void ShowContextMenu(RadioItem* item, const QPoint& global_pos); void ReloadSettings(); + Database* db() const { return db_; } + MergedProxyModel* merged_model() const { return merged_model_; } + signals: void TaskStarted(MultiLoadingIndicator::TaskType); void TaskFinished(MultiLoadingIndicator::TaskType); @@ -71,6 +76,8 @@ class RadioModel : public SimpleTreeModel { private: static QMap sServices; + Database* db_; + MergedProxyModel* merged_model_; }; #endif // RADIOMODEL_H diff --git a/src/radioservice.cpp b/src/radioservice.cpp index eea130f73..274bff3e3 100644 --- a/src/radioservice.cpp +++ b/src/radioservice.cpp @@ -15,9 +15,11 @@ */ #include "radioservice.h" +#include "radiomodel.h" -RadioService::RadioService(const QString& name, QObject *parent) - : QObject(parent), +RadioService::RadioService(const QString& name, RadioModel* model) + : QObject(model), + model_(model), name_(name) { } diff --git a/src/radioservice.h b/src/radioservice.h index a06a325df..e727c7956 100644 --- a/src/radioservice.h +++ b/src/radioservice.h @@ -25,15 +25,17 @@ #include "multiloadingindicator.h" class Song; +class RadioModel; class RadioService : public QObject { Q_OBJECT public: - RadioService(const QString& name, QObject* parent = 0); + RadioService(const QString& name, RadioModel* model); virtual ~RadioService() {} QString name() const { return name_; } + RadioModel* model() const { return model_; } virtual RadioItem* CreateRootItem(RadioItem* parent) = 0; virtual void LazyPopulate(RadioItem* item) = 0; @@ -67,6 +69,7 @@ class RadioService : public QObject { void AddItemToPlaylist(RadioItem* item); private: + RadioModel* model_; QString name_; }; diff --git a/src/radioview.cpp b/src/radioview.cpp index fc5a5ad1d..9002204ce 100644 --- a/src/radioview.cpp +++ b/src/radioview.cpp @@ -16,6 +16,7 @@ #include "radioview.h" #include "radiomodel.h" +#include "mergedproxymodel.h" #include @@ -29,6 +30,10 @@ void RadioView::contextMenuEvent(QContextMenuEvent* e) { if (!index.isValid()) return; - RadioModel* radio_model = static_cast(model()); - radio_model->ShowContextMenu(radio_model->IndexToItem(index), e->globalPos()); + MergedProxyModel* merged_model = static_cast(model()); + RadioModel* radio_model = static_cast(merged_model->sourceModel()); + + radio_model->ShowContextMenu( + radio_model->IndexToItem(merged_model->mapToSource(index)), + e->globalPos()); } diff --git a/src/savedradio.cpp b/src/savedradio.cpp index f5b806ea6..14865daf3 100644 --- a/src/savedradio.cpp +++ b/src/savedradio.cpp @@ -22,7 +22,7 @@ const char* SavedRadio::kServiceName = "SavedRadio"; const char* SavedRadio::kSettingsGroup = "SavedRadio"; -SavedRadio::SavedRadio(QObject* parent) +SavedRadio::SavedRadio(RadioModel* parent) : RadioService(kServiceName, parent), root_(NULL), context_menu_(new QMenu) diff --git a/src/savedradio.h b/src/savedradio.h index 1b6a987d9..d9ba9f417 100644 --- a/src/savedradio.h +++ b/src/savedradio.h @@ -25,7 +25,7 @@ class SavedRadio : public RadioService { Q_OBJECT public: - SavedRadio(QObject* parent = 0); + SavedRadio(RadioModel* parent); ~SavedRadio(); enum ItemType { diff --git a/src/simpletreeitem.h b/src/simpletreeitem.h index 96ee0cc55..c063022cb 100644 --- a/src/simpletreeitem.h +++ b/src/simpletreeitem.h @@ -50,6 +50,7 @@ class SimpleTreeItem { T* parent; QList children; + QAbstractItemModel* child_model; SimpleTreeModel* model; }; @@ -60,6 +61,7 @@ SimpleTreeItem::SimpleTreeItem(int _type, SimpleTreeModel* _model) row(0), lazy_loaded(true), parent(NULL), + child_model(NULL), model(_model) { } @@ -70,6 +72,7 @@ SimpleTreeItem::SimpleTreeItem(int _type, const QString& _key, T* _parent) key(_key), lazy_loaded(false), parent(_parent), + child_model(NULL), model(_parent ? _parent->model : NULL) { if (parent) { @@ -83,6 +86,7 @@ SimpleTreeItem::SimpleTreeItem(int _type, T* _parent) : type(_type), lazy_loaded(false), parent(_parent), + child_model(NULL), model(_parent ? _parent->model : NULL) { if (parent) { diff --git a/src/somafmservice.cpp b/src/somafmservice.cpp index 7f6a51b31..7360d4eb5 100644 --- a/src/somafmservice.cpp +++ b/src/somafmservice.cpp @@ -31,7 +31,7 @@ const char* SomaFMService::kServiceName = "SomaFM"; const char* SomaFMService::kChannelListUrl = "http://somafm.com/channels.xml"; const char* SomaFMService::kHomepage = "http://somafm.com"; -SomaFMService::SomaFMService(QObject* parent) +SomaFMService::SomaFMService(RadioModel* parent) : RadioService(kServiceName, parent), root_(NULL), context_menu_(new QMenu), diff --git a/src/somafmservice.h b/src/somafmservice.h index 25c20899e..a2e74c4e3 100644 --- a/src/somafmservice.h +++ b/src/somafmservice.h @@ -28,7 +28,7 @@ class SomaFMService : public RadioService { Q_OBJECT public: - SomaFMService(QObject* parent = 0); + SomaFMService(RadioModel* parent); ~SomaFMService(); enum ItemType { diff --git a/tests/CMakeLists.txt b/tests/CMakeLists.txt index 020fcf303..1ec9d3716 100644 --- a/tests/CMakeLists.txt +++ b/tests/CMakeLists.txt @@ -102,3 +102,4 @@ add_test_file(translations_test.cpp false) add_test_file(playlist_test.cpp true) add_test_file(scopedtransaction_test.cpp false) add_test_file(fileformats_test.cpp false) +add_test_file(mergedproxymodel_test.cpp false) diff --git a/tests/mergedproxymodel_test.cpp b/tests/mergedproxymodel_test.cpp new file mode 100644 index 000000000..6be23e812 --- /dev/null +++ b/tests/mergedproxymodel_test.cpp @@ -0,0 +1,84 @@ +/* This file is part of Clementine. + + 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 "gtest/gtest.h" +#include "test_utils.h" +#include "mergedproxymodel.h" + +#include + +class MergedProxyModelTest : public ::testing::Test { + protected: + void SetUp() { + merged_.setSourceModel(&source_); + } + + QStandardItemModel source_; + MergedProxyModel merged_; +}; + +TEST_F(MergedProxyModelTest, Flat) { + source_.appendRow(new QStandardItem("one")); + source_.appendRow(new QStandardItem("two")); + + ASSERT_EQ(2, merged_.rowCount(QModelIndex())); + QModelIndex one_i = merged_.index(0, 0, QModelIndex()); + QModelIndex two_i = merged_.index(1, 0, QModelIndex()); + + EXPECT_EQ("one", one_i.data().toString()); + EXPECT_EQ("two", two_i.data().toString()); + EXPECT_FALSE(merged_.parent(one_i).isValid()); + EXPECT_FALSE(merged_.hasChildren(one_i)); +} + +TEST_F(MergedProxyModelTest, Tree) { + QStandardItem* one = new QStandardItem("one"); + QStandardItem* two = new QStandardItem("two"); + source_.appendRow(one); + one->appendRow(two); + + ASSERT_EQ(1, merged_.rowCount(QModelIndex())); + QModelIndex one_i = merged_.index(0, 0, QModelIndex()); + + ASSERT_EQ(1, merged_.rowCount(one_i)); + QModelIndex two_i = merged_.index(0, 0, one_i); + + EXPECT_EQ("one", one_i.data().toString()); + EXPECT_EQ("two", two_i.data().toString()); + EXPECT_EQ("one", two_i.parent().data().toString()); +} + +TEST_F(MergedProxyModelTest, Merged) { + source_.appendRow(new QStandardItem("one")); + + QStandardItemModel submodel; + submodel.appendRow(new QStandardItem("two")); + + merged_.AddModel(source_.index(0, 0, QModelIndex()), &submodel); + + ASSERT_EQ(1, merged_.rowCount(QModelIndex())); + QModelIndex one_i = merged_.index(0, 0, QModelIndex()); + + EXPECT_EQ("one", merged_.data(one_i).toString()); + EXPECT_TRUE(merged_.hasChildren(one_i)); + + ASSERT_EQ(1, merged_.rowCount(one_i)); + QModelIndex two_i = merged_.index(0, 0, one_i); + + EXPECT_EQ("two", merged_.data(two_i).toString()); + EXPECT_EQ(0, merged_.rowCount(two_i)); + EXPECT_FALSE(merged_.hasChildren(two_i)); +}