/*
* 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 = getKindOfStatement(statement);
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) {
return pIconsManager->getPixmapForStatement(node->statement);
}
}
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);
} else {
clear();
}
}
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();
}
}
}