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 }