annotate widgets/CSVFormatDialog.cpp @ 947:e53a87a5efb2

Allow layers to be loaded without models if their layer class explicitly says it's OK (otherwise default template won't load, as it has an empty waveform layer)
author Chris Cannam
date Mon, 20 Apr 2015 10:10:26 +0100
parents 50be12cf2802
children 2633a1d73e39
rev   line source
Chris@378 1 /* -*- c-basic-offset: 4 indent-tabs-mode: nil -*- vi:set ts=8 sts=4 sw=4: */
Chris@378 2
Chris@378 3 /*
Chris@378 4 Sonic Visualiser
Chris@378 5 An audio file viewer and annotation editor.
Chris@378 6 Centre for Digital Music, Queen Mary, University of London.
Chris@378 7 This file copyright 2006 Chris Cannam.
Chris@378 8
Chris@378 9 This program is free software; you can redistribute it and/or
Chris@378 10 modify it under the terms of the GNU General Public License as
Chris@378 11 published by the Free Software Foundation; either version 2 of the
Chris@378 12 License, or (at your option) any later version. See the file
Chris@378 13 COPYING included with this distribution for more information.
Chris@378 14 */
Chris@378 15
Chris@378 16 #include "CSVFormatDialog.h"
Chris@378 17
Chris@561 18 #include "layer/LayerFactory.h"
Chris@561 19
Chris@674 20 #include "TextAbbrev.h"
Chris@674 21
Chris@378 22 #include <QFrame>
Chris@378 23 #include <QGridLayout>
Chris@378 24 #include <QPushButton>
Chris@378 25 #include <QHBoxLayout>
Chris@378 26 #include <QVBoxLayout>
Chris@378 27 #include <QTableWidget>
Chris@378 28 #include <QComboBox>
Chris@378 29 #include <QLabel>
Chris@512 30 #include <QDialogButtonBox>
Chris@378 31
Chris@560 32 #include <iostream>
Chris@913 33 #include <cmath>
Chris@378 34
Chris@682 35 #include "base/Debug.h"
Chris@682 36
Chris@581 37 CSVFormatDialog::CSVFormatDialog(QWidget *parent, CSVFormat format,
Chris@581 38 int maxDisplayCols) :
Chris@378 39 QDialog(parent),
Chris@581 40 m_format(format),
Chris@581 41 m_maxDisplayCols(maxDisplayCols),
Chris@581 42 m_fuzzyColumn(-1)
Chris@378 43 {
Chris@378 44 setModal(true);
Chris@378 45 setWindowTitle(tr("Select Data Format"));
Chris@378 46
Chris@378 47 QGridLayout *layout = new QGridLayout;
Chris@378 48
Chris@560 49 int row = 0;
Chris@378 50
Chris@560 51 layout->addWidget(new QLabel(tr("Please select the correct data format for this file.")),
Chris@560 52 row++, 0, 1, 4);
Chris@560 53
Chris@560 54 QFrame *exampleFrame = new QFrame;
Chris@560 55 exampleFrame->setFrameStyle(QFrame::StyledPanel | QFrame::Sunken);
Chris@560 56 exampleFrame->setLineWidth(2);
Chris@560 57 QGridLayout *exampleLayout = new QGridLayout;
Chris@561 58 exampleLayout->setSpacing(4);
Chris@560 59 exampleFrame->setLayout(exampleLayout);
Chris@560 60
Chris@560 61 QPalette palette = exampleFrame->palette();
Chris@560 62 palette.setColor(QPalette::Window, palette.color(QPalette::Base));
Chris@560 63 exampleFrame->setPalette(palette);
Chris@560 64
Chris@560 65 QFont fp;
Chris@908 66 fp.setPointSize(int(floor(fp.pointSize() * 0.9)));
Chris@561 67 // fp.setFixedPitch(true);
Chris@561 68 // fp.setStyleHint(QFont::TypeWriter);
Chris@561 69 // fp.setFamily("Monospaced");
Chris@560 70
Chris@560 71 int columns = format.getColumnCount();
Chris@560 72 QList<QStringList> example = m_format.getExample();
Chris@560 73
Chris@560 74 for (int i = 0; i < columns; ++i) {
Chris@581 75
Chris@560 76 QComboBox *cpc = new QComboBox;
Chris@560 77 m_columnPurposeCombos.push_back(cpc);
Chris@560 78 exampleLayout->addWidget(cpc, 0, i);
Chris@581 79 connect(cpc, SIGNAL(activated(int)), this, SLOT(columnPurposeChanged(int)));
Chris@581 80
Chris@581 81 if (i == m_maxDisplayCols && columns > i + 2) {
Chris@581 82 m_fuzzyColumn = i;
Chris@581 83 cpc->addItem(tr("<ignore>"));
Chris@581 84 cpc->addItem(tr("Values"));
Chris@581 85 cpc->setCurrentIndex
Chris@581 86 (m_format.getColumnPurpose(i-1) == CSVFormat::ColumnUnknown ? 0 : 1);
Chris@581 87 exampleLayout->addWidget(new QLabel(tr("(%1 more)").arg(columns - i)),
Chris@581 88 1, i);
Chris@581 89 break;
Chris@581 90 }
Chris@560 91
Chris@560 92 // NB must be in the same order as the CSVFormat::ColumnPurpose enum
Chris@560 93 cpc->addItem(tr("<ignore>")); // ColumnUnknown
Chris@560 94 cpc->addItem(tr("Time")); // ColumnStartTime
Chris@560 95 cpc->addItem(tr("End time")); // ColumnEndTime
Chris@560 96 cpc->addItem(tr("Duration")); // ColumnDuration
Chris@560 97 cpc->addItem(tr("Value")); // ColumnValue
Chris@763 98 cpc->addItem(tr("Pitch")); // ColumnPitch
Chris@560 99 cpc->addItem(tr("Label")); // ColumnLabel
Chris@560 100 cpc->setCurrentIndex(int(m_format.getColumnPurpose(i)));
Chris@560 101
Chris@581 102 for (int j = 0; j < example.size() && j < 6; ++j) {
Chris@866 103 if (i >= example[j].size()) {
Chris@866 104 continue;
Chris@866 105 }
Chris@581 106 QLabel *label = new QLabel;
Chris@581 107 label->setTextFormat(Qt::PlainText);
Chris@674 108 QString text = TextAbbrev::abbreviate(example[j][i], 35);
Chris@674 109 label->setText(text);
Chris@581 110 label->setFont(fp);
Chris@581 111 label->setPalette(palette);
Chris@581 112 label->setIndent(8);
Chris@581 113 exampleLayout->addWidget(label, j+1, i);
Chris@560 114 }
Chris@560 115 }
Chris@560 116
Chris@560 117 layout->addWidget(exampleFrame, row, 0, 1, 4);
Chris@560 118 layout->setColumnStretch(3, 10);
Chris@560 119 layout->setRowStretch(row++, 10);
Chris@560 120
Chris@560 121 layout->addWidget(new QLabel(tr("Timing is specified:")), row, 0);
Chris@378 122
Chris@378 123 m_timingTypeCombo = new QComboBox;
Chris@560 124 m_timingTypeCombo->addItem(tr("Explicitly, in seconds"));
Chris@865 125 m_timingTypeCombo->addItem(tr("Explicitly, in milliseconds"));
Chris@560 126 m_timingTypeCombo->addItem(tr("Explicitly, in audio sample frames"));
Chris@560 127 m_timingTypeCombo->addItem(tr("Implicitly: rows are equally spaced in time"));
Chris@560 128 layout->addWidget(m_timingTypeCombo, row++, 1, 1, 2);
Chris@378 129 connect(m_timingTypeCombo, SIGNAL(activated(int)),
Chris@378 130 this, SLOT(timingTypeChanged(int)));
Chris@560 131 m_timingTypeCombo->setCurrentIndex
Chris@560 132 (m_format.getTimingType() == CSVFormat::ExplicitTiming ?
Chris@865 133 m_format.getTimeUnits() == CSVFormat::TimeSeconds ? 0 : 2 : 3);
Chris@559 134
Chris@378 135 m_sampleRateLabel = new QLabel(tr("Audio sample rate (Hz):"));
Chris@560 136 layout->addWidget(m_sampleRateLabel, row, 0);
Chris@378 137
Chris@807 138 int sampleRates[] = {
Chris@378 139 8000, 11025, 12000, 22050, 24000, 32000,
Chris@378 140 44100, 48000, 88200, 96000, 176400, 192000
Chris@378 141 };
Chris@378 142
Chris@378 143 m_sampleRateCombo = new QComboBox;
Chris@807 144 for (int i = 0; i < int(sizeof(sampleRates) / sizeof(sampleRates[0])); ++i) {
Chris@378 145 m_sampleRateCombo->addItem(QString("%1").arg(sampleRates[i]));
Chris@560 146 if (sampleRates[i] == m_format.getSampleRate()) {
Chris@560 147 m_sampleRateCombo->setCurrentIndex(i);
Chris@560 148 }
Chris@378 149 }
Chris@378 150 m_sampleRateCombo->setEditable(true);
Chris@378 151
Chris@560 152 layout->addWidget(m_sampleRateCombo, row++, 1);
Chris@378 153 connect(m_sampleRateCombo, SIGNAL(activated(QString)),
Chris@378 154 this, SLOT(sampleRateChanged(QString)));
Chris@378 155 connect(m_sampleRateCombo, SIGNAL(editTextChanged(QString)),
Chris@378 156 this, SLOT(sampleRateChanged(QString)));
Chris@378 157
Chris@378 158 m_windowSizeLabel = new QLabel(tr("Frame increment between rows:"));
Chris@560 159 layout->addWidget(m_windowSizeLabel, row, 0);
Chris@378 160
Chris@378 161 m_windowSizeCombo = new QComboBox;
Chris@378 162 for (int i = 0; i <= 16; ++i) {
Chris@378 163 int value = 1 << i;
Chris@378 164 m_windowSizeCombo->addItem(QString("%1").arg(value));
Chris@560 165 if (value == int(m_format.getWindowSize())) {
Chris@560 166 m_windowSizeCombo->setCurrentIndex(i);
Chris@560 167 }
Chris@378 168 }
Chris@378 169 m_windowSizeCombo->setEditable(true);
Chris@378 170
Chris@560 171 layout->addWidget(m_windowSizeCombo, row++, 1);
Chris@378 172 connect(m_windowSizeCombo, SIGNAL(activated(QString)),
Chris@378 173 this, SLOT(windowSizeChanged(QString)));
Chris@378 174 connect(m_windowSizeCombo, SIGNAL(editTextChanged(QString)),
Chris@378 175 this, SLOT(windowSizeChanged(QString)));
Chris@378 176
Chris@561 177 m_modelLabel = new QLabel;
Chris@561 178 QFont f(m_modelLabel->font());
Chris@561 179 f.setItalic(true);
Chris@561 180 m_modelLabel->setFont(f);
Chris@561 181 layout->addWidget(m_modelLabel, row++, 0, 1, 4);
Chris@561 182
Chris@512 183 QDialogButtonBox *bb = new QDialogButtonBox(QDialogButtonBox::Ok |
Chris@512 184 QDialogButtonBox::Cancel);
Chris@560 185 layout->addWidget(bb, row++, 0, 1, 4);
Chris@512 186 connect(bb, SIGNAL(accepted()), this, SLOT(accept()));
Chris@512 187 connect(bb, SIGNAL(rejected()), this, SLOT(reject()));
Chris@378 188
Chris@512 189 setLayout(layout);
Chris@378 190
Chris@378 191 timingTypeChanged(m_timingTypeCombo->currentIndex());
Chris@561 192 updateModelLabel();
Chris@378 193 }
Chris@378 194
Chris@378 195 CSVFormatDialog::~CSVFormatDialog()
Chris@378 196 {
Chris@378 197 }
Chris@378 198
Chris@378 199 CSVFormat
Chris@378 200 CSVFormatDialog::getFormat() const
Chris@378 201 {
Chris@560 202 return m_format;
Chris@378 203 }
Chris@378 204
Chris@378 205 void
Chris@561 206 CSVFormatDialog::updateModelLabel()
Chris@378 207 {
Chris@561 208 LayerFactory *f = LayerFactory::getInstance();
Chris@561 209
Chris@561 210 QString s;
Chris@561 211 switch (m_format.getModelType()) {
Chris@561 212 case CSVFormat::OneDimensionalModel:
Chris@561 213 s = f->getLayerPresentationName(LayerFactory::TimeInstants);
Chris@561 214 break;
Chris@561 215 case CSVFormat::TwoDimensionalModel:
Chris@561 216 s = f->getLayerPresentationName(LayerFactory::TimeValues);
Chris@561 217 break;
Chris@561 218 case CSVFormat::TwoDimensionalModelWithDuration:
Chris@561 219 s = f->getLayerPresentationName(LayerFactory::Regions);
Chris@561 220 break;
Chris@763 221 case CSVFormat::TwoDimensionalModelWithDurationAndPitch:
Chris@763 222 s = f->getLayerPresentationName(LayerFactory::Notes);
Chris@763 223 break;
Chris@561 224 case CSVFormat::ThreeDimensionalModel:
Chris@561 225 s = f->getLayerPresentationName(LayerFactory::Colour3DPlot);
Chris@561 226 break;
Chris@561 227 }
Chris@561 228
Chris@578 229 m_modelLabel->setText("\n" + tr("Data will be displayed in a %1 layer.").arg(s));
Chris@378 230 }
Chris@378 231
Chris@378 232 void
Chris@378 233 CSVFormatDialog::timingTypeChanged(int type)
Chris@378 234 {
Chris@378 235 switch (type) {
Chris@378 236
Chris@378 237 case 0:
Chris@560 238 m_format.setTimingType(CSVFormat::ExplicitTiming);
Chris@560 239 m_format.setTimeUnits(CSVFormat::TimeSeconds);
Chris@378 240 m_sampleRateCombo->setEnabled(false);
Chris@378 241 m_sampleRateLabel->setEnabled(false);
Chris@378 242 m_windowSizeCombo->setEnabled(false);
Chris@378 243 m_windowSizeLabel->setEnabled(false);
Chris@378 244 break;
Chris@378 245
Chris@378 246 case 1:
Chris@560 247 m_format.setTimingType(CSVFormat::ExplicitTiming);
Chris@865 248 m_format.setTimeUnits(CSVFormat::TimeMilliseconds);
Chris@865 249 m_sampleRateCombo->setEnabled(true);
Chris@865 250 m_sampleRateLabel->setEnabled(true);
Chris@865 251 m_windowSizeCombo->setEnabled(false);
Chris@865 252 m_windowSizeLabel->setEnabled(false);
Chris@865 253 break;
Chris@865 254
Chris@865 255 case 2:
Chris@865 256 m_format.setTimingType(CSVFormat::ExplicitTiming);
Chris@560 257 m_format.setTimeUnits(CSVFormat::TimeAudioFrames);
Chris@378 258 m_sampleRateCombo->setEnabled(true);
Chris@378 259 m_sampleRateLabel->setEnabled(true);
Chris@378 260 m_windowSizeCombo->setEnabled(false);
Chris@378 261 m_windowSizeLabel->setEnabled(false);
Chris@378 262 break;
Chris@378 263
Chris@865 264 case 3:
Chris@560 265 m_format.setTimingType(CSVFormat::ImplicitTiming);
Chris@560 266 m_format.setTimeUnits(CSVFormat::TimeWindows);
Chris@378 267 m_sampleRateCombo->setEnabled(true);
Chris@378 268 m_sampleRateLabel->setEnabled(true);
Chris@378 269 m_windowSizeCombo->setEnabled(true);
Chris@378 270 m_windowSizeLabel->setEnabled(true);
Chris@378 271 break;
Chris@378 272 }
Chris@559 273 }
Chris@559 274
Chris@559 275 void
Chris@378 276 CSVFormatDialog::sampleRateChanged(QString rateString)
Chris@378 277 {
Chris@378 278 bool ok = false;
Chris@378 279 int sampleRate = rateString.toInt(&ok);
Chris@560 280 if (ok) m_format.setSampleRate(sampleRate);
Chris@378 281 }
Chris@378 282
Chris@378 283 void
Chris@378 284 CSVFormatDialog::windowSizeChanged(QString sizeString)
Chris@378 285 {
Chris@378 286 bool ok = false;
Chris@378 287 int size = sizeString.toInt(&ok);
Chris@560 288 if (ok) m_format.setWindowSize(size);
Chris@378 289 }
Chris@560 290
Chris@560 291 void
Chris@561 292 CSVFormatDialog::columnPurposeChanged(int p)
Chris@560 293 {
Chris@560 294 QObject *o = sender();
Chris@561 295
Chris@560 296 QComboBox *cb = qobject_cast<QComboBox *>(o);
Chris@560 297 if (!cb) return;
Chris@561 298
Chris@561 299 CSVFormat::ColumnPurpose purpose = (CSVFormat::ColumnPurpose)p;
Chris@561 300
Chris@561 301 bool haveStartTime = false;
Chris@561 302 bool haveDuration = false;
Chris@763 303 bool havePitch = false;
Chris@561 304 int valueCount = 0;
Chris@561 305
Chris@560 306 for (int i = 0; i < m_columnPurposeCombos.size(); ++i) {
Chris@561 307
Chris@561 308 CSVFormat::ColumnPurpose cp = m_format.getColumnPurpose(i);
Chris@561 309
Chris@561 310 bool thisChanged = (cb == m_columnPurposeCombos[i]);
Chris@561 311
Chris@561 312 if (thisChanged) {
Chris@561 313
Chris@682 314 cerr << "i == " << i << ", fuzzy == " << m_fuzzyColumn
Chris@682 315 << ", p == " << p << endl;
Chris@581 316
Chris@581 317 if (i == m_fuzzyColumn) {
Chris@581 318 for (int j = i; j < m_format.getColumnCount(); ++j) {
Chris@581 319 if (p == 0) { // Ignore
Chris@581 320 m_format.setColumnPurpose(j, CSVFormat::ColumnUnknown);
Chris@581 321 } else { // Value
Chris@581 322 m_format.setColumnPurpose(j, CSVFormat::ColumnValue);
Chris@581 323 ++valueCount;
Chris@581 324 }
Chris@581 325 }
Chris@581 326 continue;
Chris@581 327 }
Chris@581 328
Chris@561 329 cp = purpose;
Chris@561 330
Chris@561 331 } else {
Chris@561 332
Chris@581 333 if (i == m_fuzzyColumn) continue;
Chris@581 334
Chris@561 335 // We can only have one ColumnStartTime column, and only
Chris@561 336 // one of either ColumnDuration or ColumnEndTime
Chris@561 337
Chris@561 338 if (purpose == CSVFormat::ColumnStartTime) {
Chris@561 339 if (cp == purpose) {
Chris@561 340 cp = CSVFormat::ColumnValue;
Chris@561 341 }
Chris@561 342 } else if (purpose == CSVFormat::ColumnDuration ||
Chris@561 343 purpose == CSVFormat::ColumnEndTime) {
Chris@561 344 if (cp == CSVFormat::ColumnDuration ||
Chris@561 345 cp == CSVFormat::ColumnEndTime) {
Chris@561 346 cp = CSVFormat::ColumnValue;
Chris@561 347 }
Chris@561 348 }
Chris@561 349
Chris@561 350 // And we can only have one label
Chris@561 351 if (purpose == CSVFormat::ColumnLabel) {
Chris@561 352 if (cp == purpose) {
Chris@561 353 cp = CSVFormat::ColumnUnknown;
Chris@561 354 }
Chris@561 355 }
Chris@561 356 }
Chris@561 357
Chris@561 358 if (cp == CSVFormat::ColumnStartTime) {
Chris@561 359 haveStartTime = true;
Chris@561 360 }
Chris@561 361 if (cp == CSVFormat::ColumnEndTime ||
Chris@561 362 cp == CSVFormat::ColumnDuration) {
Chris@561 363 haveDuration = true;
Chris@561 364 }
Chris@763 365 if (cp == CSVFormat::ColumnPitch) {
Chris@763 366 havePitch = true;
Chris@763 367 }
Chris@561 368 if (cp == CSVFormat::ColumnValue) {
Chris@561 369 ++valueCount;
Chris@561 370 }
Chris@561 371
Chris@561 372 m_columnPurposeCombos[i]->setCurrentIndex(int(cp));
Chris@561 373 m_format.setColumnPurpose(i, cp);
Chris@561 374 }
Chris@561 375
Chris@561 376 if (!haveStartTime) {
Chris@561 377 m_timingTypeCombo->setCurrentIndex(2);
Chris@561 378 timingTypeChanged(2);
Chris@561 379 }
Chris@561 380
Chris@561 381 if (haveStartTime && haveDuration) {
Chris@763 382 if (havePitch) {
Chris@763 383 m_format.setModelType(CSVFormat::TwoDimensionalModelWithDurationAndPitch);
Chris@763 384 } else {
Chris@763 385 m_format.setModelType(CSVFormat::TwoDimensionalModelWithDuration);
Chris@763 386 }
Chris@561 387 } else {
Chris@561 388 if (valueCount > 1) {
Chris@561 389 m_format.setModelType(CSVFormat::ThreeDimensionalModel);
Chris@561 390 } else if (valueCount > 0) {
Chris@561 391 m_format.setModelType(CSVFormat::TwoDimensionalModel);
Chris@561 392 } else {
Chris@561 393 m_format.setModelType(CSVFormat::OneDimensionalModel);
Chris@560 394 }
Chris@560 395 }
Chris@561 396
Chris@561 397 updateModelLabel();
Chris@560 398 }
Chris@560 399
Chris@560 400