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