]> cloud.milkyroute.net Git - dolphin.git/blob - src/kitemviews/kitemlistselectionmanager.cpp
GIT_SILENT Update Appstream for new release
[dolphin.git] / src / kitemviews / kitemlistselectionmanager.cpp
1 /*
2 * SPDX-FileCopyrightText: 2011 Peter Penz <peter.penz19@gmail.com>
3 * SPDX-FileCopyrightText: 2011 Frank Reininghaus <frank78ac@googlemail.com>
4 *
5 * Based on the Itemviews NG project from Trolltech Labs
6 *
7 * SPDX-License-Identifier: GPL-2.0-or-later
8 */
9
10 #include "kitemlistselectionmanager.h"
11
12 KItemListSelectionManager::KItemListSelectionManager(QObject *parent)
13 : QObject(parent)
14 , m_currentItem(-1)
15 , m_anchorItem(-1)
16 , m_selectedItems()
17 , m_isAnchoredSelectionActive(false)
18 , m_model(nullptr)
19 {
20 }
21
22 KItemListSelectionManager::~KItemListSelectionManager()
23 {
24 }
25
26 void KItemListSelectionManager::setCurrentItem(int current)
27 {
28 const int previous = m_currentItem;
29 const KItemSet previousSelection = selectedItems();
30
31 if (m_model && current >= 0 && current < m_model->count()) {
32 m_currentItem = current;
33 } else {
34 m_currentItem = -1;
35 }
36
37 if (m_currentItem != previous) {
38 Q_EMIT currentChanged(m_currentItem, previous);
39
40 if (m_isAnchoredSelectionActive) {
41 const KItemSet selection = selectedItems();
42 if (selection != previousSelection) {
43 Q_EMIT selectionChanged(selection, previousSelection);
44 }
45 }
46 }
47 }
48
49 int KItemListSelectionManager::currentItem() const
50 {
51 return m_currentItem;
52 }
53
54 void KItemListSelectionManager::setSelectedItems(const KItemSet &items)
55 {
56 if (m_selectedItems != items) {
57 const KItemSet previous = m_selectedItems;
58 m_selectedItems = items;
59 Q_EMIT selectionChanged(m_selectedItems, previous);
60 }
61 }
62
63 KItemSet KItemListSelectionManager::selectedItems() const
64 {
65 KItemSet selectedItems = m_selectedItems;
66
67 if (m_isAnchoredSelectionActive && m_anchorItem != m_currentItem) {
68 Q_ASSERT(m_anchorItem >= 0);
69 Q_ASSERT(m_currentItem >= 0);
70 const int from = qMin(m_anchorItem, m_currentItem);
71 const int to = qMax(m_anchorItem, m_currentItem);
72
73 for (int index = from; index <= to; ++index) {
74 selectedItems.insert(index);
75 }
76 }
77
78 return selectedItems;
79 }
80
81 bool KItemListSelectionManager::isSelected(int index) const
82 {
83 if (m_selectedItems.contains(index)) {
84 return true;
85 }
86
87 if (m_isAnchoredSelectionActive && m_anchorItem != m_currentItem) {
88 Q_ASSERT(m_anchorItem >= 0);
89 Q_ASSERT(m_currentItem >= 0);
90 const int from = qMin(m_anchorItem, m_currentItem);
91 const int to = qMax(m_anchorItem, m_currentItem);
92
93 if (from <= index && index <= to) {
94 return true;
95 }
96 }
97
98 return false;
99 }
100
101 bool KItemListSelectionManager::hasSelection() const
102 {
103 return !m_selectedItems.isEmpty() || (m_isAnchoredSelectionActive && m_anchorItem != m_currentItem);
104 }
105
106 void KItemListSelectionManager::setSelected(int index, int count, SelectionMode mode)
107 {
108 if (index < 0 || count < 1 || !m_model || index >= m_model->count()) {
109 return;
110 }
111
112 endAnchoredSelection();
113 const KItemSet previous = selectedItems();
114
115 count = qMin(count, m_model->count() - index);
116
117 const int endIndex = index + count - 1;
118 switch (mode) {
119 case Select:
120 for (int i = index; i <= endIndex; ++i) {
121 m_selectedItems.insert(i);
122 }
123 break;
124
125 case Deselect:
126 for (int i = index; i <= endIndex; ++i) {
127 m_selectedItems.remove(i);
128 }
129 break;
130
131 case Toggle:
132 for (int i = index; i <= endIndex; ++i) {
133 if (m_selectedItems.contains(i)) {
134 m_selectedItems.remove(i);
135 } else {
136 m_selectedItems.insert(i);
137 }
138 }
139 break;
140
141 default:
142 Q_ASSERT(false);
143 break;
144 }
145
146 const KItemSet selection = selectedItems();
147 if (selection != previous) {
148 Q_EMIT selectionChanged(selection, previous);
149 }
150 }
151
152 void KItemListSelectionManager::clearSelection()
153 {
154 const KItemSet previous = selectedItems();
155 if (!previous.isEmpty()) {
156 m_selectedItems.clear();
157 m_isAnchoredSelectionActive = false;
158 Q_EMIT selectionChanged(KItemSet(), previous);
159 }
160 }
161
162 void KItemListSelectionManager::replaceSelection(int index, int count)
163 {
164 const KItemSet previous = selectedItems();
165 if (!previous.isEmpty()) {
166 m_selectedItems.clear();
167 m_isAnchoredSelectionActive = false;
168 }
169 setSelected(index, count);
170 }
171
172 void KItemListSelectionManager::beginAnchoredSelection(int anchor)
173 {
174 if (anchor >= 0 && m_model && anchor < m_model->count()) {
175 m_isAnchoredSelectionActive = true;
176 m_anchorItem = anchor;
177 }
178 }
179
180 void KItemListSelectionManager::endAnchoredSelection()
181 {
182 if (m_isAnchoredSelectionActive && (m_anchorItem != m_currentItem)) {
183 Q_ASSERT(m_anchorItem >= 0);
184 Q_ASSERT(m_currentItem >= 0);
185 const int from = qMin(m_anchorItem, m_currentItem);
186 const int to = qMax(m_anchorItem, m_currentItem);
187
188 for (int index = from; index <= to; ++index) {
189 m_selectedItems.insert(index);
190 }
191 }
192
193 m_isAnchoredSelectionActive = false;
194 }
195
196 bool KItemListSelectionManager::isAnchoredSelectionActive() const
197 {
198 return m_isAnchoredSelectionActive;
199 }
200
201 KItemModelBase *KItemListSelectionManager::model() const
202 {
203 return m_model;
204 }
205
206 void KItemListSelectionManager::setModel(KItemModelBase *model)
207 {
208 m_model = model;
209 if (model && model->count() > 0) {
210 m_currentItem = 0;
211 }
212 }
213
214 void KItemListSelectionManager::itemsInserted(const KItemRangeList &itemRanges)
215 {
216 // Store the current selection (needed in the selectionChanged() signal)
217 const KItemSet previousSelection = selectedItems();
218
219 // Update the current item
220 if (m_currentItem < 0) {
221 setCurrentItem(0);
222 } else {
223 const int previousCurrent = m_currentItem;
224 int inc = 0;
225 for (const KItemRange &itemRange : itemRanges) {
226 if (m_currentItem < itemRange.index) {
227 break;
228 }
229 inc += itemRange.count;
230 }
231 // Calling setCurrentItem would trigger the selectionChanged signal, but we want to
232 // emit it only once in this function -> change the current item manually and emit currentChanged
233 m_currentItem += inc;
234 if (m_currentItem >= m_model->count()) {
235 m_currentItem = -1;
236 }
237 Q_EMIT currentChanged(m_currentItem, previousCurrent);
238 }
239
240 // Update the anchor item
241 if (m_anchorItem < 0) {
242 m_anchorItem = 0;
243 } else {
244 int inc = 0;
245 for (const KItemRange &itemRange : itemRanges) {
246 if (m_anchorItem < itemRange.index) {
247 break;
248 }
249 inc += itemRange.count;
250 }
251 m_anchorItem += inc;
252 }
253
254 // Update the selections
255 if (!m_selectedItems.isEmpty()) {
256 const KItemSet previous = m_selectedItems;
257 m_selectedItems.clear();
258
259 for (int index : previous) {
260 int inc = 0;
261 for (const KItemRange &itemRange : itemRanges) {
262 if (index < itemRange.index) {
263 break;
264 }
265 inc += itemRange.count;
266 }
267 m_selectedItems.insert(index + inc);
268 }
269 }
270
271 const KItemSet selection = selectedItems();
272 if (selection != previousSelection) {
273 Q_EMIT selectionChanged(selection, previousSelection);
274 }
275 }
276
277 void KItemListSelectionManager::itemsRemoved(const KItemRangeList &itemRanges)
278 {
279 // Store the current selection (needed in the selectionChanged() signal)
280 const KItemSet previousSelection = selectedItems();
281 const int previousCurrent = m_currentItem;
282
283 // Update the current item
284 m_currentItem = indexAfterRangesRemoving(m_currentItem, itemRanges, DiscardRemovedIndex);
285 if (m_currentItem != previousCurrent) {
286 Q_EMIT currentChanged(m_currentItem, previousCurrent);
287 if (m_currentItem < 0) {
288 // Calling setCurrentItem() would trigger the selectionChanged signal, but we want to
289 // emit it only once in this function -> change the current item manually and emit currentChanged
290 m_currentItem = indexAfterRangesRemoving(previousCurrent, itemRanges, AdjustRemovedIndex);
291 Q_EMIT currentChanged(m_currentItem, -1);
292 }
293 }
294
295 // Update the anchor item
296 if (m_anchorItem >= 0) {
297 m_anchorItem = indexAfterRangesRemoving(m_anchorItem, itemRanges, DiscardRemovedIndex);
298 if (m_anchorItem < 0) {
299 m_isAnchoredSelectionActive = false;
300 }
301 }
302
303 // Update the selections and the anchor item
304 if (!m_selectedItems.isEmpty()) {
305 const KItemSet previous = m_selectedItems;
306 m_selectedItems.clear();
307
308 for (int oldIndex : previous) {
309 const int index = indexAfterRangesRemoving(oldIndex, itemRanges, DiscardRemovedIndex);
310 if (index >= 0) {
311 m_selectedItems.insert(index);
312 }
313 }
314 }
315
316 const KItemSet selection = selectedItems();
317 if (selection != previousSelection) {
318 Q_EMIT selectionChanged(selection, previousSelection);
319 }
320
321 Q_ASSERT(m_currentItem < m_model->count());
322 Q_ASSERT(m_anchorItem < m_model->count());
323 }
324
325 void KItemListSelectionManager::itemsMoved(const KItemRange &itemRange, const QList<int> &movedToIndexes)
326 {
327 // Store the current selection (needed in the selectionChanged() signal)
328 const KItemSet previousSelection = selectedItems();
329
330 // Store whether we were doing an anchored selection
331 const bool wasInAnchoredSelection = isAnchoredSelectionActive();
332
333 // endAnchoredSelection() adds all items between m_currentItem and
334 // m_anchorItem to m_selectedItems. They can then be moved
335 // individually later in this function.
336 endAnchoredSelection();
337
338 // Update the current item
339 if (m_currentItem >= itemRange.index && m_currentItem < itemRange.index + itemRange.count) {
340 const int previousCurrentItem = m_currentItem;
341 const int newCurrentItem = movedToIndexes.at(previousCurrentItem - itemRange.index);
342
343 // Calling setCurrentItem would trigger the selectionChanged signal, but we want to
344 // emit it only once in this function -> change the current item manually and emit currentChanged
345 m_currentItem = newCurrentItem;
346 Q_EMIT currentChanged(newCurrentItem, previousCurrentItem);
347 }
348
349 // Start a new anchored selection.
350 if (wasInAnchoredSelection) {
351 beginAnchoredSelection(m_currentItem);
352 }
353
354 // Update the selections
355 if (!m_selectedItems.isEmpty()) {
356 const KItemSet previous = m_selectedItems;
357 m_selectedItems.clear();
358
359 for (int index : previous) {
360 if (index >= itemRange.index && index < itemRange.index + itemRange.count) {
361 m_selectedItems.insert(movedToIndexes.at(index - itemRange.index));
362 } else {
363 m_selectedItems.insert(index);
364 }
365 }
366 }
367
368 const KItemSet selection = selectedItems();
369 if (selection != previousSelection) {
370 Q_EMIT selectionChanged(selection, previousSelection);
371 }
372 }
373
374 int KItemListSelectionManager::indexAfterRangesRemoving(int index, const KItemRangeList &itemRanges, const RangesRemovingBehaviour behaviour) const
375 {
376 int dec = 0;
377 for (const KItemRange &itemRange : itemRanges) {
378 if (index < itemRange.index) {
379 break;
380 }
381
382 dec += itemRange.count;
383
384 const int firstIndexAfterRange = itemRange.index + itemRange.count;
385 if (index < firstIndexAfterRange) {
386 // The index is part of the removed range
387 if (behaviour == DiscardRemovedIndex) {
388 return -1;
389 } else {
390 // Use the first item after the range as new index
391 index = firstIndexAfterRange;
392 break;
393 }
394 }
395 }
396 return qBound(-1, index - dec, m_model->count() - 1);
397 }
398
399 #include "moc_kitemlistselectionmanager.cpp"