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 QtWidgets 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#define QT_NO_URL_CAST_FROM_STRING
41
42#include <qvariant.h>
43#include <private/qwidgetitemdata_p.h>
44#include "qfiledialog.h"
45
46#include "qfiledialog_p.h"
47#include <private/qapplication_p.h>
48#include <private/qguiapplication_p.h>
49#include <qfontmetrics.h>
50#include <qaction.h>
51#include <qheaderview.h>
52#include <qshortcut.h>
53#include <qgridlayout.h>
54#if QT_CONFIG(menu)
55#include <qmenu.h>
56#endif
57#if QT_CONFIG(messagebox)
58#include <qmessagebox.h>
59#endif
60#include <stdlib.h>
61#if QT_CONFIG(settings)
62#include <qsettings.h>
63#endif
64#include <qdebug.h>
65#if QT_CONFIG(mimetype)
66#include <qmimedatabase.h>
67#endif
68#include <qapplication.h>
69#include <qstylepainter.h>
70#include "ui_qfiledialog.h"
71#if defined(Q_OS_UNIX)
72#include <pwd.h>
73#include <unistd.h> // for pathconf() on OS X
74#elif defined(Q_OS_WIN)
75# include <QtCore/qt_windows.h>
76#endif
77#if defined(Q_OS_WASM)
78#include <private/qwasmlocalfileaccess_p.h>
79#endif
80
81#include <algorithm>
82
83QT_BEGIN_NAMESPACE
84
85Q_GLOBAL_STATIC(QUrl, lastVisitedDir)
86
87/*!
88 \class QFileDialog
89 \brief The QFileDialog class provides a dialog that allow users to select files or directories.
90 \ingroup standard-dialogs
91 \inmodule QtWidgets
92
93 The QFileDialog class enables a user to traverse the file system in
94 order to select one or many files or a directory.
95
96 The easiest way to create a QFileDialog is to use the static functions.
97
98 \snippet code/src_gui_dialogs_qfiledialog.cpp 0
99
100 In the above example, a modal QFileDialog is created using a static
101 function. The dialog initially displays the contents of the "/home/jana"
102 directory, and displays files matching the patterns given in the
103 string "Image Files (*.png *.jpg *.bmp)". The parent of the file dialog
104 is set to \e this, and the window title is set to "Open Image".
105
106 If you want to use multiple filters, separate each one with
107 \e two semicolons. For example:
108
109 \snippet code/src_gui_dialogs_qfiledialog.cpp 1
110
111 You can create your own QFileDialog without using the static
112 functions. By calling setFileMode(), you can specify what the user must
113 select in the dialog:
114
115 \snippet code/src_gui_dialogs_qfiledialog.cpp 2
116
117 In the above example, the mode of the file dialog is set to
118 AnyFile, meaning that the user can select any file, or even specify a
119 file that doesn't exist. This mode is useful for creating a
120 "Save As" file dialog. Use ExistingFile if the user must select an
121 existing file, or \l Directory if only a directory may be selected.
122 See the \l QFileDialog::FileMode enum for the complete list of modes.
123
124 The fileMode property contains the mode of operation for the dialog;
125 this indicates what types of objects the user is expected to select.
126 Use setNameFilter() to set the dialog's file filter. For example:
127
128 \snippet code/src_gui_dialogs_qfiledialog.cpp 3
129
130 In the above example, the filter is set to \c{"Images (*.png *.xpm *.jpg)"},
131 this means that only files with the extension \c png, \c xpm,
132 or \c jpg will be shown in the QFileDialog. You can apply
133 several filters by using setNameFilters(). Use selectNameFilter() to select
134 one of the filters you've given as the file dialog's default filter.
135
136 The file dialog has two view modes: \l{QFileDialog::}{List} and
137 \l{QFileDialog::}{Detail}.
138 \l{QFileDialog::}{List} presents the contents of the current directory
139 as a list of file and directory names. \l{QFileDialog::}{Detail} also
140 displays a list of file and directory names, but provides additional
141 information alongside each name, such as the file size and modification
142 date. Set the mode with setViewMode():
143
144 \snippet code/src_gui_dialogs_qfiledialog.cpp 4
145
146 The last important function you will need to use when creating your
147 own file dialog is selectedFiles().
148
149 \snippet code/src_gui_dialogs_qfiledialog.cpp 5
150
151 In the above example, a modal file dialog is created and shown. If
152 the user clicked OK, the file they selected is put in \c fileName.
153
154 The dialog's working directory can be set with setDirectory().
155 Each file in the current directory can be selected using
156 the selectFile() function.
157
158 The \l{dialogs/standarddialogs}{Standard Dialogs} example shows
159 how to use QFileDialog as well as other built-in Qt dialogs.
160
161 By default, a platform-native file dialog will be used if the platform has
162 one. In that case, the widgets which would otherwise be used to construct the
163 dialog will not be instantiated, so related accessors such as layout() and
164 itemDelegate() will return null. You can set the \l DontUseNativeDialog option to
165 ensure that the widget-based implementation will be used instead of the
166 native dialog.
167
168 \sa QDir, QFileInfo, QFile, QColorDialog, QFontDialog, {Standard Dialogs Example},
169 {Application Example}
170*/
171
172/*!
173 \enum QFileDialog::AcceptMode
174
175 \value AcceptOpen
176 \value AcceptSave
177*/
178
179/*!
180 \enum QFileDialog::ViewMode
181
182 This enum describes the view mode of the file dialog; i.e. what
183 information about each file will be displayed.
184
185 \value Detail Displays an icon, a name, and details for each item in
186 the directory.
187 \value List Displays only an icon and a name for each item in the
188 directory.
189
190 \sa setViewMode()
191*/
192
193/*!
194 \enum QFileDialog::FileMode
195
196 This enum is used to indicate what the user may select in the file
197 dialog; i.e. what the dialog will return if the user clicks OK.
198
199 \value AnyFile The name of a file, whether it exists or not.
200 \value ExistingFile The name of a single existing file.
201 \value Directory The name of a directory. Both files and
202 directories are displayed. However, the native Windows
203 file dialog does not support displaying files in the
204 directory chooser.
205 \value ExistingFiles The names of zero or more existing files.
206
207 This value is obsolete since Qt 4.5:
208
209 \value DirectoryOnly Use \c Directory and setOption(ShowDirsOnly, true) instead.
210
211 \sa setFileMode()
212*/
213
214/*!
215 \enum QFileDialog::Option
216
217 \value ShowDirsOnly Only show directories in the file dialog. By
218 default both files and directories are shown. (Valid only in the
219 \l Directory file mode.)
220
221 \value DontResolveSymlinks Don't resolve symlinks in the file
222 dialog. By default symlinks are resolved.
223
224 \value DontConfirmOverwrite Don't ask for confirmation if an
225 existing file is selected. By default confirmation is requested.
226
227 \value DontUseNativeDialog Don't use the native file dialog. By
228 default, the native file dialog is used unless you use a subclass
229 of QFileDialog that contains the Q_OBJECT macro, or the platform
230 does not have a native dialog of the type that you require.
231
232 \note This option must be set before changing dialog properties
233 or showing the dialog.
234
235 \value ReadOnly Indicates that the model is readonly.
236
237 \value HideNameFilterDetails Indicates if the file name filter details are
238 hidden or not.
239
240 \value DontUseSheet In previous versions of Qt, the static
241 functions would create a sheet by default if the static function
242 was given a parent. This is no longer supported and does nothing in Qt 4.5, The
243 static functions will always be an application modal dialog. If
244 you want to use sheets, use QFileDialog::open() instead.
245
246 \value DontUseCustomDirectoryIcons Always use the default directory icon.
247 Some platforms allow the user to set a different icon. Custom icon lookup
248 cause a big performance impact over network or removable drives.
249 Setting this will enable the QFileIconProvider::DontUseCustomDirectoryIcons
250 option in the icon provider. This enum value was added in Qt 5.2.
251*/
252
253/*!
254 \enum QFileDialog::DialogLabel
255
256 \value LookIn
257 \value FileName
258 \value FileType
259 \value Accept
260 \value Reject
261*/
262
263/*!
264 \fn void QFileDialog::filesSelected(const QStringList &selected)
265
266 When the selection changes for local operations and the dialog is
267 accepted, this signal is emitted with the (possibly empty) list
268 of \a selected files.
269
270 \sa currentChanged(), QDialog::Accepted
271*/
272
273/*!
274 \fn void QFileDialog::urlsSelected(const QList<QUrl> &urls)
275
276 When the selection changes and the dialog is accepted, this signal is
277 emitted with the (possibly empty) list of selected \a urls.
278
279 \sa currentUrlChanged(), QDialog::Accepted
280 \since 5.2
281*/
282
283/*!
284 \fn void QFileDialog::fileSelected(const QString &file)
285
286 When the selection changes for local operations and the dialog is
287 accepted, this signal is emitted with the (possibly empty)
288 selected \a file.
289
290 \sa currentChanged(), QDialog::Accepted
291*/
292
293/*!
294 \fn void QFileDialog::urlSelected(const QUrl &url)
295
296 When the selection changes and the dialog is accepted, this signal is
297 emitted with the (possibly empty) selected \a url.
298
299 \sa currentUrlChanged(), QDialog::Accepted
300 \since 5.2
301*/
302
303/*!
304 \fn void QFileDialog::currentChanged(const QString &path)
305
306 When the current file changes for local operations, this signal is
307 emitted with the new file name as the \a path parameter.
308
309 \sa filesSelected()
310*/
311
312/*!
313 \fn void QFileDialog::currentUrlChanged(const QUrl &url)
314
315 When the current file changes, this signal is emitted with the
316 new file URL as the \a url parameter.
317
318 \sa urlsSelected()
319 \since 5.2
320*/
321
322/*!
323 \fn void QFileDialog::directoryEntered(const QString &directory)
324 \since 4.3
325
326 This signal is emitted for local operations when the user enters
327 a \a directory.
328*/
329
330/*!
331 \fn void QFileDialog::directoryUrlEntered(const QUrl &directory)
332
333 This signal is emitted when the user enters a \a directory.
334
335 \since 5.2
336*/
337
338/*!
339 \fn void QFileDialog::filterSelected(const QString &filter)
340 \since 4.3
341
342 This signal is emitted when the user selects a \a filter.
343*/
344
345QT_BEGIN_INCLUDE_NAMESPACE
346#include <QMetaEnum>
347#include <qshortcut.h>
348QT_END_INCLUDE_NAMESPACE
349
350/*!
351 \fn QFileDialog::QFileDialog(QWidget *parent, Qt::WindowFlags flags)
352
353 Constructs a file dialog with the given \a parent and widget \a flags.
354*/
355QFileDialog::QFileDialog(QWidget *parent, Qt::WindowFlags f)
356 : QDialog(*new QFileDialogPrivate, parent, f)
357{
358 Q_D(QFileDialog);
359 d->init();
360}
361
362/*!
363 Constructs a file dialog with the given \a parent and \a caption that
364 initially displays the contents of the specified \a directory.
365 The contents of the directory are filtered before being shown in the
366 dialog, using a semicolon-separated list of filters specified by
367 \a filter.
368*/
369QFileDialog::QFileDialog(QWidget *parent,
370 const QString &caption,
371 const QString &directory,
372 const QString &filter)
373 : QDialog(*new QFileDialogPrivate, parent, 0)
374{
375 Q_D(QFileDialog);
376 d->init(QUrl::fromLocalFile(directory), filter, caption);
377}
378
379/*!
380 \internal
381*/
382QFileDialog::QFileDialog(const QFileDialogArgs &args)
383 : QDialog(*new QFileDialogPrivate, args.parent, 0)
384{
385 Q_D(QFileDialog);
386 d->init(args.directory, args.filter, args.caption);
387 setFileMode(args.mode);
388 setOptions(args.options);
389 selectFile(args.selection);
390}
391
392/*!
393 Destroys the file dialog.
394*/
395QFileDialog::~QFileDialog()
396{
397#if QT_CONFIG(settings)
398 Q_D(QFileDialog);
399 d->saveSettings();
400#endif
401}
402
403/*!
404 \since 4.3
405 Sets the \a urls that are located in the sidebar.
406
407 For instance:
408
409 \snippet filedialogurls.cpp 0
410
411 The file dialog will then look like this:
412
413 \image filedialogurls.png
414
415 \sa sidebarUrls()
416*/
417void QFileDialog::setSidebarUrls(const QList<QUrl> &urls)
418{
419 Q_D(QFileDialog);
420 if (!d->nativeDialogInUse)
421 d->qFileDialogUi->sidebar->setUrls(urls);
422}
423
424/*!
425 \since 4.3
426 Returns a list of urls that are currently in the sidebar
427*/
428QList<QUrl> QFileDialog::sidebarUrls() const
429{
430 Q_D(const QFileDialog);
431 return (d->nativeDialogInUse ? QList<QUrl>() : d->qFileDialogUi->sidebar->urls());
432}
433
434static const qint32 QFileDialogMagic = 0xbe;
435
436/*!
437 \since 4.3
438 Saves the state of the dialog's layout, history and current directory.
439
440 Typically this is used in conjunction with QSettings to remember the size
441 for a future session. A version number is stored as part of the data.
442*/
443QByteArray QFileDialog::saveState() const
444{
445 Q_D(const QFileDialog);
446 int version = 4;
447 QByteArray data;
448 QDataStream stream(&data, QIODevice::WriteOnly);
449
450 stream << qint32(QFileDialogMagic);
451 stream << qint32(version);
452 if (d->usingWidgets()) {
453 stream << d->qFileDialogUi->splitter->saveState();
454 stream << d->qFileDialogUi->sidebar->urls();
455 } else {
456 stream << d->splitterState;
457 stream << d->sidebarUrls;
458 }
459 stream << history();
460 stream << *lastVisitedDir();
461 if (d->usingWidgets())
462 stream << d->qFileDialogUi->treeView->header()->saveState();
463 else
464 stream << d->headerData;
465 stream << qint32(viewMode());
466 return data;
467}
468
469/*!
470 \since 4.3
471 Restores the dialogs's layout, history and current directory to the \a state specified.
472
473 Typically this is used in conjunction with QSettings to restore the size
474 from a past session.
475
476 Returns \c false if there are errors
477*/
478bool QFileDialog::restoreState(const QByteArray &state)
479{
480 Q_D(QFileDialog);
481 QByteArray sd = state;
482 QDataStream stream(&sd, QIODevice::ReadOnly);
483 if (stream.atEnd())
484 return false;
485 QStringList history;
486 QUrl currentDirectory;
487 qint32 marker;
488 qint32 v;
489 qint32 viewMode;
490 stream >> marker;
491 stream >> v;
492 // the code below only supports versions 3 and 4
493 if (marker != QFileDialogMagic || (v != 3 && v != 4))
494 return false;
495
496 stream >> d->splitterState
497 >> d->sidebarUrls
498 >> history;
499 if (v == 3) {
500 QString currentDirectoryString;
501 stream >> currentDirectoryString;
502 currentDirectory = QUrl::fromLocalFile(currentDirectoryString);
503 } else {
504 stream >> currentDirectory;
505 }
506 stream >> d->headerData
507 >> viewMode;
508
509 setDirectoryUrl(lastVisitedDir()->isEmpty() ? currentDirectory : *lastVisitedDir());
510 setViewMode(static_cast<QFileDialog::ViewMode>(viewMode));
511
512 if (!d->usingWidgets())
513 return true;
514
515 return d->restoreWidgetState(history, -1);
516}
517
518/*!
519 \reimp
520*/
521void QFileDialog::changeEvent(QEvent *e)
522{
523 Q_D(QFileDialog);
524 if (e->type() == QEvent::LanguageChange) {
525 d->retranslateWindowTitle();
526 d->retranslateStrings();
527 }
528 QDialog::changeEvent(e);
529}
530
531QFileDialogPrivate::QFileDialogPrivate()
532 :
533#if QT_CONFIG(proxymodel)
534 proxyModel(0),
535#endif
536 model(0),
537 currentHistoryLocation(-1),
538 renameAction(0),
539 deleteAction(0),
540 showHiddenAction(0),
541 useDefaultCaption(true),
542 qFileDialogUi(0),
543 options(QFileDialogOptions::create())
544{
545}
546
547QFileDialogPrivate::~QFileDialogPrivate()
548{
549}
550
551void QFileDialogPrivate::initHelper(QPlatformDialogHelper *h)
552{
553 QFileDialog *d = q_func();
554 QObject::connect(h, SIGNAL(fileSelected(QUrl)), d, SLOT(_q_emitUrlSelected(QUrl)));
555 QObject::connect(h, SIGNAL(filesSelected(QList<QUrl>)), d, SLOT(_q_emitUrlsSelected(QList<QUrl>)));
556 QObject::connect(h, SIGNAL(currentChanged(QUrl)), d, SLOT(_q_nativeCurrentChanged(QUrl)));
557 QObject::connect(h, SIGNAL(directoryEntered(QUrl)), d, SLOT(_q_nativeEnterDirectory(QUrl)));
558 QObject::connect(h, SIGNAL(filterSelected(QString)), d, SIGNAL(filterSelected(QString)));
559 static_cast<QPlatformFileDialogHelper *>(h)->setOptions(options);
560 nativeDialogInUse = true;
561}
562
563void QFileDialogPrivate::helperPrepareShow(QPlatformDialogHelper *)
564{
565 Q_Q(QFileDialog);
566 options->setWindowTitle(q->windowTitle());
567 options->setHistory(q->history());
568 if (usingWidgets())
569 options->setSidebarUrls(qFileDialogUi->sidebar->urls());
570 if (options->initiallySelectedNameFilter().isEmpty())
571 options->setInitiallySelectedNameFilter(q->selectedNameFilter());
572 if (options->initiallySelectedFiles().isEmpty())
573 options->setInitiallySelectedFiles(userSelectedFiles());
574}
575
576void QFileDialogPrivate::helperDone(QDialog::DialogCode code, QPlatformDialogHelper *)
577{
578 if (code == QDialog::Accepted) {
579 Q_Q(QFileDialog);
580 q->setViewMode(static_cast<QFileDialog::ViewMode>(options->viewMode()));
581 q->setSidebarUrls(options->sidebarUrls());
582 q->setHistory(options->history());
583 }
584}
585
586void QFileDialogPrivate::retranslateWindowTitle()
587{
588 Q_Q(QFileDialog);
589 if (!useDefaultCaption || setWindowTitle != q->windowTitle())
590 return;
591 if (q->acceptMode() == QFileDialog::AcceptOpen) {
592 const QFileDialog::FileMode fileMode = q->fileMode();
593QT_WARNING_PUSH
594QT_WARNING_DISABLE_DEPRECATED
595 if (fileMode == QFileDialog::DirectoryOnly || fileMode == QFileDialog::Directory)
596 q->setWindowTitle(QFileDialog::tr("Find Directory"));
597 else
598 q->setWindowTitle(QFileDialog::tr("Open"));
599QT_WARNING_POP
600 } else
601 q->setWindowTitle(QFileDialog::tr("Save As"));
602
603 setWindowTitle = q->windowTitle();
604}
605
606void QFileDialogPrivate::setLastVisitedDirectory(const QUrl &dir)
607{
608 *lastVisitedDir() = dir;
609}
610
611void QFileDialogPrivate::updateLookInLabel()
612{
613 if (options->isLabelExplicitlySet(QFileDialogOptions::LookIn))
614 setLabelTextControl(QFileDialog::LookIn, options->labelText(QFileDialogOptions::LookIn));
615}
616
617void QFileDialogPrivate::updateFileNameLabel()
618{
619 if (options->isLabelExplicitlySet(QFileDialogOptions::FileName)) {
620 setLabelTextControl(QFileDialog::FileName, options->labelText(QFileDialogOptions::FileName));
621 } else {
622 switch (q_func()->fileMode()) {
623QT_WARNING_PUSH
624QT_WARNING_DISABLE_DEPRECATED
625 case QFileDialog::DirectoryOnly:
626QT_WARNING_POP
627 case QFileDialog::Directory:
628 setLabelTextControl(QFileDialog::FileName, QFileDialog::tr("Directory:"));
629 break;
630 default:
631 setLabelTextControl(QFileDialog::FileName, QFileDialog::tr("File &name:"));
632 break;
633 }
634 }
635}
636
637void QFileDialogPrivate::updateFileTypeLabel()
638{
639 if (options->isLabelExplicitlySet(QFileDialogOptions::FileType))
640 setLabelTextControl(QFileDialog::FileType, options->labelText(QFileDialogOptions::FileType));
641}
642
643void QFileDialogPrivate::updateOkButtonText(bool saveAsOnFolder)
644{
645 Q_Q(QFileDialog);
646 // 'Save as' at a folder: Temporarily change to "Open".
647 if (saveAsOnFolder) {
648 setLabelTextControl(QFileDialog::Accept, QFileDialog::tr("&Open"));
649 } else if (options->isLabelExplicitlySet(QFileDialogOptions::Accept)) {
650 setLabelTextControl(QFileDialog::Accept, options->labelText(QFileDialogOptions::Accept));
651 return;
652 } else {
653 switch (q->fileMode()) {
654QT_WARNING_PUSH
655QT_WARNING_DISABLE_DEPRECATED
656 case QFileDialog::DirectoryOnly:
657QT_WARNING_POP
658 case QFileDialog::Directory:
659 setLabelTextControl(QFileDialog::Accept, QFileDialog::tr("&Choose"));
660 break;
661 default:
662 setLabelTextControl(QFileDialog::Accept,
663 q->acceptMode() == QFileDialog::AcceptOpen ?
664 QFileDialog::tr("&Open") :
665 QFileDialog::tr("&Save"));
666 break;
667 }
668 }
669}
670
671void QFileDialogPrivate::updateCancelButtonText()
672{
673 if (options->isLabelExplicitlySet(QFileDialogOptions::Reject))
674 setLabelTextControl(QFileDialog::Reject, options->labelText(QFileDialogOptions::Reject));
675}
676
677void QFileDialogPrivate::retranslateStrings()
678{
679 Q_Q(QFileDialog);
680 /* WIDGETS */
681 if (options->useDefaultNameFilters())
682 q->setNameFilter(QFileDialogOptions::defaultNameFilterString());
683 if (!usingWidgets())
684 return;
685
686 QList<QAction*> actions = qFileDialogUi->treeView->header()->actions();
687 QAbstractItemModel *abstractModel = model;
688#if QT_CONFIG(proxymodel)
689 if (proxyModel)
690 abstractModel = proxyModel;
691#endif
692 int total = qMin(abstractModel->columnCount(QModelIndex()), actions.count() + 1);
693 for (int i = 1; i < total; ++i) {
694 actions.at(i - 1)->setText(QFileDialog::tr("Show ") + abstractModel->headerData(i, Qt::Horizontal, Qt::DisplayRole).toString());
695 }
696
697 /* MENU ACTIONS */
698 renameAction->setText(QFileDialog::tr("&Rename"));
699 deleteAction->setText(QFileDialog::tr("&Delete"));
700 showHiddenAction->setText(QFileDialog::tr("Show &hidden files"));
701 newFolderAction->setText(QFileDialog::tr("&New Folder"));
702 qFileDialogUi->retranslateUi(q);
703 updateLookInLabel();
704 updateFileNameLabel();
705 updateFileTypeLabel();
706 updateCancelButtonText();
707}
708
709void QFileDialogPrivate::emitFilesSelected(const QStringList &files)
710{
711 Q_Q(QFileDialog);
712 emit q->filesSelected(files);
713 if (files.count() == 1)
714 emit q->fileSelected(files.first());
715}
716
717bool QFileDialogPrivate::canBeNativeDialog() const
718{
719 // Don't use Q_Q here! This function is called from ~QDialog,
720 // so Q_Q calling q_func() invokes undefined behavior (invalid cast in q_func()).
721 const QDialog * const q = static_cast<const QDialog*>(q_ptr);
722 if (nativeDialogInUse)
723 return true;
724 if (QCoreApplication::testAttribute(Qt::AA_DontUseNativeDialogs)
725 || q->testAttribute(Qt::WA_DontShowOnScreen)
726 || (options->options() & QFileDialog::DontUseNativeDialog)) {
727 return false;
728 }
729
730 QLatin1String staticName(QFileDialog::staticMetaObject.className());
731 QLatin1String dynamicName(q->metaObject()->className());
732 return (staticName == dynamicName);
733}
734
735bool QFileDialogPrivate::usingWidgets() const
736{
737 return !nativeDialogInUse && qFileDialogUi;
738}
739
740/*!
741 \since 4.5
742 Sets the given \a option to be enabled if \a on is true; otherwise,
743 clears the given \a option.
744
745 Options (particularly the DontUseNativeDialogs option) should be set
746 before changing dialog properties or showing the dialog.
747
748 Setting options while the dialog is visible is not guaranteed to have
749 an immediate effect on the dialog (depending on the option and on the
750 platform).
751
752 Setting options after changing other properties may cause these
753 values to have no effect.
754
755 \sa options, testOption()
756*/
757void QFileDialog::setOption(Option option, bool on)
758{
759 const QFileDialog::Options previousOptions = options();
760 if (!(previousOptions & option) != !on)
761 setOptions(previousOptions ^ option);
762}
763
764/*!
765 \since 4.5
766
767 Returns \c true if the given \a option is enabled; otherwise, returns
768 false.
769
770 \sa options, setOption()
771*/
772bool QFileDialog::testOption(Option option) const
773{
774 Q_D(const QFileDialog);
775 return d->options->testOption(static_cast<QFileDialogOptions::FileDialogOption>(option));
776}
777
778/*!
779 \property QFileDialog::options
780 \brief the various options that affect the look and feel of the dialog
781 \since 4.5
782
783 By default, all options are disabled.
784
785 Options (particularly the DontUseNativeDialogs option) should be set
786 before changing dialog properties or showing the dialog.
787
788 Setting options while the dialog is visible is not guaranteed to have
789 an immediate effect on the dialog (depending on the option and on the
790 platform).
791
792 Setting options after changing other properties may cause these
793 values to have no effect.
794
795 \sa setOption(), testOption()
796*/
797void QFileDialog::setOptions(Options options)
798{
799 Q_D(QFileDialog);
800
801 Options changed = (options ^ QFileDialog::options());
802 if (!changed)
803 return;
804
805 d->options->setOptions(QFileDialogOptions::FileDialogOptions(int(options)));
806
807 if ((options & DontUseNativeDialog) && !d->usingWidgets())
808 d->createWidgets();
809
810 if (d->usingWidgets()) {
811 if (changed & DontResolveSymlinks)
812 d->model->setResolveSymlinks(!(options & DontResolveSymlinks));
813 if (changed & ReadOnly) {
814 bool ro = (options & ReadOnly);
815 d->model->setReadOnly(ro);
816 d->qFileDialogUi->newFolderButton->setEnabled(!ro);
817 d->renameAction->setEnabled(!ro);
818 d->deleteAction->setEnabled(!ro);
819 }
820
821 if (changed & DontUseCustomDirectoryIcons) {
822 QFileIconProvider::Options providerOptions = iconProvider()->options();
823 providerOptions.setFlag(QFileIconProvider::DontUseCustomDirectoryIcons,
824 options & DontUseCustomDirectoryIcons);
825 iconProvider()->setOptions(providerOptions);
826 }
827 }
828
829 if (changed & HideNameFilterDetails)
830 setNameFilters(d->options->nameFilters());
831
832 if (changed & ShowDirsOnly)
833 setFilter((options & ShowDirsOnly) ? filter() & ~QDir::Files : filter() | QDir::Files);
834}
835
836QFileDialog::Options QFileDialog::options() const
837{
838 Q_D(const QFileDialog);
839 return QFileDialog::Options(int(d->options->options()));
840}
841
842/*!
843 \since 4.5
844
845 This function connects one of its signals to the slot specified by \a receiver
846 and \a member. The specific signal depends is filesSelected() if fileMode is
847 ExistingFiles and fileSelected() if fileMode is anything else.
848
849 The signal will be disconnected from the slot when the dialog is closed.
850*/
851void QFileDialog::open(QObject *receiver, const char *member)
852{
853 Q_D(QFileDialog);
854 const char *signal = (fileMode() == ExistingFiles) ? SIGNAL(filesSelected(QStringList))
855 : SIGNAL(fileSelected(QString));
856 connect(this, signal, receiver, member);
857 d->signalToDisconnectOnClose = signal;
858 d->receiverToDisconnectOnClose = receiver;
859 d->memberToDisconnectOnClose = member;
860
861 QDialog::open();
862}
863
864
865/*!
866 \reimp
867*/
868void QFileDialog::setVisible(bool visible)
869{
870 Q_D(QFileDialog);
871 if (visible){
872 if (testAttribute(Qt::WA_WState_ExplicitShowHide) && !testAttribute(Qt::WA_WState_Hidden))
873 return;
874 } else if (testAttribute(Qt::WA_WState_ExplicitShowHide) && testAttribute(Qt::WA_WState_Hidden))
875 return;
876
877 if (d->canBeNativeDialog()){
878 if (d->setNativeDialogVisible(visible)){
879 // Set WA_DontShowOnScreen so that QDialog::setVisible(visible) below
880 // updates the state correctly, but skips showing the non-native version:
881 setAttribute(Qt::WA_DontShowOnScreen);
882#if QT_CONFIG(fscompleter)
883 // So the completer doesn't try to complete and therefore show a popup
884 if (!d->nativeDialogInUse)
885 d->completer->setModel(0);
886#endif
887 } else {
888 d->createWidgets();
889 setAttribute(Qt::WA_DontShowOnScreen, false);
890#if QT_CONFIG(fscompleter)
891 if (!d->nativeDialogInUse) {
892 if (d->proxyModel != 0)
893 d->completer->setModel(d->proxyModel);
894 else
895 d->completer->setModel(d->model);
896 }
897#endif
898 }
899 }
900
901 if (visible && d->usingWidgets())
902 d->qFileDialogUi->fileNameEdit->setFocus();
903
904 QDialog::setVisible(visible);
905}
906
907/*!
908 \internal
909 set the directory to url
910*/
911void QFileDialogPrivate::_q_goToUrl(const QUrl &url)
912{
913 //The shortcut in the side bar may have a parent that is not fetched yet (e.g. an hidden file)
914 //so we force the fetching
915 QFileSystemModelPrivate::QFileSystemNode *node = model->d_func()->node(url.toLocalFile(), true);
916 QModelIndex idx = model->d_func()->index(node);
917 _q_enterDirectory(idx);
918}
919
920/*!
921 \fn void QFileDialog::setDirectory(const QDir &directory)
922
923 \overload
924*/
925
926/*!
927 Sets the file dialog's current \a directory.
928
929 \note On iOS, if you set \a directory to \l{QStandardPaths::standardLocations()}
930 {QStandardPaths::standardLocations(QStandardPaths::PicturesLocation).last()},
931 a native image picker dialog will be used for accessing the user's photo album.
932 The filename returned can be loaded using QFile and related APIs.
933 For this to be enabled, the Info.plist assigned to QMAKE_INFO_PLIST in the
934 project file must contain the key \c NSPhotoLibraryUsageDescription. See
935 Info.plist documentation from Apple for more information regarding this key.
936 This feature was added in Qt 5.5.
937*/
938void QFileDialog::setDirectory(const QString &directory)
939{
940 Q_D(QFileDialog);
941 QString newDirectory = directory;
942 //we remove .. and . from the given path if exist
943 if (!directory.isEmpty())
944 newDirectory = QDir::cleanPath(directory);
945
946 if (!directory.isEmpty() && newDirectory.isEmpty())
947 return;
948
949 QUrl newDirUrl = QUrl::fromLocalFile(newDirectory);
950 QFileDialogPrivate::setLastVisitedDirectory(newDirUrl);
951
952 d->options->setInitialDirectory(QUrl::fromLocalFile(directory));
953 if (!d->usingWidgets()) {
954 d->setDirectory_sys(newDirUrl);
955 return;
956 }
957 if (d->rootPath() == newDirectory)
958 return;
959 QModelIndex root = d->model->setRootPath(newDirectory);
960 if (!d->nativeDialogInUse) {
961 d->qFileDialogUi->newFolderButton->setEnabled(d->model->flags(root) & Qt::ItemIsDropEnabled);
962 if (root != d->rootIndex()) {
963#if QT_CONFIG(fscompleter)
964 if (directory.endsWith(QLatin1Char('/')))
965 d->completer->setCompletionPrefix(newDirectory);
966 else
967 d->completer->setCompletionPrefix(newDirectory + QLatin1Char('/'));
968#endif
969 d->setRootIndex(root);
970 }
971 d->qFileDialogUi->listView->selectionModel()->clear();
972 }
973}
974
975/*!
976 Returns the directory currently being displayed in the dialog.
977*/
978QDir QFileDialog::directory() const
979{
980 Q_D(const QFileDialog);
981 if (d->nativeDialogInUse) {
982 QString dir = d->directory_sys().toLocalFile();
983 return QDir(dir.isEmpty() ? d->options->initialDirectory().toLocalFile() : dir);
984 }
985 return d->rootPath();
986}
987
988/*!
989 Sets the file dialog's current \a directory url.
990
991 \note The non-native QFileDialog supports only local files.
992
993 \note On Windows, it is possible to pass URLs representing
994 one of the \e {virtual folders}, such as "Computer" or "Network".
995 This is done by passing a QUrl using the scheme \c clsid followed
996 by the CLSID value with the curly braces removed. For example the URL
997 \c clsid:374DE290-123F-4565-9164-39C4925E467B denotes the download
998 location. For a complete list of possible values, see the MSDN documentation on
999 \l{https://msdn.microsoft.com/en-us/library/windows/desktop/dd378457.aspx}{KNOWNFOLDERID}.
1000 This feature was added in Qt 5.5.
1001
1002 \sa QUuid
1003 \since 5.2
1004*/
1005void QFileDialog::setDirectoryUrl(const QUrl &directory)
1006{
1007 Q_D(QFileDialog);
1008 if (!directory.isValid())
1009 return;
1010
1011 QFileDialogPrivate::setLastVisitedDirectory(directory);
1012 d->options->setInitialDirectory(directory);
1013
1014 if (d->nativeDialogInUse)
1015 d->setDirectory_sys(directory);
1016 else if (directory.isLocalFile())
1017 setDirectory(directory.toLocalFile());
1018 else if (Q_UNLIKELY(d->usingWidgets()))
1019 qWarning("Non-native QFileDialog supports only local files");
1020}
1021
1022/*!
1023 Returns the url of the directory currently being displayed in the dialog.
1024
1025 \since 5.2
1026*/
1027QUrl QFileDialog::directoryUrl() const
1028{
1029 Q_D(const QFileDialog);
1030 if (d->nativeDialogInUse)
1031 return d->directory_sys();
1032 else
1033 return QUrl::fromLocalFile(directory().absolutePath());
1034}
1035
1036// FIXME Qt 5.4: Use upcoming QVolumeInfo class to determine this information?
1037static inline bool isCaseSensitiveFileSystem(const QString &path)
1038{
1039 Q_UNUSED(path)
1040#if defined(Q_OS_WIN)
1041 // Return case insensitive unconditionally, even if someone has a case sensitive
1042 // file system mounted, wrongly capitalized drive letters will cause mismatches.
1043 return false;
1044#elif defined(Q_OS_OSX)
1045 return pathconf(QFile::encodeName(path).constData(), _PC_CASE_SENSITIVE);
1046#else
1047 return true;
1048#endif
1049}
1050
1051// Determine the file name to be set on the line edit from the path
1052// passed to selectFile() in mode QFileDialog::AcceptSave.
1053static inline QString fileFromPath(const QString &rootPath, QString path)
1054{
1055 if (!QFileInfo(path).isAbsolute())
1056 return path;
1057 if (path.startsWith(rootPath, isCaseSensitiveFileSystem(rootPath) ? Qt::CaseSensitive : Qt::CaseInsensitive))
1058 path.remove(0, rootPath.size());
1059
1060 if (path.isEmpty())
1061 return path;
1062
1063 if (path.at(0) == QDir::separator()
1064#ifdef Q_OS_WIN
1065 //On Windows both cases can happen
1066 || path.at(0) == QLatin1Char('/')
1067#endif
1068 ) {
1069 path.remove(0, 1);
1070 }
1071 return path;
1072}
1073
1074/*!
1075 Selects the given \a filename in the file dialog.
1076
1077 \sa selectedFiles()
1078*/
1079void QFileDialog::selectFile(const QString &filename)
1080{
1081 Q_D(QFileDialog);
1082 if (filename.isEmpty())
1083 return;
1084
1085 if (!d->usingWidgets()) {
1086 QUrl url;
1087 if (QFileInfo(filename).isRelative()) {
1088 url = d->options->initialDirectory();
1089 QString path = url.path();
1090 if (!path.endsWith(QLatin1Char('/')))
1091 path += QLatin1Char('/');
1092 url.setPath(path + filename);
1093 } else {
1094 url = QUrl::fromLocalFile(filename);
1095 }
1096 d->selectFile_sys(url);
1097 d->options->setInitiallySelectedFiles(QList<QUrl>() << url);
1098 return;
1099 }
1100
1101 if (!QDir::isRelativePath(filename)) {
1102 QFileInfo info(filename);
1103 QString filenamePath = info.absoluteDir().path();
1104
1105 if (d->model->rootPath() != filenamePath)
1106 setDirectory(filenamePath);
1107 }
1108
1109 QModelIndex index = d->model->index(filename);
1110 d->qFileDialogUi->listView->selectionModel()->clear();
1111 if (!isVisible() || !d->lineEdit()->hasFocus())
1112 d->lineEdit()->setText(index.isValid() ? index.data().toString() : fileFromPath(d->rootPath(), filename));
1113}
1114
1115/*!
1116 Selects the given \a url in the file dialog.
1117
1118 \note The non-native QFileDialog supports only local files.
1119
1120 \sa selectedUrls()
1121 \since 5.2
1122*/
1123void QFileDialog::selectUrl(const QUrl &url)
1124{
1125 Q_D(QFileDialog);
1126 if (!url.isValid())
1127 return;
1128
1129 if (d->nativeDialogInUse)
1130 d->selectFile_sys(url);
1131 else if (url.isLocalFile())
1132 selectFile(url.toLocalFile());
1133 else
1134 qWarning("Non-native QFileDialog supports only local files");
1135}
1136
1137#ifdef Q_OS_UNIX
1138Q_AUTOTEST_EXPORT QString qt_tildeExpansion(const QString &path)
1139{
1140 if (!path.startsWith(QLatin1Char('~')))
1141 return path;
1142 int separatorPosition = path.indexOf(QDir::separator());
1143 if (separatorPosition < 0)
1144 separatorPosition = path.size();
1145 if (separatorPosition == 1) {
1146 return QDir::homePath() + path.midRef(1);
1147 } else {
1148#if defined(Q_OS_VXWORKS) || defined(Q_OS_INTEGRITY)
1149 const QString homePath = QDir::homePath();
1150#else
1151 const QByteArray userName = path.midRef(1, separatorPosition - 1).toLocal8Bit();
1152# if defined(_POSIX_THREAD_SAFE_FUNCTIONS) && !defined(Q_OS_OPENBSD) && !defined(Q_OS_WASM)
1153 passwd pw;
1154 passwd *tmpPw;
1155 char buf[200];
1156 const int bufSize = sizeof(buf);
1157 int err = 0;
1158# if defined(Q_OS_SOLARIS) && (_POSIX_C_SOURCE - 0 < 199506L)
1159 tmpPw = getpwnam_r(userName.constData(), &pw, buf, bufSize);
1160# else
1161 err = getpwnam_r(userName.constData(), &pw, buf, bufSize, &tmpPw);
1162# endif
1163 if (err || !tmpPw)
1164 return path;
1165 const QString homePath = QString::fromLocal8Bit(pw.pw_dir);
1166# else
1167 passwd *pw = getpwnam(userName.constData());
1168 if (!pw)
1169 return path;
1170 const QString homePath = QString::fromLocal8Bit(pw->pw_dir);
1171# endif
1172#endif
1173 return homePath + path.midRef(separatorPosition);
1174 }
1175}
1176#endif
1177
1178/**
1179 Returns the text in the line edit which can be one or more file names
1180 */
1181QStringList QFileDialogPrivate::typedFiles() const
1182{
1183 Q_Q(const QFileDialog);
1184 QStringList files;
1185 QString editText = lineEdit()->text();
1186 if (!editText.contains(QLatin1Char('"'))) {
1187#ifdef Q_OS_UNIX
1188 const QString prefix = q->directory().absolutePath() + QDir::separator();
1189 if (QFile::exists(prefix + editText))
1190 files << editText;
1191 else
1192 files << qt_tildeExpansion(editText);
1193#else
1194 files << editText;
1195 Q_UNUSED(q)
1196#endif
1197 } else {
1198 // " is used to separate files like so: "file1" "file2" "file3" ...
1199 // ### need escape character for filenames with quotes (")
1200 QStringList tokens = editText.split(QLatin1Char('\"'));
1201 for (int i=0; i<tokens.size(); ++i) {
1202 if ((i % 2) == 0)
1203 continue; // Every even token is a separator
1204#ifdef Q_OS_UNIX
1205 const QString token = tokens.at(i);
1206 const QString prefix = q->directory().absolutePath() + QDir::separator();
1207 if (QFile::exists(prefix + token))
1208 files << token;
1209 else
1210 files << qt_tildeExpansion(token);
1211#else
1212 files << toInternal(tokens.at(i));
1213#endif
1214 }
1215 }
1216 return addDefaultSuffixToFiles(files);
1217}
1218
1219// Return selected files without defaulting to the root of the file system model
1220// used for initializing QFileDialogOptions for native dialogs. The default is
1221// not suitable for native dialogs since it mostly equals directory().
1222QList<QUrl> QFileDialogPrivate::userSelectedFiles() const
1223{
1224 QList<QUrl> files;
1225
1226 if (!usingWidgets())
1227 return addDefaultSuffixToUrls(selectedFiles_sys());
1228
1229 const QModelIndexList selectedRows = qFileDialogUi->listView->selectionModel()->selectedRows();
1230 files.reserve(selectedRows.size());
1231 for (const QModelIndex &index : selectedRows)
1232 files.append(QUrl::fromLocalFile(index.data(QFileSystemModel::FilePathRole).toString()));
1233
1234 if (files.isEmpty() && !lineEdit()->text().isEmpty()) {
1235 const QStringList typedFilesList = typedFiles();
1236 files.reserve(typedFilesList.size());
1237 for (const QString &path : typedFilesList)
1238 files.append(QUrl::fromLocalFile(path));
1239 }
1240
1241 return files;
1242}
1243
1244QStringList QFileDialogPrivate::addDefaultSuffixToFiles(const QStringList &filesToFix) const
1245{
1246 QStringList files;
1247 for (int i=0; i<filesToFix.size(); ++i) {
1248 QString name = toInternal(filesToFix.at(i));
1249 QFileInfo info(name);
1250 // if the filename has no suffix, add the default suffix
1251 const QString defaultSuffix = options->defaultSuffix();
1252 if (!defaultSuffix.isEmpty() && !info.isDir() && name.lastIndexOf(QLatin1Char('.')) == -1)
1253 name += QLatin1Char('.') + defaultSuffix;
1254 if (info.isAbsolute()) {
1255 files.append(name);
1256 } else {
1257 // at this point the path should only have Qt path separators.
1258 // This check is needed since we might be at the root directory
1259 // and on Windows it already ends with slash.
1260 QString path = rootPath();
1261 if (!path.endsWith(QLatin1Char('/')))
1262 path += QLatin1Char('/');
1263 path += name;
1264 files.append(path);
1265 }
1266 }
1267 return files;
1268}
1269
1270QList<QUrl> QFileDialogPrivate::addDefaultSuffixToUrls(const QList<QUrl> &urlsToFix) const
1271{
1272 QList<QUrl> urls;
1273 const int numUrlsToFix = urlsToFix.size();
1274 urls.reserve(numUrlsToFix);
1275 for (int i = 0; i < numUrlsToFix; ++i) {
1276 QUrl url = urlsToFix.at(i);
1277 // if the filename has no suffix, add the default suffix
1278 const QString defaultSuffix = options->defaultSuffix();
1279 if (!defaultSuffix.isEmpty() && !url.path().endsWith(QLatin1Char('/')) && url.path().lastIndexOf(QLatin1Char('.')) == -1)
1280 url.setPath(url.path() + QLatin1Char('.') + defaultSuffix);
1281 urls.append(url);
1282 }
1283 return urls;
1284}
1285
1286
1287/*!
1288 Returns a list of strings containing the absolute paths of the
1289 selected files in the dialog. If no files are selected, or
1290 the mode is not ExistingFiles or ExistingFile, selectedFiles() contains the current path in the viewport.
1291
1292 \sa selectedNameFilter(), selectFile()
1293*/
1294QStringList QFileDialog::selectedFiles() const
1295{
1296 Q_D(const QFileDialog);
1297
1298 QStringList files;
1299 const QList<QUrl> userSelectedFiles = d->userSelectedFiles();
1300 files.reserve(userSelectedFiles.size());
1301 for (const QUrl &file : userSelectedFiles)
1302 files.append(file.toLocalFile());
1303 if (files.isEmpty() && d->usingWidgets()) {
1304 const FileMode fm = fileMode();
1305 if (fm != ExistingFile && fm != ExistingFiles)
1306 files.append(d->rootIndex().data(QFileSystemModel::FilePathRole).toString());
1307 }
1308 return files;
1309}
1310
1311/*!
1312 Returns a list of urls containing the selected files in the dialog.
1313 If no files are selected, or the mode is not ExistingFiles or
1314 ExistingFile, selectedUrls() contains the current path in the viewport.
1315
1316 \sa selectedNameFilter(), selectUrl()
1317 \since 5.2
1318*/
1319QList<QUrl> QFileDialog::selectedUrls() const
1320{
1321 Q_D(const QFileDialog);
1322 if (d->nativeDialogInUse) {
1323 return d->userSelectedFiles();
1324 } else {
1325 QList<QUrl> urls;
1326 const QStringList selectedFileList = selectedFiles();
1327 urls.reserve(selectedFileList.size());
1328 for (const QString &file : selectedFileList)
1329 urls.append(QUrl::fromLocalFile(file));
1330 return urls;
1331 }
1332}
1333
1334/*
1335 Makes a list of filters from ;;-separated text.
1336 Used by the mac and windows implementations
1337*/
1338QStringList qt_make_filter_list(const QString &filter)
1339{
1340 QString f(filter);
1341
1342 if (f.isEmpty())
1343 return QStringList();
1344
1345 QString sep(QLatin1String(";;"));
1346 int i = f.indexOf(sep, 0);
1347 if (i == -1) {
1348 if (f.indexOf(QLatin1Char('\n'), 0) != -1) {
1349 sep = QLatin1Char('\n');
1350 i = f.indexOf(sep, 0);
1351 }
1352 }
1353
1354 return f.split(sep);
1355}
1356
1357/*!
1358 \since 4.4
1359
1360 Sets the filter used in the file dialog to the given \a filter.
1361
1362 If \a filter contains a pair of parentheses containing one or more
1363 filename-wildcard patterns, separated by spaces, then only the
1364 text contained in the parentheses is used as the filter. This means
1365 that these calls are all equivalent:
1366
1367 \snippet code/src_gui_dialogs_qfiledialog.cpp 6
1368
1369 \sa setMimeTypeFilters(), setNameFilters()
1370*/
1371void QFileDialog::setNameFilter(const QString &filter)
1372{
1373 setNameFilters(qt_make_filter_list(filter));
1374}
1375
1376
1377#if QT_DEPRECATED_SINCE(5, 13)
1378/*!
1379 \property QFileDialog::nameFilterDetailsVisible
1380 \obsolete
1381 \brief This property holds whether the filter details is shown or not.
1382 \since 4.4
1383
1384 When this property is \c true (the default), the filter details are shown
1385 in the combo box. When the property is set to false, these are hidden.
1386
1387 Use setOption(HideNameFilterDetails, !\e enabled) or
1388 !testOption(HideNameFilterDetails).
1389*/
1390void QFileDialog::setNameFilterDetailsVisible(bool enabled)
1391{
1392 setOption(HideNameFilterDetails, !enabled);
1393}
1394
1395bool QFileDialog::isNameFilterDetailsVisible() const
1396{
1397 return !testOption(HideNameFilterDetails);
1398}
1399#endif
1400
1401
1402/*
1403 Strip the filters by removing the details, e.g. (*.*).
1404*/
1405QStringList qt_strip_filters(const QStringList &filters)
1406{
1407 QStringList strippedFilters;
1408 QRegExp r(QString::fromLatin1(QPlatformFileDialogHelper::filterRegExp));
1409 const int numFilters = filters.count();
1410 strippedFilters.reserve(numFilters);
1411 for (int i = 0; i < numFilters; ++i) {
1412 QString filterName;
1413 int index = r.indexIn(filters[i]);
1414 if (index >= 0)
1415 filterName = r.cap(1);
1416 strippedFilters.append(filterName.simplified());
1417 }
1418 return strippedFilters;
1419}
1420
1421
1422/*!
1423 \since 4.4
1424
1425 Sets the \a filters used in the file dialog.
1426
1427 Note that the filter \b{*.*} is not portable, because the historical
1428 assumption that the file extension determines the file type is not
1429 consistent on every operating system. It is possible to have a file with no
1430 dot in its name (for example, \c Makefile). In a native Windows file
1431 dialog, \b{*.*} will match such files, while in other types of file dialogs
1432 it may not. So it is better to use \b{*} if you mean to select any file.
1433
1434 \snippet code/src_gui_dialogs_qfiledialog.cpp 7
1435
1436 \l setMimeTypeFilters() has the advantage of providing all possible name
1437 filters for each file type. For example, JPEG images have three possible
1438 extensions; if your application can open such files, selecting the
1439 \c image/jpeg mime type as a filter will allow you to open all of them.
1440*/
1441void QFileDialog::setNameFilters(const QStringList &filters)
1442{
1443 Q_D(QFileDialog);
1444 QStringList cleanedFilters;
1445 const int numFilters = filters.count();
1446 cleanedFilters.reserve(numFilters);
1447 for (int i = 0; i < numFilters; ++i) {
1448 cleanedFilters << filters[i].simplified();
1449 }
1450 d->options->setNameFilters(cleanedFilters);
1451
1452 if (!d->usingWidgets())
1453 return;
1454
1455 d->qFileDialogUi->fileTypeCombo->clear();
1456 if (cleanedFilters.isEmpty())
1457 return;
1458
1459 if (testOption(HideNameFilterDetails))
1460 d->qFileDialogUi->fileTypeCombo->addItems(qt_strip_filters(cleanedFilters));
1461 else
1462 d->qFileDialogUi->fileTypeCombo->addItems(cleanedFilters);
1463
1464 d->_q_useNameFilter(0);
1465}
1466
1467/*!
1468 \since 4.4
1469
1470 Returns the file type filters that are in operation on this file
1471 dialog.
1472*/
1473QStringList QFileDialog::nameFilters() const
1474{
1475 return d_func()->options->nameFilters();
1476}
1477
1478/*!
1479 \since 4.4
1480
1481 Sets the current file type \a filter. Multiple filters can be
1482 passed in \a filter by separating them with semicolons or spaces.
1483
1484 \sa setNameFilter(), setNameFilters(), selectedNameFilter()
1485*/
1486void QFileDialog::selectNameFilter(const QString &filter)
1487{
1488 Q_D(QFileDialog);
1489 d->options->setInitiallySelectedNameFilter(filter);
1490 if (!d->usingWidgets()) {
1491 d->selectNameFilter_sys(filter);
1492 return;
1493 }
1494 int i = -1;
1495 if (testOption(HideNameFilterDetails)) {
1496 const QStringList filters = qt_strip_filters(qt_make_filter_list(filter));
1497 if (!filters.isEmpty())
1498 i = d->qFileDialogUi->fileTypeCombo->findText(filters.first());
1499 } else {
1500 i = d->qFileDialogUi->fileTypeCombo->findText(filter);
1501 }
1502 if (i >= 0) {
1503 d->qFileDialogUi->fileTypeCombo->setCurrentIndex(i);
1504 d->_q_useNameFilter(d->qFileDialogUi->fileTypeCombo->currentIndex());
1505 }
1506}
1507
1508/*!
1509 \since 4.4
1510
1511 Returns the filter that the user selected in the file dialog.
1512
1513 \sa selectedFiles()
1514*/
1515QString QFileDialog::selectedNameFilter() const
1516{
1517 Q_D(const QFileDialog);
1518 if (!d->usingWidgets())
1519 return d->selectedNameFilter_sys();
1520
1521 return d->qFileDialogUi->fileTypeCombo->currentText();
1522}
1523
1524/*!
1525 \since 4.4
1526
1527 Returns the filter that is used when displaying files.
1528
1529 \sa setFilter()
1530*/
1531QDir::Filters QFileDialog::filter() const
1532{
1533 Q_D(const QFileDialog);
1534 if (d->usingWidgets())
1535 return d->model->filter();
1536 return d->options->filter();
1537}
1538
1539/*!
1540 \since 4.4
1541
1542 Sets the filter used by the model to \a filters. The filter is used
1543 to specify the kind of files that should be shown.
1544
1545 \sa filter()
1546*/
1547
1548void QFileDialog::setFilter(QDir::Filters filters)
1549{
1550 Q_D(QFileDialog);
1551 d->options->setFilter(filters);
1552 if (!d->usingWidgets()) {
1553 d->setFilter_sys();
1554 return;
1555 }
1556
1557 d->model->setFilter(filters);
1558 d->showHiddenAction->setChecked((filters & QDir::Hidden));
1559}
1560
1561#if QT_CONFIG(mimetype)
1562
1563static QString nameFilterForMime(const QString &mimeType)
1564{
1565 QMimeDatabase db;
1566 QMimeType mime(db.mimeTypeForName(mimeType));
1567 if (mime.isValid()) {
1568 if (mime.isDefault()) {
1569 return QFileDialog::tr("All files (*)");
1570 } else {
1571 const QString patterns = mime.globPatterns().join(QLatin1Char(' '));
1572 return mime.comment() + QLatin1String(" (") + patterns + QLatin1Char(')');
1573 }
1574 }
1575 return QString();
1576}
1577
1578/*!
1579 \since 5.2
1580
1581 Sets the \a filters used in the file dialog, from a list of MIME types.
1582
1583 Convenience method for setNameFilters().
1584 Uses QMimeType to create a name filter from the glob patterns and description
1585 defined in each MIME type.
1586
1587 Use application/octet-stream for the "All files (*)" filter, since that
1588 is the base MIME type for all files.
1589
1590 Calling setMimeTypeFilters overrides any previously set name filters,
1591 and changes the return value of nameFilters().
1592
1593 \snippet code/src_gui_dialogs_qfiledialog.cpp 13
1594*/
1595void QFileDialog::setMimeTypeFilters(const QStringList &filters)
1596{
1597 Q_D(QFileDialog);
1598 QStringList nameFilters;
1599 for (const QString &mimeType : filters) {
1600 const QString text = nameFilterForMime(mimeType);
1601 if (!text.isEmpty())
1602 nameFilters.append(text);
1603 }
1604 setNameFilters(nameFilters);
1605 d->options->setMimeTypeFilters(filters);
1606}
1607
1608/*!
1609 \since 5.2
1610
1611 Returns the MIME type filters that are in operation on this file
1612 dialog.
1613*/
1614QStringList QFileDialog::mimeTypeFilters() const
1615{
1616 return d_func()->options->mimeTypeFilters();
1617}
1618
1619/*!
1620 \since 5.2
1621
1622 Sets the current MIME type \a filter.
1623
1624*/
1625void QFileDialog::selectMimeTypeFilter(const QString &filter)
1626{
1627 Q_D(QFileDialog);
1628 d->options->setInitiallySelectedMimeTypeFilter(filter);
1629
1630 const QString filterForMime = nameFilterForMime(filter);
1631
1632 if (!d->usingWidgets()) {
1633 d->selectMimeTypeFilter_sys(filter);
1634 if (d->selectedMimeTypeFilter_sys().isEmpty() && !filterForMime.isEmpty()) {
1635 selectNameFilter(filterForMime);
1636 }
1637 } else if (!filterForMime.isEmpty()) {
1638 selectNameFilter(filterForMime);
1639 }
1640}
1641
1642#endif // mimetype
1643
1644/*!
1645 * \since 5.9
1646 * \return The mimetype of the file that the user selected in the file dialog.
1647 */
1648QString QFileDialog::selectedMimeTypeFilter() const
1649{
1650 Q_D(const QFileDialog);
1651 QString mimeTypeFilter;
1652 if (!d->usingWidgets())
1653 mimeTypeFilter = d->selectedMimeTypeFilter_sys();
1654
1655#if QT_CONFIG(mimetype)
1656 if (mimeTypeFilter.isNull() && !d->options->mimeTypeFilters().isEmpty()) {
1657 const auto nameFilter = selectedNameFilter();
1658 const auto mimeTypes = d->options->mimeTypeFilters();
1659 for (const auto &mimeType: mimeTypes) {
1660 QString filter = nameFilterForMime(mimeType);
1661 if (testOption(HideNameFilterDetails))
1662 filter = qt_strip_filters({ filter }).first();
1663 if (filter == nameFilter) {
1664 mimeTypeFilter = mimeType;
1665 break;
1666 }
1667 }
1668 }
1669#endif
1670
1671 return mimeTypeFilter;
1672}
1673
1674/*!
1675 \property QFileDialog::viewMode
1676 \brief the way files and directories are displayed in the dialog
1677
1678 By default, the \c Detail mode is used to display information about
1679 files and directories.
1680
1681 \sa ViewMode
1682*/
1683void QFileDialog::setViewMode(QFileDialog::ViewMode mode)
1684{
1685 Q_D(QFileDialog);
1686 d->options->setViewMode(static_cast<QFileDialogOptions::ViewMode>(mode));
1687 if (!d->usingWidgets())
1688 return;
1689 if (mode == Detail)
1690 d->_q_showDetailsView();
1691 else
1692 d->_q_showListView();
1693}
1694
1695QFileDialog::ViewMode QFileDialog::viewMode() const
1696{
1697 Q_D(const QFileDialog);
1698 if (!d->usingWidgets())
1699 return static_cast<QFileDialog::ViewMode>(d->options->viewMode());
1700 return (d->qFileDialogUi->stackedWidget->currentWidget() == d->qFileDialogUi->listView->parent() ? QFileDialog::List : QFileDialog::Detail);
1701}
1702
1703/*!
1704 \property QFileDialog::fileMode
1705 \brief the file mode of the dialog
1706
1707 The file mode defines the number and type of items that the user is
1708 expected to select in the dialog.
1709
1710 By default, this property is set to AnyFile.
1711
1712 This function will set the labels for the FileName and
1713 \l{QFileDialog::}{Accept} \l{DialogLabel}s. It is possible to set
1714 custom text after the call to setFileMode().
1715
1716 \sa FileMode
1717*/
1718void QFileDialog::setFileMode(QFileDialog::FileMode mode)
1719{
1720 Q_D(QFileDialog);
1721 d->options->setFileMode(static_cast<QFileDialogOptions::FileMode>(mode));
1722
1723 // keep ShowDirsOnly option in sync with fileMode (BTW, DirectoryOnly is obsolete)
1724QT_WARNING_PUSH
1725QT_WARNING_DISABLE_DEPRECATED
1726 setOption(ShowDirsOnly, mode == DirectoryOnly);
1727QT_WARNING_POP
1728
1729 if (!d->usingWidgets())
1730 return;
1731
1732 d->retranslateWindowTitle();
1733
1734 // set selection mode and behavior
1735 QAbstractItemView::SelectionMode selectionMode;
1736 if (mode == QFileDialog::ExistingFiles)
1737 selectionMode = QAbstractItemView::ExtendedSelection;
1738 else
1739 selectionMode = QAbstractItemView::SingleSelection;
1740 d->qFileDialogUi->listView->setSelectionMode(selectionMode);
1741 d->qFileDialogUi->treeView->setSelectionMode(selectionMode);
1742 // set filter
1743 d->model->setFilter(d->filterForMode(filter()));
1744 // setup file type for directory
1745QT_WARNING_PUSH
1746QT_WARNING_DISABLE_DEPRECATED
1747 if (mode == DirectoryOnly || mode == Directory) {
1748 d->qFileDialogUi->fileTypeCombo->clear();
1749 d->qFileDialogUi->fileTypeCombo->addItem(tr("Directories"));
1750 d->qFileDialogUi->fileTypeCombo->setEnabled(false);
1751 }
1752QT_WARNING_POP
1753 d->updateFileNameLabel();
1754 d->updateOkButtonText();
1755 d->qFileDialogUi->fileTypeCombo->setEnabled(!testOption(ShowDirsOnly));
1756 d->_q_updateOkButton();
1757}
1758
1759QFileDialog::FileMode QFileDialog::fileMode() const
1760{
1761 Q_D(const QFileDialog);
1762 return static_cast<FileMode>(d->options->fileMode());
1763}
1764
1765/*!
1766 \property QFileDialog::acceptMode
1767 \brief the accept mode of the dialog
1768
1769 The action mode defines whether the dialog is for opening or saving files.
1770
1771 By default, this property is set to \l{AcceptOpen}.
1772
1773 \sa AcceptMode
1774*/
1775void QFileDialog::setAcceptMode(QFileDialog::AcceptMode mode)
1776{
1777 Q_D(QFileDialog);
1778 d->options->setAcceptMode(static_cast<QFileDialogOptions::AcceptMode>(mode));
1779 // clear WA_DontShowOnScreen so that d->canBeNativeDialog() doesn't return false incorrectly
1780 setAttribute(Qt::WA_DontShowOnScreen, false);
1781 if (!d->usingWidgets())
1782 return;
1783 QDialogButtonBox::StandardButton button = (mode == AcceptOpen ? QDialogButtonBox::Open : QDialogButtonBox::Save);
1784 d->qFileDialogUi->buttonBox->setStandardButtons(button | QDialogButtonBox::Cancel);
1785 d->qFileDialogUi->buttonBox->button(button)->setEnabled(false);
1786 d->_q_updateOkButton();
1787 if (mode == AcceptSave) {
1788 d->qFileDialogUi->lookInCombo->setEditable(false);
1789 }
1790 d->retranslateWindowTitle();
1791}
1792
1793/*!
1794 \property QFileDialog::supportedSchemes
1795 \brief the URL schemes that the file dialog should allow navigating to.
1796 \since 5.6
1797
1798 Setting this property allows to restrict the type of URLs the
1799 user will be able to select. It is a way for the application to declare
1800 the protocols it will support to fetch the file content. An empty list
1801 means that no restriction is applied (the default).
1802 Supported for local files ("file" scheme) is implicit and always enabled;
1803 it is not necessary to include it in the restriction.
1804*/
1805
1806void QFileDialog::setSupportedSchemes(const QStringList &schemes)
1807{
1808 Q_D(QFileDialog);
1809 d->options->setSupportedSchemes(schemes);
1810}
1811
1812QStringList QFileDialog::supportedSchemes() const
1813{
1814 return d_func()->options->supportedSchemes();
1815}
1816
1817/*
1818 Returns the file system model index that is the root index in the
1819 views
1820*/
1821QModelIndex QFileDialogPrivate::rootIndex() const {
1822 return mapToSource(qFileDialogUi->listView->rootIndex());
1823}
1824
1825QAbstractItemView *QFileDialogPrivate::currentView() const {
1826 if (!qFileDialogUi->stackedWidget)
1827 return 0;
1828 if (qFileDialogUi->stackedWidget->currentWidget() == qFileDialogUi->listView->parent())
1829 return qFileDialogUi->listView;
1830 return qFileDialogUi->treeView;
1831}
1832
1833QLineEdit *QFileDialogPrivate::lineEdit() const {
1834 return (QLineEdit*)qFileDialogUi->fileNameEdit;
1835}
1836
1837int QFileDialogPrivate::maxNameLength(const QString &path)
1838{
1839#if defined(Q_OS_UNIX)
1840 return ::pathconf(QFile::encodeName(path).data(), _PC_NAME_MAX);
1841#elif defined(Q_OS_WINRT)
1842 Q_UNUSED(path);
1843 return MAX_PATH;
1844#elif defined(Q_OS_WIN)
1845 DWORD maxLength;
1846 const QString drive = path.left(3);
1847 if (::GetVolumeInformation(reinterpret_cast<const wchar_t *>(drive.utf16()), NULL, 0, NULL, &maxLength, NULL, NULL, 0) == false)
1848 return -1;
1849 return maxLength;
1850#else
1851 Q_UNUSED(path);
1852#endif
1853 return -1;
1854}
1855
1856/*
1857 Sets the view root index to be the file system model index
1858*/
1859void QFileDialogPrivate::setRootIndex(const QModelIndex &index) const {
1860 Q_ASSERT(index.isValid() ? index.model() == model : true);
1861 QModelIndex idx = mapFromSource(index);
1862 qFileDialogUi->treeView->setRootIndex(idx);
1863 qFileDialogUi->listView->setRootIndex(idx);
1864}
1865/*
1866 Select a file system model index
1867 returns the index that was selected (or not depending upon sortfilterproxymodel)
1868*/
1869QModelIndex QFileDialogPrivate::select(const QModelIndex &index) const {
1870 Q_ASSERT(index.isValid() ? index.model() == model : true);
1871
1872 QModelIndex idx = mapFromSource(index);
1873 if (idx.isValid() && !qFileDialogUi->listView->selectionModel()->isSelected(idx))
1874 qFileDialogUi->listView->selectionModel()->select(idx,
1875 QItemSelectionModel::Select | QItemSelectionModel::Rows);
1876 return idx;
1877}
1878
1879QFileDialog::AcceptMode QFileDialog::acceptMode() const
1880{
1881 Q_D(const QFileDialog);
1882 return static_cast<AcceptMode>(d->options->acceptMode());
1883}
1884
1885#if QT_DEPRECATED_SINCE(5, 13)
1886/*!
1887 \property QFileDialog::readOnly
1888 \obsolete
1889 \brief Whether the filedialog is read-only
1890
1891 If this property is set to false, the file dialog will allow renaming,
1892 and deleting of files and directories and creating directories.
1893
1894 Use setOption(ReadOnly, \e enabled) or testOption(ReadOnly) instead.
1895*/
1896void QFileDialog::setReadOnly(bool enabled)
1897{
1898 setOption(ReadOnly, enabled);
1899}
1900
1901bool QFileDialog::isReadOnly() const
1902{
1903 return testOption(ReadOnly);
1904}
1905
1906/*!
1907 \property QFileDialog::resolveSymlinks
1908 \obsolete
1909 \brief whether the filedialog should resolve shortcuts
1910
1911 If this property is set to true, the file dialog will resolve
1912 shortcuts or symbolic links.
1913
1914 Use setOption(DontResolveSymlinks, !\a enabled) or
1915 !testOption(DontResolveSymlinks).
1916*/
1917void QFileDialog::setResolveSymlinks(bool enabled)
1918{
1919 setOption(DontResolveSymlinks, !enabled);
1920}
1921
1922bool QFileDialog::resolveSymlinks() const
1923{
1924 return !testOption(DontResolveSymlinks);
1925}
1926
1927/*!
1928 \property QFileDialog::confirmOverwrite
1929 \obsolete
1930 \brief whether the filedialog should ask before accepting a selected file,
1931 when the accept mode is AcceptSave
1932
1933 Use setOption(DontConfirmOverwrite, !\e enabled) or
1934 !testOption(DontConfirmOverwrite) instead.
1935*/
1936void QFileDialog::setConfirmOverwrite(bool enabled)
1937{
1938 setOption(DontConfirmOverwrite, !enabled);
1939}
1940
1941bool QFileDialog::confirmOverwrite() const
1942{
1943 return !testOption(DontConfirmOverwrite);
1944}
1945#endif
1946
1947/*!
1948 \property QFileDialog::defaultSuffix
1949 \brief suffix added to the filename if no other suffix was specified
1950
1951 This property specifies a string that will be added to the
1952 filename if it has no suffix already. The suffix is typically
1953 used to indicate the file type (e.g. "txt" indicates a text
1954 file).
1955
1956 If the first character is a dot ('.'), it is removed.
1957*/
1958void QFileDialog::setDefaultSuffix(const QString &suffix)
1959{
1960 Q_D(QFileDialog);
1961 d->options->setDefaultSuffix(suffix);
1962}
1963
1964QString QFileDialog::defaultSuffix() const
1965{
1966 Q_D(const QFileDialog);
1967 return d->options->defaultSuffix();
1968}
1969
1970/*!
1971 Sets the browsing history of the filedialog to contain the given
1972 \a paths.
1973*/
1974void QFileDialog::setHistory(const QStringList &paths)
1975{
1976 Q_D(QFileDialog);
1977 if (d->usingWidgets())
1978 d->qFileDialogUi->lookInCombo->setHistory(paths);
1979}
1980
1981void QFileDialogComboBox::setHistory(const QStringList &paths)
1982{
1983 m_history = paths;
1984 // Only populate the first item, showPopup will populate the rest if needed
1985 QList<QUrl> list;
1986 QModelIndex idx = d_ptr->model->index(d_ptr->rootPath());
1987 //On windows the popup display the "C:\", convert to nativeSeparators
1988 QUrl url = QUrl::fromLocalFile(QDir::toNativeSeparators(idx.data(QFileSystemModel::FilePathRole).toString()));
1989 if (url.isValid())
1990 list.append(url);
1991 urlModel->setUrls(list);
1992}
1993
1994/*!
1995 Returns the browsing history of the filedialog as a list of paths.
1996*/
1997QStringList QFileDialog::history() const
1998{
1999 Q_D(const QFileDialog);
2000 if (!d->usingWidgets())
2001 return QStringList();
2002 QStringList currentHistory = d->qFileDialogUi->lookInCombo->history();
2003 //On windows the popup display the "C:\", convert to nativeSeparators
2004 QString newHistory = QDir::toNativeSeparators(d->rootIndex().data(QFileSystemModel::FilePathRole).toString());
2005 if (!currentHistory.contains(newHistory))
2006 currentHistory << newHistory;
2007 return currentHistory;
2008}
2009
2010/*!
2011 Sets the item delegate used to render items in the views in the
2012 file dialog to the given \a delegate.
2013
2014 \warning You should not share the same instance of a delegate between views.
2015 Doing so can cause incorrect or unintuitive editing behavior since each
2016 view connected to a given delegate may receive the \l{QAbstractItemDelegate::}{closeEditor()}
2017 signal, and attempt to access, modify or close an editor that has already been closed.
2018
2019 Note that the model used is QFileSystemModel. It has custom item data roles, which is
2020 described by the \l{QFileSystemModel::}{Roles} enum. You can use a QFileIconProvider if
2021 you only want custom icons.
2022
2023 \sa itemDelegate(), setIconProvider(), QFileSystemModel
2024*/
2025void QFileDialog::setItemDelegate(QAbstractItemDelegate *delegate)
2026{
2027 Q_D(QFileDialog);
2028 if (!d->usingWidgets())
2029 return;
2030 d->qFileDialogUi->listView->setItemDelegate(delegate);
2031 d->qFileDialogUi->treeView->setItemDelegate(delegate);
2032}
2033
2034/*!
2035 Returns the item delegate used to render the items in the views in the filedialog.
2036*/
2037QAbstractItemDelegate *QFileDialog::itemDelegate() const
2038{
2039 Q_D(const QFileDialog);
2040 if (!d->usingWidgets())
2041 return 0;
2042 return d->qFileDialogUi->listView->itemDelegate();
2043}
2044
2045/*!
2046 Sets the icon provider used by the filedialog to the specified \a provider.
2047*/
2048void QFileDialog::setIconProvider(QFileIconProvider *provider)
2049{
2050 Q_D(QFileDialog);
2051 if (!d->usingWidgets())
2052 return;
2053 d->model->setIconProvider(provider);
2054 //It forces the refresh of all entries in the side bar, then we can get new icons
2055 d->qFileDialogUi->sidebar->setUrls(d->qFileDialogUi->sidebar->urls());
2056}
2057
2058/*!
2059 Returns the icon provider used by the filedialog.
2060*/
2061QFileIconProvider *QFileDialog::iconProvider() const
2062{
2063 Q_D(const QFileDialog);
2064 if (!d->model)
2065 return nullptr;
2066 return d->model->iconProvider();
2067}
2068
2069void QFileDialogPrivate::setLabelTextControl(QFileDialog::DialogLabel label, const QString &text)
2070{
2071 if (!qFileDialogUi)
2072 return;
2073 switch (label) {
2074 case QFileDialog::LookIn:
2075 qFileDialogUi->lookInLabel->setText(text);
2076 break;
2077 case QFileDialog::FileName:
2078 qFileDialogUi->fileNameLabel->setText(text);
2079 break;
2080 case QFileDialog::FileType:
2081 qFileDialogUi->fileTypeLabel->setText(text);
2082 break;
2083 case QFileDialog::Accept:
2084 if (q_func()->acceptMode() == QFileDialog::AcceptOpen) {
2085 if (QPushButton *button = qFileDialogUi->buttonBox->button(QDialogButtonBox::Open))
2086 button->setText(text);
2087 } else {
2088 if (QPushButton *button = qFileDialogUi->buttonBox->button(QDialogButtonBox::Save))
2089 button->setText(text);
2090 }
2091 break;
2092 case QFileDialog::Reject:
2093 if (QPushButton *button = qFileDialogUi->buttonBox->button(QDialogButtonBox::Cancel))
2094 button->setText(text);
2095 break;
2096 }
2097}
2098
2099/*!
2100 Sets the \a text shown in the filedialog in the specified \a label.
2101*/
2102
2103void QFileDialog::setLabelText(DialogLabel label, const QString &text)
2104{
2105 Q_D(QFileDialog);
2106 d->options->setLabelText(static_cast<QFileDialogOptions::DialogLabel>(label), text);
2107 d->setLabelTextControl(label, text);
2108}
2109
2110/*!
2111 Returns the text shown in the filedialog in the specified \a label.
2112*/
2113QString QFileDialog::labelText(DialogLabel label) const
2114{
2115 Q_D(const QFileDialog);
2116 if (!d->usingWidgets())
2117 return d->options->labelText(static_cast<QFileDialogOptions::DialogLabel>(label));
2118 QPushButton *button;
2119 switch (label) {
2120 case LookIn:
2121 return d->qFileDialogUi->lookInLabel->text();
2122 case FileName:
2123 return d->qFileDialogUi->fileNameLabel->text();
2124 case FileType:
2125 return d->qFileDialogUi->fileTypeLabel->text();
2126 case Accept:
2127 if (acceptMode() == AcceptOpen)
2128 button = d->qFileDialogUi->buttonBox->button(QDialogButtonBox::Open);
2129 else
2130 button = d->qFileDialogUi->buttonBox->button(QDialogButtonBox::Save);
2131 if (button)
2132 return button->text();
2133 break;
2134 case Reject:
2135 button = d->qFileDialogUi->buttonBox->button(QDialogButtonBox::Cancel);
2136 if (button)
2137 return button->text();
2138 break;
2139 }
2140 return QString();
2141}
2142
2143/*!
2144 This is a convenience static function that returns an existing file
2145 selected by the user. If the user presses Cancel, it returns a null string.
2146
2147 \snippet code/src_gui_dialogs_qfiledialog.cpp 8
2148
2149 The function creates a modal file dialog with the given \a parent widget.
2150 If \a parent is not \nullptr, the dialog will be shown centered over the
2151 parent widget.
2152
2153 The file dialog's working directory will be set to \a dir. If \a dir
2154 includes a file name, the file will be selected. Only files that match the
2155 given \a filter are shown. The filter selected is set to \a selectedFilter.
2156 The parameters \a dir, \a selectedFilter, and \a filter may be empty
2157 strings. If you want multiple filters, separate them with ';;', for
2158 example:
2159
2160 \snippet code/src_gui_dialogs_qfiledialog.cpp 14
2161
2162 The \a options argument holds various options about how to run the dialog,
2163 see the QFileDialog::Option enum for more information on the flags you can
2164 pass.
2165
2166 The dialog's caption is set to \a caption. If \a caption is not specified
2167 then a default caption will be used.
2168
2169 On Windows, and \macos, this static function will use the
2170 native file dialog and not a QFileDialog.
2171
2172 On Windows the dialog will spin a blocking modal event loop that will not
2173 dispatch any QTimers, and if \a parent is not \nullptr then it will position
2174 the dialog just below the parent's title bar.
2175
2176 On Unix/X11, the normal behavior of the file dialog is to resolve and
2177 follow symlinks. For example, if \c{/usr/tmp} is a symlink to \c{/var/tmp},
2178 the file dialog will change to \c{/var/tmp} after entering \c{/usr/tmp}. If
2179 \a options includes DontResolveSymlinks, the file dialog will treat
2180 symlinks as regular directories.
2181
2182 \warning Do not delete \a parent during the execution of the dialog. If you
2183 want to do this, you should create the dialog yourself using one of the
2184 QFileDialog constructors.
2185
2186 \sa getOpenFileNames(), getSaveFileName(), getExistingDirectory()
2187*/
2188QString QFileDialog::getOpenFileName(QWidget *parent,
2189 const QString &caption,
2190 const QString &dir,
2191 const QString &filter,
2192 QString *selectedFilter,
2193 Options options)
2194{
2195 const QStringList schemes = QStringList(QStringLiteral("file"));
2196 const QUrl selectedUrl = getOpenFileUrl(parent, caption, QUrl::fromLocalFile(dir), filter, selectedFilter, options, schemes);
2197 return selectedUrl.toLocalFile();
2198}
2199
2200/*!
2201 This is a convenience static function that returns an existing file
2202 selected by the user. If the user presses Cancel, it returns an
2203 empty url.
2204
2205 The function is used similarly to QFileDialog::getOpenFileName(). In
2206 particular \a parent, \a caption, \a dir, \a filter, \a selectedFilter
2207 and \a options are used in the exact same way.
2208
2209 The main difference with QFileDialog::getOpenFileName() comes from
2210 the ability offered to the user to select a remote file. That's why
2211 the return type and the type of \a dir is QUrl.
2212
2213 The \a supportedSchemes argument allows to restrict the type of URLs the
2214 user will be able to select. It is a way for the application to declare
2215 the protocols it will support to fetch the file content. An empty list
2216 means that no restriction is applied (the default).
2217 Supported for local files ("file" scheme) is implicit and always enabled;
2218 it is not necessary to include it in the restriction.
2219
2220 When possible, this static function will use the native file dialog and
2221 not a QFileDialog. On platforms which don't support selecting remote
2222 files, Qt will allow to select only local files.
2223
2224 \sa getOpenFileName(), getOpenFileUrls(), getSaveFileUrl(), getExistingDirectoryUrl()
2225 \since 5.2
2226*/
2227QUrl QFileDialog::getOpenFileUrl(QWidget *parent,
2228 const QString &caption,
2229 const QUrl &dir,
2230 const QString &filter,
2231 QString *selectedFilter,
2232 Options options,
2233 const QStringList &supportedSchemes)
2234{
2235 QFileDialogArgs args;
2236 args.parent = parent;
2237 args.caption = caption;
2238 args.directory = QFileDialogPrivate::workingDirectory(dir);
2239 args.selection = QFileDialogPrivate::initialSelection(dir);
2240 args.filter = filter;
2241 args.mode = ExistingFile;
2242 args.options = options;
2243
2244 QFileDialog dialog(args);
2245 dialog.setSupportedSchemes(supportedSchemes);
2246 if (selectedFilter && !selectedFilter->isEmpty())
2247 dialog.selectNameFilter(*selectedFilter);
2248 if (dialog.exec() == QDialog::Accepted) {
2249 if (selectedFilter)
2250 *selectedFilter = dialog.selectedNameFilter();
2251 return dialog.selectedUrls().value(0);
2252 }
2253 return QUrl();
2254}
2255
2256/*!
2257 This is a convenience static function that will return one or more existing
2258 files selected by the user.
2259
2260 \snippet code/src_gui_dialogs_qfiledialog.cpp 9
2261
2262 This function creates a modal file dialog with the given \a parent widget.
2263 If \a parent is not \nullptr, the dialog will be shown centered over the
2264 parent widget.
2265
2266 The file dialog's working directory will be set to \a dir. If \a dir
2267 includes a file name, the file will be selected. The filter is set to
2268 \a filter so that only those files which match the filter are shown. The
2269 filter selected is set to \a selectedFilter. The parameters \a dir,
2270 \a selectedFilter and \a filter may be empty strings. If you need multiple
2271 filters, separate them with ';;', for instance:
2272
2273 \snippet code/src_gui_dialogs_qfiledialog.cpp 14
2274
2275 The dialog's caption is set to \a caption. If \a caption is not specified
2276 then a default caption will be used.
2277
2278 On Windows, and \macos, this static function will use the
2279 native file dialog and not a QFileDialog.
2280
2281 On Windows the dialog will spin a blocking modal event loop that will not
2282 dispatch any QTimers, and if \a parent is not \nullptr then it will position
2283 the dialog just below the parent's title bar.
2284
2285 On Unix/X11, the normal behavior of the file dialog is to resolve and
2286 follow symlinks. For example, if \c{/usr/tmp} is a symlink to \c{/var/tmp},
2287 the file dialog will change to \c{/var/tmp} after entering \c{/usr/tmp}.
2288 The \a options argument holds various options about how to run the dialog,
2289 see the QFileDialog::Option enum for more information on the flags you can
2290 pass.
2291
2292 \warning Do not delete \a parent during the execution of the dialog. If you
2293 want to do this, you should create the dialog yourself using one of the
2294 QFileDialog constructors.
2295
2296 \sa getOpenFileName(), getSaveFileName(), getExistingDirectory()
2297*/
2298QStringList QFileDialog::getOpenFileNames(QWidget *parent,
2299 const QString &caption,
2300 const QString &dir,
2301 const QString &filter,
2302 QString *selectedFilter,
2303 Options options)
2304{
2305 const QStringList schemes = QStringList(QStringLiteral("file"));
2306 const QList<QUrl> selectedUrls = getOpenFileUrls(parent, caption, QUrl::fromLocalFile(dir), filter, selectedFilter, options, schemes);
2307 QStringList fileNames;
2308 fileNames.reserve(selectedUrls.size());
2309 for (const QUrl &url : selectedUrls)
2310 fileNames << url.toLocalFile();
2311 return fileNames;
2312}
2313
2314/*!
2315 This is a convenience static function that will return one or more existing
2316 files selected by the user. If the user presses Cancel, it returns an
2317 empty list.
2318
2319 The function is used similarly to QFileDialog::getOpenFileNames(). In
2320 particular \a parent, \a caption, \a dir, \a filter, \a selectedFilter
2321 and \a options are used in the exact same way.
2322
2323 The main difference with QFileDialog::getOpenFileNames() comes from
2324 the ability offered to the user to select remote files. That's why
2325 the return type and the type of \a dir are respectively QList<QUrl>
2326 and QUrl.
2327
2328 The \a supportedSchemes argument allows to restrict the type of URLs the
2329 user will be able to select. It is a way for the application to declare
2330 the protocols it will support to fetch the file content. An empty list
2331 means that no restriction is applied (the default).
2332 Supported for local files ("file" scheme) is implicit and always enabled;
2333 it is not necessary to include it in the restriction.
2334
2335 When possible, this static function will use the native file dialog and
2336 not a QFileDialog. On platforms which don't support selecting remote
2337 files, Qt will allow to select only local files.
2338
2339 \sa getOpenFileNames(), getOpenFileUrl(), getSaveFileUrl(), getExistingDirectoryUrl()
2340 \since 5.2
2341*/
2342QList<QUrl> QFileDialog::getOpenFileUrls(QWidget *parent,
2343 const QString &caption,
2344 const QUrl &dir,
2345 const QString &filter,
2346 QString *selectedFilter,
2347 Options options,
2348 const QStringList &supportedSchemes)
2349{
2350 QFileDialogArgs args;
2351 args.parent = parent;
2352 args.caption = caption;
2353 args.directory = QFileDialogPrivate::workingDirectory(dir);
2354 args.selection = QFileDialogPrivate::initialSelection(dir);
2355 args.filter = filter;
2356 args.mode = ExistingFiles;
2357 args.options = options;
2358
2359 QFileDialog dialog(args);
2360 dialog.setSupportedSchemes(supportedSchemes);
2361 if (selectedFilter && !selectedFilter->isEmpty())
2362 dialog.selectNameFilter(*selectedFilter);
2363 if (dialog.exec() == QDialog::Accepted) {
2364 if (selectedFilter)
2365 *selectedFilter = dialog.selectedNameFilter();
2366 return dialog.selectedUrls();
2367 }
2368 return QList<QUrl>();
2369}
2370
2371/*!
2372 This is a convenience static function that will return the content of a file
2373 selected by the user.
2374
2375 This function is used to access local files on Qt for WebAssembly, where the web
2376 sandbox places restrictions on how such access may happen. Its implementation will
2377 make the browser display a native file dialog, where the user makes the file selection.
2378
2379 It can also be used on other platforms, where it will fall back to using QFileDialog.
2380
2381 The function is asynchronous and returns immediately. The \a fileOpenCompleted
2382 callback will be called when a file has been selected and its contents has been
2383 read into memory.
2384
2385 \snippet code/src_gui_dialogs_qfiledialog.cpp 14
2386 \since 5.13
2387*/
2388void QFileDialog::getOpenFileContent(const QString &nameFilter, const std::function<void(const QString &, const QByteArray &)> &fileOpenCompleted)
2389{
2390#ifdef Q_OS_WASM
2391 auto openFileImpl = std::make_shared<std::function<void(void)>>();
2392 QString fileName;
2393 QByteArray fileContent;
2394 *openFileImpl = [=]() mutable {
2395 auto fileDialogClosed = [&](bool fileSelected) {
2396 if (!fileSelected) {
2397 fileOpenCompleted(fileName, fileContent);
2398 openFileImpl.reset();
2399 }
2400 };
2401 auto acceptFile = [&](uint64_t size, const std::string name) -> char * {
2402 const uint64_t twoGB = 1ULL << 31; // QByteArray limit
2403 if (size > twoGB)
2404 return nullptr;
2405
2406 fileName = QString::fromStdString(name);
2407 fileContent.resize(size);
2408 return fileContent.data();
2409 };
2410 auto fileContentReady = [&]() mutable {
2411 fileOpenCompleted(fileName, fileContent);
2412 openFileImpl.reset();
2413 };
2414
2415 auto qtFilterStringToWebAcceptString = [](const QString &qtString) {
2416 // The Qt and Web name filter string formats are similar, but
2417 // not identical.
2418 return qtString.toStdString(); // ### TODO
2419 };
2420
2421 QWasmLocalFileAccess::openFile(qtFilterStringToWebAcceptString(nameFilter), fileDialogClosed, acceptFile, fileContentReady);
2422 };
2423
2424 (*openFileImpl)();
2425#else
2426 QFileDialog *dialog = new QFileDialog();
2427 dialog->selectNameFilter(nameFilter);
2428
2429 auto fileSelected = [=](const QString &fileName) {
2430 QByteArray fileContent;
2431 if (!fileName.isNull()) {
2432 QFile selectedFile(fileName);
2433 selectedFile.open(QIODevice::ReadOnly);
2434 fileContent = selectedFile.readAll();
2435 }
2436 fileOpenCompleted(fileName, fileContent);
2437 };
2438
2439 auto dialogClosed = [=](int code) {
2440 Q_UNUSED(code);
2441 delete dialog;
2442 };
2443
2444 connect(dialog, &QFileDialog::fileSelected, fileSelected);
2445 connect(dialog, &QFileDialog::finished, dialogClosed);
2446 dialog->show();
2447#endif
2448}
2449
2450/*!
2451 This is a convenience static function that will return a file name selected
2452 by the user. The file does not have to exist.
2453
2454 It creates a modal file dialog with the given \a parent widget. If
2455 \a parent is not \nullptr, the dialog will be shown centered over the
2456 parent widget.
2457
2458 \snippet code/src_gui_dialogs_qfiledialog.cpp 11
2459
2460 The file dialog's working directory will be set to \a dir. If \a dir
2461 includes a file name, the file will be selected. Only files that match the
2462 \a filter are shown. The filter selected is set to \a selectedFilter. The
2463 parameters \a dir, \a selectedFilter, and \a filter may be empty strings.
2464 Multiple filters are separated with ';;'. For instance:
2465
2466 \snippet code/src_gui_dialogs_qfiledialog.cpp 14
2467
2468 The \a options argument holds various options about how to run the dialog,
2469 see the QFileDialog::Option enum for more information on the flags you can
2470 pass.
2471
2472 The default filter can be chosen by setting \a selectedFilter to the
2473 desired value.
2474
2475 The dialog's caption is set to \a caption. If \a caption is not specified,
2476 a default caption will be used.
2477
2478 On Windows, and \macos, this static function will use the
2479 native file dialog and not a QFileDialog.
2480
2481 On Windows the dialog will spin a blocking modal event loop that will not
2482 dispatch any QTimers, and if \a parent is not \nullptr then it will
2483 position the dialog just below the parent's title bar. On \macos, with its
2484 native file dialog, the filter argument is ignored.
2485
2486 On Unix/X11, the normal behavior of the file dialog is to resolve and
2487 follow symlinks. For example, if \c{/usr/tmp} is a symlink to \c{/var/tmp},
2488 the file dialog will change to \c{/var/tmp} after entering \c{/usr/tmp}. If
2489 \a options includes DontResolveSymlinks the file dialog will treat symlinks
2490 as regular directories.
2491
2492 \warning Do not delete \a parent during the execution of the dialog. If you
2493 want to do this, you should create the dialog yourself using one of the
2494 QFileDialog constructors.
2495
2496 \sa getOpenFileName(), getOpenFileNames(), getExistingDirectory()
2497*/
2498QString QFileDialog::getSaveFileName(QWidget *parent,
2499 const QString &caption,
2500 const QString &dir,
2501 const QString &filter,
2502 QString *selectedFilter,
2503 Options options)
2504{
2505 const QStringList schemes = QStringList(QStringLiteral("file"));
2506 const QUrl selectedUrl = getSaveFileUrl(parent, caption, QUrl::fromLocalFile(dir), filter, selectedFilter, options, schemes);
2507 return selectedUrl.toLocalFile();
2508}
2509
2510/*!
2511 This is a convenience static function that returns a file selected by
2512 the user. The file does not have to exist. If the user presses Cancel,
2513 it returns an empty url.
2514
2515 The function is used similarly to QFileDialog::getSaveFileName(). In
2516 particular \a parent, \a caption, \a dir, \a filter, \a selectedFilter
2517 and \a options are used in the exact same way.
2518
2519 The main difference with QFileDialog::getSaveFileName() comes from
2520 the ability offered to the user to select a remote file. That's why
2521 the return type and the type of \a dir is QUrl.
2522
2523 The \a supportedSchemes argument allows to restrict the type of URLs the
2524 user will be able to select. It is a way for the application to declare
2525 the protocols it will support to save the file content. An empty list
2526 means that no restriction is applied (the default).
2527 Supported for local files ("file" scheme) is implicit and always enabled;
2528 it is not necessary to include it in the restriction.
2529
2530 When possible, this static function will use the native file dialog and
2531 not a QFileDialog. On platforms which don't support selecting remote
2532 files, Qt will allow to select only local files.
2533
2534 \sa getSaveFileName(), getOpenFileUrl(), getOpenFileUrls(), getExistingDirectoryUrl()
2535 \since 5.2
2536*/
2537QUrl QFileDialog::getSaveFileUrl(QWidget *parent,
2538 const QString &caption,
2539 const QUrl &dir,
2540 const QString &filter,
2541 QString *selectedFilter,
2542 Options options,
2543 const QStringList &supportedSchemes)
2544{
2545 QFileDialogArgs args;
2546 args.parent = parent;
2547 args.caption = caption;
2548 args.directory = QFileDialogPrivate::workingDirectory(dir);
2549 args.selection = QFileDialogPrivate::initialSelection(dir);
2550 args.filter = filter;
2551 args.mode = AnyFile;
2552 args.options = options;
2553
2554 QFileDialog dialog(args);
2555 dialog.setSupportedSchemes(supportedSchemes);
2556 dialog.setAcceptMode(AcceptSave);
2557 if (selectedFilter && !selectedFilter->isEmpty())
2558 dialog.selectNameFilter(*selectedFilter);
2559 if (dialog.exec() == QDialog::Accepted) {
2560 if (selectedFilter)
2561 *selectedFilter = dialog.selectedNameFilter();
2562 return dialog.selectedUrls().value(0);
2563 }
2564 return QUrl();
2565}
2566
2567/*!
2568 This is a convenience static function that will return an existing
2569 directory selected by the user.
2570
2571 \snippet code/src_gui_dialogs_qfiledialog.cpp 12
2572
2573 This function creates a modal file dialog with the given \a parent widget.
2574 If \a parent is not \nullptr, the dialog will be shown centered over the
2575 parent widget.
2576
2577 The dialog's working directory is set to \a dir, and the caption is set to
2578 \a caption. Either of these may be an empty string in which case the
2579 current directory and a default caption will be used respectively.
2580
2581 The \a options argument holds various options about how to run the dialog,
2582 see the QFileDialog::Option enum for more information on the flags you can
2583 pass. To ensure a native file dialog, \l{QFileDialog::}{ShowDirsOnly} must
2584 be set.
2585
2586 On Windows and \macos, this static function will use the
2587 native file dialog and not a QFileDialog. However, the native Windows file
2588 dialog does not support displaying files in the directory chooser. You need
2589 to pass \l{QFileDialog::}{DontUseNativeDialog} to display files using a
2590 QFileDialog.
2591
2592 On Unix/X11, the normal behavior of the file dialog is to resolve and
2593 follow symlinks. For example, if \c{/usr/tmp} is a symlink to \c{/var/tmp},
2594 the file dialog will change to \c{/var/tmp} after entering \c{/usr/tmp}. If
2595 \a options includes DontResolveSymlinks, the file dialog will treat
2596 symlinks as regular directories.
2597
2598 On Windows, the dialog will spin a blocking modal event loop that will not
2599 dispatch any QTimers, and if \a parent is not \nullptr then it will position
2600 the dialog just below the parent's title bar.
2601
2602 \warning Do not delete \a parent during the execution of the dialog. If you
2603 want to do this, you should create the dialog yourself using one of the
2604 QFileDialog constructors.
2605
2606 \sa getOpenFileName(), getOpenFileNames(), getSaveFileName()
2607*/
2608QString QFileDialog::getExistingDirectory(QWidget *parent,
2609 const QString &caption,
2610 const QString &dir,
2611 Options options)
2612{
2613 const QStringList schemes = QStringList(QStringLiteral("file"));
2614 const QUrl selectedUrl = getExistingDirectoryUrl(parent, caption, QUrl::fromLocalFile(dir), options, schemes);
2615 return selectedUrl.toLocalFile();
2616}
2617
2618/*!
2619 This is a convenience static function that will return an existing
2620 directory selected by the user. If the user presses Cancel, it
2621 returns an empty url.
2622
2623 The function is used similarly to QFileDialog::getExistingDirectory().
2624 In particular \a parent, \a caption, \a dir and \a options are used
2625 in the exact same way.
2626
2627 The main difference with QFileDialog::getExistingDirectory() comes from
2628 the ability offered to the user to select a remote directory. That's why
2629 the return type and the type of \a dir is QUrl.
2630
2631 The \a supportedSchemes argument allows to restrict the type of URLs the
2632 user will be able to select. It is a way for the application to declare
2633 the protocols it will support to fetch the file content. An empty list
2634 means that no restriction is applied (the default).
2635 Supported for local files ("file" scheme) is implicit and always enabled;
2636 it is not necessary to include it in the restriction.
2637
2638 When possible, this static function will use the native file dialog and
2639 not a QFileDialog. On platforms which don't support selecting remote
2640 files, Qt will allow to select only local files.
2641
2642 \sa getExistingDirectory(), getOpenFileUrl(), getOpenFileUrls(), getSaveFileUrl()
2643 \since 5.2
2644*/
2645QUrl QFileDialog::getExistingDirectoryUrl(QWidget *parent,
2646 const QString &caption,
2647 const QUrl &dir,
2648 Options options,
2649 const QStringList &supportedSchemes)
2650{
2651 QFileDialogArgs args;
2652 args.parent = parent;
2653 args.caption = caption;
2654 args.directory = QFileDialogPrivate::workingDirectory(dir);
2655QT_WARNING_PUSH
2656QT_WARNING_DISABLE_DEPRECATED
2657 args.mode = (options & ShowDirsOnly ? DirectoryOnly : Directory);
2658QT_WARNING_POP
2659 args.options = options;
2660
2661 QFileDialog dialog(args);
2662 dialog.setSupportedSchemes(supportedSchemes);
2663 if (dialog.exec() == QDialog::Accepted)
2664 return dialog.selectedUrls().value(0);
2665 return QUrl();
2666}
2667
2668inline static QUrl _qt_get_directory(const QUrl &url)
2669{
2670 if (url.isLocalFile()) {
2671 QFileInfo info = QFileInfo(QDir::current(), url.toLocalFile());
2672 if (info.exists() && info.isDir())
2673 return QUrl::fromLocalFile(QDir::cleanPath(info.absoluteFilePath()));
2674 info.setFile(info.absolutePath());
2675 if (info.exists() && info.isDir())
2676 return QUrl::fromLocalFile(info.absoluteFilePath());
2677 return QUrl();
2678 } else {
2679 return url;
2680 }
2681}
2682/*
2683 Get the initial directory URL
2684
2685 \sa initialSelection()
2686 */
2687QUrl QFileDialogPrivate::workingDirectory(const QUrl &url)
2688{
2689 if (!url.isEmpty()) {
2690 QUrl directory = _qt_get_directory(url);
2691 if (!directory.isEmpty())
2692 return directory;
2693 }
2694 QUrl directory = _qt_get_directory(*lastVisitedDir());
2695 if (!directory.isEmpty())
2696 return directory;
2697 return QUrl::fromLocalFile(QDir::currentPath());
2698}
2699
2700/*
2701 Get the initial selection given a path. The initial directory
2702 can contain both the initial directory and initial selection
2703 /home/user/foo.txt
2704
2705 \sa workingDirectory()
2706 */
2707QString QFileDialogPrivate::initialSelection(const QUrl &url)
2708{
2709 if (url.isEmpty())
2710 return QString();
2711 if (url.isLocalFile()) {
2712 QFileInfo info(url.toLocalFile());
2713 if (!info.isDir())
2714 return info.fileName();
2715 else
2716 return QString();
2717 }
2718 // With remote URLs we can only assume.
2719 return url.fileName();
2720}
2721
2722/*!
2723 \reimp
2724*/
2725void QFileDialog::done(int result)
2726{
2727 Q_D(QFileDialog);
2728
2729 QDialog::done(result);
2730
2731 if (d->receiverToDisconnectOnClose) {
2732 disconnect(this, d->signalToDisconnectOnClose,
2733 d->receiverToDisconnectOnClose, d->memberToDisconnectOnClose);
2734 d->receiverToDisconnectOnClose = 0;
2735 }
2736 d->memberToDisconnectOnClose.clear();
2737 d->signalToDisconnectOnClose.clear();
2738}
2739
2740/*!
2741 \reimp
2742*/
2743void QFileDialog::accept()
2744{
2745 Q_D(QFileDialog);
2746 if (!d->usingWidgets()) {
2747 const QList<QUrl> urls = selectedUrls();
2748 if (urls.isEmpty())
2749 return;
2750 d->_q_emitUrlsSelected(urls);
2751 if (urls.count() == 1)
2752 d->_q_emitUrlSelected(urls.first());
2753 QDialog::accept();
2754 return;
2755 }
2756
2757 const QStringList files = selectedFiles();
2758 if (files.isEmpty())
2759 return;
2760 QString lineEditText = d->lineEdit()->text();
2761 // "hidden feature" type .. and then enter, and it will move up a dir
2762 // special case for ".."
2763 if (lineEditText == QLatin1String("..")) {
2764 d->_q_navigateToParent();
2765 const QSignalBlocker blocker(d->qFileDialogUi->fileNameEdit);
2766 d->lineEdit()->selectAll();
2767 return;
2768 }
2769
2770 switch (fileMode()) {
2771QT_WARNING_PUSH
2772QT_WARNING_DISABLE_DEPRECATED
2773 case DirectoryOnly:
2774QT_WARNING_POP
2775 case Directory: {
2776 QString fn = files.first();
2777 QFileInfo info(fn);
2778 if (!info.exists())
2779 info = QFileInfo(d->getEnvironmentVariable(fn));
2780 if (!info.exists()) {
2781#if QT_CONFIG(messagebox)
2782 QString message = tr("%1\nDirectory not found.\nPlease verify the "
2783 "correct directory name was given.");
2784 QMessageBox::warning(this, windowTitle(), message.arg(info.fileName()));
2785#endif // QT_CONFIG(messagebox)
2786 return;
2787 }
2788 if (info.isDir()) {
2789 d->emitFilesSelected(files);
2790 QDialog::accept();
2791 }
2792 return;
2793 }
2794
2795 case AnyFile: {
2796 QString fn = files.first();
2797 QFileInfo info(fn);
2798 if (info.isDir()) {
2799 setDirectory(info.absoluteFilePath());
2800 return;
2801 }
2802
2803 if (!info.exists()) {
2804 int maxNameLength = d->maxNameLength(info.path());
2805 if (maxNameLength >= 0 && info.fileName().length() > maxNameLength)
2806 return;
2807 }
2808
2809 // check if we have to ask for permission to overwrite the file
2810 if (!info.exists() || testOption(DontConfirmOverwrite) || acceptMode() == AcceptOpen) {
2811 d->emitFilesSelected(QStringList(fn));
2812 QDialog::accept();
2813#if QT_CONFIG(messagebox)
2814 } else {
2815 if (QMessageBox::warning(this, windowTitle(),
2816 tr("%1 already exists.\nDo you want to replace it?")
2817 .arg(info.fileName()),
2818 QMessageBox::Yes | QMessageBox::No, QMessageBox::No)
2819 == QMessageBox::Yes) {
2820 d->emitFilesSelected(QStringList(fn));
2821 QDialog::accept();
2822 }
2823#endif
2824 }
2825 return;
2826 }
2827
2828 case ExistingFile:
2829 case ExistingFiles:
2830 for (const auto &file : files) {
2831 QFileInfo info(file);
2832 if (!info.exists())
2833 info = QFileInfo(d->getEnvironmentVariable(file));
2834 if (!info.exists()) {
2835#if QT_CONFIG(messagebox)
2836 QString message = tr("%1\nFile not found.\nPlease verify the "
2837 "correct file name was given.");
2838 QMessageBox::warning(this, windowTitle(), message.arg(info.fileName()));
2839#endif // QT_CONFIG(messagebox)
2840 return;
2841 }
2842 if (info.isDir()) {
2843 setDirectory(info.absoluteFilePath());
2844 d->lineEdit()->clear();
2845 return;
2846 }
2847 }
2848 d->emitFilesSelected(files);
2849 QDialog::accept();
2850 return;
2851 }
2852}
2853
2854#if QT_CONFIG(settings)
2855void QFileDialogPrivate::saveSettings()
2856{
2857 Q_Q(QFileDialog);
2858 QSettings settings(QSettings::UserScope, QLatin1String("QtProject"));
2859 settings.beginGroup(QLatin1String("FileDialog"));
2860
2861 if (usingWidgets()) {
2862 settings.setValue(QLatin1String("sidebarWidth"), qFileDialogUi->splitter->sizes().constFirst());
2863 settings.setValue(QLatin1String("shortcuts"), QUrl::toStringList(qFileDialogUi->sidebar->urls()));
2864 settings.setValue(QLatin1String("treeViewHeader"), qFileDialogUi->treeView->header()->saveState());
2865 }
2866 QStringList historyUrls;
2867 const QStringList history = q->history();
2868 historyUrls.reserve(history.size());
2869 for (const QString &path : history)
2870 historyUrls << QUrl::fromLocalFile(path).toString();
2871 settings.setValue(QLatin1String("history"), historyUrls);
2872 settings.setValue(QLatin1String("lastVisited"), lastVisitedDir()->toString());
2873 const QMetaEnum &viewModeMeta = q->metaObject()->enumerator(q->metaObject()->indexOfEnumerator("ViewMode"));
2874 settings.setValue(QLatin1String("viewMode"), QLatin1String(viewModeMeta.key(q->viewMode())));
2875 settings.setValue(QLatin1String("qtVersion"), QLatin1String(QT_VERSION_STR));
2876}
2877
2878bool QFileDialogPrivate::restoreFromSettings()
2879{
2880 Q_Q(QFileDialog);
2881 QSettings settings(QSettings::UserScope, QLatin1String("QtProject"));
2882 if (!settings.childGroups().contains(QLatin1String("FileDialog")))
2883 return false;
2884 settings.beginGroup(QLatin1String("FileDialog"));
2885
2886 q->setDirectoryUrl(lastVisitedDir()->isEmpty() ? settings.value(QLatin1String("lastVisited")).toUrl() : *lastVisitedDir());
2887
2888 QByteArray viewModeStr = settings.value(QLatin1String("viewMode")).toString().toLatin1();
2889 const QMetaEnum &viewModeMeta = q->metaObject()->enumerator(q->metaObject()->indexOfEnumerator("ViewMode"));
2890 bool ok = false;
2891 int viewMode = viewModeMeta.keyToValue(viewModeStr.constData(), &ok);
2892 if (!ok)
2893 viewMode = QFileDialog::List;
2894 q->setViewMode(static_cast<QFileDialog::ViewMode>(viewMode));
2895
2896 sidebarUrls = QUrl::fromStringList(settings.value(QLatin1String("shortcuts")).toStringList());
2897 headerData = settings.value(QLatin1String("treeViewHeader")).toByteArray();
2898
2899 if (!usingWidgets())
2900 return true;
2901
2902 QStringList history;
2903 const auto urlStrings = settings.value(QLatin1String("history")).toStringList();
2904 for (const QString &urlStr : urlStrings) {
2905 QUrl url(urlStr);
2906 if (url.isLocalFile())
2907 history << url.toLocalFile();
2908 }
2909
2910 return restoreWidgetState(history, settings.value(QLatin1String("sidebarWidth"), -1).toInt());
2911}
2912#endif // settings
2913
2914bool QFileDialogPrivate::restoreWidgetState(QStringList &history, int splitterPosition)
2915{
2916 Q_Q(QFileDialog);
2917 if (splitterPosition >= 0) {
2918 QList<int> splitterSizes;
2919 splitterSizes.append(splitterPosition);
2920 splitterSizes.append(qFileDialogUi->splitter->widget(1)->sizeHint().width());
2921 qFileDialogUi->splitter->setSizes(splitterSizes);
2922 } else {
2923 if (!qFileDialogUi->splitter->restoreState(splitterState))
2924 return false;
2925 QList<int> list = qFileDialogUi->splitter->sizes();
2926 if (list.count() >= 2 && (list.at(0) == 0 || list.at(1) == 0)) {
2927 for (int i = 0; i < list.count(); ++i)
2928 list[i] = qFileDialogUi->splitter->widget(i)->sizeHint().width();
2929 qFileDialogUi->splitter->setSizes(list);
2930 }
2931 }
2932
2933 qFileDialogUi->sidebar->setUrls(sidebarUrls);
2934
2935 static const int MaxHistorySize = 5;
2936 if (history.size() > MaxHistorySize)
2937 history.erase(history.begin(), history.end() - MaxHistorySize);
2938 q->setHistory(history);
2939
2940 QHeaderView *headerView = qFileDialogUi->treeView->header();
2941 if (!headerView->restoreState(headerData))
2942 return false;
2943
2944 QList<QAction*> actions = headerView->actions();
2945 QAbstractItemModel *abstractModel = model;
2946#if QT_CONFIG(proxymodel)
2947 if (proxyModel)
2948 abstractModel = proxyModel;
2949#endif
2950 int total = qMin(abstractModel->columnCount(QModelIndex()), actions.count() + 1);
2951 for (int i = 1; i < total; ++i)
2952 actions.at(i - 1)->setChecked(!headerView->isSectionHidden(i));
2953
2954 return true;
2955}
2956
2957/*!
2958 \internal
2959
2960 Create widgets, layout and set default values
2961*/
2962void QFileDialogPrivate::init(const QUrl &directory, const QString &nameFilter,
2963 const QString &caption)
2964{
2965 Q_Q(QFileDialog);
2966 if (!caption.isEmpty()) {
2967 useDefaultCaption = false;
2968 setWindowTitle = caption;
2969 q->setWindowTitle(caption);
2970 }
2971
2972 q->setAcceptMode(QFileDialog::AcceptOpen);
2973 nativeDialogInUse = platformFileDialogHelper() != 0;
2974 if (!nativeDialogInUse)
2975 createWidgets();
2976 q->setFileMode(QFileDialog::AnyFile);
2977 if (!nameFilter.isEmpty())
2978 q->setNameFilter(nameFilter);
2979 // QTBUG-70798, prevent the default blocking the restore logic.
2980 const bool dontStoreDir = !directory.isValid() && !lastVisitedDir()->isValid();
2981 q->setDirectoryUrl(workingDirectory(directory));
2982 if (dontStoreDir)
2983 lastVisitedDir()->clear();
2984 if (directory.isLocalFile())
2985 q->selectFile(initialSelection(directory));
2986 else
2987 q->selectUrl(directory);
2988
2989#if QT_CONFIG(settings)
2990 // Try to restore from the FileDialog settings group; if it fails, fall back
2991 // to the pre-5.5 QByteArray serialized settings.
2992 if (!restoreFromSettings()) {
2993 const QSettings settings(QSettings::UserScope, QLatin1String("QtProject"));
2994 q->restoreState(settings.value(QLatin1String("Qt/filedialog")).toByteArray());
2995 }
2996#endif
2997
2998#if defined(Q_EMBEDDED_SMALLSCREEN)
2999 qFileDialogUi->lookInLabel->setVisible(false);
3000 qFileDialogUi->fileNameLabel->setVisible(false);
3001 qFileDialogUi->fileTypeLabel->setVisible(false);
3002 qFileDialogUi->sidebar->hide();
3003#endif
3004
3005 const QSize sizeHint = q->sizeHint();
3006 if (sizeHint.isValid())
3007 q->resize(sizeHint);
3008}
3009
3010/*!
3011 \internal
3012
3013 Create the widgets, set properties and connections
3014*/
3015void QFileDialogPrivate::createWidgets()
3016{
3017 if (qFileDialogUi)
3018 return;
3019 Q_Q(QFileDialog);
3020
3021 // This function is sometimes called late (e.g as a fallback from setVisible). In that case we
3022 // need to ensure that the following UI code (setupUI in particular) doesn't reset any explicitly
3023 // set window state or geometry.
3024 QSize preSize = q->testAttribute(Qt::WA_Resized) ? q->size() : QSize();
3025 Qt::WindowStates preState = q->windowState();
3026
3027 model = new QFileSystemModel(q);
3028 model->setFilter(options->filter());
3029 model->setObjectName(QLatin1String("qt_filesystem_model"));
3030 if (QPlatformFileDialogHelper *helper = platformFileDialogHelper())
3031 model->setNameFilterDisables(helper->defaultNameFilterDisables());
3032 else
3033 model->setNameFilterDisables(false);
3034 if (nativeDialogInUse)
3035 deletePlatformHelper();
3036 model->d_func()->disableRecursiveSort = true;
3037 QFileDialog::connect(model, SIGNAL(fileRenamed(QString,QString,QString)), q, SLOT(_q_fileRenamed(QString,QString,QString)));
3038 QFileDialog::connect(model, SIGNAL(rootPathChanged(QString)),
3039 q, SLOT(_q_pathChanged(QString)));
3040 QFileDialog::connect(model, SIGNAL(rowsInserted(QModelIndex,int,int)),
3041 q, SLOT(_q_rowsInserted(QModelIndex)));
3042 model->setReadOnly(false);
3043
3044 qFileDialogUi.reset(new Ui_QFileDialog());
3045 qFileDialogUi->setupUi(q);
3046
3047 QList<QUrl> initialBookmarks;
3048 initialBookmarks << QUrl(QLatin1String("file:"))
3049 << QUrl::fromLocalFile(QDir::homePath());
3050 qFileDialogUi->sidebar->setModelAndUrls(model, initialBookmarks);
3051 QFileDialog::connect(qFileDialogUi->sidebar, SIGNAL(goToUrl(QUrl)),
3052 q, SLOT(_q_goToUrl(QUrl)));
3053
3054 QObject::connect(qFileDialogUi->buttonBox, SIGNAL(accepted()), q, SLOT(accept()));
3055 QObject::connect(qFileDialogUi->buttonBox, SIGNAL(rejected()), q, SLOT(reject()));
3056
3057 qFileDialogUi->lookInCombo->setFileDialogPrivate(this);
3058 QObject::connect(qFileDialogUi->lookInCombo, SIGNAL(activated(QString)), q, SLOT(_q_goToDirectory(QString)));
3059
3060 qFileDialogUi->lookInCombo->setInsertPolicy(QComboBox::NoInsert);
3061 qFileDialogUi->lookInCombo->setDuplicatesEnabled(false);
3062
3063 // filename
3064 qFileDialogUi->fileNameEdit->setFileDialogPrivate(this);
3065#ifndef QT_NO_SHORTCUT
3066 qFileDialogUi->fileNameLabel->setBuddy(qFileDialogUi->fileNameEdit);
3067#endif
3068#if QT_CONFIG(fscompleter)
3069 completer = new QFSCompleter(model, q);
3070 qFileDialogUi->fileNameEdit->setCompleter(completer);
3071#endif // QT_CONFIG(fscompleter)
3072
3073 qFileDialogUi->fileNameEdit->setInputMethodHints(Qt::ImhNoPredictiveText);
3074
3075 QObject::connect(qFileDialogUi->fileNameEdit, SIGNAL(textChanged(QString)),
3076 q, SLOT(_q_autoCompleteFileName(QString)));
3077 QObject::connect(qFileDialogUi->fileNameEdit, SIGNAL(textChanged(QString)),
3078 q, SLOT(_q_updateOkButton()));
3079
3080 QObject::connect(qFileDialogUi->fileNameEdit, SIGNAL(returnPressed()), q, SLOT(accept()));
3081
3082 // filetype
3083 qFileDialogUi->fileTypeCombo->setDuplicatesEnabled(false);
3084 qFileDialogUi->fileTypeCombo->setSizeAdjustPolicy(QComboBox::AdjustToMinimumContentsLength);
3085 qFileDialogUi->fileTypeCombo->