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

Provided by KDAB

Privacy Policy
Learn to use CMake with our Intro Training
Find out more

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