Mercurial > hg > svgui
comparison widgets/InteractiveFileFinder.cpp @ 529:3228b7913aa4
* Pull out the widgetry part of FileFinder into widgets/InteractiveFileFinder
(essentially this is just to avoid a dependency from data/fileio to widgets/
which is problematic for non-gui programs)
author | Chris Cannam |
---|---|
date | Fri, 27 Mar 2009 16:25:52 +0000 |
parents | |
children | 1fe7951a61e8 |
comparison
equal
deleted
inserted
replaced
528:44f391ec2172 | 529:3228b7913aa4 |
---|---|
1 /* -*- c-basic-offset: 4 indent-tabs-mode: nil -*- vi:set ts=8 sts=4 sw=4: */ | |
2 | |
3 /* | |
4 Sonic Visualiser | |
5 An audio file viewer and annotation editor. | |
6 Centre for Digital Music, Queen Mary, University of London. | |
7 This file copyright 2007 QMUL. | |
8 | |
9 This program is free software; you can redistribute it and/or | |
10 modify it under the terms of the GNU General Public License as | |
11 published by the Free Software Foundation; either version 2 of the | |
12 License, or (at your option) any later version. See the file | |
13 COPYING included with this distribution for more information. | |
14 */ | |
15 | |
16 #include "InteractiveFileFinder.h" | |
17 #include "data/fileio/FileSource.h" | |
18 #include "data/fileio/AudioFileReaderFactory.h" | |
19 #include "data/fileio/DataFileReaderFactory.h" | |
20 #include "rdf/RDFImporter.h" | |
21 #include "rdf/RDFExporter.h" | |
22 | |
23 #include <QFileInfo> | |
24 #include <QMessageBox> | |
25 #include <QFileDialog> | |
26 #include <QInputDialog> | |
27 #include <QImageReader> | |
28 #include <QSettings> | |
29 | |
30 #include <iostream> | |
31 | |
32 InteractiveFileFinder | |
33 InteractiveFileFinder::m_instance; | |
34 | |
35 InteractiveFileFinder::InteractiveFileFinder() : | |
36 m_lastLocatedLocation("") | |
37 { | |
38 std::cerr << "Registering interactive file finder" << std::endl; | |
39 FileFinder::registerFileFinder(this); | |
40 } | |
41 | |
42 InteractiveFileFinder::~InteractiveFileFinder() | |
43 { | |
44 } | |
45 | |
46 QString | |
47 InteractiveFileFinder::getOpenFileName(FileType type, QString fallbackLocation) | |
48 { | |
49 QString settingsKey; | |
50 QString lastPath = fallbackLocation; | |
51 | |
52 QString title = tr("Select file"); | |
53 QString filter = tr("All files (*.*)"); | |
54 | |
55 switch (type) { | |
56 | |
57 case SessionFile: | |
58 settingsKey = "sessionpath"; | |
59 title = tr("Select a session file"); | |
60 filter = tr("Sonic Visualiser session files (*.sv)\nRDF files (%1)\nAll files (*.*)").arg(RDFImporter::getKnownExtensions()); | |
61 break; | |
62 | |
63 case AudioFile: | |
64 settingsKey = "audiopath"; | |
65 title = "Select an audio file"; | |
66 filter = tr("Audio files (%1)\nAll files (*.*)") | |
67 .arg(AudioFileReaderFactory::getKnownExtensions()); | |
68 break; | |
69 | |
70 case LayerFile: | |
71 settingsKey = "layerpath"; | |
72 filter = tr("All supported files (%1 %2)\nSonic Visualiser Layer XML files (*.svl)\nComma-separated data files (*.csv)\nSpace-separated .lab files (*.lab)\nRDF files (%2)\nMIDI files (*.mid)\nText files (*.txt)\nAll files (*.*)") | |
73 .arg(DataFileReaderFactory::getKnownExtensions()) | |
74 .arg(RDFImporter::getKnownExtensions()); | |
75 break; | |
76 | |
77 case LayerFileNoMidi: | |
78 settingsKey = "layerpath"; | |
79 filter = tr("All supported files (%1 %2)\nSonic Visualiser Layer XML files (*.svl)\nComma-separated data files (*.csv)\nSpace-separated .lab files (*.lab)\nRDF files (%2)\nText files (*.txt)\nAll files (*.*)") | |
80 .arg(DataFileReaderFactory::getKnownExtensions()) | |
81 .arg(RDFImporter::getKnownExtensions()); | |
82 break; | |
83 | |
84 case SessionOrAudioFile: | |
85 settingsKey = "lastpath"; | |
86 filter = tr("All supported files (*.sv %1 %2)\nSonic Visualiser session files (*.sv)\nAudio files (%2)\nRDF files (%1)\nAll files (*.*)") | |
87 .arg(RDFImporter::getKnownExtensions()) | |
88 .arg(AudioFileReaderFactory::getKnownExtensions()); | |
89 break; | |
90 | |
91 case ImageFile: | |
92 settingsKey = "imagepath"; | |
93 { | |
94 QStringList fmts; | |
95 QList<QByteArray> formats = QImageReader::supportedImageFormats(); | |
96 for (QList<QByteArray>::iterator i = formats.begin(); | |
97 i != formats.end(); ++i) { | |
98 fmts.push_back(QString("*.%1") | |
99 .arg(QString::fromLocal8Bit(*i).toLower())); | |
100 } | |
101 filter = tr("Image files (%1)\nAll files (*.*)").arg(fmts.join(" ")); | |
102 } | |
103 break; | |
104 | |
105 case AnyFile: | |
106 settingsKey = "lastpath"; | |
107 filter = tr("All supported files (*.sv %1 %2 %3)\nSonic Visualiser session files (*.sv)\nAudio files (%1)\nLayer files (%2)\nRDF files (%3)\nAll files (*.*)") | |
108 .arg(AudioFileReaderFactory::getKnownExtensions()) | |
109 .arg(DataFileReaderFactory::getKnownExtensions()) | |
110 .arg(RDFImporter::getKnownExtensions()); | |
111 break; | |
112 }; | |
113 | |
114 if (lastPath == "") { | |
115 char *home = getenv("HOME"); | |
116 if (home) lastPath = home; | |
117 else lastPath = "."; | |
118 } else if (QFileInfo(lastPath).isDir()) { | |
119 lastPath = QFileInfo(lastPath).canonicalPath(); | |
120 } else { | |
121 lastPath = QFileInfo(lastPath).absoluteDir().canonicalPath(); | |
122 } | |
123 | |
124 QSettings settings; | |
125 settings.beginGroup("FileFinder"); | |
126 lastPath = settings.value(settingsKey, lastPath).toString(); | |
127 | |
128 QString path = ""; | |
129 | |
130 // Use our own QFileDialog just for symmetry with getSaveFileName below | |
131 | |
132 QFileDialog dialog; | |
133 dialog.setFilters(filter.split('\n')); | |
134 dialog.setWindowTitle(title); | |
135 dialog.setDirectory(lastPath); | |
136 | |
137 dialog.setAcceptMode(QFileDialog::AcceptOpen); | |
138 dialog.setFileMode(QFileDialog::ExistingFile); | |
139 | |
140 if (dialog.exec()) { | |
141 QStringList files = dialog.selectedFiles(); | |
142 if (!files.empty()) path = *files.begin(); | |
143 | |
144 QFileInfo fi(path); | |
145 | |
146 if (!fi.exists()) { | |
147 | |
148 QMessageBox::critical(0, tr("File does not exist"), | |
149 tr("<b>File not found</b><p>File \"%1\" does not exist").arg(path)); | |
150 path = ""; | |
151 | |
152 } else if (!fi.isReadable()) { | |
153 | |
154 QMessageBox::critical(0, tr("File is not readable"), | |
155 tr("<b>File is not readable</b><p>File \"%1\" can not be read").arg(path)); | |
156 path = ""; | |
157 | |
158 } else if (fi.isDir()) { | |
159 | |
160 QMessageBox::critical(0, tr("Directory selected"), | |
161 tr("<b>Directory selected</b><p>File \"%1\" is a directory").arg(path)); | |
162 path = ""; | |
163 | |
164 } else if (!fi.isFile()) { | |
165 | |
166 QMessageBox::critical(0, tr("Non-file selected"), | |
167 tr("<b>Not a file</b><p>Path \"%1\" is not a file").arg(path)); | |
168 path = ""; | |
169 | |
170 } else if (fi.size() == 0) { | |
171 | |
172 QMessageBox::critical(0, tr("File is empty"), | |
173 tr("<b>File is empty</b><p>File \"%1\" is empty").arg(path)); | |
174 path = ""; | |
175 } | |
176 } | |
177 | |
178 if (path != "") { | |
179 settings.setValue(settingsKey, | |
180 QFileInfo(path).absoluteDir().canonicalPath()); | |
181 } | |
182 | |
183 return path; | |
184 } | |
185 | |
186 QString | |
187 InteractiveFileFinder::getSaveFileName(FileType type, QString fallbackLocation) | |
188 { | |
189 QString settingsKey; | |
190 QString lastPath = fallbackLocation; | |
191 | |
192 QString title = tr("Select file"); | |
193 QString filter = tr("All files (*.*)"); | |
194 | |
195 switch (type) { | |
196 | |
197 case SessionFile: | |
198 settingsKey = "savesessionpath"; | |
199 title = tr("Select a session file"); | |
200 filter = tr("Sonic Visualiser session files (*.sv)\nAll files (*.*)"); | |
201 break; | |
202 | |
203 case AudioFile: | |
204 settingsKey = "saveaudiopath"; | |
205 title = "Select an audio file"; | |
206 title = tr("Select a file to export to"); | |
207 filter = tr("WAV audio files (*.wav)\nAll files (*.*)"); | |
208 break; | |
209 | |
210 case LayerFile: | |
211 settingsKey = "savelayerpath"; | |
212 title = tr("Select a file to export to"); | |
213 filter = tr("Sonic Visualiser Layer XML files (*.svl)\nComma-separated data files (*.csv)\nRDF/Turtle files (%1)\nMIDI files (*.mid)\nText files (*.txt)\nAll files (*.*)").arg(RDFExporter::getSupportedExtensions()); | |
214 break; | |
215 | |
216 case LayerFileNoMidi: | |
217 settingsKey = "savelayerpath"; | |
218 title = tr("Select a file to export to"); | |
219 filter = tr("Sonic Visualiser Layer XML files (*.svl)\nComma-separated data files (*.csv)\nRDF/Turtle files (%1)\nText files (*.txt)\nAll files (*.*)").arg(RDFExporter::getSupportedExtensions()); | |
220 break; | |
221 | |
222 case SessionOrAudioFile: | |
223 std::cerr << "ERROR: Internal error: InteractiveFileFinder::getSaveFileName: SessionOrAudioFile cannot be used here" << std::endl; | |
224 abort(); | |
225 | |
226 case ImageFile: | |
227 settingsKey = "saveimagepath"; | |
228 title = tr("Select a file to export to"); | |
229 filter = tr("Portable Network Graphics files (*.png)\nAll files (*.*)"); | |
230 break; | |
231 | |
232 case AnyFile: | |
233 std::cerr << "ERROR: Internal error: InteractiveFileFinder::getSaveFileName: AnyFile cannot be used here" << std::endl; | |
234 abort(); | |
235 }; | |
236 | |
237 if (lastPath == "") { | |
238 char *home = getenv("HOME"); | |
239 if (home) lastPath = home; | |
240 else lastPath = "."; | |
241 } else if (QFileInfo(lastPath).isDir()) { | |
242 lastPath = QFileInfo(lastPath).canonicalPath(); | |
243 } else { | |
244 lastPath = QFileInfo(lastPath).absoluteDir().canonicalPath(); | |
245 } | |
246 | |
247 QSettings settings; | |
248 settings.beginGroup("FileFinder"); | |
249 lastPath = settings.value(settingsKey, lastPath).toString(); | |
250 | |
251 QString path = ""; | |
252 | |
253 // Use our own QFileDialog instead of static functions, as we may | |
254 // need to adjust the file extension based on the selected filter | |
255 | |
256 QFileDialog dialog; | |
257 dialog.setFilters(filter.split('\n')); | |
258 dialog.setWindowTitle(title); | |
259 dialog.setDirectory(lastPath); | |
260 | |
261 dialog.setAcceptMode(QFileDialog::AcceptSave); | |
262 dialog.setFileMode(QFileDialog::AnyFile); | |
263 dialog.setConfirmOverwrite(false); // we'll do that | |
264 | |
265 if (type == SessionFile) { | |
266 dialog.setDefaultSuffix("sv"); | |
267 } else if (type == AudioFile) { | |
268 dialog.setDefaultSuffix("wav"); | |
269 } else if (type == ImageFile) { | |
270 dialog.setDefaultSuffix("png"); | |
271 } | |
272 | |
273 bool good = false; | |
274 | |
275 while (!good) { | |
276 | |
277 path = ""; | |
278 | |
279 if (!dialog.exec()) break; | |
280 | |
281 QStringList files = dialog.selectedFiles(); | |
282 if (files.empty()) break; | |
283 path = *files.begin(); | |
284 | |
285 QFileInfo fi(path); | |
286 | |
287 std::cerr << "type = " << type << ", suffix = " << fi.suffix().toStdString() << std::endl; | |
288 | |
289 if ((type == LayerFile || type == LayerFileNoMidi) | |
290 && fi.suffix() == "") { | |
291 QString expectedExtension; | |
292 QString selectedFilter = dialog.selectedFilter(); | |
293 if (selectedFilter.contains(".svl")) { | |
294 expectedExtension = "svl"; | |
295 } else if (selectedFilter.contains(".txt")) { | |
296 expectedExtension = "txt"; | |
297 } else if (selectedFilter.contains(".csv")) { | |
298 expectedExtension = "csv"; | |
299 } else if (selectedFilter.contains(".mid")) { | |
300 expectedExtension = "mid"; | |
301 } else if (selectedFilter.contains(".ttl")) { | |
302 expectedExtension = "ttl"; | |
303 } | |
304 std::cerr << "expected extension = " << expectedExtension.toStdString() << std::endl; | |
305 if (expectedExtension != "") { | |
306 path = QString("%1.%2").arg(path).arg(expectedExtension); | |
307 fi = QFileInfo(path); | |
308 } | |
309 } | |
310 | |
311 if (fi.isDir()) { | |
312 QMessageBox::critical(0, tr("Directory selected"), | |
313 tr("<b>Directory selected</b><p>File \"%1\" is a directory").arg(path)); | |
314 continue; | |
315 } | |
316 | |
317 if (fi.exists()) { | |
318 if (QMessageBox::question(0, tr("File exists"), | |
319 tr("<b>File exists</b><p>The file \"%1\" already exists.\nDo you want to overwrite it?").arg(path), | |
320 QMessageBox::Ok, | |
321 QMessageBox::Cancel) != QMessageBox::Ok) { | |
322 continue; | |
323 } | |
324 } | |
325 | |
326 good = true; | |
327 } | |
328 | |
329 if (path != "") { | |
330 settings.setValue(settingsKey, | |
331 QFileInfo(path).absoluteDir().canonicalPath()); | |
332 } | |
333 | |
334 return path; | |
335 } | |
336 | |
337 void | |
338 InteractiveFileFinder::registerLastOpenedFilePath(FileType type, QString path) | |
339 { | |
340 QString settingsKey; | |
341 | |
342 switch (type) { | |
343 case SessionFile: | |
344 settingsKey = "sessionpath"; | |
345 break; | |
346 | |
347 case AudioFile: | |
348 settingsKey = "audiopath"; | |
349 break; | |
350 | |
351 case LayerFile: | |
352 settingsKey = "layerpath"; | |
353 break; | |
354 | |
355 case LayerFileNoMidi: | |
356 settingsKey = "layerpath"; | |
357 break; | |
358 | |
359 case SessionOrAudioFile: | |
360 settingsKey = "lastpath"; | |
361 break; | |
362 | |
363 case ImageFile: | |
364 settingsKey = "imagepath"; | |
365 break; | |
366 | |
367 case AnyFile: | |
368 settingsKey = "lastpath"; | |
369 break; | |
370 } | |
371 | |
372 if (path != "") { | |
373 QSettings settings; | |
374 settings.beginGroup("FileFinder"); | |
375 path = QFileInfo(path).absoluteDir().canonicalPath(); | |
376 settings.setValue(settingsKey, path); | |
377 settings.setValue("lastpath", path); | |
378 } | |
379 } | |
380 | |
381 QString | |
382 InteractiveFileFinder::find(FileType type, QString location, QString lastKnownLocation) | |
383 { | |
384 if (FileSource::canHandleScheme(location)) { | |
385 if (FileSource(location).isAvailable()) { | |
386 std::cerr << "InteractiveFileFinder::find: ok, it's available... returning" << std::endl; | |
387 return location; | |
388 } | |
389 } | |
390 | |
391 if (QFileInfo(location).exists()) return location; | |
392 | |
393 QString foundAt = ""; | |
394 | |
395 if ((foundAt = findRelative(location, lastKnownLocation)) != "") { | |
396 return foundAt; | |
397 } | |
398 | |
399 if ((foundAt = findRelative(location, m_lastLocatedLocation)) != "") { | |
400 return foundAt; | |
401 } | |
402 | |
403 return locateInteractive(type, location); | |
404 } | |
405 | |
406 QString | |
407 InteractiveFileFinder::findRelative(QString location, QString relativeTo) | |
408 { | |
409 if (relativeTo == "") return ""; | |
410 | |
411 std::cerr << "Looking for \"" << location.toStdString() << "\" next to \"" | |
412 << relativeTo.toStdString() << "\"..." << std::endl; | |
413 | |
414 QString fileName; | |
415 QString resolved; | |
416 | |
417 if (FileSource::isRemote(location)) { | |
418 fileName = QUrl(location).path().section('/', -1, -1, | |
419 QString::SectionSkipEmpty); | |
420 } else { | |
421 if (QUrl(location).scheme() == "file") { | |
422 location = QUrl(location).toLocalFile(); | |
423 } | |
424 fileName = QFileInfo(location).fileName(); | |
425 } | |
426 | |
427 if (FileSource::isRemote(relativeTo)) { | |
428 resolved = QUrl(relativeTo).resolved(fileName).toString(); | |
429 if (!FileSource(resolved).isAvailable()) resolved = ""; | |
430 std::cerr << "resolved: " << resolved.toStdString() << std::endl; | |
431 } else { | |
432 if (QUrl(relativeTo).scheme() == "file") { | |
433 relativeTo = QUrl(relativeTo).toLocalFile(); | |
434 } | |
435 resolved = QFileInfo(relativeTo).dir().filePath(fileName); | |
436 if (!QFileInfo(resolved).exists() || | |
437 !QFileInfo(resolved).isFile() || | |
438 !QFileInfo(resolved).isReadable()) { | |
439 resolved = ""; | |
440 } | |
441 } | |
442 | |
443 return resolved; | |
444 } | |
445 | |
446 QString | |
447 InteractiveFileFinder::locateInteractive(FileType type, QString thing) | |
448 { | |
449 QString question; | |
450 if (type == AudioFile) { | |
451 question = tr("<b>File not found</b><p>Audio file \"%1\" could not be opened.\nDo you want to locate it?"); | |
452 } else { | |
453 question = tr("<b>File not found</b><p>File \"%1\" could not be opened.\nDo you want to locate it?"); | |
454 } | |
455 | |
456 QString path = ""; | |
457 bool done = false; | |
458 | |
459 while (!done) { | |
460 | |
461 int rv = QMessageBox::question | |
462 (0, | |
463 tr("Failed to open file"), | |
464 question.arg(thing), | |
465 tr("Locate file..."), | |
466 tr("Use URL..."), | |
467 tr("Cancel"), | |
468 0, 2); | |
469 | |
470 switch (rv) { | |
471 | |
472 case 0: // Locate file | |
473 | |
474 if (QFileInfo(thing).dir().exists()) { | |
475 path = QFileInfo(thing).dir().canonicalPath(); | |
476 } | |
477 | |
478 path = getOpenFileName(type, path); | |
479 done = (path != ""); | |
480 break; | |
481 | |
482 case 1: // Use URL | |
483 { | |
484 bool ok = false; | |
485 path = QInputDialog::getText | |
486 (0, tr("Use URL"), | |
487 tr("Please enter the URL to use for this file:"), | |
488 QLineEdit::Normal, "", &ok); | |
489 | |
490 if (ok && path != "") { | |
491 if (FileSource(path).isAvailable()) { | |
492 done = true; | |
493 } else { | |
494 QMessageBox::critical | |
495 (0, tr("Failed to open location"), | |
496 tr("<b>Failed to open location</b><p>URL \"%1\" could not be opened").arg(path)); | |
497 path = ""; | |
498 } | |
499 } | |
500 break; | |
501 } | |
502 | |
503 case 2: // Cancel | |
504 path = ""; | |
505 done = true; | |
506 break; | |
507 } | |
508 } | |
509 | |
510 if (path != "") m_lastLocatedLocation = path; | |
511 return path; | |
512 } | |
513 | |
514 |