1/****************************************************************************
2**
3** Copyright (C) 2016 The Qt Company Ltd.
4** Contact: https://www.qt.io/licensing/
5**
6** This file is part of the QtCore module of the Qt Toolkit.
7**
8** $QT_BEGIN_LICENSE:LGPL$
9** Commercial License Usage
10** Licensees holding valid commercial Qt licenses may use this file in
11** accordance with the commercial license agreement provided with the
12** Software or, alternatively, in accordance with the terms contained in
13** a written agreement between you and The Qt Company. For licensing terms
14** and conditions see https://www.qt.io/terms-conditions. For further
15** information use the contact form at https://www.qt.io/contact-us.
16**
17** GNU Lesser General Public License Usage
18** Alternatively, this file may be used under the terms of the GNU Lesser
19** General Public License version 3 as published by the Free Software
20** Foundation and appearing in the file LICENSE.LGPL3 included in the
21** packaging of this file. Please review the following information to
22** ensure the GNU Lesser General Public License version 3 requirements
23** will be met: https://www.gnu.org/licenses/lgpl-3.0.html.
24**
25** GNU General Public License Usage
26** Alternatively, this file may be used under the terms of the GNU
27** General Public License version 2.0 or (at your option) the GNU General
28** Public license version 3 or any later version approved by the KDE Free
29** Qt Foundation. The licenses are as published by the Free Software
30** Foundation and appearing in the file LICENSE.GPL2 and LICENSE.GPL3
31** included in the packaging of this file. Please review the following
32** information to ensure the GNU General Public License requirements will
33** be met: https://www.gnu.org/licenses/gpl-2.0.html and
34** https://www.gnu.org/licenses/gpl-3.0.html.
35**
36** $QT_END_LICENSE$
37**
38****************************************************************************/
39
40#include "qitemselectionmodel.h"
41#include <private/qitemselectionmodel_p.h>
42#include <private/qduplicatetracker_p.h>
43#include <qdebug.h>
44
45#include <algorithm>
46#include <functional>
47
48QT_BEGIN_NAMESPACE
49
50/*!
51 \class QItemSelectionRange
52 \inmodule QtCore
53
54 \brief The QItemSelectionRange class manages information about a
55 range of selected items in a model.
56
57 \ingroup model-view
58
59 A QItemSelectionRange contains information about a range of
60 selected items in a model. A range of items is a contiguous array
61 of model items, extending to cover a number of adjacent rows and
62 columns with a common parent item; this can be visualized as a
63 two-dimensional block of cells in a table. A selection range has a
64 top(), left() a bottom(), right() and a parent().
65
66 The QItemSelectionRange class is one of the \l{Model/View Classes}
67 and is part of Qt's \l{Model/View Programming}{model/view framework}.
68
69 The model items contained in the selection range can be obtained
70 using the indexes() function. Use QItemSelectionModel::selectedIndexes()
71 to get a list of all selected items for a view.
72
73 You can determine whether a given model item lies within a
74 particular range by using the contains() function. Ranges can also
75 be compared using the overloaded operators for equality and
76 inequality, and the intersects() function allows you to determine
77 whether two ranges overlap.
78
79 \sa {Model/View Programming}, QAbstractItemModel, QItemSelection,
80 QItemSelectionModel
81*/
82
83/*!
84 \fn QItemSelectionRange::QItemSelectionRange()
85
86 Constructs an empty selection range.
87*/
88
89/*!
90 \fn QItemSelectionRange::QItemSelectionRange(const QModelIndex &topLeft, const QModelIndex &bottomRight)
91
92 Constructs a new selection range containing only the index specified
93 by the \a topLeft and the index \a bottomRight.
94
95*/
96
97/*!
98 \fn QItemSelectionRange::QItemSelectionRange(const QModelIndex &index)
99
100 Constructs a new selection range containing only the model item specified
101 by the model index \a index.
102*/
103
104/*!
105 \fn QItemSelectionRange::swap(QItemSelectionRange &other)
106 \since 5.6
107
108 Swaps this selection range's contents with \a other.
109 This function is very fast and never fails.
110*/
111
112/*!
113 \fn int QItemSelectionRange::top() const
114
115 Returns the row index corresponding to the uppermost selected row in the
116 selection range.
117
118*/
119
120/*!
121 \fn int QItemSelectionRange::left() const
122
123 Returns the column index corresponding to the leftmost selected column in the
124 selection range.
125*/
126
127/*!
128 \fn int QItemSelectionRange::bottom() const
129
130 Returns the row index corresponding to the lowermost selected row in the
131 selection range.
132
133*/
134
135/*!
136 \fn int QItemSelectionRange::right() const
137
138 Returns the column index corresponding to the rightmost selected column in
139 the selection range.
140
141*/
142
143/*!
144 \fn int QItemSelectionRange::width() const
145
146 Returns the number of selected columns in the selection range.
147
148*/
149
150/*!
151 \fn int QItemSelectionRange::height() const
152
153 Returns the number of selected rows in the selection range.
154
155*/
156
157/*!
158 \fn const QAbstractItemModel *QItemSelectionRange::model() const
159
160 Returns the model that the items in the selection range belong to.
161*/
162
163/*!
164 \fn QModelIndex QItemSelectionRange::topLeft() const
165
166 Returns the index for the item located at the top-left corner of
167 the selection range.
168
169 \sa top(), left(), bottomRight()
170*/
171
172/*!
173 \fn QModelIndex QItemSelectionRange::bottomRight() const
174
175 Returns the index for the item located at the bottom-right corner
176 of the selection range.
177
178 \sa bottom(), right(), topLeft()
179*/
180
181/*!
182 \fn QModelIndex QItemSelectionRange::parent() const
183
184 Returns the parent model item index of the items in the selection range.
185
186*/
187
188/*!
189 \fn bool QItemSelectionRange::contains(const QModelIndex &index) const
190
191 Returns \c true if the model item specified by the \a index lies within the
192 range of selected items; otherwise returns \c false.
193*/
194
195/*!
196 \fn bool QItemSelectionRange::contains(int row, int column,
197 const QModelIndex &parentIndex) const
198 \overload
199
200 Returns \c true if the model item specified by (\a row, \a column)
201 and with \a parentIndex as the parent item lies within the range
202 of selected items; otherwise returns \c false.
203*/
204
205/*!
206 \fn bool QItemSelectionRange::intersects(const QItemSelectionRange &other) const
207
208 Returns \c true if this selection range intersects (overlaps with) the \a other
209 range given; otherwise returns \c false.
210
211*/
212bool QItemSelectionRange::intersects(const QItemSelectionRange &other) const
213{
214 // isValid() and parent() last since they are more expensive
215 return (model() == other.model()
216 && ((top() <= other.top() && bottom() >= other.top())
217 || (top() >= other.top() && top() <= other.bottom()))
218 && ((left() <= other.left() && right() >= other.left())
219 || (left() >= other.left() && left() <= other.right()))
220 && parent() == other.parent()
221 && isValid() && other.isValid()
222 );
223}
224
225/*!
226 \fn QItemSelectionRange QItemSelectionRange::intersected(const QItemSelectionRange &other) const
227 \since 4.2
228
229 Returns a new selection range containing only the items that are found in
230 both the selection range and the \a other selection range.
231*/
232
233QItemSelectionRange QItemSelectionRange::intersected(const QItemSelectionRange &other) const
234{
235 if (model() == other.model() && parent() == other.parent()) {
236 QModelIndex topLeft = model()->index(qMax(top(), other.top()),
237 qMax(left(), other.left()),
238 other.parent());
239 QModelIndex bottomRight = model()->index(qMin(bottom(), other.bottom()),
240 qMin(right(), other.right()),
241 other.parent());
242 return QItemSelectionRange(topLeft, bottomRight);
243 }
244 return QItemSelectionRange();
245}
246
247/*!
248 \fn bool QItemSelectionRange::operator==(const QItemSelectionRange &other) const
249
250 Returns \c true if the selection range is exactly the same as the \a other
251 range given; otherwise returns \c false.
252
253*/
254
255/*!
256 \fn bool QItemSelectionRange::operator!=(const QItemSelectionRange &other) const
257
258 Returns \c true if the selection range differs from the \a other range given;
259 otherwise returns \c false.
260
261*/
262
263/*!
264 Returns \c true if the selection range is less than the \a other
265 range given; otherwise returns \c false.
266
267 The less than calculation is not directly useful to developers - the way that ranges
268 with different parents compare is not defined. This operator only exists so that the
269 class can be used with QMap.
270
271*/
272bool QItemSelectionRange::operator<(const QItemSelectionRange &other) const
273{
274 // ### Qt 6: This is inconsistent with op== and needs to be fixed, nay,
275 // ### removed, but cannot, because it was inline up to and including 5.9
276
277 // Comparing parents will compare the models, but if two equivalent ranges
278 // in two different models have invalid parents, they would appear the same
279 if (other.tl.model() == tl.model()) {
280 // parent has to be calculated, so we only do so once.
281 const QModelIndex topLeftParent = tl.parent();
282 const QModelIndex otherTopLeftParent = other.tl.parent();
283 if (topLeftParent == otherTopLeftParent) {
284 if (other.tl.row() == tl.row()) {
285 if (other.tl.column() == tl.column()) {
286 if (other.br.row() == br.row()) {
287 return br.column() < other.br.column();
288 }
289 return br.row() < other.br.row();
290 }
291 return tl.column() < other.tl.column();
292 }
293 return tl.row() < other.tl.row();
294 }
295 return topLeftParent < otherTopLeftParent;
296 }
297
298 std::less<const QAbstractItemModel *> less;
299 return less(tl.model(), other.tl.model());
300}
301
302/*!
303 \fn bool QItemSelectionRange::isValid() const
304
305 Returns \c true if the selection range is valid; otherwise returns \c false.
306
307*/
308
309static void rowLengthsFromRange(const QItemSelectionRange &range, QList<QPair<QPersistentModelIndex, uint>> &result)
310{
311 if (range.isValid() && range.model()) {
312 const QModelIndex topLeft = range.topLeft();
313 const int bottom = range.bottom();
314 const uint width = range.width();
315 const int column = topLeft.column();
316 for (int row = topLeft.row(); row <= bottom; ++row) {
317 // We don't need to keep track of ItemIsSelectable and ItemIsEnabled here. That is
318 // required in indexesFromRange() because that method is called from public API
319 // which requires the limitation.
320 result.push_back(qMakePair(QPersistentModelIndex(topLeft.sibling(row, column)), width));
321 }
322 }
323}
324
325template<typename ModelIndexContainer>
326static void indexesFromRange(const QItemSelectionRange &range, ModelIndexContainer &result)
327{
328 if (range.isValid() && range.model()) {
329 const QModelIndex topLeft = range.topLeft();
330 const int bottom = range.bottom();
331 const int right = range.right();
332 for (int row = topLeft.row(); row <= bottom; ++row) {
333 const QModelIndex columnLeader = topLeft.sibling(row, topLeft.column());
334 for (int column = topLeft.column(); column <= right; ++column) {
335 QModelIndex index = columnLeader.sibling(row, column);
336 Qt::ItemFlags flags = range.model()->flags(index);
337 if ((flags & Qt::ItemIsSelectable) && (flags & Qt::ItemIsEnabled))
338 result.push_back(index);
339 }
340 }
341 }
342}
343
344template<typename ModelIndexContainer>
345static ModelIndexContainer qSelectionIndexes(const QItemSelection &selection)
346{
347 ModelIndexContainer result;
348 for (const auto &range : selection)
349 indexesFromRange(range, result);
350 return result;
351}
352
353/*!
354 Returns \c true if the selection range contains no selectable item
355 \since 4.7
356*/
357
358bool QItemSelectionRange::isEmpty() const
359{
360 if (!isValid() || !model())
361 return true;
362
363 for (int column = left(); column <= right(); ++column) {
364 for (int row = top(); row <= bottom(); ++row) {
365 QModelIndex index = model()->index(row, column, parent());
366 Qt::ItemFlags flags = model()->flags(index);
367 if ((flags & Qt::ItemIsSelectable) && (flags & Qt::ItemIsEnabled))
368 return false;
369 }
370 }
371 return true;
372}
373
374/*!
375 Returns the list of model index items stored in the selection.
376*/
377
378QModelIndexList QItemSelectionRange::indexes() const
379{
380 QModelIndexList result;
381 indexesFromRange(*this, result);
382 return result;
383}
384
385/*!
386 \class QItemSelection
387 \inmodule QtCore
388
389 \brief The QItemSelection class manages information about selected items in a model.
390
391 \ingroup model-view
392
393 A QItemSelection describes the items in a model that have been
394 selected by the user. A QItemSelection is basically a list of
395 selection ranges, see QItemSelectionRange. It provides functions for
396 creating and manipulating selections, and selecting a range of items
397 from a model.
398
399 The QItemSelection class is one of the \l{Model/View Classes}
400 and is part of Qt's \l{Model/View Programming}{model/view framework}.
401
402 An item selection can be constructed and initialized to contain a
403 range of items from an existing model. The following example constructs
404 a selection that contains a range of items from the given \c model,
405 beginning at the \c topLeft, and ending at the \c bottomRight.
406
407 \snippet code/src_gui_itemviews_qitemselectionmodel.cpp 0
408
409 An empty item selection can be constructed, and later populated as
410 required. So, if the model is going to be unavailable when we construct
411 the item selection, we can rewrite the above code in the following way:
412
413 \snippet code/src_gui_itemviews_qitemselectionmodel.cpp 1
414
415 QItemSelection saves memory, and avoids unnecessary work, by working with
416 selection ranges rather than recording the model item index for each
417 item in the selection. Generally, an instance of this class will contain
418 a list of non-overlapping selection ranges.
419
420 Use merge() to merge one item selection into another without making
421 overlapping ranges. Use split() to split one selection range into
422 smaller ranges based on a another selection range.
423
424 \sa {Model/View Programming}, QItemSelectionModel
425*/
426
427/*!
428 \fn QItemSelection::QItemSelection()
429
430 Constructs an empty selection.
431*/
432
433/*!
434 Constructs an item selection that extends from the top-left model item,
435 specified by the \a topLeft index, to the bottom-right item, specified
436 by \a bottomRight.
437*/
438QItemSelection::QItemSelection(const QModelIndex &topLeft, const QModelIndex &bottomRight)
439{
440 select(topLeft, bottomRight);
441}
442
443/*!
444 Adds the items in the range that extends from the top-left model
445 item, specified by the \a topLeft index, to the bottom-right item,
446 specified by \a bottomRight to the list.
447
448 \note \a topLeft and \a bottomRight must have the same parent.
449*/
450void QItemSelection::select(const QModelIndex &topLeft, const QModelIndex &bottomRight)
451{
452 if (!topLeft.isValid() || !bottomRight.isValid())
453 return;
454
455 if ((topLeft.model() != bottomRight.model())
456 || topLeft.parent() != bottomRight.parent()) {
457 qWarning("Can't select indexes from different model or with different parents");
458 return;
459 }
460 if (topLeft.row() > bottomRight.row() || topLeft.column() > bottomRight.column()) {
461 int top = qMin(topLeft.row(), bottomRight.row());
462 int bottom = qMax(topLeft.row(), bottomRight.row());
463 int left = qMin(topLeft.column(), bottomRight.column());
464 int right = qMax(topLeft.column(), bottomRight.column());
465 QModelIndex tl = topLeft.sibling(top, left);
466 QModelIndex br = bottomRight.sibling(bottom, right);
467 append(QItemSelectionRange(tl, br));
468 return;
469 }
470 append(QItemSelectionRange(topLeft, bottomRight));
471}
472
473/*!
474 Returns \c true if the selection contains the given \a index; otherwise
475 returns \c false.
476*/
477
478bool QItemSelection::contains(const QModelIndex &index) const
479{
480 if (index.flags() & Qt::ItemIsSelectable) {
481 QList<QItemSelectionRange>::const_iterator it = begin();
482 for (; it != end(); ++it)
483 if ((*it).contains(index))
484 return true;
485 }
486 return false;
487}
488
489/*!
490 Returns a list of model indexes that correspond to the selected items.
491*/
492
493QModelIndexList QItemSelection::indexes() const
494{
495 return qSelectionIndexes<QModelIndexList>(*this);
496}
497
498static QList<QPair<QPersistentModelIndex, uint>> qSelectionPersistentRowLengths(const QItemSelection &sel)
499{
500 QList<QPair<QPersistentModelIndex, uint>> result;
501 for (const QItemSelectionRange &range : sel)
502 rowLengthsFromRange(range, result);
503 return result;
504}
505
506/*!
507 Merges the \a other selection with this QItemSelection using the
508 \a command given. This method guarantees that no ranges are overlapping.
509
510 Note that only QItemSelectionModel::Select,
511 QItemSelectionModel::Deselect, and QItemSelectionModel::Toggle are
512 supported.
513
514 \sa split()
515*/
516void QItemSelection::merge(const QItemSelection &other, QItemSelectionModel::SelectionFlags command)
517{
518 if (other.isEmpty() ||
519 !(command & QItemSelectionModel::Select ||
520 command & QItemSelectionModel::Deselect ||
521 command & QItemSelectionModel::Toggle))
522 return;
523
524 QItemSelection newSelection = other;
525 // Collect intersections
526 QItemSelection intersections;
527 QItemSelection::iterator it = newSelection.begin();
528 while (it != newSelection.end()) {
529 if (!(*it).isValid()) {
530 it = newSelection.erase(it);
531 continue;
532 }
533 for (int t = 0; t < count(); ++t) {
534 if ((*it).intersects(at(t)))
535 intersections.append(at(t).intersected(*it));
536 }
537 ++it;
538 }
539
540 // Split the old (and new) ranges using the intersections
541 for (int i = 0; i < intersections.count(); ++i) { // for each intersection
542 for (int t = 0; t < count();) { // splitt each old range
543 if (at(t).intersects(intersections.at(i))) {
544 split(at(t), intersections.at(i), this);
545 removeAt(t);
546 } else {
547 ++t;
548 }
549 }
550 // only split newSelection if Toggle is specified
551 for (int n = 0; (command & QItemSelectionModel::Toggle) && n < newSelection.count();) {
552 if (newSelection.at(n).intersects(intersections.at(i))) {
553 split(newSelection.at(n), intersections.at(i), &newSelection);
554 newSelection.removeAt(n);
555 } else {
556 ++n;
557 }
558 }
559 }
560 // do not add newSelection for Deselect
561 if (!(command & QItemSelectionModel::Deselect))
562 operator+=(newSelection);
563}
564
565/*!
566 Splits the selection \a range using the selection \a other range.
567 Removes all items in \a other from \a range and puts the result in \a result.
568 This can be compared with the semantics of the \e subtract operation of a set.
569 \sa merge()
570*/
571
572void QItemSelection::split(const QItemSelectionRange &range,
573 const QItemSelectionRange &other, QItemSelection *result)
574{
575 if (range.parent() != other.parent() || range.model() != other.model())
576 return;
577
578 QModelIndex parent = other.parent();
579 int top = range.top();
580 int left = range.left();
581 int bottom = range.bottom();
582 int right = range.right();
583 int other_top = other.top();
584 int other_left = other.left();
585 int other_bottom = other.bottom();
586 int other_right = other.right();
587 const QAbstractItemModel *model = range.model();
588 Q_ASSERT(model);
589 if (other_top > top) {
590 QModelIndex tl = model->index(top, left, parent);
591 QModelIndex br = model->index(other_top - 1, right, parent);
592 result->append(QItemSelectionRange(tl, br));
593 top = other_top;
594 }
595 if (other_bottom < bottom) {
596 QModelIndex tl = model->index(other_bottom + 1, left, parent);
597 QModelIndex br = model->index(bottom, right, parent);
598 result->append(QItemSelectionRange(tl, br));
599 bottom = other_bottom;
600 }
601 if (other_left > left) {
602 QModelIndex tl = model->index(top, left, parent);
603 QModelIndex br = model->index(bottom, other_left - 1, parent);
604 result->append(QItemSelectionRange(tl, br));
605 left = other_left;
606 }
607 if (other_right < right) {
608 QModelIndex tl = model->index(top, other_right + 1, parent);
609 QModelIndex br = model->index(bottom, right, parent);
610 result->append(QItemSelectionRange(tl, br));
611 right = other_right;
612 }
613}
614
615
616void QItemSelectionModelPrivate::initModel(QAbstractItemModel *m)
617{
618 struct Cx {
619 const char *signal;
620 const char *slot;
621 };
622 static const Cx connections[] = {
623 { SIGNAL(rowsAboutToBeRemoved(QModelIndex,int,int)),
624 SLOT(_q_rowsAboutToBeRemoved(QModelIndex,int,int)) },
625 { SIGNAL(columnsAboutToBeRemoved(QModelIndex,int,int)),
626 SLOT(_q_columnsAboutToBeRemoved(QModelIndex,int,int)) },
627 { SIGNAL(rowsAboutToBeInserted(QModelIndex,int,int)),
628 SLOT(_q_rowsAboutToBeInserted(QModelIndex,int,int)) },
629 { SIGNAL(columnsAboutToBeInserted(QModelIndex,int,int)),
630 SLOT(_q_columnsAboutToBeInserted(QModelIndex,int,int)) },
631 { SIGNAL(rowsAboutToBeMoved(QModelIndex,int,int,QModelIndex,int)),
632 SLOT(_q_layoutAboutToBeChanged()) },
633 { SIGNAL(columnsAboutToBeMoved(QModelIndex,int,int,QModelIndex,int)),
634 SLOT(_q_layoutAboutToBeChanged()) },
635 { SIGNAL(rowsMoved(QModelIndex,int,int,QModelIndex,int)),
636 SLOT(_q_layoutChanged()) },
637 { SIGNAL(columnsMoved(QModelIndex,int,int,QModelIndex,int)),
638 SLOT(_q_layoutChanged()) },
639 { SIGNAL(layoutAboutToBeChanged(QList<QPersistentModelIndex>,QAbstractItemModel::LayoutChangeHint)),
640 SLOT(_q_layoutAboutToBeChanged(QList<QPersistentModelIndex>,QAbstractItemModel::LayoutChangeHint)) },
641 { SIGNAL(layoutChanged(QList<QPersistentModelIndex>,QAbstractItemModel::LayoutChangeHint)),
642 SLOT(_q_layoutChanged(QList<QPersistentModelIndex>,QAbstractItemModel::LayoutChangeHint)) },
643 { SIGNAL(modelReset()),
644 SLOT(reset()) },
645 { nullptr, nullptr }
646 };
647
648 if (model == m)
649 return;
650
651 Q_Q(QItemSelectionModel);
652 if (model) {
653 for (const Cx *cx = &connections[0]; cx->signal; cx++)
654 QObject::disconnect(model, cx->signal, q, cx->slot);
655 q->reset();
656 }
657 model = m;
658 if (model) {
659 for (const Cx *cx = &connections[0]; cx->signal; cx++)
660 QObject::connect(model, cx->signal, q, cx->slot);
661 }
662}
663
664/*!
665 \internal
666
667 returns a QItemSelection where all ranges have been expanded to:
668 Rows: left: 0 and right: columnCount()-1
669 Columns: top: 0 and bottom: rowCount()-1
670*/
671
672QItemSelection QItemSelectionModelPrivate::expandSelection(const QItemSelection &selection,
673 QItemSelectionModel::SelectionFlags command) const
674{
675 if (selection.isEmpty() && !((command & QItemSelectionModel::Rows) ||
676 (command & QItemSelectionModel::Columns)))
677 return selection;
678
679 QItemSelection expanded;
680 if (command & QItemSelectionModel::Rows) {
681 for (int i = 0; i < selection.count(); ++i) {
682 QModelIndex parent = selection.at(i).parent();
683 int colCount = model->columnCount(parent);
684 QModelIndex tl = model->index(selection.at(i).top(), 0, parent);
685 QModelIndex br = model->index(selection.at(i).bottom(), colCount - 1, parent);
686 //we need to merge because the same row could have already been inserted
687 expanded.merge(QItemSelection(tl, br), QItemSelectionModel::Select);
688 }
689 }
690 if (command & QItemSelectionModel::Columns) {
691 for (int i = 0; i < selection.count(); ++i) {
692 QModelIndex parent = selection.at(i).parent();
693 int rowCount = model->rowCount(parent);
694 QModelIndex tl = model->index(0, selection.at(i).left(), parent);
695 QModelIndex br = model->index(rowCount - 1, selection.at(i).right(), parent);
696 //we need to merge because the same column could have already been inserted
697 expanded.merge(QItemSelection(tl, br), QItemSelectionModel::Select);
698 }
699 }
700 return expanded;
701}
702
703/*!
704 \internal
705*/
706void QItemSelectionModelPrivate::_q_rowsAboutToBeRemoved(const QModelIndex &parent,
707 int start, int end)
708{
709 Q_Q(QItemSelectionModel);
710 finalize();
711
712 // update current index
713 if (currentIndex.isValid() && parent == currentIndex.parent()
714 && currentIndex.row() >= start && currentIndex.row() <= end) {
715 QModelIndex old = currentIndex;
716 if (start > 0) // there are rows left above the change
717 currentIndex = model->index(start - 1, old.column(), parent);
718 else if (model && end < model->rowCount(parent) - 1) // there are rows left below the change
719 currentIndex = model->index(end + 1, old.column(), parent);
720 else // there are no rows left in the table
721 currentIndex = QModelIndex();
722 emit q->currentChanged(currentIndex, old);
723 emit q->currentRowChanged(currentIndex, old);
724 if (currentIndex.column() != old.column())
725 emit q->currentColumnChanged(currentIndex, old);
726 }
727
728 QItemSelection deselected;
729 QItemSelection newParts;
730 QItemSelection::iterator it = ranges.begin();
731 while (it != ranges.end()) {
732 if (it->topLeft().parent() != parent) { // Check parents until reaching root or contained in range
733 QModelIndex itParent = it->topLeft().parent();
734 while (itParent.isValid() && itParent.parent() != parent)
735 itParent = itParent.parent();
736
737 if (itParent.isValid() && start <= itParent.row() && itParent.row() <= end) {
738 deselected.append(*it);
739 it = ranges.erase(it);
740 } else {
741 ++it;
742 }
743 } else if (start <= it->bottom() && it->bottom() <= end // Full inclusion
744 && start <= it->top() && it->top() <= end) {
745 deselected.append(*it);
746 it = ranges.erase(it);
747 } else if (start <= it->top() && it->top() <= end) { // Top intersection
748 deselected.append(QItemSelectionRange(it->topLeft(), model->index(end, it->right(), it->parent())));
749 *it = QItemSelectionRange(model->index(end + 1, it->left(), it->parent()), it->bottomRight());
750 ++it;
751 } else if (start <= it->bottom() && it->bottom() <= end) { // Bottom intersection
752 deselected.append(QItemSelectionRange(model->index(start, it->left(), it->parent()), it->bottomRight()));
753 *it = QItemSelectionRange(it->topLeft(), model->index(start - 1, it->right(), it->parent()));
754 ++it;
755 } else if (it->top() < start && end < it->bottom()) { // Middle intersection
756 // If the parent contains (1, 2, 3, 4, 5, 6, 7, 8) and [3, 4, 5, 6] is selected,
757 // and [4, 5] is removed, we need to split [3, 4, 5, 6] into [3], [4, 5] and [6].
758 // [4, 5] is appended to deselected, and [3] and [6] remain part of the selection
759 // in ranges.
760 const QItemSelectionRange removedRange(model->index(start, it->left(), it->parent()),
761 model->index(end, it->right(), it->parent()));
762 deselected.append(removedRange);
763 QItemSelection::split(*it, removedRange, &newParts);
764 it = ranges.erase(it);
765 } else
766 ++it;
767 }
768 ranges.append(newParts);
769
770 if (!deselected.isEmpty())
771 emit q->selectionChanged(QItemSelection(), deselected);
772}
773
774/*!
775 \internal
776*/
777void QItemSelectionModelPrivate::_q_columnsAboutToBeRemoved(const QModelIndex &parent,
778 int start, int end)
779{
780 Q_Q(QItemSelectionModel);
781
782 // update current index
783 if (currentIndex.isValid() && parent == currentIndex.parent()
784 && currentIndex.column() >= start && currentIndex.column() <= end) {
785 QModelIndex old = currentIndex;
786 if (start > 0) // there are columns to the left of the change
787 currentIndex = model->index(old.row(), start - 1, parent);
788 else if (model && end < model->columnCount() - 1) // there are columns to the right of the change
789 currentIndex = model->index(old.row(), end + 1, parent);
790 else // there are no columns left in the table
791 currentIndex = QModelIndex();
792 emit q->currentChanged(currentIndex, old);
793 if (currentIndex.row() != old.row())
794 emit q->currentRowChanged(currentIndex, old);
795 emit q->currentColumnChanged(currentIndex, old);
796 }
797
798 // update selections
799 QModelIndex tl = model->index(0, start, parent);
800 QModelIndex br = model->index(model->rowCount(parent) - 1, end, parent);
801 q->select(QItemSelection(tl, br), QItemSelectionModel::Deselect);
802 finalize();
803}
804
805/*!
806 \internal
807
808 Split selection ranges if columns are about to be inserted in the middle.
809*/
810void QItemSelectionModelPrivate::_q_columnsAboutToBeInserted(const QModelIndex &parent,
811 int start, int end)
812{
813 Q_UNUSED(end);
814 finalize();
815 QList<QItemSelectionRange> split;
816 QList<QItemSelectionRange>::iterator it = ranges.begin();
817 for (; it != ranges.end(); ) {
818 if ((*it).isValid() && (*it).parent() == parent
819 && (*it).left() < start && (*it).right() >= start) {
820 QModelIndex bottomMiddle = model->index((*it).bottom(), start - 1, (*it).parent());
821 QItemSelectionRange left((*it).topLeft(), bottomMiddle);
822 QModelIndex topMiddle = model->index((*it).top(), start, (*it).parent());
823 QItemSelectionRange right(topMiddle, (*it).bottomRight());
824 it = ranges.erase(it);
825 split.append(left);
826 split.append(right);
827 } else {
828 ++it;
829 }
830 }
831 ranges += split;
832}
833
834/*!
835 \internal
836
837 Split selection ranges if rows are about to be inserted in the middle.
838*/
839void QItemSelectionModelPrivate::_q_rowsAboutToBeInserted(const QModelIndex &parent,
840 int start, int end)
841{
842 Q_UNUSED(end);
843 finalize();
844 QList<QItemSelectionRange> split;
845 QList<QItemSelectionRange>::iterator it = ranges.begin();
846 for (; it != ranges.end(); ) {
847 if ((*it).isValid() && (*it).parent() == parent
848 && (*it).top() < start && (*it).bottom() >= start) {
849 QModelIndex middleRight = model->index(start - 1, (*it).right(), (*it).parent());
850 QItemSelectionRange top((*it).topLeft(), middleRight);
851 QModelIndex middleLeft = model->index(start, (*it).left(), (*it).parent());
852 QItemSelectionRange bottom(middleLeft, (*it).bottomRight());
853 it = ranges.erase(it);
854 split.append(top);
855 split.append(bottom);
856 } else {
857 ++it;
858 }
859 }
860 ranges += split;
861}
862
863/*!
864 \internal
865
866 Split selection into individual (persistent) indexes. This is done in
867 preparation for the layoutChanged() signal, where the indexes can be
868 merged again.
869*/
870void QItemSelectionModelPrivate::_q_layoutAboutToBeChanged(const QList<QPersistentModelIndex> &, QAbstractItemModel::LayoutChangeHint hint)
871{
872 savedPersistentIndexes.clear();
873 savedPersistentCurrentIndexes.clear();
874 savedPersistentRowLengths.clear();
875 savedPersistentCurrentRowLengths.clear();
876
877 // optimization for when all indexes are selected
878 // (only if there is lots of items (1000) because this is not entirely correct)
879 if (ranges.isEmpty() && currentSelection.count() == 1) {
880 QItemSelectionRange range = currentSelection.constFirst();
881 QModelIndex parent = range.parent();
882 tableRowCount = model->rowCount(parent);
883 tableColCount = model->columnCount(parent);
884 if (tableRowCount * tableColCount > 1000
885 && range.top() == 0
886 && range.left() == 0
887 && range.bottom() == tableRowCount - 1
888 && range.right() == tableColCount - 1) {
889 tableSelected = true;
890 tableParent = parent;
891 return;
892 }
893 }
894 tableSelected = false;
895
896 if (hint == QAbstractItemModel::VerticalSortHint) {
897 // Special case when we know we're sorting vertically. We can assume that all indexes for columns
898 // are displaced the same way, and therefore we only need to track an index from one column per
899 // row with a QPersistentModelIndex together with the length of items to the right of it
900 // which are displaced the same way.
901 // An algorithm which contains the same assumption is used to process layoutChanged.
902 savedPersistentRowLengths = qSelectionPersistentRowLengths(ranges);
903 savedPersistentCurrentRowLengths = qSelectionPersistentRowLengths(currentSelection);
904 } else {
905 savedPersistentIndexes = qSelectionIndexes<QList<QPersistentModelIndex>>(ranges);
906 savedPersistentCurrentIndexes = qSelectionIndexes<QList<QPersistentModelIndex>>(currentSelection);
907 }
908}
909/*!
910 \internal
911*/
912static QItemSelection mergeRowLengths(const QList<QPair<QPersistentModelIndex, uint>> &rowLengths)
913{
914 if (rowLengths.isEmpty())
915 return QItemSelection();
916
917 QItemSelection result;
918 int i = 0;
919 while (i < rowLengths.count()) {
920 const QPersistentModelIndex &tl = rowLengths.at(i).first;
921 if (!tl.isValid()) {
922 ++i;
923 continue;
924 }
925 QPersistentModelIndex br = tl;
926 const uint length = rowLengths.at(i).second;
927 while (++i < rowLengths.count()) {
928 const QPersistentModelIndex &next = rowLengths.at(i).first;
929 if (!next.isValid())
930 continue;
931 const uint nextLength = rowLengths.at(i).second;
932 if ((nextLength == length)
933 && (next.row() == br.row() + 1)
934 && (next.column() == br.column())
935 && (next.parent() == br.parent())) {
936 br = next;
937 } else {
938 break;
939 }
940 }
941 result.append(QItemSelectionRange(tl, br.sibling(br.row(), br.column() + length - 1)));
942 }
943 return result;
944}
945
946/*!
947 \internal
948
949 Merges \a indexes into an item selection made up of ranges.
950 Assumes that the indexes are sorted.
951*/
952static QItemSelection mergeIndexes(const QList<QPersistentModelIndex> &indexes)
953{
954 QItemSelection colSpans;
955 // merge columns
956 int i = 0;
957 while (i < indexes.count()) {
958 const QPersistentModelIndex &tl = indexes.at(i);
959 if (!tl.isValid()) {
960 ++i;
961 continue;
962 }
963 QPersistentModelIndex br = tl;
964 QModelIndex brParent = br.parent();
965 int brRow = br.row();
966 int brColumn = br.column();
967 while (++i < indexes.count()) {
968 const QPersistentModelIndex &next = indexes.at(i);
969 if (!next.isValid())
970 continue;
971 const QModelIndex nextParent = next.parent();
972 const int nextRow = next.row();
973 const int nextColumn = next.column();
974 if ((nextParent == brParent)
975 && (nextRow == brRow)
976 && (nextColumn == brColumn + 1)) {
977 br = next;
978 brParent = nextParent;
979 brRow = nextRow;
980 brColumn = nextColumn;
981 } else {
982 break;
983 }
984 }
985 colSpans.append(QItemSelectionRange(tl, br));
986 }
987 // merge rows
988 QItemSelection rowSpans;
989 i = 0;
990 while (i < colSpans.count()) {
991 QModelIndex tl = colSpans.at(i).topLeft();
992 QModelIndex br = colSpans.at(i).bottomRight();
993 QModelIndex prevTl = tl;
994 while (++i < colSpans.count()) {
995 QModelIndex nextTl = colSpans.at(i).topLeft();
996 QModelIndex nextBr = colSpans.at(i).bottomRight();
997
998 if (nextTl.parent() != tl.parent())
999 break; // we can't merge selection ranges from different parents
1000
1001 if ((nextTl.column() == prevTl.column()) && (nextBr.column() == br.column())
1002 && (nextTl.row() == prevTl.row() + 1) && (nextBr.row() == br.row() + 1)) {
1003 br = nextBr;
1004 prevTl = nextTl;
1005 } else {
1006 break;
1007 }
1008 }
1009 rowSpans.append(QItemSelectionRange(tl, br));
1010 }
1011 return rowSpans;
1012}
1013
1014/*!
1015 \internal
1016
1017 Sort predicate function for QItemSelectionModelPrivate::_q_layoutChanged(),
1018 sorting by parent first in addition to operator<(). This is to prevent
1019 fragmentation of the selection by grouping indexes with the same row, column
1020 of different parents next to each other, which may happen when a selection
1021 spans sub-trees.
1022*/
1023static bool qt_PersistentModelIndexLessThan(const QPersistentModelIndex &i1, const QPersistentModelIndex &i2)
1024{
1025 const QModelIndex parent1 = i1.parent();
1026 const QModelIndex parent2 = i2.parent();
1027 return parent1 == parent2 ? i1 < i2 : parent1 < parent2;
1028}
1029
1030/*!
1031 \internal
1032
1033 Merge the selected indexes into selection ranges again.
1034*/
1035void QItemSelectionModelPrivate::_q_layoutChanged(const QList<QPersistentModelIndex> &, QAbstractItemModel::LayoutChangeHint hint)
1036{
1037 // special case for when all indexes are selected
1038 if (tableSelected && tableColCount == model->columnCount(tableParent)
1039 && tableRowCount == model->rowCount(tableParent)) {
1040 ranges.clear();
1041 currentSelection.clear();
1042 int bottom = tableRowCount - 1;
1043 int right = tableColCount - 1;
1044 QModelIndex tl = model->index(0, 0, tableParent);
1045 QModelIndex br = model->index(bottom, right, tableParent);
1046 currentSelection << QItemSelectionRange(tl, br);
1047 tableParent = QModelIndex();
1048 tableSelected = false;
1049 return;
1050 }
1051
1052 if ((hint != QAbstractItemModel::VerticalSortHint && savedPersistentCurrentIndexes.isEmpty() && savedPersistentIndexes.isEmpty())
1053 || (hint == QAbstractItemModel::VerticalSortHint && savedPersistentRowLengths.isEmpty() && savedPersistentCurrentRowLengths.isEmpty())) {
1054 // either the selection was actually empty, or we
1055 // didn't get the layoutAboutToBeChanged() signal
1056 return;
1057 }
1058
1059 // clear the "old" selection
1060 ranges.clear();
1061 currentSelection.clear();
1062
1063 if (hint != QAbstractItemModel::VerticalSortHint) {
1064 // sort the "new" selection, as preparation for merging
1065 std::stable_sort(savedPersistentIndexes.begin(), savedPersistentIndexes.end(),
1066 qt_PersistentModelIndexLessThan);
1067 std::stable_sort(savedPersistentCurrentIndexes.begin(), savedPersistentCurrentIndexes.end(),
1068 qt_PersistentModelIndexLessThan);
1069
1070 // update the selection by merging the individual indexes
1071 ranges = mergeIndexes(savedPersistentIndexes);
1072 currentSelection = mergeIndexes(savedPersistentCurrentIndexes);
1073
1074 // release the persistent indexes
1075 savedPersistentIndexes.clear();
1076 savedPersistentCurrentIndexes.clear();
1077 } else {
1078 // sort the "new" selection, as preparation for merging
1079 std::stable_sort(savedPersistentRowLengths.begin(), savedPersistentRowLengths.end());
1080 std::stable_sort(savedPersistentCurrentRowLengths.begin(), savedPersistentCurrentRowLengths.end());
1081
1082 // update the selection by merging the individual indexes
1083 ranges = mergeRowLengths(savedPersistentRowLengths);
1084 currentSelection = mergeRowLengths(savedPersistentCurrentRowLengths);
1085
1086 // release the persistent indexes
1087 savedPersistentRowLengths.clear();
1088 savedPersistentCurrentRowLengths.clear();
1089 }
1090}
1091
1092/*!
1093 \class QItemSelectionModel
1094 \inmodule QtCore
1095
1096 \brief The QItemSelectionModel class keeps track of a view's selected items.
1097
1098 \ingroup model-view
1099
1100 A QItemSelectionModel keeps track of the selected items in a view, or
1101 in several views onto the same model. It also keeps track of the
1102 currently selected item in a view.
1103
1104 The QItemSelectionModel class is one of the \l{Model/View Classes}
1105 and is part of Qt's \l{Model/View Programming}{model/view framework}.
1106
1107 The selected items are stored using ranges. Whenever you want to
1108 modify the selected items use select() and provide either a
1109 QItemSelection, or a QModelIndex and a QItemSelectionModel::SelectionFlag.
1110
1111 The QItemSelectionModel takes a two layer approach to selection
1112 management, dealing with both selected items that have been committed
1113 and items that are part of the current selection. The current
1114 selected items are part of the current interactive selection (for
1115 example with rubber-band selection or keyboard-shift selections).
1116
1117 To update the currently selected items, use the bitwise OR of
1118 QItemSelectionModel::Current and any of the other SelectionFlags.
1119 If you omit the QItemSelectionModel::Current command, a new current
1120 selection will be created, and the previous one added to the whole
1121 selection. All functions operate on both layers; for example,
1122 \l {QTableWidget::selectedItems()}{selecteditems()} will return items from both layers.
1123
1124 \note Since 5.5, \l{QItemSelectionModel::model()}{model},
1125 \l{QItemSelectionModel::hasSelection()}{hasSelection}, and
1126 \l{QItemSelectionModel::currentIndex()}{currentIndex} are meta-object properties.
1127
1128 \sa {Model/View Programming}, QAbstractItemModel, {Chart Example}
1129*/
1130
1131/*!
1132 Constructs a selection model that operates on the specified item \a model.
1133*/
1134QItemSelectionModel::QItemSelectionModel(QAbstractItemModel *model)
1135 : QObject(*new QItemSelectionModelPrivate, model)
1136{
1137 d_func()->initModel(model);
1138}
1139
1140/*!
1141 Constructs a selection model that operates on the specified item \a model with \a parent.
1142*/
1143QItemSelectionModel::QItemSelectionModel(QAbstractItemModel *model, QObject *parent)
1144 : QObject(*new QItemSelectionModelPrivate, parent)
1145{
1146 d_func()->initModel(model);
1147}
1148
1149/*!
1150 \internal
1151*/
1152QItemSelectionModel::QItemSelectionModel(QItemSelectionModelPrivate &dd, QAbstractItemModel *model)
1153 : QObject(dd, model)
1154{
1155 dd.initModel(model);
1156}
1157
1158/*!
1159 Destroys the selection model.
1160*/
1161QItemSelectionModel::~QItemSelectionModel()
1162{
1163}
1164
1165/*!
1166 Selects the model item \a index using the specified \a command, and emits
1167 selectionChanged().
1168
1169 \sa QItemSelectionModel::SelectionFlags
1170*/
1171void QItemSelectionModel::select(const QModelIndex &index, QItemSelectionModel::SelectionFlags command)
1172{
1173 QItemSelection selection(index, index);
1174 select(selection, command);
1175}
1176
1177/*!
1178 \fn void QItemSelectionModel::currentChanged(const QModelIndex &current, const QModelIndex &previous)
1179
1180 This signal is emitted whenever the current item changes. The \a previous
1181 model item index is replaced by the \a current index as the selection's
1182 current item.
1183
1184 Note that this signal will not be emitted when the item model is reset.
1185
1186 \sa currentIndex(), setCurrentIndex(), selectionChanged()
1187*/
1188
1189/*!
1190 \fn void QItemSelectionModel::currentColumnChanged(const QModelIndex &current, const QModelIndex &previous)
1191
1192 This signal is emitted if the \a current item changes and its column is
1193 different to the column of the \a previous current item.
1194
1195 Note that this signal will not be emitted when the item model is reset.
1196
1197 \sa currentChanged(), currentRowChanged(), currentIndex(), setCurrentIndex()
1198*/
1199
1200/*!
1201 \fn void QItemSelectionModel::currentRowChanged(const QModelIndex &current, const QModelIndex &previous)
1202
1203 This signal is emitted if the \a current item changes and its row is
1204 different to the row of the \a previous current item.
1205
1206 Note that this signal will not be emitted when the item model is reset.
1207
1208 \sa currentChanged(), currentColumnChanged(), currentIndex(), setCurrentIndex()
1209*/
1210
1211/*!
1212 \fn void QItemSelectionModel::selectionChanged(const QItemSelection &selected, const QItemSelection &deselected)
1213
1214 This signal is emitted whenever the selection changes. The change in the
1215 selection is represented as an item selection of \a deselected items and
1216 an item selection of \a selected items.
1217
1218 Note the that the current index changes independently from the selection.
1219 Also note that this signal will not be emitted when the item model is reset.
1220
1221 \sa select(), currentChanged()
1222*/
1223
1224/*!
1225 \fn void QItemSelectionModel::modelChanged(QAbstractItemModel *model)
1226 \since 5.5
1227
1228 This signal is emitted when the \a model is successfully set with setModel().
1229
1230 \sa model(), setModel()
1231*/
1232
1233
1234/*!
1235 \enum QItemSelectionModel::SelectionFlag
1236
1237 This enum describes the way the selection model will be updated.
1238
1239 \value NoUpdate No selection will be made.
1240 \value Clear The complete selection will be cleared.
1241 \value Select All specified indexes will be selected.
1242 \value Deselect All specified indexes will be deselected.
1243 \value Toggle All specified indexes will be selected or
1244 deselected depending on their current state.
1245 \value Current The current selection will be updated.
1246 \value Rows All indexes will be expanded to span rows.
1247 \value Columns All indexes will be expanded to span columns.
1248 \value SelectCurrent A combination of Select and Current, provided for
1249 convenience.
1250 \value ToggleCurrent A combination of Toggle and Current, provided for
1251 convenience.
1252 \value ClearAndSelect A combination of Clear and Select, provided for
1253 convenience.
1254*/
1255
1256namespace {
1257namespace QtFunctionObjects {
1258struct IsNotValid {
1259 typedef bool result_type;
1260 struct is_transparent : std::true_type {};
1261 template <typename T>
1262 constexpr bool operator()(T &t) const noexcept(noexcept(t.isValid()))
1263 { return !t.isValid(); }
1264 template <typename T>
1265 constexpr bool operator()(T *t) const noexcept(noexcept(t->isValid()))
1266 { return !t->isValid(); }
1267};
1268}
1269} // unnamed namespace
1270
1271/*!
1272 Selects the item \a selection using the specified \a command, and emits
1273 selectionChanged().
1274
1275 \sa QItemSelectionModel::SelectionFlag
1276*/
1277void QItemSelectionModel::select(const QItemSelection &selection, QItemSelectionModel::SelectionFlags command)
1278{
1279 Q_D(QItemSelectionModel);
1280 if (!d->model) {
1281 qWarning("QItemSelectionModel: Selecting when no model has been set will result in a no-op.");
1282 return;
1283 }
1284 if (command == NoUpdate)
1285 return;
1286
1287 // store old selection
1288 QItemSelection sel = selection;
1289 // If d->ranges is non-empty when the source model is reset the persistent indexes
1290 // it contains will be invalid. We can't clear them in a modelReset slot because that might already
1291 // be too late if another model observer is connected to the same modelReset slot and is invoked first
1292 // it might call select() on this selection model before any such QItemSelectionModelPrivate::_q_modelReset() slot
1293 // is invoked, so it would not be cleared yet. We clear it invalid ranges in it here.
1294 using namespace QtFunctionObjects;
1295 d->ranges.erase(std::remove_if(d->ranges.begin(), d->ranges.end(), IsNotValid()),
1296 d->ranges.end());
1297
1298 QItemSelection old = d->ranges;
1299 old.merge(d->currentSelection, d->currentCommand);
1300
1301 // expand selection according to SelectionBehavior
1302 if (command & Rows || command & Columns)
1303 sel = d->expandSelection(sel, command);
1304
1305 // clear ranges and currentSelection
1306 if (command & Clear) {
1307 d->ranges.clear();
1308 d->currentSelection.clear();
1309 }
1310
1311 // merge and clear currentSelection if Current was not set (ie. start new currentSelection)
1312 if (!(command & Current))
1313 d->finalize();
1314
1315 // update currentSelection
1316 if (command & Toggle || command & Select || command & Deselect) {
1317 d->currentCommand = command;
1318 d->currentSelection = sel;
1319 }
1320
1321 // generate new selection, compare with old and emit selectionChanged()
1322 QItemSelection newSelection = d->ranges;
1323 newSelection.merge(d->currentSelection, d->currentCommand);
1324 emitSelectionChanged(newSelection, old);
1325}
1326
1327/*!
1328 Clears the selection model. Emits selectionChanged() and currentChanged().
1329*/
1330void QItemSelectionModel::clear()
1331{
1332 clearSelection();
1333 clearCurrentIndex();
1334}
1335
1336/*!
1337 Clears the current index. Emits currentChanged().
1338 */
1339void QItemSelectionModel::clearCurrentIndex()
1340{
1341 Q_D(QItemSelectionModel);
1342 QModelIndex previous = d->currentIndex;
1343 d->currentIndex = QModelIndex();
1344 if (previous.isValid()) {
1345 emit currentChanged(d->currentIndex, previous);
1346 emit currentRowChanged(d->currentIndex, previous);
1347 emit currentColumnChanged(d->currentIndex, previous);
1348 }
1349}
1350
1351/*!
1352 Clears the selection model. Does not emit any signals.
1353*/
1354void QItemSelectionModel::reset()
1355{
1356 const QSignalBlocker blocker(this);
1357 clear();
1358}
1359
1360/*!
1361 \since 4.2
1362 Clears the selection in the selection model. Emits selectionChanged().
1363*/
1364void QItemSelectionModel::clearSelection()
1365{
1366 Q_D(QItemSelectionModel);
1367 if (d->ranges.count() == 0 && d->currentSelection.count() == 0)
1368 return;
1369
1370 select(QItemSelection(), Clear);
1371}
1372
1373
1374/*!
1375 Sets the model item \a index to be the current item, and emits
1376 currentChanged(). The current item is used for keyboard navigation and
1377 focus indication; it is independent of any selected items, although a
1378 selected item can also be the current item.
1379
1380 Depending on the specified \a command, the \a index can also become part
1381 of the current selection.
1382 \sa select()
1383*/
1384void QItemSelectionModel::setCurrentIndex(const QModelIndex &index, QItemSelectionModel::SelectionFlags command)
1385{
1386 Q_D(QItemSelectionModel);
1387 if (!d->model) {
1388 qWarning("QItemSelectionModel: Setting the current index when no model has been set will result in a no-op.");
1389 return;
1390 }
1391 if (index == d->currentIndex) {
1392 if (command != NoUpdate)
1393 select(index, command); // select item
1394 return;
1395 }
1396 QPersistentModelIndex previous = d->currentIndex;
1397 d->currentIndex = index; // set current before emitting selection changed below
1398 if (command != NoUpdate)
1399 select(d->currentIndex, command); // select item
1400 emit currentChanged(d->currentIndex, previous);
1401 if (d->currentIndex.row() != previous.row() ||
1402 d->currentIndex.parent() != previous.parent())
1403 emit currentRowChanged(d->currentIndex, previous);
1404 if (d->currentIndex.column() != previous.column() ||
1405 d->currentIndex.parent() != previous.parent())
1406 emit currentColumnChanged(d->currentIndex, previous);
1407}
1408
1409/*!
1410 Returns the model item index for the current item, or an invalid index
1411 if there is no current item.
1412*/
1413QModelIndex QItemSelectionModel::currentIndex() const
1414{
1415 return static_cast<QModelIndex>(d_func()->currentIndex);
1416}
1417
1418/*!
1419 Returns \c true if the given model item \a index is selected.
1420*/
1421bool QItemSelectionModel::isSelected(const QModelIndex &index) const
1422{
1423 Q_D(const QItemSelectionModel);
1424 if (d->model != index.model() || !index.isValid())
1425 return false;
1426
1427 bool selected = false;
1428 // search model ranges
1429 QList<QItemSelectionRange>::const_iterator it = d->ranges.begin();
1430 for (; it != d->ranges.end(); ++it) {
1431 if ((*it).isValid() && (*it).contains(index)) {
1432 selected = true;
1433 break;
1434 }
1435 }
1436
1437 // check currentSelection
1438 if (d->currentSelection.count()) {
1439 if ((d->currentCommand & Deselect) && selected)
1440 selected = !d->currentSelection.contains(index);
1441 else if (d->currentCommand & Toggle)
1442 selected ^= d->currentSelection.contains(index);
1443 else if ((d->currentCommand & Select) && !selected)
1444 selected = d->currentSelection.contains(index);
1445 }
1446
1447 if (selected) {
1448 Qt::ItemFlags flags = d->model->flags(index);
1449 return (flags & Qt::ItemIsSelectable);
1450 }
1451
1452 return false;
1453}
1454
1455/*!
1456 Returns \c true if all items are selected in the \a row with the given
1457 \a parent.
1458
1459 Note that this function is usually faster than calling isSelected()
1460 on all items in the same row and that unselectable items are
1461 ignored.
1462
1463 \note Since Qt 5.15, the default argument for \a parent is an empty
1464 model index.
1465*/
1466bool QItemSelectionModel::isRowSelected(int row, const QModelIndex &parent) const
1467{
1468 Q_D(const QItemSelectionModel);
1469 if (!d->model)
1470 return false;
1471 if (parent.isValid() && d->model != parent.model())
1472 return false;
1473
1474 // return false if row exist in currentSelection (Deselect)
1475 if (d->currentCommand & Deselect && d->currentSelection.count()) {
1476 for (int i=0; i<d->currentSelection.count(); ++i) {
1477 if (d->currentSelection.at(i).parent() == parent &&
1478 row >= d->currentSelection.at(i).top() &&
1479 row <= d->currentSelection.at(i).bottom())
1480 return false;
1481 }
1482 }
1483 // return false if ranges in both currentSelection and ranges
1484 // intersect and have the same row contained
1485 if (d->currentCommand & Toggle && d->currentSelection.count()) {
1486 for (int i=0; i<d->currentSelection.count(); ++i)
1487 if (d->currentSelection.at(i).top() <= row &&
1488 d->currentSelection.at(i).bottom() >= row)
1489 for (int j=0; j<d->ranges.count(); ++j)
1490 if (d->ranges.at(j).top() <= row && d->ranges.at(j).bottom() >= row
1491 && d->currentSelection.at(i).intersected(d->ranges.at(j)).isValid())
1492 return false;
1493 }
1494
1495 auto isSelectable = [&](int row, int column) {
1496 Qt::ItemFlags flags = d->model->index(row, column, parent).flags();
1497 return (flags & Qt::ItemIsSelectable);
1498 };
1499
1500 const int colCount = d->model->columnCount(parent);
1501 int unselectable = 0;
1502 // add ranges and currentSelection and check through them all
1503 QList<QItemSelectionRange>::const_iterator it;
1504 QList<QItemSelectionRange> joined = d->ranges;
1505 if (d->currentSelection.count())
1506 joined += d->currentSelection;
1507 for (int column = 0; column < colCount; ++column) {
1508 if (!isSelectable(row, column)) {
1509 ++unselectable;
1510 continue;
1511 }
1512
1513 for (it = joined.constBegin(); it != joined.constEnd(); ++it) {
1514 if ((*it).contains(row, column, parent)) {
1515 for (int i = column; i <= (*it).right(); ++i) {
1516 if (!isSelectable(row, i))
1517 ++unselectable;
1518 }
1519
1520 column = qMax(column, (*it).right());
1521 break;
1522 }
1523 }
1524 if (it == joined.constEnd())
1525 return false;
1526 }
1527 return unselectable < colCount;
1528}
1529
1530/*!
1531 Returns \c true if all items are selected in the \a column with the given
1532 \a parent.
1533
1534 Note that this function is usually faster than calling isSelected()
1535 on all items in the same column and that unselectable items are
1536 ignored.
1537
1538 \note Since Qt 5.15, the default argument for \a parent is an empty
1539 model index.
1540*/
1541bool QItemSelectionModel::isColumnSelected(int column, const QModelIndex &parent) const
1542{
1543 Q_D(const QItemSelectionModel);
1544 if (!d->model)
1545 return false;
1546 if (parent.isValid() && d->model != parent.model())
1547 return false;
1548
1549 // return false if column exist in currentSelection (Deselect)
1550 if (d->currentCommand & Deselect && d->currentSelection.count()) {
1551 for (int i = 0; i < d->currentSelection.count(); ++i) {
1552 if (d->currentSelection.at(i).parent() == parent &&
1553 column >= d->currentSelection.at(i).left() &&
1554 column <= d->currentSelection.at(i).right())
1555 return false;
1556 }
1557 }
1558 // return false if ranges in both currentSelection and the selection model
1559 // intersect and have the same column contained
1560 if (d->currentCommand & Toggle && d->currentSelection.count()) {
1561 for (int i = 0; i < d->currentSelection.count(); ++i) {
1562 if (d->currentSelection.at(i).left() <= column &&
1563 d->currentSelection.at(i).right() >= column) {
1564 for (int j = 0; j < d->ranges.count(); ++j) {
1565 if (d->ranges.at(j).left() <= column && d->ranges.at(j).right() >= column
1566 && d->currentSelection.at(i).intersected(d->ranges.at(j)).isValid()) {
1567 return false;
1568 }
1569 }
1570 }
1571 }
1572 }
1573
1574 auto isSelectable = [&](int row, int column) {
1575 Qt::ItemFlags flags = d->model->index(row, column, parent).flags();
1576 return (flags & Qt::ItemIsSelectable);
1577 };
1578 const int rowCount = d->model->rowCount(parent);
1579 int unselectable = 0;
1580
1581 // add ranges and currentSelection and check through them all
1582 QList<QItemSelectionRange>::const_iterator it;
1583 QList<QItemSelectionRange> joined = d->ranges;
1584 if (d->currentSelection.count())
1585 joined += d->currentSelection;
1586 for (int row = 0; row < rowCount; ++row) {
1587 if (!isSelectable(row, column)) {
1588 ++unselectable;
1589 continue;
1590 }
1591 for (it = joined.constBegin(); it != joined.constEnd(); ++it) {
1592 if ((*it).contains(row, column, parent)) {
1593 for (int i = row; i <= (*it).bottom(); ++i) {
1594 if (!isSelectable(i, column)) {
1595 ++unselectable;
1596 }
1597 }
1598 row = qMax(row, (*it).bottom());
1599 break;
1600 }
1601 }
1602 if (it == joined.constEnd())
1603 return false;
1604 }
1605 return unselectable < rowCount;
1606}
1607
1608/*!
1609 Returns \c true if there are any items selected in the \a row with the given
1610 \a parent.
1611
1612 \note Since Qt 5.15, the default argument for \a parent is an empty
1613 model index.
1614*/
1615bool QItemSelectionModel::rowIntersectsSelection(int row, const QModelIndex &parent) const
1616{
1617 Q_D(const QItemSelectionModel);
1618 if (!d->model)
1619 return false;
1620 if (parent.isValid() && d->model != parent.model())
1621 return false;
1622
1623 QItemSelection sel = d->ranges;
1624 sel.merge(d->currentSelection, d->currentCommand);
1625 for (const QItemSelectionRange &range : qAsConst(sel)) {
1626 if (range.parent() != parent)
1627 return false;
1628 int top = range.top();
1629 int bottom = range.bottom();
1630 int left = range.left();
1631 int right = range.right();
1632 if (top <= row && bottom >= row) {
1633 for (int j = left; j <= right; j++) {
1634 const Qt::ItemFlags flags = d->model->index(row, j, parent).flags();
1635 if ((flags & Qt::ItemIsSelectable) && (flags & Qt::ItemIsEnabled))
1636 return true;
1637 }
1638 }
1639 }
1640
1641 return false;
1642}
1643
1644/*!
1645 Returns \c true if there are any items selected in the \a column with the given
1646 \a parent.
1647
1648 \note Since Qt 5.15, the default argument for \a parent is an empty
1649 model index.
1650*/
1651bool QItemSelectionModel::columnIntersectsSelection(int column, const QModelIndex &parent) const
1652{
1653 Q_D(const QItemSelectionModel);
1654 if (!d->model)
1655 return false;
1656 if (parent.isValid() && d->model != parent.model())
1657 return false;
1658
1659 QItemSelection sel = d->ranges;
1660 sel.merge(d->currentSelection, d->currentCommand);
1661 for (const QItemSelectionRange &range : qAsConst(sel)) {
1662 if (range.parent() != parent)
1663 return false;
1664 int top = range.top();
1665 int bottom = range.bottom();
1666 int left = range.left();
1667 int right = range.right();
1668 if (left <= column && right >= column) {
1669 for (int j = top; j <= bottom; j++) {
1670 const Qt::ItemFlags flags = d->model->index(j, column, parent).flags();
1671 if ((flags & Qt::ItemIsSelectable) && (flags & Qt::ItemIsEnabled))
1672 return true;
1673 }
1674 }
1675 }
1676
1677 return false;
1678}
1679
1680/*!
1681 \since 4.2
1682
1683 Returns \c true if the selection model contains any selection ranges;
1684 otherwise returns \c false.
1685*/
1686bool QItemSelectionModel::hasSelection() const
1687{
1688 Q_D(const QItemSelectionModel);
1689 if (d->currentCommand & (Toggle | Deselect)) {
1690 QItemSelection sel = d->ranges;
1691 sel.merge(d->currentSelection, d->currentCommand);
1692 return !sel.isEmpty();
1693 } else {
1694 return !(d->ranges.isEmpty() && d->currentSelection.isEmpty());
1695 }
1696}
1697
1698/*!
1699 Returns a list of all selected model item indexes. The list contains no
1700 duplicates, and is not sorted.
1701*/
1702QModelIndexList QItemSelectionModel::selectedIndexes() const
1703{
1704 Q_D(const QItemSelectionModel);
1705 QItemSelection selected = d->ranges;
1706 selected.merge(d->currentSelection, d->currentCommand);
1707 return selected.indexes();
1708}
1709
1710struct RowOrColumnDefinition {
1711 QModelIndex parent;
1712 int rowOrColumn;
1713
1714 friend bool operator==(const RowOrColumnDefinition &lhs, const RowOrColumnDefinition &rhs) noexcept
1715 { return lhs.parent == rhs.parent && lhs.rowOrColumn == rhs.rowOrColumn; }
1716 friend bool operator!=(const RowOrColumnDefinition &lhs, const RowOrColumnDefinition &rhs) noexcept
1717 { return !operator==(lhs, rhs); }
1718};
1719size_t qHash(const RowOrColumnDefinition &key, size_t seed = 0) noexcept
1720{
1721 QtPrivate::QHashCombine hash;
1722 seed = hash(seed, key.parent);
1723 seed = hash(seed, key.rowOrColumn);
1724 return seed;
1725}
1726
1727QT_SPECIALIZE_STD_HASH_TO_CALL_QHASH_BY_CREF(RowOrColumnDefinition)
1728
1729/*!
1730 \since 4.2
1731 Returns the indexes in the given \a column for the rows where all columns are selected.
1732
1733 \sa selectedIndexes(), selectedColumns()
1734*/
1735
1736QModelIndexList QItemSelectionModel::selectedRows(int column) const
1737{
1738 QModelIndexList indexes;
1739
1740 QDuplicateTracker<RowOrColumnDefinition> rowsSeen;
1741
1742 const QItemSelection ranges = selection();
1743 for (int i = 0; i < ranges.count(); ++i) {
1744 const QItemSelectionRange &range = ranges.at(i);
1745 QModelIndex parent = range.parent();
1746 for (int row = range.top(); row <= range.bottom(); row++) {
1747 if (!rowsSeen.hasSeen({parent, row})) {
1748 if (isRowSelected(row, parent)) {
1749 indexes.append(model()->index(row, column, parent));
1750 }
1751 }
1752 }
1753 }
1754
1755 return indexes;
1756}
1757
1758/*!
1759 \since 4.2
1760 Returns the indexes in the given \a row for columns where all rows are selected.
1761
1762 \sa selectedIndexes(), selectedRows()
1763*/
1764
1765QModelIndexList QItemSelectionModel::selectedColumns(int row) const
1766{
1767 QModelIndexList indexes;
1768
1769 QDuplicateTracker<RowOrColumnDefinition> columnsSeen;
1770
1771 const QItemSelection ranges = selection();
1772 for (int i = 0; i < ranges.count(); ++i) {
1773 const QItemSelectionRange &range = ranges.at(i);
1774 QModelIndex parent = range.parent();
1775 for (int column = range.left(); column <= range.right(); column++) {
1776 if (!columnsSeen.hasSeen({parent, column})) {
1777 if (isColumnSelected(column, parent)) {
1778 indexes.append(model()->index(row, column, parent));
1779 }
1780 }
1781 }
1782 }
1783
1784 return indexes;
1785}
1786
1787/*!
1788 Returns the selection ranges stored in the selection model.
1789*/
1790const QItemSelection QItemSelectionModel::selection() const
1791{
1792 Q_D(const QItemSelectionModel);
1793 QItemSelection selected = d->ranges;
1794 selected.merge(d->currentSelection, d->currentCommand);
1795 // make sure we have no invalid ranges
1796 // ### should probably be handled more generic somewhere else
1797 using namespace QtFunctionObjects;
1798 selected.erase(std::remove_if(selected.begin(), selected.end(),
1799 IsNotValid()),
1800 selected.end());
1801 return selected;
1802}
1803
1804/*!
1805 \since 5.5
1806
1807 \property QItemSelectionModel::hasSelection
1808 \internal
1809*/
1810/*!
1811 \since 5.5
1812
1813 \property QItemSelectionModel::currentIndex
1814 \internal
1815*/
1816/*!
1817 \since 5.5
1818
1819 \property QItemSelectionModel::selectedIndexes
1820*/
1821
1822/*!
1823 \since 5.5
1824
1825 \property QItemSelectionModel::selection
1826 \internal
1827*/
1828/*!
1829 \since 5.5
1830
1831 \property QItemSelectionModel::model
1832 \internal
1833*/
1834/*!
1835 \since 5.5
1836
1837 Returns the item model operated on by the selection model.
1838*/
1839QAbstractItemModel *QItemSelectionModel::model()
1840{
1841 return d_func()->model;
1842}
1843
1844/*!
1845 Returns the item model operated on by the selection model.
1846*/
1847const QAbstractItemModel *QItemSelectionModel::model() const
1848{
1849 return d_func()->model;
1850}
1851
1852/*!
1853 \since 5.5
1854
1855 Sets the model to \a model. The modelChanged() signal will be emitted.
1856
1857 \sa model(), modelChanged()
1858*/
1859void QItemSelectionModel::setModel(QAbstractItemModel *model)
1860{
1861 Q_D(QItemSelectionModel);
1862 if (d->model == model)
1863 return;
1864
1865 d->initModel(model);
1866 emit modelChanged(model);
1867}
1868
1869/*!
1870 Compares the two selections \a newSelection and \a oldSelection
1871 and emits selectionChanged() with the deselected and selected items.
1872*/
1873void QItemSelectionModel::emitSelectionChanged(const QItemSelection &newSelection,
1874 const QItemSelection &oldSelection)
1875{
1876 // if both selections are empty or equal we return
1877 if ((oldSelection.isEmpty() && newSelection.isEmpty()) ||
1878 oldSelection == newSelection)
1879 return;
1880
1881 // if either selection is empty we do not need to compare
1882 if (oldSelection.isEmpty() || newSelection.isEmpty()) {
1883 emit selectionChanged(newSelection, oldSelection);
1884 return;
1885 }
1886
1887 QItemSelection deselected = oldSelection;
1888 QItemSelection selected = newSelection;
1889
1890 // remove equal ranges
1891 bool advance;
1892 for (int o = 0; o < deselected.count(); ++o) {
1893 advance = true;
1894 for (int s = 0; s < selected.count() && o < deselected.count();) {
1895 if (deselected.at(o) == selected.at(s)) {
1896 deselected.removeAt(o);
1897 selected.removeAt(s);
1898 advance = false;
1899 } else {
1900 ++s;
1901 }
1902 }
1903 if (advance)
1904 ++o;
1905 }
1906
1907 // find intersections
1908 QItemSelection intersections;
1909 for (int o = 0; o < deselected.count(); ++o) {
1910 for (int s = 0; s < selected.count(); ++s) {
1911 if (deselected.at(o).intersects(selected.at(s)))
1912 intersections.append(deselected.at(o).intersected(selected.at(s)));
1913 }
1914 }
1915
1916 // compare remaining ranges with intersections and split them to find deselected and selected
1917 for (int i = 0; i < intersections.count(); ++i) {
1918 // split deselected
1919 for (int o = 0; o < deselected.count();) {
1920 if (deselected.at(o).intersects(intersections.at(i))) {
1921 QItemSelection::split(deselected.at(o), intersections.at(i), &deselected);
1922 deselected.removeAt(o);
1923 } else {
1924 ++o;
1925 }
1926 }
1927 // split selected
1928 for (int s = 0; s < selected.count();) {
1929 if (selected.at(s).intersects(intersections.at(i))) {
1930 QItemSelection::split(selected.at(s), intersections.at(i), &selected);
1931 selected.removeAt(s);
1932 } else {
1933 ++s;
1934 }
1935 }
1936 }
1937
1938 if (!selected.isEmpty() || !deselected.isEmpty())
1939 emit selectionChanged(selected, deselected);
1940}
1941
1942#ifndef QT_NO_DEBUG_STREAM
1943QDebug operator<<(QDebug dbg, const QItemSelectionRange &range)
1944{
1945 QDebugStateSaver saver(dbg);
1946 dbg.nospace() << "QItemSelectionRange(" << range.topLeft()
1947 << ',' << range.bottomRight() << ')';
1948 return dbg;
1949}
1950#endif
1951
1952QT_END_NAMESPACE
1953
1954#include "moc_qitemselectionmodel.cpp"
1955