FileFinder.cpp

Go to the documentation of this file.
00001 /* -*- c-basic-offset: 4 indent-tabs-mode: nil -*-  vi:set ts=8 sts=4 sw=4: */
00002 
00003 /*
00004     Sonic Visualiser
00005     An audio file viewer and annotation editor.
00006     Centre for Digital Music, Queen Mary, University of London.
00007     This file copyright 2007 QMUL.
00008     
00009     This program is free software; you can redistribute it and/or
00010     modify it under the terms of the GNU General Public License as
00011     published by the Free Software Foundation; either version 2 of the
00012     License, or (at your option) any later version.  See the file
00013     COPYING included with this distribution for more information.
00014 */
00015 
00016 #include "FileFinder.h"
00017 #include "FileSource.h"
00018 #include "AudioFileReaderFactory.h"
00019 #include "DataFileReaderFactory.h"
00020 
00021 #include <QFileInfo>
00022 #include <QMessageBox>
00023 #include <QFileDialog>
00024 #include <QInputDialog>
00025 #include <QImageReader>
00026 #include <QSettings>
00027 
00028 #include <iostream>
00029 
00030 FileFinder *
00031 FileFinder::m_instance = 0;
00032 
00033 FileFinder::FileFinder() :
00034     m_lastLocatedLocation("")
00035 {
00036 }
00037 
00038 FileFinder::~FileFinder()
00039 {
00040 }
00041 
00042 FileFinder *
00043 FileFinder::getInstance()
00044 {
00045     if (m_instance == 0) {
00046         m_instance = new FileFinder();
00047     }
00048     return m_instance;
00049 }
00050 
00051 QString
00052 FileFinder::getOpenFileName(FileType type, QString fallbackLocation)
00053 {
00054     QString settingsKey;
00055     QString lastPath = fallbackLocation;
00056     
00057     QString title = tr("Select file");
00058     QString filter = tr("All files (*.*)");
00059 
00060     switch (type) {
00061 
00062     case SessionFile:
00063         settingsKey = "sessionpath";
00064         title = tr("Select a session file");
00065         filter = tr("Sonic Visualiser session files (*.sv)\nAll files (*.*)");
00066         break;
00067 
00068     case AudioFile:
00069         settingsKey = "audiopath";
00070         title = "Select an audio file";
00071         filter = tr("Audio files (%1)\nAll files (*.*)")
00072             .arg(AudioFileReaderFactory::getKnownExtensions());
00073         break;
00074 
00075     case LayerFile:
00076         settingsKey = "layerpath";
00077         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());
00078         break;
00079 
00080     case LayerFileNoMidi:
00081         settingsKey = "layerpath";
00082         filter = tr("All supported files (%1)\nSonic Visualiser Layer XML files (*.svl)\nComma-separated data files (*.csv)\nSpace-separated .lab files (*.lab)\nText files (*.txt)\nAll files (*.*)").arg(DataFileReaderFactory::getKnownExtensions());
00083         break;
00084 
00085     case SessionOrAudioFile:
00086         settingsKey = "lastpath";
00087         filter = tr("All supported files (*.sv %1)\nSonic Visualiser session files (*.sv)\nAudio files (%1)\nAll files (*.*)")
00088             .arg(AudioFileReaderFactory::getKnownExtensions());
00089         break;
00090 
00091     case ImageFile:
00092         settingsKey = "imagepath";
00093         {
00094             QStringList fmts;
00095             QList<QByteArray> formats = QImageReader::supportedImageFormats();
00096             for (QList<QByteArray>::iterator i = formats.begin();
00097                  i != formats.end(); ++i) {
00098                 fmts.push_back(QString("*.%1")
00099                                .arg(QString::fromLocal8Bit(*i).toLower()));
00100             }
00101             filter = tr("Image files (%1)\nAll files (*.*)").arg(fmts.join(" "));
00102         }
00103         break;
00104 
00105     case AnyFile:
00106         settingsKey = "lastpath";
00107         filter = tr("All supported files (*.sv %1 %2)\nSonic Visualiser session files (*.sv)\nAudio files (%1)\nLayer files (%2)\nAll files (*.*)")
00108             .arg(AudioFileReaderFactory::getKnownExtensions())
00109             .arg(DataFileReaderFactory::getKnownExtensions());
00110         break;
00111     };
00112 
00113     if (lastPath == "") {
00114         char *home = getenv("HOME");
00115         if (home) lastPath = home;
00116         else lastPath = ".";
00117     } else if (QFileInfo(lastPath).isDir()) {
00118         lastPath = QFileInfo(lastPath).canonicalPath();
00119     } else {
00120         lastPath = QFileInfo(lastPath).absoluteDir().canonicalPath();
00121     }
00122 
00123     QSettings settings;
00124     settings.beginGroup("FileFinder");
00125     lastPath = settings.value(settingsKey, lastPath).toString();
00126 
00127     QString path = "";
00128 
00129     // Use our own QFileDialog just for symmetry with getSaveFileName below
00130 
00131     QFileDialog dialog;
00132     dialog.setFilters(filter.split('\n'));
00133     dialog.setWindowTitle(title);
00134     dialog.setDirectory(lastPath);
00135 
00136     dialog.setAcceptMode(QFileDialog::AcceptOpen);
00137     dialog.setFileMode(QFileDialog::ExistingFile);
00138     
00139     if (dialog.exec()) {
00140         QStringList files = dialog.selectedFiles();
00141         if (!files.empty()) path = *files.begin();
00142         
00143         QFileInfo fi(path);
00144         
00145         if (!fi.exists()) {
00146             
00147             QMessageBox::critical(0, tr("File does not exist"),
00148                                   tr("File \"%1\" does not exist").arg(path));
00149             path = "";
00150             
00151         } else if (!fi.isReadable()) {
00152             
00153             QMessageBox::critical(0, tr("File is not readable"),
00154                                   tr("File \"%1\" can not be read").arg(path));
00155             path = "";
00156             
00157         } else if (fi.isDir()) {
00158             
00159             QMessageBox::critical(0, tr("Directory selected"),
00160                                   tr("File \"%1\" is a directory").arg(path));
00161             path = "";
00162 
00163         } else if (!fi.isFile()) {
00164             
00165             QMessageBox::critical(0, tr("Non-file selected"),
00166                                   tr("Path \"%1\" is not a file").arg(path));
00167             path = "";
00168             
00169         } else if (fi.size() == 0) {
00170             
00171             QMessageBox::critical(0, tr("File is empty"),
00172                                   tr("File \"%1\" is empty").arg(path));
00173             path = "";
00174         }                
00175     }
00176 
00177     if (path != "") {
00178         settings.setValue(settingsKey,
00179                           QFileInfo(path).absoluteDir().canonicalPath());
00180     }
00181     
00182     return path;
00183 }
00184 
00185 QString
00186 FileFinder::getSaveFileName(FileType type, QString fallbackLocation)
00187 {
00188     QString settingsKey;
00189     QString lastPath = fallbackLocation;
00190     
00191     QString title = tr("Select file");
00192     QString filter = tr("All files (*.*)");
00193 
00194     switch (type) {
00195 
00196     case SessionFile:
00197         settingsKey = "savesessionpath";
00198         title = tr("Select a session file");
00199         filter = tr("Sonic Visualiser session files (*.sv)\nAll files (*.*)");
00200         break;
00201 
00202     case AudioFile:
00203         settingsKey = "saveaudiopath";
00204         title = "Select an audio file";
00205         title = tr("Select a file to export to");
00206         filter = tr("WAV audio files (*.wav)\nAll files (*.*)");
00207         break;
00208 
00209     case LayerFile:
00210         settingsKey = "savelayerpath";
00211         title = tr("Select a file to export to");
00212         filter = tr("Sonic Visualiser Layer XML files (*.svl)\nComma-separated data files (*.csv)\nMIDI files (*.mid)\nText files (*.txt)\nAll files (*.*)");
00213         break;
00214 
00215     case LayerFileNoMidi:
00216         settingsKey = "savelayerpath";
00217         title = tr("Select a file to export to");
00218         filter = tr("Sonic Visualiser Layer XML files (*.svl)\nComma-separated data files (*.csv)\nText files (*.txt)\nAll files (*.*)");
00219         break;
00220 
00221     case SessionOrAudioFile:
00222         std::cerr << "ERROR: Internal error: FileFinder::getSaveFileName: SessionOrAudioFile cannot be used here" << std::endl;
00223         abort();
00224 
00225     case ImageFile:
00226         settingsKey = "saveimagepath";
00227         title = tr("Select a file to export to");
00228         filter = tr("Portable Network Graphics files (*.png)\nAll files (*.*)");
00229         break;
00230 
00231     case AnyFile:
00232         std::cerr << "ERROR: Internal error: FileFinder::getSaveFileName: AnyFile cannot be used here" << std::endl;
00233         abort();
00234     };
00235 
00236     if (lastPath == "") {
00237         char *home = getenv("HOME");
00238         if (home) lastPath = home;
00239         else lastPath = ".";
00240     } else if (QFileInfo(lastPath).isDir()) {
00241         lastPath = QFileInfo(lastPath).canonicalPath();
00242     } else {
00243         lastPath = QFileInfo(lastPath).absoluteDir().canonicalPath();
00244     }
00245 
00246     QSettings settings;
00247     settings.beginGroup("FileFinder");
00248     lastPath = settings.value(settingsKey, lastPath).toString();
00249 
00250     QString path = "";
00251 
00252     // Use our own QFileDialog instead of static functions, as we may
00253     // need to adjust the file extension based on the selected filter
00254 
00255     QFileDialog dialog;
00256     dialog.setFilters(filter.split('\n'));
00257     dialog.setWindowTitle(title);
00258     dialog.setDirectory(lastPath);
00259 
00260     dialog.setAcceptMode(QFileDialog::AcceptSave);
00261     dialog.setFileMode(QFileDialog::AnyFile);
00262     dialog.setConfirmOverwrite(false); // we'll do that
00263         
00264     if (type == SessionFile) {
00265         dialog.setDefaultSuffix("sv");
00266     } else if (type == AudioFile) {
00267         dialog.setDefaultSuffix("wav");
00268     } else if (type == ImageFile) {
00269         dialog.setDefaultSuffix("png");
00270     }
00271 
00272     bool good = false;
00273 
00274     while (!good) {
00275 
00276         path = "";
00277         
00278         if (!dialog.exec()) break;
00279         
00280         QStringList files = dialog.selectedFiles();
00281         if (files.empty()) break;
00282         path = *files.begin();
00283         
00284         QFileInfo fi(path);
00285 
00286         std::cerr << "type = " << type << ", suffix = " << fi.suffix().toStdString() << std::endl;
00287         
00288         if ((type == LayerFile || type == LayerFileNoMidi)
00289             && fi.suffix() == "") {
00290             QString expectedExtension;
00291             QString selectedFilter = dialog.selectedFilter();
00292             if (selectedFilter.contains(".svl")) {
00293                 expectedExtension = "svl";
00294             } else if (selectedFilter.contains(".txt")) {
00295                 expectedExtension = "txt";
00296             } else if (selectedFilter.contains(".csv")) {
00297                 expectedExtension = "csv";
00298             } else if (selectedFilter.contains(".mid")) {
00299                 expectedExtension = "mid";
00300             }
00301             std::cerr << "expected extension = " << expectedExtension.toStdString() << std::endl;
00302             if (expectedExtension != "") {
00303                 path = QString("%1.%2").arg(path).arg(expectedExtension);
00304                 fi = QFileInfo(path);
00305             }
00306         }
00307         
00308         if (fi.isDir()) {
00309             QMessageBox::critical(0, tr("Directory selected"),
00310                                   tr("File \"%1\" is a directory").arg(path));
00311             continue;
00312         }
00313         
00314         if (fi.exists()) {
00315             if (QMessageBox::question(0, tr("File exists"),
00316                                       tr("The file \"%1\" already exists.\nDo you want to overwrite it?").arg(path),
00317                                       QMessageBox::Ok,
00318                                       QMessageBox::Cancel) != QMessageBox::Ok) {
00319                 continue;
00320             }
00321         }
00322         
00323         good = true;
00324     }
00325         
00326     if (path != "") {
00327         settings.setValue(settingsKey,
00328                           QFileInfo(path).absoluteDir().canonicalPath());
00329     }
00330     
00331     return path;
00332 }
00333 
00334 void
00335 FileFinder::registerLastOpenedFilePath(FileType type, QString path)
00336 {
00337     QString settingsKey;
00338 
00339     switch (type) {
00340     case SessionFile:
00341         settingsKey = "sessionpath";
00342         break;
00343 
00344     case AudioFile:
00345         settingsKey = "audiopath";
00346         break;
00347 
00348     case LayerFile:
00349         settingsKey = "layerpath";
00350         break;
00351 
00352     case LayerFileNoMidi:
00353         settingsKey = "layerpath";
00354         break;
00355 
00356     case SessionOrAudioFile:
00357         settingsKey = "lastpath";
00358         break;
00359 
00360     case ImageFile:
00361         settingsKey = "imagepath";
00362         break;
00363 
00364     case AnyFile:
00365         settingsKey = "lastpath";
00366         break;
00367     }
00368 
00369     if (path != "") {
00370         QSettings settings;
00371         settings.beginGroup("FileFinder");
00372         path = QFileInfo(path).absoluteDir().canonicalPath();
00373         settings.setValue(settingsKey, path);
00374         settings.setValue("lastpath", path);
00375     }
00376 }
00377     
00378 QString
00379 FileFinder::find(FileType type, QString location, QString lastKnownLocation)
00380 {
00381     if (FileSource::canHandleScheme(location)) {
00382         if (FileSource(location).isAvailable()) {
00383             std::cerr << "FileFinder::find: ok, it's available... returning" << std::endl;
00384             return location;
00385         }
00386     }
00387 
00388     if (QFileInfo(location).exists()) return location;
00389 
00390     QString foundAt = "";
00391 
00392     if ((foundAt = findRelative(location, lastKnownLocation)) != "") {
00393         return foundAt;
00394     }
00395 
00396     if ((foundAt = findRelative(location, m_lastLocatedLocation)) != "") {
00397         return foundAt;
00398     }
00399 
00400     return locateInteractive(type, location);
00401 }
00402 
00403 QString
00404 FileFinder::findRelative(QString location, QString relativeTo)
00405 {
00406     if (relativeTo == "") return "";
00407 
00408     std::cerr << "Looking for \"" << location.toStdString() << "\" next to \""
00409               << relativeTo.toStdString() << "\"..." << std::endl;
00410 
00411     QString fileName;
00412     QString resolved;
00413 
00414     if (FileSource::isRemote(location)) {
00415         fileName = QUrl(location).path().section('/', -1, -1,
00416                                                  QString::SectionSkipEmpty);
00417     } else {
00418         if (QUrl(location).scheme() == "file") {
00419             location = QUrl(location).toLocalFile();
00420         }
00421         fileName = QFileInfo(location).fileName();
00422     }
00423 
00424     if (FileSource::isRemote(relativeTo)) {
00425         resolved = QUrl(relativeTo).resolved(fileName).toString();
00426         if (!FileSource(resolved).isAvailable()) resolved = "";
00427         std::cerr << "resolved: " << resolved.toStdString() << std::endl;
00428     } else {
00429         if (QUrl(relativeTo).scheme() == "file") {
00430             relativeTo = QUrl(relativeTo).toLocalFile();
00431         }
00432         resolved = QFileInfo(relativeTo).dir().filePath(fileName);
00433         if (!QFileInfo(resolved).exists() ||
00434             !QFileInfo(resolved).isFile() ||
00435             !QFileInfo(resolved).isReadable()) {
00436             resolved = "";
00437         }
00438     }
00439             
00440     return resolved;
00441 }
00442 
00443 QString
00444 FileFinder::locateInteractive(FileType type, QString thing)
00445 {
00446     QString question;
00447     if (type == AudioFile) {
00448         question = tr("Audio file \"%1\" could not be opened.\nDo you want to locate it?");
00449     } else {
00450         question = tr("File \"%1\" could not be opened.\nDo you want to locate it?");
00451     }
00452 
00453     QString path = "";
00454     bool done = false;
00455 
00456     while (!done) {
00457 
00458         int rv = QMessageBox::question
00459             (0, 
00460              tr("Failed to open file"),
00461              question.arg(thing),
00462              tr("Locate file..."),
00463              tr("Use URL..."),
00464              tr("Cancel"),
00465              0, 2);
00466         
00467         switch (rv) {
00468 
00469         case 0: // Locate file
00470 
00471             if (QFileInfo(thing).dir().exists()) {
00472                 path = QFileInfo(thing).dir().canonicalPath();
00473             }
00474             
00475             path = getOpenFileName(type, path);
00476             done = (path != "");
00477             break;
00478 
00479         case 1: // Use URL
00480         {
00481             bool ok = false;
00482             path = QInputDialog::getText
00483                 (0, tr("Use URL"),
00484                  tr("Please enter the URL to use for this file:"),
00485                  QLineEdit::Normal, "", &ok);
00486 
00487             if (ok && path != "") {
00488                 if (FileSource(path).isAvailable()) {
00489                     done = true;
00490                 } else {
00491                     QMessageBox::critical
00492                         (0, tr("Failed to open location"),
00493                          tr("URL \"%1\" could not be opened").arg(path));
00494                     path = "";
00495                 }
00496             }
00497             break;
00498         }
00499 
00500         case 2: // Cancel
00501             path = "";
00502             done = true;
00503             break;
00504         }
00505     }
00506 
00507     if (path != "") m_lastLocatedLocation = path;
00508     return path;
00509 }
00510 
00511 

Generated on Wed Feb 20 15:45:25 2008 for SonicVisualiser by  doxygen 1.5.1