Install gammaray_probe.so to /usr/lib/gammaray
[gammaray-debian.git] / connectionmodel.cpp
blob54ef77f50e834e3db5a8fc3d0af24b6acd02d2dd
1 /*
2 connectionmodel.cpp
4 This file is part of GammaRay, the Qt application inspection and
5 manipulation tool.
7 Copyright (C) 2010-2011 Klarälvdalens Datakonsult AB, a KDAB Group company, info@kdab.com
8 Author: Volker Krause <volker.krause@kdab.com>
10 This program is free software; you can redistribute it and/or modify
11 it under the terms of the GNU General Public License as published by
12 the Free Software Foundation, either version 2 of the License, or
13 (at your option) any later version.
15 This program is distributed in the hope that it will be useful,
16 but WITHOUT ANY WARRANTY; without even the implied warranty of
17 MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
18 GNU General Public License for more details.
20 You should have received a copy of the GNU General Public License
21 along with this program. If not, see <http://www.gnu.org/licenses/>.
24 #include "connectionmodel.h"
25 #include "util.h"
26 #include "probe.h"
27 #include "readorwritelocker.h"
29 #include <QDebug>
30 #include <QMetaMethod>
31 #include <QMetaObject>
32 #include <QThread>
34 using namespace GammaRay;
36 static bool checkMethodForObject(QObject *obj, const QByteArray &signature, bool isSender)
38 if (!obj || signature.isEmpty()) {
39 return false;
41 const QMetaObject *mo = obj->metaObject();
42 const int methodIndex = mo->indexOfMethod(signature.mid(1));
43 if (methodIndex < 0) {
44 return false;
46 const QMetaMethod method = mo->method(methodIndex);
47 if (method.methodType() != QMetaMethod::Signal &&
48 (isSender || method.methodType() != QMetaMethod::Slot)) {
49 return false;
51 const int methodCode = signature.at(0) - '0';
52 if ((methodCode == QSLOT_CODE && method.methodType() != QMetaMethod::Slot) ||
53 (methodCode == QSIGNAL_CODE && method.methodType() != QMetaMethod::Signal)) {
54 return false;
56 return true;
59 ConnectionModel::Connection::Connection()
60 : sender(0), receiver(0), type(Qt::AutoConnection), valid(false)
65 ConnectionModel::ConnectionModel(QObject *parent)
66 : QAbstractTableModel(parent),
67 m_lock(QReadWriteLock::Recursive)
69 qRegisterMetaType<const char*>("const char*");
70 qRegisterMetaType<Qt::ConnectionType>("Qt::ConnectionType");
73 void ConnectionModel::connectionAdded(QObject *sender, const char *signal,
74 QObject *receiver, const char *method,
75 Qt::ConnectionType type)
77 if (sender == this || receiver == this) {
78 return;
81 // when called from background, delay into foreground, otherwise call directly
82 QMetaObject::invokeMethod(this, "connectionAddedMainThread", Qt::AutoConnection,
83 Q_ARG(QObject *, sender), Q_ARG(const char *, signal),
84 Q_ARG(QObject *, receiver), Q_ARG(const char *, method),
85 Q_ARG(Qt::ConnectionType, type));
88 void ConnectionModel::connectionAddedMainThread(QObject *sender, const char *signal,
89 QObject *receiver, const char *method,
90 Qt::ConnectionType type)
92 Q_ASSERT(thread() == QThread::currentThread());
94 ReadOrWriteLocker objectLock(Probe::instance()->objectLock());
95 if (!Probe::instance()->isValidObject(sender) ||
96 !Probe::instance()->isValidObject(receiver)) {
97 return;
100 QWriteLocker lock(&m_lock);
102 beginInsertRows(QModelIndex(), m_connections.size(), m_connections.size());
103 Connection c;
104 c.sender = sender;
105 c.signal = QMetaObject::normalizedSignature(signal);
106 c.receiver = receiver;
107 c.method = QMetaObject::normalizedSignature(method);
108 c.type = type;
109 c.location = Probe::connectLocation(signal);
111 // check if that's actually a valid connection
112 if (checkMethodForObject(sender, c.signal, true) &&
113 checkMethodForObject(receiver, c.method, false)) {
114 c.valid = QMetaObject::checkConnectArgs(c.signal, c.method);
115 } else {
116 c.valid = false;
118 //TODO: we could check more stuff here eg. if queued connection is possible etc.
119 //and use verktygs heuristics to detect likely misconnects
121 m_connections.push_back(c);
122 endInsertRows();
125 void ConnectionModel::connectionRemoved(QObject *sender, const char *signal,
126 QObject *receiver, const char *method)
128 if (sender == this || receiver == this) {
129 return;
132 // when called from background, delay into foreground, otherwise call directly
133 QMetaObject::invokeMethod(this, "connectionRemovedMainThread", Qt::AutoConnection,
134 Q_ARG(QObject *, sender), Q_ARG(const char *, signal),
135 Q_ARG(QObject *, receiver), Q_ARG(const char *, method));
138 void ConnectionModel::connectionRemovedMainThread(QObject *sender, const char *signal,
139 QObject *receiver, const char *method)
141 Q_ASSERT(thread() == QThread::currentThread());
143 QByteArray normalizedSignal, normalizedMethod;
144 if (signal) {
145 normalizedSignal = QMetaObject::normalizedSignature(signal);
147 if (method) {
148 normalizedMethod = QMetaObject::normalizedSignature(method);
151 QWriteLocker lock(&m_lock);
152 for (int i = 0; i < m_connections.size();) {
153 bool remove = false;
155 const Connection &con = m_connections.at(i);
156 if (!con.receiver || !con.sender) {
157 // might be invalidated from a background thread
158 remove = true;
159 } else if ((sender == 0 || con.sender == sender) &&
160 (signal == 0 || con.signal == normalizedSignal) &&
161 (receiver == 0 || con.receiver == receiver) &&
162 (method == 0 || con.method == normalizedMethod)) {
163 // the connection was actually removed
164 remove = true;
167 if (remove) {
168 beginRemoveRows(QModelIndex(), i, i);
169 m_connections.remove(i);
170 endRemoveRows();
171 } else {
172 ++i;
177 void ConnectionModel::objectRemoved(QObject *object)
179 QWriteLocker lock(&m_lock);
181 // invalidate data
182 for (int i = 0; i < m_connections.size(); ++i) {
183 Connection &con = m_connections[i];
184 if (con.receiver == object) {
185 con.receiver = 0;
186 } else if (con.sender == object) {
187 con.sender = 0;
192 QVariant ConnectionModel::data(const QModelIndex &index, int role) const
194 ReadOrWriteLocker lock(&m_lock);
196 if (!index.isValid() || index.row() < 0 || index.row() >= m_connections.size()) {
197 return QVariant();
200 Connection con = m_connections.at(index.row());
201 lock.unlock();
203 ReadOrWriteLocker probeLock(Probe::instance()->objectLock());
204 if (!Probe::instance()->isValidObject(con.sender)) {
205 con.sender = 0;
207 if (!Probe::instance()->isValidObject(con.receiver)) {
208 con.receiver = 0;
211 if (role == Qt::DisplayRole) {
212 if (index.column() == 0) {
213 if (con.sender) {
214 return Util::displayString(con.sender);
215 } else {
216 return QLatin1String("<destroyed>");
219 if (index.column() == 1) {
220 return con.signal.mid(1);
222 if (index.column() == 2) {
223 if (con.receiver) {
224 return Util::displayString(con.receiver);
225 } else {
226 return QLatin1String("<destroyed>");
229 if (index.column() == 3) {
230 return con.method.mid(1);
232 if (index.column() == 4) {
233 switch (con.type) {
234 case Qt::AutoCompatConnection:
235 return QLatin1String("AutoCompatConnection");
236 case Qt::AutoConnection:
237 return QLatin1String("AutoConnection");
238 case Qt::BlockingQueuedConnection:
239 return QLatin1String("BlockingQueuedConnection");
240 case Qt::DirectConnection:
241 return QLatin1String("DirectConnection");
242 case Qt::QueuedConnection:
243 return QLatin1String("QueuedConnection");
244 case Qt::UniqueConnection:
245 return QLatin1String("UniqueConnection");
246 default:
247 return tr("Unknown connection type: %1").arg(con.type);
250 if (index.column() == 5) {
251 return con.location;
253 } else if (role == SenderRole) {
254 return QVariant::fromValue(con.sender);
255 } else if (role == ReceiverRole) {
256 return QVariant::fromValue(con.receiver);
257 } else if (role == Qt::ForegroundRole) {
258 if (!con.valid) {
259 return Qt::red;
261 } else if (role == ConnectionValidRole) {
262 return con.valid;
264 return QVariant();
267 QVariant ConnectionModel::headerData(int section, Qt::Orientation orientation, int role) const
269 if (orientation == Qt::Horizontal && role == Qt::DisplayRole) {
270 switch (section) {
271 case 0:
272 return tr("Sender");
273 case 1:
274 return tr("Signal");
275 case 2:
276 return tr("Receiver");
277 case 3:
278 return tr("Method");
279 case 4:
280 return tr("Connection Type");
281 case 5:
282 return tr("Location");
285 return QAbstractItemModel::headerData(section, orientation, role);
288 int ConnectionModel::columnCount(const QModelIndex &parent) const
290 Q_UNUSED(parent);
291 return 6;
294 int ConnectionModel::rowCount(const QModelIndex &parent) const
296 if (parent.isValid()) {
297 return 0;
299 ReadOrWriteLocker lock(&m_lock);
300 return m_connections.size();
303 #include "connectionmodel.moc"