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