1/****************************************************************************
2**
3** Copyright (C) 2016 The Qt Company Ltd.
4** Contact: https://www.qt.io/licensing/
5**
6** This file is part of the QtWidgets module of the Qt Toolkit.
7**
8** $QT_BEGIN_LICENSE:LGPL$
9** Commercial License Usage
10** Licensees holding valid commercial Qt licenses may use this file in
11** accordance with the commercial license agreement provided with the
12** Software or, alternatively, in accordance with the terms contained in
13** a written agreement between you and The Qt Company. For licensing terms
14** and conditions see https://www.qt.io/terms-conditions. For further
15** information use the contact form at https://www.qt.io/contact-us.
16**
17** GNU Lesser General Public License Usage
18** Alternatively, this file may be used under the terms of the GNU Lesser
19** General Public License version 3 as published by the Free Software
20** Foundation and appearing in the file LICENSE.LGPL3 included in the
21** packaging of this file. Please review the following information to
22** ensure the GNU Lesser General Public License version 3 requirements
23** will be met: https://www.gnu.org/licenses/lgpl-3.0.html.
24**
25** GNU General Public License Usage
26** Alternatively, this file may be used under the terms of the GNU
27** General Public License version 2.0 or (at your option) the GNU General
28** Public license version 3 or any later version approved by the KDE Free
29** Qt Foundation. The licenses are as published by the Free Software
30** Foundation and appearing in the file LICENSE.GPL2 and LICENSE.GPL3
31** included in the packaging of this file. Please review the following
32** information to ensure the GNU General Public License requirements will
33** be met: https://www.gnu.org/licenses/gpl-2.0.html and
34** https://www.gnu.org/licenses/gpl-3.0.html.
35**
36** $QT_END_LICENSE$
37**
38****************************************************************************/
39
40#ifndef QFILEDIALOG_P_H
41#define QFILEDIALOG_P_H
42
43//
44// W A R N I N G
45// -------------
46//
47// This file is not part of the Qt API. It exists purely as an
48// implementation detail. This header file may change from version to
49// version without notice, or even be removed.
50//
51// We mean it.
52//
53
54#include <QtWidgets/private/qtwidgetsglobal_p.h>
55
56#include "qfiledialog.h"
57#include "private/qdialog_p.h"
58#include "qplatformdefs.h"
59
60#include <QtGui/private/qfilesystemmodel_p.h>
61#include <qlistview.h>
62#include <qtreeview.h>
63#include <qcombobox.h>
64#include <qtoolbutton.h>
65#include <qlabel.h>
66#include <qevent.h>
67#include <qlineedit.h>
68#include <qurl.h>
69#include <qstackedwidget.h>
70#include <qdialogbuttonbox.h>
71#include <qabstractproxymodel.h>
72#include <qfileiconprovider.h>
73#if QT_CONFIG(completer)
74#include <qcompleter.h>
75#endif
76#include <qpointer.h>
77#include <qdebug.h>
78#include "qsidebar_p.h"
79#if QT_CONFIG(fscompleter)
80#include "qfscompleter_p.h"
81#endif
82
83#if defined (Q_OS_UNIX)
84#include <unistd.h>
85#endif
86
87QT_REQUIRE_CONFIG(filedialog);
88
89QT_BEGIN_NAMESPACE
90
91class QFileDialogListView;
92class QFileDialogTreeView;
93class QFileDialogLineEdit;
94class QGridLayout;
95class QCompleter;
96class QHBoxLayout;
97class Ui_QFileDialog;
98class QPlatformDialogHelper;
99
100struct QFileDialogArgs
101{
102 QFileDialogArgs(const QUrl &url = {});
103
104 QWidget *parent = nullptr;
105 QString caption;
106 QUrl directory;
107 QString selection;
108 QString filter;
109 QFileDialog::FileMode mode = QFileDialog::AnyFile;
110 QFileDialog::Options options = {};
111};
112
113#define UrlRole (Qt::UserRole + 1)
114
115class Q_WIDGETS_EXPORT QFileDialogPrivate : public QDialogPrivate
116{
117 Q_DECLARE_PUBLIC(QFileDialog)
118
119public:
120 using PersistentModelIndexList = QList<QPersistentModelIndex>;
121
122 struct HistoryItem
123 {
124 QString path;
125 PersistentModelIndexList selection;
126 };
127
128 QFileDialogPrivate();
129
130 QPlatformFileDialogHelper *platformFileDialogHelper() const
131 { return static_cast<QPlatformFileDialogHelper *>(platformHelper()); }
132
133 void createToolButtons();
134 void createMenuActions();
135 void createWidgets();
136
137 void init(const QFileDialogArgs &args);
138 bool itemViewKeyboardEvent(QKeyEvent *event);
139 QString getEnvironmentVariable(const QString &string);
140 QStringList typedFiles() const;
141 QList<QUrl> userSelectedFiles() const;
142 QStringList addDefaultSuffixToFiles(const QStringList &filesToFix) const;
143 QList<QUrl> addDefaultSuffixToUrls(const QList<QUrl> &urlsToFix) const;
144 bool removeDirectory(const QString &path);
145 void setLabelTextControl(QFileDialog::DialogLabel label, const QString &text);
146 inline void updateLookInLabel();
147 inline void updateFileNameLabel();
148 inline void updateFileTypeLabel();
149 void updateOkButtonText(bool saveAsOnFolder = false);
150 void updateCancelButtonText();
151
152 inline QModelIndex mapToSource(const QModelIndex &index) const;
153 inline QModelIndex mapFromSource(const QModelIndex &index) const;
154 inline QModelIndex rootIndex() const;
155 inline void setRootIndex(const QModelIndex &index) const;
156 inline QModelIndex select(const QModelIndex &index) const;
157 inline QString rootPath() const;
158
159 QLineEdit *lineEdit() const;
160
161 static int maxNameLength(const QString &path);
162
163 QString basename(const QString &path) const
164 {
165 int separator = QDir::toNativeSeparators(path).lastIndexOf(QDir::separator());
166 if (separator != -1)
167 return path.mid(separator + 1);
168 return path;
169 }
170
171 QDir::Filters filterForMode(QDir::Filters filters) const
172 {
173 filters |= QDir::Drives | QDir::AllDirs | QDir::Dirs | QDir::Files;
174 if (q_func()->testOption(QFileDialog::ShowDirsOnly))
175 filters &= ~QDir::Files;
176 return filters;
177 }
178
179 QAbstractItemView *currentView() const;
180
181 static inline QString toInternal(const QString &path)
182 {
183#if defined(Q_OS_WIN)
184 QString n(path);
185 n.replace(QLatin1Char('\\'), QLatin1Char('/'));
186 return n;
187#else // the compile should optimize away this
188 return path;
189#endif
190 }
191
192#if QT_CONFIG(settings)
193 void saveSettings();
194 bool restoreFromSettings();
195#endif
196
197 bool restoreWidgetState(QStringList &history, int splitterPosition);
198 static void setLastVisitedDirectory(const QUrl &dir);
199 void retranslateWindowTitle();
200 void retranslateStrings();
201 void emitFilesSelected(const QStringList &files);
202 void saveHistorySelection();
203
204 void _q_goHome();
205 void _q_pathChanged(const QString &);
206 void navigate(HistoryItem &);
207 void _q_navigateBackward();
208 void _q_navigateForward();
209 void _q_navigateToParent();
210 void _q_createDirectory();
211 void _q_showListView();
212 void _q_showDetailsView();
213 void _q_showContextMenu(const QPoint &position);
214 void _q_renameCurrent();
215 void _q_deleteCurrent();
216 void _q_showHidden();
217 void _q_showHeader(QAction *);
218 void _q_updateOkButton();
219 void _q_currentChanged(const QModelIndex &index);
220 void _q_enterDirectory(const QModelIndex &index);
221 void _q_emitUrlSelected(const QUrl &file);
222 void _q_emitUrlsSelected(const QList<QUrl> &files);
223 void _q_nativeCurrentChanged(const QUrl &file);
224 void _q_nativeEnterDirectory(const QUrl &directory);
225 void _q_goToDirectory(const QString &);
226 void _q_useNameFilter(int index);
227 void _q_selectionChanged();
228 void _q_goToUrl(const QUrl &url);
229 void _q_autoCompleteFileName(const QString &);
230 void _q_rowsInserted(const QModelIndex & parent);
231 void _q_fileRenamed(const QString &path, const QString &oldName, const QString &newName);
232
233 // layout
234#if QT_CONFIG(proxymodel)
235 QAbstractProxyModel *proxyModel;
236#endif
237
238 // data
239 QStringList watching;
240 QFileSystemModel *model;
241
242#if QT_CONFIG(fscompleter)
243 QFSCompleter *completer;
244#endif //QT_CONFIG(fscompleter)
245
246 QString setWindowTitle;
247
248 QList<HistoryItem> currentHistory;
249 int currentHistoryLocation;
250
251 QAction *renameAction;
252 QAction *deleteAction;
253 QAction *showHiddenAction;
254 QAction *newFolderAction;
255
256 bool useDefaultCaption;
257
258 // setVisible_sys returns true if it ends up showing a native
259 // dialog. Returning false means that a non-native dialog must be
260 // used instead.
261 bool canBeNativeDialog() const override;
262 inline bool usingWidgets() const;
263
264 inline void setDirectory_sys(const QUrl &directory);
265 inline QUrl directory_sys() const;
266 inline void selectFile_sys(const QUrl &filename);
267 inline QList<QUrl> selectedFiles_sys() const;
268 inline void setFilter_sys();
269 inline void selectMimeTypeFilter_sys(const QString &filter);
270 inline QString selectedMimeTypeFilter_sys() const;
271 inline void selectNameFilter_sys(const QString &filter);
272 inline QString selectedNameFilter_sys() const;
273 //////////////////////////////////////////////
274
275 QScopedPointer<Ui_QFileDialog> qFileDialogUi;
276
277 QString acceptLabel;
278
279 QPointer<QObject> receiverToDisconnectOnClose;
280 QByteArray memberToDisconnectOnClose;
281 QByteArray signalToDisconnectOnClose;
282
283 QSharedPointer<QFileDialogOptions> options;
284
285 // Memory of what was read from QSettings in restoreState() in case widgets are not used
286 QByteArray splitterState;
287 QByteArray headerData;
288 QList<QUrl> sidebarUrls;
289 QFileIconProvider defaultIconProvider;
290
291 ~QFileDialogPrivate();
292
293private:
294 virtual void initHelper(QPlatformDialogHelper *) override;
295 virtual void helperPrepareShow(QPlatformDialogHelper *) override;
296 virtual void helperDone(QDialog::DialogCode, QPlatformDialogHelper *) override;
297
298 Q_DISABLE_COPY_MOVE(QFileDialogPrivate)
299};
300
301class QFileDialogLineEdit : public QLineEdit
302{
303public:
304 QFileDialogLineEdit(QWidget *parent = nullptr) : QLineEdit(parent), d_ptr(nullptr){}
305 void setFileDialogPrivate(QFileDialogPrivate *d_pointer) {d_ptr = d_pointer; }
306 void keyPressEvent(QKeyEvent *e) override;
307 bool hideOnEsc;
308private:
309 QFileDialogPrivate *d_ptr;
310};
311
312class QFileDialogComboBox : public QComboBox
313{
314public:
315 QFileDialogComboBox(QWidget *parent = nullptr) : QComboBox(parent), urlModel(nullptr) {}
316 void setFileDialogPrivate(QFileDialogPrivate *d_pointer);
317 void showPopup() override;
318 void setHistory(const QStringList &paths);
319 QStringList history() const { return m_history; }
320 void paintEvent(QPaintEvent *) override;
321
322private:
323 QUrlModel *urlModel;
324 QFileDialogPrivate *d_ptr;
325 QStringList m_history;
326};
327
328class QFileDialogListView : public QListView
329{
330public:
331 QFileDialogListView(QWidget *parent = nullptr);
332 void setFileDialogPrivate(QFileDialogPrivate *d_pointer);
333 QSize sizeHint() const override;
334protected:
335 void keyPressEvent(QKeyEvent *e) override;
336private:
337 QFileDialogPrivate *d_ptr;
338};
339
340class QFileDialogTreeView : public QTreeView
341{
342public:
343 QFileDialogTreeView(QWidget *parent);
344 void setFileDialogPrivate(QFileDialogPrivate *d_pointer);
345 QSize sizeHint() const override;
346
347protected:
348 void keyPressEvent(QKeyEvent *e) override;
349private:
350 QFileDialogPrivate *d_ptr;
351};
352
353QModelIndex QFileDialogPrivate::mapToSource(const QModelIndex &index) const {
354#if QT_CONFIG(proxymodel)
355 return proxyModel ? proxyModel->mapToSource(index) : index;
356#else
357 return index;
358#endif
359}
360QModelIndex QFileDialogPrivate::mapFromSource(const QModelIndex &index) const {
361#if QT_CONFIG(proxymodel)
362 return proxyModel ? proxyModel->mapFromSource(index) : index;
363#else
364 return index;
365#endif
366}
367
368QString QFileDialogPrivate::rootPath() const
369{
370 return (model ? model->rootPath() : QStringLiteral("/"));
371}
372
373void QFileDialogPrivate::setDirectory_sys(const QUrl &directory)
374{
375 QPlatformFileDialogHelper *helper = platformFileDialogHelper();
376
377 if (!helper)
378 return;
379
380 if (helper->isSupportedUrl(directory))
381 helper->setDirectory(directory);
382}
383
384QUrl QFileDialogPrivate::directory_sys() const
385{
386 if (QPlatformFileDialogHelper *helper = platformFileDialogHelper())
387 return helper->directory();
388 return QUrl();
389}
390
391void QFileDialogPrivate::selectFile_sys(const QUrl &filename)
392{
393 QPlatformFileDialogHelper *helper = platformFileDialogHelper();
394
395 if (!helper)
396 return;
397
398 if (helper->isSupportedUrl(filename))
399 helper->selectFile(filename);
400}
401
402QList<QUrl> QFileDialogPrivate::selectedFiles_sys() const
403{
404 if (QPlatformFileDialogHelper *helper = platformFileDialogHelper())
405 return helper->selectedFiles();
406 return QList<QUrl>();
407}
408
409void QFileDialogPrivate::setFilter_sys()
410{
411 if (QPlatformFileDialogHelper *helper = platformFileDialogHelper())
412 helper->setFilter();
413}
414
415void QFileDialogPrivate::selectMimeTypeFilter_sys(const QString &filter)
416{
417 if (QPlatformFileDialogHelper *helper = platformFileDialogHelper())
418 helper->selectMimeTypeFilter(filter);
419}
420
421QString QFileDialogPrivate::selectedMimeTypeFilter_sys() const
422{
423 if (QPlatformFileDialogHelper *helper = platformFileDialogHelper())
424 return helper->selectedMimeTypeFilter();
425
426 return QString();
427}
428
429void QFileDialogPrivate::selectNameFilter_sys(const QString &filter)
430{
431 if (QPlatformFileDialogHelper *helper = platformFileDialogHelper())
432 helper->selectNameFilter(filter);
433}
434
435QString QFileDialogPrivate::selectedNameFilter_sys() const
436{
437 if (QPlatformFileDialogHelper *helper = platformFileDialogHelper())
438 return helper->selectedNameFilter();
439 return QString();
440}
441
442QT_END_NAMESPACE
443
444#endif // QFILEDIALOG_P_H
445