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