annotate main/MainWindow.cpp @ 764:91d821455fa5 2.4beta1

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