annotate main/MainWindow.cpp @ 778:fdc445e736a0 2.4beta1

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