1// Copyright (C) 2020 The Qt Company Ltd.
2// SPDX-License-Identifier: LicenseRef-Qt-Commercial OR LGPL-3.0-only OR GPL-2.0-only OR GPL-3.0-only
3
4#define QT_NO_URL_CAST_FROM_STRING
5
6#include <qvariant.h>
7#include <private/qwidgetitemdata_p.h>
8#include "qfiledialog.h"
9
10#include "qfiledialog_p.h"
11#include <private/qapplication_p.h>
12#include <private/qguiapplication_p.h>
13#include <qfontmetrics.h>
14#include <qaction.h>
15#include <qactiongroup.h>
16#include <qheaderview.h>
17#if QT_CONFIG(shortcut)
18# include <qshortcut.h>
19#endif
20#include <qgridlayout.h>
21#if QT_CONFIG(menu)
22#include <qmenu.h>
23#endif
24#if QT_CONFIG(messagebox)
25#include <qmessagebox.h>
26#endif
27#include <stdlib.h>
28#if QT_CONFIG(settings)
29#include <qsettings.h>
30#endif
31#include <qdebug.h>
32#if QT_CONFIG(mimetype)
33#include <qmimedatabase.h>
34#endif
35#if QT_CONFIG(regularexpression)
36#include <qregularexpression.h>
37#endif
38#include <qapplication.h>
39#include <qstylepainter.h>
40#include "ui_qfiledialog.h"
41#if defined(Q_OS_UNIX)
42#include <pwd.h>
43#include <unistd.h> // for pathconf() on OS X
44#elif defined(Q_OS_WIN)
45# include <QtCore/qt_windows.h>
46#endif
47#if defined(Q_OS_WASM)
48#include <private/qwasmlocalfileaccess_p.h>
49#endif
50
51#include <algorithm>
52
53QT_BEGIN_NAMESPACE
54
55using namespace Qt::StringLiterals;
56
57Q_GLOBAL_STATIC(QUrl, lastVisitedDir)
58
59/*!
60 \class QFileDialog
61 \brief The QFileDialog class provides a dialog that allows users to select files or directories.
62 \ingroup standard-dialogs
63 \inmodule QtWidgets
64
65 The QFileDialog class enables a user to traverse the file system
66 to select one or many files or a directory.
67
68 \image qtquickdialogs-filedialog-gtk.png
69
70 The easiest way to create a QFileDialog is to use the static functions,
71 such as \l getOpenFileName().
72
73 \snippet code/src_gui_dialogs_qfiledialog.cpp 0
74
75 In the above example, a modal QFileDialog is created using a static
76 function. The dialog initially displays the contents of the "/home/jana"
77 directory, and displays files matching the patterns given in the
78 string "Image Files (*.png *.jpg *.bmp)". The parent of the file dialog
79 is set to \e this, and the window title is set to "Open Image".
80
81 If you want to use multiple filters, separate each one with
82 \e two semicolons. For example:
83
84 \snippet code/src_gui_dialogs_qfiledialog.cpp 1
85
86 You can create your own QFileDialog without using the static
87 functions. By calling setFileMode(), you can specify what the user must
88 select in the dialog:
89
90 \snippet code/src_gui_dialogs_qfiledialog.cpp 2
91
92 In the above example, the mode of the file dialog is set to
93 AnyFile, meaning that the user can select any file, or even specify a
94 file that doesn't exist. This mode is useful for creating a
95 "Save As" file dialog. Use ExistingFile if the user must select an
96 existing file, or \l Directory if only a directory can be selected.
97 See the \l QFileDialog::FileMode enum for the complete list of modes.
98
99 The fileMode property contains the mode of operation for the dialog;
100 this indicates what types of objects the user is expected to select.
101 Use setNameFilter() to set the dialog's file filter. For example:
102
103 \snippet code/src_gui_dialogs_qfiledialog.cpp 3
104
105 In the above example, the filter is set to \c{"Images (*.png *.xpm *.jpg)"}.
106 This means that only files with the extension \c png, \c xpm,
107 or \c jpg are shown in the QFileDialog. You can apply
108 several filters by using setNameFilters(). Use selectNameFilter() to select
109 one of the filters you've given as the file dialog's default filter.
110
111 The file dialog has two view modes: \l{QFileDialog::}{List} and
112 \l{QFileDialog::}{Detail}.
113 \l{QFileDialog::}{List} presents the contents of the current directory
114 as a list of file and directory names. \l{QFileDialog::}{Detail} also
115 displays a list of file and directory names, but provides additional
116 information alongside each name, such as the file size and modification
117 date. Set the mode with setViewMode():
118
119 \snippet code/src_gui_dialogs_qfiledialog.cpp 4
120
121 The last important function you need to use when creating your
122 own file dialog is selectedFiles().
123
124 \snippet code/src_gui_dialogs_qfiledialog.cpp 5
125
126 In the above example, a modal file dialog is created and shown. If
127 the user clicked OK, the file they selected is put in \c fileName.
128
129 The dialog's working directory can be set with setDirectory().
130 Each file in the current directory can be selected using
131 the selectFile() function.
132
133 The \l{dialogs/standarddialogs}{Standard Dialogs} example shows
134 how to use QFileDialog as well as other built-in Qt dialogs.
135
136 By default, a platform-native file dialog is used if the platform has
137 one. In that case, the widgets that would otherwise be used to construct the
138 dialog are not instantiated, so related accessors such as layout() and
139 itemDelegate() return null. Also, not all platforms show file dialogs
140 with a title bar, so be aware that the caption text might not be visible to
141 the user. You can set the \l DontUseNativeDialog option or set the
142 \l{Qt::AA_DontUseNativeDialogs}{AA_DontUseNativeDialogs} application attribute
143 to ensure that the widget-based implementation is used instead of the native dialog.
144
145 \sa QDir, QFileInfo, QFile, QColorDialog, QFontDialog, {Standard Dialogs Example}
146*/
147
148/*!
149 \enum QFileDialog::AcceptMode
150
151 \value AcceptOpen
152 \value AcceptSave
153*/
154
155/*!
156 \enum QFileDialog::ViewMode
157
158 This enum describes the view mode of the file dialog; that is, what
159 information about each file is displayed.
160
161 \value Detail Displays an icon, a name, and details for each item in
162 the directory.
163 \value List Displays only an icon and a name for each item in the
164 directory.
165
166 \sa setViewMode()
167*/
168
169/*!
170 \enum QFileDialog::FileMode
171
172 This enum is used to indicate what the user may select in the file
173 dialog; that is, what the dialog returns if the user clicks OK.
174
175 \value AnyFile The name of a file, whether it exists or not.
176 \value ExistingFile The name of a single existing file.
177 \value Directory The name of a directory. Both files and
178 directories are displayed. However, the native Windows
179 file dialog does not support displaying files in the
180 directory chooser.
181 \value ExistingFiles The names of zero or more existing files.
182
183 \sa setFileMode()
184*/
185
186/*!
187 \enum QFileDialog::Option
188
189 Options that influence the behavior of the dialog.
190
191 \value ShowDirsOnly Only show directories. By
192 default, both files and directories are shown.\br
193 This option is only effective in the \l Directory file mode.
194
195 \value DontResolveSymlinks Don't resolve symlinks.
196 By default, symlinks are resolved.
197
198 \value DontConfirmOverwrite Don't ask for confirmation if an
199 existing file is selected. By default, confirmation is requested.\br
200 This option is only effective if \l acceptMode is \l {QFileDialog::}{AcceptSave}).
201 It is furthermore not used on macOS for native file dialogs.
202
203 \value DontUseNativeDialog Don't use a platform-native file dialog,
204 but the widget-based one provided by Qt.\br
205 By default, a native file dialog is shown unless you use a subclass
206 of QFileDialog that contains the Q_OBJECT macro, the global
207 \l{Qt::}{AA_DontUseNativeDialogs} application attribute is set, or the platform
208 does not have a native dialog of the type that you require.\br
209 For the option to be effective, you must set it before changing
210 other properties of the dialog, or showing the dialog.
211
212 \value ReadOnly Indicates that the model is read-only.
213
214 \value HideNameFilterDetails Indicates if the file name filter details are
215 hidden or not.
216
217 \value DontUseCustomDirectoryIcons Always use the default directory icon.\br
218 Some platforms allow the user to set a different icon, but custom icon lookup
219 might cause significant performance issues over network or removable drives.\br
220 Setting this will enable the
221 \l{QAbstractFileIconProvider::}{DontUseCustomDirectoryIcons}
222 option in \l{iconProvider()}.\br
223 This enum value was added in Qt 5.2.
224
225 \sa options, testOption
226*/
227
228/*!
229 \enum QFileDialog::DialogLabel
230
231 \value LookIn
232 \value FileName
233 \value FileType
234 \value Accept
235 \value Reject
236*/
237
238/*!
239 \fn void QFileDialog::filesSelected(const QStringList &selected)
240
241 When the selection changes for local operations and the dialog is
242 accepted, this signal is emitted with the (possibly empty) list
243 of \a selected files.
244
245 \sa currentChanged(), QDialog::Accepted
246*/
247
248/*!
249 \fn void QFileDialog::urlsSelected(const QList<QUrl> &urls)
250
251 When the selection changes and the dialog is accepted, this signal is
252 emitted with the (possibly empty) list of selected \a urls.
253
254 \sa currentUrlChanged(), QDialog::Accepted
255 \since 5.2
256*/
257
258/*!
259 \fn void QFileDialog::fileSelected(const QString &file)
260
261 When the selection changes for local operations and the dialog is
262 accepted, this signal is emitted with the (possibly empty)
263 selected \a file.
264
265 \sa currentChanged(), QDialog::Accepted
266*/
267
268/*!
269 \fn void QFileDialog::urlSelected(const QUrl &url)
270
271 When the selection changes and the dialog is accepted, this signal is
272 emitted with the (possibly empty) selected \a url.
273
274 \sa currentUrlChanged(), QDialog::Accepted
275 \since 5.2
276*/
277
278/*!
279 \fn void QFileDialog::currentChanged(const QString &path)
280
281 When the current file changes for local operations, this signal is
282 emitted with the new file name as the \a path parameter.
283
284 \sa filesSelected()
285*/
286
287/*!
288 \fn void QFileDialog::currentUrlChanged(const QUrl &url)
289
290 When the current file changes, this signal is emitted with the
291 new file URL as the \a url parameter.
292
293 \sa urlsSelected()
294 \since 5.2
295*/
296
297/*!
298 \fn void QFileDialog::directoryEntered(const QString &directory)
299
300 This signal is emitted for local operations when the user enters
301 a \a directory.
302*/
303
304/*!
305 \fn void QFileDialog::directoryUrlEntered(const QUrl &directory)
306
307 This signal is emitted when the user enters a \a directory.
308
309 \since 5.2
310*/
311
312/*!
313 \fn void QFileDialog::filterSelected(const QString &filter)
314
315 This signal is emitted when the user selects a \a filter.
316*/
317
318QT_BEGIN_INCLUDE_NAMESPACE
319#include <QMetaEnum>
320#if QT_CONFIG(shortcut)
321# include <qshortcut.h>
322#endif
323QT_END_INCLUDE_NAMESPACE
324
325/*!
326 \fn QFileDialog::QFileDialog(QWidget *parent, Qt::WindowFlags flags)
327
328 Constructs a file dialog with the given \a parent and widget \a flags.
329*/
330QFileDialog::QFileDialog(QWidget *parent, Qt::WindowFlags f)
331 : QDialog(*new QFileDialogPrivate, parent, f)
332{
333 Q_D(QFileDialog);
334 QFileDialogArgs args;
335 d->init(args);
336}
337
338/*!
339 Constructs a file dialog with the given \a parent and \a caption that
340 initially displays the contents of the specified \a directory.
341 The contents of the directory are filtered before being shown in the
342 dialog, using a semicolon-separated list of filters specified by
343 \a filter.
344*/
345QFileDialog::QFileDialog(QWidget *parent,
346 const QString &caption,
347 const QString &directory,
348 const QString &filter)
349 : QDialog(*new QFileDialogPrivate, parent, { })
350{
351 Q_D(QFileDialog);
352 QFileDialogArgs args(QUrl::fromLocalFile(localfile: directory));
353 args.filter = filter;
354 args.caption = caption;
355 d->init(args);
356}
357
358/*!
359 \internal
360*/
361QFileDialog::QFileDialog(const QFileDialogArgs &args)
362 : QDialog(*new QFileDialogPrivate, args.parent, { })
363{
364 Q_D(QFileDialog);
365 d->init(args);
366 setFileMode(args.mode);
367 setOptions(args.options);
368 selectFile(filename: args.selection);
369}
370
371/*!
372 Destroys the file dialog.
373*/
374QFileDialog::~QFileDialog()
375{
376 Q_D(QFileDialog);
377#if QT_CONFIG(settings)
378 d->saveSettings();
379#endif
380 if (QPlatformFileDialogHelper *platformHelper = d->platformFileDialogHelper()) {
381 // QIOSFileDialog emits directoryChanged while hiding, causing an assert
382 // because of a partially destroyed QFileDialog.
383 QObjectPrivate::disconnect(sender: platformHelper, signal: &QPlatformFileDialogHelper::directoryEntered,
384 receiverPrivate: d, slot: &QFileDialogPrivate::nativeEnterDirectory);
385 }
386}
387
388/*!
389 Sets the \a urls that are located in the sidebar.
390
391 For instance:
392
393 \snippet filedialogurls/filedialogurls.cpp 0
394
395 Then the file dialog looks like this:
396
397 \image filedialogurls.png
398
399 \sa sidebarUrls()
400*/
401void QFileDialog::setSidebarUrls(const QList<QUrl> &urls)
402{
403 Q_D(QFileDialog);
404 if (!d->nativeDialogInUse)
405 d->qFileDialogUi->sidebar->setUrls(urls);
406}
407
408/*!
409 Returns a list of urls that are currently in the sidebar
410*/
411QList<QUrl> QFileDialog::sidebarUrls() const
412{
413 Q_D(const QFileDialog);
414 return (d->nativeDialogInUse ? QList<QUrl>() : d->qFileDialogUi->sidebar->urls());
415}
416
417static const qint32 QFileDialogMagic = 0xbe;
418
419/*!
420 Saves the state of the dialog's layout, history and current directory.
421
422 Typically this is used in conjunction with QSettings to remember the size
423 for a future session. A version number is stored as part of the data.
424*/
425QByteArray QFileDialog::saveState() const
426{
427 Q_D(const QFileDialog);
428 int version = 4;
429 QByteArray data;
430 QDataStream stream(&data, QIODevice::WriteOnly);
431 stream.setVersion(QDataStream::Qt_5_0);
432
433 stream << qint32(QFileDialogMagic);
434 stream << qint32(version);
435 if (d->usingWidgets()) {
436 stream << d->qFileDialogUi->splitter->saveState();
437 stream << d->qFileDialogUi->sidebar->urls();
438 } else {
439 stream << d->splitterState;
440 stream << d->sidebarUrls;
441 }
442 stream << history();
443 stream << *lastVisitedDir();
444 if (d->usingWidgets())
445 stream << d->qFileDialogUi->treeView->header()->saveState();
446 else
447 stream << d->headerData;
448 stream << qint32(viewMode());
449 return data;
450}
451
452/*!
453 Restores the dialogs's layout, history and current directory to the \a state specified.
454
455 Typically this is used in conjunction with QSettings to restore the size
456 from a past session.
457
458 Returns \c false if there are errors
459*/
460bool QFileDialog::restoreState(const QByteArray &state)
461{
462 Q_D(QFileDialog);
463 QByteArray sd = state;
464 QDataStream stream(&sd, QIODevice::ReadOnly);
465 stream.setVersion(QDataStream::Qt_5_0);
466 if (stream.atEnd())
467 return false;
468 QStringList history;
469 QUrl currentDirectory;
470 qint32 marker;
471 qint32 v;
472 qint32 viewMode;
473 stream >> marker;
474 stream >> v;
475 // the code below only supports versions 3 and 4
476 if (marker != QFileDialogMagic || (v != 3 && v != 4))
477 return false;
478
479 stream >> d->splitterState
480 >> d->sidebarUrls
481 >> history;
482 if (v == 3) {
483 QString currentDirectoryString;
484 stream >> currentDirectoryString;
485 currentDirectory = QUrl::fromLocalFile(localfile: currentDirectoryString);
486 } else {
487 stream >> currentDirectory;
488 }
489 stream >> d->headerData
490 >> viewMode;
491
492 setDirectoryUrl(lastVisitedDir()->isEmpty() ? currentDirectory : *lastVisitedDir());
493 setViewMode(static_cast<QFileDialog::ViewMode>(viewMode));
494
495 if (!d->usingWidgets())
496 return true;
497
498 return d->restoreWidgetState(history, splitterPosition: -1);
499}
500
501/*!
502 \reimp
503*/
504void QFileDialog::changeEvent(QEvent *e)
505{
506 Q_D(QFileDialog);
507 if (e->type() == QEvent::LanguageChange) {
508 d->retranslateWindowTitle();
509 d->retranslateStrings();
510 }
511 QDialog::changeEvent(e);
512}
513
514QFileDialogPrivate::QFileDialogPrivate()
515 :
516#if QT_CONFIG(proxymodel)
517 proxyModel(nullptr),
518#endif
519 model(nullptr),
520 currentHistoryLocation(-1),
521 renameAction(nullptr),
522 deleteAction(nullptr),
523 showHiddenAction(nullptr),
524 useDefaultCaption(true),
525 qFileDialogUi(nullptr),
526 options(QFileDialogOptions::create())
527{
528}
529
530QFileDialogPrivate::~QFileDialogPrivate()
531{
532}
533
534void QFileDialogPrivate::initHelper(QPlatformDialogHelper *h)
535{
536 Q_Q(QFileDialog);
537 auto *fileDialogHelper = static_cast<QPlatformFileDialogHelper *>(h);
538 QObjectPrivate::connect(sender: fileDialogHelper, signal: &QPlatformFileDialogHelper::fileSelected,
539 receiverPrivate: this, slot: &QFileDialogPrivate::emitUrlSelected);
540 QObjectPrivate::connect(sender: fileDialogHelper, signal: &QPlatformFileDialogHelper::filesSelected,
541 receiverPrivate: this, slot: &QFileDialogPrivate::emitUrlsSelected);
542 QObjectPrivate::connect(sender: fileDialogHelper, signal: &QPlatformFileDialogHelper::currentChanged,
543 receiverPrivate: this, slot: &QFileDialogPrivate::nativeCurrentChanged);
544 QObjectPrivate::connect(sender: fileDialogHelper, signal: &QPlatformFileDialogHelper::directoryEntered,
545 receiverPrivate: this, slot: &QFileDialogPrivate::nativeEnterDirectory);
546 QObject::connect(sender: fileDialogHelper, signal: &QPlatformFileDialogHelper::filterSelected,
547 context: q, slot: &QFileDialog::filterSelected);
548 fileDialogHelper->setOptions(options);
549}
550
551void QFileDialogPrivate::helperPrepareShow(QPlatformDialogHelper *)
552{
553 Q_Q(QFileDialog);
554 options->setWindowTitle(q->windowTitle());
555 options->setHistory(q->history());
556 if (usingWidgets())
557 options->setSidebarUrls(qFileDialogUi->sidebar->urls());
558 if (options->initiallySelectedNameFilter().isEmpty())
559 options->setInitiallySelectedNameFilter(q->selectedNameFilter());
560 if (options->initiallySelectedFiles().isEmpty())
561 options->setInitiallySelectedFiles(userSelectedFiles());
562}
563
564void QFileDialogPrivate::helperDone(QDialog::DialogCode code, QPlatformDialogHelper *)
565{
566 if (code == QDialog::Accepted) {
567 Q_Q(QFileDialog);
568 q->setViewMode(static_cast<QFileDialog::ViewMode>(options->viewMode()));
569 q->setSidebarUrls(options->sidebarUrls());
570 q->setHistory(options->history());
571 }
572}
573
574void QFileDialogPrivate::retranslateWindowTitle()
575{
576 Q_Q(QFileDialog);
577 if (!useDefaultCaption || setWindowTitle != q->windowTitle())
578 return;
579 if (q->acceptMode() == QFileDialog::AcceptOpen) {
580 const QFileDialog::FileMode fileMode = q->fileMode();
581 if (fileMode == QFileDialog::Directory)
582 q->setWindowTitle(QFileDialog::tr(s: "Find Directory"));
583 else
584 q->setWindowTitle(QFileDialog::tr(s: "Open"));
585 } else
586 q->setWindowTitle(QFileDialog::tr(s: "Save As"));
587
588 setWindowTitle = q->windowTitle();
589}
590
591void QFileDialogPrivate::setLastVisitedDirectory(const QUrl &dir)
592{
593 *lastVisitedDir() = dir;
594}
595
596void QFileDialogPrivate::updateLookInLabel()
597{
598 if (options->isLabelExplicitlySet(label: QFileDialogOptions::LookIn))
599 setLabelTextControl(label: QFileDialog::LookIn, text: options->labelText(label: QFileDialogOptions::LookIn));
600}
601
602void QFileDialogPrivate::updateFileNameLabel()
603{
604 if (options->isLabelExplicitlySet(label: QFileDialogOptions::FileName)) {
605 setLabelTextControl(label: QFileDialog::FileName, text: options->labelText(label: QFileDialogOptions::FileName));
606 } else {
607 switch (q_func()->fileMode()) {
608 case QFileDialog::Directory:
609 setLabelTextControl(label: QFileDialog::FileName, text: QFileDialog::tr(s: "Directory:"));
610 break;
611 default:
612 setLabelTextControl(label: QFileDialog::FileName, text: QFileDialog::tr(s: "File &name:"));
613 break;
614 }
615 }
616}
617
618void QFileDialogPrivate::updateFileTypeLabel()
619{
620 if (options->isLabelExplicitlySet(label: QFileDialogOptions::FileType))
621 setLabelTextControl(label: QFileDialog::FileType, text: options->labelText(label: QFileDialogOptions::FileType));
622}
623
624void QFileDialogPrivate::updateOkButtonText(bool saveAsOnFolder)
625{
626 Q_Q(QFileDialog);
627 // 'Save as' at a folder: Temporarily change to "Open".
628 if (saveAsOnFolder) {
629 setLabelTextControl(label: QFileDialog::Accept, text: QFileDialog::tr(s: "&Open"));
630 } else if (options->isLabelExplicitlySet(label: QFileDialogOptions::Accept)) {
631 setLabelTextControl(label: QFileDialog::Accept, text: options->labelText(label: QFileDialogOptions::Accept));
632 return;
633 } else {
634 switch (q->fileMode()) {
635 case QFileDialog::Directory:
636 setLabelTextControl(label: QFileDialog::Accept, text: QFileDialog::tr(s: "&Choose"));
637 break;
638 default:
639 setLabelTextControl(label: QFileDialog::Accept,
640 text: q->acceptMode() == QFileDialog::AcceptOpen ?
641 QFileDialog::tr(s: "&Open") :
642 QFileDialog::tr(s: "&Save"));
643 break;
644 }
645 }
646}
647
648void QFileDialogPrivate::updateCancelButtonText()
649{
650 if (options->isLabelExplicitlySet(label: QFileDialogOptions::Reject))
651 setLabelTextControl(label: QFileDialog::Reject, text: options->labelText(label: QFileDialogOptions::Reject));
652}
653
654void QFileDialogPrivate::retranslateStrings()
655{
656 Q_Q(QFileDialog);
657 /* WIDGETS */
658 if (options->useDefaultNameFilters())
659 q->setNameFilter(QFileDialogOptions::defaultNameFilterString());
660 if (!usingWidgets())
661 return;
662
663 QList<QAction*> actions = qFileDialogUi->treeView->header()->actions();
664 QAbstractItemModel *abstractModel = model;
665#if QT_CONFIG(proxymodel)
666 if (proxyModel)
667 abstractModel = proxyModel;
668#endif
669 const int total = qMin(a: abstractModel->columnCount(parent: QModelIndex()), b: int(actions.size() + 1));
670 for (int i = 1; i < total; ++i) {
671 actions.at(i: i - 1)->setText(QFileDialog::tr(s: "Show ") + abstractModel->headerData(section: i, orientation: Qt::Horizontal, role: Qt::DisplayRole).toString());
672 }
673
674 /* MENU ACTIONS */
675 renameAction->setText(QFileDialog::tr(s: "&Rename"));
676 deleteAction->setText(QFileDialog::tr(s: "&Delete"));
677 showHiddenAction->setText(QFileDialog::tr(s: "Show &hidden files"));
678 newFolderAction->setText(QFileDialog::tr(s: "&New Folder"));
679 qFileDialogUi->retranslateUi(q);
680 updateLookInLabel();
681 updateFileNameLabel();
682 updateFileTypeLabel();
683 updateCancelButtonText();
684}
685
686void QFileDialogPrivate::emitFilesSelected(const QStringList &files)
687{
688 Q_Q(QFileDialog);
689 emit q->filesSelected(files);
690 if (files.size() == 1)
691 emit q->fileSelected(file: files.first());
692}
693
694bool QFileDialogPrivate::canBeNativeDialog() const
695{
696 // Don't use Q_Q here! This function is called from ~QDialog,
697 // so Q_Q calling q_func() invokes undefined behavior (invalid cast in q_func()).
698 const QDialog * const q = static_cast<const QDialog*>(q_ptr);
699 if (nativeDialogInUse)
700 return true;
701 if (QCoreApplication::testAttribute(attribute: Qt::AA_DontUseNativeDialogs)
702 || q->testAttribute(attribute: Qt::WA_DontShowOnScreen)
703 || (options->options() & QFileDialog::DontUseNativeDialog)) {
704 return false;
705 }
706
707 return strcmp(s1: QFileDialog::staticMetaObject.className(), s2: q->metaObject()->className()) == 0;
708}
709
710bool QFileDialogPrivate::usingWidgets() const
711{
712 return !nativeDialogInUse && qFileDialogUi;
713}
714
715/*!
716 Sets the given \a option to be enabled if \a on is true; otherwise,
717 clears the given \a option.
718
719 Options (particularly the \l DontUseNativeDialog option) should be set
720 before changing dialog properties or showing the dialog.
721
722 Setting options while the dialog is visible is not guaranteed to have
723 an immediate effect on the dialog (depending on the option and on the
724 platform).
725
726 Setting options after changing other properties may cause these
727 values to have no effect.
728
729 \sa options, testOption()
730*/
731void QFileDialog::setOption(Option option, bool on)
732{
733 const QFileDialog::Options previousOptions = options();
734 if (!(previousOptions & option) != !on)
735 setOptions(previousOptions ^ option);
736}
737
738/*!
739 Returns \c true if the given \a option is enabled; otherwise, returns
740 false.
741
742 \sa options, setOption()
743*/
744bool QFileDialog::testOption(Option option) const
745{
746 Q_D(const QFileDialog);
747 return d->options->testOption(option: static_cast<QFileDialogOptions::FileDialogOption>(option));
748}
749
750/*!
751 \property QFileDialog::options
752 \brief The various options that affect the look and feel of the dialog.
753
754 By default, all options are disabled.
755
756 Options (particularly the \l DontUseNativeDialog option) should be set
757 before changing dialog properties or showing the dialog.
758
759 Setting options while the dialog is visible is not guaranteed to have
760 an immediate effect on the dialog (depending on the option and on the
761 platform).
762
763 Setting options after changing other properties may cause these
764 values to have no effect.
765
766 \sa setOption(), testOption()
767*/
768void QFileDialog::setOptions(Options options)
769{
770 Q_D(QFileDialog);
771
772 Options changed = (options ^ QFileDialog::options());
773 if (!changed)
774 return;
775
776 d->options->setOptions(QFileDialogOptions::FileDialogOptions(int(options)));
777
778 if (options & DontUseNativeDialog) {
779 d->nativeDialogInUse = false;
780 d->createWidgets();
781 }
782
783 if (d->usingWidgets()) {
784 if (changed & DontResolveSymlinks)
785 d->model->setResolveSymlinks(!(options & DontResolveSymlinks));
786 if (changed & ReadOnly) {
787 bool ro = (options & ReadOnly);
788 d->model->setReadOnly(ro);
789 d->qFileDialogUi->newFolderButton->setEnabled(!ro);
790 d->renameAction->setEnabled(!ro);
791 d->deleteAction->setEnabled(!ro);
792 }
793
794 if (changed & DontUseCustomDirectoryIcons) {
795 QFileIconProvider::Options providerOptions = iconProvider()->options();
796 providerOptions.setFlag(flag: QFileIconProvider::DontUseCustomDirectoryIcons,
797 on: options & DontUseCustomDirectoryIcons);
798 iconProvider()->setOptions(providerOptions);
799 }
800 }
801
802 if (changed & HideNameFilterDetails)
803 setNameFilters(d->options->nameFilters());
804
805 if (changed & ShowDirsOnly)
806 setFilter((options & ShowDirsOnly) ? filter() & ~QDir::Files : filter() | QDir::Files);
807}
808
809QFileDialog::Options QFileDialog::options() const
810{
811 Q_D(const QFileDialog);
812 static_assert((int)QFileDialog::ShowDirsOnly == (int)QFileDialogOptions::ShowDirsOnly);
813 static_assert((int)QFileDialog::DontResolveSymlinks == (int)QFileDialogOptions::DontResolveSymlinks);
814 static_assert((int)QFileDialog::DontConfirmOverwrite == (int)QFileDialogOptions::DontConfirmOverwrite);
815 static_assert((int)QFileDialog::DontUseNativeDialog == (int)QFileDialogOptions::DontUseNativeDialog);
816 static_assert((int)QFileDialog::ReadOnly == (int)QFileDialogOptions::ReadOnly);
817 static_assert((int)QFileDialog::HideNameFilterDetails == (int)QFileDialogOptions::HideNameFilterDetails);
818 static_assert((int)QFileDialog::DontUseCustomDirectoryIcons == (int)QFileDialogOptions::DontUseCustomDirectoryIcons);
819 return QFileDialog::Options(int(d->options->options()));
820}
821
822/*!
823 This function shows the dialog, and connects the slot specified by \a receiver
824 and \a member to the signal that informs about selection changes. If the fileMode is
825 ExistingFiles, this is the filesSelected() signal, otherwise it is the fileSelected() signal.
826
827 The signal is disconnected from the slot when the dialog is closed.
828*/
829void QFileDialog::open(QObject *receiver, const char *member)
830{
831 Q_D(QFileDialog);
832 const char *signal = (fileMode() == ExistingFiles) ? SIGNAL(filesSelected(QStringList))
833 : SIGNAL(fileSelected(QString));
834 connect(sender: this, signal, receiver, member);
835 d->signalToDisconnectOnClose = signal;
836 d->receiverToDisconnectOnClose = receiver;
837 d->memberToDisconnectOnClose = member;
838
839 QDialog::open();
840}
841
842
843/*!
844 \reimp
845*/
846void QFileDialog::setVisible(bool visible)
847{
848 // will call QFileDialogPrivate::setVisible override
849 QDialog::setVisible(visible);
850}
851
852/*!
853 \internal
854
855 The logic has to live here so that the call to hide() in ~QDialog calls
856 this function; it wouldn't call an override of QDialog::setVisible().
857*/
858void QFileDialogPrivate::setVisible(bool visible)
859{
860 Q_Q(QFileDialog);
861
862 if (canBeNativeDialog()){
863 if (setNativeDialogVisible(visible)){
864 // Set WA_DontShowOnScreen so that QDialogPrivate::setVisible(visible) below
865 // updates the state correctly, but skips showing the non-native version:
866 q->setAttribute(Qt::WA_DontShowOnScreen);
867#if QT_CONFIG(fscompleter)
868 // So the completer doesn't try to complete and therefore show a popup
869 if (!nativeDialogInUse)
870 completer->setModel(nullptr);
871#endif
872 } else {
873 createWidgets();
874 q->setAttribute(Qt::WA_DontShowOnScreen, on: false);
875#if QT_CONFIG(fscompleter)
876 if (!nativeDialogInUse) {
877 if (proxyModel != nullptr)
878 completer->setModel(proxyModel);
879 else
880 completer->setModel(model);
881 }
882#endif
883 }
884 }
885
886 if (visible && usingWidgets())
887 qFileDialogUi->fileNameEdit->setFocus();
888
889 QDialogPrivate::setVisible(visible);
890}
891
892/*!
893 \internal
894 set the directory to url
895*/
896void QFileDialogPrivate::goToUrl(const QUrl &url)
897{
898 //The shortcut in the side bar may have a parent that is not fetched yet (e.g. an hidden file)
899 //so we force the fetching
900 QFileSystemModelPrivate::QFileSystemNode *node = model->d_func()->node(path: url.toLocalFile(), fetch: true);
901 QModelIndex idx = model->d_func()->index(node);
902 enterDirectory(index: idx);
903}
904
905/*!
906 \fn void QFileDialog::setDirectory(const QDir &directory)
907
908 \overload
909*/
910
911/*!
912 Sets the file dialog's current \a directory.
913
914 \note On iOS, if you set \a directory to \l{QStandardPaths::standardLocations()}
915 {QStandardPaths::standardLocations(QStandardPaths::PicturesLocation).last()},
916 a native image picker dialog is used for accessing the user's photo album.
917 The filename returned can be loaded using QFile and related APIs.
918 For this to be enabled, the Info.plist assigned to QMAKE_INFO_PLIST in the
919 project file must contain the key \c NSPhotoLibraryUsageDescription. See
920 Info.plist documentation from Apple for more information regarding this key.
921 This feature was added in Qt 5.5.
922*/
923void QFileDialog::setDirectory(const QString &directory)
924{
925 Q_D(QFileDialog);
926 QString newDirectory = directory;
927 //we remove .. and . from the given path if exist
928 if (!directory.isEmpty())
929 newDirectory = QDir::cleanPath(path: directory);
930
931 if (!directory.isEmpty() && newDirectory.isEmpty())
932 return;
933
934 QUrl newDirUrl = QUrl::fromLocalFile(localfile: newDirectory);
935 QFileDialogPrivate::setLastVisitedDirectory(newDirUrl);
936
937 d->options->setInitialDirectory(QUrl::fromLocalFile(localfile: directory));
938 if (!d->usingWidgets()) {
939 d->setDirectory_sys(newDirUrl);
940 return;
941 }
942 if (d->rootPath() == newDirectory)
943 return;
944 QModelIndex root = d->model->setRootPath(newDirectory);
945 if (!d->nativeDialogInUse) {
946 d->qFileDialogUi->newFolderButton->setEnabled(d->model->flags(index: root) & Qt::ItemIsDropEnabled);
947 if (root != d->rootIndex()) {
948#if QT_CONFIG(fscompleter)
949 if (directory.endsWith(c: u'/'))
950 d->completer->setCompletionPrefix(newDirectory);
951 else
952 d->completer->setCompletionPrefix(newDirectory + u'/');
953#endif
954 d->setRootIndex(root);
955 }
956 d->qFileDialogUi->listView->selectionModel()->clear();
957 }
958}
959
960/*!
961 Returns the directory currently being displayed in the dialog.
962*/
963QDir QFileDialog::directory() const
964{
965 Q_D(const QFileDialog);
966 if (d->nativeDialogInUse) {
967 QString dir = d->directory_sys().toLocalFile();
968 return QDir(dir.isEmpty() ? d->options->initialDirectory().toLocalFile() : dir);
969 }
970 return d->rootPath();
971}
972
973/*!
974 Sets the file dialog's current \a directory url.
975
976 \note The non-native QFileDialog supports only local files.
977
978 \note On Windows, it is possible to pass URLs representing
979 one of the \e {virtual folders}, such as "Computer" or "Network".
980 This is done by passing a QUrl using the scheme \c clsid followed
981 by the CLSID value with the curly braces removed. For example the URL
982 \c clsid:374DE290-123F-4565-9164-39C4925E467B denotes the download
983 location. For a complete list of possible values, see the MSDN documentation on
984 \l{https://docs.microsoft.com/en-us/windows/win32/shell/knownfolderid}{KNOWNFOLDERID}.
985 This feature was added in Qt 5.5.
986
987 \sa QUuid
988 \since 5.2
989*/
990void QFileDialog::setDirectoryUrl(const QUrl &directory)
991{
992 Q_D(QFileDialog);
993 if (!directory.isValid())
994 return;
995
996 QFileDialogPrivate::setLastVisitedDirectory(directory);
997 d->options->setInitialDirectory(directory);
998
999 if (d->nativeDialogInUse)
1000 d->setDirectory_sys(directory);
1001 else if (directory.isLocalFile())
1002 setDirectory(directory.toLocalFile());
1003 else if (Q_UNLIKELY(d->usingWidgets()))
1004 qWarning(msg: "Non-native QFileDialog supports only local files");
1005}
1006
1007/*!
1008 Returns the url of the directory currently being displayed in the dialog.
1009
1010 \since 5.2
1011*/
1012QUrl QFileDialog::directoryUrl() const
1013{
1014 Q_D(const QFileDialog);
1015 if (d->nativeDialogInUse)
1016 return d->directory_sys();
1017 else
1018 return QUrl::fromLocalFile(localfile: directory().absolutePath());
1019}
1020
1021// FIXME Qt 5.4: Use upcoming QVolumeInfo class to determine this information?
1022static inline bool isCaseSensitiveFileSystem(const QString &path)
1023{
1024 Q_UNUSED(path);
1025#if defined(Q_OS_WIN)
1026 // Return case insensitive unconditionally, even if someone has a case sensitive
1027 // file system mounted, wrongly capitalized drive letters will cause mismatches.
1028 return false;
1029#elif defined(Q_OS_MACOS)
1030 return pathconf(QFile::encodeName(path).constData(), _PC_CASE_SENSITIVE) == 1;
1031#else
1032 return true;
1033#endif
1034}
1035
1036// Determine the file name to be set on the line edit from the path
1037// passed to selectFile() in mode QFileDialog::AcceptSave.
1038static inline QString fileFromPath(const QString &rootPath, QString path)
1039{
1040 if (!QFileInfo(path).isAbsolute())
1041 return path;
1042 if (path.startsWith(s: rootPath, cs: isCaseSensitiveFileSystem(path: rootPath) ? Qt::CaseSensitive : Qt::CaseInsensitive))
1043 path.remove(i: 0, len: rootPath.size());
1044
1045 if (path.isEmpty())
1046 return path;
1047
1048 if (path.at(i: 0) == QDir::separator()
1049#ifdef Q_OS_WIN
1050 //On Windows both cases can happen
1051 || path.at(0) == u'/'
1052#endif
1053 ) {
1054 path.remove(i: 0, len: 1);
1055 }
1056 return path;
1057}
1058
1059/*!
1060 Selects the given \a filename in the file dialog.
1061
1062 \sa selectedFiles()
1063*/
1064void QFileDialog::selectFile(const QString &filename)
1065{
1066 Q_D(QFileDialog);
1067 if (filename.isEmpty())
1068 return;
1069
1070 if (!d->usingWidgets()) {
1071 QUrl url;
1072 if (QFileInfo(filename).isRelative()) {
1073 url = d->options->initialDirectory();
1074 QString path = url.path();
1075 if (!path.endsWith(c: u'/'))
1076 path += u'/';
1077 url.setPath(path: path + filename);
1078 } else {
1079 url = QUrl::fromLocalFile(localfile: filename);
1080 }
1081 d->selectFile_sys(filename: url);
1082 d->options->setInitiallySelectedFiles(QList<QUrl>() << url);
1083 return;
1084 }
1085
1086 if (!QDir::isRelativePath(path: filename)) {
1087 QFileInfo info(filename);
1088 QString filenamePath = info.absoluteDir().path();
1089
1090 if (d->model->rootPath() != filenamePath)
1091 setDirectory(filenamePath);
1092 }
1093
1094 QModelIndex index = d->model->index(path: filename);
1095 d->qFileDialogUi->listView->selectionModel()->clear();
1096 if (!isVisible() || !d->lineEdit()->hasFocus())
1097 d->lineEdit()->setText(index.isValid() ? index.data().toString() : fileFromPath(rootPath: d->rootPath(), path: filename));
1098}
1099
1100/*!
1101 Selects the given \a url in the file dialog.
1102
1103 \note The non-native QFileDialog supports only local files.
1104
1105 \sa selectedUrls()
1106 \since 5.2
1107*/
1108void QFileDialog::selectUrl(const QUrl &url)
1109{
1110 Q_D(QFileDialog);
1111 if (!url.isValid())
1112 return;
1113
1114 if (d->nativeDialogInUse)
1115 d->selectFile_sys(filename: url);
1116 else if (url.isLocalFile())
1117 selectFile(filename: url.toLocalFile());
1118 else
1119 qWarning(msg: "Non-native QFileDialog supports only local files");
1120}
1121
1122#ifdef Q_OS_UNIX
1123static QString homeDirFromPasswdEntry(const QString &path, const QByteArray &userName)
1124{
1125#if defined(_POSIX_THREAD_SAFE_FUNCTIONS) && !defined(Q_OS_OPENBSD) && !defined(Q_OS_WASM)
1126 passwd pw;
1127 passwd *tmpPw;
1128 long bufSize = ::sysconf(_SC_GETPW_R_SIZE_MAX);
1129 if (bufSize == -1)
1130 bufSize = 1024;
1131 QVarLengthArray<char, 1024> buf(bufSize);
1132 int err = 0;
1133# if defined(Q_OS_SOLARIS) && (_POSIX_C_SOURCE - 0 < 199506L)
1134 tmpPw = getpwnam_r(userName.constData(), &pw, buf.data(), buf.size());
1135# else
1136 err = getpwnam_r(name: userName.constData(), resultbuf: &pw, buffer: buf.data(), buflen: buf.size(), result: &tmpPw);
1137# endif
1138 if (err || !tmpPw)
1139 return path;
1140 return QFile::decodeName(localFileName: pw.pw_dir);
1141#else
1142 passwd *pw = getpwnam(userName.constData());
1143 if (!pw)
1144 return path;
1145 return QFile::decodeName(pw->pw_dir);
1146#endif // defined(_POSIX_THREAD_SAFE_FUNCTIONS) && !defined(Q_OS_OPENBSD) && !defined(Q_OS_WASM)
1147}
1148
1149Q_AUTOTEST_EXPORT QString qt_tildeExpansion(const QString &path)
1150{
1151 if (!path.startsWith(c: u'~'))
1152 return path;
1153
1154 if (path.size() == 1) // '~'
1155 return QDir::homePath();
1156
1157 QStringView sv(path);
1158 const qsizetype sepIndex = sv.indexOf(c: QDir::separator());
1159 if (sepIndex == 1) // '~/' or '~/a/b/c'
1160 return QDir::homePath() + sv.sliced(pos: 1);
1161
1162#if defined(Q_OS_VXWORKS) || defined(Q_OS_INTEGRITY)
1163 if (sepIndex == -1)
1164 return QDir::homePath();
1165 return QDir::homePath() + sv.sliced(sepIndex);
1166#else
1167 const qsizetype userNameLen = sepIndex != -1 ? sepIndex - strlen(s: "~") // '~user/a/b'
1168 : path.size() - strlen(s: "~"); // '~user'
1169 const QByteArray userName = sv.sliced(pos: 1, n: userNameLen).toLocal8Bit();
1170 QString homePath = homeDirFromPasswdEntry(path, userName);
1171 if (sepIndex == -1)
1172 return homePath;
1173 return homePath + sv.sliced(pos: sepIndex);
1174#endif // defined(Q_OS_VXWORKS) || defined(Q_OS_INTEGRITY)
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(c: u'"')) {
1187#ifdef Q_OS_UNIX
1188 const QString prefix = q->directory().absolutePath() + QDir::separator();
1189 if (QFile::exists(fileName: prefix + editText))
1190 files << editText;
1191 else
1192 files << qt_tildeExpansion(path: 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(sep: u'\"');
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(fileName: prefix + token))
1208 files << token;
1209 else
1210 files << qt_tildeExpansion(path: token);
1211#else
1212 files << toInternal(tokens.at(i));
1213#endif
1214 }
1215 }
1216 return addDefaultSuffixToFiles(filesToFix: 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(urlsToFix: selectedFiles_sys());
1228
1229 const QModelIndexList selectedRows = qFileDialogUi->listView->selectionModel()->selectedRows();
1230 files.reserve(asize: 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(asize: typedFilesList.size());
1237 for (const QString &path : typedFilesList)
1238 files.append(t: QUrl::fromLocalFile(localfile: 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(path: 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() && !info.fileName().contains(c: u'.'))
1253 name += u'.' + defaultSuffix;
1254
1255 if (info.isAbsolute()) {
1256 files.append(t: name);
1257 } else {
1258 // at this point the path should only have Qt path separators.
1259 // This check is needed since we might be at the root directory
1260 // and on Windows it already ends with slash.
1261 QString path = rootPath();
1262 if (!path.endsWith(c: u'/'))
1263 path += u'/';
1264 path += name;
1265 files.append(t: path);
1266 }
1267 }
1268 return files;
1269}
1270
1271QList<QUrl> QFileDialogPrivate::addDefaultSuffixToUrls(const QList<QUrl> &urlsToFix) const
1272{
1273 QList<QUrl> urls;
1274 urls.reserve(asize: urlsToFix.size());
1275 // if the filename has no suffix, add the default suffix
1276 const QString defaultSuffix = options->defaultSuffix();
1277 for (QUrl url : urlsToFix) {
1278 if (!defaultSuffix.isEmpty()) {
1279 const QString urlPath = url.path();
1280 const auto idx = urlPath.lastIndexOf(c: u'/');
1281 if (idx != (urlPath.size() - 1) && !QStringView{urlPath}.mid(pos: idx + 1).contains(c: u'.'))
1282 url.setPath(path: urlPath + u'.' + defaultSuffix);
1283 }
1284 urls.append(t: url);
1285 }
1286 return urls;
1287}
1288
1289
1290/*!
1291 Returns a list of strings containing the absolute paths of the
1292 selected files in the dialog. If no files are selected, or
1293 the mode is not ExistingFiles or ExistingFile, selectedFiles() contains the current path in the viewport.
1294
1295 \sa selectedNameFilter(), selectFile()
1296*/
1297QStringList QFileDialog::selectedFiles() const
1298{
1299 Q_D(const QFileDialog);
1300
1301 QStringList files;
1302 const QList<QUrl> userSelectedFiles = d->userSelectedFiles();
1303 files.reserve(asize: userSelectedFiles.size());
1304 for (const QUrl &file : userSelectedFiles)
1305 files.append(t: file.toString(options: QUrl::PreferLocalFile));
1306
1307 if (files.isEmpty() && d->usingWidgets()) {
1308 const FileMode fm = fileMode();
1309 if (fm != ExistingFile && fm != ExistingFiles)
1310 files.append(t: d->rootIndex().data(arole: QFileSystemModel::FilePathRole).toString());
1311 }
1312 return files;
1313}
1314
1315/*!
1316 Returns a list of urls containing the selected files in the dialog.
1317 If no files are selected, or the mode is not ExistingFiles or
1318 ExistingFile, selectedUrls() contains the current path in the viewport.
1319
1320 \sa selectedNameFilter(), selectUrl()
1321 \since 5.2
1322*/
1323QList<QUrl> QFileDialog::selectedUrls() const
1324{
1325 Q_D(const QFileDialog);
1326 if (d->nativeDialogInUse) {
1327 return d->userSelectedFiles();
1328 } else {
1329 QList<QUrl> urls;
1330 const QStringList selectedFileList = selectedFiles();
1331 urls.reserve(asize: selectedFileList.size());
1332 for (const QString &file : selectedFileList)
1333 urls.append(t: QUrl::fromLocalFile(localfile: file));
1334 return urls;
1335 }
1336}
1337
1338/*
1339 Makes a list of filters from ;;-separated text.
1340 Used by the mac and windows implementations
1341*/
1342QStringList qt_make_filter_list(const QString &filter)
1343{
1344 if (filter.isEmpty())
1345 return QStringList();
1346
1347 auto sep = ";;"_L1;
1348 if (!filter.contains(s: sep) && filter.contains(c: u'\n'))
1349 sep = "\n"_L1;
1350
1351 return filter.split(sep);
1352}
1353
1354/*!
1355 Sets the filter used in the file dialog to the given \a filter.
1356
1357 If \a filter contains a pair of parentheses containing one or more
1358 filename-wildcard patterns, separated by spaces, then only the
1359 text contained in the parentheses is used as the filter. This means
1360 that these calls are all equivalent:
1361
1362 \snippet code/src_gui_dialogs_qfiledialog.cpp 6
1363
1364 \note With Android's native file dialog, the mime type matching the given
1365 name filter is used because only mime types are supported.
1366
1367 \sa setMimeTypeFilters(), setNameFilters()
1368*/
1369void QFileDialog::setNameFilter(const QString &filter)
1370{
1371 setNameFilters(qt_make_filter_list(filter));
1372}
1373
1374
1375/*
1376 Strip the filters by removing the details, e.g. (*.*).
1377*/
1378QStringList qt_strip_filters(const QStringList &filters)
1379{
1380#if QT_CONFIG(regularexpression)
1381 QStringList strippedFilters;
1382 static const QRegularExpression r(QString::fromLatin1(ba: QPlatformFileDialogHelper::filterRegExp));
1383 strippedFilters.reserve(asize: filters.size());
1384 for (const QString &filter : filters) {
1385 QString filterName;
1386 auto match = r.match(subject: filter);
1387 if (match.hasMatch())
1388 filterName = match.captured(nth: 1);
1389 strippedFilters.append(t: filterName.simplified());
1390 }
1391 return strippedFilters;
1392#else
1393 return filters;
1394#endif
1395}
1396
1397
1398/*!
1399 Sets the \a filters used in the file dialog.
1400
1401 Note that the filter \b{*.*} is not portable, because the historical
1402 assumption that the file extension determines the file type is not
1403 consistent on every operating system. It is possible to have a file with no
1404 dot in its name (for example, \c Makefile). In a native Windows file
1405 dialog, \b{*.*} matches such files, while in other types of file dialogs
1406 it might not match. So, it's better to use \b{*} if you mean to select any file.
1407
1408 \snippet code/src_gui_dialogs_qfiledialog.cpp 7
1409
1410 \l setMimeTypeFilters() has the advantage of providing all possible name
1411 filters for each file type. For example, JPEG images have three possible
1412 extensions; if your application can open such files, selecting the
1413 \c image/jpeg mime type as a filter allows you to open all of them.
1414*/
1415void QFileDialog::setNameFilters(const QStringList &filters)
1416{
1417 Q_D(QFileDialog);
1418 QStringList cleanedFilters;
1419 cleanedFilters.reserve(asize: filters.size());
1420 for (const QString &filter : filters)
1421 cleanedFilters << filter.simplified();
1422
1423 d->options->setNameFilters(cleanedFilters);
1424
1425 if (!d->usingWidgets())
1426 return;
1427
1428 d->qFileDialogUi->fileTypeCombo->clear();
1429 if (cleanedFilters.isEmpty())
1430 return;
1431
1432 if (testOption(option: HideNameFilterDetails))
1433 d->qFileDialogUi->fileTypeCombo->addItems(qt_strip_filters(filters: cleanedFilters));
1434 else
1435 d->qFileDialogUi->fileTypeCombo->addItems(cleanedFilters);
1436
1437 d->useNameFilter(index: 0);
1438}
1439
1440/*!
1441 Returns the file type filters that are in operation on this file
1442 dialog.
1443*/
1444QStringList QFileDialog::nameFilters() const
1445{
1446 return d_func()->options->nameFilters();
1447}
1448
1449/*!
1450 Sets the current file type \a filter. Multiple filters can be
1451 passed in \a filter by separating them with semicolons or spaces.
1452
1453 \sa setNameFilter(), setNameFilters(), selectedNameFilter()
1454*/
1455void QFileDialog::selectNameFilter(const QString &filter)
1456{
1457 Q_D(QFileDialog);
1458 d->options->setInitiallySelectedNameFilter(filter);
1459 if (!d->usingWidgets()) {
1460 d->selectNameFilter_sys(filter);
1461 return;
1462 }
1463 int i = -1;
1464 if (testOption(option: HideNameFilterDetails)) {
1465 const QStringList filters = qt_strip_filters(filters: qt_make_filter_list(filter));
1466 if (!filters.isEmpty())
1467 i = d->qFileDialogUi->fileTypeCombo->findText(filters.first());
1468 } else {
1469 i = d->qFileDialogUi->fileTypeCombo->findText(filter);
1470 }
1471 if (i >= 0) {
1472 d->qFileDialogUi->fileTypeCombo->setCurrentIndex(i);
1473 d->useNameFilter(index: d->qFileDialogUi->fileTypeCombo->currentIndex());
1474 }
1475}
1476
1477/*!
1478 Returns the filter that the user selected in the file dialog.
1479
1480 \sa selectedFiles()
1481*/
1482QString QFileDialog::selectedNameFilter() const
1483{
1484 Q_D(const QFileDialog);
1485 if (!d->usingWidgets())
1486 return d->selectedNameFilter_sys();
1487
1488 if (testOption(option: HideNameFilterDetails)) {
1489 const auto idx = d->qFileDialogUi->fileTypeCombo->currentIndex();
1490 if (idx >= 0 && idx < d->options->nameFilters().size())
1491 return d->options->nameFilters().at(i: d->qFileDialogUi->fileTypeCombo->currentIndex());
1492 }
1493 return d->qFileDialogUi->fileTypeCombo->currentText();
1494}
1495
1496/*!
1497 Returns the filter that is used when displaying files.
1498
1499 \sa setFilter()
1500*/
1501QDir::Filters QFileDialog::filter() const
1502{
1503 Q_D(const QFileDialog);
1504 if (d->usingWidgets())
1505 return d->model->filter();
1506 return d->options->filter();
1507}
1508
1509/*!
1510 Sets the filter used by the model to \a filters. The filter is used
1511 to specify the kind of files that should be shown.
1512
1513 \sa filter()
1514*/
1515
1516void QFileDialog::setFilter(QDir::Filters filters)
1517{
1518 Q_D(QFileDialog);
1519 d->options->setFilter(filters);
1520 if (!d->usingWidgets()) {
1521 d->setFilter_sys();
1522 return;
1523 }
1524
1525 d->model->setFilter(filters);
1526 d->showHiddenAction->setChecked((filters & QDir::Hidden));
1527}
1528
1529#if QT_CONFIG(mimetype)
1530
1531static QString nameFilterForMime(const QString &mimeType)
1532{
1533 QMimeDatabase db;
1534 QMimeType mime(db.mimeTypeForName(nameOrAlias: mimeType));
1535 if (mime.isValid()) {
1536 if (mime.isDefault()) {
1537 return QFileDialog::tr(s: "All files (*)");
1538 } else {
1539 const QString patterns = mime.globPatterns().join(sep: u' ');
1540 return mime.comment() + " ("_L1 + patterns + u')';
1541 }
1542 }
1543 return QString();
1544}
1545
1546/*!
1547 \since 5.2
1548
1549 Sets the \a filters used in the file dialog, from a list of MIME types.
1550
1551 Convenience method for setNameFilters().
1552 Uses QMimeType to create a name filter from the glob patterns and description
1553 defined in each MIME type.
1554
1555 Use application/octet-stream for the "All files (*)" filter, since that
1556 is the base MIME type for all files.
1557
1558 Calling setMimeTypeFilters overrides any previously set name filters,
1559 and changes the return value of nameFilters().
1560
1561 \snippet code/src_gui_dialogs_qfiledialog.cpp 13
1562*/
1563void QFileDialog::setMimeTypeFilters(const QStringList &filters)
1564{
1565 Q_D(QFileDialog);
1566 QStringList nameFilters;
1567 for (const QString &mimeType : filters) {
1568 const QString text = nameFilterForMime(mimeType);
1569 if (!text.isEmpty())
1570 nameFilters.append(t: text);
1571 }
1572 setNameFilters(nameFilters);
1573 d->options->setMimeTypeFilters(filters);
1574}
1575
1576/*!
1577 \since 5.2
1578
1579 Returns the MIME type filters that are in operation on this file
1580 dialog.
1581*/
1582QStringList QFileDialog::mimeTypeFilters() const
1583{
1584 return d_func()->options->mimeTypeFilters();
1585}
1586
1587/*!
1588 \since 5.2
1589
1590 Sets the current MIME type \a filter.
1591
1592*/
1593void QFileDialog::selectMimeTypeFilter(const QString &filter)
1594{
1595 Q_D(QFileDialog);
1596 d->options->setInitiallySelectedMimeTypeFilter(filter);
1597
1598 const QString filterForMime = nameFilterForMime(mimeType: filter);
1599
1600 if (!d->usingWidgets()) {
1601 d->selectMimeTypeFilter_sys(filter);
1602 if (d->selectedMimeTypeFilter_sys().isEmpty() && !filterForMime.isEmpty()) {
1603 selectNameFilter(filter: filterForMime);
1604 }
1605 } else if (!filterForMime.isEmpty()) {
1606 selectNameFilter(filter: filterForMime);
1607 }
1608}
1609
1610#endif // mimetype
1611
1612/*!
1613 * \since 5.9
1614 * \return The mimetype of the file that the user selected in the file dialog.
1615 */
1616QString QFileDialog::selectedMimeTypeFilter() const
1617{
1618 Q_D(const QFileDialog);
1619 QString mimeTypeFilter;
1620 if (!d->usingWidgets())
1621 mimeTypeFilter = d->selectedMimeTypeFilter_sys();
1622
1623#if QT_CONFIG(mimetype)
1624 if (mimeTypeFilter.isNull() && !d->options->mimeTypeFilters().isEmpty()) {
1625 const auto nameFilter = selectedNameFilter();
1626 const auto mimeTypes = d->options->mimeTypeFilters();
1627 for (const auto &mimeType: mimeTypes) {
1628 QString filter = nameFilterForMime(mimeType);
1629 if (testOption(option: HideNameFilterDetails))
1630 filter = qt_strip_filters(filters: { filter }).constFirst();
1631 if (filter == nameFilter) {
1632 mimeTypeFilter = mimeType;
1633 break;
1634 }
1635 }
1636 }
1637#endif
1638
1639 return mimeTypeFilter;
1640}
1641
1642/*!
1643 \property QFileDialog::viewMode
1644 \brief The way files and directories are displayed in the dialog.
1645
1646 By default, the \c Detail mode is used to display information about
1647 files and directories.
1648
1649 \sa ViewMode
1650*/
1651void QFileDialog::setViewMode(QFileDialog::ViewMode mode)
1652{
1653 Q_D(QFileDialog);
1654 d->options->setViewMode(static_cast<QFileDialogOptions::ViewMode>(mode));
1655 if (!d->usingWidgets())
1656 return;
1657 if (mode == Detail)
1658 d->showDetailsView();
1659 else
1660 d->showListView();
1661}
1662
1663QFileDialog::ViewMode QFileDialog::viewMode() const
1664{
1665 Q_D(const QFileDialog);
1666 if (!d->usingWidgets())
1667 return static_cast<QFileDialog::ViewMode>(d->options->viewMode());
1668 return (d->qFileDialogUi->stackedWidget->currentWidget() == d->qFileDialogUi->listView->parent() ? QFileDialog::List : QFileDialog::Detail);
1669}
1670
1671/*!
1672 \property QFileDialog::fileMode
1673 \brief The file mode of the dialog.
1674
1675 The file mode defines the number and type of items that the user is
1676 expected to select in the dialog.
1677
1678 By default, this property is set to AnyFile.
1679
1680 This function sets the labels for the FileName and
1681 \l{QFileDialog::}{Accept} \l{DialogLabel}s. It is possible to set
1682 custom text after the call to setFileMode().
1683
1684 \sa FileMode
1685*/
1686void QFileDialog::setFileMode(QFileDialog::FileMode mode)
1687{
1688 Q_D(QFileDialog);
1689 d->options->setFileMode(static_cast<QFileDialogOptions::FileMode>(mode));
1690 if (!d->usingWidgets())
1691 return;
1692
1693 d->retranslateWindowTitle();
1694
1695 // set selection mode and behavior
1696 QAbstractItemView::SelectionMode selectionMode;
1697 if (mode == QFileDialog::ExistingFiles)
1698 selectionMode = QAbstractItemView::ExtendedSelection;
1699 else
1700 selectionMode = QAbstractItemView::SingleSelection;
1701 d->qFileDialogUi->listView->setSelectionMode(selectionMode);
1702 d->qFileDialogUi->treeView->setSelectionMode(selectionMode);
1703 // set filter
1704 d->model->setFilter(d->filterForMode(filters: filter()));
1705 // setup file type for directory
1706 if (mode == Directory) {
1707 d->qFileDialogUi->fileTypeCombo->clear();
1708 d->qFileDialogUi->fileTypeCombo->addItem(tr(s: "Directories"));
1709 d->qFileDialogUi->fileTypeCombo->setEnabled(false);
1710 }
1711 d->updateFileNameLabel();
1712 d->updateOkButtonText();
1713 d->qFileDialogUi->fileTypeCombo->setEnabled(!testOption(option: ShowDirsOnly));
1714 d->updateOkButton();
1715}
1716
1717QFileDialog::FileMode QFileDialog::fileMode() const
1718{
1719 Q_D(const QFileDialog);
1720 return static_cast<FileMode>(d->options->fileMode());
1721}
1722
1723/*!
1724 \property QFileDialog::acceptMode
1725 \brief The accept mode of the dialog.
1726
1727 The action mode defines whether the dialog is for opening or saving files.
1728
1729 By default, this property is set to \l{AcceptOpen}.
1730
1731 \sa AcceptMode
1732*/
1733void QFileDialog::setAcceptMode(QFileDialog::AcceptMode mode)
1734{
1735 Q_D(QFileDialog);
1736 d->options->setAcceptMode(static_cast<QFileDialogOptions::AcceptMode>(mode));
1737 // clear WA_DontShowOnScreen so that d->canBeNativeDialog() doesn't return false incorrectly
1738 setAttribute(Qt::WA_DontShowOnScreen, on: false);
1739 if (!d->usingWidgets())
1740 return;
1741 QDialogButtonBox::StandardButton button = (mode == AcceptOpen ? QDialogButtonBox::Open : QDialogButtonBox::Save);
1742 d->qFileDialogUi->buttonBox->setStandardButtons(button | QDialogButtonBox::Cancel);
1743 d->qFileDialogUi->buttonBox->button(button)->setEnabled(false);
1744 d->updateOkButton();
1745 if (mode == AcceptSave) {
1746 d->qFileDialogUi->lookInCombo->setEditable(false);
1747 }
1748 d->retranslateWindowTitle();
1749}
1750
1751/*!
1752 \property QFileDialog::supportedSchemes
1753 \brief The URL schemes that the file dialog should allow navigating to.
1754 \since 5.6
1755
1756 Setting this property allows to restrict the type of URLs the
1757 user can select. It is a way for the application to declare
1758 the protocols it supports to fetch the file content. An empty list
1759 means that no restriction is applied (the default).
1760 Support for local files ("file" scheme) is implicit and always enabled;
1761 it is not necessary to include it in the restriction.
1762*/
1763
1764void QFileDialog::setSupportedSchemes(const QStringList &schemes)
1765{
1766 Q_D(QFileDialog);
1767 d->options->setSupportedSchemes(schemes);
1768}
1769
1770QStringList QFileDialog::supportedSchemes() const
1771{
1772 return d_func()->options->supportedSchemes();
1773}
1774
1775/*
1776 Returns the file system model index that is the root index in the
1777 views
1778*/
1779QModelIndex QFileDialogPrivate::rootIndex() const {
1780 return mapToSource(index: qFileDialogUi->listView->rootIndex());
1781}
1782
1783QAbstractItemView *QFileDialogPrivate::currentView() const {
1784 if (!qFileDialogUi->stackedWidget)
1785 return nullptr;
1786 if (qFileDialogUi->stackedWidget->currentWidget() == qFileDialogUi->listView->parent())
1787 return qFileDialogUi->listView;
1788 return qFileDialogUi->treeView;
1789}
1790
1791QLineEdit *QFileDialogPrivate::lineEdit() const {
1792 return (QLineEdit*)qFileDialogUi->fileNameEdit;
1793}
1794
1795long QFileDialogPrivate::maxNameLength(const QString &path)
1796{
1797#if defined(Q_OS_UNIX)
1798 return ::pathconf(path: QFile::encodeName(fileName: path).data(), _PC_NAME_MAX);
1799#elif defined(Q_OS_WIN)
1800 DWORD maxLength;
1801 const QString drive = path.left(3);
1802 if (::GetVolumeInformation(reinterpret_cast<const wchar_t *>(drive.utf16()), NULL, 0, NULL, &maxLength, NULL, NULL, 0) == false)
1803 return -1;
1804 return maxLength;
1805#else
1806 Q_UNUSED(path);
1807#endif
1808 return -1;
1809}
1810
1811/*
1812 Sets the view root index to be the file system model index
1813*/
1814void QFileDialogPrivate::setRootIndex(const QModelIndex &index) const {
1815 Q_ASSERT(index.isValid() ? index.model() == model : true);
1816 QModelIndex idx = mapFromSource(index);
1817 qFileDialogUi->treeView->setRootIndex(idx);
1818 qFileDialogUi->listView->setRootIndex(idx);
1819}
1820/*
1821 Select a file system model index
1822 returns the index that was selected (or not depending upon sortfilterproxymodel)
1823*/
1824QModelIndex QFileDialogPrivate::select(const QModelIndex &index) const {
1825 Q_ASSERT(index.isValid() ? index.model() == model : true);
1826
1827 QModelIndex idx = mapFromSource(index);
1828 if (idx.isValid() && !qFileDialogUi->listView->selectionModel()->isSelected(idx))
1829 qFileDialogUi->listView->selectionModel()->select(idx,
1830 QItemSelectionModel::Select | QItemSelectionModel::Rows);
1831 return idx;
1832}
1833
1834QFileDialog::AcceptMode QFileDialog::acceptMode() const
1835{
1836 Q_D(const QFileDialog);
1837 return static_cast<AcceptMode>(d->options->acceptMode());
1838}
1839
1840/*!
1841 \property QFileDialog::defaultSuffix
1842 \brief Suffix added to the filename if no other suffix was specified.
1843
1844 This property specifies a string that is added to the
1845 filename if it has no suffix yet. The suffix is typically
1846 used to indicate the file type (e.g. "txt" indicates a text
1847 file).
1848
1849 If the first character is a dot ('.'), it is removed.
1850*/
1851void QFileDialog::setDefaultSuffix(const QString &suffix)
1852{
1853 Q_D(QFileDialog);
1854 d->options->setDefaultSuffix(suffix);
1855}
1856
1857QString QFileDialog::defaultSuffix() const
1858{
1859 Q_D(const QFileDialog);
1860 return d->options->defaultSuffix();
1861}
1862
1863/*!
1864 Sets the browsing history of the filedialog to contain the given
1865 \a paths.
1866*/
1867void QFileDialog::setHistory(const QStringList &paths)
1868{
1869 Q_D(QFileDialog);
1870 if (d->usingWidgets())
1871 d->qFileDialogUi->lookInCombo->setHistory(paths);
1872}
1873
1874void QFileDialogComboBox::setHistory(const QStringList &paths)
1875{
1876 m_history = paths;
1877 // Only populate the first item, showPopup will populate the rest if needed
1878 QList<QUrl> list;
1879 const QModelIndex idx = d_ptr->model->index(path: d_ptr->rootPath());
1880 //On windows the popup display the "C:\", convert to nativeSeparators
1881 const QUrl url = idx.isValid()
1882 ? QUrl::fromLocalFile(localfile: QDir::toNativeSeparators(pathName: idx.data(arole: QFileSystemModel::FilePathRole).toString()))
1883 : QUrl("file:"_L1);
1884 if (url.isValid())
1885 list.append(t: url);
1886 urlModel->setUrls(list);
1887}
1888
1889/*!
1890 Returns the browsing history of the filedialog as a list of paths.
1891*/
1892QStringList QFileDialog::history() const
1893{
1894 Q_D(const QFileDialog);
1895 if (!d->usingWidgets())
1896 return QStringList();
1897 QStringList currentHistory = d->qFileDialogUi->lookInCombo->history();
1898 //On windows the popup display the "C:\", convert to nativeSeparators
1899 QString newHistory = QDir::toNativeSeparators(pathName: d->rootIndex().data(arole: QFileSystemModel::FilePathRole).toString());
1900 if (!currentHistory.contains(str: newHistory))
1901 currentHistory << newHistory;
1902 return currentHistory;
1903}
1904
1905/*!
1906 Sets the item delegate used to render items in the views in the
1907 file dialog to the given \a delegate.
1908
1909 Any existing delegate will be removed, but not deleted. QFileDialog
1910 does not take ownership of \a delegate.
1911
1912 \warning You should not share the same instance of a delegate between views.
1913 Doing so can cause incorrect or unintuitive editing behavior since each
1914 view connected to a given delegate may receive the \l{QAbstractItemDelegate::}{closeEditor()}
1915 signal, and attempt to access, modify or close an editor that has already been closed.
1916
1917 Note that the model used is QFileSystemModel. It has custom item data roles, which is
1918 described by the \l{QFileSystemModel::}{Roles} enum. You can use a QFileIconProvider if
1919 you only want custom icons.
1920
1921 \sa itemDelegate(), setIconProvider(), QFileSystemModel
1922*/
1923void QFileDialog::setItemDelegate(QAbstractItemDelegate *delegate)
1924{
1925 Q_D(QFileDialog);
1926 if (!d->usingWidgets())
1927 return;
1928 d->qFileDialogUi->listView->setItemDelegate(delegate);
1929 d->qFileDialogUi->treeView->setItemDelegate(delegate);
1930}
1931
1932/*!
1933 Returns the item delegate used to render the items in the views in the filedialog.
1934*/
1935QAbstractItemDelegate *QFileDialog::itemDelegate() const
1936{
1937 Q_D(const QFileDialog);
1938 if (!d->usingWidgets())
1939 return nullptr;
1940 return d->qFileDialogUi->listView->itemDelegate();
1941}
1942
1943/*!
1944 Sets the icon provider used by the filedialog to the specified \a provider.
1945*/
1946void QFileDialog::setIconProvider(QAbstractFileIconProvider *provider)
1947{
1948 Q_D(QFileDialog);
1949 if (!d->usingWidgets())
1950 return;
1951 d->model->setIconProvider(provider);
1952 //It forces the refresh of all entries in the side bar, then we can get new icons
1953 d->qFileDialogUi->sidebar->setUrls(d->qFileDialogUi->sidebar->urls());
1954}
1955
1956/*!
1957 Returns the icon provider used by the filedialog.
1958*/
1959QAbstractFileIconProvider *QFileDialog::iconProvider() const
1960{
1961 Q_D(const QFileDialog);
1962 if (!d->model)
1963 return nullptr;
1964 return d->model->iconProvider();
1965}
1966
1967void QFileDialogPrivate::setLabelTextControl(QFileDialog::DialogLabel label, const QString &text)
1968{
1969 if (!qFileDialogUi)
1970 return;
1971 switch (label) {
1972 case QFileDialog::LookIn:
1973 qFileDialogUi->lookInLabel->setText(text);
1974 break;
1975 case QFileDialog::FileName:
1976 qFileDialogUi->fileNameLabel->setText(text);
1977 break;
1978 case QFileDialog::FileType:
1979 qFileDialogUi->fileTypeLabel->setText(text);
1980 break;
1981 case QFileDialog::Accept:
1982 if (q_func()->acceptMode() == QFileDialog::AcceptOpen) {
1983 if (QPushButton *button = qFileDialogUi->buttonBox->button(QDialogButtonBox::Open))
1984 button->setText(text);
1985 } else {
1986 if (QPushButton *button = qFileDialogUi->buttonBox->button(QDialogButtonBox::Save))
1987 button->setText(text);
1988 }
1989 break;
1990 case QFileDialog::Reject:
1991 if (QPushButton *button = qFileDialogUi->buttonBox->button(QDialogButtonBox::Cancel))
1992 button->setText(text);
1993 break;
1994 }
1995}
1996
1997/*!
1998 Sets the \a text shown in the filedialog in the specified \a label.
1999*/
2000
2001void QFileDialog::setLabelText(DialogLabel label, const QString &text)
2002{
2003 Q_D(QFileDialog);
2004 d->options->setLabelText(label: static_cast<QFileDialogOptions::DialogLabel>(label), text);
2005 d->setLabelTextControl(label, text);
2006}
2007
2008/*!
2009 Returns the text shown in the filedialog in the specified \a label.
2010*/
2011QString QFileDialog::labelText(DialogLabel label) const
2012{
2013 Q_D(const QFileDialog);
2014 if (!d->usingWidgets())
2015 return d->options->labelText(label: static_cast<QFileDialogOptions::DialogLabel>(label));
2016 QPushButton *button;
2017 switch (label) {
2018 case LookIn:
2019 return d->qFileDialogUi->lookInLabel->text();
2020 case FileName:
2021 return d->qFileDialogUi->fileNameLabel->text();
2022 case FileType:
2023 return d->qFileDialogUi->fileTypeLabel->text();
2024 case Accept:
2025 if (acceptMode() == AcceptOpen)
2026 button = d->qFileDialogUi->buttonBox->button(QDialogButtonBox::Open);
2027 else
2028 button = d->qFileDialogUi->buttonBox->button(QDialogButtonBox::Save);
2029 if (button)
2030 return button->text();
2031 break;
2032 case Reject:
2033 button = d->qFileDialogUi->buttonBox->button(QDialogButtonBox::Cancel);
2034 if (button)
2035 return button->text();
2036 break;
2037 }
2038 return QString();
2039}
2040
2041/*!
2042 This is a convenience static function that returns an existing file
2043 selected by the user. If the user presses Cancel, it returns a null string.
2044
2045 \snippet code/src_gui_dialogs_qfiledialog.cpp 8
2046
2047 The function creates a modal file dialog with the given \a parent widget.
2048 If \a parent is not \nullptr, the dialog is shown centered over the
2049 parent widget.
2050
2051 The file dialog's working directory is set to \a dir. If \a dir
2052 includes a file name, the file is selected. Only files that match the
2053 given \a filter are shown. The selected filter is set to \a selectedFilter.
2054 The parameters \a dir, \a selectedFilter, and \a filter may be empty
2055 strings. If you want multiple filters, separate them with ';;', for
2056 example:
2057
2058 \snippet code/src_gui_dialogs_qfiledialog.cpp 14
2059
2060 The \a options argument holds various options about how to run the dialog.
2061 See the QFileDialog::Option enum for more information on the flags you can
2062 pass.
2063
2064 The dialog's caption is set to \a caption. If \a caption is not specified,
2065 then a default caption will be used.
2066
2067 On Windows, and \macos, this static function uses the
2068 native file dialog and not a QFileDialog. Note that the \macos native file
2069 dialog does not show a title bar.
2070
2071 On Windows the dialog spins a blocking modal event loop that does not
2072 dispatch any QTimers, and if \a parent is not \nullptr then it positions
2073 the dialog just below the parent's title bar.
2074
2075 On Unix/X11, the normal behavior of the file dialog is to resolve and
2076 follow symlinks. For example, if \c{/usr/tmp} is a symlink to \c{/var/tmp},
2077 the file dialog changes to \c{/var/tmp} after entering \c{/usr/tmp}. If
2078 \a options includes DontResolveSymlinks, the file dialog treats
2079 symlinks as regular directories.
2080
2081 \sa getOpenFileNames(), getSaveFileName(), getExistingDirectory()
2082*/
2083QString QFileDialog::getOpenFileName(QWidget *parent,
2084 const QString &caption,
2085 const QString &dir,
2086 const QString &filter,
2087 QString *selectedFilter,
2088 Options options)
2089{
2090 const QStringList schemes = QStringList(QStringLiteral("file"));
2091 const QUrl selectedUrl = getOpenFileUrl(parent, caption, dir: QUrl::fromLocalFile(localfile: dir), filter,
2092 selectedFilter, options, supportedSchemes: schemes);
2093 if (selectedUrl.isLocalFile() || selectedUrl.isEmpty())
2094 return selectedUrl.toLocalFile();
2095 else
2096 return selectedUrl.toString();
2097}
2098
2099/*!
2100 This is a convenience static function that returns an existing file
2101 selected by the user. If the user presses Cancel, it returns an
2102 empty url.
2103
2104 The function is used similarly to QFileDialog::getOpenFileName(). In
2105 particular \a parent, \a caption, \a dir, \a filter, \a selectedFilter
2106 and \a options are used in exactly the same way.
2107
2108 The main difference with QFileDialog::getOpenFileName() comes from
2109 the ability offered to the user to select a remote file. That's why
2110 the return type and the type of \a dir is QUrl.
2111
2112 The \a supportedSchemes argument allows to restrict the type of URLs the
2113 user is able to select. It is a way for the application to declare
2114 the protocols it will support to fetch the file content. An empty list
2115 means that no restriction is applied (the default).
2116 Support for local files ("file" scheme) is implicit and always enabled;
2117 it is not necessary to include it in the restriction.
2118
2119 When possible, this static function uses the native file dialog and
2120 not a QFileDialog. On platforms that don't support selecting remote
2121 files, Qt will allow to select only local files.
2122
2123 \sa getOpenFileName(), getOpenFileUrls(), getSaveFileUrl(), getExistingDirectoryUrl()
2124 \since 5.2
2125*/
2126QUrl QFileDialog::getOpenFileUrl(QWidget *parent,
2127 const QString &caption,
2128 const QUrl &dir,
2129 const QString &filter,
2130 QString *selectedFilter,
2131 Options options,
2132 const QStringList &supportedSchemes)
2133{
2134 QFileDialogArgs args(dir);
2135 args.parent = parent;
2136 args.caption = caption;
2137 args.filter = filter;
2138 args.mode = ExistingFile;
2139 args.options = options;
2140
2141 QAutoPointer<QFileDialog> dialog(new QFileDialog(args));
2142 dialog->setSupportedSchemes(supportedSchemes);
2143 if (selectedFilter && !selectedFilter->isEmpty())
2144 dialog->selectNameFilter(filter: *selectedFilter);
2145 const int execResult = dialog->exec();
2146 if (bool(dialog) && execResult == QDialog::Accepted) {
2147 if (selectedFilter)
2148 *selectedFilter = dialog->selectedNameFilter();
2149 return dialog->selectedUrls().value(i: 0);
2150 }
2151 return QUrl();
2152}
2153
2154/*!
2155 This is a convenience static function that returns one or more existing
2156 files selected by the user.
2157
2158 \snippet code/src_gui_dialogs_qfiledialog.cpp 9
2159
2160 This function creates a modal file dialog with the given \a parent widget.
2161 If \a parent is not \nullptr, the dialog is shown centered over the
2162 parent widget.
2163
2164 The file dialog's working directory is set to \a dir. If \a dir
2165 includes a file name, the file is selected. The filter is set to
2166 \a filter so that only those files which match the filter are shown. The
2167 filter selected is set to \a selectedFilter. The parameters \a dir,
2168 \a selectedFilter and \a filter can be empty strings. If you need multiple
2169 filters, separate them with ';;', for instance:
2170
2171 \snippet code/src_gui_dialogs_qfiledialog.cpp 14
2172
2173 The dialog's caption is set to \a caption. If \a caption is not specified,
2174 then a default caption is used.
2175
2176 On Windows and \macos, this static function uses the
2177 native file dialog and not a QFileDialog. Note that the \macos native file
2178 dialog does not show a title bar.
2179
2180 On Windows the dialog spins a blocking modal event loop that does not
2181 dispatch any QTimers, and if \a parent is not \nullptr then it positions
2182 the dialog just below the parent's title bar.
2183
2184 On Unix/X11, the normal behavior of the file dialog is to resolve and
2185 follow symlinks. For example, if \c{/usr/tmp} is a symlink to \c{/var/tmp},
2186 the file dialog will change to \c{/var/tmp} after entering \c{/usr/tmp}.
2187 The \a options argument holds various options about how to run the dialog,
2188 see the QFileDialog::Option enum for more information on the flags you can
2189 pass.
2190
2191 \sa getOpenFileName(), getSaveFileName(), getExistingDirectory()
2192*/
2193QStringList QFileDialog::getOpenFileNames(QWidget *parent,
2194 const QString &caption,
2195 const QString &dir,
2196 const QString &filter,
2197 QString *selectedFilter,
2198 Options options)
2199{
2200 const QStringList schemes = QStringList(QStringLiteral("file"));
2201 const QList<QUrl> selectedUrls = getOpenFileUrls(parent, caption, dir: QUrl::fromLocalFile(localfile: dir),
2202 filter, selectedFilter, options, supportedSchemes: schemes);
2203 QStringList fileNames;
2204 fileNames.reserve(asize: selectedUrls.size());
2205 for (const QUrl &url : selectedUrls)
2206 fileNames.append(t: url.toString(options: QUrl::PreferLocalFile));
2207 return fileNames;
2208}
2209
2210/*!
2211 This is a convenience static function that returns one or more existing
2212 files selected by the user. If the user presses Cancel, it returns an
2213 empty list.
2214
2215 The function is used similarly to QFileDialog::getOpenFileNames(). In
2216 particular \a parent, \a caption, \a dir, \a filter, \a selectedFilter
2217 and \a options are used in exactly the same way.
2218
2219 The main difference with QFileDialog::getOpenFileNames() comes from
2220 the ability offered to the user to select remote files. That's why
2221 the return type and the type of \a dir are respectively QList<QUrl>
2222 and QUrl.
2223
2224 The \a supportedSchemes argument allows to restrict the type of URLs the
2225 user can select. It is a way for the application to declare
2226 the protocols it supports to fetch the file content. An empty list
2227 means that no restriction is applied (the default).
2228 Support for local files ("file" scheme) is implicit and always enabled;
2229 it is not necessary to include it in the restriction.
2230
2231 When possible, this static function uses the native file dialog and
2232 not a QFileDialog. On platforms that don't support selecting remote
2233 files, Qt will allow to select only local files.
2234
2235 \sa getOpenFileNames(), getOpenFileUrl(), getSaveFileUrl(), getExistingDirectoryUrl()
2236 \since 5.2
2237*/
2238QList<QUrl> QFileDialog::getOpenFileUrls(QWidget *parent,
2239 const QString &caption,
2240 const QUrl &dir,
2241 const QString &filter,
2242 QString *selectedFilter,
2243 Options options,
2244 const QStringList &supportedSchemes)
2245{
2246 QFileDialogArgs args(dir);
2247 args.parent = parent;
2248 args.caption = caption;
2249 args.filter = filter;
2250 args.mode = ExistingFiles;
2251 args.options = options;
2252
2253 QAutoPointer<QFileDialog> dialog(new QFileDialog(args));
2254 dialog->setSupportedSchemes(supportedSchemes);
2255 if (selectedFilter && !selectedFilter->isEmpty())
2256 dialog->selectNameFilter(filter: *selectedFilter);
2257 const int execResult = dialog->exec();
2258 if (bool(dialog) && execResult == QDialog::Accepted) {
2259 if (selectedFilter)
2260 *selectedFilter = dialog->selectedNameFilter();
2261 return dialog->selectedUrls();
2262 }
2263 return QList<QUrl>();
2264}
2265
2266/*!
2267 This is a convenience static function that returns the content of a file
2268 selected by the user.
2269
2270 Use this function to access local files on Qt for WebAssembly, if the web sandbox
2271 restricts file access. Its implementation enables displaying a native file dialog in
2272 the browser, where the user selects a file based on the \a nameFilter parameter.
2273
2274 \a parent is ignored on Qt for WebAssembly. Pass \a parent on other platforms, to make
2275 the popup a child of another widget. If the platform doesn't support native file
2276 dialogs, the function falls back to QFileDialog.
2277
2278 The function is asynchronous and returns immediately. The \a fileOpenCompleted
2279 callback will be called when a file has been selected and its contents have been
2280 read into memory.
2281
2282 \snippet code/src_gui_dialogs_qfiledialog.cpp 15
2283 \since 5.13
2284*/
2285void QFileDialog::getOpenFileContent(const QString &nameFilter, const std::function<void(const QString &, const QByteArray &)> &fileOpenCompleted, QWidget *parent)
2286{
2287#ifdef Q_OS_WASM
2288 Q_UNUSED(parent);
2289 auto openFileImpl = std::make_shared<std::function<void(void)>>();
2290 QString fileName;
2291 QByteArray fileContent;
2292 *openFileImpl = [=]() mutable {
2293 auto fileDialogClosed = [&](bool fileSelected) {
2294 if (!fileSelected) {
2295 fileOpenCompleted(fileName, fileContent);
2296 openFileImpl.reset();
2297 }
2298 };
2299 auto acceptFile = [&](uint64_t size, const std::string name) -> char * {
2300 const uint64_t twoGB = 1ULL << 31; // QByteArray limit
2301 if (size > twoGB)
2302 return nullptr;
2303
2304 fileName = QString::fromStdString(name);
2305 fileContent.resize(size);
2306 return fileContent.data();
2307 };
2308 auto fileContentReady = [&]() mutable {
2309 fileOpenCompleted(fileName, fileContent);
2310 openFileImpl.reset();
2311 };
2312
2313 QWasmLocalFileAccess::openFile(nameFilter.toStdString(), fileDialogClosed, acceptFile, fileContentReady);
2314 };
2315
2316 (*openFileImpl)();
2317#else
2318 QFileDialog *dialog = new QFileDialog(parent);
2319 dialog->setFileMode(QFileDialog::ExistingFile);
2320 dialog->setNameFilter(nameFilter);
2321 dialog->setAttribute(Qt::WA_DeleteOnClose);
2322
2323 auto fileSelected = [=](const QString &fileName) {
2324 QByteArray fileContent;
2325 if (!fileName.isNull()) {
2326 QFile selectedFile(fileName);
2327 if (selectedFile.open(flags: QIODevice::ReadOnly))
2328 fileContent = selectedFile.readAll();
2329 }
2330 fileOpenCompleted(fileName, fileContent);
2331 };
2332
2333 connect(sender: dialog, signal: &QFileDialog::fileSelected, context: dialog, slot&: fileSelected);
2334 dialog->show();
2335#endif
2336}
2337
2338/*!
2339 This is a convenience static function that saves \a fileContent to a file, using
2340 a file name and location chosen by the user. \a fileNameHint can be provided to
2341 suggest a file name to the user.
2342
2343 Use this function to save content to local files on Qt for WebAssembly, if the web sandbox
2344 restricts file access. Its implementation enables displaying a native file dialog in the
2345 browser, where the user specifies an output file based on the \a fileNameHint argument.
2346
2347 \a parent is ignored on Qt for WebAssembly. Pass \a parent on other platforms, to make
2348 the popup a child of another widget. If the platform doesn't support native file
2349 dialogs, the function falls back to QFileDialog.
2350
2351 The function is asynchronous and returns immediately.
2352
2353 \snippet code/src_gui_dialogs_qfiledialog.cpp 16
2354 \since 5.14
2355*/
2356void QFileDialog::saveFileContent(const QByteArray &fileContent, const QString &fileNameHint, QWidget *parent)
2357{
2358#ifdef Q_OS_WASM
2359 Q_UNUSED(parent);
2360 QWasmLocalFileAccess::saveFile(fileContent, fileNameHint.toStdString());
2361#else
2362 QFileDialog *dialog = new QFileDialog(parent);
2363 dialog->setAcceptMode(QFileDialog::AcceptSave);
2364 dialog->setFileMode(QFileDialog::AnyFile);
2365 dialog->selectFile(filename: fileNameHint);
2366
2367 auto fileSelected = [=](const QString &fileName) {
2368 if (!fileName.isNull()) {
2369 QFile selectedFile(fileName);
2370 if (selectedFile.open(flags: QIODevice::WriteOnly))
2371 selectedFile.write(data: fileContent);
2372 }
2373 };
2374
2375 connect(sender: dialog, signal: &QFileDialog::fileSelected, context: dialog, slot&: fileSelected);
2376 dialog->setAttribute(Qt::WA_DeleteOnClose);
2377 dialog->show();
2378#endif
2379}
2380
2381/*!
2382 This is a convenience static function that returns a file name selected
2383 by the user. The file does not have to exist.
2384
2385 It creates a modal file dialog with the given \a parent widget. If
2386 \a parent is not \nullptr, the dialog will be shown centered over the
2387 parent widget.
2388
2389 \snippet code/src_gui_dialogs_qfiledialog.cpp 11
2390
2391 The file dialog's working directory is set to \a dir. If \a dir
2392 includes a file name, the file is selected. Only files that match the
2393 \a filter are shown. The filter selected is set to \a selectedFilter. The
2394 parameters \a dir, \a selectedFilter, and \a filter may be empty strings.
2395 Multiple filters are separated with ';;'. For instance:
2396
2397 \snippet code/src_gui_dialogs_qfiledialog.cpp 14
2398
2399 The \a options argument holds various options about how to run the dialog,
2400 see the QFileDialog::Option enum for more information on the flags you can
2401 pass.
2402
2403 The default filter can be chosen by setting \a selectedFilter to the
2404 desired value.
2405
2406 The dialog's caption is set to \a caption. If \a caption is not specified,
2407 a default caption is used.
2408
2409 On Windows, and \macos, this static function uses the
2410 native file dialog and not a QFileDialog.
2411
2412 On Windows the dialog spins a blocking modal event loop that does not
2413 dispatch any QTimers, and if \a parent is not \nullptr then it
2414 positions the dialog just below the parent's title bar. On \macos, with its
2415 native file dialog, the filter argument is ignored.
2416
2417 On Unix/X11, the normal behavior of the file dialog is to resolve and
2418 follow symlinks. For example, if \c{/usr/tmp} is a symlink to \c{/var/tmp},
2419 the file dialog changes to \c{/var/tmp} after entering \c{/usr/tmp}. If
2420 \a options includes DontResolveSymlinks, the file dialog treats symlinks
2421 as regular directories.
2422
2423 \sa getOpenFileName(), getOpenFileNames(), getExistingDirectory()
2424*/
2425QString QFileDialog::getSaveFileName(QWidget *parent,
2426 const QString &caption,
2427 const QString &dir,
2428 const QString &filter,
2429 QString *selectedFilter,
2430 Options options)
2431{
2432 const QStringList schemes = QStringList(QStringLiteral("file"));
2433 const QUrl selectedUrl = getSaveFileUrl(parent, caption, dir: QUrl::fromLocalFile(localfile: dir), filter,
2434 selectedFilter, options, supportedSchemes: schemes);
2435 if (selectedUrl.isLocalFile() || selectedUrl.isEmpty())
2436 return selectedUrl.toLocalFile();
2437 else
2438 return selectedUrl.toString();
2439}
2440
2441/*!
2442 This is a convenience static function that returns a file selected by
2443 the user. The file does not have to exist. If the user presses Cancel,
2444 it returns an empty url.
2445
2446 The function is used similarly to QFileDialog::getSaveFileName(). In
2447 particular \a parent, \a caption, \a dir, \a filter, \a selectedFilter
2448 and \a options are used in exactly the same way.
2449
2450 The main difference with QFileDialog::getSaveFileName() comes from
2451 the ability offered to the user to select a remote file. That's why
2452 the return type and the type of \a dir is QUrl.
2453
2454 The \a supportedSchemes argument allows to restrict the type of URLs the
2455 user can select. It is a way for the application to declare
2456 the protocols it supports to save the file content. An empty list
2457 means that no restriction is applied (the default).
2458 Support for local files ("file" scheme) is implicit and always enabled;
2459 it is not necessary to include it in the restriction.
2460
2461 When possible, this static function uses the native file dialog and
2462 not a QFileDialog. On platforms that don't support selecting remote
2463 files, Qt will allow to select only local files.
2464
2465 \sa getSaveFileName(), getOpenFileUrl(), getOpenFileUrls(), getExistingDirectoryUrl()
2466 \since 5.2
2467*/
2468QUrl QFileDialog::getSaveFileUrl(QWidget *parent,
2469 const QString &caption,
2470 const QUrl &dir,
2471 const QString &filter,
2472 QString *selectedFilter,
2473 Options options,
2474 const QStringList &supportedSchemes)
2475{
2476 QFileDialogArgs args(dir);
2477 args.parent = parent;
2478 args.caption = caption;
2479 args.filter = filter;
2480 args.mode = AnyFile;
2481 args.options = options;
2482
2483 QAutoPointer<QFileDialog> dialog(new QFileDialog(args));
2484 dialog->setSupportedSchemes(supportedSchemes);
2485 dialog->setAcceptMode(AcceptSave);
2486 if (selectedFilter && !selectedFilter->isEmpty())
2487 dialog->selectNameFilter(filter: *selectedFilter);
2488 const int execResult = dialog->exec();
2489 if (bool(dialog) && execResult == QDialog::Accepted) {
2490 if (selectedFilter)
2491 *selectedFilter = dialog->selectedNameFilter();
2492 return dialog->selectedUrls().value(i: 0);
2493 }
2494 return QUrl();
2495}
2496
2497/*!
2498 This is a convenience static function that returns an existing
2499 directory selected by the user.
2500
2501 \snippet code/src_gui_dialogs_qfiledialog.cpp 12
2502
2503 This function creates a modal file dialog with the given \a parent widget.
2504 If \a parent is not \nullptr, the dialog is shown centered over the
2505 parent widget.
2506
2507 The dialog's working directory is set to \a dir, and the caption is set to
2508 \a caption. Either of these can be an empty string in which case the
2509 current directory and a default caption are used respectively.
2510
2511 The \a options argument holds various options about how to run the dialog.
2512 See the QFileDialog::Option enum for more information on the flags you can
2513 pass. To ensure a native file dialog, \l{QFileDialog::}{ShowDirsOnly} must
2514 be set.
2515
2516 On Windows and \macos, this static function uses the
2517 native file dialog and not a QFileDialog. However, the native Windows file
2518 dialog does not support displaying files in the directory chooser. You need
2519 to pass the \l{QFileDialog::}{DontUseNativeDialog} option, or set the global
2520 \\l{Qt::}{AA_DontUseNativeDialogs} application attribute to display files using a
2521 QFileDialog.
2522
2523 Note that the \macos native file dialog does not show a title bar.
2524
2525 On Unix/X11, the normal behavior of the file dialog is to resolve and
2526 follow symlinks. For example, if \c{/usr/tmp} is a symlink to \c{/var/tmp},
2527 the file dialog changes to \c{/var/tmp} after entering \c{/usr/tmp}. If
2528 \a options includes DontResolveSymlinks, the file dialog treats
2529 symlinks as regular directories.
2530
2531 On Windows, the dialog spins a blocking modal event loop that does not
2532 dispatch any QTimers, and if \a parent is not \nullptr then it positions
2533 the dialog just below the parent's title bar.
2534
2535 \sa getOpenFileName(), getOpenFileNames(), getSaveFileName()
2536*/
2537QString QFileDialog::getExistingDirectory(QWidget *parent,
2538 const QString &caption,
2539 const QString &dir,
2540 Options options)
2541{
2542 const QStringList schemes = QStringList(QStringLiteral("file"));
2543 const QUrl selectedUrl =
2544 getExistingDirectoryUrl(parent, caption, dir: QUrl::fromLocalFile(localfile: dir), options, supportedSchemes: schemes);
2545 if (selectedUrl.isLocalFile() || selectedUrl.isEmpty())
2546 return selectedUrl.toLocalFile();
2547 else
2548 return selectedUrl.toString();
2549}
2550
2551/*!
2552 This is a convenience static function that returns an existing
2553 directory selected by the user. If the user presses Cancel, it
2554 returns an empty url.
2555
2556 The function is used similarly to QFileDialog::getExistingDirectory().
2557 In particular \a parent, \a caption, \a dir and \a options are used
2558 in exactly the same way.
2559
2560 The main difference with QFileDialog::getExistingDirectory() comes from
2561 the ability offered to the user to select a remote directory. That's why
2562 the return type and the type of \a dir is QUrl.
2563
2564 The \a supportedSchemes argument allows to restrict the type of URLs the
2565 user is able to select. It is a way for the application to declare
2566 the protocols it supports to fetch the file content. An empty list
2567 means that no restriction is applied (the default).
2568 Support for local files ("file" scheme) is implicit and always enabled;
2569 it is not necessary to include it in the restriction.
2570
2571 When possible, this static function uses the native file dialog and
2572 not a QFileDialog. On platforms that don't support selecting remote
2573 files, Qt allows to select only local files.
2574
2575 \sa getExistingDirectory(), getOpenFileUrl(), getOpenFileUrls(), getSaveFileUrl()
2576 \since 5.2
2577*/
2578QUrl QFileDialog::getExistingDirectoryUrl(QWidget *parent,
2579 const QString &caption,
2580 const QUrl &dir,
2581 Options options,
2582 const QStringList &supportedSchemes)
2583{
2584 QFileDialogArgs args(dir);
2585 args.parent = parent;
2586 args.caption = caption;
2587 args.mode = Directory;
2588 args.options = options;
2589
2590 QAutoPointer<QFileDialog> dialog(new QFileDialog(args));
2591 dialog->setSupportedSchemes(supportedSchemes);
2592 const int execResult = dialog->exec();
2593 if (bool(dialog) && execResult == QDialog::Accepted)
2594 return dialog->selectedUrls().value(i: 0);
2595 return QUrl();
2596}
2597
2598inline static QUrl _qt_get_directory(const QUrl &url, const QFileInfo &local)
2599{
2600 if (url.isLocalFile()) {
2601 QFileInfo info = local;
2602 if (!local.isAbsolute())
2603 info = QFileInfo(QDir::current(), url.toLocalFile());
2604 const QFileInfo pathInfo(info.absolutePath());
2605 if (!pathInfo.exists() || !pathInfo.isDir())
2606 return QUrl();
2607 if (info.exists() && info.isDir())
2608 return QUrl::fromLocalFile(localfile: QDir::cleanPath(path: info.absoluteFilePath()));
2609 return QUrl::fromLocalFile(localfile: pathInfo.absoluteFilePath());
2610 } else {
2611 return url;
2612 }
2613}
2614
2615inline static void _qt_init_lastVisited() {
2616#if QT_CONFIG(settings)
2617 if (lastVisitedDir()->isEmpty()) {
2618 QSettings settings(QSettings::UserScope, u"QtProject"_s);
2619 const QString &lastVisisted = settings.value(key: "FileDialog/lastVisited", defaultValue: QString()).toString();
2620 *lastVisitedDir() = QUrl::fromLocalFile(localfile: lastVisisted);
2621 }
2622#endif
2623}
2624
2625/*
2626 Initialize working directory and selection from \a url.
2627*/
2628QFileDialogArgs::QFileDialogArgs(const QUrl &url)
2629{
2630 // default case, re-use QFileInfo to avoid stat'ing
2631 const QFileInfo local(url.toLocalFile());
2632 // Get the initial directory URL
2633 if (!url.isEmpty())
2634 directory = _qt_get_directory(url, local);
2635 if (directory.isEmpty()) {
2636 _qt_init_lastVisited();
2637 const QUrl lastVisited = *lastVisitedDir();
2638 if (lastVisited != url)
2639 directory = _qt_get_directory(url: lastVisited, local: QFileInfo());
2640 }
2641 if (directory.isEmpty())
2642 directory = QUrl::fromLocalFile(localfile: QDir::currentPath());
2643
2644 /*
2645 The initial directory can contain both the initial directory
2646 and initial selection, e.g. /home/user/foo.txt
2647 */
2648 if (selection.isEmpty() && !url.isEmpty()) {
2649 if (url.isLocalFile()) {
2650 if (!local.isDir())
2651 selection = local.fileName();
2652 } else {
2653 // With remote URLs we can only assume.
2654 selection = url.fileName();
2655 }
2656 }
2657}
2658
2659/*!
2660 \reimp
2661*/
2662void QFileDialog::done(int result)
2663{
2664 Q_D(QFileDialog);
2665
2666 QDialog::done(result);
2667
2668 if (d->receiverToDisconnectOnClose) {
2669 disconnect(sender: this, signal: d->signalToDisconnectOnClose,
2670 receiver: d->receiverToDisconnectOnClose, member: d->memberToDisconnectOnClose);
2671 d->receiverToDisconnectOnClose = nullptr;
2672 }
2673 d->memberToDisconnectOnClose.clear();
2674 d->signalToDisconnectOnClose.clear();
2675}
2676
2677bool QFileDialogPrivate::itemAlreadyExists(const QString &fileName)
2678{
2679#if QT_CONFIG(messagebox)
2680 Q_Q(QFileDialog);
2681 const QString msg = QFileDialog::tr(s: "%1 already exists.\nDo you want to replace it?").arg(a: fileName);
2682 using B = QMessageBox;
2683 const auto res = B::warning(parent: q, title: q->windowTitle(), text: msg, buttons: B::Yes | B::No, defaultButton: B::No);
2684 return res == B::Yes;
2685#endif
2686 return false;
2687}
2688
2689void QFileDialogPrivate::itemNotFound(const QString &fileName, QFileDialog::FileMode mode)
2690{
2691#if QT_CONFIG(messagebox)
2692 Q_Q(QFileDialog);
2693 const QString message = mode == QFileDialog::Directory
2694 ? QFileDialog::tr(s: "%1\nDirectory not found.\n"
2695 "Please verify the correct directory name was given.")
2696 : QFileDialog::tr(s: "%1\nFile not found.\nPlease verify the "
2697 "correct file name was given.");
2698
2699 QMessageBox::warning(parent: q, title: q->windowTitle(), text: message.arg(a: fileName));
2700#endif // QT_CONFIG(messagebox)
2701}
2702
2703/*!
2704 \reimp
2705*/
2706void QFileDialog::accept()
2707{
2708 Q_D(QFileDialog);
2709 if (!d->usingWidgets()) {
2710 const QList<QUrl> urls = selectedUrls();
2711 if (urls.isEmpty())
2712 return;
2713 d->emitUrlsSelected(files: urls);
2714 if (urls.size() == 1)
2715 d->emitUrlSelected(file: urls.first());
2716 QDialog::accept();
2717 return;
2718 }
2719
2720 const QStringList files = selectedFiles();
2721 if (files.isEmpty())
2722 return;
2723 QString lineEditText = d->lineEdit()->text();
2724 // "hidden feature" type .. and then enter, and it will move up a dir
2725 // special case for ".."
2726 if (lineEditText == ".."_L1) {
2727 d->navigateToParent();
2728 const QSignalBlocker blocker(d->qFileDialogUi->fileNameEdit);
2729 d->lineEdit()->selectAll();
2730 return;
2731 }
2732
2733 const auto mode = fileMode();
2734 switch (mode) {
2735 case Directory: {
2736 QString fn = files.first();
2737 QFileInfo info(fn);
2738 if (!info.exists())
2739 info = QFileInfo(d->getEnvironmentVariable(string: fn));
2740 if (!info.exists()) {
2741 d->itemNotFound(fileName: info.fileName(), mode);
2742 return;
2743 }
2744 if (info.isDir()) {
2745 d->emitFilesSelected(files);
2746 QDialog::accept();
2747 }
2748 return;
2749 }
2750
2751 case AnyFile: {
2752 QString fn = files.first();
2753 QFileInfo info(fn);
2754 if (info.isDir()) {
2755 setDirectory(info.absoluteFilePath());
2756 return;
2757 }
2758
2759 if (!info.exists()) {
2760 const long maxNameLength = d->maxNameLength(path: info.path());
2761 if (maxNameLength >= 0 && info.fileName().size() > maxNameLength)
2762 return;
2763 }
2764
2765 // check if we have to ask for permission to overwrite the file
2766 if (!info.exists() || testOption(option: DontConfirmOverwrite) || acceptMode() == AcceptOpen) {
2767 d->emitFilesSelected(files: QStringList(fn));
2768 QDialog::accept();
2769 } else {
2770 if (d->itemAlreadyExists(fileName: info.fileName())) {
2771 d->emitFilesSelected(files: QStringList(fn));
2772 QDialog::accept();
2773 }
2774 }
2775 return;
2776 }
2777
2778 case ExistingFile:
2779 case ExistingFiles:
2780 for (const auto &file : files) {
2781 QFileInfo info(file);
2782 if (!info.exists())
2783 info = QFileInfo(d->getEnvironmentVariable(string: file));
2784 if (!info.exists()) {
2785 d->itemNotFound(fileName: info.fileName(), mode);
2786 return;
2787 }
2788 if (info.isDir()) {
2789 setDirectory(info.absoluteFilePath());
2790 d->lineEdit()->clear();
2791 return;
2792 }
2793 }
2794 d->emitFilesSelected(files);
2795 QDialog::accept();
2796 return;
2797 }
2798}
2799
2800#if QT_CONFIG(settings)
2801void QFileDialogPrivate::saveSettings()
2802{
2803 Q_Q(QFileDialog);
2804 QSettings settings(QSettings::UserScope, u"QtProject"_s);
2805 settings.beginGroup(prefix: "FileDialog");
2806
2807 if (usingWidgets()) {
2808 settings.setValue(key: "sidebarWidth", value: qFileDialogUi->splitter->sizes().constFirst());
2809 settings.setValue(key: "shortcuts", value: QUrl::toStringList(uris: qFileDialogUi->sidebar->urls()));
2810 settings.setValue(key: "treeViewHeader", value: qFileDialogUi->treeView->header()->saveState());
2811 }
2812 QStringList historyUrls;
2813 const QStringList history = q->history();
2814 historyUrls.reserve(asize: history.size());
2815 for (const QString &path : history)
2816 historyUrls << QUrl::fromLocalFile(localfile: path).toString();
2817 settings.setValue(key: "history", value: historyUrls);
2818 settings.setValue(key: "lastVisited", value: lastVisitedDir()->toString());
2819 const QMetaEnum &viewModeMeta = q->metaObject()->enumerator(index: q->metaObject()->indexOfEnumerator(name: "ViewMode"));
2820 settings.setValue(key: "viewMode", value: QLatin1StringView(viewModeMeta.key(index: q->viewMode())));
2821 settings.setValue(key: "qtVersion", QT_VERSION_STR ""_L1);
2822}
2823
2824bool QFileDialogPrivate::restoreFromSettings()
2825{
2826 Q_Q(QFileDialog);
2827 QSettings settings(QSettings::UserScope, u"QtProject"_s);
2828 if (!settings.childGroups().contains(str: "FileDialog"_L1))
2829 return false;
2830 settings.beginGroup(prefix: "FileDialog");
2831
2832 q->setDirectoryUrl(lastVisitedDir()->isEmpty() ? settings.value(key: "lastVisited").toUrl() : *lastVisitedDir());
2833
2834 QByteArray viewModeStr = settings.value(key: "viewMode").toString().toLatin1();
2835 const QMetaEnum &viewModeMeta = q->metaObject()->enumerator(index: q->metaObject()->indexOfEnumerator(name: "ViewMode"));
2836 bool ok = false;
2837 int viewMode = viewModeMeta.keyToValue(key: viewModeStr.constData(), ok: &ok);
2838 if (!ok)
2839 viewMode = QFileDialog::List;
2840 q->setViewMode(static_cast<QFileDialog::ViewMode>(viewMode));
2841
2842 sidebarUrls = QUrl::fromStringList(uris: settings.value(key: "shortcuts").toStringList());
2843 headerData = settings.value(key: "treeViewHeader").toByteArray();
2844
2845 if (!usingWidgets())
2846 return true;
2847
2848 QStringList history;
2849 const auto urlStrings = settings.value(key: "history").toStringList();
2850 for (const QString &urlStr : urlStrings) {
2851 QUrl url(urlStr);
2852 if (url.isLocalFile())
2853 history << url.toLocalFile();
2854 }
2855
2856 return restoreWidgetState(history, splitterPosition: settings.value(key: "sidebarWidth", defaultValue: -1).toInt());
2857}
2858#endif // settings
2859
2860bool QFileDialogPrivate::restoreWidgetState(QStringList &history, int splitterPosition)
2861{
2862 Q_Q(QFileDialog);
2863 if (splitterPosition >= 0) {
2864 QList<int> splitterSizes;
2865 splitterSizes.append(t: splitterPosition);
2866 splitterSizes.append(qFileDialogUi->splitter->widget(1)->sizeHint().width());
2867 qFileDialogUi->splitter->setSizes(splitterSizes);
2868 } else {
2869 if (!qFileDialogUi->splitter->restoreState(splitterState))
2870 return false;
2871 QList<int> list = qFileDialogUi->splitter->sizes();
2872 if (list.size() >= 2 && (list.at(i: 0) == 0 || list.at(i: 1) == 0)) {
2873 for (int i = 0; i < list.size(); ++i)
2874 list[i] = qFileDialogUi->splitter->widget(i)->sizeHint().width();
2875 qFileDialogUi->splitter->setSizes(list);
2876 }
2877 }
2878
2879 qFileDialogUi->sidebar->setUrls(sidebarUrls);
2880
2881 static const int MaxHistorySize = 5;
2882 if (history.size() > MaxHistorySize)
2883 history.erase(abegin: history.begin(), aend: history.end() - MaxHistorySize);
2884 q->setHistory(history);
2885
2886 QHeaderView *headerView = qFileDialogUi->treeView->header();
2887 if (!headerView->restoreState(state: headerData))
2888 return false;
2889
2890 QList<QAction*> actions = headerView->actions();
2891 QAbstractItemModel *abstractModel = model;
2892#if QT_CONFIG(proxymodel)
2893 if (proxyModel)
2894 abstractModel = proxyModel;
2895#endif
2896 const int total = qMin(a: abstractModel->columnCount(parent: QModelIndex()), b: int(actions.size() + 1));
2897 for (int i = 1; i < total; ++i)
2898 actions.at(i: i - 1)->setChecked(!headerView->isSectionHidden(logicalIndex: i));
2899
2900 return true;
2901}
2902
2903/*!
2904 \internal
2905
2906 Create widgets, layout and set default values
2907*/
2908void QFileDialogPrivate::init(const QFileDialogArgs &args)
2909{
2910 Q_Q(QFileDialog);
2911 if (!args.caption.isEmpty()) {
2912 useDefaultCaption = false;
2913 setWindowTitle = args.caption;
2914 q->setWindowTitle(args.caption);
2915 }
2916
2917 q->setAcceptMode(QFileDialog::AcceptOpen);
2918 nativeDialogInUse = platformFileDialogHelper() != nullptr;
2919 if (!nativeDialogInUse)
2920 createWidgets();
2921 q->setFileMode(QFileDialog::AnyFile);
2922 if (!args.filter.isEmpty())
2923 q->setNameFilter(args.filter);
2924 q->setDirectoryUrl(args.directory);
2925 if (args.directory.isLocalFile())
2926 q->selectFile(filename: args.selection);
2927 else
2928 q->selectUrl(url: args.directory);
2929
2930#if QT_CONFIG(settings)
2931 // Try to restore from the FileDialog settings group; if it fails, fall back
2932 // to the pre-5.5 QByteArray serialized settings.
2933 if (!restoreFromSettings()) {
2934 const QSettings settings(QSettings::UserScope, u"QtProject"_s);
2935 q->restoreState(state: settings.value(key: "Qt/filedialog").toByteArray());
2936 }
2937#endif
2938
2939#if defined(Q_EMBEDDED_SMALLSCREEN)
2940 qFileDialogUi->lookInLabel->setVisible(false);
2941 qFileDialogUi->fileNameLabel->setVisible(false);
2942 qFileDialogUi->fileTypeLabel->setVisible(false);
2943 qFileDialogUi->sidebar->hide();
2944#endif
2945
2946 const QSize sizeHint = q->sizeHint();
2947 if (sizeHint.isValid())
2948 q->resize(sizeHint);
2949}
2950
2951/*!
2952 \internal
2953
2954 Create the widgets, set properties and connections
2955*/
2956void QFileDialogPrivate::createWidgets()
2957{
2958 if (qFileDialogUi)
2959 return;
2960 Q_Q(QFileDialog);
2961
2962 // This function is sometimes called late (e.g as a fallback from setVisible). In that case we
2963 // need to ensure that the following UI code (setupUI in particular) doesn't reset any explicitly
2964 // set window state or geometry.
2965 QSize preSize = q->testAttribute(attribute: Qt::WA_Resized) ? q->size() : QSize();
2966 Qt::WindowStates preState = q->windowState();
2967
2968 model = new QFileSystemModel(q);
2969 model->setIconProvider(&defaultIconProvider);
2970 model->setFilter(options->filter());
2971 model->setObjectName("qt_filesystem_model"_L1);
2972 if (QPlatformFileDialogHelper *helper = platformFileDialogHelper())
2973 model->setNameFilterDisables(helper->defaultNameFilterDisables());
2974 else
2975 model->setNameFilterDisables(false);
2976 model->d_func()->disableRecursiveSort = true;
2977 QObjectPrivate::connect(sender: model, signal: &QFileSystemModel::fileRenamed,
2978 receiverPrivate: this, slot: &QFileDialogPrivate::fileRenamed);
2979 QObjectPrivate::connect(sender: model, signal: &QFileSystemModel::rootPathChanged,
2980 receiverPrivate: this, slot: &QFileDialogPrivate::pathChanged);
2981 QObjectPrivate::connect(sender: model, signal: &QFileSystemModel::rowsInserted,
2982 receiverPrivate: this, slot: &QFileDialogPrivate::rowsInserted);
2983 model->setReadOnly(false);
2984
2985 qFileDialogUi.reset(new Ui_QFileDialog());
2986 qFileDialogUi->setupUi(q);
2987
2988 QList<QUrl> initialBookmarks;
2989 initialBookmarks << QUrl("file:"_L1)
2990 << QUrl::fromLocalFile(localfile: QDir::homePath());
2991 qFileDialogUi->sidebar->setModelAndUrls(model, initialBookmarks);
2992 QObjectPrivate::connect(qFileDialogUi->sidebar, &QSidebar::goToUrl,
2993 this, &QFileDialogPrivate::goToUrl);
2994
2995 QObject::connect(qFileDialogUi->buttonBox, &QDialogButtonBox::accepted,
2996 q, &QFileDialog::accept);
2997 QObject::connect(qFileDialogUi->buttonBox, &QDialogButtonBox::rejected,
2998 q, &QFileDialog::reject);
2999
3000 qFileDialogUi->lookInCombo->setFileDialogPrivate(this);
3001 QObjectPrivate::connect(qFileDialogUi->lookInCombo, &QComboBox::textActivated,
3002 this, &QFileDialogPrivate::goToDirectory);
3003
3004 qFileDialogUi->lookInCombo->setInsertPolicy(QComboBox::NoInsert);
3005 qFileDialogUi->lookInCombo->setDuplicatesEnabled(false);
3006
3007 // filename
3008 qFileDialogUi->fileNameEdit->setFileDialogPrivate(this);
3009#ifndef QT_NO_SHORTCUT
3010 qFileDialogUi->fileNameLabel->setBuddy(qFileDialogUi->fileNameEdit);
3011#endif
3012#if QT_CONFIG(fscompleter)
3013 completer = new QFSCompleter(model, q);
3014 qFileDialogUi->fileNameEdit->setCompleter(completer);
3015#endif // QT_CONFIG(fscompleter)
3016
3017 qFileDialogUi->fileNameEdit->setInputMethodHints(Qt::ImhNoPredictiveText);
3018
3019 QObjectPrivate::connect(qFileDialogUi->fileNameEdit, &QLineEdit::textChanged,
3020 this, &QFileDialogPrivate::autoCompleteFileName);
3021 QObjectPrivate::connect(qFileDialogUi->fileNameEdit, &QLineEdit::textChanged,
3022 this, &QFileDialogPrivate::updateOkButton);
3023 QObject::connect(qFileDialogUi->fileNameEdit, &QLineEdit::returnPressed,
3024 q, &QFileDialog::accept);
3025
3026 // filetype
3027 qFileDialogUi->fileTypeCombo->setDuplicatesEnabled(false);
3028 qFileDialogUi->fileTypeCombo->setSizeAdjustPolicy(QComboBox::AdjustToContentsOnFirstShow);
3029 qFileDialogUi->fileTypeCombo->setSizePolicy(QSizePolicy::Expanding, QSizePolicy::Fixed);
3030 QObjectPrivate::connect(qFileDialogUi->fileTypeCombo, &QComboBox::activated,
3031 this, &QFileDialogPrivate::useNameFilter);
3032 QObject::connect(qFileDialogUi->fileTypeCombo, &QComboBox::textActivated,
3033 q, &QFileDialog::filterSelected);
3034
3035 qFileDialogUi->listView->setFileDialogPrivate(this);
3036 qFileDialogUi->listView->setModel(model);
3037 QObjectPrivate::connect(qFileDialogUi->listView, &QAbstractItemView::activated,
3038 this, &QFileDialogPrivate::enterDirectory);
3039 QObjectPrivate::connect(qFileDialogUi->listView, &QAbstractItemView::customContextMenuRequested,
3040 this, &QFileDialogPrivate::showContextMenu);
3041#ifndef QT_NO_SHORTCUT
3042 QShortcut *shortcut = new QShortcut(QKeySequence::Delete, qFileDialogUi->listView);
3043 QObjectPrivate::connect(sender: shortcut, signal: &QShortcut::activated,
3044 receiverPrivate: this, slot: &QFileDialogPrivate::deleteCurrent);
3045#endif
3046
3047 qFileDialogUi->treeView->setFileDialogPrivate(this);
3048 qFileDialogUi->treeView->setModel(model);
3049 QHeaderView *treeHeader = qFileDialogUi->treeView->header();
3050 QFontMetrics fm(q->font());
3051 treeHeader->resizeSection(logicalIndex: 0, size: fm.horizontalAdvance("wwwwwwwwwwwwwwwwwwwwwwwwww"_L1));
3052 treeHeader->resizeSection(logicalIndex: 1, size: fm.horizontalAdvance("128.88 GB"_L1));
3053 treeHeader->resizeSection(logicalIndex: 2, size: fm.horizontalAdvance("mp3Folder"_L1));
3054 treeHeader->resizeSection(logicalIndex: 3, size: fm.horizontalAdvance("10/29/81 02:02PM"_L1));
3055 treeHeader->setContextMenuPolicy(Qt::ActionsContextMenu);
3056
3057 QActionGroup *showActionGroup = new QActionGroup(q);
3058 showActionGroup->setExclusive(false);
3059 QObjectPrivate::connect(sender: showActionGroup, signal: &QActionGroup::triggered,
3060 receiverPrivate: this, slot: &QFileDialogPrivate::showHeader);
3061
3062 QAbstractItemModel *abstractModel = model;
3063#if QT_CONFIG(proxymodel)
3064 if (proxyModel)
3065 abstractModel = proxyModel;
3066#endif
3067 for (int i = 1; i < abstractModel->columnCount(parent: QModelIndex()); ++i) {
3068 QAction *showHeader = new QAction(showActionGroup);
3069 showHeader->setCheckable(true);
3070 showHeader->setChecked(true);
3071 treeHeader->addAction(action: showHeader);
3072 }
3073
3074 QScopedPointer<QItemSelectionModel> selModel(qFileDialogUi->treeView->selectionModel());
3075 qFileDialogUi->treeView->setSelectionModel(qFileDialogUi->listView->selectionModel());
3076
3077 QObjectPrivate::connect(qFileDialogUi->treeView, &QAbstractItemView::activated,
3078 this, &QFileDialogPrivate::enterDirectory);
3079 QObjectPrivate::connect(qFileDialogUi->treeView, &QAbstractItemView::customContextMenuRequested,
3080 this, &QFileDialogPrivate::showContextMenu);
3081#ifndef QT_NO_SHORTCUT
3082 shortcut = new QShortcut(QKeySequence::Delete, qFileDialogUi->treeView);
3083 QObjectPrivate::connect(sender: shortcut, signal: &QShortcut::activated,
3084 receiverPrivate: this, slot: &QFileDialogPrivate::deleteCurrent);
3085#endif
3086
3087 // Selections
3088 QItemSelectionModel *selections = qFileDialogUi->listView->selectionModel();
3089 QObjectPrivate::connect(sender: selections, signal: &QItemSelectionModel::selectionChanged,
3090 receiverPrivate: this, slot: &QFileDialogPrivate::selectionChanged);
3091 QObjectPrivate::connect(sender: selections, signal: &QItemSelectionModel::currentChanged,
3092 receiverPrivate: this, slot: &QFileDialogPrivate::currentChanged);
3093 qFileDialogUi->splitter->setStretchFactor(qFileDialogUi->splitter->indexOf(qFileDialogUi->splitter->widget(1)), QSizePolicy::Expanding);
3094
3095 createToolButtons();
3096 createMenuActions();
3097
3098#if QT_CONFIG(settings)
3099 // Try to restore from the FileDialog settings group; if it fails, fall back
3100 // to the pre-5.5 QByteArray serialized settings.
3101 if (!restoreFromSettings()) {
3102 const QSettings settings(QSettings::UserScope, u"QtProject"_s);
3103 q->restoreState(state: settings.value(key: "Qt/filedialog").toByteArray());
3104 }
3105#endif
3106
3107 // Initial widget states from options
3108 q->setFileMode(static_cast<QFileDialog::FileMode>(options->fileMode()));
3109 q->setAcceptMode(static_cast<QFileDialog::AcceptMode>(options->acceptMode()));
3110 q->setViewMode(static_cast<QFileDialog::ViewMode>(options->viewMode()));
3111 q->setOptions(static_cast<QFileDialog::Options>(static_cast<int>(options->options())));
3112 if (!options->sidebarUrls().isEmpty())
3113 q->setSidebarUrls(options->sidebarUrls());
3114 q->setDirectoryUrl(options->initialDirectory());
3115#if QT_CONFIG(mimetype)
3116 if (!options->mimeTypeFilters().isEmpty())
3117 q->setMimeTypeFilters(options->mimeTypeFilters());
3118 else
3119#endif
3120 if (!options->nameFilters().isEmpty())
3121 q->setNameFilters(options->nameFilters());
3122 q->selectNameFilter(filter: options->initiallySelectedNameFilter());
3123 q->setDefaultSuffix(options->defaultSuffix());
3124 q->setHistory(options->history());
3125 const auto initiallySelectedFiles = options->initiallySelectedFiles();
3126 if (initiallySelectedFiles.size() == 1)
3127 q->selectFile(filename: initiallySelectedFiles.first().fileName());
3128 for (const QUrl &url : initiallySelectedFiles)
3129 q->selectUrl(url);
3130 lineEdit()->selectAll();
3131 updateOkButton();
3132 retranslateStrings();
3133 q->resize(preSize.isValid() ? preSize : q->sizeHint());
3134 q->setWindowState(preState);
3135}
3136
3137void QFileDialogPrivate::showHeader(QAction *action)
3138{
3139 Q_Q(QFileDialog);
3140 QActionGroup *actionGroup = qobject_cast<QActionGroup*>(object: q->sender());
3141 qFileDialogUi->treeView->header()->setSectionHidden(int(actionGroup->actions().indexOf(t: action) + 1),
3142 !action->isChecked());
3143}
3144
3145#if QT_CONFIG(proxymodel)
3146/*!
3147 Sets the model for the views to the given \a proxyModel. This is useful if you
3148 want to modify the underlying model; for example, to add columns, filter
3149 data or add drives.
3150
3151 Any existing proxy model is removed, but not deleted. The file dialog
3152 takes ownership of the \a proxyModel.
3153
3154 \sa proxyModel()
3155*/
3156void QFileDialog::setProxyModel(QAbstractProxyModel *proxyModel)
3157{
3158 Q_D(QFileDialog);
3159 if (!d->usingWidgets())
3160 return;
3161 if ((!proxyModel && !d->proxyModel)
3162 || (proxyModel == d->proxyModel))
3163 return;
3164
3165 QModelIndex idx = d->rootIndex();
3166 if (d->proxyModel)
3167 QObjectPrivate::disconnect(sender: d->proxyModel, signal: &QAbstractProxyModel::rowsInserted,
3168 receiverPrivate: d, slot: &QFileDialogPrivate::rowsInserted);
3169 else
3170 QObjectPrivate::disconnect(sender: d->model, signal: &QAbstractItemModel::rowsInserted,
3171 receiverPrivate: d, slot: &QFileDialogPrivate::rowsInserted);
3172
3173 if (proxyModel != nullptr) {
3174 proxyModel->setParent(this);
3175 d->proxyModel = proxyModel;
3176 proxyModel->setSourceModel(d->model);
3177 d->qFileDialogUi->listView->setModel(d->proxyModel);
3178 d->qFileDialogUi->treeView->setModel(d->proxyModel);
3179#if QT_CONFIG(fscompleter)
3180 d->completer->setModel(d->proxyModel);
3181 d->completer->proxyModel = d->proxyModel;
3182#endif
3183 QObjectPrivate::connect(sender: d->proxyModel, signal: &QAbstractItemModel::rowsInserted,
3184 receiverPrivate: d, slot: &QFileDialogPrivate::rowsInserted);
3185 } else {
3186 d->proxyModel = nullptr;
3187 d->qFileDialogUi->listView->setModel(d->model);
3188 d->qFileDialogUi->treeView->setModel(d->model);
3189#if QT_CONFIG(fscompleter)
3190 d->completer->setModel(d->model);
3191 d->completer->sourceModel = d->model;
3192 d->completer->proxyModel = nullptr;
3193#endif
3194 QObjectPrivate::connect(sender: d->model, signal: &QAbstractItemModel::rowsInserted,
3195 receiverPrivate: d, slot: &QFileDialogPrivate::rowsInserted);
3196 }
3197 QScopedPointer<QItemSelectionModel> selModel(d->qFileDialogUi->treeView->selectionModel());
3198 d->qFileDialogUi->treeView->setSelectionModel(d->qFileDialogUi->listView->selectionModel());
3199
3200 d->setRootIndex(idx);
3201
3202 // reconnect selection
3203 QItemSelectionModel *selections = d->qFileDialogUi->listView->selectionModel();
3204 QObjectPrivate::connect(sender: selections, signal: &QItemSelectionModel::selectionChanged,
3205 receiverPrivate: d, slot: &QFileDialogPrivate::selectionChanged);
3206 QObjectPrivate::connect(sender: selections, signal: &QItemSelectionModel::currentChanged,
3207 receiverPrivate: d, slot: &QFileDialogPrivate::currentChanged);
3208}
3209
3210/*!
3211 Returns the proxy model used by the file dialog. By default no proxy is set.
3212
3213 \sa setProxyModel()
3214*/
3215QAbstractProxyModel *QFileDialog::proxyModel() const
3216{
3217 Q_D(const QFileDialog);
3218 return d->proxyModel;
3219}
3220#endif // QT_CONFIG(proxymodel)
3221
3222/*!
3223 \internal
3224
3225 Create tool buttons, set properties and connections
3226*/
3227void QFileDialogPrivate::createToolButtons()
3228{
3229 Q_Q(QFileDialog);
3230 qFileDialogUi->backButton->setIcon(q->style()->standardIcon(standardIcon: QStyle::SP_ArrowBack, option: nullptr, widget: q));
3231 qFileDialogUi->backButton->setAutoRaise(true);
3232 qFileDialogUi->backButton->setEnabled(false);
3233 QObjectPrivate::connect(qFileDialogUi->backButton, &QPushButton::clicked,
3234 this, &QFileDialogPrivate::navigateBackward);
3235
3236 qFileDialogUi->forwardButton->setIcon(q->style()->standardIcon(standardIcon: QStyle::SP_ArrowForward, option: nullptr, widget: q));
3237 qFileDialogUi->forwardButton->setAutoRaise(true);
3238 qFileDialogUi->forwardButton->setEnabled(false);
3239 QObjectPrivate::connect(qFileDialogUi->forwardButton, &QPushButton::clicked,
3240 this, &QFileDialogPrivate::navigateForward);
3241
3242 qFileDialogUi->toParentButton->setIcon(q->style()->standardIcon(standardIcon: QStyle::SP_FileDialogToParent, option: nullptr, widget: q));
3243 qFileDialogUi->toParentButton->setAutoRaise(true);
3244 qFileDialogUi->toParentButton->setEnabled(false);
3245 QObjectPrivate::connect(qFileDialogUi->toParentButton, &QPushButton::clicked,
3246 this, &QFileDialogPrivate::navigateToParent);
3247
3248 qFileDialogUi->listModeButton->setIcon(q->style()->standardIcon(standardIcon: QStyle::SP_FileDialogListView, option: nullptr, widget: q));
3249 qFileDialogUi->listModeButton->setAutoRaise(true);
3250 qFileDialogUi->listModeButton->setDown(true);
3251 QObjectPrivate::connect(qFileDialogUi->listModeButton, &QPushButton::clicked,
3252 this, &QFileDialogPrivate::showListView);
3253
3254 qFileDialogUi->detailModeButton->setIcon(q->style()->standardIcon(standardIcon: QStyle::SP_FileDialogDetailedView, option: nullptr, widget: q));
3255 qFileDialogUi->detailModeButton->setAutoRaise(true);
3256 QObjectPrivate::connect(qFileDialogUi->detailModeButton, &QPushButton::clicked,
3257 this, &QFileDialogPrivate::showDetailsView);
3258
3259 QSize toolSize(qFileDialogUi->fileNameEdit->sizeHint().height(), qFileDialogUi->fileNameEdit->sizeHint().height());
3260 qFileDialogUi->backButton->setFixedSize(toolSize);
3261 qFileDialogUi->listModeButton->setFixedSize(toolSize);
3262 qFileDialogUi->detailModeButton->setFixedSize(toolSize);
3263 qFileDialogUi->forwardButton->setFixedSize(toolSize);
3264 qFileDialogUi->toParentButton->setFixedSize(toolSize);
3265
3266 qFileDialogUi->newFolderButton->setIcon(q->style()->standardIcon(standardIcon: QStyle::SP_FileDialogNewFolder, option: nullptr, widget: q));
3267 qFileDialogUi->newFolderButton->setFixedSize(toolSize);
3268 qFileDialogUi->newFolderButton->setAutoRaise(true);
3269 qFileDialogUi->newFolderButton->setEnabled(false);
3270 QObjectPrivate::connect(qFileDialogUi->newFolderButton, &QPushButton::clicked,
3271 this, &QFileDialogPrivate::createDirectory);
3272}
3273
3274/*!
3275 \internal
3276
3277 Create actions which will be used in the right click.
3278*/
3279void QFileDialogPrivate::createMenuActions()
3280{
3281 Q_Q(QFileDialog);
3282
3283 QAction *goHomeAction = new QAction(q);
3284#ifndef QT_NO_SHORTCUT
3285 goHomeAction->setShortcut(Qt::CTRL | Qt::SHIFT | Qt::Key_H);
3286#endif
3287 QObjectPrivate::connect(sender: goHomeAction, signal: &QAction::triggered,
3288 receiverPrivate: this, slot: &QFileDialogPrivate::goHome);
3289 q->addAction(action: goHomeAction);
3290
3291 // ### TODO add Desktop & Computer actions
3292
3293 QAction *goToParent = new QAction(q);
3294 goToParent->setObjectName("qt_goto_parent_action"_L1);
3295#ifndef QT_NO_SHORTCUT
3296 goToParent->setShortcut(Qt::CTRL | Qt::Key_Up);
3297#endif
3298 QObjectPrivate::connect(sender: goToParent, signal: &QAction::triggered,
3299 receiverPrivate: this, slot: &QFileDialogPrivate::navigateToParent);
3300 q->addAction(action: goToParent);
3301
3302 renameAction = new QAction(q);
3303 renameAction->setEnabled(false);
3304 renameAction->setObjectName("qt_rename_action"_L1);
3305 QObjectPrivate::connect(sender: renameAction, signal: &QAction::triggered,
3306 receiverPrivate: this, slot: &QFileDialogPrivate::renameCurrent);
3307
3308 deleteAction = new QAction(q);
3309 deleteAction->setEnabled(false);
3310 deleteAction->setObjectName("qt_delete_action"_L1);
3311 QObjectPrivate::connect(sender: deleteAction, signal: &QAction::triggered,
3312 receiverPrivate: this, slot: &QFileDialogPrivate::deleteCurrent);
3313
3314 showHiddenAction = new QAction(q);
3315 showHiddenAction->setObjectName("qt_show_hidden_action"_L1);
3316 showHiddenAction->setCheckable(true);
3317 QObjectPrivate::connect(sender: showHiddenAction, signal: &QAction::triggered,
3318 receiverPrivate: this, slot: &QFileDialogPrivate::showHidden);
3319
3320 newFolderAction = new QAction(q);
3321 newFolderAction->setObjectName("qt_new_folder_action"_L1);
3322 QObjectPrivate::connect(sender: newFolderAction, signal: &QAction::triggered,
3323 receiverPrivate: this, slot: &QFileDialogPrivate::createDirectory);
3324}
3325
3326void QFileDialogPrivate::goHome()
3327{
3328 Q_Q(QFileDialog);
3329 q->setDirectory(QDir::homePath());
3330}
3331
3332
3333void QFileDialogPrivate::saveHistorySelection()
3334{
3335 if (qFileDialogUi.isNull() || currentHistoryLocation < 0 || currentHistoryLocation >= currentHistory.size())
3336 return;
3337 auto &item = currentHistory[currentHistoryLocation];
3338 item.selection.clear();
3339 const auto selectedIndexes = qFileDialogUi->listView->selectionModel()->selectedRows();
3340 for (const auto &index : selectedIndexes)
3341 item.selection.append(QPersistentModelIndex(index));
3342}
3343
3344/*!
3345 \internal
3346
3347 Update history with new path, buttons, and combo
3348*/
3349void QFileDialogPrivate::pathChanged(const QString &newPath)
3350{
3351 Q_Q(QFileDialog);
3352 qFileDialogUi->toParentButton->setEnabled(QFileInfo::exists(file: model->rootPath()));
3353 qFileDialogUi->sidebar->selectUrl(QUrl::fromLocalFile(localfile: newPath));
3354 q->setHistory(qFileDialogUi->lookInCombo->history());
3355
3356 const QString newNativePath = QDir::toNativeSeparators(pathName: newPath);
3357
3358 // equal paths indicate this was invoked by _q_navigateBack/Forward()
3359 if (currentHistoryLocation < 0 || currentHistory.value(i: currentHistoryLocation).path != newNativePath) {
3360 if (currentHistoryLocation >= 0)
3361 saveHistorySelection();
3362 while (currentHistoryLocation >= 0 && currentHistoryLocation + 1 < currentHistory.size()) {
3363 currentHistory.removeLast();
3364 }
3365 currentHistory.append(t: {.path: newNativePath, .selection: PersistentModelIndexList()});
3366 ++currentHistoryLocation;
3367 }
3368 qFileDialogUi->forwardButton->setEnabled(currentHistory.size() - currentHistoryLocation > 1);
3369 qFileDialogUi->backButton->setEnabled(currentHistoryLocation > 0);
3370}
3371
3372void QFileDialogPrivate::navigate(HistoryItem &historyItem)
3373{
3374 Q_Q(QFileDialog);
3375 q->setDirectory(historyItem.path);
3376 // Restore selection unless something has changed in the file system
3377 if (qFileDialogUi.isNull() || historyItem.selection.isEmpty())
3378 return;
3379 if (std::any_of(first: historyItem.selection.cbegin(), last: historyItem.selection.cend(),
3380 pred: [](const QPersistentModelIndex &i) { return !i.isValid(); })) {
3381 historyItem.selection.clear();
3382 return;
3383 }
3384
3385 QAbstractItemView *view = q->viewMode() == QFileDialog::List
3386 ? static_cast<QAbstractItemView *>(qFileDialogUi->listView)
3387 : static_cast<QAbstractItemView *>(qFileDialogUi->treeView);
3388 auto selectionModel = view->selectionModel();
3389 const QItemSelectionModel::SelectionFlags flags = QItemSelectionModel::Select
3390 | QItemSelectionModel::Rows;
3391 selectionModel->select(historyItem.selection.constFirst(),
3392 flags | QItemSelectionModel::Clear | QItemSelectionModel::Current);
3393 auto it = historyItem.selection.cbegin() + 1;
3394 const auto end = historyItem.selection.cend();
3395 for (; it != end; ++it)
3396 selectionModel->select(*it, flags);
3397
3398 view->scrollTo(index: historyItem.selection.constFirst());
3399}
3400
3401/*!
3402 \internal
3403
3404 Navigates to the last directory viewed in the dialog.
3405*/
3406void QFileDialogPrivate::navigateBackward()
3407{
3408 if (!currentHistory.isEmpty() && currentHistoryLocation > 0) {
3409 saveHistorySelection();
3410 navigate(historyItem&: currentHistory[--currentHistoryLocation]);
3411 }
3412}
3413
3414/*!
3415 \internal
3416
3417 Navigates to the last directory viewed in the dialog.
3418*/
3419void QFileDialogPrivate::navigateForward()
3420{
3421 if (!currentHistory.isEmpty() && currentHistoryLocation < currentHistory.size() - 1) {
3422 saveHistorySelection();
3423 navigate(historyItem&: currentHistory[++currentHistoryLocation]);
3424 }
3425}
3426
3427/*!
3428 \internal
3429
3430 Navigates to the parent directory of the currently displayed directory
3431 in the dialog.
3432*/
3433void QFileDialogPrivate::navigateToParent()
3434{
3435 Q_Q(QFileDialog);
3436 QDir dir(model->rootDirectory());
3437 QString newDirectory;
3438 if (dir.isRoot()) {
3439 newDirectory = model->myComputer().toString();
3440 } else {
3441 dir.cdUp();
3442 newDirectory = dir.absolutePath();
3443 }
3444 q->setDirectory(newDirectory);
3445 emit q->directoryEntered(directory: newDirectory);
3446}
3447
3448/*!
3449 \internal
3450
3451 Creates a new directory, first asking the user for a suitable name.
3452*/
3453void QFileDialogPrivate::createDirectory()
3454{
3455 Q_Q(QFileDialog);
3456 qFileDialogUi->listView->clearSelection();
3457
3458 QString newFolderString = QFileDialog::tr(s: "New Folder");
3459 QString folderName = newFolderString;
3460 QString prefix = q->directory().absolutePath() + QDir::separator();
3461 if (QFile::exists(fileName: prefix + folderName)) {
3462 qlonglong suffix = 2;
3463 while (QFile::exists(fileName: prefix + folderName)) {
3464 folderName = newFolderString + QString::number(suffix++);
3465 }
3466 }
3467
3468 QModelIndex parent = rootIndex();
3469 QModelIndex index = model->mkdir(parent, name: folderName);
3470 if (!index.isValid())
3471 return;
3472
3473 index = select(index);
3474 if (index.isValid()) {
3475 qFileDialogUi->treeView->setCurrentIndex(index);
3476 currentView()->edit(index);
3477 }
3478}
3479
3480void QFileDialogPrivate::showListView()
3481{
3482 qFileDialogUi->listModeButton->setDown(true);
3483 qFileDialogUi->detailModeButton->setDown(false);
3484 qFileDialogUi->treeView->hide();
3485 qFileDialogUi->listView->show();
3486 qFileDialogUi->stackedWidget->setCurrentWidget(qFileDialogUi->listView->parentWidget());
3487 qFileDialogUi->listView->doItemsLayout();
3488}
3489
3490void QFileDialogPrivate::showDetailsView()
3491{
3492 qFileDialogUi->listModeButton->setDown(false);
3493 qFileDialogUi->detailModeButton->setDown(true);
3494 qFileDialogUi->listView->hide();
3495 qFileDialogUi->treeView->show();
3496 qFileDialogUi->stackedWidget->setCurrentWidget(qFileDialogUi->treeView->parentWidget());
3497 qFileDialogUi->treeView->doItemsLayout();
3498}
3499
3500/*!
3501 \internal
3502
3503 Show the context menu for the file/dir under position
3504*/
3505void QFileDialogPrivate::showContextMenu(const QPoint &position)
3506{
3507#if !QT_CONFIG(menu)
3508 Q_UNUSED(position);
3509#else
3510 Q_Q(QFileDialog);
3511 QAbstractItemView *view = nullptr;
3512 if (q->viewMode() == QFileDialog::Detail)
3513 view = qFileDialogUi->treeView;
3514 else
3515 view = qFileDialogUi->listView;
3516 QModelIndex index = view->indexAt(point: position);
3517 index = mapToSource(index: index.sibling(arow: index.row(), acolumn: 0));
3518
3519 QMenu *menu = new QMenu(view);
3520 menu->setAttribute(Qt::WA_DeleteOnClose);
3521
3522 if (index.isValid()) {
3523 // file context menu
3524 const bool ro = model && model->isReadOnly();
3525 QFile::Permissions p(index.parent().data(arole: QFileSystemModel::FilePermissions).toInt());
3526 renameAction->setEnabled(!ro && p & QFile::WriteUser);
3527 menu->addAction(action: renameAction);
3528 deleteAction->setEnabled(!ro && p & QFile::WriteUser);
3529 menu->addAction(action: deleteAction);
3530 menu->addSeparator();
3531 }
3532 menu->addAction(action: showHiddenAction);
3533 if (qFileDialogUi->newFolderButton->isVisible()) {
3534 newFolderAction->setEnabled(qFileDialogUi->newFolderButton->isEnabled());
3535 menu->addAction(action: newFolderAction);
3536 }
3537 menu->popup(pos: view->viewport()->mapToGlobal(position));
3538
3539#endif // QT_CONFIG(menu)
3540}
3541
3542/*!
3543 \internal
3544*/
3545void QFileDialogPrivate::renameCurrent()
3546{
3547 Q_Q(QFileDialog);
3548 QModelIndex index = qFileDialogUi->listView->currentIndex();
3549 index = index.sibling(arow: index.row(), acolumn: 0);
3550 if (q->viewMode() == QFileDialog::List)
3551 qFileDialogUi->listView->edit(index);
3552 else
3553 qFileDialogUi->treeView->edit(index);
3554}
3555
3556bool QFileDialogPrivate::removeDirectory(const QString &path)
3557{
3558 QModelIndex modelIndex = model->index(path);
3559 return model->remove(index: modelIndex);
3560}
3561
3562/*!
3563 \internal
3564
3565 Deletes the currently selected item in the dialog.
3566*/
3567void QFileDialogPrivate::deleteCurrent()
3568{
3569 if (model->isReadOnly())
3570 return;
3571
3572 const QModelIndexList list = qFileDialogUi->listView->selectionModel()->selectedRows();
3573 for (auto it = list.crbegin(), end = list.crend(); it != end; ++it) {
3574 QPersistentModelIndex index = *it;
3575 if (index == qFileDialogUi->listView->rootIndex())
3576 continue;
3577
3578 index = mapToSource(index: index.sibling(row: index.row(), column: 0));
3579 if (!index.isValid())
3580 continue;
3581
3582 QString fileName = index.data(role: QFileSystemModel::FileNameRole).toString();
3583 QString filePath = index.data(role: QFileSystemModel::FilePathRole).toString();
3584
3585 QFile::Permissions p(index.parent().data(arole: QFileSystemModel::FilePermissions).toInt());
3586#if QT_CONFIG(messagebox)
3587 Q_Q(QFileDialog);
3588 if (!(p & QFile::WriteUser) && (QMessageBox::warning(parent: q_func(), title: QFileDialog::tr(s: "Delete"),
3589 text: QFileDialog::tr(s: "'%1' is write protected.\nDo you want to delete it anyway?")
3590 .arg(a: fileName),
3591 buttons: QMessageBox::Yes | QMessageBox::No, defaultButton: QMessageBox::No) == QMessageBox::No))
3592 return;
3593 else if (QMessageBox::warning(parent: q_func(), title: QFileDialog::tr(s: "Delete"),
3594 text: QFileDialog::tr(s: "Are you sure you want to delete '%1'?")
3595 .arg(a: fileName),
3596 buttons: QMessageBox::Yes | QMessageBox::No, defaultButton: QMessageBox::No) == QMessageBox::No)
3597 return;
3598
3599 // the event loop has run, we have to validate if the index is valid because the model might have removed it.
3600 if (!index.isValid())
3601 return;
3602
3603#else
3604 if (!(p & QFile::WriteUser))
3605 return;
3606#endif // QT_CONFIG(messagebox)
3607
3608 if (model->isDir(index) && !model->fileInfo(index).isSymLink()) {
3609 if (!removeDirectory(path: filePath)) {
3610#if QT_CONFIG(messagebox)
3611 QMessageBox::warning(parent: q, title: q->windowTitle(),
3612 text: QFileDialog::tr(s: "Could not delete directory."));
3613#endif
3614 }
3615 } else {
3616 model->remove(index);
3617 }
3618 }
3619}
3620
3621void QFileDialogPrivate::autoCompleteFileName(const QString &text)
3622{
3623 if (text.startsWith(s: "//"_L1) || text.startsWith(c: u'\\')) {
3624 qFileDialogUi->listView->selectionModel()->clearSelection();
3625 return;
3626 }
3627
3628 const QStringList multipleFiles = typedFiles();
3629 if (multipleFiles.size() > 0) {
3630 QModelIndexList oldFiles = qFileDialogUi->listView->selectionModel()->selectedRows();
3631 QList<QModelIndex> newFiles;
3632 for (const auto &file : multipleFiles) {
3633 QModelIndex idx = model->index(path: file);
3634 if (oldFiles.removeAll(t: idx) == 0)
3635 newFiles.append(t: idx);
3636 }
3637 for (const auto &newFile : std::as_const(t&: newFiles))
3638 select(index: newFile);
3639 if (lineEdit()->hasFocus()) {
3640 auto *sm = qFileDialogUi->listView->selectionModel();
3641 for (const auto &oldFile : std::as_const(oldFiles))
3642 sm->select(oldFile, QItemSelectionModel::Toggle | QItemSelectionModel::Rows);
3643 }
3644 }
3645}
3646
3647/*!
3648 \internal
3649*/
3650void QFileDialogPrivate::updateOkButton()
3651{
3652 Q_Q(QFileDialog);
3653 QPushButton *button = qFileDialogUi->buttonBox->button((q->acceptMode() == QFileDialog::AcceptOpen)
3654 ? QDialogButtonBox::Open : QDialogButtonBox::Save);
3655 if (!button)
3656 return;
3657 const QFileDialog::FileMode fileMode = q->fileMode();
3658
3659 bool enableButton = true;
3660 bool isOpenDirectory = false;
3661
3662 const QStringList files = q->selectedFiles();
3663 QString lineEditText = lineEdit()->text();
3664
3665 if (lineEditText.startsWith(s: "//"_L1) || lineEditText.startsWith(c: u'\\')) {
3666 button->setEnabled(true);
3667 updateOkButtonText();
3668 return;
3669 }
3670
3671 if (files.isEmpty()) {
3672 enableButton = false;
3673 } else if (lineEditText == ".."_L1) {
3674 isOpenDirectory = true;
3675 } else {
3676 switch (fileMode) {
3677 case QFileDialog::Directory: {
3678 QString fn = files.first();
3679 QModelIndex idx = model->index(path: fn);
3680 if (!idx.isValid())
3681 idx = model->index(path: getEnvironmentVariable(string: fn));
3682 if (!idx.isValid() || !model->isDir(index: idx))
3683 enableButton = false;
3684 break;
3685 }
3686 case QFileDialog::AnyFile: {
3687 QString fn = files.first();
3688 QFileInfo info(fn);
3689 QModelIndex idx = model->index(path: fn);
3690 QString fileDir;
3691 QString fileName;
3692 if (info.isDir()) {
3693 fileDir = info.canonicalFilePath();
3694 } else {
3695 fileDir = fn.mid(position: 0, n: fn.lastIndexOf(c: u'/'));
3696 fileName = fn.mid(position: fileDir.size() + 1);
3697 }
3698 if (lineEditText.contains(s: ".."_L1)) {
3699 fileDir = info.canonicalFilePath();
3700 fileName = info.fileName();
3701 }
3702
3703 if (fileDir == q->directory().canonicalPath() && fileName.isEmpty()) {
3704 enableButton = false;
3705 break;
3706 }
3707 if (idx.isValid() && model->isDir(index: idx)) {
3708 isOpenDirectory = true;
3709 enableButton = true;
3710 break;
3711 }
3712 if (!idx.isValid()) {
3713 const long maxLength = maxNameLength(path: fileDir);
3714 enableButton = maxLength < 0 || fileName.size() <= maxLength;
3715 }
3716 break;
3717 }
3718 case QFileDialog::ExistingFile:
3719 case QFileDialog::ExistingFiles:
3720 for (const auto &file : files) {
3721 QModelIndex idx = model->index(path: file);
3722 if (!idx.isValid())
3723 idx = model->index(path: getEnvironmentVariable(string: file));
3724 if (!idx.isValid()) {
3725 enableButton = false;
3726 break;
3727 }
3728 if (idx.isValid() && model->isDir(index: idx)) {
3729 isOpenDirectory = true;
3730 break;
3731 }
3732 }
3733 break;
3734 default:
3735 break;
3736 }
3737 }
3738
3739 button->setEnabled(enableButton);
3740 updateOkButtonText(saveAsOnFolder: isOpenDirectory);
3741}
3742
3743/*!
3744 \internal
3745*/
3746void QFileDialogPrivate::currentChanged(const QModelIndex &index)
3747{
3748 updateOkButton();
3749 emit q_func()->currentChanged(path: index.data(arole: QFileSystemModel::FilePathRole).toString());
3750}
3751
3752/*!
3753 \internal
3754
3755 This is called when the user double clicks on a file with the corresponding
3756 model item \a index.
3757*/
3758void QFileDialogPrivate::enterDirectory(const QModelIndex &index)
3759{
3760 Q_Q(QFileDialog);
3761 // My Computer or a directory
3762 QModelIndex sourceIndex = index.model() == proxyModel ? mapToSource(index) : index;
3763 QString path = sourceIndex.data(arole: QFileSystemModel::FilePathRole).toString();
3764 if (path.isEmpty() || model->isDir(index: sourceIndex)) {
3765 const QFileDialog::FileMode fileMode = q->fileMode();
3766 q->setDirectory(path);
3767 emit q->directoryEntered(directory: path);
3768 if (fileMode == QFileDialog::Directory) {
3769 // ### find out why you have to do both of these.
3770 lineEdit()->setText(QString());
3771 lineEdit()->clear();
3772 }
3773 } else {
3774 // Do not accept when shift-clicking to multi-select a file in environments with single-click-activation (KDE)
3775 if ((!q->style()->styleHint(stylehint: QStyle::SH_ItemView_ActivateItemOnSingleClick, opt: nullptr, widget: qFileDialogUi->treeView)
3776 || q->fileMode() != QFileDialog::ExistingFiles || !(QGuiApplication::keyboardModifiers() & Qt::CTRL))
3777 && index.model()->flags(index) & Qt::ItemIsEnabled) {
3778 q->accept();
3779 }
3780 }
3781}
3782
3783/*!
3784 \internal
3785
3786 Changes the file dialog's current directory to the one specified
3787 by \a path.
3788*/
3789void QFileDialogPrivate::goToDirectory(const QString &path)
3790{
3791 enum { UrlRole = Qt::UserRole + 1 };
3792
3793 #if QT_CONFIG(messagebox)
3794 Q_Q(QFileDialog);
3795#endif
3796 QModelIndex index = qFileDialogUi->lookInCombo->model()->index(qFileDialogUi->lookInCombo->currentIndex(),
3797 qFileDialogUi->lookInCombo->modelColumn(),
3798 qFileDialogUi->lookInCombo->rootModelIndex());
3799 QString path2 = path;
3800 if (!index.isValid())
3801 index = mapFromSource(index: model->index(path: getEnvironmentVariable(string: path)));
3802 else {
3803 path2 = index.data(arole: UrlRole).toUrl().toLocalFile();
3804 index = mapFromSource(index: model->index(path: path2));
3805 }
3806 QDir dir(path2);
3807 if (!dir.exists())
3808 dir.setPath(getEnvironmentVariable(string: path2));
3809
3810 if (dir.exists() || path2.isEmpty() || path2 == model->myComputer().toString()) {
3811 enterDirectory(index);
3812#if QT_CONFIG(messagebox)
3813 } else {
3814 QString message = QFileDialog::tr(s: "%1\nDirectory not found.\nPlease verify the "
3815 "correct directory name was given.");
3816 QMessageBox::warning(parent: q, title: q->windowTitle(), text: message.arg(a: path2));
3817#endif // QT_CONFIG(messagebox)
3818 }
3819}
3820
3821/*!
3822 \internal
3823
3824 Sets the current name filter to be nameFilter and
3825 update the qFileDialogUi->fileNameEdit when in AcceptSave mode with the new extension.
3826*/
3827void QFileDialogPrivate::useNameFilter(int index)
3828{
3829 QStringList nameFilters = options->nameFilters();
3830 if (index == nameFilters.size()) {
3831 QAbstractItemModel *comboModel = qFileDialogUi->fileTypeCombo->model();
3832 nameFilters.append(t: comboModel->index(row: comboModel->rowCount() - 1, column: 0).data().toString());
3833 options->setNameFilters(nameFilters);
3834 }
3835
3836 QString nameFilter = nameFilters.at(i: index);
3837 QStringList newNameFilters = QPlatformFileDialogHelper::cleanFilterList(filter: nameFilter);
3838 if (q_func()->acceptMode() == QFileDialog::AcceptSave) {
3839 QString newNameFilterExtension;
3840 if (newNameFilters.size() > 0)
3841 newNameFilterExtension = QFileInfo(newNameFilters.at(i: 0)).suffix();
3842
3843 QString fileName = lineEdit()->text();
3844 const QString fileNameExtension = QFileInfo(fileName).suffix();
3845 if (!fileNameExtension.isEmpty() && !newNameFilterExtension.isEmpty()) {
3846 const qsizetype fileNameExtensionLength = fileNameExtension.size();
3847 fileName.replace(i: fileName.size() - fileNameExtensionLength,
3848 len: fileNameExtensionLength, after: newNameFilterExtension);
3849 qFileDialogUi->listView->clearSelection();
3850 lineEdit()->setText(fileName);
3851 }
3852 }
3853
3854 model->setNameFilters(newNameFilters);
3855}
3856
3857/*!
3858 \internal
3859
3860 This is called when the model index corresponding to the current file is changed
3861 from \a index to \a current.
3862*/
3863void QFileDialogPrivate::selectionChanged()
3864{
3865 const QFileDialog::FileMode fileMode = q_func()->fileMode();
3866 const QModelIndexList indexes = qFileDialogUi->listView->selectionModel()->selectedRows();
3867 bool stripDirs = fileMode != QFileDialog::Directory;
3868
3869 QStringList allFiles;
3870 for (const auto &index : indexes) {
3871 if (stripDirs && model->isDir(mapToSource(index)))
3872 continue;
3873 allFiles.append(index.data().toString());
3874 }
3875 if (allFiles.size() > 1)
3876 for (qsizetype i = 0; i < allFiles.size(); ++i) {
3877 allFiles.replace(i, t: QString(u'"' + allFiles.at(i) + u'"'));
3878 }
3879
3880 QString finalFiles = allFiles.join(sep: u' ');
3881 if (!finalFiles.isEmpty() && !lineEdit()->hasFocus() && lineEdit()->isVisible())
3882 lineEdit()->setText(finalFiles);
3883 else
3884 updateOkButton();
3885}
3886
3887/*!
3888 \internal
3889
3890 Includes hidden files and directories in the items displayed in the dialog.
3891*/
3892void QFileDialogPrivate::showHidden()
3893{
3894 Q_Q(QFileDialog);
3895 QDir::Filters dirFilters = q->filter();
3896 dirFilters.setFlag(flag: QDir::Hidden, on: showHiddenAction->isChecked());
3897 q->setFilter(dirFilters);
3898}
3899
3900/*!
3901 \internal
3902
3903 When parent is root and rows have been inserted when none was there before
3904 then select the first one.
3905*/
3906void QFileDialogPrivate::rowsInserted(const QModelIndex &parent)
3907{
3908 if (!qFileDialogUi->treeView
3909 || parent != qFileDialogUi->treeView->rootIndex()
3910 || !qFileDialogUi->treeView->selectionModel()
3911 || qFileDialogUi->treeView->selectionModel()->hasSelection()
3912 || qFileDialogUi->treeView->model()->rowCount(parent) == 0)
3913 return;
3914}
3915
3916void QFileDialogPrivate::fileRenamed(const QString &path, const QString &oldName, const QString &newName)
3917{
3918 const QFileDialog::FileMode fileMode = q_func()->fileMode();
3919 if (fileMode == QFileDialog::Directory) {
3920 if (path == rootPath() && lineEdit()->text() == oldName)
3921 lineEdit()->setText(newName);
3922 }
3923}
3924
3925void QFileDialogPrivate::emitUrlSelected(const QUrl &file)
3926{
3927 Q_Q(QFileDialog);
3928 emit q->urlSelected(url: file);
3929 if (file.isLocalFile())
3930 emit q->fileSelected(file: file.toLocalFile());
3931}
3932
3933void QFileDialogPrivate::emitUrlsSelected(const QList<QUrl> &files)
3934{
3935 Q_Q(QFileDialog);
3936 emit q->urlsSelected(urls: files);
3937 QStringList localFiles;
3938 for (const QUrl &file : files)
3939 if (file.isLocalFile())
3940 localFiles.append(t: file.toLocalFile());
3941 if (!localFiles.isEmpty())
3942 emit q->filesSelected(files: localFiles);
3943}
3944
3945void QFileDialogPrivate::nativeCurrentChanged(const QUrl &file)
3946{
3947 Q_Q(QFileDialog);
3948 emit q->currentUrlChanged(url: file);
3949 if (file.isLocalFile())
3950 emit q->currentChanged(path: file.toLocalFile());
3951}
3952
3953void QFileDialogPrivate::nativeEnterDirectory(const QUrl &directory)
3954{
3955 Q_Q(QFileDialog);
3956 emit q->directoryUrlEntered(directory);
3957 if (!directory.isEmpty()) { // Windows native dialogs occasionally emit signals with empty strings.
3958 *lastVisitedDir() = directory;
3959 if (directory.isLocalFile())
3960 emit q->directoryEntered(directory: directory.toLocalFile());
3961 }
3962}
3963
3964/*!
3965 \internal
3966
3967 For the list and tree view watch keys to goto parent and back in the history
3968
3969 returns \c true if handled
3970*/
3971bool QFileDialogPrivate::itemViewKeyboardEvent(QKeyEvent *event) {
3972
3973#if QT_CONFIG(shortcut)
3974 Q_Q(QFileDialog);
3975 if (event->matches(key: QKeySequence::Cancel)) {
3976 q->reject();
3977 return true;
3978 }
3979#endif
3980 switch (event->key()) {
3981 case Qt::Key_Backspace:
3982 navigateToParent();
3983 return true;
3984 case Qt::Key_Back:
3985#ifdef QT_KEYPAD_NAVIGATION
3986 if (QApplicationPrivate::keypadNavigationEnabled())
3987 return false;
3988#endif
3989 case Qt::Key_Left:
3990 if (event->key() == Qt::Key_Back || event->modifiers() == Qt::AltModifier) {
3991 navigateBackward();
3992 return true;
3993 }
3994 break;
3995 default:
3996 break;
3997 }
3998 return false;
3999}
4000
4001QString QFileDialogPrivate::getEnvironmentVariable(const QString &string)
4002{
4003#ifdef Q_OS_UNIX
4004 if (string.size() > 1 && string.startsWith(c: u'$')) {
4005 return QString::fromLocal8Bit(ba: qgetenv(varName: QStringView{string}.mid(pos: 1).toLatin1().constData()));
4006 }
4007#else
4008 if (string.size() > 2 && string.startsWith(u'%') && string.endsWith(u'%')) {
4009 return QString::fromLocal8Bit(qgetenv(QStringView{string}.mid(1, string.size() - 2).toLatin1().constData()));
4010 }
4011#endif
4012 return string;
4013}
4014
4015void QFileDialogComboBox::setFileDialogPrivate(QFileDialogPrivate *d_pointer) {
4016 d_ptr = d_pointer;
4017 urlModel = new QUrlModel(this);
4018 urlModel->showFullPath = true;
4019 urlModel->setFileSystemModel(d_ptr->model);
4020 setModel(urlModel);
4021}
4022
4023void QFileDialogComboBox::showPopup()
4024{
4025 if (model()->rowCount() > 1)
4026 QComboBox::showPopup();
4027
4028 urlModel->setUrls(QList<QUrl>());
4029 QList<QUrl> list;
4030 QModelIndex idx = d_ptr->model->index(path: d_ptr->rootPath());
4031 while (idx.isValid()) {
4032 QUrl url = QUrl::fromLocalFile(localfile: idx.data(arole: QFileSystemModel::FilePathRole).toString());
4033 if (url.isValid())
4034 list.append(t: url);
4035 idx = idx.parent();
4036 }
4037 // add "my computer"
4038 list.append(t: QUrl("file:"_L1));
4039 urlModel->addUrls(urls: list, row: 0);
4040 idx = model()->index(row: model()->rowCount() - 1, column: 0);
4041
4042 // append history
4043 QList<QUrl> urls;
4044 for (int i = 0; i < m_history.size(); ++i) {
4045 QUrl path = QUrl::fromLocalFile(localfile: m_history.at(i));
4046 if (!urls.contains(t: path))
4047 urls.prepend(t: path);
4048 }
4049 if (urls.size() > 0) {
4050 model()->insertRow(arow: model()->rowCount());
4051 idx = model()->index(row: model()->rowCount()-1, column: 0);
4052 // ### TODO maybe add a horizontal line before this
4053 model()->setData(index: idx, value: QFileDialog::tr(s: "Recent Places"));
4054 QStandardItemModel *m = qobject_cast<QStandardItemModel*>(object: model());
4055 if (m) {
4056 Qt::ItemFlags flags = m->flags(index: idx);
4057 flags &= ~Qt::ItemIsEnabled;
4058 m->item(row: idx.row(), column: idx.column())->setFlags(flags);
4059 }
4060 urlModel->addUrls(urls, row: -1, move: false);
4061 }
4062 setCurrentIndex(0);
4063
4064 QComboBox::showPopup();
4065}
4066
4067// Exact same as QComboBox::paintEvent(), except we elide the text.
4068void QFileDialogComboBox::paintEvent(QPaintEvent *)
4069{
4070 QStylePainter painter(this);
4071 painter.setPen(palette().color(cr: QPalette::Text));
4072
4073 // draw the combobox frame, focusrect and selected etc.
4074 QStyleOptionComboBox opt;
4075 initStyleOption(option: &opt);
4076
4077 QRect editRect = style()->subControlRect(cc: QStyle::CC_ComboBox, opt: &opt,
4078 sc: QStyle::SC_ComboBoxEditField, widget: this);
4079 int size = editRect.width() - opt.iconSize.width() - 4;
4080 opt.currentText = opt.fontMetrics.elidedText(text: opt.currentText, mode: Qt::ElideMiddle, width: size);
4081 painter.drawComplexControl(cc: QStyle::CC_ComboBox, opt);
4082
4083 // draw the icon and text
4084 painter.drawControl(ce: QStyle::CE_ComboBoxLabel, opt);
4085}
4086
4087void QFileDialogListView::setFileDialogPrivate(QFileDialogPrivate *d_pointer)
4088{
4089 d_ptr = d_pointer;
4090 setSelectionBehavior(QAbstractItemView::SelectRows);
4091 setWrapping(true);
4092 setResizeMode(QListView::Adjust);
4093 setEditTriggers(QAbstractItemView::EditKeyPressed);
4094 setContextMenuPolicy(Qt::CustomContextMenu);
4095#if QT_CONFIG(draganddrop)
4096 setDragDropMode(QAbstractItemView::InternalMove);
4097#endif
4098}
4099
4100QSize QFileDialogListView::sizeHint() const
4101{
4102 int height = qMax(a: 10, b: sizeHintForRow(row: 0));
4103 return QSize(QListView::sizeHint().width() * 2, height * 30);
4104}
4105
4106void QFileDialogListView::keyPressEvent(QKeyEvent *e)
4107{
4108#ifdef QT_KEYPAD_NAVIGATION
4109 if (QApplication::navigationMode() == Qt::NavigationModeKeypadDirectional) {
4110 QListView::keyPressEvent(e);
4111 return;
4112 }
4113#endif // QT_KEYPAD_NAVIGATION
4114
4115 if (!d_ptr->itemViewKeyboardEvent(event: e))
4116 QListView::keyPressEvent(event: e);
4117 e->accept();
4118}
4119
4120void QFileDialogTreeView::setFileDialogPrivate(QFileDialogPrivate *d_pointer)
4121{
4122 d_ptr = d_pointer;
4123 setSelectionBehavior(QAbstractItemView::SelectRows);
4124 setRootIsDecorated(false);
4125 setItemsExpandable(false);
4126 setSortingEnabled(true);
4127 header()->setSortIndicator(logicalIndex: 0, order: Qt::AscendingOrder);
4128 header()->setStretchLastSection(false);
4129 setTextElideMode(Qt::ElideMiddle);
4130 setEditTriggers(QAbstractItemView::EditKeyPressed);
4131 setContextMenuPolicy(Qt::CustomContextMenu);
4132#if QT_CONFIG(draganddrop)
4133 setDragDropMode(QAbstractItemView::InternalMove);
4134#endif
4135}
4136
4137void QFileDialogTreeView::keyPressEvent(QKeyEvent *e)
4138{
4139#ifdef QT_KEYPAD_NAVIGATION
4140 if (QApplication::navigationMode() == Qt::NavigationModeKeypadDirectional) {
4141 QTreeView::keyPressEvent(e);
4142 return;
4143 }
4144#endif // QT_KEYPAD_NAVIGATION
4145
4146 if (!d_ptr->itemViewKeyboardEvent(event: e))
4147 QTreeView::keyPressEvent(event: e);
4148 e->accept();
4149}
4150
4151QSize QFileDialogTreeView::sizeHint() const
4152{
4153 int height = qMax(a: 10, b: sizeHintForRow(row: 0));
4154 QSize sizeHint = header()->sizeHint();
4155 return QSize(sizeHint.width() * 4, height * 30);
4156}
4157
4158/*!
4159 // FIXME: this is a hack to avoid propagating key press events
4160 // to the dialog and from there to the "Ok" button
4161*/
4162void QFileDialogLineEdit::keyPressEvent(QKeyEvent *e)
4163{
4164#ifdef QT_KEYPAD_NAVIGATION
4165 if (QApplication::navigationMode() == Qt::NavigationModeKeypadDirectional) {
4166 QLineEdit::keyPressEvent(e);
4167 return;
4168 }
4169#endif // QT_KEYPAD_NAVIGATION
4170
4171#if QT_CONFIG(shortcut)
4172 int key = e->key();
4173#endif
4174 QLineEdit::keyPressEvent(e);
4175#if QT_CONFIG(shortcut)
4176 if (!e->matches(key: QKeySequence::Cancel) && key != Qt::Key_Back)
4177#endif
4178 e->accept();
4179}
4180
4181#if QT_CONFIG(fscompleter)
4182
4183QString QFSCompleter::pathFromIndex(const QModelIndex &index) const
4184{
4185 const QFileSystemModel *dirModel;
4186 if (proxyModel)
4187 dirModel = qobject_cast<const QFileSystemModel *>(object: proxyModel->sourceModel());
4188 else
4189 dirModel = sourceModel;
4190 QString currentLocation = dirModel->rootPath();
4191 QString path = index.data(arole: QFileSystemModel::FilePathRole).toString();
4192 if (!currentLocation.isEmpty() && path.startsWith(s: currentLocation)) {
4193#if defined(Q_OS_UNIX)
4194 if (currentLocation == QDir::separator())
4195 return path.remove(i: 0, len: currentLocation.size());
4196#endif
4197 if (currentLocation.endsWith(c: u'/'))
4198 return path.remove(i: 0, len: currentLocation.size());
4199 else
4200 return path.remove(i: 0, len: currentLocation.size()+1);
4201 }
4202 return index.data(arole: QFileSystemModel::FilePathRole).toString();
4203}
4204
4205QStringList QFSCompleter::splitPath(const QString &path) const
4206{
4207 if (path.isEmpty())
4208 return QStringList(completionPrefix());
4209
4210 QString pathCopy = QDir::toNativeSeparators(pathName: path);
4211 QChar sep = QDir::separator();
4212#if defined(Q_OS_WIN)
4213 if (pathCopy == "\\"_L1 || pathCopy == "\\\\"_L1)
4214 return QStringList(pathCopy);
4215 QString doubleSlash("\\\\"_L1);
4216 if (pathCopy.startsWith(doubleSlash))
4217 pathCopy = pathCopy.mid(2);
4218 else
4219 doubleSlash.clear();
4220#elif defined(Q_OS_UNIX)
4221 {
4222 QString tildeExpanded = qt_tildeExpansion(path: pathCopy);
4223 if (tildeExpanded != pathCopy) {
4224 QFileSystemModel *dirModel;
4225 if (proxyModel)
4226 dirModel = qobject_cast<QFileSystemModel *>(object: proxyModel->sourceModel());
4227 else
4228 dirModel = sourceModel;
4229 dirModel->fetchMore(parent: dirModel->index(path: tildeExpanded));
4230 }
4231 pathCopy = std::move(tildeExpanded);
4232 }
4233#endif
4234
4235#if defined(Q_OS_WIN)
4236 QStringList parts = pathCopy.split(sep, Qt::SkipEmptyParts);
4237 if (!doubleSlash.isEmpty() && !parts.isEmpty())
4238 parts[0].prepend(doubleSlash);
4239 if (pathCopy.endsWith(sep))
4240 parts.append(QString());
4241#else
4242 QStringList parts = pathCopy.split(sep);
4243 if (pathCopy[0] == sep) // read the "/" at the beginning as the split removed it
4244 parts[0] = sep;
4245#endif
4246
4247#if defined(Q_OS_WIN)
4248 bool startsFromRoot = !parts.isEmpty() && parts[0].endsWith(u':');
4249#else
4250 bool startsFromRoot = pathCopy[0] == sep;
4251#endif
4252 if (parts.size() == 1 || (parts.size() > 1 && !startsFromRoot)) {
4253 const QFileSystemModel *dirModel;
4254 if (proxyModel)
4255 dirModel = qobject_cast<const QFileSystemModel *>(object: proxyModel->sourceModel());
4256 else
4257 dirModel = sourceModel;
4258 QString currentLocation = QDir::toNativeSeparators(pathName: dirModel->rootPath());
4259#if defined(Q_OS_WIN)
4260 if (currentLocation.endsWith(u':'))
4261 currentLocation.append(sep);
4262#endif
4263 if (currentLocation.contains(c: sep) && path != currentLocation) {
4264 QStringList currentLocationList = splitPath(path: currentLocation);
4265 while (!currentLocationList.isEmpty() && parts.size() > 0 && parts.at(i: 0) == ".."_L1) {
4266 parts.removeFirst();
4267 currentLocationList.removeLast();
4268 }
4269 if (!currentLocationList.isEmpty() && currentLocationList.constLast().isEmpty())
4270 currentLocationList.removeLast();
4271 return currentLocationList + parts;
4272 }
4273 }
4274 return parts;
4275}
4276
4277#endif // QT_CONFIG(completer)
4278
4279
4280QT_END_NAMESPACE
4281
4282#include "moc_qfiledialog.cpp"
4283

source code of qtbase/src/widgets/dialogs/qfiledialog.cpp