1/*
2 SPDX-FileCopyrightText: 2010 Klarälvdalens Datakonsult AB, a KDAB Group company <info@kdab.net>
3 SPDX-FileContributor: Stephen Kelly <stephen@kdab.com>
4
5 SPDX-License-Identifier: LGPL-2.0-or-later
6*/
7
8#include "kbreadcrumbselectionmodel.h"
9
10class KBreadcrumbSelectionModelPrivate
11{
12 Q_DECLARE_PUBLIC(KBreadcrumbSelectionModel)
13 KBreadcrumbSelectionModel *const q_ptr;
14
15public:
16 KBreadcrumbSelectionModelPrivate(KBreadcrumbSelectionModel *breadcrumbSelector,
17 QItemSelectionModel *selectionModel,
18 KBreadcrumbSelectionModel::BreadcrumbTarget direction);
19
20 /**
21 Returns a selection containing the breadcrumbs for @p index
22 */
23 QItemSelection getBreadcrumbSelection(const QModelIndex &index);
24
25 /**
26 Returns a selection containing the breadcrumbs for @p selection
27 */
28 QItemSelection getBreadcrumbSelection(const QItemSelection &selection);
29
30 void sourceSelectionChanged(const QItemSelection &selected, const QItemSelection &deselected);
31
32 void syncBreadcrumbs();
33
34 bool m_includeActualSelection = true;
35 bool m_showHiddenAscendantData = false;
36 bool m_ignoreCurrentChanged = false;
37 int m_selectionDepth = -1;
38 KBreadcrumbSelectionModel::BreadcrumbTarget m_direction = KBreadcrumbSelectionModel::MakeBreadcrumbSelectionInSelf;
39 QItemSelectionModel *m_selectionModel = nullptr;
40};
41
42KBreadcrumbSelectionModelPrivate::KBreadcrumbSelectionModelPrivate(KBreadcrumbSelectionModel *breadcrumbSelector,
43 QItemSelectionModel *selectionModel,
44 KBreadcrumbSelectionModel::BreadcrumbTarget direction)
45 : q_ptr(breadcrumbSelector)
46 , m_direction(direction)
47 , m_selectionModel(selectionModel)
48{
49 Q_Q(KBreadcrumbSelectionModel);
50
51 if (direction != KBreadcrumbSelectionModel::MakeBreadcrumbSelectionInSelf) {
52 q->connect(sender: selectionModel, signal: &QItemSelectionModel::selectionChanged, context: q, slot: [this](const QItemSelection &selected, const QItemSelection &deselected) {
53 sourceSelectionChanged(selected, deselected);
54 });
55 }
56
57 q->connect(sender: m_selectionModel->model(), signal: &QAbstractItemModel::layoutChanged, context: q, slot: [this]() {
58 syncBreadcrumbs();
59 });
60 q->connect(sender: m_selectionModel->model(), signal: &QAbstractItemModel::modelReset, context: q, slot: [this]() {
61 syncBreadcrumbs();
62 });
63 q->connect(sender: m_selectionModel->model(), signal: &QAbstractItemModel::rowsMoved, context: q, slot: [this]() {
64 syncBreadcrumbs();
65 });
66
67 // Don't need to handle insert & remove because they can't change the breadcrumbs on their own.
68}
69
70KBreadcrumbSelectionModel::KBreadcrumbSelectionModel(QItemSelectionModel *selectionModel, QObject *parent)
71 : QItemSelectionModel(const_cast<QAbstractItemModel *>(selectionModel->model()), parent)
72 , d_ptr(new KBreadcrumbSelectionModelPrivate(this, selectionModel, MakeBreadcrumbSelectionInSelf))
73{
74}
75
76KBreadcrumbSelectionModel::KBreadcrumbSelectionModel(QItemSelectionModel *selectionModel, BreadcrumbTarget direction, QObject *parent)
77 : QItemSelectionModel(const_cast<QAbstractItemModel *>(selectionModel->model()), parent)
78 , d_ptr(new KBreadcrumbSelectionModelPrivate(this, selectionModel, direction))
79{
80}
81
82KBreadcrumbSelectionModel::~KBreadcrumbSelectionModel() = default;
83
84bool KBreadcrumbSelectionModel::isActualSelectionIncluded() const
85{
86 Q_D(const KBreadcrumbSelectionModel);
87 return d->m_includeActualSelection;
88}
89
90void KBreadcrumbSelectionModel::setActualSelectionIncluded(bool includeActualSelection)
91{
92 Q_D(KBreadcrumbSelectionModel);
93 d->m_includeActualSelection = includeActualSelection;
94}
95
96int KBreadcrumbSelectionModel::breadcrumbLength() const
97{
98 Q_D(const KBreadcrumbSelectionModel);
99 return d->m_selectionDepth;
100}
101
102void KBreadcrumbSelectionModel::setBreadcrumbLength(int breadcrumbLength)
103{
104 Q_D(KBreadcrumbSelectionModel);
105 d->m_selectionDepth = breadcrumbLength;
106}
107
108QItemSelection KBreadcrumbSelectionModelPrivate::getBreadcrumbSelection(const QModelIndex &index)
109{
110 QItemSelection breadcrumbSelection;
111
112 if (m_includeActualSelection) {
113 breadcrumbSelection.append(t: QItemSelectionRange(index));
114 }
115
116 QModelIndex parent = index.parent();
117 int sumBreadcrumbs = 0;
118 bool includeAll = m_selectionDepth < 0;
119 while (parent.isValid() && (includeAll || sumBreadcrumbs < m_selectionDepth)) {
120 breadcrumbSelection.append(t: QItemSelectionRange(parent));
121 parent = parent.parent();
122 }
123 return breadcrumbSelection;
124}
125
126QItemSelection KBreadcrumbSelectionModelPrivate::getBreadcrumbSelection(const QItemSelection &selection)
127{
128 QItemSelection breadcrumbSelection;
129
130 if (m_includeActualSelection) {
131 breadcrumbSelection = selection;
132 }
133
134 QItemSelection::const_iterator it = selection.constBegin();
135 const QItemSelection::const_iterator end = selection.constEnd();
136
137 for (; it != end; ++it) {
138 QModelIndex parent = it->parent();
139
140 if (breadcrumbSelection.contains(index: parent)) {
141 continue;
142 }
143
144 int sumBreadcrumbs = 0;
145 bool includeAll = m_selectionDepth < 0;
146
147 while (parent.isValid() && (includeAll || sumBreadcrumbs < m_selectionDepth)) {
148 breadcrumbSelection.append(t: QItemSelectionRange(parent));
149 parent = parent.parent();
150
151 if (breadcrumbSelection.contains(index: parent)) {
152 break;
153 }
154
155 ++sumBreadcrumbs;
156 }
157 }
158 return breadcrumbSelection;
159}
160
161void KBreadcrumbSelectionModelPrivate::sourceSelectionChanged(const QItemSelection &selected, const QItemSelection &deselected)
162{
163 Q_Q(KBreadcrumbSelectionModel);
164 const QItemSelection deselectedCrumbs = getBreadcrumbSelection(selection: deselected);
165 const QItemSelection selectedCrumbs = getBreadcrumbSelection(selection: selected);
166
167 QItemSelection removed = deselectedCrumbs;
168 for (const QItemSelectionRange &range : selectedCrumbs) {
169 removed.removeAll(t: range);
170 }
171
172 QItemSelection added = selectedCrumbs;
173 for (const QItemSelectionRange &range : deselectedCrumbs) {
174 added.removeAll(t: range);
175 }
176
177 if (!removed.isEmpty()) {
178 q->QItemSelectionModel::select(selection: removed, command: QItemSelectionModel::Deselect);
179 }
180 if (!added.isEmpty()) {
181 q->QItemSelectionModel::select(selection: added, command: QItemSelectionModel::Select);
182 }
183}
184
185void KBreadcrumbSelectionModel::select(const QModelIndex &index, QItemSelectionModel::SelectionFlags command)
186{
187 Q_D(KBreadcrumbSelectionModel);
188 // When an item is removed, the current index is set to the top index in the model.
189 // That causes a selectionChanged signal with a selection which we do not want.
190 if (d->m_ignoreCurrentChanged) {
191 d->m_ignoreCurrentChanged = false;
192 return;
193 }
194 if (d->m_direction == MakeBreadcrumbSelectionInOther) {
195 d->m_selectionModel->select(selection: d->getBreadcrumbSelection(index), command);
196 QItemSelectionModel::select(index, command);
197 } else {
198 d->m_selectionModel->select(index, command);
199 QItemSelectionModel::select(selection: d->getBreadcrumbSelection(index), command);
200 }
201}
202
203void KBreadcrumbSelectionModel::select(const QItemSelection &selection, QItemSelectionModel::SelectionFlags command)
204{
205 Q_D(KBreadcrumbSelectionModel);
206 QItemSelection bcc = d->getBreadcrumbSelection(selection);
207 if (d->m_direction == MakeBreadcrumbSelectionInOther) {
208 d->m_selectionModel->select(selection, command);
209 QItemSelectionModel::select(selection: bcc, command);
210 } else {
211 d->m_selectionModel->select(selection: bcc, command);
212 QItemSelectionModel::select(selection, command);
213 }
214}
215
216void KBreadcrumbSelectionModelPrivate::syncBreadcrumbs()
217{
218 Q_Q(KBreadcrumbSelectionModel);
219 q->select(selection: m_selectionModel->selection(), command: QItemSelectionModel::ClearAndSelect);
220}
221
222#include "moc_kbreadcrumbselectionmodel.cpp"
223

source code of kitemmodels/src/core/kbreadcrumbselectionmodel.cpp