7import QGroundControl.Controls
9/// This control is meant to be a direct replacement for the standard Qml FileDialog control.
10/// It differs for mobile builds which uses a completely custom file picker.
15 property string folder // Due to Qt bug with file url parsing this must be an absolute path
16 property var nameFilters: [] // Important: Only name filters with simple wildcarding like *.foo are supported.
18 property bool selectFolder: false
19 property string defaultSuffix: ""
21 signal acceptedForLoad(string file)
22 signal acceptedForSave(string file)
24 signal fileImportedNotify // Emitted on a successful import so the open dialog can refresh its list.
26 function openForLoad() {
28 if (QGCFileDialogController.testHookArmed()) {
29 // Unit test shim: bypass the native dialog and produce the armed result
30 var testFile = QGCFileDialogController.takeTestNextFile()
31 if (testFile.length === 0) {
34 _root.acceptedForLoad(testFile)
36 } else if (_mobileDlg && folder.length !== 0) {
37 mobileFileOpenDialogFactory.open()
38 } else if (selectFolder) {
39 fullFolderDialog.open()
41 fullFileDialog.fileMode = FileDialog.OpenFile
46 function openForSave() {
48 if (QGCFileDialogController.testHookArmed()) {
49 // Unit test shim: bypass the native dialog and produce the armed result
50 var testFile = QGCFileDialogController.takeTestNextFile()
51 if (testFile.length === 0) {
54 _root.acceptedForSave(testFile)
56 } else if (_mobileDlg && folder.length !== 0) {
57 mobileFileSaveDialogFactory.open()
59 fullFileDialog.fileMode = FileDialog.SaveFile
65 fullFileDialog.close()
68 property bool _openForLoad: true
69 property real _margins: ScreenTools.defaultFontPixelHeight / 2
70 property bool _mobileDlg: QGroundControl.corePlugin.options.useMobileFileDialog
71 property var _rgExtensions
72 property string _mobileShortPath
73 property bool _importPending: false
75 Component.onCompleted: {
76 _setupFileExtensions()
77 _updateMobileShortPath()
80 onFolderChanged: _updateMobileShortPath()
81 onNameFiltersChanged: _setupFileExtensions()
83 function _updateMobileShortPath() {
84 if (ScreenTools.isMobile) {
85 _mobileShortPath = QGCFileDialogController.fullFolderPathToShortMobilePath(folder);
89 function _setupFileExtensions() {
91 for (var i=0; i<_root.nameFilters.length; i++) {
92 var filter = _root.nameFilters[i]
93 var regExp = /^.*\((.*)\)$/
94 var result = regExp.exec(filter)
95 if (result.length === 2) {
98 var rgFilters = filter.split(" ")
99 for (var j=0; j<rgFilters.length; j++) {
100 if (!_mobileDlg || (rgFilters[j] !== "*" && rgFilters[j] !== "*.*")) {
101 _rgExtensions.push(rgFilters[j])
107 QGCPalette { id: qgcPal; colorGroupEnabled: true }
110 target: QGCFileDialogController
111 enabled: Qt.platform.os === "android" && _root._importPending
113 function onFileImported() {
114 _root._importPending = false
115 _root.fileImportedNotify()
118 function onImportFailed(errorMessage) {
119 _root._importPending = false
120 QGroundControl.showMessageDialog(_root, qsTr("Import"), errorMessage)
126 currentFolder: QGCFileDialogController.localFileToUrl(_root.folder)
127 nameFilters: _root.nameFilters ? _root.nameFilters : []
129 defaultSuffix: _root.defaultSuffix
132 var fullPath = QGCFileDialogController.urlToLocalFile(selectedFile)
133 if (fileMode == FileDialog.OpenFile) {
134 _root.acceptedForLoad(fullPath)
136 _root.acceptedForSave(fullPath)
139 onRejected: _root.rejected()
144 currentFolder: QGCFileDialogController.localFileToUrl(_root.folder)
147 onAccepted: _root.acceptedForLoad(QGCFileDialogController.urlToLocalFile(selectedFolder))
148 onRejected: _root.rejected()
151 QGCPopupDialogFactory {
152 id: mobileFileOpenDialogFactory
154 dialogComponent: mobileFileOpenDialogComponent
158 id: mobileFileOpenDialogComponent
161 id: mobileFileOpenDialog
163 buttons: Dialog.Cancel
167 function onFileImportedNotify() {
168 fileRepeater.model = QGCFileDialogController.getFiles(folder, _rgExtensions)
174 width: 40 * ScreenTools.defaultFontPixelWidth
175 spacing: ScreenTools.defaultFontPixelHeight / 2
177 QGCLabel { text: qsTr("Path: %1").arg(_mobileShortPath) }
181 model: QGCFileDialogController.getFiles(folder, _rgExtensions)
185 anchors.left: parent.left
186 anchors.right: parent.right
190 mobileFileOpenDialog.close()
191 _root.acceptedForLoad(QGCFileDialogController.fullyQualifiedFilename(folder, modelData))
194 onHamburgerClicked: {
196 hamburgerMenu.fileToDelete = QGCFileDialogController.fullyQualifiedFilename(folder, modelData)
197 hamburgerMenu.popup()
203 property string fileToDelete
205 onAboutToHide: fileButton.highlight = false
210 QGCFileDialogController.deleteFile(hamburgerMenu.fileToDelete)
211 fileRepeater.model = QGCFileDialogController.getFiles(folder, _rgExtensions)
219 text: qsTr("No files")
220 visible: fileRepeater.model.length === 0
224 anchors.left: parent.left
225 anchors.right: parent.right
227 visible: Qt.platform.os === "android"
230 _root._importPending = true
231 QGCFileDialogController.importFromNativePicker()
238 QGCPopupDialogFactory {
239 id: mobileFileSaveDialogFactory
241 dialogComponent: mobileFileSaveDialogComponent
245 id: mobileFileSaveDialogComponent
248 id: mobileFileSaveDialog
250 buttons: Dialog.Cancel | Dialog.Ok
253 if (filenameTextField.text == "") {
254 mobileFileSaveDialog.preventClose = true
257 if (!replaceMessage.visible) {
258 if (QGCFileDialogController.fileExists(QGCFileDialogController.fullyQualifiedFilename(folder, filenameTextField.text, _rgExtensions))) {
259 replaceMessage.visible = true
260 mobileFileSaveDialog.preventClose = true
264 _root.acceptedForSave(QGCFileDialogController.fullyQualifiedFilename(folder, filenameTextField.text, _rgExtensions))
269 width: 40 * ScreenTools.defaultFontPixelWidth
270 spacing: ScreenTools.defaultFontPixelHeight / 2
273 anchors.left: parent.left
274 anchors.right: parent.right
275 spacing: ScreenTools.defaultFontPixelWidth
277 QGCLabel { text: qsTr("New file name:") }
280 id: filenameTextField
281 Layout.fillWidth: true
282 onTextChanged: replaceMessage.visible = false
288 anchors.left: parent.left
289 anchors.right: parent.right
290 wrapMode: Text.WordWrap
291 text: qsTr("The file %1 exists. Click Save again to replace it.").arg(filenameTextField.text)
293 color: qgcPal.warningText
297 anchors.left: parent.left
298 anchors.right: parent.right
299 text: qsTr("Save to existing file:")
304 model: QGCFileDialogController.getFiles(folder, [ _rgExtensions ])
308 anchors.left: parent.left
309 anchors.right: parent.right
313 mobileFileSaveDialog.close()
314 _root.acceptedForSave(QGCFileDialogController.fullyQualifiedFilename(folder, modelData))
317 onHamburgerClicked: {
319 hamburgerMenu.fileToDelete = QGCFileDialogController.fullyQualifiedFilename(folder, modelData)
320 hamburgerMenu.popup()
326 property string fileToDelete
328 onAboutToHide: fileButton.highlight = false
333 QGCFileDialogController.deleteFile(hamburgerMenu.fileToDelete)
334 fileRepeater.model = QGCFileDialogController.getFiles(folder, [ _rgExtensions ])