annotate main/MainWindow.cpp @ 1620:33d32b3190bf

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