annotate main/MainWindow.cpp @ 1418:2dd0bc9f8fd4 3.0-integration

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