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 "qremoteobjectabstractitemmodelreplica.h"
41#include "qremoteobjectabstractitemmodelreplica_p.h"
42
43#include "qremoteobjectnode.h"
44
45#include <QtCore/qdebug.h>
46#include <QtCore/qrect.h>
47#include <QtCore/qpoint.h>
48
49QT_BEGIN_NAMESPACE
50
51inline QDebug operator<<(QDebug stream, const RequestedData &data)
52{
53 return stream.nospace() << "RequestedData[start=" << data.start << ", end=" << data.end << ", roles=" << data.roles << "]";
54}
55
56CacheData::CacheData(QAbstractItemModelReplicaImplementation *model, CacheData *parentItem)
57 : replicaModel(model)
58 , parent(parentItem)
59 , hasChildren(false)
60 , columnCount(0)
61 , rowCount(0)
62{
63 if (parent)
64 replicaModel->m_activeParents.insert(x: parent);
65}
66
67CacheData::~CacheData() {
68 if (parent && !replicaModel->m_activeParents.empty())
69 replicaModel->m_activeParents.erase(x: this);
70}
71
72QAbstractItemModelReplicaImplementation::QAbstractItemModelReplicaImplementation()
73 : QRemoteObjectReplica()
74 , m_selectionModel(0)
75 , m_rootItem(this)
76{
77 QAbstractItemModelReplicaImplementation::registerMetatypes();
78 initializeModelConnections();
79 connect(sender: this, signal: &QAbstractItemModelReplicaImplementation::availableRolesChanged, context: this, slot: [this]{
80 m_availableRoles.clear();
81 });
82}
83
84QAbstractItemModelReplicaImplementation::QAbstractItemModelReplicaImplementation(QRemoteObjectNode *node, const QString &name)
85 : QRemoteObjectReplica(ConstructWithNode)
86 , m_selectionModel(0)
87 , m_rootItem(this)
88{
89 QAbstractItemModelReplicaImplementation::registerMetatypes();
90 initializeModelConnections();
91 initializeNode(node, name);
92 connect(sender: this, signal: &QAbstractItemModelReplicaImplementation::availableRolesChanged, context: this, slot: [this]{
93 m_availableRoles.clear();
94 });
95}
96
97QAbstractItemModelReplicaImplementation::~QAbstractItemModelReplicaImplementation()
98{
99 m_rootItem.clear();
100 qDeleteAll(c: m_pendingRequests);
101}
102
103void QAbstractItemModelReplicaImplementation::initialize()
104{
105 QVariantList properties;
106 properties << QVariant::fromValue(value: QVector<int>());
107 properties << QVariant::fromValue(value: QIntHash());
108 setProperties(properties);
109}
110
111void QAbstractItemModelReplicaImplementation::registerMetatypes()
112{
113 static bool alreadyRegistered = false;
114 if (alreadyRegistered)
115 return;
116
117 alreadyRegistered = true;
118 qRegisterMetaType<QAbstractItemModel*>();
119 qRegisterMetaType<Qt::Orientation>();
120 qRegisterMetaType<QVector<Qt::Orientation> >();
121 qRegisterMetaTypeStreamOperators<ModelIndex>();
122 qRegisterMetaTypeStreamOperators<IndexList>();
123 qRegisterMetaTypeStreamOperators<DataEntries>();
124 qRegisterMetaTypeStreamOperators<MetaAndDataEntries>();
125 qRegisterMetaTypeStreamOperators<Qt::Orientation>();
126 qRegisterMetaTypeStreamOperators<QVector<Qt::Orientation> >();
127 qRegisterMetaTypeStreamOperators<QItemSelectionModel::SelectionFlags>();
128 qRegisterMetaType<QItemSelectionModel::SelectionFlags>();
129 qRegisterMetaType<QSize>();
130 qRegisterMetaType<QIntHash>();
131 qRegisterMetaTypeStreamOperators<QIntHash>();
132}
133
134void QAbstractItemModelReplicaImplementation::initializeModelConnections()
135{
136 connect(sender: this, signal: &QAbstractItemModelReplicaImplementation::dataChanged, receiver: this, slot: &QAbstractItemModelReplicaImplementation::onDataChanged);
137 connect(sender: this, signal: &QAbstractItemModelReplicaImplementation::rowsInserted, receiver: this, slot: &QAbstractItemModelReplicaImplementation::onRowsInserted);
138 connect(sender: this, signal: &QAbstractItemModelReplicaImplementation::columnsInserted, receiver: this, slot: &QAbstractItemModelReplicaImplementation::onColumnsInserted);
139 connect(sender: this, signal: &QAbstractItemModelReplicaImplementation::rowsRemoved, receiver: this, slot: &QAbstractItemModelReplicaImplementation::onRowsRemoved);
140 connect(sender: this, signal: &QAbstractItemModelReplicaImplementation::rowsMoved, receiver: this, slot: &QAbstractItemModelReplicaImplementation::onRowsMoved);
141 connect(sender: this, signal: &QAbstractItemModelReplicaImplementation::currentChanged, receiver: this, slot: &QAbstractItemModelReplicaImplementation::onCurrentChanged);
142 connect(sender: this, signal: &QAbstractItemModelReplicaImplementation::modelReset, receiver: this, slot: &QAbstractItemModelReplicaImplementation::onModelReset);
143 connect(sender: this, signal: &QAbstractItemModelReplicaImplementation::headerDataChanged, receiver: this, slot: &QAbstractItemModelReplicaImplementation::onHeaderDataChanged);
144 connect(sender: this, signal: &QAbstractItemModelReplicaImplementation::layoutChanged, receiver: this, slot: &QAbstractItemModelReplicaImplementation::onLayoutChanged);
145
146}
147
148inline void removeIndexFromRow(const QModelIndex &index, const QVector<int> &roles, CachedRowEntry *entry)
149{
150 CachedRowEntry &entryRef = *entry;
151 if (index.column() < entryRef.size()) {
152 CacheEntry &entry = entryRef[index.column()];
153 if (roles.isEmpty()) {
154 entry.data.clear();
155 } else {
156 for (int role : roles)
157 entry.data.remove(akey: role);
158 }
159 }
160}
161
162void QAbstractItemModelReplicaImplementation::onReplicaCurrentChanged(const QModelIndex &current, const QModelIndex &previous)
163{
164 Q_UNUSED(previous)
165 IndexList currentIndex = toModelIndexList(index: current, model: q);
166 qCDebug(QT_REMOTEOBJECT_MODELS) << Q_FUNC_INFO << "current=" << currentIndex;
167 replicaSetCurrentIndex(index: currentIndex, command: QItemSelectionModel::Clear|QItemSelectionModel::Select|QItemSelectionModel::Current);
168}
169
170void QAbstractItemModelReplicaImplementation::setModel(QAbstractItemModelReplica *model)
171{
172 q = model;
173 setParent(model);
174 m_selectionModel.reset(other: new QItemSelectionModel(model));
175 connect(sender: m_selectionModel.data(), signal: &QItemSelectionModel::currentChanged, receiver: this, slot: &QAbstractItemModelReplicaImplementation::onReplicaCurrentChanged);
176}
177
178bool QAbstractItemModelReplicaImplementation::clearCache(const IndexList &start, const IndexList &end, const QVector<int> &roles = QVector<int>())
179{
180 Q_ASSERT(start.size() == end.size());
181
182 bool ok = true;
183 const QModelIndex startIndex = toQModelIndex(list: start, model: q, ok: &ok);
184 if (!ok)
185 return false;
186 const QModelIndex endIndex = toQModelIndex(list: end, model: q, ok: &ok);
187 if (!ok)
188 return false;
189 Q_ASSERT(startIndex.isValid());
190 Q_ASSERT(endIndex.isValid());
191 Q_ASSERT(startIndex.parent() == endIndex.parent());
192 Q_UNUSED(endIndex);
193 QModelIndex parentIndex = startIndex.parent();
194 auto parentItem = cacheData(index: parentIndex);
195
196 const int startRow = start.last().row;
197 const int lastRow = end.last().row;
198 const int startColumn = start.last().column;
199 const int lastColumn = end.last().column;
200 for (int row = startRow; row <= lastRow; ++row) {
201 Q_ASSERT_X(row >= 0 && row < parentItem->rowCount, __FUNCTION__, qPrintable(QString(QLatin1String("0 <= %1 < %2")).arg(row).arg(parentItem->rowCount)));
202 auto item = parentItem->children.get(key: row);
203 if (item) {
204 CachedRowEntry *entry = &(item->cachedRowEntry);
205 for (int column = startColumn; column <= lastColumn; ++column)
206 removeIndexFromRow(index: q->index(row, column, parent: parentIndex), roles, entry);
207 }
208 }
209 return true;
210}
211
212void QAbstractItemModelReplicaImplementation::onDataChanged(const IndexList &start, const IndexList &end, const QVector<int> &roles)
213{
214 qCDebug(QT_REMOTEOBJECT_MODELS) << Q_FUNC_INFO << "start=" << start << "end=" << end << "roles=" << roles;
215
216 // we need to clear the cache to make sure the new remote data is fetched if the new data call is happening
217 if (clearCache(start, end, roles)) {
218 bool ok = true;
219 const QModelIndex startIndex = toQModelIndex(list: start, model: q, ok: &ok);
220 if (!ok)
221 return;
222 const QModelIndex endIndex = toQModelIndex(list: end, model: q, ok: &ok);
223 if (!ok)
224 return;
225 Q_ASSERT(startIndex.parent() == endIndex.parent());
226 auto parentItem = cacheData(index: startIndex.parent());
227 int startRow = start.last().row;
228 int endRow = end.last().row;
229 bool dataChanged = false;
230 while (startRow <= endRow) {
231 for (;startRow <= endRow; startRow++) {
232 if (parentItem->children.exists(key: startRow))
233 break;
234 }
235
236 if (startRow > endRow)
237 break;
238
239 RequestedData data;
240 data.roles = roles;
241 data.start = start;
242 data.start.last().row = startRow;
243
244 while (startRow <= endRow && parentItem->children.exists(key: startRow))
245 ++startRow;
246
247 data.end = end;
248 data.end.last().row = startRow -1;
249
250 m_requestedData.append(t: data);
251 dataChanged = true;
252 }
253
254 if (dataChanged)
255 QMetaObject::invokeMethod(obj: this, member: "fetchPendingData", type: Qt::QueuedConnection);
256 }
257}
258
259void QAbstractItemModelReplicaImplementation::onRowsInserted(const IndexList &parent, int start, int end)
260{
261 qCDebug(QT_REMOTEOBJECT_MODELS) << Q_FUNC_INFO << "start=" << start << "end=" << end << "parent=" << parent;
262
263 bool treeFullyLazyLoaded = true;
264 const QModelIndex parentIndex = toQModelIndex(list: parent, model: q, ok: &treeFullyLazyLoaded, ensureItem: true);
265 if (!treeFullyLazyLoaded)
266 return;
267
268 auto parentItem = cacheData(index: parentIndex);
269 q->beginInsertRows(parent: parentIndex, first: start, last: end);
270 parentItem->insertChildren(start, end);
271 for (int i = start; i <= end; ++i)
272 m_headerData[1].append(t: CacheEntry());
273 q->endInsertRows();
274 if (!parentItem->hasChildren && parentItem->columnCount > 0) {
275 parentItem->hasChildren = true;
276 emit q->dataChanged(topLeft: parentIndex, bottomRight: parentIndex);
277 }
278}
279
280void QAbstractItemModelReplicaImplementation::onColumnsInserted(const IndexList &parent, int start, int end)
281{
282 qCDebug(QT_REMOTEOBJECT_MODELS) << Q_FUNC_INFO << "start=" << start << "end=" << end << "parent=" << parent;
283
284 bool treeFullyLazyLoaded = true;
285 const QModelIndex parentIndex = toQModelIndex(list: parent, model: q, ok: &treeFullyLazyLoaded);
286 if (!treeFullyLazyLoaded)
287 return;
288
289 //Since we need to support QAIM and models that don't emit columnCountChanged
290 //check if we have a constant columnCount everywhere if thats the case don't insert
291 //more columns
292 auto parentItem = cacheData(index: parentIndex);
293 auto parentOfParent = parentItem->parent;
294 if (parentOfParent && parentItem != &m_rootItem)
295 if (parentOfParent->columnCount == parentItem->columnCount)
296 return;
297 q->beginInsertColumns(parent: parentIndex, first: start, last: end);
298 parentItem->columnCount += end - start + 1;
299 for (int i = start; i <= end; ++i)
300 m_headerData[0].append(t: CacheEntry());
301 q->endInsertColumns();
302 if (!parentItem->hasChildren && parentItem->children.size() > 0) {
303 parentItem->hasChildren = true;
304 emit q->dataChanged(topLeft: parentIndex, bottomRight: parentIndex);
305 }
306
307}
308
309void QAbstractItemModelReplicaImplementation::onRowsRemoved(const IndexList &parent, int start, int end)
310{
311 qCDebug(QT_REMOTEOBJECT_MODELS) << Q_FUNC_INFO << "start=" << start << "end=" << end << "parent=" << parent;
312
313 bool treeFullyLazyLoaded = true;
314 const QModelIndex parentIndex = toQModelIndex(list: parent, model: q, ok: &treeFullyLazyLoaded);
315 if (!treeFullyLazyLoaded)
316 return;
317
318 auto parentItem = cacheData(index: parentIndex);
319 q->beginRemoveRows(parent: parentIndex, first: start, last: end);
320 if (parentItem)
321 parentItem->removeChildren(start, end);
322 m_headerData[1].erase(abegin: m_headerData[1].begin() + start, aend: m_headerData[1].begin() + end + 1);
323 q->endRemoveRows();
324}
325
326void QAbstractItemModelReplicaImplementation::onRowsMoved(IndexList srcParent, int srcRow, int count, IndexList destParent, int destRow)
327{
328 qCDebug(QT_REMOTEOBJECT_MODELS) << Q_FUNC_INFO;
329
330 const QModelIndex sourceParent = toQModelIndex(list: srcParent, model: q);
331 const QModelIndex destinationParent = toQModelIndex(list: destParent, model: q);
332 Q_ASSERT(!sourceParent.isValid());
333 Q_ASSERT(!destinationParent.isValid());
334 q->beginMoveRows(sourceParent, sourceFirst: srcRow, sourceLast: count, destinationParent, destinationRow: destRow);
335//TODO misses parents...
336 IndexList start, end;
337 start << ModelIndex(srcRow, 0);
338 end << ModelIndex(srcRow + count, q->columnCount(parent: sourceParent)-1);
339 clearCache(start, end);
340 IndexList start2, end2;
341 start2 << ModelIndex(destRow, 0);
342 end2 << ModelIndex(destRow + count, q->columnCount(parent: destinationParent)-1);
343 clearCache(start: start2, end: end2);
344 q->endMoveRows();
345}
346
347void QAbstractItemModelReplicaImplementation::onCurrentChanged(IndexList current, IndexList previous)
348{
349 qCDebug(QT_REMOTEOBJECT_MODELS) << Q_FUNC_INFO << "current=" << current << "previous=" << previous;
350 Q_UNUSED(previous);
351 Q_ASSERT(m_selectionModel);
352 bool ok;
353 // If we have several tree models sharing a selection model, we
354 // can't guarantee that all Replicas have the selected cell
355 // available.
356 const QModelIndex currentIndex = toQModelIndex(list: current, model: q, ok: &ok);
357 // Ignore selection if we can't find the desired cell.
358 if (ok)
359 m_selectionModel->setCurrentIndex(index: currentIndex, command: QItemSelectionModel::Clear|QItemSelectionModel::Select|QItemSelectionModel::Current);
360}
361
362void QAbstractItemModelReplicaImplementation::handleInitDone(QRemoteObjectPendingCallWatcher *watcher)
363{
364 qCDebug(QT_REMOTEOBJECT_MODELS) << Q_FUNC_INFO;
365
366 handleModelResetDone(watcher);
367 m_initDone = true;
368 emit q->initialized();
369}
370
371void QAbstractItemModelReplicaImplementation::handleModelResetDone(QRemoteObjectPendingCallWatcher *watcher)
372{
373 QSize size;
374 if (m_initialAction == QtRemoteObjects::FetchRootSize)
375 size = watcher->returnValue().toSize();
376 else {
377 Q_ASSERT(watcher->returnValue().canConvert<MetaAndDataEntries>());
378 size = watcher->returnValue().value<MetaAndDataEntries>().size;
379 }
380
381 qCDebug(QT_REMOTEOBJECT_MODELS) << Q_FUNC_INFO << "size=" << size;
382
383 q->beginResetModel();
384 m_rootItem.clear();
385 if (size.height() > 0) {
386 m_rootItem.rowCount = size.height();
387 m_rootItem.hasChildren = true;
388 }
389
390 m_rootItem.columnCount = size.width();
391 m_headerData[0].resize(asize: size.width());
392 m_headerData[1].resize(asize: size.height());
393 {
394 QVector<CacheEntry> &headerEntries = m_headerData[0];
395 for (int i = 0; i < size.width(); ++i )
396 headerEntries[i].data.clear();
397 }
398 {
399 QVector<CacheEntry> &headerEntries = m_headerData[1];
400 for (int i = 0; i < size.height(); ++i )
401 headerEntries[i].data.clear();
402 }
403 if (m_initialAction == QtRemoteObjects::PrefetchData) {
404 auto entries = watcher->returnValue().value<MetaAndDataEntries>();
405 for (int i = 0; i < entries.data.size(); ++i)
406 fillCache(pair: entries.data[i], roles: entries.roles);
407 }
408 q->endResetModel();
409 m_pendingRequests.removeAll(t: watcher);
410 delete watcher;
411}
412
413void QAbstractItemModelReplicaImplementation::handleSizeDone(QRemoteObjectPendingCallWatcher *watcher)
414{
415 SizeWatcher *sizeWatcher = static_cast<SizeWatcher*>(watcher);
416 const QSize size = sizeWatcher->returnValue().toSize();
417 auto parentItem = cacheData(index: sizeWatcher->parentList);
418 const QModelIndex parent = toQModelIndex(list: sizeWatcher->parentList, model: q);
419
420 if (size.width() != parentItem->columnCount) {
421 const int columnCount = std::max(a: 0, b: parentItem->columnCount);
422 Q_ASSERT_X(size.width() >= parentItem->columnCount, __FUNCTION__, "The column count should only shrink in columnsRemoved!!");
423 parentItem->columnCount = size.width();
424 if (size.width() > columnCount) {
425 Q_ASSERT(size.width() > 0);
426 q->beginInsertColumns(parent, first: columnCount, last: size.width() - 1);
427 q->endInsertColumns();
428 } else {
429 Q_ASSERT_X(size.width() == columnCount, __FUNCTION__, qPrintable(QString(QLatin1String("%1 != %2")).arg(size.width()).arg(columnCount)));
430 }
431 }
432
433 Q_ASSERT_X(size.height() >= parentItem->rowCount, __FUNCTION__, "The new size and the current size should match!!");
434 if (!parentItem->rowCount) {
435 if (size.height() > 0) {
436 q->beginInsertRows(parent, first: 0, last: size.height() - 1);
437 parentItem->rowCount = size.height();
438 q->endInsertRows();
439 }
440 } else {
441 Q_ASSERT_X(parentItem->rowCount == size.height(), __FUNCTION__, qPrintable(QString(QLatin1String("%1 != %2")).arg(parentItem->rowCount).arg(size.height())));
442 }
443 m_pendingRequests.removeAll(t: watcher);
444 delete watcher;
445}
446
447void QAbstractItemModelReplicaImplementation::init()
448{
449 qCDebug(QT_REMOTEOBJECT_MODELS) << Q_FUNC_INFO << this->node()->objectName();
450 QRemoteObjectPendingCallWatcher *watcher = doModelReset();
451 connect(sender: watcher, signal: &QRemoteObjectPendingCallWatcher::finished, receiver: this, slot: &QAbstractItemModelReplicaImplementation::handleInitDone);
452}
453
454QRemoteObjectPendingCallWatcher* QAbstractItemModelReplicaImplementation::doModelReset()
455{
456 qDeleteAll(c: m_pendingRequests);
457 m_pendingRequests.clear();
458 IndexList parentList;
459 QRemoteObjectPendingCallWatcher *watcher;
460 if (m_initialAction == QtRemoteObjects::FetchRootSize) {
461 auto call = replicaSizeRequest(parentList);
462 watcher = new SizeWatcher(parentList, call);
463 } else {
464 auto call = replicaCacheRequest(size: m_rootItem.children.cacheSize, roles: m_initialFetchRolesHint);
465 watcher = new QRemoteObjectPendingCallWatcher(call);
466 }
467 m_pendingRequests.push_back(t: watcher);
468 return watcher;
469}
470
471inline void fillCacheEntry(CacheEntry *entry, const IndexValuePair &pair, const QVector<int> &roles)
472{
473 Q_ASSERT(entry);
474
475 const QVariantList &data = pair.data;
476 Q_ASSERT(roles.size() == data.size());
477
478 entry->flags = pair.flags;
479
480 qCDebug(QT_REMOTEOBJECT_MODELS) << Q_FUNC_INFO << "data.size=" << data.size();
481 for (int i = 0; i < data.size(); ++i) {
482 const int role = roles[i];
483 const QVariant dataVal = data[i];
484 qCDebug(QT_REMOTEOBJECT_MODELS) << Q_FUNC_INFO << "role=" << role << "data=" << dataVal;
485 entry->data[role] = dataVal;
486 }
487}
488
489inline void fillRow(CacheData *item, const IndexValuePair &pair, const QAbstractItemModel *model, const QVector<int> &roles)
490{
491 CachedRowEntry &rowRef = item->cachedRowEntry;
492 const QModelIndex index = toQModelIndex(list: pair.index, model);
493 Q_ASSERT(index.isValid());
494 qCDebug(QT_REMOTEOBJECT_MODELS) << Q_FUNC_INFO << "row=" << index.row() << "column=" << index.column();
495 if (index.column() == 0)
496 item->hasChildren = pair.hasChildren;
497 bool existed = false;
498 for (int i = 0; i < rowRef.size(); ++i) {
499 if (i == index.column()) {
500 fillCacheEntry(entry: &rowRef[i], pair, roles);
501 existed = true;
502 }
503 }
504 qCDebug(QT_REMOTEOBJECT_MODELS) << Q_FUNC_INFO << "existed=" << existed;
505 if (!existed) {
506 CacheEntry entries;
507 fillCacheEntry(entry: &entries, pair, roles);
508 rowRef.append(t: entries);
509 }
510}
511
512int collectEntriesForRow(DataEntries* filteredEntries, int row, const DataEntries &entries, int startIndex)
513{
514 Q_ASSERT(filteredEntries);
515 const int size = entries.data.size();
516 for (int i = startIndex; i < size; ++i)
517 {
518 const IndexValuePair &pair = entries.data[i];
519 if (pair.index.last().row == row)
520 filteredEntries->data << pair;
521 else
522 return i;
523 }
524 return size;
525}
526
527void QAbstractItemModelReplicaImplementation::fillCache(const IndexValuePair &pair, const QVector<int> &roles)
528{
529 if (auto item = createCacheData(index: pair.index)) {
530 fillRow(item, pair, model: q, roles);
531 item->rowCount = pair.size.height();
532 item->columnCount = pair.size.width();
533 }
534 for (const auto &it : pair.children)
535 fillCache(pair: it, roles);
536}
537
538void QAbstractItemModelReplicaImplementation::requestedData(QRemoteObjectPendingCallWatcher *qobject)
539{
540 RowWatcher *watcher = static_cast<RowWatcher *>(qobject);
541 Q_ASSERT(watcher);
542 Q_ASSERT(watcher->start.size() == watcher->end.size());
543
544 qCDebug(QT_REMOTEOBJECT_MODELS) << Q_FUNC_INFO << "start=" << watcher->start << "end=" << watcher->end;
545
546 IndexList parentList = watcher->start;
547 Q_ASSERT(!parentList.isEmpty());
548 parentList.pop_back();
549 auto parentItem = cacheData(index: parentList);
550 DataEntries entries = watcher->returnValue().value<DataEntries>();
551
552 const int rowCount = parentItem->rowCount;
553 const int columnCount = parentItem->columnCount;
554
555 if (rowCount < 1 || columnCount < 1)
556 return;
557
558 const int startRow = std::min(a: watcher->start.last().row, b: rowCount - 1);
559 const int endRow = std::min(a: watcher->end.last().row, b: rowCount - 1);
560 const int startColumn = std::min(a: watcher->start.last().column, b: columnCount - 1);
561 const int endColumn = std::min(a: watcher->end.last().column, b: columnCount - 1);
562 Q_ASSERT_X(startRow >= 0 && startRow < parentItem->rowCount, __FUNCTION__, qPrintable(QString(QLatin1String("0 <= %1 < %2")).arg(startRow).arg(parentItem->rowCount)));
563 Q_ASSERT_X(endRow >= 0 && endRow < parentItem->rowCount, __FUNCTION__, qPrintable(QString(QLatin1String("0 <= %1 < %2")).arg(endRow).arg(parentItem->rowCount)));
564
565 for (int i = 0; i < entries.data.size(); ++i) {
566 IndexValuePair pair = entries.data[i];
567 if (auto item = createCacheData(index: pair.index))
568 fillRow(item, pair, model: q, roles: watcher->roles);
569 }
570
571 const QModelIndex parentIndex = toQModelIndex(list: parentList, model: q);
572 const QModelIndex startIndex = q->index(row: startRow, column: startColumn, parent: parentIndex);
573 const QModelIndex endIndex = q->index(row: endRow, column: endColumn, parent: parentIndex);
574 Q_ASSERT(startIndex.isValid());
575 Q_ASSERT(endIndex.isValid());
576 emit q->dataChanged(topLeft: startIndex, bottomRight: endIndex, roles: watcher->roles);
577 m_pendingRequests.removeAll(t: watcher);
578 delete watcher;
579}
580
581void QAbstractItemModelReplicaImplementation::fetchPendingData()
582{
583 if (m_requestedData.isEmpty())
584 return;
585
586 qCDebug(QT_REMOTEOBJECT_MODELS) << Q_FUNC_INFO << "m_requestedData.size=" << m_requestedData.size();
587
588 std::vector<RequestedData> finalRequests;
589 RequestedData curData;
590 for (const RequestedData &data : qExchange(t&: m_requestedData, newValue: {})) {
591 qCDebug(QT_REMOTEOBJECT_MODELS) << Q_FUNC_INFO << "REQUESTED start=" << data.start << "end=" << data.end << "roles=" << data.roles;
592
593 Q_ASSERT(!data.start.isEmpty());
594 Q_ASSERT(!data.end.isEmpty());
595 Q_ASSERT(data.start.size() == data.end.size());
596 if (curData.start.isEmpty() || curData.start.last().row == -1 || curData.start.last().column == -1)
597 curData = data;
598 if (curData.start.size() != data.start.size()) {
599 finalRequests.push_back(x: curData);
600 curData = data;
601 } else {
602 if (data.start.size() > 1) {
603 for (int i = 0; i < data.start.size() - 1; ++i) {
604 if (curData.start[i].row != data.start[i].row ||
605 curData.start[i].column != data.start[i].column) {
606 finalRequests.push_back(x: curData);
607 curData = data;
608 }
609 }
610 }
611
612 const ModelIndex curIndStart = curData.start.last();
613 const ModelIndex curIndEnd = curData.end.last();
614 const ModelIndex dataIndStart = data.start.last();
615 const ModelIndex dataIndEnd = data.end.last();
616 const ModelIndex resStart(std::min(a: curIndStart.row, b: dataIndStart.row), std::min(a: curIndStart.column, b: dataIndStart.column));
617 const ModelIndex resEnd(std::max(a: curIndEnd.row, b: dataIndEnd.row), std::max(a: curIndEnd.column, b: dataIndEnd.column));
618 QVector<int> roles = curData.roles;
619 if (!curData.roles.isEmpty()) {
620 for (int role : data.roles) {
621 if (!curData.roles.contains(t: role))
622 roles.append(t: role);
623 }
624 }
625 QRect firstRect( QPoint(curIndStart.row, curIndStart.column), QPoint(curIndEnd.row, curIndEnd.column));
626 QRect secondRect( QPoint(dataIndStart.row, dataIndStart.column), QPoint(dataIndEnd.row, dataIndEnd.column));
627
628 const bool borders = (qAbs(t: curIndStart.row - dataIndStart.row) == 1) ||
629 (qAbs(t: curIndStart.column - dataIndStart.column) == 1) ||
630 (qAbs(t: curIndEnd.row - dataIndEnd.row) == 1) ||
631 (qAbs(t: curIndEnd.column - dataIndEnd.column) == 1);
632
633 if ((resEnd.row - resStart.row < 100) && (firstRect.intersects(r: secondRect) || borders)) {
634 IndexList start = curData.start;
635 start.pop_back();
636 start.push_back(t: resStart);
637 IndexList end = curData.end;
638 end.pop_back();
639 end.push_back(t: resEnd);
640 curData.start = start;
641 curData.end = end;
642 curData.roles = roles;
643 Q_ASSERT(!start.isEmpty());
644 Q_ASSERT(!end.isEmpty());
645 } else {
646 finalRequests.push_back(x: curData);
647 curData = data;
648 }
649 }
650 }
651 finalRequests.push_back(x: curData);
652 //qCDebug(QT_REMOTEOBJECT_MODELS) << "Final requests" << finalRequests;
653 int rows = 0;
654 // There is no point to eat more than can chew
655 for (auto it = finalRequests.rbegin(); it != finalRequests.rend() && size_t(rows) < m_rootItem.children.cacheSize; ++it) {
656 qCDebug(QT_REMOTEOBJECT_MODELS) << Q_FUNC_INFO << "FINAL start=" << it->start << "end=" << it->end << "roles=" << it->roles;
657
658 QRemoteObjectPendingReply<DataEntries> reply = replicaRowRequest(start: it->start, end: it->end, roles: it->roles);
659 RowWatcher *watcher = new RowWatcher(it->start, it->end, it->roles, reply);
660 rows += 1 + it->end.first().row - it->start.first().row;
661 m_pendingRequests.push_back(t: watcher);
662 connect(sender: watcher, signal: &RowWatcher::finished, receiver: this, slot: &QAbstractItemModelReplicaImplementation::requestedData);
663 }
664}
665
666void QAbstractItemModelReplicaImplementation::onModelReset()
667{
668 if (!m_initDone)
669 return;
670
671 qCDebug(QT_REMOTEOBJECT_MODELS) << Q_FUNC_INFO;
672 QRemoteObjectPendingCallWatcher *watcher = doModelReset();
673 connect(sender: watcher, signal: &QRemoteObjectPendingCallWatcher::finished, receiver: this, slot: &QAbstractItemModelReplicaImplementation::handleModelResetDone);
674}
675
676void QAbstractItemModelReplicaImplementation::onHeaderDataChanged(Qt::Orientation orientation, int first, int last)
677{
678 // TODO clean cache
679 const int index = orientation == Qt::Horizontal ? 0 : 1;
680 QVector<CacheEntry> &entries = m_headerData[index];
681 for (int i = first; i <= last && i < entries.size(); ++i )
682 entries[i].data.clear();
683 emit q->headerDataChanged(orientation, first, last);
684}
685
686void QAbstractItemModelReplicaImplementation::fetchPendingHeaderData()
687{
688 QVector<int> roles;
689 QVector<int> sections;
690 QVector<Qt::Orientation> orientations;
691 for (const RequestedHeaderData &data : qAsConst(t&: m_requestedHeaderData)) {
692 roles.push_back(t: data.role);
693 sections.push_back(t: data.section);
694 orientations.push_back(t: data.orientation);
695 }
696 QRemoteObjectPendingReply<QVariantList> reply = replicaHeaderRequest(orientations, sections, roles);
697 HeaderWatcher *watcher = new HeaderWatcher(orientations, sections, roles, reply);
698 connect(sender: watcher, signal: &HeaderWatcher::finished, receiver: this, slot: &QAbstractItemModelReplicaImplementation::requestedHeaderData);
699 m_requestedHeaderData.clear();
700 m_pendingRequests.push_back(t: watcher);
701}
702
703void QAbstractItemModelReplicaImplementation::onLayoutChanged(const IndexList &parents,
704 QAbstractItemModel::LayoutChangeHint hint)
705{
706 QList<QPersistentModelIndex> indexes;
707 for (const ModelIndex &parent : qAsConst(t: parents)) {
708 const QModelIndex parentIndex = toQModelIndex(list: IndexList{parent}, model: q);
709 indexes << QPersistentModelIndex(parentIndex);
710 }
711 QRemoteObjectPendingCallWatcher *watcher;
712 auto call = replicaCacheRequest(size: m_rootItem.children.cacheSize, roles: m_initialFetchRolesHint);
713 watcher = new QRemoteObjectPendingCallWatcher(call);
714 m_pendingRequests.push_back(t: watcher);
715 connect(sender: watcher, signal: &QRemoteObjectPendingCallWatcher::finished, context: this, slot: [this, watcher, indexes, hint]() {
716 Q_ASSERT(watcher->returnValue().canConvert<MetaAndDataEntries>());
717 const QSize size = watcher->returnValue().value<MetaAndDataEntries>().size;
718
719 q->layoutAboutToBeChanged(parents: indexes, hint);
720 m_rootItem.clear();
721 if (size.height() > 0) {
722 m_rootItem.rowCount = size.height();
723 m_rootItem.hasChildren = true;
724 }
725
726 m_rootItem.columnCount = size.width();
727 if (m_initialAction == QtRemoteObjects::PrefetchData) {
728 auto entries = watcher->returnValue().value<MetaAndDataEntries>();
729 for (int i = 0; i < entries.data.size(); ++i)
730 fillCache(pair: entries.data[i], roles: entries.roles);
731 }
732 m_pendingRequests.removeAll(t: watcher);
733 watcher->deleteLater();
734 emit q->layoutChanged(parents: indexes, hint);
735 });
736}
737
738static inline QVector<QPair<int, int> > listRanges(const QVector<int> &list)
739{
740 QVector<QPair<int, int> > result;
741 if (!list.isEmpty()) {
742 QPair<int, int> currentElem = qMakePair(x: list.first(), y: list.first());
743 QVector<int>::const_iterator end = list.end();
744 for (QVector<int>::const_iterator it = list.constBegin() + 1; it != end; ++it) {
745 if (currentElem.first == *it +1)
746 currentElem.first = *it;
747 else if ( currentElem.second == *it -1)
748 currentElem.second = *it;
749 else if (currentElem.first <= *it && currentElem.second >= *it)
750 continue;
751 else {
752 result.push_back(t: currentElem);
753 currentElem.first = *it;
754 currentElem.second = *it;
755 }
756 }
757 result.push_back(t: currentElem);
758 }
759 return result;
760}
761
762void QAbstractItemModelReplicaImplementation::requestedHeaderData(QRemoteObjectPendingCallWatcher *qobject)
763{
764 HeaderWatcher *watcher = static_cast<HeaderWatcher *>(qobject);
765 Q_ASSERT(watcher);
766
767 QVariantList data = watcher->returnValue().value<QVariantList>();
768 Q_ASSERT(watcher->orientations.size() == data.size());
769 Q_ASSERT(watcher->sections.size() == data.size());
770 Q_ASSERT(watcher->roles.size() == data.size());
771 QVector<int> horizontalSections;
772 QVector<int> verticalSections;
773
774 for (int i = 0; i < data.size(); ++i) {
775 if (watcher->orientations[i] == Qt::Horizontal)
776 horizontalSections.append(t: watcher->sections[i]);
777 else
778 verticalSections.append(t: watcher->sections[i]);
779 const int index = watcher->orientations[i] == Qt::Horizontal ? 0 : 1;
780 const int role = watcher->roles[i];
781 QHash<int, QVariant> &dat = m_headerData[index][watcher->sections[i]].data;
782 dat[role] = data[i];
783 }
784 QVector<QPair<int, int> > horRanges = listRanges(list: horizontalSections);
785 QVector<QPair<int, int> > verRanges = listRanges(list: verticalSections);
786
787 for (int i = 0; i < horRanges.size(); ++i)
788 emit q->headerDataChanged(orientation: Qt::Horizontal, first: horRanges[i].first, last: horRanges[i].second);
789 for (int i = 0; i < verRanges.size(); ++i)
790 emit q->headerDataChanged(orientation: Qt::Vertical, first: verRanges[i].first, last: verRanges[i].second);
791 m_pendingRequests.removeAll(t: watcher);
792 delete watcher;
793}
794
795QAbstractItemModelReplica::QAbstractItemModelReplica(QAbstractItemModelReplicaImplementation *rep, QtRemoteObjects::InitialAction action, const QVector<int> &rolesHint)
796 : QAbstractItemModel()
797 , d(rep)
798{
799 d->m_initialAction = action;
800 d->m_initialFetchRolesHint = rolesHint;
801
802 rep->setModel(this);
803 connect(sender: rep, signal: &QAbstractItemModelReplicaImplementation::initialized, receiver: d.data(), slot: &QAbstractItemModelReplicaImplementation::init);
804}
805
806QAbstractItemModelReplica::~QAbstractItemModelReplica()
807{
808}
809
810static QVariant findData(const CachedRowEntry &row, const QModelIndex &index, int role, bool *cached = 0)
811{
812 if (index.column() < row.size()) {
813 const CacheEntry &entry = row[index.column()];
814 QHash<int, QVariant>::ConstIterator it = entry.data.constFind(akey: role);
815 if (it != entry.data.constEnd()) {
816 if (cached)
817 *cached = true;
818 return it.value();
819 }
820 }
821 if (cached)
822 *cached = false;
823 return QVariant();
824}
825
826QItemSelectionModel* QAbstractItemModelReplica::selectionModel() const
827{
828 return d->m_selectionModel.data();
829}
830
831bool QAbstractItemModelReplica::setData(const QModelIndex &index, const QVariant &value, int role)
832{
833 if (role == Qt::UserRole - 1) {
834 auto parent = d->cacheData(index);
835 if (!parent)
836 return false;
837 bool ok = true;
838 auto row = value.toInt(ok: &ok);
839 if (ok)
840 parent->ensureChildren(start: row, end: row);
841 return ok;
842 }
843 if (!index.isValid())
844 return false;
845 if (index.row() < 0 || index.row() >= rowCount(parent: index.parent()))
846 return false;
847 if (index.column() < 0 || index.column() >= columnCount(parent: index.parent()))
848 return false;
849
850 const QVector<int > &availRoles = availableRoles();
851 const QVector<int>::const_iterator res = std::find(first: availRoles.begin(), last: availRoles.end(), val: role);
852 if (res == availRoles.end()) {
853 qCWarning(QT_REMOTEOBJECT_MODELS) << "Tried to setData for index" << index << "on a not supported role" << role;
854 return false;
855 }
856 // sendInvocationRequest to change server side data;
857 d->replicaSetData(index: toModelIndexList(index, model: this), value, role);
858 return true;
859}
860
861QVariant QAbstractItemModelReplica::data(const QModelIndex & index, int role) const
862{
863
864 if (!d->isInitialized()) {
865 qCDebug(QT_REMOTEOBJECT_MODELS)<<"Data not initialized yet";
866 return QVariant();
867 }
868
869 if (!index.isValid())
870 return QVariant();
871
872 if (!availableRoles().contains(t: role))
873 return QVariant();
874
875 auto item = d->cacheData(index);
876 if (item) {
877 bool cached = false;
878 QVariant result = findData(row: item->cachedRowEntry, index, role, cached: &cached);
879 if (cached)
880 return result;
881 }
882
883 auto parentItem = d->cacheData(index: index.parent());
884 Q_ASSERT(parentItem);
885 Q_ASSERT(index.row() < parentItem->rowCount);
886 const int row = index.row();
887 IndexList parentList = toModelIndexList(index: index.parent(), model: this);
888 IndexList start = IndexList() << parentList << ModelIndex(row, 0);
889 IndexList end = IndexList() << parentList << ModelIndex(row, std::max(a: 0, b: parentItem->columnCount - 1));
890 Q_ASSERT(toQModelIndex(start, this).isValid());
891
892 RequestedData data;
893 QVector<int> roles;
894 roles << role;
895 data.start = start;
896 data.end = end;
897 data.roles = roles;
898 d->m_requestedData.push_back(t: data);
899 qCDebug(QT_REMOTEOBJECT_MODELS) << "FETCH PENDING DATA" << start << end << roles;
900 QMetaObject::invokeMethod(obj: d.data(), member: "fetchPendingData", type: Qt::QueuedConnection);
901 return QVariant{};
902}
903QModelIndex QAbstractItemModelReplica::parent(const QModelIndex &index) const
904{
905 if (!index.isValid() || !index.internalPointer())
906 return QModelIndex();
907 auto parent = static_cast<CacheData*>(index.internalPointer());
908 Q_ASSERT(parent);
909 if (parent == &d->m_rootItem)
910 return QModelIndex();
911 if (d->m_activeParents.find(x: parent) == d->m_activeParents.end() || d->m_activeParents.find(x: parent->parent) == d->m_activeParents.end())
912 return QModelIndex();
913 int row = parent->parent->children.find(val: parent);
914 Q_ASSERT(row >= 0);
915 return createIndex(arow: row, acolumn: 0, adata: parent->parent);
916}
917QModelIndex QAbstractItemModelReplica::index(int row, int column, const QModelIndex &parent) const
918{
919 auto parentItem = d->cacheData(index: parent);
920 if (!parentItem)
921 return QModelIndex();
922
923 Q_ASSERT_X((parent.isValid() && parentItem && parentItem != &d->m_rootItem) || (!parent.isValid() && parentItem == &d->m_rootItem), __FUNCTION__, qPrintable(QString(QLatin1String("isValid=%1 equals=%2")).arg(parent.isValid()).arg(parentItem == &d->m_rootItem)));
924
925 // hmpf, following works around a Q_ASSERT-bug in QAbstractItemView::setModel which does just call
926 // d->model->index(0,0) without checking the range before-hand what triggers our assert in case the
927 // model is empty when view::setModel is called :-/ So, work around with the following;
928 if (row < 0 || row >= parentItem->rowCount)
929 return QModelIndex();
930
931 if (column < 0 || column >= parentItem->columnCount)
932 return QModelIndex();
933
934 if (parentItem != &d->m_rootItem)
935 parentItem->ensureChildren(start: row, end: row);
936 return createIndex(arow: row, acolumn: column, adata: reinterpret_cast<void*>(parentItem));
937}
938bool QAbstractItemModelReplica::hasChildren(const QModelIndex &parent) const
939{
940 auto parentItem = d->cacheData(index: parent);
941 if (parent.isValid() && parent.column() != 0)
942 return false;
943 else
944 return parentItem ? parentItem->hasChildren : false;
945}
946int QAbstractItemModelReplica::rowCount(const QModelIndex &parent) const
947{
948 auto parentItem = d->cacheData(index: parent);
949 const bool canHaveChildren = parentItem && parentItem->hasChildren && !parentItem->rowCount && parent.column() == 0;
950 if (canHaveChildren) {
951 IndexList parentList = toModelIndexList(index: parent, model: this);
952 QRemoteObjectPendingReply<QSize> reply = d->replicaSizeRequest(parentList);
953 SizeWatcher *watcher = new SizeWatcher(parentList, reply);
954 connect(sender: watcher, signal: &SizeWatcher::finished, receiver: d.data(), slot: &QAbstractItemModelReplicaImplementation::handleSizeDone);
955 } else if (parent.column() > 0) {
956 return 0;
957 }
958
959 return parentItem ? parentItem->rowCount : 0;
960}
961int QAbstractItemModelReplica::columnCount(const QModelIndex &parent) const
962{
963 if (parent.isValid() && parent.column() > 0)
964 return 0;
965 auto parentItem = d->cacheData(index: parent);
966 if (!parentItem)
967 return 0;
968 while (parentItem->columnCount < 0 && parentItem->parent)
969 parentItem = parentItem->parent;
970 return std::max(a: 0, b: parentItem->columnCount);
971}
972
973QVariant QAbstractItemModelReplica::headerData(int section, Qt::Orientation orientation, int role) const
974{
975 const int index = orientation == Qt::Horizontal ? 0 : 1;
976 const QVector<CacheEntry> elem = d->m_headerData[index];
977 if (section >= elem.size())
978 return QVariant();
979
980 const QHash<int, QVariant> &dat = elem.at(i: section).data;
981 QHash<int, QVariant>::ConstIterator it = dat.constFind(akey: role);
982 if (it != dat.constEnd())
983 return it.value();
984
985 RequestedHeaderData data;
986 data.role = role;
987 data.section = section;
988 data.orientation = orientation;
989 d->m_requestedHeaderData.push_back(t: data);
990 QMetaObject::invokeMethod(obj: d.data(), member: "fetchPendingHeaderData", type: Qt::QueuedConnection);
991 return QVariant();
992}
993
994Qt::ItemFlags QAbstractItemModelReplica::flags(const QModelIndex &index) const
995{
996 CacheEntry *entry = d->cacheEntry(index);
997 return entry ? entry->flags : Qt::NoItemFlags;
998}
999
1000bool QAbstractItemModelReplica::isInitialized() const
1001{
1002 return d->isInitialized();
1003}
1004
1005bool QAbstractItemModelReplica::hasData(const QModelIndex &index, int role) const
1006{
1007 if (!d->isInitialized() || !index.isValid())
1008 return false;
1009 auto item = d->cacheData(index);
1010 if (!item)
1011 return false;
1012 bool cached = false;
1013 const CachedRowEntry &entry = item->cachedRowEntry;
1014 QVariant result = findData(row: entry, index, role, cached: &cached);
1015 Q_UNUSED(result);
1016 return cached;
1017}
1018
1019size_t QAbstractItemModelReplica::rootCacheSize() const
1020{
1021 return d->m_rootItem.children.cacheSize;
1022}
1023
1024void QAbstractItemModelReplica::setRootCacheSize(size_t rootCacheSize)
1025{
1026 d->m_rootItem.children.setCacheSize(rootCacheSize);
1027}
1028
1029QVector<int> QAbstractItemModelReplica::availableRoles() const
1030{
1031 return d->availableRoles();
1032}
1033
1034QHash<int, QByteArray> QAbstractItemModelReplica::roleNames() const
1035{
1036 return d->roleNames();
1037}
1038
1039QT_END_NAMESPACE
1040

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