# HG changeset patch # User Chris Cannam # Date 1205514861 0 # Node ID 22b72f0f6a4e34845cd4e6b527f5612577885aeb # Parent 0bcb449d15f43092be5e1033329d6f29021ba425 * More work to abstract out interactive components used in the data library, so that it does not need to depend on QtGui. diff -r 0bcb449d15f4 -r 22b72f0f6a4e layer/ImageLayer.cpp --- a/layer/ImageLayer.cpp Thu Mar 13 14:06:03 2008 +0000 +++ b/layer/ImageLayer.cpp Fri Mar 14 17:14:21 2008 +0000 @@ -24,6 +24,7 @@ #include "data/fileio/FileSource.h" #include "widgets/ImageDialog.h" +#include "widgets/ProgressDialog.h" #include #include @@ -912,7 +913,8 @@ return; } - FileSource *rf = new FileSource(img, FileSource::ProgressDialog); + ProgressDialog dialog(tr("Opening image URL..."), true, 2000); + FileSource *rf = new FileSource(img, &dialog); if (rf->isOK()) { std::cerr << "ok, adding it (local filename = " << rf->getLocalFilename().toStdString() << ")" << std::endl; m_remoteFiles[img] = rf; diff -r 0bcb449d15f4 -r 22b72f0f6a4e widgets/CSVFormatDialog.cpp --- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/widgets/CSVFormatDialog.cpp Fri Mar 14 17:14:21 2008 +0000 @@ -0,0 +1,270 @@ +/* -*- 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 2006 Chris Cannam. + + 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 "CSVFormatDialog.h" + +#include +#include +#include +#include +#include +#include +#include +#include + + +CSVFormatDialog::CSVFormatDialog(QWidget *parent, CSVFormat format, + size_t defaultSampleRate) : + QDialog(parent), + m_modelType(CSVFormat::OneDimensionalModel), + m_timingType(CSVFormat::ExplicitTiming), + m_timeUnits(CSVFormat::TimeAudioFrames), + m_separator(""), + m_behaviour(QString::KeepEmptyParts) +{ + setModal(true); + setWindowTitle(tr("Select Data Format")); + + m_modelType = format.getModelType(); + m_timingType = format.getTimingType(); + m_timeUnits = format.getTimeUnits(); + m_separator = format.getSeparator(); + m_sampleRate = format.getSampleRate(); + m_windowSize = format.getWindowSize(); + m_behaviour = format.getSplitBehaviour(); + m_example = format.getExample(); + m_maxExampleCols = format.getMaxExampleCols(); + + QGridLayout *layout = new QGridLayout; + + layout->addWidget(new QLabel(tr("Select Data Format

Please select the correct data format for this file.")), + 0, 0, 1, 4); + + layout->addWidget(new QLabel(tr("Each row specifies:")), 1, 0); + + m_modelTypeCombo = new QComboBox; + m_modelTypeCombo->addItem(tr("A point in time")); + m_modelTypeCombo->addItem(tr("A value at a time")); + m_modelTypeCombo->addItem(tr("A set of values")); + layout->addWidget(m_modelTypeCombo, 1, 1, 1, 2); + connect(m_modelTypeCombo, SIGNAL(activated(int)), + this, SLOT(modelTypeChanged(int))); + m_modelTypeCombo->setCurrentIndex(int(m_modelType)); + + layout->addWidget(new QLabel(tr("The first column contains:")), 2, 0); + + m_timingTypeCombo = new QComboBox; + m_timingTypeCombo->addItem(tr("Time, in seconds")); + m_timingTypeCombo->addItem(tr("Time, in audio sample frames")); + m_timingTypeCombo->addItem(tr("Data (rows are consecutive in time)")); + layout->addWidget(m_timingTypeCombo, 2, 1, 1, 2); + connect(m_timingTypeCombo, SIGNAL(activated(int)), + this, SLOT(timingTypeChanged(int))); + m_timingTypeCombo->setCurrentIndex(m_timingType == CSVFormat::ExplicitTiming ? + m_timeUnits == CSVFormat::TimeSeconds ? 0 : 1 : 2); + + m_sampleRateLabel = new QLabel(tr("Audio sample rate (Hz):")); + layout->addWidget(m_sampleRateLabel, 3, 0); + + size_t sampleRates[] = { + 8000, 11025, 12000, 22050, 24000, 32000, + 44100, 48000, 88200, 96000, 176400, 192000 + }; + + m_sampleRateCombo = new QComboBox; + m_sampleRate = defaultSampleRate; + for (size_t i = 0; i < sizeof(sampleRates) / sizeof(sampleRates[0]); ++i) { + m_sampleRateCombo->addItem(QString("%1").arg(sampleRates[i])); + if (sampleRates[i] == m_sampleRate) m_sampleRateCombo->setCurrentIndex(i); + } + m_sampleRateCombo->setEditable(true); + + layout->addWidget(m_sampleRateCombo, 3, 1); + connect(m_sampleRateCombo, SIGNAL(activated(QString)), + this, SLOT(sampleRateChanged(QString))); + connect(m_sampleRateCombo, SIGNAL(editTextChanged(QString)), + this, SLOT(sampleRateChanged(QString))); + + m_windowSizeLabel = new QLabel(tr("Frame increment between rows:")); + layout->addWidget(m_windowSizeLabel, 4, 0); + + m_windowSizeCombo = new QComboBox; + m_windowSize = 1024; + for (int i = 0; i <= 16; ++i) { + int value = 1 << i; + m_windowSizeCombo->addItem(QString("%1").arg(value)); + if (value == int(m_windowSize)) m_windowSizeCombo->setCurrentIndex(i); + } + m_windowSizeCombo->setEditable(true); + + layout->addWidget(m_windowSizeCombo, 4, 1); + connect(m_windowSizeCombo, SIGNAL(activated(QString)), + this, SLOT(windowSizeChanged(QString))); + connect(m_windowSizeCombo, SIGNAL(editTextChanged(QString)), + this, SLOT(windowSizeChanged(QString))); + + layout->addWidget(new QLabel(tr("\nExample data from file:")), 5, 0, 1, 4); + + m_exampleWidget = new QTableWidget + (std::min(10, m_example.size()), m_maxExampleCols); + + layout->addWidget(m_exampleWidget, 6, 0, 1, 4); + layout->setColumnStretch(3, 10); + layout->setRowStretch(4, 10); + + QPushButton *ok = new QPushButton(tr("OK")); + connect(ok, SIGNAL(clicked()), this, SLOT(accept())); + ok->setDefault(true); + + QPushButton *cancel = new QPushButton(tr("Cancel")); + connect(cancel, SIGNAL(clicked()), this, SLOT(reject())); + + QHBoxLayout *buttonLayout = new QHBoxLayout; + buttonLayout->addStretch(1); + buttonLayout->addWidget(ok); + buttonLayout->addWidget(cancel); + + QVBoxLayout *mainLayout = new QVBoxLayout; + mainLayout->addLayout(layout); + mainLayout->addLayout(buttonLayout); + + setLayout(mainLayout); + + timingTypeChanged(m_timingTypeCombo->currentIndex()); +} + +CSVFormatDialog::~CSVFormatDialog() +{ +} + +CSVFormat +CSVFormatDialog::getFormat() const +{ + CSVFormat format; + format.setModelType(m_modelType); + format.setTimingType(m_timingType); + format.setTimeUnits(m_timeUnits); + format.setSeparator(m_separator); + format.setSampleRate(m_sampleRate); + format.setWindowSize(m_windowSize); + format.setSplitBehaviour(m_behaviour); + return format; +} + +void +CSVFormatDialog::populateExample() +{ + m_exampleWidget->setColumnCount + (m_timingType == CSVFormat::ExplicitTiming ? + m_maxExampleCols - 1 : m_maxExampleCols); + + m_exampleWidget->setHorizontalHeaderLabels(QStringList()); + + for (int i = 0; i < m_example.size(); ++i) { + for (int j = 0; j < m_example[i].size(); ++j) { + + QTableWidgetItem *item = new QTableWidgetItem(m_example[i][j]); + + if (j == 0) { + if (m_timingType == CSVFormat::ExplicitTiming) { + m_exampleWidget->setVerticalHeaderItem(i, item); + continue; + } else { + QTableWidgetItem *header = + new QTableWidgetItem(QString("%1").arg(i)); + header->setFlags(Qt::ItemIsEnabled); + m_exampleWidget->setVerticalHeaderItem(i, header); + } + } + int index = j; + if (m_timingType == CSVFormat::ExplicitTiming) --index; + item->setFlags(Qt::ItemIsEnabled); + m_exampleWidget->setItem(i, index, item); + } + } +} + +void +CSVFormatDialog::modelTypeChanged(int type) +{ + m_modelType = (CSVFormat::ModelType)type; + + if (m_modelType == CSVFormat::ThreeDimensionalModel) { + // We can't load 3d models with explicit timing, because the 3d + // model is dense so we need a fixed sample increment + m_timingTypeCombo->setCurrentIndex(2); + timingTypeChanged(2); + } +} + +void +CSVFormatDialog::timingTypeChanged(int type) +{ + switch (type) { + + case 0: + m_timingType = CSVFormat::ExplicitTiming; + m_timeUnits = CSVFormat::TimeSeconds; + m_sampleRateCombo->setEnabled(false); + m_sampleRateLabel->setEnabled(false); + m_windowSizeCombo->setEnabled(false); + m_windowSizeLabel->setEnabled(false); + if (m_modelType == CSVFormat::ThreeDimensionalModel) { + m_modelTypeCombo->setCurrentIndex(1); + modelTypeChanged(1); + } + break; + + case 1: + m_timingType = CSVFormat::ExplicitTiming; + m_timeUnits = CSVFormat::TimeAudioFrames; + m_sampleRateCombo->setEnabled(true); + m_sampleRateLabel->setEnabled(true); + m_windowSizeCombo->setEnabled(false); + m_windowSizeLabel->setEnabled(false); + if (m_modelType == CSVFormat::ThreeDimensionalModel) { + m_modelTypeCombo->setCurrentIndex(1); + modelTypeChanged(1); + } + break; + + case 2: + m_timingType = CSVFormat::ImplicitTiming; + m_timeUnits = CSVFormat::TimeWindows; + m_sampleRateCombo->setEnabled(true); + m_sampleRateLabel->setEnabled(true); + m_windowSizeCombo->setEnabled(true); + m_windowSizeLabel->setEnabled(true); + break; + } + + populateExample(); +} + +void +CSVFormatDialog::sampleRateChanged(QString rateString) +{ + bool ok = false; + int sampleRate = rateString.toInt(&ok); + if (ok) m_sampleRate = sampleRate; +} + +void +CSVFormatDialog::windowSizeChanged(QString sizeString) +{ + bool ok = false; + int size = sizeString.toInt(&ok); + if (ok) m_windowSize = size; +} diff -r 0bcb449d15f4 -r 22b72f0f6a4e widgets/CSVFormatDialog.h --- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/widgets/CSVFormatDialog.h Fri Mar 14 17:14:21 2008 +0000 @@ -0,0 +1,69 @@ +/* -*- 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 2006 Chris Cannam. + + 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. +*/ + +#ifndef _CSV_FORMAT_DIALOG_H_ +#define _CSV_FORMAT_DIALOG_H_ + +#include "data/fileio/CSVFormat.h" + +class QTableWidget; +class QComboBox; +class QLabel; + +#include + +class CSVFormatDialog : public QDialog +{ + Q_OBJECT + +public: + CSVFormatDialog(QWidget *parent, CSVFormat initialFormat, + size_t defaultSampleRate); + ~CSVFormatDialog(); + + CSVFormat getFormat() const; + +protected slots: + void modelTypeChanged(int type); + void timingTypeChanged(int type); + void sampleRateChanged(QString); + void windowSizeChanged(QString); + +protected: + CSVFormat::ModelType m_modelType; + CSVFormat::TimingType m_timingType; + CSVFormat::TimeUnits m_timeUnits; + + QString m_separator; + size_t m_sampleRate; + size_t m_windowSize; + + QString::SplitBehavior m_behaviour; + + QList m_example; + int m_maxExampleCols; + QTableWidget *m_exampleWidget; + + QComboBox *m_modelTypeCombo; + QComboBox *m_timingTypeCombo; + QLabel *m_sampleRateLabel; + QComboBox *m_sampleRateCombo; + QLabel *m_windowSizeLabel; + QComboBox *m_windowSizeCombo; + + void populateExample(); +}; + +#endif diff -r 0bcb449d15f4 -r 22b72f0f6a4e widgets/FileFinder.cpp --- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/widgets/FileFinder.cpp Fri Mar 14 17:14:21 2008 +0000 @@ -0,0 +1,511 @@ +/* -*- 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 "FileFinder.h" +#include "data/fileio/FileSource.h" +#include "data/fileio/AudioFileReaderFactory.h" +#include "data/fileio/DataFileReaderFactory.h" + +#include +#include +#include +#include +#include +#include + +#include + +FileFinder * +FileFinder::m_instance = 0; + +FileFinder::FileFinder() : + m_lastLocatedLocation("") +{ +} + +FileFinder::~FileFinder() +{ +} + +FileFinder * +FileFinder::getInstance() +{ + if (m_instance == 0) { + m_instance = new FileFinder(); + } + return m_instance; +} + +QString +FileFinder::getOpenFileName(FileType type, QString fallbackLocation) +{ + QString settingsKey; + QString lastPath = fallbackLocation; + + QString title = tr("Select file"); + QString filter = tr("All files (*.*)"); + + switch (type) { + + case SessionFile: + settingsKey = "sessionpath"; + title = tr("Select a session file"); + filter = tr("Sonic Visualiser session files (*.sv)\nAll files (*.*)"); + break; + + case AudioFile: + settingsKey = "audiopath"; + title = "Select an audio file"; + filter = tr("Audio files (%1)\nAll files (*.*)") + .arg(AudioFileReaderFactory::getKnownExtensions()); + break; + + case LayerFile: + settingsKey = "layerpath"; + 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()); + break; + + case LayerFileNoMidi: + settingsKey = "layerpath"; + 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()); + break; + + case SessionOrAudioFile: + settingsKey = "lastpath"; + filter = tr("All supported files (*.sv %1)\nSonic Visualiser session files (*.sv)\nAudio files (%1)\nAll files (*.*)") + .arg(AudioFileReaderFactory::getKnownExtensions()); + break; + + case ImageFile: + settingsKey = "imagepath"; + { + QStringList fmts; + QList formats = QImageReader::supportedImageFormats(); + for (QList::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 AnyFile: + settingsKey = "lastpath"; + filter = tr("All supported files (*.sv %1 %2)\nSonic Visualiser session files (*.sv)\nAudio files (%1)\nLayer files (%2)\nAll files (*.*)") + .arg(AudioFileReaderFactory::getKnownExtensions()) + .arg(DataFileReaderFactory::getKnownExtensions()); + break; + }; + + if (lastPath == "") { + char *home = getenv("HOME"); + if (home) lastPath = 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(settingsKey, lastPath).toString(); + + QString path = ""; + + // Use our own QFileDialog just for symmetry with getSaveFileName below + + QFileDialog dialog; + dialog.setFilters(filter.split('\n')); + dialog.setWindowTitle(title); + dialog.setDirectory(lastPath); + + dialog.setAcceptMode(QFileDialog::AcceptOpen); + dialog.setFileMode(QFileDialog::ExistingFile); + + if (dialog.exec()) { + QStringList files = dialog.selectedFiles(); + if (!files.empty()) path = *files.begin(); + + QFileInfo fi(path); + + if (!fi.exists()) { + + QMessageBox::critical(0, tr("File does not exist"), + tr("File \"%1\" does not exist").arg(path)); + path = ""; + + } else if (!fi.isReadable()) { + + QMessageBox::critical(0, tr("File is not readable"), + tr("File \"%1\" can not be read").arg(path)); + path = ""; + + } else if (fi.isDir()) { + + QMessageBox::critical(0, tr("Directory selected"), + tr("File \"%1\" is a directory").arg(path)); + path = ""; + + } else if (!fi.isFile()) { + + QMessageBox::critical(0, tr("Non-file selected"), + tr("Path \"%1\" is not a file").arg(path)); + path = ""; + + } else if (fi.size() == 0) { + + QMessageBox::critical(0, tr("File is empty"), + tr("File \"%1\" is empty").arg(path)); + path = ""; + } + } + + if (path != "") { + settings.setValue(settingsKey, + QFileInfo(path).absoluteDir().canonicalPath()); + } + + return path; +} + +QString +FileFinder::getSaveFileName(FileType type, QString fallbackLocation) +{ + QString settingsKey; + QString lastPath = fallbackLocation; + + QString title = tr("Select file"); + QString filter = tr("All files (*.*)"); + + switch (type) { + + case SessionFile: + settingsKey = "savesessionpath"; + title = tr("Select a session file"); + filter = tr("Sonic Visualiser session files (*.sv)\nAll files (*.*)"); + break; + + case AudioFile: + settingsKey = "saveaudiopath"; + title = "Select an audio file"; + title = tr("Select a file to export to"); + filter = tr("WAV audio files (*.wav)\nAll files (*.*)"); + break; + + case LayerFile: + settingsKey = "savelayerpath"; + title = tr("Select a file to export to"); + filter = tr("Sonic Visualiser Layer XML files (*.svl)\nComma-separated data files (*.csv)\nMIDI files (*.mid)\nText files (*.txt)\nAll files (*.*)"); + break; + + case LayerFileNoMidi: + settingsKey = "savelayerpath"; + title = tr("Select a file to export to"); + filter = tr("Sonic Visualiser Layer XML files (*.svl)\nComma-separated data files (*.csv)\nText files (*.txt)\nAll files (*.*)"); + break; + + case SessionOrAudioFile: + std::cerr << "ERROR: Internal error: FileFinder::getSaveFileName: SessionOrAudioFile cannot be used here" << std::endl; + abort(); + + case ImageFile: + settingsKey = "saveimagepath"; + title = tr("Select a file to export to"); + filter = tr("Portable Network Graphics files (*.png)\nAll files (*.*)"); + break; + + case AnyFile: + std::cerr << "ERROR: Internal error: FileFinder::getSaveFileName: AnyFile cannot be used here" << std::endl; + abort(); + }; + + if (lastPath == "") { + char *home = getenv("HOME"); + if (home) lastPath = 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(settingsKey, 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; + dialog.setFilters(filter.split('\n')); + dialog.setWindowTitle(title); + dialog.setDirectory(lastPath); + + dialog.setAcceptMode(QFileDialog::AcceptSave); + dialog.setFileMode(QFileDialog::AnyFile); + dialog.setConfirmOverwrite(false); // we'll do that + + if (type == SessionFile) { + dialog.setDefaultSuffix("sv"); + } else if (type == AudioFile) { + dialog.setDefaultSuffix("wav"); + } else if (type == ImageFile) { + dialog.setDefaultSuffix("png"); + } + + bool good = false; + + while (!good) { + + path = ""; + + if (!dialog.exec()) break; + + QStringList files = dialog.selectedFiles(); + if (files.empty()) break; + path = *files.begin(); + + QFileInfo fi(path); + + std::cerr << "type = " << type << ", suffix = " << fi.suffix().toStdString() << std::endl; + + if ((type == LayerFile || type == LayerFileNoMidi) + && fi.suffix() == "") { + QString expectedExtension; + QString selectedFilter = dialog.selectedFilter(); + 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"; + } + std::cerr << "expected extension = " << expectedExtension.toStdString() << std::endl; + if (expectedExtension != "") { + path = QString("%1.%2").arg(path).arg(expectedExtension); + fi = QFileInfo(path); + } + } + + if (fi.isDir()) { + QMessageBox::critical(0, tr("Directory selected"), + tr("File \"%1\" is a directory").arg(path)); + continue; + } + + if (fi.exists()) { + if (QMessageBox::question(0, tr("File exists"), + tr("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(settingsKey, + QFileInfo(path).absoluteDir().canonicalPath()); + } + + return path; +} + +void +FileFinder::registerLastOpenedFilePath(FileType type, QString path) +{ + QString settingsKey; + + switch (type) { + case SessionFile: + settingsKey = "sessionpath"; + break; + + case AudioFile: + settingsKey = "audiopath"; + break; + + case LayerFile: + settingsKey = "layerpath"; + break; + + case LayerFileNoMidi: + settingsKey = "layerpath"; + break; + + case SessionOrAudioFile: + settingsKey = "lastpath"; + break; + + case ImageFile: + settingsKey = "imagepath"; + break; + + case AnyFile: + settingsKey = "lastpath"; + break; + } + + if (path != "") { + QSettings settings; + settings.beginGroup("FileFinder"); + path = QFileInfo(path).absoluteDir().canonicalPath(); + settings.setValue(settingsKey, path); + settings.setValue("lastpath", path); + } +} + +QString +FileFinder::find(FileType type, QString location, QString lastKnownLocation) +{ + if (FileSource::canHandleScheme(location)) { + if (FileSource(location).isAvailable()) { + std::cerr << "FileFinder::find: ok, it's available... returning" << std::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 +FileFinder::findRelative(QString location, QString relativeTo) +{ + if (relativeTo == "") return ""; + + std::cerr << "Looking for \"" << location.toStdString() << "\" next to \"" + << relativeTo.toStdString() << "\"..." << std::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 = ""; + std::cerr << "resolved: " << resolved.toStdString() << std::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 +FileFinder::locateInteractive(FileType type, QString thing) +{ + QString question; + if (type == AudioFile) { + question = tr("Audio file \"%1\" could not be opened.\nDo you want to locate it?"); + } else { + question = tr("File \"%1\" could not be opened.\nDo you want to locate it?"); + } + + QString path = ""; + bool done = false; + + while (!done) { + + int rv = QMessageBox::question + (0, + 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 + (0, 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 + (0, tr("Failed to open location"), + tr("URL \"%1\" could not be opened").arg(path)); + path = ""; + } + } + break; + } + + case 2: // Cancel + path = ""; + done = true; + break; + } + } + + if (path != "") m_lastLocatedLocation = path; + return path; +} + + diff -r 0bcb449d15f4 -r 22b72f0f6a4e widgets/FileFinder.h --- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/widgets/FileFinder.h Fri Mar 14 17:14:21 2008 +0000 @@ -0,0 +1,58 @@ +/* -*- 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. +*/ + +#ifndef _FILE_FINDER_H_ +#define _FILE_FINDER_H_ + +#include +#include + +class FileFinder : public QObject +{ + Q_OBJECT + +public: + virtual ~FileFinder(); + + enum FileType { + SessionFile, + AudioFile, + LayerFile, + LayerFileNoMidi, + SessionOrAudioFile, + ImageFile, + AnyFile + }; + + QString getOpenFileName(FileType type, QString fallbackLocation = ""); + QString getSaveFileName(FileType type, QString fallbackLocation = ""); + void registerLastOpenedFilePath(FileType type, QString path); + + QString find(FileType type, QString location, QString lastKnownLocation = ""); + + static FileFinder *getInstance(); + +protected: + FileFinder(); + static FileFinder *m_instance; + + QString findRelative(QString location, QString relativeTo); + QString locateInteractive(FileType type, QString thing); + + QString m_lastLocatedLocation; +}; + +#endif + diff -r 0bcb449d15f4 -r 22b72f0f6a4e widgets/ImageDialog.cpp --- a/widgets/ImageDialog.cpp Thu Mar 13 14:06:03 2008 +0000 +++ b/widgets/ImageDialog.cpp Fri Mar 14 17:14:21 2008 +0000 @@ -26,8 +26,10 @@ #include #include +#include "ProgressDialog.h" + #include "data/fileio/FileSource.h" -#include "data/fileio/FileFinder.h" +#include "FileFinder.h" #include @@ -186,7 +188,9 @@ tr("The URL scheme \"%1\" is not supported") .arg(url.scheme())); } else { - m_remoteFile = new FileSource(url, FileSource::ProgressDialog); + + ProgressDialog dialog(tr("Opening image URL..."), true, 2000); + m_remoteFile = new FileSource(url, &dialog); m_remoteFile->waitForData(); if (!m_remoteFile->isOK()) { QMessageBox::critical(this, tr("File download failed"), diff -r 0bcb449d15f4 -r 22b72f0f6a4e widgets/MIDIFileImportDialog.cpp --- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/widgets/MIDIFileImportDialog.cpp Fri Mar 14 17:14:21 2008 +0000 @@ -0,0 +1,72 @@ +/* -*- 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 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 "MIDIFileImportDialog.h" + +#include +#include + +MIDIFileImportDialog::MIDIFileImportDialog(QWidget *parent) : + m_parent(parent) +{ +} + +MIDIFileImportDialog::TrackPreference +MIDIFileImportDialog::getTrackImportPreference(QStringList displayNames, + bool haveSomePercussion, + QString &singleTrack) const +{ + QStringList available; + + QString allTracks = tr("Merge all tracks"); + QString allNonPercussion = tr("Merge all non-percussion tracks"); + + singleTrack = ""; + + int nonTrackItems = 1; + + available << allTracks; + + if (haveSomePercussion) { + available << allNonPercussion; + ++nonTrackItems; + } + + available << displayNames; + + bool ok = false; + QString selected = QInputDialog::getItem + (0, tr("Select track or tracks to import"), + tr("Select track to import

You can only import this file as a single annotation layer, but the file contains more than one track, or notes on more than one channel.

Please select the track or merged tracks you wish to import:"), + available, 0, false, &ok); + + if (!ok || selected.isEmpty()) return ImportNothing; + + TrackPreference pref; + if (selected == allTracks) pref = MergeAllTracks; + else if (selected == allNonPercussion) pref = MergeAllNonPercussionTracks; + else { + singleTrack = selected; + pref = ImportSingleTrack; + } + + return pref; +} + +void +MIDIFileImportDialog::showError(QString error) +{ + QMessageBox::critical(0, tr("Error in MIDI file import"), error); +} + diff -r 0bcb449d15f4 -r 22b72f0f6a4e widgets/MIDIFileImportDialog.h --- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/widgets/MIDIFileImportDialog.h Fri Mar 14 17:14:21 2008 +0000 @@ -0,0 +1,38 @@ +/* -*- 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 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. +*/ + +#ifndef _MIDI_FILE_IMPORT_DIALOG_H_ +#define _MIDI_FILE_IMPORT_DIALOG_H_ + +#include + +#include "data/fileio/MIDIFileReader.h" + +class MIDIFileImportDialog : public QObject, + public MIDIFileImportPreferenceAcquirer +{ +public: + MIDIFileImportDialog(QWidget *parent = 0); + + virtual TrackPreference getTrackImportPreference + (QStringList trackNames, bool haveSomePercussion, + QString &singleTrack) const; + + virtual void showError(QString error); + +protected: + QWidget *m_parent; +}; + +#endif diff -r 0bcb449d15f4 -r 22b72f0f6a4e widgets/ProgressDialog.cpp --- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/widgets/ProgressDialog.cpp Fri Mar 14 17:14:21 2008 +0000 @@ -0,0 +1,89 @@ +/* -*- 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-2008 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 "ProgressDialog.h" + +#include +#include +#include + +ProgressDialog::ProgressDialog(QString message, bool cancellable, + int timeBeforeShow, QWidget *parent) : + m_showTimer(0), + m_timerElapsed(false) +{ + m_dialog = new QProgressDialog(message, cancellable ? tr("Cancel") : 0, + 0, 100, parent); + if (timeBeforeShow > 0) { + m_dialog->hide(); + m_showTimer = new QTimer; + connect(m_showTimer, SIGNAL(timeout()), this, SLOT(showTimerElapsed())); + m_showTimer->setSingleShot(true); + m_showTimer->start(timeBeforeShow); + } else { + m_dialog->show(); + m_dialog->raise(); + m_timerElapsed = true; + } + + if (cancellable) { + connect(m_dialog, SIGNAL(canceled()), this, SIGNAL(cancelled())); + } +} + +ProgressDialog::~ProgressDialog() +{ + delete m_showTimer; + delete m_dialog; +} + +void +ProgressDialog::setMessage(QString text) +{ + m_dialog->setLabelText(text); +} + +void +ProgressDialog::showTimerElapsed() +{ + m_timerElapsed = true; + delete m_showTimer; + m_showTimer = 0; + if (m_dialog->value() > 0) { + m_dialog->show(); + } + qApp->processEvents(); +} + +void +ProgressDialog::setProgress(int percentage) +{ + if (percentage > m_dialog->value()) { + + m_dialog->setValue(percentage); + + if (percentage >= 100) { + delete m_showTimer; + m_showTimer = 0; + m_dialog->hide(); + } else if (m_timerElapsed && !m_dialog->isVisible()) { + m_dialog->show(); + m_dialog->raise(); + } + + qApp->processEvents(); + } +} + diff -r 0bcb449d15f4 -r 22b72f0f6a4e widgets/ProgressDialog.h --- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/widgets/ProgressDialog.h Fri Mar 14 17:14:21 2008 +0000 @@ -0,0 +1,48 @@ +/* -*- 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-2008 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. +*/ + +#ifndef _PROGRESS_DIALOG_H_ + +#include "base/ProgressReporter.h" + +class QProgressDialog; +class QTimer; + +class ProgressDialog : public ProgressReporter +{ + Q_OBJECT + +public: + ProgressDialog(QString message, bool cancellable, + int timeBeforeShow = 0, QWidget *parent = 0); + virtual ~ProgressDialog(); + +signals: + void cancelled(); + +public slots: + virtual void setMessage(QString text); + virtual void setProgress(int percentage); + +protected slots: + virtual void showTimerElapsed(); + +protected: + QProgressDialog *m_dialog; + QTimer *m_showTimer; + bool m_timerElapsed; +}; + +#endif diff -r 0bcb449d15f4 -r 22b72f0f6a4e widgets/widgets.pro --- a/widgets/widgets.pro Thu Mar 13 14:06:03 2008 +0000 +++ b/widgets/widgets.pro Fri Mar 14 17:14:21 2008 +0000 @@ -17,7 +17,9 @@ HEADERS += AudioDial.h \ ColourNameDialog.h \ CommandHistory.h \ + CSVFormatDialog.h \ Fader.h \ + FileFinder.h \ IconLoader.h \ ImageDialog.h \ ItemEditDialog.h \ @@ -27,6 +29,7 @@ LayerTreeDialog.h \ LEDButton.h \ ListInputDialog.h \ + MIDIFileImportDialog.h \ NotifyingCheckBox.h \ NotifyingComboBox.h \ NotifyingPushButton.h \ @@ -34,6 +37,7 @@ Panner.h \ PluginParameterBox.h \ PluginParameterDialog.h \ + ProgressDialog.h \ PropertyBox.h \ PropertyStack.h \ RangeInputDialog.h \ @@ -46,7 +50,9 @@ SOURCES += AudioDial.cpp \ ColourNameDialog.cpp \ CommandHistory.cpp \ + CSVFormatDialog.cpp \ Fader.cpp \ + FileFinder.cpp \ IconLoader.cpp \ ImageDialog.cpp \ ItemEditDialog.cpp \ @@ -56,6 +62,7 @@ LayerTreeDialog.cpp \ LEDButton.cpp \ ListInputDialog.cpp \ + MIDIFileImportDialog.cpp \ NotifyingCheckBox.cpp \ NotifyingComboBox.cpp \ NotifyingPushButton.cpp \ @@ -63,6 +70,7 @@ Panner.cpp \ PluginParameterBox.cpp \ PluginParameterDialog.cpp \ + ProgressDialog.cpp \ PropertyBox.cpp \ PropertyStack.cpp \ RangeInputDialog.cpp \