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>
39 : sender(0), receiver(0), type(Qt::AutoConnection
), valid(false)
47 Qt::ConnectionType type
;
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()) {
62 const QMetaObject
*mo
= obj
->metaObject();
63 const int methodIndex
= mo
->indexOfMethod(signature
.mid(1));
64 if (methodIndex
< 0) {
67 const QMetaMethod method
= mo
->method(methodIndex
);
68 if (method
.methodType() != QMetaMethod::Signal
&&
69 (isSender
|| method
.methodType() != QMetaMethod::Slot
)) {
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
)) {
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) {
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
)) {
114 beginInsertRows(QModelIndex(), m_connections
.size(), m_connections
.size());
117 c
.signal
= QMetaObject::normalizedSignature(signal
);
118 c
.receiver
= receiver
;
119 c
.method
= QMetaObject::normalizedSignature(method
);
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
);
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
);
137 void ConnectionModel::connectionRemoved(QObject
*sender
, const char *signal
,
138 QObject
*receiver
, const char *method
)
140 if (sender
== this || receiver
== this) {
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
;
157 normalizedSignal
= QMetaObject::normalizedSignature(signal
);
160 normalizedMethod
= QMetaObject::normalizedSignature(method
);
163 for (int i
= 0; i
< m_connections
.size();) {
166 const Connection
&con
= m_connections
.at(i
);
167 if (!con
.receiver
|| !con
.sender
) {
168 // might be invalidated from a background thread
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
179 beginRemoveRows(QModelIndex(), i
, i
);
180 m_connections
.remove(i
);
188 QVariant
ConnectionModel::data(const QModelIndex
&index
, int role
) const
190 if (!index
.isValid() || index
.row() < 0 || index
.row() >= m_connections
.size()) {
194 Connection con
= m_connections
.at(index
.row());
196 ReadOrWriteLocker
probeLock(Probe::instance()->objectLock());
197 if (!Probe::instance()->isValidObject(con
.sender
)) {
200 if (!Probe::instance()->isValidObject(con
.receiver
)) {
204 if (role
== Qt::DisplayRole
) {
205 if (index
.column() == 0) {
207 return Util::displayString(con
.sender
);
209 return QLatin1String("<destroyed>");
212 if (index
.column() == 1) {
213 return con
.signal
.mid(1);
215 if (index
.column() == 2) {
217 return Util::displayString(con
.receiver
);
219 return QLatin1String("<destroyed>");
222 if (index
.column() == 3) {
223 return con
.method
.mid(1);
225 if (index
.column() == 4) {
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");
240 return tr("Unknown connection type: %1").arg(con
.type
);
243 if (index
.column() == 5) {
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
) {
254 } else if (role
== ConnectionValidRole
) {
260 QVariant
ConnectionModel::headerData(int section
, Qt::Orientation orientation
, int role
) const
262 if (orientation
== Qt::Horizontal
&& role
== Qt::DisplayRole
) {
269 return tr("Receiver");
273 return tr("Connection Type");
275 return tr("Location");
278 return QAbstractItemModel::headerData(section
, orientation
, role
);
281 int ConnectionModel::columnCount(const QModelIndex
&parent
) const
287 int ConnectionModel::rowCount(const QModelIndex
&parent
) const
289 if (parent
.isValid()) {
292 return m_connections
.size();
295 #include "connectionmodel.moc"