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
26 function openForLoad() {
27 _openForLoad = true
28 if (_mobileDlg && folder.length !== 0) {
29 mobileFileOpenDialogFactory.open()
30 } else if (selectFolder) {
31 fullFolderDialog.open()
32 } else {
33 fullFileDialog.fileMode = FileDialog.OpenFile
34 fullFileDialog.open()
35 }
36 }
37
38 function openForSave() {
39 _openForLoad = false
40 if (_mobileDlg && folder.length !== 0) {
41 mobileFileSaveDialogFactory.open()
42 } else {
43 fullFileDialog.fileMode = FileDialog.SaveFile
44 fullFileDialog.open()
45 }
46 }
47
48 function close() {
49 fullFileDialog.close()
50 }
51
52 property bool _openForLoad: true
53 property real _margins: ScreenTools.defaultFontPixelHeight / 2
54 property bool _mobileDlg: QGroundControl.corePlugin.options.useMobileFileDialog
55 property var _rgExtensions
56 property string _mobileShortPath
57
58 Component.onCompleted: {
59 _setupFileExtensions()
60 _updateMobileShortPath()
61 }
62
63 onFolderChanged: _updateMobileShortPath()
64 onNameFiltersChanged: _setupFileExtensions()
65
66 function _updateMobileShortPath() {
67 if (ScreenTools.isMobile) {
68 _mobileShortPath = QGCFileDialogController.fullFolderPathToShortMobilePath(folder);
69 }
70 }
71
72 function _setupFileExtensions() {
73 _rgExtensions = [ ]
74 for (var i=0; i<_root.nameFilters.length; i++) {
75 var filter = _root.nameFilters[i]
76 var regExp = /^.*\‍((.*)\‍)$/
77 var result = regExp.exec(filter)
78 if (result.length === 2) {
79 filter = result[1]
80 }
81 var rgFilters = filter.split(" ")
82 for (var j=0; j<rgFilters.length; j++) {
83 if (!_mobileDlg || (rgFilters[j] !== "*" && rgFilters[j] !== "*.*")) {
84 _rgExtensions.push(rgFilters[j])
85 }
86 }
87 }
88 }
89
90 QGCPalette { id: qgcPal; colorGroupEnabled: true }
91
92 FileDialog {
93 id: fullFileDialog
94 currentFolder: "file:///" + _root.folder
95 nameFilters: _root.nameFilters ? _root.nameFilters : []
96 title: _root.title
97 defaultSuffix: _root.defaultSuffix
98
99 onAccepted: {
100 var fullPath = QGCFileDialogController.urlToLocalFile(selectedFile)
101 if (fileMode == FileDialog.OpenFile) {
102 _root.acceptedForLoad(fullPath)
103 } else {
104 _root.acceptedForSave(fullPath)
105 }
106 }
107 onRejected: _root.rejected()
108 }
109
110 Labs.FolderDialog {
111 id: fullFolderDialog
112 currentFolder: "file:///" + _root.folder
113 title: _root.title
114
115 onAccepted: _root.acceptedForLoad(QGCFileDialogController.urlToLocalFile(folder))
116 onRejected: _root.rejected()
117 }
118
119 QGCPopupDialogFactory {
120 id: mobileFileOpenDialogFactory
121
122 dialogComponent: mobileFileOpenDialogComponent
123 }
124
125 Component {
126 id: mobileFileOpenDialogComponent
127
128 QGCPopupDialog {
129 id: mobileFileOpenDialog
130 title: _root.title
131 buttons: Dialog.Cancel
132
133 Column {
134 id: fileOpenColumn
135 width: 40 * ScreenTools.defaultFontPixelWidth
136 spacing: ScreenTools.defaultFontPixelHeight / 2
137
138 QGCLabel { text: qsTr("Path: %1").arg(_mobileShortPath) }
139
140 Repeater {
141 id: fileRepeater
142 model: QGCFileDialogController.getFiles(folder, _rgExtensions)
143
144 FileButton {
145 id: fileButton
146 anchors.left: parent.left
147 anchors.right: parent.right
148 text: modelData
149
150 onClicked: {
151 mobileFileOpenDialog.close()
152 _root.acceptedForLoad(QGCFileDialogController.fullyQualifiedFilename(folder, modelData))
153 }
154
155 onHamburgerClicked: {
156 highlight = true
157 hamburgerMenu.fileToDelete = QGCFileDialogController.fullyQualifiedFilename(folder, modelData)
158 hamburgerMenu.popup()
159 }
160
161 QGCMenu {
162 id: hamburgerMenu
163
164 property string fileToDelete
165
166 onAboutToHide: fileButton.highlight = false
167
168 QGCMenuItem {
169 text: qsTr("Delete")
170 onTriggered: {
171 QGCFileDialogController.deleteFile(hamburgerMenu.fileToDelete)
172 fileRepeater.model = QGCFileDialogController.getFiles(folder, _rgExtensions)
173 }
174 }
175 }
176 }
177 }
178
179 QGCLabel {
180 text: qsTr("No files")
181 visible: fileRepeater.model.length === 0
182 }
183 }
184 }
185 }
186
187 QGCPopupDialogFactory {
188 id: mobileFileSaveDialogFactory
189
190 dialogComponent: mobileFileSaveDialogComponent
191 }
192
193 Component {
194 id: mobileFileSaveDialogComponent
195
196 QGCPopupDialog {
197 id: mobileFileSaveDialog
198 title: _root.title
199 buttons: Dialog.Cancel | Dialog.Ok
200
201 onAccepted: {
202 if (filenameTextField.text == "") {
203 mobileFileSaveDialog.preventClose = true
204 return
205 }
206 if (!replaceMessage.visible) {
207 if (QGCFileDialogController.fileExists(QGCFileDialogController.fullyQualifiedFilename(folder, filenameTextField.text, _rgExtensions))) {
208 replaceMessage.visible = true
209 mobileFileSaveDialog.preventClose = true
210 return
211 }
212 }
213 _root.acceptedForSave(QGCFileDialogController.fullyQualifiedFilename(folder, filenameTextField.text, _rgExtensions))
214 }
215
216 Column {
217 id: fileSaveColumn
218 width: 40 * ScreenTools.defaultFontPixelWidth
219 spacing: ScreenTools.defaultFontPixelHeight / 2
220
221 RowLayout {
222 anchors.left: parent.left
223 anchors.right: parent.right
224 spacing: ScreenTools.defaultFontPixelWidth
225
226 QGCLabel { text: qsTr("New file name:") }
227
228 QGCTextField {
229 id: filenameTextField
230 Layout.fillWidth: true
231 onTextChanged: replaceMessage.visible = false
232 }
233 }
234
235 QGCLabel {
236 id: replaceMessage
237 anchors.left: parent.left
238 anchors.right: parent.right
239 wrapMode: Text.WordWrap
240 text: qsTr("The file %1 exists. Click Save again to replace it.").arg(filenameTextField.text)
241 visible: false
242 color: qgcPal.warningText
243 }
244
245 SectionHeader {
246 anchors.left: parent.left
247 anchors.right: parent.right
248 text: qsTr("Save to existing file:")
249 }
250
251 Repeater {
252 id: fileRepeater
253 model: QGCFileDialogController.getFiles(folder, [ _rgExtensions ])
254
255 FileButton {
256 id: fileButton
257 anchors.left: parent.left
258 anchors.right: parent.right
259 text: modelData
260
261 onClicked: {
262 mobileFileSaveDialog.close()
263 _root.acceptedForSave(QGCFileDialogController.fullyQualifiedFilename(folder, modelData))
264 }
265
266 onHamburgerClicked: {
267 highlight = true
268 hamburgerMenu.fileToDelete = QGCFileDialogController.fullyQualifiedFilename(folder, modelData)
269 hamburgerMenu.popup()
270 }
271
272 QGCMenu {
273 id: hamburgerMenu
274
275 property string fileToDelete
276
277 onAboutToHide: fileButton.highlight = false
278
279 QGCMenuItem {
280 text: qsTr("Delete")
281 onTriggered: {
282 QGCFileDialogController.deleteFile(hamburgerMenu.fileToDelete)
283 fileRepeater.model = QGCFileDialogController.getFiles(folder, [ _rgExtensions ])
284 }
285 }
286 }
287 }
288 }
289 }
290 }
291 }
292}