1// Copyright (C) 2021 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#include "qquickfiledialogdelegate_p.h"
5
6#include <QtCore/qfileinfo.h>
7#include <QtGui/qpa/qplatformtheme.h>
8#include <QtQml/QQmlFile>
9#include <QtQml/qqmlexpression.h>
10#include <QtQuick/private/qquicklistview_p.h>
11#include <QtQuickTemplates2/private/qquickitemdelegate_p_p.h>
12
13#include "qquickfiledialogimpl_p.h"
14#include "qquickfolderdialogimpl_p.h"
15
16QT_BEGIN_NAMESPACE
17
18class QQuickFileDialogDelegatePrivate : public QQuickItemDelegatePrivate
19{
20public:
21 Q_DECLARE_PUBLIC(QQuickFileDialogDelegate)
22
23 void highlightFile();
24 void chooseFile();
25
26 bool acceptKeyClick(Qt::Key key) const override;
27
28 QQuickDialog *dialog = nullptr;
29 QQuickFileDialogImpl *fileDialog = nullptr;
30 QQuickFolderDialogImpl *folderDialog = nullptr;
31 QUrl file;
32};
33
34void QQuickFileDialogDelegatePrivate::highlightFile()
35{
36 Q_Q(QQuickFileDialogDelegate);
37 QQuickListViewAttached *attached = static_cast<QQuickListViewAttached*>(
38 qmlAttachedPropertiesObject<QQuickListView>(obj: q));
39 if (!attached)
40 return;
41
42 QQmlContext *delegateContext = qmlContext(q);
43 if (!delegateContext)
44 return;
45
46 bool converted = false;
47 const int index = q->property(name: "index").toInt(ok: &converted);
48 if (converted) {
49 attached->view()->setCurrentIndex(index);
50 if (fileDialog)
51 fileDialog->setSelectedFile(file);
52 else if (folderDialog)
53 folderDialog->setSelectedFolder(file);
54 }
55}
56
57void QQuickFileDialogDelegatePrivate::chooseFile()
58{
59 const QFileInfo fileInfo(QQmlFile::urlToLocalFileOrQrc(file));
60 if (fileInfo.isDir()) {
61 // If it's a directory, navigate to it.
62 if (fileDialog)
63 fileDialog->setCurrentFolder(currentFolder: file);
64 else
65 folderDialog->setCurrentFolder(file);
66 } else {
67 Q_ASSERT(fileDialog);
68 // Otherwise it's a file, so select it and close the dialog.
69 fileDialog->setSelectedFile(file);
70 fileDialog->accept();
71 }
72}
73
74bool QQuickFileDialogDelegatePrivate::acceptKeyClick(Qt::Key key) const
75{
76 return key == Qt::Key_Return || key == Qt::Key_Enter;
77}
78
79QQuickFileDialogDelegate::QQuickFileDialogDelegate(QQuickItem *parent)
80 : QQuickItemDelegate(*(new QQuickFileDialogDelegatePrivate), parent)
81{
82 Q_D(QQuickFileDialogDelegate);
83 // Clicking and tabbing should result in it getting focus,
84 // as e.g. Ubuntu and Windows both allow tabbing through file dialogs.
85 setFocusPolicy(Qt::StrongFocus);
86 setCheckable(true);
87 QObjectPrivate::connect(sender: this, signal: &QQuickFileDialogDelegate::clicked,
88 receiverPrivate: d, slot: &QQuickFileDialogDelegatePrivate::highlightFile);
89 QObjectPrivate::connect(sender: this, signal: &QQuickFileDialogDelegate::doubleClicked,
90 receiverPrivate: d, slot: &QQuickFileDialogDelegatePrivate::chooseFile);
91}
92
93QQuickDialog *QQuickFileDialogDelegate::dialog() const
94{
95 Q_D(const QQuickFileDialogDelegate);
96 return d->dialog;
97}
98
99void QQuickFileDialogDelegate::setDialog(QQuickDialog *dialog)
100{
101 Q_D(QQuickFileDialogDelegate);
102 if (dialog == d->dialog)
103 return;
104
105 d->dialog = dialog;
106 d->fileDialog = qobject_cast<QQuickFileDialogImpl*>(object: dialog);
107 d->folderDialog = qobject_cast<QQuickFolderDialogImpl*>(object: dialog);
108 emit dialogChanged();
109}
110
111QUrl QQuickFileDialogDelegate::file() const
112{
113 Q_D(const QQuickFileDialogDelegate);
114 return d->file;
115}
116
117void QQuickFileDialogDelegate::setFile(const QUrl &file)
118{
119 Q_D(QQuickFileDialogDelegate);
120 QUrl adjustedFile = file;
121#ifdef Q_OS_WIN32
122 // Work around QTBUG-99105 (FolderListModel uses lowercase drive letter).
123 QString path = adjustedFile.path();
124 const int driveColonIndex = path.indexOf(QLatin1Char(':'));
125 if (driveColonIndex == 2) {
126 path.replace(1, 1, path.at(1).toUpper());
127 adjustedFile.setPath(path);
128 }
129#endif
130 if (adjustedFile == d->file)
131 return;
132
133 d->file = adjustedFile;
134 emit fileChanged();
135}
136
137void QQuickFileDialogDelegate::keyReleaseEvent(QKeyEvent *event)
138{
139 Q_D(QQuickFileDialogDelegate);
140 // We need to respond to being triggered by enter being pressed,
141 // but we can't use event->isAccepted() to check, because events are pre-accepted.
142 auto connection = QObjectPrivate::connect(sender: this, signal: &QQuickFileDialogDelegate::clicked,
143 receiverPrivate: d, slot: &QQuickFileDialogDelegatePrivate::chooseFile);
144
145 QQuickItemDelegate::keyReleaseEvent(event);
146
147 disconnect(connection);
148}
149
150QT_END_NAMESPACE
151
152#include "moc_qquickfiledialogdelegate_p.cpp"
153

source code of qtdeclarative/src/quickdialogs/quickdialogsquickimpl/qquickfiledialogdelegate.cpp