1// Copyright (C) 2016 BlackBerry Limited. All rights reserved.
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 <QtCore/QFileSelector>
5#include <QtQml/QQmlAbstractUrlInterceptor>
6#include <QtQml/private/qqmlengine_p.h>
7#include <QtQml/private/qqmlapplicationengine_p.h>
8#include <qobjectdefs.h>
9#include "qqmlfileselector.h"
10#include "qqmlfileselector_p.h"
11#include "qqmlengine_p.h"
12#include <QDebug>
13
14QT_BEGIN_NAMESPACE
15
16/*!
17 \class QQmlFileSelector
18 \since 5.2
19 \inmodule QtQml
20 \brief A class for applying a QFileSelector to QML file loading.
21
22 QQmlFileSelector will automatically apply a QFileSelector to
23 qml file and asset paths.
24
25 It is used as follows:
26
27 \code
28 QQmlEngine engine;
29 QQmlFileSelector* selector = new QQmlFileSelector(&engine);
30 \endcode
31
32 Then you can swap out files like so:
33 \code
34 main.qml
35 Component.qml
36 asset.png
37 +unix/Component.qml
38 +mac/asset.png
39 \endcode
40
41 In this example, main.qml will normally use Component.qml for the Component type. However on a
42 unix platform, the unix selector will be present and the +unix/Component.qml version will be
43 used instead. Note that this acts like swapping out Component.qml with +unix/Component.qml, so
44 when using Component.qml you should not need to alter any paths based on which version was
45 selected.
46
47 For example, to pass the "asset.png" file path around you would refer to it just as "asset.png" in
48 all of main.qml, Component.qml, and +linux/Component.qml. It will be replaced with +mac/asset.png
49 on Mac platforms in all cases.
50
51 For a list of available selectors, see \c QFileSelector.
52
53 Your platform may also provide additional selectors for you to use. As specified by QFileSelector,
54 directories used for selection must start with a '+' character, so you will not accidentally
55 trigger this feature unless you have directories with such names inside your project.
56
57 If a new QQmlFileSelector is set on the engine, the old one will be replaced.
58 */
59
60/*!
61 Creates a new QQmlFileSelector with parent object \a parent, which includes
62 its own QFileSelector. \a engine is the QQmlEngine you wish to apply file
63 selectors to. It will also take ownership of the QQmlFileSelector.
64*/
65
66QQmlFileSelector::QQmlFileSelector(QQmlEngine* engine, QObject* parent)
67 : QObject(*(new QQmlFileSelectorPrivate), parent)
68{
69 Q_D(QQmlFileSelector);
70 d->engine = engine;
71 d->engine->addUrlInterceptor(urlInterceptor: d->myInstance.data());
72}
73
74/*!
75 Destroys the QQmlFileSelector object.
76*/
77QQmlFileSelector::~QQmlFileSelector()
78{
79 Q_D(QQmlFileSelector);
80 if (d->engine) {
81 d->engine->removeUrlInterceptor(urlInterceptor: d->myInstance.data());
82 d->engine = nullptr;
83 }
84}
85
86/*!
87 \since 5.7
88 Returns the QFileSelector instance used by the QQmlFileSelector.
89*/
90QFileSelector *QQmlFileSelector::selector() const noexcept
91{
92 Q_D(const QQmlFileSelector);
93 return d->selector;
94}
95
96QQmlFileSelectorPrivate::QQmlFileSelectorPrivate()
97{
98 Q_Q(QQmlFileSelector);
99 ownSelector = true;
100 selector = new QFileSelector(q);
101 myInstance.reset(other: new QQmlFileSelectorInterceptor(this));
102}
103
104QQmlFileSelectorPrivate::~QQmlFileSelectorPrivate()
105{
106 if (ownSelector)
107 delete selector;
108}
109
110/*!
111 Sets the QFileSelector instance for use by the QQmlFileSelector to \a selector.
112 QQmlFileSelector does not take ownership of the new QFileSelector. To reset QQmlFileSelector
113 to use its internal QFileSelector instance, call setSelector(\nullptr).
114*/
115
116void QQmlFileSelector::setSelector(QFileSelector *selector)
117{
118 Q_D(QQmlFileSelector);
119 if (selector) {
120 if (d->ownSelector) {
121 delete d->selector;
122 d->ownSelector = false;
123 }
124 d->selector = selector;
125 } else {
126 if (!d->ownSelector) {
127 d->ownSelector = true;
128 d->selector = new QFileSelector(this);
129 } // Do nothing if already using internal selector
130 }
131}
132
133/*!
134 Adds extra selectors contained in \a strings to the current QFileSelector being used.
135 Use this when extra selectors are all you need to avoid having to create your own
136 QFileSelector instance.
137*/
138void QQmlFileSelector::setExtraSelectors(const QStringList &strings)
139{
140 Q_D(QQmlFileSelector);
141 d->selector->setExtraSelectors(strings);
142}
143
144#if QT_DEPRECATED_SINCE(6, 0)
145/*!
146 \deprecated [6.0] The file selector should not be accessed after it
147 is set. It may be in use. See below for further details.
148
149 Gets the QQmlFileSelector currently active on the target \a engine.
150
151 This method is deprecated. You should not retrieve the files selector from an
152 engine after setting it. It may be in use.
153
154 If the \a engine passed here is a QQmlApplicationEngine that hasn't loaded any
155 QML files, yet, it will be initialized. Any later calls to
156 QQmlApplicationEngine::setExtraFileSelectors() will have no effect.
157
158 \sa QQmlApplicationEngine
159*/
160QQmlFileSelector* QQmlFileSelector::get(QQmlEngine* engine)
161{
162 QQmlEnginePrivate *enginePrivate = QQmlEnginePrivate::get(e: engine);
163
164 if (qobject_cast<QQmlApplicationEngine *>(object: engine)) {
165 auto *appEnginePrivate = static_cast<QQmlApplicationEnginePrivate *>(enginePrivate);
166 appEnginePrivate->ensureInitialized();
167 }
168
169 const QUrl nonEmptyInvalid(QLatin1String(":"));
170 for (QQmlAbstractUrlInterceptor *interceptor : enginePrivate->urlInterceptors) {
171 const QUrl result = interceptor->intercept(
172 path: nonEmptyInvalid, type: QQmlAbstractUrlInterceptor::UrlString);
173 if (result.scheme() == QLatin1String("type")
174 && result.path() == QLatin1String("fileselector")) {
175 return static_cast<QQmlFileSelectorInterceptor *>(interceptor)->d->q_func();
176 }
177 }
178 return nullptr;
179}
180#endif
181
182/*!
183 \internal
184*/
185QQmlFileSelectorInterceptor::QQmlFileSelectorInterceptor(QQmlFileSelectorPrivate* pd)
186 : d(pd)
187{
188}
189
190/*!
191 \internal
192*/
193QUrl QQmlFileSelectorInterceptor::intercept(const QUrl &path, DataType type)
194{
195 if (!path.isEmpty() && !path.isValid())
196 return QUrl(QLatin1String("type:fileselector"));
197
198 return type == QQmlAbstractUrlInterceptor::QmldirFile
199 ? path // Don't intercept qmldir files, to prevent double interception
200 : d->selector->select(filePath: path);
201}
202
203QT_END_NAMESPACE
204
205#include "moc_qqmlfileselector.cpp"
206

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