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