File indexing completed on 2025-03-09 04:51:37

0001 /*
0002  * SPDX-FileCopyrightText: 2014 Christian Mollekopf <mollekopf@kolabsys.com>
0003  *
0004  * SPDX-License-Identifier: GPL-2.0-or-later WITH Qt-Commercial-exception-1.0
0005  */
0006 
0007 #include "reparentingmodel.h"
0008 
0009 #include "korganizer_debug.h"
0010 
0011 /*
0012  * Notes:
0013  * * layoutChanged must never add or remove nodes.
0014  * * rebuildAll can therefore only be called if it doesn't introduce new nodes or within a reset.
0015  * * The node memory management is done using the node tree, nodes are deleted by being removed from the node tree.
0016  */
0017 
0018 ReparentingModel::Node::Node(ReparentingModel &model)
0019     : personModel(model)
0020     , mIsSourceNode(false)
0021 {
0022 }
0023 
0024 ReparentingModel::Node::Node(ReparentingModel &model, ReparentingModel::Node *p, const QModelIndex &srcIndex)
0025     : sourceIndex(srcIndex)
0026     , parent(p)
0027     , personModel(model)
0028     , mIsSourceNode(true)
0029 {
0030     if (sourceIndex.isValid()) {
0031         personModel.mSourceNodes.append(this);
0032     }
0033     Q_ASSERT(parent);
0034 }
0035 
0036 ReparentingModel::Node::~Node()
0037 {
0038     // The source index may be invalid meanwhile (it's a persistent index)
0039     personModel.mSourceNodes.removeOne(this);
0040 }
0041 
0042 bool ReparentingModel::Node::operator==(const ReparentingModel::Node &node) const
0043 {
0044     return this == &node;
0045 }
0046 
0047 ReparentingModel::Node::Ptr ReparentingModel::Node::searchNode(ReparentingModel::Node *node)
0048 {
0049     Node::Ptr nodePtr;
0050     if (node->parent) {
0051         // Reparent node
0052         QList<Node::Ptr>::iterator it = node->parent->children.begin();
0053         for (; it != node->parent->children.end(); ++it) {
0054             if (it->data() == node) {
0055                 // Reuse smart pointer
0056                 nodePtr = *it;
0057                 node->parent->children.erase(it);
0058                 break;
0059             }
0060         }
0061         Q_ASSERT(nodePtr);
0062     } else {
0063         nodePtr = Node::Ptr(node);
0064     }
0065 
0066     return nodePtr;
0067 }
0068 
0069 void ReparentingModel::Node::reparent(ReparentingModel::Node *node)
0070 {
0071     Node::Ptr nodePtr = searchNode(node);
0072     addChild(nodePtr);
0073 }
0074 
0075 void ReparentingModel::Node::addChild(const ReparentingModel::Node::Ptr &node)
0076 {
0077     node->parent = this;
0078     children.append(node);
0079 }
0080 
0081 void ReparentingModel::Node::clearHierarchy()
0082 {
0083     parent = nullptr;
0084     children.clear();
0085 }
0086 
0087 bool ReparentingModel::Node::setData(const QVariant & /* value */, int /* role */)
0088 {
0089     return false;
0090 }
0091 
0092 QVariant ReparentingModel::Node::data(int role) const
0093 {
0094     if (sourceIndex.isValid()) {
0095         return sourceIndex.data(role);
0096     }
0097     return {};
0098 }
0099 
0100 bool ReparentingModel::Node::adopts(const QModelIndex & /* sourceIndex */)
0101 {
0102     return false;
0103 }
0104 
0105 bool ReparentingModel::Node::isDuplicateOf(const QModelIndex & /* sourceIndex */)
0106 {
0107     return false;
0108 }
0109 
0110 void ReparentingModel::Node::update(const Node::Ptr & /* node */)
0111 {
0112 }
0113 
0114 bool ReparentingModel::Node::isSourceNode() const
0115 {
0116     return mIsSourceNode;
0117 }
0118 
0119 int ReparentingModel::Node::row() const
0120 {
0121     Q_ASSERT(parent);
0122     int row = 0;
0123     for (const Node::Ptr &node : std::as_const(parent->children)) {
0124         if (node.data() == this) {
0125             return row;
0126         }
0127         row++;
0128     }
0129     return -1;
0130 }
0131 
0132 ReparentingModel::ReparentingModel(QObject *parent)
0133     : QAbstractProxyModel(parent)
0134     , mRootNode(*this)
0135     , mNodeManager(NodeManager::Ptr(new NodeManager(*this)))
0136 {
0137 }
0138 
0139 ReparentingModel::~ReparentingModel()
0140 {
0141     // Otherwise we cannot guarantee that the nodes reference to *this is always valid
0142     mRootNode.children.clear();
0143     mProxyNodes.clear();
0144     mSourceNodes.clear();
0145 }
0146 
0147 bool ReparentingModel::validateNode(const Node *node) const
0148 {
0149     // Expected:
0150     // * Each node tree starts at mRootNode
0151     // * Each node is listed in the children of it's parent
0152     // * Root node never leaves the model and thus should never enter this function
0153     if (!node) {
0154         qCWarning(KORGANIZER_LOG) << "nullptr";
0155         return false;
0156     }
0157     if (node == &mRootNode) {
0158         qCWarning(KORGANIZER_LOG) << "is root node";
0159         return false;
0160     }
0161     const Node *n = node;
0162     int depth = 0;
0163     while (n) {
0164         if ((intptr_t)(n) < 1000) {
0165             // Detect corruptions with unlikely pointers
0166             qCWarning(KORGANIZER_LOG) << "corrupt pointer" << depth;
0167             return false;
0168         }
0169         if (!n->parent) {
0170             qCWarning(KORGANIZER_LOG) << "nullptr parent" << depth << n->isSourceNode();
0171             return false;
0172         }
0173         if (n->parent == n) {
0174             qCWarning(KORGANIZER_LOG) << "loop" << depth;
0175             return false;
0176         }
0177 
0178         bool found = false;
0179         for (const Node::Ptr &child : std::as_const(n->parent->children)) {
0180             if (child.data() == n) {
0181                 found = true;
0182             }
0183         }
0184         if (!found) {
0185             qCWarning(KORGANIZER_LOG) << "not linked as child" << depth;
0186             return false;
0187         }
0188         depth++;
0189         if (depth > 1000) {
0190             qCWarning(KORGANIZER_LOG) << "loop detected" << depth;
0191             return false;
0192         }
0193 
0194         if (n->parent == &mRootNode) {
0195             return true;
0196         }
0197         // If the parent isn't root there is at least one more level
0198         if (!n->parent->parent) {
0199             qCWarning(KORGANIZER_LOG) << "missing parent parent" << depth;
0200             return false;
0201         }
0202         if (n->parent->parent == n) {
0203             qCWarning(KORGANIZER_LOG) << "parent parent loop" << depth;
0204             return false;
0205         }
0206         n = n->parent;
0207     }
0208     qCWarning(KORGANIZER_LOG) << "not linked to root" << depth;
0209     return false;
0210 }
0211 
0212 void ReparentingModel::addNode(const ReparentingModel::Node::Ptr &node)
0213 {
0214     // We have to make this check before issuing the async method,
0215     // otherwise we run into the problem that while a node is being removed,
0216     // the async request could be triggered (due to a changed signal),
0217     // resulting in the node getting read immediately after it had been removed.
0218     for (const ReparentingModel::Node::Ptr &existing : std::as_const(mProxyNodes)) {
0219         if (*existing == *node) {
0220             // qCDebug(KORGANIZER_LOG) << "node is already existing";
0221             return;
0222         }
0223     }
0224     mNodesToAdd << node;
0225     qRegisterMetaType<Node::Ptr>("Node::Ptr");
0226     QMetaObject::invokeMethod(this, "doAddNode", Qt::QueuedConnection, Q_ARG(Node::Ptr, node));
0227 }
0228 
0229 void ReparentingModel::doAddNode(const Node::Ptr &node)
0230 {
0231     for (const ReparentingModel::Node::Ptr &existing : std::as_const(mProxyNodes)) {
0232         if (*existing == *node) {
0233             // qCDebug(KORGANIZER_LOG) << "node is already existing";
0234             return;
0235         }
0236     }
0237     // If a datachanged call triggered this through checkSourceIndex, right after a person node has been removed.
0238     // We'd end-up re-inserting the node that has just been removed. Therefore removeNode can cancel the pending addNode
0239     // call through mNodesToAdd.
0240     bool addNodeAborted = true;
0241     for (int i = 0; i < mNodesToAdd.size(); ++i) {
0242         if (*mNodesToAdd.at(i) == *node) {
0243             mNodesToAdd.remove(i);
0244             addNodeAborted = false;
0245             break;
0246         }
0247     }
0248     if (addNodeAborted) {
0249         return;
0250     }
0251 
0252     if (!isDuplicate(node)) {
0253         const int targetRow = mRootNode.children.size();
0254         beginInsertRows(QModelIndex(), targetRow, targetRow);
0255         mProxyNodes << node;
0256         insertProxyNode(node);
0257         endInsertRows();
0258         reparentSourceNodes(node);
0259     } else {
0260         mProxyNodes << node;
0261     }
0262 }
0263 
0264 void ReparentingModel::updateNode(const ReparentingModel::Node::Ptr &node)
0265 {
0266     for (const ReparentingModel::Node::Ptr &existing : std::as_const(mProxyNodes)) {
0267         if (*existing == *node) {
0268             existing->update(node);
0269             const QModelIndex i = index(existing.data());
0270             Q_EMIT dataChanged(i, i);
0271             return;
0272         }
0273     }
0274 
0275     qCWarning(KORGANIZER_LOG) << objectName() << "no node to update, create new node";
0276     addNode(node);
0277 }
0278 
0279 void ReparentingModel::removeNode(const ReparentingModel::Node &node)
0280 {
0281     // If there is an addNode in progress for that node, abort it.
0282     for (int i = 0; i < mNodesToAdd.size(); ++i) {
0283         if (*mNodesToAdd.at(i) == node) {
0284             mNodesToAdd.remove(i);
0285         }
0286     }
0287     for (int i = 0; i < mProxyNodes.size(); ++i) {
0288         if (*mProxyNodes.at(i) == node) {
0289             // TODO: this does not yet take care of un-reparenting reparented nodes.
0290             const Node &n = *mProxyNodes.at(i);
0291             Node *parentNode = n.parent;
0292             beginRemoveRows(index(parentNode), n.row(), n.row());
0293             parentNode->children.remove(n.row()); // deletes node
0294             mProxyNodes.remove(i);
0295             endRemoveRows();
0296             break;
0297         }
0298     }
0299 }
0300 
0301 void ReparentingModel::setNodes(const QList<Node::Ptr> &nodes)
0302 {
0303     for (const ReparentingModel::Node::Ptr &node : nodes) {
0304         addNode(node);
0305     }
0306     const auto currentProxyNodes = mProxyNodes;
0307     for (const ReparentingModel::Node::Ptr &node : currentProxyNodes) {
0308         if (!nodes.contains(node)) {
0309             removeNode(*node);
0310         }
0311     }
0312 }
0313 
0314 void ReparentingModel::clear()
0315 {
0316     beginResetModel();
0317     mProxyNodes.clear();
0318     rebuildAll();
0319     endResetModel();
0320 }
0321 
0322 void ReparentingModel::setNodeManager(const NodeManager::Ptr &nodeManager)
0323 {
0324     mNodeManager = nodeManager;
0325 }
0326 
0327 void ReparentingModel::setSourceModel(QAbstractItemModel *sourceModel)
0328 {
0329     beginResetModel();
0330     QAbstractProxyModel::setSourceModel(sourceModel);
0331     if (sourceModel) {
0332         connect(sourceModel, &QAbstractProxyModel::rowsAboutToBeInserted, this, &ReparentingModel::onSourceRowsAboutToBeInserted);
0333         connect(sourceModel, &QAbstractProxyModel::rowsInserted, this, &ReparentingModel::onSourceRowsInserted);
0334         connect(sourceModel, &QAbstractProxyModel::rowsAboutToBeRemoved, this, &ReparentingModel::onSourceRowsAboutToBeRemoved);
0335         connect(sourceModel, &QAbstractProxyModel::rowsRemoved, this, &ReparentingModel::onSourceRowsRemoved);
0336         connect(sourceModel, &QAbstractProxyModel::rowsAboutToBeMoved, this, &ReparentingModel::onSourceRowsAboutToBeMoved);
0337         connect(sourceModel, &QAbstractProxyModel::rowsMoved, this, &ReparentingModel::onSourceRowsMoved);
0338         connect(sourceModel, &QAbstractProxyModel::modelAboutToBeReset, this, &ReparentingModel::onSourceModelAboutToBeReset);
0339         connect(sourceModel, &QAbstractProxyModel::modelReset, this, &ReparentingModel::onSourceModelReset);
0340         connect(sourceModel, &QAbstractProxyModel::dataChanged, this, &ReparentingModel::onSourceDataChanged);
0341         //       connect(sourceModel, &QAbstractProxyModel::headerDataChanged, this, &ReparentingModel::_k_sourceHeaderDataChanged);
0342         connect(sourceModel, &QAbstractProxyModel::layoutAboutToBeChanged, this, &ReparentingModel::onSourceLayoutAboutToBeChanged);
0343         connect(sourceModel, &QAbstractProxyModel::layoutChanged, this, &ReparentingModel::onSourceLayoutChanged);
0344         //       connect(sourceModel, &QAbstractProxyModel::destroyed, this, &ReparentingModel::onSourceModelDestroyed);
0345     }
0346 
0347     rebuildAll();
0348     endResetModel();
0349 }
0350 
0351 void ReparentingModel::onSourceRowsAboutToBeInserted(const QModelIndex &parent, int start, int end)
0352 {
0353     Q_UNUSED(parent)
0354     Q_UNUSED(start)
0355     Q_UNUSED(end)
0356 }
0357 
0358 ReparentingModel::Node *ReparentingModel::getReparentNode(const QModelIndex &sourceIndex)
0359 {
0360     for (const Node::Ptr &proxyNode : std::as_const(mProxyNodes)) {
0361         // Reparent source nodes according to the provided rules
0362         // The proxy can be ignored if it is a duplicate, so only reparent to proxies that are in the model
0363         if (proxyNode->parent && proxyNode->adopts(sourceIndex)) {
0364             Q_ASSERT(validateNode(proxyNode.data()));
0365             return proxyNode.data();
0366         }
0367     }
0368     return nullptr;
0369 }
0370 
0371 ReparentingModel::Node *ReparentingModel::getParentNode(const QModelIndex &sourceIndex)
0372 {
0373     if (Node *node = getReparentNode(sourceIndex)) {
0374         return node;
0375     }
0376     const QModelIndex proxyIndex = mapFromSource(sourceIndex.parent());
0377     if (proxyIndex.isValid()) {
0378         return extractNode(proxyIndex);
0379     }
0380     return nullptr;
0381 }
0382 
0383 void ReparentingModel::appendSourceNode(Node *parentNode, const QModelIndex &sourceIndex, const QModelIndexList &skip)
0384 {
0385     mNodeManager->checkSourceIndex(sourceIndex);
0386 
0387     Node::Ptr node(new Node(*this, parentNode, sourceIndex));
0388     parentNode->children.append(node);
0389     Q_ASSERT(validateNode(node.data()));
0390     rebuildFromSource(node.data(), sourceIndex, skip);
0391 }
0392 
0393 QModelIndexList ReparentingModel::descendants(const QModelIndex &sourceIndex)
0394 {
0395     if (!sourceModel()) {
0396         return {};
0397     }
0398     QModelIndexList list;
0399     if (sourceModel()->hasChildren(sourceIndex)) {
0400         const int count = sourceModel()->rowCount(sourceIndex);
0401         list.reserve(count * 2);
0402         for (int i = 0; i < count; ++i) {
0403             const QModelIndex index = sourceModel()->index(i, 0, sourceIndex);
0404             list << index;
0405             list << descendants(index);
0406         }
0407     }
0408     return list;
0409 }
0410 
0411 void ReparentingModel::removeDuplicates(const QModelIndex &sourceIndex)
0412 {
0413     const QModelIndexList list = QModelIndexList() << sourceIndex << descendants(sourceIndex);
0414     for (const QModelIndex &descendant : list) {
0415         for (const Node::Ptr &proxyNode : std::as_const(mProxyNodes)) {
0416             if (proxyNode->isDuplicateOf(descendant)) {
0417                 // Removenode from proxy
0418                 if (!proxyNode->parent) {
0419                     qCWarning(KORGANIZER_LOG) << objectName() << "Found proxy that is already not part of the model "
0420                                               << proxyNode->data(Qt::DisplayRole).toString();
0421                     continue;
0422                 }
0423                 const int targetRow = proxyNode->row();
0424                 beginRemoveRows(index(proxyNode->parent), targetRow, targetRow);
0425                 proxyNode->parent->children.remove(targetRow);
0426                 proxyNode->parent = nullptr;
0427                 endRemoveRows();
0428             }
0429         }
0430     }
0431 }
0432 
0433 void ReparentingModel::onSourceRowsInserted(const QModelIndex &parent, int start, int end)
0434 {
0435     // qCDebug(KORGANIZER_LOG) << objectName() << parent << start << end;
0436     for (int row = start; row <= end; row++) {
0437         QModelIndex sourceIndex = sourceModel()->index(row, 0, parent);
0438         Q_ASSERT(sourceIndex.isValid());
0439         Node *parentNode = getParentNode(sourceIndex);
0440         if (!parentNode) {
0441             parentNode = &mRootNode;
0442         } else {
0443             Q_ASSERT(validateNode(parentNode));
0444         }
0445         Q_ASSERT(parentNode);
0446 
0447         // Remove any duplicates that we are going to replace
0448         removeDuplicates(sourceIndex);
0449 
0450         QModelIndexList reparented;
0451         // Check for children to reparent
0452         {
0453             const auto descendantsItem = descendants(sourceIndex);
0454             for (const QModelIndex &descendant : descendantsItem) {
0455                 if (Node *proxyNode = getReparentNode(descendant)) {
0456                     qCDebug(KORGANIZER_LOG) << "reparenting " << descendant.data().toString();
0457                     int targetRow = proxyNode->children.size();
0458                     beginInsertRows(index(proxyNode), targetRow, targetRow);
0459                     appendSourceNode(proxyNode, descendant);
0460                     reparented << descendant;
0461                     endInsertRows();
0462                 }
0463             }
0464         }
0465 
0466         if (parentNode->isSourceNode()) {
0467             int targetRow = parentNode->children.size();
0468             beginInsertRows(mapFromSource(parent), targetRow, targetRow);
0469             appendSourceNode(parentNode, sourceIndex, reparented);
0470             endInsertRows();
0471         } else { // Reparented
0472             int targetRow = parentNode->children.size();
0473             beginInsertRows(index(parentNode), targetRow, targetRow);
0474             appendSourceNode(parentNode, sourceIndex);
0475             endInsertRows();
0476         }
0477     }
0478 }
0479 
0480 void ReparentingModel::onSourceRowsAboutToBeRemoved(const QModelIndex &parent, int start, int end)
0481 {
0482     // qCDebug(KORGANIZER_LOG) << objectName() << parent << start << end;
0483     // we remove in reverse order as otherwise the indexes in parentNode->children wouldn't be correct
0484     for (int row = end; row >= start; row--) {
0485         QModelIndex sourceIndex = sourceModel()->index(row, 0, parent);
0486         Q_ASSERT(sourceIndex.isValid());
0487 
0488         const QModelIndex proxyIndex = mapFromSource(sourceIndex);
0489         // If the indexes have already been removed (e.g. by removeNode)this can indeed return an invalid index
0490         if (proxyIndex.isValid()) {
0491             const Node *node = extractNode(proxyIndex);
0492             Node *parentNode = node->parent;
0493             Q_ASSERT(parentNode);
0494             const int targetRow = node->row();
0495             beginRemoveRows(index(parentNode), targetRow, targetRow);
0496             parentNode->children.remove(targetRow); // deletes node
0497             endRemoveRows();
0498         }
0499     }
0500     // Allows the node manager to remove nodes that are no longer relevant
0501     for (int row = start; row <= end; row++) {
0502         mNodeManager->checkSourceIndexRemoval(sourceModel()->index(row, 0, parent));
0503     }
0504 }
0505 
0506 void ReparentingModel::onSourceRowsRemoved(const QModelIndex & /* parent */, int /* start */, int /* end */)
0507 {
0508 }
0509 
0510 void ReparentingModel::onSourceRowsAboutToBeMoved(const QModelIndex & /* sourceParent */,
0511                                                   int /* sourceStart */,
0512                                                   int /* sourceEnd */,
0513                                                   const QModelIndex & /* destParent */,
0514                                                   int /* dest */)
0515 {
0516     qCWarning(KORGANIZER_LOG) << "not implemented";
0517     // TODO
0518     beginResetModel();
0519 }
0520 
0521 void ReparentingModel::onSourceRowsMoved(const QModelIndex & /* sourceParent */,
0522                                          int /* sourceStart */,
0523                                          int /* sourceEnd */,
0524                                          const QModelIndex & /* destParent */,
0525                                          int /* dest */)
0526 {
0527     qCWarning(KORGANIZER_LOG) << "not implemented";
0528     // TODO
0529     endResetModel();
0530 }
0531 
0532 void ReparentingModel::onSourceLayoutAboutToBeChanged()
0533 {
0534     layoutAboutToBeChanged();
0535     // Q_FOREACH(const QModelIndex &proxyPersistentIndex, persistentIndexList()) {
0536     //     Q_ASSERT(proxyPersistentIndex.isValid());
0537     //     const QPersistentModelIndex srcPersistentIndex = mapToSource(proxyPersistentIndex);
0538     //     // TODO also update the proxy persistent indexes
0539     //     //Skip indexes that are not in the source model
0540     //     if (!srcPersistentIndex.isValid()) {
0541     //         continue;
0542     //     }
0543     //     mLayoutChangedProxyIndexes << proxyPersistentIndex;
0544     //     mLayoutChangedSourcePersistentModelIndexes << srcPersistentIndex;
0545     // }
0546 }
0547 
0548 void ReparentingModel::onSourceLayoutChanged()
0549 {
0550     // By ignoring this we miss structural changes in the sourcemodel, which is mostly ok.
0551     // Before we can re-enable this we need to properly deal with skipped duplicates, because
0552     // a layout change MUST NOT add/remove new nodes (only shuffling allowed)
0553     //
0554     // Our source indexes are not endagered since we use persistend model indexes anyways
0555 
0556     // rebuildAll();
0557 
0558     // for (int i = 0; i < mLayoutChangedProxyIndexes.size(); ++i) {
0559     //     const QModelIndex oldProxyIndex = mLayoutChangedProxyIndexes.at(i);
0560     //     const QModelIndex newProxyIndex = mapFromSource(mLayoutChangedSourcePersistentModelIndexes.at(i));
0561     //     if (oldProxyIndex != newProxyIndex) {
0562     //         changePersistentIndex(oldProxyIndex, newProxyIndex);
0563     //     }
0564     // }
0565 
0566     // mLayoutChangedProxyIndexes.clear();
0567     // mLayoutChangedSourcePersistentModelIndexes.clear();
0568 
0569     // layoutChanged();
0570 }
0571 
0572 void ReparentingModel::onSourceDataChanged(const QModelIndex &begin, const QModelIndex &end)
0573 {
0574     // qCDebug(KORGANIZER_LOG) << objectName() << begin << end;
0575     for (int row = begin.row(); row <= end.row(); row++) {
0576         mNodeManager->updateSourceIndex(sourceModel()->index(row, begin.column(), begin.parent()));
0577     }
0578     Q_EMIT dataChanged(mapFromSource(begin), mapFromSource(end));
0579 }
0580 
0581 void ReparentingModel::onSourceModelAboutToBeReset()
0582 {
0583     beginResetModel();
0584 }
0585 
0586 void ReparentingModel::onSourceModelReset()
0587 {
0588     rebuildAll();
0589     endResetModel();
0590 }
0591 
0592 ReparentingModel::Node *ReparentingModel::extractNode(const QModelIndex &index) const
0593 {
0594     Node *node = static_cast<Node *>(index.internalPointer());
0595     Q_ASSERT(node);
0596     Q_ASSERT(validateNode(node));
0597     return node;
0598 }
0599 
0600 QModelIndex ReparentingModel::index(int row, int column, const QModelIndex &parent) const
0601 {
0602     if (row < 0 || column != 0) {
0603         return {};
0604     }
0605     // qCDebug(KORGANIZER_LOG) << parent << row;
0606     const Node *parentNode;
0607     if (parent.isValid()) {
0608         parentNode = extractNode(parent);
0609     } else {
0610         parentNode = &mRootNode;
0611     }
0612     // At least QAbstractItemView expects that we deal with this properly (see rowsAboutToBeRemoved "find the next visible and enabled item")
0613     // Also QAbstractItemModel::match does all kinds of weird shit including passing row=-1
0614     if (parentNode->children.size() <= row) {
0615         return {};
0616     }
0617     Node *node = parentNode->children.at(row).data();
0618     Q_ASSERT(validateNode(node));
0619     return createIndex(row, column, node);
0620 }
0621 
0622 QModelIndex ReparentingModel::mapToSource(const QModelIndex &idx) const
0623 {
0624     if (!idx.isValid() || !sourceModel()) {
0625         return {};
0626     }
0627     Node *node = extractNode(idx);
0628     if (!node->isSourceNode()) {
0629         return {};
0630     }
0631     Q_ASSERT(node->sourceIndex.model() == sourceModel());
0632     return node->sourceIndex;
0633 }
0634 
0635 ReparentingModel::Node *ReparentingModel::getSourceNode(const QModelIndex &sourceIndex) const
0636 {
0637     for (Node *n : std::as_const(mSourceNodes)) {
0638         if (n->sourceIndex == sourceIndex) {
0639             return n;
0640         }
0641     }
0642     // qCDebug(KORGANIZER_LOG) << objectName() <<  "no node found for " << sourceIndex;
0643     return nullptr;
0644 }
0645 
0646 QModelIndex ReparentingModel::mapFromSource(const QModelIndex &sourceIndex) const
0647 {
0648     // qCDebug(KORGANIZER_LOG) << sourceIndex << sourceIndex.data().toString();
0649     if (!sourceIndex.isValid()) {
0650         return {};
0651     }
0652     Node *node = getSourceNode(sourceIndex);
0653     if (!node) {
0654         // This can happen if a source nodes is hidden (person collections)
0655         return {};
0656     }
0657     Q_ASSERT(validateNode(node));
0658     return index(node);
0659 }
0660 
0661 void ReparentingModel::rebuildFromSource(Node *parentNode, const QModelIndex &sourceParent, const QModelIndexList &skip)
0662 {
0663     Q_ASSERT(parentNode);
0664     if (!sourceModel()) {
0665         return;
0666     }
0667     for (int i = 0; i < sourceModel()->rowCount(sourceParent); ++i) {
0668         const QModelIndex &sourceIndex = sourceModel()->index(i, 0, sourceParent);
0669         // Skip indexes that should be excluded because they have been reparented
0670         if (skip.contains(sourceIndex)) {
0671             continue;
0672         }
0673         appendSourceNode(parentNode, sourceIndex, skip);
0674     }
0675 }
0676 
0677 bool ReparentingModel::isDuplicate(const Node::Ptr &proxyNode) const
0678 {
0679     for (const Node *n : std::as_const(mSourceNodes)) {
0680         // qCDebug(KORGANIZER_LOG) << index << index.data().toString();
0681         if (proxyNode->isDuplicateOf(n->sourceIndex)) {
0682             return true;
0683         }
0684     }
0685     return false;
0686 }
0687 
0688 void ReparentingModel::insertProxyNode(const Node::Ptr &proxyNode)
0689 {
0690     // qCDebug(KORGANIZER_LOG) << "checking " << proxyNode->data(Qt::DisplayRole).toString();
0691     proxyNode->parent = &mRootNode;
0692     mRootNode.addChild(proxyNode);
0693     Q_ASSERT(validateNode(proxyNode.data()));
0694 }
0695 
0696 void ReparentingModel::reparentSourceNodes(const Node::Ptr &proxyNode)
0697 {
0698     // Reparent source nodes according to the provided rules
0699     for (Node *n : std::as_const(mSourceNodes)) {
0700         if (proxyNode->adopts(n->sourceIndex)) {
0701             // qCDebug(KORGANIZER_LOG) << "reparenting" << n->data(Qt::DisplayRole).toString() << "from" << n->parent->data(Qt::DisplayRole).toString()
0702             //         << "to" << proxyNode->data(Qt::DisplayRole).toString();
0703 
0704             // WARNING: While a beginMoveRows/endMoveRows would be more suitable, QSortFilterProxyModel can't deal with that. Therefore we
0705             // cannot use them.
0706             const int oldRow = n->row();
0707             beginRemoveRows(index(n->parent), oldRow, oldRow);
0708             Node::Ptr nodePtr = proxyNode->searchNode(n);
0709             // We lie about the row being removed already, but the view can deal with that better than if we call endRemoveRows after beginInsertRows
0710             endRemoveRows();
0711 
0712             const int newRow = proxyNode->children.size();
0713             beginInsertRows(index(proxyNode.data()), newRow, newRow);
0714             proxyNode->addChild(nodePtr);
0715             endInsertRows();
0716             Q_ASSERT(validateNode(n));
0717         }
0718     }
0719 }
0720 
0721 void ReparentingModel::rebuildAll()
0722 {
0723     mRootNode.children.clear();
0724     for (const Node::Ptr &proxyNode : std::as_const(mProxyNodes)) {
0725         proxyNode->clearHierarchy();
0726     }
0727     Q_ASSERT(mSourceNodes.isEmpty());
0728     mSourceNodes.clear();
0729     rebuildFromSource(&mRootNode, QModelIndex());
0730     for (const Node::Ptr &proxyNode : std::as_const(mProxyNodes)) {
0731         // qCDebug(KORGANIZER_LOG) << "checking " << proxyNode->data(Qt::DisplayRole).toString();
0732         // Avoid inserting a node that is already part of the source model
0733         if (isDuplicate(proxyNode)) {
0734             continue;
0735         }
0736         insertProxyNode(proxyNode);
0737         reparentSourceNodes(proxyNode);
0738     }
0739 }
0740 
0741 QVariant ReparentingModel::data(const QModelIndex &proxyIndex, int role) const
0742 {
0743     if (!proxyIndex.isValid()) {
0744         return {};
0745     }
0746     const Node *node = extractNode(proxyIndex);
0747     if (node->isSourceNode()) {
0748         return sourceModel()->data(mapToSource(proxyIndex), role);
0749     }
0750     return node->data(role);
0751 }
0752 
0753 bool ReparentingModel::setData(const QModelIndex &index, const QVariant &value, int role)
0754 {
0755     if (!index.isValid()) {
0756         return false;
0757     }
0758     Q_ASSERT(index.isValid());
0759     if (!sourceModel()) {
0760         return false;
0761     }
0762     Node *node = extractNode(index);
0763     if (node->isSourceNode()) {
0764         return sourceModel()->setData(mapToSource(index), value, role);
0765     }
0766     return node->setData(value, role);
0767 }
0768 
0769 Qt::ItemFlags ReparentingModel::flags(const QModelIndex &index) const
0770 {
0771     if (!index.isValid() || !sourceModel()) {
0772         return Qt::NoItemFlags;
0773     }
0774     Node *node = extractNode(index);
0775     if (!node->isSourceNode()) {
0776         return Qt::ItemIsEnabled | Qt::ItemIsUserCheckable;
0777     }
0778     return sourceModel()->flags(mapToSource(index));
0779 }
0780 
0781 int ReparentingModel::row(ReparentingModel::Node *node) const
0782 {
0783     Q_ASSERT(node);
0784     if (node == &mRootNode) {
0785         return -1;
0786     }
0787     Q_ASSERT(validateNode(node));
0788     int row = 0;
0789     for (const Node::Ptr &c : std::as_const(node->parent->children)) {
0790         if (c.data() == node) {
0791             return row;
0792         }
0793         row++;
0794     }
0795     return -1;
0796 }
0797 
0798 QModelIndex ReparentingModel::index(Node *node) const
0799 {
0800     const int r = row(node);
0801     if (r < 0) {
0802         return {};
0803     }
0804     return createIndex(r, 0, node);
0805 }
0806 
0807 QModelIndex ReparentingModel::parent(const QModelIndex &child) const
0808 {
0809     // qCDebug(KORGANIZER_LOG) << child << child.data().toString();
0810     if (!child.isValid()) {
0811         return {};
0812     }
0813     const Node *node = extractNode(child);
0814     return index(node->parent);
0815 }
0816 
0817 QModelIndex ReparentingModel::buddy(const QModelIndex &index) const
0818 {
0819     if (!index.isValid() || !sourceModel()) {
0820         return {};
0821     }
0822     Node *node = extractNode(index);
0823     if (node->isSourceNode()) {
0824         return mapFromSource(sourceModel()->buddy(mapToSource(index)));
0825     }
0826     return index;
0827 }
0828 
0829 int ReparentingModel::rowCount(const QModelIndex &parent) const
0830 {
0831     if (!parent.isValid()) {
0832         return mRootNode.children.size();
0833     }
0834 
0835     if (parent.column() != 0) {
0836         return 0;
0837     }
0838 
0839     Node *node = extractNode(parent);
0840     return node->children.size();
0841 }
0842 
0843 bool ReparentingModel::hasChildren(const QModelIndex &parent) const
0844 {
0845     return rowCount(parent) != 0;
0846 }
0847 
0848 int ReparentingModel::columnCount(const QModelIndex & /* parent */) const
0849 {
0850     return 1;
0851 }
0852 
0853 #include "moc_reparentingmodel.cpp"