source: trunk/src/gui/itemviews/qitemselectionmodel.cpp@ 836

Last change on this file since 836 was 651, checked in by Dmitry A. Kuminov, 15 years ago

trunk: Merged in qt 4.6.2 sources.

File size: 56.6 KB
Line 
1/****************************************************************************
2**
3** Copyright (C) 2010 Nokia Corporation and/or its subsidiary(-ies).
4** All rights reserved.
5** Contact: Nokia Corporation (qt-info@nokia.com)
6**
7** This file is part of the QtGui module of the Qt Toolkit.
8**
9** $QT_BEGIN_LICENSE:LGPL$
10** Commercial Usage
11** Licensees holding valid Qt Commercial licenses may use this file in
12** accordance with the Qt Commercial License Agreement provided with the
13** Software or, alternatively, in accordance with the terms contained in
14** a written agreement between you and Nokia.
15**
16** GNU Lesser General Public License Usage
17** Alternatively, this file may be used under the terms of the GNU Lesser
18** General Public License version 2.1 as published by the Free Software
19** Foundation and appearing in the file LICENSE.LGPL included in the
20** packaging of this file. Please review the following information to
21** ensure the GNU Lesser General Public License version 2.1 requirements
22** will be met: http://www.gnu.org/licenses/old-licenses/lgpl-2.1.html.
23**
24** In addition, as a special exception, Nokia gives you certain additional
25** rights. These rights are described in the Nokia Qt LGPL Exception
26** version 1.1, included in the file LGPL_EXCEPTION.txt in this package.
27**
28** GNU General Public License Usage
29** Alternatively, this file may be used under the terms of the GNU
30** General Public License version 3.0 as published by the Free Software
31** Foundation and appearing in the file LICENSE.GPL included in the
32** packaging of this file. Please review the following information to
33** ensure the GNU General Public License version 3.0 requirements will be
34** met: http://www.gnu.org/copyleft/gpl.html.
35**
36** If you have questions regarding the use of this file, please contact
37** Nokia at qt-info@nokia.com.
38** $QT_END_LICENSE$
39**
40****************************************************************************/
41
42#include "qitemselectionmodel.h"
43#include <private/qitemselectionmodel_p.h>
44#include <qdebug.h>
45
46#ifndef QT_NO_ITEMVIEWS
47
48QT_BEGIN_NAMESPACE
49
50/*!
51 \class QItemSelectionRange
52
53 \brief The QItemSelectionRange class manages information about a
54 range of selected items in a model.
55
56 \ingroup model-view
57
58 A QItemSelectionRange contains information about a range of
59 selected items in a model. A range of items is a contiguous array
60 of model items, extending to cover a number of adjacent rows and
61 columns with a common parent item; this can be visualized as a
62 two-dimensional block of cells in a table. A selection range has a
63 top(), left() a bottom(), right() and a parent().
64
65 The QItemSelectionRange class is one of the \l{Model/View Classes}
66 and is part of Qt's \l{Model/View Programming}{model/view framework}.
67
68 The model items contained in the selection range can be obtained
69 using the indexes() function. Use QItemSelectionModel::selectedIndexes()
70 to get a list of all selected items for a view.
71
72 You can determine whether a given model item lies within a
73 particular range by using the contains() function. Ranges can also
74 be compared using the overloaded operators for equality and
75 inequality, and the intersects() function allows you to determine
76 whether two ranges overlap.
77
78 \sa {Model/View Programming}, QAbstractItemModel, QItemSelection,
79 QItemSelectionModel
80*/
81
82/*!
83 \fn QItemSelectionRange::QItemSelectionRange()
84
85 Constructs an empty selection range.
86*/
87
88/*!
89 \fn QItemSelectionRange::QItemSelectionRange(const QItemSelectionRange &other)
90
91 Copy constructor. Constructs a new selection range with the same contents
92 as the \a other range given.
93
94*/
95
96/*!
97 \fn QItemSelectionRange::QItemSelectionRange(const QModelIndex &topLeft, const QModelIndex &bottomRight)
98
99 Constructs a new selection range containing only the index specified
100 by the \a topLeft and the index \a bottomRight.
101
102*/
103
104/*!
105 \fn QItemSelectionRange::QItemSelectionRange(const QModelIndex &index)
106
107 Constructs a new selection range containing only the model item specified
108 by the model index \a index.
109*/
110
111/*!
112 \fn int QItemSelectionRange::top() const
113
114 Returns the row index corresponding to the uppermost selected row in the
115 selection range.
116
117*/
118
119/*!
120 \fn int QItemSelectionRange::left() const
121
122 Returns the column index corresponding to the leftmost selected column in the
123 selection range.
124*/
125
126/*!
127 \fn int QItemSelectionRange::bottom() const
128
129 Returns the row index corresponding to the lowermost selected row in the
130 selection range.
131
132*/
133
134/*!
135 \fn int QItemSelectionRange::right() const
136
137 Returns the column index corresponding to the rightmost selected column in
138 the selection range.
139
140*/
141
142/*!
143 \fn int QItemSelectionRange::width() const
144
145 Returns the number of selected columns in the selection range.
146
147*/
148
149/*!
150 \fn int QItemSelectionRange::height() const
151
152 Returns the number of selected rows in the selection range.
153
154*/
155
156/*!
157 \fn const QAbstractItemModel *QItemSelectionRange::model() const
158
159 Returns the model that the items in the selection range belong to.
160*/
161
162/*!
163 \fn QModelIndex QItemSelectionRange::topLeft() const
164
165 Returns the index for the item located at the top-left corner of
166 the selection range.
167
168 \sa top(), left(), bottomRight()
169*/
170
171/*!
172 \fn QModelIndex QItemSelectionRange::bottomRight() const
173
174 Returns the index for the item located at the bottom-right corner
175 of the selection range.
176
177 \sa bottom(), right(), topLeft()
178*/
179
180/*!
181 \fn QModelIndex QItemSelectionRange::parent() const
182
183 Returns the parent model item index of the items in the selection range.
184
185*/
186
187/*!
188 \fn bool QItemSelectionRange::contains(const QModelIndex &index) const
189
190 Returns true if the model item specified by the \a index lies within the
191 range of selected items; otherwise returns false.
192*/
193
194/*!
195 \fn bool QItemSelectionRange::contains(int row, int column,
196 const QModelIndex &parentIndex) const
197 \overload
198
199 Returns true if the model item specified by (\a row, \a column)
200 and with \a parentIndex as the parent item lies within the range
201 of selected items; otherwise returns false.
202*/
203
204/*!
205 \fn bool QItemSelectionRange::intersects(const QItemSelectionRange &other) const
206
207 Returns true if this selection range intersects (overlaps with) the \a other
208 range given; otherwise returns false.
209
210*/
211bool QItemSelectionRange::intersects(const QItemSelectionRange &other) const
212{
213 return (isValid() && other.isValid()
214 && parent() == other.parent()
215 && ((top() <= other.top() && bottom() >= other.top())
216 || (top() >= other.top() && top() <= other.bottom()))
217 && ((left() <= other.left() && right() >= other.left())
218 || (left() >= other.left() && left() <= other.right())));
219}
220
221/*!
222 \fn QItemSelectionRange QItemSelectionRange::intersect(const QItemSelectionRange &other) const
223 \obsolete
224
225 Use intersected(\a other) instead.
226*/
227
228/*!
229 \fn QItemSelectionRange QItemSelectionRange::intersected(const QItemSelectionRange &other) const
230 \since 4.2
231
232 Returns a new selection range containing only the items that are found in
233 both the selection range and the \a other selection range.
234*/
235
236QItemSelectionRange QItemSelectionRange::intersect(const QItemSelectionRange &other) const
237{
238 if (model() == other.model() && parent() == other.parent()) {
239 QModelIndex topLeft = model()->index(qMax(top(), other.top()),
240 qMax(left(), other.left()),
241 other.parent());
242 QModelIndex bottomRight = model()->index(qMin(bottom(), other.bottom()),
243 qMin(right(), other.right()),
244 other.parent());
245 return QItemSelectionRange(topLeft, bottomRight);
246 }
247 return QItemSelectionRange();
248}
249
250/*!
251 \fn bool QItemSelectionRange::operator==(const QItemSelectionRange &other) const
252
253 Returns true if the selection range is exactly the same as the \a other
254 range given; otherwise returns false.
255
256*/
257
258/*!
259 \fn bool QItemSelectionRange::operator!=(const QItemSelectionRange &other) const
260
261 Returns true if the selection range differs from the \a other range given;
262 otherwise returns false.
263
264*/
265
266/*!
267 \fn bool QItemSelectionRange::isValid() const
268
269 Returns true if the selection range is valid; otherwise returns false.
270
271*/
272
273/*
274 \internal
275
276 utility function for getting the indexes from a range
277 it avoid concatenating list and works on one
278 */
279
280static void indexesFromRange(const QItemSelectionRange &range, QModelIndexList &result)
281{
282 if (range.isValid() && range.model()) {
283 for (int column = range.left(); column <= range.right(); ++column) {
284 for (int row = range.top(); row <= range.bottom(); ++row) {
285 QModelIndex index = range.model()->index(row, column, range.parent());
286 Qt::ItemFlags flags = range.model()->flags(index);
287 if ((flags & Qt::ItemIsSelectable) && (flags & Qt::ItemIsEnabled))
288 result.append(index);
289 }
290 }
291 }
292}
293
294/*!
295 Returns the list of model index items stored in the selection.
296*/
297
298QModelIndexList QItemSelectionRange::indexes() const
299{
300 QModelIndexList result;
301 indexesFromRange(*this, result);
302 return result;
303}
304
305/*!
306 \class QItemSelection
307
308 \brief The QItemSelection class manages information about selected items in a model.
309
310 \ingroup model-view
311
312 A QItemSelection describes the items in a model that have been
313 selected by the user. A QItemSelection is basically a list of
314 selection ranges, see QItemSelectionRange. It provides functions for
315 creating and manipulating selections, and selecting a range of items
316 from a model.
317
318 The QItemSelection class is one of the \l{Model/View Classes}
319 and is part of Qt's \l{Model/View Programming}{model/view framework}.
320
321 An item selection can be constructed and initialized to contain a
322 range of items from an existing model. The following example constructs
323 a selection that contains a range of items from the given \c model,
324 beginning at the \c topLeft, and ending at the \c bottomRight.
325
326 \snippet doc/src/snippets/code/src_gui_itemviews_qitemselectionmodel.cpp 0
327
328 An empty item selection can be constructed, and later populated as
329 required. So, if the model is going to be unavailable when we construct
330 the item selection, we can rewrite the above code in the following way:
331
332 \snippet doc/src/snippets/code/src_gui_itemviews_qitemselectionmodel.cpp 1
333
334 QItemSelection saves memory, and avoids unnecessary work, by working with
335 selection ranges rather than recording the model item index for each
336 item in the selection. Generally, an instance of this class will contain
337 a list of non-overlapping selection ranges.
338
339 Use merge() to merge one item selection into another without making
340 overlapping ranges. Use split() to split one selection range into
341 smaller ranges based on a another selection range.
342
343 \sa {Model/View Programming}, QItemSelectionModel
344*/
345
346/*!
347 \fn QItemSelection::QItemSelection()
348
349 Constructs an empty selection.
350*/
351
352/*!
353 Constructs an item selection that extends from the top-left model item,
354 specified by the \a topLeft index, to the bottom-right item, specified
355 by \a bottomRight.
356*/
357QItemSelection::QItemSelection(const QModelIndex &topLeft, const QModelIndex &bottomRight)
358{
359 select(topLeft, bottomRight);
360}
361
362/*!
363 Adds the items in the range that extends from the top-left model
364 item, specified by the \a topLeft index, to the bottom-right item,
365 specified by \a bottomRight to the list.
366
367 \note \a topLeft and \a bottomRight must have the same parent.
368*/
369void QItemSelection::select(const QModelIndex &topLeft, const QModelIndex &bottomRight)
370{
371 if (!topLeft.isValid() || !bottomRight.isValid())
372 return;
373
374 if ((topLeft.model() != bottomRight.model())
375 || topLeft.parent() != bottomRight.parent()) {
376 qWarning("Can't select indexes from different model or with different parents");
377 return;
378 }
379 if (topLeft.row() > bottomRight.row() || topLeft.column() > bottomRight.column()) {
380 int top = qMin(topLeft.row(), bottomRight.row());
381 int bottom = qMax(topLeft.row(), bottomRight.row());
382 int left = qMin(topLeft.column(), bottomRight.column());
383 int right = qMax(topLeft.column(), bottomRight.column());
384 QModelIndex tl = topLeft.sibling(top, left);
385 QModelIndex br = bottomRight.sibling(bottom, right);
386 append(QItemSelectionRange(tl, br));
387 return;
388 }
389 append(QItemSelectionRange(topLeft, bottomRight));
390}
391
392/*!
393 Returns true if the selection contains the given \a index; otherwise
394 returns false.
395*/
396
397bool QItemSelection::contains(const QModelIndex &index) const
398{
399 if (index.flags() & Qt::ItemIsSelectable) {
400 QList<QItemSelectionRange>::const_iterator it = begin();
401 for (; it != end(); ++it)
402 if ((*it).contains(index))
403 return true;
404 }
405 return false;
406}
407
408/*!
409 Returns a list of model indexes that correspond to the selected items.
410*/
411
412QModelIndexList QItemSelection::indexes() const
413{
414 QModelIndexList result;
415 QList<QItemSelectionRange>::const_iterator it = begin();
416 for (; it != end(); ++it)
417 indexesFromRange(*it, result);
418 return result;
419}
420
421/*!
422 Merges the \a other selection with this QItemSelection using the
423 \a command given. This method guarantees that no ranges are overlapping.
424
425 Note that only QItemSelectionModel::Select,
426 QItemSelectionModel::Deselect, and QItemSelectionModel::Toggle are
427 supported.
428
429 \sa split()
430*/
431void QItemSelection::merge(const QItemSelection &other, QItemSelectionModel::SelectionFlags command)
432{
433 if (other.isEmpty() ||
434 !(command & QItemSelectionModel::Select ||
435 command & QItemSelectionModel::Deselect ||
436 command & QItemSelectionModel::Toggle))
437 return;
438
439 QItemSelection newSelection = other;
440 // Collect intersections
441 QItemSelection intersections;
442 QItemSelection::iterator it = newSelection.begin();
443 while (it != newSelection.end()) {
444 if (!(*it).isValid()) {
445 it = newSelection.erase(it);
446 continue;
447 }
448 for (int t = 0; t < count(); ++t) {
449 if ((*it).intersects(at(t)))
450 intersections.append(at(t).intersected(*it));
451 }
452 ++it;
453 }
454
455 // Split the old (and new) ranges using the intersections
456 for (int i = 0; i < intersections.count(); ++i) { // for each intersection
457 for (int t = 0; t < count();) { // splitt each old range
458 if (at(t).intersects(intersections.at(i))) {
459 split(at(t), intersections.at(i), this);
460 removeAt(t);
461 } else {
462 ++t;
463 }
464 }
465 // only split newSelection if Toggle is specified
466 for (int n = 0; (command & QItemSelectionModel::Toggle) && n < newSelection.count();) {
467 if (newSelection.at(n).intersects(intersections.at(i))) {
468 split(newSelection.at(n), intersections.at(i), &newSelection);
469 newSelection.removeAt(n);
470 } else {
471 ++n;
472 }
473 }
474 }
475 // do not add newSelection for Deselect
476 if (!(command & QItemSelectionModel::Deselect))
477 operator+=(newSelection);
478}
479
480/*!
481 Splits the selection \a range using the selection \a other range.
482 Removes all items in \a other from \a range and puts the result in \a result.
483 This can be compared with the semantics of the \e subtract operation of a set.
484 \sa merge()
485*/
486
487void QItemSelection::split(const QItemSelectionRange &range,
488 const QItemSelectionRange &other, QItemSelection *result)
489{
490 if (range.parent() != other.parent())
491 return;
492
493 QModelIndex parent = other.parent();
494 int top = range.top();
495 int left = range.left();
496 int bottom = range.bottom();
497 int right = range.right();
498 int other_top = other.top();
499 int other_left = other.left();
500 int other_bottom = other.bottom();
501 int other_right = other.right();
502 const QAbstractItemModel *model = range.model();
503 Q_ASSERT(model);
504 if (other_top > top) {
505 QModelIndex tl = model->index(top, left, parent);
506 QModelIndex br = model->index(other_top - 1, right, parent);
507 result->append(QItemSelectionRange(tl, br));
508 top = other_top;
509 }
510 if (other_bottom < bottom) {
511 QModelIndex tl = model->index(other_bottom + 1, left, parent);
512 QModelIndex br = model->index(bottom, right, parent);
513 result->append(QItemSelectionRange(tl, br));
514 bottom = other_bottom;
515 }
516 if (other_left > left) {
517 QModelIndex tl = model->index(top, left, parent);
518 QModelIndex br = model->index(bottom, other_left - 1, parent);
519 result->append(QItemSelectionRange(tl, br));
520 left = other_left;
521 }
522 if (other_right < right) {
523 QModelIndex tl = model->index(top, other_right + 1, parent);
524 QModelIndex br = model->index(bottom, right, parent);
525 result->append(QItemSelectionRange(tl, br));
526 right = other_right;
527 }
528}
529
530/*!
531 \internal
532
533 returns a QItemSelection where all ranges have been expanded to:
534 Rows: left: 0 and right: columnCount()-1
535 Columns: top: 0 and bottom: rowCount()-1
536*/
537
538QItemSelection QItemSelectionModelPrivate::expandSelection(const QItemSelection &selection,
539 QItemSelectionModel::SelectionFlags command) const
540{
541 if (selection.isEmpty() && !((command & QItemSelectionModel::Rows) ||
542 (command & QItemSelectionModel::Columns)))
543 return selection;
544
545 QItemSelection expanded;
546 if (command & QItemSelectionModel::Rows) {
547 for (int i = 0; i < selection.count(); ++i) {
548 QModelIndex parent = selection.at(i).parent();
549 int colCount = model->columnCount(parent);
550 QModelIndex tl = model->index(selection.at(i).top(), 0, parent);
551 QModelIndex br = model->index(selection.at(i).bottom(), colCount - 1, parent);
552 //we need to merge because the same row could have already been inserted
553 expanded.merge(QItemSelection(tl, br), QItemSelectionModel::Select);
554 }
555 }
556 if (command & QItemSelectionModel::Columns) {
557 for (int i = 0; i < selection.count(); ++i) {
558 QModelIndex parent = selection.at(i).parent();
559 int rowCount = model->rowCount(parent);
560 QModelIndex tl = model->index(0, selection.at(i).left(), parent);
561 QModelIndex br = model->index(rowCount - 1, selection.at(i).right(), parent);
562 //we need to merge because the same column could have already been inserted
563 expanded.merge(QItemSelection(tl, br), QItemSelectionModel::Select);
564 }
565 }
566 return expanded;
567}
568
569/*!
570 \internal
571*/
572void QItemSelectionModelPrivate::_q_rowsAboutToBeRemoved(const QModelIndex &parent,
573 int start, int end)
574{
575 Q_Q(QItemSelectionModel);
576 finalize();
577
578 // update current index
579 if (currentIndex.isValid() && parent == currentIndex.parent()
580 && currentIndex.row() >= start && currentIndex.row() <= end) {
581 QModelIndex old = currentIndex;
582 if (start > 0) // there are rows left above the change
583 currentIndex = model->index(start - 1, old.column(), parent);
584 else if (model && end < model->rowCount(parent) - 1) // there are rows left below the change
585 currentIndex = model->index(end + 1, old.column(), parent);
586 else // there are no rows left in the table
587 currentIndex = QModelIndex();
588 emit q->currentChanged(currentIndex, old);
589 emit q->currentRowChanged(currentIndex, old);
590 if (currentIndex.column() != old.column())
591 emit q->currentColumnChanged(currentIndex, old);
592 }
593
594 QItemSelection deselected;
595 QItemSelection::iterator it = ranges.begin();
596 while (it != ranges.end()) {
597 if (it->topLeft().parent() != parent) { // Check parents until reaching root or contained in range
598 QModelIndex itParent = it->topLeft().parent();
599 while (itParent.isValid() && itParent.parent() != parent)
600 itParent = itParent.parent();
601
602 if (itParent.isValid() && start <= itParent.row() && itParent.row() <= end) {
603 deselected.append(*it);
604 it = ranges.erase(it);
605 } else {
606 ++it;
607 }
608 } else if (start <= it->bottom() && it->bottom() <= end // Full inclusion
609 && start <= it->top() && it->top() <= end) {
610 deselected.append(*it);
611 it = ranges.erase(it);
612 } else if (start <= it->top() && it->top() <= end) { // Top intersection
613 deselected.append(QItemSelectionRange(it->topLeft(), model->index(end, it->left(), it->parent())));
614 *it = QItemSelectionRange(model->index(end + 1, it->left(), it->parent()), it->bottomRight());
615 ++it;
616 } else if (start <= it->bottom() && it->bottom() <= end) { // Bottom intersection
617 deselected.append(QItemSelectionRange(model->index(start, it->right(), it->parent()), it->bottomRight()));
618 *it = QItemSelectionRange(it->topLeft(), model->index(start - 1, it->right(), it->parent()));
619 ++it;
620 } else {
621 if (it->top() < start && end < it->bottom()) // Middle intersection (do nothing)
622 deselected.append(QItemSelectionRange(model->index(start, it->right(), it->parent()),
623 model->index(end, it->left(), it->parent())));
624 ++it;
625 }
626 }
627
628 if (!deselected.isEmpty())
629 emit q->selectionChanged(QItemSelection(), deselected);
630}
631
632/*!
633 \internal
634*/
635void QItemSelectionModelPrivate::_q_columnsAboutToBeRemoved(const QModelIndex &parent,
636 int start, int end)
637{
638 Q_Q(QItemSelectionModel);
639
640 // update current index
641 if (currentIndex.isValid() && parent == currentIndex.parent()
642 && currentIndex.column() >= start && currentIndex.column() <= end) {
643 QModelIndex old = currentIndex;
644 if (start > 0) // there are columns to the left of the change
645 currentIndex = model->index(old.row(), start - 1, parent);
646 else if (model && end < model->columnCount() - 1) // there are columns to the right of the change
647 currentIndex = model->index(old.row(), end + 1, parent);
648 else // there are no columns left in the table
649 currentIndex = QModelIndex();
650 emit q->currentChanged(currentIndex, old);
651 if (currentIndex.row() != old.row())
652 emit q->currentRowChanged(currentIndex, old);
653 emit q->currentColumnChanged(currentIndex, old);
654 }
655
656 // update selections
657 QModelIndex tl = model->index(0, start, parent);
658 QModelIndex br = model->index(model->rowCount(parent) - 1, end, parent);
659 q->select(QItemSelection(tl, br), QItemSelectionModel::Deselect);
660 finalize();
661}
662
663/*!
664 \internal
665
666 Split selection ranges if columns are about to be inserted in the middle.
667*/
668void QItemSelectionModelPrivate::_q_columnsAboutToBeInserted(const QModelIndex &parent,
669 int start, int end)
670{
671 Q_UNUSED(end);
672 finalize();
673 QList<QItemSelectionRange> split;
674 QList<QItemSelectionRange>::iterator it = ranges.begin();
675 for (; it != ranges.end(); ) {
676 if ((*it).isValid() && (*it).parent() == parent
677 && (*it).left() < start && (*it).right() >= start) {
678 QModelIndex bottomMiddle = model->index((*it).bottom(), start - 1, (*it).parent());
679 QItemSelectionRange left((*it).topLeft(), bottomMiddle);
680 QModelIndex topMiddle = model->index((*it).top(), start, (*it).parent());
681 QItemSelectionRange right(topMiddle, (*it).bottomRight());
682 it = ranges.erase(it);
683 split.append(left);
684 split.append(right);
685 } else {
686 ++it;
687 }
688 }
689 ranges += split;
690}
691
692/*!
693 \internal
694
695 Split selection ranges if rows are about to be inserted in the middle.
696*/
697void QItemSelectionModelPrivate::_q_rowsAboutToBeInserted(const QModelIndex &parent,
698 int start, int end)
699{
700 Q_UNUSED(end);
701 finalize();
702 QList<QItemSelectionRange> split;
703 QList<QItemSelectionRange>::iterator it = ranges.begin();
704 for (; it != ranges.end(); ) {
705 if ((*it).isValid() && (*it).parent() == parent
706 && (*it).top() < start && (*it).bottom() >= start) {
707 QModelIndex middleRight = model->index(start - 1, (*it).right(), (*it).parent());
708 QItemSelectionRange top((*it).topLeft(), middleRight);
709 QModelIndex middleLeft = model->index(start, (*it).left(), (*it).parent());
710 QItemSelectionRange bottom(middleLeft, (*it).bottomRight());
711 it = ranges.erase(it);
712 split.append(top);
713 split.append(bottom);
714 } else {
715 ++it;
716 }
717 }
718 ranges += split;
719}
720
721/*!
722 \internal
723
724 Split selection into individual (persistent) indexes. This is done in
725 preparation for the layoutChanged() signal, where the indexes can be
726 merged again.
727*/
728void QItemSelectionModelPrivate::_q_layoutAboutToBeChanged()
729{
730 savedPersistentIndexes.clear();
731 savedPersistentCurrentIndexes.clear();
732
733 // optimisation for when all indexes are selected
734 // (only if there is lots of items (1000) because this is not entirely correct)
735 if (ranges.isEmpty() && currentSelection.count() == 1) {
736 QItemSelectionRange range = currentSelection.first();
737 QModelIndex parent = range.parent();
738 tableRowCount = model->rowCount(parent);
739 tableColCount = model->columnCount(parent);
740 if (tableRowCount * tableColCount > 1000
741 && range.top() == 0
742 && range.left() == 0
743 && range.bottom() == tableRowCount - 1
744 && range.right() == tableColCount - 1) {
745 tableSelected = true;
746 tableParent = parent;
747 return;
748 }
749 }
750 tableSelected = false;
751
752 QModelIndexList indexes = ranges.indexes();
753 QModelIndexList::const_iterator it;
754 for (it = indexes.constBegin(); it != indexes.constEnd(); ++it)
755 savedPersistentIndexes.append(QPersistentModelIndex(*it));
756 indexes = currentSelection.indexes();
757 for (it = indexes.constBegin(); it != indexes.constEnd(); ++it)
758 savedPersistentCurrentIndexes.append(QPersistentModelIndex(*it));
759}
760
761/*!
762 \internal
763
764 Merges \a indexes into an item selection made up of ranges.
765 Assumes that the indexes are sorted.
766*/
767static QItemSelection mergeIndexes(const QList<QPersistentModelIndex> &indexes)
768{
769 QItemSelection colSpans;
770 // merge columns
771 int i = 0;
772 while (i < indexes.count()) {
773 QModelIndex tl = indexes.at(i);
774 QModelIndex br = tl;
775 while (++i < indexes.count()) {
776 QModelIndex next = indexes.at(i);
777 if ((next.parent() == br.parent())
778 && (next.row() == br.row())
779 && (next.column() == br.column() + 1))
780 br = next;
781 else
782 break;
783 }
784 colSpans.append(QItemSelectionRange(tl, br));
785 }
786 // merge rows
787 QItemSelection rowSpans;
788 i = 0;
789 while (i < colSpans.count()) {
790 QModelIndex tl = colSpans.at(i).topLeft();
791 QModelIndex br = colSpans.at(i).bottomRight();
792 QModelIndex prevTl = tl;
793 while (++i < colSpans.count()) {
794 QModelIndex nextTl = colSpans.at(i).topLeft();
795 QModelIndex nextBr = colSpans.at(i).bottomRight();
796 if ((nextTl.column() == prevTl.column()) && (nextBr.column() == br.column())
797 && (nextTl.row() == prevTl.row() + 1) && (nextBr.row() == br.row() + 1)) {
798 br = nextBr;
799 prevTl = nextTl;
800 } else {
801 break;
802 }
803 }
804 rowSpans.append(QItemSelectionRange(tl, br));
805 }
806 return rowSpans;
807}
808
809/*!
810 \internal
811
812 Merge the selected indexes into selection ranges again.
813*/
814void QItemSelectionModelPrivate::_q_layoutChanged()
815{
816 // special case for when all indexes are selected
817 if (tableSelected && tableColCount == model->columnCount(tableParent)
818 && tableRowCount == model->rowCount(tableParent)) {
819 ranges.clear();
820 currentSelection.clear();
821 int bottom = tableRowCount - 1;
822 int right = tableColCount - 1;
823 QModelIndex tl = model->index(0, 0, tableParent);
824 QModelIndex br = model->index(bottom, right, tableParent);
825 currentSelection << QItemSelectionRange(tl, br);
826 tableParent = QModelIndex();
827 tableSelected = false;
828 return;
829 }
830
831 if (savedPersistentCurrentIndexes.isEmpty() && savedPersistentIndexes.isEmpty()) {
832 // either the selection was actually empty, or we
833 // didn't get the layoutAboutToBeChanged() signal
834 return;
835 }
836 // clear the "old" selection
837 ranges.clear();
838 currentSelection.clear();
839
840 // sort the "new" selection, as preparation for merging
841 qStableSort(savedPersistentIndexes.begin(), savedPersistentIndexes.end());
842 qStableSort(savedPersistentCurrentIndexes.begin(), savedPersistentCurrentIndexes.end());
843
844 // update the selection by merging the individual indexes
845 ranges = mergeIndexes(savedPersistentIndexes);
846 currentSelection = mergeIndexes(savedPersistentCurrentIndexes);
847
848 // release the persistent indexes
849 savedPersistentIndexes.clear();
850 savedPersistentCurrentIndexes.clear();
851}
852
853/*!
854 \class QItemSelectionModel
855
856 \brief The QItemSelectionModel class keeps track of a view's selected items.
857
858 \ingroup model-view
859
860 A QItemSelectionModel keeps track of the selected items in a view, or
861 in several views onto the same model. It also keeps track of the
862 currently selected item in a view.
863
864 The QItemSelectionModel class is one of the \l{Model/View Classes}
865 and is part of Qt's \l{Model/View Programming}{model/view framework}.
866
867 The selected items are stored using ranges. Whenever you want to
868 modify the selected items use select() and provide either a
869 QItemSelection, or a QModelIndex and a QItemSelectionModel::SelectionFlag.
870
871 The QItemSelectionModel takes a two layer approach to selection
872 management, dealing with both selected items that have been committed
873 and items that are part of the current selection. The current
874 selected items are part of the current interactive selection (for
875 example with rubber-band selection or keyboard-shift selections).
876
877 To update the currently selected items, use the bitwise OR of
878 QItemSelectionModel::Current and any of the other SelectionFlags.
879 If you omit the QItemSelectionModel::Current command, a new current
880 selection will be created, and the previous one added to the whole
881 selection. All functions operate on both layers; for example,
882 selectedItems() will return items from both layers.
883
884 \sa {Model/View Programming}, QAbstractItemModel, {Chart Example}
885*/
886
887/*!
888 Constructs a selection model that operates on the specified item \a model.
889*/
890QItemSelectionModel::QItemSelectionModel(QAbstractItemModel *model)
891 : QObject(*new QItemSelectionModelPrivate, model)
892{
893 d_func()->model = model;
894 if (model) {
895 connect(model, SIGNAL(rowsAboutToBeRemoved(QModelIndex,int,int)),
896 this, SLOT(_q_rowsAboutToBeRemoved(QModelIndex,int,int)));
897 connect(model, SIGNAL(columnsAboutToBeRemoved(QModelIndex,int,int)),
898 this, SLOT(_q_columnsAboutToBeRemoved(QModelIndex,int,int)));
899 connect(model, SIGNAL(rowsAboutToBeInserted(QModelIndex,int,int)),
900 this, SLOT(_q_rowsAboutToBeInserted(QModelIndex,int,int)));
901 connect(model, SIGNAL(columnsAboutToBeInserted(QModelIndex,int,int)),
902 this, SLOT(_q_columnsAboutToBeInserted(QModelIndex,int,int)));
903 connect(model, SIGNAL(layoutAboutToBeChanged()),
904 this, SLOT(_q_layoutAboutToBeChanged()));
905 connect(model, SIGNAL(layoutChanged()),
906 this, SLOT(_q_layoutChanged()));
907 }
908}
909
910/*!
911 Constructs a selection model that operates on the specified item \a model with \a parent.
912*/
913QItemSelectionModel::QItemSelectionModel(QAbstractItemModel *model, QObject *parent)
914 : QObject(*new QItemSelectionModelPrivate, parent)
915{
916 d_func()->model = model;
917 if (model) {
918 connect(model, SIGNAL(rowsAboutToBeRemoved(QModelIndex,int,int)),
919 this, SLOT(_q_rowsAboutToBeRemoved(QModelIndex,int,int)));
920 connect(model, SIGNAL(columnsAboutToBeRemoved(QModelIndex,int,int)),
921 this, SLOT(_q_columnsAboutToBeRemoved(QModelIndex,int,int)));
922 connect(model, SIGNAL(rowsAboutToBeInserted(QModelIndex,int,int)),
923 this, SLOT(_q_rowsAboutToBeInserted(QModelIndex,int,int)));
924 connect(model, SIGNAL(columnsAboutToBeInserted(QModelIndex,int,int)),
925 this, SLOT(_q_columnsAboutToBeInserted(QModelIndex,int,int)));
926 connect(model, SIGNAL(layoutAboutToBeChanged()),
927 this, SLOT(_q_layoutAboutToBeChanged()));
928 connect(model, SIGNAL(layoutChanged()),
929 this, SLOT(_q_layoutChanged()));
930 }
931}
932
933/*!
934 \internal
935*/
936QItemSelectionModel::QItemSelectionModel(QItemSelectionModelPrivate &dd, QAbstractItemModel *model)
937 : QObject(dd, model)
938{
939 d_func()->model = model;
940 if (model) {
941 connect(model, SIGNAL(rowsAboutToBeRemoved(QModelIndex,int,int)),
942 this, SLOT(_q_rowsAboutToBeRemoved(QModelIndex,int,int)));
943 connect(model, SIGNAL(columnsAboutToBeRemoved(QModelIndex,int,int)),
944 this, SLOT(_q_columnsAboutToBeRemoved(QModelIndex,int,int)));
945 connect(model, SIGNAL(rowsAboutToBeInserted(QModelIndex,int,int)),
946 this, SLOT(_q_rowsAboutToBeInserted(QModelIndex,int,int)));
947 connect(model, SIGNAL(columnsAboutToBeInserted(QModelIndex,int,int)),
948 this, SLOT(_q_columnsAboutToBeInserted(QModelIndex,int,int)));
949 connect(model, SIGNAL(layoutAboutToBeChanged()),
950 this, SLOT(_q_layoutAboutToBeChanged()));
951 connect(model, SIGNAL(layoutChanged()),
952 this, SLOT(_q_layoutChanged()));
953 }
954}
955
956/*!
957 Destroys the selection model.
958*/
959QItemSelectionModel::~QItemSelectionModel()
960{
961 Q_D(QItemSelectionModel);
962 if (d->model) {
963 disconnect(d->model, SIGNAL(rowsAboutToBeRemoved(QModelIndex,int,int)),
964 this, SLOT(_q_rowsAboutToBeRemoved(QModelIndex,int,int)));
965 disconnect(d->model, SIGNAL(columnsAboutToBeRemoved(QModelIndex,int,int)),
966 this, SLOT(_q_columnsAboutToBeRemoved(QModelIndex,int,int)));
967 disconnect(d->model, SIGNAL(rowsAboutToBeInserted(QModelIndex,int,int)),
968 this, SLOT(_q_rowsAboutToBeInserted(QModelIndex,int,int)));
969 disconnect(d->model, SIGNAL(columnsAboutToBeInserted(QModelIndex,int,int)),
970 this, SLOT(_q_columnsAboutToBeInserted(QModelIndex,int,int)));
971 disconnect(d->model, SIGNAL(layoutAboutToBeChanged()),
972 this, SLOT(_q_layoutAboutToBeChanged()));
973 disconnect(d->model, SIGNAL(layoutChanged()),
974 this, SLOT(_q_layoutChanged()));
975 }
976}
977
978/*!
979 Selects the model item \a index using the specified \a command, and emits
980 selectionChanged().
981
982 \sa QItemSelectionModel::SelectionFlags
983*/
984void QItemSelectionModel::select(const QModelIndex &index, QItemSelectionModel::SelectionFlags command)
985{
986 QItemSelection selection(index, index);
987 select(selection, command);
988}
989
990/*!
991 \fn void QItemSelectionModel::currentChanged(const QModelIndex &current, const QModelIndex &previous)
992
993 This signal is emitted whenever the current item changes. The \a previous
994 model item index is replaced by the \a current index as the selection's
995 current item.
996
997 Note that this signal will not be emitted when the item model is reset.
998
999 \sa currentIndex() setCurrentIndex() selectionChanged()
1000*/
1001
1002/*!
1003 \fn void QItemSelectionModel::currentColumnChanged(const QModelIndex &current, const QModelIndex &previous)
1004
1005 This signal is emitted if the \a current item changes and its column is
1006 different to the column of the \a previous current item.
1007
1008 Note that this signal will not be emitted when the item model is reset.
1009
1010 \sa currentChanged() currentRowChanged() currentIndex() setCurrentIndex()
1011*/
1012
1013/*!
1014 \fn void QItemSelectionModel::currentRowChanged(const QModelIndex &current, const QModelIndex &previous)
1015
1016 This signal is emitted if the \a current item changes and its row is
1017 different to the row of the \a previous current item.
1018
1019 Note that this signal will not be emitted when the item model is reset.
1020
1021 \sa currentChanged() currentColumnChanged() currentIndex() setCurrentIndex()
1022*/
1023
1024/*!
1025 \fn void QItemSelectionModel::selectionChanged(const QItemSelection &selected, const QItemSelection &deselected)
1026
1027 This signal is emitted whenever the selection changes. The change in the
1028 selection is represented as an item selection of \a deselected items and
1029 an item selection of \a selected items.
1030
1031 Note the that the current index changes independently from the selection.
1032 Also note that this signal will not be emitted when the item model is reset.
1033
1034 \sa select() currentChanged()
1035*/
1036
1037/*!
1038 \enum QItemSelectionModel::SelectionFlag
1039
1040 This enum describes the way the selection model will be updated.
1041
1042 \value NoUpdate No selection will be made.
1043 \value Clear The complete selection will be cleared.
1044 \value Select All specified indexes will be selected.
1045 \value Deselect All specified indexes will be deselected.
1046 \value Toggle All specified indexes will be selected or
1047 deselected depending on their current state.
1048 \value Current The current selection will be updated.
1049 \value Rows All indexes will be expanded to span rows.
1050 \value Columns All indexes will be expanded to span columns.
1051 \value SelectCurrent A combination of Select and Current, provided for
1052 convenience.
1053 \value ToggleCurrent A combination of Toggle and Current, provided for
1054 convenience.
1055 \value ClearAndSelect A combination of Clear and Select, provided for
1056 convenience.
1057*/
1058
1059/*!
1060 Selects the item \a selection using the specified \a command, and emits
1061 selectionChanged().
1062
1063 \sa QItemSelectionModel::SelectionFlag
1064*/
1065void QItemSelectionModel::select(const QItemSelection &selection, QItemSelectionModel::SelectionFlags command)
1066{
1067 Q_D(QItemSelectionModel);
1068 if (command == NoUpdate)
1069 return;
1070
1071 // store old selection
1072 QItemSelection sel = selection;
1073 QItemSelection old = d->ranges;
1074 old.merge(d->currentSelection, d->currentCommand);
1075
1076 // expand selection according to SelectionBehavior
1077 if (command & Rows || command & Columns)
1078 sel = d->expandSelection(sel, command);
1079
1080 // clear ranges and currentSelection
1081 if (command & Clear) {
1082 d->ranges.clear();
1083 d->currentSelection.clear();
1084 }
1085
1086 // merge and clear currentSelection if Current was not set (ie. start new currentSelection)
1087 if (!(command & Current))
1088 d->finalize();
1089
1090 // update currentSelection
1091 if (command & Toggle || command & Select || command & Deselect) {
1092 d->currentCommand = command;
1093 d->currentSelection = sel;
1094 }
1095
1096 // generate new selection, compare with old and emit selectionChanged()
1097 QItemSelection newSelection = d->ranges;
1098 newSelection.merge(d->currentSelection, d->currentCommand);
1099 emitSelectionChanged(newSelection, old);
1100}
1101
1102/*!
1103 Clears the selection model. Emits selectionChanged() and currentChanged().
1104*/
1105void QItemSelectionModel::clear()
1106{
1107 Q_D(QItemSelectionModel);
1108 clearSelection();
1109 QModelIndex previous = d->currentIndex;
1110 d->currentIndex = QModelIndex();
1111 if (previous.isValid()) {
1112 emit currentChanged(d->currentIndex, previous);
1113 emit currentRowChanged(d->currentIndex, previous);
1114 emit currentColumnChanged(d->currentIndex, previous);
1115 }
1116}
1117
1118/*!
1119 Clears the selection model. Does not emit any signals.
1120*/
1121void QItemSelectionModel::reset()
1122{
1123 bool block = blockSignals(true);
1124 clear();
1125 blockSignals(block);
1126}
1127
1128/*!
1129 \since 4.2
1130 Clears the selection in the selection model. Emits selectionChanged().
1131*/
1132void QItemSelectionModel::clearSelection()
1133{
1134 Q_D(QItemSelectionModel);
1135 if (d->ranges.count() == 0 && d->currentSelection.count() == 0)
1136 return;
1137 QItemSelection selection = d->ranges;
1138 selection.merge(d->currentSelection, d->currentCommand);
1139 d->ranges.clear();
1140 d->currentSelection.clear();
1141 emit selectionChanged(QItemSelection(), selection);
1142}
1143
1144
1145/*!
1146 Sets the model item \a index to be the current item, and emits
1147 currentChanged(). The current item is used for keyboard navigation and
1148 focus indication; it is independent of any selected items, although a
1149 selected item can also be the current item.
1150
1151 Depending on the specified \a command, the \a index can also become part
1152 of the current selection.
1153 \sa select()
1154*/
1155void QItemSelectionModel::setCurrentIndex(const QModelIndex &index, QItemSelectionModel::SelectionFlags command)
1156{
1157 Q_D(QItemSelectionModel);
1158 if (index == d->currentIndex) {
1159 if (command != NoUpdate)
1160 select(index, command); // select item
1161 return;
1162 }
1163 QPersistentModelIndex previous = d->currentIndex;
1164 d->currentIndex = index; // set current before emitting selection changed below
1165 if (command != NoUpdate)
1166 select(d->currentIndex, command); // select item
1167 emit currentChanged(d->currentIndex, previous);
1168 if (d->currentIndex.row() != previous.row() ||
1169 d->currentIndex.parent() != previous.parent())
1170 emit currentRowChanged(d->currentIndex, previous);
1171 if (d->currentIndex.column() != previous.column() ||
1172 d->currentIndex.parent() != previous.parent())
1173 emit currentColumnChanged(d->currentIndex, previous);
1174}
1175
1176/*!
1177 Returns the model item index for the current item, or an invalid index
1178 if there is no current item.
1179*/
1180QModelIndex QItemSelectionModel::currentIndex() const
1181{
1182 return static_cast<QModelIndex>(d_func()->currentIndex);
1183}
1184
1185/*!
1186 Returns true if the given model item \a index is selected.
1187*/
1188bool QItemSelectionModel::isSelected(const QModelIndex &index) const
1189{
1190 Q_D(const QItemSelectionModel);
1191 if (d->model != index.model() || !index.isValid())
1192 return false;
1193
1194 bool selected = false;
1195 // search model ranges
1196 QList<QItemSelectionRange>::const_iterator it = d->ranges.begin();
1197 for (; it != d->ranges.end(); ++it) {
1198 if ((*it).isValid() && (*it).contains(index)) {
1199 selected = true;
1200 break;
1201 }
1202 }
1203
1204 // check currentSelection
1205 if (d->currentSelection.count()) {
1206 if ((d->currentCommand & Deselect) && selected)
1207 selected = !d->currentSelection.contains(index);
1208 else if (d->currentCommand & Toggle)
1209 selected ^= d->currentSelection.contains(index);
1210 else if ((d->currentCommand & Select) && !selected)
1211 selected = d->currentSelection.contains(index);
1212 }
1213
1214 if (selected) {
1215 Qt::ItemFlags flags = d->model->flags(index);
1216 return (flags & Qt::ItemIsSelectable);
1217 }
1218
1219 return false;
1220}
1221
1222/*!
1223 Returns true if all items are selected in the \a row with the given
1224 \a parent.
1225
1226 Note that this function is usually faster than calling isSelected()
1227 on all items in the same row and that unselectable items are
1228 ignored.
1229*/
1230bool QItemSelectionModel::isRowSelected(int row, const QModelIndex &parent) const
1231{
1232 Q_D(const QItemSelectionModel);
1233 if (parent.isValid() && d->model != parent.model())
1234 return false;
1235
1236 // return false if row exist in currentSelection (Deselect)
1237 if (d->currentCommand & Deselect && d->currentSelection.count()) {
1238 for (int i=0; i<d->currentSelection.count(); ++i) {
1239 if (d->currentSelection.at(i).parent() == parent &&
1240 row >= d->currentSelection.at(i).top() &&
1241 row <= d->currentSelection.at(i).bottom())
1242 return false;
1243 }
1244 }
1245 // return false if ranges in both currentSelection and ranges
1246 // intersect and have the same row contained
1247 if (d->currentCommand & Toggle && d->currentSelection.count()) {
1248 for (int i=0; i<d->currentSelection.count(); ++i)
1249 if (d->currentSelection.at(i).top() <= row &&
1250 d->currentSelection.at(i).bottom() >= row)
1251 for (int j=0; j<d->ranges.count(); ++j)
1252 if (d->ranges.at(j).top() <= row && d->ranges.at(j).bottom() >= row
1253 && d->currentSelection.at(i).intersected(d->ranges.at(j)).isValid())
1254 return false;
1255 }
1256 // add ranges and currentSelection and check through them all
1257 QList<QItemSelectionRange>::const_iterator it;
1258 QList<QItemSelectionRange> joined = d->ranges;
1259 if (d->currentSelection.count())
1260 joined += d->currentSelection;
1261 int colCount = d->model->columnCount(parent);
1262 for (int column = 0; column < colCount; ++column) {
1263 for (it = joined.constBegin(); it != joined.constEnd(); ++it) {
1264 if ((*it).contains(row, column, parent)) {
1265 bool selectable = false;
1266 for (int i = column; !selectable && i <= (*it).right(); ++i) {
1267 Qt::ItemFlags flags = d->model->index(row, i, parent).flags();
1268 selectable = flags & Qt::ItemIsSelectable;
1269 }
1270 if (selectable){
1271 column = qMax(column, (*it).right());
1272 break;
1273 }
1274 }
1275 }
1276 if (it == joined.constEnd())
1277 return false;
1278 }
1279 return colCount > 0; // no columns means no selected items
1280}
1281
1282/*!
1283 Returns true if all items are selected in the \a column with the given
1284 \a parent.
1285
1286 Note that this function is usually faster than calling isSelected()
1287 on all items in the same column and that unselectable items are
1288 ignored.
1289*/
1290bool QItemSelectionModel::isColumnSelected(int column, const QModelIndex &parent) const
1291{
1292 Q_D(const QItemSelectionModel);
1293 if (parent.isValid() && d->model != parent.model())
1294 return false;
1295
1296 // return false if column exist in currentSelection (Deselect)
1297 if (d->currentCommand & Deselect && d->currentSelection.count()) {
1298 for (int i = 0; i < d->currentSelection.count(); ++i) {
1299 if (d->currentSelection.at(i).parent() == parent &&
1300 column >= d->currentSelection.at(i).left() &&
1301 column <= d->currentSelection.at(i).right())
1302 return false;
1303 }
1304 }
1305 // return false if ranges in both currentSelection and the selection model
1306 // intersect and have the same column contained
1307 if (d->currentCommand & Toggle && d->currentSelection.count()) {
1308 for (int i = 0; i < d->currentSelection.count(); ++i) {
1309 if (d->currentSelection.at(i).left() <= column &&
1310 d->currentSelection.at(i).right() >= column) {
1311 for (int j = 0; j < d->ranges.count(); ++j) {
1312 if (d->ranges.at(j).left() <= column && d->ranges.at(j).right() >= column
1313 && d->currentSelection.at(i).intersected(d->ranges.at(j)).isValid()) {
1314 return false;
1315 }
1316 }
1317 }
1318 }
1319 }
1320 // add ranges and currentSelection and check through them all
1321 QList<QItemSelectionRange>::const_iterator it;
1322 QList<QItemSelectionRange> joined = d->ranges;
1323 if (d->currentSelection.count())
1324 joined += d->currentSelection;
1325 int rowCount = d->model->rowCount(parent);
1326 for (int row = 0; row < rowCount; ++row) {
1327 for (it = joined.constBegin(); it != joined.constEnd(); ++it) {
1328 if ((*it).contains(row, column, parent)) {
1329 Qt::ItemFlags flags = d->model->index(row, column, parent).flags();
1330 if ((flags & Qt::ItemIsSelectable) && (flags & Qt::ItemIsEnabled)) {
1331 row = qMax(row, (*it).bottom());
1332 break;
1333 }
1334 }
1335 }
1336 if (it == joined.constEnd())
1337 return false;
1338 }
1339 return rowCount > 0; // no rows means no selected items
1340}
1341
1342/*!
1343 Returns true if there are any items selected in the \a row with the given
1344 \a parent.
1345*/
1346bool QItemSelectionModel::rowIntersectsSelection(int row, const QModelIndex &parent) const
1347{
1348 Q_D(const QItemSelectionModel);
1349 if (parent.isValid() && d->model != parent.model())
1350 return false;
1351
1352 QItemSelection sel = d->ranges;
1353 sel.merge(d->currentSelection, d->currentCommand);
1354 for (int i = 0; i < sel.count(); ++i) {
1355 int top = sel.at(i).top();
1356 int bottom = sel.at(i).bottom();
1357 int left = sel.at(i).left();
1358 int right = sel.at(i).right();
1359 if (top <= row && bottom >= row) {
1360 for (int j = left; j <= right; j++) {
1361 const Qt::ItemFlags flags = d->model->index(row, j, parent).flags();
1362 if ((flags & Qt::ItemIsSelectable) && (flags & Qt::ItemIsEnabled))
1363 return true;
1364 }
1365 }
1366 }
1367
1368 return false;
1369}
1370
1371/*!
1372 Returns true if there are any items selected in the \a column with the given
1373 \a parent.
1374*/
1375bool QItemSelectionModel::columnIntersectsSelection(int column, const QModelIndex &parent) const
1376{
1377 Q_D(const QItemSelectionModel);
1378 if (parent.isValid() && d->model != parent.model())
1379 return false;
1380
1381 QItemSelection sel = d->ranges;
1382 sel.merge(d->currentSelection, d->currentCommand);
1383 for (int i = 0; i < sel.count(); ++i) {
1384 int left = sel.at(i).left();
1385 int right = sel.at(i).right();
1386 int top = sel.at(i).top();
1387 int bottom = sel.at(i).bottom();
1388 if (left <= column && right >= column) {
1389 for (int j = top; j <= bottom; j++) {
1390 const Qt::ItemFlags flags = d->model->index(j, column, parent).flags();
1391 if ((flags & Qt::ItemIsSelectable) && (flags & Qt::ItemIsEnabled))
1392 return true;
1393 }
1394 }
1395 }
1396
1397 return false;
1398}
1399
1400/*!
1401 \since 4.2
1402
1403 Returns true if the selection model contains any selection ranges;
1404 otherwise returns false.
1405*/
1406bool QItemSelectionModel::hasSelection() const
1407{
1408 Q_D(const QItemSelectionModel);
1409 if (d->currentCommand & (Toggle | Deselect)) {
1410 QItemSelection sel = d->ranges;
1411 sel.merge(d->currentSelection, d->currentCommand);
1412 return !sel.isEmpty();
1413 } else {
1414 return !(d->ranges.isEmpty() && d->currentSelection.isEmpty());
1415 }
1416}
1417
1418/*!
1419 Returns a list of all selected model item indexes. The list contains no
1420 duplicates, and is not sorted.
1421*/
1422QModelIndexList QItemSelectionModel::selectedIndexes() const
1423{
1424 Q_D(const QItemSelectionModel);
1425 QItemSelection selected = d->ranges;
1426 selected.merge(d->currentSelection, d->currentCommand);
1427 return selected.indexes();
1428}
1429
1430/*!
1431 \since 4.2
1432 Returns the indexes in the given \a column for the rows where all columns are selected.
1433
1434 \sa selectedIndexes(), selectedColumns()
1435*/
1436
1437QModelIndexList QItemSelectionModel::selectedRows(int column) const
1438{
1439 QModelIndexList indexes;
1440 //the QSet contains pairs of parent modelIndex
1441 //and row number
1442 QSet< QPair<QModelIndex, int> > rowsSeen;
1443
1444 const QItemSelection ranges = selection();
1445 for (int i = 0; i < ranges.count(); ++i) {
1446 const QItemSelectionRange &range = ranges.at(i);
1447 QModelIndex parent = range.parent();
1448 for (int row = range.top(); row <= range.bottom(); row++) {
1449 QPair<QModelIndex, int> rowDef = qMakePair(parent, row);
1450 if (!rowsSeen.contains(rowDef)) {
1451 rowsSeen << rowDef;
1452 if (isRowSelected(row, parent)) {
1453 indexes.append(model()->index(row, column, parent));
1454 }
1455 }
1456 }
1457 }
1458
1459 return indexes;
1460}
1461
1462/*!
1463 \since 4.2
1464 Returns the indexes in the given \a row for columns where all rows are selected.
1465
1466 \sa selectedIndexes(), selectedRows()
1467*/
1468
1469QModelIndexList QItemSelectionModel::selectedColumns(int row) const
1470{
1471 QModelIndexList indexes;
1472 //the QSet contains pairs of parent modelIndex
1473 //and column number
1474 QSet< QPair<QModelIndex, int> > columnsSeen;
1475
1476 const QItemSelection ranges = selection();
1477 for (int i = 0; i < ranges.count(); ++i) {
1478 const QItemSelectionRange &range = ranges.at(i);
1479 QModelIndex parent = range.parent();
1480 for (int column = range.left(); column <= range.right(); column++) {
1481 QPair<QModelIndex, int> columnDef = qMakePair(parent, column);
1482 if (!columnsSeen.contains(columnDef)) {
1483 columnsSeen << columnDef;
1484 if (isColumnSelected(column, parent)) {
1485 indexes.append(model()->index(row, column, parent));
1486 }
1487 }
1488 }
1489 }
1490
1491 return indexes;
1492}
1493
1494/*!
1495 Returns the selection ranges stored in the selection model.
1496*/
1497const QItemSelection QItemSelectionModel::selection() const
1498{
1499 Q_D(const QItemSelectionModel);
1500 QItemSelection selected = d->ranges;
1501 selected.merge(d->currentSelection, d->currentCommand);
1502 int i = 0;
1503 // make sure we have no invalid ranges
1504 // ### should probably be handled more generic somewhere else
1505 while (i<selected.count()) {
1506 if (selected.at(i).isValid())
1507 ++i;
1508 else
1509 (selected.removeAt(i));
1510 }
1511 return selected;
1512}
1513
1514/*!
1515 Returns the item model operated on by the selection model.
1516*/
1517const QAbstractItemModel *QItemSelectionModel::model() const
1518{
1519 return d_func()->model;
1520}
1521
1522/*!
1523 Compares the two selections \a newSelection and \a oldSelection
1524 and emits selectionChanged() with the deselected and selected items.
1525*/
1526void QItemSelectionModel::emitSelectionChanged(const QItemSelection &newSelection,
1527 const QItemSelection &oldSelection)
1528{
1529 // if both selections are empty or equal we return
1530 if ((oldSelection.isEmpty() && newSelection.isEmpty()) ||
1531 oldSelection == newSelection)
1532 return;
1533
1534 // if either selection is empty we do not need to compare
1535 if (oldSelection.isEmpty() || newSelection.isEmpty()) {
1536 emit selectionChanged(newSelection, oldSelection);
1537 return;
1538 }
1539
1540 QItemSelection deselected = oldSelection;
1541 QItemSelection selected = newSelection;
1542
1543 // remove equal ranges
1544 bool advance;
1545 for (int o = 0; o < deselected.count(); ++o) {
1546 advance = true;
1547 for (int s = 0; s < selected.count() && o < deselected.count();) {
1548 if (deselected.at(o) == selected.at(s)) {
1549 deselected.removeAt(o);
1550 selected.removeAt(s);
1551 advance = false;
1552 } else {
1553 ++s;
1554 }
1555 }
1556 if (advance)
1557 ++o;
1558 }
1559
1560 // find intersections
1561 QItemSelection intersections;
1562 for (int o = 0; o < deselected.count(); ++o) {
1563 for (int s = 0; s < selected.count(); ++s) {
1564 if (deselected.at(o).intersects(selected.at(s)))
1565 intersections.append(deselected.at(o).intersected(selected.at(s)));
1566 }
1567 }
1568
1569 // compare remaining ranges with intersections and split them to find deselected and selected
1570 for (int i = 0; i < intersections.count(); ++i) {
1571 // split deselected
1572 for (int o = 0; o < deselected.count();) {
1573 if (deselected.at(o).intersects(intersections.at(i))) {
1574 QItemSelection::split(deselected.at(o), intersections.at(i), &deselected);
1575 deselected.removeAt(o);
1576 } else {
1577 ++o;
1578 }
1579 }
1580 // split selected
1581 for (int s = 0; s < selected.count();) {
1582 if (selected.at(s).intersects(intersections.at(i))) {
1583 QItemSelection::split(selected.at(s), intersections.at(i), &selected);
1584 selected.removeAt(s);
1585 } else {
1586 ++s;
1587 }
1588 }
1589 }
1590
1591 if (!selected.isEmpty() || !deselected.isEmpty())
1592 emit selectionChanged(selected, deselected);
1593}
1594
1595#ifndef QT_NO_DEBUG_STREAM
1596QDebug operator<<(QDebug dbg, const QItemSelectionRange &range)
1597{
1598#ifndef Q_BROKEN_DEBUG_STREAM
1599 dbg.nospace() << "QItemSelectionRange(" << range.topLeft()
1600 << ',' << range.bottomRight() << ')';
1601 return dbg.space();
1602#else
1603 qWarning("This compiler doesn't support streaming QItemSelectionRange to QDebug");
1604 return dbg;
1605 Q_UNUSED(range);
1606#endif
1607}
1608#endif
1609
1610QT_END_NAMESPACE
1611
1612#include "moc_qitemselectionmodel.cpp"
1613
1614#endif // QT_NO_ITEMVIEWS
Note: See TracBrowser for help on using the repository browser.