1 /****************************************************************************
2 **
3 ** Copyright (C) 2015 The Qt Company Ltd.
4 ** Contact: http://www.qt.io/licensing/
5 **
6 ** This file is part of the QtGui 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 http://www.qt.io/terms-conditions. For further
15 ** information use the contact form at http://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 2.1 or version 3 as published by the Free
20 ** Software Foundation and appearing in the file LICENSE.LGPLv21 and
21 ** LICENSE.LGPLv3 included in the packaging of this file. Please review the
22 ** following information to ensure the GNU Lesser General Public License
23 ** requirements will be met: https://www.gnu.org/licenses/lgpl.html and
24 ** http://www.gnu.org/licenses/old-licenses/lgpl-2.1.html.
25 **
26 ** As a special exception, The Qt Company gives you certain additional
27 ** rights. These rights are described in The Qt Company LGPL Exception
28 ** version 1.1, included in the file LGPL_EXCEPTION.txt in this package.
29 **
30 ** GNU General Public License Usage
31 ** Alternatively, this file may be used under the terms of the GNU
32 ** General Public License version 3.0 as published by the Free Software
33 ** Foundation and appearing in the file LICENSE.GPL included in the
34 ** packaging of this file.  Please review the following information to
35 ** ensure the GNU General Public License version 3.0 requirements will be
36 ** met: http://www.gnu.org/copyleft/gpl.html.
37 **
38 ** $QT_END_LICENSE$
39 **
40 ****************************************************************************/
41 
42 #include <qvariant.h>
43 #include <private/qwidgetitemdata_p.h>
44 #include "qfiledialog.h"
45 
46 #ifndef QT_NO_FILEDIALOG
47 #include "qfiledialog_p.h"
48 #include <qfontmetrics.h>
49 #include <qaction.h>
50 #include <qheaderview.h>
51 #include <qshortcut.h>
52 #include <qgridlayout.h>
53 #include <qmenu.h>
54 #include <qmessagebox.h>
55 #include <qinputdialog.h>
56 #include <stdlib.h>
57 #include <qsettings.h>
58 #include <qdebug.h>
59 #include <qapplication.h>
60 #include <qstylepainter.h>
61 #include <private/qfileiconprovider_p.h>
62 #if !defined(Q_WS_WINCE) && !defined(Q_OS_SYMBIAN)
63 #include "ui_qfiledialog.h"
64 #else
65 #define Q_EMBEDDED_SMALLSCREEN
66 #include "ui_qfiledialog_embedded.h"
67 #if defined(Q_OS_WINCE)
68 extern bool qt_priv_ptr_valid;
69 #endif
70 #if defined(Q_OS_UNIX)
71 #include <pwd.h>
72 #endif
73 #endif
74 
75 QT_BEGIN_NAMESPACE
76 
77 Q_GLOBAL_STATIC(QString, lastVisitedDir)
78 
79 /*
80     \internal
81 
82     Exported hooks that can be used to customize the static functions.
83  */
84 typedef QString (*_qt_filedialog_existing_directory_hook)(QWidget *parent, const QString &caption, const QString &dir, QFileDialog::Options options);
85 Q_GUI_EXPORT _qt_filedialog_existing_directory_hook qt_filedialog_existing_directory_hook = 0;
86 
87 typedef QString (*_qt_filedialog_open_filename_hook)(QWidget * parent, const QString &caption, const QString &dir, const QString &filter, QString *selectedFilter, QFileDialog::Options options);
88 Q_GUI_EXPORT _qt_filedialog_open_filename_hook qt_filedialog_open_filename_hook = 0;
89 
90 typedef QStringList (*_qt_filedialog_open_filenames_hook)(QWidget * parent, const QString &caption, const QString &dir, const QString &filter, QString *selectedFilter, QFileDialog::Options options);
91 Q_GUI_EXPORT _qt_filedialog_open_filenames_hook qt_filedialog_open_filenames_hook = 0;
92 
93 typedef QString (*_qt_filedialog_save_filename_hook)(QWidget * parent, const QString &caption, const QString &dir, const QString &filter, QString *selectedFilter, QFileDialog::Options options);
94 Q_GUI_EXPORT _qt_filedialog_save_filename_hook qt_filedialog_save_filename_hook = 0;
95 
96 /*!
97   \class QFileDialog
98   \brief The QFileDialog class provides a dialog that allow users to select files or directories.
99   \ingroup standard-dialogs
100 
101 
102   The QFileDialog class enables a user to traverse the file system in
103   order to select one or many files or a directory.
104 
105   The easiest way to create a QFileDialog is to use the static
106   functions. On Windows, Mac OS X, KDE and GNOME, these static functions will
107   call the native file dialog when possible.
108 
109   \snippet doc/src/snippets/code/src_gui_dialogs_qfiledialog.cpp 0
110 
111   In the above example, a modal QFileDialog is created using a static
112   function. The dialog initially displays the contents of the "/home/jana"
113   directory, and displays files matching the patterns given in the
114   string "Image Files (*.png *.jpg *.bmp)". The parent of the file dialog
115   is set to \e this, and the window title is set to "Open Image".
116 
117   If you want to use multiple filters, separate each one with
118   \e two semicolons. For example:
119 
120   \snippet doc/src/snippets/code/src_gui_dialogs_qfiledialog.cpp 1
121 
122   You can create your own QFileDialog without using the static
123   functions. By calling setFileMode(), you can specify what the user must
124   select in the dialog:
125 
126   \snippet doc/src/snippets/code/src_gui_dialogs_qfiledialog.cpp 2
127 
128   In the above example, the mode of the file dialog is set to
129   AnyFile, meaning that the user can select any file, or even specify a
130   file that doesn't exist. This mode is useful for creating a
131   "Save As" file dialog. Use ExistingFile if the user must select an
132   existing file, or \l Directory if only a directory may be selected.
133   See the \l QFileDialog::FileMode enum for the complete list of modes.
134 
135   The fileMode property contains the mode of operation for the dialog;
136   this indicates what types of objects the user is expected to select.
137   Use setNameFilter() to set the dialog's file filter. For example:
138 
139   \snippet doc/src/snippets/code/src_gui_dialogs_qfiledialog.cpp 3
140 
141   In the above example, the filter is set to \c{"Images (*.png *.xpm *.jpg)"},
142   this means that only files with the extension \c png, \c xpm,
143   or \c jpg will be shown in the QFileDialog. You can apply
144   several filters by using setNameFilters(). Use selectNameFilter() to select
145   one of the filters you've given as the file dialog's default filter.
146 
147   The file dialog has two view modes: \l{QFileDialog::}{List} and
148   \l{QFileDialog::}{Detail}.
149   \l{QFileDialog::}{List} presents the contents of the current directory
150   as a list of file and directory names. \l{QFileDialog::}{Detail} also
151   displays a list of file and directory names, but provides additional
152   information alongside each name, such as the file size and modification
153   date. Set the mode with setViewMode():
154 
155   \snippet doc/src/snippets/code/src_gui_dialogs_qfiledialog.cpp 4
156 
157   The last important function you will need to use when creating your
158   own file dialog is selectedFiles().
159 
160   \snippet doc/src/snippets/code/src_gui_dialogs_qfiledialog.cpp 5
161 
162   In the above example, a modal file dialog is created and shown. If
163   the user clicked OK, the file they selected is put in \c fileName.
164 
165   The dialog's working directory can be set with setDirectory().
166   Each file in the current directory can be selected using
167   the selectFile() function.
168 
169   The \l{dialogs/standarddialogs}{Standard Dialogs} example shows
170   how to use QFileDialog as well as other built-in Qt dialogs.
171 
172   \sa QDir, QFileInfo, QFile, QPrintDialog, QColorDialog, QFontDialog, {Standard Dialogs Example},
173       {Application Example}
174 */
175 
176 /*!
177     \enum QFileDialog::AcceptMode
178 
179     \value AcceptOpen
180     \value AcceptSave
181 */
182 
183 /*!
184     \enum QFileDialog::ViewMode
185 
186     This enum describes the view mode of the file dialog; i.e. what
187     information about each file will be displayed.
188 
189     \value Detail Displays an icon, a name, and details for each item in
190                   the directory.
191     \value List   Displays only an icon and a name for each item in the
192                   directory.
193 
194     \sa setViewMode()
195 */
196 
197 /*!
198     \enum QFileDialog::FileMode
199 
200     This enum is used to indicate what the user may select in the file
201     dialog; i.e. what the dialog will return if the user clicks OK.
202 
203     \value AnyFile        The name of a file, whether it exists or not.
204     \value ExistingFile   The name of a single existing file.
205     \value Directory      The name of a directory. Both files and
206                           directories are displayed.
207     \value ExistingFiles  The names of zero or more existing files.
208 
209     This value is obsolete since Qt 4.5:
210 
211     \value DirectoryOnly  Use \c Directory and setOption(ShowDirsOnly, true) instead.
212 
213     \sa setFileMode()
214 */
215 
216 /*!
217     \enum QFileDialog::Option
218 
219     \value ShowDirsOnly Only show directories in the file dialog. By
220     default both files and directories are shown. (Valid only in the
221     \l Directory file mode.)
222 
223     \value DontResolveSymlinks Don't resolve symlinks in the file
224     dialog. By default symlinks are resolved.
225 
226     \value DontConfirmOverwrite Don't ask for confirmation if an
227     existing file is selected.  By default confirmation is requested.
228 
229     \value DontUseNativeDialog Don't use the native file dialog. By
230     default, the native file dialog is used unless you use a subclass
231     of QFileDialog that contains the Q_OBJECT macro.
232 
233     \value ReadOnly Indicates that the model is readonly.
234 
235     \value HideNameFilterDetails Indicates if the file name filter details are
236     hidden or not.
237 
238     \value DontUseSheet In previous versions of Qt, the static
239     functions would create a sheet by default if the static function
240     was given a parent. This is no longer supported and does nothing in Qt 4.5, The
241     static functions will always be an application modal dialog. If
242     you want to use sheets, use QFileDialog::open() instead.
243 
244     \value DontUseCustomDirectoryIcons Always use the default directory icon.
245     Some platforms allow the user to set a different icon. Custom icon lookup
246     cause a big performance impact over network or removable drives. Setting this
247     will affect the behavior of the icon provider. This enum value was added in Qt 4.8.6.
248 */
249 
250 /*!
251   \enum QFileDialog::DialogLabel
252 
253   \value LookIn
254   \value FileName
255   \value FileType
256   \value Accept
257   \value Reject
258 */
259 
260 /*!
261     \fn void QFileDialog::filesSelected(const QStringList &selected)
262 
263     When the selection changes and the dialog is accepted, this signal is
264     emitted with the (possibly empty) list of \a selected files.
265 
266     \sa currentChanged(), QDialog::Accepted
267 */
268 
269 
270 /*!
271     \fn void QFileDialog::fileSelected(const QString &file)
272 
273     When the selection changes and the dialog is accepted, this signal is
274     emitted with the (possibly empty) selected \a file.
275 
276     \sa currentChanged(), QDialog::Accepted
277 */
278 
279 
280 /*!
281     \fn void QFileDialog::currentChanged(const QString &path)
282 
283     When the current file changes, this signal is emitted with the
284     new file name as the \a path parameter.
285 
286     \sa filesSelected()
287 */
288 
289 /*!
290   \fn void QFileDialog::directoryEntered(const QString &directory)
291   \since 4.3
292 
293   This signal is emitted when the user enters a \a directory.
294 */
295 
296 /*!
297   \fn void QFileDialog::filterSelected(const QString &filter)
298   \since 4.3
299 
300   This signal is emitted when the user selects a \a filter.
301 */
302 
303 #if defined(Q_WS_WIN) || defined(Q_WS_MAC)
304 bool Q_GUI_EXPORT qt_use_native_dialogs = true; // for the benefit of testing tools, until we have a proper API
305 #endif
306 
307 QT_BEGIN_INCLUDE_NAMESPACE
308 #ifdef Q_WS_WIN
309 #include <qwindowsstyle.h>
310 #endif
311 #include <qshortcut.h>
312 #ifdef Q_WS_MAC
313 #include <qmacstyle_mac.h>
314 #endif
315 QT_END_INCLUDE_NAMESPACE
316 
317 /*!
318     \fn QFileDialog::QFileDialog(QWidget *parent, Qt::WindowFlags flags)
319 
320     Constructs a file dialog with the given \a parent and widget \a flags.
321 */
QFileDialog(QWidget * parent,Qt::WindowFlags f)322 QFileDialog::QFileDialog(QWidget *parent, Qt::WindowFlags f)
323     : QDialog(*new QFileDialogPrivate, parent, f)
324 {
325     Q_D(QFileDialog);
326     d->init();
327     d->lineEdit()->selectAll();
328 }
329 
330 /*!
331     Constructs a file dialog with the given \a parent and \a caption that
332     initially displays the contents of the specified \a directory.
333     The contents of the directory are filtered before being shown in the
334     dialog, using a semicolon-separated list of filters specified by
335     \a filter.
336 */
QFileDialog(QWidget * parent,const QString & caption,const QString & directory,const QString & filter)337 QFileDialog::QFileDialog(QWidget *parent,
338                      const QString &caption,
339                      const QString &directory,
340                      const QString &filter)
341     : QDialog(*new QFileDialogPrivate, parent, 0)
342 {
343     Q_D(QFileDialog);
344     d->init(directory, filter, caption);
345     d->lineEdit()->selectAll();
346 }
347 
348 /*!
349     \internal
350 */
QFileDialog(const QFileDialogArgs & args)351 QFileDialog::QFileDialog(const QFileDialogArgs &args)
352     : QDialog(*new QFileDialogPrivate, args.parent, 0)
353 {
354     Q_D(QFileDialog);
355     d->init(args.directory, args.filter, args.caption);
356     setFileMode(args.mode);
357     setOptions(args.options);
358     selectFile(args.selection);
359     d->lineEdit()->selectAll();
360 }
361 
362 /*!
363     Destroys the file dialog.
364 */
~QFileDialog()365 QFileDialog::~QFileDialog()
366 {
367     Q_D(QFileDialog);
368 #ifndef QT_NO_SETTINGS
369     QSettings settings(QSettings::UserScope, QLatin1String("Trolltech"));
370     settings.beginGroup(QLatin1String("Qt"));
371     settings.setValue(QLatin1String("filedialog"), saveState());
372 #endif
373     d->deleteNativeDialog_sys();
374 }
375 
376 /*!
377     \since 4.3
378     Sets the \a urls that are located in the sidebar.
379 
380     For instance:
381 
382     \snippet doc/src/snippets/filedialogurls.cpp 0
383 
384     The file dialog will then look like this:
385 
386     \image filedialogurls.png
387 
388     \sa sidebarUrls()
389 */
setSidebarUrls(const QList<QUrl> & urls)390 void QFileDialog::setSidebarUrls(const QList<QUrl> &urls)
391 {
392     Q_D(QFileDialog);
393     d->qFileDialogUi->sidebar->setUrls(urls);
394 }
395 
396 /*!
397     \since 4.3
398     Returns a list of urls that are currently in the sidebar
399 */
sidebarUrls() const400 QList<QUrl> QFileDialog::sidebarUrls() const
401 {
402     Q_D(const QFileDialog);
403     return d->qFileDialogUi->sidebar->urls();
404 }
405 
406 static const qint32 QFileDialogMagic = 0xbe;
407 
408 const char *qt_file_dialog_filter_reg_exp =
409 "^(.*)\\(([a-zA-Z0-9_.*? +;#\\-\\[\\]@\\{\\}/!<>\\$%&=^~:\\|]*)\\)$";
410 
411 /*!
412     \since 4.3
413     Saves the state of the dialog's layout, history and current directory.
414 
415     Typically this is used in conjunction with QSettings to remember the size
416     for a future session. A version number is stored as part of the data.
417 */
saveState() const418 QByteArray QFileDialog::saveState() const
419 {
420     Q_D(const QFileDialog);
421     int version = 3;
422     QByteArray data;
423     QDataStream stream(&data, QIODevice::WriteOnly);
424 
425     stream << qint32(QFileDialogMagic);
426     stream << qint32(version);
427     stream << d->qFileDialogUi->splitter->saveState();
428     stream << d->qFileDialogUi->sidebar->urls();
429     stream << history();
430     stream << *lastVisitedDir();
431     stream << d->qFileDialogUi->treeView->header()->saveState();
432     stream << qint32(viewMode());
433     return data;
434 }
435 
436 /*!
437     \since 4.3
438     Restores the dialogs's layout, history and current directory to the \a state specified.
439 
440     Typically this is used in conjunction with QSettings to restore the size
441     from a past session.
442 
443     Returns false if there are errors
444 */
restoreState(const QByteArray & state)445 bool QFileDialog::restoreState(const QByteArray &state)
446 {
447     Q_D(QFileDialog);
448     int version = 3;
449     QByteArray sd = state;
450     QDataStream stream(&sd, QIODevice::ReadOnly);
451     if (stream.atEnd())
452         return false;
453     QByteArray splitterState;
454     QByteArray headerData;
455     QList<QUrl> bookmarks;
456     QStringList history;
457     QString currentDirectory;
458     qint32 marker;
459     qint32 v;
460     qint32 viewMode;
461     stream >> marker;
462     stream >> v;
463     if (marker != QFileDialogMagic || v != version)
464         return false;
465 
466     stream >> splitterState
467            >> bookmarks
468            >> history
469            >> currentDirectory
470            >> headerData
471            >> viewMode;
472 
473     if (!d->qFileDialogUi->splitter->restoreState(splitterState))
474         return false;
475     QList<int> list = d->qFileDialogUi->splitter->sizes();
476     if (list.count() >= 2 && list.at(0) == 0 && list.at(1) == 0) {
477         for (int i = 0; i < list.count(); ++i)
478             list[i] = d->qFileDialogUi->splitter->widget(i)->sizeHint().width();
479         d->qFileDialogUi->splitter->setSizes(list);
480     }
481 
482     d->qFileDialogUi->sidebar->setUrls(bookmarks);
483     while (history.count() > 5)
484         history.pop_front();
485     setHistory(history);
486     setDirectory(lastVisitedDir()->isEmpty() ? currentDirectory : *lastVisitedDir());
487     QHeaderView *headerView = d->qFileDialogUi->treeView->header();
488     if (!headerView->restoreState(headerData))
489         return false;
490 
491     QList<QAction*> actions = headerView->actions();
492     QAbstractItemModel *abstractModel = d->model;
493 #ifndef QT_NO_PROXYMODEL
494     if (d->proxyModel)
495         abstractModel = d->proxyModel;
496 #endif
497     int total = qMin(abstractModel->columnCount(QModelIndex()), actions.count() + 1);
498     for (int i = 1; i < total; ++i)
499         actions.at(i - 1)->setChecked(!headerView->isSectionHidden(i));
500 
501     setViewMode(ViewMode(viewMode));
502     return true;
503 }
504 
505 /*!
506     \reimp
507 */
changeEvent(QEvent * e)508 void QFileDialog::changeEvent(QEvent *e)
509 {
510     Q_D(QFileDialog);
511     if (e->type() == QEvent::LanguageChange) {
512         d->retranslateWindowTitle();
513         d->retranslateStrings();
514     }
515     QDialog::changeEvent(e);
516 }
517 
QFileDialogPrivate()518 QFileDialogPrivate::QFileDialogPrivate()
519     :
520 #ifndef QT_NO_PROXYMODEL
521         proxyModel(0),
522 #endif
523         model(0),
524         fileMode(QFileDialog::AnyFile),
525         acceptMode(QFileDialog::AcceptOpen),
526         currentHistoryLocation(-1),
527         renameAction(0),
528         deleteAction(0),
529         showHiddenAction(0),
530         useDefaultCaption(true),
531         defaultFileTypes(true),
532         fileNameLabelExplicitlySat(false),
533         nativeDialogInUse(false),
534 #ifdef Q_WS_MAC
535         mDelegate(0),
536 #ifndef QT_MAC_USE_COCOA
537         mDialog(0),
538         mDialogStarted(false),
539         mDialogClosed(true),
540 #endif
541 #endif
542         qFileDialogUi(0)
543 {
544 }
545 
~QFileDialogPrivate()546 QFileDialogPrivate::~QFileDialogPrivate()
547 {
548 }
549 
retranslateWindowTitle()550 void QFileDialogPrivate::retranslateWindowTitle()
551 {
552     Q_Q(QFileDialog);
553     if (!useDefaultCaption || setWindowTitle != q->windowTitle())
554         return;
555     if (acceptMode == QFileDialog::AcceptOpen) {
556         if (fileMode == QFileDialog::DirectoryOnly || fileMode == QFileDialog::Directory)
557             q->setWindowTitle(QFileDialog::tr("Find Directory"));
558         else
559             q->setWindowTitle(QFileDialog::tr("Open"));
560     } else
561         q->setWindowTitle(QFileDialog::tr("Save As"));
562 
563     setWindowTitle = q->windowTitle();
564 }
565 
setLastVisitedDirectory(const QString & dir)566 void QFileDialogPrivate::setLastVisitedDirectory(const QString &dir)
567 {
568     *lastVisitedDir() = dir;
569 }
570 
retranslateStrings()571 void QFileDialogPrivate::retranslateStrings()
572 {
573     Q_Q(QFileDialog);
574     /* WIDGETS */
575     if (defaultFileTypes)
576         q->setNameFilter(QFileDialog::tr("All Files (*)"));
577 
578     QList<QAction*> actions = qFileDialogUi->treeView->header()->actions();
579     QAbstractItemModel *abstractModel = model;
580 #ifndef QT_NO_PROXYMODEL
581     if (proxyModel)
582         abstractModel = proxyModel;
583 #endif
584     int total = qMin(abstractModel->columnCount(QModelIndex()), actions.count() + 1);
585     for (int i = 1; i < total; ++i) {
586         actions.at(i - 1)->setText(QFileDialog::tr("Show ") + abstractModel->headerData(i, Qt::Horizontal, Qt::DisplayRole).toString());
587     }
588 
589     /* MENU ACTIONS */
590     renameAction->setText(QFileDialog::tr("&Rename"));
591     deleteAction->setText(QFileDialog::tr("&Delete"));
592     showHiddenAction->setText(QFileDialog::tr("Show &hidden files"));
593     newFolderAction->setText(QFileDialog::tr("&New Folder"));
594     qFileDialogUi->retranslateUi(q);
595 
596     if (!fileNameLabelExplicitlySat){
597         if (fileMode == QFileDialog::DirectoryOnly || fileMode == QFileDialog::Directory) {
598             q->setLabelText(QFileDialog::FileName, QFileDialog::tr("Directory:"));
599         } else {
600             q->setLabelText(QFileDialog::FileName, QFileDialog::tr("File &name:"));
601         }
602         fileNameLabelExplicitlySat = false;
603     }
604 }
605 
emitFilesSelected(const QStringList & files)606 void QFileDialogPrivate::emitFilesSelected(const QStringList &files)
607 {
608     Q_Q(QFileDialog);
609     emit q->filesSelected(files);
610     if (files.count() == 1)
611         emit q->fileSelected(files.first());
612 }
613 
canBeNativeDialog()614 bool QFileDialogPrivate::canBeNativeDialog()
615 {
616     Q_Q(QFileDialog);
617     if (nativeDialogInUse)
618         return true;
619     if (q->testAttribute(Qt::WA_DontShowOnScreen))
620         return false;
621     if (opts & QFileDialog::DontUseNativeDialog)
622         return false;
623 
624     QLatin1String staticName(QFileDialog::staticMetaObject.className());
625     QLatin1String dynamicName(q->metaObject()->className());
626     return (staticName == dynamicName);
627 }
628 
629 /*!
630     \since 4.5
631     Sets the given \a option to be enabled if \a on is true; otherwise,
632     clears the given \a option.
633 
634     \sa options, testOption()
635 */
setOption(Option option,bool on)636 void QFileDialog::setOption(Option option, bool on)
637 {
638     Q_D(QFileDialog);
639     if (!(d->opts & option) != !on)
640         setOptions(d->opts ^ option);
641 }
642 
643 /*!
644     \since 4.5
645 
646     Returns true if the given \a option is enabled; otherwise, returns
647     false.
648 
649     \sa options, setOption()
650 */
testOption(Option option) const651 bool QFileDialog::testOption(Option option) const
652 {
653     Q_D(const QFileDialog);
654     return (d->opts & option) != 0;
655 }
656 
657 /*!
658     \property QFileDialog::options
659     \brief the various options that affect the look and feel of the dialog
660     \since 4.5
661 
662     By default, all options are disabled.
663 
664     Options should be set before showing the dialog. Setting them while the
665     dialog is visible is not guaranteed to have an immediate effect on the
666     dialog (depending on the option and on the platform).
667 
668     \sa setOption(), testOption()
669 */
setOptions(Options options)670 void QFileDialog::setOptions(Options options)
671 {
672     Q_D(QFileDialog);
673 
674     Options changed = (options ^ d->opts);
675     if (!changed)
676         return;
677 
678     d->opts = options;
679     if (changed & DontResolveSymlinks)
680         d->model->setResolveSymlinks(!(options & DontResolveSymlinks));
681     if (changed & ReadOnly) {
682         bool ro = (options & ReadOnly);
683         d->model->setReadOnly(ro);
684         d->qFileDialogUi->newFolderButton->setEnabled(!ro);
685         d->renameAction->setEnabled(!ro);
686         d->deleteAction->setEnabled(!ro);
687     }
688     if (changed & HideNameFilterDetails)
689         setNameFilters(d->nameFilters);
690 
691     if (changed & ShowDirsOnly)
692         setFilter((options & ShowDirsOnly) ? filter() & ~QDir::Files : filter() | QDir::Files);
693 
694     if (changed & DontUseCustomDirectoryIcons)
695         iconProvider()->d_ptr->setUseCustomDirectoryIcons(!(options & DontUseCustomDirectoryIcons));
696 }
697 
options() const698 QFileDialog::Options QFileDialog::options() const
699 {
700     Q_D(const QFileDialog);
701     return d->opts;
702 }
703 
704 /*!
705     \overload
706 
707     \since 4.5
708 
709     This function connects one of its signals to the slot specified by \a receiver
710     and \a member. The specific signal depends is filesSelected() if fileMode is
711     ExistingFiles and fileSelected() if fileMode is anything else.
712 
713     The signal will be disconnected from the slot when the dialog is closed.
714 */
open(QObject * receiver,const char * member)715 void QFileDialog::open(QObject *receiver, const char *member)
716 {
717     Q_D(QFileDialog);
718     const char *signal = (fileMode() == ExistingFiles) ? SIGNAL(filesSelected(QStringList))
719                                                        : SIGNAL(fileSelected(QString));
720     connect(this, signal, receiver, member);
721     d->signalToDisconnectOnClose = signal;
722     d->receiverToDisconnectOnClose = receiver;
723     d->memberToDisconnectOnClose = member;
724 
725     QDialog::open();
726 }
727 
728 
729 /*!
730     \reimp
731 */
setVisible(bool visible)732 void QFileDialog::setVisible(bool visible)
733 {
734     Q_D(QFileDialog);
735     if (visible){
736         if (testAttribute(Qt::WA_WState_ExplicitShowHide) && !testAttribute(Qt::WA_WState_Hidden))
737             return;
738     } else if (testAttribute(Qt::WA_WState_ExplicitShowHide) && testAttribute(Qt::WA_WState_Hidden))
739         return;
740 
741     if (d->canBeNativeDialog()){
742         if (d->setVisible_sys(visible)){
743             d->nativeDialogInUse = true;
744             // Set WA_DontShowOnScreen so that QDialog::setVisible(visible) below
745             // updates the state correctly, but skips showing the non-native version:
746             setAttribute(Qt::WA_DontShowOnScreen);
747 #ifndef QT_NO_FSCOMPLETER
748             //So the completer don't try to complete and therefore to show a popup
749             d->completer->setModel(0);
750 #endif
751         } else {
752             d->nativeDialogInUse = false;
753             setAttribute(Qt::WA_DontShowOnScreen, false);
754 #ifndef QT_NO_FSCOMPLETER
755             if (d->proxyModel != 0)
756                 d->completer->setModel(d->proxyModel);
757             else
758                 d->completer->setModel(d->model);
759 #endif
760         }
761     }
762 
763     if (!d->nativeDialogInUse)
764         d->qFileDialogUi->fileNameEdit->setFocus();
765 
766     QDialog::setVisible(visible);
767 }
768 
769 /*!
770     \internal
771     set the directory to url
772 */
_q_goToUrl(const QUrl & url)773 void QFileDialogPrivate::_q_goToUrl(const QUrl &url)
774 {
775     //The shortcut in the side bar may have a parent that is not fetched yet (e.g. an hidden file)
776     //so we force the fetching
777     QFileSystemModelPrivate::QFileSystemNode *node = model->d_func()->node(url.toLocalFile(), true);
778     QModelIndex idx =  model->d_func()->index(node);
779     _q_enterDirectory(idx);
780 }
781 
782 /*!
783     \fn void QFileDialog::setDirectory(const QDir &directory)
784 
785     \overload
786 */
787 
788 /*!
789     Sets the file dialog's current \a directory.
790 */
setDirectory(const QString & directory)791 void QFileDialog::setDirectory(const QString &directory)
792 {
793     Q_D(QFileDialog);
794     QString newDirectory = directory;
795     QFileInfo info(directory);
796     //we remove .. and . from the given path if exist
797     if (!directory.isEmpty())
798         newDirectory = QDir::cleanPath(directory);
799 
800     if (!directory.isEmpty() && newDirectory.isEmpty())
801         return;
802 
803     d->setLastVisitedDirectory(newDirectory);
804 
805     if (d->nativeDialogInUse){
806         d->setDirectory_sys(newDirectory);
807         return;
808     }
809     if (d->rootPath() == newDirectory)
810         return;
811     QModelIndex root = d->model->setRootPath(newDirectory);
812     d->qFileDialogUi->newFolderButton->setEnabled(d->model->flags(root) & Qt::ItemIsDropEnabled);
813     if (root != d->rootIndex()) {
814 #ifndef QT_NO_FSCOMPLETER
815     if (directory.endsWith(QLatin1Char('/')))
816         d->completer->setCompletionPrefix(newDirectory);
817     else
818         d->completer->setCompletionPrefix(newDirectory + QLatin1Char('/'));
819 #endif
820         d->setRootIndex(root);
821     }
822     d->qFileDialogUi->listView->selectionModel()->clear();
823 }
824 
825 /*!
826     Returns the directory currently being displayed in the dialog.
827 */
directory() const828 QDir QFileDialog::directory() const
829 {
830     Q_D(const QFileDialog);
831     return QDir(d->nativeDialogInUse ? d->directory_sys() : d->rootPath());
832 }
833 
834 /*!
835     Selects the given \a filename in the file dialog.
836 
837     \sa selectedFiles()
838 */
selectFile(const QString & filename)839 void QFileDialog::selectFile(const QString &filename)
840 {
841     Q_D(QFileDialog);
842     if (filename.isEmpty())
843         return;
844 
845     if (d->nativeDialogInUse){
846         d->selectFile_sys(filename);
847         return;
848     }
849 
850     if (!QDir::isRelativePath(filename)) {
851         QFileInfo info(filename);
852         QString filenamePath = info.absoluteDir().path();
853 
854         if (d->model->rootPath() != filenamePath)
855             setDirectory(filenamePath);
856     }
857 
858     QModelIndex index = d->model->index(filename);
859     QString file;
860     if (!index.isValid()) {
861         // save as dialog where we want to input a default value
862         QString text = filename;
863         if (QFileInfo(filename).isAbsolute()) {
864             QString current = d->rootPath();
865             text.remove(current);
866             if (text.at(0) == QDir::separator()
867 #ifdef Q_OS_WIN
868                 //On Windows both cases can happen
869                 || text.at(0) == QLatin1Char('/')
870 #endif
871                 )
872                 text = text.remove(0,1);
873         }
874         file = text;
875     } else {
876         file = index.data().toString();
877     }
878     d->qFileDialogUi->listView->selectionModel()->clear();
879     if (!isVisible() || !d->lineEdit()->hasFocus())
880         d->lineEdit()->setText(file);
881 }
882 
883 #ifdef Q_OS_UNIX
qt_tildeExpansion(const QString & path,bool * expanded=0)884 Q_AUTOTEST_EXPORT QString qt_tildeExpansion(const QString &path, bool *expanded = 0)
885 {
886     if (expanded != 0)
887         *expanded = false;
888     if (!path.startsWith(QLatin1Char('~')))
889         return path;
890     QString ret = path;
891 #if !defined(Q_OS_INTEGRITY)
892     QStringList tokens = ret.split(QDir::separator());
893     if (tokens.first() == QLatin1String("~")) {
894         ret.replace(0, 1, QDir::homePath());
895     } else {
896         QString userName = tokens.first();
897         userName.remove(0, 1);
898 #if defined(Q_OS_VXWORKS)
899         const QString homePath = QDir::homePath();
900 #elif defined(_POSIX_THREAD_SAFE_FUNCTIONS) && !defined(Q_OS_OPENBSD)
901         passwd pw;
902         passwd *tmpPw;
903         char buf[200];
904         const int bufSize = sizeof(buf);
905         int err = 0;
906 #if defined(Q_OS_SOLARIS) && (_POSIX_C_SOURCE - 0 < 199506L)
907         tmpPw = getpwnam_r(userName.toLocal8Bit().constData(), &pw, buf, bufSize);
908 #else
909         err = getpwnam_r(userName.toLocal8Bit().constData(), &pw, buf, bufSize, &tmpPw);
910 #endif
911         if (err || !tmpPw)
912             return ret;
913         const QString homePath = QString::fromLocal8Bit(pw.pw_dir);
914 #else
915         passwd *pw = getpwnam(userName.toLocal8Bit().constData());
916         if (!pw)
917             return ret;
918         const QString homePath = QString::fromLocal8Bit(pw->pw_dir);
919 #endif
920         ret.replace(0, tokens.first().length(), homePath);
921     }
922     if (expanded != 0)
923         *expanded = true;
924 #endif
925     return ret;
926 }
927 #endif
928 
929 /**
930     Returns the text in the line edit which can be one or more file names
931   */
typedFiles() const932 QStringList QFileDialogPrivate::typedFiles() const
933 {
934 #ifdef Q_OS_UNIX
935     Q_Q(const QFileDialog);
936 #endif
937     QStringList files;
938     QString editText = lineEdit()->text();
939     if (!editText.contains(QLatin1Char('"'))) {
940 #ifdef Q_OS_UNIX
941         const QString prefix = q->directory().absolutePath() + QDir::separator();
942         if (QFile::exists(prefix + editText))
943             files << editText;
944         else
945             files << qt_tildeExpansion(editText);
946 #else
947         files << editText;
948 #endif
949     } else {
950         // " is used to separate files like so: "file1" "file2" "file3" ...
951         // ### need escape character for filenames with quotes (")
952         QStringList tokens = editText.split(QLatin1Char('\"'));
953         for (int i=0; i<tokens.size(); ++i) {
954             if ((i % 2) == 0)
955                 continue; // Every even token is a separator
956 #ifdef Q_OS_UNIX
957             const QString token = tokens.at(i);
958             const QString prefix = q->directory().absolutePath() + QDir::separator();
959             if (QFile::exists(prefix + token))
960                 files << token;
961             else
962                 files << qt_tildeExpansion(token);
963 #else
964             files << toInternal(tokens.at(i));
965 #endif
966         }
967     }
968     return addDefaultSuffixToFiles(files);
969 }
970 
addDefaultSuffixToFiles(const QStringList filesToFix) const971 QStringList QFileDialogPrivate::addDefaultSuffixToFiles(const QStringList filesToFix) const
972 {
973     QStringList files;
974     for (int i=0; i<filesToFix.size(); ++i) {
975         QString name = toInternal(filesToFix.at(i));
976         QFileInfo info(name);
977         // if the filename has no suffix, add the default suffix
978         if (!defaultSuffix.isEmpty() && !info.isDir() && name.lastIndexOf(QLatin1Char('.')) == -1)
979             name += QLatin1Char('.') + defaultSuffix;
980         if (info.isAbsolute()) {
981             files.append(name);
982         } else {
983             // at this point the path should only have Qt path separators.
984             // This check is needed since we might be at the root directory
985             // and on Windows it already ends with slash.
986             QString path = rootPath();
987             if (!path.endsWith(QLatin1Char('/')))
988                 path += QLatin1Char('/');
989             path += name;
990             files.append(path);
991         }
992     }
993     return files;
994 }
995 
996 
997 /*!
998     Returns a list of strings containing the absolute paths of the
999     selected files in the dialog. If no files are selected, or
1000     the mode is not ExistingFiles or ExistingFile, selectedFiles() contains the current path in the viewport.
1001 
1002     \sa selectedNameFilter(), selectFile()
1003 */
selectedFiles() const1004 QStringList QFileDialog::selectedFiles() const
1005 {
1006     Q_D(const QFileDialog);
1007     if (d->nativeDialogInUse)
1008         return d->addDefaultSuffixToFiles(d->selectedFiles_sys());
1009 
1010     QModelIndexList indexes = d->qFileDialogUi->listView->selectionModel()->selectedRows();
1011     QStringList files;
1012     for (int i = 0; i < indexes.count(); ++i)
1013         files.append(indexes.at(i).data(QFileSystemModel::FilePathRole).toString());
1014 
1015     if (files.isEmpty() && !d->lineEdit()->text().isEmpty())
1016         files = d->typedFiles();
1017 
1018     if (files.isEmpty() && !(d->fileMode == ExistingFile || d->fileMode == ExistingFiles))
1019         files.append(d->rootIndex().data(QFileSystemModel::FilePathRole).toString());
1020     return files;
1021 }
1022 
1023 /*
1024     Makes a list of filters from ;;-separated text.
1025     Used by the mac and windows implementations
1026 */
qt_make_filter_list(const QString & filter)1027 QStringList qt_make_filter_list(const QString &filter)
1028 {
1029     QString f(filter);
1030 
1031     if (f.isEmpty())
1032         return QStringList();
1033 
1034     QString sep(QLatin1String(";;"));
1035     int i = f.indexOf(sep, 0);
1036     if (i == -1) {
1037         if (f.indexOf(QLatin1Char('\n'), 0) != -1) {
1038             sep = QLatin1Char('\n');
1039             i = f.indexOf(sep, 0);
1040         }
1041     }
1042 
1043     return f.split(sep);
1044 }
1045 
1046 /*!
1047     \since 4.4
1048 
1049     Sets the filter used in the file dialog to the given \a filter.
1050 
1051     If \a filter contains a pair of parentheses containing one or more
1052     of \bold{anything*something}, separated by spaces, then only the
1053     text contained in the parentheses is used as the filter. This means
1054     that these calls are all equivalent:
1055 
1056     \snippet doc/src/snippets/code/src_gui_dialogs_qfiledialog.cpp 6
1057 
1058     \sa setNameFilters()
1059 */
setNameFilter(const QString & filter)1060 void QFileDialog::setNameFilter(const QString &filter)
1061 {
1062     setNameFilters(qt_make_filter_list(filter));
1063 }
1064 
1065 /*!
1066   \obsolete
1067 
1068   Use setNameFilter() instead.
1069 */
setFilter(const QString & filter)1070 void QFileDialog::setFilter(const QString &filter)
1071 {
1072     setNameFilter(filter);
1073 }
1074 
1075 /*!
1076     \property QFileDialog::nameFilterDetailsVisible
1077     \obsolete
1078     \brief This property holds whether the filter details is shown or not.
1079     \since 4.4
1080 
1081     When this property is true (the default), the filter details are shown
1082     in the combo box.  When the property is set to false, these are hidden.
1083 
1084     Use setOption(HideNameFilterDetails, !\e enabled) or
1085     !testOption(HideNameFilterDetails).
1086 */
setNameFilterDetailsVisible(bool enabled)1087 void QFileDialog::setNameFilterDetailsVisible(bool enabled)
1088 {
1089     setOption(HideNameFilterDetails, !enabled);
1090 }
1091 
isNameFilterDetailsVisible() const1092 bool QFileDialog::isNameFilterDetailsVisible() const
1093 {
1094     return !testOption(HideNameFilterDetails);
1095 }
1096 
1097 
1098 /*
1099     Strip the filters by removing the details, e.g. (*.*).
1100 */
qt_strip_filters(const QStringList & filters)1101 QStringList qt_strip_filters(const QStringList &filters)
1102 {
1103     QStringList strippedFilters;
1104     QRegExp r(QString::fromLatin1(qt_file_dialog_filter_reg_exp));
1105     for (int i = 0; i < filters.count(); ++i) {
1106         QString filterName;
1107         int index = r.indexIn(filters[i]);
1108         if (index >= 0)
1109             filterName = r.cap(1);
1110         strippedFilters.append(filterName.simplified());
1111     }
1112     return strippedFilters;
1113 }
1114 
1115 
1116 /*!
1117     \since 4.4
1118 
1119     Sets the \a filters used in the file dialog.
1120 
1121     \snippet doc/src/snippets/code/src_gui_dialogs_qfiledialog.cpp 7
1122 */
setNameFilters(const QStringList & filters)1123 void QFileDialog::setNameFilters(const QStringList &filters)
1124 {
1125     Q_D(QFileDialog);
1126     d->defaultFileTypes = (filters == QStringList(QFileDialog::tr("All Files (*)")));
1127     QStringList cleanedFilters;
1128     for (int i = 0; i < filters.count(); ++i) {
1129         cleanedFilters << filters[i].simplified();
1130     }
1131     d->nameFilters = cleanedFilters;
1132 
1133     if (d->nativeDialogInUse){
1134         d->setNameFilters_sys(cleanedFilters);
1135         return;
1136     }
1137 
1138     d->qFileDialogUi->fileTypeCombo->clear();
1139     if (cleanedFilters.isEmpty())
1140         return;
1141 
1142     if (testOption(HideNameFilterDetails))
1143         d->qFileDialogUi->fileTypeCombo->addItems(qt_strip_filters(cleanedFilters));
1144     else
1145         d->qFileDialogUi->fileTypeCombo->addItems(cleanedFilters);
1146 
1147     d->_q_useNameFilter(0);
1148 }
1149 
1150 /*!
1151     \obsolete
1152 
1153     Use setNameFilters() instead.
1154 */
setFilters(const QStringList & filters)1155 void QFileDialog::setFilters(const QStringList &filters)
1156 {
1157     setNameFilters(filters);
1158 }
1159 
1160 /*!
1161     \since 4.4
1162 
1163     Returns the file type filters that are in operation on this file
1164     dialog.
1165 */
nameFilters() const1166 QStringList QFileDialog::nameFilters() const
1167 {
1168     return d_func()->nameFilters;
1169 }
1170 
1171 /*!
1172     \obsolete
1173 
1174     Use nameFilters() instead.
1175 */
1176 
filters() const1177 QStringList QFileDialog::filters() const
1178 {
1179     return nameFilters();
1180 }
1181 
1182 /*!
1183     \since 4.4
1184 
1185     Sets the current file type \a filter. Multiple filters can be
1186     passed in \a filter by separating them with semicolons or spaces.
1187 
1188     \sa setNameFilter(), setNameFilters(), selectedNameFilter()
1189 */
selectNameFilter(const QString & filter)1190 void QFileDialog::selectNameFilter(const QString &filter)
1191 {
1192     Q_D(QFileDialog);
1193     if (d->nativeDialogInUse) {
1194         d->selectNameFilter_sys(filter);
1195         return;
1196     }
1197     int i = -1;
1198     if (testOption(HideNameFilterDetails)) {
1199         const QStringList filters = qt_strip_filters(qt_make_filter_list(filter));
1200         if (!filters.isEmpty())
1201             i = d->qFileDialogUi->fileTypeCombo->findText(filters.first());
1202     } else {
1203         i = d->qFileDialogUi->fileTypeCombo->findText(filter);
1204     }
1205     if (i >= 0) {
1206         d->qFileDialogUi->fileTypeCombo->setCurrentIndex(i);
1207         d->_q_useNameFilter(d->qFileDialogUi->fileTypeCombo->currentIndex());
1208     }
1209 }
1210 
1211 /*!
1212     \obsolete
1213 
1214     Use selectNameFilter() instead.
1215 */
1216 
selectFilter(const QString & filter)1217 void QFileDialog::selectFilter(const QString &filter)
1218 {
1219     selectNameFilter(filter);
1220 }
1221 
1222 /*!
1223     \since 4.4
1224 
1225     Returns the filter that the user selected in the file dialog.
1226 
1227     \sa selectedFiles()
1228 */
selectedNameFilter() const1229 QString QFileDialog::selectedNameFilter() const
1230 {
1231     Q_D(const QFileDialog);
1232     if (d->nativeDialogInUse)
1233         return d->selectedNameFilter_sys();
1234 
1235     return d->qFileDialogUi->fileTypeCombo->currentText();
1236 }
1237 
1238 /*!
1239     \obsolete
1240 
1241     Use selectedNameFilter() instead.
1242 */
selectedFilter() const1243 QString QFileDialog::selectedFilter() const
1244 {
1245     return selectedNameFilter();
1246 }
1247 
1248 /*!
1249     \since 4.4
1250 
1251     Returns the filter that is used when displaying files.
1252 
1253     \sa setFilter()
1254 */
filter() const1255 QDir::Filters QFileDialog::filter() const
1256 {
1257     Q_D(const QFileDialog);
1258     return d->model->filter();
1259 }
1260 
1261 /*!
1262     \since 4.4
1263 
1264     Sets the filter used by the model to \a filters. The filter is used
1265     to specify the kind of files that should be shown.
1266 
1267     \sa filter()
1268 */
1269 
setFilter(QDir::Filters filters)1270 void QFileDialog::setFilter(QDir::Filters filters)
1271 {
1272     Q_D(QFileDialog);
1273     d->model->setFilter(filters);
1274     if (d->nativeDialogInUse){
1275         d->setFilter_sys();
1276         return;
1277     }
1278 
1279     d->showHiddenAction->setChecked((filters & QDir::Hidden));
1280 }
1281 
1282 /*!
1283     \property QFileDialog::viewMode
1284     \brief the way files and directories are displayed in the dialog
1285 
1286     By default, the \c Detail mode is used to display information about
1287     files and directories.
1288 
1289     \sa ViewMode
1290 */
setViewMode(QFileDialog::ViewMode mode)1291 void QFileDialog::setViewMode(QFileDialog::ViewMode mode)
1292 {
1293     Q_D(QFileDialog);
1294     if (mode == Detail)
1295         d->_q_showDetailsView();
1296     else
1297         d->_q_showListView();
1298 }
1299 
viewMode() const1300 QFileDialog::ViewMode QFileDialog::viewMode() const
1301 {
1302     Q_D(const QFileDialog);
1303     return (d->qFileDialogUi->stackedWidget->currentWidget() == d->qFileDialogUi->listView->parent() ? QFileDialog::List : QFileDialog::Detail);
1304 }
1305 
1306 /*!
1307     \property QFileDialog::fileMode
1308     \brief the file mode of the dialog
1309 
1310     The file mode defines the number and type of items that the user is
1311     expected to select in the dialog.
1312 
1313     By default, this property is set to AnyFile.
1314 
1315     This function will set the labels for the FileName and
1316     \l{QFileDialog::}{Accept} \l{DialogLabel}s. It is possible to set
1317     custom text after the call to setFileMode().
1318 
1319     \sa FileMode
1320 */
setFileMode(QFileDialog::FileMode mode)1321 void QFileDialog::setFileMode(QFileDialog::FileMode mode)
1322 {
1323     Q_D(QFileDialog);
1324     d->fileMode = mode;
1325     d->retranslateWindowTitle();
1326 
1327     // keep ShowDirsOnly option in sync with fileMode (BTW, DirectoryOnly is obsolete)
1328     setOption(ShowDirsOnly, mode == DirectoryOnly);
1329 
1330     // set selection mode and behavior
1331     QAbstractItemView::SelectionMode selectionMode;
1332     if (mode == QFileDialog::ExistingFiles)
1333         selectionMode = QAbstractItemView::ExtendedSelection;
1334     else
1335         selectionMode = QAbstractItemView::SingleSelection;
1336     d->qFileDialogUi->listView->setSelectionMode(selectionMode);
1337     d->qFileDialogUi->treeView->setSelectionMode(selectionMode);
1338     // set filter
1339     d->model->setFilter(d->filterForMode(filter()));
1340     // setup file type for directory
1341     QString buttonText = (d->acceptMode == AcceptOpen ? tr("&Open") : tr("&Save"));
1342     if (mode == DirectoryOnly || mode == Directory) {
1343         d->qFileDialogUi->fileTypeCombo->clear();
1344         d->qFileDialogUi->fileTypeCombo->addItem(tr("Directories"));
1345         d->qFileDialogUi->fileTypeCombo->setEnabled(false);
1346 
1347         if (!d->fileNameLabelExplicitlySat){
1348             setLabelText(FileName, tr("Directory:"));
1349             d->fileNameLabelExplicitlySat = false;
1350         }
1351         buttonText = tr("&Choose");
1352     } else {
1353         if (!d->fileNameLabelExplicitlySat){
1354             setLabelText(FileName, tr("File &name:"));
1355             d->fileNameLabelExplicitlySat = false;
1356         }
1357     }
1358     setLabelText(Accept, buttonText);
1359     if (d->nativeDialogInUse){
1360         d->setFilter_sys();
1361         return;
1362     }
1363 
1364     d->qFileDialogUi->fileTypeCombo->setEnabled(!testOption(ShowDirsOnly));
1365     d->_q_updateOkButton();
1366 }
1367 
fileMode() const1368 QFileDialog::FileMode QFileDialog::fileMode() const
1369 {
1370     Q_D(const QFileDialog);
1371     return d->fileMode;
1372 }
1373 
1374 /*!
1375     \property QFileDialog::acceptMode
1376     \brief the accept mode of the dialog
1377 
1378     The action mode defines whether the dialog is for opening or saving files.
1379 
1380     By default, this property is set to \l{AcceptOpen}.
1381 
1382     \sa AcceptMode
1383 */
setAcceptMode(QFileDialog::AcceptMode mode)1384 void QFileDialog::setAcceptMode(QFileDialog::AcceptMode mode)
1385 {
1386     Q_D(QFileDialog);
1387     d->acceptMode = mode;
1388     bool directoryMode = (d->fileMode == Directory || d->fileMode == DirectoryOnly);
1389     QDialogButtonBox::StandardButton button = (mode == AcceptOpen ? QDialogButtonBox::Open : QDialogButtonBox::Save);
1390     d->qFileDialogUi->buttonBox->setStandardButtons(button | QDialogButtonBox::Cancel);
1391     d->qFileDialogUi->buttonBox->button(button)->setEnabled(false);
1392     d->_q_updateOkButton();
1393     if (mode == AcceptOpen && directoryMode)
1394         setLabelText(Accept, tr("&Choose"));
1395     else
1396         setLabelText(Accept, (mode == AcceptOpen ? tr("&Open") : tr("&Save")));
1397     if (mode == AcceptSave) {
1398         d->qFileDialogUi->lookInCombo->setEditable(false);
1399     }
1400     d->retranslateWindowTitle();
1401 #if defined(Q_WS_MAC)
1402     d->deleteNativeDialog_sys();
1403     setAttribute(Qt::WA_DontShowOnScreen, false);
1404 #endif
1405 }
1406 
1407 /*
1408     Returns the file system model index that is the root index in the
1409     views
1410 */
rootIndex() const1411 QModelIndex QFileDialogPrivate::rootIndex() const {
1412     return mapToSource(qFileDialogUi->listView->rootIndex());
1413 }
1414 
currentView() const1415 QAbstractItemView *QFileDialogPrivate::currentView() const {
1416     if (!qFileDialogUi->stackedWidget)
1417         return 0;
1418     if (qFileDialogUi->stackedWidget->currentWidget() == qFileDialogUi->listView->parent())
1419         return qFileDialogUi->listView;
1420     return qFileDialogUi->treeView;
1421 }
1422 
lineEdit() const1423 QLineEdit *QFileDialogPrivate::lineEdit() const {
1424     return (QLineEdit*)qFileDialogUi->fileNameEdit;
1425 }
1426 
1427 /*
1428     Sets the view root index to be the file system model index
1429 */
setRootIndex(const QModelIndex & index) const1430 void QFileDialogPrivate::setRootIndex(const QModelIndex &index) const {
1431     Q_ASSERT(index.isValid() ? index.model() == model : true);
1432     QModelIndex idx = mapFromSource(index);
1433     qFileDialogUi->treeView->setRootIndex(idx);
1434     qFileDialogUi->listView->setRootIndex(idx);
1435 }
1436 /*
1437     Select a file system model index
1438     returns the index that was selected (or not depending upon sortfilterproxymodel)
1439 */
select(const QModelIndex & index) const1440 QModelIndex QFileDialogPrivate::select(const QModelIndex &index) const {
1441     Q_ASSERT(index.isValid() ? index.model() == model : true);
1442 
1443     QModelIndex idx = mapFromSource(index);
1444     if (idx.isValid() && !qFileDialogUi->listView->selectionModel()->isSelected(idx))
1445         qFileDialogUi->listView->selectionModel()->select(idx,
1446             QItemSelectionModel::Select | QItemSelectionModel::Rows);
1447     return idx;
1448 }
1449 
acceptMode() const1450 QFileDialog::AcceptMode QFileDialog::acceptMode() const
1451 {
1452     Q_D(const QFileDialog);
1453     return d->acceptMode;
1454 }
1455 
1456 /*!
1457     \property QFileDialog::readOnly
1458     \obsolete
1459     \brief Whether the filedialog is read-only
1460 
1461     If this property is set to false, the file dialog will allow renaming,
1462     and deleting of files and directories and creating directories.
1463 
1464     Use setOption(ReadOnly, \e enabled) or testOption(ReadOnly) instead.
1465 */
setReadOnly(bool enabled)1466 void QFileDialog::setReadOnly(bool enabled)
1467 {
1468     setOption(ReadOnly, enabled);
1469 }
1470 
isReadOnly() const1471 bool QFileDialog::isReadOnly() const
1472 {
1473     return testOption(ReadOnly);
1474 }
1475 
1476 /*!
1477     \property QFileDialog::resolveSymlinks
1478     \obsolete
1479     \brief whether the filedialog should resolve shortcuts
1480 
1481     If this property is set to true, the file dialog will resolve
1482     shortcuts or symbolic links.
1483 
1484     Use setOption(DontResolveSymlinks, !\a enabled) or
1485     !testOption(DontResolveSymlinks).
1486 */
setResolveSymlinks(bool enabled)1487 void QFileDialog::setResolveSymlinks(bool enabled)
1488 {
1489     setOption(DontResolveSymlinks, !enabled);
1490 }
1491 
resolveSymlinks() const1492 bool QFileDialog::resolveSymlinks() const
1493 {
1494     return !testOption(DontResolveSymlinks);
1495 }
1496 
1497 /*!
1498     \property QFileDialog::confirmOverwrite
1499     \obsolete
1500     \brief whether the filedialog should ask before accepting a selected file,
1501     when the accept mode is AcceptSave
1502 
1503     Use setOption(DontConfirmOverwrite, !\e enabled) or
1504     !testOption(DontConfirmOverwrite) instead.
1505 */
setConfirmOverwrite(bool enabled)1506 void QFileDialog::setConfirmOverwrite(bool enabled)
1507 {
1508     setOption(DontConfirmOverwrite, !enabled);
1509 }
1510 
confirmOverwrite() const1511 bool QFileDialog::confirmOverwrite() const
1512 {
1513     return !testOption(DontConfirmOverwrite);
1514 }
1515 
1516 /*!
1517     \property QFileDialog::defaultSuffix
1518     \brief suffix added to the filename if no other suffix was specified
1519 
1520     This property specifies a string that will be added to the
1521     filename if it has no suffix already. The suffix is typically
1522     used to indicate the file type (e.g. "txt" indicates a text
1523     file).
1524 */
setDefaultSuffix(const QString & suffix)1525 void QFileDialog::setDefaultSuffix(const QString &suffix)
1526 {
1527     Q_D(QFileDialog);
1528     d->defaultSuffix = suffix;
1529 }
1530 
defaultSuffix() const1531 QString QFileDialog::defaultSuffix() const
1532 {
1533     Q_D(const QFileDialog);
1534     return d->defaultSuffix;
1535 }
1536 
1537 /*!
1538     Sets the browsing history of the filedialog to contain the given
1539     \a paths.
1540 */
setHistory(const QStringList & paths)1541 void QFileDialog::setHistory(const QStringList &paths)
1542 {
1543     Q_D(QFileDialog);
1544     d->qFileDialogUi->lookInCombo->setHistory(paths);
1545 }
1546 
setHistory(const QStringList & paths)1547 void QFileDialogComboBox::setHistory(const QStringList &paths)
1548 {
1549     m_history = paths;
1550     // Only populate the first item, showPopup will populate the rest if needed
1551     QList<QUrl> list;
1552     QModelIndex idx = d_ptr->model->index(d_ptr->rootPath());
1553     //On windows the popup display the "C:\", convert to nativeSeparators
1554     QUrl url = QUrl::fromLocalFile(QDir::toNativeSeparators(idx.data(QFileSystemModel::FilePathRole).toString()));
1555     if (url.isValid())
1556         list.append(url);
1557     urlModel->setUrls(list);
1558 }
1559 
1560 /*!
1561     Returns the browsing history of the filedialog as a list of paths.
1562 */
history() const1563 QStringList QFileDialog::history() const
1564 {
1565     Q_D(const QFileDialog);
1566     QStringList currentHistory = d->qFileDialogUi->lookInCombo->history();
1567     //On windows the popup display the "C:\", convert to nativeSeparators
1568     QString newHistory = QDir::toNativeSeparators(d->rootIndex().data(QFileSystemModel::FilePathRole).toString());
1569     if (!currentHistory.contains(newHistory))
1570         currentHistory << newHistory;
1571     return currentHistory;
1572 }
1573 
1574 /*!
1575     Sets the item delegate used to render items in the views in the
1576     file dialog to the given \a delegate.
1577 
1578     \warning You should not share the same instance of a delegate between views.
1579     Doing so can cause incorrect or unintuitive editing behavior since each
1580     view connected to a given delegate may receive the \l{QAbstractItemDelegate::}{closeEditor()}
1581     signal, and attempt to access, modify or close an editor that has already been closed.
1582 
1583     Note that the model used is QFileSystemModel. It has custom item data roles, which is
1584     described by the \l{QFileSystemModel::}{Roles} enum. You can use a QFileIconProvider if
1585     you only want custom icons.
1586 
1587     \sa itemDelegate(), setIconProvider(), QFileSystemModel
1588 */
setItemDelegate(QAbstractItemDelegate * delegate)1589 void QFileDialog::setItemDelegate(QAbstractItemDelegate *delegate)
1590 {
1591     Q_D(QFileDialog);
1592     d->qFileDialogUi->listView->setItemDelegate(delegate);
1593     d->qFileDialogUi->treeView->setItemDelegate(delegate);
1594 }
1595 
1596 /*!
1597   Returns the item delegate used to render the items in the views in the filedialog.
1598 */
itemDelegate() const1599 QAbstractItemDelegate *QFileDialog::itemDelegate() const
1600 {
1601     Q_D(const QFileDialog);
1602     return d->qFileDialogUi->listView->itemDelegate();
1603 }
1604 
1605 /*!
1606     Sets the icon provider used by the filedialog to the specified \a provider.
1607 */
setIconProvider(QFileIconProvider * provider)1608 void QFileDialog::setIconProvider(QFileIconProvider *provider)
1609 {
1610     Q_D(QFileDialog);
1611     d->model->setIconProvider(provider);
1612     //It forces the refresh of all entries in the side bar, then we can get new icons
1613     d->qFileDialogUi->sidebar->setUrls(d->qFileDialogUi->sidebar->urls());
1614 }
1615 
1616 /*!
1617     Returns the icon provider used by the filedialog.
1618 */
iconProvider() const1619 QFileIconProvider *QFileDialog::iconProvider() const
1620 {
1621     Q_D(const QFileDialog);
1622     return d->model->iconProvider();
1623 }
1624 
1625 /*!
1626     Sets the \a text shown in the filedialog in the specified \a label.
1627 */
setLabelText(DialogLabel label,const QString & text)1628 void QFileDialog::setLabelText(DialogLabel label, const QString &text)
1629 {
1630     Q_D(QFileDialog);
1631     QPushButton *button;
1632     switch (label) {
1633     case LookIn:
1634         d->qFileDialogUi->lookInLabel->setText(text);
1635         break;
1636     case FileName:
1637         d->qFileDialogUi->fileNameLabel->setText(text);
1638         d->fileNameLabelExplicitlySat = true;
1639         break;
1640     case FileType:
1641         d->qFileDialogUi->fileTypeLabel->setText(text);
1642         break;
1643     case Accept:
1644         d->acceptLabel = text;
1645         if (acceptMode() == AcceptOpen)
1646             button = d->qFileDialogUi->buttonBox->button(QDialogButtonBox::Open);
1647         else
1648             button = d->qFileDialogUi->buttonBox->button(QDialogButtonBox::Save);
1649         if (button)
1650             button->setText(text);
1651         break;
1652     case Reject:
1653         button = d->qFileDialogUi->buttonBox->button(QDialogButtonBox::Cancel);
1654         if (button)
1655             button->setText(text);
1656         break;
1657     }
1658 }
1659 
1660 /*!
1661     Returns the text shown in the filedialog in the specified \a label.
1662 */
labelText(DialogLabel label) const1663 QString QFileDialog::labelText(DialogLabel label) const
1664 {
1665     QPushButton *button;
1666     Q_D(const QFileDialog);
1667     switch (label) {
1668     case LookIn:
1669         return d->qFileDialogUi->lookInLabel->text();
1670     case FileName:
1671         return d->qFileDialogUi->fileNameLabel->text();
1672     case FileType:
1673         return d->qFileDialogUi->fileTypeLabel->text();
1674     case Accept:
1675         if (acceptMode() == AcceptOpen)
1676             button = d->qFileDialogUi->buttonBox->button(QDialogButtonBox::Open);
1677         else
1678             button = d->qFileDialogUi->buttonBox->button(QDialogButtonBox::Save);
1679         if (button)
1680             return button->text();
1681     case Reject:
1682         button = d->qFileDialogUi->buttonBox->button(QDialogButtonBox::Cancel);
1683         if (button)
1684             return button->text();
1685     }
1686     return QString();
1687 }
1688 
1689 /*
1690     For the native file dialogs
1691 */
1692 
1693 #if defined(Q_WS_WIN)
1694 extern QString qt_win_get_open_file_name(const QFileDialogArgs &args,
1695                                          QString *initialDirectory,
1696                                          QString *selectedFilter);
1697 
1698 extern QString qt_win_get_save_file_name(const QFileDialogArgs &args,
1699                                          QString *initialDirectory,
1700                                          QString *selectedFilter);
1701 
1702 extern QStringList qt_win_get_open_file_names(const QFileDialogArgs &args,
1703                                               QString *initialDirectory,
1704                                               QString *selectedFilter);
1705 
1706 extern QString qt_win_get_existing_directory(const QFileDialogArgs &args);
1707 #endif
1708 
1709 /*
1710     For Symbian file dialogs
1711 */
1712 #if defined(Q_WS_S60)
1713 extern QString qtSymbianGetOpenFileName(const QString &caption,
1714                                         const QString &dir,
1715                                         const QString &filter);
1716 
1717 extern QStringList qtSymbianGetOpenFileNames(const QString &caption,
1718                                              const QString &dir,
1719                                              const QString &filter);
1720 
1721 extern QString qtSymbianGetSaveFileName(const QString &caption,
1722                                         const QString &dir);
1723 
1724 extern QString qtSymbianGetExistingDirectory(const QString &caption,
1725                                              const QString &dir);
1726 #endif
1727 
1728 /*!
1729     This is a convenience static function that returns an existing file
1730     selected by the user. If the user presses Cancel, it returns a null string.
1731 
1732     \snippet doc/src/snippets/code/src_gui_dialogs_qfiledialog.cpp 8
1733 
1734     The function creates a modal file dialog with the given \a parent widget.
1735     If \a parent is not 0, the dialog will be shown centered over the parent
1736     widget.
1737 
1738     The file dialog's working directory will be set to \a dir. If \a dir
1739     includes a file name, the file will be selected. Only files that match the
1740     given \a filter are shown. The filter selected is set to \a selectedFilter.
1741     The parameters \a dir, \a selectedFilter, and \a filter may be empty
1742     strings. If you want multiple filters, separate them with ';;', for
1743     example:
1744 
1745     \code
1746     "Images (*.png *.xpm *.jpg);;Text files (*.txt);;XML files (*.xml)"
1747     \endcode
1748 
1749     The \a options argument holds various options about how to run the dialog,
1750     see the QFileDialog::Option enum for more information on the flags you can
1751     pass.
1752 
1753     The dialog's caption is set to \a caption. If \a caption is not specified
1754     then a default caption will be used.
1755 
1756     On Windows, Mac OS X and Symbian^3, this static function will use the
1757     native file dialog and not a QFileDialog.
1758 
1759     On Windows the dialog will spin a blocking modal event loop that will not
1760     dispatch any QTimers, and if \a parent is not 0 then it will position the
1761     dialog just below the parent's title bar.
1762 
1763     On Unix/X11, the normal behavior of the file dialog is to resolve and
1764     follow symlinks. For example, if \c{/usr/tmp} is a symlink to \c{/var/tmp},
1765     the file dialog will change to \c{/var/tmp} after entering \c{/usr/tmp}. If
1766     \a options includes DontResolveSymlinks, the file dialog will treat
1767     symlinks as regular directories.
1768 
1769     On Symbian^3 the parameter \a selectedFilter has no meaning and the
1770     \a options parameter is only used to define if the native file dialog is
1771     used.
1772 
1773     \warning Do not delete \a parent during the execution of the dialog. If you
1774     want to do this, you should create the dialog yourself using one of the
1775     QFileDialog constructors.
1776 
1777     \sa getOpenFileNames(), getSaveFileName(), getExistingDirectory()
1778 */
getOpenFileName(QWidget * parent,const QString & caption,const QString & dir,const QString & filter,QString * selectedFilter,Options options)1779 QString QFileDialog::getOpenFileName(QWidget *parent,
1780                                const QString &caption,
1781                                const QString &dir,
1782                                const QString &filter,
1783                                QString *selectedFilter,
1784                                Options options)
1785 {
1786     if (qt_filedialog_open_filename_hook && !(options & DontUseNativeDialog))
1787         return qt_filedialog_open_filename_hook(parent, caption, dir, filter, selectedFilter, options);
1788 #if defined(Q_WS_S60)
1789     if (QSysInfo::s60Version() > QSysInfo::SV_S60_5_0 && !(options & DontUseNativeDialog))
1790         return qtSymbianGetOpenFileName(caption, dir, filter);
1791 #endif
1792     QFileDialogArgs args;
1793     args.parent = parent;
1794     args.caption = caption;
1795     args.directory = QFileDialogPrivate::workingDirectory(dir);
1796     args.selection = QFileDialogPrivate::initialSelection(dir);
1797     args.filter = filter;
1798     args.mode = ExistingFile;
1799     args.options = options;
1800 #if defined(Q_WS_WIN)
1801     if (qt_use_native_dialogs && !(args.options & DontUseNativeDialog)) {
1802         return qt_win_get_open_file_name(args, &(args.directory), selectedFilter);
1803     }
1804 #endif
1805 
1806     // create a qt dialog
1807     QFileDialog dialog(args);
1808     if (selectedFilter && !selectedFilter->isEmpty())
1809         dialog.selectNameFilter(*selectedFilter);
1810     if (dialog.exec() == QDialog::Accepted) {
1811         if (selectedFilter)
1812             *selectedFilter = dialog.selectedFilter();
1813         return dialog.selectedFiles().value(0);
1814     }
1815     return QString();
1816 }
1817 
1818 /*!
1819     This is a convenience static function that will return one or more existing
1820     files selected by the user.
1821 
1822     \snippet doc/src/snippets/code/src_gui_dialogs_qfiledialog.cpp 9
1823 
1824     This function creates a modal file dialog with the given \a parent widget.
1825     If \a parent is not 0, the dialog will be shown centered over the parent
1826     widget.
1827 
1828     The file dialog's working directory will be set to \a dir. If \a dir
1829     includes a file name, the file will be selected. The filter is set to
1830     \a filter so that only those files which match the filter are shown. The
1831     filter selected is set to \a selectedFilter. The parameters \a dir,
1832     \a selectedFilter and \a filter may be empty strings. If you need multiple
1833     filters, separate them with ';;', for instance:
1834 
1835     \code
1836     "Images (*.png *.xpm *.jpg);;Text files (*.txt);;XML files (*.xml)"
1837     \endcode
1838 
1839     The dialog's caption is set to \a caption. If \a caption is not specified
1840     then a default caption will be used.
1841 
1842     On Windows, Mac OS X and Symbian^3, this static function will use the
1843     native file dialog and not a QFileDialog.
1844 
1845     On Windows the dialog will spin a blocking modal event loop that will not
1846     dispatch any QTimers, and if \a parent is not 0 then it will position the
1847     dialog just below the parent's title bar.
1848 
1849     On Unix/X11, the normal behavior of the file dialog is to resolve and
1850     follow symlinks. For example, if \c{/usr/tmp} is a symlink to \c{/var/tmp},
1851     the file dialog will change to \c{/var/tmp} after entering \c{/usr/tmp}.
1852     The \a options argument holds various options about how to run the dialog,
1853     see the QFileDialog::Option enum for more information on the flags you can
1854     pass.
1855 
1856     \note If you want to iterate over the list of files, you should iterate
1857     over a copy. For example:
1858 
1859     \snippet doc/src/snippets/code/src_gui_dialogs_qfiledialog.cpp 10
1860 
1861     On Symbian^3 the parameter \a selectedFilter has no meaning and the
1862     \a options parameter is only used to define if the native file dialog is
1863     used. On Symbian^3, this function can only return a single filename.
1864 
1865     \warning Do not delete \a parent during the execution of the dialog. If you
1866     want to do this, you should create the dialog yourself using one of the
1867     QFileDialog constructors.
1868 
1869     \sa getOpenFileName(), getSaveFileName(), getExistingDirectory()
1870 */
getOpenFileNames(QWidget * parent,const QString & caption,const QString & dir,const QString & filter,QString * selectedFilter,Options options)1871 QStringList QFileDialog::getOpenFileNames(QWidget *parent,
1872                                           const QString &caption,
1873                                           const QString &dir,
1874                                           const QString &filter,
1875                                           QString *selectedFilter,
1876                                           Options options)
1877 {
1878     if (qt_filedialog_open_filenames_hook && !(options & DontUseNativeDialog))
1879         return qt_filedialog_open_filenames_hook(parent, caption, dir, filter, selectedFilter, options);
1880 #if defined(Q_WS_S60)
1881     if (QSysInfo::s60Version() > QSysInfo::SV_S60_5_0 && !(options & DontUseNativeDialog))
1882         return qtSymbianGetOpenFileNames(caption, dir, filter);
1883 #endif
1884     QFileDialogArgs args;
1885     args.parent = parent;
1886     args.caption = caption;
1887     args.directory = QFileDialogPrivate::workingDirectory(dir);
1888     args.selection = QFileDialogPrivate::initialSelection(dir);
1889     args.filter = filter;
1890     args.mode = ExistingFiles;
1891     args.options = options;
1892 
1893 #if defined(Q_WS_WIN)
1894     if (qt_use_native_dialogs && !(args.options & DontUseNativeDialog)) {
1895         return qt_win_get_open_file_names(args, &(args.directory), selectedFilter);
1896     }
1897 #endif
1898 
1899     // create a qt dialog
1900     QFileDialog dialog(args);
1901     if (selectedFilter && !selectedFilter->isEmpty())
1902         dialog.selectNameFilter(*selectedFilter);
1903     if (dialog.exec() == QDialog::Accepted) {
1904         if (selectedFilter)
1905             *selectedFilter = dialog.selectedFilter();
1906         return dialog.selectedFiles();
1907     }
1908     return QStringList();
1909 }
1910 
1911 /*!
1912     This is a convenience static function that will return a file name selected
1913     by the user. The file does not have to exist.
1914 
1915     It creates a modal file dialog with the given \a parent widget. If
1916     \a parent is not 0, the dialog will be shown centered over the parent
1917     widget.
1918 
1919     \snippet doc/src/snippets/code/src_gui_dialogs_qfiledialog.cpp 11
1920 
1921     The file dialog's working directory will be set to \a dir. If \a dir
1922     includes a file name, the file will be selected. Only files that match the
1923     \a filter are shown. The filter selected is set to \a selectedFilter. The
1924     parameters \a dir, \a selectedFilter, and \a filter may be empty strings.
1925     Multiple filters are separated with ';;'. For instance:
1926 
1927     \code
1928     "Images (*.png *.xpm *.jpg);;Text files (*.txt);;XML files (*.xml)"
1929     \endcode
1930 
1931     The \a options argument holds various options about how to run the dialog,
1932     see the QFileDialog::Option enum for more information on the flags you can
1933     pass.
1934 
1935     The default filter can be chosen by setting \a selectedFilter to the
1936     desired value.
1937 
1938     The dialog's caption is set to \a caption. If \a caption is not specified,
1939     a default caption will be used.
1940 
1941     On Windows, Mac OS X and Symbian^3, this static function will use the
1942     native file dialog and not a QFileDialog.
1943 
1944     On Windows the dialog will spin a blocking modal event loop that will not
1945     dispatch any QTimers, and if \a parent is not 0 then it will position the
1946     dialog just below the parent's title bar. On Mac OS X, with its native file
1947     dialog, the filter argument is ignored.
1948 
1949     On Unix/X11, the normal behavior of the file dialog is to resolve and
1950     follow symlinks. For example, if \c{/usr/tmp} is a symlink to \c{/var/tmp},
1951     the file dialog will change to \c{/var/tmp} after entering \c{/usr/tmp}. If
1952     \a options includes DontResolveSymlinks the file dialog will treat symlinks
1953     as regular directories.
1954 
1955     On Symbian^3 the parameters \a filter and \a selectedFilter have no
1956     meaning. The \a options parameter is only used to define if the native file
1957     dialog is used.
1958 
1959     \warning Do not delete \a parent during the execution of the dialog. If you
1960     want to do this, you should create the dialog yourself using one of the
1961     QFileDialog constructors.
1962 
1963     \sa getOpenFileName(), getOpenFileNames(), getExistingDirectory()
1964 */
getSaveFileName(QWidget * parent,const QString & caption,const QString & dir,const QString & filter,QString * selectedFilter,Options options)1965 QString QFileDialog::getSaveFileName(QWidget *parent,
1966                                      const QString &caption,
1967                                      const QString &dir,
1968                                      const QString &filter,
1969                                      QString *selectedFilter,
1970                                      Options options)
1971 {
1972     if (qt_filedialog_save_filename_hook && !(options & DontUseNativeDialog))
1973         return qt_filedialog_save_filename_hook(parent, caption, dir, filter, selectedFilter, options);
1974 #if defined(Q_WS_S60)
1975     if (QSysInfo::s60Version() > QSysInfo::SV_S60_5_0 && !(options & DontUseNativeDialog))
1976         return qtSymbianGetSaveFileName(caption, dir);
1977 #endif
1978     QFileDialogArgs args;
1979     args.parent = parent;
1980     args.caption = caption;
1981     args.directory = QFileDialogPrivate::workingDirectory(dir);
1982     args.selection = QFileDialogPrivate::initialSelection(dir);
1983     args.filter = filter;
1984     args.mode = AnyFile;
1985     args.options = options;
1986 
1987 #if defined(Q_WS_WIN)
1988     if (qt_use_native_dialogs && !(args.options & DontUseNativeDialog)) {
1989         return qt_win_get_save_file_name(args, &(args.directory), selectedFilter);
1990     }
1991 #endif
1992 
1993     // create a qt dialog
1994     QFileDialog dialog(args);
1995     dialog.setAcceptMode(AcceptSave);
1996     if (selectedFilter && !selectedFilter->isEmpty())
1997         dialog.selectNameFilter(*selectedFilter);
1998     if (dialog.exec() == QDialog::Accepted) {
1999         if (selectedFilter)
2000             *selectedFilter = dialog.selectedFilter();
2001         return dialog.selectedFiles().value(0);
2002     }
2003 
2004     return QString();
2005 }
2006 
2007 /*!
2008     This is a convenience static function that will return an existing
2009     directory selected by the user.
2010 
2011     \snippet doc/src/snippets/code/src_gui_dialogs_qfiledialog.cpp 12
2012 
2013     This function creates a modal file dialog with the given \a parent widget.
2014     If \a parent is not 0, the dialog will be shown centered over the parent
2015     widget.
2016 
2017     The dialog's working directory is set to \a dir, and the caption is set to
2018     \a caption. Either of these may be an empty string in which case the
2019     current directory and a default caption will be used respectively.
2020 
2021     The \a options argument holds various options about how to run the dialog,
2022     see the QFileDialog::Option enum for more information on the flags you can
2023     pass. To ensure a native file dialog, \l{QFileDialog::}{ShowDirsOnly} must
2024     be set.
2025 
2026     On Windows, Mac OS X and Symbian^3, this static function will use the
2027     native file dialog and not a QFileDialog. On Windows CE, if the device has
2028     no native file dialog, a QFileDialog will be used.
2029 
2030     On Unix/X11, the normal behavior of the file dialog is to resolve and
2031     follow symlinks. For example, if \c{/usr/tmp} is a symlink to \c{/var/tmp},
2032     the file dialog will change to \c{/var/tmp} after entering \c{/usr/tmp}. If
2033     \a options includes DontResolveSymlinks, the file dialog will treat
2034     symlinks as regular directories.
2035 
2036     On Windows the dialog will spin a blocking modal event loop that will not
2037     dispatch any QTimers, and if \a parent is not 0 then it will position the
2038     dialog just below the parent's title bar.
2039 
2040     On Symbian^3 the \a options parameter is only used to define if the native
2041     file dialog is used.
2042 
2043     \warning Do not delete \a parent during the execution of the dialog. If you
2044     want to do this, you should create the dialog yourself using one of the
2045     QFileDialog constructors.
2046 
2047     \sa getOpenFileName(), getOpenFileNames(), getSaveFileName()
2048 */
getExistingDirectory(QWidget * parent,const QString & caption,const QString & dir,Options options)2049 QString QFileDialog::getExistingDirectory(QWidget *parent,
2050                                           const QString &caption,
2051                                           const QString &dir,
2052                                           Options options)
2053 {
2054     if (qt_filedialog_existing_directory_hook && !(options & DontUseNativeDialog))
2055         return qt_filedialog_existing_directory_hook(parent, caption, dir, options);
2056 #if defined(Q_WS_S60)
2057     if (QSysInfo::s60Version() > QSysInfo::SV_S60_5_0 && !(options & DontUseNativeDialog))
2058         return qtSymbianGetExistingDirectory(caption, dir);
2059 #endif
2060     QFileDialogArgs args;
2061     args.parent = parent;
2062     args.caption = caption;
2063     args.directory = QFileDialogPrivate::workingDirectory(dir);
2064     args.mode = (options & ShowDirsOnly ? DirectoryOnly : Directory);
2065     args.options = options;
2066 
2067 #if defined(Q_WS_WIN)
2068     if (qt_use_native_dialogs && !(args.options & DontUseNativeDialog) && (options & ShowDirsOnly)
2069 #if defined(Q_WS_WINCE)
2070         && qt_priv_ptr_valid
2071 #endif
2072         ) {
2073         return qt_win_get_existing_directory(args);
2074     }
2075 #endif
2076 
2077     // create a qt dialog
2078     QFileDialog dialog(args);
2079     if (dialog.exec() == QDialog::Accepted) {
2080         return dialog.selectedFiles().value(0);
2081     }
2082     return QString();
2083 }
2084 
_qt_get_directory(const QString & path)2085 inline static QString _qt_get_directory(const QString &path)
2086 {
2087     QFileInfo info = QFileInfo(QDir::current(), path);
2088     if (info.exists() && info.isDir())
2089         return QDir::cleanPath(info.absoluteFilePath());
2090     info.setFile(info.absolutePath());
2091     if (info.exists() && info.isDir())
2092         return info.absoluteFilePath();
2093     return QString();
2094 }
2095 /*
2096     Get the initial directory path
2097 
2098     \sa initialSelection()
2099  */
workingDirectory(const QString & path)2100 QString QFileDialogPrivate::workingDirectory(const QString &path)
2101 {
2102     if (!path.isEmpty()) {
2103         QString directory = _qt_get_directory(path);
2104         if (!directory.isEmpty())
2105             return directory;
2106     }
2107     QString directory = _qt_get_directory(*lastVisitedDir());
2108     if (!directory.isEmpty())
2109         return directory;
2110     return QDir::currentPath();
2111 }
2112 
2113 /*
2114     Get the initial selection given a path.  The initial directory
2115     can contain both the initial directory and initial selection
2116     /home/user/foo.txt
2117 
2118     \sa workingDirectory()
2119  */
initialSelection(const QString & path)2120 QString QFileDialogPrivate::initialSelection(const QString &path)
2121 {
2122     if (!path.isEmpty()) {
2123         QFileInfo info(path);
2124         if (!info.isDir())
2125             return info.fileName();
2126     }
2127     return QString();
2128 }
2129 
2130 /*!
2131  \reimp
2132 */
done(int result)2133 void QFileDialog::done(int result)
2134 {
2135     Q_D(QFileDialog);
2136 
2137     QDialog::done(result);
2138 
2139     if (d->receiverToDisconnectOnClose) {
2140         disconnect(this, d->signalToDisconnectOnClose,
2141                    d->receiverToDisconnectOnClose, d->memberToDisconnectOnClose);
2142         d->receiverToDisconnectOnClose = 0;
2143     }
2144     d->memberToDisconnectOnClose.clear();
2145     d->signalToDisconnectOnClose.clear();
2146 }
2147 
2148 /*!
2149  \reimp
2150 */
accept()2151 void QFileDialog::accept()
2152 {
2153     Q_D(QFileDialog);
2154     QStringList files = selectedFiles();
2155     if (files.isEmpty())
2156         return;
2157     if (d->nativeDialogInUse){
2158         d->emitFilesSelected(files);
2159         QDialog::accept();
2160         return;
2161     }
2162 
2163     QString lineEditText = d->lineEdit()->text();
2164     // "hidden feature" type .. and then enter, and it will move up a dir
2165     // special case for ".."
2166     if (lineEditText == QLatin1String("..")) {
2167         d->_q_navigateToParent();
2168         bool block = d->qFileDialogUi->fileNameEdit->blockSignals(true);
2169         d->lineEdit()->selectAll();
2170         d->qFileDialogUi->fileNameEdit->blockSignals(block);
2171         return;
2172     }
2173 
2174     switch (d->fileMode) {
2175     case DirectoryOnly:
2176     case Directory: {
2177         QString fn = files.first();
2178         QFileInfo info(fn);
2179         if (!info.exists())
2180             info = QFileInfo(d->getEnvironmentVariable(fn));
2181         if (!info.exists()) {
2182 #ifndef QT_NO_MESSAGEBOX
2183             QString message = tr("%1\nDirectory not found.\nPlease verify the "
2184                                           "correct directory name was given.");
2185             QMessageBox::warning(this, windowTitle(), message.arg(info.fileName()));
2186 #endif // QT_NO_MESSAGEBOX
2187             return;
2188         }
2189         if (info.isDir()) {
2190             d->emitFilesSelected(files);
2191             QDialog::accept();
2192         }
2193         return;
2194     }
2195 
2196     case AnyFile: {
2197         QString fn = files.first();
2198         QFileInfo info(fn);
2199         if (info.isDir()) {
2200             setDirectory(info.absoluteFilePath());
2201             return;
2202         }
2203 
2204         if (!info.exists()) {
2205             int maxNameLength = d->maxNameLength(info.path());
2206             if (maxNameLength >= 0 && info.fileName().length() > maxNameLength)
2207                 return;
2208         }
2209 
2210         // check if we have to ask for permission to overwrite the file
2211         if (!info.exists() || !confirmOverwrite() || acceptMode() == AcceptOpen) {
2212             d->emitFilesSelected(QStringList(fn));
2213             QDialog::accept();
2214 #ifndef QT_NO_MESSAGEBOX
2215         } else {
2216             if (QMessageBox::warning(this, windowTitle(),
2217                                      tr("%1 already exists.\nDo you want to replace it?")
2218                                      .arg(info.fileName()),
2219                                      QMessageBox::Yes | QMessageBox::No, QMessageBox::No)
2220                     == QMessageBox::Yes) {
2221                 d->emitFilesSelected(QStringList(fn));
2222                 QDialog::accept();
2223             }
2224 #endif
2225         }
2226         return;
2227     }
2228 
2229     case ExistingFile:
2230     case ExistingFiles:
2231         for (int i = 0; i < files.count(); ++i) {
2232             QFileInfo info(files.at(i));
2233             if (!info.exists())
2234                 info = QFileInfo(d->getEnvironmentVariable(files.at(i)));
2235             if (!info.exists()) {
2236 #ifndef QT_NO_MESSAGEBOX
2237                 QString message = tr("%1\nFile not found.\nPlease verify the "
2238                                      "correct file name was given.");
2239                 QMessageBox::warning(this, windowTitle(), message.arg(info.fileName()));
2240 #endif // QT_NO_MESSAGEBOX
2241                 return;
2242             }
2243             if (info.isDir()) {
2244                 setDirectory(info.absoluteFilePath());
2245                 d->lineEdit()->clear();
2246                 return;
2247             }
2248         }
2249         d->emitFilesSelected(files);
2250         QDialog::accept();
2251         return;
2252     }
2253 }
2254 
2255 /*!
2256     \internal
2257 
2258     Create widgets, layout and set default values
2259 */
init(const QString & directory,const QString & nameFilter,const QString & caption)2260 void QFileDialogPrivate::init(const QString &directory, const QString &nameFilter,
2261                               const QString &caption)
2262 {
2263     Q_Q(QFileDialog);
2264     if (!caption.isEmpty()) {
2265         useDefaultCaption = false;
2266         setWindowTitle = caption;
2267         q->setWindowTitle(caption);
2268     }
2269 
2270     createWidgets();
2271     createMenuActions();
2272     retranslateStrings();
2273     q->setFileMode(fileMode);
2274 
2275 #ifndef QT_NO_SETTINGS
2276     QSettings settings(QSettings::UserScope, QLatin1String("Trolltech"));
2277     settings.beginGroup(QLatin1String("Qt"));
2278     if (!directory.isEmpty())
2279         setLastVisitedDirectory(workingDirectory(directory));
2280     q->restoreState(settings.value(QLatin1String("filedialog")).toByteArray());
2281 #endif
2282 
2283 #if defined(Q_EMBEDDED_SMALLSCREEN)
2284     qFileDialogUi->lookInLabel->setVisible(false);
2285     qFileDialogUi->fileNameLabel->setVisible(false);
2286     qFileDialogUi->fileTypeLabel->setVisible(false);
2287     qFileDialogUi->sidebar->hide();
2288 #endif
2289     // Default case
2290     if (!nameFilter.isEmpty())
2291         q->setNameFilter(nameFilter);
2292     q->setAcceptMode(QFileDialog::AcceptOpen);
2293     q->setDirectory(workingDirectory(directory));
2294     q->selectFile(initialSelection(directory));
2295 
2296     _q_updateOkButton();
2297     q->resize(q->sizeHint());
2298 }
2299 
2300 /*!
2301     \internal
2302 
2303     Create the widgets, set properties and connections
2304 */
createWidgets()2305 void QFileDialogPrivate::createWidgets()
2306 {
2307     Q_Q(QFileDialog);
2308     model = new QFileSystemModel(q);
2309     model->setObjectName(QLatin1String("qt_filesystem_model"));
2310 #ifdef Q_WS_MAC
2311     model->setNameFilterDisables(true);
2312 #else
2313     model->setNameFilterDisables(false);
2314 #endif
2315     model->d_func()->disableRecursiveSort = true;
2316     QFileDialog::connect(model, SIGNAL(fileRenamed(QString,QString,QString)), q, SLOT(_q_fileRenamed(QString,QString,QString)));
2317     QFileDialog::connect(model, SIGNAL(rootPathChanged(QString)),
2318             q, SLOT(_q_pathChanged(QString)));
2319     QFileDialog::connect(model, SIGNAL(rowsInserted(QModelIndex,int,int)),
2320             q, SLOT(_q_rowsInserted(QModelIndex)));
2321     model->setReadOnly(false);
2322 
2323     qFileDialogUi.reset(new Ui_QFileDialog());
2324     qFileDialogUi->setupUi(q);
2325 
2326     QList<QUrl> initialBookmarks;
2327     initialBookmarks << QUrl::fromLocalFile(QLatin1String(""))
2328                      << QUrl::fromLocalFile(QDir::homePath());
2329     qFileDialogUi->sidebar->init(model, initialBookmarks);
2330     QFileDialog::connect(qFileDialogUi->sidebar, SIGNAL(goToUrl(QUrl)),
2331                          q, SLOT(_q_goToUrl(QUrl)));
2332 
2333     QObject::connect(qFileDialogUi->buttonBox, SIGNAL(accepted()), q, SLOT(accept()));
2334     QObject::connect(qFileDialogUi->buttonBox, SIGNAL(rejected()), q, SLOT(reject()));
2335 
2336 
2337     qFileDialogUi->lookInCombo->init(this);
2338     QObject::connect(qFileDialogUi->lookInCombo, SIGNAL(activated(QString)), q, SLOT(_q_goToDirectory(QString)));
2339 
2340     qFileDialogUi->lookInCombo->setInsertPolicy(QComboBox::NoInsert);
2341     qFileDialogUi->lookInCombo->setDuplicatesEnabled(false);
2342 
2343     // filename
2344     qFileDialogUi->fileNameEdit->init(this);
2345 #ifndef QT_NO_SHORTCUT
2346     qFileDialogUi->fileNameLabel->setBuddy(qFileDialogUi->fileNameEdit);
2347 #endif
2348 #ifndef QT_NO_FSCOMPLETER
2349     completer = new QFSCompleter(model, q);
2350     qFileDialogUi->fileNameEdit->setCompleter(completer);
2351 #endif // QT_NO_FSCOMPLETER
2352     QObject::connect(qFileDialogUi->fileNameEdit, SIGNAL(textChanged(QString)),
2353             q, SLOT(_q_autoCompleteFileName(QString)));
2354     QObject::connect(qFileDialogUi->fileNameEdit, SIGNAL(textChanged(QString)),
2355                      q, SLOT(_q_updateOkButton()));
2356 
2357     QObject::connect(qFileDialogUi->fileNameEdit, SIGNAL(returnPressed()), q, SLOT(accept()));
2358 
2359     // filetype
2360     qFileDialogUi->fileTypeCombo->setDuplicatesEnabled(false);
2361     qFileDialogUi->fileTypeCombo->setSizeAdjustPolicy(QComboBox::AdjustToMinimumContentsLength);
2362     qFileDialogUi->fileTypeCombo->setSizePolicy(QSizePolicy::Expanding, QSizePolicy::Fixed);
2363     QObject::connect(qFileDialogUi->fileTypeCombo, SIGNAL(activated(int)),
2364                      q, SLOT(_q_useNameFilter(int)));
2365     QObject::connect(qFileDialogUi->fileTypeCombo, SIGNAL(activated(QString)),
2366                      q, SIGNAL(filterSelected(QString)));
2367 
2368     qFileDialogUi->listView->init(this);
2369     qFileDialogUi->listView->setModel(model);
2370     QObject::connect(qFileDialogUi->listView, SIGNAL(activated(QModelIndex)),
2371                      q, SLOT(_q_enterDirectory(QModelIndex)));
2372     QObject::connect(qFileDialogUi->listView, SIGNAL(customContextMenuRequested(QPoint)),
2373                     q, SLOT(_q_showContextMenu(QPoint)));
2374 #ifndef QT_NO_SHORTCUT
2375     QShortcut *shortcut = new QShortcut(qFileDialogUi->listView);
2376     shortcut->setKey(QKeySequence(QLatin1String("Delete")));
2377     QObject::connect(shortcut, SIGNAL(activated()), q, SLOT(_q_deleteCurrent()));
2378 #endif
2379 
2380     qFileDialogUi->treeView->init(this);
2381     qFileDialogUi->treeView->setModel(model);
2382     QHeaderView *treeHeader = qFileDialogUi->treeView->header();
2383     QFontMetrics fm(q->font());
2384     treeHeader->resizeSection(0, fm.width(QLatin1String("wwwwwwwwwwwwwwwwwwwwwwwwww")));
2385     treeHeader->resizeSection(1, fm.width(QLatin1String("128.88 GB")));
2386     treeHeader->resizeSection(2, fm.width(QLatin1String("mp3Folder")));
2387     treeHeader->resizeSection(3, fm.width(QLatin1String("10/29/81 02:02PM")));
2388     treeHeader->setContextMenuPolicy(Qt::ActionsContextMenu);
2389 
2390     QActionGroup *showActionGroup = new QActionGroup(q);
2391     showActionGroup->setExclusive(false);
2392     QObject::connect(showActionGroup, SIGNAL(triggered(QAction*)),
2393                      q, SLOT(_q_showHeader(QAction*)));;
2394 
2395     QAbstractItemModel *abstractModel = model;
2396 #ifndef QT_NO_PROXYMODEL
2397     if (proxyModel)
2398         abstractModel = proxyModel;
2399 #endif
2400     for (int i = 1; i < abstractModel->columnCount(QModelIndex()); ++i) {
2401         QAction *showHeader = new QAction(showActionGroup);
2402         showHeader->setCheckable(true);
2403         showHeader->setChecked(true);
2404         treeHeader->addAction(showHeader);
2405     }
2406 
2407     QScopedPointer<QItemSelectionModel> selModel(qFileDialogUi->treeView->selectionModel());
2408     qFileDialogUi->treeView->setSelectionModel(qFileDialogUi->listView->selectionModel());
2409 
2410     QObject::connect(qFileDialogUi->treeView, SIGNAL(activated(QModelIndex)),
2411                      q, SLOT(_q_enterDirectory(QModelIndex)));
2412     QObject::connect(qFileDialogUi->treeView, SIGNAL(customContextMenuRequested(QPoint)),
2413                      q, SLOT(_q_showContextMenu(QPoint)));
2414 #ifndef QT_NO_SHORTCUT
2415     shortcut = new QShortcut(qFileDialogUi->treeView);
2416     shortcut->setKey(QKeySequence(QLatin1String("Delete")));
2417     QObject::connect(shortcut, SIGNAL(activated()), q, SLOT(_q_deleteCurrent()));
2418 #endif
2419 
2420     // Selections
2421     QItemSelectionModel *selections = qFileDialogUi->listView->selectionModel();
2422     QObject::connect(selections, SIGNAL(selectionChanged(QItemSelection,QItemSelection)),
2423                      q, SLOT(_q_selectionChanged()));
2424     QObject::connect(selections, SIGNAL(currentChanged(QModelIndex,QModelIndex)),
2425                      q, SLOT(_q_currentChanged(QModelIndex)));
2426     qFileDialogUi->splitter->setStretchFactor(qFileDialogUi->splitter->indexOf(qFileDialogUi->splitter->widget(1)), QSizePolicy::Expanding);
2427 
2428     createToolButtons();
2429 }
2430 
_q_showHeader(QAction * action)2431 void QFileDialogPrivate::_q_showHeader(QAction *action)
2432 {
2433     Q_Q(QFileDialog);
2434     QActionGroup *actionGroup = qobject_cast<QActionGroup*>(q->sender());
2435     qFileDialogUi->treeView->header()->setSectionHidden(actionGroup->actions().indexOf(action) + 1, !action->isChecked());
2436 }
2437 
2438 #ifndef QT_NO_PROXYMODEL
2439 /*!
2440     \since 4.3
2441 
2442     Sets the model for the views to the given \a proxyModel.  This is useful if you
2443     want to modify the underlying model; for example, to add columns, filter
2444     data or add drives.
2445 
2446     Any existing proxy model will be removed, but not deleted.  The file dialog
2447     will take ownership of the \a proxyModel.
2448 
2449     \sa proxyModel()
2450 */
setProxyModel(QAbstractProxyModel * proxyModel)2451 void QFileDialog::setProxyModel(QAbstractProxyModel *proxyModel)
2452 {
2453     Q_D(QFileDialog);
2454     if ((!proxyModel && !d->proxyModel)
2455         || (proxyModel == d->proxyModel))
2456         return;
2457 
2458     QModelIndex idx = d->rootIndex();
2459     if (d->proxyModel) {
2460         disconnect(d->proxyModel, SIGNAL(rowsInserted(QModelIndex,int,int)),
2461             this, SLOT(_q_rowsInserted(QModelIndex)));
2462     } else {
2463         disconnect(d->model, SIGNAL(rowsInserted(QModelIndex,int,int)),
2464             this, SLOT(_q_rowsInserted(QModelIndex)));
2465     }
2466 
2467     if (proxyModel != 0) {
2468         proxyModel->setParent(this);
2469         d->proxyModel = proxyModel;
2470         proxyModel->setSourceModel(d->model);
2471         d->qFileDialogUi->listView->setModel(d->proxyModel);
2472         d->qFileDialogUi->treeView->setModel(d->proxyModel);
2473 #ifndef QT_NO_FSCOMPLETER
2474         d->completer->setModel(d->proxyModel);
2475         d->completer->proxyModel = d->proxyModel;
2476 #endif
2477         connect(d->proxyModel, SIGNAL(rowsInserted(QModelIndex,int,int)),
2478             this, SLOT(_q_rowsInserted(QModelIndex)));
2479     } else {
2480         d->proxyModel = 0;
2481         d->qFileDialogUi->listView->setModel(d->model);
2482         d->qFileDialogUi->treeView->setModel(d->model);
2483 #ifndef QT_NO_FSCOMPLETER
2484         d->completer->setModel(d->model);
2485         d->completer->sourceModel = d->model;
2486         d->completer->proxyModel = 0;
2487 #endif
2488         connect(d->model, SIGNAL(rowsInserted(QModelIndex,int,int)),
2489             this, SLOT(_q_rowsInserted(QModelIndex)));
2490     }
2491     QScopedPointer<QItemSelectionModel> selModel(d->qFileDialogUi->treeView->selectionModel());
2492     d->qFileDialogUi->treeView->setSelectionModel(d->qFileDialogUi->listView->selectionModel());
2493 
2494     d->setRootIndex(idx);
2495 
2496     // reconnect selection
2497     QItemSelectionModel *selections = d->qFileDialogUi->listView->selectionModel();
2498     QObject::connect(selections, SIGNAL(selectionChanged(QItemSelection,QItemSelection)),
2499                      this, SLOT(_q_selectionChanged()));
2500     QObject::connect(selections, SIGNAL(currentChanged(QModelIndex,QModelIndex)),
2501                      this, SLOT(_q_currentChanged(QModelIndex)));
2502 }
2503 
2504 /*!
2505     Returns the proxy model used by the file dialog.  By default no proxy is set.
2506 
2507     \sa setProxyModel()
2508 */
proxyModel() const2509 QAbstractProxyModel *QFileDialog::proxyModel() const
2510 {
2511     Q_D(const QFileDialog);
2512     return d->proxyModel;
2513 }
2514 #endif // QT_NO_PROXYMODEL
2515 
2516 /*!
2517     \internal
2518 
2519     Create tool buttons, set properties and connections
2520 */
createToolButtons()2521 void QFileDialogPrivate::createToolButtons()
2522 {
2523     Q_Q(QFileDialog);
2524     qFileDialogUi->backButton->setIcon(q->style()->standardIcon(QStyle::SP_ArrowBack, 0, q));
2525     qFileDialogUi->backButton->setAutoRaise(true);
2526     qFileDialogUi->backButton->setEnabled(false);
2527     QObject::connect(qFileDialogUi->backButton, SIGNAL(clicked()), q, SLOT(_q_navigateBackward()));
2528 
2529     qFileDialogUi->forwardButton->setIcon(q->style()->standardIcon(QStyle::SP_ArrowForward, 0, q));
2530     qFileDialogUi->forwardButton->setAutoRaise(true);
2531     qFileDialogUi->forwardButton->setEnabled(false);
2532     QObject::connect(qFileDialogUi->forwardButton, SIGNAL(clicked()), q, SLOT(_q_navigateForward()));
2533 
2534     qFileDialogUi->toParentButton->setIcon(q->style()->standardIcon(QStyle::SP_FileDialogToParent, 0, q));
2535     qFileDialogUi->toParentButton->setAutoRaise(true);
2536     qFileDialogUi->toParentButton->setEnabled(false);
2537     QObject::connect(qFileDialogUi->toParentButton, SIGNAL(clicked()), q, SLOT(_q_navigateToParent()));
2538 
2539     qFileDialogUi->listModeButton->setIcon(q->style()->standardIcon(QStyle::SP_FileDialogListView, 0, q));
2540     qFileDialogUi->listModeButton->setAutoRaise(true);
2541     qFileDialogUi->listModeButton->setDown(true);
2542     QObject::connect(qFileDialogUi->listModeButton, SIGNAL(clicked()), q, SLOT(_q_showListView()));
2543 
2544     qFileDialogUi->detailModeButton->setIcon(q->style()->standardIcon(QStyle::SP_FileDialogDetailedView, 0, q));
2545     qFileDialogUi->detailModeButton->setAutoRaise(true);
2546     QObject::connect(qFileDialogUi->detailModeButton, SIGNAL(clicked()), q, SLOT(_q_showDetailsView()));
2547 
2548     QSize toolSize(qFileDialogUi->fileNameEdit->sizeHint().height(), qFileDialogUi->fileNameEdit->sizeHint().height());
2549     qFileDialogUi->backButton->setFixedSize(toolSize);
2550     qFileDialogUi->listModeButton->setFixedSize(toolSize);
2551     qFileDialogUi->detailModeButton->setFixedSize(toolSize);
2552     qFileDialogUi->forwardButton->setFixedSize(toolSize);
2553     qFileDialogUi->toParentButton->setFixedSize(toolSize);
2554 
2555     qFileDialogUi->newFolderButton->setIcon(q->style()->standardIcon(QStyle::SP_FileDialogNewFolder, 0, q));
2556     qFileDialogUi->newFolderButton->setFixedSize(toolSize);
2557     qFileDialogUi->newFolderButton->setAutoRaise(true);
2558     qFileDialogUi->newFolderButton->setEnabled(false);
2559     QObject::connect(qFileDialogUi->newFolderButton, SIGNAL(clicked()), q, SLOT(_q_createDirectory()));
2560 }
2561 
2562 /*!
2563     \internal
2564 
2565     Create actions which will be used in the right click.
2566 */
createMenuActions()2567 void QFileDialogPrivate::createMenuActions()
2568 {
2569     Q_Q(QFileDialog);
2570 
2571     QAction *goHomeAction =  new QAction(q);
2572 #ifndef QT_NO_SHORTCUT
2573     goHomeAction->setShortcut(Qt::CTRL + Qt::Key_H + Qt::SHIFT);
2574 #endif
2575     QObject::connect(goHomeAction, SIGNAL(triggered()), q, SLOT(_q_goHome()));
2576     q->addAction(goHomeAction);
2577 
2578     // ### TODO add Desktop & Computer actions
2579 
2580     QAction *goToParent =  new QAction(q);
2581     goToParent->setObjectName(QLatin1String("qt_goto_parent_action"));
2582 #ifndef QT_NO_SHORTCUT
2583     goToParent->setShortcut(Qt::CTRL + Qt::UpArrow);
2584 #endif
2585     QObject::connect(goToParent, SIGNAL(triggered()), q, SLOT(_q_navigateToParent()));
2586     q->addAction(goToParent);
2587 
2588     renameAction = new QAction(q);
2589     renameAction->setEnabled(false);
2590     renameAction->setObjectName(QLatin1String("qt_rename_action"));
2591     QObject::connect(renameAction, SIGNAL(triggered()), q, SLOT(_q_renameCurrent()));
2592 
2593     deleteAction = new QAction(q);
2594     deleteAction->setEnabled(false);
2595     deleteAction->setObjectName(QLatin1String("qt_delete_action"));
2596     QObject::connect(deleteAction, SIGNAL(triggered()), q, SLOT(_q_deleteCurrent()));
2597 
2598     showHiddenAction = new QAction(q);
2599     showHiddenAction->setObjectName(QLatin1String("qt_show_hidden_action"));
2600     showHiddenAction->setCheckable(true);
2601     QObject::connect(showHiddenAction, SIGNAL(triggered()), q, SLOT(_q_showHidden()));
2602 
2603     newFolderAction = new QAction(q);
2604     newFolderAction->setObjectName(QLatin1String("qt_new_folder_action"));
2605     QObject::connect(newFolderAction, SIGNAL(triggered()), q, SLOT(_q_createDirectory()));
2606 }
2607 
_q_goHome()2608 void QFileDialogPrivate::_q_goHome()
2609 {
2610     Q_Q(QFileDialog);
2611     q->setDirectory(QDir::homePath());
2612 }
2613 
2614 /*!
2615     \internal
2616 
2617     Update history with new path, buttons, and combo
2618 */
_q_pathChanged(const QString & newPath)2619 void QFileDialogPrivate::_q_pathChanged(const QString &newPath)
2620 {
2621     Q_Q(QFileDialog);
2622     QDir dir(model->rootDirectory());
2623     qFileDialogUi->toParentButton->setEnabled(dir.exists());
2624     qFileDialogUi->sidebar->selectUrl(QUrl::fromLocalFile(newPath));
2625     q->setHistory(qFileDialogUi->lookInCombo->history());
2626 
2627     if (currentHistoryLocation < 0 || currentHistory.value(currentHistoryLocation) != QDir::toNativeSeparators(newPath)) {
2628         while (currentHistoryLocation >= 0 && currentHistoryLocation + 1 < currentHistory.count()) {
2629             currentHistory.removeLast();
2630         }
2631         currentHistory.append(QDir::toNativeSeparators(newPath));
2632         ++currentHistoryLocation;
2633     }
2634     qFileDialogUi->forwardButton->setEnabled(currentHistory.size() - currentHistoryLocation > 1);
2635     qFileDialogUi->backButton->setEnabled(currentHistoryLocation > 0);
2636 }
2637 
2638 /*!
2639     \internal
2640 
2641     Navigates to the last directory viewed in the dialog.
2642 */
_q_navigateBackward()2643 void QFileDialogPrivate::_q_navigateBackward()
2644 {
2645     Q_Q(QFileDialog);
2646     if (!currentHistory.isEmpty() && currentHistoryLocation > 0) {
2647         --currentHistoryLocation;
2648         QString previousHistory = currentHistory.at(currentHistoryLocation);
2649         q->setDirectory(previousHistory);
2650     }
2651 }
2652 
2653 /*!
2654     \internal
2655 
2656     Navigates to the last directory viewed in the dialog.
2657 */
_q_navigateForward()2658 void QFileDialogPrivate::_q_navigateForward()
2659 {
2660     Q_Q(QFileDialog);
2661     if (!currentHistory.isEmpty() && currentHistoryLocation < currentHistory.size() - 1) {
2662         ++currentHistoryLocation;
2663         QString nextHistory = currentHistory.at(currentHistoryLocation);
2664         q->setDirectory(nextHistory);
2665     }
2666 }
2667 
2668 /*!
2669     \internal
2670 
2671     Navigates to the parent directory of the currently displayed directory
2672     in the dialog.
2673 */
_q_navigateToParent()2674 void QFileDialogPrivate::_q_navigateToParent()
2675 {
2676     Q_Q(QFileDialog);
2677     QDir dir(model->rootDirectory());
2678     QString newDirectory;
2679     if (dir.isRoot()) {
2680         newDirectory = model->myComputer().toString();
2681     } else {
2682         dir.cdUp();
2683         newDirectory = dir.absolutePath();
2684     }
2685     q->setDirectory(newDirectory);
2686     emit q->directoryEntered(newDirectory);
2687 }
2688 
2689 /*!
2690     \internal
2691 
2692     Creates a new directory, first asking the user for a suitable name.
2693 */
_q_createDirectory()2694 void QFileDialogPrivate::_q_createDirectory()
2695 {
2696     Q_Q(QFileDialog);
2697     qFileDialogUi->listView->clearSelection();
2698 
2699     QString newFolderString = QFileDialog::tr("New Folder");
2700     QString folderName = newFolderString;
2701     QString prefix = q->directory().absolutePath() + QDir::separator();
2702     if (QFile::exists(prefix + folderName)) {
2703         qlonglong suffix = 2;
2704         while (QFile::exists(prefix + folderName)) {
2705             folderName = newFolderString + QString::number(suffix++);
2706         }
2707     }
2708 
2709     QModelIndex parent = rootIndex();
2710     QModelIndex index = model->mkdir(parent, folderName);
2711     if (!index.isValid())
2712         return;
2713 
2714     index = select(index);
2715     if (index.isValid()) {
2716         qFileDialogUi->treeView->setCurrentIndex(index);
2717         currentView()->edit(index);
2718     }
2719 }
2720 
_q_showListView()2721 void QFileDialogPrivate::_q_showListView()
2722 {
2723     qFileDialogUi->listModeButton->setDown(true);
2724     qFileDialogUi->detailModeButton->setDown(false);
2725     qFileDialogUi->treeView->hide();
2726     qFileDialogUi->listView->show();
2727     qFileDialogUi->stackedWidget->setCurrentWidget(qFileDialogUi->listView->parentWidget());
2728     qFileDialogUi->listView->doItemsLayout();
2729 }
2730 
_q_showDetailsView()2731 void QFileDialogPrivate::_q_showDetailsView()
2732 {
2733     qFileDialogUi->listModeButton->setDown(false);
2734     qFileDialogUi->detailModeButton->setDown(true);
2735     qFileDialogUi->listView->hide();
2736     qFileDialogUi->treeView->show();
2737     qFileDialogUi->stackedWidget->setCurrentWidget(qFileDialogUi->treeView->parentWidget());
2738     qFileDialogUi->treeView->doItemsLayout();
2739 }
2740 
2741 /*!
2742     \internal
2743 
2744     Show the context menu for the file/dir under position
2745 */
_q_showContextMenu(const QPoint & position)2746 void QFileDialogPrivate::_q_showContextMenu(const QPoint &position)
2747 {
2748 #ifdef QT_NO_MENU
2749     Q_UNUSED(position);
2750 #else
2751     Q_Q(QFileDialog);
2752     QAbstractItemView *view = 0;
2753     if (q->viewMode() == QFileDialog::Detail)
2754         view = qFileDialogUi->treeView;
2755     else
2756         view = qFileDialogUi->listView;
2757     QModelIndex index = view->indexAt(position);
2758     index = mapToSource(index.sibling(index.row(), 0));
2759 
2760     QMenu menu(view);
2761     if (index.isValid()) {
2762         // file context menu
2763         QFile::Permissions p(index.parent().data(QFileSystemModel::FilePermissions).toInt());
2764         renameAction->setEnabled(p & QFile::WriteUser);
2765         menu.addAction(renameAction);
2766         deleteAction->setEnabled(p & QFile::WriteUser);
2767         menu.addAction(deleteAction);
2768         menu.addSeparator();
2769     }
2770     menu.addAction(showHiddenAction);
2771     if (qFileDialogUi->newFolderButton->isVisible()) {
2772         newFolderAction->setEnabled(qFileDialogUi->newFolderButton->isEnabled());
2773         menu.addAction(newFolderAction);
2774     }
2775     menu.exec(view->viewport()->mapToGlobal(position));
2776 #endif // QT_NO_MENU
2777 }
2778 
2779 /*!
2780     \internal
2781 */
_q_renameCurrent()2782 void QFileDialogPrivate::_q_renameCurrent()
2783 {
2784     Q_Q(QFileDialog);
2785     QModelIndex index = qFileDialogUi->listView->currentIndex();
2786     index = index.sibling(index.row(), 0);
2787     if (q->viewMode() == QFileDialog::List)
2788         qFileDialogUi->listView->edit(index);
2789     else
2790         qFileDialogUi->treeView->edit(index);
2791 }
2792 
removeDirectory(const QString & path)2793 bool QFileDialogPrivate::removeDirectory(const QString &path)
2794 {
2795     QModelIndex modelIndex = model->index(path);
2796     return model->remove(modelIndex);
2797 }
2798 
2799 /*!
2800     \internal
2801 
2802     Deletes the currently selected item in the dialog.
2803 */
_q_deleteCurrent()2804 void QFileDialogPrivate::_q_deleteCurrent()
2805 {
2806     if (model->isReadOnly())
2807         return;
2808 
2809     QModelIndexList list = qFileDialogUi->listView->selectionModel()->selectedRows();
2810     for (int i = list.count() - 1; i >= 0; --i) {
2811         QModelIndex index = list.at(i);
2812         if (index == qFileDialogUi->listView->rootIndex())
2813             continue;
2814 
2815         index = mapToSource(index.sibling(index.row(), 0));
2816         if (!index.isValid())
2817             continue;
2818 
2819     QString fileName = index.data(QFileSystemModel::FileNameRole).toString();
2820     QString filePath = index.data(QFileSystemModel::FilePathRole).toString();
2821     bool isDir = model->isDir(index);
2822 
2823     QFile::Permissions p(index.parent().data(QFileSystemModel::FilePermissions).toInt());
2824 #ifndef QT_NO_MESSAGEBOX
2825     Q_Q(QFileDialog);
2826     if (!(p & QFile::WriteUser) && (QMessageBox::warning(q_func(), q_func()->windowTitle(),
2827                                 QFileDialog::tr("'%1' is write protected.\nDo you want to delete it anyway?")
2828                                 .arg(fileName),
2829                                  QMessageBox::Yes | QMessageBox::No, QMessageBox::No) == QMessageBox::No))
2830         return;
2831     else if (QMessageBox::warning(q_func(), q_func()->windowTitle(),
2832                                   QFileDialog::tr("Are sure you want to delete '%1'?")
2833                                   .arg(fileName),
2834                                   QMessageBox::Yes | QMessageBox::No, QMessageBox::No) == QMessageBox::No)
2835         return;
2836 
2837 #else
2838     if (!(p & QFile::WriteUser))
2839         return;
2840 #endif // QT_NO_MESSAGEBOX
2841 
2842         // the event loop has run, we can NOT reuse index because the model might have removed it.
2843         if (isDir) {
2844             if (!removeDirectory(filePath)) {
2845 #ifndef QT_NO_MESSAGEBOX
2846             QMessageBox::warning(q, q->windowTitle(),
2847                                 QFileDialog::tr("Could not delete directory."));
2848 #endif
2849             }
2850         } else {
2851             model->remove(index);
2852         }
2853     }
2854 }
2855 
_q_autoCompleteFileName(const QString & text)2856 void QFileDialogPrivate::_q_autoCompleteFileName(const QString &text)
2857 {
2858     if (text.startsWith(QLatin1String("//")) || text.startsWith(QLatin1Char('\\'))) {
2859         qFileDialogUi->listView->selectionModel()->clearSelection();
2860         return;
2861     }
2862 
2863     QStringList multipleFiles = typedFiles();
2864     if (multipleFiles.count() > 0) {
2865         QModelIndexList oldFiles = qFileDialogUi->listView->selectionModel()->selectedRows();
2866         QModelIndexList newFiles;
2867         for (int i = 0; i < multipleFiles.count(); ++i) {
2868             QModelIndex idx = model->index(multipleFiles.at(i));
2869             if (oldFiles.contains(idx))
2870                 oldFiles.removeAll(idx);
2871             else
2872                 newFiles.append(idx);
2873         }
2874         for (int i = 0; i < newFiles.count(); ++i)
2875             select(newFiles.at(i));
2876         if (lineEdit()->hasFocus())
2877             for (int i = 0; i < oldFiles.count(); ++i)
2878                 qFileDialogUi->listView->selectionModel()->select(oldFiles.at(i),
2879                     QItemSelectionModel::Toggle | QItemSelectionModel::Rows);
2880     }
2881 }
2882 
2883 /*!
2884     \internal
2885 */
_q_updateOkButton()2886 void QFileDialogPrivate::_q_updateOkButton()
2887 {
2888     Q_Q(QFileDialog);
2889     QPushButton *button =  qFileDialogUi->buttonBox->button((acceptMode == QFileDialog::AcceptOpen)
2890                     ? QDialogButtonBox::Open : QDialogButtonBox::Save);
2891     if (!button)
2892         return;
2893 
2894     bool enableButton = true;
2895     bool isOpenDirectory = false;
2896 
2897     QStringList files = q->selectedFiles();
2898     QString lineEditText = lineEdit()->text();
2899 
2900     if (lineEditText.startsWith(QLatin1String("//")) || lineEditText.startsWith(QLatin1Char('\\'))) {
2901         button->setEnabled(true);
2902         if (acceptMode == QFileDialog::AcceptSave)
2903             button->setText(acceptLabel);
2904         return;
2905     }
2906 
2907     if (files.isEmpty()) {
2908         enableButton = false;
2909     } else if (lineEditText == QLatin1String("..")) {
2910         isOpenDirectory = true;
2911     } else {
2912         switch (fileMode) {
2913         case QFileDialog::DirectoryOnly:
2914         case QFileDialog::Directory: {
2915             QString fn = files.first();
2916             QModelIndex idx = model->index(fn);
2917             if (!idx.isValid())
2918                 idx = model->index(getEnvironmentVariable(fn));
2919             if (!idx.isValid() || !model->isDir(idx))
2920                 enableButton = false;
2921             break;
2922         }
2923         case QFileDialog::AnyFile: {
2924             QString fn = files.first();
2925             QFileInfo info(fn);
2926             QModelIndex idx = model->index(fn);
2927             QString fileDir;
2928             QString fileName;
2929             if (info.isDir()) {
2930                 fileDir = info.canonicalFilePath();
2931             } else {
2932                 fileDir = fn.mid(0, fn.lastIndexOf(QLatin1Char('/')));
2933                 fileName = fn.mid(fileDir.length() + 1);
2934             }
2935             if (lineEditText.contains(QLatin1String(".."))) {
2936                 fileDir = info.canonicalFilePath();
2937                 fileName = info.fileName();
2938             }
2939 
2940             if (fileDir == q->directory().canonicalPath() && fileName.isEmpty()) {
2941                 enableButton = false;
2942                 break;
2943             }
2944             if (idx.isValid() && model->isDir(idx)) {
2945                 isOpenDirectory = true;
2946                 enableButton = true;
2947                 break;
2948             }
2949             if (!idx.isValid()) {
2950                 int maxLength = maxNameLength(fileDir);
2951                 enableButton = maxLength < 0 || fileName.length() <= maxLength;
2952             }
2953             break;
2954         }
2955         case QFileDialog::ExistingFile:
2956         case QFileDialog::ExistingFiles:
2957             for (int i = 0; i < files.count(); ++i) {
2958                 QModelIndex idx = model->index(files.at(i));
2959                 if (!idx.isValid())
2960                     idx = model->index(getEnvironmentVariable(files.at(i)));
2961                 if (!idx.isValid()) {
2962                     enableButton = false;
2963                     break;
2964                 }
2965                 if (idx.isValid() && model->isDir(idx)) {
2966                     isOpenDirectory = true;
2967                     break;
2968                 }
2969             }
2970             break;
2971         default:
2972             break;
2973         }
2974     }
2975 
2976     button->setEnabled(enableButton);
2977     if (acceptMode == QFileDialog::AcceptSave)
2978         button->setText(isOpenDirectory ? QFileDialog::tr("&Open") : acceptLabel);
2979 }
2980 
2981 /*!
2982     \internal
2983 */
_q_currentChanged(const QModelIndex & index)2984 void QFileDialogPrivate::_q_currentChanged(const QModelIndex &index)
2985 {
2986     _q_updateOkButton();
2987     emit q_func()->currentChanged(index.data(QFileSystemModel::FilePathRole).toString());
2988 }
2989 
2990 /*!
2991     \internal
2992 
2993     This is called when the user double clicks on a file with the corresponding
2994     model item \a index.
2995 */
_q_enterDirectory(const QModelIndex & index)2996 void QFileDialogPrivate::_q_enterDirectory(const QModelIndex &index)
2997 {
2998     Q_Q(QFileDialog);
2999     // My Computer or a directory
3000     QModelIndex sourceIndex = index.model() == proxyModel ? mapToSource(index) : index;
3001     QString path = sourceIndex.data(QFileSystemModel::FilePathRole).toString();
3002     if (path.isEmpty() || model->isDir(sourceIndex)) {
3003         q->setDirectory(path);
3004         emit q->directoryEntered(path);
3005         if (fileMode == QFileDialog::Directory
3006                 || fileMode == QFileDialog::DirectoryOnly) {
3007             // ### find out why you have to do both of these.
3008             lineEdit()->setText(QString());
3009             lineEdit()->clear();
3010         }
3011     } else {
3012         // Do not accept when shift-clicking to multi-select a file in environments with single-click-activation (KDE)
3013         if (!q->style()->styleHint(QStyle::SH_ItemView_ActivateItemOnSingleClick)
3014             || q->fileMode() != QFileDialog::ExistingFiles || !(QApplication::keyboardModifiers() & Qt::CTRL)) {
3015             q->accept();
3016         }
3017     }
3018 }
3019 
3020 /*!
3021     \internal
3022 
3023     Changes the file dialog's current directory to the one specified
3024     by \a path.
3025 */
_q_goToDirectory(const QString & path)3026 void QFileDialogPrivate::_q_goToDirectory(const QString &path)
3027 {
3028  #ifndef QT_NO_MESSAGEBOX
3029     Q_Q(QFileDialog);
3030 #endif
3031     QModelIndex index = qFileDialogUi->lookInCombo->model()->index(qFileDialogUi->lookInCombo->currentIndex(),
3032                                                     qFileDialogUi->lookInCombo->modelColumn(),
3033                                                     qFileDialogUi->lookInCombo->rootModelIndex());
3034     QString path2 = path;
3035     if (!index.isValid())
3036         index = mapFromSource(model->index(getEnvironmentVariable(path)));
3037     else {
3038         path2 = index.data(UrlRole).toUrl().toLocalFile();
3039         index = mapFromSource(model->index(path2));
3040     }
3041     QDir dir(path2);
3042     if (!dir.exists())
3043         dir = getEnvironmentVariable(path2);
3044 
3045     if (dir.exists() || path2.isEmpty() || path2 == model->myComputer().toString()) {
3046         _q_enterDirectory(index);
3047 #ifndef QT_NO_MESSAGEBOX
3048     } else {
3049         QString message = QFileDialog::tr("%1\nDirectory not found.\nPlease verify the "
3050                                           "correct directory name was given.");
3051         QMessageBox::warning(q, q->windowTitle(), message.arg(path2));
3052 #endif // QT_NO_MESSAGEBOX
3053     }
3054 }
3055 
3056 // Makes a list of filters from a normal filter string "Image Files (*.png *.jpg)"
qt_clean_filter_list(const QString & filter)3057 QStringList qt_clean_filter_list(const QString &filter)
3058 {
3059     QRegExp regexp(QString::fromLatin1(qt_file_dialog_filter_reg_exp));
3060     QString f = filter;
3061     int i = regexp.indexIn(f);
3062     if (i >= 0)
3063         f = regexp.cap(2);
3064     return f.split(QLatin1Char(' '), QString::SkipEmptyParts);
3065 }
3066 
3067 /*!
3068     \internal
3069 
3070     Sets the current name filter to be nameFilter and
3071     update the qFileDialogUi->fileNameEdit when in AcceptSave mode with the new extension.
3072 */
_q_useNameFilter(int index)3073 void QFileDialogPrivate::_q_useNameFilter(int index)
3074 {
3075     if (index == nameFilters.size()) {
3076         QAbstractItemModel *comboModel = qFileDialogUi->fileTypeCombo->model();
3077         nameFilters.append(comboModel->index(comboModel->rowCount() - 1, 0).data().toString());
3078     }
3079 
3080     QString nameFilter = nameFilters.at(index);
3081     QStringList newNameFilters = qt_clean_filter_list(nameFilter);
3082     if (acceptMode == QFileDialog::AcceptSave) {
3083         QString newNameFilterExtension;
3084         if (newNameFilters.count() > 0)
3085             newNameFilterExtension = QFileInfo(newNameFilters.at(0)).suffix();
3086 
3087         QString fileName = lineEdit()->text();
3088         const QString fileNameExtension = QFileInfo(fileName).suffix();
3089         if (!fileNameExtension.isEmpty() && !newNameFilterExtension.isEmpty()) {
3090             const int fileNameExtensionLength = fileNameExtension.count();
3091             fileName.replace(fileName.count() - fileNameExtensionLength,
3092                              fileNameExtensionLength, newNameFilterExtension);
3093             qFileDialogUi->listView->clearSelection();
3094             lineEdit()->setText(fileName);
3095         }
3096     }
3097 
3098     model->setNameFilters(newNameFilters);
3099 }
3100 
3101 /*!
3102     \internal
3103 
3104     This is called when the model index corresponding to the current file is changed
3105     from \a index to \a current.
3106 */
_q_selectionChanged()3107 void QFileDialogPrivate::_q_selectionChanged()
3108 {
3109     QModelIndexList indexes = qFileDialogUi->listView->selectionModel()->selectedRows();
3110     bool stripDirs = (fileMode != QFileDialog::DirectoryOnly && fileMode != QFileDialog::Directory);
3111 
3112     QStringList allFiles;
3113     for (int i = 0; i < indexes.count(); ++i) {
3114         if (stripDirs && model->isDir(mapToSource(indexes.at(i))))
3115             continue;
3116         allFiles.append(indexes.at(i).data().toString());
3117     }
3118     if (allFiles.count() > 1)
3119         for (int i = 0; i < allFiles.count(); ++i) {
3120             allFiles.replace(i, QString(QLatin1Char('"') + allFiles.at(i) + QLatin1Char('"')));
3121     }
3122 
3123     QString finalFiles = allFiles.join(QLatin1String(" "));
3124     if (!finalFiles.isEmpty() && !lineEdit()->hasFocus() && lineEdit()->isVisible())
3125         lineEdit()->setText(finalFiles);
3126     else
3127         _q_updateOkButton();
3128 }
3129 
3130 /*!
3131     \internal
3132 
3133     Includes hidden files and directories in the items displayed in the dialog.
3134 */
_q_showHidden()3135 void QFileDialogPrivate::_q_showHidden()
3136 {
3137     Q_Q(QFileDialog);
3138     QDir::Filters dirFilters = q->filter();
3139     if (showHiddenAction->isChecked())
3140         dirFilters |= QDir::Hidden;
3141     else
3142         dirFilters &= ~QDir::Hidden;
3143     q->setFilter(dirFilters);
3144 }
3145 
3146 /*!
3147     \internal
3148 
3149     When parent is root and rows have been inserted when none was there before
3150     then select the first one.
3151 */
_q_rowsInserted(const QModelIndex & parent)3152 void QFileDialogPrivate::_q_rowsInserted(const QModelIndex &parent)
3153 {
3154     if (!qFileDialogUi->treeView
3155         || parent != qFileDialogUi->treeView->rootIndex()
3156         || !qFileDialogUi->treeView->selectionModel()
3157         || qFileDialogUi->treeView->selectionModel()->hasSelection()
3158         || qFileDialogUi->treeView->model()->rowCount(parent) == 0)
3159         return;
3160 }
3161 
_q_fileRenamed(const QString & path,const QString oldName,const QString newName)3162 void QFileDialogPrivate::_q_fileRenamed(const QString &path, const QString oldName, const QString newName)
3163 {
3164     if (fileMode == QFileDialog::Directory || fileMode == QFileDialog::DirectoryOnly) {
3165         if (path == rootPath() && lineEdit()->text() == oldName)
3166             lineEdit()->setText(newName);
3167     }
3168 }
3169 
3170 /*!
3171     \internal
3172 
3173     For the list and tree view watch keys to goto parent and back in the history
3174 
3175     returns true if handled
3176 */
itemViewKeyboardEvent(QKeyEvent * event)3177 bool QFileDialogPrivate::itemViewKeyboardEvent(QKeyEvent *event) {
3178 
3179     Q_Q(QFileDialog);
3180     switch (event->key()) {
3181     case Qt::Key_Backspace:
3182         _q_navigateToParent();
3183         return true;
3184     case Qt::Key_Back:
3185 #ifdef QT_KEYPAD_NAVIGATION
3186         if (QApplication::keypadNavigationEnabled())
3187             return false;
3188 #endif
3189     case Qt::Key_Left:
3190         if (event->key() == Qt::Key_Back || event->modifiers() == Qt::AltModifier) {
3191             _q_navigateBackward();
3192             return true;
3193         }
3194         break;
3195     case Qt::Key_Escape:
3196         q->hide();
3197         return true;
3198     default:
3199         break;
3200     }
3201     return false;
3202 }
3203 
getEnvironmentVariable(const QString & string)3204 QString QFileDialogPrivate::getEnvironmentVariable(const QString &string)
3205 {
3206 #ifdef Q_OS_UNIX
3207     if (string.size() > 1 && string.startsWith(QLatin1Char('$'))) {
3208         return QString::fromLocal8Bit(getenv(string.mid(1).toLatin1().constData()));
3209     }
3210 #else
3211     if (string.size() > 2 && string.startsWith(QLatin1Char('%')) && string.endsWith(QLatin1Char('%'))) {
3212         return QString::fromLocal8Bit(qgetenv(string.mid(1, string.size() - 2).toLatin1().constData()));
3213     }
3214 #endif
3215     return string;
3216 }
3217 
init(QFileDialogPrivate * d_pointer)3218 void QFileDialogComboBox::init(QFileDialogPrivate *d_pointer) {
3219     d_ptr = d_pointer;
3220     urlModel = new QUrlModel(this);
3221     urlModel->showFullPath = true;
3222     urlModel->setFileSystemModel(d_ptr->model);
3223     setModel(urlModel);
3224 }
3225 
showPopup()3226 void QFileDialogComboBox::showPopup()
3227 {
3228     if (model()->rowCount() > 1)
3229         QComboBox::showPopup();
3230 
3231     urlModel->setUrls(QList<QUrl>());
3232     QList<QUrl> list;
3233     QModelIndex idx = d_ptr->model->index(d_ptr->rootPath());
3234     while (idx.isValid()) {
3235         QUrl url = QUrl::fromLocalFile(idx.data(QFileSystemModel::FilePathRole).toString());
3236         if (url.isValid())
3237             list.append(url);
3238         idx = idx.parent();
3239     }
3240     // add "my computer"
3241     list.append(QUrl::fromLocalFile(QLatin1String("")));
3242     urlModel->addUrls(list, 0);
3243     idx = model()->index(model()->rowCount() - 1, 0);
3244 
3245     // append history
3246     QList<QUrl> urls;
3247     for (int i = 0; i < m_history.count(); ++i) {
3248         QUrl path = QUrl::fromLocalFile(m_history.at(i));
3249         if (!urls.contains(path))
3250             urls.prepend(path);
3251     }
3252     if (urls.count() > 0) {
3253         model()->insertRow(model()->rowCount());
3254         idx = model()->index(model()->rowCount()-1, 0);
3255         // ### TODO maybe add a horizontal line before this
3256         model()->setData(idx, QFileDialog::tr("Recent Places"));
3257         QStandardItemModel *m = qobject_cast<QStandardItemModel*>(model());
3258         if (m) {
3259             Qt::ItemFlags flags = m->flags(idx);
3260             flags &= ~Qt::ItemIsEnabled;
3261             m->item(idx.row(), idx.column())->setFlags(flags);
3262         }
3263         urlModel->addUrls(urls, -1, false);
3264     }
3265     setCurrentIndex(0);
3266 
3267     QComboBox::showPopup();
3268 }
3269 
3270 // Exact same as QComboBox::paintEvent(), except we elide the text.
paintEvent(QPaintEvent *)3271 void QFileDialogComboBox::paintEvent(QPaintEvent *)
3272 {
3273     QStylePainter painter(this);
3274     painter.setPen(palette().color(QPalette::Text));
3275 
3276     // draw the combobox frame, focusrect and selected etc.
3277     QStyleOptionComboBox opt;
3278     initStyleOption(&opt);
3279 
3280     QRect editRect = style()->subControlRect(QStyle::CC_ComboBox, &opt,
3281                                                 QStyle::SC_ComboBoxEditField, this);
3282     int size = editRect.width() - opt.iconSize.width() - 4;
3283     opt.currentText = opt.fontMetrics.elidedText(opt.currentText, Qt::ElideMiddle, size);
3284     painter.drawComplexControl(QStyle::CC_ComboBox, opt);
3285 
3286     // draw the icon and text
3287     painter.drawControl(QStyle::CE_ComboBoxLabel, opt);
3288 }
3289 
QFileDialogListView(QWidget * parent)3290 QFileDialogListView::QFileDialogListView(QWidget *parent) : QListView(parent)
3291 {
3292 }
3293 
init(QFileDialogPrivate * d_pointer)3294 void QFileDialogListView::init(QFileDialogPrivate *d_pointer)
3295 {
3296     d_ptr = d_pointer;
3297     setSelectionBehavior(QAbstractItemView::SelectRows);
3298     setWrapping(true);
3299     setResizeMode(QListView::Adjust);
3300     setEditTriggers(QAbstractItemView::EditKeyPressed);
3301     setContextMenuPolicy(Qt::CustomContextMenu);
3302 #ifndef QT_NO_DRAGANDDROP
3303     setDragDropMode(QAbstractItemView::InternalMove);
3304 #endif
3305 }
3306 
sizeHint() const3307 QSize QFileDialogListView::sizeHint() const
3308 {
3309     int height = qMax(10, sizeHintForRow(0));
3310     return QSize(QListView::sizeHint().width() * 2, height * 30);
3311 }
3312 
keyPressEvent(QKeyEvent * e)3313 void QFileDialogListView::keyPressEvent(QKeyEvent *e)
3314 {
3315 #ifdef QT_KEYPAD_NAVIGATION
3316     if (QApplication::navigationMode() == Qt::NavigationModeKeypadDirectional) {
3317         QListView::keyPressEvent(e);
3318         return;
3319     }
3320 #endif // QT_KEYPAD_NAVIGATION
3321 
3322     if (!d_ptr->itemViewKeyboardEvent(e))
3323         QListView::keyPressEvent(e);
3324     e->accept();
3325 }
3326 
QFileDialogTreeView(QWidget * parent)3327 QFileDialogTreeView::QFileDialogTreeView(QWidget *parent) : QTreeView(parent)
3328 {
3329 }
3330 
init(QFileDialogPrivate * d_pointer)3331 void QFileDialogTreeView::init(QFileDialogPrivate *d_pointer)
3332 {
3333     d_ptr = d_pointer;
3334     setSelectionBehavior(QAbstractItemView::SelectRows);
3335     setRootIsDecorated(false);
3336     setItemsExpandable(false);
3337     setSortingEnabled(true);
3338     header()->setSortIndicator(0, Qt::AscendingOrder);
3339     header()->setStretchLastSection(false);
3340     setTextElideMode(Qt::ElideMiddle);
3341     setEditTriggers(QAbstractItemView::EditKeyPressed);
3342     setContextMenuPolicy(Qt::CustomContextMenu);
3343 #ifndef QT_NO_DRAGANDDROP
3344     setDragDropMode(QAbstractItemView::InternalMove);
3345 #endif
3346 }
3347 
keyPressEvent(QKeyEvent * e)3348 void QFileDialogTreeView::keyPressEvent(QKeyEvent *e)
3349 {
3350 #ifdef QT_KEYPAD_NAVIGATION
3351     if (QApplication::navigationMode() == Qt::NavigationModeKeypadDirectional) {
3352         QTreeView::keyPressEvent(e);
3353         return;
3354     }
3355 #endif // QT_KEYPAD_NAVIGATION
3356 
3357     if (!d_ptr->itemViewKeyboardEvent(e))
3358         QTreeView::keyPressEvent(e);
3359     e->accept();
3360 }
3361 
sizeHint() const3362 QSize QFileDialogTreeView::sizeHint() const
3363 {
3364     int height = qMax(10, sizeHintForRow(0));
3365     QSize sizeHint = header()->sizeHint();
3366     return QSize(sizeHint.width() * 4, height * 30);
3367 }
3368 
3369 /*!
3370     // FIXME: this is a hack to avoid propagating key press events
3371     // to the dialog and from there to the "Ok" button
3372 */
keyPressEvent(QKeyEvent * e)3373 void QFileDialogLineEdit::keyPressEvent(QKeyEvent *e)
3374 {
3375 #ifdef QT_KEYPAD_NAVIGATION
3376     if (QApplication::navigationMode() == Qt::NavigationModeKeypadDirectional) {
3377         QLineEdit::keyPressEvent(e);
3378         return;
3379     }
3380 #endif // QT_KEYPAD_NAVIGATION
3381 
3382     int key = e->key();
3383     QLineEdit::keyPressEvent(e);
3384     if (key != Qt::Key_Escape)
3385         e->accept();
3386     if (hideOnEsc && (key == Qt::Key_Escape || key == Qt::Key_Return || key == Qt::Key_Enter)) {
3387         e->accept();
3388         hide();
3389         d_ptr->currentView()->setFocus(Qt::ShortcutFocusReason);
3390     }
3391 }
3392 
3393 #ifndef QT_NO_FSCOMPLETER
3394 
pathFromIndex(const QModelIndex & index) const3395 QString QFSCompleter::pathFromIndex(const QModelIndex &index) const
3396 {
3397     const QFileSystemModel *dirModel;
3398     if (proxyModel)
3399         dirModel = qobject_cast<const QFileSystemModel *>(proxyModel->sourceModel());
3400     else
3401         dirModel = sourceModel;
3402     QString currentLocation = dirModel->rootPath();
3403     QString path = index.data(QFileSystemModel::FilePathRole).toString();
3404     if (!currentLocation.isEmpty() && path.startsWith(currentLocation)) {
3405 #if defined(Q_OS_UNIX) || defined(Q_OS_WINCE)
3406         if (currentLocation == QDir::separator())
3407             return path.mid(currentLocation.length());
3408 #endif
3409         if (currentLocation.endsWith(QLatin1Char('/')))
3410             return path.mid(currentLocation.length());
3411         else
3412             return path.mid(currentLocation.length()+1);
3413     }
3414     return index.data(QFileSystemModel::FilePathRole).toString();
3415 }
3416 
splitPath(const QString & path) const3417 QStringList QFSCompleter::splitPath(const QString &path) const
3418 {
3419     if (path.isEmpty())
3420         return QStringList(completionPrefix());
3421 
3422     QString pathCopy = QDir::toNativeSeparators(path);
3423     QString sep = QDir::separator();
3424 #if defined(Q_OS_SYMBIAN)
3425     if (pathCopy == QLatin1String("\\"))
3426         return QStringList(pathCopy);
3427 #elif defined(Q_OS_WIN)
3428     if (pathCopy == QLatin1String("\\") || pathCopy == QLatin1String("\\\\"))
3429         return QStringList(pathCopy);
3430     QString doubleSlash(QLatin1String("\\\\"));
3431     if (pathCopy.startsWith(doubleSlash))
3432         pathCopy = pathCopy.mid(2);
3433     else
3434         doubleSlash.clear();
3435 #elif defined(Q_OS_UNIX)
3436     bool expanded;
3437     pathCopy = qt_tildeExpansion(pathCopy, &expanded);
3438     if (expanded) {
3439         QFileSystemModel *dirModel;
3440         if (proxyModel)
3441             dirModel = qobject_cast<QFileSystemModel *>(proxyModel->sourceModel());
3442         else
3443             dirModel = sourceModel;
3444         dirModel->fetchMore(dirModel->index(pathCopy));
3445     }
3446 #endif
3447 
3448     QRegExp re(QLatin1Char('[') + QRegExp::escape(sep) + QLatin1Char(']'));
3449 
3450 #if defined(Q_OS_SYMBIAN)
3451     QStringList parts = pathCopy.split(re, QString::SkipEmptyParts);
3452     if (pathCopy.endsWith(sep))
3453         parts.append(QString());
3454 #elif defined(Q_OS_WIN)
3455     QStringList parts = pathCopy.split(re, QString::SkipEmptyParts);
3456     if (!doubleSlash.isEmpty() && !parts.isEmpty())
3457         parts[0].prepend(doubleSlash);
3458     if (pathCopy.endsWith(sep))
3459         parts.append(QString());
3460 #else
3461     QStringList parts = pathCopy.split(re);
3462     if (pathCopy[0] == sep[0]) // read the "/" at the beginning as the split removed it
3463         parts[0] = sep[0];
3464 #endif
3465 
3466 #if defined(Q_OS_WIN) || defined(Q_OS_SYMBIAN)
3467     bool startsFromRoot = !parts.isEmpty() && parts[0].endsWith(QLatin1Char(':'));
3468 #else
3469     bool startsFromRoot = pathCopy[0] == sep[0];
3470 #endif
3471     if (parts.count() == 1 || (parts.count() > 1 && !startsFromRoot)) {
3472         const QFileSystemModel *dirModel;
3473         if (proxyModel)
3474             dirModel = qobject_cast<const QFileSystemModel *>(proxyModel->sourceModel());
3475         else
3476             dirModel = sourceModel;
3477         QString currentLocation = QDir::toNativeSeparators(dirModel->rootPath());
3478 #if defined(Q_OS_WIN) || defined(Q_OS_SYMBIAN)
3479         if (currentLocation.endsWith(QLatin1Char(':')))
3480             currentLocation.append(sep);
3481 #endif
3482         if (currentLocation.contains(sep) && path != currentLocation) {
3483             QStringList currentLocationList = splitPath(currentLocation);
3484             while (!currentLocationList.isEmpty()
3485                    && parts.count() > 0
3486                    && parts.at(0) == QLatin1String("..")) {
3487                 parts.removeFirst();
3488                 currentLocationList.removeLast();
3489             }
3490             if (!currentLocationList.isEmpty() && currentLocationList.last().isEmpty())
3491                 currentLocationList.removeLast();
3492             return currentLocationList + parts;
3493         }
3494     }
3495     return parts;
3496 }
3497 
3498 #endif // QT_NO_COMPLETER
3499 
3500 #ifdef QT3_SUPPORT
3501 /*!
3502     Use selectedFiles() instead.
3503 
3504     \oldcode
3505        QString selected = dialog->selectedFile();
3506     \newcode
3507         QStringList files = dialog->selectedFiles();
3508         QString selected;
3509         if (!files.isEmpty())
3510             selected = files[0];
3511     \endcode
3512 */
selectedFile() const3513 QString QFileDialog::selectedFile() const
3514 {
3515     QStringList files = selectedFiles();
3516     return files.size() ? files.at(0) : QString();
3517 }
3518 
3519 /*!
3520     \typedef QFileDialog::Mode
3521 
3522     Use QFileDialog::FileMode instead.
3523 */
3524 
3525 /*!
3526     \fn void QFileDialog::setMode(FileMode m)
3527 
3528     Use setFileMode() instead.
3529 */
3530 
3531 /*!
3532     \fn FileMode QFileDialog::mode() const
3533 
3534     Use fileMode() instead.
3535 */
3536 
3537 /*!
3538     \fn void QFileDialog::setDir(const QString &directory)
3539 
3540     Use setDirectory() instead.
3541 */
3542 
3543 /*!
3544     \fn void QFileDialog::setDir( const QDir &directory )
3545 
3546     Use setDirectory() instead.
3547 */
3548 
3549 /*!
3550     \fn QStringList QFileDialog::getOpenFileNames(const QString &filter,
3551         const QString &dir, QWidget *parent, const char* name,
3552         const QString &caption, QString *selectedFilter, bool resolveSymlinks)
3553 
3554     Use the getOpenFileNames() overload that takes \a parent as the first
3555     argument instead.
3556 */
3557 
3558 /*!
3559     \fn QString QFileDialog::getOpenFileName(const QString &dir,
3560         const QString &filter, QWidget *parent = 0, const char *name,
3561         const QString &caption, QString *selectedFilter, bool resolveSymlinks)
3562 
3563     Use the getOpenFileName() overload that takes \a parent as the first
3564     argument instead.
3565 */
3566 
3567 /*!
3568     \fn QString QFileDialog::getSaveFileName(const QString &dir,
3569         const QString &filter, QWidget *parent, const char *name,
3570         const QString &caption, QString *selectedFilter, bool resolveSymlinks)
3571 
3572     Use the getSaveFileName() overload that takes \a parent as the first
3573     argument instead.
3574 */
3575 
3576 /*!
3577     \fn QString QFileDialog::getExistingDirectory(const QString &dir,
3578         QWidget *parent, const char *name, const QString &caption,
3579         bool dirOnly, bool resolveSymlinks)
3580 
3581     Use the getExistingDirectory() overload that takes \a parent as
3582     the first argument instead.
3583 */
3584 
3585 #endif // QT3_SUPPORT
3586 
3587 QT_END_NAMESPACE
3588 
3589 #include "moc_qfiledialog.cpp"
3590 
3591 #endif // QT_NO_FILEDIALOG
3592