2018-02-27 18:06:05 +01:00
|
|
|
/*
|
|
|
|
* Strawberry Music Player
|
|
|
|
* This file was part of Clementine.
|
|
|
|
* Copyright 2010, David Sansome <me@davidsansome.com>
|
2021-03-20 21:14:47 +01:00
|
|
|
* Copyright 2019-2021, Jonas Kvinge <jonas@jkvinge.net>
|
2018-02-27 18:06:05 +01:00
|
|
|
*
|
|
|
|
* Strawberry 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.
|
|
|
|
*
|
|
|
|
* Strawberry 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 Strawberry. If not, see <http://www.gnu.org/licenses/>.
|
2018-08-09 18:39:44 +02:00
|
|
|
*
|
2018-02-27 18:06:05 +01:00
|
|
|
*/
|
|
|
|
|
|
|
|
#include "config.h"
|
|
|
|
|
2018-05-01 00:41:33 +02:00
|
|
|
#include <functional>
|
2018-02-27 18:06:05 +01:00
|
|
|
#include <limits>
|
|
|
|
|
2018-05-01 00:41:33 +02:00
|
|
|
#include <QObject>
|
|
|
|
#include <QMimeData>
|
2020-02-08 03:40:30 +01:00
|
|
|
#include <QList>
|
2018-02-27 18:06:05 +01:00
|
|
|
#include <QStringList>
|
2018-05-01 00:41:33 +02:00
|
|
|
#include <QtAlgorithms>
|
|
|
|
#include <QAbstractItemModel>
|
|
|
|
#include <QAbstractProxyModel>
|
|
|
|
|
|
|
|
#include "mergedproxymodel.h"
|
2018-02-27 18:06:05 +01:00
|
|
|
|
2018-05-01 00:41:33 +02:00
|
|
|
#include <boost/multi_index/detail/bidir_node_iterator.hpp>
|
|
|
|
#include <boost/multi_index/detail/hash_index_iterator.hpp>
|
2018-02-27 18:06:05 +01:00
|
|
|
#include <boost/multi_index/hashed_index.hpp>
|
2018-05-01 00:41:33 +02:00
|
|
|
#include <boost/multi_index/identity.hpp>
|
|
|
|
#include <boost/multi_index/indexed_by.hpp>
|
|
|
|
#include <boost/multi_index/member.hpp>
|
2018-02-27 18:06:05 +01:00
|
|
|
#include <boost/multi_index/ordered_index.hpp>
|
2018-05-01 00:41:33 +02:00
|
|
|
#include <boost/multi_index/tag.hpp>
|
|
|
|
#include <boost/multi_index_container.hpp>
|
|
|
|
#include <boost/operators.hpp>
|
2018-02-27 18:06:05 +01:00
|
|
|
|
|
|
|
using boost::multi_index::hashed_unique;
|
|
|
|
using boost::multi_index::identity;
|
|
|
|
using boost::multi_index::indexed_by;
|
|
|
|
using boost::multi_index::member;
|
|
|
|
using boost::multi_index::multi_index_container;
|
|
|
|
using boost::multi_index::ordered_unique;
|
|
|
|
using boost::multi_index::tag;
|
|
|
|
|
2021-06-20 19:04:08 +02:00
|
|
|
size_t hash_value(const QModelIndex &idx) { return qHash(idx); }
|
2018-02-27 18:06:05 +01:00
|
|
|
|
|
|
|
namespace {
|
|
|
|
|
|
|
|
struct Mapping {
|
2018-05-01 00:41:33 +02:00
|
|
|
explicit Mapping(const QModelIndex &_source_index) : source_index(_source_index) {}
|
2018-02-27 18:06:05 +01:00
|
|
|
|
|
|
|
QModelIndex source_index;
|
|
|
|
};
|
|
|
|
|
|
|
|
struct tag_by_source {};
|
|
|
|
struct tag_by_pointer {};
|
|
|
|
|
|
|
|
} // namespace
|
|
|
|
|
|
|
|
class MergedProxyModelPrivate {
|
|
|
|
private:
|
|
|
|
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;
|
|
|
|
|
|
|
|
public:
|
|
|
|
MappingContainer mappings_;
|
|
|
|
};
|
|
|
|
|
2018-05-01 00:41:33 +02:00
|
|
|
MergedProxyModel::MergedProxyModel(QObject *parent)
|
2018-02-27 18:06:05 +01:00
|
|
|
: QAbstractProxyModel(parent),
|
|
|
|
resetting_model_(nullptr),
|
|
|
|
p_(new MergedProxyModelPrivate) {}
|
|
|
|
|
|
|
|
MergedProxyModel::~MergedProxyModel() { DeleteAllMappings(); }
|
|
|
|
|
|
|
|
void MergedProxyModel::DeleteAllMappings() {
|
2018-05-01 00:41:33 +02:00
|
|
|
const auto &begin = p_->mappings_.get<tag_by_pointer>().begin();
|
|
|
|
const auto &end = p_->mappings_.get<tag_by_pointer>().end();
|
2018-02-27 18:06:05 +01:00
|
|
|
qDeleteAll(begin, end);
|
|
|
|
}
|
|
|
|
|
2018-05-01 00:41:33 +02:00
|
|
|
void MergedProxyModel::AddSubModel(const QModelIndex &source_parent, QAbstractItemModel *submodel) {
|
2018-02-27 18:06:05 +01:00
|
|
|
|
2021-01-26 16:48:04 +01:00
|
|
|
QObject::connect(submodel, &QAbstractItemModel::modelAboutToBeReset, this, &MergedProxyModel::SubModelAboutToBeReset);
|
|
|
|
QObject::connect(submodel, &QAbstractItemModel::modelReset, this, &MergedProxyModel::SubModelResetSlot);
|
|
|
|
QObject::connect(submodel, &QAbstractItemModel::rowsAboutToBeInserted, this, &MergedProxyModel::RowsAboutToBeInserted);
|
|
|
|
QObject::connect(submodel, &QAbstractItemModel::rowsAboutToBeRemoved, this, &MergedProxyModel::RowsAboutToBeRemoved);
|
|
|
|
QObject::connect(submodel, &QAbstractItemModel::rowsInserted, this, &MergedProxyModel::RowsInserted);
|
|
|
|
QObject::connect(submodel, &QAbstractItemModel::rowsRemoved, this, &MergedProxyModel::RowsRemoved);
|
|
|
|
QObject::connect(submodel, &QAbstractItemModel::dataChanged, this, &MergedProxyModel::DataChanged);
|
2018-02-27 18:06:05 +01:00
|
|
|
|
|
|
|
QModelIndex proxy_parent = mapFromSource(source_parent);
|
|
|
|
const int rows = submodel->rowCount();
|
|
|
|
|
|
|
|
if (rows) beginInsertRows(proxy_parent, 0, rows - 1);
|
|
|
|
|
|
|
|
merge_points_.insert(submodel, source_parent);
|
|
|
|
|
|
|
|
if (rows) endInsertRows();
|
|
|
|
}
|
|
|
|
|
2018-05-01 00:41:33 +02:00
|
|
|
void MergedProxyModel::RemoveSubModel(const QModelIndex &source_parent) {
|
|
|
|
|
2018-02-27 18:06:05 +01:00
|
|
|
// Find the submodel that the parent corresponded to
|
2018-05-01 00:41:33 +02:00
|
|
|
QAbstractItemModel *submodel = merge_points_.key(source_parent);
|
2018-02-27 18:06:05 +01:00
|
|
|
merge_points_.remove(submodel);
|
|
|
|
|
2018-05-01 00:41:33 +02:00
|
|
|
// The submodel might have been deleted already so we must be careful not to dereference it.
|
2018-02-27 18:06:05 +01:00
|
|
|
|
|
|
|
// Remove all the children of the item that got deleted
|
|
|
|
QModelIndex proxy_parent = mapFromSource(source_parent);
|
|
|
|
|
|
|
|
// We can't know how many children it had, since we can't dereference it
|
|
|
|
resetting_model_ = submodel;
|
|
|
|
beginRemoveRows(proxy_parent, 0, std::numeric_limits<int>::max() - 1);
|
|
|
|
endRemoveRows();
|
|
|
|
resetting_model_ = nullptr;
|
|
|
|
|
|
|
|
// Delete all the mappings that reference the submodel
|
|
|
|
auto it = p_->mappings_.get<tag_by_pointer>().begin();
|
|
|
|
auto end = p_->mappings_.get<tag_by_pointer>().end();
|
|
|
|
while (it != end) {
|
|
|
|
if ((*it)->source_index.model() == submodel) {
|
|
|
|
delete *it;
|
|
|
|
it = p_->mappings_.get<tag_by_pointer>().erase(it);
|
2018-05-01 00:41:33 +02:00
|
|
|
}
|
|
|
|
else {
|
2018-02-27 18:06:05 +01:00
|
|
|
++it;
|
|
|
|
}
|
|
|
|
}
|
2018-05-01 00:41:33 +02:00
|
|
|
|
2018-02-27 18:06:05 +01:00
|
|
|
}
|
|
|
|
|
2018-05-01 00:41:33 +02:00
|
|
|
void MergedProxyModel::setSourceModel(QAbstractItemModel *source_model) {
|
2018-02-27 18:06:05 +01:00
|
|
|
|
|
|
|
if (sourceModel()) {
|
2021-01-26 16:48:04 +01:00
|
|
|
QObject::disconnect(sourceModel(), &QAbstractItemModel::modelReset, this, &MergedProxyModel::SourceModelReset);
|
|
|
|
QObject::disconnect(sourceModel(), &QAbstractItemModel::rowsAboutToBeInserted, this, &MergedProxyModel::RowsAboutToBeInserted);
|
|
|
|
QObject::disconnect(sourceModel(), &QAbstractItemModel::rowsAboutToBeRemoved, this, &MergedProxyModel::RowsAboutToBeRemoved);
|
|
|
|
QObject::disconnect(sourceModel(), &QAbstractItemModel::rowsInserted, this, &MergedProxyModel::RowsInserted);
|
|
|
|
QObject::disconnect(sourceModel(), &QAbstractItemModel::rowsRemoved, this, &MergedProxyModel::RowsRemoved);
|
|
|
|
QObject::disconnect(sourceModel(), &QAbstractItemModel::dataChanged, this, &MergedProxyModel::DataChanged);
|
|
|
|
QObject::disconnect(sourceModel(), &QAbstractItemModel::layoutAboutToBeChanged, this, &MergedProxyModel::LayoutAboutToBeChanged);
|
|
|
|
QObject::disconnect(sourceModel(), &QAbstractItemModel::layoutChanged, this, &MergedProxyModel::LayoutChanged);
|
2018-02-27 18:06:05 +01:00
|
|
|
}
|
|
|
|
|
|
|
|
QAbstractProxyModel::setSourceModel(source_model);
|
|
|
|
|
2021-01-26 16:48:04 +01:00
|
|
|
QObject::connect(sourceModel(), &QAbstractItemModel::modelReset, this, &MergedProxyModel::SourceModelReset);
|
|
|
|
QObject::connect(sourceModel(), &QAbstractItemModel::rowsAboutToBeInserted, this, &MergedProxyModel::RowsAboutToBeInserted);
|
|
|
|
QObject::connect(sourceModel(), &QAbstractItemModel::rowsAboutToBeRemoved, this, &MergedProxyModel::RowsAboutToBeRemoved);
|
|
|
|
QObject::connect(sourceModel(), &QAbstractItemModel::rowsInserted, this, &MergedProxyModel::RowsInserted);
|
|
|
|
QObject::connect(sourceModel(), &QAbstractItemModel::rowsRemoved, this, &MergedProxyModel::RowsRemoved);
|
|
|
|
QObject::connect(sourceModel(), &QAbstractItemModel::dataChanged, this, &MergedProxyModel::DataChanged);
|
|
|
|
QObject::connect(sourceModel(), &QAbstractItemModel::layoutAboutToBeChanged, this, &MergedProxyModel::LayoutAboutToBeChanged);
|
|
|
|
QObject::connect(sourceModel(), &QAbstractItemModel::layoutChanged, this, &MergedProxyModel::LayoutChanged);
|
2018-02-27 18:06:05 +01:00
|
|
|
|
|
|
|
}
|
|
|
|
|
|
|
|
void MergedProxyModel::SourceModelReset() {
|
|
|
|
|
|
|
|
// Delete all mappings
|
|
|
|
DeleteAllMappings();
|
|
|
|
|
|
|
|
// Reset the proxy
|
|
|
|
beginResetModel();
|
|
|
|
|
|
|
|
// Clear the containers
|
|
|
|
p_->mappings_.clear();
|
|
|
|
merge_points_.clear();
|
|
|
|
|
|
|
|
endResetModel();
|
|
|
|
|
|
|
|
}
|
|
|
|
|
2020-12-24 12:33:05 +01:00
|
|
|
void MergedProxyModel::SubModelAboutToBeReset() {
|
2018-02-27 18:06:05 +01:00
|
|
|
|
2020-07-17 01:32:07 +02:00
|
|
|
QAbstractItemModel *submodel = qobject_cast<QAbstractItemModel*>(sender());
|
2018-02-27 18:06:05 +01:00
|
|
|
|
|
|
|
QModelIndex source_parent = merge_points_.value(submodel);
|
|
|
|
QModelIndex proxy_parent = mapFromSource(source_parent);
|
|
|
|
|
|
|
|
resetting_model_ = submodel;
|
2020-12-24 12:33:05 +01:00
|
|
|
beginRemoveRows(proxy_parent, 0, submodel->rowCount());
|
2018-02-27 18:06:05 +01:00
|
|
|
endRemoveRows();
|
|
|
|
resetting_model_ = nullptr;
|
|
|
|
|
|
|
|
// Delete all the mappings that reference the submodel
|
|
|
|
auto it = p_->mappings_.get<tag_by_pointer>().begin();
|
|
|
|
auto end = p_->mappings_.get<tag_by_pointer>().end();
|
|
|
|
while (it != end) {
|
|
|
|
if ((*it)->source_index.model() == submodel) {
|
|
|
|
delete *it;
|
|
|
|
it = p_->mappings_.get<tag_by_pointer>().erase(it);
|
2018-05-01 00:41:33 +02:00
|
|
|
}
|
|
|
|
else {
|
2018-02-27 18:06:05 +01:00
|
|
|
++it;
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
2020-12-24 12:33:05 +01:00
|
|
|
}
|
|
|
|
|
2021-01-26 16:48:04 +01:00
|
|
|
void MergedProxyModel::SubModelResetSlot() {
|
2020-12-24 12:33:05 +01:00
|
|
|
|
|
|
|
QAbstractItemModel *submodel = static_cast<QAbstractItemModel*>(sender());
|
|
|
|
|
|
|
|
QModelIndex source_parent = merge_points_.value(submodel);
|
|
|
|
QModelIndex proxy_parent = mapFromSource(source_parent);
|
|
|
|
|
2018-02-27 18:06:05 +01:00
|
|
|
// "Insert" items from the newly reset submodel
|
|
|
|
int count = submodel->rowCount();
|
|
|
|
if (count) {
|
|
|
|
beginInsertRows(proxy_parent, 0, count - 1);
|
|
|
|
endInsertRows();
|
|
|
|
}
|
|
|
|
|
|
|
|
emit SubModelReset(proxy_parent, submodel);
|
|
|
|
|
|
|
|
}
|
|
|
|
|
2018-05-01 00:41:33 +02:00
|
|
|
QModelIndex MergedProxyModel::GetActualSourceParent(const QModelIndex &source_parent, QAbstractItemModel *model) const {
|
2018-02-27 18:06:05 +01:00
|
|
|
|
|
|
|
if (!source_parent.isValid() && model != sourceModel())
|
|
|
|
return merge_points_.value(model);
|
|
|
|
return source_parent;
|
|
|
|
|
|
|
|
}
|
|
|
|
|
2018-05-01 00:41:33 +02:00
|
|
|
void MergedProxyModel::RowsAboutToBeInserted(const QModelIndex &source_parent, int start, int end) {
|
2020-07-17 01:32:07 +02:00
|
|
|
beginInsertRows(mapFromSource(GetActualSourceParent(source_parent, qobject_cast<QAbstractItemModel*>(sender()))), start, end);
|
2018-02-27 18:06:05 +01:00
|
|
|
}
|
|
|
|
|
|
|
|
void MergedProxyModel::RowsInserted(const QModelIndex&, int, int) {
|
|
|
|
endInsertRows();
|
|
|
|
}
|
|
|
|
|
2018-05-01 00:41:33 +02:00
|
|
|
void MergedProxyModel::RowsAboutToBeRemoved(const QModelIndex &source_parent, int start, int end) {
|
2020-07-17 01:32:07 +02:00
|
|
|
beginRemoveRows(mapFromSource(GetActualSourceParent(source_parent, qobject_cast<QAbstractItemModel*>(sender()))), start, end);
|
2018-02-27 18:06:05 +01:00
|
|
|
}
|
|
|
|
|
|
|
|
void MergedProxyModel::RowsRemoved(const QModelIndex&, int, int) {
|
|
|
|
endRemoveRows();
|
|
|
|
}
|
|
|
|
|
2018-05-01 00:41:33 +02:00
|
|
|
QModelIndex MergedProxyModel::mapToSource(const QModelIndex &proxy_index) const {
|
2018-02-27 18:06:05 +01:00
|
|
|
|
|
|
|
if (!proxy_index.isValid()) return QModelIndex();
|
|
|
|
|
2018-05-01 00:41:33 +02:00
|
|
|
Mapping *mapping = static_cast<Mapping*>(proxy_index.internalPointer());
|
|
|
|
if (p_->mappings_.get<tag_by_pointer>().find(mapping) == p_->mappings_.get<tag_by_pointer>().end())
|
2018-02-27 18:06:05 +01:00
|
|
|
return QModelIndex();
|
|
|
|
if (mapping->source_index.model() == resetting_model_) return QModelIndex();
|
|
|
|
|
|
|
|
return mapping->source_index;
|
|
|
|
|
|
|
|
}
|
|
|
|
|
2018-05-01 00:41:33 +02:00
|
|
|
QModelIndex MergedProxyModel::mapFromSource(const QModelIndex &source_index) const {
|
2018-02-27 18:06:05 +01:00
|
|
|
|
|
|
|
if (!source_index.isValid()) return QModelIndex();
|
|
|
|
if (source_index.model() == resetting_model_) return QModelIndex();
|
|
|
|
|
|
|
|
// Add a mapping if we don't have one already
|
2018-05-01 00:41:33 +02:00
|
|
|
const auto &it = p_->mappings_.get<tag_by_source>().find(source_index);
|
2021-03-26 21:30:13 +01:00
|
|
|
Mapping *mapping = nullptr;
|
2018-02-27 18:06:05 +01:00
|
|
|
if (it != p_->mappings_.get<tag_by_source>().end()) {
|
|
|
|
mapping = *it;
|
2018-05-01 00:41:33 +02:00
|
|
|
}
|
|
|
|
else {
|
2018-02-27 18:06:05 +01:00
|
|
|
mapping = new Mapping(source_index);
|
|
|
|
const_cast<MergedProxyModel*>(this)->p_->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);
|
2018-05-01 00:41:33 +02:00
|
|
|
const QAbstractItemModel *child_model = merge_points_.key(source_parent);
|
2018-02-27 18:06:05 +01:00
|
|
|
|
|
|
|
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);
|
|
|
|
|
|
|
|
}
|
|
|
|
|
2018-05-01 00:41:33 +02:00
|
|
|
QModelIndex MergedProxyModel::parent(const QModelIndex &child) const {
|
2018-02-27 18:06:05 +01:00
|
|
|
|
|
|
|
QModelIndex source_child = mapToSource(child);
|
|
|
|
if (source_child.model() == sourceModel())
|
|
|
|
return mapFromSource(source_child.parent());
|
|
|
|
|
|
|
|
if (!IsKnownModel(source_child.model())) return QModelIndex();
|
|
|
|
|
|
|
|
if (!source_child.parent().isValid())
|
|
|
|
return mapFromSource(merge_points_.value(GetModel(source_child)));
|
|
|
|
return mapFromSource(source_child.parent());
|
|
|
|
|
|
|
|
}
|
|
|
|
|
2018-05-01 00:41:33 +02:00
|
|
|
int MergedProxyModel::rowCount(const QModelIndex &parent) const {
|
2018-02-27 18:06:05 +01:00
|
|
|
|
|
|
|
if (!parent.isValid()) return sourceModel()->rowCount(QModelIndex());
|
|
|
|
|
|
|
|
QModelIndex source_parent = mapToSource(parent);
|
|
|
|
if (!IsKnownModel(source_parent.model())) return 0;
|
|
|
|
|
2018-05-01 00:41:33 +02:00
|
|
|
const QAbstractItemModel *child_model = merge_points_.key(source_parent);
|
2018-02-27 18:06:05 +01:00
|
|
|
if (child_model) {
|
2018-05-01 00:41:33 +02:00
|
|
|
// Query the source model but disregard what it says, so it gets a chance to lazy load
|
2018-02-27 18:06:05 +01:00
|
|
|
source_parent.model()->rowCount(source_parent);
|
|
|
|
|
|
|
|
return child_model->rowCount(QModelIndex());
|
|
|
|
}
|
|
|
|
|
|
|
|
return source_parent.model()->rowCount(source_parent);
|
|
|
|
|
|
|
|
}
|
|
|
|
|
2018-05-01 00:41:33 +02:00
|
|
|
int MergedProxyModel::columnCount(const QModelIndex &parent) const {
|
2018-02-27 18:06:05 +01:00
|
|
|
|
|
|
|
if (!parent.isValid()) return sourceModel()->columnCount(QModelIndex());
|
|
|
|
|
|
|
|
QModelIndex source_parent = mapToSource(parent);
|
|
|
|
if (!IsKnownModel(source_parent.model())) return 0;
|
|
|
|
|
2018-05-01 00:41:33 +02:00
|
|
|
const QAbstractItemModel *child_model = merge_points_.key(source_parent);
|
2018-02-27 18:06:05 +01:00
|
|
|
if (child_model) return child_model->columnCount(QModelIndex());
|
|
|
|
return source_parent.model()->columnCount(source_parent);
|
|
|
|
|
|
|
|
}
|
|
|
|
|
2018-05-01 00:41:33 +02:00
|
|
|
bool MergedProxyModel::hasChildren(const QModelIndex &parent) const {
|
2018-02-27 18:06:05 +01:00
|
|
|
|
|
|
|
if (!parent.isValid()) return sourceModel()->hasChildren(QModelIndex());
|
|
|
|
|
|
|
|
QModelIndex source_parent = mapToSource(parent);
|
|
|
|
if (!IsKnownModel(source_parent.model())) return false;
|
|
|
|
|
2018-05-01 00:41:33 +02:00
|
|
|
const QAbstractItemModel *child_model = merge_points_.key(source_parent);
|
2018-02-27 18:06:05 +01:00
|
|
|
|
|
|
|
if (child_model) return child_model->hasChildren(QModelIndex()) || source_parent.model()->hasChildren(source_parent);
|
|
|
|
return source_parent.model()->hasChildren(source_parent);
|
|
|
|
|
|
|
|
}
|
|
|
|
|
2020-06-14 18:58:24 +02:00
|
|
|
QVariant MergedProxyModel::data(const QModelIndex &proxy_index, int role) const {
|
2018-02-27 18:06:05 +01:00
|
|
|
|
2020-06-14 18:58:24 +02:00
|
|
|
QModelIndex source_index = mapToSource(proxy_index);
|
2018-02-27 18:06:05 +01:00
|
|
|
if (!IsKnownModel(source_index.model())) return QVariant();
|
|
|
|
|
|
|
|
return source_index.model()->data(source_index, role);
|
|
|
|
|
|
|
|
}
|
|
|
|
|
2018-05-01 00:41:33 +02:00
|
|
|
QMap<int, QVariant> MergedProxyModel::itemData(const QModelIndex &proxy_index) const {
|
2018-02-27 18:06:05 +01:00
|
|
|
|
|
|
|
QModelIndex source_index = mapToSource(proxy_index);
|
|
|
|
|
|
|
|
if (!source_index.isValid()) return sourceModel()->itemData(QModelIndex());
|
|
|
|
return source_index.model()->itemData(source_index);
|
|
|
|
|
|
|
|
}
|
|
|
|
|
2021-01-26 16:48:04 +01:00
|
|
|
Qt::ItemFlags MergedProxyModel::flags(const QModelIndex &idx) const {
|
2018-02-27 18:06:05 +01:00
|
|
|
|
2021-01-26 16:48:04 +01:00
|
|
|
QModelIndex source_index = mapToSource(idx);
|
2018-02-27 18:06:05 +01:00
|
|
|
|
|
|
|
if (!source_index.isValid()) return sourceModel()->flags(QModelIndex());
|
|
|
|
return source_index.model()->flags(source_index);
|
|
|
|
|
|
|
|
}
|
|
|
|
|
2021-01-26 16:48:04 +01:00
|
|
|
bool MergedProxyModel::setData(const QModelIndex &idx, const QVariant &value, int role) {
|
2018-02-27 18:06:05 +01:00
|
|
|
|
2021-01-26 16:48:04 +01:00
|
|
|
QModelIndex source_index = mapToSource(idx);
|
2018-02-27 18:06:05 +01:00
|
|
|
|
|
|
|
if (!source_index.isValid())
|
2021-01-26 16:48:04 +01:00
|
|
|
return sourceModel()->setData(idx, value, role);
|
|
|
|
return GetModel(idx)->setData(idx, value, role);
|
2018-02-27 18:06:05 +01:00
|
|
|
|
|
|
|
}
|
|
|
|
|
|
|
|
QStringList MergedProxyModel::mimeTypes() const {
|
|
|
|
|
|
|
|
QStringList ret;
|
|
|
|
ret << sourceModel()->mimeTypes();
|
|
|
|
|
2021-03-21 04:47:11 +01:00
|
|
|
QList<QAbstractItemModel*> models = merge_points_.keys();
|
|
|
|
for (const QAbstractItemModel *model : models) {
|
2018-02-27 18:06:05 +01:00
|
|
|
ret << model->mimeTypes();
|
|
|
|
}
|
|
|
|
|
|
|
|
return ret;
|
|
|
|
|
|
|
|
}
|
|
|
|
|
2018-05-01 00:41:33 +02:00
|
|
|
QMimeData *MergedProxyModel::mimeData(const QModelIndexList &indexes) const {
|
2018-02-27 18:06:05 +01:00
|
|
|
|
2018-12-15 00:41:22 +01:00
|
|
|
if (indexes.isEmpty()) return nullptr;
|
2018-02-27 18:06:05 +01:00
|
|
|
|
|
|
|
// Only ask the first index's model
|
2018-05-01 00:41:33 +02:00
|
|
|
const QAbstractItemModel *model = mapToSource(indexes[0]).model();
|
2018-02-27 18:06:05 +01:00
|
|
|
if (!model) {
|
2018-12-15 00:41:22 +01:00
|
|
|
return nullptr;
|
2018-02-27 18:06:05 +01:00
|
|
|
}
|
|
|
|
|
|
|
|
// Only ask about the indexes that are actually in that model
|
|
|
|
QModelIndexList indexes_in_model;
|
|
|
|
|
2018-05-01 00:41:33 +02:00
|
|
|
for (const QModelIndex &proxy_index : indexes) {
|
2018-02-27 18:06:05 +01:00
|
|
|
QModelIndex source_index = mapToSource(proxy_index);
|
|
|
|
if (source_index.model() != model) continue;
|
|
|
|
indexes_in_model << source_index;
|
|
|
|
}
|
|
|
|
|
|
|
|
return model->mimeData(indexes_in_model);
|
|
|
|
|
|
|
|
}
|
|
|
|
|
2018-05-01 00:41:33 +02:00
|
|
|
bool MergedProxyModel::dropMimeData(const QMimeData *data, Qt::DropAction action, int row, int column, const QModelIndex &parent) {
|
2018-02-27 18:06:05 +01:00
|
|
|
|
|
|
|
if (!parent.isValid()) {
|
|
|
|
return false;
|
|
|
|
}
|
|
|
|
|
|
|
|
return sourceModel()->dropMimeData(data, action, row, column, parent);
|
|
|
|
|
|
|
|
}
|
|
|
|
|
2018-05-01 00:41:33 +02:00
|
|
|
QModelIndex MergedProxyModel::FindSourceParent(const QModelIndex &proxy_index) const {
|
2018-10-02 00:38:52 +02:00
|
|
|
|
2018-02-27 18:06:05 +01:00
|
|
|
if (!proxy_index.isValid()) return QModelIndex();
|
|
|
|
|
|
|
|
QModelIndex source_index = mapToSource(proxy_index);
|
|
|
|
if (source_index.model() == sourceModel()) return source_index;
|
|
|
|
return merge_points_.value(GetModel(source_index));
|
|
|
|
|
|
|
|
}
|
|
|
|
|
2018-05-01 00:41:33 +02:00
|
|
|
bool MergedProxyModel::canFetchMore(const QModelIndex &parent) const {
|
2018-10-02 00:38:52 +02:00
|
|
|
|
2018-02-27 18:06:05 +01:00
|
|
|
QModelIndex source_index = mapToSource(parent);
|
|
|
|
|
|
|
|
if (!source_index.isValid())
|
|
|
|
return sourceModel()->canFetchMore(QModelIndex());
|
|
|
|
return source_index.model()->canFetchMore(source_index);
|
|
|
|
|
|
|
|
}
|
|
|
|
|
2018-05-01 00:41:33 +02:00
|
|
|
void MergedProxyModel::fetchMore(const QModelIndex &parent) {
|
2018-10-02 00:38:52 +02:00
|
|
|
|
2018-02-27 18:06:05 +01:00
|
|
|
QModelIndex source_index = mapToSource(parent);
|
|
|
|
|
|
|
|
if (!source_index.isValid())
|
|
|
|
sourceModel()->fetchMore(QModelIndex());
|
|
|
|
else
|
|
|
|
GetModel(source_index)->fetchMore(source_index);
|
|
|
|
|
|
|
|
}
|
|
|
|
|
2018-05-01 00:41:33 +02:00
|
|
|
QAbstractItemModel *MergedProxyModel::GetModel(const QModelIndex &source_index) const {
|
2018-02-27 18:06:05 +01:00
|
|
|
|
2018-05-01 00:41:33 +02:00
|
|
|
// This is essentially const_cast<QAbstractItemModel*>(source_index.model()), but without the const_cast
|
|
|
|
const QAbstractItemModel *const_model = source_index.model();
|
2018-02-27 18:06:05 +01:00
|
|
|
if (const_model == sourceModel()) return sourceModel();
|
2021-03-21 04:47:11 +01:00
|
|
|
QList<QAbstractItemModel*> submodels = merge_points_.keys();
|
|
|
|
for (QAbstractItemModel *submodel : submodels) {
|
2018-02-27 18:06:05 +01:00
|
|
|
if (submodel == const_model) return submodel;
|
|
|
|
}
|
|
|
|
return nullptr;
|
|
|
|
|
|
|
|
}
|
|
|
|
|
2018-05-01 00:41:33 +02:00
|
|
|
void MergedProxyModel::DataChanged(const QModelIndex &top_left, const QModelIndex &bottom_right) {
|
2018-02-27 18:06:05 +01:00
|
|
|
emit dataChanged(mapFromSource(top_left), mapFromSource(bottom_right));
|
|
|
|
}
|
|
|
|
|
|
|
|
void MergedProxyModel::LayoutAboutToBeChanged() {
|
|
|
|
|
|
|
|
old_merge_points_.clear();
|
2021-03-21 04:47:11 +01:00
|
|
|
QList<QAbstractItemModel*> models = merge_points_.keys();
|
|
|
|
for (QAbstractItemModel *model : models) {
|
|
|
|
old_merge_points_[model] = merge_points_.value(model);
|
2018-02-27 18:06:05 +01:00
|
|
|
}
|
|
|
|
|
|
|
|
}
|
|
|
|
|
|
|
|
void MergedProxyModel::LayoutChanged() {
|
|
|
|
|
2021-03-21 04:47:11 +01:00
|
|
|
QList<QAbstractItemModel*> models = merge_points_.keys();
|
|
|
|
for (QAbstractItemModel *model : models) {
|
|
|
|
if (!old_merge_points_.contains(model)) continue;
|
2018-02-27 18:06:05 +01:00
|
|
|
|
2021-03-21 04:47:11 +01:00
|
|
|
const int old_row = old_merge_points_[model].row();
|
|
|
|
const int new_row = merge_points_[model].row();
|
2018-02-27 18:06:05 +01:00
|
|
|
|
|
|
|
if (old_row != new_row) {
|
|
|
|
beginResetModel();
|
|
|
|
endResetModel();
|
|
|
|
return;
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
}
|
|
|
|
|
2018-05-01 00:41:33 +02:00
|
|
|
bool MergedProxyModel::IsKnownModel(const QAbstractItemModel *model) const {
|
2018-02-27 18:06:05 +01:00
|
|
|
|
2018-05-01 00:41:33 +02:00
|
|
|
if (model == this || model == sourceModel() || merge_points_.contains(const_cast<QAbstractItemModel*>(model)))
|
2018-02-27 18:06:05 +01:00
|
|
|
return true;
|
|
|
|
return false;
|
|
|
|
|
|
|
|
}
|
|
|
|
|
2018-05-01 00:41:33 +02:00
|
|
|
QModelIndexList MergedProxyModel::mapFromSource(const QModelIndexList &source_indexes) const {
|
2018-02-27 18:06:05 +01:00
|
|
|
|
|
|
|
QModelIndexList ret;
|
2021-06-20 19:04:08 +02:00
|
|
|
ret.reserve(source_indexes.count());
|
2021-01-26 16:48:04 +01:00
|
|
|
for (const QModelIndex &idx : source_indexes) {
|
|
|
|
ret << mapFromSource(idx);
|
2018-02-27 18:06:05 +01:00
|
|
|
}
|
|
|
|
return ret;
|
|
|
|
|
|
|
|
}
|
|
|
|
|
2018-05-01 00:41:33 +02:00
|
|
|
QModelIndexList MergedProxyModel::mapToSource(const QModelIndexList &proxy_indexes) const {
|
2018-02-27 18:06:05 +01:00
|
|
|
|
|
|
|
QModelIndexList ret;
|
2021-06-20 19:04:08 +02:00
|
|
|
ret.reserve(proxy_indexes.count());
|
2021-01-26 16:48:04 +01:00
|
|
|
for (const QModelIndex &idx : proxy_indexes) {
|
|
|
|
ret << mapToSource(idx);
|
2018-02-27 18:06:05 +01:00
|
|
|
}
|
|
|
|
return ret;
|
|
|
|
|
|
|
|
}
|