annotate data/fileio/FileFinder.cpp @ 228:f02989f7e160

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