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