annotate main/MainWindow.cpp @ 1145:52cbb3f6b9fa pluginscan

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