Uploaded image for project: 'Qt'
  1. Qt
  2. QTBUG-3537

In QTableView, setting the span inside another span leads to undefined behavior

    XMLWordPrintable

Details

    Description

      With QTableView, setting the span inside another span leads to strange painting and strange selection model.

      The following example show some of the problems (select the cells to see some problems):

      #include <QtGui>
      #include <QtCore>

      int main(int argc, char **argv){
      QApplication app(argc, argv);

      QStandardItemModel model;

      for(uint row = 0; row<4; ++row){
      for(uint column = 0; column < 3; ++column)

      { model.setItem( row, column, new QStandardItem(QString("Cell row %1 column %2").arg(row).arg(column)) ); }

      }

      QTableView view;
      view.setModel( &model );
      // case of overlapping span
      view.setSpan( 0, 0, 1, 2 );
      view.setSpan( 0, 1, 1, 2 );

      // case of null span inside a span
      view.setSpan( 1, 1, 1, 2 );
      view.setSpan( 1, 2, 1, 1 );

      // case of a new span extending a previous one
      view.setSpan( 2, 1, 1, 2 );
      view.setSpan( 2, 0, 1, 3 );

      // previous example, degenerated
      view.setSpan( 3, 1, 1, 2 );
      view.setSpan( 3, 0, 1, 2 );

      view.show();

      return app.exec();
      }

      Here is a test case associated, assuming that overlapping span should be forbidden and that the last span defined have precedence if it is not included in an existing span (another possibility is to assume that the last span always have precedence):

      #include <QtTest/QtTest>
      #include <QStandardItemModel>
      #include <QStandardItem>
      #include <QTableView>

      class Test : public QObject{
      Q_OBJECT

      private slots:
      void testSpan_data()

      { QTest::addColumn<int>("span1Row"); QTest::addColumn<int>("span1Column"); QTest::addColumn<int>("span1Rowspan"); QTest::addColumn<int>("span1Colspan"); QTest::addColumn<int>("span2Row"); QTest::addColumn<int>("span2Column"); QTest::addColumn<int>("span2Rowspan"); QTest::addColumn<int>("span2Colspan"); QTest::addColumn<int>("column1Span"); QTest::addColumn<int>("column2Span"); QTest::addColumn<int>("column3Span"); QTest::addColumn<int>("row1Span"); QTest::addColumn<int>("row2Span"); QTest::addColumn<int>("row3Span"); QTest::newRow("Overlapping spans") << 0 << 0 << 1 << 2 << 0 << 1 << 1 << 2 << 2 << 2 << 1 << 1 << 1 << 1; QTest::newRow("Trivial span inside a regular span") << 0 << 0 << 1 << 2 << 0 << 1 << 1 << 1 << 2 << 2 << 1 << 1 << 1 << 1; QTest::newRow("Expand a span from the left") << 0 << 1 << 1 << 2 << 0 << 0 << 1 << 3 << 3 << 3 << 3 << 1 << 1 << 1; QTest::newRow("Expand a span to the right") << 0 << 0 << 1 << 2 << 0 << 0 << 1 << 3 << 3 << 3 << 3 << 1 << 1 << 1; QTest::newRow("Redefind span from left") << 0 << 1 << 1 << 2 << 0 << 0 << 1 << 2 << 2 << 2 << 1 << 1 << 1 << 1; }

      void testSpan()

      { QStandardItemModel model; model.setItem(0, 0, new QStandardItem() ); model.setItem(0, 1, new QStandardItem() ); model.setItem(0, 2, new QStandardItem() ); QTableView view; view.setModel( &model ); QFETCH(int, span1Row); QFETCH(int, span1Column); QFETCH(int, span1Rowspan); QFETCH(int, span1Colspan); view.setSpan( span1Row, span1Column, span1Rowspan, span1Colspan ); QFETCH(int, span2Row); QFETCH(int, span2Column); QFETCH(int, span2Rowspan); QFETCH(int, span2Colspan); view.setSpan( span2Row, span2Column, span2Rowspan, span2Colspan ); QFETCH(int, column1Span); QCOMPARE(view.columnSpan(0, 0), column1Span); QFETCH(int, column2Span); QCOMPARE(view.columnSpan(0, 1), column2Span); QFETCH(int, column3Span); QCOMPARE(view.columnSpan(0, 2), column3Span); QFETCH(int, row1Span); QCOMPARE(view.rowSpan(0, 0), row1Span); QFETCH(int, row2Span); QCOMPARE(view.rowSpan(0, 1), row2Span); QFETCH(int, row3Span); QCOMPARE(view.rowSpan(0, 2), row3Span); }

      };

      QTEST_MAIN(Test)
      #include "test.moc"

      Here is a patch to solve the ambiguities:

      diff --git a/src/gui/itemviews/qtableview.cpp b/src/gui/itemviews/qtableview.cpp
      index 6aeedfa..c31264a 100644
      — a/src/gui/itemviews/qtableview.cpp
      +++ b/src/gui/itemviews/qtableview.cpp
      @@ -119,18 +119,28 @@ void QTableViewPrivate::trimHiddenSelections(QItemSelectionRange *range) const
      */
      void QTableViewPrivate::setSpan(int row, int column, int rowSpan, int columnSpan)
      {

      • if (row < 0 || column < 0 || rowSpan < 0 || columnSpan < 0)
        + if (row < 0 || column < 0 || rowSpan <= 0 || columnSpan <= 0)
        return;
      • Span sp(row, column, rowSpan, columnSpan);
      • QList<Span>::iterator it;
      • for (it = spans.begin(); it != spans.end(); ++it) {
        + const Span sp(row, column, rowSpan, columnSpan);
        + QList<Span>::iterator it = spans.begin();
        + while(it != spans.end())
        Unknown macro: { if (((*it).top() == sp.top()) && ((*it).left() == sp.left())) { if ((sp.height() == 1) && (sp.width() == 1)) spans.erase(it); // "Implicit" span (1, 1), no need to store it else *it = sp; // Replace return; - }+ }

        else if ((sp.top() >= (*it).top()) && (sp.top() <= (*it).bottom())
        + && (sp.left() >= (*it).left()) && (sp.left() <= (*it).right()))

        { + // the first cell is inside another span + return; + }

        else if ((sp.top() <= (*it).top()) && (sp.left() <= (*it).left())
        + && (sp.bottom() >= (*it).top()) && (sp.right() >= (*it).left()))

        { + // the end cell is inside another span + it = spans.erase(it); + continue; + }

        + ++it;
        }
        spans.append(sp);
        }

      Attachments

        No reviews matched the request. Check your Options in the drop-down menu of this sections header.

        Activity

          People

            Unassigned Unassigned
            poulain Benjamin Poulain (closed Nokia identity) (Inactive)
            Votes:
            0 Vote for this issue
            Watchers:
            1 Start watching this issue

            Dates

              Created:
              Updated:
              Resolved:

              Gerrit Reviews

                There are no open Gerrit changes