annotate main/MainWindow.cpp @ 180:98ba77e0d897

* Merge from sv-match-alignment branch (excluding alignment-specific document). - add aggregate wave model (not yet complete enough to be added as a true model in a layer, but there's potential) - add play solo mode - add alignment model -- unused in plain SV - fix two plugin leaks - add m3u playlist support (opens all files at once, potentially hazardous) - fix retrieval of pre-encoded URLs - add ability to resample audio files on import, so as to match rates with other files previously loaded; add preference for same - add preliminary support in transform code for range and rate of transform input - reorganise preferences dialog, move dark-background option to preferences, add option for temporary directory location
author Chris Cannam
date Fri, 28 Sep 2007 13:56:38 +0000
parents 5bde373ad5ca
children ebd906049fb6
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@77 7 This file copyright 2006 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@1 19 #include "document/Document.h"
Chris@0 20 #include "PreferencesDialog.h"
Chris@0 21
Chris@1 22 #include "view/Pane.h"
Chris@1 23 #include "view/PaneStack.h"
Chris@1 24 #include "data/model/WaveFileModel.h"
Chris@1 25 #include "data/model/SparseOneDimensionalModel.h"
Chris@1 26 #include "view/ViewManager.h"
Chris@0 27 #include "base/Preferences.h"
Chris@0 28 #include "layer/WaveformLayer.h"
Chris@0 29 #include "layer/TimeRulerLayer.h"
Chris@0 30 #include "layer/TimeInstantLayer.h"
Chris@0 31 #include "layer/TimeValueLayer.h"
Chris@0 32 #include "layer/Colour3DPlotLayer.h"
Chris@95 33 #include "layer/SliceLayer.h"
Chris@95 34 #include "layer/SliceableLayer.h"
Chris@0 35 #include "widgets/Fader.h"
Chris@65 36 #include "view/Overview.h"
Chris@0 37 #include "widgets/PropertyBox.h"
Chris@0 38 #include "widgets/PropertyStack.h"
Chris@0 39 #include "widgets/AudioDial.h"
Chris@168 40 #include "widgets/IconLoader.h"
Chris@0 41 #include "widgets/LayerTree.h"
Chris@0 42 #include "widgets/ListInputDialog.h"
Chris@36 43 #include "widgets/SubdividingMenu.h"
Chris@90 44 #include "widgets/NotifyingPushButton.h"
Chris@162 45 #include "widgets/KeyReference.h"
Chris@0 46 #include "audioio/AudioCallbackPlaySource.h"
Chris@0 47 #include "audioio/AudioCallbackPlayTarget.h"
Chris@0 48 #include "audioio/AudioTargetFactory.h"
Chris@59 49 #include "audioio/PlaySpeedRangeMapper.h"
Chris@1 50 #include "data/fileio/DataFileReaderFactory.h"
Chris@180 51 #include "data/fileio/PlaylistFileReader.h"
Chris@1 52 #include "data/fileio/WavFileWriter.h"
Chris@1 53 #include "data/fileio/CSVFileWriter.h"
Chris@1 54 #include "data/fileio/BZipFileDevice.h"
Chris@85 55 #include "data/fileio/RemoteFile.h"
Chris@91 56 #include "data/fft/FFTDataServer.h"
Chris@1 57 #include "base/RecentFiles.h"
Chris@0 58 #include "transform/TransformFactory.h"
Chris@0 59 #include "base/PlayParameterRepository.h"
Chris@0 60 #include "base/XmlExportable.h"
Chris@0 61 #include "base/CommandHistory.h"
Chris@0 62 #include "base/Profiler.h"
Chris@0 63 #include "base/Clipboard.h"
Chris@165 64 #include "base/UnitDatabase.h"
Chris@165 65 #include "base/ColourDatabase.h"
Chris@69 66 #include "osc/OSCQueue.h"
Chris@0 67
Chris@180 68 //!!!
Chris@180 69 #include "data/model/AggregateWaveModel.h"
Chris@180 70
Chris@0 71 // For version information
Chris@0 72 #include "vamp/vamp.h"
Chris@0 73 #include "vamp-sdk/PluginBase.h"
Chris@0 74 #include "plugin/api/ladspa.h"
Chris@0 75 #include "plugin/api/dssi.h"
Chris@0 76
Chris@0 77 #include <QApplication>
Chris@0 78 #include <QMessageBox>
Chris@0 79 #include <QGridLayout>
Chris@0 80 #include <QLabel>
Chris@0 81 #include <QAction>
Chris@0 82 #include <QMenuBar>
Chris@0 83 #include <QToolBar>
Chris@0 84 #include <QInputDialog>
Chris@0 85 #include <QStatusBar>
Chris@0 86 #include <QTreeView>
Chris@0 87 #include <QFile>
Chris@88 88 #include <QFileInfo>
Chris@88 89 #include <QDir>
Chris@0 90 #include <QTextStream>
Chris@0 91 #include <QProcess>
Chris@7 92 #include <QShortcut>
Chris@5 93 #include <QSettings>
Chris@11 94 #include <QDateTime>
Chris@11 95 #include <QProcess>
Chris@16 96 #include <QCheckBox>
Chris@55 97 #include <QRegExp>
Chris@158 98 #include <QScrollArea>
Chris@0 99
Chris@0 100 #include <iostream>
Chris@0 101 #include <cstdio>
Chris@0 102 #include <errno.h>
Chris@0 103
Chris@0 104 using std::cerr;
Chris@0 105 using std::endl;
Chris@0 106
Chris@33 107 using std::vector;
Chris@33 108 using std::map;
Chris@33 109 using std::set;
Chris@33 110
Chris@0 111
Chris@70 112 MainWindow::MainWindow(bool withAudioOutput, bool withOSCSupport) :
Chris@0 113 m_document(0),
Chris@0 114 m_paneStack(0),
Chris@0 115 m_viewManager(0),
Chris@65 116 m_overview(0),
Chris@0 117 m_timeRulerLayer(0),
Chris@46 118 m_audioOutput(withAudioOutput),
Chris@0 119 m_playSource(0),
Chris@0 120 m_playTarget(0),
Chris@70 121 m_oscQueue(withOSCSupport ? new OSCQueue() : 0),
Chris@81 122 m_recentFiles("RecentFiles", 20),
Chris@49 123 m_recentTransforms("RecentTransforms", 20),
Chris@0 124 m_mainMenusCreated(false),
Chris@0 125 m_paneMenu(0),
Chris@0 126 m_layerMenu(0),
Chris@34 127 m_transformsMenu(0),
Chris@155 128 m_playbackMenu(0),
Chris@0 129 m_existingLayersMenu(0),
Chris@95 130 m_sliceMenu(0),
Chris@34 131 m_recentFilesMenu(0),
Chris@34 132 m_recentTransformsMenu(0),
Chris@0 133 m_rightButtonMenu(0),
Chris@0 134 m_rightButtonLayerMenu(0),
Chris@34 135 m_rightButtonTransformsMenu(0),
Chris@155 136 m_rightButtonPlaybackMenu(0),
Chris@155 137 m_ffwdAction(0),
Chris@155 138 m_rwdAction(0),
Chris@0 139 m_documentModified(false),
Chris@70 140 m_openingAudioFile(false),
Chris@70 141 m_abandoning(false),
Chris@162 142 m_preferencesDialog(0),
Chris@177 143 m_layerTreeView(0),
Chris@162 144 m_keyReference(new KeyReference())
Chris@0 145 {
Chris@0 146 setWindowTitle(tr("Sonic Visualiser"));
Chris@0 147
Chris@165 148 UnitDatabase *udb = UnitDatabase::getInstance();
Chris@165 149 udb->registerUnit("Hz");
Chris@165 150 udb->registerUnit("dB");
Chris@165 151 udb->registerUnit("s");
Chris@165 152
Chris@165 153 ColourDatabase *cdb = ColourDatabase::getInstance();
Chris@165 154 cdb->addColour(Qt::black, tr("Black"));
Chris@165 155 cdb->addColour(Qt::darkRed, tr("Red"));
Chris@165 156 cdb->addColour(Qt::darkBlue, tr("Blue"));
Chris@165 157 cdb->addColour(Qt::darkGreen, tr("Green"));
Chris@165 158 cdb->addColour(QColor(200, 50, 255), tr("Purple"));
Chris@165 159 cdb->addColour(QColor(255, 150, 50), tr("Orange"));
Chris@166 160 cdb->setUseDarkBackground(cdb->addColour(Qt::white, tr("White")), true);
Chris@166 161 cdb->setUseDarkBackground(cdb->addColour(Qt::red, tr("Bright Red")), true);
Chris@166 162 cdb->setUseDarkBackground(cdb->addColour(QColor(30, 150, 255), tr("Bright Blue")), true);
Chris@166 163 cdb->setUseDarkBackground(cdb->addColour(Qt::green, tr("Bright Green")), true);
Chris@166 164 cdb->setUseDarkBackground(cdb->addColour(QColor(225, 74, 255), tr("Bright Purple")), true);
Chris@166 165 cdb->setUseDarkBackground(cdb->addColour(QColor(255, 188, 80), tr("Bright Orange")), true);
Chris@0 166
Chris@0 167 connect(CommandHistory::getInstance(), SIGNAL(commandExecuted()),
Chris@0 168 this, SLOT(documentModified()));
Chris@0 169 connect(CommandHistory::getInstance(), SIGNAL(documentRestored()),
Chris@0 170 this, SLOT(documentRestored()));
Chris@0 171
Chris@0 172 QFrame *frame = new QFrame;
Chris@0 173 setCentralWidget(frame);
Chris@0 174
Chris@0 175 QGridLayout *layout = new QGridLayout;
Chris@0 176
Chris@0 177 m_viewManager = new ViewManager();
Chris@0 178 connect(m_viewManager, SIGNAL(selectionChanged()),
Chris@0 179 this, SLOT(updateMenuStates()));
Chris@117 180 connect(m_viewManager, SIGNAL(inProgressSelectionChanged()),
Chris@117 181 this, SLOT(inProgressSelectionChanged()));
Chris@0 182
Chris@180 183 Preferences::BackgroundMode mode =
Chris@180 184 Preferences::getInstance()->getBackgroundMode();
Chris@180 185 m_initialDarkBackground = m_viewManager->getGlobalDarkBackground();
Chris@180 186 if (mode != Preferences::BackgroundFromTheme) {
Chris@180 187 m_viewManager->setGlobalDarkBackground
Chris@180 188 (mode == Preferences::DarkBackground);
Chris@180 189 }
Chris@180 190
Chris@0 191 m_descriptionLabel = new QLabel;
Chris@0 192
Chris@158 193 QScrollArea *scroll = new QScrollArea(frame);
Chris@158 194 scroll->setWidgetResizable(true);
Chris@158 195 scroll->setHorizontalScrollBarPolicy(Qt::ScrollBarAlwaysOff);
Chris@159 196 scroll->setFrameShape(QFrame::NoFrame);
Chris@159 197
Chris@159 198 m_paneStack = new PaneStack(scroll, m_viewManager);
Chris@0 199 connect(m_paneStack, SIGNAL(currentPaneChanged(Pane *)),
Chris@0 200 this, SLOT(currentPaneChanged(Pane *)));
Chris@0 201 connect(m_paneStack, SIGNAL(currentLayerChanged(Pane *, Layer *)),
Chris@0 202 this, SLOT(currentLayerChanged(Pane *, Layer *)));
Chris@0 203 connect(m_paneStack, SIGNAL(rightButtonMenuRequested(Pane *, QPoint)),
Chris@0 204 this, SLOT(rightButtonMenuRequested(Pane *, QPoint)));
Chris@73 205 connect(m_paneStack, SIGNAL(propertyStacksResized()),
Chris@73 206 this, SLOT(propertyStacksResized()));
Chris@90 207 connect(m_paneStack, SIGNAL(contextHelpChanged(const QString &)),
Chris@116 208 this, SLOT(contextHelpChanged(const QString &)));
Chris@0 209
Chris@159 210 scroll->setWidget(m_paneStack);
Chris@159 211
Chris@65 212 m_overview = new Overview(frame);
Chris@65 213 m_overview->setViewManager(m_viewManager);
Chris@65 214 m_overview->setFixedHeight(40);
Chris@144 215 #ifndef _WIN32
Chris@144 216 // For some reason, the contents of the overview never appear if we
Chris@144 217 // make this setting on Windows. I have no inclination at the moment
Chris@144 218 // to track down the reason why.
Chris@129 219 m_overview->setFrameStyle(QFrame::StyledPanel | QFrame::Sunken);
Chris@144 220 #endif
Chris@90 221 connect(m_overview, SIGNAL(contextHelpChanged(const QString &)),
Chris@116 222 this, SLOT(contextHelpChanged(const QString &)));
Chris@0 223
Chris@0 224 m_panLayer = new WaveformLayer;
Chris@0 225 m_panLayer->setChannelMode(WaveformLayer::MergeChannels);
Chris@0 226 m_panLayer->setAggressiveCacheing(true);
Chris@65 227 m_overview->addLayer(m_panLayer);
Chris@174 228
Chris@174 229 if (m_viewManager->getGlobalDarkBackground()) {
Chris@174 230 m_panLayer->setBaseColour
Chris@174 231 (ColourDatabase::getInstance()->getColourIndex(tr("Bright Green")));
Chris@174 232 } else {
Chris@174 233 m_panLayer->setBaseColour
Chris@174 234 (ColourDatabase::getInstance()->getColourIndex(tr("Green")));
Chris@174 235 }
Chris@0 236
Chris@0 237 m_playSource = new AudioCallbackPlaySource(m_viewManager);
Chris@0 238
Chris@0 239 connect(m_playSource, SIGNAL(sampleRateMismatch(size_t, size_t, bool)),
Chris@0 240 this, SLOT(sampleRateMismatch(size_t, size_t, bool)));
Chris@42 241 connect(m_playSource, SIGNAL(audioOverloadPluginDisabled()),
Chris@42 242 this, SLOT(audioOverloadPluginDisabled()));
Chris@0 243
Chris@0 244 m_fader = new Fader(frame, false);
Chris@90 245 connect(m_fader, SIGNAL(mouseEntered()), this, SLOT(mouseEnteredWidget()));
Chris@90 246 connect(m_fader, SIGNAL(mouseLeft()), this, SLOT(mouseLeftWidget()));
Chris@0 247
Chris@0 248 m_playSpeed = new AudioDial(frame);
Chris@12 249 m_playSpeed->setMinimum(0);
Chris@48 250 m_playSpeed->setMaximum(200);
Chris@25 251 m_playSpeed->setValue(100);
Chris@0 252 m_playSpeed->setFixedWidth(24);
Chris@0 253 m_playSpeed->setFixedHeight(24);
Chris@0 254 m_playSpeed->setNotchesVisible(true);
Chris@25 255 m_playSpeed->setPageStep(10);
Chris@60 256 m_playSpeed->setObjectName(tr("Playback Speedup"));
Chris@25 257 m_playSpeed->setDefaultValue(100);
Chris@59 258 m_playSpeed->setRangeMapper(new PlaySpeedRangeMapper(0, 200));
Chris@60 259 m_playSpeed->setShowToolTip(true);
Chris@0 260 connect(m_playSpeed, SIGNAL(valueChanged(int)),
Chris@0 261 this, SLOT(playSpeedChanged(int)));
Chris@90 262 connect(m_playSpeed, SIGNAL(mouseEntered()), this, SLOT(mouseEnteredWidget()));
Chris@90 263 connect(m_playSpeed, SIGNAL(mouseLeft()), this, SLOT(mouseLeftWidget()));
Chris@90 264
Chris@168 265 IconLoader il;
Chris@168 266
Chris@90 267 m_playSharpen = new NotifyingPushButton(frame);
Chris@17 268 m_playSharpen->setToolTip(tr("Sharpen percussive transients"));
Chris@26 269 m_playSharpen->setFixedSize(20, 20);
Chris@16 270 m_playSharpen->setEnabled(false);
Chris@26 271 m_playSharpen->setCheckable(true);
Chris@26 272 m_playSharpen->setChecked(false);
Chris@168 273 m_playSharpen->setIcon(il.load("sharpen"));
Chris@26 274 connect(m_playSharpen, SIGNAL(clicked()), this, SLOT(playSharpenToggled()));
Chris@90 275 connect(m_playSharpen, SIGNAL(mouseEntered()), this, SLOT(mouseEnteredWidget()));
Chris@90 276 connect(m_playSharpen, SIGNAL(mouseLeft()), this, SLOT(mouseLeftWidget()));
Chris@90 277
Chris@90 278 m_playMono = new NotifyingPushButton(frame);
Chris@26 279 m_playMono->setToolTip(tr("Run time stretcher in mono only"));
Chris@26 280 m_playMono->setFixedSize(20, 20);
Chris@26 281 m_playMono->setEnabled(false);
Chris@26 282 m_playMono->setCheckable(true);
Chris@26 283 m_playMono->setChecked(false);
Chris@168 284 m_playMono->setIcon(il.load("mono"));
Chris@26 285 connect(m_playMono, SIGNAL(clicked()), this, SLOT(playMonoToggled()));
Chris@90 286 connect(m_playMono, SIGNAL(mouseEntered()), this, SLOT(mouseEnteredWidget()));
Chris@90 287 connect(m_playMono, SIGNAL(mouseLeft()), this, SLOT(mouseLeftWidget()));
Chris@21 288
Chris@21 289 QSettings settings;
Chris@21 290 settings.beginGroup("MainWindow");
Chris@24 291 m_playSharpen->setChecked(settings.value("playsharpen", true).toBool());
Chris@26 292 m_playMono->setChecked(settings.value("playmono", false).toBool());
Chris@21 293 settings.endGroup();
Chris@21 294
Chris@129 295 layout->setSpacing(4);
Chris@159 296 layout->addWidget(scroll, 0, 0, 1, 5);
Chris@65 297 layout->addWidget(m_overview, 1, 0);
Chris@0 298 layout->addWidget(m_fader, 1, 1);
Chris@0 299 layout->addWidget(m_playSpeed, 1, 2);
Chris@16 300 layout->addWidget(m_playSharpen, 1, 3);
Chris@26 301 layout->addWidget(m_playMono, 1, 4);
Chris@16 302
Chris@129 303 m_paneStack->setPropertyStackMinWidth
Chris@129 304 (m_fader->width() + m_playSpeed->width() + m_playSharpen->width() +
Chris@129 305 m_playMono->width() + layout->spacing() * 4);
Chris@129 306
Chris@16 307 layout->setColumnStretch(0, 10);
Chris@16 308
Chris@0 309 frame->setLayout(layout);
Chris@0 310
Chris@0 311 connect(m_viewManager, SIGNAL(outputLevelsChanged(float, float)),
Chris@0 312 this, SLOT(outputLevelsChanged(float, float)));
Chris@0 313
Chris@116 314 connect(m_viewManager, SIGNAL(playbackFrameChanged(unsigned long)),
Chris@116 315 this, SLOT(playbackFrameChanged(unsigned long)));
Chris@116 316
Chris@116 317 connect(m_viewManager, SIGNAL(globalCentreFrameChanged(unsigned long)),
Chris@116 318 this, SLOT(globalCentreFrameChanged(unsigned long)));
Chris@116 319
Chris@116 320 connect(m_viewManager, SIGNAL(viewCentreFrameChanged(View *, unsigned long)),
Chris@116 321 this, SLOT(viewCentreFrameChanged(View *, unsigned long)));
Chris@116 322
Chris@116 323 connect(m_viewManager, SIGNAL(viewZoomLevelChanged(View *, unsigned long, bool)),
Chris@116 324 this, SLOT(viewZoomLevelChanged(View *, unsigned long, bool)));
Chris@116 325
Chris@0 326 connect(Preferences::getInstance(),
Chris@0 327 SIGNAL(propertyChanged(PropertyContainer::PropertyName)),
Chris@0 328 this,
Chris@0 329 SLOT(preferenceChanged(PropertyContainer::PropertyName)));
Chris@0 330
Chris@98 331 // preferenceChanged("Property Box Layout");
Chris@98 332
Chris@70 333 if (m_oscQueue && m_oscQueue->isOK()) {
Chris@69 334 connect(m_oscQueue, SIGNAL(messagesAvailable()), this, SLOT(pollOSC()));
Chris@69 335 QTimer *oscTimer = new QTimer(this);
Chris@69 336 connect(oscTimer, SIGNAL(timeout()), this, SLOT(pollOSC()));
Chris@69 337 oscTimer->start(1000);
Chris@69 338 }
Chris@69 339
Chris@0 340 setupMenus();
Chris@0 341 setupToolbars();
Chris@155 342 setupHelpMenu();
Chris@0 343
Chris@90 344 statusBar();
Chris@0 345
Chris@0 346 newSession();
Chris@0 347 }
Chris@0 348
Chris@0 349 MainWindow::~MainWindow()
Chris@0 350 {
Chris@137 351 // std::cerr << "MainWindow::~MainWindow()" << std::endl;
Chris@118 352
Chris@70 353 if (!m_abandoning) {
Chris@70 354 closeSession();
Chris@70 355 }
Chris@0 356 delete m_playTarget;
Chris@0 357 delete m_playSource;
Chris@0 358 delete m_viewManager;
Chris@69 359 delete m_oscQueue;
Chris@162 360 delete m_keyReference;
Chris@163 361 delete m_preferencesDialog;
Chris@177 362 delete m_layerTreeView;
Chris@0 363 Profiles::getInstance()->dump();
Chris@0 364 }
Chris@0 365
Chris@81 366 QString
Chris@88 367 MainWindow::getOpenFileName(FileFinder::FileType type)
Chris@81 368 {
Chris@88 369 FileFinder *ff = FileFinder::getInstance();
Chris@81 370 switch (type) {
Chris@88 371 case FileFinder::SessionFile:
Chris@88 372 return ff->getOpenFileName(type, m_sessionFile);
Chris@88 373 case FileFinder::AudioFile:
Chris@88 374 return ff->getOpenFileName(type, m_audioFile);
Chris@88 375 case FileFinder::LayerFile:
Chris@88 376 return ff->getOpenFileName(type, m_sessionFile);
Chris@88 377 case FileFinder::SessionOrAudioFile:
Chris@88 378 return ff->getOpenFileName(type, m_sessionFile);
Chris@121 379 case FileFinder::ImageFile:
Chris@121 380 return ff->getOpenFileName(type, m_sessionFile);
Chris@88 381 case FileFinder::AnyFile:
Chris@88 382 if (getMainModel() != 0 &&
Chris@88 383 m_paneStack != 0 &&
Chris@88 384 m_paneStack->getCurrentPane() != 0) { // can import a layer
Chris@88 385 return ff->getOpenFileName(FileFinder::AnyFile, m_sessionFile);
Chris@81 386 } else {
Chris@88 387 return ff->getOpenFileName(FileFinder::SessionOrAudioFile,
Chris@88 388 m_sessionFile);
Chris@81 389 }
Chris@81 390 }
Chris@88 391 return "";
Chris@88 392 }
Chris@88 393
Chris@88 394 QString
Chris@88 395 MainWindow::getSaveFileName(FileFinder::FileType type)
Chris@88 396 {
Chris@88 397 FileFinder *ff = FileFinder::getInstance();
Chris@88 398 switch (type) {
Chris@88 399 case FileFinder::SessionFile:
Chris@88 400 return ff->getSaveFileName(type, m_sessionFile);
Chris@88 401 case FileFinder::AudioFile:
Chris@88 402 return ff->getSaveFileName(type, m_audioFile);
Chris@88 403 case FileFinder::LayerFile:
Chris@88 404 return ff->getSaveFileName(type, m_sessionFile);
Chris@88 405 case FileFinder::SessionOrAudioFile:
Chris@88 406 return ff->getSaveFileName(type, m_sessionFile);
Chris@121 407 case FileFinder::ImageFile:
Chris@121 408 return ff->getSaveFileName(type, m_sessionFile);
Chris@88 409 case FileFinder::AnyFile:
Chris@88 410 return ff->getSaveFileName(type, m_sessionFile);
Chris@81 411 }
Chris@88 412 return "";
Chris@81 413 }
Chris@81 414
Chris@88 415 void
Chris@88 416 MainWindow::registerLastOpenedFilePath(FileFinder::FileType type, QString path)
Chris@81 417 {
Chris@88 418 FileFinder *ff = FileFinder::getInstance();
Chris@88 419 ff->registerLastOpenedFilePath(type, path);
Chris@81 420 }
Chris@81 421
Chris@81 422 void
Chris@0 423 MainWindow::setupMenus()
Chris@0 424 {
Chris@0 425 if (!m_mainMenusCreated) {
Chris@0 426 m_rightButtonMenu = new QMenu();
Chris@104 427
Chris@104 428 // No -- we don't want tear-off enabled on the right-button
Chris@104 429 // menu. If it is enabled, then simply right-clicking and
Chris@104 430 // releasing will pop up the menu, activate the tear-off, and
Chris@104 431 // leave the torn-off menu window in front of the main window.
Chris@104 432 // That isn't desirable. I'm not sure it ever would be, in a
Chris@104 433 // context menu -- perhaps technically a Qt bug?
Chris@104 434 // m_rightButtonMenu->setTearOffEnabled(true);
Chris@0 435 }
Chris@0 436
Chris@0 437 if (m_rightButtonLayerMenu) {
Chris@0 438 m_rightButtonLayerMenu->clear();
Chris@0 439 } else {
Chris@0 440 m_rightButtonLayerMenu = m_rightButtonMenu->addMenu(tr("&Layer"));
Chris@97 441 m_rightButtonLayerMenu->setTearOffEnabled(true);
Chris@0 442 m_rightButtonMenu->addSeparator();
Chris@0 443 }
Chris@0 444
Chris@34 445 if (m_rightButtonTransformsMenu) {
Chris@34 446 m_rightButtonTransformsMenu->clear();
Chris@34 447 } else {
Chris@34 448 m_rightButtonTransformsMenu = m_rightButtonMenu->addMenu(tr("&Transform"));
Chris@97 449 m_rightButtonTransformsMenu->setTearOffEnabled(true);
Chris@34 450 m_rightButtonMenu->addSeparator();
Chris@34 451 }
Chris@34 452
Chris@0 453 if (!m_mainMenusCreated) {
Chris@0 454 CommandHistory::getInstance()->registerMenu(m_rightButtonMenu);
Chris@0 455 m_rightButtonMenu->addSeparator();
Chris@66 456 }
Chris@66 457
Chris@66 458 setupFileMenu();
Chris@66 459 setupEditMenu();
Chris@66 460 setupViewMenu();
Chris@66 461 setupPaneAndLayerMenus();
Chris@66 462 setupTransformsMenu();
Chris@66 463
Chris@66 464 m_mainMenusCreated = true;
Chris@66 465 }
Chris@66 466
Chris@66 467 void
Chris@66 468 MainWindow::setupFileMenu()
Chris@66 469 {
Chris@66 470 if (m_mainMenusCreated) return;
Chris@66 471
Chris@66 472 QMenu *menu = menuBar()->addMenu(tr("&File"));
Chris@97 473 menu->setTearOffEnabled(true);
Chris@66 474 QToolBar *toolbar = addToolBar(tr("File Toolbar"));
Chris@66 475
Chris@162 476 m_keyReference->setCategory(tr("File and Session Management"));
Chris@162 477
Chris@168 478 IconLoader il;
Chris@168 479
Chris@168 480 QIcon icon = il.load("filenew");
Chris@168 481 icon.addPixmap(il.loadPixmap("filenew-22"));
Chris@66 482 QAction *action = new QAction(icon, tr("&New Session"), this);
Chris@66 483 action->setShortcut(tr("Ctrl+N"));
Chris@90 484 action->setStatusTip(tr("Abandon the current Sonic Visualiser session and start a new one"));
Chris@66 485 connect(action, SIGNAL(triggered()), this, SLOT(newSession()));
Chris@162 486 m_keyReference->registerShortcut(action);
Chris@66 487 menu->addAction(action);
Chris@66 488 toolbar->addAction(action);
Chris@138 489
Chris@168 490 icon = il.load("fileopensession");
Chris@66 491 action = new QAction(icon, tr("&Open Session..."), this);
Chris@66 492 action->setShortcut(tr("Ctrl+O"));
Chris@66 493 action->setStatusTip(tr("Open a previously saved Sonic Visualiser session file"));
Chris@66 494 connect(action, SIGNAL(triggered()), this, SLOT(openSession()));
Chris@162 495 m_keyReference->registerShortcut(action);
Chris@66 496 menu->addAction(action);
Chris@66 497
Chris@168 498 icon = il.load("fileopen");
Chris@168 499 icon.addPixmap(il.loadPixmap("fileopen-22"));
Chris@138 500
Chris@66 501 action = new QAction(icon, tr("&Open..."), this);
Chris@66 502 action->setStatusTip(tr("Open a session file, audio file, or layer"));
Chris@66 503 connect(action, SIGNAL(triggered()), this, SLOT(openSomething()));
Chris@66 504 toolbar->addAction(action);
Chris@66 505
Chris@168 506 icon = il.load("filesave");
Chris@168 507 icon.addPixmap(il.loadPixmap("filesave-22"));
Chris@66 508 action = new QAction(icon, tr("&Save Session"), this);
Chris@66 509 action->setShortcut(tr("Ctrl+S"));
Chris@66 510 action->setStatusTip(tr("Save the current session into a Sonic Visualiser session file"));
Chris@66 511 connect(action, SIGNAL(triggered()), this, SLOT(saveSession()));
Chris@66 512 connect(this, SIGNAL(canSave(bool)), action, SLOT(setEnabled(bool)));
Chris@162 513 m_keyReference->registerShortcut(action);
Chris@66 514 menu->addAction(action);
Chris@66 515 toolbar->addAction(action);
Chris@0 516
Chris@168 517 icon = il.load("filesaveas");
Chris@168 518 icon.addPixmap(il.loadPixmap("filesaveas-22"));
Chris@66 519 action = new QAction(icon, tr("Save Session &As..."), this);
Chris@66 520 action->setStatusTip(tr("Save the current session into a new Sonic Visualiser session file"));
Chris@66 521 connect(action, SIGNAL(triggered()), this, SLOT(saveSessionAs()));
Chris@66 522 menu->addAction(action);
Chris@66 523 toolbar->addAction(action);
Chris@66 524
Chris@66 525 menu->addSeparator();
Chris@66 526
Chris@168 527 icon = il.load("fileopenaudio");
Chris@138 528 action = new QAction(icon, tr("&Import Audio File..."), this);
Chris@66 529 action->setShortcut(tr("Ctrl+I"));
Chris@66 530 action->setStatusTip(tr("Import an existing audio file"));
Chris@66 531 connect(action, SIGNAL(triggered()), this, SLOT(importAudio()));
Chris@162 532 m_keyReference->registerShortcut(action);
Chris@66 533 menu->addAction(action);
Chris@66 534
Chris@66 535 action = new QAction(tr("Import Secondary Audio File..."), this);
Chris@66 536 action->setShortcut(tr("Ctrl+Shift+I"));
Chris@66 537 action->setStatusTip(tr("Import an extra audio file as a separate layer"));
Chris@66 538 connect(action, SIGNAL(triggered()), this, SLOT(importMoreAudio()));
Chris@66 539 connect(this, SIGNAL(canImportMoreAudio(bool)), action, SLOT(setEnabled(bool)));
Chris@162 540 m_keyReference->registerShortcut(action);
Chris@66 541 menu->addAction(action);
Chris@66 542
Chris@66 543 action = new QAction(tr("&Export Audio File..."), this);
Chris@66 544 action->setStatusTip(tr("Export selection as an audio file"));
Chris@66 545 connect(action, SIGNAL(triggered()), this, SLOT(exportAudio()));
Chris@66 546 connect(this, SIGNAL(canExportAudio(bool)), action, SLOT(setEnabled(bool)));
Chris@66 547 menu->addAction(action);
Chris@66 548
Chris@66 549 menu->addSeparator();
Chris@66 550
Chris@66 551 action = new QAction(tr("Import Annotation &Layer..."), this);
Chris@66 552 action->setShortcut(tr("Ctrl+L"));
Chris@66 553 action->setStatusTip(tr("Import layer data from an existing file"));
Chris@66 554 connect(action, SIGNAL(triggered()), this, SLOT(importLayer()));
Chris@66 555 connect(this, SIGNAL(canImportLayer(bool)), action, SLOT(setEnabled(bool)));
Chris@162 556 m_keyReference->registerShortcut(action);
Chris@66 557 menu->addAction(action);
Chris@66 558
Chris@66 559 action = new QAction(tr("Export Annotation Layer..."), this);
Chris@66 560 action->setStatusTip(tr("Export layer data to a file"));
Chris@66 561 connect(action, SIGNAL(triggered()), this, SLOT(exportLayer()));
Chris@66 562 connect(this, SIGNAL(canExportLayer(bool)), action, SLOT(setEnabled(bool)));
Chris@66 563 menu->addAction(action);
Chris@66 564
Chris@66 565 menu->addSeparator();
Chris@86 566
Chris@121 567 action = new QAction(tr("Export Image File..."), this);
Chris@121 568 action->setStatusTip(tr("Export a single pane to an image file"));
Chris@121 569 connect(action, SIGNAL(triggered()), this, SLOT(exportImage()));
Chris@121 570 connect(this, SIGNAL(canExportImage(bool)), action, SLOT(setEnabled(bool)));
Chris@121 571 menu->addAction(action);
Chris@121 572
Chris@121 573 menu->addSeparator();
Chris@121 574
Chris@86 575 action = new QAction(tr("Open Lo&cation..."), this);
Chris@86 576 action->setShortcut(tr("Ctrl+Shift+O"));
Chris@86 577 action->setStatusTip(tr("Open or import a file from a remote URL"));
Chris@86 578 connect(action, SIGNAL(triggered()), this, SLOT(openLocation()));
Chris@162 579 m_keyReference->registerShortcut(action);
Chris@86 580 menu->addAction(action);
Chris@86 581
Chris@86 582 menu->addSeparator();
Chris@86 583
Chris@66 584 m_recentFilesMenu = menu->addMenu(tr("&Recent Files"));
Chris@97 585 m_recentFilesMenu->setTearOffEnabled(true);
Chris@66 586 setupRecentFilesMenu();
Chris@66 587 connect(&m_recentFiles, SIGNAL(recentChanged()),
Chris@66 588 this, SLOT(setupRecentFilesMenu()));
Chris@66 589
Chris@66 590 menu->addSeparator();
Chris@66 591 action = new QAction(tr("&Preferences..."), this);
Chris@66 592 action->setStatusTip(tr("Adjust the application preferences"));
Chris@66 593 connect(action, SIGNAL(triggered()), this, SLOT(preferences()));
Chris@66 594 menu->addAction(action);
Chris@0 595
Chris@66 596 menu->addSeparator();
Chris@168 597 action = new QAction(il.load("exit"),
Chris@66 598 tr("&Quit"), this);
Chris@66 599 action->setShortcut(tr("Ctrl+Q"));
Chris@90 600 action->setStatusTip(tr("Exit Sonic Visualiser"));
Chris@66 601 connect(action, SIGNAL(triggered()), this, SLOT(close()));
Chris@162 602 m_keyReference->registerShortcut(action);
Chris@66 603 menu->addAction(action);
Chris@66 604 }
Chris@66 605
Chris@66 606 void
Chris@66 607 MainWindow::setupEditMenu()
Chris@66 608 {
Chris@66 609 if (m_mainMenusCreated) return;
Chris@66 610
Chris@66 611 QMenu *menu = menuBar()->addMenu(tr("&Edit"));
Chris@97 612 menu->setTearOffEnabled(true);
Chris@66 613 CommandHistory::getInstance()->registerMenu(menu);
Chris@66 614
Chris@162 615 m_keyReference->setCategory(tr("Editing"));
Chris@162 616
Chris@66 617 menu->addSeparator();
Chris@66 618
Chris@168 619 IconLoader il;
Chris@168 620
Chris@168 621 QAction *action = new QAction(il.load("editcut"),
Chris@66 622 tr("Cu&t"), this);
Chris@66 623 action->setShortcut(tr("Ctrl+X"));
Chris@90 624 action->setStatusTip(tr("Cut the selection from the current layer to the clipboard"));
Chris@66 625 connect(action, SIGNAL(triggered()), this, SLOT(cut()));
Chris@66 626 connect(this, SIGNAL(canEditSelection(bool)), action, SLOT(setEnabled(bool)));
Chris@162 627 m_keyReference->registerShortcut(action);
Chris@66 628 menu->addAction(action);
Chris@66 629 m_rightButtonMenu->addAction(action);
Chris@66 630
Chris@168 631 action = new QAction(il.load("editcopy"),
Chris@66 632 tr("&Copy"), this);
Chris@66 633 action->setShortcut(tr("Ctrl+C"));
Chris@90 634 action->setStatusTip(tr("Copy the selection from the current layer to the clipboard"));
Chris@66 635 connect(action, SIGNAL(triggered()), this, SLOT(copy()));
Chris@66 636 connect(this, SIGNAL(canEditSelection(bool)), action, SLOT(setEnabled(bool)));
Chris@162 637 m_keyReference->registerShortcut(action);
Chris@66 638 menu->addAction(action);
Chris@66 639 m_rightButtonMenu->addAction(action);
Chris@66 640
Chris@168 641 action = new QAction(il.load("editpaste"),
Chris@66 642 tr("&Paste"), this);
Chris@66 643 action->setShortcut(tr("Ctrl+V"));
Chris@90 644 action->setStatusTip(tr("Paste from the clipboard to the current layer"));
Chris@66 645 connect(action, SIGNAL(triggered()), this, SLOT(paste()));
Chris@66 646 connect(this, SIGNAL(canPaste(bool)), action, SLOT(setEnabled(bool)));
Chris@162 647 m_keyReference->registerShortcut(action);
Chris@66 648 menu->addAction(action);
Chris@66 649 m_rightButtonMenu->addAction(action);
Chris@66 650
Chris@164 651 m_deleteSelectedAction = new QAction(tr("&Delete Selected Items"), this);
Chris@164 652 m_deleteSelectedAction->setShortcut(tr("Del"));
Chris@164 653 m_deleteSelectedAction->setStatusTip(tr("Delete items in current selection from the current layer"));
Chris@164 654 connect(m_deleteSelectedAction, SIGNAL(triggered()), this, SLOT(deleteSelected()));
Chris@164 655 connect(this, SIGNAL(canDeleteSelection(bool)), m_deleteSelectedAction, SLOT(setEnabled(bool)));
Chris@164 656 m_keyReference->registerShortcut(m_deleteSelectedAction);
Chris@164 657 menu->addAction(m_deleteSelectedAction);
Chris@164 658 m_rightButtonMenu->addAction(m_deleteSelectedAction);
Chris@66 659
Chris@66 660 menu->addSeparator();
Chris@66 661 m_rightButtonMenu->addSeparator();
Chris@162 662
Chris@162 663 m_keyReference->setCategory(tr("Selection"));
Chris@162 664
Chris@66 665 action = new QAction(tr("Select &All"), this);
Chris@66 666 action->setShortcut(tr("Ctrl+A"));
Chris@90 667 action->setStatusTip(tr("Select the whole duration of the current session"));
Chris@66 668 connect(action, SIGNAL(triggered()), this, SLOT(selectAll()));
Chris@66 669 connect(this, SIGNAL(canSelect(bool)), action, SLOT(setEnabled(bool)));
Chris@162 670 m_keyReference->registerShortcut(action);
Chris@66 671 menu->addAction(action);
Chris@66 672 m_rightButtonMenu->addAction(action);
Chris@0 673
Chris@66 674 action = new QAction(tr("Select &Visible Range"), this);
Chris@66 675 action->setShortcut(tr("Ctrl+Shift+A"));
Chris@90 676 action->setStatusTip(tr("Select the time range corresponding to the current window width"));
Chris@66 677 connect(action, SIGNAL(triggered()), this, SLOT(selectVisible()));
Chris@66 678 connect(this, SIGNAL(canSelect(bool)), action, SLOT(setEnabled(bool)));
Chris@162 679 m_keyReference->registerShortcut(action);
Chris@66 680 menu->addAction(action);
Chris@0 681
Chris@66 682 action = new QAction(tr("Select to &Start"), this);
Chris@66 683 action->setShortcut(tr("Shift+Left"));
Chris@90 684 action->setStatusTip(tr("Select from the start of the session to the current playback position"));
Chris@66 685 connect(action, SIGNAL(triggered()), this, SLOT(selectToStart()));
Chris@66 686 connect(this, SIGNAL(canSelect(bool)), action, SLOT(setEnabled(bool)));
Chris@162 687 m_keyReference->registerShortcut(action);
Chris@66 688 menu->addAction(action);
Chris@0 689
Chris@66 690 action = new QAction(tr("Select to &End"), this);
Chris@66 691 action->setShortcut(tr("Shift+Right"));
Chris@90 692 action->setStatusTip(tr("Select from the current playback position to the end of the session"));
Chris@66 693 connect(action, SIGNAL(triggered()), this, SLOT(selectToEnd()));
Chris@66 694 connect(this, SIGNAL(canSelect(bool)), action, SLOT(setEnabled(bool)));
Chris@162 695 m_keyReference->registerShortcut(action);
Chris@66 696 menu->addAction(action);
Chris@66 697
Chris@66 698 action = new QAction(tr("C&lear Selection"), this);
Chris@66 699 action->setShortcut(tr("Esc"));
Chris@90 700 action->setStatusTip(tr("Clear the selection"));
Chris@66 701 connect(action, SIGNAL(triggered()), this, SLOT(clearSelection()));
Chris@66 702 connect(this, SIGNAL(canClearSelection(bool)), action, SLOT(setEnabled(bool)));
Chris@162 703 m_keyReference->registerShortcut(action);
Chris@66 704 menu->addAction(action);
Chris@66 705 m_rightButtonMenu->addAction(action);
Chris@66 706
Chris@66 707 menu->addSeparator();
Chris@66 708
Chris@162 709 m_keyReference->setCategory(tr("Tapping Time Instants"));
Chris@162 710
Chris@66 711 action = new QAction(tr("&Insert Instant at Playback Position"), this);
Chris@66 712 action->setShortcut(tr("Enter"));
Chris@90 713 action->setStatusTip(tr("Insert a new time instant at the current playback position, in a new layer if necessary"));
Chris@66 714 connect(action, SIGNAL(triggered()), this, SLOT(insertInstant()));
Chris@66 715 connect(this, SIGNAL(canInsertInstant(bool)), action, SLOT(setEnabled(bool)));
Chris@162 716 m_keyReference->registerShortcut(action);
Chris@66 717 menu->addAction(action);
Chris@66 718
Chris@162 719 // Laptop shortcut (no keypad Enter key)
Chris@162 720 QString shortcut(tr(";"));
Chris@162 721 connect(new QShortcut(shortcut, this), SIGNAL(activated()),
Chris@162 722 this, SLOT(insertInstant()));
Chris@162 723 m_keyReference->registerAlternativeShortcut(action, shortcut);
Chris@162 724
Chris@81 725 action = new QAction(tr("Insert Instants at Selection &Boundaries"), this);
Chris@81 726 action->setShortcut(tr("Shift+Enter"));
Chris@163 727 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 728 connect(action, SIGNAL(triggered()), this, SLOT(insertInstantsAtBoundaries()));
Chris@81 729 connect(this, SIGNAL(canInsertInstantsAtBoundaries(bool)), action, SLOT(setEnabled(bool)));
Chris@162 730 m_keyReference->registerShortcut(action);
Chris@81 731 menu->addAction(action);
Chris@66 732 }
Chris@66 733
Chris@66 734 void
Chris@66 735 MainWindow::setupViewMenu()
Chris@66 736 {
Chris@66 737 if (m_mainMenusCreated) return;
Chris@66 738
Chris@168 739 IconLoader il;
Chris@168 740
Chris@90 741 QAction *action = 0;
Chris@90 742
Chris@162 743 m_keyReference->setCategory(tr("Panning and Navigation"));
Chris@162 744
Chris@66 745 QMenu *menu = menuBar()->addMenu(tr("&View"));
Chris@97 746 menu->setTearOffEnabled(true);
Chris@66 747 action = new QAction(tr("Scroll &Left"), this);
Chris@66 748 action->setShortcut(tr("Left"));
Chris@66 749 action->setStatusTip(tr("Scroll the current pane to the left"));
Chris@66 750 connect(action, SIGNAL(triggered()), this, SLOT(scrollLeft()));
Chris@66 751 connect(this, SIGNAL(canScroll(bool)), action, SLOT(setEnabled(bool)));
Chris@162 752 m_keyReference->registerShortcut(action);
Chris@66 753 menu->addAction(action);
Chris@0 754
Chris@66 755 action = new QAction(tr("Scroll &Right"), this);
Chris@66 756 action->setShortcut(tr("Right"));
Chris@66 757 action->setStatusTip(tr("Scroll the current pane to the right"));
Chris@66 758 connect(action, SIGNAL(triggered()), this, SLOT(scrollRight()));
Chris@66 759 connect(this, SIGNAL(canScroll(bool)), action, SLOT(setEnabled(bool)));
Chris@162 760 m_keyReference->registerShortcut(action);
Chris@66 761 menu->addAction(action);
Chris@0 762
Chris@90 763 action = new QAction(tr("&Jump Left"), this);
Chris@66 764 action->setShortcut(tr("Ctrl+Left"));
Chris@66 765 action->setStatusTip(tr("Scroll the current pane a big step to the left"));
Chris@66 766 connect(action, SIGNAL(triggered()), this, SLOT(jumpLeft()));
Chris@66 767 connect(this, SIGNAL(canScroll(bool)), action, SLOT(setEnabled(bool)));
Chris@162 768 m_keyReference->registerShortcut(action);
Chris@66 769 menu->addAction(action);
Chris@0 770
Chris@90 771 action = new QAction(tr("J&ump Right"), this);
Chris@66 772 action->setShortcut(tr("Ctrl+Right"));
Chris@66 773 action->setStatusTip(tr("Scroll the current pane a big step to the right"));
Chris@66 774 connect(action, SIGNAL(triggered()), this, SLOT(jumpRight()));
Chris@66 775 connect(this, SIGNAL(canScroll(bool)), action, SLOT(setEnabled(bool)));
Chris@162 776 m_keyReference->registerShortcut(action);
Chris@66 777 menu->addAction(action);
Chris@66 778
Chris@66 779 menu->addSeparator();
Chris@66 780
Chris@162 781 m_keyReference->setCategory(tr("Zoom"));
Chris@162 782
Chris@168 783 action = new QAction(il.load("zoom-in"),
Chris@66 784 tr("Zoom &In"), this);
Chris@66 785 action->setShortcut(tr("Up"));
Chris@66 786 action->setStatusTip(tr("Increase the zoom level"));
Chris@66 787 connect(action, SIGNAL(triggered()), this, SLOT(zoomIn()));
Chris@66 788 connect(this, SIGNAL(canZoom(bool)), action, SLOT(setEnabled(bool)));
Chris@162 789 m_keyReference->registerShortcut(action);
Chris@66 790 menu->addAction(action);
Chris@0 791
Chris@168 792 action = new QAction(il.load("zoom-out"),
Chris@66 793 tr("Zoom &Out"), this);
Chris@66 794 action->setShortcut(tr("Down"));
Chris@66 795 action->setStatusTip(tr("Decrease the zoom level"));
Chris@66 796 connect(action, SIGNAL(triggered()), this, SLOT(zoomOut()));
Chris@66 797 connect(this, SIGNAL(canZoom(bool)), action, SLOT(setEnabled(bool)));
Chris@162 798 m_keyReference->registerShortcut(action);
Chris@66 799 menu->addAction(action);
Chris@0 800
Chris@66 801 action = new QAction(tr("Restore &Default Zoom"), this);
Chris@90 802 action->setStatusTip(tr("Restore the zoom level to the default"));
Chris@66 803 connect(action, SIGNAL(triggered()), this, SLOT(zoomDefault()));
Chris@66 804 connect(this, SIGNAL(canZoom(bool)), action, SLOT(setEnabled(bool)));
Chris@66 805 menu->addAction(action);
Chris@66 806
Chris@168 807 action = new QAction(il.load("zoom-fit"),
Chris@138 808 tr("Zoom to &Fit"), this);
Chris@155 809 action->setShortcut(tr("F"));
Chris@66 810 action->setStatusTip(tr("Zoom to show the whole file"));
Chris@66 811 connect(action, SIGNAL(triggered()), this, SLOT(zoomToFit()));
Chris@66 812 connect(this, SIGNAL(canZoom(bool)), action, SLOT(setEnabled(bool)));
Chris@162 813 m_keyReference->registerShortcut(action);
Chris@66 814 menu->addAction(action);
Chris@90 815
Chris@90 816 menu->addSeparator();
Chris@90 817
Chris@162 818 m_keyReference->setCategory(tr("Display Features"));
Chris@162 819
Chris@90 820 QActionGroup *overlayGroup = new QActionGroup(this);
Chris@90 821
Chris@90 822 action = new QAction(tr("Show &No Overlays"), this);
Chris@90 823 action->setShortcut(tr("0"));
Chris@90 824 action->setStatusTip(tr("Hide centre indicator, frame times, layer names and scale"));
Chris@90 825 connect(action, SIGNAL(triggered()), this, SLOT(showNoOverlays()));
Chris@90 826 action->setCheckable(true);
Chris@90 827 action->setChecked(false);
Chris@90 828 overlayGroup->addAction(action);
Chris@162 829 m_keyReference->registerShortcut(action);
Chris@90 830 menu->addAction(action);
Chris@90 831
Chris@90 832 action = new QAction(tr("Show &Minimal Overlays"), this);
Chris@90 833 action->setShortcut(tr("9"));
Chris@90 834 action->setStatusTip(tr("Show centre indicator only"));
Chris@90 835 connect(action, SIGNAL(triggered()), this, SLOT(showMinimalOverlays()));
Chris@90 836 action->setCheckable(true);
Chris@90 837 action->setChecked(false);
Chris@90 838 overlayGroup->addAction(action);
Chris@162 839 m_keyReference->registerShortcut(action);
Chris@90 840 menu->addAction(action);
Chris@90 841
Chris@90 842 action = new QAction(tr("Show &Standard Overlays"), this);
Chris@90 843 action->setShortcut(tr("8"));
Chris@90 844 action->setStatusTip(tr("Show centre indicator, frame times and scale"));
Chris@90 845 connect(action, SIGNAL(triggered()), this, SLOT(showStandardOverlays()));
Chris@90 846 action->setCheckable(true);
Chris@90 847 action->setChecked(true);
Chris@90 848 overlayGroup->addAction(action);
Chris@162 849 m_keyReference->registerShortcut(action);
Chris@90 850 menu->addAction(action);
Chris@90 851
Chris@90 852 action = new QAction(tr("Show &All Overlays"), this);
Chris@90 853 action->setShortcut(tr("7"));
Chris@90 854 action->setStatusTip(tr("Show all texts and scale"));
Chris@90 855 connect(action, SIGNAL(triggered()), this, SLOT(showAllOverlays()));
Chris@90 856 action->setCheckable(true);
Chris@90 857 action->setChecked(false);
Chris@90 858 overlayGroup->addAction(action);
Chris@162 859 m_keyReference->registerShortcut(action);
Chris@90 860 menu->addAction(action);
Chris@7 861
Chris@72 862 menu->addSeparator();
Chris@72 863
Chris@66 864 action = new QAction(tr("Show &Zoom Wheels"), this);
Chris@66 865 action->setShortcut(tr("Z"));
Chris@66 866 action->setStatusTip(tr("Show thumbwheels for zooming horizontally and vertically"));
Chris@66 867 connect(action, SIGNAL(triggered()), this, SLOT(toggleZoomWheels()));
Chris@66 868 action->setCheckable(true);
Chris@66 869 action->setChecked(m_viewManager->getZoomWheelsEnabled());
Chris@162 870 m_keyReference->registerShortcut(action);
Chris@66 871 menu->addAction(action);
Chris@72 872
Chris@72 873 action = new QAction(tr("Show Property Bo&xes"), this);
Chris@72 874 action->setShortcut(tr("X"));
Chris@72 875 action->setStatusTip(tr("Show the layer property boxes at the side of the main window"));
Chris@72 876 connect(action, SIGNAL(triggered()), this, SLOT(togglePropertyBoxes()));
Chris@72 877 action->setCheckable(true);
Chris@72 878 action->setChecked(true);
Chris@162 879 m_keyReference->registerShortcut(action);
Chris@72 880 menu->addAction(action);
Chris@0 881
Chris@90 882 action = new QAction(tr("Show Status &Bar"), this);
Chris@90 883 action->setStatusTip(tr("Show context help information in the status bar at the bottom of the window"));
Chris@90 884 connect(action, SIGNAL(triggered()), this, SLOT(toggleStatusBar()));
Chris@90 885 action->setCheckable(true);
Chris@90 886 action->setChecked(true);
Chris@90 887 menu->addAction(action);
Chris@90 888
Chris@90 889 QSettings settings;
Chris@90 890 settings.beginGroup("MainWindow");
Chris@90 891 bool sb = settings.value("showstatusbar", true).toBool();
Chris@90 892 if (!sb) {
Chris@90 893 action->setChecked(false);
Chris@90 894 statusBar()->hide();
Chris@90 895 }
Chris@90 896 settings.endGroup();
Chris@90 897
Chris@66 898 menu->addSeparator();
Chris@66 899
Chris@90 900 action = new QAction(tr("Show La&yer Hierarchy"), this);
Chris@155 901 action->setShortcut(tr("H"));
Chris@90 902 action->setStatusTip(tr("Open a window displaying the hierarchy of panes and layers in this session"));
Chris@66 903 connect(action, SIGNAL(triggered()), this, SLOT(showLayerTree()));
Chris@162 904 m_keyReference->registerShortcut(action);
Chris@66 905 menu->addAction(action);
Chris@66 906 }
Chris@66 907
Chris@66 908 void
Chris@66 909 MainWindow::setupPaneAndLayerMenus()
Chris@66 910 {
Chris@0 911 if (m_paneMenu) {
Chris@0 912 m_paneActions.clear();
Chris@0 913 m_paneMenu->clear();
Chris@0 914 } else {
Chris@0 915 m_paneMenu = menuBar()->addMenu(tr("&Pane"));
Chris@97 916 m_paneMenu->setTearOffEnabled(true);
Chris@0 917 }
Chris@0 918
Chris@0 919 if (m_layerMenu) {
Chris@0 920 m_layerActions.clear();
Chris@0 921 m_layerMenu->clear();
Chris@0 922 } else {
Chris@0 923 m_layerMenu = menuBar()->addMenu(tr("&Layer"));
Chris@97 924 m_layerMenu->setTearOffEnabled(true);
Chris@0 925 }
Chris@0 926
Chris@66 927 QMenu *menu = m_paneMenu;
Chris@66 928
Chris@168 929 IconLoader il;
Chris@168 930
Chris@162 931 m_keyReference->setCategory(tr("Managing Panes and Layers"));
Chris@162 932
Chris@168 933 QAction *action = new QAction(il.load("pane"), tr("Add &New Pane"), this);
Chris@155 934 action->setShortcut(tr("N"));
Chris@66 935 action->setStatusTip(tr("Add a new pane containing only a time ruler"));
Chris@66 936 connect(action, SIGNAL(triggered()), this, SLOT(addPane()));
Chris@66 937 connect(this, SIGNAL(canAddPane(bool)), action, SLOT(setEnabled(bool)));
Chris@66 938 m_paneActions[action] = PaneConfiguration(LayerFactory::TimeRuler);
Chris@162 939 m_keyReference->registerShortcut(action);
Chris@66 940 menu->addAction(action);
Chris@66 941
Chris@66 942 menu->addSeparator();
Chris@66 943
Chris@66 944 menu = m_layerMenu;
Chris@66 945
Chris@66 946 // menu->addSeparator();
Chris@66 947
Chris@66 948 LayerFactory::LayerTypeSet emptyLayerTypes =
Chris@66 949 LayerFactory::getInstance()->getValidEmptyLayerTypes();
Chris@66 950
Chris@66 951 for (LayerFactory::LayerTypeSet::iterator i = emptyLayerTypes.begin();
Chris@66 952 i != emptyLayerTypes.end(); ++i) {
Chris@66 953
Chris@66 954 QIcon icon;
Chris@66 955 QString mainText, tipText, channelText;
Chris@66 956 LayerFactory::LayerType type = *i;
Chris@66 957 QString name = LayerFactory::getInstance()->getLayerPresentationName(type);
Chris@66 958
Chris@168 959 icon = il.load(LayerFactory::getInstance()->getLayerIconName(type));
Chris@66 960
Chris@66 961 mainText = tr("Add New %1 Layer").arg(name);
Chris@66 962 tipText = tr("Add a new empty layer of type %1").arg(name);
Chris@66 963
Chris@66 964 action = new QAction(icon, mainText, this);
Chris@66 965 action->setStatusTip(tipText);
Chris@66 966
Chris@66 967 if (type == LayerFactory::Text) {
Chris@155 968 action->setShortcut(tr("T"));
Chris@162 969 m_keyReference->registerShortcut(action);
Chris@66 970 }
Chris@66 971
Chris@66 972 connect(action, SIGNAL(triggered()), this, SLOT(addLayer()));
Chris@66 973 connect(this, SIGNAL(canAddLayer(bool)), action, SLOT(setEnabled(bool)));
Chris@66 974 m_layerActions[action] = type;
Chris@66 975 menu->addAction(action);
Chris@66 976 m_rightButtonLayerMenu->addAction(action);
Chris@66 977 }
Chris@66 978
Chris@66 979 m_rightButtonLayerMenu->addSeparator();
Chris@66 980 menu->addSeparator();
Chris@66 981
Chris@66 982 LayerFactory::LayerType backgroundTypes[] = {
Chris@66 983 LayerFactory::Waveform,
Chris@66 984 LayerFactory::Spectrogram,
Chris@66 985 LayerFactory::MelodicRangeSpectrogram,
Chris@66 986 LayerFactory::PeakFrequencySpectrogram,
Chris@66 987 LayerFactory::Spectrum
Chris@66 988 };
Chris@66 989
Chris@66 990 std::vector<Model *> models;
Chris@66 991 if (m_document) models = m_document->getTransformInputModels(); //!!! not well named for this!
Chris@66 992 bool plural = (models.size() > 1);
Chris@66 993 if (models.empty()) {
Chris@67 994 models.push_back(getMainModel()); // probably 0
Chris@66 995 }
Chris@66 996
Chris@66 997 for (unsigned int i = 0;
Chris@66 998 i < sizeof(backgroundTypes)/sizeof(backgroundTypes[0]); ++i) {
Chris@66 999
Chris@66 1000 for (int menuType = 0; menuType <= 1; ++menuType) { // pane, layer
Chris@66 1001
Chris@66 1002 if (menuType == 0) menu = m_paneMenu;
Chris@66 1003 else menu = m_layerMenu;
Chris@66 1004
Chris@66 1005 QMenu *submenu = 0;
Chris@66 1006
Chris@66 1007 QIcon icon;
Chris@66 1008 QString mainText, shortcutText, tipText, channelText;
Chris@66 1009 LayerFactory::LayerType type = backgroundTypes[i];
Chris@66 1010 bool mono = true;
Chris@66 1011
Chris@66 1012 switch (type) {
Chris@66 1013
Chris@66 1014 case LayerFactory::Waveform:
Chris@168 1015 icon = il.load("waveform");
Chris@66 1016 mainText = tr("Add &Waveform");
Chris@66 1017 if (menuType == 0) {
Chris@155 1018 shortcutText = tr("W");
Chris@66 1019 tipText = tr("Add a new pane showing a waveform view");
Chris@66 1020 } else {
Chris@66 1021 tipText = tr("Add a new layer showing a waveform view");
Chris@66 1022 }
Chris@66 1023 mono = false;
Chris@66 1024 break;
Chris@66 1025
Chris@66 1026 case LayerFactory::Spectrogram:
Chris@168 1027 icon = il.load("spectrogram");
Chris@161 1028 mainText = tr("Add Spectro&gram");
Chris@66 1029 if (menuType == 0) {
Chris@155 1030 shortcutText = tr("G");
Chris@90 1031 tipText = tr("Add a new pane showing a spectrogram");
Chris@66 1032 } else {
Chris@90 1033 tipText = tr("Add a new layer showing a spectrogram");
Chris@66 1034 }
Chris@66 1035 break;
Chris@66 1036
Chris@66 1037 case LayerFactory::MelodicRangeSpectrogram:
Chris@168 1038 icon = il.load("spectrogram");
Chris@66 1039 mainText = tr("Add &Melodic Range Spectrogram");
Chris@66 1040 if (menuType == 0) {
Chris@155 1041 shortcutText = tr("M");
Chris@90 1042 tipText = tr("Add a new pane showing a spectrogram set up for an overview of note pitches");
Chris@66 1043 } else {
Chris@90 1044 tipText = tr("Add a new layer showing a spectrogram set up for an overview of note pitches");
Chris@66 1045 }
Chris@66 1046 break;
Chris@66 1047
Chris@66 1048 case LayerFactory::PeakFrequencySpectrogram:
Chris@168 1049 icon = il.load("spectrogram");
Chris@155 1050 mainText = tr("Add Pea&k Frequency Spectrogram");
Chris@66 1051 if (menuType == 0) {
Chris@155 1052 shortcutText = tr("K");
Chris@66 1053 tipText = tr("Add a new pane showing a spectrogram set up for tracking frequencies");
Chris@66 1054 } else {
Chris@66 1055 tipText = tr("Add a new layer showing a spectrogram set up for tracking frequencies");
Chris@66 1056 }
Chris@66 1057 break;
Chris@66 1058
Chris@66 1059 case LayerFactory::Spectrum:
Chris@168 1060 icon = il.load("spectrum");
Chris@66 1061 mainText = tr("Add Spectr&um");
Chris@66 1062 if (menuType == 0) {
Chris@155 1063 shortcutText = tr("U");
Chris@66 1064 tipText = tr("Add a new pane showing a frequency spectrum");
Chris@66 1065 } else {
Chris@66 1066 tipText = tr("Add a new layer showing a frequency spectrum");
Chris@66 1067 }
Chris@66 1068 break;
Chris@66 1069
Chris@66 1070 default: break;
Chris@66 1071 }
Chris@66 1072
Chris@66 1073 std::vector<Model *> candidateModels;
Chris@66 1074 if (menuType == 0) {
Chris@66 1075 candidateModels = models;
Chris@66 1076 } else {
Chris@66 1077 candidateModels.push_back(0);
Chris@66 1078 }
Chris@66 1079
Chris@66 1080 for (std::vector<Model *>::iterator mi =
Chris@66 1081 candidateModels.begin();
Chris@66 1082 mi != candidateModels.end(); ++mi) {
Chris@66 1083
Chris@66 1084 Model *model = *mi;
Chris@66 1085
Chris@66 1086 int channels = 0;
Chris@66 1087 if (model) {
Chris@66 1088 DenseTimeValueModel *dtvm =
Chris@66 1089 dynamic_cast<DenseTimeValueModel *>(model);
Chris@66 1090 if (dtvm) channels = dtvm->getChannelCount();
Chris@66 1091 }
Chris@66 1092 if (channels < 1 && getMainModel()) {
Chris@66 1093 channels = getMainModel()->getChannelCount();
Chris@66 1094 }
Chris@66 1095 if (channels < 1) channels = 1;
Chris@66 1096
Chris@66 1097 for (int c = 0; c <= channels; ++c) {
Chris@66 1098
Chris@66 1099 if (c == 1 && channels == 1) continue;
Chris@66 1100 bool isDefault = (c == 0);
Chris@66 1101 bool isOnly = (isDefault && (channels == 1));
Chris@66 1102
Chris@66 1103 if (menuType == 1) {
Chris@66 1104 if (isDefault) isOnly = true;
Chris@66 1105 else continue;
Chris@66 1106 }
Chris@66 1107
Chris@66 1108 if (isOnly && (!plural || menuType == 1)) {
Chris@66 1109
Chris@138 1110 if (menuType == 1 && type != LayerFactory::Waveform) {
Chris@67 1111 action = new QAction(mainText, this);
Chris@67 1112 } else {
Chris@67 1113 action = new QAction(icon, mainText, this);
Chris@67 1114 }
Chris@67 1115
Chris@66 1116 action->setShortcut(shortcutText);
Chris@66 1117 action->setStatusTip(tipText);
Chris@66 1118 if (menuType == 0) {
Chris@66 1119 connect(action, SIGNAL(triggered()),
Chris@66 1120 this, SLOT(addPane()));
Chris@66 1121 connect(this, SIGNAL(canAddPane(bool)),
Chris@66 1122 action, SLOT(setEnabled(bool)));
Chris@66 1123 m_paneActions[action] = PaneConfiguration(type);
Chris@66 1124 } else {
Chris@66 1125 connect(action, SIGNAL(triggered()),
Chris@66 1126 this, SLOT(addLayer()));
Chris@66 1127 connect(this, SIGNAL(canAddLayer(bool)),
Chris@66 1128 action, SLOT(setEnabled(bool)));
Chris@66 1129 m_layerActions[action] = type;
Chris@66 1130 }
Chris@162 1131 if (shortcutText != "") {
Chris@162 1132 m_keyReference->registerShortcut(action);
Chris@162 1133 }
Chris@66 1134 menu->addAction(action);
Chris@66 1135
Chris@66 1136 } else {
Chris@66 1137
Chris@66 1138 if (!submenu) {
Chris@66 1139 submenu = menu->addMenu(mainText);
Chris@97 1140 submenu->setTearOffEnabled(true);
Chris@67 1141 } else if (isDefault) {
Chris@67 1142 submenu->addSeparator();
Chris@66 1143 }
Chris@66 1144
Chris@66 1145 QString actionText;
Chris@66 1146 if (c == 0) {
Chris@66 1147 if (mono) {
Chris@66 1148 actionText = tr("&All Channels Mixed");
Chris@66 1149 } else {
Chris@66 1150 actionText = tr("&All Channels");
Chris@66 1151 }
Chris@66 1152 } else {
Chris@66 1153 actionText = tr("Channel &%1").arg(c);
Chris@66 1154 }
Chris@66 1155
Chris@66 1156 if (model) {
Chris@66 1157 actionText = tr("%1: %2")
Chris@66 1158 .arg(model->objectName())
Chris@66 1159 .arg(actionText);
Chris@66 1160 }
Chris@67 1161
Chris@67 1162 if (isDefault) {
Chris@67 1163 action = new QAction(icon, actionText, this);
Chris@67 1164 if (!model || model == getMainModel()) {
Chris@162 1165 action->setShortcut(shortcutText);
Chris@67 1166 }
Chris@67 1167 } else {
Chris@67 1168 action = new QAction(actionText, this);
Chris@67 1169 }
Chris@67 1170
Chris@66 1171 action->setStatusTip(tipText);
Chris@66 1172
Chris@66 1173 if (menuType == 0) {
Chris@66 1174 connect(action, SIGNAL(triggered()),
Chris@66 1175 this, SLOT(addPane()));
Chris@66 1176 connect(this, SIGNAL(canAddPane(bool)),
Chris@66 1177 action, SLOT(setEnabled(bool)));
Chris@66 1178 m_paneActions[action] =
Chris@66 1179 PaneConfiguration(type, model, c - 1);
Chris@66 1180 } else {
Chris@66 1181 connect(action, SIGNAL(triggered()),
Chris@66 1182 this, SLOT(addLayer()));
Chris@66 1183 connect(this, SIGNAL(canAddLayer(bool)),
Chris@66 1184 action, SLOT(setEnabled(bool)));
Chris@66 1185 m_layerActions[action] = type;
Chris@66 1186 }
Chris@66 1187
Chris@66 1188 submenu->addAction(action);
Chris@66 1189 }
Chris@66 1190 }
Chris@66 1191 }
Chris@66 1192 }
Chris@66 1193 }
Chris@66 1194
Chris@66 1195 menu = m_paneMenu;
Chris@66 1196
Chris@66 1197 menu->addSeparator();
Chris@66 1198
Chris@168 1199 action = new QAction(il.load("editdelete"), tr("&Delete Pane"), this);
Chris@155 1200 action->setShortcut(tr("Ctrl+Shift+D"));
Chris@90 1201 action->setStatusTip(tr("Delete the currently active pane"));
Chris@66 1202 connect(action, SIGNAL(triggered()), this, SLOT(deleteCurrentPane()));
Chris@66 1203 connect(this, SIGNAL(canDeleteCurrentPane(bool)), action, SLOT(setEnabled(bool)));
Chris@162 1204 m_keyReference->registerShortcut(action);
Chris@66 1205 menu->addAction(action);
Chris@66 1206
Chris@66 1207 menu = m_layerMenu;
Chris@66 1208
Chris@168 1209 action = new QAction(il.load("timeruler"), tr("Add &Time Ruler"), this);
Chris@66 1210 action->setStatusTip(tr("Add a new layer showing a time ruler"));
Chris@66 1211 connect(action, SIGNAL(triggered()), this, SLOT(addLayer()));
Chris@66 1212 connect(this, SIGNAL(canAddLayer(bool)), action, SLOT(setEnabled(bool)));
Chris@66 1213 m_layerActions[action] = LayerFactory::TimeRuler;
Chris@66 1214 menu->addAction(action);
Chris@66 1215
Chris@66 1216 menu->addSeparator();
Chris@66 1217
Chris@66 1218 m_existingLayersMenu = menu->addMenu(tr("Add &Existing Layer"));
Chris@97 1219 m_existingLayersMenu->setTearOffEnabled(true);
Chris@66 1220 m_rightButtonLayerMenu->addMenu(m_existingLayersMenu);
Chris@95 1221
Chris@95 1222 m_sliceMenu = menu->addMenu(tr("Add S&lice of Layer"));
Chris@97 1223 m_sliceMenu->setTearOffEnabled(true);
Chris@95 1224 m_rightButtonLayerMenu->addMenu(m_sliceMenu);
Chris@95 1225
Chris@95 1226 setupExistingLayersMenus();
Chris@66 1227
Chris@66 1228 m_rightButtonLayerMenu->addSeparator();
Chris@66 1229 menu->addSeparator();
Chris@66 1230
Chris@163 1231 QAction *raction = new QAction(tr("&Rename Layer..."), this);
Chris@163 1232 raction->setShortcut(tr("R"));
Chris@163 1233 raction->setStatusTip(tr("Rename the currently active layer"));
Chris@163 1234 connect(raction, SIGNAL(triggered()), this, SLOT(renameCurrentLayer()));
Chris@163 1235 connect(this, SIGNAL(canRenameLayer(bool)), raction, SLOT(setEnabled(bool)));
Chris@163 1236 menu->addAction(raction);
Chris@163 1237 m_rightButtonLayerMenu->addAction(raction);
Chris@66 1238
Chris@168 1239 action = new QAction(il.load("editdelete"), tr("&Delete Layer"), this);
Chris@155 1240 action->setShortcut(tr("Ctrl+D"));
Chris@66 1241 action->setStatusTip(tr("Delete the currently active layer"));
Chris@66 1242 connect(action, SIGNAL(triggered()), this, SLOT(deleteCurrentLayer()));
Chris@66 1243 connect(this, SIGNAL(canDeleteCurrentLayer(bool)), action, SLOT(setEnabled(bool)));
Chris@162 1244 m_keyReference->registerShortcut(action);
Chris@66 1245 menu->addAction(action);
Chris@66 1246 m_rightButtonLayerMenu->addAction(action);
Chris@163 1247
Chris@163 1248 m_keyReference->registerShortcut(raction); // rename after delete, so delete layer goes next to delete pane
Chris@66 1249 }
Chris@66 1250
Chris@66 1251 void
Chris@66 1252 MainWindow::setupTransformsMenu()
Chris@66 1253 {
Chris@34 1254 if (m_transformsMenu) {
Chris@34 1255 m_transformActions.clear();
Chris@34 1256 m_transformActionsReverse.clear();
Chris@34 1257 m_transformsMenu->clear();
Chris@34 1258 } else {
Chris@97 1259 m_transformsMenu = menuBar()->addMenu(tr("&Transform"));
Chris@97 1260 m_transformsMenu->setTearOffEnabled(true);
Chris@97 1261 }
Chris@34 1262
Chris@0 1263 TransformFactory::TransformList transforms =
Chris@0 1264 TransformFactory::getInstance()->getAllTransforms();
Chris@0 1265
Chris@33 1266 vector<QString> types =
Chris@0 1267 TransformFactory::getInstance()->getAllTransformTypes();
Chris@0 1268
Chris@37 1269 map<QString, map<QString, SubdividingMenu *> > categoryMenus;
Chris@37 1270 map<QString, map<QString, SubdividingMenu *> > makerMenus;
Chris@33 1271
Chris@36 1272 map<QString, SubdividingMenu *> byPluginNameMenus;
Chris@33 1273 map<QString, map<QString, QMenu *> > pluginNameMenus;
Chris@33 1274
Chris@37 1275 set<SubdividingMenu *> pendingMenus;
Chris@37 1276
Chris@34 1277 m_recentTransformsMenu = m_transformsMenu->addMenu(tr("&Recent Transforms"));
Chris@97 1278 m_recentTransformsMenu->setTearOffEnabled(true);
Chris@34 1279 m_rightButtonTransformsMenu->addMenu(m_recentTransformsMenu);
Chris@34 1280 connect(&m_recentTransforms, SIGNAL(recentChanged()),
Chris@34 1281 this, SLOT(setupRecentTransformsMenu()));
Chris@34 1282
Chris@34 1283 m_transformsMenu->addSeparator();
Chris@34 1284 m_rightButtonTransformsMenu->addSeparator();
Chris@34 1285
Chris@33 1286 for (vector<QString>::iterator i = types.begin(); i != types.end(); ++i) {
Chris@33 1287
Chris@33 1288 if (i != types.begin()) {
Chris@34 1289 m_transformsMenu->addSeparator();
Chris@34 1290 m_rightButtonTransformsMenu->addSeparator();
Chris@33 1291 }
Chris@33 1292
Chris@33 1293 QString byCategoryLabel = tr("%1 by Category").arg(*i);
Chris@37 1294 SubdividingMenu *byCategoryMenu = new SubdividingMenu(byCategoryLabel,
Chris@37 1295 20, 40);
Chris@97 1296 byCategoryMenu->setTearOffEnabled(true);
Chris@37 1297 m_transformsMenu->addMenu(byCategoryMenu);
Chris@34 1298 m_rightButtonTransformsMenu->addMenu(byCategoryMenu);
Chris@37 1299 pendingMenus.insert(byCategoryMenu);
Chris@33 1300
Chris@33 1301 vector<QString> categories =
Chris@33 1302 TransformFactory::getInstance()->getTransformCategories(*i);
Chris@33 1303
Chris@33 1304 for (vector<QString>::iterator j = categories.begin();
Chris@33 1305 j != categories.end(); ++j) {
Chris@33 1306
Chris@33 1307 QString category = *j;
Chris@33 1308 if (category == "") category = tr("Unclassified");
Chris@33 1309
Chris@33 1310 if (categories.size() < 2) {
Chris@33 1311 categoryMenus[*i][category] = byCategoryMenu;
Chris@33 1312 continue;
Chris@33 1313 }
Chris@33 1314
Chris@33 1315 QStringList components = category.split(" > ");
Chris@33 1316 QString key;
Chris@33 1317
Chris@33 1318 for (QStringList::iterator k = components.begin();
Chris@33 1319 k != components.end(); ++k) {
Chris@33 1320
Chris@33 1321 QString parentKey = key;
Chris@33 1322 if (key != "") key += " > ";
Chris@33 1323 key += *k;
Chris@33 1324
Chris@33 1325 if (categoryMenus[*i].find(key) == categoryMenus[*i].end()) {
Chris@37 1326 SubdividingMenu *m = new SubdividingMenu(*k, 20, 40);
Chris@97 1327 m->setTearOffEnabled(true);
Chris@37 1328 pendingMenus.insert(m);
Chris@37 1329 categoryMenus[*i][key] = m;
Chris@33 1330 if (parentKey == "") {
Chris@37 1331 byCategoryMenu->addMenu(m);
Chris@33 1332 } else {
Chris@37 1333 categoryMenus[*i][parentKey]->addMenu(m);
Chris@33 1334 }
Chris@33 1335 }
Chris@33 1336 }
Chris@33 1337 }
Chris@33 1338
Chris@34 1339 QString byPluginNameLabel = tr("%1 by Plugin Name").arg(*i);
Chris@36 1340 byPluginNameMenus[*i] = new SubdividingMenu(byPluginNameLabel);
Chris@97 1341 byPluginNameMenus[*i]->setTearOffEnabled(true);
Chris@36 1342 m_transformsMenu->addMenu(byPluginNameMenus[*i]);
Chris@34 1343 m_rightButtonTransformsMenu->addMenu(byPluginNameMenus[*i]);
Chris@37 1344 pendingMenus.insert(byPluginNameMenus[*i]);
Chris@34 1345
Chris@33 1346 QString byMakerLabel = tr("%1 by Maker").arg(*i);
Chris@37 1347 SubdividingMenu *byMakerMenu = new SubdividingMenu(byMakerLabel, 20, 40);
Chris@97 1348 byMakerMenu->setTearOffEnabled(true);
Chris@37 1349 m_transformsMenu->addMenu(byMakerMenu);
Chris@34 1350 m_rightButtonTransformsMenu->addMenu(byMakerMenu);
Chris@37 1351 pendingMenus.insert(byMakerMenu);
Chris@33 1352
Chris@33 1353 vector<QString> makers =
Chris@33 1354 TransformFactory::getInstance()->getTransformMakers(*i);
Chris@37 1355
Chris@33 1356 for (vector<QString>::iterator j = makers.begin();
Chris@33 1357 j != makers.end(); ++j) {
Chris@33 1358
Chris@33 1359 QString maker = *j;
Chris@33 1360 if (maker == "") maker = tr("Unknown");
Chris@55 1361 maker.replace(QRegExp(tr(" [\\(<].*$")), "");
Chris@55 1362
Chris@37 1363 makerMenus[*i][maker] = new SubdividingMenu(maker, 30, 40);
Chris@97 1364 makerMenus[*i][maker]->setTearOffEnabled(true);
Chris@37 1365 byMakerMenu->addMenu(makerMenus[*i][maker]);
Chris@37 1366 pendingMenus.insert(makerMenus[*i][maker]);
Chris@33 1367 }
Chris@0 1368 }
Chris@0 1369
Chris@0 1370 for (unsigned int i = 0; i < transforms.size(); ++i) {
Chris@0 1371
Chris@107 1372 QString name = transforms[i].name;
Chris@107 1373 if (name == "") name = transforms[i].identifier;
Chris@107 1374
Chris@107 1375 // std::cerr << "Plugin Name: " << name.toStdString() << std::endl;
Chris@80 1376
Chris@33 1377 QString type = transforms[i].type;
Chris@33 1378
Chris@33 1379 QString category = transforms[i].category;
Chris@33 1380 if (category == "") category = tr("Unclassified");
Chris@33 1381
Chris@33 1382 QString maker = transforms[i].maker;
Chris@33 1383 if (maker == "") maker = tr("Unknown");
Chris@55 1384 maker.replace(QRegExp(tr(" [\\(<].*$")), "");
Chris@33 1385
Chris@107 1386 QString pluginName = name.section(": ", 0, 0);
Chris@107 1387 QString output = name.section(": ", 1);
Chris@107 1388
Chris@107 1389 QAction *action = new QAction(tr("%1...").arg(name), this);
Chris@0 1390 connect(action, SIGNAL(triggered()), this, SLOT(addLayer()));
Chris@107 1391 m_transformActions[action] = transforms[i].identifier;
Chris@107 1392 m_transformActionsReverse[transforms[i].identifier] = action;
Chris@0 1393 connect(this, SIGNAL(canAddLayer(bool)), action, SLOT(setEnabled(bool)));
Chris@33 1394
Chris@108 1395 action->setStatusTip(transforms[i].description);
Chris@90 1396
Chris@33 1397 if (categoryMenus[type].find(category) == categoryMenus[type].end()) {
Chris@33 1398 std::cerr << "WARNING: MainWindow::setupMenus: Internal error: "
Chris@33 1399 << "No category menu for transform \""
Chris@107 1400 << name.toStdString() << "\" (category = \""
Chris@33 1401 << category.toStdString() << "\")" << std::endl;
Chris@33 1402 } else {
Chris@33 1403 categoryMenus[type][category]->addAction(action);
Chris@33 1404 }
Chris@33 1405
Chris@33 1406 if (makerMenus[type].find(maker) == makerMenus[type].end()) {
Chris@33 1407 std::cerr << "WARNING: MainWindow::setupMenus: Internal error: "
Chris@33 1408 << "No maker menu for transform \""
Chris@107 1409 << name.toStdString() << "\" (maker = \""
Chris@33 1410 << maker.toStdString() << "\")" << std::endl;
Chris@33 1411 } else {
Chris@80 1412 makerMenus[type][maker]->addAction(action);
Chris@33 1413 }
Chris@33 1414
Chris@33 1415 action = new QAction(tr("%1...").arg(output == "" ? pluginName : output), this);
Chris@33 1416 connect(action, SIGNAL(triggered()), this, SLOT(addLayer()));
Chris@107 1417 m_transformActions[action] = transforms[i].identifier;
Chris@33 1418 connect(this, SIGNAL(canAddLayer(bool)), action, SLOT(setEnabled(bool)));
Chris@108 1419 action->setStatusTip(transforms[i].description);
Chris@33 1420
Chris@34 1421 // cerr << "Transform: \"" << name.toStdString() << "\": plugin name \"" << pluginName.toStdString() << "\"" << endl;
Chris@34 1422
Chris@33 1423 if (pluginNameMenus[type].find(pluginName) ==
Chris@33 1424 pluginNameMenus[type].end()) {
Chris@33 1425
Chris@36 1426 SubdividingMenu *parentMenu = byPluginNameMenus[type];
Chris@97 1427 parentMenu->setTearOffEnabled(true);
Chris@34 1428
Chris@33 1429 if (output == "") {
Chris@36 1430 parentMenu->addAction(pluginName, action);
Chris@33 1431 } else {
Chris@34 1432 pluginNameMenus[type][pluginName] =
Chris@34 1433 parentMenu->addMenu(pluginName);
Chris@33 1434 connect(this, SIGNAL(canAddLayer(bool)),
Chris@33 1435 pluginNameMenus[type][pluginName],
Chris@33 1436 SLOT(setEnabled(bool)));
Chris@33 1437 }
Chris@33 1438 }
Chris@33 1439
Chris@33 1440 if (pluginNameMenus[type].find(pluginName) !=
Chris@33 1441 pluginNameMenus[type].end()) {
Chris@33 1442 pluginNameMenus[type][pluginName]->addAction(action);
Chris@33 1443 }
Chris@0 1444 }
Chris@0 1445
Chris@37 1446 for (set<SubdividingMenu *>::iterator i = pendingMenus.begin();
Chris@37 1447 i != pendingMenus.end(); ++i) {
Chris@37 1448 (*i)->entriesAdded();
Chris@37 1449 }
Chris@37 1450
Chris@34 1451 setupRecentTransformsMenu();
Chris@66 1452 }
Chris@66 1453
Chris@66 1454 void
Chris@66 1455 MainWindow::setupHelpMenu()
Chris@66 1456 {
Chris@66 1457 QMenu *menu = menuBar()->addMenu(tr("&Help"));
Chris@97 1458 menu->setTearOffEnabled(true);
Chris@66 1459
Chris@162 1460 m_keyReference->setCategory(tr("Help"));
Chris@162 1461
Chris@168 1462 IconLoader il;
Chris@168 1463
Chris@168 1464 QAction *action = new QAction(il.load("help"),
Chris@138 1465 tr("&Help Reference"), this);
Chris@162 1466 action->setShortcut(tr("F1"));
Chris@66 1467 action->setStatusTip(tr("Open the Sonic Visualiser reference manual"));
Chris@66 1468 connect(action, SIGNAL(triggered()), this, SLOT(help()));
Chris@162 1469 m_keyReference->registerShortcut(action);
Chris@0 1470 menu->addAction(action);
Chris@162 1471
Chris@163 1472 action = new QAction(tr("&Key and Mouse Reference"), this);
Chris@162 1473 action->setShortcut(tr("F2"));
Chris@162 1474 action->setStatusTip(tr("Open a window showing the keystrokes you can use in Sonic Visualiser"));
Chris@162 1475 connect(action, SIGNAL(triggered()), this, SLOT(keyReference()));
Chris@162 1476 m_keyReference->registerShortcut(action);
Chris@162 1477 menu->addAction(action);
Chris@66 1478
Chris@164 1479 action = new QAction(tr("Sonic Visualiser on the &Web"), this);
Chris@164 1480 action->setStatusTip(tr("Open the Sonic Visualiser website"));
Chris@164 1481 connect(action, SIGNAL(triggered()), this, SLOT(website()));
Chris@164 1482 menu->addAction(action);
Chris@164 1483
Chris@66 1484 action = new QAction(tr("&About Sonic Visualiser"), this);
Chris@66 1485 action->setStatusTip(tr("Show information about Sonic Visualiser"));
Chris@66 1486 connect(action, SIGNAL(triggered()), this, SLOT(about()));
Chris@0 1487 menu->addAction(action);
Chris@0 1488 }
Chris@0 1489
Chris@0 1490 void
Chris@0 1491 MainWindow::setupRecentFilesMenu()
Chris@0 1492 {
Chris@0 1493 m_recentFilesMenu->clear();
Chris@34 1494 vector<QString> files = m_recentFiles.getRecent();
Chris@0 1495 for (size_t i = 0; i < files.size(); ++i) {
Chris@0 1496 QAction *action = new QAction(files[i], this);
Chris@0 1497 connect(action, SIGNAL(triggered()), this, SLOT(openRecentFile()));
Chris@162 1498 if (i == 0) {
Chris@162 1499 action->setShortcut(tr("Ctrl+R"));
Chris@162 1500 m_keyReference->registerShortcut
Chris@163 1501 (tr("Re-open"),
Chris@163 1502 action->shortcut(),
Chris@163 1503 tr("Re-open the current or most recently opened file"));
Chris@162 1504 }
Chris@0 1505 m_recentFilesMenu->addAction(action);
Chris@0 1506 }
Chris@0 1507 }
Chris@0 1508
Chris@0 1509 void
Chris@34 1510 MainWindow::setupRecentTransformsMenu()
Chris@34 1511 {
Chris@34 1512 m_recentTransformsMenu->clear();
Chris@34 1513 vector<QString> transforms = m_recentTransforms.getRecent();
Chris@34 1514 for (size_t i = 0; i < transforms.size(); ++i) {
Chris@34 1515 TransformActionReverseMap::iterator ti =
Chris@34 1516 m_transformActionsReverse.find(transforms[i]);
Chris@34 1517 if (ti == m_transformActionsReverse.end()) {
Chris@34 1518 std::cerr << "WARNING: MainWindow::setupRecentTransformsMenu: "
Chris@34 1519 << "Unknown transform \"" << transforms[i].toStdString()
Chris@34 1520 << "\" in recent transforms list" << std::endl;
Chris@34 1521 continue;
Chris@34 1522 }
Chris@162 1523 if (i == 0) {
Chris@162 1524 ti->second->setShortcut(tr("Ctrl+T"));
Chris@162 1525 m_keyReference->registerShortcut
Chris@163 1526 (tr("Repeat Transform"),
Chris@162 1527 ti->second->shortcut(),
Chris@163 1528 tr("Re-select the most recently run transform"));
Chris@162 1529 }
Chris@34 1530 m_recentTransformsMenu->addAction(ti->second);
Chris@34 1531 }
Chris@34 1532 }
Chris@34 1533
Chris@34 1534 void
Chris@95 1535 MainWindow::setupExistingLayersMenus()
Chris@0 1536 {
Chris@0 1537 if (!m_existingLayersMenu) return; // should have been created by setupMenus
Chris@0 1538
Chris@0 1539 // std::cerr << "MainWindow::setupExistingLayersMenu" << std::endl;
Chris@0 1540
Chris@0 1541 m_existingLayersMenu->clear();
Chris@0 1542 m_existingLayerActions.clear();
Chris@0 1543
Chris@95 1544 m_sliceMenu->clear();
Chris@95 1545 m_sliceActions.clear();
Chris@95 1546
Chris@168 1547 IconLoader il;
Chris@168 1548
Chris@33 1549 vector<Layer *> orderedLayers;
Chris@33 1550 set<Layer *> observedLayers;
Chris@95 1551 set<Layer *> sliceableLayers;
Chris@95 1552
Chris@95 1553 LayerFactory *factory = LayerFactory::getInstance();
Chris@0 1554
Chris@0 1555 for (int i = 0; i < m_paneStack->getPaneCount(); ++i) {
Chris@0 1556
Chris@0 1557 Pane *pane = m_paneStack->getPane(i);
Chris@0 1558 if (!pane) continue;
Chris@0 1559
Chris@0 1560 for (int j = 0; j < pane->getLayerCount(); ++j) {
Chris@0 1561
Chris@0 1562 Layer *layer = pane->getLayer(j);
Chris@0 1563 if (!layer) continue;
Chris@0 1564 if (observedLayers.find(layer) != observedLayers.end()) {
Chris@137 1565 // std::cerr << "found duplicate layer " << layer << std::endl;
Chris@0 1566 continue;
Chris@0 1567 }
Chris@0 1568
Chris@0 1569 // std::cerr << "found new layer " << layer << " (name = "
Chris@0 1570 // << layer->getLayerPresentationName().toStdString() << ")" << std::endl;
Chris@0 1571
Chris@0 1572 orderedLayers.push_back(layer);
Chris@0 1573 observedLayers.insert(layer);
Chris@95 1574
Chris@95 1575 if (factory->isLayerSliceable(layer)) {
Chris@95 1576 sliceableLayers.insert(layer);
Chris@95 1577 }
Chris@0 1578 }
Chris@0 1579 }
Chris@0 1580
Chris@33 1581 map<QString, int> observedNames;
Chris@0 1582
Chris@137 1583 for (size_t i = 0; i < orderedLayers.size(); ++i) {
Chris@0 1584
Chris@95 1585 Layer *layer = orderedLayers[i];
Chris@95 1586
Chris@95 1587 QString name = layer->getLayerPresentationName();
Chris@0 1588 int n = ++observedNames[name];
Chris@0 1589 if (n > 1) name = QString("%1 <%2>").arg(name).arg(n);
Chris@0 1590
Chris@168 1591 QIcon icon = il.load(factory->getLayerIconName
Chris@168 1592 (factory->getLayerType(layer)));
Chris@95 1593
Chris@95 1594 QAction *action = new QAction(icon, name, this);
Chris@0 1595 connect(action, SIGNAL(triggered()), this, SLOT(addLayer()));
Chris@0 1596 connect(this, SIGNAL(canAddLayer(bool)), action, SLOT(setEnabled(bool)));
Chris@95 1597 m_existingLayerActions[action] = layer;
Chris@0 1598
Chris@0 1599 m_existingLayersMenu->addAction(action);
Chris@95 1600
Chris@95 1601 if (sliceableLayers.find(layer) != sliceableLayers.end()) {
Chris@95 1602 action = new QAction(icon, name, this);
Chris@95 1603 connect(action, SIGNAL(triggered()), this, SLOT(addLayer()));
Chris@95 1604 connect(this, SIGNAL(canAddLayer(bool)), action, SLOT(setEnabled(bool)));
Chris@95 1605 m_sliceActions[action] = layer;
Chris@95 1606 m_sliceMenu->addAction(action);
Chris@95 1607 }
Chris@0 1608 }
Chris@95 1609
Chris@95 1610 m_sliceMenu->setEnabled(!m_sliceActions.empty());
Chris@0 1611 }
Chris@0 1612
Chris@0 1613 void
Chris@0 1614 MainWindow::setupToolbars()
Chris@0 1615 {
Chris@162 1616 m_keyReference->setCategory(tr("Playback and Transport Controls"));
Chris@162 1617
Chris@168 1618 IconLoader il;
Chris@168 1619
Chris@155 1620 QMenu *menu = m_playbackMenu = menuBar()->addMenu(tr("Play&back"));
Chris@155 1621 menu->setTearOffEnabled(true);
Chris@155 1622 m_rightButtonMenu->addSeparator();
Chris@155 1623 m_rightButtonPlaybackMenu = m_rightButtonMenu->addMenu(tr("Playback"));
Chris@155 1624
Chris@155 1625 QToolBar *toolbar = addToolBar(tr("Playback Toolbar"));
Chris@155 1626
Chris@168 1627 QAction *rwdStartAction = toolbar->addAction(il.load("rewind-start"),
Chris@155 1628 tr("Rewind to Start"));
Chris@155 1629 rwdStartAction->setShortcut(tr("Home"));
Chris@155 1630 rwdStartAction->setStatusTip(tr("Rewind to the start"));
Chris@155 1631 connect(rwdStartAction, SIGNAL(triggered()), this, SLOT(rewindStart()));
Chris@155 1632 connect(this, SIGNAL(canPlay(bool)), rwdStartAction, SLOT(setEnabled(bool)));
Chris@155 1633
Chris@168 1634 QAction *m_rwdAction = toolbar->addAction(il.load("rewind"),
Chris@155 1635 tr("Rewind"));
Chris@155 1636 m_rwdAction->setShortcut(tr("PgUp"));
Chris@163 1637 m_rwdAction->setStatusTip(tr("Rewind to the previous time instant or time ruler notch"));
Chris@155 1638 connect(m_rwdAction, SIGNAL(triggered()), this, SLOT(rewind()));
Chris@155 1639 connect(this, SIGNAL(canRewind(bool)), m_rwdAction, SLOT(setEnabled(bool)));
Chris@155 1640
Chris@168 1641 QAction *playAction = toolbar->addAction(il.load("playpause"),
Chris@155 1642 tr("Play / Pause"));
Chris@155 1643 playAction->setCheckable(true);
Chris@155 1644 playAction->setShortcut(tr("Space"));
Chris@155 1645 playAction->setStatusTip(tr("Start or stop playback from the current position"));
Chris@155 1646 connect(playAction, SIGNAL(triggered()), this, SLOT(play()));
Chris@0 1647 connect(m_playSource, SIGNAL(playStatusChanged(bool)),
Chris@155 1648 playAction, SLOT(setChecked(bool)));
Chris@155 1649 connect(this, SIGNAL(canPlay(bool)), playAction, SLOT(setEnabled(bool)));
Chris@155 1650
Chris@168 1651 m_ffwdAction = toolbar->addAction(il.load("ffwd"),
Chris@155 1652 tr("Fast Forward"));
Chris@155 1653 m_ffwdAction->setShortcut(tr("PgDown"));
Chris@163 1654 m_ffwdAction->setStatusTip(tr("Fast-forward to the next time instant or time ruler notch"));
Chris@155 1655 connect(m_ffwdAction, SIGNAL(triggered()), this, SLOT(ffwd()));
Chris@155 1656 connect(this, SIGNAL(canFfwd(bool)), m_ffwdAction, SLOT(setEnabled(bool)));
Chris@155 1657
Chris@168 1658 QAction *ffwdEndAction = toolbar->addAction(il.load("ffwd-end"),
Chris@155 1659 tr("Fast Forward to End"));
Chris@155 1660 ffwdEndAction->setShortcut(tr("End"));
Chris@155 1661 ffwdEndAction->setStatusTip(tr("Fast-forward to the end"));
Chris@155 1662 connect(ffwdEndAction, SIGNAL(triggered()), this, SLOT(ffwdEnd()));
Chris@155 1663 connect(this, SIGNAL(canPlay(bool)), ffwdEndAction, SLOT(setEnabled(bool)));
Chris@0 1664
Chris@0 1665 toolbar = addToolBar(tr("Play Mode Toolbar"));
Chris@0 1666
Chris@168 1667 QAction *psAction = toolbar->addAction(il.load("playselection"),
Chris@155 1668 tr("Constrain Playback to Selection"));
Chris@155 1669 psAction->setCheckable(true);
Chris@155 1670 psAction->setChecked(m_viewManager->getPlaySelectionMode());
Chris@155 1671 psAction->setShortcut(tr("s"));
Chris@163 1672 psAction->setStatusTip(tr("Constrain playback to the selected regions"));
Chris@69 1673 connect(m_viewManager, SIGNAL(playSelectionModeChanged(bool)),
Chris@155 1674 psAction, SLOT(setChecked(bool)));
Chris@155 1675 connect(psAction, SIGNAL(triggered()), this, SLOT(playSelectionToggled()));
Chris@155 1676 connect(this, SIGNAL(canPlaySelection(bool)), psAction, SLOT(setEnabled(bool)));
Chris@155 1677
Chris@168 1678 QAction *plAction = toolbar->addAction(il.load("playloop"),
Chris@155 1679 tr("Loop Playback"));
Chris@155 1680 plAction->setCheckable(true);
Chris@155 1681 plAction->setChecked(m_viewManager->getPlayLoopMode());
Chris@155 1682 plAction->setShortcut(tr("l"));
Chris@155 1683 plAction->setStatusTip(tr("Loop playback"));
Chris@69 1684 connect(m_viewManager, SIGNAL(playLoopModeChanged(bool)),
Chris@155 1685 plAction, SLOT(setChecked(bool)));
Chris@155 1686 connect(plAction, SIGNAL(triggered()), this, SLOT(playLoopToggled()));
Chris@155 1687 connect(this, SIGNAL(canPlay(bool)), plAction, SLOT(setEnabled(bool)));
Chris@155 1688
Chris@180 1689 QAction *soAction = toolbar->addAction(il.load("solo"),
Chris@180 1690 tr("Solo Current Pane"));
Chris@180 1691 soAction->setCheckable(true);
Chris@180 1692 soAction->setChecked(m_viewManager->getPlaySoloMode());
Chris@180 1693 soAction->setShortcut(tr("o"));
Chris@180 1694 soAction->setStatusTip(tr("Solo the current pane during playback"));
Chris@180 1695 connect(m_viewManager, SIGNAL(playSoloModeChanged(bool)),
Chris@180 1696 soAction, SLOT(setChecked(bool)));
Chris@180 1697 connect(soAction, SIGNAL(triggered()), this, SLOT(playSoloToggled()));
Chris@180 1698 connect(this, SIGNAL(canPlay(bool)), soAction, SLOT(setEnabled(bool)));
Chris@180 1699
Chris@162 1700 m_keyReference->registerShortcut(playAction);
Chris@162 1701 m_keyReference->registerShortcut(psAction);
Chris@162 1702 m_keyReference->registerShortcut(plAction);
Chris@180 1703 m_keyReference->registerShortcut(soAction);
Chris@162 1704 m_keyReference->registerShortcut(m_rwdAction);
Chris@162 1705 m_keyReference->registerShortcut(m_ffwdAction);
Chris@162 1706 m_keyReference->registerShortcut(rwdStartAction);
Chris@162 1707 m_keyReference->registerShortcut(ffwdEndAction);
Chris@162 1708
Chris@155 1709 menu->addAction(playAction);
Chris@155 1710 menu->addAction(psAction);
Chris@155 1711 menu->addAction(plAction);
Chris@180 1712 menu->addAction(soAction);
Chris@155 1713 menu->addSeparator();
Chris@155 1714 menu->addAction(m_rwdAction);
Chris@155 1715 menu->addAction(m_ffwdAction);
Chris@155 1716 menu->addSeparator();
Chris@155 1717 menu->addAction(rwdStartAction);
Chris@155 1718 menu->addAction(ffwdEndAction);
Chris@155 1719 menu->addSeparator();
Chris@155 1720
Chris@155 1721 m_rightButtonPlaybackMenu->addAction(playAction);
Chris@155 1722 m_rightButtonPlaybackMenu->addAction(psAction);
Chris@155 1723 m_rightButtonPlaybackMenu->addAction(plAction);
Chris@180 1724 m_rightButtonPlaybackMenu->addAction(soAction);
Chris@155 1725 m_rightButtonPlaybackMenu->addSeparator();
Chris@155 1726 m_rightButtonPlaybackMenu->addAction(m_rwdAction);
Chris@155 1727 m_rightButtonPlaybackMenu->addAction(m_ffwdAction);
Chris@155 1728 m_rightButtonPlaybackMenu->addSeparator();
Chris@155 1729 m_rightButtonPlaybackMenu->addAction(rwdStartAction);
Chris@155 1730 m_rightButtonPlaybackMenu->addAction(ffwdEndAction);
Chris@155 1731 m_rightButtonPlaybackMenu->addSeparator();
Chris@155 1732
Chris@155 1733 QAction *fastAction = menu->addAction(tr("Speed Up"));
Chris@155 1734 fastAction->setShortcut(tr("Ctrl+PgUp"));
Chris@163 1735 fastAction->setStatusTip(tr("Time-stretch playback to speed it up without changing pitch"));
Chris@155 1736 connect(fastAction, SIGNAL(triggered()), this, SLOT(speedUpPlayback()));
Chris@155 1737 connect(this, SIGNAL(canSpeedUpPlayback(bool)), fastAction, SLOT(setEnabled(bool)));
Chris@155 1738
Chris@155 1739 QAction *slowAction = menu->addAction(tr("Slow Down"));
Chris@155 1740 slowAction->setShortcut(tr("Ctrl+PgDown"));
Chris@163 1741 slowAction->setStatusTip(tr("Time-stretch playback to slow it down without changing pitch"));
Chris@155 1742 connect(slowAction, SIGNAL(triggered()), this, SLOT(slowDownPlayback()));
Chris@155 1743 connect(this, SIGNAL(canSlowDownPlayback(bool)), slowAction, SLOT(setEnabled(bool)));
Chris@155 1744
Chris@155 1745 QAction *normalAction = menu->addAction(tr("Restore Normal Speed"));
Chris@155 1746 normalAction->setShortcut(tr("Ctrl+Home"));
Chris@163 1747 normalAction->setStatusTip(tr("Restore non-time-stretched playback"));
Chris@155 1748 connect(normalAction, SIGNAL(triggered()), this, SLOT(restoreNormalPlayback()));
Chris@155 1749 connect(this, SIGNAL(canChangePlaybackSpeed(bool)), normalAction, SLOT(setEnabled(bool)));
Chris@155 1750
Chris@162 1751 m_keyReference->registerShortcut(fastAction);
Chris@162 1752 m_keyReference->registerShortcut(slowAction);
Chris@162 1753 m_keyReference->registerShortcut(normalAction);
Chris@162 1754
Chris@155 1755 m_rightButtonPlaybackMenu->addAction(fastAction);
Chris@155 1756 m_rightButtonPlaybackMenu->addAction(slowAction);
Chris@155 1757 m_rightButtonPlaybackMenu->addAction(normalAction);
Chris@0 1758
Chris@0 1759 toolbar = addToolBar(tr("Edit Toolbar"));
Chris@0 1760 CommandHistory::getInstance()->registerToolbar(toolbar);
Chris@0 1761
Chris@162 1762 m_keyReference->setCategory(tr("Tool Selection"));
Chris@162 1763
Chris@0 1764 toolbar = addToolBar(tr("Tools Toolbar"));
Chris@0 1765 QActionGroup *group = new QActionGroup(this);
Chris@0 1766
Chris@168 1767 QAction *action = toolbar->addAction(il.load("navigate"),
Chris@155 1768 tr("Navigate"));
Chris@0 1769 action->setCheckable(true);
Chris@0 1770 action->setChecked(true);
Chris@0 1771 action->setShortcut(tr("1"));
Chris@90 1772 action->setStatusTip(tr("Navigate"));
Chris@0 1773 connect(action, SIGNAL(triggered()), this, SLOT(toolNavigateSelected()));
Chris@0 1774 group->addAction(action);
Chris@162 1775 m_keyReference->registerShortcut(action);
Chris@0 1776 m_toolActions[ViewManager::NavigateMode] = action;
Chris@0 1777
Chris@168 1778 action = toolbar->addAction(il.load("select"),
Chris@0 1779 tr("Select"));
Chris@0 1780 action->setCheckable(true);
Chris@0 1781 action->setShortcut(tr("2"));
Chris@90 1782 action->setStatusTip(tr("Select ranges"));
Chris@0 1783 connect(action, SIGNAL(triggered()), this, SLOT(toolSelectSelected()));
Chris@0 1784 group->addAction(action);
Chris@162 1785 m_keyReference->registerShortcut(action);
Chris@0 1786 m_toolActions[ViewManager::SelectMode] = action;
Chris@0 1787
Chris@168 1788 action = toolbar->addAction(il.load("move"),
Chris@0 1789 tr("Edit"));
Chris@0 1790 action->setCheckable(true);
Chris@0 1791 action->setShortcut(tr("3"));
Chris@90 1792 action->setStatusTip(tr("Edit items in layer"));
Chris@0 1793 connect(action, SIGNAL(triggered()), this, SLOT(toolEditSelected()));
Chris@0 1794 connect(this, SIGNAL(canEditLayer(bool)), action, SLOT(setEnabled(bool)));
Chris@0 1795 group->addAction(action);
Chris@162 1796 m_keyReference->registerShortcut(action);
Chris@0 1797 m_toolActions[ViewManager::EditMode] = action;
Chris@0 1798
Chris@168 1799 action = toolbar->addAction(il.load("draw"),
Chris@0 1800 tr("Draw"));
Chris@0 1801 action->setCheckable(true);
Chris@0 1802 action->setShortcut(tr("4"));
Chris@90 1803 action->setStatusTip(tr("Draw new items in layer"));
Chris@0 1804 connect(action, SIGNAL(triggered()), this, SLOT(toolDrawSelected()));
Chris@0 1805 connect(this, SIGNAL(canEditLayer(bool)), action, SLOT(setEnabled(bool)));
Chris@0 1806 group->addAction(action);
Chris@162 1807 m_keyReference->registerShortcut(action);
Chris@0 1808 m_toolActions[ViewManager::DrawMode] = action;
Chris@0 1809
Chris@168 1810 action = toolbar->addAction(il.load("measure"),
Chris@151 1811 tr("Measure"));
Chris@151 1812 action->setCheckable(true);
Chris@151 1813 action->setShortcut(tr("5"));
Chris@151 1814 action->setStatusTip(tr("Make measurements in layer"));
Chris@151 1815 connect(action, SIGNAL(triggered()), this, SLOT(toolMeasureSelected()));
Chris@169 1816 connect(this, SIGNAL(canMeasureLayer(bool)), action, SLOT(setEnabled(bool)));
Chris@151 1817 group->addAction(action);
Chris@162 1818 m_keyReference->registerShortcut(action);
Chris@151 1819 m_toolActions[ViewManager::MeasureMode] = action;
Chris@151 1820
Chris@168 1821 // action = toolbar->addAction(il.load("text"),
Chris@0 1822 // tr("Text"));
Chris@0 1823 // action->setCheckable(true);
Chris@0 1824 // action->setShortcut(tr("5"));
Chris@0 1825 // connect(action, SIGNAL(triggered()), this, SLOT(toolTextSelected()));
Chris@0 1826 // group->addAction(action);
Chris@0 1827 // m_toolActions[ViewManager::TextMode] = action;
Chris@0 1828
Chris@0 1829 toolNavigateSelected();
Chris@163 1830
Chris@163 1831 Pane::registerShortcuts(*m_keyReference);
Chris@0 1832 }
Chris@0 1833
Chris@0 1834 void
Chris@0 1835 MainWindow::updateMenuStates()
Chris@0 1836 {
Chris@117 1837 Pane *currentPane = 0;
Chris@117 1838 Layer *currentLayer = 0;
Chris@117 1839
Chris@117 1840 if (m_paneStack) currentPane = m_paneStack->getCurrentPane();
Chris@117 1841 if (currentPane) currentLayer = currentPane->getSelectedLayer();
Chris@117 1842
Chris@0 1843 bool haveCurrentPane =
Chris@117 1844 (currentPane != 0);
Chris@0 1845 bool haveCurrentLayer =
Chris@117 1846 (haveCurrentPane &&
Chris@117 1847 (currentLayer != 0));
Chris@0 1848 bool haveMainModel =
Chris@0 1849 (getMainModel() != 0);
Chris@0 1850 bool havePlayTarget =
Chris@0 1851 (m_playTarget != 0);
Chris@0 1852 bool haveSelection =
Chris@0 1853 (m_viewManager &&
Chris@0 1854 !m_viewManager->getSelections().empty());
Chris@0 1855 bool haveCurrentEditableLayer =
Chris@0 1856 (haveCurrentLayer &&
Chris@117 1857 currentLayer->isLayerEditable());
Chris@0 1858 bool haveCurrentTimeInstantsLayer =
Chris@0 1859 (haveCurrentLayer &&
Chris@117 1860 dynamic_cast<TimeInstantLayer *>(currentLayer));
Chris@0 1861 bool haveCurrentTimeValueLayer =
Chris@0 1862 (haveCurrentLayer &&
Chris@117 1863 dynamic_cast<TimeValueLayer *>(currentLayer));
Chris@0 1864 bool haveCurrentColour3DPlot =
Chris@0 1865 (haveCurrentLayer &&
Chris@117 1866 dynamic_cast<Colour3DPlotLayer *>(currentLayer));
Chris@0 1867 bool haveClipboardContents =
Chris@0 1868 (m_viewManager &&
Chris@0 1869 !m_viewManager->getClipboard().empty());
Chris@0 1870
Chris@0 1871 emit canAddPane(haveMainModel);
Chris@0 1872 emit canDeleteCurrentPane(haveCurrentPane);
Chris@0 1873 emit canZoom(haveMainModel && haveCurrentPane);
Chris@0 1874 emit canScroll(haveMainModel && haveCurrentPane);
Chris@0 1875 emit canAddLayer(haveMainModel && haveCurrentPane);
Chris@0 1876 emit canImportMoreAudio(haveMainModel);
Chris@0 1877 emit canImportLayer(haveMainModel && haveCurrentPane);
Chris@0 1878 emit canExportAudio(haveMainModel);
Chris@0 1879 emit canExportLayer(haveMainModel &&
Chris@0 1880 (haveCurrentEditableLayer || haveCurrentColour3DPlot));
Chris@121 1881 emit canExportImage(haveMainModel && haveCurrentPane);
Chris@0 1882 emit canDeleteCurrentLayer(haveCurrentLayer);
Chris@0 1883 emit canRenameLayer(haveCurrentLayer);
Chris@0 1884 emit canEditLayer(haveCurrentEditableLayer);
Chris@169 1885 emit canMeasureLayer(haveCurrentLayer);
Chris@0 1886 emit canSelect(haveMainModel && haveCurrentPane);
Chris@155 1887 emit canPlay(havePlayTarget);
Chris@155 1888 emit canFfwd(true);
Chris@155 1889 emit canRewind(true);
Chris@0 1890 emit canPaste(haveCurrentEditableLayer && haveClipboardContents);
Chris@0 1891 emit canInsertInstant(haveCurrentPane);
Chris@81 1892 emit canInsertInstantsAtBoundaries(haveCurrentPane && haveSelection);
Chris@0 1893 emit canPlaySelection(haveMainModel && havePlayTarget && haveSelection);
Chris@0 1894 emit canClearSelection(haveSelection);
Chris@0 1895 emit canEditSelection(haveSelection && haveCurrentEditableLayer);
Chris@0 1896 emit canSave(m_sessionFile != "" && m_documentModified);
Chris@155 1897
Chris@164 1898 if (m_viewManager &&
Chris@164 1899 (m_viewManager->getToolMode() == ViewManager::MeasureMode)) {
Chris@164 1900 emit canDeleteSelection(haveCurrentLayer);
Chris@164 1901 m_deleteSelectedAction->setText(tr("&Delete Current Measurement"));
Chris@164 1902 m_deleteSelectedAction->setStatusTip(tr("Delete the measurement currently under the mouse pointer"));
Chris@164 1903 } else {
Chris@164 1904 emit canDeleteSelection(haveSelection && haveCurrentEditableLayer);
Chris@164 1905 m_deleteSelectedAction->setText(tr("&Delete Selected Items"));
Chris@164 1906 m_deleteSelectedAction->setStatusTip(tr("Delete items in current selection from the current layer"));
Chris@164 1907 }
Chris@164 1908
Chris@155 1909 emit canChangePlaybackSpeed(true);
Chris@155 1910 int v = m_playSpeed->value();
Chris@155 1911 emit canSpeedUpPlayback(v < m_playSpeed->maximum());
Chris@155 1912 emit canSlowDownPlayback(v > m_playSpeed->minimum());
Chris@155 1913
Chris@155 1914 if (m_ffwdAction && m_rwdAction) {
Chris@155 1915 if (haveCurrentTimeInstantsLayer) {
Chris@155 1916 m_ffwdAction->setText(tr("Fast Forward to Next Instant"));
Chris@155 1917 m_ffwdAction->setStatusTip(tr("Fast forward to the next time instant in the current layer"));
Chris@155 1918 m_rwdAction->setText(tr("Rewind to Previous Instant"));
Chris@155 1919 m_rwdAction->setStatusTip(tr("Rewind to the previous time instant in the current layer"));
Chris@155 1920 } else if (haveCurrentTimeValueLayer) {
Chris@155 1921 m_ffwdAction->setText(tr("Fast Forward to Next Point"));
Chris@155 1922 m_ffwdAction->setStatusTip(tr("Fast forward to the next point in the current layer"));
Chris@155 1923 m_rwdAction->setText(tr("Rewind to Previous Point"));
Chris@155 1924 m_rwdAction->setStatusTip(tr("Rewind to the previous point in the current layer"));
Chris@155 1925 } else {
Chris@155 1926 m_ffwdAction->setText(tr("Fast Forward"));
Chris@155 1927 m_ffwdAction->setStatusTip(tr("Fast forward"));
Chris@155 1928 m_rwdAction->setText(tr("Rewind"));
Chris@155 1929 m_rwdAction->setStatusTip(tr("Rewind"));
Chris@155 1930 }
Chris@155 1931 }
Chris@0 1932 }
Chris@0 1933
Chris@0 1934 void
Chris@0 1935 MainWindow::updateDescriptionLabel()
Chris@0 1936 {
Chris@0 1937 if (!getMainModel()) {
Chris@0 1938 m_descriptionLabel->setText(tr("No audio file loaded."));
Chris@0 1939 return;
Chris@0 1940 }
Chris@0 1941
Chris@0 1942 QString description;
Chris@0 1943
Chris@0 1944 size_t ssr = getMainModel()->getSampleRate();
Chris@0 1945 size_t tsr = ssr;
Chris@0 1946 if (m_playSource) tsr = m_playSource->getTargetSampleRate();
Chris@0 1947
Chris@0 1948 if (ssr != tsr) {
Chris@0 1949 description = tr("%1Hz (resampling to %2Hz)").arg(ssr).arg(tsr);
Chris@0 1950 } else {
Chris@0 1951 description = QString("%1Hz").arg(ssr);
Chris@0 1952 }
Chris@0 1953
Chris@0 1954 description = QString("%1 - %2")
Chris@0 1955 .arg(RealTime::frame2RealTime(getMainModel()->getEndFrame(), ssr)
Chris@0 1956 .toText(false).c_str())
Chris@0 1957 .arg(description);
Chris@0 1958
Chris@0 1959 m_descriptionLabel->setText(description);
Chris@0 1960 }
Chris@0 1961
Chris@0 1962 void
Chris@0 1963 MainWindow::documentModified()
Chris@0 1964 {
Chris@0 1965 // std::cerr << "MainWindow::documentModified" << std::endl;
Chris@0 1966
Chris@0 1967 if (!m_documentModified) {
Chris@0 1968 setWindowTitle(tr("%1 (modified)").arg(windowTitle()));
Chris@0 1969 }
Chris@0 1970
Chris@0 1971 m_documentModified = true;
Chris@0 1972 updateMenuStates();
Chris@0 1973 }
Chris@0 1974
Chris@0 1975 void
Chris@0 1976 MainWindow::documentRestored()
Chris@0 1977 {
Chris@0 1978 // std::cerr << "MainWindow::documentRestored" << std::endl;
Chris@0 1979
Chris@0 1980 if (m_documentModified) {
Chris@0 1981 QString wt(windowTitle());
Chris@0 1982 wt.replace(tr(" (modified)"), "");
Chris@0 1983 setWindowTitle(wt);
Chris@0 1984 }
Chris@0 1985
Chris@0 1986 m_documentModified = false;
Chris@0 1987 updateMenuStates();
Chris@0 1988 }
Chris@0 1989
Chris@0 1990 void
Chris@0 1991 MainWindow::playLoopToggled()
Chris@0 1992 {
Chris@0 1993 QAction *action = dynamic_cast<QAction *>(sender());
Chris@0 1994
Chris@0 1995 if (action) {
Chris@0 1996 m_viewManager->setPlayLoopMode(action->isChecked());
Chris@0 1997 } else {
Chris@0 1998 m_viewManager->setPlayLoopMode(!m_viewManager->getPlayLoopMode());
Chris@0 1999 }
Chris@0 2000 }
Chris@0 2001
Chris@0 2002 void
Chris@0 2003 MainWindow::playSelectionToggled()
Chris@0 2004 {
Chris@0 2005 QAction *action = dynamic_cast<QAction *>(sender());
Chris@0 2006
Chris@0 2007 if (action) {
Chris@0 2008 m_viewManager->setPlaySelectionMode(action->isChecked());
Chris@0 2009 } else {
Chris@0 2010 m_viewManager->setPlaySelectionMode(!m_viewManager->getPlaySelectionMode());
Chris@0 2011 }
Chris@0 2012 }
Chris@0 2013
Chris@0 2014 void
Chris@180 2015 MainWindow::playSoloToggled()
Chris@180 2016 {
Chris@180 2017 QAction *action = dynamic_cast<QAction *>(sender());
Chris@180 2018
Chris@180 2019 if (action) {
Chris@180 2020 m_viewManager->setPlaySoloMode(action->isChecked());
Chris@180 2021 } else {
Chris@180 2022 m_viewManager->setPlaySoloMode(!m_viewManager->getPlaySoloMode());
Chris@180 2023 }
Chris@180 2024
Chris@180 2025 if (!m_viewManager->getPlaySoloMode()) {
Chris@180 2026 m_viewManager->setPlaybackModel(0);
Chris@180 2027 if (m_playSource) {
Chris@180 2028 m_playSource->clearSoloModelSet();
Chris@180 2029 }
Chris@180 2030 }
Chris@180 2031 }
Chris@180 2032
Chris@180 2033 void
Chris@116 2034 MainWindow::currentPaneChanged(Pane *p)
Chris@0 2035 {
Chris@0 2036 updateMenuStates();
Chris@116 2037 updateVisibleRangeDisplay(p);
Chris@180 2038
Chris@180 2039 if (!p) return;
Chris@180 2040
Chris@180 2041 if (!(m_viewManager &&
Chris@180 2042 m_playSource &&
Chris@180 2043 m_viewManager->getPlaySoloMode())) {
Chris@180 2044 if (m_viewManager) m_viewManager->setPlaybackModel(0);
Chris@180 2045 return;
Chris@180 2046 }
Chris@180 2047
Chris@180 2048 Model *prevPlaybackModel = m_viewManager->getPlaybackModel();
Chris@180 2049
Chris@180 2050 std::set<Model *> soloModels;
Chris@180 2051
Chris@180 2052 for (int i = 0; i < p->getLayerCount(); ++i) {
Chris@180 2053 Layer *layer = p->getLayer(i);
Chris@180 2054 if (dynamic_cast<TimeRulerLayer *>(layer)) {
Chris@180 2055 continue;
Chris@180 2056 }
Chris@180 2057 if (layer && layer->getModel()) {
Chris@180 2058 Model *model = layer->getModel();
Chris@180 2059 if (dynamic_cast<RangeSummarisableTimeValueModel *>(model)) {
Chris@180 2060 m_viewManager->setPlaybackModel(model);
Chris@180 2061 }
Chris@180 2062 soloModels.insert(model);
Chris@180 2063 }
Chris@180 2064 }
Chris@180 2065
Chris@180 2066 RangeSummarisableTimeValueModel *a =
Chris@180 2067 dynamic_cast<RangeSummarisableTimeValueModel *>(prevPlaybackModel);
Chris@180 2068 RangeSummarisableTimeValueModel *b =
Chris@180 2069 dynamic_cast<RangeSummarisableTimeValueModel *>(m_viewManager->
Chris@180 2070 getPlaybackModel());
Chris@180 2071
Chris@180 2072 m_playSource->setSoloModelSet(soloModels);
Chris@180 2073
Chris@180 2074 if (a && b && (a != b)) {
Chris@180 2075 int frame = m_playSource->getCurrentPlayingFrame();
Chris@180 2076 //!!! I don't really believe that these functions are the right way around
Chris@180 2077 int rframe = a->alignFromReference(frame);
Chris@180 2078 int bframe = b->alignToReference(rframe);
Chris@180 2079 if (m_playSource->isPlaying()) m_playSource->play(bframe);
Chris@180 2080 }
Chris@0 2081 }
Chris@0 2082
Chris@0 2083 void
Chris@116 2084 MainWindow::currentLayerChanged(Pane *p, Layer *)
Chris@0 2085 {
Chris@0 2086 updateMenuStates();
Chris@116 2087 updateVisibleRangeDisplay(p);
Chris@0 2088 }
Chris@0 2089
Chris@0 2090 void
Chris@0 2091 MainWindow::toolNavigateSelected()
Chris@0 2092 {
Chris@0 2093 m_viewManager->setToolMode(ViewManager::NavigateMode);
Chris@0 2094 }
Chris@0 2095
Chris@0 2096 void
Chris@0 2097 MainWindow::toolSelectSelected()
Chris@0 2098 {
Chris@0 2099 m_viewManager->setToolMode(ViewManager::SelectMode);
Chris@0 2100 }
Chris@0 2101
Chris@0 2102 void
Chris@0 2103 MainWindow::toolEditSelected()
Chris@0 2104 {
Chris@0 2105 m_viewManager->setToolMode(ViewManager::EditMode);
Chris@0 2106 }
Chris@0 2107
Chris@0 2108 void
Chris@0 2109 MainWindow::toolDrawSelected()
Chris@0 2110 {
Chris@0 2111 m_viewManager->setToolMode(ViewManager::DrawMode);
Chris@0 2112 }
Chris@0 2113
Chris@151 2114 void
Chris@151 2115 MainWindow::toolMeasureSelected()
Chris@151 2116 {
Chris@151 2117 m_viewManager->setToolMode(ViewManager::MeasureMode);
Chris@151 2118 }
Chris@151 2119
Chris@0 2120 //void
Chris@0 2121 //MainWindow::toolTextSelected()
Chris@0 2122 //{
Chris@0 2123 // m_viewManager->setToolMode(ViewManager::TextMode);
Chris@0 2124 //}
Chris@0 2125
Chris@0 2126 void
Chris@0 2127 MainWindow::selectAll()
Chris@0 2128 {
Chris@0 2129 if (!getMainModel()) return;
Chris@0 2130 m_viewManager->setSelection(Selection(getMainModel()->getStartFrame(),
Chris@0 2131 getMainModel()->getEndFrame()));
Chris@0 2132 }
Chris@0 2133
Chris@0 2134 void
Chris@0 2135 MainWindow::selectToStart()
Chris@0 2136 {
Chris@0 2137 if (!getMainModel()) return;
Chris@0 2138 m_viewManager->setSelection(Selection(getMainModel()->getStartFrame(),
Chris@0 2139 m_viewManager->getGlobalCentreFrame()));
Chris@0 2140 }
Chris@0 2141
Chris@0 2142 void
Chris@0 2143 MainWindow::selectToEnd()
Chris@0 2144 {
Chris@0 2145 if (!getMainModel()) return;
Chris@0 2146 m_viewManager->setSelection(Selection(m_viewManager->getGlobalCentreFrame(),
Chris@0 2147 getMainModel()->getEndFrame()));
Chris@0 2148 }
Chris@0 2149
Chris@0 2150 void
Chris@0 2151 MainWindow::selectVisible()
Chris@0 2152 {
Chris@0 2153 Model *model = getMainModel();
Chris@0 2154 if (!model) return;
Chris@0 2155
Chris@0 2156 Pane *currentPane = m_paneStack->getCurrentPane();
Chris@0 2157 if (!currentPane) return;
Chris@0 2158
Chris@0 2159 size_t startFrame, endFrame;
Chris@0 2160
Chris@0 2161 if (currentPane->getStartFrame() < 0) startFrame = 0;
Chris@0 2162 else startFrame = currentPane->getStartFrame();
Chris@0 2163
Chris@0 2164 if (currentPane->getEndFrame() > model->getEndFrame()) endFrame = model->getEndFrame();
Chris@0 2165 else endFrame = currentPane->getEndFrame();
Chris@0 2166
Chris@0 2167 m_viewManager->setSelection(Selection(startFrame, endFrame));
Chris@0 2168 }
Chris@0 2169
Chris@0 2170 void
Chris@0 2171 MainWindow::clearSelection()
Chris@0 2172 {
Chris@0 2173 m_viewManager->clearSelections();
Chris@0 2174 }
Chris@0 2175
Chris@0 2176 void
Chris@0 2177 MainWindow::cut()
Chris@0 2178 {
Chris@0 2179 Pane *currentPane = m_paneStack->getCurrentPane();
Chris@0 2180 if (!currentPane) return;
Chris@0 2181
Chris@0 2182 Layer *layer = currentPane->getSelectedLayer();
Chris@0 2183 if (!layer) return;
Chris@0 2184
Chris@0 2185 Clipboard &clipboard = m_viewManager->getClipboard();
Chris@0 2186 clipboard.clear();
Chris@0 2187
Chris@0 2188 MultiSelection::SelectionList selections = m_viewManager->getSelections();
Chris@0 2189
Chris@0 2190 CommandHistory::getInstance()->startCompoundOperation(tr("Cut"), true);
Chris@0 2191
Chris@0 2192 for (MultiSelection::SelectionList::iterator i = selections.begin();
Chris@0 2193 i != selections.end(); ++i) {
Chris@0 2194 layer->copy(*i, clipboard);
Chris@0 2195 layer->deleteSelection(*i);
Chris@0 2196 }
Chris@0 2197
Chris@0 2198 CommandHistory::getInstance()->endCompoundOperation();
Chris@0 2199 }
Chris@0 2200
Chris@0 2201 void
Chris@0 2202 MainWindow::copy()
Chris@0 2203 {
Chris@0 2204 Pane *currentPane = m_paneStack->getCurrentPane();
Chris@0 2205 if (!currentPane) return;
Chris@0 2206
Chris@0 2207 Layer *layer = currentPane->getSelectedLayer();
Chris@0 2208 if (!layer) return;
Chris@0 2209
Chris@0 2210 Clipboard &clipboard = m_viewManager->getClipboard();
Chris@0 2211 clipboard.clear();
Chris@0 2212
Chris@0 2213 MultiSelection::SelectionList selections = m_viewManager->getSelections();
Chris@0 2214
Chris@0 2215 for (MultiSelection::SelectionList::iterator i = selections.begin();
Chris@0 2216 i != selections.end(); ++i) {
Chris@0 2217 layer->copy(*i, clipboard);
Chris@0 2218 }
Chris@0 2219 }
Chris@0 2220
Chris@0 2221 void
Chris@0 2222 MainWindow::paste()
Chris@0 2223 {
Chris@0 2224 Pane *currentPane = m_paneStack->getCurrentPane();
Chris@0 2225 if (!currentPane) return;
Chris@0 2226
Chris@0 2227 //!!! if we have no current layer, we should create one of the most
Chris@0 2228 // appropriate type
Chris@0 2229
Chris@0 2230 Layer *layer = currentPane->getSelectedLayer();
Chris@0 2231 if (!layer) return;
Chris@0 2232
Chris@0 2233 Clipboard &clipboard = m_viewManager->getClipboard();
Chris@0 2234 Clipboard::PointList contents = clipboard.getPoints();
Chris@0 2235 /*
Chris@0 2236 long minFrame = 0;
Chris@0 2237 bool have = false;
Chris@0 2238 for (int i = 0; i < contents.size(); ++i) {
Chris@0 2239 if (!contents[i].haveFrame()) continue;
Chris@0 2240 if (!have || contents[i].getFrame() < minFrame) {
Chris@0 2241 minFrame = contents[i].getFrame();
Chris@0 2242 have = true;
Chris@0 2243 }
Chris@0 2244 }
Chris@0 2245
Chris@0 2246 long frameOffset = long(m_viewManager->getGlobalCentreFrame()) - minFrame;
Chris@0 2247
Chris@0 2248 layer->paste(clipboard, frameOffset);
Chris@0 2249 */
Chris@0 2250 layer->paste(clipboard, 0, true);
Chris@0 2251 }
Chris@0 2252
Chris@0 2253 void
Chris@0 2254 MainWindow::deleteSelected()
Chris@0 2255 {
Chris@0 2256 if (m_paneStack->getCurrentPane() &&
Chris@0 2257 m_paneStack->getCurrentPane()->getSelectedLayer()) {
Chris@164 2258
Chris@164 2259 Layer *layer = m_paneStack->getCurrentPane()->getSelectedLayer();
Chris@164 2260
Chris@164 2261 if (m_viewManager &&
Chris@164 2262 (m_viewManager->getToolMode() == ViewManager::MeasureMode)) {
Chris@164 2263
Chris@164 2264 layer->deleteCurrentMeasureRect();
Chris@164 2265
Chris@164 2266 } else {
Chris@164 2267
Chris@164 2268 MultiSelection::SelectionList selections =
Chris@164 2269 m_viewManager->getSelections();
Chris@164 2270
Chris@164 2271 for (MultiSelection::SelectionList::iterator i = selections.begin();
Chris@164 2272 i != selections.end(); ++i) {
Chris@164 2273 layer->deleteSelection(*i);
Chris@164 2274 }
Chris@0 2275 }
Chris@0 2276 }
Chris@0 2277 }
Chris@0 2278
Chris@0 2279 void
Chris@0 2280 MainWindow::insertInstant()
Chris@0 2281 {
Chris@0 2282 int frame = m_viewManager->getPlaybackFrame();
Chris@81 2283 insertInstantAt(frame);
Chris@81 2284 }
Chris@81 2285
Chris@81 2286 void
Chris@81 2287 MainWindow::insertInstantsAtBoundaries()
Chris@81 2288 {
Chris@81 2289 MultiSelection::SelectionList selections = m_viewManager->getSelections();
Chris@81 2290 for (MultiSelection::SelectionList::iterator i = selections.begin();
Chris@81 2291 i != selections.end(); ++i) {
Chris@81 2292 size_t start = i->getStartFrame();
Chris@81 2293 size_t end = i->getEndFrame();
Chris@81 2294 if (start != end) {
Chris@81 2295 insertInstantAt(i->getStartFrame());
Chris@81 2296 insertInstantAt(i->getEndFrame());
Chris@81 2297 }
Chris@81 2298 }
Chris@81 2299 }
Chris@81 2300
Chris@81 2301 void
Chris@81 2302 MainWindow::insertInstantAt(size_t frame)
Chris@81 2303 {
Chris@0 2304 Pane *pane = m_paneStack->getCurrentPane();
Chris@0 2305 if (!pane) {
Chris@0 2306 return;
Chris@0 2307 }
Chris@0 2308
Chris@0 2309 Layer *layer = dynamic_cast<TimeInstantLayer *>
Chris@0 2310 (pane->getSelectedLayer());
Chris@0 2311
Chris@0 2312 if (!layer) {
Chris@0 2313 for (int i = pane->getLayerCount(); i > 0; --i) {
Chris@0 2314 layer = dynamic_cast<TimeInstantLayer *>(pane->getLayer(i - 1));
Chris@0 2315 if (layer) break;
Chris@0 2316 }
Chris@0 2317
Chris@0 2318 if (!layer) {
Chris@0 2319 CommandHistory::getInstance()->startCompoundOperation
Chris@0 2320 (tr("Add Point"), true);
Chris@0 2321 layer = m_document->createEmptyLayer(LayerFactory::TimeInstants);
Chris@0 2322 if (layer) {
Chris@0 2323 m_document->addLayerToView(pane, layer);
Chris@0 2324 m_paneStack->setCurrentLayer(pane, layer);
Chris@0 2325 }
Chris@0 2326 CommandHistory::getInstance()->endCompoundOperation();
Chris@0 2327 }
Chris@0 2328 }
Chris@0 2329
Chris@0 2330 if (layer) {
Chris@0 2331
Chris@0 2332 Model *model = layer->getModel();
Chris@0 2333 SparseOneDimensionalModel *sodm = dynamic_cast<SparseOneDimensionalModel *>
Chris@0 2334 (model);
Chris@0 2335
Chris@0 2336 if (sodm) {
Chris@0 2337 SparseOneDimensionalModel::Point point
Chris@0 2338 (frame, QString("%1").arg(sodm->getPointCount() + 1));
Chris@0 2339 CommandHistory::getInstance()->addCommand
Chris@0 2340 (new SparseOneDimensionalModel::AddPointCommand(sodm, point,
Chris@0 2341 tr("Add Points")),
Chris@0 2342 true, true); // bundled
Chris@0 2343 }
Chris@0 2344 }
Chris@0 2345 }
Chris@0 2346
Chris@0 2347 void
Chris@0 2348 MainWindow::importAudio()
Chris@0 2349 {
Chris@88 2350 QString path = getOpenFileName(FileFinder::AudioFile);
Chris@0 2351
Chris@0 2352 if (path != "") {
Chris@82 2353 if (openAudioFile(path, ReplaceMainModel) == FileOpenFailed) {
Chris@0 2354 QMessageBox::critical(this, tr("Failed to open file"),
Chris@0 2355 tr("Audio file \"%1\" could not be opened").arg(path));
Chris@0 2356 }
Chris@0 2357 }
Chris@0 2358 }
Chris@0 2359
Chris@0 2360 void
Chris@0 2361 MainWindow::importMoreAudio()
Chris@0 2362 {
Chris@88 2363 QString path = getOpenFileName(FileFinder::AudioFile);
Chris@0 2364
Chris@0 2365 if (path != "") {
Chris@82 2366 if (openAudioFile(path, CreateAdditionalModel) == FileOpenFailed) {
Chris@0 2367 QMessageBox::critical(this, tr("Failed to open file"),
Chris@0 2368 tr("Audio file \"%1\" could not be opened").arg(path));
Chris@0 2369 }
Chris@0 2370 }
Chris@0 2371 }
Chris@0 2372
Chris@0 2373 void
Chris@0 2374 MainWindow::exportAudio()
Chris@0 2375 {
Chris@0 2376 if (!getMainModel()) return;
Chris@0 2377
Chris@88 2378 QString path = getSaveFileName(FileFinder::AudioFile);
Chris@0 2379
Chris@0 2380 if (path == "") return;
Chris@0 2381
Chris@0 2382 bool ok = false;
Chris@0 2383 QString error;
Chris@0 2384
Chris@0 2385 MultiSelection ms = m_viewManager->getSelection();
Chris@0 2386 MultiSelection::SelectionList selections = m_viewManager->getSelections();
Chris@0 2387
Chris@0 2388 bool multiple = false;
Chris@0 2389
Chris@38 2390 MultiSelection *selectionToWrite = 0;
Chris@38 2391
Chris@38 2392 if (selections.size() == 1) {
Chris@0 2393
Chris@0 2394 QStringList items;
Chris@0 2395 items << tr("Export the selected region only")
Chris@0 2396 << tr("Export the whole audio file");
Chris@0 2397
Chris@0 2398 bool ok = false;
Chris@0 2399 QString item = ListInputDialog::getItem
Chris@0 2400 (this, tr("Select region to export"),
Chris@0 2401 tr("Which region from the original audio file do you want to export?"),
Chris@0 2402 items, 0, &ok);
Chris@0 2403
Chris@0 2404 if (!ok || item.isEmpty()) return;
Chris@0 2405
Chris@38 2406 if (item == items[0]) selectionToWrite = &ms;
Chris@38 2407
Chris@38 2408 } else if (selections.size() > 1) {
Chris@0 2409
Chris@0 2410 QStringList items;
Chris@0 2411 items << tr("Export the selected regions into a single audio file")
Chris@0 2412 << tr("Export the selected regions into separate files")
Chris@0 2413 << tr("Export the whole audio file");
Chris@0 2414
Chris@0 2415 QString item = ListInputDialog::getItem
Chris@0 2416 (this, tr("Select region to export"),
Chris@0 2417 tr("Multiple regions of the original audio file are selected.\nWhat do you want to export?"),
Chris@0 2418 items, 0, &ok);
Chris@0 2419
Chris@0 2420 if (!ok || item.isEmpty()) return;
Chris@0 2421
Chris@0 2422 if (item == items[0]) {
Chris@0 2423
Chris@38 2424 selectionToWrite = &ms;
Chris@38 2425
Chris@38 2426 } else if (item == items[1]) {
Chris@0 2427
Chris@0 2428 multiple = true;
Chris@0 2429
Chris@0 2430 int n = 1;
Chris@0 2431 QString base = path;
Chris@0 2432 base.replace(".wav", "");
Chris@0 2433
Chris@0 2434 for (MultiSelection::SelectionList::iterator i = selections.begin();
Chris@0 2435 i != selections.end(); ++i) {
Chris@0 2436
Chris@0 2437 MultiSelection subms;
Chris@0 2438 subms.setSelection(*i);
Chris@0 2439
Chris@0 2440 QString subpath = QString("%1.%2.wav").arg(base).arg(n);
Chris@0 2441 ++n;
Chris@0 2442
Chris@0 2443 if (QFileInfo(subpath).exists()) {
Chris@0 2444 error = tr("Fragment file %1 already exists, aborting").arg(subpath);
Chris@0 2445 break;
Chris@0 2446 }
Chris@0 2447
Chris@38 2448 WavFileWriter subwriter(subpath,
Chris@38 2449 getMainModel()->getSampleRate(),
Chris@38 2450 getMainModel()->getChannelCount());
Chris@38 2451 subwriter.writeModel(getMainModel(), &subms);
Chris@0 2452 ok = subwriter.isOK();
Chris@0 2453
Chris@0 2454 if (!ok) {
Chris@0 2455 error = subwriter.getError();
Chris@0 2456 break;
Chris@0 2457 }
Chris@0 2458 }
Chris@0 2459 }
Chris@0 2460 }
Chris@0 2461
Chris@38 2462 if (!multiple) {
Chris@38 2463 WavFileWriter writer(path,
Chris@38 2464 getMainModel()->getSampleRate(),
Chris@38 2465 getMainModel()->getChannelCount());
Chris@38 2466 writer.writeModel(getMainModel(), selectionToWrite);
Chris@38 2467 ok = writer.isOK();
Chris@38 2468 error = writer.getError();
Chris@0 2469 }
Chris@0 2470
Chris@0 2471 if (ok) {
Chris@0 2472 if (!multiple) {
Chris@34 2473 m_recentFiles.addFile(path);
Chris@0 2474 }
Chris@0 2475 } else {
Chris@0 2476 QMessageBox::critical(this, tr("Failed to write file"), error);
Chris@0 2477 }
Chris@0 2478 }
Chris@0 2479
Chris@0 2480 void
Chris@0 2481 MainWindow::importLayer()
Chris@0 2482 {
Chris@0 2483 Pane *pane = m_paneStack->getCurrentPane();
Chris@0 2484
Chris@0 2485 if (!pane) {
Chris@0 2486 // shouldn't happen, as the menu action should have been disabled
Chris@0 2487 std::cerr << "WARNING: MainWindow::importLayer: no current pane" << std::endl;
Chris@0 2488 return;
Chris@0 2489 }
Chris@0 2490
Chris@0 2491 if (!getMainModel()) {
Chris@0 2492 // shouldn't happen, as the menu action should have been disabled
Chris@0 2493 std::cerr << "WARNING: MainWindow::importLayer: No main model -- hence no default sample rate available" << std::endl;
Chris@0 2494 return;
Chris@0 2495 }
Chris@0 2496
Chris@88 2497 QString path = getOpenFileName(FileFinder::LayerFile);
Chris@0 2498
Chris@0 2499 if (path != "") {
Chris@0 2500
Chris@82 2501 if (openLayerFile(path) == FileOpenFailed) {
Chris@0 2502 QMessageBox::critical(this, tr("Failed to open file"),
Chris@0 2503 tr("File %1 could not be opened.").arg(path));
Chris@0 2504 return;
Chris@0 2505 }
Chris@0 2506 }
Chris@0 2507 }
Chris@0 2508
Chris@82 2509 MainWindow::FileOpenStatus
Chris@0 2510 MainWindow::openLayerFile(QString path)
Chris@0 2511 {
Chris@86 2512 return openLayerFile(path, path);
Chris@86 2513 }
Chris@86 2514
Chris@86 2515 MainWindow::FileOpenStatus
Chris@86 2516 MainWindow::openLayerFile(QString path, QString location)
Chris@86 2517 {
Chris@0 2518 Pane *pane = m_paneStack->getCurrentPane();
Chris@0 2519
Chris@0 2520 if (!pane) {
Chris@0 2521 // shouldn't happen, as the menu action should have been disabled
Chris@0 2522 std::cerr << "WARNING: MainWindow::openLayerFile: no current pane" << std::endl;
Chris@82 2523 return FileOpenFailed;
Chris@0 2524 }
Chris@0 2525
Chris@0 2526 if (!getMainModel()) {
Chris@0 2527 // shouldn't happen, as the menu action should have been disabled
Chris@0 2528 std::cerr << "WARNING: MainWindow::openLayerFile: No main model -- hence no default sample rate available" << std::endl;
Chris@82 2529 return FileOpenFailed;
Chris@0 2530 }
Chris@0 2531
Chris@86 2532 bool realFile = (location == path);
Chris@86 2533
Chris@0 2534 if (path.endsWith(".svl") || path.endsWith(".xml")) {
Chris@0 2535
Chris@0 2536 PaneCallback callback(this);
Chris@0 2537 QFile file(path);
Chris@0 2538
Chris@0 2539 if (!file.open(QIODevice::ReadOnly | QIODevice::Text)) {
Chris@0 2540 std::cerr << "ERROR: MainWindow::openLayerFile("
Chris@86 2541 << location.toStdString()
Chris@0 2542 << "): Failed to open file for reading" << std::endl;
Chris@82 2543 return FileOpenFailed;
Chris@0 2544 }
Chris@0 2545
Chris@87 2546 SVFileReader reader(m_document, callback, location);
Chris@0 2547 reader.setCurrentPane(pane);
Chris@0 2548
Chris@0 2549 QXmlInputSource inputSource(&file);
Chris@0 2550 reader.parse(inputSource);
Chris@0 2551
Chris@0 2552 if (!reader.isOK()) {
Chris@0 2553 std::cerr << "ERROR: MainWindow::openLayerFile("
Chris@86 2554 << location.toStdString()
Chris@0 2555 << "): Failed to read XML file: "
Chris@0 2556 << reader.getErrorString().toStdString() << std::endl;
Chris@82 2557 return FileOpenFailed;
Chris@0 2558 }
Chris@0 2559
Chris@86 2560 m_recentFiles.addFile(location);
Chris@86 2561
Chris@86 2562 if (realFile) {
Chris@88 2563 registerLastOpenedFilePath(FileFinder::LayerFile, path); // for file dialog
Chris@86 2564 }
Chris@86 2565
Chris@82 2566 return FileOpenSucceeded;
Chris@0 2567
Chris@0 2568 } else {
Chris@0 2569
Chris@0 2570 Model *model = DataFileReaderFactory::load(path, getMainModel()->getSampleRate());
Chris@0 2571
Chris@0 2572 if (model) {
Chris@86 2573
Chris@0 2574 Layer *newLayer = m_document->createImportedLayer(model);
Chris@86 2575
Chris@0 2576 if (newLayer) {
Chris@86 2577
Chris@0 2578 m_document->addLayerToView(pane, newLayer);
Chris@86 2579 m_recentFiles.addFile(location);
Chris@86 2580
Chris@86 2581 if (realFile) {
Chris@88 2582 registerLastOpenedFilePath(FileFinder::LayerFile, path); // for file dialog
Chris@86 2583 }
Chris@86 2584
Chris@82 2585 return FileOpenSucceeded;
Chris@0 2586 }
Chris@0 2587 }
Chris@0 2588 }
Chris@0 2589
Chris@82 2590 return FileOpenFailed;
Chris@0 2591 }
Chris@0 2592
Chris@0 2593 void
Chris@0 2594 MainWindow::exportLayer()
Chris@0 2595 {
Chris@0 2596 Pane *pane = m_paneStack->getCurrentPane();
Chris@0 2597 if (!pane) return;
Chris@0 2598
Chris@0 2599 Layer *layer = pane->getSelectedLayer();
Chris@0 2600 if (!layer) return;
Chris@0 2601
Chris@0 2602 Model *model = layer->getModel();
Chris@0 2603 if (!model) return;
Chris@0 2604
Chris@88 2605 QString path = getSaveFileName(FileFinder::LayerFile);
Chris@0 2606
Chris@0 2607 if (path == "") return;
Chris@0 2608
Chris@0 2609 if (QFileInfo(path).suffix() == "") path += ".svl";
Chris@0 2610
Chris@0 2611 QString error;
Chris@0 2612
Chris@0 2613 if (path.endsWith(".xml") || path.endsWith(".svl")) {
Chris@0 2614
Chris@0 2615 QFile file(path);
Chris@0 2616 if (!file.open(QIODevice::WriteOnly | QIODevice::Text)) {
Chris@0 2617 error = tr("Failed to open file %1 for writing").arg(path);
Chris@0 2618 } else {
Chris@0 2619 QTextStream out(&file);
Chris@0 2620 out << "<?xml version=\"1.0\" encoding=\"UTF-8\"?>\n"
Chris@0 2621 << "<!DOCTYPE sonic-visualiser>\n"
Chris@0 2622 << "<sv>\n"
Chris@0 2623 << " <data>\n";
Chris@0 2624
Chris@0 2625 model->toXml(out, " ");
Chris@0 2626
Chris@0 2627 out << " </data>\n"
Chris@0 2628 << " <display>\n";
Chris@0 2629
Chris@0 2630 layer->toXml(out, " ");
Chris@0 2631
Chris@0 2632 out << " </display>\n"
Chris@0 2633 << "</sv>\n";
Chris@0 2634 }
Chris@0 2635
Chris@0 2636 } else {
Chris@0 2637
Chris@0 2638 CSVFileWriter writer(path, model,
Chris@0 2639 (path.endsWith(".csv") ? "," : "\t"));
Chris@0 2640 writer.write();
Chris@0 2641
Chris@0 2642 if (!writer.isOK()) {
Chris@0 2643 error = writer.getError();
Chris@0 2644 }
Chris@0 2645 }
Chris@0 2646
Chris@0 2647 if (error != "") {
Chris@0 2648 QMessageBox::critical(this, tr("Failed to write file"), error);
Chris@0 2649 } else {
Chris@34 2650 m_recentFiles.addFile(path);
Chris@0 2651 }
Chris@0 2652 }
Chris@0 2653
Chris@121 2654 void
Chris@121 2655 MainWindow::exportImage()
Chris@121 2656 {
Chris@121 2657 Pane *pane = m_paneStack->getCurrentPane();
Chris@121 2658 if (!pane) return;
Chris@121 2659
Chris@121 2660 QString path = getSaveFileName(FileFinder::ImageFile);
Chris@121 2661
Chris@121 2662 if (path == "") return;
Chris@121 2663
Chris@121 2664 if (QFileInfo(path).suffix() == "") path += ".png";
Chris@121 2665
Chris@123 2666 bool haveSelection = m_viewManager && !m_viewManager->getSelections().empty();
Chris@123 2667
Chris@123 2668 QSize total, visible, selected;
Chris@123 2669 total = pane->getImageSize();
Chris@123 2670 visible = pane->getImageSize(pane->getFirstVisibleFrame(),
Chris@123 2671 pane->getLastVisibleFrame());
Chris@123 2672
Chris@123 2673 size_t sf0 = 0, sf1 = 0;
Chris@123 2674
Chris@123 2675 if (haveSelection) {
Chris@123 2676 MultiSelection::SelectionList selections = m_viewManager->getSelections();
Chris@123 2677 sf0 = selections.begin()->getStartFrame();
Chris@123 2678 MultiSelection::SelectionList::iterator e = selections.end();
Chris@123 2679 --e;
Chris@123 2680 sf1 = e->getEndFrame();
Chris@123 2681 selected = pane->getImageSize(sf0, sf1);
Chris@123 2682 }
Chris@123 2683
Chris@123 2684 QStringList items;
Chris@125 2685 items << tr("Export the whole pane (%1x%2 pixels)")
Chris@123 2686 .arg(total.width()).arg(total.height());
Chris@123 2687 items << tr("Export the visible area only (%1x%2 pixels)")
Chris@123 2688 .arg(visible.width()).arg(visible.height());
Chris@123 2689 if (haveSelection) {
Chris@123 2690 items << tr("Export the selection extent (%1x%2 pixels)")
Chris@123 2691 .arg(selected.width()).arg(selected.height());
Chris@124 2692 } else {
Chris@124 2693 items << tr("Export the selection extent");
Chris@123 2694 }
Chris@123 2695
Chris@123 2696 QSettings settings;
Chris@123 2697 settings.beginGroup("MainWindow");
Chris@123 2698 int deflt = settings.value("lastimageexportregion", 0).toInt();
Chris@123 2699 if (deflt == 2 && !haveSelection) deflt = 1;
Chris@124 2700 if (deflt == 0 && total.width() > 32767) deflt = 1;
Chris@124 2701
Chris@124 2702 ListInputDialog *lid = new ListInputDialog
Chris@123 2703 (this, tr("Select region to export"),
Chris@123 2704 tr("Which region of the current pane do you want to export as an image?"),
Chris@124 2705 items, deflt);
Chris@124 2706
Chris@124 2707 if (!haveSelection) {
Chris@124 2708 lid->setItemAvailability(2, false);
Chris@124 2709 }
Chris@124 2710 if (total.width() > 32767) { // appears to be the limit of a QImage
Chris@124 2711 lid->setItemAvailability(0, false);
Chris@124 2712 lid->setFootnote(tr("Note: the whole pane is too wide to be exported as a single image."));
Chris@124 2713 }
Chris@124 2714
Chris@124 2715 bool ok = lid->exec();
Chris@124 2716 QString item = lid->getCurrentString();
Chris@124 2717 delete lid;
Chris@123 2718
Chris@123 2719 if (!ok || item.isEmpty()) return;
Chris@123 2720
Chris@123 2721 settings.setValue("lastimageexportregion", deflt);
Chris@123 2722
Chris@123 2723 QImage *image = 0;
Chris@123 2724
Chris@123 2725 if (item == items[0]) {
Chris@123 2726 image = pane->toNewImage();
Chris@123 2727 } else if (item == items[1]) {
Chris@123 2728 image = pane->toNewImage(pane->getFirstVisibleFrame(),
Chris@123 2729 pane->getLastVisibleFrame());
Chris@123 2730 } else if (haveSelection) {
Chris@123 2731 image = pane->toNewImage(sf0, sf1);
Chris@123 2732 }
Chris@123 2733
Chris@121 2734 if (!image) return;
Chris@121 2735
Chris@121 2736 if (!image->save(path, "PNG")) {
Chris@121 2737 QMessageBox::critical(this, tr("Failed to save image file"),
Chris@121 2738 tr("Failed to save image file %1").arg(path));
Chris@121 2739 }
Chris@121 2740
Chris@121 2741 delete image;
Chris@121 2742 }
Chris@121 2743
Chris@82 2744 MainWindow::FileOpenStatus
Chris@0 2745 MainWindow::openAudioFile(QString path, AudioFileOpenMode mode)
Chris@0 2746 {
Chris@86 2747 return openAudioFile(path, path, mode);
Chris@86 2748 }
Chris@86 2749
Chris@86 2750 MainWindow::FileOpenStatus
Chris@86 2751 MainWindow::openAudioFile(QString path, QString location, AudioFileOpenMode mode)
Chris@86 2752 {
Chris@0 2753 if (!(QFileInfo(path).exists() &&
Chris@0 2754 QFileInfo(path).isFile() &&
Chris@0 2755 QFileInfo(path).isReadable())) {
Chris@82 2756 return FileOpenFailed;
Chris@0 2757 }
Chris@0 2758
Chris@70 2759 m_openingAudioFile = true;
Chris@70 2760
Chris@180 2761 size_t rate = 0;
Chris@180 2762
Chris@180 2763 if (Preferences::getInstance()->getResampleOnLoad()) {
Chris@180 2764 rate = m_playSource->getSourceSampleRate();
Chris@180 2765 }
Chris@180 2766
Chris@180 2767 WaveFileModel *newModel = new WaveFileModel(path, location, rate);
Chris@0 2768
Chris@0 2769 if (!newModel->isOK()) {
Chris@0 2770 delete newModel;
Chris@70 2771 m_openingAudioFile = false;
Chris@82 2772 return FileOpenFailed;
Chris@0 2773 }
Chris@0 2774
Chris@0 2775 bool setAsMain = true;
Chris@0 2776 static bool prevSetAsMain = true;
Chris@0 2777
Chris@86 2778 bool realFile = (location == path);
Chris@86 2779
Chris@0 2780 if (mode == CreateAdditionalModel) setAsMain = false;
Chris@0 2781 else if (mode == AskUser) {
Chris@0 2782 if (m_document->getMainModel()) {
Chris@0 2783
Chris@0 2784 QStringList items;
Chris@0 2785 items << tr("Replace the existing main waveform")
Chris@0 2786 << tr("Load this file into a new waveform pane");
Chris@0 2787
Chris@0 2788 bool ok = false;
Chris@0 2789 QString item = ListInputDialog::getItem
Chris@0 2790 (this, tr("Select target for import"),
Chris@0 2791 tr("You already have an audio waveform loaded.\nWhat would you like to do with the new audio file?"),
Chris@0 2792 items, prevSetAsMain ? 0 : 1, &ok);
Chris@0 2793
Chris@0 2794 if (!ok || item.isEmpty()) {
Chris@0 2795 delete newModel;
Chris@70 2796 m_openingAudioFile = false;
Chris@82 2797 return FileOpenCancelled;
Chris@0 2798 }
Chris@0 2799
Chris@0 2800 setAsMain = (item == items[0]);
Chris@0 2801 prevSetAsMain = setAsMain;
Chris@0 2802 }
Chris@0 2803 }
Chris@0 2804
Chris@0 2805 if (setAsMain) {
Chris@0 2806
Chris@0 2807 Model *prevMain = getMainModel();
Chris@118 2808 if (prevMain) {
Chris@118 2809 m_playSource->removeModel(prevMain);
Chris@118 2810 PlayParameterRepository::getInstance()->removeModel(prevMain);
Chris@118 2811 }
Chris@118 2812
Chris@118 2813 PlayParameterRepository::getInstance()->addModel(newModel);
Chris@0 2814
Chris@0 2815 m_document->setMainModel(newModel);
Chris@0 2816 setupMenus();
Chris@0 2817
Chris@0 2818 if (m_sessionFile == "") {
Chris@0 2819 setWindowTitle(tr("Sonic Visualiser: %1")
Chris@86 2820 .arg(QFileInfo(location).fileName()));
Chris@0 2821 CommandHistory::getInstance()->clear();
Chris@0 2822 CommandHistory::getInstance()->documentSaved();
Chris@0 2823 m_documentModified = false;
Chris@0 2824 } else {
Chris@0 2825 setWindowTitle(tr("Sonic Visualiser: %1 [%2]")
Chris@0 2826 .arg(QFileInfo(m_sessionFile).fileName())
Chris@86 2827 .arg(QFileInfo(location).fileName()));
Chris@0 2828 if (m_documentModified) {
Chris@0 2829 m_documentModified = false;
Chris@0 2830 documentModified(); // so as to restore "(modified)" window title
Chris@0 2831 }
Chris@0 2832 }
Chris@0 2833
Chris@86 2834 if (realFile) m_audioFile = path;
Chris@0 2835
Chris@0 2836 } else { // !setAsMain
Chris@0 2837
Chris@0 2838 CommandHistory::getInstance()->startCompoundOperation
Chris@86 2839 (tr("Import \"%1\"").arg(QFileInfo(location).fileName()), true);
Chris@0 2840
Chris@0 2841 m_document->addImportedModel(newModel);
Chris@0 2842
Chris@0 2843 AddPaneCommand *command = new AddPaneCommand(this);
Chris@0 2844 CommandHistory::getInstance()->addCommand(command);
Chris@0 2845
Chris@0 2846 Pane *pane = command->getPane();
Chris@0 2847
Chris@0 2848 if (!m_timeRulerLayer) {
Chris@0 2849 m_timeRulerLayer = m_document->createMainModelLayer
Chris@0 2850 (LayerFactory::TimeRuler);
Chris@0 2851 }
Chris@0 2852
Chris@0 2853 m_document->addLayerToView(pane, m_timeRulerLayer);
Chris@0 2854
Chris@0 2855 Layer *newLayer = m_document->createImportedLayer(newModel);
Chris@0 2856
Chris@0 2857 if (newLayer) {
Chris@0 2858 m_document->addLayerToView(pane, newLayer);
Chris@0 2859 }
Chris@0 2860
Chris@0 2861 CommandHistory::getInstance()->endCompoundOperation();
Chris@0 2862 }
Chris@0 2863
Chris@0 2864 updateMenuStates();
Chris@86 2865 m_recentFiles.addFile(location);
Chris@86 2866 if (realFile) {
Chris@88 2867 registerLastOpenedFilePath(FileFinder::AudioFile, path); // for file dialog
Chris@86 2868 }
Chris@70 2869 m_openingAudioFile = false;
Chris@0 2870
Chris@180 2871 currentPaneChanged(m_paneStack->getCurrentPane());
Chris@180 2872
Chris@82 2873 return FileOpenSucceeded;
Chris@0 2874 }
Chris@0 2875
Chris@180 2876 MainWindow::FileOpenStatus
Chris@180 2877 MainWindow::openPlaylistFile(QString path, AudioFileOpenMode mode)
Chris@180 2878 {
Chris@180 2879 return openPlaylistFile(path, path, mode);
Chris@180 2880 }
Chris@180 2881
Chris@180 2882 MainWindow::FileOpenStatus
Chris@180 2883 MainWindow::openPlaylistFile(QString path, QString location, AudioFileOpenMode mode)
Chris@180 2884 {
Chris@180 2885 if (!(QFileInfo(path).exists() &&
Chris@180 2886 QFileInfo(path).isFile() &&
Chris@180 2887 QFileInfo(path).isReadable())) {
Chris@180 2888 return FileOpenFailed;
Chris@180 2889 }
Chris@180 2890
Chris@180 2891 std::set<QString> extensions;
Chris@180 2892 PlaylistFileReader::getSupportedExtensions(extensions);
Chris@180 2893 QString extension = QFileInfo(path).suffix();
Chris@180 2894 if (extensions.find(extension) == extensions.end()) return FileOpenFailed;
Chris@180 2895
Chris@180 2896 PlaylistFileReader reader(path);
Chris@180 2897 if (!reader.isOK()) return FileOpenFailed;
Chris@180 2898
Chris@180 2899 PlaylistFileReader::Playlist playlist = reader.load();
Chris@180 2900
Chris@180 2901 bool someSuccess = false;
Chris@180 2902
Chris@180 2903 for (PlaylistFileReader::Playlist::const_iterator i = playlist.begin();
Chris@180 2904 i != playlist.end(); ++i) {
Chris@180 2905
Chris@180 2906 FileOpenStatus status = openURL(*i, mode);
Chris@180 2907
Chris@180 2908 if (status == FileOpenCancelled) {
Chris@180 2909 return FileOpenCancelled;
Chris@180 2910 }
Chris@180 2911
Chris@180 2912 if (status == FileOpenSucceeded) {
Chris@180 2913 someSuccess = true;
Chris@180 2914 mode = CreateAdditionalModel;
Chris@180 2915 }
Chris@180 2916 }
Chris@180 2917
Chris@180 2918 if (someSuccess) return FileOpenSucceeded;
Chris@180 2919 else return FileOpenFailed;
Chris@180 2920 }
Chris@180 2921
Chris@0 2922 void
Chris@0 2923 MainWindow::createPlayTarget()
Chris@0 2924 {
Chris@0 2925 if (m_playTarget) return;
Chris@0 2926
Chris@0 2927 m_playTarget = AudioTargetFactory::createCallbackTarget(m_playSource);
Chris@0 2928 if (!m_playTarget) {
Chris@0 2929 QMessageBox::warning
Chris@0 2930 (this, tr("Couldn't open audio device"),
Chris@0 2931 tr("Could not open an audio device for playback.\nAudio playback will not be available during this session.\n"),
Chris@165 2932 QMessageBox::Ok);
Chris@0 2933 }
Chris@0 2934 connect(m_fader, SIGNAL(valueChanged(float)),
Chris@0 2935 m_playTarget, SLOT(setOutputGain(float)));
Chris@0 2936 }
Chris@0 2937
Chris@0 2938 WaveFileModel *
Chris@0 2939 MainWindow::getMainModel()
Chris@0 2940 {
Chris@0 2941 if (!m_document) return 0;
Chris@0 2942 return m_document->getMainModel();
Chris@0 2943 }
Chris@0 2944
Chris@116 2945 const WaveFileModel *
Chris@116 2946 MainWindow::getMainModel() const
Chris@116 2947 {
Chris@116 2948 if (!m_document) return 0;
Chris@116 2949 return m_document->getMainModel();
Chris@116 2950 }
Chris@116 2951
Chris@0 2952 void
Chris@0 2953 MainWindow::newSession()
Chris@0 2954 {
Chris@0 2955 if (!checkSaveModified()) return;
Chris@0 2956
Chris@0 2957 closeSession();
Chris@0 2958 createDocument();
Chris@0 2959
Chris@0 2960 Pane *pane = m_paneStack->addPane();
Chris@0 2961
Chris@90 2962 connect(pane, SIGNAL(contextHelpChanged(const QString &)),
Chris@116 2963 this, SLOT(contextHelpChanged(const QString &)));
Chris@90 2964
Chris@0 2965 if (!m_timeRulerLayer) {
Chris@0 2966 m_timeRulerLayer = m_document->createMainModelLayer
Chris@0 2967 (LayerFactory::TimeRuler);
Chris@0 2968 }
Chris@0 2969
Chris@0 2970 m_document->addLayerToView(pane, m_timeRulerLayer);
Chris@0 2971
Chris@0 2972 Layer *waveform = m_document->createMainModelLayer(LayerFactory::Waveform);
Chris@0 2973 m_document->addLayerToView(pane, waveform);
Chris@0 2974
Chris@65 2975 m_overview->registerView(pane);
Chris@0 2976
Chris@0 2977 CommandHistory::getInstance()->clear();
Chris@0 2978 CommandHistory::getInstance()->documentSaved();
Chris@0 2979 documentRestored();
Chris@0 2980 updateMenuStates();
Chris@0 2981 }
Chris@0 2982
Chris@0 2983 void
Chris@0 2984 MainWindow::createDocument()
Chris@0 2985 {
Chris@0 2986 m_document = new Document;
Chris@0 2987
Chris@0 2988 connect(m_document, SIGNAL(layerAdded(Layer *)),
Chris@0 2989 this, SLOT(layerAdded(Layer *)));
Chris@0 2990 connect(m_document, SIGNAL(layerRemoved(Layer *)),
Chris@0 2991 this, SLOT(layerRemoved(Layer *)));
Chris@0 2992 connect(m_document, SIGNAL(layerAboutToBeDeleted(Layer *)),
Chris@0 2993 this, SLOT(layerAboutToBeDeleted(Layer *)));
Chris@0 2994 connect(m_document, SIGNAL(layerInAView(Layer *, bool)),
Chris@0 2995 this, SLOT(layerInAView(Layer *, bool)));
Chris@0 2996
Chris@0 2997 connect(m_document, SIGNAL(modelAdded(Model *)),
Chris@0 2998 this, SLOT(modelAdded(Model *)));
Chris@0 2999 connect(m_document, SIGNAL(mainModelChanged(WaveFileModel *)),
Chris@0 3000 this, SLOT(mainModelChanged(WaveFileModel *)));
Chris@0 3001 connect(m_document, SIGNAL(modelAboutToBeDeleted(Model *)),
Chris@0 3002 this, SLOT(modelAboutToBeDeleted(Model *)));
Chris@0 3003
Chris@0 3004 connect(m_document, SIGNAL(modelGenerationFailed(QString)),
Chris@0 3005 this, SLOT(modelGenerationFailed(QString)));
Chris@63 3006 connect(m_document, SIGNAL(modelRegenerationFailed(QString, QString)),
Chris@63 3007 this, SLOT(modelRegenerationFailed(QString, QString)));
Chris@0 3008 }
Chris@0 3009
Chris@0 3010 void
Chris@0 3011 MainWindow::closeSession()
Chris@0 3012 {
Chris@0 3013 if (!checkSaveModified()) return;
Chris@0 3014
Chris@0 3015 while (m_paneStack->getPaneCount() > 0) {
Chris@0 3016
Chris@0 3017 Pane *pane = m_paneStack->getPane(m_paneStack->getPaneCount() - 1);
Chris@0 3018
Chris@0 3019 while (pane->getLayerCount() > 0) {
Chris@0 3020 m_document->removeLayerFromView
Chris@0 3021 (pane, pane->getLayer(pane->getLayerCount() - 1));
Chris@0 3022 }
Chris@0 3023
Chris@65 3024 m_overview->unregisterView(pane);
Chris@0 3025 m_paneStack->deletePane(pane);
Chris@0 3026 }
Chris@0 3027
Chris@0 3028 while (m_paneStack->getHiddenPaneCount() > 0) {
Chris@0 3029
Chris@0 3030 Pane *pane = m_paneStack->getHiddenPane
Chris@0 3031 (m_paneStack->getHiddenPaneCount() - 1);
Chris@0 3032
Chris@0 3033 while (pane->getLayerCount() > 0) {
Chris@0 3034 m_document->removeLayerFromView
Chris@0 3035 (pane, pane->getLayer(pane->getLayerCount() - 1));
Chris@0 3036 }
Chris@0 3037
Chris@65 3038 m_overview->unregisterView(pane);
Chris@0 3039 m_paneStack->deletePane(pane);
Chris@0 3040 }
Chris@0 3041
Chris@0 3042 delete m_document;
Chris@0 3043 m_document = 0;
Chris@0 3044 m_viewManager->clearSelections();
Chris@0 3045 m_timeRulerLayer = 0; // document owned this
Chris@0 3046
Chris@0 3047 m_sessionFile = "";
Chris@0 3048 setWindowTitle(tr("Sonic Visualiser"));
Chris@0 3049
Chris@0 3050 CommandHistory::getInstance()->clear();
Chris@0 3051 CommandHistory::getInstance()->documentSaved();
Chris@0 3052 documentRestored();
Chris@0 3053 }
Chris@0 3054
Chris@0 3055 void
Chris@0 3056 MainWindow::openSession()
Chris@0 3057 {
Chris@0 3058 if (!checkSaveModified()) return;
Chris@0 3059
Chris@0 3060 QString orig = m_audioFile;
Chris@0 3061 if (orig == "") orig = ".";
Chris@0 3062 else orig = QFileInfo(orig).absoluteDir().canonicalPath();
Chris@0 3063
Chris@88 3064 QString path = getOpenFileName(FileFinder::SessionFile);
Chris@0 3065
Chris@0 3066 if (path.isEmpty()) return;
Chris@0 3067
Chris@82 3068 if (openSessionFile(path) == FileOpenFailed) {
Chris@0 3069 QMessageBox::critical(this, tr("Failed to open file"),
Chris@0 3070 tr("Session file \"%1\" could not be opened").arg(path));
Chris@0 3071 }
Chris@0 3072 }
Chris@0 3073
Chris@0 3074 void
Chris@0 3075 MainWindow::openSomething()
Chris@0 3076 {
Chris@0 3077 QString orig = m_audioFile;
Chris@0 3078 if (orig == "") orig = ".";
Chris@0 3079 else orig = QFileInfo(orig).absoluteDir().canonicalPath();
Chris@0 3080
Chris@0 3081 bool canImportLayer = (getMainModel() != 0 &&
Chris@0 3082 m_paneStack != 0 &&
Chris@0 3083 m_paneStack->getCurrentPane() != 0);
Chris@0 3084
Chris@88 3085 QString path = getOpenFileName(FileFinder::AnyFile);
Chris@0 3086
Chris@0 3087 if (path.isEmpty()) return;
Chris@0 3088
Chris@0 3089 if (path.endsWith(".sv")) {
Chris@0 3090
Chris@0 3091 if (!checkSaveModified()) return;
Chris@0 3092
Chris@82 3093 if (openSessionFile(path) == FileOpenFailed) {
Chris@0 3094 QMessageBox::critical(this, tr("Failed to open file"),
Chris@0 3095 tr("Session file \"%1\" could not be opened").arg(path));
Chris@0 3096 }
Chris@0 3097
Chris@0 3098 } else {
Chris@0 3099
Chris@180 3100 if (openPlaylistFile(path, AskUser) == FileOpenFailed) {
Chris@180 3101
Chris@180 3102 if (openAudioFile(path, AskUser) == FileOpenFailed) {
Chris@180 3103
Chris@180 3104 if (!canImportLayer || (openLayerFile(path) == FileOpenFailed)) {
Chris@180 3105
Chris@180 3106 QMessageBox::critical(this, tr("Failed to open file"),
Chris@180 3107 tr("File \"%1\" could not be opened").arg(path));
Chris@180 3108 }
Chris@0 3109 }
Chris@0 3110 }
Chris@0 3111 }
Chris@0 3112 }
Chris@0 3113
Chris@0 3114 void
Chris@86 3115 MainWindow::openLocation()
Chris@86 3116 {
Chris@103 3117 QSettings settings;
Chris@103 3118 settings.beginGroup("MainWindow");
Chris@103 3119 QString lastLocation = settings.value("lastremote", "").toString();
Chris@103 3120
Chris@86 3121 bool ok = false;
Chris@86 3122 QString text = QInputDialog::getText
Chris@86 3123 (this, tr("Open Location"),
Chris@86 3124 tr("Please enter the URL of the location to open:"),
Chris@103 3125 QLineEdit::Normal, lastLocation, &ok);
Chris@103 3126
Chris@103 3127 if (!ok) return;
Chris@103 3128
Chris@103 3129 settings.setValue("lastremote", text);
Chris@103 3130
Chris@103 3131 if (text.isEmpty()) return;
Chris@86 3132
Chris@86 3133 if (openURL(QUrl(text)) == FileOpenFailed) {
Chris@86 3134 QMessageBox::critical(this, tr("Failed to open location"),
Chris@86 3135 tr("URL \"%1\" could not be opened").arg(text));
Chris@86 3136 }
Chris@86 3137 }
Chris@86 3138
Chris@86 3139 void
Chris@0 3140 MainWindow::openRecentFile()
Chris@0 3141 {
Chris@0 3142 QObject *obj = sender();
Chris@0 3143 QAction *action = dynamic_cast<QAction *>(obj);
Chris@0 3144
Chris@0 3145 if (!action) {
Chris@0 3146 std::cerr << "WARNING: MainWindow::openRecentFile: sender is not an action"
Chris@0 3147 << std::endl;
Chris@0 3148 return;
Chris@0 3149 }
Chris@0 3150
Chris@0 3151 QString path = action->text();
Chris@0 3152 if (path == "") return;
Chris@0 3153
Chris@86 3154 QUrl url(path);
Chris@86 3155 if (RemoteFile::canHandleScheme(url)) {
Chris@86 3156 openURL(url);
Chris@86 3157 return;
Chris@86 3158 }
Chris@86 3159
Chris@0 3160 if (path.endsWith("sv")) {
Chris@0 3161
Chris@103 3162 if (!checkSaveModified()) return;
Chris@0 3163
Chris@82 3164 if (openSessionFile(path) == FileOpenFailed) {
Chris@0 3165 QMessageBox::critical(this, tr("Failed to open file"),
Chris@0 3166 tr("Session file \"%1\" could not be opened").arg(path));
Chris@0 3167 }
Chris@0 3168
Chris@0 3169 } else {
Chris@0 3170
Chris@180 3171 if (openPlaylistFile(path, AskUser) == FileOpenFailed) {
Chris@180 3172
Chris@180 3173 if (openAudioFile(path, AskUser) == FileOpenFailed) {
Chris@180 3174
Chris@180 3175 bool canImportLayer = (getMainModel() != 0 &&
Chris@180 3176 m_paneStack != 0 &&
Chris@180 3177 m_paneStack->getCurrentPane() != 0);
Chris@180 3178
Chris@180 3179 if (!canImportLayer || (openLayerFile(path) == FileOpenFailed)) {
Chris@180 3180
Chris@180 3181 QMessageBox::critical(this, tr("Failed to open file"),
Chris@180 3182 tr("File \"%1\" could not be opened").arg(path));
Chris@180 3183 }
Chris@0 3184 }
Chris@0 3185 }
Chris@0 3186 }
Chris@0 3187 }
Chris@0 3188
Chris@82 3189 MainWindow::FileOpenStatus
Chris@180 3190 MainWindow::openURL(QUrl url, AudioFileOpenMode mode)
Chris@85 3191 {
Chris@85 3192 if (url.scheme().toLower() == "file") {
Chris@180 3193
Chris@180 3194 return openSomeFile(url.toLocalFile(), mode);
Chris@180 3195
Chris@86 3196 } else if (!RemoteFile::canHandleScheme(url)) {
Chris@180 3197
Chris@85 3198 QMessageBox::critical(this, tr("Unsupported scheme in URL"),
Chris@85 3199 tr("The URL scheme \"%1\" is not supported")
Chris@85 3200 .arg(url.scheme()));
Chris@85 3201 return FileOpenFailed;
Chris@180 3202
Chris@85 3203 } else {
Chris@85 3204 RemoteFile rf(url);
Chris@85 3205 rf.wait();
Chris@85 3206 if (!rf.isOK()) {
Chris@85 3207 QMessageBox::critical(this, tr("File download failed"),
Chris@85 3208 tr("Failed to download URL \"%1\": %2")
Chris@85 3209 .arg(url.toString()).arg(rf.getErrorString()));
Chris@85 3210 return FileOpenFailed;
Chris@85 3211 }
Chris@88 3212 FileOpenStatus status;
Chris@180 3213 if ((status = openSomeFile(rf.getLocalFilename(), url.toString(),
Chris@180 3214 mode)) !=
Chris@88 3215 FileOpenSucceeded) {
Chris@88 3216 rf.deleteLocalFile();
Chris@88 3217 }
Chris@88 3218 return status;
Chris@85 3219 }
Chris@85 3220 }
Chris@85 3221
Chris@85 3222 MainWindow::FileOpenStatus
Chris@180 3223 MainWindow::openURL(QString ustr, AudioFileOpenMode mode)
Chris@180 3224 {
Chris@180 3225 // This function is used when we don't know whether the string is
Chris@180 3226 // an encoded or human-readable url
Chris@180 3227
Chris@180 3228 QUrl url(ustr);
Chris@180 3229
Chris@180 3230 if (url.scheme().toLower() == "file") {
Chris@180 3231
Chris@180 3232 return openSomeFile(url.toLocalFile(), mode);
Chris@180 3233
Chris@180 3234 } else if (!RemoteFile::canHandleScheme(url)) {
Chris@180 3235
Chris@180 3236 QMessageBox::critical(this, tr("Unsupported scheme in URL"),
Chris@180 3237 tr("The URL scheme \"%1\" is not supported")
Chris@180 3238 .arg(url.scheme()));
Chris@180 3239 return FileOpenFailed;
Chris@180 3240
Chris@180 3241 } else {
Chris@180 3242 RemoteFile rf(url);
Chris@180 3243 rf.wait();
Chris@180 3244 if (!rf.isOK()) {
Chris@180 3245 // rf was created on the assumption that ustr was
Chris@180 3246 // human-readable. Let's try again, this time assuming it
Chris@180 3247 // was already encoded.
Chris@180 3248 std::cerr << "MainWindow::openURL: Failed to retrieve URL \""
Chris@180 3249 << ustr.toStdString() << "\" as human-readable URL; "
Chris@180 3250 << "trying again treating it as encoded URL"
Chris@180 3251 << std::endl;
Chris@180 3252 url.setEncodedUrl(ustr.toAscii());
Chris@180 3253 return openURL(url, mode);
Chris@180 3254 }
Chris@180 3255
Chris@180 3256 FileOpenStatus status;
Chris@180 3257 if ((status = openSomeFile(rf.getLocalFilename(), ustr, mode)) !=
Chris@180 3258 FileOpenSucceeded) {
Chris@180 3259 rf.deleteLocalFile();
Chris@180 3260 }
Chris@180 3261 return status;
Chris@180 3262 }
Chris@180 3263 }
Chris@180 3264
Chris@180 3265 MainWindow::FileOpenStatus
Chris@54 3266 MainWindow::openSomeFile(QString path, AudioFileOpenMode mode)
Chris@0 3267 {
Chris@86 3268 return openSomeFile(path, path, mode);
Chris@86 3269 }
Chris@86 3270
Chris@86 3271 MainWindow::FileOpenStatus
Chris@86 3272 MainWindow::openSomeFile(QString path, QString location,
Chris@86 3273 AudioFileOpenMode mode)
Chris@86 3274 {
Chris@82 3275 FileOpenStatus status;
Chris@82 3276
Chris@85 3277 bool canImportLayer = (getMainModel() != 0 &&
Chris@85 3278 m_paneStack != 0 &&
Chris@85 3279 m_paneStack->getCurrentPane() != 0);
Chris@85 3280
Chris@180 3281 if ((status = openPlaylistFile(path, location, mode)) != FileOpenFailed) {
Chris@180 3282 return status;
Chris@180 3283 } else if ((status = openAudioFile(path, location, mode)) != FileOpenFailed) {
Chris@82 3284 return status;
Chris@86 3285 } else if ((status = openSessionFile(path, location)) != FileOpenFailed) {
Chris@82 3286 return status;
Chris@85 3287 } else if (!canImportLayer) {
Chris@85 3288 return FileOpenFailed;
Chris@86 3289 } else if ((status = openLayerFile(path, location)) != FileOpenFailed) {
Chris@85 3290 return status;
Chris@0 3291 } else {
Chris@82 3292 return FileOpenFailed;
Chris@0 3293 }
Chris@0 3294 }
Chris@0 3295
Chris@82 3296 MainWindow::FileOpenStatus
Chris@0 3297 MainWindow::openSessionFile(QString path)
Chris@0 3298 {
Chris@86 3299 return openSessionFile(path, path);
Chris@86 3300 }
Chris@86 3301
Chris@86 3302 MainWindow::FileOpenStatus
Chris@86 3303 MainWindow::openSessionFile(QString path, QString location)
Chris@86 3304 {
Chris@0 3305 BZipFileDevice bzFile(path);
Chris@0 3306 if (!bzFile.open(QIODevice::ReadOnly)) {
Chris@86 3307 std::cerr << "Failed to open session file \"" << location.toStdString()
Chris@0 3308 << "\": " << bzFile.errorString().toStdString() << std::endl;
Chris@82 3309 return FileOpenFailed;
Chris@0 3310 }
Chris@0 3311
Chris@103 3312 if (!checkSaveModified()) return FileOpenCancelled;
Chris@103 3313
Chris@0 3314 QString error;
Chris@0 3315 closeSession();
Chris@0 3316 createDocument();
Chris@0 3317
Chris@0 3318 PaneCallback callback(this);
Chris@0 3319 m_viewManager->clearSelections();
Chris@0 3320
Chris@87 3321 SVFileReader reader(m_document, callback, location);
Chris@0 3322 QXmlInputSource inputSource(&bzFile);
Chris@0 3323 reader.parse(inputSource);
Chris@0 3324
Chris@0 3325 if (!reader.isOK()) {
Chris@0 3326 error = tr("SV XML file read error:\n%1").arg(reader.getErrorString());
Chris@0 3327 }
Chris@0 3328
Chris@0 3329 bzFile.close();
Chris@0 3330
Chris@0 3331 bool ok = (error == "");
Chris@86 3332
Chris@86 3333 bool realFile = (location == path);
Chris@0 3334
Chris@0 3335 if (ok) {
Chris@86 3336
Chris@0 3337 setWindowTitle(tr("Sonic Visualiser: %1")
Chris@86 3338 .arg(QFileInfo(location).fileName()));
Chris@86 3339
Chris@86 3340 if (realFile) m_sessionFile = path;
Chris@86 3341
Chris@0 3342 setupMenus();
Chris@0 3343 CommandHistory::getInstance()->clear();
Chris@0 3344 CommandHistory::getInstance()->documentSaved();
Chris@0 3345 m_documentModified = false;
Chris@0 3346 updateMenuStates();
Chris@86 3347
Chris@86 3348 m_recentFiles.addFile(location);
Chris@86 3349
Chris@86 3350 if (realFile) {
Chris@88 3351 registerLastOpenedFilePath(FileFinder::SessionFile, path); // for file dialog
Chris@86 3352 }
Chris@86 3353
Chris@0 3354 } else {
Chris@0 3355 setWindowTitle(tr("Sonic Visualiser"));
Chris@0 3356 }
Chris@0 3357
Chris@82 3358 return ok ? FileOpenSucceeded : FileOpenFailed;
Chris@0 3359 }
Chris@0 3360
Chris@0 3361 void
Chris@0 3362 MainWindow::closeEvent(QCloseEvent *e)
Chris@0 3363 {
Chris@137 3364 // std::cerr << "MainWindow::closeEvent" << std::endl;
Chris@118 3365
Chris@136 3366 if (m_openingAudioFile) {
Chris@137 3367 // std::cerr << "Busy - ignoring close event" << std::endl;
Chris@136 3368 e->ignore();
Chris@136 3369 return;
Chris@136 3370 }
Chris@136 3371
Chris@70 3372 if (!m_abandoning && !checkSaveModified()) {
Chris@137 3373 // std::cerr << "Ignoring close event" << std::endl;
Chris@0 3374 e->ignore();
Chris@0 3375 return;
Chris@0 3376 }
Chris@0 3377
Chris@5 3378 QSettings settings;
Chris@5 3379 settings.beginGroup("MainWindow");
Chris@5 3380 settings.setValue("size", size());
Chris@5 3381 settings.setValue("position", pos());
Chris@5 3382 settings.endGroup();
Chris@5 3383
Chris@163 3384 delete m_keyReference;
Chris@163 3385 m_keyReference = 0;
Chris@163 3386
Chris@163 3387 if (m_preferencesDialog &&
Chris@163 3388 m_preferencesDialog->isVisible()) {
Chris@164 3389 closeSession(); // otherwise we'll have to wait for prefs changes
Chris@163 3390 m_preferencesDialog->applicationClosing(false);
Chris@163 3391 }
Chris@163 3392
Chris@177 3393 if (m_layerTreeView &&
Chris@177 3394 m_layerTreeView->isVisible()) {
Chris@177 3395 delete m_layerTreeView;
Chris@177 3396 }
Chris@177 3397
Chris@0 3398 e->accept();
Chris@0 3399 return;
Chris@0 3400 }
Chris@0 3401
Chris@0 3402 bool
Chris@11 3403 MainWindow::commitData(bool mayAskUser)
Chris@11 3404 {
Chris@11 3405 if (mayAskUser) {
Chris@163 3406 bool rv = checkSaveModified();
Chris@163 3407 if (rv) {
Chris@163 3408 if (m_preferencesDialog &&
Chris@163 3409 m_preferencesDialog->isVisible()) {
Chris@163 3410 m_preferencesDialog->applicationClosing(false);
Chris@163 3411 }
Chris@163 3412 }
Chris@163 3413 return rv;
Chris@11 3414 } else {
Chris@163 3415 if (m_preferencesDialog &&
Chris@163 3416 m_preferencesDialog->isVisible()) {
Chris@163 3417 m_preferencesDialog->applicationClosing(true);
Chris@163 3418 }
Chris@11 3419 if (!m_documentModified) return true;
Chris@11 3420
Chris@11 3421 // If we can't check with the user first, then we can't save
Chris@11 3422 // to the original session file (even if we have it) -- have
Chris@11 3423 // to use a temporary file
Chris@11 3424
Chris@11 3425 QString svDirBase = ".sv1";
Chris@11 3426 QString svDir = QDir::home().filePath(svDirBase);
Chris@11 3427
Chris@11 3428 if (!QFileInfo(svDir).exists()) {
Chris@11 3429 if (!QDir::home().mkdir(svDirBase)) return false;
Chris@11 3430 } else {
Chris@11 3431 if (!QFileInfo(svDir).isDir()) return false;
Chris@11 3432 }
Chris@11 3433
Chris@11 3434 // This name doesn't have to be unguessable
Chris@93 3435 #ifndef _WIN32
Chris@11 3436 QString fname = QString("tmp-%1-%2.sv")
Chris@11 3437 .arg(QDateTime::currentDateTime().toString("yyyyMMddhhmmsszzz"))
Chris@11 3438 .arg(QProcess().pid());
Chris@93 3439 #else
Chris@93 3440 QString fname = QString("tmp-%1.sv")
Chris@93 3441 .arg(QDateTime::currentDateTime().toString("yyyyMMddhhmmsszzz"));
Chris@93 3442 #endif
Chris@11 3443 QString fpath = QDir(svDir).filePath(fname);
Chris@11 3444 if (saveSessionFile(fpath)) {
Chris@34 3445 m_recentFiles.addFile(fpath);
Chris@11 3446 return true;
Chris@11 3447 } else {
Chris@11 3448 return false;
Chris@11 3449 }
Chris@11 3450 }
Chris@11 3451 }
Chris@11 3452
Chris@11 3453 bool
Chris@0 3454 MainWindow::checkSaveModified()
Chris@0 3455 {
Chris@0 3456 // Called before some destructive operation (e.g. new session,
Chris@0 3457 // exit program). Return true if we can safely proceed, false to
Chris@0 3458 // cancel.
Chris@0 3459
Chris@0 3460 if (!m_documentModified) return true;
Chris@0 3461
Chris@0 3462 int button =
Chris@0 3463 QMessageBox::warning(this,
Chris@0 3464 tr("Session modified"),
Chris@0 3465 tr("The current session has been modified.\nDo you want to save it?"),
Chris@165 3466 QMessageBox::Yes | QMessageBox::No | QMessageBox::Cancel,
Chris@165 3467 QMessageBox::Yes);
Chris@0 3468
Chris@0 3469 if (button == QMessageBox::Yes) {
Chris@0 3470 saveSession();
Chris@0 3471 if (m_documentModified) { // save failed -- don't proceed!
Chris@0 3472 return false;
Chris@0 3473 } else {
Chris@0 3474 return true; // saved, so it's safe to continue now
Chris@0 3475 }
Chris@0 3476 } else if (button == QMessageBox::No) {
Chris@0 3477 m_documentModified = false; // so we know to abandon it
Chris@0 3478 return true;
Chris@0 3479 }
Chris@0 3480
Chris@0 3481 // else cancel
Chris@0 3482 return false;
Chris@0 3483 }
Chris@0 3484
Chris@0 3485 void
Chris@0 3486 MainWindow::saveSession()
Chris@0 3487 {
Chris@0 3488 if (m_sessionFile != "") {
Chris@0 3489 if (!saveSessionFile(m_sessionFile)) {
Chris@0 3490 QMessageBox::critical(this, tr("Failed to save file"),
Chris@0 3491 tr("Session file \"%1\" could not be saved.").arg(m_sessionFile));
Chris@0 3492 } else {
Chris@0 3493 CommandHistory::getInstance()->documentSaved();
Chris@0 3494 documentRestored();
Chris@0 3495 }
Chris@0 3496 } else {
Chris@0 3497 saveSessionAs();
Chris@0 3498 }
Chris@0 3499 }
Chris@0 3500
Chris@0 3501 void
Chris@0 3502 MainWindow::saveSessionAs()
Chris@0 3503 {
Chris@0 3504 QString orig = m_audioFile;
Chris@0 3505 if (orig == "") orig = ".";
Chris@0 3506 else orig = QFileInfo(orig).absoluteDir().canonicalPath();
Chris@0 3507
Chris@88 3508 QString path = getSaveFileName(FileFinder::SessionFile);
Chris@81 3509
Chris@81 3510 if (path == "") return;
Chris@0 3511
Chris@0 3512 if (!saveSessionFile(path)) {
Chris@0 3513 QMessageBox::critical(this, tr("Failed to save file"),
Chris@79 3514 tr("Session file \"%1\" could not be saved.").arg(path));
Chris@0 3515 } else {
Chris@0 3516 setWindowTitle(tr("Sonic Visualiser: %1")
Chris@0 3517 .arg(QFileInfo(path).fileName()));
Chris@0 3518 m_sessionFile = path;
Chris@0 3519 CommandHistory::getInstance()->documentSaved();
Chris@0 3520 documentRestored();
Chris@34 3521 m_recentFiles.addFile(path);
Chris@0 3522 }
Chris@0 3523 }
Chris@0 3524
Chris@0 3525 bool
Chris@0 3526 MainWindow::saveSessionFile(QString path)
Chris@0 3527 {
Chris@0 3528 BZipFileDevice bzFile(path);
Chris@0 3529 if (!bzFile.open(QIODevice::WriteOnly)) {
Chris@0 3530 std::cerr << "Failed to open session file \"" << path.toStdString()
Chris@0 3531 << "\" for writing: "
Chris@0 3532 << bzFile.errorString().toStdString() << std::endl;
Chris@0 3533 return false;
Chris@0 3534 }
Chris@0 3535
Chris@0 3536 QApplication::setOverrideCursor(QCursor(Qt::WaitCursor));
Chris@0 3537
Chris@0 3538 QTextStream out(&bzFile);
Chris@0 3539 toXml(out);
Chris@0 3540 out.flush();
Chris@0 3541
Chris@0 3542 QApplication::restoreOverrideCursor();
Chris@0 3543
Chris@84 3544 if (!bzFile.isOK()) {
Chris@0 3545 QMessageBox::critical(this, tr("Failed to write file"),
Chris@0 3546 tr("Failed to write to file \"%1\": %2")
Chris@0 3547 .arg(path).arg(bzFile.errorString()));
Chris@0 3548 bzFile.close();
Chris@0 3549 return false;
Chris@0 3550 }
Chris@0 3551
Chris@0 3552 bzFile.close();
Chris@0 3553 return true;
Chris@0 3554 }
Chris@0 3555
Chris@0 3556 void
Chris@0 3557 MainWindow::toXml(QTextStream &out)
Chris@0 3558 {
Chris@0 3559 QString indent(" ");
Chris@0 3560
Chris@0 3561 out << "<?xml version=\"1.0\" encoding=\"UTF-8\"?>\n";
Chris@0 3562 out << "<!DOCTYPE sonic-visualiser>\n";
Chris@0 3563 out << "<sv>\n";
Chris@0 3564
Chris@0 3565 m_document->toXml(out, "", "");
Chris@0 3566
Chris@0 3567 out << "<display>\n";
Chris@0 3568
Chris@0 3569 out << QString(" <window width=\"%1\" height=\"%2\"/>\n")
Chris@0 3570 .arg(width()).arg(height());
Chris@0 3571
Chris@0 3572 for (int i = 0; i < m_paneStack->getPaneCount(); ++i) {
Chris@0 3573
Chris@0 3574 Pane *pane = m_paneStack->getPane(i);
Chris@0 3575
Chris@0 3576 if (pane) {
Chris@0 3577 pane->toXml(out, indent);
Chris@0 3578 }
Chris@0 3579 }
Chris@0 3580
Chris@0 3581 out << "</display>\n";
Chris@0 3582
Chris@0 3583 m_viewManager->getSelection().toXml(out);
Chris@0 3584
Chris@0 3585 out << "</sv>\n";
Chris@0 3586 }
Chris@0 3587
Chris@0 3588 Pane *
Chris@0 3589 MainWindow::addPaneToStack()
Chris@0 3590 {
Chris@0 3591 AddPaneCommand *command = new AddPaneCommand(this);
Chris@0 3592 CommandHistory::getInstance()->addCommand(command);
Chris@0 3593 return command->getPane();
Chris@0 3594 }
Chris@0 3595
Chris@0 3596 void
Chris@0 3597 MainWindow::zoomIn()
Chris@0 3598 {
Chris@0 3599 Pane *currentPane = m_paneStack->getCurrentPane();
Chris@0 3600 if (currentPane) currentPane->zoom(true);
Chris@0 3601 }
Chris@0 3602
Chris@0 3603 void
Chris@0 3604 MainWindow::zoomOut()
Chris@0 3605 {
Chris@0 3606 Pane *currentPane = m_paneStack->getCurrentPane();
Chris@0 3607 if (currentPane) currentPane->zoom(false);
Chris@0 3608 }
Chris@0 3609
Chris@0 3610 void
Chris@0 3611 MainWindow::zoomToFit()
Chris@0 3612 {
Chris@0 3613 Pane *currentPane = m_paneStack->getCurrentPane();
Chris@0 3614 if (!currentPane) return;
Chris@0 3615
Chris@0 3616 Model *model = getMainModel();
Chris@0 3617 if (!model) return;
Chris@0 3618
Chris@0 3619 size_t start = model->getStartFrame();
Chris@0 3620 size_t end = model->getEndFrame();
Chris@0 3621 size_t pixels = currentPane->width();
Chris@159 3622
Chris@158 3623 size_t sw = currentPane->getVerticalScaleWidth();
Chris@158 3624 if (pixels > sw * 2) pixels -= sw * 2;
Chris@158 3625 else pixels = 1;
Chris@159 3626 if (pixels > 4) pixels -= 4;
Chris@159 3627
Chris@0 3628 size_t zoomLevel = (end - start) / pixels;
Chris@0 3629
Chris@0 3630 currentPane->setZoomLevel(zoomLevel);
Chris@159 3631 currentPane->setCentreFrame((start + end) / 2);
Chris@0 3632 }
Chris@0 3633
Chris@0 3634 void
Chris@0 3635 MainWindow::zoomDefault()
Chris@0 3636 {
Chris@0 3637 Pane *currentPane = m_paneStack->getCurrentPane();
Chris@0 3638 if (currentPane) currentPane->setZoomLevel(1024);
Chris@0 3639 }
Chris@0 3640
Chris@0 3641 void
Chris@0 3642 MainWindow::scrollLeft()
Chris@0 3643 {
Chris@0 3644 Pane *currentPane = m_paneStack->getCurrentPane();
Chris@0 3645 if (currentPane) currentPane->scroll(false, false);
Chris@0 3646 }
Chris@0 3647
Chris@0 3648 void
Chris@0 3649 MainWindow::jumpLeft()
Chris@0 3650 {
Chris@0 3651 Pane *currentPane = m_paneStack->getCurrentPane();
Chris@0 3652 if (currentPane) currentPane->scroll(false, true);
Chris@0 3653 }
Chris@0 3654
Chris@0 3655 void
Chris@0 3656 MainWindow::scrollRight()
Chris@0 3657 {
Chris@0 3658 Pane *currentPane = m_paneStack->getCurrentPane();
Chris@0 3659 if (currentPane) currentPane->scroll(true, false);
Chris@0 3660 }
Chris@0 3661
Chris@0 3662 void
Chris@0 3663 MainWindow::jumpRight()
Chris@0 3664 {
Chris@0 3665 Pane *currentPane = m_paneStack->getCurrentPane();
Chris@0 3666 if (currentPane) currentPane->scroll(true, true);
Chris@0 3667 }
Chris@0 3668
Chris@0 3669 void
Chris@0 3670 MainWindow::showNoOverlays()
Chris@0 3671 {
Chris@0 3672 m_viewManager->setOverlayMode(ViewManager::NoOverlays);
Chris@0 3673 }
Chris@0 3674
Chris@0 3675 void
Chris@90 3676 MainWindow::showMinimalOverlays()
Chris@0 3677 {
Chris@90 3678 m_viewManager->setOverlayMode(ViewManager::MinimalOverlays);
Chris@0 3679 }
Chris@0 3680
Chris@0 3681 void
Chris@90 3682 MainWindow::showStandardOverlays()
Chris@90 3683 {
Chris@90 3684 m_viewManager->setOverlayMode(ViewManager::StandardOverlays);
Chris@90 3685 }
Chris@90 3686
Chris@90 3687 void
Chris@90 3688 MainWindow::showAllOverlays()
Chris@0 3689 {
Chris@0 3690 m_viewManager->setOverlayMode(ViewManager::AllOverlays);
Chris@0 3691 }
Chris@0 3692
Chris@0 3693 void
Chris@7 3694 MainWindow::toggleZoomWheels()
Chris@7 3695 {
Chris@7 3696 if (m_viewManager->getZoomWheelsEnabled()) {
Chris@7 3697 m_viewManager->setZoomWheelsEnabled(false);
Chris@7 3698 } else {
Chris@7 3699 m_viewManager->setZoomWheelsEnabled(true);
Chris@7 3700 }
Chris@7 3701 }
Chris@7 3702
Chris@7 3703 void
Chris@72 3704 MainWindow::togglePropertyBoxes()
Chris@72 3705 {
Chris@72 3706 if (m_paneStack->getLayoutStyle() == PaneStack::NoPropertyStacks) {
Chris@72 3707 if (Preferences::getInstance()->getPropertyBoxLayout() ==
Chris@72 3708 Preferences::VerticallyStacked) {
Chris@72 3709 m_paneStack->setLayoutStyle(PaneStack::PropertyStackPerPaneLayout);
Chris@72 3710 } else {
Chris@72 3711 m_paneStack->setLayoutStyle(PaneStack::SinglePropertyStackLayout);
Chris@72 3712 }
Chris@72 3713 } else {
Chris@72 3714 m_paneStack->setLayoutStyle(PaneStack::NoPropertyStacks);
Chris@72 3715 }
Chris@72 3716 }
Chris@72 3717
Chris@72 3718 void
Chris@90 3719 MainWindow::toggleStatusBar()
Chris@90 3720 {
Chris@90 3721 QSettings settings;
Chris@90 3722 settings.beginGroup("MainWindow");
Chris@90 3723 bool sb = settings.value("showstatusbar", true).toBool();
Chris@90 3724
Chris@90 3725 if (sb) {
Chris@90 3726 statusBar()->hide();
Chris@90 3727 } else {
Chris@90 3728 statusBar()->show();
Chris@90 3729 }
Chris@90 3730
Chris@90 3731 settings.setValue("showstatusbar", !sb);
Chris@90 3732
Chris@90 3733 settings.endGroup();
Chris@90 3734 }
Chris@90 3735
Chris@90 3736 void
Chris@72 3737 MainWindow::preferenceChanged(PropertyContainer::PropertyName name)
Chris@72 3738 {
Chris@72 3739 if (name == "Property Box Layout") {
Chris@72 3740 if (m_paneStack->getLayoutStyle() != PaneStack::NoPropertyStacks) {
Chris@72 3741 if (Preferences::getInstance()->getPropertyBoxLayout() ==
Chris@72 3742 Preferences::VerticallyStacked) {
Chris@72 3743 m_paneStack->setLayoutStyle(PaneStack::PropertyStackPerPaneLayout);
Chris@72 3744 } else {
Chris@72 3745 m_paneStack->setLayoutStyle(PaneStack::SinglePropertyStackLayout);
Chris@72 3746 }
Chris@72 3747 }
Chris@180 3748 } else if (name == "Background Mode" && m_viewManager) {
Chris@180 3749 Preferences::BackgroundMode mode =
Chris@180 3750 Preferences::getInstance()->getBackgroundMode();
Chris@180 3751 if (mode == Preferences::BackgroundFromTheme) {
Chris@180 3752 m_viewManager->setGlobalDarkBackground(m_initialDarkBackground);
Chris@180 3753 } else if (mode == Preferences::DarkBackground) {
Chris@180 3754 m_viewManager->setGlobalDarkBackground(true);
Chris@180 3755 } else {
Chris@180 3756 m_viewManager->setGlobalDarkBackground(false);
Chris@180 3757 }
Chris@180 3758 if (m_viewManager->getGlobalDarkBackground()) {
Chris@180 3759 m_panLayer->setBaseColour
Chris@180 3760 (ColourDatabase::getInstance()->getColourIndex(tr("Bright Green")));
Chris@180 3761 } else {
Chris@180 3762 m_panLayer->setBaseColour
Chris@180 3763 (ColourDatabase::getInstance()->getColourIndex(tr("Green")));
Chris@180 3764 }
Chris@180 3765 }
Chris@72 3766 }
Chris@72 3767
Chris@72 3768 void
Chris@0 3769 MainWindow::play()
Chris@0 3770 {
Chris@0 3771 if (m_playSource->isPlaying()) {
Chris@116 3772 stop();
Chris@0 3773 } else {
Chris@116 3774 playbackFrameChanged(m_viewManager->getPlaybackFrame());
Chris@0 3775 m_playSource->play(m_viewManager->getPlaybackFrame());
Chris@0 3776 }
Chris@0 3777 }
Chris@0 3778
Chris@0 3779 void
Chris@0 3780 MainWindow::ffwd()
Chris@0 3781 {
Chris@0 3782 if (!getMainModel()) return;
Chris@0 3783
Chris@0 3784 int frame = m_viewManager->getPlaybackFrame();
Chris@0 3785 ++frame;
Chris@0 3786
Chris@155 3787 Layer *layer = getSnapLayer();
Chris@155 3788 size_t sr = getMainModel()->getSampleRate();
Chris@155 3789
Chris@155 3790 if (!layer) {
Chris@155 3791
Chris@155 3792 frame = RealTime::realTime2Frame
Chris@155 3793 (RealTime::frame2RealTime(frame, sr) + RealTime(2, 0), sr);
Chris@155 3794 if (frame > int(getMainModel()->getEndFrame())) {
Chris@155 3795 frame = getMainModel()->getEndFrame();
Chris@155 3796 }
Chris@155 3797
Chris@155 3798 } else {
Chris@155 3799
Chris@155 3800 size_t resolution = 0;
Chris@155 3801 if (!layer->snapToFeatureFrame(m_paneStack->getCurrentPane(),
Chris@155 3802 frame, resolution, Layer::SnapRight)) {
Chris@155 3803 frame = getMainModel()->getEndFrame();
Chris@155 3804 }
Chris@0 3805 }
Chris@155 3806
Chris@155 3807 if (frame < 0) frame = 0;
Chris@147 3808
Chris@147 3809 if (m_viewManager->getPlaySelectionMode()) {
Chris@155 3810 frame = m_viewManager->constrainFrameToSelection(size_t(frame));
Chris@147 3811 }
Chris@0 3812
Chris@0 3813 m_viewManager->setPlaybackFrame(frame);
Chris@0 3814 }
Chris@0 3815
Chris@0 3816 void
Chris@0 3817 MainWindow::ffwdEnd()
Chris@0 3818 {
Chris@0 3819 if (!getMainModel()) return;
Chris@147 3820
Chris@147 3821 size_t frame = getMainModel()->getEndFrame();
Chris@147 3822
Chris@147 3823 if (m_viewManager->getPlaySelectionMode()) {
Chris@155 3824 frame = m_viewManager->constrainFrameToSelection(frame);
Chris@147 3825 }
Chris@147 3826
Chris@147 3827 m_viewManager->setPlaybackFrame(frame);
Chris@0 3828 }
Chris@0 3829
Chris@0 3830 void
Chris@0 3831 MainWindow::rewind()
Chris@0 3832 {
Chris@0 3833 if (!getMainModel()) return;
Chris@0 3834
Chris@0 3835 int frame = m_viewManager->getPlaybackFrame();
Chris@0 3836 if (frame > 0) --frame;
Chris@0 3837
Chris@155 3838 Layer *layer = getSnapLayer();
Chris@155 3839 size_t sr = getMainModel()->getSampleRate();
Chris@156 3840
Chris@156 3841 // when rewinding during playback, we want to allow a period
Chris@156 3842 // following a rewind target point at which the rewind will go to
Chris@156 3843 // the prior point instead of the immediately neighbouring one
Chris@156 3844 if (m_playSource && m_playSource->isPlaying()) {
Chris@156 3845 RealTime ct = RealTime::frame2RealTime(frame, sr);
Chris@156 3846 ct = ct - RealTime::fromSeconds(0.25);
Chris@156 3847 if (ct < RealTime::zeroTime) ct = RealTime::zeroTime;
Chris@156 3848 // std::cerr << "rewind: frame " << frame << " -> ";
Chris@156 3849 frame = RealTime::realTime2Frame(ct, sr);
Chris@156 3850 // std::cerr << frame << std::endl;
Chris@156 3851 }
Chris@155 3852
Chris@155 3853 if (!layer) {
Chris@155 3854
Chris@155 3855 frame = RealTime::realTime2Frame
Chris@155 3856 (RealTime::frame2RealTime(frame, sr) - RealTime(2, 0), sr);
Chris@155 3857 if (frame < int(getMainModel()->getStartFrame())) {
Chris@155 3858 frame = getMainModel()->getStartFrame();
Chris@155 3859 }
Chris@155 3860
Chris@155 3861 } else {
Chris@155 3862
Chris@155 3863 size_t resolution = 0;
Chris@155 3864 if (!layer->snapToFeatureFrame(m_paneStack->getCurrentPane(),
Chris@155 3865 frame, resolution, Layer::SnapLeft)) {
Chris@155 3866 frame = getMainModel()->getStartFrame();
Chris@155 3867 }
Chris@0 3868 }
Chris@147 3869
Chris@155 3870 if (frame < 0) frame = 0;
Chris@155 3871
Chris@147 3872 if (m_viewManager->getPlaySelectionMode()) {
Chris@155 3873 frame = m_viewManager->constrainFrameToSelection(size_t(frame));
Chris@147 3874 }
Chris@147 3875
Chris@0 3876 m_viewManager->setPlaybackFrame(frame);
Chris@0 3877 }
Chris@0 3878
Chris@0 3879 void
Chris@0 3880 MainWindow::rewindStart()
Chris@0 3881 {
Chris@0 3882 if (!getMainModel()) return;
Chris@147 3883
Chris@147 3884 size_t frame = getMainModel()->getStartFrame();
Chris@147 3885
Chris@147 3886 if (m_viewManager->getPlaySelectionMode()) {
Chris@155 3887 frame = m_viewManager->constrainFrameToSelection(frame);
Chris@155 3888 }
Chris@155 3889
Chris@155 3890 m_viewManager->setPlaybackFrame(frame);
Chris@155 3891 }
Chris@155 3892
Chris@155 3893 Layer *
Chris@155 3894 MainWindow::getSnapLayer() const
Chris@155 3895 {
Chris@155 3896 Pane *pane = m_paneStack->getCurrentPane();
Chris@155 3897 if (!pane) return 0;
Chris@155 3898
Chris@155 3899 Layer *layer = pane->getSelectedLayer();
Chris@155 3900
Chris@155 3901 if (!dynamic_cast<TimeInstantLayer *>(layer) &&
Chris@155 3902 !dynamic_cast<TimeValueLayer *>(layer) &&
Chris@155 3903 !dynamic_cast<TimeRulerLayer *>(layer)) {
Chris@155 3904
Chris@155 3905 layer = 0;
Chris@155 3906
Chris@155 3907 for (int i = pane->getLayerCount(); i > 0; --i) {
Chris@155 3908 Layer *l = pane->getLayer(i-1);
Chris@155 3909 if (dynamic_cast<TimeRulerLayer *>(l)) {
Chris@155 3910 layer = l;
Chris@155 3911 break;
Chris@155 3912 }
Chris@147 3913 }
Chris@147 3914 }
Chris@147 3915
Chris@155 3916 return layer;
Chris@0 3917 }
Chris@0 3918
Chris@0 3919 void
Chris@0 3920 MainWindow::stop()
Chris@0 3921 {
Chris@0 3922 m_playSource->stop();
Chris@116 3923
Chris@116 3924 if (m_paneStack && m_paneStack->getCurrentPane()) {
Chris@116 3925 updateVisibleRangeDisplay(m_paneStack->getCurrentPane());
Chris@116 3926 } else {
Chris@116 3927 m_myStatusMessage = "";
Chris@116 3928 statusBar()->showMessage("");
Chris@116 3929 }
Chris@0 3930 }
Chris@0 3931
Chris@0 3932 void
Chris@0 3933 MainWindow::addPane()
Chris@0 3934 {
Chris@0 3935 QObject *s = sender();
Chris@0 3936 QAction *action = dynamic_cast<QAction *>(s);
Chris@0 3937
Chris@0 3938 if (!action) {
Chris@0 3939 std::cerr << "WARNING: MainWindow::addPane: sender is not an action"
Chris@0 3940 << std::endl;
Chris@0 3941 return;
Chris@0 3942 }
Chris@0 3943
Chris@0 3944 PaneActionMap::iterator i = m_paneActions.find(action);
Chris@0 3945
Chris@0 3946 if (i == m_paneActions.end()) {
Chris@0 3947 std::cerr << "WARNING: MainWindow::addPane: unknown action "
Chris@0 3948 << action->objectName().toStdString() << std::endl;
Chris@0 3949 return;
Chris@0 3950 }
Chris@0 3951
Chris@69 3952 addPane(i->second, action->text());
Chris@69 3953 }
Chris@69 3954
Chris@69 3955 void
Chris@69 3956 MainWindow::addPane(const PaneConfiguration &configuration, QString text)
Chris@69 3957 {
Chris@69 3958 CommandHistory::getInstance()->startCompoundOperation(text, true);
Chris@0 3959
Chris@0 3960 AddPaneCommand *command = new AddPaneCommand(this);
Chris@0 3961 CommandHistory::getInstance()->addCommand(command);
Chris@0 3962
Chris@0 3963 Pane *pane = command->getPane();
Chris@0 3964
Chris@69 3965 if (configuration.layer == LayerFactory::Spectrum) {
Chris@109 3966 pane->setPlaybackFollow(PlaybackScrollContinuous);
Chris@110 3967 pane->setFollowGlobalZoom(false);
Chris@112 3968 pane->setZoomLevel(512);
Chris@7 3969 }
Chris@7 3970
Chris@69 3971 if (configuration.layer != LayerFactory::TimeRuler &&
Chris@69 3972 configuration.layer != LayerFactory::Spectrum) {
Chris@8 3973
Chris@0 3974 if (!m_timeRulerLayer) {
Chris@0 3975 // std::cerr << "no time ruler layer, creating one" << std::endl;
Chris@0 3976 m_timeRulerLayer = m_document->createMainModelLayer
Chris@0 3977 (LayerFactory::TimeRuler);
Chris@0 3978 }
Chris@0 3979
Chris@0 3980 // std::cerr << "adding time ruler layer " << m_timeRulerLayer << std::endl;
Chris@0 3981
Chris@0 3982 m_document->addLayerToView(pane, m_timeRulerLayer);
Chris@0 3983 }
Chris@0 3984
Chris@69 3985 Layer *newLayer = m_document->createLayer(configuration.layer);
Chris@69 3986
Chris@69 3987 Model *suggestedModel = configuration.sourceModel;
Chris@66 3988 Model *model = 0;
Chris@66 3989
Chris@66 3990 if (suggestedModel) {
Chris@66 3991
Chris@66 3992 // check its validity
Chris@66 3993 std::vector<Model *> inputModels = m_document->getTransformInputModels();
Chris@66 3994 for (size_t j = 0; j < inputModels.size(); ++j) {
Chris@66 3995 if (inputModels[j] == suggestedModel) {
Chris@66 3996 model = suggestedModel;
Chris@66 3997 break;
Chris@66 3998 }
Chris@66 3999 }
Chris@66 4000
Chris@66 4001 if (!model) {
Chris@66 4002 std::cerr << "WARNING: Model " << (void *)suggestedModel
Chris@66 4003 << " appears in pane action map, but is not reported "
Chris@66 4004 << "by document as a valid transform source" << std::endl;
Chris@66 4005 }
Chris@66 4006 }
Chris@66 4007
Chris@66 4008 if (!model) model = m_document->getMainModel();
Chris@66 4009
Chris@66 4010 m_document->setModel(newLayer, model);
Chris@66 4011
Chris@69 4012 m_document->setChannel(newLayer, configuration.channel);
Chris@0 4013 m_document->addLayerToView(pane, newLayer);
Chris@0 4014
Chris@0 4015 m_paneStack->setCurrentPane(pane);
Chris@70 4016 m_paneStack->setCurrentLayer(pane, newLayer);
Chris@0 4017
Chris@130 4018 // std::cerr << "MainWindow::addPane: global centre frame is "
Chris@130 4019 // << m_viewManager->getGlobalCentreFrame() << std::endl;
Chris@130 4020 // pane->setCentreFrame(m_viewManager->getGlobalCentreFrame());
Chris@73 4021
Chris@0 4022 CommandHistory::getInstance()->endCompoundOperation();
Chris@0 4023
Chris@0 4024 updateMenuStates();
Chris@0 4025 }
Chris@0 4026
Chris@0 4027 MainWindow::AddPaneCommand::AddPaneCommand(MainWindow *mw) :
Chris@0 4028 m_mw(mw),
Chris@0 4029 m_pane(0),
Chris@0 4030 m_prevCurrentPane(0),
Chris@0 4031 m_added(false)
Chris@0 4032 {
Chris@0 4033 }
Chris@0 4034
Chris@0 4035 MainWindow::AddPaneCommand::~AddPaneCommand()
Chris@0 4036 {
Chris@0 4037 if (m_pane && !m_added) {
Chris@0 4038 m_mw->m_paneStack->deletePane(m_pane);
Chris@0 4039 }
Chris@0 4040 }
Chris@0 4041
Chris@0 4042 QString
Chris@0 4043 MainWindow::AddPaneCommand::getName() const
Chris@0 4044 {
Chris@0 4045 return tr("Add Pane");
Chris@0 4046 }
Chris@0 4047
Chris@0 4048 void
Chris@0 4049 MainWindow::AddPaneCommand::execute()
Chris@0 4050 {
Chris@0 4051 if (!m_pane) {
Chris@0 4052 m_prevCurrentPane = m_mw->m_paneStack->getCurrentPane();
Chris@0 4053 m_pane = m_mw->m_paneStack->addPane();
Chris@90 4054
Chris@90 4055 connect(m_pane, SIGNAL(contextHelpChanged(const QString &)),
Chris@116 4056 m_mw, SLOT(contextHelpChanged(const QString &)));
Chris@0 4057 } else {
Chris@0 4058 m_mw->m_paneStack->showPane(m_pane);
Chris@0 4059 }
Chris@0 4060
Chris@0 4061 m_mw->m_paneStack->setCurrentPane(m_pane);
Chris@65 4062 m_mw->m_overview->registerView(m_pane);
Chris@0 4063 m_added = true;
Chris@0 4064 }
Chris@0 4065
Chris@0 4066 void
Chris@0 4067 MainWindow::AddPaneCommand::unexecute()
Chris@0 4068 {
Chris@0 4069 m_mw->m_paneStack->hidePane(m_pane);
Chris@0 4070 m_mw->m_paneStack->setCurrentPane(m_prevCurrentPane);
Chris@65 4071 m_mw->m_overview->unregisterView(m_pane);
Chris@0 4072 m_added = false;
Chris@0 4073 }
Chris@0 4074
Chris@0 4075 MainWindow::RemovePaneCommand::RemovePaneCommand(MainWindow *mw, Pane *pane) :
Chris@0 4076 m_mw(mw),
Chris@0 4077 m_pane(pane),
Chris@0 4078 m_added(true)
Chris@0 4079 {
Chris@0 4080 }
Chris@0 4081
Chris@0 4082 MainWindow::RemovePaneCommand::~RemovePaneCommand()
Chris@0 4083 {
Chris@0 4084 if (m_pane && !m_added) {
Chris@0 4085 m_mw->m_paneStack->deletePane(m_pane);
Chris@0 4086 }
Chris@0 4087 }
Chris@0 4088
Chris@0 4089 QString
Chris@0 4090 MainWindow::RemovePaneCommand::getName() const
Chris@0 4091 {
Chris@0 4092 return tr("Remove Pane");
Chris@0 4093 }
Chris@0 4094
Chris@0 4095 void
Chris@0 4096 MainWindow::RemovePaneCommand::execute()
Chris@0 4097 {
Chris@0 4098 m_prevCurrentPane = m_mw->m_paneStack->getCurrentPane();
Chris@0 4099 m_mw->m_paneStack->hidePane(m_pane);
Chris@65 4100 m_mw->m_overview->unregisterView(m_pane);
Chris@0 4101 m_added = false;
Chris@0 4102 }
Chris@0 4103
Chris@0 4104 void
Chris@0 4105 MainWindow::RemovePaneCommand::unexecute()
Chris@0 4106 {
Chris@0 4107 m_mw->m_paneStack->showPane(m_pane);
Chris@0 4108 m_mw->m_paneStack->setCurrentPane(m_prevCurrentPane);
Chris@65 4109 m_mw->m_overview->registerView(m_pane);
Chris@0 4110 m_added = true;
Chris@0 4111 }
Chris@0 4112
Chris@0 4113 void
Chris@0 4114 MainWindow::addLayer()
Chris@0 4115 {
Chris@0 4116 QObject *s = sender();
Chris@0 4117 QAction *action = dynamic_cast<QAction *>(s);
Chris@0 4118
Chris@0 4119 if (!action) {
Chris@0 4120 std::cerr << "WARNING: MainWindow::addLayer: sender is not an action"
Chris@0 4121 << std::endl;
Chris@0 4122 return;
Chris@0 4123 }
Chris@0 4124
Chris@0 4125 Pane *pane = m_paneStack->getCurrentPane();
Chris@0 4126
Chris@0 4127 if (!pane) {
Chris@0 4128 std::cerr << "WARNING: MainWindow::addLayer: no current pane" << std::endl;
Chris@0 4129 return;
Chris@0 4130 }
Chris@0 4131
Chris@0 4132 ExistingLayerActionMap::iterator ei = m_existingLayerActions.find(action);
Chris@0 4133
Chris@0 4134 if (ei != m_existingLayerActions.end()) {
Chris@0 4135 Layer *newLayer = ei->second;
Chris@0 4136 m_document->addLayerToView(pane, newLayer);
Chris@0 4137 m_paneStack->setCurrentLayer(pane, newLayer);
Chris@0 4138 return;
Chris@0 4139 }
Chris@0 4140
Chris@95 4141 ei = m_sliceActions.find(action);
Chris@95 4142
Chris@95 4143 if (ei != m_sliceActions.end()) {
Chris@95 4144 Layer *newLayer = m_document->createLayer(LayerFactory::Slice);
Chris@95 4145 // document->setModel(newLayer, ei->second->getModel());
Chris@95 4146 SliceableLayer *source = dynamic_cast<SliceableLayer *>(ei->second);
Chris@95 4147 SliceLayer *dest = dynamic_cast<SliceLayer *>(newLayer);
Chris@95 4148 if (source && dest) {
Chris@95 4149 dest->setSliceableModel(source->getSliceableModel());
Chris@95 4150 connect(source, SIGNAL(sliceableModelReplaced(const Model *, const Model *)),
Chris@95 4151 dest, SLOT(sliceableModelReplaced(const Model *, const Model *)));
Chris@95 4152 connect(m_document, SIGNAL(modelAboutToBeDeleted(Model *)),
Chris@95 4153 dest, SLOT(modelAboutToBeDeleted(Model *)));
Chris@95 4154 }
Chris@95 4155 m_document->addLayerToView(pane, newLayer);
Chris@95 4156 m_paneStack->setCurrentLayer(pane, newLayer);
Chris@95 4157 return;
Chris@95 4158 }
Chris@95 4159
Chris@34 4160 TransformActionMap::iterator i = m_transformActions.find(action);
Chris@34 4161
Chris@34 4162 if (i == m_transformActions.end()) {
Chris@0 4163
Chris@0 4164 LayerActionMap::iterator i = m_layerActions.find(action);
Chris@0 4165
Chris@0 4166 if (i == m_layerActions.end()) {
Chris@0 4167 std::cerr << "WARNING: MainWindow::addLayer: unknown action "
Chris@0 4168 << action->objectName().toStdString() << std::endl;
Chris@0 4169 return;
Chris@0 4170 }
Chris@0 4171
Chris@0 4172 LayerFactory::LayerType type = i->second;
Chris@0 4173
Chris@0 4174 LayerFactory::LayerTypeSet emptyTypes =
Chris@0 4175 LayerFactory::getInstance()->getValidEmptyLayerTypes();
Chris@0 4176
Chris@0 4177 Layer *newLayer;
Chris@0 4178
Chris@0 4179 if (emptyTypes.find(type) != emptyTypes.end()) {
Chris@0 4180
Chris@0 4181 newLayer = m_document->createEmptyLayer(type);
Chris@0 4182 m_toolActions[ViewManager::DrawMode]->trigger();
Chris@0 4183
Chris@0 4184 } else {
Chris@0 4185
Chris@0 4186 newLayer = m_document->createMainModelLayer(type);
Chris@0 4187 }
Chris@0 4188
Chris@0 4189 m_document->addLayerToView(pane, newLayer);
Chris@0 4190 m_paneStack->setCurrentLayer(pane, newLayer);
Chris@0 4191
Chris@0 4192 return;
Chris@0 4193 }
Chris@0 4194
Chris@107 4195 TransformId transform = i->second;
Chris@0 4196 TransformFactory *factory = TransformFactory::getInstance();
Chris@0 4197
Chris@0 4198 QString configurationXml;
Chris@0 4199
Chris@0 4200 int channel = -1;
Chris@0 4201 // pick up the default channel from any existing layers on the same pane
Chris@0 4202 for (int j = 0; j < pane->getLayerCount(); ++j) {
Chris@0 4203 int c = LayerFactory::getInstance()->getChannel(pane->getLayer(j));
Chris@0 4204 if (c != -1) {
Chris@0 4205 channel = c;
Chris@0 4206 break;
Chris@0 4207 }
Chris@0 4208 }
Chris@0 4209
Chris@33 4210 // We always ask for configuration, even if the plugin isn't
Chris@33 4211 // supposed to be configurable, because we need to let the user
Chris@33 4212 // change the execution context (block size etc).
Chris@0 4213
Chris@27 4214 PluginTransform::ExecutionContext context(channel);
Chris@27 4215
Chris@66 4216 std::vector<Model *> candidateInputModels =
Chris@66 4217 m_document->getTransformInputModels();
Chris@53 4218
Chris@54 4219 Model *inputModel = factory->getConfigurationForTransform(transform,
Chris@54 4220 candidateInputModels,
Chris@54 4221 context,
Chris@54 4222 configurationXml,
Chris@54 4223 m_playSource);
Chris@54 4224 if (!inputModel) return;
Chris@54 4225
Chris@135 4226 // std::cerr << "MainWindow::addLayer: Input model is " << inputModel << " \"" << inputModel->objectName().toStdString() << "\"" << std::endl;
Chris@0 4227
Chris@0 4228 Layer *newLayer = m_document->createDerivedLayer(transform,
Chris@54 4229 inputModel,
Chris@27 4230 context,
Chris@0 4231 configurationXml);
Chris@0 4232
Chris@0 4233 if (newLayer) {
Chris@0 4234 m_document->addLayerToView(pane, newLayer);
Chris@27 4235 m_document->setChannel(newLayer, context.channel);
Chris@34 4236 m_recentTransforms.add(transform);
Chris@70 4237 m_paneStack->setCurrentLayer(pane, newLayer);
Chris@0 4238 }
Chris@0 4239
Chris@0 4240 updateMenuStates();
Chris@0 4241 }
Chris@0 4242
Chris@0 4243 void
Chris@0 4244 MainWindow::deleteCurrentPane()
Chris@0 4245 {
Chris@0 4246 CommandHistory::getInstance()->startCompoundOperation
Chris@0 4247 (tr("Delete Pane"), true);
Chris@0 4248
Chris@0 4249 Pane *pane = m_paneStack->getCurrentPane();
Chris@0 4250 if (pane) {
Chris@0 4251 while (pane->getLayerCount() > 0) {
Chris@0 4252 Layer *layer = pane->getLayer(0);
Chris@0 4253 if (layer) {
Chris@0 4254 m_document->removeLayerFromView(pane, layer);
Chris@0 4255 } else {
Chris@0 4256 break;
Chris@0 4257 }
Chris@0 4258 }
Chris@0 4259
Chris@0 4260 RemovePaneCommand *command = new RemovePaneCommand(this, pane);
Chris@0 4261 CommandHistory::getInstance()->addCommand(command);
Chris@0 4262 }
Chris@0 4263
Chris@0 4264 CommandHistory::getInstance()->endCompoundOperation();
Chris@0 4265
Chris@0 4266 updateMenuStates();
Chris@0 4267 }
Chris@0 4268
Chris@0 4269 void
Chris@0 4270 MainWindow::deleteCurrentLayer()
Chris@0 4271 {
Chris@0 4272 Pane *pane = m_paneStack->getCurrentPane();
Chris@0 4273 if (pane) {
Chris@0 4274 Layer *layer = pane->getSelectedLayer();
Chris@0 4275 if (layer) {
Chris@0 4276 m_document->removeLayerFromView(pane, layer);
Chris@0 4277 }
Chris@0 4278 }
Chris@0 4279 updateMenuStates();
Chris@0 4280 }
Chris@0 4281
Chris@0 4282 void
Chris@0 4283 MainWindow::renameCurrentLayer()
Chris@0 4284 {
Chris@0 4285 Pane *pane = m_paneStack->getCurrentPane();
Chris@0 4286 if (pane) {
Chris@0 4287 Layer *layer = pane->getSelectedLayer();
Chris@0 4288 if (layer) {
Chris@0 4289 bool ok = false;
Chris@0 4290 QString newName = QInputDialog::getText
Chris@0 4291 (this, tr("Rename Layer"),
Chris@0 4292 tr("New name for this layer:"),
Chris@0 4293 QLineEdit::Normal, layer->objectName(), &ok);
Chris@0 4294 if (ok) {
Chris@0 4295 layer->setObjectName(newName);
Chris@95 4296 setupExistingLayersMenus();
Chris@0 4297 }
Chris@0 4298 }
Chris@0 4299 }
Chris@0 4300 }
Chris@0 4301
Chris@0 4302 void
Chris@59 4303 MainWindow::playSpeedChanged(int position)
Chris@0 4304 {
Chris@59 4305 PlaySpeedRangeMapper mapper(0, 200);
Chris@60 4306
Chris@60 4307 float percent = m_playSpeed->mappedValue();
Chris@60 4308 float factor = mapper.getFactorForValue(percent);
Chris@60 4309
Chris@60 4310 std::cerr << "speed = " << position << " percent = " << percent << " factor = " << factor << std::endl;
Chris@60 4311
Chris@59 4312 bool something = (position != 100);
Chris@155 4313
Chris@59 4314 int pc = lrintf(percent);
Chris@21 4315
Chris@155 4316 if (!something) {
Chris@155 4317 contextHelpChanged(tr("Playback speed: Normal"));
Chris@155 4318 } else {
Chris@155 4319 contextHelpChanged(tr("Playback speed: %1%2%")
Chris@155 4320 .arg(position > 100 ? "+" : "")
Chris@155 4321 .arg(pc));
Chris@155 4322 }
Chris@155 4323
Chris@25 4324 m_playSharpen->setEnabled(something);
Chris@26 4325 m_playMono->setEnabled(something);
Chris@25 4326 bool sharpen = (something && m_playSharpen->isChecked());
Chris@26 4327 bool mono = (something && m_playMono->isChecked());
Chris@26 4328 m_playSource->setTimeStretch(factor, sharpen, mono);
Chris@155 4329
Chris@155 4330 updateMenuStates();
Chris@16 4331 }
Chris@16 4332
Chris@16 4333 void
Chris@16 4334 MainWindow::playSharpenToggled()
Chris@16 4335 {
Chris@21 4336 QSettings settings;
Chris@21 4337 settings.beginGroup("MainWindow");
Chris@21 4338 settings.setValue("playsharpen", m_playSharpen->isChecked());
Chris@21 4339 settings.endGroup();
Chris@21 4340
Chris@16 4341 playSpeedChanged(m_playSpeed->value());
Chris@0 4342 }
Chris@0 4343
Chris@0 4344 void
Chris@26 4345 MainWindow::playMonoToggled()
Chris@26 4346 {
Chris@26 4347 QSettings settings;
Chris@26 4348 settings.beginGroup("MainWindow");
Chris@26 4349 settings.setValue("playmono", m_playMono->isChecked());
Chris@26 4350 settings.endGroup();
Chris@26 4351
Chris@26 4352 playSpeedChanged(m_playSpeed->value());
Chris@26 4353 }
Chris@26 4354
Chris@26 4355 void
Chris@155 4356 MainWindow::speedUpPlayback()
Chris@155 4357 {
Chris@155 4358 int value = m_playSpeed->value();
Chris@155 4359 value = value + m_playSpeed->pageStep();
Chris@155 4360 if (value > m_playSpeed->maximum()) value = m_playSpeed->maximum();
Chris@155 4361 m_playSpeed->setValue(value);
Chris@155 4362 }
Chris@155 4363
Chris@155 4364 void
Chris@155 4365 MainWindow::slowDownPlayback()
Chris@155 4366 {
Chris@155 4367 int value = m_playSpeed->value();
Chris@155 4368 value = value - m_playSpeed->pageStep();
Chris@155 4369 if (value < m_playSpeed->minimum()) value = m_playSpeed->minimum();
Chris@155 4370 m_playSpeed->setValue(value);
Chris@155 4371 }
Chris@155 4372
Chris@155 4373 void
Chris@155 4374 MainWindow::restoreNormalPlayback()
Chris@155 4375 {
Chris@155 4376 m_playSpeed->setValue(m_playSpeed->defaultValue());
Chris@155 4377 }
Chris@155 4378
Chris@155 4379 void
Chris@116 4380 MainWindow::playbackFrameChanged(unsigned long frame)
Chris@116 4381 {
Chris@116 4382 if (!(m_playSource && m_playSource->isPlaying()) || !getMainModel()) return;
Chris@116 4383
Chris@116 4384 RealTime now = RealTime::frame2RealTime
Chris@116 4385 (frame, getMainModel()->getSampleRate());
Chris@116 4386
Chris@116 4387 if (now.sec == m_lastPlayStatusSec) return;
Chris@116 4388
Chris@116 4389 RealTime then = RealTime::frame2RealTime
Chris@116 4390 (m_playSource->getPlayEndFrame(), getMainModel()->getSampleRate());
Chris@116 4391
Chris@116 4392 QString nowStr;
Chris@116 4393 QString thenStr;
Chris@116 4394 QString remainingStr;
Chris@116 4395
Chris@116 4396 if (then.sec > 10) {
Chris@116 4397 nowStr = now.toSecText().c_str();
Chris@116 4398 thenStr = then.toSecText().c_str();
Chris@116 4399 remainingStr = (then - now).toSecText().c_str();
Chris@116 4400 m_lastPlayStatusSec = now.sec;
Chris@116 4401 } else {
Chris@116 4402 nowStr = now.toText(true).c_str();
Chris@116 4403 thenStr = then.toText(true).c_str();
Chris@116 4404 remainingStr = (then - now).toText(true).c_str();
Chris@116 4405 }
Chris@116 4406
Chris@116 4407 m_myStatusMessage = tr("Playing: %1 of %2 (%3 remaining)")
Chris@116 4408 .arg(nowStr).arg(thenStr).arg(remainingStr);
Chris@116 4409
Chris@116 4410 statusBar()->showMessage(m_myStatusMessage);
Chris@116 4411 }
Chris@116 4412
Chris@116 4413 void
Chris@137 4414 MainWindow::globalCentreFrameChanged(unsigned long )
Chris@116 4415 {
Chris@116 4416 if ((m_playSource && m_playSource->isPlaying()) || !getMainModel()) return;
Chris@116 4417 Pane *p = 0;
Chris@116 4418 if (!m_paneStack || !(p = m_paneStack->getCurrentPane())) return;
Chris@116 4419 if (!p->getFollowGlobalPan()) return;
Chris@116 4420 updateVisibleRangeDisplay(p);
Chris@116 4421 }
Chris@116 4422
Chris@116 4423 void
Chris@137 4424 MainWindow::viewCentreFrameChanged(View *v, unsigned long )
Chris@116 4425 {
Chris@116 4426 if ((m_playSource && m_playSource->isPlaying()) || !getMainModel()) return;
Chris@116 4427 Pane *p = 0;
Chris@116 4428 if (!m_paneStack || !(p = m_paneStack->getCurrentPane())) return;
Chris@116 4429 if (v == p) updateVisibleRangeDisplay(p);
Chris@116 4430 }
Chris@116 4431
Chris@116 4432 void
Chris@137 4433 MainWindow::viewZoomLevelChanged(View *v, unsigned long , bool )
Chris@116 4434 {
Chris@116 4435 if ((m_playSource && m_playSource->isPlaying()) || !getMainModel()) return;
Chris@116 4436 Pane *p = 0;
Chris@116 4437 if (!m_paneStack || !(p = m_paneStack->getCurrentPane())) return;
Chris@116 4438 if (v == p) updateVisibleRangeDisplay(p);
Chris@116 4439 }
Chris@116 4440
Chris@116 4441 void
Chris@116 4442 MainWindow::updateVisibleRangeDisplay(Pane *p) const
Chris@116 4443 {
Chris@116 4444 if (!getMainModel() || !p) {
Chris@116 4445 return;
Chris@116 4446 }
Chris@116 4447
Chris@117 4448 bool haveSelection = false;
Chris@117 4449 size_t startFrame = 0, endFrame = 0;
Chris@117 4450
Chris@117 4451 if (m_viewManager && m_viewManager->haveInProgressSelection()) {
Chris@117 4452
Chris@117 4453 bool exclusive = false;
Chris@117 4454 Selection s = m_viewManager->getInProgressSelection(exclusive);
Chris@117 4455
Chris@117 4456 if (!s.isEmpty()) {
Chris@117 4457 haveSelection = true;
Chris@117 4458 startFrame = s.getStartFrame();
Chris@117 4459 endFrame = s.getEndFrame();
Chris@117 4460 }
Chris@117 4461 }
Chris@117 4462
Chris@117 4463 if (!haveSelection) {
Chris@117 4464 startFrame = p->getFirstVisibleFrame();
Chris@117 4465 endFrame = p->getLastVisibleFrame();
Chris@117 4466 }
Chris@117 4467
Chris@116 4468 RealTime start = RealTime::frame2RealTime
Chris@117 4469 (startFrame, getMainModel()->getSampleRate());
Chris@116 4470
Chris@116 4471 RealTime end = RealTime::frame2RealTime
Chris@117 4472 (endFrame, getMainModel()->getSampleRate());
Chris@116 4473
Chris@116 4474 RealTime duration = end - start;
Chris@116 4475
Chris@116 4476 QString startStr, endStr, durationStr;
Chris@116 4477 startStr = start.toText(true).c_str();
Chris@116 4478 endStr = end.toText(true).c_str();
Chris@116 4479 durationStr = duration.toText(true).c_str();
Chris@116 4480
Chris@117 4481 if (haveSelection) {
Chris@117 4482 m_myStatusMessage = tr("Selection: %1 to %2 (duration %3)")
Chris@117 4483 .arg(startStr).arg(endStr).arg(durationStr);
Chris@117 4484 } else {
Chris@117 4485 m_myStatusMessage = tr("Visible: %1 to %2 (duration %3)")
Chris@117 4486 .arg(startStr).arg(endStr).arg(durationStr);
Chris@117 4487 }
Chris@116 4488
Chris@116 4489 statusBar()->showMessage(m_myStatusMessage);
Chris@116 4490 }
Chris@116 4491
Chris@116 4492 void
Chris@0 4493 MainWindow::outputLevelsChanged(float left, float right)
Chris@0 4494 {
Chris@0 4495 m_fader->setPeakLeft(left);
Chris@0 4496 m_fader->setPeakRight(right);
Chris@0 4497 }
Chris@0 4498
Chris@0 4499 void
Chris@0 4500 MainWindow::sampleRateMismatch(size_t requested, size_t actual,
Chris@0 4501 bool willResample)
Chris@0 4502 {
Chris@0 4503 if (!willResample) {
Chris@0 4504 //!!! more helpful message needed
Chris@0 4505 QMessageBox::information
Chris@0 4506 (this, tr("Sample rate mismatch"),
Chris@118 4507 tr("The sample rate of this audio file (%1 Hz) does not match\nthe current playback rate (%2 Hz).\n\nThe file will play at the wrong speed and pitch.")
Chris@0 4508 .arg(requested).arg(actual));
Chris@0 4509 }
Chris@0 4510
Chris@0 4511 updateDescriptionLabel();
Chris@0 4512 }
Chris@0 4513
Chris@0 4514 void
Chris@42 4515 MainWindow::audioOverloadPluginDisabled()
Chris@42 4516 {
Chris@42 4517 QMessageBox::information
Chris@42 4518 (this, tr("Audio processing overload"),
Chris@42 4519 tr("Audio effects plugin auditioning has been disabled\ndue to a processing overload."));
Chris@42 4520 }
Chris@42 4521
Chris@42 4522 void
Chris@137 4523 MainWindow::layerAdded(Layer *)
Chris@0 4524 {
Chris@0 4525 // std::cerr << "MainWindow::layerAdded(" << layer << ")" << std::endl;
Chris@0 4526 // setupExistingLayersMenu();
Chris@0 4527 updateMenuStates();
Chris@0 4528 }
Chris@0 4529
Chris@0 4530 void
Chris@137 4531 MainWindow::layerRemoved(Layer *)
Chris@0 4532 {
Chris@0 4533 // std::cerr << "MainWindow::layerRemoved(" << layer << ")" << std::endl;
Chris@95 4534 setupExistingLayersMenus();
Chris@0 4535 updateMenuStates();
Chris@0 4536 }
Chris@0 4537
Chris@0 4538 void
Chris@0 4539 MainWindow::layerAboutToBeDeleted(Layer *layer)
Chris@0 4540 {
Chris@0 4541 // std::cerr << "MainWindow::layerAboutToBeDeleted(" << layer << ")" << std::endl;
Chris@0 4542 if (layer == m_timeRulerLayer) {
Chris@0 4543 // std::cerr << "(this is the time ruler layer)" << std::endl;
Chris@0 4544 m_timeRulerLayer = 0;
Chris@0 4545 }
Chris@0 4546 }
Chris@0 4547
Chris@0 4548 void
Chris@0 4549 MainWindow::layerInAView(Layer *layer, bool inAView)
Chris@0 4550 {
Chris@0 4551 // std::cerr << "MainWindow::layerInAView(" << layer << "," << inAView << ")" << std::endl;
Chris@0 4552
Chris@0 4553 // Check whether we need to add or remove model from play source
Chris@0 4554 Model *model = layer->getModel();
Chris@0 4555 if (model) {
Chris@0 4556 if (inAView) {
Chris@0 4557 m_playSource->addModel(model);
Chris@0 4558 } else {
Chris@0 4559 bool found = false;
Chris@0 4560 for (int i = 0; i < m_paneStack->getPaneCount(); ++i) {
Chris@0 4561 Pane *pane = m_paneStack->getPane(i);
Chris@0 4562 if (!pane) continue;
Chris@0 4563 for (int j = 0; j < pane->getLayerCount(); ++j) {
Chris@0 4564 Layer *pl = pane->getLayer(j);
Chris@0 4565 if (pl && pl->getModel() == model) {
Chris@0 4566 found = true;
Chris@0 4567 break;
Chris@0 4568 }
Chris@0 4569 }
Chris@0 4570 if (found) break;
Chris@0 4571 }
Chris@0 4572 if (!found) m_playSource->removeModel(model);
Chris@0 4573 }
Chris@0 4574 }
Chris@0 4575
Chris@95 4576 setupExistingLayersMenus();
Chris@0 4577 updateMenuStates();
Chris@0 4578 }
Chris@0 4579
Chris@0 4580 void
Chris@0 4581 MainWindow::modelAdded(Model *model)
Chris@0 4582 {
Chris@0 4583 // std::cerr << "MainWindow::modelAdded(" << model << ")" << std::endl;
Chris@0 4584 m_playSource->addModel(model);
Chris@66 4585 if (dynamic_cast<DenseTimeValueModel *>(model)) {
Chris@66 4586 setupPaneAndLayerMenus();
Chris@66 4587 }
Chris@0 4588 }
Chris@0 4589
Chris@0 4590 void
Chris@0 4591 MainWindow::mainModelChanged(WaveFileModel *model)
Chris@0 4592 {
Chris@0 4593 // std::cerr << "MainWindow::mainModelChanged(" << model << ")" << std::endl;
Chris@0 4594 updateDescriptionLabel();
Chris@0 4595 m_panLayer->setModel(model);
Chris@0 4596 if (model) m_viewManager->setMainModelSampleRate(model->getSampleRate());
Chris@46 4597 if (model && !m_playTarget && m_audioOutput) createPlayTarget();
Chris@0 4598 }
Chris@0 4599
Chris@0 4600 void
Chris@0 4601 MainWindow::modelAboutToBeDeleted(Model *model)
Chris@0 4602 {
Chris@0 4603 // std::cerr << "MainWindow::modelAboutToBeDeleted(" << model << ")" << std::endl;
Chris@180 4604 if (model == m_viewManager->getPlaybackModel()) {
Chris@180 4605 m_viewManager->setPlaybackModel(0);
Chris@180 4606 }
Chris@0 4607 m_playSource->removeModel(model);
Chris@91 4608 FFTDataServer::modelAboutToBeDeleted(model);
Chris@0 4609 }
Chris@0 4610
Chris@0 4611 void
Chris@0 4612 MainWindow::modelGenerationFailed(QString transformName)
Chris@0 4613 {
Chris@0 4614 QMessageBox::warning
Chris@0 4615 (this,
Chris@0 4616 tr("Failed to generate layer"),
Chris@34 4617 tr("Failed to generate a derived layer.\n\nThe layer transform \"%1\" failed.\n\nThis probably means that a plugin failed to initialise, perhaps because it\nrejected the processing block size that was requested.")
Chris@0 4618 .arg(transformName),
Chris@165 4619 QMessageBox::Ok);
Chris@0 4620 }
Chris@0 4621
Chris@0 4622 void
Chris@0 4623 MainWindow::modelRegenerationFailed(QString layerName, QString transformName)
Chris@0 4624 {
Chris@0 4625 QMessageBox::warning
Chris@0 4626 (this,
Chris@0 4627 tr("Failed to regenerate layer"),
Chris@34 4628 tr("Failed to regenerate derived layer \"%1\".\n\nThe layer transform \"%2\" failed to run.\n\nThis probably means the layer used a plugin that is not currently available.")
Chris@0 4629 .arg(layerName).arg(transformName),
Chris@165 4630 QMessageBox::Ok);
Chris@0 4631 }
Chris@0 4632
Chris@0 4633 void
Chris@0 4634 MainWindow::rightButtonMenuRequested(Pane *pane, QPoint position)
Chris@0 4635 {
Chris@0 4636 // std::cerr << "MainWindow::rightButtonMenuRequested(" << pane << ", " << position.x() << ", " << position.y() << ")" << std::endl;
Chris@0 4637 m_paneStack->setCurrentPane(pane);
Chris@0 4638 m_rightButtonMenu->popup(position);
Chris@0 4639 }
Chris@0 4640
Chris@0 4641 void
Chris@73 4642 MainWindow::propertyStacksResized()
Chris@73 4643 {
Chris@73 4644 /*
Chris@73 4645 std::cerr << "MainWindow::propertyStacksResized" << std::endl;
Chris@73 4646 Pane *pane = m_paneStack->getCurrentPane();
Chris@73 4647 if (pane && m_overview) {
Chris@73 4648 std::cerr << "Fixed width: " << pane->width() << std::endl;
Chris@73 4649 m_overview->setFixedWidth(pane->width());
Chris@73 4650 }
Chris@73 4651 */
Chris@73 4652 }
Chris@73 4653
Chris@73 4654 void
Chris@0 4655 MainWindow::showLayerTree()
Chris@0 4656 {
Chris@177 4657 if (!m_layerTreeView.isNull()) {
Chris@177 4658 m_layerTreeView->show();
Chris@177 4659 m_layerTreeView->raise();
Chris@177 4660 return;
Chris@177 4661 }
Chris@177 4662
Chris@177 4663 //!!! should use an actual dialog class
Chris@177 4664
Chris@177 4665 m_layerTreeView = new QTreeView();
Chris@0 4666 LayerTreeModel *tree = new LayerTreeModel(m_paneStack);
Chris@177 4667 m_layerTreeView->resize(500, 300); //!!!
Chris@177 4668 m_layerTreeView->setModel(tree);
Chris@177 4669 m_layerTreeView->expandAll();
Chris@177 4670 m_layerTreeView->show();
Chris@0 4671 }
Chris@0 4672
Chris@0 4673 void
Chris@69 4674 MainWindow::pollOSC()
Chris@69 4675 {
Chris@70 4676 if (!m_oscQueue || m_oscQueue->isEmpty()) return;
Chris@69 4677 std::cerr << "MainWindow::pollOSC: have " << m_oscQueue->getMessagesAvailable() << " messages" << std::endl;
Chris@69 4678
Chris@70 4679 if (m_openingAudioFile) return;
Chris@70 4680
Chris@69 4681 OSCMessage message = m_oscQueue->readMessage();
Chris@69 4682
Chris@69 4683 if (message.getTarget() != 0) {
Chris@69 4684 return; //!!! for now -- this class is target 0, others not handled yet
Chris@69 4685 }
Chris@69 4686
Chris@69 4687 handleOSCMessage(message);
Chris@69 4688 }
Chris@69 4689
Chris@69 4690 void
Chris@69 4691 MainWindow::handleOSCMessage(const OSCMessage &message)
Chris@69 4692 {
Chris@70 4693 std::cerr << "MainWindow::handleOSCMessage: thread id = "
Chris@70 4694 << QThread::currentThreadId() << std::endl;
Chris@70 4695
Chris@69 4696 // This large function should really be abstracted out.
Chris@69 4697
Chris@69 4698 if (message.getMethod() == "open") {
Chris@69 4699
Chris@69 4700 if (message.getArgCount() == 1 &&
Chris@69 4701 message.getArg(0).canConvert(QVariant::String)) {
Chris@69 4702 QString path = message.getArg(0).toString();
Chris@82 4703 if (openSomeFile(path, ReplaceMainModel) != FileOpenSucceeded) {
Chris@69 4704 std::cerr << "MainWindow::handleOSCMessage: File open failed for path \""
Chris@69 4705 << path.toStdString() << "\"" << std::endl;
Chris@69 4706 }
Chris@69 4707 //!!! we really need to spin here and not return until the
Chris@69 4708 // file has been completely decoded...
Chris@69 4709 }
Chris@69 4710
Chris@69 4711 } else if (message.getMethod() == "openadditional") {
Chris@69 4712
Chris@69 4713 if (message.getArgCount() == 1 &&
Chris@69 4714 message.getArg(0).canConvert(QVariant::String)) {
Chris@69 4715 QString path = message.getArg(0).toString();
Chris@82 4716 if (openSomeFile(path, CreateAdditionalModel) != FileOpenSucceeded) {
Chris@69 4717 std::cerr << "MainWindow::handleOSCMessage: File open failed for path \""
Chris@69 4718 << path.toStdString() << "\"" << std::endl;
Chris@69 4719 }
Chris@69 4720 }
Chris@69 4721
Chris@69 4722 } else if (message.getMethod() == "recent" ||
Chris@69 4723 message.getMethod() == "last") {
Chris@69 4724
Chris@69 4725 int n = 0;
Chris@69 4726 if (message.getMethod() == "recent" &&
Chris@69 4727 message.getArgCount() == 1 &&
Chris@69 4728 message.getArg(0).canConvert(QVariant::Int)) {
Chris@69 4729 n = message.getArg(0).toInt() - 1;
Chris@69 4730 }
Chris@69 4731 std::vector<QString> recent = m_recentFiles.getRecent();
Chris@137 4732 if (n >= 0 && n < int(recent.size())) {
Chris@82 4733 if (openSomeFile(recent[n], ReplaceMainModel) != FileOpenSucceeded) {
Chris@69 4734 std::cerr << "MainWindow::handleOSCMessage: File open failed for path \""
Chris@69 4735 << recent[n].toStdString() << "\"" << std::endl;
Chris@69 4736 }
Chris@69 4737 }
Chris@69 4738
Chris@69 4739 } else if (message.getMethod() == "save") {
Chris@69 4740
Chris@69 4741 QString path;
Chris@69 4742 if (message.getArgCount() == 1 &&
Chris@69 4743 message.getArg(0).canConvert(QVariant::String)) {
Chris@69 4744 path = message.getArg(0).toString();
Chris@69 4745 if (QFileInfo(path).exists()) {
Chris@69 4746 std::cerr << "MainWindow::handleOSCMessage: Refusing to overwrite existing file in save" << std::endl;
Chris@69 4747 } else {
Chris@69 4748 saveSessionFile(path);
Chris@69 4749 }
Chris@69 4750 }
Chris@69 4751
Chris@69 4752 } else if (message.getMethod() == "export") {
Chris@69 4753
Chris@69 4754 QString path;
Chris@69 4755 if (getMainModel()) {
Chris@69 4756 if (message.getArgCount() == 1 &&
Chris@69 4757 message.getArg(0).canConvert(QVariant::String)) {
Chris@69 4758 path = message.getArg(0).toString();
Chris@69 4759 if (QFileInfo(path).exists()) {
Chris@69 4760 std::cerr << "MainWindow::handleOSCMessage: Refusing to overwrite existing file in export" << std::endl;
Chris@69 4761 } else {
Chris@69 4762 WavFileWriter writer(path,
Chris@69 4763 getMainModel()->getSampleRate(),
Chris@69 4764 getMainModel()->getChannelCount());
Chris@69 4765 MultiSelection ms = m_viewManager->getSelection();
Chris@69 4766 if (!ms.getSelections().empty()) {
Chris@69 4767 writer.writeModel(getMainModel(), &ms);
Chris@69 4768 } else {
Chris@69 4769 writer.writeModel(getMainModel());
Chris@69 4770 }
Chris@69 4771 }
Chris@69 4772 }
Chris@69 4773 }
Chris@69 4774
Chris@69 4775 } else if (message.getMethod() == "jump" ||
Chris@69 4776 message.getMethod() == "play") {
Chris@69 4777
Chris@69 4778 if (getMainModel()) {
Chris@69 4779
Chris@69 4780 unsigned long frame = m_viewManager->getPlaybackFrame();
Chris@69 4781 bool selection = false;
Chris@69 4782 bool play = (message.getMethod() == "play");
Chris@69 4783
Chris@69 4784 if (message.getArgCount() == 1) {
Chris@69 4785
Chris@69 4786 if (message.getArg(0).canConvert(QVariant::String) &&
Chris@69 4787 message.getArg(0).toString() == "selection") {
Chris@69 4788
Chris@69 4789 selection = true;
Chris@69 4790
Chris@69 4791 } else if (message.getArg(0).canConvert(QVariant::String) &&
Chris@69 4792 message.getArg(0).toString() == "end") {
Chris@69 4793
Chris@69 4794 frame = getMainModel()->getEndFrame();
Chris@69 4795
Chris@69 4796 } else if (message.getArg(0).canConvert(QVariant::Double)) {
Chris@69 4797
Chris@69 4798 double time = message.getArg(0).toDouble();
Chris@69 4799 if (time < 0.0) time = 0.0;
Chris@69 4800
Chris@69 4801 frame = lrint(time * getMainModel()->getSampleRate());
Chris@69 4802 }
Chris@69 4803 }
Chris@69 4804
Chris@69 4805 if (frame > getMainModel()->getEndFrame()) {
Chris@69 4806 frame = getMainModel()->getEndFrame();
Chris@69 4807 }
Chris@69 4808
Chris@69 4809 if (play) {
Chris@69 4810 m_viewManager->setPlaySelectionMode(selection);
Chris@69 4811 }
Chris@69 4812
Chris@69 4813 if (selection) {
Chris@69 4814 MultiSelection::SelectionList sl = m_viewManager->getSelections();
Chris@69 4815 if (!sl.empty()) {
Chris@69 4816 frame = sl.begin()->getStartFrame();
Chris@69 4817 }
Chris@69 4818 }
Chris@69 4819
Chris@69 4820 m_viewManager->setPlaybackFrame(frame);
Chris@69 4821
Chris@69 4822 if (play && !m_playSource->isPlaying()) {
Chris@69 4823 m_playSource->play(frame);
Chris@69 4824 }
Chris@69 4825 }
Chris@69 4826
Chris@69 4827 } else if (message.getMethod() == "stop") {
Chris@69 4828
Chris@69 4829 if (m_playSource->isPlaying()) m_playSource->stop();
Chris@69 4830
Chris@69 4831 } else if (message.getMethod() == "loop") {
Chris@69 4832
Chris@69 4833 if (message.getArgCount() == 1 &&
Chris@69 4834 message.getArg(0).canConvert(QVariant::String)) {
Chris@69 4835
Chris@69 4836 QString str = message.getArg(0).toString();
Chris@69 4837 if (str == "on") {
Chris@69 4838 m_viewManager->setPlayLoopMode(true);
Chris@69 4839 } else if (str == "off") {
Chris@69 4840 m_viewManager->setPlayLoopMode(false);
Chris@69 4841 }
Chris@69 4842 }
Chris@69 4843
Chris@180 4844 } else if (message.getMethod() == "solo") {
Chris@180 4845
Chris@180 4846 if (message.getArgCount() == 1 &&
Chris@180 4847 message.getArg(0).canConvert(QVariant::String)) {
Chris@180 4848
Chris@180 4849 QString str = message.getArg(0).toString();
Chris@180 4850 if (str == "on") {
Chris@180 4851 m_viewManager->setPlaySoloMode(true);
Chris@180 4852 } else if (str == "off") {
Chris@180 4853 m_viewManager->setPlaySoloMode(false);
Chris@180 4854 }
Chris@180 4855 }
Chris@180 4856
Chris@69 4857 } else if (message.getMethod() == "select" ||
Chris@69 4858 message.getMethod() == "addselect") {
Chris@69 4859
Chris@69 4860 if (getMainModel()) {
Chris@69 4861
Chris@73 4862 int f0 = getMainModel()->getStartFrame();
Chris@73 4863 int f1 = getMainModel()->getEndFrame();
Chris@69 4864
Chris@69 4865 bool done = false;
Chris@69 4866
Chris@69 4867 if (message.getArgCount() == 2 &&
Chris@69 4868 message.getArg(0).canConvert(QVariant::Double) &&
Chris@69 4869 message.getArg(1).canConvert(QVariant::Double)) {
Chris@69 4870
Chris@69 4871 double t0 = message.getArg(0).toDouble();
Chris@69 4872 double t1 = message.getArg(1).toDouble();
Chris@69 4873 if (t1 < t0) { double temp = t0; t0 = t1; t1 = temp; }
Chris@69 4874 if (t0 < 0.0) t0 = 0.0;
Chris@69 4875 if (t1 < 0.0) t1 = 0.0;
Chris@69 4876
Chris@69 4877 f0 = lrint(t0 * getMainModel()->getSampleRate());
Chris@69 4878 f1 = lrint(t1 * getMainModel()->getSampleRate());
Chris@73 4879
Chris@73 4880 Pane *pane = m_paneStack->getCurrentPane();
Chris@73 4881 Layer *layer = 0;
Chris@73 4882 if (pane) layer = pane->getSelectedLayer();
Chris@73 4883 if (layer) {
Chris@73 4884 size_t resolution;
Chris@73 4885 layer->snapToFeatureFrame(pane, f0, resolution,
Chris@73 4886 Layer::SnapLeft);
Chris@73 4887 layer->snapToFeatureFrame(pane, f1, resolution,
Chris@73 4888 Layer::SnapRight);
Chris@73 4889 }
Chris@69 4890
Chris@69 4891 } else if (message.getArgCount() == 1 &&
Chris@69 4892 message.getArg(0).canConvert(QVariant::String)) {
Chris@69 4893
Chris@69 4894 QString str = message.getArg(0).toString();
Chris@69 4895 if (str == "none") {
Chris@69 4896 m_viewManager->clearSelections();
Chris@69 4897 done = true;
Chris@69 4898 }
Chris@69 4899 }
Chris@69 4900
Chris@69 4901 if (!done) {
Chris@69 4902 if (message.getMethod() == "select") {
Chris@69 4903 m_viewManager->setSelection(Selection(f0, f1));
Chris@69 4904 } else {
Chris@69 4905 m_viewManager->addSelection(Selection(f0, f1));
Chris@69 4906 }
Chris@69 4907 }
Chris@69 4908 }
Chris@69 4909
Chris@69 4910 } else if (message.getMethod() == "add") {
Chris@69 4911
Chris@69 4912 if (getMainModel()) {
Chris@69 4913
Chris@69 4914 if (message.getArgCount() >= 1 &&
Chris@69 4915 message.getArg(0).canConvert(QVariant::String)) {
Chris@69 4916
Chris@69 4917 int channel = -1;
Chris@69 4918 if (message.getArgCount() == 2 &&
Chris@69 4919 message.getArg(0).canConvert(QVariant::Int)) {
Chris@69 4920 channel = message.getArg(0).toInt();
Chris@69 4921 if (channel < -1 ||
Chris@137 4922 channel > int(getMainModel()->getChannelCount())) {
Chris@69 4923 std::cerr << "WARNING: MainWindow::handleOSCMessage: channel "
Chris@69 4924 << channel << " out of range" << std::endl;
Chris@69 4925 channel = -1;
Chris@69 4926 }
Chris@69 4927 }
Chris@69 4928
Chris@69 4929 QString str = message.getArg(0).toString();
Chris@69 4930
Chris@69 4931 LayerFactory::LayerType type =
Chris@69 4932 LayerFactory::getInstance()->getLayerTypeForName(str);
Chris@69 4933
Chris@69 4934 if (type == LayerFactory::UnknownLayer) {
Chris@69 4935 std::cerr << "WARNING: MainWindow::handleOSCMessage: unknown layer "
Chris@69 4936 << "type " << str.toStdString() << std::endl;
Chris@69 4937 } else {
Chris@69 4938
Chris@69 4939 PaneConfiguration configuration(type,
Chris@69 4940 getMainModel(),
Chris@69 4941 channel);
Chris@69 4942
Chris@69 4943 addPane(configuration,
Chris@69 4944 tr("Add %1 Pane")
Chris@69 4945 .arg(LayerFactory::getInstance()->
Chris@69 4946 getLayerPresentationName(type)));
Chris@69 4947 }
Chris@69 4948 }
Chris@69 4949 }
Chris@69 4950
Chris@69 4951 } else if (message.getMethod() == "undo") {
Chris@69 4952
Chris@69 4953 CommandHistory::getInstance()->undo();
Chris@69 4954
Chris@69 4955 } else if (message.getMethod() == "redo") {
Chris@69 4956
Chris@69 4957 CommandHistory::getInstance()->redo();
Chris@69 4958
Chris@69 4959 } else if (message.getMethod() == "set") {
Chris@69 4960
Chris@70 4961 if (message.getArgCount() == 2 &&
Chris@69 4962 message.getArg(0).canConvert(QVariant::String) &&
Chris@69 4963 message.getArg(1).canConvert(QVariant::Double)) {
Chris@69 4964
Chris@69 4965 QString property = message.getArg(0).toString();
Chris@69 4966 float value = (float)message.getArg(1).toDouble();
Chris@69 4967
Chris@69 4968 if (property == "gain") {
Chris@69 4969 if (value < 0.0) value = 0.0;
Chris@69 4970 m_fader->setValue(value);
Chris@69 4971 if (m_playTarget) m_playTarget->setOutputGain(value);
Chris@69 4972 } else if (property == "speedup") {
Chris@69 4973 m_playSpeed->setMappedValue(value);
Chris@69 4974 } else if (property == "overlays") {
Chris@69 4975 if (value < 0.5) {
Chris@69 4976 m_viewManager->setOverlayMode(ViewManager::NoOverlays);
Chris@69 4977 } else if (value < 1.5) {
Chris@90 4978 m_viewManager->setOverlayMode(ViewManager::MinimalOverlays);
Chris@90 4979 } else if (value < 2.5) {
Chris@90 4980 m_viewManager->setOverlayMode(ViewManager::StandardOverlays);
Chris@69 4981 } else {
Chris@69 4982 m_viewManager->setOverlayMode(ViewManager::AllOverlays);
Chris@69 4983 }
Chris@70 4984 } else if (property == "zoomwheels") {
Chris@70 4985 m_viewManager->setZoomWheelsEnabled(value > 0.5);
Chris@72 4986 } else if (property == "propertyboxes") {
Chris@72 4987 bool toggle = ((value < 0.5) !=
Chris@72 4988 (m_paneStack->getLayoutStyle() == PaneStack::NoPropertyStacks));
Chris@72 4989 if (toggle) togglePropertyBoxes();
Chris@70 4990 }
Chris@70 4991
Chris@70 4992 } else {
Chris@70 4993 PropertyContainer *container = 0;
Chris@70 4994 Pane *pane = m_paneStack->getCurrentPane();
Chris@70 4995 if (pane &&
Chris@70 4996 message.getArgCount() == 3 &&
Chris@70 4997 message.getArg(0).canConvert(QVariant::String) &&
Chris@70 4998 message.getArg(1).canConvert(QVariant::String) &&
Chris@70 4999 message.getArg(2).canConvert(QVariant::String)) {
Chris@70 5000 if (message.getArg(0).toString() == "pane") {
Chris@70 5001 container = pane->getPropertyContainer(0);
Chris@70 5002 } else if (message.getArg(0).toString() == "layer") {
Chris@70 5003 container = pane->getSelectedLayer();
Chris@70 5004 }
Chris@70 5005 }
Chris@70 5006 if (container) {
Chris@70 5007 QString nameString = message.getArg(1).toString();
Chris@70 5008 QString valueString = message.getArg(2).toString();
Chris@70 5009 container->setPropertyWithCommand(nameString, valueString);
Chris@69 5010 }
Chris@69 5011 }
Chris@69 5012
Chris@69 5013 } else if (message.getMethod() == "setcurrent") {
Chris@69 5014
Chris@69 5015 int paneIndex = -1, layerIndex = -1;
Chris@69 5016 bool wantLayer = false;
Chris@69 5017
Chris@69 5018 if (message.getArgCount() >= 1 &&
Chris@69 5019 message.getArg(0).canConvert(QVariant::Int)) {
Chris@69 5020
Chris@69 5021 paneIndex = message.getArg(0).toInt() - 1;
Chris@69 5022
Chris@69 5023 if (message.getArgCount() >= 2 &&
Chris@69 5024 message.getArg(1).canConvert(QVariant::Int)) {
Chris@69 5025 wantLayer = true;
Chris@69 5026 layerIndex = message.getArg(1).toInt() - 1;
Chris@69 5027 }
Chris@69 5028 }
Chris@69 5029
Chris@69 5030 if (paneIndex >= 0 && paneIndex < m_paneStack->getPaneCount()) {
Chris@69 5031 Pane *pane = m_paneStack->getPane(paneIndex);
Chris@70 5032 m_paneStack->setCurrentPane(pane);
Chris@69 5033 if (layerIndex >= 0 && layerIndex < pane->getLayerCount()) {
Chris@69 5034 Layer *layer = pane->getLayer(layerIndex);
Chris@69 5035 m_paneStack->setCurrentLayer(pane, layer);
Chris@69 5036 } else if (wantLayer && layerIndex == -1) {
Chris@69 5037 m_paneStack->setCurrentLayer(pane, 0);
Chris@69 5038 }
Chris@69 5039 }
Chris@69 5040
Chris@69 5041 } else if (message.getMethod() == "delete") {
Chris@69 5042
Chris@69 5043 if (message.getArgCount() == 1 &&
Chris@69 5044 message.getArg(0).canConvert(QVariant::String)) {
Chris@69 5045
Chris@69 5046 QString target = message.getArg(0).toString();
Chris@69 5047
Chris@69 5048 if (target == "pane") {
Chris@69 5049
Chris@69 5050 deleteCurrentPane();
Chris@69 5051
Chris@69 5052 } else if (target == "layer") {
Chris@69 5053
Chris@69 5054 deleteCurrentLayer();
Chris@69 5055
Chris@69 5056 } else {
Chris@69 5057
Chris@69 5058 std::cerr << "WARNING: MainWindow::handleOSCMessage: Unknown delete target " << target.toStdString() << std::endl;
Chris@69 5059 }
Chris@69 5060 }
Chris@69 5061
Chris@69 5062 } else if (message.getMethod() == "zoom") {
Chris@69 5063
Chris@69 5064 if (message.getArgCount() == 1) {
Chris@69 5065 if (message.getArg(0).canConvert(QVariant::String) &&
Chris@69 5066 message.getArg(0).toString() == "in") {
Chris@69 5067 zoomIn();
Chris@69 5068 } else if (message.getArg(0).canConvert(QVariant::String) &&
Chris@69 5069 message.getArg(0).toString() == "out") {
Chris@69 5070 zoomOut();
Chris@69 5071 } else if (message.getArg(0).canConvert(QVariant::String) &&
Chris@69 5072 message.getArg(0).toString() == "default") {
Chris@69 5073 zoomDefault();
Chris@69 5074 } else if (message.getArg(0).canConvert(QVariant::Double)) {
Chris@69 5075 double level = message.getArg(0).toDouble();
Chris@69 5076 Pane *currentPane = m_paneStack->getCurrentPane();
Chris@69 5077 if (level < 1.0) level = 1.0;
Chris@69 5078 if (currentPane) currentPane->setZoomLevel(lrint(level));
Chris@69 5079 }
Chris@69 5080 }
Chris@69 5081
Chris@73 5082 } else if (message.getMethod() == "zoomvertical") {
Chris@73 5083
Chris@73 5084 Pane *pane = m_paneStack->getCurrentPane();
Chris@73 5085 Layer *layer = 0;
Chris@73 5086 if (pane && pane->getLayerCount() > 0) {
Chris@73 5087 layer = pane->getLayer(pane->getLayerCount() - 1);
Chris@73 5088 }
Chris@73 5089 int defaultStep = 0;
Chris@73 5090 int steps = 0;
Chris@73 5091 if (layer) {
Chris@73 5092 steps = layer->getVerticalZoomSteps(defaultStep);
Chris@73 5093 if (message.getArgCount() == 1 && steps > 0) {
Chris@73 5094 if (message.getArg(0).canConvert(QVariant::String) &&
Chris@73 5095 message.getArg(0).toString() == "in") {
Chris@73 5096 int step = layer->getCurrentVerticalZoomStep() + 1;
Chris@73 5097 if (step < steps) layer->setVerticalZoomStep(step);
Chris@73 5098 } else if (message.getArg(0).canConvert(QVariant::String) &&
Chris@73 5099 message.getArg(0).toString() == "out") {
Chris@73 5100 int step = layer->getCurrentVerticalZoomStep() - 1;
Chris@73 5101 if (step >= 0) layer->setVerticalZoomStep(step);
Chris@73 5102 } else if (message.getArg(0).canConvert(QVariant::String) &&
Chris@73 5103 message.getArg(0).toString() == "default") {
Chris@73 5104 layer->setVerticalZoomStep(defaultStep);
Chris@73 5105 }
Chris@73 5106 } else if (message.getArgCount() == 2) {
Chris@73 5107 if (message.getArg(0).canConvert(QVariant::Double) &&
Chris@73 5108 message.getArg(1).canConvert(QVariant::Double)) {
Chris@73 5109 double min = message.getArg(0).toDouble();
Chris@73 5110 double max = message.getArg(1).toDouble();
Chris@73 5111 layer->setDisplayExtents(min, max);
Chris@73 5112 }
Chris@73 5113 }
Chris@73 5114 }
Chris@73 5115
Chris@70 5116 } else if (message.getMethod() == "quit") {
Chris@70 5117
Chris@70 5118 m_abandoning = true;
Chris@70 5119 close();
Chris@70 5120
Chris@70 5121 } else if (message.getMethod() == "resize") {
Chris@70 5122
Chris@70 5123 if (message.getArgCount() == 2) {
Chris@70 5124
Chris@70 5125 int width = 0, height = 0;
Chris@70 5126
Chris@70 5127 if (message.getArg(1).canConvert(QVariant::Int)) {
Chris@70 5128
Chris@70 5129 height = message.getArg(1).toInt();
Chris@70 5130
Chris@70 5131 if (message.getArg(0).canConvert(QVariant::String) &&
Chris@70 5132 message.getArg(0).toString() == "pane") {
Chris@70 5133
Chris@70 5134 Pane *pane = m_paneStack->getCurrentPane();
Chris@70 5135 if (pane) pane->resize(pane->width(), height);
Chris@70 5136
Chris@70 5137 } else if (message.getArg(0).canConvert(QVariant::Int)) {
Chris@70 5138
Chris@70 5139 width = message.getArg(0).toInt();
Chris@70 5140 resize(width, height);
Chris@70 5141 }
Chris@70 5142 }
Chris@70 5143 }
Chris@70 5144
Chris@70 5145 } else if (message.getMethod() == "transform") {
Chris@70 5146
Chris@70 5147 Pane *pane = m_paneStack->getCurrentPane();
Chris@70 5148
Chris@70 5149 if (getMainModel() &&
Chris@70 5150 pane &&
Chris@70 5151 message.getArgCount() == 1 &&
Chris@70 5152 message.getArg(0).canConvert(QVariant::String)) {
Chris@70 5153
Chris@107 5154 TransformId transform = message.getArg(0).toString();
Chris@70 5155
Chris@70 5156 Layer *newLayer = m_document->createDerivedLayer
Chris@70 5157 (transform,
Chris@70 5158 getMainModel(),
Chris@73 5159 TransformFactory::getInstance()->getDefaultContextForTransform
Chris@73 5160 (transform, getMainModel()),
Chris@70 5161 "");
Chris@70 5162
Chris@70 5163 if (newLayer) {
Chris@70 5164 m_document->addLayerToView(pane, newLayer);
Chris@70 5165 m_recentTransforms.add(transform);
Chris@70 5166 m_paneStack->setCurrentLayer(pane, newLayer);
Chris@70 5167 }
Chris@70 5168 }
Chris@70 5169
Chris@69 5170 } else {
Chris@69 5171 std::cerr << "WARNING: MainWindow::handleOSCMessage: Unknown or unsupported "
Chris@69 5172 << "method \"" << message.getMethod().toStdString()
Chris@69 5173 << "\"" << std::endl;
Chris@69 5174 }
Chris@69 5175
Chris@69 5176 }
Chris@69 5177
Chris@69 5178 void
Chris@0 5179 MainWindow::preferences()
Chris@0 5180 {
Chris@0 5181 if (!m_preferencesDialog.isNull()) {
Chris@0 5182 m_preferencesDialog->show();
Chris@0 5183 m_preferencesDialog->raise();
Chris@0 5184 return;
Chris@0 5185 }
Chris@0 5186
Chris@0 5187 m_preferencesDialog = new PreferencesDialog(this);
Chris@0 5188
Chris@0 5189 // DeleteOnClose is safe here, because m_preferencesDialog is a
Chris@0 5190 // QPointer that will be zeroed when the dialog is deleted. We
Chris@0 5191 // use it in preference to leaving the dialog lying around because
Chris@0 5192 // if you Cancel the dialog, it resets the preferences state
Chris@0 5193 // without resetting its own widgets, so its state will be
Chris@0 5194 // incorrect when next shown unless we construct it afresh
Chris@0 5195 m_preferencesDialog->setAttribute(Qt::WA_DeleteOnClose);
Chris@0 5196
Chris@0 5197 m_preferencesDialog->show();
Chris@0 5198 }
Chris@0 5199
Chris@0 5200 void
Chris@90 5201 MainWindow::mouseEnteredWidget()
Chris@90 5202 {
Chris@90 5203 QWidget *w = dynamic_cast<QWidget *>(sender());
Chris@90 5204 if (!w) return;
Chris@90 5205
Chris@90 5206 if (w == m_fader) {
Chris@116 5207 contextHelpChanged(tr("Adjust the master playback level"));
Chris@90 5208 } else if (w == m_playSpeed) {
Chris@116 5209 contextHelpChanged(tr("Adjust the master playback speed"));
Chris@90 5210 } else if (w == m_playSharpen && w->isEnabled()) {
Chris@116 5211 contextHelpChanged(tr("Toggle transient sharpening for playback time scaling"));
Chris@90 5212 } else if (w == m_playMono && w->isEnabled()) {
Chris@116 5213 contextHelpChanged(tr("Toggle mono mode for playback time scaling"));
Chris@90 5214 }
Chris@90 5215 }
Chris@90 5216
Chris@90 5217 void
Chris@90 5218 MainWindow::mouseLeftWidget()
Chris@90 5219 {
Chris@116 5220 contextHelpChanged("");
Chris@116 5221 }
Chris@116 5222
Chris@116 5223 void
Chris@117 5224 MainWindow::inProgressSelectionChanged()
Chris@117 5225 {
Chris@117 5226 Pane *currentPane = 0;
Chris@117 5227 if (m_paneStack) currentPane = m_paneStack->getCurrentPane();
Chris@117 5228 if (currentPane) updateVisibleRangeDisplay(currentPane);
Chris@117 5229 }
Chris@117 5230
Chris@117 5231 void
Chris@116 5232 MainWindow::contextHelpChanged(const QString &s)
Chris@116 5233 {
Chris@116 5234 if (s == "" && m_myStatusMessage != "") {
Chris@116 5235 statusBar()->showMessage(m_myStatusMessage);
Chris@116 5236 return;
Chris@116 5237 }
Chris@116 5238 statusBar()->showMessage(s);
Chris@90 5239 }
Chris@90 5240
Chris@90 5241 void
Chris@0 5242 MainWindow::website()
Chris@0 5243 {
Chris@0 5244 openHelpUrl(tr("http://www.sonicvisualiser.org/"));
Chris@0 5245 }
Chris@0 5246
Chris@0 5247 void
Chris@0 5248 MainWindow::help()
Chris@0 5249 {
Chris@126 5250 openHelpUrl(tr("http://www.sonicvisualiser.org/doc/reference/1.0/en/"));
Chris@0 5251 }
Chris@0 5252
Chris@0 5253 void
Chris@0 5254 MainWindow::openHelpUrl(QString url)
Chris@0 5255 {
Chris@0 5256 // This method mostly lifted from Qt Assistant source code
Chris@0 5257
Chris@0 5258 QProcess *process = new QProcess(this);
Chris@0 5259 connect(process, SIGNAL(finished(int)), process, SLOT(deleteLater()));
Chris@0 5260
Chris@0 5261 QStringList args;
Chris@0 5262
Chris@0 5263 #ifdef Q_OS_MAC
Chris@0 5264 args.append(url);
Chris@0 5265 process->start("open", args);
Chris@0 5266 #else
Chris@0 5267 #ifdef Q_OS_WIN32
Chris@0 5268
Chris@0 5269 QString pf(getenv("ProgramFiles"));
Chris@0 5270 QString command = pf + QString("\\Internet Explorer\\IEXPLORE.EXE");
Chris@0 5271
Chris@0 5272 args.append(url);
Chris@0 5273 process->start(command, args);
Chris@0 5274
Chris@0 5275 #else
Chris@0 5276 #ifdef Q_WS_X11
Chris@0 5277 if (!qgetenv("KDE_FULL_SESSION").isEmpty()) {
Chris@0 5278 args.append("exec");
Chris@0 5279 args.append(url);
Chris@0 5280 process->start("kfmclient", args);
Chris@0 5281 } else if (!qgetenv("BROWSER").isEmpty()) {
Chris@0 5282 args.append(url);
Chris@0 5283 process->start(qgetenv("BROWSER"), args);
Chris@0 5284 } else {
Chris@0 5285 args.append(url);
Chris@0 5286 process->start("firefox", args);
Chris@0 5287 }
Chris@0 5288 #endif
Chris@0 5289 #endif
Chris@0 5290 #endif
Chris@0 5291 }
Chris@0 5292
Chris@0 5293 void
Chris@0 5294 MainWindow::about()
Chris@0 5295 {
Chris@0 5296 bool debug = false;
Chris@0 5297 QString version = "(unknown version)";
Chris@0 5298
Chris@0 5299 #ifdef BUILD_DEBUG
Chris@0 5300 debug = true;
Chris@0 5301 #endif
Chris@0 5302 #ifdef SV_VERSION
Chris@0 5303 #ifdef SVNREV
Chris@0 5304 version = tr("Release %1 : Revision %2").arg(SV_VERSION).arg(SVNREV);
Chris@0 5305 #else
Chris@0 5306 version = tr("Release %1").arg(SV_VERSION);
Chris@0 5307 #endif
Chris@0 5308 #else
Chris@0 5309 #ifdef SVNREV
Chris@0 5310 version = tr("Unreleased : Revision %1").arg(SVNREV);
Chris@0 5311 #endif
Chris@0 5312 #endif
Chris@0 5313
Chris@0 5314 QString aboutText;
Chris@0 5315
Chris@0 5316 aboutText += tr("<h3>About Sonic Visualiser</h3>");
Chris@90 5317 aboutText += tr("<p>Sonic Visualiser is a program for viewing and exploring audio data for<br>semantic music analysis and annotation.</p>");
Chris@111 5318 aboutText += tr("<p>%1 : %2 configuration</p>")
Chris@0 5319 .arg(version)
Chris@0 5320 .arg(debug ? tr("Debug") : tr("Release"));
Chris@0 5321
Chris@132 5322 #ifndef BUILD_STATIC
Chris@132 5323 aboutText += tr("<br>Using Qt v%1 &copy; Trolltech AS").arg(QT_VERSION_STR);
Chris@132 5324 #else
Chris@132 5325 #ifdef QT_SHARED
Chris@132 5326 aboutText += tr("<br>Using Qt v%1 &copy; Trolltech AS").arg(QT_VERSION_STR);
Chris@132 5327 #endif
Chris@132 5328 #endif
Chris@132 5329
Chris@0 5330 #ifdef BUILD_STATIC
Chris@0 5331 aboutText += tr("<p>Statically linked");
Chris@0 5332 #ifndef QT_SHARED
Chris@0 5333 aboutText += tr("<br>With Qt (v%1) &copy; Trolltech AS").arg(QT_VERSION_STR);
Chris@0 5334 #endif
Chris@0 5335 #ifdef HAVE_JACK
Chris@93 5336 #ifdef JACK_VERSION
Chris@0 5337 aboutText += tr("<br>With JACK audio output (v%1) &copy; Paul Davis and Jack O'Quin").arg(JACK_VERSION);
Chris@93 5338 #else
Chris@93 5339 aboutText += tr("<br>With JACK audio output &copy; Paul Davis and Jack O'Quin");
Chris@93 5340 #endif
Chris@0 5341 #endif
Chris@0 5342 #ifdef HAVE_PORTAUDIO
Chris@0 5343 aboutText += tr("<br>With PortAudio audio output &copy; Ross Bencina and Phil Burk");
Chris@0 5344 #endif
Chris@0 5345 #ifdef HAVE_OGGZ
Chris@93 5346 #ifdef OGGZ_VERSION
Chris@0 5347 aboutText += tr("<br>With Ogg file decoder (oggz v%1, fishsound v%2) &copy; CSIRO Australia").arg(OGGZ_VERSION).arg(FISHSOUND_VERSION);
Chris@93 5348 #else
Chris@93 5349 aboutText += tr("<br>With Ogg file decoder &copy; CSIRO Australia");
Chris@93 5350 #endif
Chris@0 5351 #endif
Chris@0 5352 #ifdef HAVE_MAD
Chris@93 5353 #ifdef MAD_VERSION
Chris@0 5354 aboutText += tr("<br>With MAD mp3 decoder (v%1) &copy; Underbit Technologies Inc").arg(MAD_VERSION);
Chris@93 5355 #else
Chris@93 5356 aboutText += tr("<br>With MAD mp3 decoder &copy; Underbit Technologies Inc");
Chris@93 5357 #endif
Chris@0 5358 #endif
Chris@0 5359 #ifdef HAVE_SAMPLERATE
Chris@93 5360 #ifdef SAMPLERATE_VERSION
Chris@0 5361 aboutText += tr("<br>With libsamplerate (v%1) &copy; Erik de Castro Lopo").arg(SAMPLERATE_VERSION);
Chris@93 5362 #else
Chris@93 5363 aboutText += tr("<br>With libsamplerate &copy; Erik de Castro Lopo");
Chris@93 5364 #endif
Chris@0 5365 #endif
Chris@0 5366 #ifdef HAVE_SNDFILE
Chris@93 5367 #ifdef SNDFILE_VERSION
Chris@0 5368 aboutText += tr("<br>With libsndfile (v%1) &copy; Erik de Castro Lopo").arg(SNDFILE_VERSION);
Chris@93 5369 #else
Chris@93 5370 aboutText += tr("<br>With libsndfile &copy; Erik de Castro Lopo");
Chris@93 5371 #endif
Chris@0 5372 #endif
Chris@127 5373 #ifdef HAVE_FFTW3F
Chris@93 5374 #ifdef FFTW3_VERSION
Chris@0 5375 aboutText += tr("<br>With FFTW3 (v%1) &copy; Matteo Frigo and MIT").arg(FFTW3_VERSION);
Chris@93 5376 #else
Chris@93 5377 aboutText += tr("<br>With FFTW3 &copy; Matteo Frigo and MIT");
Chris@93 5378 #endif
Chris@0 5379 #endif
Chris@0 5380 #ifdef HAVE_VAMP
Chris@114 5381 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 5382 #endif
Chris@0 5383 aboutText += tr("<br>With LADSPA plugin support (API v%1) &copy; Richard Furse, Paul Davis, Stefan Westerfeld").arg(LADSPA_VERSION);
Chris@0 5384 aboutText += tr("<br>With DSSI plugin support (API v%1) &copy; Chris Cannam, Steve Harris, Sean Bolton").arg(DSSI_VERSION);
Chris@69 5385 #ifdef HAVE_LIBLO
Chris@93 5386 #ifdef LIBLO_VERSION
Chris@69 5387 aboutText += tr("<br>With liblo Lite OSC library (v%1) &copy; Steve Harris").arg(LIBLO_VERSION);
Chris@93 5388 #else
Chris@93 5389 aboutText += tr("<br>With liblo Lite OSC library &copy; Steve Harris").arg(LIBLO_VERSION);
Chris@93 5390 #endif
Chris@70 5391 if (m_oscQueue && m_oscQueue->isOK()) {
Chris@69 5392 aboutText += tr("<p>The OSC URL for this instance is: \"%1\"").arg(m_oscQueue->getOSCURL());
Chris@69 5393 }
Chris@69 5394 #endif
Chris@0 5395 aboutText += "</p>";
Chris@0 5396 #endif
Chris@0 5397
Chris@0 5398 aboutText +=
Chris@90 5399 "<p>Sonic Visualiser Copyright &copy; 2005 - 2007 Chris Cannam and<br>"
Chris@90 5400 "Queen Mary, University of London.</p>"
Chris@0 5401 "<p>This program is free software; you can redistribute it and/or<br>"
Chris@0 5402 "modify it under the terms of the GNU General Public License as<br>"
Chris@0 5403 "published by the Free Software Foundation; either version 2 of the<br>"
Chris@0 5404 "License, or (at your option) any later version.<br>See the file "
Chris@0 5405 "COPYING included with this distribution for more information.</p>";
Chris@0 5406
Chris@0 5407 QMessageBox::about(this, tr("About Sonic Visualiser"), aboutText);
Chris@0 5408 }
Chris@0 5409
Chris@162 5410 void
Chris@162 5411 MainWindow::keyReference()
Chris@162 5412 {
Chris@162 5413 m_keyReference->show();
Chris@162 5414 }
Chris@162 5415