annotate main/MainWindow.cpp @ 465:b3b9cc8f5466 integration_library

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