annotate main/MainWindow.cpp @ 1683:203372cdba61 scale-ticks

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