annotate main/MainWindow.cpp @ 1109:ed51b940662c spectrogram-minor-refactor

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