1/*
2 * Copyright (C) 2020-2022 Roy Qu (royqh1979@gmail.com)
3 *
4 * This program is free software: you can redistribute it and/or modify
5 * it under the terms of the GNU General Public License as published by
6 * the Free Software Foundation, either version 3 of the License, or
7 * (at your option) any later version.
8 *
9 * This program is distributed in the hope that it will be useful,
10 * but WITHOUT ANY WARRANTY; without even the implied warranty of
11 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
12 * GNU General Public License for more details.
13 *
14 * You should have received a copy of the GNU General Public License
15 * along with this program. If not, see <https://www.gnu.org/licenses/>.
16 */
17#include "classbrowser.h"
18#include "../utils.h"
19#include <QDebug>
20#include <QColor>
21#include <QPalette>
22#include "../mainwindow.h"
23#include "../settings.h"
24#include "../colorscheme.h"
25#include "../utils.h"
26#include "../iconsmanager.h"
27
28ClassBrowserModel::ClassBrowserModel(QObject *parent):QAbstractItemModel(parent),
29 mMutex(QMutex::Recursive)
30{
31 mRoot = new ClassBrowserNode();
32 mRoot->parent = nullptr;
33 mRoot->statement = PStatement();
34// mRoot->childrenFetched = true;
35 mUpdating = false;
36 mUpdateCount = 0;
37}
38
39ClassBrowserModel::~ClassBrowserModel()
40{
41 delete mRoot;
42}
43
44QModelIndex ClassBrowserModel::index(int row, int column, const QModelIndex &parent) const
45{
46 if (!hasIndex(row,column,parent))
47 return QModelIndex();
48
49 ClassBrowserNode *parentNode;
50 if (!parent.isValid()) { // top level
51 parentNode = mRoot;
52 } else {
53 parentNode = static_cast<ClassBrowserNode *>(parent.internalPointer());
54 }
55 return createIndex(row,column,parentNode->children[row]);
56}
57
58QModelIndex ClassBrowserModel::parent(const QModelIndex &child) const
59{
60 if (!child.isValid()) {
61 return QModelIndex();
62 }
63 ClassBrowserNode *childNode = static_cast<ClassBrowserNode *>(child.internalPointer());
64 ClassBrowserNode *parentNode = childNode->parent;
65 if (parentNode->parent == nullptr) //it's root node
66 return QModelIndex();
67
68 ClassBrowserNode *grandNode = parentNode->parent;
69 int row = grandNode->children.indexOf(parentNode);
70 return createIndex(row,0,parentNode);
71}
72
73bool ClassBrowserModel::hasChildren(const QModelIndex &parent) const
74{
75 ClassBrowserNode *parentNode;
76 if (!parent.isValid()) { // top level
77 return mRoot->children.count()>0;
78 } else {
79 parentNode = static_cast<ClassBrowserNode *>(parent.internalPointer());
80// if (parentNode->childrenFetched)
81 return parentNode->children.count()>0;
82// if (parentNode->statement)
83// return !parentNode->statement->children.isEmpty();
84// return false;
85 }
86}
87
88int ClassBrowserModel::rowCount(const QModelIndex &parent) const
89{
90 ClassBrowserNode *parentNode;
91 if (!parent.isValid()) { // top level
92 parentNode = mRoot;
93 } else {
94 parentNode = static_cast<ClassBrowserNode *>(parent.internalPointer());
95 }
96 return parentNode->children.count();
97}
98
99int ClassBrowserModel::columnCount(const QModelIndex&) const
100{
101 return 1;
102}
103
104//void ClassBrowserModel::fetchMore(const QModelIndex &parent)
105//{
106// if (!parent.isValid()) { // top level
107// return;
108// }
109
110// ClassBrowserNode *parentNode = static_cast<ClassBrowserNode *>(parent.internalPointer());
111// if (!parentNode->childrenFetched) {
112// parentNode->childrenFetched = true;
113// if (parentNode->statement && !parentNode->statement->children.isEmpty()) {
114// filterChildren(parentNode, parentNode->statement->children);
115// beginInsertRows(parent,0,parentNode->children.count());
116// endInsertRows();
117// }
118// }
119//}
120
121//bool ClassBrowserModel::canFetchMore(const QModelIndex &parent) const
122//{
123// if (!parent.isValid()) { // top level
124// return false;
125// }
126// ClassBrowserNode *parentNode = static_cast<ClassBrowserNode *>(parent.internalPointer());
127// if (!parentNode->childrenFetched) {
128// if (parentNode->statement && !parentNode->statement->children.isEmpty())
129// return true;
130// else
131// parentNode->childrenFetched = true;
132// }
133// return false;
134//}
135
136QVariant ClassBrowserModel::data(const QModelIndex &index, int role) const
137{
138 if (!index.isValid()){
139 return QVariant();
140 }
141 ClassBrowserNode *node = static_cast<ClassBrowserNode *>(index.internalPointer());
142 if (!node)
143 return QVariant();
144 if (role == Qt::DisplayRole) {
145 if (node->statement) {
146 if (!(node->statement->type.isEmpty()) &&
147 ((node->statement->kind == StatementKind::skFunction)
148 || (node->statement->kind == StatementKind::skVariable)
149 || (node->statement->kind == StatementKind::skTypedef)
150 )) {
151 return node->statement->command + node->statement->args + " : " + node->statement->type;
152 }
153 return node->statement->command + node->statement->args;
154 }
155 } else if (role == Qt::ForegroundRole) {
156 if (mColors && node->statement) {
157 PStatement statement = (node->statement);
158 StatementKind kind = getKindOfStatement(statement);
159 if (kind == StatementKind::skKeyword) {
160 if (statement->command.startsWith('#'))
161 kind = StatementKind::skPreprocessor;
162 }
163 PColorSchemeItem item = mColors->value(kind,PColorSchemeItem());
164 if (item) {
165 return item->foreground();
166 } else {
167 return pMainWindow->palette().color(QPalette::Text);
168 }
169 }
170 return pMainWindow->palette().color(QPalette::Text);
171 } else if (role == Qt::DecorationRole) {
172 if (node->statement) {
173 return pIconsManager->getPixmapForStatement(node->statement);
174 }
175 }
176 return QVariant();
177}
178
179const PCppParser &ClassBrowserModel::parser() const
180{
181 return mParser;
182}
183
184void ClassBrowserModel::setParser(const PCppParser &newCppParser)
185{
186 if (mParser) {
187 disconnect(mParser.get(),
188 &CppParser::onEndParsing,
189 this,
190 &ClassBrowserModel::fillStatements);
191 }
192 mParser = newCppParser;
193 if (mParser) {
194 connect(mParser.get(),
195 &CppParser::onEndParsing,
196 this,
197 &ClassBrowserModel::fillStatements);
198 } else {
199 clear();
200 }
201}
202
203void ClassBrowserModel::clear()
204{
205 beginResetModel();
206 mRoot->children.clear();
207 mNodes.clear();
208 mDummyStatements.clear();
209 endResetModel();
210}
211
212void ClassBrowserModel::fillStatements()
213{
214 {
215 QMutexLocker locker(&mMutex);
216 if (mUpdateCount!=0 || mUpdating)
217 return;
218 mUpdating = true;
219 }
220 beginResetModel();
221 clear();
222 {
223 auto action = finally([this]{
224 endResetModel();
225 mUpdating = false;
226 });
227 if (!mParser)
228 return;
229 if (!mParser->enabled())
230 return;
231 if (!mParser->freeze())
232 return;
233 {
234 auto action2 = finally([this]{
235 mParser->unFreeze();
236 });
237 QString mParserSerialId = mParser->serialId();
238 if (!mCurrentFile.isEmpty()) {
239 // QSet<QString> includedFiles = mParser->getFileIncludes(mCurrentFile);
240
241 addMembers();
242 // Remember selection
243// if fLastSelection <> '' then
244// ReSelect;
245 }
246
247 }
248 }
249}
250
251void ClassBrowserModel::addChild(ClassBrowserNode *node, PStatement statement)
252{
253 PClassBrowserNode newNode = std::make_shared<ClassBrowserNode>();
254 newNode->parent = node;
255 newNode->statement = statement;
256// newNode->childrenFetched = false;
257 node->children.append(newNode.get());
258 mNodes.append(newNode);
259 //don't show enum type's children values (they are displayed in parent scope)
260 if (statement->kind != StatementKind::skEnumType)
261 filterChildren(newNode.get(), statement->children);
262}
263
264void ClassBrowserModel::addMembers()
265{
266 // show statements in the file
267 PFileIncludes p = mParser->findFileIncludes(mCurrentFile);
268 if (!p)
269 return;
270 filterChildren(mRoot,p->statements);
271}
272
273void ClassBrowserModel::filterChildren(ClassBrowserNode *node, const StatementMap &statements)
274{
275 for (PStatement statement:statements) {
276 if (statement->kind == StatementKind::skBlock)
277 continue;
278 if (statement->isInherited && !pSettings->ui().classBrowserShowInherited())
279 continue;
280
281 if (statement == node->statement) // prevent infinite recursion
282 continue;
283
284 if (statement->scope == StatementScope::ssLocal)
285 continue;
286
287
288// if (fStatementsType = cbstProject) then begin
289// if not Statement^._InProject then
290// Continue;
291// if Statement^._Static and not SameText(Statement^._FileName,fCurrentFile)
292// and not SameText(Statement^._FileName,fCurrentFile) then
293// Continue;
294// end;
295
296 // we only test and handle orphan statements in the top level (node->statement is null)
297 PStatement parentScope = statement->parentScope.lock();
298 if ((parentScope!=node->statement) && (!node->statement)) {
299
300// // we only handle orphan statements when type is cbstFile
301// if fStatementsType <> cbstFile then
302// Continue;
303
304// //should not happend, just in case of error
305 if (!parentScope)
306 continue;
307
308 // Processing the orphan statement
309 while (statement) {
310 //the statement's parent is in this file, so it's not a real orphan
311 if ((parentScope->fileName==mCurrentFile)
312 ||(parentScope->definitionFileName==mCurrentFile))
313 break;
314
315 PStatement dummyParent = mDummyStatements.value(parentScope->fullName,PStatement());
316 if (dummyParent) {
317 dummyParent->children.insert(statement->command,statement);
318 break;
319 }
320 dummyParent = createDummy(parentScope);
321 dummyParent->children.insert(statement->command,statement);
322 //we are adding an orphan statement, just add it
323 statement = dummyParent;
324 parentScope = statement->parentScope.lock();
325 if (!parentScope) {
326 addChild(node,statement);
327
328 break;
329 }
330 }
331 } else if (statement->kind == StatementKind::skNamespace) {
332 PStatement dummy = mDummyStatements.value(statement->fullName,PStatement());
333 if (dummy) {
334 for (PStatement child: statement->children) {
335 dummy->children.insert(child->command,child);
336 }
337 continue;
338 }
339 dummy = createDummy(statement);
340 dummy->children = statement->children;
341 addChild(node,dummy);
342 } else {
343 addChild(node,statement);
344 }
345 }
346 if (pSettings->ui().classBrowserSortAlpha()
347 && pSettings->ui().classBrowserSortType()) {
348 std::sort(node->children.begin(),node->children.end(),
349 [](ClassBrowserNode* node1,ClassBrowserNode* node2) {
350 if (node1->statement->kind < node2->statement->kind) {
351 return true;
352 } else if (node1->statement->kind == node2->statement->kind) {
353 return node1->statement->command < node2->statement->command;
354 } else {
355 return false;
356 }
357 });
358 } else if (pSettings->ui().classBrowserSortAlpha()) {
359 std::sort(node->children.begin(),node->children.end(),
360 [](ClassBrowserNode* node1,ClassBrowserNode* node2) {
361 return node1->statement->command < node2->statement->command;
362 });
363 } else if (pSettings->ui().classBrowserSortType()) {
364 std::sort(node->children.begin(),node->children.end(),
365 [](ClassBrowserNode* node1,ClassBrowserNode* node2) {
366 return node1->statement->kind < node2->statement->kind;
367 });
368 }
369}
370
371PStatement ClassBrowserModel::createDummy(PStatement statement)
372{
373 PStatement result = std::make_shared<Statement>();
374 result->parentScope = statement->parentScope;
375 result->command = statement->command;
376 result->args = statement->args;
377 result->noNameArgs = statement->noNameArgs;
378 result->fullName = statement->fullName;
379 result->kind = statement->kind;
380 result->type = statement->type;
381 result->value = statement->value;
382 result->scope = statement->scope;
383 result->classScope = statement->classScope;
384 result->inProject = statement->inProject;
385 result->inSystemHeader = statement->inSystemHeader;
386 result->isStatic = statement->isStatic;
387 result->isInherited = statement->isInherited;
388 result->fileName = mCurrentFile;
389 result->definitionFileName = mCurrentFile;
390 result->line = 0;
391 result->definitionLine = 0;
392 mDummyStatements.insert(result->fullName,result);
393 return result;
394}
395
396const std::shared_ptr<QHash<StatementKind, std::shared_ptr<ColorSchemeItem> > > &ClassBrowserModel::colors() const
397{
398 return mColors;
399}
400
401void ClassBrowserModel::setColors(const std::shared_ptr<QHash<StatementKind, std::shared_ptr<ColorSchemeItem> > > &newColors)
402{
403 mColors = newColors;
404}
405
406const QString &ClassBrowserModel::currentFile() const
407{
408 return mCurrentFile;
409}
410
411void ClassBrowserModel::setCurrentFile(const QString &newCurrentFile)
412{
413 mCurrentFile = newCurrentFile;
414}
415
416void ClassBrowserModel::beginUpdate()
417{
418 mUpdateCount++;
419}
420
421void ClassBrowserModel::endUpdate()
422{
423 mUpdateCount--;
424 if (mUpdateCount == 0) {
425 if (mParser && !mParser->parsing()) {
426 this->fillStatements();
427 }
428 }
429}
430