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

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