annotate main/MainWindow.cpp @ 1315:ed3009a50f81 piper

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