svgui
1.9
|
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 "InteractiveFileFinder.h" 00017 #include "data/fileio/FileSource.h" 00018 #include "data/fileio/AudioFileReaderFactory.h" 00019 #include "data/fileio/DataFileReaderFactory.h" 00020 #include "rdf/RDFImporter.h" 00021 #include "rdf/RDFExporter.h" 00022 00023 #include <QFileInfo> 00024 #include <QMessageBox> 00025 #include <QFileDialog> 00026 #include <QInputDialog> 00027 #include <QImageReader> 00028 #include <QSettings> 00029 00030 #include <iostream> 00031 00032 InteractiveFileFinder 00033 InteractiveFileFinder::m_instance; 00034 00035 InteractiveFileFinder::InteractiveFileFinder() : 00036 m_sessionExtension("sv"), 00037 m_lastLocatedLocation(""), 00038 m_parent(0) 00039 { 00040 SVDEBUG << "Registering interactive file finder" << endl; 00041 FileFinder::registerFileFinder(this); 00042 } 00043 00044 InteractiveFileFinder::~InteractiveFileFinder() 00045 { 00046 } 00047 00048 void 00049 InteractiveFileFinder::setParentWidget(QWidget *parent) 00050 { 00051 getInstance()->m_parent = parent; 00052 } 00053 00054 void 00055 InteractiveFileFinder::setApplicationSessionExtension(QString extension) 00056 { 00057 m_sessionExtension = extension; 00058 } 00059 00060 QString 00061 InteractiveFileFinder::getOpenFileName(FileType type, QString fallbackLocation) 00062 { 00063 QString settingsKeyStub; 00064 QString lastPath = fallbackLocation; 00065 00066 QString title = tr("Select file"); 00067 QString filter = tr("All files (*.*)"); 00068 00069 switch (type) { 00070 00071 case SessionFile: 00072 settingsKeyStub = "session"; 00073 title = tr("Select a session file"); 00074 filter = tr("%1 session files (*.%1)\nRDF files (%3)\nAll files (*.*)") 00075 .arg(QApplication::applicationName()) 00076 .arg(m_sessionExtension) 00077 .arg(RDFImporter::getKnownExtensions()); 00078 break; 00079 00080 case AudioFile: 00081 settingsKeyStub = "audio"; 00082 title = "Select an audio file"; 00083 filter = tr("Audio files (%1)\nAll files (*.*)") 00084 .arg(AudioFileReaderFactory::getKnownExtensions()); 00085 break; 00086 00087 case LayerFile: 00088 settingsKeyStub = "layer"; 00089 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 (*.*)") 00090 .arg(DataFileReaderFactory::getKnownExtensions()) 00091 .arg(RDFImporter::getKnownExtensions()); 00092 break; 00093 00094 case LayerFileNoMidi: 00095 settingsKeyStub = "layer"; 00096 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 (*.*)") 00097 .arg(DataFileReaderFactory::getKnownExtensions()) 00098 .arg(RDFImporter::getKnownExtensions()); 00099 break; 00100 00101 case LayerFileNonSV: 00102 settingsKeyStub = "layer"; 00103 filter = tr("All supported files (%1 %2)\nComma-separated data files (*.csv)\nSonic Visualiser Layer XML files (*.svl)\nSpace-separated .lab files (*.lab)\nRDF files (%2)\nMIDI files (*.mid)\nText files (*.txt)\nAll files (*.*)") 00104 .arg(DataFileReaderFactory::getKnownExtensions()) 00105 .arg(RDFImporter::getKnownExtensions()); 00106 break; 00107 00108 case LayerFileNoMidiNonSV: 00109 settingsKeyStub = "layer"; 00110 filter = tr("All supported files (%1 %2)\nComma-separated data files (*.csv)\nSonic Visualiser Layer XML files (*.svl)\nSpace-separated .lab files (*.lab)\nRDF files (%2)\nText files (*.txt)\nAll files (*.*)") 00111 .arg(DataFileReaderFactory::getKnownExtensions()) 00112 .arg(RDFImporter::getKnownExtensions()); 00113 break; 00114 00115 case SessionOrAudioFile: 00116 settingsKeyStub = "last"; 00117 filter = tr("All supported files (*.sv %1 %2)\n%3 session files (*.%4)\nAudio files (%2)\nRDF files (%1)\nAll files (*.*)") 00118 .arg(RDFImporter::getKnownExtensions()) 00119 .arg(AudioFileReaderFactory::getKnownExtensions()) 00120 .arg(QApplication::applicationName()) 00121 .arg(m_sessionExtension); 00122 break; 00123 00124 case ImageFile: 00125 settingsKeyStub = "image"; 00126 { 00127 QStringList fmts; 00128 QList<QByteArray> formats = QImageReader::supportedImageFormats(); 00129 for (QList<QByteArray>::iterator i = formats.begin(); 00130 i != formats.end(); ++i) { 00131 fmts.push_back(QString("*.%1") 00132 .arg(QString::fromLocal8Bit(*i).toLower())); 00133 } 00134 filter = tr("Image files (%1)\nAll files (*.*)").arg(fmts.join(" ")); 00135 } 00136 break; 00137 00138 case CSVFile: 00139 settingsKeyStub = "layer"; 00140 filter = tr("Comma-separated data files (*.csv)\nSpace-separated .lab files (*.lab)\nText files (*.txt)\nAll files (*.*)"); 00141 break; 00142 00143 case AnyFile: 00144 settingsKeyStub = "last"; 00145 filter = tr("All supported files (*.sv %1 %2 %3)\n%4 session files (*.%5)\nAudio files (%1)\nLayer files (%2)\nRDF files (%3)\nAll files (*.*)") 00146 .arg(AudioFileReaderFactory::getKnownExtensions()) 00147 .arg(DataFileReaderFactory::getKnownExtensions()) 00148 .arg(RDFImporter::getKnownExtensions()) 00149 .arg(QApplication::applicationName()) 00150 .arg(m_sessionExtension); 00151 break; 00152 }; 00153 00154 if (lastPath == "") { 00155 char *home = getenv("HOME"); 00156 if (home) lastPath = home; 00157 else lastPath = "."; 00158 } else if (QFileInfo(lastPath).isDir()) { 00159 lastPath = QFileInfo(lastPath).canonicalPath(); 00160 } else { 00161 lastPath = QFileInfo(lastPath).absoluteDir().canonicalPath(); 00162 } 00163 00164 QSettings settings; 00165 settings.beginGroup("FileFinder"); 00166 lastPath = settings.value(settingsKeyStub + "path", lastPath).toString(); 00167 00168 QString path = ""; 00169 00170 // Use our own QFileDialog just for symmetry with getSaveFileName below 00171 00172 QFileDialog dialog(m_parent); 00173 dialog.setNameFilters(filter.split('\n')); 00174 dialog.setWindowTitle(title); 00175 dialog.setDirectory(lastPath); 00176 00177 dialog.setAcceptMode(QFileDialog::AcceptOpen); 00178 dialog.setFileMode(QFileDialog::ExistingFile); 00179 00180 if (dialog.exec()) { 00181 QStringList files = dialog.selectedFiles(); 00182 if (!files.empty()) path = *files.begin(); 00183 00184 QFileInfo fi(path); 00185 00186 if (!fi.exists()) { 00187 00188 QMessageBox::critical(0, tr("File does not exist"), 00189 tr("<b>File not found</b><p>File \"%1\" does not exist").arg(path)); 00190 path = ""; 00191 00192 } else if (!fi.isReadable()) { 00193 00194 QMessageBox::critical(0, tr("File is not readable"), 00195 tr("<b>File is not readable</b><p>File \"%1\" can not be read").arg(path)); 00196 path = ""; 00197 00198 } else if (fi.isDir()) { 00199 00200 QMessageBox::critical(0, tr("Directory selected"), 00201 tr("<b>Directory selected</b><p>File \"%1\" is a directory").arg(path)); 00202 path = ""; 00203 00204 } else if (!fi.isFile()) { 00205 00206 QMessageBox::critical(0, tr("Non-file selected"), 00207 tr("<b>Not a file</b><p>Path \"%1\" is not a file").arg(path)); 00208 path = ""; 00209 00210 } else if (fi.size() == 0) { 00211 00212 QMessageBox::critical(0, tr("File is empty"), 00213 tr("<b>File is empty</b><p>File \"%1\" is empty").arg(path)); 00214 path = ""; 00215 } 00216 } 00217 00218 if (path != "") { 00219 settings.setValue(settingsKeyStub + "path", 00220 QFileInfo(path).absoluteDir().canonicalPath()); 00221 } 00222 00223 return path; 00224 } 00225 00226 QString 00227 InteractiveFileFinder::getSaveFileName(FileType type, 00228 QString fallbackLocation) 00229 { 00230 QString settingsKeyStub; 00231 QString lastPath = fallbackLocation; 00232 00233 QString title = tr("Select file"); 00234 QString filter = tr("All files (*.*)"); 00235 00236 switch (type) { 00237 00238 case SessionFile: 00239 settingsKeyStub = "savesession"; 00240 title = tr("Select a session file"); 00241 filter = tr("%1 session files (*.%2)\nAll files (*.*)") 00242 .arg(QApplication::applicationName()).arg(m_sessionExtension); 00243 break; 00244 00245 case AudioFile: 00246 settingsKeyStub = "saveaudio"; 00247 title = "Select an audio file"; 00248 title = tr("Select a file to export to"); 00249 filter = tr("WAV audio files (*.wav)\nAll files (*.*)"); 00250 break; 00251 00252 case LayerFile: 00253 settingsKeyStub = "savelayer"; 00254 title = tr("Select a file to export to"); 00255 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()); 00256 break; 00257 00258 case LayerFileNoMidi: 00259 settingsKeyStub = "savelayer"; 00260 title = tr("Select a file to export to"); 00261 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()); 00262 break; 00263 00264 case LayerFileNonSV: 00265 settingsKeyStub = "savelayer"; 00266 title = tr("Select a file to export to"); 00267 filter = tr("Comma-separated data files (*.csv)\nSonic Visualiser Layer XML files (*.svl)\nRDF/Turtle files (%1)\nMIDI files (*.mid)\nText files (*.txt)\nAll files (*.*)").arg(RDFExporter::getSupportedExtensions()); 00268 break; 00269 00270 case LayerFileNoMidiNonSV: 00271 settingsKeyStub = "savelayer"; 00272 title = tr("Select a file to export to"); 00273 filter = tr("Comma-separated data files (*.csv)\nSonic Visualiser Layer XML files (*.svl)\nRDF/Turtle files (%1)\nText files (*.txt)\nAll files (*.*)").arg(RDFExporter::getSupportedExtensions()); 00274 break; 00275 00276 case SessionOrAudioFile: 00277 cerr << "ERROR: Internal error: InteractiveFileFinder::getSaveFileName: SessionOrAudioFile cannot be used here" << endl; 00278 abort(); 00279 00280 case ImageFile: 00281 settingsKeyStub = "saveimage"; 00282 title = tr("Select a file to export to"); 00283 filter = tr("Portable Network Graphics files (*.png)\nAll files (*.*)"); 00284 break; 00285 00286 case CSVFile: 00287 settingsKeyStub = "savelayer"; 00288 title = tr("Select a file to export to"); 00289 filter = tr("Comma-separated data files (*.csv)\nText files (*.txt)\nAll files (*.*)"); 00290 break; 00291 00292 case AnyFile: 00293 cerr << "ERROR: Internal error: InteractiveFileFinder::getSaveFileName: AnyFile cannot be used here" << endl; 00294 abort(); 00295 }; 00296 00297 if (lastPath == "") { 00298 char *home = getenv("HOME"); 00299 if (home) lastPath = home; 00300 else lastPath = "."; 00301 } else if (QFileInfo(lastPath).isDir()) { 00302 lastPath = QFileInfo(lastPath).canonicalPath(); 00303 } else { 00304 lastPath = QFileInfo(lastPath).absoluteDir().canonicalPath(); 00305 } 00306 00307 QSettings settings; 00308 settings.beginGroup("FileFinder"); 00309 lastPath = settings.value(settingsKeyStub + "path", lastPath).toString(); 00310 00311 QString path = ""; 00312 00313 // Use our own QFileDialog instead of static functions, as we may 00314 // need to adjust the file extension based on the selected filter 00315 00316 QFileDialog dialog(m_parent); 00317 00318 QStringList filters = filter.split('\n'); 00319 00320 dialog.setNameFilters(filters); 00321 dialog.setWindowTitle(title); 00322 dialog.setDirectory(lastPath); 00323 dialog.setAcceptMode(QFileDialog::AcceptSave); 00324 dialog.setFileMode(QFileDialog::AnyFile); 00325 dialog.setConfirmOverwrite(false); // we'll do that 00326 00327 QString defaultSuffix; 00328 if (type == SessionFile) { 00329 defaultSuffix = m_sessionExtension; 00330 } else if (type == AudioFile) { 00331 defaultSuffix = "wav"; 00332 } else if (type == ImageFile) { 00333 defaultSuffix = "png"; 00334 } else if (type == CSVFile) { 00335 defaultSuffix = "csv"; 00336 } 00337 00338 defaultSuffix = 00339 settings.value(settingsKeyStub + "suffix", defaultSuffix).toString(); 00340 00341 dialog.setDefaultSuffix(defaultSuffix); 00342 00343 foreach (QString f, filters) { 00344 if (f.contains("." + defaultSuffix)) { 00345 dialog.selectNameFilter(f); 00346 } 00347 } 00348 00349 bool good = false; 00350 00351 while (!good) { 00352 00353 path = ""; 00354 00355 if (!dialog.exec()) break; 00356 00357 QStringList files = dialog.selectedFiles(); 00358 if (files.empty()) break; 00359 path = *files.begin(); 00360 00361 QFileInfo fi(path); 00362 00363 cerr << "type = " << type << ", suffix = " << fi.suffix() << endl; 00364 00365 if ((type == LayerFile || type == LayerFileNoMidi || 00366 type == LayerFileNonSV || type == LayerFileNoMidiNonSV) 00367 && fi.suffix() == "") { 00368 QString expectedExtension; 00369 QString selectedFilter = dialog.selectedNameFilter(); 00370 if (selectedFilter.contains(".svl")) { 00371 expectedExtension = "svl"; 00372 } else if (selectedFilter.contains(".txt")) { 00373 expectedExtension = "txt"; 00374 } else if (selectedFilter.contains(".csv")) { 00375 expectedExtension = "csv"; 00376 } else if (selectedFilter.contains(".mid")) { 00377 expectedExtension = "mid"; 00378 } else if (selectedFilter.contains(".ttl")) { 00379 expectedExtension = "ttl"; 00380 } 00381 cerr << "expected extension = " << expectedExtension << endl; 00382 if (expectedExtension != "") { 00383 path = QString("%1.%2").arg(path).arg(expectedExtension); 00384 fi = QFileInfo(path); 00385 } 00386 } 00387 00388 if (fi.isDir()) { 00389 QMessageBox::critical(0, tr("Directory selected"), 00390 tr("<b>Directory selected</b><p>File \"%1\" is a directory").arg(path)); 00391 continue; 00392 } 00393 00394 if (fi.exists()) { 00395 if (QMessageBox::question(0, tr("File exists"), 00396 tr("<b>File exists</b><p>The file \"%1\" already exists.\nDo you want to overwrite it?").arg(path), 00397 QMessageBox::Ok, 00398 QMessageBox::Cancel) != QMessageBox::Ok) { 00399 continue; 00400 } 00401 } 00402 00403 good = true; 00404 } 00405 00406 if (path != "") { 00407 settings.setValue(settingsKeyStub + "path", 00408 QFileInfo(path).absoluteDir().canonicalPath()); 00409 settings.setValue(settingsKeyStub + "suffix", 00410 QFileInfo(path).suffix()); 00411 } 00412 00413 return path; 00414 } 00415 00416 void 00417 InteractiveFileFinder::registerLastOpenedFilePath(FileType type, QString path) 00418 { 00419 QString settingsKeyStub; 00420 00421 switch (type) { 00422 case SessionFile: 00423 settingsKeyStub = "session"; 00424 break; 00425 00426 case AudioFile: 00427 settingsKeyStub = "audio"; 00428 break; 00429 00430 case LayerFile: 00431 settingsKeyStub = "layer"; 00432 break; 00433 00434 case LayerFileNoMidi: 00435 settingsKeyStub = "layer"; 00436 break; 00437 00438 case LayerFileNonSV: 00439 settingsKeyStub = "layer"; 00440 break; 00441 00442 case LayerFileNoMidiNonSV: 00443 settingsKeyStub = "layer"; 00444 break; 00445 00446 case SessionOrAudioFile: 00447 settingsKeyStub = "last"; 00448 break; 00449 00450 case ImageFile: 00451 settingsKeyStub = "image"; 00452 break; 00453 00454 case CSVFile: 00455 settingsKeyStub = "layer"; 00456 break; 00457 00458 case AnyFile: 00459 settingsKeyStub = "last"; 00460 break; 00461 } 00462 00463 if (path != "") { 00464 QSettings settings; 00465 settings.beginGroup("FileFinder"); 00466 path = QFileInfo(path).absoluteDir().canonicalPath(); 00467 QString suffix = QFileInfo(path).suffix(); 00468 settings.setValue(settingsKeyStub + "path", path); 00469 settings.setValue(settingsKeyStub + "suffix", suffix); 00470 settings.setValue("lastpath", path); 00471 } 00472 } 00473 00474 QString 00475 InteractiveFileFinder::find(FileType type, QString location, QString lastKnownLocation) 00476 { 00477 if (FileSource::canHandleScheme(location)) { 00478 if (FileSource(location).isAvailable()) { 00479 SVDEBUG << "InteractiveFileFinder::find: ok, it's available... returning" << endl; 00480 return location; 00481 } 00482 } 00483 00484 if (QFileInfo(location).exists()) return location; 00485 00486 QString foundAt = ""; 00487 00488 if ((foundAt = findRelative(location, lastKnownLocation)) != "") { 00489 return foundAt; 00490 } 00491 00492 if ((foundAt = findRelative(location, m_lastLocatedLocation)) != "") { 00493 return foundAt; 00494 } 00495 00496 return locateInteractive(type, location); 00497 } 00498 00499 QString 00500 InteractiveFileFinder::findRelative(QString location, QString relativeTo) 00501 { 00502 if (relativeTo == "") return ""; 00503 00504 SVDEBUG << "Looking for \"" << location << "\" next to \"" 00505 << relativeTo << "\"..." << endl; 00506 00507 QString fileName; 00508 QString resolved; 00509 00510 if (FileSource::isRemote(location)) { 00511 fileName = QUrl(location).path().section('/', -1, -1, 00512 QString::SectionSkipEmpty); 00513 } else { 00514 if (QUrl(location).scheme() == "file") { 00515 location = QUrl(location).toLocalFile(); 00516 } 00517 fileName = QFileInfo(location).fileName(); 00518 } 00519 00520 if (FileSource::isRemote(relativeTo)) { 00521 resolved = QUrl(relativeTo).resolved(fileName).toString(); 00522 if (!FileSource(resolved).isAvailable()) resolved = ""; 00523 cerr << "resolved: " << resolved << endl; 00524 } else { 00525 if (QUrl(relativeTo).scheme() == "file") { 00526 relativeTo = QUrl(relativeTo).toLocalFile(); 00527 } 00528 resolved = QFileInfo(relativeTo).dir().filePath(fileName); 00529 if (!QFileInfo(resolved).exists() || 00530 !QFileInfo(resolved).isFile() || 00531 !QFileInfo(resolved).isReadable()) { 00532 resolved = ""; 00533 } 00534 } 00535 00536 return resolved; 00537 } 00538 00539 QString 00540 InteractiveFileFinder::locateInteractive(FileType type, QString thing) 00541 { 00542 QString question; 00543 if (type == AudioFile) { 00544 question = tr("<b>File not found</b><p>Audio file \"%1\" could not be opened.\nDo you want to locate it?"); 00545 } else { 00546 question = tr("<b>File not found</b><p>File \"%1\" could not be opened.\nDo you want to locate it?"); 00547 } 00548 00549 QString path = ""; 00550 bool done = false; 00551 00552 while (!done) { 00553 00554 int rv = QMessageBox::question 00555 (0, 00556 tr("Failed to open file"), 00557 question.arg(thing), 00558 tr("Locate file..."), 00559 tr("Use URL..."), 00560 tr("Cancel"), 00561 0, 2); 00562 00563 switch (rv) { 00564 00565 case 0: // Locate file 00566 00567 if (QFileInfo(thing).dir().exists()) { 00568 path = QFileInfo(thing).dir().canonicalPath(); 00569 } 00570 00571 path = getOpenFileName(type, path); 00572 done = (path != ""); 00573 break; 00574 00575 case 1: // Use URL 00576 { 00577 bool ok = false; 00578 path = QInputDialog::getText 00579 (0, tr("Use URL"), 00580 tr("Please enter the URL to use for this file:"), 00581 QLineEdit::Normal, "", &ok); 00582 00583 if (ok && path != "") { 00584 if (FileSource(path).isAvailable()) { 00585 done = true; 00586 } else { 00587 QMessageBox::critical 00588 (0, tr("Failed to open location"), 00589 tr("<b>Failed to open location</b><p>URL \"%1\" could not be opened").arg(path)); 00590 path = ""; 00591 } 00592 } 00593 break; 00594 } 00595 00596 case 2: // Cancel 00597 path = ""; 00598 done = true; 00599 break; 00600 } 00601 } 00602 00603 if (path != "") m_lastLocatedLocation = path; 00604 return path; 00605 } 00606 00607