annotate widgets/CSVFormatDialog.cpp @ 1615:911330a28a7c

Where the "below" view represents only a subset of the "above" view, cut off the feature mappings at the outer edges of the "below" view - don't map everything outside this (it would all just map onto the same single points at beginning and end, which is excessive, confusing and not useful)
author Chris Cannam
date Thu, 02 Jul 2020 15:37:43 +0100
parents 129c704566ff
children
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@1319 7 This file copyright 2006-2018 Chris Cannam and QMUL.
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@1612 31 #include <QCheckBox>
Chris@378 32
Chris@560 33 #include <iostream>
Chris@913 34 #include <cmath>
Chris@378 35
Chris@682 36 #include "base/Debug.h"
Chris@682 37
Chris@1413 38 CSVFormatDialog::CSVFormatDialog(QWidget *parent,
Chris@1413 39 CSVFormat format,
Chris@581 40 int maxDisplayCols) :
Chris@378 41 QDialog(parent),
Chris@1413 42 m_csvFilePath(""),
Chris@1413 43 m_referenceSampleRate(0),
Chris@581 44 m_format(format),
Chris@581 45 m_maxDisplayCols(maxDisplayCols),
Chris@581 46 m_fuzzyColumn(-1)
Chris@378 47 {
Chris@1413 48 init();
Chris@1413 49 }
Chris@1413 50
Chris@1413 51 CSVFormatDialog::CSVFormatDialog(QWidget *parent,
Chris@1413 52 QString csvFilePath,
Chris@1413 53 sv_samplerate_t referenceSampleRate,
Chris@1413 54 int maxDisplayCols) :
Chris@1413 55 QDialog(parent),
Chris@1413 56 m_csvFilePath(csvFilePath),
Chris@1413 57 m_referenceSampleRate(referenceSampleRate),
Chris@1413 58 m_maxDisplayCols(maxDisplayCols),
Chris@1413 59 m_fuzzyColumn(-1)
Chris@1413 60 {
Chris@1413 61 m_format = CSVFormat(csvFilePath);
Chris@1413 62 m_format.setSampleRate(referenceSampleRate);
Chris@1413 63 init();
Chris@1413 64 }
Chris@1413 65
Chris@1413 66 CSVFormatDialog::~CSVFormatDialog()
Chris@1413 67 {
Chris@1413 68 }
Chris@1413 69
Chris@1413 70 static int sampleRates[] = {
Chris@1413 71 8000, 11025, 12000, 22050, 24000, 32000,
Chris@1413 72 44100, 48000, 88200, 96000, 176400, 192000
Chris@1413 73 };
Chris@1413 74
Chris@1413 75 void
Chris@1413 76 CSVFormatDialog::init()
Chris@1413 77 {
Chris@378 78 setModal(true);
Chris@378 79 setWindowTitle(tr("Select Data Format"));
Chris@1612 80
Chris@1612 81 m_tabText = tr("<tab>");
Chris@1612 82 m_whitespaceText = tr("<whitespace>");
Chris@1413 83
Chris@378 84 QGridLayout *layout = new QGridLayout;
Chris@378 85
Chris@560 86 int row = 0;
Chris@378 87
Chris@1318 88 layout->addWidget
Chris@1318 89 (new QLabel(tr("Please select the correct data format for this file.")),
Chris@1318 90 row++, 0, 1, 4);
Chris@560 91
Chris@1413 92 m_exampleFrame = nullptr;
Chris@1413 93 m_exampleFrameRow = row++;
Chris@1612 94
Chris@1413 95 std::set<QChar> plausible = m_format.getPlausibleSeparators();
Chris@1413 96 SVDEBUG << "Have " << plausible.size() << " plausible separator(s)" << endl;
Chris@1413 97
Chris@1413 98 if (m_csvFilePath != "" && plausible.size() > 1) {
Chris@1413 99 // can only update when separator changed if we still have a
Chris@1413 100 // file to refer to
Chris@1413 101 layout->addWidget(new QLabel(tr("Column separator:")), row, 0);
Chris@1413 102 m_separatorCombo = new QComboBox;
Chris@1413 103 for (QChar c: plausible) {
Chris@1612 104 if (c == '\t') {
Chris@1612 105 m_separatorCombo->addItem(m_tabText);
Chris@1612 106 } else if (c == ' ') {
Chris@1612 107 m_separatorCombo->addItem(m_whitespaceText);
Chris@1612 108 } else {
Chris@1612 109 m_separatorCombo->addItem(QString(c));
Chris@1612 110 }
Chris@1413 111 if (c == m_format.getSeparator()) {
Chris@1413 112 m_separatorCombo->setCurrentIndex(m_separatorCombo->count()-1);
Chris@1413 113 }
Chris@1413 114 }
Chris@1413 115 m_separatorCombo->setEditable(false);
Chris@1413 116
Chris@1413 117 layout->addWidget(m_separatorCombo, row++, 1);
Chris@1413 118 connect(m_separatorCombo, SIGNAL(activated(QString)),
Chris@1413 119 this, SLOT(separatorChanged(QString)));
Chris@1612 120
Chris@1612 121 } else {
Chris@1612 122 m_separatorCombo = nullptr;
Chris@1413 123 }
Chris@1413 124
Chris@1612 125 layout->addWidget(new QLabel(tr("First row contains column headings:")), row, 0);
Chris@1612 126 m_headerCheckBox = new QCheckBox;
Chris@1612 127 m_headerCheckBox->setChecked
Chris@1612 128 (m_format.getHeaderStatus() == CSVFormat::HeaderPresent);
Chris@1612 129 layout->addWidget(m_headerCheckBox, row++, 1);
Chris@1612 130 connect(m_headerCheckBox, SIGNAL(toggled(bool)),
Chris@1612 131 this, SLOT(headerChanged(bool)));
Chris@1612 132
Chris@1413 133 layout->addWidget(new QLabel(tr("Timing is specified:")), row, 0);
Chris@1413 134
Chris@1413 135 m_timingTypeCombo = new QComboBox;
Chris@1413 136
Chris@1413 137 m_timingLabels = {
Chris@1413 138 { TimingExplicitSeconds, tr("Explicitly, in seconds") },
Chris@1413 139 { TimingExplicitMsec, tr("Explicitly, in milliseconds") },
Chris@1413 140 { TimingExplicitSamples, tr("Explicitly, in audio sample frames") },
Chris@1413 141 { TimingImplicit, tr("Implicitly: rows are equally spaced in time") }
Chris@1413 142 };
Chris@1413 143
Chris@1413 144 for (auto &l: m_timingLabels) {
Chris@1413 145 m_timingTypeCombo->addItem(l.second);
Chris@1413 146 }
Chris@1413 147
Chris@1413 148 layout->addWidget(m_timingTypeCombo, row++, 1, 1, 2);
Chris@1413 149
Chris@1413 150 connect(m_timingTypeCombo, SIGNAL(activated(int)),
Chris@1413 151 this, SLOT(timingTypeChanged(int)));
Chris@1413 152
Chris@1413 153 m_sampleRateLabel = new QLabel(tr("Audio sample rate (Hz):"));
Chris@1413 154 layout->addWidget(m_sampleRateLabel, row, 0);
Chris@1413 155
Chris@1413 156 m_sampleRateCombo = new QComboBox;
Chris@1413 157 for (int i = 0; i < int(sizeof(sampleRates) / sizeof(sampleRates[0])); ++i) {
Chris@1413 158 m_sampleRateCombo->addItem(QString("%1").arg(sampleRates[i]));
Chris@1413 159 }
Chris@1413 160 m_sampleRateCombo->setEditable(true);
Chris@1413 161
Chris@1413 162 layout->addWidget(m_sampleRateCombo, row++, 1);
Chris@1413 163 connect(m_sampleRateCombo, SIGNAL(activated(QString)),
Chris@1413 164 this, SLOT(sampleRateChanged(QString)));
Chris@1413 165 connect(m_sampleRateCombo, SIGNAL(editTextChanged(QString)),
Chris@1413 166 this, SLOT(sampleRateChanged(QString)));
Chris@1413 167
Chris@1413 168 m_windowSizeLabel = new QLabel(tr("Frame increment between rows:"));
Chris@1413 169 layout->addWidget(m_windowSizeLabel, row, 0);
Chris@1413 170
Chris@1413 171 m_windowSizeCombo = new QComboBox;
Chris@1413 172 for (int i = 0; i <= 16; ++i) {
Chris@1413 173 int value = 1 << i;
Chris@1413 174 m_windowSizeCombo->addItem(QString("%1").arg(value));
Chris@1413 175 }
Chris@1413 176 m_windowSizeCombo->setEditable(true);
Chris@1413 177
Chris@1413 178 layout->addWidget(m_windowSizeCombo, row++, 1);
Chris@1413 179 connect(m_windowSizeCombo, SIGNAL(activated(QString)),
Chris@1413 180 this, SLOT(windowSizeChanged(QString)));
Chris@1413 181 connect(m_windowSizeCombo, SIGNAL(editTextChanged(QString)),
Chris@1413 182 this, SLOT(windowSizeChanged(QString)));
Chris@1413 183
Chris@1413 184 m_modelLabel = new QLabel;
Chris@1413 185 QFont f(m_modelLabel->font());
Chris@1413 186 f.setItalic(true);
Chris@1413 187 m_modelLabel->setFont(f);
Chris@1413 188 layout->addWidget(m_modelLabel, row++, 0, 1, 4);
Chris@1413 189
Chris@1413 190 QDialogButtonBox *bb = new QDialogButtonBox(QDialogButtonBox::Ok |
Chris@1413 191 QDialogButtonBox::Cancel);
Chris@1413 192 layout->addWidget(bb, row++, 0, 1, 4);
Chris@1413 193 connect(bb, SIGNAL(accepted()), this, SLOT(accept()));
Chris@1413 194 connect(bb, SIGNAL(rejected()), this, SLOT(reject()));
Chris@1413 195
Chris@1413 196 setLayout(layout);
Chris@1413 197
Chris@1413 198 repopulate();
Chris@1413 199 }
Chris@1413 200
Chris@1413 201 void
Chris@1413 202 CSVFormatDialog::repopulate()
Chris@1413 203 {
Chris@1413 204 SVCERR << "CSVFormatDialog::repopulate()" << endl;
Chris@1413 205
Chris@1413 206 QGridLayout *layout = qobject_cast<QGridLayout *>(this->layout());
Chris@1413 207
Chris@560 208 QFrame *exampleFrame = new QFrame;
Chris@560 209 exampleFrame->setFrameStyle(QFrame::StyledPanel | QFrame::Sunken);
Chris@560 210 exampleFrame->setLineWidth(2);
Chris@560 211 QGridLayout *exampleLayout = new QGridLayout;
Chris@561 212 exampleLayout->setSpacing(4);
Chris@560 213 exampleFrame->setLayout(exampleLayout);
Chris@560 214
Chris@560 215 QPalette palette = exampleFrame->palette();
Chris@560 216 palette.setColor(QPalette::Window, palette.color(QPalette::Base));
Chris@560 217 exampleFrame->setPalette(palette);
Chris@560 218
Chris@560 219 QFont fp;
Chris@908 220 fp.setPointSize(int(floor(fp.pointSize() * 0.9)));
Chris@1612 221
Chris@1612 222 QFont fpi(fp);
Chris@1612 223 fpi.setItalic(true);
Chris@560 224
Chris@1413 225 int columns = m_format.getColumnCount();
Chris@560 226 QList<QStringList> example = m_format.getExample();
Chris@560 227
Chris@1413 228 m_columnPurposeCombos.clear();
Chris@1413 229
Chris@560 230 for (int i = 0; i < columns; ++i) {
Chris@581 231
Chris@560 232 QComboBox *cpc = new QComboBox;
Chris@560 233 m_columnPurposeCombos.push_back(cpc);
Chris@560 234 exampleLayout->addWidget(cpc, 0, i);
Chris@581 235 connect(cpc, SIGNAL(activated(int)), this, SLOT(columnPurposeChanged(int)));
Chris@1318 236
Chris@581 237 if (i == m_maxDisplayCols && columns > i + 2) {
Chris@581 238 m_fuzzyColumn = i;
Chris@1318 239
Chris@1319 240 cpc->addItem(tr("<ignore>"));
Chris@1319 241 cpc->addItem(tr("Values"));
Chris@1319 242 cpc->setCurrentIndex
Chris@1319 243 (m_format.getColumnPurpose(i-1) ==
Chris@1319 244 CSVFormat::ColumnUnknown ? 0 : 1);
Chris@1319 245
Chris@1318 246 exampleLayout->addWidget
Chris@1318 247 (new QLabel(tr("(%1 more)").arg(columns - i)), 1, i);
Chris@581 248 break;
Chris@581 249 }
Chris@560 250
Chris@1319 251 // NB must be in the same order as the CSVFormat::ColumnPurpose enum
Chris@1319 252 cpc->addItem(tr("<ignore>")); // ColumnUnknown
Chris@1319 253 cpc->addItem(tr("Time")); // ColumnStartTime
Chris@1319 254 cpc->addItem(tr("End time")); // ColumnEndTime
Chris@1319 255 cpc->addItem(tr("Duration")); // ColumnDuration
Chris@1319 256 cpc->addItem(tr("Value")); // ColumnValue
Chris@1319 257 cpc->addItem(tr("Pitch")); // ColumnPitch
Chris@1319 258 cpc->addItem(tr("Label")); // ColumnLabel
Chris@1319 259 cpc->setCurrentIndex(int(m_format.getColumnPurpose(i)));
Chris@1318 260
Chris@581 261 for (int j = 0; j < example.size() && j < 6; ++j) {
Chris@866 262 if (i >= example[j].size()) {
Chris@866 263 continue;
Chris@866 264 }
Chris@581 265 QLabel *label = new QLabel;
Chris@581 266 label->setTextFormat(Qt::PlainText);
Chris@674 267 QString text = TextAbbrev::abbreviate(example[j][i], 35);
Chris@674 268 label->setText(text);
Chris@1612 269 if (j == 0 &&
Chris@1612 270 m_format.getHeaderStatus() == CSVFormat::HeaderPresent) {
Chris@1612 271 label->setFont(fpi);
Chris@1612 272 } else {
Chris@1612 273 label->setFont(fp);
Chris@1612 274 }
Chris@581 275 label->setPalette(palette);
Chris@581 276 label->setIndent(8);
Chris@581 277 exampleLayout->addWidget(label, j+1, i);
Chris@560 278 }
Chris@560 279 }
Chris@560 280
Chris@1413 281 if (m_exampleFrame) {
Chris@1413 282 delete m_exampleFrame;
Chris@1413 283 }
Chris@1413 284 m_exampleFrame = exampleFrame;
Chris@1413 285
Chris@1413 286 layout->addWidget(exampleFrame, m_exampleFrameRow, 0, 1, 4);
Chris@560 287 layout->setColumnStretch(3, 10);
Chris@1413 288 layout->setRowStretch(m_exampleFrameRow, 10);
Chris@1319 289
Chris@1319 290 m_initialTimingOption = TimingImplicit;
Chris@1319 291 if (m_format.getTimingType() == CSVFormat::ExplicitTiming) {
Chris@1319 292 switch (m_format.getTimeUnits()) {
Chris@1319 293 case CSVFormat::TimeSeconds:
Chris@1319 294 m_initialTimingOption = TimingExplicitSeconds; break;
Chris@1319 295 case CSVFormat::TimeMilliseconds:
Chris@1319 296 m_initialTimingOption = TimingExplicitMsec; break;
Chris@1319 297 case CSVFormat::TimeAudioFrames:
Chris@1319 298 m_initialTimingOption = TimingExplicitSamples; break;
Chris@1319 299 case CSVFormat::TimeWindows:
Chris@1319 300 m_initialTimingOption = TimingImplicit; break;
Chris@1318 301 }
Chris@959 302 }
Chris@1319 303 m_timingTypeCombo->setCurrentIndex(int(m_initialTimingOption));
Chris@378 304
Chris@807 305 for (int i = 0; i < int(sizeof(sampleRates) / sizeof(sampleRates[0])); ++i) {
Chris@1266 306 if (sampleRates[i] == m_format.getSampleRate()) {
Chris@560 307 m_sampleRateCombo->setCurrentIndex(i);
Chris@560 308 }
Chris@378 309 }
Chris@1318 310
Chris@1319 311 for (int i = 0; i <= 16; ++i) {
Chris@1319 312 int value = 1 << i;
Chris@1319 313 if (value == int(m_format.getWindowSize())) {
Chris@1319 314 m_windowSizeCombo->setCurrentIndex(i);
Chris@1319 315 }
Chris@1319 316 }
Chris@378 317
Chris@1319 318 timingTypeChanged(m_timingTypeCombo->currentIndex());
Chris@378 319 }
Chris@378 320
Chris@378 321 CSVFormat
Chris@378 322 CSVFormatDialog::getFormat() const
Chris@378 323 {
Chris@560 324 return m_format;
Chris@378 325 }
Chris@378 326
Chris@378 327 void
Chris@561 328 CSVFormatDialog::updateModelLabel()
Chris@378 329 {
Chris@1318 330 if (!m_modelLabel) {
Chris@1318 331 return;
Chris@1318 332 }
Chris@1318 333
Chris@561 334 LayerFactory *f = LayerFactory::getInstance();
Chris@561 335
Chris@561 336 QString s;
Chris@561 337 switch (m_format.getModelType()) {
Chris@561 338 case CSVFormat::OneDimensionalModel:
Chris@561 339 s = f->getLayerPresentationName(LayerFactory::TimeInstants);
Chris@561 340 break;
Chris@561 341 case CSVFormat::TwoDimensionalModel:
Chris@561 342 s = f->getLayerPresentationName(LayerFactory::TimeValues);
Chris@561 343 break;
Chris@561 344 case CSVFormat::TwoDimensionalModelWithDuration:
Chris@561 345 s = f->getLayerPresentationName(LayerFactory::Regions);
Chris@561 346 break;
Chris@763 347 case CSVFormat::TwoDimensionalModelWithDurationAndPitch:
Chris@763 348 s = f->getLayerPresentationName(LayerFactory::Notes);
Chris@763 349 break;
Chris@1521 350 case CSVFormat::TwoDimensionalModelWithDurationAndExtent:
Chris@1521 351 s = f->getLayerPresentationName(LayerFactory::Boxes);
Chris@1521 352 break;
Chris@561 353 case CSVFormat::ThreeDimensionalModel:
Chris@561 354 s = f->getLayerPresentationName(LayerFactory::Colour3DPlot);
Chris@561 355 break;
Chris@1312 356 case CSVFormat::WaveFileModel:
Chris@1312 357 s = f->getLayerPresentationName(LayerFactory::Waveform);
Chris@1312 358 break;
Chris@561 359 }
Chris@561 360
Chris@1318 361 m_modelLabel->setText("\n" + tr("Data will be displayed in a %1 layer.")
Chris@1318 362 .arg(s));
Chris@378 363 }
Chris@378 364
Chris@378 365 void
Chris@959 366 CSVFormatDialog::applyStartTimePurpose()
Chris@959 367 {
Chris@971 368 // First check if we already have any. NB there may be fewer than
Chris@971 369 // m_format.getColumnCount() elements in m_columnPurposeCombos
Chris@992 370 // (because of the fuzzy column behaviour). Note also that the
Chris@992 371 // fuzzy column (which is the one just showing how many more
Chris@992 372 // columns there are) has a different combo with only two items
Chris@992 373 // (ignore or Values)
Chris@971 374 for (int i = 0; i < m_columnPurposeCombos.size(); ++i) {
Chris@992 375 if (i == m_fuzzyColumn) continue;
Chris@959 376 QComboBox *cb = m_columnPurposeCombos[i];
Chris@959 377 if (cb->currentIndex() == int(CSVFormat::ColumnStartTime)) {
Chris@959 378 return;
Chris@959 379 }
Chris@959 380 }
Chris@959 381 // and if not, select one
Chris@971 382 for (int i = 0; i < m_columnPurposeCombos.size(); ++i) {
Chris@992 383 if (i == m_fuzzyColumn) continue;
Chris@959 384 QComboBox *cb = m_columnPurposeCombos[i];
Chris@959 385 if (cb->currentIndex() == int(CSVFormat::ColumnValue)) {
Chris@959 386 cb->setCurrentIndex(int(CSVFormat::ColumnStartTime));
Chris@959 387 return;
Chris@959 388 }
Chris@959 389 }
Chris@959 390 }
Chris@959 391
Chris@959 392 void
Chris@959 393 CSVFormatDialog::removeStartTimePurpose()
Chris@959 394 {
Chris@971 395 // NB there may be fewer than m_format.getColumnCount() elements
Chris@971 396 // in m_columnPurposeCombos (because of the fuzzy column
Chris@971 397 // behaviour)
Chris@971 398 for (int i = 0; i < m_columnPurposeCombos.size(); ++i) {
Chris@992 399 if (i == m_fuzzyColumn) continue;
Chris@959 400 QComboBox *cb = m_columnPurposeCombos[i];
Chris@959 401 if (cb->currentIndex() == int(CSVFormat::ColumnStartTime)) {
Chris@959 402 cb->setCurrentIndex(int(CSVFormat::ColumnValue));
Chris@959 403 }
Chris@959 404 }
Chris@959 405 }
Chris@959 406
Chris@959 407 void
Chris@959 408 CSVFormatDialog::updateComboVisibility()
Chris@959 409 {
Chris@1319 410 bool wantRate = (m_format.getTimingType() == CSVFormat::ImplicitTiming ||
Chris@959 411 m_format.getTimeUnits() == CSVFormat::TimeAudioFrames);
Chris@959 412 bool wantWindow = (m_format.getTimingType() == CSVFormat::ImplicitTiming);
Chris@959 413
Chris@959 414 m_sampleRateCombo->setEnabled(wantRate);
Chris@959 415 m_sampleRateLabel->setEnabled(wantRate);
Chris@959 416
Chris@1319 417 m_windowSizeCombo->setEnabled(wantWindow);
Chris@1319 418 m_windowSizeLabel->setEnabled(wantWindow);
Chris@959 419 }
Chris@959 420
Chris@959 421 void
Chris@1612 422 CSVFormatDialog::headerChanged(bool header)
Chris@1612 423 {
Chris@1612 424 m_format.setHeaderStatus(header ?
Chris@1612 425 CSVFormat::HeaderPresent :
Chris@1612 426 CSVFormat::HeaderAbsent);
Chris@1612 427 m_format.guessFormatFor(m_csvFilePath);
Chris@1612 428
Chris@1612 429 repopulate();
Chris@1612 430 }
Chris@1612 431
Chris@1612 432 void
Chris@1413 433 CSVFormatDialog::separatorChanged(QString sep)
Chris@1413 434 {
Chris@1413 435 if (sep == "" || m_csvFilePath == "") {
Chris@1413 436 return;
Chris@1413 437 }
Chris@1413 438
Chris@1612 439 if (sep == m_tabText) {
Chris@1612 440 sep = "\t";
Chris@1612 441 } else if (sep == m_whitespaceText) {
Chris@1612 442 sep = " ";
Chris@1612 443 }
Chris@1612 444
Chris@1413 445 m_format.setSeparator(sep[0]);
Chris@1413 446 m_format.guessFormatFor(m_csvFilePath);
Chris@1413 447
Chris@1413 448 repopulate();
Chris@1413 449 }
Chris@1413 450
Chris@1413 451 void
Chris@378 452 CSVFormatDialog::timingTypeChanged(int type)
Chris@378 453 {
Chris@959 454 // Update any column purpose combos
Chris@959 455 if (TimingOption(type) == TimingImplicit) {
Chris@959 456 removeStartTimePurpose();
Chris@959 457 } else {
Chris@959 458 applyStartTimePurpose();
Chris@378 459 }
Chris@959 460 updateFormatFromDialog();
Chris@959 461 updateComboVisibility();
Chris@559 462 }
Chris@559 463
Chris@559 464 void
Chris@378 465 CSVFormatDialog::sampleRateChanged(QString rateString)
Chris@378 466 {
Chris@378 467 bool ok = false;
Chris@378 468 int sampleRate = rateString.toInt(&ok);
Chris@560 469 if (ok) m_format.setSampleRate(sampleRate);
Chris@378 470 }
Chris@378 471
Chris@378 472 void
Chris@378 473 CSVFormatDialog::windowSizeChanged(QString sizeString)
Chris@378 474 {
Chris@378 475 bool ok = false;
Chris@378 476 int size = sizeString.toInt(&ok);
Chris@560 477 if (ok) m_format.setWindowSize(size);
Chris@378 478 }
Chris@560 479
Chris@560 480 void
Chris@561 481 CSVFormatDialog::columnPurposeChanged(int p)
Chris@560 482 {
Chris@560 483 QObject *o = sender();
Chris@560 484 QComboBox *cb = qobject_cast<QComboBox *>(o);
Chris@560 485 if (!cb) return;
Chris@561 486
Chris@1319 487 // Ensure a consistent set of column purposes, in case of a
Chris@1319 488 // situation where some combinations are contradictory. Only
Chris@1319 489 // updates the UI, does not update the stored format record from
Chris@1319 490 // the UI - that's the job of updateFormatFromDialog
Chris@1318 491
Chris@561 492 CSVFormat::ColumnPurpose purpose = (CSVFormat::ColumnPurpose)p;
Chris@561 493
Chris@959 494 bool haveStartTime = false; // so as to update timing type combo appropriately
Chris@959 495
Chris@959 496 // Ensure the column purpose combos are consistent with one
Chris@959 497 // another, without reference to m_format (which we'll update
Chris@959 498 // separately)
Chris@959 499
Chris@560 500 for (int i = 0; i < m_columnPurposeCombos.size(); ++i) {
Chris@561 501
Chris@992 502 // The fuzzy column combo only has the entries <ignore> or
Chris@992 503 // Values, so it can't affect the timing type and none of this
Chris@992 504 // logic affects it
Chris@992 505 if (i == m_fuzzyColumn) continue;
Chris@992 506
Chris@959 507 QComboBox *thisCombo = m_columnPurposeCombos[i];
Chris@561 508
Chris@959 509 CSVFormat::ColumnPurpose cp = (CSVFormat::ColumnPurpose)
Chris@959 510 (thisCombo->currentIndex());
Chris@959 511 bool thisChanged = (cb == thisCombo);
Chris@959 512
Chris@959 513 if (!thisChanged) {
Chris@561 514
Chris@561 515 // We can only have one ColumnStartTime column, and only
Chris@561 516 // one of either ColumnDuration or ColumnEndTime
Chris@561 517
Chris@561 518 if (purpose == CSVFormat::ColumnStartTime) {
Chris@561 519 if (cp == purpose) {
Chris@561 520 cp = CSVFormat::ColumnValue;
Chris@561 521 }
Chris@561 522 } else if (purpose == CSVFormat::ColumnDuration ||
Chris@561 523 purpose == CSVFormat::ColumnEndTime) {
Chris@561 524 if (cp == CSVFormat::ColumnDuration ||
Chris@561 525 cp == CSVFormat::ColumnEndTime) {
Chris@561 526 cp = CSVFormat::ColumnValue;
Chris@561 527 }
Chris@561 528 }
Chris@561 529
Chris@561 530 // And we can only have one label
Chris@561 531 if (purpose == CSVFormat::ColumnLabel) {
Chris@561 532 if (cp == purpose) {
Chris@561 533 cp = CSVFormat::ColumnUnknown;
Chris@561 534 }
Chris@561 535 }
Chris@959 536
Chris@959 537 if (cp == CSVFormat::ColumnStartTime) {
Chris@959 538 haveStartTime = true;
Chris@959 539 }
Chris@959 540
Chris@959 541 thisCombo->setCurrentIndex(int(cp));
Chris@959 542
Chris@959 543 } else {
Chris@959 544 if (purpose == CSVFormat::ColumnStartTime) {
Chris@959 545 haveStartTime = true;
Chris@959 546 }
Chris@561 547 }
Chris@959 548 }
Chris@561 549
Chris@1319 550 if (!haveStartTime) {
Chris@1319 551 m_timingTypeCombo->setCurrentIndex(int(TimingImplicit));
Chris@1319 552 } else if (m_timingTypeCombo->currentIndex() == int(TimingImplicit)) {
Chris@1319 553 if (m_initialTimingOption == TimingImplicit) {
Chris@1319 554 m_timingTypeCombo->setCurrentIndex(TimingExplicitSeconds);
Chris@1319 555 } else {
Chris@1319 556 m_timingTypeCombo->setCurrentIndex(m_initialTimingOption);
Chris@959 557 }
Chris@959 558 }
Chris@1319 559
Chris@1319 560 updateFormatFromDialog();
Chris@1319 561 updateComboVisibility();
Chris@959 562 }
Chris@1318 563
Chris@959 564 void
Chris@959 565 CSVFormatDialog::updateFormatFromDialog()
Chris@959 566 {
Chris@1319 567 switch (TimingOption(m_timingTypeCombo->currentIndex())) {
Chris@959 568
Chris@1319 569 case TimingExplicitSeconds:
Chris@1319 570 m_format.setTimingType(CSVFormat::ExplicitTiming);
Chris@1319 571 m_format.setTimeUnits(CSVFormat::TimeSeconds);
Chris@1319 572 break;
Chris@1319 573
Chris@1319 574 case TimingExplicitMsec:
Chris@1319 575 m_format.setTimingType(CSVFormat::ExplicitTiming);
Chris@1319 576 m_format.setTimeUnits(CSVFormat::TimeMilliseconds);
Chris@1319 577 break;
Chris@1319 578
Chris@1319 579 case TimingExplicitSamples:
Chris@1319 580 m_format.setTimingType(CSVFormat::ExplicitTiming);
Chris@1319 581 m_format.setTimeUnits(CSVFormat::TimeAudioFrames);
Chris@1319 582 break;
Chris@1319 583
Chris@1319 584 case TimingImplicit:
Chris@1318 585 m_format.setTimingType(CSVFormat::ImplicitTiming);
Chris@1319 586 m_format.setTimeUnits(CSVFormat::TimeWindows);
Chris@1319 587 break;
Chris@959 588 }
Chris@1318 589
Chris@959 590 bool haveStartTime = false;
Chris@959 591 bool haveDuration = false;
Chris@959 592 bool havePitch = false;
Chris@959 593 int valueCount = 0;
Chris@959 594
Chris@959 595 for (int i = 0; i < m_columnPurposeCombos.size(); ++i) {
Chris@959 596
Chris@959 597 QComboBox *thisCombo = m_columnPurposeCombos[i];
Chris@959 598
Chris@1319 599 CSVFormat::ColumnPurpose purpose =
Chris@1319 600 (CSVFormat::ColumnPurpose) (thisCombo->currentIndex());
Chris@1319 601
Chris@959 602 if (i == m_fuzzyColumn) {
Chris@992 603 for (int j = i; j < m_format.getColumnCount(); ++j) {
Chris@959 604 if (purpose == CSVFormat::ColumnUnknown) {
Chris@959 605 m_format.setColumnPurpose(j, CSVFormat::ColumnUnknown);
Chris@959 606 } else { // Value
Chris@959 607 m_format.setColumnPurpose(j, CSVFormat::ColumnValue);
Chris@959 608 ++valueCount;
Chris@959 609 }
Chris@959 610 }
Chris@992 611 } else {
Chris@992 612
Chris@992 613 if (purpose == CSVFormat::ColumnStartTime) {
Chris@992 614 haveStartTime = true;
Chris@992 615 }
Chris@992 616 if (purpose == CSVFormat::ColumnEndTime ||
Chris@992 617 purpose == CSVFormat::ColumnDuration) {
Chris@992 618 haveDuration = true;
Chris@992 619 }
Chris@992 620 if (purpose == CSVFormat::ColumnPitch) {
Chris@992 621 havePitch = true;
Chris@992 622 }
Chris@992 623 if (purpose == CSVFormat::ColumnValue) {
Chris@992 624 ++valueCount;
Chris@992 625 }
Chris@992 626
Chris@992 627 m_format.setColumnPurpose(i, purpose);
Chris@959 628 }
Chris@561 629 }
Chris@561 630
Chris@1319 631 if (haveStartTime && haveDuration) {
Chris@763 632 if (havePitch) {
Chris@763 633 m_format.setModelType(CSVFormat::TwoDimensionalModelWithDurationAndPitch);
Chris@1521 634 } else if (valueCount == 2) {
Chris@1521 635 m_format.setModelType(CSVFormat::TwoDimensionalModelWithDurationAndExtent);
Chris@763 636 } else {
Chris@763 637 m_format.setModelType(CSVFormat::TwoDimensionalModelWithDuration);
Chris@763 638 }
Chris@561 639 } else {
Chris@561 640 if (valueCount > 1) {
Chris@561 641 m_format.setModelType(CSVFormat::ThreeDimensionalModel);
Chris@561 642 } else if (valueCount > 0) {
Chris@561 643 m_format.setModelType(CSVFormat::TwoDimensionalModel);
Chris@561 644 } else {
Chris@561 645 m_format.setModelType(CSVFormat::OneDimensionalModel);
Chris@560 646 }
Chris@560 647 }
Chris@561 648
Chris@561 649 updateModelLabel();
Chris@560 650 }
Chris@560 651
Chris@560 652
Chris@959 653