1// Copyright (C) 2017 Ford Motor Company
2// SPDX-License-Identifier: LicenseRef-Qt-Commercial OR LGPL-3.0-only OR GPL-2.0-only OR GPL-3.0-only
3
4#include "qremoteobjectabstractitemmodeladapter_p.h"
5
6#include <QtCore/qitemselectionmodel.h>
7
8inline QList<QModelRoleData> createModelRoleData(const QList<int> &roles)
9{
10 QList<QModelRoleData> roleData;
11 roleData.reserve(asize: roles.size());
12 for (int role : roles)
13 roleData.emplace_back(args&: role);
14 return roleData;
15}
16
17// consider evaluating performance difference with item data
18inline QVariantList collectData(const QModelIndex &index, const QAbstractItemModel *model,
19 QModelRoleDataSpan roleDataSpan)
20{
21 model->multiData(index, roleDataSpan);
22
23 QVariantList result;
24 result.reserve(asize: roleDataSpan.size());
25 for (auto &roleData : roleDataSpan)
26 result.push_back(t: std::move(roleData.data()));
27
28 return result;
29}
30
31inline QList<int> filterRoles(const QList<int> &roles, const QList<int> &availableRoles)
32{
33 if (roles.isEmpty())
34 return availableRoles;
35
36 QList<int> neededRoles;
37 for (int inRole : roles) {
38 for (int availableRole : availableRoles)
39 if (inRole == availableRole) {
40 neededRoles << inRole;
41 continue;
42 }
43 }
44 return neededRoles;
45}
46
47QAbstractItemModelSourceAdapter::QAbstractItemModelSourceAdapter(QAbstractItemModel *obj, QItemSelectionModel *sel, const QList<int> &roles)
48 : QObject(obj),
49 m_model(obj),
50 m_availableRoles(roles)
51{
52 QAbstractItemModelSourceAdapter::registerTypes();
53 m_selectionModel = sel;
54 connect(sender: m_model, signal: &QAbstractItemModel::dataChanged, context: this, slot: &QAbstractItemModelSourceAdapter::sourceDataChanged);
55 connect(sender: m_model, signal: &QAbstractItemModel::rowsInserted, context: this, slot: &QAbstractItemModelSourceAdapter::sourceRowsInserted);
56 connect(sender: m_model, signal: &QAbstractItemModel::columnsInserted, context: this, slot: &QAbstractItemModelSourceAdapter::sourceColumnsInserted);
57 connect(sender: m_model, signal: &QAbstractItemModel::rowsRemoved, context: this, slot: &QAbstractItemModelSourceAdapter::sourceRowsRemoved);
58 connect(sender: m_model, signal: &QAbstractItemModel::rowsMoved, context: this, slot: &QAbstractItemModelSourceAdapter::sourceRowsMoved);
59 connect(sender: m_model, signal: &QAbstractItemModel::layoutChanged, context: this, slot: &QAbstractItemModelSourceAdapter::sourceLayoutChanged);
60 if (m_selectionModel)
61 connect(sender: m_selectionModel, signal: &QItemSelectionModel::currentChanged, context: this, slot: &QAbstractItemModelSourceAdapter::sourceCurrentChanged);
62}
63
64void QAbstractItemModelSourceAdapter::registerTypes()
65{
66 static bool alreadyRegistered = false;
67 if (alreadyRegistered)
68 return;
69
70 alreadyRegistered = true;
71 qRegisterMetaType<QAbstractItemModel*>();
72 qRegisterMetaType<Qt::Orientation>();
73 qRegisterMetaType<QList<Qt::Orientation>>();
74 qRegisterMetaType<QtPrivate::ModelIndex>();
75 qRegisterMetaType<QtPrivate::IndexList>();
76 qRegisterMetaType<QtPrivate::DataEntries>();
77 qRegisterMetaType<QtPrivate::MetaAndDataEntries>();
78 qRegisterMetaType<QItemSelectionModel::SelectionFlags>();
79 qRegisterMetaType<QSize>();
80 qRegisterMetaType<QIntHash>();
81 qRegisterMetaType<QList<int>>();
82}
83
84QItemSelectionModel* QAbstractItemModelSourceAdapter::selectionModel() const
85{
86 return m_selectionModel;
87}
88
89QSize QAbstractItemModelSourceAdapter::replicaSizeRequest(QtPrivate::IndexList parentList)
90{
91 QModelIndex parent = toQModelIndex(list: parentList, model: m_model);
92 const int rowCount = m_model->rowCount(parent);
93 const int columnCount = m_model->columnCount(parent);
94 const QSize size(columnCount, rowCount);
95 qCDebug(QT_REMOTEOBJECT_MODELS) << "parent" << parentList << "size=" << size;
96 return size;
97}
98
99void QAbstractItemModelSourceAdapter::replicaSetData(const QtPrivate::IndexList &index, const QVariant &value, int role)
100{
101 const QModelIndex modelIndex = toQModelIndex(list: index, model: m_model);
102 Q_ASSERT(modelIndex.isValid());
103 const bool result = m_model->setData(index: modelIndex, value, role);
104 Q_ASSERT(result);
105 Q_UNUSED(result)
106}
107
108QtPrivate::DataEntries QAbstractItemModelSourceAdapter::replicaRowRequest(QtPrivate::IndexList start, QtPrivate::IndexList end, QList<int> roles)
109{
110 qCDebug(QT_REMOTEOBJECT_MODELS) << "Requested rows" << "start=" << start << "end=" << end << "roles=" << roles;
111
112 Q_ASSERT(start.size() == end.size());
113 Q_ASSERT(!start.isEmpty());
114
115 if (roles.isEmpty())
116 roles << m_availableRoles;
117
118 QtPrivate::IndexList parentList = start;
119 Q_ASSERT(!parentList.isEmpty());
120 parentList.pop_back();
121 QModelIndex parent = toQModelIndex(list: parentList, model: m_model);
122
123 const int startRow = start.last().row;
124 const int startColumn = start.last().column;
125 const int rowCount = m_model->rowCount(parent);
126 const int columnCount = m_model->columnCount(parent);
127
128 QtPrivate::DataEntries entries;
129 if (rowCount <= 0)
130 return entries;
131 const int endRow = std::min(a: end.last().row, b: rowCount - 1);
132 const int endColumn = std::min(a: end.last().column, b: columnCount - 1);
133 Q_ASSERT_X(endRow >= 0 && endRow < rowCount, __FUNCTION__, qPrintable(QString(QLatin1String("0 <= %1 < %2")).arg(endRow).arg(rowCount)));
134 Q_ASSERT_X(endColumn >= 0 && endColumn < columnCount, __FUNCTION__, qPrintable(QString(QLatin1String("0 <= %1 < %2")).arg(endColumn).arg(columnCount)));
135
136 auto roleData = createModelRoleData(roles);
137 for (int row = startRow; row <= endRow; ++row) {
138 for (int column = startColumn; column <= endColumn; ++column) {
139 const QModelIndex current = m_model->index(row, column, parent);
140 Q_ASSERT(current.isValid());
141 const QtPrivate::IndexList currentList = QtPrivate::toModelIndexList(index: current, model: m_model);
142 const QVariantList data = collectData(index: current, model: m_model, roleDataSpan: roleData);
143 const bool hasChildren = m_model->hasChildren(parent: current);
144 const Qt::ItemFlags flags = m_model->flags(index: current);
145 qCDebug(QT_REMOTEOBJECT_MODELS) << Q_FUNC_INFO << "current=" << currentList << "data=" << data;
146 entries.data << QtPrivate::IndexValuePair(currentList, data, hasChildren, flags);
147 }
148 }
149 return entries;
150}
151
152QtPrivate::MetaAndDataEntries QAbstractItemModelSourceAdapter::replicaCacheRequest(size_t size, const QList<int> &roles)
153{
154 QtPrivate::MetaAndDataEntries res;
155 res.roles = roles.isEmpty() ? m_availableRoles : roles;
156 res.data = fetchTree(parent: QModelIndex {}, size, roles: res.roles);
157 const int rowCount = m_model->rowCount(parent: QModelIndex{});
158 const int columnCount = m_model->columnCount(parent: QModelIndex{});
159 res.size = QSize{columnCount, rowCount};
160 return res;
161}
162
163QVariantList QAbstractItemModelSourceAdapter::replicaHeaderRequest(QList<Qt::Orientation> orientations, QList<int> sections, QList<int> roles)
164{
165 qCDebug(QT_REMOTEOBJECT_MODELS) << Q_FUNC_INFO << "orientations=" << orientations << "sections=" << sections << "roles=" << roles;
166 QVariantList data;
167 Q_ASSERT(roles.size() == sections.size());
168 Q_ASSERT(roles.size() == orientations.size());
169 for (int i = 0; i < roles.size(); ++i) {
170 data << m_model->headerData(section: sections[i], orientation: orientations[i], role: roles[i]);
171 }
172 return data;
173}
174
175void QAbstractItemModelSourceAdapter::replicaSetCurrentIndex(QtPrivate::IndexList index, QItemSelectionModel::SelectionFlags command)
176{
177 if (m_selectionModel)
178 m_selectionModel->setCurrentIndex(index: toQModelIndex(list: index, model: m_model), command);
179}
180
181void QAbstractItemModelSourceAdapter::sourceDataChanged(const QModelIndex & topLeft, const QModelIndex & bottomRight, const QList<int> & roles) const
182{
183 QList<int> neededRoles = filterRoles(roles, availableRoles: availableRoles());
184 if (neededRoles.isEmpty()) {
185 qCDebug(QT_REMOTEOBJECT_MODELS) << Q_FUNC_INFO << "Needed roles is empty!";
186 return;
187 }
188 Q_ASSERT(topLeft.isValid());
189 Q_ASSERT(bottomRight.isValid());
190 QtPrivate::IndexList start = QtPrivate::toModelIndexList(index: topLeft, model: m_model);
191 QtPrivate::IndexList end = QtPrivate::toModelIndexList(index: bottomRight, model: m_model);
192 qCDebug(QT_REMOTEOBJECT_MODELS) << Q_FUNC_INFO << "start=" << start << "end=" << end << "neededRoles=" << neededRoles;
193 emit dataChanged(topLeft: start, bottomRight: end, roles: neededRoles);
194}
195
196void QAbstractItemModelSourceAdapter::sourceRowsInserted(const QModelIndex & parent, int start, int end)
197{
198 QtPrivate::IndexList parentList = QtPrivate::toModelIndexList(index: parent, model: m_model);
199 emit rowsInserted(parent: parentList, start, end);
200}
201
202void QAbstractItemModelSourceAdapter::sourceColumnsInserted(const QModelIndex & parent, int start, int end)
203{
204 QtPrivate::IndexList parentList = QtPrivate::toModelIndexList(index: parent, model: m_model);
205 emit columnsInserted(parent: parentList, start, end);
206}
207
208void QAbstractItemModelSourceAdapter::sourceRowsRemoved(const QModelIndex & parent, int start, int end)
209{
210 QtPrivate::IndexList parentList = QtPrivate::toModelIndexList(index: parent, model: m_model);
211 emit rowsRemoved(parent: parentList, start, end);
212}
213
214void QAbstractItemModelSourceAdapter::sourceRowsMoved(const QModelIndex & sourceParent, int sourceRow, int count, const QModelIndex & destinationParent, int destinationChild) const
215{
216 emit rowsMoved(sourceParent: QtPrivate::toModelIndexList(index: sourceParent, model: m_model), sourceRow, count, destinationParent: QtPrivate::toModelIndexList(index: destinationParent, model: m_model), destinationChild);
217}
218
219void QAbstractItemModelSourceAdapter::sourceCurrentChanged(const QModelIndex & current, const QModelIndex & previous)
220{
221 QtPrivate::IndexList currentIndex = QtPrivate::toModelIndexList(index: current, model: m_model);
222 QtPrivate::IndexList previousIndex = QtPrivate::toModelIndexList(index: previous, model: m_model);
223 qCDebug(QT_REMOTEOBJECT_MODELS) << Q_FUNC_INFO << "current=" << currentIndex << "previous=" << previousIndex;
224 emit currentChanged(current: currentIndex, previous: previousIndex);
225}
226
227void QAbstractItemModelSourceAdapter::sourceLayoutChanged(const QList<QPersistentModelIndex> &parents, QAbstractItemModel::LayoutChangeHint hint)
228{
229 QtPrivate::IndexList indexes;
230 for (const QPersistentModelIndex &idx : parents)
231 indexes << QtPrivate::toModelIndexList(index: (QModelIndex)idx, model: m_model);
232 emit layoutChanged(parents: indexes, hint);
233}
234
235QList<QtPrivate::IndexValuePair> QAbstractItemModelSourceAdapter::fetchTree(const QModelIndex &parent, size_t &size, const QList<int> &roles)
236{
237 QList<QtPrivate::IndexValuePair> entries;
238 const int rowCount = m_model->rowCount(parent);
239 const int columnCount = m_model->columnCount(parent);
240 if (!columnCount || !rowCount)
241 return entries;
242 entries.reserve(asize: std::min(a: rowCount * columnCount, b: int(size)));
243 auto roleData = createModelRoleData(roles);
244 for (int row = 0; row < rowCount && size > 0; ++row)
245 for (int column = 0; column < columnCount && size > 0; ++column) {
246 const auto index = m_model->index(row, column, parent);
247 const QtPrivate::IndexList currentList = QtPrivate::toModelIndexList(index, model: m_model);
248 const QVariantList data = collectData(index, model: m_model, roleDataSpan: roleData);
249 const bool hasChildren = m_model->hasChildren(parent: index);
250 const Qt::ItemFlags flags = m_model->flags(index);
251 int rc = m_model->rowCount(parent: index);
252 int cc = m_model->columnCount(parent: index);
253 QtPrivate::IndexValuePair rowData(currentList, data, hasChildren, flags, QSize{cc, rc});
254 --size;
255 if (hasChildren)
256 rowData.children = fetchTree(parent: index, size, roles);
257 entries.push_back(t: rowData);
258 }
259 return entries;
260}
261

source code of qtremoteobjects/src/remoteobjects/qremoteobjectabstractitemmodeladapter.cpp