annotate main/MainWindow.cpp @ 1371:b75831cdf795 mp3-gapless

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