353 lines
10 KiB
C++
353 lines
10 KiB
C++
#include "classbrowser.h"
|
|
#include "../utils.h"
|
|
#include <QDebug>
|
|
|
|
ClassBrowserModel::ClassBrowserModel(QObject *parent):QAbstractItemModel(parent)
|
|
{
|
|
mRoot = new ClassBrowserNode();
|
|
mRoot->parent = nullptr;
|
|
mRoot->statement = PStatement();
|
|
mRoot->childrenFetched = true;
|
|
mUpdating = false;
|
|
mUpdateCount = 0;
|
|
mShowInheritedMembers = false;
|
|
}
|
|
|
|
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<ClassBrowserNode *>(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<ClassBrowserNode *>(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();
|
|
} else {
|
|
parentNode = static_cast<ClassBrowserNode *>(parent.internalPointer());
|
|
if (parentNode->childrenFetched)
|
|
return parentNode->children.count();
|
|
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<ClassBrowserNode *>(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<ClassBrowserNode *>(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<ClassBrowserNode *>(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<ClassBrowserNode *>(index.internalPointer());
|
|
if (!node)
|
|
return QVariant();
|
|
if (role == Qt::DisplayRole) {
|
|
if (node->statement) {
|
|
return node->statement->command;
|
|
}
|
|
}
|
|
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<QString> includedFiles = mParser->getFileIncludes(mCurrentFile);
|
|
|
|
addMembers(includedFiles);
|
|
// Remember selection
|
|
// if fLastSelection <> '' then
|
|
// ReSelect;
|
|
}
|
|
|
|
}
|
|
}
|
|
}
|
|
|
|
void ClassBrowserModel::addChild(ClassBrowserNode *node, PStatement statement)
|
|
{
|
|
PClassBrowserNode newNode = std::make_shared<ClassBrowserNode>();
|
|
newNode->parent = node;
|
|
newNode->statement = statement;
|
|
newNode->childrenFetched = false;
|
|
node->children.append(newNode.get());
|
|
mNodes.append(newNode);
|
|
}
|
|
|
|
void ClassBrowserModel::addMembers(const QSet<QString> &includedFiles)
|
|
{
|
|
// 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 && !mShowInheritedMembers)
|
|
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 sortAlphabetically and sortByType then begin
|
|
// filtered.Sort(@CompareByAlphaAndType);
|
|
// end else if sortAlphabetically then begin
|
|
// filtered.Sort(@CompareByAlpha);
|
|
// end else if sortByType then begin
|
|
// filtered.Sort(@CompareByType);
|
|
// end;
|
|
}
|
|
|
|
PStatement ClassBrowserModel::createDummy(PStatement statement)
|
|
{
|
|
PStatement result = std::make_shared<Statement>();
|
|
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 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();
|
|
}
|
|
}
|
|
}
|
|
|
|
|