annotate main/MainWindow.cpp @ 86:6113fdda2737

* Add Open Location... * Add support for URLs in Recent Files * Do the right thing with the last location for file open dialogs when opening a URL (i.e. don't change it to the temporary file directory)
author Chris Cannam
date Wed, 10 Jan 2007 12:27:55 +0000
parents 4eae5b521a34
children 8944f3005a15
rev   line source
Chris@0 1 /* -*- c-basic-offset: 4 indent-tabs-mode: nil -*- vi:set ts=8 sts=4 sw=4: */
Chris@0 2
Chris@0 3 /*
Chris@0 4 Sonic Visualiser
Chris@0 5 An audio file viewer and annotation editor.
Chris@0 6 Centre for Digital Music, Queen Mary, University of London.
Chris@77 7 This file copyright 2006 Chris Cannam and QMUL.
Chris@0 8
Chris@0 9 This program is free software; you can redistribute it and/or
Chris@0 10 modify it under the terms of the GNU General Public License as
Chris@0 11 published by the Free Software Foundation; either version 2 of the
Chris@0 12 License, or (at your option) any later version. See the file
Chris@0 13 COPYING included with this distribution for more information.
Chris@0 14 */
Chris@0 15
Chris@1 16 #include "version.h"
Chris@0 17
Chris@0 18 #include "MainWindow.h"
Chris@1 19 #include "document/Document.h"
Chris@0 20 #include "PreferencesDialog.h"
Chris@0 21
Chris@1 22 #include "view/Pane.h"
Chris@1 23 #include "view/PaneStack.h"
Chris@1 24 #include "data/model/WaveFileModel.h"
Chris@1 25 #include "data/model/SparseOneDimensionalModel.h"
Chris@1 26 #include "view/ViewManager.h"
Chris@0 27 #include "base/Preferences.h"
Chris@0 28 #include "layer/WaveformLayer.h"
Chris@0 29 #include "layer/TimeRulerLayer.h"
Chris@0 30 #include "layer/TimeInstantLayer.h"
Chris@0 31 #include "layer/TimeValueLayer.h"
Chris@0 32 #include "layer/Colour3DPlotLayer.h"
Chris@0 33 #include "widgets/Fader.h"
Chris@65 34 #include "view/Overview.h"
Chris@0 35 #include "widgets/PropertyBox.h"
Chris@0 36 #include "widgets/PropertyStack.h"
Chris@0 37 #include "widgets/AudioDial.h"
Chris@0 38 #include "widgets/LayerTree.h"
Chris@0 39 #include "widgets/ListInputDialog.h"
Chris@36 40 #include "widgets/SubdividingMenu.h"
Chris@0 41 #include "audioio/AudioCallbackPlaySource.h"
Chris@0 42 #include "audioio/AudioCallbackPlayTarget.h"
Chris@0 43 #include "audioio/AudioTargetFactory.h"
Chris@59 44 #include "audioio/PlaySpeedRangeMapper.h"
Chris@1 45 #include "data/fileio/AudioFileReaderFactory.h"
Chris@1 46 #include "data/fileio/DataFileReaderFactory.h"
Chris@1 47 #include "data/fileio/WavFileWriter.h"
Chris@1 48 #include "data/fileio/CSVFileWriter.h"
Chris@1 49 #include "data/fileio/BZipFileDevice.h"
Chris@85 50 #include "data/fileio/RemoteFile.h"
Chris@1 51 #include "base/RecentFiles.h"
Chris@0 52 #include "transform/TransformFactory.h"
Chris@0 53 #include "base/PlayParameterRepository.h"
Chris@0 54 #include "base/XmlExportable.h"
Chris@0 55 #include "base/CommandHistory.h"
Chris@0 56 #include "base/Profiler.h"
Chris@0 57 #include "base/Clipboard.h"
Chris@69 58 #include "osc/OSCQueue.h"
Chris@0 59
Chris@0 60 // For version information
Chris@0 61 #include "vamp/vamp.h"
Chris@0 62 #include "vamp-sdk/PluginBase.h"
Chris@0 63 #include "plugin/api/ladspa.h"
Chris@0 64 #include "plugin/api/dssi.h"
Chris@0 65
Chris@0 66 #include <QApplication>
Chris@0 67 #include <QPushButton>
Chris@0 68 #include <QFileDialog>
Chris@0 69 #include <QMessageBox>
Chris@0 70 #include <QGridLayout>
Chris@0 71 #include <QLabel>
Chris@0 72 #include <QAction>
Chris@0 73 #include <QMenuBar>
Chris@0 74 #include <QToolBar>
Chris@0 75 #include <QInputDialog>
Chris@0 76 #include <QStatusBar>
Chris@0 77 #include <QTreeView>
Chris@0 78 #include <QFile>
Chris@0 79 #include <QTextStream>
Chris@0 80 #include <QProcess>
Chris@7 81 #include <QShortcut>
Chris@5 82 #include <QSettings>
Chris@11 83 #include <QDateTime>
Chris@11 84 #include <QProcess>
Chris@16 85 #include <QCheckBox>
Chris@55 86 #include <QRegExp>
Chris@0 87
Chris@0 88 #include <iostream>
Chris@0 89 #include <cstdio>
Chris@0 90 #include <errno.h>
Chris@0 91
Chris@0 92 using std::cerr;
Chris@0 93 using std::endl;
Chris@0 94
Chris@33 95 using std::vector;
Chris@33 96 using std::map;
Chris@33 97 using std::set;
Chris@33 98
Chris@0 99
Chris@70 100 MainWindow::MainWindow(bool withAudioOutput, bool withOSCSupport) :
Chris@0 101 m_document(0),
Chris@0 102 m_paneStack(0),
Chris@0 103 m_viewManager(0),
Chris@65 104 m_overview(0),
Chris@0 105 m_timeRulerLayer(0),
Chris@46 106 m_audioOutput(withAudioOutput),
Chris@0 107 m_playSource(0),
Chris@0 108 m_playTarget(0),
Chris@70 109 m_oscQueue(withOSCSupport ? new OSCQueue() : 0),
Chris@81 110 m_recentFiles("RecentFiles", 20),
Chris@49 111 m_recentTransforms("RecentTransforms", 20),
Chris@0 112 m_mainMenusCreated(false),
Chris@0 113 m_paneMenu(0),
Chris@0 114 m_layerMenu(0),
Chris@34 115 m_transformsMenu(0),
Chris@0 116 m_existingLayersMenu(0),
Chris@34 117 m_recentFilesMenu(0),
Chris@34 118 m_recentTransformsMenu(0),
Chris@0 119 m_rightButtonMenu(0),
Chris@0 120 m_rightButtonLayerMenu(0),
Chris@34 121 m_rightButtonTransformsMenu(0),
Chris@0 122 m_documentModified(false),
Chris@70 123 m_openingAudioFile(false),
Chris@70 124 m_abandoning(false),
Chris@0 125 m_preferencesDialog(0)
Chris@0 126 {
Chris@0 127 setWindowTitle(tr("Sonic Visualiser"));
Chris@0 128
Chris@0 129 UnitDatabase::getInstance()->registerUnit("Hz");
Chris@0 130 UnitDatabase::getInstance()->registerUnit("dB");
Chris@0 131
Chris@0 132 connect(CommandHistory::getInstance(), SIGNAL(commandExecuted()),
Chris@0 133 this, SLOT(documentModified()));
Chris@0 134 connect(CommandHistory::getInstance(), SIGNAL(documentRestored()),
Chris@0 135 this, SLOT(documentRestored()));
Chris@0 136
Chris@0 137 QFrame *frame = new QFrame;
Chris@0 138 setCentralWidget(frame);
Chris@0 139
Chris@0 140 QGridLayout *layout = new QGridLayout;
Chris@0 141
Chris@0 142 m_viewManager = new ViewManager();
Chris@0 143 connect(m_viewManager, SIGNAL(selectionChanged()),
Chris@0 144 this, SLOT(updateMenuStates()));
Chris@0 145
Chris@0 146 m_descriptionLabel = new QLabel;
Chris@0 147
Chris@0 148 m_paneStack = new PaneStack(frame, m_viewManager);
Chris@0 149 connect(m_paneStack, SIGNAL(currentPaneChanged(Pane *)),
Chris@0 150 this, SLOT(currentPaneChanged(Pane *)));
Chris@0 151 connect(m_paneStack, SIGNAL(currentLayerChanged(Pane *, Layer *)),
Chris@0 152 this, SLOT(currentLayerChanged(Pane *, Layer *)));
Chris@0 153 connect(m_paneStack, SIGNAL(rightButtonMenuRequested(Pane *, QPoint)),
Chris@0 154 this, SLOT(rightButtonMenuRequested(Pane *, QPoint)));
Chris@73 155 connect(m_paneStack, SIGNAL(propertyStacksResized()),
Chris@73 156 this, SLOT(propertyStacksResized()));
Chris@0 157
Chris@65 158 m_overview = new Overview(frame);
Chris@65 159 m_overview->setViewManager(m_viewManager);
Chris@65 160 m_overview->setFixedHeight(40);
Chris@0 161
Chris@0 162 m_panLayer = new WaveformLayer;
Chris@0 163 m_panLayer->setChannelMode(WaveformLayer::MergeChannels);
Chris@0 164 // m_panLayer->setScale(WaveformLayer::MeterScale);
Chris@39 165 // m_panLayer->setAutoNormalize(true);
Chris@0 166 m_panLayer->setBaseColour(Qt::darkGreen);
Chris@0 167 m_panLayer->setAggressiveCacheing(true);
Chris@65 168 m_overview->addLayer(m_panLayer);
Chris@0 169
Chris@0 170 m_playSource = new AudioCallbackPlaySource(m_viewManager);
Chris@0 171
Chris@0 172 connect(m_playSource, SIGNAL(sampleRateMismatch(size_t, size_t, bool)),
Chris@0 173 this, SLOT(sampleRateMismatch(size_t, size_t, bool)));
Chris@42 174 connect(m_playSource, SIGNAL(audioOverloadPluginDisabled()),
Chris@42 175 this, SLOT(audioOverloadPluginDisabled()));
Chris@0 176
Chris@0 177 m_fader = new Fader(frame, false);
Chris@0 178
Chris@0 179 m_playSpeed = new AudioDial(frame);
Chris@12 180 m_playSpeed->setMinimum(0);
Chris@48 181 m_playSpeed->setMaximum(200);
Chris@25 182 m_playSpeed->setValue(100);
Chris@0 183 m_playSpeed->setFixedWidth(24);
Chris@0 184 m_playSpeed->setFixedHeight(24);
Chris@0 185 m_playSpeed->setNotchesVisible(true);
Chris@25 186 m_playSpeed->setPageStep(10);
Chris@60 187 m_playSpeed->setObjectName(tr("Playback Speedup"));
Chris@25 188 m_playSpeed->setDefaultValue(100);
Chris@59 189 m_playSpeed->setRangeMapper(new PlaySpeedRangeMapper(0, 200));
Chris@60 190 m_playSpeed->setShowToolTip(true);
Chris@0 191 connect(m_playSpeed, SIGNAL(valueChanged(int)),
Chris@0 192 this, SLOT(playSpeedChanged(int)));
Chris@0 193
Chris@26 194 m_playSharpen = new QPushButton(frame);
Chris@17 195 m_playSharpen->setToolTip(tr("Sharpen percussive transients"));
Chris@26 196 m_playSharpen->setFixedSize(20, 20);
Chris@26 197 // m_playSharpen->setFlat(true);
Chris@16 198 m_playSharpen->setEnabled(false);
Chris@26 199 m_playSharpen->setCheckable(true);
Chris@26 200 m_playSharpen->setChecked(false);
Chris@26 201 m_playSharpen->setIcon(QIcon(":icons/sharpen.png"));
Chris@26 202 connect(m_playSharpen, SIGNAL(clicked()), this, SLOT(playSharpenToggled()));
Chris@26 203
Chris@26 204 m_playMono = new QPushButton(frame);
Chris@26 205 m_playMono->setToolTip(tr("Run time stretcher in mono only"));
Chris@26 206 m_playMono->setFixedSize(20, 20);
Chris@26 207 // m_playMono->setFlat(true);
Chris@26 208 m_playMono->setEnabled(false);
Chris@26 209 m_playMono->setCheckable(true);
Chris@26 210 m_playMono->setChecked(false);
Chris@26 211 m_playMono->setIcon(QIcon(":icons/mono.png"));
Chris@26 212 connect(m_playMono, SIGNAL(clicked()), this, SLOT(playMonoToggled()));
Chris@21 213
Chris@21 214 QSettings settings;
Chris@21 215 settings.beginGroup("MainWindow");
Chris@24 216 m_playSharpen->setChecked(settings.value("playsharpen", true).toBool());
Chris@26 217 m_playMono->setChecked(settings.value("playmono", false).toBool());
Chris@21 218 settings.endGroup();
Chris@21 219
Chris@26 220 layout->addWidget(m_paneStack, 0, 0, 1, 5);
Chris@65 221 layout->addWidget(m_overview, 1, 0);
Chris@0 222 layout->addWidget(m_fader, 1, 1);
Chris@0 223 layout->addWidget(m_playSpeed, 1, 2);
Chris@16 224 layout->addWidget(m_playSharpen, 1, 3);
Chris@26 225 layout->addWidget(m_playMono, 1, 4);
Chris@16 226
Chris@16 227 layout->setColumnStretch(0, 10);
Chris@16 228
Chris@0 229 frame->setLayout(layout);
Chris@0 230
Chris@0 231 connect(m_viewManager, SIGNAL(outputLevelsChanged(float, float)),
Chris@0 232 this, SLOT(outputLevelsChanged(float, float)));
Chris@0 233
Chris@0 234 connect(Preferences::getInstance(),
Chris@0 235 SIGNAL(propertyChanged(PropertyContainer::PropertyName)),
Chris@0 236 this,
Chris@0 237 SLOT(preferenceChanged(PropertyContainer::PropertyName)));
Chris@0 238
Chris@70 239 if (m_oscQueue && m_oscQueue->isOK()) {
Chris@69 240 connect(m_oscQueue, SIGNAL(messagesAvailable()), this, SLOT(pollOSC()));
Chris@69 241 QTimer *oscTimer = new QTimer(this);
Chris@69 242 connect(oscTimer, SIGNAL(timeout()), this, SLOT(pollOSC()));
Chris@69 243 oscTimer->start(1000);
Chris@69 244 }
Chris@69 245
Chris@0 246 setupMenus();
Chris@0 247 setupToolbars();
Chris@0 248
Chris@47 249 // statusBar()->addWidget(m_descriptionLabel);
Chris@0 250
Chris@0 251 newSession();
Chris@0 252 }
Chris@0 253
Chris@0 254 MainWindow::~MainWindow()
Chris@0 255 {
Chris@70 256 if (!m_abandoning) {
Chris@70 257 closeSession();
Chris@70 258 }
Chris@0 259 delete m_playTarget;
Chris@0 260 delete m_playSource;
Chris@0 261 delete m_viewManager;
Chris@69 262 delete m_oscQueue;
Chris@0 263 Profiles::getInstance()->dump();
Chris@0 264 }
Chris@0 265
Chris@81 266 QString
Chris@81 267 MainWindow::getOpenFileName(FileType type)
Chris@81 268 {
Chris@81 269 QString settingsKey;
Chris@81 270 QString lastPath;
Chris@81 271
Chris@81 272 QString title = tr("Select file");
Chris@81 273 QString filter = tr("All files (*.*)");
Chris@81 274
Chris@81 275 bool canImportLayer = (getMainModel() != 0 &&
Chris@81 276 m_paneStack != 0 &&
Chris@81 277 m_paneStack->getCurrentPane() != 0);
Chris@81 278
Chris@81 279 switch (type) {
Chris@81 280
Chris@81 281 case SessionFile:
Chris@81 282 settingsKey = "sessionpath";
Chris@81 283 lastPath = m_sessionFile;
Chris@81 284 title = tr("Select a session file");
Chris@81 285 filter = tr("Sonic Visualiser session files (*.sv)\nAll files (*.*)");
Chris@81 286 break;
Chris@81 287
Chris@81 288 case AudioFile:
Chris@81 289 settingsKey = "audiopath";
Chris@81 290 lastPath = m_audioFile;
Chris@81 291 title = "Select an audio file";
Chris@81 292 filter = tr("Audio files (%1)\nAll files (*.*)")
Chris@81 293 .arg(AudioFileReaderFactory::getKnownExtensions());
Chris@81 294 break;
Chris@81 295
Chris@81 296 case LayerFile:
Chris@81 297 settingsKey = "layerpath";
Chris@81 298 lastPath = m_sessionFile;
Chris@81 299 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());
Chris@81 300 break;
Chris@81 301
Chris@81 302 case AnyFile:
Chris@81 303 settingsKey = "lastpath";
Chris@81 304 lastPath = m_sessionFile;
Chris@81 305 if (canImportLayer) {
Chris@81 306 filter = tr("All supported files (*.sv %1 %2)\nSonic Visualiser session files (*.sv)\nAudio files (%1)\nLayer files (%2)\nAll files (*.*)")
Chris@81 307 .arg(AudioFileReaderFactory::getKnownExtensions())
Chris@81 308 .arg(DataFileReaderFactory::getKnownExtensions());
Chris@81 309 } else {
Chris@81 310 filter = tr("All supported files (*.sv %1)\nSonic Visualiser session files (*.sv)\nAudio files (%1)\nAll files (*.*)")
Chris@81 311 .arg(AudioFileReaderFactory::getKnownExtensions());
Chris@81 312 }
Chris@81 313 break;
Chris@81 314 };
Chris@81 315
Chris@81 316 if (lastPath == "") lastPath = m_audioFile;
Chris@81 317 if (lastPath == "") {
Chris@81 318 char *home = getenv("HOME");
Chris@81 319 if (home) lastPath = home;
Chris@81 320 else lastPath = ".";
Chris@81 321 }
Chris@81 322 lastPath = QFileInfo(lastPath).absoluteDir().canonicalPath();
Chris@81 323
Chris@81 324 QSettings settings;
Chris@81 325 settings.beginGroup("MainWindow");
Chris@81 326 lastPath = settings.value(settingsKey, lastPath).toString();
Chris@81 327
Chris@81 328 QString path = "";
Chris@81 329
Chris@81 330 // Use our own QFileDialog just for symmetry with getSaveFileName below
Chris@81 331
Chris@81 332 QFileDialog dialog(this);
Chris@81 333 dialog.setFilters(filter.split('\n'));
Chris@81 334 dialog.setWindowTitle(title);
Chris@81 335 dialog.setDirectory(lastPath);
Chris@81 336
Chris@81 337 dialog.setAcceptMode(QFileDialog::AcceptOpen);
Chris@81 338 dialog.setFileMode(QFileDialog::ExistingFile);
Chris@81 339
Chris@81 340 if (dialog.exec()) {
Chris@81 341 QStringList files = dialog.selectedFiles();
Chris@81 342 if (!files.empty()) path = *files.begin();
Chris@81 343
Chris@81 344 QFileInfo fi(path);
Chris@81 345
Chris@81 346 if (!fi.exists()) {
Chris@81 347
Chris@81 348 QMessageBox::critical(this, tr("File does not exist"),
Chris@81 349 tr("File \"%1\" does not exist").arg(path));
Chris@81 350 path = "";
Chris@81 351
Chris@81 352 } else if (!fi.isReadable()) {
Chris@81 353
Chris@81 354 QMessageBox::critical(this, tr("File is not readable"),
Chris@81 355 tr("File \"%1\" can not be read").arg(path));
Chris@81 356 path = "";
Chris@81 357
Chris@81 358 } else if (fi.isDir()) {
Chris@81 359
Chris@81 360 QMessageBox::critical(this, tr("Directory selected"),
Chris@81 361 tr("File \"%1\" is a directory").arg(path));
Chris@81 362 path = "";
Chris@81 363
Chris@81 364 } else if (!fi.isFile()) {
Chris@81 365
Chris@81 366 QMessageBox::critical(this, tr("Non-file selected"),
Chris@81 367 tr("Path \"%1\" is not a file").arg(path));
Chris@81 368 path = "";
Chris@81 369
Chris@81 370 } else if (fi.size() == 0) {
Chris@81 371
Chris@81 372 QMessageBox::critical(this, tr("File is empty"),
Chris@81 373 tr("File \"%1\" is empty").arg(path));
Chris@81 374 path = "";
Chris@81 375 }
Chris@81 376 }
Chris@81 377
Chris@81 378 if (path != "") {
Chris@81 379 settings.setValue(settingsKey,
Chris@81 380 QFileInfo(path).absoluteDir().canonicalPath());
Chris@81 381 }
Chris@81 382
Chris@81 383 return path;
Chris@81 384 }
Chris@81 385
Chris@81 386 QString
Chris@81 387 MainWindow::getSaveFileName(FileType type)
Chris@81 388 {
Chris@81 389 QString settingsKey;
Chris@81 390 QString lastPath;
Chris@81 391
Chris@81 392 QString title = tr("Select file");
Chris@81 393 QString filter = tr("All files (*.*)");
Chris@81 394
Chris@81 395 switch (type) {
Chris@81 396
Chris@81 397 case SessionFile:
Chris@81 398 settingsKey = "savesessionpath";
Chris@81 399 lastPath = m_sessionFile;
Chris@81 400 title = tr("Select a session file");
Chris@81 401 filter = tr("Sonic Visualiser session files (*.sv)\nAll files (*.*)");
Chris@81 402 break;
Chris@81 403
Chris@81 404 case AudioFile:
Chris@81 405 settingsKey = "saveaudiopath";
Chris@81 406 lastPath = m_audioFile;
Chris@81 407 title = "Select an audio file";
Chris@81 408 title = tr("Select a file to export to");
Chris@81 409 filter = tr("WAV audio files (*.wav)\nAll files (*.*)");
Chris@81 410 break;
Chris@81 411
Chris@81 412 case LayerFile:
Chris@81 413 settingsKey = "savelayerpath";
Chris@81 414 lastPath = m_sessionFile;
Chris@81 415 title = tr("Select a file to export to");
Chris@81 416 filter = tr("Sonic Visualiser Layer XML files (*.svl)\nComma-separated data files (*.csv)\nText files (*.txt)\nAll files (*.*)");
Chris@81 417 break;
Chris@81 418
Chris@81 419 case AnyFile:
Chris@81 420 std::cerr << "ERROR: Internal error: MainWindow::getSaveFileName: AnyFile cannot be used here" << std::endl;
Chris@81 421 abort();
Chris@81 422 };
Chris@81 423
Chris@81 424 if (lastPath == "") lastPath = m_audioFile;
Chris@81 425 if (lastPath == "") {
Chris@81 426 char *home = getenv("HOME");
Chris@81 427 if (home) lastPath = home;
Chris@81 428 else lastPath = ".";
Chris@81 429 }
Chris@81 430 lastPath = QFileInfo(lastPath).absoluteDir().canonicalPath();
Chris@81 431
Chris@81 432 QSettings settings;
Chris@81 433 settings.beginGroup("MainWindow");
Chris@81 434 lastPath = settings.value(settingsKey, lastPath).toString();
Chris@81 435
Chris@81 436 QString path = "";
Chris@81 437
Chris@81 438 // Use our own QFileDialog instead of static functions, as we may
Chris@81 439 // need to adjust the file extension based on the selected filter
Chris@81 440
Chris@81 441 QFileDialog dialog(this);
Chris@81 442 dialog.setFilters(filter.split('\n'));
Chris@81 443 dialog.setWindowTitle(title);
Chris@81 444 dialog.setDirectory(lastPath);
Chris@81 445
Chris@81 446 dialog.setAcceptMode(QFileDialog::AcceptSave);
Chris@81 447 dialog.setFileMode(QFileDialog::AnyFile);
Chris@81 448 dialog.setConfirmOverwrite(false); // we'll do that
Chris@81 449
Chris@81 450 if (type == SessionFile) {
Chris@81 451 dialog.setDefaultSuffix("sv");
Chris@81 452 } else if (type == AudioFile) {
Chris@81 453 dialog.setDefaultSuffix("wav");
Chris@81 454 }
Chris@81 455
Chris@81 456 bool good = false;
Chris@81 457
Chris@81 458 while (!good) {
Chris@81 459
Chris@81 460 path = "";
Chris@81 461
Chris@81 462 if (!dialog.exec()) break;
Chris@81 463
Chris@81 464 QStringList files = dialog.selectedFiles();
Chris@81 465 if (files.empty()) break;
Chris@81 466 path = *files.begin();
Chris@81 467
Chris@81 468 QFileInfo fi(path);
Chris@81 469
Chris@81 470 if (type == LayerFile && fi.suffix() == "") {
Chris@81 471 QString expectedExtension;
Chris@81 472 QString selectedFilter = dialog.selectedFilter();
Chris@81 473 if (selectedFilter.contains(".svl")) {
Chris@81 474 expectedExtension = "svl";
Chris@81 475 } else if (selectedFilter.contains(".txt")) {
Chris@81 476 expectedExtension = "txt";
Chris@81 477 } else if (selectedFilter.contains(".csv")) {
Chris@81 478 expectedExtension = "csv";
Chris@81 479 }
Chris@81 480 if (expectedExtension != "") {
Chris@81 481 path = QString("%1.%2").arg(path).arg(expectedExtension);
Chris@81 482 fi = QFileInfo(path);
Chris@81 483 }
Chris@81 484 }
Chris@81 485
Chris@81 486 if (fi.isDir()) {
Chris@81 487 QMessageBox::critical(this, tr("Directory selected"),
Chris@81 488 tr("File \"%1\" is a directory").arg(path));
Chris@81 489 continue;
Chris@81 490 }
Chris@81 491
Chris@81 492 if (fi.exists()) {
Chris@81 493 if (QMessageBox::question(this, tr("File exists"),
Chris@81 494 tr("The file \"%1\" already exists.\nDo you want to overwrite it?").arg(path),
Chris@81 495 QMessageBox::Ok,
Chris@81 496 QMessageBox::Cancel) != QMessageBox::Ok) {
Chris@81 497 continue;
Chris@81 498 }
Chris@81 499 }
Chris@81 500
Chris@81 501 good = true;
Chris@81 502 }
Chris@81 503
Chris@81 504 if (path != "") {
Chris@81 505 settings.setValue(settingsKey,
Chris@81 506 QFileInfo(path).absoluteDir().canonicalPath());
Chris@81 507 }
Chris@81 508
Chris@81 509 return path;
Chris@81 510 }
Chris@81 511
Chris@81 512 void
Chris@81 513 MainWindow::registerLastOpenedFilePath(FileType type, QString path)
Chris@81 514 {
Chris@81 515 QString settingsKey;
Chris@81 516
Chris@81 517 switch (type) {
Chris@81 518 case SessionFile:
Chris@81 519 settingsKey = "sessionpath";
Chris@81 520 break;
Chris@81 521
Chris@81 522 case AudioFile:
Chris@81 523 settingsKey = "audiopath";
Chris@81 524 break;
Chris@81 525
Chris@81 526 case LayerFile:
Chris@81 527 settingsKey = "layerpath";
Chris@81 528 break;
Chris@81 529
Chris@81 530 case AnyFile:
Chris@81 531 settingsKey = "lastpath";
Chris@81 532 break;
Chris@81 533 }
Chris@81 534
Chris@81 535 if (path != "") {
Chris@81 536 QSettings settings;
Chris@81 537 settings.beginGroup("MainWindow");
Chris@81 538 path = QFileInfo(path).absoluteDir().canonicalPath();
Chris@81 539 settings.setValue(settingsKey, path);
Chris@81 540 settings.setValue("lastpath", path);
Chris@81 541 }
Chris@81 542 }
Chris@81 543
Chris@0 544 void
Chris@0 545 MainWindow::setupMenus()
Chris@0 546 {
Chris@0 547 if (!m_mainMenusCreated) {
Chris@0 548 m_rightButtonMenu = new QMenu();
Chris@0 549 }
Chris@0 550
Chris@0 551 if (m_rightButtonLayerMenu) {
Chris@0 552 m_rightButtonLayerMenu->clear();
Chris@0 553 } else {
Chris@0 554 m_rightButtonLayerMenu = m_rightButtonMenu->addMenu(tr("&Layer"));
Chris@0 555 m_rightButtonMenu->addSeparator();
Chris@0 556 }
Chris@0 557
Chris@34 558 if (m_rightButtonTransformsMenu) {
Chris@34 559 m_rightButtonTransformsMenu->clear();
Chris@34 560 } else {
Chris@34 561 m_rightButtonTransformsMenu = m_rightButtonMenu->addMenu(tr("&Transform"));
Chris@34 562 m_rightButtonMenu->addSeparator();
Chris@34 563 }
Chris@34 564
Chris@0 565 if (!m_mainMenusCreated) {
Chris@0 566 CommandHistory::getInstance()->registerMenu(m_rightButtonMenu);
Chris@0 567 m_rightButtonMenu->addSeparator();
Chris@66 568 }
Chris@66 569
Chris@66 570 setupFileMenu();
Chris@66 571 setupEditMenu();
Chris@66 572 setupViewMenu();
Chris@66 573 setupPaneAndLayerMenus();
Chris@66 574 setupTransformsMenu();
Chris@66 575 setupHelpMenu();
Chris@66 576
Chris@66 577 m_mainMenusCreated = true;
Chris@66 578 }
Chris@66 579
Chris@66 580 void
Chris@66 581 MainWindow::setupFileMenu()
Chris@66 582 {
Chris@66 583 if (m_mainMenusCreated) return;
Chris@66 584
Chris@66 585 QMenu *menu = menuBar()->addMenu(tr("&File"));
Chris@66 586 QToolBar *toolbar = addToolBar(tr("File Toolbar"));
Chris@66 587
Chris@66 588 QIcon icon(":icons/filenew.png");
Chris@66 589 icon.addFile(":icons/filenew-22.png");
Chris@66 590 QAction *action = new QAction(icon, tr("&New Session"), this);
Chris@66 591 action->setShortcut(tr("Ctrl+N"));
Chris@66 592 action->setStatusTip(tr("Clear the current Sonic Visualiser session and start a new one"));
Chris@66 593 connect(action, SIGNAL(triggered()), this, SLOT(newSession()));
Chris@66 594 menu->addAction(action);
Chris@66 595 toolbar->addAction(action);
Chris@0 596
Chris@66 597 icon = QIcon(":icons/fileopen.png");
Chris@66 598 icon.addFile(":icons/fileopen-22.png");
Chris@66 599
Chris@66 600 action = new QAction(icon, tr("&Open Session..."), this);
Chris@66 601 action->setShortcut(tr("Ctrl+O"));
Chris@66 602 action->setStatusTip(tr("Open a previously saved Sonic Visualiser session file"));
Chris@66 603 connect(action, SIGNAL(triggered()), this, SLOT(openSession()));
Chris@66 604 menu->addAction(action);
Chris@66 605
Chris@66 606 action = new QAction(icon, tr("&Open..."), this);
Chris@66 607 action->setStatusTip(tr("Open a session file, audio file, or layer"));
Chris@66 608 connect(action, SIGNAL(triggered()), this, SLOT(openSomething()));
Chris@66 609 toolbar->addAction(action);
Chris@66 610
Chris@66 611 icon = QIcon(":icons/filesave.png");
Chris@66 612 icon.addFile(":icons/filesave-22.png");
Chris@66 613 action = new QAction(icon, tr("&Save Session"), this);
Chris@66 614 action->setShortcut(tr("Ctrl+S"));
Chris@66 615 action->setStatusTip(tr("Save the current session into a Sonic Visualiser session file"));
Chris@66 616 connect(action, SIGNAL(triggered()), this, SLOT(saveSession()));
Chris@66 617 connect(this, SIGNAL(canSave(bool)), action, SLOT(setEnabled(bool)));
Chris@66 618 menu->addAction(action);
Chris@66 619 toolbar->addAction(action);
Chris@0 620
Chris@66 621 icon = QIcon(":icons/filesaveas.png");
Chris@66 622 icon.addFile(":icons/filesaveas-22.png");
Chris@66 623 action = new QAction(icon, tr("Save Session &As..."), this);
Chris@66 624 action->setStatusTip(tr("Save the current session into a new Sonic Visualiser session file"));
Chris@66 625 connect(action, SIGNAL(triggered()), this, SLOT(saveSessionAs()));
Chris@66 626 menu->addAction(action);
Chris@66 627 toolbar->addAction(action);
Chris@66 628
Chris@66 629 menu->addSeparator();
Chris@66 630
Chris@66 631 action = new QAction(tr("&Import Audio File..."), this);
Chris@66 632 action->setShortcut(tr("Ctrl+I"));
Chris@66 633 action->setStatusTip(tr("Import an existing audio file"));
Chris@66 634 connect(action, SIGNAL(triggered()), this, SLOT(importAudio()));
Chris@66 635 menu->addAction(action);
Chris@66 636
Chris@66 637 action = new QAction(tr("Import Secondary Audio File..."), this);
Chris@66 638 action->setShortcut(tr("Ctrl+Shift+I"));
Chris@66 639 action->setStatusTip(tr("Import an extra audio file as a separate layer"));
Chris@66 640 connect(action, SIGNAL(triggered()), this, SLOT(importMoreAudio()));
Chris@66 641 connect(this, SIGNAL(canImportMoreAudio(bool)), action, SLOT(setEnabled(bool)));
Chris@66 642 menu->addAction(action);
Chris@66 643
Chris@66 644 action = new QAction(tr("&Export Audio File..."), this);
Chris@66 645 action->setStatusTip(tr("Export selection as an audio file"));
Chris@66 646 connect(action, SIGNAL(triggered()), this, SLOT(exportAudio()));
Chris@66 647 connect(this, SIGNAL(canExportAudio(bool)), action, SLOT(setEnabled(bool)));
Chris@66 648 menu->addAction(action);
Chris@66 649
Chris@66 650 menu->addSeparator();
Chris@66 651
Chris@66 652 action = new QAction(tr("Import Annotation &Layer..."), this);
Chris@66 653 action->setShortcut(tr("Ctrl+L"));
Chris@66 654 action->setStatusTip(tr("Import layer data from an existing file"));
Chris@66 655 connect(action, SIGNAL(triggered()), this, SLOT(importLayer()));
Chris@66 656 connect(this, SIGNAL(canImportLayer(bool)), action, SLOT(setEnabled(bool)));
Chris@66 657 menu->addAction(action);
Chris@66 658
Chris@66 659 action = new QAction(tr("Export Annotation Layer..."), this);
Chris@66 660 action->setStatusTip(tr("Export layer data to a file"));
Chris@66 661 connect(action, SIGNAL(triggered()), this, SLOT(exportLayer()));
Chris@66 662 connect(this, SIGNAL(canExportLayer(bool)), action, SLOT(setEnabled(bool)));
Chris@66 663 menu->addAction(action);
Chris@66 664
Chris@66 665 menu->addSeparator();
Chris@86 666
Chris@86 667 action = new QAction(tr("Open Lo&cation..."), this);
Chris@86 668 action->setShortcut(tr("Ctrl+Shift+O"));
Chris@86 669 action->setStatusTip(tr("Open or import a file from a remote URL"));
Chris@86 670 connect(action, SIGNAL(triggered()), this, SLOT(openLocation()));
Chris@86 671 menu->addAction(action);
Chris@86 672
Chris@86 673 menu->addSeparator();
Chris@86 674
Chris@66 675 m_recentFilesMenu = menu->addMenu(tr("&Recent Files"));
Chris@66 676 setupRecentFilesMenu();
Chris@66 677 connect(&m_recentFiles, SIGNAL(recentChanged()),
Chris@66 678 this, SLOT(setupRecentFilesMenu()));
Chris@66 679
Chris@66 680 menu->addSeparator();
Chris@66 681 action = new QAction(tr("&Preferences..."), this);
Chris@66 682 action->setStatusTip(tr("Adjust the application preferences"));
Chris@66 683 connect(action, SIGNAL(triggered()), this, SLOT(preferences()));
Chris@66 684 menu->addAction(action);
Chris@0 685
Chris@66 686 /*!!!
Chris@66 687 menu->addSeparator();
Chris@0 688
Chris@66 689 action = new QAction(tr("Play / Pause"), this);
Chris@66 690 action->setShortcut(tr("Space"));
Chris@66 691 action->setStatusTip(tr("Start or stop playback from the current position"));
Chris@66 692 connect(action, SIGNAL(triggered()), this, SLOT(play()));
Chris@66 693 menu->addAction(action);
Chris@66 694 */
Chris@66 695
Chris@66 696 menu->addSeparator();
Chris@66 697 action = new QAction(QIcon(":/icons/exit.png"),
Chris@66 698 tr("&Quit"), this);
Chris@66 699 action->setShortcut(tr("Ctrl+Q"));
Chris@66 700 connect(action, SIGNAL(triggered()), this, SLOT(close()));
Chris@66 701 menu->addAction(action);
Chris@66 702 }
Chris@66 703
Chris@66 704 void
Chris@66 705 MainWindow::setupEditMenu()
Chris@66 706 {
Chris@66 707 if (m_mainMenusCreated) return;
Chris@66 708
Chris@66 709 QMenu *menu = menuBar()->addMenu(tr("&Edit"));
Chris@66 710 CommandHistory::getInstance()->registerMenu(menu);
Chris@66 711
Chris@66 712 menu->addSeparator();
Chris@66 713
Chris@66 714 QAction *action = new QAction(QIcon(":/icons/editcut.png"),
Chris@66 715 tr("Cu&t"), this);
Chris@66 716 action->setShortcut(tr("Ctrl+X"));
Chris@66 717 connect(action, SIGNAL(triggered()), this, SLOT(cut()));
Chris@66 718 connect(this, SIGNAL(canEditSelection(bool)), action, SLOT(setEnabled(bool)));
Chris@66 719 menu->addAction(action);
Chris@66 720 m_rightButtonMenu->addAction(action);
Chris@66 721
Chris@66 722 action = new QAction(QIcon(":/icons/editcopy.png"),
Chris@66 723 tr("&Copy"), this);
Chris@66 724 action->setShortcut(tr("Ctrl+C"));
Chris@66 725 connect(action, SIGNAL(triggered()), this, SLOT(copy()));
Chris@66 726 connect(this, SIGNAL(canEditSelection(bool)), action, SLOT(setEnabled(bool)));
Chris@66 727 menu->addAction(action);
Chris@66 728 m_rightButtonMenu->addAction(action);
Chris@66 729
Chris@66 730 action = new QAction(QIcon(":/icons/editpaste.png"),
Chris@66 731 tr("&Paste"), this);
Chris@66 732 action->setShortcut(tr("Ctrl+V"));
Chris@66 733 connect(action, SIGNAL(triggered()), this, SLOT(paste()));
Chris@66 734 connect(this, SIGNAL(canPaste(bool)), action, SLOT(setEnabled(bool)));
Chris@66 735 menu->addAction(action);
Chris@66 736 m_rightButtonMenu->addAction(action);
Chris@66 737
Chris@66 738 action = new QAction(tr("&Delete Selected Items"), this);
Chris@66 739 action->setShortcut(tr("Del"));
Chris@66 740 connect(action, SIGNAL(triggered()), this, SLOT(deleteSelected()));
Chris@66 741 connect(this, SIGNAL(canEditSelection(bool)), action, SLOT(setEnabled(bool)));
Chris@66 742 menu->addAction(action);
Chris@66 743 m_rightButtonMenu->addAction(action);
Chris@66 744
Chris@66 745 menu->addSeparator();
Chris@66 746 m_rightButtonMenu->addSeparator();
Chris@0 747
Chris@66 748 action = new QAction(tr("Select &All"), this);
Chris@66 749 action->setShortcut(tr("Ctrl+A"));
Chris@66 750 connect(action, SIGNAL(triggered()), this, SLOT(selectAll()));
Chris@66 751 connect(this, SIGNAL(canSelect(bool)), action, SLOT(setEnabled(bool)));
Chris@66 752 menu->addAction(action);
Chris@66 753 m_rightButtonMenu->addAction(action);
Chris@0 754
Chris@66 755 action = new QAction(tr("Select &Visible Range"), this);
Chris@66 756 action->setShortcut(tr("Ctrl+Shift+A"));
Chris@66 757 connect(action, SIGNAL(triggered()), this, SLOT(selectVisible()));
Chris@66 758 connect(this, SIGNAL(canSelect(bool)), action, SLOT(setEnabled(bool)));
Chris@66 759 menu->addAction(action);
Chris@0 760
Chris@66 761 action = new QAction(tr("Select to &Start"), this);
Chris@66 762 action->setShortcut(tr("Shift+Left"));
Chris@66 763 connect(action, SIGNAL(triggered()), this, SLOT(selectToStart()));
Chris@66 764 connect(this, SIGNAL(canSelect(bool)), action, SLOT(setEnabled(bool)));
Chris@66 765 menu->addAction(action);
Chris@0 766
Chris@66 767 action = new QAction(tr("Select to &End"), this);
Chris@66 768 action->setShortcut(tr("Shift+Right"));
Chris@66 769 connect(action, SIGNAL(triggered()), this, SLOT(selectToEnd()));
Chris@66 770 connect(this, SIGNAL(canSelect(bool)), action, SLOT(setEnabled(bool)));
Chris@66 771 menu->addAction(action);
Chris@66 772
Chris@66 773 action = new QAction(tr("C&lear Selection"), this);
Chris@66 774 action->setShortcut(tr("Esc"));
Chris@66 775 connect(action, SIGNAL(triggered()), this, SLOT(clearSelection()));
Chris@66 776 connect(this, SIGNAL(canClearSelection(bool)), action, SLOT(setEnabled(bool)));
Chris@66 777 menu->addAction(action);
Chris@66 778 m_rightButtonMenu->addAction(action);
Chris@66 779
Chris@66 780 menu->addSeparator();
Chris@66 781
Chris@66 782 action = new QAction(tr("&Insert Instant at Playback Position"), this);
Chris@66 783 action->setShortcut(tr("Enter"));
Chris@66 784 connect(action, SIGNAL(triggered()), this, SLOT(insertInstant()));
Chris@66 785 connect(this, SIGNAL(canInsertInstant(bool)), action, SLOT(setEnabled(bool)));
Chris@66 786 menu->addAction(action);
Chris@66 787
Chris@81 788 action = new QAction(tr("Insert Instants at Selection &Boundaries"), this);
Chris@81 789 action->setShortcut(tr("Shift+Enter"));
Chris@81 790 connect(action, SIGNAL(triggered()), this, SLOT(insertInstantsAtBoundaries()));
Chris@81 791 connect(this, SIGNAL(canInsertInstantsAtBoundaries(bool)), action, SLOT(setEnabled(bool)));
Chris@81 792 menu->addAction(action);
Chris@81 793
Chris@66 794 // Laptop shortcut (no keypad Enter key)
Chris@66 795 connect(new QShortcut(tr(";"), this), SIGNAL(activated()),
Chris@66 796 this, SLOT(insertInstant()));
Chris@66 797 }
Chris@66 798
Chris@66 799 void
Chris@66 800 MainWindow::setupViewMenu()
Chris@66 801 {
Chris@66 802 if (m_mainMenusCreated) return;
Chris@66 803
Chris@66 804 QMenu *menu = menuBar()->addMenu(tr("&View"));
Chris@66 805
Chris@66 806 QActionGroup *overlayGroup = new QActionGroup(this);
Chris@0 807
Chris@66 808 QAction *action = new QAction(tr("&No Text Overlays"), this);
Chris@66 809 action->setShortcut(tr("0"));
Chris@66 810 action->setStatusTip(tr("Show no texts for frame times, layer names etc"));
Chris@66 811 connect(action, SIGNAL(triggered()), this, SLOT(showNoOverlays()));
Chris@66 812 action->setCheckable(true);
Chris@66 813 action->setChecked(false);
Chris@66 814 overlayGroup->addAction(action);
Chris@66 815 menu->addAction(action);
Chris@0 816
Chris@66 817 action = new QAction(tr("Basic &Text Overlays"), this);
Chris@66 818 action->setShortcut(tr("9"));
Chris@66 819 action->setStatusTip(tr("Show texts for frame times etc, but not layer names etc"));
Chris@66 820 connect(action, SIGNAL(triggered()), this, SLOT(showBasicOverlays()));
Chris@66 821 action->setCheckable(true);
Chris@66 822 action->setChecked(true);
Chris@66 823 overlayGroup->addAction(action);
Chris@66 824 menu->addAction(action);
Chris@0 825
Chris@66 826 action = new QAction(tr("&All Text Overlays"), this);
Chris@66 827 action->setShortcut(tr("8"));
Chris@66 828 action->setStatusTip(tr("Show texts for frame times, layer names etc"));
Chris@66 829 connect(action, SIGNAL(triggered()), this, SLOT(showAllTextOverlays()));
Chris@66 830 action->setCheckable(true);
Chris@66 831 action->setChecked(false);
Chris@66 832 overlayGroup->addAction(action);
Chris@66 833 menu->addAction(action);
Chris@66 834
Chris@66 835 menu->addSeparator();
Chris@0 836
Chris@66 837 action = new QAction(tr("Scroll &Left"), this);
Chris@66 838 action->setShortcut(tr("Left"));
Chris@66 839 action->setStatusTip(tr("Scroll the current pane to the left"));
Chris@66 840 connect(action, SIGNAL(triggered()), this, SLOT(scrollLeft()));
Chris@66 841 connect(this, SIGNAL(canScroll(bool)), action, SLOT(setEnabled(bool)));
Chris@66 842 menu->addAction(action);
Chris@0 843
Chris@66 844 action = new QAction(tr("Scroll &Right"), this);
Chris@66 845 action->setShortcut(tr("Right"));
Chris@66 846 action->setStatusTip(tr("Scroll the current pane to the right"));
Chris@66 847 connect(action, SIGNAL(triggered()), this, SLOT(scrollRight()));
Chris@66 848 connect(this, SIGNAL(canScroll(bool)), action, SLOT(setEnabled(bool)));
Chris@66 849 menu->addAction(action);
Chris@0 850
Chris@66 851 action = new QAction(tr("Jump Left"), this);
Chris@66 852 action->setShortcut(tr("Ctrl+Left"));
Chris@66 853 action->setStatusTip(tr("Scroll the current pane a big step to the left"));
Chris@66 854 connect(action, SIGNAL(triggered()), this, SLOT(jumpLeft()));
Chris@66 855 connect(this, SIGNAL(canScroll(bool)), action, SLOT(setEnabled(bool)));
Chris@66 856 menu->addAction(action);
Chris@0 857
Chris@66 858 action = new QAction(tr("Jump Right"), this);
Chris@66 859 action->setShortcut(tr("Ctrl+Right"));
Chris@66 860 action->setStatusTip(tr("Scroll the current pane a big step to the right"));
Chris@66 861 connect(action, SIGNAL(triggered()), this, SLOT(jumpRight()));
Chris@66 862 connect(this, SIGNAL(canScroll(bool)), action, SLOT(setEnabled(bool)));
Chris@66 863 menu->addAction(action);
Chris@66 864
Chris@66 865 menu->addSeparator();
Chris@66 866
Chris@66 867 action = new QAction(QIcon(":/icons/zoom-in.png"),
Chris@66 868 tr("Zoom &In"), this);
Chris@66 869 action->setShortcut(tr("Up"));
Chris@66 870 action->setStatusTip(tr("Increase the zoom level"));
Chris@66 871 connect(action, SIGNAL(triggered()), this, SLOT(zoomIn()));
Chris@66 872 connect(this, SIGNAL(canZoom(bool)), action, SLOT(setEnabled(bool)));
Chris@66 873 menu->addAction(action);
Chris@0 874
Chris@66 875 action = new QAction(QIcon(":/icons/zoom-out.png"),
Chris@66 876 tr("Zoom &Out"), this);
Chris@66 877 action->setShortcut(tr("Down"));
Chris@66 878 action->setStatusTip(tr("Decrease the zoom level"));
Chris@66 879 connect(action, SIGNAL(triggered()), this, SLOT(zoomOut()));
Chris@66 880 connect(this, SIGNAL(canZoom(bool)), action, SLOT(setEnabled(bool)));
Chris@66 881 menu->addAction(action);
Chris@0 882
Chris@66 883 action = new QAction(tr("Restore &Default Zoom"), this);
Chris@66 884 connect(action, SIGNAL(triggered()), this, SLOT(zoomDefault()));
Chris@66 885 connect(this, SIGNAL(canZoom(bool)), action, SLOT(setEnabled(bool)));
Chris@66 886 menu->addAction(action);
Chris@66 887
Chris@66 888 action = new QAction(tr("Zoom to &Fit"), this);
Chris@66 889 action->setStatusTip(tr("Zoom to show the whole file"));
Chris@66 890 connect(action, SIGNAL(triggered()), this, SLOT(zoomToFit()));
Chris@66 891 connect(this, SIGNAL(canZoom(bool)), action, SLOT(setEnabled(bool)));
Chris@66 892 menu->addAction(action);
Chris@7 893
Chris@72 894 menu->addSeparator();
Chris@72 895
Chris@66 896 action = new QAction(tr("Show &Zoom Wheels"), this);
Chris@66 897 action->setShortcut(tr("Z"));
Chris@66 898 action->setStatusTip(tr("Show thumbwheels for zooming horizontally and vertically"));
Chris@66 899 connect(action, SIGNAL(triggered()), this, SLOT(toggleZoomWheels()));
Chris@66 900 action->setCheckable(true);
Chris@66 901 action->setChecked(m_viewManager->getZoomWheelsEnabled());
Chris@66 902 menu->addAction(action);
Chris@72 903
Chris@72 904 action = new QAction(tr("Show Property Bo&xes"), this);
Chris@72 905 action->setShortcut(tr("X"));
Chris@72 906 action->setStatusTip(tr("Show the layer property boxes at the side of the main window"));
Chris@72 907 connect(action, SIGNAL(triggered()), this, SLOT(togglePropertyBoxes()));
Chris@72 908 action->setCheckable(true);
Chris@72 909 action->setChecked(true);
Chris@72 910 menu->addAction(action);
Chris@0 911
Chris@0 912 /*!!! This one doesn't work properly yet
Chris@62 913 */
Chris@66 914 menu->addSeparator();
Chris@66 915
Chris@66 916 action = new QAction(tr("Show &Layer Hierarchy"), this);
Chris@66 917 action->setShortcut(tr("Alt+L"));
Chris@66 918 connect(action, SIGNAL(triggered()), this, SLOT(showLayerTree()));
Chris@66 919 menu->addAction(action);
Chris@62 920 /* */
Chris@66 921 }
Chris@66 922
Chris@66 923 void
Chris@66 924 MainWindow::setupPaneAndLayerMenus()
Chris@66 925 {
Chris@0 926 if (m_paneMenu) {
Chris@0 927 m_paneActions.clear();
Chris@0 928 m_paneMenu->clear();
Chris@0 929 } else {
Chris@0 930 m_paneMenu = menuBar()->addMenu(tr("&Pane"));
Chris@0 931 }
Chris@0 932
Chris@0 933 if (m_layerMenu) {
Chris@0 934 m_layerActions.clear();
Chris@0 935 m_layerMenu->clear();
Chris@0 936 } else {
Chris@0 937 m_layerMenu = menuBar()->addMenu(tr("&Layer"));
Chris@0 938 }
Chris@0 939
Chris@66 940 QMenu *menu = m_paneMenu;
Chris@66 941
Chris@66 942 QAction *action = new QAction(QIcon(":/icons/pane.png"), tr("Add &New Pane"), this);
Chris@66 943 action->setShortcut(tr("Alt+N"));
Chris@66 944 action->setStatusTip(tr("Add a new pane containing only a time ruler"));
Chris@66 945 connect(action, SIGNAL(triggered()), this, SLOT(addPane()));
Chris@66 946 connect(this, SIGNAL(canAddPane(bool)), action, SLOT(setEnabled(bool)));
Chris@66 947 m_paneActions[action] = PaneConfiguration(LayerFactory::TimeRuler);
Chris@66 948 menu->addAction(action);
Chris@66 949
Chris@66 950 menu->addSeparator();
Chris@66 951
Chris@66 952 menu = m_layerMenu;
Chris@66 953
Chris@66 954 // menu->addSeparator();
Chris@66 955
Chris@66 956 LayerFactory::LayerTypeSet emptyLayerTypes =
Chris@66 957 LayerFactory::getInstance()->getValidEmptyLayerTypes();
Chris@66 958
Chris@66 959 for (LayerFactory::LayerTypeSet::iterator i = emptyLayerTypes.begin();
Chris@66 960 i != emptyLayerTypes.end(); ++i) {
Chris@66 961
Chris@66 962 QIcon icon;
Chris@66 963 QString mainText, tipText, channelText;
Chris@66 964 LayerFactory::LayerType type = *i;
Chris@66 965 QString name = LayerFactory::getInstance()->getLayerPresentationName(type);
Chris@66 966
Chris@66 967 icon = QIcon(QString(":/icons/%1.png")
Chris@66 968 .arg(LayerFactory::getInstance()->getLayerIconName(type)));
Chris@66 969
Chris@66 970 mainText = tr("Add New %1 Layer").arg(name);
Chris@66 971 tipText = tr("Add a new empty layer of type %1").arg(name);
Chris@66 972
Chris@66 973 action = new QAction(icon, mainText, this);
Chris@66 974 action->setStatusTip(tipText);
Chris@66 975
Chris@66 976 if (type == LayerFactory::Text) {
Chris@66 977 action->setShortcut(tr("Alt+T"));
Chris@66 978 }
Chris@66 979
Chris@66 980 connect(action, SIGNAL(triggered()), this, SLOT(addLayer()));
Chris@66 981 connect(this, SIGNAL(canAddLayer(bool)), action, SLOT(setEnabled(bool)));
Chris@66 982 m_layerActions[action] = type;
Chris@66 983 menu->addAction(action);
Chris@66 984 m_rightButtonLayerMenu->addAction(action);
Chris@66 985 }
Chris@66 986
Chris@66 987 m_rightButtonLayerMenu->addSeparator();
Chris@66 988 menu->addSeparator();
Chris@66 989
Chris@66 990 LayerFactory::LayerType backgroundTypes[] = {
Chris@66 991 LayerFactory::Waveform,
Chris@66 992 LayerFactory::Spectrogram,
Chris@66 993 LayerFactory::MelodicRangeSpectrogram,
Chris@66 994 LayerFactory::PeakFrequencySpectrogram,
Chris@66 995 LayerFactory::Spectrum
Chris@66 996 };
Chris@66 997
Chris@66 998 std::vector<Model *> models;
Chris@66 999 if (m_document) models = m_document->getTransformInputModels(); //!!! not well named for this!
Chris@66 1000 bool plural = (models.size() > 1);
Chris@66 1001 if (models.empty()) {
Chris@67 1002 models.push_back(getMainModel()); // probably 0
Chris@66 1003 }
Chris@66 1004
Chris@66 1005 for (unsigned int i = 0;
Chris@66 1006 i < sizeof(backgroundTypes)/sizeof(backgroundTypes[0]); ++i) {
Chris@66 1007
Chris@66 1008 for (int menuType = 0; menuType <= 1; ++menuType) { // pane, layer
Chris@66 1009
Chris@66 1010 if (menuType == 0) menu = m_paneMenu;
Chris@66 1011 else menu = m_layerMenu;
Chris@66 1012
Chris@66 1013 QMenu *submenu = 0;
Chris@66 1014
Chris@66 1015 QIcon icon;
Chris@66 1016 QString mainText, shortcutText, tipText, channelText;
Chris@66 1017 LayerFactory::LayerType type = backgroundTypes[i];
Chris@66 1018 bool mono = true;
Chris@66 1019
Chris@66 1020 switch (type) {
Chris@66 1021
Chris@66 1022 case LayerFactory::Waveform:
Chris@66 1023 icon = QIcon(":/icons/waveform.png");
Chris@66 1024 mainText = tr("Add &Waveform");
Chris@66 1025 if (menuType == 0) {
Chris@66 1026 shortcutText = tr("Alt+W");
Chris@66 1027 tipText = tr("Add a new pane showing a waveform view");
Chris@66 1028 } else {
Chris@66 1029 tipText = tr("Add a new layer showing a waveform view");
Chris@66 1030 }
Chris@66 1031 mono = false;
Chris@66 1032 break;
Chris@66 1033
Chris@66 1034 case LayerFactory::Spectrogram:
Chris@67 1035 icon = QIcon(":/icons/spectrogram.png");
Chris@66 1036 mainText = tr("Add &Spectrogram");
Chris@66 1037 if (menuType == 0) {
Chris@66 1038 shortcutText = tr("Alt+S");
Chris@66 1039 tipText = tr("Add a new pane showing a dB spectrogram");
Chris@66 1040 } else {
Chris@66 1041 tipText = tr("Add a new layer showing a dB spectrogram");
Chris@66 1042 }
Chris@66 1043 break;
Chris@66 1044
Chris@66 1045 case LayerFactory::MelodicRangeSpectrogram:
Chris@67 1046 icon = QIcon(":/icons/spectrogram.png");
Chris@66 1047 mainText = tr("Add &Melodic Range Spectrogram");
Chris@66 1048 if (menuType == 0) {
Chris@66 1049 shortcutText = tr("Alt+M");
Chris@66 1050 tipText = tr("Add a new pane showing a spectrogram set up for a pitch overview");
Chris@66 1051 } else {
Chris@66 1052 tipText = tr("Add a new layer showing a spectrogram set up for a pitch overview");
Chris@66 1053 }
Chris@66 1054 break;
Chris@66 1055
Chris@66 1056 case LayerFactory::PeakFrequencySpectrogram:
Chris@67 1057 icon = QIcon(":/icons/spectrogram.png");
Chris@66 1058 mainText = tr("Add &Peak Frequency Spectrogram");
Chris@66 1059 if (menuType == 0) {
Chris@66 1060 shortcutText = tr("Alt+P");
Chris@66 1061 tipText = tr("Add a new pane showing a spectrogram set up for tracking frequencies");
Chris@66 1062 } else {
Chris@66 1063 tipText = tr("Add a new layer showing a spectrogram set up for tracking frequencies");
Chris@66 1064 }
Chris@66 1065 break;
Chris@66 1066
Chris@66 1067 case LayerFactory::Spectrum:
Chris@67 1068 icon = QIcon(":/icons/spectrum.png");
Chris@66 1069 mainText = tr("Add Spectr&um");
Chris@66 1070 if (menuType == 0) {
Chris@66 1071 shortcutText = tr("Alt+U");
Chris@66 1072 tipText = tr("Add a new pane showing a frequency spectrum");
Chris@66 1073 } else {
Chris@66 1074 tipText = tr("Add a new layer showing a frequency spectrum");
Chris@66 1075 }
Chris@66 1076 break;
Chris@66 1077
Chris@66 1078 default: break;
Chris@66 1079 }
Chris@66 1080
Chris@66 1081 std::vector<Model *> candidateModels;
Chris@66 1082 if (menuType == 0) {
Chris@66 1083 candidateModels = models;
Chris@66 1084 } else {
Chris@66 1085 candidateModels.push_back(0);
Chris@66 1086 }
Chris@66 1087
Chris@66 1088 for (std::vector<Model *>::iterator mi =
Chris@66 1089 candidateModels.begin();
Chris@66 1090 mi != candidateModels.end(); ++mi) {
Chris@66 1091
Chris@66 1092 Model *model = *mi;
Chris@66 1093
Chris@66 1094 int channels = 0;
Chris@66 1095 if (model) {
Chris@66 1096 DenseTimeValueModel *dtvm =
Chris@66 1097 dynamic_cast<DenseTimeValueModel *>(model);
Chris@66 1098 if (dtvm) channels = dtvm->getChannelCount();
Chris@66 1099 }
Chris@66 1100 if (channels < 1 && getMainModel()) {
Chris@66 1101 channels = getMainModel()->getChannelCount();
Chris@66 1102 }
Chris@66 1103 if (channels < 1) channels = 1;
Chris@66 1104
Chris@66 1105 for (int c = 0; c <= channels; ++c) {
Chris@66 1106
Chris@66 1107 if (c == 1 && channels == 1) continue;
Chris@66 1108 bool isDefault = (c == 0);
Chris@66 1109 bool isOnly = (isDefault && (channels == 1));
Chris@66 1110
Chris@66 1111 if (menuType == 1) {
Chris@66 1112 if (isDefault) isOnly = true;
Chris@66 1113 else continue;
Chris@66 1114 }
Chris@66 1115
Chris@66 1116 if (isOnly && (!plural || menuType == 1)) {
Chris@66 1117
Chris@67 1118 if (type != LayerFactory::Waveform) {
Chris@67 1119 action = new QAction(mainText, this);
Chris@67 1120 } else {
Chris@67 1121 action = new QAction(icon, mainText, this);
Chris@67 1122 }
Chris@67 1123
Chris@66 1124 action->setShortcut(shortcutText);
Chris@66 1125 action->setStatusTip(tipText);
Chris@66 1126 if (menuType == 0) {
Chris@66 1127 connect(action, SIGNAL(triggered()),
Chris@66 1128 this, SLOT(addPane()));
Chris@66 1129 connect(this, SIGNAL(canAddPane(bool)),
Chris@66 1130 action, SLOT(setEnabled(bool)));
Chris@66 1131 m_paneActions[action] = PaneConfiguration(type);
Chris@66 1132 } else {
Chris@66 1133 connect(action, SIGNAL(triggered()),
Chris@66 1134 this, SLOT(addLayer()));
Chris@66 1135 connect(this, SIGNAL(canAddLayer(bool)),
Chris@66 1136 action, SLOT(setEnabled(bool)));
Chris@66 1137 m_layerActions[action] = type;
Chris@66 1138 }
Chris@66 1139 menu->addAction(action);
Chris@66 1140
Chris@66 1141 } else {
Chris@66 1142
Chris@66 1143 if (!submenu) {
Chris@66 1144 submenu = menu->addMenu(mainText);
Chris@67 1145 } else if (isDefault) {
Chris@67 1146 submenu->addSeparator();
Chris@66 1147 }
Chris@66 1148
Chris@66 1149 QString actionText;
Chris@66 1150 if (c == 0) {
Chris@66 1151 if (mono) {
Chris@66 1152 actionText = tr("&All Channels Mixed");
Chris@66 1153 } else {
Chris@66 1154 actionText = tr("&All Channels");
Chris@66 1155 }
Chris@66 1156 } else {
Chris@66 1157 actionText = tr("Channel &%1").arg(c);
Chris@66 1158 }
Chris@66 1159
Chris@66 1160 if (model) {
Chris@66 1161 actionText = tr("%1: %2")
Chris@66 1162 .arg(model->objectName())
Chris@66 1163 .arg(actionText);
Chris@66 1164 }
Chris@67 1165
Chris@67 1166 if (isDefault) {
Chris@67 1167 action = new QAction(icon, actionText, this);
Chris@67 1168 if (!model || model == getMainModel()) {
Chris@67 1169 action->setShortcut(shortcutText);
Chris@67 1170 }
Chris@67 1171 } else {
Chris@67 1172 action = new QAction(actionText, this);
Chris@67 1173 }
Chris@67 1174
Chris@66 1175 action->setStatusTip(tipText);
Chris@66 1176
Chris@66 1177 if (menuType == 0) {
Chris@66 1178 connect(action, SIGNAL(triggered()),
Chris@66 1179 this, SLOT(addPane()));
Chris@66 1180 connect(this, SIGNAL(canAddPane(bool)),
Chris@66 1181 action, SLOT(setEnabled(bool)));
Chris@66 1182 m_paneActions[action] =
Chris@66 1183 PaneConfiguration(type, model, c - 1);
Chris@66 1184 } else {
Chris@66 1185 connect(action, SIGNAL(triggered()),
Chris@66 1186 this, SLOT(addLayer()));
Chris@66 1187 connect(this, SIGNAL(canAddLayer(bool)),
Chris@66 1188 action, SLOT(setEnabled(bool)));
Chris@66 1189 m_layerActions[action] = type;
Chris@66 1190 }
Chris@66 1191
Chris@66 1192 submenu->addAction(action);
Chris@66 1193 }
Chris@66 1194 }
Chris@66 1195 }
Chris@66 1196 }
Chris@66 1197 }
Chris@66 1198
Chris@66 1199 menu = m_paneMenu;
Chris@66 1200
Chris@66 1201 menu->addSeparator();
Chris@66 1202
Chris@66 1203 action = new QAction(QIcon(":/icons/editdelete.png"), tr("&Delete Pane"), this);
Chris@66 1204 action->setShortcut(tr("Alt+D"));
Chris@66 1205 action->setStatusTip(tr("Delete the currently selected pane"));
Chris@66 1206 connect(action, SIGNAL(triggered()), this, SLOT(deleteCurrentPane()));
Chris@66 1207 connect(this, SIGNAL(canDeleteCurrentPane(bool)), action, SLOT(setEnabled(bool)));
Chris@66 1208 menu->addAction(action);
Chris@66 1209
Chris@66 1210 menu = m_layerMenu;
Chris@66 1211
Chris@66 1212 action = new QAction(QIcon(":/icons/timeruler.png"), tr("Add &Time Ruler"), this);
Chris@66 1213 action->setStatusTip(tr("Add a new layer showing a time ruler"));
Chris@66 1214 connect(action, SIGNAL(triggered()), this, SLOT(addLayer()));
Chris@66 1215 connect(this, SIGNAL(canAddLayer(bool)), action, SLOT(setEnabled(bool)));
Chris@66 1216 m_layerActions[action] = LayerFactory::TimeRuler;
Chris@66 1217 menu->addAction(action);
Chris@66 1218
Chris@66 1219 menu->addSeparator();
Chris@66 1220
Chris@66 1221 m_existingLayersMenu = menu->addMenu(tr("Add &Existing Layer"));
Chris@66 1222 m_rightButtonLayerMenu->addMenu(m_existingLayersMenu);
Chris@66 1223 setupExistingLayersMenu();
Chris@66 1224
Chris@66 1225 m_rightButtonLayerMenu->addSeparator();
Chris@66 1226 menu->addSeparator();
Chris@66 1227
Chris@66 1228 action = new QAction(tr("&Rename Layer..."), this);
Chris@66 1229 action->setShortcut(tr("Alt+R"));
Chris@66 1230 action->setStatusTip(tr("Rename the currently active layer"));
Chris@66 1231 connect(action, SIGNAL(triggered()), this, SLOT(renameCurrentLayer()));
Chris@66 1232 connect(this, SIGNAL(canRenameLayer(bool)), action, SLOT(setEnabled(bool)));
Chris@66 1233 menu->addAction(action);
Chris@66 1234 m_rightButtonLayerMenu->addAction(action);
Chris@66 1235
Chris@66 1236 action = new QAction(QIcon(":/icons/editdelete.png"), tr("&Delete Layer"), this);
Chris@66 1237 action->setShortcut(tr("Alt+Shift+D"));
Chris@66 1238 action->setStatusTip(tr("Delete the currently active layer"));
Chris@66 1239 connect(action, SIGNAL(triggered()), this, SLOT(deleteCurrentLayer()));
Chris@66 1240 connect(this, SIGNAL(canDeleteCurrentLayer(bool)), action, SLOT(setEnabled(bool)));
Chris@66 1241 menu->addAction(action);
Chris@66 1242 m_rightButtonLayerMenu->addAction(action);
Chris@66 1243 }
Chris@66 1244
Chris@66 1245 void
Chris@66 1246 MainWindow::setupTransformsMenu()
Chris@66 1247 {
Chris@34 1248 if (m_transformsMenu) {
Chris@34 1249 m_transformActions.clear();
Chris@34 1250 m_transformActionsReverse.clear();
Chris@34 1251 m_transformsMenu->clear();
Chris@34 1252 } else {
Chris@34 1253 m_transformsMenu = menuBar()->addMenu(tr("&Transform"));
Chris@34 1254 }
Chris@34 1255
Chris@0 1256 TransformFactory::TransformList transforms =
Chris@0 1257 TransformFactory::getInstance()->getAllTransforms();
Chris@0 1258
Chris@33 1259 vector<QString> types =
Chris@0 1260 TransformFactory::getInstance()->getAllTransformTypes();
Chris@0 1261
Chris@37 1262 map<QString, map<QString, SubdividingMenu *> > categoryMenus;
Chris@37 1263 map<QString, map<QString, SubdividingMenu *> > makerMenus;
Chris@33 1264
Chris@36 1265 map<QString, SubdividingMenu *> byPluginNameMenus;
Chris@33 1266 map<QString, map<QString, QMenu *> > pluginNameMenus;
Chris@33 1267
Chris@37 1268 set<SubdividingMenu *> pendingMenus;
Chris@37 1269
Chris@34 1270 m_recentTransformsMenu = m_transformsMenu->addMenu(tr("&Recent Transforms"));
Chris@34 1271 m_rightButtonTransformsMenu->addMenu(m_recentTransformsMenu);
Chris@34 1272 connect(&m_recentTransforms, SIGNAL(recentChanged()),
Chris@34 1273 this, SLOT(setupRecentTransformsMenu()));
Chris@34 1274
Chris@34 1275 m_transformsMenu->addSeparator();
Chris@34 1276 m_rightButtonTransformsMenu->addSeparator();
Chris@34 1277
Chris@33 1278 for (vector<QString>::iterator i = types.begin(); i != types.end(); ++i) {
Chris@33 1279
Chris@33 1280 if (i != types.begin()) {
Chris@34 1281 m_transformsMenu->addSeparator();
Chris@34 1282 m_rightButtonTransformsMenu->addSeparator();
Chris@33 1283 }
Chris@33 1284
Chris@33 1285 QString byCategoryLabel = tr("%1 by Category").arg(*i);
Chris@37 1286 SubdividingMenu *byCategoryMenu = new SubdividingMenu(byCategoryLabel,
Chris@37 1287 20, 40);
Chris@37 1288 m_transformsMenu->addMenu(byCategoryMenu);
Chris@34 1289 m_rightButtonTransformsMenu->addMenu(byCategoryMenu);
Chris@37 1290 pendingMenus.insert(byCategoryMenu);
Chris@33 1291
Chris@33 1292 vector<QString> categories =
Chris@33 1293 TransformFactory::getInstance()->getTransformCategories(*i);
Chris@33 1294
Chris@33 1295 for (vector<QString>::iterator j = categories.begin();
Chris@33 1296 j != categories.end(); ++j) {
Chris@33 1297
Chris@33 1298 QString category = *j;
Chris@33 1299 if (category == "") category = tr("Unclassified");
Chris@33 1300
Chris@33 1301 if (categories.size() < 2) {
Chris@33 1302 categoryMenus[*i][category] = byCategoryMenu;
Chris@33 1303 continue;
Chris@33 1304 }
Chris@33 1305
Chris@33 1306 QStringList components = category.split(" > ");
Chris@33 1307 QString key;
Chris@33 1308
Chris@33 1309 for (QStringList::iterator k = components.begin();
Chris@33 1310 k != components.end(); ++k) {
Chris@33 1311
Chris@33 1312 QString parentKey = key;
Chris@33 1313 if (key != "") key += " > ";
Chris@33 1314 key += *k;
Chris@33 1315
Chris@33 1316 if (categoryMenus[*i].find(key) == categoryMenus[*i].end()) {
Chris@37 1317 SubdividingMenu *m = new SubdividingMenu(*k, 20, 40);
Chris@37 1318 pendingMenus.insert(m);
Chris@37 1319 categoryMenus[*i][key] = m;
Chris@33 1320 if (parentKey == "") {
Chris@37 1321 byCategoryMenu->addMenu(m);
Chris@33 1322 } else {
Chris@37 1323 categoryMenus[*i][parentKey]->addMenu(m);
Chris@33 1324 }
Chris@33 1325 }
Chris@33 1326 }
Chris@33 1327 }
Chris@33 1328
Chris@34 1329 QString byPluginNameLabel = tr("%1 by Plugin Name").arg(*i);
Chris@36 1330 byPluginNameMenus[*i] = new SubdividingMenu(byPluginNameLabel);
Chris@36 1331 m_transformsMenu->addMenu(byPluginNameMenus[*i]);
Chris@34 1332 m_rightButtonTransformsMenu->addMenu(byPluginNameMenus[*i]);
Chris@37 1333 pendingMenus.insert(byPluginNameMenus[*i]);
Chris@34 1334
Chris@33 1335 QString byMakerLabel = tr("%1 by Maker").arg(*i);
Chris@37 1336 SubdividingMenu *byMakerMenu = new SubdividingMenu(byMakerLabel, 20, 40);
Chris@37 1337 m_transformsMenu->addMenu(byMakerMenu);
Chris@34 1338 m_rightButtonTransformsMenu->addMenu(byMakerMenu);
Chris@37 1339 pendingMenus.insert(byMakerMenu);
Chris@33 1340
Chris@33 1341 vector<QString> makers =
Chris@33 1342 TransformFactory::getInstance()->getTransformMakers(*i);
Chris@37 1343
Chris@33 1344 for (vector<QString>::iterator j = makers.begin();
Chris@33 1345 j != makers.end(); ++j) {
Chris@33 1346
Chris@33 1347 QString maker = *j;
Chris@33 1348 if (maker == "") maker = tr("Unknown");
Chris@55 1349 maker.replace(QRegExp(tr(" [\\(<].*$")), "");
Chris@55 1350
Chris@37 1351 makerMenus[*i][maker] = new SubdividingMenu(maker, 30, 40);
Chris@37 1352 byMakerMenu->addMenu(makerMenus[*i][maker]);
Chris@37 1353 pendingMenus.insert(makerMenus[*i][maker]);
Chris@33 1354 }
Chris@0 1355 }
Chris@0 1356
Chris@0 1357 for (unsigned int i = 0; i < transforms.size(); ++i) {
Chris@0 1358
Chris@0 1359 QString description = transforms[i].description;
Chris@0 1360 if (description == "") description = transforms[i].name;
Chris@0 1361
Chris@80 1362 // std::cerr << "Plugin Description: " << description.toStdString() << std::endl;
Chris@80 1363
Chris@33 1364 QString type = transforms[i].type;
Chris@33 1365
Chris@33 1366 QString category = transforms[i].category;
Chris@33 1367 if (category == "") category = tr("Unclassified");
Chris@33 1368
Chris@33 1369 QString maker = transforms[i].maker;
Chris@33 1370 if (maker == "") maker = tr("Unknown");
Chris@55 1371 maker.replace(QRegExp(tr(" [\\(<].*$")), "");
Chris@33 1372
Chris@33 1373 QString pluginName = description.section(": ", 0, 0);
Chris@33 1374 QString output = description.section(": ", 1);
Chris@33 1375
Chris@66 1376 QAction *action = new QAction(tr("%1...").arg(description), this);
Chris@0 1377 connect(action, SIGNAL(triggered()), this, SLOT(addLayer()));
Chris@34 1378 m_transformActions[action] = transforms[i].name;
Chris@34 1379 m_transformActionsReverse[transforms[i].name] = action;
Chris@0 1380 connect(this, SIGNAL(canAddLayer(bool)), action, SLOT(setEnabled(bool)));
Chris@33 1381
Chris@33 1382 if (categoryMenus[type].find(category) == categoryMenus[type].end()) {
Chris@33 1383 std::cerr << "WARNING: MainWindow::setupMenus: Internal error: "
Chris@33 1384 << "No category menu for transform \""
Chris@33 1385 << description.toStdString() << "\" (category = \""
Chris@33 1386 << category.toStdString() << "\")" << std::endl;
Chris@33 1387 } else {
Chris@33 1388 categoryMenus[type][category]->addAction(action);
Chris@33 1389 }
Chris@33 1390
Chris@33 1391 if (makerMenus[type].find(maker) == makerMenus[type].end()) {
Chris@33 1392 std::cerr << "WARNING: MainWindow::setupMenus: Internal error: "
Chris@33 1393 << "No maker menu for transform \""
Chris@33 1394 << description.toStdString() << "\" (maker = \""
Chris@33 1395 << maker.toStdString() << "\")" << std::endl;
Chris@33 1396 } else {
Chris@80 1397 makerMenus[type][maker]->addAction(action);
Chris@33 1398 }
Chris@33 1399
Chris@33 1400 action = new QAction(tr("%1...").arg(output == "" ? pluginName : output), this);
Chris@33 1401 connect(action, SIGNAL(triggered()), this, SLOT(addLayer()));
Chris@34 1402 m_transformActions[action] = transforms[i].name;
Chris@33 1403 connect(this, SIGNAL(canAddLayer(bool)), action, SLOT(setEnabled(bool)));
Chris@33 1404
Chris@34 1405 // cerr << "Transform: \"" << name.toStdString() << "\": plugin name \"" << pluginName.toStdString() << "\"" << endl;
Chris@34 1406
Chris@33 1407 if (pluginNameMenus[type].find(pluginName) ==
Chris@33 1408 pluginNameMenus[type].end()) {
Chris@33 1409
Chris@36 1410 SubdividingMenu *parentMenu = byPluginNameMenus[type];
Chris@34 1411
Chris@33 1412 if (output == "") {
Chris@36 1413 parentMenu->addAction(pluginName, action);
Chris@33 1414 } else {
Chris@34 1415 pluginNameMenus[type][pluginName] =
Chris@34 1416 parentMenu->addMenu(pluginName);
Chris@33 1417 connect(this, SIGNAL(canAddLayer(bool)),
Chris@33 1418 pluginNameMenus[type][pluginName],
Chris@33 1419 SLOT(setEnabled(bool)));
Chris@33 1420 }
Chris@33 1421 }
Chris@33 1422
Chris@33 1423 if (pluginNameMenus[type].find(pluginName) !=
Chris@33 1424 pluginNameMenus[type].end()) {
Chris@33 1425 pluginNameMenus[type][pluginName]->addAction(action);
Chris@33 1426 }
Chris@0 1427 }
Chris@0 1428
Chris@37 1429 for (set<SubdividingMenu *>::iterator i = pendingMenus.begin();
Chris@37 1430 i != pendingMenus.end(); ++i) {
Chris@37 1431 (*i)->entriesAdded();
Chris@37 1432 }
Chris@37 1433
Chris@34 1434 setupRecentTransformsMenu();
Chris@66 1435 }
Chris@66 1436
Chris@66 1437 void
Chris@66 1438 MainWindow::setupHelpMenu()
Chris@66 1439 {
Chris@66 1440 if (m_mainMenusCreated) return;
Chris@66 1441
Chris@66 1442 QMenu *menu = menuBar()->addMenu(tr("&Help"));
Chris@66 1443
Chris@66 1444 QAction *action = new QAction(tr("&Help Reference"), this);
Chris@66 1445 action->setStatusTip(tr("Open the Sonic Visualiser reference manual"));
Chris@66 1446 connect(action, SIGNAL(triggered()), this, SLOT(help()));
Chris@0 1447 menu->addAction(action);
Chris@0 1448
Chris@66 1449 action = new QAction(tr("Sonic Visualiser on the &Web"), this);
Chris@66 1450 action->setStatusTip(tr("Open the Sonic Visualiser website"));
Chris@66 1451 connect(action, SIGNAL(triggered()), this, SLOT(website()));
Chris@0 1452 menu->addAction(action);
Chris@66 1453
Chris@66 1454 action = new QAction(tr("&About Sonic Visualiser"), this);
Chris@66 1455 action->setStatusTip(tr("Show information about Sonic Visualiser"));
Chris@66 1456 connect(action, SIGNAL(triggered()), this, SLOT(about()));
Chris@0 1457 menu->addAction(action);
Chris@0 1458 }
Chris@0 1459
Chris@0 1460 void
Chris@0 1461 MainWindow::setupRecentFilesMenu()
Chris@0 1462 {
Chris@0 1463 m_recentFilesMenu->clear();
Chris@34 1464 vector<QString> files = m_recentFiles.getRecent();
Chris@0 1465 for (size_t i = 0; i < files.size(); ++i) {
Chris@0 1466 QAction *action = new QAction(files[i], this);
Chris@0 1467 connect(action, SIGNAL(triggered()), this, SLOT(openRecentFile()));
Chris@0 1468 m_recentFilesMenu->addAction(action);
Chris@0 1469 }
Chris@0 1470 }
Chris@0 1471
Chris@0 1472 void
Chris@34 1473 MainWindow::setupRecentTransformsMenu()
Chris@34 1474 {
Chris@34 1475 m_recentTransformsMenu->clear();
Chris@34 1476 vector<QString> transforms = m_recentTransforms.getRecent();
Chris@34 1477 for (size_t i = 0; i < transforms.size(); ++i) {
Chris@34 1478 TransformActionReverseMap::iterator ti =
Chris@34 1479 m_transformActionsReverse.find(transforms[i]);
Chris@34 1480 if (ti == m_transformActionsReverse.end()) {
Chris@34 1481 std::cerr << "WARNING: MainWindow::setupRecentTransformsMenu: "
Chris@34 1482 << "Unknown transform \"" << transforms[i].toStdString()
Chris@34 1483 << "\" in recent transforms list" << std::endl;
Chris@34 1484 continue;
Chris@34 1485 }
Chris@34 1486 m_recentTransformsMenu->addAction(ti->second);
Chris@34 1487 }
Chris@34 1488 }
Chris@34 1489
Chris@34 1490 void
Chris@0 1491 MainWindow::setupExistingLayersMenu()
Chris@0 1492 {
Chris@0 1493 if (!m_existingLayersMenu) return; // should have been created by setupMenus
Chris@0 1494
Chris@0 1495 // std::cerr << "MainWindow::setupExistingLayersMenu" << std::endl;
Chris@0 1496
Chris@0 1497 m_existingLayersMenu->clear();
Chris@0 1498 m_existingLayerActions.clear();
Chris@0 1499
Chris@33 1500 vector<Layer *> orderedLayers;
Chris@33 1501 set<Layer *> observedLayers;
Chris@0 1502
Chris@0 1503 for (int i = 0; i < m_paneStack->getPaneCount(); ++i) {
Chris@0 1504
Chris@0 1505 Pane *pane = m_paneStack->getPane(i);
Chris@0 1506 if (!pane) continue;
Chris@0 1507
Chris@0 1508 for (int j = 0; j < pane->getLayerCount(); ++j) {
Chris@0 1509
Chris@0 1510 Layer *layer = pane->getLayer(j);
Chris@0 1511 if (!layer) continue;
Chris@0 1512 if (observedLayers.find(layer) != observedLayers.end()) {
Chris@0 1513 std::cerr << "found duplicate layer " << layer << std::endl;
Chris@0 1514 continue;
Chris@0 1515 }
Chris@0 1516
Chris@0 1517 // std::cerr << "found new layer " << layer << " (name = "
Chris@0 1518 // << layer->getLayerPresentationName().toStdString() << ")" << std::endl;
Chris@0 1519
Chris@0 1520 orderedLayers.push_back(layer);
Chris@0 1521 observedLayers.insert(layer);
Chris@0 1522 }
Chris@0 1523 }
Chris@0 1524
Chris@33 1525 map<QString, int> observedNames;
Chris@0 1526
Chris@0 1527 for (int i = 0; i < orderedLayers.size(); ++i) {
Chris@0 1528
Chris@0 1529 QString name = orderedLayers[i]->getLayerPresentationName();
Chris@0 1530 int n = ++observedNames[name];
Chris@0 1531 if (n > 1) name = QString("%1 <%2>").arg(name).arg(n);
Chris@0 1532
Chris@0 1533 QAction *action = new QAction(name, this);
Chris@0 1534 connect(action, SIGNAL(triggered()), this, SLOT(addLayer()));
Chris@0 1535 connect(this, SIGNAL(canAddLayer(bool)), action, SLOT(setEnabled(bool)));
Chris@0 1536 m_existingLayerActions[action] = orderedLayers[i];
Chris@0 1537
Chris@0 1538 m_existingLayersMenu->addAction(action);
Chris@0 1539 }
Chris@0 1540 }
Chris@0 1541
Chris@0 1542 void
Chris@0 1543 MainWindow::setupToolbars()
Chris@0 1544 {
Chris@0 1545 QToolBar *toolbar = addToolBar(tr("Transport Toolbar"));
Chris@0 1546
Chris@0 1547 QAction *action = toolbar->addAction(QIcon(":/icons/rewind-start.png"),
Chris@0 1548 tr("Rewind to Start"));
Chris@0 1549 action->setShortcut(tr("Home"));
Chris@0 1550 action->setStatusTip(tr("Rewind to the start"));
Chris@0 1551 connect(action, SIGNAL(triggered()), this, SLOT(rewindStart()));
Chris@0 1552 connect(this, SIGNAL(canPlay(bool)), action, SLOT(setEnabled(bool)));
Chris@0 1553
Chris@0 1554 action = toolbar->addAction(QIcon(":/icons/rewind.png"),
Chris@0 1555 tr("Rewind"));
Chris@0 1556 action->setShortcut(tr("PageUp"));
Chris@0 1557 action->setStatusTip(tr("Rewind to the previous time instant in the current layer"));
Chris@0 1558 connect(action, SIGNAL(triggered()), this, SLOT(rewind()));
Chris@0 1559 connect(this, SIGNAL(canRewind(bool)), action, SLOT(setEnabled(bool)));
Chris@0 1560
Chris@0 1561 action = toolbar->addAction(QIcon(":/icons/playpause.png"),
Chris@0 1562 tr("Play / Pause"));
Chris@0 1563 action->setCheckable(true);
Chris@0 1564 action->setShortcut(tr("Space"));
Chris@0 1565 action->setStatusTip(tr("Start or stop playback from the current position"));
Chris@0 1566 connect(action, SIGNAL(triggered()), this, SLOT(play()));
Chris@0 1567 connect(m_playSource, SIGNAL(playStatusChanged(bool)),
Chris@0 1568 action, SLOT(setChecked(bool)));
Chris@0 1569 connect(this, SIGNAL(canPlay(bool)), action, SLOT(setEnabled(bool)));
Chris@0 1570
Chris@0 1571 action = toolbar->addAction(QIcon(":/icons/ffwd.png"),
Chris@0 1572 tr("Fast Forward"));
Chris@0 1573 action->setShortcut(tr("PageDown"));
Chris@0 1574 action->setStatusTip(tr("Fast forward to the next time instant in the current layer"));
Chris@0 1575 connect(action, SIGNAL(triggered()), this, SLOT(ffwd()));
Chris@0 1576 connect(this, SIGNAL(canFfwd(bool)), action, SLOT(setEnabled(bool)));
Chris@0 1577
Chris@0 1578 action = toolbar->addAction(QIcon(":/icons/ffwd-end.png"),
Chris@0 1579 tr("Fast Forward to End"));
Chris@0 1580 action->setShortcut(tr("End"));
Chris@0 1581 action->setStatusTip(tr("Fast-forward to the end"));
Chris@0 1582 connect(action, SIGNAL(triggered()), this, SLOT(ffwdEnd()));
Chris@0 1583 connect(this, SIGNAL(canPlay(bool)), action, SLOT(setEnabled(bool)));
Chris@0 1584
Chris@0 1585 toolbar = addToolBar(tr("Play Mode Toolbar"));
Chris@0 1586
Chris@0 1587 action = toolbar->addAction(QIcon(":/icons/playselection.png"),
Chris@0 1588 tr("Constrain Playback to Selection"));
Chris@0 1589 action->setCheckable(true);
Chris@0 1590 action->setChecked(m_viewManager->getPlaySelectionMode());
Chris@0 1591 action->setShortcut(tr("s"));
Chris@0 1592 action->setStatusTip(tr("Constrain playback to the selected area"));
Chris@69 1593 connect(m_viewManager, SIGNAL(playSelectionModeChanged(bool)),
Chris@69 1594 action, SLOT(setChecked(bool)));
Chris@0 1595 connect(action, SIGNAL(triggered()), this, SLOT(playSelectionToggled()));
Chris@0 1596 connect(this, SIGNAL(canPlaySelection(bool)), action, SLOT(setEnabled(bool)));
Chris@0 1597
Chris@0 1598 action = toolbar->addAction(QIcon(":/icons/playloop.png"),
Chris@0 1599 tr("Loop Playback"));
Chris@0 1600 action->setCheckable(true);
Chris@0 1601 action->setChecked(m_viewManager->getPlayLoopMode());
Chris@0 1602 action->setShortcut(tr("l"));
Chris@0 1603 action->setStatusTip(tr("Loop playback"));
Chris@69 1604 connect(m_viewManager, SIGNAL(playLoopModeChanged(bool)),
Chris@69 1605 action, SLOT(setChecked(bool)));
Chris@0 1606 connect(action, SIGNAL(triggered()), this, SLOT(playLoopToggled()));
Chris@0 1607 connect(this, SIGNAL(canPlay(bool)), action, SLOT(setEnabled(bool)));
Chris@0 1608
Chris@0 1609 toolbar = addToolBar(tr("Edit Toolbar"));
Chris@0 1610 CommandHistory::getInstance()->registerToolbar(toolbar);
Chris@0 1611
Chris@0 1612 toolbar = addToolBar(tr("Tools Toolbar"));
Chris@0 1613 QActionGroup *group = new QActionGroup(this);
Chris@0 1614
Chris@0 1615 action = toolbar->addAction(QIcon(":/icons/navigate.png"),
Chris@0 1616 tr("Navigate"));
Chris@0 1617 action->setCheckable(true);
Chris@0 1618 action->setChecked(true);
Chris@0 1619 action->setShortcut(tr("1"));
Chris@0 1620 connect(action, SIGNAL(triggered()), this, SLOT(toolNavigateSelected()));
Chris@0 1621 group->addAction(action);
Chris@0 1622 m_toolActions[ViewManager::NavigateMode] = action;
Chris@0 1623
Chris@0 1624 action = toolbar->addAction(QIcon(":/icons/select.png"),
Chris@0 1625 tr("Select"));
Chris@0 1626 action->setCheckable(true);
Chris@0 1627 action->setShortcut(tr("2"));
Chris@0 1628 connect(action, SIGNAL(triggered()), this, SLOT(toolSelectSelected()));
Chris@0 1629 group->addAction(action);
Chris@0 1630 m_toolActions[ViewManager::SelectMode] = action;
Chris@0 1631
Chris@0 1632 action = toolbar->addAction(QIcon(":/icons/move.png"),
Chris@0 1633 tr("Edit"));
Chris@0 1634 action->setCheckable(true);
Chris@0 1635 action->setShortcut(tr("3"));
Chris@0 1636 connect(action, SIGNAL(triggered()), this, SLOT(toolEditSelected()));
Chris@0 1637 connect(this, SIGNAL(canEditLayer(bool)), action, SLOT(setEnabled(bool)));
Chris@0 1638 group->addAction(action);
Chris@0 1639 m_toolActions[ViewManager::EditMode] = action;
Chris@0 1640
Chris@0 1641 action = toolbar->addAction(QIcon(":/icons/draw.png"),
Chris@0 1642 tr("Draw"));
Chris@0 1643 action->setCheckable(true);
Chris@0 1644 action->setShortcut(tr("4"));
Chris@0 1645 connect(action, SIGNAL(triggered()), this, SLOT(toolDrawSelected()));
Chris@0 1646 connect(this, SIGNAL(canEditLayer(bool)), action, SLOT(setEnabled(bool)));
Chris@0 1647 group->addAction(action);
Chris@0 1648 m_toolActions[ViewManager::DrawMode] = action;
Chris@0 1649
Chris@0 1650 // action = toolbar->addAction(QIcon(":/icons/text.png"),
Chris@0 1651 // tr("Text"));
Chris@0 1652 // action->setCheckable(true);
Chris@0 1653 // action->setShortcut(tr("5"));
Chris@0 1654 // connect(action, SIGNAL(triggered()), this, SLOT(toolTextSelected()));
Chris@0 1655 // group->addAction(action);
Chris@0 1656 // m_toolActions[ViewManager::TextMode] = action;
Chris@0 1657
Chris@0 1658 toolNavigateSelected();
Chris@0 1659 }
Chris@0 1660
Chris@0 1661 void
Chris@0 1662 MainWindow::updateMenuStates()
Chris@0 1663 {
Chris@0 1664 bool haveCurrentPane =
Chris@0 1665 (m_paneStack &&
Chris@0 1666 (m_paneStack->getCurrentPane() != 0));
Chris@0 1667 bool haveCurrentLayer =
Chris@0 1668 (haveCurrentPane &&
Chris@0 1669 (m_paneStack->getCurrentPane()->getSelectedLayer()));
Chris@0 1670 bool haveMainModel =
Chris@0 1671 (getMainModel() != 0);
Chris@0 1672 bool havePlayTarget =
Chris@0 1673 (m_playTarget != 0);
Chris@0 1674 bool haveSelection =
Chris@0 1675 (m_viewManager &&
Chris@0 1676 !m_viewManager->getSelections().empty());
Chris@0 1677 bool haveCurrentEditableLayer =
Chris@0 1678 (haveCurrentLayer &&
Chris@0 1679 m_paneStack->getCurrentPane()->getSelectedLayer()->
Chris@0 1680 isLayerEditable());
Chris@0 1681 bool haveCurrentTimeInstantsLayer =
Chris@0 1682 (haveCurrentLayer &&
Chris@0 1683 dynamic_cast<TimeInstantLayer *>
Chris@0 1684 (m_paneStack->getCurrentPane()->getSelectedLayer()));
Chris@0 1685 bool haveCurrentTimeValueLayer =
Chris@0 1686 (haveCurrentLayer &&
Chris@0 1687 dynamic_cast<TimeValueLayer *>
Chris@0 1688 (m_paneStack->getCurrentPane()->getSelectedLayer()));
Chris@0 1689 bool haveCurrentColour3DPlot =
Chris@0 1690 (haveCurrentLayer &&
Chris@0 1691 dynamic_cast<Colour3DPlotLayer *>
Chris@0 1692 (m_paneStack->getCurrentPane()->getSelectedLayer()));
Chris@0 1693 bool haveClipboardContents =
Chris@0 1694 (m_viewManager &&
Chris@0 1695 !m_viewManager->getClipboard().empty());
Chris@0 1696
Chris@0 1697 emit canAddPane(haveMainModel);
Chris@0 1698 emit canDeleteCurrentPane(haveCurrentPane);
Chris@0 1699 emit canZoom(haveMainModel && haveCurrentPane);
Chris@0 1700 emit canScroll(haveMainModel && haveCurrentPane);
Chris@0 1701 emit canAddLayer(haveMainModel && haveCurrentPane);
Chris@0 1702 emit canImportMoreAudio(haveMainModel);
Chris@0 1703 emit canImportLayer(haveMainModel && haveCurrentPane);
Chris@0 1704 emit canExportAudio(haveMainModel);
Chris@0 1705 emit canExportLayer(haveMainModel &&
Chris@0 1706 (haveCurrentEditableLayer || haveCurrentColour3DPlot));
Chris@0 1707 emit canDeleteCurrentLayer(haveCurrentLayer);
Chris@0 1708 emit canRenameLayer(haveCurrentLayer);
Chris@0 1709 emit canEditLayer(haveCurrentEditableLayer);
Chris@0 1710 emit canSelect(haveMainModel && haveCurrentPane);
Chris@0 1711 emit canPlay(/*!!! haveMainModel && */ havePlayTarget);
Chris@0 1712 emit canFfwd(haveCurrentTimeInstantsLayer || haveCurrentTimeValueLayer);
Chris@0 1713 emit canRewind(haveCurrentTimeInstantsLayer || haveCurrentTimeValueLayer);
Chris@0 1714 emit canPaste(haveCurrentEditableLayer && haveClipboardContents);
Chris@0 1715 emit canInsertInstant(haveCurrentPane);
Chris@81 1716 emit canInsertInstantsAtBoundaries(haveCurrentPane && haveSelection);
Chris@0 1717 emit canPlaySelection(haveMainModel && havePlayTarget && haveSelection);
Chris@0 1718 emit canClearSelection(haveSelection);
Chris@0 1719 emit canEditSelection(haveSelection && haveCurrentEditableLayer);
Chris@0 1720 emit canSave(m_sessionFile != "" && m_documentModified);
Chris@0 1721 }
Chris@0 1722
Chris@0 1723 void
Chris@0 1724 MainWindow::updateDescriptionLabel()
Chris@0 1725 {
Chris@0 1726 if (!getMainModel()) {
Chris@0 1727 m_descriptionLabel->setText(tr("No audio file loaded."));
Chris@0 1728 return;
Chris@0 1729 }
Chris@0 1730
Chris@0 1731 QString description;
Chris@0 1732
Chris@0 1733 size_t ssr = getMainModel()->getSampleRate();
Chris@0 1734 size_t tsr = ssr;
Chris@0 1735 if (m_playSource) tsr = m_playSource->getTargetSampleRate();
Chris@0 1736
Chris@0 1737 if (ssr != tsr) {
Chris@0 1738 description = tr("%1Hz (resampling to %2Hz)").arg(ssr).arg(tsr);
Chris@0 1739 } else {
Chris@0 1740 description = QString("%1Hz").arg(ssr);
Chris@0 1741 }
Chris@0 1742
Chris@0 1743 description = QString("%1 - %2")
Chris@0 1744 .arg(RealTime::frame2RealTime(getMainModel()->getEndFrame(), ssr)
Chris@0 1745 .toText(false).c_str())
Chris@0 1746 .arg(description);
Chris@0 1747
Chris@0 1748 m_descriptionLabel->setText(description);
Chris@0 1749 }
Chris@0 1750
Chris@0 1751 void
Chris@0 1752 MainWindow::documentModified()
Chris@0 1753 {
Chris@0 1754 // std::cerr << "MainWindow::documentModified" << std::endl;
Chris@0 1755
Chris@0 1756 if (!m_documentModified) {
Chris@0 1757 setWindowTitle(tr("%1 (modified)").arg(windowTitle()));
Chris@0 1758 }
Chris@0 1759
Chris@0 1760 m_documentModified = true;
Chris@0 1761 updateMenuStates();
Chris@0 1762 }
Chris@0 1763
Chris@0 1764 void
Chris@0 1765 MainWindow::documentRestored()
Chris@0 1766 {
Chris@0 1767 // std::cerr << "MainWindow::documentRestored" << std::endl;
Chris@0 1768
Chris@0 1769 if (m_documentModified) {
Chris@0 1770 QString wt(windowTitle());
Chris@0 1771 wt.replace(tr(" (modified)"), "");
Chris@0 1772 setWindowTitle(wt);
Chris@0 1773 }
Chris@0 1774
Chris@0 1775 m_documentModified = false;
Chris@0 1776 updateMenuStates();
Chris@0 1777 }
Chris@0 1778
Chris@0 1779 void
Chris@0 1780 MainWindow::playLoopToggled()
Chris@0 1781 {
Chris@0 1782 QAction *action = dynamic_cast<QAction *>(sender());
Chris@0 1783
Chris@0 1784 if (action) {
Chris@0 1785 m_viewManager->setPlayLoopMode(action->isChecked());
Chris@0 1786 } else {
Chris@0 1787 m_viewManager->setPlayLoopMode(!m_viewManager->getPlayLoopMode());
Chris@0 1788 }
Chris@0 1789 }
Chris@0 1790
Chris@0 1791 void
Chris@0 1792 MainWindow::playSelectionToggled()
Chris@0 1793 {
Chris@0 1794 QAction *action = dynamic_cast<QAction *>(sender());
Chris@0 1795
Chris@0 1796 if (action) {
Chris@0 1797 m_viewManager->setPlaySelectionMode(action->isChecked());
Chris@0 1798 } else {
Chris@0 1799 m_viewManager->setPlaySelectionMode(!m_viewManager->getPlaySelectionMode());
Chris@0 1800 }
Chris@0 1801 }
Chris@0 1802
Chris@0 1803 void
Chris@0 1804 MainWindow::currentPaneChanged(Pane *)
Chris@0 1805 {
Chris@0 1806 updateMenuStates();
Chris@0 1807 }
Chris@0 1808
Chris@0 1809 void
Chris@0 1810 MainWindow::currentLayerChanged(Pane *, Layer *)
Chris@0 1811 {
Chris@0 1812 updateMenuStates();
Chris@0 1813 }
Chris@0 1814
Chris@0 1815 void
Chris@0 1816 MainWindow::toolNavigateSelected()
Chris@0 1817 {
Chris@0 1818 m_viewManager->setToolMode(ViewManager::NavigateMode);
Chris@0 1819 }
Chris@0 1820
Chris@0 1821 void
Chris@0 1822 MainWindow::toolSelectSelected()
Chris@0 1823 {
Chris@0 1824 m_viewManager->setToolMode(ViewManager::SelectMode);
Chris@0 1825 }
Chris@0 1826
Chris@0 1827 void
Chris@0 1828 MainWindow::toolEditSelected()
Chris@0 1829 {
Chris@0 1830 m_viewManager->setToolMode(ViewManager::EditMode);
Chris@0 1831 }
Chris@0 1832
Chris@0 1833 void
Chris@0 1834 MainWindow::toolDrawSelected()
Chris@0 1835 {
Chris@0 1836 m_viewManager->setToolMode(ViewManager::DrawMode);
Chris@0 1837 }
Chris@0 1838
Chris@0 1839 //void
Chris@0 1840 //MainWindow::toolTextSelected()
Chris@0 1841 //{
Chris@0 1842 // m_viewManager->setToolMode(ViewManager::TextMode);
Chris@0 1843 //}
Chris@0 1844
Chris@0 1845 void
Chris@0 1846 MainWindow::selectAll()
Chris@0 1847 {
Chris@0 1848 if (!getMainModel()) return;
Chris@0 1849 m_viewManager->setSelection(Selection(getMainModel()->getStartFrame(),
Chris@0 1850 getMainModel()->getEndFrame()));
Chris@0 1851 }
Chris@0 1852
Chris@0 1853 void
Chris@0 1854 MainWindow::selectToStart()
Chris@0 1855 {
Chris@0 1856 if (!getMainModel()) return;
Chris@0 1857 m_viewManager->setSelection(Selection(getMainModel()->getStartFrame(),
Chris@0 1858 m_viewManager->getGlobalCentreFrame()));
Chris@0 1859 }
Chris@0 1860
Chris@0 1861 void
Chris@0 1862 MainWindow::selectToEnd()
Chris@0 1863 {
Chris@0 1864 if (!getMainModel()) return;
Chris@0 1865 m_viewManager->setSelection(Selection(m_viewManager->getGlobalCentreFrame(),
Chris@0 1866 getMainModel()->getEndFrame()));
Chris@0 1867 }
Chris@0 1868
Chris@0 1869 void
Chris@0 1870 MainWindow::selectVisible()
Chris@0 1871 {
Chris@0 1872 Model *model = getMainModel();
Chris@0 1873 if (!model) return;
Chris@0 1874
Chris@0 1875 Pane *currentPane = m_paneStack->getCurrentPane();
Chris@0 1876 if (!currentPane) return;
Chris@0 1877
Chris@0 1878 size_t startFrame, endFrame;
Chris@0 1879
Chris@0 1880 if (currentPane->getStartFrame() < 0) startFrame = 0;
Chris@0 1881 else startFrame = currentPane->getStartFrame();
Chris@0 1882
Chris@0 1883 if (currentPane->getEndFrame() > model->getEndFrame()) endFrame = model->getEndFrame();
Chris@0 1884 else endFrame = currentPane->getEndFrame();
Chris@0 1885
Chris@0 1886 m_viewManager->setSelection(Selection(startFrame, endFrame));
Chris@0 1887 }
Chris@0 1888
Chris@0 1889 void
Chris@0 1890 MainWindow::clearSelection()
Chris@0 1891 {
Chris@0 1892 m_viewManager->clearSelections();
Chris@0 1893 }
Chris@0 1894
Chris@0 1895 void
Chris@0 1896 MainWindow::cut()
Chris@0 1897 {
Chris@0 1898 Pane *currentPane = m_paneStack->getCurrentPane();
Chris@0 1899 if (!currentPane) return;
Chris@0 1900
Chris@0 1901 Layer *layer = currentPane->getSelectedLayer();
Chris@0 1902 if (!layer) return;
Chris@0 1903
Chris@0 1904 Clipboard &clipboard = m_viewManager->getClipboard();
Chris@0 1905 clipboard.clear();
Chris@0 1906
Chris@0 1907 MultiSelection::SelectionList selections = m_viewManager->getSelections();
Chris@0 1908
Chris@0 1909 CommandHistory::getInstance()->startCompoundOperation(tr("Cut"), true);
Chris@0 1910
Chris@0 1911 for (MultiSelection::SelectionList::iterator i = selections.begin();
Chris@0 1912 i != selections.end(); ++i) {
Chris@0 1913 layer->copy(*i, clipboard);
Chris@0 1914 layer->deleteSelection(*i);
Chris@0 1915 }
Chris@0 1916
Chris@0 1917 CommandHistory::getInstance()->endCompoundOperation();
Chris@0 1918 }
Chris@0 1919
Chris@0 1920 void
Chris@0 1921 MainWindow::copy()
Chris@0 1922 {
Chris@0 1923 Pane *currentPane = m_paneStack->getCurrentPane();
Chris@0 1924 if (!currentPane) return;
Chris@0 1925
Chris@0 1926 Layer *layer = currentPane->getSelectedLayer();
Chris@0 1927 if (!layer) return;
Chris@0 1928
Chris@0 1929 Clipboard &clipboard = m_viewManager->getClipboard();
Chris@0 1930 clipboard.clear();
Chris@0 1931
Chris@0 1932 MultiSelection::SelectionList selections = m_viewManager->getSelections();
Chris@0 1933
Chris@0 1934 for (MultiSelection::SelectionList::iterator i = selections.begin();
Chris@0 1935 i != selections.end(); ++i) {
Chris@0 1936 layer->copy(*i, clipboard);
Chris@0 1937 }
Chris@0 1938 }
Chris@0 1939
Chris@0 1940 void
Chris@0 1941 MainWindow::paste()
Chris@0 1942 {
Chris@0 1943 Pane *currentPane = m_paneStack->getCurrentPane();
Chris@0 1944 if (!currentPane) return;
Chris@0 1945
Chris@0 1946 //!!! if we have no current layer, we should create one of the most
Chris@0 1947 // appropriate type
Chris@0 1948
Chris@0 1949 Layer *layer = currentPane->getSelectedLayer();
Chris@0 1950 if (!layer) return;
Chris@0 1951
Chris@0 1952 Clipboard &clipboard = m_viewManager->getClipboard();
Chris@0 1953 Clipboard::PointList contents = clipboard.getPoints();
Chris@0 1954 /*
Chris@0 1955 long minFrame = 0;
Chris@0 1956 bool have = false;
Chris@0 1957 for (int i = 0; i < contents.size(); ++i) {
Chris@0 1958 if (!contents[i].haveFrame()) continue;
Chris@0 1959 if (!have || contents[i].getFrame() < minFrame) {
Chris@0 1960 minFrame = contents[i].getFrame();
Chris@0 1961 have = true;
Chris@0 1962 }
Chris@0 1963 }
Chris@0 1964
Chris@0 1965 long frameOffset = long(m_viewManager->getGlobalCentreFrame()) - minFrame;
Chris@0 1966
Chris@0 1967 layer->paste(clipboard, frameOffset);
Chris@0 1968 */
Chris@0 1969 layer->paste(clipboard, 0, true);
Chris@0 1970 }
Chris@0 1971
Chris@0 1972 void
Chris@0 1973 MainWindow::deleteSelected()
Chris@0 1974 {
Chris@0 1975 if (m_paneStack->getCurrentPane() &&
Chris@0 1976 m_paneStack->getCurrentPane()->getSelectedLayer()) {
Chris@0 1977
Chris@0 1978 MultiSelection::SelectionList selections =
Chris@0 1979 m_viewManager->getSelections();
Chris@0 1980
Chris@0 1981 for (MultiSelection::SelectionList::iterator i = selections.begin();
Chris@0 1982 i != selections.end(); ++i) {
Chris@0 1983
Chris@0 1984 m_paneStack->getCurrentPane()->getSelectedLayer()->deleteSelection(*i);
Chris@0 1985 }
Chris@0 1986 }
Chris@0 1987 }
Chris@0 1988
Chris@0 1989 void
Chris@0 1990 MainWindow::insertInstant()
Chris@0 1991 {
Chris@0 1992 int frame = m_viewManager->getPlaybackFrame();
Chris@81 1993 insertInstantAt(frame);
Chris@81 1994 }
Chris@81 1995
Chris@81 1996 void
Chris@81 1997 MainWindow::insertInstantsAtBoundaries()
Chris@81 1998 {
Chris@81 1999 MultiSelection::SelectionList selections = m_viewManager->getSelections();
Chris@81 2000 for (MultiSelection::SelectionList::iterator i = selections.begin();
Chris@81 2001 i != selections.end(); ++i) {
Chris@81 2002 size_t start = i->getStartFrame();
Chris@81 2003 size_t end = i->getEndFrame();
Chris@81 2004 if (start != end) {
Chris@81 2005 insertInstantAt(i->getStartFrame());
Chris@81 2006 insertInstantAt(i->getEndFrame());
Chris@81 2007 }
Chris@81 2008 }
Chris@81 2009 }
Chris@81 2010
Chris@81 2011 void
Chris@81 2012 MainWindow::insertInstantAt(size_t frame)
Chris@81 2013 {
Chris@0 2014 Pane *pane = m_paneStack->getCurrentPane();
Chris@0 2015 if (!pane) {
Chris@0 2016 return;
Chris@0 2017 }
Chris@0 2018
Chris@0 2019 Layer *layer = dynamic_cast<TimeInstantLayer *>
Chris@0 2020 (pane->getSelectedLayer());
Chris@0 2021
Chris@0 2022 if (!layer) {
Chris@0 2023 for (int i = pane->getLayerCount(); i > 0; --i) {
Chris@0 2024 layer = dynamic_cast<TimeInstantLayer *>(pane->getLayer(i - 1));
Chris@0 2025 if (layer) break;
Chris@0 2026 }
Chris@0 2027
Chris@0 2028 if (!layer) {
Chris@0 2029 CommandHistory::getInstance()->startCompoundOperation
Chris@0 2030 (tr("Add Point"), true);
Chris@0 2031 layer = m_document->createEmptyLayer(LayerFactory::TimeInstants);
Chris@0 2032 if (layer) {
Chris@0 2033 m_document->addLayerToView(pane, layer);
Chris@0 2034 m_paneStack->setCurrentLayer(pane, layer);
Chris@0 2035 }
Chris@0 2036 CommandHistory::getInstance()->endCompoundOperation();
Chris@0 2037 }
Chris@0 2038 }
Chris@0 2039
Chris@0 2040 if (layer) {
Chris@0 2041
Chris@0 2042 Model *model = layer->getModel();
Chris@0 2043 SparseOneDimensionalModel *sodm = dynamic_cast<SparseOneDimensionalModel *>
Chris@0 2044 (model);
Chris@0 2045
Chris@0 2046 if (sodm) {
Chris@0 2047 SparseOneDimensionalModel::Point point
Chris@0 2048 (frame, QString("%1").arg(sodm->getPointCount() + 1));
Chris@0 2049 CommandHistory::getInstance()->addCommand
Chris@0 2050 (new SparseOneDimensionalModel::AddPointCommand(sodm, point,
Chris@0 2051 tr("Add Points")),
Chris@0 2052 true, true); // bundled
Chris@0 2053 }
Chris@0 2054 }
Chris@0 2055 }
Chris@0 2056
Chris@0 2057 void
Chris@0 2058 MainWindow::importAudio()
Chris@0 2059 {
Chris@81 2060 QString path = getOpenFileName(AudioFile);
Chris@0 2061
Chris@0 2062 if (path != "") {
Chris@82 2063 if (openAudioFile(path, ReplaceMainModel) == FileOpenFailed) {
Chris@0 2064 QMessageBox::critical(this, tr("Failed to open file"),
Chris@0 2065 tr("Audio file \"%1\" could not be opened").arg(path));
Chris@0 2066 }
Chris@0 2067 }
Chris@0 2068 }
Chris@0 2069
Chris@0 2070 void
Chris@0 2071 MainWindow::importMoreAudio()
Chris@0 2072 {
Chris@81 2073 QString path = getOpenFileName(AudioFile);
Chris@0 2074
Chris@0 2075 if (path != "") {
Chris@82 2076 if (openAudioFile(path, CreateAdditionalModel) == FileOpenFailed) {
Chris@0 2077 QMessageBox::critical(this, tr("Failed to open file"),
Chris@0 2078 tr("Audio file \"%1\" could not be opened").arg(path));
Chris@0 2079 }
Chris@0 2080 }
Chris@0 2081 }
Chris@0 2082
Chris@0 2083 void
Chris@0 2084 MainWindow::exportAudio()
Chris@0 2085 {
Chris@0 2086 if (!getMainModel()) return;
Chris@0 2087
Chris@81 2088 QString path = getSaveFileName(AudioFile);
Chris@0 2089
Chris@0 2090 if (path == "") return;
Chris@0 2091
Chris@0 2092 bool ok = false;
Chris@0 2093 QString error;
Chris@0 2094
Chris@0 2095 MultiSelection ms = m_viewManager->getSelection();
Chris@0 2096 MultiSelection::SelectionList selections = m_viewManager->getSelections();
Chris@0 2097
Chris@0 2098 bool multiple = false;
Chris@0 2099
Chris@38 2100 MultiSelection *selectionToWrite = 0;
Chris@38 2101
Chris@38 2102 if (selections.size() == 1) {
Chris@0 2103
Chris@0 2104 QStringList items;
Chris@0 2105 items << tr("Export the selected region only")
Chris@0 2106 << tr("Export the whole audio file");
Chris@0 2107
Chris@0 2108 bool ok = false;
Chris@0 2109 QString item = ListInputDialog::getItem
Chris@0 2110 (this, tr("Select region to export"),
Chris@0 2111 tr("Which region from the original audio file do you want to export?"),
Chris@0 2112 items, 0, &ok);
Chris@0 2113
Chris@0 2114 if (!ok || item.isEmpty()) return;
Chris@0 2115
Chris@38 2116 if (item == items[0]) selectionToWrite = &ms;
Chris@38 2117
Chris@38 2118 } else if (selections.size() > 1) {
Chris@0 2119
Chris@0 2120 QStringList items;
Chris@0 2121 items << tr("Export the selected regions into a single audio file")
Chris@0 2122 << tr("Export the selected regions into separate files")
Chris@0 2123 << tr("Export the whole audio file");
Chris@0 2124
Chris@0 2125 QString item = ListInputDialog::getItem
Chris@0 2126 (this, tr("Select region to export"),
Chris@0 2127 tr("Multiple regions of the original audio file are selected.\nWhat do you want to export?"),
Chris@0 2128 items, 0, &ok);
Chris@0 2129
Chris@0 2130 if (!ok || item.isEmpty()) return;
Chris@0 2131
Chris@0 2132 if (item == items[0]) {
Chris@0 2133
Chris@38 2134 selectionToWrite = &ms;
Chris@38 2135
Chris@38 2136 } else if (item == items[1]) {
Chris@0 2137
Chris@0 2138 multiple = true;
Chris@0 2139
Chris@0 2140 int n = 1;
Chris@0 2141 QString base = path;
Chris@0 2142 base.replace(".wav", "");
Chris@0 2143
Chris@0 2144 for (MultiSelection::SelectionList::iterator i = selections.begin();
Chris@0 2145 i != selections.end(); ++i) {
Chris@0 2146
Chris@0 2147 MultiSelection subms;
Chris@0 2148 subms.setSelection(*i);
Chris@0 2149
Chris@0 2150 QString subpath = QString("%1.%2.wav").arg(base).arg(n);
Chris@0 2151 ++n;
Chris@0 2152
Chris@0 2153 if (QFileInfo(subpath).exists()) {
Chris@0 2154 error = tr("Fragment file %1 already exists, aborting").arg(subpath);
Chris@0 2155 break;
Chris@0 2156 }
Chris@0 2157
Chris@38 2158 WavFileWriter subwriter(subpath,
Chris@38 2159 getMainModel()->getSampleRate(),
Chris@38 2160 getMainModel()->getChannelCount());
Chris@38 2161 subwriter.writeModel(getMainModel(), &subms);
Chris@0 2162 ok = subwriter.isOK();
Chris@0 2163
Chris@0 2164 if (!ok) {
Chris@0 2165 error = subwriter.getError();
Chris@0 2166 break;
Chris@0 2167 }
Chris@0 2168 }
Chris@0 2169 }
Chris@0 2170 }
Chris@0 2171
Chris@38 2172 if (!multiple) {
Chris@38 2173 WavFileWriter writer(path,
Chris@38 2174 getMainModel()->getSampleRate(),
Chris@38 2175 getMainModel()->getChannelCount());
Chris@38 2176 writer.writeModel(getMainModel(), selectionToWrite);
Chris@38 2177 ok = writer.isOK();
Chris@38 2178 error = writer.getError();
Chris@0 2179 }
Chris@0 2180
Chris@0 2181 if (ok) {
Chris@0 2182 if (!multiple) {
Chris@34 2183 m_recentFiles.addFile(path);
Chris@0 2184 }
Chris@0 2185 } else {
Chris@0 2186 QMessageBox::critical(this, tr("Failed to write file"), error);
Chris@0 2187 }
Chris@0 2188 }
Chris@0 2189
Chris@0 2190 void
Chris@0 2191 MainWindow::importLayer()
Chris@0 2192 {
Chris@0 2193 Pane *pane = m_paneStack->getCurrentPane();
Chris@0 2194
Chris@0 2195 if (!pane) {
Chris@0 2196 // shouldn't happen, as the menu action should have been disabled
Chris@0 2197 std::cerr << "WARNING: MainWindow::importLayer: no current pane" << std::endl;
Chris@0 2198 return;
Chris@0 2199 }
Chris@0 2200
Chris@0 2201 if (!getMainModel()) {
Chris@0 2202 // shouldn't happen, as the menu action should have been disabled
Chris@0 2203 std::cerr << "WARNING: MainWindow::importLayer: No main model -- hence no default sample rate available" << std::endl;
Chris@0 2204 return;
Chris@0 2205 }
Chris@0 2206
Chris@81 2207 QString path = getOpenFileName(LayerFile);
Chris@0 2208
Chris@0 2209 if (path != "") {
Chris@0 2210
Chris@82 2211 if (openLayerFile(path) == FileOpenFailed) {
Chris@0 2212 QMessageBox::critical(this, tr("Failed to open file"),
Chris@0 2213 tr("File %1 could not be opened.").arg(path));
Chris@0 2214 return;
Chris@0 2215 }
Chris@0 2216 }
Chris@0 2217 }
Chris@0 2218
Chris@82 2219 MainWindow::FileOpenStatus
Chris@0 2220 MainWindow::openLayerFile(QString path)
Chris@0 2221 {
Chris@86 2222 return openLayerFile(path, path);
Chris@86 2223 }
Chris@86 2224
Chris@86 2225 MainWindow::FileOpenStatus
Chris@86 2226 MainWindow::openLayerFile(QString path, QString location)
Chris@86 2227 {
Chris@0 2228 Pane *pane = m_paneStack->getCurrentPane();
Chris@0 2229
Chris@0 2230 if (!pane) {
Chris@0 2231 // shouldn't happen, as the menu action should have been disabled
Chris@0 2232 std::cerr << "WARNING: MainWindow::openLayerFile: no current pane" << std::endl;
Chris@82 2233 return FileOpenFailed;
Chris@0 2234 }
Chris@0 2235
Chris@0 2236 if (!getMainModel()) {
Chris@0 2237 // shouldn't happen, as the menu action should have been disabled
Chris@0 2238 std::cerr << "WARNING: MainWindow::openLayerFile: No main model -- hence no default sample rate available" << std::endl;
Chris@82 2239 return FileOpenFailed;
Chris@0 2240 }
Chris@0 2241
Chris@86 2242 bool realFile = (location == path);
Chris@86 2243
Chris@0 2244 if (path.endsWith(".svl") || path.endsWith(".xml")) {
Chris@0 2245
Chris@0 2246 PaneCallback callback(this);
Chris@0 2247 QFile file(path);
Chris@0 2248
Chris@0 2249 if (!file.open(QIODevice::ReadOnly | QIODevice::Text)) {
Chris@0 2250 std::cerr << "ERROR: MainWindow::openLayerFile("
Chris@86 2251 << location.toStdString()
Chris@0 2252 << "): Failed to open file for reading" << std::endl;
Chris@82 2253 return FileOpenFailed;
Chris@0 2254 }
Chris@0 2255
Chris@0 2256 SVFileReader reader(m_document, callback);
Chris@0 2257 reader.setCurrentPane(pane);
Chris@0 2258
Chris@0 2259 QXmlInputSource inputSource(&file);
Chris@0 2260 reader.parse(inputSource);
Chris@0 2261
Chris@0 2262 if (!reader.isOK()) {
Chris@0 2263 std::cerr << "ERROR: MainWindow::openLayerFile("
Chris@86 2264 << location.toStdString()
Chris@0 2265 << "): Failed to read XML file: "
Chris@0 2266 << reader.getErrorString().toStdString() << std::endl;
Chris@82 2267 return FileOpenFailed;
Chris@0 2268 }
Chris@0 2269
Chris@86 2270 m_recentFiles.addFile(location);
Chris@86 2271
Chris@86 2272 if (realFile) {
Chris@86 2273 registerLastOpenedFilePath(LayerFile, path); // for file dialog
Chris@86 2274 }
Chris@86 2275
Chris@82 2276 return FileOpenSucceeded;
Chris@0 2277
Chris@0 2278 } else {
Chris@0 2279
Chris@0 2280 Model *model = DataFileReaderFactory::load(path, getMainModel()->getSampleRate());
Chris@0 2281
Chris@0 2282 if (model) {
Chris@86 2283
Chris@0 2284 Layer *newLayer = m_document->createImportedLayer(model);
Chris@86 2285
Chris@0 2286 if (newLayer) {
Chris@86 2287
Chris@0 2288 m_document->addLayerToView(pane, newLayer);
Chris@86 2289 m_recentFiles.addFile(location);
Chris@86 2290
Chris@86 2291 if (realFile) {
Chris@86 2292 registerLastOpenedFilePath(LayerFile, path); // for file dialog
Chris@86 2293 }
Chris@86 2294
Chris@82 2295 return FileOpenSucceeded;
Chris@0 2296 }
Chris@0 2297 }
Chris@0 2298 }
Chris@0 2299
Chris@82 2300 return FileOpenFailed;
Chris@0 2301 }
Chris@0 2302
Chris@0 2303 void
Chris@0 2304 MainWindow::exportLayer()
Chris@0 2305 {
Chris@0 2306 Pane *pane = m_paneStack->getCurrentPane();
Chris@0 2307 if (!pane) return;
Chris@0 2308
Chris@0 2309 Layer *layer = pane->getSelectedLayer();
Chris@0 2310 if (!layer) return;
Chris@0 2311
Chris@0 2312 Model *model = layer->getModel();
Chris@0 2313 if (!model) return;
Chris@0 2314
Chris@81 2315 QString path = getSaveFileName(LayerFile);
Chris@0 2316
Chris@0 2317 if (path == "") return;
Chris@0 2318
Chris@0 2319 if (QFileInfo(path).suffix() == "") path += ".svl";
Chris@0 2320
Chris@0 2321 QString error;
Chris@0 2322
Chris@0 2323 if (path.endsWith(".xml") || path.endsWith(".svl")) {
Chris@0 2324
Chris@0 2325 QFile file(path);
Chris@0 2326 if (!file.open(QIODevice::WriteOnly | QIODevice::Text)) {
Chris@0 2327 error = tr("Failed to open file %1 for writing").arg(path);
Chris@0 2328 } else {
Chris@0 2329 QTextStream out(&file);
Chris@0 2330 out << "<?xml version=\"1.0\" encoding=\"UTF-8\"?>\n"
Chris@0 2331 << "<!DOCTYPE sonic-visualiser>\n"
Chris@0 2332 << "<sv>\n"
Chris@0 2333 << " <data>\n";
Chris@0 2334
Chris@0 2335 model->toXml(out, " ");
Chris@0 2336
Chris@0 2337 out << " </data>\n"
Chris@0 2338 << " <display>\n";
Chris@0 2339
Chris@0 2340 layer->toXml(out, " ");
Chris@0 2341
Chris@0 2342 out << " </display>\n"
Chris@0 2343 << "</sv>\n";
Chris@0 2344 }
Chris@0 2345
Chris@0 2346 } else {
Chris@0 2347
Chris@0 2348 CSVFileWriter writer(path, model,
Chris@0 2349 (path.endsWith(".csv") ? "," : "\t"));
Chris@0 2350 writer.write();
Chris@0 2351
Chris@0 2352 if (!writer.isOK()) {
Chris@0 2353 error = writer.getError();
Chris@0 2354 }
Chris@0 2355 }
Chris@0 2356
Chris@0 2357 if (error != "") {
Chris@0 2358 QMessageBox::critical(this, tr("Failed to write file"), error);
Chris@0 2359 } else {
Chris@34 2360 m_recentFiles.addFile(path);
Chris@0 2361 }
Chris@0 2362 }
Chris@0 2363
Chris@82 2364 MainWindow::FileOpenStatus
Chris@0 2365 MainWindow::openAudioFile(QString path, AudioFileOpenMode mode)
Chris@0 2366 {
Chris@86 2367 return openAudioFile(path, path, mode);
Chris@86 2368 }
Chris@86 2369
Chris@86 2370 MainWindow::FileOpenStatus
Chris@86 2371 MainWindow::openAudioFile(QString path, QString location, AudioFileOpenMode mode)
Chris@86 2372 {
Chris@0 2373 if (!(QFileInfo(path).exists() &&
Chris@0 2374 QFileInfo(path).isFile() &&
Chris@0 2375 QFileInfo(path).isReadable())) {
Chris@82 2376 return FileOpenFailed;
Chris@0 2377 }
Chris@0 2378
Chris@70 2379 m_openingAudioFile = true;
Chris@70 2380
Chris@0 2381 WaveFileModel *newModel = new WaveFileModel(path);
Chris@0 2382
Chris@0 2383 if (!newModel->isOK()) {
Chris@0 2384 delete newModel;
Chris@70 2385 m_openingAudioFile = false;
Chris@82 2386 return FileOpenFailed;
Chris@0 2387 }
Chris@0 2388
Chris@0 2389 bool setAsMain = true;
Chris@0 2390 static bool prevSetAsMain = true;
Chris@0 2391
Chris@86 2392 bool realFile = (location == path);
Chris@86 2393
Chris@0 2394 if (mode == CreateAdditionalModel) setAsMain = false;
Chris@0 2395 else if (mode == AskUser) {
Chris@0 2396 if (m_document->getMainModel()) {
Chris@0 2397
Chris@0 2398 QStringList items;
Chris@0 2399 items << tr("Replace the existing main waveform")
Chris@0 2400 << tr("Load this file into a new waveform pane");
Chris@0 2401
Chris@0 2402 bool ok = false;
Chris@0 2403 QString item = ListInputDialog::getItem
Chris@0 2404 (this, tr("Select target for import"),
Chris@0 2405 tr("You already have an audio waveform loaded.\nWhat would you like to do with the new audio file?"),
Chris@0 2406 items, prevSetAsMain ? 0 : 1, &ok);
Chris@0 2407
Chris@0 2408 if (!ok || item.isEmpty()) {
Chris@0 2409 delete newModel;
Chris@70 2410 m_openingAudioFile = false;
Chris@82 2411 return FileOpenCancelled;
Chris@0 2412 }
Chris@0 2413
Chris@0 2414 setAsMain = (item == items[0]);
Chris@0 2415 prevSetAsMain = setAsMain;
Chris@0 2416 }
Chris@0 2417 }
Chris@0 2418
Chris@0 2419 if (setAsMain) {
Chris@0 2420
Chris@0 2421 Model *prevMain = getMainModel();
Chris@0 2422 if (prevMain) m_playSource->removeModel(prevMain);
Chris@0 2423
Chris@0 2424 PlayParameterRepository::getInstance()->clear();
Chris@0 2425
Chris@0 2426 // The clear() call will have removed the parameters for the
Chris@0 2427 // main model. Re-add them with the new one.
Chris@0 2428 PlayParameterRepository::getInstance()->addModel(newModel);
Chris@0 2429
Chris@0 2430 m_document->setMainModel(newModel);
Chris@0 2431 setupMenus();
Chris@0 2432
Chris@0 2433 if (m_sessionFile == "") {
Chris@0 2434 setWindowTitle(tr("Sonic Visualiser: %1")
Chris@86 2435 .arg(QFileInfo(location).fileName()));
Chris@0 2436 CommandHistory::getInstance()->clear();
Chris@0 2437 CommandHistory::getInstance()->documentSaved();
Chris@0 2438 m_documentModified = false;
Chris@0 2439 } else {
Chris@0 2440 setWindowTitle(tr("Sonic Visualiser: %1 [%2]")
Chris@0 2441 .arg(QFileInfo(m_sessionFile).fileName())
Chris@86 2442 .arg(QFileInfo(location).fileName()));
Chris@0 2443 if (m_documentModified) {
Chris@0 2444 m_documentModified = false;
Chris@0 2445 documentModified(); // so as to restore "(modified)" window title
Chris@0 2446 }
Chris@0 2447 }
Chris@0 2448
Chris@86 2449 if (realFile) m_audioFile = path;
Chris@0 2450
Chris@0 2451 } else { // !setAsMain
Chris@0 2452
Chris@0 2453 CommandHistory::getInstance()->startCompoundOperation
Chris@86 2454 (tr("Import \"%1\"").arg(QFileInfo(location).fileName()), true);
Chris@0 2455
Chris@0 2456 m_document->addImportedModel(newModel);
Chris@0 2457
Chris@0 2458 AddPaneCommand *command = new AddPaneCommand(this);
Chris@0 2459 CommandHistory::getInstance()->addCommand(command);
Chris@0 2460
Chris@0 2461 Pane *pane = command->getPane();
Chris@0 2462
Chris@0 2463 if (!m_timeRulerLayer) {
Chris@0 2464 m_timeRulerLayer = m_document->createMainModelLayer
Chris@0 2465 (LayerFactory::TimeRuler);
Chris@0 2466 }
Chris@0 2467
Chris@0 2468 m_document->addLayerToView(pane, m_timeRulerLayer);
Chris@0 2469
Chris@0 2470 Layer *newLayer = m_document->createImportedLayer(newModel);
Chris@0 2471
Chris@0 2472 if (newLayer) {
Chris@0 2473 m_document->addLayerToView(pane, newLayer);
Chris@0 2474 }
Chris@0 2475
Chris@0 2476 CommandHistory::getInstance()->endCompoundOperation();
Chris@0 2477 }
Chris@0 2478
Chris@0 2479 updateMenuStates();
Chris@86 2480 m_recentFiles.addFile(location);
Chris@86 2481 if (realFile) {
Chris@86 2482 registerLastOpenedFilePath(AudioFile, path); // for file dialog
Chris@86 2483 }
Chris@70 2484 m_openingAudioFile = false;
Chris@0 2485
Chris@82 2486 return FileOpenSucceeded;
Chris@0 2487 }
Chris@0 2488
Chris@0 2489 void
Chris@0 2490 MainWindow::createPlayTarget()
Chris@0 2491 {
Chris@0 2492 if (m_playTarget) return;
Chris@0 2493
Chris@0 2494 m_playTarget = AudioTargetFactory::createCallbackTarget(m_playSource);
Chris@0 2495 if (!m_playTarget) {
Chris@0 2496 QMessageBox::warning
Chris@0 2497 (this, tr("Couldn't open audio device"),
Chris@0 2498 tr("Could not open an audio device for playback.\nAudio playback will not be available during this session.\n"),
Chris@0 2499 QMessageBox::Ok, 0);
Chris@0 2500 }
Chris@0 2501 connect(m_fader, SIGNAL(valueChanged(float)),
Chris@0 2502 m_playTarget, SLOT(setOutputGain(float)));
Chris@0 2503 }
Chris@0 2504
Chris@0 2505 WaveFileModel *
Chris@0 2506 MainWindow::getMainModel()
Chris@0 2507 {
Chris@0 2508 if (!m_document) return 0;
Chris@0 2509 return m_document->getMainModel();
Chris@0 2510 }
Chris@0 2511
Chris@0 2512 void
Chris@0 2513 MainWindow::newSession()
Chris@0 2514 {
Chris@0 2515 if (!checkSaveModified()) return;
Chris@0 2516
Chris@0 2517 closeSession();
Chris@0 2518 createDocument();
Chris@0 2519
Chris@0 2520 Pane *pane = m_paneStack->addPane();
Chris@0 2521
Chris@0 2522 if (!m_timeRulerLayer) {
Chris@0 2523 m_timeRulerLayer = m_document->createMainModelLayer
Chris@0 2524 (LayerFactory::TimeRuler);
Chris@0 2525 }
Chris@0 2526
Chris@0 2527 m_document->addLayerToView(pane, m_timeRulerLayer);
Chris@0 2528
Chris@0 2529 Layer *waveform = m_document->createMainModelLayer(LayerFactory::Waveform);
Chris@0 2530 m_document->addLayerToView(pane, waveform);
Chris@0 2531
Chris@65 2532 m_overview->registerView(pane);
Chris@0 2533
Chris@0 2534 CommandHistory::getInstance()->clear();
Chris@0 2535 CommandHistory::getInstance()->documentSaved();
Chris@0 2536 documentRestored();
Chris@0 2537 updateMenuStates();
Chris@0 2538 }
Chris@0 2539
Chris@0 2540 void
Chris@0 2541 MainWindow::createDocument()
Chris@0 2542 {
Chris@0 2543 m_document = new Document;
Chris@0 2544
Chris@0 2545 connect(m_document, SIGNAL(layerAdded(Layer *)),
Chris@0 2546 this, SLOT(layerAdded(Layer *)));
Chris@0 2547 connect(m_document, SIGNAL(layerRemoved(Layer *)),
Chris@0 2548 this, SLOT(layerRemoved(Layer *)));
Chris@0 2549 connect(m_document, SIGNAL(layerAboutToBeDeleted(Layer *)),
Chris@0 2550 this, SLOT(layerAboutToBeDeleted(Layer *)));
Chris@0 2551 connect(m_document, SIGNAL(layerInAView(Layer *, bool)),
Chris@0 2552 this, SLOT(layerInAView(Layer *, bool)));
Chris@0 2553
Chris@0 2554 connect(m_document, SIGNAL(modelAdded(Model *)),
Chris@0 2555 this, SLOT(modelAdded(Model *)));
Chris@0 2556 connect(m_document, SIGNAL(mainModelChanged(WaveFileModel *)),
Chris@0 2557 this, SLOT(mainModelChanged(WaveFileModel *)));
Chris@0 2558 connect(m_document, SIGNAL(modelAboutToBeDeleted(Model *)),
Chris@0 2559 this, SLOT(modelAboutToBeDeleted(Model *)));
Chris@0 2560
Chris@0 2561 connect(m_document, SIGNAL(modelGenerationFailed(QString)),
Chris@0 2562 this, SLOT(modelGenerationFailed(QString)));
Chris@63 2563 connect(m_document, SIGNAL(modelRegenerationFailed(QString, QString)),
Chris@63 2564 this, SLOT(modelRegenerationFailed(QString, QString)));
Chris@0 2565 }
Chris@0 2566
Chris@0 2567 void
Chris@0 2568 MainWindow::closeSession()
Chris@0 2569 {
Chris@0 2570 if (!checkSaveModified()) return;
Chris@0 2571
Chris@0 2572 while (m_paneStack->getPaneCount() > 0) {
Chris@0 2573
Chris@0 2574 Pane *pane = m_paneStack->getPane(m_paneStack->getPaneCount() - 1);
Chris@0 2575
Chris@0 2576 while (pane->getLayerCount() > 0) {
Chris@0 2577 m_document->removeLayerFromView
Chris@0 2578 (pane, pane->getLayer(pane->getLayerCount() - 1));
Chris@0 2579 }
Chris@0 2580
Chris@65 2581 m_overview->unregisterView(pane);
Chris@0 2582 m_paneStack->deletePane(pane);
Chris@0 2583 }
Chris@0 2584
Chris@0 2585 while (m_paneStack->getHiddenPaneCount() > 0) {
Chris@0 2586
Chris@0 2587 Pane *pane = m_paneStack->getHiddenPane
Chris@0 2588 (m_paneStack->getHiddenPaneCount() - 1);
Chris@0 2589
Chris@0 2590 while (pane->getLayerCount() > 0) {
Chris@0 2591 m_document->removeLayerFromView
Chris@0 2592 (pane, pane->getLayer(pane->getLayerCount() - 1));
Chris@0 2593 }
Chris@0 2594
Chris@65 2595 m_overview->unregisterView(pane);
Chris@0 2596 m_paneStack->deletePane(pane);
Chris@0 2597 }
Chris@0 2598
Chris@0 2599 delete m_document;
Chris@0 2600 m_document = 0;
Chris@0 2601 m_viewManager->clearSelections();
Chris@0 2602 m_timeRulerLayer = 0; // document owned this
Chris@0 2603
Chris@0 2604 m_sessionFile = "";
Chris@0 2605 setWindowTitle(tr("Sonic Visualiser"));
Chris@0 2606
Chris@0 2607 CommandHistory::getInstance()->clear();
Chris@0 2608 CommandHistory::getInstance()->documentSaved();
Chris@0 2609 documentRestored();
Chris@0 2610 }
Chris@0 2611
Chris@0 2612 void
Chris@0 2613 MainWindow::openSession()
Chris@0 2614 {
Chris@0 2615 if (!checkSaveModified()) return;
Chris@0 2616
Chris@0 2617 QString orig = m_audioFile;
Chris@0 2618 if (orig == "") orig = ".";
Chris@0 2619 else orig = QFileInfo(orig).absoluteDir().canonicalPath();
Chris@0 2620
Chris@81 2621 QString path = getOpenFileName(SessionFile);
Chris@0 2622
Chris@0 2623 if (path.isEmpty()) return;
Chris@0 2624
Chris@82 2625 if (openSessionFile(path) == FileOpenFailed) {
Chris@0 2626 QMessageBox::critical(this, tr("Failed to open file"),
Chris@0 2627 tr("Session file \"%1\" could not be opened").arg(path));
Chris@0 2628 }
Chris@0 2629 }
Chris@0 2630
Chris@0 2631 void
Chris@0 2632 MainWindow::openSomething()
Chris@0 2633 {
Chris@0 2634 QString orig = m_audioFile;
Chris@0 2635 if (orig == "") orig = ".";
Chris@0 2636 else orig = QFileInfo(orig).absoluteDir().canonicalPath();
Chris@0 2637
Chris@0 2638 bool canImportLayer = (getMainModel() != 0 &&
Chris@0 2639 m_paneStack != 0 &&
Chris@0 2640 m_paneStack->getCurrentPane() != 0);
Chris@0 2641
Chris@81 2642 QString path = getOpenFileName(AnyFile);
Chris@0 2643
Chris@0 2644 if (path.isEmpty()) return;
Chris@0 2645
Chris@0 2646 if (path.endsWith(".sv")) {
Chris@0 2647
Chris@0 2648 if (!checkSaveModified()) return;
Chris@0 2649
Chris@82 2650 if (openSessionFile(path) == FileOpenFailed) {
Chris@0 2651 QMessageBox::critical(this, tr("Failed to open file"),
Chris@0 2652 tr("Session file \"%1\" could not be opened").arg(path));
Chris@0 2653 }
Chris@0 2654
Chris@0 2655 } else {
Chris@0 2656
Chris@82 2657 if (openAudioFile(path, AskUser) == FileOpenFailed) {
Chris@82 2658
Chris@82 2659 if (!canImportLayer || (openLayerFile(path) == FileOpenFailed)) {
Chris@0 2660
Chris@0 2661 QMessageBox::critical(this, tr("Failed to open file"),
Chris@0 2662 tr("File \"%1\" could not be opened").arg(path));
Chris@0 2663 }
Chris@0 2664 }
Chris@0 2665 }
Chris@0 2666 }
Chris@0 2667
Chris@0 2668 void
Chris@86 2669 MainWindow::openLocation()
Chris@86 2670 {
Chris@86 2671 bool ok = false;
Chris@86 2672 QString text = QInputDialog::getText
Chris@86 2673 (this, tr("Open Location"),
Chris@86 2674 tr("Please enter the URL of the location to open:"),
Chris@86 2675 QLineEdit::Normal, "", &ok);
Chris@86 2676 if (!ok || text.isEmpty()) return;
Chris@86 2677
Chris@86 2678 if (openURL(QUrl(text)) == FileOpenFailed) {
Chris@86 2679 QMessageBox::critical(this, tr("Failed to open location"),
Chris@86 2680 tr("URL \"%1\" could not be opened").arg(text));
Chris@86 2681 }
Chris@86 2682 }
Chris@86 2683
Chris@86 2684 void
Chris@0 2685 MainWindow::openRecentFile()
Chris@0 2686 {
Chris@0 2687 QObject *obj = sender();
Chris@0 2688 QAction *action = dynamic_cast<QAction *>(obj);
Chris@0 2689
Chris@0 2690 if (!action) {
Chris@0 2691 std::cerr << "WARNING: MainWindow::openRecentFile: sender is not an action"
Chris@0 2692 << std::endl;
Chris@0 2693 return;
Chris@0 2694 }
Chris@0 2695
Chris@0 2696 QString path = action->text();
Chris@0 2697 if (path == "") return;
Chris@0 2698
Chris@86 2699 QUrl url(path);
Chris@86 2700 if (RemoteFile::canHandleScheme(url)) {
Chris@86 2701 openURL(url);
Chris@86 2702 return;
Chris@86 2703 }
Chris@86 2704
Chris@0 2705 if (path.endsWith("sv")) {
Chris@0 2706
Chris@0 2707 if (!checkSaveModified()) return ;
Chris@0 2708
Chris@82 2709 if (openSessionFile(path) == FileOpenFailed) {
Chris@0 2710 QMessageBox::critical(this, tr("Failed to open file"),
Chris@0 2711 tr("Session file \"%1\" could not be opened").arg(path));
Chris@0 2712 }
Chris@0 2713
Chris@0 2714 } else {
Chris@0 2715
Chris@82 2716 if (openAudioFile(path, AskUser) == FileOpenFailed) {
Chris@0 2717
Chris@0 2718 bool canImportLayer = (getMainModel() != 0 &&
Chris@0 2719 m_paneStack != 0 &&
Chris@0 2720 m_paneStack->getCurrentPane() != 0);
Chris@0 2721
Chris@82 2722 if (!canImportLayer || (openLayerFile(path) == FileOpenFailed)) {
Chris@0 2723
Chris@0 2724 QMessageBox::critical(this, tr("Failed to open file"),
Chris@0 2725 tr("File \"%1\" could not be opened").arg(path));
Chris@0 2726 }
Chris@0 2727 }
Chris@0 2728 }
Chris@0 2729 }
Chris@0 2730
Chris@82 2731 MainWindow::FileOpenStatus
Chris@85 2732 MainWindow::openURL(QUrl url)
Chris@85 2733 {
Chris@85 2734 if (url.scheme().toLower() == "file") {
Chris@85 2735 return openSomeFile(url.toLocalFile());
Chris@86 2736 } else if (!RemoteFile::canHandleScheme(url)) {
Chris@85 2737 QMessageBox::critical(this, tr("Unsupported scheme in URL"),
Chris@85 2738 tr("The URL scheme \"%1\" is not supported")
Chris@85 2739 .arg(url.scheme()));
Chris@85 2740 return FileOpenFailed;
Chris@85 2741 } else {
Chris@85 2742 RemoteFile rf(url);
Chris@85 2743 rf.wait();
Chris@85 2744 if (!rf.isOK()) {
Chris@85 2745 //!!! need to clean up any partially downloaded file!
Chris@85 2746 QMessageBox::critical(this, tr("File download failed"),
Chris@85 2747 tr("Failed to download URL \"%1\": %2")
Chris@85 2748 .arg(url.toString()).arg(rf.getErrorString()));
Chris@85 2749 return FileOpenFailed;
Chris@85 2750 }
Chris@85 2751 //!!! and delete the file if we fail to open it here?
Chris@86 2752 return openSomeFile(rf.getLocalFilename(), url.toString());
Chris@85 2753 }
Chris@85 2754 }
Chris@85 2755
Chris@85 2756 MainWindow::FileOpenStatus
Chris@54 2757 MainWindow::openSomeFile(QString path, AudioFileOpenMode mode)
Chris@0 2758 {
Chris@86 2759 return openSomeFile(path, path, mode);
Chris@86 2760 }
Chris@86 2761
Chris@86 2762 MainWindow::FileOpenStatus
Chris@86 2763 MainWindow::openSomeFile(QString path, QString location,
Chris@86 2764 AudioFileOpenMode mode)
Chris@86 2765 {
Chris@82 2766 FileOpenStatus status;
Chris@82 2767
Chris@85 2768 bool canImportLayer = (getMainModel() != 0 &&
Chris@85 2769 m_paneStack != 0 &&
Chris@85 2770 m_paneStack->getCurrentPane() != 0);
Chris@85 2771
Chris@86 2772 if ((status = openAudioFile(path, location, mode)) != FileOpenFailed) {
Chris@82 2773 return status;
Chris@86 2774 } else if ((status = openSessionFile(path, location)) != FileOpenFailed) {
Chris@82 2775 return status;
Chris@85 2776 } else if (!canImportLayer) {
Chris@85 2777 return FileOpenFailed;
Chris@86 2778 } else if ((status = openLayerFile(path, location)) != FileOpenFailed) {
Chris@85 2779 return status;
Chris@0 2780 } else {
Chris@82 2781 return FileOpenFailed;
Chris@0 2782 }
Chris@0 2783 }
Chris@0 2784
Chris@82 2785 MainWindow::FileOpenStatus
Chris@0 2786 MainWindow::openSessionFile(QString path)
Chris@0 2787 {
Chris@86 2788 return openSessionFile(path, path);
Chris@86 2789 }
Chris@86 2790
Chris@86 2791 MainWindow::FileOpenStatus
Chris@86 2792 MainWindow::openSessionFile(QString path, QString location)
Chris@86 2793 {
Chris@0 2794 BZipFileDevice bzFile(path);
Chris@0 2795 if (!bzFile.open(QIODevice::ReadOnly)) {
Chris@86 2796 std::cerr << "Failed to open session file \"" << location.toStdString()
Chris@0 2797 << "\": " << bzFile.errorString().toStdString() << std::endl;
Chris@82 2798 return FileOpenFailed;
Chris@0 2799 }
Chris@0 2800
Chris@0 2801 QString error;
Chris@0 2802 closeSession();
Chris@0 2803 createDocument();
Chris@0 2804
Chris@0 2805 PaneCallback callback(this);
Chris@0 2806 m_viewManager->clearSelections();
Chris@0 2807
Chris@0 2808 SVFileReader reader(m_document, callback);
Chris@0 2809 QXmlInputSource inputSource(&bzFile);
Chris@0 2810 reader.parse(inputSource);
Chris@0 2811
Chris@0 2812 if (!reader.isOK()) {
Chris@0 2813 error = tr("SV XML file read error:\n%1").arg(reader.getErrorString());
Chris@0 2814 }
Chris@0 2815
Chris@0 2816 bzFile.close();
Chris@0 2817
Chris@0 2818 bool ok = (error == "");
Chris@86 2819
Chris@86 2820 bool realFile = (location == path);
Chris@0 2821
Chris@0 2822 if (ok) {
Chris@86 2823
Chris@0 2824 setWindowTitle(tr("Sonic Visualiser: %1")
Chris@86 2825 .arg(QFileInfo(location).fileName()));
Chris@86 2826
Chris@86 2827 if (realFile) m_sessionFile = path;
Chris@86 2828
Chris@0 2829 setupMenus();
Chris@0 2830 CommandHistory::getInstance()->clear();
Chris@0 2831 CommandHistory::getInstance()->documentSaved();
Chris@0 2832 m_documentModified = false;
Chris@0 2833 updateMenuStates();
Chris@86 2834
Chris@86 2835 m_recentFiles.addFile(location);
Chris@86 2836
Chris@86 2837 if (realFile) {
Chris@86 2838 registerLastOpenedFilePath(SessionFile, path); // for file dialog
Chris@86 2839 }
Chris@86 2840
Chris@0 2841 } else {
Chris@0 2842 setWindowTitle(tr("Sonic Visualiser"));
Chris@0 2843 }
Chris@0 2844
Chris@82 2845 return ok ? FileOpenSucceeded : FileOpenFailed;
Chris@0 2846 }
Chris@0 2847
Chris@0 2848 void
Chris@0 2849 MainWindow::closeEvent(QCloseEvent *e)
Chris@0 2850 {
Chris@70 2851 if (!m_abandoning && !checkSaveModified()) {
Chris@0 2852 e->ignore();
Chris@0 2853 return;
Chris@0 2854 }
Chris@0 2855
Chris@5 2856 QSettings settings;
Chris@5 2857 settings.beginGroup("MainWindow");
Chris@5 2858 settings.setValue("size", size());
Chris@5 2859 settings.setValue("position", pos());
Chris@5 2860 settings.endGroup();
Chris@5 2861
Chris@0 2862 e->accept();
Chris@0 2863 return;
Chris@0 2864 }
Chris@0 2865
Chris@0 2866 bool
Chris@11 2867 MainWindow::commitData(bool mayAskUser)
Chris@11 2868 {
Chris@11 2869 if (mayAskUser) {
Chris@11 2870 return checkSaveModified();
Chris@11 2871 } else {
Chris@11 2872 if (!m_documentModified) return true;
Chris@11 2873
Chris@11 2874 // If we can't check with the user first, then we can't save
Chris@11 2875 // to the original session file (even if we have it) -- have
Chris@11 2876 // to use a temporary file
Chris@11 2877
Chris@11 2878 QString svDirBase = ".sv1";
Chris@11 2879 QString svDir = QDir::home().filePath(svDirBase);
Chris@11 2880
Chris@11 2881 if (!QFileInfo(svDir).exists()) {
Chris@11 2882 if (!QDir::home().mkdir(svDirBase)) return false;
Chris@11 2883 } else {
Chris@11 2884 if (!QFileInfo(svDir).isDir()) return false;
Chris@11 2885 }
Chris@11 2886
Chris@11 2887 // This name doesn't have to be unguessable
Chris@11 2888
Chris@11 2889 QString fname = QString("tmp-%1-%2.sv")
Chris@11 2890 .arg(QDateTime::currentDateTime().toString("yyyyMMddhhmmsszzz"))
Chris@11 2891 .arg(QProcess().pid());
Chris@11 2892 QString fpath = QDir(svDir).filePath(fname);
Chris@11 2893 if (saveSessionFile(fpath)) {
Chris@34 2894 m_recentFiles.addFile(fpath);
Chris@11 2895 return true;
Chris@11 2896 } else {
Chris@11 2897 return false;
Chris@11 2898 }
Chris@11 2899 }
Chris@11 2900 }
Chris@11 2901
Chris@11 2902 bool
Chris@0 2903 MainWindow::checkSaveModified()
Chris@0 2904 {
Chris@0 2905 // Called before some destructive operation (e.g. new session,
Chris@0 2906 // exit program). Return true if we can safely proceed, false to
Chris@0 2907 // cancel.
Chris@0 2908
Chris@0 2909 if (!m_documentModified) return true;
Chris@0 2910
Chris@0 2911 int button =
Chris@0 2912 QMessageBox::warning(this,
Chris@0 2913 tr("Session modified"),
Chris@0 2914 tr("The current session has been modified.\nDo you want to save it?"),
Chris@0 2915 QMessageBox::Yes,
Chris@0 2916 QMessageBox::No,
Chris@0 2917 QMessageBox::Cancel);
Chris@0 2918
Chris@0 2919 if (button == QMessageBox::Yes) {
Chris@0 2920 saveSession();
Chris@0 2921 if (m_documentModified) { // save failed -- don't proceed!
Chris@0 2922 return false;
Chris@0 2923 } else {
Chris@0 2924 return true; // saved, so it's safe to continue now
Chris@0 2925 }
Chris@0 2926 } else if (button == QMessageBox::No) {
Chris@0 2927 m_documentModified = false; // so we know to abandon it
Chris@0 2928 return true;
Chris@0 2929 }
Chris@0 2930
Chris@0 2931 // else cancel
Chris@0 2932 return false;
Chris@0 2933 }
Chris@0 2934
Chris@0 2935 void
Chris@0 2936 MainWindow::saveSession()
Chris@0 2937 {
Chris@0 2938 if (m_sessionFile != "") {
Chris@0 2939 if (!saveSessionFile(m_sessionFile)) {
Chris@0 2940 QMessageBox::critical(this, tr("Failed to save file"),
Chris@0 2941 tr("Session file \"%1\" could not be saved.").arg(m_sessionFile));
Chris@0 2942 } else {
Chris@0 2943 CommandHistory::getInstance()->documentSaved();
Chris@0 2944 documentRestored();
Chris@0 2945 }
Chris@0 2946 } else {
Chris@0 2947 saveSessionAs();
Chris@0 2948 }
Chris@0 2949 }
Chris@0 2950
Chris@0 2951 void
Chris@0 2952 MainWindow::saveSessionAs()
Chris@0 2953 {
Chris@0 2954 QString orig = m_audioFile;
Chris@0 2955 if (orig == "") orig = ".";
Chris@0 2956 else orig = QFileInfo(orig).absoluteDir().canonicalPath();
Chris@0 2957
Chris@81 2958 QString path = getSaveFileName(SessionFile);
Chris@81 2959
Chris@81 2960 if (path == "") return;
Chris@0 2961
Chris@0 2962 if (!saveSessionFile(path)) {
Chris@0 2963 QMessageBox::critical(this, tr("Failed to save file"),
Chris@79 2964 tr("Session file \"%1\" could not be saved.").arg(path));
Chris@0 2965 } else {
Chris@0 2966 setWindowTitle(tr("Sonic Visualiser: %1")
Chris@0 2967 .arg(QFileInfo(path).fileName()));
Chris@0 2968 m_sessionFile = path;
Chris@0 2969 CommandHistory::getInstance()->documentSaved();
Chris@0 2970 documentRestored();
Chris@34 2971 m_recentFiles.addFile(path);
Chris@0 2972 }
Chris@0 2973 }
Chris@0 2974
Chris@0 2975 bool
Chris@0 2976 MainWindow::saveSessionFile(QString path)
Chris@0 2977 {
Chris@0 2978 BZipFileDevice bzFile(path);
Chris@0 2979 if (!bzFile.open(QIODevice::WriteOnly)) {
Chris@0 2980 std::cerr << "Failed to open session file \"" << path.toStdString()
Chris@0 2981 << "\" for writing: "
Chris@0 2982 << bzFile.errorString().toStdString() << std::endl;
Chris@0 2983 return false;
Chris@0 2984 }
Chris@0 2985
Chris@0 2986 QApplication::setOverrideCursor(QCursor(Qt::WaitCursor));
Chris@0 2987
Chris@0 2988 QTextStream out(&bzFile);
Chris@0 2989 toXml(out);
Chris@0 2990 out.flush();
Chris@0 2991
Chris@0 2992 QApplication::restoreOverrideCursor();
Chris@0 2993
Chris@84 2994 if (!bzFile.isOK()) {
Chris@0 2995 QMessageBox::critical(this, tr("Failed to write file"),
Chris@0 2996 tr("Failed to write to file \"%1\": %2")
Chris@0 2997 .arg(path).arg(bzFile.errorString()));
Chris@0 2998 bzFile.close();
Chris@0 2999 return false;
Chris@0 3000 }
Chris@0 3001
Chris@0 3002 bzFile.close();
Chris@0 3003 return true;
Chris@0 3004 }
Chris@0 3005
Chris@0 3006 void
Chris@0 3007 MainWindow::toXml(QTextStream &out)
Chris@0 3008 {
Chris@0 3009 QString indent(" ");
Chris@0 3010
Chris@0 3011 out << "<?xml version=\"1.0\" encoding=\"UTF-8\"?>\n";
Chris@0 3012 out << "<!DOCTYPE sonic-visualiser>\n";
Chris@0 3013 out << "<sv>\n";
Chris@0 3014
Chris@0 3015 m_document->toXml(out, "", "");
Chris@0 3016
Chris@0 3017 out << "<display>\n";
Chris@0 3018
Chris@0 3019 out << QString(" <window width=\"%1\" height=\"%2\"/>\n")
Chris@0 3020 .arg(width()).arg(height());
Chris@0 3021
Chris@0 3022 for (int i = 0; i < m_paneStack->getPaneCount(); ++i) {
Chris@0 3023
Chris@0 3024 Pane *pane = m_paneStack->getPane(i);
Chris@0 3025
Chris@0 3026 if (pane) {
Chris@0 3027 pane->toXml(out, indent);
Chris@0 3028 }
Chris@0 3029 }
Chris@0 3030
Chris@0 3031 out << "</display>\n";
Chris@0 3032
Chris@0 3033 m_viewManager->getSelection().toXml(out);
Chris@0 3034
Chris@0 3035 out << "</sv>\n";
Chris@0 3036 }
Chris@0 3037
Chris@0 3038 Pane *
Chris@0 3039 MainWindow::addPaneToStack()
Chris@0 3040 {
Chris@0 3041 AddPaneCommand *command = new AddPaneCommand(this);
Chris@0 3042 CommandHistory::getInstance()->addCommand(command);
Chris@0 3043 return command->getPane();
Chris@0 3044 }
Chris@0 3045
Chris@0 3046 void
Chris@0 3047 MainWindow::zoomIn()
Chris@0 3048 {
Chris@0 3049 Pane *currentPane = m_paneStack->getCurrentPane();
Chris@0 3050 if (currentPane) currentPane->zoom(true);
Chris@0 3051 }
Chris@0 3052
Chris@0 3053 void
Chris@0 3054 MainWindow::zoomOut()
Chris@0 3055 {
Chris@0 3056 Pane *currentPane = m_paneStack->getCurrentPane();
Chris@0 3057 if (currentPane) currentPane->zoom(false);
Chris@0 3058 }
Chris@0 3059
Chris@0 3060 void
Chris@0 3061 MainWindow::zoomToFit()
Chris@0 3062 {
Chris@0 3063 Pane *currentPane = m_paneStack->getCurrentPane();
Chris@0 3064 if (!currentPane) return;
Chris@0 3065
Chris@0 3066 Model *model = getMainModel();
Chris@0 3067 if (!model) return;
Chris@0 3068
Chris@0 3069 size_t start = model->getStartFrame();
Chris@0 3070 size_t end = model->getEndFrame();
Chris@0 3071 size_t pixels = currentPane->width();
Chris@0 3072 size_t zoomLevel = (end - start) / pixels;
Chris@0 3073
Chris@0 3074 currentPane->setZoomLevel(zoomLevel);
Chris@0 3075 currentPane->setStartFrame(start);
Chris@0 3076 }
Chris@0 3077
Chris@0 3078 void
Chris@0 3079 MainWindow::zoomDefault()
Chris@0 3080 {
Chris@0 3081 Pane *currentPane = m_paneStack->getCurrentPane();
Chris@0 3082 if (currentPane) currentPane->setZoomLevel(1024);
Chris@0 3083 }
Chris@0 3084
Chris@0 3085 void
Chris@0 3086 MainWindow::scrollLeft()
Chris@0 3087 {
Chris@0 3088 Pane *currentPane = m_paneStack->getCurrentPane();
Chris@0 3089 if (currentPane) currentPane->scroll(false, false);
Chris@0 3090 }
Chris@0 3091
Chris@0 3092 void
Chris@0 3093 MainWindow::jumpLeft()
Chris@0 3094 {
Chris@0 3095 Pane *currentPane = m_paneStack->getCurrentPane();
Chris@0 3096 if (currentPane) currentPane->scroll(false, true);
Chris@0 3097 }
Chris@0 3098
Chris@0 3099 void
Chris@0 3100 MainWindow::scrollRight()
Chris@0 3101 {
Chris@0 3102 Pane *currentPane = m_paneStack->getCurrentPane();
Chris@0 3103 if (currentPane) currentPane->scroll(true, false);
Chris@0 3104 }
Chris@0 3105
Chris@0 3106 void
Chris@0 3107 MainWindow::jumpRight()
Chris@0 3108 {
Chris@0 3109 Pane *currentPane = m_paneStack->getCurrentPane();
Chris@0 3110 if (currentPane) currentPane->scroll(true, true);
Chris@0 3111 }
Chris@0 3112
Chris@0 3113 void
Chris@0 3114 MainWindow::showNoOverlays()
Chris@0 3115 {
Chris@0 3116 m_viewManager->setOverlayMode(ViewManager::NoOverlays);
Chris@0 3117 }
Chris@0 3118
Chris@0 3119 void
Chris@0 3120 MainWindow::showBasicOverlays()
Chris@0 3121 {
Chris@0 3122 m_viewManager->setOverlayMode(ViewManager::BasicOverlays);
Chris@0 3123 }
Chris@0 3124
Chris@0 3125 void
Chris@7 3126 MainWindow::showAllTextOverlays()
Chris@0 3127 {
Chris@0 3128 m_viewManager->setOverlayMode(ViewManager::AllOverlays);
Chris@0 3129 }
Chris@0 3130
Chris@0 3131 void
Chris@7 3132 MainWindow::toggleZoomWheels()
Chris@7 3133 {
Chris@7 3134 if (m_viewManager->getZoomWheelsEnabled()) {
Chris@7 3135 m_viewManager->setZoomWheelsEnabled(false);
Chris@7 3136 } else {
Chris@7 3137 m_viewManager->setZoomWheelsEnabled(true);
Chris@7 3138 }
Chris@7 3139 }
Chris@7 3140
Chris@7 3141 void
Chris@72 3142 MainWindow::togglePropertyBoxes()
Chris@72 3143 {
Chris@72 3144 if (m_paneStack->getLayoutStyle() == PaneStack::NoPropertyStacks) {
Chris@72 3145 if (Preferences::getInstance()->getPropertyBoxLayout() ==
Chris@72 3146 Preferences::VerticallyStacked) {
Chris@72 3147 m_paneStack->setLayoutStyle(PaneStack::PropertyStackPerPaneLayout);
Chris@72 3148 } else {
Chris@72 3149 m_paneStack->setLayoutStyle(PaneStack::SinglePropertyStackLayout);
Chris@72 3150 }
Chris@72 3151 } else {
Chris@72 3152 m_paneStack->setLayoutStyle(PaneStack::NoPropertyStacks);
Chris@72 3153 }
Chris@72 3154 }
Chris@72 3155
Chris@72 3156 void
Chris@72 3157 MainWindow::preferenceChanged(PropertyContainer::PropertyName name)
Chris@72 3158 {
Chris@72 3159 if (name == "Property Box Layout") {
Chris@72 3160 if (m_paneStack->getLayoutStyle() != PaneStack::NoPropertyStacks) {
Chris@72 3161 if (Preferences::getInstance()->getPropertyBoxLayout() ==
Chris@72 3162 Preferences::VerticallyStacked) {
Chris@72 3163 m_paneStack->setLayoutStyle(PaneStack::PropertyStackPerPaneLayout);
Chris@72 3164 } else {
Chris@72 3165 m_paneStack->setLayoutStyle(PaneStack::SinglePropertyStackLayout);
Chris@72 3166 }
Chris@72 3167 }
Chris@72 3168 }
Chris@72 3169 }
Chris@72 3170
Chris@72 3171 void
Chris@0 3172 MainWindow::play()
Chris@0 3173 {
Chris@0 3174 if (m_playSource->isPlaying()) {
Chris@0 3175 m_playSource->stop();
Chris@0 3176 } else {
Chris@0 3177 m_playSource->play(m_viewManager->getPlaybackFrame());
Chris@0 3178 }
Chris@0 3179 }
Chris@0 3180
Chris@0 3181 void
Chris@0 3182 MainWindow::ffwd()
Chris@0 3183 {
Chris@0 3184 if (!getMainModel()) return;
Chris@0 3185
Chris@0 3186 int frame = m_viewManager->getPlaybackFrame();
Chris@0 3187 ++frame;
Chris@0 3188
Chris@0 3189 Pane *pane = m_paneStack->getCurrentPane();
Chris@0 3190 if (!pane) return;
Chris@0 3191
Chris@0 3192 Layer *layer = pane->getSelectedLayer();
Chris@0 3193
Chris@0 3194 if (!dynamic_cast<TimeInstantLayer *>(layer) &&
Chris@0 3195 !dynamic_cast<TimeValueLayer *>(layer)) return;
Chris@0 3196
Chris@0 3197 size_t resolution = 0;
Chris@0 3198 if (!layer->snapToFeatureFrame(pane, frame, resolution, Layer::SnapRight)) {
Chris@0 3199 frame = getMainModel()->getEndFrame();
Chris@0 3200 }
Chris@0 3201
Chris@0 3202 m_viewManager->setPlaybackFrame(frame);
Chris@0 3203 }
Chris@0 3204
Chris@0 3205 void
Chris@0 3206 MainWindow::ffwdEnd()
Chris@0 3207 {
Chris@0 3208 if (!getMainModel()) return;
Chris@0 3209 m_viewManager->setPlaybackFrame(getMainModel()->getEndFrame());
Chris@0 3210 }
Chris@0 3211
Chris@0 3212 void
Chris@0 3213 MainWindow::rewind()
Chris@0 3214 {
Chris@0 3215 if (!getMainModel()) return;
Chris@0 3216
Chris@0 3217 int frame = m_viewManager->getPlaybackFrame();
Chris@0 3218 if (frame > 0) --frame;
Chris@0 3219
Chris@0 3220 Pane *pane = m_paneStack->getCurrentPane();
Chris@0 3221 if (!pane) return;
Chris@0 3222
Chris@0 3223 Layer *layer = pane->getSelectedLayer();
Chris@0 3224
Chris@0 3225 if (!dynamic_cast<TimeInstantLayer *>(layer) &&
Chris@0 3226 !dynamic_cast<TimeValueLayer *>(layer)) return;
Chris@0 3227
Chris@0 3228 size_t resolution = 0;
Chris@0 3229 if (!layer->snapToFeatureFrame(pane, frame, resolution, Layer::SnapLeft)) {
Chris@0 3230 frame = getMainModel()->getEndFrame();
Chris@0 3231 }
Chris@0 3232
Chris@0 3233 m_viewManager->setPlaybackFrame(frame);
Chris@0 3234 }
Chris@0 3235
Chris@0 3236 void
Chris@0 3237 MainWindow::rewindStart()
Chris@0 3238 {
Chris@0 3239 if (!getMainModel()) return;
Chris@0 3240 m_viewManager->setPlaybackFrame(getMainModel()->getStartFrame());
Chris@0 3241 }
Chris@0 3242
Chris@0 3243 void
Chris@0 3244 MainWindow::stop()
Chris@0 3245 {
Chris@0 3246 m_playSource->stop();
Chris@0 3247 }
Chris@0 3248
Chris@0 3249 void
Chris@0 3250 MainWindow::addPane()
Chris@0 3251 {
Chris@0 3252 QObject *s = sender();
Chris@0 3253 QAction *action = dynamic_cast<QAction *>(s);
Chris@0 3254
Chris@0 3255 if (!action) {
Chris@0 3256 std::cerr << "WARNING: MainWindow::addPane: sender is not an action"
Chris@0 3257 << std::endl;
Chris@0 3258 return;
Chris@0 3259 }
Chris@0 3260
Chris@0 3261 PaneActionMap::iterator i = m_paneActions.find(action);
Chris@0 3262
Chris@0 3263 if (i == m_paneActions.end()) {
Chris@0 3264 std::cerr << "WARNING: MainWindow::addPane: unknown action "
Chris@0 3265 << action->objectName().toStdString() << std::endl;
Chris@0 3266 return;
Chris@0 3267 }
Chris@0 3268
Chris@69 3269 addPane(i->second, action->text());
Chris@69 3270 }
Chris@69 3271
Chris@69 3272 void
Chris@69 3273 MainWindow::addPane(const PaneConfiguration &configuration, QString text)
Chris@69 3274 {
Chris@69 3275 CommandHistory::getInstance()->startCompoundOperation(text, true);
Chris@0 3276
Chris@0 3277 AddPaneCommand *command = new AddPaneCommand(this);
Chris@0 3278 CommandHistory::getInstance()->addCommand(command);
Chris@0 3279
Chris@0 3280 Pane *pane = command->getPane();
Chris@0 3281
Chris@69 3282 if (configuration.layer == LayerFactory::Spectrum) {
Chris@7 3283 pane->setPlaybackFollow(View::PlaybackScrollContinuous);
Chris@7 3284 }
Chris@7 3285
Chris@69 3286 if (configuration.layer != LayerFactory::TimeRuler &&
Chris@69 3287 configuration.layer != LayerFactory::Spectrum) {
Chris@8 3288
Chris@0 3289 if (!m_timeRulerLayer) {
Chris@0 3290 // std::cerr << "no time ruler layer, creating one" << std::endl;
Chris@0 3291 m_timeRulerLayer = m_document->createMainModelLayer
Chris@0 3292 (LayerFactory::TimeRuler);
Chris@0 3293 }
Chris@0 3294
Chris@0 3295 // std::cerr << "adding time ruler layer " << m_timeRulerLayer << std::endl;
Chris@0 3296
Chris@0 3297 m_document->addLayerToView(pane, m_timeRulerLayer);
Chris@0 3298 }
Chris@0 3299
Chris@69 3300 Layer *newLayer = m_document->createLayer(configuration.layer);
Chris@69 3301
Chris@69 3302 Model *suggestedModel = configuration.sourceModel;
Chris@66 3303 Model *model = 0;
Chris@66 3304
Chris@66 3305 if (suggestedModel) {
Chris@66 3306
Chris@66 3307 // check its validity
Chris@66 3308 std::vector<Model *> inputModels = m_document->getTransformInputModels();
Chris@66 3309 for (size_t j = 0; j < inputModels.size(); ++j) {
Chris@66 3310 if (inputModels[j] == suggestedModel) {
Chris@66 3311 model = suggestedModel;
Chris@66 3312 break;
Chris@66 3313 }
Chris@66 3314 }
Chris@66 3315
Chris@66 3316 if (!model) {
Chris@66 3317 std::cerr << "WARNING: Model " << (void *)suggestedModel
Chris@66 3318 << " appears in pane action map, but is not reported "
Chris@66 3319 << "by document as a valid transform source" << std::endl;
Chris@66 3320 }
Chris@66 3321 }
Chris@66 3322
Chris@66 3323 if (!model) model = m_document->getMainModel();
Chris@66 3324
Chris@66 3325 m_document->setModel(newLayer, model);
Chris@66 3326
Chris@69 3327 m_document->setChannel(newLayer, configuration.channel);
Chris@0 3328 m_document->addLayerToView(pane, newLayer);
Chris@0 3329
Chris@0 3330 m_paneStack->setCurrentPane(pane);
Chris@70 3331 m_paneStack->setCurrentLayer(pane, newLayer);
Chris@0 3332
Chris@73 3333 std::cerr << "MainWindow::addPane: global centre frame is "
Chris@73 3334 << m_viewManager->getGlobalCentreFrame() << std::endl;
Chris@73 3335 pane->setCentreFrame(m_viewManager->getGlobalCentreFrame());
Chris@73 3336
Chris@0 3337 CommandHistory::getInstance()->endCompoundOperation();
Chris@0 3338
Chris@0 3339 updateMenuStates();
Chris@0 3340 }
Chris@0 3341
Chris@0 3342 MainWindow::AddPaneCommand::AddPaneCommand(MainWindow *mw) :
Chris@0 3343 m_mw(mw),
Chris@0 3344 m_pane(0),
Chris@0 3345 m_prevCurrentPane(0),
Chris@0 3346 m_added(false)
Chris@0 3347 {
Chris@0 3348 }
Chris@0 3349
Chris@0 3350 MainWindow::AddPaneCommand::~AddPaneCommand()
Chris@0 3351 {
Chris@0 3352 if (m_pane && !m_added) {
Chris@0 3353 m_mw->m_paneStack->deletePane(m_pane);
Chris@0 3354 }
Chris@0 3355 }
Chris@0 3356
Chris@0 3357 QString
Chris@0 3358 MainWindow::AddPaneCommand::getName() const
Chris@0 3359 {
Chris@0 3360 return tr("Add Pane");
Chris@0 3361 }
Chris@0 3362
Chris@0 3363 void
Chris@0 3364 MainWindow::AddPaneCommand::execute()
Chris@0 3365 {
Chris@0 3366 if (!m_pane) {
Chris@0 3367 m_prevCurrentPane = m_mw->m_paneStack->getCurrentPane();
Chris@0 3368 m_pane = m_mw->m_paneStack->addPane();
Chris@0 3369 } else {
Chris@0 3370 m_mw->m_paneStack->showPane(m_pane);
Chris@0 3371 }
Chris@0 3372
Chris@0 3373 m_mw->m_paneStack->setCurrentPane(m_pane);
Chris@65 3374 m_mw->m_overview->registerView(m_pane);
Chris@0 3375 m_added = true;
Chris@0 3376 }
Chris@0 3377
Chris@0 3378 void
Chris@0 3379 MainWindow::AddPaneCommand::unexecute()
Chris@0 3380 {
Chris@0 3381 m_mw->m_paneStack->hidePane(m_pane);
Chris@0 3382 m_mw->m_paneStack->setCurrentPane(m_prevCurrentPane);
Chris@65 3383 m_mw->m_overview->unregisterView(m_pane);
Chris@0 3384 m_added = false;
Chris@0 3385 }
Chris@0 3386
Chris@0 3387 MainWindow::RemovePaneCommand::RemovePaneCommand(MainWindow *mw, Pane *pane) :
Chris@0 3388 m_mw(mw),
Chris@0 3389 m_pane(pane),
Chris@0 3390 m_added(true)
Chris@0 3391 {
Chris@0 3392 }
Chris@0 3393
Chris@0 3394 MainWindow::RemovePaneCommand::~RemovePaneCommand()
Chris@0 3395 {
Chris@0 3396 if (m_pane && !m_added) {
Chris@0 3397 m_mw->m_paneStack->deletePane(m_pane);
Chris@0 3398 }
Chris@0 3399 }
Chris@0 3400
Chris@0 3401 QString
Chris@0 3402 MainWindow::RemovePaneCommand::getName() const
Chris@0 3403 {
Chris@0 3404 return tr("Remove Pane");
Chris@0 3405 }
Chris@0 3406
Chris@0 3407 void
Chris@0 3408 MainWindow::RemovePaneCommand::execute()
Chris@0 3409 {
Chris@0 3410 m_prevCurrentPane = m_mw->m_paneStack->getCurrentPane();
Chris@0 3411 m_mw->m_paneStack->hidePane(m_pane);
Chris@65 3412 m_mw->m_overview->unregisterView(m_pane);
Chris@0 3413 m_added = false;
Chris@0 3414 }
Chris@0 3415
Chris@0 3416 void
Chris@0 3417 MainWindow::RemovePaneCommand::unexecute()
Chris@0 3418 {
Chris@0 3419 m_mw->m_paneStack->showPane(m_pane);
Chris@0 3420 m_mw->m_paneStack->setCurrentPane(m_prevCurrentPane);
Chris@65 3421 m_mw->m_overview->registerView(m_pane);
Chris@0 3422 m_added = true;
Chris@0 3423 }
Chris@0 3424
Chris@0 3425 void
Chris@0 3426 MainWindow::addLayer()
Chris@0 3427 {
Chris@0 3428 QObject *s = sender();
Chris@0 3429 QAction *action = dynamic_cast<QAction *>(s);
Chris@0 3430
Chris@0 3431 if (!action) {
Chris@0 3432 std::cerr << "WARNING: MainWindow::addLayer: sender is not an action"
Chris@0 3433 << std::endl;
Chris@0 3434 return;
Chris@0 3435 }
Chris@0 3436
Chris@0 3437 Pane *pane = m_paneStack->getCurrentPane();
Chris@0 3438
Chris@0 3439 if (!pane) {
Chris@0 3440 std::cerr << "WARNING: MainWindow::addLayer: no current pane" << std::endl;
Chris@0 3441 return;
Chris@0 3442 }
Chris@0 3443
Chris@0 3444 ExistingLayerActionMap::iterator ei = m_existingLayerActions.find(action);
Chris@0 3445
Chris@0 3446 if (ei != m_existingLayerActions.end()) {
Chris@0 3447 Layer *newLayer = ei->second;
Chris@0 3448 m_document->addLayerToView(pane, newLayer);
Chris@0 3449 m_paneStack->setCurrentLayer(pane, newLayer);
Chris@0 3450 return;
Chris@0 3451 }
Chris@0 3452
Chris@34 3453 TransformActionMap::iterator i = m_transformActions.find(action);
Chris@34 3454
Chris@34 3455 if (i == m_transformActions.end()) {
Chris@0 3456
Chris@0 3457 LayerActionMap::iterator i = m_layerActions.find(action);
Chris@0 3458
Chris@0 3459 if (i == m_layerActions.end()) {
Chris@0 3460 std::cerr << "WARNING: MainWindow::addLayer: unknown action "
Chris@0 3461 << action->objectName().toStdString() << std::endl;
Chris@0 3462 return;
Chris@0 3463 }
Chris@0 3464
Chris@0 3465 LayerFactory::LayerType type = i->second;
Chris@0 3466
Chris@0 3467 LayerFactory::LayerTypeSet emptyTypes =
Chris@0 3468 LayerFactory::getInstance()->getValidEmptyLayerTypes();
Chris@0 3469
Chris@0 3470 Layer *newLayer;
Chris@0 3471
Chris@0 3472 if (emptyTypes.find(type) != emptyTypes.end()) {
Chris@0 3473
Chris@0 3474 newLayer = m_document->createEmptyLayer(type);
Chris@0 3475 m_toolActions[ViewManager::DrawMode]->trigger();
Chris@0 3476
Chris@0 3477 } else {
Chris@0 3478
Chris@0 3479 newLayer = m_document->createMainModelLayer(type);
Chris@0 3480 }
Chris@0 3481
Chris@0 3482 m_document->addLayerToView(pane, newLayer);
Chris@0 3483 m_paneStack->setCurrentLayer(pane, newLayer);
Chris@0 3484
Chris@0 3485 return;
Chris@0 3486 }
Chris@0 3487
Chris@0 3488 TransformName transform = i->second;
Chris@0 3489 TransformFactory *factory = TransformFactory::getInstance();
Chris@0 3490
Chris@0 3491 QString configurationXml;
Chris@0 3492
Chris@0 3493 int channel = -1;
Chris@0 3494 // pick up the default channel from any existing layers on the same pane
Chris@0 3495 for (int j = 0; j < pane->getLayerCount(); ++j) {
Chris@0 3496 int c = LayerFactory::getInstance()->getChannel(pane->getLayer(j));
Chris@0 3497 if (c != -1) {
Chris@0 3498 channel = c;
Chris@0 3499 break;
Chris@0 3500 }
Chris@0 3501 }
Chris@0 3502
Chris@33 3503 // We always ask for configuration, even if the plugin isn't
Chris@33 3504 // supposed to be configurable, because we need to let the user
Chris@33 3505 // change the execution context (block size etc).
Chris@0 3506
Chris@27 3507 PluginTransform::ExecutionContext context(channel);
Chris@27 3508
Chris@66 3509 std::vector<Model *> candidateInputModels =
Chris@66 3510 m_document->getTransformInputModels();
Chris@53 3511
Chris@54 3512 Model *inputModel = factory->getConfigurationForTransform(transform,
Chris@54 3513 candidateInputModels,
Chris@54 3514 context,
Chris@54 3515 configurationXml,
Chris@54 3516 m_playSource);
Chris@54 3517 if (!inputModel) return;
Chris@54 3518
Chris@54 3519 std::cerr << "Input model is " << inputModel << " \"" << inputModel->objectName().toStdString() << std::endl;
Chris@0 3520
Chris@0 3521 Layer *newLayer = m_document->createDerivedLayer(transform,
Chris@54 3522 inputModel,
Chris@27 3523 context,
Chris@0 3524 configurationXml);
Chris@0 3525
Chris@0 3526 if (newLayer) {
Chris@0 3527 m_document->addLayerToView(pane, newLayer);
Chris@27 3528 m_document->setChannel(newLayer, context.channel);
Chris@34 3529 m_recentTransforms.add(transform);
Chris@70 3530 m_paneStack->setCurrentLayer(pane, newLayer);
Chris@0 3531 }
Chris@0 3532
Chris@0 3533 updateMenuStates();
Chris@0 3534 }
Chris@0 3535
Chris@0 3536 void
Chris@0 3537 MainWindow::deleteCurrentPane()
Chris@0 3538 {
Chris@0 3539 CommandHistory::getInstance()->startCompoundOperation
Chris@0 3540 (tr("Delete Pane"), true);
Chris@0 3541
Chris@0 3542 Pane *pane = m_paneStack->getCurrentPane();
Chris@0 3543 if (pane) {
Chris@0 3544 while (pane->getLayerCount() > 0) {
Chris@0 3545 Layer *layer = pane->getLayer(0);
Chris@0 3546 if (layer) {
Chris@0 3547 m_document->removeLayerFromView(pane, layer);
Chris@0 3548 } else {
Chris@0 3549 break;
Chris@0 3550 }
Chris@0 3551 }
Chris@0 3552
Chris@0 3553 RemovePaneCommand *command = new RemovePaneCommand(this, pane);
Chris@0 3554 CommandHistory::getInstance()->addCommand(command);
Chris@0 3555 }
Chris@0 3556
Chris@0 3557 CommandHistory::getInstance()->endCompoundOperation();
Chris@0 3558
Chris@0 3559 updateMenuStates();
Chris@0 3560 }
Chris@0 3561
Chris@0 3562 void
Chris@0 3563 MainWindow::deleteCurrentLayer()
Chris@0 3564 {
Chris@0 3565 Pane *pane = m_paneStack->getCurrentPane();
Chris@0 3566 if (pane) {
Chris@0 3567 Layer *layer = pane->getSelectedLayer();
Chris@0 3568 if (layer) {
Chris@0 3569 m_document->removeLayerFromView(pane, layer);
Chris@0 3570 }
Chris@0 3571 }
Chris@0 3572 updateMenuStates();
Chris@0 3573 }
Chris@0 3574
Chris@0 3575 void
Chris@0 3576 MainWindow::renameCurrentLayer()
Chris@0 3577 {
Chris@0 3578 Pane *pane = m_paneStack->getCurrentPane();
Chris@0 3579 if (pane) {
Chris@0 3580 Layer *layer = pane->getSelectedLayer();
Chris@0 3581 if (layer) {
Chris@0 3582 bool ok = false;
Chris@0 3583 QString newName = QInputDialog::getText
Chris@0 3584 (this, tr("Rename Layer"),
Chris@0 3585 tr("New name for this layer:"),
Chris@0 3586 QLineEdit::Normal, layer->objectName(), &ok);
Chris@0 3587 if (ok) {
Chris@0 3588 layer->setObjectName(newName);
Chris@0 3589 setupExistingLayersMenu();
Chris@0 3590 }
Chris@0 3591 }
Chris@0 3592 }
Chris@0 3593 }
Chris@0 3594
Chris@0 3595 void
Chris@59 3596 MainWindow::playSpeedChanged(int position)
Chris@0 3597 {
Chris@59 3598 PlaySpeedRangeMapper mapper(0, 200);
Chris@60 3599
Chris@60 3600 float percent = m_playSpeed->mappedValue();
Chris@60 3601
Chris@60 3602 float factor = mapper.getFactorForValue(percent);
Chris@60 3603
Chris@60 3604 // float factor = mapper.getFactorForPosition(position);
Chris@60 3605 // float percent = mapper.getValueForPosition(position);
Chris@60 3606
Chris@60 3607 std::cerr << "speed = " << position << " percent = " << percent << " factor = " << factor << std::endl;
Chris@60 3608
Chris@60 3609 //!!! bool slow = (position < 100);
Chris@59 3610 bool something = (position != 100);
Chris@60 3611 /*!!!
Chris@59 3612 int pc = lrintf(percent);
Chris@21 3613
Chris@21 3614 m_playSpeed->setToolTip(tr("Playback speed: %1%2%")
Chris@60 3615 .arg(!slow ? "+" : "")
Chris@21 3616 .arg(pc));
Chris@60 3617 */
Chris@25 3618 m_playSharpen->setEnabled(something);
Chris@26 3619 m_playMono->setEnabled(something);
Chris@25 3620 bool sharpen = (something && m_playSharpen->isChecked());
Chris@26 3621 bool mono = (something && m_playMono->isChecked());
Chris@26 3622 m_playSource->setTimeStretch(factor, sharpen, mono);
Chris@16 3623 }
Chris@16 3624
Chris@16 3625 void
Chris@16 3626 MainWindow::playSharpenToggled()
Chris@16 3627 {
Chris@21 3628 QSettings settings;
Chris@21 3629 settings.beginGroup("MainWindow");
Chris@21 3630 settings.setValue("playsharpen", m_playSharpen->isChecked());
Chris@21 3631 settings.endGroup();
Chris@21 3632
Chris@16 3633 playSpeedChanged(m_playSpeed->value());
Chris@0 3634 }
Chris@0 3635
Chris@0 3636 void
Chris@26 3637 MainWindow::playMonoToggled()
Chris@26 3638 {
Chris@26 3639 QSettings settings;
Chris@26 3640 settings.beginGroup("MainWindow");
Chris@26 3641 settings.setValue("playmono", m_playMono->isChecked());
Chris@26 3642 settings.endGroup();
Chris@26 3643
Chris@26 3644 playSpeedChanged(m_playSpeed->value());
Chris@26 3645 }
Chris@26 3646
Chris@26 3647 void
Chris@0 3648 MainWindow::outputLevelsChanged(float left, float right)
Chris@0 3649 {
Chris@0 3650 m_fader->setPeakLeft(left);
Chris@0 3651 m_fader->setPeakRight(right);
Chris@0 3652 }
Chris@0 3653
Chris@0 3654 void
Chris@0 3655 MainWindow::sampleRateMismatch(size_t requested, size_t actual,
Chris@0 3656 bool willResample)
Chris@0 3657 {
Chris@0 3658 if (!willResample) {
Chris@0 3659 //!!! more helpful message needed
Chris@0 3660 QMessageBox::information
Chris@0 3661 (this, tr("Sample rate mismatch"),
Chris@0 3662 tr("The sample rate of this audio file (%1 Hz) does not match\nthe current playback rate (%2 Hz).\n\nThe file will play at the wrong speed.")
Chris@0 3663 .arg(requested).arg(actual));
Chris@0 3664 }
Chris@0 3665
Chris@0 3666 /*!!! Let's not do this for now, and see how we go -- now that we're putting
Chris@0 3667 sample rate information in the status bar
Chris@0 3668
Chris@0 3669 QMessageBox::information
Chris@0 3670 (this, tr("Sample rate mismatch"),
Chris@0 3671 tr("The sample rate of this audio file (%1 Hz) does not match\nthat of the output audio device (%2 Hz).\n\nThe file will be resampled automatically during playback.")
Chris@0 3672 .arg(requested).arg(actual));
Chris@0 3673 */
Chris@0 3674
Chris@0 3675 updateDescriptionLabel();
Chris@0 3676 }
Chris@0 3677
Chris@0 3678 void
Chris@42 3679 MainWindow::audioOverloadPluginDisabled()
Chris@42 3680 {
Chris@42 3681 QMessageBox::information
Chris@42 3682 (this, tr("Audio processing overload"),
Chris@42 3683 tr("Audio effects plugin auditioning has been disabled\ndue to a processing overload."));
Chris@42 3684 }
Chris@42 3685
Chris@42 3686 void
Chris@0 3687 MainWindow::layerAdded(Layer *layer)
Chris@0 3688 {
Chris@0 3689 // std::cerr << "MainWindow::layerAdded(" << layer << ")" << std::endl;
Chris@0 3690 // setupExistingLayersMenu();
Chris@0 3691 updateMenuStates();
Chris@0 3692 }
Chris@0 3693
Chris@0 3694 void
Chris@0 3695 MainWindow::layerRemoved(Layer *layer)
Chris@0 3696 {
Chris@0 3697 // std::cerr << "MainWindow::layerRemoved(" << layer << ")" << std::endl;
Chris@0 3698 setupExistingLayersMenu();
Chris@0 3699 updateMenuStates();
Chris@0 3700 }
Chris@0 3701
Chris@0 3702 void
Chris@0 3703 MainWindow::layerAboutToBeDeleted(Layer *layer)
Chris@0 3704 {
Chris@0 3705 // std::cerr << "MainWindow::layerAboutToBeDeleted(" << layer << ")" << std::endl;
Chris@0 3706 if (layer == m_timeRulerLayer) {
Chris@0 3707 // std::cerr << "(this is the time ruler layer)" << std::endl;
Chris@0 3708 m_timeRulerLayer = 0;
Chris@0 3709 }
Chris@0 3710 }
Chris@0 3711
Chris@0 3712 void
Chris@0 3713 MainWindow::layerInAView(Layer *layer, bool inAView)
Chris@0 3714 {
Chris@0 3715 // std::cerr << "MainWindow::layerInAView(" << layer << "," << inAView << ")" << std::endl;
Chris@0 3716
Chris@0 3717 // Check whether we need to add or remove model from play source
Chris@0 3718 Model *model = layer->getModel();
Chris@0 3719 if (model) {
Chris@0 3720 if (inAView) {
Chris@0 3721 m_playSource->addModel(model);
Chris@0 3722 } else {
Chris@0 3723 bool found = false;
Chris@0 3724 for (int i = 0; i < m_paneStack->getPaneCount(); ++i) {
Chris@0 3725 Pane *pane = m_paneStack->getPane(i);
Chris@0 3726 if (!pane) continue;
Chris@0 3727 for (int j = 0; j < pane->getLayerCount(); ++j) {
Chris@0 3728 Layer *pl = pane->getLayer(j);
Chris@0 3729 if (pl && pl->getModel() == model) {
Chris@0 3730 found = true;
Chris@0 3731 break;
Chris@0 3732 }
Chris@0 3733 }
Chris@0 3734 if (found) break;
Chris@0 3735 }
Chris@0 3736 if (!found) m_playSource->removeModel(model);
Chris@0 3737 }
Chris@0 3738 }
Chris@0 3739
Chris@0 3740 setupExistingLayersMenu();
Chris@0 3741 updateMenuStates();
Chris@0 3742 }
Chris@0 3743
Chris@0 3744 void
Chris@0 3745 MainWindow::modelAdded(Model *model)
Chris@0 3746 {
Chris@0 3747 // std::cerr << "MainWindow::modelAdded(" << model << ")" << std::endl;
Chris@0 3748 m_playSource->addModel(model);
Chris@66 3749 if (dynamic_cast<DenseTimeValueModel *>(model)) {
Chris@66 3750 setupPaneAndLayerMenus();
Chris@66 3751 }
Chris@0 3752 }
Chris@0 3753
Chris@0 3754 void
Chris@0 3755 MainWindow::mainModelChanged(WaveFileModel *model)
Chris@0 3756 {
Chris@0 3757 // std::cerr << "MainWindow::mainModelChanged(" << model << ")" << std::endl;
Chris@0 3758 updateDescriptionLabel();
Chris@0 3759 m_panLayer->setModel(model);
Chris@0 3760 if (model) m_viewManager->setMainModelSampleRate(model->getSampleRate());
Chris@46 3761 if (model && !m_playTarget && m_audioOutput) createPlayTarget();
Chris@0 3762 }
Chris@0 3763
Chris@0 3764 void
Chris@0 3765 MainWindow::modelAboutToBeDeleted(Model *model)
Chris@0 3766 {
Chris@0 3767 // std::cerr << "MainWindow::modelAboutToBeDeleted(" << model << ")" << std::endl;
Chris@0 3768 m_playSource->removeModel(model);
Chris@0 3769 }
Chris@0 3770
Chris@0 3771 void
Chris@0 3772 MainWindow::modelGenerationFailed(QString transformName)
Chris@0 3773 {
Chris@0 3774 QMessageBox::warning
Chris@0 3775 (this,
Chris@0 3776 tr("Failed to generate layer"),
Chris@34 3777 tr("Failed to generate a derived layer.\n\nThe layer transform \"%1\" failed.\n\nThis probably means that a plugin failed to initialise, perhaps because it\nrejected the processing block size that was requested.")
Chris@0 3778 .arg(transformName),
Chris@0 3779 QMessageBox::Ok, 0);
Chris@0 3780 }
Chris@0 3781
Chris@0 3782 void
Chris@0 3783 MainWindow::modelRegenerationFailed(QString layerName, QString transformName)
Chris@0 3784 {
Chris@0 3785 QMessageBox::warning
Chris@0 3786 (this,
Chris@0 3787 tr("Failed to regenerate layer"),
Chris@34 3788 tr("Failed to regenerate derived layer \"%1\".\n\nThe layer transform \"%2\" failed to run.\n\nThis probably means the layer used a plugin that is not currently available.")
Chris@0 3789 .arg(layerName).arg(transformName),
Chris@0 3790 QMessageBox::Ok, 0);
Chris@0 3791 }
Chris@0 3792
Chris@0 3793 void
Chris@0 3794 MainWindow::rightButtonMenuRequested(Pane *pane, QPoint position)
Chris@0 3795 {
Chris@0 3796 // std::cerr << "MainWindow::rightButtonMenuRequested(" << pane << ", " << position.x() << ", " << position.y() << ")" << std::endl;
Chris@0 3797 m_paneStack->setCurrentPane(pane);
Chris@0 3798 m_rightButtonMenu->popup(position);
Chris@0 3799 }
Chris@0 3800
Chris@0 3801 void
Chris@73 3802 MainWindow::propertyStacksResized()
Chris@73 3803 {
Chris@73 3804 /*
Chris@73 3805 std::cerr << "MainWindow::propertyStacksResized" << std::endl;
Chris@73 3806 Pane *pane = m_paneStack->getCurrentPane();
Chris@73 3807 if (pane && m_overview) {
Chris@73 3808 std::cerr << "Fixed width: " << pane->width() << std::endl;
Chris@73 3809 m_overview->setFixedWidth(pane->width());
Chris@73 3810 }
Chris@73 3811 */
Chris@73 3812 }
Chris@73 3813
Chris@73 3814 void
Chris@0 3815 MainWindow::showLayerTree()
Chris@0 3816 {
Chris@0 3817 QTreeView *view = new QTreeView();
Chris@0 3818 LayerTreeModel *tree = new LayerTreeModel(m_paneStack);
Chris@0 3819 view->expand(tree->index(0, 0, QModelIndex()));
Chris@0 3820 view->setModel(tree);
Chris@0 3821 view->show();
Chris@0 3822 }
Chris@0 3823
Chris@0 3824 void
Chris@69 3825 MainWindow::pollOSC()
Chris@69 3826 {
Chris@70 3827 if (!m_oscQueue || m_oscQueue->isEmpty()) return;
Chris@69 3828 std::cerr << "MainWindow::pollOSC: have " << m_oscQueue->getMessagesAvailable() << " messages" << std::endl;
Chris@69 3829
Chris@70 3830 if (m_openingAudioFile) return;
Chris@70 3831
Chris@69 3832 OSCMessage message = m_oscQueue->readMessage();
Chris@69 3833
Chris@69 3834 if (message.getTarget() != 0) {
Chris@69 3835 return; //!!! for now -- this class is target 0, others not handled yet
Chris@69 3836 }
Chris@69 3837
Chris@69 3838 handleOSCMessage(message);
Chris@69 3839 }
Chris@69 3840
Chris@69 3841 void
Chris@69 3842 MainWindow::handleOSCMessage(const OSCMessage &message)
Chris@69 3843 {
Chris@70 3844 std::cerr << "MainWindow::handleOSCMessage: thread id = "
Chris@70 3845 << QThread::currentThreadId() << std::endl;
Chris@70 3846
Chris@69 3847 // This large function should really be abstracted out.
Chris@69 3848
Chris@69 3849 if (message.getMethod() == "open") {
Chris@69 3850
Chris@69 3851 if (message.getArgCount() == 1 &&
Chris@69 3852 message.getArg(0).canConvert(QVariant::String)) {
Chris@69 3853 QString path = message.getArg(0).toString();
Chris@82 3854 if (openSomeFile(path, ReplaceMainModel) != FileOpenSucceeded) {
Chris@69 3855 std::cerr << "MainWindow::handleOSCMessage: File open failed for path \""
Chris@69 3856 << path.toStdString() << "\"" << std::endl;
Chris@69 3857 }
Chris@69 3858 //!!! we really need to spin here and not return until the
Chris@69 3859 // file has been completely decoded...
Chris@69 3860 }
Chris@69 3861
Chris@69 3862 } else if (message.getMethod() == "openadditional") {
Chris@69 3863
Chris@69 3864 if (message.getArgCount() == 1 &&
Chris@69 3865 message.getArg(0).canConvert(QVariant::String)) {
Chris@69 3866 QString path = message.getArg(0).toString();
Chris@82 3867 if (openSomeFile(path, CreateAdditionalModel) != FileOpenSucceeded) {
Chris@69 3868 std::cerr << "MainWindow::handleOSCMessage: File open failed for path \""
Chris@69 3869 << path.toStdString() << "\"" << std::endl;
Chris@69 3870 }
Chris@69 3871 }
Chris@69 3872
Chris@69 3873 } else if (message.getMethod() == "recent" ||
Chris@69 3874 message.getMethod() == "last") {
Chris@69 3875
Chris@69 3876 int n = 0;
Chris@69 3877 if (message.getMethod() == "recent" &&
Chris@69 3878 message.getArgCount() == 1 &&
Chris@69 3879 message.getArg(0).canConvert(QVariant::Int)) {
Chris@69 3880 n = message.getArg(0).toInt() - 1;
Chris@69 3881 }
Chris@69 3882 std::vector<QString> recent = m_recentFiles.getRecent();
Chris@69 3883 if (n >= 0 && n < recent.size()) {
Chris@82 3884 if (openSomeFile(recent[n], ReplaceMainModel) != FileOpenSucceeded) {
Chris@69 3885 std::cerr << "MainWindow::handleOSCMessage: File open failed for path \""
Chris@69 3886 << recent[n].toStdString() << "\"" << std::endl;
Chris@69 3887 }
Chris@69 3888 }
Chris@69 3889
Chris@69 3890 } else if (message.getMethod() == "save") {
Chris@69 3891
Chris@69 3892 QString path;
Chris@69 3893 if (message.getArgCount() == 1 &&
Chris@69 3894 message.getArg(0).canConvert(QVariant::String)) {
Chris@69 3895 path = message.getArg(0).toString();
Chris@69 3896 if (QFileInfo(path).exists()) {
Chris@69 3897 std::cerr << "MainWindow::handleOSCMessage: Refusing to overwrite existing file in save" << std::endl;
Chris@69 3898 } else {
Chris@69 3899 saveSessionFile(path);
Chris@69 3900 }
Chris@69 3901 }
Chris@69 3902
Chris@69 3903 } else if (message.getMethod() == "export") {
Chris@69 3904
Chris@69 3905 QString path;
Chris@69 3906 if (getMainModel()) {
Chris@69 3907 if (message.getArgCount() == 1 &&
Chris@69 3908 message.getArg(0).canConvert(QVariant::String)) {
Chris@69 3909 path = message.getArg(0).toString();
Chris@69 3910 if (QFileInfo(path).exists()) {
Chris@69 3911 std::cerr << "MainWindow::handleOSCMessage: Refusing to overwrite existing file in export" << std::endl;
Chris@69 3912 } else {
Chris@69 3913 WavFileWriter writer(path,
Chris@69 3914 getMainModel()->getSampleRate(),
Chris@69 3915 getMainModel()->getChannelCount());
Chris@69 3916 MultiSelection ms = m_viewManager->getSelection();
Chris@69 3917 if (!ms.getSelections().empty()) {
Chris@69 3918 writer.writeModel(getMainModel(), &ms);
Chris@69 3919 } else {
Chris@69 3920 writer.writeModel(getMainModel());
Chris@69 3921 }
Chris@69 3922 }
Chris@69 3923 }
Chris@69 3924 }
Chris@69 3925
Chris@69 3926 } else if (message.getMethod() == "jump" ||
Chris@69 3927 message.getMethod() == "play") {
Chris@69 3928
Chris@69 3929 if (getMainModel()) {
Chris@69 3930
Chris@69 3931 unsigned long frame = m_viewManager->getPlaybackFrame();
Chris@69 3932 bool selection = false;
Chris@69 3933 bool play = (message.getMethod() == "play");
Chris@69 3934
Chris@69 3935 if (message.getArgCount() == 1) {
Chris@69 3936
Chris@69 3937 if (message.getArg(0).canConvert(QVariant::String) &&
Chris@69 3938 message.getArg(0).toString() == "selection") {
Chris@69 3939
Chris@69 3940 selection = true;
Chris@69 3941
Chris@69 3942 } else if (message.getArg(0).canConvert(QVariant::String) &&
Chris@69 3943 message.getArg(0).toString() == "end") {
Chris@69 3944
Chris@69 3945 frame = getMainModel()->getEndFrame();
Chris@69 3946
Chris@69 3947 } else if (message.getArg(0).canConvert(QVariant::Double)) {
Chris@69 3948
Chris@69 3949 double time = message.getArg(0).toDouble();
Chris@69 3950 if (time < 0.0) time = 0.0;
Chris@69 3951
Chris@69 3952 frame = lrint(time * getMainModel()->getSampleRate());
Chris@69 3953 }
Chris@69 3954 }
Chris@69 3955
Chris@69 3956 if (frame > getMainModel()->getEndFrame()) {
Chris@69 3957 frame = getMainModel()->getEndFrame();
Chris@69 3958 }
Chris@69 3959
Chris@69 3960 if (play) {
Chris@69 3961 m_viewManager->setPlaySelectionMode(selection);
Chris@69 3962 }
Chris@69 3963
Chris@69 3964 if (selection) {
Chris@69 3965 MultiSelection::SelectionList sl = m_viewManager->getSelections();
Chris@69 3966 if (!sl.empty()) {
Chris@69 3967 frame = sl.begin()->getStartFrame();
Chris@69 3968 }
Chris@69 3969 }
Chris@69 3970
Chris@69 3971 m_viewManager->setPlaybackFrame(frame);
Chris@69 3972
Chris@69 3973 if (play && !m_playSource->isPlaying()) {
Chris@69 3974 m_playSource->play(frame);
Chris@69 3975 }
Chris@69 3976 }
Chris@69 3977
Chris@69 3978 } else if (message.getMethod() == "stop") {
Chris@69 3979
Chris@69 3980 if (m_playSource->isPlaying()) m_playSource->stop();
Chris@69 3981
Chris@69 3982 } else if (message.getMethod() == "loop") {
Chris@69 3983
Chris@69 3984 if (message.getArgCount() == 1 &&
Chris@69 3985 message.getArg(0).canConvert(QVariant::String)) {
Chris@69 3986
Chris@69 3987 QString str = message.getArg(0).toString();
Chris@69 3988 if (str == "on") {
Chris@69 3989 m_viewManager->setPlayLoopMode(true);
Chris@69 3990 } else if (str == "off") {
Chris@69 3991 m_viewManager->setPlayLoopMode(false);
Chris@69 3992 }
Chris@69 3993 }
Chris@69 3994
Chris@69 3995 } else if (message.getMethod() == "select" ||
Chris@69 3996 message.getMethod() == "addselect") {
Chris@69 3997
Chris@69 3998 if (getMainModel()) {
Chris@69 3999
Chris@73 4000 int f0 = getMainModel()->getStartFrame();
Chris@73 4001 int f1 = getMainModel()->getEndFrame();
Chris@69 4002
Chris@69 4003 bool done = false;
Chris@69 4004
Chris@69 4005 if (message.getArgCount() == 2 &&
Chris@69 4006 message.getArg(0).canConvert(QVariant::Double) &&
Chris@69 4007 message.getArg(1).canConvert(QVariant::Double)) {
Chris@69 4008
Chris@69 4009 double t0 = message.getArg(0).toDouble();
Chris@69 4010 double t1 = message.getArg(1).toDouble();
Chris@69 4011 if (t1 < t0) { double temp = t0; t0 = t1; t1 = temp; }
Chris@69 4012 if (t0 < 0.0) t0 = 0.0;
Chris@69 4013 if (t1 < 0.0) t1 = 0.0;
Chris@69 4014
Chris@69 4015 f0 = lrint(t0 * getMainModel()->getSampleRate());
Chris@69 4016 f1 = lrint(t1 * getMainModel()->getSampleRate());
Chris@73 4017
Chris@73 4018 Pane *pane = m_paneStack->getCurrentPane();
Chris@73 4019 Layer *layer = 0;
Chris@73 4020 if (pane) layer = pane->getSelectedLayer();
Chris@73 4021 if (layer) {
Chris@73 4022 size_t resolution;
Chris@73 4023 layer->snapToFeatureFrame(pane, f0, resolution,
Chris@73 4024 Layer::SnapLeft);
Chris@73 4025 layer->snapToFeatureFrame(pane, f1, resolution,
Chris@73 4026 Layer::SnapRight);
Chris@73 4027 }
Chris@69 4028
Chris@69 4029 } else if (message.getArgCount() == 1 &&
Chris@69 4030 message.getArg(0).canConvert(QVariant::String)) {
Chris@69 4031
Chris@69 4032 QString str = message.getArg(0).toString();
Chris@69 4033 if (str == "none") {
Chris@69 4034 m_viewManager->clearSelections();
Chris@69 4035 done = true;
Chris@69 4036 }
Chris@69 4037 }
Chris@69 4038
Chris@69 4039 if (!done) {
Chris@69 4040 if (message.getMethod() == "select") {
Chris@69 4041 m_viewManager->setSelection(Selection(f0, f1));
Chris@69 4042 } else {
Chris@69 4043 m_viewManager->addSelection(Selection(f0, f1));
Chris@69 4044 }
Chris@69 4045 }
Chris@69 4046 }
Chris@69 4047
Chris@69 4048 } else if (message.getMethod() == "add") {
Chris@69 4049
Chris@69 4050 if (getMainModel()) {
Chris@69 4051
Chris@69 4052 if (message.getArgCount() >= 1 &&
Chris@69 4053 message.getArg(0).canConvert(QVariant::String)) {
Chris@69 4054
Chris@69 4055 int channel = -1;
Chris@69 4056 if (message.getArgCount() == 2 &&
Chris@69 4057 message.getArg(0).canConvert(QVariant::Int)) {
Chris@69 4058 channel = message.getArg(0).toInt();
Chris@69 4059 if (channel < -1 ||
Chris@69 4060 channel > getMainModel()->getChannelCount()) {
Chris@69 4061 std::cerr << "WARNING: MainWindow::handleOSCMessage: channel "
Chris@69 4062 << channel << " out of range" << std::endl;
Chris@69 4063 channel = -1;
Chris@69 4064 }
Chris@69 4065 }
Chris@69 4066
Chris@69 4067 QString str = message.getArg(0).toString();
Chris@69 4068
Chris@69 4069 LayerFactory::LayerType type =
Chris@69 4070 LayerFactory::getInstance()->getLayerTypeForName(str);
Chris@69 4071
Chris@69 4072 if (type == LayerFactory::UnknownLayer) {
Chris@69 4073 std::cerr << "WARNING: MainWindow::handleOSCMessage: unknown layer "
Chris@69 4074 << "type " << str.toStdString() << std::endl;
Chris@69 4075 } else {
Chris@69 4076
Chris@69 4077 PaneConfiguration configuration(type,
Chris@69 4078 getMainModel(),
Chris@69 4079 channel);
Chris@69 4080
Chris@69 4081 addPane(configuration,
Chris@69 4082 tr("Add %1 Pane")
Chris@69 4083 .arg(LayerFactory::getInstance()->
Chris@69 4084 getLayerPresentationName(type)));
Chris@69 4085 }
Chris@69 4086 }
Chris@69 4087 }
Chris@69 4088
Chris@69 4089 } else if (message.getMethod() == "undo") {
Chris@69 4090
Chris@69 4091 CommandHistory::getInstance()->undo();
Chris@69 4092
Chris@69 4093 } else if (message.getMethod() == "redo") {
Chris@69 4094
Chris@69 4095 CommandHistory::getInstance()->redo();
Chris@69 4096
Chris@69 4097 } else if (message.getMethod() == "set") {
Chris@69 4098
Chris@70 4099 if (message.getArgCount() == 2 &&
Chris@69 4100 message.getArg(0).canConvert(QVariant::String) &&
Chris@69 4101 message.getArg(1).canConvert(QVariant::Double)) {
Chris@69 4102
Chris@69 4103 QString property = message.getArg(0).toString();
Chris@69 4104 float value = (float)message.getArg(1).toDouble();
Chris@69 4105
Chris@69 4106 if (property == "gain") {
Chris@69 4107 if (value < 0.0) value = 0.0;
Chris@69 4108 m_fader->setValue(value);
Chris@69 4109 if (m_playTarget) m_playTarget->setOutputGain(value);
Chris@69 4110 } else if (property == "speedup") {
Chris@69 4111 m_playSpeed->setMappedValue(value);
Chris@69 4112 } else if (property == "overlays") {
Chris@69 4113 if (value < 0.5) {
Chris@69 4114 m_viewManager->setOverlayMode(ViewManager::NoOverlays);
Chris@69 4115 } else if (value < 1.5) {
Chris@69 4116 m_viewManager->setOverlayMode(ViewManager::BasicOverlays);
Chris@69 4117 } else {
Chris@69 4118 m_viewManager->setOverlayMode(ViewManager::AllOverlays);
Chris@69 4119 }
Chris@70 4120 } else if (property == "zoomwheels") {
Chris@70 4121 m_viewManager->setZoomWheelsEnabled(value > 0.5);
Chris@72 4122 } else if (property == "propertyboxes") {
Chris@72 4123 bool toggle = ((value < 0.5) !=
Chris@72 4124 (m_paneStack->getLayoutStyle() == PaneStack::NoPropertyStacks));
Chris@72 4125 if (toggle) togglePropertyBoxes();
Chris@70 4126 }
Chris@70 4127
Chris@70 4128 } else {
Chris@70 4129 PropertyContainer *container = 0;
Chris@70 4130 Pane *pane = m_paneStack->getCurrentPane();
Chris@70 4131 if (pane &&
Chris@70 4132 message.getArgCount() == 3 &&
Chris@70 4133 message.getArg(0).canConvert(QVariant::String) &&
Chris@70 4134 message.getArg(1).canConvert(QVariant::String) &&
Chris@70 4135 message.getArg(2).canConvert(QVariant::String)) {
Chris@70 4136 if (message.getArg(0).toString() == "pane") {
Chris@70 4137 container = pane->getPropertyContainer(0);
Chris@70 4138 } else if (message.getArg(0).toString() == "layer") {
Chris@70 4139 container = pane->getSelectedLayer();
Chris@70 4140 }
Chris@70 4141 }
Chris@70 4142 if (container) {
Chris@70 4143 QString nameString = message.getArg(1).toString();
Chris@70 4144 QString valueString = message.getArg(2).toString();
Chris@70 4145 container->setPropertyWithCommand(nameString, valueString);
Chris@69 4146 }
Chris@69 4147 }
Chris@69 4148
Chris@69 4149 } else if (message.getMethod() == "setcurrent") {
Chris@69 4150
Chris@69 4151 int paneIndex = -1, layerIndex = -1;
Chris@69 4152 bool wantLayer = false;
Chris@69 4153
Chris@69 4154 if (message.getArgCount() >= 1 &&
Chris@69 4155 message.getArg(0).canConvert(QVariant::Int)) {
Chris@69 4156
Chris@69 4157 paneIndex = message.getArg(0).toInt() - 1;
Chris@69 4158
Chris@69 4159 if (message.getArgCount() >= 2 &&
Chris@69 4160 message.getArg(1).canConvert(QVariant::Int)) {
Chris@69 4161 wantLayer = true;
Chris@69 4162 layerIndex = message.getArg(1).toInt() - 1;
Chris@69 4163 }
Chris@69 4164 }
Chris@69 4165
Chris@69 4166 if (paneIndex >= 0 && paneIndex < m_paneStack->getPaneCount()) {
Chris@69 4167 Pane *pane = m_paneStack->getPane(paneIndex);
Chris@70 4168 m_paneStack->setCurrentPane(pane);
Chris@69 4169 if (layerIndex >= 0 && layerIndex < pane->getLayerCount()) {
Chris@69 4170 Layer *layer = pane->getLayer(layerIndex);
Chris@69 4171 m_paneStack->setCurrentLayer(pane, layer);
Chris@69 4172 } else if (wantLayer && layerIndex == -1) {
Chris@69 4173 m_paneStack->setCurrentLayer(pane, 0);
Chris@69 4174 }
Chris@69 4175 }
Chris@69 4176
Chris@69 4177 } else if (message.getMethod() == "delete") {
Chris@69 4178
Chris@69 4179 if (message.getArgCount() == 1 &&
Chris@69 4180 message.getArg(0).canConvert(QVariant::String)) {
Chris@69 4181
Chris@69 4182 QString target = message.getArg(0).toString();
Chris@69 4183
Chris@69 4184 if (target == "pane") {
Chris@69 4185
Chris@69 4186 deleteCurrentPane();
Chris@69 4187
Chris@69 4188 } else if (target == "layer") {
Chris@69 4189
Chris@69 4190 deleteCurrentLayer();
Chris@69 4191
Chris@69 4192 } else {
Chris@69 4193
Chris@69 4194 std::cerr << "WARNING: MainWindow::handleOSCMessage: Unknown delete target " << target.toStdString() << std::endl;
Chris@69 4195 }
Chris@69 4196 }
Chris@69 4197
Chris@69 4198 } else if (message.getMethod() == "zoom") {
Chris@69 4199
Chris@69 4200 if (message.getArgCount() == 1) {
Chris@69 4201 if (message.getArg(0).canConvert(QVariant::String) &&
Chris@69 4202 message.getArg(0).toString() == "in") {
Chris@69 4203 zoomIn();
Chris@69 4204 } else if (message.getArg(0).canConvert(QVariant::String) &&
Chris@69 4205 message.getArg(0).toString() == "out") {
Chris@69 4206 zoomOut();
Chris@69 4207 } else if (message.getArg(0).canConvert(QVariant::String) &&
Chris@69 4208 message.getArg(0).toString() == "default") {
Chris@69 4209 zoomDefault();
Chris@69 4210 } else if (message.getArg(0).canConvert(QVariant::Double)) {
Chris@69 4211 double level = message.getArg(0).toDouble();
Chris@69 4212 Pane *currentPane = m_paneStack->getCurrentPane();
Chris@69 4213 if (level < 1.0) level = 1.0;
Chris@69 4214 if (currentPane) currentPane->setZoomLevel(lrint(level));
Chris@69 4215 }
Chris@69 4216 }
Chris@69 4217
Chris@73 4218 } else if (message.getMethod() == "zoomvertical") {
Chris@73 4219
Chris@73 4220 Pane *pane = m_paneStack->getCurrentPane();
Chris@73 4221 Layer *layer = 0;
Chris@73 4222 if (pane && pane->getLayerCount() > 0) {
Chris@73 4223 layer = pane->getLayer(pane->getLayerCount() - 1);
Chris@73 4224 }
Chris@73 4225 int defaultStep = 0;
Chris@73 4226 int steps = 0;
Chris@73 4227 if (layer) {
Chris@73 4228 steps = layer->getVerticalZoomSteps(defaultStep);
Chris@73 4229 if (message.getArgCount() == 1 && steps > 0) {
Chris@73 4230 if (message.getArg(0).canConvert(QVariant::String) &&
Chris@73 4231 message.getArg(0).toString() == "in") {
Chris@73 4232 int step = layer->getCurrentVerticalZoomStep() + 1;
Chris@73 4233 if (step < steps) layer->setVerticalZoomStep(step);
Chris@73 4234 } else if (message.getArg(0).canConvert(QVariant::String) &&
Chris@73 4235 message.getArg(0).toString() == "out") {
Chris@73 4236 int step = layer->getCurrentVerticalZoomStep() - 1;
Chris@73 4237 if (step >= 0) layer->setVerticalZoomStep(step);
Chris@73 4238 } else if (message.getArg(0).canConvert(QVariant::String) &&
Chris@73 4239 message.getArg(0).toString() == "default") {
Chris@73 4240 layer->setVerticalZoomStep(defaultStep);
Chris@73 4241 }
Chris@73 4242 } else if (message.getArgCount() == 2) {
Chris@73 4243 if (message.getArg(0).canConvert(QVariant::Double) &&
Chris@73 4244 message.getArg(1).canConvert(QVariant::Double)) {
Chris@73 4245 double min = message.getArg(0).toDouble();
Chris@73 4246 double max = message.getArg(1).toDouble();
Chris@73 4247 layer->setDisplayExtents(min, max);
Chris@73 4248 }
Chris@73 4249 }
Chris@73 4250 }
Chris@73 4251
Chris@70 4252 } else if (message.getMethod() == "quit") {
Chris@70 4253
Chris@70 4254 m_abandoning = true;
Chris@70 4255 close();
Chris@70 4256
Chris@70 4257 } else if (message.getMethod() == "resize") {
Chris@70 4258
Chris@70 4259 if (message.getArgCount() == 2) {
Chris@70 4260
Chris@70 4261 int width = 0, height = 0;
Chris@70 4262
Chris@70 4263 if (message.getArg(1).canConvert(QVariant::Int)) {
Chris@70 4264
Chris@70 4265 height = message.getArg(1).toInt();
Chris@70 4266
Chris@70 4267 if (message.getArg(0).canConvert(QVariant::String) &&
Chris@70 4268 message.getArg(0).toString() == "pane") {
Chris@70 4269
Chris@70 4270 Pane *pane = m_paneStack->getCurrentPane();
Chris@70 4271 if (pane) pane->resize(pane->width(), height);
Chris@70 4272
Chris@70 4273 } else if (message.getArg(0).canConvert(QVariant::Int)) {
Chris@70 4274
Chris@70 4275 width = message.getArg(0).toInt();
Chris@70 4276 resize(width, height);
Chris@70 4277 }
Chris@70 4278 }
Chris@70 4279 }
Chris@70 4280
Chris@70 4281 } else if (message.getMethod() == "transform") {
Chris@70 4282
Chris@70 4283 Pane *pane = m_paneStack->getCurrentPane();
Chris@70 4284
Chris@70 4285 if (getMainModel() &&
Chris@70 4286 pane &&
Chris@70 4287 message.getArgCount() == 1 &&
Chris@70 4288 message.getArg(0).canConvert(QVariant::String)) {
Chris@70 4289
Chris@70 4290 TransformName transform = message.getArg(0).toString();
Chris@70 4291
Chris@70 4292 Layer *newLayer = m_document->createDerivedLayer
Chris@70 4293 (transform,
Chris@70 4294 getMainModel(),
Chris@73 4295 TransformFactory::getInstance()->getDefaultContextForTransform
Chris@73 4296 (transform, getMainModel()),
Chris@70 4297 "");
Chris@70 4298
Chris@70 4299 if (newLayer) {
Chris@70 4300 m_document->addLayerToView(pane, newLayer);
Chris@70 4301 m_recentTransforms.add(transform);
Chris@70 4302 m_paneStack->setCurrentLayer(pane, newLayer);
Chris@70 4303 }
Chris@70 4304 }
Chris@70 4305
Chris@69 4306 } else {
Chris@69 4307 std::cerr << "WARNING: MainWindow::handleOSCMessage: Unknown or unsupported "
Chris@69 4308 << "method \"" << message.getMethod().toStdString()
Chris@69 4309 << "\"" << std::endl;
Chris@69 4310 }
Chris@69 4311
Chris@69 4312 }
Chris@69 4313
Chris@69 4314 void
Chris@0 4315 MainWindow::preferences()
Chris@0 4316 {
Chris@0 4317 if (!m_preferencesDialog.isNull()) {
Chris@0 4318 m_preferencesDialog->show();
Chris@0 4319 m_preferencesDialog->raise();
Chris@0 4320 return;
Chris@0 4321 }
Chris@0 4322
Chris@0 4323 m_preferencesDialog = new PreferencesDialog(this);
Chris@0 4324
Chris@0 4325 // DeleteOnClose is safe here, because m_preferencesDialog is a
Chris@0 4326 // QPointer that will be zeroed when the dialog is deleted. We
Chris@0 4327 // use it in preference to leaving the dialog lying around because
Chris@0 4328 // if you Cancel the dialog, it resets the preferences state
Chris@0 4329 // without resetting its own widgets, so its state will be
Chris@0 4330 // incorrect when next shown unless we construct it afresh
Chris@0 4331 m_preferencesDialog->setAttribute(Qt::WA_DeleteOnClose);
Chris@0 4332
Chris@0 4333 m_preferencesDialog->show();
Chris@0 4334 }
Chris@0 4335
Chris@0 4336 void
Chris@0 4337 MainWindow::website()
Chris@0 4338 {
Chris@0 4339 openHelpUrl(tr("http://www.sonicvisualiser.org/"));
Chris@0 4340 }
Chris@0 4341
Chris@0 4342 void
Chris@0 4343 MainWindow::help()
Chris@0 4344 {
Chris@0 4345 openHelpUrl(tr("http://www.sonicvisualiser.org/doc/reference/en/"));
Chris@0 4346 }
Chris@0 4347
Chris@0 4348 void
Chris@0 4349 MainWindow::openHelpUrl(QString url)
Chris@0 4350 {
Chris@0 4351 // This method mostly lifted from Qt Assistant source code
Chris@0 4352
Chris@0 4353 QProcess *process = new QProcess(this);
Chris@0 4354 connect(process, SIGNAL(finished(int)), process, SLOT(deleteLater()));
Chris@0 4355
Chris@0 4356 QStringList args;
Chris@0 4357
Chris@0 4358 #ifdef Q_OS_MAC
Chris@0 4359 args.append(url);
Chris@0 4360 process->start("open", args);
Chris@0 4361 #else
Chris@0 4362 #ifdef Q_OS_WIN32
Chris@0 4363
Chris@0 4364 QString pf(getenv("ProgramFiles"));
Chris@0 4365 QString command = pf + QString("\\Internet Explorer\\IEXPLORE.EXE");
Chris@0 4366
Chris@0 4367 args.append(url);
Chris@0 4368 process->start(command, args);
Chris@0 4369
Chris@0 4370 #else
Chris@0 4371 #ifdef Q_WS_X11
Chris@0 4372 if (!qgetenv("KDE_FULL_SESSION").isEmpty()) {
Chris@0 4373 args.append("exec");
Chris@0 4374 args.append(url);
Chris@0 4375 process->start("kfmclient", args);
Chris@0 4376 } else if (!qgetenv("BROWSER").isEmpty()) {
Chris@0 4377 args.append(url);
Chris@0 4378 process->start(qgetenv("BROWSER"), args);
Chris@0 4379 } else {
Chris@0 4380 args.append(url);
Chris@0 4381 process->start("firefox", args);
Chris@0 4382 }
Chris@0 4383 #endif
Chris@0 4384 #endif
Chris@0 4385 #endif
Chris@0 4386 }
Chris@0 4387
Chris@0 4388 void
Chris@0 4389 MainWindow::about()
Chris@0 4390 {
Chris@0 4391 bool debug = false;
Chris@0 4392 QString version = "(unknown version)";
Chris@0 4393
Chris@0 4394 #ifdef BUILD_DEBUG
Chris@0 4395 debug = true;
Chris@0 4396 #endif
Chris@0 4397 #ifdef SV_VERSION
Chris@0 4398 #ifdef SVNREV
Chris@0 4399 version = tr("Release %1 : Revision %2").arg(SV_VERSION).arg(SVNREV);
Chris@0 4400 #else
Chris@0 4401 version = tr("Release %1").arg(SV_VERSION);
Chris@0 4402 #endif
Chris@0 4403 #else
Chris@0 4404 #ifdef SVNREV
Chris@0 4405 version = tr("Unreleased : Revision %1").arg(SVNREV);
Chris@0 4406 #endif
Chris@0 4407 #endif
Chris@0 4408
Chris@0 4409 QString aboutText;
Chris@0 4410
Chris@0 4411 aboutText += tr("<h3>About Sonic Visualiser</h3>");
Chris@0 4412 aboutText += tr("<p>Sonic Visualiser is a program for viewing and exploring audio data for semantic music analysis and annotation.</p>");
Chris@0 4413 aboutText += tr("<p>%1 : %2 build</p>")
Chris@0 4414 .arg(version)
Chris@0 4415 .arg(debug ? tr("Debug") : tr("Release"));
Chris@0 4416
Chris@0 4417 #ifdef BUILD_STATIC
Chris@0 4418 aboutText += tr("<p>Statically linked");
Chris@0 4419 #ifndef QT_SHARED
Chris@0 4420 aboutText += tr("<br>With Qt (v%1) &copy; Trolltech AS").arg(QT_VERSION_STR);
Chris@0 4421 #endif
Chris@0 4422 #ifdef HAVE_JACK
Chris@0 4423 aboutText += tr("<br>With JACK audio output (v%1) &copy; Paul Davis and Jack O'Quin").arg(JACK_VERSION);
Chris@0 4424 #endif
Chris@0 4425 #ifdef HAVE_PORTAUDIO
Chris@0 4426 aboutText += tr("<br>With PortAudio audio output &copy; Ross Bencina and Phil Burk");
Chris@0 4427 #endif
Chris@0 4428 #ifdef HAVE_OGGZ
Chris@0 4429 aboutText += tr("<br>With Ogg file decoder (oggz v%1, fishsound v%2) &copy; CSIRO Australia").arg(OGGZ_VERSION).arg(FISHSOUND_VERSION);
Chris@0 4430 #endif
Chris@0 4431 #ifdef HAVE_MAD
Chris@0 4432 aboutText += tr("<br>With MAD mp3 decoder (v%1) &copy; Underbit Technologies Inc").arg(MAD_VERSION);
Chris@0 4433 #endif
Chris@0 4434 #ifdef HAVE_SAMPLERATE
Chris@0 4435 aboutText += tr("<br>With libsamplerate (v%1) &copy; Erik de Castro Lopo").arg(SAMPLERATE_VERSION);
Chris@0 4436 #endif
Chris@0 4437 #ifdef HAVE_SNDFILE
Chris@0 4438 aboutText += tr("<br>With libsndfile (v%1) &copy; Erik de Castro Lopo").arg(SNDFILE_VERSION);
Chris@0 4439 #endif
Chris@0 4440 #ifdef HAVE_FFTW3
Chris@0 4441 aboutText += tr("<br>With FFTW3 (v%1) &copy; Matteo Frigo and MIT").arg(FFTW3_VERSION);
Chris@0 4442 #endif
Chris@0 4443 #ifdef HAVE_VAMP
Chris@0 4444 aboutText += tr("<br>With Vamp plugin support (API v%1, SDK v%2) &copy; Chris Cannam").arg(VAMP_API_VERSION).arg(VAMP_SDK_VERSION);
Chris@0 4445 #endif
Chris@0 4446 aboutText += tr("<br>With LADSPA plugin support (API v%1) &copy; Richard Furse, Paul Davis, Stefan Westerfeld").arg(LADSPA_VERSION);
Chris@0 4447 aboutText += tr("<br>With DSSI plugin support (API v%1) &copy; Chris Cannam, Steve Harris, Sean Bolton").arg(DSSI_VERSION);
Chris@69 4448 #ifdef HAVE_LIBLO
Chris@69 4449 aboutText += tr("<br>With liblo Lite OSC library (v%1) &copy; Steve Harris").arg(LIBLO_VERSION);
Chris@70 4450 if (m_oscQueue && m_oscQueue->isOK()) {
Chris@69 4451 aboutText += tr("<p>The OSC URL for this instance is: \"%1\"").arg(m_oscQueue->getOSCURL());
Chris@69 4452 }
Chris@69 4453 #endif
Chris@0 4454 aboutText += "</p>";
Chris@0 4455 #endif
Chris@0 4456
Chris@0 4457 aboutText +=
Chris@77 4458 "<p>Sonic Visualiser Copyright &copy; 2005 - 2006 Chris Cannam and<br>"
Chris@0 4459 "Centre for Digital Music, Queen Mary, University of London.</p>"
Chris@0 4460 "<p>This program is free software; you can redistribute it and/or<br>"
Chris@0 4461 "modify it under the terms of the GNU General Public License as<br>"
Chris@0 4462 "published by the Free Software Foundation; either version 2 of the<br>"
Chris@0 4463 "License, or (at your option) any later version.<br>See the file "
Chris@0 4464 "COPYING included with this distribution for more information.</p>";
Chris@0 4465
Chris@0 4466 QMessageBox::about(this, tr("About Sonic Visualiser"), aboutText);
Chris@0 4467 }
Chris@0 4468
Chris@0 4469
Chris@0 4470 #ifdef INCLUDE_MOCFILES
Chris@0 4471 #include "MainWindow.moc.cpp"
Chris@0 4472 #endif