annotate main/MainWindow.cpp @ 289:ee96e37a3cbb

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