diff --git a/src/widgets/itemviews/qtableview.cpp b/src/widgets/itemviews/qtableview.cpp index d1793a3049..c3138860fa 100644 --- a/src/widgets/itemviews/qtableview.cpp +++ b/src/widgets/itemviews/qtableview.cpp @@ -2004,6 +2004,9 @@ void QTableView::setSelection(const QRect &rect, QItemSelectionModel::SelectionF if (d->hasSpans()) { bool expanded; + // when the current selection does not intersect with any spans of merged cells, + // the range of selected cells must be the same as if there were no merged cells + bool intersectsSpan = false; int top = qMin(d->visualRow(tl.row()), d->visualRow(br.row())); int left = qMin(d->visualColumn(tl.column()), d->visualColumn(br.column())); int bottom = qMax(d->visualRow(tl.row()), d->visualRow(br.row())); @@ -2018,6 +2021,7 @@ void QTableView::setSelection(const QRect &rect, QItemSelectionModel::SelectionF int r = d->visualColumn(d->columnSpanEndLogical(span.left(), span.width())); if ((t > bottom) || (l > right) || (top > b) || (left > r)) continue; // no intersect + intersectsSpan = true; if (t < top) { top = t; expanded = true; @@ -2038,14 +2042,20 @@ void QTableView::setSelection(const QRect &rect, QItemSelectionModel::SelectionF break; } } while (expanded); - selection.reserve((right - left + 1) * (bottom - top + 1)); - for (int horizontal = left; horizontal <= right; ++horizontal) { - int column = d->logicalColumn(horizontal); - for (int vertical = top; vertical <= bottom; ++vertical) { - int row = d->logicalRow(vertical); - QModelIndex index = d->model->index(row, column, d->root); - selection.append(QItemSelectionRange(index)); + if (intersectsSpan) { + selection.reserve((right - left + 1) * (bottom - top + 1)); + for (int horizontal = left; horizontal <= right; ++horizontal) { + int column = d->logicalColumn(horizontal); + for (int vertical = top; vertical <= bottom; ++vertical) { + int row = d->logicalRow(vertical); + QModelIndex index = d->model->index(row, column, d->root); + selection.append(QItemSelectionRange(index)); + } } + } else { + QItemSelectionRange range(tl, br); + if (!range.isEmpty()) + selection.append(range); } } else if (verticalMoved && horizontalMoved) { int top = d->visualRow(tl.row()); diff --git a/tests/auto/widgets/itemviews/qtablewidget/tst_qtablewidget.cpp b/tests/auto/widgets/itemviews/qtablewidget/tst_qtablewidget.cpp index 23e69b2190..aa56376c4a 100644 --- a/tests/auto/widgets/itemviews/qtablewidget/tst_qtablewidget.cpp +++ b/tests/auto/widgets/itemviews/qtablewidget/tst_qtablewidget.cpp @@ -61,6 +61,8 @@ private slots: void takeItem(); void selectedItems_data(); void selectedItems(); + void selectedSpannedCells_data(); + void selectedSpannedCells(); void removeRow_data(); void removeRow(); void removeColumn_data(); @@ -585,6 +587,75 @@ void tst_QTableWidget::selectedItems() } } +void tst_QTableWidget::selectedSpannedCells_data() +{ + QTest::addColumn("spannedCells"); // in cells, not pixels + QTest::addColumn("selectionStartCell"); + QTest::addColumn("selectionEndCell"); + QTest::addColumn("expectedSelectionRangeCount"); + QTest::addColumn("expectedFirstSelectionRange"); + + QTest::newRow("merge 2 cells in column, select adjacent left") + << QRect(1, 2, 1, 2) << QPoint(0, 1) << QPoint(0, 3) + << 1 << QTableWidgetSelectionRange(1, 0, 3, 0); + + QTest::newRow("merge 2 cells in column, select those and one more") + << QRect(1, 2, 1, 2) << QPoint(1, 1) << QPoint(1, 3) + << 3 << QTableWidgetSelectionRange(1, 1, 1, 1); + + QTest::newRow("merge 2 cells in column, select rows above") + << QRect(1, 2, 1, 2) << QPoint(0, 0) << QPoint(3, 1) + << 1 << QTableWidgetSelectionRange(0, 0, 1, 3); + + QTest::newRow("merge 4 cells in column, select adjacent right") + << QRect(1, 0, 1, 4) << QPoint(2, 0) << QPoint(3, 3) + << 1 << QTableWidgetSelectionRange(0, 2, 3, 3); + + QTest::newRow("merge 3 cells in row, select those and one more") + << QRect(0, 1, 3, 1) << QPoint(0, 1) << QPoint(3, 1) + << 4 << QTableWidgetSelectionRange(1, 0, 1, 0); + + QTest::newRow("merge 3 cells in row, select adjacent to right") + << QRect(0, 1, 3, 1) << QPoint(3, 0) << QPoint(3, 2) + << 1 << QTableWidgetSelectionRange(0, 3, 2, 3); + + QTest::newRow("merge 3 cells in row, select adjacent above") + << QRect(0, 2, 3, 2) << QPoint(0, 1) << QPoint(2, 1) + << 1 << QTableWidgetSelectionRange(1, 0, 1, 2); +} + +void tst_QTableWidget::selectedSpannedCells() // QTBUG-255 +{ + QFETCH(QRect, spannedCells); + QFETCH(QPoint, selectionStartCell); + QFETCH(QPoint, selectionEndCell); + QFETCH(int, expectedSelectionRangeCount); + QFETCH(const QTableWidgetSelectionRange, expectedFirstSelectionRange); + + QTableWidget testWidget(4, 4); + testWidget.resize(600, 200); + testWidget.show(); + + // create and set items + for (int c = 0; c < 4; ++c) { + for (int r = 0; r < 4; ++r) + testWidget.setItem(r, c, new QTableWidgetItem(QString("Item %1 %2").arg(c).arg(r))); + } + + // merge some cells + testWidget.setSpan(spannedCells.top(), spannedCells.left(), spannedCells.height(), spannedCells.width()); + + // click one cell and shift-click another, to select a range + QTest::mouseClick(testWidget.viewport(), Qt::LeftButton, Qt::NoModifier, + testWidget.visualRect(testWidget.model()->index(selectionStartCell.y(), selectionStartCell.x())).center()); + QTest::mouseClick(testWidget.viewport(), Qt::LeftButton, Qt::ShiftModifier, + testWidget.visualRect(testWidget.model()->index(selectionEndCell.y(), selectionEndCell.x())).center()); + + auto ranges = testWidget.selectedRanges(); + QCOMPARE(ranges.count(), expectedSelectionRangeCount); + QCOMPARE(ranges.first(), expectedFirstSelectionRange); +} + void tst_QTableWidget::removeRow_data() { QTest::addColumn("rowCount");