annotate main/MainWindow.cpp @ 35:06787742542a

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