annotate main/MainWindow.cpp @ 25:e74f508db18c

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