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