4 This file is part of GammaRay, the Qt application inspection and
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"
27 #include "readorwritelocker.h"
30 #include <QMetaMethod>
31 #include <QMetaObject>
34 using namespace GammaRay
;
36 static bool checkMethodForObject(QObject
*obj
, const QByteArray
&signature
, bool isSender
)
38 if (!obj
|| signature
.isEmpty()) {
41 const QMetaObject
*mo
= obj
->metaObject();
42 const int methodIndex
= mo
->indexOfMethod(signature
.mid(1));
43 if (methodIndex
< 0) {
46 const QMetaMethod method
= mo
->method(methodIndex
);
47 if (method
.methodType() != QMetaMethod::Signal
&&
48 (isSender
|| method
.methodType() != QMetaMethod::Slot
)) {
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
)) {
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) {
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
)) {
100 QWriteLocker
lock(&m_lock
);
102 beginInsertRows(QModelIndex(), m_connections
.size(), m_connections
.size());
105 c
.signal
= QMetaObject::normalizedSignature(signal
);
106 c
.receiver
= receiver
;
107 c
.method
= QMetaObject::normalizedSignature(method
);
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
);
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
);
125 void ConnectionModel::connectionRemoved(QObject
*sender
, const char *signal
,
126 QObject
*receiver
, const char *method
)
128 if (sender
== this || receiver
== this) {
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
;
145 normalizedSignal
= QMetaObject::normalizedSignature(signal
);
148 normalizedMethod
= QMetaObject::normalizedSignature(method
);
151 QWriteLocker
lock(&m_lock
);
152 for (int i
= 0; i
< m_connections
.size();) {
155 const Connection
&con
= m_connections
.at(i
);
156 if (!con
.receiver
|| !con
.sender
) {
157 // might be invalidated from a background thread
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
168 beginRemoveRows(QModelIndex(), i
, i
);
169 m_connections
.remove(i
);
177 void ConnectionModel::objectRemoved(QObject
*object
)
179 QWriteLocker
lock(&m_lock
);
182 for (int i
= 0; i
< m_connections
.size(); ++i
) {
183 Connection
&con
= m_connections
[i
];
184 if (con
.receiver
== object
) {
186 } else if (con
.sender
== object
) {
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()) {
200 Connection con
= m_connections
.at(index
.row());
203 ReadOrWriteLocker
probeLock(Probe::instance()->objectLock());
204 if (!Probe::instance()->isValidObject(con
.sender
)) {
207 if (!Probe::instance()->isValidObject(con
.receiver
)) {
211 if (role
== Qt::DisplayRole
) {
212 if (index
.column() == 0) {
214 return Util::displayString(con
.sender
);
216 return QLatin1String("<destroyed>");
219 if (index
.column() == 1) {
220 return con
.signal
.mid(1);
222 if (index
.column() == 2) {
224 return Util::displayString(con
.receiver
);
226 return QLatin1String("<destroyed>");
229 if (index
.column() == 3) {
230 return con
.method
.mid(1);
232 if (index
.column() == 4) {
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");
247 return tr("Unknown connection type: %1").arg(con
.type
);
250 if (index
.column() == 5) {
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
) {
261 } else if (role
== ConnectionValidRole
) {
267 QVariant
ConnectionModel::headerData(int section
, Qt::Orientation orientation
, int role
) const
269 if (orientation
== Qt::Horizontal
&& role
== Qt::DisplayRole
) {
276 return tr("Receiver");
280 return tr("Connection Type");
282 return tr("Location");
285 return QAbstractItemModel::headerData(section
, orientation
, role
);
288 int ConnectionModel::columnCount(const QModelIndex
&parent
) const
294 int ConnectionModel::rowCount(const QModelIndex
&parent
) const
296 if (parent
.isValid()) {
299 ReadOrWriteLocker
lock(&m_lock
);
300 return m_connections
.size();
303 #include "connectionmodel.moc"