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 | |
13 | QT_BEGIN_NAMESPACE |
14 | |
15 | QT_IMPL_METATYPE_EXTERN_TAGGED(QtPrivate::ModelIndex, QtPrivate__ModelIndex) |
16 | QT_IMPL_METATYPE_EXTERN_TAGGED(QtPrivate::IndexList, QtPrivate__IndexList) |
17 | QT_IMPL_METATYPE_EXTERN_TAGGED(QtPrivate::DataEntries, QtPrivate__DataEntries) |
18 | QT_IMPL_METATYPE_EXTERN_TAGGED(QtPrivate::MetaAndDataEntries, QtPrivate__MetaAndDataEntries) |
19 | QT_IMPL_METATYPE_EXTERN_TAGGED(QtPrivate::IndexValuePair, QtPrivate__IndexValuePair) |
20 | QT_IMPL_METATYPE_EXTERN_TAGGED(Qt::Orientation, Qt__Orientation) |
21 | QT_IMPL_METATYPE_EXTERN_TAGGED(QItemSelectionModel::SelectionFlags, |
22 | QItemSelectionModel__SelectionFlags) |
23 | |
24 | inline QDebug operator<<(QDebug stream, const RequestedData &data) |
25 | { |
26 | return stream.nospace() << "RequestedData[start=" << data.start << ", end=" << data.end << ", roles=" << data.roles << "]" ; |
27 | } |
28 | |
29 | CacheData::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 | |
40 | CacheData::~CacheData() { |
41 | if (parent && !replicaModel->m_activeParents.empty()) |
42 | replicaModel->m_activeParents.erase(x: this); |
43 | } |
44 | |
45 | QAbstractItemModelReplicaImplementation::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 | |
57 | QAbstractItemModelReplicaImplementation::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 | |
70 | QAbstractItemModelReplicaImplementation::~QAbstractItemModelReplicaImplementation() |
71 | { |
72 | m_rootItem.clear(); |
73 | qDeleteAll(c: m_pendingRequests); |
74 | } |
75 | |
76 | void 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 | |
84 | void 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 | |
104 | void 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 | |
118 | inline 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 | |
132 | void QAbstractItemModelReplicaImplementation::onReplicaCurrentChanged(const QModelIndex ¤t, 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 | |
140 | void 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 | |
148 | bool 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 | |
182 | void 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 | |
229 | void 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 | |
250 | void 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 | |
279 | void 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 | |
296 | void 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 | |
317 | void 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 | |
332 | void 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 | |
341 | void 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> & = m_headerData[0]; |
365 | for (int i = 0; i < size.width(); ++i ) |
366 | headerEntries[i].data.clear(); |
367 | } |
368 | { |
369 | QList<CacheEntry> & = 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 | |
383 | void 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 | |
417 | void 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 | |
424 | QRemoteObjectPendingCallWatcher* 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 | |
441 | inline 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 | |
459 | inline 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 | |
482 | int 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 | |
497 | void 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 | |
508 | void 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 | |
551 | void 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 | |
636 | void 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 | |
646 | void QAbstractItemModelReplicaImplementation::(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 | |
656 | void QAbstractItemModelReplicaImplementation::() |
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 | |
673 | void 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 | |
708 | static 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 | |
732 | void QAbstractItemModelReplicaImplementation::(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 | */ |
780 | QAbstractItemModelReplica::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 | */ |
794 | QAbstractItemModelReplica::~QAbstractItemModelReplica() |
795 | { |
796 | } |
797 | |
798 | static 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 | */ |
818 | QItemSelectionModel* QAbstractItemModelReplica::selectionModel() const |
819 | { |
820 | return d->m_selectionModel.data(); |
821 | } |
822 | |
823 | /*! |
824 | \reimp |
825 | */ |
826 | bool 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 | */ |
866 | QVariant 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 | */ |
879 | void 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 | */ |
945 | QModelIndex 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 | */ |
963 | QModelIndex 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 | */ |
988 | bool 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 | */ |
1000 | int 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 | */ |
1019 | int 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 | */ |
1039 | QVariant QAbstractItemModelReplica::(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 | */ |
1063 | Qt::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 | */ |
1084 | bool 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 | */ |
1093 | bool 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 | */ |
1115 | size_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 | */ |
1125 | void 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 | */ |
1135 | QList<int> QAbstractItemModelReplica::availableRoles() const |
1136 | { |
1137 | return d->availableRoles(); |
1138 | } |
1139 | |
1140 | /*! |
1141 | \reimp |
1142 | */ |
1143 | QHash<int, QByteArray> QAbstractItemModelReplica::roleNames() const |
1144 | { |
1145 | return d->roleNames(); |
1146 | } |
1147 | |
1148 | QT_END_NAMESPACE |
1149 | |