annotate main/MainWindow.cpp @ 855:7c0534369ddd

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