annotate main/MainWindow.cpp @ 52:527598e2fa10

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