Qt 4.8
qitemselectionmodel.cpp
Go to the documentation of this file.
1 /****************************************************************************
2 **
3 ** Copyright (C) 2014 Digia Plc and/or its subsidiary(-ies).
4 ** Contact: http://www.qt-project.org/legal
5 **
6 ** This file is part of the QtGui module of the Qt Toolkit.
7 **
8 ** $QT_BEGIN_LICENSE:LGPL$
9 ** Commercial License Usage
10 ** Licensees holding valid commercial Qt licenses may use this file in
11 ** accordance with the commercial license agreement provided with the
12 ** Software or, alternatively, in accordance with the terms contained in
13 ** a written agreement between you and Digia. For licensing terms and
14 ** conditions see http://qt.digia.com/licensing. For further information
15 ** use the contact form at http://qt.digia.com/contact-us.
16 **
17 ** GNU Lesser General Public License Usage
18 ** Alternatively, this file may be used under the terms of the GNU Lesser
19 ** General Public License version 2.1 as published by the Free Software
20 ** Foundation and appearing in the file LICENSE.LGPL included in the
21 ** packaging of this file. Please review the following information to
22 ** ensure the GNU Lesser General Public License version 2.1 requirements
23 ** will be met: http://www.gnu.org/licenses/old-licenses/lgpl-2.1.html.
24 **
25 ** In addition, as a special exception, Digia gives you certain additional
26 ** rights. These rights are described in the Digia Qt LGPL Exception
27 ** version 1.1, included in the file LGPL_EXCEPTION.txt in this package.
28 **
29 ** GNU General Public License Usage
30 ** Alternatively, this file may be used under the terms of the GNU
31 ** General Public License version 3.0 as published by the Free Software
32 ** Foundation and appearing in the file LICENSE.GPL included in the
33 ** packaging of this file. Please review the following information to
34 ** ensure the GNU General Public License version 3.0 requirements will be
35 ** met: http://www.gnu.org/copyleft/gpl.html.
36 **
37 **
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 
49 
263 {
264  return (isValid() && other.isValid()
265  && parent() == other.parent()
266  && model() == other.model()
267  && ((top() <= other.top() && bottom() >= other.top())
268  || (top() >= other.top() && top() <= other.bottom()))
269  && ((left() <= other.left() && right() >= other.left())
270  || (left() >= other.left() && left() <= other.right())));
271 }
272 
295 {
296  if (model() == other.model() && parent() == other.parent()) {
297  QModelIndex topLeft = model()->index(qMax(top(), other.top()),
298  qMax(left(), other.left()),
299  other.parent());
301  qMin(right(), other.right()),
302  other.parent());
303  return QItemSelectionRange(topLeft, bottomRight);
304  }
305  return QItemSelectionRange();
306 }
307 
340 /*
341  \internal
342 
343  utility function for getting the indexes from a range
344  it avoid concatenating list and works on one
345  */
346 
347 static void indexesFromRange(const QItemSelectionRange &range, QModelIndexList &result)
348 {
349  if (range.isValid() && range.model()) {
350  for (int column = range.left(); column <= range.right(); ++column) {
351  for (int row = range.top(); row <= range.bottom(); ++row) {
352  QModelIndex index = range.model()->index(row, column, range.parent());
353  Qt::ItemFlags flags = range.model()->flags(index);
354  if ((flags & Qt::ItemIsSelectable) && (flags & Qt::ItemIsEnabled))
355  result.append(index);
356  }
357  }
358  }
359 }
360 
367 {
368  if (!isValid() || !model())
369  return true;
370 
371  for (int column = left(); column <= right(); ++column) {
372  for (int row = top(); row <= bottom(); ++row) {
373  QModelIndex index = model()->index(row, column, parent());
374  Qt::ItemFlags flags = model()->flags(index);
375  if ((flags & Qt::ItemIsSelectable) && (flags & Qt::ItemIsEnabled))
376  return false;
377  }
378  }
379  return true;
380 }
381 
387 {
388  QModelIndexList result;
389  indexesFromRange(*this, result);
390  return result;
391 }
392 
452 {
453  select(topLeft, bottomRight);
454 }
455 
464 {
465  if (!topLeft.isValid() || !bottomRight.isValid())
466  return;
467 
468  if ((topLeft.model() != bottomRight.model())
469  || topLeft.parent() != bottomRight.parent()) {
470  qWarning("Can't select indexes from different model or with different parents");
471  return;
472  }
473  if (topLeft.row() > bottomRight.row() || topLeft.column() > bottomRight.column()) {
474  int top = qMin(topLeft.row(), bottomRight.row());
475  int bottom = qMax(topLeft.row(), bottomRight.row());
476  int left = qMin(topLeft.column(), bottomRight.column());
477  int right = qMax(topLeft.column(), bottomRight.column());
478  QModelIndex tl = topLeft.sibling(top, left);
479  QModelIndex br = bottomRight.sibling(bottom, right);
480  append(QItemSelectionRange(tl, br));
481  return;
482  }
483  append(QItemSelectionRange(topLeft, bottomRight));
484 }
485 
492 {
493  if (index.flags() & Qt::ItemIsSelectable) {
495  for (; it != end(); ++it)
496  if ((*it).contains(index))
497  return true;
498  }
499  return false;
500 }
501 
507 {
508  QModelIndexList result;
510  for (; it != end(); ++it)
511  indexesFromRange(*it, result);
512  return result;
513 }
514 
525 void QItemSelection::merge(const QItemSelection &other, QItemSelectionModel::SelectionFlags command)
526 {
527  if (other.isEmpty() ||
528  !(command & QItemSelectionModel::Select ||
529  command & QItemSelectionModel::Deselect ||
530  command & QItemSelectionModel::Toggle))
531  return;
532 
533  QItemSelection newSelection = other;
534  // Collect intersections
535  QItemSelection intersections;
536  QItemSelection::iterator it = newSelection.begin();
537  while (it != newSelection.end()) {
538  if (!(*it).isValid()) {
539  it = newSelection.erase(it);
540  continue;
541  }
542  for (int t = 0; t < count(); ++t) {
543  if ((*it).intersects(at(t)))
544  intersections.append(at(t).intersected(*it));
545  }
546  ++it;
547  }
548 
549  // Split the old (and new) ranges using the intersections
550  for (int i = 0; i < intersections.count(); ++i) { // for each intersection
551  for (int t = 0; t < count();) { // splitt each old range
552  if (at(t).intersects(intersections.at(i))) {
553  split(at(t), intersections.at(i), this);
554  removeAt(t);
555  } else {
556  ++t;
557  }
558  }
559  // only split newSelection if Toggle is specified
560  for (int n = 0; (command & QItemSelectionModel::Toggle) && n < newSelection.count();) {
561  if (newSelection.at(n).intersects(intersections.at(i))) {
562  split(newSelection.at(n), intersections.at(i), &newSelection);
563  newSelection.removeAt(n);
564  } else {
565  ++n;
566  }
567  }
568  }
569  // do not add newSelection for Deselect
570  if (!(command & QItemSelectionModel::Deselect))
571  operator+=(newSelection);
572 }
573 
582  const QItemSelectionRange &other, QItemSelection *result)
583 {
584  if (range.parent() != other.parent() || range.model() != other.model())
585  return;
586 
587  QModelIndex parent = other.parent();
588  int top = range.top();
589  int left = range.left();
590  int bottom = range.bottom();
591  int right = range.right();
592  int other_top = other.top();
593  int other_left = other.left();
594  int other_bottom = other.bottom();
595  int other_right = other.right();
596  const QAbstractItemModel *model = range.model();
597  Q_ASSERT(model);
598  if (other_top > top) {
599  QModelIndex tl = model->index(top, left, parent);
600  QModelIndex br = model->index(other_top - 1, right, parent);
601  result->append(QItemSelectionRange(tl, br));
602  top = other_top;
603  }
604  if (other_bottom < bottom) {
605  QModelIndex tl = model->index(other_bottom + 1, left, parent);
606  QModelIndex br = model->index(bottom, right, parent);
607  result->append(QItemSelectionRange(tl, br));
608  bottom = other_bottom;
609  }
610  if (other_left > left) {
611  QModelIndex tl = model->index(top, left, parent);
612  QModelIndex br = model->index(bottom, other_left - 1, parent);
613  result->append(QItemSelectionRange(tl, br));
614  left = other_left;
615  }
616  if (other_right < right) {
617  QModelIndex tl = model->index(top, other_right + 1, parent);
618  QModelIndex br = model->index(bottom, right, parent);
619  result->append(QItemSelectionRange(tl, br));
620  right = other_right;
621  }
622 }
623 
624 
626 {
627  this->model = model;
628  if (model) {
630  QObject::connect(model, SIGNAL(rowsAboutToBeRemoved(QModelIndex,int,int)),
631  q, SLOT(_q_rowsAboutToBeRemoved(QModelIndex,int,int)));
632  QObject::connect(model, SIGNAL(columnsAboutToBeRemoved(QModelIndex,int,int)),
633  q, SLOT(_q_columnsAboutToBeRemoved(QModelIndex,int,int)));
634  QObject::connect(model, SIGNAL(rowsAboutToBeInserted(QModelIndex,int,int)),
635  q, SLOT(_q_rowsAboutToBeInserted(QModelIndex,int,int)));
636  QObject::connect(model, SIGNAL(columnsAboutToBeInserted(QModelIndex,int,int)),
637  q, SLOT(_q_columnsAboutToBeInserted(QModelIndex,int,int)));
638  QObject::connect(model, SIGNAL(layoutAboutToBeChanged()),
639  q, SLOT(_q_layoutAboutToBeChanged()));
640  QObject::connect(model, SIGNAL(layoutChanged()),
641  q, SLOT(_q_layoutChanged()));
642  }
643 }
644 
654  QItemSelectionModel::SelectionFlags command) const
655 {
656  if (selection.isEmpty() && !((command & QItemSelectionModel::Rows) ||
657  (command & QItemSelectionModel::Columns)))
658  return selection;
659 
660  QItemSelection expanded;
661  if (command & QItemSelectionModel::Rows) {
662  for (int i = 0; i < selection.count(); ++i) {
663  QModelIndex parent = selection.at(i).parent();
664  int colCount = model->columnCount(parent);
665  QModelIndex tl = model->index(selection.at(i).top(), 0, parent);
666  QModelIndex br = model->index(selection.at(i).bottom(), colCount - 1, parent);
667  //we need to merge because the same row could have already been inserted
669  }
670  }
671  if (command & QItemSelectionModel::Columns) {
672  for (int i = 0; i < selection.count(); ++i) {
673  QModelIndex parent = selection.at(i).parent();
674  int rowCount = model->rowCount(parent);
675  QModelIndex tl = model->index(0, selection.at(i).left(), parent);
676  QModelIndex br = model->index(rowCount - 1, selection.at(i).right(), parent);
677  //we need to merge because the same column could have already been inserted
679  }
680  }
681  return expanded;
682 }
683 
688  int start, int end)
689 {
691  finalize();
692 
693  // update current index
694  if (currentIndex.isValid() && parent == currentIndex.parent()
695  && currentIndex.row() >= start && currentIndex.row() <= end) {
696  QModelIndex old = currentIndex;
697  if (start > 0) // there are rows left above the change
698  currentIndex = model->index(start - 1, old.column(), parent);
699  else if (model && end < model->rowCount(parent) - 1) // there are rows left below the change
700  currentIndex = model->index(end + 1, old.column(), parent);
701  else // there are no rows left in the table
702  currentIndex = QModelIndex();
703  emit q->currentChanged(currentIndex, old);
704  emit q->currentRowChanged(currentIndex, old);
705  if (currentIndex.column() != old.column())
706  emit q->currentColumnChanged(currentIndex, old);
707  }
708 
709  QItemSelection deselected;
710  QItemSelection newParts;
711  QItemSelection::iterator it = ranges.begin();
712  while (it != ranges.end()) {
713  if (it->topLeft().parent() != parent) { // Check parents until reaching root or contained in range
714  QModelIndex itParent = it->topLeft().parent();
715  while (itParent.isValid() && itParent.parent() != parent)
716  itParent = itParent.parent();
717 
718  if (itParent.isValid() && start <= itParent.row() && itParent.row() <= end) {
719  deselected.append(*it);
720  it = ranges.erase(it);
721  } else {
722  ++it;
723  }
724  } else if (start <= it->bottom() && it->bottom() <= end // Full inclusion
725  && start <= it->top() && it->top() <= end) {
726  deselected.append(*it);
727  it = ranges.erase(it);
728  } else if (start <= it->top() && it->top() <= end) { // Top intersection
729  deselected.append(QItemSelectionRange(it->topLeft(), model->index(end, it->left(), it->parent())));
730  *it = QItemSelectionRange(model->index(end + 1, it->left(), it->parent()), it->bottomRight());
731  ++it;
732  } else if (start <= it->bottom() && it->bottom() <= end) { // Bottom intersection
733  deselected.append(QItemSelectionRange(model->index(start, it->right(), it->parent()), it->bottomRight()));
734  *it = QItemSelectionRange(it->topLeft(), model->index(start - 1, it->right(), it->parent()));
735  ++it;
736  } else if (it->top() < start && end < it->bottom()) { // Middle intersection
737  // If the parent contains (1, 2, 3, 4, 5, 6, 7, 8) and [3, 4, 5, 6] is selected,
738  // and [4, 5] is removed, we need to split [3, 4, 5, 6] into [3], [4, 5] and [6].
739  // [4, 5] is appended to deselected, and [3] and [6] remain part of the selection
740  // in ranges.
741  const QItemSelectionRange removedRange(model->index(start, it->right(), it->parent()),
742  model->index(end, it->left(), it->parent()));
743  deselected.append(removedRange);
744  QItemSelection::split(*it, removedRange, &newParts);
745  it = ranges.erase(it);
746  } else
747  ++it;
748  }
749  ranges.append(newParts);
750 
751  if (!deselected.isEmpty())
752  emit q->selectionChanged(QItemSelection(), deselected);
753 }
754 
759  int start, int end)
760 {
762 
763  // update current index
764  if (currentIndex.isValid() && parent == currentIndex.parent()
765  && currentIndex.column() >= start && currentIndex.column() <= end) {
766  QModelIndex old = currentIndex;
767  if (start > 0) // there are columns to the left of the change
768  currentIndex = model->index(old.row(), start - 1, parent);
769  else if (model && end < model->columnCount() - 1) // there are columns to the right of the change
770  currentIndex = model->index(old.row(), end + 1, parent);
771  else // there are no columns left in the table
772  currentIndex = QModelIndex();
773  emit q->currentChanged(currentIndex, old);
774  if (currentIndex.row() != old.row())
775  emit q->currentRowChanged(currentIndex, old);
776  emit q->currentColumnChanged(currentIndex, old);
777  }
778 
779  // update selections
780  QModelIndex tl = model->index(0, start, parent);
781  QModelIndex br = model->index(model->rowCount(parent) - 1, end, parent);
782  q->select(QItemSelection(tl, br), QItemSelectionModel::Deselect);
783  finalize();
784 }
785 
795  int start, int end)
796 {
797  Q_UNUSED(end);
798  finalize();
800  QList<QItemSelectionRange>::iterator it = ranges.begin();
801  for (; it != ranges.end(); ) {
802  if ((*it).isValid() && (*it).parent() == parent
803  && (*it).left() < start && (*it).right() >= start) {
804  QModelIndex bottomMiddle = model->index((*it).bottom(), start - 1, (*it).parent());
805  QItemSelectionRange left((*it).topLeft(), bottomMiddle);
806  QModelIndex topMiddle = model->index((*it).top(), start, (*it).parent());
807  QItemSelectionRange right(topMiddle, (*it).bottomRight());
808  it = ranges.erase(it);
809  split.append(left);
810  split.append(right);
811  } else {
812  ++it;
813  }
814  }
815  ranges += split;
816 }
817 
827  int start, int end)
828 {
829  Q_UNUSED(end);
830  finalize();
832  QList<QItemSelectionRange>::iterator it = ranges.begin();
833  for (; it != ranges.end(); ) {
834  if ((*it).isValid() && (*it).parent() == parent
835  && (*it).top() < start && (*it).bottom() >= start) {
836  QModelIndex middleRight = model->index(start - 1, (*it).right(), (*it).parent());
837  QItemSelectionRange top((*it).topLeft(), middleRight);
838  QModelIndex middleLeft = model->index(start, (*it).left(), (*it).parent());
839  QItemSelectionRange bottom(middleLeft, (*it).bottomRight());
840  it = ranges.erase(it);
841  split.append(top);
842  split.append(bottom);
843  } else {
844  ++it;
845  }
846  }
847  ranges += split;
848 }
849 
861 {
862  savedPersistentIndexes.clear();
863  savedPersistentCurrentIndexes.clear();
864 
865  // optimization for when all indexes are selected
866  // (only if there is lots of items (1000) because this is not entirely correct)
867  if (ranges.isEmpty() && currentSelection.count() == 1) {
868  QItemSelectionRange range = currentSelection.first();
869  QModelIndex parent = range.parent();
870  tableRowCount = model->rowCount(parent);
871  tableColCount = model->columnCount(parent);
872  if (tableRowCount * tableColCount > 1000
873  && range.top() == 0
874  && range.left() == 0
875  && range.bottom() == tableRowCount - 1
876  && range.right() == tableColCount - 1) {
877  tableSelected = true;
878  tableParent = parent;
879  return;
880  }
881  }
882  tableSelected = false;
883 
884  QModelIndexList indexes = ranges.indexes();
886  for (it = indexes.constBegin(); it != indexes.constEnd(); ++it)
887  savedPersistentIndexes.append(QPersistentModelIndex(*it));
888  indexes = currentSelection.indexes();
889  for (it = indexes.constBegin(); it != indexes.constEnd(); ++it)
890  savedPersistentCurrentIndexes.append(QPersistentModelIndex(*it));
891 }
892 
903 {
904  QItemSelection colSpans;
905  // merge columns
906  int i = 0;
907  while (i < indexes.count()) {
908  QModelIndex tl = indexes.at(i);
909  QModelIndex br = tl;
910  while (++i < indexes.count()) {
911  QModelIndex next = indexes.at(i);
912  if ((next.parent() == br.parent())
913  && (next.row() == br.row())
914  && (next.column() == br.column() + 1))
915  br = next;
916  else
917  break;
918  }
919  colSpans.append(QItemSelectionRange(tl, br));
920  }
921  // merge rows
922  QItemSelection rowSpans;
923  i = 0;
924  while (i < colSpans.count()) {
925  QModelIndex tl = colSpans.at(i).topLeft();
926  QModelIndex br = colSpans.at(i).bottomRight();
927  QModelIndex prevTl = tl;
928  while (++i < colSpans.count()) {
929  QModelIndex nextTl = colSpans.at(i).topLeft();
930  QModelIndex nextBr = colSpans.at(i).bottomRight();
931 
932  if (nextTl.parent() != tl.parent())
933  break; // we can't merge selection ranges from different parents
934 
935  if ((nextTl.column() == prevTl.column()) && (nextBr.column() == br.column())
936  && (nextTl.row() == prevTl.row() + 1) && (nextBr.row() == br.row() + 1)) {
937  br = nextBr;
938  prevTl = nextTl;
939  } else {
940  break;
941  }
942  }
943  rowSpans.append(QItemSelectionRange(tl, br));
944  }
945  return rowSpans;
946 }
947 
957 {
958  // special case for when all indexes are selected
959  if (tableSelected && tableColCount == model->columnCount(tableParent)
960  && tableRowCount == model->rowCount(tableParent)) {
961  ranges.clear();
962  currentSelection.clear();
963  int bottom = tableRowCount - 1;
964  int right = tableColCount - 1;
965  QModelIndex tl = model->index(0, 0, tableParent);
966  QModelIndex br = model->index(bottom, right, tableParent);
967  currentSelection << QItemSelectionRange(tl, br);
968  tableParent = QModelIndex();
969  tableSelected = false;
970  return;
971  }
972 
973  if (savedPersistentCurrentIndexes.isEmpty() && savedPersistentIndexes.isEmpty()) {
974  // either the selection was actually empty, or we
975  // didn't get the layoutAboutToBeChanged() signal
976  return;
977  }
978  // clear the "old" selection
979  ranges.clear();
980  currentSelection.clear();
981 
982  // sort the "new" selection, as preparation for merging
983  qStableSort(savedPersistentIndexes.begin(), savedPersistentIndexes.end());
984  qStableSort(savedPersistentCurrentIndexes.begin(), savedPersistentCurrentIndexes.end());
985 
986  // update the selection by merging the individual indexes
987  ranges = mergeIndexes(savedPersistentIndexes);
988  currentSelection = mergeIndexes(savedPersistentCurrentIndexes);
989 
990  // release the persistent indexes
991  savedPersistentIndexes.clear();
992  savedPersistentCurrentIndexes.clear();
993 }
994 
1036  : QObject(*new QItemSelectionModelPrivate, model)
1037 {
1038  d_func()->initModel(model);
1039 }
1040 
1045  : QObject(*new QItemSelectionModelPrivate, parent)
1046 {
1047  d_func()->initModel(model);
1048 }
1049 
1054  : QObject(dd, model)
1055 {
1056  dd.initModel(model);
1057 }
1058 
1063 {
1064 }
1065 
1072 void QItemSelectionModel::select(const QModelIndex &index, QItemSelectionModel::SelectionFlags command)
1073 {
1074  QItemSelection selection(index, index);
1075  select(selection, command);
1076 }
1077 
1168 void QItemSelectionModel::select(const QItemSelection &selection, QItemSelectionModel::SelectionFlags command)
1169 {
1171  if (command == NoUpdate)
1172  return;
1173 
1174  // store old selection
1175  QItemSelection sel = selection;
1176  // If d->ranges is non-empty when the source model is reset the persistent indexes
1177  // it contains will be invalid. We can't clear them in a modelReset slot because that might already
1178  // be too late if another model observer is connected to the same modelReset slot and is invoked first
1179  // it might call select() on this selection model before any such QItemSelectionModelPrivate::_q_modelReset() slot
1180  // is invoked, so it would not be cleared yet. We clear it invalid ranges in it here.
1181  QItemSelection::iterator it = d->ranges.begin();
1182  while (it != d->ranges.end()) {
1183  if (!it->isValid())
1184  it = d->ranges.erase(it);
1185  else
1186  ++it;
1187  }
1188 
1189  QItemSelection old = d->ranges;
1190  old.merge(d->currentSelection, d->currentCommand);
1191 
1192  // expand selection according to SelectionBehavior
1193  if (command & Rows || command & Columns)
1194  sel = d->expandSelection(sel, command);
1195 
1196  // clear ranges and currentSelection
1197  if (command & Clear) {
1198  d->ranges.clear();
1199  d->currentSelection.clear();
1200  }
1201 
1202  // merge and clear currentSelection if Current was not set (ie. start new currentSelection)
1203  if (!(command & Current))
1204  d->finalize();
1205 
1206  // update currentSelection
1207  if (command & Toggle || command & Select || command & Deselect) {
1208  d->currentCommand = command;
1209  d->currentSelection = sel;
1210  }
1211 
1212  // generate new selection, compare with old and emit selectionChanged()
1213  QItemSelection newSelection = d->ranges;
1214  newSelection.merge(d->currentSelection, d->currentCommand);
1215  emitSelectionChanged(newSelection, old);
1216 }
1217 
1222 {
1224  clearSelection();
1225  QModelIndex previous = d->currentIndex;
1226  d->currentIndex = QModelIndex();
1227  if (previous.isValid()) {
1228  emit currentChanged(d->currentIndex, previous);
1229  emit currentRowChanged(d->currentIndex, previous);
1230  emit currentColumnChanged(d->currentIndex, previous);
1231  }
1232 }
1233 
1238 {
1239  bool block = blockSignals(true);
1240  clear();
1241  blockSignals(block);
1242 }
1243 
1252 {
1254  if (d->ranges.count() == 0 && d->currentSelection.count() == 0)
1255  return;
1256 
1258 }
1259 
1260 
1271 void QItemSelectionModel::setCurrentIndex(const QModelIndex &index, QItemSelectionModel::SelectionFlags command)
1272 {
1274  if (index == d->currentIndex) {
1275  if (command != NoUpdate)
1276  select(index, command); // select item
1277  return;
1278  }
1279  QPersistentModelIndex previous = d->currentIndex;
1280  d->currentIndex = index; // set current before emitting selection changed below
1281  if (command != NoUpdate)
1282  select(d->currentIndex, command); // select item
1283  emit currentChanged(d->currentIndex, previous);
1284  if (d->currentIndex.row() != previous.row() ||
1285  d->currentIndex.parent() != previous.parent())
1286  emit currentRowChanged(d->currentIndex, previous);
1287  if (d->currentIndex.column() != previous.column() ||
1288  d->currentIndex.parent() != previous.parent())
1289  emit currentColumnChanged(d->currentIndex, previous);
1290 }
1291 
1297 {
1298  return static_cast<QModelIndex>(d_func()->currentIndex);
1299 }
1300 
1305 {
1306  Q_D(const QItemSelectionModel);
1307  if (d->model != index.model() || !index.isValid())
1308  return false;
1309 
1310  bool selected = false;
1311  // search model ranges
1313  for (; it != d->ranges.end(); ++it) {
1314  if ((*it).isValid() && (*it).contains(index)) {
1315  selected = true;
1316  break;
1317  }
1318  }
1319 
1320  // check currentSelection
1321  if (d->currentSelection.count()) {
1322  if ((d->currentCommand & Deselect) && selected)
1323  selected = !d->currentSelection.contains(index);
1324  else if (d->currentCommand & Toggle)
1325  selected ^= d->currentSelection.contains(index);
1326  else if ((d->currentCommand & Select) && !selected)
1327  selected = d->currentSelection.contains(index);
1328  }
1329 
1330  if (selected) {
1331  Qt::ItemFlags flags = d->model->flags(index);
1332  return (flags & Qt::ItemIsSelectable);
1333  }
1334 
1335  return false;
1336 }
1337 
1347 {
1348  Q_D(const QItemSelectionModel);
1349  if (parent.isValid() && d->model != parent.model())
1350  return false;
1351 
1352  // return false if row exist in currentSelection (Deselect)
1353  if (d->currentCommand & Deselect && d->currentSelection.count()) {
1354  for (int i=0; i<d->currentSelection.count(); ++i) {
1355  if (d->currentSelection.at(i).parent() == parent &&
1356  row >= d->currentSelection.at(i).top() &&
1357  row <= d->currentSelection.at(i).bottom())
1358  return false;
1359  }
1360  }
1361  // return false if ranges in both currentSelection and ranges
1362  // intersect and have the same row contained
1363  if (d->currentCommand & Toggle && d->currentSelection.count()) {
1364  for (int i=0; i<d->currentSelection.count(); ++i)
1365  if (d->currentSelection.at(i).top() <= row &&
1366  d->currentSelection.at(i).bottom() >= row)
1367  for (int j=0; j<d->ranges.count(); ++j)
1368  if (d->ranges.at(j).top() <= row && d->ranges.at(j).bottom() >= row
1369  && d->currentSelection.at(i).intersected(d->ranges.at(j)).isValid())
1370  return false;
1371  }
1372  // add ranges and currentSelection and check through them all
1374  QList<QItemSelectionRange> joined = d->ranges;
1375  if (d->currentSelection.count())
1376  joined += d->currentSelection;
1377  int colCount = d->model->columnCount(parent);
1378  for (int column = 0; column < colCount; ++column) {
1379  for (it = joined.constBegin(); it != joined.constEnd(); ++it) {
1380  if ((*it).contains(row, column, parent)) {
1381  bool selectable = false;
1382  for (int i = column; !selectable && i <= (*it).right(); ++i) {
1383  Qt::ItemFlags flags = d->model->index(row, i, parent).flags();
1384  selectable = flags & Qt::ItemIsSelectable;
1385  }
1386  if (selectable){
1387  column = qMax(column, (*it).right());
1388  break;
1389  }
1390  }
1391  }
1392  if (it == joined.constEnd())
1393  return false;
1394  }
1395  return colCount > 0; // no columns means no selected items
1396 }
1397 
1407 {
1408  Q_D(const QItemSelectionModel);
1409  if (parent.isValid() && d->model != parent.model())
1410  return false;
1411 
1412  // return false if column exist in currentSelection (Deselect)
1413  if (d->currentCommand & Deselect && d->currentSelection.count()) {
1414  for (int i = 0; i < d->currentSelection.count(); ++i) {
1415  if (d->currentSelection.at(i).parent() == parent &&
1416  column >= d->currentSelection.at(i).left() &&
1417  column <= d->currentSelection.at(i).right())
1418  return false;
1419  }
1420  }
1421  // return false if ranges in both currentSelection and the selection model
1422  // intersect and have the same column contained
1423  if (d->currentCommand & Toggle && d->currentSelection.count()) {
1424  for (int i = 0; i < d->currentSelection.count(); ++i) {
1425  if (d->currentSelection.at(i).left() <= column &&
1426  d->currentSelection.at(i).right() >= column) {
1427  for (int j = 0; j < d->ranges.count(); ++j) {
1428  if (d->ranges.at(j).left() <= column && d->ranges.at(j).right() >= column
1429  && d->currentSelection.at(i).intersected(d->ranges.at(j)).isValid()) {
1430  return false;
1431  }
1432  }
1433  }
1434  }
1435  }
1436  // add ranges and currentSelection and check through them all
1438  QList<QItemSelectionRange> joined = d->ranges;
1439  if (d->currentSelection.count())
1440  joined += d->currentSelection;
1441  int rowCount = d->model->rowCount(parent);
1442  for (int row = 0; row < rowCount; ++row) {
1443  for (it = joined.constBegin(); it != joined.constEnd(); ++it) {
1444  if ((*it).contains(row, column, parent)) {
1445  Qt::ItemFlags flags = d->model->index(row, column, parent).flags();
1446  if ((flags & Qt::ItemIsSelectable) && (flags & Qt::ItemIsEnabled)) {
1447  row = qMax(row, (*it).bottom());
1448  break;
1449  }
1450  }
1451  }
1452  if (it == joined.constEnd())
1453  return false;
1454  }
1455  return rowCount > 0; // no rows means no selected items
1456 }
1457 
1463 {
1464  Q_D(const QItemSelectionModel);
1465  if (parent.isValid() && d->model != parent.model())
1466  return false;
1467 
1468  QItemSelection sel = d->ranges;
1469  sel.merge(d->currentSelection, d->currentCommand);
1470  for (int i = 0; i < sel.count(); ++i) {
1471  int top = sel.at(i).top();
1472  int bottom = sel.at(i).bottom();
1473  int left = sel.at(i).left();
1474  int right = sel.at(i).right();
1475  if (top <= row && bottom >= row) {
1476  for (int j = left; j <= right; j++) {
1477  const Qt::ItemFlags flags = d->model->index(row, j, parent).flags();
1478  if ((flags & Qt::ItemIsSelectable) && (flags & Qt::ItemIsEnabled))
1479  return true;
1480  }
1481  }
1482  }
1483 
1484  return false;
1485 }
1486 
1492 {
1493  Q_D(const QItemSelectionModel);
1494  if (parent.isValid() && d->model != parent.model())
1495  return false;
1496 
1497  QItemSelection sel = d->ranges;
1498  sel.merge(d->currentSelection, d->currentCommand);
1499  for (int i = 0; i < sel.count(); ++i) {
1500  int left = sel.at(i).left();
1501  int right = sel.at(i).right();
1502  int top = sel.at(i).top();
1503  int bottom = sel.at(i).bottom();
1504  if (left <= column && right >= column) {
1505  for (int j = top; j <= bottom; j++) {
1506  const Qt::ItemFlags flags = d->model->index(j, column, parent).flags();
1507  if ((flags & Qt::ItemIsSelectable) && (flags & Qt::ItemIsEnabled))
1508  return true;
1509  }
1510  }
1511  }
1512 
1513  return false;
1514 }
1515 
1526 {
1527  Q_D(const QItemSelectionModel);
1528  if (d->currentCommand & (Toggle | Deselect)) {
1529  QItemSelection sel = d->ranges;
1530  sel.merge(d->currentSelection, d->currentCommand);
1531  return !sel.isEmpty();
1532  } else {
1533  return !(d->ranges.isEmpty() && d->currentSelection.isEmpty());
1534  }
1535 }
1536 
1542 {
1543  Q_D(const QItemSelectionModel);
1544  QItemSelection selected = d->ranges;
1545  selected.merge(d->currentSelection, d->currentCommand);
1546  return selected.indexes();
1547 }
1548 
1560 {
1561  QModelIndexList indexes;
1562  //the QSet contains pairs of parent modelIndex
1563  //and row number
1564  QSet< QPair<QModelIndex, int> > rowsSeen;
1565 
1566  const QItemSelection ranges = selection();
1567  for (int i = 0; i < ranges.count(); ++i) {
1568  const QItemSelectionRange &range = ranges.at(i);
1569  QModelIndex parent = range.parent();
1570  for (int row = range.top(); row <= range.bottom(); row++) {
1571  QPair<QModelIndex, int> rowDef = qMakePair(parent, row);
1572  if (!rowsSeen.contains(rowDef)) {
1573  rowsSeen << rowDef;
1574  if (isRowSelected(row, parent)) {
1575  indexes.append(model()->index(row, column, parent));
1576  }
1577  }
1578  }
1579  }
1580 
1581  return indexes;
1582 }
1583 
1595 {
1596  QModelIndexList indexes;
1597  //the QSet contains pairs of parent modelIndex
1598  //and column number
1599  QSet< QPair<QModelIndex, int> > columnsSeen;
1600 
1601  const QItemSelection ranges = selection();
1602  for (int i = 0; i < ranges.count(); ++i) {
1603  const QItemSelectionRange &range = ranges.at(i);
1604  QModelIndex parent = range.parent();
1605  for (int column = range.left(); column <= range.right(); column++) {
1606  QPair<QModelIndex, int> columnDef = qMakePair(parent, column);
1607  if (!columnsSeen.contains(columnDef)) {
1608  columnsSeen << columnDef;
1609  if (isColumnSelected(column, parent)) {
1610  indexes.append(model()->index(row, column, parent));
1611  }
1612  }
1613  }
1614  }
1615 
1616  return indexes;
1617 }
1618 
1623 {
1624  Q_D(const QItemSelectionModel);
1625  QItemSelection selected = d->ranges;
1626  selected.merge(d->currentSelection, d->currentCommand);
1627  int i = 0;
1628  // make sure we have no invalid ranges
1629  // ### should probably be handled more generic somewhere else
1630  while (i<selected.count()) {
1631  if (selected.at(i).isValid())
1632  ++i;
1633  else
1634  (selected.removeAt(i));
1635  }
1636  return selected;
1637 }
1638 
1643 {
1644  return d_func()->model;
1645 }
1646 
1652  const QItemSelection &oldSelection)
1653 {
1654  // if both selections are empty or equal we return
1655  if ((oldSelection.isEmpty() && newSelection.isEmpty()) ||
1656  oldSelection == newSelection)
1657  return;
1658 
1659  // if either selection is empty we do not need to compare
1660  if (oldSelection.isEmpty() || newSelection.isEmpty()) {
1661  emit selectionChanged(newSelection, oldSelection);
1662  return;
1663  }
1664 
1665  QItemSelection deselected = oldSelection;
1666  QItemSelection selected = newSelection;
1667 
1668  // remove equal ranges
1669  bool advance;
1670  for (int o = 0; o < deselected.count(); ++o) {
1671  advance = true;
1672  for (int s = 0; s < selected.count() && o < deselected.count();) {
1673  if (deselected.at(o) == selected.at(s)) {
1674  deselected.removeAt(o);
1675  selected.removeAt(s);
1676  advance = false;
1677  } else {
1678  ++s;
1679  }
1680  }
1681  if (advance)
1682  ++o;
1683  }
1684 
1685  // find intersections
1686  QItemSelection intersections;
1687  for (int o = 0; o < deselected.count(); ++o) {
1688  for (int s = 0; s < selected.count(); ++s) {
1689  if (deselected.at(o).intersects(selected.at(s)))
1690  intersections.append(deselected.at(o).intersected(selected.at(s)));
1691  }
1692  }
1693 
1694  // compare remaining ranges with intersections and split them to find deselected and selected
1695  for (int i = 0; i < intersections.count(); ++i) {
1696  // split deselected
1697  for (int o = 0; o < deselected.count();) {
1698  if (deselected.at(o).intersects(intersections.at(i))) {
1699  QItemSelection::split(deselected.at(o), intersections.at(i), &deselected);
1700  deselected.removeAt(o);
1701  } else {
1702  ++o;
1703  }
1704  }
1705  // split selected
1706  for (int s = 0; s < selected.count();) {
1707  if (selected.at(s).intersects(intersections.at(i))) {
1708  QItemSelection::split(selected.at(s), intersections.at(i), &selected);
1709  selected.removeAt(s);
1710  } else {
1711  ++s;
1712  }
1713  }
1714  }
1715 
1716  if (!selected.isEmpty() || !deselected.isEmpty())
1717  emit selectionChanged(selected, deselected);
1718 }
1719 
1720 #ifndef QT_NO_DEBUG_STREAM
1722 {
1723 #ifndef Q_BROKEN_DEBUG_STREAM
1724  dbg.nospace() << "QItemSelectionRange(" << range.topLeft()
1725  << ',' << range.bottomRight() << ')';
1726  return dbg.space();
1727 #else
1728  qWarning("This compiler doesn't support streaming QItemSelectionRange to QDebug");
1729  return dbg;
1730  Q_UNUSED(range);
1731 #endif
1732 }
1733 #endif
1734 
1736 
1737 #include "moc_qitemselectionmodel.cpp"
1738 
1739 #endif // QT_NO_ITEMVIEWS
QModelIndexList selectedRows(int column=0) const
Returns the indexes in the given column for the rows where all columns are selected.
The QDebug class provides an output stream for debugging information.
Definition: qdebug.h:62
double d
Definition: qnumeric_p.h:62
virtual int columnCount(const QModelIndex &parent=QModelIndex()) const =0
Returns the number of columns for the children of the given parent.
int row() const
Returns the row this persistent model index refers to.
bool blockSignals(bool b)
If block is true, signals emitted by this object are blocked (i.e., emitting a signal will not invoke...
Definition: qobject.cpp:1406
The QItemSelectionModel class keeps track of a view&#39;s selected items.
void merge(const QItemSelection &other, QItemSelectionModel::SelectionFlags command)
Merges the other selection with this QItemSelection using the command given.
int left() const
Returns the column index corresponding to the leftmost selected column in the selection range...
virtual void clear()
Clears the selection model.
Q_DECL_CONSTEXPR const T & qMin(const T &a, const T &b)
Definition: qglobal.h:1215
#define QT_END_NAMESPACE
This macro expands to.
Definition: qglobal.h:90
QModelIndex bottomRight() const
Returns the index for the item located at the bottom-right corner of the selection range...
QModelIndex sibling(int row, int column) const
Returns the sibling at row and column.
#define it(className, varName)
void _q_columnsAboutToBeRemoved(const QModelIndex &parent, int start, int end)
bool isColumnSelected(int column, const QModelIndex &parent) const
Returns true if all items are selected in the column with the given parent.
QDebug & nospace()
Clears the stream&#39;s internal flag that records whether the last character was a space and returns a r...
Definition: qdebug.h:92
bool isValid() const
Returns true if the selection range is valid; otherwise returns false.
int column() const
Returns the column this persistent model index refers to.
virtual int rowCount(const QModelIndex &parent=QModelIndex()) const =0
Returns the number of rows under the given parent.
virtual void reset()
Clears the selection model.
#define at(className, varName)
QModelIndexList selectedIndexes() const
Returns a list of all selected model item indexes.
static QItemSelection mergeIndexes(const QList< QPersistentModelIndex > &indexes)
Merges indexes into an item selection made up of ranges.
#define SLOT(a)
Definition: qobjectdefs.h:226
void clearSelection()
Clears the selection in the selection model.
QPersistentModelIndex br
QDebug operator<<(QDebug dbg, const QItemSelectionRange &range)
iterator begin()
Returns an STL-style iterator pointing to the first item in the list.
Definition: qlist.h:267
int select(int, fd_set *, fd_set *, fd_set *, struct timeval *)
The QItemSelectionRange class manages information about a range of selected items in a model...
const QItemSelection selection() const
Returns the selection ranges stored in the selection model.
const_iterator constBegin() const
Returns a const STL-style iterator pointing to the first item in the list.
Definition: qlist.h:269
int bottom() const
Returns the row index corresponding to the lowermost selected row in the selection range...
int count(const T &t) const
Returns the number of occurrences of value in the list.
Definition: qlist.h:891
int right() const
Returns the column index corresponding to the rightmost selected column in the selection range...
void emitSelectionChanged(const QItemSelection &newSelection, const QItemSelection &oldSelection)
Compares the two selections newSelection and oldSelection and emits selectionChanged() with the desel...
bool isEmpty() const
Returns true if the selection range contains no selectable item.
#define Q_ASSERT(cond)
Definition: qglobal.h:1823
The QObject class is the base class of all Qt objects.
Definition: qobject.h:111
#define Q_D(Class)
Definition: qglobal.h:2482
void _q_columnsAboutToBeInserted(const QModelIndex &parent, int start, int end)
Split selection ranges if columns are about to be inserted in the middle.
QModelIndex parent() const
Returns the parent model item index of the items in the selection range.
QModelIndex parent() const
Returns the parent of the model index, or QModelIndex() if it has no parent.
Q_CORE_EXPORT QTextStream & right(QTextStream &s)
bool rowIntersectsSelection(int row, const QModelIndex &parent) const
Returns true if there are any items selected in the row with the given parent.
Q_DECL_CONSTEXPR const T & qMax(const T &a, const T &b)
Definition: qglobal.h:1217
bool isEmpty() const
Returns true if the list contains no items; otherwise returns false.
Definition: qlist.h:152
#define Q_Q(Class)
Definition: qglobal.h:2483
void setCurrentIndex(const QModelIndex &index, QItemSelectionModel::SelectionFlags command)
Sets the model item index to be the current item, and emits currentChanged().
QModelIndex topLeft() const
Returns the index for the item located at the top-left corner of the selection range.
virtual Qt::ItemFlags flags(const QModelIndex &index) const
Returns the item flags for the given index.
virtual QModelIndex index(int row, int column, const QModelIndex &parent=QModelIndex()) const =0
Returns the index of the item in the model specified by the given row, column and parent index...
#define SIGNAL(a)
Definition: qobjectdefs.h:227
friend class const_iterator
Definition: qlist.h:264
void _q_rowsAboutToBeInserted(const QModelIndex &parent, int start, int end)
Split selection ranges if rows are about to be inserted in the middle.
void append(const T &t)
Inserts value at the end of the list.
Definition: qlist.h:507
#define QT_BEGIN_NAMESPACE
This macro expands to.
Definition: qglobal.h:89
QPersistentModelIndex tl
void currentRowChanged(const QModelIndex &current, const QModelIndex &previous)
This signal is emitted if the current item changes and its row is different to the row of the previou...
bool contains(const T &value) const
Definition: qset.h:91
static bool connect(const QObject *sender, const char *signal, const QObject *receiver, const char *member, Qt::ConnectionType=Qt::AutoConnection)
Creates a connection of the given type from the signal in the sender object to the method in the rece...
Definition: qobject.cpp:2580
QModelIndexList indexes() const
Returns the list of model index items stored in the selection.
bool isSelected(const QModelIndex &index) const
Returns true if the given model item index is selected.
iterator end()
Returns an STL-style iterator pointing to the imaginary item after the last item in the list...
Definition: qlist.h:270
int row() const
Returns the row this model index refers to.
const T & at(int i) const
Returns the item at index position i in the list.
Definition: qlist.h:468
#define emit
Definition: qobjectdefs.h:76
const QAbstractItemModel * model() const
Returns a pointer to the model containing the item that this index refers to.
QModelIndex parent() const
Returns the parent QModelIndex for this persistent index, or an invalid QModelIndex if it has no pare...
Q_CORE_EXPORT void qWarning(const char *,...)
static void indexesFromRange(const QItemSelectionRange &range, QModelIndexList &result)
static void split(QT_FT_Vector *b)
friend class iterator
Definition: qlist.h:226
void selectionChanged(const QItemSelection &selected, const QItemSelection &deselected)
This signal is emitted whenever the selection changes.
QModelIndex currentIndex() const
Returns the model item index for the current item, or an invalid index if there is no current item...
void _q_layoutChanged()
Merge the selected indexes into selection ranges again.
void select(const QModelIndex &topLeft, const QModelIndex &bottomRight)
Adds the items in the range that extends from the top-left model item, specified by the topLeft index...
QItemSelection()
Constructs an empty selection.
bool isValid() const
Returns true if this model index is valid; otherwise returns false.
The QAbstractItemModel class provides the abstract interface for item model classes.
Qt::ItemFlags flags() const
Returns the flags for the item referred to by the index.
void qStableSort(RandomAccessIterator start, RandomAccessIterator end)
Definition: qalgorithms.h:202
QItemSelection expandSelection(const QItemSelection &selection, QItemSelectionModel::SelectionFlags command) const
bool hasSelection() const
Returns true if the selection model contains any selection ranges; otherwise returns false...
virtual ~QItemSelectionModel()
Destroys the selection model.
iterator erase(iterator pos)
Removes the item associated with the iterator pos from the list, and returns an iterator to the next ...
Definition: qlist.h:464
bool columnIntersectsSelection(int column, const QModelIndex &parent) const
Returns true if there are any items selected in the column with the given parent. ...
The QItemSelection class manages information about selected items in a model.
QModelIndexList selectedColumns(int row=0) const
Returns the indexes in the given row for columns where all rows are selected.
QItemSelectionRange()
Constructs an empty selection range.
The QPersistentModelIndex class is used to locate data in a data model.
virtual void select(const QModelIndex &index, QItemSelectionModel::SelectionFlags command)
Selects the model item index using the specified command, and emits selectionChanged().
QObject * parent() const
Returns a pointer to the parent object.
Definition: qobject.h:273
Q_OUTOFLINE_TEMPLATE QPair< T1, T2 > qMakePair(const T1 &x, const T2 &y)
Definition: qpair.h:102
The QModelIndex class is used to locate data in a data model.
void currentChanged(const QModelIndex &current, const QModelIndex &previous)
This signal is emitted whenever the current item changes.
void currentColumnChanged(const QModelIndex &current, const QModelIndex &previous)
This signal is emitted if the current item changes and its column is different to the column of the p...
const QAbstractItemModel * model() const
Returns the item model operated on by the selection model.
QItemSelectionRange intersect(const QItemSelectionRange &other) const
Use intersected(other) instead.
QItemSelectionRange intersected(const QItemSelectionRange &other) const
Returns a new selection range containing only the items that are found in both the selection range an...
quint16 index
void _q_layoutAboutToBeChanged()
Split selection into individual (persistent) indexes.
int top() const
Returns the row index corresponding to the uppermost selected row in the selection range...
bool intersects(const QItemSelectionRange &other) const
Returns true if this selection range intersects (overlaps with) the other range given; otherwise retu...
static void split(const QItemSelectionRange &range, const QItemSelectionRange &other, QItemSelection *result)
Splits the selection range using the selection other range.
void initModel(QAbstractItemModel *model)
QModelIndexList indexes() const
Returns a list of model indexes that correspond to the selected items.
bool contains(const QModelIndex &index) const
Returns true if the selection contains the given index; otherwise returns false.
timeval & operator+=(timeval &t1, const timeval &t2)
Definition: qcore_unix_p.h:120
QItemSelectionModel(QAbstractItemModel *model)
Constructs a selection model that operates on the specified item model.
static const KeyPair *const end
QDebug & space()
Writes a space character to the debug stream and returns a reference to the stream.
Definition: qdebug.h:91
Q_CORE_EXPORT QTextStream & left(QTextStream &s)
#define Q_UNUSED(x)
Indicates to the compiler that the parameter with the specified name is not used in the body of a fun...
Definition: qglobal.h:1729
bool isRowSelected(int row, const QModelIndex &parent) const
Returns true if all items are selected in the row with the given parent.
int column() const
Returns the column this model index refers to.
void _q_rowsAboutToBeRemoved(const QModelIndex &parent, int start, int end)
const_iterator constEnd() const
Returns a const STL-style iterator pointing to the imaginary item after the last item in the list...
Definition: qlist.h:272
const QAbstractItemModel * model() const
Returns the model that the items in the selection range belong to.
void removeAt(int i)
Removes the item at index position i.
Definition: qlist.h:480