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 "qqmlinfo.h" |
5 | |
6 | #include "qqmldata_p.h" |
7 | #include "qqmlmetatype_p.h" |
8 | #include "qqmlengine_p.h" |
9 | #include "qqmlsourcecoordinate_p.h" |
10 | |
11 | #include <QCoreApplication> |
12 | |
13 | QT_BEGIN_NAMESPACE |
14 | |
15 | /*! |
16 | \class QQmlInfo |
17 | \inmodule QtQml |
18 | \brief The QQmlInfo class allows logging of QML-related messages. |
19 | |
20 | QQmlInfo is an opaque handle for QML-related diagnostic messages. You can |
21 | use the \c{<<} operator to add content to the message. When the QQmlInfo |
22 | object is destroyed, it prints the resulting message along with information |
23 | on the context. |
24 | |
25 | \sa qmlDebug, qmlInfo, qmlWarning |
26 | */ |
27 | |
28 | /*! |
29 | \fn QQmlInfo qmlDebug(const QObject *object) |
30 | \relates QQmlInfo |
31 | \since 5.9 |
32 | |
33 | Prints debug messages that include the file and line number for the |
34 | specified QML \a object. |
35 | |
36 | //! [qqmlinfo-desc] |
37 | When QML types produce logging messages, it improves traceability |
38 | if they include the QML file and line number on which the |
39 | particular instance was instantiated. |
40 | |
41 | To include the file and line number, an object must be passed. If |
42 | the file and line number is not available for that instance |
43 | (either it was not instantiated by the QML engine or location |
44 | information is disabled), "unknown location" will be used instead. |
45 | //! [qqmlinfo-desc] |
46 | |
47 | For example, |
48 | |
49 | \code |
50 | qmlDebug(object) << "Internal state: 42"; |
51 | \endcode |
52 | |
53 | prints |
54 | |
55 | \badcode |
56 | QML MyCustomType (unknown location): Internal state: 42 |
57 | \endcode |
58 | |
59 | \sa qmlInfo, qmlWarning |
60 | */ |
61 | |
62 | /*! |
63 | \fn QQmlInfo qmlInfo(const QObject *object) |
64 | \relates QQmlInfo |
65 | |
66 | Prints informational messages that include the file and line number for the |
67 | specified QML \a object. |
68 | |
69 | \include qqmlinfo.cpp qqmlinfo-desc |
70 | |
71 | For example, |
72 | |
73 | \code |
74 | qmlInfo(object) << tr("component property is a write-once property"); |
75 | \endcode |
76 | |
77 | prints |
78 | |
79 | \badcode |
80 | QML MyCustomType (unknown location): component property is a write-once property |
81 | \endcode |
82 | |
83 | \note In versions prior to Qt 5.9, qmlInfo reported messages using a warning |
84 | QtMsgType. For Qt 5.9 and above, qmlInfo uses an info QtMsgType. To send |
85 | warnings, use qmlWarning. |
86 | |
87 | \sa qmlDebug, qmlWarning |
88 | */ |
89 | |
90 | /*! |
91 | \fn QQmlInfo qmlWarning(const QObject *object) |
92 | \relates QQmlInfo |
93 | \since 5.9 |
94 | |
95 | Prints warning messages that include the file and line number for the |
96 | specified QML \a object. |
97 | |
98 | \include qqmlinfo.cpp qqmlinfo-desc |
99 | |
100 | For example, |
101 | |
102 | \code |
103 | qmlInfo(object) << tr("property cannot be set to 0"); |
104 | \endcode |
105 | |
106 | prints |
107 | |
108 | \badcode |
109 | QML MyCustomType (unknown location): property cannot be set to 0 |
110 | \endcode |
111 | |
112 | \sa qmlDebug, qmlInfo |
113 | */ |
114 | |
115 | /*! |
116 | \fn QQmlInfo qmlDebug(const QObject *object, const QQmlError &error) |
117 | \internal |
118 | */ |
119 | |
120 | /*! |
121 | \fn QQmlInfo qmlDebug(const QObject *object, const QList<QQmlError> &errors) |
122 | \internal |
123 | */ |
124 | |
125 | /*! |
126 | \fn QQmlInfo qmlInfo(const QObject *object, const QQmlError &error) |
127 | \internal |
128 | */ |
129 | |
130 | /*! |
131 | \fn QQmlInfo qmlInfo(const QObject *object, const QList<QQmlError> &errors) |
132 | \internal |
133 | */ |
134 | |
135 | /*! |
136 | \fn QQmlInfo qmlWarning(const QObject *object, const QQmlError &error) |
137 | \internal |
138 | */ |
139 | |
140 | /*! |
141 | \fn QQmlInfo qmlWarning(const QObject *object, const QList<QQmlError> &errors) |
142 | \internal |
143 | */ |
144 | |
145 | class QQmlInfoPrivate |
146 | { |
147 | public: |
148 | QQmlInfoPrivate(QtMsgType type) |
149 | : ref (1) |
150 | , msgType(type) |
151 | , object(nullptr) |
152 | {} |
153 | |
154 | int ref; |
155 | QtMsgType msgType; |
156 | const QObject *object; |
157 | QString buffer; |
158 | QList<QQmlError> errors; |
159 | }; |
160 | |
161 | QQmlInfo::QQmlInfo(QQmlInfoPrivate *p) |
162 | : QDebug(&p->buffer), d(p) |
163 | { |
164 | nospace(); |
165 | } |
166 | |
167 | QQmlInfo::QQmlInfo(const QQmlInfo &other) |
168 | : QDebug(other), d(other.d) |
169 | { |
170 | d->ref++; |
171 | } |
172 | |
173 | QQmlInfo::~QQmlInfo() |
174 | { |
175 | if (0 == --d->ref) { |
176 | QList<QQmlError> errors = d->errors; |
177 | |
178 | QQmlEngine *engine = nullptr; |
179 | |
180 | if (!d->buffer.isEmpty()) { |
181 | QQmlError error; |
182 | error.setMessageType(d->msgType); |
183 | |
184 | QObject *object = const_cast<QObject *>(d->object); |
185 | |
186 | if (object) { |
187 | // Some objects (e.g. like attached objects created in C++) won't have an associated engine, |
188 | // but we can still try to look for a parent object that does. |
189 | QObject *objectWithEngine = object; |
190 | while (objectWithEngine) { |
191 | engine = qmlEngine(objectWithEngine); |
192 | if (engine) |
193 | break; |
194 | objectWithEngine = objectWithEngine->parent(); |
195 | } |
196 | |
197 | if (!objectWithEngine || objectWithEngine == object) { |
198 | d->buffer.prepend(s: QLatin1String("QML " ) + QQmlMetaType::prettyTypeName(object) + QLatin1String(": " )); |
199 | } else { |
200 | d->buffer.prepend(s: QLatin1String("QML " ) + QQmlMetaType::prettyTypeName(object: objectWithEngine) |
201 | + QLatin1String(" (parent or ancestor of " ) + QQmlMetaType::prettyTypeName(object) + QLatin1String("): " )); |
202 | } |
203 | |
204 | QQmlData *ddata = QQmlData::get(object: objectWithEngine ? objectWithEngine : object, create: false); |
205 | if (ddata && ddata->outerContext) { |
206 | error.setUrl(ddata->outerContext->url()); |
207 | error.setLine(qmlConvertSourceCoordinate<quint16, int>(n: ddata->lineNumber)); |
208 | error.setColumn(qmlConvertSourceCoordinate<quint16, int>(n: ddata->columnNumber)); |
209 | } |
210 | } |
211 | |
212 | error.setDescription(d->buffer); |
213 | |
214 | errors.prepend(t: error); |
215 | } |
216 | |
217 | QQmlEnginePrivate::warning(engine, errors); |
218 | |
219 | delete d; |
220 | } |
221 | } |
222 | |
223 | #define MESSAGE_FUNCS(FuncName, MessageLevel) \ |
224 | QQmlInfo FuncName(const QObject *me) \ |
225 | { \ |
226 | QQmlInfoPrivate *d = new QQmlInfoPrivate(MessageLevel); \ |
227 | d->object = me; \ |
228 | return QQmlInfo(d); \ |
229 | } \ |
230 | QQmlInfo FuncName(const QObject *me, const QQmlError &error) \ |
231 | { \ |
232 | QQmlInfoPrivate *d = new QQmlInfoPrivate(MessageLevel); \ |
233 | d->object = me; \ |
234 | d->errors << error; \ |
235 | return QQmlInfo(d); \ |
236 | } \ |
237 | QQmlInfo FuncName(const QObject *me, const QList<QQmlError> &errors) \ |
238 | { \ |
239 | QQmlInfoPrivate *d = new QQmlInfoPrivate(MessageLevel); \ |
240 | d->object = me; \ |
241 | d->errors = errors; \ |
242 | return QQmlInfo(d); \ |
243 | } |
244 | |
245 | MESSAGE_FUNCS(qmlDebug, QtMsgType::QtDebugMsg) |
246 | MESSAGE_FUNCS(qmlInfo, QtMsgType::QtInfoMsg) |
247 | MESSAGE_FUNCS(qmlWarning, QtMsgType::QtWarningMsg) |
248 | |
249 | QT_END_NAMESPACE |
250 | |