annotate main/MainWindow.cpp @ 849:4a7c62938a13 2.4beta2 sv_v2.4beta2

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