1/****************************************************************************
2**
3** Copyright (C) 2016 Research In Motion.
4** Contact: https://www.qt.io/licensing/
5**
6** This file is part of the QtQml module of the Qt Toolkit.
7**
8** $QT_BEGIN_LICENSE:LGPL$
9** Commercial License Usage
10** Licensees holding valid commercial Qt licenses may use this file in
11** accordance with the commercial license agreement provided with the
12** Software or, alternatively, in accordance with the terms contained in
13** a written agreement between you and The Qt Company. For licensing terms
14** and conditions see https://www.qt.io/terms-conditions. For further
15** information use the contact form at https://www.qt.io/contact-us.
16**
17** GNU Lesser General Public License Usage
18** Alternatively, this file may be used under the terms of the GNU Lesser
19** General Public License version 3 as published by the Free Software
20** Foundation and appearing in the file LICENSE.LGPL3 included in the
21** packaging of this file. Please review the following information to
22** ensure the GNU Lesser General Public License version 3 requirements
23** will be met: https://www.gnu.org/licenses/lgpl-3.0.html.
24**
25** GNU General Public License Usage
26** Alternatively, this file may be used under the terms of the GNU
27** General Public License version 2.0 or (at your option) the GNU General
28** Public license version 3 or any later version approved by the KDE Free
29** Qt Foundation. The licenses are as published by the Free Software
30** Foundation and appearing in the file LICENSE.GPL2 and LICENSE.GPL3
31** included in the packaging of this file. Please review the following
32** information to ensure the GNU General Public License requirements will
33** be met: https://www.gnu.org/licenses/gpl-2.0.html and
34** https://www.gnu.org/licenses/gpl-3.0.html.
35**
36** $QT_END_LICENSE$
37**
38****************************************************************************/
39
40#include <QtQml/qqmlfile.h>
41#include <QtCore/QCoreApplication>
42#include <QtCore/QTranslator>
43#include <QQmlComponent>
44#include "qqmlapplicationengine.h"
45#include "qqmlapplicationengine_p.h"
46#include "qqmlfileselector.h"
47
48QT_BEGIN_NAMESPACE
49
50QQmlApplicationEnginePrivate::QQmlApplicationEnginePrivate(QQmlEngine *e)
51 : QQmlEnginePrivate(e)
52{
53 uiLanguage = QLocale().bcp47Name();
54}
55
56QQmlApplicationEnginePrivate::~QQmlApplicationEnginePrivate()
57{
58}
59
60void QQmlApplicationEnginePrivate::cleanUp()
61{
62 Q_Q(QQmlApplicationEngine);
63 for (auto obj : qAsConst(t&: objects))
64 obj->disconnect(receiver: q);
65
66 qDeleteAll(c: objects);
67}
68
69void QQmlApplicationEnginePrivate::init()
70{
71 Q_Q(QQmlApplicationEngine);
72 q->connect(sender: q, signal: &QQmlApplicationEngine::quit, context: QCoreApplication::instance(),
73 slot: &QCoreApplication::quit, type: Qt::QueuedConnection);
74 q->connect(sender: q, signal: &QQmlApplicationEngine::exit, context: QCoreApplication::instance(),
75 slot: &QCoreApplication::exit, type: Qt::QueuedConnection);
76 q->connect(sender: q, SIGNAL(uiLanguageChanged()), receiver: q_func(), SLOT(_q_loadTranslations()));
77#if QT_CONFIG(translation)
78 QTranslator* qtTranslator = new QTranslator(q);
79 if (qtTranslator->load(locale: QLocale(), filename: QLatin1String("qt"), prefix: QLatin1String("_"), directory: QLibraryInfo::location(QLibraryInfo::TranslationsPath), suffix: QLatin1String(".qm")))
80 QCoreApplication::installTranslator(messageFile: qtTranslator);
81 else
82 delete qtTranslator;
83#endif
84 new QQmlFileSelector(q,q);
85 QCoreApplication::instance()->setProperty(name: "__qml_using_qqmlapplicationengine", value: QVariant(true));
86}
87
88void QQmlApplicationEnginePrivate::_q_loadTranslations()
89{
90#if QT_CONFIG(translation)
91 if (translationsDirectory.isEmpty())
92 return;
93
94 Q_Q(QQmlApplicationEngine);
95
96 QScopedPointer<QTranslator> translator(new QTranslator);
97 if (!uiLanguage.isEmpty()) {
98 QLocale locale(uiLanguage);
99 if (translator->load(locale, filename: QLatin1String("qml"), prefix: QLatin1String("_"), directory: translationsDirectory, suffix: QLatin1String(".qm"))) {
100 if (activeTranslator)
101 QCoreApplication::removeTranslator(messageFile: activeTranslator.data());
102 QCoreApplication::installTranslator(messageFile: translator.data());
103 activeTranslator.swap(other&: translator);
104 }
105 } else {
106 activeTranslator.reset();
107 }
108 q->retranslate();
109#endif
110}
111
112void QQmlApplicationEnginePrivate::startLoad(const QUrl &url, const QByteArray &data, bool dataFlag)
113{
114 Q_Q(QQmlApplicationEngine);
115
116 if (url.scheme() == QLatin1String("file") || url.scheme() == QLatin1String("qrc")) {
117 QFileInfo fi(QQmlFile::urlToLocalFileOrQrc(url));
118 translationsDirectory = fi.path() + QLatin1String("/i18n");
119 } else {
120 translationsDirectory.clear();
121 }
122
123 _q_loadTranslations(); //Translations must be loaded before the QML file is
124 QQmlComponent *c = new QQmlComponent(q, q);
125
126 if (dataFlag)
127 c->setData(data,baseUrl: url);
128 else
129 c->loadUrl(url);
130
131 if (!c->isLoading()) {
132 finishLoad(component: c);
133 return;
134 }
135 QObject::connect(sender: c, signal: &QQmlComponent::statusChanged, context: q, slot: [this, c] { this->finishLoad(component: c); });
136}
137
138void QQmlApplicationEnginePrivate::finishLoad(QQmlComponent *c)
139{
140 Q_Q(QQmlApplicationEngine);
141 switch (c->status()) {
142 case QQmlComponent::Error:
143 qWarning() << "QQmlApplicationEngine failed to load component";
144 warning(c->errors());
145 q->objectCreated(object: nullptr, url: c->url());
146 break;
147 case QQmlComponent::Ready: {
148 auto newObj = initialProperties.empty() ? c->create() : c->createWithInitialProperties(initialProperties);
149
150 if (c->isError()) {
151 qWarning() << "QQmlApplicationEngine failed to create component";
152 warning(c->errors());
153 q->objectCreated(object: nullptr, url: c->url());
154 break;
155 }
156
157 objects << newObj;
158 QObject::connect(sender: newObj, signal: &QObject::destroyed, context: q, slot: [&](QObject *obj) { objects.removeAll(t: obj); });
159 q->objectCreated(object: objects.constLast(), url: c->url());
160 }
161 break;
162 case QQmlComponent::Loading:
163 case QQmlComponent::Null:
164 return; //These cases just wait for the next status update
165 }
166
167 c->deleteLater();
168}
169
170/*!
171 \class QQmlApplicationEngine
172 \since 5.1
173 \inmodule QtQml
174 \brief QQmlApplicationEngine provides a convenient way to load an application from a single QML file.
175
176 This class combines a QQmlEngine and QQmlComponent to provide a convenient way to load a single QML file. It also exposes some central application functionality to QML, which a C++/QML hybrid application would normally control from C++.
177
178 It can be used like so:
179
180 \code
181 #include <QGuiApplication>
182 #include <QQmlApplicationEngine>
183
184 int main(int argc, char *argv[])
185 {
186 QGuiApplication app(argc, argv);
187 QQmlApplicationEngine engine("main.qml");
188 return app.exec();
189 }
190 \endcode
191
192 Unlike QQuickView, QQmlApplicationEngine does not automatically create a root
193 window. If you are using visual items from Qt Quick, you will need to place
194 them inside of a \l [QML] {Window}.
195
196 You can also use QCoreApplication with QQmlApplicationEngine, if you are not using any QML modules which require a QGuiApplication (such as \c QtQuick).
197
198 List of configuration changes from a default QQmlEngine:
199
200 \list
201 \li Connecting Qt.quit() to QCoreApplication::quit()
202 \li Automatically loads translation files from an i18n directory adjacent to the main QML file.
203 \list
204 \li Translation files must have "qml_" prefix e.g. qml_ja_JP.qm.
205 \endlist
206 \li Translations are reloaded when the \c QJSEngine::uiLanguage / \c Qt.uiLanguage property is changed.
207 \li Automatically sets an incubation controller if the scene contains a QQuickWindow.
208 \li Automatically sets a \c QQmlFileSelector as the url interceptor, applying file selectors to all
209 QML files and assets.
210 \endlist
211
212 The engine behavior can be further tweaked by using the inherited methods from QQmlEngine.
213
214*/
215
216/*!
217 \fn QQmlApplicationEngine::objectCreated(QObject *object, const QUrl &url)
218
219 This signal is emitted when an object finishes loading. If loading was
220 successful, \a object contains a pointer to the loaded object, otherwise
221 the pointer is NULL.
222
223 The \a url to the component the \a object came from is also provided.
224
225 \note If the path to the component was provided as a QString containing a
226 relative path, the \a url will contain a fully resolved path to the file.
227*/
228
229/*!
230 Create a new QQmlApplicationEngine with the given \a parent. You will have to call load() later in
231 order to load a QML file.
232*/
233QQmlApplicationEngine::QQmlApplicationEngine(QObject *parent)
234: QQmlEngine(*(new QQmlApplicationEnginePrivate(this)), parent)
235{
236 Q_D(QQmlApplicationEngine);
237 d->init();
238 QJSEnginePrivate::addToDebugServer(q: this);
239}
240
241/*!
242 Create a new QQmlApplicationEngine and loads the QML file at the given \a url.
243 This is provided as a convenience, and is the same as using the empty constructor and calling load afterwards.
244*/
245QQmlApplicationEngine::QQmlApplicationEngine(const QUrl &url, QObject *parent)
246 : QQmlApplicationEngine(parent)
247{
248 load(url);
249}
250
251/*!
252 Create a new QQmlApplicationEngine and loads the QML file at the given
253 \a filePath, which must be a local file path. If a relative path is
254 given then it will be interpreted as relative to the working directory of the
255 application.
256
257 This is provided as a convenience, and is the same as using the empty constructor and calling load afterwards.
258*/
259QQmlApplicationEngine::QQmlApplicationEngine(const QString &filePath, QObject *parent)
260 : QQmlApplicationEngine(QUrl::fromUserInput(userInput: filePath, workingDirectory: QLatin1String("."), options: QUrl::AssumeLocalFile), parent)
261{
262}
263
264/*!
265 Destroys the QQmlApplicationEngine and all QML objects it loaded.
266*/
267QQmlApplicationEngine::~QQmlApplicationEngine()
268{
269 Q_D(QQmlApplicationEngine);
270 QJSEnginePrivate::removeFromDebugServer(q: this);
271 d->cleanUp();//Instantiated root objects must be deleted before the engine
272}
273
274/*!
275 Loads the root QML file located at \a url. The object tree defined by the file
276 is created immediately for local file urls. Remote urls are loaded asynchronously,
277 listen to the \l {QQmlApplicationEngine::objectCreated()}{objectCreated} signal to
278 determine when the object tree is ready.
279
280 If an error occurs, the \l {QQmlApplicationEngine::objectCreated()}{objectCreated}
281 signal is emitted with a null pointer as parameter and error messages are printed
282 with qWarning.
283*/
284void QQmlApplicationEngine::load(const QUrl &url)
285{
286 Q_D(QQmlApplicationEngine);
287 d->startLoad(url);
288}
289
290/*!
291 Loads the root QML file located at \a filePath. \a filePath must be a path to
292 a local file. If \a filePath is a relative path, it is taken as relative to
293 the application's working directory. The object tree defined by the file is
294 instantiated immediately.
295
296 If an error occurs, error messages are printed with qWarning.
297*/
298void QQmlApplicationEngine::load(const QString &filePath)
299{
300 Q_D(QQmlApplicationEngine);
301 d->startLoad(url: QUrl::fromUserInput(userInput: filePath, workingDirectory: QLatin1String("."), options: QUrl::AssumeLocalFile));
302}
303
304/*!
305 Sets the \a initialProperties with which the QML component gets initialized after
306 it gets loaded.
307
308 \code
309 QQmlApplicationEngine engine;
310
311 EventDatabase eventDatabase;
312 EventMonitor eventMonitor;
313
314 engine.setInitialProperties({
315 { "eventDatabase", QVariant::fromValue(&eventDatabase) },
316 { "eventMonitor", QVariant::fromValue(&eventMonitor) }
317 });
318 \endcode
319
320 \sa QQmlComponent::setInitialProperties
321 \sa QQmlApplicationEngine::load
322 \sa QQmlApplicationEngine::loadData
323 \since 5.14
324*/
325void QQmlApplicationEngine::setInitialProperties(const QVariantMap &initialProperties)
326{
327 Q_D(QQmlApplicationEngine);
328 d->initialProperties = initialProperties;
329}
330
331/*!
332 Loads the QML given in \a data. The object tree defined by \a data is
333 instantiated immediately.
334
335 If a \a url is specified it is used as the base url of the component. This affects
336 relative paths within the data and error messages.
337
338 If an error occurs, error messages are printed with qWarning.
339*/
340void QQmlApplicationEngine::loadData(const QByteArray &data, const QUrl &url)
341{
342 Q_D(QQmlApplicationEngine);
343 d->startLoad(url, data, dataFlag: true);
344}
345
346/*!
347 Returns a list of all the root objects instantiated by the
348 QQmlApplicationEngine. This will only contain objects loaded via load() or a
349 convenience constructor.
350
351 \note In Qt versions prior to 5.9, this function is marked as non-\c{const}.
352*/
353
354QList<QObject *> QQmlApplicationEngine::rootObjects() const
355{
356 Q_D(const QQmlApplicationEngine);
357 return d->objects;
358}
359
360#if QT_VERSION < QT_VERSION_CHECK(6, 0, 0)
361/*!
362 \overload
363 \internal
364*/
365QList<QObject *> QQmlApplicationEngine::rootObjects()
366{
367 return qAsConst(t&: *this).rootObjects();
368}
369#endif // < Qt 6
370
371QT_END_NAMESPACE
372
373#include "moc_qqmlapplicationengine.cpp"
374

source code of qtdeclarative/src/qml/qml/qqmlapplicationengine.cpp