annotate main/MainWindow.cpp @ 1480:f1e1745acc3b 3.0-integration

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