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
collectData(const QModelIndex & index,const QAbstractItemModel * model,const QVector<int> & roles)45 inline QVariantList collectData(const QModelIndex &index, const QAbstractItemModel *model, const QVector<int> &roles)
46 {
47     QVariantList result;
48     result.reserve(roles.size());
49     for (int role : roles)
50         result << model->data(index, role);
51     return result;
52 }
53 
filterRoles(const QVector<int> & roles,const QVector<int> & availableRoles)54 inline 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 
QAbstractItemModelSourceAdapter(QAbstractItemModel * obj,QItemSelectionModel * sel,const QVector<int> & roles)70 QAbstractItemModelSourceAdapter::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(m_model, SIGNAL(dataChanged(QModelIndex,QModelIndex,QVector<int>)), this, SLOT(sourceDataChanged(QModelIndex,QModelIndex,QVector<int>)));
78     connect(m_model, SIGNAL(rowsInserted(QModelIndex,int,int)), this, SLOT(sourceRowsInserted(QModelIndex,int,int)));
79     connect(m_model, SIGNAL(columnsInserted(QModelIndex,int,int)), this, SLOT(sourceColumnsInserted(QModelIndex,int,int)));
80     connect(m_model, SIGNAL(rowsRemoved(QModelIndex,int,int)), this, SLOT(sourceRowsRemoved(QModelIndex,int,int)));
81     connect(m_model, SIGNAL(rowsMoved(QModelIndex,int,int,QModelIndex,int)), this, SLOT(sourceRowsMoved(QModelIndex,int,int,QModelIndex,int)));
82     if (m_selectionModel)
83         connect(m_selectionModel, SIGNAL(currentChanged(QModelIndex,QModelIndex)), this, SLOT(sourceCurrentChanged(QModelIndex,QModelIndex)));
84 }
85 
registerTypes()86 void QAbstractItemModelSourceAdapter::registerTypes()
87 {
88     static bool alreadyRegistered = false;
89     if (alreadyRegistered)
90         return;
91 
92     alreadyRegistered = true;
93     qRegisterMetaType<QAbstractItemModel*>();
94     qRegisterMetaType<Qt::Orientation>();
95     qRegisterMetaType<QVector<Qt::Orientation> >();
96     qRegisterMetaTypeStreamOperators<ModelIndex>();
97     qRegisterMetaTypeStreamOperators<IndexList>();
98     qRegisterMetaTypeStreamOperators<DataEntries>();
99     qRegisterMetaTypeStreamOperators<MetaAndDataEntries>();
100     qRegisterMetaTypeStreamOperators<Qt::Orientation>();
101     qRegisterMetaTypeStreamOperators<QVector<Qt::Orientation> >();
102     qRegisterMetaType<QItemSelectionModel::SelectionFlags>();
103     qRegisterMetaTypeStreamOperators<QItemSelectionModel::SelectionFlags>();
104     qRegisterMetaType<QSize>();
105     qRegisterMetaType<QIntHash>();
106     qRegisterMetaTypeStreamOperators<QIntHash>();
107 }
108 
selectionModel() const109 QItemSelectionModel* QAbstractItemModelSourceAdapter::selectionModel() const
110 {
111     return m_selectionModel;
112 }
113 
replicaSizeRequest(IndexList parentList)114 QSize QAbstractItemModelSourceAdapter::replicaSizeRequest(IndexList parentList)
115 {
116     QModelIndex parent = toQModelIndex(parentList, m_model);
117     const int rowCount = m_model->rowCount(parent);
118     const int columnCount = m_model->columnCount(parent);
119     const QSize size(columnCount, rowCount);
120     qCDebug(QT_REMOTEOBJECT_MODELS) << "parent" << parentList << "size=" << size;
121     return size;
122 }
123 
replicaSetData(const IndexList & index,const QVariant & value,int role)124 void QAbstractItemModelSourceAdapter::replicaSetData(const IndexList &index, const QVariant &value, int role)
125 {
126     const QModelIndex modelIndex = toQModelIndex(index, m_model);
127     Q_ASSERT(modelIndex.isValid());
128     const bool result = m_model->setData(modelIndex, value, role);
129     Q_ASSERT(result);
130     Q_UNUSED(result);
131 }
132 
replicaRowRequest(IndexList start,IndexList end,QVector<int> roles)133 DataEntries QAbstractItemModelSourceAdapter::replicaRowRequest(IndexList start, IndexList end, QVector<int> roles)
134 {
135     qCDebug(QT_REMOTEOBJECT_MODELS) << "Requested rows" << "start=" << start << "end=" << end << "roles=" << roles;
136 
137     Q_ASSERT(start.size() == end.size());
138     Q_ASSERT(!start.isEmpty());
139 
140     if (roles.isEmpty())
141         roles << m_availableRoles;
142 
143     IndexList parentList = start;
144     Q_ASSERT(!parentList.isEmpty());
145     parentList.pop_back();
146     QModelIndex parent = toQModelIndex(parentList, m_model);
147 
148     const int startRow = start.last().row;
149     const int startColumn = start.last().column;
150     const int rowCount = m_model->rowCount(parent);
151     const int columnCount = m_model->columnCount(parent);
152 
153     DataEntries entries;
154     if (rowCount <= 0)
155         return entries;
156     const int endRow = std::min(end.last().row, rowCount - 1);
157     const int endColumn = std::min(end.last().column, columnCount - 1);
158     Q_ASSERT_X(endRow >= 0 && endRow < rowCount, __FUNCTION__, qPrintable(QString(QLatin1String("0 <= %1 < %2")).arg(endRow).arg(rowCount)));
159     Q_ASSERT_X(endColumn >= 0 && endColumn < columnCount, __FUNCTION__, qPrintable(QString(QLatin1String("0 <= %1 < %2")).arg(endColumn).arg(columnCount)));
160 
161     for (int row = startRow; row <= endRow; ++row) {
162         for (int column = startColumn; column <= endColumn; ++column) {
163             const QModelIndex current = m_model->index(row, column, parent);
164             Q_ASSERT(current.isValid());
165             const IndexList currentList = toModelIndexList(current, m_model);
166             const QVariantList data = collectData(current, m_model, roles);
167             const bool hasChildren = m_model->hasChildren(current);
168             const Qt::ItemFlags flags = m_model->flags(current);
169             qCDebug(QT_REMOTEOBJECT_MODELS) << Q_FUNC_INFO << "current=" << currentList << "data=" << data;
170             entries.data << IndexValuePair(currentList, data, hasChildren, flags);
171         }
172     }
173     return entries;
174 }
175 
replicaCacheRequest(size_t size,const QVector<int> & roles)176 MetaAndDataEntries QAbstractItemModelSourceAdapter::replicaCacheRequest(size_t size, const QVector<int> &roles)
177 {
178     MetaAndDataEntries res;
179     res.roles = roles.isEmpty() ? m_availableRoles : roles;
180     res.data = fetchTree(QModelIndex{}, size, roles);
181     const int rowCount = m_model->rowCount(QModelIndex{});
182     const int columnCount = m_model->columnCount(QModelIndex{});
183     res.size = QSize{columnCount, rowCount};
184     return res;
185 }
186 
replicaHeaderRequest(QVector<Qt::Orientation> orientations,QVector<int> sections,QVector<int> roles)187 QVariantList QAbstractItemModelSourceAdapter::replicaHeaderRequest(QVector<Qt::Orientation> orientations, QVector<int> sections, QVector<int> roles)
188 {
189     qCDebug(QT_REMOTEOBJECT_MODELS) << Q_FUNC_INFO << "orientations=" << orientations << "sections=" << sections << "roles=" << roles;
190     QVariantList data;
191     Q_ASSERT(roles.size() == sections.size());
192     Q_ASSERT(roles.size() == orientations.size());
193     for (int i = 0; i < roles.size(); ++i) {
194         data << m_model->headerData(sections[i], orientations[i], roles[i]);
195     }
196     return data;
197 }
198 
replicaSetCurrentIndex(IndexList index,QItemSelectionModel::SelectionFlags command)199 void QAbstractItemModelSourceAdapter::replicaSetCurrentIndex(IndexList index, QItemSelectionModel::SelectionFlags command)
200 {
201     if (m_selectionModel)
202         m_selectionModel->setCurrentIndex(toQModelIndex(index, m_model), command);
203 }
204 
sourceDataChanged(const QModelIndex & topLeft,const QModelIndex & bottomRight,const QVector<int> & roles) const205 void QAbstractItemModelSourceAdapter::sourceDataChanged(const QModelIndex & topLeft, const QModelIndex & bottomRight, const QVector<int> & roles) const
206 {
207     QVector<int> neededRoles = filterRoles(roles, availableRoles());
208     if (neededRoles.isEmpty()) {
209         qCDebug(QT_REMOTEOBJECT_MODELS) << Q_FUNC_INFO << "Needed roles is empty!";
210         return;
211     }
212     Q_ASSERT(topLeft.isValid());
213     Q_ASSERT(bottomRight.isValid());
214     IndexList start = toModelIndexList(topLeft, m_model);
215     IndexList end = toModelIndexList(bottomRight, m_model);
216     qCDebug(QT_REMOTEOBJECT_MODELS) << Q_FUNC_INFO << "start=" << start << "end=" << end << "neededRoles=" << neededRoles;
217     emit dataChanged(start, end, neededRoles);
218 }
219 
sourceRowsInserted(const QModelIndex & parent,int start,int end)220 void QAbstractItemModelSourceAdapter::sourceRowsInserted(const QModelIndex & parent, int start, int end)
221 {
222     IndexList parentList = toModelIndexList(parent, m_model);
223     emit rowsInserted(parentList, start, end);
224 }
225 
sourceColumnsInserted(const QModelIndex & parent,int start,int end)226 void QAbstractItemModelSourceAdapter::sourceColumnsInserted(const QModelIndex & parent, int start, int end)
227 {
228     IndexList parentList = toModelIndexList(parent, m_model);
229     emit columnsInserted(parentList, start, end);
230 }
231 
sourceRowsRemoved(const QModelIndex & parent,int start,int end)232 void QAbstractItemModelSourceAdapter::sourceRowsRemoved(const QModelIndex & parent, int start, int end)
233 {
234     IndexList parentList = toModelIndexList(parent, m_model);
235     emit rowsRemoved(parentList, start, end);
236 }
237 
sourceRowsMoved(const QModelIndex & sourceParent,int sourceRow,int count,const QModelIndex & destinationParent,int destinationChild) const238 void QAbstractItemModelSourceAdapter::sourceRowsMoved(const QModelIndex & sourceParent, int sourceRow, int count, const QModelIndex & destinationParent, int destinationChild) const
239 {
240     emit rowsMoved(toModelIndexList(sourceParent, m_model), sourceRow, count, toModelIndexList(destinationParent, m_model), destinationChild);
241 }
242 
sourceCurrentChanged(const QModelIndex & current,const QModelIndex & previous)243 void QAbstractItemModelSourceAdapter::sourceCurrentChanged(const QModelIndex & current, const QModelIndex & previous)
244 {
245     IndexList currentIndex = toModelIndexList(current, m_model);
246     IndexList previousIndex = toModelIndexList(previous, m_model);
247     qCDebug(QT_REMOTEOBJECT_MODELS) << Q_FUNC_INFO << "current=" << currentIndex << "previous=" << previousIndex;
248     emit currentChanged(currentIndex, previousIndex);
249 }
250 
fetchTree(const QModelIndex & parent,size_t & size,const QVector<int> & roles)251 QVector<IndexValuePair> QAbstractItemModelSourceAdapter::fetchTree(const QModelIndex &parent, size_t &size, const QVector<int> &roles)
252 {
253     QVector<IndexValuePair> entries;
254     const int rowCount = m_model->rowCount(parent);
255     const int columnCount = m_model->columnCount(parent);
256     if (!columnCount || !rowCount)
257         return entries;
258     entries.reserve(std::min(rowCount * columnCount, int(size)));
259     for (int row = 0; row < rowCount && size > 0; ++row)
260         for (int column = 0; column < columnCount && size > 0; ++column) {
261             const auto index = m_model->index(row, column, parent);
262             const IndexList currentList = toModelIndexList(index, m_model);
263             const QVariantList data = collectData(index, m_model, roles);
264             const bool hasChildren = m_model->hasChildren(index);
265             const Qt::ItemFlags flags = m_model->flags(index);
266             int rc = m_model->rowCount(index);
267             int cc = m_model->columnCount(index);
268             IndexValuePair rowData(currentList, data, hasChildren, flags, QSize{cc, rc});
269             --size;
270             if (hasChildren)
271                 rowData.children = fetchTree(index, size, roles);
272             entries.push_back(rowData);
273         }
274     return entries;
275 }
276