1 /*
2 remotemodelserver.cpp
3
4 This file is part of GammaRay, the Qt application inspection and
5 manipulation tool.
6
7 Copyright (C) 2013-2021 Klarälvdalens Datakonsult AB, a KDAB Group company, info@kdab.com
8 Author: Volker Krause <volker.krause@kdab.com>
9
10 Licensees holding valid commercial KDAB GammaRay licenses may use this file in
11 accordance with GammaRay Commercial License Agreement provided with the Software.
12
13 Contact info@kdab.com if any conditions of this licensing are not clear to you.
14
15 This program is free software; you can redistribute it and/or modify
16 it under the terms of the GNU General Public License as published by
17 the Free Software Foundation, either version 2 of the License, or
18 (at your option) any later version.
19
20 This program is distributed in the hope that it will be useful,
21 but WITHOUT ANY WARRANTY; without even the implied warranty of
22 MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
23 GNU General Public License for more details.
24
25 You should have received a copy of the GNU General Public License
26 along with this program. If not, see <http://www.gnu.org/licenses/>.
27 */
28
29 #include "remotemodelserver.h"
30 #include "server.h"
31 #include <core/probeguard.h>
32 #include <common/protocol.h>
33 #include <common/message.h>
34 #include <common/modelevent.h>
35 #include <common/sourcelocation.h>
36
37 #include <compat/qasconst.h>
38
39 #include <QAbstractItemModel>
40 #include <QSortFilterProxyModel>
41 #include <QDataStream>
42 #include <QDebug>
43 #include <QBuffer>
44 #include <QIcon>
45
46 #include <iostream>
47
48 using namespace GammaRay;
49 using namespace std;
50
51 void(*RemoteModelServer::s_registerServerCallback)() = nullptr;
52
RemoteModelServer(const QString & objectName,QObject * parent)53 RemoteModelServer::RemoteModelServer(const QString &objectName, QObject *parent)
54 : QObject(parent)
55 , m_model(nullptr)
56 , m_dummyBuffer(new QBuffer(&m_dummyData, this))
57 , m_monitored(false)
58 {
59 setObjectName(objectName);
60 m_dummyBuffer->open(QIODevice::WriteOnly);
61 registerServer();
62 }
63
64 RemoteModelServer::~RemoteModelServer() = default;
65
model() const66 QAbstractItemModel *RemoteModelServer::model() const
67 {
68 return m_model;
69 }
70
setModel(QAbstractItemModel * model)71 void RemoteModelServer::setModel(QAbstractItemModel *model)
72 {
73 if (model == m_model)
74 return;
75
76 if (m_model)
77 disconnectModel();
78
79 m_model = model;
80 if (m_model && m_monitored)
81 connectModel();
82
83 if (m_monitored)
84 modelReset();
85 }
86
connectModel()87 void RemoteModelServer::connectModel()
88 {
89 Q_ASSERT(m_model);
90 Model::used(m_model);
91
92 connect(m_model.data(), &QAbstractItemModel::headerDataChanged,
93 this, &RemoteModelServer::headerDataChanged);
94 connect(m_model.data(), &QAbstractItemModel::rowsInserted,
95 this, &RemoteModelServer::rowsInserted);
96 connect(m_model.data(), &QAbstractItemModel::rowsAboutToBeMoved,
97 this, &RemoteModelServer::rowsAboutToBeMoved);
98 connect(m_model.data(), &QAbstractItemModel::rowsMoved,
99 this, &RemoteModelServer::rowsMoved);
100 connect(m_model.data(), &QAbstractItemModel::rowsRemoved,
101 this, &RemoteModelServer::rowsRemoved);
102 connect(m_model.data(), &QAbstractItemModel::columnsInserted,
103 this, &RemoteModelServer::columnsInserted);
104 connect(m_model.data(), &QAbstractItemModel::columnsMoved,
105 this, &RemoteModelServer::columnsMoved);
106 connect(m_model.data(), &QAbstractItemModel::columnsRemoved,
107 this, &RemoteModelServer::columnsRemoved);
108 connect(m_model.data(), &QAbstractItemModel::dataChanged,
109 this, &RemoteModelServer::dataChanged);
110 connect(m_model.data(),
111 &QAbstractItemModel::layoutChanged,
112 this,
113 &RemoteModelServer::layoutChanged);
114 connect(m_model.data(), &QAbstractItemModel::modelReset, this, &RemoteModelServer::modelReset);
115 connect(m_model.data(), &QObject::destroyed, this, &RemoteModelServer::modelDeleted);
116 }
117
disconnectModel()118 void RemoteModelServer::disconnectModel()
119 {
120 Q_ASSERT(m_model);
121 Model::unused(m_model);
122
123 disconnect(m_model.data(), &QAbstractItemModel::headerDataChanged,
124 this, &RemoteModelServer::headerDataChanged);
125 disconnect(m_model.data(), &QAbstractItemModel::rowsInserted,
126 this, &RemoteModelServer::rowsInserted);
127 disconnect(m_model.data(), &QAbstractItemModel::rowsAboutToBeMoved,
128 this, &RemoteModelServer::rowsAboutToBeMoved);
129 disconnect(m_model.data(), &QAbstractItemModel::rowsMoved,
130 this, &RemoteModelServer::rowsMoved);
131 disconnect(m_model.data(), &QAbstractItemModel::rowsRemoved,
132 this, &RemoteModelServer::rowsRemoved);
133 disconnect(m_model.data(), &QAbstractItemModel::columnsInserted,
134 this, &RemoteModelServer::columnsInserted);
135 disconnect(m_model.data(), &QAbstractItemModel::columnsMoved,
136 this, &RemoteModelServer::columnsMoved);
137 disconnect(m_model.data(), &QAbstractItemModel::columnsRemoved,
138 this, &RemoteModelServer::columnsRemoved);
139 disconnect(m_model.data(), &QAbstractItemModel::dataChanged,
140 this, &RemoteModelServer::dataChanged);
141 disconnect(m_model.data(), &QAbstractItemModel::layoutChanged,
142 this, &RemoteModelServer::layoutChanged);
143 disconnect(m_model.data(), &QAbstractItemModel::modelReset, this, &RemoteModelServer::modelReset);
144 disconnect(m_model.data(), &QObject::destroyed, this, &RemoteModelServer::modelDeleted);
145 }
146
newRequest(const GammaRay::Message & msg)147 void RemoteModelServer::newRequest(const GammaRay::Message &msg)
148 {
149 if (!m_model && msg.type() != Protocol::ModelSyncBarrier)
150 return;
151
152 ProbeGuard g;
153 switch (msg.type()) {
154 case Protocol::ModelRowColumnCountRequest:
155 {
156 quint32 size;
157 msg >> size;
158 Q_ASSERT(size > 0);
159
160 Message reply(m_myAddress, Protocol::ModelRowColumnCountReply);
161 reply << size;
162 for (quint32 i = 0; i < size; ++i) {
163 Protocol::ModelIndex index;
164 msg >> index;
165 const QModelIndex qmIndex = Protocol::toQModelIndex(m_model, index);
166
167 qint32 rowCount = -1, columnCount = -1;
168 if (index.isEmpty() || qmIndex.isValid()) {
169 rowCount = m_model->rowCount(qmIndex);
170 columnCount = m_model->columnCount(qmIndex);
171 }
172
173 reply << index << rowCount << columnCount;
174 }
175 sendMessage(reply);
176 break;
177 }
178
179 case Protocol::ModelContentRequest:
180 {
181 quint32 size;
182 msg >> size;
183 Q_ASSERT(size > 0);
184
185 QVector<QModelIndex> indexes;
186 indexes.reserve(size);
187 for (quint32 i = 0; i < size; ++i) {
188 Protocol::ModelIndex index;
189 msg >> index;
190 const QModelIndex qmIndex = Protocol::toQModelIndex(m_model, index);
191 if (!qmIndex.isValid())
192 continue;
193 indexes.push_back(qmIndex);
194 }
195 if (indexes.isEmpty())
196 break;
197
198 Message msg(m_myAddress, Protocol::ModelContentReply);
199 msg << quint32(indexes.size());
200 for (const auto &qmIndex : qAsConst(indexes))
201 msg << Protocol::fromQModelIndex(qmIndex)
202 << filterItemData(m_model->itemData(qmIndex))
203 << qint32(m_model->flags(qmIndex));
204
205 sendMessage(msg);
206 break;
207 }
208
209 case Protocol::ModelHeaderRequest:
210 {
211 qint8 orientation;
212 qint32 section;
213 msg >> orientation >> section;
214 Q_ASSERT(orientation == Qt::Horizontal || orientation == Qt::Vertical);
215 Q_ASSERT(section >= 0);
216
217 QHash<qint32, QVariant> data;
218 // TODO: add all roles
219 data.insert(Qt::DisplayRole,
220 m_model->headerData(section, static_cast<Qt::Orientation>(orientation),
221 Qt::DisplayRole));
222 data.insert(Qt::ToolTipRole,
223 m_model->headerData(section, static_cast<Qt::Orientation>(orientation),
224 Qt::ToolTipRole));
225
226 Message msg(m_myAddress, Protocol::ModelHeaderReply);
227 msg << orientation << section << data;
228 sendMessage(msg);
229 break;
230 }
231
232 case Protocol::ModelSetDataRequest:
233 {
234 Protocol::ModelIndex index;
235 int role;
236 QVariant value;
237 msg >> index >> role >> value;
238
239 m_model->setData(Protocol::toQModelIndex(m_model, index), value, role);
240 break;
241 }
242
243 case Protocol::ModelSortRequest:
244 {
245 quint32 column, order;
246 msg >> column >> order;
247 m_model->sort(column, (Qt::SortOrder)order);
248 break;
249 }
250
251 case Protocol::ModelSyncBarrier:
252 {
253 qint32 barrierId;
254 msg >> barrierId;
255 Message reply(m_myAddress, Protocol::ModelSyncBarrier);
256 reply << barrierId;
257 sendMessage(reply);
258 break;
259 }
260 }
261 }
262
filterItemData(QMap<int,QVariant> && itemData) const263 QMap<int, QVariant> RemoteModelServer::filterItemData(QMap<int, QVariant> &&itemData) const
264 {
265 for (auto it = itemData.begin(); it != itemData.end();) {
266 if (!it.value().isValid()) {
267 it = itemData.erase(it);
268 } else if (it.value().userType() == qMetaTypeId<QIcon>()) {
269 // see also: https://bugreports.qt-project.org/browse/QTBUG-33321
270 const QIcon icon = it.value().value<QIcon>();
271 ///TODO: what size to use? icon.availableSizes is empty...
272 if (!icon.isNull())
273 it.value() = icon.pixmap(QSize(16, 16));
274 ++it;
275 } else if (canSerialize(it.value())) {
276 ++it;
277 } else {
278 // qWarning() << "Cannot serialize QVariant of type" << it.value().typeName();
279 it = itemData.erase(it);
280 }
281 }
282 return std::move(itemData);
283 }
284
canSerialize(const QVariant & value) const285 bool RemoteModelServer::canSerialize(const QVariant &value) const
286 {
287 if (qstrcmp(value.typeName(), "QJSValue") == 0 || qstrcmp(value.typeName(), "QJsonObject") == 0 || qstrcmp(value.typeName(), "QJsonValue") == 0 || qstrcmp(value.typeName(), "QJsonArray") == 0) {
288 // QJSValue tries to serialize nested elements and asserts if that fails
289 // too bad it can contain QObject* as nested element, which obviously can't be serialized...
290 // QJsonObject serialization fails due to QTBUG-73437
291 return false;
292 }
293
294 // recurse into containers
295 if (value.canConvert<QVariantList>()) {
296 QSequentialIterable it = value.value<QSequentialIterable>();
297 for (const QVariant &v : it) {
298 if (!canSerialize(v))
299 return false;
300 }
301 // note: do not return true here, the fact we can write every single element
302 // does not mean we can write the entire thing, or vice vesa...
303 } else if (value.canConvert<QVariantMap>()) {
304 auto iterable = value.value<QAssociativeIterable>();
305 for (auto it = iterable.begin(); it != iterable.end(); ++it) {
306 if (!canSerialize(it.value()) || !canSerialize(it.key()))
307 return false;
308 }
309 // see above
310 }
311
312 // whitelist a few expensive to encode types we know we can serialize
313 if (value.userType() == qMetaTypeId<QUrl>() || value.userType() == qMetaTypeId<GammaRay::SourceLocation>())
314 return true;
315
316 // ugly, but there doesn't seem to be a better way atm to find out without trying
317 m_dummyBuffer->seek(0);
318 QDataStream stream(m_dummyBuffer);
319 return QMetaType::save(stream, value.userType(), value.constData());
320 }
321
modelMonitored(bool monitored)322 void RemoteModelServer::modelMonitored(bool monitored)
323 {
324 if (m_monitored == monitored)
325 return;
326 m_monitored = monitored;
327 if (m_model) {
328 if (m_monitored)
329 connectModel();
330 else
331 disconnectModel();
332 }
333 }
334
dataChanged(const QModelIndex & begin,const QModelIndex & end,const QVector<int> & roles)335 void RemoteModelServer::dataChanged(const QModelIndex &begin, const QModelIndex &end,
336 const QVector<int> &roles)
337 {
338 if (!isConnected())
339 return;
340 Message msg(m_myAddress, Protocol::ModelContentChanged);
341 msg << Protocol::fromQModelIndex(begin) << Protocol::fromQModelIndex(end) << roles;
342 sendMessage(msg);
343 }
344
headerDataChanged(Qt::Orientation orientation,int first,int last)345 void RemoteModelServer::headerDataChanged(Qt::Orientation orientation, int first, int last)
346 {
347 if (!isConnected())
348 return;
349 Message msg(m_myAddress, Protocol::ModelHeaderChanged);
350 msg << qint8(orientation) << first << last;
351 sendMessage(msg);
352 }
353
rowsInserted(const QModelIndex & parent,int start,int end)354 void RemoteModelServer::rowsInserted(const QModelIndex &parent, int start, int end)
355 {
356 sendAddRemoveMessage(Protocol::ModelRowsAdded, parent, start, end);
357 }
358
rowsAboutToBeMoved(const QModelIndex & sourceParent,int sourceStart,int sourceEnd,const QModelIndex & destinationParent,int destinationRow)359 void RemoteModelServer::rowsAboutToBeMoved(const QModelIndex &sourceParent, int sourceStart,
360 int sourceEnd, const QModelIndex &destinationParent,
361 int destinationRow)
362 {
363 Q_UNUSED(sourceStart);
364 Q_UNUSED(sourceEnd);
365 Q_UNUSED(destinationRow);
366 m_preOpIndexes.push_back(Protocol::fromQModelIndex(sourceParent));
367 m_preOpIndexes.push_back(Protocol::fromQModelIndex(destinationParent));
368 }
369
rowsMoved(const QModelIndex & sourceParent,int sourceStart,int sourceEnd,const QModelIndex & destinationParent,int destinationRow)370 void RemoteModelServer::rowsMoved(const QModelIndex &sourceParent, int sourceStart, int sourceEnd,
371 const QModelIndex &destinationParent, int destinationRow)
372 {
373 Q_UNUSED(sourceParent);
374 Q_UNUSED(destinationParent);
375 Q_ASSERT(m_preOpIndexes.size() >= 2);
376 const auto destParentIdx = m_preOpIndexes.takeLast();
377 const auto sourceParentIdx = m_preOpIndexes.takeLast();
378 sendMoveMessage(Protocol::ModelRowsMoved, sourceParentIdx, sourceStart, sourceEnd,
379 destParentIdx, destinationRow);
380 }
381
rowsRemoved(const QModelIndex & parent,int start,int end)382 void RemoteModelServer::rowsRemoved(const QModelIndex &parent, int start, int end)
383 {
384 sendAddRemoveMessage(Protocol::ModelRowsRemoved, parent, start, end);
385 }
386
columnsInserted(const QModelIndex & parent,int start,int end)387 void RemoteModelServer::columnsInserted(const QModelIndex &parent, int start, int end)
388 {
389 sendAddRemoveMessage(Protocol::ModelColumnsAdded, parent, start, end);
390 }
391
columnsMoved(const QModelIndex & sourceParent,int sourceStart,int sourceEnd,const QModelIndex & destinationParent,int destinationColumn)392 void RemoteModelServer::columnsMoved(const QModelIndex &sourceParent, int sourceStart,
393 int sourceEnd, const QModelIndex &destinationParent,
394 int destinationColumn)
395 {
396 sendMoveMessage(Protocol::ModelColumnsMoved,
397 Protocol::fromQModelIndex(sourceParent), sourceStart, sourceEnd,
398 Protocol::fromQModelIndex(destinationParent), destinationColumn);
399 }
400
columnsRemoved(const QModelIndex & parent,int start,int end)401 void RemoteModelServer::columnsRemoved(const QModelIndex &parent, int start, int end)
402 {
403 sendAddRemoveMessage(Protocol::ModelColumnsRemoved, parent, start, end);
404 }
405
406
layoutChanged(const QList<QPersistentModelIndex> & parents,QAbstractItemModel::LayoutChangeHint hint)407 void RemoteModelServer::layoutChanged(const QList<QPersistentModelIndex> &parents,
408 QAbstractItemModel::LayoutChangeHint hint)
409 {
410 QVector<Protocol::ModelIndex> indexes;
411 indexes.reserve(parents.size());
412 for (const auto &index : parents)
413 indexes.push_back(Protocol::fromQModelIndex(index));
414 sendLayoutChanged(indexes, hint);
415 }
416
417
sendLayoutChanged(const QVector<Protocol::ModelIndex> & parents,quint32 hint)418 void RemoteModelServer::sendLayoutChanged(const QVector< Protocol::ModelIndex > &parents,
419 quint32 hint)
420 {
421 if (!isConnected())
422 return;
423 Message msg(m_myAddress, Protocol::ModelLayoutChanged);
424 msg << parents << hint;
425 sendMessage(msg);
426 }
427
modelReset()428 void RemoteModelServer::modelReset()
429 {
430 if (!isConnected())
431 return;
432 sendMessage(Message(m_myAddress, Protocol::ModelReset));
433 }
434
sendAddRemoveMessage(Protocol::MessageType type,const QModelIndex & parent,int start,int end)435 void RemoteModelServer::sendAddRemoveMessage(Protocol::MessageType type, const QModelIndex &parent,
436 int start, int end)
437 {
438 if (!isConnected())
439 return;
440 Message msg(m_myAddress, type);
441 msg << Protocol::fromQModelIndex(parent) << start << end;
442 sendMessage(msg);
443 }
444
sendMoveMessage(Protocol::MessageType type,const Protocol::ModelIndex & sourceParent,int sourceStart,int sourceEnd,const Protocol::ModelIndex & destinationParent,int destinationIndex)445 void RemoteModelServer::sendMoveMessage(Protocol::MessageType type,
446 const Protocol::ModelIndex &sourceParent, int sourceStart,
447 int sourceEnd,
448 const Protocol::ModelIndex &destinationParent,
449 int destinationIndex)
450 {
451 if (!isConnected())
452 return;
453 Message msg(m_myAddress, type);
454 msg << sourceParent << qint32(sourceStart) << qint32(sourceEnd)
455 << destinationParent << qint32(destinationIndex);
456 sendMessage(msg);
457 }
458
modelDeleted()459 void RemoteModelServer::modelDeleted()
460 {
461 m_model = nullptr;
462 if (m_monitored)
463 modelReset();
464 }
465
registerServer()466 void RemoteModelServer::registerServer()
467 {
468 if (Q_UNLIKELY(s_registerServerCallback)) { // called from the ctor, so we can't rely on virtuals
469 s_registerServerCallback();
470 return;
471 }
472 m_myAddress = Server::instance()->registerObject(objectName(), this, Server::ExportProperties);
473 Server::instance()->registerMessageHandler(m_myAddress, this, "newRequest");
474 Server::instance()->registerMonitorNotifier(m_myAddress, this, "modelMonitored");
475 connect(Endpoint::instance(), &Endpoint::disconnected, this, [this] { modelMonitored(); });
476 }
477
isConnected() const478 bool RemoteModelServer::isConnected() const
479 {
480 return Endpoint::isConnected();
481 }
482
sendMessage(const Message & msg) const483 void RemoteModelServer::sendMessage(const Message &msg) const
484 {
485 Endpoint::send(msg);
486 }
487
proxyDynamicSortFilter() const488 bool RemoteModelServer::proxyDynamicSortFilter() const
489 {
490 if (auto proxy = qobject_cast<QSortFilterProxyModel *>(m_model))
491 return proxy->dynamicSortFilter();
492 return false;
493 }
494
setProxyDynamicSortFilter(bool dynamicSortFilter)495 void RemoteModelServer::setProxyDynamicSortFilter(bool dynamicSortFilter)
496 {
497 if (auto proxy = qobject_cast<QSortFilterProxyModel *>(m_model))
498 proxy->setDynamicSortFilter(dynamicSortFilter);
499 }
500
proxyFilterCaseSensitivity() const501 Qt::CaseSensitivity RemoteModelServer::proxyFilterCaseSensitivity() const
502 {
503 if (auto proxy = qobject_cast<QSortFilterProxyModel *>(m_model))
504 return proxy->filterCaseSensitivity();
505 return Qt::CaseSensitive;
506 }
507
setProxyFilterCaseSensitivity(Qt::CaseSensitivity caseSensitivity)508 void RemoteModelServer::setProxyFilterCaseSensitivity(Qt::CaseSensitivity caseSensitivity)
509 {
510 if (auto proxy = qobject_cast<QSortFilterProxyModel *>(m_model))
511 proxy->setFilterCaseSensitivity(caseSensitivity);
512 }
513
proxyFilterKeyColumn() const514 int RemoteModelServer::proxyFilterKeyColumn() const
515 {
516 if (auto proxy = qobject_cast<QSortFilterProxyModel *>(m_model))
517 return proxy->filterKeyColumn();
518 return 0;
519 }
520
setProxyFilterKeyColumn(int column)521 void RemoteModelServer::setProxyFilterKeyColumn(int column)
522 {
523 if (auto proxy = qobject_cast<QSortFilterProxyModel *>(m_model))
524 proxy->setFilterKeyColumn(column);
525 }
526
proxyFilterRegExp() const527 QRegExp RemoteModelServer::proxyFilterRegExp() const
528 {
529 if (auto proxy = qobject_cast<QSortFilterProxyModel *>(m_model))
530 return proxy->filterRegExp();
531 return QRegExp();
532 }
533
setProxyFilterRegExp(const QRegExp & regExp)534 void RemoteModelServer::setProxyFilterRegExp(const QRegExp ®Exp)
535 {
536 if (auto proxy = qobject_cast<QSortFilterProxyModel *>(m_model))
537 proxy->setFilterRegExp(regExp);
538 }
539