1// SPDX-FileCopyrightText: 2023 UnionTech Software Technology Co., Ltd.
2//
3// SPDX-License-Identifier: GPL-3.0-or-later
4
5#include "taskfiltermodel.h"
6
7namespace ReverseDebugger {
8namespace Internal {
9
10TaskFilterModel::TaskFilterModel(TaskModel *sourceModel, QObject *parent) : QAbstractItemModel(parent),
11 sourceModel(sourceModel)
12{
13 Q_ASSERT(sourceModel);
14 updateMapping();
15
16 connect(sourceModel, &QAbstractItemModel::rowsInserted,
17 this, &TaskFilterModel::handleNewRows);
18 connect(sourceModel, &QAbstractItemModel::rowsAboutToBeRemoved,
19 this, &TaskFilterModel::handleRowsAboutToBeRemoved);
20 connect(sourceModel, &QAbstractItemModel::modelReset,
21 this, &TaskFilterModel::handleReset);
22 connect(sourceModel, &QAbstractItemModel::dataChanged,
23 this, &TaskFilterModel::handleDataChanged);
24
25 sortType = 0;
26 tid = -1;
27 eventBegin = -1;
28 eventEnd = -1;
29 eventIndexBegin = -1;
30 eventIndexEnd = -1;
31}
32QModelIndex TaskFilterModel::index(int row, int column, const QModelIndex &parent) const
33{
34 if (parent.isValid())
35 return QModelIndex();
36 return createIndex(row, column);
37}
38
39QModelIndex TaskFilterModel::parent(const QModelIndex &child) const
40{
41 Q_UNUSED(child)
42 return QModelIndex();
43}
44
45int TaskFilterModel::rowCount(const QModelIndex &parent) const
46{
47 if (parent.isValid())
48 return 0;
49
50 return rowMapping.count();
51}
52
53int TaskFilterModel::columnCount(const QModelIndex &parent) const
54{
55 if (parent.isValid())
56 return 0;
57 return sourceModel->columnCount(parent);
58}
59
60QVariant TaskFilterModel::data(const QModelIndex &index, int role) const
61{
62 return sourceModel->data(mapToSource(index), role);
63}
64
65QModelIndex TaskFilterModel::mapFromSource(const QModelIndex &idx) const
66{
67 QList<int>::const_iterator it = std::lower_bound(rowMapping.constBegin(), rowMapping.constEnd(), idx.row());
68 if (it == rowMapping.constEnd())
69 return QModelIndex();
70 return index(it - rowMapping.constBegin(), 0);
71}
72
73void TaskFilterModel::handleNewRows(const QModelIndex &index, int first, int last)
74{
75 if (index.isValid())
76 return;
77
78 QList<int> newMapping;
79 for (int i = first; i <= last; ++i) {
80 const Task &task = sourceModel->task(sourceModel->index(i, 0));
81 if (filterAcceptsTask(task))
82 newMapping.append(i);
83 }
84
85 const int newItems = newMapping.count();
86 if (!newItems)
87 return;
88
89 int filteredFirst = -1;
90 if (last == sourceModel->rowCount() - 1)
91 filteredFirst = rowMapping.count();
92 else
93 filteredFirst = std::lower_bound(rowMapping.constBegin(), rowMapping.constEnd(), first) - rowMapping.constBegin();
94
95 const int filteredLast = filteredFirst + newItems - 1;
96 beginInsertRows(QModelIndex(), filteredFirst, filteredLast);
97 if (filteredFirst == rowMapping.count()) {
98 rowMapping.append(newMapping);
99 } else {
100 QList<int> rest = rowMapping.mid(filteredFirst);
101
102 rowMapping.reserve(rowMapping.count() + newItems);
103 rowMapping.erase(rowMapping.begin() + filteredFirst, rowMapping.end());
104 rowMapping.append(newMapping);
105 foreach (int pos, rest)
106 rowMapping.append(pos + newItems);
107 }
108 endInsertRows();
109}
110
111static QPair<int, int> findFilteredRange(int first, int last, const QList<int> &list)
112{
113 QList<int>::const_iterator filteredFirst = std::lower_bound(list.constBegin(), list.constEnd(), first);
114 QList<int>::const_iterator filteredLast = std::upper_bound(filteredFirst, list.constEnd(), last);
115 return qMakePair(filteredFirst - list.constBegin(), filteredLast - list.constBegin() - 1);
116}
117
118
119void TaskFilterModel::handleRowsAboutToBeRemoved(const QModelIndex &index, int first, int last)
120{
121 if (index.isValid())
122 return;
123
124 const QPair<int, int> range = findFilteredRange(first, last, rowMapping);
125 if (range.first > range.second)
126 return;
127
128 beginRemoveRows(QModelIndex(), range.first, range.second);
129 rowMapping.erase(rowMapping.begin() + range.first, rowMapping.begin() + range.second + 1);
130 for (int i = range.first; i < rowMapping.count(); ++i)
131 rowMapping[i] = rowMapping.at(i) - (last - first) - 1;
132 endRemoveRows();
133}
134
135void TaskFilterModel::handleDataChanged(const QModelIndex &top, const QModelIndex &bottom)
136{
137 const QPair<int, int> range = findFilteredRange(top.row(), bottom.row(), rowMapping);
138 if (range.first > range.second)
139 return;
140
141 emit dataChanged(index(range.first, top.column()), index(range.second, bottom.column()));
142}
143
144void TaskFilterModel::handleReset()
145{
146 invalidateFilter();
147}
148
149QModelIndex TaskFilterModel::mapToSource(const QModelIndex &index) const
150{
151 int row = index.row();
152 if (row >= rowMapping.count())
153 return QModelIndex();
154 return sourceModel->index(rowMapping.at(row), index.column(), index.parent());
155}
156
157void TaskFilterModel::invalidateFilter()
158{
159 beginResetModel();
160 updateMapping();
161 endResetModel();
162}
163
164static const EventEntry* g_event_table = nullptr;
165
166bool sort_duration(const int v1, const int v2)
167{
168 return g_event_table[v1].duration < g_event_table[v2].duration;
169}
170
171bool sort_result(const int v1, const int v2)
172{
173 return g_event_table[v1].syscall_result < g_event_table[v2].syscall_result;
174}
175
176bool sort_threads_number(const int v1, const int v2)
177{
178 return g_event_table[v1].thread_num < g_event_table[v2].thread_num;
179}
180
181void TaskFilterModel::updateMapping() const
182{
183 rowMapping.clear();
184 for (int i = 0; i < sourceModel->rowCount(); ++i) {
185 QModelIndex index = sourceModel->index(i, 0);
186 const Task &task = sourceModel->task(index);
187 if (filterAcceptsTask(task)) {
188 if (tid > 0 && tid != task.event->tid) {
189 continue;
190 }
191
192 if (eventIndexBegin >= 0 &&
193 eventIndexEnd >= eventIndexBegin &&
194 (i < eventIndexBegin || i > eventIndexEnd)){
195 // not in index range!
196 continue;
197 }
198
199 if (eventBegin >= 0 &&
200 eventEnd >= eventBegin &&
201 (task.event->type < eventBegin || task.event->type > eventEnd)){
202 // not in event range!
203 continue;
204 }
205 rowMapping.append(i);
206 }
207 }
208
209 const Task &task = sourceModel->task(0);
210 g_event_table = task.event;
211 switch (sortType) {
212 case 1:
213 std::sort(rowMapping.begin(), rowMapping.end(), sort_duration);
214 break;
215 case 2:
216 std::sort(rowMapping.begin(), rowMapping.end(), sort_result);
217 break;
218 case 3:
219 std::sort(rowMapping.begin(), rowMapping.end(), sort_threads_number);
220 break;
221 default:
222 break;
223 }
224}
225
226bool TaskFilterModel::filterAcceptsTask(const Task &task) const
227{
228 bool accept = true;
229
230 if (categoryIds.contains(task.category))
231 accept = false;
232
233 return accept;
234}
235
236
237
238} // namespace Internal
239} // namespace ReverseDebugger
240
241