annotate main/MainWindow.cpp @ 232:99e0dfd3ea75

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