annotate main/MainWindow.cpp @ 2090:0a93e653e107 spectrogramparam

Use log-frequency rather than log-bin for calculating x coord in spectrum. This has the advantage that frequency positions don't move when we change the window size or oversampling ratio, but it does give us an unhelpfully large amount of space for very low frequencies - to be considered
author Chris Cannam
date Mon, 12 Nov 2018 11:35:03 +0000
parents 63da6a82c12d
children 6e3ef3aa341e
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@2062 3242 stop();
Chris@0 3243 createDocument();
Chris@0 3244
Chris@0 3245 Pane *pane = m_paneStack->addPane();
Chris@0 3246
Chris@90 3247 connect(pane, SIGNAL(contextHelpChanged(const QString &)),
Chris@116 3248 this, SLOT(contextHelpChanged(const QString &)));
Chris@90 3249
Chris@0 3250 if (!m_timeRulerLayer) {
Chris@1770 3251 m_timeRulerLayer = m_document->createMainModelLayer
Chris@1770 3252 (LayerFactory::TimeRuler);
Chris@0 3253 }
Chris@0 3254
Chris@0 3255 m_document->addLayerToView(pane, m_timeRulerLayer);
Chris@0 3256
Chris@0 3257 Layer *waveform = m_document->createMainModelLayer(LayerFactory::Waveform);
Chris@0 3258 m_document->addLayerToView(pane, waveform);
Chris@0 3259
Chris@65 3260 m_overview->registerView(pane);
Chris@0 3261
Chris@0 3262 CommandHistory::getInstance()->clear();
Chris@0 3263 CommandHistory::getInstance()->documentSaved();
Chris@0 3264 documentRestored();
Chris@0 3265 updateMenuStates();
Chris@0 3266 }
Chris@0 3267
Chris@0 3268 void
Chris@303 3269 MainWindow::documentReplaced()
Chris@303 3270 {
Chris@303 3271 if (m_document) {
Chris@303 3272 connect(m_document, SIGNAL(activity(QString)),
Chris@303 3273 m_activityLog, SLOT(activityHappened(QString)));
Chris@303 3274 }
Chris@303 3275 }
Chris@303 3276
Chris@303 3277 void
Chris@0 3278 MainWindow::closeSession()
Chris@0 3279 {
Chris@0 3280 if (!checkSaveModified()) return;
Chris@0 3281
Chris@0 3282 while (m_paneStack->getPaneCount() > 0) {
Chris@0 3283
Chris@1770 3284 Pane *pane = m_paneStack->getPane(m_paneStack->getPaneCount() - 1);
Chris@1770 3285
Chris@1770 3286 while (pane->getLayerCount() > 0) {
Chris@1770 3287 m_document->removeLayerFromView
Chris@1770 3288 (pane, pane->getLayer(pane->getLayerCount() - 1));
Chris@1770 3289 }
Chris@1770 3290
Chris@1770 3291 m_overview->unregisterView(pane);
Chris@1770 3292 m_paneStack->deletePane(pane);
Chris@0 3293 }
Chris@0 3294
Chris@0 3295 while (m_paneStack->getHiddenPaneCount() > 0) {
Chris@0 3296
Chris@1770 3297 Pane *pane = m_paneStack->getHiddenPane
Chris@1770 3298 (m_paneStack->getHiddenPaneCount() - 1);
Chris@1770 3299
Chris@1770 3300 while (pane->getLayerCount() > 0) {
Chris@1770 3301 m_document->removeLayerFromView
Chris@1770 3302 (pane, pane->getLayer(pane->getLayerCount() - 1));
Chris@1770 3303 }
Chris@1770 3304
Chris@1770 3305 m_overview->unregisterView(pane);
Chris@1770 3306 m_paneStack->deletePane(pane);
Chris@0 3307 }
Chris@0 3308
Chris@504 3309 delete m_layerTreeDialog.data();
Chris@504 3310 delete m_preferencesDialog.data();
Chris@504 3311
Chris@504 3312 m_activityLog->hide();
Chris@891 3313 m_unitConverter->hide();
Chris@504 3314 m_keyReference->hide();
Chris@504 3315
Chris@0 3316 delete m_document;
Chris@0 3317 m_document = 0;
Chris@0 3318 m_viewManager->clearSelections();
Chris@0 3319 m_timeRulerLayer = 0; // document owned this
Chris@0 3320
Chris@0 3321 m_sessionFile = "";
Chris@518 3322 setWindowTitle(QApplication::applicationName());
Chris@0 3323
Chris@0 3324 CommandHistory::getInstance()->clear();
Chris@0 3325 CommandHistory::getInstance()->documentSaved();
Chris@0 3326 documentRestored();
Chris@0 3327 }
Chris@0 3328
Chris@0 3329 void
Chris@0 3330 MainWindow::openSomething()
Chris@0 3331 {
Chris@0 3332 QString orig = m_audioFile;
Chris@0 3333 if (orig == "") orig = ".";
Chris@0 3334 else orig = QFileInfo(orig).absoluteDir().canonicalPath();
Chris@0 3335
Chris@88 3336 QString path = getOpenFileName(FileFinder::AnyFile);
Chris@0 3337
Chris@0 3338 if (path.isEmpty()) return;
Chris@0 3339
Chris@844 3340 FileOpenStatus status = openPath(path, ReplaceSession);
Chris@193 3341
Chris@193 3342 if (status == FileOpenFailed) {
Chris@247 3343 emit hideSplash();
Chris@193 3344 QMessageBox::critical(this, tr("Failed to open file"),
Chris@193 3345 tr("<b>File open failed</b><p>File \"%1\" could not be opened").arg(path));
Chris@193 3346 } else if (status == FileOpenWrongMode) {
Chris@247 3347 emit hideSplash();
Chris@193 3348 QMessageBox::critical(this, tr("Failed to open file"),
Chris@294 3349 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 3350 }
Chris@0 3351 }
Chris@0 3352
Chris@0 3353 void
Chris@86 3354 MainWindow::openLocation()
Chris@86 3355 {
Chris@103 3356 QSettings settings;
Chris@103 3357 settings.beginGroup("MainWindow");
Chris@103 3358 QString lastLocation = settings.value("lastremote", "").toString();
Chris@103 3359
Chris@86 3360 bool ok = false;
Chris@86 3361 QString text = QInputDialog::getText
Chris@86 3362 (this, tr("Open Location"),
Chris@86 3363 tr("Please enter the URL of the location to open:"),
Chris@103 3364 QLineEdit::Normal, lastLocation, &ok);
Chris@103 3365
Chris@103 3366 if (!ok) return;
Chris@103 3367
Chris@103 3368 settings.setValue("lastremote", text);
Chris@103 3369
Chris@103 3370 if (text.isEmpty()) return;
Chris@86 3371
Chris@844 3372 FileOpenStatus status = openPath(text, AskUser);
Chris@193 3373
Chris@193 3374 if (status == FileOpenFailed) {
Chris@247 3375 emit hideSplash();
Chris@86 3376 QMessageBox::critical(this, tr("Failed to open location"),
Chris@193 3377 tr("<b>Open failed</b><p>URL \"%1\" could not be opened").arg(text));
Chris@193 3378 } else if (status == FileOpenWrongMode) {
Chris@247 3379 emit hideSplash();
Chris@193 3380 QMessageBox::critical(this, tr("Failed to open location"),
Chris@294 3381 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 3382 }
Chris@86 3383 }
Chris@86 3384
Chris@86 3385 void
Chris@1253 3386 MainWindow::openRecentFile(const QString& path)
Chris@0 3387 {
Chris@1253 3388 /* F. Nicol patch 13 Aug. 2016 */
Chris@1253 3389 #if 0
Chris@0 3390 QObject *obj = sender();
Chris@0 3391 QAction *action = dynamic_cast<QAction *>(obj);
Chris@0 3392
Chris@0 3393 if (!action) {
Chris@1770 3394 cerr << "WARNING: MainWindow::openRecentFile: sender is not an action"
Chris@1770 3395 << endl;
Chris@1770 3396 return;
Chris@0 3397 }
Chris@0 3398
Chris@0 3399 QString path = action->text();
Chris@1253 3400 #endif
Chris@1253 3401 /* End of F. Nicol patch 13 Aug. 2016 */
Chris@1253 3402
Chris@0 3403 if (path == "") return;
Chris@0 3404
Chris@844 3405 FileOpenStatus status = openPath(path, ReplaceSession);
Chris@193 3406
Chris@193 3407 if (status == FileOpenFailed) {
Chris@247 3408 emit hideSplash();
Chris@193 3409 QMessageBox::critical(this, tr("Failed to open location"),
Chris@193 3410 tr("<b>Open failed</b><p>File or URL \"%1\" could not be opened").arg(path));
Chris@193 3411 } else if (status == FileOpenWrongMode) {
Chris@247 3412 emit hideSplash();
Chris@193 3413 QMessageBox::critical(this, tr("Failed to open location"),
Chris@294 3414 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 3415 }
Chris@0 3416 }
Chris@0 3417
Chris@0 3418 void
Chris@435 3419 MainWindow::applyTemplate()
Chris@435 3420 {
Chris@435 3421 QObject *s = sender();
Chris@435 3422 QAction *action = qobject_cast<QAction *>(s);
Chris@435 3423
Chris@435 3424 if (!action) {
Chris@1770 3425 cerr << "WARNING: MainWindow::applyTemplate: sender is not an action"
Chris@1770 3426 << endl;
Chris@1770 3427 return;
Chris@435 3428 }
Chris@435 3429
Chris@435 3430 QString n = action->objectName();
Chris@435 3431 if (n == "") n = action->text();
Chris@435 3432
Chris@435 3433 if (n == "") {
Chris@665 3434 cerr << "WARNING: MainWindow::applyTemplate: sender has no name"
Chris@665 3435 << endl;
Chris@435 3436 return;
Chris@435 3437 }
Chris@435 3438
Chris@435 3439 QString mainModelLocation;
Chris@435 3440 WaveFileModel *mm = getMainModel();
Chris@435 3441 if (mm) mainModelLocation = mm->getLocation();
Chris@435 3442 if (mainModelLocation != "") {
Chris@435 3443 openAudio(mainModelLocation, ReplaceSession, n);
Chris@435 3444 } else {
Chris@435 3445 openSessionTemplate(n);
Chris@435 3446 }
Chris@435 3447 }
Chris@435 3448
Chris@435 3449 void
Chris@425 3450 MainWindow::saveSessionAsTemplate()
Chris@425 3451 {
Chris@762 3452 QDialog *d = new QDialog(this);
Chris@483 3453 d->setWindowTitle(tr("Enter template name"));
Chris@483 3454
Chris@483 3455 QGridLayout *layout = new QGridLayout;
Chris@483 3456 d->setLayout(layout);
Chris@483 3457
Chris@483 3458 layout->addWidget(new QLabel(tr("Please enter a name for the saved template:")),
Chris@483 3459 0, 0);
Chris@483 3460 QLineEdit *lineEdit = new QLineEdit;
Chris@483 3461 layout->addWidget(lineEdit, 1, 0);
Chris@483 3462 QCheckBox *makeDefault = new QCheckBox(tr("Set as default template for future audio files"));
Chris@483 3463 layout->addWidget(makeDefault, 2, 0);
Chris@425 3464
Chris@483 3465 QDialogButtonBox *bb = new QDialogButtonBox(QDialogButtonBox::Ok |
Chris@483 3466 QDialogButtonBox::Cancel);
Chris@483 3467 layout->addWidget(bb, 3, 0);
Chris@483 3468 connect(bb, SIGNAL(accepted()), d, SLOT(accept()));
Chris@483 3469 connect(bb, SIGNAL(accepted()), d, SLOT(accept()));
Chris@483 3470 connect(bb, SIGNAL(rejected()), d, SLOT(reject()));
Chris@483 3471
Chris@483 3472 if (d->exec() == QDialog::Accepted) {
Chris@483 3473
Chris@483 3474 QString name = lineEdit->text();
Chris@483 3475 name.replace(QRegExp("[^\\w\\s\\.\"'-]"), "_");
Chris@483 3476
Chris@483 3477 ResourceFinder rf;
Chris@483 3478 QString dir = rf.getResourceSaveDir("templates");
Chris@483 3479 QString filename = QString("%1/%2.svt").arg(dir).arg(name);
Chris@483 3480 if (QFile(filename).exists()) {
Chris@483 3481 if (QMessageBox::warning(this,
Chris@483 3482 tr("Template file exists"),
Chris@483 3483 tr("<b>Template file exists</b><p>The template \"%1\" already exists.<br>Overwrite it?").arg(name),
Chris@483 3484 QMessageBox::Ok | QMessageBox::Cancel,
Chris@483 3485 QMessageBox::Cancel) != QMessageBox::Ok) {
Chris@1516 3486 delete d;
Chris@483 3487 return;
Chris@483 3488 }
Chris@483 3489 }
Chris@483 3490
Chris@483 3491 if (saveSessionTemplate(filename)) {
Chris@483 3492 if (makeDefault->isChecked()) {
Chris@483 3493 setDefaultSessionTemplate(name);
Chris@483 3494 }
Chris@431 3495 }
Chris@431 3496 }
Chris@1516 3497
Chris@1516 3498 delete d;
Chris@425 3499 }
Chris@425 3500
Chris@425 3501 void
Chris@425 3502 MainWindow::manageSavedTemplates()
Chris@425 3503 {
Chris@425 3504 ResourceFinder rf;
Chris@1930 3505 openLocalFolder(rf.getResourceSaveDir("templates"));
Chris@423 3506 }
Chris@423 3507
Chris@423 3508 void
Chris@200 3509 MainWindow::paneAdded(Pane *pane)
Chris@200 3510 {
Chris@200 3511 if (m_overview) m_overview->registerView(pane);
Chris@200 3512 }
Chris@200 3513
Chris@200 3514 void
Chris@200 3515 MainWindow::paneHidden(Pane *pane)
Chris@200 3516 {
Chris@200 3517 if (m_overview) m_overview->unregisterView(pane);
Chris@200 3518 }
Chris@200 3519
Chris@200 3520 void
Chris@200 3521 MainWindow::paneAboutToBeDeleted(Pane *pane)
Chris@200 3522 {
Chris@200 3523 if (m_overview) m_overview->unregisterView(pane);
Chris@200 3524 }
Chris@200 3525
Chris@200 3526 void
Chris@193 3527 MainWindow::paneDropAccepted(Pane *pane, QStringList uriList)
Chris@193 3528 {
Chris@193 3529 if (pane) m_paneStack->setCurrentPane(pane);
Chris@193 3530
Chris@193 3531 for (QStringList::iterator i = uriList.begin(); i != uriList.end(); ++i) {
Chris@193 3532
Chris@295 3533 FileOpenStatus status;
Chris@295 3534
Chris@295 3535 if (i == uriList.begin()) {
Chris@844 3536 status = openPath(*i, ReplaceCurrentPane);
Chris@295 3537 } else {
Chris@844 3538 status = openPath(*i, CreateAdditionalModel);
Chris@295 3539 }
Chris@193 3540
Chris@193 3541 if (status == FileOpenFailed) {
Chris@247 3542 emit hideSplash();
Chris@193 3543 QMessageBox::critical(this, tr("Failed to open dropped URL"),
Chris@193 3544 tr("<b>Open failed</b><p>Dropped URL \"%1\" could not be opened").arg(*i));
Chris@295 3545 break;
Chris@193 3546 } else if (status == FileOpenWrongMode) {
Chris@247 3547 emit hideSplash();
Chris@193 3548 QMessageBox::critical(this, tr("Failed to open dropped URL"),
Chris@294 3549 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 3550 break;
Chris@295 3551 } else if (status == FileOpenCancelled) {
Chris@295 3552 break;
Chris@193 3553 }
Chris@193 3554 }
Chris@193 3555 }
Chris@193 3556
Chris@193 3557 void
Chris@193 3558 MainWindow::paneDropAccepted(Pane *pane, QString text)
Chris@193 3559 {
Chris@193 3560 if (pane) m_paneStack->setCurrentPane(pane);
Chris@193 3561
Chris@193 3562 QUrl testUrl(text);
Chris@193 3563 if (testUrl.scheme() == "file" ||
Chris@193 3564 testUrl.scheme() == "http" ||
Chris@193 3565 testUrl.scheme() == "ftp") {
Chris@193 3566 QStringList list;
Chris@193 3567 list.push_back(text);
Chris@193 3568 paneDropAccepted(pane, list);
Chris@193 3569 return;
Chris@193 3570 }
Chris@193 3571
Chris@193 3572 //!!! open as text -- but by importing as if a CSV, or just adding
Chris@193 3573 //to a text layer?
Chris@193 3574 }
Chris@193 3575
Chris@193 3576 void
Chris@0 3577 MainWindow::closeEvent(QCloseEvent *e)
Chris@0 3578 {
Chris@2027 3579 SVDEBUG << "MainWindow::closeEvent" << endl;
Chris@118 3580
Chris@136 3581 if (m_openingAudioFile) {
Chris@2027 3582 SVCERR << "Busy - ignoring close event" << endl;
Chris@1770 3583 e->ignore();
Chris@1770 3584 return;
Chris@136 3585 }
Chris@136 3586
Chris@70 3587 if (!m_abandoning && !checkSaveModified()) {
Chris@2027 3588 SVCERR << "Close refused by user - ignoring close event" << endl;
Chris@1770 3589 e->ignore();
Chris@1770 3590 return;
Chris@0 3591 }
Chris@0 3592
Chris@5 3593 QSettings settings;
Chris@5 3594 settings.beginGroup("MainWindow");
Chris@624 3595 settings.setValue("maximised", isMaximized());
Chris@624 3596 if (!isMaximized()) {
Chris@624 3597 settings.setValue("size", size());
Chris@624 3598 settings.setValue("position", pos());
Chris@624 3599 }
Chris@5 3600 settings.endGroup();
Chris@5 3601
Chris@163 3602 if (m_preferencesDialog &&
Chris@163 3603 m_preferencesDialog->isVisible()) {
Chris@1434 3604 m_preferencesDialog->applicationClosing(true);
Chris@163 3605 }
Chris@163 3606
Chris@200 3607 closeSession();
Chris@200 3608
Chris@0 3609 e->accept();
Chris@489 3610
Chris@0 3611 return;
Chris@0 3612 }
Chris@0 3613
Chris@0 3614 bool
Chris@11 3615 MainWindow::commitData(bool mayAskUser)
Chris@11 3616 {
Chris@11 3617 if (mayAskUser) {
Chris@163 3618 bool rv = checkSaveModified();
Chris@163 3619 if (rv) {
Chris@163 3620 if (m_preferencesDialog &&
Chris@163 3621 m_preferencesDialog->isVisible()) {
Chris@163 3622 m_preferencesDialog->applicationClosing(false);
Chris@163 3623 }
Chris@163 3624 }
Chris@163 3625 return rv;
Chris@11 3626 } else {
Chris@163 3627 if (m_preferencesDialog &&
Chris@163 3628 m_preferencesDialog->isVisible()) {
Chris@163 3629 m_preferencesDialog->applicationClosing(true);
Chris@163 3630 }
Chris@11 3631 if (!m_documentModified) return true;
Chris@11 3632
Chris@11 3633 // If we can't check with the user first, then we can't save
Chris@11 3634 // to the original session file (even if we have it) -- have
Chris@11 3635 // to use a temporary file
Chris@11 3636
Chris@11 3637 QString svDirBase = ".sv1";
Chris@11 3638 QString svDir = QDir::home().filePath(svDirBase);
Chris@11 3639
Chris@11 3640 if (!QFileInfo(svDir).exists()) {
Chris@11 3641 if (!QDir::home().mkdir(svDirBase)) return false;
Chris@11 3642 } else {
Chris@11 3643 if (!QFileInfo(svDir).isDir()) return false;
Chris@11 3644 }
Chris@11 3645
Chris@11 3646 // This name doesn't have to be unguessable
Chris@93 3647 #ifndef _WIN32
Chris@11 3648 QString fname = QString("tmp-%1-%2.sv")
Chris@11 3649 .arg(QDateTime::currentDateTime().toString("yyyyMMddhhmmsszzz"))
Chris@11 3650 .arg(QProcess().pid());
Chris@93 3651 #else
Chris@93 3652 QString fname = QString("tmp-%1.sv")
Chris@93 3653 .arg(QDateTime::currentDateTime().toString("yyyyMMddhhmmsszzz"));
Chris@93 3654 #endif
Chris@11 3655 QString fpath = QDir(svDir).filePath(fname);
Chris@11 3656 if (saveSessionFile(fpath)) {
Chris@34 3657 m_recentFiles.addFile(fpath);
Chris@310 3658 emit activity(tr("Export image to \"%1\"").arg(fpath));
Chris@11 3659 return true;
Chris@11 3660 } else {
Chris@11 3661 return false;
Chris@11 3662 }
Chris@11 3663 }
Chris@11 3664 }
Chris@11 3665
Chris@11 3666 bool
Chris@0 3667 MainWindow::checkSaveModified()
Chris@0 3668 {
Chris@0 3669 // Called before some destructive operation (e.g. new session,
Chris@0 3670 // exit program). Return true if we can safely proceed, false to
Chris@0 3671 // cancel.
Chris@0 3672
Chris@0 3673 if (!m_documentModified) return true;
Chris@0 3674
Chris@247 3675 emit hideSplash();
Chris@247 3676
Chris@0 3677 int button =
Chris@1770 3678 QMessageBox::warning(this,
Chris@1770 3679 tr("Session modified"),
Chris@1770 3680 tr("<b>Session modified</b><p>The current session has been modified.<br>Do you want to save it?"),
Chris@1770 3681 QMessageBox::Yes | QMessageBox::No | QMessageBox::Cancel,
Chris@165 3682 QMessageBox::Yes);
Chris@0 3683
Chris@0 3684 if (button == QMessageBox::Yes) {
Chris@1770 3685 saveSession();
Chris@1770 3686 if (m_documentModified) { // save failed -- don't proceed!
Chris@1770 3687 return false;
Chris@1770 3688 } else {
Chris@0 3689 return true; // saved, so it's safe to continue now
Chris@0 3690 }
Chris@0 3691 } else if (button == QMessageBox::No) {
Chris@1770 3692 m_documentModified = false; // so we know to abandon it
Chris@1770 3693 return true;
Chris@0 3694 }
Chris@0 3695
Chris@0 3696 // else cancel
Chris@0 3697 return false;
Chris@0 3698 }
Chris@0 3699
Chris@290 3700 bool
Chris@294 3701 MainWindow::shouldCreateNewSessionForRDFAudio(bool *cancel)
Chris@290 3702 {
Chris@294 3703 //!!! This is very similar to part of MainWindowBase::openAudio --
Chris@294 3704 //!!! make them a bit more uniform
Chris@294 3705
Chris@294 3706 QSettings settings;
Chris@294 3707 settings.beginGroup("MainWindow");
Chris@294 3708 bool prevNewSession = settings.value("newsessionforrdfaudio", true).toBool();
Chris@294 3709 settings.endGroup();
Chris@294 3710 bool newSession = true;
Chris@294 3711
Chris@294 3712 QStringList items;
Chris@294 3713 items << tr("Close the current session and create a new one")
Chris@294 3714 << tr("Add this data to the current session");
Chris@294 3715
Chris@294 3716 bool ok = false;
Chris@294 3717 QString item = ListInputDialog::getItem
Chris@294 3718 (this, tr("Select target for import"),
Chris@294 3719 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 3720 items, prevNewSession ? 0 : 1, &ok);
Chris@294 3721
Chris@294 3722 if (!ok || item.isEmpty()) {
Chris@294 3723 *cancel = true;
Chris@290 3724 return false;
Chris@290 3725 }
Chris@294 3726
Chris@294 3727 newSession = (item == items[0]);
Chris@294 3728 settings.beginGroup("MainWindow");
Chris@294 3729 settings.setValue("newsessionforrdfaudio", newSession);
Chris@294 3730 settings.endGroup();
Chris@294 3731
Chris@294 3732 if (newSession) return true;
Chris@294 3733 else return false;
Chris@290 3734 }
Chris@290 3735
Chris@0 3736 void
Chris@0 3737 MainWindow::saveSession()
Chris@0 3738 {
Chris@0 3739 if (m_sessionFile != "") {
Chris@1770 3740 if (!saveSessionFile(m_sessionFile)) {
Chris@1770 3741 QMessageBox::critical(this, tr("Failed to save file"),
Chris@1770 3742 tr("<b>Save failed</b><p>Session file \"%1\" could not be saved.").arg(m_sessionFile));
Chris@1770 3743 } else {
Chris@1770 3744 CommandHistory::getInstance()->documentSaved();
Chris@1770 3745 documentRestored();
Chris@1770 3746 }
Chris@0 3747 } else {
Chris@1770 3748 saveSessionAs();
Chris@0 3749 }
Chris@0 3750 }
Chris@0 3751
Chris@0 3752 void
Chris@0 3753 MainWindow::saveSessionAs()
Chris@0 3754 {
Chris@0 3755 QString orig = m_audioFile;
Chris@0 3756 if (orig == "") orig = ".";
Chris@0 3757 else orig = QFileInfo(orig).absoluteDir().canonicalPath();
Chris@0 3758
Chris@88 3759 QString path = getSaveFileName(FileFinder::SessionFile);
Chris@81 3760
Chris@81 3761 if (path == "") return;
Chris@0 3762
Chris@0 3763 if (!saveSessionFile(path)) {
Chris@1770 3764 QMessageBox::critical(this, tr("Failed to save file"),
Chris@1770 3765 tr("<b>Save failed</b><p>Session file \"%1\" could not be saved.").arg(path));
Chris@0 3766 } else {
Chris@1770 3767 setWindowTitle(tr("%1: %2")
Chris@518 3768 .arg(QApplication::applicationName())
Chris@1770 3769 .arg(QFileInfo(path).fileName()));
Chris@1770 3770 m_sessionFile = path;
Chris@1770 3771 CommandHistory::getInstance()->documentSaved();
Chris@1770 3772 documentRestored();
Chris@34 3773 m_recentFiles.addFile(path);
Chris@310 3774 emit activity(tr("Save session as \"%1\"").arg(path));
Chris@0 3775 }
Chris@0 3776 }
Chris@0 3777
Chris@90 3778 void
Chris@72 3779 MainWindow::preferenceChanged(PropertyContainer::PropertyName name)
Chris@72 3780 {
Chris@200 3781 MainWindowBase::preferenceChanged(name);
Chris@200 3782
Chris@1448 3783 if (name == "Background Mode") {
Chris@1448 3784 coloursChanged();
Chris@200 3785 }
Chris@0 3786 }
Chris@0 3787
Chris@0 3788 void
Chris@1448 3789 MainWindow::coloursChanged()
Chris@1448 3790 {
Chris@1448 3791 QSettings settings;
Chris@1448 3792 settings.beginGroup("Preferences");
Chris@1448 3793 QString defaultColourName(tr("Green"));
Chris@1448 3794 if (m_viewManager && m_viewManager->getGlobalDarkBackground()) {
Chris@1448 3795 defaultColourName = tr("Bright Green");
Chris@1448 3796 }
Chris@1448 3797 ColourDatabase *cdb = ColourDatabase::getInstance();
cannam@1463 3798 QColor colour = QColor
cannam@1463 3799 (settings.value("overview-colour",
cannam@1463 3800 cdb->getColour(defaultColourName).name()).toString());
Chris@1448 3801 settings.endGroup();
Chris@1448 3802
Chris@1448 3803 int index = cdb->getColourIndex(colour);
Chris@1448 3804 if (index >= 0) {
Chris@1448 3805 m_panLayer->setBaseColour(index);
Chris@1448 3806 }
Chris@1448 3807 }
Chris@1448 3808
Chris@1448 3809 void
Chris@239 3810 MainWindow::propertyStacksResized(int width)
Chris@239 3811 {
Chris@438 3812 // SVDEBUG << "MainWindow::propertyStacksResized(" << width << ")" << endl;
Chris@239 3813
Chris@239 3814 if (!m_playControlsSpacer) return;
Chris@239 3815
Chris@239 3816 int spacerWidth = width - m_playControlsWidth - 4;
Chris@239 3817
Chris@438 3818 // SVDEBUG << "resizing spacer from " << m_playControlsSpacer->width() << " to " << spacerWidth << endl;
Chris@239 3819
Chris@239 3820 m_playControlsSpacer->setFixedSize(QSize(spacerWidth, 2));
Chris@239 3821 }
Chris@239 3822
Chris@239 3823 void
Chris@0 3824 MainWindow::addPane()
Chris@0 3825 {
Chris@0 3826 QObject *s = sender();
Chris@0 3827 QAction *action = dynamic_cast<QAction *>(s);
Chris@753 3828
Chris@753 3829 cerr << "addPane: sender is " << s << ", action is " << action << ", name " << action->text() << endl;
Chris@0 3830
Chris@0 3831 if (!action) {
Chris@1770 3832 cerr << "WARNING: MainWindow::addPane: sender is not an action"
Chris@1770 3833 << endl;
Chris@1770 3834 return;
Chris@0 3835 }
Chris@0 3836
Chris@0 3837 PaneActionMap::iterator i = m_paneActions.find(action);
Chris@0 3838
Chris@0 3839 if (i == m_paneActions.end()) {
Chris@1770 3840 cerr << "WARNING: MainWindow::addPane: unknown action "
Chris@1770 3841 << action->objectName() << endl;
Chris@753 3842 cerr << "known actions are:" << endl;
Chris@753 3843 for (PaneActionMap::const_iterator i = m_paneActions.begin();
Chris@753 3844 i != m_paneActions.end(); ++i) {
Chris@753 3845 cerr << i->first << ", name " << i->first->text() << endl;
Chris@753 3846 }
Chris@1770 3847 return;
Chris@0 3848 }
Chris@0 3849
Chris@69 3850 addPane(i->second, action->text());
Chris@69 3851 }
Chris@69 3852
Chris@69 3853 void
Chris@232 3854 MainWindow::addPane(const LayerConfiguration &configuration, QString text)
Chris@69 3855 {
Chris@69 3856 CommandHistory::getInstance()->startCompoundOperation(text, true);
Chris@0 3857
Chris@0 3858 AddPaneCommand *command = new AddPaneCommand(this);
Chris@0 3859 CommandHistory::getInstance()->addCommand(command);
Chris@0 3860
Chris@0 3861 Pane *pane = command->getPane();
Chris@0 3862
Chris@69 3863 if (configuration.layer == LayerFactory::Spectrum) {
Chris@109 3864 pane->setPlaybackFollow(PlaybackScrollContinuous);
Chris@110 3865 pane->setFollowGlobalZoom(false);
Chris@2011 3866 pane->setZoomLevel(ZoomLevel(ZoomLevel::FramesPerPixel, 512));
Chris@7 3867 }
Chris@7 3868
Chris@69 3869 if (configuration.layer != LayerFactory::TimeRuler &&
Chris@69 3870 configuration.layer != LayerFactory::Spectrum) {
Chris@8 3871
Chris@1770 3872 if (!m_timeRulerLayer) {
Chris@1770 3873 // cerr << "no time ruler layer, creating one" << endl;
Chris@1770 3874 m_timeRulerLayer = m_document->createMainModelLayer
Chris@1770 3875 (LayerFactory::TimeRuler);
Chris@1770 3876 }
Chris@1770 3877
Chris@1770 3878 // SVDEBUG << "adding time ruler layer " << m_timeRulerLayer << endl;
Chris@1770 3879
Chris@1770 3880 m_document->addLayerToView(pane, m_timeRulerLayer);
Chris@0 3881 }
Chris@0 3882
Chris@69 3883 Layer *newLayer = m_document->createLayer(configuration.layer);
Chris@69 3884
Chris@69 3885 Model *suggestedModel = configuration.sourceModel;
Chris@66 3886 Model *model = 0;
Chris@66 3887
Chris@66 3888 if (suggestedModel) {
Chris@66 3889
Chris@66 3890 // check its validity
Chris@224 3891 std::vector<Model *> inputModels = m_document->getTransformInputModels();
Chris@66 3892 for (size_t j = 0; j < inputModels.size(); ++j) {
Chris@66 3893 if (inputModels[j] == suggestedModel) {
Chris@66 3894 model = suggestedModel;
Chris@66 3895 break;
Chris@66 3896 }
Chris@66 3897 }
Chris@66 3898
Chris@66 3899 if (!model) {
Chris@665 3900 cerr << "WARNING: Model " << (void *)suggestedModel
Chris@66 3901 << " appears in pane action map, but is not reported "
Chris@665 3902 << "by document as a valid transform source" << endl;
Chris@66 3903 }
Chris@66 3904 }
Chris@66 3905
Chris@346 3906 if (!model) {
Chris@346 3907 model = m_document->getMainModel();
Chris@346 3908 }
Chris@66 3909
Chris@66 3910 m_document->setModel(newLayer, model);
Chris@66 3911
Chris@69 3912 m_document->setChannel(newLayer, configuration.channel);
Chris@0 3913 m_document->addLayerToView(pane, newLayer);
Chris@0 3914
Chris@0 3915 m_paneStack->setCurrentPane(pane);
Chris@70 3916 m_paneStack->setCurrentLayer(pane, newLayer);
Chris@0 3917
Chris@438 3918 // SVDEBUG << "MainWindow::addPane: global centre frame is "
Chris@433 3919 // << m_viewManager->getGlobalCentreFrame() << endl;
Chris@130 3920 // pane->setCentreFrame(m_viewManager->getGlobalCentreFrame());
Chris@73 3921
Chris@0 3922 CommandHistory::getInstance()->endCompoundOperation();
Chris@0 3923
Chris@0 3924 updateMenuStates();
Chris@0 3925 }
Chris@0 3926
Chris@0 3927 void
Chris@0 3928 MainWindow::addLayer()
Chris@0 3929 {
Chris@0 3930 QObject *s = sender();
Chris@0 3931 QAction *action = dynamic_cast<QAction *>(s);
Chris@0 3932
Chris@0 3933 if (!action) {
Chris@1770 3934 cerr << "WARNING: MainWindow::addLayer: sender is not an action"
Chris@1770 3935 << endl;
Chris@1770 3936 return;
Chris@0 3937 }
Chris@0 3938
Chris@0 3939 Pane *pane = m_paneStack->getCurrentPane();
Chris@0 3940
Chris@0 3941 if (!pane) {
Chris@1770 3942 cerr << "WARNING: MainWindow::addLayer: no current pane" << endl;
Chris@1770 3943 return;
Chris@0 3944 }
Chris@0 3945
Chris@0 3946 ExistingLayerActionMap::iterator ei = m_existingLayerActions.find(action);
Chris@0 3947
Chris@0 3948 if (ei != m_existingLayerActions.end()) {
Chris@1770 3949 Layer *newLayer = ei->second;
Chris@1770 3950 m_document->addLayerToView(pane, newLayer);
Chris@1770 3951 m_paneStack->setCurrentLayer(pane, newLayer);
Chris@1770 3952 return;
Chris@0 3953 }
Chris@0 3954
Chris@95 3955 ei = m_sliceActions.find(action);
Chris@95 3956
Chris@95 3957 if (ei != m_sliceActions.end()) {
Chris@95 3958 Layer *newLayer = m_document->createLayer(LayerFactory::Slice);
Chris@95 3959 // document->setModel(newLayer, ei->second->getModel());
Chris@95 3960 SliceableLayer *source = dynamic_cast<SliceableLayer *>(ei->second);
Chris@95 3961 SliceLayer *dest = dynamic_cast<SliceLayer *>(newLayer);
Chris@95 3962 if (source && dest) {
Chris@205 3963 //!!!???
Chris@95 3964 dest->setSliceableModel(source->getSliceableModel());
Chris@95 3965 connect(source, SIGNAL(sliceableModelReplaced(const Model *, const Model *)),
Chris@95 3966 dest, SLOT(sliceableModelReplaced(const Model *, const Model *)));
Chris@95 3967 connect(m_document, SIGNAL(modelAboutToBeDeleted(Model *)),
Chris@95 3968 dest, SLOT(modelAboutToBeDeleted(Model *)));
Chris@95 3969 }
Chris@1770 3970 m_document->addLayerToView(pane, newLayer);
Chris@1770 3971 m_paneStack->setCurrentLayer(pane, newLayer);
Chris@1770 3972 return;
Chris@95 3973 }
Chris@95 3974
Chris@211 3975 TransformActionMap::iterator i = m_transformActions.find(action);
Chris@34 3976
Chris@34 3977 if (i == m_transformActions.end()) {
Chris@0 3978
Chris@1770 3979 LayerActionMap::iterator i = m_layerActions.find(action);
Chris@1770 3980
Chris@1770 3981 if (i == m_layerActions.end()) {
Chris@1770 3982 cerr << "WARNING: MainWindow::addLayer: unknown action "
Chris@1770 3983 << action->objectName() << endl;
Chris@1770 3984 return;
Chris@1770 3985 }
Chris@1770 3986
Chris@1770 3987 LayerFactory::LayerType type = i->second.layer;
Chris@1770 3988
Chris@1770 3989 LayerFactory::LayerTypeSet emptyTypes =
Chris@1770 3990 LayerFactory::getInstance()->getValidEmptyLayerTypes();
Chris@1770 3991
Chris@1770 3992 Layer *newLayer = 0;
Chris@1770 3993
Chris@1770 3994 if (emptyTypes.find(type) != emptyTypes.end()) {
Chris@1770 3995
Chris@1770 3996 newLayer = m_document->createEmptyLayer(type);
Chris@217 3997 if (newLayer) {
Chris@217 3998 m_toolActions[ViewManager::DrawMode]->trigger();
Chris@217 3999 }
Chris@0 4000
Chris@1770 4001 } else {
Chris@0 4002
Chris@346 4003 Model *model = i->second.sourceModel;
Chris@346 4004
Chris@415 4005 cerr << "model = "<< model << endl;
Chris@415 4006
Chris@346 4007 if (!model) {
Chris@346 4008 if (type == LayerFactory::TimeRuler) {
Chris@346 4009 newLayer = m_document->createMainModelLayer(type);
Chris@346 4010 } else {
Chris@346 4011 // if model is unspecified and this is not a
Chris@415 4012 // time-ruler layer, use any plausible model from
Chris@415 4013 // the current pane -- this is the case for
Chris@415 4014 // right-button menu layer additions
Chris@415 4015 Pane::ModelSet ms = pane->getModels();
Chris@415 4016 foreach (Model *m, ms) {
Chris@415 4017 RangeSummarisableTimeValueModel *r =
Chris@415 4018 dynamic_cast<RangeSummarisableTimeValueModel *>(m);
Chris@415 4019 if (r) model = m;
Chris@346 4020 }
Chris@346 4021 if (!model) model = getMainModel();
Chris@346 4022 }
Chris@346 4023 }
Chris@346 4024
Chris@346 4025 if (model) {
Chris@238 4026 newLayer = m_document->createLayer(type);
Chris@346 4027 if (m_document->isKnownModel(model)) {
Chris@238 4028 m_document->setChannel(newLayer, i->second.channel);
Chris@346 4029 m_document->setModel(newLayer, model);
Chris@238 4030 } else {
Chris@665 4031 cerr << "WARNING: MainWindow::addLayer: unknown model "
Chris@346 4032 << model
Chris@238 4033 << " (\""
Chris@838 4034 << model->objectName()
Chris@238 4035 << "\") in layer action map"
Chris@665 4036 << endl;
Chris@238 4037 }
Chris@232 4038 }
Chris@238 4039 }
Chris@0 4040
Chris@217 4041 if (newLayer) {
Chris@217 4042 m_document->addLayerToView(pane, newLayer);
Chris@217 4043 m_paneStack->setCurrentLayer(pane, newLayer);
Chris@217 4044 }
Chris@0 4045
Chris@1770 4046 return;
Chris@0 4047 }
Chris@0 4048
Chris@224 4049 //!!! want to do something like this, but it's not supported in
Chris@224 4050 //ModelTransformerFactory yet
Chris@224 4051 /*
Chris@0 4052 int channel = -1;
Chris@0 4053 // pick up the default channel from any existing layers on the same pane
Chris@0 4054 for (int j = 0; j < pane->getLayerCount(); ++j) {
Chris@1770 4055 int c = LayerFactory::getInstance()->getChannel(pane->getLayer(j));
Chris@1770 4056 if (c != -1) {
Chris@1770 4057 channel = c;
Chris@1770 4058 break;
Chris@1770 4059 }
Chris@0 4060 }
Chris@224 4061 */
Chris@0 4062
Chris@33 4063 // We always ask for configuration, even if the plugin isn't
Chris@33 4064 // supposed to be configurable, because we need to let the user
Chris@33 4065 // change the execution context (block size etc).
Chris@0 4066
Chris@224 4067 QString transformId = i->second;
Chris@274 4068
Chris@274 4069 addLayer(transformId);
Chris@274 4070 }
Chris@274 4071
Chris@274 4072 void
Chris@274 4073 MainWindow::addLayer(QString transformId)
Chris@274 4074 {
Chris@274 4075 Pane *pane = m_paneStack->getCurrentPane();
Chris@274 4076 if (!pane) {
Chris@1770 4077 cerr << "WARNING: MainWindow::addLayer: no current pane" << endl;
Chris@1770 4078 return;
Chris@274 4079 }
Chris@274 4080
Chris@1558 4081 Transform transform;
Chris@1558 4082 try {
Chris@1558 4083 transform = TransformFactory::getInstance()->
Chris@1558 4084 getDefaultTransformFor(transformId);
Chris@1558 4085 } catch (std::exception &e) { // e.g. Piper server failure
Chris@1558 4086 QMessageBox::critical
Chris@1558 4087 (this, tr("Failed to query transform attributes"),
Chris@1558 4088 tr("<b>Failed to query transform attributes</b><p>Plugin or server error: %1</p>")
Chris@1558 4089 .arg(e.what()));
Chris@1558 4090 return;
Chris@1558 4091 }
Chris@27 4092
Chris@66 4093 std::vector<Model *> candidateInputModels =
Chris@224 4094 m_document->getTransformInputModels();
Chris@53 4095
Chris@219 4096 Model *defaultInputModel = 0;
Chris@895 4097
Chris@219 4098 for (int j = 0; j < pane->getLayerCount(); ++j) {
Chris@895 4099
Chris@219 4100 Layer *layer = pane->getLayer(j);
Chris@219 4101 if (!layer) continue;
Chris@895 4102
Chris@243 4103 if (LayerFactory::getInstance()->getLayerType(layer) !=
Chris@243 4104 LayerFactory::Waveform &&
Chris@243 4105 !layer->isLayerOpaque()) continue;
Chris@895 4106
Chris@219 4107 Model *model = layer->getModel();
Chris@219 4108 if (!model) continue;
Chris@895 4109
Chris@219 4110 for (size_t k = 0; k < candidateInputModels.size(); ++k) {
Chris@219 4111 if (candidateInputModels[k] == model) {
Chris@219 4112 defaultInputModel = model;
Chris@219 4113 break;
Chris@219 4114 }
Chris@219 4115 }
Chris@895 4116
Chris@219 4117 if (defaultInputModel) break;
Chris@219 4118 }
Chris@895 4119
Chris@1620 4120 AggregateWaveModel *aggregate = 0;
Chris@1620 4121
Chris@895 4122 if (candidateInputModels.size() > 1) {
Chris@895 4123 // Add an aggregate model as another option
Chris@895 4124 AggregateWaveModel::ChannelSpecList sl;
Chris@895 4125 foreach (Model *m, candidateInputModels) {
Chris@895 4126 RangeSummarisableTimeValueModel *r =
Chris@895 4127 qobject_cast<RangeSummarisableTimeValueModel *>(m);
Chris@895 4128 if (r) {
Chris@895 4129 sl.push_back(AggregateWaveModel::ModelChannelSpec(r, -1));
Chris@895 4130 }
Chris@895 4131 }
Chris@895 4132 if (!sl.empty()) {
Chris@1620 4133 aggregate = new AggregateWaveModel(sl);
Chris@895 4134 aggregate->setObjectName(tr("Multiplex all of the above"));
Chris@895 4135 candidateInputModels.push_back(aggregate);
Chris@895 4136 }
Chris@895 4137 }
Chris@219 4138
Chris@914 4139 sv_frame_t startFrame = 0, duration = 0;
Chris@914 4140 sv_frame_t endFrame = 0;
Chris@184 4141 m_viewManager->getSelection().getExtents(startFrame, endFrame);
Chris@184 4142 if (endFrame > startFrame) duration = endFrame - startFrame;
Chris@184 4143 else startFrame = 0;
Chris@184 4144
Chris@357 4145 TransformUserConfigurator configurator;
Chris@357 4146
Chris@224 4147 ModelTransformer::Input input = ModelTransformerFactory::getInstance()->
Chris@224 4148 getConfigurationForTransform
Chris@211 4149 (transform,
Chris@211 4150 candidateInputModels,
Chris@219 4151 defaultInputModel,
Chris@211 4152 m_playSource,
Chris@211 4153 startFrame,
Chris@357 4154 duration,
Chris@357 4155 &configurator);
Chris@211 4156
Chris@1623 4157 if (aggregate) {
Chris@1623 4158 if (input.getModel() == aggregate) {
Chris@1623 4159 aggregate->setObjectName(tr("Multiplexed audio"));
Chris@1623 4160 m_document->addAggregateModel(aggregate);
Chris@1623 4161 } else {
Chris@1623 4162 aggregate->aboutToDelete();
Chris@1623 4163 delete aggregate;
Chris@1623 4164 }
Chris@1620 4165 }
Chris@1620 4166
Chris@224 4167 if (!input.getModel()) return;
Chris@224 4168
Chris@438 4169 // SVDEBUG << "MainWindow::addLayer: Input model is " << input.getModel() << " \"" << input.getModel()->objectName() << "\"" << endl << "transform:" << endl << transform.toXmlString() << endl;
Chris@224 4170
Chris@1558 4171 try {
Chris@1558 4172 Layer *newLayer = m_document->createDerivedLayer(transform, input);
Chris@1558 4173 if (newLayer) {
Chris@1558 4174 m_document->addLayerToView(pane, newLayer);
Chris@1558 4175 m_document->setChannel(newLayer, input.getChannel());
Chris@1558 4176 m_recentTransforms.add(transformId);
Chris@1558 4177 m_paneStack->setCurrentLayer(pane, newLayer);
Chris@1558 4178 }
Chris@1558 4179 } catch (std::exception &e) { // e.g. Piper server failure
Chris@1558 4180 QMessageBox::critical
Chris@1558 4181 (this, tr("Transform failed"),
Chris@1558 4182 tr("<b>Failed to run transform</b><p>Plugin or server error: %1</p>")
Chris@1558 4183 .arg(e.what()));
Chris@1558 4184 return;
Chris@0 4185 }
Chris@1558 4186
Chris@0 4187 updateMenuStates();
Chris@0 4188 }
Chris@0 4189
Chris@0 4190 void
Chris@0 4191 MainWindow::renameCurrentLayer()
Chris@0 4192 {
Chris@0 4193 Pane *pane = m_paneStack->getCurrentPane();
Chris@0 4194 if (pane) {
Chris@1770 4195 Layer *layer = pane->getSelectedLayer();
Chris@1770 4196 if (layer) {
Chris@1770 4197 bool ok = false;
Chris@1770 4198 QString newName = QInputDialog::getText
Chris@1770 4199 (this, tr("Rename Layer"),
Chris@1770 4200 tr("New name for this layer:"),
Chris@1770 4201 QLineEdit::Normal, layer->objectName(), &ok);
Chris@1770 4202 if (ok) {
Chris@1770 4203 layer->setPresentationName(newName);
Chris@1770 4204 setupExistingLayersMenus();
Chris@1770 4205 }
Chris@1770 4206 }
Chris@0 4207 }
Chris@0 4208 }
Chris@0 4209
Chris@0 4210 void
Chris@272 4211 MainWindow::findTransform()
Chris@272 4212 {
Chris@274 4213 TransformFinder *finder = new TransformFinder(this);
Chris@274 4214 if (!finder->exec()) {
Chris@274 4215 delete finder;
Chris@274 4216 return;
Chris@273 4217 }
Chris@274 4218 TransformId transform = finder->getTransform();
Chris@274 4219 delete finder;
Chris@287 4220
Chris@287 4221 if (getMainModel() != 0 && m_paneStack->getCurrentPane() != 0) {
Chris@287 4222 addLayer(transform);
Chris@287 4223 }
Chris@272 4224 }
Chris@272 4225
Chris@272 4226 void
Chris@207 4227 MainWindow::playSoloToggled()
Chris@207 4228 {
Chris@207 4229 MainWindowBase::playSoloToggled();
Chris@207 4230 m_soloModified = true;
Chris@207 4231 }
Chris@207 4232
Chris@207 4233 void
Chris@206 4234 MainWindow::alignToggled()
Chris@206 4235 {
Chris@206 4236 QAction *action = dynamic_cast<QAction *>(sender());
Chris@206 4237
Chris@207 4238 if (!m_viewManager) return;
Chris@207 4239
Chris@206 4240 if (action) {
Chris@1770 4241 m_viewManager->setAlignMode(action->isChecked());
Chris@206 4242 } else {
Chris@1770 4243 m_viewManager->setAlignMode(!m_viewManager->getAlignMode());
Chris@206 4244 }
Chris@206 4245
Chris@206 4246 if (m_viewManager->getAlignMode()) {
Chris@207 4247 m_prevSolo = m_soloAction->isChecked();
Chris@208 4248 if (!m_soloAction->isChecked()) {
Chris@208 4249 m_soloAction->setChecked(true);
Chris@208 4250 MainWindowBase::playSoloToggled();
Chris@208 4251 }
Chris@207 4252 m_soloModified = false;
Chris@207 4253 emit canChangeSolo(false);
Chris@206 4254 m_document->alignModels();
Chris@206 4255 m_document->setAutoAlignment(true);
Chris@206 4256 } else {
Chris@207 4257 if (!m_soloModified) {
Chris@208 4258 if (m_soloAction->isChecked() != m_prevSolo) {
Chris@208 4259 m_soloAction->setChecked(m_prevSolo);
Chris@208 4260 MainWindowBase::playSoloToggled();
Chris@208 4261 }
Chris@207 4262 }
Chris@207 4263 emit canChangeSolo(true);
Chris@206 4264 m_document->setAutoAlignment(false);
Chris@206 4265 }
Chris@206 4266
Chris@206 4267 for (int i = 0; i < m_paneStack->getPaneCount(); ++i) {
Chris@206 4268
Chris@1770 4269 Pane *pane = m_paneStack->getPane(i);
Chris@1770 4270 if (!pane) continue;
Chris@206 4271
Chris@206 4272 pane->update();
Chris@206 4273 }
Chris@206 4274 }
Chris@206 4275
Chris@206 4276 void
Chris@59 4277 MainWindow::playSpeedChanged(int position)
Chris@0 4278 {
Chris@1031 4279 PlaySpeedRangeMapper mapper;
Chris@60 4280
Chris@922 4281 double percent = m_playSpeed->mappedValue();
Chris@922 4282 double factor = mapper.getFactorForValue(percent);
Chris@60 4283
Chris@1031 4284 // cerr << "play speed position = " << position << " (range 0-120) percent = " << percent << " factor = " << factor << endl;
Chris@1031 4285
Chris@1031 4286 int centre = m_playSpeed->defaultValue();
Chris@1031 4287
Chris@1031 4288 // Percentage is shown to 0dp if >100, to 1dp if <100; factor is
Chris@1031 4289 // shown to 3sf
Chris@1031 4290
Chris@1031 4291 char pcbuf[30];
Chris@1031 4292 char facbuf[30];
Chris@1031 4293
Chris@1031 4294 if (position == centre) {
Chris@155 4295 contextHelpChanged(tr("Playback speed: Normal"));
Chris@1031 4296 } else if (position < centre) {
Chris@1031 4297 sprintf(pcbuf, "%.1f", percent);
Chris@1031 4298 sprintf(facbuf, "%.3g", 1.0 / factor);
Chris@1031 4299 contextHelpChanged(tr("Playback speed: %1% (%2x slower)")
Chris@1031 4300 .arg(pcbuf)
Chris@1031 4301 .arg(facbuf));
Chris@155 4302 } else {
Chris@1031 4303 sprintf(pcbuf, "%.0f", percent);
Chris@1031 4304 sprintf(facbuf, "%.3g", factor);
Chris@1031 4305 contextHelpChanged(tr("Playback speed: %1% (%2x faster)")
Chris@1031 4306 .arg(pcbuf)
Chris@1031 4307 .arg(facbuf));
Chris@155 4308 }
Chris@155 4309
Chris@1031 4310 m_playSource->setTimeStretch(1.0 / factor); // factor is a speedup
Chris@155 4311
Chris@155 4312 updateMenuStates();
Chris@16 4313 }
Chris@16 4314
Chris@26 4315 void
Chris@155 4316 MainWindow::speedUpPlayback()
Chris@155 4317 {
Chris@155 4318 int value = m_playSpeed->value();
Chris@155 4319 value = value + m_playSpeed->pageStep();
Chris@155 4320 if (value > m_playSpeed->maximum()) value = m_playSpeed->maximum();
Chris@155 4321 m_playSpeed->setValue(value);
Chris@155 4322 }
Chris@155 4323
Chris@155 4324 void
Chris@155 4325 MainWindow::slowDownPlayback()
Chris@155 4326 {
Chris@155 4327 int value = m_playSpeed->value();
Chris@155 4328 value = value - m_playSpeed->pageStep();
Chris@155 4329 if (value < m_playSpeed->minimum()) value = m_playSpeed->minimum();
Chris@155 4330 m_playSpeed->setValue(value);
Chris@155 4331 }
Chris@155 4332
Chris@155 4333 void
Chris@155 4334 MainWindow::restoreNormalPlayback()
Chris@155 4335 {
Chris@155 4336 m_playSpeed->setValue(m_playSpeed->defaultValue());
Chris@155 4337 }
Chris@155 4338
Chris@155 4339 void
Chris@227 4340 MainWindow::currentPaneChanged(Pane *pane)
Chris@227 4341 {
Chris@228 4342 MainWindowBase::currentPaneChanged(pane);
Chris@228 4343
Chris@227 4344 if (!pane || !m_panLayer) return;
Chris@761 4345
Chris@761 4346 // If this pane contains the main model, it usually makes sense to
Chris@761 4347 // show the main model in the pan layer even if it isn't the top
Chris@761 4348 // layer in the pane (e.g. if the top layer is one derived from
Chris@761 4349 // the main model).
Chris@761 4350 bool containsMainModel = false;
Chris@761 4351 for (int i = pane->getLayerCount(); i > 0; ) {
Chris@761 4352 --i;
Chris@761 4353 Layer *layer = pane->getLayer(i);
Chris@761 4354 if (layer &&
Chris@761 4355 LayerFactory::getInstance()->getLayerType(layer) ==
Chris@761 4356 LayerFactory::Waveform &&
Chris@761 4357 layer->getModel() == getMainModel()) {
Chris@761 4358 containsMainModel = true;
Chris@761 4359 break;
Chris@761 4360 }
Chris@761 4361 }
Chris@761 4362
Chris@1794 4363 bool panLayerSet = false;
Chris@1794 4364
Chris@227 4365 for (int i = pane->getLayerCount(); i > 0; ) {
Chris@227 4366 --i;
Chris@227 4367 Layer *layer = pane->getLayer(i);
Chris@1794 4368 RangeSummarisableTimeValueModel *tvm =
Chris@1794 4369 qobject_cast<RangeSummarisableTimeValueModel *>(layer->getModel());
Chris@1794 4370 if (tvm) {
Chris@1794 4371 auto type = LayerFactory::getInstance()->getLayerType(layer);
Chris@1794 4372 if (type != LayerFactory::TimeRuler) {
Chris@1794 4373 updateLayerShortcutsFor(tvm);
Chris@1794 4374 }
Chris@1794 4375 if (type == LayerFactory::Waveform) {
Chris@227 4376 m_panLayer->setModel(tvm);
Chris@1794 4377 panLayerSet = true;
Chris@1794 4378 break;
Chris@227 4379 }
Chris@227 4380 }
Chris@227 4381 }
Chris@1794 4382
Chris@1794 4383 if (containsMainModel && !panLayerSet) {
Chris@1794 4384 m_panLayer->setModel(getMainModel());
Chris@1794 4385 }
Chris@227 4386 }
Chris@227 4387
Chris@227 4388 void
Chris@116 4389 MainWindow::updateVisibleRangeDisplay(Pane *p) const
Chris@116 4390 {
Chris@116 4391 if (!getMainModel() || !p) {
Chris@116 4392 return;
Chris@116 4393 }
Chris@116 4394
Chris@117 4395 bool haveSelection = false;
Chris@922 4396 sv_frame_t startFrame = 0, endFrame = 0;
Chris@117 4397
Chris@117 4398 if (m_viewManager && m_viewManager->haveInProgressSelection()) {
Chris@117 4399
Chris@117 4400 bool exclusive = false;
Chris@117 4401 Selection s = m_viewManager->getInProgressSelection(exclusive);
Chris@117 4402
Chris@117 4403 if (!s.isEmpty()) {
Chris@117 4404 haveSelection = true;
Chris@117 4405 startFrame = s.getStartFrame();
Chris@117 4406 endFrame = s.getEndFrame();
Chris@117 4407 }
Chris@117 4408 }
Chris@117 4409
Chris@117 4410 if (!haveSelection) {
Chris@117 4411 startFrame = p->getFirstVisibleFrame();
Chris@117 4412 endFrame = p->getLastVisibleFrame();
Chris@117 4413 }
Chris@117 4414
Chris@116 4415 RealTime start = RealTime::frame2RealTime
Chris@117 4416 (startFrame, getMainModel()->getSampleRate());
Chris@116 4417
Chris@116 4418 RealTime end = RealTime::frame2RealTime
Chris@117 4419 (endFrame, getMainModel()->getSampleRate());
Chris@116 4420
Chris@116 4421 RealTime duration = end - start;
Chris@116 4422
Chris@116 4423 QString startStr, endStr, durationStr;
Chris@116 4424 startStr = start.toText(true).c_str();
Chris@116 4425 endStr = end.toText(true).c_str();
Chris@116 4426 durationStr = duration.toText(true).c_str();
Chris@116 4427
Chris@117 4428 if (haveSelection) {
Chris@117 4429 m_myStatusMessage = tr("Selection: %1 to %2 (duration %3)")
Chris@117 4430 .arg(startStr).arg(endStr).arg(durationStr);
Chris@117 4431 } else {
Chris@117 4432 m_myStatusMessage = tr("Visible: %1 to %2 (duration %3)")
Chris@117 4433 .arg(startStr).arg(endStr).arg(durationStr);
Chris@117 4434 }
Chris@116 4435
Chris@739 4436 if (getStatusLabel()->text() != m_myStatusMessage) {
Chris@739 4437 getStatusLabel()->setText(m_myStatusMessage);
Chris@737 4438 }
Chris@340 4439
Chris@340 4440 updatePositionStatusDisplays();
Chris@340 4441 }
Chris@340 4442
Chris@340 4443 void
Chris@340 4444 MainWindow::updatePositionStatusDisplays() const
Chris@340 4445 {
Chris@340 4446 if (!statusBar()->isVisible()) return;
Chris@340 4447
Chris@340 4448 Pane *pane = 0;
Chris@922 4449 sv_frame_t frame = m_viewManager->getPlaybackFrame();
Chris@340 4450
Chris@340 4451 if (m_paneStack) pane = m_paneStack->getCurrentPane();
Chris@340 4452 if (!pane) return;
Chris@340 4453
Chris@340 4454 int layers = pane->getLayerCount();
Chris@340 4455 if (layers == 0) m_currentLabel->setText("");
Chris@340 4456
Chris@340 4457 for (int i = layers-1; i >= 0; --i) {
Chris@340 4458 Layer *layer = pane->getLayer(i);
Chris@340 4459 if (!layer) continue;
Chris@340 4460 if (!layer->isLayerEditable()) continue;
Chris@340 4461 QString label = layer->getLabelPreceding
Chris@340 4462 (pane->alignFromReference(frame));
Chris@340 4463 m_currentLabel->setText(label);
Chris@340 4464 break;
Chris@340 4465 }
Chris@116 4466 }
Chris@116 4467
Chris@116 4468 void
Chris@1476 4469 MainWindow::monitoringLevelsChanged(float left, float right)
Chris@0 4470 {
Chris@1386 4471 m_mainLevelPan->setMonitoringLevels(left, right);
Chris@0 4472 }
Chris@0 4473
Chris@0 4474 void
Chris@921 4475 MainWindow::sampleRateMismatch(sv_samplerate_t requested,
Chris@921 4476 sv_samplerate_t actual,
Chris@0 4477 bool willResample)
Chris@0 4478 {
Chris@0 4479 if (!willResample) {
Chris@247 4480 emit hideSplash();
Chris@0 4481 QMessageBox::information
Chris@0 4482 (this, tr("Sample rate mismatch"),
Chris@193 4483 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 4484 .arg(requested).arg(actual));
Chris@0 4485 }
Chris@0 4486
Chris@0 4487 updateDescriptionLabel();
Chris@0 4488 }
Chris@0 4489
Chris@0 4490 void
Chris@42 4491 MainWindow::audioOverloadPluginDisabled()
Chris@42 4492 {
Chris@42 4493 QMessageBox::information
Chris@42 4494 (this, tr("Audio processing overload"),
Chris@193 4495 tr("<b>Overloaded</b><p>Audio effects plugin auditioning has been disabled due to a processing overload."));
Chris@42 4496 }
Chris@42 4497
Chris@42 4498 void
Chris@266 4499 MainWindow::audioTimeStretchMultiChannelDisabled()
Chris@266 4500 {
Chris@266 4501 static bool shownOnce = false;
Chris@266 4502 if (shownOnce) return;
Chris@266 4503 QMessageBox::information
Chris@266 4504 (this, tr("Audio processing overload"),
Chris@266 4505 tr("<b>Overloaded</b><p>Audio playback speed processing has been reduced to a single channel, due to a processing overload."));
Chris@266 4506 shownOnce = true;
Chris@266 4507 }
Chris@266 4508
Chris@1630 4509 /*
Chris@266 4510 void
Chris@1521 4511 MainWindow::betaReleaseWarning()
Chris@1521 4512 {
Chris@1521 4513 QMessageBox::information
Chris@1521 4514 (this, tr("Beta release"),
Chris@1521 4515 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 4516 }
Chris@1630 4517 */
Chris@1521 4518
Chris@1521 4519 void
Chris@1148 4520 MainWindow::pluginPopulationWarning()
Chris@1087 4521 {
Chris@1277 4522 QString scanWarning = PluginScan::getInstance()->getStartupFailureReport();
Chris@1277 4523 QString factWarning = TransformFactory::getInstance()->getStartupFailureReport();
Chris@1277 4524 QString warning;
Chris@1277 4525 if (factWarning != "") {
Chris@1277 4526 // The order of events on startup implies that, if scanWarning
Chris@1277 4527 // and factWarning are both present, then we have already been
Chris@1277 4528 // called once for scanWarning so don't want to report it again
Chris@1277 4529 warning = factWarning;
Chris@1277 4530 } else if (scanWarning != "") {
Chris@1277 4531 warning = scanWarning;
Chris@1277 4532 }
Chris@1277 4533 if (warning != "") {
Chris@1277 4534 emit hideSplash();
Chris@2067 4535 QMessageBox box;
Chris@2067 4536 box.setWindowTitle(tr("Problems loading plugins"));
Chris@2067 4537 box.setText(tr("<b>Failed to load plugins</b>"));
Chris@2067 4538 box.setInformativeText(warning);
Chris@2067 4539 box.setIcon(QMessageBox::Warning);
Chris@2067 4540 box.setStandardButtons(QMessageBox::Ok);
Chris@2067 4541 box.exec();
Chris@1277 4542 }
Chris@1087 4543 }
Chris@1087 4544
Chris@1087 4545 void
Chris@304 4546 MainWindow::midiEventsAvailable()
Chris@304 4547 {
Chris@307 4548 Pane *currentPane = 0;
Chris@307 4549 NoteLayer *currentNoteLayer = 0;
Chris@309 4550 TimeValueLayer *currentTimeValueLayer = 0;
Chris@307 4551
Chris@793 4552 if (m_paneStack) {
Chris@793 4553 currentPane = m_paneStack->getCurrentPane();
Chris@793 4554 }
Chris@793 4555
Chris@307 4556 if (currentPane) {
Chris@307 4557 currentNoteLayer = dynamic_cast<NoteLayer *>
Chris@307 4558 (currentPane->getSelectedLayer());
Chris@309 4559 currentTimeValueLayer = dynamic_cast<TimeValueLayer *>
Chris@309 4560 (currentPane->getSelectedLayer());
Chris@838 4561 } else {
Chris@793 4562 // discard these events
Chris@793 4563 while (m_midiInput->getEventsAvailable() > 0) {
Chris@793 4564 (void)m_midiInput->readEvent();
Chris@793 4565 }
Chris@793 4566 return;
Chris@793 4567 }
Chris@793 4568
Chris@305 4569 // This is called through a serialised signal/slot invocation
Chris@305 4570 // (across threads). It could happen quite some time after the
Chris@305 4571 // event was actually received, which is why event timestamping
Chris@305 4572 // happens in the MIDI input class and not here.
Chris@307 4573
Chris@305 4574 while (m_midiInput->getEventsAvailable() > 0) {
Chris@308 4575
Chris@305 4576 MIDIEvent ev(m_midiInput->readEvent());
Chris@307 4577
Chris@922 4578 sv_frame_t frame = currentPane->alignFromReference(ev.getTime());
Chris@309 4579
Chris@308 4580 bool noteOn = (ev.getMessageType() == MIDIConstants::MIDI_NOTE_ON &&
Chris@308 4581 ev.getVelocity() > 0);
Chris@308 4582
Chris@308 4583 bool noteOff = (ev.getMessageType() == MIDIConstants::MIDI_NOTE_OFF ||
Chris@308 4584 (ev.getMessageType() == MIDIConstants::MIDI_NOTE_ON &&
Chris@308 4585 ev.getVelocity() == 0));
Chris@308 4586
Chris@307 4587 if (currentNoteLayer) {
Chris@307 4588
Chris@310 4589 if (!m_playSource || !m_playSource->isPlaying()) continue;
Chris@310 4590
Chris@308 4591 if (noteOn) {
Chris@307 4592
Chris@309 4593 currentNoteLayer->addNoteOn(frame,
Chris@307 4594 ev.getPitch(),
Chris@307 4595 ev.getVelocity());
Chris@307 4596
Chris@308 4597 } else if (noteOff) {
Chris@307 4598
Chris@309 4599 currentNoteLayer->addNoteOff(frame,
Chris@307 4600 ev.getPitch());
Chris@307 4601
Chris@307 4602 }
Chris@307 4603
Chris@309 4604 continue;
Chris@309 4605 }
Chris@309 4606
Chris@309 4607 if (currentTimeValueLayer) {
Chris@308 4608
Chris@308 4609 if (!noteOn) continue;
Chris@310 4610
Chris@310 4611 if (!m_playSource || !m_playSource->isPlaying()) continue;
Chris@310 4612
Chris@309 4613 Model *model = static_cast<Layer *>(currentTimeValueLayer)->getModel();
Chris@309 4614 SparseTimeValueModel *tvm =
Chris@309 4615 dynamic_cast<SparseTimeValueModel *>(model);
Chris@309 4616 if (tvm) {
Chris@1754 4617 SparseTimeValueModel::Point point(frame,
Chris@1754 4618 float(ev.getPitch() % 12),
Chris@1754 4619 "");
Chris@309 4620 SparseTimeValueModel::AddPointCommand *command =
Chris@309 4621 new SparseTimeValueModel::AddPointCommand
Chris@309 4622 (tvm, point, tr("Add Point"));
Chris@309 4623 CommandHistory::getInstance()->addCommand(command);
Chris@309 4624 }
Chris@838 4625
Chris@309 4626 continue;
Chris@305 4627 }
Chris@309 4628
Chris@838 4629 // This is reached only if !currentNoteLayer and
Chris@838 4630 // !currentTimeValueLayer, i.e. there is some other sort of
Chris@838 4631 // layer that may be insertable-into
Chris@838 4632
Chris@309 4633 if (!noteOn) continue;
Chris@309 4634 insertInstantAt(ev.getTime());
Chris@304 4635 }
Chris@304 4636 }
Chris@304 4637
Chris@304 4638 void
Chris@730 4639 MainWindow::playStatusChanged(bool )
Chris@305 4640 {
Chris@307 4641 Pane *currentPane = 0;
Chris@307 4642 NoteLayer *currentNoteLayer = 0;
Chris@307 4643
Chris@307 4644 if (m_paneStack) currentPane = m_paneStack->getCurrentPane();
Chris@307 4645 if (currentPane) {
Chris@307 4646 currentNoteLayer = dynamic_cast<NoteLayer *>(currentPane->getSelectedLayer());
Chris@307 4647 }
Chris@307 4648
Chris@307 4649 if (currentNoteLayer) {
Chris@307 4650 currentNoteLayer->abandonNoteOns();
Chris@307 4651 }
Chris@305 4652 }
Chris@305 4653
Chris@305 4654 void
Chris@200 4655 MainWindow::layerRemoved(Layer *layer)
Chris@0 4656 {
Chris@95 4657 setupExistingLayersMenus();
Chris@200 4658 MainWindowBase::layerRemoved(layer);
Chris@0 4659 }
Chris@0 4660
Chris@0 4661 void
Chris@0 4662 MainWindow::layerInAView(Layer *layer, bool inAView)
Chris@0 4663 {
Chris@95 4664 setupExistingLayersMenus();
Chris@200 4665 MainWindowBase::layerInAView(layer, inAView);
Chris@0 4666 }
Chris@0 4667
Chris@0 4668 void
Chris@0 4669 MainWindow::modelAdded(Model *model)
Chris@0 4670 {
Chris@200 4671 MainWindowBase::modelAdded(model);
Chris@66 4672 if (dynamic_cast<DenseTimeValueModel *>(model)) {
Chris@66 4673 setupPaneAndLayerMenus();
Chris@66 4674 }
Chris@0 4675 }
Chris@0 4676
Chris@0 4677 void
Chris@0 4678 MainWindow::mainModelChanged(WaveFileModel *model)
Chris@0 4679 {
Chris@0 4680 m_panLayer->setModel(model);
Chris@200 4681
Chris@200 4682 MainWindowBase::mainModelChanged(model);
Chris@200 4683
Chris@1055 4684 if (m_playTarget || m_audioIO) {
Chris@1386 4685 connect(m_mainLevelPan, SIGNAL(levelChanged(float)),
Chris@1035 4686 this, SLOT(mainModelGainChanged(float)));
Chris@1386 4687 connect(m_mainLevelPan, SIGNAL(panChanged(float)),
Chris@1386 4688 this, SLOT(mainModelPanChanged(float)));
Chris@1035 4689 }
Chris@1035 4690 }
Chris@1035 4691
Chris@1035 4692 void
Chris@1035 4693 MainWindow::mainModelGainChanged(float gain)
Chris@1035 4694 {
Chris@1035 4695 if (m_playTarget) {
Chris@1035 4696 m_playTarget->setOutputGain(gain);
Chris@1055 4697 } else if (m_audioIO) {
Chris@1055 4698 m_audioIO->setOutputGain(gain);
Chris@200 4699 }
Chris@0 4700 }
Chris@0 4701
Chris@0 4702 void
Chris@1386 4703 MainWindow::mainModelPanChanged(float balance)
Chris@1386 4704 {
Chris@1386 4705 // this is indeed stereo balance rather than pan
Chris@1386 4706 if (m_playTarget) {
Chris@1386 4707 m_playTarget->setOutputBalance(balance);
Chris@1386 4708 } else if (m_audioIO) {
Chris@1386 4709 m_audioIO->setOutputBalance(balance);
Chris@1386 4710 }
Chris@1386 4711 }
Chris@1386 4712
Chris@1386 4713 void
Chris@760 4714 MainWindow::modelAboutToBeDeleted(Model *model)
Chris@760 4715 {
Chris@760 4716 if (model == m_panLayer->getModel()) {
Chris@760 4717 if (model == getMainModel()) {
Chris@760 4718 m_panLayer->setModel(0);
Chris@760 4719 } else {
Chris@760 4720 m_panLayer->setModel(getMainModel());
Chris@760 4721 }
Chris@760 4722 }
Chris@760 4723 MainWindowBase::modelAboutToBeDeleted(model);
Chris@760 4724 }
Chris@760 4725
Chris@760 4726 void
Chris@200 4727 MainWindow::setInstantsNumbering()
Chris@0 4728 {
Chris@200 4729 QAction *a = dynamic_cast<QAction *>(sender());
Chris@200 4730 if (!a) return;
Chris@200 4731
Chris@200 4732 int type = m_numberingActions[a];
Chris@200 4733
Chris@200 4734 if (m_labeller) m_labeller->setType(Labeller::ValueType(type));
Chris@200 4735
Chris@200 4736 QSettings settings;
Chris@200 4737 settings.beginGroup("MainWindow");
Chris@200 4738 settings.setValue("labellertype", type);
Chris@200 4739 settings.endGroup();
Chris@200 4740 }
Chris@200 4741
Chris@200 4742 void
Chris@200 4743 MainWindow::setInstantsCounterCycle()
Chris@200 4744 {
Chris@200 4745 QAction *a = dynamic_cast<QAction *>(sender());
Chris@200 4746 if (!a) return;
Chris@200 4747
Chris@200 4748 int cycle = a->text().toInt();
Chris@200 4749 if (cycle == 0) return;
Chris@200 4750
Chris@200 4751 if (m_labeller) m_labeller->setCounterCycleSize(cycle);
Chris@200 4752
Chris@200 4753 QSettings settings;
Chris@200 4754 settings.beginGroup("MainWindow");
Chris@200 4755 settings.setValue("labellercycle", cycle);
Chris@200 4756 settings.endGroup();
Chris@200 4757 }
Chris@200 4758
Chris@200 4759 void
Chris@597 4760 MainWindow::setInstantsCounters()
Chris@200 4761 {
Chris@200 4762 LabelCounterInputDialog dialog(m_labeller, this);
Chris@241 4763 dialog.setWindowTitle(tr("Reset Counters"));
Chris@200 4764 dialog.exec();
Chris@0 4765 }
Chris@0 4766
Chris@0 4767 void
Chris@597 4768 MainWindow::resetInstantsCounters()
Chris@597 4769 {
Chris@597 4770 if (m_labeller) m_labeller->resetCounters();
Chris@597 4771 }
Chris@597 4772
Chris@597 4773 void
Chris@1355 4774 MainWindow::subdivideInstants()
Chris@1355 4775 {
Chris@1355 4776 QSettings settings;
Chris@1355 4777 settings.beginGroup("MainWindow");
Chris@1355 4778 int n = settings.value("subdivisions", 4).toInt();
Chris@1355 4779
Chris@1355 4780 bool ok;
Chris@1355 4781
Chris@1355 4782 n = QInputDialog::getInt(this,
Chris@1355 4783 tr("Subdivide instants"),
Chris@1355 4784 tr("Number of subdivisions:"),
Chris@1355 4785 n, 2, 96, 1, &ok);
Chris@1355 4786
Chris@1355 4787 if (ok) {
Chris@1355 4788 settings.setValue("subdivisions", n);
Chris@1355 4789 subdivideInstantsBy(n);
Chris@1355 4790 }
Chris@1355 4791
Chris@1355 4792 settings.endGroup();
Chris@1355 4793 }
Chris@1355 4794
Chris@1355 4795 void
Chris@1356 4796 MainWindow::winnowInstants()
Chris@1356 4797 {
Chris@1356 4798 QSettings settings;
Chris@1356 4799 settings.beginGroup("MainWindow");
Chris@1356 4800 int n = settings.value("winnow-subdivisions", 4).toInt();
Chris@1356 4801
Chris@1356 4802 bool ok;
Chris@1356 4803
Chris@1356 4804 n = QInputDialog::getInt(this,
Chris@1356 4805 tr("Winnow instants"),
Chris@1356 4806 tr("Remove all instants apart from multiples of:"),
Chris@1356 4807 n, 2, 96, 1, &ok);
Chris@1356 4808
Chris@1356 4809 if (ok) {
Chris@1356 4810 settings.setValue("winnow-subdivisions", n);
Chris@1356 4811 winnowInstantsBy(n);
Chris@1356 4812 }
Chris@1356 4813
Chris@1356 4814 settings.endGroup();
Chris@1356 4815 }
Chris@1356 4816
Chris@1356 4817 void
Chris@233 4818 MainWindow::modelGenerationFailed(QString transformName, QString message)
Chris@233 4819 {
Chris@247 4820 emit hideSplash();
Chris@247 4821
Chris@983 4822 QString quoted;
Chris@983 4823 if (transformName != "") {
Chris@983 4824 quoted = QString("\"%1\" ").arg(transformName);
Chris@983 4825 }
Chris@983 4826
Chris@233 4827 if (message != "") {
Chris@233 4828
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 derived layer.<p>The layer transform %1failed:<p>%2")
Chris@983 4833 .arg(quoted).arg(message),
Chris@233 4834 QMessageBox::Ok);
Chris@233 4835 } else {
Chris@233 4836 QMessageBox::warning
Chris@233 4837 (this,
Chris@233 4838 tr("Failed to generate layer"),
Chris@983 4839 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 4840 .arg(quoted),
Chris@233 4841 QMessageBox::Ok);
Chris@233 4842 }
Chris@233 4843 }
Chris@233 4844
Chris@233 4845 void
Chris@730 4846 MainWindow::modelGenerationWarning(QString /* transformName */, QString message)
Chris@233 4847 {
Chris@247 4848 emit hideSplash();
Chris@247 4849
Chris@233 4850 QMessageBox::warning
Chris@233 4851 (this, tr("Warning"), message, QMessageBox::Ok);
Chris@233 4852 }
Chris@233 4853
Chris@233 4854 void
Chris@233 4855 MainWindow::modelRegenerationFailed(QString layerName,
Chris@233 4856 QString transformName, QString message)
Chris@233 4857 {
Chris@247 4858 emit hideSplash();
Chris@247 4859
Chris@233 4860 if (message != "") {
Chris@233 4861
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>%3")
Chris@233 4866 .arg(layerName).arg(transformName).arg(message),
Chris@233 4867 QMessageBox::Ok);
Chris@233 4868 } else {
Chris@233 4869 QMessageBox::warning
Chris@233 4870 (this,
Chris@233 4871 tr("Failed to regenerate layer"),
Chris@233 4872 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 4873 .arg(layerName).arg(transformName),
Chris@233 4874 QMessageBox::Ok);
Chris@233 4875 }
Chris@233 4876 }
Chris@233 4877
Chris@233 4878 void
Chris@233 4879 MainWindow::modelRegenerationWarning(QString layerName,
Chris@730 4880 QString /* transformName */,
Chris@730 4881 QString message)
Chris@233 4882 {
Chris@247 4883 emit hideSplash();
Chris@247 4884
Chris@233 4885 QMessageBox::warning
Chris@233 4886 (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 4887 }
Chris@233 4888
Chris@233 4889 void
Chris@1151 4890 MainWindow::alignmentFailed(QString message)
Chris@0 4891 {
Chris@0 4892 QMessageBox::warning
Chris@0 4893 (this,
Chris@233 4894 tr("Failed to calculate alignment"),
Chris@1151 4895 tr("<b>Alignment calculation failed</b><p>Failed to calculate an audio alignment:<p>%1")
Chris@1151 4896 .arg(message),
Chris@165 4897 QMessageBox::Ok);
Chris@0 4898 }
Chris@0 4899
Chris@0 4900 void
Chris@0 4901 MainWindow::rightButtonMenuRequested(Pane *pane, QPoint position)
Chris@0 4902 {
Chris@438 4903 // SVDEBUG << "MainWindow::rightButtonMenuRequested(" << pane << ", " << position.x() << ", " << position.y() << ")" << endl;
Chris@0 4904 m_paneStack->setCurrentPane(pane);
Chris@0 4905 m_rightButtonMenu->popup(position);
Chris@0 4906 }
Chris@0 4907
Chris@0 4908 void
Chris@0 4909 MainWindow::showLayerTree()
Chris@0 4910 {
Chris@219 4911 if (!m_layerTreeDialog.isNull()) {
Chris@219 4912 m_layerTreeDialog->show();
Chris@219 4913 m_layerTreeDialog->raise();
Chris@177 4914 return;
Chris@177 4915 }
Chris@177 4916
Chris@762 4917 m_layerTreeDialog = new LayerTreeDialog(m_paneStack, this);
Chris@232 4918 m_layerTreeDialog->setAttribute(Qt::WA_DeleteOnClose); // see below
Chris@219 4919 m_layerTreeDialog->show();
Chris@0 4920 }
Chris@0 4921
Chris@0 4922 void
Chris@306 4923 MainWindow::showActivityLog()
Chris@306 4924 {
Chris@306 4925 m_activityLog->show();
Chris@306 4926 m_activityLog->raise();
Chris@306 4927 m_activityLog->scrollToEnd();
Chris@306 4928 }
Chris@306 4929
Chris@306 4930 void
Chris@891 4931 MainWindow::showUnitConverter()
Chris@891 4932 {
Chris@891 4933 m_unitConverter->show();
Chris@891 4934 m_unitConverter->raise();
Chris@891 4935 }
Chris@891 4936
Chris@891 4937 void
Chris@0 4938 MainWindow::preferences()
Chris@0 4939 {
Chris@436 4940 bool goToTemplateTab =
Chris@436 4941 (sender() && sender()->objectName() == "set_default_template");
Chris@436 4942
Chris@0 4943 if (!m_preferencesDialog.isNull()) {
Chris@0 4944 m_preferencesDialog->show();
Chris@0 4945 m_preferencesDialog->raise();
Chris@436 4946 if (goToTemplateTab) {
Chris@436 4947 m_preferencesDialog->switchToTab(PreferencesDialog::TemplateTab);
Chris@436 4948 }
Chris@0 4949 return;
Chris@0 4950 }
Chris@0 4951
Chris@0 4952 m_preferencesDialog = new PreferencesDialog(this);
Chris@0 4953
Chris@1413 4954 connect(m_preferencesDialog, SIGNAL(audioDeviceChanged()),
Chris@1413 4955 this, SLOT(recreateAudioIO()));
Chris@1448 4956 connect(m_preferencesDialog, SIGNAL(coloursChanged()),
Chris@1448 4957 this, SLOT(coloursChanged()));
Chris@1413 4958
Chris@0 4959 // DeleteOnClose is safe here, because m_preferencesDialog is a
Chris@0 4960 // QPointer that will be zeroed when the dialog is deleted. We
Chris@0 4961 // use it in preference to leaving the dialog lying around because
Chris@0 4962 // if you Cancel the dialog, it resets the preferences state
Chris@0 4963 // without resetting its own widgets, so its state will be
Chris@0 4964 // incorrect when next shown unless we construct it afresh
Chris@0 4965 m_preferencesDialog->setAttribute(Qt::WA_DeleteOnClose);
Chris@0 4966
Chris@0 4967 m_preferencesDialog->show();
Chris@436 4968 if (goToTemplateTab) {
Chris@436 4969 m_preferencesDialog->switchToTab(PreferencesDialog::TemplateTab);
Chris@436 4970 }
Chris@0 4971 }
Chris@0 4972
Chris@0 4973 void
Chris@90 4974 MainWindow::mouseEnteredWidget()
Chris@90 4975 {
Chris@90 4976 QWidget *w = dynamic_cast<QWidget *>(sender());
Chris@90 4977 if (!w) return;
Chris@90 4978
Chris@2079 4979 QString mainText, editText;
Chris@2079 4980
Chris@1386 4981 if (w == m_mainLevelPan) {
Chris@2079 4982 mainText = tr("Adjust the master playback level and pan");
Chris@2079 4983 editText = tr("click then drag to adjust, ctrl+click to reset");
Chris@90 4984 } else if (w == m_playSpeed) {
Chris@2079 4985 mainText = tr("Adjust the master playback speed");
Chris@2079 4986 editText = tr("drag up/down to adjust, ctrl+click to reset");
Chris@2079 4987 }
Chris@2079 4988
Chris@2079 4989 if (mainText != "") {
Chris@2079 4990 contextHelpChanged(tr("%1: %2").arg(mainText).arg(editText));
Chris@90 4991 }
Chris@90 4992 }
Chris@90 4993
Chris@90 4994 void
Chris@90 4995 MainWindow::mouseLeftWidget()
Chris@90 4996 {
Chris@116 4997 contextHelpChanged("");
Chris@116 4998 }
Chris@116 4999
Chris@116 5000 void
Chris@0 5001 MainWindow::website()
Chris@0 5002 {
Chris@0 5003 openHelpUrl(tr("http://www.sonicvisualiser.org/"));
Chris@0 5004 }
Chris@0 5005
Chris@0 5006 void
Chris@0 5007 MainWindow::help()
Chris@0 5008 {
Chris@318 5009 openHelpUrl(tr("http://www.sonicvisualiser.org/doc/reference/%1/en/").arg(SV_VERSION));
Chris@0 5010 }
Chris@0 5011
Chris@0 5012 void
Chris@1516 5013 MainWindow::whatsNew()
Chris@1516 5014 {
Chris@1516 5015 QFile changelog(":CHANGELOG");
Chris@1516 5016 changelog.open(QFile::ReadOnly);
Chris@1516 5017 QByteArray content = changelog.readAll();
Chris@1516 5018 QString text = QString::fromUtf8(content);
Chris@1516 5019
Chris@1516 5020 QDialog *d = new QDialog(this);
Chris@1516 5021 d->setWindowTitle(tr("What's New"));
Chris@1516 5022
Chris@1516 5023 QGridLayout *layout = new QGridLayout;
Chris@1516 5024 d->setLayout(layout);
Chris@1516 5025
Chris@1516 5026 int row = 0;
Chris@1516 5027
Chris@1516 5028 QLabel *iconLabel = new QLabel;
Chris@1516 5029 iconLabel->setPixmap(QApplication::windowIcon().pixmap(64, 64));
Chris@1516 5030 layout->addWidget(iconLabel, row, 0);
Chris@1516 5031
Chris@1516 5032 layout->addWidget
Chris@1516 5033 (new QLabel(tr("<h3>What's New in %1</h3>")
Chris@1516 5034 .arg(QApplication::applicationName())),
Chris@1516 5035 row++, 1);
Chris@1516 5036 layout->setColumnStretch(2, 10);
Chris@1516 5037
Chris@1516 5038 QTextEdit *textEdit = new QTextEdit;
Chris@1516 5039 layout->addWidget(textEdit, row++, 1, 1, 2);
Chris@1516 5040
Chris@1516 5041 if (m_newerVersionIs != "") {
Chris@1516 5042 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 5043 }
Chris@1516 5044
Chris@1516 5045 QDialogButtonBox *bb = new QDialogButtonBox(QDialogButtonBox::Ok);
Chris@1516 5046 layout->addWidget(bb, row++, 0, 1, 3);
Chris@1516 5047 connect(bb, SIGNAL(accepted()), d, SLOT(accept()));
Chris@1516 5048
Chris@1516 5049 text.replace(QRegExp("(.)\n +(.)"), "\\1 \\2");
Chris@1516 5050 text.replace(QRegExp("\n - ([^\n]+)"), "\n<li>\\1</li>");
Chris@1516 5051 text.replace(QRegExp(": *\n"), ":\n<ul>\n");
Chris@1516 5052 text.replace(QRegExp("</li>\n\\s*\n"), "</li>\n</ul>\n\n");
Chris@1516 5053 text.replace(QRegExp("\n(\\w[^:\n]+:)"), "\n<p><b>\\1</b></p>");
Chris@1516 5054 // text.replace(QRegExp("<li>([^,.\n]+)([,.] +\\w)"), "<li><b>\\1</b>\\2");
Chris@1516 5055
Chris@1516 5056 textEdit->setHtml(text);
Chris@1516 5057 textEdit->setReadOnly(true);
Chris@1516 5058
cannam@1517 5059 d->setMinimumSize(m_viewManager->scalePixelSize(520),
cannam@1517 5060 m_viewManager->scalePixelSize(450));
Chris@1516 5061
Chris@1516 5062 d->exec();
Chris@1516 5063
Chris@1516 5064 delete d;
Chris@1516 5065 }
Chris@1516 5066
Chris@1610 5067 QString
Chris@1610 5068 MainWindow::getReleaseText() const
Chris@0 5069 {
Chris@0 5070 bool debug = false;
Chris@0 5071 QString version = "(unknown version)";
Chris@0 5072
Chris@0 5073 #ifdef BUILD_DEBUG
Chris@0 5074 debug = true;
Chris@285 5075 #endif // BUILD_DEBUG
Chris@0 5076 #ifdef SV_VERSION
Chris@0 5077 #ifdef SVNREV
Chris@0 5078 version = tr("Release %1 : Revision %2").arg(SV_VERSION).arg(SVNREV);
Chris@285 5079 #else // !SVNREV
Chris@0 5080 version = tr("Release %1").arg(SV_VERSION);
Chris@285 5081 #endif // SVNREV
Chris@285 5082 #else // !SV_VERSION
Chris@0 5083 #ifdef SVNREV
Chris@0 5084 version = tr("Unreleased : Revision %1").arg(SVNREV);
Chris@285 5085 #endif // SVNREV
Chris@285 5086 #endif // SV_VERSION
Chris@0 5087
Chris@1610 5088 return tr("%1 : %2 configuration, %3-bit build")
Chris@0 5089 .arg(version)
Chris@1264 5090 .arg(debug ? tr("Debug") : tr("Release"))
Chris@1264 5091 .arg(sizeof(void *) * 8);
Chris@1610 5092 }
Chris@1610 5093
Chris@1610 5094 void
Chris@1610 5095 MainWindow::about()
Chris@1610 5096 {
Chris@1610 5097 QString aboutText;
Chris@1610 5098
Chris@1610 5099 aboutText += tr("<h3>About Sonic Visualiser</h3>");
Chris@1610 5100 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 5101 aboutText += QString("<p><small>%1</small></p>").arg(getReleaseText());
Chris@0 5102
Chris@1516 5103 if (m_oscQueue && m_oscQueue->isOK()) {
Chris@1516 5104 aboutText += tr("</small><p><small>The OSC URL for this instance is: \"%1\"").arg(m_oscQueue->getOSCURL());
Chris@1516 5105 }
Chris@1516 5106
Chris@1516 5107 aboutText += "</small><p><small>";
Chris@285 5108
Chris@1155 5109 aboutText += tr("With Qt v%1 &copy; The Qt Company").arg(QT_VERSION_STR);
Chris@285 5110
Chris@1516 5111 aboutText += "</small><small>";
Chris@1516 5112
Chris@0 5113 #ifdef HAVE_JACK
Chris@93 5114 #ifdef JACK_VERSION
Chris@285 5115 aboutText += tr("<br>With JACK audio output library v%1 &copy; Paul Davis and Jack O'Quin").arg(JACK_VERSION);
Chris@285 5116 #else // !JACK_VERSION
Chris@257 5117 aboutText += tr("<br>With JACK audio output library &copy; Paul Davis and Jack O'Quin");
Chris@285 5118 #endif // JACK_VERSION
Chris@285 5119 #endif // HAVE_JACK
Chris@0 5120 #ifdef HAVE_PORTAUDIO
Chris@257 5121 aboutText += tr("<br>With PortAudio audio output library &copy; Ross Bencina and Phil Burk");
Chris@285 5122 #endif // HAVE_PORTAUDIO
Chris@257 5123 #ifdef HAVE_LIBPULSE
Chris@285 5124 #ifdef LIBPULSE_VERSION
Chris@285 5125 aboutText += tr("<br>With PulseAudio audio output library v%1 &copy; Lennart Poettering and Pierre Ossman").arg(LIBPULSE_VERSION);
Chris@285 5126 #else // !LIBPULSE_VERSION
Chris@257 5127 aboutText += tr("<br>With PulseAudio audio output library &copy; Lennart Poettering and Pierre Ossman");
Chris@285 5128 #endif // LIBPULSE_VERSION
Chris@285 5129 #endif // HAVE_LIBPULSE
Chris@0 5130 #ifdef HAVE_OGGZ
Chris@93 5131 #ifdef OGGZ_VERSION
Chris@0 5132 aboutText += tr("<br>With Ogg file decoder (oggz v%1, fishsound v%2) &copy; CSIRO Australia").arg(OGGZ_VERSION).arg(FISHSOUND_VERSION);
Chris@285 5133 #else // !OGGZ_VERSION
Chris@93 5134 aboutText += tr("<br>With Ogg file decoder &copy; CSIRO Australia");
Chris@285 5135 #endif // OGGZ_VERSION
Chris@285 5136 #endif // HAVE_OGGZ
Chris@0 5137 #ifdef HAVE_MAD
Chris@93 5138 #ifdef MAD_VERSION
Chris@285 5139 aboutText += tr("<br>With MAD mp3 decoder v%1 &copy; Underbit Technologies Inc").arg(MAD_VERSION);
Chris@285 5140 #else // !MAD_VERSION
Chris@93 5141 aboutText += tr("<br>With MAD mp3 decoder &copy; Underbit Technologies Inc");
Chris@285 5142 #endif // MAD_VERSION
Chris@285 5143 #endif // HAVE_MAD
Chris@0 5144 #ifdef HAVE_SAMPLERATE
Chris@93 5145 #ifdef SAMPLERATE_VERSION
Chris@285 5146 aboutText += tr("<br>With libsamplerate v%1 &copy; Erik de Castro Lopo").arg(SAMPLERATE_VERSION);
Chris@285 5147 #else // !SAMPLERATE_VERSION
Chris@93 5148 aboutText += tr("<br>With libsamplerate &copy; Erik de Castro Lopo");
Chris@285 5149 #endif // SAMPLERATE_VERSION
Chris@285 5150 #endif // HAVE_SAMPLERATE
Chris@0 5151 #ifdef HAVE_SNDFILE
Chris@93 5152 #ifdef SNDFILE_VERSION
Chris@285 5153 aboutText += tr("<br>With libsndfile v%1 &copy; Erik de Castro Lopo").arg(SNDFILE_VERSION);
Chris@285 5154 #else // !SNDFILE_VERSION
Chris@93 5155 aboutText += tr("<br>With libsndfile &copy; Erik de Castro Lopo");
Chris@285 5156 #endif // SNDFILE_VERSION
Chris@285 5157 #endif // HAVE_SNDFILE
Chris@127 5158 #ifdef HAVE_FFTW3F
Chris@93 5159 #ifdef FFTW3_VERSION
Chris@285 5160 aboutText += tr("<br>With FFTW3 v%1 &copy; Matteo Frigo and MIT").arg(FFTW3_VERSION);
Chris@285 5161 #else // !FFTW3_VERSION
Chris@93 5162 aboutText += tr("<br>With FFTW3 &copy; Matteo Frigo and MIT");
Chris@285 5163 #endif // FFTW3_VERSION
Chris@285 5164 #endif // HAVE_FFTW3F
Chris@267 5165 #ifdef HAVE_RUBBERBAND
Chris@267 5166 #ifdef RUBBERBAND_VERSION
Chris@1315 5167 aboutText += tr("<br>With Rubber Band Library v%1 &copy; Particular Programs Ltd").arg(RUBBERBAND_VERSION);
Chris@285 5168 #else // !RUBBERBAND_VERSION
Chris@1315 5169 aboutText += tr("<br>With Rubber Band Library &copy; Particular Programs Ltd");
Chris@285 5170 #endif // RUBBERBAND_VERSION
Chris@285 5171 #endif // HAVE_RUBBERBAND
Chris@1315 5172 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 5173 aboutText += tr("<br>With Piper Vamp protocol bridge &copy; QMUL");
Chris@0 5174 aboutText += tr("<br>With LADSPA plugin support (API v%1) &copy; Richard Furse, Paul Davis, Stefan Westerfeld").arg(LADSPA_VERSION);
Chris@0 5175 aboutText += tr("<br>With DSSI plugin support (API v%1) &copy; Chris Cannam, Steve Harris, Sean Bolton").arg(DSSI_VERSION);
Chris@285 5176 #ifdef REDLAND_VERSION
Chris@285 5177 aboutText += tr("<br>With Redland RDF datastore v%1 &copy; Dave Beckett and the University of Bristol").arg(REDLAND_VERSION);
Chris@285 5178 #else // !REDLAND_VERSION
Chris@285 5179 aboutText += tr("<br>With Redland RDF datastore &copy; Dave Beckett and the University of Bristol");
Chris@285 5180 #endif // REDLAND_VERSION
Chris@523 5181 aboutText += tr("<br>With Serd and Sord RDF parser and store &copy; David Robillard");
Chris@1315 5182 aboutText += tr("<br>With Dataquay Qt/RDF library &copy; Particular Programs Ltd");
Chris@1315 5183 aboutText += tr("<br>With Cap'n Proto serialisation &copy; Sandstorm Development Group");
Chris@300 5184 aboutText += tr("<br>With RtMidi &copy; Gary P. Scavone");
Chris@300 5185
Chris@69 5186 #ifdef HAVE_LIBLO
Chris@93 5187 #ifdef LIBLO_VERSION
Chris@285 5188 aboutText += tr("<br>With liblo Lite OSC library v%1 &copy; Steve Harris").arg(LIBLO_VERSION);
Chris@285 5189 #else // !LIBLO_VERSION
Chris@327 5190 aboutText += tr("<br>With liblo Lite OSC library &copy; Steve Harris");
Chris@285 5191 #endif // LIBLO_VERSION
Chris@285 5192
Chris@285 5193 aboutText += "</small></p>";
Chris@285 5194 #endif // HAVE_LIBLO
Chris@285 5195
Chris@1667 5196 aboutText += "<p><small>";
Chris@1667 5197 aboutText += tr("Russian UI translation contributed by Alexandre Prokoudine.");
Chris@1667 5198 aboutText += "<br>";
Chris@1667 5199 aboutText += tr("Czech UI translation contributed by Pavel Fric.");
Chris@1667 5200 aboutText += "</small></p>";
Chris@1667 5201
Chris@0 5202 aboutText +=
Chris@1960 5203 "<p><small>Sonic Visualiser Copyright &copy; 2005&ndash;2018 Chris Cannam and "
Chris@1667 5204 "Queen Mary, University of London.</small></p>";
Chris@1667 5205
Chris@1667 5206 aboutText +=
Chris@285 5207 "<p><small>This program is free software; you can redistribute it and/or "
Chris@231 5208 "modify it under the terms of the GNU General Public License as "
Chris@231 5209 "published by the Free Software Foundation; either version 2 of the "
Chris@0 5210 "License, or (at your option) any later version.<br>See the file "
Chris@285 5211 "COPYING included with this distribution for more information.</small></p>";
Chris@1516 5212
Chris@1516 5213 // use our own dialog so we can influence the size
Chris@1516 5214
Chris@1516 5215 QDialog *d = new QDialog(this);
Chris@1516 5216
Chris@1516 5217 d->setWindowTitle(tr("About %1").arg(QApplication::applicationName()));
Chris@1516 5218
Chris@1516 5219 QGridLayout *layout = new QGridLayout;
Chris@1516 5220 d->setLayout(layout);
Chris@1516 5221
Chris@1516 5222 int row = 0;
Chris@0 5223
Chris@1516 5224 QLabel *iconLabel = new QLabel;
Chris@1516 5225 iconLabel->setPixmap(QApplication::windowIcon().pixmap(64, 64));
Chris@1516 5226 layout->addWidget(iconLabel, row, 0, Qt::AlignTop);
Chris@1516 5227
Chris@1516 5228 QLabel *mainText = new QLabel();
Chris@1516 5229 layout->addWidget(mainText, row, 1, 1, 2);
Chris@1516 5230
Chris@1516 5231 layout->setRowStretch(row, 10);
Chris@1516 5232 layout->setColumnStretch(1, 10);
Chris@1516 5233
Chris@1516 5234 ++row;
Chris@1516 5235
Chris@1516 5236 QDialogButtonBox *bb = new QDialogButtonBox(QDialogButtonBox::Ok);
Chris@1516 5237 layout->addWidget(bb, row++, 0, 1, 3);
Chris@1516 5238 connect(bb, SIGNAL(accepted()), d, SLOT(accept()));
Chris@1516 5239
Chris@1516 5240 // mainText->setHtml(aboutText);
Chris@1516 5241 // mainText->setReadOnly(true);
Chris@1516 5242 mainText->setWordWrap(true);
Chris@1516 5243 mainText->setOpenExternalLinks(true);
Chris@1516 5244 mainText->setText(aboutText);
Chris@1516 5245
Chris@1516 5246 d->setMinimumSize(m_viewManager->scalePixelSize(420),
Chris@1516 5247 m_viewManager->scalePixelSize(200));
Chris@1516 5248
Chris@1516 5249 d->exec();
Chris@1516 5250
Chris@1516 5251 delete d;
Chris@1516 5252 /*
Chris@1516 5253 QMessageBox about(QMessageBox::Information,
Chris@1516 5254 tr("About Sonic Visualiser"),
Chris@1516 5255 aboutText,
Chris@1516 5256 QMessageBox::StandardButtons(QMessageBox::Ok),
Chris@1516 5257 this);
Chris@1516 5258
Chris@1516 5259 QIcon icon = QApplication::windowIcon();
Chris@1516 5260 QSize size = icon.actualSize(QSize(64, 64));
Chris@1516 5261 about.setIconPixmap(icon.pixmap(size));
Chris@1516 5262
Chris@1516 5263 about.setMinimumSize(m_viewManager->scalePixelSize(400),
Chris@1516 5264 m_viewManager->scalePixelSize(400));
Chris@1516 5265
Chris@1516 5266 about.exec();
Chris@1516 5267 */
Chris@0 5268 }
Chris@0 5269
Chris@162 5270 void
Chris@162 5271 MainWindow::keyReference()
Chris@162 5272 {
Chris@162 5273 m_keyReference->show();
Chris@162 5274 }
Chris@162 5275
Chris@333 5276 void
Chris@333 5277 MainWindow::newerVersionAvailable(QString version)
Chris@333 5278 {
Chris@1516 5279 m_newerVersionIs = version;
Chris@1516 5280
Chris@334 5281 QSettings settings;
Chris@334 5282 settings.beginGroup("NewerVersionWarning");
Chris@334 5283 QString tag = QString("version-%1-available-show").arg(version);
Chris@334 5284 if (settings.value(tag, true).toBool()) {
Chris@334 5285 QString title(tr("Newer version available"));
Chris@663 5286 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 5287 QMessageBox::information(this, title, text);
Chris@334 5288 settings.setValue(tag, false);
Chris@334 5289 }
Chris@334 5290 settings.endGroup();
Chris@333 5291 }
Chris@333 5292
Chris@333 5293