comparison data/fileio/FileFinder.cpp @ 0:fc9323a41f5a

start base : Sonic Visualiser sv1-1.0rc1
author lbajardsilogic
date Fri, 11 May 2007 09:08:14 +0000
parents
children 00b5875d8c30
comparison
equal deleted inserted replaced
-1:000000000000 0:fc9323a41f5a
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 "FileFinder.h"
17 #include "RemoteFile.h"
18 #include "AudioFileReaderFactory.h"
19 #include "DataFileReaderFactory.h"
20
21 #include <QFileInfo>
22 #include <QMessageBox>
23 #include <QFileDialog>
24 #include <QInputDialog>
25 #include <QSettings>
26
27 #include <iostream>
28
29 FileFinder *
30 FileFinder::m_instance = 0;
31
32 FileFinder::FileFinder() :
33 m_lastLocatedLocation("")
34 {
35 }
36
37 FileFinder::~FileFinder()
38 {
39 }
40
41 FileFinder *
42 FileFinder::getInstance()
43 {
44 if (m_instance == 0) {
45 m_instance = new FileFinder();
46 }
47 return m_instance;
48 }
49
50 QString
51 FileFinder::getOpenFileName(FileType type, QString fallbackLocation)
52 {
53 QString settingsKey;
54 QString lastPath = fallbackLocation;
55
56 QString title = tr("Select file");
57 QString filter = tr("All files (*.*)");
58
59 switch (type) {
60
61 case SessionFile:
62 settingsKey = "sessionpath";
63 title = tr("Select a session file");
64 filter = tr("Sonic Visualiser session files (*.sv)\nAll files (*.*)");
65 break;
66
67 case AudioFile:
68 settingsKey = "audiopath";
69 title = "Select an audio file";
70 filter = tr("Audio files (%1)\nAll files (*.*)")
71 .arg(AudioFileReaderFactory::getKnownExtensions());
72 break;
73
74 case LayerFile:
75 settingsKey = "layerpath";
76 filter = tr("All supported files (%1)\nSonic Visualiser Layer XML files (*.svl)\nComma-separated data files (*.csv)\nSpace-separated .lab files (*.lab)\nMIDI files (*.mid)\nText files (*.txt)\nAll files (*.*)").arg(DataFileReaderFactory::getKnownExtensions());
77 break;
78
79 case SessionOrAudioFile:
80 settingsKey = "lastpath";
81 filter = tr("All supported files (*.sv %1)\nSonic Visualiser session files (*.sv)\nAudio files (%1)\nAll files (*.*)")
82 .arg(AudioFileReaderFactory::getKnownExtensions());
83 break;
84
85 case ImageFile:
86 settingsKey = "imagepath";
87 filter = tr("Portable Network Graphics files (*.png)\nAll files (*.*)");
88 break;
89
90 case AnyFile:
91 settingsKey = "lastpath";
92 filter = tr("All supported files (*.sv %1 %2)\nSonic Visualiser session files (*.sv)\nAudio files (%1)\nLayer files (%2)\nAll files (*.*)")
93 .arg(AudioFileReaderFactory::getKnownExtensions())
94 .arg(DataFileReaderFactory::getKnownExtensions());
95 break;
96 };
97
98 if (lastPath == "") {
99 char *home = getenv("HOME");
100 if (home) lastPath = home;
101 else lastPath = ".";
102 } else if (QFileInfo(lastPath).isDir()) {
103 lastPath = QFileInfo(lastPath).canonicalPath();
104 } else {
105 lastPath = QFileInfo(lastPath).absoluteDir().canonicalPath();
106 }
107
108 QSettings settings;
109 settings.beginGroup("FileFinder");
110 lastPath = settings.value(settingsKey, lastPath).toString();
111
112 QString path = "";
113
114 // Use our own QFileDialog just for symmetry with getSaveFileName below
115
116 QFileDialog dialog;
117 dialog.setFilters(filter.split('\n'));
118 dialog.setWindowTitle(title);
119 dialog.setDirectory(lastPath);
120
121 dialog.setAcceptMode(QFileDialog::AcceptOpen);
122 dialog.setFileMode(QFileDialog::ExistingFile);
123
124 if (dialog.exec()) {
125 QStringList files = dialog.selectedFiles();
126 if (!files.empty()) path = *files.begin();
127
128 QFileInfo fi(path);
129
130 if (!fi.exists()) {
131
132 QMessageBox::critical(0, tr("File does not exist"),
133 tr("File \"%1\" does not exist").arg(path));
134 path = "";
135
136 } else if (!fi.isReadable()) {
137
138 QMessageBox::critical(0, tr("File is not readable"),
139 tr("File \"%1\" can not be read").arg(path));
140 path = "";
141
142 } else if (fi.isDir()) {
143
144 QMessageBox::critical(0, tr("Directory selected"),
145 tr("File \"%1\" is a directory").arg(path));
146 path = "";
147
148 } else if (!fi.isFile()) {
149
150 QMessageBox::critical(0, tr("Non-file selected"),
151 tr("Path \"%1\" is not a file").arg(path));
152 path = "";
153
154 } else if (fi.size() == 0) {
155
156 QMessageBox::critical(0, tr("File is empty"),
157 tr("File \"%1\" is empty").arg(path));
158 path = "";
159 }
160 }
161
162 if (path != "") {
163 settings.setValue(settingsKey,
164 QFileInfo(path).absoluteDir().canonicalPath());
165 }
166
167 return path;
168 }
169
170 QString
171 FileFinder::getSaveFileName(FileType type, QString fallbackLocation)
172 {
173 QString settingsKey;
174 QString lastPath = fallbackLocation;
175
176 QString title = tr("Select file");
177 QString filter = tr("All files (*.*)");
178
179 switch (type) {
180
181 case SessionFile:
182 settingsKey = "savesessionpath";
183 title = tr("Select a session file");
184 filter = tr("Sonic Visualiser session files (*.sv)\nAll files (*.*)");
185 break;
186
187 case AudioFile:
188 settingsKey = "saveaudiopath";
189 title = "Select an audio file";
190 title = tr("Select a file to export to");
191 filter = tr("WAV audio files (*.wav)\nAll files (*.*)");
192 break;
193
194 case LayerFile:
195 settingsKey = "savelayerpath";
196 title = tr("Select a file to export to");
197 filter = tr("Sonic Visualiser Layer XML files (*.svl)\nComma-separated data files (*.csv)\nText files (*.txt)\nAll files (*.*)");
198 break;
199
200 case SessionOrAudioFile:
201 std::cerr << "ERROR: Internal error: FileFinder::getSaveFileName: SessionOrAudioFile cannot be used here" << std::endl;
202 abort();
203
204 case ImageFile:
205 settingsKey = "saveimagepath";
206 title = tr("Select a file to export to");
207 filter = tr("Portable Network Graphics files (*.png)\nAll files (*.*)");
208 break;
209
210 case AnyFile:
211 std::cerr << "ERROR: Internal error: FileFinder::getSaveFileName: AnyFile cannot be used here" << std::endl;
212 abort();
213 };
214
215 if (lastPath == "") {
216 char *home = getenv("HOME");
217 if (home) lastPath = home;
218 else lastPath = ".";
219 } else if (QFileInfo(lastPath).isDir()) {
220 lastPath = QFileInfo(lastPath).canonicalPath();
221 } else {
222 lastPath = QFileInfo(lastPath).absoluteDir().canonicalPath();
223 }
224
225 QSettings settings;
226 settings.beginGroup("FileFinder");
227 lastPath = settings.value(settingsKey, lastPath).toString();
228
229 QString path = "";
230
231 // Use our own QFileDialog instead of static functions, as we may
232 // need to adjust the file extension based on the selected filter
233
234 QFileDialog dialog;
235 dialog.setFilters(filter.split('\n'));
236 dialog.setWindowTitle(title);
237 dialog.setDirectory(lastPath);
238
239 dialog.setAcceptMode(QFileDialog::AcceptSave);
240 dialog.setFileMode(QFileDialog::AnyFile);
241 dialog.setConfirmOverwrite(false); // we'll do that
242
243 if (type == SessionFile) {
244 dialog.setDefaultSuffix("sv");
245 } else if (type == AudioFile) {
246 dialog.setDefaultSuffix("wav");
247 } else if (type == ImageFile) {
248 dialog.setDefaultSuffix("png");
249 }
250
251 bool good = false;
252
253 while (!good) {
254
255 path = "";
256
257 if (!dialog.exec()) break;
258
259 QStringList files = dialog.selectedFiles();
260 if (files.empty()) break;
261 path = *files.begin();
262
263 QFileInfo fi(path);
264
265 if (type == LayerFile && fi.suffix() == "") {
266 QString expectedExtension;
267 QString selectedFilter = dialog.selectedFilter();
268 if (selectedFilter.contains(".svl")) {
269 expectedExtension = "svl";
270 } else if (selectedFilter.contains(".txt")) {
271 expectedExtension = "txt";
272 } else if (selectedFilter.contains(".csv")) {
273 expectedExtension = "csv";
274 }
275 if (expectedExtension != "") {
276 path = QString("%1.%2").arg(path).arg(expectedExtension);
277 fi = QFileInfo(path);
278 }
279 }
280
281 if (fi.isDir()) {
282 QMessageBox::critical(0, tr("Directory selected"),
283 tr("File \"%1\" is a directory").arg(path));
284 continue;
285 }
286
287 if (fi.exists()) {
288 if (QMessageBox::question(0, tr("File exists"),
289 tr("The file \"%1\" already exists.\nDo you want to overwrite it?").arg(path),
290 QMessageBox::Ok,
291 QMessageBox::Cancel) != QMessageBox::Ok) {
292 continue;
293 }
294 }
295
296 good = true;
297 }
298
299 if (path != "") {
300 settings.setValue(settingsKey,
301 QFileInfo(path).absoluteDir().canonicalPath());
302 }
303
304 return path;
305 }
306
307 void
308 FileFinder::registerLastOpenedFilePath(FileType type, QString path)
309 {
310 QString settingsKey;
311
312 switch (type) {
313 case SessionFile:
314 settingsKey = "sessionpath";
315 break;
316
317 case AudioFile:
318 settingsKey = "audiopath";
319 break;
320
321 case LayerFile:
322 settingsKey = "layerpath";
323 break;
324
325 case SessionOrAudioFile:
326 settingsKey = "lastpath";
327 break;
328
329 case ImageFile:
330 settingsKey = "imagepath";
331 break;
332
333 case AnyFile:
334 settingsKey = "lastpath";
335 break;
336 }
337
338 if (path != "") {
339 QSettings settings;
340 settings.beginGroup("FileFinder");
341 path = QFileInfo(path).absoluteDir().canonicalPath();
342 settings.setValue(settingsKey, path);
343 settings.setValue("lastpath", path);
344 }
345 }
346
347 QString
348 FileFinder::find(FileType type, QString location, QString lastKnownLocation)
349 {
350 if (QFileInfo(location).exists()) return location;
351
352 if (RemoteFile::canHandleScheme(QUrl(location))) {
353 RemoteFile rf(location);
354 bool available = rf.isAvailable();
355 rf.deleteLocalFile();
356 if (available) return location;
357 }
358
359 QString foundAt = "";
360
361 if ((foundAt = findRelative(location, lastKnownLocation)) != "") {
362 return foundAt;
363 }
364
365 if ((foundAt = findRelative(location, m_lastLocatedLocation)) != "") {
366 return foundAt;
367 }
368
369 return locateInteractive(type, location);
370 }
371
372 QString
373 FileFinder::findRelative(QString location, QString relativeTo)
374 {
375 if (relativeTo == "") return "";
376
377 std::cerr << "Looking for \"" << location.toStdString() << "\" next to \""
378 << relativeTo.toStdString() << "\"..." << std::endl;
379
380 QString fileName;
381 QString resolved;
382
383 if (RemoteFile::canHandleScheme(QUrl(location))) {
384 fileName = QUrl(location).path().section('/', -1, -1,
385 QString::SectionSkipEmpty);
386 } else {
387 fileName = QFileInfo(location).fileName();
388 }
389
390 if (RemoteFile::canHandleScheme(QUrl(relativeTo))) {
391 resolved = QUrl(relativeTo).resolved(fileName).toString();
392 RemoteFile rf(resolved);
393 if (!rf.isAvailable()) resolved = "";
394 std::cerr << "resolved: " << resolved.toStdString() << std::endl;
395 rf.deleteLocalFile();
396 } else {
397 resolved = QFileInfo(relativeTo).dir().filePath(fileName);
398 if (!QFileInfo(resolved).exists() ||
399 !QFileInfo(resolved).isFile() ||
400 !QFileInfo(resolved).isReadable()) {
401 resolved = "";
402 }
403 }
404
405 return resolved;
406 }
407
408 QString
409 FileFinder::locateInteractive(FileType type, QString thing)
410 {
411 QString question;
412 if (type == AudioFile) {
413 question = tr("Audio file \"%1\" could not be opened.\nDo you want to locate it?");
414 } else {
415 question = tr("File \"%1\" could not be opened.\nDo you want to locate it?");
416 }
417
418 QString path = "";
419 bool done = false;
420
421 while (!done) {
422
423 int rv = QMessageBox::question
424 (0,
425 tr("Failed to open file"),
426 question.arg(thing),
427 tr("Locate file..."),
428 tr("Use URL..."),
429 tr("Cancel"),
430 0, 2);
431
432 switch (rv) {
433
434 case 0: // Locate file
435
436 if (QFileInfo(thing).dir().exists()) {
437 path = QFileInfo(thing).dir().canonicalPath();
438 }
439
440 path = getOpenFileName(type, path);
441 done = (path != "");
442 break;
443
444 case 1: // Use URL
445 {
446 bool ok = false;
447 path = QInputDialog::getText
448 (0, tr("Use URL"),
449 tr("Please enter the URL to use for this file:"),
450 QLineEdit::Normal, "", &ok);
451
452 if (ok && path != "") {
453 RemoteFile rf(path);
454 if (rf.isAvailable()) {
455 done = true;
456 } else {
457 QMessageBox::critical
458 (0, tr("Failed to open location"),
459 tr("URL \"%1\" could not be opened").arg(path));
460 path = "";
461 }
462 rf.deleteLocalFile();
463 }
464 break;
465 }
466
467 case 2: // Cancel
468 path = "";
469 done = true;
470 break;
471 }
472 }
473
474 if (path != "") m_lastLocatedLocation = path;
475 return path;
476 }
477
478