annotate main/MainWindow.cpp @ 88:51be0daa1386

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