annotate main/MainWindow.cpp @ 942:abe5cfed6a88

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