Mercurial > hg > svgui
view widgets/InteractiveFileFinder.cpp @ 1497:175770a13495
Show relative pitch if recorded
author | Chris Cannam |
---|---|
date | Thu, 15 Aug 2019 18:18:22 +0100 |
parents | c268fe2ad597 |
children |
line wrap: on
line source
/* -*- c-basic-offset: 4 indent-tabs-mode: nil -*- vi:set ts=8 sts=4 sw=4: */ /* Sonic Visualiser An audio file viewer and annotation editor. Centre for Digital Music, Queen Mary, University of London. This file copyright 2007 QMUL. This program is free software; you can redistribute it and/or modify it under the terms of the GNU General Public License as published by the Free Software Foundation; either version 2 of the License, or (at your option) any later version. See the file COPYING included with this distribution for more information. */ #include "InteractiveFileFinder.h" #include "data/fileio/FileSource.h" #include "data/fileio/AudioFileReaderFactory.h" #include "data/fileio/DataFileReaderFactory.h" #include "rdf/RDFImporter.h" #include "rdf/RDFExporter.h" #include "system/System.h" #include <QFileInfo> #include <QMessageBox> #include <QFileDialog> #include <QInputDialog> #include <QImageReader> #include <QSettings> #include <iostream> InteractiveFileFinder InteractiveFileFinder::m_instance; InteractiveFileFinder::InteractiveFileFinder() : m_sessionExtension("sv"), m_lastLocatedLocation(""), m_parent(nullptr) { FileFinder::registerFileFinder(this); } InteractiveFileFinder::~InteractiveFileFinder() { } void InteractiveFileFinder::setParentWidget(QWidget *parent) { getInstance()->m_parent = parent; } void InteractiveFileFinder::setApplicationSessionExtension(QString extension) { m_sessionExtension = extension; } QString InteractiveFileFinder::getOpenFileName(FileType type, QString fallbackLocation) { QStringList names = getOpenFileNames(type, fallbackLocation, false); if (names.empty()) return ""; else return names[0]; } QStringList InteractiveFileFinder::getOpenFileNames(FileType type, QString fallbackLocation) { return getOpenFileNames(type, fallbackLocation, true); } QStringList InteractiveFileFinder::getOpenFileNames(FileType type, QString fallbackLocation, bool multiple) { QString settingsKeyStub; QString lastPath = fallbackLocation; QString title; if (multiple) { title = tr("Select one or more files"); } else { title = tr("Select file"); } QString filter = tr("All files (*.*)"); QStringList names; switch (type) { case SessionFile: settingsKeyStub = "session"; if (multiple) { title = tr("Select one or more session files"); } else { title = tr("Select a session file"); } filter = tr("%1 session files (*.%2)\nRDF files (%3)\nAll files (*.*)") .arg(QApplication::applicationName()) .arg(m_sessionExtension) .arg(RDFImporter::getKnownExtensions()); break; case AudioFile: settingsKeyStub = "audio"; if (multiple) { title = tr("Select one or more audio files"); } else { title = tr("Select an audio file"); } filter = tr("Audio files (%1)\nAll files (*.*)") .arg(AudioFileReaderFactory::getKnownExtensions()); break; case LayerFile: settingsKeyStub = "layer"; 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 (*.*)") .arg(DataFileReaderFactory::getKnownExtensions()) .arg(RDFImporter::getKnownExtensions()); break; case LayerFileNoMidi: settingsKeyStub = "layer"; 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 (*.*)") .arg(DataFileReaderFactory::getKnownExtensions()) .arg(RDFImporter::getKnownExtensions()); break; case LayerFileNonSV: settingsKeyStub = "layer"; 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 (*.*)") .arg(DataFileReaderFactory::getKnownExtensions()) .arg(RDFImporter::getKnownExtensions()); break; case LayerFileNoMidiNonSV: settingsKeyStub = "layer"; 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 (*.*)") .arg(DataFileReaderFactory::getKnownExtensions()) .arg(RDFImporter::getKnownExtensions()); break; case SessionOrAudioFile: settingsKeyStub = "last"; filter = tr("All supported files (*.%1 %2 %3)\n%4 session files (*.%1)\nAudio files (%3)\nRDF files (%2)\nAll files (*.*)") .arg(m_sessionExtension) .arg(RDFImporter::getKnownExtensions()) .arg(AudioFileReaderFactory::getKnownExtensions()) .arg(QApplication::applicationName()); break; case ImageFile: settingsKeyStub = "image"; { QStringList fmts; QList<QByteArray> formats = QImageReader::supportedImageFormats(); for (QList<QByteArray>::iterator i = formats.begin(); i != formats.end(); ++i) { fmts.push_back(QString("*.%1") .arg(QString::fromLocal8Bit(*i).toLower())); } filter = tr("Image files (%1)\nAll files (*.*)").arg(fmts.join(" ")); } break; case SVGFile: settingsKeyStub = "svg"; filter = tr("Scalable Vector Graphics files (*.svg)\nAll files (*.*)"); break; case CSVFile: settingsKeyStub = "layer"; filter = tr("Comma-separated data files (*.csv)\nSpace-separated .lab files (*.lab)\nText files (*.txt)\nAll files (*.*)"); break; case AnyFile: settingsKeyStub = "last"; filter = tr("All supported files (*.%1 %2 %3 %4)\n%5 session files (*.%1)\nAudio files (%2)\nLayer files (%3)\nRDF files (%4)\nAll files (*.*)") .arg(m_sessionExtension) .arg(AudioFileReaderFactory::getKnownExtensions()) .arg(DataFileReaderFactory::getKnownExtensions()) .arg(RDFImporter::getKnownExtensions()) .arg(QApplication::applicationName()); break; }; if (lastPath == "") { std::string home; if (getEnvUtf8("HOME", home)) { lastPath = QString::fromStdString(home); } else { lastPath = "."; } } else if (QFileInfo(lastPath).isDir()) { lastPath = QFileInfo(lastPath).canonicalPath(); } else { lastPath = QFileInfo(lastPath).absoluteDir().canonicalPath(); } QSettings settings; settings.beginGroup("FileFinder"); lastPath = settings.value(settingsKeyStub + "path", lastPath).toString(); // Use our own QFileDialog just for symmetry with getSaveFileName below QFileDialog dialog(m_parent); dialog.setNameFilters(filter.split('\n')); dialog.setWindowTitle(title); dialog.setDirectory(lastPath); dialog.setAcceptMode(QFileDialog::AcceptOpen); if (multiple) { dialog.setFileMode(QFileDialog::ExistingFiles); } else { dialog.setFileMode(QFileDialog::ExistingFile); } QString testPath = ""; QString pathToRemember = ""; if (dialog.exec()) { names = dialog.selectedFiles(); if (!multiple && !names.empty()) { testPath = *names.begin(); QFileInfo fi(testPath); if (!fi.exists()) { QMessageBox::critical(nullptr, tr("File does not exist"), tr("<b>File not found</b><p>File \"%1\" does not exist").arg(testPath)); } else if (!fi.isReadable()) { QMessageBox::critical(nullptr, tr("File is not readable"), tr("<b>File is not readable</b><p>File \"%1\" can not be read").arg(testPath)); } else if (fi.isDir()) { QMessageBox::critical(nullptr, tr("Directory selected"), tr("<b>Directory selected</b><p>File \"%1\" is a directory").arg(testPath)); } else if (!fi.isFile()) { QMessageBox::critical(nullptr, tr("Non-file selected"), tr("<b>Not a file</b><p>Path \"%1\" is not a file").arg(testPath)); } else if (fi.size() == 0) { QMessageBox::critical(nullptr, tr("File is empty"), tr("<b>File is empty</b><p>File \"%1\" is empty").arg(testPath)); } else { pathToRemember = testPath; } } } if (pathToRemember != "") { settings.setValue(settingsKeyStub + "path", QFileInfo(pathToRemember) .absoluteDir() .canonicalPath()); } return names; } QString InteractiveFileFinder::getSaveFileName(FileType type, QString fallbackLocation) { QString settingsKeyStub; QString lastPath = fallbackLocation; QString title = tr("Select file"); QString filter = tr("All files (*.*)"); switch (type) { case SessionFile: settingsKeyStub = "savesession"; title = tr("Select a session file"); filter = tr("%1 session files (*.%2)\nAll files (*.*)") .arg(QApplication::applicationName()) .arg(m_sessionExtension); break; case AudioFile: settingsKeyStub = "saveaudio"; title = "Select an audio file"; title = tr("Select a file to export to"); filter = tr("WAV audio files (*.wav)\nAll files (*.*)"); break; case LayerFile: settingsKeyStub = "savelayer"; title = tr("Select a file to export to"); 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()); break; case LayerFileNoMidi: settingsKeyStub = "savelayer"; title = tr("Select a file to export to"); 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()); break; case LayerFileNonSV: settingsKeyStub = "savelayer"; title = tr("Select a file to export to"); 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()); break; case LayerFileNoMidiNonSV: settingsKeyStub = "savelayer"; title = tr("Select a file to export to"); 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()); break; case SessionOrAudioFile: cerr << "ERROR: Internal error: InteractiveFileFinder::getSaveFileName: SessionOrAudioFile cannot be used here" << endl; abort(); case ImageFile: settingsKeyStub = "saveimage"; title = tr("Select a file to export to"); filter = tr("Portable Network Graphics files (*.png)\nAll files (*.*)"); break; case SVGFile: settingsKeyStub = "savesvg"; title = tr("Select a file to export to"); filter = tr("Scalable Vector Graphics files (*.svg)\nAll files (*.*)"); break; case CSVFile: settingsKeyStub = "savelayer"; title = tr("Select a file to export to"); filter = tr("Comma-separated data files (*.csv)\nText files (*.txt)\nAll files (*.*)"); break; case AnyFile: cerr << "ERROR: Internal error: InteractiveFileFinder::getSaveFileName: AnyFile cannot be used here" << endl; abort(); }; if (lastPath == "") { std::string home; if (getEnvUtf8("HOME", home)) { lastPath = QString::fromStdString(home); } else { lastPath = "."; } } else if (QFileInfo(lastPath).isDir()) { lastPath = QFileInfo(lastPath).canonicalPath(); } else { lastPath = QFileInfo(lastPath).absoluteDir().canonicalPath(); } QSettings settings; settings.beginGroup("FileFinder"); lastPath = settings.value(settingsKeyStub + "path", lastPath).toString(); QString path = ""; // Use our own QFileDialog instead of static functions, as we may // need to adjust the file extension based on the selected filter QFileDialog dialog(m_parent); QStringList filters = filter.split('\n'); dialog.setNameFilters(filters); dialog.setWindowTitle(title); dialog.setDirectory(lastPath); dialog.setAcceptMode(QFileDialog::AcceptSave); dialog.setFileMode(QFileDialog::AnyFile); dialog.setOption(QFileDialog::DontConfirmOverwrite, true); // we'll do that QString defaultSuffix; if (type == SessionFile) { defaultSuffix = m_sessionExtension; } else if (type == AudioFile) { defaultSuffix = "wav"; } else if (type == ImageFile) { defaultSuffix = "png"; } else if (type == SVGFile) { defaultSuffix = "svg"; } else if (type == CSVFile) { defaultSuffix = "csv"; } defaultSuffix = settings.value(settingsKeyStub + "suffix", defaultSuffix).toString(); dialog.setDefaultSuffix(defaultSuffix); foreach (QString f, filters) { if (f.contains("." + defaultSuffix)) { dialog.selectNameFilter(f); } } bool good = false; while (!good) { path = ""; if (!dialog.exec()) break; QStringList files = dialog.selectedFiles(); if (files.empty()) break; path = *files.begin(); QFileInfo fi(path); cerr << "type = " << type << ", suffix = " << fi.suffix() << endl; if ((type == LayerFile || type == LayerFileNoMidi || type == LayerFileNonSV || type == LayerFileNoMidiNonSV) && fi.suffix() == "") { QString expectedExtension; QString selectedFilter = dialog.selectedNameFilter(); if (selectedFilter.contains(".svl")) { expectedExtension = "svl"; } else if (selectedFilter.contains(".txt")) { expectedExtension = "txt"; } else if (selectedFilter.contains(".csv")) { expectedExtension = "csv"; } else if (selectedFilter.contains(".mid")) { expectedExtension = "mid"; } else if (selectedFilter.contains(".ttl")) { expectedExtension = "ttl"; } cerr << "expected extension = " << expectedExtension << endl; if (expectedExtension != "") { path = QString("%1.%2").arg(path).arg(expectedExtension); fi = QFileInfo(path); } } if (fi.isDir()) { QMessageBox::critical(nullptr, tr("Directory selected"), tr("<b>Directory selected</b><p>File \"%1\" is a directory").arg(path)); continue; } if (fi.exists()) { if (QMessageBox::question(nullptr, tr("File exists"), tr("<b>File exists</b><p>The file \"%1\" already exists.\nDo you want to overwrite it?").arg(path), QMessageBox::Ok, QMessageBox::Cancel) != QMessageBox::Ok) { continue; } } good = true; } if (path != "") { settings.setValue(settingsKeyStub + "path", QFileInfo(path).absoluteDir().canonicalPath()); settings.setValue(settingsKeyStub + "suffix", QFileInfo(path).suffix()); } return path; } void InteractiveFileFinder::registerLastOpenedFilePath(FileType type, QString path) { QString settingsKeyStub; switch (type) { case SessionFile: settingsKeyStub = "session"; break; case AudioFile: settingsKeyStub = "audio"; break; case LayerFile: settingsKeyStub = "layer"; break; case LayerFileNoMidi: settingsKeyStub = "layer"; break; case LayerFileNonSV: settingsKeyStub = "layer"; break; case LayerFileNoMidiNonSV: settingsKeyStub = "layer"; break; case SessionOrAudioFile: settingsKeyStub = "last"; break; case ImageFile: settingsKeyStub = "image"; break; case SVGFile: settingsKeyStub = "svg"; break; case CSVFile: settingsKeyStub = "layer"; break; case AnyFile: settingsKeyStub = "last"; break; } if (path != "") { QSettings settings; settings.beginGroup("FileFinder"); path = QFileInfo(path).absoluteDir().canonicalPath(); QString suffix = QFileInfo(path).suffix(); settings.setValue(settingsKeyStub + "path", path); settings.setValue(settingsKeyStub + "suffix", suffix); settings.setValue("lastpath", path); } } QString InteractiveFileFinder::find(FileType type, QString location, QString lastKnownLocation) { if (FileSource::canHandleScheme(location)) { if (FileSource(location).isAvailable()) { SVDEBUG << "InteractiveFileFinder::find: ok, it's available... returning" << endl; return location; } } if (QFileInfo(location).exists()) return location; QString foundAt = ""; if ((foundAt = findRelative(location, lastKnownLocation)) != "") { return foundAt; } if ((foundAt = findRelative(location, m_lastLocatedLocation)) != "") { return foundAt; } return locateInteractive(type, location); } QString InteractiveFileFinder::findRelative(QString location, QString relativeTo) { if (relativeTo == "") return ""; SVDEBUG << "Looking for \"" << location << "\" next to \"" << relativeTo << "\"..." << endl; QString fileName; QString resolved; if (FileSource::isRemote(location)) { fileName = QUrl(location).path().section('/', -1, -1, QString::SectionSkipEmpty); } else { if (QUrl(location).scheme() == "file") { location = QUrl(location).toLocalFile(); } fileName = QFileInfo(location).fileName(); } if (FileSource::isRemote(relativeTo)) { resolved = QUrl(relativeTo).resolved(fileName).toString(); if (!FileSource(resolved).isAvailable()) resolved = ""; cerr << "resolved: " << resolved << endl; } else { if (QUrl(relativeTo).scheme() == "file") { relativeTo = QUrl(relativeTo).toLocalFile(); } resolved = QFileInfo(relativeTo).dir().filePath(fileName); if (!QFileInfo(resolved).exists() || !QFileInfo(resolved).isFile() || !QFileInfo(resolved).isReadable()) { resolved = ""; } } return resolved; } QString InteractiveFileFinder::locateInteractive(FileType type, QString thing) { QString question; if (type == AudioFile) { question = tr("<b>File not found</b><p>Audio file \"%1\" could not be opened.\nDo you want to locate it?"); } else { question = tr("<b>File not found</b><p>File \"%1\" could not be opened.\nDo you want to locate it?"); } QString path = ""; bool done = false; while (!done) { int rv = QMessageBox::question (nullptr, tr("Failed to open file"), question.arg(thing), tr("Locate file..."), tr("Use URL..."), tr("Cancel"), 0, 2); switch (rv) { case 0: // Locate file if (QFileInfo(thing).dir().exists()) { path = QFileInfo(thing).dir().canonicalPath(); } path = getOpenFileName(type, path); done = (path != ""); break; case 1: // Use URL { bool ok = false; path = QInputDialog::getText (nullptr, tr("Use URL"), tr("Please enter the URL to use for this file:"), QLineEdit::Normal, "", &ok); if (ok && path != "") { if (FileSource(path).isAvailable()) { done = true; } else { QMessageBox::critical (nullptr, tr("Failed to open location"), tr("<b>Failed to open location</b><p>URL \"%1\" could not be opened").arg(path)); path = ""; } } break; } case 2: // Cancel path = ""; done = true; break; } } if (path != "") m_lastLocatedLocation = path; return path; }