1/****************************************************************************
2**
3** Copyright (C) 2017 Ford Motor Company
4** Contact: https://www.qt.io/licensing/
5**
6** This file is part of the QtRemoteObjects module of the Qt Toolkit.
7**
8** $QT_BEGIN_LICENSE:LGPL$
9** Commercial License Usage
10** Licensees holding valid commercial Qt licenses may use this file in
11** accordance with the commercial license agreement provided with the
12** Software or, alternatively, in accordance with the terms contained in
13** a written agreement between you and The Qt Company. For licensing terms
14** and conditions see https://www.qt.io/terms-conditions. For further
15** information use the contact form at https://www.qt.io/contact-us.
16**
17** GNU Lesser General Public License Usage
18** Alternatively, this file may be used under the terms of the GNU Lesser
19** General Public License version 3 as published by the Free Software
20** Foundation and appearing in the file LICENSE.LGPL3 included in the
21** packaging of this file. Please review the following information to
22** ensure the GNU Lesser General Public License version 3 requirements
23** will be met: https://www.gnu.org/licenses/lgpl-3.0.html.
24**
25** GNU General Public License Usage
26** Alternatively, this file may be used under the terms of the GNU
27** General Public License version 2.0 or (at your option) the GNU General
28** Public license version 3 or any later version approved by the KDE Free
29** Qt Foundation. The licenses are as published by the Free Software
30** Foundation and appearing in the file LICENSE.GPL2 and LICENSE.GPL3
31** included in the packaging of this file. Please review the following
32** information to ensure the GNU General Public License requirements will
33** be met: https://www.gnu.org/licenses/gpl-2.0.html and
34** https://www.gnu.org/licenses/gpl-3.0.html.
35**
36** $QT_END_LICENSE$
37**
38****************************************************************************/
39
40#include "qremoteobjectabstractitemmodeladapter_p.h"
41
42#include <QtCore/qitemselectionmodel.h>
43
44// consider evaluating performance difference with item data
45inline QVariantList collectData(const QModelIndex &index, const QAbstractItemModel *model, const QVector<int> &roles)
46{
47 QVariantList result;
48 result.reserve(alloc: roles.size());
49 for (int role : roles)
50 result << model->data(index, role);
51 return result;
52}
53
54inline QVector<int> filterRoles(const QVector<int> &roles, const QVector<int> &availableRoles)
55{
56 if (roles.isEmpty())
57 return availableRoles;
58
59 QVector<int> neededRoles;
60 for (int inRole : roles) {
61 for (int availableRole : availableRoles)
62 if (inRole == availableRole) {
63 neededRoles << inRole;
64 continue;
65 }
66 }
67 return neededRoles;
68}
69
70QAbstractItemModelSourceAdapter::QAbstractItemModelSourceAdapter(QAbstractItemModel *obj, QItemSelectionModel *sel, const QVector<int> &roles)
71 : QObject(obj),
72 m_model(obj),
73 m_availableRoles(roles)
74{
75 QAbstractItemModelSourceAdapter::registerTypes();
76 m_selectionModel = sel;
77 connect(sender: m_model, signal: &QAbstractItemModel::dataChanged, receiver: this, slot: &QAbstractItemModelSourceAdapter::sourceDataChanged);
78 connect(sender: m_model, signal: &QAbstractItemModel::rowsInserted, receiver: this, slot: &QAbstractItemModelSourceAdapter::sourceRowsInserted);
79 connect(sender: m_model, signal: &QAbstractItemModel::columnsInserted, receiver: this, slot: &QAbstractItemModelSourceAdapter::sourceColumnsInserted);
80 connect(sender: m_model, signal: &QAbstractItemModel::rowsRemoved, receiver: this, slot: &QAbstractItemModelSourceAdapter::sourceRowsRemoved);
81 connect(sender: m_model, signal: &QAbstractItemModel::rowsMoved, receiver: this, slot: &QAbstractItemModelSourceAdapter::sourceRowsMoved);
82 connect(sender: m_model, signal: &QAbstractItemModel::layoutChanged, receiver: this, slot: &QAbstractItemModelSourceAdapter::sourceLayoutChanged);
83 if (m_selectionModel)
84 connect(sender: m_selectionModel, signal: &QItemSelectionModel::currentChanged, receiver: this, slot: &QAbstractItemModelSourceAdapter::sourceCurrentChanged);
85}
86
87void QAbstractItemModelSourceAdapter::registerTypes()
88{
89 static bool alreadyRegistered = false;
90 if (alreadyRegistered)
91 return;
92
93 alreadyRegistered = true;
94 qRegisterMetaType<QAbstractItemModel*>();
95 qRegisterMetaType<Qt::Orientation>();
96 qRegisterMetaType<QVector<Qt::Orientation> >();
97 qRegisterMetaTypeStreamOperators<ModelIndex>();
98 qRegisterMetaTypeStreamOperators<IndexList>();
99 qRegisterMetaTypeStreamOperators<DataEntries>();
100 qRegisterMetaTypeStreamOperators<MetaAndDataEntries>();
101 qRegisterMetaTypeStreamOperators<Qt::Orientation>();
102 qRegisterMetaTypeStreamOperators<QVector<Qt::Orientation> >();
103 qRegisterMetaType<QItemSelectionModel::SelectionFlags>();
104 qRegisterMetaTypeStreamOperators<QItemSelectionModel::SelectionFlags>();
105 qRegisterMetaType<QSize>();
106 qRegisterMetaType<QIntHash>();
107 qRegisterMetaTypeStreamOperators<QIntHash>();
108}
109
110QItemSelectionModel* QAbstractItemModelSourceAdapter::selectionModel() const
111{
112 return m_selectionModel;
113}
114
115QSize QAbstractItemModelSourceAdapter::replicaSizeRequest(IndexList parentList)
116{
117 QModelIndex parent = toQModelIndex(list: parentList, model: m_model);
118 const int rowCount = m_model->rowCount(parent);
119 const int columnCount = m_model->columnCount(parent);
120 const QSize size(columnCount, rowCount);
121 qCDebug(QT_REMOTEOBJECT_MODELS) << "parent" << parentList << "size=" << size;
122 return size;
123}
124
125void QAbstractItemModelSourceAdapter::replicaSetData(const IndexList &index, const QVariant &value, int role)
126{
127 const QModelIndex modelIndex = toQModelIndex(list: index, model: m_model);
128 Q_ASSERT(modelIndex.isValid());
129 const bool result = m_model->setData(index: modelIndex, value, role);
130 Q_ASSERT(result);
131 Q_UNUSED(result);
132}
133
134DataEntries QAbstractItemModelSourceAdapter::replicaRowRequest(IndexList start, IndexList end, QVector<int> roles)
135{
136 qCDebug(QT_REMOTEOBJECT_MODELS) << "Requested rows" << "start=" << start << "end=" << end << "roles=" << roles;
137
138 Q_ASSERT(start.size() == end.size());
139 Q_ASSERT(!start.isEmpty());
140
141 if (roles.isEmpty())
142 roles << m_availableRoles;
143
144 IndexList parentList = start;
145 Q_ASSERT(!parentList.isEmpty());
146 parentList.pop_back();
147 QModelIndex parent = toQModelIndex(list: parentList, model: m_model);
148
149 const int startRow = start.last().row;
150 const int startColumn = start.last().column;
151 const int rowCount = m_model->rowCount(parent);
152 const int columnCount = m_model->columnCount(parent);
153
154 DataEntries entries;
155 if (rowCount <= 0)
156 return entries;
157 const int endRow = std::min(a: end.last().row, b: rowCount - 1);
158 const int endColumn = std::min(a: end.last().column, b: columnCount - 1);
159 Q_ASSERT_X(endRow >= 0 && endRow < rowCount, __FUNCTION__, qPrintable(QString(QLatin1String("0 <= %1 < %2")).arg(endRow).arg(rowCount)));
160 Q_ASSERT_X(endColumn >= 0 && endColumn < columnCount, __FUNCTION__, qPrintable(QString(QLatin1String("0 <= %1 < %2")).arg(endColumn).arg(columnCount)));
161
162 for (int row = startRow; row <= endRow; ++row) {
163 for (int column = startColumn; column <= endColumn; ++column) {
164 const QModelIndex current = m_model->index(row, column, parent);
165 Q_ASSERT(current.isValid());
166 const IndexList currentList = toModelIndexList(index: current, model: m_model);
167 const QVariantList data = collectData(index: current, model: m_model, roles);
168 const bool hasChildren = m_model->hasChildren(parent: current);
169 const Qt::ItemFlags flags = m_model->flags(index: current);
170 qCDebug(QT_REMOTEOBJECT_MODELS) << Q_FUNC_INFO << "current=" << currentList << "data=" << data;
171 entries.data << IndexValuePair(currentList, data, hasChildren, flags);
172 }
173 }
174 return entries;
175}
176
177MetaAndDataEntries QAbstractItemModelSourceAdapter::replicaCacheRequest(size_t size, const QVector<int> &roles)
178{
179 MetaAndDataEntries res;
180 res.roles = roles.isEmpty() ? m_availableRoles : roles;
181 res.data = fetchTree(parent: QModelIndex {}, size, roles: res.roles);
182 const int rowCount = m_model->rowCount(parent: QModelIndex{});
183 const int columnCount = m_model->columnCount(parent: QModelIndex{});
184 res.size = QSize{columnCount, rowCount};
185 return res;
186}
187
188QVariantList QAbstractItemModelSourceAdapter::replicaHeaderRequest(QVector<Qt::Orientation> orientations, QVector<int> sections, QVector<int> roles)
189{
190 qCDebug(QT_REMOTEOBJECT_MODELS) << Q_FUNC_INFO << "orientations=" << orientations << "sections=" << sections << "roles=" << roles;
191 QVariantList data;
192 Q_ASSERT(roles.size() == sections.size());
193 Q_ASSERT(roles.size() == orientations.size());
194 for (int i = 0; i < roles.size(); ++i) {
195 data << m_model->headerData(section: sections[i], orientation: orientations[i], role: roles[i]);
196 }
197 return data;
198}
199
200void QAbstractItemModelSourceAdapter::replicaSetCurrentIndex(IndexList index, QItemSelectionModel::SelectionFlags command)
201{
202 if (m_selectionModel)
203 m_selectionModel->setCurrentIndex(index: toQModelIndex(list: index, model: m_model), command);
204}
205
206void QAbstractItemModelSourceAdapter::sourceDataChanged(const QModelIndex & topLeft, const QModelIndex & bottomRight, const QVector<int> & roles) const
207{
208 QVector<int> neededRoles = filterRoles(roles, availableRoles: availableRoles());
209 if (neededRoles.isEmpty()) {
210 qCDebug(QT_REMOTEOBJECT_MODELS) << Q_FUNC_INFO << "Needed roles is empty!";
211 return;
212 }
213 Q_ASSERT(topLeft.isValid());
214 Q_ASSERT(bottomRight.isValid());
215 IndexList start = toModelIndexList(index: topLeft, model: m_model);
216 IndexList end = toModelIndexList(index: bottomRight, model: m_model);
217 qCDebug(QT_REMOTEOBJECT_MODELS) << Q_FUNC_INFO << "start=" << start << "end=" << end << "neededRoles=" << neededRoles;
218 emit dataChanged(topLeft: start, bottomRight: end, roles: neededRoles);
219}
220
221void QAbstractItemModelSourceAdapter::sourceRowsInserted(const QModelIndex & parent, int start, int end)
222{
223 IndexList parentList = toModelIndexList(index: parent, model: m_model);
224 emit rowsInserted(parent: parentList, start, end);
225}
226
227void QAbstractItemModelSourceAdapter::sourceColumnsInserted(const QModelIndex & parent, int start, int end)
228{
229 IndexList parentList = toModelIndexList(index: parent, model: m_model);
230 emit columnsInserted(parent: parentList, start, end);
231}
232
233void QAbstractItemModelSourceAdapter::sourceRowsRemoved(const QModelIndex & parent, int start, int end)
234{
235 IndexList parentList = toModelIndexList(index: parent, model: m_model);
236 emit rowsRemoved(parent: parentList, start, end);
237}
238
239void QAbstractItemModelSourceAdapter::sourceRowsMoved(const QModelIndex & sourceParent, int sourceRow, int count, const QModelIndex & destinationParent, int destinationChild) const
240{
241 emit rowsMoved(sourceParent: toModelIndexList(index: sourceParent, model: m_model), sourceRow, count, destinationParent: toModelIndexList(index: destinationParent, model: m_model), destinationChild);
242}
243
244void QAbstractItemModelSourceAdapter::sourceCurrentChanged(const QModelIndex & current, const QModelIndex & previous)
245{
246 IndexList currentIndex = toModelIndexList(index: current, model: m_model);
247 IndexList previousIndex = toModelIndexList(index: previous, model: m_model);
248 qCDebug(QT_REMOTEOBJECT_MODELS) << Q_FUNC_INFO << "current=" << currentIndex << "previous=" << previousIndex;
249 emit currentChanged(current: currentIndex, previous: previousIndex);
250}
251
252void QAbstractItemModelSourceAdapter::sourceLayoutChanged(const QList<QPersistentModelIndex> &parents, QAbstractItemModel::LayoutChangeHint hint)
253{
254 IndexList indexes;
255 for (const QPersistentModelIndex &idx : parents)
256 indexes << toModelIndexList(index: (QModelIndex)idx, model: m_model);
257 emit layoutChanged(parents: indexes, hint);
258}
259
260QVector<IndexValuePair> QAbstractItemModelSourceAdapter::fetchTree(const QModelIndex &parent, size_t &size, const QVector<int> &roles)
261{
262 QVector<IndexValuePair> entries;
263 const int rowCount = m_model->rowCount(parent);
264 const int columnCount = m_model->columnCount(parent);
265 if (!columnCount || !rowCount)
266 return entries;
267 entries.reserve(asize: std::min(a: rowCount * columnCount, b: int(size)));
268 for (int row = 0; row < rowCount && size > 0; ++row)
269 for (int column = 0; column < columnCount && size > 0; ++column) {
270 const auto index = m_model->index(row, column, parent);
271 const IndexList currentList = toModelIndexList(index, model: m_model);
272 const QVariantList data = collectData(index, model: m_model, roles);
273 const bool hasChildren = m_model->hasChildren(parent: index);
274 const Qt::ItemFlags flags = m_model->flags(index);
275 int rc = m_model->rowCount(parent: index);
276 int cc = m_model->columnCount(parent: index);
277 IndexValuePair rowData(currentList, data, hasChildren, flags, QSize{cc, rc});
278 --size;
279 if (hasChildren)
280 rowData.children = fetchTree(parent: index, size, roles);
281 entries.push_back(t: rowData);
282 }
283 return entries;
284}
285

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