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 | |
16 | QT_BEGIN_NAMESPACE |
17 | |
18 | class QQuickFileDialogDelegatePrivate : public QQuickItemDelegatePrivate |
19 | { |
20 | public: |
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 | |
34 | void 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 | |
57 | void 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 | |
74 | bool QQuickFileDialogDelegatePrivate::acceptKeyClick(Qt::Key key) const |
75 | { |
76 | return key == Qt::Key_Return || key == Qt::Key_Enter; |
77 | } |
78 | |
79 | QQuickFileDialogDelegate::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 | |
93 | QQuickDialog *QQuickFileDialogDelegate::dialog() const |
94 | { |
95 | Q_D(const QQuickFileDialogDelegate); |
96 | return d->dialog; |
97 | } |
98 | |
99 | void 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 | |
111 | QUrl QQuickFileDialogDelegate::file() const |
112 | { |
113 | Q_D(const QQuickFileDialogDelegate); |
114 | return d->file; |
115 | } |
116 | |
117 | void 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 | |
137 | void 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 | |
150 | QT_END_NAMESPACE |
151 | |
152 | #include "moc_qquickfiledialogdelegate_p.cpp" |
153 | |