File indexing completed on 2024-04-21 04:50:52

0001 /*
0002     SPDX-FileCopyrightText: 2017 Nicolas Carion
0003     SPDX-License-Identifier: GPL-3.0-only OR LicenseRef-KDE-Accepted-GPL
0004 */
0005 
0006 #include "treeitem.hpp"
0007 #include "abstracttreemodel.hpp"
0008 #include <QDebug>
0009 #include <numeric>
0010 #include <utility>
0011 
0012 TreeItem::TreeItem(QList<QVariant> data, const std::shared_ptr<AbstractTreeModel> &model, bool isRoot, int id)
0013     : m_itemData(std::move(data))
0014     , m_model(model)
0015     , m_depth(0)
0016     , m_id(id == -1 ? AbstractTreeModel::getNextId() : id)
0017     , m_isInModel(false)
0018     , m_isRoot(isRoot)
0019     , m_isInvalid(false)
0020 {
0021 }
0022 
0023 std::shared_ptr<TreeItem> TreeItem::construct(const QList<QVariant> &data, const std::shared_ptr<AbstractTreeModel> &model, bool isRoot, int id)
0024 {
0025     std::shared_ptr<TreeItem> self(new TreeItem(data, model, isRoot, id));
0026     baseFinishConstruct(self);
0027     return self;
0028 }
0029 
0030 // static
0031 void TreeItem::baseFinishConstruct(const std::shared_ptr<TreeItem> &self)
0032 {
0033     if (self->m_isRoot) {
0034         registerSelf(self);
0035     }
0036 }
0037 
0038 TreeItem::~TreeItem()
0039 {
0040     deregisterSelf();
0041 }
0042 
0043 std::shared_ptr<TreeItem> TreeItem::appendChild(const QList<QVariant> &data)
0044 {
0045     if (auto ptr = m_model.lock()) {
0046         auto child = construct(data, ptr, false);
0047         appendChild(child);
0048         return child;
0049     }
0050     qDebug() << "ERROR: Something went wrong when appending child in TreeItem. Model is not available anymore";
0051     Q_ASSERT(false);
0052     return std::shared_ptr<TreeItem>();
0053 }
0054 
0055 bool TreeItem::appendChild(const std::shared_ptr<TreeItem> &child)
0056 {
0057     if (hasAncestor(child->getId())) {
0058         // in that case, we are trying to create a cycle, abort
0059         return false;
0060     }
0061     if (auto oldParent = child->parentItem().lock()) {
0062         if (oldParent->getId() == m_id) {
0063             // no change needed
0064             return true;
0065         }
0066         // in that case a call to removeChild should have been carried out
0067         qDebug() << "ERROR: trying to append a child that alrealdy has a parent";
0068         return false;
0069     }
0070     if (auto ptr = m_model.lock()) {
0071         ptr->notifyRowAboutToAppend(shared_from_this());
0072         child->updateParent(shared_from_this());
0073         int id = child->getId();
0074         auto it = m_childItems.insert(m_childItems.end(), child);
0075         m_iteratorTable[id] = it;
0076         registerSelf(child);
0077         ptr->notifyRowAppended(child);
0078         return true;
0079     }
0080     qDebug() << "ERROR: Something went wrong when appending child in TreeItem. Model is not available anymore";
0081     Q_ASSERT(false);
0082     return false;
0083 }
0084 
0085 void TreeItem::moveChild(int ix, const std::shared_ptr<TreeItem> &child)
0086 {
0087     if (auto ptr = m_model.lock()) {
0088         auto parentPtr = child->m_parentItem.lock();
0089         if (parentPtr && parentPtr->getId() != m_id) {
0090             parentPtr->removeChild(child);
0091         } else {
0092             // deletion of child
0093             auto it = m_iteratorTable[child->getId()];
0094             m_childItems.erase(it);
0095         }
0096         ptr->notifyRowAboutToAppend(shared_from_this());
0097         child->updateParent(shared_from_this());
0098         int id = child->getId();
0099         auto pos = m_childItems.begin();
0100         std::advance(pos, ix);
0101         auto it = m_childItems.insert(pos, child);
0102         m_iteratorTable[id] = it;
0103         ptr->notifyRowAppended(child);
0104         m_isInModel = true;
0105     } else {
0106         qDebug() << "ERROR: Something went wrong when moving child in TreeItem. Model is not available anymore";
0107         Q_ASSERT(false);
0108     }
0109 }
0110 
0111 void TreeItem::removeChild(const std::shared_ptr<TreeItem> &child)
0112 {
0113     if (auto ptr = m_model.lock()) {
0114         ptr->notifyRowAboutToDelete(shared_from_this(), child->row());
0115         // get iterator corresponding to child
0116         Q_ASSERT(m_iteratorTable.count(child->getId()) > 0);
0117         auto it = m_iteratorTable[child->getId()];
0118         // deletion of child
0119         m_childItems.erase(it);
0120         // clean iterator table
0121         m_iteratorTable.erase(child->getId());
0122         child->m_depth = 0;
0123         child->m_parentItem.reset();
0124         child->deregisterSelf();
0125         ptr->notifyRowDeleted();
0126     } else {
0127         qDebug() << "ERROR: Something went wrong when removing child in TreeItem. Model is not available anymore";
0128         Q_ASSERT(false);
0129     }
0130 }
0131 
0132 bool TreeItem::changeParent(std::shared_ptr<TreeItem> newParent)
0133 {
0134     Q_ASSERT(!m_isRoot);
0135     if (m_isRoot) return false;
0136     std::shared_ptr<TreeItem> oldParent;
0137     if ((oldParent = m_parentItem.lock())) {
0138         oldParent->removeChild(shared_from_this());
0139     }
0140     bool res = true;
0141     if (newParent) {
0142         res = newParent->appendChild(shared_from_this());
0143         if (res) {
0144             m_parentItem = newParent;
0145         } else if (oldParent) {
0146             // something went wrong, we have to reset the parent.
0147             bool reverse = oldParent->appendChild(shared_from_this());
0148             Q_ASSERT(reverse);
0149         }
0150     }
0151     return res;
0152 }
0153 
0154 std::shared_ptr<TreeItem> TreeItem::child(int row) const
0155 {
0156     Q_ASSERT(row >= 0 && row < int(m_childItems.size()));
0157     auto it = m_childItems.cbegin();
0158     std::advance(it, row);
0159     return (*it);
0160 }
0161 
0162 int TreeItem::childCount() const
0163 {
0164     return int(m_childItems.size());
0165 }
0166 
0167 int TreeItem::columnCount() const
0168 {
0169     return m_itemData.count();
0170 }
0171 
0172 QVariant TreeItem::dataColumn(int column) const
0173 {
0174     return m_itemData.value(column);
0175 }
0176 
0177 void TreeItem::setData(int column, const QVariant &dataColumn)
0178 {
0179     m_itemData[column] = dataColumn;
0180 }
0181 
0182 std::weak_ptr<TreeItem> TreeItem::parentItem() const
0183 {
0184     return m_parentItem;
0185 }
0186 
0187 int TreeItem::row() const
0188 {
0189     if (auto ptr = m_parentItem.lock()) {
0190         // we compute the distance in the parent's children list
0191         auto it = ptr->m_childItems.begin();
0192         return int(std::distance(it, static_cast<decltype(it)>(ptr->m_iteratorTable.at(m_id))));
0193     }
0194     return -1;
0195 }
0196 
0197 int TreeItem::depth() const
0198 {
0199     return m_depth;
0200 }
0201 
0202 int TreeItem::getId() const
0203 {
0204     return m_id;
0205 }
0206 
0207 bool TreeItem::isInModel() const
0208 {
0209     return m_isInModel;
0210 }
0211 
0212 void TreeItem::registerSelf(const std::shared_ptr<TreeItem> &self)
0213 {
0214     for (const auto &child : self->m_childItems) {
0215         registerSelf(child);
0216     }
0217     if (auto ptr = self->m_model.lock()) {
0218         ptr->registerItem(self);
0219         self->m_isInModel = true;
0220     } else {
0221         qDebug() << "Error : construction of treeItem failed because parent model is not available anymore";
0222         Q_ASSERT(false);
0223     }
0224 }
0225 
0226 void TreeItem::deregisterSelf()
0227 {
0228     for (const auto &child : m_childItems) {
0229         child->deregisterSelf();
0230     }
0231     if (m_isInModel) {
0232         if (auto ptr = m_model.lock()) {
0233             ptr->deregisterItem(m_id, this);
0234             m_isInModel = false;
0235         }
0236     }
0237 }
0238 
0239 bool TreeItem::hasAncestor(int id)
0240 {
0241     if (m_id == id) {
0242         return true;
0243     }
0244     if (auto ptr = m_parentItem.lock()) {
0245         return ptr->hasAncestor(id);
0246     }
0247     return false;
0248 }
0249 
0250 bool TreeItem::isRoot() const
0251 {
0252     return m_isRoot;
0253 }
0254 
0255 void TreeItem::updateParent(std::shared_ptr<TreeItem> parent)
0256 {
0257     m_parentItem = parent;
0258     if (parent) {
0259         m_depth = parent->m_depth + 1;
0260     }
0261 }
0262 
0263 std::vector<std::shared_ptr<TreeItem>> TreeItem::getLeaves()
0264 {
0265     if (childCount() == 0) {
0266         return {shared_from_this()};
0267     }
0268     std::vector<std::shared_ptr<TreeItem>> leaves;
0269     for (const auto &c : m_childItems) {
0270         for (const auto &l : c->getLeaves()) {
0271             leaves.push_back(l);
0272         }
0273     }
0274     return leaves;
0275 }