annotate main/MainWindow.cpp @ 1239:6928a84b918d spectrogram-minor-refactor

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