Chris@378: /* -*- c-basic-offset: 4 indent-tabs-mode: nil -*-  vi:set ts=8 sts=4 sw=4: */
Chris@378: 
Chris@378: /*
Chris@378:     Sonic Visualiser
Chris@378:     An audio file viewer and annotation editor.
Chris@378:     Centre for Digital Music, Queen Mary, University of London.
Chris@378:     This file copyright 2006 Chris Cannam.
Chris@378:     
Chris@378:     This program is free software; you can redistribute it and/or
Chris@378:     modify it under the terms of the GNU General Public License as
Chris@378:     published by the Free Software Foundation; either version 2 of the
Chris@378:     License, or (at your option) any later version.  See the file
Chris@378:     COPYING included with this distribution for more information.
Chris@378: */
Chris@378: 
Chris@378: #include "CSVFormatDialog.h"
Chris@378: 
Chris@378: #include <QFrame>
Chris@378: #include <QGridLayout>
Chris@378: #include <QPushButton>
Chris@378: #include <QHBoxLayout>
Chris@378: #include <QVBoxLayout>
Chris@378: #include <QTableWidget>
Chris@378: #include <QComboBox>
Chris@378: #include <QLabel>
Chris@378: 
Chris@378: 
Chris@378: CSVFormatDialog::CSVFormatDialog(QWidget *parent, CSVFormat format,
Chris@378: 				 size_t defaultSampleRate) :
Chris@378:     QDialog(parent),
Chris@378:     m_modelType(CSVFormat::OneDimensionalModel),
Chris@378:     m_timingType(CSVFormat::ExplicitTiming),
Chris@378:     m_timeUnits(CSVFormat::TimeAudioFrames),
Chris@378:     m_separator(""),
Chris@378:     m_behaviour(QString::KeepEmptyParts)
Chris@378: {
Chris@378:     setModal(true);
Chris@378:     setWindowTitle(tr("Select Data Format"));
Chris@378: 
Chris@378:     m_modelType = format.getModelType();
Chris@378:     m_timingType = format.getTimingType();
Chris@378:     m_timeUnits = format.getTimeUnits();
Chris@378:     m_separator = format.getSeparator();
Chris@378:     m_sampleRate = format.getSampleRate();
Chris@378:     m_windowSize = format.getWindowSize();
Chris@378:     m_behaviour = format.getSplitBehaviour();
Chris@378:     m_example = format.getExample();
Chris@378:     m_maxExampleCols = format.getMaxExampleCols();
Chris@378: 
Chris@378:     QGridLayout *layout = new QGridLayout;
Chris@378: 
Chris@378:     layout->addWidget(new QLabel(tr("<b>Select Data Format</b><p>Please select the correct data format for this file.")),
Chris@378: 		      0, 0, 1, 4);
Chris@378: 
Chris@378:     layout->addWidget(new QLabel(tr("Each row specifies:")), 1, 0);
Chris@378: 
Chris@378:     m_modelTypeCombo = new QComboBox;
Chris@378:     m_modelTypeCombo->addItem(tr("A point in time"));
Chris@378:     m_modelTypeCombo->addItem(tr("A value at a time"));
Chris@378:     m_modelTypeCombo->addItem(tr("A set of values"));
Chris@378:     layout->addWidget(m_modelTypeCombo, 1, 1, 1, 2);
Chris@378:     connect(m_modelTypeCombo, SIGNAL(activated(int)),
Chris@378: 	    this, SLOT(modelTypeChanged(int)));
Chris@378:     m_modelTypeCombo->setCurrentIndex(int(m_modelType));
Chris@378: 
Chris@378:     layout->addWidget(new QLabel(tr("The first column contains:")), 2, 0);
Chris@378:     
Chris@378:     m_timingTypeCombo = new QComboBox;
Chris@378:     m_timingTypeCombo->addItem(tr("Time, in seconds"));
Chris@378:     m_timingTypeCombo->addItem(tr("Time, in audio sample frames"));
Chris@378:     m_timingTypeCombo->addItem(tr("Data (rows are consecutive in time)"));
Chris@378:     layout->addWidget(m_timingTypeCombo, 2, 1, 1, 2);
Chris@378:     connect(m_timingTypeCombo, SIGNAL(activated(int)),
Chris@378: 	    this, SLOT(timingTypeChanged(int)));
Chris@378:     m_timingTypeCombo->setCurrentIndex(m_timingType == CSVFormat::ExplicitTiming ?
Chris@378:                                        m_timeUnits == CSVFormat::TimeSeconds ? 0 : 1 : 2);
Chris@378: 
Chris@378:     m_sampleRateLabel = new QLabel(tr("Audio sample rate (Hz):"));
Chris@378:     layout->addWidget(m_sampleRateLabel, 3, 0);
Chris@378:     
Chris@378:     size_t sampleRates[] = {
Chris@378: 	8000, 11025, 12000, 22050, 24000, 32000,
Chris@378: 	44100, 48000, 88200, 96000, 176400, 192000
Chris@378:     };
Chris@378: 
Chris@378:     m_sampleRateCombo = new QComboBox;
Chris@378:     m_sampleRate = defaultSampleRate;
Chris@378:     for (size_t i = 0; i < sizeof(sampleRates) / sizeof(sampleRates[0]); ++i) {
Chris@378: 	m_sampleRateCombo->addItem(QString("%1").arg(sampleRates[i]));
Chris@378: 	if (sampleRates[i] == m_sampleRate) m_sampleRateCombo->setCurrentIndex(i);
Chris@378:     }
Chris@378:     m_sampleRateCombo->setEditable(true);
Chris@378: 
Chris@378:     layout->addWidget(m_sampleRateCombo, 3, 1);
Chris@378:     connect(m_sampleRateCombo, SIGNAL(activated(QString)),
Chris@378: 	    this, SLOT(sampleRateChanged(QString)));
Chris@378:     connect(m_sampleRateCombo, SIGNAL(editTextChanged(QString)),
Chris@378: 	    this, SLOT(sampleRateChanged(QString)));
Chris@378: 
Chris@378:     m_windowSizeLabel = new QLabel(tr("Frame increment between rows:"));
Chris@378:     layout->addWidget(m_windowSizeLabel, 4, 0);
Chris@378: 
Chris@378:     m_windowSizeCombo = new QComboBox;
Chris@378:     m_windowSize = 1024;
Chris@378:     for (int i = 0; i <= 16; ++i) {
Chris@378: 	int value = 1 << i;
Chris@378: 	m_windowSizeCombo->addItem(QString("%1").arg(value));
Chris@378: 	if (value == int(m_windowSize)) m_windowSizeCombo->setCurrentIndex(i);
Chris@378:     }
Chris@378:     m_windowSizeCombo->setEditable(true);
Chris@378: 
Chris@378:     layout->addWidget(m_windowSizeCombo, 4, 1);
Chris@378:     connect(m_windowSizeCombo, SIGNAL(activated(QString)),
Chris@378: 	    this, SLOT(windowSizeChanged(QString)));
Chris@378:     connect(m_windowSizeCombo, SIGNAL(editTextChanged(QString)),
Chris@378: 	    this, SLOT(windowSizeChanged(QString)));
Chris@378: 
Chris@378:     layout->addWidget(new QLabel(tr("\nExample data from file:")), 5, 0, 1, 4);
Chris@378: 
Chris@378:     m_exampleWidget = new QTableWidget
Chris@378: 	(std::min(10, m_example.size()), m_maxExampleCols);
Chris@378: 
Chris@378:     layout->addWidget(m_exampleWidget, 6, 0, 1, 4);
Chris@378:     layout->setColumnStretch(3, 10);
Chris@378:     layout->setRowStretch(4, 10);
Chris@378: 
Chris@378:     QPushButton *ok = new QPushButton(tr("OK"));
Chris@378:     connect(ok, SIGNAL(clicked()), this, SLOT(accept()));
Chris@378:     ok->setDefault(true);
Chris@378: 
Chris@378:     QPushButton *cancel = new QPushButton(tr("Cancel"));
Chris@378:     connect(cancel, SIGNAL(clicked()), this, SLOT(reject()));
Chris@378: 
Chris@378:     QHBoxLayout *buttonLayout = new QHBoxLayout;
Chris@378:     buttonLayout->addStretch(1);
Chris@378:     buttonLayout->addWidget(ok);
Chris@378:     buttonLayout->addWidget(cancel);
Chris@378: 
Chris@378:     QVBoxLayout *mainLayout = new QVBoxLayout;
Chris@378:     mainLayout->addLayout(layout);
Chris@378:     mainLayout->addLayout(buttonLayout);
Chris@378: 
Chris@378:     setLayout(mainLayout);
Chris@378:     
Chris@378:     timingTypeChanged(m_timingTypeCombo->currentIndex());
Chris@378: }
Chris@378: 
Chris@378: CSVFormatDialog::~CSVFormatDialog()
Chris@378: {
Chris@378: }
Chris@378: 
Chris@378: CSVFormat
Chris@378: CSVFormatDialog::getFormat() const
Chris@378: {
Chris@378:     CSVFormat format;
Chris@378:     format.setModelType(m_modelType);
Chris@378:     format.setTimingType(m_timingType);
Chris@378:     format.setTimeUnits(m_timeUnits);
Chris@378:     format.setSeparator(m_separator);
Chris@378:     format.setSampleRate(m_sampleRate);
Chris@378:     format.setWindowSize(m_windowSize);
Chris@378:     format.setSplitBehaviour(m_behaviour);
Chris@378:     return format;
Chris@378: }
Chris@378: 
Chris@378: void
Chris@378: CSVFormatDialog::populateExample()
Chris@378: {
Chris@378:     m_exampleWidget->setColumnCount
Chris@378:         (m_timingType == CSVFormat::ExplicitTiming ?
Chris@378: 	 m_maxExampleCols - 1 : m_maxExampleCols);
Chris@378: 
Chris@378:     m_exampleWidget->setHorizontalHeaderLabels(QStringList());
Chris@378: 
Chris@378:     for (int i = 0; i < m_example.size(); ++i) {
Chris@378: 	for (int j = 0; j < m_example[i].size(); ++j) {
Chris@378: 
Chris@378: 	    QTableWidgetItem *item = new QTableWidgetItem(m_example[i][j]);
Chris@378: 
Chris@378: 	    if (j == 0) {
Chris@378: 		if (m_timingType == CSVFormat::ExplicitTiming) {
Chris@378: 		    m_exampleWidget->setVerticalHeaderItem(i, item);
Chris@378: 		    continue;
Chris@378: 		} else {
Chris@378: 		    QTableWidgetItem *header =
Chris@378: 			new QTableWidgetItem(QString("%1").arg(i));
Chris@378: 		    header->setFlags(Qt::ItemIsEnabled);
Chris@378: 		    m_exampleWidget->setVerticalHeaderItem(i, header);
Chris@378: 		}
Chris@378: 	    }
Chris@378: 	    int index = j;
Chris@378: 	    if (m_timingType == CSVFormat::ExplicitTiming) --index;
Chris@378: 	    item->setFlags(Qt::ItemIsEnabled);
Chris@378: 	    m_exampleWidget->setItem(i, index, item);
Chris@378: 	}
Chris@378:     }
Chris@378: }
Chris@378: 
Chris@378: void
Chris@378: CSVFormatDialog::modelTypeChanged(int type)
Chris@378: {
Chris@378:     m_modelType = (CSVFormat::ModelType)type;
Chris@378: 
Chris@378:     if (m_modelType == CSVFormat::ThreeDimensionalModel) {
Chris@378:         // We can't load 3d models with explicit timing, because the 3d
Chris@378:         // model is dense so we need a fixed sample increment
Chris@378:         m_timingTypeCombo->setCurrentIndex(2);
Chris@378:         timingTypeChanged(2);
Chris@378:     }
Chris@378: }
Chris@378: 
Chris@378: void
Chris@378: CSVFormatDialog::timingTypeChanged(int type)
Chris@378: {
Chris@378:     switch (type) {
Chris@378: 
Chris@378:     case 0:
Chris@378: 	m_timingType = CSVFormat::ExplicitTiming;
Chris@378: 	m_timeUnits = CSVFormat::TimeSeconds;
Chris@378: 	m_sampleRateCombo->setEnabled(false);
Chris@378: 	m_sampleRateLabel->setEnabled(false);
Chris@378: 	m_windowSizeCombo->setEnabled(false);
Chris@378: 	m_windowSizeLabel->setEnabled(false);
Chris@378:         if (m_modelType == CSVFormat::ThreeDimensionalModel) {
Chris@378:             m_modelTypeCombo->setCurrentIndex(1);
Chris@378:             modelTypeChanged(1);
Chris@378:         }
Chris@378: 	break;
Chris@378: 
Chris@378:     case 1:
Chris@378: 	m_timingType = CSVFormat::ExplicitTiming;
Chris@378: 	m_timeUnits = CSVFormat::TimeAudioFrames;
Chris@378: 	m_sampleRateCombo->setEnabled(true);
Chris@378: 	m_sampleRateLabel->setEnabled(true);
Chris@378: 	m_windowSizeCombo->setEnabled(false);
Chris@378: 	m_windowSizeLabel->setEnabled(false);
Chris@378:         if (m_modelType == CSVFormat::ThreeDimensionalModel) {
Chris@378:             m_modelTypeCombo->setCurrentIndex(1);
Chris@378:             modelTypeChanged(1);
Chris@378:         }
Chris@378: 	break;
Chris@378: 
Chris@378:     case 2:
Chris@378: 	m_timingType = CSVFormat::ImplicitTiming;
Chris@378: 	m_timeUnits = CSVFormat::TimeWindows;
Chris@378: 	m_sampleRateCombo->setEnabled(true);
Chris@378: 	m_sampleRateLabel->setEnabled(true);
Chris@378: 	m_windowSizeCombo->setEnabled(true);
Chris@378: 	m_windowSizeLabel->setEnabled(true);
Chris@378: 	break;
Chris@378:     }
Chris@378: 
Chris@378:     populateExample();
Chris@378: }
Chris@378: 
Chris@378: void
Chris@378: CSVFormatDialog::sampleRateChanged(QString rateString)
Chris@378: {
Chris@378:     bool ok = false;
Chris@378:     int sampleRate = rateString.toInt(&ok);
Chris@378:     if (ok) m_sampleRate = sampleRate;
Chris@378: }
Chris@378: 
Chris@378: void
Chris@378: CSVFormatDialog::windowSizeChanged(QString sizeString)
Chris@378: {
Chris@378:     bool ok = false;
Chris@378:     int size = sizeString.toInt(&ok);
Chris@378:     if (ok) m_windowSize = size;
Chris@378: }