annotate main/MainWindow.cpp @ 785:624a8864391e

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