annotate main/MainWindow.cpp @ 497:e583f57f1311

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