Qt
Internal/Contributor docs for the Qt SDK. Note: These are NOT official API docs; those are found at https://doc.qt.io/
Loading...
Searching...
No Matches
qdbusmodel.cpp
Go to the documentation of this file.
1// Copyright (C) 2016 The Qt Company Ltd.
2// SPDX-License-Identifier: LicenseRef-Qt-Commercial OR GPL-3.0-only WITH Qt-GPL-exception-1.0
3
4#include "qdbusmodel.h"
5
6#include <QtCore/QDebug>
7#include <QtCore/QList>
8
9#include <QtDBus/QDBusObjectPath>
10#include <QtDBus/QDBusInterface>
11#include <QtDBus/QDBusReply>
12
13#include <QtXml/QDomDocument>
14
15using namespace Qt::StringLiterals;
16
18{
19 inline QDBusItem(QDBusModel::Type aType, const QString &aName, QDBusItem *aParent = 0)
21 {}
22 inline ~QDBusItem()
23 {
24 qDeleteAll(children);
25 }
26
27 QString path() const
28 {
29 Q_ASSERT(type == QDBusModel::PathItem);
30
31 QString s;
32 const QDBusItem *item = this;
33 while (item) {
34 s.prepend(item->name);
35 item = item->parent;
36 }
37 if (s.size() > 1)
38 s.chop(1); // remove tailing slash
39 return s;
40 }
41
49};
50
51QDomDocument QDBusModel::introspect(const QString &path)
52{
53 QDomDocument doc;
54
55 QDBusInterface iface(service, path, "org.freedesktop.DBus.Introspectable"_L1, c);
56 if (!iface.isValid()) {
57 QDBusError err(iface.lastError());
58 emit busError(tr("Cannot introspect object %1 at %2:\n %3 (%4)\n")
59 .arg(path)
60 .arg(service)
61 .arg(err.name())
62 .arg(err.message()));
63 return doc;
64 }
65
66 QDBusReply<QString> xml = iface.call("Introspect"_L1);
67
68 if (!xml.isValid()) {
69 QDBusError err(xml.error());
70 if (err.isValid()) {
71 emit busError(tr("Call to object %1 at %2:\n %3 (%4) failed\n")
72 .arg(path)
73 .arg(service)
74 .arg(err.name())
75 .arg(err.message()));
76 } else {
77 emit busError(tr("Invalid XML received from object %1 at %2\n").arg(path).arg(service));
78 }
79 return doc;
80 }
81
82 doc.setContent(xml.value());
83 return doc;
84}
85
86void QDBusModel::addMethods(QDBusItem *parent, const QDomElement &iface)
87{
88 Q_ASSERT(parent);
89
90 QDomElement child = iface.firstChildElement();
91 while (!child.isNull()) {
92 QDBusItem *item = nullptr;
93 if (child.tagName() == "method"_L1) {
94 item = new QDBusItem(QDBusModel::MethodItem, child.attribute("name"_L1), parent);
95 item->caption = tr("Method: %1").arg(item->name);
96 //get "type" from <arg> where "direction" is "in"
97 QDomElement n = child.firstChildElement();
98 while (!n.isNull()) {
99 if (n.attribute("direction"_L1) == "in"_L1)
100 item->typeSignature += n.attribute("type"_L1);
101 n = n.nextSiblingElement();
102 }
103 } else if (child.tagName() == "signal"_L1) {
104 item = new QDBusItem(QDBusModel::SignalItem, child.attribute("name"_L1), parent);
105 item->caption = tr("Signal: %1").arg(item->name);
106 } else if (child.tagName() == "property"_L1) {
107 item = new QDBusItem(QDBusModel::PropertyItem, child.attribute("name"_L1), parent);
108 item->caption = tr("Property: %1").arg(item->name);
109 } else {
110 qDebug() << "addMethods: unknown tag:" << child.tagName();
111 }
112 if (item)
113 parent->children.append(item);
114
115 child = child.nextSiblingElement();
116 }
117}
118
119void QDBusModel::addPath(QDBusItem *parent)
120{
121 Q_ASSERT(parent);
122
123 QString path = parent->path();
124
125 QDomDocument doc = introspect(path);
126 QDomElement node = doc.documentElement();
127 QDomElement child = node.firstChildElement();
128 while (!child.isNull()) {
129 if (child.tagName() == "node"_L1) {
130 QDBusItem *item = new QDBusItem(QDBusModel::PathItem,
131 child.attribute("name"_L1) + '/'_L1, parent);
132 parent->children.append(item);
133
134 addMethods(item, child);
135 } else if (child.tagName() == "interface"_L1) {
136 QDBusItem *item =
137 new QDBusItem(QDBusModel::InterfaceItem, child.attribute("name"_L1), parent);
138 parent->children.append(item);
139
140 addMethods(item, child);
141 } else {
142 qDebug() << "addPath: Unknown tag name:" << child.tagName();
143 }
144 child = child.nextSiblingElement();
145 }
146
147 parent->isPrefetched = true;
148}
149
150QDBusModel::QDBusModel(const QString &aService, const QDBusConnection &connection)
151 : service(aService), c(connection), root(0)
152{
153 root = new QDBusItem(QDBusModel::PathItem, "/"_L1);
154}
155
157{
158 delete root;
159}
160
161QModelIndex QDBusModel::index(int row, int column, const QModelIndex &parent) const
162{
163 const QDBusItem *item = static_cast<QDBusItem *>(parent.internalPointer());
164 if (!item)
165 item = root;
166
167 if (column != 0 || row < 0 || row >= item->children.size())
168 return QModelIndex();
169
170 return createIndex(row, 0, item->children.at(row));
171}
172
173QModelIndex QDBusModel::parent(const QModelIndex &child) const
174{
175 QDBusItem *item = static_cast<QDBusItem *>(child.internalPointer());
176 if (!item || !item->parent || !item->parent->parent)
177 return QModelIndex();
178
179 return createIndex(item->parent->parent->children.indexOf(item->parent), 0, item->parent);
180}
181
182int QDBusModel::rowCount(const QModelIndex &parent) const
183{
184 QDBusItem *item = static_cast<QDBusItem *>(parent.internalPointer());
185 if (!item)
186 item = root;
187 if (!item->isPrefetched)
188 const_cast<QDBusModel *>(this)->addPath(item);
189
190 return item->children.size();
191}
192
193int QDBusModel::columnCount(const QModelIndex &) const
194{
195 return 1;
196}
197
198QVariant QDBusModel::data(const QModelIndex &index, int role) const
199{
200 const QDBusItem *item = static_cast<QDBusItem *>(index.internalPointer());
201 if (!item)
202 return QVariant();
203
204 if (role != Qt::DisplayRole)
205 return QVariant();
206
207 return item->caption.isEmpty() ? item->name : item->caption;
208}
209
210QVariant QDBusModel::headerData(int section, Qt::Orientation orientation, int role) const
211{
212 if (role != Qt::DisplayRole || orientation == Qt::Vertical || section != 0)
213 return QVariant();
214
215 return tr("Methods");
216}
217
218QDBusModel::Type QDBusModel::itemType(const QModelIndex &index) const
219{
220 const QDBusItem *item = static_cast<QDBusItem *>(index.internalPointer());
221 return item ? item->type : PathItem;
222}
223
224void QDBusModel::refresh(const QModelIndex &aIndex)
225{
226 QModelIndex index = aIndex;
227 while (index.isValid() && static_cast<QDBusItem *>(index.internalPointer())->type != PathItem) {
228 index = index.parent();
229 }
230
231 QDBusItem *item = static_cast<QDBusItem *>(index.internalPointer());
232 if (!item)
233 item = root;
234
235 if (!item->children.isEmpty()) {
236 beginRemoveRows(index, 0, item->children.size() - 1);
237 qDeleteAll(item->children);
238 item->children.clear();
239 endRemoveRows();
240 }
241
242 addPath(item);
243 if (!item->children.isEmpty()) {
244 beginInsertRows(index, 0, item->children.size() - 1);
245 endInsertRows();
246 }
247}
248
249QString QDBusModel::dBusPath(const QModelIndex &aIndex) const
250{
251 QModelIndex index = aIndex;
252 while (index.isValid() && static_cast<QDBusItem *>(index.internalPointer())->type != PathItem) {
253 index = index.parent();
254 }
255
256 QDBusItem *item = static_cast<QDBusItem *>(index.internalPointer());
257 if (!item)
258 item = root;
259
260 return item->path();
261}
262
263QString QDBusModel::dBusInterface(const QModelIndex &index) const
264{
265 QDBusItem *item = static_cast<QDBusItem *>(index.internalPointer());
266 if (!item)
267 return QString();
268 if (item->type == InterfaceItem)
269 return item->name;
270 if (item->parent && item->parent->type == InterfaceItem)
271 return item->parent->name;
272 return QString();
273}
274
275QString QDBusModel::dBusMethodName(const QModelIndex &index) const
276{
277 QDBusItem *item = static_cast<QDBusItem *>(index.internalPointer());
278 return item ? item->name : QString();
279}
280
281QString QDBusModel::dBusTypeSignature(const QModelIndex &index) const
282{
283 QDBusItem *item = static_cast<QDBusItem *>(index.internalPointer());
284 return item ? item->typeSignature : QString();
285}
286
287QModelIndex QDBusModel::findObject(const QDBusObjectPath &objectPath)
288{
289 QStringList path = objectPath.path().split('/'_L1, Qt::SkipEmptyParts);
290
291 QDBusItem *item = root;
292 int childIdx = -1;
293 while (item && !path.isEmpty()) {
294 const QString branch = path.takeFirst() + '/'_L1;
295 childIdx = -1;
296
297 // do a linear search over all the children
298 for (int i = 0; i < item->children.size(); ++i) {
299 QDBusItem *child = item->children.at(i);
300 if (child->type == PathItem && child->name == branch) {
301 item = child;
302 childIdx = i;
303
304 // prefetch the found branch
305 if (!item->isPrefetched)
306 addPath(item);
307 break;
308 }
309 }
310
311 // branch not found - bail out
312 if (childIdx == -1)
313 return QModelIndex();
314 }
315
316 // found the right item
317 if (childIdx != -1 && item && path.isEmpty())
318 return createIndex(childIdx, 0, item);
319
320 return QModelIndex();
321}
QString dBusMethodName(const QModelIndex &index) const
int columnCount(const QModelIndex &parent=QModelIndex()) const override
Returns the number of columns for the children of the given parent.
QVariant data(const QModelIndex &index, int role=Qt::DisplayRole) const override
Returns the data stored under the given role for the item referred to by the index.
QString dBusTypeSignature(const QModelIndex &index) const
int rowCount(const QModelIndex &parent=QModelIndex()) const override
Returns the number of rows under the given parent.
QModelIndex parent(const QModelIndex &child) const override
QVariant headerData(int section, Qt::Orientation orientation, int role=Qt::DisplayRole) const override
Returns the data for the given role and section in the header with the specified orientation.
void refresh(const QModelIndex &index=QModelIndex())
QString dBusPath(const QModelIndex &index) const
QModelIndex findObject(const QDBusObjectPath &objectPath)
Type itemType(const QModelIndex &index) const
QString dBusInterface(const QModelIndex &index) const
QDBusModel(const QString &service, const QDBusConnection &connection)
QModelIndex index(int row, int column, const QModelIndex &parent=QModelIndex()) const override
Returns the index of the item in the model specified by the given row, column and parent index.
QT_FORWARD_DECLARE_CLASS(QDomDocument)
QT_FORWARD_DECLARE_CLASS(QDomElement)
QDBusItem * parent
QDBusModel::Type type
QString name
QString path() const
QString caption
QString typeSignature
QList< QDBusItem * > children
bool isPrefetched
QDBusItem(QDBusModel::Type aType, const QString &aName, QDBusItem *aParent=0)