/* * Copyright (C) 2020-2022 Roy Qu (royqh1979@gmail.com) * * This program 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. * * 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, see . */ #include "classbrowser.h" #include "../utils.h" #include #include #include #include "../mainwindow.h" #include "../settings.h" #include "../colorscheme.h" #include "../utils.h" #include "../iconsmanager.h" ClassBrowserModel::ClassBrowserModel(QObject *parent):QAbstractItemModel(parent), mMutex(QMutex::Recursive) { mRoot = new ClassBrowserNode(); mRoot->parent = nullptr; mRoot->statement = PStatement(); // mRoot->childrenFetched = true; mUpdating = false; mUpdateCount = 0; } ClassBrowserModel::~ClassBrowserModel() { delete mRoot; } QModelIndex ClassBrowserModel::index(int row, int column, const QModelIndex &parent) const { if (!hasIndex(row,column,parent)) return QModelIndex(); ClassBrowserNode *parentNode; if (!parent.isValid()) { // top level parentNode = mRoot; } else { parentNode = static_cast(parent.internalPointer()); } return createIndex(row,column,parentNode->children[row]); } QModelIndex ClassBrowserModel::parent(const QModelIndex &child) const { if (!child.isValid()) { return QModelIndex(); } ClassBrowserNode *childNode = static_cast(child.internalPointer()); ClassBrowserNode *parentNode = childNode->parent; if (parentNode->parent == nullptr) //it's root node return QModelIndex(); ClassBrowserNode *grandNode = parentNode->parent; int row = grandNode->children.indexOf(parentNode); return createIndex(row,0,parentNode); } bool ClassBrowserModel::hasChildren(const QModelIndex &parent) const { ClassBrowserNode *parentNode; if (!parent.isValid()) { // top level return mRoot->children.count()>0; } else { parentNode = static_cast(parent.internalPointer()); // if (parentNode->childrenFetched) return parentNode->children.count()>0; // if (parentNode->statement) // return !parentNode->statement->children.isEmpty(); // return false; } } int ClassBrowserModel::rowCount(const QModelIndex &parent) const { ClassBrowserNode *parentNode; if (!parent.isValid()) { // top level parentNode = mRoot; } else { parentNode = static_cast(parent.internalPointer()); } return parentNode->children.count(); } int ClassBrowserModel::columnCount(const QModelIndex&) const { return 1; } //void ClassBrowserModel::fetchMore(const QModelIndex &parent) //{ // if (!parent.isValid()) { // top level // return; // } // ClassBrowserNode *parentNode = static_cast(parent.internalPointer()); // if (!parentNode->childrenFetched) { // parentNode->childrenFetched = true; // if (parentNode->statement && !parentNode->statement->children.isEmpty()) { // filterChildren(parentNode, parentNode->statement->children); // beginInsertRows(parent,0,parentNode->children.count()); // endInsertRows(); // } // } //} //bool ClassBrowserModel::canFetchMore(const QModelIndex &parent) const //{ // if (!parent.isValid()) { // top level // return false; // } // ClassBrowserNode *parentNode = static_cast(parent.internalPointer()); // if (!parentNode->childrenFetched) { // if (parentNode->statement && !parentNode->statement->children.isEmpty()) // return true; // else // parentNode->childrenFetched = true; // } // return false; //} QVariant ClassBrowserModel::data(const QModelIndex &index, int role) const { if (!index.isValid()){ return QVariant(); } ClassBrowserNode *node = static_cast(index.internalPointer()); if (!node) return QVariant(); if (role == Qt::DisplayRole) { if (node->statement) { return node->statement->command + node->statement->args; } } else if (role == Qt::ForegroundRole) { if (mColors && node->statement) { PStatement statement = (node->statement); StatementKind kind; if (mParser) { kind = mParser->getKindOfStatement(statement); } else { kind = statement->kind; } if (kind == StatementKind::skKeyword) { if (statement->command.startsWith('#')) kind = StatementKind::skPreprocessor; } PColorSchemeItem item = mColors->value(kind,PColorSchemeItem()); if (item) { return item->foreground(); } else { return pMainWindow->palette().color(QPalette::Text); } } return pMainWindow->palette().color(QPalette::Text); } else if (role == Qt::DecorationRole) { if (node->statement) { PStatement statement = (node->statement); StatementKind kind; if (mParser) { kind = mParser->getKindOfStatement(statement); } else { kind = statement->kind; } switch (kind) { case StatementKind::skTypedef: return *(pIconsManager->getPixmap(IconsManager::PARSER_TYPE)); case StatementKind::skClass: return *(pIconsManager->getPixmap(IconsManager::PARSER_CLASS)); case StatementKind::skNamespace: case StatementKind::skNamespaceAlias: return *(pIconsManager->getPixmap(IconsManager::PARSER_NAMESPACE)); case StatementKind::skPreprocessor: return *(pIconsManager->getPixmap(IconsManager::PARSER_DEFINE)); case StatementKind::skEnumClassType: case StatementKind::skEnumType: case StatementKind::skEnum: return *(pIconsManager->getPixmap(IconsManager::PARSER_ENUM)); case StatementKind::skFunction: case StatementKind::skConstructor: case StatementKind::skDestructor: if (statement->scope == StatementScope::ssGlobal) return *(pIconsManager->getPixmap(IconsManager::PARSER_GLOBAL_METHOD)); if (statement->isInherited) { if (statement->classScope == StatementClassScope::scsProtected) { return *(pIconsManager->getPixmap(IconsManager::PARSER_INHERITED_PROTECTED_METHOD)); } else if (statement->classScope == StatementClassScope::scsPublic) { return *(pIconsManager->getPixmap(IconsManager::PARSER_INHERITED_METHOD)); } } else { if (statement->classScope == StatementClassScope::scsProtected) { return *(pIconsManager->getPixmap(IconsManager::PARSER_PROTECTED_METHOD)); } else if (statement->classScope == StatementClassScope::scsPublic) { return *(pIconsManager->getPixmap(IconsManager::PARSER_PUBLIC_METHOD)); } else { return *(pIconsManager->getPixmap(IconsManager::PARSER_PRIVATE_METHOD)); } } break; case StatementKind::skGlobalVariable: return *(pIconsManager->getPixmap(IconsManager::PARSER_GLOBAL_VAR)); case StatementKind::skVariable: // if (statement->scope == StatementScope::ssGlobal) // return QIcon(":/icons/images/classparser/global.ico"); if (statement->isInherited) { if (statement->classScope == StatementClassScope::scsProtected) { return *(pIconsManager->getPixmap(IconsManager::PARSER_INHERITED_PROTECTD_VAR)); } else if (statement->classScope == StatementClassScope::scsPublic) { return *(pIconsManager->getPixmap(IconsManager::PARSER_INHERITED_VAR)); } } else { if (statement->classScope == StatementClassScope::scsProtected) { return *(pIconsManager->getPixmap(IconsManager::PARSER_PROTECTED_VAR)); } else if (statement->classScope == StatementClassScope::scsPublic) { return *(pIconsManager->getPixmap(IconsManager::PARSER_PUBLIC_VAR)); } else { return *(pIconsManager->getPixmap(IconsManager::PARSER_PRIVATE_VAR)); } } break; default: break; } } } return QVariant(); } const PCppParser &ClassBrowserModel::parser() const { return mParser; } void ClassBrowserModel::setParser(const PCppParser &newCppParser) { if (mParser) { disconnect(mParser.get(), &CppParser::onEndParsing, this, &ClassBrowserModel::fillStatements); } mParser = newCppParser; if (mParser) { connect(mParser.get(), &CppParser::onEndParsing, this, &ClassBrowserModel::fillStatements); } } void ClassBrowserModel::clear() { beginResetModel(); mRoot->children.clear(); mNodes.clear(); mDummyStatements.clear(); endResetModel(); } void ClassBrowserModel::fillStatements() { { QMutexLocker locker(&mMutex); if (mUpdateCount!=0 || mUpdating) return; mUpdating = true; } beginResetModel(); clear(); { auto action = finally([this]{ endResetModel(); mUpdating = false; }); if (!mParser) return; if (!mParser->enabled()) return; if (!mParser->freeze()) return; { auto action2 = finally([this]{ mParser->unFreeze(); }); QString mParserSerialId = mParser->serialId(); if (!mCurrentFile.isEmpty()) { // QSet includedFiles = mParser->getFileIncludes(mCurrentFile); addMembers(); // Remember selection // if fLastSelection <> '' then // ReSelect; } } } } void ClassBrowserModel::addChild(ClassBrowserNode *node, PStatement statement) { PClassBrowserNode newNode = std::make_shared(); newNode->parent = node; newNode->statement = statement; // newNode->childrenFetched = false; node->children.append(newNode.get()); mNodes.append(newNode); //don't show enum type's children values (they are displayed in parent scope) if (statement->kind != StatementKind::skEnumType) filterChildren(newNode.get(), statement->children); } void ClassBrowserModel::addMembers() { // show statements in the file PFileIncludes p = mParser->findFileIncludes(mCurrentFile); if (!p) return; filterChildren(mRoot,p->statements); } void ClassBrowserModel::filterChildren(ClassBrowserNode *node, const StatementMap &statements) { for (PStatement statement:statements) { if (statement->kind == StatementKind::skBlock) continue; if (statement->isInherited && !pSettings->ui().classBrowserShowInherited()) continue; if (statement == node->statement) // prevent infinite recursion continue; if (statement->scope == StatementScope::ssLocal) continue; // if (fStatementsType = cbstProject) then begin // if not Statement^._InProject then // Continue; // if Statement^._Static and not SameText(Statement^._FileName,fCurrentFile) // and not SameText(Statement^._FileName,fCurrentFile) then // Continue; // end; // we only test and handle orphan statements in the top level (node->statement is null) PStatement parentScope = statement->parentScope.lock(); if ((parentScope!=node->statement) && (!node->statement)) { // // we only handle orphan statements when type is cbstFile // if fStatementsType <> cbstFile then // Continue; // //should not happend, just in case of error if (!parentScope) continue; // Processing the orphan statement while (statement) { //the statement's parent is in this file, so it's not a real orphan if ((parentScope->fileName==mCurrentFile) ||(parentScope->definitionFileName==mCurrentFile)) break; PStatement dummyParent = mDummyStatements.value(parentScope->fullName,PStatement()); if (dummyParent) { dummyParent->children.insert(statement->command,statement); break; } dummyParent = createDummy(parentScope); dummyParent->children.insert(statement->command,statement); //we are adding an orphan statement, just add it statement = dummyParent; parentScope = statement->parentScope.lock(); if (!parentScope) { addChild(node,statement); break; } } } else if (statement->kind == StatementKind::skNamespace) { PStatement dummy = mDummyStatements.value(statement->fullName,PStatement()); if (dummy) { for (PStatement child: statement->children) { dummy->children.insert(child->command,child); } continue; } dummy = createDummy(statement); dummy->children = statement->children; addChild(node,dummy); } else { addChild(node,statement); } } if (pSettings->ui().classBrowserSortAlpha() && pSettings->ui().classBrowserSortType()) { std::sort(node->children.begin(),node->children.end(), [](ClassBrowserNode* node1,ClassBrowserNode* node2) { if (node1->statement->kind < node2->statement->kind) { return true; } else if (node1->statement->kind == node2->statement->kind) { return node1->statement->command < node2->statement->command; } else { return false; } }); } else if (pSettings->ui().classBrowserSortAlpha()) { std::sort(node->children.begin(),node->children.end(), [](ClassBrowserNode* node1,ClassBrowserNode* node2) { return node1->statement->command < node2->statement->command; }); } else if (pSettings->ui().classBrowserSortType()) { std::sort(node->children.begin(),node->children.end(), [](ClassBrowserNode* node1,ClassBrowserNode* node2) { return node1->statement->kind < node2->statement->kind; }); } } PStatement ClassBrowserModel::createDummy(PStatement statement) { PStatement result = std::make_shared(); result->parentScope = statement->parentScope; result->command = statement->command; result->args = statement->args; result->noNameArgs = statement->noNameArgs; result->fullName = statement->fullName; result->kind = statement->kind; result->type = statement->type; result->value = statement->value; result->scope = statement->scope; result->classScope = statement->classScope; result->inProject = statement->inProject; result->inSystemHeader = statement->inSystemHeader; result->isStatic = statement->isStatic; result->isInherited = statement->isInherited; result->fileName = mCurrentFile; result->definitionFileName = mCurrentFile; result->line = 0; result->definitionLine = 0; mDummyStatements.insert(result->fullName,result); return result; } const std::shared_ptr > > &ClassBrowserModel::colors() const { return mColors; } void ClassBrowserModel::setColors(const std::shared_ptr > > &newColors) { mColors = newColors; } const QString &ClassBrowserModel::currentFile() const { return mCurrentFile; } void ClassBrowserModel::setCurrentFile(const QString &newCurrentFile) { mCurrentFile = newCurrentFile; } void ClassBrowserModel::beginUpdate() { mUpdateCount++; } void ClassBrowserModel::endUpdate() { mUpdateCount--; if (mUpdateCount == 0) { if (mParser && !mParser->parsing()) { this->fillStatements(); } } }