annotate main/MainWindow.cpp @ 116:99d65ba33c88

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