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

Last change on this file since 855 was 846, checked in by Dmitry A. Kuminov, 14 years ago

trunk: Merged in qt 4.7.2 sources from branches/vendor/nokia/qt.

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