1/****************************************************************************
2**
3** Copyright (C) 2016 The Qt Company Ltd.
4** Contact: https://www.qt.io/licensing/
5**
6** This file is part of the Qt Quick Dialogs 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 "qquickabstractfiledialog_p.h"
41#include "qquickitem.h"
42
43#include <private/qguiapplication_p.h>
44#include <QQmlEngine>
45#include <QQuickWindow>
46#include <QRegularExpression>
47#include <QWindow>
48
49QT_BEGIN_NAMESPACE
50
51QQuickAbstractFileDialog::QQuickAbstractFileDialog(QObject *parent)
52 : QQuickAbstractDialog(parent)
53 , m_dlgHelper(0)
54 , m_options(QFileDialogOptions::create())
55 , m_selectExisting(true)
56 , m_selectMultiple(false)
57 , m_selectFolder(false)
58 , m_sidebarVisible(true)
59{
60 updateModes();
61 connect(sender: this, SIGNAL(accepted()), receiver: this, SIGNAL(selectionAccepted()));
62}
63
64QQuickAbstractFileDialog::~QQuickAbstractFileDialog()
65{
66}
67
68void QQuickAbstractFileDialog::setVisible(bool v)
69{
70 if (helper() && v) {
71 m_dlgHelper->setOptions(m_options);
72 m_dlgHelper->setFilter();
73 emit filterSelected();
74 }
75 QQuickAbstractDialog::setVisible(v);
76}
77
78QString QQuickAbstractFileDialog::title() const
79{
80 return m_options->windowTitle();
81}
82
83void QQuickAbstractFileDialog::setTitle(const QString &t)
84{
85 if (m_options->windowTitle() == t) return;
86 m_options->setWindowTitle(t);
87 emit titleChanged();
88}
89
90void QQuickAbstractFileDialog::setSelectExisting(bool selectExisting)
91{
92 if (selectExisting == m_selectExisting) return;
93 m_selectExisting = selectExisting;
94 updateModes();
95}
96
97void QQuickAbstractFileDialog::setSelectMultiple(bool selectMultiple)
98{
99 if (selectMultiple == m_selectMultiple) return;
100 m_selectMultiple = selectMultiple;
101 updateModes();
102}
103
104void QQuickAbstractFileDialog::setSelectFolder(bool selectFolder)
105{
106 if (selectFolder == m_selectFolder) return;
107 m_selectFolder = selectFolder;
108 updateModes();
109}
110
111QUrl QQuickAbstractFileDialog::folder() const
112{
113 if (m_dlgHelper && !m_dlgHelper->directory().isEmpty())
114 return m_dlgHelper->directory();
115 return m_options->initialDirectory();
116}
117
118static QUrl fixupFolder(const QUrl &f)
119{
120 QString lf = f.toLocalFile();
121#ifndef Q_OS_WIN // Don't mangle Windows network paths
122 while (lf.startsWith(s: "//"))
123 lf.remove(i: 0, len: 1);
124#endif
125 if (lf.isEmpty())
126 lf = QDir::currentPath();
127 return QUrl::fromLocalFile(localfile: lf);
128}
129
130void QQuickAbstractFileDialog::setFolder(const QUrl &f)
131{
132 QUrl u = fixupFolder(f);
133 if (m_dlgHelper)
134 m_dlgHelper->setDirectory(u);
135 m_options->setInitialDirectory(u);
136 emit folderChanged();
137}
138
139void QQuickAbstractFileDialog::updateFolder(const QUrl &f)
140{
141 QUrl u = fixupFolder(f);
142 m_options->setInitialDirectory(u);
143 emit folderChanged();
144}
145
146void QQuickAbstractFileDialog::setNameFilters(const QStringList &f)
147{
148 m_options->setNameFilters(f);
149 if (f.isEmpty())
150 selectNameFilter(f: QString());
151 else if (!f.contains(str: selectedNameFilter(), cs: Qt::CaseInsensitive))
152 selectNameFilter(f: f.first());
153 emit nameFiltersChanged();
154}
155
156QString QQuickAbstractFileDialog::selectedNameFilter() const
157{
158 QString ret;
159 if (m_dlgHelper)
160 ret = m_dlgHelper->selectedNameFilter();
161 if (ret.isEmpty())
162 return m_options->initiallySelectedNameFilter();
163 return ret;
164}
165
166void QQuickAbstractFileDialog::selectNameFilter(const QString &f)
167{
168 // This should work whether the dialog is currently being shown already, or ahead of time.
169 m_options->setInitiallySelectedNameFilter(f);
170 if (m_dlgHelper)
171 m_dlgHelper->selectNameFilter(filter: f);
172 emit filterSelected();
173}
174
175void QQuickAbstractFileDialog::setSelectedNameFilterIndex(int idx)
176{
177 selectNameFilter(f: nameFilters().at(i: idx));
178}
179
180void QQuickAbstractFileDialog::setSidebarVisible(bool s)
181{
182 if (s == m_sidebarVisible) return;
183 m_sidebarVisible = s;
184 emit sidebarVisibleChanged();
185}
186
187QStringList QQuickAbstractFileDialog::selectedNameFilterExtensions() const
188{
189 QString filterRaw = selectedNameFilter();
190 QStringList ret;
191#if QT_CONFIG(regularexpression)
192 if (filterRaw.isEmpty()) {
193 ret << "*";
194 return ret;
195 }
196 QRegularExpression re("(\\*\\.?\\w*)");
197 QRegularExpressionMatchIterator i = re.globalMatch(subject: filterRaw);
198 while (i.hasNext())
199 ret << i.next().captured(nth: 1);
200#endif // QT_CONFIG(regularexpression)
201 if (ret.isEmpty())
202 ret << filterRaw;
203 return ret;
204}
205
206int QQuickAbstractFileDialog::selectedNameFilterIndex() const
207{
208 return nameFilters().indexOf(t: selectedNameFilter());
209}
210
211QUrl QQuickAbstractFileDialog::fileUrl() const
212{
213 QList<QUrl> urls = fileUrls();
214 return (urls.count() == 1) ? urls[0] : QUrl();
215}
216
217void QQuickAbstractFileDialog::updateModes()
218{
219 // The 4 possible modes are AnyFile, ExistingFile, Directory, ExistingFiles
220 // Assume AnyFile until we find a reason to the contrary
221 QFileDialogOptions::FileMode mode = QFileDialogOptions::AnyFile;
222
223 if (m_selectFolder) {
224 mode = QFileDialogOptions::Directory;
225 m_options->setOption(option: QFileDialogOptions::ShowDirsOnly);
226 m_selectMultiple = false;
227 m_selectExisting = true;
228 setNameFilters(QStringList());
229 } else if (m_selectExisting) {
230 mode = m_selectMultiple ?
231 QFileDialogOptions::ExistingFiles : QFileDialogOptions::ExistingFile;
232 m_options->setOption(option: QFileDialogOptions::ShowDirsOnly, on: false);
233 } else if (m_selectMultiple) {
234 m_selectExisting = true;
235 }
236 if (!m_selectExisting)
237 m_selectMultiple = false;
238 m_options->setFileMode(mode);
239 m_options->setAcceptMode(m_selectExisting ?
240 QFileDialogOptions::AcceptOpen : QFileDialogOptions::AcceptSave);
241 emit fileModeChanged();
242}
243
244void QQuickAbstractFileDialog::addShortcut(const QString &name, const QString &visibleName, const QString &path)
245{
246 QQmlEngine *engine = qmlEngine(this);
247 QUrl url = QUrl::fromLocalFile(localfile: path);
248
249 // Since the app can have bindings to the shortcut, we always add it
250 // to the public API, even if the directory doesn't (yet) exist.
251 m_shortcuts.setProperty(name, value: url.toString());
252
253 // ...but we are more strict about showing it as a clickable link inside the dialog
254 if (visibleName.isEmpty() || !QDir(path).exists())
255 return;
256
257 QJSValue o = engine->newObject();
258 o.setProperty(name: "name", value: visibleName);
259 // TODO maybe some day QJSValue could directly store a QUrl
260 o.setProperty(name: "url", value: url.toString());
261
262 int length = m_shortcutDetails.property(name: QLatin1String("length")).toInt();
263 m_shortcutDetails.setProperty(arrayIndex: length, value: o);
264}
265
266void QQuickAbstractFileDialog::addShortcutFromStandardLocation(const QString &name, QStandardPaths::StandardLocation loc, bool local)
267{
268 if (m_selectExisting) {
269 QStringList readPaths = QStandardPaths::standardLocations(type: loc);
270 QString path = readPaths.isEmpty() ? QString() : local ? readPaths.first() : readPaths.last();
271 addShortcut(name, visibleName: QStandardPaths::displayName(type: loc), path);
272 } else {
273 QString path = QStandardPaths::writableLocation(type: loc);
274 addShortcut(name, visibleName: QStandardPaths::displayName(type: loc), path);
275 }
276}
277
278void QQuickAbstractFileDialog::populateShortcuts()
279{
280 QJSEngine *engine = qmlEngine(this);
281 m_shortcutDetails = engine->newArray();
282 m_shortcuts = engine->newObject();
283
284 addShortcutFromStandardLocation(name: QLatin1String("desktop"), loc: QStandardPaths::DesktopLocation);
285 addShortcutFromStandardLocation(name: QLatin1String("documents"), loc: QStandardPaths::DocumentsLocation);
286 addShortcutFromStandardLocation(name: QLatin1String("music"), loc: QStandardPaths::MusicLocation);
287 addShortcutFromStandardLocation(name: QLatin1String("movies"), loc: QStandardPaths::MoviesLocation);
288 addShortcutFromStandardLocation(name: QLatin1String("home"), loc: QStandardPaths::HomeLocation);
289
290#ifndef Q_OS_IOS
291 addShortcutFromStandardLocation(name: QLatin1String("pictures"), loc: QStandardPaths::PicturesLocation);
292#else
293 // On iOS we point pictures to the system picture folder when loading
294 addShortcutFromStandardLocation(QLatin1String("pictures"), QStandardPaths::PicturesLocation, !m_selectExisting);
295#endif
296
297#ifndef Q_OS_IOS
298 // on iOS, this returns only "/", which is never a useful path to read or write anything
299 const QFileInfoList drives = QDir::drives();
300 for (const QFileInfo &fi : drives)
301 addShortcut(name: fi.absoluteFilePath(), visibleName: fi.absoluteFilePath(), path: fi.absoluteFilePath());
302#endif
303}
304
305QJSValue QQuickAbstractFileDialog::shortcuts()
306{
307 if (m_shortcuts.isUndefined())
308 populateShortcuts();
309
310 return m_shortcuts;
311}
312
313QJSValue QQuickAbstractFileDialog::__shortcuts()
314{
315 if (m_shortcutDetails.isUndefined())
316 populateShortcuts();
317
318 return m_shortcutDetails;
319}
320
321void QQuickAbstractFileDialog::setDefaultSuffix(const QString &suffix)
322{
323 if (suffix == m_options->defaultSuffix())
324 return;
325 m_options->setDefaultSuffix(suffix);
326 emit defaultSuffixChanged();
327}
328
329QT_END_NAMESPACE
330

source code of qtquickcontrols/src/dialogs/qquickabstractfiledialog.cpp