1// SPDX-FileCopyrightText: 2023 UnionTech Software Technology Co., Ltd.
2//
3// SPDX-License-Identifier: GPL-3.0-or-later
4
5#include "stackframemodel.h"
6
7#include <QFile>
8
9enum StackColumns {
10 kStackLevelColumn,
11 kStackFunctionNameColumn,
12 kStackFileNameColumn,
13 kStackLineNumberColumn,
14 kStackAddressColumn,
15 kStackColumnCount
16};
17
18StackFrameModel::StackFrameModel()
19{
20 setObjectName("StackModel");
21}
22
23StackFrameModel::~StackFrameModel() = default;
24
25int StackFrameModel::rowCount(const QModelIndex &parent) const
26{
27 // Since the stack is not a tree, row count is 0 for any valid parent
28 return parent.isValid() ? 0 : (stackFrames.size() + canExpand);
29}
30
31int StackFrameModel::columnCount(const QModelIndex &parent) const
32{
33 return parent.isValid() ? 0 : kStackColumnCount;
34}
35
36QVariant StackFrameModel::data(const QModelIndex &index, int role) const
37{
38 if (!index.isValid() || index.row() >= stackFrames.size() + canExpand)
39 return QVariant();
40
41 if (index.row() == stackFrames.size()) {
42 if (role == Qt::DisplayRole && index.column() == kStackLevelColumn)
43 return tr("...");
44 if (role == Qt::DisplayRole && index.column() == kStackFunctionNameColumn)
45 return tr("<More>");
46 if (role == Qt::DecorationRole && index.column() == kStackLevelColumn)
47 return "";
48 return QVariant();
49 }
50
51 const StackFrameData &frame = stackFrames.at(index.row());
52
53 if (role == Qt::DisplayRole) {
54 switch (index.column()) {
55 case kStackLevelColumn:
56 return QString::number(index.row() + 1);
57 case kStackFunctionNameColumn:
58 return frame.function;
59 case kStackFileNameColumn:
60 return frame.file.isEmpty() ? frame.module : frame.file;
61 case kStackLineNumberColumn:
62 return frame.line > 0 ? QVariant(frame.line) : QVariant();
63 case kStackAddressColumn:
64 return frame.address;
65 }
66 return QVariant();
67 }
68
69 if (role == Qt::ToolTipRole)
70 return frame.toToolTip();
71
72 return QVariant();
73}
74
75QVariant StackFrameModel::headerData(int section, Qt::Orientation orient, int role) const
76{
77 if (orient == Qt::Horizontal && role == Qt::DisplayRole) {
78 switch (section) {
79 case kStackLevelColumn:
80 return tr("Level");
81 case kStackFunctionNameColumn:
82 return tr("Function");
83 case kStackFileNameColumn:
84 return tr("File");
85 case kStackLineNumberColumn:
86 return tr("Line");
87 case kStackAddressColumn:
88 return tr("Address");
89 };
90 }
91 return QVariant();
92}
93
94void StackFrameModel::setCurrentIndex(int level)
95{
96 if (level == -1 || level == currentIndex)
97 return;
98
99 // Emit changed for previous frame
100 QModelIndex i = index(currentIndex, 0);
101 emit dataChanged(i, i);
102
103 currentIndex = level;
104 emit currentIndexChanged();
105
106 // Emit changed for new frame
107 i = index(currentIndex, 0);
108 emit dataChanged(i, i);
109}
110
111void StackFrameModel::removeAll()
112{
113 beginResetModel();
114 stackFrames.clear();
115 setCurrentIndex(-1);
116 endResetModel();
117}
118
119StackFrameData StackFrameModel::currentFrame() const
120{
121 if (currentIndex == -1 || stackFrames.size() == 0)
122 return StackFrameData();
123
124 return stackFrames.at(currentIndex);
125}
126
127bool StackFrameModel::setData(const QModelIndex &idx, const QVariant &data, int role)
128{
129 Q_UNUSED(data)
130 if (role == ItemActivatedRole || role == ItemClickedRole) {
131 setCurrentIndex(idx.row());
132 return true;
133 }
134
135 return false;
136}
137
138void StackFrameModel::setFrames(const StackFrames &frames, bool canExpand)
139{
140 beginResetModel();
141 contentsValid = true;
142 this->canExpand = canExpand;
143 stackFrames = frames;
144
145 if (stackFrames.size() > 0) {
146 for (int i = 0; i < stackFrames.size(); i++) {
147 bool bExists = QFile::exists(stackFrames[i].file);
148 if (bExists) {
149 setCurrentIndex(i);
150 break;
151 }
152 }
153 } else {
154 currentIndex = -1;
155 }
156
157 endResetModel();
158 emit stackChanged();
159}
160