From 37fd733debc8c8d98220a6a920b14328ec5ab038 Mon Sep 17 00:00:00 2001 From: Stanislav Bas Date: Sun, 26 Jul 2015 20:06:30 +0300 Subject: [PATCH 1/4] Create a custom signal to inform about a row addition in IdTableProxyModel --- apps/opencs/model/world/idtableproxymodel.cpp | 53 ++++++++++++------- apps/opencs/model/world/idtableproxymodel.hpp | 20 +++++-- 2 files changed, 51 insertions(+), 22 deletions(-) diff --git a/apps/opencs/model/world/idtableproxymodel.cpp b/apps/opencs/model/world/idtableproxymodel.cpp index ce9d44ed6..8beccd497 100644 --- a/apps/opencs/model/world/idtableproxymodel.cpp +++ b/apps/opencs/model/world/idtableproxymodel.cpp @@ -7,23 +7,23 @@ void CSMWorld::IdTableProxyModel::updateColumnMap() { - mColumnMap.clear(); + Q_ASSERT(mSourceModel != NULL); + mColumnMap.clear(); if (mFilter) { std::vector columns = mFilter->getReferencedColumns(); - - const IdTableBase& table = dynamic_cast (*sourceModel()); - for (std::vector::const_iterator iter (columns.begin()); iter!=columns.end(); ++iter) - mColumnMap.insert (std::make_pair (*iter, - table.searchColumnIndex (static_cast (*iter)))); + mColumnMap.insert (std::make_pair (*iter, + mSourceModel->searchColumnIndex (static_cast (*iter)))); } } bool CSMWorld::IdTableProxyModel::filterAcceptsRow (int sourceRow, const QModelIndex& sourceParent) const { + Q_ASSERT(mSourceModel != NULL); + // It is not possible to use filterAcceptsColumn() and check for // sourceModel()->headerData (sourceColumn, Qt::Horizontal, CSMWorld::ColumnBase::Role_Flags) // because the sourceColumn parameter excludes the hidden columns, i.e. wrong columns can @@ -35,34 +35,37 @@ bool CSMWorld::IdTableProxyModel::filterAcceptsRow (int sourceRow, const QModelI if (!mFilter) return true; - return mFilter->test ( - dynamic_cast (*sourceModel()), sourceRow, mColumnMap); + return mFilter->test (*mSourceModel, sourceRow, mColumnMap); } CSMWorld::IdTableProxyModel::IdTableProxyModel (QObject *parent) -: QSortFilterProxyModel (parent) + : QSortFilterProxyModel (parent), + mSourceModel(NULL) { setSortCaseSensitivity (Qt::CaseInsensitive); } QModelIndex CSMWorld::IdTableProxyModel::getModelIndex (const std::string& id, int column) const { - return mapFromSource (dynamic_cast (*sourceModel()).getModelIndex (id, column)); + Q_ASSERT(mSourceModel != NULL); + + return mapFromSource(mSourceModel->getModelIndex (id, column)); } void CSMWorld::IdTableProxyModel::setSourceModel(QAbstractItemModel *model) { QSortFilterProxyModel::setSourceModel(model); - connect(sourceModel(), - SIGNAL(rowsRemoved(const QModelIndex &, int, int)), - this, - SLOT(sourceRowsChanged(const QModelIndex &, int, int))); - connect(sourceModel(), + mSourceModel = dynamic_cast(sourceModel()); + connect(mSourceModel, SIGNAL(rowsInserted(const QModelIndex &, int, int)), this, - SLOT(sourceRowsChanged(const QModelIndex &, int, int))); - connect(sourceModel(), + SLOT(sourceRowsInserted(const QModelIndex &, int, int))); + connect(mSourceModel, + SIGNAL(rowsRemoved(const QModelIndex &, int, int)), + this, + SLOT(sourceRowsRemoved(const QModelIndex &, int, int))); + connect(mSourceModel, SIGNAL(dataChanged(const QModelIndex &, const QModelIndex &)), this, SLOT(sourceDataChanged(const QModelIndex &, const QModelIndex &))); @@ -95,13 +98,27 @@ bool CSMWorld::IdTableProxyModel::lessThan(const QModelIndex &left, const QModel return QSortFilterProxyModel::lessThan(left, right); } +QString CSMWorld::IdTableProxyModel::getRecordId(int sourceRow) const +{ + Q_ASSERT(mSourceModel != NULL); + + int idColumn = mSourceModel->findColumnIndex(Columns::ColumnId_Id); + return mSourceModel->data(mSourceModel->index(sourceRow, idColumn)).toString(); +} + void CSMWorld::IdTableProxyModel::refreshFilter() { updateColumnMap(); invalidateFilter(); } -void CSMWorld::IdTableProxyModel::sourceRowsChanged(const QModelIndex &/*parent*/, int /*start*/, int /*end*/) +void CSMWorld::IdTableProxyModel::sourceRowsInserted(const QModelIndex &/*parent*/, int /*start*/, int end) +{ + refreshFilter(); + emit rowAdded(getRecordId(end).toUtf8().constData()); +} + +void CSMWorld::IdTableProxyModel::sourceRowsRemoved(const QModelIndex &/*parent*/, int /*start*/, int /*end*/) { refreshFilter(); } diff --git a/apps/opencs/model/world/idtableproxymodel.hpp b/apps/opencs/model/world/idtableproxymodel.hpp index 17c30361a..cf31b5c11 100644 --- a/apps/opencs/model/world/idtableproxymodel.hpp +++ b/apps/opencs/model/world/idtableproxymodel.hpp @@ -27,6 +27,10 @@ namespace CSMWorld typedef std::map > EnumColumnCache; mutable EnumColumnCache mEnumColumnCache; + protected: + + IdTableBase *mSourceModel; + private: void updateColumnMap(); @@ -45,15 +49,23 @@ namespace CSMWorld protected: - bool lessThan(const QModelIndex &left, const QModelIndex &right) const; + virtual bool lessThan(const QModelIndex &left, const QModelIndex &right) const; virtual bool filterAcceptsRow (int sourceRow, const QModelIndex& sourceParent) const; - private slots: + QString getRecordId(int sourceRow) const; + + protected slots: + + virtual void sourceRowsInserted(const QModelIndex &parent, int start, int end); + + virtual void sourceRowsRemoved(const QModelIndex &parent, int start, int end); + + virtual void sourceDataChanged(const QModelIndex &topLeft, const QModelIndex &bottomRight); - void sourceRowsChanged(const QModelIndex &parent, int start, int end); + signals: - void sourceDataChanged(const QModelIndex &topLeft, const QModelIndex &bottomRight); + void rowAdded(const std::string &id); }; } From 86b7d2a43d2842eaab0938afbab699dcce4684e0 Mon Sep 17 00:00:00 2001 From: Stanislav Bas Date: Sun, 26 Jul 2015 20:07:36 +0300 Subject: [PATCH 2/4] Inform about a row addition after re-sorting in InfoTableProxyModel --- .../model/world/infotableproxymodel.cpp | 56 ++++++++++++++----- .../model/world/infotableproxymodel.hpp | 12 ++-- 2 files changed, 50 insertions(+), 18 deletions(-) diff --git a/apps/opencs/model/world/infotableproxymodel.cpp b/apps/opencs/model/world/infotableproxymodel.cpp index 6216291d0..c6216ba5d 100644 --- a/apps/opencs/model/world/infotableproxymodel.cpp +++ b/apps/opencs/model/world/infotableproxymodel.cpp @@ -9,16 +9,17 @@ namespace { QString toLower(const QString &str) { - return QString::fromUtf8(Misc::StringUtils::lowerCase(str.toStdString()).c_str()); + return QString::fromUtf8(Misc::StringUtils::lowerCase(str.toUtf8().constData()).c_str()); } } CSMWorld::InfoTableProxyModel::InfoTableProxyModel(CSMWorld::UniversalId::Type type, QObject *parent) : IdTableProxyModel(parent), mType(type), - mSourceModel(NULL), mInfoColumnId(type == UniversalId::Type_TopicInfos ? Columns::ColumnId_Topic : - Columns::ColumnId_Journal) + Columns::ColumnId_Journal), + mInfoColumnIndex(-1), + mLastAddedSourceRow(-1) { Q_ASSERT(type == UniversalId::Type_TopicInfos || type == UniversalId::Type_JournalInfos); } @@ -26,23 +27,18 @@ CSMWorld::InfoTableProxyModel::InfoTableProxyModel(CSMWorld::UniversalId::Type t void CSMWorld::InfoTableProxyModel::setSourceModel(QAbstractItemModel *sourceModel) { IdTableProxyModel::setSourceModel(sourceModel); - mSourceModel = dynamic_cast(sourceModel); + if (mSourceModel != NULL) { - connect(mSourceModel, - SIGNAL(rowsInserted(const QModelIndex &, int, int)), - this, - SLOT(modelRowsChanged(const QModelIndex &, int, int))); - connect(mSourceModel, - SIGNAL(rowsRemoved(const QModelIndex &, int, int)), - this, - SLOT(modelRowsChanged(const QModelIndex &, int, int))); + mInfoColumnIndex = mSourceModel->findColumnIndex(mInfoColumnId); mFirstRowCache.clear(); } } bool CSMWorld::InfoTableProxyModel::lessThan(const QModelIndex &left, const QModelIndex &right) const { + Q_ASSERT(mSourceModel != NULL); + QModelIndex first = mSourceModel->index(getFirstInfoRow(left.row()), left.column()); QModelIndex second = mSourceModel->index(getFirstInfoRow(right.row()), right.column()); @@ -56,8 +52,10 @@ bool CSMWorld::InfoTableProxyModel::lessThan(const QModelIndex &left, const QMod int CSMWorld::InfoTableProxyModel::getFirstInfoRow(int currentRow) const { + Q_ASSERT(mSourceModel != NULL); + int row = currentRow; - int column = mSourceModel->findColumnIndex(mInfoColumnId); + int column = mInfoColumnIndex; QString info = toLower(mSourceModel->data(mSourceModel->index(row, column)).toString()); if (mFirstRowCache.contains(info)) @@ -73,7 +71,37 @@ int CSMWorld::InfoTableProxyModel::getFirstInfoRow(int currentRow) const return row; } -void CSMWorld::InfoTableProxyModel::modelRowsChanged(const QModelIndex &/*parent*/, int /*start*/, int /*end*/) +void CSMWorld::InfoTableProxyModel::sourceRowsRemoved(const QModelIndex &/*parent*/, int /*start*/, int /*end*/) +{ + refreshFilter(); + mFirstRowCache.clear(); +} + +void CSMWorld::InfoTableProxyModel::sourceRowsInserted(const QModelIndex &/*parent*/, int /*start*/, int end) { + refreshFilter(); mFirstRowCache.clear(); + // We can't re-sort the model here, because the topic of the added row isn't set yet. + // Store the row index for using in the first dataChanged() after this row insertion. + mLastAddedSourceRow = end; +} + +void CSMWorld::InfoTableProxyModel::sourceDataChanged(const QModelIndex &topLeft, const QModelIndex &bottomRight) +{ + refreshFilter(); + + if (mLastAddedSourceRow != -1 && + topLeft.row() <= mLastAddedSourceRow && bottomRight.row() >= mLastAddedSourceRow) + { + // Now the topic of the last added row is set, + // so we can re-sort the model to ensure the corrent position of this row + int column = sortColumn(); + Qt::SortOrder order = sortOrder(); + sort(mInfoColumnIndex); // Restore the correct position of an added row + sort(column, order); // Restore the original sort order + emit rowAdded(getRecordId(mLastAddedSourceRow).toUtf8().constData()); + + // Make sure that we perform a re-sorting only in the first dataChanged() after a row insertion + mLastAddedSourceRow = -1; + } } diff --git a/apps/opencs/model/world/infotableproxymodel.hpp b/apps/opencs/model/world/infotableproxymodel.hpp index 28d6017b3..51d93f9a1 100644 --- a/apps/opencs/model/world/infotableproxymodel.hpp +++ b/apps/opencs/model/world/infotableproxymodel.hpp @@ -16,25 +16,29 @@ namespace CSMWorld Q_OBJECT UniversalId::Type mType; - IdTableBase *mSourceModel; Columns::ColumnId mInfoColumnId; ///< Contains ID for Topic or Journal ID + int mInfoColumnIndex; + int mLastAddedSourceRow; mutable QHash mFirstRowCache; int getFirstInfoRow(int currentRow) const; ///< Finds the first row with the same topic (journal entry) as in \a currentRow + ///< \a currentRow is a row of the source model. public: InfoTableProxyModel(UniversalId::Type type, QObject *parent = 0); - void setSourceModel(QAbstractItemModel *sourceModel); + virtual void setSourceModel(QAbstractItemModel *sourceModel); protected: virtual bool lessThan(const QModelIndex &left, const QModelIndex &right) const; - private slots: - void modelRowsChanged(const QModelIndex &parent, int start, int end); + protected slots: + virtual void sourceRowsInserted(const QModelIndex &parent, int start, int end); + virtual void sourceRowsRemoved(const QModelIndex &parent, int start, int end); + virtual void sourceDataChanged(const QModelIndex &topLeft, const QModelIndex &bottomRight); }; } From 2471e4d67a343ef8f64eb324feb08c8629f9d673 Mon Sep 17 00:00:00 2001 From: Stanislav Bas Date: Sun, 26 Jul 2015 20:09:31 +0300 Subject: [PATCH 3/4] Rework Table to use rowAdded() signal of a proxy model --- apps/opencs/view/world/table.cpp | 11 +++++++---- apps/opencs/view/world/table.hpp | 2 +- 2 files changed, 8 insertions(+), 5 deletions(-) diff --git a/apps/opencs/view/world/table.cpp b/apps/opencs/view/world/table.cpp index 5a2a572e2..02a9bb07f 100644 --- a/apps/opencs/view/world/table.cpp +++ b/apps/opencs/view/world/table.cpp @@ -371,8 +371,10 @@ CSVWorld::Table::Table (const CSMWorld::UniversalId& id, connect (mProxyModel, SIGNAL (rowsRemoved (const QModelIndex&, int, int)), this, SLOT (tableSizeUpdate())); - connect (mProxyModel, SIGNAL (rowsInserted (const QModelIndex&, int, int)), - this, SLOT (rowsInsertedEvent(const QModelIndex&, int, int))); + //connect (mProxyModel, SIGNAL (rowsInserted (const QModelIndex&, int, int)), + // this, SLOT (rowsInsertedEvent(const QModelIndex&, int, int))); + connect (mProxyModel, SIGNAL (rowAdded (const std::string &)), + this, SLOT (rowAdded (const std::string &))); /// \note This signal could instead be connected to a slot that filters out changes not affecting /// the records status column (for permanence reasons) @@ -714,12 +716,13 @@ std::vector< CSMWorld::UniversalId > CSVWorld::Table::getDraggedRecords() const return idToDrag; } -void CSVWorld::Table::rowsInsertedEvent(const QModelIndex& parent, int start, int end) +void CSVWorld::Table::rowAdded(const std::string &id) { tableSizeUpdate(); if(mJumpToAddedRecord) { - selectRow(end); + int idColumn = mModel->findColumnIndex(CSMWorld::Columns::ColumnId_Id); + selectRow(mProxyModel->getModelIndex(id, idColumn).row()); if(mUnselectAfterJump) clearSelection(); diff --git a/apps/opencs/view/world/table.hpp b/apps/opencs/view/world/table.hpp index adacd3a9d..7ddeff0a5 100644 --- a/apps/opencs/view/world/table.hpp +++ b/apps/opencs/view/world/table.hpp @@ -140,7 +140,7 @@ namespace CSVWorld void updateUserSetting (const QString &name, const QStringList &list); - void rowsInsertedEvent(const QModelIndex& parent, int start, int end); + void rowAdded(const std::string &id); }; } From 812fffbadcc4ef8c69cf7382ab0e4c8939a8ef9f Mon Sep 17 00:00:00 2001 From: Stanislav Bas Date: Sun, 26 Jul 2015 20:25:46 +0300 Subject: [PATCH 4/4] Don't inform about a nested row addition in proxy models for top-level tables --- apps/opencs/model/world/idtableproxymodel.cpp | 7 +++++-- apps/opencs/model/world/infotableproxymodel.cpp | 14 +++++++++----- 2 files changed, 14 insertions(+), 7 deletions(-) diff --git a/apps/opencs/model/world/idtableproxymodel.cpp b/apps/opencs/model/world/idtableproxymodel.cpp index 8beccd497..10fd92b46 100644 --- a/apps/opencs/model/world/idtableproxymodel.cpp +++ b/apps/opencs/model/world/idtableproxymodel.cpp @@ -112,10 +112,13 @@ void CSMWorld::IdTableProxyModel::refreshFilter() invalidateFilter(); } -void CSMWorld::IdTableProxyModel::sourceRowsInserted(const QModelIndex &/*parent*/, int /*start*/, int end) +void CSMWorld::IdTableProxyModel::sourceRowsInserted(const QModelIndex &parent, int /*start*/, int end) { refreshFilter(); - emit rowAdded(getRecordId(end).toUtf8().constData()); + if (!parent.isValid()) + { + emit rowAdded(getRecordId(end).toUtf8().constData()); + } } void CSMWorld::IdTableProxyModel::sourceRowsRemoved(const QModelIndex &/*parent*/, int /*start*/, int /*end*/) diff --git a/apps/opencs/model/world/infotableproxymodel.cpp b/apps/opencs/model/world/infotableproxymodel.cpp index c6216ba5d..f55f775ab 100644 --- a/apps/opencs/model/world/infotableproxymodel.cpp +++ b/apps/opencs/model/world/infotableproxymodel.cpp @@ -77,13 +77,17 @@ void CSMWorld::InfoTableProxyModel::sourceRowsRemoved(const QModelIndex &/*paren mFirstRowCache.clear(); } -void CSMWorld::InfoTableProxyModel::sourceRowsInserted(const QModelIndex &/*parent*/, int /*start*/, int end) +void CSMWorld::InfoTableProxyModel::sourceRowsInserted(const QModelIndex &parent, int /*start*/, int end) { refreshFilter(); - mFirstRowCache.clear(); - // We can't re-sort the model here, because the topic of the added row isn't set yet. - // Store the row index for using in the first dataChanged() after this row insertion. - mLastAddedSourceRow = end; + + if (!parent.isValid()) + { + mFirstRowCache.clear(); + // We can't re-sort the model here, because the topic of the added row isn't set yet. + // Store the row index for using in the first dataChanged() after this row insertion. + mLastAddedSourceRow = end; + } } void CSMWorld::InfoTableProxyModel::sourceDataChanged(const QModelIndex &topLeft, const QModelIndex &bottomRight)