| 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@587 | 38     SVDEBUG << "Registering interactive file finder" << 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@672 | 105     case CSVFile: | 
| Chris@672 | 106         settingsKey = "layerpath"; | 
| Chris@672 | 107         filter = tr("Comma-separated data files (*.csv)\nSpace-separated .lab files (*.lab)\nText files (*.txt)\nAll files (*.*)"); | 
| Chris@672 | 108         break; | 
| Chris@672 | 109 | 
| alo@994 | 110     case IMAFile: | 
| alo@994 | 111         settingsKey = "imafpath"; | 
| alo@994 | 112         title = "Select an IMAF file"; | 
| alo@994 | 113         filter = tr("IMAF files (*.ima)\nAll files (*.*)"); | 
| alo@994 | 114         break; | 
| alo@994 | 115 | 
| Chris@529 | 116     case AnyFile: | 
| Chris@529 | 117         settingsKey = "lastpath"; | 
| Chris@529 | 118         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 | 119             .arg(AudioFileReaderFactory::getKnownExtensions()) | 
| Chris@529 | 120             .arg(DataFileReaderFactory::getKnownExtensions()) | 
| Chris@529 | 121             .arg(RDFImporter::getKnownExtensions()); | 
| Chris@529 | 122         break; | 
| Chris@529 | 123     }; | 
| Chris@529 | 124 | 
| Chris@529 | 125     if (lastPath == "") { | 
| Chris@529 | 126         char *home = getenv("HOME"); | 
| Chris@529 | 127         if (home) lastPath = home; | 
| Chris@529 | 128         else lastPath = "."; | 
| Chris@529 | 129     } else if (QFileInfo(lastPath).isDir()) { | 
| Chris@529 | 130         lastPath = QFileInfo(lastPath).canonicalPath(); | 
| Chris@529 | 131     } else { | 
| Chris@529 | 132         lastPath = QFileInfo(lastPath).absoluteDir().canonicalPath(); | 
| Chris@529 | 133     } | 
| Chris@529 | 134 | 
| Chris@529 | 135     QSettings settings; | 
| Chris@529 | 136     settings.beginGroup("FileFinder"); | 
| Chris@529 | 137     lastPath = settings.value(settingsKey, lastPath).toString(); | 
| Chris@529 | 138 | 
| Chris@529 | 139     QString path = ""; | 
| Chris@529 | 140 | 
| Chris@529 | 141     // Use our own QFileDialog just for symmetry with getSaveFileName below | 
| Chris@529 | 142 | 
| Chris@529 | 143     QFileDialog dialog; | 
| Chris@616 | 144     dialog.setNameFilters(filter.split('\n')); | 
| Chris@529 | 145     dialog.setWindowTitle(title); | 
| Chris@529 | 146     dialog.setDirectory(lastPath); | 
| Chris@529 | 147 | 
| Chris@529 | 148     dialog.setAcceptMode(QFileDialog::AcceptOpen); | 
| Chris@529 | 149     dialog.setFileMode(QFileDialog::ExistingFile); | 
| Chris@529 | 150 | 
| Chris@529 | 151     if (dialog.exec()) { | 
| Chris@529 | 152         QStringList files = dialog.selectedFiles(); | 
| Chris@529 | 153         if (!files.empty()) path = *files.begin(); | 
| Chris@529 | 154 | 
| Chris@529 | 155         QFileInfo fi(path); | 
| Chris@529 | 156 | 
| Chris@529 | 157         if (!fi.exists()) { | 
| Chris@529 | 158 | 
| Chris@529 | 159             QMessageBox::critical(0, tr("File does not exist"), | 
| Chris@529 | 160                                   tr("<b>File not found</b><p>File \"%1\" does not exist").arg(path)); | 
| Chris@529 | 161             path = ""; | 
| Chris@529 | 162 | 
| Chris@529 | 163         } else if (!fi.isReadable()) { | 
| Chris@529 | 164 | 
| Chris@529 | 165             QMessageBox::critical(0, tr("File is not readable"), | 
| Chris@529 | 166                                   tr("<b>File is not readable</b><p>File \"%1\" can not be read").arg(path)); | 
| Chris@529 | 167             path = ""; | 
| Chris@529 | 168 | 
| Chris@529 | 169         } else if (fi.isDir()) { | 
| Chris@529 | 170 | 
| Chris@529 | 171             QMessageBox::critical(0, tr("Directory selected"), | 
| Chris@529 | 172                                   tr("<b>Directory selected</b><p>File \"%1\" is a directory").arg(path)); | 
| Chris@529 | 173             path = ""; | 
| Chris@529 | 174 | 
| Chris@529 | 175         } else if (!fi.isFile()) { | 
| Chris@529 | 176 | 
| Chris@529 | 177             QMessageBox::critical(0, tr("Non-file selected"), | 
| Chris@529 | 178                                   tr("<b>Not a file</b><p>Path \"%1\" is not a file").arg(path)); | 
| Chris@529 | 179             path = ""; | 
| Chris@529 | 180 | 
| Chris@529 | 181         } else if (fi.size() == 0) { | 
| Chris@529 | 182 | 
| Chris@529 | 183             QMessageBox::critical(0, tr("File is empty"), | 
| Chris@529 | 184                                   tr("<b>File is empty</b><p>File \"%1\" is empty").arg(path)); | 
| Chris@529 | 185             path = ""; | 
| Chris@529 | 186         } | 
| Chris@529 | 187     } | 
| Chris@529 | 188 | 
| Chris@529 | 189     if (path != "") { | 
| Chris@529 | 190         settings.setValue(settingsKey, | 
| Chris@529 | 191                           QFileInfo(path).absoluteDir().canonicalPath()); | 
| Chris@529 | 192     } | 
| Chris@529 | 193 | 
| Chris@529 | 194     return path; | 
| Chris@529 | 195 } | 
| Chris@529 | 196 | 
| Chris@529 | 197 QString | 
| Chris@529 | 198 InteractiveFileFinder::getSaveFileName(FileType type, QString fallbackLocation) | 
| Chris@529 | 199 { | 
| Chris@529 | 200     QString settingsKey; | 
| Chris@529 | 201     QString lastPath = fallbackLocation; | 
| Chris@529 | 202 | 
| Chris@529 | 203     QString title = tr("Select file"); | 
| Chris@529 | 204     QString filter = tr("All files (*.*)"); | 
| Chris@529 | 205 | 
| Chris@529 | 206     switch (type) { | 
| Chris@529 | 207 | 
| Chris@529 | 208     case SessionFile: | 
| Chris@529 | 209         settingsKey = "savesessionpath"; | 
| Chris@529 | 210         title = tr("Select a session file"); | 
| Chris@529 | 211         filter = tr("Sonic Visualiser session files (*.sv)\nAll files (*.*)"); | 
| Chris@529 | 212         break; | 
| Chris@529 | 213 | 
| Chris@529 | 214     case AudioFile: | 
| Chris@529 | 215         settingsKey = "saveaudiopath"; | 
| Chris@529 | 216         title = "Select an audio file"; | 
| Chris@529 | 217         title = tr("Select a file to export to"); | 
| Chris@529 | 218         filter = tr("WAV audio files (*.wav)\nAll files (*.*)"); | 
| Chris@529 | 219         break; | 
| Chris@529 | 220 | 
| Chris@529 | 221     case LayerFile: | 
| Chris@529 | 222         settingsKey = "savelayerpath"; | 
| Chris@529 | 223         title = tr("Select a file to export to"); | 
| Chris@529 | 224         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 | 225         break; | 
| Chris@529 | 226 | 
| Chris@529 | 227     case LayerFileNoMidi: | 
| Chris@529 | 228         settingsKey = "savelayerpath"; | 
| Chris@529 | 229         title = tr("Select a file to export to"); | 
| Chris@529 | 230         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 | 231         break; | 
| Chris@529 | 232 | 
| Chris@529 | 233     case SessionOrAudioFile: | 
| Chris@682 | 234         cerr << "ERROR: Internal error: InteractiveFileFinder::getSaveFileName: SessionOrAudioFile cannot be used here" << endl; | 
| Chris@529 | 235         abort(); | 
| Chris@529 | 236 | 
| Chris@529 | 237     case ImageFile: | 
| Chris@529 | 238         settingsKey = "saveimagepath"; | 
| Chris@529 | 239         title = tr("Select a file to export to"); | 
| Chris@529 | 240         filter = tr("Portable Network Graphics files (*.png)\nAll files (*.*)"); | 
| Chris@529 | 241         break; | 
| Chris@529 | 242 | 
| Chris@672 | 243     case CSVFile: | 
| Chris@672 | 244         settingsKey = "savelayerpath"; | 
| Chris@672 | 245         title = tr("Select a file to export to"); | 
| Chris@672 | 246         filter = tr("Comma-separated data files (*.csv)\nText files (*.txt)\nAll files (*.*)"); | 
| Chris@672 | 247         break; | 
| Chris@672 | 248 | 
| Chris@529 | 249     case AnyFile: | 
| Chris@682 | 250         cerr << "ERROR: Internal error: InteractiveFileFinder::getSaveFileName: AnyFile cannot be used here" << endl; | 
| Chris@529 | 251         abort(); | 
| Chris@529 | 252     }; | 
| Chris@529 | 253 | 
| Chris@529 | 254     if (lastPath == "") { | 
| Chris@529 | 255         char *home = getenv("HOME"); | 
| Chris@529 | 256         if (home) lastPath = home; | 
| Chris@529 | 257         else lastPath = "."; | 
| Chris@529 | 258     } else if (QFileInfo(lastPath).isDir()) { | 
| Chris@529 | 259         lastPath = QFileInfo(lastPath).canonicalPath(); | 
| Chris@529 | 260     } else { | 
| Chris@529 | 261         lastPath = QFileInfo(lastPath).absoluteDir().canonicalPath(); | 
| Chris@529 | 262     } | 
| Chris@529 | 263 | 
| Chris@529 | 264     QSettings settings; | 
| Chris@529 | 265     settings.beginGroup("FileFinder"); | 
| Chris@529 | 266     lastPath = settings.value(settingsKey, lastPath).toString(); | 
| Chris@529 | 267 | 
| Chris@529 | 268     QString path = ""; | 
| Chris@529 | 269 | 
| Chris@529 | 270     // Use our own QFileDialog instead of static functions, as we may | 
| Chris@529 | 271     // need to adjust the file extension based on the selected filter | 
| Chris@529 | 272 | 
| Chris@529 | 273     QFileDialog dialog; | 
| Chris@616 | 274     dialog.setNameFilters(filter.split('\n')); | 
| Chris@529 | 275     dialog.setWindowTitle(title); | 
| Chris@529 | 276     dialog.setDirectory(lastPath); | 
| Chris@529 | 277 | 
| Chris@529 | 278     dialog.setAcceptMode(QFileDialog::AcceptSave); | 
| Chris@529 | 279     dialog.setFileMode(QFileDialog::AnyFile); | 
| Chris@529 | 280     dialog.setConfirmOverwrite(false); // we'll do that | 
| Chris@529 | 281 | 
| Chris@529 | 282     if (type == SessionFile) { | 
| Chris@529 | 283         dialog.setDefaultSuffix("sv"); | 
| Chris@529 | 284     } else if (type == AudioFile) { | 
| Chris@529 | 285         dialog.setDefaultSuffix("wav"); | 
| Chris@529 | 286     } else if (type == ImageFile) { | 
| Chris@529 | 287         dialog.setDefaultSuffix("png"); | 
| Chris@672 | 288     } else if (type == CSVFile) { | 
| Chris@672 | 289         dialog.setDefaultSuffix("csv"); | 
| Chris@529 | 290     } | 
| Chris@529 | 291 | 
| Chris@529 | 292     bool good = false; | 
| Chris@529 | 293 | 
| Chris@529 | 294     while (!good) { | 
| Chris@529 | 295 | 
| Chris@529 | 296         path = ""; | 
| Chris@529 | 297 | 
| Chris@529 | 298         if (!dialog.exec()) break; | 
| Chris@529 | 299 | 
| Chris@529 | 300         QStringList files = dialog.selectedFiles(); | 
| Chris@529 | 301         if (files.empty()) break; | 
| Chris@529 | 302         path = *files.begin(); | 
| Chris@529 | 303 | 
| Chris@529 | 304         QFileInfo fi(path); | 
| Chris@529 | 305 | 
| Chris@682 | 306         cerr << "type = " << type << ", suffix = " << fi.suffix() << endl; | 
| Chris@529 | 307 | 
| Chris@529 | 308         if ((type == LayerFile || type == LayerFileNoMidi) | 
| Chris@529 | 309             && fi.suffix() == "") { | 
| Chris@529 | 310             QString expectedExtension; | 
| Chris@616 | 311             QString selectedFilter = dialog.selectedNameFilter(); | 
| Chris@529 | 312             if (selectedFilter.contains(".svl")) { | 
| Chris@529 | 313                 expectedExtension = "svl"; | 
| Chris@529 | 314             } else if (selectedFilter.contains(".txt")) { | 
| Chris@529 | 315                 expectedExtension = "txt"; | 
| Chris@529 | 316             } else if (selectedFilter.contains(".csv")) { | 
| Chris@529 | 317                 expectedExtension = "csv"; | 
| Chris@529 | 318             } else if (selectedFilter.contains(".mid")) { | 
| Chris@529 | 319                 expectedExtension = "mid"; | 
| Chris@529 | 320             } else if (selectedFilter.contains(".ttl")) { | 
| Chris@529 | 321                 expectedExtension = "ttl"; | 
| Chris@529 | 322             } | 
| Chris@682 | 323             cerr << "expected extension = " << expectedExtension << endl; | 
| Chris@529 | 324             if (expectedExtension != "") { | 
| Chris@529 | 325                 path = QString("%1.%2").arg(path).arg(expectedExtension); | 
| Chris@529 | 326                 fi = QFileInfo(path); | 
| Chris@529 | 327             } | 
| Chris@529 | 328         } | 
| Chris@529 | 329 | 
| Chris@529 | 330         if (fi.isDir()) { | 
| Chris@529 | 331             QMessageBox::critical(0, tr("Directory selected"), | 
| Chris@529 | 332                                   tr("<b>Directory selected</b><p>File \"%1\" is a directory").arg(path)); | 
| Chris@529 | 333             continue; | 
| Chris@529 | 334         } | 
| Chris@529 | 335 | 
| Chris@529 | 336         if (fi.exists()) { | 
| Chris@529 | 337             if (QMessageBox::question(0, tr("File exists"), | 
| Chris@529 | 338                                       tr("<b>File exists</b><p>The file \"%1\" already exists.\nDo you want to overwrite it?").arg(path), | 
| Chris@529 | 339                                       QMessageBox::Ok, | 
| Chris@529 | 340                                       QMessageBox::Cancel) != QMessageBox::Ok) { | 
| Chris@529 | 341                 continue; | 
| Chris@529 | 342             } | 
| Chris@529 | 343         } | 
| Chris@529 | 344 | 
| Chris@529 | 345         good = true; | 
| Chris@529 | 346     } | 
| Chris@529 | 347 | 
| Chris@529 | 348     if (path != "") { | 
| Chris@529 | 349         settings.setValue(settingsKey, | 
| Chris@529 | 350                           QFileInfo(path).absoluteDir().canonicalPath()); | 
| Chris@529 | 351     } | 
| Chris@529 | 352 | 
| Chris@529 | 353     return path; | 
| Chris@529 | 354 } | 
| Chris@529 | 355 | 
| Chris@529 | 356 void | 
| Chris@529 | 357 InteractiveFileFinder::registerLastOpenedFilePath(FileType type, QString path) | 
| Chris@529 | 358 { | 
| Chris@529 | 359     QString settingsKey; | 
| Chris@529 | 360 | 
| Chris@529 | 361     switch (type) { | 
| Chris@529 | 362     case SessionFile: | 
| Chris@529 | 363         settingsKey = "sessionpath"; | 
| Chris@529 | 364         break; | 
| Chris@529 | 365 | 
| Chris@529 | 366     case AudioFile: | 
| Chris@529 | 367         settingsKey = "audiopath"; | 
| Chris@529 | 368         break; | 
| Chris@529 | 369 | 
| Chris@529 | 370     case LayerFile: | 
| Chris@529 | 371         settingsKey = "layerpath"; | 
| Chris@529 | 372         break; | 
| Chris@529 | 373 | 
| Chris@529 | 374     case LayerFileNoMidi: | 
| Chris@529 | 375         settingsKey = "layerpath"; | 
| Chris@529 | 376         break; | 
| Chris@529 | 377 | 
| Chris@529 | 378     case SessionOrAudioFile: | 
| Chris@529 | 379         settingsKey = "lastpath"; | 
| Chris@529 | 380         break; | 
| Chris@529 | 381 | 
| Chris@529 | 382     case ImageFile: | 
| Chris@529 | 383         settingsKey = "imagepath"; | 
| Chris@529 | 384         break; | 
| Chris@529 | 385 | 
| Chris@672 | 386     case CSVFile: | 
| Chris@672 | 387         settingsKey = "layerpath"; | 
| Chris@672 | 388         break; | 
| Chris@672 | 389 | 
| Chris@529 | 390     case AnyFile: | 
| Chris@529 | 391         settingsKey = "lastpath"; | 
| Chris@529 | 392         break; | 
| Chris@529 | 393     } | 
| Chris@529 | 394 | 
| Chris@529 | 395     if (path != "") { | 
| Chris@529 | 396         QSettings settings; | 
| Chris@529 | 397         settings.beginGroup("FileFinder"); | 
| Chris@529 | 398         path = QFileInfo(path).absoluteDir().canonicalPath(); | 
| Chris@529 | 399         settings.setValue(settingsKey, path); | 
| Chris@529 | 400         settings.setValue("lastpath", path); | 
| Chris@529 | 401     } | 
| Chris@529 | 402 } | 
| Chris@529 | 403 | 
| Chris@529 | 404 QString | 
| Chris@529 | 405 InteractiveFileFinder::find(FileType type, QString location, QString lastKnownLocation) | 
| Chris@529 | 406 { | 
| Chris@529 | 407     if (FileSource::canHandleScheme(location)) { | 
| Chris@529 | 408         if (FileSource(location).isAvailable()) { | 
| Chris@587 | 409             SVDEBUG << "InteractiveFileFinder::find: ok, it's available... returning" << endl; | 
| Chris@529 | 410             return location; | 
| Chris@529 | 411         } | 
| Chris@529 | 412     } | 
| Chris@529 | 413 | 
| Chris@529 | 414     if (QFileInfo(location).exists()) return location; | 
| Chris@529 | 415 | 
| Chris@529 | 416     QString foundAt = ""; | 
| Chris@529 | 417 | 
| Chris@529 | 418     if ((foundAt = findRelative(location, lastKnownLocation)) != "") { | 
| Chris@529 | 419         return foundAt; | 
| Chris@529 | 420     } | 
| Chris@529 | 421 | 
| Chris@529 | 422     if ((foundAt = findRelative(location, m_lastLocatedLocation)) != "") { | 
| Chris@529 | 423         return foundAt; | 
| Chris@529 | 424     } | 
| Chris@529 | 425 | 
| Chris@529 | 426     return locateInteractive(type, location); | 
| Chris@529 | 427 } | 
| Chris@529 | 428 | 
| Chris@529 | 429 QString | 
| Chris@529 | 430 InteractiveFileFinder::findRelative(QString location, QString relativeTo) | 
| Chris@529 | 431 { | 
| Chris@529 | 432     if (relativeTo == "") return ""; | 
| Chris@529 | 433 | 
| Chris@587 | 434     SVDEBUG << "Looking for \"" << location << "\" next to \"" | 
| Chris@585 | 435               << relativeTo << "\"..." << endl; | 
| Chris@529 | 436 | 
| Chris@529 | 437     QString fileName; | 
| Chris@529 | 438     QString resolved; | 
| Chris@529 | 439 | 
| Chris@529 | 440     if (FileSource::isRemote(location)) { | 
| Chris@529 | 441         fileName = QUrl(location).path().section('/', -1, -1, | 
| Chris@529 | 442                                                  QString::SectionSkipEmpty); | 
| Chris@529 | 443     } else { | 
| Chris@529 | 444         if (QUrl(location).scheme() == "file") { | 
| Chris@529 | 445             location = QUrl(location).toLocalFile(); | 
| Chris@529 | 446         } | 
| Chris@529 | 447         fileName = QFileInfo(location).fileName(); | 
| Chris@529 | 448     } | 
| Chris@529 | 449 | 
| Chris@529 | 450     if (FileSource::isRemote(relativeTo)) { | 
| Chris@529 | 451         resolved = QUrl(relativeTo).resolved(fileName).toString(); | 
| Chris@529 | 452         if (!FileSource(resolved).isAvailable()) resolved = ""; | 
| Chris@682 | 453         cerr << "resolved: " << resolved << endl; | 
| Chris@529 | 454     } else { | 
| Chris@529 | 455         if (QUrl(relativeTo).scheme() == "file") { | 
| Chris@529 | 456             relativeTo = QUrl(relativeTo).toLocalFile(); | 
| Chris@529 | 457         } | 
| Chris@529 | 458         resolved = QFileInfo(relativeTo).dir().filePath(fileName); | 
| Chris@529 | 459         if (!QFileInfo(resolved).exists() || | 
| Chris@529 | 460             !QFileInfo(resolved).isFile() || | 
| Chris@529 | 461             !QFileInfo(resolved).isReadable()) { | 
| Chris@529 | 462             resolved = ""; | 
| Chris@529 | 463         } | 
| Chris@529 | 464     } | 
| Chris@529 | 465 | 
| Chris@529 | 466     return resolved; | 
| Chris@529 | 467 } | 
| Chris@529 | 468 | 
| Chris@529 | 469 QString | 
| Chris@529 | 470 InteractiveFileFinder::locateInteractive(FileType type, QString thing) | 
| Chris@529 | 471 { | 
| Chris@529 | 472     QString question; | 
| Chris@529 | 473     if (type == AudioFile) { | 
| Chris@529 | 474         question = tr("<b>File not found</b><p>Audio file \"%1\" could not be opened.\nDo you want to locate it?"); | 
| Chris@529 | 475     } else { | 
| Chris@529 | 476         question = tr("<b>File not found</b><p>File \"%1\" could not be opened.\nDo you want to locate it?"); | 
| Chris@529 | 477     } | 
| Chris@529 | 478 | 
| Chris@529 | 479     QString path = ""; | 
| Chris@529 | 480     bool done = false; | 
| Chris@529 | 481 | 
| Chris@529 | 482     while (!done) { | 
| Chris@529 | 483 | 
| Chris@529 | 484         int rv = QMessageBox::question | 
| Chris@529 | 485             (0, | 
| Chris@529 | 486              tr("Failed to open file"), | 
| Chris@529 | 487              question.arg(thing), | 
| Chris@529 | 488              tr("Locate file..."), | 
| Chris@529 | 489              tr("Use URL..."), | 
| Chris@529 | 490              tr("Cancel"), | 
| Chris@529 | 491              0, 2); | 
| Chris@529 | 492 | 
| Chris@529 | 493         switch (rv) { | 
| Chris@529 | 494 | 
| Chris@529 | 495         case 0: // Locate file | 
| Chris@529 | 496 | 
| Chris@529 | 497             if (QFileInfo(thing).dir().exists()) { | 
| Chris@529 | 498                 path = QFileInfo(thing).dir().canonicalPath(); | 
| Chris@529 | 499             } | 
| Chris@529 | 500 | 
| Chris@529 | 501             path = getOpenFileName(type, path); | 
| Chris@529 | 502             done = (path != ""); | 
| Chris@529 | 503             break; | 
| Chris@529 | 504 | 
| Chris@529 | 505         case 1: // Use URL | 
| Chris@529 | 506         { | 
| Chris@529 | 507             bool ok = false; | 
| Chris@529 | 508             path = QInputDialog::getText | 
| Chris@529 | 509                 (0, tr("Use URL"), | 
| Chris@529 | 510                  tr("Please enter the URL to use for this file:"), | 
| Chris@529 | 511                  QLineEdit::Normal, "", &ok); | 
| Chris@529 | 512 | 
| Chris@529 | 513             if (ok && path != "") { | 
| Chris@529 | 514                 if (FileSource(path).isAvailable()) { | 
| Chris@529 | 515                     done = true; | 
| Chris@529 | 516                 } else { | 
| Chris@529 | 517                     QMessageBox::critical | 
| Chris@529 | 518                         (0, tr("Failed to open location"), | 
| Chris@529 | 519                          tr("<b>Failed to open location</b><p>URL \"%1\" could not be opened").arg(path)); | 
| Chris@529 | 520                     path = ""; | 
| Chris@529 | 521                 } | 
| Chris@529 | 522             } | 
| Chris@529 | 523             break; | 
| Chris@529 | 524         } | 
| Chris@529 | 525 | 
| Chris@529 | 526         case 2: // Cancel | 
| Chris@529 | 527             path = ""; | 
| Chris@529 | 528             done = true; | 
| Chris@529 | 529             break; | 
| Chris@529 | 530         } | 
| Chris@529 | 531     } | 
| Chris@529 | 532 | 
| Chris@529 | 533     if (path != "") m_lastLocatedLocation = path; | 
| Chris@529 | 534     return path; | 
| Chris@529 | 535 } | 
| Chris@529 | 536 | 
| Chris@529 | 537 |