QGroundControl
Ground Control Station for MAVLink Drones
Loading...
Searching...
No Matches
QGCFileDialog.qml
Go to the documentation of this file.
1import QtQuick
2import QtQuick.Controls
3import QtQuick.Dialogs
4import QtQuick.Layouts
5import Qt.labs.platform as Labs
6
7import QGroundControl
8import QGroundControl.Controls
9
10/// This control is meant to be a direct replacement for the standard Qml FileDialog control.
11/// It differs for mobile builds which uses a completely custom file picker.
12Item {
13 id: _root
14 visible: false
15
16 property string folder // Due to Qt bug with file url parsing this must be an absolute path
17 property var nameFilters: [] // Important: Only name filters with simple wildcarding like *.foo are supported.
18 property string title
19 property bool selectFolder: false
20 property string defaultSuffix: ""
21
22 signal acceptedForLoad(string file)
23 signal acceptedForSave(string file)
24 signal rejected
25 signal fileImportedNotify // Emitted on a successful import so the open dialog can refresh its list.
26
27 function openForLoad() {
28 _openForLoad = true
29 if (_mobileDlg && folder.length !== 0) {
30 mobileFileOpenDialogFactory.open()
31 } else if (selectFolder) {
32 fullFolderDialog.open()
33 } else {
34 fullFileDialog.fileMode = FileDialog.OpenFile
35 fullFileDialog.open()
36 }
37 }
38
39 function openForSave() {
40 _openForLoad = false
41 if (_mobileDlg && folder.length !== 0) {
42 mobileFileSaveDialogFactory.open()
43 } else {
44 fullFileDialog.fileMode = FileDialog.SaveFile
45 fullFileDialog.open()
46 }
47 }
48
49 function close() {
50 fullFileDialog.close()
51 }
52
53 property bool _openForLoad: true
54 property real _margins: ScreenTools.defaultFontPixelHeight / 2
55 property bool _mobileDlg: QGroundControl.corePlugin.options.useMobileFileDialog
56 property var _rgExtensions
57 property string _mobileShortPath
58 property bool _importPending: false
59
60 Component.onCompleted: {
61 _setupFileExtensions()
62 _updateMobileShortPath()
63 }
64
65 onFolderChanged: _updateMobileShortPath()
66 onNameFiltersChanged: _setupFileExtensions()
67
68 function _updateMobileShortPath() {
69 if (ScreenTools.isMobile) {
70 _mobileShortPath = QGCFileDialogController.fullFolderPathToShortMobilePath(folder);
71 }
72 }
73
74 function _setupFileExtensions() {
75 _rgExtensions = [ ]
76 for (var i=0; i<_root.nameFilters.length; i++) {
77 var filter = _root.nameFilters[i]
78 var regExp = /^.*\‍((.*)\‍)$/
79 var result = regExp.exec(filter)
80 if (result.length === 2) {
81 filter = result[1]
82 }
83 var rgFilters = filter.split(" ")
84 for (var j=0; j<rgFilters.length; j++) {
85 if (!_mobileDlg || (rgFilters[j] !== "*" && rgFilters[j] !== "*.*")) {
86 _rgExtensions.push(rgFilters[j])
87 }
88 }
89 }
90 }
91
92 QGCPalette { id: qgcPal; colorGroupEnabled: true }
93
94 Connections {
95 target: QGCFileDialogController
96 enabled: Qt.platform.os === "android" && _root._importPending
97
98 function onFileImported() {
99 _root._importPending = false
100 _root.fileImportedNotify()
101 }
102
103 function onImportFailed(errorMessage) {
104 _root._importPending = false
105 QGroundControl.showMessageDialog(_root, qsTr("Import"), errorMessage)
106 }
107 }
108
109 FileDialog {
110 id: fullFileDialog
111 currentFolder: "file:///" + _root.folder
112 nameFilters: _root.nameFilters ? _root.nameFilters : []
113 title: _root.title
114 defaultSuffix: _root.defaultSuffix
115
116 onAccepted: {
117 var fullPath = QGCFileDialogController.urlToLocalFile(selectedFile)
118 if (fileMode == FileDialog.OpenFile) {
119 _root.acceptedForLoad(fullPath)
120 } else {
121 _root.acceptedForSave(fullPath)
122 }
123 }
124 onRejected: _root.rejected()
125 }
126
127 Labs.FolderDialog {
128 id: fullFolderDialog
129 currentFolder: "file:///" + _root.folder
130 title: _root.title
131
132 onAccepted: _root.acceptedForLoad(QGCFileDialogController.urlToLocalFile(folder))
133 onRejected: _root.rejected()
134 }
135
136 QGCPopupDialogFactory {
137 id: mobileFileOpenDialogFactory
138
139 dialogComponent: mobileFileOpenDialogComponent
140 }
141
142 Component {
143 id: mobileFileOpenDialogComponent
144
145 QGCPopupDialog {
146 id: mobileFileOpenDialog
147 title: _root.title
148 buttons: Dialog.Cancel
149
150 Connections {
151 target: _root
152 function onFileImportedNotify() {
153 fileRepeater.model = QGCFileDialogController.getFiles(folder, _rgExtensions)
154 }
155 }
156
157 Column {
158 id: fileOpenColumn
159 width: 40 * ScreenTools.defaultFontPixelWidth
160 spacing: ScreenTools.defaultFontPixelHeight / 2
161
162 QGCLabel { text: qsTr("Path: %1").arg(_mobileShortPath) }
163
164 Repeater {
165 id: fileRepeater
166 model: QGCFileDialogController.getFiles(folder, _rgExtensions)
167
168 FileButton {
169 id: fileButton
170 anchors.left: parent.left
171 anchors.right: parent.right
172 text: modelData
173
174 onClicked: {
175 mobileFileOpenDialog.close()
176 _root.acceptedForLoad(QGCFileDialogController.fullyQualifiedFilename(folder, modelData))
177 }
178
179 onHamburgerClicked: {
180 highlight = true
181 hamburgerMenu.fileToDelete = QGCFileDialogController.fullyQualifiedFilename(folder, modelData)
182 hamburgerMenu.popup()
183 }
184
185 QGCMenu {
186 id: hamburgerMenu
187
188 property string fileToDelete
189
190 onAboutToHide: fileButton.highlight = false
191
192 QGCMenuItem {
193 text: qsTr("Delete")
194 onTriggered: {
195 QGCFileDialogController.deleteFile(hamburgerMenu.fileToDelete)
196 fileRepeater.model = QGCFileDialogController.getFiles(folder, _rgExtensions)
197 }
198 }
199 }
200 }
201 }
202
203 QGCLabel {
204 text: qsTr("No files")
205 visible: fileRepeater.model.length === 0
206 }
207
208 QGCButton {
209 anchors.left: parent.left
210 anchors.right: parent.right
211 text: qsTr("Import")
212 visible: Qt.platform.os === "android"
213
214 onClicked: {
215 _root._importPending = true
216 QGCFileDialogController.importFromNativePicker()
217 }
218 }
219 }
220 }
221 }
222
223 QGCPopupDialogFactory {
224 id: mobileFileSaveDialogFactory
225
226 dialogComponent: mobileFileSaveDialogComponent
227 }
228
229 Component {
230 id: mobileFileSaveDialogComponent
231
232 QGCPopupDialog {
233 id: mobileFileSaveDialog
234 title: _root.title
235 buttons: Dialog.Cancel | Dialog.Ok
236
237 onAccepted: {
238 if (filenameTextField.text == "") {
239 mobileFileSaveDialog.preventClose = true
240 return
241 }
242 if (!replaceMessage.visible) {
243 if (QGCFileDialogController.fileExists(QGCFileDialogController.fullyQualifiedFilename(folder, filenameTextField.text, _rgExtensions))) {
244 replaceMessage.visible = true
245 mobileFileSaveDialog.preventClose = true
246 return
247 }
248 }
249 _root.acceptedForSave(QGCFileDialogController.fullyQualifiedFilename(folder, filenameTextField.text, _rgExtensions))
250 }
251
252 Column {
253 id: fileSaveColumn
254 width: 40 * ScreenTools.defaultFontPixelWidth
255 spacing: ScreenTools.defaultFontPixelHeight / 2
256
257 RowLayout {
258 anchors.left: parent.left
259 anchors.right: parent.right
260 spacing: ScreenTools.defaultFontPixelWidth
261
262 QGCLabel { text: qsTr("New file name:") }
263
264 QGCTextField {
265 id: filenameTextField
266 Layout.fillWidth: true
267 onTextChanged: replaceMessage.visible = false
268 }
269 }
270
271 QGCLabel {
272 id: replaceMessage
273 anchors.left: parent.left
274 anchors.right: parent.right
275 wrapMode: Text.WordWrap
276 text: qsTr("The file %1 exists. Click Save again to replace it.").arg(filenameTextField.text)
277 visible: false
278 color: qgcPal.warningText
279 }
280
281 SectionHeader {
282 anchors.left: parent.left
283 anchors.right: parent.right
284 text: qsTr("Save to existing file:")
285 }
286
287 Repeater {
288 id: fileRepeater
289 model: QGCFileDialogController.getFiles(folder, [ _rgExtensions ])
290
291 FileButton {
292 id: fileButton
293 anchors.left: parent.left
294 anchors.right: parent.right
295 text: modelData
296
297 onClicked: {
298 mobileFileSaveDialog.close()
299 _root.acceptedForSave(QGCFileDialogController.fullyQualifiedFilename(folder, modelData))
300 }
301
302 onHamburgerClicked: {
303 highlight = true
304 hamburgerMenu.fileToDelete = QGCFileDialogController.fullyQualifiedFilename(folder, modelData)
305 hamburgerMenu.popup()
306 }
307
308 QGCMenu {
309 id: hamburgerMenu
310
311 property string fileToDelete
312
313 onAboutToHide: fileButton.highlight = false
314
315 QGCMenuItem {
316 text: qsTr("Delete")
317 onTriggered: {
318 QGCFileDialogController.deleteFile(hamburgerMenu.fileToDelete)
319 fileRepeater.model = QGCFileDialogController.getFiles(folder, [ _rgExtensions ])
320 }
321 }
322 }
323 }
324 }
325 }
326 }
327 }
328}