annotate main/MainWindow.cpp @ 2038:8529a60df1b4

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