Qt 6.x
The Qt SDK
Loading...
Searching...
No Matches
qqmlfile.cpp
Go to the documentation of this file.
1// Copyright (C) 2016 The Qt Company Ltd.
2// SPDX-License-Identifier: LicenseRef-Qt-Commercial OR LGPL-3.0-only OR GPL-2.0-only OR GPL-3.0-only
3
4#include "qqmlfile.h"
5
6#include <QtCore/qurl.h>
7#include <QtCore/qobject.h>
8#include <QtCore/qmetaobject.h>
9#include <QtCore/qfile.h>
10#include <private/qqmlengine_p.h>
11#include <private/qqmlglobal_p.h>
12
23
24static char qrc_string[] = "qrc";
25static char file_string[] = "file";
26
27#if defined(Q_OS_ANDROID)
28static char assets_string[] = "assets";
29static char content_string[] = "content";
30static char authority_externalstorage[] = "com.android.externalstorage.documents";
31static char authority_downloads_documents[] = "com.android.providers.downloads.documents";
32static char authority_media_documents[] = "com.android.providers.media.documents";
33#endif
34
35class QQmlFilePrivate;
36
37#if QT_CONFIG(qml_network)
38class QQmlFileNetworkReply : public QObject
39{
41public:
42 QQmlFileNetworkReply(QQmlEngine *, QQmlFilePrivate *, const QUrl &);
43 ~QQmlFileNetworkReply();
44
46 void finished();
47 void downloadProgress(qint64, qint64);
48
49public slots:
50 void networkFinished();
51 void networkDownloadProgress(qint64, qint64);
52
53public:
54 static int finishedIndex;
55 static int downloadProgressIndex;
56 static int networkFinishedIndex;
57 static int networkDownloadProgressIndex;
58 static int replyFinishedIndex;
59 static int replyDownloadProgressIndex;
60
61private:
62 QQmlEngine *m_engine;
63 QQmlFilePrivate *m_p;
64
65 QNetworkReply *m_reply;
66};
67#endif
68
70{
71public:
73
74 mutable QUrl url;
76
78
79 enum Error {
81 };
82
85#if QT_CONFIG(qml_network)
86 QQmlFileNetworkReply *reply;
87#endif
88};
89
90#if QT_CONFIG(qml_network)
91int QQmlFileNetworkReply::finishedIndex = -1;
92int QQmlFileNetworkReply::downloadProgressIndex = -1;
93int QQmlFileNetworkReply::networkFinishedIndex = -1;
94int QQmlFileNetworkReply::networkDownloadProgressIndex = -1;
95int QQmlFileNetworkReply::replyFinishedIndex = -1;
96int QQmlFileNetworkReply::replyDownloadProgressIndex = -1;
97
98QQmlFileNetworkReply::QQmlFileNetworkReply(QQmlEngine *e, QQmlFilePrivate *p, const QUrl &url)
99: m_engine(e), m_p(p), m_reply(nullptr)
100{
101 if (finishedIndex == -1) {
102 finishedIndex = QMetaMethod::fromSignal(&QQmlFileNetworkReply::finished).methodIndex();
103 downloadProgressIndex = QMetaMethod::fromSignal(&QQmlFileNetworkReply::downloadProgress).methodIndex();
104 const QMetaObject *smo = &staticMetaObject;
105 networkFinishedIndex = smo->indexOfMethod("networkFinished()");
106 networkDownloadProgressIndex = smo->indexOfMethod("networkDownloadProgress(qint64,qint64)");
107
110 }
111 Q_ASSERT(finishedIndex != -1 && downloadProgressIndex != -1 &&
112 networkFinishedIndex != -1 && networkDownloadProgressIndex != -1 &&
113 replyFinishedIndex != -1 && replyDownloadProgressIndex != -1);
114
115 QNetworkRequest req(url);
117
118 m_reply = m_engine->networkAccessManager()->get(req);
119 QMetaObject::connect(m_reply, replyFinishedIndex, this, networkFinishedIndex);
120 QMetaObject::connect(m_reply, replyDownloadProgressIndex, this, networkDownloadProgressIndex);
121}
122
123QQmlFileNetworkReply::~QQmlFileNetworkReply()
124{
125 if (m_reply) {
126 m_reply->disconnect();
127 m_reply->deleteLater();
128 }
129}
130
131void QQmlFileNetworkReply::networkFinished()
132{
133 if (m_reply->error()) {
134 m_p->errorString = m_reply->errorString();
135 m_p->error = QQmlFilePrivate::Network;
136 } else {
137 m_p->data = m_reply->readAll();
138 }
139
140 m_reply->deleteLater();
141 m_reply = nullptr;
142
143 m_p->reply = nullptr;
144 emit finished();
145 delete this;
146}
147
148void QQmlFileNetworkReply::networkDownloadProgress(qint64 a, qint64 b)
149{
150 emit downloadProgress(a, b);
151}
152#endif // qml_network
153
155: error(None)
156#if QT_CONFIG(qml_network)
157, reply(nullptr)
158#endif
159{
160}
161
163: d(new QQmlFilePrivate)
164{
165}
166
168: d(new QQmlFilePrivate)
169{
170 load(e, url);
171}
172
174 : QQmlFile(e, QUrl(url))
175{
176}
177
179{
180#if QT_CONFIG(qml_network)
181 delete d->reply;
182#endif
183 delete d;
184 d = nullptr;
185}
186
188{
189 return status() == Null;
190}
191
193{
194 return status() == Ready;
195}
196
198{
199 return status() == Error;
200}
201
203{
204 return status() == Loading;
205}
206
208{
209 if (!d->urlString.isEmpty()) {
210 d->url = QUrl(d->urlString);
211 d->urlString = QString();
212 }
213 return d->url;
214}
215
217{
218 if (d->url.isEmpty() && d->urlString.isEmpty())
219 return Null;
220#if QT_CONFIG(qml_network)
221 else if (d->reply)
222 return Loading;
223#endif
224 else if (d->error != QQmlFilePrivate::None)
225 return Error;
226 else
227 return Ready;
228}
229
231{
232 switch (d->error) {
233 default:
235 return QString();
237 return QLatin1String("File not found");
239 return QLatin1String("File name case mismatch");
240 }
241}
242
244{
245 return d->data.size();
246}
247
248const char *QQmlFile::data() const
249{
250 return d->data.constData();
251}
252
254{
255 return d->data;
256}
257
259{
261
262 clear();
263 d->url = url;
264
265 if (isLocalFile(url)) {
267
268 if (!QQml_isFileCaseCorrect(lf)) {
270 return;
271 }
272
273 QFile file(lf);
275 d->data = file.readAll();
276 } else {
278 }
279 } else {
280#if QT_CONFIG(qml_network)
281 d->reply = new QQmlFileNetworkReply(engine, d, url);
282#else
284#endif
285 }
286}
287
289{
291
292 clear();
293
294 d->urlString = url;
295
296 if (isLocalFile(url)) {
298
299 if (!QQml_isFileCaseCorrect(lf)) {
301 return;
302 }
303
304 QFile file(lf);
306 d->data = file.readAll();
307 } else {
309 }
310 } else {
311#if QT_CONFIG(qml_network)
312 QUrl qurl(url);
313 d->url = qurl;
314 d->urlString = QString();
315 d->reply = new QQmlFileNetworkReply(engine, d, qurl);
316#else
318#endif
319 }
320}
321
323{
324 d->url = QUrl();
325 d->urlString = QString();
326 d->data = QByteArray();
328}
329
331{
332 clear();
333}
334
335#if QT_CONFIG(qml_network)
336bool QQmlFile::connectFinished(QObject *object, const char *method)
337{
338 if (!d || !d->reply) {
339 qWarning("QQmlFile: connectFinished() called when not loading.");
340 return false;
341 }
342
343 return QObject::connect(d->reply, SIGNAL(finished()),
344 object, method);
345}
346
347bool QQmlFile::connectFinished(QObject *object, int method)
348{
349 if (!d || !d->reply) {
350 qWarning("QQmlFile: connectFinished() called when not loading.");
351 return false;
352 }
353
354 return QMetaObject::connect(d->reply, QQmlFileNetworkReply::finishedIndex,
355 object, method);
356}
357
358bool QQmlFile::connectDownloadProgress(QObject *object, const char *method)
359{
360 if (!d || !d->reply) {
361 qWarning("QQmlFile: connectDownloadProgress() called when not loading.");
362 return false;
363 }
364
365 return QObject::connect(d->reply, SIGNAL(downloadProgress(qint64,qint64)),
366 object, method);
367}
368
369bool QQmlFile::connectDownloadProgress(QObject *object, int method)
370{
371 if (!d || !d->reply) {
372 qWarning("QQmlFile: connectDownloadProgress() called when not loading.");
373 return false;
374 }
375
376 return QMetaObject::connect(d->reply, QQmlFileNetworkReply::downloadProgressIndex,
377 object, method);
378}
379#endif
380
389{
390 QString scheme = url.scheme();
391
392 if ((scheme.size() == 4 && 0 == scheme.compare(QLatin1String(file_string), Qt::CaseInsensitive)) ||
393 (scheme.size() == 3 && 0 == scheme.compare(QLatin1String(qrc_string), Qt::CaseInsensitive))) {
394 return true;
395
396#if defined(Q_OS_ANDROID)
397 } else if (scheme.length() == 6 && 0 == scheme.compare(QLatin1String(assets_string), Qt::CaseInsensitive)) {
398 return true;
399 } else if (scheme.length() == 7 && 0 == scheme.compare(QLatin1String(content_string), Qt::CaseInsensitive)) {
400 return true;
401#endif
402
403 } else {
404 return false;
405 }
406}
407
416{
417 if (url.size() < 5 /* qrc:/ */)
418 return false;
419
420 QChar f = url[0];
421
422 if (f == QLatin1Char('f') || f == QLatin1Char('F')) {
423
424 return url.size() >= 7 /* file:// */ &&
426 url[4] == QLatin1Char(':') && url[5] == QLatin1Char('/') && url[6] == QLatin1Char('/');
427
428 } else if (f == QLatin1Char('q') || f == QLatin1Char('Q')) {
429
430 return url.size() >= 5 /* qrc:/ */ &&
432 url[3] == QLatin1Char(':') && url[4] == QLatin1Char('/');
433
434 }
435
436#if defined(Q_OS_ANDROID)
437 else if (f == QLatin1Char('a') || f == QLatin1Char('A')) {
438 return url.length() >= 8 /* assets:/ */ &&
439 url.startsWith(QLatin1String(assets_string), Qt::CaseInsensitive) &&
440 url[6] == QLatin1Char(':') && url[7] == QLatin1Char('/');
441 } else if (f == QLatin1Char('c') || f == QLatin1Char('C')) {
442 return url.length() >= 9 /* content:/ */ &&
443 url.startsWith(QLatin1String(content_string), Qt::CaseInsensitive) &&
444 url[7] == QLatin1Char(':') && url[8] == QLatin1Char('/');
445 }
446#endif
447
448 return false;
449}
450
451#if defined(Q_OS_ANDROID)
452static bool hasLocalContentAuthority(const QUrl &url)
453{
454 const QString authority = url.authority();
455 return authority.isEmpty()
456 || authority == QLatin1String(authority_externalstorage)
457 || authority == QLatin1String(authority_downloads_documents)
458 || authority == QLatin1String(authority_media_documents);
459}
460#endif
461
470{
471 QString scheme = url.scheme();
472
473 // file: URLs with two slashes following the scheme can be interpreted as local files
474 // where the slashes are part of the path. Therefore, disregard the authority.
475 // See QUrl::toLocalFile().
476 if (scheme.size() == 4 && scheme.startsWith(QLatin1String(file_string), Qt::CaseInsensitive))
477 return true;
478
479 if (scheme.size() == 3 && scheme.startsWith(QLatin1String(qrc_string), Qt::CaseInsensitive))
480 return url.authority().isEmpty();
481
482#if defined(Q_OS_ANDROID)
483 if (scheme.length() == 6
484 && scheme.startsWith(QLatin1String(assets_string), Qt::CaseInsensitive))
485 return url.authority().isEmpty();
486 if (scheme.length() == 7
487 && scheme.startsWith(QLatin1String(content_string), Qt::CaseInsensitive))
488 return hasLocalContentAuthority(url);
489#endif
490
491 return false;
492}
493
494static bool hasScheme(const QString &url, const char *scheme, qsizetype schemeLength)
495{
496 const qsizetype urlLength = url.size();
497
498 if (urlLength < schemeLength + 1)
499 return false;
500
501 if (!url.startsWith(QLatin1String(scheme, scheme + schemeLength), Qt::CaseInsensitive))
502 return false;
503
504 if (url[schemeLength] != QLatin1Char(':'))
505 return false;
506
507 return true;
508}
509
510static qsizetype authorityOffset(const QString &url, qsizetype schemeLength)
511{
512 const qsizetype urlLength = url.size();
513
514 if (urlLength < schemeLength + 3)
515 return -1;
516
517 const QLatin1Char slash('/');
518 if (url[schemeLength + 1] == slash && url[schemeLength + 2] == slash) {
519 // Exactly two slashes denote an authority.
520 if (urlLength < schemeLength + 4 || url[schemeLength + 3] != slash)
521 return schemeLength + 3;
522 }
523
524 return -1;
525}
526
527#if defined(Q_OS_ANDROID)
528static bool hasLocalContentAuthority(const QString &url, qsizetype schemeLength)
529{
530 const qsizetype offset = authorityOffset(url, schemeLength);
531 if (offset == -1)
532 return true; // no authority is a local authority.
533
534 const QString authorityAndPath = url.sliced(offset);
535 return authorityAndPath.startsWith(QLatin1String(authority_externalstorage))
536 || authorityAndPath.startsWith(QLatin1String(authority_downloads_documents))
537 || authorityAndPath.startsWith(QLatin1String(authority_media_documents));
538}
539
540#endif
541
550{
551 if (url.size() < 4 /* qrc: */)
552 return false;
553
554 switch (url[0].toLatin1()) {
555 case 'f':
556 case 'F': {
557 // file: URLs with two slashes following the scheme can be interpreted as local files
558 // where the slashes are part of the path. Therefore, disregard the authority.
559 // See QUrl::toLocalFile().
560 const qsizetype fileLength = strlen(file_string);
561 return url.startsWith(QLatin1String(file_string, file_string + fileLength),
563 && url.size() > fileLength
564 && url[fileLength] == QLatin1Char(':');
565 }
566 case 'q':
567 case 'Q':
568 return hasScheme(url, qrc_string, strlen(qrc_string))
569 && authorityOffset(url, strlen(qrc_string)) == -1;
570#if defined(Q_OS_ANDROID)
571 case 'a':
572 case 'A':
573 return hasScheme(url, assets_string, strlen(assets_string))
574 && authorityOffset(url, strlen(assets_string)) == -1;
575 case 'c':
576 case 'C':
577 return hasScheme(url, content_string, strlen(content_string))
578 && hasLocalContentAuthority(url, strlen(content_string));
579#endif
580 default:
581 break;
582 }
583
584 return false;
585}
586
592{
593 if (url.scheme().compare(QLatin1String("qrc"), Qt::CaseInsensitive) == 0) {
594 if (url.authority().isEmpty())
595 return QLatin1Char(':') + url.path();
596 return QString();
597 }
598
599#if defined(Q_OS_ANDROID)
600 if (url.scheme().compare(QLatin1String("assets"), Qt::CaseInsensitive) == 0)
601 return url.authority().isEmpty() ? url.toString() : QString();
602 if (url.scheme().compare(QLatin1String("content"), Qt::CaseInsensitive) == 0) {
603 if (hasLocalContentAuthority(url))
604 return url.toString();
605 return QString();
606 }
607#endif
608 return url.toLocalFile();
609}
610
612{
613 const QUrl file(url);
614 if (!file.isLocalFile())
615 return QString();
616
617 // QUrl::toLocalFile() interprets two slashes as part of the path.
618 // Therefore windows hostnames like "//servername/path/to/file.txt" are preserved.
619
620 return file.toLocalFile();
621}
622
624{
625 const qsizetype urlLength = url.size();
626 if (urlLength < offset + 2)
627 return false;
628
629 const QLatin1Char slash('/');
630 if (url[offset] != slash || url[offset + 1] != slash)
631 return false;
632
633 if (urlLength < offset + 3)
634 return true;
635
636 return url[offset + 2] != slash;
637}
638
644{
645 if (url.startsWith(QLatin1String("qrc://"), Qt::CaseInsensitive)) {
646 // Exactly two slashes are bad because that's a URL authority.
647 // One slash is fine and >= 3 slashes are file.
648 if (url.size() == 6 || url[6] != QLatin1Char('/')) {
649 Q_ASSERT(isDoubleSlashed(url, strlen("qrc:")));
650 return QString();
651 }
652 Q_ASSERT(!isDoubleSlashed(url, strlen("qrc:")));
653 return QLatin1Char(':') + QStringView{url}.mid(6);
654 }
655
656 if (url.startsWith(QLatin1String("qrc:"), Qt::CaseInsensitive)) {
657 Q_ASSERT(!isDoubleSlashed(url, strlen("qrc:")));
658 if (url.size() > 4)
659 return QLatin1Char(':') + QStringView{url}.mid(4);
660 return QStringLiteral(":");
661 }
662
663#if defined(Q_OS_ANDROID)
664 if (url.startsWith(QLatin1String("assets:"), Qt::CaseInsensitive))
665 return isDoubleSlashed(url, strlen("assets:")) ? QString() : url;
666 if (hasScheme(url, content_string, strlen(content_string)))
667 return hasLocalContentAuthority(url, strlen(content_string)) ? url : QString();
668#endif
669
670 return toLocalFile(url);
671}
672
674
675#include "qqmlfile.moc"
\inmodule QtCore
Definition qbytearray.h:57
qsizetype size() const noexcept
Returns the number of bytes in this byte array.
Definition qbytearray.h:474
const char * constData() const noexcept
Returns a pointer to the const data stored in the byte array.
Definition qbytearray.h:122
\inmodule QtCore
Definition qchar.h:48
\inmodule QtCore
Definition qfile.h:93
bool open(OpenMode flags) override
Opens the file using OpenMode mode, returning true if successful; otherwise false.
Definition qfile.cpp:881
QByteArray readAll()
Reads all remaining data from the device, and returns it as a byte array.
static QMetaMethod fromSignal(PointerToMemberFunction signal)
int methodIndex() const
The QNetworkReply class contains the data and headers for a request sent with QNetworkAccessManager.
void downloadProgress(qint64 bytesReceived, qint64 bytesTotal)
This signal is emitted to indicate the progress of the download part of this network request,...
void finished()
This signal is emitted when the reply has finished processing.
The QNetworkRequest class holds a request to be sent with QNetworkAccessManager.
\inmodule QtCore
Definition qobject.h:90
static QMetaObject::Connection connect(const QObject *sender, const char *signal, const QObject *receiver, const char *member, Qt::ConnectionType=Qt::AutoConnection)
\threadsafe
Definition qobject.cpp:2823
The QQmlEngine class provides an environment for instantiating QML components.
Definition qqmlengine.h:57
QByteArray data
Definition qqmlfile.cpp:77
QString errorString
Definition qqmlfile.cpp:84
QString urlString
Definition qqmlfile.cpp:75
The QQmlFile class gives access to local and remote files.
Definition qqmlfile.h:18
qint64 size() const
Definition qqmlfile.cpp:243
static bool isLocalFile(const QString &url)
Returns true if url is a local file that can be opened with QFile.
Definition qqmlfile.cpp:549
static bool isSynchronous(const QString &url)
Returns true if QQmlFile will open url synchronously.
Definition qqmlfile.cpp:415
bool isReady() const
Definition qqmlfile.cpp:192
QString error() const
Definition qqmlfile.cpp:230
bool isError() const
Definition qqmlfile.cpp:197
static QString urlToLocalFileOrQrc(const QString &)
If url is a local file returns a path suitable for passing to QFile.
Definition qqmlfile.cpp:643
bool isNull() const
Definition qqmlfile.cpp:187
QUrl url() const
Definition qqmlfile.cpp:207
const char * data() const
Definition qqmlfile.cpp:248
QByteArray dataByteArray() const
Definition qqmlfile.cpp:253
Status status() const
Definition qqmlfile.cpp:216
@ Loading
Definition qqmlfile.h:25
bool isLoading() const
Definition qqmlfile.cpp:202
void load(QQmlEngine *, const QUrl &)
Definition qqmlfile.cpp:258
void clear()
Definition qqmlfile.cpp:322
\inmodule QtCore
Definition qstringview.h:76
\macro QT_RESTRICTED_CAST_FROM_ASCII
Definition qstring.h:127
bool startsWith(const QString &s, Qt::CaseSensitivity cs=Qt::CaseSensitive) const
Returns true if the string starts with s; otherwise returns false.
Definition qstring.cpp:5299
qsizetype size() const
Returns the number of characters in this string.
Definition qstring.h:182
bool isEmpty() const
Returns true if the string has no characters; otherwise returns false.
Definition qstring.h:1083
int compare(const QString &s, Qt::CaseSensitivity cs=Qt::CaseSensitive) const noexcept
Definition qstring.cpp:6498
qsizetype length() const
Returns the number of characters in this string.
Definition qstring.h:187
\inmodule QtCore
Definition qurl.h:94
bool isEmpty() const
Returns true if the URL has no data; otherwise returns false.
Definition qurl.cpp:1888
QString authority(ComponentFormattingOptions options=PrettyDecoded) const
Returns the authority of the URL if it is defined; otherwise an empty string is returned.
Definition qurl.cpp:2052
QString scheme() const
Returns the scheme of the URL.
Definition qurl.cpp:1983
QString toString(FormattingOptions options=FormattingOptions(PrettyDecoded)) const
Returns a string representation of the URL.
Definition qurl.cpp:2828
QString toLocalFile() const
Returns the path of this URL formatted as a local file path.
Definition qurl.cpp:3411
QString path(ComponentFormattingOptions options=FullyDecoded) const
Returns the path of the URL.
Definition qurl.cpp:2465
double e
Combined button and popup list for selecting options.
@ CaseInsensitive
DBusConnection const char DBusError * error
DBusConnection const char DBusError DBusBusType DBusError return DBusConnection DBusHandleMessageFunction void DBusFreeFunction return DBusConnection return DBusConnection return const char DBusError return DBusConnection DBusMessage dbus_uint32_t return DBusConnection dbus_bool_t DBusConnection DBusAddWatchFunction DBusRemoveWatchFunction DBusWatchToggledFunction void DBusFreeFunction return DBusConnection DBusDispatchStatusFunction void DBusFreeFunction DBusTimeout return DBusTimeout return DBusWatch return DBusWatch unsigned int return DBusError const DBusError return const DBusMessage return DBusMessage return DBusMessage return DBusMessage return DBusMessage return DBusMessage return DBusMessageIter int const void return DBusMessageIter DBusMessageIter return DBusMessageIter void DBusMessageIter void int return DBusMessage DBusMessageIter return DBusMessageIter return DBusMessageIter DBusMessageIter const char const char const char const char * method
#define qWarning
Definition qlogging.h:162
#define SIGNAL(a)
Definition qobjectdefs.h:52
GLboolean GLboolean GLboolean b
GLboolean GLboolean GLboolean GLboolean a
[7]
GLfloat GLfloat f
GLenum GLuint GLintptr offset
GLfloat GLfloat p
[1]
bool QQml_isFileCaseCorrect(const QString &fileName, int lengthIn)
Returns true if the case of fileName is equivalent to the file case of fileName on disk,...
static QT_BEGIN_NAMESPACE char qrc_string[]
Definition qqmlfile.cpp:24
static QString toLocalFile(const QString &url)
Definition qqmlfile.cpp:611
static bool isDoubleSlashed(const QString &url, qsizetype offset)
Definition qqmlfile.cpp:623
static char file_string[]
Definition qqmlfile.cpp:25
static qsizetype authorityOffset(const QString &url, qsizetype schemeLength)
Definition qqmlfile.cpp:510
static bool hasScheme(const QString &url, const char *scheme, qsizetype schemeLength)
Definition qqmlfile.cpp:494
#define Q_ASSERT(cond)
Definition qrandom.cpp:47
QLatin1StringView QLatin1String
Definition qstringfwd.h:31
#define QStringLiteral(str)
#define QT_CONFIG(feature)
#define Q_OBJECT
#define slots
#define signals
#define emit
ptrdiff_t qsizetype
Definition qtypes.h:70
long long qint64
Definition qtypes.h:55
if(qFloatDistance(a, b)<(1<< 7))
[0]
QFile file
[0]
QUrl url("example.com")
[constructor-url-reference]
QObject::connect nullptr
QNetworkReply * reply
QJSEngine engine
[0]
\inmodule QtCore \reentrant
Definition qchar.h:17
\inmodule QtCore
static Connection connect(const QObject *sender, int signal_index, const QObject *receiver, int method_index, int type=0, int *types=nullptr)
Definition qobject.cpp:3419
int indexOfMethod(const char *method) const
Finds method and returns its index; otherwise returns -1.