annotate main/MainWindow.cpp @ 490:bc04d651f8a4

Ensure all windows are closed when the main one is (thus exiting the application even if some other windows were still open)
author Chris Cannam
date Thu, 19 Jan 2012 15:36:59 +0000
parents a6cbec451508
children e583f57f1311
rev   line source
Chris@0 1 /* -*- c-basic-offset: 4 indent-tabs-mode: nil -*- vi:set ts=8 sts=4 sw=4: */
Chris@0 2
Chris@0 3 /*
Chris@0 4 Sonic Visualiser
Chris@0 5 An audio file viewer and annotation editor.
Chris@0 6 Centre for Digital Music, Queen Mary, University of London.
Chris@200 7 This file copyright 2006-2007 Chris Cannam and QMUL.
Chris@0 8
Chris@0 9 This program is free software; you can redistribute it and/or
Chris@0 10 modify it under the terms of the GNU General Public License as
Chris@0 11 published by the Free Software Foundation; either version 2 of the
Chris@0 12 License, or (at your option) any later version. See the file
Chris@0 13 COPYING included with this distribution for more information.
Chris@0 14 */
Chris@0 15
Chris@144 16 #include "../version.h"
Chris@0 17
Chris@0 18 #include "MainWindow.h"
Chris@0 19 #include "PreferencesDialog.h"
Chris@0 20
Chris@1 21 #include "view/Pane.h"
Chris@1 22 #include "view/PaneStack.h"
Chris@1 23 #include "data/model/WaveFileModel.h"
Chris@1 24 #include "data/model/SparseOneDimensionalModel.h"
Chris@415 25 #include "data/model/RangeSummarisableTimeValueModel.h"
Chris@185 26 #include "data/model/NoteModel.h"
Chris@189 27 #include "data/model/Labeller.h"
Chris@222 28 #include "data/osc/OSCQueue.h"
Chris@216 29 #include "framework/Document.h"
Chris@357 30 #include "framework/TransformUserConfigurator.h"
Chris@1 31 #include "view/ViewManager.h"
Chris@0 32 #include "base/Preferences.h"
Chris@423 33 #include "base/ResourceFinder.h"
Chris@0 34 #include "layer/WaveformLayer.h"
Chris@0 35 #include "layer/TimeRulerLayer.h"
Chris@0 36 #include "layer/TimeInstantLayer.h"
Chris@0 37 #include "layer/TimeValueLayer.h"
Chris@307 38 #include "layer/NoteLayer.h"
Chris@0 39 #include "layer/Colour3DPlotLayer.h"
Chris@95 40 #include "layer/SliceLayer.h"
Chris@95 41 #include "layer/SliceableLayer.h"
Chris@193 42 #include "layer/ImageLayer.h"
Chris@340 43 #include "layer/RegionLayer.h"
Chris@0 44 #include "widgets/Fader.h"
Chris@65 45 #include "view/Overview.h"
Chris@0 46 #include "widgets/PropertyBox.h"
Chris@0 47 #include "widgets/PropertyStack.h"
Chris@0 48 #include "widgets/AudioDial.h"
Chris@168 49 #include "widgets/IconLoader.h"
Chris@219 50 #include "widgets/LayerTreeDialog.h"
Chris@0 51 #include "widgets/ListInputDialog.h"
Chris@36 52 #include "widgets/SubdividingMenu.h"
Chris@90 53 #include "widgets/NotifyingPushButton.h"
Chris@162 54 #include "widgets/KeyReference.h"
Chris@273 55 #include "widgets/TransformFinder.h"
Chris@192 56 #include "widgets/LabelCounterInputDialog.h"
Chris@302 57 #include "widgets/ActivityLog.h"
Chris@0 58 #include "audioio/AudioCallbackPlaySource.h"
Chris@0 59 #include "audioio/AudioCallbackPlayTarget.h"
Chris@0 60 #include "audioio/AudioTargetFactory.h"
Chris@59 61 #include "audioio/PlaySpeedRangeMapper.h"
Chris@1 62 #include "data/fileio/DataFileReaderFactory.h"
Chris@180 63 #include "data/fileio/PlaylistFileReader.h"
Chris@1 64 #include "data/fileio/WavFileWriter.h"
Chris@1 65 #include "data/fileio/CSVFileWriter.h"
Chris@185 66 #include "data/fileio/MIDIFileWriter.h"
Chris@1 67 #include "data/fileio/BZipFileDevice.h"
Chris@198 68 #include "data/fileio/FileSource.h"
Chris@91 69 #include "data/fft/FFTDataServer.h"
Chris@304 70 #include "data/midi/MIDIInput.h"
Chris@1 71 #include "base/RecentFiles.h"
Chris@249 72 #include "transform/TransformFactory.h"
Chris@249 73 #include "transform/ModelTransformerFactory.h"
Chris@0 74 #include "base/PlayParameterRepository.h"
Chris@0 75 #include "base/XmlExportable.h"
Chris@248 76 #include "widgets/CommandHistory.h"
Chris@0 77 #include "base/Profiler.h"
Chris@0 78 #include "base/Clipboard.h"
Chris@165 79 #include "base/UnitDatabase.h"
Chris@248 80 #include "layer/ColourDatabase.h"
Chris@265 81 #include "widgets/ModelDataTableDialog.h"
Chris@289 82 #include "rdf/PluginRDFIndexer.h"
Chris@291 83 #include "rdf/RDFExporter.h"
Chris@276 84
Chris@333 85 #include "Surveyer.h"
Chris@334 86 #include "framework/VersionTester.h"
Chris@333 87
Chris@0 88 // For version information
Chris@280 89 #include <vamp/vamp.h>
Chris@280 90 #include <vamp-hostsdk/PluginBase.h>
Chris@0 91 #include "plugin/api/ladspa.h"
Chris@0 92 #include "plugin/api/dssi.h"
Chris@0 93
Chris@0 94 #include <QApplication>
Chris@0 95 #include <QMessageBox>
Chris@0 96 #include <QGridLayout>
Chris@0 97 #include <QLabel>
Chris@0 98 #include <QAction>
Chris@0 99 #include <QMenuBar>
Chris@0 100 #include <QToolBar>
Chris@0 101 #include <QInputDialog>
Chris@0 102 #include <QStatusBar>
Chris@0 103 #include <QTreeView>
Chris@0 104 #include <QFile>
Chris@88 105 #include <QFileInfo>
Chris@88 106 #include <QDir>
Chris@0 107 #include <QTextStream>
Chris@0 108 #include <QProcess>
Chris@7 109 #include <QShortcut>
Chris@5 110 #include <QSettings>
Chris@11 111 #include <QDateTime>
Chris@11 112 #include <QProcess>
Chris@16 113 #include <QCheckBox>
Chris@55 114 #include <QRegExp>
Chris@158 115 #include <QScrollArea>
Chris@425 116 #include <QDesktopServices>
Chris@483 117 #include <QDialogButtonBox>
Chris@426 118 #include <QFileSystemWatcher>
Chris@0 119
Chris@0 120 #include <iostream>
Chris@0 121 #include <cstdio>
Chris@0 122 #include <errno.h>
Chris@0 123
Chris@0 124 using std::cerr;
Chris@0 125 using std::endl;
Chris@0 126
Chris@33 127 using std::vector;
Chris@33 128 using std::map;
Chris@33 129 using std::set;
Chris@33 130
Chris@0 131
Chris@70 132 MainWindow::MainWindow(bool withAudioOutput, bool withOSCSupport) :
Chris@305 133 MainWindowBase(withAudioOutput, withOSCSupport, true),
Chris@65 134 m_overview(0),
Chris@0 135 m_mainMenusCreated(false),
Chris@0 136 m_paneMenu(0),
Chris@0 137 m_layerMenu(0),
Chris@34 138 m_transformsMenu(0),
Chris@155 139 m_playbackMenu(0),
Chris@0 140 m_existingLayersMenu(0),
Chris@95 141 m_sliceMenu(0),
Chris@34 142 m_recentFilesMenu(0),
Chris@211 143 m_recentTransformsMenu(0),
Chris@426 144 m_templatesMenu(0),
Chris@0 145 m_rightButtonMenu(0),
Chris@0 146 m_rightButtonLayerMenu(0),
Chris@211 147 m_rightButtonTransformsMenu(0),
Chris@155 148 m_rightButtonPlaybackMenu(0),
Chris@207 149 m_soloAction(0),
Chris@207 150 m_soloModified(false),
Chris@207 151 m_prevSolo(false),
Chris@490 152 m_exiting(false),
Chris@265 153 m_rwdStartAction(0),
Chris@323 154 m_rwdSimilarAction(0),
Chris@265 155 m_rwdAction(0),
Chris@155 156 m_ffwdAction(0),
Chris@323 157 m_ffwdSimilarAction(0),
Chris@265 158 m_ffwdEndAction(0),
Chris@265 159 m_playAction(0),
Chris@265 160 m_playSelectionAction(0),
Chris@265 161 m_playLoopAction(0),
Chris@239 162 m_playControlsSpacer(0),
Chris@239 163 m_playControlsWidth(0),
Chris@162 164 m_preferencesDialog(0),
Chris@219 165 m_layerTreeDialog(0),
Chris@302 166 m_activityLog(new ActivityLog()),
Chris@426 167 m_keyReference(new KeyReference()),
Chris@426 168 m_templateWatcher(0)
Chris@0 169 {
Chris@253 170 Profiler profiler("MainWindow::MainWindow");
Chris@253 171
Chris@0 172 setWindowTitle(tr("Sonic Visualiser"));
Chris@0 173
Chris@165 174 UnitDatabase *udb = UnitDatabase::getInstance();
Chris@165 175 udb->registerUnit("Hz");
Chris@165 176 udb->registerUnit("dB");
Chris@165 177 udb->registerUnit("s");
Chris@165 178
Chris@165 179 ColourDatabase *cdb = ColourDatabase::getInstance();
Chris@165 180 cdb->addColour(Qt::black, tr("Black"));
Chris@165 181 cdb->addColour(Qt::darkRed, tr("Red"));
Chris@165 182 cdb->addColour(Qt::darkBlue, tr("Blue"));
Chris@165 183 cdb->addColour(Qt::darkGreen, tr("Green"));
Chris@165 184 cdb->addColour(QColor(200, 50, 255), tr("Purple"));
Chris@165 185 cdb->addColour(QColor(255, 150, 50), tr("Orange"));
Chris@166 186 cdb->setUseDarkBackground(cdb->addColour(Qt::white, tr("White")), true);
Chris@166 187 cdb->setUseDarkBackground(cdb->addColour(Qt::red, tr("Bright Red")), true);
Chris@166 188 cdb->setUseDarkBackground(cdb->addColour(QColor(30, 150, 255), tr("Bright Blue")), true);
Chris@166 189 cdb->setUseDarkBackground(cdb->addColour(Qt::green, tr("Bright Green")), true);
Chris@166 190 cdb->setUseDarkBackground(cdb->addColour(QColor(225, 74, 255), tr("Bright Purple")), true);
Chris@166 191 cdb->setUseDarkBackground(cdb->addColour(QColor(255, 188, 80), tr("Bright Orange")), true);
Chris@0 192
Chris@0 193 QFrame *frame = new QFrame;
Chris@0 194 setCentralWidget(frame);
Chris@0 195
Chris@0 196 QGridLayout *layout = new QGridLayout;
Chris@180 197
Chris@205 198 m_descriptionLabel = new QLabel; //!!! hang on, this is declared in base class -- should be declared and initialised by same class
Chris@0 199
Chris@158 200 QScrollArea *scroll = new QScrollArea(frame);
Chris@158 201 scroll->setWidgetResizable(true);
Chris@158 202 scroll->setHorizontalScrollBarPolicy(Qt::ScrollBarAlwaysOff);
Chris@159 203 scroll->setFrameShape(QFrame::NoFrame);
Chris@159 204
Chris@159 205 scroll->setWidget(m_paneStack);
Chris@159 206
Chris@65 207 m_overview = new Overview(frame);
Chris@65 208 m_overview->setViewManager(m_viewManager);
Chris@65 209 m_overview->setFixedHeight(40);
Chris@144 210 #ifndef _WIN32
Chris@144 211 // For some reason, the contents of the overview never appear if we
Chris@144 212 // make this setting on Windows. I have no inclination at the moment
Chris@144 213 // to track down the reason why.
Chris@129 214 m_overview->setFrameStyle(QFrame::StyledPanel | QFrame::Sunken);
Chris@144 215 #endif
Chris@90 216 connect(m_overview, SIGNAL(contextHelpChanged(const QString &)),
Chris@116 217 this, SLOT(contextHelpChanged(const QString &)));
Chris@0 218
Chris@0 219 m_panLayer = new WaveformLayer;
Chris@0 220 m_panLayer->setChannelMode(WaveformLayer::MergeChannels);
Chris@0 221 m_panLayer->setAggressiveCacheing(true);
Chris@65 222 m_overview->addLayer(m_panLayer);
Chris@174 223
Chris@174 224 if (m_viewManager->getGlobalDarkBackground()) {
Chris@174 225 m_panLayer->setBaseColour
Chris@174 226 (ColourDatabase::getInstance()->getColourIndex(tr("Bright Green")));
Chris@174 227 } else {
Chris@174 228 m_panLayer->setBaseColour
Chris@174 229 (ColourDatabase::getInstance()->getColourIndex(tr("Green")));
Chris@200 230 }
Chris@0 231
Chris@0 232 m_fader = new Fader(frame, false);
Chris@90 233 connect(m_fader, SIGNAL(mouseEntered()), this, SLOT(mouseEnteredWidget()));
Chris@90 234 connect(m_fader, SIGNAL(mouseLeft()), this, SLOT(mouseLeftWidget()));
Chris@0 235
Chris@0 236 m_playSpeed = new AudioDial(frame);
Chris@12 237 m_playSpeed->setMinimum(0);
Chris@48 238 m_playSpeed->setMaximum(200);
Chris@25 239 m_playSpeed->setValue(100);
Chris@261 240 m_playSpeed->setFixedWidth(32);
Chris@261 241 m_playSpeed->setFixedHeight(32);
Chris@0 242 m_playSpeed->setNotchesVisible(true);
Chris@25 243 m_playSpeed->setPageStep(10);
Chris@60 244 m_playSpeed->setObjectName(tr("Playback Speedup"));
Chris@25 245 m_playSpeed->setDefaultValue(100);
Chris@59 246 m_playSpeed->setRangeMapper(new PlaySpeedRangeMapper(0, 200));
Chris@60 247 m_playSpeed->setShowToolTip(true);
Chris@0 248 connect(m_playSpeed, SIGNAL(valueChanged(int)),
Chris@0 249 this, SLOT(playSpeedChanged(int)));
Chris@90 250 connect(m_playSpeed, SIGNAL(mouseEntered()), this, SLOT(mouseEnteredWidget()));
Chris@90 251 connect(m_playSpeed, SIGNAL(mouseLeft()), this, SLOT(mouseLeftWidget()));
Chris@90 252
Chris@168 253 IconLoader il;
Chris@168 254
Chris@21 255 QSettings settings;
Chris@21 256 settings.beginGroup("MainWindow");
Chris@21 257 settings.endGroup();
Chris@21 258
Chris@239 259 m_playControlsSpacer = new QFrame;
Chris@239 260
Chris@129 261 layout->setSpacing(4);
Chris@159 262 layout->addWidget(scroll, 0, 0, 1, 5);
Chris@239 263 layout->addWidget(m_overview, 1, 1);
Chris@239 264 layout->addWidget(m_playControlsSpacer, 1, 2);
Chris@239 265 layout->addWidget(m_playSpeed, 1, 3);
Chris@239 266 layout->addWidget(m_fader, 1, 4);
Chris@239 267
Chris@239 268 m_playControlsWidth =
Chris@240 269 m_fader->width() + m_playSpeed->width() + layout->spacing() * 2;
Chris@239 270
Chris@239 271 layout->setColumnMinimumWidth(0, 14);
Chris@239 272 layout->setColumnStretch(0, 0);
Chris@239 273
Chris@239 274 m_paneStack->setPropertyStackMinWidth(m_playControlsWidth
Chris@239 275 + 2 + layout->spacing());
Chris@239 276 m_playControlsSpacer->setFixedSize(QSize(2, 2));
Chris@239 277
Chris@239 278 layout->setColumnStretch(1, 10);
Chris@239 279
Chris@239 280 connect(m_paneStack, SIGNAL(propertyStacksResized(int)),
Chris@239 281 this, SLOT(propertyStacksResized(int)));
Chris@16 282
Chris@0 283 frame->setLayout(layout);
Chris@0 284
Chris@0 285 setupMenus();
Chris@0 286 setupToolbars();
Chris@155 287 setupHelpMenu();
Chris@0 288
Chris@90 289 statusBar();
Chris@340 290 m_currentLabel = new QLabel;
Chris@340 291 statusBar()->addPermanentWidget(m_currentLabel);
Chris@0 292
Chris@302 293 connect(m_viewManager, SIGNAL(activity(QString)),
Chris@302 294 m_activityLog, SLOT(activityHappened(QString)));
Chris@302 295 connect(m_playSource, SIGNAL(activity(QString)),
Chris@302 296 m_activityLog, SLOT(activityHappened(QString)));
Chris@302 297 connect(CommandHistory::getInstance(), SIGNAL(activity(QString)),
Chris@302 298 m_activityLog, SLOT(activityHappened(QString)));
Chris@310 299 connect(this, SIGNAL(activity(QString)),
Chris@310 300 m_activityLog, SLOT(activityHappened(QString)));
Chris@303 301 connect(this, SIGNAL(replacedDocument()), this, SLOT(documentReplaced()));
Chris@306 302 m_activityLog->hide();
Chris@303 303
Chris@303 304 newSession();
Chris@303 305
Chris@304 306 connect(m_midiInput, SIGNAL(eventsAvailable()),
Chris@304 307 this, SLOT(midiEventsAvailable()));
Chris@281 308
Chris@283 309 TransformFactory::getInstance()->startPopulationThread();
Chris@334 310
Chris@334 311 Surveyer *surveyer = new Surveyer(this);
Chris@334 312 VersionTester *vt = new VersionTester
Chris@334 313 ("sonicvisualiser.org", "/latest-version.txt", SV_VERSION);
Chris@334 314 connect(vt, SIGNAL(newerVersionAvailable(QString)),
Chris@334 315 this, SLOT(newerVersionAvailable(QString)));
Chris@0 316 }
Chris@0 317
Chris@0 318 MainWindow::~MainWindow()
Chris@0 319 {
Chris@438 320 // SVDEBUG << "MainWindow::~MainWindow" << endl;
Chris@162 321 delete m_keyReference;
Chris@163 322 delete m_preferencesDialog;
Chris@219 323 delete m_layerTreeDialog;
Chris@0 324 Profiles::getInstance()->dump();
Chris@438 325 // SVDEBUG << "MainWindow::~MainWindow finishing" << endl;
Chris@0 326 }
Chris@0 327
Chris@81 328 void
Chris@0 329 MainWindow::setupMenus()
Chris@0 330 {
Chris@0 331 if (!m_mainMenusCreated) {
Chris@0 332 m_rightButtonMenu = new QMenu();
Chris@104 333
Chris@104 334 // No -- we don't want tear-off enabled on the right-button
Chris@104 335 // menu. If it is enabled, then simply right-clicking and
Chris@104 336 // releasing will pop up the menu, activate the tear-off, and
Chris@104 337 // leave the torn-off menu window in front of the main window.
Chris@104 338 // That isn't desirable. I'm not sure it ever would be, in a
Chris@104 339 // context menu -- perhaps technically a Qt bug?
Chris@104 340 // m_rightButtonMenu->setTearOffEnabled(true);
Chris@0 341 }
Chris@0 342
Chris@211 343 if (m_rightButtonTransformsMenu) {
Chris@211 344 m_rightButtonTransformsMenu->clear();
Chris@34 345 } else {
Chris@211 346 m_rightButtonTransformsMenu = m_rightButtonMenu->addMenu(tr("&Transform"));
Chris@211 347 m_rightButtonTransformsMenu->setTearOffEnabled(true);
Chris@34 348 m_rightButtonMenu->addSeparator();
Chris@34 349 }
Chris@34 350
Chris@346 351 // This will be created (if not found) or cleared (if found) in
Chris@346 352 // setupPaneAndLayerMenus, but we want to ensure it's created
Chris@346 353 // sooner so it can go nearer the top of the right button menu
Chris@346 354 if (m_rightButtonLayerMenu) {
Chris@346 355 m_rightButtonLayerMenu->clear();
Chris@346 356 } else {
Chris@346 357 m_rightButtonLayerMenu = m_rightButtonMenu->addMenu(tr("&Layer"));
Chris@346 358 m_rightButtonLayerMenu->setTearOffEnabled(true);
Chris@346 359 m_rightButtonMenu->addSeparator();
Chris@346 360 }
Chris@346 361
Chris@0 362 if (!m_mainMenusCreated) {
Chris@0 363 CommandHistory::getInstance()->registerMenu(m_rightButtonMenu);
Chris@0 364 m_rightButtonMenu->addSeparator();
Chris@66 365 }
Chris@66 366
Chris@66 367 setupFileMenu();
Chris@66 368 setupEditMenu();
Chris@66 369 setupViewMenu();
Chris@66 370 setupPaneAndLayerMenus();
Chris@211 371 setupTransformsMenu();
Chris@66 372
Chris@66 373 m_mainMenusCreated = true;
Chris@66 374 }
Chris@66 375
Chris@66 376 void
Chris@66 377 MainWindow::setupFileMenu()
Chris@66 378 {
Chris@66 379 if (m_mainMenusCreated) return;
Chris@66 380
Chris@66 381 QMenu *menu = menuBar()->addMenu(tr("&File"));
Chris@97 382 menu->setTearOffEnabled(true);
Chris@66 383 QToolBar *toolbar = addToolBar(tr("File Toolbar"));
Chris@66 384
Chris@162 385 m_keyReference->setCategory(tr("File and Session Management"));
Chris@162 386
Chris@168 387 IconLoader il;
Chris@168 388
Chris@168 389 QIcon icon = il.load("filenew");
Chris@168 390 icon.addPixmap(il.loadPixmap("filenew-22"));
Chris@66 391 QAction *action = new QAction(icon, tr("&New Session"), this);
Chris@66 392 action->setShortcut(tr("Ctrl+N"));
Chris@90 393 action->setStatusTip(tr("Abandon the current Sonic Visualiser session and start a new one"));
Chris@66 394 connect(action, SIGNAL(triggered()), this, SLOT(newSession()));
Chris@162 395 m_keyReference->registerShortcut(action);
Chris@66 396 menu->addAction(action);
Chris@66 397 toolbar->addAction(action);
Chris@418 398 /*
Chris@168 399 icon = il.load("fileopensession");
Chris@66 400 action = new QAction(icon, tr("&Open Session..."), this);
Chris@66 401 action->setShortcut(tr("Ctrl+O"));
Chris@66 402 action->setStatusTip(tr("Open a previously saved Sonic Visualiser session file"));
Chris@66 403 connect(action, SIGNAL(triggered()), this, SLOT(openSession()));
Chris@162 404 m_keyReference->registerShortcut(action);
Chris@66 405 menu->addAction(action);
Chris@418 406 */
Chris@168 407 icon = il.load("fileopen");
Chris@168 408 icon.addPixmap(il.loadPixmap("fileopen-22"));
Chris@66 409 action = new QAction(icon, tr("&Open..."), this);
Chris@418 410 action->setShortcut(tr("Ctrl+O"));
Chris@66 411 action->setStatusTip(tr("Open a session file, audio file, or layer"));
Chris@66 412 connect(action, SIGNAL(triggered()), this, SLOT(openSomething()));
Chris@66 413 toolbar->addAction(action);
Chris@418 414 menu->addAction(action);
Chris@418 415
Chris@418 416 // We want this one to go on the toolbar now, if we add it at all,
Chris@418 417 // but on the menu later
Chris@418 418 QAction *iaction = new QAction(tr("&Import More Audio..."), this);
Chris@418 419 iaction->setShortcut(tr("Ctrl+I"));
Chris@418 420 iaction->setStatusTip(tr("Import an extra audio file into a new pane"));
Chris@418 421 connect(iaction, SIGNAL(triggered()), this, SLOT(importMoreAudio()));
Chris@418 422 connect(this, SIGNAL(canImportMoreAudio(bool)), iaction, SLOT(setEnabled(bool)));
Chris@418 423 m_keyReference->registerShortcut(iaction);
Chris@418 424
Chris@418 425 action = new QAction(tr("Open Lo&cation..."), this);
Chris@418 426 action->setShortcut(tr("Ctrl+Shift+O"));
Chris@418 427 action->setStatusTip(tr("Open or import a file from a remote URL"));
Chris@418 428 connect(action, SIGNAL(triggered()), this, SLOT(openLocation()));
Chris@418 429 m_keyReference->registerShortcut(action);
Chris@418 430 menu->addAction(action);
Chris@418 431
Chris@420 432 m_recentFilesMenu = menu->addMenu(tr("Open &Recent"));
Chris@418 433 m_recentFilesMenu->setTearOffEnabled(true);
Chris@418 434 setupRecentFilesMenu();
Chris@418 435 connect(&m_recentFiles, SIGNAL(recentChanged()),
Chris@418 436 this, SLOT(setupRecentFilesMenu()));
Chris@418 437
Chris@418 438 menu->addSeparator();
Chris@415 439
Chris@168 440 icon = il.load("filesave");
Chris@168 441 icon.addPixmap(il.loadPixmap("filesave-22"));
Chris@66 442 action = new QAction(icon, tr("&Save Session"), this);
Chris@66 443 action->setShortcut(tr("Ctrl+S"));
Chris@66 444 action->setStatusTip(tr("Save the current session into a Sonic Visualiser session file"));
Chris@66 445 connect(action, SIGNAL(triggered()), this, SLOT(saveSession()));
Chris@66 446 connect(this, SIGNAL(canSave(bool)), action, SLOT(setEnabled(bool)));
Chris@162 447 m_keyReference->registerShortcut(action);
Chris@66 448 menu->addAction(action);
Chris@66 449 toolbar->addAction(action);
Chris@0 450
Chris@168 451 icon = il.load("filesaveas");
Chris@168 452 icon.addPixmap(il.loadPixmap("filesaveas-22"));
Chris@66 453 action = new QAction(icon, tr("Save Session &As..."), this);
Chris@336 454 action->setShortcut(tr("Ctrl+Shift+S"));
Chris@66 455 action->setStatusTip(tr("Save the current session into a new Sonic Visualiser session file"));
Chris@66 456 connect(action, SIGNAL(triggered()), this, SLOT(saveSessionAs()));
Chris@66 457 menu->addAction(action);
Chris@66 458 toolbar->addAction(action);
Chris@66 459
Chris@66 460 menu->addSeparator();
Chris@66 461
Chris@418 462 /*
Chris@168 463 icon = il.load("fileopenaudio");
Chris@138 464 action = new QAction(icon, tr("&Import Audio File..."), this);
Chris@66 465 action->setShortcut(tr("Ctrl+I"));
Chris@66 466 action->setStatusTip(tr("Import an existing audio file"));
Chris@66 467 connect(action, SIGNAL(triggered()), this, SLOT(importAudio()));
Chris@162 468 m_keyReference->registerShortcut(action);
Chris@66 469 menu->addAction(action);
Chris@418 470 */
Chris@418 471
Chris@418 472 // the Import action we made earlier
Chris@418 473 menu->addAction(iaction);
Chris@66 474
Chris@66 475 action = new QAction(tr("&Export Audio File..."), this);
Chris@66 476 action->setStatusTip(tr("Export selection as an audio file"));
Chris@66 477 connect(action, SIGNAL(triggered()), this, SLOT(exportAudio()));
Chris@66 478 connect(this, SIGNAL(canExportAudio(bool)), action, SLOT(setEnabled(bool)));
Chris@66 479 menu->addAction(action);
Chris@66 480
Chris@66 481 menu->addSeparator();
Chris@66 482
Chris@66 483 action = new QAction(tr("Import Annotation &Layer..."), this);
Chris@66 484 action->setShortcut(tr("Ctrl+L"));
Chris@66 485 action->setStatusTip(tr("Import layer data from an existing file"));
Chris@66 486 connect(action, SIGNAL(triggered()), this, SLOT(importLayer()));
Chris@66 487 connect(this, SIGNAL(canImportLayer(bool)), action, SLOT(setEnabled(bool)));
Chris@162 488 m_keyReference->registerShortcut(action);
Chris@66 489 menu->addAction(action);
Chris@66 490
Chris@66 491 action = new QAction(tr("Export Annotation Layer..."), this);
Chris@66 492 action->setStatusTip(tr("Export layer data to a file"));
Chris@66 493 connect(action, SIGNAL(triggered()), this, SLOT(exportLayer()));
Chris@66 494 connect(this, SIGNAL(canExportLayer(bool)), action, SLOT(setEnabled(bool)));
Chris@66 495 menu->addAction(action);
Chris@66 496
Chris@66 497 menu->addSeparator();
Chris@86 498
Chris@121 499 action = new QAction(tr("Export Image File..."), this);
Chris@121 500 action->setStatusTip(tr("Export a single pane to an image file"));
Chris@121 501 connect(action, SIGNAL(triggered()), this, SLOT(exportImage()));
Chris@121 502 connect(this, SIGNAL(canExportImage(bool)), action, SLOT(setEnabled(bool)));
Chris@121 503 menu->addAction(action);
Chris@121 504
Chris@121 505 menu->addSeparator();
Chris@121 506
Chris@435 507 QString templatesMenuLabel = tr("Apply Session Template");
Chris@425 508 m_templatesMenu = menu->addMenu(templatesMenuLabel);
Chris@425 509 m_templatesMenu->setTearOffEnabled(true);
Chris@436 510 // We need to have a main model for this option to be useful:
Chris@436 511 // canExportAudio captures that
Chris@436 512 connect(this, SIGNAL(canExportAudio(bool)), m_templatesMenu, SLOT(setEnabled(bool)));
Chris@436 513
Chris@436 514 // Set up the menu in a moment, after m_manageTemplatesAction constructed
Chris@435 515
Chris@435 516 action = new QAction(tr("Export Session as Template..."), this);
Chris@435 517 connect(action, SIGNAL(triggered()), this, SLOT(saveSessionAsTemplate()));
Chris@436 518 // We need to have something in the session for this to be useful:
Chris@436 519 // canDeleteCurrentLayer captures that
Chris@436 520 connect(this, SIGNAL(canExportAudio(bool)), action, SLOT(setEnabled(bool)));
Chris@435 521 menu->addAction(action);
Chris@435 522
Chris@436 523 m_manageTemplatesAction = new QAction(tr("Manage Exported Templates"), this);
Chris@436 524 connect(m_manageTemplatesAction, SIGNAL(triggered()), this, SLOT(manageSavedTemplates()));
Chris@436 525 menu->addAction(m_manageTemplatesAction);
Chris@436 526
Chris@436 527 setupTemplatesMenu();
Chris@425 528
Chris@66 529 action = new QAction(tr("&Preferences..."), this);
Chris@66 530 action->setStatusTip(tr("Adjust the application preferences"));
Chris@66 531 connect(action, SIGNAL(triggered()), this, SLOT(preferences()));
Chris@66 532 menu->addAction(action);
Chris@0 533
Chris@66 534 menu->addSeparator();
Chris@168 535 action = new QAction(il.load("exit"),
Chris@66 536 tr("&Quit"), this);
Chris@66 537 action->setShortcut(tr("Ctrl+Q"));
Chris@90 538 action->setStatusTip(tr("Exit Sonic Visualiser"));
Chris@66 539 connect(action, SIGNAL(triggered()), this, SLOT(close()));
Chris@162 540 m_keyReference->registerShortcut(action);
Chris@66 541 menu->addAction(action);
Chris@66 542 }
Chris@66 543
Chris@66 544 void
Chris@66 545 MainWindow::setupEditMenu()
Chris@66 546 {
Chris@66 547 if (m_mainMenusCreated) return;
Chris@66 548
Chris@66 549 QMenu *menu = menuBar()->addMenu(tr("&Edit"));
Chris@97 550 menu->setTearOffEnabled(true);
Chris@66 551 CommandHistory::getInstance()->registerMenu(menu);
Chris@66 552
Chris@162 553 m_keyReference->setCategory(tr("Editing"));
Chris@162 554
Chris@66 555 menu->addSeparator();
Chris@66 556
Chris@168 557 IconLoader il;
Chris@168 558
Chris@168 559 QAction *action = new QAction(il.load("editcut"),
Chris@66 560 tr("Cu&t"), this);
Chris@66 561 action->setShortcut(tr("Ctrl+X"));
Chris@90 562 action->setStatusTip(tr("Cut the selection from the current layer to the clipboard"));
Chris@66 563 connect(action, SIGNAL(triggered()), this, SLOT(cut()));
Chris@66 564 connect(this, SIGNAL(canEditSelection(bool)), action, SLOT(setEnabled(bool)));
Chris@162 565 m_keyReference->registerShortcut(action);
Chris@66 566 menu->addAction(action);
Chris@66 567 m_rightButtonMenu->addAction(action);
Chris@66 568
Chris@168 569 action = new QAction(il.load("editcopy"),
Chris@66 570 tr("&Copy"), this);
Chris@66 571 action->setShortcut(tr("Ctrl+C"));
Chris@90 572 action->setStatusTip(tr("Copy the selection from the current layer to the clipboard"));
Chris@66 573 connect(action, SIGNAL(triggered()), this, SLOT(copy()));
Chris@66 574 connect(this, SIGNAL(canEditSelection(bool)), action, SLOT(setEnabled(bool)));
Chris@162 575 m_keyReference->registerShortcut(action);
Chris@66 576 menu->addAction(action);
Chris@66 577 m_rightButtonMenu->addAction(action);
Chris@66 578
Chris@168 579 action = new QAction(il.load("editpaste"),
Chris@66 580 tr("&Paste"), this);
Chris@66 581 action->setShortcut(tr("Ctrl+V"));
Chris@90 582 action->setStatusTip(tr("Paste from the clipboard to the current layer"));
Chris@66 583 connect(action, SIGNAL(triggered()), this, SLOT(paste()));
Chris@66 584 connect(this, SIGNAL(canPaste(bool)), action, SLOT(setEnabled(bool)));
Chris@162 585 m_keyReference->registerShortcut(action);
Chris@66 586 menu->addAction(action);
Chris@66 587 m_rightButtonMenu->addAction(action);
Chris@66 588
Chris@395 589 action = new QAction(tr("Paste at Playback Position"), this);
Chris@395 590 action->setShortcut(tr("Ctrl+Shift+V"));
Chris@395 591 action->setStatusTip(tr("Paste from the clipboard to the current layer, placing the first item at the playback position"));
Chris@395 592 connect(action, SIGNAL(triggered()), this, SLOT(pasteAtPlaybackPosition()));
Chris@395 593 connect(this, SIGNAL(canPaste(bool)), action, SLOT(setEnabled(bool)));
Chris@395 594 m_keyReference->registerShortcut(action);
Chris@395 595 menu->addAction(action);
Chris@395 596 m_rightButtonMenu->addAction(action);
Chris@395 597
Chris@164 598 m_deleteSelectedAction = new QAction(tr("&Delete Selected Items"), this);
Chris@164 599 m_deleteSelectedAction->setShortcut(tr("Del"));
Chris@164 600 m_deleteSelectedAction->setStatusTip(tr("Delete items in current selection from the current layer"));
Chris@164 601 connect(m_deleteSelectedAction, SIGNAL(triggered()), this, SLOT(deleteSelected()));
Chris@164 602 connect(this, SIGNAL(canDeleteSelection(bool)), m_deleteSelectedAction, SLOT(setEnabled(bool)));
Chris@164 603 m_keyReference->registerShortcut(m_deleteSelectedAction);
Chris@164 604 menu->addAction(m_deleteSelectedAction);
Chris@164 605 m_rightButtonMenu->addAction(m_deleteSelectedAction);
Chris@66 606
Chris@66 607 menu->addSeparator();
Chris@66 608 m_rightButtonMenu->addSeparator();
Chris@162 609
Chris@162 610 m_keyReference->setCategory(tr("Selection"));
Chris@162 611
Chris@66 612 action = new QAction(tr("Select &All"), this);
Chris@66 613 action->setShortcut(tr("Ctrl+A"));
Chris@90 614 action->setStatusTip(tr("Select the whole duration of the current session"));
Chris@66 615 connect(action, SIGNAL(triggered()), this, SLOT(selectAll()));
Chris@66 616 connect(this, SIGNAL(canSelect(bool)), action, SLOT(setEnabled(bool)));
Chris@162 617 m_keyReference->registerShortcut(action);
Chris@66 618 menu->addAction(action);
Chris@66 619 m_rightButtonMenu->addAction(action);
Chris@0 620
Chris@66 621 action = new QAction(tr("Select &Visible Range"), this);
Chris@66 622 action->setShortcut(tr("Ctrl+Shift+A"));
Chris@90 623 action->setStatusTip(tr("Select the time range corresponding to the current window width"));
Chris@66 624 connect(action, SIGNAL(triggered()), this, SLOT(selectVisible()));
Chris@66 625 connect(this, SIGNAL(canSelect(bool)), action, SLOT(setEnabled(bool)));
Chris@162 626 m_keyReference->registerShortcut(action);
Chris@66 627 menu->addAction(action);
Chris@0 628
Chris@66 629 action = new QAction(tr("Select to &Start"), this);
Chris@66 630 action->setShortcut(tr("Shift+Left"));
Chris@90 631 action->setStatusTip(tr("Select from the start of the session to the current playback position"));
Chris@66 632 connect(action, SIGNAL(triggered()), this, SLOT(selectToStart()));
Chris@66 633 connect(this, SIGNAL(canSelect(bool)), action, SLOT(setEnabled(bool)));
Chris@162 634 m_keyReference->registerShortcut(action);
Chris@66 635 menu->addAction(action);
Chris@0 636
Chris@66 637 action = new QAction(tr("Select to &End"), this);
Chris@66 638 action->setShortcut(tr("Shift+Right"));
Chris@90 639 action->setStatusTip(tr("Select from the current playback position to the end of the session"));
Chris@66 640 connect(action, SIGNAL(triggered()), this, SLOT(selectToEnd()));
Chris@66 641 connect(this, SIGNAL(canSelect(bool)), action, SLOT(setEnabled(bool)));
Chris@162 642 m_keyReference->registerShortcut(action);
Chris@66 643 menu->addAction(action);
Chris@66 644
Chris@66 645 action = new QAction(tr("C&lear Selection"), this);
Chris@66 646 action->setShortcut(tr("Esc"));
Chris@90 647 action->setStatusTip(tr("Clear the selection"));
Chris@66 648 connect(action, SIGNAL(triggered()), this, SLOT(clearSelection()));
Chris@66 649 connect(this, SIGNAL(canClearSelection(bool)), action, SLOT(setEnabled(bool)));
Chris@162 650 m_keyReference->registerShortcut(action);
Chris@66 651 menu->addAction(action);
Chris@66 652 m_rightButtonMenu->addAction(action);
Chris@66 653
Chris@66 654 menu->addSeparator();
Chris@66 655
Chris@162 656 m_keyReference->setCategory(tr("Tapping Time Instants"));
Chris@162 657
Chris@66 658 action = new QAction(tr("&Insert Instant at Playback Position"), this);
Chris@66 659 action->setShortcut(tr("Enter"));
Chris@90 660 action->setStatusTip(tr("Insert a new time instant at the current playback position, in a new layer if necessary"));
Chris@66 661 connect(action, SIGNAL(triggered()), this, SLOT(insertInstant()));
Chris@66 662 connect(this, SIGNAL(canInsertInstant(bool)), action, SLOT(setEnabled(bool)));
Chris@162 663 m_keyReference->registerShortcut(action);
Chris@66 664 menu->addAction(action);
Chris@66 665
Chris@162 666 // Laptop shortcut (no keypad Enter key)
Chris@162 667 QString shortcut(tr(";"));
Chris@162 668 connect(new QShortcut(shortcut, this), SIGNAL(activated()),
Chris@162 669 this, SLOT(insertInstant()));
Chris@162 670 m_keyReference->registerAlternativeShortcut(action, shortcut);
Chris@162 671
Chris@81 672 action = new QAction(tr("Insert Instants at Selection &Boundaries"), this);
Chris@81 673 action->setShortcut(tr("Shift+Enter"));
Chris@163 674 action->setStatusTip(tr("Insert new time instants at the start and end of the current selected regions, in a new layer if necessary"));
Chris@81 675 connect(action, SIGNAL(triggered()), this, SLOT(insertInstantsAtBoundaries()));
Chris@81 676 connect(this, SIGNAL(canInsertInstantsAtBoundaries(bool)), action, SLOT(setEnabled(bool)));
Chris@162 677 m_keyReference->registerShortcut(action);
Chris@81 678 menu->addAction(action);
Chris@189 679
Chris@338 680 action = new QAction(tr("Insert Item at Selection"), this);
Chris@338 681 action->setShortcut(tr("Ctrl+Shift+Enter"));
Chris@338 682 action->setStatusTip(tr("Insert a new note or region item corresponding to the current selection"));
Chris@338 683 connect(action, SIGNAL(triggered()), this, SLOT(insertItemAtSelection()));
Chris@338 684 connect(this, SIGNAL(canInsertItemAtSelection(bool)), action, SLOT(setEnabled(bool)));
Chris@338 685 m_keyReference->registerShortcut(action);
Chris@338 686 menu->addAction(action);
Chris@338 687
Chris@338 688 menu->addSeparator();
Chris@338 689
Chris@190 690 QMenu *numberingMenu = menu->addMenu(tr("Number New Instants with"));
Chris@225 691 numberingMenu->setTearOffEnabled(true);
Chris@189 692 QActionGroup *numberingGroup = new QActionGroup(this);
Chris@189 693
Chris@189 694 Labeller::TypeNameMap types = m_labeller->getTypeNames();
Chris@189 695 for (Labeller::TypeNameMap::iterator i = types.begin(); i != types.end(); ++i) {
Chris@190 696
Chris@190 697 if (i->first == Labeller::ValueFromLabel ||
Chris@190 698 i->first == Labeller::ValueFromExistingNeighbour) continue;
Chris@190 699
Chris@189 700 action = new QAction(i->second, this);
Chris@189 701 connect(action, SIGNAL(triggered()), this, SLOT(setInstantsNumbering()));
Chris@189 702 action->setCheckable(true);
Chris@189 703 action->setChecked(m_labeller->getType() == i->first);
Chris@189 704 numberingGroup->addAction(action);
Chris@189 705 numberingMenu->addAction(action);
Chris@189 706 m_numberingActions[action] = (int)i->first;
Chris@190 707
Chris@190 708 if (i->first == Labeller::ValueFromTwoLevelCounter) {
Chris@192 709
Chris@190 710 QMenu *cycleMenu = numberingMenu->addMenu(tr("Cycle size"));
Chris@190 711 QActionGroup *cycleGroup = new QActionGroup(this);
Chris@190 712
Chris@229 713 int cycles[] = { 2, 3, 4, 5, 6, 7, 8, 9, 10, 12, 16 };
Chris@200 714 for (int i = 0; i < int(sizeof(cycles)/sizeof(cycles[0])); ++i) {
Chris@190 715 action = new QAction(QString("%1").arg(cycles[i]), this);
Chris@190 716 connect(action, SIGNAL(triggered()), this, SLOT(setInstantsCounterCycle()));
Chris@190 717 action->setCheckable(true);
Chris@190 718 action->setChecked(cycles[i] == m_labeller->getCounterCycleSize());
Chris@190 719 cycleGroup->addAction(action);
Chris@190 720 cycleMenu->addAction(action);
Chris@190 721 }
Chris@190 722 }
Chris@190 723
Chris@190 724 if (i->first == Labeller::ValueNone ||
Chris@190 725 i->first == Labeller::ValueFromTwoLevelCounter ||
Chris@190 726 i->first == Labeller::ValueFromRealTime) {
Chris@190 727 numberingMenu->addSeparator();
Chris@190 728 }
Chris@189 729 }
Chris@189 730
Chris@241 731 action = new QAction(tr("Set Numbering Counters..."), this);
Chris@241 732 action->setStatusTip(tr("Set the counters used for counter-based labelling"));
Chris@241 733 connect(action, SIGNAL(triggered()), this, SLOT(resetInstantsCounters()));
Chris@241 734 menu->addAction(action);
Chris@241 735
Chris@241 736 action = new QAction(tr("Renumber Selected Instants"), this);
Chris@241 737 action->setStatusTip(tr("Renumber the selected instants using the current labelling scheme"));
Chris@189 738 connect(action, SIGNAL(triggered()), this, SLOT(renumberInstants()));
Chris@189 739 connect(this, SIGNAL(canRenumberInstants(bool)), action, SLOT(setEnabled(bool)));
Chris@189 740 // m_keyReference->registerShortcut(action);
Chris@189 741 menu->addAction(action);
Chris@66 742 }
Chris@66 743
Chris@66 744 void
Chris@66 745 MainWindow::setupViewMenu()
Chris@66 746 {
Chris@66 747 if (m_mainMenusCreated) return;
Chris@66 748
Chris@168 749 IconLoader il;
Chris@168 750
Chris@90 751 QAction *action = 0;
Chris@90 752
Chris@162 753 m_keyReference->setCategory(tr("Panning and Navigation"));
Chris@162 754
Chris@66 755 QMenu *menu = menuBar()->addMenu(tr("&View"));
Chris@97 756 menu->setTearOffEnabled(true);
Chris@66 757 action = new QAction(tr("Scroll &Left"), this);
Chris@66 758 action->setShortcut(tr("Left"));
Chris@66 759 action->setStatusTip(tr("Scroll the current pane to the left"));
Chris@66 760 connect(action, SIGNAL(triggered()), this, SLOT(scrollLeft()));
Chris@66 761 connect(this, SIGNAL(canScroll(bool)), action, SLOT(setEnabled(bool)));
Chris@162 762 m_keyReference->registerShortcut(action);
Chris@66 763 menu->addAction(action);
Chris@0 764
Chris@66 765 action = new QAction(tr("Scroll &Right"), this);
Chris@66 766 action->setShortcut(tr("Right"));
Chris@66 767 action->setStatusTip(tr("Scroll the current pane to the right"));
Chris@66 768 connect(action, SIGNAL(triggered()), this, SLOT(scrollRight()));
Chris@66 769 connect(this, SIGNAL(canScroll(bool)), action, SLOT(setEnabled(bool)));
Chris@162 770 m_keyReference->registerShortcut(action);
Chris@66 771 menu->addAction(action);
Chris@0 772
Chris@90 773 action = new QAction(tr("&Jump Left"), this);
Chris@66 774 action->setShortcut(tr("Ctrl+Left"));
Chris@66 775 action->setStatusTip(tr("Scroll the current pane a big step to the left"));
Chris@66 776 connect(action, SIGNAL(triggered()), this, SLOT(jumpLeft()));
Chris@66 777 connect(this, SIGNAL(canScroll(bool)), action, SLOT(setEnabled(bool)));
Chris@162 778 m_keyReference->registerShortcut(action);
Chris@66 779 menu->addAction(action);
Chris@0 780
Chris@90 781 action = new QAction(tr("J&ump Right"), this);
Chris@66 782 action->setShortcut(tr("Ctrl+Right"));
Chris@66 783 action->setStatusTip(tr("Scroll the current pane a big step to the right"));
Chris@66 784 connect(action, SIGNAL(triggered()), this, SLOT(jumpRight()));
Chris@66 785 connect(this, SIGNAL(canScroll(bool)), action, SLOT(setEnabled(bool)));
Chris@162 786 m_keyReference->registerShortcut(action);
Chris@66 787 menu->addAction(action);
Chris@66 788
Chris@308 789 action = new QAction(tr("Peek Left"), this);
Chris@308 790 action->setShortcut(tr("Alt+Left"));
Chris@308 791 action->setStatusTip(tr("Scroll the current pane to the left without moving the playback cursor or other panes"));
Chris@308 792 connect(action, SIGNAL(triggered()), this, SLOT(peekLeft()));
Chris@308 793 connect(this, SIGNAL(canScroll(bool)), action, SLOT(setEnabled(bool)));
Chris@308 794 m_keyReference->registerShortcut(action);
Chris@308 795 menu->addAction(action);
Chris@308 796
Chris@308 797 action = new QAction(tr("Peek Right"), this);
Chris@308 798 action->setShortcut(tr("Alt+Right"));
Chris@308 799 action->setStatusTip(tr("Scroll the current pane to the right without moving the playback cursor or other panes"));
Chris@308 800 connect(action, SIGNAL(triggered()), this, SLOT(peekRight()));
Chris@308 801 connect(this, SIGNAL(canScroll(bool)), action, SLOT(setEnabled(bool)));
Chris@308 802 m_keyReference->registerShortcut(action);
Chris@308 803 menu->addAction(action);
Chris@308 804
Chris@66 805 menu->addSeparator();
Chris@66 806
Chris@162 807 m_keyReference->setCategory(tr("Zoom"));
Chris@162 808
Chris@168 809 action = new QAction(il.load("zoom-in"),
Chris@66 810 tr("Zoom &In"), this);
Chris@66 811 action->setShortcut(tr("Up"));
Chris@66 812 action->setStatusTip(tr("Increase the zoom level"));
Chris@66 813 connect(action, SIGNAL(triggered()), this, SLOT(zoomIn()));
Chris@66 814 connect(this, SIGNAL(canZoom(bool)), action, SLOT(setEnabled(bool)));
Chris@162 815 m_keyReference->registerShortcut(action);
Chris@66 816 menu->addAction(action);
Chris@0 817
Chris@168 818 action = new QAction(il.load("zoom-out"),
Chris@66 819 tr("Zoom &Out"), this);
Chris@66 820 action->setShortcut(tr("Down"));
Chris@66 821 action->setStatusTip(tr("Decrease the zoom level"));
Chris@66 822 connect(action, SIGNAL(triggered()), this, SLOT(zoomOut()));
Chris@66 823 connect(this, SIGNAL(canZoom(bool)), action, SLOT(setEnabled(bool)));
Chris@162 824 m_keyReference->registerShortcut(action);
Chris@66 825 menu->addAction(action);
Chris@0 826
Chris@66 827 action = new QAction(tr("Restore &Default Zoom"), this);
Chris@90 828 action->setStatusTip(tr("Restore the zoom level to the default"));
Chris@66 829 connect(action, SIGNAL(triggered()), this, SLOT(zoomDefault()));
Chris@66 830 connect(this, SIGNAL(canZoom(bool)), action, SLOT(setEnabled(bool)));
Chris@66 831 menu->addAction(action);
Chris@66 832
Chris@168 833 action = new QAction(il.load("zoom-fit"),
Chris@138 834 tr("Zoom to &Fit"), this);
Chris@155 835 action->setShortcut(tr("F"));
Chris@66 836 action->setStatusTip(tr("Zoom to show the whole file"));
Chris@66 837 connect(action, SIGNAL(triggered()), this, SLOT(zoomToFit()));
Chris@66 838 connect(this, SIGNAL(canZoom(bool)), action, SLOT(setEnabled(bool)));
Chris@162 839 m_keyReference->registerShortcut(action);
Chris@66 840 menu->addAction(action);
Chris@90 841
Chris@90 842 menu->addSeparator();
Chris@90 843
Chris@162 844 m_keyReference->setCategory(tr("Display Features"));
Chris@162 845
Chris@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
Chris@0 2260 void
Chris@0 2261 MainWindow::exportAudio()
Chris@0 2262 {
Chris@0 2263 if (!getMainModel()) return;
Chris@0 2264
Chris@320 2265 RangeSummarisableTimeValueModel *model = getMainModel();
Chris@320 2266 std::set<RangeSummarisableTimeValueModel *> otherModels;
Chris@320 2267 RangeSummarisableTimeValueModel *current = model;
Chris@320 2268 if (m_paneStack) {
Chris@320 2269 for (int i = 0; i < m_paneStack->getPaneCount(); ++i) {
Chris@320 2270 Pane *pane = m_paneStack->getPane(i);
Chris@320 2271 if (!pane) continue;
Chris@320 2272 for (int j = 0; j < pane->getLayerCount(); ++j) {
Chris@320 2273 Layer *layer = pane->getLayer(j);
Chris@320 2274 if (!layer) continue;
Chris@432 2275 cerr << "layer = " << layer->objectName() << endl;
Chris@320 2276 Model *m = layer->getModel();
Chris@320 2277 RangeSummarisableTimeValueModel *wm =
Chris@320 2278 dynamic_cast<RangeSummarisableTimeValueModel *>(m);
Chris@320 2279 if (wm) {
Chris@432 2280 cerr << "found: " << wm->objectName() << endl;
Chris@320 2281 otherModels.insert(wm);
Chris@320 2282 if (pane == m_paneStack->getCurrentPane()) {
Chris@320 2283 current = wm;
Chris@320 2284 }
Chris@320 2285 }
Chris@320 2286 }
Chris@320 2287 }
Chris@320 2288 }
Chris@320 2289 if (!otherModels.empty()) {
Chris@320 2290 std::map<QString, RangeSummarisableTimeValueModel *> m;
Chris@320 2291 m[tr("1. %2").arg(model->objectName())] = model;
Chris@320 2292 int n = 2;
Chris@320 2293 int c = 0;
Chris@320 2294 for (std::set<RangeSummarisableTimeValueModel *>::const_iterator i
Chris@320 2295 = otherModels.begin();
Chris@320 2296 i != otherModels.end(); ++i) {
Chris@320 2297 if (*i == model) continue;
Chris@320 2298 m[tr("%1. %2").arg(n).arg((*i)->objectName())] = *i;
Chris@320 2299 ++n;
Chris@320 2300 if (*i == current) c = n-1;
Chris@320 2301 }
Chris@320 2302 QStringList items;
Chris@320 2303 for (std::map<QString, RangeSummarisableTimeValueModel *>
Chris@320 2304 ::const_iterator i = m.begin();
Chris@320 2305 i != m.end(); ++i) {
Chris@320 2306 items << i->first;
Chris@320 2307 }
Chris@325 2308 if (items.size() > 1) {
Chris@325 2309 bool ok = false;
Chris@325 2310 QString item = QInputDialog::getItem
Chris@325 2311 (this, tr("Select audio file to export"),
Chris@325 2312 tr("Which audio file do you want to export from?"),
Chris@325 2313 items, c, false, &ok);
Chris@325 2314 if (!ok || item.isEmpty()) return;
Chris@325 2315 if (m.find(item) == m.end()) {
Chris@325 2316 cerr << "WARNING: Model " << item.toStdString()
Chris@325 2317 << " not found in list!" << endl;
Chris@325 2318 } else {
Chris@325 2319 model = m[item];
Chris@325 2320 }
Chris@320 2321 }
Chris@320 2322 }
Chris@320 2323
Chris@88 2324 QString path = getSaveFileName(FileFinder::AudioFile);
Chris@0 2325 if (path == "") return;
Chris@0 2326
Chris@0 2327 bool ok = false;
Chris@0 2328 QString error;
Chris@0 2329
Chris@0 2330 MultiSelection ms = m_viewManager->getSelection();
Chris@0 2331 MultiSelection::SelectionList selections = m_viewManager->getSelections();
Chris@0 2332
Chris@0 2333 bool multiple = false;
Chris@0 2334
Chris@38 2335 MultiSelection *selectionToWrite = 0;
Chris@38 2336
Chris@38 2337 if (selections.size() == 1) {
Chris@0 2338
Chris@0 2339 QStringList items;
Chris@0 2340 items << tr("Export the selected region only")
Chris@0 2341 << tr("Export the whole audio file");
Chris@0 2342
Chris@0 2343 bool ok = false;
Chris@0 2344 QString item = ListInputDialog::getItem
Chris@0 2345 (this, tr("Select region to export"),
Chris@0 2346 tr("Which region from the original audio file do you want to export?"),
Chris@0 2347 items, 0, &ok);
Chris@0 2348
Chris@0 2349 if (!ok || item.isEmpty()) return;
Chris@0 2350
Chris@38 2351 if (item == items[0]) selectionToWrite = &ms;
Chris@38 2352
Chris@38 2353 } else if (selections.size() > 1) {
Chris@0 2354
Chris@0 2355 QStringList items;
Chris@0 2356 items << tr("Export the selected regions into a single audio file")
Chris@0 2357 << tr("Export the selected regions into separate files")
Chris@0 2358 << tr("Export the whole audio file");
Chris@0 2359
Chris@0 2360 QString item = ListInputDialog::getItem
Chris@0 2361 (this, tr("Select region to export"),
Chris@0 2362 tr("Multiple regions of the original audio file are selected.\nWhat do you want to export?"),
Chris@0 2363 items, 0, &ok);
Chris@0 2364
Chris@0 2365 if (!ok || item.isEmpty()) return;
Chris@0 2366
Chris@0 2367 if (item == items[0]) {
Chris@0 2368
Chris@38 2369 selectionToWrite = &ms;
Chris@38 2370
Chris@38 2371 } else if (item == items[1]) {
Chris@0 2372
Chris@0 2373 multiple = true;
Chris@0 2374
Chris@0 2375 int n = 1;
Chris@0 2376 QString base = path;
Chris@0 2377 base.replace(".wav", "");
Chris@0 2378
Chris@0 2379 for (MultiSelection::SelectionList::iterator i = selections.begin();
Chris@0 2380 i != selections.end(); ++i) {
Chris@0 2381
Chris@0 2382 MultiSelection subms;
Chris@0 2383 subms.setSelection(*i);
Chris@0 2384
Chris@0 2385 QString subpath = QString("%1.%2.wav").arg(base).arg(n);
Chris@0 2386 ++n;
Chris@0 2387
Chris@0 2388 if (QFileInfo(subpath).exists()) {
Chris@0 2389 error = tr("Fragment file %1 already exists, aborting").arg(subpath);
Chris@0 2390 break;
Chris@0 2391 }
Chris@0 2392
Chris@38 2393 WavFileWriter subwriter(subpath,
Chris@320 2394 model->getSampleRate(),
Chris@428 2395 model->getChannelCount(),
Chris@428 2396 WavFileWriter::WriteToTemporary);
Chris@320 2397 subwriter.writeModel(model, &subms);
Chris@0 2398 ok = subwriter.isOK();
Chris@0 2399
Chris@0 2400 if (!ok) {
Chris@0 2401 error = subwriter.getError();
Chris@0 2402 break;
Chris@0 2403 }
Chris@0 2404 }
Chris@0 2405 }
Chris@0 2406 }
Chris@0 2407
Chris@38 2408 if (!multiple) {
Chris@38 2409 WavFileWriter writer(path,
Chris@320 2410 model->getSampleRate(),
Chris@428 2411 model->getChannelCount(),
Chris@428 2412 WavFileWriter::WriteToTemporary);
Chris@320 2413 writer.writeModel(model, selectionToWrite);
Chris@38 2414 ok = writer.isOK();
Chris@38 2415 error = writer.getError();
Chris@0 2416 }
Chris@0 2417
Chris@0 2418 if (ok) {
Chris@310 2419 if (multiple) {
Chris@310 2420 emit activity(tr("Export multiple audio files"));
Chris@310 2421 } else {
Chris@310 2422 emit activity(tr("Export audio to \"%1\"").arg(path));
Chris@34 2423 m_recentFiles.addFile(path);
Chris@0 2424 }
Chris@0 2425 } else {
Chris@0 2426 QMessageBox::critical(this, tr("Failed to write file"), error);
Chris@0 2427 }
Chris@0 2428 }
Chris@0 2429
Chris@0 2430 void
Chris@0 2431 MainWindow::importLayer()
Chris@0 2432 {
Chris@0 2433 Pane *pane = m_paneStack->getCurrentPane();
Chris@0 2434
Chris@0 2435 if (!pane) {
Chris@0 2436 // shouldn't happen, as the menu action should have been disabled
Chris@0 2437 std::cerr << "WARNING: MainWindow::importLayer: no current pane" << std::endl;
Chris@0 2438 return;
Chris@0 2439 }
Chris@0 2440
Chris@0 2441 if (!getMainModel()) {
Chris@0 2442 // shouldn't happen, as the menu action should have been disabled
Chris@0 2443 std::cerr << "WARNING: MainWindow::importLayer: No main model -- hence no default sample rate available" << std::endl;
Chris@0 2444 return;
Chris@0 2445 }
Chris@0 2446
Chris@88 2447 QString path = getOpenFileName(FileFinder::LayerFile);
Chris@0 2448
Chris@0 2449 if (path != "") {
Chris@0 2450
Chris@197 2451 FileOpenStatus status = openLayer(path);
Chris@193 2452
Chris@193 2453 if (status == FileOpenFailed) {
Chris@247 2454 emit hideSplash();
Chris@0 2455 QMessageBox::critical(this, tr("Failed to open file"),
Chris@193 2456 tr("<b>File open failed</b><p>Layer file %1 could not be opened.").arg(path));
Chris@0 2457 return;
Chris@193 2458 } else if (status == FileOpenWrongMode) {
Chris@247 2459 emit hideSplash();
Chris@193 2460 QMessageBox::critical(this, tr("Failed to open file"),
Chris@294 2461 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 2462 }
Chris@0 2463 }
Chris@0 2464 }
Chris@0 2465
Chris@0 2466 void
Chris@0 2467 MainWindow::exportLayer()
Chris@0 2468 {
Chris@0 2469 Pane *pane = m_paneStack->getCurrentPane();
Chris@0 2470 if (!pane) return;
Chris@0 2471
Chris@0 2472 Layer *layer = pane->getSelectedLayer();
Chris@0 2473 if (!layer) return;
Chris@0 2474
Chris@0 2475 Model *model = layer->getModel();
Chris@0 2476 if (!model) return;
Chris@0 2477
Chris@185 2478 FileFinder::FileType type = FileFinder::LayerFileNoMidi;
Chris@185 2479
Chris@185 2480 if (dynamic_cast<NoteModel *>(model)) type = FileFinder::LayerFile;
Chris@185 2481
Chris@185 2482 QString path = getSaveFileName(type);
Chris@0 2483
Chris@0 2484 if (path == "") return;
Chris@0 2485
Chris@0 2486 if (QFileInfo(path).suffix() == "") path += ".svl";
Chris@0 2487
Chris@185 2488 QString suffix = QFileInfo(path).suffix().toLower();
Chris@185 2489
Chris@0 2490 QString error;
Chris@0 2491
Chris@185 2492 if (suffix == "xml" || suffix == "svl") {
Chris@0 2493
Chris@0 2494 QFile file(path);
Chris@0 2495 if (!file.open(QIODevice::WriteOnly | QIODevice::Text)) {
Chris@0 2496 error = tr("Failed to open file %1 for writing").arg(path);
Chris@0 2497 } else {
Chris@0 2498 QTextStream out(&file);
Chris@0 2499 out << "<?xml version=\"1.0\" encoding=\"UTF-8\"?>\n"
Chris@0 2500 << "<!DOCTYPE sonic-visualiser>\n"
Chris@0 2501 << "<sv>\n"
Chris@0 2502 << " <data>\n";
Chris@0 2503
Chris@0 2504 model->toXml(out, " ");
Chris@0 2505
Chris@0 2506 out << " </data>\n"
Chris@0 2507 << " <display>\n";
Chris@0 2508
Chris@0 2509 layer->toXml(out, " ");
Chris@0 2510
Chris@0 2511 out << " </display>\n"
Chris@0 2512 << "</sv>\n";
Chris@0 2513 }
Chris@0 2514
Chris@185 2515 } else if (suffix == "mid" || suffix == "midi") {
Chris@185 2516
Chris@185 2517 NoteModel *nm = dynamic_cast<NoteModel *>(model);
Chris@185 2518
Chris@185 2519 if (!nm) {
Chris@185 2520 error = tr("Can't export non-note layers to MIDI");
Chris@185 2521 } else {
Chris@185 2522 MIDIFileWriter writer(path, nm);
Chris@185 2523 writer.write();
Chris@185 2524 if (!writer.isOK()) {
Chris@185 2525 error = writer.getError();
Chris@185 2526 }
Chris@185 2527 }
Chris@185 2528
Chris@291 2529 } else if (suffix == "ttl" || suffix == "n3") {
Chris@291 2530
Chris@291 2531 RDFExporter exporter(path, model);
Chris@291 2532 exporter.write();
Chris@291 2533 if (!exporter.isOK()) {
Chris@291 2534 error = exporter.getError();
Chris@291 2535 }
Chris@291 2536
Chris@0 2537 } else {
Chris@0 2538
Chris@0 2539 CSVFileWriter writer(path, model,
Chris@185 2540 ((suffix == "csv") ? "," : "\t"));
Chris@0 2541 writer.write();
Chris@0 2542
Chris@0 2543 if (!writer.isOK()) {
Chris@0 2544 error = writer.getError();
Chris@0 2545 }
Chris@0 2546 }
Chris@0 2547
Chris@0 2548 if (error != "") {
Chris@0 2549 QMessageBox::critical(this, tr("Failed to write file"), error);
Chris@0 2550 } else {
Chris@34 2551 m_recentFiles.addFile(path);
Chris@310 2552 emit activity(tr("Export layer to \"%1\"").arg(path));
Chris@0 2553 }
Chris@0 2554 }
Chris@0 2555
Chris@121 2556 void
Chris@121 2557 MainWindow::exportImage()
Chris@121 2558 {
Chris@121 2559 Pane *pane = m_paneStack->getCurrentPane();
Chris@121 2560 if (!pane) return;
Chris@121 2561
Chris@121 2562 QString path = getSaveFileName(FileFinder::ImageFile);
Chris@121 2563
Chris@121 2564 if (path == "") return;
Chris@121 2565
Chris@121 2566 if (QFileInfo(path).suffix() == "") path += ".png";
Chris@121 2567
Chris@123 2568 bool haveSelection = m_viewManager && !m_viewManager->getSelections().empty();
Chris@123 2569
Chris@123 2570 QSize total, visible, selected;
Chris@123 2571 total = pane->getImageSize();
Chris@123 2572 visible = pane->getImageSize(pane->getFirstVisibleFrame(),
Chris@123 2573 pane->getLastVisibleFrame());
Chris@123 2574
Chris@123 2575 size_t sf0 = 0, sf1 = 0;
Chris@123 2576
Chris@123 2577 if (haveSelection) {
Chris@123 2578 MultiSelection::SelectionList selections = m_viewManager->getSelections();
Chris@123 2579 sf0 = selections.begin()->getStartFrame();
Chris@123 2580 MultiSelection::SelectionList::iterator e = selections.end();
Chris@123 2581 --e;
Chris@123 2582 sf1 = e->getEndFrame();
Chris@123 2583 selected = pane->getImageSize(sf0, sf1);
Chris@123 2584 }
Chris@123 2585
Chris@123 2586 QStringList items;
Chris@125 2587 items << tr("Export the whole pane (%1x%2 pixels)")
Chris@123 2588 .arg(total.width()).arg(total.height());
Chris@123 2589 items << tr("Export the visible area only (%1x%2 pixels)")
Chris@123 2590 .arg(visible.width()).arg(visible.height());
Chris@123 2591 if (haveSelection) {
Chris@123 2592 items << tr("Export the selection extent (%1x%2 pixels)")
Chris@123 2593 .arg(selected.width()).arg(selected.height());
Chris@124 2594 } else {
Chris@124 2595 items << tr("Export the selection extent");
Chris@123 2596 }
Chris@123 2597
Chris@123 2598 QSettings settings;
Chris@123 2599 settings.beginGroup("MainWindow");
Chris@123 2600 int deflt = settings.value("lastimageexportregion", 0).toInt();
Chris@123 2601 if (deflt == 2 && !haveSelection) deflt = 1;
Chris@124 2602 if (deflt == 0 && total.width() > 32767) deflt = 1;
Chris@124 2603
Chris@124 2604 ListInputDialog *lid = new ListInputDialog
Chris@123 2605 (this, tr("Select region to export"),
Chris@123 2606 tr("Which region of the current pane do you want to export as an image?"),
Chris@124 2607 items, deflt);
Chris@124 2608
Chris@124 2609 if (!haveSelection) {
Chris@124 2610 lid->setItemAvailability(2, false);
Chris@124 2611 }
Chris@124 2612 if (total.width() > 32767) { // appears to be the limit of a QImage
Chris@124 2613 lid->setItemAvailability(0, false);
Chris@124 2614 lid->setFootnote(tr("Note: the whole pane is too wide to be exported as a single image."));
Chris@124 2615 }
Chris@124 2616
Chris@124 2617 bool ok = lid->exec();
Chris@124 2618 QString item = lid->getCurrentString();
Chris@124 2619 delete lid;
Chris@123 2620
Chris@123 2621 if (!ok || item.isEmpty()) return;
Chris@123 2622
Chris@123 2623 settings.setValue("lastimageexportregion", deflt);
Chris@123 2624
Chris@123 2625 QImage *image = 0;
Chris@123 2626
Chris@123 2627 if (item == items[0]) {
Chris@123 2628 image = pane->toNewImage();
Chris@123 2629 } else if (item == items[1]) {
Chris@123 2630 image = pane->toNewImage(pane->getFirstVisibleFrame(),
Chris@123 2631 pane->getLastVisibleFrame());
Chris@123 2632 } else if (haveSelection) {
Chris@123 2633 image = pane->toNewImage(sf0, sf1);
Chris@123 2634 }
Chris@123 2635
Chris@121 2636 if (!image) return;
Chris@121 2637
Chris@121 2638 if (!image->save(path, "PNG")) {
Chris@121 2639 QMessageBox::critical(this, tr("Failed to save image file"),
Chris@121 2640 tr("Failed to save image file %1").arg(path));
Chris@121 2641 }
Chris@121 2642
Chris@121 2643 delete image;
Chris@121 2644 }
Chris@121 2645
Chris@0 2646 void
Chris@0 2647 MainWindow::newSession()
Chris@0 2648 {
Chris@0 2649 if (!checkSaveModified()) return;
Chris@0 2650
Chris@0 2651 closeSession();
Chris@0 2652 createDocument();
Chris@0 2653
Chris@0 2654 Pane *pane = m_paneStack->addPane();
Chris@0 2655
Chris@90 2656 connect(pane, SIGNAL(contextHelpChanged(const QString &)),
Chris@116 2657 this, SLOT(contextHelpChanged(const QString &)));
Chris@90 2658
Chris@0 2659 if (!m_timeRulerLayer) {
Chris@0 2660 m_timeRulerLayer = m_document->createMainModelLayer
Chris@0 2661 (LayerFactory::TimeRuler);
Chris@0 2662 }
Chris@0 2663
Chris@0 2664 m_document->addLayerToView(pane, m_timeRulerLayer);
Chris@0 2665
Chris@0 2666 Layer *waveform = m_document->createMainModelLayer(LayerFactory::Waveform);
Chris@0 2667 m_document->addLayerToView(pane, waveform);
Chris@0 2668
Chris@65 2669 m_overview->registerView(pane);
Chris@0 2670
Chris@0 2671 CommandHistory::getInstance()->clear();
Chris@0 2672 CommandHistory::getInstance()->documentSaved();
Chris@0 2673 documentRestored();
Chris@0 2674 updateMenuStates();
Chris@0 2675 }
Chris@0 2676
Chris@0 2677 void
Chris@303 2678 MainWindow::documentReplaced()
Chris@303 2679 {
Chris@303 2680 if (m_document) {
Chris@303 2681 connect(m_document, SIGNAL(activity(QString)),
Chris@303 2682 m_activityLog, SLOT(activityHappened(QString)));
Chris@303 2683 }
Chris@303 2684 }
Chris@303 2685
Chris@303 2686 void
Chris@0 2687 MainWindow::closeSession()
Chris@0 2688 {
Chris@0 2689 if (!checkSaveModified()) return;
Chris@0 2690
Chris@0 2691 while (m_paneStack->getPaneCount() > 0) {
Chris@0 2692
Chris@0 2693 Pane *pane = m_paneStack->getPane(m_paneStack->getPaneCount() - 1);
Chris@0 2694
Chris@0 2695 while (pane->getLayerCount() > 0) {
Chris@0 2696 m_document->removeLayerFromView
Chris@0 2697 (pane, pane->getLayer(pane->getLayerCount() - 1));
Chris@0 2698 }
Chris@0 2699
Chris@65 2700 m_overview->unregisterView(pane);
Chris@0 2701 m_paneStack->deletePane(pane);
Chris@0 2702 }
Chris@0 2703
Chris@0 2704 while (m_paneStack->getHiddenPaneCount() > 0) {
Chris@0 2705
Chris@0 2706 Pane *pane = m_paneStack->getHiddenPane
Chris@0 2707 (m_paneStack->getHiddenPaneCount() - 1);
Chris@0 2708
Chris@0 2709 while (pane->getLayerCount() > 0) {
Chris@0 2710 m_document->removeLayerFromView
Chris@0 2711 (pane, pane->getLayer(pane->getLayerCount() - 1));
Chris@0 2712 }
Chris@0 2713
Chris@65 2714 m_overview->unregisterView(pane);
Chris@0 2715 m_paneStack->deletePane(pane);
Chris@0 2716 }
Chris@0 2717
Chris@0 2718 delete m_document;
Chris@0 2719 m_document = 0;
Chris@0 2720 m_viewManager->clearSelections();
Chris@0 2721 m_timeRulerLayer = 0; // document owned this
Chris@0 2722
Chris@0 2723 m_sessionFile = "";
Chris@0 2724 setWindowTitle(tr("Sonic Visualiser"));
Chris@0 2725
Chris@0 2726 CommandHistory::getInstance()->clear();
Chris@0 2727 CommandHistory::getInstance()->documentSaved();
Chris@0 2728 documentRestored();
Chris@0 2729 }
Chris@0 2730
Chris@0 2731 void
Chris@0 2732 MainWindow::openSession()
Chris@0 2733 {
Chris@0 2734 if (!checkSaveModified()) return;
Chris@0 2735
Chris@0 2736 QString orig = m_audioFile;
Chris@0 2737 if (orig == "") orig = ".";
Chris@0 2738 else orig = QFileInfo(orig).absoluteDir().canonicalPath();
Chris@0 2739
Chris@88 2740 QString path = getOpenFileName(FileFinder::SessionFile);
Chris@0 2741
Chris@0 2742 if (path.isEmpty()) return;
Chris@0 2743
Chris@200 2744 if (openSessionFile(path) == FileOpenFailed) {
Chris@247 2745 emit hideSplash();
Chris@0 2746 QMessageBox::critical(this, tr("Failed to open file"),
Chris@193 2747 tr("<b>File open failed</b><p>Session file \"%1\" could not be opened").arg(path));
Chris@0 2748 }
Chris@0 2749 }
Chris@0 2750
Chris@0 2751 void
Chris@0 2752 MainWindow::openSomething()
Chris@0 2753 {
Chris@0 2754 QString orig = m_audioFile;
Chris@0 2755 if (orig == "") orig = ".";
Chris@0 2756 else orig = QFileInfo(orig).absoluteDir().canonicalPath();
Chris@0 2757
Chris@88 2758 QString path = getOpenFileName(FileFinder::AnyFile);
Chris@0 2759
Chris@0 2760 if (path.isEmpty()) return;
Chris@0 2761
Chris@422 2762 FileOpenStatus status = open(path, ReplaceSession);
Chris@193 2763
Chris@193 2764 if (status == FileOpenFailed) {
Chris@247 2765 emit hideSplash();
Chris@193 2766 QMessageBox::critical(this, tr("Failed to open file"),
Chris@193 2767 tr("<b>File open failed</b><p>File \"%1\" could not be opened").arg(path));
Chris@193 2768 } else if (status == FileOpenWrongMode) {
Chris@247 2769 emit hideSplash();
Chris@193 2770 QMessageBox::critical(this, tr("Failed to open file"),
Chris@294 2771 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 2772 }
Chris@0 2773 }
Chris@0 2774
Chris@0 2775 void
Chris@86 2776 MainWindow::openLocation()
Chris@86 2777 {
Chris@103 2778 QSettings settings;
Chris@103 2779 settings.beginGroup("MainWindow");
Chris@103 2780 QString lastLocation = settings.value("lastremote", "").toString();
Chris@103 2781
Chris@86 2782 bool ok = false;
Chris@86 2783 QString text = QInputDialog::getText
Chris@86 2784 (this, tr("Open Location"),
Chris@86 2785 tr("Please enter the URL of the location to open:"),
Chris@103 2786 QLineEdit::Normal, lastLocation, &ok);
Chris@103 2787
Chris@103 2788 if (!ok) return;
Chris@103 2789
Chris@103 2790 settings.setValue("lastremote", text);
Chris@103 2791
Chris@103 2792 if (text.isEmpty()) return;
Chris@86 2793
Chris@419 2794 FileOpenStatus status = open(text, AskUser);
Chris@193 2795
Chris@193 2796 if (status == FileOpenFailed) {
Chris@247 2797 emit hideSplash();
Chris@86 2798 QMessageBox::critical(this, tr("Failed to open location"),
Chris@193 2799 tr("<b>Open failed</b><p>URL \"%1\" could not be opened").arg(text));
Chris@193 2800 } else if (status == FileOpenWrongMode) {
Chris@247 2801 emit hideSplash();
Chris@193 2802 QMessageBox::critical(this, tr("Failed to open location"),
Chris@294 2803 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 2804 }
Chris@86 2805 }
Chris@86 2806
Chris@86 2807 void
Chris@0 2808 MainWindow::openRecentFile()
Chris@0 2809 {
Chris@0 2810 QObject *obj = sender();
Chris@0 2811 QAction *action = dynamic_cast<QAction *>(obj);
Chris@0 2812
Chris@0 2813 if (!action) {
Chris@0 2814 std::cerr << "WARNING: MainWindow::openRecentFile: sender is not an action"
Chris@0 2815 << std::endl;
Chris@0 2816 return;
Chris@0 2817 }
Chris@0 2818
Chris@0 2819 QString path = action->text();
Chris@0 2820 if (path == "") return;
Chris@0 2821
Chris@422 2822 FileOpenStatus status = open(path, ReplaceSession);
Chris@193 2823
Chris@193 2824 if (status == FileOpenFailed) {
Chris@247 2825 emit hideSplash();
Chris@193 2826 QMessageBox::critical(this, tr("Failed to open location"),
Chris@193 2827 tr("<b>Open failed</b><p>File or URL \"%1\" could not be opened").arg(path));
Chris@193 2828 } else if (status == FileOpenWrongMode) {
Chris@247 2829 emit hideSplash();
Chris@193 2830 QMessageBox::critical(this, tr("Failed to open location"),
Chris@294 2831 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 2832 }
Chris@0 2833 }
Chris@0 2834
Chris@0 2835 void
Chris@435 2836 MainWindow::applyTemplate()
Chris@435 2837 {
Chris@435 2838 QObject *s = sender();
Chris@435 2839 QAction *action = qobject_cast<QAction *>(s);
Chris@435 2840
Chris@435 2841 if (!action) {
Chris@435 2842 std::cerr << "WARNING: MainWindow::applyTemplate: sender is not an action"
Chris@435 2843 << std::endl;
Chris@435 2844 return;
Chris@435 2845 }
Chris@435 2846
Chris@435 2847 QString n = action->objectName();
Chris@435 2848 if (n == "") n = action->text();
Chris@435 2849
Chris@435 2850 if (n == "") {
Chris@435 2851 std::cerr << "WARNING: MainWindow::applyTemplate: sender has no name"
Chris@435 2852 << std::endl;
Chris@435 2853 return;
Chris@435 2854 }
Chris@435 2855
Chris@435 2856 QString mainModelLocation;
Chris@435 2857 WaveFileModel *mm = getMainModel();
Chris@435 2858 if (mm) mainModelLocation = mm->getLocation();
Chris@435 2859 if (mainModelLocation != "") {
Chris@435 2860 openAudio(mainModelLocation, ReplaceSession, n);
Chris@435 2861 } else {
Chris@435 2862 openSessionTemplate(n);
Chris@435 2863 }
Chris@435 2864 }
Chris@435 2865
Chris@435 2866 void
Chris@425 2867 MainWindow::saveSessionAsTemplate()
Chris@425 2868 {
Chris@483 2869 QDialog *d = new QDialog;
Chris@483 2870 d->setWindowTitle(tr("Enter template name"));
Chris@483 2871
Chris@483 2872 QGridLayout *layout = new QGridLayout;
Chris@483 2873 d->setLayout(layout);
Chris@483 2874
Chris@483 2875 layout->addWidget(new QLabel(tr("Please enter a name for the saved template:")),
Chris@483 2876 0, 0);
Chris@483 2877 QLineEdit *lineEdit = new QLineEdit;
Chris@483 2878 layout->addWidget(lineEdit, 1, 0);
Chris@483 2879 QCheckBox *makeDefault = new QCheckBox(tr("Set as default template for future audio files"));
Chris@483 2880 layout->addWidget(makeDefault, 2, 0);
Chris@425 2881
Chris@483 2882 QDialogButtonBox *bb = new QDialogButtonBox(QDialogButtonBox::Ok |
Chris@483 2883 QDialogButtonBox::Cancel);
Chris@483 2884 layout->addWidget(bb, 3, 0);
Chris@483 2885 connect(bb, SIGNAL(accepted()), d, SLOT(accept()));
Chris@483 2886 connect(bb, SIGNAL(accepted()), d, SLOT(accept()));
Chris@483 2887 connect(bb, SIGNAL(rejected()), d, SLOT(reject()));
Chris@483 2888
Chris@483 2889 if (d->exec() == QDialog::Accepted) {
Chris@483 2890
Chris@483 2891 QString name = lineEdit->text();
Chris@483 2892 name.replace(QRegExp("[^\\w\\s\\.\"'-]"), "_");
Chris@483 2893
Chris@483 2894 ResourceFinder rf;
Chris@483 2895 QString dir = rf.getResourceSaveDir("templates");
Chris@483 2896 QString filename = QString("%1/%2.svt").arg(dir).arg(name);
Chris@483 2897 if (QFile(filename).exists()) {
Chris@483 2898 if (QMessageBox::warning(this,
Chris@483 2899 tr("Template file exists"),
Chris@483 2900 tr("<b>Template file exists</b><p>The template \"%1\" already exists.<br>Overwrite it?").arg(name),
Chris@483 2901 QMessageBox::Ok | QMessageBox::Cancel,
Chris@483 2902 QMessageBox::Cancel) != QMessageBox::Ok) {
Chris@483 2903 return;
Chris@483 2904 }
Chris@483 2905 }
Chris@483 2906
Chris@483 2907 if (saveSessionTemplate(filename)) {
Chris@483 2908 if (makeDefault->isChecked()) {
Chris@483 2909 setDefaultSessionTemplate(name);
Chris@483 2910 }
Chris@431 2911 }
Chris@431 2912 }
Chris@425 2913 }
Chris@425 2914
Chris@425 2915 void
Chris@425 2916 MainWindow::manageSavedTemplates()
Chris@425 2917 {
Chris@425 2918 ResourceFinder rf;
Chris@425 2919 QDesktopServices::openUrl("file:" + rf.getResourceSaveDir("templates"));
Chris@423 2920 }
Chris@423 2921
Chris@423 2922 void
Chris@200 2923 MainWindow::paneAdded(Pane *pane)
Chris@200 2924 {
Chris@200 2925 if (m_overview) m_overview->registerView(pane);
Chris@200 2926 }
Chris@200 2927
Chris@200 2928 void
Chris@200 2929 MainWindow::paneHidden(Pane *pane)
Chris@200 2930 {
Chris@200 2931 if (m_overview) m_overview->unregisterView(pane);
Chris@200 2932 }
Chris@200 2933
Chris@200 2934 void
Chris@200 2935 MainWindow::paneAboutToBeDeleted(Pane *pane)
Chris@200 2936 {
Chris@200 2937 if (m_overview) m_overview->unregisterView(pane);
Chris@200 2938 }
Chris@200 2939
Chris@200 2940 void
Chris@193 2941 MainWindow::paneDropAccepted(Pane *pane, QStringList uriList)
Chris@193 2942 {
Chris@193 2943 if (pane) m_paneStack->setCurrentPane(pane);
Chris@193 2944
Chris@193 2945 for (QStringList::iterator i = uriList.begin(); i != uriList.end(); ++i) {
Chris@193 2946
Chris@295 2947 FileOpenStatus status;
Chris@295 2948
Chris@295 2949 if (i == uriList.begin()) {
Chris@295 2950 status = open(*i, ReplaceCurrentPane);
Chris@295 2951 } else {
Chris@295 2952 status = open(*i, CreateAdditionalModel);
Chris@295 2953 }
Chris@193 2954
Chris@193 2955 if (status == FileOpenFailed) {
Chris@247 2956 emit hideSplash();
Chris@193 2957 QMessageBox::critical(this, tr("Failed to open dropped URL"),
Chris@193 2958 tr("<b>Open failed</b><p>Dropped URL \"%1\" could not be opened").arg(*i));
Chris@295 2959 break;
Chris@193 2960 } else if (status == FileOpenWrongMode) {
Chris@247 2961 emit hideSplash();
Chris@193 2962 QMessageBox::critical(this, tr("Failed to open dropped URL"),
Chris@294 2963 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 2964 break;
Chris@295 2965 } else if (status == FileOpenCancelled) {
Chris@295 2966 break;
Chris@193 2967 }
Chris@193 2968 }
Chris@193 2969 }
Chris@193 2970
Chris@193 2971 void
Chris@193 2972 MainWindow::paneDropAccepted(Pane *pane, QString text)
Chris@193 2973 {
Chris@193 2974 if (pane) m_paneStack->setCurrentPane(pane);
Chris@193 2975
Chris@193 2976 QUrl testUrl(text);
Chris@193 2977 if (testUrl.scheme() == "file" ||
Chris@193 2978 testUrl.scheme() == "http" ||
Chris@193 2979 testUrl.scheme() == "ftp") {
Chris@193 2980 QStringList list;
Chris@193 2981 list.push_back(text);
Chris@193 2982 paneDropAccepted(pane, list);
Chris@193 2983 return;
Chris@193 2984 }
Chris@193 2985
Chris@193 2986 //!!! open as text -- but by importing as if a CSV, or just adding
Chris@193 2987 //to a text layer?
Chris@193 2988 }
Chris@193 2989
Chris@193 2990 void
Chris@0 2991 MainWindow::closeEvent(QCloseEvent *e)
Chris@0 2992 {
Chris@490 2993 if (m_exiting) {
Chris@490 2994 e->accept();
Chris@490 2995 return;
Chris@490 2996 }
Chris@490 2997
Chris@438 2998 // SVDEBUG << "MainWindow::closeEvent" << endl;
Chris@118 2999
Chris@136 3000 if (m_openingAudioFile) {
Chris@137 3001 // std::cerr << "Busy - ignoring close event" << std::endl;
Chris@136 3002 e->ignore();
Chris@136 3003 return;
Chris@136 3004 }
Chris@136 3005
Chris@70 3006 if (!m_abandoning && !checkSaveModified()) {
Chris@438 3007 // SVDEBUG << "Ignoring close event" << endl;
Chris@0 3008 e->ignore();
Chris@0 3009 return;
Chris@0 3010 }
Chris@0 3011
Chris@5 3012 QSettings settings;
Chris@5 3013 settings.beginGroup("MainWindow");
Chris@5 3014 settings.setValue("size", size());
Chris@5 3015 settings.setValue("position", pos());
Chris@5 3016 settings.endGroup();
Chris@5 3017
Chris@163 3018 delete m_keyReference;
Chris@163 3019 m_keyReference = 0;
Chris@163 3020
Chris@163 3021 if (m_preferencesDialog &&
Chris@163 3022 m_preferencesDialog->isVisible()) {
Chris@164 3023 closeSession(); // otherwise we'll have to wait for prefs changes
Chris@163 3024 m_preferencesDialog->applicationClosing(false);
Chris@163 3025 }
Chris@163 3026
Chris@200 3027 closeSession();
Chris@200 3028
Chris@0 3029 e->accept();
Chris@490 3030
Chris@490 3031 m_exiting = true;
Chris@490 3032 qApp->closeAllWindows();
Chris@490 3033
Chris@0 3034 return;
Chris@0 3035 }
Chris@0 3036
Chris@0 3037 bool
Chris@11 3038 MainWindow::commitData(bool mayAskUser)
Chris@11 3039 {
Chris@11 3040 if (mayAskUser) {
Chris@163 3041 bool rv = checkSaveModified();
Chris@163 3042 if (rv) {
Chris@163 3043 if (m_preferencesDialog &&
Chris@163 3044 m_preferencesDialog->isVisible()) {
Chris@163 3045 m_preferencesDialog->applicationClosing(false);
Chris@163 3046 }
Chris@163 3047 }
Chris@163 3048 return rv;
Chris@11 3049 } else {
Chris@163 3050 if (m_preferencesDialog &&
Chris@163 3051 m_preferencesDialog->isVisible()) {
Chris@163 3052 m_preferencesDialog->applicationClosing(true);
Chris@163 3053 }
Chris@11 3054 if (!m_documentModified) return true;
Chris@11 3055
Chris@11 3056 // If we can't check with the user first, then we can't save
Chris@11 3057 // to the original session file (even if we have it) -- have
Chris@11 3058 // to use a temporary file
Chris@11 3059
Chris@11 3060 QString svDirBase = ".sv1";
Chris@11 3061 QString svDir = QDir::home().filePath(svDirBase);
Chris@11 3062
Chris@11 3063 if (!QFileInfo(svDir).exists()) {
Chris@11 3064 if (!QDir::home().mkdir(svDirBase)) return false;
Chris@11 3065 } else {
Chris@11 3066 if (!QFileInfo(svDir).isDir()) return false;
Chris@11 3067 }
Chris@11 3068
Chris@11 3069 // This name doesn't have to be unguessable
Chris@93 3070 #ifndef _WIN32
Chris@11 3071 QString fname = QString("tmp-%1-%2.sv")
Chris@11 3072 .arg(QDateTime::currentDateTime().toString("yyyyMMddhhmmsszzz"))
Chris@11 3073 .arg(QProcess().pid());
Chris@93 3074 #else
Chris@93 3075 QString fname = QString("tmp-%1.sv")
Chris@93 3076 .arg(QDateTime::currentDateTime().toString("yyyyMMddhhmmsszzz"));
Chris@93 3077 #endif
Chris@11 3078 QString fpath = QDir(svDir).filePath(fname);
Chris@11 3079 if (saveSessionFile(fpath)) {
Chris@34 3080 m_recentFiles.addFile(fpath);
Chris@310 3081 emit activity(tr("Export image to \"%1\"").arg(fpath));
Chris@11 3082 return true;
Chris@11 3083 } else {
Chris@11 3084 return false;
Chris@11 3085 }
Chris@11 3086 }
Chris@11 3087 }
Chris@11 3088
Chris@11 3089 bool
Chris@0 3090 MainWindow::checkSaveModified()
Chris@0 3091 {
Chris@0 3092 // Called before some destructive operation (e.g. new session,
Chris@0 3093 // exit program). Return true if we can safely proceed, false to
Chris@0 3094 // cancel.
Chris@0 3095
Chris@0 3096 if (!m_documentModified) return true;
Chris@0 3097
Chris@247 3098 emit hideSplash();
Chris@247 3099
Chris@0 3100 int button =
Chris@0 3101 QMessageBox::warning(this,
Chris@0 3102 tr("Session modified"),
Chris@207 3103 tr("<b>Session modified</b><p>The current session has been modified.<br>Do you want to save it?"),
Chris@165 3104 QMessageBox::Yes | QMessageBox::No | QMessageBox::Cancel,
Chris@165 3105 QMessageBox::Yes);
Chris@0 3106
Chris@0 3107 if (button == QMessageBox::Yes) {
Chris@0 3108 saveSession();
Chris@0 3109 if (m_documentModified) { // save failed -- don't proceed!
Chris@0 3110 return false;
Chris@0 3111 } else {
Chris@0 3112 return true; // saved, so it's safe to continue now
Chris@0 3113 }
Chris@0 3114 } else if (button == QMessageBox::No) {
Chris@0 3115 m_documentModified = false; // so we know to abandon it
Chris@0 3116 return true;
Chris@0 3117 }
Chris@0 3118
Chris@0 3119 // else cancel
Chris@0 3120 return false;
Chris@0 3121 }
Chris@0 3122
Chris@290 3123 bool
Chris@294 3124 MainWindow::shouldCreateNewSessionForRDFAudio(bool *cancel)
Chris@290 3125 {
Chris@294 3126 //!!! This is very similar to part of MainWindowBase::openAudio --
Chris@294 3127 //!!! make them a bit more uniform
Chris@294 3128
Chris@294 3129 QSettings settings;
Chris@294 3130 settings.beginGroup("MainWindow");
Chris@294 3131 bool prevNewSession = settings.value("newsessionforrdfaudio", true).toBool();
Chris@294 3132 settings.endGroup();
Chris@294 3133 bool newSession = true;
Chris@294 3134
Chris@294 3135 QStringList items;
Chris@294 3136 items << tr("Close the current session and create a new one")
Chris@294 3137 << tr("Add this data to the current session");
Chris@294 3138
Chris@294 3139 bool ok = false;
Chris@294 3140 QString item = ListInputDialog::getItem
Chris@294 3141 (this, tr("Select target for import"),
Chris@294 3142 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 3143 items, prevNewSession ? 0 : 1, &ok);
Chris@294 3144
Chris@294 3145 if (!ok || item.isEmpty()) {
Chris@294 3146 *cancel = true;
Chris@290 3147 return false;
Chris@290 3148 }
Chris@294 3149
Chris@294 3150 newSession = (item == items[0]);
Chris@294 3151 settings.beginGroup("MainWindow");
Chris@294 3152 settings.setValue("newsessionforrdfaudio", newSession);
Chris@294 3153 settings.endGroup();
Chris@294 3154
Chris@294 3155 if (newSession) return true;
Chris@294 3156 else return false;
Chris@290 3157 }
Chris@290 3158
Chris@0 3159 void
Chris@0 3160 MainWindow::saveSession()
Chris@0 3161 {
Chris@0 3162 if (m_sessionFile != "") {
Chris@0 3163 if (!saveSessionFile(m_sessionFile)) {
Chris@0 3164 QMessageBox::critical(this, tr("Failed to save file"),
Chris@193 3165 tr("<b>Save failed</b><p>Session file \"%1\" could not be saved.").arg(m_sessionFile));
Chris@0 3166 } else {
Chris@0 3167 CommandHistory::getInstance()->documentSaved();
Chris@0 3168 documentRestored();
Chris@0 3169 }
Chris@0 3170 } else {
Chris@0 3171 saveSessionAs();
Chris@0 3172 }
Chris@0 3173 }
Chris@0 3174
Chris@0 3175 void
Chris@0 3176 MainWindow::saveSessionAs()
Chris@0 3177 {
Chris@0 3178 QString orig = m_audioFile;
Chris@0 3179 if (orig == "") orig = ".";
Chris@0 3180 else orig = QFileInfo(orig).absoluteDir().canonicalPath();
Chris@0 3181
Chris@88 3182 QString path = getSaveFileName(FileFinder::SessionFile);
Chris@81 3183
Chris@81 3184 if (path == "") return;
Chris@0 3185
Chris@0 3186 if (!saveSessionFile(path)) {
Chris@0 3187 QMessageBox::critical(this, tr("Failed to save file"),
Chris@193 3188 tr("<b>Save failed</b><p>Session file \"%1\" could not be saved.").arg(path));
Chris@0 3189 } else {
Chris@0 3190 setWindowTitle(tr("Sonic Visualiser: %1")
Chris@0 3191 .arg(QFileInfo(path).fileName()));
Chris@0 3192 m_sessionFile = path;
Chris@0 3193 CommandHistory::getInstance()->documentSaved();
Chris@0 3194 documentRestored();
Chris@34 3195 m_recentFiles.addFile(path);
Chris@310 3196 emit activity(tr("Save session as \"%1\"").arg(path));
Chris@0 3197 }
Chris@0 3198 }
Chris@0 3199
Chris@90 3200 void
Chris@72 3201 MainWindow::preferenceChanged(PropertyContainer::PropertyName name)
Chris@72 3202 {
Chris@200 3203 MainWindowBase::preferenceChanged(name);
Chris@200 3204
Chris@200 3205 if (name == "Background Mode" && m_viewManager) {
Chris@180 3206 if (m_viewManager->getGlobalDarkBackground()) {
Chris@180 3207 m_panLayer->setBaseColour
Chris@180 3208 (ColourDatabase::getInstance()->getColourIndex(tr("Bright Green")));
Chris@180 3209 } else {
Chris@180 3210 m_panLayer->setBaseColour
Chris@180 3211 (ColourDatabase::getInstance()->getColourIndex(tr("Green")));
Chris@180 3212 }
Chris@200 3213 }
Chris@0 3214 }
Chris@0 3215
Chris@0 3216 void
Chris@239 3217 MainWindow::propertyStacksResized(int width)
Chris@239 3218 {
Chris@438 3219 // SVDEBUG << "MainWindow::propertyStacksResized(" << width << ")" << endl;
Chris@239 3220
Chris@239 3221 if (!m_playControlsSpacer) return;
Chris@239 3222
Chris@239 3223 int spacerWidth = width - m_playControlsWidth - 4;
Chris@239 3224
Chris@438 3225 // SVDEBUG << "resizing spacer from " << m_playControlsSpacer->width() << " to " << spacerWidth << endl;
Chris@239 3226
Chris@239 3227 m_playControlsSpacer->setFixedSize(QSize(spacerWidth, 2));
Chris@239 3228 }
Chris@239 3229
Chris@239 3230 void
Chris@0 3231 MainWindow::addPane()
Chris@0 3232 {
Chris@0 3233 QObject *s = sender();
Chris@0 3234 QAction *action = dynamic_cast<QAction *>(s);
Chris@0 3235
Chris@0 3236 if (!action) {
Chris@0 3237 std::cerr << "WARNING: MainWindow::addPane: sender is not an action"
Chris@0 3238 << std::endl;
Chris@0 3239 return;
Chris@0 3240 }
Chris@0 3241
Chris@0 3242 PaneActionMap::iterator i = m_paneActions.find(action);
Chris@0 3243
Chris@0 3244 if (i == m_paneActions.end()) {
Chris@0 3245 std::cerr << "WARNING: MainWindow::addPane: unknown action "
Chris@432 3246 << action->objectName() << std::endl;
Chris@0 3247 return;
Chris@0 3248 }
Chris@0 3249
Chris@69 3250 addPane(i->second, action->text());
Chris@69 3251 }
Chris@69 3252
Chris@69 3253 void
Chris@232 3254 MainWindow::addPane(const LayerConfiguration &configuration, QString text)
Chris@69 3255 {
Chris@69 3256 CommandHistory::getInstance()->startCompoundOperation(text, true);
Chris@0 3257
Chris@0 3258 AddPaneCommand *command = new AddPaneCommand(this);
Chris@0 3259 CommandHistory::getInstance()->addCommand(command);
Chris@0 3260
Chris@0 3261 Pane *pane = command->getPane();
Chris@0 3262
Chris@69 3263 if (configuration.layer == LayerFactory::Spectrum) {
Chris@109 3264 pane->setPlaybackFollow(PlaybackScrollContinuous);
Chris@110 3265 pane->setFollowGlobalZoom(false);
Chris@112 3266 pane->setZoomLevel(512);
Chris@7 3267 }
Chris@7 3268
Chris@69 3269 if (configuration.layer != LayerFactory::TimeRuler &&
Chris@69 3270 configuration.layer != LayerFactory::Spectrum) {
Chris@8 3271
Chris@0 3272 if (!m_timeRulerLayer) {
Chris@0 3273 // std::cerr << "no time ruler layer, creating one" << std::endl;
Chris@0 3274 m_timeRulerLayer = m_document->createMainModelLayer
Chris@0 3275 (LayerFactory::TimeRuler);
Chris@0 3276 }
Chris@0 3277
Chris@438 3278 // SVDEBUG << "adding time ruler layer " << m_timeRulerLayer << endl;
Chris@0 3279
Chris@0 3280 m_document->addLayerToView(pane, m_timeRulerLayer);
Chris@0 3281 }
Chris@0 3282
Chris@69 3283 Layer *newLayer = m_document->createLayer(configuration.layer);
Chris@69 3284
Chris@69 3285 Model *suggestedModel = configuration.sourceModel;
Chris@66 3286 Model *model = 0;
Chris@66 3287
Chris@66 3288 if (suggestedModel) {
Chris@66 3289
Chris@66 3290 // check its validity
Chris@224 3291 std::vector<Model *> inputModels = m_document->getTransformInputModels();
Chris@66 3292 for (size_t j = 0; j < inputModels.size(); ++j) {
Chris@66 3293 if (inputModels[j] == suggestedModel) {
Chris@66 3294 model = suggestedModel;
Chris@66 3295 break;
Chris@66 3296 }
Chris@66 3297 }
Chris@66 3298
Chris@66 3299 if (!model) {
Chris@66 3300 std::cerr << "WARNING: Model " << (void *)suggestedModel
Chris@66 3301 << " appears in pane action map, but is not reported "
Chris@66 3302 << "by document as a valid transform source" << std::endl;
Chris@66 3303 }
Chris@66 3304 }
Chris@66 3305
Chris@346 3306 if (!model) {
Chris@346 3307 model = m_document->getMainModel();
Chris@346 3308 }
Chris@66 3309
Chris@66 3310 m_document->setModel(newLayer, model);
Chris@66 3311
Chris@69 3312 m_document->setChannel(newLayer, configuration.channel);
Chris@0 3313 m_document->addLayerToView(pane, newLayer);
Chris@0 3314
Chris@0 3315 m_paneStack->setCurrentPane(pane);
Chris@70 3316 m_paneStack->setCurrentLayer(pane, newLayer);
Chris@0 3317
Chris@438 3318 // SVDEBUG << "MainWindow::addPane: global centre frame is "
Chris@433 3319 // << m_viewManager->getGlobalCentreFrame() << endl;
Chris@130 3320 // pane->setCentreFrame(m_viewManager->getGlobalCentreFrame());
Chris@73 3321
Chris@0 3322 CommandHistory::getInstance()->endCompoundOperation();
Chris@0 3323
Chris@0 3324 updateMenuStates();
Chris@0 3325 }
Chris@0 3326
Chris@0 3327 void
Chris@0 3328 MainWindow::addLayer()
Chris@0 3329 {
Chris@0 3330 QObject *s = sender();
Chris@0 3331 QAction *action = dynamic_cast<QAction *>(s);
Chris@0 3332
Chris@0 3333 if (!action) {
Chris@0 3334 std::cerr << "WARNING: MainWindow::addLayer: sender is not an action"
Chris@0 3335 << std::endl;
Chris@0 3336 return;
Chris@0 3337 }
Chris@0 3338
Chris@0 3339 Pane *pane = m_paneStack->getCurrentPane();
Chris@0 3340
Chris@0 3341 if (!pane) {
Chris@0 3342 std::cerr << "WARNING: MainWindow::addLayer: no current pane" << std::endl;
Chris@0 3343 return;
Chris@0 3344 }
Chris@0 3345
Chris@0 3346 ExistingLayerActionMap::iterator ei = m_existingLayerActions.find(action);
Chris@0 3347
Chris@0 3348 if (ei != m_existingLayerActions.end()) {
Chris@0 3349 Layer *newLayer = ei->second;
Chris@0 3350 m_document->addLayerToView(pane, newLayer);
Chris@0 3351 m_paneStack->setCurrentLayer(pane, newLayer);
Chris@0 3352 return;
Chris@0 3353 }
Chris@0 3354
Chris@95 3355 ei = m_sliceActions.find(action);
Chris@95 3356
Chris@95 3357 if (ei != m_sliceActions.end()) {
Chris@95 3358 Layer *newLayer = m_document->createLayer(LayerFactory::Slice);
Chris@95 3359 // document->setModel(newLayer, ei->second->getModel());
Chris@95 3360 SliceableLayer *source = dynamic_cast<SliceableLayer *>(ei->second);
Chris@95 3361 SliceLayer *dest = dynamic_cast<SliceLayer *>(newLayer);
Chris@95 3362 if (source && dest) {
Chris@205 3363 //!!!???
Chris@95 3364 dest->setSliceableModel(source->getSliceableModel());
Chris@95 3365 connect(source, SIGNAL(sliceableModelReplaced(const Model *, const Model *)),
Chris@95 3366 dest, SLOT(sliceableModelReplaced(const Model *, const Model *)));
Chris@95 3367 connect(m_document, SIGNAL(modelAboutToBeDeleted(Model *)),
Chris@95 3368 dest, SLOT(modelAboutToBeDeleted(Model *)));
Chris@95 3369 }
Chris@95 3370 m_document->addLayerToView(pane, newLayer);
Chris@95 3371 m_paneStack->setCurrentLayer(pane, newLayer);
Chris@95 3372 return;
Chris@95 3373 }
Chris@95 3374
Chris@211 3375 TransformActionMap::iterator i = m_transformActions.find(action);
Chris@34 3376
Chris@34 3377 if (i == m_transformActions.end()) {
Chris@0 3378
Chris@0 3379 LayerActionMap::iterator i = m_layerActions.find(action);
Chris@0 3380
Chris@0 3381 if (i == m_layerActions.end()) {
Chris@0 3382 std::cerr << "WARNING: MainWindow::addLayer: unknown action "
Chris@432 3383 << action->objectName() << std::endl;
Chris@0 3384 return;
Chris@0 3385 }
Chris@0 3386
Chris@232 3387 LayerFactory::LayerType type = i->second.layer;
Chris@0 3388
Chris@0 3389 LayerFactory::LayerTypeSet emptyTypes =
Chris@0 3390 LayerFactory::getInstance()->getValidEmptyLayerTypes();
Chris@0 3391
Chris@0 3392 Layer *newLayer;
Chris@0 3393
Chris@0 3394 if (emptyTypes.find(type) != emptyTypes.end()) {
Chris@0 3395
Chris@0 3396 newLayer = m_document->createEmptyLayer(type);
Chris@217 3397 if (newLayer) {
Chris@217 3398 m_toolActions[ViewManager::DrawMode]->trigger();
Chris@217 3399 }
Chris@0 3400
Chris@0 3401 } else {
Chris@0 3402
Chris@346 3403 Model *model = i->second.sourceModel;
Chris@346 3404
Chris@415 3405 cerr << "model = "<< model << endl;
Chris@415 3406
Chris@346 3407 if (!model) {
Chris@346 3408 if (type == LayerFactory::TimeRuler) {
Chris@346 3409 newLayer = m_document->createMainModelLayer(type);
Chris@346 3410 } else {
Chris@346 3411 // if model is unspecified and this is not a
Chris@415 3412 // time-ruler layer, use any plausible model from
Chris@415 3413 // the current pane -- this is the case for
Chris@415 3414 // right-button menu layer additions
Chris@415 3415 Pane::ModelSet ms = pane->getModels();
Chris@415 3416 foreach (Model *m, ms) {
Chris@415 3417 RangeSummarisableTimeValueModel *r =
Chris@415 3418 dynamic_cast<RangeSummarisableTimeValueModel *>(m);
Chris@415 3419 if (r) model = m;
Chris@346 3420 }
Chris@346 3421 if (!model) model = getMainModel();
Chris@346 3422 }
Chris@346 3423 }
Chris@346 3424
Chris@346 3425 if (model) {
Chris@238 3426 newLayer = m_document->createLayer(type);
Chris@346 3427 if (m_document->isKnownModel(model)) {
Chris@238 3428 m_document->setChannel(newLayer, i->second.channel);
Chris@346 3429 m_document->setModel(newLayer, model);
Chris@238 3430 } else {
Chris@238 3431 std::cerr << "WARNING: MainWindow::addLayer: unknown model "
Chris@346 3432 << model
Chris@238 3433 << " (\""
Chris@346 3434 << (model ? model->objectName().toStdString() : "")
Chris@238 3435 << "\") in layer action map"
Chris@238 3436 << std::endl;
Chris@238 3437 }
Chris@232 3438 }
Chris@238 3439 }
Chris@0 3440
Chris@217 3441 if (newLayer) {
Chris@217 3442 m_document->addLayerToView(pane, newLayer);
Chris@217 3443 m_paneStack->setCurrentLayer(pane, newLayer);
Chris@217 3444 }
Chris@0 3445
Chris@0 3446 return;
Chris@0 3447 }
Chris@0 3448
Chris@224 3449 //!!! want to do something like this, but it's not supported in
Chris@224 3450 //ModelTransformerFactory yet
Chris@224 3451 /*
Chris@0 3452 int channel = -1;
Chris@0 3453 // pick up the default channel from any existing layers on the same pane
Chris@0 3454 for (int j = 0; j < pane->getLayerCount(); ++j) {
Chris@0 3455 int c = LayerFactory::getInstance()->getChannel(pane->getLayer(j));
Chris@0 3456 if (c != -1) {
Chris@0 3457 channel = c;
Chris@0 3458 break;
Chris@0 3459 }
Chris@0 3460 }
Chris@224 3461 */
Chris@0 3462
Chris@33 3463 // We always ask for configuration, even if the plugin isn't
Chris@33 3464 // supposed to be configurable, because we need to let the user
Chris@33 3465 // change the execution context (block size etc).
Chris@0 3466
Chris@224 3467 QString transformId = i->second;
Chris@274 3468
Chris@274 3469 addLayer(transformId);
Chris@274 3470 }
Chris@274 3471
Chris@274 3472 void
Chris@274 3473 MainWindow::addLayer(QString transformId)
Chris@274 3474 {
Chris@274 3475 Pane *pane = m_paneStack->getCurrentPane();
Chris@274 3476 if (!pane) {
Chris@274 3477 std::cerr << "WARNING: MainWindow::addLayer: no current pane" << std::endl;
Chris@274 3478 return;
Chris@274 3479 }
Chris@274 3480
Chris@224 3481 Transform transform = TransformFactory::getInstance()->
Chris@224 3482 getDefaultTransformFor(transformId);
Chris@27 3483
Chris@66 3484 std::vector<Model *> candidateInputModels =
Chris@224 3485 m_document->getTransformInputModels();
Chris@53 3486
Chris@219 3487 Model *defaultInputModel = 0;
Chris@219 3488 for (int j = 0; j < pane->getLayerCount(); ++j) {
Chris@219 3489 Layer *layer = pane->getLayer(j);
Chris@219 3490 if (!layer) continue;
Chris@243 3491 if (LayerFactory::getInstance()->getLayerType(layer) !=
Chris@243 3492 LayerFactory::Waveform &&
Chris@243 3493 !layer->isLayerOpaque()) continue;
Chris@219 3494 Model *model = layer->getModel();
Chris@219 3495 if (!model) continue;
Chris@219 3496 for (size_t k = 0; k < candidateInputModels.size(); ++k) {
Chris@219 3497 if (candidateInputModels[k] == model) {
Chris@219 3498 defaultInputModel = model;
Chris@219 3499 break;
Chris@219 3500 }
Chris@219 3501 }
Chris@219 3502 if (defaultInputModel) break;
Chris@219 3503 }
Chris@219 3504
Chris@184 3505 size_t startFrame = 0, duration = 0;
Chris@184 3506 size_t endFrame = 0;
Chris@184 3507 m_viewManager->getSelection().getExtents(startFrame, endFrame);
Chris@184 3508 if (endFrame > startFrame) duration = endFrame - startFrame;
Chris@184 3509 else startFrame = 0;
Chris@184 3510
Chris@357 3511 TransformUserConfigurator configurator;
Chris@357 3512
Chris@224 3513 ModelTransformer::Input input = ModelTransformerFactory::getInstance()->
Chris@224 3514 getConfigurationForTransform
Chris@211 3515 (transform,
Chris@211 3516 candidateInputModels,
Chris@219 3517 defaultInputModel,
Chris@211 3518 m_playSource,
Chris@211 3519 startFrame,
Chris@357 3520 duration,
Chris@357 3521 &configurator);
Chris@211 3522
Chris@224 3523 if (!input.getModel()) return;
Chris@224 3524
Chris@438 3525 // SVDEBUG << "MainWindow::addLayer: Input model is " << input.getModel() << " \"" << input.getModel()->objectName() << "\"" << endl << "transform:" << endl << transform.toXmlString() << endl;
Chris@224 3526
Chris@224 3527 Layer *newLayer = m_document->createDerivedLayer(transform, input);
Chris@0 3528
Chris@0 3529 if (newLayer) {
Chris@0 3530 m_document->addLayerToView(pane, newLayer);
Chris@224 3531 m_document->setChannel(newLayer, input.getChannel());
Chris@224 3532 m_recentTransforms.add(transformId);
Chris@70 3533 m_paneStack->setCurrentLayer(pane, newLayer);
Chris@0 3534 }
Chris@0 3535
Chris@0 3536 updateMenuStates();
Chris@0 3537 }
Chris@0 3538
Chris@0 3539 void
Chris@0 3540 MainWindow::renameCurrentLayer()
Chris@0 3541 {
Chris@0 3542 Pane *pane = m_paneStack->getCurrentPane();
Chris@0 3543 if (pane) {
Chris@0 3544 Layer *layer = pane->getSelectedLayer();
Chris@0 3545 if (layer) {
Chris@0 3546 bool ok = false;
Chris@0 3547 QString newName = QInputDialog::getText
Chris@0 3548 (this, tr("Rename Layer"),
Chris@0 3549 tr("New name for this layer:"),
Chris@0 3550 QLineEdit::Normal, layer->objectName(), &ok);
Chris@0 3551 if (ok) {
Chris@239 3552 layer->setPresentationName(newName);
Chris@95 3553 setupExistingLayersMenus();
Chris@0 3554 }
Chris@0 3555 }
Chris@0 3556 }
Chris@0 3557 }
Chris@0 3558
Chris@0 3559 void
Chris@272 3560 MainWindow::findTransform()
Chris@272 3561 {
Chris@274 3562 TransformFinder *finder = new TransformFinder(this);
Chris@274 3563 if (!finder->exec()) {
Chris@274 3564 delete finder;
Chris@274 3565 return;
Chris@273 3566 }
Chris@274 3567 TransformId transform = finder->getTransform();
Chris@274 3568 delete finder;
Chris@287 3569
Chris@287 3570 if (getMainModel() != 0 && m_paneStack->getCurrentPane() != 0) {
Chris@287 3571 addLayer(transform);
Chris@287 3572 }
Chris@272 3573 }
Chris@272 3574
Chris@272 3575 void
Chris@207 3576 MainWindow::playSoloToggled()
Chris@207 3577 {
Chris@207 3578 MainWindowBase::playSoloToggled();
Chris@207 3579 m_soloModified = true;
Chris@207 3580 }
Chris@207 3581
Chris@207 3582 void
Chris@206 3583 MainWindow::alignToggled()
Chris@206 3584 {
Chris@206 3585 QAction *action = dynamic_cast<QAction *>(sender());
Chris@206 3586
Chris@207 3587 if (!m_viewManager) return;
Chris@207 3588
Chris@206 3589 if (action) {
Chris@206 3590 m_viewManager->setAlignMode(action->isChecked());
Chris@206 3591 } else {
Chris@206 3592 m_viewManager->setAlignMode(!m_viewManager->getAlignMode());
Chris@206 3593 }
Chris@206 3594
Chris@206 3595 if (m_viewManager->getAlignMode()) {
Chris@207 3596 m_prevSolo = m_soloAction->isChecked();
Chris@208 3597 if (!m_soloAction->isChecked()) {
Chris@208 3598 m_soloAction->setChecked(true);
Chris@208 3599 MainWindowBase::playSoloToggled();
Chris@208 3600 }
Chris@207 3601 m_soloModified = false;
Chris@207 3602 emit canChangeSolo(false);
Chris@206 3603 m_document->alignModels();
Chris@206 3604 m_document->setAutoAlignment(true);
Chris@206 3605 } else {
Chris@207 3606 if (!m_soloModified) {
Chris@208 3607 if (m_soloAction->isChecked() != m_prevSolo) {
Chris@208 3608 m_soloAction->setChecked(m_prevSolo);
Chris@208 3609 MainWindowBase::playSoloToggled();
Chris@208 3610 }
Chris@207 3611 }
Chris@207 3612 emit canChangeSolo(true);
Chris@206 3613 m_document->setAutoAlignment(false);
Chris@206 3614 }
Chris@206 3615
Chris@206 3616 for (int i = 0; i < m_paneStack->getPaneCount(); ++i) {
Chris@206 3617
Chris@206 3618 Pane *pane = m_paneStack->getPane(i);
Chris@206 3619 if (!pane) continue;
Chris@206 3620
Chris@206 3621 pane->update();
Chris@206 3622 }
Chris@206 3623 }
Chris@206 3624
Chris@206 3625 void
Chris@59 3626 MainWindow::playSpeedChanged(int position)
Chris@0 3627 {
Chris@59 3628 PlaySpeedRangeMapper mapper(0, 200);
Chris@60 3629
Chris@60 3630 float percent = m_playSpeed->mappedValue();
Chris@60 3631 float factor = mapper.getFactorForValue(percent);
Chris@60 3632
Chris@267 3633 // std::cerr << "speed = " << position << " percent = " << percent << " factor = " << factor << std::endl;
Chris@60 3634
Chris@59 3635 bool something = (position != 100);
Chris@155 3636
Chris@59 3637 int pc = lrintf(percent);
Chris@21 3638
Chris@155 3639 if (!something) {
Chris@155 3640 contextHelpChanged(tr("Playback speed: Normal"));
Chris@155 3641 } else {
Chris@155 3642 contextHelpChanged(tr("Playback speed: %1%2%")
Chris@155 3643 .arg(position > 100 ? "+" : "")
Chris@155 3644 .arg(pc));
Chris@155 3645 }
Chris@155 3646
Chris@240 3647 m_playSource->setTimeStretch(factor);
Chris@155 3648
Chris@155 3649 updateMenuStates();
Chris@16 3650 }
Chris@16 3651
Chris@26 3652 void
Chris@155 3653 MainWindow::speedUpPlayback()
Chris@155 3654 {
Chris@155 3655 int value = m_playSpeed->value();
Chris@155 3656 value = value + m_playSpeed->pageStep();
Chris@155 3657 if (value > m_playSpeed->maximum()) value = m_playSpeed->maximum();
Chris@155 3658 m_playSpeed->setValue(value);
Chris@155 3659 }
Chris@155 3660
Chris@155 3661 void
Chris@155 3662 MainWindow::slowDownPlayback()
Chris@155 3663 {
Chris@155 3664 int value = m_playSpeed->value();
Chris@155 3665 value = value - m_playSpeed->pageStep();
Chris@155 3666 if (value < m_playSpeed->minimum()) value = m_playSpeed->minimum();
Chris@155 3667 m_playSpeed->setValue(value);
Chris@155 3668 }
Chris@155 3669
Chris@155 3670 void
Chris@155 3671 MainWindow::restoreNormalPlayback()
Chris@155 3672 {
Chris@155 3673 m_playSpeed->setValue(m_playSpeed->defaultValue());
Chris@155 3674 }
Chris@155 3675
Chris@155 3676 void
Chris@227 3677 MainWindow::currentPaneChanged(Pane *pane)
Chris@227 3678 {
Chris@228 3679 MainWindowBase::currentPaneChanged(pane);
Chris@228 3680
Chris@227 3681 if (!pane || !m_panLayer) return;
Chris@227 3682 for (int i = pane->getLayerCount(); i > 0; ) {
Chris@227 3683 --i;
Chris@227 3684 Layer *layer = pane->getLayer(i);
Chris@227 3685 if (LayerFactory::getInstance()->getLayerType(layer) ==
Chris@227 3686 LayerFactory::Waveform) {
Chris@227 3687 RangeSummarisableTimeValueModel *tvm =
Chris@227 3688 dynamic_cast<RangeSummarisableTimeValueModel *>(layer->getModel());
Chris@227 3689 if (tvm) {
Chris@227 3690 m_panLayer->setModel(tvm);
Chris@227 3691 return;
Chris@227 3692 }
Chris@227 3693 }
Chris@227 3694 }
Chris@227 3695 }
Chris@227 3696
Chris@227 3697 void
Chris@116 3698 MainWindow::updateVisibleRangeDisplay(Pane *p) const
Chris@116 3699 {
Chris@116 3700 if (!getMainModel() || !p) {
Chris@116 3701 return;
Chris@116 3702 }
Chris@116 3703
Chris@117 3704 bool haveSelection = false;
Chris@117 3705 size_t startFrame = 0, endFrame = 0;
Chris@117 3706
Chris@117 3707 if (m_viewManager && m_viewManager->haveInProgressSelection()) {
Chris@117 3708
Chris@117 3709 bool exclusive = false;
Chris@117 3710 Selection s = m_viewManager->getInProgressSelection(exclusive);
Chris@117 3711
Chris@117 3712 if (!s.isEmpty()) {
Chris@117 3713 haveSelection = true;
Chris@117 3714 startFrame = s.getStartFrame();
Chris@117 3715 endFrame = s.getEndFrame();
Chris@117 3716 }
Chris@117 3717 }
Chris@117 3718
Chris@117 3719 if (!haveSelection) {
Chris@117 3720 startFrame = p->getFirstVisibleFrame();
Chris@117 3721 endFrame = p->getLastVisibleFrame();
Chris@117 3722 }
Chris@117 3723
Chris@116 3724 RealTime start = RealTime::frame2RealTime
Chris@117 3725 (startFrame, getMainModel()->getSampleRate());
Chris@116 3726
Chris@116 3727 RealTime end = RealTime::frame2RealTime
Chris@117 3728 (endFrame, getMainModel()->getSampleRate());
Chris@116 3729
Chris@116 3730 RealTime duration = end - start;
Chris@116 3731
Chris@116 3732 QString startStr, endStr, durationStr;
Chris@116 3733 startStr = start.toText(true).c_str();
Chris@116 3734 endStr = end.toText(true).c_str();
Chris@116 3735 durationStr = duration.toText(true).c_str();
Chris@116 3736
Chris@117 3737 if (haveSelection) {
Chris@117 3738 m_myStatusMessage = tr("Selection: %1 to %2 (duration %3)")
Chris@117 3739 .arg(startStr).arg(endStr).arg(durationStr);
Chris@117 3740 } else {
Chris@117 3741 m_myStatusMessage = tr("Visible: %1 to %2 (duration %3)")
Chris@117 3742 .arg(startStr).arg(endStr).arg(durationStr);
Chris@117 3743 }
Chris@116 3744
Chris@116 3745 statusBar()->showMessage(m_myStatusMessage);
Chris@340 3746
Chris@340 3747 updatePositionStatusDisplays();
Chris@340 3748 }
Chris@340 3749
Chris@340 3750 void
Chris@340 3751 MainWindow::updatePositionStatusDisplays() const
Chris@340 3752 {
Chris@340 3753 if (!statusBar()->isVisible()) return;
Chris@340 3754
Chris@340 3755 Pane *pane = 0;
Chris@340 3756 size_t frame = m_viewManager->getPlaybackFrame();
Chris@340 3757
Chris@340 3758 if (m_paneStack) pane = m_paneStack->getCurrentPane();
Chris@340 3759 if (!pane) return;
Chris@340 3760
Chris@340 3761 int layers = pane->getLayerCount();
Chris@340 3762 if (layers == 0) m_currentLabel->setText("");
Chris@340 3763
Chris@340 3764 for (int i = layers-1; i >= 0; --i) {
Chris@340 3765 Layer *layer = pane->getLayer(i);
Chris@340 3766 if (!layer) continue;
Chris@340 3767 if (!layer->isLayerEditable()) continue;
Chris@340 3768 QString label = layer->getLabelPreceding
Chris@340 3769 (pane->alignFromReference(frame));
Chris@340 3770 m_currentLabel->setText(label);
Chris@340 3771 break;
Chris@340 3772 }
Chris@116 3773 }
Chris@116 3774
Chris@116 3775 void
Chris@0 3776 MainWindow::outputLevelsChanged(float left, float right)
Chris@0 3777 {
Chris@0 3778 m_fader->setPeakLeft(left);
Chris@0 3779 m_fader->setPeakRight(right);
Chris@0 3780 }
Chris@0 3781
Chris@0 3782 void
Chris@0 3783 MainWindow::sampleRateMismatch(size_t requested, size_t actual,
Chris@0 3784 bool willResample)
Chris@0 3785 {
Chris@0 3786 if (!willResample) {
Chris@247 3787 emit hideSplash();
Chris@0 3788 QMessageBox::information
Chris@0 3789 (this, tr("Sample rate mismatch"),
Chris@193 3790 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 3791 .arg(requested).arg(actual));
Chris@0 3792 }
Chris@0 3793
Chris@0 3794 updateDescriptionLabel();
Chris@0 3795 }
Chris@0 3796
Chris@0 3797 void
Chris@42 3798 MainWindow::audioOverloadPluginDisabled()
Chris@42 3799 {
Chris@42 3800 QMessageBox::information
Chris@42 3801 (this, tr("Audio processing overload"),
Chris@193 3802 tr("<b>Overloaded</b><p>Audio effects plugin auditioning has been disabled due to a processing overload."));
Chris@42 3803 }
Chris@42 3804
Chris@42 3805 void
Chris@266 3806 MainWindow::audioTimeStretchMultiChannelDisabled()
Chris@266 3807 {
Chris@266 3808 static bool shownOnce = false;
Chris@266 3809 if (shownOnce) return;
Chris@266 3810 QMessageBox::information
Chris@266 3811 (this, tr("Audio processing overload"),
Chris@266 3812 tr("<b>Overloaded</b><p>Audio playback speed processing has been reduced to a single channel, due to a processing overload."));
Chris@266 3813 shownOnce = true;
Chris@266 3814 }
Chris@266 3815
Chris@266 3816 void
Chris@304 3817 MainWindow::midiEventsAvailable()
Chris@304 3818 {
Chris@307 3819 Pane *currentPane = 0;
Chris@307 3820 NoteLayer *currentNoteLayer = 0;
Chris@309 3821 TimeValueLayer *currentTimeValueLayer = 0;
Chris@307 3822
Chris@307 3823 if (m_paneStack) currentPane = m_paneStack->getCurrentPane();
Chris@307 3824 if (currentPane) {
Chris@307 3825 currentNoteLayer = dynamic_cast<NoteLayer *>
Chris@307 3826 (currentPane->getSelectedLayer());
Chris@309 3827 currentTimeValueLayer = dynamic_cast<TimeValueLayer *>
Chris@309 3828 (currentPane->getSelectedLayer());
Chris@307 3829 }
Chris@307 3830
Chris@305 3831 // This is called through a serialised signal/slot invocation
Chris@305 3832 // (across threads). It could happen quite some time after the
Chris@305 3833 // event was actually received, which is why event timestamping
Chris@305 3834 // happens in the MIDI input class and not here.
Chris@307 3835
Chris@305 3836 while (m_midiInput->getEventsAvailable() > 0) {
Chris@308 3837
Chris@305 3838 MIDIEvent ev(m_midiInput->readEvent());
Chris@307 3839
Chris@309 3840 size_t frame = currentPane->alignFromReference(ev.getTime());
Chris@309 3841
Chris@308 3842 bool noteOn = (ev.getMessageType() == MIDIConstants::MIDI_NOTE_ON &&
Chris@308 3843 ev.getVelocity() > 0);
Chris@308 3844
Chris@308 3845 bool noteOff = (ev.getMessageType() == MIDIConstants::MIDI_NOTE_OFF ||
Chris@308 3846 (ev.getMessageType() == MIDIConstants::MIDI_NOTE_ON &&
Chris@308 3847 ev.getVelocity() == 0));
Chris@308 3848
Chris@307 3849 if (currentNoteLayer) {
Chris@307 3850
Chris@310 3851 if (!m_playSource || !m_playSource->isPlaying()) continue;
Chris@310 3852
Chris@308 3853 if (noteOn) {
Chris@307 3854
Chris@309 3855 currentNoteLayer->addNoteOn(frame,
Chris@307 3856 ev.getPitch(),
Chris@307 3857 ev.getVelocity());
Chris@307 3858
Chris@308 3859 } else if (noteOff) {
Chris@307 3860
Chris@309 3861 currentNoteLayer->addNoteOff(frame,
Chris@307 3862 ev.getPitch());
Chris@307 3863
Chris@307 3864 }
Chris@307 3865
Chris@309 3866 continue;
Chris@309 3867 }
Chris@309 3868
Chris@309 3869 if (currentTimeValueLayer) {
Chris@308 3870
Chris@308 3871 if (!noteOn) continue;
Chris@310 3872
Chris@310 3873 if (!m_playSource || !m_playSource->isPlaying()) continue;
Chris@310 3874
Chris@309 3875 Model *model = static_cast<Layer *>(currentTimeValueLayer)->getModel();
Chris@309 3876 SparseTimeValueModel *tvm =
Chris@309 3877 dynamic_cast<SparseTimeValueModel *>(model);
Chris@309 3878 if (tvm) {
Chris@309 3879 SparseTimeValueModel::Point point(frame, ev.getPitch() % 12, "");
Chris@309 3880 SparseTimeValueModel::AddPointCommand *command =
Chris@309 3881 new SparseTimeValueModel::AddPointCommand
Chris@309 3882 (tvm, point, tr("Add Point"));
Chris@309 3883 CommandHistory::getInstance()->addCommand(command);
Chris@309 3884 }
Chris@309 3885 continue;
Chris@309 3886
Chris@305 3887 }
Chris@309 3888
Chris@309 3889 if (!noteOn) continue;
Chris@309 3890 insertInstantAt(ev.getTime());
Chris@304 3891 }
Chris@304 3892 }
Chris@304 3893
Chris@304 3894 void
Chris@305 3895 MainWindow::playStatusChanged(bool playing)
Chris@305 3896 {
Chris@307 3897 Pane *currentPane = 0;
Chris@307 3898 NoteLayer *currentNoteLayer = 0;
Chris@307 3899
Chris@307 3900 if (m_paneStack) currentPane = m_paneStack->getCurrentPane();
Chris@307 3901 if (currentPane) {
Chris@307 3902 currentNoteLayer = dynamic_cast<NoteLayer *>(currentPane->getSelectedLayer());
Chris@307 3903 }
Chris@307 3904
Chris@307 3905 if (currentNoteLayer) {
Chris@307 3906 currentNoteLayer->abandonNoteOns();
Chris@307 3907 }
Chris@305 3908 }
Chris@305 3909
Chris@305 3910 void
Chris@200 3911 MainWindow::layerRemoved(Layer *layer)
Chris@0 3912 {
Chris@95 3913 setupExistingLayersMenus();
Chris@200 3914 MainWindowBase::layerRemoved(layer);
Chris@0 3915 }
Chris@0 3916
Chris@0 3917 void
Chris@0 3918 MainWindow::layerInAView(Layer *layer, bool inAView)
Chris@0 3919 {
Chris@95 3920 setupExistingLayersMenus();
Chris@200 3921 MainWindowBase::layerInAView(layer, inAView);
Chris@0 3922 }
Chris@0 3923
Chris@0 3924 void
Chris@0 3925 MainWindow::modelAdded(Model *model)
Chris@0 3926 {
Chris@200 3927 MainWindowBase::modelAdded(model);
Chris@66 3928 if (dynamic_cast<DenseTimeValueModel *>(model)) {
Chris@66 3929 setupPaneAndLayerMenus();
Chris@66 3930 }
Chris@0 3931 }
Chris@0 3932
Chris@0 3933 void
Chris@0 3934 MainWindow::mainModelChanged(WaveFileModel *model)
Chris@0 3935 {
Chris@0 3936 m_panLayer->setModel(model);
Chris@200 3937
Chris@200 3938 MainWindowBase::mainModelChanged(model);
Chris@200 3939
Chris@200 3940 if (m_playTarget) {
Chris@200 3941 connect(m_fader, SIGNAL(valueChanged(float)),
Chris@200 3942 m_playTarget, SLOT(setOutputGain(float)));
Chris@200 3943 }
Chris@0 3944 }
Chris@0 3945
Chris@0 3946 void
Chris@200 3947 MainWindow::setInstantsNumbering()
Chris@0 3948 {
Chris@200 3949 QAction *a = dynamic_cast<QAction *>(sender());
Chris@200 3950 if (!a) return;
Chris@200 3951
Chris@200 3952 int type = m_numberingActions[a];
Chris@200 3953
Chris@200 3954 if (m_labeller) m_labeller->setType(Labeller::ValueType(type));
Chris@200 3955
Chris@200 3956 QSettings settings;
Chris@200 3957 settings.beginGroup("MainWindow");
Chris@200 3958 settings.setValue("labellertype", type);
Chris@200 3959 settings.endGroup();
Chris@200 3960 }
Chris@200 3961
Chris@200 3962 void
Chris@200 3963 MainWindow::setInstantsCounterCycle()
Chris@200 3964 {
Chris@200 3965 QAction *a = dynamic_cast<QAction *>(sender());
Chris@200 3966 if (!a) return;
Chris@200 3967
Chris@200 3968 int cycle = a->text().toInt();
Chris@200 3969 if (cycle == 0) return;
Chris@200 3970
Chris@200 3971 if (m_labeller) m_labeller->setCounterCycleSize(cycle);
Chris@200 3972
Chris@200 3973 QSettings settings;
Chris@200 3974 settings.beginGroup("MainWindow");
Chris@200 3975 settings.setValue("labellercycle", cycle);
Chris@200 3976 settings.endGroup();
Chris@200 3977 }
Chris@200 3978
Chris@200 3979 void
Chris@200 3980 MainWindow::resetInstantsCounters()
Chris@200 3981 {
Chris@200 3982 LabelCounterInputDialog dialog(m_labeller, this);
Chris@241 3983 dialog.setWindowTitle(tr("Reset Counters"));
Chris@200 3984 dialog.exec();
Chris@0 3985 }
Chris@0 3986
Chris@0 3987 void
Chris@233 3988 MainWindow::modelGenerationFailed(QString transformName, QString message)
Chris@233 3989 {
Chris@247 3990 emit hideSplash();
Chris@247 3991
Chris@233 3992 if (message != "") {
Chris@233 3993
Chris@233 3994 QMessageBox::warning
Chris@233 3995 (this,
Chris@233 3996 tr("Failed to generate layer"),
Chris@233 3997 tr("<b>Layer generation failed</b><p>Failed to generate derived layer.<p>The layer transform \"%1\" failed:<p>%2")
Chris@233 3998 .arg(transformName).arg(message),
Chris@233 3999 QMessageBox::Ok);
Chris@233 4000 } else {
Chris@233 4001 QMessageBox::warning
Chris@233 4002 (this,
Chris@233 4003 tr("Failed to generate layer"),
Chris@233 4004 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 4005 .arg(transformName),
Chris@233 4006 QMessageBox::Ok);
Chris@233 4007 }
Chris@233 4008 }
Chris@233 4009
Chris@233 4010 void
Chris@233 4011 MainWindow::modelGenerationWarning(QString transformName, QString message)
Chris@233 4012 {
Chris@247 4013 emit hideSplash();
Chris@247 4014
Chris@233 4015 QMessageBox::warning
Chris@233 4016 (this, tr("Warning"), message, QMessageBox::Ok);
Chris@233 4017 }
Chris@233 4018
Chris@233 4019 void
Chris@233 4020 MainWindow::modelRegenerationFailed(QString layerName,
Chris@233 4021 QString transformName, QString message)
Chris@233 4022 {
Chris@247 4023 emit hideSplash();
Chris@247 4024
Chris@233 4025 if (message != "") {
Chris@233 4026
Chris@233 4027 QMessageBox::warning
Chris@233 4028 (this,
Chris@233 4029 tr("Failed to regenerate layer"),
Chris@233 4030 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 4031 .arg(layerName).arg(transformName).arg(message),
Chris@233 4032 QMessageBox::Ok);
Chris@233 4033 } else {
Chris@233 4034 QMessageBox::warning
Chris@233 4035 (this,
Chris@233 4036 tr("Failed to regenerate layer"),
Chris@233 4037 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 4038 .arg(layerName).arg(transformName),
Chris@233 4039 QMessageBox::Ok);
Chris@233 4040 }
Chris@233 4041 }
Chris@233 4042
Chris@233 4043 void
Chris@233 4044 MainWindow::modelRegenerationWarning(QString layerName,
Chris@233 4045 QString transformName, QString message)
Chris@233 4046 {
Chris@247 4047 emit hideSplash();
Chris@247 4048
Chris@233 4049 QMessageBox::warning
Chris@233 4050 (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 4051 }
Chris@233 4052
Chris@233 4053 void
Chris@233 4054 MainWindow::alignmentFailed(QString transformName, QString message)
Chris@0 4055 {
Chris@247 4056 emit hideSplash();
Chris@247 4057
Chris@0 4058 QMessageBox::warning
Chris@0 4059 (this,
Chris@233 4060 tr("Failed to calculate alignment"),
Chris@233 4061 tr("<b>Alignment calculation failed</b><p>Failed to calculate an audio alignment using transform \"%1\":<p>%2")
Chris@233 4062 .arg(transformName).arg(message),
Chris@165 4063 QMessageBox::Ok);
Chris@0 4064 }
Chris@0 4065
Chris@0 4066 void
Chris@0 4067 MainWindow::rightButtonMenuRequested(Pane *pane, QPoint position)
Chris@0 4068 {
Chris@438 4069 // SVDEBUG << "MainWindow::rightButtonMenuRequested(" << pane << ", " << position.x() << ", " << position.y() << ")" << endl;
Chris@0 4070 m_paneStack->setCurrentPane(pane);
Chris@0 4071 m_rightButtonMenu->popup(position);
Chris@0 4072 }
Chris@0 4073
Chris@0 4074 void
Chris@0 4075 MainWindow::showLayerTree()
Chris@0 4076 {
Chris@219 4077 if (!m_layerTreeDialog.isNull()) {
Chris@219 4078 m_layerTreeDialog->show();
Chris@219 4079 m_layerTreeDialog->raise();
Chris@177 4080 return;
Chris@177 4081 }
Chris@177 4082
Chris@219 4083 m_layerTreeDialog = new LayerTreeDialog(m_paneStack);
Chris@232 4084 m_layerTreeDialog->setAttribute(Qt::WA_DeleteOnClose); // see below
Chris@219 4085 m_layerTreeDialog->show();
Chris@0 4086 }
Chris@0 4087
Chris@0 4088 void
Chris@306 4089 MainWindow::showActivityLog()
Chris@306 4090 {
Chris@306 4091 m_activityLog->show();
Chris@306 4092 m_activityLog->raise();
Chris@306 4093 m_activityLog->scrollToEnd();
Chris@306 4094 }
Chris@306 4095
Chris@306 4096 void
Chris@0 4097 MainWindow::preferences()
Chris@0 4098 {
Chris@436 4099 bool goToTemplateTab =
Chris@436 4100 (sender() && sender()->objectName() == "set_default_template");
Chris@436 4101
Chris@0 4102 if (!m_preferencesDialog.isNull()) {
Chris@0 4103 m_preferencesDialog->show();
Chris@0 4104 m_preferencesDialog->raise();
Chris@436 4105 if (goToTemplateTab) {
Chris@436 4106 m_preferencesDialog->switchToTab(PreferencesDialog::TemplateTab);
Chris@436 4107 }
Chris@0 4108 return;
Chris@0 4109 }
Chris@0 4110
Chris@0 4111 m_preferencesDialog = new PreferencesDialog(this);
Chris@0 4112
Chris@0 4113 // DeleteOnClose is safe here, because m_preferencesDialog is a
Chris@0 4114 // QPointer that will be zeroed when the dialog is deleted. We
Chris@0 4115 // use it in preference to leaving the dialog lying around because
Chris@0 4116 // if you Cancel the dialog, it resets the preferences state
Chris@0 4117 // without resetting its own widgets, so its state will be
Chris@0 4118 // incorrect when next shown unless we construct it afresh
Chris@0 4119 m_preferencesDialog->setAttribute(Qt::WA_DeleteOnClose);
Chris@0 4120
Chris@0 4121 m_preferencesDialog->show();
Chris@436 4122 if (goToTemplateTab) {
Chris@436 4123 m_preferencesDialog->switchToTab(PreferencesDialog::TemplateTab);
Chris@436 4124 }
Chris@0 4125 }
Chris@0 4126
Chris@0 4127 void
Chris@90 4128 MainWindow::mouseEnteredWidget()
Chris@90 4129 {
Chris@90 4130 QWidget *w = dynamic_cast<QWidget *>(sender());
Chris@90 4131 if (!w) return;
Chris@90 4132
Chris@90 4133 if (w == m_fader) {
Chris@116 4134 contextHelpChanged(tr("Adjust the master playback level"));
Chris@90 4135 } else if (w == m_playSpeed) {
Chris@116 4136 contextHelpChanged(tr("Adjust the master playback speed"));
Chris@90 4137 }
Chris@90 4138 }
Chris@90 4139
Chris@90 4140 void
Chris@90 4141 MainWindow::mouseLeftWidget()
Chris@90 4142 {
Chris@116 4143 contextHelpChanged("");
Chris@116 4144 }
Chris@116 4145
Chris@116 4146 void
Chris@0 4147 MainWindow::website()
Chris@0 4148 {
Chris@0 4149 openHelpUrl(tr("http://www.sonicvisualiser.org/"));
Chris@0 4150 }
Chris@0 4151
Chris@0 4152 void
Chris@0 4153 MainWindow::help()
Chris@0 4154 {
Chris@318 4155 openHelpUrl(tr("http://www.sonicvisualiser.org/doc/reference/%1/en/").arg(SV_VERSION));
Chris@0 4156 }
Chris@0 4157
Chris@0 4158 void
Chris@0 4159 MainWindow::about()
Chris@0 4160 {
Chris@0 4161 bool debug = false;
Chris@0 4162 QString version = "(unknown version)";
Chris@0 4163
Chris@0 4164 #ifdef BUILD_DEBUG
Chris@0 4165 debug = true;
Chris@285 4166 #endif // BUILD_DEBUG
Chris@0 4167 #ifdef SV_VERSION
Chris@0 4168 #ifdef SVNREV
Chris@0 4169 version = tr("Release %1 : Revision %2").arg(SV_VERSION).arg(SVNREV);
Chris@285 4170 #else // !SVNREV
Chris@0 4171 version = tr("Release %1").arg(SV_VERSION);
Chris@285 4172 #endif // SVNREV
Chris@285 4173 #else // !SV_VERSION
Chris@0 4174 #ifdef SVNREV
Chris@0 4175 version = tr("Unreleased : Revision %1").arg(SVNREV);
Chris@285 4176 #endif // SVNREV
Chris@285 4177 #endif // SV_VERSION
Chris@0 4178
Chris@0 4179 QString aboutText;
Chris@0 4180
Chris@0 4181 aboutText += tr("<h3>About Sonic Visualiser</h3>");
Chris@285 4182 aboutText += tr("<p>Sonic Visualiser is a program for viewing and exploring audio data for semantic music analysis and annotation.<br><a href=\"http://www.sonicvisualiser.org/\">http://www.sonicvisualiser.org/</a></p>");
Chris@285 4183 aboutText += tr("<p><small>%1 : %2 configuration</small></p>")
Chris@0 4184 .arg(version)
Chris@0 4185 .arg(debug ? tr("Debug") : tr("Release"));
Chris@0 4186
Chris@285 4187 aboutText += "<small>";
Chris@285 4188
Chris@285 4189 aboutText += tr("With Qt v%1 &copy; Nokia Corporation").arg(QT_VERSION_STR);
Chris@285 4190
Chris@0 4191 #ifdef HAVE_JACK
Chris@93 4192 #ifdef JACK_VERSION
Chris@285 4193 aboutText += tr("<br>With JACK audio output library v%1 &copy; Paul Davis and Jack O'Quin").arg(JACK_VERSION);
Chris@285 4194 #else // !JACK_VERSION
Chris@257 4195 aboutText += tr("<br>With JACK audio output library &copy; Paul Davis and Jack O'Quin");
Chris@285 4196 #endif // JACK_VERSION
Chris@285 4197 #endif // HAVE_JACK
Chris@0 4198 #ifdef HAVE_PORTAUDIO
Chris@257 4199 aboutText += tr("<br>With PortAudio audio output library &copy; Ross Bencina and Phil Burk");
Chris@285 4200 #endif // HAVE_PORTAUDIO
Chris@257 4201 #ifdef HAVE_LIBPULSE
Chris@285 4202 #ifdef LIBPULSE_VERSION
Chris@285 4203 aboutText += tr("<br>With PulseAudio audio output library v%1 &copy; Lennart Poettering and Pierre Ossman").arg(LIBPULSE_VERSION);
Chris@285 4204 #else // !LIBPULSE_VERSION
Chris@257 4205 aboutText += tr("<br>With PulseAudio audio output library &copy; Lennart Poettering and Pierre Ossman");
Chris@285 4206 #endif // LIBPULSE_VERSION
Chris@285 4207 #endif // HAVE_LIBPULSE
Chris@0 4208 #ifdef HAVE_OGGZ
Chris@93 4209 #ifdef OGGZ_VERSION
Chris@0 4210 aboutText += tr("<br>With Ogg file decoder (oggz v%1, fishsound v%2) &copy; CSIRO Australia").arg(OGGZ_VERSION).arg(FISHSOUND_VERSION);
Chris@285 4211 #else // !OGGZ_VERSION
Chris@93 4212 aboutText += tr("<br>With Ogg file decoder &copy; CSIRO Australia");
Chris@285 4213 #endif // OGGZ_VERSION
Chris@285 4214 #endif // HAVE_OGGZ
Chris@0 4215 #ifdef HAVE_MAD
Chris@93 4216 #ifdef MAD_VERSION
Chris@285 4217 aboutText += tr("<br>With MAD mp3 decoder v%1 &copy; Underbit Technologies Inc").arg(MAD_VERSION);
Chris@285 4218 #else // !MAD_VERSION
Chris@93 4219 aboutText += tr("<br>With MAD mp3 decoder &copy; Underbit Technologies Inc");
Chris@285 4220 #endif // MAD_VERSION
Chris@285 4221 #endif // HAVE_MAD
Chris@0 4222 #ifdef HAVE_SAMPLERATE
Chris@93 4223 #ifdef SAMPLERATE_VERSION
Chris@285 4224 aboutText += tr("<br>With libsamplerate v%1 &copy; Erik de Castro Lopo").arg(SAMPLERATE_VERSION);
Chris@285 4225 #else // !SAMPLERATE_VERSION
Chris@93 4226 aboutText += tr("<br>With libsamplerate &copy; Erik de Castro Lopo");
Chris@285 4227 #endif // SAMPLERATE_VERSION
Chris@285 4228 #endif // HAVE_SAMPLERATE
Chris@0 4229 #ifdef HAVE_SNDFILE
Chris@93 4230 #ifdef SNDFILE_VERSION
Chris@285 4231 aboutText += tr("<br>With libsndfile v%1 &copy; Erik de Castro Lopo").arg(SNDFILE_VERSION);
Chris@285 4232 #else // !SNDFILE_VERSION
Chris@93 4233 aboutText += tr("<br>With libsndfile &copy; Erik de Castro Lopo");
Chris@285 4234 #endif // SNDFILE_VERSION
Chris@285 4235 #endif // HAVE_SNDFILE
Chris@127 4236 #ifdef HAVE_FFTW3F
Chris@93 4237 #ifdef FFTW3_VERSION
Chris@285 4238 aboutText += tr("<br>With FFTW3 v%1 &copy; Matteo Frigo and MIT").arg(FFTW3_VERSION);
Chris@285 4239 #else // !FFTW3_VERSION
Chris@93 4240 aboutText += tr("<br>With FFTW3 &copy; Matteo Frigo and MIT");
Chris@285 4241 #endif // FFTW3_VERSION
Chris@285 4242 #endif // HAVE_FFTW3F
Chris@267 4243 #ifdef HAVE_RUBBERBAND
Chris@267 4244 #ifdef RUBBERBAND_VERSION
Chris@285 4245 aboutText += tr("<br>With Rubber Band v%1 &copy; Chris Cannam").arg(RUBBERBAND_VERSION);
Chris@285 4246 #else // !RUBBERBAND_VERSION
Chris@267 4247 aboutText += tr("<br>With Rubber Band &copy; Chris Cannam");
Chris@285 4248 #endif // RUBBERBAND_VERSION
Chris@285 4249 #endif // HAVE_RUBBERBAND
Chris@0 4250 #ifdef HAVE_VAMP
Chris@114 4251 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 4252 #endif // !HAVE_VAMP
Chris@0 4253 aboutText += tr("<br>With LADSPA plugin support (API v%1) &copy; Richard Furse, Paul Davis, Stefan Westerfeld").arg(LADSPA_VERSION);
Chris@0 4254 aboutText += tr("<br>With DSSI plugin support (API v%1) &copy; Chris Cannam, Steve Harris, Sean Bolton").arg(DSSI_VERSION);
Chris@285 4255 #ifdef RAPTOR_VERSION
Chris@285 4256 aboutText += tr("<br>With Raptor RDF parser v%1 &copy; Dave Beckett and the University of Bristol").arg(RAPTOR_VERSION);
Chris@285 4257 #else // !RAPTOR_VERSION
Chris@285 4258 aboutText += tr("<br>With Raptor RDF parser &copy; Dave Beckett and the University of Bristol");
Chris@285 4259 #endif // RAPTOR_VERSION
Chris@285 4260 #ifdef RASQAL_VERSION
Chris@285 4261 aboutText += tr("<br>With Rasqal RDF query engine v%1 &copy; Dave Beckett and the University of Bristol").arg(RASQAL_VERSION);
Chris@285 4262 #else // !RASQAL_VERSION
Chris@285 4263 aboutText += tr("<br>With Rasqal RDF query engine &copy; Dave Beckett and the University of Bristol");
Chris@285 4264 #endif // RASQAL_VERSION
Chris@285 4265 #ifdef HAVE_REDLAND
Chris@285 4266 #ifdef REDLAND_VERSION
Chris@285 4267 aboutText += tr("<br>With Redland RDF datastore v%1 &copy; Dave Beckett and the University of Bristol").arg(REDLAND_VERSION);
Chris@285 4268 #else // !REDLAND_VERSION
Chris@285 4269 aboutText += tr("<br>With Redland RDF datastore &copy; Dave Beckett and the University of Bristol");
Chris@285 4270 #endif // REDLAND_VERSION
Chris@285 4271 #endif // HAVE_REDLAND
Chris@285 4272
Chris@300 4273 aboutText += tr("<br>With RtMidi &copy; Gary P. Scavone");
Chris@300 4274
Chris@69 4275 #ifdef HAVE_LIBLO
Chris@93 4276 #ifdef LIBLO_VERSION
Chris@285 4277 aboutText += tr("<br>With liblo Lite OSC library v%1 &copy; Steve Harris").arg(LIBLO_VERSION);
Chris@285 4278 #else // !LIBLO_VERSION
Chris@327 4279 aboutText += tr("<br>With liblo Lite OSC library &copy; Steve Harris");
Chris@285 4280 #endif // LIBLO_VERSION
Chris@285 4281
Chris@285 4282 if (m_oscQueue && m_oscQueue->isOK()) {
Chris@285 4283 aboutText += tr("</small><p><small>The OSC URL for this instance is: \"%1\"").arg(m_oscQueue->getOSCURL());
Chris@285 4284 }
Chris@285 4285
Chris@285 4286 aboutText += "</small></p>";
Chris@285 4287 #endif // HAVE_LIBLO
Chris@285 4288
Chris@285 4289 #ifndef BUILD_STATIC
Chris@285 4290 aboutText.replace(tr("With "), tr("Using "));
Chris@93 4291 #endif
Chris@0 4292
Chris@0 4293 aboutText +=
Chris@392 4294 "<p><small>Sonic Visualiser Copyright &copy; 2005&ndash;2011 Chris Cannam and "
Chris@285 4295 "Queen Mary, University of London.</small></p>"
Chris@285 4296 "<p><small>This program is free software; you can redistribute it and/or "
Chris@231 4297 "modify it under the terms of the GNU General Public License as "
Chris@231 4298 "published by the Free Software Foundation; either version 2 of the "
Chris@0 4299 "License, or (at your option) any later version.<br>See the file "
Chris@285 4300 "COPYING included with this distribution for more information.</small></p>";
Chris@0 4301
Chris@0 4302 QMessageBox::about(this, tr("About Sonic Visualiser"), aboutText);
Chris@0 4303 }
Chris@0 4304
Chris@162 4305 void
Chris@162 4306 MainWindow::keyReference()
Chris@162 4307 {
Chris@162 4308 m_keyReference->show();
Chris@162 4309 }
Chris@162 4310
Chris@333 4311 void
Chris@333 4312 MainWindow::newerVersionAvailable(QString version)
Chris@333 4313 {
Chris@334 4314 QSettings settings;
Chris@334 4315 settings.beginGroup("NewerVersionWarning");
Chris@334 4316 QString tag = QString("version-%1-available-show").arg(version);
Chris@334 4317 if (settings.value(tag, true).toBool()) {
Chris@334 4318 QString title(tr("Newer version available"));
Chris@334 4319 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 4320 QMessageBox::information(this, title, text);
Chris@334 4321 settings.setValue(tag, false);
Chris@334 4322 }
Chris@334 4323 settings.endGroup();
Chris@333 4324 }
Chris@333 4325
Chris@333 4326