changeset 392:183ee2a55fc7

* More work to abstract out interactive components used in the data library, so that it does not need to depend on QtGui.
author Chris Cannam
date Fri, 14 Mar 2008 17:14:21 +0000
parents 5858cc462d0a
children cfd11e44ad87
files base/ProgressPrinter.cpp base/ProgressPrinter.h base/ProgressReporter.cpp base/ProgressReporter.h base/Thread.h base/base.pro data/data.pro data/fileio/AudioFileReaderFactory.cpp data/fileio/AudioFileReaderFactory.h data/fileio/CSVFileReader.cpp data/fileio/CSVFileReader.h data/fileio/CSVFormat.cpp data/fileio/CSVFormat.h data/fileio/DataFileReaderFactory.cpp data/fileio/DataFileReaderFactory.h data/fileio/FileFinder.cpp data/fileio/FileFinder.h data/fileio/FileSource.cpp data/fileio/FileSource.h data/fileio/MIDIFileReader.cpp data/fileio/MIDIFileReader.h data/fileio/MP3FileReader.cpp data/fileio/MP3FileReader.h data/fileio/OggVorbisFileReader.cpp data/fileio/OggVorbisFileReader.h data/fileio/ProgressPrinter.cpp data/fileio/ProgressPrinter.h data/fileio/QuickTimeFileReader.cpp data/fileio/QuickTimeFileReader.h data/fileio/ResamplingWavFileReader.cpp data/fileio/ResamplingWavFileReader.h data/model/WaveFileModel.cpp
diffstat 32 files changed, 824 insertions(+), 1400 deletions(-) [+]
line wrap: on
line diff
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/base/ProgressPrinter.cpp	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 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 "ProgressPrinter.h"
+
+#include <iostream>
+
+ProgressPrinter::ProgressPrinter(QString message, QObject *parent) :
+    ProgressReporter(parent),
+    m_prefix(message),
+    m_lastProgress(0)
+{
+}
+
+ProgressPrinter::~ProgressPrinter()
+{
+    if (m_lastProgress > 0 && m_lastProgress != 100) {
+        std::cerr << "\r\n";
+    }
+//    std::cerr << "(progress printer dtor)" << std::endl;
+}
+
+void
+ProgressPrinter::setProgress(int progress)
+{
+    if (progress == m_lastProgress) return;
+    if (progress == 100) std::cerr << "\r\n";
+    else {
+        std::cerr << "\r"
+                  << m_prefix.toStdString() 
+                  << (m_prefix == "" ? "" : " ")
+                  << progress << "%";
+    }
+    m_lastProgress = progress;
+}
+
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/base/ProgressPrinter.h	Fri Mar 14 17:14:21 2008 +0000
@@ -0,0 +1,37 @@
+/* -*- 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 _PROGRESS_PRINTER_H_
+#define _PROGRESS_PRINTER_H_
+
+#include "ProgressReporter.h"
+
+class ProgressPrinter : public ProgressReporter
+{
+    Q_OBJECT
+    
+public:
+    ProgressPrinter(QString message, QObject *parent = 0);
+    virtual ~ProgressPrinter();
+    
+public slots:
+    virtual void setProgress(int);
+
+protected:
+    QString m_prefix;
+    int m_lastProgress;
+};
+
+#endif
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/base/ProgressReporter.cpp	Fri Mar 14 17:14:21 2008 +0000
@@ -0,0 +1,26 @@
+/* -*- 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 "ProgressReporter.h"
+
+ProgressReporter::ProgressReporter(QObject *parent) :
+	QObject(parent)
+{
+}
+
+ProgressReporter::~ProgressReporter()
+{
+}
+
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/base/ProgressReporter.h	Fri Mar 14 17:14:21 2008 +0000
@@ -0,0 +1,37 @@
+/* -*- 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_REPORTER_H_
+
+#include <QObject>
+#include <QString>
+
+class ProgressReporter : public QObject
+{
+    Q_OBJECT
+    
+public:
+    ProgressReporter(QObject *parent = 0);
+    virtual ~ProgressReporter();
+
+signals:
+    void cancelled();
+
+public slots:
+    virtual void setMessage(QString text) = 0;
+    virtual void setProgress(int percentage) = 0;
+};
+
+#endif
--- a/base/Thread.h	Thu Mar 13 14:06:03 2008 +0000
+++ b/base/Thread.h	Fri Mar 14 17:14:21 2008 +0000
@@ -31,6 +31,9 @@
 public slots:
     void start();
 
+protected:
+    virtual void run() = 0;
+
 private:    
     Type m_type;
 };
--- a/base/base.pro	Thu Mar 13 14:06:03 2008 +0000
+++ b/base/base.pro	Fri Mar 14 17:14:21 2008 +0000
@@ -26,6 +26,8 @@
            PlayParameters.h \
            Preferences.h \
            Profiler.h \
+           ProgressPrinter.h \
+           ProgressReporter.h \
            PropertyContainer.h \
            RangeMapper.h \
            RealTime.h \
@@ -54,6 +56,8 @@
            PlayParameters.cpp \
            Preferences.cpp \
            Profiler.cpp \
+           ProgressPrinter.cpp \
+           ProgressReporter.cpp \
            PropertyContainer.cpp \
            RangeMapper.cpp \
            RealTime.cpp \
--- a/data/data.pro	Thu Mar 13 14:06:03 2008 +0000
+++ b/data/data.pro	Fri Mar 14 17:14:21 2008 +0000
@@ -5,6 +5,7 @@
 
 CONFIG += sv staticlib qt thread warn_on stl rtti exceptions
 QT += network
+QT -= gui
 
 TARGET = svdata
 
@@ -25,9 +26,9 @@
            fileio/CodedAudioFileReader.h \
            fileio/CSVFileReader.h \
            fileio/CSVFileWriter.h \
+           fileio/CSVFormat.h \
            fileio/DataFileReader.h \
            fileio/DataFileReaderFactory.h \
-           fileio/FileFinder.h \
            fileio/FileReadThread.h \
            fileio/FileSource.h \
            fileio/MatchFileReader.h \
@@ -38,7 +39,6 @@
            fileio/MP3FileReader.h \
            fileio/OggVorbisFileReader.h \
            fileio/PlaylistFileReader.h \
-           fileio/ProgressPrinter.h \
            fileio/QuickTimeFileReader.h \
            fileio/ResamplingWavFileReader.h \
            fileio/WavFileReader.h \
@@ -75,8 +75,8 @@
            fileio/CodedAudioFileReader.cpp \
            fileio/CSVFileReader.cpp \
            fileio/CSVFileWriter.cpp \
+           fileio/CSVFormat.cpp \
            fileio/DataFileReaderFactory.cpp \
-           fileio/FileFinder.cpp \
            fileio/FileReadThread.cpp \
            fileio/FileSource.cpp \
            fileio/MatchFileReader.cpp \
@@ -86,7 +86,6 @@
            fileio/MP3FileReader.cpp \
            fileio/OggVorbisFileReader.cpp \
            fileio/PlaylistFileReader.cpp \
-           fileio/ProgressPrinter.cpp \
            fileio/QuickTimeFileReader.cpp \
            fileio/ResamplingWavFileReader.cpp \
            fileio/WavFileReader.cpp \
--- a/data/fileio/AudioFileReaderFactory.cpp	Thu Mar 13 14:06:03 2008 +0000
+++ b/data/fileio/AudioFileReaderFactory.cpp	Fri Mar 14 17:14:21 2008 +0000
@@ -54,19 +54,22 @@
 }
 
 AudioFileReader *
-AudioFileReaderFactory::createReader(FileSource source, size_t targetRate)
+AudioFileReaderFactory::createReader(FileSource source, size_t targetRate,
+                                     ProgressReporter *reporter)
 {
-    return create(source, targetRate, false);
+    return create(source, targetRate, false, reporter);
 }
 
 AudioFileReader *
-AudioFileReaderFactory::createThreadingReader(FileSource source, size_t targetRate)
+AudioFileReaderFactory::createThreadingReader(FileSource source, size_t targetRate,
+                                              ProgressReporter *reporter)
 {
-    return create(source, targetRate, true);
+    return create(source, targetRate, true, reporter);
 }
 
 AudioFileReader *
-AudioFileReaderFactory::create(FileSource source, size_t targetRate, bool threading)
+AudioFileReaderFactory::create(FileSource source, size_t targetRate, bool threading,
+                               ProgressReporter *reporter)
 {
     QString err;
 
@@ -99,7 +102,8 @@
                  ResamplingWavFileReader::ResampleThreaded :
                  ResamplingWavFileReader::ResampleAtOnce,
                  ResamplingWavFileReader::CacheInTemporaryFile,
-                 targetRate);
+                 targetRate,
+                 reporter);
         }
     }
     
@@ -113,7 +117,8 @@
                  OggVorbisFileReader::DecodeThreaded :
                  OggVorbisFileReader::DecodeAtOnce,
                  OggVorbisFileReader::CacheInTemporaryFile,
-                 targetRate);
+                 targetRate,
+                 reporter);
         }
     }
 #endif
@@ -128,7 +133,8 @@
                  MP3FileReader::DecodeThreaded :
                  MP3FileReader::DecodeAtOnce,
                  MP3FileReader::CacheInTemporaryFile,
-                 targetRate);
+                 targetRate,
+                 reporter);
         }
     }
 #endif
@@ -142,7 +148,8 @@
                  QuickTimeFileReader::DecodeThreaded : 
                  QuickTimeFileReader::DecodeAtOnce,
                  QuickTimeFileReader::CacheInTemporaryFile,
-                 targetRate);
+                 targetRate,
+                 reporter);
         }
     }
 #endif
--- a/data/fileio/AudioFileReaderFactory.h	Thu Mar 13 14:06:03 2008 +0000
+++ b/data/fileio/AudioFileReaderFactory.h	Fri Mar 14 17:14:21 2008 +0000
@@ -21,6 +21,7 @@
 #include "FileSource.h"
 
 class AudioFileReader;
+class ProgressReporter;
 
 class AudioFileReaderFactory
 {
@@ -42,10 +43,15 @@
      * if you want to find out whether the file is being resampled
      * or not.
      *
+     * If a ProgressReporter is provided, it will be updated with
+     * progress status.  Caller retains ownership of the reporter
+     * object.
+     *
      * Caller owns the returned object and must delete it after use.
      */
     static AudioFileReader *createReader(FileSource source,
-                                         size_t targetRate = 0);
+                                         size_t targetRate = 0,
+                                         ProgressReporter *reporter = 0);
 
     /**
      * Return an audio file reader initialised to the file at the
@@ -59,15 +65,24 @@
      * if you want to find out whether the file is being resampled
      * or not.
      *
+     * If a ProgressReporter is provided, it will be updated with
+     * progress status.  This will only be meaningful if threading
+     * mode is not used because the file reader in use does not
+     * support it; otherwise progress as reported will jump straight
+     * to 100% before threading mode takes over.  Caller retains
+     * ownership of the reporter object.
+     *
      * Caller owns the returned object and must delete it after use.
      */
     static AudioFileReader *createThreadingReader(FileSource source,
-                                                  size_t targetRate = 0);
+                                                  size_t targetRate = 0,
+                                                  ProgressReporter *reporter = 0);
 
 protected:
     static AudioFileReader *create(FileSource source,
                                    size_t targetRate,
-                                   bool threading);
+                                   bool threading,
+                                   ProgressReporter *reporter);
 };
 
 #endif
--- a/data/fileio/CSVFileReader.cpp	Thu Mar 13 14:06:03 2008 +0000
+++ b/data/fileio/CSVFileReader.cpp	Fri Mar 14 17:14:21 2008 +0000
@@ -27,18 +27,12 @@
 #include <QRegExp>
 #include <QStringList>
 #include <QTextStream>
-#include <QFrame>
-#include <QGridLayout>
-#include <QPushButton>
-#include <QHBoxLayout>
-#include <QVBoxLayout>
-#include <QTableWidget>
-#include <QComboBox>
-#include <QLabel>
 
 #include <iostream>
 
-CSVFileReader::CSVFileReader(QString path, size_t mainModelSampleRate) :
+CSVFileReader::CSVFileReader(QString path, CSVFormat format,
+                             size_t mainModelSampleRate) :
+    m_format(format),
     m_file(0),
     m_mainModelSampleRate(mainModelSampleRate)
 {
@@ -86,7 +80,7 @@
 CSVFileReader::load() const
 {
     if (!m_file) return 0;
-
+/*!!!
     CSVFormatDialog *dialog = new CSVFormatDialog
 	(0, m_file, m_mainModelSampleRate);
 
@@ -94,20 +88,19 @@
 	delete dialog;
         throw DataFileReaderFactory::ImportCancelled;
     }
+*/
 
-    CSVFormatDialog::ModelType   modelType = dialog->getModelType();
-    CSVFormatDialog::TimingType timingType = dialog->getTimingType();
-    CSVFormatDialog::TimeUnits   timeUnits = dialog->getTimeUnits();
-    QString separator = dialog->getSeparator();
-    QString::SplitBehavior behaviour = dialog->getSplitBehaviour();
-    size_t sampleRate = dialog->getSampleRate();
-    size_t windowSize = dialog->getWindowSize();
+    CSVFormat::ModelType   modelType = m_format.getModelType();
+    CSVFormat::TimingType timingType = m_format.getTimingType();
+    CSVFormat::TimeUnits   timeUnits = m_format.getTimeUnits();
+    QString separator = m_format.getSeparator();
+    QString::SplitBehavior behaviour = m_format.getSplitBehaviour();
+    size_t sampleRate = m_format.getSampleRate();
+    size_t windowSize = m_format.getWindowSize();
 
-    delete dialog;
-
-    if (timingType == CSVFormatDialog::ExplicitTiming) {
+    if (timingType == CSVFormat::ExplicitTiming) {
 	windowSize = 1;
-	if (timeUnits == CSVFormatDialog::TimeSeconds) {
+	if (timeUnits == CSVFormat::TimeSeconds) {
 	    sampleRate = m_mainModelSampleRate;
 	}
     }
@@ -155,17 +148,17 @@
 
                 switch (modelType) {
 
-                case CSVFormatDialog::OneDimensionalModel:
+                case CSVFormat::OneDimensionalModel:
                     model1 = new SparseOneDimensionalModel(sampleRate, windowSize);
                     model = model1;
                     break;
 		
-                case CSVFormatDialog::TwoDimensionalModel:
+                case CSVFormat::TwoDimensionalModel:
                     model2 = new SparseTimeValueModel(sampleRate, windowSize, false);
                     model = model2;
                     break;
 		
-                case CSVFormatDialog::ThreeDimensionalModel:
+                case CSVFormat::ThreeDimensionalModel:
                     model3 = new EditableDenseThreeDimensionalModel(sampleRate,
                                                                     windowSize,
                                                                     list.size());
@@ -187,13 +180,13 @@
                     s = s.mid(1, s.length() - 2);
                 }
 
-                if (i == 0 && timingType == CSVFormatDialog::ExplicitTiming) {
+                if (i == 0 && timingType == CSVFormat::ExplicitTiming) {
 
                     bool ok = false;
                     QString numeric = s;
                     numeric.remove(nonNumericRx);
 
-                    if (timeUnits == CSVFormatDialog::TimeSeconds) {
+                    if (timeUnits == CSVFormat::TimeSeconds) {
 
                         double time = numeric.toDouble(&ok);
                         frameNo = int(time * sampleRate + 0.00001);
@@ -202,7 +195,7 @@
 
                         frameNo = numeric.toInt(&ok);
 
-                        if (timeUnits == CSVFormatDialog::TimeWindows) {
+                        if (timeUnits == CSVFormat::TimeWindows) {
                             frameNo *= windowSize;
                         }
                     }
@@ -224,7 +217,7 @@
                 }
             }
 
-            if (modelType == CSVFormatDialog::OneDimensionalModel) {
+            if (modelType == CSVFormat::OneDimensionalModel) {
 	    
                 SparseOneDimensionalModel::Point point
                     (frameNo,
@@ -233,7 +226,7 @@
 
                 model1->addPoint(point);
 
-            } else if (modelType == CSVFormatDialog::TwoDimensionalModel) {
+            } else if (modelType == CSVFormat::TwoDimensionalModel) {
 
                 SparseTimeValueModel::Point point
                     (frameNo,
@@ -242,7 +235,7 @@
 
                 model2->addPoint(point);
 
-            } else if (modelType == CSVFormatDialog::ThreeDimensionalModel) {
+            } else if (modelType == CSVFormat::ThreeDimensionalModel) {
 
                 DenseThreeDimensionalModel::Column values;
 
@@ -277,14 +270,14 @@
             }
 
             ++lineno;
-            if (timingType == CSVFormatDialog::ImplicitTiming ||
+            if (timingType == CSVFormat::ImplicitTiming ||
                 list.size() == 0) {
                 frameNo += windowSize;
             }
         }
     }
 
-    if (modelType == CSVFormatDialog::ThreeDimensionalModel) {
+    if (modelType == CSVFormat::ThreeDimensionalModel) {
 	model3->setMinimumLevel(min);
 	model3->setMaximumLevel(max);
     }
@@ -292,390 +285,3 @@
     return model;
 }
 
-
-CSVFormatDialog::CSVFormatDialog(QWidget *parent, QFile *file,
-				 size_t defaultSampleRate) :
-    QDialog(parent),
-    m_modelType(OneDimensionalModel),
-    m_timingType(ExplicitTiming),
-    m_timeUnits(TimeAudioFrames),
-    m_separator(""),
-    m_behaviour(QString::KeepEmptyParts)
-{
-    setModal(true);
-    setWindowTitle(tr("Select Data Format"));
-
-    (void)guessFormat(file);
-
-    QGridLayout *layout = new QGridLayout;
-
-    layout->addWidget(new QLabel(tr("<b>Select Data Format</b><p>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 == ExplicitTiming ?
-                                       m_timeUnits == 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()
-{
-}
-
-void
-CSVFormatDialog::populateExample()
-{
-    m_exampleWidget->setColumnCount
-	(m_timingType == 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 == 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 == ExplicitTiming) --index;
-	    item->setFlags(Qt::ItemIsEnabled);
-	    m_exampleWidget->setItem(i, index, item);
-	}
-    }
-}
-
-void
-CSVFormatDialog::modelTypeChanged(int type)
-{
-    m_modelType = (ModelType)type;
-
-    if (m_modelType == 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 = ExplicitTiming;
-	m_timeUnits = TimeSeconds;
-	m_sampleRateCombo->setEnabled(false);
-	m_sampleRateLabel->setEnabled(false);
-	m_windowSizeCombo->setEnabled(false);
-	m_windowSizeLabel->setEnabled(false);
-        if (m_modelType == ThreeDimensionalModel) {
-            m_modelTypeCombo->setCurrentIndex(1);
-            modelTypeChanged(1);
-        }
-	break;
-
-    case 1:
-	m_timingType = ExplicitTiming;
-	m_timeUnits = TimeAudioFrames;
-	m_sampleRateCombo->setEnabled(true);
-	m_sampleRateLabel->setEnabled(true);
-	m_windowSizeCombo->setEnabled(false);
-	m_windowSizeLabel->setEnabled(false);
-        if (m_modelType == ThreeDimensionalModel) {
-            m_modelTypeCombo->setCurrentIndex(1);
-            modelTypeChanged(1);
-        }
-	break;
-
-    case 2:
-	m_timingType = ImplicitTiming;
-	m_timeUnits = 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;
-}
-
-bool
-CSVFormatDialog::guessFormat(QFile *file)
-{
-    QTextStream in(file);
-    in.seek(0);
-
-    unsigned int lineno = 0;
-
-    bool nonIncreasingPrimaries = false;
-    bool nonNumericPrimaries = false;
-    bool floatPrimaries = false;
-    bool variableItemCount = false;
-    int itemCount = 1;
-    int earliestNonNumericItem = -1;
-
-    float prevPrimary = 0.0;
-
-    m_maxExampleCols = 0;
-
-    while (!in.atEnd()) {
-
-        // See comment about line endings in load() above
-
-        QString chunk = in.readLine();
-        QStringList lines = chunk.split('\r', QString::SkipEmptyParts);
-
-        for (size_t li = 0; li < lines.size(); ++li) {
-
-            QString line = lines[li];
-
-            if (line.startsWith("#")) continue;
-
-            m_behaviour = QString::KeepEmptyParts;
-
-            if (m_separator == "") {
-                //!!! to do: ask the user
-                if (line.split(",").size() >= 2) m_separator = ",";
-                else if (line.split("\t").size() >= 2) m_separator = "\t";
-                else if (line.split("|").size() >= 2) m_separator = "|";
-                else if (line.split("/").size() >= 2) m_separator = "/";
-                else if (line.split(":").size() >= 2) m_separator = ":";
-                else {
-                    m_separator = " ";
-                    m_behaviour = QString::SkipEmptyParts;
-                }
-            }
-
-            QStringList list = line.split(m_separator, m_behaviour);
-            QStringList tidyList;
-
-            for (int i = 0; i < list.size(); ++i) {
-	    
-                QString s(list[i]);
-                bool numeric = false;
-
-                if (s.length() >= 2 && s.startsWith("\"") && s.endsWith("\"")) {
-                    s = s.mid(1, s.length() - 2);
-                } else if (s.length() >= 2 && s.startsWith("'") && s.endsWith("'")) {
-                    s = s.mid(1, s.length() - 2);
-                } else {
-                    (void)s.toFloat(&numeric);
-                }
-
-                tidyList.push_back(s);
-
-                if (lineno == 0 || (list.size() < itemCount)) {
-                    itemCount = list.size();
-                } else {
-                    if (itemCount != list.size()) {
-                        variableItemCount = true;
-                    }
-                }
-	    
-                if (i == 0) { // primary
-
-                    if (numeric) {
-
-                        float primary = s.toFloat();
-
-                        if (lineno > 0 && primary <= prevPrimary) {
-                            nonIncreasingPrimaries = true;
-                        }
-
-                        if (s.contains(".") || s.contains(",")) {
-                            floatPrimaries = true;
-                        }
-
-                        prevPrimary = primary;
-
-                    } else {
-                        nonNumericPrimaries = true;
-                    }
-                } else { // secondary
-
-                    if (!numeric) {
-                        if (earliestNonNumericItem < 0 ||
-                            i < earliestNonNumericItem) {
-                            earliestNonNumericItem = i;
-                        }
-                    }
-                }
-            }
-
-            if (lineno < 10) {
-                m_example.push_back(tidyList);
-                if (lineno == 0 || tidyList.size() > m_maxExampleCols) {
-                    m_maxExampleCols = tidyList.size();
-                }
-            }
-
-            ++lineno;
-
-            if (lineno == 50) break;
-        }
-    }
-
-    if (nonNumericPrimaries || nonIncreasingPrimaries) {
-	
-	// Primaries are probably not a series of times
-
-	m_timingType = ImplicitTiming;
-	m_timeUnits = TimeWindows;
-	
-	if (nonNumericPrimaries) {
-	    m_modelType = OneDimensionalModel;
-	} else if (itemCount == 1 || variableItemCount ||
-		   (earliestNonNumericItem != -1)) {
-	    m_modelType = TwoDimensionalModel;
-	} else {
-	    m_modelType = ThreeDimensionalModel;
-	}
-
-    } else {
-
-	// Increasing numeric primaries -- likely to be time
-
-	m_timingType = ExplicitTiming;
-
-	if (floatPrimaries) {
-	    m_timeUnits = TimeSeconds;
-	} else {
-	    m_timeUnits = TimeAudioFrames;
-	}
-
-	if (itemCount == 1) {
-	    m_modelType = OneDimensionalModel;
-	} else if (variableItemCount || (earliestNonNumericItem != -1)) {
-	    if (earliestNonNumericItem != -1 && earliestNonNumericItem < 2) {
-		m_modelType = OneDimensionalModel;
-	    } else {
-		m_modelType = TwoDimensionalModel;
-	    }
-	} else {
-	    m_modelType = ThreeDimensionalModel;
-	}
-    }
-
-    std::cerr << "Estimated model type: " << m_modelType << std::endl;
-    std::cerr << "Estimated timing type: " << m_timingType << std::endl;
-    std::cerr << "Estimated units: " << m_timeUnits << std::endl;
-
-    in.seek(0);
-    return true;
-}
--- a/data/fileio/CSVFileReader.h	Thu Mar 13 14:06:03 2008 +0000
+++ b/data/fileio/CSVFileReader.h	Fri Mar 14 17:14:21 2008 +0000
@@ -18,20 +18,17 @@
 
 #include "DataFileReader.h"
 
+#include "CSVFormat.h"
+
 #include <QList>
 #include <QStringList>
-#include <QDialog>
 
 class QFile;
-class QTableWidget;
-class QComboBox;
-class QLabel;
-
 
 class CSVFileReader : public DataFileReader
 {
 public:
-    CSVFileReader(QString path, size_t mainModelSampleRate);
+    CSVFileReader(QString path, CSVFormat format, size_t mainModelSampleRate);
     virtual ~CSVFileReader();
 
     virtual bool isOK() const;
@@ -39,77 +36,12 @@
     virtual Model *load() const;
 
 protected:
+    CSVFormat m_format;
     QFile *m_file;
     QString m_error;
     size_t m_mainModelSampleRate;
 };
 
 
-class CSVFormatDialog : public QDialog
-{
-    Q_OBJECT
-    
-public:
-    CSVFormatDialog(QWidget *parent, QFile *file, size_t defaultSampleRate);
-    
-    ~CSVFormatDialog();
-    
-    enum ModelType {
-	OneDimensionalModel,
-	TwoDimensionalModel,
-	ThreeDimensionalModel
-    };
-    
-    enum TimingType {
-	ExplicitTiming,
-	ImplicitTiming
-    };
-    
-    enum TimeUnits {
-	TimeSeconds,
-	TimeAudioFrames,
-	TimeWindows
-    };
-
-    ModelType  getModelType()   const { return m_modelType;   }
-    TimingType getTimingType()  const { return m_timingType;  }
-    TimeUnits  getTimeUnits()   const { return m_timeUnits;   }
-    QString    getSeparator()   const { return m_separator;   }
-    size_t     getSampleRate()  const { return m_sampleRate;  }
-    size_t     getWindowSize()  const { return m_windowSize;  }
-
-    QString::SplitBehavior getSplitBehaviour() const { return m_behaviour; }
-
-protected slots:
-    void modelTypeChanged(int type);
-    void timingTypeChanged(int type);
-    void sampleRateChanged(QString);
-    void windowSizeChanged(QString);
-
-protected:
-    ModelType  m_modelType;
-    TimingType m_timingType;
-    TimeUnits  m_timeUnits;
-    QString    m_separator;
-    size_t     m_sampleRate;
-    size_t     m_windowSize;
-
-    QString::SplitBehavior m_behaviour;
-    
-    QList<QStringList> 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;
-
-    bool guessFormat(QFile *file);
-    void populateExample();
-};
-
 #endif
 
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/data/fileio/CSVFormat.cpp	Fri Mar 14 17:14:21 2008 +0000
@@ -0,0 +1,202 @@
+/* -*- 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 "CSVFormat.h"
+
+#include <QFile>
+#include <QString>
+#include <QRegExp>
+#include <QStringList>
+#include <QTextStream>
+
+#include <iostream>
+
+CSVFormat::CSVFormat(QString filename) :
+    m_modelType(TwoDimensionalModel),
+    m_timingType(ExplicitTiming),
+    m_timeUnits(TimeSeconds),
+    m_separator(","),
+    m_sampleRate(44100),
+    m_windowSize(1024),
+    m_behaviour(QString::KeepEmptyParts),
+    m_maxExampleCols(0)
+{
+    QFile file(filename);
+    if (!file.exists()) return;
+    if (!file.open(QIODevice::ReadOnly | QIODevice::Text)) return;
+
+    QTextStream in(&file);
+    in.seek(0);
+
+    unsigned int lineno = 0;
+
+    bool nonIncreasingPrimaries = false;
+    bool nonNumericPrimaries = false;
+    bool floatPrimaries = false;
+    bool variableItemCount = false;
+    int itemCount = 1;
+    int earliestNonNumericItem = -1;
+
+    float prevPrimary = 0.0;
+
+    m_maxExampleCols = 0;
+    m_separator = "";
+
+    while (!in.atEnd()) {
+
+        // See comment about line endings in CSVFileReader::load() 
+
+        QString chunk = in.readLine();
+        QStringList lines = chunk.split('\r', QString::SkipEmptyParts);
+
+        for (size_t li = 0; li < lines.size(); ++li) {
+
+            QString line = lines[li];
+
+            if (line.startsWith("#")) continue;
+
+            m_behaviour = QString::KeepEmptyParts;
+
+            if (m_separator == "") {
+                //!!! to do: ask the user
+                if (line.split(",").size() >= 2) m_separator = ",";
+                else if (line.split("\t").size() >= 2) m_separator = "\t";
+                else if (line.split("|").size() >= 2) m_separator = "|";
+                else if (line.split("/").size() >= 2) m_separator = "/";
+                else if (line.split(":").size() >= 2) m_separator = ":";
+                else {
+                    m_separator = " ";
+                    m_behaviour = QString::SkipEmptyParts;
+                }
+            }
+
+            std::cerr << "separator = \"" << m_separator.toStdString() << "\"" << std::endl;
+
+            QStringList list = line.split(m_separator, m_behaviour);
+            QStringList tidyList;
+
+            for (int i = 0; i < list.size(); ++i) {
+	    
+                QString s(list[i]);
+                bool numeric = false;
+
+                if (s.length() >= 2 && s.startsWith("\"") && s.endsWith("\"")) {
+                    s = s.mid(1, s.length() - 2);
+                } else if (s.length() >= 2 && s.startsWith("'") && s.endsWith("'")) {
+                    s = s.mid(1, s.length() - 2);
+                } else {
+                    float f = s.toFloat(&numeric);
+                    std::cerr << "converted \"" << s.toStdString() << "\" to float, got " << f << " and success = " << numeric << std::endl;
+                }
+
+                tidyList.push_back(s);
+
+                if (lineno == 0 || (list.size() < itemCount)) {
+                    itemCount = list.size();
+                } else {
+                    if (itemCount != list.size()) {
+                        variableItemCount = true;
+                    }
+                }
+	    
+                if (i == 0) { // primary
+
+                    if (numeric) {
+
+                        float primary = s.toFloat();
+
+                        if (lineno > 0 && primary <= prevPrimary) {
+                            nonIncreasingPrimaries = true;
+                        }
+
+                        if (s.contains(".") || s.contains(",")) {
+                            floatPrimaries = true;
+                        }
+
+                        prevPrimary = primary;
+
+                    } else {
+                        nonNumericPrimaries = true;
+                    }
+                } else { // secondary
+
+                    if (!numeric) {
+                        if (earliestNonNumericItem < 0 ||
+                            i < earliestNonNumericItem) {
+                            earliestNonNumericItem = i;
+                        }
+                    }
+                }
+            }
+
+            if (lineno < 10) {
+                m_example.push_back(tidyList);
+                if (lineno == 0 || tidyList.size() > m_maxExampleCols) {
+                    m_maxExampleCols = tidyList.size();
+                }
+            }
+
+            ++lineno;
+
+            if (lineno == 50) break;
+        }
+    }
+
+    if (nonNumericPrimaries || nonIncreasingPrimaries) {
+	
+	// Primaries are probably not a series of times
+
+	m_timingType = CSVFormat::ImplicitTiming;
+	m_timeUnits = CSVFormat::TimeWindows;
+	
+	if (nonNumericPrimaries) {
+	    m_modelType = CSVFormat::OneDimensionalModel;
+	} else if (itemCount == 1 || variableItemCount ||
+		   (earliestNonNumericItem != -1)) {
+	    m_modelType = CSVFormat::TwoDimensionalModel;
+	} else {
+	    m_modelType = CSVFormat::ThreeDimensionalModel;
+	}
+
+    } else {
+
+	// Increasing numeric primaries -- likely to be time
+
+	m_timingType = CSVFormat::ExplicitTiming;
+
+	if (floatPrimaries) {
+	    m_timeUnits = CSVFormat::TimeSeconds;
+	} else {
+	    m_timeUnits = CSVFormat::TimeAudioFrames;
+	}
+
+	if (itemCount == 1) {
+	    m_modelType = CSVFormat::OneDimensionalModel;
+	} else if (variableItemCount || (earliestNonNumericItem != -1)) {
+	    if (earliestNonNumericItem != -1 && earliestNonNumericItem < 2) {
+		m_modelType = CSVFormat::OneDimensionalModel;
+	    } else {
+		m_modelType = CSVFormat::TwoDimensionalModel;
+	    }
+	} else {
+	    m_modelType = CSVFormat::ThreeDimensionalModel;
+	}
+    }
+
+    std::cerr << "Estimated model type: " << m_modelType << std::endl;
+    std::cerr << "Estimated timing type: " << m_timingType << std::endl;
+    std::cerr << "Estimated units: " << m_timeUnits << std::endl;
+}
+
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/data/fileio/CSVFormat.h	Fri Mar 14 17:14:21 2008 +0000
@@ -0,0 +1,90 @@
+/* -*- 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_H_
+#define _CSV_FORMAT_H_
+
+#include <QString>
+#include <QStringList>
+
+class CSVFormat
+{
+public:
+    enum ModelType {
+	OneDimensionalModel,
+	TwoDimensionalModel,
+	ThreeDimensionalModel
+    };
+    
+    enum TimingType {
+	ExplicitTiming,
+	ImplicitTiming
+    };
+    
+    enum TimeUnits {
+	TimeSeconds,
+	TimeAudioFrames,
+	TimeWindows
+    };
+
+    CSVFormat(QString path); // guess format
+
+    CSVFormat() : // arbitrary defaults
+        m_modelType(TwoDimensionalModel),
+        m_timingType(ExplicitTiming),
+        m_timeUnits(TimeSeconds),
+        m_separator(","),
+        m_sampleRate(44100),
+        m_windowSize(1024),
+        m_behaviour(QString::KeepEmptyParts)
+    { }
+
+    ModelType  getModelType()   const { return m_modelType;   }
+    TimingType getTimingType()  const { return m_timingType;  }
+    TimeUnits  getTimeUnits()   const { return m_timeUnits;   }
+    QString    getSeparator()   const { return m_separator;   }
+    size_t     getSampleRate()  const { return m_sampleRate;  }
+    size_t     getWindowSize()  const { return m_windowSize;  }
+
+    QString::SplitBehavior getSplitBehaviour() const { return m_behaviour; }
+	
+    void setModelType(ModelType t)    { m_modelType  = t; }
+    void setTimingType(TimingType t)  { m_timingType = t; }
+    void setTimeUnits(TimeUnits t)    { m_timeUnits  = t; }
+    void setSeparator(QString s)      { m_separator  = s; }
+    void setSampleRate(size_t r)      { m_sampleRate = r; }
+    void setWindowSize(size_t s)      { m_windowSize = s; }
+
+    void setSplitBehaviour(QString::SplitBehavior b) { m_behaviour = b; }
+    
+    // only valid if constructor that guesses format was used:
+    QList<QStringList> getExample() const { return m_example; }
+    int getMaxExampleCols() const { return m_maxExampleCols; }
+
+protected:
+    ModelType  m_modelType;
+    TimingType m_timingType;
+    TimeUnits  m_timeUnits;
+    QString    m_separator;
+    size_t     m_sampleRate;
+    size_t     m_windowSize;
+
+    QString::SplitBehavior m_behaviour;
+
+    QList<QStringList> m_example;
+    int m_maxExampleCols;
+};
+
+#endif
--- a/data/fileio/DataFileReaderFactory.cpp	Thu Mar 13 14:06:03 2008 +0000
+++ b/data/fileio/DataFileReaderFactory.cpp	Fri Mar 14 17:14:21 2008 +0000
@@ -28,29 +28,57 @@
 }
 
 DataFileReader *
-DataFileReaderFactory::createReader(QString path, size_t mainModelSampleRate)
+DataFileReaderFactory::createReader(QString path,
+                                    bool csv,
+                                    MIDIFileImportPreferenceAcquirer *acquirer,
+                                    CSVFormat format,
+                                    size_t mainModelSampleRate)
 {
     QString err;
 
     DataFileReader *reader = 0;
 
-    reader = new MIDIFileReader(path, mainModelSampleRate);
-    if (reader->isOK()) return reader;
-    if (reader->getError() != "") err = reader->getError();
-    delete reader;
+    if (!csv) {
+        reader = new MIDIFileReader(path, acquirer, mainModelSampleRate);
+        if (reader->isOK()) return reader;
+        if (reader->getError() != "") err = reader->getError();
+        delete reader;
+    }
 
-    reader = new CSVFileReader(path, mainModelSampleRate);
-    if (reader->isOK()) return reader;
-    if (reader->getError() != "") err = reader->getError();
-    delete reader;
+    if (csv) {
+        reader = new CSVFileReader(path, format, mainModelSampleRate);
+        if (reader->isOK()) return reader;
+        if (reader->getError() != "") err = reader->getError();
+        delete reader;
+    }
+
+    return 0;
+}
+
+DataFileReader *
+DataFileReaderFactory::createReader(QString path,
+                                    MIDIFileImportPreferenceAcquirer *acquirer,
+                                    size_t mainModelSampleRate)
+{
+    DataFileReader *reader = createReader
+        (path, false, acquirer, CSVFormat(), mainModelSampleRate);
+    if (reader) return reader;
+
+    reader = createReader
+        (path, true, acquirer, CSVFormat(path), mainModelSampleRate);
+    if (reader) return reader;
 
     return 0;
 }
 
 Model *
-DataFileReaderFactory::load(QString path, size_t mainModelSampleRate)
+DataFileReaderFactory::load(QString path,
+                            MIDIFileImportPreferenceAcquirer *acquirer,
+                            size_t mainModelSampleRate)
 {
-    DataFileReader *reader = createReader(path, mainModelSampleRate);
+    DataFileReader *reader = createReader(path,
+                                          acquirer,
+                                          mainModelSampleRate);
     if (!reader) return NULL;
 
     try {
@@ -63,3 +91,42 @@
     }
 }
 
+Model *
+DataFileReaderFactory::loadNonCSV(QString path,
+                                  MIDIFileImportPreferenceAcquirer *acquirer,
+                                  size_t mainModelSampleRate)
+{
+    DataFileReader *reader = createReader(path, false,
+                                          acquirer,
+                                          CSVFormat(),
+                                          mainModelSampleRate);
+    if (!reader) return NULL;
+
+    try {
+        Model *model = reader->load();
+        delete reader;
+        return model;
+    } catch (Exception) {
+        delete reader;
+        throw;
+    }
+}
+
+Model *
+DataFileReaderFactory::loadCSV(QString path, CSVFormat format,
+                               size_t mainModelSampleRate)
+{
+    DataFileReader *reader = createReader(path, true, 0, format,
+                                          mainModelSampleRate);
+    if (!reader) return NULL;
+
+    try {
+        Model *model = reader->load();
+        delete reader;
+        return model;
+    } catch (Exception) {
+        delete reader;
+        throw;
+    }
+}
+
--- a/data/fileio/DataFileReaderFactory.h	Thu Mar 13 14:06:03 2008 +0000
+++ b/data/fileio/DataFileReaderFactory.h	Fri Mar 14 17:14:21 2008 +0000
@@ -18,6 +18,9 @@
 
 #include <QString>
 
+#include "CSVFormat.h"
+#include "MIDIFileReader.h"
+
 class DataFileReader;
 class Model;
 
@@ -37,16 +40,50 @@
      * Return a data file reader initialised to the file at the
      * given path, or NULL if no suitable reader for this path is
      * available or the file cannot be opened.
+     *
      * Caller owns the returned object and must delete it after use.
+     * 
+     * Note that this function is non-interactive -- the user is not
+     * asked for file format preferences.
      */
     static DataFileReader *createReader(QString path,
+                                        MIDIFileImportPreferenceAcquirer *,
 					size_t mainModelSampleRate);
 
     /**
      * Read the given path, if a suitable reader is available.
      * Return NULL if no reader succeeded in reading this file.
+     * 
+     * Note that this function is non-interactive -- the user is not
+     * asked for file format preferences.  If the CSV file reader is
+     * used, it is with default format.
      */
-    static Model *load(QString path, size_t mainModelSampleRate);
+    static Model *load(QString path,
+                       MIDIFileImportPreferenceAcquirer *acquirer,
+                       size_t mainModelSampleRate);
+
+    /**
+     * Read the given path, if a suitable reader is available.
+     * Return NULL if no reader succeeded in reading this file.
+     * Do not attempt the general CSV reader.
+     */
+    static Model *loadNonCSV(QString path,
+                             MIDIFileImportPreferenceAcquirer *acquirer,
+                             size_t mainModelSampleRate);
+
+    /**
+     * Read the given path using the CSV reader with the given format.
+     * Return NULL if it failed in reading this file.
+     */
+    static Model *loadCSV(QString path,
+                          CSVFormat format,
+                          size_t mainModelSampleRate);
+
+protected:
+    static DataFileReader *createReader(QString path, bool csv,
+                                        MIDIFileImportPreferenceAcquirer *,
+                                        CSVFormat format,
+					size_t mainModelSampleRate);
 };
 
 #endif
--- a/data/fileio/FileFinder.cpp	Thu Mar 13 14:06:03 2008 +0000
+++ /dev/null	Thu Jan 01 00:00:00 1970 +0000
@@ -1,511 +0,0 @@
-/* -*- 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 "FileSource.h"
-#include "AudioFileReaderFactory.h"
-#include "DataFileReaderFactory.h"
-
-#include <QFileInfo>
-#include <QMessageBox>
-#include <QFileDialog>
-#include <QInputDialog>
-#include <QImageReader>
-#include <QSettings>
-
-#include <iostream>
-
-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<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 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;
-}
-
-
--- a/data/fileio/FileFinder.h	Thu Mar 13 14:06:03 2008 +0000
+++ /dev/null	Thu Jan 01 00:00:00 1970 +0000
@@ -1,58 +0,0 @@
-/* -*- 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 <QString>
-#include <QObject>
-
-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
-
--- a/data/fileio/FileSource.cpp	Thu Mar 13 14:06:03 2008 +0000
+++ b/data/fileio/FileSource.cpp	Fri Mar 14 17:14:21 2008 +0000
@@ -14,17 +14,16 @@
 */
 
 #include "FileSource.h"
-#include "ProgressPrinter.h"
 
 #include "base/TempDirectory.h"
 #include "base/Exceptions.h"
+#include "base/ProgressReporter.h"
 
 #include <QHttp>
 #include <QFtp>
 #include <QFileInfo>
 #include <QDir>
-#include <QApplication>
-#include <QProgressDialog>
+#include <QCoreApplication>
 #include <QHttpResponseHeader>
 
 #include <iostream>
@@ -46,7 +45,7 @@
 QMutex
 FileSource::m_mapMutex;
 
-FileSource::FileSource(QString fileOrUrl, ShowProgressType progressType) :
+FileSource::FileSource(QString fileOrUrl, ProgressReporter *reporter) :
     m_url(fileOrUrl),
     m_ftp(0),
     m_http(0),
@@ -56,10 +55,7 @@
     m_remote(isRemote(fileOrUrl)),
     m_done(false),
     m_leaveLocalFile(false),
-    m_progressType(progressType),
-    m_progressPrinter(0),
-    m_progressDialog(0),
-    m_progressShowTimer(this),
+    m_reporter(reporter),
     m_refCounted(false)
 {
 #ifdef DEBUG_FILE_SOURCE
@@ -113,7 +109,7 @@
     }
 }
 
-FileSource::FileSource(QUrl url, ShowProgressType progressType) :
+FileSource::FileSource(QUrl url, ProgressReporter *reporter) :
     m_url(url),
     m_ftp(0),
     m_http(0),
@@ -123,10 +119,7 @@
     m_remote(isRemote(url.toString())),
     m_done(false),
     m_leaveLocalFile(false),
-    m_progressType(progressType),
-    m_progressPrinter(0),
-    m_progressDialog(0),
-    m_progressShowTimer(this),
+    m_reporter(reporter),
     m_refCounted(false)
 {
 #ifdef DEBUG_FILE_SOURCE
@@ -153,10 +146,7 @@
     m_remote(rf.m_remote),
     m_done(false),
     m_leaveLocalFile(false),
-    m_progressType(rf.m_progressType),
-    m_progressPrinter(0),
-    m_progressDialog(0),
-    m_progressShowTimer(0),
+    m_reporter(rf.m_reporter),
     m_refCounted(false)
 {
 #ifdef DEBUG_FILE_SOURCE
@@ -304,28 +294,12 @@
         m_refCountMap[m_url]++;
         m_refCounted = true;
 
-        switch (m_progressType) {
-
-        case ProgressNone: break;
-
-        case ProgressDialog:
-            m_progressDialog = new QProgressDialog
-                (tr("Downloading %1...").arg(m_url.toString()),
-                 tr("Cancel"), 0, 100);
-            m_progressDialog->hide();
-            connect(&m_progressShowTimer, SIGNAL(timeout()),
-                    this, SLOT(showProgressDialog()));
-            connect(m_progressDialog, SIGNAL(canceled()),
-                    this, SLOT(cancelled()));
-            m_progressShowTimer.setSingleShot(true);
-            m_progressShowTimer.start(2000);
-            break;
-
-        case ProgressToConsole:
-            m_progressPrinter = new ProgressPrinter(tr("Downloading..."));
+        if (m_reporter) {
+            m_reporter->setMessage
+                (tr("Downloading %1...").arg(m_url.toString()));
+            connect(m_reporter, SIGNAL(cancelled()), this, SLOT(cancelled()));
             connect(this, SIGNAL(progress(int)),
-                    m_progressPrinter, SLOT(progress(int)));
-            break;
+                    m_reporter, SLOT(setProgress(int)));
         }
     }
 }
@@ -441,10 +415,6 @@
         f->abort();
         f->deleteLater();
     }
-    delete m_progressDialog;
-    m_progressDialog = 0;
-    delete m_progressPrinter;
-    m_progressPrinter = 0;
     delete m_localFile; // does not actually delete the file
     m_localFile = 0;
 }
@@ -486,7 +456,7 @@
 {
     while (m_ok && (!m_done && m_lastStatus == 0)) {
 //        std::cerr << "waitForStatus: processing (last status " << m_lastStatus << ")" << std::endl;
-        QApplication::processEvents();
+        QCoreApplication::processEvents();
     }
 }
 
@@ -495,7 +465,7 @@
 {
     while (m_ok && !m_done) {
 //        std::cerr << "FileSource::waitForData: calling QApplication::processEvents" << std::endl;
-        QApplication::processEvents();
+        QCoreApplication::processEvents();
     }
 }
 
@@ -621,13 +591,6 @@
 {
     int percent = int((double(done) / double(total)) * 100.0 - 0.1);
     emit progress(percent);
-
-    if (!m_progressDialog) return;
-
-    if (percent > 0) {
-        m_progressDialog->setValue(percent);
-        m_progressDialog->show();
-    }
 }
 
 void
@@ -743,12 +706,6 @@
     m_done = true;
 }
 
-void
-FileSource::showProgressDialog()
-{
-    if (m_progressDialog) m_progressDialog->show();
-}
-
 bool
 FileSource::createCacheFile()
 {
--- a/data/fileio/FileSource.h	Thu Mar 13 14:06:03 2008 +0000
+++ b/data/fileio/FileSource.h	Fri Mar 14 17:14:21 2008 +0000
@@ -26,9 +26,8 @@
 class QFtp;
 class QHttp;
 class QFile;
-class QProgressDialog;
 class QHttpResponseHeader;
-class ProgressPrinter;
+class ProgressReporter;
 
 /**
  * FileSource is a class used to refer to the contents of a file that
@@ -62,37 +61,26 @@
     Q_OBJECT
 
 public:
-
-    enum ShowProgressType {
-        ProgressNone,
-        ProgressDialog,
-        ProgressToConsole
-    };
-
     /**
      * Construct a FileSource using the given local file path or URL.
      * The URL may be raw or encoded.
      *
-     * If progressType is ProgressDialog, a progress dialog will be
-     * shown for any network transfers; if it is ProgressToConsole, a
-     * progress indication will be sent to the console.
-     * Note that the progress() signal will also be emitted regularly
-     * during retrieval, even if progressType is ProgressNone.
+     * If a ProgressReporter is provided, it will be updated with
+     * progress status.  Note that the progress() signal will also be
+     * emitted regularly during retrieval, even if no reporter is
+     * supplied here.  Caller retains ownership of the reporter object.
      */
-    FileSource(QString fileOrUrl,
-               ShowProgressType progressType = ProgressNone);
+    FileSource(QString fileOrUrl, ProgressReporter *reporter = 0);
 
     /**
      * Construct a FileSource using the given remote URL.
      *
-     * If progressType is ProgressDialog, a progress dialog will be
-     * shown for any network transfers; if it is ProgressToConsole, a
-     * progress indication will be sent to the console.
-     * Note that the progress() signal also will be emitted regularly
-     * during retrieval, even if progressType is ProgressNone.
+     * If a ProgressReporter is provided, it will be updated with
+     * progress status.  Note that the progress() signal will also be
+     * emitted regularly during retrieval, even if no reporter is
+     * supplied here.  Caller retains ownership of the reporter object.
      */
-    FileSource(QUrl url,
-               ShowProgressType progressType = ProgressNone);
+    FileSource(QUrl url, ProgressReporter *reporter = 0);
 
     FileSource(const FileSource &);
 
@@ -212,7 +200,6 @@
     void ftpCommandFinished(int, bool);
     void dataTransferProgress(qint64 done, qint64 total);
     void done(bool error);
-    void showProgressDialog();
     void cancelled();
 
 protected:
@@ -230,10 +217,7 @@
     bool m_remote;
     bool m_done;
     bool m_leaveLocalFile;
-    ShowProgressType m_progressType;
-    ProgressPrinter *m_progressPrinter;
-    QProgressDialog *m_progressDialog;
-    QTimer m_progressShowTimer;
+    ProgressReporter *m_reporter;
 
     typedef std::map<QUrl, int> RemoteRefCountMap;
     typedef std::map<QUrl, QString> RemoteLocalMap;
--- a/data/fileio/MIDIFileReader.cpp	Thu Mar 13 14:06:03 2008 +0000
+++ b/data/fileio/MIDIFileReader.cpp	Fri Mar 14 17:14:21 2008 +0000
@@ -36,8 +36,6 @@
 #include "model/NoteModel.h"
 
 #include <QString>
-#include <QMessageBox>
-#include <QInputDialog>
 
 #include <sstream>
 
@@ -58,6 +56,7 @@
 
 
 MIDIFileReader::MIDIFileReader(QString path,
+                               MIDIFileImportPreferenceAcquirer *acquirer,
 			       size_t mainModelSampleRate) :
     m_timingDivision(0),
     m_format(MIDI_FILE_BAD_FORMAT),
@@ -67,7 +66,8 @@
     m_path(path),
     m_midiFile(0),
     m_fileSize(0),
-    m_mainModelSampleRate(mainModelSampleRate)
+    m_mainModelSampleRate(mainModelSampleRate),
+    m_acquirer(acquirer)
 {
     if (parseFile()) {
 	m_error = "";
@@ -813,9 +813,10 @@
     if (!isOK()) return 0;
 
     if (m_loadableTracks.empty()) {
-	QMessageBox::critical(0, tr("No notes in MIDI file"),
-			      tr("MIDI file \"%1\" has no notes in any track")
-			      .arg(m_path));
+        if (m_acquirer) {
+            m_acquirer->showError
+                (tr("MIDI file \"%1\" has no notes in any track").arg(m_path));
+        }
 	return 0;
     }
 
@@ -827,19 +828,7 @@
 
     } else {
 
-	QStringList available;
-	QString allTracks = tr("Merge all tracks");
-	QString allNonPercussion = tr("Merge all non-percussion tracks");
-
-	int nonTrackItems = 1;
-
-	available << allTracks;
-
-	if (!m_percussionTracks.empty() &&
-	    (m_percussionTracks.size() < m_loadableTracks.size())) {
-	    available << allNonPercussion;
-	    ++nonTrackItems;
-	}
+        QStringList displayNames;
 
 	for (set<unsigned int>::iterator i = m_loadableTracks.begin();
 	     i != m_loadableTracks.end(); ++i) {
@@ -859,37 +848,49 @@
 	    } else {
 		label = tr("Track %1 (untitled)%3").arg(trackNo).arg(perc);
 	    }
-	    available << label;
+
+            displayNames << label;
 	}
 
-	bool ok = false;
-	QString selected = QInputDialog::getItem
-	    (0, tr("Select track or tracks to import"),
-	     tr("<b>Select track to import</b><p>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.<p>Please select the track or merged tracks you wish to import:"),
-	     available, 0, false, &ok);
+        QString singleTrack;
 
-	if (!ok || selected.isEmpty()) return 0;
-	
-	if (selected == allTracks || selected == allNonPercussion) {
+        bool haveSomePercussion = 
+            (!m_percussionTracks.empty() &&
+             (m_percussionTracks.size() < m_loadableTracks.size()));
 
-	    for (set<unsigned int>::iterator i = m_loadableTracks.begin();
-		 i != m_loadableTracks.end(); ++i) {
-		
-		if (selected == allTracks ||
+        MIDIFileImportPreferenceAcquirer::TrackPreference pref;
+
+        if (m_acquirer) {
+            pref = m_acquirer->getTrackImportPreference(displayNames,
+                                                        haveSomePercussion,
+                                                        singleTrack);
+        } else {
+            pref = MIDIFileImportPreferenceAcquirer::MergeAllTracks;
+        }
+
+        if (pref == MIDIFileImportPreferenceAcquirer::ImportNothing) return 0;
+
+        if (pref == MIDIFileImportPreferenceAcquirer::MergeAllTracks ||
+            pref == MIDIFileImportPreferenceAcquirer::MergeAllNonPercussionTracks) {
+            
+            for (set<unsigned int>::iterator i = m_loadableTracks.begin();
+                 i != m_loadableTracks.end(); ++i) {
+                
+		if (pref == MIDIFileImportPreferenceAcquirer::MergeAllTracks ||
 		    m_percussionTracks.find(*i) == m_percussionTracks.end()) {
-
+                    
 		    tracksToLoad.insert(*i);
 		}
 	    }
 
 	} else {
 	    
-	    int j = nonTrackItems;
+	    int j = 0;
 
 	    for (set<unsigned int>::iterator i = m_loadableTracks.begin();
 		 i != m_loadableTracks.end(); ++i) {
 		
-		if (selected == available[j]) {
+		if (singleTrack == displayNames[j]) {
 		    tracksToLoad.insert(*i);
 		    break;
 		}
--- a/data/fileio/MIDIFileReader.h	Thu Mar 13 14:06:03 2008 +0000
+++ b/data/fileio/MIDIFileReader.h	Fri Mar 14 17:14:21 2008 +0000
@@ -35,12 +35,34 @@
 
 typedef unsigned char MIDIByte;
 
+class MIDIFileImportPreferenceAcquirer // welcome to our grand marble foyer
+{
+public:
+    enum TrackPreference {
+        ImportNothing,
+        ImportSingleTrack,
+        MergeAllTracks,
+        MergeAllNonPercussionTracks
+    };
+
+    virtual ~MIDIFileImportPreferenceAcquirer() { }
+
+    virtual TrackPreference getTrackImportPreference
+    (QStringList trackNames, bool haveSomePercussion,
+     QString &singleTrack) const = 0;
+
+    virtual void showError(QString error) = 0;
+};
+
+
 class MIDIFileReader : public DataFileReader
 {
     Q_OBJECT
 
 public:
-    MIDIFileReader(QString path, size_t mainModelSampleRate);
+    MIDIFileReader(QString path,
+                   MIDIFileImportPreferenceAcquirer *pref,
+                   size_t mainModelSampleRate);
     virtual ~MIDIFileReader();
 
     virtual bool isOK() const;
@@ -104,6 +126,8 @@
     size_t                 m_fileSize;
     QString                m_error;
     size_t                 m_mainModelSampleRate;
+
+    MIDIFileImportPreferenceAcquirer *m_acquirer;
 };
 
 
--- a/data/fileio/MP3FileReader.cpp	Thu Mar 13 14:06:03 2008 +0000
+++ b/data/fileio/MP3FileReader.cpp	Fri Mar 14 17:14:21 2008 +0000
@@ -17,7 +17,7 @@
 #ifdef HAVE_MAD
 
 #include "MP3FileReader.h"
-#include "ProgressPrinter.h"
+#include "base/ProgressReporter.h"
 
 #include "system/System.h"
 
@@ -32,12 +32,11 @@
 #endif
 #define DEBUG_ID3TAG 1
 
-#include <QApplication>
 #include <QFileInfo>
-#include <QProgressDialog>
 
 MP3FileReader::MP3FileReader(FileSource source, DecodeMode decodeMode, 
-                             CacheMode mode, size_t targetRate) :
+                             CacheMode mode, size_t targetRate,
+                             ProgressReporter *reporter) :
     CodedAudioFileReader(mode, targetRate),
     m_source(source),
     m_path(source.getLocalFilename()),
@@ -51,7 +50,7 @@
     m_cancelled = false;
     m_completion = 0;
     m_done = false;
-    m_progress = 0;
+    m_reporter = reporter;
 
     struct stat stat;
     if (::stat(m_path.toLocal8Bit().data(), &stat) == -1 || stat.st_size == 0) {
@@ -108,14 +107,10 @@
 
     if (decodeMode == DecodeAtOnce) {
 
-        if (dynamic_cast<QApplication *>(QCoreApplication::instance())) {
-            m_progress = new QProgressDialog
-                (QObject::tr("Decoding %1...").arg(QFileInfo(m_path).fileName()),
-                 QObject::tr("Stop"), 0, 100);
-            m_progress->hide();
-        } else {
-            ProgressPrinter *pp = new ProgressPrinter(tr("Decoding..."), this);
-            connect(this, SIGNAL(progress(int)), pp, SLOT(progress(int)));
+        if (m_reporter) {
+            connect(m_reporter, SIGNAL(cancelled()), this, SLOT(cancelled()));
+            m_reporter->setMessage
+                (tr("Decoding %1...").arg(QFileInfo(m_path).fileName()));
         }
 
         if (!decode(m_filebuffer, m_fileSize)) {
@@ -127,10 +122,9 @@
 
         if (isDecodeCacheInitialised()) finishDecodeCache();
 
-	delete m_progress;
-	m_progress = 0;
+    } else {
 
-    } else {
+        if (m_reporter) m_reporter->setProgress(100);
 
         m_decodeThread = new DecodeThread(this);
         m_decodeThread->start();
@@ -154,6 +148,12 @@
 }
 
 void
+MP3FileReader::cancelled()
+{
+    m_cancelled = true;
+}
+
+void
 MP3FileReader::loadTags()
 {
     m_title = "";
@@ -366,20 +366,9 @@
         int p = int(percent);
         if (p < 1) p = 1;
         if (p > 99) p = 99;
-        if (m_completion != p || (m_progress && !m_progress->isVisible())) {
+        if (m_completion != p && m_reporter) {
             m_completion = p;
-            emit progress(m_completion);
-            if (m_progress) {
-                if (m_completion > m_progress->value()) {
-                    m_progress->setValue(m_completion);
-                    m_progress->show();
-                    m_progress->raise();
-                    qApp->processEvents();
-                    if (m_progress->wasCanceled()) {
-                        m_cancelled = true;
-                    }
-                }
-            }
+            m_reporter->setProgress(m_completion);
         }
     }
 
--- a/data/fileio/MP3FileReader.h	Thu Mar 13 14:06:03 2008 +0000
+++ b/data/fileio/MP3FileReader.h	Fri Mar 14 17:14:21 2008 +0000
@@ -25,20 +25,23 @@
 
 #include <set>
 
-class QProgressDialog;
+class ProgressReporter;
 
 class MP3FileReader : public CodedAudioFileReader
 {
+    Q_OBJECT
+
 public:
     enum DecodeMode {
-        DecodeAtOnce, // decode the file on construction, with progress dialog
+        DecodeAtOnce, // decode the file on construction, with progress
         DecodeThreaded // decode in a background thread after construction
     };
 
     MP3FileReader(FileSource source,
                   DecodeMode decodeMode,
                   CacheMode cacheMode,
-                  size_t targetRate = 0);
+                  size_t targetRate = 0,
+                  ProgressReporter *reporter = 0);
     virtual ~MP3FileReader();
 
     virtual QString getError() const { return m_error; }
@@ -58,6 +61,9 @@
         return m_decodeThread && m_decodeThread->isRunning();
     }
 
+public slots:
+    void cancelled();
+
 protected:
     FileSource m_source;
     QString m_path;
@@ -74,7 +80,7 @@
     float **m_samplebuffer;
     size_t m_samplebuffersize;
 
-    QProgressDialog *m_progress;
+    ProgressReporter *m_reporter;
     bool m_cancelled;
 
     struct DecoderData
--- a/data/fileio/OggVorbisFileReader.cpp	Thu Mar 13 14:06:03 2008 +0000
+++ b/data/fileio/OggVorbisFileReader.cpp	Fri Mar 14 17:14:21 2008 +0000
@@ -17,8 +17,8 @@
 #ifdef HAVE_FISHSOUND
 
 #include "OggVorbisFileReader.h"
-#include "ProgressPrinter.h"
 
+#include "base/ProgressReporter.h"
 #include "base/Profiler.h"
 #include "system/System.h"
 
@@ -28,20 +28,19 @@
 #include <fcntl.h>
 #include <cmath>
 
-#include <QApplication>
 #include <QFileInfo>
-#include <QProgressDialog>
 
 static int instances = 0;
 
 OggVorbisFileReader::OggVorbisFileReader(FileSource source,
                                          DecodeMode decodeMode,
                                          CacheMode mode,
-                                         size_t targetRate) :
+                                         size_t targetRate,
+                                         ProgressReporter *reporter) :
     CodedAudioFileReader(mode, targetRate),
     m_source(source),
     m_path(source.getLocalFilename()),
-    m_progress(0),
+    m_reporter(reporter),
     m_fileSize(0),
     m_bytesRead(0),
     m_commentsRead(false),
@@ -72,14 +71,10 @@
 
     if (decodeMode == DecodeAtOnce) {
 
-        if (dynamic_cast<QApplication *>(QCoreApplication::instance())) {
-            m_progress = new QProgressDialog
-                (QObject::tr("Decoding %1...").arg(QFileInfo(m_path).fileName()),
-                 QObject::tr("Stop"), 0, 100);
-            m_progress->hide();
-        } else {
-            ProgressPrinter *pp = new ProgressPrinter(tr("Decoding..."), this);
-            connect(this, SIGNAL(progress(int)), pp, SLOT(progress(int)));
+        if (m_reporter) {
+            connect(m_reporter, SIGNAL(cancelled()), this, SLOT(cancelled()));
+            m_reporter->setMessage
+                (tr("Decoding %1...").arg(QFileInfo(m_path).fileName()));
         }
 
         while (oggz_read(m_oggz, 1024) > 0);
@@ -91,10 +86,9 @@
 
         if (isDecodeCacheInitialised()) finishDecodeCache();
 
-        delete m_progress;
-        m_progress = 0;
+    } else {
 
-    } else {
+        if (m_reporter) m_reporter->setProgress(100);
 
         while (oggz_read(m_oggz, 1024) > 0 &&
                (m_channelCount == 0 || m_fileRate == 0 || m_sampleRate == 0));
@@ -117,6 +111,12 @@
 }
 
 void
+OggVorbisFileReader::cancelled()
+{
+    m_cancelled = true; 
+}
+
+void
 OggVorbisFileReader::DecodeThread::run()
 {
     if (m_reader->m_cacheMode == CacheInTemporaryFile) {
@@ -156,16 +156,8 @@
     reader->m_completion = p;
     reader->progress(p);
 
-    if (reader->m_fileSize > 0 && reader->m_progress) {
-	if (p > reader->m_progress->value()) {
-	    reader->m_progress->setValue(p);
-	    reader->m_progress->show();
-	    reader->m_progress->raise();
-	    qApp->processEvents();
-	    if (reader->m_progress->wasCanceled()) {
-		reader->m_cancelled = true;
-	    }
-	}
+    if (reader->m_fileSize > 0 && reader->m_reporter) {
+        reader->m_reporter->setProgress(p);
     }
 
     if (reader->m_cancelled) return 1;
--- a/data/fileio/OggVorbisFileReader.h	Thu Mar 13 14:06:03 2008 +0000
+++ b/data/fileio/OggVorbisFileReader.h	Fri Mar 14 17:14:21 2008 +0000
@@ -27,20 +27,23 @@
 
 #include <set>
 
-class QProgressDialog;
+class ProgressReporter;
 
 class OggVorbisFileReader : public CodedAudioFileReader
 {
+    Q_OBJECT
+
 public:
     enum DecodeMode {
-        DecodeAtOnce, // decode the file on construction, with progress dialog
+        DecodeAtOnce, // decode the file on construction, with progress 
         DecodeThreaded // decode in a background thread after construction
     };
 
     OggVorbisFileReader(FileSource source,
                         DecodeMode decodeMode,
                         CacheMode cacheMode,
-                        size_t targetRate = 0);
+                        size_t targetRate = 0,
+                        ProgressReporter *reporter = 0);
     virtual ~OggVorbisFileReader();
 
     virtual QString getError() const { return m_error; }
@@ -60,6 +63,9 @@
         return m_decodeThread && m_decodeThread->isRunning();
     }
 
+public slots:
+    void cancelled();
+
 protected:
     FileSource m_source;
     QString m_path;
@@ -69,7 +75,7 @@
 
     OGGZ *m_oggz;
     FishSound *m_fishSound;
-    QProgressDialog *m_progress;
+    ProgressReporter *m_reporter;
     size_t m_fileSize;
     size_t m_bytesRead;
     bool m_commentsRead;
--- a/data/fileio/ProgressPrinter.cpp	Thu Mar 13 14:06:03 2008 +0000
+++ /dev/null	Thu Jan 01 00:00:00 1970 +0000
@@ -1,48 +0,0 @@
-/* -*- 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 "ProgressPrinter.h"
-
-#include <iostream>
-
-ProgressPrinter::ProgressPrinter(QString prefix, QObject *parent) :
-    QObject(parent),
-    m_prefix(prefix),
-    m_lastProgress(0)
-{
-}
-
-ProgressPrinter::~ProgressPrinter()
-{
-    if (m_lastProgress > 0 && m_lastProgress != 100) {
-        std::cerr << "\r\n";
-    }
-    std::cerr << "(progress printer dtor)" << std::endl;
-}
-
-void
-ProgressPrinter::progress(int progress)
-{
-    if (progress == m_lastProgress) return;
-    if (progress == 100) std::cerr << "\r\n";
-    else {
-        std::cerr << "\r"
-                  << m_prefix.toStdString() 
-                  << (m_prefix == "" ? "" : " ")
-                  << progress << "%";
-    }
-    m_lastProgress = progress;
-}
-
--- a/data/fileio/ProgressPrinter.h	Thu Mar 13 14:06:03 2008 +0000
+++ /dev/null	Thu Jan 01 00:00:00 1970 +0000
@@ -1,38 +0,0 @@
-/* -*- 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 _PROGRESS_PRINTER_H_
-#define _PROGRESS_PRINTER_H_
-
-#include <QObject>
-#include <QString>
-
-class ProgressPrinter : public QObject
-{
-    Q_OBJECT
-
-public:
-    ProgressPrinter(QString prefix = "", QObject *parent = 0);
-    virtual ~ProgressPrinter();
-    
-public slots:
-    void progress(int);
-
-protected:
-    QString m_prefix;
-    int m_lastProgress;
-};
-
-#endif
--- a/data/fileio/QuickTimeFileReader.cpp	Thu Mar 13 14:06:03 2008 +0000
+++ b/data/fileio/QuickTimeFileReader.cpp	Fri Mar 14 17:14:21 2008 +0000
@@ -20,11 +20,10 @@
 
 #include "QuickTimeFileReader.h"
 #include "base/Profiler.h"
+#include "base/ProgressReporter.h"
 #include "system/System.h"
 
-#include <QApplication>
 #include <QFileInfo>
-#include <QProgressDialog>
 
 #ifdef _WIN32
 #include <QTML.h>
@@ -51,12 +50,13 @@
 QuickTimeFileReader::QuickTimeFileReader(FileSource source,
                                          DecodeMode decodeMode,
                                          CacheMode mode,
-                                         size_t targetRate) :
+                                         size_t targetRate,
+                                         ProgressReporter *reporter) :
     CodedAudioFileReader(mode, targetRate),
     m_source(source),
     m_path(source.getLocalFilename()),
     m_d(new D),
-    m_progress(0),
+    m_reporter(reporter),
     m_cancelled(false),
     m_completion(0),
     m_decodeThread(0)
@@ -216,11 +216,10 @@
 
     if (decodeMode == DecodeAtOnce) {
 
-        if (dynamic_cast<QApplication *>(QCoreApplication::instance())) {
-            m_progress = new QProgressDialog
-                (QObject::tr("Decoding %1...").arg(QFileInfo(m_path).fileName()),
-                 QObject::tr("Stop"), 0, 100);
-            m_progress->hide();
+        if (m_reporter) {
+            connect(m_reporter, SIGNAL(cancelled()), this, SLOT(cancelled()));
+            m_reporter->setMessage
+                (tr("Decoding %1...").arg(QFileInfo(m_path).fileName()));
         }
 
         while (1) {
@@ -255,10 +254,9 @@
 
         m_completion = 100;
 
-        delete m_progress;
-        m_progress = 0;
+    } else {
+        if (m_reporter) m_reporter->setProgress(100);
 
-    } else {
         if (m_channelCount > 0) {
             m_decodeThread = new DecodeThread(this);
             m_decodeThread->start();
@@ -286,6 +284,12 @@
 }
 
 void
+QuickTimeFileReader::cancelled()
+{
+    m_cancelled = true;
+}
+
+void
 QuickTimeFileReader::DecodeThread::run()
 {
     if (m_reader->m_cacheMode == CacheInTemporaryFile) {
--- a/data/fileio/QuickTimeFileReader.h	Thu Mar 13 14:06:03 2008 +0000
+++ b/data/fileio/QuickTimeFileReader.h	Fri Mar 14 17:14:21 2008 +0000
@@ -27,20 +27,23 @@
 
 #include <set>
 
-class QProgressDialog;
+class ProgressReporter;
 
 class QuickTimeFileReader : public CodedAudioFileReader
 {
+    Q_OBJECT
+
 public:
     enum DecodeMode {
-        DecodeAtOnce, // decode the file on construction, with progress dialog
+        DecodeAtOnce, // decode the file on construction, with progress
         DecodeThreaded // decode in a background thread after construction
     };
 
     QuickTimeFileReader(FileSource source,
                         DecodeMode decodeMode,
                         CacheMode cacheMode,
-                        size_t targetRate = 0);
+                        size_t targetRate = 0,
+                        ProgressReporter *reporter = 0);
     virtual ~QuickTimeFileReader();
 
     virtual QString getError() const { return m_error; }
@@ -58,6 +61,9 @@
         return m_decodeThread && m_decodeThread->isRunning();
     }
 
+public slots:
+    void cancelled();
+
 protected:
     FileSource m_source;
     QString m_path;
@@ -67,7 +73,7 @@
     class D;
     D *m_d;
 
-    QProgressDialog *m_progress;
+    ProgressReporter *m_reporter;
     bool m_cancelled;
     int m_completion;
 
--- a/data/fileio/ResamplingWavFileReader.cpp	Thu Mar 13 14:06:03 2008 +0000
+++ b/data/fileio/ResamplingWavFileReader.cpp	Fri Mar 14 17:14:21 2008 +0000
@@ -17,15 +17,15 @@
 
 #include "WavFileReader.h"
 #include "base/Profiler.h"
+#include "base/ProgressReporter.h"
 
-#include <QProgressDialog>
 #include <QFileInfo>
-#include <QApplication>
 
 ResamplingWavFileReader::ResamplingWavFileReader(FileSource source,
 						 ResampleMode resampleMode,
 						 CacheMode mode,
-						 size_t targetRate) :
+						 size_t targetRate,
+                                                 ProgressReporter *reporter) :
     CodedAudioFileReader(mode, targetRate),
     m_source(source),
     m_path(source.getLocalFilename()),
@@ -33,7 +33,7 @@
     m_processed(0),
     m_completion(0),
     m_original(0),
-    m_progress(0),
+    m_reporter(reporter),
     m_decodeThread(0)
 {
     m_channelCount = 0;
@@ -57,11 +57,10 @@
 
     if (resampleMode == ResampleAtOnce) {
 
-        if (dynamic_cast<QApplication *>(QCoreApplication::instance())) {
-            m_progress = new QProgressDialog
-                (QObject::tr("Resampling %1...").arg(QFileInfo(m_path).fileName()),
-                 QObject::tr("Stop"), 0, 100);
-            m_progress->hide();
+        if (m_reporter) {
+            connect(m_reporter, SIGNAL(cancelled()), this, SLOT(cancelled()));
+            m_reporter->setMessage
+                (tr("Resampling %1...").arg(QFileInfo(m_path).fileName()));
         }
 
         size_t blockSize = 16384;
@@ -85,10 +84,9 @@
         delete m_original;
         m_original = 0;
 
-        delete m_progress;
-        m_progress = 0;
+    } else {
 
-    } else {
+        if (m_reporter) m_reporter->setProgress(100);
 
         m_decodeThread = new DecodeThread(this);
         m_decodeThread->start();
@@ -107,6 +105,12 @@
 }
 
 void
+ResamplingWavFileReader::cancelled()
+{
+    m_cancelled = true;
+}
+
+void
 ResamplingWavFileReader::DecodeThread::run()
 {
     if (m_reader->m_cacheMode == CacheInTemporaryFile) {
@@ -151,16 +155,8 @@
     if (progress > 99) progress = 99;
     m_completion = progress;
     
-    if (m_progress) {
-	if (progress > m_progress->value()) {
-	    m_progress->setValue(progress);
-	    m_progress->show();
-	    m_progress->raise();
-	    qApp->processEvents();
-	    if (m_progress->wasCanceled()) {
-		m_cancelled = true;
-	    }
-	}
+    if (m_reporter) {
+        m_reporter->setProgress(progress);
     }
 }
 
--- a/data/fileio/ResamplingWavFileReader.h	Thu Mar 13 14:06:03 2008 +0000
+++ b/data/fileio/ResamplingWavFileReader.h	Fri Mar 14 17:14:21 2008 +0000
@@ -23,10 +23,11 @@
 #include <set>
 
 class WavFileReader;
-class QProgressDialog;
+class ProgressReporter;
 
 class ResamplingWavFileReader : public CodedAudioFileReader
 {
+    Q_OBJECT
 public:
     enum ResampleMode {
         ResampleAtOnce, // resample the file on construction, with progress dialog
@@ -36,7 +37,8 @@
     ResamplingWavFileReader(FileSource source,
                             ResampleMode resampleMode,
                             CacheMode cacheMode,
-                            size_t targetRate = 0);
+                            size_t targetRate = 0,
+                            ProgressReporter *reporter = 0);
     virtual ~ResamplingWavFileReader();
 
     virtual QString getError() const { return m_error; }
@@ -52,6 +54,9 @@
         return m_decodeThread && m_decodeThread->isRunning();
     }
 
+public slots:
+    void cancelled();
+
 protected:
     FileSource m_source;
     QString m_path;
@@ -61,7 +66,7 @@
     int m_completion;
 
     WavFileReader *m_original;
-    QProgressDialog *m_progress;
+    ProgressReporter *m_reporter;
 
     void addBlock(const SampleBlock &frames);
     
--- a/data/model/WaveFileModel.cpp	Thu Mar 13 14:06:03 2008 +0000
+++ b/data/model/WaveFileModel.cpp	Fri Mar 14 17:14:21 2008 +0000
@@ -20,7 +20,6 @@
 
 #include "system/System.h"
 
-#include <QMessageBox>
 #include <QFileInfo>
 #include <QTextStream>