1 /*
2 SPDX-FileCopyrightText: 2009 Stephen Kelly <steveire@gmail.com>
3 SPDX-FileCopyrightText: 2016 Ableton AG <info@ableton.com>
4 SPDX-FileContributor: Stephen Kelly <stephen.kelly@ableton.com>
5
6 SPDX-License-Identifier: LGPL-2.0-or-later
7 */
8
9 #include "kselectionproxymodel.h"
10
11 #include <QItemSelectionRange>
12 #include <QPointer>
13 #include <QStringList>
14
15 #include "kbihash_p.h"
16 #include "kmodelindexproxymapper.h"
17 #include "kvoidpointerfactory_p.h"
18
19 typedef KBiHash<QPersistentModelIndex, QModelIndex> SourceProxyIndexMapping;
20 typedef KBiHash<void *, QModelIndex> ParentMapping;
21 typedef KHash2Map<QPersistentModelIndex, int> SourceIndexProxyRowMapping;
22
23 /**
24 Return true if @p idx is a descendant of one of the indexes in @p list.
25 Note that this returns false if @p list contains @p idx.
26 */
27 template<typename ModelIndex>
isDescendantOf(const QList<ModelIndex> & list,const QModelIndex & idx)28 bool isDescendantOf(const QList<ModelIndex> &list, const QModelIndex &idx)
29 {
30 if (!idx.isValid()) {
31 return false;
32 }
33
34 if (list.contains(idx)) {
35 return false;
36 }
37
38 QModelIndex parent = idx.parent();
39 while (parent.isValid()) {
40 if (list.contains(parent)) {
41 return true;
42 }
43 parent = parent.parent();
44 }
45 return false;
46 }
47
isDescendantOf(const QItemSelection & selection,const QModelIndex & descendant)48 static bool isDescendantOf(const QItemSelection &selection, const QModelIndex &descendant)
49 {
50 if (!descendant.isValid()) {
51 return false;
52 }
53
54 if (selection.contains(descendant)) {
55 return false;
56 }
57
58 QModelIndex parent = descendant.parent();
59 while (parent.isValid()) {
60 if (selection.contains(parent)) {
61 return true;
62 }
63
64 parent = parent.parent();
65 }
66 return false;
67 }
68
isDescendantOf(const QItemSelectionRange & range,const QModelIndex & descendant)69 static bool isDescendantOf(const QItemSelectionRange &range, const QModelIndex &descendant)
70 {
71 if (!descendant.isValid()) {
72 return false;
73 }
74
75 if (range.contains(descendant)) {
76 return false;
77 }
78
79 QModelIndex parent = descendant.parent();
80 while (parent.isValid()) {
81 if (range.contains(parent)) {
82 return true;
83 }
84
85 parent = parent.parent();
86 }
87 return false;
88 }
89
_getRootListRow(const QList<QModelIndexList> & rootAncestors,const QModelIndex & index)90 static int _getRootListRow(const QList<QModelIndexList> &rootAncestors, const QModelIndex &index)
91 {
92 QModelIndex commonParent = index;
93 QModelIndex youngestAncestor;
94
95 int firstCommonParent = -1;
96 int bestParentRow = -1;
97 while (commonParent.isValid()) {
98 youngestAncestor = commonParent;
99 commonParent = commonParent.parent();
100
101 for (int i = 0; i < rootAncestors.size(); ++i) {
102 const QModelIndexList ancestorList = rootAncestors.at(i);
103
104 const int parentRow = ancestorList.indexOf(commonParent);
105
106 if (parentRow < 0) {
107 continue;
108 }
109
110 if (parentRow > bestParentRow) {
111 firstCommonParent = i;
112 bestParentRow = parentRow;
113 }
114 }
115
116 if (firstCommonParent >= 0) {
117 break;
118 }
119 }
120
121 // If @p list is non-empty, the invalid QModelIndex() will at least be found in ancestorList.
122 Q_ASSERT(firstCommonParent >= 0);
123
124 const QModelIndexList firstAnsList = rootAncestors.at(firstCommonParent);
125
126 const QModelIndex eldestSibling = firstAnsList.value(bestParentRow + 1);
127
128 if (eldestSibling.isValid()) {
129 // firstCommonParent is a sibling of one of the ancestors of @p index.
130 // It is the first index to share a common parent with one of the ancestors of @p index.
131 if (eldestSibling.row() >= youngestAncestor.row()) {
132 return firstCommonParent;
133 }
134 }
135
136 int siblingOffset = 1;
137
138 // The same commonParent might be common to several root indexes.
139 // If this is the last in the list, it's the only match. We instruct the model
140 // to insert the new index after it ( + siblingOffset).
141 if (rootAncestors.size() <= firstCommonParent + siblingOffset) {
142 return firstCommonParent + siblingOffset;
143 }
144
145 // A
146 // - B
147 // - C
148 // - D
149 // - E
150 // F
151 //
152 // F is selected, then C then D. When inserting D into the model, the commonParent is B (the parent of C).
153 // The next existing sibling of B is F (in the proxy model). bestParentRow will then refer to an index on
154 // the level of a child of F (which doesn't exist - Boom!). If it doesn't exist, then we've already found
155 // the place to insert D
156 QModelIndexList ansList = rootAncestors.at(firstCommonParent + siblingOffset);
157 if (ansList.size() <= bestParentRow) {
158 return firstCommonParent + siblingOffset;
159 }
160
161 QModelIndex nextParent = ansList.at(bestParentRow);
162 while (nextParent == commonParent) {
163 if (ansList.size() < bestParentRow + 1)
164 // If the list is longer, it means that at the end of it is a descendant of the new index.
165 // We insert the ancestors items first in that case.
166 {
167 break;
168 }
169
170 const QModelIndex nextSibling = ansList.value(bestParentRow + 1);
171
172 if (!nextSibling.isValid()) {
173 continue;
174 }
175
176 if (youngestAncestor.row() <= nextSibling.row()) {
177 break;
178 }
179
180 siblingOffset++;
181
182 if (rootAncestors.size() <= firstCommonParent + siblingOffset) {
183 break;
184 }
185
186 ansList = rootAncestors.at(firstCommonParent + siblingOffset);
187
188 // In the scenario above, E is selected after D, causing this loop to be entered,
189 // and requiring a similar result if the next sibling in the proxy model does not have children.
190 if (ansList.size() <= bestParentRow) {
191 break;
192 }
193
194 nextParent = ansList.at(bestParentRow);
195 }
196
197 return firstCommonParent + siblingOffset;
198 }
199
200 /**
201 Determines the correct location to insert @p index into @p list.
202 */
203 template<typename ModelIndex>
getRootListRow(const QList<ModelIndex> & list,const QModelIndex & index)204 static int getRootListRow(const QList<ModelIndex> &list, const QModelIndex &index)
205 {
206 if (!index.isValid()) {
207 return -1;
208 }
209
210 if (list.isEmpty()) {
211 return 0;
212 }
213
214 // What's going on?
215 // Consider a tree like
216 //
217 // A
218 // - B
219 // - - C
220 // - - - D
221 // - E
222 // - F
223 // - - G
224 // - - - H
225 // - I
226 // - - J
227 // - K
228 //
229 // If D, E and J are already selected, and H is newly selected, we need to put H between E and J in the proxy model.
230 // To figure that out, we create a list for each already selected index of its ancestors. Then,
231 // we climb the ancestors of H until we reach an index with siblings which have a descendant
232 // selected (F above has siblings B, E and I which have descendants which are already selected).
233 // Those child indexes are traversed to find the right sibling to put F beside.
234 //
235 // i.e., new items are inserted in the expected location.
236
237 QList<QModelIndexList> rootAncestors;
238 for (const QModelIndex &root : list) {
239 QModelIndexList ancestors;
240 ancestors << root;
241 QModelIndex parent = root.parent();
242 while (parent.isValid()) {
243 ancestors.prepend(parent);
244 parent = parent.parent();
245 }
246 ancestors.prepend(QModelIndex());
247 rootAncestors << ancestors;
248 }
249 return _getRootListRow(rootAncestors, index);
250 }
251
252 /**
253 Returns a selection in which no descendants of selected indexes are also themselves selected.
254 For example,
255 @code
256 A
257 - B
258 C
259 D
260 @endcode
261 If A, B and D are selected in @p selection, the returned selection contains only A and D.
262 */
getRootRanges(const QItemSelection & _selection)263 static QItemSelection getRootRanges(const QItemSelection &_selection)
264 {
265 QItemSelection rootSelection;
266 QItemSelection selection = _selection;
267 QList<QItemSelectionRange>::iterator it = selection.begin();
268 while (it != selection.end()) {
269 if (!it->topLeft().parent().isValid()) {
270 rootSelection.append(*it);
271 it = selection.erase(it);
272 } else {
273 ++it;
274 }
275 }
276
277 it = selection.begin();
278 const QList<QItemSelectionRange>::iterator end = selection.end();
279 while (it != end) {
280 const QItemSelectionRange range = *it;
281 it = selection.erase(it);
282
283 if (isDescendantOf(rootSelection, range.topLeft()) || isDescendantOf(selection, range.topLeft())) {
284 continue;
285 }
286
287 rootSelection << range;
288 }
289 return rootSelection;
290 }
291
292 /**
293 */
294 struct RangeLessThan {
operator ()RangeLessThan295 bool operator()(const QItemSelectionRange &left, const QItemSelectionRange &right) const
296 {
297 if (right.model() == left.model()) {
298 // parent has to be calculated, so we only do so once.
299 const QModelIndex topLeftParent = left.parent();
300 const QModelIndex otherTopLeftParent = right.parent();
301 if (topLeftParent == otherTopLeftParent) {
302 if (right.top() == left.top()) {
303 if (right.left() == left.left()) {
304 if (right.bottom() == left.bottom()) {
305 return left.right() < right.right();
306 }
307 return left.bottom() < right.bottom();
308 }
309 return left.left() < right.left();
310 }
311 return left.top() < right.top();
312 }
313 return topLeftParent < otherTopLeftParent;
314 }
315 return left.model() < right.model();
316 }
317 };
318
stableNormalizeSelection(const QItemSelection & selection)319 static QItemSelection stableNormalizeSelection(const QItemSelection &selection)
320 {
321 if (selection.size() <= 1) {
322 return selection;
323 }
324
325 QItemSelection::const_iterator it = selection.begin();
326 const QItemSelection::const_iterator end = selection.end();
327
328 Q_ASSERT(it != end);
329 QItemSelection::const_iterator scout = it + 1;
330
331 QItemSelection result;
332 while (scout != end) {
333 Q_ASSERT(it != end);
334 int bottom = it->bottom();
335 while (scout != end && it->parent() == scout->parent() && bottom + 1 == scout->top()) {
336 bottom = scout->bottom();
337 ++scout;
338 }
339 if (bottom != it->bottom()) {
340 const QModelIndex topLeft = it->topLeft();
341 result << QItemSelectionRange(topLeft, topLeft.sibling(bottom, it->right()));
342 }
343 Q_ASSERT(it != scout);
344 if (scout == end) {
345 break;
346 }
347 if (it + 1 == scout) {
348 result << *it;
349 }
350 it = scout;
351 ++scout;
352 if (scout == end) {
353 result << *it;
354 }
355 }
356 return result;
357 }
358
kNormalizeSelection(QItemSelection selection)359 static QItemSelection kNormalizeSelection(QItemSelection selection)
360 {
361 if (selection.size() <= 1) {
362 return selection;
363 }
364
365 // KSelectionProxyModel has a strong assumption that
366 // the views always select rows, so usually the
367 // @p selection here contains ranges that span all
368 // columns. However, if a QSortFilterProxyModel
369 // is used too, it splits up the complete ranges into
370 // one index per range. That confuses the data structures
371 // held by this proxy (which keeps track of indexes in the
372 // first column). As this proxy already assumes that if the
373 // zeroth column is selected, then its entire row is selected,
374 // we can safely remove the ranges which do not include
375 // column 0 without a loss of functionality.
376 QItemSelection::iterator i = selection.begin();
377 while (i != selection.end()) {
378 if (i->left() > 0) {
379 i = selection.erase(i);
380 } else {
381 ++i;
382 }
383 }
384
385 RangeLessThan lt;
386 std::sort(selection.begin(), selection.end(), lt);
387 return stableNormalizeSelection(selection);
388 }
389
390 class KSelectionProxyModelPrivate
391 {
392 public:
KSelectionProxyModelPrivate(KSelectionProxyModel * model)393 KSelectionProxyModelPrivate(KSelectionProxyModel *model)
394 : q_ptr(model)
395 , m_indexMapper(nullptr)
396 , m_startWithChildTrees(false)
397 , m_omitChildren(false)
398 , m_omitDescendants(false)
399 , m_includeAllSelected(false)
400 , m_rowsInserted(false)
401 , m_rowsRemoved(false)
402 , m_recreateFirstChildMappingOnRemoval(false)
403 , m_rowsMoved(false)
404 , m_resetting(false)
405 , m_sourceModelResetting(false)
406 , m_doubleResetting(false)
407 , m_layoutChanging(false)
408 , m_ignoreNextLayoutAboutToBeChanged(false)
409 , m_ignoreNextLayoutChanged(false)
410 , m_selectionModel(nullptr)
411 , m_filterBehavior(KSelectionProxyModel::InvalidBehavior)
412 {
413 }
414
415 Q_DECLARE_PUBLIC(KSelectionProxyModel)
416 KSelectionProxyModel *const q_ptr;
417
418 // A unique id is generated for each parent. It is used for the internalPointer of its children in the proxy
419 // This is used to store a unique id for QModelIndexes in the proxy which have children.
420 // If an index newly gets children it is added to this hash. If its last child is removed it is removed from this map.
421 // If this map contains an index, that index hasChildren(). This hash is populated when new rows are inserted in the
422 // source model, or a new selection is made.
423 mutable ParentMapping m_parentIds;
424 // This mapping maps indexes with children in the source to indexes with children in the proxy.
425 // The order of indexes in this list is not relevant.
426 mutable SourceProxyIndexMapping m_mappedParents;
427
428 KVoidPointerFactory<> m_voidPointerFactory;
429
430 /**
431 Keeping Persistent indexes from this model in this model breaks in certain situations
432 such as after source insert, but before calling endInsertRows in this model. In such a state,
433 the persistent indexes are not updated, but the methods assume they are already up-to-date.
434
435 Instead of using persistentindexes for proxy indexes in m_mappedParents, we maintain them ourselves with this method.
436
437 m_mappedParents and m_parentIds are affected.
438
439 @p parent and @p start refer to the proxy model. Any rows >= @p start will be updated.
440 @p offset is the amount that affected indexes will be changed.
441 */
442 void updateInternalIndexes(const QModelIndex &parent, int start, int offset);
443
444 /**
445 * Updates stored indexes in the proxy. Any proxy row >= @p start is changed by @p offset.
446 *
447 * This is only called to update indexes in the top level of the proxy. Most commonly that is
448 *
449 * m_mappedParents, m_parentIds and m_mappedFirstChildren are affected.
450 */
451 void updateInternalTopIndexes(int start, int offset);
452
453 void updateFirstChildMapping(const QModelIndex &parent, int offset);
454
isFlat() const455 bool isFlat() const
456 {
457 return m_omitChildren || (m_omitDescendants && m_startWithChildTrees);
458 }
459
460 /**
461 * Tries to ensure that @p parent is a mapped parent in the proxy.
462 * Returns true if parent is mappable in the model, and false otherwise.
463 */
464 bool ensureMappable(const QModelIndex &parent) const;
parentIsMappable(const QModelIndex & parent) const465 bool parentIsMappable(const QModelIndex &parent) const
466 {
467 return parentAlreadyMapped(parent) || m_rootIndexList.contains(parent);
468 }
469
470 /**
471 * Maps @p parent to source if it is already mapped, and otherwise returns an invalid QModelIndex.
472 */
473 QModelIndex mapFromSource(const QModelIndex &parent) const;
474
475 /**
476 Creates mappings in m_parentIds and m_mappedParents between the source and the proxy.
477
478 This is not recursive
479 */
480 void createParentMappings(const QModelIndex &parent, int start, int end) const;
481 void createFirstChildMapping(const QModelIndex &parent, int proxyRow) const;
482 bool firstChildAlreadyMapped(const QModelIndex &firstChild) const;
483 bool parentAlreadyMapped(const QModelIndex &parent) const;
484 void removeFirstChildMappings(int start, int end);
485 void removeParentMappings(const QModelIndex &parent, int start, int end);
486
487 /**
488 Given a QModelIndex in the proxy, return the corresponding QModelIndex in the source.
489
490 This method works only if the index has children in the proxy model which already has a mapping from the source.
491
492 This means that if the proxy is a flat list, this method will always return QModelIndex(). Additionally, it means that m_mappedParents is not populated
493 automatically and must be populated manually.
494
495 No new mapping is created by this method.
496 */
497 QModelIndex mapParentToSource(const QModelIndex &proxyParent) const;
498
499 /**
500 Given a QModelIndex in the source model, return the corresponding QModelIndex in the proxy.
501
502 This method works only if the index has children in the proxy model which already has a mapping from the source.
503
504 No new mapping is created by this method.
505 */
506 QModelIndex mapParentFromSource(const QModelIndex &sourceParent) const;
507
508 QModelIndex mapTopLevelToSource(int row, int column) const;
509 QModelIndex mapTopLevelFromSource(const QModelIndex &sourceIndex) const;
510 QModelIndex createTopLevelIndex(int row, int column) const;
511 int topLevelRowCount() const;
512
parentId(const QModelIndex & proxyParent) const513 void *parentId(const QModelIndex &proxyParent) const
514 {
515 return m_parentIds.rightToLeft(proxyParent);
516 }
parentForId(void * id) const517 QModelIndex parentForId(void *id) const
518 {
519 return m_parentIds.leftToRight(id);
520 }
521
522 // Only populated if m_startWithChildTrees.
523
524 mutable SourceIndexProxyRowMapping m_mappedFirstChildren;
525
526 // Source list is the selection in the source model.
527 QList<QPersistentModelIndex> m_rootIndexList;
528
529 KModelIndexProxyMapper *m_indexMapper;
530
531 QPair<int, int> beginRemoveRows(const QModelIndex &parent, int start, int end) const;
532 QPair<int, int> beginInsertRows(const QModelIndex &parent, int start, int end) const;
533 void endRemoveRows(const QModelIndex &sourceParent, int proxyStart, int proxyEnd);
534 void endInsertRows(const QModelIndex &parent, int start, int end);
535
536 void sourceRowsAboutToBeInserted(const QModelIndex &parent, int start, int end);
537 void sourceRowsInserted(const QModelIndex &parent, int start, int end);
538 void sourceRowsAboutToBeRemoved(const QModelIndex &parent, int start, int end);
539 void sourceRowsRemoved(const QModelIndex &parent, int start, int end);
540 void sourceRowsAboutToBeMoved(const QModelIndex &parent, int start, int end, const QModelIndex &destParent, int destRow);
541 void sourceRowsMoved(const QModelIndex &parent, int start, int end, const QModelIndex &destParent, int destRow);
542 void sourceModelAboutToBeReset();
543 void sourceModelReset();
544 void sourceLayoutAboutToBeChanged();
545 void sourceLayoutChanged();
546 void emitContinuousRanges(const QModelIndex &sourceFirst, const QModelIndex &sourceLast, const QModelIndex &proxyFirst, const QModelIndex &proxyLast);
547 void sourceDataChanged(const QModelIndex &topLeft, const QModelIndex &bottomRight);
548
549 void removeSelectionFromProxy(const QItemSelection &selection);
550
551 void selectionChanged(const QItemSelection &selected, const QItemSelection &deselected);
552 void sourceModelDestroyed();
553
554 void resetInternalData();
555
556 bool rootWillBeRemoved(const QItemSelection &selection, const QModelIndex &root);
557
558 /**
559 When items are inserted or removed in the m_startWithChildTrees configuration,
560 this method helps find the startRow for use emitting the signals from the proxy.
561 */
562 int getProxyInitialRow(const QModelIndex &parent) const;
563
564 /**
565 If m_startWithChildTrees is true, this method returns the row in the proxy model to insert newIndex
566 items.
567
568 This is a special case because the items above rootListRow in the list are not in the model, but
569 their children are. Those children must be counted.
570
571 If m_startWithChildTrees is false, this method returns @p rootListRow.
572 */
573 int getTargetRow(int rootListRow);
574
575 /**
576 Inserts the indexes in @p list into the proxy model.
577 */
578 void insertSelectionIntoProxy(const QItemSelection &selection);
579
580 bool m_startWithChildTrees;
581 bool m_omitChildren;
582 bool m_omitDescendants;
583 bool m_includeAllSelected;
584 bool m_rowsInserted;
585 bool m_rowsRemoved;
586 bool m_recreateFirstChildMappingOnRemoval;
587 QPair<int, int> m_proxyRemoveRows;
588 bool m_rowsMoved;
589 bool m_resetting;
590 bool m_sourceModelResetting;
591 bool m_doubleResetting;
592 bool m_layoutChanging;
593 bool m_ignoreNextLayoutAboutToBeChanged;
594 bool m_ignoreNextLayoutChanged;
595 QPointer<QItemSelectionModel> m_selectionModel;
596
597 KSelectionProxyModel::FilterBehavior m_filterBehavior;
598
599 QList<QPersistentModelIndex> m_layoutChangePersistentIndexes;
600 QModelIndexList m_proxyIndexes;
601
602 struct PendingSelectionChange {
PendingSelectionChangeKSelectionProxyModelPrivate::PendingSelectionChange603 PendingSelectionChange()
604 {
605 }
PendingSelectionChangeKSelectionProxyModelPrivate::PendingSelectionChange606 PendingSelectionChange(const QItemSelection &selected_, const QItemSelection &deselected_)
607 : selected(selected_)
608 , deselected(deselected_)
609 {
610 }
611 QItemSelection selected;
612 QItemSelection deselected;
613 };
614 QVector<PendingSelectionChange> m_pendingSelectionChanges;
615 QMetaObject::Connection selectionModelModelAboutToBeResetConnection;
616 QMetaObject::Connection selectionModelModelResetConnection;
617 };
618
emitContinuousRanges(const QModelIndex & sourceFirst,const QModelIndex & sourceLast,const QModelIndex & proxyFirst,const QModelIndex & proxyLast)619 void KSelectionProxyModelPrivate::emitContinuousRanges(const QModelIndex &sourceFirst,
620 const QModelIndex &sourceLast,
621 const QModelIndex &proxyFirst,
622 const QModelIndex &proxyLast)
623 {
624 Q_Q(KSelectionProxyModel);
625
626 Q_ASSERT(sourceFirst.model() == q->sourceModel());
627 Q_ASSERT(sourceLast.model() == q->sourceModel());
628 Q_ASSERT(proxyFirst.model() == q);
629 Q_ASSERT(proxyLast.model() == q);
630
631 const int proxyRangeSize = proxyLast.row() - proxyFirst.row();
632 const int sourceRangeSize = sourceLast.row() - sourceFirst.row();
633
634 if (proxyRangeSize == sourceRangeSize) {
635 Q_EMIT q->dataChanged(proxyFirst, proxyLast);
636 return;
637 }
638
639 // TODO: Loop to skip descendant ranges.
640 // int lastRow;
641 //
642 // const QModelIndex sourceHalfWay = sourceFirst.sibling(sourceFirst.row() + (sourceRangeSize / 2));
643 // const QModelIndex proxyHalfWay = proxyFirst.sibling(proxyFirst.row() + (proxyRangeSize / 2));
644 // const QModelIndex mappedSourceHalfway = q->mapToSource(proxyHalfWay);
645 //
646 // const int halfProxyRange = mappedSourceHalfway.row() - proxyFirst.row();
647 // const int halfSourceRange = sourceHalfWay.row() - sourceFirst.row();
648 //
649 // if (proxyRangeSize == sourceRangeSize)
650 // {
651 // Q_EMIT q->dataChanged(proxyFirst, proxyLast.sibling(proxyFirst.row() + proxyRangeSize, proxyLast.column()));
652 // return;
653 // }
654
655 Q_EMIT q->dataChanged(proxyFirst, proxyLast);
656 }
657
sourceDataChanged(const QModelIndex & topLeft,const QModelIndex & bottomRight)658 void KSelectionProxyModelPrivate::sourceDataChanged(const QModelIndex &topLeft, const QModelIndex &bottomRight)
659 {
660 Q_Q(KSelectionProxyModel);
661
662 Q_ASSERT(topLeft.model() == q->sourceModel());
663 Q_ASSERT(bottomRight.model() == q->sourceModel());
664
665 const QModelIndex sourceRangeParent = topLeft.parent();
666 if (!sourceRangeParent.isValid() && m_startWithChildTrees && !m_rootIndexList.contains(sourceRangeParent)) {
667 return;
668 }
669
670 const QModelIndex proxyTopLeft = q->mapFromSource(topLeft);
671 const QModelIndex proxyBottomRight = q->mapFromSource(bottomRight);
672
673 const QModelIndex proxyRangeParent = proxyTopLeft.parent();
674
675 if (!m_omitChildren && m_omitDescendants && m_startWithChildTrees && m_includeAllSelected) {
676 // ChildrenOfExactSelection
677 if (proxyTopLeft.isValid()) {
678 emitContinuousRanges(topLeft, bottomRight, proxyTopLeft, proxyBottomRight);
679 }
680 return;
681 }
682
683 if ((m_omitChildren && !m_startWithChildTrees && m_includeAllSelected) || (!proxyRangeParent.isValid() && !m_startWithChildTrees)) {
684 // Exact selection and SubTreeRoots and SubTrees in top level
685 // Emit continuous ranges.
686 QList<int> changedRows;
687 for (int row = topLeft.row(); row <= bottomRight.row(); ++row) {
688 const QModelIndex index = q->sourceModel()->index(row, topLeft.column(), topLeft.parent());
689 const int idx = m_rootIndexList.indexOf(index);
690 if (idx != -1) {
691 changedRows.append(idx);
692 }
693 }
694 if (changedRows.isEmpty()) {
695 return;
696 }
697 int first = changedRows.first();
698 int previous = first;
699 QList<int>::const_iterator it = changedRows.constBegin();
700 const QList<int>::const_iterator end = changedRows.constEnd();
701 for (; it != end; ++it) {
702 if (*it == previous + 1) {
703 ++previous;
704 } else {
705 const QModelIndex _top = q->index(first, topLeft.column());
706 const QModelIndex _bottom = q->index(previous, bottomRight.column());
707 Q_EMIT q->dataChanged(_top, _bottom);
708 previous = first = *it;
709 }
710 }
711 if (first != previous) {
712 const QModelIndex _top = q->index(first, topLeft.column());
713 const QModelIndex _bottom = q->index(previous, bottomRight.column());
714 Q_EMIT q->dataChanged(_top, _bottom);
715 }
716 return;
717 }
718 if (proxyRangeParent.isValid()) {
719 if (m_omitChildren && !m_startWithChildTrees && !m_includeAllSelected)
720 // SubTreeRoots
721 {
722 return;
723 }
724 if (!proxyTopLeft.isValid()) {
725 return;
726 }
727 // SubTrees and SubTreesWithoutRoots
728 Q_EMIT q->dataChanged(proxyTopLeft, proxyBottomRight);
729 return;
730 }
731
732 if (m_startWithChildTrees && !m_omitChildren && !m_includeAllSelected && !m_omitDescendants) {
733 // SubTreesWithoutRoots
734 if (proxyTopLeft.isValid()) {
735 Q_EMIT q->dataChanged(proxyTopLeft, proxyBottomRight);
736 }
737 return;
738 }
739 }
740
sourceLayoutAboutToBeChanged()741 void KSelectionProxyModelPrivate::sourceLayoutAboutToBeChanged()
742 {
743 Q_Q(KSelectionProxyModel);
744
745 if (m_ignoreNextLayoutAboutToBeChanged) {
746 m_ignoreNextLayoutAboutToBeChanged = false;
747 return;
748 }
749
750 if (m_rootIndexList.isEmpty()) {
751 return;
752 }
753
754 Q_EMIT q->layoutAboutToBeChanged();
755
756 QItemSelection selection;
757 for (const QModelIndex &rootIndex : std::as_const(m_rootIndexList)) {
758 // This will be optimized later.
759 Q_EMIT q->rootIndexAboutToBeRemoved(rootIndex, {});
760 selection.append(QItemSelectionRange(rootIndex, rootIndex));
761 }
762
763 selection = kNormalizeSelection(selection);
764 Q_EMIT q->rootSelectionAboutToBeRemoved(selection, {});
765
766 QPersistentModelIndex srcPersistentIndex;
767 const auto lst = q->persistentIndexList();
768 for (const QModelIndex &proxyPersistentIndex : lst) {
769 m_proxyIndexes << proxyPersistentIndex;
770 Q_ASSERT(proxyPersistentIndex.isValid());
771 srcPersistentIndex = q->mapToSource(proxyPersistentIndex);
772 Q_ASSERT(srcPersistentIndex.isValid());
773 m_layoutChangePersistentIndexes << srcPersistentIndex;
774 }
775
776 m_rootIndexList.clear();
777 }
778
sourceLayoutChanged()779 void KSelectionProxyModelPrivate::sourceLayoutChanged()
780 {
781 Q_Q(KSelectionProxyModel);
782
783 if (m_ignoreNextLayoutChanged) {
784 m_ignoreNextLayoutChanged = false;
785 return;
786 }
787
788 if (!m_selectionModel || !m_selectionModel->hasSelection()) {
789 return;
790 }
791
792 // Handling this signal is slow.
793 // The problem is that anything can happen between emissions of layoutAboutToBeChanged and layoutChanged.
794 // We can't assume anything is the same about the structure anymore. items have been sorted, items which
795 // were parents before are now not, items which were not parents before now are, items which used to be the
796 // first child are now the Nth child and items which used to be the Nth child are now the first child.
797 // We effectively can't update our mapping because we don't have enough information to update everything.
798 // The only way we would have is if we take a persistent index of the entire source model
799 // on sourceLayoutAboutToBeChanged and then examine it here. That would be far too expensive.
800 // Instead we just have to clear the entire mapping and recreate it.
801
802 m_rootIndexList.clear();
803 m_mappedFirstChildren.clear();
804 m_mappedParents.clear();
805 m_parentIds.clear();
806
807 m_resetting = true;
808 m_layoutChanging = true;
809 selectionChanged(m_selectionModel->selection(), QItemSelection());
810 m_resetting = false;
811 m_layoutChanging = false;
812
813 for (int i = 0; i < m_proxyIndexes.size(); ++i) {
814 q->changePersistentIndex(m_proxyIndexes.at(i), q->mapFromSource(m_layoutChangePersistentIndexes.at(i)));
815 }
816
817 m_layoutChangePersistentIndexes.clear();
818 m_proxyIndexes.clear();
819
820 Q_EMIT q->layoutChanged();
821 }
822
resetInternalData()823 void KSelectionProxyModelPrivate::resetInternalData()
824 {
825 m_rootIndexList.clear();
826 m_layoutChangePersistentIndexes.clear();
827 m_proxyIndexes.clear();
828 m_mappedParents.clear();
829 m_parentIds.clear();
830 m_mappedFirstChildren.clear();
831 m_voidPointerFactory.clear();
832 }
833
sourceModelDestroyed()834 void KSelectionProxyModelPrivate::sourceModelDestroyed()
835 {
836 // There is very little we can do here.
837 resetInternalData();
838 m_resetting = false;
839 m_sourceModelResetting = false;
840 }
841
sourceModelAboutToBeReset()842 void KSelectionProxyModelPrivate::sourceModelAboutToBeReset()
843 {
844 Q_Q(KSelectionProxyModel);
845
846 // We might be resetting as a result of the selection source model resetting.
847 // If so we don't want to emit
848 // sourceModelAboutToBeReset
849 // sourceModelAboutToBeReset
850 // sourceModelReset
851 // sourceModelReset
852 // So we ensure that we just emit one.
853 if (m_resetting) {
854 // If both the source model and the selection source model are reset,
855 // We want to begin our reset before the first one is reset and end
856 // it after the second one is reset.
857 m_doubleResetting = true;
858 return;
859 }
860
861 q->beginResetModel();
862 m_resetting = true;
863 m_sourceModelResetting = true;
864 }
865
sourceModelReset()866 void KSelectionProxyModelPrivate::sourceModelReset()
867 {
868 Q_Q(KSelectionProxyModel);
869
870 if (m_doubleResetting) {
871 m_doubleResetting = false;
872 return;
873 }
874
875 resetInternalData();
876 m_sourceModelResetting = false;
877 m_resetting = false;
878 selectionChanged(m_selectionModel->selection(), QItemSelection());
879 q->endResetModel();
880 }
881
getProxyInitialRow(const QModelIndex & parent) const882 int KSelectionProxyModelPrivate::getProxyInitialRow(const QModelIndex &parent) const
883 {
884 Q_ASSERT(m_rootIndexList.contains(parent));
885
886 // The difficulty here is that parent and parent.parent() might both be in the m_rootIndexList.
887
888 // - A
889 // - B
890 // - - C
891 // - - D
892 // - - - E
893
894 // Consider that B and D are selected. The proxy model is:
895
896 // - C
897 // - D
898 // - E
899
900 // Then D gets a new child at 0. In that case we require adding F between D and E.
901
902 // Consider instead that D gets removed. Then @p parent will be B.
903
904 Q_Q(const KSelectionProxyModel);
905
906 Q_ASSERT(parent.model() == q->sourceModel());
907
908 int parentPosition = m_rootIndexList.indexOf(parent);
909
910 QModelIndex parentAbove;
911
912 // If parentPosition == 0, then parent.parent() is not also in the model. (ordering is preserved)
913 while (parentPosition > 0) {
914 parentPosition--;
915
916 parentAbove = m_rootIndexList.at(parentPosition);
917 Q_ASSERT(parentAbove.isValid());
918
919 int rows = q->sourceModel()->rowCount(parentAbove);
920 if (rows > 0) {
921 QModelIndex sourceIndexAbove = q->sourceModel()->index(rows - 1, 0, parentAbove);
922 Q_ASSERT(sourceIndexAbove.isValid());
923 QModelIndex proxyChildAbove = mapFromSource(sourceIndexAbove);
924 Q_ASSERT(proxyChildAbove.isValid());
925 return proxyChildAbove.row() + 1;
926 }
927 }
928 return 0;
929 }
930
updateFirstChildMapping(const QModelIndex & parent,int offset)931 void KSelectionProxyModelPrivate::updateFirstChildMapping(const QModelIndex &parent, int offset)
932 {
933 Q_Q(KSelectionProxyModel);
934
935 Q_ASSERT(parent.isValid() ? parent.model() == q->sourceModel() : true);
936
937 static const int column = 0;
938 static const int row = 0;
939
940 const QPersistentModelIndex srcIndex = q->sourceModel()->index(row, column, parent);
941
942 const QPersistentModelIndex previousFirstChild = q->sourceModel()->index(offset, column, parent);
943
944 SourceIndexProxyRowMapping::left_iterator it = m_mappedFirstChildren.findLeft(previousFirstChild);
945 if (it == m_mappedFirstChildren.leftEnd()) {
946 return;
947 }
948
949 Q_ASSERT(srcIndex.isValid());
950 const int proxyRow = it.value();
951 Q_ASSERT(proxyRow >= 0);
952
953 m_mappedFirstChildren.eraseLeft(it);
954
955 // The proxy row in the mapping has already been updated by the offset in updateInternalTopIndexes
956 // so we restore it by applying the reverse.
957 m_mappedFirstChildren.insert(srcIndex, proxyRow - offset);
958 }
959
beginInsertRows(const QModelIndex & parent,int start,int end) const960 QPair<int, int> KSelectionProxyModelPrivate::beginInsertRows(const QModelIndex &parent, int start, int end) const
961 {
962 const QModelIndex proxyParent = mapFromSource(parent);
963
964 if (!proxyParent.isValid()) {
965 if (!m_startWithChildTrees) {
966 return qMakePair(-1, -1);
967 }
968
969 if (!m_rootIndexList.contains(parent)) {
970 return qMakePair(-1, -1);
971 }
972 }
973
974 if (!m_startWithChildTrees) {
975 // SubTrees
976 if (proxyParent.isValid()) {
977 return qMakePair(start, end);
978 }
979 return qMakePair(-1, -1);
980 }
981
982 if (!m_includeAllSelected && proxyParent.isValid()) {
983 // SubTreesWithoutRoots deeper than topLevel
984 return qMakePair(start, end);
985 }
986
987 if (!m_rootIndexList.contains(parent)) {
988 return qMakePair(-1, -1);
989 }
990
991 const int proxyStartRow = getProxyInitialRow(parent) + start;
992 return qMakePair(proxyStartRow, proxyStartRow + (end - start));
993 }
994
sourceRowsAboutToBeInserted(const QModelIndex & parent,int start,int end)995 void KSelectionProxyModelPrivate::sourceRowsAboutToBeInserted(const QModelIndex &parent, int start, int end)
996 {
997 Q_Q(KSelectionProxyModel);
998
999 Q_ASSERT(parent.isValid() ? parent.model() == q->sourceModel() : true);
1000
1001 if (!m_selectionModel || !m_selectionModel->hasSelection()) {
1002 return;
1003 }
1004
1005 if (m_omitChildren)
1006 // ExactSelection and SubTreeRoots
1007 {
1008 return;
1009 }
1010
1011 // topLevel insertions can be ignored because topLevel items would need to be selected to affect the proxy.
1012 if (!parent.isValid()) {
1013 return;
1014 }
1015
1016 QPair<int, int> pair = beginInsertRows(parent, start, end);
1017 if (pair.first == -1) {
1018 return;
1019 }
1020
1021 const QModelIndex proxyParent = m_startWithChildTrees ? QModelIndex() : mapFromSource(parent);
1022
1023 m_rowsInserted = true;
1024 q->beginInsertRows(proxyParent, pair.first, pair.second);
1025 }
1026
endInsertRows(const QModelIndex & parent,int start,int end)1027 void KSelectionProxyModelPrivate::endInsertRows(const QModelIndex &parent, int start, int end)
1028 {
1029 Q_Q(const KSelectionProxyModel);
1030 const QModelIndex proxyParent = mapFromSource(parent);
1031 const int offset = end - start + 1;
1032
1033 const bool isNewParent = (q->sourceModel()->rowCount(parent) == offset);
1034
1035 if (m_startWithChildTrees && m_rootIndexList.contains(parent)) {
1036 const int proxyInitialRow = getProxyInitialRow(parent);
1037 Q_ASSERT(proxyInitialRow >= 0);
1038 const int proxyStartRow = proxyInitialRow + start;
1039
1040 updateInternalTopIndexes(proxyStartRow, offset);
1041 if (isNewParent) {
1042 createFirstChildMapping(parent, proxyStartRow);
1043 } else if (start == 0)
1044 // We already have a first child mapping, but what we have mapped is not the first child anymore
1045 // so we need to update it.
1046 {
1047 updateFirstChildMapping(parent, end + 1);
1048 }
1049 } else {
1050 Q_ASSERT(proxyParent.isValid());
1051 if (!isNewParent) {
1052 updateInternalIndexes(proxyParent, start, offset);
1053 } else {
1054 createParentMappings(parent.parent(), parent.row(), parent.row());
1055 }
1056 }
1057 createParentMappings(parent, start, end);
1058 }
1059
sourceRowsInserted(const QModelIndex & parent,int start,int end)1060 void KSelectionProxyModelPrivate::sourceRowsInserted(const QModelIndex &parent, int start, int end)
1061 {
1062 Q_Q(KSelectionProxyModel);
1063
1064 Q_ASSERT(parent.isValid() ? parent.model() == q->sourceModel() : true);
1065
1066 if (!m_rowsInserted) {
1067 return;
1068 }
1069 m_rowsInserted = false;
1070 endInsertRows(parent, start, end);
1071 q->endInsertRows();
1072 for (const PendingSelectionChange &pendingChange : std::as_const(m_pendingSelectionChanges)) {
1073 selectionChanged(pendingChange.selected, pendingChange.deselected);
1074 }
1075 m_pendingSelectionChanges.clear();
1076 }
1077
rootWillBeRemovedFrom(const QModelIndex & ancestor,int start,int end,const QModelIndex & root)1078 static bool rootWillBeRemovedFrom(const QModelIndex &ancestor, int start, int end, const QModelIndex &root)
1079 {
1080 Q_ASSERT(root.isValid());
1081
1082 auto parent = root;
1083 while (parent.isValid()) {
1084 auto prev = parent;
1085 parent = parent.parent();
1086 if (parent == ancestor) {
1087 return (prev.row() <= end && prev.row() >= start);
1088 }
1089 }
1090 return false;
1091 }
1092
rootWillBeRemoved(const QItemSelection & selection,const QModelIndex & root)1093 bool KSelectionProxyModelPrivate::rootWillBeRemoved(const QItemSelection &selection, const QModelIndex &root)
1094 {
1095 Q_ASSERT(root.isValid());
1096
1097 for (auto &r : selection) {
1098 if (m_includeAllSelected) {
1099 if (r.parent() == root.parent() && root.row() <= r.bottom() && root.row() >= r.top()) {
1100 return true;
1101 }
1102 } else {
1103 if (rootWillBeRemovedFrom(r.parent(), r.top(), r.bottom(), root)) {
1104 return true;
1105 }
1106 }
1107 }
1108 return false;
1109 }
1110
beginRemoveRows(const QModelIndex & parent,int start,int end) const1111 QPair<int, int> KSelectionProxyModelPrivate::beginRemoveRows(const QModelIndex &parent, int start, int end) const
1112 {
1113 Q_Q(const KSelectionProxyModel);
1114
1115 if (!m_includeAllSelected && !m_omitChildren) {
1116 // SubTrees and SubTreesWithoutRoots
1117 const QModelIndex proxyParent = mapParentFromSource(parent);
1118 if (proxyParent.isValid()) {
1119 return qMakePair(start, end);
1120 }
1121 }
1122
1123 if (m_startWithChildTrees && m_rootIndexList.contains(parent)) {
1124 const int proxyStartRow = getProxyInitialRow(parent) + start;
1125 const int proxyEndRow = proxyStartRow + (end - start);
1126 return qMakePair(proxyStartRow, proxyEndRow);
1127 }
1128
1129 QList<QPersistentModelIndex>::const_iterator rootIt = m_rootIndexList.constBegin();
1130 const QList<QPersistentModelIndex>::const_iterator rootEnd = m_rootIndexList.constEnd();
1131 int proxyStartRemove = 0;
1132
1133 for (; rootIt != rootEnd; ++rootIt) {
1134 if (rootWillBeRemovedFrom(parent, start, end, *rootIt)) {
1135 break;
1136 } else {
1137 if (m_startWithChildTrees) {
1138 proxyStartRemove += q->sourceModel()->rowCount(*rootIt);
1139 } else {
1140 ++proxyStartRemove;
1141 }
1142 }
1143 }
1144
1145 if (rootIt == rootEnd) {
1146 return qMakePair(-1, -1);
1147 }
1148
1149 int proxyEndRemove = proxyStartRemove;
1150
1151 for (; rootIt != rootEnd; ++rootIt) {
1152 if (!rootWillBeRemovedFrom(parent, start, end, *rootIt)) {
1153 break;
1154 }
1155 if (m_startWithChildTrees) {
1156 proxyEndRemove += q->sourceModel()->rowCount(*rootIt);
1157 } else {
1158 ++proxyEndRemove;
1159 }
1160 }
1161
1162 --proxyEndRemove;
1163 if (proxyEndRemove >= proxyStartRemove) {
1164 return qMakePair(proxyStartRemove, proxyEndRemove);
1165 }
1166 return qMakePair(-1, -1);
1167 }
1168
sourceRowsAboutToBeRemoved(const QModelIndex & parent,int start,int end)1169 void KSelectionProxyModelPrivate::sourceRowsAboutToBeRemoved(const QModelIndex &parent, int start, int end)
1170 {
1171 Q_Q(KSelectionProxyModel);
1172
1173 Q_ASSERT(parent.isValid() ? parent.model() == q->sourceModel() : true);
1174
1175 if (!m_selectionModel || !m_selectionModel->hasSelection()) {
1176 return;
1177 }
1178
1179 QPair<int, int> pair = beginRemoveRows(parent, start, end);
1180 if (pair.first == -1) {
1181 return;
1182 }
1183
1184 const QModelIndex proxyParent = mapParentFromSource(parent);
1185
1186 m_rowsRemoved = true;
1187 m_proxyRemoveRows = pair;
1188 m_recreateFirstChildMappingOnRemoval = m_mappedFirstChildren.leftContains(q->sourceModel()->index(start, 0, parent));
1189 q->beginRemoveRows(proxyParent, pair.first, pair.second);
1190 }
1191
endRemoveRows(const QModelIndex & sourceParent,int proxyStart,int proxyEnd)1192 void KSelectionProxyModelPrivate::endRemoveRows(const QModelIndex &sourceParent, int proxyStart, int proxyEnd)
1193 {
1194 const QModelIndex proxyParent = mapParentFromSource(sourceParent);
1195
1196 // We need to make sure to remove entries from the mappings before updating internal indexes.
1197
1198 // - A
1199 // - - B
1200 // - C
1201 // - - D
1202
1203 // If A and C are selected, B and D are in the proxy. B maps to row 0 and D maps to row 1.
1204 // If B is then deleted leaving only D in the proxy, D needs to be updated to be a mapping
1205 // to row 0 instead of row 1. If that is done before removing the mapping for B, then the mapping
1206 // for D would overwrite the mapping for B and then the code for removing mappings would incorrectly
1207 // remove D.
1208 // So we first remove B and then update D.
1209
1210 {
1211 SourceProxyIndexMapping::right_iterator it = m_mappedParents.rightBegin();
1212
1213 while (it != m_mappedParents.rightEnd()) {
1214 if (!it.value().isValid()) {
1215 m_parentIds.removeRight(it.key());
1216 it = m_mappedParents.eraseRight(it);
1217 } else {
1218 ++it;
1219 }
1220 }
1221 }
1222
1223 {
1224 // Depending on what is selected at the time, a single removal in the source could invalidate
1225 // many mapped first child items at once.
1226
1227 // - A
1228 // - B
1229 // - - C
1230 // - - D
1231 // - - - E
1232 // - - - F
1233 // - - - - G
1234 // - - - - H
1235
1236 // If D and F are selected, the proxy contains E, F, G, H. If B is then deleted E to H will
1237 // be removed, including both first child mappings at E and G.
1238
1239 if (!proxyParent.isValid()) {
1240 removeFirstChildMappings(proxyStart, proxyEnd);
1241 }
1242 }
1243
1244 if (proxyParent.isValid()) {
1245 updateInternalIndexes(proxyParent, proxyEnd + 1, -1 * (proxyEnd - proxyStart + 1));
1246 } else {
1247 updateInternalTopIndexes(proxyEnd + 1, -1 * (proxyEnd - proxyStart + 1));
1248 }
1249
1250 QList<QPersistentModelIndex>::iterator rootIt = m_rootIndexList.begin();
1251 while (rootIt != m_rootIndexList.end()) {
1252 if (!rootIt->isValid()) {
1253 rootIt = m_rootIndexList.erase(rootIt);
1254 } else {
1255 ++rootIt;
1256 }
1257 }
1258 }
1259
sourceRowsRemoved(const QModelIndex & parent,int start,int end)1260 void KSelectionProxyModelPrivate::sourceRowsRemoved(const QModelIndex &parent, int start, int end)
1261 {
1262 Q_Q(KSelectionProxyModel);
1263 Q_UNUSED(end)
1264 Q_UNUSED(start)
1265
1266 Q_ASSERT(parent.isValid() ? parent.model() == q->sourceModel() : true);
1267
1268 if (!m_selectionModel) {
1269 return;
1270 }
1271
1272 if (!m_rowsRemoved) {
1273 return;
1274 }
1275 m_rowsRemoved = false;
1276
1277 Q_ASSERT(m_proxyRemoveRows.first >= 0);
1278 Q_ASSERT(m_proxyRemoveRows.second >= 0);
1279 endRemoveRows(parent, m_proxyRemoveRows.first, m_proxyRemoveRows.second);
1280 if (m_recreateFirstChildMappingOnRemoval && q->sourceModel()->hasChildren(parent))
1281 // The private endRemoveRows call might remove the first child mapping for parent, so
1282 // we create it again in that case.
1283 {
1284 createFirstChildMapping(parent, m_proxyRemoveRows.first);
1285 }
1286 m_recreateFirstChildMappingOnRemoval = false;
1287
1288 m_proxyRemoveRows = qMakePair(-1, -1);
1289 q->endRemoveRows();
1290 }
1291
sourceRowsAboutToBeMoved(const QModelIndex & srcParent,int srcStart,int srcEnd,const QModelIndex & destParent,int destRow)1292 void KSelectionProxyModelPrivate::sourceRowsAboutToBeMoved(const QModelIndex &srcParent, int srcStart, int srcEnd, const QModelIndex &destParent, int destRow)
1293 {
1294 Q_UNUSED(srcParent)
1295 Q_UNUSED(srcStart)
1296 Q_UNUSED(srcEnd)
1297 Q_UNUSED(destParent)
1298 Q_UNUSED(destRow)
1299 // we cheat and just act like the layout changed; this might benefit from some
1300 // optimization
1301 sourceLayoutAboutToBeChanged();
1302 }
1303
sourceRowsMoved(const QModelIndex & srcParent,int srcStart,int srcEnd,const QModelIndex & destParent,int destRow)1304 void KSelectionProxyModelPrivate::sourceRowsMoved(const QModelIndex &srcParent, int srcStart, int srcEnd, const QModelIndex &destParent, int destRow)
1305 {
1306 Q_UNUSED(srcParent)
1307 Q_UNUSED(srcStart)
1308 Q_UNUSED(srcEnd)
1309 Q_UNUSED(destParent)
1310 Q_UNUSED(destRow)
1311 // we cheat and just act like the layout changed; this might benefit from some
1312 // optimization
1313 sourceLayoutChanged();
1314 }
1315
mapParentToSource(const QModelIndex & proxyParent) const1316 QModelIndex KSelectionProxyModelPrivate::mapParentToSource(const QModelIndex &proxyParent) const
1317 {
1318 return m_mappedParents.rightToLeft(proxyParent);
1319 }
1320
mapParentFromSource(const QModelIndex & sourceParent) const1321 QModelIndex KSelectionProxyModelPrivate::mapParentFromSource(const QModelIndex &sourceParent) const
1322 {
1323 return m_mappedParents.leftToRight(sourceParent);
1324 }
1325
1326 static bool
indexIsValid(bool startWithChildTrees,int row,const QList<QPersistentModelIndex> & rootIndexList,const SourceIndexProxyRowMapping & mappedFirstChildren)1327 indexIsValid(bool startWithChildTrees, int row, const QList<QPersistentModelIndex> &rootIndexList, const SourceIndexProxyRowMapping &mappedFirstChildren)
1328 {
1329 if (!startWithChildTrees) {
1330 Q_ASSERT(rootIndexList.size() > row);
1331 } else {
1332 Q_ASSERT(!mappedFirstChildren.isEmpty());
1333
1334 SourceIndexProxyRowMapping::right_const_iterator result = mappedFirstChildren.rightUpperBound(row) - 1;
1335
1336 Q_ASSERT(result != mappedFirstChildren.rightEnd());
1337 const int proxyFirstRow = result.key();
1338 const QModelIndex sourceFirstChild = result.value();
1339 Q_ASSERT(proxyFirstRow >= 0);
1340 Q_ASSERT(sourceFirstChild.isValid());
1341 Q_ASSERT(sourceFirstChild.parent().isValid());
1342 Q_ASSERT(row <= proxyFirstRow + sourceFirstChild.model()->rowCount(sourceFirstChild.parent()));
1343 }
1344 return true;
1345 }
1346
createTopLevelIndex(int row,int column) const1347 QModelIndex KSelectionProxyModelPrivate::createTopLevelIndex(int row, int column) const
1348 {
1349 Q_Q(const KSelectionProxyModel);
1350
1351 Q_ASSERT(indexIsValid(m_startWithChildTrees, row, m_rootIndexList, m_mappedFirstChildren));
1352 return q->createIndex(row, column);
1353 }
1354
mapTopLevelFromSource(const QModelIndex & sourceIndex) const1355 QModelIndex KSelectionProxyModelPrivate::mapTopLevelFromSource(const QModelIndex &sourceIndex) const
1356 {
1357 Q_Q(const KSelectionProxyModel);
1358
1359 const QModelIndex sourceParent = sourceIndex.parent();
1360 const int row = m_rootIndexList.indexOf(sourceIndex);
1361 if (row == -1) {
1362 return QModelIndex();
1363 }
1364
1365 if (!m_startWithChildTrees) {
1366 Q_ASSERT(m_rootIndexList.size() > row);
1367 return q->createIndex(row, sourceIndex.column());
1368 }
1369 if (!m_rootIndexList.contains(sourceParent)) {
1370 return QModelIndex();
1371 }
1372
1373 const QModelIndex firstChild = q->sourceModel()->index(0, 0, sourceParent);
1374 const int firstProxyRow = m_mappedFirstChildren.leftToRight(firstChild);
1375
1376 return q->createIndex(firstProxyRow + sourceIndex.row(), sourceIndex.column());
1377 }
1378
mapFromSource(const QModelIndex & sourceIndex) const1379 QModelIndex KSelectionProxyModelPrivate::mapFromSource(const QModelIndex &sourceIndex) const
1380 {
1381 Q_Q(const KSelectionProxyModel);
1382
1383 const QModelIndex maybeMapped = mapParentFromSource(sourceIndex);
1384 if (maybeMapped.isValid()) {
1385 // Q_ASSERT((!d->m_startWithChildTrees && d->m_rootIndexList.contains(maybeMapped)) ? maybeMapped.row() < 0 : true );
1386 return maybeMapped;
1387 }
1388 const QModelIndex sourceParent = sourceIndex.parent();
1389
1390 const QModelIndex proxyParent = mapParentFromSource(sourceParent);
1391 if (proxyParent.isValid()) {
1392 void *const parentId = m_parentIds.rightToLeft(proxyParent);
1393 static const int column = 0;
1394 return q->createIndex(sourceIndex.row(), column, parentId);
1395 }
1396
1397 const QModelIndex firstChild = q->sourceModel()->index(0, 0, sourceParent);
1398
1399 if (m_mappedFirstChildren.leftContains(firstChild)) {
1400 const int firstProxyRow = m_mappedFirstChildren.leftToRight(firstChild);
1401 return q->createIndex(firstProxyRow + sourceIndex.row(), sourceIndex.column());
1402 }
1403 return mapTopLevelFromSource(sourceIndex);
1404 }
1405
topLevelRowCount() const1406 int KSelectionProxyModelPrivate::topLevelRowCount() const
1407 {
1408 Q_Q(const KSelectionProxyModel);
1409
1410 if (!m_startWithChildTrees) {
1411 return m_rootIndexList.size();
1412 }
1413
1414 if (m_mappedFirstChildren.isEmpty()) {
1415 return 0;
1416 }
1417
1418 const SourceIndexProxyRowMapping::right_const_iterator result = m_mappedFirstChildren.rightConstEnd() - 1;
1419
1420 const int proxyFirstRow = result.key();
1421 const QModelIndex sourceFirstChild = result.value();
1422 Q_ASSERT(sourceFirstChild.isValid());
1423 const QModelIndex sourceParent = sourceFirstChild.parent();
1424 Q_ASSERT(sourceParent.isValid());
1425 return q->sourceModel()->rowCount(sourceParent) + proxyFirstRow;
1426 }
1427
ensureMappable(const QModelIndex & parent) const1428 bool KSelectionProxyModelPrivate::ensureMappable(const QModelIndex &parent) const
1429 {
1430 Q_Q(const KSelectionProxyModel);
1431
1432 if (isFlat()) {
1433 return true;
1434 }
1435
1436 if (parentIsMappable(parent)) {
1437 return true;
1438 }
1439
1440 QModelIndex ancestor = parent.parent();
1441 QModelIndexList ancestorList;
1442 while (ancestor.isValid()) {
1443 if (parentIsMappable(ancestor)) {
1444 break;
1445 } else {
1446 ancestorList.prepend(ancestor);
1447 }
1448
1449 ancestor = ancestor.parent();
1450 }
1451
1452 if (!ancestor.isValid())
1453 // @p parent is not a descendant of m_rootIndexList.
1454 {
1455 return false;
1456 }
1457
1458 // sourceIndex can be mapped to the proxy. We just need to create mappings for its ancestors first.
1459 for (int i = 0; i < ancestorList.size(); ++i) {
1460 const QModelIndex existingAncestor = mapParentFromSource(ancestor);
1461 Q_ASSERT(existingAncestor.isValid());
1462
1463 void *const ansId = m_parentIds.rightToLeft(existingAncestor);
1464 const QModelIndex newSourceParent = ancestorList.at(i);
1465 const QModelIndex newProxyParent = q->createIndex(newSourceParent.row(), newSourceParent.column(), ansId);
1466
1467 void *const newId = m_voidPointerFactory.createPointer();
1468 m_parentIds.insert(newId, newProxyParent);
1469 m_mappedParents.insert(QPersistentModelIndex(newSourceParent), newProxyParent);
1470 ancestor = newSourceParent;
1471 }
1472 return true;
1473 }
1474
updateInternalTopIndexes(int start,int offset)1475 void KSelectionProxyModelPrivate::updateInternalTopIndexes(int start, int offset)
1476 {
1477 updateInternalIndexes(QModelIndex(), start, offset);
1478
1479 QHash<QPersistentModelIndex, int> updates;
1480 {
1481 SourceIndexProxyRowMapping::right_iterator it = m_mappedFirstChildren.rightLowerBound(start);
1482 const SourceIndexProxyRowMapping::right_iterator end = m_mappedFirstChildren.rightEnd();
1483
1484 for (; it != end; ++it) {
1485 updates.insert(*it, it.key() + offset);
1486 }
1487 }
1488 {
1489 QHash<QPersistentModelIndex, int>::const_iterator it = updates.constBegin();
1490 const QHash<QPersistentModelIndex, int>::const_iterator end = updates.constEnd();
1491
1492 for (; it != end; ++it) {
1493 m_mappedFirstChildren.insert(it.key(), it.value());
1494 }
1495 }
1496 }
1497
updateInternalIndexes(const QModelIndex & parent,int start,int offset)1498 void KSelectionProxyModelPrivate::updateInternalIndexes(const QModelIndex &parent, int start, int offset)
1499 {
1500 Q_Q(KSelectionProxyModel);
1501
1502 Q_ASSERT(start + offset >= 0);
1503 Q_ASSERT(parent.isValid() ? parent.model() == q : true);
1504
1505 if (isFlat()) {
1506 return;
1507 }
1508
1509 SourceProxyIndexMapping::left_iterator mappedParentIt = m_mappedParents.leftBegin();
1510
1511 QHash<void *, QModelIndex> updatedParentIds;
1512 QHash<QPersistentModelIndex, QModelIndex> updatedParents;
1513
1514 for (; mappedParentIt != m_mappedParents.leftEnd(); ++mappedParentIt) {
1515 const QModelIndex proxyIndex = mappedParentIt.value();
1516 Q_ASSERT(proxyIndex.isValid());
1517
1518 if (proxyIndex.row() < start) {
1519 continue;
1520 }
1521
1522 const QModelIndex proxyParent = proxyIndex.parent();
1523
1524 if (parent.isValid()) {
1525 if (proxyParent != parent) {
1526 continue;
1527 }
1528 } else {
1529 if (proxyParent.isValid()) {
1530 continue;
1531 }
1532 }
1533 Q_ASSERT(m_parentIds.rightContains(proxyIndex));
1534 void *const key = m_parentIds.rightToLeft(proxyIndex);
1535
1536 const QModelIndex newIndex = q->createIndex(proxyIndex.row() + offset, proxyIndex.column(), proxyIndex.internalPointer());
1537
1538 Q_ASSERT(newIndex.isValid());
1539
1540 updatedParentIds.insert(key, newIndex);
1541 updatedParents.insert(mappedParentIt.key(), newIndex);
1542 }
1543
1544 {
1545 QHash<QPersistentModelIndex, QModelIndex>::const_iterator it = updatedParents.constBegin();
1546 const QHash<QPersistentModelIndex, QModelIndex>::const_iterator end = updatedParents.constEnd();
1547 for (; it != end; ++it) {
1548 m_mappedParents.insert(it.key(), it.value());
1549 }
1550 }
1551
1552 {
1553 QHash<void *, QModelIndex>::const_iterator it = updatedParentIds.constBegin();
1554 const QHash<void *, QModelIndex>::const_iterator end = updatedParentIds.constEnd();
1555 for (; it != end; ++it) {
1556 m_parentIds.insert(it.key(), it.value());
1557 }
1558 }
1559 }
1560
parentAlreadyMapped(const QModelIndex & parent) const1561 bool KSelectionProxyModelPrivate::parentAlreadyMapped(const QModelIndex &parent) const
1562 {
1563 Q_Q(const KSelectionProxyModel);
1564 Q_UNUSED(q) // except in Q_ASSERT
1565 Q_ASSERT(parent.model() == q->sourceModel());
1566 return m_mappedParents.leftContains(parent);
1567 }
1568
firstChildAlreadyMapped(const QModelIndex & firstChild) const1569 bool KSelectionProxyModelPrivate::firstChildAlreadyMapped(const QModelIndex &firstChild) const
1570 {
1571 Q_Q(const KSelectionProxyModel);
1572 Q_UNUSED(q) // except in Q_ASSERT
1573 Q_ASSERT(firstChild.model() == q->sourceModel());
1574 return m_mappedFirstChildren.leftContains(firstChild);
1575 }
1576
createFirstChildMapping(const QModelIndex & parent,int proxyRow) const1577 void KSelectionProxyModelPrivate::createFirstChildMapping(const QModelIndex &parent, int proxyRow) const
1578 {
1579 Q_Q(const KSelectionProxyModel);
1580
1581 Q_ASSERT(parent.isValid() ? parent.model() == q->sourceModel() : true);
1582
1583 static const int column = 0;
1584 static const int row = 0;
1585
1586 const QPersistentModelIndex srcIndex = q->sourceModel()->index(row, column, parent);
1587
1588 if (firstChildAlreadyMapped(srcIndex)) {
1589 return;
1590 }
1591
1592 Q_ASSERT(srcIndex.isValid());
1593 m_mappedFirstChildren.insert(srcIndex, proxyRow);
1594 }
1595
createParentMappings(const QModelIndex & parent,int start,int end) const1596 void KSelectionProxyModelPrivate::createParentMappings(const QModelIndex &parent, int start, int end) const
1597 {
1598 if (isFlat()) {
1599 return;
1600 }
1601
1602 Q_Q(const KSelectionProxyModel);
1603
1604 Q_ASSERT(parent.isValid() ? parent.model() == q->sourceModel() : true);
1605
1606 static const int column = 0;
1607
1608 for (int row = start; row <= end; ++row) {
1609 const QModelIndex srcIndex = q->sourceModel()->index(row, column, parent);
1610 Q_ASSERT(srcIndex.isValid());
1611 if (!q->sourceModel()->hasChildren(srcIndex) || parentAlreadyMapped(srcIndex)) {
1612 continue;
1613 }
1614
1615 const QModelIndex proxyIndex = mapFromSource(srcIndex);
1616 if (!proxyIndex.isValid()) {
1617 return; // If one of them is not mapped, its siblings won't be either
1618 }
1619
1620 void *const newId = m_voidPointerFactory.createPointer();
1621 m_parentIds.insert(newId, proxyIndex);
1622 Q_ASSERT(srcIndex.isValid());
1623 m_mappedParents.insert(QPersistentModelIndex(srcIndex), proxyIndex);
1624 }
1625 }
1626
removeFirstChildMappings(int start,int end)1627 void KSelectionProxyModelPrivate::removeFirstChildMappings(int start, int end)
1628 {
1629 SourceIndexProxyRowMapping::right_iterator it = m_mappedFirstChildren.rightLowerBound(start);
1630 const SourceIndexProxyRowMapping::right_iterator endIt = m_mappedFirstChildren.rightUpperBound(end);
1631 while (it != endIt) {
1632 it = m_mappedFirstChildren.eraseRight(it);
1633 }
1634 }
1635
removeParentMappings(const QModelIndex & parent,int start,int end)1636 void KSelectionProxyModelPrivate::removeParentMappings(const QModelIndex &parent, int start, int end)
1637 {
1638 Q_Q(KSelectionProxyModel);
1639
1640 Q_ASSERT(parent.isValid() ? parent.model() == q : true);
1641
1642 SourceProxyIndexMapping::right_iterator it = m_mappedParents.rightBegin();
1643 SourceProxyIndexMapping::right_iterator endIt = m_mappedParents.rightEnd();
1644
1645 const bool flatList = isFlat();
1646
1647 while (it != endIt) {
1648 if (it.key().row() >= start && it.key().row() <= end) {
1649 const QModelIndex sourceParent = it.value();
1650 const QModelIndex proxyGrandParent = mapParentFromSource(sourceParent.parent());
1651 if (proxyGrandParent == parent) {
1652 if (!flatList)
1653 // Due to recursive calls, we could have several iterators on the container
1654 // when erase is called. That's safe according to the QHash::iterator docs though.
1655 {
1656 removeParentMappings(it.key(), 0, q->sourceModel()->rowCount(it.value()) - 1);
1657 }
1658
1659 m_parentIds.removeRight(it.key());
1660 it = m_mappedParents.eraseRight(it);
1661 } else {
1662 ++it;
1663 }
1664 } else {
1665 ++it;
1666 }
1667 }
1668 }
1669
mapTopLevelToSource(int row,int column) const1670 QModelIndex KSelectionProxyModelPrivate::mapTopLevelToSource(int row, int column) const
1671 {
1672 if (!m_startWithChildTrees) {
1673 const QModelIndex idx = m_rootIndexList.at(row);
1674 return idx.sibling(idx.row(), column);
1675 }
1676
1677 if (m_mappedFirstChildren.isEmpty()) {
1678 return QModelIndex();
1679 }
1680
1681 SourceIndexProxyRowMapping::right_iterator result = m_mappedFirstChildren.rightUpperBound(row) - 1;
1682
1683 Q_ASSERT(result != m_mappedFirstChildren.rightEnd());
1684
1685 const int proxyFirstRow = result.key();
1686 const QModelIndex sourceFirstChild = result.value();
1687 Q_ASSERT(sourceFirstChild.isValid());
1688 return sourceFirstChild.sibling(row - proxyFirstRow, column);
1689 }
1690
removeSelectionFromProxy(const QItemSelection & selection)1691 void KSelectionProxyModelPrivate::removeSelectionFromProxy(const QItemSelection &selection)
1692 {
1693 Q_Q(KSelectionProxyModel);
1694 if (selection.isEmpty()) {
1695 return;
1696 }
1697
1698 QList<QPersistentModelIndex>::iterator rootIt = m_rootIndexList.begin();
1699 const QList<QPersistentModelIndex>::iterator rootEnd = m_rootIndexList.end();
1700 int proxyStartRemove = 0;
1701
1702 for (; rootIt != rootEnd; ++rootIt) {
1703 if (rootWillBeRemoved(selection, *rootIt)) {
1704 break;
1705 } else {
1706 if (m_startWithChildTrees) {
1707 auto rc = q->sourceModel()->rowCount(*rootIt);
1708 proxyStartRemove += rc;
1709 } else {
1710 ++proxyStartRemove;
1711 }
1712 }
1713 }
1714
1715 if (rootIt == rootEnd) {
1716 return;
1717 }
1718
1719 int proxyEndRemove = proxyStartRemove;
1720
1721 QList<QPersistentModelIndex>::iterator rootRemoveStart = rootIt;
1722
1723 for (; rootIt != rootEnd; ++rootIt) {
1724 if (!rootWillBeRemoved(selection, *rootIt)) {
1725 break;
1726 }
1727 q->rootIndexAboutToBeRemoved(*rootIt, {});
1728 if (m_startWithChildTrees) {
1729 auto rc = q->sourceModel()->rowCount(*rootIt);
1730 proxyEndRemove += rc;
1731 } else {
1732 ++proxyEndRemove;
1733 }
1734 }
1735
1736 --proxyEndRemove;
1737 if (proxyEndRemove >= proxyStartRemove) {
1738 q->beginRemoveRows(QModelIndex(), proxyStartRemove, proxyEndRemove);
1739
1740 rootIt = m_rootIndexList.erase(rootRemoveStart, rootIt);
1741
1742 removeParentMappings(QModelIndex(), proxyStartRemove, proxyEndRemove);
1743 if (m_startWithChildTrees) {
1744 removeFirstChildMappings(proxyStartRemove, proxyEndRemove);
1745 }
1746 updateInternalTopIndexes(proxyEndRemove + 1, -1 * (proxyEndRemove - proxyStartRemove + 1));
1747
1748 q->endRemoveRows();
1749 } else {
1750 rootIt = m_rootIndexList.erase(rootRemoveStart, rootIt);
1751 }
1752 if (rootIt != rootEnd) {
1753 removeSelectionFromProxy(selection);
1754 }
1755 }
1756
selectionChanged(const QItemSelection & _selected,const QItemSelection & _deselected)1757 void KSelectionProxyModelPrivate::selectionChanged(const QItemSelection &_selected, const QItemSelection &_deselected)
1758 {
1759 Q_Q(KSelectionProxyModel);
1760
1761 if (!q->sourceModel() || (_selected.isEmpty() && _deselected.isEmpty())) {
1762 return;
1763 }
1764
1765 if (m_sourceModelResetting) {
1766 return;
1767 }
1768
1769 if (m_rowsInserted || m_rowsRemoved) {
1770 m_pendingSelectionChanges.append(PendingSelectionChange(_selected, _deselected));
1771 return;
1772 }
1773
1774 // Any deselected indexes in the m_rootIndexList are removed. Then, any
1775 // indexes in the selected range which are not a descendant of one of the already selected indexes
1776 // are inserted into the model.
1777 //
1778 // All ranges from the selection model need to be split into individual rows. Ranges which are contiguous in
1779 // the selection model may not be contiguous in the source model if there's a sort filter proxy model in the chain.
1780 //
1781 // Some descendants of deselected indexes may still be selected. The ranges in m_selectionModel->selection()
1782 // are examined. If any of the ranges are descendants of one of the
1783 // indexes in deselected, they are added to the ranges to be inserted into the model.
1784 //
1785 // The new indexes are inserted in sorted order.
1786
1787 const QItemSelection selected = kNormalizeSelection(m_indexMapper->mapSelectionRightToLeft(_selected));
1788 const QItemSelection deselected = kNormalizeSelection(m_indexMapper->mapSelectionRightToLeft(_deselected));
1789
1790 #if QT_VERSION < 0x040800
1791 // The QItemSelectionModel sometimes doesn't remove deselected items from its selection
1792 // Fixed in Qt 4.8 : http://qt.gitorious.org/qt/qt/merge_requests/2403
1793 QItemSelection reportedSelection = m_selectionModel->selection();
1794 reportedSelection.merge(deselected, QItemSelectionModel::Deselect);
1795 QItemSelection fullSelection = m_indexMapper->mapSelectionRightToLeft(reportedSelection);
1796 #else
1797 QItemSelection fullSelection = m_indexMapper->mapSelectionRightToLeft(m_selectionModel->selection());
1798 #endif
1799
1800 fullSelection = kNormalizeSelection(fullSelection);
1801
1802 QItemSelection newRootRanges;
1803 QItemSelection removedRootRanges;
1804 if (!m_includeAllSelected) {
1805 newRootRanges = getRootRanges(selected);
1806
1807 QItemSelection existingSelection = fullSelection;
1808 // What was selected before the selection was made.
1809 existingSelection.merge(selected, QItemSelectionModel::Deselect);
1810
1811 // This is similar to m_rootRanges, but that m_rootRanges at this point still contains the roots
1812 // of deselected and existingRootRanges does not.
1813
1814 const QItemSelection existingRootRanges = getRootRanges(existingSelection);
1815 {
1816 QMutableListIterator<QItemSelectionRange> i(newRootRanges);
1817 while (i.hasNext()) {
1818 const QItemSelectionRange range = i.next();
1819 const QModelIndex topLeft = range.topLeft();
1820 if (isDescendantOf(existingRootRanges, topLeft)) {
1821 i.remove();
1822 }
1823 }
1824 }
1825
1826 QItemSelection exposedSelection;
1827 {
1828 QItemSelection deselectedRootRanges = getRootRanges(deselected);
1829 QListIterator<QItemSelectionRange> i(deselectedRootRanges);
1830 while (i.hasNext()) {
1831 const QItemSelectionRange range = i.next();
1832 const QModelIndex topLeft = range.topLeft();
1833 // Consider this:
1834 //
1835 // - A
1836 // - - B
1837 // - - - C
1838 // - - - - D
1839 //
1840 // B and D were selected, then B was deselected and C was selected in one go.
1841 if (!isDescendantOf(existingRootRanges, topLeft)) {
1842 // B is topLeft and fullSelection contains D.
1843 // B is not a descendant of D.
1844
1845 // range is not a descendant of the selection, but maybe the selection is a descendant of range.
1846 // no need to check selected here. That's already in newRootRanges.
1847 // existingRootRanges and newRootRanges do not overlap.
1848 for (const QItemSelectionRange &selectedRange : existingRootRanges) {
1849 const QModelIndex selectedRangeTopLeft = selectedRange.topLeft();
1850 // existingSelection (and selectedRangeTopLeft) is D.
1851 // D is a descendant of B, so when B was removed, D might have been exposed as a root.
1852 if (isDescendantOf(range, selectedRangeTopLeft)
1853 // But D is also a descendant of part of the new selection C, which is already set to be a new root
1854 // so D would not be added to exposedSelection because C is in newRootRanges.
1855 && !isDescendantOf(newRootRanges, selectedRangeTopLeft)) {
1856 exposedSelection.append(selectedRange);
1857 }
1858 }
1859 removedRootRanges << range;
1860 }
1861 }
1862 }
1863
1864 QItemSelection obscuredRanges;
1865 {
1866 QListIterator<QItemSelectionRange> i(existingRootRanges);
1867 while (i.hasNext()) {
1868 const QItemSelectionRange range = i.next();
1869 if (isDescendantOf(newRootRanges, range.topLeft())) {
1870 obscuredRanges << range;
1871 }
1872 }
1873 }
1874 removedRootRanges << getRootRanges(obscuredRanges);
1875 newRootRanges << getRootRanges(exposedSelection);
1876
1877 removedRootRanges = kNormalizeSelection(removedRootRanges);
1878 newRootRanges = kNormalizeSelection(newRootRanges);
1879 } else {
1880 removedRootRanges = deselected;
1881 newRootRanges = selected;
1882 }
1883
1884 removeSelectionFromProxy(removedRootRanges);
1885
1886 if (!m_selectionModel->hasSelection()) {
1887 Q_ASSERT(m_rootIndexList.isEmpty());
1888 Q_ASSERT(m_mappedFirstChildren.isEmpty());
1889 Q_ASSERT(m_mappedParents.isEmpty());
1890 Q_ASSERT(m_parentIds.isEmpty());
1891 }
1892
1893 insertSelectionIntoProxy(newRootRanges);
1894 }
1895
getTargetRow(int rootListRow)1896 int KSelectionProxyModelPrivate::getTargetRow(int rootListRow)
1897 {
1898 Q_Q(KSelectionProxyModel);
1899 if (!m_startWithChildTrees) {
1900 return rootListRow;
1901 }
1902
1903 --rootListRow;
1904 while (rootListRow >= 0) {
1905 const QModelIndex idx = m_rootIndexList.at(rootListRow);
1906 Q_ASSERT(idx.isValid());
1907 const int rowCount = q->sourceModel()->rowCount(idx);
1908 if (rowCount > 0) {
1909 static const int column = 0;
1910 const QModelIndex srcIdx = q->sourceModel()->index(rowCount - 1, column, idx);
1911 const QModelIndex proxyLastChild = mapFromSource(srcIdx);
1912 return proxyLastChild.row() + 1;
1913 }
1914 --rootListRow;
1915 }
1916 return 0;
1917 }
1918
insertSelectionIntoProxy(const QItemSelection & selection)1919 void KSelectionProxyModelPrivate::insertSelectionIntoProxy(const QItemSelection &selection)
1920 {
1921 Q_Q(KSelectionProxyModel);
1922
1923 if (selection.isEmpty()) {
1924 return;
1925 }
1926
1927 const auto lst = selection.indexes();
1928 for (const QModelIndex &newIndex : lst) {
1929 Q_ASSERT(newIndex.model() == q->sourceModel());
1930 if (newIndex.column() > 0) {
1931 continue;
1932 }
1933 if (m_startWithChildTrees) {
1934 const int rootListRow = getRootListRow(m_rootIndexList, newIndex);
1935 Q_ASSERT(q->sourceModel() == newIndex.model());
1936 const int rowCount = q->sourceModel()->rowCount(newIndex);
1937 const int startRow = getTargetRow(rootListRow);
1938
1939 if (rowCount == 0) {
1940 // Even if the newindex doesn't have any children to put into the model yet,
1941 // We still need to make sure its future children are inserted into the model.
1942 m_rootIndexList.insert(rootListRow, newIndex);
1943 if (!m_resetting || m_layoutChanging) {
1944 Q_EMIT q->rootIndexAdded(newIndex, {});
1945 }
1946 continue;
1947 }
1948 if (!m_resetting) {
1949 q->beginInsertRows(QModelIndex(), startRow, startRow + rowCount - 1);
1950 }
1951 Q_ASSERT(newIndex.isValid());
1952 m_rootIndexList.insert(rootListRow, newIndex);
1953 if (!m_resetting || m_layoutChanging) {
1954 Q_EMIT q->rootIndexAdded(newIndex, {});
1955 }
1956
1957 int _start = 0;
1958 for (int i = 0; i < rootListRow; ++i) {
1959 _start += q->sourceModel()->rowCount(m_rootIndexList.at(i));
1960 }
1961
1962 updateInternalTopIndexes(_start, rowCount);
1963 createFirstChildMapping(newIndex, _start);
1964 createParentMappings(newIndex, 0, rowCount - 1);
1965
1966 if (!m_resetting) {
1967 q->endInsertRows();
1968 }
1969
1970 } else {
1971 const int row = getRootListRow(m_rootIndexList, newIndex);
1972 if (!m_resetting) {
1973 q->beginInsertRows(QModelIndex(), row, row);
1974 }
1975
1976 Q_ASSERT(newIndex.isValid());
1977 m_rootIndexList.insert(row, newIndex);
1978
1979 if (!m_resetting || m_layoutChanging) {
1980 Q_EMIT q->rootIndexAdded(newIndex, {});
1981 }
1982 Q_ASSERT(m_rootIndexList.size() > row);
1983 updateInternalIndexes(QModelIndex(), row, 1);
1984 createParentMappings(newIndex.parent(), newIndex.row(), newIndex.row());
1985
1986 if (!m_resetting) {
1987 q->endInsertRows();
1988 }
1989 }
1990 }
1991 q->rootSelectionAdded(selection, {});
1992 }
1993
KSelectionProxyModel(QItemSelectionModel * selectionModel,QObject * parent)1994 KSelectionProxyModel::KSelectionProxyModel(QItemSelectionModel *selectionModel, QObject *parent)
1995 : QAbstractProxyModel(parent)
1996 , d_ptr(new KSelectionProxyModelPrivate(this))
1997 {
1998 setSelectionModel(selectionModel);
1999 }
2000
KSelectionProxyModel()2001 KSelectionProxyModel::KSelectionProxyModel()
2002 : QAbstractProxyModel(nullptr)
2003 , d_ptr(new KSelectionProxyModelPrivate(this))
2004 {
2005 }
2006
2007 KSelectionProxyModel::~KSelectionProxyModel() = default;
2008
setFilterBehavior(FilterBehavior behavior)2009 void KSelectionProxyModel::setFilterBehavior(FilterBehavior behavior)
2010 {
2011 Q_D(KSelectionProxyModel);
2012
2013 Q_ASSERT(behavior != InvalidBehavior);
2014 if (behavior == InvalidBehavior) {
2015 return;
2016 }
2017 if (d->m_filterBehavior != behavior) {
2018 beginResetModel();
2019
2020 d->m_filterBehavior = behavior;
2021
2022 switch (behavior) {
2023 case InvalidBehavior: {
2024 Q_ASSERT(!"InvalidBehavior can't be used here");
2025 return;
2026 }
2027 case SubTrees: {
2028 d->m_omitChildren = false;
2029 d->m_omitDescendants = false;
2030 d->m_startWithChildTrees = false;
2031 d->m_includeAllSelected = false;
2032 break;
2033 }
2034 case SubTreeRoots: {
2035 d->m_omitChildren = true;
2036 d->m_startWithChildTrees = false;
2037 d->m_includeAllSelected = false;
2038 break;
2039 }
2040 case SubTreesWithoutRoots: {
2041 d->m_omitChildren = false;
2042 d->m_omitDescendants = false;
2043 d->m_startWithChildTrees = true;
2044 d->m_includeAllSelected = false;
2045 break;
2046 }
2047 case ExactSelection: {
2048 d->m_omitChildren = true;
2049 d->m_startWithChildTrees = false;
2050 d->m_includeAllSelected = true;
2051 break;
2052 }
2053 case ChildrenOfExactSelection: {
2054 d->m_omitChildren = false;
2055 d->m_omitDescendants = true;
2056 d->m_startWithChildTrees = true;
2057 d->m_includeAllSelected = true;
2058 break;
2059 }
2060 }
2061 Q_EMIT filterBehaviorChanged({});
2062 d->resetInternalData();
2063 if (d->m_selectionModel) {
2064 d->selectionChanged(d->m_selectionModel->selection(), QItemSelection());
2065 }
2066
2067 endResetModel();
2068 }
2069 }
2070
filterBehavior() const2071 KSelectionProxyModel::FilterBehavior KSelectionProxyModel::filterBehavior() const
2072 {
2073 Q_D(const KSelectionProxyModel);
2074 return d->m_filterBehavior;
2075 }
2076
setSourceModel(QAbstractItemModel * _sourceModel)2077 void KSelectionProxyModel::setSourceModel(QAbstractItemModel *_sourceModel)
2078 {
2079 Q_D(KSelectionProxyModel);
2080
2081 Q_ASSERT(_sourceModel != this);
2082
2083 if (_sourceModel == sourceModel()) {
2084 return;
2085 }
2086
2087 beginResetModel();
2088 d->m_resetting = true;
2089
2090 if (auto *oldSourceModel = sourceModel()) {
2091 disconnect(oldSourceModel, nullptr, this, nullptr);
2092 }
2093
2094 // Must be called before QAbstractProxyModel::setSourceModel because it emit some signals.
2095 d->resetInternalData();
2096 QAbstractProxyModel::setSourceModel(_sourceModel);
2097 if (_sourceModel) {
2098 if (d->m_selectionModel) {
2099 delete d->m_indexMapper;
2100 d->m_indexMapper = new KModelIndexProxyMapper(_sourceModel, d->m_selectionModel->model(), this);
2101 if (d->m_selectionModel->hasSelection()) {
2102 d->selectionChanged(d->m_selectionModel->selection(), QItemSelection());
2103 }
2104 }
2105
2106 connect(_sourceModel, &QAbstractItemModel::rowsAboutToBeInserted, this, [d](const QModelIndex &parent, int start, int end) {
2107 d->sourceRowsAboutToBeInserted(parent, start, end);
2108 });
2109
2110 connect(_sourceModel, &QAbstractItemModel::rowsInserted, this, [d](const QModelIndex &parent, int start, int end) {
2111 d->sourceRowsInserted(parent, start, end);
2112 });
2113
2114 connect(_sourceModel, &QAbstractItemModel::rowsAboutToBeRemoved, this, [d](const QModelIndex &parent, int start, int end) {
2115 d->sourceRowsAboutToBeRemoved(parent, start, end);
2116 });
2117
2118 connect(_sourceModel, &QAbstractItemModel::rowsRemoved, this, [d](const QModelIndex &parent, int start, int end) {
2119 d->sourceRowsRemoved(parent, start, end);
2120 });
2121
2122 connect(_sourceModel,
2123 &QAbstractItemModel::rowsAboutToBeMoved,
2124 this,
2125 [d](const QModelIndex &parent, int start, int end, const QModelIndex &destParent, int destRow) {
2126 d->sourceRowsAboutToBeMoved(parent, start, end, destParent, destRow);
2127 });
2128
2129 connect(_sourceModel,
2130 &QAbstractItemModel::rowsMoved,
2131 this,
2132 [d](const QModelIndex &parent, int start, int end, const QModelIndex &destParent, int destRow) {
2133 d->sourceRowsMoved(parent, start, end, destParent, destRow);
2134 });
2135
2136 connect(_sourceModel, &QAbstractItemModel::modelAboutToBeReset, this, [d]() {
2137 d->sourceModelAboutToBeReset();
2138 });
2139
2140 connect(_sourceModel, &QAbstractItemModel::modelReset, this, [d]() {
2141 d->sourceModelReset();
2142 });
2143
2144 connect(_sourceModel, &QAbstractItemModel::dataChanged, this, [d](const QModelIndex &topLeft, const QModelIndex &bottomRight) {
2145 d->sourceDataChanged(topLeft, bottomRight);
2146 });
2147
2148 connect(_sourceModel, &QAbstractItemModel::layoutAboutToBeChanged, this, [d]() {
2149 d->sourceLayoutAboutToBeChanged();
2150 });
2151
2152 connect(_sourceModel, &QAbstractItemModel::layoutChanged, this, [d]() {
2153 d->sourceLayoutChanged();
2154 });
2155
2156 connect(_sourceModel, &QObject::destroyed, this, [d]() {
2157 d->sourceModelDestroyed();
2158 });
2159 }
2160
2161 d->m_resetting = false;
2162 endResetModel();
2163 }
2164
mapToSource(const QModelIndex & proxyIndex) const2165 QModelIndex KSelectionProxyModel::mapToSource(const QModelIndex &proxyIndex) const
2166 {
2167 Q_D(const KSelectionProxyModel);
2168
2169 if (!proxyIndex.isValid() || !sourceModel() || d->m_rootIndexList.isEmpty()) {
2170 return QModelIndex();
2171 }
2172
2173 Q_ASSERT(proxyIndex.model() == this);
2174
2175 if (proxyIndex.internalPointer() == nullptr) {
2176 return d->mapTopLevelToSource(proxyIndex.row(), proxyIndex.column());
2177 }
2178
2179 const QModelIndex proxyParent = d->parentForId(proxyIndex.internalPointer());
2180 Q_ASSERT(proxyParent.isValid());
2181 const QModelIndex sourceParent = d->mapParentToSource(proxyParent);
2182 Q_ASSERT(sourceParent.isValid());
2183 return sourceModel()->index(proxyIndex.row(), proxyIndex.column(), sourceParent);
2184 }
2185
mapFromSource(const QModelIndex & sourceIndex) const2186 QModelIndex KSelectionProxyModel::mapFromSource(const QModelIndex &sourceIndex) const
2187 {
2188 Q_D(const KSelectionProxyModel);
2189
2190 if (!sourceModel() || !sourceIndex.isValid() || d->m_rootIndexList.isEmpty()) {
2191 return QModelIndex();
2192 }
2193
2194 Q_ASSERT(sourceIndex.model() == sourceModel());
2195
2196 if (!sourceIndex.isValid()) {
2197 return QModelIndex();
2198 }
2199
2200 if (!d->ensureMappable(sourceIndex)) {
2201 return QModelIndex();
2202 }
2203
2204 return d->mapFromSource(sourceIndex);
2205 }
2206
rowCount(const QModelIndex & index) const2207 int KSelectionProxyModel::rowCount(const QModelIndex &index) const
2208 {
2209 Q_D(const KSelectionProxyModel);
2210
2211 if (!sourceModel() || index.column() > 0 || d->m_rootIndexList.isEmpty()) {
2212 return 0;
2213 }
2214
2215 Q_ASSERT(index.isValid() ? index.model() == this : true);
2216 if (!index.isValid()) {
2217 return d->topLevelRowCount();
2218 }
2219
2220 // index is valid
2221 if (d->isFlat()) {
2222 return 0;
2223 }
2224
2225 QModelIndex sourceParent = d->mapParentToSource(index);
2226
2227 if (!sourceParent.isValid() && sourceModel()->hasChildren(sourceParent)) {
2228 sourceParent = mapToSource(index.parent());
2229 d->createParentMappings(sourceParent, 0, sourceModel()->rowCount(sourceParent) - 1);
2230 sourceParent = d->mapParentToSource(index);
2231 }
2232
2233 if (!sourceParent.isValid()) {
2234 return 0;
2235 }
2236
2237 return sourceModel()->rowCount(sourceParent);
2238 }
2239
index(int row,int column,const QModelIndex & parent) const2240 QModelIndex KSelectionProxyModel::index(int row, int column, const QModelIndex &parent) const
2241 {
2242 Q_D(const KSelectionProxyModel);
2243
2244 if (!sourceModel() || d->m_rootIndexList.isEmpty() || !hasIndex(row, column, parent)) {
2245 return QModelIndex();
2246 }
2247
2248 Q_ASSERT(parent.isValid() ? parent.model() == this : true);
2249
2250 if (!parent.isValid()) {
2251 return d->createTopLevelIndex(row, column);
2252 }
2253
2254 void *const parentId = d->parentId(parent);
2255 Q_ASSERT(parentId);
2256 return createIndex(row, column, parentId);
2257 }
2258
parent(const QModelIndex & index) const2259 QModelIndex KSelectionProxyModel::parent(const QModelIndex &index) const
2260 {
2261 Q_D(const KSelectionProxyModel);
2262
2263 if (!sourceModel() || !index.isValid() || d->m_rootIndexList.isEmpty()) {
2264 return QModelIndex();
2265 }
2266
2267 Q_ASSERT(index.model() == this);
2268
2269 return d->parentForId(index.internalPointer());
2270 }
2271
flags(const QModelIndex & index) const2272 Qt::ItemFlags KSelectionProxyModel::flags(const QModelIndex &index) const
2273 {
2274 if (!index.isValid() || !sourceModel()) {
2275 return QAbstractProxyModel::flags(index);
2276 }
2277
2278 Q_ASSERT(index.model() == this);
2279
2280 const QModelIndex srcIndex = mapToSource(index);
2281 Q_ASSERT(srcIndex.isValid());
2282 return sourceModel()->flags(srcIndex);
2283 }
2284
data(const QModelIndex & index,int role) const2285 QVariant KSelectionProxyModel::data(const QModelIndex &index, int role) const
2286 {
2287 if (!sourceModel()) {
2288 return QVariant();
2289 }
2290
2291 if (index.isValid()) {
2292 Q_ASSERT(index.model() == this);
2293 const QModelIndex idx = mapToSource(index);
2294 return idx.data(role);
2295 }
2296 return sourceModel()->data(index, role);
2297 }
2298
headerData(int section,Qt::Orientation orientation,int role) const2299 QVariant KSelectionProxyModel::headerData(int section, Qt::Orientation orientation, int role) const
2300 {
2301 if (!sourceModel()) {
2302 return QVariant();
2303 }
2304 return sourceModel()->headerData(section, orientation, role);
2305 }
2306
mimeData(const QModelIndexList & indexes) const2307 QMimeData *KSelectionProxyModel::mimeData(const QModelIndexList &indexes) const
2308 {
2309 if (!sourceModel()) {
2310 return QAbstractProxyModel::mimeData(indexes);
2311 }
2312 QModelIndexList sourceIndexes;
2313 for (const QModelIndex &index : indexes) {
2314 sourceIndexes << mapToSource(index);
2315 }
2316 return sourceModel()->mimeData(sourceIndexes);
2317 }
2318
mimeTypes() const2319 QStringList KSelectionProxyModel::mimeTypes() const
2320 {
2321 if (!sourceModel()) {
2322 return QAbstractProxyModel::mimeTypes();
2323 }
2324 return sourceModel()->mimeTypes();
2325 }
2326
supportedDropActions() const2327 Qt::DropActions KSelectionProxyModel::supportedDropActions() const
2328 {
2329 if (!sourceModel()) {
2330 return QAbstractProxyModel::supportedDropActions();
2331 }
2332 return sourceModel()->supportedDropActions();
2333 }
2334
hasChildren(const QModelIndex & parent) const2335 bool KSelectionProxyModel::hasChildren(const QModelIndex &parent) const
2336 {
2337 Q_D(const KSelectionProxyModel);
2338
2339 if (d->m_rootIndexList.isEmpty() || !sourceModel()) {
2340 return false;
2341 }
2342
2343 if (parent.isValid()) {
2344 Q_ASSERT(parent.model() == this);
2345 if (d->isFlat()) {
2346 return false;
2347 }
2348 return sourceModel()->hasChildren(mapToSource(parent));
2349 }
2350
2351 if (!d->m_startWithChildTrees) {
2352 return true;
2353 }
2354
2355 return !d->m_mappedFirstChildren.isEmpty();
2356 }
2357
columnCount(const QModelIndex & index) const2358 int KSelectionProxyModel::columnCount(const QModelIndex &index) const
2359 {
2360 if (!sourceModel() || index.column() > 0) {
2361 return 0;
2362 }
2363
2364 return sourceModel()->columnCount(mapToSource(index));
2365 }
2366
selectionModel() const2367 QItemSelectionModel *KSelectionProxyModel::selectionModel() const
2368 {
2369 Q_D(const KSelectionProxyModel);
2370 return d->m_selectionModel;
2371 }
2372
setSelectionModel(QItemSelectionModel * itemSelectionModel)2373 void KSelectionProxyModel::setSelectionModel(QItemSelectionModel *itemSelectionModel)
2374 {
2375 Q_D(KSelectionProxyModel);
2376 if (d->m_selectionModel != itemSelectionModel) {
2377 if (d->m_selectionModel) {
2378 disconnect(d->m_selectionModel,
2379 SIGNAL(selectionChanged(QItemSelection, QItemSelection)),
2380 this,
2381 SLOT(selectionChanged(QItemSelection, QItemSelection)));
2382 }
2383
2384 d->m_selectionModel = itemSelectionModel;
2385 Q_EMIT selectionModelChanged({});
2386
2387 if (d->m_selectionModel) {
2388 connect(d->m_selectionModel, SIGNAL(selectionChanged(QItemSelection, QItemSelection)), SLOT(selectionChanged(QItemSelection, QItemSelection)));
2389
2390 auto handleSelectionModelModel = [&, d] {
2391 beginResetModel();
2392 if (d->selectionModelModelAboutToBeResetConnection) {
2393 disconnect(d->selectionModelModelAboutToBeResetConnection);
2394 }
2395 if (d->selectionModelModelResetConnection) {
2396 disconnect(d->selectionModelModelResetConnection);
2397 }
2398 if (d->m_selectionModel->model()) {
2399 d->selectionModelModelAboutToBeResetConnection =
2400 connect(d->m_selectionModel->model(), SIGNAL(modelAboutToBeReset()), this, SLOT(sourceModelAboutToBeReset()));
2401 d->selectionModelModelResetConnection = connect(d->m_selectionModel->model(), SIGNAL(modelReset()), this, SLOT(sourceModelReset()));
2402 d->m_rootIndexList.clear();
2403 delete d->m_indexMapper;
2404 d->m_indexMapper = new KModelIndexProxyMapper(sourceModel(), d->m_selectionModel->model(), this);
2405 }
2406 endResetModel();
2407 };
2408 connect(d->m_selectionModel.data(), &QItemSelectionModel::modelChanged, this, handleSelectionModelModel);
2409 handleSelectionModelModel();
2410 }
2411
2412 if (sourceModel()) {
2413 delete d->m_indexMapper;
2414 d->m_indexMapper = new KModelIndexProxyMapper(sourceModel(), d->m_selectionModel->model(), this);
2415 if (d->m_selectionModel->hasSelection()) {
2416 d->selectionChanged(d->m_selectionModel->selection(), QItemSelection());
2417 }
2418 }
2419 }
2420 }
2421
dropMimeData(const QMimeData * data,Qt::DropAction action,int row,int column,const QModelIndex & parent)2422 bool KSelectionProxyModel::dropMimeData(const QMimeData *data, Qt::DropAction action, int row, int column, const QModelIndex &parent)
2423 {
2424 Q_D(const KSelectionProxyModel);
2425 if (!sourceModel() || d->m_rootIndexList.isEmpty()) {
2426 return false;
2427 }
2428
2429 if ((row == -1) && (column == -1)) {
2430 return sourceModel()->dropMimeData(data, action, -1, -1, mapToSource(parent));
2431 }
2432
2433 int source_destination_row = -1;
2434 int source_destination_column = -1;
2435 QModelIndex source_parent;
2436
2437 if (row == rowCount(parent)) {
2438 source_parent = mapToSource(parent);
2439 source_destination_row = sourceModel()->rowCount(source_parent);
2440 } else {
2441 const QModelIndex proxy_index = index(row, column, parent);
2442 const QModelIndex source_index = mapToSource(proxy_index);
2443 source_destination_row = source_index.row();
2444 source_destination_column = source_index.column();
2445 source_parent = source_index.parent();
2446 }
2447 return sourceModel()->dropMimeData(data, action, source_destination_row, source_destination_column, source_parent);
2448 }
2449
sourceRootIndexes() const2450 QList<QPersistentModelIndex> KSelectionProxyModel::sourceRootIndexes() const
2451 {
2452 Q_D(const KSelectionProxyModel);
2453 return d->m_rootIndexList;
2454 }
2455
match(const QModelIndex & start,int role,const QVariant & value,int hits,Qt::MatchFlags flags) const2456 QModelIndexList KSelectionProxyModel::match(const QModelIndex &start, int role, const QVariant &value, int hits, Qt::MatchFlags flags) const
2457 {
2458 if (role < Qt::UserRole) {
2459 return QAbstractProxyModel::match(start, role, value, hits, flags);
2460 }
2461
2462 QModelIndexList list;
2463 QModelIndex proxyIndex;
2464 const auto lst = sourceModel()->match(mapToSource(start), role, value, hits, flags);
2465 for (const QModelIndex &idx : lst) {
2466 proxyIndex = mapFromSource(idx);
2467 if (proxyIndex.isValid()) {
2468 list << proxyIndex;
2469 }
2470 }
2471 return list;
2472 }
2473
mapSelectionFromSource(const QItemSelection & selection) const2474 QItemSelection KSelectionProxyModel::mapSelectionFromSource(const QItemSelection &selection) const
2475 {
2476 Q_D(const KSelectionProxyModel);
2477 if (!d->m_startWithChildTrees && d->m_includeAllSelected) {
2478 // QAbstractProxyModel::mapSelectionFromSource puts invalid ranges in the result
2479 // without checking. We can't have that.
2480 QItemSelection proxySelection;
2481 for (const QItemSelectionRange &range : selection) {
2482 QModelIndex proxyTopLeft = mapFromSource(range.topLeft());
2483 if (!proxyTopLeft.isValid()) {
2484 continue;
2485 }
2486 QModelIndex proxyBottomRight = mapFromSource(range.bottomRight());
2487 Q_ASSERT(proxyBottomRight.isValid());
2488 proxySelection.append(QItemSelectionRange(proxyTopLeft, proxyBottomRight));
2489 }
2490 return proxySelection;
2491 }
2492
2493 QItemSelection proxySelection;
2494 QItemSelection::const_iterator it = selection.constBegin();
2495 const QItemSelection::const_iterator end = selection.constEnd();
2496 for (; it != end; ++it) {
2497 const QModelIndex proxyTopLeft = mapFromSource(it->topLeft());
2498 if (!proxyTopLeft.isValid()) {
2499 continue;
2500 }
2501
2502 if (it->height() == 1 && it->width() == 1) {
2503 proxySelection.append(QItemSelectionRange(proxyTopLeft, proxyTopLeft));
2504 } else {
2505 proxySelection.append(QItemSelectionRange(proxyTopLeft, d->mapFromSource(it->bottomRight())));
2506 }
2507 }
2508 return proxySelection;
2509 }
2510
mapSelectionToSource(const QItemSelection & selection) const2511 QItemSelection KSelectionProxyModel::mapSelectionToSource(const QItemSelection &selection) const
2512 {
2513 Q_D(const KSelectionProxyModel);
2514
2515 if (selection.isEmpty()) {
2516 return selection;
2517 }
2518
2519 if (!d->m_startWithChildTrees && d->m_includeAllSelected) {
2520 // QAbstractProxyModel::mapSelectionFromSource puts invalid ranges in the result
2521 // without checking. We can't have that.
2522 QItemSelection sourceSelection;
2523 for (const QItemSelectionRange &range : selection) {
2524 QModelIndex sourceTopLeft = mapToSource(range.topLeft());
2525 Q_ASSERT(sourceTopLeft.isValid());
2526
2527 QModelIndex sourceBottomRight = mapToSource(range.bottomRight());
2528 Q_ASSERT(sourceBottomRight.isValid());
2529 sourceSelection.append(QItemSelectionRange(sourceTopLeft, sourceBottomRight));
2530 }
2531 return sourceSelection;
2532 }
2533
2534 QItemSelection sourceSelection;
2535 QItemSelection extraSelection;
2536 QItemSelection::const_iterator it = selection.constBegin();
2537 const QItemSelection::const_iterator end = selection.constEnd();
2538 for (; it != end; ++it) {
2539 const QModelIndex sourceTopLeft = mapToSource(it->topLeft());
2540 if (it->height() == 1 && it->width() == 1) {
2541 sourceSelection.append(QItemSelectionRange(sourceTopLeft, sourceTopLeft));
2542 } else if (it->parent().isValid()) {
2543 sourceSelection.append(QItemSelectionRange(sourceTopLeft, mapToSource(it->bottomRight())));
2544 } else {
2545 // A contiguous selection in the proxy might not be contiguous in the source if it
2546 // is at the top level of the proxy.
2547 if (d->m_startWithChildTrees) {
2548 const QModelIndex sourceParent = mapFromSource(sourceTopLeft);
2549 Q_ASSERT(sourceParent.isValid());
2550 const int rowCount = sourceModel()->rowCount(sourceParent);
2551 if (rowCount < it->bottom()) {
2552 Q_ASSERT(sourceTopLeft.isValid());
2553 Q_ASSERT(it->bottomRight().isValid());
2554 const QModelIndex sourceBottomRight = mapToSource(it->bottomRight());
2555 Q_ASSERT(sourceBottomRight.isValid());
2556 sourceSelection.append(QItemSelectionRange(sourceTopLeft, sourceBottomRight));
2557 continue;
2558 }
2559 // Store the contiguous part...
2560 const QModelIndex sourceBottomRight = sourceModel()->index(rowCount - 1, it->right(), sourceParent);
2561 Q_ASSERT(sourceTopLeft.isValid());
2562 Q_ASSERT(sourceBottomRight.isValid());
2563 sourceSelection.append(QItemSelectionRange(sourceTopLeft, sourceBottomRight));
2564 // ... and the rest will be processed later.
2565 extraSelection.append(QItemSelectionRange(createIndex(it->top() - rowCount, it->right()), it->bottomRight()));
2566 } else {
2567 QItemSelection topSelection;
2568 const QModelIndex idx = createIndex(it->top(), it->right());
2569 const QModelIndex sourceIdx = mapToSource(idx);
2570 Q_ASSERT(sourceIdx.isValid());
2571 topSelection.append(QItemSelectionRange(sourceTopLeft, sourceIdx));
2572 for (int i = it->top() + 1; i <= it->bottom(); ++i) {
2573 const QModelIndex left = mapToSource(createIndex(i, 0));
2574 const QModelIndex right = mapToSource(createIndex(i, it->right()));
2575 Q_ASSERT(left.isValid());
2576 Q_ASSERT(right.isValid());
2577 topSelection.append(QItemSelectionRange(left, right));
2578 }
2579 sourceSelection += kNormalizeSelection(topSelection);
2580 }
2581 }
2582 }
2583 sourceSelection << mapSelectionToSource(extraSelection);
2584 return sourceSelection;
2585 }
2586
2587 #include "moc_kselectionproxymodel.cpp"
2588