annotate main/MainWindow.cpp @ 1257:f2105c5b0b12 piper

Pull out model creation into the transformer thread run(), so that all communications with the plugin server happen on a single thread. Then make the model accessor wait for them to be created (which still happens right at the start of processing) before returning.
author Chris Cannam
date Mon, 17 Oct 2016 14:18:23 +0100
parents cc434d37a126
children 65b8c009a9f2
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@730 1183 #pragma GCC diagnostic ignored "-Wswitch-enum"
Chris@66 1184
Chris@66 1185 switch (type) {
Chris@66 1186
Chris@66 1187 case LayerFactory::Waveform:
Chris@168 1188 icon = il.load("waveform");
Chris@66 1189 mainText = tr("Add &Waveform");
Chris@231 1190 if (menuType == paneMenuType) {
Chris@155 1191 shortcutText = tr("W");
Chris@66 1192 tipText = tr("Add a new pane showing a waveform view");
Chris@66 1193 } else {
Chris@348 1194 shortcutText = tr("Shift+W");
Chris@66 1195 tipText = tr("Add a new layer showing a waveform view");
Chris@66 1196 }
Chris@66 1197 mono = false;
Chris@66 1198 break;
Chris@66 1199
Chris@66 1200 case LayerFactory::Spectrogram:
Chris@168 1201 icon = il.load("spectrogram");
Chris@161 1202 mainText = tr("Add Spectro&gram");
Chris@231 1203 if (menuType == paneMenuType) {
Chris@155 1204 shortcutText = tr("G");
Chris@90 1205 tipText = tr("Add a new pane showing a spectrogram");
Chris@66 1206 } else {
Chris@348 1207 shortcutText = tr("Shift+G");
Chris@90 1208 tipText = tr("Add a new layer showing a spectrogram");
Chris@66 1209 }
Chris@66 1210 break;
Chris@66 1211
Chris@66 1212 case LayerFactory::MelodicRangeSpectrogram:
Chris@168 1213 icon = il.load("spectrogram");
Chris@66 1214 mainText = tr("Add &Melodic Range Spectrogram");
Chris@231 1215 if (menuType == paneMenuType) {
Chris@155 1216 shortcutText = tr("M");
Chris@90 1217 tipText = tr("Add a new pane showing a spectrogram set up for an overview of note pitches");
Chris@66 1218 } else {
Chris@348 1219 shortcutText = tr("Shift+M");
Chris@90 1220 tipText = tr("Add a new layer showing a spectrogram set up for an overview of note pitches");
Chris@66 1221 }
Chris@66 1222 break;
Chris@66 1223
Chris@66 1224 case LayerFactory::PeakFrequencySpectrogram:
Chris@168 1225 icon = il.load("spectrogram");
Chris@155 1226 mainText = tr("Add Pea&k Frequency Spectrogram");
Chris@231 1227 if (menuType == paneMenuType) {
Chris@155 1228 shortcutText = tr("K");
Chris@66 1229 tipText = tr("Add a new pane showing a spectrogram set up for tracking frequencies");
Chris@66 1230 } else {
Chris@348 1231 shortcutText = tr("Shift+K");
Chris@66 1232 tipText = tr("Add a new layer showing a spectrogram set up for tracking frequencies");
Chris@66 1233 }
Chris@66 1234 break;
Chris@66 1235
Chris@66 1236 case LayerFactory::Spectrum:
Chris@168 1237 icon = il.load("spectrum");
Chris@66 1238 mainText = tr("Add Spectr&um");
Chris@231 1239 if (menuType == paneMenuType) {
Chris@155 1240 shortcutText = tr("U");
Chris@66 1241 tipText = tr("Add a new pane showing a frequency spectrum");
Chris@66 1242 } else {
Chris@348 1243 shortcutText = tr("Shift+U");
Chris@66 1244 tipText = tr("Add a new layer showing a frequency spectrum");
Chris@66 1245 }
Chris@66 1246 break;
Chris@66 1247
Chris@66 1248 default: break;
Chris@66 1249 }
Chris@66 1250
Chris@346 1251 std::vector<Model *> candidateModels = models;
Chris@66 1252
Chris@66 1253 for (std::vector<Model *>::iterator mi =
Chris@66 1254 candidateModels.begin();
Chris@66 1255 mi != candidateModels.end(); ++mi) {
Chris@66 1256
Chris@66 1257 Model *model = *mi;
Chris@66 1258
Chris@66 1259 int channels = 0;
Chris@66 1260 if (model) {
Chris@66 1261 DenseTimeValueModel *dtvm =
Chris@66 1262 dynamic_cast<DenseTimeValueModel *>(model);
Chris@66 1263 if (dtvm) channels = dtvm->getChannelCount();
Chris@66 1264 }
Chris@66 1265 if (channels < 1 && getMainModel()) {
Chris@66 1266 channels = getMainModel()->getChannelCount();
Chris@66 1267 }
Chris@66 1268 if (channels < 1) channels = 1;
Chris@66 1269
Chris@66 1270 for (int c = 0; c <= channels; ++c) {
Chris@66 1271
Chris@66 1272 if (c == 1 && channels == 1) continue;
Chris@66 1273 bool isDefault = (c == 0);
Chris@66 1274 bool isOnly = (isDefault && (channels == 1));
Chris@66 1275
Chris@346 1276 if (isOnly && !plural) {
Chris@346 1277
Chris@346 1278 action = new QAction(icon, mainText, this);
Chris@67 1279
Chris@66 1280 action->setShortcut(shortcutText);
Chris@66 1281 action->setStatusTip(tipText);
Chris@231 1282 if (menuType == paneMenuType) {
Chris@66 1283 connect(action, SIGNAL(triggered()),
Chris@66 1284 this, SLOT(addPane()));
Chris@66 1285 connect(this, SIGNAL(canAddPane(bool)),
Chris@66 1286 action, SLOT(setEnabled(bool)));
Chris@346 1287 m_paneActions[action] =
Chris@346 1288 LayerConfiguration(type, model);
Chris@66 1289 } else {
Chris@66 1290 connect(action, SIGNAL(triggered()),
Chris@66 1291 this, SLOT(addLayer()));
Chris@66 1292 connect(this, SIGNAL(canAddLayer(bool)),
Chris@66 1293 action, SLOT(setEnabled(bool)));
Chris@346 1294 m_layerActions[action] =
Chris@346 1295 LayerConfiguration(type, model);
Chris@66 1296 }
Chris@162 1297 if (shortcutText != "") {
Chris@162 1298 m_keyReference->registerShortcut(action);
Chris@162 1299 }
Chris@66 1300 menu->addAction(action);
Chris@66 1301
Chris@66 1302 } else {
Chris@66 1303
Chris@66 1304 if (!submenu) {
Chris@66 1305 submenu = menu->addMenu(mainText);
Chris@97 1306 submenu->setTearOffEnabled(true);
Chris@67 1307 } else if (isDefault) {
Chris@67 1308 submenu->addSeparator();
Chris@66 1309 }
Chris@66 1310
Chris@66 1311 QString actionText;
Chris@66 1312 if (c == 0) {
Chris@66 1313 if (mono) {
Chris@66 1314 actionText = tr("&All Channels Mixed");
Chris@66 1315 } else {
Chris@66 1316 actionText = tr("&All Channels");
Chris@66 1317 }
Chris@66 1318 } else {
Chris@66 1319 actionText = tr("Channel &%1").arg(c);
Chris@66 1320 }
Chris@66 1321
Chris@66 1322 if (model) {
Chris@66 1323 actionText = tr("%1: %2")
Chris@66 1324 .arg(model->objectName())
Chris@66 1325 .arg(actionText);
Chris@66 1326 }
Chris@67 1327
Chris@67 1328 if (isDefault) {
Chris@67 1329 action = new QAction(icon, actionText, this);
Chris@67 1330 if (!model || model == getMainModel()) {
Chris@162 1331 action->setShortcut(shortcutText);
Chris@67 1332 }
Chris@67 1333 } else {
Chris@67 1334 action = new QAction(actionText, this);
Chris@67 1335 }
Chris@67 1336
Chris@66 1337 action->setStatusTip(tipText);
Chris@66 1338
Chris@231 1339 if (menuType == paneMenuType) {
Chris@66 1340 connect(action, SIGNAL(triggered()),
Chris@66 1341 this, SLOT(addPane()));
Chris@66 1342 connect(this, SIGNAL(canAddPane(bool)),
Chris@66 1343 action, SLOT(setEnabled(bool)));
Chris@66 1344 m_paneActions[action] =
Chris@232 1345 LayerConfiguration(type, model, c - 1);
Chris@66 1346 } else {
Chris@66 1347 connect(action, SIGNAL(triggered()),
Chris@66 1348 this, SLOT(addLayer()));
Chris@66 1349 connect(this, SIGNAL(canAddLayer(bool)),
Chris@66 1350 action, SLOT(setEnabled(bool)));
Chris@232 1351 m_layerActions[action] =
Chris@232 1352 LayerConfiguration(type, model, c - 1);
Chris@66 1353 }
Chris@66 1354
Chris@66 1355 submenu->addAction(action);
Chris@66 1356 }
Chris@346 1357
Chris@415 1358 if (isDefault && menuType == layerMenuType &&
Chris@415 1359 mi == candidateModels.begin()) {
Chris@415 1360 // only add for one model, one channel, one menu on
Chris@415 1361 // right button -- the action itself will discover
Chris@415 1362 // which model is the correct one (based on pane)
Chris@346 1363 action = new QAction(icon, mainText, this);
Chris@346 1364 action->setStatusTip(tipText);
Chris@346 1365 connect(action, SIGNAL(triggered()),
Chris@346 1366 this, SLOT(addLayer()));
Chris@346 1367 connect(this, SIGNAL(canAddLayer(bool)),
Chris@346 1368 action, SLOT(setEnabled(bool)));
Chris@346 1369 m_layerActions[action] = LayerConfiguration(type, 0, 0);
Chris@346 1370 m_rightButtonLayerMenu->addAction(action);
Chris@346 1371 }
Chris@66 1372 }
Chris@66 1373 }
Chris@66 1374 }
Chris@66 1375 }
Chris@66 1376
Chris@347 1377 m_rightButtonLayerMenu->addSeparator();
Chris@347 1378
Chris@66 1379 menu = m_paneMenu;
Chris@225 1380 menu->addSeparator();
Chris@225 1381
Chris@225 1382 action = new QAction(tr("Switch to Previous Pane"), this);
Chris@225 1383 action->setShortcut(tr("["));
Chris@225 1384 action->setStatusTip(tr("Make the next pane up in the pane stack current"));
Chris@225 1385 connect(action, SIGNAL(triggered()), this, SLOT(previousPane()));
Chris@225 1386 connect(this, SIGNAL(canSelectPreviousPane(bool)), action, SLOT(setEnabled(bool)));
Chris@225 1387 m_keyReference->registerShortcut(action);
Chris@225 1388 menu->addAction(action);
Chris@225 1389
Chris@225 1390 action = new QAction(tr("Switch to Next Pane"), this);
Chris@225 1391 action->setShortcut(tr("]"));
Chris@225 1392 action->setStatusTip(tr("Make the next pane down in the pane stack current"));
Chris@225 1393 connect(action, SIGNAL(triggered()), this, SLOT(nextPane()));
Chris@225 1394 connect(this, SIGNAL(canSelectNextPane(bool)), action, SLOT(setEnabled(bool)));
Chris@225 1395 m_keyReference->registerShortcut(action);
Chris@225 1396 menu->addAction(action);
Chris@66 1397
Chris@66 1398 menu->addSeparator();
Chris@66 1399
Chris@168 1400 action = new QAction(il.load("editdelete"), tr("&Delete Pane"), this);
Chris@155 1401 action->setShortcut(tr("Ctrl+Shift+D"));
Chris@90 1402 action->setStatusTip(tr("Delete the currently active pane"));
Chris@66 1403 connect(action, SIGNAL(triggered()), this, SLOT(deleteCurrentPane()));
Chris@66 1404 connect(this, SIGNAL(canDeleteCurrentPane(bool)), action, SLOT(setEnabled(bool)));
Chris@162 1405 m_keyReference->registerShortcut(action);
Chris@66 1406 menu->addAction(action);
Chris@66 1407
Chris@66 1408 menu = m_layerMenu;
Chris@66 1409
Chris@168 1410 action = new QAction(il.load("timeruler"), tr("Add &Time Ruler"), this);
Chris@66 1411 action->setStatusTip(tr("Add a new layer showing a time ruler"));
Chris@66 1412 connect(action, SIGNAL(triggered()), this, SLOT(addLayer()));
Chris@66 1413 connect(this, SIGNAL(canAddLayer(bool)), action, SLOT(setEnabled(bool)));
Chris@232 1414 m_layerActions[action] = LayerConfiguration(LayerFactory::TimeRuler);
Chris@66 1415 menu->addAction(action);
Chris@66 1416
Chris@66 1417 menu->addSeparator();
Chris@66 1418
Chris@66 1419 m_existingLayersMenu = menu->addMenu(tr("Add &Existing Layer"));
Chris@97 1420 m_existingLayersMenu->setTearOffEnabled(true);
Chris@66 1421 m_rightButtonLayerMenu->addMenu(m_existingLayersMenu);
Chris@95 1422
Chris@95 1423 m_sliceMenu = menu->addMenu(tr("Add S&lice of Layer"));
Chris@97 1424 m_sliceMenu->setTearOffEnabled(true);
Chris@95 1425 m_rightButtonLayerMenu->addMenu(m_sliceMenu);
Chris@95 1426
Chris@95 1427 setupExistingLayersMenus();
Chris@66 1428
Chris@225 1429 menu->addSeparator();
Chris@225 1430
Chris@225 1431 action = new QAction(tr("Switch to Previous Layer"), this);
Chris@225 1432 action->setShortcut(tr("{"));
Chris@225 1433 action->setStatusTip(tr("Make the previous layer in the pane current"));
Chris@225 1434 connect(action, SIGNAL(triggered()), this, SLOT(previousLayer()));
Chris@225 1435 connect(this, SIGNAL(canSelectPreviousLayer(bool)), action, SLOT(setEnabled(bool)));
Chris@225 1436 m_keyReference->registerShortcut(action);
Chris@225 1437 menu->addAction(action);
Chris@225 1438
Chris@225 1439 action = new QAction(tr("Switch to Next Layer"), this);
Chris@225 1440 action->setShortcut(tr("}"));
Chris@225 1441 action->setStatusTip(tr("Make the next layer in the pane current"));
Chris@225 1442 connect(action, SIGNAL(triggered()), this, SLOT(nextLayer()));
Chris@225 1443 connect(this, SIGNAL(canSelectNextLayer(bool)), action, SLOT(setEnabled(bool)));
Chris@225 1444 m_keyReference->registerShortcut(action);
Chris@225 1445 menu->addAction(action);
Chris@785 1446
Chris@66 1447 m_rightButtonLayerMenu->addSeparator();
Chris@66 1448 menu->addSeparator();
Chris@66 1449
Chris@163 1450 QAction *raction = new QAction(tr("&Rename Layer..."), this);
Chris@163 1451 raction->setShortcut(tr("R"));
Chris@163 1452 raction->setStatusTip(tr("Rename the currently active layer"));
Chris@163 1453 connect(raction, SIGNAL(triggered()), this, SLOT(renameCurrentLayer()));
Chris@163 1454 connect(this, SIGNAL(canRenameLayer(bool)), raction, SLOT(setEnabled(bool)));
Chris@163 1455 menu->addAction(raction);
Chris@163 1456 m_rightButtonLayerMenu->addAction(raction);
Chris@66 1457
Chris@258 1458 QAction *eaction = new QAction(tr("Edit Layer Data"), this);
Chris@258 1459 eaction->setShortcut(tr("E"));
Chris@258 1460 eaction->setStatusTip(tr("Edit the currently active layer as a data grid"));
Chris@258 1461 connect(eaction, SIGNAL(triggered()), this, SLOT(editCurrentLayer()));
Chris@291 1462 connect(this, SIGNAL(canEditLayerTabular(bool)), eaction, SLOT(setEnabled(bool)));
Chris@258 1463 menu->addAction(eaction);
Chris@258 1464 m_rightButtonLayerMenu->addAction(eaction);
Chris@258 1465
Chris@168 1466 action = new QAction(il.load("editdelete"), tr("&Delete Layer"), this);
Chris@155 1467 action->setShortcut(tr("Ctrl+D"));
Chris@66 1468 action->setStatusTip(tr("Delete the currently active layer"));
Chris@66 1469 connect(action, SIGNAL(triggered()), this, SLOT(deleteCurrentLayer()));
Chris@66 1470 connect(this, SIGNAL(canDeleteCurrentLayer(bool)), action, SLOT(setEnabled(bool)));
Chris@162 1471 m_keyReference->registerShortcut(action);
Chris@66 1472 menu->addAction(action);
Chris@66 1473 m_rightButtonLayerMenu->addAction(action);
Chris@163 1474
Chris@163 1475 m_keyReference->registerShortcut(raction); // rename after delete, so delete layer goes next to delete pane
Chris@258 1476 m_keyReference->registerShortcut(eaction); // edit also after delete
Chris@755 1477
Chris@755 1478 finaliseMenus();
Chris@66 1479 }
Chris@66 1480
Chris@66 1481 void
Chris@211 1482 MainWindow::setupTransformsMenu()
Chris@66 1483 {
Chris@34 1484 if (m_transformsMenu) {
Chris@34 1485 m_transformActions.clear();
Chris@34 1486 m_transformActionsReverse.clear();
Chris@34 1487 m_transformsMenu->clear();
Chris@34 1488 } else {
Chris@97 1489 m_transformsMenu = menuBar()->addMenu(tr("&Transform"));
Chris@97 1490 m_transformsMenu->setTearOffEnabled(true);
Chris@286 1491 m_transformsMenu->setSeparatorsCollapsible(true);
Chris@272 1492 }
Chris@34 1493
Chris@288 1494 TransformFactory *factory = TransformFactory::getInstance();
Chris@288 1495
Chris@288 1496 TransformList transforms = factory->getAllTransformDescriptions();
Chris@288 1497 vector<TransformDescription::Type> types = factory->getAllTransformTypes();
Chris@288 1498
Chris@288 1499 map<TransformDescription::Type, map<QString, SubdividingMenu *> > categoryMenus;
Chris@288 1500 map<TransformDescription::Type, map<QString, SubdividingMenu *> > makerMenus;
Chris@288 1501
Chris@288 1502 map<TransformDescription::Type, SubdividingMenu *> byPluginNameMenus;
Chris@288 1503 map<TransformDescription::Type, map<QString, QMenu *> > pluginNameMenus;
Chris@33 1504
Chris@37 1505 set<SubdividingMenu *> pendingMenus;
Chris@37 1506
Chris@211 1507 m_recentTransformsMenu = m_transformsMenu->addMenu(tr("&Recent Transforms"));
Chris@211 1508 m_recentTransformsMenu->setTearOffEnabled(true);
Chris@211 1509 m_rightButtonTransformsMenu->addMenu(m_recentTransformsMenu);
Chris@211 1510 connect(&m_recentTransforms, SIGNAL(recentChanged()),
Chris@211 1511 this, SLOT(setupRecentTransformsMenu()));
Chris@34 1512
Chris@34 1513 m_transformsMenu->addSeparator();
Chris@211 1514 m_rightButtonTransformsMenu->addSeparator();
Chris@34 1515
Chris@288 1516 for (vector<TransformDescription::Type>::iterator i = types.begin();
Chris@288 1517 i != types.end(); ++i) {
Chris@33 1518
Chris@33 1519 if (i != types.begin()) {
Chris@34 1520 m_transformsMenu->addSeparator();
Chris@211 1521 m_rightButtonTransformsMenu->addSeparator();
Chris@33 1522 }
Chris@33 1523
Chris@288 1524 QString byCategoryLabel = tr("%1 by Category")
Chris@288 1525 .arg(factory->getTransformTypeName(*i));
Chris@37 1526 SubdividingMenu *byCategoryMenu = new SubdividingMenu(byCategoryLabel,
Chris@37 1527 20, 40);
Chris@97 1528 byCategoryMenu->setTearOffEnabled(true);
Chris@37 1529 m_transformsMenu->addMenu(byCategoryMenu);
Chris@211 1530 m_rightButtonTransformsMenu->addMenu(byCategoryMenu);
Chris@37 1531 pendingMenus.insert(byCategoryMenu);
Chris@33 1532
Chris@288 1533 vector<QString> categories = factory->getTransformCategories(*i);
Chris@33 1534
Chris@33 1535 for (vector<QString>::iterator j = categories.begin();
Chris@33 1536 j != categories.end(); ++j) {
Chris@33 1537
Chris@33 1538 QString category = *j;
Chris@33 1539 if (category == "") category = tr("Unclassified");
Chris@33 1540
Chris@33 1541 if (categories.size() < 2) {
Chris@33 1542 categoryMenus[*i][category] = byCategoryMenu;
Chris@33 1543 continue;
Chris@33 1544 }
Chris@33 1545
Chris@33 1546 QStringList components = category.split(" > ");
Chris@33 1547 QString key;
Chris@33 1548
Chris@33 1549 for (QStringList::iterator k = components.begin();
Chris@33 1550 k != components.end(); ++k) {
Chris@33 1551
Chris@33 1552 QString parentKey = key;
Chris@33 1553 if (key != "") key += " > ";
Chris@33 1554 key += *k;
Chris@33 1555
Chris@33 1556 if (categoryMenus[*i].find(key) == categoryMenus[*i].end()) {
Chris@37 1557 SubdividingMenu *m = new SubdividingMenu(*k, 20, 40);
Chris@97 1558 m->setTearOffEnabled(true);
Chris@37 1559 pendingMenus.insert(m);
Chris@37 1560 categoryMenus[*i][key] = m;
Chris@33 1561 if (parentKey == "") {
Chris@37 1562 byCategoryMenu->addMenu(m);
Chris@33 1563 } else {
Chris@37 1564 categoryMenus[*i][parentKey]->addMenu(m);
Chris@33 1565 }
Chris@33 1566 }
Chris@33 1567 }
Chris@33 1568 }
Chris@33 1569
Chris@288 1570 QString byPluginNameLabel = tr("%1 by Plugin Name")
Chris@288 1571 .arg(factory->getTransformTypeName(*i));
Chris@36 1572 byPluginNameMenus[*i] = new SubdividingMenu(byPluginNameLabel);
Chris@97 1573 byPluginNameMenus[*i]->setTearOffEnabled(true);
Chris@36 1574 m_transformsMenu->addMenu(byPluginNameMenus[*i]);
Chris@211 1575 m_rightButtonTransformsMenu->addMenu(byPluginNameMenus[*i]);
Chris@37 1576 pendingMenus.insert(byPluginNameMenus[*i]);
Chris@34 1577
Chris@288 1578 QString byMakerLabel = tr("%1 by Maker")
Chris@288 1579 .arg(factory->getTransformTypeName(*i));
Chris@37 1580 SubdividingMenu *byMakerMenu = new SubdividingMenu(byMakerLabel, 20, 40);
Chris@97 1581 byMakerMenu->setTearOffEnabled(true);
Chris@37 1582 m_transformsMenu->addMenu(byMakerMenu);
Chris@211 1583 m_rightButtonTransformsMenu->addMenu(byMakerMenu);
Chris@37 1584 pendingMenus.insert(byMakerMenu);
Chris@33 1585
Chris@288 1586 vector<QString> makers = factory->getTransformMakers(*i);
Chris@37 1587
Chris@33 1588 for (vector<QString>::iterator j = makers.begin();
Chris@33 1589 j != makers.end(); ++j) {
Chris@33 1590
Chris@33 1591 QString maker = *j;
Chris@33 1592 if (maker == "") maker = tr("Unknown");
Chris@55 1593 maker.replace(QRegExp(tr(" [\\(<].*$")), "");
Chris@55 1594
Chris@37 1595 makerMenus[*i][maker] = new SubdividingMenu(maker, 30, 40);
Chris@97 1596 makerMenus[*i][maker]->setTearOffEnabled(true);
Chris@37 1597 byMakerMenu->addMenu(makerMenus[*i][maker]);
Chris@37 1598 pendingMenus.insert(makerMenus[*i][maker]);
Chris@33 1599 }
Chris@0 1600 }
Chris@0 1601
Chris@230 1602 // Names should only be duplicated here if they have the same
Chris@230 1603 // plugin name, output name and maker but are in different library
Chris@230 1604 // .so names -- that won't happen often I hope
Chris@230 1605 std::map<QString, QString> idNameSonameMap;
Chris@230 1606 std::set<QString> seenNames, duplicateNames;
Chris@230 1607 for (unsigned int i = 0; i < transforms.size(); ++i) {
Chris@230 1608 QString name = transforms[i].name;
Chris@230 1609 if (seenNames.find(name) != seenNames.end()) {
Chris@230 1610 duplicateNames.insert(name);
Chris@230 1611 } else {
Chris@230 1612 seenNames.insert(name);
Chris@230 1613 }
Chris@230 1614 }
Chris@230 1615
Chris@0 1616 for (unsigned int i = 0; i < transforms.size(); ++i) {
Chris@0 1617
Chris@107 1618 QString name = transforms[i].name;
Chris@107 1619 if (name == "") name = transforms[i].identifier;
Chris@107 1620
Chris@665 1621 // cerr << "Plugin Name: " << name << endl;
Chris@80 1622
Chris@288 1623 TransformDescription::Type type = transforms[i].type;
Chris@288 1624 QString typeStr = factory->getTransformTypeName(type);
Chris@33 1625
Chris@33 1626 QString category = transforms[i].category;
Chris@33 1627 if (category == "") category = tr("Unclassified");
Chris@33 1628
Chris@33 1629 QString maker = transforms[i].maker;
Chris@33 1630 if (maker == "") maker = tr("Unknown");
Chris@55 1631 maker.replace(QRegExp(tr(" [\\(<].*$")), "");
Chris@33 1632
Chris@107 1633 QString pluginName = name.section(": ", 0, 0);
Chris@107 1634 QString output = name.section(": ", 1);
Chris@107 1635
Chris@230 1636 if (duplicateNames.find(pluginName) != duplicateNames.end()) {
Chris@230 1637 pluginName = QString("%1 <%2>")
Chris@230 1638 .arg(pluginName)
Chris@230 1639 .arg(transforms[i].identifier.section(':', 1, 1));
Chris@230 1640 if (output == "") name = pluginName;
Chris@230 1641 else name = QString("%1: %2")
Chris@230 1642 .arg(pluginName)
Chris@230 1643 .arg(output);
Chris@230 1644 }
Chris@230 1645
Chris@107 1646 QAction *action = new QAction(tr("%1...").arg(name), this);
Chris@0 1647 connect(action, SIGNAL(triggered()), this, SLOT(addLayer()));
Chris@107 1648 m_transformActions[action] = transforms[i].identifier;
Chris@107 1649 m_transformActionsReverse[transforms[i].identifier] = action;
Chris@0 1650 connect(this, SIGNAL(canAddLayer(bool)), action, SLOT(setEnabled(bool)));
Chris@33 1651
Chris@272 1652 action->setStatusTip(transforms[i].longDescription);
Chris@90 1653
Chris@33 1654 if (categoryMenus[type].find(category) == categoryMenus[type].end()) {
Chris@665 1655 cerr << "WARNING: MainWindow::setupMenus: Internal error: "
Chris@33 1656 << "No category menu for transform \""
Chris@432 1657 << name << "\" (category = \""
Chris@665 1658 << category << "\")" << endl;
Chris@33 1659 } else {
Chris@33 1660 categoryMenus[type][category]->addAction(action);
Chris@33 1661 }
Chris@33 1662
Chris@33 1663 if (makerMenus[type].find(maker) == makerMenus[type].end()) {
Chris@665 1664 cerr << "WARNING: MainWindow::setupMenus: Internal error: "
Chris@33 1665 << "No maker menu for transform \""
Chris@432 1666 << name << "\" (maker = \""
Chris@665 1667 << maker << "\")" << endl;
Chris@33 1668 } else {
Chris@80 1669 makerMenus[type][maker]->addAction(action);
Chris@33 1670 }
Chris@33 1671
Chris@33 1672 action = new QAction(tr("%1...").arg(output == "" ? pluginName : output), this);
Chris@33 1673 connect(action, SIGNAL(triggered()), this, SLOT(addLayer()));
Chris@107 1674 m_transformActions[action] = transforms[i].identifier;
Chris@33 1675 connect(this, SIGNAL(canAddLayer(bool)), action, SLOT(setEnabled(bool)));
Chris@272 1676 action->setStatusTip(transforms[i].longDescription);
Chris@33 1677
Chris@432 1678 // cerr << "Transform: \"" << name << "\": plugin name \"" << pluginName << "\"" << endl;
Chris@34 1679
Chris@33 1680 if (pluginNameMenus[type].find(pluginName) ==
Chris@33 1681 pluginNameMenus[type].end()) {
Chris@33 1682
Chris@36 1683 SubdividingMenu *parentMenu = byPluginNameMenus[type];
Chris@97 1684 parentMenu->setTearOffEnabled(true);
Chris@34 1685
Chris@33 1686 if (output == "") {
Chris@36 1687 parentMenu->addAction(pluginName, action);
Chris@33 1688 } else {
Chris@34 1689 pluginNameMenus[type][pluginName] =
Chris@34 1690 parentMenu->addMenu(pluginName);
Chris@33 1691 connect(this, SIGNAL(canAddLayer(bool)),
Chris@33 1692 pluginNameMenus[type][pluginName],
Chris@33 1693 SLOT(setEnabled(bool)));
Chris@33 1694 }
Chris@33 1695 }
Chris@33 1696
Chris@33 1697 if (pluginNameMenus[type].find(pluginName) !=
Chris@33 1698 pluginNameMenus[type].end()) {
Chris@33 1699 pluginNameMenus[type][pluginName]->addAction(action);
Chris@33 1700 }
Chris@0 1701 }
Chris@0 1702
Chris@37 1703 for (set<SubdividingMenu *>::iterator i = pendingMenus.begin();
Chris@37 1704 i != pendingMenus.end(); ++i) {
Chris@37 1705 (*i)->entriesAdded();
Chris@37 1706 }
Chris@37 1707
Chris@273 1708 m_transformsMenu->addSeparator();
Chris@273 1709 m_rightButtonTransformsMenu->addSeparator();
Chris@273 1710
Chris@273 1711 QAction *action = new QAction(tr("Find a Transform..."), this);
Chris@273 1712 action->setStatusTip(tr("Search for a transform from the installed plugins, by name or description"));
Chris@275 1713 action->setShortcut(tr("Ctrl+M"));
Chris@273 1714 connect(action, SIGNAL(triggered()), this, SLOT(findTransform()));
Chris@287 1715 // connect(this, SIGNAL(canAddLayer(bool)), action, SLOT(setEnabled(bool)));
Chris@275 1716 m_keyReference->registerShortcut(action);
Chris@273 1717 m_transformsMenu->addAction(action);
Chris@273 1718 m_rightButtonTransformsMenu->addAction(action);
Chris@273 1719
Chris@211 1720 setupRecentTransformsMenu();
Chris@66 1721 }
Chris@66 1722
Chris@66 1723 void
Chris@66 1724 MainWindow::setupHelpMenu()
Chris@66 1725 {
Chris@66 1726 QMenu *menu = menuBar()->addMenu(tr("&Help"));
Chris@97 1727 menu->setTearOffEnabled(true);
Chris@66 1728
Chris@162 1729 m_keyReference->setCategory(tr("Help"));
Chris@162 1730
Chris@168 1731 IconLoader il;
Chris@168 1732
Chris@518 1733 QString name = QApplication::applicationName();
Chris@518 1734
Chris@168 1735 QAction *action = new QAction(il.load("help"),
Chris@138 1736 tr("&Help Reference"), this);
Chris@162 1737 action->setShortcut(tr("F1"));
Chris@518 1738 action->setStatusTip(tr("Open the %1 reference manual").arg(name));
Chris@66 1739 connect(action, SIGNAL(triggered()), this, SLOT(help()));
Chris@162 1740 m_keyReference->registerShortcut(action);
Chris@0 1741 menu->addAction(action);
Chris@162 1742
Chris@163 1743 action = new QAction(tr("&Key and Mouse Reference"), this);
Chris@162 1744 action->setShortcut(tr("F2"));
Chris@518 1745 action->setStatusTip(tr("Open a window showing the keystrokes you can use in %1").arg(name));
Chris@162 1746 connect(action, SIGNAL(triggered()), this, SLOT(keyReference()));
Chris@162 1747 m_keyReference->registerShortcut(action);
Chris@162 1748 menu->addAction(action);
Chris@66 1749
Chris@518 1750 action = new QAction(tr("%1 on the &Web").arg(name), this);
Chris@518 1751 action->setStatusTip(tr("Open the %1 website").arg(name));
Chris@164 1752 connect(action, SIGNAL(triggered()), this, SLOT(website()));
Chris@164 1753 menu->addAction(action);
Chris@164 1754
Chris@518 1755 action = new QAction(tr("&About %1").arg(name), this);
Chris@518 1756 action->setStatusTip(tr("Show information about %1").arg(name));
Chris@66 1757 connect(action, SIGNAL(triggered()), this, SLOT(about()));
Chris@0 1758 menu->addAction(action);
Chris@0 1759 }
Chris@0 1760
Chris@0 1761 void
Chris@0 1762 MainWindow::setupRecentFilesMenu()
Chris@0 1763 {
Chris@0 1764 m_recentFilesMenu->clear();
Chris@34 1765 vector<QString> files = m_recentFiles.getRecent();
Chris@0 1766 for (size_t i = 0; i < files.size(); ++i) {
Chris@1253 1767 /* F. Nicol patch 13 Aug. 2016 */
Chris@1253 1768 const QString& path = files[i];
Chris@1253 1769 QAction *action = new QAction(path, this);
Chris@1253 1770 connect(action, &QAction::triggered, [this, path] { openRecentFile(path);});
Chris@1253 1771 /* end of patch */
Chris@162 1772 if (i == 0) {
Chris@162 1773 action->setShortcut(tr("Ctrl+R"));
Chris@162 1774 m_keyReference->registerShortcut
Chris@163 1775 (tr("Re-open"),
Chris@528 1776 action->shortcut().toString(),
Chris@163 1777 tr("Re-open the current or most recently opened file"));
Chris@162 1778 }
Chris@0 1779 m_recentFilesMenu->addAction(action);
Chris@0 1780 }
Chris@0 1781 }
Chris@0 1782
Chris@0 1783 void
Chris@423 1784 MainWindow::setupTemplatesMenu()
Chris@423 1785 {
Chris@423 1786 m_templatesMenu->clear();
Chris@423 1787
Chris@455 1788 QAction *defaultAction = new QAction(tr("Standard Waveform"), this);
Chris@435 1789 defaultAction->setObjectName("default");
Chris@435 1790 connect(defaultAction, SIGNAL(triggered()), this, SLOT(applyTemplate()));
Chris@435 1791 m_templatesMenu->addAction(defaultAction);
Chris@435 1792
Chris@435 1793 m_templatesMenu->addSeparator();
Chris@435 1794
Chris@435 1795 QAction *action = 0;
Chris@435 1796
Chris@435 1797 QStringList templates = ResourceFinder().getResourceFiles("templates", "svt");
Chris@435 1798
Chris@436 1799 bool havePersonal = false;
Chris@436 1800
Chris@435 1801 // (ordered by name)
Chris@435 1802 std::set<QString> byName;
Chris@435 1803 foreach (QString t, templates) {
Chris@436 1804 if (!t.startsWith(":")) havePersonal = true;
Chris@435 1805 byName.insert(QFileInfo(t).baseName());
Chris@435 1806 }
Chris@435 1807
Chris@435 1808 foreach (QString t, byName) {
Chris@435 1809 if (t.toLower() == "default") continue;
Chris@435 1810 action = new QAction(t, this);
Chris@435 1811 connect(action, SIGNAL(triggered()), this, SLOT(applyTemplate()));
Chris@435 1812 m_templatesMenu->addAction(action);
Chris@435 1813 }
Chris@435 1814
Chris@435 1815 if (!templates.empty()) m_templatesMenu->addSeparator();
Chris@435 1816
Chris@435 1817 if (!m_templateWatcher) {
Chris@435 1818 m_templateWatcher = new QFileSystemWatcher(this);
Chris@435 1819 m_templateWatcher->addPath(ResourceFinder().getResourceSaveDir("templates"));
Chris@435 1820 connect(m_templateWatcher, SIGNAL(directoryChanged(const QString &)),
Chris@435 1821 this, SLOT(setupTemplatesMenu()));
Chris@435 1822 }
Chris@436 1823
Chris@436 1824 QAction *setDefaultAction = new QAction(tr("Choose Default Template..."), this);
Chris@436 1825 setDefaultAction->setObjectName("set_default_template");
Chris@436 1826 connect(setDefaultAction, SIGNAL(triggered()), this, SLOT(preferences()));
Chris@436 1827 m_templatesMenu->addSeparator();
Chris@436 1828 m_templatesMenu->addAction(setDefaultAction);
Chris@436 1829
Chris@436 1830 m_manageTemplatesAction->setEnabled(havePersonal);
Chris@435 1831 }
Chris@435 1832
Chris@423 1833
Chris@423 1834 void
Chris@211 1835 MainWindow::setupRecentTransformsMenu()
Chris@34 1836 {
Chris@211 1837 m_recentTransformsMenu->clear();
Chris@211 1838 vector<QString> transforms = m_recentTransforms.getRecent();
Chris@34 1839 for (size_t i = 0; i < transforms.size(); ++i) {
Chris@211 1840 TransformActionReverseMap::iterator ti =
Chris@34 1841 m_transformActionsReverse.find(transforms[i]);
Chris@34 1842 if (ti == m_transformActionsReverse.end()) {
Chris@665 1843 cerr << "WARNING: MainWindow::setupRecentTransformsMenu: "
Chris@665 1844 << "Unknown transform \"" << transforms[i]
Chris@665 1845 << "\" in recent transforms list" << endl;
Chris@34 1846 continue;
Chris@34 1847 }
Chris@162 1848 if (i == 0) {
Chris@162 1849 ti->second->setShortcut(tr("Ctrl+T"));
Chris@162 1850 m_keyReference->registerShortcut
Chris@211 1851 (tr("Repeat Transform"),
Chris@528 1852 ti->second->shortcut().toString(),
Chris@163 1853 tr("Re-select the most recently run transform"));
Chris@216 1854 } else {
Chris@216 1855 ti->second->setShortcut(QString(""));
Chris@162 1856 }
Chris@211 1857 m_recentTransformsMenu->addAction(ti->second);
Chris@34 1858 }
Chris@34 1859 }
Chris@34 1860
Chris@34 1861 void
Chris@95 1862 MainWindow::setupExistingLayersMenus()
Chris@0 1863 {
Chris@0 1864 if (!m_existingLayersMenu) return; // should have been created by setupMenus
Chris@0 1865
Chris@438 1866 // SVDEBUG << "MainWindow::setupExistingLayersMenu" << endl;
Chris@0 1867
Chris@0 1868 m_existingLayersMenu->clear();
Chris@0 1869 m_existingLayerActions.clear();
Chris@0 1870
Chris@95 1871 m_sliceMenu->clear();
Chris@95 1872 m_sliceActions.clear();
Chris@95 1873
Chris@168 1874 IconLoader il;
Chris@168 1875
Chris@33 1876 vector<Layer *> orderedLayers;
Chris@33 1877 set<Layer *> observedLayers;
Chris@95 1878 set<Layer *> sliceableLayers;
Chris@95 1879
Chris@95 1880 LayerFactory *factory = LayerFactory::getInstance();
Chris@0 1881
Chris@0 1882 for (int i = 0; i < m_paneStack->getPaneCount(); ++i) {
Chris@0 1883
Chris@0 1884 Pane *pane = m_paneStack->getPane(i);
Chris@0 1885 if (!pane) continue;
Chris@0 1886
Chris@0 1887 for (int j = 0; j < pane->getLayerCount(); ++j) {
Chris@0 1888
Chris@0 1889 Layer *layer = pane->getLayer(j);
Chris@0 1890 if (!layer) continue;
Chris@0 1891 if (observedLayers.find(layer) != observedLayers.end()) {
Chris@665 1892 // cerr << "found duplicate layer " << layer << endl;
Chris@0 1893 continue;
Chris@0 1894 }
Chris@0 1895
Chris@665 1896 // cerr << "found new layer " << layer << " (name = "
Chris@665 1897 // << layer->getLayerPresentationName() << ")" << endl;
Chris@0 1898
Chris@0 1899 orderedLayers.push_back(layer);
Chris@0 1900 observedLayers.insert(layer);
Chris@95 1901
Chris@95 1902 if (factory->isLayerSliceable(layer)) {
Chris@95 1903 sliceableLayers.insert(layer);
Chris@95 1904 }
Chris@0 1905 }
Chris@0 1906 }
Chris@0 1907
Chris@33 1908 map<QString, int> observedNames;
Chris@0 1909
Chris@137 1910 for (size_t i = 0; i < orderedLayers.size(); ++i) {
Chris@0 1911
Chris@95 1912 Layer *layer = orderedLayers[i];
Chris@95 1913
Chris@95 1914 QString name = layer->getLayerPresentationName();
Chris@0 1915 int n = ++observedNames[name];
Chris@0 1916 if (n > 1) name = QString("%1 <%2>").arg(name).arg(n);
Chris@0 1917
Chris@168 1918 QIcon icon = il.load(factory->getLayerIconName
Chris@168 1919 (factory->getLayerType(layer)));
Chris@95 1920
Chris@95 1921 QAction *action = new QAction(icon, name, this);
Chris@0 1922 connect(action, SIGNAL(triggered()), this, SLOT(addLayer()));
Chris@0 1923 connect(this, SIGNAL(canAddLayer(bool)), action, SLOT(setEnabled(bool)));
Chris@95 1924 m_existingLayerActions[action] = layer;
Chris@0 1925
Chris@0 1926 m_existingLayersMenu->addAction(action);
Chris@95 1927
Chris@95 1928 if (sliceableLayers.find(layer) != sliceableLayers.end()) {
Chris@95 1929 action = new QAction(icon, name, this);
Chris@95 1930 connect(action, SIGNAL(triggered()), this, SLOT(addLayer()));
Chris@95 1931 connect(this, SIGNAL(canAddLayer(bool)), action, SLOT(setEnabled(bool)));
Chris@95 1932 m_sliceActions[action] = layer;
Chris@95 1933 m_sliceMenu->addAction(action);
Chris@95 1934 }
Chris@0 1935 }
Chris@95 1936
Chris@95 1937 m_sliceMenu->setEnabled(!m_sliceActions.empty());
Chris@0 1938 }
Chris@0 1939
Chris@0 1940 void
Chris@0 1941 MainWindow::setupToolbars()
Chris@0 1942 {
Chris@162 1943 m_keyReference->setCategory(tr("Playback and Transport Controls"));
Chris@162 1944
Chris@168 1945 IconLoader il;
Chris@168 1946
Chris@155 1947 QMenu *menu = m_playbackMenu = menuBar()->addMenu(tr("Play&back"));
Chris@155 1948 menu->setTearOffEnabled(true);
Chris@155 1949 m_rightButtonMenu->addSeparator();
Chris@155 1950 m_rightButtonPlaybackMenu = m_rightButtonMenu->addMenu(tr("Playback"));
Chris@155 1951
Chris@155 1952 QToolBar *toolbar = addToolBar(tr("Playback Toolbar"));
Chris@155 1953
Chris@265 1954 m_rwdStartAction = toolbar->addAction(il.load("rewind-start"),
Chris@265 1955 tr("Rewind to Start"));
Chris@265 1956 m_rwdStartAction->setShortcut(tr("Home"));
Chris@265 1957 m_rwdStartAction->setStatusTip(tr("Rewind to the start"));
Chris@265 1958 connect(m_rwdStartAction, SIGNAL(triggered()), this, SLOT(rewindStart()));
Chris@265 1959 connect(this, SIGNAL(canPlay(bool)), m_rwdStartAction, SLOT(setEnabled(bool)));
Chris@265 1960
Chris@265 1961 m_rwdAction = toolbar->addAction(il.load("rewind"), tr("Rewind"));
Chris@155 1962 m_rwdAction->setShortcut(tr("PgUp"));
Chris@163 1963 m_rwdAction->setStatusTip(tr("Rewind to the previous time instant or time ruler notch"));
Chris@155 1964 connect(m_rwdAction, SIGNAL(triggered()), this, SLOT(rewind()));
Chris@155 1965 connect(this, SIGNAL(canRewind(bool)), m_rwdAction, SLOT(setEnabled(bool)));
Chris@155 1966
Chris@323 1967 m_rwdSimilarAction = new QAction(tr("Rewind to Similar Point"), this);
Chris@323 1968 m_rwdSimilarAction->setShortcut(tr("Shift+PgUp"));
Chris@323 1969 m_rwdSimilarAction->setStatusTip(tr("Rewind to the previous similarly valued time instant"));
Chris@323 1970 connect(m_rwdSimilarAction, SIGNAL(triggered()), this, SLOT(rewindSimilar()));
Chris@323 1971 connect(this, SIGNAL(canRewind(bool)), m_rwdSimilarAction, SLOT(setEnabled(bool)));
Chris@323 1972
Chris@265 1973 m_playAction = toolbar->addAction(il.load("playpause"),
Chris@265 1974 tr("Play / Pause"));
Chris@265 1975 m_playAction->setCheckable(true);
Chris@265 1976 m_playAction->setShortcut(tr("Space"));
Chris@265 1977 m_playAction->setStatusTip(tr("Start or stop playback from the current position"));
Chris@265 1978 connect(m_playAction, SIGNAL(triggered()), this, SLOT(play()));
Chris@0 1979 connect(m_playSource, SIGNAL(playStatusChanged(bool)),
Chris@265 1980 m_playAction, SLOT(setChecked(bool)));
Chris@305 1981 connect(m_playSource, SIGNAL(playStatusChanged(bool)),
Chris@305 1982 this, SLOT(playStatusChanged(bool)));
Chris@265 1983 connect(this, SIGNAL(canPlay(bool)), m_playAction, SLOT(setEnabled(bool)));
Chris@155 1984
Chris@168 1985 m_ffwdAction = toolbar->addAction(il.load("ffwd"),
Chris@286 1986 tr("Fast Forward"));
Chris@155 1987 m_ffwdAction->setShortcut(tr("PgDown"));
Chris@163 1988 m_ffwdAction->setStatusTip(tr("Fast-forward to the next time instant or time ruler notch"));
Chris@155 1989 connect(m_ffwdAction, SIGNAL(triggered()), this, SLOT(ffwd()));
Chris@155 1990 connect(this, SIGNAL(canFfwd(bool)), m_ffwdAction, SLOT(setEnabled(bool)));
Chris@155 1991
Chris@323 1992 m_ffwdSimilarAction = new QAction(tr("Fast Forward to Similar Point"), this);
Chris@323 1993 m_ffwdSimilarAction->setShortcut(tr("Shift+PgDown"));
Chris@323 1994 m_ffwdSimilarAction->setStatusTip(tr("Fast-forward to the next similarly valued time instant"));
Chris@323 1995 connect(m_ffwdSimilarAction, SIGNAL(triggered()), this, SLOT(ffwdSimilar()));
Chris@323 1996 connect(this, SIGNAL(canFfwd(bool)), m_ffwdSimilarAction, SLOT(setEnabled(bool)));
Chris@323 1997
Chris@265 1998 m_ffwdEndAction = toolbar->addAction(il.load("ffwd-end"),
Chris@265 1999 tr("Fast Forward to End"));
Chris@265 2000 m_ffwdEndAction->setShortcut(tr("End"));
Chris@265 2001 m_ffwdEndAction->setStatusTip(tr("Fast-forward to the end"));
Chris@265 2002 connect(m_ffwdEndAction, SIGNAL(triggered()), this, SLOT(ffwdEnd()));
Chris@265 2003 connect(this, SIGNAL(canPlay(bool)), m_ffwdEndAction, SLOT(setEnabled(bool)));
Chris@0 2004
Chris@1047 2005 m_recordAction = toolbar->addAction(il.load("record"),
Chris@1047 2006 tr("Record"));
Chris@1047 2007 m_recordAction->setCheckable(true);
Chris@1047 2008 m_recordAction->setShortcut(tr("Ctrl+Space"));
Chris@1047 2009 m_recordAction->setStatusTip(tr("Record a new audio file"));
Chris@1047 2010 connect(m_recordAction, SIGNAL(triggered()), this, SLOT(record()));
Chris@1047 2011 connect(m_recordTarget, SIGNAL(recordStatusChanged(bool)),
Chris@1047 2012 m_recordAction, SLOT(setChecked(bool)));
Chris@1047 2013 connect(this, SIGNAL(canRecord(bool)),
Chris@1047 2014 m_recordAction, SLOT(setEnabled(bool)));
Chris@1047 2015
Chris@0 2016 toolbar = addToolBar(tr("Play Mode Toolbar"));
Chris@0 2017
Chris@265 2018 m_playSelectionAction = toolbar->addAction(il.load("playselection"),
Chris@265 2019 tr("Constrain Playback to Selection"));
Chris@265 2020 m_playSelectionAction->setCheckable(true);
Chris@265 2021 m_playSelectionAction->setChecked(m_viewManager->getPlaySelectionMode());
Chris@265 2022 m_playSelectionAction->setShortcut(tr("s"));
Chris@265 2023 m_playSelectionAction->setStatusTip(tr("Constrain playback to the selected regions"));
Chris@69 2024 connect(m_viewManager, SIGNAL(playSelectionModeChanged(bool)),
Chris@265 2025 m_playSelectionAction, SLOT(setChecked(bool)));
Chris@265 2026 connect(m_playSelectionAction, SIGNAL(triggered()), this, SLOT(playSelectionToggled()));
Chris@265 2027 connect(this, SIGNAL(canPlaySelection(bool)), m_playSelectionAction, SLOT(setEnabled(bool)));
Chris@265 2028
Chris@265 2029 m_playLoopAction = toolbar->addAction(il.load("playloop"),
Chris@265 2030 tr("Loop Playback"));
Chris@265 2031 m_playLoopAction->setCheckable(true);
Chris@265 2032 m_playLoopAction->setChecked(m_viewManager->getPlayLoopMode());
Chris@265 2033 m_playLoopAction->setShortcut(tr("l"));
Chris@265 2034 m_playLoopAction->setStatusTip(tr("Loop playback"));
Chris@69 2035 connect(m_viewManager, SIGNAL(playLoopModeChanged(bool)),
Chris@265 2036 m_playLoopAction, SLOT(setChecked(bool)));
Chris@265 2037 connect(m_playLoopAction, SIGNAL(triggered()), this, SLOT(playLoopToggled()));
Chris@265 2038 connect(this, SIGNAL(canPlay(bool)), m_playLoopAction, SLOT(setEnabled(bool)));
Chris@155 2039
Chris@207 2040 m_soloAction = toolbar->addAction(il.load("solo"),
Chris@323 2041 tr("Solo Current Pane"));
Chris@207 2042 m_soloAction->setCheckable(true);
Chris@207 2043 m_soloAction->setChecked(m_viewManager->getPlaySoloMode());
Chris@207 2044 m_prevSolo = m_viewManager->getPlaySoloMode();
Chris@207 2045 m_soloAction->setShortcut(tr("o"));
Chris@207 2046 m_soloAction->setStatusTip(tr("Solo the current pane during playback"));
Chris@180 2047 connect(m_viewManager, SIGNAL(playSoloModeChanged(bool)),
Chris@207 2048 m_soloAction, SLOT(setChecked(bool)));
Chris@207 2049 connect(m_soloAction, SIGNAL(triggered()), this, SLOT(playSoloToggled()));
Chris@207 2050 connect(this, SIGNAL(canChangeSolo(bool)), m_soloAction, SLOT(setEnabled(bool)));
Chris@180 2051
Chris@208 2052 QAction *alAction = 0;
Chris@208 2053 if (Document::canAlign()) {
Chris@208 2054 alAction = toolbar->addAction(il.load("align"),
Chris@208 2055 tr("Align File Timelines"));
Chris@208 2056 alAction->setCheckable(true);
Chris@208 2057 alAction->setChecked(m_viewManager->getAlignMode());
Chris@208 2058 alAction->setStatusTip(tr("Treat multiple audio files as versions of the same work, and align their timelines"));
Chris@208 2059 connect(m_viewManager, SIGNAL(alignModeChanged(bool)),
Chris@208 2060 alAction, SLOT(setChecked(bool)));
Chris@208 2061 connect(alAction, SIGNAL(triggered()), this, SLOT(alignToggled()));
Chris@208 2062 connect(this, SIGNAL(canAlign(bool)), alAction, SLOT(setEnabled(bool)));
Chris@208 2063 }
Chris@206 2064
Chris@265 2065 m_keyReference->registerShortcut(m_playAction);
Chris@1056 2066 m_keyReference->registerShortcut(m_recordAction);
Chris@265 2067 m_keyReference->registerShortcut(m_playSelectionAction);
Chris@265 2068 m_keyReference->registerShortcut(m_playLoopAction);
Chris@207 2069 m_keyReference->registerShortcut(m_soloAction);
Chris@208 2070 if (alAction) m_keyReference->registerShortcut(alAction);
Chris@162 2071 m_keyReference->registerShortcut(m_rwdAction);
Chris@162 2072 m_keyReference->registerShortcut(m_ffwdAction);
Chris@323 2073 m_keyReference->registerShortcut(m_rwdSimilarAction);
Chris@323 2074 m_keyReference->registerShortcut(m_ffwdSimilarAction);
Chris@265 2075 m_keyReference->registerShortcut(m_rwdStartAction);
Chris@265 2076 m_keyReference->registerShortcut(m_ffwdEndAction);
Chris@265 2077
Chris@265 2078 menu->addAction(m_playAction);
Chris@1056 2079 menu->addAction(m_recordAction);
Chris@265 2080 menu->addAction(m_playSelectionAction);
Chris@265 2081 menu->addAction(m_playLoopAction);
Chris@207 2082 menu->addAction(m_soloAction);
Chris@208 2083 if (alAction) menu->addAction(alAction);
Chris@155 2084 menu->addSeparator();
Chris@155 2085 menu->addAction(m_rwdAction);
Chris@155 2086 menu->addAction(m_ffwdAction);
Chris@155 2087 menu->addSeparator();
Chris@323 2088 menu->addAction(m_rwdSimilarAction);
Chris@323 2089 menu->addAction(m_ffwdSimilarAction);
Chris@323 2090 menu->addSeparator();
Chris@265 2091 menu->addAction(m_rwdStartAction);
Chris@265 2092 menu->addAction(m_ffwdEndAction);
Chris@155 2093 menu->addSeparator();
Chris@1055 2094 menu->addAction(m_recordAction);
Chris@1055 2095 menu->addSeparator();
Chris@155 2096
Chris@265 2097 m_rightButtonPlaybackMenu->addAction(m_playAction);
Chris@1056 2098 m_rightButtonPlaybackMenu->addAction(m_recordAction);
Chris@265 2099 m_rightButtonPlaybackMenu->addAction(m_playSelectionAction);
Chris@265 2100 m_rightButtonPlaybackMenu->addAction(m_playLoopAction);
Chris@207 2101 m_rightButtonPlaybackMenu->addAction(m_soloAction);
Chris@208 2102 if (alAction) m_rightButtonPlaybackMenu->addAction(alAction);
Chris@155 2103 m_rightButtonPlaybackMenu->addSeparator();
Chris@155 2104 m_rightButtonPlaybackMenu->addAction(m_rwdAction);
Chris@155 2105 m_rightButtonPlaybackMenu->addAction(m_ffwdAction);
Chris@155 2106 m_rightButtonPlaybackMenu->addSeparator();
Chris@265 2107 m_rightButtonPlaybackMenu->addAction(m_rwdStartAction);
Chris@265 2108 m_rightButtonPlaybackMenu->addAction(m_ffwdEndAction);
Chris@155 2109 m_rightButtonPlaybackMenu->addSeparator();
Chris@1055 2110 m_rightButtonPlaybackMenu->addAction(m_recordAction);
Chris@1055 2111 m_rightButtonPlaybackMenu->addSeparator();
Chris@155 2112
Chris@155 2113 QAction *fastAction = menu->addAction(tr("Speed Up"));
Chris@155 2114 fastAction->setShortcut(tr("Ctrl+PgUp"));
Chris@163 2115 fastAction->setStatusTip(tr("Time-stretch playback to speed it up without changing pitch"));
Chris@155 2116 connect(fastAction, SIGNAL(triggered()), this, SLOT(speedUpPlayback()));
Chris@155 2117 connect(this, SIGNAL(canSpeedUpPlayback(bool)), fastAction, SLOT(setEnabled(bool)));
Chris@155 2118
Chris@155 2119 QAction *slowAction = menu->addAction(tr("Slow Down"));
Chris@155 2120 slowAction->setShortcut(tr("Ctrl+PgDown"));
Chris@163 2121 slowAction->setStatusTip(tr("Time-stretch playback to slow it down without changing pitch"));
Chris@155 2122 connect(slowAction, SIGNAL(triggered()), this, SLOT(slowDownPlayback()));
Chris@155 2123 connect(this, SIGNAL(canSlowDownPlayback(bool)), slowAction, SLOT(setEnabled(bool)));
Chris@155 2124
Chris@155 2125 QAction *normalAction = menu->addAction(tr("Restore Normal Speed"));
Chris@155 2126 normalAction->setShortcut(tr("Ctrl+Home"));
Chris@163 2127 normalAction->setStatusTip(tr("Restore non-time-stretched playback"));
Chris@155 2128 connect(normalAction, SIGNAL(triggered()), this, SLOT(restoreNormalPlayback()));
Chris@155 2129 connect(this, SIGNAL(canChangePlaybackSpeed(bool)), normalAction, SLOT(setEnabled(bool)));
Chris@155 2130
Chris@162 2131 m_keyReference->registerShortcut(fastAction);
Chris@162 2132 m_keyReference->registerShortcut(slowAction);
Chris@162 2133 m_keyReference->registerShortcut(normalAction);
Chris@162 2134
Chris@155 2135 m_rightButtonPlaybackMenu->addAction(fastAction);
Chris@155 2136 m_rightButtonPlaybackMenu->addAction(slowAction);
Chris@155 2137 m_rightButtonPlaybackMenu->addAction(normalAction);
Chris@0 2138
Chris@0 2139 toolbar = addToolBar(tr("Edit Toolbar"));
Chris@0 2140 CommandHistory::getInstance()->registerToolbar(toolbar);
Chris@0 2141
Chris@0 2142 toolbar = addToolBar(tr("Tools Toolbar"));
Chris@0 2143 QActionGroup *group = new QActionGroup(this);
Chris@0 2144
Chris@705 2145 m_keyReference->setCategory(tr("Tool Selection"));
Chris@168 2146 QAction *action = toolbar->addAction(il.load("navigate"),
Chris@155 2147 tr("Navigate"));
Chris@0 2148 action->setCheckable(true);
Chris@0 2149 action->setChecked(true);
Chris@0 2150 action->setShortcut(tr("1"));
Chris@90 2151 action->setStatusTip(tr("Navigate"));
Chris@0 2152 connect(action, SIGNAL(triggered()), this, SLOT(toolNavigateSelected()));
Chris@596 2153 connect(this, SIGNAL(replacedDocument()), action, SLOT(trigger()));
Chris@0 2154 group->addAction(action);
Chris@162 2155 m_keyReference->registerShortcut(action);
Chris@0 2156 m_toolActions[ViewManager::NavigateMode] = action;
Chris@705 2157
Chris@705 2158 m_keyReference->setCategory
Chris@705 2159 (tr("Navigate Tool Mouse Actions"));
Chris@705 2160 m_keyReference->registerShortcut
Chris@705 2161 (tr("Navigate"), tr("Left"),
Chris@705 2162 tr("Click left button and drag to move around"));
Chris@705 2163 m_keyReference->registerShortcut
Chris@705 2164 (tr("Zoom to Area"), tr("Shift+Left"),
Chris@705 2165 tr("Shift-click left button and drag to zoom to a rectangular area"));
Chris@705 2166 m_keyReference->registerShortcut
Chris@705 2167 (tr("Relocate"), tr("Double-Click Left"),
Chris@705 2168 tr("Double-click left button to jump to clicked location"));
Chris@705 2169 m_keyReference->registerShortcut
Chris@705 2170 (tr("Edit"), tr("Double-Click Left"),
Chris@705 2171 tr("Double-click left button on an item to edit it"));
Chris@705 2172
Chris@705 2173 m_keyReference->setCategory(tr("Tool Selection"));
Chris@168 2174 action = toolbar->addAction(il.load("select"),
Chris@0 2175 tr("Select"));
Chris@0 2176 action->setCheckable(true);
Chris@0 2177 action->setShortcut(tr("2"));
Chris@90 2178 action->setStatusTip(tr("Select ranges"));
Chris@0 2179 connect(action, SIGNAL(triggered()), this, SLOT(toolSelectSelected()));
Chris@0 2180 group->addAction(action);
Chris@162 2181 m_keyReference->registerShortcut(action);
Chris@0 2182 m_toolActions[ViewManager::SelectMode] = action;
Chris@705 2183
Chris@705 2184 m_keyReference->setCategory
Chris@705 2185 (tr("Select Tool Mouse Actions"));
Chris@705 2186 m_keyReference->registerShortcut
Chris@705 2187 (tr("Select"), tr("Left"),
Chris@705 2188 tr("Click left button and drag to select region; drag region edge to resize"));
Chris@705 2189 #ifdef Q_OS_MAC
Chris@705 2190 m_keyReference->registerShortcut
Chris@705 2191 (tr("Multi Select"), tr("Ctrl+Left"),
Chris@705 2192 tr("Cmd-click left button and drag to select an additional region"));
Chris@705 2193 #else
Chris@705 2194 m_keyReference->registerShortcut
Chris@705 2195 (tr("Multi Select"), tr("Ctrl+Left"),
Chris@705 2196 tr("Ctrl-click left button and drag to select an additional region"));
Chris@705 2197 #endif
Chris@705 2198 m_keyReference->registerShortcut
Chris@705 2199 (tr("Fine Select"), tr("Shift+Left"),
Chris@705 2200 tr("Shift-click left button and drag to select without snapping to items or grid"));
Chris@705 2201
Chris@705 2202 m_keyReference->setCategory(tr("Tool Selection"));
Chris@168 2203 action = toolbar->addAction(il.load("move"),
Chris@0 2204 tr("Edit"));
Chris@0 2205 action->setCheckable(true);
Chris@0 2206 action->setShortcut(tr("3"));
Chris@90 2207 action->setStatusTip(tr("Edit items in layer"));
Chris@0 2208 connect(action, SIGNAL(triggered()), this, SLOT(toolEditSelected()));
Chris@0 2209 connect(this, SIGNAL(canEditLayer(bool)), action, SLOT(setEnabled(bool)));
Chris@0 2210 group->addAction(action);
Chris@162 2211 m_keyReference->registerShortcut(action);
Chris@0 2212 m_toolActions[ViewManager::EditMode] = action;
Chris@705 2213
Chris@705 2214 m_keyReference->setCategory
Chris@705 2215 (tr("Edit Tool Mouse Actions"));
Chris@705 2216 m_keyReference->registerShortcut
Chris@705 2217 (tr("Move"), tr("Left"),
Chris@705 2218 tr("Click left button on an item or selected region and drag to move"));
Chris@705 2219 m_keyReference->registerShortcut
Chris@705 2220 (tr("Edit"), tr("Double-Click Left"),
Chris@705 2221 tr("Double-click left button on an item to edit it"));
Chris@705 2222
Chris@705 2223 m_keyReference->setCategory(tr("Tool Selection"));
Chris@168 2224 action = toolbar->addAction(il.load("draw"),
Chris@0 2225 tr("Draw"));
Chris@0 2226 action->setCheckable(true);
Chris@0 2227 action->setShortcut(tr("4"));
Chris@90 2228 action->setStatusTip(tr("Draw new items in layer"));
Chris@0 2229 connect(action, SIGNAL(triggered()), this, SLOT(toolDrawSelected()));
Chris@0 2230 connect(this, SIGNAL(canEditLayer(bool)), action, SLOT(setEnabled(bool)));
Chris@0 2231 group->addAction(action);
Chris@162 2232 m_keyReference->registerShortcut(action);
Chris@0 2233 m_toolActions[ViewManager::DrawMode] = action;
Chris@0 2234
Chris@705 2235 m_keyReference->setCategory
Chris@705 2236 (tr("Draw Tool Mouse Actions"));
Chris@705 2237 m_keyReference->registerShortcut
Chris@705 2238 (tr("Draw"), tr("Left"),
Chris@705 2239 tr("Click left button and drag to create new item"));
Chris@705 2240
Chris@705 2241 m_keyReference->setCategory(tr("Tool Selection"));
Chris@217 2242 action = toolbar->addAction(il.load("erase"),
Chris@217 2243 tr("Erase"));
Chris@217 2244 action->setCheckable(true);
Chris@217 2245 action->setShortcut(tr("5"));
Chris@217 2246 action->setStatusTip(tr("Erase items from layer"));
Chris@217 2247 connect(action, SIGNAL(triggered()), this, SLOT(toolEraseSelected()));
Chris@217 2248 connect(this, SIGNAL(canEditLayer(bool)), action, SLOT(setEnabled(bool)));
Chris@217 2249 group->addAction(action);
Chris@217 2250 m_keyReference->registerShortcut(action);
Chris@217 2251 m_toolActions[ViewManager::EraseMode] = action;
Chris@217 2252
Chris@705 2253 m_keyReference->setCategory
Chris@705 2254 (tr("Erase Tool Mouse Actions"));
Chris@705 2255 m_keyReference->registerShortcut
Chris@705 2256 (tr("Erase"), tr("Left"),
Chris@705 2257 tr("Click left button on an item to remove it from the layer"));
Chris@705 2258
Chris@705 2259 m_keyReference->setCategory(tr("Tool Selection"));
Chris@265 2260 action = toolbar->addAction(il.load("measure"), tr("Measure"));
Chris@151 2261 action->setCheckable(true);
Chris@217 2262 action->setShortcut(tr("6"));
Chris@151 2263 action->setStatusTip(tr("Make measurements in layer"));
Chris@151 2264 connect(action, SIGNAL(triggered()), this, SLOT(toolMeasureSelected()));
Chris@169 2265 connect(this, SIGNAL(canMeasureLayer(bool)), action, SLOT(setEnabled(bool)));
Chris@151 2266 group->addAction(action);
Chris@162 2267 m_keyReference->registerShortcut(action);
Chris@151 2268 m_toolActions[ViewManager::MeasureMode] = action;
Chris@151 2269
Chris@705 2270 m_keyReference->setCategory
Chris@705 2271 (tr("Measure Tool Mouse Actions"));
Chris@705 2272 m_keyReference->registerShortcut
Chris@705 2273 (tr("Measure Area"), tr("Left"),
Chris@705 2274 tr("Click left button and drag to measure a rectangular area"));
Chris@705 2275 m_keyReference->registerShortcut
Chris@705 2276 (tr("Measure Item"), tr("Double-Click Left"),
Chris@705 2277 tr("Click left button and drag to measure extents of an item or shape"));
Chris@705 2278 m_keyReference->registerShortcut
Chris@705 2279 (tr("Zoom to Area"), tr("Shift+Left"),
Chris@705 2280 tr("Shift-click left button and drag to zoom to a rectangular area"));
Chris@705 2281
Chris@0 2282 toolNavigateSelected();
Chris@163 2283
Chris@163 2284 Pane::registerShortcuts(*m_keyReference);
Chris@0 2285 }
Chris@0 2286
Chris@0 2287 void
Chris@265 2288 MainWindow::connectLayerEditDialog(ModelDataTableDialog *dialog)
Chris@265 2289 {
Chris@265 2290 MainWindowBase::connectLayerEditDialog(dialog);
Chris@265 2291 QToolBar *toolbar = dialog->getPlayToolbar();
Chris@265 2292 if (toolbar) {
Chris@265 2293 toolbar->addAction(m_rwdStartAction);
Chris@265 2294 toolbar->addAction(m_rwdAction);
Chris@265 2295 toolbar->addAction(m_playAction);
Chris@265 2296 toolbar->addAction(m_ffwdAction);
Chris@265 2297 toolbar->addAction(m_ffwdEndAction);
Chris@265 2298 }
Chris@265 2299 }
Chris@265 2300
Chris@265 2301 void
Chris@0 2302 MainWindow::updateMenuStates()
Chris@0 2303 {
Chris@200 2304 MainWindowBase::updateMenuStates();
Chris@200 2305
Chris@117 2306 Pane *currentPane = 0;
Chris@117 2307 Layer *currentLayer = 0;
Chris@117 2308
Chris@117 2309 if (m_paneStack) currentPane = m_paneStack->getCurrentPane();
Chris@117 2310 if (currentPane) currentLayer = currentPane->getSelectedLayer();
Chris@117 2311
Chris@0 2312 bool haveCurrentPane =
Chris@117 2313 (currentPane != 0);
Chris@0 2314 bool haveCurrentLayer =
Chris@117 2315 (haveCurrentPane &&
Chris@117 2316 (currentLayer != 0));
Chris@206 2317 bool havePlayTarget =
Chris@1055 2318 (m_playTarget != 0 || m_audioIO != 0);
Chris@0 2319 bool haveSelection =
Chris@0 2320 (m_viewManager &&
Chris@0 2321 !m_viewManager->getSelections().empty());
Chris@0 2322 bool haveCurrentEditableLayer =
Chris@0 2323 (haveCurrentLayer &&
Chris@117 2324 currentLayer->isLayerEditable());
Chris@0 2325 bool haveCurrentTimeInstantsLayer =
Chris@0 2326 (haveCurrentLayer &&
Chris@117 2327 dynamic_cast<TimeInstantLayer *>(currentLayer));
Chris@0 2328 bool haveCurrentTimeValueLayer =
Chris@0 2329 (haveCurrentLayer &&
Chris@117 2330 dynamic_cast<TimeValueLayer *>(currentLayer));
Chris@207 2331
Chris@314 2332 bool alignMode = m_viewManager && m_viewManager->getAlignMode();
Chris@314 2333 emit canChangeSolo(havePlayTarget && !alignMode);
Chris@207 2334 emit canAlign(havePlayTarget && m_document && m_document->canAlign());
Chris@206 2335
Chris@200 2336 emit canChangePlaybackSpeed(true);
Chris@200 2337 int v = m_playSpeed->value();
Chris@200 2338 emit canSpeedUpPlayback(v < m_playSpeed->maximum());
Chris@200 2339 emit canSlowDownPlayback(v > m_playSpeed->minimum());
Chris@155 2340
Chris@164 2341 if (m_viewManager &&
Chris@164 2342 (m_viewManager->getToolMode() == ViewManager::MeasureMode)) {
Chris@164 2343 emit canDeleteSelection(haveCurrentLayer);
Chris@164 2344 m_deleteSelectedAction->setText(tr("&Delete Current Measurement"));
Chris@164 2345 m_deleteSelectedAction->setStatusTip(tr("Delete the measurement currently under the mouse pointer"));
Chris@164 2346 } else {
Chris@164 2347 emit canDeleteSelection(haveSelection && haveCurrentEditableLayer);
Chris@164 2348 m_deleteSelectedAction->setText(tr("&Delete Selected Items"));
Chris@164 2349 m_deleteSelectedAction->setStatusTip(tr("Delete items in current selection from the current layer"));
Chris@164 2350 }
Chris@164 2351
Chris@155 2352 if (m_ffwdAction && m_rwdAction) {
Chris@155 2353 if (haveCurrentTimeInstantsLayer) {
Chris@155 2354 m_ffwdAction->setText(tr("Fast Forward to Next Instant"));
Chris@155 2355 m_ffwdAction->setStatusTip(tr("Fast forward to the next time instant in the current layer"));
Chris@155 2356 m_rwdAction->setText(tr("Rewind to Previous Instant"));
Chris@155 2357 m_rwdAction->setStatusTip(tr("Rewind to the previous time instant in the current layer"));
Chris@155 2358 } else if (haveCurrentTimeValueLayer) {
Chris@155 2359 m_ffwdAction->setText(tr("Fast Forward to Next Point"));
Chris@155 2360 m_ffwdAction->setStatusTip(tr("Fast forward to the next point in the current layer"));
Chris@155 2361 m_rwdAction->setText(tr("Rewind to Previous Point"));
Chris@155 2362 m_rwdAction->setStatusTip(tr("Rewind to the previous point in the current layer"));
Chris@155 2363 } else {
Chris@155 2364 m_ffwdAction->setText(tr("Fast Forward"));
Chris@155 2365 m_ffwdAction->setStatusTip(tr("Fast forward"));
Chris@155 2366 m_rwdAction->setText(tr("Rewind"));
Chris@155 2367 m_rwdAction->setStatusTip(tr("Rewind"));
Chris@155 2368 }
Chris@155 2369 }
Chris@0 2370 }
Chris@0 2371
Chris@0 2372 void
Chris@0 2373 MainWindow::updateDescriptionLabel()
Chris@0 2374 {
Chris@0 2375 if (!getMainModel()) {
Chris@0 2376 m_descriptionLabel->setText(tr("No audio file loaded."));
Chris@0 2377 return;
Chris@0 2378 }
Chris@0 2379
Chris@0 2380 QString description;
Chris@0 2381
Chris@922 2382 sv_samplerate_t ssr = getMainModel()->getSampleRate();
Chris@922 2383 sv_samplerate_t tsr = ssr;
Chris@0 2384 if (m_playSource) tsr = m_playSource->getTargetSampleRate();
Chris@0 2385
Chris@0 2386 if (ssr != tsr) {
Chris@0 2387 description = tr("%1Hz (resampling to %2Hz)").arg(ssr).arg(tsr);
Chris@0 2388 } else {
Chris@0 2389 description = QString("%1Hz").arg(ssr);
Chris@0 2390 }
Chris@0 2391
Chris@0 2392 description = QString("%1 - %2")
Chris@0 2393 .arg(RealTime::frame2RealTime(getMainModel()->getEndFrame(), ssr)
Chris@0 2394 .toText(false).c_str())
Chris@0 2395 .arg(description);
Chris@0 2396
Chris@0 2397 m_descriptionLabel->setText(description);
Chris@0 2398 }
Chris@0 2399
Chris@0 2400 void
Chris@0 2401 MainWindow::documentModified()
Chris@0 2402 {
Chris@200 2403 //!!!
Chris@200 2404 MainWindowBase::documentModified();
Chris@0 2405 }
Chris@0 2406
Chris@0 2407 void
Chris@0 2408 MainWindow::documentRestored()
Chris@0 2409 {
Chris@200 2410 //!!!
Chris@200 2411 MainWindowBase::documentRestored();
Chris@0 2412 }
Chris@0 2413
Chris@0 2414 void
Chris@0 2415 MainWindow::toolNavigateSelected()
Chris@0 2416 {
Chris@0 2417 m_viewManager->setToolMode(ViewManager::NavigateMode);
Chris@0 2418 }
Chris@0 2419
Chris@0 2420 void
Chris@0 2421 MainWindow::toolSelectSelected()
Chris@0 2422 {
Chris@0 2423 m_viewManager->setToolMode(ViewManager::SelectMode);
Chris@0 2424 }
Chris@0 2425
Chris@0 2426 void
Chris@0 2427 MainWindow::toolEditSelected()
Chris@0 2428 {
Chris@0 2429 m_viewManager->setToolMode(ViewManager::EditMode);
Chris@0 2430 }
Chris@0 2431
Chris@0 2432 void
Chris@0 2433 MainWindow::toolDrawSelected()
Chris@0 2434 {
Chris@0 2435 m_viewManager->setToolMode(ViewManager::DrawMode);
Chris@0 2436 }
Chris@0 2437
Chris@151 2438 void
Chris@217 2439 MainWindow::toolEraseSelected()
Chris@217 2440 {
Chris@217 2441 m_viewManager->setToolMode(ViewManager::EraseMode);
Chris@217 2442 }
Chris@217 2443
Chris@217 2444 void
Chris@151 2445 MainWindow::toolMeasureSelected()
Chris@151 2446 {
Chris@151 2447 m_viewManager->setToolMode(ViewManager::MeasureMode);
Chris@151 2448 }
Chris@151 2449
Chris@0 2450 void
Chris@0 2451 MainWindow::importAudio()
Chris@0 2452 {
Chris@88 2453 QString path = getOpenFileName(FileFinder::AudioFile);
Chris@0 2454
Chris@0 2455 if (path != "") {
Chris@417 2456 if (openAudio(path, ReplaceSession) == FileOpenFailed) {
Chris@247 2457 emit hideSplash();
Chris@0 2458 QMessageBox::critical(this, tr("Failed to open file"),
Chris@193 2459 tr("<b>File open failed</b><p>Audio file \"%1\" could not be opened").arg(path));
Chris@0 2460 }
Chris@0 2461 }
Chris@0 2462 }
Chris@0 2463
Chris@0 2464 void
Chris@0 2465 MainWindow::importMoreAudio()
Chris@0 2466 {
Chris@88 2467 QString path = getOpenFileName(FileFinder::AudioFile);
Chris@0 2468
Chris@0 2469 if (path != "") {
Chris@197 2470 if (openAudio(path, CreateAdditionalModel) == FileOpenFailed) {
Chris@247 2471 emit hideSplash();
Chris@0 2472 QMessageBox::critical(this, tr("Failed to open file"),
Chris@193 2473 tr("<b>File open failed</b><p>Audio file \"%1\" could not be opened").arg(path));
Chris@0 2474 }
Chris@0 2475 }
Chris@0 2476 }
Chris@0 2477
Chris@0 2478 void
Chris@508 2479 MainWindow::replaceMainAudio()
Chris@508 2480 {
Chris@508 2481 QString path = getOpenFileName(FileFinder::AudioFile);
Chris@508 2482
Chris@508 2483 if (path != "") {
Chris@508 2484 if (openAudio(path, ReplaceMainModel) == FileOpenFailed) {
Chris@508 2485 emit hideSplash();
Chris@508 2486 QMessageBox::critical(this, tr("Failed to open file"),
Chris@508 2487 tr("<b>File open failed</b><p>Audio file \"%1\" could not be opened").arg(path));
Chris@508 2488 }
Chris@508 2489 }
Chris@508 2490 }
Chris@508 2491
Chris@508 2492 void
Chris@0 2493 MainWindow::exportAudio()
Chris@0 2494 {
Chris@631 2495 exportAudio(false);
Chris@631 2496 }
Chris@631 2497
Chris@631 2498 void
Chris@631 2499 MainWindow::exportAudioData()
Chris@631 2500 {
Chris@631 2501 exportAudio(true);
Chris@631 2502 }
Chris@631 2503
Chris@631 2504 void
Chris@631 2505 MainWindow::exportAudio(bool asData)
Chris@631 2506 {
Chris@0 2507 if (!getMainModel()) return;
Chris@0 2508
Chris@320 2509 RangeSummarisableTimeValueModel *model = getMainModel();
Chris@320 2510 std::set<RangeSummarisableTimeValueModel *> otherModels;
Chris@320 2511 RangeSummarisableTimeValueModel *current = model;
Chris@320 2512 if (m_paneStack) {
Chris@320 2513 for (int i = 0; i < m_paneStack->getPaneCount(); ++i) {
Chris@320 2514 Pane *pane = m_paneStack->getPane(i);
Chris@320 2515 if (!pane) continue;
Chris@320 2516 for (int j = 0; j < pane->getLayerCount(); ++j) {
Chris@320 2517 Layer *layer = pane->getLayer(j);
Chris@320 2518 if (!layer) continue;
Chris@432 2519 cerr << "layer = " << layer->objectName() << endl;
Chris@320 2520 Model *m = layer->getModel();
Chris@320 2521 RangeSummarisableTimeValueModel *wm =
Chris@320 2522 dynamic_cast<RangeSummarisableTimeValueModel *>(m);
Chris@320 2523 if (wm) {
Chris@432 2524 cerr << "found: " << wm->objectName() << endl;
Chris@320 2525 otherModels.insert(wm);
Chris@320 2526 if (pane == m_paneStack->getCurrentPane()) {
Chris@320 2527 current = wm;
Chris@320 2528 }
Chris@320 2529 }
Chris@320 2530 }
Chris@320 2531 }
Chris@320 2532 }
Chris@320 2533 if (!otherModels.empty()) {
Chris@320 2534 std::map<QString, RangeSummarisableTimeValueModel *> m;
Chris@320 2535 m[tr("1. %2").arg(model->objectName())] = model;
Chris@320 2536 int n = 2;
Chris@320 2537 int c = 0;
Chris@320 2538 for (std::set<RangeSummarisableTimeValueModel *>::const_iterator i
Chris@320 2539 = otherModels.begin();
Chris@320 2540 i != otherModels.end(); ++i) {
Chris@320 2541 if (*i == model) continue;
Chris@320 2542 m[tr("%1. %2").arg(n).arg((*i)->objectName())] = *i;
Chris@320 2543 ++n;
Chris@320 2544 if (*i == current) c = n-1;
Chris@320 2545 }
Chris@320 2546 QStringList items;
Chris@320 2547 for (std::map<QString, RangeSummarisableTimeValueModel *>
Chris@320 2548 ::const_iterator i = m.begin();
Chris@320 2549 i != m.end(); ++i) {
Chris@320 2550 items << i->first;
Chris@320 2551 }
Chris@325 2552 if (items.size() > 1) {
Chris@325 2553 bool ok = false;
Chris@325 2554 QString item = QInputDialog::getItem
Chris@325 2555 (this, tr("Select audio file to export"),
Chris@325 2556 tr("Which audio file do you want to export from?"),
Chris@325 2557 items, c, false, &ok);
Chris@325 2558 if (!ok || item.isEmpty()) return;
Chris@325 2559 if (m.find(item) == m.end()) {
Chris@665 2560 cerr << "WARNING: Model " << item
Chris@325 2561 << " not found in list!" << endl;
Chris@325 2562 } else {
Chris@325 2563 model = m[item];
Chris@325 2564 }
Chris@320 2565 }
Chris@320 2566 }
Chris@320 2567
Chris@631 2568 QString path;
Chris@631 2569 if (asData) {
Chris@631 2570 path = getSaveFileName(FileFinder::CSVFile);
Chris@631 2571 } else {
Chris@631 2572 path = getSaveFileName(FileFinder::AudioFile);
Chris@631 2573 }
Chris@0 2574 if (path == "") return;
Chris@0 2575
Chris@0 2576 bool ok = false;
Chris@0 2577 QString error;
Chris@0 2578
Chris@0 2579 MultiSelection ms = m_viewManager->getSelection();
Chris@0 2580 MultiSelection::SelectionList selections = m_viewManager->getSelections();
Chris@0 2581
Chris@0 2582 bool multiple = false;
Chris@0 2583
Chris@38 2584 MultiSelection *selectionToWrite = 0;
Chris@38 2585
Chris@38 2586 if (selections.size() == 1) {
Chris@0 2587
Chris@0 2588 QStringList items;
Chris@0 2589 items << tr("Export the selected region only")
Chris@0 2590 << tr("Export the whole audio file");
Chris@0 2591
Chris@0 2592 bool ok = false;
Chris@0 2593 QString item = ListInputDialog::getItem
Chris@0 2594 (this, tr("Select region to export"),
Chris@0 2595 tr("Which region from the original audio file do you want to export?"),
Chris@0 2596 items, 0, &ok);
Chris@0 2597
Chris@0 2598 if (!ok || item.isEmpty()) return;
Chris@0 2599
Chris@38 2600 if (item == items[0]) selectionToWrite = &ms;
Chris@38 2601
Chris@38 2602 } else if (selections.size() > 1) {
Chris@0 2603
Chris@631 2604 if (!asData) { // Multi-file export not supported for data
Chris@631 2605
Chris@631 2606 QStringList items;
Chris@631 2607 items << tr("Export the selected regions into a single file")
Chris@631 2608 << tr("Export the selected regions into separate files")
Chris@631 2609 << tr("Export the whole file");
Chris@631 2610
Chris@631 2611 QString item = ListInputDialog::getItem
Chris@631 2612 (this, tr("Select region to export"),
Chris@631 2613 tr("Multiple regions of the original audio file are selected.\nWhat do you want to export?"),
Chris@631 2614 items, 0, &ok);
Chris@0 2615
Chris@631 2616 if (!ok || item.isEmpty()) return;
Chris@631 2617
Chris@631 2618 if (item == items[0]) {
Chris@631 2619 selectionToWrite = &ms;
Chris@631 2620 } else if (item == items[1]) {
Chris@631 2621 multiple = true;
Chris@631 2622 }
Chris@631 2623
Chris@631 2624 } else { // asData
Chris@38 2625 selectionToWrite = &ms;
Chris@631 2626 }
Chris@631 2627
Chris@631 2628 if (multiple) { // Can only happen when asData false
Chris@0 2629
Chris@0 2630 int n = 1;
Chris@0 2631 QString base = path;
Chris@0 2632 base.replace(".wav", "");
Chris@0 2633
Chris@0 2634 for (MultiSelection::SelectionList::iterator i = selections.begin();
Chris@0 2635 i != selections.end(); ++i) {
Chris@0 2636
Chris@0 2637 MultiSelection subms;
Chris@0 2638 subms.setSelection(*i);
Chris@0 2639
Chris@0 2640 QString subpath = QString("%1.%2.wav").arg(base).arg(n);
Chris@0 2641 ++n;
Chris@0 2642
Chris@0 2643 if (QFileInfo(subpath).exists()) {
Chris@0 2644 error = tr("Fragment file %1 already exists, aborting").arg(subpath);
Chris@0 2645 break;
Chris@0 2646 }
Chris@0 2647
Chris@38 2648 WavFileWriter subwriter(subpath,
Chris@320 2649 model->getSampleRate(),
Chris@428 2650 model->getChannelCount(),
Chris@428 2651 WavFileWriter::WriteToTemporary);
Chris@320 2652 subwriter.writeModel(model, &subms);
Chris@0 2653 ok = subwriter.isOK();
Chris@0 2654
Chris@0 2655 if (!ok) {
Chris@0 2656 error = subwriter.getError();
Chris@0 2657 break;
Chris@0 2658 }
Chris@0 2659 }
Chris@0 2660 }
Chris@0 2661 }
Chris@0 2662
Chris@38 2663 if (!multiple) {
Chris@631 2664 if (asData) {
Chris@631 2665 CSVFileWriter writer(path, model,
Chris@631 2666 ((QFileInfo(path).suffix() == "csv") ?
Chris@631 2667 "," : "\t"));
Chris@631 2668 if (selectionToWrite) {
Chris@631 2669 writer.writeSelection(selectionToWrite);
Chris@631 2670 } else {
Chris@631 2671 writer.write();
Chris@631 2672 }
Chris@631 2673 ok = writer.isOK();
Chris@631 2674 error = writer.getError();
Chris@631 2675 } else {
Chris@631 2676 WavFileWriter writer(path,
Chris@631 2677 model->getSampleRate(),
Chris@631 2678 model->getChannelCount(),
Chris@631 2679 WavFileWriter::WriteToTemporary);
Chris@631 2680 writer.writeModel(model, selectionToWrite);
Chris@631 2681 ok = writer.isOK();
Chris@631 2682 error = writer.getError();
Chris@631 2683 }
Chris@0 2684 }
Chris@0 2685
Chris@0 2686 if (ok) {
Chris@310 2687 if (multiple) {
Chris@310 2688 emit activity(tr("Export multiple audio files"));
Chris@310 2689 } else {
Chris@310 2690 emit activity(tr("Export audio to \"%1\"").arg(path));
Chris@34 2691 m_recentFiles.addFile(path);
Chris@0 2692 }
Chris@0 2693 } else {
Chris@0 2694 QMessageBox::critical(this, tr("Failed to write file"), error);
Chris@0 2695 }
Chris@0 2696 }
Chris@0 2697
Chris@0 2698 void
Chris@0 2699 MainWindow::importLayer()
Chris@0 2700 {
Chris@0 2701 Pane *pane = m_paneStack->getCurrentPane();
Chris@0 2702
Chris@0 2703 if (!pane) {
Chris@0 2704 // shouldn't happen, as the menu action should have been disabled
Chris@665 2705 cerr << "WARNING: MainWindow::importLayer: no current pane" << endl;
Chris@0 2706 return;
Chris@0 2707 }
Chris@0 2708
Chris@0 2709 if (!getMainModel()) {
Chris@0 2710 // shouldn't happen, as the menu action should have been disabled
Chris@665 2711 cerr << "WARNING: MainWindow::importLayer: No main model -- hence no default sample rate available" << endl;
Chris@0 2712 return;
Chris@0 2713 }
Chris@0 2714
Chris@88 2715 QString path = getOpenFileName(FileFinder::LayerFile);
Chris@0 2716
Chris@0 2717 if (path != "") {
Chris@0 2718
Chris@197 2719 FileOpenStatus status = openLayer(path);
Chris@193 2720
Chris@193 2721 if (status == FileOpenFailed) {
Chris@247 2722 emit hideSplash();
Chris@0 2723 QMessageBox::critical(this, tr("Failed to open file"),
Chris@193 2724 tr("<b>File open failed</b><p>Layer file %1 could not be opened.").arg(path));
Chris@0 2725 return;
Chris@193 2726 } else if (status == FileOpenWrongMode) {
Chris@247 2727 emit hideSplash();
Chris@193 2728 QMessageBox::critical(this, tr("Failed to open file"),
Chris@294 2729 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 2730 }
Chris@0 2731 }
Chris@0 2732 }
Chris@0 2733
Chris@0 2734 void
Chris@0 2735 MainWindow::exportLayer()
Chris@0 2736 {
Chris@0 2737 Pane *pane = m_paneStack->getCurrentPane();
Chris@0 2738 if (!pane) return;
Chris@0 2739
Chris@0 2740 Layer *layer = pane->getSelectedLayer();
Chris@0 2741 if (!layer) return;
Chris@0 2742
Chris@0 2743 Model *model = layer->getModel();
Chris@0 2744 if (!model) return;
Chris@0 2745
Chris@185 2746 FileFinder::FileType type = FileFinder::LayerFileNoMidi;
Chris@185 2747
Chris@185 2748 if (dynamic_cast<NoteModel *>(model)) type = FileFinder::LayerFile;
Chris@185 2749
Chris@185 2750 QString path = getSaveFileName(type);
Chris@0 2751
Chris@0 2752 if (path == "") return;
Chris@0 2753
Chris@0 2754 if (QFileInfo(path).suffix() == "") path += ".svl";
Chris@0 2755
Chris@185 2756 QString suffix = QFileInfo(path).suffix().toLower();
Chris@185 2757
Chris@0 2758 QString error;
Chris@0 2759
Chris@185 2760 if (suffix == "xml" || suffix == "svl") {
Chris@0 2761
Chris@0 2762 QFile file(path);
Chris@0 2763 if (!file.open(QIODevice::WriteOnly | QIODevice::Text)) {
Chris@0 2764 error = tr("Failed to open file %1 for writing").arg(path);
Chris@0 2765 } else {
Chris@0 2766 QTextStream out(&file);
Chris@911 2767 out.setCodec(QTextCodec::codecForName("UTF-8"));
Chris@0 2768 out << "<?xml version=\"1.0\" encoding=\"UTF-8\"?>\n"
Chris@0 2769 << "<!DOCTYPE sonic-visualiser>\n"
Chris@0 2770 << "<sv>\n"
Chris@0 2771 << " <data>\n";
Chris@0 2772
Chris@0 2773 model->toXml(out, " ");
Chris@0 2774
Chris@0 2775 out << " </data>\n"
Chris@0 2776 << " <display>\n";
Chris@0 2777
Chris@0 2778 layer->toXml(out, " ");
Chris@0 2779
Chris@0 2780 out << " </display>\n"
Chris@0 2781 << "</sv>\n";
Chris@0 2782 }
Chris@0 2783
Chris@185 2784 } else if (suffix == "mid" || suffix == "midi") {
Chris@185 2785
Chris@185 2786 NoteModel *nm = dynamic_cast<NoteModel *>(model);
Chris@185 2787
Chris@185 2788 if (!nm) {
Chris@185 2789 error = tr("Can't export non-note layers to MIDI");
Chris@185 2790 } else {
Chris@694 2791 MIDIFileWriter writer(path, nm, nm->getSampleRate());
Chris@185 2792 writer.write();
Chris@185 2793 if (!writer.isOK()) {
Chris@185 2794 error = writer.getError();
Chris@185 2795 }
Chris@185 2796 }
Chris@185 2797
Chris@291 2798 } else if (suffix == "ttl" || suffix == "n3") {
Chris@291 2799
Chris@522 2800 if (!RDFExporter::canExportModel(model)) {
Chris@522 2801 error = tr("Sorry, cannot export this layer type to RDF (supported types are: region, note, text, time instants, time values)");
Chris@522 2802 } else {
Chris@522 2803 RDFExporter exporter(path, model);
Chris@522 2804 exporter.write();
Chris@522 2805 if (!exporter.isOK()) {
Chris@522 2806 error = exporter.getError();
Chris@522 2807 }
Chris@291 2808 }
Chris@291 2809
Chris@0 2810 } else {
Chris@0 2811
Chris@0 2812 CSVFileWriter writer(path, model,
Chris@185 2813 ((suffix == "csv") ? "," : "\t"));
Chris@0 2814 writer.write();
Chris@0 2815
Chris@0 2816 if (!writer.isOK()) {
Chris@0 2817 error = writer.getError();
Chris@0 2818 }
Chris@0 2819 }
Chris@0 2820
Chris@0 2821 if (error != "") {
Chris@0 2822 QMessageBox::critical(this, tr("Failed to write file"), error);
Chris@0 2823 } else {
Chris@34 2824 m_recentFiles.addFile(path);
Chris@310 2825 emit activity(tr("Export layer to \"%1\"").arg(path));
Chris@0 2826 }
Chris@0 2827 }
Chris@0 2828
Chris@121 2829 void
Chris@121 2830 MainWindow::exportImage()
Chris@121 2831 {
Chris@121 2832 Pane *pane = m_paneStack->getCurrentPane();
Chris@121 2833 if (!pane) return;
Chris@121 2834
Chris@121 2835 QString path = getSaveFileName(FileFinder::ImageFile);
Chris@121 2836
Chris@121 2837 if (path == "") return;
Chris@121 2838
Chris@121 2839 if (QFileInfo(path).suffix() == "") path += ".png";
Chris@121 2840
Chris@123 2841 bool haveSelection = m_viewManager && !m_viewManager->getSelections().empty();
Chris@123 2842
Chris@123 2843 QSize total, visible, selected;
Chris@123 2844 total = pane->getImageSize();
Chris@123 2845 visible = pane->getImageSize(pane->getFirstVisibleFrame(),
Chris@123 2846 pane->getLastVisibleFrame());
Chris@123 2847
Chris@922 2848 sv_frame_t sf0 = 0, sf1 = 0;
Chris@123 2849
Chris@123 2850 if (haveSelection) {
Chris@123 2851 MultiSelection::SelectionList selections = m_viewManager->getSelections();
Chris@123 2852 sf0 = selections.begin()->getStartFrame();
Chris@123 2853 MultiSelection::SelectionList::iterator e = selections.end();
Chris@123 2854 --e;
Chris@123 2855 sf1 = e->getEndFrame();
Chris@123 2856 selected = pane->getImageSize(sf0, sf1);
Chris@123 2857 }
Chris@123 2858
Chris@123 2859 QStringList items;
Chris@125 2860 items << tr("Export the whole pane (%1x%2 pixels)")
Chris@123 2861 .arg(total.width()).arg(total.height());
Chris@123 2862 items << tr("Export the visible area only (%1x%2 pixels)")
Chris@123 2863 .arg(visible.width()).arg(visible.height());
Chris@123 2864 if (haveSelection) {
Chris@123 2865 items << tr("Export the selection extent (%1x%2 pixels)")
Chris@123 2866 .arg(selected.width()).arg(selected.height());
Chris@124 2867 } else {
Chris@124 2868 items << tr("Export the selection extent");
Chris@123 2869 }
Chris@123 2870
Chris@123 2871 QSettings settings;
Chris@123 2872 settings.beginGroup("MainWindow");
Chris@123 2873 int deflt = settings.value("lastimageexportregion", 0).toInt();
Chris@123 2874 if (deflt == 2 && !haveSelection) deflt = 1;
Chris@124 2875 if (deflt == 0 && total.width() > 32767) deflt = 1;
Chris@124 2876
Chris@124 2877 ListInputDialog *lid = new ListInputDialog
Chris@123 2878 (this, tr("Select region to export"),
Chris@123 2879 tr("Which region of the current pane do you want to export as an image?"),
Chris@124 2880 items, deflt);
Chris@124 2881
Chris@124 2882 if (!haveSelection) {
Chris@124 2883 lid->setItemAvailability(2, false);
Chris@124 2884 }
Chris@124 2885 if (total.width() > 32767) { // appears to be the limit of a QImage
Chris@124 2886 lid->setItemAvailability(0, false);
Chris@124 2887 lid->setFootnote(tr("Note: the whole pane is too wide to be exported as a single image."));
Chris@124 2888 }
Chris@124 2889
Chris@124 2890 bool ok = lid->exec();
Chris@124 2891 QString item = lid->getCurrentString();
Chris@124 2892 delete lid;
Chris@123 2893
Chris@123 2894 if (!ok || item.isEmpty()) return;
Chris@123 2895
Chris@123 2896 settings.setValue("lastimageexportregion", deflt);
Chris@123 2897
Chris@123 2898 QImage *image = 0;
Chris@123 2899
Chris@123 2900 if (item == items[0]) {
Chris@123 2901 image = pane->toNewImage();
Chris@123 2902 } else if (item == items[1]) {
Chris@123 2903 image = pane->toNewImage(pane->getFirstVisibleFrame(),
Chris@123 2904 pane->getLastVisibleFrame());
Chris@123 2905 } else if (haveSelection) {
Chris@123 2906 image = pane->toNewImage(sf0, sf1);
Chris@123 2907 }
Chris@123 2908
Chris@121 2909 if (!image) return;
Chris@121 2910
Chris@121 2911 if (!image->save(path, "PNG")) {
Chris@121 2912 QMessageBox::critical(this, tr("Failed to save image file"),
Chris@121 2913 tr("Failed to save image file %1").arg(path));
Chris@121 2914 }
Chris@121 2915
Chris@121 2916 delete image;
Chris@121 2917 }
Chris@121 2918
Chris@0 2919 void
Chris@1056 2920 MainWindow::browseRecordedAudio()
Chris@1056 2921 {
Chris@1056 2922 if (!m_recordTarget) return;
Chris@1056 2923
Chris@1056 2924 QString path = m_recordTarget->getRecordFolder();
Chris@1056 2925 if (path == "") return;
Chris@1056 2926
Chris@1056 2927 openLocalFolder(path);
Chris@1056 2928 }
Chris@1056 2929
Chris@1056 2930 void
Chris@0 2931 MainWindow::newSession()
Chris@0 2932 {
Chris@0 2933 if (!checkSaveModified()) return;
Chris@0 2934
Chris@0 2935 closeSession();
Chris@0 2936 createDocument();
Chris@0 2937
Chris@0 2938 Pane *pane = m_paneStack->addPane();
Chris@0 2939
Chris@90 2940 connect(pane, SIGNAL(contextHelpChanged(const QString &)),
Chris@116 2941 this, SLOT(contextHelpChanged(const QString &)));
Chris@90 2942
Chris@0 2943 if (!m_timeRulerLayer) {
Chris@0 2944 m_timeRulerLayer = m_document->createMainModelLayer
Chris@0 2945 (LayerFactory::TimeRuler);
Chris@0 2946 }
Chris@0 2947
Chris@0 2948 m_document->addLayerToView(pane, m_timeRulerLayer);
Chris@0 2949
Chris@0 2950 Layer *waveform = m_document->createMainModelLayer(LayerFactory::Waveform);
Chris@0 2951 m_document->addLayerToView(pane, waveform);
Chris@0 2952
Chris@65 2953 m_overview->registerView(pane);
Chris@0 2954
Chris@0 2955 CommandHistory::getInstance()->clear();
Chris@0 2956 CommandHistory::getInstance()->documentSaved();
Chris@0 2957 documentRestored();
Chris@0 2958 updateMenuStates();
Chris@0 2959 }
Chris@0 2960
Chris@0 2961 void
Chris@303 2962 MainWindow::documentReplaced()
Chris@303 2963 {
Chris@303 2964 if (m_document) {
Chris@303 2965 connect(m_document, SIGNAL(activity(QString)),
Chris@303 2966 m_activityLog, SLOT(activityHappened(QString)));
Chris@303 2967 }
Chris@303 2968 }
Chris@303 2969
Chris@303 2970 void
Chris@0 2971 MainWindow::closeSession()
Chris@0 2972 {
Chris@0 2973 if (!checkSaveModified()) return;
Chris@0 2974
Chris@0 2975 while (m_paneStack->getPaneCount() > 0) {
Chris@0 2976
Chris@0 2977 Pane *pane = m_paneStack->getPane(m_paneStack->getPaneCount() - 1);
Chris@0 2978
Chris@0 2979 while (pane->getLayerCount() > 0) {
Chris@0 2980 m_document->removeLayerFromView
Chris@0 2981 (pane, pane->getLayer(pane->getLayerCount() - 1));
Chris@0 2982 }
Chris@0 2983
Chris@65 2984 m_overview->unregisterView(pane);
Chris@0 2985 m_paneStack->deletePane(pane);
Chris@0 2986 }
Chris@0 2987
Chris@0 2988 while (m_paneStack->getHiddenPaneCount() > 0) {
Chris@0 2989
Chris@0 2990 Pane *pane = m_paneStack->getHiddenPane
Chris@0 2991 (m_paneStack->getHiddenPaneCount() - 1);
Chris@0 2992
Chris@0 2993 while (pane->getLayerCount() > 0) {
Chris@0 2994 m_document->removeLayerFromView
Chris@0 2995 (pane, pane->getLayer(pane->getLayerCount() - 1));
Chris@0 2996 }
Chris@0 2997
Chris@65 2998 m_overview->unregisterView(pane);
Chris@0 2999 m_paneStack->deletePane(pane);
Chris@0 3000 }
Chris@0 3001
Chris@504 3002 delete m_layerTreeDialog.data();
Chris@504 3003 delete m_preferencesDialog.data();
Chris@504 3004
Chris@504 3005 m_activityLog->hide();
Chris@891 3006 m_unitConverter->hide();
Chris@504 3007 m_keyReference->hide();
Chris@504 3008
Chris@0 3009 delete m_document;
Chris@0 3010 m_document = 0;
Chris@0 3011 m_viewManager->clearSelections();
Chris@0 3012 m_timeRulerLayer = 0; // document owned this
Chris@0 3013
Chris@0 3014 m_sessionFile = "";
Chris@518 3015 setWindowTitle(QApplication::applicationName());
Chris@0 3016
Chris@0 3017 CommandHistory::getInstance()->clear();
Chris@0 3018 CommandHistory::getInstance()->documentSaved();
Chris@0 3019 documentRestored();
Chris@0 3020 }
Chris@0 3021
Chris@0 3022 void
Chris@0 3023 MainWindow::openSomething()
Chris@0 3024 {
Chris@0 3025 QString orig = m_audioFile;
Chris@0 3026 if (orig == "") orig = ".";
Chris@0 3027 else orig = QFileInfo(orig).absoluteDir().canonicalPath();
Chris@0 3028
Chris@88 3029 QString path = getOpenFileName(FileFinder::AnyFile);
Chris@0 3030
Chris@0 3031 if (path.isEmpty()) return;
Chris@0 3032
Chris@844 3033 FileOpenStatus status = openPath(path, ReplaceSession);
Chris@193 3034
Chris@193 3035 if (status == FileOpenFailed) {
Chris@247 3036 emit hideSplash();
Chris@193 3037 QMessageBox::critical(this, tr("Failed to open file"),
Chris@193 3038 tr("<b>File open failed</b><p>File \"%1\" could not be opened").arg(path));
Chris@193 3039 } else if (status == FileOpenWrongMode) {
Chris@247 3040 emit hideSplash();
Chris@193 3041 QMessageBox::critical(this, tr("Failed to open file"),
Chris@294 3042 tr("<b>Audio required</b><p>Unable to load layer data from \"%1\" without an audio file.<br>Please load at least one audio file before importing annotations.").arg(path));
Chris@0 3043 }
Chris@0 3044 }
Chris@0 3045
Chris@0 3046 void
Chris@86 3047 MainWindow::openLocation()
Chris@86 3048 {
Chris@103 3049 QSettings settings;
Chris@103 3050 settings.beginGroup("MainWindow");
Chris@103 3051 QString lastLocation = settings.value("lastremote", "").toString();
Chris@103 3052
Chris@86 3053 bool ok = false;
Chris@86 3054 QString text = QInputDialog::getText
Chris@86 3055 (this, tr("Open Location"),
Chris@86 3056 tr("Please enter the URL of the location to open:"),
Chris@103 3057 QLineEdit::Normal, lastLocation, &ok);
Chris@103 3058
Chris@103 3059 if (!ok) return;
Chris@103 3060
Chris@103 3061 settings.setValue("lastremote", text);
Chris@103 3062
Chris@103 3063 if (text.isEmpty()) return;
Chris@86 3064
Chris@844 3065 FileOpenStatus status = openPath(text, AskUser);
Chris@193 3066
Chris@193 3067 if (status == FileOpenFailed) {
Chris@247 3068 emit hideSplash();
Chris@86 3069 QMessageBox::critical(this, tr("Failed to open location"),
Chris@193 3070 tr("<b>Open failed</b><p>URL \"%1\" could not be opened").arg(text));
Chris@193 3071 } else if (status == FileOpenWrongMode) {
Chris@247 3072 emit hideSplash();
Chris@193 3073 QMessageBox::critical(this, tr("Failed to open location"),
Chris@294 3074 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 3075 }
Chris@86 3076 }
Chris@86 3077
Chris@86 3078 void
Chris@1253 3079 MainWindow::openRecentFile(const QString& path)
Chris@0 3080 {
Chris@1253 3081 /* F. Nicol patch 13 Aug. 2016 */
Chris@1253 3082 #if 0
Chris@0 3083 QObject *obj = sender();
Chris@0 3084 QAction *action = dynamic_cast<QAction *>(obj);
Chris@0 3085
Chris@0 3086 if (!action) {
Chris@665 3087 cerr << "WARNING: MainWindow::openRecentFile: sender is not an action"
Chris@665 3088 << endl;
Chris@0 3089 return;
Chris@0 3090 }
Chris@0 3091
Chris@0 3092 QString path = action->text();
Chris@1253 3093 #endif
Chris@1253 3094 /* End of F. Nicol patch 13 Aug. 2016 */
Chris@1253 3095
Chris@0 3096 if (path == "") return;
Chris@0 3097
Chris@844 3098 FileOpenStatus status = openPath(path, ReplaceSession);
Chris@193 3099
Chris@193 3100 if (status == FileOpenFailed) {
Chris@247 3101 emit hideSplash();
Chris@193 3102 QMessageBox::critical(this, tr("Failed to open location"),
Chris@193 3103 tr("<b>Open failed</b><p>File or URL \"%1\" could not be opened").arg(path));
Chris@193 3104 } else if (status == FileOpenWrongMode) {
Chris@247 3105 emit hideSplash();
Chris@193 3106 QMessageBox::critical(this, tr("Failed to open location"),
Chris@294 3107 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 3108 }
Chris@0 3109 }
Chris@0 3110
Chris@0 3111 void
Chris@435 3112 MainWindow::applyTemplate()
Chris@435 3113 {
Chris@435 3114 QObject *s = sender();
Chris@435 3115 QAction *action = qobject_cast<QAction *>(s);
Chris@435 3116
Chris@435 3117 if (!action) {
Chris@665 3118 cerr << "WARNING: MainWindow::applyTemplate: sender is not an action"
Chris@665 3119 << endl;
Chris@435 3120 return;
Chris@435 3121 }
Chris@435 3122
Chris@435 3123 QString n = action->objectName();
Chris@435 3124 if (n == "") n = action->text();
Chris@435 3125
Chris@435 3126 if (n == "") {
Chris@665 3127 cerr << "WARNING: MainWindow::applyTemplate: sender has no name"
Chris@665 3128 << endl;
Chris@435 3129 return;
Chris@435 3130 }
Chris@435 3131
Chris@435 3132 QString mainModelLocation;
Chris@435 3133 WaveFileModel *mm = getMainModel();
Chris@435 3134 if (mm) mainModelLocation = mm->getLocation();
Chris@435 3135 if (mainModelLocation != "") {
Chris@435 3136 openAudio(mainModelLocation, ReplaceSession, n);
Chris@435 3137 } else {
Chris@435 3138 openSessionTemplate(n);
Chris@435 3139 }
Chris@435 3140 }
Chris@435 3141
Chris@435 3142 void
Chris@425 3143 MainWindow::saveSessionAsTemplate()
Chris@425 3144 {
Chris@762 3145 QDialog *d = new QDialog(this);
Chris@483 3146 d->setWindowTitle(tr("Enter template name"));
Chris@483 3147
Chris@483 3148 QGridLayout *layout = new QGridLayout;
Chris@483 3149 d->setLayout(layout);
Chris@483 3150
Chris@483 3151 layout->addWidget(new QLabel(tr("Please enter a name for the saved template:")),
Chris@483 3152 0, 0);
Chris@483 3153 QLineEdit *lineEdit = new QLineEdit;
Chris@483 3154 layout->addWidget(lineEdit, 1, 0);
Chris@483 3155 QCheckBox *makeDefault = new QCheckBox(tr("Set as default template for future audio files"));
Chris@483 3156 layout->addWidget(makeDefault, 2, 0);
Chris@425 3157
Chris@483 3158 QDialogButtonBox *bb = new QDialogButtonBox(QDialogButtonBox::Ok |
Chris@483 3159 QDialogButtonBox::Cancel);
Chris@483 3160 layout->addWidget(bb, 3, 0);
Chris@483 3161 connect(bb, SIGNAL(accepted()), d, SLOT(accept()));
Chris@483 3162 connect(bb, SIGNAL(accepted()), d, SLOT(accept()));
Chris@483 3163 connect(bb, SIGNAL(rejected()), d, SLOT(reject()));
Chris@483 3164
Chris@483 3165 if (d->exec() == QDialog::Accepted) {
Chris@483 3166
Chris@483 3167 QString name = lineEdit->text();
Chris@483 3168 name.replace(QRegExp("[^\\w\\s\\.\"'-]"), "_");
Chris@483 3169
Chris@483 3170 ResourceFinder rf;
Chris@483 3171 QString dir = rf.getResourceSaveDir("templates");
Chris@483 3172 QString filename = QString("%1/%2.svt").arg(dir).arg(name);
Chris@483 3173 if (QFile(filename).exists()) {
Chris@483 3174 if (QMessageBox::warning(this,
Chris@483 3175 tr("Template file exists"),
Chris@483 3176 tr("<b>Template file exists</b><p>The template \"%1\" already exists.<br>Overwrite it?").arg(name),
Chris@483 3177 QMessageBox::Ok | QMessageBox::Cancel,
Chris@483 3178 QMessageBox::Cancel) != QMessageBox::Ok) {
Chris@483 3179 return;
Chris@483 3180 }
Chris@483 3181 }
Chris@483 3182
Chris@483 3183 if (saveSessionTemplate(filename)) {
Chris@483 3184 if (makeDefault->isChecked()) {
Chris@483 3185 setDefaultSessionTemplate(name);
Chris@483 3186 }
Chris@431 3187 }
Chris@431 3188 }
Chris@425 3189 }
Chris@425 3190
Chris@425 3191 void
Chris@425 3192 MainWindow::manageSavedTemplates()
Chris@425 3193 {
Chris@425 3194 ResourceFinder rf;
Chris@425 3195 QDesktopServices::openUrl("file:" + rf.getResourceSaveDir("templates"));
Chris@423 3196 }
Chris@423 3197
Chris@423 3198 void
Chris@200 3199 MainWindow::paneAdded(Pane *pane)
Chris@200 3200 {
Chris@200 3201 if (m_overview) m_overview->registerView(pane);
Chris@200 3202 }
Chris@200 3203
Chris@200 3204 void
Chris@200 3205 MainWindow::paneHidden(Pane *pane)
Chris@200 3206 {
Chris@200 3207 if (m_overview) m_overview->unregisterView(pane);
Chris@200 3208 }
Chris@200 3209
Chris@200 3210 void
Chris@200 3211 MainWindow::paneAboutToBeDeleted(Pane *pane)
Chris@200 3212 {
Chris@200 3213 if (m_overview) m_overview->unregisterView(pane);
Chris@200 3214 }
Chris@200 3215
Chris@200 3216 void
Chris@193 3217 MainWindow::paneDropAccepted(Pane *pane, QStringList uriList)
Chris@193 3218 {
Chris@193 3219 if (pane) m_paneStack->setCurrentPane(pane);
Chris@193 3220
Chris@193 3221 for (QStringList::iterator i = uriList.begin(); i != uriList.end(); ++i) {
Chris@193 3222
Chris@295 3223 FileOpenStatus status;
Chris@295 3224
Chris@295 3225 if (i == uriList.begin()) {
Chris@844 3226 status = openPath(*i, ReplaceCurrentPane);
Chris@295 3227 } else {
Chris@844 3228 status = openPath(*i, CreateAdditionalModel);
Chris@295 3229 }
Chris@193 3230
Chris@193 3231 if (status == FileOpenFailed) {
Chris@247 3232 emit hideSplash();
Chris@193 3233 QMessageBox::critical(this, tr("Failed to open dropped URL"),
Chris@193 3234 tr("<b>Open failed</b><p>Dropped URL \"%1\" could not be opened").arg(*i));
Chris@295 3235 break;
Chris@193 3236 } else if (status == FileOpenWrongMode) {
Chris@247 3237 emit hideSplash();
Chris@193 3238 QMessageBox::critical(this, tr("Failed to open dropped URL"),
Chris@294 3239 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 3240 break;
Chris@295 3241 } else if (status == FileOpenCancelled) {
Chris@295 3242 break;
Chris@193 3243 }
Chris@193 3244 }
Chris@193 3245 }
Chris@193 3246
Chris@193 3247 void
Chris@193 3248 MainWindow::paneDropAccepted(Pane *pane, QString text)
Chris@193 3249 {
Chris@193 3250 if (pane) m_paneStack->setCurrentPane(pane);
Chris@193 3251
Chris@193 3252 QUrl testUrl(text);
Chris@193 3253 if (testUrl.scheme() == "file" ||
Chris@193 3254 testUrl.scheme() == "http" ||
Chris@193 3255 testUrl.scheme() == "ftp") {
Chris@193 3256 QStringList list;
Chris@193 3257 list.push_back(text);
Chris@193 3258 paneDropAccepted(pane, list);
Chris@193 3259 return;
Chris@193 3260 }
Chris@193 3261
Chris@193 3262 //!!! open as text -- but by importing as if a CSV, or just adding
Chris@193 3263 //to a text layer?
Chris@193 3264 }
Chris@193 3265
Chris@193 3266 void
Chris@0 3267 MainWindow::closeEvent(QCloseEvent *e)
Chris@0 3268 {
Chris@665 3269 // cerr << "MainWindow::closeEvent" << endl;
Chris@118 3270
Chris@136 3271 if (m_openingAudioFile) {
Chris@665 3272 // cerr << "Busy - ignoring close event" << endl;
Chris@136 3273 e->ignore();
Chris@136 3274 return;
Chris@136 3275 }
Chris@136 3276
Chris@70 3277 if (!m_abandoning && !checkSaveModified()) {
Chris@665 3278 // cerr << "Close refused by user - ignoring close event" << endl;
Chris@0 3279 e->ignore();
Chris@0 3280 return;
Chris@0 3281 }
Chris@0 3282
Chris@5 3283 QSettings settings;
Chris@5 3284 settings.beginGroup("MainWindow");
Chris@624 3285 settings.setValue("maximised", isMaximized());
Chris@624 3286 if (!isMaximized()) {
Chris@624 3287 settings.setValue("size", size());
Chris@624 3288 settings.setValue("position", pos());
Chris@624 3289 }
Chris@5 3290 settings.endGroup();
Chris@5 3291
Chris@163 3292 if (m_preferencesDialog &&
Chris@163 3293 m_preferencesDialog->isVisible()) {
Chris@164 3294 closeSession(); // otherwise we'll have to wait for prefs changes
Chris@163 3295 m_preferencesDialog->applicationClosing(false);
Chris@163 3296 }
Chris@163 3297
Chris@200 3298 closeSession();
Chris@200 3299
Chris@0 3300 e->accept();
Chris@489 3301
Chris@0 3302 return;
Chris@0 3303 }
Chris@0 3304
Chris@0 3305 bool
Chris@11 3306 MainWindow::commitData(bool mayAskUser)
Chris@11 3307 {
Chris@11 3308 if (mayAskUser) {
Chris@163 3309 bool rv = checkSaveModified();
Chris@163 3310 if (rv) {
Chris@163 3311 if (m_preferencesDialog &&
Chris@163 3312 m_preferencesDialog->isVisible()) {
Chris@163 3313 m_preferencesDialog->applicationClosing(false);
Chris@163 3314 }
Chris@163 3315 }
Chris@163 3316 return rv;
Chris@11 3317 } else {
Chris@163 3318 if (m_preferencesDialog &&
Chris@163 3319 m_preferencesDialog->isVisible()) {
Chris@163 3320 m_preferencesDialog->applicationClosing(true);
Chris@163 3321 }
Chris@11 3322 if (!m_documentModified) return true;
Chris@11 3323
Chris@11 3324 // If we can't check with the user first, then we can't save
Chris@11 3325 // to the original session file (even if we have it) -- have
Chris@11 3326 // to use a temporary file
Chris@11 3327
Chris@11 3328 QString svDirBase = ".sv1";
Chris@11 3329 QString svDir = QDir::home().filePath(svDirBase);
Chris@11 3330
Chris@11 3331 if (!QFileInfo(svDir).exists()) {
Chris@11 3332 if (!QDir::home().mkdir(svDirBase)) return false;
Chris@11 3333 } else {
Chris@11 3334 if (!QFileInfo(svDir).isDir()) return false;
Chris@11 3335 }
Chris@11 3336
Chris@11 3337 // This name doesn't have to be unguessable
Chris@93 3338 #ifndef _WIN32
Chris@11 3339 QString fname = QString("tmp-%1-%2.sv")
Chris@11 3340 .arg(QDateTime::currentDateTime().toString("yyyyMMddhhmmsszzz"))
Chris@11 3341 .arg(QProcess().pid());
Chris@93 3342 #else
Chris@93 3343 QString fname = QString("tmp-%1.sv")
Chris@93 3344 .arg(QDateTime::currentDateTime().toString("yyyyMMddhhmmsszzz"));
Chris@93 3345 #endif
Chris@11 3346 QString fpath = QDir(svDir).filePath(fname);
Chris@11 3347 if (saveSessionFile(fpath)) {
Chris@34 3348 m_recentFiles.addFile(fpath);
Chris@310 3349 emit activity(tr("Export image to \"%1\"").arg(fpath));
Chris@11 3350 return true;
Chris@11 3351 } else {
Chris@11 3352 return false;
Chris@11 3353 }
Chris@11 3354 }
Chris@11 3355 }
Chris@11 3356
Chris@11 3357 bool
Chris@0 3358 MainWindow::checkSaveModified()
Chris@0 3359 {
Chris@0 3360 // Called before some destructive operation (e.g. new session,
Chris@0 3361 // exit program). Return true if we can safely proceed, false to
Chris@0 3362 // cancel.
Chris@0 3363
Chris@0 3364 if (!m_documentModified) return true;
Chris@0 3365
Chris@247 3366 emit hideSplash();
Chris@247 3367
Chris@0 3368 int button =
Chris@0 3369 QMessageBox::warning(this,
Chris@0 3370 tr("Session modified"),
Chris@207 3371 tr("<b>Session modified</b><p>The current session has been modified.<br>Do you want to save it?"),
Chris@165 3372 QMessageBox::Yes | QMessageBox::No | QMessageBox::Cancel,
Chris@165 3373 QMessageBox::Yes);
Chris@0 3374
Chris@0 3375 if (button == QMessageBox::Yes) {
Chris@0 3376 saveSession();
Chris@0 3377 if (m_documentModified) { // save failed -- don't proceed!
Chris@0 3378 return false;
Chris@0 3379 } else {
Chris@0 3380 return true; // saved, so it's safe to continue now
Chris@0 3381 }
Chris@0 3382 } else if (button == QMessageBox::No) {
Chris@0 3383 m_documentModified = false; // so we know to abandon it
Chris@0 3384 return true;
Chris@0 3385 }
Chris@0 3386
Chris@0 3387 // else cancel
Chris@0 3388 return false;
Chris@0 3389 }
Chris@0 3390
Chris@290 3391 bool
Chris@294 3392 MainWindow::shouldCreateNewSessionForRDFAudio(bool *cancel)
Chris@290 3393 {
Chris@294 3394 //!!! This is very similar to part of MainWindowBase::openAudio --
Chris@294 3395 //!!! make them a bit more uniform
Chris@294 3396
Chris@294 3397 QSettings settings;
Chris@294 3398 settings.beginGroup("MainWindow");
Chris@294 3399 bool prevNewSession = settings.value("newsessionforrdfaudio", true).toBool();
Chris@294 3400 settings.endGroup();
Chris@294 3401 bool newSession = true;
Chris@294 3402
Chris@294 3403 QStringList items;
Chris@294 3404 items << tr("Close the current session and create a new one")
Chris@294 3405 << tr("Add this data to the current session");
Chris@294 3406
Chris@294 3407 bool ok = false;
Chris@294 3408 QString item = ListInputDialog::getItem
Chris@294 3409 (this, tr("Select target for import"),
Chris@294 3410 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 3411 items, prevNewSession ? 0 : 1, &ok);
Chris@294 3412
Chris@294 3413 if (!ok || item.isEmpty()) {
Chris@294 3414 *cancel = true;
Chris@290 3415 return false;
Chris@290 3416 }
Chris@294 3417
Chris@294 3418 newSession = (item == items[0]);
Chris@294 3419 settings.beginGroup("MainWindow");
Chris@294 3420 settings.setValue("newsessionforrdfaudio", newSession);
Chris@294 3421 settings.endGroup();
Chris@294 3422
Chris@294 3423 if (newSession) return true;
Chris@294 3424 else return false;
Chris@290 3425 }
Chris@290 3426
Chris@0 3427 void
Chris@0 3428 MainWindow::saveSession()
Chris@0 3429 {
Chris@0 3430 if (m_sessionFile != "") {
Chris@0 3431 if (!saveSessionFile(m_sessionFile)) {
Chris@0 3432 QMessageBox::critical(this, tr("Failed to save file"),
Chris@193 3433 tr("<b>Save failed</b><p>Session file \"%1\" could not be saved.").arg(m_sessionFile));
Chris@0 3434 } else {
Chris@0 3435 CommandHistory::getInstance()->documentSaved();
Chris@0 3436 documentRestored();
Chris@0 3437 }
Chris@0 3438 } else {
Chris@0 3439 saveSessionAs();
Chris@0 3440 }
Chris@0 3441 }
Chris@0 3442
Chris@0 3443 void
Chris@0 3444 MainWindow::saveSessionAs()
Chris@0 3445 {
Chris@0 3446 QString orig = m_audioFile;
Chris@0 3447 if (orig == "") orig = ".";
Chris@0 3448 else orig = QFileInfo(orig).absoluteDir().canonicalPath();
Chris@0 3449
Chris@88 3450 QString path = getSaveFileName(FileFinder::SessionFile);
Chris@81 3451
Chris@81 3452 if (path == "") return;
Chris@0 3453
Chris@0 3454 if (!saveSessionFile(path)) {
Chris@0 3455 QMessageBox::critical(this, tr("Failed to save file"),
Chris@193 3456 tr("<b>Save failed</b><p>Session file \"%1\" could not be saved.").arg(path));
Chris@0 3457 } else {
Chris@902 3458 setWindowTitle(tr("%1: %2")
Chris@518 3459 .arg(QApplication::applicationName())
Chris@0 3460 .arg(QFileInfo(path).fileName()));
Chris@0 3461 m_sessionFile = path;
Chris@0 3462 CommandHistory::getInstance()->documentSaved();
Chris@0 3463 documentRestored();
Chris@34 3464 m_recentFiles.addFile(path);
Chris@310 3465 emit activity(tr("Save session as \"%1\"").arg(path));
Chris@0 3466 }
Chris@0 3467 }
Chris@0 3468
Chris@90 3469 void
Chris@72 3470 MainWindow::preferenceChanged(PropertyContainer::PropertyName name)
Chris@72 3471 {
Chris@200 3472 MainWindowBase::preferenceChanged(name);
Chris@200 3473
Chris@200 3474 if (name == "Background Mode" && m_viewManager) {
Chris@180 3475 if (m_viewManager->getGlobalDarkBackground()) {
Chris@180 3476 m_panLayer->setBaseColour
Chris@180 3477 (ColourDatabase::getInstance()->getColourIndex(tr("Bright Green")));
Chris@180 3478 } else {
Chris@180 3479 m_panLayer->setBaseColour
Chris@180 3480 (ColourDatabase::getInstance()->getColourIndex(tr("Green")));
Chris@180 3481 }
Chris@200 3482 }
Chris@0 3483 }
Chris@0 3484
Chris@0 3485 void
Chris@239 3486 MainWindow::propertyStacksResized(int width)
Chris@239 3487 {
Chris@438 3488 // SVDEBUG << "MainWindow::propertyStacksResized(" << width << ")" << endl;
Chris@239 3489
Chris@239 3490 if (!m_playControlsSpacer) return;
Chris@239 3491
Chris@239 3492 int spacerWidth = width - m_playControlsWidth - 4;
Chris@239 3493
Chris@438 3494 // SVDEBUG << "resizing spacer from " << m_playControlsSpacer->width() << " to " << spacerWidth << endl;
Chris@239 3495
Chris@239 3496 m_playControlsSpacer->setFixedSize(QSize(spacerWidth, 2));
Chris@239 3497 }
Chris@239 3498
Chris@239 3499 void
Chris@0 3500 MainWindow::addPane()
Chris@0 3501 {
Chris@0 3502 QObject *s = sender();
Chris@0 3503 QAction *action = dynamic_cast<QAction *>(s);
Chris@753 3504
Chris@753 3505 cerr << "addPane: sender is " << s << ", action is " << action << ", name " << action->text() << endl;
Chris@0 3506
Chris@0 3507 if (!action) {
Chris@665 3508 cerr << "WARNING: MainWindow::addPane: sender is not an action"
Chris@665 3509 << endl;
Chris@0 3510 return;
Chris@0 3511 }
Chris@0 3512
Chris@0 3513 PaneActionMap::iterator i = m_paneActions.find(action);
Chris@0 3514
Chris@0 3515 if (i == m_paneActions.end()) {
Chris@665 3516 cerr << "WARNING: MainWindow::addPane: unknown action "
Chris@665 3517 << action->objectName() << endl;
Chris@753 3518 cerr << "known actions are:" << endl;
Chris@753 3519 for (PaneActionMap::const_iterator i = m_paneActions.begin();
Chris@753 3520 i != m_paneActions.end(); ++i) {
Chris@753 3521 cerr << i->first << ", name " << i->first->text() << endl;
Chris@753 3522 }
Chris@0 3523 return;
Chris@0 3524 }
Chris@0 3525
Chris@69 3526 addPane(i->second, action->text());
Chris@69 3527 }
Chris@69 3528
Chris@69 3529 void
Chris@232 3530 MainWindow::addPane(const LayerConfiguration &configuration, QString text)
Chris@69 3531 {
Chris@69 3532 CommandHistory::getInstance()->startCompoundOperation(text, true);
Chris@0 3533
Chris@0 3534 AddPaneCommand *command = new AddPaneCommand(this);
Chris@0 3535 CommandHistory::getInstance()->addCommand(command);
Chris@0 3536
Chris@0 3537 Pane *pane = command->getPane();
Chris@0 3538
Chris@69 3539 if (configuration.layer == LayerFactory::Spectrum) {
Chris@109 3540 pane->setPlaybackFollow(PlaybackScrollContinuous);
Chris@110 3541 pane->setFollowGlobalZoom(false);
Chris@112 3542 pane->setZoomLevel(512);
Chris@7 3543 }
Chris@7 3544
Chris@69 3545 if (configuration.layer != LayerFactory::TimeRuler &&
Chris@69 3546 configuration.layer != LayerFactory::Spectrum) {
Chris@8 3547
Chris@0 3548 if (!m_timeRulerLayer) {
Chris@665 3549 // cerr << "no time ruler layer, creating one" << endl;
Chris@0 3550 m_timeRulerLayer = m_document->createMainModelLayer
Chris@0 3551 (LayerFactory::TimeRuler);
Chris@0 3552 }
Chris@0 3553
Chris@438 3554 // SVDEBUG << "adding time ruler layer " << m_timeRulerLayer << endl;
Chris@0 3555
Chris@0 3556 m_document->addLayerToView(pane, m_timeRulerLayer);
Chris@0 3557 }
Chris@0 3558
Chris@69 3559 Layer *newLayer = m_document->createLayer(configuration.layer);
Chris@69 3560
Chris@69 3561 Model *suggestedModel = configuration.sourceModel;
Chris@66 3562 Model *model = 0;
Chris@66 3563
Chris@66 3564 if (suggestedModel) {
Chris@66 3565
Chris@66 3566 // check its validity
Chris@224 3567 std::vector<Model *> inputModels = m_document->getTransformInputModels();
Chris@66 3568 for (size_t j = 0; j < inputModels.size(); ++j) {
Chris@66 3569 if (inputModels[j] == suggestedModel) {
Chris@66 3570 model = suggestedModel;
Chris@66 3571 break;
Chris@66 3572 }
Chris@66 3573 }
Chris@66 3574
Chris@66 3575 if (!model) {
Chris@665 3576 cerr << "WARNING: Model " << (void *)suggestedModel
Chris@66 3577 << " appears in pane action map, but is not reported "
Chris@665 3578 << "by document as a valid transform source" << endl;
Chris@66 3579 }
Chris@66 3580 }
Chris@66 3581
Chris@346 3582 if (!model) {
Chris@346 3583 model = m_document->getMainModel();
Chris@346 3584 }
Chris@66 3585
Chris@66 3586 m_document->setModel(newLayer, model);
Chris@66 3587
Chris@69 3588 m_document->setChannel(newLayer, configuration.channel);
Chris@0 3589 m_document->addLayerToView(pane, newLayer);
Chris@0 3590
Chris@0 3591 m_paneStack->setCurrentPane(pane);
Chris@70 3592 m_paneStack->setCurrentLayer(pane, newLayer);
Chris@0 3593
Chris@438 3594 // SVDEBUG << "MainWindow::addPane: global centre frame is "
Chris@433 3595 // << m_viewManager->getGlobalCentreFrame() << endl;
Chris@130 3596 // pane->setCentreFrame(m_viewManager->getGlobalCentreFrame());
Chris@73 3597
Chris@0 3598 CommandHistory::getInstance()->endCompoundOperation();
Chris@0 3599
Chris@0 3600 updateMenuStates();
Chris@0 3601 }
Chris@0 3602
Chris@0 3603 void
Chris@0 3604 MainWindow::addLayer()
Chris@0 3605 {
Chris@0 3606 QObject *s = sender();
Chris@0 3607 QAction *action = dynamic_cast<QAction *>(s);
Chris@0 3608
Chris@0 3609 if (!action) {
Chris@665 3610 cerr << "WARNING: MainWindow::addLayer: sender is not an action"
Chris@665 3611 << endl;
Chris@0 3612 return;
Chris@0 3613 }
Chris@0 3614
Chris@0 3615 Pane *pane = m_paneStack->getCurrentPane();
Chris@0 3616
Chris@0 3617 if (!pane) {
Chris@665 3618 cerr << "WARNING: MainWindow::addLayer: no current pane" << endl;
Chris@0 3619 return;
Chris@0 3620 }
Chris@0 3621
Chris@0 3622 ExistingLayerActionMap::iterator ei = m_existingLayerActions.find(action);
Chris@0 3623
Chris@0 3624 if (ei != m_existingLayerActions.end()) {
Chris@0 3625 Layer *newLayer = ei->second;
Chris@0 3626 m_document->addLayerToView(pane, newLayer);
Chris@0 3627 m_paneStack->setCurrentLayer(pane, newLayer);
Chris@0 3628 return;
Chris@0 3629 }
Chris@0 3630
Chris@95 3631 ei = m_sliceActions.find(action);
Chris@95 3632
Chris@95 3633 if (ei != m_sliceActions.end()) {
Chris@95 3634 Layer *newLayer = m_document->createLayer(LayerFactory::Slice);
Chris@95 3635 // document->setModel(newLayer, ei->second->getModel());
Chris@95 3636 SliceableLayer *source = dynamic_cast<SliceableLayer *>(ei->second);
Chris@95 3637 SliceLayer *dest = dynamic_cast<SliceLayer *>(newLayer);
Chris@95 3638 if (source && dest) {
Chris@205 3639 //!!!???
Chris@95 3640 dest->setSliceableModel(source->getSliceableModel());
Chris@95 3641 connect(source, SIGNAL(sliceableModelReplaced(const Model *, const Model *)),
Chris@95 3642 dest, SLOT(sliceableModelReplaced(const Model *, const Model *)));
Chris@95 3643 connect(m_document, SIGNAL(modelAboutToBeDeleted(Model *)),
Chris@95 3644 dest, SLOT(modelAboutToBeDeleted(Model *)));
Chris@95 3645 }
Chris@95 3646 m_document->addLayerToView(pane, newLayer);
Chris@95 3647 m_paneStack->setCurrentLayer(pane, newLayer);
Chris@95 3648 return;
Chris@95 3649 }
Chris@95 3650
Chris@211 3651 TransformActionMap::iterator i = m_transformActions.find(action);
Chris@34 3652
Chris@34 3653 if (i == m_transformActions.end()) {
Chris@0 3654
Chris@0 3655 LayerActionMap::iterator i = m_layerActions.find(action);
Chris@0 3656
Chris@0 3657 if (i == m_layerActions.end()) {
Chris@665 3658 cerr << "WARNING: MainWindow::addLayer: unknown action "
Chris@665 3659 << action->objectName() << endl;
Chris@0 3660 return;
Chris@0 3661 }
Chris@0 3662
Chris@232 3663 LayerFactory::LayerType type = i->second.layer;
Chris@0 3664
Chris@0 3665 LayerFactory::LayerTypeSet emptyTypes =
Chris@0 3666 LayerFactory::getInstance()->getValidEmptyLayerTypes();
Chris@0 3667
Chris@499 3668 Layer *newLayer = 0;
Chris@0 3669
Chris@0 3670 if (emptyTypes.find(type) != emptyTypes.end()) {
Chris@0 3671
Chris@0 3672 newLayer = m_document->createEmptyLayer(type);
Chris@217 3673 if (newLayer) {
Chris@217 3674 m_toolActions[ViewManager::DrawMode]->trigger();
Chris@217 3675 }
Chris@0 3676
Chris@0 3677 } else {
Chris@0 3678
Chris@346 3679 Model *model = i->second.sourceModel;
Chris@346 3680
Chris@415 3681 cerr << "model = "<< model << endl;
Chris@415 3682
Chris@346 3683 if (!model) {
Chris@346 3684 if (type == LayerFactory::TimeRuler) {
Chris@346 3685 newLayer = m_document->createMainModelLayer(type);
Chris@346 3686 } else {
Chris@346 3687 // if model is unspecified and this is not a
Chris@415 3688 // time-ruler layer, use any plausible model from
Chris@415 3689 // the current pane -- this is the case for
Chris@415 3690 // right-button menu layer additions
Chris@415 3691 Pane::ModelSet ms = pane->getModels();
Chris@415 3692 foreach (Model *m, ms) {
Chris@415 3693 RangeSummarisableTimeValueModel *r =
Chris@415 3694 dynamic_cast<RangeSummarisableTimeValueModel *>(m);
Chris@415 3695 if (r) model = m;
Chris@346 3696 }
Chris@346 3697 if (!model) model = getMainModel();
Chris@346 3698 }
Chris@346 3699 }
Chris@346 3700
Chris@346 3701 if (model) {
Chris@238 3702 newLayer = m_document->createLayer(type);
Chris@346 3703 if (m_document->isKnownModel(model)) {
Chris@238 3704 m_document->setChannel(newLayer, i->second.channel);
Chris@346 3705 m_document->setModel(newLayer, model);
Chris@238 3706 } else {
Chris@665 3707 cerr << "WARNING: MainWindow::addLayer: unknown model "
Chris@346 3708 << model
Chris@238 3709 << " (\""
Chris@838 3710 << model->objectName()
Chris@238 3711 << "\") in layer action map"
Chris@665 3712 << endl;
Chris@238 3713 }
Chris@232 3714 }
Chris@238 3715 }
Chris@0 3716
Chris@217 3717 if (newLayer) {
Chris@217 3718 m_document->addLayerToView(pane, newLayer);
Chris@217 3719 m_paneStack->setCurrentLayer(pane, newLayer);
Chris@217 3720 }
Chris@0 3721
Chris@0 3722 return;
Chris@0 3723 }
Chris@0 3724
Chris@224 3725 //!!! want to do something like this, but it's not supported in
Chris@224 3726 //ModelTransformerFactory yet
Chris@224 3727 /*
Chris@0 3728 int channel = -1;
Chris@0 3729 // pick up the default channel from any existing layers on the same pane
Chris@0 3730 for (int j = 0; j < pane->getLayerCount(); ++j) {
Chris@0 3731 int c = LayerFactory::getInstance()->getChannel(pane->getLayer(j));
Chris@0 3732 if (c != -1) {
Chris@0 3733 channel = c;
Chris@0 3734 break;
Chris@0 3735 }
Chris@0 3736 }
Chris@224 3737 */
Chris@0 3738
Chris@33 3739 // We always ask for configuration, even if the plugin isn't
Chris@33 3740 // supposed to be configurable, because we need to let the user
Chris@33 3741 // change the execution context (block size etc).
Chris@0 3742
Chris@224 3743 QString transformId = i->second;
Chris@274 3744
Chris@274 3745 addLayer(transformId);
Chris@274 3746 }
Chris@274 3747
Chris@274 3748 void
Chris@274 3749 MainWindow::addLayer(QString transformId)
Chris@274 3750 {
Chris@274 3751 Pane *pane = m_paneStack->getCurrentPane();
Chris@274 3752 if (!pane) {
Chris@665 3753 cerr << "WARNING: MainWindow::addLayer: no current pane" << endl;
Chris@274 3754 return;
Chris@274 3755 }
Chris@274 3756
Chris@224 3757 Transform transform = TransformFactory::getInstance()->
Chris@224 3758 getDefaultTransformFor(transformId);
Chris@27 3759
Chris@66 3760 std::vector<Model *> candidateInputModels =
Chris@224 3761 m_document->getTransformInputModels();
Chris@53 3762
Chris@219 3763 Model *defaultInputModel = 0;
Chris@895 3764
Chris@219 3765 for (int j = 0; j < pane->getLayerCount(); ++j) {
Chris@895 3766
Chris@219 3767 Layer *layer = pane->getLayer(j);
Chris@219 3768 if (!layer) continue;
Chris@895 3769
Chris@243 3770 if (LayerFactory::getInstance()->getLayerType(layer) !=
Chris@243 3771 LayerFactory::Waveform &&
Chris@243 3772 !layer->isLayerOpaque()) continue;
Chris@895 3773
Chris@219 3774 Model *model = layer->getModel();
Chris@219 3775 if (!model) continue;
Chris@895 3776
Chris@219 3777 for (size_t k = 0; k < candidateInputModels.size(); ++k) {
Chris@219 3778 if (candidateInputModels[k] == model) {
Chris@219 3779 defaultInputModel = model;
Chris@219 3780 break;
Chris@219 3781 }
Chris@219 3782 }
Chris@895 3783
Chris@219 3784 if (defaultInputModel) break;
Chris@219 3785 }
Chris@895 3786
Chris@895 3787 if (candidateInputModels.size() > 1) {
Chris@895 3788 // Add an aggregate model as another option
Chris@895 3789 AggregateWaveModel::ChannelSpecList sl;
Chris@895 3790 foreach (Model *m, candidateInputModels) {
Chris@895 3791 RangeSummarisableTimeValueModel *r =
Chris@895 3792 qobject_cast<RangeSummarisableTimeValueModel *>(m);
Chris@895 3793 if (r) {
Chris@895 3794 sl.push_back(AggregateWaveModel::ModelChannelSpec(r, -1));
Chris@895 3795 }
Chris@895 3796 }
Chris@895 3797 if (!sl.empty()) {
Chris@895 3798 AggregateWaveModel *aggregate = new AggregateWaveModel(sl);
Chris@895 3799 aggregate->setObjectName(tr("Multiplex all of the above"));
Chris@895 3800 candidateInputModels.push_back(aggregate);
Chris@895 3801 //!!! but it leaks
Chris@895 3802 }
Chris@895 3803 }
Chris@219 3804
Chris@914 3805 sv_frame_t startFrame = 0, duration = 0;
Chris@914 3806 sv_frame_t endFrame = 0;
Chris@184 3807 m_viewManager->getSelection().getExtents(startFrame, endFrame);
Chris@184 3808 if (endFrame > startFrame) duration = endFrame - startFrame;
Chris@184 3809 else startFrame = 0;
Chris@184 3810
Chris@357 3811 TransformUserConfigurator configurator;
Chris@357 3812
Chris@224 3813 ModelTransformer::Input input = ModelTransformerFactory::getInstance()->
Chris@224 3814 getConfigurationForTransform
Chris@211 3815 (transform,
Chris@211 3816 candidateInputModels,
Chris@219 3817 defaultInputModel,
Chris@211 3818 m_playSource,
Chris@211 3819 startFrame,
Chris@357 3820 duration,
Chris@357 3821 &configurator);
Chris@211 3822
Chris@224 3823 if (!input.getModel()) return;
Chris@224 3824
Chris@438 3825 // SVDEBUG << "MainWindow::addLayer: Input model is " << input.getModel() << " \"" << input.getModel()->objectName() << "\"" << endl << "transform:" << endl << transform.toXmlString() << endl;
Chris@224 3826
Chris@224 3827 Layer *newLayer = m_document->createDerivedLayer(transform, input);
Chris@0 3828
Chris@0 3829 if (newLayer) {
Chris@0 3830 m_document->addLayerToView(pane, newLayer);
Chris@224 3831 m_document->setChannel(newLayer, input.getChannel());
Chris@224 3832 m_recentTransforms.add(transformId);
Chris@70 3833 m_paneStack->setCurrentLayer(pane, newLayer);
Chris@0 3834 }
Chris@0 3835
Chris@0 3836 updateMenuStates();
Chris@0 3837 }
Chris@0 3838
Chris@0 3839 void
Chris@0 3840 MainWindow::renameCurrentLayer()
Chris@0 3841 {
Chris@0 3842 Pane *pane = m_paneStack->getCurrentPane();
Chris@0 3843 if (pane) {
Chris@0 3844 Layer *layer = pane->getSelectedLayer();
Chris@0 3845 if (layer) {
Chris@0 3846 bool ok = false;
Chris@0 3847 QString newName = QInputDialog::getText
Chris@0 3848 (this, tr("Rename Layer"),
Chris@0 3849 tr("New name for this layer:"),
Chris@0 3850 QLineEdit::Normal, layer->objectName(), &ok);
Chris@0 3851 if (ok) {
Chris@239 3852 layer->setPresentationName(newName);
Chris@95 3853 setupExistingLayersMenus();
Chris@0 3854 }
Chris@0 3855 }
Chris@0 3856 }
Chris@0 3857 }
Chris@0 3858
Chris@0 3859 void
Chris@272 3860 MainWindow::findTransform()
Chris@272 3861 {
Chris@274 3862 TransformFinder *finder = new TransformFinder(this);
Chris@274 3863 if (!finder->exec()) {
Chris@274 3864 delete finder;
Chris@274 3865 return;
Chris@273 3866 }
Chris@274 3867 TransformId transform = finder->getTransform();
Chris@274 3868 delete finder;
Chris@287 3869
Chris@287 3870 if (getMainModel() != 0 && m_paneStack->getCurrentPane() != 0) {
Chris@287 3871 addLayer(transform);
Chris@287 3872 }
Chris@272 3873 }
Chris@272 3874
Chris@272 3875 void
Chris@207 3876 MainWindow::playSoloToggled()
Chris@207 3877 {
Chris@207 3878 MainWindowBase::playSoloToggled();
Chris@207 3879 m_soloModified = true;
Chris@207 3880 }
Chris@207 3881
Chris@207 3882 void
Chris@206 3883 MainWindow::alignToggled()
Chris@206 3884 {
Chris@206 3885 QAction *action = dynamic_cast<QAction *>(sender());
Chris@206 3886
Chris@207 3887 if (!m_viewManager) return;
Chris@207 3888
Chris@206 3889 if (action) {
Chris@206 3890 m_viewManager->setAlignMode(action->isChecked());
Chris@206 3891 } else {
Chris@206 3892 m_viewManager->setAlignMode(!m_viewManager->getAlignMode());
Chris@206 3893 }
Chris@206 3894
Chris@206 3895 if (m_viewManager->getAlignMode()) {
Chris@207 3896 m_prevSolo = m_soloAction->isChecked();
Chris@208 3897 if (!m_soloAction->isChecked()) {
Chris@208 3898 m_soloAction->setChecked(true);
Chris@208 3899 MainWindowBase::playSoloToggled();
Chris@208 3900 }
Chris@207 3901 m_soloModified = false;
Chris@207 3902 emit canChangeSolo(false);
Chris@206 3903 m_document->alignModels();
Chris@206 3904 m_document->setAutoAlignment(true);
Chris@206 3905 } else {
Chris@207 3906 if (!m_soloModified) {
Chris@208 3907 if (m_soloAction->isChecked() != m_prevSolo) {
Chris@208 3908 m_soloAction->setChecked(m_prevSolo);
Chris@208 3909 MainWindowBase::playSoloToggled();
Chris@208 3910 }
Chris@207 3911 }
Chris@207 3912 emit canChangeSolo(true);
Chris@206 3913 m_document->setAutoAlignment(false);
Chris@206 3914 }
Chris@206 3915
Chris@206 3916 for (int i = 0; i < m_paneStack->getPaneCount(); ++i) {
Chris@206 3917
Chris@206 3918 Pane *pane = m_paneStack->getPane(i);
Chris@206 3919 if (!pane) continue;
Chris@206 3920
Chris@206 3921 pane->update();
Chris@206 3922 }
Chris@206 3923 }
Chris@206 3924
Chris@206 3925 void
Chris@59 3926 MainWindow::playSpeedChanged(int position)
Chris@0 3927 {
Chris@1031 3928 PlaySpeedRangeMapper mapper;
Chris@60 3929
Chris@922 3930 double percent = m_playSpeed->mappedValue();
Chris@922 3931 double factor = mapper.getFactorForValue(percent);
Chris@60 3932
Chris@1031 3933 // cerr << "play speed position = " << position << " (range 0-120) percent = " << percent << " factor = " << factor << endl;
Chris@1031 3934
Chris@1031 3935 int centre = m_playSpeed->defaultValue();
Chris@1031 3936
Chris@1031 3937 // Percentage is shown to 0dp if >100, to 1dp if <100; factor is
Chris@1031 3938 // shown to 3sf
Chris@1031 3939
Chris@1031 3940 char pcbuf[30];
Chris@1031 3941 char facbuf[30];
Chris@1031 3942
Chris@1031 3943 if (position == centre) {
Chris@155 3944 contextHelpChanged(tr("Playback speed: Normal"));
Chris@1031 3945 } else if (position < centre) {
Chris@1031 3946 sprintf(pcbuf, "%.1f", percent);
Chris@1031 3947 sprintf(facbuf, "%.3g", 1.0 / factor);
Chris@1031 3948 contextHelpChanged(tr("Playback speed: %1% (%2x slower)")
Chris@1031 3949 .arg(pcbuf)
Chris@1031 3950 .arg(facbuf));
Chris@155 3951 } else {
Chris@1031 3952 sprintf(pcbuf, "%.0f", percent);
Chris@1031 3953 sprintf(facbuf, "%.3g", factor);
Chris@1031 3954 contextHelpChanged(tr("Playback speed: %1% (%2x faster)")
Chris@1031 3955 .arg(pcbuf)
Chris@1031 3956 .arg(facbuf));
Chris@155 3957 }
Chris@155 3958
Chris@1031 3959 m_playSource->setTimeStretch(1.0 / factor); // factor is a speedup
Chris@155 3960
Chris@155 3961 updateMenuStates();
Chris@16 3962 }
Chris@16 3963
Chris@26 3964 void
Chris@155 3965 MainWindow::speedUpPlayback()
Chris@155 3966 {
Chris@155 3967 int value = m_playSpeed->value();
Chris@155 3968 value = value + m_playSpeed->pageStep();
Chris@155 3969 if (value > m_playSpeed->maximum()) value = m_playSpeed->maximum();
Chris@155 3970 m_playSpeed->setValue(value);
Chris@155 3971 }
Chris@155 3972
Chris@155 3973 void
Chris@155 3974 MainWindow::slowDownPlayback()
Chris@155 3975 {
Chris@155 3976 int value = m_playSpeed->value();
Chris@155 3977 value = value - m_playSpeed->pageStep();
Chris@155 3978 if (value < m_playSpeed->minimum()) value = m_playSpeed->minimum();
Chris@155 3979 m_playSpeed->setValue(value);
Chris@155 3980 }
Chris@155 3981
Chris@155 3982 void
Chris@155 3983 MainWindow::restoreNormalPlayback()
Chris@155 3984 {
Chris@155 3985 m_playSpeed->setValue(m_playSpeed->defaultValue());
Chris@155 3986 }
Chris@155 3987
Chris@155 3988 void
Chris@227 3989 MainWindow::currentPaneChanged(Pane *pane)
Chris@227 3990 {
Chris@228 3991 MainWindowBase::currentPaneChanged(pane);
Chris@228 3992
Chris@227 3993 if (!pane || !m_panLayer) return;
Chris@761 3994
Chris@761 3995 // If this pane contains the main model, it usually makes sense to
Chris@761 3996 // show the main model in the pan layer even if it isn't the top
Chris@761 3997 // layer in the pane (e.g. if the top layer is one derived from
Chris@761 3998 // the main model).
Chris@761 3999 bool containsMainModel = false;
Chris@761 4000 for (int i = pane->getLayerCount(); i > 0; ) {
Chris@761 4001 --i;
Chris@761 4002 Layer *layer = pane->getLayer(i);
Chris@761 4003 if (layer &&
Chris@761 4004 LayerFactory::getInstance()->getLayerType(layer) ==
Chris@761 4005 LayerFactory::Waveform &&
Chris@761 4006 layer->getModel() == getMainModel()) {
Chris@761 4007 containsMainModel = true;
Chris@761 4008 break;
Chris@761 4009 }
Chris@761 4010 }
Chris@761 4011
Chris@761 4012 if (containsMainModel) {
Chris@761 4013 m_panLayer->setModel(getMainModel());
Chris@761 4014 return;
Chris@761 4015 }
Chris@761 4016
Chris@227 4017 for (int i = pane->getLayerCount(); i > 0; ) {
Chris@227 4018 --i;
Chris@227 4019 Layer *layer = pane->getLayer(i);
Chris@227 4020 if (LayerFactory::getInstance()->getLayerType(layer) ==
Chris@227 4021 LayerFactory::Waveform) {
Chris@227 4022 RangeSummarisableTimeValueModel *tvm =
Chris@227 4023 dynamic_cast<RangeSummarisableTimeValueModel *>(layer->getModel());
Chris@227 4024 if (tvm) {
Chris@227 4025 m_panLayer->setModel(tvm);
Chris@227 4026 return;
Chris@227 4027 }
Chris@227 4028 }
Chris@227 4029 }
Chris@227 4030 }
Chris@227 4031
Chris@227 4032 void
Chris@116 4033 MainWindow::updateVisibleRangeDisplay(Pane *p) const
Chris@116 4034 {
Chris@116 4035 if (!getMainModel() || !p) {
Chris@116 4036 return;
Chris@116 4037 }
Chris@116 4038
Chris@117 4039 bool haveSelection = false;
Chris@922 4040 sv_frame_t startFrame = 0, endFrame = 0;
Chris@117 4041
Chris@117 4042 if (m_viewManager && m_viewManager->haveInProgressSelection()) {
Chris@117 4043
Chris@117 4044 bool exclusive = false;
Chris@117 4045 Selection s = m_viewManager->getInProgressSelection(exclusive);
Chris@117 4046
Chris@117 4047 if (!s.isEmpty()) {
Chris@117 4048 haveSelection = true;
Chris@117 4049 startFrame = s.getStartFrame();
Chris@117 4050 endFrame = s.getEndFrame();
Chris@117 4051 }
Chris@117 4052 }
Chris@117 4053
Chris@117 4054 if (!haveSelection) {
Chris@117 4055 startFrame = p->getFirstVisibleFrame();
Chris@117 4056 endFrame = p->getLastVisibleFrame();
Chris@117 4057 }
Chris@117 4058
Chris@116 4059 RealTime start = RealTime::frame2RealTime
Chris@117 4060 (startFrame, getMainModel()->getSampleRate());
Chris@116 4061
Chris@116 4062 RealTime end = RealTime::frame2RealTime
Chris@117 4063 (endFrame, getMainModel()->getSampleRate());
Chris@116 4064
Chris@116 4065 RealTime duration = end - start;
Chris@116 4066
Chris@116 4067 QString startStr, endStr, durationStr;
Chris@116 4068 startStr = start.toText(true).c_str();
Chris@116 4069 endStr = end.toText(true).c_str();
Chris@116 4070 durationStr = duration.toText(true).c_str();
Chris@116 4071
Chris@117 4072 if (haveSelection) {
Chris@117 4073 m_myStatusMessage = tr("Selection: %1 to %2 (duration %3)")
Chris@117 4074 .arg(startStr).arg(endStr).arg(durationStr);
Chris@117 4075 } else {
Chris@117 4076 m_myStatusMessage = tr("Visible: %1 to %2 (duration %3)")
Chris@117 4077 .arg(startStr).arg(endStr).arg(durationStr);
Chris@117 4078 }
Chris@116 4079
Chris@739 4080 if (getStatusLabel()->text() != m_myStatusMessage) {
Chris@739 4081 getStatusLabel()->setText(m_myStatusMessage);
Chris@737 4082 }
Chris@340 4083
Chris@340 4084 updatePositionStatusDisplays();
Chris@340 4085 }
Chris@340 4086
Chris@340 4087 void
Chris@340 4088 MainWindow::updatePositionStatusDisplays() const
Chris@340 4089 {
Chris@340 4090 if (!statusBar()->isVisible()) return;
Chris@340 4091
Chris@340 4092 Pane *pane = 0;
Chris@922 4093 sv_frame_t frame = m_viewManager->getPlaybackFrame();
Chris@340 4094
Chris@340 4095 if (m_paneStack) pane = m_paneStack->getCurrentPane();
Chris@340 4096 if (!pane) return;
Chris@340 4097
Chris@340 4098 int layers = pane->getLayerCount();
Chris@340 4099 if (layers == 0) m_currentLabel->setText("");
Chris@340 4100
Chris@340 4101 for (int i = layers-1; i >= 0; --i) {
Chris@340 4102 Layer *layer = pane->getLayer(i);
Chris@340 4103 if (!layer) continue;
Chris@340 4104 if (!layer->isLayerEditable()) continue;
Chris@340 4105 QString label = layer->getLabelPreceding
Chris@340 4106 (pane->alignFromReference(frame));
Chris@340 4107 m_currentLabel->setText(label);
Chris@340 4108 break;
Chris@340 4109 }
Chris@116 4110 }
Chris@116 4111
Chris@116 4112 void
Chris@0 4113 MainWindow::outputLevelsChanged(float left, float right)
Chris@0 4114 {
Chris@0 4115 m_fader->setPeakLeft(left);
Chris@0 4116 m_fader->setPeakRight(right);
Chris@0 4117 }
Chris@0 4118
Chris@0 4119 void
Chris@921 4120 MainWindow::sampleRateMismatch(sv_samplerate_t requested,
Chris@921 4121 sv_samplerate_t actual,
Chris@0 4122 bool willResample)
Chris@0 4123 {
Chris@0 4124 if (!willResample) {
Chris@247 4125 emit hideSplash();
Chris@0 4126 QMessageBox::information
Chris@0 4127 (this, tr("Sample rate mismatch"),
Chris@193 4128 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 4129 .arg(requested).arg(actual));
Chris@0 4130 }
Chris@0 4131
Chris@0 4132 updateDescriptionLabel();
Chris@0 4133 }
Chris@0 4134
Chris@0 4135 void
Chris@42 4136 MainWindow::audioOverloadPluginDisabled()
Chris@42 4137 {
Chris@42 4138 QMessageBox::information
Chris@42 4139 (this, tr("Audio processing overload"),
Chris@193 4140 tr("<b>Overloaded</b><p>Audio effects plugin auditioning has been disabled due to a processing overload."));
Chris@42 4141 }
Chris@42 4142
Chris@42 4143 void
Chris@266 4144 MainWindow::audioTimeStretchMultiChannelDisabled()
Chris@266 4145 {
Chris@266 4146 static bool shownOnce = false;
Chris@266 4147 if (shownOnce) return;
Chris@266 4148 QMessageBox::information
Chris@266 4149 (this, tr("Audio processing overload"),
Chris@266 4150 tr("<b>Overloaded</b><p>Audio playback speed processing has been reduced to a single channel, due to a processing overload."));
Chris@266 4151 shownOnce = true;
Chris@266 4152 }
Chris@266 4153
Chris@266 4154 void
Chris@1148 4155 MainWindow::pluginPopulationWarning()
Chris@1087 4156 {
Chris@1148 4157 QString warning = PluginScan::getInstance()->getStartupFailureReport();
Chris@1087 4158 QMessageBox::warning(this, tr("Problems loading plugins"), warning);
Chris@1087 4159 }
Chris@1087 4160
Chris@1087 4161 void
Chris@304 4162 MainWindow::midiEventsAvailable()
Chris@304 4163 {
Chris@307 4164 Pane *currentPane = 0;
Chris@307 4165 NoteLayer *currentNoteLayer = 0;
Chris@309 4166 TimeValueLayer *currentTimeValueLayer = 0;
Chris@307 4167
Chris@793 4168 if (m_paneStack) {
Chris@793 4169 currentPane = m_paneStack->getCurrentPane();
Chris@793 4170 }
Chris@793 4171
Chris@307 4172 if (currentPane) {
Chris@307 4173 currentNoteLayer = dynamic_cast<NoteLayer *>
Chris@307 4174 (currentPane->getSelectedLayer());
Chris@309 4175 currentTimeValueLayer = dynamic_cast<TimeValueLayer *>
Chris@309 4176 (currentPane->getSelectedLayer());
Chris@838 4177 } else {
Chris@793 4178 // discard these events
Chris@793 4179 while (m_midiInput->getEventsAvailable() > 0) {
Chris@793 4180 (void)m_midiInput->readEvent();
Chris@793 4181 }
Chris@793 4182 return;
Chris@793 4183 }
Chris@793 4184
Chris@305 4185 // This is called through a serialised signal/slot invocation
Chris@305 4186 // (across threads). It could happen quite some time after the
Chris@305 4187 // event was actually received, which is why event timestamping
Chris@305 4188 // happens in the MIDI input class and not here.
Chris@307 4189
Chris@305 4190 while (m_midiInput->getEventsAvailable() > 0) {
Chris@308 4191
Chris@305 4192 MIDIEvent ev(m_midiInput->readEvent());
Chris@307 4193
Chris@922 4194 sv_frame_t frame = currentPane->alignFromReference(ev.getTime());
Chris@309 4195
Chris@308 4196 bool noteOn = (ev.getMessageType() == MIDIConstants::MIDI_NOTE_ON &&
Chris@308 4197 ev.getVelocity() > 0);
Chris@308 4198
Chris@308 4199 bool noteOff = (ev.getMessageType() == MIDIConstants::MIDI_NOTE_OFF ||
Chris@308 4200 (ev.getMessageType() == MIDIConstants::MIDI_NOTE_ON &&
Chris@308 4201 ev.getVelocity() == 0));
Chris@308 4202
Chris@307 4203 if (currentNoteLayer) {
Chris@307 4204
Chris@310 4205 if (!m_playSource || !m_playSource->isPlaying()) continue;
Chris@310 4206
Chris@308 4207 if (noteOn) {
Chris@307 4208
Chris@309 4209 currentNoteLayer->addNoteOn(frame,
Chris@307 4210 ev.getPitch(),
Chris@307 4211 ev.getVelocity());
Chris@307 4212
Chris@308 4213 } else if (noteOff) {
Chris@307 4214
Chris@309 4215 currentNoteLayer->addNoteOff(frame,
Chris@307 4216 ev.getPitch());
Chris@307 4217
Chris@307 4218 }
Chris@307 4219
Chris@309 4220 continue;
Chris@309 4221 }
Chris@309 4222
Chris@309 4223 if (currentTimeValueLayer) {
Chris@308 4224
Chris@308 4225 if (!noteOn) continue;
Chris@310 4226
Chris@310 4227 if (!m_playSource || !m_playSource->isPlaying()) continue;
Chris@310 4228
Chris@309 4229 Model *model = static_cast<Layer *>(currentTimeValueLayer)->getModel();
Chris@309 4230 SparseTimeValueModel *tvm =
Chris@309 4231 dynamic_cast<SparseTimeValueModel *>(model);
Chris@309 4232 if (tvm) {
Chris@309 4233 SparseTimeValueModel::Point point(frame, ev.getPitch() % 12, "");
Chris@309 4234 SparseTimeValueModel::AddPointCommand *command =
Chris@309 4235 new SparseTimeValueModel::AddPointCommand
Chris@309 4236 (tvm, point, tr("Add Point"));
Chris@309 4237 CommandHistory::getInstance()->addCommand(command);
Chris@309 4238 }
Chris@838 4239
Chris@309 4240 continue;
Chris@305 4241 }
Chris@309 4242
Chris@838 4243 // This is reached only if !currentNoteLayer and
Chris@838 4244 // !currentTimeValueLayer, i.e. there is some other sort of
Chris@838 4245 // layer that may be insertable-into
Chris@838 4246
Chris@309 4247 if (!noteOn) continue;
Chris@309 4248 insertInstantAt(ev.getTime());
Chris@304 4249 }
Chris@304 4250 }
Chris@304 4251
Chris@304 4252 void
Chris@730 4253 MainWindow::playStatusChanged(bool )
Chris@305 4254 {
Chris@307 4255 Pane *currentPane = 0;
Chris@307 4256 NoteLayer *currentNoteLayer = 0;
Chris@307 4257
Chris@307 4258 if (m_paneStack) currentPane = m_paneStack->getCurrentPane();
Chris@307 4259 if (currentPane) {
Chris@307 4260 currentNoteLayer = dynamic_cast<NoteLayer *>(currentPane->getSelectedLayer());
Chris@307 4261 }
Chris@307 4262
Chris@307 4263 if (currentNoteLayer) {
Chris@307 4264 currentNoteLayer->abandonNoteOns();
Chris@307 4265 }
Chris@305 4266 }
Chris@305 4267
Chris@305 4268 void
Chris@200 4269 MainWindow::layerRemoved(Layer *layer)
Chris@0 4270 {
Chris@95 4271 setupExistingLayersMenus();
Chris@200 4272 MainWindowBase::layerRemoved(layer);
Chris@0 4273 }
Chris@0 4274
Chris@0 4275 void
Chris@0 4276 MainWindow::layerInAView(Layer *layer, bool inAView)
Chris@0 4277 {
Chris@95 4278 setupExistingLayersMenus();
Chris@200 4279 MainWindowBase::layerInAView(layer, inAView);
Chris@0 4280 }
Chris@0 4281
Chris@0 4282 void
Chris@0 4283 MainWindow::modelAdded(Model *model)
Chris@0 4284 {
Chris@200 4285 MainWindowBase::modelAdded(model);
Chris@66 4286 if (dynamic_cast<DenseTimeValueModel *>(model)) {
Chris@66 4287 setupPaneAndLayerMenus();
Chris@66 4288 }
Chris@0 4289 }
Chris@0 4290
Chris@0 4291 void
Chris@0 4292 MainWindow::mainModelChanged(WaveFileModel *model)
Chris@0 4293 {
Chris@0 4294 m_panLayer->setModel(model);
Chris@200 4295
Chris@200 4296 MainWindowBase::mainModelChanged(model);
Chris@200 4297
Chris@1055 4298 if (m_playTarget || m_audioIO) {
Chris@200 4299 connect(m_fader, SIGNAL(valueChanged(float)),
Chris@1035 4300 this, SLOT(mainModelGainChanged(float)));
Chris@1035 4301 }
Chris@1035 4302 }
Chris@1035 4303
Chris@1035 4304 void
Chris@1035 4305 MainWindow::mainModelGainChanged(float gain)
Chris@1035 4306 {
Chris@1035 4307 if (m_playTarget) {
Chris@1035 4308 m_playTarget->setOutputGain(gain);
Chris@1055 4309 } else if (m_audioIO) {
Chris@1055 4310 m_audioIO->setOutputGain(gain);
Chris@200 4311 }
Chris@0 4312 }
Chris@0 4313
Chris@0 4314 void
Chris@760 4315 MainWindow::modelAboutToBeDeleted(Model *model)
Chris@760 4316 {
Chris@760 4317 if (model == m_panLayer->getModel()) {
Chris@760 4318 if (model == getMainModel()) {
Chris@760 4319 m_panLayer->setModel(0);
Chris@760 4320 } else {
Chris@760 4321 m_panLayer->setModel(getMainModel());
Chris@760 4322 }
Chris@760 4323 }
Chris@760 4324 MainWindowBase::modelAboutToBeDeleted(model);
Chris@760 4325 }
Chris@760 4326
Chris@760 4327 void
Chris@200 4328 MainWindow::setInstantsNumbering()
Chris@0 4329 {
Chris@200 4330 QAction *a = dynamic_cast<QAction *>(sender());
Chris@200 4331 if (!a) return;
Chris@200 4332
Chris@200 4333 int type = m_numberingActions[a];
Chris@200 4334
Chris@200 4335 if (m_labeller) m_labeller->setType(Labeller::ValueType(type));
Chris@200 4336
Chris@200 4337 QSettings settings;
Chris@200 4338 settings.beginGroup("MainWindow");
Chris@200 4339 settings.setValue("labellertype", type);
Chris@200 4340 settings.endGroup();
Chris@200 4341 }
Chris@200 4342
Chris@200 4343 void
Chris@200 4344 MainWindow::setInstantsCounterCycle()
Chris@200 4345 {
Chris@200 4346 QAction *a = dynamic_cast<QAction *>(sender());
Chris@200 4347 if (!a) return;
Chris@200 4348
Chris@200 4349 int cycle = a->text().toInt();
Chris@200 4350 if (cycle == 0) return;
Chris@200 4351
Chris@200 4352 if (m_labeller) m_labeller->setCounterCycleSize(cycle);
Chris@200 4353
Chris@200 4354 QSettings settings;
Chris@200 4355 settings.beginGroup("MainWindow");
Chris@200 4356 settings.setValue("labellercycle", cycle);
Chris@200 4357 settings.endGroup();
Chris@200 4358 }
Chris@200 4359
Chris@200 4360 void
Chris@597 4361 MainWindow::setInstantsCounters()
Chris@200 4362 {
Chris@200 4363 LabelCounterInputDialog dialog(m_labeller, this);
Chris@241 4364 dialog.setWindowTitle(tr("Reset Counters"));
Chris@200 4365 dialog.exec();
Chris@0 4366 }
Chris@0 4367
Chris@0 4368 void
Chris@597 4369 MainWindow::resetInstantsCounters()
Chris@597 4370 {
Chris@597 4371 if (m_labeller) m_labeller->resetCounters();
Chris@597 4372 }
Chris@597 4373
Chris@597 4374 void
Chris@233 4375 MainWindow::modelGenerationFailed(QString transformName, QString message)
Chris@233 4376 {
Chris@247 4377 emit hideSplash();
Chris@247 4378
Chris@983 4379 QString quoted;
Chris@983 4380 if (transformName != "") {
Chris@983 4381 quoted = QString("\"%1\" ").arg(transformName);
Chris@983 4382 }
Chris@983 4383
Chris@233 4384 if (message != "") {
Chris@233 4385
Chris@233 4386 QMessageBox::warning
Chris@233 4387 (this,
Chris@233 4388 tr("Failed to generate layer"),
Chris@983 4389 tr("<b>Layer generation failed</b><p>Failed to generate derived layer.<p>The layer transform %1failed:<p>%2")
Chris@983 4390 .arg(quoted).arg(message),
Chris@233 4391 QMessageBox::Ok);
Chris@233 4392 } else {
Chris@233 4393 QMessageBox::warning
Chris@233 4394 (this,
Chris@233 4395 tr("Failed to generate layer"),
Chris@983 4396 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 4397 .arg(quoted),
Chris@233 4398 QMessageBox::Ok);
Chris@233 4399 }
Chris@233 4400 }
Chris@233 4401
Chris@233 4402 void
Chris@730 4403 MainWindow::modelGenerationWarning(QString /* transformName */, QString message)
Chris@233 4404 {
Chris@247 4405 emit hideSplash();
Chris@247 4406
Chris@233 4407 QMessageBox::warning
Chris@233 4408 (this, tr("Warning"), message, QMessageBox::Ok);
Chris@233 4409 }
Chris@233 4410
Chris@233 4411 void
Chris@233 4412 MainWindow::modelRegenerationFailed(QString layerName,
Chris@233 4413 QString transformName, QString message)
Chris@233 4414 {
Chris@247 4415 emit hideSplash();
Chris@247 4416
Chris@233 4417 if (message != "") {
Chris@233 4418
Chris@233 4419 QMessageBox::warning
Chris@233 4420 (this,
Chris@233 4421 tr("Failed to regenerate layer"),
Chris@233 4422 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 4423 .arg(layerName).arg(transformName).arg(message),
Chris@233 4424 QMessageBox::Ok);
Chris@233 4425 } else {
Chris@233 4426 QMessageBox::warning
Chris@233 4427 (this,
Chris@233 4428 tr("Failed to regenerate layer"),
Chris@233 4429 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 4430 .arg(layerName).arg(transformName),
Chris@233 4431 QMessageBox::Ok);
Chris@233 4432 }
Chris@233 4433 }
Chris@233 4434
Chris@233 4435 void
Chris@233 4436 MainWindow::modelRegenerationWarning(QString layerName,
Chris@730 4437 QString /* transformName */,
Chris@730 4438 QString message)
Chris@233 4439 {
Chris@247 4440 emit hideSplash();
Chris@247 4441
Chris@233 4442 QMessageBox::warning
Chris@233 4443 (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 4444 }
Chris@233 4445
Chris@233 4446 void
Chris@1151 4447 MainWindow::alignmentFailed(QString message)
Chris@0 4448 {
Chris@0 4449 QMessageBox::warning
Chris@0 4450 (this,
Chris@233 4451 tr("Failed to calculate alignment"),
Chris@1151 4452 tr("<b>Alignment calculation failed</b><p>Failed to calculate an audio alignment:<p>%1")
Chris@1151 4453 .arg(message),
Chris@165 4454 QMessageBox::Ok);
Chris@0 4455 }
Chris@0 4456
Chris@0 4457 void
Chris@0 4458 MainWindow::rightButtonMenuRequested(Pane *pane, QPoint position)
Chris@0 4459 {
Chris@438 4460 // SVDEBUG << "MainWindow::rightButtonMenuRequested(" << pane << ", " << position.x() << ", " << position.y() << ")" << endl;
Chris@0 4461 m_paneStack->setCurrentPane(pane);
Chris@0 4462 m_rightButtonMenu->popup(position);
Chris@0 4463 }
Chris@0 4464
Chris@0 4465 void
Chris@0 4466 MainWindow::showLayerTree()
Chris@0 4467 {
Chris@219 4468 if (!m_layerTreeDialog.isNull()) {
Chris@219 4469 m_layerTreeDialog->show();
Chris@219 4470 m_layerTreeDialog->raise();
Chris@177 4471 return;
Chris@177 4472 }
Chris@177 4473
Chris@762 4474 m_layerTreeDialog = new LayerTreeDialog(m_paneStack, this);
Chris@232 4475 m_layerTreeDialog->setAttribute(Qt::WA_DeleteOnClose); // see below
Chris@219 4476 m_layerTreeDialog->show();
Chris@0 4477 }
Chris@0 4478
Chris@0 4479 void
Chris@306 4480 MainWindow::showActivityLog()
Chris@306 4481 {
Chris@306 4482 m_activityLog->show();
Chris@306 4483 m_activityLog->raise();
Chris@306 4484 m_activityLog->scrollToEnd();
Chris@306 4485 }
Chris@306 4486
Chris@306 4487 void
Chris@891 4488 MainWindow::showUnitConverter()
Chris@891 4489 {
Chris@891 4490 m_unitConverter->show();
Chris@891 4491 m_unitConverter->raise();
Chris@891 4492 }
Chris@891 4493
Chris@891 4494 void
Chris@0 4495 MainWindow::preferences()
Chris@0 4496 {
Chris@436 4497 bool goToTemplateTab =
Chris@436 4498 (sender() && sender()->objectName() == "set_default_template");
Chris@436 4499
Chris@0 4500 if (!m_preferencesDialog.isNull()) {
Chris@0 4501 m_preferencesDialog->show();
Chris@0 4502 m_preferencesDialog->raise();
Chris@436 4503 if (goToTemplateTab) {
Chris@436 4504 m_preferencesDialog->switchToTab(PreferencesDialog::TemplateTab);
Chris@436 4505 }
Chris@0 4506 return;
Chris@0 4507 }
Chris@0 4508
Chris@0 4509 m_preferencesDialog = new PreferencesDialog(this);
Chris@0 4510
Chris@0 4511 // DeleteOnClose is safe here, because m_preferencesDialog is a
Chris@0 4512 // QPointer that will be zeroed when the dialog is deleted. We
Chris@0 4513 // use it in preference to leaving the dialog lying around because
Chris@0 4514 // if you Cancel the dialog, it resets the preferences state
Chris@0 4515 // without resetting its own widgets, so its state will be
Chris@0 4516 // incorrect when next shown unless we construct it afresh
Chris@0 4517 m_preferencesDialog->setAttribute(Qt::WA_DeleteOnClose);
Chris@0 4518
Chris@0 4519 m_preferencesDialog->show();
Chris@436 4520 if (goToTemplateTab) {
Chris@436 4521 m_preferencesDialog->switchToTab(PreferencesDialog::TemplateTab);
Chris@436 4522 }
Chris@0 4523 }
Chris@0 4524
Chris@0 4525 void
Chris@90 4526 MainWindow::mouseEnteredWidget()
Chris@90 4527 {
Chris@90 4528 QWidget *w = dynamic_cast<QWidget *>(sender());
Chris@90 4529 if (!w) return;
Chris@90 4530
Chris@90 4531 if (w == m_fader) {
Chris@116 4532 contextHelpChanged(tr("Adjust the master playback level"));
Chris@90 4533 } else if (w == m_playSpeed) {
Chris@116 4534 contextHelpChanged(tr("Adjust the master playback speed"));
Chris@90 4535 }
Chris@90 4536 }
Chris@90 4537
Chris@90 4538 void
Chris@90 4539 MainWindow::mouseLeftWidget()
Chris@90 4540 {
Chris@116 4541 contextHelpChanged("");
Chris@116 4542 }
Chris@116 4543
Chris@116 4544 void
Chris@0 4545 MainWindow::website()
Chris@0 4546 {
Chris@0 4547 openHelpUrl(tr("http://www.sonicvisualiser.org/"));
Chris@0 4548 }
Chris@0 4549
Chris@0 4550 void
Chris@0 4551 MainWindow::help()
Chris@0 4552 {
Chris@318 4553 openHelpUrl(tr("http://www.sonicvisualiser.org/doc/reference/%1/en/").arg(SV_VERSION));
Chris@0 4554 }
Chris@0 4555
Chris@0 4556 void
Chris@0 4557 MainWindow::about()
Chris@0 4558 {
Chris@0 4559 bool debug = false;
Chris@0 4560 QString version = "(unknown version)";
Chris@0 4561
Chris@0 4562 #ifdef BUILD_DEBUG
Chris@0 4563 debug = true;
Chris@285 4564 #endif // BUILD_DEBUG
Chris@0 4565 #ifdef SV_VERSION
Chris@0 4566 #ifdef SVNREV
Chris@0 4567 version = tr("Release %1 : Revision %2").arg(SV_VERSION).arg(SVNREV);
Chris@285 4568 #else // !SVNREV
Chris@0 4569 version = tr("Release %1").arg(SV_VERSION);
Chris@285 4570 #endif // SVNREV
Chris@285 4571 #else // !SV_VERSION
Chris@0 4572 #ifdef SVNREV
Chris@0 4573 version = tr("Unreleased : Revision %1").arg(SVNREV);
Chris@285 4574 #endif // SVNREV
Chris@285 4575 #endif // SV_VERSION
Chris@0 4576
Chris@0 4577 QString aboutText;
Chris@0 4578
Chris@0 4579 aboutText += tr("<h3>About Sonic Visualiser</h3>");
Chris@285 4580 aboutText += tr("<p>Sonic Visualiser is a program for viewing and exploring audio data for semantic music analysis and annotation.<br><a href=\"http://www.sonicvisualiser.org/\">http://www.sonicvisualiser.org/</a></p>");
Chris@285 4581 aboutText += tr("<p><small>%1 : %2 configuration</small></p>")
Chris@0 4582 .arg(version)
Chris@0 4583 .arg(debug ? tr("Debug") : tr("Release"));
Chris@0 4584
Chris@285 4585 aboutText += "<small>";
Chris@285 4586
Chris@1155 4587 aboutText += tr("With Qt v%1 &copy; The Qt Company").arg(QT_VERSION_STR);
Chris@285 4588
Chris@0 4589 #ifdef HAVE_JACK
Chris@93 4590 #ifdef JACK_VERSION
Chris@285 4591 aboutText += tr("<br>With JACK audio output library v%1 &copy; Paul Davis and Jack O'Quin").arg(JACK_VERSION);
Chris@285 4592 #else // !JACK_VERSION
Chris@257 4593 aboutText += tr("<br>With JACK audio output library &copy; Paul Davis and Jack O'Quin");
Chris@285 4594 #endif // JACK_VERSION
Chris@285 4595 #endif // HAVE_JACK
Chris@0 4596 #ifdef HAVE_PORTAUDIO
Chris@257 4597 aboutText += tr("<br>With PortAudio audio output library &copy; Ross Bencina and Phil Burk");
Chris@285 4598 #endif // HAVE_PORTAUDIO
Chris@257 4599 #ifdef HAVE_LIBPULSE
Chris@285 4600 #ifdef LIBPULSE_VERSION
Chris@285 4601 aboutText += tr("<br>With PulseAudio audio output library v%1 &copy; Lennart Poettering and Pierre Ossman").arg(LIBPULSE_VERSION);
Chris@285 4602 #else // !LIBPULSE_VERSION
Chris@257 4603 aboutText += tr("<br>With PulseAudio audio output library &copy; Lennart Poettering and Pierre Ossman");
Chris@285 4604 #endif // LIBPULSE_VERSION
Chris@285 4605 #endif // HAVE_LIBPULSE
Chris@0 4606 #ifdef HAVE_OGGZ
Chris@93 4607 #ifdef OGGZ_VERSION
Chris@0 4608 aboutText += tr("<br>With Ogg file decoder (oggz v%1, fishsound v%2) &copy; CSIRO Australia").arg(OGGZ_VERSION).arg(FISHSOUND_VERSION);
Chris@285 4609 #else // !OGGZ_VERSION
Chris@93 4610 aboutText += tr("<br>With Ogg file decoder &copy; CSIRO Australia");
Chris@285 4611 #endif // OGGZ_VERSION
Chris@285 4612 #endif // HAVE_OGGZ
Chris@0 4613 #ifdef HAVE_MAD
Chris@93 4614 #ifdef MAD_VERSION
Chris@285 4615 aboutText += tr("<br>With MAD mp3 decoder v%1 &copy; Underbit Technologies Inc").arg(MAD_VERSION);
Chris@285 4616 #else // !MAD_VERSION
Chris@93 4617 aboutText += tr("<br>With MAD mp3 decoder &copy; Underbit Technologies Inc");
Chris@285 4618 #endif // MAD_VERSION
Chris@285 4619 #endif // HAVE_MAD
Chris@0 4620 #ifdef HAVE_SAMPLERATE
Chris@93 4621 #ifdef SAMPLERATE_VERSION
Chris@285 4622 aboutText += tr("<br>With libsamplerate v%1 &copy; Erik de Castro Lopo").arg(SAMPLERATE_VERSION);
Chris@285 4623 #else // !SAMPLERATE_VERSION
Chris@93 4624 aboutText += tr("<br>With libsamplerate &copy; Erik de Castro Lopo");
Chris@285 4625 #endif // SAMPLERATE_VERSION
Chris@285 4626 #endif // HAVE_SAMPLERATE
Chris@0 4627 #ifdef HAVE_SNDFILE
Chris@93 4628 #ifdef SNDFILE_VERSION
Chris@285 4629 aboutText += tr("<br>With libsndfile v%1 &copy; Erik de Castro Lopo").arg(SNDFILE_VERSION);
Chris@285 4630 #else // !SNDFILE_VERSION
Chris@93 4631 aboutText += tr("<br>With libsndfile &copy; Erik de Castro Lopo");
Chris@285 4632 #endif // SNDFILE_VERSION
Chris@285 4633 #endif // HAVE_SNDFILE
Chris@127 4634 #ifdef HAVE_FFTW3F
Chris@93 4635 #ifdef FFTW3_VERSION
Chris@285 4636 aboutText += tr("<br>With FFTW3 v%1 &copy; Matteo Frigo and MIT").arg(FFTW3_VERSION);
Chris@285 4637 #else // !FFTW3_VERSION
Chris@93 4638 aboutText += tr("<br>With FFTW3 &copy; Matteo Frigo and MIT");
Chris@285 4639 #endif // FFTW3_VERSION
Chris@285 4640 #endif // HAVE_FFTW3F
Chris@267 4641 #ifdef HAVE_RUBBERBAND
Chris@267 4642 #ifdef RUBBERBAND_VERSION
Chris@285 4643 aboutText += tr("<br>With Rubber Band v%1 &copy; Chris Cannam").arg(RUBBERBAND_VERSION);
Chris@285 4644 #else // !RUBBERBAND_VERSION
Chris@267 4645 aboutText += tr("<br>With Rubber Band &copy; Chris Cannam");
Chris@285 4646 #endif // RUBBERBAND_VERSION
Chris@285 4647 #endif // HAVE_RUBBERBAND
Chris@0 4648 #ifdef HAVE_VAMP
Chris@114 4649 aboutText += tr("<br>With Vamp plugin support (API v%1, host SDK v%2) &copy; Chris Cannam").arg(VAMP_API_VERSION).arg(VAMP_SDK_VERSION);
Chris@285 4650 #endif // !HAVE_VAMP
Chris@0 4651 aboutText += tr("<br>With LADSPA plugin support (API v%1) &copy; Richard Furse, Paul Davis, Stefan Westerfeld").arg(LADSPA_VERSION);
Chris@0 4652 aboutText += tr("<br>With DSSI plugin support (API v%1) &copy; Chris Cannam, Steve Harris, Sean Bolton").arg(DSSI_VERSION);
Chris@285 4653 #ifdef REDLAND_VERSION
Chris@285 4654 aboutText += tr("<br>With Redland RDF datastore v%1 &copy; Dave Beckett and the University of Bristol").arg(REDLAND_VERSION);
Chris@285 4655 #else // !REDLAND_VERSION
Chris@285 4656 aboutText += tr("<br>With Redland RDF datastore &copy; Dave Beckett and the University of Bristol");
Chris@285 4657 #endif // REDLAND_VERSION
Chris@523 4658 aboutText += tr("<br>With Serd and Sord RDF parser and store &copy; David Robillard");
Chris@523 4659 aboutText += tr("<br>With Dataquay Qt/RDF library &copy; Chris Cannam");
Chris@285 4660
Chris@300 4661 aboutText += tr("<br>With RtMidi &copy; Gary P. Scavone");
Chris@300 4662
Chris@69 4663 #ifdef HAVE_LIBLO
Chris@93 4664 #ifdef LIBLO_VERSION
Chris@285 4665 aboutText += tr("<br>With liblo Lite OSC library v%1 &copy; Steve Harris").arg(LIBLO_VERSION);
Chris@285 4666 #else // !LIBLO_VERSION
Chris@327 4667 aboutText += tr("<br>With liblo Lite OSC library &copy; Steve Harris");
Chris@285 4668 #endif // LIBLO_VERSION
Chris@285 4669
Chris@285 4670 if (m_oscQueue && m_oscQueue->isOK()) {
Chris@285 4671 aboutText += tr("</small><p><small>The OSC URL for this instance is: \"%1\"").arg(m_oscQueue->getOSCURL());
Chris@285 4672 }
Chris@285 4673
Chris@285 4674 aboutText += "</small></p>";
Chris@285 4675 #endif // HAVE_LIBLO
Chris@285 4676
Chris@285 4677 #ifndef BUILD_STATIC
Chris@285 4678 aboutText.replace(tr("With "), tr("Using "));
Chris@93 4679 #endif
Chris@0 4680
Chris@0 4681 aboutText +=
Chris@962 4682 "<p><small>Sonic Visualiser Copyright &copy; 2005&ndash;2015 Chris Cannam and "
Chris@285 4683 "Queen Mary, University of London.</small></p>"
Chris@285 4684 "<p><small>This program is free software; you can redistribute it and/or "
Chris@231 4685 "modify it under the terms of the GNU General Public License as "
Chris@231 4686 "published by the Free Software Foundation; either version 2 of the "
Chris@0 4687 "License, or (at your option) any later version.<br>See the file "
Chris@285 4688 "COPYING included with this distribution for more information.</small></p>";
Chris@0 4689
Chris@0 4690 QMessageBox::about(this, tr("About Sonic Visualiser"), aboutText);
Chris@0 4691 }
Chris@0 4692
Chris@162 4693 void
Chris@162 4694 MainWindow::keyReference()
Chris@162 4695 {
Chris@162 4696 m_keyReference->show();
Chris@162 4697 }
Chris@162 4698
Chris@333 4699 void
Chris@333 4700 MainWindow::newerVersionAvailable(QString version)
Chris@333 4701 {
Chris@334 4702 QSettings settings;
Chris@334 4703 settings.beginGroup("NewerVersionWarning");
Chris@334 4704 QString tag = QString("version-%1-available-show").arg(version);
Chris@334 4705 if (settings.value(tag, true).toBool()) {
Chris@334 4706 QString title(tr("Newer version available"));
Chris@663 4707 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 4708 QMessageBox::information(this, title, text);
Chris@334 4709 settings.setValue(tag, false);
Chris@334 4710 }
Chris@334 4711 settings.endGroup();
Chris@333 4712 }
Chris@333 4713
Chris@333 4714