Mercurial > hg > tony
diff src/MainWindow.cpp @ 0:158f40a613a5
Initial import of material from SV and Vect. This builds and runs, but does not yet have the layout we need
author | Chris Cannam |
---|---|
date | Fri, 16 Nov 2012 11:48:16 +0000 |
parents | |
children | a11a9c04df44 |
line wrap: on
line diff
--- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/src/MainWindow.cpp Fri Nov 16 11:48:16 2012 +0000 @@ -0,0 +1,1720 @@ +/* -*- c-basic-offset: 4 indent-tabs-mode: nil -*- vi:set ts=8 sts=4 sw=4: */ + +/* + Tony + An intonation analysis and annotation tool + Centre for Digital Music, Queen Mary, University of London. + This file copyright 2006-2012 Chris Cannam and QMUL. + + This program is free software; you can redistribute it and/or + modify it under the terms of the GNU General Public License as + published by the Free Software Foundation; either version 2 of the + License, or (at your option) any later version. See the file + COPYING included with this distribution for more information. +*/ + +#include "../version.h" + +#include "MainWindow.h" +#include "framework/Document.h" + +#include "view/Pane.h" +#include "view/PaneStack.h" +#include "data/model/WaveFileModel.h" +#include "data/model/SparseOneDimensionalModel.h" +#include "data/model/FFTModel.h" +#include "view/ViewManager.h" +#include "base/Preferences.h" +#include "layer/WaveformLayer.h" +#include "layer/TimeRulerLayer.h" +#include "layer/TimeInstantLayer.h" +#include "layer/TimeValueLayer.h" +#include "layer/Colour3DPlotLayer.h" +#include "layer/SliceLayer.h" +#include "layer/SliceableLayer.h" +#include "widgets/Fader.h" +#include "view/Overview.h" +#include "widgets/PropertyBox.h" +#include "widgets/PropertyStack.h" +#include "widgets/AudioDial.h" +#include "widgets/IconLoader.h" +#include "widgets/LayerTree.h" +#include "widgets/ListInputDialog.h" +#include "widgets/SubdividingMenu.h" +#include "widgets/NotifyingPushButton.h" +#include "widgets/KeyReference.h" +#include "audioio/AudioCallbackPlaySource.h" +#include "audioio/AudioCallbackPlayTarget.h" +#include "audioio/AudioTargetFactory.h" +#include "audioio/PlaySpeedRangeMapper.h" +#include "data/fileio/DataFileReaderFactory.h" +#include "data/fileio/PlaylistFileReader.h" +#include "data/fileio/WavFileWriter.h" +#include "data/fileio/CSVFileWriter.h" +#include "data/fileio/BZipFileDevice.h" +#include "data/fileio/FileSource.h" +#include "data/fft/FFTDataServer.h" +#include "base/RecentFiles.h" +#include "transform/TransformFactory.h" +#include "transform/ModelTransformerFactory.h" +#include "base/PlayParameterRepository.h" +#include "base/XmlExportable.h" +#include "widgets/CommandHistory.h" +#include "base/Profiler.h" +#include "base/Clipboard.h" +#include "base/UnitDatabase.h" +#include "layer/ColourDatabase.h" +#include "data/osc/OSCQueue.h" + +// For version information +#include "vamp/vamp.h" +#include "vamp-sdk/PluginBase.h" +#include "plugin/api/ladspa.h" +#include "plugin/api/dssi.h" + +#include <QApplication> +#include <QMessageBox> +#include <QGridLayout> +#include <QLabel> +#include <QAction> +#include <QMenuBar> +#include <QToolBar> +#include <QToolButton> +#include <QButtonGroup> +#include <QInputDialog> +#include <QStatusBar> +#include <QTreeView> +#include <QFile> +#include <QFileInfo> +#include <QDir> +#include <QTextStream> +#include <QProcess> +#include <QShortcut> +#include <QSettings> +#include <QDateTime> +#include <QProcess> +#include <QCheckBox> +#include <QRegExp> +#include <QScrollArea> + +#include <iostream> +#include <cstdio> +#include <errno.h> + +using std::cerr; +using std::endl; + +using std::vector; +using std::map; +using std::set; + + +MainWindow::MainWindow(bool withAudioOutput, bool withOSCSupport) : + MainWindowBase(withAudioOutput, withOSCSupport, false), + m_overview(0), + m_mainMenusCreated(false), + m_playbackMenu(0), + m_recentFilesMenu(0), + m_rightButtonMenu(0), + m_rightButtonPlaybackMenu(0), + m_deleteSelectedAction(0), + m_ffwdAction(0), + m_rwdAction(0), + m_layerTreeView(0), + m_keyReference(new KeyReference()) +{ + setWindowTitle(QApplication::applicationName()); + + UnitDatabase *udb = UnitDatabase::getInstance(); + udb->registerUnit("Hz"); + udb->registerUnit("dB"); + udb->registerUnit("s"); + + ColourDatabase *cdb = ColourDatabase::getInstance(); + cdb->addColour(Qt::black, tr("Black")); + cdb->addColour(Qt::darkRed, tr("Red")); + cdb->addColour(Qt::darkBlue, tr("Blue")); + cdb->addColour(Qt::darkGreen, tr("Green")); + cdb->addColour(QColor(200, 50, 255), tr("Purple")); + cdb->addColour(QColor(255, 150, 50), tr("Orange")); + cdb->setUseDarkBackground(cdb->addColour(Qt::white, tr("White")), true); + cdb->setUseDarkBackground(cdb->addColour(Qt::red, tr("Bright Red")), true); + cdb->setUseDarkBackground(cdb->addColour(QColor(30, 150, 255), tr("Bright Blue")), true); + cdb->setUseDarkBackground(cdb->addColour(Qt::green, tr("Bright Green")), true); + cdb->setUseDarkBackground(cdb->addColour(QColor(225, 74, 255), tr("Bright Purple")), true); + cdb->setUseDarkBackground(cdb->addColour(QColor(255, 188, 80), tr("Bright Orange")), true); + + Preferences::getInstance()->setResampleOnLoad(true); + Preferences::getInstance()->setSpectrogramSmoothing + (Preferences::SpectrogramInterpolated); + + QSettings settings; + + settings.beginGroup("LayerDefaults"); + + settings.setValue("waveform", + QString("<layer scale=\"%1\" channelMode=\"%2\"/>") + .arg(int(WaveformLayer::MeterScale)) +// .arg(int(WaveformLayer::LinearScale)) + .arg(int(WaveformLayer::MergeChannels))); + + settings.setValue("timevalues", + QString("<layer plotStyle=\"%1\"/>") + .arg(int(TimeValueLayer::PlotStems))); + + settings.setValue("spectrogram", + QString("<layer channel=\"-1\" windowSize=\"2048\" windowHopLevel=\"2\"/>")); + + settings.setValue("melodicrange", + QString("<layer channel=\"-1\" gain=\"10\" normalizeVisibleArea=\"false\" normalizeColumns=\"false\" minFrequency=\"100\" maxFrequency=\"1200\" windowSize=\"4096\" windowOverlap=\"75\" binDisplay=\"0\" />")); + + settings.endGroup(); + + settings.beginGroup("MainWindow"); + settings.setValue("showstatusbar", false); + settings.endGroup(); + + m_viewManager->setAlignMode(true); + m_viewManager->setPlaySoloMode(true); + m_viewManager->setToolMode(ViewManager::NavigateMode); + m_viewManager->setZoomWheelsEnabled(false); + m_viewManager->setIlluminateLocalFeatures(false); + m_viewManager->setShowWorkTitle(true); + + QFrame *frame = new QFrame; + setCentralWidget(frame); + + QGridLayout *layout = new QGridLayout; + + m_descriptionLabel = new QLabel; + + QScrollArea *scroll = new QScrollArea(frame); + scroll->setWidgetResizable(true); + scroll->setHorizontalScrollBarPolicy(Qt::ScrollBarAlwaysOff); + scroll->setFrameShape(QFrame::NoFrame); + + m_paneStack->setLayoutStyle(PaneStack::NoPropertyStacks); + scroll->setWidget(m_paneStack); + + m_overview = new Overview(frame); + m_overview->setViewManager(m_viewManager); + m_overview->setFixedHeight(40); +#ifndef _WIN32 + // For some reason, the contents of the overview never appear if we + // make this setting on Windows. I have no inclination at the moment + // to track down the reason why. + m_overview->setFrameStyle(QFrame::StyledPanel | QFrame::Sunken); +#endif + connect(m_overview, SIGNAL(contextHelpChanged(const QString &)), + this, SLOT(contextHelpChanged(const QString &))); + m_overview->hide(); + + m_panLayer = new WaveformLayer; + m_panLayer->setChannelMode(WaveformLayer::MergeChannels); + m_panLayer->setAggressiveCacheing(true); + m_overview->addLayer(m_panLayer); + + if (m_viewManager->getGlobalDarkBackground()) { + m_panLayer->setBaseColour + (ColourDatabase::getInstance()->getColourIndex(tr("Bright Green"))); + } else { + m_panLayer->setBaseColour + (ColourDatabase::getInstance()->getColourIndex(tr("Green"))); + } + + m_fader = new Fader(frame, false); + connect(m_fader, SIGNAL(mouseEntered()), this, SLOT(mouseEnteredWidget())); + connect(m_fader, SIGNAL(mouseLeft()), this, SLOT(mouseLeftWidget())); + + m_playSpeed = new AudioDial(frame); + m_playSpeed->setMinimum(0); + m_playSpeed->setMaximum(200); + m_playSpeed->setValue(100); + m_playSpeed->setFixedWidth(24); + m_playSpeed->setFixedHeight(24); + m_playSpeed->setNotchesVisible(true); + m_playSpeed->setPageStep(10); + m_playSpeed->setObjectName(tr("Playback Speedup")); + m_playSpeed->setDefaultValue(100); + m_playSpeed->setRangeMapper(new PlaySpeedRangeMapper(0, 200)); + m_playSpeed->setShowToolTip(true); + connect(m_playSpeed, SIGNAL(valueChanged(int)), + this, SLOT(playSpeedChanged(int))); + connect(m_playSpeed, SIGNAL(mouseEntered()), this, SLOT(mouseEnteredWidget())); + connect(m_playSpeed, SIGNAL(mouseLeft()), this, SLOT(mouseLeftWidget())); + + layout->setSpacing(4); + layout->addWidget(scroll, 0, 0, 1, 6); + layout->addWidget(m_overview, 1, 1); + layout->addWidget(m_fader, 1, 2); + layout->addWidget(m_playSpeed, 1, 3); + + m_paneStack->setPropertyStackMinWidth + (m_fader->width() + m_playSpeed->width() + + layout->spacing() * 4); + + layout->setColumnStretch(1, 10); + + frame->setLayout(layout); + + setupMenus(); + setupToolbars(); + setupHelpMenu(); + + statusBar(); + + newSession(); +} + +MainWindow::~MainWindow() +{ + delete m_keyReference; + delete m_layerTreeView; + Profiles::getInstance()->dump(); +} + +void +MainWindow::setupMenus() +{ + if (!m_mainMenusCreated) { + m_rightButtonMenu = new QMenu(); + } + + if (!m_mainMenusCreated) { + CommandHistory::getInstance()->registerMenu(m_rightButtonMenu); + m_rightButtonMenu->addSeparator(); + } + + setupFileMenu(); + setupEditMenu(); + setupViewMenu(); + + m_mainMenusCreated = true; +} + +void +MainWindow::setupFileMenu() +{ + if (m_mainMenusCreated) return; + + QMenu *menu = menuBar()->addMenu(tr("&File")); + menu->setTearOffEnabled(true); + QToolBar *toolbar = addToolBar(tr("File Toolbar")); + + m_keyReference->setCategory(tr("File and Session Management")); + + IconLoader il; + + QIcon icon = il.load("filenew"); + icon.addPixmap(il.loadPixmap("filenew-22")); + QAction *action = new QAction(icon, tr("&Clear Session"), this); + action->setShortcut(tr("Ctrl+N")); + action->setStatusTip(tr("Abandon the current session and start a new one")); + connect(action, SIGNAL(triggered()), this, SLOT(newSession())); + m_keyReference->registerShortcut(action); + menu->addAction(action); + toolbar->addAction(action); + + icon = il.load("fileopen"); + icon.addPixmap(il.loadPixmap("fileopen-22")); + action = new QAction(icon, tr("&Add File..."), this); + action->setShortcut(tr("Ctrl+O")); + action->setStatusTip(tr("Add a file")); + connect(action, SIGNAL(triggered()), this, SLOT(openFile())); + m_keyReference->registerShortcut(action); + menu->addAction(action); + toolbar->addAction(action); + + action = new QAction(tr("Add Lo&cation..."), this); + action->setShortcut(tr("Ctrl+Shift+O")); + action->setStatusTip(tr("Add a file from a remote URL")); + connect(action, SIGNAL(triggered()), this, SLOT(openLocation())); + m_keyReference->registerShortcut(action); + menu->addAction(action); + + menu->addSeparator(); + + m_recentFilesMenu = menu->addMenu(tr("&Recent Locations")); + m_recentFilesMenu->setTearOffEnabled(true); + setupRecentFilesMenu(); + connect(&m_recentFiles, SIGNAL(recentChanged()), + this, SLOT(setupRecentFilesMenu())); + + menu->addSeparator(); + action = new QAction(il.load("exit"), tr("&Quit"), this); + action->setShortcut(tr("Ctrl+Q")); + action->setStatusTip(tr("Exit %1").arg(QApplication::applicationName())); + connect(action, SIGNAL(triggered()), this, SLOT(close())); + m_keyReference->registerShortcut(action); + menu->addAction(action); +} + +void +MainWindow::setupEditMenu() +{ + if (m_mainMenusCreated) return; + + QMenu *menu = menuBar()->addMenu(tr("&Edit")); + menu->setTearOffEnabled(true); + CommandHistory::getInstance()->registerMenu(menu); +} + +void +MainWindow::setupViewMenu() +{ + if (m_mainMenusCreated) return; + + IconLoader il; + + QAction *action = 0; + + m_keyReference->setCategory(tr("Panning and Navigation")); + + QMenu *menu = menuBar()->addMenu(tr("&View")); + menu->setTearOffEnabled(true); + action = new QAction(tr("Scroll &Left"), this); + action->setShortcut(tr("Left")); + action->setStatusTip(tr("Scroll the current pane to the left")); + connect(action, SIGNAL(triggered()), this, SLOT(scrollLeft())); + connect(this, SIGNAL(canScroll(bool)), action, SLOT(setEnabled(bool))); + m_keyReference->registerShortcut(action); + menu->addAction(action); + + action = new QAction(tr("Scroll &Right"), this); + action->setShortcut(tr("Right")); + action->setStatusTip(tr("Scroll the current pane to the right")); + connect(action, SIGNAL(triggered()), this, SLOT(scrollRight())); + connect(this, SIGNAL(canScroll(bool)), action, SLOT(setEnabled(bool))); + m_keyReference->registerShortcut(action); + menu->addAction(action); + + action = new QAction(tr("&Jump Left"), this); + action->setShortcut(tr("Ctrl+Left")); + action->setStatusTip(tr("Scroll the current pane a big step to the left")); + connect(action, SIGNAL(triggered()), this, SLOT(jumpLeft())); + connect(this, SIGNAL(canScroll(bool)), action, SLOT(setEnabled(bool))); + m_keyReference->registerShortcut(action); + menu->addAction(action); + + action = new QAction(tr("J&ump Right"), this); + action->setShortcut(tr("Ctrl+Right")); + action->setStatusTip(tr("Scroll the current pane a big step to the right")); + connect(action, SIGNAL(triggered()), this, SLOT(jumpRight())); + connect(this, SIGNAL(canScroll(bool)), action, SLOT(setEnabled(bool))); + m_keyReference->registerShortcut(action); + menu->addAction(action); + + menu->addSeparator(); + + m_keyReference->setCategory(tr("Zoom")); + + action = new QAction(il.load("zoom-in"), + tr("Zoom &In"), this); + action->setShortcut(tr("Up")); + action->setStatusTip(tr("Increase the zoom level")); + connect(action, SIGNAL(triggered()), this, SLOT(zoomIn())); + connect(this, SIGNAL(canZoom(bool)), action, SLOT(setEnabled(bool))); + m_keyReference->registerShortcut(action); + menu->addAction(action); + + action = new QAction(il.load("zoom-out"), + tr("Zoom &Out"), this); + action->setShortcut(tr("Down")); + action->setStatusTip(tr("Decrease the zoom level")); + connect(action, SIGNAL(triggered()), this, SLOT(zoomOut())); + connect(this, SIGNAL(canZoom(bool)), action, SLOT(setEnabled(bool))); + m_keyReference->registerShortcut(action); + menu->addAction(action); + + action = new QAction(tr("Restore &Default Zoom"), this); + action->setStatusTip(tr("Restore the zoom level to the default")); + connect(action, SIGNAL(triggered()), this, SLOT(zoomDefault())); + connect(this, SIGNAL(canZoom(bool)), action, SLOT(setEnabled(bool))); + menu->addAction(action); + + action = new QAction(il.load("zoom-fit"), + tr("Zoom to &Fit"), this); + action->setShortcut(tr("F")); + action->setStatusTip(tr("Zoom to show the whole file")); + connect(action, SIGNAL(triggered()), this, SLOT(zoomToFit())); + connect(this, SIGNAL(canZoom(bool)), action, SLOT(setEnabled(bool))); + m_keyReference->registerShortcut(action); + menu->addAction(action); + + menu->addSeparator(); + + m_keyReference->setCategory(tr("Display Features")); + + QActionGroup *overlayGroup = new QActionGroup(this); + + action = new QAction(tr("Show &No Overlays"), this); + action->setShortcut(tr("0")); + action->setStatusTip(tr("Hide centre indicator, frame times, layer names and scale")); + connect(action, SIGNAL(triggered()), this, SLOT(showNoOverlays())); + action->setCheckable(true); + action->setChecked(false); + overlayGroup->addAction(action); + m_keyReference->registerShortcut(action); + menu->addAction(action); + + action = new QAction(tr("Show &Minimal Overlays"), this); + action->setShortcut(tr("9")); + action->setStatusTip(tr("Show centre indicator only")); + connect(action, SIGNAL(triggered()), this, SLOT(showMinimalOverlays())); + action->setCheckable(true); + action->setChecked(false); + overlayGroup->addAction(action); + m_keyReference->registerShortcut(action); + menu->addAction(action); + + action = new QAction(tr("Show &Standard Overlays"), this); + action->setShortcut(tr("8")); + action->setStatusTip(tr("Show centre indicator, frame times and scale")); + connect(action, SIGNAL(triggered()), this, SLOT(showStandardOverlays())); + action->setCheckable(true); + action->setChecked(true); + overlayGroup->addAction(action); + m_keyReference->registerShortcut(action); + menu->addAction(action); + + action = new QAction(tr("Show &All Overlays"), this); + action->setShortcut(tr("7")); + action->setStatusTip(tr("Show all texts and scale")); + connect(action, SIGNAL(triggered()), this, SLOT(showAllOverlays())); + action->setCheckable(true); + action->setChecked(false); + overlayGroup->addAction(action); + m_keyReference->registerShortcut(action); + menu->addAction(action); + + menu->addSeparator(); + + action = new QAction(tr("Show &Zoom Wheels"), this); + action->setShortcut(tr("Z")); + action->setStatusTip(tr("Show thumbwheels for zooming horizontally and vertically")); + connect(action, SIGNAL(triggered()), this, SLOT(toggleZoomWheels())); + action->setCheckable(true); + action->setChecked(m_viewManager->getZoomWheelsEnabled()); + m_keyReference->registerShortcut(action); + menu->addAction(action); + + action = new QAction(tr("Show Property Bo&xes"), this); + action->setShortcut(tr("X")); + action->setStatusTip(tr("Show the layer property boxes at the side of the main window")); + connect(action, SIGNAL(triggered()), this, SLOT(togglePropertyBoxes())); + action->setCheckable(true); + action->setChecked(false); //!!! + m_keyReference->registerShortcut(action); + menu->addAction(action); + + action = new QAction(tr("Show Status &Bar"), this); + action->setStatusTip(tr("Show context help information in the status bar at the bottom of the window")); + connect(action, SIGNAL(triggered()), this, SLOT(toggleStatusBar())); + action->setCheckable(true); + action->setChecked(true); + menu->addAction(action); + + QSettings settings; + settings.beginGroup("MainWindow"); + bool sb = settings.value("showstatusbar", true).toBool(); + if (!sb) { + action->setChecked(false); + statusBar()->hide(); + } + settings.endGroup(); + + menu->addSeparator(); + + action = new QAction(tr("Show La&yer Hierarchy"), this); + action->setShortcut(tr("H")); + action->setStatusTip(tr("Open a window displaying the hierarchy of panes and layers in this session")); + connect(action, SIGNAL(triggered()), this, SLOT(showLayerTree())); + m_keyReference->registerShortcut(action); + menu->addAction(action); +} + +void +MainWindow::setupHelpMenu() +{ + QMenu *menu = menuBar()->addMenu(tr("&Help")); + menu->setTearOffEnabled(true); + + m_keyReference->setCategory(tr("Help")); + + IconLoader il; + + QAction *action = new QAction(il.load("help"), + tr("&Help Reference"), this); + action->setShortcut(tr("F1")); + action->setStatusTip(tr("Open the reference manual")); + connect(action, SIGNAL(triggered()), this, SLOT(help())); + m_keyReference->registerShortcut(action); + menu->addAction(action); + + action = new QAction(tr("&Key and Mouse Reference"), this); + action->setShortcut(tr("F2")); + action->setStatusTip(tr("Open a window showing the keystrokes you can use")); + connect(action, SIGNAL(triggered()), this, SLOT(keyReference())); + m_keyReference->registerShortcut(action); + menu->addAction(action); + + action = new QAction(tr("Sonic Visualiser on the &Web"), this); + action->setStatusTip(tr("Open the Sonic Visualiser website")); + connect(action, SIGNAL(triggered()), this, SLOT(website())); + menu->addAction(action); + + action = new QAction(tr("&About Sonic Visualiser"), this); + action->setStatusTip(tr("Show information about Sonic Visualiser")); + connect(action, SIGNAL(triggered()), this, SLOT(about())); + menu->addAction(action); +} + +void +MainWindow::setupRecentFilesMenu() +{ + m_recentFilesMenu->clear(); + vector<QString> files = m_recentFiles.getRecent(); + for (size_t i = 0; i < files.size(); ++i) { + QAction *action = new QAction(files[i], this); + connect(action, SIGNAL(triggered()), this, SLOT(openRecentFile())); + if (i == 0) { + action->setShortcut(tr("Ctrl+R")); + m_keyReference->registerShortcut + (tr("Re-open"), + action->shortcut(), + tr("Re-open the current or most recently opened file")); + } + m_recentFilesMenu->addAction(action); + } +} + +void +MainWindow::setupToolbars() +{ + m_keyReference->setCategory(tr("Playback and Transport Controls")); + + IconLoader il; + + QMenu *menu = m_playbackMenu = menuBar()->addMenu(tr("Play&back")); + menu->setTearOffEnabled(true); + m_rightButtonMenu->addSeparator(); + m_rightButtonPlaybackMenu = m_rightButtonMenu->addMenu(tr("Playback")); + + QToolBar *toolbar = addToolBar(tr("Playback Toolbar")); + + QAction *rwdStartAction = toolbar->addAction(il.load("rewind-start"), + tr("Rewind to Start")); + rwdStartAction->setShortcut(tr("Home")); + rwdStartAction->setStatusTip(tr("Rewind to the start")); + connect(rwdStartAction, SIGNAL(triggered()), this, SLOT(rewindStart())); + connect(this, SIGNAL(canPlay(bool)), rwdStartAction, SLOT(setEnabled(bool))); + + QAction *m_rwdAction = toolbar->addAction(il.load("rewind"), + tr("Rewind")); + m_rwdAction->setShortcut(tr("PgUp")); + m_rwdAction->setStatusTip(tr("Rewind to the previous time instant or time ruler notch")); + connect(m_rwdAction, SIGNAL(triggered()), this, SLOT(rewind())); + connect(this, SIGNAL(canRewind(bool)), m_rwdAction, SLOT(setEnabled(bool))); + + QAction *playAction = toolbar->addAction(il.load("playpause"), + tr("Play / Pause")); + playAction->setCheckable(true); + playAction->setShortcut(tr("Space")); + playAction->setStatusTip(tr("Start or stop playback from the current position")); + connect(playAction, SIGNAL(triggered()), this, SLOT(play())); + connect(m_playSource, SIGNAL(playStatusChanged(bool)), + playAction, SLOT(setChecked(bool))); + connect(this, SIGNAL(canPlay(bool)), playAction, SLOT(setEnabled(bool))); + + m_ffwdAction = toolbar->addAction(il.load("ffwd"), + tr("Fast Forward")); + m_ffwdAction->setShortcut(tr("PgDown")); + m_ffwdAction->setStatusTip(tr("Fast-forward to the next time instant or time ruler notch")); + connect(m_ffwdAction, SIGNAL(triggered()), this, SLOT(ffwd())); + connect(this, SIGNAL(canFfwd(bool)), m_ffwdAction, SLOT(setEnabled(bool))); + + QAction *ffwdEndAction = toolbar->addAction(il.load("ffwd-end"), + tr("Fast Forward to End")); + ffwdEndAction->setShortcut(tr("End")); + ffwdEndAction->setStatusTip(tr("Fast-forward to the end")); + connect(ffwdEndAction, SIGNAL(triggered()), this, SLOT(ffwdEnd())); + connect(this, SIGNAL(canPlay(bool)), ffwdEndAction, SLOT(setEnabled(bool))); +/* + toolbar = addToolBar(tr("Play Mode Toolbar")); + + QAction *psAction = toolbar->addAction(il.load("playselection"), + tr("Constrain Playback to Selection")); + psAction->setCheckable(true); + psAction->setChecked(m_viewManager->getPlaySelectionMode()); + psAction->setShortcut(tr("s")); + psAction->setStatusTip(tr("Constrain playback to the selected regions")); + connect(m_viewManager, SIGNAL(playSelectionModeChanged(bool)), + psAction, SLOT(setChecked(bool))); + connect(psAction, SIGNAL(triggered()), this, SLOT(playSelectionToggled())); + connect(this, SIGNAL(canPlaySelection(bool)), psAction, SLOT(setEnabled(bool))); + + QAction *plAction = toolbar->addAction(il.load("playloop"), + tr("Loop Playback")); + plAction->setCheckable(true); + plAction->setChecked(m_viewManager->getPlayLoopMode()); + plAction->setShortcut(tr("l")); + plAction->setStatusTip(tr("Loop playback")); + connect(m_viewManager, SIGNAL(playLoopModeChanged(bool)), + plAction, SLOT(setChecked(bool))); + connect(plAction, SIGNAL(triggered()), this, SLOT(playLoopToggled())); + connect(this, SIGNAL(canPlay(bool)), plAction, SLOT(setEnabled(bool))); + + QAction *soAction = toolbar->addAction(il.load("solo"), + tr("Solo Current Pane")); + soAction->setCheckable(true); + soAction->setChecked(m_viewManager->getPlaySoloMode()); + soAction->setShortcut(tr("o")); + soAction->setStatusTip(tr("Solo the current pane during playback")); + connect(m_viewManager, SIGNAL(playSoloModeChanged(bool)), + soAction, SLOT(setChecked(bool))); + connect(soAction, SIGNAL(triggered()), this, SLOT(playSoloToggled())); + connect(this, SIGNAL(canPlay(bool)), soAction, SLOT(setEnabled(bool))); + + m_keyReference->registerShortcut(psAction); + m_keyReference->registerShortcut(plAction); + m_keyReference->registerShortcut(soAction); +*/ + m_keyReference->registerShortcut(playAction); + m_keyReference->registerShortcut(m_rwdAction); + m_keyReference->registerShortcut(m_ffwdAction); + m_keyReference->registerShortcut(rwdStartAction); + m_keyReference->registerShortcut(ffwdEndAction); + +/* + menu->addAction(psAction); + menu->addAction(plAction); + menu->addAction(soAction); +*/ + menu->addAction(playAction); + menu->addSeparator(); + menu->addAction(m_rwdAction); + menu->addAction(m_ffwdAction); + menu->addSeparator(); + menu->addAction(rwdStartAction); + menu->addAction(ffwdEndAction); + menu->addSeparator(); + + m_rightButtonPlaybackMenu->addAction(playAction); +/* + m_rightButtonPlaybackMenu->addAction(psAction); + m_rightButtonPlaybackMenu->addAction(plAction); + m_rightButtonPlaybackMenu->addAction(soAction); +*/ + m_rightButtonPlaybackMenu->addSeparator(); + m_rightButtonPlaybackMenu->addAction(m_rwdAction); + m_rightButtonPlaybackMenu->addAction(m_ffwdAction); + m_rightButtonPlaybackMenu->addSeparator(); + m_rightButtonPlaybackMenu->addAction(rwdStartAction); + m_rightButtonPlaybackMenu->addAction(ffwdEndAction); + m_rightButtonPlaybackMenu->addSeparator(); + + QAction *fastAction = menu->addAction(tr("Speed Up")); + fastAction->setShortcut(tr("Ctrl+PgUp")); + fastAction->setStatusTip(tr("Time-stretch playback to speed it up without changing pitch")); + connect(fastAction, SIGNAL(triggered()), this, SLOT(speedUpPlayback())); + connect(this, SIGNAL(canSpeedUpPlayback(bool)), fastAction, SLOT(setEnabled(bool))); + + QAction *slowAction = menu->addAction(tr("Slow Down")); + slowAction->setShortcut(tr("Ctrl+PgDown")); + slowAction->setStatusTip(tr("Time-stretch playback to slow it down without changing pitch")); + connect(slowAction, SIGNAL(triggered()), this, SLOT(slowDownPlayback())); + connect(this, SIGNAL(canSlowDownPlayback(bool)), slowAction, SLOT(setEnabled(bool))); + + QAction *normalAction = menu->addAction(tr("Restore Normal Speed")); + normalAction->setShortcut(tr("Ctrl+Home")); + normalAction->setStatusTip(tr("Restore non-time-stretched playback")); + connect(normalAction, SIGNAL(triggered()), this, SLOT(restoreNormalPlayback())); + connect(this, SIGNAL(canChangePlaybackSpeed(bool)), normalAction, SLOT(setEnabled(bool))); + + m_keyReference->registerShortcut(fastAction); + m_keyReference->registerShortcut(slowAction); + m_keyReference->registerShortcut(normalAction); + + m_rightButtonPlaybackMenu->addAction(fastAction); + m_rightButtonPlaybackMenu->addAction(slowAction); + m_rightButtonPlaybackMenu->addAction(normalAction); +/* + toolbar = addToolBar(tr("Edit Toolbar")); + CommandHistory::getInstance()->registerToolbar(toolbar); +*/ + + Pane::registerShortcuts(*m_keyReference); +} + +void +MainWindow::updateMenuStates() +{ + MainWindowBase::updateMenuStates(); + + Pane *currentPane = 0; + Layer *currentLayer = 0; + + if (m_paneStack) currentPane = m_paneStack->getCurrentPane(); + if (currentPane) currentLayer = currentPane->getSelectedLayer(); + + bool haveCurrentPane = + (currentPane != 0); + bool haveCurrentLayer = + (haveCurrentPane && + (currentLayer != 0)); + bool haveSelection = + (m_viewManager && + !m_viewManager->getSelections().empty()); + bool haveCurrentEditableLayer = + (haveCurrentLayer && + currentLayer->isLayerEditable()); + bool haveCurrentTimeInstantsLayer = + (haveCurrentLayer && + dynamic_cast<TimeInstantLayer *>(currentLayer)); + bool haveCurrentTimeValueLayer = + (haveCurrentLayer && + dynamic_cast<TimeValueLayer *>(currentLayer)); + + emit canChangePlaybackSpeed(true); + int v = m_playSpeed->value(); + emit canSpeedUpPlayback(v < m_playSpeed->maximum()); + emit canSlowDownPlayback(v > m_playSpeed->minimum()); + + if (m_ffwdAction && m_rwdAction) { + if (haveCurrentTimeInstantsLayer) { + m_ffwdAction->setText(tr("Fast Forward to Next Instant")); + m_ffwdAction->setStatusTip(tr("Fast forward to the next time instant in the current layer")); + m_rwdAction->setText(tr("Rewind to Previous Instant")); + m_rwdAction->setStatusTip(tr("Rewind to the previous time instant in the current layer")); + } else if (haveCurrentTimeValueLayer) { + m_ffwdAction->setText(tr("Fast Forward to Next Point")); + m_ffwdAction->setStatusTip(tr("Fast forward to the next point in the current layer")); + m_rwdAction->setText(tr("Rewind to Previous Point")); + m_rwdAction->setStatusTip(tr("Rewind to the previous point in the current layer")); + } else { + m_ffwdAction->setText(tr("Fast Forward")); + m_ffwdAction->setStatusTip(tr("Fast forward")); + m_rwdAction->setText(tr("Rewind")); + m_rwdAction->setStatusTip(tr("Rewind")); + } + } +} + +void +MainWindow::updateDescriptionLabel() +{ + if (!getMainModel()) { + m_descriptionLabel->setText(tr("No audio file loaded.")); + return; + } + + QString description; + + size_t ssr = getMainModel()->getSampleRate(); + size_t tsr = ssr; + if (m_playSource) tsr = m_playSource->getTargetSampleRate(); + + if (ssr != tsr) { + description = tr("%1Hz (resampling to %2Hz)").arg(ssr).arg(tsr); + } else { + description = QString("%1Hz").arg(ssr); + } + + description = QString("%1 - %2") + .arg(RealTime::frame2RealTime(getMainModel()->getEndFrame(), ssr) + .toText(false).c_str()) + .arg(description); + + m_descriptionLabel->setText(description); +} + +void +MainWindow::documentModified() +{ + //!!! + MainWindowBase::documentModified(); +} + +void +MainWindow::documentRestored() +{ + //!!! + MainWindowBase::documentRestored(); +} + +void +MainWindow::newSession() +{ + if (!checkSaveModified()) return; + + closeSession(); + createDocument(); + m_document->setAutoAlignment(true); + + Pane *pane = m_paneStack->addPane(); + + connect(pane, SIGNAL(contextHelpChanged(const QString &)), + this, SLOT(contextHelpChanged(const QString &))); + + Layer *waveform = m_document->createMainModelLayer(LayerFactory::Waveform); + m_document->addLayerToView(pane, waveform); + + m_overview->registerView(pane); + + CommandHistory::getInstance()->clear(); + CommandHistory::getInstance()->documentSaved(); + documentRestored(); + updateMenuStates(); +} + +void +MainWindow::closeSession() +{ + if (!checkSaveModified()) return; + + while (m_paneStack->getPaneCount() > 0) { + + Pane *pane = m_paneStack->getPane(m_paneStack->getPaneCount() - 1); + + while (pane->getLayerCount() > 0) { + m_document->removeLayerFromView + (pane, pane->getLayer(pane->getLayerCount() - 1)); + } + + m_overview->unregisterView(pane); + m_paneStack->deletePane(pane); + } + + while (m_paneStack->getHiddenPaneCount() > 0) { + + Pane *pane = m_paneStack->getHiddenPane + (m_paneStack->getHiddenPaneCount() - 1); + + while (pane->getLayerCount() > 0) { + m_document->removeLayerFromView + (pane, pane->getLayer(pane->getLayerCount() - 1)); + } + + m_overview->unregisterView(pane); + m_paneStack->deletePane(pane); + } + + delete m_document; + m_document = 0; + m_viewManager->clearSelections(); + m_timeRulerLayer = 0; // document owned this + + m_sessionFile = ""; + + CommandHistory::getInstance()->clear(); + CommandHistory::getInstance()->documentSaved(); + documentRestored(); +} + +void +MainWindow::openFile() +{ + QString orig = m_audioFile; + if (orig == "") orig = "."; + else orig = QFileInfo(orig).absoluteDir().canonicalPath(); + + QString path = getOpenFileName(FileFinder::AnyFile); + + if (path.isEmpty()) return; + + FileOpenStatus status = open(path, CreateAdditionalModel); + + if (status == FileOpenFailed) { + QMessageBox::critical(this, tr("Failed to open file"), + tr("<b>File open failed</b><p>File \"%1\" could not be opened").arg(path)); + } else if (status == FileOpenWrongMode) { + QMessageBox::critical(this, tr("Failed to open file"), + tr("<b>Audio required</b><p>Please load at least one audio file before importing annotation data")); + } else { + configureNewPane(m_paneStack->getCurrentPane()); + } +} + +void +MainWindow::openLocation() +{ + QSettings settings; + settings.beginGroup("MainWindow"); + QString lastLocation = settings.value("lastremote", "").toString(); + + bool ok = false; + QString text = QInputDialog::getText + (this, tr("Open Location"), + tr("Please enter the URL of the location to open:"), + QLineEdit::Normal, lastLocation, &ok); + + if (!ok) return; + + settings.setValue("lastremote", text); + + if (text.isEmpty()) return; + + FileOpenStatus status = open(text, CreateAdditionalModel); + + if (status == FileOpenFailed) { + QMessageBox::critical(this, tr("Failed to open location"), + tr("<b>Open failed</b><p>URL \"%1\" could not be opened").arg(text)); + } else if (status == FileOpenWrongMode) { + QMessageBox::critical(this, tr("Failed to open location"), + tr("<b>Audio required</b><p>Please load at least one audio file before importing annotation data")); + } else { + configureNewPane(m_paneStack->getCurrentPane()); + } +} + +void +MainWindow::openRecentFile() +{ + QObject *obj = sender(); + QAction *action = dynamic_cast<QAction *>(obj); + + if (!action) { + std::cerr << "WARNING: MainWindow::openRecentFile: sender is not an action" + << std::endl; + return; + } + + QString path = action->text(); + if (path == "") return; + + FileOpenStatus status = open(path, CreateAdditionalModel); + + if (status == FileOpenFailed) { + QMessageBox::critical(this, tr("Failed to open location"), + tr("<b>Open failed</b><p>File or URL \"%1\" could not be opened").arg(path)); + } else if (status == FileOpenWrongMode) { + QMessageBox::critical(this, tr("Failed to open location"), + tr("<b>Audio required</b><p>Please load at least one audio file before importing annotation data")); + } else { + configureNewPane(m_paneStack->getCurrentPane()); + } +} + +void +MainWindow::paneAdded(Pane *pane) +{ + pane->setPlaybackFollow(PlaybackScrollContinuous); + m_paneStack->sizePanesEqually(); + if (m_overview) m_overview->registerView(pane); +} + +void +MainWindow::paneHidden(Pane *pane) +{ + if (m_overview) m_overview->unregisterView(pane); +} + +void +MainWindow::paneAboutToBeDeleted(Pane *pane) +{ + if (m_overview) m_overview->unregisterView(pane); +} + +void +MainWindow::paneDropAccepted(Pane *pane, QStringList uriList) +{ +// if (pane) m_paneStack->setCurrentPane(pane); + + for (QStringList::iterator i = uriList.begin(); i != uriList.end(); ++i) { + + FileOpenStatus status = open(*i, CreateAdditionalModel); + + if (status == FileOpenFailed) { + QMessageBox::critical(this, tr("Failed to open dropped URL"), + tr("<b>Open failed</b><p>Dropped URL \"%1\" could not be opened").arg(*i)); + } else if (status == FileOpenWrongMode) { + QMessageBox::critical(this, tr("Failed to open dropped URL"), + tr("<b>Audio required</b><p>Please load at least one audio file before importing annotation data")); + } else { + configureNewPane(m_paneStack->getCurrentPane()); + } + } +} + +void +MainWindow::paneDropAccepted(Pane *pane, QString text) +{ + if (pane) m_paneStack->setCurrentPane(pane); + + QUrl testUrl(text); + if (testUrl.scheme() == "file" || + testUrl.scheme() == "http" || + testUrl.scheme() == "ftp") { + QStringList list; + list.push_back(text); + paneDropAccepted(pane, list); + return; + } + + //!!! open as text -- but by importing as if a CSV, or just adding + //to a text layer? +} + +void +MainWindow::configureNewPane(Pane *pane) +{ + std::cerr << "MainWindow::configureNewPane(" << pane << ")" << std::endl; + + if (!pane) return; + + Layer *waveformLayer = 0; + + for (int i = 0; i < pane->getLayerCount(); ++i) { + Layer *layer = pane->getLayer(i); + if (!layer) continue; + if (dynamic_cast<WaveformLayer *>(layer)) waveformLayer = layer; + if (dynamic_cast<TimeValueLayer *>(layer)) return; + } + if (!waveformLayer) return; + + waveformLayer->setObjectName(tr("Waveform")); + + zoomToFit(); +} + +void +MainWindow::closeEvent(QCloseEvent *e) +{ +// std::cerr << "MainWindow::closeEvent" << std::endl; + + if (m_openingAudioFile) { +// std::cerr << "Busy - ignoring close event" << std::endl; + e->ignore(); + return; + } + + if (!m_abandoning && !checkSaveModified()) { +// std::cerr << "Ignoring close event" << std::endl; + e->ignore(); + return; + } + + QSettings settings; + settings.beginGroup("MainWindow"); + settings.setValue("size", size()); + settings.setValue("position", pos()); + settings.endGroup(); + + delete m_keyReference; + m_keyReference = 0; + + if (m_layerTreeView && + m_layerTreeView->isVisible()) { + delete m_layerTreeView; + } + + closeSession(); + + e->accept(); + return; +} + +bool +MainWindow::commitData(bool mayAskUser) +{ + if (mayAskUser) { + bool rv = checkSaveModified(); + return rv; + } else { + if (!m_documentModified) return true; + + // If we can't check with the user first, then we can't save + // to the original session file (even if we have it) -- have + // to use a temporary file + + QString svDirBase = ".sv1"; + QString svDir = QDir::home().filePath(svDirBase); + + if (!QFileInfo(svDir).exists()) { + if (!QDir::home().mkdir(svDirBase)) return false; + } else { + if (!QFileInfo(svDir).isDir()) return false; + } + + // This name doesn't have to be unguessable +#ifndef _WIN32 + QString fname = QString("tmp-%1-%2.sv") + .arg(QDateTime::currentDateTime().toString("yyyyMMddhhmmsszzz")) + .arg(QProcess().pid()); +#else + QString fname = QString("tmp-%1.sv") + .arg(QDateTime::currentDateTime().toString("yyyyMMddhhmmsszzz")); +#endif + QString fpath = QDir(svDir).filePath(fname); + if (saveSessionFile(fpath)) { + m_recentFiles.addFile(fpath); + return true; + } else { + return false; + } + } +} + +bool +MainWindow::checkSaveModified() +{ + // Called before some destructive operation (e.g. new session, + // exit program). Return true if we can safely proceed, false to + // cancel. + + if (!m_documentModified) return true; + + int button = + QMessageBox::warning(this, + tr("Session modified"), + tr("The current session has been modified.\nDo you want to save it?"), + QMessageBox::Yes | QMessageBox::No | QMessageBox::Cancel, + QMessageBox::Yes); + + if (button == QMessageBox::Yes) { + saveSession(); + if (m_documentModified) { // save failed -- don't proceed! + return false; + } else { + return true; // saved, so it's safe to continue now + } + } else if (button == QMessageBox::No) { + m_documentModified = false; // so we know to abandon it + return true; + } + + // else cancel + return false; +} + +void +MainWindow::saveSession() +{ + if (m_sessionFile != "") { + if (!saveSessionFile(m_sessionFile)) { + QMessageBox::critical(this, tr("Failed to save file"), + tr("Session file \"%1\" could not be saved.").arg(m_sessionFile)); + } else { + CommandHistory::getInstance()->documentSaved(); + documentRestored(); + } + } else { + saveSessionAs(); + } +} + +void +MainWindow::saveSessionAs() +{ + QString orig = m_audioFile; + if (orig == "") orig = "."; + else orig = QFileInfo(orig).absoluteDir().canonicalPath(); + + QString path = getSaveFileName(FileFinder::SessionFile); + + if (path == "") return; + + if (!saveSessionFile(path)) { + QMessageBox::critical(this, tr("Failed to save file"), + tr("Session file \"%1\" could not be saved.").arg(path)); + } else { + setWindowTitle(tr("%1: %2") + .arg(QApplication::applicationName()) + .arg(QFileInfo(path).fileName())); + m_sessionFile = path; + CommandHistory::getInstance()->documentSaved(); + documentRestored(); + m_recentFiles.addFile(path); + } +} + +void +MainWindow::renameCurrentLayer() +{ + Pane *pane = m_paneStack->getCurrentPane(); + if (pane) { + Layer *layer = pane->getSelectedLayer(); + if (layer) { + bool ok = false; + QString newName = QInputDialog::getText + (this, tr("Rename Layer"), + tr("New name for this layer:"), + QLineEdit::Normal, layer->objectName(), &ok); + if (ok) { + layer->setObjectName(newName); + } + } + } +} + +void +MainWindow::playSpeedChanged(int position) +{ + PlaySpeedRangeMapper mapper(0, 200); + + float percent = m_playSpeed->mappedValue(); + float factor = mapper.getFactorForValue(percent); + + std::cerr << "speed = " << position << " percent = " << percent << " factor = " << factor << std::endl; + + bool something = (position != 100); + + int pc = lrintf(percent); + + if (!something) { + contextHelpChanged(tr("Playback speed: Normal")); + } else { + contextHelpChanged(tr("Playback speed: %1%2%") + .arg(position > 100 ? "+" : "") + .arg(pc)); + } + + m_playSource->setTimeStretch(factor); + + updateMenuStates(); +} + +void +MainWindow::playSharpenToggled() +{ + QSettings settings; + settings.beginGroup("MainWindow"); + settings.setValue("playsharpen", m_playSharpen->isChecked()); + settings.endGroup(); + + playSpeedChanged(m_playSpeed->value()); +} + +void +MainWindow::playMonoToggled() +{ + QSettings settings; + settings.beginGroup("MainWindow"); + settings.setValue("playmono", m_playMono->isChecked()); + settings.endGroup(); + + playSpeedChanged(m_playSpeed->value()); +} + +void +MainWindow::speedUpPlayback() +{ + int value = m_playSpeed->value(); + value = value + m_playSpeed->pageStep(); + if (value > m_playSpeed->maximum()) value = m_playSpeed->maximum(); + m_playSpeed->setValue(value); +} + +void +MainWindow::slowDownPlayback() +{ + int value = m_playSpeed->value(); + value = value - m_playSpeed->pageStep(); + if (value < m_playSpeed->minimum()) value = m_playSpeed->minimum(); + m_playSpeed->setValue(value); +} + +void +MainWindow::restoreNormalPlayback() +{ + m_playSpeed->setValue(m_playSpeed->defaultValue()); +} + +void +MainWindow::updateVisibleRangeDisplay(Pane *p) const +{ + if (!getMainModel() || !p) { + return; + } + + bool haveSelection = false; + size_t startFrame = 0, endFrame = 0; + + if (m_viewManager && m_viewManager->haveInProgressSelection()) { + + bool exclusive = false; + Selection s = m_viewManager->getInProgressSelection(exclusive); + + if (!s.isEmpty()) { + haveSelection = true; + startFrame = s.getStartFrame(); + endFrame = s.getEndFrame(); + } + } + + if (!haveSelection) { + startFrame = p->getFirstVisibleFrame(); + endFrame = p->getLastVisibleFrame(); + } + + RealTime start = RealTime::frame2RealTime + (startFrame, getMainModel()->getSampleRate()); + + RealTime end = RealTime::frame2RealTime + (endFrame, getMainModel()->getSampleRate()); + + RealTime duration = end - start; + + QString startStr, endStr, durationStr; + startStr = start.toText(true).c_str(); + endStr = end.toText(true).c_str(); + durationStr = duration.toText(true).c_str(); + + if (haveSelection) { + m_myStatusMessage = tr("Selection: %1 to %2 (duration %3)") + .arg(startStr).arg(endStr).arg(durationStr); + } else { + m_myStatusMessage = tr("Visible: %1 to %2 (duration %3)") + .arg(startStr).arg(endStr).arg(durationStr); + } + + statusBar()->showMessage(m_myStatusMessage); +} + +void +MainWindow::updatePositionStatusDisplays() const +{ + if (!statusBar()->isVisible()) return; + +} + +void +MainWindow::outputLevelsChanged(float left, float right) +{ + m_fader->setPeakLeft(left); + m_fader->setPeakRight(right); +} + +void +MainWindow::sampleRateMismatch(size_t requested, size_t actual, + bool willResample) +{ + if (!willResample) { + //!!! more helpful message needed + QMessageBox::information + (this, tr("Sample rate mismatch"), + 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 and pitch.") + .arg(requested).arg(actual)); + } + + updateDescriptionLabel(); +} + +void +MainWindow::audioOverloadPluginDisabled() +{ + QMessageBox::information + (this, tr("Audio processing overload"), + tr("<b>Overloaded</b><p>Audio effects plugin auditioning has been disabled due to a processing overload.")); +} + +void +MainWindow::audioTimeStretchMultiChannelDisabled() +{ + static bool shownOnce = false; + if (shownOnce) return; + QMessageBox::information + (this, tr("Audio processing overload"), + tr("<b>Overloaded</b><p>Audio playback speed processing has been reduced to a single channel, due to a processing overload.")); + shownOnce = true; +} + +void +MainWindow::layerRemoved(Layer *layer) +{ + MainWindowBase::layerRemoved(layer); +} + +void +MainWindow::layerInAView(Layer *layer, bool inAView) +{ + MainWindowBase::layerInAView(layer, inAView); +} + +void +MainWindow::modelAdded(Model *model) +{ + MainWindowBase::modelAdded(model); + DenseTimeValueModel *dtvm = dynamic_cast<DenseTimeValueModel *>(model); + if (dtvm) { + std::cerr << "A dense time-value model (such as an audio file) has been loaded" << std::endl; + } +} + +void +MainWindow::modelReady() +{ + QObject *s = sender(); + std::cerr << "MainWindow::modelReady(" << s << ")" << std::endl; +} + +void +MainWindow::modelAboutToBeDeleted(Model *model) +{ + MainWindowBase::modelAboutToBeDeleted(model); +} + +void +MainWindow::mainModelChanged(WaveFileModel *model) +{ + m_panLayer->setModel(model); + + MainWindowBase::mainModelChanged(model); + + if (m_playTarget) { + connect(m_fader, SIGNAL(valueChanged(float)), + m_playTarget, SLOT(setOutputGain(float))); + } +} + +void +MainWindow::modelGenerationFailed(QString transformName, QString message) +{ + if (message != "") { + + QMessageBox::warning + (this, + tr("Failed to generate layer"), + tr("<b>Layer generation failed</b><p>Failed to generate derived layer.<p>The layer transform \"%1\" failed:<p>%2") + .arg(transformName).arg(message), + QMessageBox::Ok); + } else { + QMessageBox::warning + (this, + tr("Failed to generate layer"), + tr("<b>Layer generation failed</b><p>Failed to generate a derived layer.<p>The layer transform \"%1\" failed.<p>No error information is available.") + .arg(transformName), + QMessageBox::Ok); + } +} + +void +MainWindow::modelGenerationWarning(QString transformName, QString message) +{ + QMessageBox::warning + (this, tr("Warning"), message, QMessageBox::Ok); +} + +void +MainWindow::modelRegenerationFailed(QString layerName, + QString transformName, QString message) +{ + if (message != "") { + + QMessageBox::warning + (this, + tr("Failed to regenerate layer"), + tr("<b>Layer generation failed</b><p>Failed to regenerate derived layer \"%1\" using new data model as input.<p>The layer transform \"%2\" failed:<p>%3") + .arg(layerName).arg(transformName).arg(message), + QMessageBox::Ok); + } else { + QMessageBox::warning + (this, + tr("Failed to regenerate layer"), + tr("<b>Layer generation failed</b><p>Failed to regenerate derived layer \"%1\" using new data model as input.<p>The layer transform \"%2\" failed.<p>No error information is available.") + .arg(layerName).arg(transformName), + QMessageBox::Ok); + } +} + +void +MainWindow::modelRegenerationWarning(QString layerName, + QString transformName, QString message) +{ + QMessageBox::warning + (this, tr("Warning"), tr("<b>Warning when regenerating layer</b><p>When regenerating the derived layer \"%1\" using new data model as input:<p>%2").arg(layerName).arg(message), QMessageBox::Ok); +} + +void +MainWindow::alignmentFailed(QString transformName, QString message) +{ + QMessageBox::warning + (this, + tr("Failed to calculate alignment"), + tr("<b>Alignment calculation failed</b><p>Failed to calculate an audio alignment using transform \"%1\":<p>%2") + .arg(transformName).arg(message), + QMessageBox::Ok); +} + +void +MainWindow::rightButtonMenuRequested(Pane *pane, QPoint position) +{ +// std::cerr << "MainWindow::rightButtonMenuRequested(" << pane << ", " << position.x() << ", " << position.y() << ")" << std::endl; + m_paneStack->setCurrentPane(pane); + m_rightButtonMenu->popup(position); +} + +void +MainWindow::showLayerTree() +{ + if (!m_layerTreeView.isNull()) { + m_layerTreeView->show(); + m_layerTreeView->raise(); + return; + } + + //!!! should use an actual dialog class + + m_layerTreeView = new QTreeView(); + LayerTreeModel *tree = new LayerTreeModel(m_paneStack); + m_layerTreeView->resize(500, 300); //!!! + m_layerTreeView->setModel(tree); + m_layerTreeView->expandAll(); + m_layerTreeView->show(); +} + +void +MainWindow::handleOSCMessage(const OSCMessage &message) +{ + std::cerr << "MainWindow::handleOSCMessage: Not implemented" << std::endl; +} + +void +MainWindow::mouseEnteredWidget() +{ + QWidget *w = dynamic_cast<QWidget *>(sender()); + if (!w) return; + + if (w == m_fader) { + contextHelpChanged(tr("Adjust the master playback level")); + } else if (w == m_playSpeed) { + contextHelpChanged(tr("Adjust the master playback speed")); + } else if (w == m_playSharpen && w->isEnabled()) { + contextHelpChanged(tr("Toggle transient sharpening for playback time scaling")); + } else if (w == m_playMono && w->isEnabled()) { + contextHelpChanged(tr("Toggle mono mode for playback time scaling")); + } +} + +void +MainWindow::mouseLeftWidget() +{ + contextHelpChanged(""); +} + +void +MainWindow::website() +{ + openHelpUrl(tr("http://www.sonicvisualiser.org/")); +} + +void +MainWindow::help() +{ + openHelpUrl(tr("http://www.sonicvisualiser.org/doc/reference/1.0/en/")); +} + +void +MainWindow::about() +{ + bool debug = false; + QString version = "(unknown version)"; + +#ifdef BUILD_DEBUG + debug = true; +#endif + version = tr("Release %1").arg(TONY_VERSION); + + QString aboutText; + + aboutText += tr("<h3>About Tony</h3>"); + aboutText += tr("<p>Tony is a program for interactive note and pitch analysis and annotation.</p>"); + aboutText += tr("<p>%1 : %2 configuration</p>") + .arg(version) + .arg(debug ? tr("Debug") : tr("Release")); + +#ifndef BUILD_STATIC + aboutText += tr("<br>Using Qt v%1 © Trolltech AS").arg(QT_VERSION_STR); +#else +#ifdef QT_SHARED + aboutText += tr("<br>Using Qt v%1 © Trolltech AS").arg(QT_VERSION_STR); +#endif +#endif + +#ifdef BUILD_STATIC + aboutText += tr("<p>Statically linked"); +#ifndef QT_SHARED + aboutText += tr("<br>With Qt (v%1) © Trolltech AS").arg(QT_VERSION_STR); +#endif +#ifdef HAVE_JACK +#ifdef JACK_VERSION + aboutText += tr("<br>With JACK audio output (v%1) © Paul Davis and Jack O'Quin").arg(JACK_VERSION); +#else + aboutText += tr("<br>With JACK audio output © Paul Davis and Jack O'Quin"); +#endif +#endif +#ifdef HAVE_PORTAUDIO + aboutText += tr("<br>With PortAudio audio output © Ross Bencina and Phil Burk"); +#endif +#ifdef HAVE_OGGZ +#ifdef OGGZ_VERSION + aboutText += tr("<br>With Ogg file decoder (oggz v%1, fishsound v%2) © CSIRO Australia").arg(OGGZ_VERSION).arg(FISHSOUND_VERSION); +#else + aboutText += tr("<br>With Ogg file decoder © CSIRO Australia"); +#endif +#endif +#ifdef HAVE_MAD +#ifdef MAD_VERSION + aboutText += tr("<br>With MAD mp3 decoder (v%1) © Underbit Technologies Inc").arg(MAD_VERSION); +#else + aboutText += tr("<br>With MAD mp3 decoder © Underbit Technologies Inc"); +#endif +#endif +#ifdef HAVE_SAMPLERATE +#ifdef SAMPLERATE_VERSION + aboutText += tr("<br>With libsamplerate (v%1) © Erik de Castro Lopo").arg(SAMPLERATE_VERSION); +#else + aboutText += tr("<br>With libsamplerate © Erik de Castro Lopo"); +#endif +#endif +#ifdef HAVE_SNDFILE +#ifdef SNDFILE_VERSION + aboutText += tr("<br>With libsndfile (v%1) © Erik de Castro Lopo").arg(SNDFILE_VERSION); +#else + aboutText += tr("<br>With libsndfile © Erik de Castro Lopo"); +#endif +#endif +#ifdef HAVE_FFTW3F +#ifdef FFTW3_VERSION + aboutText += tr("<br>With FFTW3 (v%1) © Matteo Frigo and MIT").arg(FFTW3_VERSION); +#else + aboutText += tr("<br>With FFTW3 © Matteo Frigo and MIT"); +#endif +#endif +#ifdef HAVE_VAMP + aboutText += tr("<br>With Vamp plugin support (API v%1, host SDK v%2) © Chris Cannam").arg(VAMP_API_VERSION).arg(VAMP_SDK_VERSION); +#endif + aboutText += tr("<br>With LADSPA plugin support (API v%1) © Richard Furse, Paul Davis, Stefan Westerfeld").arg(LADSPA_VERSION); + aboutText += tr("<br>With DSSI plugin support (API v%1) © Chris Cannam, Steve Harris, Sean Bolton").arg(DSSI_VERSION); +#ifdef HAVE_LIBLO +#ifdef LIBLO_VERSION + aboutText += tr("<br>With liblo Lite OSC library (v%1) © Steve Harris").arg(LIBLO_VERSION); +#else + aboutText += tr("<br>With liblo Lite OSC library © Steve Harris").arg(LIBLO_VERSION); +#endif + if (m_oscQueue && m_oscQueue->isOK()) { + aboutText += tr("<p>The OSC URL for this instance is: \"%1\"").arg(m_oscQueue->getOSCURL()); + } +#endif + aboutText += "</p>"; +#endif + + aboutText += + "<p>Copyright © 2005–2012 Chris Cannam and<br>" + "Queen Mary, University of London.</p>" + "<p>This program is free software; you can redistribute it and/or<br>" + "modify it under the terms of the GNU General Public License as<br>" + "published by the Free Software Foundation; either version 2 of the<br>" + "License, or (at your option) any later version.<br>See the file " + "COPYING included with this distribution for more information.</p>"; + + QMessageBox::about(this, tr("About %1").arg(QApplication::applicationName()), aboutText); +} + +void +MainWindow::keyReference() +{ + m_keyReference->show(); +} + +