annotate main/MainWindow.cpp @ 189:26397b84e6a2

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