annotate main/MainWindow.cpp @ 45:6b6bca31ad53

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