d/control: Recommend gdb
[gammaray-debian.git] / connectionmodel.cpp
blob2a4b3aeacfa3d4f8bc1baf1a65c1747dbe270174
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 namespace GammaRay {
36 struct Connection
38 Connection()
39 : sender(0), receiver(0), type(Qt::AutoConnection), valid(false)
42 QObject *sender;
43 QByteArray signal;
44 QObject *receiver;
45 QByteArray method;
46 QByteArray location;
47 Qt::ConnectionType type;
48 bool valid;
53 Q_DECLARE_TYPEINFO(GammaRay::Connection, Q_MOVABLE_TYPE);
55 using namespace GammaRay;
57 static bool checkMethodForObject(QObject *obj, const QByteArray &signature, bool isSender)
59 if (!obj || signature.isEmpty()) {
60 return false;
62 const QMetaObject *mo = obj->metaObject();
63 const int methodIndex = mo->indexOfMethod(signature.mid(1));
64 if (methodIndex < 0) {
65 return false;
67 const QMetaMethod method = mo->method(methodIndex);
68 if (method.methodType() != QMetaMethod::Signal &&
69 (isSender || method.methodType() != QMetaMethod::Slot)) {
70 return false;
72 const int methodCode = signature.at(0) - '0';
73 if ((methodCode == QSLOT_CODE && method.methodType() != QMetaMethod::Slot) ||
74 (methodCode == QSIGNAL_CODE && method.methodType() != QMetaMethod::Signal)) {
75 return false;
77 return true;
80 ConnectionModel::ConnectionModel(QObject *parent)
81 : QAbstractTableModel(parent)
83 qRegisterMetaType<const char*>("const char*");
84 qRegisterMetaType<Qt::ConnectionType>("Qt::ConnectionType");
87 void ConnectionModel::connectionAdded(QObject *sender, const char *signal,
88 QObject *receiver, const char *method,
89 Qt::ConnectionType type)
91 if (sender == this || receiver == this) {
92 return;
95 // when called from background, delay into foreground, otherwise call directly
96 QMetaObject::invokeMethod(this, "connectionAddedMainThread", Qt::AutoConnection,
97 Q_ARG(QObject *, sender), Q_ARG(const char *, signal),
98 Q_ARG(QObject *, receiver), Q_ARG(const char *, method),
99 Q_ARG(Qt::ConnectionType, type));
102 void ConnectionModel::connectionAddedMainThread(QObject *sender, const char *signal,
103 QObject *receiver, const char *method,
104 Qt::ConnectionType type)
106 Q_ASSERT(thread() == QThread::currentThread());
108 ReadOrWriteLocker objectLock(Probe::instance()->objectLock());
109 if (!Probe::instance()->isValidObject(sender) ||
110 !Probe::instance()->isValidObject(receiver)) {
111 return;
114 beginInsertRows(QModelIndex(), m_connections.size(), m_connections.size());
115 Connection c;
116 c.sender = sender;
117 c.signal = QMetaObject::normalizedSignature(signal);
118 c.receiver = receiver;
119 c.method = QMetaObject::normalizedSignature(method);
120 c.type = type;
121 c.location = Probe::connectLocation(signal);
123 // check if that's actually a valid connection
124 if (checkMethodForObject(sender, c.signal, true) &&
125 checkMethodForObject(receiver, c.method, false)) {
126 c.valid = QMetaObject::checkConnectArgs(c.signal, c.method);
127 } else {
128 c.valid = false;
130 //TODO: we could check more stuff here eg. if queued connection is possible etc.
131 //and use verktygs heuristics to detect likely misconnects
133 m_connections.push_back(c);
134 endInsertRows();
137 void ConnectionModel::connectionRemoved(QObject *sender, const char *signal,
138 QObject *receiver, const char *method)
140 if (sender == this || receiver == this) {
141 return;
144 // when called from background, delay into foreground, otherwise call directly
145 QMetaObject::invokeMethod(this, "connectionRemovedMainThread", Qt::AutoConnection,
146 Q_ARG(QObject *, sender), Q_ARG(const char *, signal),
147 Q_ARG(QObject *, receiver), Q_ARG(const char *, method));
150 void ConnectionModel::connectionRemovedMainThread(QObject *sender, const char *signal,
151 QObject *receiver, const char *method)
153 Q_ASSERT(thread() == QThread::currentThread());
155 QByteArray normalizedSignal, normalizedMethod;
156 if (signal) {
157 normalizedSignal = QMetaObject::normalizedSignature(signal);
159 if (method) {
160 normalizedMethod = QMetaObject::normalizedSignature(method);
163 for (int i = 0; i < m_connections.size();) {
164 bool remove = false;
166 const Connection &con = m_connections.at(i);
167 if (!con.receiver || !con.sender) {
168 // might be invalidated from a background thread
169 remove = true;
170 } else if ((sender == 0 || con.sender == sender) &&
171 (signal == 0 || con.signal == normalizedSignal) &&
172 (receiver == 0 || con.receiver == receiver) &&
173 (method == 0 || con.method == normalizedMethod)) {
174 // the connection was actually removed
175 remove = true;
178 if (remove) {
179 beginRemoveRows(QModelIndex(), i, i);
180 m_connections.remove(i);
181 endRemoveRows();
182 } else {
183 ++i;
188 QVariant ConnectionModel::data(const QModelIndex &index, int role) const
190 if (!index.isValid() || index.row() < 0 || index.row() >= m_connections.size()) {
191 return QVariant();
194 Connection con = m_connections.at(index.row());
196 ReadOrWriteLocker probeLock(Probe::instance()->objectLock());
197 if (!Probe::instance()->isValidObject(con.sender)) {
198 con.sender = 0;
200 if (!Probe::instance()->isValidObject(con.receiver)) {
201 con.receiver = 0;
204 if (role == Qt::DisplayRole) {
205 if (index.column() == 0) {
206 if (con.sender) {
207 return Util::displayString(con.sender);
208 } else {
209 return QLatin1String("<destroyed>");
212 if (index.column() == 1) {
213 return con.signal.mid(1);
215 if (index.column() == 2) {
216 if (con.receiver) {
217 return Util::displayString(con.receiver);
218 } else {
219 return QLatin1String("<destroyed>");
222 if (index.column() == 3) {
223 return con.method.mid(1);
225 if (index.column() == 4) {
226 switch (con.type) {
227 case Qt::AutoCompatConnection:
228 return QLatin1String("AutoCompatConnection");
229 case Qt::AutoConnection:
230 return QLatin1String("AutoConnection");
231 case Qt::BlockingQueuedConnection:
232 return QLatin1String("BlockingQueuedConnection");
233 case Qt::DirectConnection:
234 return QLatin1String("DirectConnection");
235 case Qt::QueuedConnection:
236 return QLatin1String("QueuedConnection");
237 case Qt::UniqueConnection:
238 return QLatin1String("UniqueConnection");
239 default:
240 return tr("Unknown connection type: %1").arg(con.type);
243 if (index.column() == 5) {
244 return con.location;
246 } else if (role == SenderRole) {
247 return QVariant::fromValue(con.sender);
248 } else if (role == ReceiverRole) {
249 return QVariant::fromValue(con.receiver);
250 } else if (role == Qt::ForegroundRole) {
251 if (!con.valid) {
252 return Qt::red;
254 } else if (role == ConnectionValidRole) {
255 return con.valid;
257 return QVariant();
260 QVariant ConnectionModel::headerData(int section, Qt::Orientation orientation, int role) const
262 if (orientation == Qt::Horizontal && role == Qt::DisplayRole) {
263 switch (section) {
264 case 0:
265 return tr("Sender");
266 case 1:
267 return tr("Signal");
268 case 2:
269 return tr("Receiver");
270 case 3:
271 return tr("Method");
272 case 4:
273 return tr("Connection Type");
274 case 5:
275 return tr("Location");
278 return QAbstractItemModel::headerData(section, orientation, role);
281 int ConnectionModel::columnCount(const QModelIndex &parent) const
283 Q_UNUSED(parent);
284 return 6;
287 int ConnectionModel::rowCount(const QModelIndex &parent) const
289 if (parent.isValid()) {
290 return 0;
292 return m_connections.size();
295 #include "connectionmodel.moc"