annotate main/MainWindow.cpp @ 333:faff9cd8f663

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