mirror of
https://github.com/clementine-player/Clementine
synced 2024-12-18 12:28:31 +01:00
Add a MergedProxyModel that lets us merge two models into one...
This commit is contained in:
parent
1b00aaa8b3
commit
6b8d6c93f9
@ -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
|
||||
|
@ -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),
|
||||
|
@ -47,7 +47,7 @@ class LastFMService : public RadioService {
|
||||
Q_OBJECT
|
||||
|
||||
public:
|
||||
LastFMService(QObject* parent = 0);
|
||||
LastFMService(RadioModel* parent);
|
||||
~LastFMService();
|
||||
|
||||
static const char* kServiceName;
|
||||
|
@ -16,6 +16,10 @@
|
||||
|
||||
#include "magnatuneservice.h"
|
||||
#include "song.h"
|
||||
#include "radiomodel.h"
|
||||
#include "mergedproxymodel.h"
|
||||
#include "librarymodel.h"
|
||||
#include "librarybackend.h"
|
||||
|
||||
#include <QNetworkAccessManager>
|
||||
#include <QNetworkRequest>
|
||||
@ -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_;
|
||||
}
|
||||
|
||||
|
@ -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_;
|
||||
};
|
||||
|
@ -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();
|
||||
|
||||
|
227
src/mergedproxymodel.cpp
Normal file
227
src/mergedproxymodel.cpp
Normal file
@ -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 <http://www.gnu.org/licenses/>.
|
||||
*/
|
||||
|
||||
#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<tag_by_pointer>::type::iterator begin =
|
||||
mappings_.get<tag_by_pointer>().begin();
|
||||
MappingContainer::index<tag_by_pointer>::type::iterator end =
|
||||
mappings_.get<tag_by_pointer>().end();
|
||||
qDeleteAll(begin, end);
|
||||
|
||||
// Clear the containers
|
||||
mappings_.clear();
|
||||
merge_points_.clear();
|
||||
|
||||
// Reset the proxy
|
||||
reset();
|
||||
}
|
||||
|
||||
void MergedProxyModel::SubModelReset() {
|
||||
const QAbstractItemModel* submodel = static_cast<const QAbstractItemModel*>(sender());
|
||||
|
||||
// Delete all the mappings that reference the submodel
|
||||
MappingContainer::index<tag_by_pointer>::type::iterator it =
|
||||
mappings_.get<tag_by_pointer>().begin();
|
||||
MappingContainer::index<tag_by_pointer>::type::iterator end =
|
||||
mappings_.get<tag_by_pointer>().end();
|
||||
|
||||
while (it != end) {
|
||||
if ((*it)->source_index.model() == submodel) {
|
||||
delete *it;
|
||||
it = mappings_.get<tag_by_pointer>().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<const QAbstractItemModel*>(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<const QAbstractItemModel*>(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<Mapping*>(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<tag_by_source>::type::iterator it =
|
||||
mappings_.get<tag_by_source>().find(source_index);
|
||||
Mapping* mapping;
|
||||
if (it != mappings_.get<tag_by_source>().end()) {
|
||||
mapping = *it;
|
||||
} else {
|
||||
mapping = new Mapping(source_index);
|
||||
const_cast<MergedProxyModel*>(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);
|
||||
}
|
92
src/mergedproxymodel.h
Normal file
92
src/mergedproxymodel.h
Normal file
@ -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 <http://www.gnu.org/licenses/>.
|
||||
*/
|
||||
|
||||
#ifndef MERGEDPROXYMODEL_H
|
||||
#define MERGEDPROXYMODEL_H
|
||||
|
||||
#include <QAbstractProxyModel>
|
||||
|
||||
#include <boost/multi_index_container.hpp>
|
||||
#include <boost/multi_index/member.hpp>
|
||||
#include <boost/multi_index/ordered_index.hpp>
|
||||
#include <boost/multi_index/hashed_index.hpp>
|
||||
|
||||
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<tag<tag_by_source>,
|
||||
member<Mapping, QModelIndex, &Mapping::source_index> >,
|
||||
ordered_unique<tag<tag_by_pointer>,
|
||||
identity<Mapping*> >
|
||||
>
|
||||
> MappingContainer;
|
||||
|
||||
MappingContainer mappings_;
|
||||
QMap<const QAbstractItemModel*, QModelIndex> merge_points_;
|
||||
};
|
||||
|
||||
#endif // MERGEDPROXYMODEL_H
|
@ -21,18 +21,22 @@
|
||||
#include "radiomimedata.h"
|
||||
#include "savedradio.h"
|
||||
#include "magnatuneservice.h"
|
||||
#include "mergedproxymodel.h"
|
||||
|
||||
#include <QMimeData>
|
||||
#include <QtDebug>
|
||||
|
||||
QMap<QString, RadioService*> RadioModel::sServices;
|
||||
|
||||
RadioModel::RadioModel(QObject* parent)
|
||||
: SimpleTreeModel<RadioItem>(new RadioItem(this), parent)
|
||||
RadioModel::RadioModel(Database* db, QObject* parent)
|
||||
: SimpleTreeModel<RadioItem>(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));
|
||||
|
@ -24,12 +24,14 @@
|
||||
class RadioService;
|
||||
class LastFMService;
|
||||
class Song;
|
||||
class MergedProxyModel;
|
||||
class Database;
|
||||
|
||||
class RadioModel : public SimpleTreeModel<RadioItem> {
|
||||
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<RadioItem> {
|
||||
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<RadioItem> {
|
||||
|
||||
private:
|
||||
static QMap<QString, RadioService*> sServices;
|
||||
Database* db_;
|
||||
MergedProxyModel* merged_model_;
|
||||
};
|
||||
|
||||
#endif // RADIOMODEL_H
|
||||
|
@ -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)
|
||||
{
|
||||
}
|
||||
|
@ -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_;
|
||||
};
|
||||
|
||||
|
@ -16,6 +16,7 @@
|
||||
|
||||
#include "radioview.h"
|
||||
#include "radiomodel.h"
|
||||
#include "mergedproxymodel.h"
|
||||
|
||||
#include <QContextMenuEvent>
|
||||
|
||||
@ -29,6 +30,10 @@ void RadioView::contextMenuEvent(QContextMenuEvent* e) {
|
||||
if (!index.isValid())
|
||||
return;
|
||||
|
||||
RadioModel* radio_model = static_cast<RadioModel*>(model());
|
||||
radio_model->ShowContextMenu(radio_model->IndexToItem(index), e->globalPos());
|
||||
MergedProxyModel* merged_model = static_cast<MergedProxyModel*>(model());
|
||||
RadioModel* radio_model = static_cast<RadioModel*>(merged_model->sourceModel());
|
||||
|
||||
radio_model->ShowContextMenu(
|
||||
radio_model->IndexToItem(merged_model->mapToSource(index)),
|
||||
e->globalPos());
|
||||
}
|
||||
|
@ -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)
|
||||
|
@ -25,7 +25,7 @@ class SavedRadio : public RadioService {
|
||||
Q_OBJECT
|
||||
|
||||
public:
|
||||
SavedRadio(QObject* parent = 0);
|
||||
SavedRadio(RadioModel* parent);
|
||||
~SavedRadio();
|
||||
|
||||
enum ItemType {
|
||||
|
@ -50,6 +50,7 @@ class SimpleTreeItem {
|
||||
|
||||
T* parent;
|
||||
QList<T*> children;
|
||||
QAbstractItemModel* child_model;
|
||||
|
||||
SimpleTreeModel<T>* model;
|
||||
};
|
||||
@ -60,6 +61,7 @@ SimpleTreeItem<T>::SimpleTreeItem(int _type, SimpleTreeModel<T>* _model)
|
||||
row(0),
|
||||
lazy_loaded(true),
|
||||
parent(NULL),
|
||||
child_model(NULL),
|
||||
model(_model)
|
||||
{
|
||||
}
|
||||
@ -70,6 +72,7 @@ SimpleTreeItem<T>::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<T>::SimpleTreeItem(int _type, T* _parent)
|
||||
: type(_type),
|
||||
lazy_loaded(false),
|
||||
parent(_parent),
|
||||
child_model(NULL),
|
||||
model(_parent ? _parent->model : NULL)
|
||||
{
|
||||
if (parent) {
|
||||
|
@ -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),
|
||||
|
@ -28,7 +28,7 @@ class SomaFMService : public RadioService {
|
||||
Q_OBJECT
|
||||
|
||||
public:
|
||||
SomaFMService(QObject* parent = 0);
|
||||
SomaFMService(RadioModel* parent);
|
||||
~SomaFMService();
|
||||
|
||||
enum ItemType {
|
||||
|
@ -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)
|
||||
|
84
tests/mergedproxymodel_test.cpp
Normal file
84
tests/mergedproxymodel_test.cpp
Normal file
@ -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 <http://www.gnu.org/licenses/>.
|
||||
*/
|
||||
|
||||
#include "gtest/gtest.h"
|
||||
#include "test_utils.h"
|
||||
#include "mergedproxymodel.h"
|
||||
|
||||
#include <QStandardItemModel>
|
||||
|
||||
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));
|
||||
}
|
Loading…
Reference in New Issue
Block a user