GCC Code Coverage Report


Directory: ./
File: src/MessageModelUnion.cpp
Date: 2026-09-08 09:09:37
Exec Total Coverage
Lines: 118 173 68.2%
Branches: 88 271 32.5%

Line Branch Exec Source
1 /*****************************************************************************
2 *
3 * Copyright (C) 2009 - 2025 Florian Pose <fp@igh.de>
4 *
5 * This file is part of the QtPdCom library.
6 *
7 * The QtPdCom library is free software: you can redistribute it and/or modify
8 * it under the terms of the GNU Lesser General Public License as published by
9 * the Free Software Foundation, either version 3 of the License, or (at your
10 * option) any later version.
11 *
12 * The QtPdCom library is distributed in the hope that it will be useful, but
13 * WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY
14 * or FITNESS FOR A PARTICULAR PURPOSE. See the GNU Lesser General Public
15 * License for more details.
16 *
17 * You should have received a copy of the GNU Lesser General Public License
18 * along with the QtPdCom Library. If not, see <http://www.gnu.org/licenses/>.
19 *
20 ****************************************************************************/
21
22 #include "MessageModelUnion.h"
23 using QtPdCom::MessageModelUnion;
24
25 #include "MessageModel.h"
26
27 #include <QAbstractProxyModel>
28
29 #if PD_DEBUG_MESSAGE_MODEL
30 #include <QDebug>
31 #endif
32
33 /*****************************************************************************
34 * MessageModelUnion::Impl
35 ****************************************************************************/
36
37 3 struct MessageModelUnion::Impl
38 {
39 Impl() = delete;
40 3 Impl(MessageModelUnion *parent):
41 3 parentModel {parent}
42 3 {}
43
44 MessageModelUnion *const parentModel;
45
46
1/2
✓ Branch 2 taken 5 times.
✗ Branch 3 not taken.
6 struct SourceModel
47 {
48 10 ~SourceModel() { QObject::disconnect(connection); }
49
50 QAbstractItemModel *model;
51 QString name;
52 const QtPdCom::Message *announcedMessage;
53 QMetaObject::Connection connection;
54 };
55 QList<SourceModel> sourceModels;
56 const QtPdCom::Message *lastAnnounced {nullptr};
57
58 struct SourceEntry
59 {
60 SourceModel *sourceModel;
61 int row; // original row in source model
62 };
63 QVector<SourceEntry> mergedRows;
64
65 enum { SortColumn = 1 };
66
67 22 void rebuildMergedRows()
68 {
69 22 parentModel->beginResetModel();
70 22 mergedRows.clear();
71
72
5/8
✓ Branch 2 taken 22 times.
✗ Branch 3 not taken.
✓ Branch 6 taken 22 times.
✗ Branch 7 not taken.
✓ Branch 9 taken 26 times.
✓ Branch 10 taken 22 times.
✓ Branch 12 taken 26 times.
✗ Branch 13 not taken.
48 for (auto &sourceModel : sourceModels) {
73
1/2
✓ Branch 3 taken 26 times.
✗ Branch 4 not taken.
26 int rows = sourceModel.model->rowCount();
74
2/2
✓ Branch 0 taken 7 times.
✓ Branch 1 taken 26 times.
33 for (int row = 0; row < rows; ++row) {
75
1/2
✓ Branch 2 taken 7 times.
✗ Branch 3 not taken.
7 mergedRows.append({&sourceModel, row});
76 }
77 }
78
79 22 std::sort(
80 mergedRows.begin(),
81 mergedRows.end(),
82 2 [this](const SourceEntry &a, const SourceEntry &b) {
83 2 QVariant va = a.sourceModel->model->data(
84
1/2
✓ Branch 2 taken 2 times.
✗ Branch 3 not taken.
8 a.sourceModel->model->index(
85 2 a.row,
86
1/2
✓ Branch 3 taken 2 times.
✗ Branch 4 not taken.
12 SortColumn));
87 2 QVariant vb = b.sourceModel->model->data(
88
1/2
✓ Branch 2 taken 2 times.
✗ Branch 3 not taken.
8 b.sourceModel->model->index(
89 2 b.row,
90
1/2
✓ Branch 3 taken 2 times.
✗ Branch 4 not taken.
12 SortColumn));
91
2/4
✓ Branch 2 taken 2 times.
✗ Branch 3 not taken.
✓ Branch 6 taken 2 times.
✗ Branch 7 not taken.
4 return vb.toString() < va.toString();
92 });
93
94 #if PD_DEBUG_MESSAGE_MODEL
95
4/8
✓ Branch 3 taken 22 times.
✗ Branch 4 not taken.
✓ Branch 6 taken 22 times.
✗ Branch 7 not taken.
✓ Branch 9 taken 22 times.
✗ Branch 10 not taken.
✓ Branch 13 taken 22 times.
✗ Branch 14 not taken.
44 qDebug() << __func__ << "(): Now " << mergedRows.size()
96
1/2
✓ Branch 2 taken 22 times.
✗ Branch 3 not taken.
44 << " rows.";
97 #endif
98 22 parentModel->endResetModel();
99 22 }
100
101 8 void currentMessage(
102 SourceModel *sourceModel,
103 const QtPdCom::Message *message)
104 {
105 #if PD_DEBUG_MESSAGE_MODEL
106
4/8
✓ Branch 6 taken 8 times.
✗ Branch 7 not taken.
✓ Branch 9 taken 8 times.
✗ Branch 10 not taken.
✓ Branch 12 taken 8 times.
✗ Branch 13 not taken.
✓ Branch 16 taken 8 times.
✗ Branch 17 not taken.
8 qDebug() << __func__ << sourceModel << message;
107 #endif
108 8 sourceModel->announcedMessage = message;
109 8 updateCurrentMessage();
110 8 }
111
112 14 void updateCurrentMessage()
113 {
114 14 const QtPdCom::Message *candidate {nullptr};
115
5/8
✓ Branch 2 taken 14 times.
✗ Branch 3 not taken.
✓ Branch 6 taken 14 times.
✗ Branch 7 not taken.
✓ Branch 9 taken 20 times.
✓ Branch 10 taken 14 times.
✓ Branch 12 taken 20 times.
✗ Branch 13 not taken.
34 for (auto &sourceModel : sourceModels) {
116
2/2
✓ Branch 0 taken 7 times.
✓ Branch 1 taken 13 times.
20 if (sourceModel.announcedMessage) {
117
2/2
✓ Branch 0 taken 6 times.
✓ Branch 1 taken 1 times.
7 if (not candidate
118
4/6
✓ Branch 0 taken 1 times.
✓ Branch 1 taken 6 times.
✓ Branch 3 taken 1 times.
✗ Branch 4 not taken.
✗ Branch 5 not taken.
✓ Branch 6 taken 1 times.
8 or sourceModel.announcedMessage->getType()
119
1/2
✓ Branch 1 taken 1 times.
✗ Branch 2 not taken.
1 > candidate->getType()) {
120 6 candidate = sourceModel.announcedMessage;
121 }
122 }
123 }
124 #if PD_DEBUG_MESSAGE_MODEL
125
3/6
✓ Branch 6 taken 14 times.
✗ Branch 7 not taken.
✓ Branch 9 taken 14 times.
✗ Branch 10 not taken.
✓ Branch 13 taken 14 times.
✗ Branch 14 not taken.
14 qDebug() << __func__ << candidate;
126 #endif
127
2/2
✓ Branch 0 taken 8 times.
✓ Branch 1 taken 6 times.
14 if (candidate == lastAnnounced) {
128 8 return;
129 }
130 6 lastAnnounced = candidate;
131 6 emit parentModel->currentMessage(candidate);
132 }
133 };
134
135 /*****************************************************************************
136 * MessageModelUnion
137 ****************************************************************************/
138
139 /** Constructor.
140 */
141 3 MessageModelUnion::MessageModelUnion(QObject *parent):
142 QAbstractTableModel(parent),
143
1/2
✓ Branch 2 taken 3 times.
✗ Branch 3 not taken.
3 impl(std::unique_ptr<Impl>(new MessageModelUnion::Impl(this)))
144 3 {}
145
146 /****************************************************************************/
147
148 /** Destructor.
149 */
150 9 MessageModelUnion::~MessageModelUnion()
151 {
152 3 clearSourceModels();
153 6 }
154
155 /****************************************************************************/
156
157 5 void MessageModelUnion::addSourceModel(
158 QAbstractItemModel *model,
159 QString sourceName)
160 {
161
1/2
✓ Branch 5 taken 5 times.
✗ Branch 6 not taken.
5 impl->sourceModels.push_back(
162 {model,
163 sourceName,
164 nullptr, // announced message
165 QMetaObject::Connection()});
166 5 auto *sourceModel = &impl->sourceModels.last();
167
168 9 connect(model, &QAbstractTableModel::rowsInserted, [this]() {
169 4 impl->rebuildMergedRows();
170 4 });
171 5 connect(model, &QAbstractTableModel::rowsRemoved, [this]() {
172 ✗ impl->rebuildMergedRows();
173 ✗ });
174 5 connect(model, &QAbstractTableModel::dataChanged, [this]() {
175 ✗ impl->rebuildMergedRows();
176 ✗ });
177 5 connect(model, &QAbstractTableModel::layoutChanged, [this]() {
178 ✗ impl->rebuildMergedRows();
179 ✗ });
180 17 connect(model, &QAbstractTableModel::modelReset, [this]() {
181 12 impl->rebuildMergedRows();
182 12 });
183
184 // find a QtPdCom::MessageModel after a chain of QAbstractProxyModels
185 5 QtPdCom::MessageModel *messageModel {nullptr};
186 while (true) {
187 5 auto *proxy {qobject_cast<QAbstractProxyModel *>(model)};
188
1/2
✓ Branch 0 taken 5 times.
✗ Branch 1 not taken.
5 if (not proxy) {
189 5 messageModel = qobject_cast<QtPdCom::MessageModel *>(model);
190 5 break;
191 }
192 ✗ model = proxy->sourceModel();
193 }
194
195
1/2
✓ Branch 0 taken 5 times.
✗ Branch 1 not taken.
5 if (messageModel) {
196 10 sourceModel->connection =
197 10 connect(messageModel,
198 &MessageModel::currentMessage,
199 16 [this, sourceModel](const QtPdCom::Message *message) {
200 16 impl->currentMessage(sourceModel, message);
201 13 });
202 }
203
204 5 impl->rebuildMergedRows();
205 5 impl->updateCurrentMessage();
206 5 }
207
208 /****************************************************************************/
209
210 1 void MessageModelUnion::removeSourceModel(QAbstractItemModel *model)
211 {
212 1 auto it = std::remove_if(
213
1/2
✓ Branch 2 taken 1 times.
✗ Branch 3 not taken.
2 impl->sourceModels.begin(),
214
1/2
✓ Branch 2 taken 1 times.
✗ Branch 3 not taken.
2 impl->sourceModels.end(),
215
1/2
✓ Branch 3 taken 1 times.
✗ Branch 4 not taken.
5 [model](const Impl::SourceModel &s) { return s.model == model; });
216
2/4
✓ Branch 5 taken 1 times.
✗ Branch 6 not taken.
✓ Branch 10 taken 1 times.
✗ Branch 11 not taken.
1 impl->sourceModels.erase(it, impl->sourceModels.end());
217
218
1/2
✓ Branch 2 taken 1 times.
✗ Branch 3 not taken.
1 impl->rebuildMergedRows();
219
1/2
✓ Branch 2 taken 1 times.
✗ Branch 3 not taken.
1 impl->updateCurrentMessage();
220 1 }
221
222 /****************************************************************************/
223
224 4 void MessageModelUnion::clearSourceModels()
225 {
226 4 beginResetModel();
227 4 impl->sourceModels.clear();
228 4 endResetModel();
229 4 }
230
231 /****************************************************************************/
232
233 24 int MessageModelUnion::rowCount(const QModelIndex &) const
234 {
235 24 return impl->mergedRows.count();
236 }
237
238 /****************************************************************************/
239
240 14 int MessageModelUnion::columnCount(const QModelIndex &) const
241 {
242 14 return 4;
243 }
244
245 /****************************************************************************/
246
247 14 QVariant MessageModelUnion::data(const QModelIndex &index, int role) const
248 {
249 14 QVariant ret;
250
251
3/6
✓ Branch 1 taken 14 times.
✗ Branch 2 not taken.
✓ Branch 6 taken 14 times.
✗ Branch 7 not taken.
✓ Branch 8 taken 14 times.
✗ Branch 9 not taken.
14 if (index.isValid() and index.row() < impl->mergedRows.size()) {
252
1/2
✓ Branch 3 taken 14 times.
✗ Branch 4 not taken.
14 auto entry {impl->mergedRows[index.row()]};
253 14 QModelIndex sourceIndex =
254
1/2
✓ Branch 4 taken 14 times.
✗ Branch 5 not taken.
14 entry.sourceModel->model->index(entry.row, index.column());
255
5/6
✓ Branch 1 taken 14 times.
✗ Branch 2 not taken.
✓ Branch 4 taken 12 times.
✓ Branch 5 taken 2 times.
✓ Branch 6 taken 12 times.
✓ Branch 7 taken 2 times.
14 if (index.column() >= TextColumn and index.column() < SourceColumn) {
256
1/2
✓ Branch 2 taken 12 times.
✗ Branch 3 not taken.
12 ret = entry.sourceModel->model->data(sourceIndex, role);
257 }
258
3/6
✓ Branch 1 taken 2 times.
✗ Branch 2 not taken.
✓ Branch 3 taken 2 times.
✗ Branch 4 not taken.
✓ Branch 5 taken 2 times.
✗ Branch 6 not taken.
2 else if (index.column() == SourceColumn and role == Qt::DisplayRole) {
259
1/2
✓ Branch 2 taken 2 times.
✗ Branch 3 not taken.
2 ret = entry.sourceModel->name;
260 }
261 }
262
263 #if PD_DEBUG_MESSAGE_MODEL
264
5/10
✓ Branch 8 taken 14 times.
✗ Branch 9 not taken.
✓ Branch 11 taken 14 times.
✗ Branch 12 not taken.
✓ Branch 15 taken 14 times.
✗ Branch 16 not taken.
✓ Branch 18 taken 14 times.
✗ Branch 19 not taken.
✓ Branch 22 taken 14 times.
✗ Branch 23 not taken.
14 qDebug() << __func__ << index << role << ret;
265 #endif
266 14 return ret;
267 }
268
269 /****************************************************************************/
270
271 QVariant
272 ✗ MessageModelUnion::headerData(int section, Qt::Orientation o, int role) const
273 {
274 ✗ if (role == Qt::DisplayRole && o == Qt::Horizontal) {
275 ✗ switch (section) {
276 ✗ case TextColumn:
277 ✗ return tr("Message");
278
279 ✗ case TimeOccurredColumn:
280 ✗ return tr("Time");
281
282 ✗ case TimeResetColumn:
283 ✗ return tr("Reset");
284
285 ✗ case SourceColumn:
286 ✗ return tr("Source");
287
288 ✗ default:
289 ✗ return QVariant();
290 }
291 }
292 else {
293 ✗ return QVariant();
294 }
295 }
296
297 /****************************************************************************/
298
299 4 Qt::ItemFlags MessageModelUnion::flags(const QModelIndex &index) const
300 {
301 4 Qt::ItemFlags ret;
302
303
3/6
✓ Branch 1 taken 4 times.
✗ Branch 2 not taken.
✓ Branch 6 taken 4 times.
✗ Branch 7 not taken.
✓ Branch 8 taken 4 times.
✗ Branch 9 not taken.
4 if (index.isValid() and index.row() < impl->mergedRows.size()) {
304
1/2
✓ Branch 3 taken 4 times.
✗ Branch 4 not taken.
4 auto entry {impl->mergedRows[index.row()]};
305 4 QModelIndex sourceIndex =
306
1/2
✓ Branch 4 taken 4 times.
✗ Branch 5 not taken.
4 entry.sourceModel->model->index(entry.row, index.column());
307
3/6
✓ Branch 1 taken 4 times.
✗ Branch 2 not taken.
✓ Branch 4 taken 4 times.
✗ Branch 5 not taken.
✓ Branch 6 taken 4 times.
✗ Branch 7 not taken.
4 if (index.column() >= TextColumn and index.column() < SourceColumn) {
308
1/2
✓ Branch 1 taken 4 times.
✗ Branch 2 not taken.
4 ret = entry.sourceModel->model->flags(sourceIndex);
309 }
310 ✗ else if (index.column() == SourceColumn) {
311 ✗ QModelIndex textIndex =
312 ✗ entry.sourceModel->model->index(entry.row, TextColumn);
313 ✗ ret = entry.sourceModel->model->flags(textIndex);
314 }
315 }
316
317 #if PD_DEBUG_MESSAGE_MODEL
318
4/8
✓ Branch 7 taken 4 times.
✗ Branch 8 not taken.
✓ Branch 10 taken 4 times.
✗ Branch 11 not taken.
✓ Branch 14 taken 4 times.
✗ Branch 15 not taken.
✓ Branch 17 taken 4 times.
✗ Branch 18 not taken.
4 qDebug() << __func__ << index << ret;
319 #endif
320 4 return ret;
321 }
322
323 /****************************************************************************/
324
325 ✗ bool MessageModelUnion::canFetchMore(const QModelIndex &index) const
326 {
327 #if PD_DEBUG_MESSAGE_MODEL
328 ✗ qDebug() << __func__ << index;
329 #endif
330 ✗ if (not index.isValid()) {
331 // root layer - return true if *any* model can fetch more rows
332 ✗ for (auto &sourceModel : impl->sourceModels) {
333 ✗ if (sourceModel.model->canFetchMore(index)) {
334 #if PD_DEBUG_MESSAGE_MODEL
335 ✗ qDebug() << sourceModel.model << sourceModel.name << "yes";
336 #endif
337 ✗ return true;
338 }
339 }
340 #if PD_DEBUG_MESSAGE_MODEL
341 ✗ qDebug() << "no";
342 #endif
343 ✗ return false;
344 }
345 ✗ else if (index.row() < impl->mergedRows.size()) {
346 ✗ auto entry {impl->mergedRows[index.row()]};
347 ✗ auto sourceIndex {
348 ✗ entry.sourceModel->model->index(entry.row, index.column())};
349 ✗ auto canFetch {entry.sourceModel->model->canFetchMore(sourceIndex)};
350 #if PD_DEBUG_MESSAGE_MODEL
351 ✗ qDebug() << "specific" << canFetch;
352 #endif
353 ✗ return canFetch;
354 }
355 else {
356 #if PD_DEBUG_MESSAGE_MODEL
357 ✗ qDebug() << "invalid";
358 #endif
359 ✗ return false;
360 }
361 }
362
363 /****************************************************************************/
364
365 ✗ void MessageModelUnion::fetchMore(const QModelIndex &index)
366 {
367 #if PD_DEBUG_MESSAGE_MODEL
368 ✗ qDebug() << __func__ << index;
369 #endif
370 ✗ if (not index.isValid()) {
371 // root layer - pass on to *any* model that can fetch more rows
372 ✗ for (auto &sourceModel : impl->sourceModels) {
373 ✗ if (sourceModel.model->canFetchMore(index)) {
374 #if PD_DEBUG_MESSAGE_MODEL
375 ✗ qDebug() << __func__ << sourceModel.model << sourceModel.name;
376 #endif
377 ✗ sourceModel.model->fetchMore(index);
378 }
379 }
380 }
381 ✗ else if (index.row() < impl->mergedRows.size()) {
382 ✗ auto entry {impl->mergedRows[index.row()]};
383 ✗ auto sourceIndex {
384 ✗ entry.sourceModel->model->index(entry.row, index.column())};
385 ✗ entry.sourceModel->model->fetchMore(sourceIndex);
386 }
387 }
388
389 /****************************************************************************/
390