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

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