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

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