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

Last change on this file since 123 was 2, checked in by Dmitry A. Kuminov, 16 years ago

Initially imported qt-all-opensource-src-4.5.1 from Trolltech.

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