fix: Updates based on comments
This commit is contained in:
parent
6da897e733
commit
56f194c96c
|
@ -74,7 +74,7 @@ Item {
|
|||
|
||||
RowLayout{
|
||||
Button {
|
||||
text: "Insert"
|
||||
text: "Insert at"
|
||||
onClicked: {
|
||||
listModel.insert(parseInt(insertIndex.text),{
|
||||
name: "Item " + (listModel.count + 1),
|
||||
|
@ -84,21 +84,21 @@ Item {
|
|||
}
|
||||
TextField {
|
||||
id: insertIndex
|
||||
text: "Insert at"
|
||||
text: "0"
|
||||
cursorVisible: false
|
||||
inputMethodHints: Qt.ImhDigitsOnly
|
||||
}
|
||||
}
|
||||
RowLayout{
|
||||
Button {
|
||||
text: "Remove"
|
||||
text: "Remove at"
|
||||
onClicked: {
|
||||
listModel.remove(parseInt(removeIndex.text), 1)
|
||||
}
|
||||
}
|
||||
TextField {
|
||||
id: removeIndex
|
||||
text: "Remove from"
|
||||
text: "0"
|
||||
cursorVisible: false
|
||||
inputMethodHints: Qt.ImhDigitsOnly
|
||||
}
|
||||
|
@ -146,34 +146,42 @@ Item {
|
|||
|
||||
RowLayout{
|
||||
Button {
|
||||
text: "Insert"
|
||||
text: "Insert at"
|
||||
onClicked: {
|
||||
if(writableProxyModel.insert(parseInt(insertWritableIndex.text)))
|
||||
{
|
||||
let item = writableProxyModel.get(parseInt(insertWritableIndex.text))
|
||||
item.name = "New " + (listView.count + 1)
|
||||
item.key = listView.count + 1
|
||||
writableProxyModel.set(parseInt(insertWritableIndex.text), item)
|
||||
}
|
||||
writableProxyModel.insert(parseInt(insertWritableIndex.text), {
|
||||
name: "Item " + (writableProxyModel.rowCount() + 1),
|
||||
key: writableProxyModel.rowCount() + 1
|
||||
})
|
||||
}
|
||||
}
|
||||
TextField {
|
||||
id: insertWritableIndex
|
||||
text: "Insert at"
|
||||
text: "0"
|
||||
cursorVisible: false
|
||||
inputMethodHints: Qt.ImhDigitsOnly
|
||||
}
|
||||
}
|
||||
RowLayout{
|
||||
Button {
|
||||
text: "Remove"
|
||||
text: "Append"
|
||||
onClicked: {
|
||||
writableProxyModel.append({
|
||||
name: "Item " + (writableProxyModel.rowCount() + 1),
|
||||
key: writableProxyModel.rowCount() + 1
|
||||
})
|
||||
}
|
||||
}
|
||||
}
|
||||
RowLayout{
|
||||
Button {
|
||||
text: "Remove from"
|
||||
onClicked: {
|
||||
writableProxyModel.remove(parseInt(removeWritableIndex.text))
|
||||
}
|
||||
}
|
||||
TextField {
|
||||
id: removeWritableIndex
|
||||
text: "Remove from"
|
||||
text: "0"
|
||||
cursorVisible: false
|
||||
inputMethodHints: Qt.ImhDigitsOnly
|
||||
}
|
||||
|
|
|
@ -32,7 +32,8 @@ public:
|
|||
~WritableProxyModel();
|
||||
|
||||
Q_INVOKABLE QVariantMap toVariantMap() const;
|
||||
Q_INVOKABLE bool insert(int at);
|
||||
Q_INVOKABLE bool insert(int at, const QVariantMap& data = {});
|
||||
Q_INVOKABLE bool append(const QVariantMap& data = {});
|
||||
Q_INVOKABLE bool remove(int at);
|
||||
//Returns a VariantMap of the data at the given index
|
||||
//The map contains the role names as keys and the data as values
|
||||
|
@ -67,28 +68,22 @@ public:
|
|||
bool hasChildren(const QModelIndex& parent = QModelIndex()) const override;
|
||||
void revert() override;
|
||||
|
||||
// TODO: implement these
|
||||
// bool moveRows(const QModelIndex &sourceParent, int sourceRow, int count, const QModelIndex &destinationParent, int destinationChild) override;
|
||||
// bool submit() override;
|
||||
|
||||
signals:
|
||||
void dirtyChanged();
|
||||
|
||||
private:
|
||||
void setDirty(bool flag);
|
||||
|
||||
void handleSourceDataChanged(const QModelIndex& topLeft, const QModelIndex& bottomRight, const QVector<int>& roles);
|
||||
void handleRowsAboutToBeInserted(const QModelIndex &parent, int start, int end);
|
||||
void handleRowsInserted(const QModelIndex &parent, int first, int last);
|
||||
void handleRowsAboutToBeRemoved(const QModelIndex &parent, int start, int end);
|
||||
void handleRowsRemoved(const QModelIndex &parent, int first, int last);
|
||||
void handleModelAboutToBeReset();
|
||||
void handleModelReset();
|
||||
void handleLayoutAboutToBeChanged(const QList<QPersistentModelIndex> &sourceParents, QAbstractItemModel::LayoutChangeHint hint);
|
||||
void handleLayoutChanged(const QList<QPersistentModelIndex> &sourceParents, QAbstractItemModel::LayoutChangeHint hint);
|
||||
void handleRowsMoved(const QModelIndex &sourceParent, int sourceStart, int sourceEnd, const QModelIndex &destinationParent, int destinationRow);
|
||||
|
||||
bool m_dirty{false};
|
||||
void onSourceDataChanged(const QModelIndex& topLeft, const QModelIndex& bottomRight, const QVector<int>& roles);
|
||||
void onRowsAboutToBeInserted(const QModelIndex& parent, int start, int end);
|
||||
void onRowsInserted(const QModelIndex& parent, int first, int last);
|
||||
void onRowsAboutToBeRemoved(const QModelIndex& parent, int start, int end);
|
||||
void onRowsRemoved(const QModelIndex& parent, int first, int last);
|
||||
void onModelAboutToBeReset();
|
||||
void onModelReset();
|
||||
void onLayoutAboutToBeChanged(const QList<QPersistentModelIndex>& sourceParents, QAbstractItemModel::LayoutChangeHint hint);
|
||||
void onLayoutChanged(const QList<QPersistentModelIndex>& sourceParents, QAbstractItemModel::LayoutChangeHint hint);
|
||||
void onRowsMoved(const QModelIndex& sourceParent, int sourceStart, int sourceEnd, const QModelIndex& destinationParent, int destinationRow);
|
||||
|
||||
QScopedPointer<WritableProxyModelPrivate> d;
|
||||
friend class WritableProxyModelPrivate;
|
||||
|
|
|
@ -5,7 +5,6 @@
|
|||
#endif
|
||||
#include <memory>
|
||||
|
||||
|
||||
template <typename T>
|
||||
using IndexedValues = QHash<T /*key*/, QMap<int/*role*/, QVariant/*value*/>>;
|
||||
|
||||
|
@ -20,49 +19,52 @@ public:
|
|||
WritableProxyModel& q;
|
||||
IndexedValues<QPersistentModelIndex> cache;
|
||||
IndexedValues<QModelIndex> insertedRows;
|
||||
int rowsAboutToBeInserted = 0;
|
||||
QSet<QPersistentModelIndex> removedRows;
|
||||
QVector<int> proxyToSourceRowMapping;
|
||||
//internal operations can change dirty flag
|
||||
bool canUpdateDirtyFlag = true;
|
||||
bool dirty{false};
|
||||
|
||||
inline void setData(const QModelIndex& index, const QVariant& value, int role);
|
||||
void setData(const QModelIndex& index, const QVariant& value, int role);
|
||||
template<typename T>
|
||||
inline void setData(const QModelIndex& index, const QVariant& value, int role, IndexedValues<T>& indexedMap);
|
||||
void setData(const QModelIndex& index, const QVariant& value, int role, IndexedValues<T>& indexedMap);
|
||||
|
||||
inline QVariant data(const QModelIndex &index, int role, bool& found) const;
|
||||
QVariant data(const QModelIndex& index, int role, bool& found) const;
|
||||
template<typename T>
|
||||
inline QVariant data(const QModelIndex &index, int role, bool& found, const IndexedValues<T>& indexedMap) const;
|
||||
QVariant data(const QModelIndex& index, int role, bool& found, const IndexedValues<T>& indexedMap) const;
|
||||
|
||||
inline int proxyToSourceRow(int row) const;
|
||||
inline int sourceToProxyRow(int row) const;
|
||||
int proxyToSourceRow(int row) const;
|
||||
int sourceToProxyRow(int row) const;
|
||||
QVector<QPair<int, int>> sourceRowRangesBetween(int start, int end) const;
|
||||
|
||||
//Simple mapping. No sorting, no moving
|
||||
//TODO: add mapping for temporarely moved rows
|
||||
//TODO: add mapping for temporarily moved rows
|
||||
void createProxyToSourceRowMap();
|
||||
|
||||
inline bool contains(const QModelIndex& sourceIndex) const;
|
||||
inline void clear();
|
||||
inline void clearInvalidatedCache();
|
||||
void clear();
|
||||
void clearInvalidatedCache();
|
||||
bool contains(const QModelIndex& sourceIndex, const QVector<int>& roles = {}) const;
|
||||
int countOffset() const;
|
||||
|
||||
void moveFromCacheToInserted(const QModelIndex& sourceIndex);
|
||||
void adjustInsertedRowsBy(int start, int offset);
|
||||
bool removeRows(int row, int count, const QModelIndex& parent = {});
|
||||
|
||||
void adjustInsertedRowsBy(int start, int offset);
|
||||
void alignInsertedRowsAtBeginning();
|
||||
|
||||
void checkForDirtyRemoval(const QModelIndex& sourceIndex, const QVector<int>& roles);
|
||||
|
||||
//Fix for missing role names in source model
|
||||
void applyRoleNamesFix();
|
||||
};
|
||||
|
||||
template<typename T>
|
||||
inline void WritableProxyModelPrivate::setData(const QModelIndex &index, const QVariant &value, int role, IndexedValues<T>& indexedMap)
|
||||
void WritableProxyModelPrivate::setData(const QModelIndex& index, const QVariant& value, int role, IndexedValues<T>& indexedMap)
|
||||
{
|
||||
auto valueMap = indexedMap.take(index);
|
||||
valueMap[role] = value;
|
||||
indexedMap.insert(index, valueMap);
|
||||
}
|
||||
|
||||
inline void WritableProxyModelPrivate::setData(const QModelIndex &index, const QVariant &value, int role)
|
||||
void WritableProxyModelPrivate::setData(const QModelIndex& index, const QVariant& value, int role)
|
||||
{
|
||||
if (proxyToSourceRowMapping[index.row()] >= 0)
|
||||
{
|
||||
|
@ -73,7 +75,7 @@ inline void WritableProxyModelPrivate::setData(const QModelIndex &index, const Q
|
|||
setData(index, value, role, insertedRows);
|
||||
}
|
||||
|
||||
inline QVariant WritableProxyModelPrivate::data(const QModelIndex &index, int role, bool& found) const
|
||||
QVariant WritableProxyModelPrivate::data(const QModelIndex& index, int role, bool& found) const
|
||||
{
|
||||
if (index.row() < 0 || index.row() >= proxyToSourceRowMapping.size())
|
||||
{
|
||||
|
@ -82,16 +84,15 @@ inline QVariant WritableProxyModelPrivate::data(const QModelIndex &index, int ro
|
|||
}
|
||||
|
||||
if (proxyToSourceRowMapping[index.row()] >= 0)
|
||||
{
|
||||
//value in cache (updated role value)
|
||||
return data(q.mapToSource(index), role, found, cache);
|
||||
}
|
||||
|
||||
//value in inserted rows
|
||||
return data(index, role, found, insertedRows);
|
||||
}
|
||||
|
||||
template<typename T>
|
||||
inline QVariant WritableProxyModelPrivate::data(const QModelIndex &index, int role, bool& found, const IndexedValues<T>& indexedMap) const
|
||||
QVariant WritableProxyModelPrivate::data(const QModelIndex& index, int role, bool& found, const IndexedValues<T>& indexedMap) const
|
||||
{
|
||||
QVariant value;
|
||||
auto it = indexedMap.find(index);
|
||||
|
@ -110,30 +111,25 @@ inline QVariant WritableProxyModelPrivate::data(const QModelIndex &index, int ro
|
|||
int WritableProxyModelPrivate::proxyToSourceRow(int row) const
|
||||
{
|
||||
if (row < 0 || row >= proxyToSourceRowMapping.size())
|
||||
{
|
||||
return -1;
|
||||
}
|
||||
|
||||
return proxyToSourceRowMapping[row];
|
||||
}
|
||||
|
||||
int WritableProxyModelPrivate::sourceToProxyRow(int row) const
|
||||
{
|
||||
for (int i = 0; i < proxyToSourceRowMapping.size(); ++i) {
|
||||
if(proxyToSourceRowMapping[i] == row)
|
||||
for (int i = 0; i < proxyToSourceRowMapping.size(); ++i)
|
||||
{
|
||||
if (proxyToSourceRowMapping[i] == row)
|
||||
return i;
|
||||
}
|
||||
}
|
||||
return -1;
|
||||
}
|
||||
|
||||
void WritableProxyModelPrivate::createProxyToSourceRowMap()
|
||||
{
|
||||
if (!q.sourceModel())
|
||||
{
|
||||
return;
|
||||
}
|
||||
|
||||
auto sourceModel = q.sourceModel();
|
||||
|
||||
|
@ -146,18 +142,30 @@ void WritableProxyModelPrivate::createProxyToSourceRowMap()
|
|||
continue;
|
||||
}
|
||||
|
||||
while(removedRows.contains(q.sourceModel()->index(sourceIter, 0)) && sourceIter < sourceModel->rowCount())
|
||||
{
|
||||
while(removedRows.contains(sourceModel->index(sourceIter, 0)) && sourceIter < sourceModel->rowCount())
|
||||
++sourceIter;
|
||||
}
|
||||
|
||||
proxyToSourceRowMapping.append(sourceIter);
|
||||
sourceIter++;
|
||||
}
|
||||
}
|
||||
|
||||
bool WritableProxyModelPrivate::contains(const QModelIndex& sourceIndex) const {
|
||||
return cache.contains(sourceIndex) || insertedRows.contains(q.mapFromSource(sourceIndex));
|
||||
bool WritableProxyModelPrivate::contains(const QModelIndex& sourceIndex, const QVector<int>& roles) const {
|
||||
if (cache.contains(sourceIndex)) {
|
||||
auto valueMap = cache[sourceIndex];
|
||||
return std::all_of(roles.begin(), roles.end(), [&valueMap](int role) { return valueMap.contains(role); });
|
||||
}
|
||||
|
||||
if (insertedRows.contains(q.mapFromSource(sourceIndex))) {
|
||||
auto valueMap = insertedRows[q.mapFromSource(sourceIndex)];
|
||||
for (auto& role : roles) {
|
||||
if (!valueMap.contains(role))
|
||||
return false;
|
||||
}
|
||||
return true;
|
||||
}
|
||||
|
||||
return false;
|
||||
}
|
||||
|
||||
void WritableProxyModelPrivate::WritableProxyModelPrivate::clear()
|
||||
|
@ -193,34 +201,70 @@ void WritableProxyModelPrivate::clearInvalidatedCache()
|
|||
}
|
||||
}
|
||||
|
||||
void WritableProxyModelPrivate::moveFromCacheToInserted(const QModelIndex& sourceIndex)
|
||||
int WritableProxyModelPrivate::countOffset() const
|
||||
{
|
||||
//User updated this row. Move it in inserted rows. We shouldn't delete it
|
||||
insertedRows.insert(q.mapFromSource(sourceIndex), cache.take(sourceIndex));
|
||||
auto itemData = q.sourceModel()->itemData(sourceIndex);
|
||||
auto proxyIndex = q.mapFromSource(sourceIndex);
|
||||
for(auto it = itemData.begin(); it != itemData.end(); ++it)
|
||||
{
|
||||
if(insertedRows[proxyIndex].contains(it.key()))
|
||||
{
|
||||
continue;
|
||||
return insertedRows.count() - removedRows.count();
|
||||
}
|
||||
|
||||
insertedRows[proxyIndex][it.key()] = it.value();
|
||||
void WritableProxyModelPrivate::moveFromCacheToInserted(const QModelIndex& sourceIndex)
|
||||
{
|
||||
if (!q.sourceModel())
|
||||
return;
|
||||
|
||||
//User updated this row. Move it in inserted rows. We shouldn't delete it
|
||||
auto proxyIndex = insertedRows.insert(q.mapFromSource(sourceIndex), cache.take(sourceIndex));
|
||||
auto itemData = q.sourceModel()->itemData(sourceIndex);
|
||||
for (auto it = itemData.begin(); it != itemData.end(); ++it)
|
||||
{
|
||||
if (proxyIndex.value().contains(it.key()))
|
||||
continue;
|
||||
|
||||
proxyIndex.value()[it.key()] = it.value();
|
||||
}
|
||||
|
||||
if (proxyIndex.key().isValid())
|
||||
proxyToSourceRowMapping[proxyIndex.key().row()] = -1;
|
||||
}
|
||||
|
||||
void WritableProxyModelPrivate::checkForDirtyRemoval(const QModelIndex& sourceIndex, const QVector<int>& roles)
|
||||
{
|
||||
q.setDirty(!cache.isEmpty() || !insertedRows.isEmpty() || !removedRows.isEmpty());
|
||||
|
||||
if (!q.sourceModel() || !q.dirty())
|
||||
return;
|
||||
|
||||
auto sourceCount = q.sourceModel()->rowCount();
|
||||
auto proxyCount = q.rowCount();
|
||||
auto insertedRowsCount = insertedRows.size();
|
||||
|
||||
if (sourceCount != proxyCount - insertedRowsCount)
|
||||
return;
|
||||
|
||||
if (cache.contains(sourceIndex))
|
||||
{
|
||||
auto& cachedData = cache[sourceIndex];
|
||||
for (auto& role : roles)
|
||||
{
|
||||
if (cachedData.contains(role) && cachedData[role] == q.sourceModel()->data(sourceIndex, role))
|
||||
cachedData.remove(role);
|
||||
}
|
||||
|
||||
if (cachedData.isEmpty()) {
|
||||
cache.remove(sourceIndex);
|
||||
q.setDirty(!cache.isEmpty() || !insertedRows.isEmpty() || !removedRows.isEmpty());
|
||||
return;
|
||||
}
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
void WritableProxyModelPrivate::applyRoleNamesFix()
|
||||
{
|
||||
if (!q.sourceModel())
|
||||
{
|
||||
return;
|
||||
}
|
||||
|
||||
if (q.sourceModel()->rowCount() != 0)
|
||||
{
|
||||
return;
|
||||
}
|
||||
|
||||
auto connectionPtr = new QMetaObject::Connection();
|
||||
*connectionPtr =
|
||||
|
@ -236,42 +280,91 @@ QVector<QPair<int, int>> WritableProxyModelPrivate::sourceRowRangesBetween(int s
|
|||
QVector<QPair<int, int>> result;
|
||||
int currentStart = -1;
|
||||
int currentEnd = -1;
|
||||
|
||||
for (int i = start; i <= end; ++i)
|
||||
{
|
||||
auto proxyRow = sourceToProxyRow(i);
|
||||
if(proxyRow >= 0)
|
||||
{
|
||||
if(currentStart == -1)
|
||||
{
|
||||
currentStart = proxyRow;
|
||||
currentEnd = currentStart;
|
||||
}
|
||||
else
|
||||
{
|
||||
currentEnd = proxyRow;
|
||||
}
|
||||
}
|
||||
else
|
||||
|
||||
if (proxyRow == -1) //is removed
|
||||
{
|
||||
//first removed row is hit
|
||||
if (currentStart != -1)
|
||||
{
|
||||
result.append({ currentStart, currentEnd });
|
||||
currentStart = -1;
|
||||
currentEnd = -1;
|
||||
}
|
||||
continue;
|
||||
}
|
||||
|
||||
if (currentStart == -1) //first row to be added to current range
|
||||
{
|
||||
currentStart = proxyRow;
|
||||
currentEnd = currentStart;
|
||||
continue;
|
||||
}
|
||||
|
||||
if (currentEnd + 1 == proxyRow) //continue current range
|
||||
{
|
||||
currentEnd = proxyRow;
|
||||
continue;
|
||||
}
|
||||
|
||||
result.append({ currentStart, currentEnd });
|
||||
currentStart = proxyRow;
|
||||
currentEnd = proxyRow;
|
||||
}
|
||||
|
||||
if (currentStart != -1)
|
||||
{
|
||||
result.append({ currentStart, currentEnd });
|
||||
}
|
||||
|
||||
return result;
|
||||
}
|
||||
|
||||
bool WritableProxyModelPrivate::removeRows(int row, int count, const QModelIndex& parent)
|
||||
{
|
||||
if (row < 0 || count < 1 || row + count > q.rowCount(parent))
|
||||
return false;
|
||||
|
||||
q.beginRemoveRows(parent, row, row + count - 1);
|
||||
|
||||
|
||||
QVector<QPersistentModelIndex> populateRemovedRows;
|
||||
QVector<QModelIndex> removeFromInsertedRows;
|
||||
|
||||
for (int i = row; i < row + count; ++i) {
|
||||
auto proxyIndex = q.index(i, 0, parent);
|
||||
auto sourceIndex = q.mapToSource(proxyIndex);
|
||||
if (sourceIndex.isValid())
|
||||
populateRemovedRows.push_back(sourceIndex);
|
||||
else
|
||||
removeFromInsertedRows.push_back(proxyIndex);
|
||||
}
|
||||
|
||||
for (auto iter = removeFromInsertedRows.rbegin(); iter != removeFromInsertedRows.rend(); ++iter)
|
||||
{
|
||||
insertedRows.remove(*iter);
|
||||
adjustInsertedRowsBy(iter->row(), -1);
|
||||
}
|
||||
|
||||
for (auto iter = populateRemovedRows.rbegin(); iter != populateRemovedRows.rend(); ++iter)
|
||||
{
|
||||
removedRows.insert(*iter);
|
||||
adjustInsertedRowsBy(sourceToProxyRow(iter->row()), -1);
|
||||
}
|
||||
|
||||
createProxyToSourceRowMap();
|
||||
q.endRemoveRows();
|
||||
|
||||
checkForDirtyRemoval({}, {});
|
||||
return true;
|
||||
}
|
||||
|
||||
void WritableProxyModelPrivate::adjustInsertedRowsBy(int start, int offset)
|
||||
{
|
||||
if (offset == 0)
|
||||
return;
|
||||
|
||||
IndexedValues<QModelIndex> newInsertedRows;
|
||||
for (auto iter = insertedRows.begin(); iter != insertedRows.end(); ++iter)
|
||||
{
|
||||
|
@ -279,16 +372,26 @@ void WritableProxyModelPrivate::adjustInsertedRowsBy(int start, int offset)
|
|||
auto value = iter.value();
|
||||
if (key.row() >= start)
|
||||
{
|
||||
newInsertedRows.insert(key.siblingAtRow(key.row() + offset), value);
|
||||
auto index = q.createIndex(key.row() + offset, 0);
|
||||
newInsertedRows.insert(index, value);
|
||||
}
|
||||
else
|
||||
{
|
||||
newInsertedRows.insert(key, value);
|
||||
}
|
||||
}
|
||||
insertedRows.swap(newInsertedRows);
|
||||
}
|
||||
|
||||
void WritableProxyModelPrivate::alignInsertedRowsAtBeginning()
|
||||
{
|
||||
for (int i = 0; i < proxyToSourceRowMapping.size(); ++i)
|
||||
{
|
||||
if (proxyToSourceRowMapping[i] != -1)
|
||||
continue;
|
||||
|
||||
adjustInsertedRowsBy(i, -i);
|
||||
}
|
||||
}
|
||||
|
||||
WritableProxyModel::WritableProxyModel(QObject* parent)
|
||||
: QAbstractProxyModel(parent)
|
||||
, d(new WritableProxyModelPrivate(*this))
|
||||
|
@ -311,31 +414,37 @@ QVariantMap WritableProxyModel::toVariantMap() const
|
|||
auto data = itemData(index);
|
||||
QVariantMap rowMap;
|
||||
for (auto it = data.begin(); it != data.end(); ++it)
|
||||
{
|
||||
rowMap[QString::number(it.key())] = it.value();
|
||||
}
|
||||
result[QString::number(row)] = rowMap;
|
||||
}
|
||||
|
||||
return result;
|
||||
}
|
||||
|
||||
bool WritableProxyModel::insert(int at)
|
||||
{
|
||||
if(at < 0 || at > rowCount())
|
||||
bool WritableProxyModel::insert(int at, const QVariantMap& data)
|
||||
{
|
||||
auto rowCount = this->rowCount();
|
||||
|
||||
if (at < 0 || at > rowCount)
|
||||
return false;
|
||||
|
||||
auto success = insertRows(at, 1);
|
||||
|
||||
if (success)
|
||||
set(at, data);
|
||||
|
||||
return success;
|
||||
}
|
||||
|
||||
return insertRows(at, 1);
|
||||
bool WritableProxyModel::append(const QVariantMap& data)
|
||||
{
|
||||
return insert(rowCount(), data);
|
||||
}
|
||||
|
||||
bool WritableProxyModel::remove(int at)
|
||||
{
|
||||
if (at < 0 || at >= rowCount())
|
||||
{
|
||||
return false;
|
||||
}
|
||||
|
||||
return removeRows(at, 1);
|
||||
}
|
||||
|
@ -343,9 +452,7 @@ bool WritableProxyModel::remove(int at)
|
|||
QVariantMap WritableProxyModel::get(int at) const
|
||||
{
|
||||
if (at < 0 || at >= rowCount())
|
||||
{
|
||||
return {};
|
||||
}
|
||||
|
||||
auto index = this->index(at, 0);
|
||||
auto data = this->itemData(index);
|
||||
|
@ -362,9 +469,7 @@ QVariantMap WritableProxyModel::get(int at) const
|
|||
bool WritableProxyModel::set(int at, const QVariantMap& data)
|
||||
{
|
||||
if (at < 0 || at >= rowCount())
|
||||
{
|
||||
return false;
|
||||
}
|
||||
|
||||
auto index = this->index(at, 0);
|
||||
auto itemData = this->itemData(index);
|
||||
|
@ -381,35 +486,29 @@ bool WritableProxyModel::set(int at, const QVariantMap& data)
|
|||
|
||||
bool WritableProxyModel::dirty() const
|
||||
{
|
||||
return m_dirty;
|
||||
return d->dirty;
|
||||
}
|
||||
|
||||
void WritableProxyModel::setDirty(bool flag)
|
||||
{
|
||||
if(m_dirty == flag || !d->canUpdateDirtyFlag)
|
||||
{
|
||||
if (d->dirty == flag)
|
||||
return;
|
||||
}
|
||||
|
||||
m_dirty = flag;
|
||||
d->dirty = flag;
|
||||
emit dirtyChanged();
|
||||
}
|
||||
|
||||
void WritableProxyModel::setSourceModel(QAbstractItemModel* sourceModel)
|
||||
{
|
||||
if (sourceModel == QAbstractProxyModel::sourceModel())
|
||||
{
|
||||
return;
|
||||
}
|
||||
|
||||
beginResetModel();
|
||||
|
||||
d->clear();
|
||||
|
||||
if (QAbstractProxyModel::sourceModel())
|
||||
{
|
||||
disconnect(QAbstractProxyModel::sourceModel(), nullptr, this, nullptr);
|
||||
}
|
||||
|
||||
setDirty(false);
|
||||
QAbstractProxyModel::setSourceModel(sourceModel);
|
||||
|
@ -423,16 +522,16 @@ void WritableProxyModel::setSourceModel(QAbstractItemModel* sourceModel)
|
|||
d->applyRoleNamesFix();
|
||||
|
||||
d->createProxyToSourceRowMap();
|
||||
connect(sourceModel, &QAbstractItemModel::dataChanged, this, &WritableProxyModel::handleSourceDataChanged);
|
||||
connect(sourceModel, &QAbstractItemModel::rowsAboutToBeInserted, this, &WritableProxyModel::handleRowsAboutToBeInserted);
|
||||
connect(sourceModel, &QAbstractItemModel::rowsInserted, this, &WritableProxyModel::handleRowsInserted);
|
||||
connect(sourceModel, &QAbstractItemModel::rowsAboutToBeRemoved, this, &WritableProxyModel::handleRowsAboutToBeRemoved);
|
||||
connect(sourceModel, &QAbstractItemModel::rowsRemoved, this, &WritableProxyModel::handleRowsRemoved);
|
||||
connect(sourceModel, &QAbstractItemModel::modelAboutToBeReset, this, &WritableProxyModel::handleModelAboutToBeReset);
|
||||
connect(sourceModel, &QAbstractItemModel::modelReset, this, &WritableProxyModel::handleModelReset);
|
||||
connect(sourceModel, &QAbstractItemModel::rowsMoved, this, &WritableProxyModel::handleRowsMoved);
|
||||
connect(sourceModel, &QAbstractItemModel::layoutAboutToBeChanged, this, &WritableProxyModel::handleLayoutAboutToBeChanged);
|
||||
connect(sourceModel, &QAbstractItemModel::layoutChanged, this, &WritableProxyModel::handleModelReset);
|
||||
connect(sourceModel, &QAbstractItemModel::dataChanged, this, &WritableProxyModel::onSourceDataChanged);
|
||||
connect(sourceModel, &QAbstractItemModel::rowsAboutToBeInserted, this, &WritableProxyModel::onRowsAboutToBeInserted);
|
||||
connect(sourceModel, &QAbstractItemModel::rowsInserted, this, &WritableProxyModel::onRowsInserted);
|
||||
connect(sourceModel, &QAbstractItemModel::rowsAboutToBeRemoved, this, &WritableProxyModel::onRowsAboutToBeRemoved);
|
||||
connect(sourceModel, &QAbstractItemModel::rowsRemoved, this, &WritableProxyModel::onRowsRemoved);
|
||||
connect(sourceModel, &QAbstractItemModel::modelAboutToBeReset, this, &WritableProxyModel::onModelAboutToBeReset);
|
||||
connect(sourceModel, &QAbstractItemModel::modelReset, this, &WritableProxyModel::onModelReset);
|
||||
connect(sourceModel, &QAbstractItemModel::rowsMoved, this, &WritableProxyModel::onRowsMoved);
|
||||
connect(sourceModel, &QAbstractItemModel::layoutAboutToBeChanged, this, &WritableProxyModel::onLayoutAboutToBeChanged);
|
||||
connect(sourceModel, &QAbstractItemModel::layoutChanged, this, &WritableProxyModel::onModelReset);
|
||||
|
||||
endResetModel();
|
||||
}
|
||||
|
@ -440,9 +539,7 @@ void WritableProxyModel::setSourceModel(QAbstractItemModel* sourceModel)
|
|||
int WritableProxyModel::columnCount(const QModelIndex& parent) const
|
||||
{
|
||||
if (parent.isValid())
|
||||
{
|
||||
return 0; //no children
|
||||
}
|
||||
|
||||
return 1;
|
||||
}
|
||||
|
@ -450,29 +547,21 @@ int WritableProxyModel::columnCount(const QModelIndex &parent) const
|
|||
int WritableProxyModel::rowCount(const QModelIndex& parent) const
|
||||
{
|
||||
if (parent.isValid())
|
||||
{
|
||||
return 0; //no children
|
||||
}
|
||||
|
||||
if (!sourceModel())
|
||||
{
|
||||
return 0;
|
||||
}
|
||||
|
||||
return sourceModel()->rowCount(parent) + d->insertedRows.count() + d->rowsAboutToBeInserted - d->removedRows.count();
|
||||
return sourceModel()->rowCount(parent) + d->countOffset();
|
||||
}
|
||||
|
||||
QModelIndex WritableProxyModel::index(int row, int column, const QModelIndex& parent) const
|
||||
{
|
||||
if (parent.isValid())
|
||||
{
|
||||
return {}; //no children
|
||||
}
|
||||
|
||||
if (row < 0 || column < 0 || row >= rowCount(parent) || column >= columnCount(parent))
|
||||
{
|
||||
return {};
|
||||
}
|
||||
|
||||
return createIndex(row, column);
|
||||
}
|
||||
|
@ -480,14 +569,10 @@ QModelIndex WritableProxyModel::index(int row, int column, const QModelIndex &pa
|
|||
QModelIndex WritableProxyModel::sibling(int row, int column, const QModelIndex& idx) const
|
||||
{
|
||||
if (!idx.isValid())
|
||||
{
|
||||
return {};
|
||||
}
|
||||
|
||||
if (row < 0 || column < 0 || row >= rowCount(idx.parent()) || column >= columnCount(idx.parent()))
|
||||
{
|
||||
return {};
|
||||
}
|
||||
|
||||
return createIndex(row, column);
|
||||
}
|
||||
|
@ -500,14 +585,14 @@ QModelIndex WritableProxyModel::parent(const QModelIndex &child) const
|
|||
|
||||
QModelIndex WritableProxyModel::mapToSource(const QModelIndex& proxyIndex) const
|
||||
{
|
||||
if(!proxyIndex.isValid())
|
||||
{
|
||||
if (!sourceModel())
|
||||
return {};
|
||||
}
|
||||
|
||||
if(auto row = d->proxyToSourceRow(proxyIndex.row()); row >= 0) {
|
||||
if (!proxyIndex.isValid())
|
||||
return {};
|
||||
|
||||
if (auto row = d->proxyToSourceRow(proxyIndex.row()); row >= 0)
|
||||
return sourceModel()->index(row, proxyIndex.column());
|
||||
}
|
||||
|
||||
return {};
|
||||
}
|
||||
|
@ -515,13 +600,10 @@ QModelIndex WritableProxyModel::mapToSource(const QModelIndex &proxyIndex) const
|
|||
QModelIndex WritableProxyModel::mapFromSource(const QModelIndex& sourceIndex) const
|
||||
{
|
||||
if (!sourceIndex.isValid())
|
||||
{
|
||||
return {};
|
||||
}
|
||||
|
||||
if(auto row = d->sourceToProxyRow(sourceIndex.row()); row >= 0) {
|
||||
if (auto row = d->sourceToProxyRow(sourceIndex.row()); row >= 0)
|
||||
return index(row, sourceIndex.column());
|
||||
}
|
||||
|
||||
return {};
|
||||
}
|
||||
|
@ -529,14 +611,10 @@ QModelIndex WritableProxyModel::mapFromSource(const QModelIndex &sourceIndex) co
|
|||
bool WritableProxyModel::hasChildren(const QModelIndex& parent) const
|
||||
{
|
||||
if (parent.isValid())
|
||||
{
|
||||
return false; //no children
|
||||
}
|
||||
|
||||
if (!sourceModel())
|
||||
{
|
||||
return false;
|
||||
}
|
||||
|
||||
return rowCount(parent) > 0;
|
||||
}
|
||||
|
@ -554,37 +632,31 @@ void WritableProxyModel::revert()
|
|||
QVariant WritableProxyModel::data(const QModelIndex& index, int role) const
|
||||
{
|
||||
if (!index.isValid())
|
||||
{
|
||||
return {};
|
||||
}
|
||||
|
||||
if (!sourceModel())
|
||||
{
|
||||
return {};
|
||||
}
|
||||
|
||||
if(role == -1)
|
||||
{
|
||||
auto roleNames = this->roleNames();
|
||||
if (!roleNames.contains(role))
|
||||
return {};
|
||||
}
|
||||
|
||||
bool found = false;
|
||||
auto data = d->data(index, role, found);
|
||||
|
||||
if (found)
|
||||
{
|
||||
return data;
|
||||
}
|
||||
|
||||
return QAbstractProxyModel::data(index, role);
|
||||
}
|
||||
|
||||
bool WritableProxyModel::setData(const QModelIndex& index, const QVariant& value, int role)
|
||||
{
|
||||
if(!index.isValid())
|
||||
{
|
||||
if (!sourceModel())
|
||||
return false;
|
||||
|
||||
if (!index.isValid())
|
||||
return false;
|
||||
}
|
||||
|
||||
d->setData(index, value, role);
|
||||
|
||||
|
@ -597,9 +669,7 @@ bool WritableProxyModel::setData(const QModelIndex& index, const QVariant& value
|
|||
QMap<int, QVariant> WritableProxyModel::itemData(const QModelIndex& index) const
|
||||
{
|
||||
if (!index.isValid())
|
||||
{
|
||||
return {};
|
||||
}
|
||||
|
||||
return QAbstractProxyModel::itemData(index);
|
||||
}
|
||||
|
@ -607,14 +677,10 @@ QMap<int, QVariant> WritableProxyModel::itemData(const QModelIndex &index) const
|
|||
bool WritableProxyModel::setItemData(const QModelIndex& index, const QMap<int, QVariant>& roles)
|
||||
{
|
||||
if (!index.isValid())
|
||||
{
|
||||
return false;
|
||||
}
|
||||
|
||||
if (QAbstractProxyModel::itemData(index) == roles)
|
||||
{
|
||||
return false;
|
||||
}
|
||||
|
||||
setDirty(true);
|
||||
return QAbstractProxyModel::setItemData(index, roles);
|
||||
|
@ -622,58 +688,39 @@ bool WritableProxyModel::setItemData(const QModelIndex& index, const QMap<int, Q
|
|||
|
||||
bool WritableProxyModel::removeRows(int row, int count, const QModelIndex& parent)
|
||||
{
|
||||
if(row < 0 || count < 1 || row + count > rowCount(parent))
|
||||
{
|
||||
return false;
|
||||
}
|
||||
|
||||
beginRemoveRows(parent, row, row + count - 1);
|
||||
for (int i = row; i < row + count; ++i) {
|
||||
auto sourceIndex = mapToSource(index(i, 0, parent));
|
||||
if(sourceIndex.isValid())
|
||||
{
|
||||
d->removedRows.insert(sourceIndex);
|
||||
}
|
||||
else
|
||||
{
|
||||
d->insertedRows.remove(index(i, 0, parent));
|
||||
}
|
||||
|
||||
d->adjustInsertedRowsBy(i, -1);
|
||||
}
|
||||
|
||||
d->createProxyToSourceRowMap();
|
||||
endRemoveRows();
|
||||
setDirty(true);
|
||||
return true;
|
||||
return d->removeRows(row, count, parent);;
|
||||
}
|
||||
|
||||
bool WritableProxyModel::insertRows(int row, int count, const QModelIndex& parent)
|
||||
{
|
||||
if(row < 0 || count < 1 || row > rowCount(parent))
|
||||
{
|
||||
if (!sourceModel())
|
||||
return false;
|
||||
|
||||
if (row < 0 || count < 1 || row > rowCount(parent))
|
||||
return false;
|
||||
}
|
||||
|
||||
beginInsertRows(parent, row, row + count - 1);
|
||||
d->rowsAboutToBeInserted += count;
|
||||
d->adjustInsertedRowsBy(row, count);
|
||||
for (int i = row; i < row + count; ++i) {
|
||||
d->insertedRows.insert(index(i, 0, parent), {});
|
||||
|
||||
for (int i = row; i < row + count; ++i)
|
||||
{
|
||||
auto index = createIndex(i, 0);
|
||||
d->insertedRows.insert(index, {});
|
||||
}
|
||||
d->rowsAboutToBeInserted -= count;
|
||||
|
||||
d->createProxyToSourceRowMap();
|
||||
endInsertRows();
|
||||
setDirty(true);
|
||||
return true;
|
||||
}
|
||||
|
||||
void WritableProxyModel::handleSourceDataChanged(const QModelIndex& topLeft, const QModelIndex& bottomRight, const QVector<int>& roles)
|
||||
{
|
||||
if(!topLeft.isValid() || !bottomRight.isValid())
|
||||
void WritableProxyModel::onSourceDataChanged(const QModelIndex& topLeft, const QModelIndex& bottomRight, const QVector<int>& roles)
|
||||
{
|
||||
if (!sourceModel())
|
||||
return;
|
||||
|
||||
if (!topLeft.isValid() || !bottomRight.isValid())
|
||||
return;
|
||||
}
|
||||
|
||||
if (!dirty())
|
||||
{
|
||||
|
@ -685,40 +732,41 @@ void WritableProxyModel::handleSourceDataChanged(const QModelIndex& topLeft, con
|
|||
|
||||
auto begin = qMin(topLeft.row(), rowCount() - 1);
|
||||
auto end = qMin(bottomRight.row(), rowCount() - 1);
|
||||
|
||||
for (int row = topLeft.row(); row <= end; ++row)
|
||||
{
|
||||
auto sourceIndex = sourceModel()->index(row, 0);
|
||||
|
||||
if(d->contains(sourceIndex) || d->removedRows.contains(sourceIndex))
|
||||
if (d->contains(sourceIndex, roles) || d->removedRows.contains(sourceIndex))
|
||||
{
|
||||
if (begin < row - 1)
|
||||
{
|
||||
emit dataChanged(mapFromSource(sourceModel()->index(begin, 0)), mapFromSource(sourceModel()->index(row - 1, 0)), roles);
|
||||
}
|
||||
|
||||
begin = row + 1;
|
||||
}
|
||||
|
||||
d->checkForDirtyRemoval(sourceIndex, roles);
|
||||
}
|
||||
|
||||
if (begin <= end)
|
||||
{
|
||||
emit dataChanged(mapFromSource(sourceModel()->index(begin, 0)), mapFromSource(sourceModel()->index(end, 0)), roles);
|
||||
}
|
||||
}
|
||||
|
||||
void WritableProxyModel::handleRowsAboutToBeInserted(const QModelIndex &parent, int start, int end)
|
||||
{
|
||||
if(parent.isValid())
|
||||
void WritableProxyModel::onRowsAboutToBeInserted(const QModelIndex& parent, int start, int end)
|
||||
{
|
||||
if (!sourceModel())
|
||||
return;
|
||||
|
||||
if (parent.isValid())
|
||||
return;
|
||||
}
|
||||
|
||||
if (!dirty())
|
||||
{
|
||||
beginInsertRows({}, start, start + end - start);
|
||||
beginInsertRows({}, start, end);
|
||||
return;
|
||||
}
|
||||
|
||||
auto count = end - start + 1;
|
||||
auto sourceRowRanges = d->sourceRowRangesBetween(start, qMax(end, sourceModel()->rowCount()));
|
||||
if (sourceRowRanges.isEmpty())
|
||||
{
|
||||
|
@ -728,28 +776,46 @@ void WritableProxyModel::handleRowsAboutToBeInserted(const QModelIndex &parent,
|
|||
}
|
||||
|
||||
beginInsertRows({}, sourceRowRanges.first().first, sourceRowRanges.first().first + end - start);
|
||||
d->rowsAboutToBeInserted += end - start;
|
||||
d->adjustInsertedRowsBy(sourceRowRanges.first().second, end - start);
|
||||
d->rowsAboutToBeInserted -= end - start;
|
||||
d->adjustInsertedRowsBy(sourceRowRanges.first().second, count);
|
||||
}
|
||||
|
||||
void WritableProxyModel::handleRowsInserted(const QModelIndex &parent, int first, int last)
|
||||
{
|
||||
if(parent.isValid())
|
||||
void WritableProxyModel::onRowsInserted(const QModelIndex& parent, int first, int last)
|
||||
{
|
||||
if (!sourceModel())
|
||||
return;
|
||||
|
||||
if (parent.isValid())
|
||||
return;
|
||||
}
|
||||
|
||||
d->createProxyToSourceRowMap();
|
||||
endInsertRows();
|
||||
|
||||
if (!sourceModel())
|
||||
return;
|
||||
|
||||
int rowToRemove = first;
|
||||
for (int row = first; row <= last; ++row)
|
||||
{
|
||||
if (d->insertedRows.contains(index(rowToRemove, 0)))
|
||||
{
|
||||
if (itemData(index(rowToRemove, 0)) == sourceModel()->itemData(sourceModel()->index(row, 0)))
|
||||
{
|
||||
//rowToRemove remains in place if the proxy row is removed
|
||||
d->removeRows(rowToRemove, 1, {});
|
||||
continue;
|
||||
}
|
||||
rowToRemove++;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
void WritableProxyModel::handleRowsAboutToBeRemoved(const QModelIndex &parent, int start, int end)
|
||||
{
|
||||
if(parent.isValid())
|
||||
void WritableProxyModel::onRowsAboutToBeRemoved(const QModelIndex& parent, int start, int end)
|
||||
{
|
||||
if (!sourceModel())
|
||||
return;
|
||||
|
||||
if (parent.isValid())
|
||||
return;
|
||||
}
|
||||
|
||||
for (int row = start; row <= end; ++row)
|
||||
{
|
||||
|
@ -760,6 +826,7 @@ void WritableProxyModel::handleRowsAboutToBeRemoved(const QModelIndex &parent, i
|
|||
d->moveFromCacheToInserted(sourceIndex);
|
||||
continue;
|
||||
}
|
||||
}
|
||||
|
||||
auto sourceRemoveRanges = d->sourceRowRangesBetween(start, end);
|
||||
for (auto& sourceRemoveRange : sourceRemoveRanges)
|
||||
|
@ -767,29 +834,23 @@ void WritableProxyModel::handleRowsAboutToBeRemoved(const QModelIndex &parent, i
|
|||
auto proxyStart = sourceRemoveRange.first;
|
||||
auto proxyEnd = sourceRemoveRange.second;
|
||||
if (proxyStart == -1 || proxyEnd == -1)
|
||||
{
|
||||
continue;
|
||||
}
|
||||
|
||||
d->canUpdateDirtyFlag = false;
|
||||
removeRows(proxyStart, proxyEnd - proxyStart + 1);
|
||||
d->canUpdateDirtyFlag = true;
|
||||
}
|
||||
d->removeRows(proxyStart, proxyEnd - proxyStart + 1);
|
||||
}
|
||||
}
|
||||
|
||||
void WritableProxyModel::handleRowsRemoved(const QModelIndex &parent, int first, int last)
|
||||
void WritableProxyModel::onRowsRemoved(const QModelIndex& parent, int first, int last)
|
||||
{
|
||||
if (parent.isValid())
|
||||
{
|
||||
return;
|
||||
}
|
||||
|
||||
d->clearInvalidatedCache();
|
||||
d->createProxyToSourceRowMap();
|
||||
d->checkForDirtyRemoval({}, {});
|
||||
}
|
||||
|
||||
void WritableProxyModel::handleModelAboutToBeReset()
|
||||
void WritableProxyModel::onModelAboutToBeReset()
|
||||
{
|
||||
beginResetModel();
|
||||
for (auto iter = d->cache.begin(); iter != d->cache.end();)
|
||||
|
@ -798,9 +859,10 @@ void WritableProxyModel::handleModelAboutToBeReset()
|
|||
iter++;
|
||||
d->moveFromCacheToInserted(key);
|
||||
}
|
||||
d->alignInsertedRowsAtBeginning();
|
||||
}
|
||||
|
||||
void WritableProxyModel::handleModelReset()
|
||||
void WritableProxyModel::onModelReset()
|
||||
{
|
||||
d->clearInvalidatedCache();
|
||||
d->createProxyToSourceRowMap();
|
||||
|
@ -808,31 +870,30 @@ void WritableProxyModel::handleModelReset()
|
|||
endResetModel();
|
||||
}
|
||||
|
||||
void WritableProxyModel::handleRowsMoved(const QModelIndex &sourceParent, int sourceStart, int sourceEnd, const QModelIndex &destinationParent, int destinationRow)
|
||||
void WritableProxyModel::onRowsMoved(const QModelIndex& sourceParent, int sourceStart, int sourceEnd, const QModelIndex& destinationParent, int destinationRow)
|
||||
{
|
||||
if(sourceParent.isValid() || destinationParent.isValid())
|
||||
{
|
||||
return;
|
||||
}
|
||||
|
||||
beginResetModel();
|
||||
emit layoutAboutToBeChanged();
|
||||
d->clearInvalidatedCache();
|
||||
d->createProxyToSourceRowMap();
|
||||
endResetModel();
|
||||
emit layoutChanged();
|
||||
}
|
||||
|
||||
void WritableProxyModel::handleLayoutAboutToBeChanged(const QList<QPersistentModelIndex> &sourceParents, QAbstractItemModel::LayoutChangeHint hint)
|
||||
void WritableProxyModel::onLayoutAboutToBeChanged(const QList<QPersistentModelIndex>& sourceParents, QAbstractItemModel::LayoutChangeHint hint)
|
||||
{
|
||||
if (!sourceParents.isEmpty())
|
||||
{
|
||||
return;
|
||||
}
|
||||
beginResetModel();
|
||||
|
||||
emit layoutAboutToBeChanged();
|
||||
}
|
||||
|
||||
void WritableProxyModel::handleLayoutChanged(const QList<QPersistentModelIndex> &sourceParents, QAbstractItemModel::LayoutChangeHint hint)
|
||||
void WritableProxyModel::onLayoutChanged(const QList<QPersistentModelIndex>& sourceParents, QAbstractItemModel::LayoutChangeHint hint)
|
||||
{
|
||||
d->clearInvalidatedCache();
|
||||
d->createProxyToSourceRowMap();
|
||||
endResetModel();
|
||||
emit layoutChanged();
|
||||
}
|
||||
|
|
File diff suppressed because it is too large
Load Diff
Loading…
Reference in New Issue