From b979956ec46093e5668c2b264f9b68da3cbb0326 Mon Sep 17 00:00:00 2001 From: Mark Brand Date: Wed, 15 Feb 2012 09:56:23 +0100 Subject: QSqlTableModel: handle changes between submit and select Once an insert has been submitted, the cached record behaves like an update. For row bookkeeping, we still have to remember that it was originally inserted and is not in the query rows. Between submitting a delete and selecting, we remove the values from the deleted record. This causes a blank row to be displayed. Read-only flag is set for cells in deleted row. Reverting between submit and select means going back to the last submitted values. When removing rows, it's better to process from highest row numbers to lowest. This avoids complications with higher rows shifting down when lower rows are removed. Change-Id: I8752fa11f7a1b88f2a71b9e03a020ac37e62487f Reviewed-by: Honglei Zhang --- src/sql/models/qsqltablemodel.cpp | 52 +++++++++++++--------- src/sql/models/qsqltablemodel_p.h | 47 ++++++++++++++++--- .../models/qsqltablemodel/tst_qsqltablemodel.cpp | 8 ++-- 3 files changed, 75 insertions(+), 32 deletions(-) diff --git a/src/sql/models/qsqltablemodel.cpp b/src/sql/models/qsqltablemodel.cpp index 24668dd946..9ade5f14d5 100644 --- a/src/sql/models/qsqltablemodel.cpp +++ b/src/sql/models/qsqltablemodel.cpp @@ -87,7 +87,7 @@ int QSqlTableModelPrivate::insertCount(int maxRow) const for (; i != e && (maxRow < 0 || i.key() <= maxRow); ++i) { - if (i.value().op() == Insert) + if (i.value().insert()) ++cnt; } @@ -122,19 +122,17 @@ void QSqlTableModelPrivate::revertCachedRow(int row) Q_Q(QSqlTableModel); ModifiedRow r = cache.value(row); - // cannot revert a committed change - if (r.submitted()) - return; - switch (r.op()) { case QSqlTableModelPrivate::None: Q_ASSERT_X(false, "QSqlTableModelPrivate::revertCachedRow()", "Invalid entry in cache map"); return; case QSqlTableModelPrivate::Update: case QSqlTableModelPrivate::Delete: - cache.remove(row); - emit q->dataChanged(q->createIndex(row, 0), - q->createIndex(row, q->columnCount() - 1)); + if (!r.submitted()) { + cache[row].revert(); + emit q->dataChanged(q->createIndex(row, 0), + q->createIndex(row, q->columnCount() - 1)); + } break; case QSqlTableModelPrivate::Insert: { QMap::Iterator it = cache.find(row); @@ -373,7 +371,7 @@ bool QSqlTableModel::select() while (it != d->cache.constBegin()) { --it; // rows must be accounted for - if (it.value().op() == QSqlTableModelPrivate::Insert) { + if (it.value().insert()) { beginRemoveRows(QModelIndex(), it.key(), it.key()); it = d->cache.erase(it); endRemoveRows(); @@ -470,11 +468,14 @@ bool QSqlTableModel::setData(const QModelIndex &index, const QVariant &value, in if (!index.isValid() || index.column() >= d->rec.count() || index.row() >= rowCount()) return false; + if (d->cache.value(index.row()).op() == QSqlTableModelPrivate::Delete) + return false; + if (d->strategy == OnFieldChange && d->cache.value(index.row()).op() != QSqlTableModelPrivate::Insert) { - d->cache.clear(); + revertAll(); } else if (d->strategy == OnRowChange && !d->cache.isEmpty() && !d->cache.contains(index.row())) { submit(); - d->cache.clear(); + revertAll(); } QSqlTableModelPrivate::ModifiedRow &row = d->cache[index.row()]; @@ -759,8 +760,10 @@ void QSqlTableModel::revertAll() { Q_D(QSqlTableModel); - while (!d->cache.isEmpty()) - revertRow(d->cache.constBegin().key()); + const QList rows(d->cache.keys()); + for (int i = rows.size() - 1; i >= 0; --i) { + revertRow(rows.value(i)); + } } /*! @@ -967,15 +970,17 @@ bool QSqlTableModel::removeRows(int row, int count, const QModelIndex &parent) else if (!count) return true; - for (int i = 0; i < count; ++i) { - int idx = row + i; - if (d->cache.value(idx).op() == QSqlTableModelPrivate::Insert) { + // Iterate backwards so we don't have to worry about removed rows causing + // higher cache entries to shift downwards. + for (int idx = row + count - 1; idx >= row; --idx) { + QSqlTableModelPrivate::ModifiedRow& mrow = d->cache[idx]; + if (mrow.op() == QSqlTableModelPrivate::Insert) { revertRow(idx); - // Reverting a row means all the other cache entries have been adjusted downwards - // so fake this by adjusting row - --row; } else { - d->cache[idx] = QSqlTableModelPrivate::ModifiedRow(QSqlTableModelPrivate::Delete, record(idx)); + if (mrow.op() == QSqlTableModelPrivate::None) + mrow = QSqlTableModelPrivate::ModifiedRow(QSqlTableModelPrivate::Delete, record(idx)); + else + mrow.setOp(QSqlTableModelPrivate::Delete); if (d->strategy == OnManualSubmit) emit headerDataChanged(Qt::Vertical, idx, idx); } @@ -1158,6 +1163,8 @@ Qt::ItemFlags QSqlTableModel::flags(const QModelIndex &index) const return 0; if (d->rec.field(index.column()).isReadOnly()) return Qt::ItemIsSelectable | Qt::ItemIsEnabled; + if (d->cache.value(index.row()).op() == QSqlTableModelPrivate::Delete) + return Qt::ItemIsSelectable | Qt::ItemIsEnabled; return Qt::ItemIsSelectable | Qt::ItemIsEnabled | Qt::ItemIsEditable; } @@ -1186,8 +1193,11 @@ bool QSqlTableModel::setRecord(int row, const QSqlRecord &values) if (row >= rowCount()) return false; + if (d->cache.value(row).op() == QSqlTableModelPrivate::Delete) + return false; + if (d->strategy == OnFieldChange && d->cache.value(row).op() != QSqlTableModelPrivate::Insert) - d->cache.clear(); + revertAll(); else if (d->strategy == OnRowChange && !d->cache.isEmpty() && !d->cache.contains(row)) submit(); diff --git a/src/sql/models/qsqltablemodel_p.h b/src/sql/models/qsqltablemodel_p.h index 57051a0e9c..ba2fdf5df3 100644 --- a/src/sql/models/qsqltablemodel_p.h +++ b/src/sql/models/qsqltablemodel_p.h @@ -101,18 +101,51 @@ public: { public: inline ModifiedRow(Op o = None, const QSqlRecord &r = QSqlRecord()) - : m_op(o), m_rec(r), m_submitted(false) - { init_rec(); } + : m_op(None), m_db_values(r), m_insert(o == Insert) + { setOp(o); } inline Op op() const { return m_op; } + inline void setOp(Op o) + { + if (o == m_op) + return; + m_submitted = (o != Insert && o != Delete); + m_op = o; + m_rec = m_db_values; + setGenerated(m_rec, m_op == Delete); + } inline QSqlRecord rec() const { return m_rec; } inline QSqlRecord& recRef() { return m_rec; } inline void setValue(int c, const QVariant &v) { + m_submitted = false; m_rec.setValue(c, v); m_rec.setGenerated(c, true); } inline bool submitted() const { return m_submitted; } - inline void setSubmitted() { m_submitted = true; } + inline void setSubmitted() + { + m_submitted = true; + setGenerated(m_rec, false); + if (m_op == Delete) { + m_rec.clearValues(); + } + else { + m_op = Update; + m_db_values = m_rec; + setGenerated(m_db_values, true); + } + } + inline bool insert() const { return m_insert; } + inline void revert() + { + if (m_submitted) + return; + if (m_op == Delete) + m_op = Update; + m_rec = m_db_values; + setGenerated(m_rec, false); + m_submitted = true; + } inline QSqlRecord primaryValues(const QSqlRecord& pi) const { if (m_op == None || m_op == Insert) @@ -126,16 +159,16 @@ public: return values; } private: - void init_rec() + inline static void setGenerated(QSqlRecord& r, bool g) { - for (int i = m_rec.count() - 1; i >= 0; --i) - m_rec.setGenerated(i, false); - m_db_values = m_rec; + for (int i = r.count() - 1; i >= 0; --i) + r.setGenerated(i, g); } Op m_op; QSqlRecord m_rec; QSqlRecord m_db_values; bool m_submitted; + bool m_insert; }; typedef QMap CacheMap; diff --git a/tests/auto/sql/models/qsqltablemodel/tst_qsqltablemodel.cpp b/tests/auto/sql/models/qsqltablemodel/tst_qsqltablemodel.cpp index 270de8292b..43829987d4 100644 --- a/tests/auto/sql/models/qsqltablemodel/tst_qsqltablemodel.cpp +++ b/tests/auto/sql/models/qsqltablemodel/tst_qsqltablemodel.cpp @@ -730,10 +730,10 @@ void tst_QSqlTableModel::removeRows() QSignalSpy headerDataChangedSpy(&model, SIGNAL(headerDataChanged(Qt::Orientation, int, int))); QVERIFY(model.removeRows(0, 2, QModelIndex())); QCOMPARE(headerDataChangedSpy.count(), 2); - QCOMPARE(headerDataChangedSpy.at(0).at(1).toInt(), 0); - QCOMPARE(headerDataChangedSpy.at(0).at(2).toInt(), 0); - QCOMPARE(headerDataChangedSpy.at(1).at(1).toInt(), 1); - QCOMPARE(headerDataChangedSpy.at(1).at(2).toInt(), 1); + QCOMPARE(headerDataChangedSpy.at(0).at(1).toInt(), 1); + QCOMPARE(headerDataChangedSpy.at(0).at(2).toInt(), 1); + QCOMPARE(headerDataChangedSpy.at(1).at(1).toInt(), 0); + QCOMPARE(headerDataChangedSpy.at(1).at(2).toInt(), 0); QCOMPARE(model.rowCount(), 3); QVERIFY(beforeDeleteSpy.count() == 0); QVERIFY(model.submitAll()); -- cgit v1.2.3