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