Changeset View
Changeset View
Standalone View
Standalone View
libtaskmanager/taskgroupingproxymodel.cpp
Show All 32 Lines | 32 | public: | |||
---|---|---|---|---|---|
33 | Private(TaskGroupingProxyModel *q); | 33 | Private(TaskGroupingProxyModel *q); | ||
34 | 34 | | |||
35 | AbstractTasksModelIface *abstractTasksSourceModel = nullptr; | 35 | AbstractTasksModelIface *abstractTasksSourceModel = nullptr; | ||
36 | 36 | | |||
37 | TasksModel::GroupMode groupMode = TasksModel::GroupApplications; | 37 | TasksModel::GroupMode groupMode = TasksModel::GroupApplications; | ||
38 | bool groupDemandingAttention = false; | 38 | bool groupDemandingAttention = false; | ||
39 | int windowTasksThreshold = -1; | 39 | int windowTasksThreshold = -1; | ||
40 | 40 | | |||
41 | QVector<QVector<int>> rowMap; | 41 | QVector<QVector<int> *> rowMap; | ||
davidedmundson: Cleanup on destruction.
(which sounds like the name of a new Megadeth single) | |||||
hein: Will do. | |||||
42 | 42 | | |||
43 | QSet<QString> blacklistedAppIds; | 43 | QSet<QString> blacklistedAppIds; | ||
44 | QSet<QString> blacklistedLauncherUrls; | 44 | QSet<QString> blacklistedLauncherUrls; | ||
45 | 45 | | |||
46 | bool isGroup(int row); | 46 | bool isGroup(int row); | ||
47 | bool any(const QModelIndex &parent, int role); | 47 | bool any(const QModelIndex &parent, int role); | ||
48 | bool all(const QModelIndex &parent, int role); | 48 | bool all(const QModelIndex &parent, int role); | ||
49 | 49 | | |||
Show All 25 Lines | |||||
75 | } | 75 | } | ||
76 | 76 | | |||
77 | bool TaskGroupingProxyModel::Private::isGroup(int row) | 77 | bool TaskGroupingProxyModel::Private::isGroup(int row) | ||
78 | { | 78 | { | ||
79 | if (row < 0 || row >= rowMap.count()) { | 79 | if (row < 0 || row >= rowMap.count()) { | ||
80 | return false; | 80 | return false; | ||
81 | } | 81 | } | ||
82 | 82 | | |||
83 | return (rowMap.at(row).count() > 1); | 83 | return (rowMap.at(row)->count() > 1); | ||
84 | } | 84 | } | ||
85 | 85 | | |||
86 | bool TaskGroupingProxyModel::Private::any(const QModelIndex &parent, int role) | 86 | bool TaskGroupingProxyModel::Private::any(const QModelIndex &parent, int role) | ||
87 | { | 87 | { | ||
88 | bool is = false; | 88 | bool is = false; | ||
89 | 89 | | |||
90 | for (int i = 0; i < q->rowCount(parent); ++i) { | 90 | for (int i = 0; i < q->rowCount(parent); ++i) { | ||
91 | if (parent.child(i, 0).data(role).toBool()) { | 91 | if (parent.child(i, 0).data(role).toBool()) { | ||
Show All 33 Lines | 120 | { | |||
125 | 125 | | |||
126 | adjustMap(start, (end - start) + 1); | 126 | adjustMap(start, (end - start) + 1); | ||
127 | 127 | | |||
128 | bool shouldGroup = shouldGroupTasks(); // Can be slightly expensive; cache return value. | 128 | bool shouldGroup = shouldGroupTasks(); // Can be slightly expensive; cache return value. | ||
129 | 129 | | |||
130 | for (int i = start; i <= end; ++i) { | 130 | for (int i = start; i <= end; ++i) { | ||
131 | if (!shouldGroup || !tryToGroup(q->sourceModel()->index(i, 0))) { | 131 | if (!shouldGroup || !tryToGroup(q->sourceModel()->index(i, 0))) { | ||
132 | q->beginInsertRows(QModelIndex(), rowMap.count(), rowMap.count()); | 132 | q->beginInsertRows(QModelIndex(), rowMap.count(), rowMap.count()); | ||
133 | rowMap.append(QVector<int>{i}); | 133 | rowMap.append(new QVector<int>{i}); | ||
134 | q->endInsertRows(); | 134 | q->endInsertRows(); | ||
135 | } | 135 | } | ||
136 | } | 136 | } | ||
137 | 137 | | |||
138 | checkGrouping(); | 138 | checkGrouping(); | ||
139 | } | 139 | } | ||
140 | 140 | | |||
141 | void TaskGroupingProxyModel::Private::sourceRowsAboutToBeRemoved(const QModelIndex &parent, int first, int last) | 141 | void TaskGroupingProxyModel::Private::sourceRowsAboutToBeRemoved(const QModelIndex &parent, int first, int last) | ||
142 | { | 142 | { | ||
143 | // We only support flat source models. | 143 | // We only support flat source models. | ||
144 | if (parent.isValid()) { | 144 | if (parent.isValid()) { | ||
145 | return; | 145 | return; | ||
146 | } | 146 | } | ||
147 | 147 | | |||
148 | for (int i = first; i <= last; ++i) { | 148 | for (int i = first; i <= last; ++i) { | ||
149 | for (int j = 0; j < rowMap.count(); ++j) { | 149 | for (int j = 0; j < rowMap.count(); ++j) { | ||
150 | const QVector<int> &sourceRows = rowMap.at(j); | 150 | const QVector<int> *sourceRows = rowMap.at(j); | ||
151 | const int mapIndex = sourceRows.indexOf(i); | 151 | const int mapIndex = sourceRows->indexOf(i); | ||
152 | 152 | | |||
153 | if (mapIndex != -1) { | 153 | if (mapIndex != -1) { | ||
154 | // Remove top-level item. | 154 | // Remove top-level item. | ||
155 | if (sourceRows.count() == 1) { | 155 | if (sourceRows->count() == 1) { | ||
156 | q->beginRemoveRows(QModelIndex(), j, j); | 156 | q->beginRemoveRows(QModelIndex(), j, j); | ||
157 | rowMap.remove(j); | 157 | delete rowMap.takeAt(j); | ||
158 | | ||||
159 | // index() encodes parent row numbers in indices returned for group | | |||
160 | // members. endRemoveRows() is not aware of this scheme, and so won't | | |||
161 | // update any persistent indices with new row values. We're now going | | |||
162 | // to do it ourselves. | | |||
163 | foreach (const QModelIndex &idx, q->persistentIndexList()) { | | |||
164 | // internalId() for group members is parent row + 1, so the first | | |||
165 | // id after j is (j + 2). | | |||
166 | if (idx.internalId() > ((uint)j + 1)) { | | |||
167 | q->changePersistentIndex(idx, q->createIndex(idx.row(), 0, idx.internalId() - 1)); | | |||
168 | } | | |||
169 | } | | |||
170 | | ||||
171 | q->endRemoveRows(); | 158 | q->endRemoveRows(); | ||
172 | // Dissolve group. | 159 | // Dissolve group. | ||
173 | } else if (sourceRows.count() == 2) { | 160 | } else if (sourceRows->count() == 2) { | ||
174 | const QModelIndex parent = q->index(j, 0); | 161 | const QModelIndex parent = q->index(j, 0); | ||
175 | q->beginRemoveRows(parent, 0, 1); | 162 | q->beginRemoveRows(parent, 0, 1); | ||
176 | rowMap[j].remove(mapIndex); | 163 | rowMap[j]->remove(mapIndex); | ||
177 | | ||||
178 | // index() encodes parent row numbers in indices returned for group | | |||
179 | // members. endRemoveRows() is not aware of this scheme, and so won't | | |||
180 | // invalidate persistent indices for the members of the group we're | | |||
181 | // dissolving. We're now going to do it ourselves. | | |||
182 | foreach (const QModelIndex &idx, q->persistentIndexList()) { | | |||
183 | if (idx.internalId() == ((uint)j + 1)) { | | |||
184 | q->changePersistentIndex(idx, QModelIndex()); | | |||
185 | } | | |||
186 | } | | |||
187 | | ||||
188 | q->endRemoveRows(); | 164 | q->endRemoveRows(); | ||
189 | 165 | | |||
190 | // We're no longer a group parent. | 166 | // We're no longer a group parent. | ||
191 | q->dataChanged(parent, parent); | 167 | q->dataChanged(parent, parent); | ||
192 | // Remove group member. | 168 | // Remove group member. | ||
193 | } else { | 169 | } else { | ||
194 | const QModelIndex parent = q->index(j, 0); | 170 | const QModelIndex parent = q->index(j, 0); | ||
195 | q->beginRemoveRows(parent, mapIndex, mapIndex); | 171 | q->beginRemoveRows(parent, mapIndex, mapIndex); | ||
196 | rowMap[j].remove(mapIndex); | 172 | rowMap[j]->remove(mapIndex); | ||
197 | q->endRemoveRows(); | 173 | q->endRemoveRows(); | ||
198 | 174 | | |||
199 | // Various roles of the parent evaluate child data, and the | 175 | // Various roles of the parent evaluate child data, and the | ||
200 | // child list has changed. | 176 | // child list has changed. | ||
201 | q->dataChanged(parent, parent); | 177 | q->dataChanged(parent, parent); | ||
202 | } | 178 | } | ||
203 | 179 | | |||
204 | break; | 180 | break; | ||
▲ Show 20 Lines • Show All 50 Lines • ▼ Show 20 Line(s) | 212 | for (int i = topLeft.row(); i <= bottomRight.row(); ++i) { | |||
255 | // which demand attention from being grouped. Therefore if this task is no longer | 231 | // which demand attention from being grouped. Therefore if this task is no longer | ||
256 | // demanding attention, we need to try grouping it now. | 232 | // demanding attention, we need to try grouping it now. | ||
257 | if (!parent.isValid() | 233 | if (!parent.isValid() | ||
258 | && !groupDemandingAttention && roles.contains(AbstractTasksModel::IsDemandingAttention) | 234 | && !groupDemandingAttention && roles.contains(AbstractTasksModel::IsDemandingAttention) | ||
259 | && !sourceIndex.data(AbstractTasksModel::IsDemandingAttention).toBool()) { | 235 | && !sourceIndex.data(AbstractTasksModel::IsDemandingAttention).toBool()) { | ||
260 | 236 | | |||
261 | if (shouldGroupTasks() && tryToGroup(sourceIndex)) { | 237 | if (shouldGroupTasks() && tryToGroup(sourceIndex)) { | ||
262 | q->beginRemoveRows(QModelIndex(), proxyIndex.row(), proxyIndex.row()); | 238 | q->beginRemoveRows(QModelIndex(), proxyIndex.row(), proxyIndex.row()); | ||
263 | rowMap.remove(proxyIndex.row()); | 239 | delete rowMap.takeAt(proxyIndex.row()); | ||
264 | q->endRemoveRows(); | 240 | q->endRemoveRows(); | ||
265 | } else { | 241 | } else { | ||
266 | q->dataChanged(proxyIndex, proxyIndex, roles); | 242 | q->dataChanged(proxyIndex, proxyIndex, roles); | ||
267 | } | 243 | } | ||
268 | } else { | 244 | } else { | ||
269 | q->dataChanged(proxyIndex, proxyIndex, roles); | 245 | q->dataChanged(proxyIndex, proxyIndex, roles); | ||
270 | } | 246 | } | ||
271 | } | 247 | } | ||
272 | } | 248 | } | ||
273 | 249 | | |||
274 | void TaskGroupingProxyModel::Private::adjustMap(int anchor, int delta) | 250 | void TaskGroupingProxyModel::Private::adjustMap(int anchor, int delta) | ||
275 | { | 251 | { | ||
276 | for (int i = 0; i < rowMap.count(); ++i) { | 252 | for (int i = 0; i < rowMap.count(); ++i) { | ||
277 | bool changed = false; | 253 | QVector<int> *sourceRows = rowMap.at(i); | ||
278 | QVector<int> sourceRows = rowMap.at(i); | 254 | QMutableVectorIterator<int> it(*sourceRows); | ||
279 | QMutableVectorIterator<int> it(sourceRows); | | |||
280 | 255 | | |||
281 | while (it.hasNext()) { | 256 | while (it.hasNext()) { | ||
282 | it.next(); | 257 | it.next(); | ||
283 | 258 | | |||
284 | if (it.value() >= anchor) { | 259 | if (it.value() >= anchor) { | ||
285 | it.setValue(it.value() + delta); | 260 | it.setValue(it.value() + delta); | ||
286 | changed = true; | | |||
287 | } | 261 | } | ||
288 | } | 262 | } | ||
289 | | ||||
290 | if (changed) { | | |||
291 | rowMap.replace(i, sourceRows); | | |||
292 | } | | |||
293 | } | 263 | } | ||
294 | } | 264 | } | ||
295 | 265 | | |||
296 | void TaskGroupingProxyModel::Private::rebuildMap() | 266 | void TaskGroupingProxyModel::Private::rebuildMap() | ||
297 | { | 267 | { | ||
268 | qDeleteAll(rowMap); | ||||
298 | rowMap.clear(); | 269 | rowMap.clear(); | ||
299 | 270 | | |||
300 | const int rows = q->sourceModel()->rowCount(); | 271 | const int rows = q->sourceModel()->rowCount(); | ||
301 | 272 | | |||
302 | rowMap.reserve(rows); | 273 | rowMap.reserve(rows); | ||
303 | 274 | | |||
304 | for (int i = 0; i < rows; ++i) { | 275 | for (int i = 0; i < rows; ++i) { | ||
305 | rowMap.append(QVector<int>{i}); | 276 | rowMap.append(new QVector<int>{i}); | ||
306 | } | 277 | } | ||
307 | 278 | | |||
308 | checkGrouping(true /* silent */); | 279 | checkGrouping(true /* silent */); | ||
309 | } | 280 | } | ||
310 | 281 | | |||
311 | bool TaskGroupingProxyModel::Private::shouldGroupTasks() | 282 | bool TaskGroupingProxyModel::Private::shouldGroupTasks() | ||
312 | { | 283 | { | ||
313 | if (groupMode == TasksModel::GroupDisabled) { | 284 | if (groupMode == TasksModel::GroupDisabled) { | ||
Show All 26 Lines | |||||
340 | void TaskGroupingProxyModel::Private::checkGrouping(bool silent) | 311 | void TaskGroupingProxyModel::Private::checkGrouping(bool silent) | ||
341 | { | 312 | { | ||
342 | if (shouldGroupTasks()) { | 313 | if (shouldGroupTasks()) { | ||
343 | for (int i = (rowMap.count()) - 1; i >= 0; --i) { | 314 | for (int i = (rowMap.count()) - 1; i >= 0; --i) { | ||
344 | if (isGroup(i)) { | 315 | if (isGroup(i)) { | ||
345 | continue; | 316 | continue; | ||
346 | } | 317 | } | ||
347 | 318 | | |||
348 | if (tryToGroup(q->sourceModel()->index(rowMap.at(i).constFirst(), 0), silent)) { | 319 | if (tryToGroup(q->sourceModel()->index(rowMap.at(i)->constFirst(), 0), silent)) { | ||
349 | q->beginRemoveRows(QModelIndex(), i, i); | 320 | q->beginRemoveRows(QModelIndex(), i, i); | ||
350 | rowMap.remove(i); // Safe since we're iterating backwards. | 321 | delete rowMap.takeAt(i); // Safe since we're iterating backwards. | ||
351 | q->endRemoveRows(); | 322 | q->endRemoveRows(); | ||
352 | } | 323 | } | ||
353 | } | 324 | } | ||
354 | } else { | 325 | } else { | ||
355 | for (int i = (rowMap.count()) - 1; i >= 0; --i) { | 326 | for (int i = (rowMap.count()) - 1; i >= 0; --i) { | ||
356 | breakGroupFor(q->index(i, 0), silent); | 327 | breakGroupFor(q->index(i, 0), silent); | ||
357 | } | 328 | } | ||
358 | } | 329 | } | ||
▲ Show 20 Lines • Show All 41 Lines • ▼ Show 20 Line(s) | 354 | { | |||
400 | // Blacklist checks. | 371 | // Blacklist checks. | ||
401 | if (isBlacklisted(sourceIndex)) { | 372 | if (isBlacklisted(sourceIndex)) { | ||
402 | return false; | 373 | return false; | ||
403 | } | 374 | } | ||
404 | 375 | | |||
405 | // Meat of the matter: Try to add this source row to a sub-list with source rows | 376 | // Meat of the matter: Try to add this source row to a sub-list with source rows | ||
406 | // associated with the same application. | 377 | // associated with the same application. | ||
407 | for (int i = 0; i < rowMap.count(); ++i) { | 378 | for (int i = 0; i < rowMap.count(); ++i) { | ||
408 | const QModelIndex &groupRep = q->sourceModel()->index(rowMap.at(i).constFirst(), 0); | 379 | const QModelIndex &groupRep = q->sourceModel()->index(rowMap.at(i)->constFirst(), 0); | ||
409 | 380 | | |||
410 | // Don't match a row with itself. | 381 | // Don't match a row with itself. | ||
411 | if (sourceIndex == groupRep) { | 382 | if (sourceIndex == groupRep) { | ||
412 | continue; | 383 | continue; | ||
413 | } | 384 | } | ||
414 | 385 | | |||
415 | // Don't group windows with anything other than windows. | 386 | // Don't group windows with anything other than windows. | ||
416 | if (!groupRep.data(AbstractTasksModel::IsWindow).toBool()) { | 387 | if (!groupRep.data(AbstractTasksModel::IsWindow).toBool()) { | ||
417 | continue; | 388 | continue; | ||
418 | } | 389 | } | ||
419 | 390 | | |||
420 | if (appsMatch(sourceIndex, groupRep)) { | 391 | if (appsMatch(sourceIndex, groupRep)) { | ||
421 | const QModelIndex parent = q->index(i, 0); | 392 | const QModelIndex parent = q->index(i, 0); | ||
422 | 393 | | |||
423 | if (!silent) { | 394 | if (!silent) { | ||
424 | const int newIndex = rowMap.at(i).count(); | 395 | const int newIndex = rowMap.at(i)->count(); | ||
425 | 396 | | |||
426 | if (newIndex == 1) { | 397 | if (newIndex == 1) { | ||
427 | q->beginInsertRows(parent, 0, 1); | 398 | q->beginInsertRows(parent, 0, 1); | ||
428 | } else { | 399 | } else { | ||
429 | q->beginInsertRows(parent, newIndex, newIndex); | 400 | q->beginInsertRows(parent, newIndex, newIndex); | ||
430 | } | 401 | } | ||
431 | } | 402 | } | ||
432 | 403 | | |||
433 | rowMap[i].append(sourceIndex.row()); | 404 | rowMap[i]->append(sourceIndex.row()); | ||
434 | 405 | | |||
435 | if (!silent) { | 406 | if (!silent) { | ||
436 | q->endInsertRows(); | 407 | q->endInsertRows(); | ||
437 | 408 | | |||
438 | q->dataChanged(parent, parent); | 409 | q->dataChanged(parent, parent); | ||
439 | } | 410 | } | ||
440 | 411 | | |||
441 | return true; | 412 | return true; | ||
Show All 10 Lines | 422 | if (index.parent().isValid() || isGroup(index.row())) { | |||
452 | return; | 423 | return; | ||
453 | } | 424 | } | ||
454 | 425 | | |||
455 | // We need to grab a source index as we may invalidate the index passed | 426 | // We need to grab a source index as we may invalidate the index passed | ||
456 | // in through grouping. | 427 | // in through grouping. | ||
457 | const QModelIndex &sourceTarget = q->mapToSource(index); | 428 | const QModelIndex &sourceTarget = q->mapToSource(index); | ||
458 | 429 | | |||
459 | for (int i = (rowMap.count() - 1); i >= 0; --i) { | 430 | for (int i = (rowMap.count() - 1); i >= 0; --i) { | ||
460 | const QModelIndex &sourceIndex = q->sourceModel()->index(rowMap.at(i).constFirst(), 0); | 431 | const QModelIndex &sourceIndex = q->sourceModel()->index(rowMap.at(i)->constFirst(), 0); | ||
461 | 432 | | |||
462 | if (!appsMatch(sourceTarget, sourceIndex)) { | 433 | if (!appsMatch(sourceTarget, sourceIndex)) { | ||
463 | continue; | 434 | continue; | ||
464 | } | 435 | } | ||
465 | 436 | | |||
466 | if (tryToGroup(sourceIndex)) { | 437 | if (tryToGroup(sourceIndex)) { | ||
467 | q->beginRemoveRows(QModelIndex(), i, i); | 438 | q->beginRemoveRows(QModelIndex(), i, i); | ||
468 | rowMap.remove(i); // Safe since we're iterating backwards. | 439 | delete rowMap.takeAt(i); // Safe since we're iterating backwards. | ||
469 | q->endRemoveRows(); | 440 | q->endRemoveRows(); | ||
470 | } | 441 | } | ||
471 | } | 442 | } | ||
472 | } | 443 | } | ||
473 | 444 | | |||
474 | void TaskGroupingProxyModel::Private::breakGroupFor(const QModelIndex &index, bool silent) | 445 | void TaskGroupingProxyModel::Private::breakGroupFor(const QModelIndex &index, bool silent) | ||
475 | { | 446 | { | ||
476 | const int row = index.row(); | 447 | const int row = index.row(); | ||
477 | 448 | | |||
478 | if (!isGroup(row)) { | 449 | if (!isGroup(row)) { | ||
479 | return; | 450 | return; | ||
480 | } | 451 | } | ||
481 | 452 | | |||
482 | // The first child will move up to the top level. | 453 | // The first child will move up to the top level. | ||
483 | QVector<int> extraChildren = rowMap.at(row).mid(1); | 454 | QVector<int> extraChildren = rowMap.at(row)->mid(1); | ||
484 | 455 | | |||
485 | // NOTE: We're going to do remove+insert transactions instead of a | 456 | // NOTE: We're going to do remove+insert transactions instead of a | ||
486 | // single reparenting move transaction to save on complexity in the | 457 | // single reparenting move transaction to save on complexity in the | ||
487 | // proxies above us. | 458 | // proxies above us. | ||
488 | // TODO: This could technically be optimized, though it's very | 459 | // TODO: This could technically be optimized, though it's very | ||
489 | // unlikely to be ever worth it. | 460 | // unlikely to be ever worth it. | ||
490 | if (!silent) { | 461 | if (!silent) { | ||
491 | q->beginRemoveRows(index, 0, extraChildren.count()); | 462 | q->beginRemoveRows(index, 0, extraChildren.count()); | ||
492 | } | 463 | } | ||
493 | 464 | | |||
494 | rowMap[row].resize(1); | 465 | rowMap[row]->resize(1); | ||
(I know this existing code) why 1? davidedmundson: (I know this existing code)
why 1?
shouldn't this should be currentSize + extraChildren.count… | |||||
This code is breaking a group apart, and this transaction specifically removes all the children from a group parent, leaving only the top-level item. In the row map this means an element QVector with only one element, hence it's resized to size 1. hein: This code is breaking a group apart, and this transaction specifically removes all the children… | |||||
495 | | ||||
496 | // index() encodes parent row numbers in indices returned for group | | |||
497 | // members. endRemoveRows() is not aware of this scheme, and so won't | | |||
498 | // invalidate persistent indices for the members of the group we're | | |||
499 | // dissolving. We're now going to do it ourselves. | | |||
500 | foreach (const QModelIndex &idx, q->persistentIndexList()) { | | |||
501 | if (idx.internalId() == ((uint)row + 1)) { | | |||
502 | q->changePersistentIndex(idx, QModelIndex()); | | |||
503 | } | | |||
504 | } | | |||
505 | 466 | | |||
506 | if (!silent) { | 467 | if (!silent) { | ||
507 | q->endRemoveRows(); | 468 | q->endRemoveRows(); | ||
508 | 469 | | |||
509 | // We're no longer a group parent. | 470 | // We're no longer a group parent. | ||
510 | q->dataChanged(index, index); | 471 | q->dataChanged(index, index); | ||
511 | 472 | | |||
512 | q->beginInsertRows(QModelIndex(), rowMap.count(), | 473 | q->beginInsertRows(QModelIndex(), rowMap.count(), | ||
513 | rowMap.count() + (extraChildren.count() - 1)); | 474 | rowMap.count() + (extraChildren.count() - 1)); | ||
514 | } | 475 | } | ||
515 | 476 | | |||
516 | for (int i = 0; i < extraChildren.count(); ++i) { | 477 | for (int i = 0; i < extraChildren.count(); ++i) { | ||
517 | rowMap.append(QVector<int>{extraChildren.at(i)}); | 478 | rowMap.append(new QVector<int>{extraChildren.at(i)}); | ||
518 | } | 479 | } | ||
519 | 480 | | |||
520 | if (!silent) { | 481 | if (!silent) { | ||
521 | q->endInsertRows(); | 482 | q->endInsertRows(); | ||
522 | } | 483 | } | ||
523 | } | 484 | } | ||
524 | 485 | | |||
525 | TaskGroupingProxyModel::TaskGroupingProxyModel(QObject *parent) : QAbstractProxyModel(parent) | 486 | TaskGroupingProxyModel::TaskGroupingProxyModel(QObject *parent) : QAbstractProxyModel(parent) | ||
526 | , d(new Private(this)) | 487 | , d(new Private(this)) | ||
527 | { | 488 | { | ||
528 | } | 489 | } | ||
529 | 490 | | |||
530 | TaskGroupingProxyModel::~TaskGroupingProxyModel() | 491 | TaskGroupingProxyModel::~TaskGroupingProxyModel() | ||
531 | { | 492 | { | ||
532 | } | 493 | } | ||
533 | 494 | | |||
534 | QModelIndex TaskGroupingProxyModel::index(int row, int column, const QModelIndex &parent) const | 495 | QModelIndex TaskGroupingProxyModel::index(int row, int column, const QModelIndex &parent) const | ||
535 | { | 496 | { | ||
536 | if (row < 0 || column != 0) { | 497 | if (row < 0 || column != 0) { | ||
537 | return QModelIndex(); | 498 | return QModelIndex(); | ||
538 | } | 499 | } | ||
539 | 500 | | |||
540 | if (parent.isValid() && row < d->rowMap.at(parent.row()).count()) { | 501 | if (parent.isValid() && row < d->rowMap.at(parent.row())->count()) { | ||
541 | return createIndex(row, column, parent.row() + 1); | 502 | return createIndex(row, column, d->rowMap.at(parent.row())); | ||
542 | } | 503 | } | ||
543 | 504 | | |||
544 | if (row < d->rowMap.count()) { | 505 | if (row < d->rowMap.count()) { | ||
545 | return createIndex(row, column, (quintptr)0); | 506 | return createIndex(row, column, nullptr); | ||
546 | } | 507 | } | ||
547 | 508 | | |||
548 | return QModelIndex(); | 509 | return QModelIndex(); | ||
549 | } | 510 | } | ||
550 | 511 | | |||
551 | QModelIndex TaskGroupingProxyModel::parent(const QModelIndex &child) const | 512 | QModelIndex TaskGroupingProxyModel::parent(const QModelIndex &child) const | ||
552 | { | 513 | { | ||
553 | return (child.internalId() == 0) ? QModelIndex() : index(child.internalId() - 1, 0); | 514 | if (child.internalPointer() == nullptr) { | ||
515 | return QModelIndex(); | ||||
516 | } else { | ||||
517 | const int parentRow = d->rowMap.indexOf(static_cast<QVector<int> *>(child.internalPointer())); | ||||
518 | | ||||
519 | if (parentRow != -1) { | ||||
520 | return index(parentRow, 0); | ||||
521 | } | ||||
davidedmundson: I think we should assert after this if. | |||||
hein: Will do. | |||||
522 | } | ||||
523 | | ||||
524 | return QModelIndex(); | ||||
554 | } | 525 | } | ||
555 | 526 | | |||
556 | QModelIndex TaskGroupingProxyModel::mapFromSource(const QModelIndex &sourceIndex) const | 527 | QModelIndex TaskGroupingProxyModel::mapFromSource(const QModelIndex &sourceIndex) const | ||
557 | { | 528 | { | ||
558 | if (!sourceIndex.isValid() || sourceIndex.model() != sourceModel()) { | 529 | if (!sourceIndex.isValid() || sourceIndex.model() != sourceModel()) { | ||
559 | return QModelIndex(); | 530 | return QModelIndex(); | ||
560 | } | 531 | } | ||
561 | 532 | | |||
562 | for (int i = 0; i < d->rowMap.count(); ++i) { | 533 | for (int i = 0; i < d->rowMap.count(); ++i) { | ||
563 | const QVector<int> &sourceRows = d->rowMap.at(i); | 534 | const QVector<int> *sourceRows = d->rowMap.at(i); | ||
564 | const int childIndex = sourceRows.indexOf(sourceIndex.row()); | 535 | const int childIndex = sourceRows->indexOf(sourceIndex.row()); | ||
565 | const QModelIndex parent = index(i, 0); | 536 | const QModelIndex parent = index(i, 0); | ||
566 | 537 | | |||
567 | if (childIndex == 0) { | 538 | if (childIndex == 0) { | ||
568 | // If the sub-list we found the source row in is larger than 1 (i.e. part | 539 | // If the sub-list we found the source row in is larger than 1 (i.e. part | ||
569 | // of a group, map to the logical child item instead of the parent item | 540 | // of a group, map to the logical child item instead of the parent item | ||
570 | // the source row also stands in for. The parent is therefore unreachable | 541 | // the source row also stands in for. The parent is therefore unreachable | ||
571 | // from mapToSource(). | 542 | // from mapToSource(). | ||
572 | if (d->isGroup(i)) { | 543 | if (d->isGroup(i)) { | ||
Show All 18 Lines | 558 | { | |||
591 | 562 | | |||
592 | const QModelIndex &parent = proxyIndex.parent(); | 563 | const QModelIndex &parent = proxyIndex.parent(); | ||
593 | 564 | | |||
594 | if (parent.isValid()) { | 565 | if (parent.isValid()) { | ||
595 | if (parent.row() < 0 || parent.row() >= d->rowMap.count()) { | 566 | if (parent.row() < 0 || parent.row() >= d->rowMap.count()) { | ||
596 | return QModelIndex(); | 567 | return QModelIndex(); | ||
597 | } | 568 | } | ||
598 | 569 | | |||
599 | return sourceModel()->index(d->rowMap.at(parent.row()).at(proxyIndex.row()), 0); | 570 | return sourceModel()->index(d->rowMap.at(parent.row())->at(proxyIndex.row()), 0); | ||
600 | } else { | 571 | } else { | ||
601 | // Group parents items therefore equate to the first child item; the source | 572 | // Group parents items therefore equate to the first child item; the source | ||
602 | // row logically appears twice in the proxy. | 573 | // row logically appears twice in the proxy. | ||
603 | // mapFromSource() is not required to handle this well (consider proxies can | 574 | // mapFromSource() is not required to handle this well (consider proxies can | ||
604 | // filter out rows, too) and opts to map to the child item, as the group parent | 575 | // filter out rows, too) and opts to map to the child item, as the group parent | ||
605 | // has its Qt::DisplayRole mangled by data(), and it's more useful for trans- | 576 | // has its Qt::DisplayRole mangled by data(), and it's more useful for trans- | ||
606 | // lating dataChanged() from the source model. | 577 | // lating dataChanged() from the source model. | ||
607 | return sourceModel()->index(d->rowMap.at(proxyIndex.row()).at(0), 0); | 578 | return sourceModel()->index(d->rowMap.at(proxyIndex.row())->at(0), 0); | ||
608 | } | 579 | } | ||
609 | 580 | | |||
610 | return QModelIndex(); | 581 | return QModelIndex(); | ||
611 | } | 582 | } | ||
612 | 583 | | |||
613 | int TaskGroupingProxyModel::rowCount(const QModelIndex &parent) const | 584 | int TaskGroupingProxyModel::rowCount(const QModelIndex &parent) const | ||
614 | { | 585 | { | ||
615 | if (!sourceModel()) { | 586 | if (!sourceModel()) { | ||
616 | return 0; | 587 | return 0; | ||
617 | } | 588 | } | ||
618 | 589 | | |||
619 | if (parent.isValid() && parent.model() == this) { | 590 | if (parent.isValid() && parent.model() == this) { | ||
620 | // Don't return row count for top-level item at child row: Group members | 591 | // Don't return row count for top-level item at child row: Group members | ||
621 | // never have further children of their own. | 592 | // never have further children of their own. | ||
622 | if (parent.parent().isValid()) { | 593 | if (parent.parent().isValid()) { | ||
623 | return 0; | 594 | return 0; | ||
624 | } | 595 | } | ||
625 | 596 | | |||
626 | if (parent.row() < 0 || parent.row() >= d->rowMap.count()) { | 597 | if (parent.row() < 0 || parent.row() >= d->rowMap.count()) { | ||
627 | return 0; | 598 | return 0; | ||
628 | } | 599 | } | ||
629 | 600 | | |||
630 | const uint rowCount = d->rowMap.at(parent.row()).count(); | 601 | const uint rowCount = d->rowMap.at(parent.row())->count(); | ||
631 | 602 | | |||
632 | // If this sub-list in the map only has one entry, it's a plain item, not | 603 | // If this sub-list in the map only has one entry, it's a plain item, not | ||
633 | // parent to a group. | 604 | // parent to a group. | ||
634 | if (rowCount == 1) { | 605 | if (rowCount == 1) { | ||
635 | return 0; | 606 | return 0; | ||
636 | } else { | 607 | } else { | ||
637 | return rowCount; | 608 | return rowCount; | ||
638 | } | 609 | } | ||
▲ Show 20 Lines • Show All 584 Lines • Show Last 20 Lines |
Cleanup on destruction.
(which sounds like the name of a new Megadeth single)