diff --git a/kdevplatform/language/classmodel/classmodel.cpp b/kdevplatform/language/classmodel/classmodel.cpp index 54cb33038c..ce4a325bc8 100644 --- a/kdevplatform/language/classmodel/classmodel.cpp +++ b/kdevplatform/language/classmodel/classmodel.cpp @@ -1,278 +1,282 @@ /* * KDevelop Class Browser * * Copyright 2007-2008 Hamish Rodda * Copyright 2009 Lior Mualem * * This program is free software; you can redistribute it and/or modify * it under the terms of the GNU Library General Public License as * published by the Free Software Foundation; either version 2 of the * License, or (at your option) any later version. * * This program 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 this program; if not, write to the * Free Software Foundation, Inc., * 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA. */ #include "classmodel.h" #include "classmodelnode.h" #include "allclassesfolder.h" #include "projectfolder.h" #include "../duchain/declaration.h" #include #include "../../interfaces/icore.h" #include "../../interfaces/iproject.h" #include "../../interfaces/iprojectcontroller.h" using namespace KDevelop; using namespace ClassModelNodes; ////////////////////////////////////////////////////////////////////////////// ////////////////////////////////////////////////////////////////////////////// NodesModelInterface::~NodesModelInterface() { } ////////////////////////////////////////////////////////////////////////////// ////////////////////////////////////////////////////////////////////////////// ClassModel::ClassModel() : m_features(NodesModelInterface::AllProjectsClasses | NodesModelInterface::BaseAndDerivedClasses | NodesModelInterface::ClassInternals) { m_topNode = new FolderNode(QStringLiteral("Top Node"), this); if ( features().testFlag(NodesModelInterface::AllProjectsClasses) ) { m_allClassesNode = new FilteredAllClassesFolder(this); m_topNode->addNode( m_allClassesNode ); } connect(ICore::self()->projectController(), &IProjectController::projectClosing, this, &ClassModel::removeProjectNode); connect(ICore::self()->projectController(), &IProjectController::projectOpened, this, &ClassModel::addProjectNode); foreach ( IProject* project, ICore::self()->projectController()->projects() ) { addProjectNode(project); } } ClassModel::~ClassModel() { delete m_topNode; } void ClassModel::updateFilterString(const QString& a_newFilterString) { m_allClassesNode->updateFilterString(a_newFilterString); foreach ( ClassModelNodes::FilteredProjectFolder* folder, m_projectNodes ) { folder->updateFilterString(a_newFilterString); } } void ClassModel::collapsed(const QModelIndex& index) { Node* node = static_cast(index.internalPointer()); node->collapse(); } void ClassModel::expanded(const QModelIndex& index) { Node* node = static_cast(index.internalPointer()); node->expand(); } QFlags< Qt::ItemFlag > ClassModel::flags(const QModelIndex&) const { return Qt::ItemIsSelectable | Qt::ItemIsEnabled; } int ClassModel::rowCount(const QModelIndex& parent) const { Node* node = m_topNode; if ( parent.isValid() ) node = static_cast(parent.internalPointer()); return node->getChildren().size(); } QVariant ClassModel::data(const QModelIndex& index, int role) const { if ( !index.isValid() ) return QVariant(); Node* node = static_cast(index.internalPointer()); if ( role == Qt::DisplayRole ) return node->displayName(); if ( role == Qt::DecorationRole ) { QIcon icon = node->getCachedIcon(); return icon.isNull() ? QVariant() : icon; } return QVariant(); } QVariant ClassModel::headerData(int, Qt::Orientation, int role) const { if ( role == Qt::DisplayRole ) return QStringLiteral("Class"); return QVariant(); } int ClassModel::columnCount(const QModelIndex&) const { return 1; } bool ClassModel::hasChildren(const QModelIndex& parent) const { if ( !parent.isValid() ) return true; Node* node = static_cast(parent.internalPointer()); return node->hasChildren(); } QModelIndex ClassModel::index(int row, int column, const QModelIndex& parent) const { if (row < 0 || column != 0) return QModelIndex(); Node* node = m_topNode; if ( parent.isValid() ) node = static_cast(parent.internalPointer()); if ( row >= node->getChildren().size() ) return QModelIndex(); return index(node->getChildren()[row]); } QModelIndex ClassModel::parent(const QModelIndex& childIndex) const { if ( !childIndex.isValid() ) return QModelIndex(); Node* childNode = static_cast(childIndex.internalPointer()); if ( childNode->getParent() == m_topNode ) return QModelIndex(); return index( childNode->getParent() ); } QModelIndex ClassModel::index(ClassModelNodes::Node* a_node) const { if (!a_node) { return QModelIndex(); } // If no parent exists, we have an invalid index (root node or not part of a model). if ( a_node->getParent() == nullptr ) return QModelIndex(); return createIndex(a_node->row(), 0, a_node); } KDevelop::DUChainBase* ClassModel::duObjectForIndex(const QModelIndex& a_index) { if ( !a_index.isValid() ) return nullptr; Node* node = static_cast(a_index.internalPointer()); if ( IdentifierNode* identifierNode = dynamic_cast(node) ) return identifierNode->getDeclaration(); // Non was found. return nullptr; } QModelIndex ClassModel::getIndexForIdentifier(const KDevelop::IndexedQualifiedIdentifier& a_id) { ClassNode* node = m_allClassesNode->findClassNode(a_id); if ( node == nullptr ) return QModelIndex(); return index(node); } void ClassModel::nodesLayoutAboutToBeChanged(ClassModelNodes::Node*) { emit layoutAboutToBeChanged(); } void ClassModel::nodesLayoutChanged(ClassModelNodes::Node*) { QModelIndexList oldIndexList = persistentIndexList(); QModelIndexList newIndexList; foreach(const QModelIndex& oldIndex, oldIndexList) { Node* node = static_cast(oldIndex.internalPointer()); if ( node ) { // Re-map the index. newIndexList << createIndex(node->row(), 0, node); } else newIndexList << oldIndex; } changePersistentIndexList(oldIndexList, newIndexList); emit layoutChanged(); } -void ClassModel::nodesRemoved(ClassModelNodes::Node* a_parent, int a_first, int a_last) +void ClassModel::nodesAboutToBeRemoved(ClassModelNodes::Node* a_parent, int a_first, int a_last) { beginRemoveRows(index(a_parent), a_first, a_last); +} + +void ClassModel::nodesRemoved(ClassModelNodes::Node*) +{ endRemoveRows(); } void ClassModel::nodesAboutToBeAdded(ClassModelNodes::Node* a_parent, int a_pos, int a_size) { beginInsertRows(index(a_parent), a_pos, a_pos + a_size - 1); } void ClassModel::nodesAdded(ClassModelNodes::Node*) { endInsertRows(); } void ClassModel::addProjectNode( IProject* project ) { m_projectNodes[project] = new ClassModelNodes::FilteredProjectFolder(this, project); nodesLayoutAboutToBeChanged(m_projectNodes[project]); m_topNode->addNode(m_projectNodes[project]); nodesLayoutChanged(m_projectNodes[project]); } void ClassModel::removeProjectNode( IProject* project ) { m_topNode->removeNode(m_projectNodes[project]); m_projectNodes.remove(project); } // kate: space-indent on; indent-width 2; tab-width 4; replace-tabs on; auto-insert-doxygen on diff --git a/kdevplatform/language/classmodel/classmodel.h b/kdevplatform/language/classmodel/classmodel.h index de9cff53f3..6c92cf4fad 100644 --- a/kdevplatform/language/classmodel/classmodel.h +++ b/kdevplatform/language/classmodel/classmodel.h @@ -1,153 +1,155 @@ /* * KDevelop Class Browser * * Copyright 2007-2008 Hamish Rodda * Copyright 2009 Lior Mualem * * This program is free software; you can redistribute it and/or modify * it under the terms of the GNU Library General Public License as * published by the Free Software Foundation; either version 2 of the * License, or (at your option) any later version. * * This program 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 this program; if not, write to the * Free Software Foundation, Inc., * 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA. */ #ifndef KDEVPLATFORM_CLASSMODEL_H #define KDEVPLATFORM_CLASSMODEL_H #include #include "classmodelnode.h" #include class ClassBrowserPlugin; namespace KDevelop { class TopDUContext; class IDocument; class DUContext; class IProject; class DUChainBase; class IndexedQualifiedIdentifier; } namespace ClassModelNodes { class Node; class FilteredAllClassesFolder; class FilteredProjectFolder; class FolderNode; class IdentifierNode; } /// The model interface accessible from the nodes. class NodesModelInterface { public: virtual ~NodesModelInterface(); public: enum Feature { AllProjectsClasses = 0x1, BaseAndDerivedClasses = 0x2, ClassInternals = 0x4 }; Q_DECLARE_FLAGS(Features, Feature) virtual void nodesLayoutAboutToBeChanged(ClassModelNodes::Node* a_parent) = 0; virtual void nodesLayoutChanged(ClassModelNodes::Node* a_parent) = 0; - virtual void nodesRemoved(ClassModelNodes::Node* a_parent, int a_first, int a_last) = 0; + virtual void nodesAboutToBeRemoved(ClassModelNodes::Node* a_parent, int a_first, int a_last) = 0; + virtual void nodesRemoved(ClassModelNodes::Node* a_parent) = 0; virtual void nodesAboutToBeAdded(ClassModelNodes::Node* a_parent, int a_pos, int a_size) = 0; virtual void nodesAdded(ClassModelNodes::Node* a_parent) = 0; virtual Features features() const = 0; }; /** * @short A model that holds a convinient representation of the defined class in the project * * This model doesn't have much code in it, it mostly acts as a glue between the different * nodes and the tree view. * * The nodes are defined in the namespace @ref ClassModelNodes */ class KDEVPLATFORMLANGUAGE_EXPORT ClassModel : public QAbstractItemModel, public NodesModelInterface { Q_OBJECT public: ClassModel(); ~ClassModel() override; public: /// Retrieve the DU object related to the specified index. /// @note DUCHAINS READER LOCK MUST BE TAKEN! KDevelop::DUChainBase* duObjectForIndex(const QModelIndex& a_index); /// Call this to retrieve the index for the node associated with the specified id. QModelIndex getIndexForIdentifier(const KDevelop::IndexedQualifiedIdentifier& a_id); /// Return the model index associated with the given node. QModelIndex index(ClassModelNodes::Node* a_node) const; inline void setFeatures(NodesModelInterface::Features features); inline NodesModelInterface::Features features() const override { return m_features; } public Q_SLOTS: /// Call this to update the filter string for the search results folder. void updateFilterString(const QString& a_newFilterString); /// removes the project-specific node void removeProjectNode(KDevelop::IProject* project); /// adds the project-specific node void addProjectNode(KDevelop::IProject* project); private: // NodesModelInterface overrides void nodesLayoutAboutToBeChanged(ClassModelNodes::Node* a_parent) override; void nodesLayoutChanged(ClassModelNodes::Node* a_parent) override; - void nodesRemoved(ClassModelNodes::Node* a_parent, int a_first, int a_last) override; + void nodesAboutToBeRemoved(ClassModelNodes::Node* a_parent, int a_first, int a_last) override; + void nodesRemoved(ClassModelNodes::Node* a_parent) override; void nodesAboutToBeAdded(ClassModelNodes::Node* a_parent, int a_pos, int a_size) override; void nodesAdded(ClassModelNodes::Node* a_parent) override; private: /// Main level node - it's usually invisible. ClassModelNodes::Node* m_topNode; ClassModelNodes::FilteredAllClassesFolder* m_allClassesNode; QMap m_projectNodes; NodesModelInterface::Features m_features; public Q_SLOTS: /// This slot needs to be attached to collapsed signal in the tree view. void collapsed(const QModelIndex& index); /// This slot needs to be attached to expanded signal in the tree view. void expanded(const QModelIndex& index); public: // QAbstractItemModel overrides QFlags< Qt::ItemFlag > flags(const QModelIndex&) const override; QVariant data(const QModelIndex& index, int role = Qt::DisplayRole) const override; QVariant headerData(int section, Qt::Orientation orientation, int role = Qt::DisplayRole) const override; int rowCount(const QModelIndex& parent = QModelIndex()) const override; int columnCount(const QModelIndex& parent = QModelIndex()) const override; bool hasChildren(const QModelIndex& parent = QModelIndex()) const override; QModelIndex index(int row, int column, const QModelIndex& parent = QModelIndex()) const override; QModelIndex parent(const QModelIndex& child) const override; }; inline void ClassModel::setFeatures(Features features) { m_features = features; } Q_DECLARE_OPERATORS_FOR_FLAGS(NodesModelInterface::Features) #endif // kate: space-indent on; indent-width 2; tab-width 4; replace-tabs on; auto-insert-doxygen on diff --git a/kdevplatform/language/classmodel/classmodelnode.cpp b/kdevplatform/language/classmodel/classmodelnode.cpp index 9ed78de405..4673fb2699 100644 --- a/kdevplatform/language/classmodel/classmodelnode.cpp +++ b/kdevplatform/language/classmodel/classmodelnode.cpp @@ -1,609 +1,614 @@ /* * KDevelop Class Browser * * Copyright 2007-2009 Hamish Rodda * Copyright 2009 Lior Mualem * * This program is free software; you can redistribute it and/or modify * it under the terms of the GNU Library General Public License as * published by the Free Software Foundation; either version 2 of the * License, or (at your option) any later version. * * This program 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 this program; if not, write to the * Free Software Foundation, Inc., * 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA. */ #include "classmodelnode.h" #include #include #include "../duchain/duchainlock.h" #include "../duchain/duchain.h" #include "../duchain/persistentsymboltable.h" #include "../duchain/duchainutils.h" #include "../duchain/classdeclaration.h" #include "../duchain/classfunctiondeclaration.h" #include "../duchain/types/functiontype.h" #include "../duchain/types/enumerationtype.h" #include using namespace KDevelop; using namespace ClassModelNodes; IdentifierNode::IdentifierNode(KDevelop::Declaration* a_decl, NodesModelInterface* a_model, const QString& a_displayName) : DynamicNode(a_displayName.isEmpty() ? a_decl->identifier().toString() : a_displayName, a_model) , m_identifier(a_decl->qualifiedIdentifier()) , m_indexedDeclaration(a_decl) , m_cachedDeclaration(a_decl) { } Declaration* IdentifierNode::getDeclaration() { if ( !m_cachedDeclaration ) m_cachedDeclaration = m_indexedDeclaration.declaration(); return m_cachedDeclaration.data(); } bool IdentifierNode::getIcon(QIcon& a_resultIcon) { DUChainReadLocker readLock(DUChain::lock()); Declaration* decl = getDeclaration(); if ( decl ) a_resultIcon = DUChainUtils::iconForDeclaration(decl); return !a_resultIcon.isNull(); } ////////////////////////////////////////////////////////////////////////////// ////////////////////////////////////////////////////////////////////////////// EnumNode::EnumNode(KDevelop::Declaration* a_decl, NodesModelInterface* a_model) : IdentifierNode(a_decl, a_model) { // Set display name for anonymous enums if ( m_displayName.isEmpty() ) m_displayName = QStringLiteral("*Anonymous*"); } bool EnumNode::getIcon(QIcon& a_resultIcon) { DUChainReadLocker readLock(DUChain::lock()); ClassMemberDeclaration* decl = dynamic_cast(getDeclaration()); if ( decl == nullptr ) { static QIcon Icon = QIcon::fromTheme(QStringLiteral("enum")); a_resultIcon = Icon; } else { if ( decl->accessPolicy() == Declaration::Protected ) { static QIcon Icon = QIcon::fromTheme(QStringLiteral("protected_enum")); a_resultIcon = Icon; } else if ( decl->accessPolicy() == Declaration::Private ) { static QIcon Icon = QIcon::fromTheme(QStringLiteral("private_enum")); a_resultIcon = Icon; } else { static QIcon Icon = QIcon::fromTheme(QStringLiteral("enum")); a_resultIcon = Icon; } } return true; } void EnumNode::populateNode() { DUChainReadLocker readLock(DUChain::lock()); Declaration* decl = getDeclaration(); if ( decl->internalContext() ) foreach( Declaration* enumDecl, decl->internalContext()->localDeclarations() ) addNode( new EnumNode(enumDecl, m_model) ); } ////////////////////////////////////////////////////////////////////////////// ////////////////////////////////////////////////////////////////////////////// ClassNode::ClassNode(Declaration* a_decl, NodesModelInterface* a_model) : IdentifierNode(a_decl, a_model) { } ClassNode::~ClassNode() { if ( !m_cachedUrl.isEmpty() ) { ClassModelNodesController::self().unregisterForChanges(m_cachedUrl, this); m_cachedUrl = IndexedString(); } } void ClassNode::populateNode() { DUChainReadLocker readLock(DUChain::lock()); if ( m_model->features().testFlag(NodesModelInterface::ClassInternals) ) { if ( updateClassDeclarations() ) { m_cachedUrl = getDeclaration()->url(); ClassModelNodesController::self().registerForChanges(m_cachedUrl, this); } } // Add special folders if (m_model->features().testFlag(NodesModelInterface::BaseAndDerivedClasses)) addBaseAndDerived(); } template <> inline bool qMapLessThanKey(const IndexedIdentifier &key1, const IndexedIdentifier &key2) { return key1.getIndex() < key2.getIndex(); } bool ClassNode::updateClassDeclarations() { bool hadChanges = false; SubIdentifiersMap existingIdentifiers = m_subIdentifiers; ClassDeclaration* klass = dynamic_cast(getDeclaration()); if ( klass ) { foreach(Declaration* decl, klass->internalContext()->localDeclarations()) { // Ignore forward declarations. if ( decl->isForwardDeclaration() ) continue; // Don't add existing declarations. if ( existingIdentifiers.contains( decl->ownIndex() ) ) { existingIdentifiers.remove(decl->ownIndex()); continue; } Node* newNode = nullptr; if ( EnumerationType::Ptr enumType = decl->type() ) newNode = new EnumNode( decl, m_model ); else if ( decl->isFunctionDeclaration() ) newNode = new FunctionNode( decl, m_model ); else if ( ClassDeclaration* classDecl = dynamic_cast(decl) ) newNode = new ClassNode(classDecl, m_model); else if ( ClassMemberDeclaration* memDecl = dynamic_cast(decl) ) newNode = new ClassMemberNode( memDecl, m_model ); else { // Debug - for reference. qCDebug(LANGUAGE) << "class: " << klass->toString() << "name: " << decl->toString() << " - unknown declaration type: " << typeid(*decl).name(); } if ( newNode ) { addNode(newNode); // Also remember the identifier. m_subIdentifiers.insert(decl->ownIndex(), newNode); hadChanges = true; } } } // Remove old existing identifiers for ( SubIdentifiersMap::iterator iter = existingIdentifiers.begin(); iter != existingIdentifiers.end(); ++iter ) { iter.value()->removeSelf(); m_subIdentifiers.remove(iter.key()); hadChanges = true; } return hadChanges; } bool ClassNode::addBaseAndDerived() { bool added = false; BaseClassesFolderNode *baseClassesNode = new BaseClassesFolderNode( m_model ); addNode( baseClassesNode ); if ( !baseClassesNode->hasChildren() ) removeNode( baseClassesNode ); else added = true; DerivedClassesFolderNode *derivedClassesNode = new DerivedClassesFolderNode( m_model ); addNode( derivedClassesNode ); if ( !derivedClassesNode->hasChildren() ) removeNode( derivedClassesNode ); else added = true; return added; } void ClassNode::nodeCleared() { if ( !m_cachedUrl.isEmpty() ) { ClassModelNodesController::self().unregisterForChanges(m_cachedUrl, this); m_cachedUrl = IndexedString(); } m_subIdentifiers.clear(); } void ClassModelNodes::ClassNode::documentChanged(const KDevelop::IndexedString&) { DUChainReadLocker readLock(DUChain::lock()); if ( updateClassDeclarations() ) recursiveSort(); } ClassNode* ClassNode::findSubClass(const KDevelop::IndexedQualifiedIdentifier& a_id) { // Make sure we have sub nodes. performPopulateNode(); /// @todo This is slow - we go over all the sub identifiers but the assumption is that /// this function call is rare and the list is not that long. foreach(Node* item, m_subIdentifiers) { ClassNode* classNode = dynamic_cast(item); if ( classNode == nullptr ) continue; if ( classNode->getIdentifier() == a_id ) return classNode; } return nullptr; } ////////////////////////////////////////////////////////////////////////////// ////////////////////////////////////////////////////////////////////////////// FunctionNode::FunctionNode(Declaration* a_decl, NodesModelInterface* a_model) : IdentifierNode(a_decl, a_model) { // Append the argument signature to the identifier's name (which is what the displayName is. if (FunctionType::Ptr type = a_decl->type()) m_displayName += type->partToString(FunctionType::SignatureArguments); // Add special values for ctor / dtor to sort first ClassFunctionDeclaration* classmember = dynamic_cast(a_decl); if ( classmember ) { if ( classmember->isConstructor() || classmember->isDestructor() ) m_sortableString = QLatin1Char('0') + m_displayName; else m_sortableString = QLatin1Char('1') + m_displayName; } else { m_sortableString = m_displayName; } } ////////////////////////////////////////////////////////////////////////////// ////////////////////////////////////////////////////////////////////////////// ClassMemberNode::ClassMemberNode(KDevelop::ClassMemberDeclaration* a_decl, NodesModelInterface* a_model) : IdentifierNode(a_decl, a_model) { } bool ClassMemberNode::getIcon(QIcon& a_resultIcon) { DUChainReadLocker readLock(DUChain::lock()); ClassMemberDeclaration* decl = dynamic_cast(getDeclaration()); if ( decl == nullptr ) return false; if ( decl->isTypeAlias() ) { static QIcon Icon = QIcon::fromTheme(QStringLiteral("typedef")); a_resultIcon = Icon; } else if ( decl->accessPolicy() == Declaration::Protected ) { static QIcon Icon = QIcon::fromTheme(QStringLiteral("protected_field")); a_resultIcon = Icon; } else if ( decl->accessPolicy() == Declaration::Private ) { static QIcon Icon = QIcon::fromTheme(QStringLiteral("private_field")); a_resultIcon = Icon; } else { static QIcon Icon = QIcon::fromTheme(QStringLiteral("field")); a_resultIcon = Icon; } return true; } ////////////////////////////////////////////////////////////////////////////// ////////////////////////////////////////////////////////////////////////////// DynamicFolderNode::DynamicFolderNode(const QString& a_displayName, NodesModelInterface* a_model) : DynamicNode(a_displayName, a_model) { } bool DynamicFolderNode::getIcon(QIcon& a_resultIcon) { static QIcon folderIcon = QIcon::fromTheme(QStringLiteral("folder")); a_resultIcon = folderIcon; return true; } ////////////////////////////////////////////////////////////////////////////// ////////////////////////////////////////////////////////////////////////////// FolderNode::FolderNode(const QString& a_displayName, NodesModelInterface* a_model) : Node(a_displayName, a_model) { } bool FolderNode::getIcon(QIcon& a_resultIcon) { static QIcon folderIcon = QIcon::fromTheme(QStringLiteral("folder")); a_resultIcon = folderIcon; return true; } ////////////////////////////////////////////////////////////////////////////// ////////////////////////////////////////////////////////////////////////////// BaseClassesFolderNode::BaseClassesFolderNode(NodesModelInterface* a_model) : DynamicFolderNode(i18n("Base classes"), a_model) { } void BaseClassesFolderNode::populateNode() { DUChainReadLocker readLock(DUChain::lock()); ClassDeclaration* klass = dynamic_cast( static_cast(getParent())->getDeclaration() ); if ( klass ) { // I use the imports instead of the baseClasses in the ClassDeclaration because I need // to get to the base class identifier which is not directly accessible through the // baseClasses function. foreach( const DUContext::Import& import, klass->internalContext()->importedParentContexts() ) { DUContext* baseContext = import.context( klass->topContext() ); if ( baseContext && baseContext->type() == DUContext::Class ) { Declaration* baseClassDeclaration = baseContext->owner(); if ( baseClassDeclaration ) { // Add the base class. addNode( new ClassNode(baseClassDeclaration, m_model) ); } } } } } ////////////////////////////////////////////////////////////////////////////// ////////////////////////////////////////////////////////////////////////////// DerivedClassesFolderNode::DerivedClassesFolderNode(NodesModelInterface* a_model) : DynamicFolderNode(i18n("Derived classes"), a_model) { } void DerivedClassesFolderNode::populateNode() { DUChainReadLocker readLock(DUChain::lock()); ClassDeclaration* klass = dynamic_cast( static_cast(getParent())->getDeclaration() ); if ( klass ) { uint steps = 10000; QList< Declaration* > inheriters = DUChainUtils::getInheriters(klass, steps, true); foreach( Declaration* decl, inheriters ) { addNode( new ClassNode(decl, m_model) ); } } } ////////////////////////////////////////////////////////////////////////////// ////////////////////////////////////////////////////////////////////////////// Node::Node(const QString& a_displayName, NodesModelInterface* a_model) : m_parentNode(nullptr) , m_displayName(a_displayName) , m_model(a_model) { } Node::~Node() { // Notify the model about the removal of this nodes' children. if ( !m_children.empty() && m_model ) - m_model->nodesRemoved(this, 0, m_children.size()-1); - - clear(); + { + m_model->nodesAboutToBeRemoved(this, 0, m_children.size()-1); + clear(); + m_model->nodesRemoved(this); + } } void Node::clear() { qDeleteAll(m_children); m_children.clear(); } void Node::addNode(Node* a_child) { /// @note This is disabled for performance reasons - we add them to the bottom and a /// sort usually follows which causes a layout change to be fired. // m_model->nodesAboutToBeAdded(this, m_children.size(), 1); a_child->m_parentNode = this; m_children.push_back(a_child); // m_model->nodesAdded(this); } void Node::removeNode(Node* a_child) { int row = a_child->row(); + m_model->nodesAboutToBeRemoved(this, row, row); m_children.removeAt(row); - m_model->nodesRemoved(this, row, row ); delete a_child; + m_model->nodesRemoved(this); } // Sort algorithm for the nodes. struct SortNodesFunctor { bool operator() (Node* a_lhs, Node* a_rhs) { if ( a_lhs->getScore() == a_rhs->getScore() ) { return a_lhs->getSortableString() < a_rhs->getSortableString(); } else return a_lhs->getScore() < a_rhs->getScore(); } }; void Node::recursiveSortInternal() { // Sort my nodes. std::sort(m_children.begin(), m_children.end(), SortNodesFunctor()); // Tell each node to sort it self. foreach (Node* node, m_children) node->recursiveSortInternal(); } void Node::recursiveSort() { m_model->nodesLayoutAboutToBeChanged(this); recursiveSortInternal(); m_model->nodesLayoutChanged(this); } int Node::row() { if ( m_parentNode == nullptr ) return -1; return m_parentNode->m_children.indexOf(this); } QIcon ClassModelNodes::Node::getCachedIcon() { // Load the cached icon if it's null. if ( m_cachedIcon.isNull() ) { if ( !getIcon(m_cachedIcon) ) m_cachedIcon = QIcon(); } return m_cachedIcon; } ////////////////////////////////////////////////////////////////////////////// ////////////////////////////////////////////////////////////////////////////// DynamicNode::DynamicNode(const QString& a_displayName, NodesModelInterface* a_model) : Node(a_displayName, a_model) , m_populated(false) { } void DynamicNode::collapse() { performNodeCleanup(); } void DynamicNode::expand() { performPopulateNode(); } void DynamicNode::performNodeCleanup() { if ( !m_populated ) return; if ( !m_children.empty() ) { // Notify model for this node. - m_model->nodesRemoved(this, 0, m_children.size()-1); - } + m_model->nodesAboutToBeRemoved(this, 0, m_children.size()-1); + + // Clear sub-nodes. + clear(); - // Clear sub-nodes. - clear(); + m_model->nodesRemoved(this); + } // This shouldn't be called from clear since clear is called also from the d-tor // and the function is virtual. nodeCleared(); // Mark the fact that we've been collapsed m_populated = false; } void DynamicNode::performPopulateNode(bool a_forceRepopulate) { if ( m_populated ) { if ( a_forceRepopulate ) performNodeCleanup(); else return; } populateNode(); // We're populated. m_populated = true; // Sort the list. recursiveSort(); } bool DynamicNode::hasChildren() const { // To get a true status, we'll need to populate the node. const_cast(this)->performPopulateNode(); return !m_children.empty(); } // kate: space-indent on; indent-width 2; tab-width 4; replace-tabs on; auto-insert-doxygen on