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}
54
55QQmlApplicationEnginePrivate::~QQmlApplicationEnginePrivate()
56{
57}
58
59void QQmlApplicationEnginePrivate::cleanUp()
60{
61 Q_Q(QQmlApplicationEngine);
62 for (auto obj : qAsConst(objects))
63 obj->disconnect(q);
64
65 qDeleteAll(objects);
66}
67
68void QQmlApplicationEnginePrivate::init()
69{
70 Q_Q(QQmlApplicationEngine);
71 q->connect(q, &QQmlApplicationEngine::quit, QCoreApplication::instance(),
72 &QCoreApplication::quit, Qt::QueuedConnection);
73 q->connect(q, &QQmlApplicationEngine::exit, QCoreApplication::instance(),
74 &QCoreApplication::exit, Qt::QueuedConnection);
75#if QT_CONFIG(translation)
76 QTranslator* qtTranslator = new QTranslator(q);
77 if (qtTranslator->load(QLocale(), QLatin1String("qt"), QLatin1String("_"), QLibraryInfo::location(QLibraryInfo::TranslationsPath), QLatin1String(".qm")))
78 QCoreApplication::installTranslator(qtTranslator);
79 else
80 delete qtTranslator;
81#endif
82 new QQmlFileSelector(q,q);
83 QCoreApplication::instance()->setProperty("__qml_using_qqmlapplicationengine", QVariant(true));
84}
85
86void QQmlApplicationEnginePrivate::loadTranslations(const QUrl &rootFile)
87{
88#if QT_CONFIG(translation)
89 if (rootFile.scheme() != QLatin1String("file") && rootFile.scheme() != QLatin1String("qrc"))
90 return;
91
92 QFileInfo fi(QQmlFile::urlToLocalFileOrQrc(rootFile));
93
94 Q_Q(QQmlApplicationEngine);
95 QTranslator *translator = new QTranslator(q);
96 if (translator->load(QLocale(), QLatin1String("qml"), QLatin1String("_"), fi.path() + QLatin1String("/i18n"), QLatin1String(".qm")))
97 QCoreApplication::installTranslator(translator);
98 else
99 delete translator;
100#else
101 Q_UNUSED(rootFile)
102#endif
103}
104
105void QQmlApplicationEnginePrivate::startLoad(const QUrl &url, const QByteArray &data, bool dataFlag)
106{
107 Q_Q(QQmlApplicationEngine);
108
109 loadTranslations(url); //Translations must be loaded before the QML file is
110 QQmlComponent *c = new QQmlComponent(q, q);
111
112 if (dataFlag)
113 c->setData(data,url);
114 else
115 c->loadUrl(url);
116
117 if (!c->isLoading()) {
118 finishLoad(c);
119 return;
120 }
121 QObject::connect(c, &QQmlComponent::statusChanged, q, [this, c] { this->finishLoad(c); });
122}
123
124void QQmlApplicationEnginePrivate::finishLoad(QQmlComponent *c)
125{
126 Q_Q(QQmlApplicationEngine);
127 switch (c->status()) {
128 case QQmlComponent::Error:
129 qWarning() << "QQmlApplicationEngine failed to load component";
130 qWarning() << qPrintable(c->errorString());
131 q->objectCreated(nullptr, c->url());
132 break;
133 case QQmlComponent::Ready: {
134 auto newObj = c->create();
135 objects << newObj;
136 QObject::connect(newObj, &QObject::destroyed, q, [&](QObject *obj) { objects.removeAll(obj); });
137 q->objectCreated(objects.constLast(), c->url());
138 }
139 break;
140 case QQmlComponent::Loading:
141 case QQmlComponent::Null:
142 return; //These cases just wait for the next status update
143 }
144
145 c->deleteLater();
146}
147
148/*!
149 \class QQmlApplicationEngine
150 \since 5.1
151 \inmodule QtQml
152 \brief QQmlApplicationEngine provides a convenient way to load an application from a single QML file.
153
154 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++.
155
156 It can be used like so:
157
158 \code
159 #include <QGuiApplication>
160 #include <QQmlApplicationEngine>
161
162 int main(int argc, char *argv[])
163 {
164 QGuiApplication app(argc, argv);
165 QQmlApplicationEngine engine("main.qml");
166 return app.exec();
167 }
168 \endcode
169
170 Unlike QQuickView, QQmlApplicationEngine does not automatically create a root
171 window. If you are using visual items from Qt Quick, you will need to place
172 them inside of a \l [QML] {Window}.
173
174 You can also use QCoreApplication with QQmlApplicationEngine, if you are not using any QML modules which require a QGuiApplication (such as \c QtQuick).
175
176 List of configuration changes from a default QQmlEngine:
177
178 \list
179 \li Connecting Qt.quit() to QCoreApplication::quit()
180 \li Automatically loads translation files from an i18n directory adjacent to the main QML file.
181 \list
182 \li Translation files must have "qml_" prefix e.g. qml_ja_JP.qm.
183 \endlist
184 \li Automatically sets an incubation controller if the scene contains a QQuickWindow.
185 \li Automatically sets a \c QQmlFileSelector as the url interceptor, applying file selectors to all
186 QML files and assets.
187 \endlist
188
189 The engine behavior can be further tweaked by using the inherited methods from QQmlEngine.
190
191*/
192
193/*!
194 \fn QQmlApplicationEngine::objectCreated(QObject *object, const QUrl &url)
195
196 This signal is emitted when an object finishes loading. If loading was
197 successful, \a object contains a pointer to the loaded object, otherwise
198 the pointer is NULL.
199
200 The \a url to the component the \a object came from is also provided.
201
202 \note If the path to the component was provided as a QString containing a
203 relative path, the \a url will contain a fully resolved path to the file.
204*/
205
206/*!
207 Create a new QQmlApplicationEngine with the given \a parent. You will have to call load() later in
208 order to load a QML file.
209*/
210QQmlApplicationEngine::QQmlApplicationEngine(QObject *parent)
211: QQmlEngine(*(new QQmlApplicationEnginePrivate(this)), parent)
212{
213 Q_D(QQmlApplicationEngine);
214 d->init();
215 QJSEnginePrivate::addToDebugServer(this);
216}
217
218/*!
219 Create a new QQmlApplicationEngine and loads the QML file at the given \a url.
220 This is provided as a convenience, and is the same as using the empty constructor and calling load afterwards.
221*/
222QQmlApplicationEngine::QQmlApplicationEngine(const QUrl &url, QObject *parent)
223 : QQmlApplicationEngine(parent)
224{
225 load(url);
226}
227
228/*!
229 Create a new QQmlApplicationEngine and loads the QML file at the given
230 \a filePath, which must be a local file path. If a relative path is
231 given then it will be interpreted as relative to the working directory of the
232 application.
233
234 This is provided as a convenience, and is the same as using the empty constructor and calling load afterwards.
235*/
236QQmlApplicationEngine::QQmlApplicationEngine(const QString &filePath, QObject *parent)
237 : QQmlApplicationEngine(QUrl::fromUserInput(filePath, QLatin1String("."), QUrl::AssumeLocalFile), parent)
238{
239}
240
241/*!
242 Destroys the QQmlApplicationEngine and all QML objects it loaded.
243*/
244QQmlApplicationEngine::~QQmlApplicationEngine()
245{
246 Q_D(QQmlApplicationEngine);
247 QJSEnginePrivate::removeFromDebugServer(this);
248 d->cleanUp();//Instantiated root objects must be deleted before the engine
249}
250
251/*!
252 Loads the root QML file located at \a url. The object tree defined by the file
253 is created immediately for local file urls. Remote urls are loaded asynchronously,
254 listen to the \l {QQmlApplicationEngine::objectCreated()}{objectCreated} signal to
255 determine when the object tree is ready.
256
257 If an error occurs, the \l {QQmlApplicationEngine::objectCreated()}{objectCreated}
258 signal is emitted with a null pointer as parameter and error messages are printed
259 with qWarning.
260*/
261void QQmlApplicationEngine::load(const QUrl &url)
262{
263 Q_D(QQmlApplicationEngine);
264 d->startLoad(url);
265}
266
267/*!
268 Loads the root QML file located at \a filePath. \a filePath must be a path to
269 a local file. If \a filePath is a relative path, it is taken as relative to
270 the application's working directory. The object tree defined by the file is
271 instantiated immediately.
272
273 If an error occurs, error messages are printed with qWarning.
274*/
275void QQmlApplicationEngine::load(const QString &filePath)
276{
277 Q_D(QQmlApplicationEngine);
278 d->startLoad(QUrl::fromUserInput(filePath, QLatin1String("."), QUrl::AssumeLocalFile));
279}
280
281/*!
282 Loads the QML given in \a data. The object tree defined by \a data is
283 instantiated immediately.
284
285 If a \a url is specified it is used as the base url of the component. This affects
286 relative paths within the data and error messages.
287
288 If an error occurs, error messages are printed with qWarning.
289*/
290void QQmlApplicationEngine::loadData(const QByteArray &data, const QUrl &url)
291{
292 Q_D(QQmlApplicationEngine);
293 d->startLoad(url, data, true);
294}
295
296/*!
297 Returns a list of all the root objects instantiated by the
298 QQmlApplicationEngine. This will only contain objects loaded via load() or a
299 convenience constructor.
300
301 \note In Qt versions prior to 5.9, this function is marked as non-\c{const}.
302*/
303
304QList<QObject *> QQmlApplicationEngine::rootObjects() const
305{
306 Q_D(const QQmlApplicationEngine);
307 return d->objects;
308}
309
310#if QT_VERSION < QT_VERSION_CHECK(6, 0, 0)
311/*!
312 \overload
313 \internal
314*/
315QList<QObject *> QQmlApplicationEngine::rootObjects()
316{
317 return qAsConst(*this).rootObjects();
318}
319#endif // < Qt 6
320
321QT_END_NAMESPACE
322
323#include "moc_qqmlapplicationengine.cpp"
324