5import Qt.labs.platform as Labs
8import QGroundControl.Controls
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.
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.
19 property bool selectFolder: false
20 property string defaultSuffix: ""
22 signal acceptedForLoad(string file)
23 signal acceptedForSave(string file)
25 signal fileImportedNotify // Emitted on a successful import so the open dialog can refresh its list.
27 function openForLoad() {
29 if (QGCFileDialogController.testHookArmed()) {
30 // Unit test shim: bypass the native dialog and produce the armed result
31 var testFile = QGCFileDialogController.takeTestNextFile()
32 if (testFile.length === 0) {
35 _root.acceptedForLoad(testFile)
37 } else if (_mobileDlg && folder.length !== 0) {
38 mobileFileOpenDialogFactory.open()
39 } else if (selectFolder) {
40 fullFolderDialog.open()
42 fullFileDialog.fileMode = FileDialog.OpenFile
47 function openForSave() {
49 if (QGCFileDialogController.testHookArmed()) {
50 // Unit test shim: bypass the native dialog and produce the armed result
51 var testFile = QGCFileDialogController.takeTestNextFile()
52 if (testFile.length === 0) {
55 _root.acceptedForSave(testFile)
57 } else if (_mobileDlg && folder.length !== 0) {
58 mobileFileSaveDialogFactory.open()
60 fullFileDialog.fileMode = FileDialog.SaveFile
66 fullFileDialog.close()
69 property bool _openForLoad: true
70 property real _margins: ScreenTools.defaultFontPixelHeight / 2
71 property bool _mobileDlg: QGroundControl.corePlugin.options.useMobileFileDialog
72 property var _rgExtensions
73 property string _mobileShortPath
74 property bool _importPending: false
76 Component.onCompleted: {
77 _setupFileExtensions()
78 _updateMobileShortPath()
81 onFolderChanged: _updateMobileShortPath()
82 onNameFiltersChanged: _setupFileExtensions()
84 function _updateMobileShortPath() {
85 if (ScreenTools.isMobile) {
86 _mobileShortPath = QGCFileDialogController.fullFolderPathToShortMobilePath(folder);
90 function _setupFileExtensions() {
92 for (var i=0; i<_root.nameFilters.length; i++) {
93 var filter = _root.nameFilters[i]
94 var regExp = /^.*\((.*)\)$/
95 var result = regExp.exec(filter)
96 if (result.length === 2) {
99 var rgFilters = filter.split(" ")
100 for (var j=0; j<rgFilters.length; j++) {
101 if (!_mobileDlg || (rgFilters[j] !== "*" && rgFilters[j] !== "*.*")) {
102 _rgExtensions.push(rgFilters[j])
108 QGCPalette { id: qgcPal; colorGroupEnabled: true }
111 target: QGCFileDialogController
112 enabled: Qt.platform.os === "android" && _root._importPending
114 function onFileImported() {
115 _root._importPending = false
116 _root.fileImportedNotify()
119 function onImportFailed(errorMessage) {
120 _root._importPending = false
121 QGroundControl.showMessageDialog(_root, qsTr("Import"), errorMessage)
127 currentFolder: "file:///" + _root.folder
128 nameFilters: _root.nameFilters ? _root.nameFilters : []
130 defaultSuffix: _root.defaultSuffix
133 var fullPath = QGCFileDialogController.urlToLocalFile(selectedFile)
134 if (fileMode == FileDialog.OpenFile) {
135 _root.acceptedForLoad(fullPath)
137 _root.acceptedForSave(fullPath)
140 onRejected: _root.rejected()
145 currentFolder: "file:///" + _root.folder
148 onAccepted: _root.acceptedForLoad(QGCFileDialogController.urlToLocalFile(folder))
149 onRejected: _root.rejected()
152 QGCPopupDialogFactory {
153 id: mobileFileOpenDialogFactory
155 dialogComponent: mobileFileOpenDialogComponent
159 id: mobileFileOpenDialogComponent
162 id: mobileFileOpenDialog
164 buttons: Dialog.Cancel
168 function onFileImportedNotify() {
169 fileRepeater.model = QGCFileDialogController.getFiles(folder, _rgExtensions)
175 width: 40 * ScreenTools.defaultFontPixelWidth
176 spacing: ScreenTools.defaultFontPixelHeight / 2
178 QGCLabel { text: qsTr("Path: %1").arg(_mobileShortPath) }
182 model: QGCFileDialogController.getFiles(folder, _rgExtensions)
186 anchors.left: parent.left
187 anchors.right: parent.right
191 mobileFileOpenDialog.close()
192 _root.acceptedForLoad(QGCFileDialogController.fullyQualifiedFilename(folder, modelData))
195 onHamburgerClicked: {
197 hamburgerMenu.fileToDelete = QGCFileDialogController.fullyQualifiedFilename(folder, modelData)
198 hamburgerMenu.popup()
204 property string fileToDelete
206 onAboutToHide: fileButton.highlight = false
211 QGCFileDialogController.deleteFile(hamburgerMenu.fileToDelete)
212 fileRepeater.model = QGCFileDialogController.getFiles(folder, _rgExtensions)
220 text: qsTr("No files")
221 visible: fileRepeater.model.length === 0
225 anchors.left: parent.left
226 anchors.right: parent.right
228 visible: Qt.platform.os === "android"
231 _root._importPending = true
232 QGCFileDialogController.importFromNativePicker()
239 QGCPopupDialogFactory {
240 id: mobileFileSaveDialogFactory
242 dialogComponent: mobileFileSaveDialogComponent
246 id: mobileFileSaveDialogComponent
249 id: mobileFileSaveDialog
251 buttons: Dialog.Cancel | Dialog.Ok
254 if (filenameTextField.text == "") {
255 mobileFileSaveDialog.preventClose = true
258 if (!replaceMessage.visible) {
259 if (QGCFileDialogController.fileExists(QGCFileDialogController.fullyQualifiedFilename(folder, filenameTextField.text, _rgExtensions))) {
260 replaceMessage.visible = true
261 mobileFileSaveDialog.preventClose = true
265 _root.acceptedForSave(QGCFileDialogController.fullyQualifiedFilename(folder, filenameTextField.text, _rgExtensions))
270 width: 40 * ScreenTools.defaultFontPixelWidth
271 spacing: ScreenTools.defaultFontPixelHeight / 2
274 anchors.left: parent.left
275 anchors.right: parent.right
276 spacing: ScreenTools.defaultFontPixelWidth
278 QGCLabel { text: qsTr("New file name:") }
281 id: filenameTextField
282 Layout.fillWidth: true
283 onTextChanged: replaceMessage.visible = false
289 anchors.left: parent.left
290 anchors.right: parent.right
291 wrapMode: Text.WordWrap
292 text: qsTr("The file %1 exists. Click Save again to replace it.").arg(filenameTextField.text)
294 color: qgcPal.warningText
298 anchors.left: parent.left
299 anchors.right: parent.right
300 text: qsTr("Save to existing file:")
305 model: QGCFileDialogController.getFiles(folder, [ _rgExtensions ])
309 anchors.left: parent.left
310 anchors.right: parent.right
314 mobileFileSaveDialog.close()
315 _root.acceptedForSave(QGCFileDialogController.fullyQualifiedFilename(folder, modelData))
318 onHamburgerClicked: {
320 hamburgerMenu.fileToDelete = QGCFileDialogController.fullyQualifiedFilename(folder, modelData)
321 hamburgerMenu.popup()
327 property string fileToDelete
329 onAboutToHide: fileButton.highlight = false
334 QGCFileDialogController.deleteFile(hamburgerMenu.fileToDelete)
335 fileRepeater.model = QGCFileDialogController.getFiles(folder, [ _rgExtensions ])