1 /*
2   objectbroker.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 "objectbroker.h"
30 #include "endpoint.h"
31 #include "modelevent.h"
32 
33 #include <kde/klinkitemselectionmodel.h>
34 
35 #include <QHash>
36 #include <QString>
37 #include <QAbstractItemModel>
38 #include <QItemSelectionModel>
39 #include <QAbstractProxyModel>
40 #include <QCoreApplication>
41 #include <QVector>
42 
43 namespace GammaRay {
44 struct ObjectlBrokerData {
45     ObjectlBrokerData() = default;
46     QHash<QString, QObject *> objects;
47     QHash<QString, QAbstractItemModel *> models;
48     QHash<QAbstractItemModel *, QItemSelectionModel *> selectionModels;
49     QHash<QByteArray, ObjectBroker::ClientObjectFactoryCallback> clientObjectFactories;
50     ObjectBroker::ModelFactoryCallback modelCallback = nullptr;
51     ObjectBroker::selectionModelFactoryCallback selectionCallback = nullptr;
52     QVector<QObject *> ownedObjects;
53 };
54 
Q_GLOBAL_STATIC(ObjectlBrokerData,s_objectBroker)55 Q_GLOBAL_STATIC(ObjectlBrokerData, s_objectBroker)
56 
57 void ObjectBroker::registerObject(const QString &name, QObject *object)
58 {
59     Q_ASSERT(!name.isEmpty());
60     Q_ASSERT(object->objectName().isEmpty());
61     object->setObjectName(name);
62 
63     Q_ASSERT(!s_objectBroker()->objects.contains(name));
64     s_objectBroker()->objects.insert(name, object);
65 
66     Q_ASSERT(Endpoint::instance());
67     Endpoint::instance()->registerObject(name, object);
68 }
69 
hasObject(const QString & name)70 bool ObjectBroker::hasObject(const QString &name)
71 {
72     return s_objectBroker()->objects.contains(name);
73 }
74 
objectInternal(const QString & name,const QByteArray & type)75 QObject *ObjectBroker::objectInternal(const QString &name, const QByteArray &type)
76 {
77     auto it = s_objectBroker()->objects.constFind(name);
78     if (it != s_objectBroker()->objects.constEnd())
79         return it.value();
80 
81     // Below here only valid for clients!
82     // Remote/probe side should have registered the object directly
83     QObject *obj = nullptr;
84 
85     if (!type.isEmpty()) {
86         Q_ASSERT(s_objectBroker()->clientObjectFactories.contains(type));
87         obj = s_objectBroker()->clientObjectFactories.value(type)(name, qApp);
88     } else {
89         // fallback
90         obj = new QObject(qApp);
91         registerObject(name, obj);
92     }
93     s_objectBroker()->ownedObjects.push_back(obj);
94 
95     Q_ASSERT(obj);
96     // ensure it was registered
97     Q_ASSERT_X(s_objectBroker()->objects.value(name, nullptr) == obj, Q_FUNC_INFO,
98                qPrintable(QStringLiteral("Object %1 was not registered in the broker.").arg(name)));
99 
100     return obj;
101 }
102 
registerClientObjectFactoryCallbackInternal(const QByteArray & type,ObjectBroker::ClientObjectFactoryCallback callback)103 void ObjectBroker::registerClientObjectFactoryCallbackInternal(const QByteArray &type,
104                                                                ObjectBroker::ClientObjectFactoryCallback callback)
105 {
106     Q_ASSERT(!type.isEmpty());
107     s_objectBroker()->clientObjectFactories[type] = callback;
108 }
109 
registerModelInternal(const QString & name,QAbstractItemModel * model)110 void ObjectBroker::registerModelInternal(const QString &name, QAbstractItemModel *model)
111 {
112     Q_ASSERT(!s_objectBroker()->models.contains(name));
113     model->setObjectName(name);
114     s_objectBroker()->models.insert(name, model);
115 }
116 
model(const QString & name)117 QAbstractItemModel *ObjectBroker::model(const QString &name)
118 {
119     ModelEvent event(true);
120     auto it = s_objectBroker()->models.constFind(name);
121     if (it != s_objectBroker()->models.constEnd()) {
122         QCoreApplication::sendEvent(it.value(), &event);
123         return it.value();
124     }
125 
126     if (s_objectBroker()->modelCallback) {
127         QAbstractItemModel *model = s_objectBroker()->modelCallback(name);
128         if (model) {
129             model->setObjectName(name);
130             s_objectBroker()->models.insert(name, model);
131             s_objectBroker()->ownedObjects.push_back(model);
132             QCoreApplication::sendEvent(model, &event);
133             return model;
134         }
135     }
136     return nullptr;
137 }
138 
setModelFactoryCallback(ObjectBroker::ModelFactoryCallback callback)139 void ObjectBroker::setModelFactoryCallback(ObjectBroker::ModelFactoryCallback callback)
140 {
141     s_objectBroker()->modelCallback = callback;
142 }
143 
registerSelectionModel(QItemSelectionModel * selectionModel)144 void ObjectBroker::registerSelectionModel(QItemSelectionModel *selectionModel)
145 {
146     Q_ASSERT(!s_objectBroker()->selectionModels.contains(const_cast<QAbstractItemModel *>(
147                                                              selectionModel->model())));
148     s_objectBroker()->selectionModels.insert(
149         const_cast<QAbstractItemModel *>(selectionModel->model()), selectionModel);
150 }
151 
unregisterSelectionModel(QItemSelectionModel * selectionModel)152 void ObjectBroker::unregisterSelectionModel(QItemSelectionModel *selectionModel)
153 {
154     Q_ASSERT(s_objectBroker()->selectionModels.contains(const_cast<QAbstractItemModel *>(
155                                                             selectionModel->model())));
156     s_objectBroker()->selectionModels.remove(
157         const_cast<QAbstractItemModel *>(selectionModel->model()));
158 }
159 
hasSelectionModel(QAbstractItemModel * model)160 bool ObjectBroker::hasSelectionModel(QAbstractItemModel *model)
161 {
162     return s_objectBroker()->selectionModels.contains(model);
163 }
164 
sourceModelForProxy(QAbstractItemModel * model)165 static QAbstractItemModel *sourceModelForProxy(QAbstractItemModel *model)
166 {
167     // stop once we found a registered model, this is what network communication is based on
168     if (s_objectBroker()->models.values().contains(model))
169         return model;
170 
171     QAbstractProxyModel *proxy = qobject_cast<QAbstractProxyModel *>(model);
172     if (!proxy)
173         return model;
174     return sourceModelForProxy(proxy->sourceModel());
175 }
176 
selectionModel(QAbstractItemModel * model)177 QItemSelectionModel *ObjectBroker::selectionModel(QAbstractItemModel *model)
178 {
179     auto it = s_objectBroker()->selectionModels.constFind(model);
180     if (it != s_objectBroker()->selectionModels.constEnd())
181         return it.value();
182 
183     if (s_objectBroker()->selectionCallback) {
184         QAbstractItemModel *sourceModel = sourceModelForProxy(model);
185 
186         QItemSelectionModel *selectionModel = nullptr;
187         if (sourceModel == model) {
188             selectionModel = s_objectBroker()->selectionCallback(sourceModel);
189             s_objectBroker()->ownedObjects.push_back(selectionModel);
190         } else {
191             QItemSelectionModel *sourceSelectionModel = ObjectBroker::selectionModel(sourceModel);
192             selectionModel = new KLinkItemSelectionModel(model, sourceSelectionModel, model);
193         }
194 
195         if (selectionModel) {
196             registerSelectionModel(selectionModel);
197             return selectionModel;
198         }
199     }
200     return nullptr;
201 }
202 
setSelectionModelFactoryCallback(ObjectBroker::selectionModelFactoryCallback callback)203 void ObjectBroker::setSelectionModelFactoryCallback(
204     ObjectBroker::selectionModelFactoryCallback callback)
205 {
206     s_objectBroker()->selectionCallback = callback;
207 }
208 
clear()209 void ObjectBroker::clear()
210 {
211     auto *ob = s_objectBroker();
212     qDeleteAll(ob->ownedObjects);
213     ob->ownedObjects.clear();
214     ob->objects.clear();
215     ob->models.clear();
216     ob->selectionModels.clear();
217 }
218 }
219