Mercurial > hg > easaier-soundaccess
changeset 121:5b667a8edafa
(none)
author | ivand_qmul |
---|---|
date | Mon, 22 Oct 2007 13:54:06 +0000 |
parents | 0c99667c07ec |
children | a7e59826d5b0 |
files | sv/main/MainWindow.cpp |
diffstat | 1 files changed, 4907 insertions(+), 4865 deletions(-) [+] |
line wrap: on
line diff
--- a/sv/main/MainWindow.cpp Mon Oct 22 13:47:44 2007 +0000 +++ b/sv/main/MainWindow.cpp Mon Oct 22 13:54:06 2007 +0000 @@ -1,280 +1,280 @@ -/* -*- c-basic-offset: 4 indent-tabs-mode: nil -*- vi:set ts=8 sts=4 sw=4: */ - -/* - Sonic Visualiser - An audio file viewer and annotation editor. - Centre for Digital Music, Queen Mary, University of London. - This file copyright 2006 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 "document/Document.h" -#include "PreferencesDialog.h" - -#include "view/Pane.h" -#include "view/PaneStack.h" -#include "data/model/WaveFileModel.h" -#include "data/model/SparseOneDimensionalModel.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/LayerTree.h" -#include "widgets/ListInputDialog.h" -#include "widgets/SubdividingMenu.h" -#include "widgets/NotifyingPushButton.h" +/* -*- c-basic-offset: 4 indent-tabs-mode: nil -*- vi:set ts=8 sts=4 sw=4: */ + +/* + Sonic Visualiser + An audio file viewer and annotation editor. + Centre for Digital Music, Queen Mary, University of London. + This file copyright 2006 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 "document/Document.h" +#include "PreferencesDialog.h" + +#include "view/Pane.h" +#include "view/PaneStack.h" +#include "data/model/WaveFileModel.h" +#include "data/model/SparseOneDimensionalModel.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/LayerTree.h" +#include "widgets/ListInputDialog.h" +#include "widgets/SubdividingMenu.h" +#include "widgets/NotifyingPushButton.h" #include "widgets/InfoWidget.h" #include "widgets/SearchWidget.h" -#include "widgets/QueryResultsWidget.h" -#include "widgets/ExpandWidget.h" -#include "widgets/AdvancedToolBox.h" -#include "widgets/ConnectionStatus.h" -#include "widgets/LayerViewerWidget.h" - -#include "audioio/AudioCallbackPlaySource.h" -#include "audioio/AudioCallbackPlayTarget.h" -#include "audioio/AudioTargetFactory.h" -#include "audioio/PlaySpeedRangeMapper.h" -#include "data/fileio/AudioFileReaderFactory.h" -#include "data/fileio/DataFileReaderFactory.h" -#include "data/fileio/WavFileWriter.h" -#include "data/fileio/CSVFileWriter.h" -#include "data/fileio/BZipFileDevice.h" -#include "data/fileio/RemoteFile.h" -#include "data/fft/FFTDataServer.h" -#include "base/RecentFiles.h" -#include "transform/TransformFactory.h" -#include "base/PlayParameterRepository.h" -#include "base/XmlExportable.h" -#include "base/CommandHistory.h" -#include "base/Profiler.h" -#include "base/Clipboard.h" -#include "base/TempDirectory.h" -#include "osc/OSCQueue.h" +#include "widgets/QueryResultsWidget.h" +#include "widgets/ExpandWidget.h" +#include "widgets/AdvancedToolBox.h" +#include "widgets/ConnectionStatus.h" +#include "widgets/LayerViewerWidget.h" + +#include "audioio/AudioCallbackPlaySource.h" +#include "audioio/AudioCallbackPlayTarget.h" +#include "audioio/AudioTargetFactory.h" +#include "audioio/PlaySpeedRangeMapper.h" +#include "data/fileio/AudioFileReaderFactory.h" +#include "data/fileio/DataFileReaderFactory.h" +#include "data/fileio/WavFileWriter.h" +#include "data/fileio/CSVFileWriter.h" +#include "data/fileio/BZipFileDevice.h" +#include "data/fileio/RemoteFile.h" +#include "data/fft/FFTDataServer.h" +#include "base/RecentFiles.h" +#include "transform/TransformFactory.h" +#include "base/PlayParameterRepository.h" +#include "base/XmlExportable.h" +#include "base/CommandHistory.h" +#include "base/Profiler.h" +#include "base/Clipboard.h" +#include "base/TempDirectory.h" +#include "osc/OSCQueue.h" #include "main/EasaierSessionManager.h" -#include "widgets/RealTimeFilterPropertyStack.h" -#include "filter/FilterStack.h" -#include "filter/RealTimeFilterFactory.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 <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 <QFileDialog> - -#include <iostream> -#include <cstdio> -#include <errno.h> - -using std::cerr; -using std::endl; - -using std::vector; -using std::map; -using std::set; - +#include "widgets/RealTimeFilterPropertyStack.h" +#include "filter/FilterStack.h" +#include "filter/RealTimeFilterFactory.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 <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 <QFileDialog> + +#include <iostream> +#include <cstdio> +#include <errno.h> + +using std::cerr; +using std::endl; + +using std::vector; +using std::map; +using std::set; + MainWindow * MainWindow::m_instance = 0; MainWindow * MainWindow::instance() { return m_instance; -} - -MainWindow::MainWindow(bool withAudioOutput, bool withOSCSupport) : - m_document(0), - m_paneStack(0), - m_viewManager(0), - m_overview(0), - m_timeRulerLayer(0), - m_audioOutput(withAudioOutput), - m_playSource(0), - m_playTarget(0), - m_oscQueue(withOSCSupport ? new OSCQueue() : 0), - m_recentFiles("RecentFiles", 20), - m_recentTransforms("RecentTransforms", 20), - m_mainMenusCreated(false), - m_paneMenu(0), - m_layerMenu(0), - m_transformsMenu(0), - m_existingLayersMenu(0), - m_sliceMenu(0), - m_recentFilesMenu(0), - m_recentTransformsMenu(0), - m_rightButtonMenu(0), - m_rightButtonLayerMenu(0), - m_rightButtonTransformsMenu(0), - m_documentModified(false), - m_openingAudioFile(false), - m_abandoning(false), - m_preferencesDialog(0) -{ - m_instance = this; - - setWindowTitle(tr("Sound Access")); - - UnitDatabase::getInstance()->registerUnit("Hz"); - UnitDatabase::getInstance()->registerUnit("dB"); - - connect(CommandHistory::getInstance(), SIGNAL(commandExecuted()), - this, SLOT(documentModified())); - connect(CommandHistory::getInstance(), SIGNAL(documentRestored()), - this, SLOT(documentRestored())); - - QFrame *frame = new QFrame; - setCentralWidget(frame); - - QGridLayout *layout = new QGridLayout; - - m_viewManager = new ViewManager(); - connect(m_viewManager, SIGNAL(selectionChanged()), - this, SLOT(updateMenuStates())); - connect(m_viewManager, SIGNAL(inProgressSelectionChanged()), - this, SLOT(inProgressSelectionChanged())); - - m_descriptionLabel = new QLabel; - - m_paneStack = new PaneStack(frame, m_viewManager); - connect(m_paneStack, SIGNAL(currentPaneChanged(Pane *)), - this, SLOT(currentPaneChanged(Pane *))); - connect(m_paneStack, SIGNAL(currentLayerChanged(Pane *, Layer *)), - this, SLOT(currentLayerChanged(Pane *, Layer *))); - connect(m_paneStack, SIGNAL(rightButtonMenuRequested(Pane *, QPoint)), - this, SLOT(rightButtonMenuRequested(Pane *, QPoint))); - connect(m_paneStack, SIGNAL(propertyStacksResized()), - this, SLOT(propertyStacksResized())); - connect(m_paneStack, SIGNAL(contextHelpChanged(const QString &)), - this, SLOT(contextHelpChanged(const QString &))); - - m_overview = new Overview(frame); - m_overview->setViewManager(m_viewManager); - m_overview->setFixedHeight(40); - m_overview->setFrameStyle(QFrame::StyledPanel | QFrame::Sunken); - connect(m_overview, SIGNAL(contextHelpChanged(const QString &)), - this, SLOT(contextHelpChanged(const QString &))); - - m_panLayer = new WaveformLayer; - m_panLayer->setChannelMode(WaveformLayer::MergeChannels); -// m_panLayer->setScale(WaveformLayer::MeterScale); -// m_panLayer->setAutoNormalize(true); - m_panLayer->setBaseColour(Qt::darkGreen); - m_panLayer->setAggressiveCacheing(true); - m_overview->addLayer(m_panLayer); - - m_playSource = new AudioCallbackPlaySource(m_viewManager); - - connect(m_playSource, SIGNAL(sampleRateMismatch(size_t, size_t, bool)), - this, SLOT(sampleRateMismatch(size_t, size_t, bool))); - connect(m_playSource, SIGNAL(audioOverloadPluginDisabled()), - this, SLOT(audioOverloadPluginDisabled())); - - 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())); - - m_playSharpen = new NotifyingPushButton(frame); - m_playSharpen->setToolTip(tr("Sharpen percussive transients")); - m_playSharpen->setFixedSize(20, 20); -// m_playSharpen->setFlat(true); - m_playSharpen->setEnabled(false); - m_playSharpen->setCheckable(true); - m_playSharpen->setChecked(false); - m_playSharpen->setIcon(QIcon(":icons/sharpen.png")); - connect(m_playSharpen, SIGNAL(clicked()), this, SLOT(playSharpenToggled())); - connect(m_playSharpen, SIGNAL(mouseEntered()), this, SLOT(mouseEnteredWidget())); - connect(m_playSharpen, SIGNAL(mouseLeft()), this, SLOT(mouseLeftWidget())); - - m_playMono = new NotifyingPushButton(frame); - m_playMono->setToolTip(tr("Run time stretcher in mono only")); - m_playMono->setFixedSize(20, 20); -// m_playMono->setFlat(true); - m_playMono->setEnabled(false); - m_playMono->setCheckable(true); - m_playMono->setChecked(false); - m_playMono->setIcon(QIcon(":icons/mono.png")); - connect(m_playMono, SIGNAL(clicked()), this, SLOT(playMonoToggled())); - connect(m_playMono, SIGNAL(mouseEntered()), this, SLOT(mouseEnteredWidget())); - connect(m_playMono, SIGNAL(mouseLeft()), this, SLOT(mouseLeftWidget())); - - QSettings settings; - settings.beginGroup("MainWindow"); - m_playSharpen->setChecked(settings.value("playsharpen", true).toBool()); - m_playMono->setChecked(settings.value("playmono", false).toBool()); - settings.endGroup(); - +} + +MainWindow::MainWindow(bool withAudioOutput, bool withOSCSupport) : + m_document(0), + m_paneStack(0), + m_viewManager(0), + m_overview(0), + m_timeRulerLayer(0), + m_audioOutput(withAudioOutput), + m_playSource(0), + m_playTarget(0), + m_oscQueue(withOSCSupport ? new OSCQueue() : 0), + m_recentFiles("RecentFiles", 20), + m_recentTransforms("RecentTransforms", 20), + m_mainMenusCreated(false), + m_paneMenu(0), + m_layerMenu(0), + m_transformsMenu(0), + m_existingLayersMenu(0), + m_sliceMenu(0), + m_recentFilesMenu(0), + m_recentTransformsMenu(0), + m_rightButtonMenu(0), + m_rightButtonLayerMenu(0), + m_rightButtonTransformsMenu(0), + m_documentModified(false), + m_openingAudioFile(false), + m_abandoning(false), + m_preferencesDialog(0) +{ + m_instance = this; + + setWindowTitle(tr("Sound Access")); + + UnitDatabase::getInstance()->registerUnit("Hz"); + UnitDatabase::getInstance()->registerUnit("dB"); + + connect(CommandHistory::getInstance(), SIGNAL(commandExecuted()), + this, SLOT(documentModified())); + connect(CommandHistory::getInstance(), SIGNAL(documentRestored()), + this, SLOT(documentRestored())); + + QFrame *frame = new QFrame; + setCentralWidget(frame); + + QGridLayout *layout = new QGridLayout; + + m_viewManager = new ViewManager(); + connect(m_viewManager, SIGNAL(selectionChanged()), + this, SLOT(updateMenuStates())); + connect(m_viewManager, SIGNAL(inProgressSelectionChanged()), + this, SLOT(inProgressSelectionChanged())); + + m_descriptionLabel = new QLabel; + + m_paneStack = new PaneStack(frame, m_viewManager); + connect(m_paneStack, SIGNAL(currentPaneChanged(Pane *)), + this, SLOT(currentPaneChanged(Pane *))); + connect(m_paneStack, SIGNAL(currentLayerChanged(Pane *, Layer *)), + this, SLOT(currentLayerChanged(Pane *, Layer *))); + connect(m_paneStack, SIGNAL(rightButtonMenuRequested(Pane *, QPoint)), + this, SLOT(rightButtonMenuRequested(Pane *, QPoint))); + connect(m_paneStack, SIGNAL(propertyStacksResized()), + this, SLOT(propertyStacksResized())); + connect(m_paneStack, SIGNAL(contextHelpChanged(const QString &)), + this, SLOT(contextHelpChanged(const QString &))); + + m_overview = new Overview(frame); + m_overview->setViewManager(m_viewManager); + m_overview->setFixedHeight(40); + m_overview->setFrameStyle(QFrame::StyledPanel | QFrame::Sunken); + connect(m_overview, SIGNAL(contextHelpChanged(const QString &)), + this, SLOT(contextHelpChanged(const QString &))); + + m_panLayer = new WaveformLayer; + m_panLayer->setChannelMode(WaveformLayer::MergeChannels); +// m_panLayer->setScale(WaveformLayer::MeterScale); +// m_panLayer->setAutoNormalize(true); + m_panLayer->setBaseColour(Qt::darkGreen); + m_panLayer->setAggressiveCacheing(true); + m_overview->addLayer(m_panLayer); + + m_playSource = new AudioCallbackPlaySource(m_viewManager); + + connect(m_playSource, SIGNAL(sampleRateMismatch(size_t, size_t, bool)), + this, SLOT(sampleRateMismatch(size_t, size_t, bool))); + connect(m_playSource, SIGNAL(audioOverloadPluginDisabled()), + this, SLOT(audioOverloadPluginDisabled())); + + 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())); + + m_playSharpen = new NotifyingPushButton(frame); + m_playSharpen->setToolTip(tr("Sharpen percussive transients")); + m_playSharpen->setFixedSize(20, 20); +// m_playSharpen->setFlat(true); + m_playSharpen->setEnabled(false); + m_playSharpen->setCheckable(true); + m_playSharpen->setChecked(false); + m_playSharpen->setIcon(QIcon(":icons/sharpen.png")); + connect(m_playSharpen, SIGNAL(clicked()), this, SLOT(playSharpenToggled())); + connect(m_playSharpen, SIGNAL(mouseEntered()), this, SLOT(mouseEnteredWidget())); + connect(m_playSharpen, SIGNAL(mouseLeft()), this, SLOT(mouseLeftWidget())); + + m_playMono = new NotifyingPushButton(frame); + m_playMono->setToolTip(tr("Run time stretcher in mono only")); + m_playMono->setFixedSize(20, 20); +// m_playMono->setFlat(true); + m_playMono->setEnabled(false); + m_playMono->setCheckable(true); + m_playMono->setChecked(false); + m_playMono->setIcon(QIcon(":icons/mono.png")); + connect(m_playMono, SIGNAL(clicked()), this, SLOT(playMonoToggled())); + connect(m_playMono, SIGNAL(mouseEntered()), this, SLOT(mouseEnteredWidget())); + connect(m_playMono, SIGNAL(mouseLeft()), this, SLOT(mouseLeftWidget())); + + QSettings settings; + settings.beginGroup("MainWindow"); + m_playSharpen->setChecked(settings.value("playsharpen", true).toBool()); + m_playMono->setChecked(settings.value("playmono", false).toBool()); + settings.endGroup(); + m_infoWidget = new InfoWidget(); m_searchWidget = new SearchWidget(); - m_resultsWidget = new QueryResultsWidget(); - + m_resultsWidget = new QueryResultsWidget(); + m_qtabwidget = new QTabWidget(); m_qtabwidget->addTab(m_searchWidget, tr("Search")); m_qtabwidget->addTab(m_resultsWidget, tr("Result")); m_qtabwidget->addTab(m_infoWidget, tr("Info")); - m_qtabwidget->addTab(new QWidget, tr("Related media")); - + m_qtabwidget->addTab(new QWidget, tr("Related media")); + QGridLayout *auxlayout = new QGridLayout; - auxlayout->addWidget(m_paneStack, 0, 0, 1, 5); - auxlayout->addWidget(m_overview, 1, 0); - auxlayout->addWidget(m_fader, 1, 1); - auxlayout->addWidget(m_playSpeed, 1, 2); - auxlayout->addWidget(m_playSharpen, 1, 3); + auxlayout->addWidget(m_paneStack, 0, 0, 1, 5); + auxlayout->addWidget(m_overview, 1, 0); + auxlayout->addWidget(m_fader, 1, 1); + auxlayout->addWidget(m_playSpeed, 1, 2); + auxlayout->addWidget(m_playSharpen, 1, 3); auxlayout->addWidget(m_playMono, 1, 4); QWidget *auxwidget = new QWidget; auxwidget->setLayout(auxlayout); @@ -312,323 +312,334 @@ m_rightExpandWidget->setPanes(m_leftExpandWidget, widgetAuxForLogo); m_rightExpandWidget->setPercentage( 3, 1); - layout->addWidget(m_rightExpandWidget, 0, 0); - - m_paneStack->setPropertyStackMinWidth - (m_fader->width() + m_playSpeed->width() + m_playSharpen->width() + - m_playMono->width() + layout->spacing() * 4); - - layout->setColumnStretch(0, 10); - - frame->setLayout(layout); - - connect(m_viewManager, SIGNAL(outputLevelsChanged(float, float)), - this, SLOT(outputLevelsChanged(float, float))); - - connect(m_viewManager, SIGNAL(playbackFrameChanged(unsigned long)), - this, SLOT(playbackFrameChanged(unsigned long))); - - connect(m_viewManager, SIGNAL(globalCentreFrameChanged(unsigned long)), - this, SLOT(globalCentreFrameChanged(unsigned long))); - - connect(m_viewManager, SIGNAL(viewCentreFrameChanged(View *, unsigned long)), - this, SLOT(viewCentreFrameChanged(View *, unsigned long))); - - connect(m_viewManager, SIGNAL(viewZoomLevelChanged(View *, unsigned long, bool)), - this, SLOT(viewZoomLevelChanged(View *, unsigned long, bool))); - - connect(Preferences::getInstance(), - SIGNAL(propertyChanged(PropertyContainer::PropertyName)), - this, - SLOT(preferenceChanged(PropertyContainer::PropertyName))); - -// preferenceChanged("Property Box Layout"); - - if (m_oscQueue && m_oscQueue->isOK()) { - connect(m_oscQueue, SIGNAL(messagesAvailable()), this, SLOT(pollOSC())); - QTimer *oscTimer = new QTimer(this); - connect(oscTimer, SIGNAL(timeout()), this, SLOT(pollOSC())); - oscTimer->start(1000); - } - - m_httpClient = new HttpClient(TempDirectory::getInstance()->getConfigPath()); + layout->addWidget(m_rightExpandWidget, 0, 0); + + m_paneStack->setPropertyStackMinWidth + (m_fader->width() + m_playSpeed->width() + m_playSharpen->width() + + m_playMono->width() + layout->spacing() * 4); + + layout->setColumnStretch(0, 10); + + frame->setLayout(layout); + + connect(m_viewManager, SIGNAL(outputLevelsChanged(float, float)), + this, SLOT(outputLevelsChanged(float, float))); + + connect(m_viewManager, SIGNAL(playbackFrameChanged(unsigned long)), + this, SLOT(playbackFrameChanged(unsigned long))); + + connect(m_viewManager, SIGNAL(globalCentreFrameChanged(unsigned long)), + this, SLOT(globalCentreFrameChanged(unsigned long))); + + connect(m_viewManager, SIGNAL(viewCentreFrameChanged(View *, unsigned long)), + this, SLOT(viewCentreFrameChanged(View *, unsigned long))); + + connect(m_viewManager, SIGNAL(viewZoomLevelChanged(View *, unsigned long, bool)), + this, SLOT(viewZoomLevelChanged(View *, unsigned long, bool))); + + connect(Preferences::getInstance(), + SIGNAL(propertyChanged(PropertyContainer::PropertyName)), + this, + SLOT(preferenceChanged(PropertyContainer::PropertyName))); + +// preferenceChanged("Property Box Layout"); + + if (m_oscQueue && m_oscQueue->isOK()) { + connect(m_oscQueue, SIGNAL(messagesAvailable()), this, SLOT(pollOSC())); + QTimer *oscTimer = new QTimer(this); + connect(oscTimer, SIGNAL(timeout()), this, SLOT(pollOSC())); + oscTimer->start(1000); + } + + m_httpClient = new HttpClient(TempDirectory::getInstance()->getConfigPath()); m_EasaierManager = new EasaierSessionManager(m_httpClient); connect(m_EasaierManager, SIGNAL(queryModelLoaded(QueryModel*)), - this, SLOT(queryModelLoaded(QueryModel*))); - - setupMenus(); - setupToolbars(); - - statusBar(); - - newSession(); - -} - -MainWindow::~MainWindow() -{ -// std::cerr << "MainWindow::~MainWindow()" << std::endl; - - if (!m_abandoning) { - closeSession(); - } + this, SLOT(queryModelLoaded(QueryModel*))); + + setupMenus(); + setupToolbars(); + + statusBar(); + + newSession(); + +} + +MainWindow::~MainWindow() +{ +// std::cerr << "MainWindow::~MainWindow()" << std::endl; + + if (!m_abandoning) { + closeSession(); + } delete m_EasaierManager; - delete m_httpClient; - delete m_playTarget; - delete m_playSource; - delete m_viewManager; - delete m_oscQueue; - Profiles::getInstance()->dump(); - - m_instance = 0; -} - -QString -MainWindow::getOpenFileName(FileFinder::FileType type) -{ - FileFinder *ff = FileFinder::getInstance(); - switch (type) { - case FileFinder::SessionFile: - return ff->getOpenFileName(type, m_sessionFile); - case FileFinder::AudioFile: - return ff->getOpenFileName(type, m_audioFile); - case FileFinder::LayerFile: - return ff->getOpenFileName(type, m_sessionFile); - case FileFinder::SessionOrAudioFile: - return ff->getOpenFileName(type, m_sessionFile); - case FileFinder::ImageFile: - return ff->getOpenFileName(type, m_sessionFile); - case FileFinder::AnyFile: - if (getMainModel() != 0 && - m_paneStack != 0 && - m_paneStack->getCurrentPane() != 0) { // can import a layer - return ff->getOpenFileName(FileFinder::AnyFile, m_sessionFile); - } else { - return ff->getOpenFileName(FileFinder::SessionOrAudioFile, - m_sessionFile); - } - } - return ""; -} - -QString -MainWindow::getSaveFileName(FileFinder::FileType type) -{ - FileFinder *ff = FileFinder::getInstance(); - switch (type) { - case FileFinder::SessionFile: - return ff->getSaveFileName(type, m_sessionFile); - case FileFinder::AudioFile: - return ff->getSaveFileName(type, m_audioFile); - case FileFinder::LayerFile: - return ff->getSaveFileName(type, m_sessionFile); - case FileFinder::SessionOrAudioFile: - return ff->getSaveFileName(type, m_sessionFile); - case FileFinder::ImageFile: - return ff->getSaveFileName(type, m_sessionFile); - case FileFinder::AnyFile: - return ff->getSaveFileName(type, m_sessionFile); - } - return ""; -} - -void -MainWindow::registerLastOpenedFilePath(FileFinder::FileType type, QString path) -{ - FileFinder *ff = FileFinder::getInstance(); - ff->registerLastOpenedFilePath(type, path); -} - -void -MainWindow::setupMenus() -{ - if (!m_mainMenusCreated) { - m_rightButtonMenu = new QMenu(); - - // No -- we don't want tear-off enabled on the right-button - // menu. If it is enabled, then simply right-clicking and - // releasing will pop up the menu, activate the tear-off, and - // leave the torn-off menu window in front of the main window. - // That isn't desirable. I'm not sure it ever would be, in a - // context menu -- perhaps technically a Qt bug? -// m_rightButtonMenu->setTearOffEnabled(true); - } - - if (m_rightButtonLayerMenu) { - m_rightButtonLayerMenu->clear(); - } else { - m_rightButtonLayerMenu = m_rightButtonMenu->addMenu(tr("&Layer")); - m_rightButtonLayerMenu->setTearOffEnabled(true); - m_rightButtonMenu->addSeparator(); - } - - if (m_rightButtonTransformsMenu) { - m_rightButtonTransformsMenu->clear(); - } else { - m_rightButtonTransformsMenu = m_rightButtonMenu->addMenu(tr("&Transform")); - m_rightButtonTransformsMenu->setTearOffEnabled(true); - m_rightButtonMenu->addSeparator(); - } - - if (!m_mainMenusCreated) { - CommandHistory::getInstance()->registerMenu(m_rightButtonMenu); - m_rightButtonMenu->addSeparator(); - } - - setupFileMenu(); - setupEasaierMenu(); - setupEditMenu(); - setupViewMenu(); - setupPaneAndLayerMenus(); - setupTransformsMenu(); - setupFiltersMenu(); - setupSettingMenu(); - setupHelpMenu(); - - 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")); - - QIcon icon(":icons/filenew.png"); - icon.addFile(":icons/filenew-22.png"); - QAction *action = new QAction(icon, tr("&New Session"), this); - action->setShortcut(tr("Ctrl+N")); - action->setStatusTip(tr("Abandon the current Sonic Visualiser session and start a new one")); - connect(action, SIGNAL(triggered()), this, SLOT(newSession())); - menu->addAction(action); - //toolbar->addAction(action); - - icon = QIcon(":icons/fileopensession.png"); - action = new QAction(icon, tr("&Open Session..."), this); - action->setShortcut(tr("Ctrl+O")); - action->setStatusTip(tr("Open a previously saved Sonic Visualiser session file")); - connect(action, SIGNAL(triggered()), this, SLOT(openSession())); - menu->addAction(action); - - icon = QIcon(":icons/fileopen.png"); - icon.addFile(":icons/fileopen-22.png"); - - action = new QAction(icon, tr("&Open..."), this); - action->setStatusTip(tr("Open a session file, audio file, or layer")); - connect(action, SIGNAL(triggered()), this, SLOT(openSomething())); - //toolbar->addAction(action); - - icon = QIcon(":icons/filesave.png"); - icon.addFile(":icons/filesave-22.png"); - action = new QAction(icon, tr("&Save Session"), this); - action->setShortcut(tr("Ctrl+S")); - action->setStatusTip(tr("Save the current session into a Sonic Visualiser session file")); - connect(action, SIGNAL(triggered()), this, SLOT(saveSession())); - connect(this, SIGNAL(canSave(bool)), action, SLOT(setEnabled(bool))); - menu->addAction(action); - //toolbar->addAction(action); - - icon = QIcon(":icons/filesaveas.png"); - icon.addFile(":icons/filesaveas-22.png"); - action = new QAction(icon, tr("Save Session &As..."), this); - action->setStatusTip(tr("Save the current session into a new Sonic Visualiser session file")); - connect(action, SIGNAL(triggered()), this, SLOT(saveSessionAs())); - menu->addAction(action); - //toolbar->addAction(action); - - menu->addSeparator(); - - icon = QIcon(":icons/fileopenaudio.png"); - action = new QAction(icon, tr("&Import Audio File..."), this); - action->setShortcut(tr("Ctrl+I")); - action->setStatusTip(tr("Import an existing audio file")); - connect(action, SIGNAL(triggered()), this, SLOT(importAudio())); - menu->addAction(action); - - action = new QAction(tr("Import Secondary Audio File..."), this); - action->setShortcut(tr("Ctrl+Shift+I")); - action->setStatusTip(tr("Import an extra audio file as a separate layer")); - connect(action, SIGNAL(triggered()), this, SLOT(importMoreAudio())); - connect(this, SIGNAL(canImportMoreAudio(bool)), action, SLOT(setEnabled(bool))); - menu->addAction(action); - - action = new QAction(tr("&Export Audio File..."), this); - action->setStatusTip(tr("Export selection as an audio file")); - connect(action, SIGNAL(triggered()), this, SLOT(exportAudio())); - connect(this, SIGNAL(canExportAudio(bool)), action, SLOT(setEnabled(bool))); - menu->addAction(action); - - menu->addSeparator(); - - action = new QAction(tr("Import Annotation &Layer..."), this); - action->setShortcut(tr("Ctrl+L")); - action->setStatusTip(tr("Import layer data from an existing file")); - connect(action, SIGNAL(triggered()), this, SLOT(importLayer())); - connect(this, SIGNAL(canImportLayer(bool)), action, SLOT(setEnabled(bool))); - menu->addAction(action); - - action = new QAction(tr("Export Annotation Layer..."), this); - action->setStatusTip(tr("Export layer data to a file")); - connect(action, SIGNAL(triggered()), this, SLOT(exportLayer())); - connect(this, SIGNAL(canExportLayer(bool)), action, SLOT(setEnabled(bool))); - menu->addAction(action); - - menu->addSeparator(); - - action = new QAction(tr("Export Image File..."), this); - action->setStatusTip(tr("Export a single pane to an image file")); - connect(action, SIGNAL(triggered()), this, SLOT(exportImage())); - connect(this, SIGNAL(canExportImage(bool)), action, SLOT(setEnabled(bool))); - menu->addAction(action); - - menu->addSeparator(); - - action = new QAction(tr("Open Lo&cation..."), this); - action->setShortcut(tr("Ctrl+Shift+O")); - action->setStatusTip(tr("Open or import a file from a remote URL")); - connect(action, SIGNAL(triggered()), this, SLOT(openLocation())); - menu->addAction(action); - - menu->addSeparator(); - - m_recentFilesMenu = menu->addMenu(tr("&Recent Files")); - m_recentFilesMenu->setTearOffEnabled(true); - setupRecentFilesMenu(); - connect(&m_recentFiles, SIGNAL(recentChanged()), - this, SLOT(setupRecentFilesMenu())); - - menu->addSeparator(); - action = new QAction(tr("&Preferences..."), this); - action->setStatusTip(tr("Adjust the application preferences")); - connect(action, SIGNAL(triggered()), this, SLOT(preferences())); - menu->addAction(action); - - /*!!! - menu->addSeparator(); - - action = new QAction(tr("Play / Pause"), this); - action->setShortcut(tr("Space")); - action->setStatusTip(tr("Start or stop playback from the current position")); - connect(action, SIGNAL(triggered()), this, SLOT(play())); - menu->addAction(action); - */ - - menu->addSeparator(); - action = new QAction(QIcon(":/icons/exit.png"), - tr("&Quit"), this); - action->setShortcut(tr("Ctrl+Q")); - action->setStatusTip(tr("Exit Sonic Visualiser")); - connect(action, SIGNAL(triggered()), this, SLOT(exit())); - menu->addAction(action); -} - -void -MainWindow::setupEasaierMenu() -{ + delete m_httpClient; + delete m_playTarget; + delete m_playSource; + delete m_viewManager; + delete m_oscQueue; + Profiles::getInstance()->dump(); + + m_instance = 0; +} + +QString +MainWindow::getOpenFileName(FileFinder::FileType type) +{ + FileFinder *ff = FileFinder::getInstance(); + switch (type) { + case FileFinder::SessionFile: + return ff->getOpenFileName(type, m_sessionFile); + case FileFinder::AudioFile: + return ff->getOpenFileName(type, m_audioFile); +// Ivan Damnjanovic 09-10/2007 added video file import + case FileFinder::VideoFile: + return ff->getOpenFileName(type, m_videoFile); +// + case FileFinder::LayerFile: + return ff->getOpenFileName(type, m_sessionFile); + case FileFinder::SessionOrAudioFile: + return ff->getOpenFileName(type, m_sessionFile); + case FileFinder::ImageFile: + return ff->getOpenFileName(type, m_sessionFile); + case FileFinder::AnyFile: + if (getMainModel() != 0 && + m_paneStack != 0 && + m_paneStack->getCurrentPane() != 0) { // can import a layer + return ff->getOpenFileName(FileFinder::AnyFile, m_sessionFile); + } else { + return ff->getOpenFileName(FileFinder::SessionOrAudioFile, + m_sessionFile); + } + } + return ""; +} + +QString +MainWindow::getSaveFileName(FileFinder::FileType type) +{ + FileFinder *ff = FileFinder::getInstance(); + switch (type) { + case FileFinder::SessionFile: + return ff->getSaveFileName(type, m_sessionFile); + case FileFinder::AudioFile: + return ff->getSaveFileName(type, m_audioFile); + case FileFinder::LayerFile: + return ff->getSaveFileName(type, m_sessionFile); + case FileFinder::SessionOrAudioFile: + return ff->getSaveFileName(type, m_sessionFile); + case FileFinder::ImageFile: + return ff->getSaveFileName(type, m_sessionFile); + case FileFinder::AnyFile: + return ff->getSaveFileName(type, m_sessionFile); + } + return ""; +} + +void +MainWindow::registerLastOpenedFilePath(FileFinder::FileType type, QString path) +{ + FileFinder *ff = FileFinder::getInstance(); + ff->registerLastOpenedFilePath(type, path); +} + +void +MainWindow::setupMenus() +{ + if (!m_mainMenusCreated) { + m_rightButtonMenu = new QMenu(); + + // No -- we don't want tear-off enabled on the right-button + // menu. If it is enabled, then simply right-clicking and + // releasing will pop up the menu, activate the tear-off, and + // leave the torn-off menu window in front of the main window. + // That isn't desirable. I'm not sure it ever would be, in a + // context menu -- perhaps technically a Qt bug? +// m_rightButtonMenu->setTearOffEnabled(true); + } + + if (m_rightButtonLayerMenu) { + m_rightButtonLayerMenu->clear(); + } else { + m_rightButtonLayerMenu = m_rightButtonMenu->addMenu(tr("&Layer")); + m_rightButtonLayerMenu->setTearOffEnabled(true); + m_rightButtonMenu->addSeparator(); + } + + if (m_rightButtonTransformsMenu) { + m_rightButtonTransformsMenu->clear(); + } else { + m_rightButtonTransformsMenu = m_rightButtonMenu->addMenu(tr("&Transform")); + m_rightButtonTransformsMenu->setTearOffEnabled(true); + m_rightButtonMenu->addSeparator(); + } + + if (!m_mainMenusCreated) { + CommandHistory::getInstance()->registerMenu(m_rightButtonMenu); + m_rightButtonMenu->addSeparator(); + } + + setupFileMenu(); + setupEasaierMenu(); + setupEditMenu(); + setupViewMenu(); + setupPaneAndLayerMenus(); + setupTransformsMenu(); + setupFiltersMenu(); + setupSettingMenu(); + setupHelpMenu(); + + 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")); + + QIcon icon(":icons/filenew.png"); + icon.addFile(":icons/filenew-22.png"); + QAction *action = new QAction(icon, tr("&New Session"), this); + action->setShortcut(tr("Ctrl+N")); + action->setStatusTip(tr("Abandon the current Sonic Visualiser session and start a new one")); + connect(action, SIGNAL(triggered()), this, SLOT(newSession())); + menu->addAction(action); + //toolbar->addAction(action); + + icon = QIcon(":icons/fileopensession.png"); + action = new QAction(icon, tr("&Open Session..."), this); + action->setShortcut(tr("Ctrl+O")); + action->setStatusTip(tr("Open a previously saved Sonic Visualiser session file")); + connect(action, SIGNAL(triggered()), this, SLOT(openSession())); + menu->addAction(action); + + icon = QIcon(":icons/fileopen.png"); + icon.addFile(":icons/fileopen-22.png"); + + action = new QAction(icon, tr("&Open..."), this); + action->setStatusTip(tr("Open a session file, audio file, or layer")); + connect(action, SIGNAL(triggered()), this, SLOT(openSomething())); + //toolbar->addAction(action); + + icon = QIcon(":icons/filesave.png"); + icon.addFile(":icons/filesave-22.png"); + action = new QAction(icon, tr("&Save Session"), this); + action->setShortcut(tr("Ctrl+S")); + action->setStatusTip(tr("Save the current session into a Sonic Visualiser session file")); + connect(action, SIGNAL(triggered()), this, SLOT(saveSession())); + connect(this, SIGNAL(canSave(bool)), action, SLOT(setEnabled(bool))); + menu->addAction(action); + //toolbar->addAction(action); + + icon = QIcon(":icons/filesaveas.png"); + icon.addFile(":icons/filesaveas-22.png"); + action = new QAction(icon, tr("Save Session &As..."), this); + action->setStatusTip(tr("Save the current session into a new Sonic Visualiser session file")); + connect(action, SIGNAL(triggered()), this, SLOT(saveSessionAs())); + menu->addAction(action); + //toolbar->addAction(action); + + menu->addSeparator(); + + icon = QIcon(":icons/fileopenaudio.png"); + action = new QAction(icon, tr("&Import Audio File..."), this); + action->setShortcut(tr("Ctrl+I")); + action->setStatusTip(tr("Import an existing audio file")); + connect(action, SIGNAL(triggered()), this, SLOT(importAudio())); + menu->addAction(action); + + action = new QAction(tr("Import Secondary Audio File..."), this); + action->setShortcut(tr("Ctrl+Shift+I")); + action->setStatusTip(tr("Import an extra audio file as a separate layer")); + connect(action, SIGNAL(triggered()), this, SLOT(importMoreAudio())); + connect(this, SIGNAL(canImportMoreAudio(bool)), action, SLOT(setEnabled(bool))); + menu->addAction(action); + + action = new QAction(tr("&Export Audio File..."), this); + action->setStatusTip(tr("Export selection as an audio file")); + connect(action, SIGNAL(triggered()), this, SLOT(exportAudio())); + connect(this, SIGNAL(canExportAudio(bool)), action, SLOT(setEnabled(bool))); + menu->addAction(action); +// Ivan Damnjanovic 09-10/2007 added video file import + menu->addSeparator(); + action = new QAction(tr("&Import Video File..."), this); + action->setShortcut(tr("Ctrl+Alt+I")); + action->setStatusTip(tr("Import an existing video file")); + connect(action, SIGNAL(triggered()), this, SLOT(importVideo())); + menu->addAction(action); +// + menu->addSeparator(); + + action = new QAction(tr("Import Annotation &Layer..."), this); + action->setShortcut(tr("Ctrl+L")); + action->setStatusTip(tr("Import layer data from an existing file")); + connect(action, SIGNAL(triggered()), this, SLOT(importLayer())); + connect(this, SIGNAL(canImportLayer(bool)), action, SLOT(setEnabled(bool))); + menu->addAction(action); + + action = new QAction(tr("Export Annotation Layer..."), this); + action->setStatusTip(tr("Export layer data to a file")); + connect(action, SIGNAL(triggered()), this, SLOT(exportLayer())); + connect(this, SIGNAL(canExportLayer(bool)), action, SLOT(setEnabled(bool))); + menu->addAction(action); + + menu->addSeparator(); + + action = new QAction(tr("Export Image File..."), this); + action->setStatusTip(tr("Export a single pane to an image file")); + connect(action, SIGNAL(triggered()), this, SLOT(exportImage())); + connect(this, SIGNAL(canExportImage(bool)), action, SLOT(setEnabled(bool))); + menu->addAction(action); + + menu->addSeparator(); + + action = new QAction(tr("Open Lo&cation..."), this); + action->setShortcut(tr("Ctrl+Shift+O")); + action->setStatusTip(tr("Open or import a file from a remote URL")); + connect(action, SIGNAL(triggered()), this, SLOT(openLocation())); + menu->addAction(action); + + menu->addSeparator(); + + m_recentFilesMenu = menu->addMenu(tr("&Recent Files")); + m_recentFilesMenu->setTearOffEnabled(true); + setupRecentFilesMenu(); + connect(&m_recentFiles, SIGNAL(recentChanged()), + this, SLOT(setupRecentFilesMenu())); + + menu->addSeparator(); + action = new QAction(tr("&Preferences..."), this); + action->setStatusTip(tr("Adjust the application preferences")); + connect(action, SIGNAL(triggered()), this, SLOT(preferences())); + menu->addAction(action); + + /*!!! + menu->addSeparator(); + + action = new QAction(tr("Play / Pause"), this); + action->setShortcut(tr("Space")); + action->setStatusTip(tr("Start or stop playback from the current position")); + connect(action, SIGNAL(triggered()), this, SLOT(play())); + menu->addAction(action); + */ + + menu->addSeparator(); + action = new QAction(QIcon(":/icons/exit.png"), + tr("&Quit"), this); + action->setShortcut(tr("Ctrl+Q")); + action->setStatusTip(tr("Exit Sonic Visualiser")); + connect(action, SIGNAL(triggered()), this, SLOT(exit())); + menu->addAction(action); +} + +void +MainWindow::setupEasaierMenu() +{ if (m_mainMenusCreated) return; QMenu* menu = menuBar()->addMenu(tr("&Easaier")); @@ -674,125 +685,125 @@ connect(action, SIGNAL(triggered()), this, SLOT(exit())); menu->addAction(action); -} - -void -MainWindow::setupEditMenu() -{ - if (m_mainMenusCreated) return; - - QMenu *menu = menuBar()->addMenu(tr("&Edit")); - menu->setTearOffEnabled(true); - CommandHistory::getInstance()->registerMenu(menu); - - menu->addSeparator(); - - QAction *action = new QAction(QIcon(":/icons/editcut.png"), - tr("Cu&t"), this); - action->setShortcut(tr("Ctrl+X")); - action->setStatusTip(tr("Cut the selection from the current layer to the clipboard")); - connect(action, SIGNAL(triggered()), this, SLOT(cut())); - connect(this, SIGNAL(canEditSelection(bool)), action, SLOT(setEnabled(bool))); - menu->addAction(action); - m_rightButtonMenu->addAction(action); - - action = new QAction(QIcon(":/icons/editcopy.png"), - tr("&Copy"), this); - action->setShortcut(tr("Ctrl+C")); - action->setStatusTip(tr("Copy the selection from the current layer to the clipboard")); - connect(action, SIGNAL(triggered()), this, SLOT(copy())); - connect(this, SIGNAL(canEditSelection(bool)), action, SLOT(setEnabled(bool))); - menu->addAction(action); - m_rightButtonMenu->addAction(action); - - action = new QAction(QIcon(":/icons/editpaste.png"), - tr("&Paste"), this); - action->setShortcut(tr("Ctrl+V")); - action->setStatusTip(tr("Paste from the clipboard to the current layer")); - connect(action, SIGNAL(triggered()), this, SLOT(paste())); - connect(this, SIGNAL(canPaste(bool)), action, SLOT(setEnabled(bool))); - menu->addAction(action); - m_rightButtonMenu->addAction(action); - - action = new QAction(tr("&Delete Selected Items"), this); - action->setShortcut(tr("Del")); - action->setStatusTip(tr("Delete the selection from the current layer")); - connect(action, SIGNAL(triggered()), this, SLOT(deleteSelected())); - connect(this, SIGNAL(canEditSelection(bool)), action, SLOT(setEnabled(bool))); - menu->addAction(action); - m_rightButtonMenu->addAction(action); - - menu->addSeparator(); - m_rightButtonMenu->addSeparator(); - - action = new QAction(tr("Select &All"), this); - action->setShortcut(tr("Ctrl+A")); - action->setStatusTip(tr("Select the whole duration of the current session")); - connect(action, SIGNAL(triggered()), this, SLOT(selectAll())); - connect(this, SIGNAL(canSelect(bool)), action, SLOT(setEnabled(bool))); - menu->addAction(action); - m_rightButtonMenu->addAction(action); - - action = new QAction(tr("Select &Visible Range"), this); - action->setShortcut(tr("Ctrl+Shift+A")); - action->setStatusTip(tr("Select the time range corresponding to the current window width")); - connect(action, SIGNAL(triggered()), this, SLOT(selectVisible())); - connect(this, SIGNAL(canSelect(bool)), action, SLOT(setEnabled(bool))); - menu->addAction(action); - - action = new QAction(tr("Select to &Start"), this); - action->setShortcut(tr("Shift+Left")); - action->setStatusTip(tr("Select from the start of the session to the current playback position")); - connect(action, SIGNAL(triggered()), this, SLOT(selectToStart())); - connect(this, SIGNAL(canSelect(bool)), action, SLOT(setEnabled(bool))); - menu->addAction(action); - - action = new QAction(tr("Select to &End"), this); - action->setShortcut(tr("Shift+Right")); - action->setStatusTip(tr("Select from the current playback position to the end of the session")); - connect(action, SIGNAL(triggered()), this, SLOT(selectToEnd())); - connect(this, SIGNAL(canSelect(bool)), action, SLOT(setEnabled(bool))); - menu->addAction(action); - - action = new QAction(tr("C&lear Selection"), this); - action->setShortcut(tr("Esc")); - action->setStatusTip(tr("Clear the selection")); - connect(action, SIGNAL(triggered()), this, SLOT(clearSelection())); - connect(this, SIGNAL(canClearSelection(bool)), action, SLOT(setEnabled(bool))); - menu->addAction(action); - m_rightButtonMenu->addAction(action); - - menu->addSeparator(); - - action = new QAction(tr("&Insert Instant at Playback Position"), this); - action->setShortcut(tr("Enter")); - action->setStatusTip(tr("Insert a new time instant at the current playback position, in a new layer if necessary")); - connect(action, SIGNAL(triggered()), this, SLOT(insertInstant())); - connect(this, SIGNAL(canInsertInstant(bool)), action, SLOT(setEnabled(bool))); - menu->addAction(action); - - action = new QAction(tr("Insert Instants at Selection &Boundaries"), this); - action->setShortcut(tr("Shift+Enter")); - action->setStatusTip(tr("Insert new time instants at the start and end of the current selection, in a new layer if necessary")); - connect(action, SIGNAL(triggered()), this, SLOT(insertInstantsAtBoundaries())); - connect(this, SIGNAL(canInsertInstantsAtBoundaries(bool)), action, SLOT(setEnabled(bool))); - menu->addAction(action); - - // Laptop shortcut (no keypad Enter key) - connect(new QShortcut(tr(";"), this), SIGNAL(activated()), - this, SLOT(insertInstant())); -} - -void -MainWindow::setupSettingMenu() -{ - if (m_mainMenusCreated) return; - - QAction *action = 0; - QMenu *menu = 0; - - menu = menuBar()->addMenu(tr("&Settings")); - menu->setTearOffEnabled(true); +} + +void +MainWindow::setupEditMenu() +{ + if (m_mainMenusCreated) return; + + QMenu *menu = menuBar()->addMenu(tr("&Edit")); + menu->setTearOffEnabled(true); + CommandHistory::getInstance()->registerMenu(menu); + + menu->addSeparator(); + + QAction *action = new QAction(QIcon(":/icons/editcut.png"), + tr("Cu&t"), this); + action->setShortcut(tr("Ctrl+X")); + action->setStatusTip(tr("Cut the selection from the current layer to the clipboard")); + connect(action, SIGNAL(triggered()), this, SLOT(cut())); + connect(this, SIGNAL(canEditSelection(bool)), action, SLOT(setEnabled(bool))); + menu->addAction(action); + m_rightButtonMenu->addAction(action); + + action = new QAction(QIcon(":/icons/editcopy.png"), + tr("&Copy"), this); + action->setShortcut(tr("Ctrl+C")); + action->setStatusTip(tr("Copy the selection from the current layer to the clipboard")); + connect(action, SIGNAL(triggered()), this, SLOT(copy())); + connect(this, SIGNAL(canEditSelection(bool)), action, SLOT(setEnabled(bool))); + menu->addAction(action); + m_rightButtonMenu->addAction(action); + + action = new QAction(QIcon(":/icons/editpaste.png"), + tr("&Paste"), this); + action->setShortcut(tr("Ctrl+V")); + action->setStatusTip(tr("Paste from the clipboard to the current layer")); + connect(action, SIGNAL(triggered()), this, SLOT(paste())); + connect(this, SIGNAL(canPaste(bool)), action, SLOT(setEnabled(bool))); + menu->addAction(action); + m_rightButtonMenu->addAction(action); + + action = new QAction(tr("&Delete Selected Items"), this); + action->setShortcut(tr("Del")); + action->setStatusTip(tr("Delete the selection from the current layer")); + connect(action, SIGNAL(triggered()), this, SLOT(deleteSelected())); + connect(this, SIGNAL(canEditSelection(bool)), action, SLOT(setEnabled(bool))); + menu->addAction(action); + m_rightButtonMenu->addAction(action); + + menu->addSeparator(); + m_rightButtonMenu->addSeparator(); + + action = new QAction(tr("Select &All"), this); + action->setShortcut(tr("Ctrl+A")); + action->setStatusTip(tr("Select the whole duration of the current session")); + connect(action, SIGNAL(triggered()), this, SLOT(selectAll())); + connect(this, SIGNAL(canSelect(bool)), action, SLOT(setEnabled(bool))); + menu->addAction(action); + m_rightButtonMenu->addAction(action); + + action = new QAction(tr("Select &Visible Range"), this); + action->setShortcut(tr("Ctrl+Shift+A")); + action->setStatusTip(tr("Select the time range corresponding to the current window width")); + connect(action, SIGNAL(triggered()), this, SLOT(selectVisible())); + connect(this, SIGNAL(canSelect(bool)), action, SLOT(setEnabled(bool))); + menu->addAction(action); + + action = new QAction(tr("Select to &Start"), this); + action->setShortcut(tr("Shift+Left")); + action->setStatusTip(tr("Select from the start of the session to the current playback position")); + connect(action, SIGNAL(triggered()), this, SLOT(selectToStart())); + connect(this, SIGNAL(canSelect(bool)), action, SLOT(setEnabled(bool))); + menu->addAction(action); + + action = new QAction(tr("Select to &End"), this); + action->setShortcut(tr("Shift+Right")); + action->setStatusTip(tr("Select from the current playback position to the end of the session")); + connect(action, SIGNAL(triggered()), this, SLOT(selectToEnd())); + connect(this, SIGNAL(canSelect(bool)), action, SLOT(setEnabled(bool))); + menu->addAction(action); + + action = new QAction(tr("C&lear Selection"), this); + action->setShortcut(tr("Esc")); + action->setStatusTip(tr("Clear the selection")); + connect(action, SIGNAL(triggered()), this, SLOT(clearSelection())); + connect(this, SIGNAL(canClearSelection(bool)), action, SLOT(setEnabled(bool))); + menu->addAction(action); + m_rightButtonMenu->addAction(action); + + menu->addSeparator(); + + action = new QAction(tr("&Insert Instant at Playback Position"), this); + action->setShortcut(tr("Enter")); + action->setStatusTip(tr("Insert a new time instant at the current playback position, in a new layer if necessary")); + connect(action, SIGNAL(triggered()), this, SLOT(insertInstant())); + connect(this, SIGNAL(canInsertInstant(bool)), action, SLOT(setEnabled(bool))); + menu->addAction(action); + + action = new QAction(tr("Insert Instants at Selection &Boundaries"), this); + action->setShortcut(tr("Shift+Enter")); + action->setStatusTip(tr("Insert new time instants at the start and end of the current selection, in a new layer if necessary")); + connect(action, SIGNAL(triggered()), this, SLOT(insertInstantsAtBoundaries())); + connect(this, SIGNAL(canInsertInstantsAtBoundaries(bool)), action, SLOT(setEnabled(bool))); + menu->addAction(action); + + // Laptop shortcut (no keypad Enter key) + connect(new QShortcut(tr(";"), this), SIGNAL(activated()), + this, SLOT(insertInstant())); +} + +void +MainWindow::setupSettingMenu() +{ + if (m_mainMenusCreated) return; + + QAction *action = 0; + QMenu *menu = 0; + + menu = menuBar()->addMenu(tr("&Settings")); + menu->setTearOffEnabled(true); action = new QAction(tr("&Connection settings"), this); connect(action, SIGNAL(triggered()), this, SLOT(connectionSettings())); @@ -800,2290 +811,2311 @@ action = new QAction(tr("&Style setting"), this); connect(action, SIGNAL(triggered()), this, SLOT(styleSetting())); - menu->addAction(action); -} - -void -MainWindow::setupViewMenu() -{ - if (m_mainMenusCreated) return; - - QAction *action = 0; - - 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))); - 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))); - 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))); - 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))); - menu->addAction(action); - - menu->addSeparator(); - - action = new QAction(QIcon(":/icons/zoom-in.png"), - 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))); - menu->addAction(action); - - action = new QAction(QIcon(":/icons/zoom-out.png"), - 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))); - 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(QIcon(":/icons/zoom-fit.png"), - tr("Zoom to &Fit"), this); - 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))); - menu->addAction(action); - - menu->addSeparator(); - - 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); - 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); - 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); - 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); - 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()); - 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(true); - 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(); - -/*!!! This one doesn't work properly yet - - menu->addSeparator(); - - action = new QAction(tr("Show La&yer Hierarchy"), this); - action->setShortcut(tr("Alt+L")); - action->setStatusTip(tr("Open a window displaying the hierarchy of panes and layers in this session")); - connect(action, SIGNAL(triggered()), this, SLOT(showLayerTree())); - menu->addAction(action); - */ -} - -void -MainWindow::setupPaneAndLayerMenus() -{ - if (m_paneMenu) { - m_paneActions.clear(); - m_paneMenu->clear(); - } else { - m_paneMenu = menuBar()->addMenu(tr("&Pane")); - m_paneMenu->setTearOffEnabled(true); - } - - if (m_layerMenu) { - m_layerActions.clear(); - m_layerMenu->clear(); - } else { - m_layerMenu = menuBar()->addMenu(tr("&Layer")); - m_layerMenu->setTearOffEnabled(true); - } - - QMenu *menu = m_paneMenu; - - QAction *action = new QAction(QIcon(":/icons/pane.png"), tr("Add &New Pane"), this); - action->setShortcut(tr("Alt+N")); - action->setStatusTip(tr("Add a new pane containing only a time ruler")); - connect(action, SIGNAL(triggered()), this, SLOT(addPane())); - connect(this, SIGNAL(canAddPane(bool)), action, SLOT(setEnabled(bool))); - m_paneActions[action] = PaneConfiguration(LayerFactory::TimeRuler); - menu->addAction(action); - - menu->addSeparator(); - - menu = m_layerMenu; - -// menu->addSeparator(); - - LayerFactory::LayerTypeSet emptyLayerTypes = - LayerFactory::getInstance()->getValidEmptyLayerTypes(); - - for (LayerFactory::LayerTypeSet::iterator i = emptyLayerTypes.begin(); - i != emptyLayerTypes.end(); ++i) { - - QIcon icon; - QString mainText, tipText, channelText; - LayerFactory::LayerType type = *i; - QString name = LayerFactory::getInstance()->getLayerPresentationName(type); - - icon = QIcon(QString(":/icons/%1.png") - .arg(LayerFactory::getInstance()->getLayerIconName(type))); - - mainText = tr("Add New %1 Layer").arg(name); - tipText = tr("Add a new empty layer of type %1").arg(name); - - action = new QAction(icon, mainText, this); - action->setStatusTip(tipText); - - if (type == LayerFactory::Text) { - action->setShortcut(tr("Alt+T")); - } - - connect(action, SIGNAL(triggered()), this, SLOT(addLayer())); - connect(this, SIGNAL(canAddLayer(bool)), action, SLOT(setEnabled(bool))); - m_layerActions[action] = type; - menu->addAction(action); - m_rightButtonLayerMenu->addAction(action); - } - - m_rightButtonLayerMenu->addSeparator(); - menu->addSeparator(); - - LayerFactory::LayerType backgroundTypes[] = { - LayerFactory::Waveform, - LayerFactory::Spectrogram, - LayerFactory::MelodicRangeSpectrogram, - LayerFactory::PeakFrequencySpectrogram, - LayerFactory::Spectrum - }; - - std::vector<Model *> models; - if (m_document) models = m_document->getTransformInputModels(); //!!! not well named for this! - bool plural = (models.size() > 1); - if (models.empty()) { - models.push_back(getMainModel()); // probably 0 - } - - for (unsigned int i = 0; - i < sizeof(backgroundTypes)/sizeof(backgroundTypes[0]); ++i) { - - for (int menuType = 0; menuType <= 1; ++menuType) { // pane, layer - - if (menuType == 0) menu = m_paneMenu; - else menu = m_layerMenu; - - QMenu *submenu = 0; - - QIcon icon; - QString mainText, shortcutText, tipText, channelText; - LayerFactory::LayerType type = backgroundTypes[i]; - bool mono = true; - - switch (type) { - - case LayerFactory::Waveform: - icon = QIcon(":/icons/waveform.png"); - mainText = tr("Add &Waveform"); - if (menuType == 0) { - shortcutText = tr("Alt+W"); - tipText = tr("Add a new pane showing a waveform view"); - } else { - tipText = tr("Add a new layer showing a waveform view"); - } - mono = false; - break; - - case LayerFactory::Spectrogram: - icon = QIcon(":/icons/spectrogram.png"); - mainText = tr("Add &Spectrogram"); - if (menuType == 0) { - shortcutText = tr("Alt+S"); - tipText = tr("Add a new pane showing a spectrogram"); - } else { - tipText = tr("Add a new layer showing a spectrogram"); - } - break; - - case LayerFactory::MelodicRangeSpectrogram: - icon = QIcon(":/icons/spectrogram.png"); - mainText = tr("Add &Melodic Range Spectrogram"); - if (menuType == 0) { - shortcutText = tr("Alt+M"); - tipText = tr("Add a new pane showing a spectrogram set up for an overview of note pitches"); - } else { - tipText = tr("Add a new layer showing a spectrogram set up for an overview of note pitches"); - } - break; - - case LayerFactory::PeakFrequencySpectrogram: - icon = QIcon(":/icons/spectrogram.png"); - mainText = tr("Add &Peak Frequency Spectrogram"); - if (menuType == 0) { - shortcutText = tr("Alt+P"); - tipText = tr("Add a new pane showing a spectrogram set up for tracking frequencies"); - } else { - tipText = tr("Add a new layer showing a spectrogram set up for tracking frequencies"); - } - break; - - case LayerFactory::Spectrum: - icon = QIcon(":/icons/spectrum.png"); - mainText = tr("Add Spectr&um"); - if (menuType == 0) { - shortcutText = tr("Alt+U"); - tipText = tr("Add a new pane showing a frequency spectrum"); - } else { - tipText = tr("Add a new layer showing a frequency spectrum"); - } - break; - - default: break; - } - - std::vector<Model *> candidateModels; - if (menuType == 0) { - candidateModels = models; - } else { - candidateModels.push_back(0); - } - - for (std::vector<Model *>::iterator mi = - candidateModels.begin(); - mi != candidateModels.end(); ++mi) { - - Model *model = *mi; - - int channels = 0; - if (model) { - DenseTimeValueModel *dtvm = - dynamic_cast<DenseTimeValueModel *>(model); - if (dtvm) channels = dtvm->getChannelCount(); - } - if (channels < 1 && getMainModel()) { - channels = getMainModel()->getChannelCount(); - } - if (channels < 1) channels = 1; - - for (int c = 0; c <= channels; ++c) { - - if (c == 1 && channels == 1) continue; - bool isDefault = (c == 0); - bool isOnly = (isDefault && (channels == 1)); - - if (menuType == 1) { - if (isDefault) isOnly = true; - else continue; - } - - if (isOnly && (!plural || menuType == 1)) { - - if (menuType == 1 && type != LayerFactory::Waveform) { - action = new QAction(mainText, this); - } else { - action = new QAction(icon, mainText, this); - } - - action->setShortcut(shortcutText); - action->setStatusTip(tipText); - if (menuType == 0) { - connect(action, SIGNAL(triggered()), - this, SLOT(addPane())); - connect(this, SIGNAL(canAddPane(bool)), - action, SLOT(setEnabled(bool))); - m_paneActions[action] = PaneConfiguration(type); - } else { - connect(action, SIGNAL(triggered()), - this, SLOT(addLayer())); - connect(this, SIGNAL(canAddLayer(bool)), - action, SLOT(setEnabled(bool))); - m_layerActions[action] = type; - } - menu->addAction(action); - - } else { - - if (!submenu) { - submenu = menu->addMenu(mainText); - submenu->setTearOffEnabled(true); - } else if (isDefault) { - submenu->addSeparator(); - } - - QString actionText; - if (c == 0) { - if (mono) { - actionText = tr("&All Channels Mixed"); - } else { - actionText = tr("&All Channels"); - } - } else { - actionText = tr("Channel &%1").arg(c); - } - - if (model) { - actionText = tr("%1: %2") - .arg(model->objectName()) - .arg(actionText); - } - - if (isDefault) { - action = new QAction(icon, actionText, this); - if (!model || model == getMainModel()) { - action->setShortcut(shortcutText); - } - } else { - action = new QAction(actionText, this); - } - - action->setStatusTip(tipText); - - if (menuType == 0) { - connect(action, SIGNAL(triggered()), - this, SLOT(addPane())); - connect(this, SIGNAL(canAddPane(bool)), - action, SLOT(setEnabled(bool))); - m_paneActions[action] = - PaneConfiguration(type, model, c - 1); - } else { - connect(action, SIGNAL(triggered()), - this, SLOT(addLayer())); - connect(this, SIGNAL(canAddLayer(bool)), - action, SLOT(setEnabled(bool))); - m_layerActions[action] = type; - } - - submenu->addAction(action); - } - } - } - } - } - - menu = m_paneMenu; - - menu->addSeparator(); - - action = new QAction(QIcon(":/icons/editdelete.png"), tr("&Delete Pane"), this); - action->setShortcut(tr("Alt+D")); - action->setStatusTip(tr("Delete the currently active pane")); - connect(action, SIGNAL(triggered()), this, SLOT(deleteCurrentPane())); - connect(this, SIGNAL(canDeleteCurrentPane(bool)), action, SLOT(setEnabled(bool))); - menu->addAction(action); - - menu = m_layerMenu; - - action = new QAction(QIcon(":/icons/timeruler.png"), tr("Add &Time Ruler"), this); - action->setStatusTip(tr("Add a new layer showing a time ruler")); - connect(action, SIGNAL(triggered()), this, SLOT(addLayer())); - connect(this, SIGNAL(canAddLayer(bool)), action, SLOT(setEnabled(bool))); - m_layerActions[action] = LayerFactory::TimeRuler; - menu->addAction(action); - - menu->addSeparator(); - - m_existingLayersMenu = menu->addMenu(tr("Add &Existing Layer")); - m_existingLayersMenu->setTearOffEnabled(true); - m_rightButtonLayerMenu->addMenu(m_existingLayersMenu); - - m_sliceMenu = menu->addMenu(tr("Add S&lice of Layer")); - m_sliceMenu->setTearOffEnabled(true); - m_rightButtonLayerMenu->addMenu(m_sliceMenu); - - setupExistingLayersMenus(); - - m_rightButtonLayerMenu->addSeparator(); - menu->addSeparator(); - - action = new QAction(tr("&Rename Layer..."), this); - action->setShortcut(tr("Alt+R")); - action->setStatusTip(tr("Rename the currently active layer")); - connect(action, SIGNAL(triggered()), this, SLOT(renameCurrentLayer())); - connect(this, SIGNAL(canRenameLayer(bool)), action, SLOT(setEnabled(bool))); - menu->addAction(action); - m_rightButtonLayerMenu->addAction(action); - - action = new QAction(QIcon(":/icons/editdelete.png"), tr("&Delete Layer"), this); - action->setShortcut(tr("Alt+Shift+D")); - action->setStatusTip(tr("Delete the currently active layer")); - connect(action, SIGNAL(triggered()), this, SLOT(deleteCurrentLayer())); - connect(this, SIGNAL(canDeleteCurrentLayer(bool)), action, SLOT(setEnabled(bool))); - menu->addAction(action); - m_rightButtonLayerMenu->addAction(action); -} - -void -MainWindow::setupTransformsMenu() -{ - if (m_transformsMenu) { - m_transformActions.clear(); - m_transformActionsReverse.clear(); - m_transformsMenu->clear(); - } else { - m_transformsMenu = menuBar()->addMenu(tr("&Transform")); - m_transformsMenu->setTearOffEnabled(true); - } - - TransformFactory::TransformList transforms = - TransformFactory::getInstance()->getAllTransforms(); - - vector<QString> types = - TransformFactory::getInstance()->getAllTransformTypes(); - - map<QString, map<QString, SubdividingMenu *> > categoryMenus; - map<QString, map<QString, SubdividingMenu *> > makerMenus; - - map<QString, SubdividingMenu *> byPluginNameMenus; - map<QString, map<QString, QMenu *> > pluginNameMenus; - - set<SubdividingMenu *> pendingMenus; - - m_recentTransformsMenu = m_transformsMenu->addMenu(tr("&Recent Transforms")); - m_recentTransformsMenu->setTearOffEnabled(true); - m_rightButtonTransformsMenu->addMenu(m_recentTransformsMenu); - connect(&m_recentTransforms, SIGNAL(recentChanged()), - this, SLOT(setupRecentTransformsMenu())); - - m_transformsMenu->addSeparator(); - m_rightButtonTransformsMenu->addSeparator(); - - for (vector<QString>::iterator i = types.begin(); i != types.end(); ++i) { - - if (i != types.begin()) { - m_transformsMenu->addSeparator(); - m_rightButtonTransformsMenu->addSeparator(); - } - - QString byCategoryLabel = tr("%1 by Category").arg(*i); - SubdividingMenu *byCategoryMenu = new SubdividingMenu(byCategoryLabel, - 20, 40); - byCategoryMenu->setTearOffEnabled(true); - m_transformsMenu->addMenu(byCategoryMenu); - m_rightButtonTransformsMenu->addMenu(byCategoryMenu); - pendingMenus.insert(byCategoryMenu); - - vector<QString> categories = - TransformFactory::getInstance()->getTransformCategories(*i); - - for (vector<QString>::iterator j = categories.begin(); - j != categories.end(); ++j) { - - QString category = *j; - if (category == "") category = tr("Unclassified"); - - if (categories.size() < 2) { - categoryMenus[*i][category] = byCategoryMenu; - continue; - } - - QStringList components = category.split(" > "); - QString key; - - for (QStringList::iterator k = components.begin(); - k != components.end(); ++k) { - - QString parentKey = key; - if (key != "") key += " > "; - key += *k; - - if (categoryMenus[*i].find(key) == categoryMenus[*i].end()) { - SubdividingMenu *m = new SubdividingMenu(*k, 20, 40); - m->setTearOffEnabled(true); - pendingMenus.insert(m); - categoryMenus[*i][key] = m; - if (parentKey == "") { - byCategoryMenu->addMenu(m); - } else { - categoryMenus[*i][parentKey]->addMenu(m); - } - } - } - } - - QString byPluginNameLabel = tr("%1 by Plugin Name").arg(*i); - byPluginNameMenus[*i] = new SubdividingMenu(byPluginNameLabel); - byPluginNameMenus[*i]->setTearOffEnabled(true); - m_transformsMenu->addMenu(byPluginNameMenus[*i]); - m_rightButtonTransformsMenu->addMenu(byPluginNameMenus[*i]); - pendingMenus.insert(byPluginNameMenus[*i]); - - QString byMakerLabel = tr("%1 by Maker").arg(*i); - SubdividingMenu *byMakerMenu = new SubdividingMenu(byMakerLabel, 20, 40); - byMakerMenu->setTearOffEnabled(true); - m_transformsMenu->addMenu(byMakerMenu); - m_rightButtonTransformsMenu->addMenu(byMakerMenu); - pendingMenus.insert(byMakerMenu); - - vector<QString> makers = - TransformFactory::getInstance()->getTransformMakers(*i); - - for (vector<QString>::iterator j = makers.begin(); - j != makers.end(); ++j) { - - QString maker = *j; - if (maker == "") maker = tr("Unknown"); - maker.replace(QRegExp(tr(" [\\(<].*$")), ""); - - makerMenus[*i][maker] = new SubdividingMenu(maker, 30, 40); - makerMenus[*i][maker]->setTearOffEnabled(true); - byMakerMenu->addMenu(makerMenus[*i][maker]); - pendingMenus.insert(makerMenus[*i][maker]); - } - } - - for (unsigned int i = 0; i < transforms.size(); ++i) { - - QString name = transforms[i].name; - if (name == "") name = transforms[i].identifier; - -// std::cerr << "Plugin Name: " << name.toStdString() << std::endl; - - QString type = transforms[i].type; - - QString category = transforms[i].category; - if (category == "") category = tr("Unclassified"); - - QString maker = transforms[i].maker; - if (maker == "") maker = tr("Unknown"); - maker.replace(QRegExp(tr(" [\\(<].*$")), ""); - - QString pluginName = name.section(": ", 0, 0); - QString output = name.section(": ", 1); - - QAction *action = new QAction(tr("%1...").arg(name), this); - connect(action, SIGNAL(triggered()), this, SLOT(addLayer())); - m_transformActions[action] = transforms[i].identifier; - m_transformActionsReverse[transforms[i].identifier] = action; - connect(this, SIGNAL(canAddLayer(bool)), action, SLOT(setEnabled(bool))); - - action->setStatusTip(transforms[i].description); - - if (categoryMenus[type].find(category) == categoryMenus[type].end()) { - std::cerr << "WARNING: MainWindow::setupMenus: Internal error: " - << "No category menu for transform \"" - << name.toStdString() << "\" (category = \"" - << category.toStdString() << "\")" << std::endl; - } else { - categoryMenus[type][category]->addAction(action); - } - - if (makerMenus[type].find(maker) == makerMenus[type].end()) { - std::cerr << "WARNING: MainWindow::setupMenus: Internal error: " - << "No maker menu for transform \"" - << name.toStdString() << "\" (maker = \"" - << maker.toStdString() << "\")" << std::endl; - } else { - makerMenus[type][maker]->addAction(action); - } - - action = new QAction(tr("%1...").arg(output == "" ? pluginName : output), this); - connect(action, SIGNAL(triggered()), this, SLOT(addLayer())); - m_transformActions[action] = transforms[i].identifier; - connect(this, SIGNAL(canAddLayer(bool)), action, SLOT(setEnabled(bool))); - action->setStatusTip(transforms[i].description); - -// cerr << "Transform: \"" << name.toStdString() << "\": plugin name \"" << pluginName.toStdString() << "\"" << endl; - - if (pluginNameMenus[type].find(pluginName) == - pluginNameMenus[type].end()) { - - SubdividingMenu *parentMenu = byPluginNameMenus[type]; - parentMenu->setTearOffEnabled(true); - - if (output == "") { - parentMenu->addAction(pluginName, action); - } else { - pluginNameMenus[type][pluginName] = - parentMenu->addMenu(pluginName); - connect(this, SIGNAL(canAddLayer(bool)), - pluginNameMenus[type][pluginName], - SLOT(setEnabled(bool))); - } - } - - if (pluginNameMenus[type].find(pluginName) != - pluginNameMenus[type].end()) { - pluginNameMenus[type][pluginName]->addAction(action); - } - } - - for (set<SubdividingMenu *>::iterator i = pendingMenus.begin(); - i != pendingMenus.end(); ++i) { - (*i)->entriesAdded(); - } - - setupRecentTransformsMenu(); -} - -void -MainWindow::setupHelpMenu() -{ - if (m_mainMenusCreated) return; - - QMenu *menu = menuBar()->addMenu(tr("&Help")); - menu->setTearOffEnabled(true); - - QAction *action = new QAction(QIcon(":icons/help.png"), - tr("&Help Reference"), this); - action->setStatusTip(tr("Open the Sonic Visualiser reference manual")); - connect(action, SIGNAL(triggered()), this, SLOT(help())); - 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 Sound Access"), this); - action->setStatusTip(tr("Show information about Sound Access")); - 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())); - m_recentFilesMenu->addAction(action); - } -} - -void -MainWindow::setupRecentTransformsMenu() -{ - m_recentTransformsMenu->clear(); - vector<QString> transforms = m_recentTransforms.getRecent(); - for (size_t i = 0; i < transforms.size(); ++i) { - TransformActionReverseMap::iterator ti = - m_transformActionsReverse.find(transforms[i]); - if (ti == m_transformActionsReverse.end()) { - std::cerr << "WARNING: MainWindow::setupRecentTransformsMenu: " - << "Unknown transform \"" << transforms[i].toStdString() - << "\" in recent transforms list" << std::endl; - continue; - } - m_recentTransformsMenu->addAction(ti->second); - } -} - -void -MainWindow::setupExistingLayersMenus() -{ - if (!m_existingLayersMenu) return; // should have been created by setupMenus - -// std::cerr << "MainWindow::setupExistingLayersMenu" << std::endl; - - m_existingLayersMenu->clear(); - m_existingLayerActions.clear(); - - m_sliceMenu->clear(); - m_sliceActions.clear(); - - vector<Layer *> orderedLayers; - set<Layer *> observedLayers; - set<Layer *> sliceableLayers; - - LayerFactory *factory = LayerFactory::getInstance(); - - for (int i = 0; i < m_paneStack->getPaneCount(); ++i) { - - Pane *pane = m_paneStack->getPane(i); - if (!pane) continue; - - for (int j = 0; j < pane->getLayerCount(); ++j) { - - Layer *layer = pane->getLayer(j); - if (!layer) continue; - if (observedLayers.find(layer) != observedLayers.end()) { -// std::cerr << "found duplicate layer " << layer << std::endl; - continue; - } - -// std::cerr << "found new layer " << layer << " (name = " -// << layer->getLayerPresentationName().toStdString() << ")" << std::endl; - - orderedLayers.push_back(layer); - observedLayers.insert(layer); - - if (factory->isLayerSliceable(layer)) { - sliceableLayers.insert(layer); - } - } - } - - map<QString, int> observedNames; - - for (size_t i = 0; i < orderedLayers.size(); ++i) { - - Layer *layer = orderedLayers[i]; - - QString name = layer->getLayerPresentationName(); - int n = ++observedNames[name]; - if (n > 1) name = QString("%1 <%2>").arg(name).arg(n); - - QIcon icon = QIcon(QString(":/icons/%1.png") - .arg(factory->getLayerIconName - (factory->getLayerType(layer)))); - - QAction *action = new QAction(icon, name, this); - connect(action, SIGNAL(triggered()), this, SLOT(addLayer())); - connect(this, SIGNAL(canAddLayer(bool)), action, SLOT(setEnabled(bool))); - m_existingLayerActions[action] = layer; - - m_existingLayersMenu->addAction(action); - - if (sliceableLayers.find(layer) != sliceableLayers.end()) { - action = new QAction(icon, name, this); - connect(action, SIGNAL(triggered()), this, SLOT(addLayer())); - connect(this, SIGNAL(canAddLayer(bool)), action, SLOT(setEnabled(bool))); - m_sliceActions[action] = layer; - m_sliceMenu->addAction(action); - } - } - - m_sliceMenu->setEnabled(!m_sliceActions.empty()); -} - -void -MainWindow::setupToolbars() -{ - QToolBar *toolbar = addToolBar(tr("Transport Toolbar")); - - QAction *action = toolbar->addAction(QIcon(":/icons/rewind-start.png"), - tr("Rewind to Start")); - action->setShortcut(tr("Home")); - action->setStatusTip(tr("Rewind to the start")); - connect(action, SIGNAL(triggered()), this, SLOT(rewindStart())); - connect(this, SIGNAL(canPlay(bool)), action, SLOT(setEnabled(bool))); - - action = toolbar->addAction(QIcon(":/icons/rewind.png"), - tr("Rewind")); - action->setShortcut(tr("PageUp")); - action->setStatusTip(tr("Rewind to the previous time instant in the current layer")); - connect(action, SIGNAL(triggered()), this, SLOT(rewind())); - connect(this, SIGNAL(canRewind(bool)), action, SLOT(setEnabled(bool))); - - action = toolbar->addAction(QIcon(":/icons/playpause.png"), - tr("Play / Pause")); - action->setCheckable(true); - action->setShortcut(tr("Space")); - action->setStatusTip(tr("Start or stop playback from the current position")); - connect(action, SIGNAL(triggered()), this, SLOT(play())); - connect(m_playSource, SIGNAL(playStatusChanged(bool)), - action, SLOT(setChecked(bool))); - connect(this, SIGNAL(canPlay(bool)), action, SLOT(setEnabled(bool))); - - action = toolbar->addAction(QIcon(":/icons/ffwd.png"), - tr("Fast Forward")); - action->setShortcut(tr("PageDown")); - action->setStatusTip(tr("Fast forward to the next time instant in the current layer")); - connect(action, SIGNAL(triggered()), this, SLOT(ffwd())); - connect(this, SIGNAL(canFfwd(bool)), action, SLOT(setEnabled(bool))); - - action = toolbar->addAction(QIcon(":/icons/ffwd-end.png"), - tr("Fast Forward to End")); - action->setShortcut(tr("End")); - action->setStatusTip(tr("Fast-forward to the end")); - connect(action, SIGNAL(triggered()), this, SLOT(ffwdEnd())); - connect(this, SIGNAL(canPlay(bool)), action, SLOT(setEnabled(bool))); - - toolbar = addToolBar(tr("Play Mode Toolbar")); - - action = toolbar->addAction(QIcon(":/icons/playselection.png"), - tr("Constrain Playback to Selection")); - action->setCheckable(true); - action->setChecked(m_viewManager->getPlaySelectionMode()); - action->setShortcut(tr("s")); - action->setStatusTip(tr("Constrain playback to the selected area")); - connect(m_viewManager, SIGNAL(playSelectionModeChanged(bool)), - action, SLOT(setChecked(bool))); - connect(action, SIGNAL(triggered()), this, SLOT(playSelectionToggled())); - connect(this, SIGNAL(canPlaySelection(bool)), action, SLOT(setEnabled(bool))); - - action = toolbar->addAction(QIcon(":/icons/playloop.png"), - tr("Loop Playback")); - action->setCheckable(true); - action->setChecked(m_viewManager->getPlayLoopMode()); - action->setShortcut(tr("l")); - action->setStatusTip(tr("Loop playback")); - connect(m_viewManager, SIGNAL(playLoopModeChanged(bool)), - action, SLOT(setChecked(bool))); - connect(action, SIGNAL(triggered()), this, SLOT(playLoopToggled())); - connect(this, SIGNAL(canPlay(bool)), action, SLOT(setEnabled(bool))); - - toolbar = addToolBar(tr("Edit Toolbar")); - CommandHistory::getInstance()->registerToolbar(toolbar); - - toolbar = addToolBar(tr("Tools Toolbar")); - QActionGroup *group = new QActionGroup(this); - - action = toolbar->addAction(QIcon(":/icons/navigate.png"), - tr("Navigate")); - action->setCheckable(true); - action->setChecked(true); - action->setShortcut(tr("1")); - action->setStatusTip(tr("Navigate")); - connect(action, SIGNAL(triggered()), this, SLOT(toolNavigateSelected())); - group->addAction(action); - m_toolActions[ViewManager::NavigateMode] = action; - - action = toolbar->addAction(QIcon(":/icons/select.png"), - tr("Select")); - action->setCheckable(true); - action->setShortcut(tr("2")); - action->setStatusTip(tr("Select ranges")); - connect(action, SIGNAL(triggered()), this, SLOT(toolSelectSelected())); - group->addAction(action); - m_toolActions[ViewManager::SelectMode] = action; - - action = toolbar->addAction(QIcon(":/icons/move.png"), - tr("Edit")); - action->setCheckable(true); - action->setShortcut(tr("3")); - action->setStatusTip(tr("Edit items in layer")); - connect(action, SIGNAL(triggered()), this, SLOT(toolEditSelected())); - connect(this, SIGNAL(canEditLayer(bool)), action, SLOT(setEnabled(bool))); - group->addAction(action); - m_toolActions[ViewManager::EditMode] = action; - - action = toolbar->addAction(QIcon(":/icons/draw.png"), - tr("Draw")); - action->setCheckable(true); - action->setShortcut(tr("4")); - action->setStatusTip(tr("Draw new items in layer")); - connect(action, SIGNAL(triggered()), this, SLOT(toolDrawSelected())); - connect(this, SIGNAL(canEditLayer(bool)), action, SLOT(setEnabled(bool))); - group->addAction(action); - m_toolActions[ViewManager::DrawMode] = action; - -// action = toolbar->addAction(QIcon(":/icons/text.png"), -// tr("Text")); -// action->setCheckable(true); -// action->setShortcut(tr("5")); -// connect(action, SIGNAL(triggered()), this, SLOT(toolTextSelected())); -// group->addAction(action); -// m_toolActions[ViewManager::TextMode] = action; - + menu->addAction(action); +} + +void +MainWindow::setupViewMenu() +{ + if (m_mainMenusCreated) return; + + QAction *action = 0; + + 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))); + 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))); + 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))); + 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))); + menu->addAction(action); + + menu->addSeparator(); + + action = new QAction(QIcon(":/icons/zoom-in.png"), + 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))); + menu->addAction(action); + + action = new QAction(QIcon(":/icons/zoom-out.png"), + 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))); + 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(QIcon(":/icons/zoom-fit.png"), + tr("Zoom to &Fit"), this); + 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))); + menu->addAction(action); + + menu->addSeparator(); + + 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); + 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); + 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); + 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); + 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()); + 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(true); + 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(); + +/*!!! This one doesn't work properly yet + + menu->addSeparator(); + + action = new QAction(tr("Show La&yer Hierarchy"), this); + action->setShortcut(tr("Alt+L")); + action->setStatusTip(tr("Open a window displaying the hierarchy of panes and layers in this session")); + connect(action, SIGNAL(triggered()), this, SLOT(showLayerTree())); + menu->addAction(action); + */ +} + +void +MainWindow::setupPaneAndLayerMenus() +{ + if (m_paneMenu) { + m_paneActions.clear(); + m_paneMenu->clear(); + } else { + m_paneMenu = menuBar()->addMenu(tr("&Pane")); + m_paneMenu->setTearOffEnabled(true); + } + + if (m_layerMenu) { + m_layerActions.clear(); + m_layerMenu->clear(); + } else { + m_layerMenu = menuBar()->addMenu(tr("&Layer")); + m_layerMenu->setTearOffEnabled(true); + } + + QMenu *menu = m_paneMenu; + + QAction *action = new QAction(QIcon(":/icons/pane.png"), tr("Add &New Pane"), this); + action->setShortcut(tr("Alt+N")); + action->setStatusTip(tr("Add a new pane containing only a time ruler")); + connect(action, SIGNAL(triggered()), this, SLOT(addPane())); + connect(this, SIGNAL(canAddPane(bool)), action, SLOT(setEnabled(bool))); + m_paneActions[action] = PaneConfiguration(LayerFactory::TimeRuler); + menu->addAction(action); + + menu->addSeparator(); + + menu = m_layerMenu; + +// menu->addSeparator(); + + LayerFactory::LayerTypeSet emptyLayerTypes = + LayerFactory::getInstance()->getValidEmptyLayerTypes(); + + for (LayerFactory::LayerTypeSet::iterator i = emptyLayerTypes.begin(); + i != emptyLayerTypes.end(); ++i) { + + QIcon icon; + QString mainText, tipText, channelText; + LayerFactory::LayerType type = *i; + QString name = LayerFactory::getInstance()->getLayerPresentationName(type); + + icon = QIcon(QString(":/icons/%1.png") + .arg(LayerFactory::getInstance()->getLayerIconName(type))); + + mainText = tr("Add New %1 Layer").arg(name); + tipText = tr("Add a new empty layer of type %1").arg(name); + + action = new QAction(icon, mainText, this); + action->setStatusTip(tipText); + + if (type == LayerFactory::Text) { + action->setShortcut(tr("Alt+T")); + } + + connect(action, SIGNAL(triggered()), this, SLOT(addLayer())); + connect(this, SIGNAL(canAddLayer(bool)), action, SLOT(setEnabled(bool))); + m_layerActions[action] = type; + menu->addAction(action); + m_rightButtonLayerMenu->addAction(action); + } + + m_rightButtonLayerMenu->addSeparator(); + menu->addSeparator(); + + LayerFactory::LayerType backgroundTypes[] = { + LayerFactory::Waveform, + LayerFactory::Spectrogram, + LayerFactory::MelodicRangeSpectrogram, + LayerFactory::PeakFrequencySpectrogram, + LayerFactory::Spectrum + }; + + std::vector<Model *> models; + if (m_document) models = m_document->getTransformInputModels(); //!!! not well named for this! + bool plural = (models.size() > 1); + if (models.empty()) { + models.push_back(getMainModel()); // probably 0 + } + + for (unsigned int i = 0; + i < sizeof(backgroundTypes)/sizeof(backgroundTypes[0]); ++i) { + + for (int menuType = 0; menuType <= 1; ++menuType) { // pane, layer + + if (menuType == 0) menu = m_paneMenu; + else menu = m_layerMenu; + + QMenu *submenu = 0; + + QIcon icon; + QString mainText, shortcutText, tipText, channelText; + LayerFactory::LayerType type = backgroundTypes[i]; + bool mono = true; + + switch (type) { + + case LayerFactory::Waveform: + icon = QIcon(":/icons/waveform.png"); + mainText = tr("Add &Waveform"); + if (menuType == 0) { + shortcutText = tr("Alt+W"); + tipText = tr("Add a new pane showing a waveform view"); + } else { + tipText = tr("Add a new layer showing a waveform view"); + } + mono = false; + break; + + case LayerFactory::Spectrogram: + icon = QIcon(":/icons/spectrogram.png"); + mainText = tr("Add &Spectrogram"); + if (menuType == 0) { + shortcutText = tr("Alt+S"); + tipText = tr("Add a new pane showing a spectrogram"); + } else { + tipText = tr("Add a new layer showing a spectrogram"); + } + break; + + case LayerFactory::MelodicRangeSpectrogram: + icon = QIcon(":/icons/spectrogram.png"); + mainText = tr("Add &Melodic Range Spectrogram"); + if (menuType == 0) { + shortcutText = tr("Alt+M"); + tipText = tr("Add a new pane showing a spectrogram set up for an overview of note pitches"); + } else { + tipText = tr("Add a new layer showing a spectrogram set up for an overview of note pitches"); + } + break; + + case LayerFactory::PeakFrequencySpectrogram: + icon = QIcon(":/icons/spectrogram.png"); + mainText = tr("Add &Peak Frequency Spectrogram"); + if (menuType == 0) { + shortcutText = tr("Alt+P"); + tipText = tr("Add a new pane showing a spectrogram set up for tracking frequencies"); + } else { + tipText = tr("Add a new layer showing a spectrogram set up for tracking frequencies"); + } + break; + + case LayerFactory::Spectrum: + icon = QIcon(":/icons/spectrum.png"); + mainText = tr("Add Spectr&um"); + if (menuType == 0) { + shortcutText = tr("Alt+U"); + tipText = tr("Add a new pane showing a frequency spectrum"); + } else { + tipText = tr("Add a new layer showing a frequency spectrum"); + } + break; + + default: break; + } + + std::vector<Model *> candidateModels; + if (menuType == 0) { + candidateModels = models; + } else { + candidateModels.push_back(0); + } + + for (std::vector<Model *>::iterator mi = + candidateModels.begin(); + mi != candidateModels.end(); ++mi) { + + Model *model = *mi; + + int channels = 0; + if (model) { + DenseTimeValueModel *dtvm = + dynamic_cast<DenseTimeValueModel *>(model); + if (dtvm) channels = dtvm->getChannelCount(); + } + if (channels < 1 && getMainModel()) { + channels = getMainModel()->getChannelCount(); + } + if (channels < 1) channels = 1; + + for (int c = 0; c <= channels; ++c) { + + if (c == 1 && channels == 1) continue; + bool isDefault = (c == 0); + bool isOnly = (isDefault && (channels == 1)); + + if (menuType == 1) { + if (isDefault) isOnly = true; + else continue; + } + + if (isOnly && (!plural || menuType == 1)) { + + if (menuType == 1 && type != LayerFactory::Waveform) { + action = new QAction(mainText, this); + } else { + action = new QAction(icon, mainText, this); + } + + action->setShortcut(shortcutText); + action->setStatusTip(tipText); + if (menuType == 0) { + connect(action, SIGNAL(triggered()), + this, SLOT(addPane())); + connect(this, SIGNAL(canAddPane(bool)), + action, SLOT(setEnabled(bool))); + m_paneActions[action] = PaneConfiguration(type); + } else { + connect(action, SIGNAL(triggered()), + this, SLOT(addLayer())); + connect(this, SIGNAL(canAddLayer(bool)), + action, SLOT(setEnabled(bool))); + m_layerActions[action] = type; + } + menu->addAction(action); + + } else { + + if (!submenu) { + submenu = menu->addMenu(mainText); + submenu->setTearOffEnabled(true); + } else if (isDefault) { + submenu->addSeparator(); + } + + QString actionText; + if (c == 0) { + if (mono) { + actionText = tr("&All Channels Mixed"); + } else { + actionText = tr("&All Channels"); + } + } else { + actionText = tr("Channel &%1").arg(c); + } + + if (model) { + actionText = tr("%1: %2") + .arg(model->objectName()) + .arg(actionText); + } + + if (isDefault) { + action = new QAction(icon, actionText, this); + if (!model || model == getMainModel()) { + action->setShortcut(shortcutText); + } + } else { + action = new QAction(actionText, this); + } + + action->setStatusTip(tipText); + + if (menuType == 0) { + connect(action, SIGNAL(triggered()), + this, SLOT(addPane())); + connect(this, SIGNAL(canAddPane(bool)), + action, SLOT(setEnabled(bool))); + m_paneActions[action] = + PaneConfiguration(type, model, c - 1); + } else { + connect(action, SIGNAL(triggered()), + this, SLOT(addLayer())); + connect(this, SIGNAL(canAddLayer(bool)), + action, SLOT(setEnabled(bool))); + m_layerActions[action] = type; + } + + submenu->addAction(action); + } + } + } + } + } + + menu = m_paneMenu; + + menu->addSeparator(); + + action = new QAction(QIcon(":/icons/editdelete.png"), tr("&Delete Pane"), this); + action->setShortcut(tr("Alt+D")); + action->setStatusTip(tr("Delete the currently active pane")); + connect(action, SIGNAL(triggered()), this, SLOT(deleteCurrentPane())); + connect(this, SIGNAL(canDeleteCurrentPane(bool)), action, SLOT(setEnabled(bool))); + menu->addAction(action); + + menu = m_layerMenu; + + action = new QAction(QIcon(":/icons/timeruler.png"), tr("Add &Time Ruler"), this); + action->setStatusTip(tr("Add a new layer showing a time ruler")); + connect(action, SIGNAL(triggered()), this, SLOT(addLayer())); + connect(this, SIGNAL(canAddLayer(bool)), action, SLOT(setEnabled(bool))); + m_layerActions[action] = LayerFactory::TimeRuler; + menu->addAction(action); + + menu->addSeparator(); + + m_existingLayersMenu = menu->addMenu(tr("Add &Existing Layer")); + m_existingLayersMenu->setTearOffEnabled(true); + m_rightButtonLayerMenu->addMenu(m_existingLayersMenu); + + m_sliceMenu = menu->addMenu(tr("Add S&lice of Layer")); + m_sliceMenu->setTearOffEnabled(true); + m_rightButtonLayerMenu->addMenu(m_sliceMenu); + + setupExistingLayersMenus(); + + m_rightButtonLayerMenu->addSeparator(); + menu->addSeparator(); + + action = new QAction(tr("&Rename Layer..."), this); + action->setShortcut(tr("Alt+R")); + action->setStatusTip(tr("Rename the currently active layer")); + connect(action, SIGNAL(triggered()), this, SLOT(renameCurrentLayer())); + connect(this, SIGNAL(canRenameLayer(bool)), action, SLOT(setEnabled(bool))); + menu->addAction(action); + m_rightButtonLayerMenu->addAction(action); + + action = new QAction(QIcon(":/icons/editdelete.png"), tr("&Delete Layer"), this); + action->setShortcut(tr("Alt+Shift+D")); + action->setStatusTip(tr("Delete the currently active layer")); + connect(action, SIGNAL(triggered()), this, SLOT(deleteCurrentLayer())); + connect(this, SIGNAL(canDeleteCurrentLayer(bool)), action, SLOT(setEnabled(bool))); + menu->addAction(action); + m_rightButtonLayerMenu->addAction(action); +} + +void +MainWindow::setupTransformsMenu() +{ + if (m_transformsMenu) { + m_transformActions.clear(); + m_transformActionsReverse.clear(); + m_transformsMenu->clear(); + } else { + m_transformsMenu = menuBar()->addMenu(tr("&Transform")); + m_transformsMenu->setTearOffEnabled(true); + } + + TransformFactory::TransformList transforms = + TransformFactory::getInstance()->getAllTransforms(); + + vector<QString> types = + TransformFactory::getInstance()->getAllTransformTypes(); + + map<QString, map<QString, SubdividingMenu *> > categoryMenus; + map<QString, map<QString, SubdividingMenu *> > makerMenus; + + map<QString, SubdividingMenu *> byPluginNameMenus; + map<QString, map<QString, QMenu *> > pluginNameMenus; + + set<SubdividingMenu *> pendingMenus; + + m_recentTransformsMenu = m_transformsMenu->addMenu(tr("&Recent Transforms")); + m_recentTransformsMenu->setTearOffEnabled(true); + m_rightButtonTransformsMenu->addMenu(m_recentTransformsMenu); + connect(&m_recentTransforms, SIGNAL(recentChanged()), + this, SLOT(setupRecentTransformsMenu())); + + m_transformsMenu->addSeparator(); + m_rightButtonTransformsMenu->addSeparator(); + + for (vector<QString>::iterator i = types.begin(); i != types.end(); ++i) { + + if (i != types.begin()) { + m_transformsMenu->addSeparator(); + m_rightButtonTransformsMenu->addSeparator(); + } + + QString byCategoryLabel = tr("%1 by Category").arg(*i); + SubdividingMenu *byCategoryMenu = new SubdividingMenu(byCategoryLabel, + 20, 40); + byCategoryMenu->setTearOffEnabled(true); + m_transformsMenu->addMenu(byCategoryMenu); + m_rightButtonTransformsMenu->addMenu(byCategoryMenu); + pendingMenus.insert(byCategoryMenu); + + vector<QString> categories = + TransformFactory::getInstance()->getTransformCategories(*i); + + for (vector<QString>::iterator j = categories.begin(); + j != categories.end(); ++j) { + + QString category = *j; + if (category == "") category = tr("Unclassified"); + + if (categories.size() < 2) { + categoryMenus[*i][category] = byCategoryMenu; + continue; + } + + QStringList components = category.split(" > "); + QString key; + + for (QStringList::iterator k = components.begin(); + k != components.end(); ++k) { + + QString parentKey = key; + if (key != "") key += " > "; + key += *k; + + if (categoryMenus[*i].find(key) == categoryMenus[*i].end()) { + SubdividingMenu *m = new SubdividingMenu(*k, 20, 40); + m->setTearOffEnabled(true); + pendingMenus.insert(m); + categoryMenus[*i][key] = m; + if (parentKey == "") { + byCategoryMenu->addMenu(m); + } else { + categoryMenus[*i][parentKey]->addMenu(m); + } + } + } + } + + QString byPluginNameLabel = tr("%1 by Plugin Name").arg(*i); + byPluginNameMenus[*i] = new SubdividingMenu(byPluginNameLabel); + byPluginNameMenus[*i]->setTearOffEnabled(true); + m_transformsMenu->addMenu(byPluginNameMenus[*i]); + m_rightButtonTransformsMenu->addMenu(byPluginNameMenus[*i]); + pendingMenus.insert(byPluginNameMenus[*i]); + + QString byMakerLabel = tr("%1 by Maker").arg(*i); + SubdividingMenu *byMakerMenu = new SubdividingMenu(byMakerLabel, 20, 40); + byMakerMenu->setTearOffEnabled(true); + m_transformsMenu->addMenu(byMakerMenu); + m_rightButtonTransformsMenu->addMenu(byMakerMenu); + pendingMenus.insert(byMakerMenu); + + vector<QString> makers = + TransformFactory::getInstance()->getTransformMakers(*i); + + for (vector<QString>::iterator j = makers.begin(); + j != makers.end(); ++j) { + + QString maker = *j; + if (maker == "") maker = tr("Unknown"); + maker.replace(QRegExp(tr(" [\\(<].*$")), ""); + + makerMenus[*i][maker] = new SubdividingMenu(maker, 30, 40); + makerMenus[*i][maker]->setTearOffEnabled(true); + byMakerMenu->addMenu(makerMenus[*i][maker]); + pendingMenus.insert(makerMenus[*i][maker]); + } + } + + for (unsigned int i = 0; i < transforms.size(); ++i) { + + QString name = transforms[i].name; + if (name == "") name = transforms[i].identifier; + +// std::cerr << "Plugin Name: " << name.toStdString() << std::endl; + + QString type = transforms[i].type; + + QString category = transforms[i].category; + if (category == "") category = tr("Unclassified"); + + QString maker = transforms[i].maker; + if (maker == "") maker = tr("Unknown"); + maker.replace(QRegExp(tr(" [\\(<].*$")), ""); + + QString pluginName = name.section(": ", 0, 0); + QString output = name.section(": ", 1); + + QAction *action = new QAction(tr("%1...").arg(name), this); + connect(action, SIGNAL(triggered()), this, SLOT(addLayer())); + m_transformActions[action] = transforms[i].identifier; + m_transformActionsReverse[transforms[i].identifier] = action; + connect(this, SIGNAL(canAddLayer(bool)), action, SLOT(setEnabled(bool))); + + action->setStatusTip(transforms[i].description); + + if (categoryMenus[type].find(category) == categoryMenus[type].end()) { + std::cerr << "WARNING: MainWindow::setupMenus: Internal error: " + << "No category menu for transform \"" + << name.toStdString() << "\" (category = \"" + << category.toStdString() << "\")" << std::endl; + } else { + categoryMenus[type][category]->addAction(action); + } + + if (makerMenus[type].find(maker) == makerMenus[type].end()) { + std::cerr << "WARNING: MainWindow::setupMenus: Internal error: " + << "No maker menu for transform \"" + << name.toStdString() << "\" (maker = \"" + << maker.toStdString() << "\")" << std::endl; + } else { + makerMenus[type][maker]->addAction(action); + } + + action = new QAction(tr("%1...").arg(output == "" ? pluginName : output), this); + connect(action, SIGNAL(triggered()), this, SLOT(addLayer())); + m_transformActions[action] = transforms[i].identifier; + connect(this, SIGNAL(canAddLayer(bool)), action, SLOT(setEnabled(bool))); + action->setStatusTip(transforms[i].description); + +// cerr << "Transform: \"" << name.toStdString() << "\": plugin name \"" << pluginName.toStdString() << "\"" << endl; + + if (pluginNameMenus[type].find(pluginName) == + pluginNameMenus[type].end()) { + + SubdividingMenu *parentMenu = byPluginNameMenus[type]; + parentMenu->setTearOffEnabled(true); + + if (output == "") { + parentMenu->addAction(pluginName, action); + } else { + pluginNameMenus[type][pluginName] = + parentMenu->addMenu(pluginName); + connect(this, SIGNAL(canAddLayer(bool)), + pluginNameMenus[type][pluginName], + SLOT(setEnabled(bool))); + } + } + + if (pluginNameMenus[type].find(pluginName) != + pluginNameMenus[type].end()) { + pluginNameMenus[type][pluginName]->addAction(action); + } + } + + for (set<SubdividingMenu *>::iterator i = pendingMenus.begin(); + i != pendingMenus.end(); ++i) { + (*i)->entriesAdded(); + } + + setupRecentTransformsMenu(); +} + +void +MainWindow::setupHelpMenu() +{ + if (m_mainMenusCreated) return; + + QMenu *menu = menuBar()->addMenu(tr("&Help")); + menu->setTearOffEnabled(true); + + QAction *action = new QAction(QIcon(":icons/help.png"), + tr("&Help Reference"), this); + action->setStatusTip(tr("Open the Sonic Visualiser reference manual")); + connect(action, SIGNAL(triggered()), this, SLOT(help())); + 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 Sound Access"), this); + action->setStatusTip(tr("Show information about Sound Access")); + 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())); + m_recentFilesMenu->addAction(action); + } +} + +void +MainWindow::setupRecentTransformsMenu() +{ + m_recentTransformsMenu->clear(); + vector<QString> transforms = m_recentTransforms.getRecent(); + for (size_t i = 0; i < transforms.size(); ++i) { + TransformActionReverseMap::iterator ti = + m_transformActionsReverse.find(transforms[i]); + if (ti == m_transformActionsReverse.end()) { + std::cerr << "WARNING: MainWindow::setupRecentTransformsMenu: " + << "Unknown transform \"" << transforms[i].toStdString() + << "\" in recent transforms list" << std::endl; + continue; + } + m_recentTransformsMenu->addAction(ti->second); + } +} + +void +MainWindow::setupExistingLayersMenus() +{ + if (!m_existingLayersMenu) return; // should have been created by setupMenus + +// std::cerr << "MainWindow::setupExistingLayersMenu" << std::endl; + + m_existingLayersMenu->clear(); + m_existingLayerActions.clear(); + + m_sliceMenu->clear(); + m_sliceActions.clear(); + + vector<Layer *> orderedLayers; + set<Layer *> observedLayers; + set<Layer *> sliceableLayers; + + LayerFactory *factory = LayerFactory::getInstance(); + + for (int i = 0; i < m_paneStack->getPaneCount(); ++i) { + + Pane *pane = m_paneStack->getPane(i); + if (!pane) continue; + + for (int j = 0; j < pane->getLayerCount(); ++j) { + + Layer *layer = pane->getLayer(j); + if (!layer) continue; + if (observedLayers.find(layer) != observedLayers.end()) { +// std::cerr << "found duplicate layer " << layer << std::endl; + continue; + } + +// std::cerr << "found new layer " << layer << " (name = " +// << layer->getLayerPresentationName().toStdString() << ")" << std::endl; + + orderedLayers.push_back(layer); + observedLayers.insert(layer); + + if (factory->isLayerSliceable(layer)) { + sliceableLayers.insert(layer); + } + } + } + + map<QString, int> observedNames; + + for (size_t i = 0; i < orderedLayers.size(); ++i) { + + Layer *layer = orderedLayers[i]; + + QString name = layer->getLayerPresentationName(); + int n = ++observedNames[name]; + if (n > 1) name = QString("%1 <%2>").arg(name).arg(n); + + QIcon icon = QIcon(QString(":/icons/%1.png") + .arg(factory->getLayerIconName + (factory->getLayerType(layer)))); + + QAction *action = new QAction(icon, name, this); + connect(action, SIGNAL(triggered()), this, SLOT(addLayer())); + connect(this, SIGNAL(canAddLayer(bool)), action, SLOT(setEnabled(bool))); + m_existingLayerActions[action] = layer; + + m_existingLayersMenu->addAction(action); + + if (sliceableLayers.find(layer) != sliceableLayers.end()) { + action = new QAction(icon, name, this); + connect(action, SIGNAL(triggered()), this, SLOT(addLayer())); + connect(this, SIGNAL(canAddLayer(bool)), action, SLOT(setEnabled(bool))); + m_sliceActions[action] = layer; + m_sliceMenu->addAction(action); + } + } + + m_sliceMenu->setEnabled(!m_sliceActions.empty()); +} + +void +MainWindow::setupToolbars() +{ + QToolBar *toolbar = addToolBar(tr("Transport Toolbar")); + + QAction *action = toolbar->addAction(QIcon(":/icons/rewind-start.png"), + tr("Rewind to Start")); + action->setShortcut(tr("Home")); + action->setStatusTip(tr("Rewind to the start")); + connect(action, SIGNAL(triggered()), this, SLOT(rewindStart())); + connect(this, SIGNAL(canPlay(bool)), action, SLOT(setEnabled(bool))); + + action = toolbar->addAction(QIcon(":/icons/rewind.png"), + tr("Rewind")); + action->setShortcut(tr("PageUp")); + action->setStatusTip(tr("Rewind to the previous time instant in the current layer")); + connect(action, SIGNAL(triggered()), this, SLOT(rewind())); + connect(this, SIGNAL(canRewind(bool)), action, SLOT(setEnabled(bool))); + + action = toolbar->addAction(QIcon(":/icons/playpause.png"), + tr("Play / Pause")); + action->setCheckable(true); + action->setShortcut(tr("Space")); + action->setStatusTip(tr("Start or stop playback from the current position")); + connect(action, SIGNAL(triggered()), this, SLOT(play())); + connect(m_playSource, SIGNAL(playStatusChanged(bool)), + action, SLOT(setChecked(bool))); + connect(this, SIGNAL(canPlay(bool)), action, SLOT(setEnabled(bool))); + + action = toolbar->addAction(QIcon(":/icons/ffwd.png"), + tr("Fast Forward")); + action->setShortcut(tr("PageDown")); + action->setStatusTip(tr("Fast forward to the next time instant in the current layer")); + connect(action, SIGNAL(triggered()), this, SLOT(ffwd())); + connect(this, SIGNAL(canFfwd(bool)), action, SLOT(setEnabled(bool))); + + action = toolbar->addAction(QIcon(":/icons/ffwd-end.png"), + tr("Fast Forward to End")); + action->setShortcut(tr("End")); + action->setStatusTip(tr("Fast-forward to the end")); + connect(action, SIGNAL(triggered()), this, SLOT(ffwdEnd())); + connect(this, SIGNAL(canPlay(bool)), action, SLOT(setEnabled(bool))); + + toolbar = addToolBar(tr("Play Mode Toolbar")); + + action = toolbar->addAction(QIcon(":/icons/playselection.png"), + tr("Constrain Playback to Selection")); + action->setCheckable(true); + action->setChecked(m_viewManager->getPlaySelectionMode()); + action->setShortcut(tr("s")); + action->setStatusTip(tr("Constrain playback to the selected area")); + connect(m_viewManager, SIGNAL(playSelectionModeChanged(bool)), + action, SLOT(setChecked(bool))); + connect(action, SIGNAL(triggered()), this, SLOT(playSelectionToggled())); + connect(this, SIGNAL(canPlaySelection(bool)), action, SLOT(setEnabled(bool))); + + action = toolbar->addAction(QIcon(":/icons/playloop.png"), + tr("Loop Playback")); + action->setCheckable(true); + action->setChecked(m_viewManager->getPlayLoopMode()); + action->setShortcut(tr("l")); + action->setStatusTip(tr("Loop playback")); + connect(m_viewManager, SIGNAL(playLoopModeChanged(bool)), + action, SLOT(setChecked(bool))); + connect(action, SIGNAL(triggered()), this, SLOT(playLoopToggled())); + connect(this, SIGNAL(canPlay(bool)), action, SLOT(setEnabled(bool))); + + toolbar = addToolBar(tr("Edit Toolbar")); + CommandHistory::getInstance()->registerToolbar(toolbar); + + toolbar = addToolBar(tr("Tools Toolbar")); + QActionGroup *group = new QActionGroup(this); + + action = toolbar->addAction(QIcon(":/icons/navigate.png"), + tr("Navigate")); + action->setCheckable(true); + action->setChecked(true); + action->setShortcut(tr("1")); + action->setStatusTip(tr("Navigate")); + connect(action, SIGNAL(triggered()), this, SLOT(toolNavigateSelected())); + group->addAction(action); + m_toolActions[ViewManager::NavigateMode] = action; + + action = toolbar->addAction(QIcon(":/icons/select.png"), + tr("Select")); + action->setCheckable(true); + action->setShortcut(tr("2")); + action->setStatusTip(tr("Select ranges")); + connect(action, SIGNAL(triggered()), this, SLOT(toolSelectSelected())); + group->addAction(action); + m_toolActions[ViewManager::SelectMode] = action; + + action = toolbar->addAction(QIcon(":/icons/move.png"), + tr("Edit")); + action->setCheckable(true); + action->setShortcut(tr("3")); + action->setStatusTip(tr("Edit items in layer")); + connect(action, SIGNAL(triggered()), this, SLOT(toolEditSelected())); + connect(this, SIGNAL(canEditLayer(bool)), action, SLOT(setEnabled(bool))); + group->addAction(action); + m_toolActions[ViewManager::EditMode] = action; + + action = toolbar->addAction(QIcon(":/icons/draw.png"), + tr("Draw")); + action->setCheckable(true); + action->setShortcut(tr("4")); + action->setStatusTip(tr("Draw new items in layer")); + connect(action, SIGNAL(triggered()), this, SLOT(toolDrawSelected())); + connect(this, SIGNAL(canEditLayer(bool)), action, SLOT(setEnabled(bool))); + group->addAction(action); + m_toolActions[ViewManager::DrawMode] = action; + +// action = toolbar->addAction(QIcon(":/icons/text.png"), +// tr("Text")); +// action->setCheckable(true); +// action->setShortcut(tr("5")); +// connect(action, SIGNAL(triggered()), this, SLOT(toolTextSelected())); +// group->addAction(action); +// m_toolActions[ViewManager::TextMode] = action; + ConnectionStatus *connectionStatus = new ConnectionStatus(m_httpClient, tr("Connection status")); - addToolBar(connectionStatus); - - toolNavigateSelected(); -} - -void -MainWindow::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 haveMainModel = - (getMainModel() != 0); - bool havePlayTarget = - (m_playTarget != 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)); - bool haveCurrentColour3DPlot = - (haveCurrentLayer && - dynamic_cast<Colour3DPlotLayer *>(currentLayer)); - bool haveClipboardContents = - (m_viewManager && - !m_viewManager->getClipboard().empty()); - - emit canAddPane(haveMainModel); - emit canDeleteCurrentPane(haveCurrentPane); - emit canZoom(haveMainModel && haveCurrentPane); - emit canScroll(haveMainModel && haveCurrentPane); - emit canAddLayer(haveMainModel && haveCurrentPane); - emit canImportMoreAudio(haveMainModel); - emit canImportLayer(haveMainModel && haveCurrentPane); - emit canExportAudio(haveMainModel); - emit canExportLayer(haveMainModel && - (haveCurrentEditableLayer || haveCurrentColour3DPlot)); - emit canExportImage(haveMainModel && haveCurrentPane); - emit canDeleteCurrentLayer(haveCurrentLayer); - emit canRenameLayer(haveCurrentLayer); - emit canEditLayer(haveCurrentEditableLayer); - emit canSelect(haveMainModel && haveCurrentPane); - emit canPlay(/*!!! haveMainModel && */ havePlayTarget); - emit canFfwd(haveCurrentTimeInstantsLayer || haveCurrentTimeValueLayer); - emit canRewind(haveCurrentTimeInstantsLayer || haveCurrentTimeValueLayer); - emit canPaste(haveCurrentEditableLayer && haveClipboardContents); - emit canInsertInstant(haveCurrentPane); - emit canInsertInstantsAtBoundaries(haveCurrentPane && haveSelection); - emit canPlaySelection(haveMainModel && havePlayTarget && haveSelection); - emit canClearSelection(haveSelection); - emit canEditSelection(haveSelection && haveCurrentEditableLayer); - emit canSave(m_sessionFile != "" && m_documentModified); - emit canAddFilter(haveMainModel); -} - -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() -{ -// std::cerr << "MainWindow::documentModified" << std::endl; - - if (!m_documentModified) { - setWindowTitle(tr("%1 (modified)").arg(windowTitle())); - } - - m_documentModified = true; - updateMenuStates(); -} - -void -MainWindow::documentRestored() -{ -// std::cerr << "MainWindow::documentRestored" << std::endl; - - if (m_documentModified) { - QString wt(windowTitle()); - wt.replace(tr(" (modified)"), ""); - setWindowTitle(wt); - } - - m_documentModified = false; - updateMenuStates(); -} - -void -MainWindow::playLoopToggled() -{ - QAction *action = dynamic_cast<QAction *>(sender()); - - if (action) { - m_viewManager->setPlayLoopMode(action->isChecked()); - } else { - m_viewManager->setPlayLoopMode(!m_viewManager->getPlayLoopMode()); - } -} - -void -MainWindow::playSelectionToggled() -{ - QAction *action = dynamic_cast<QAction *>(sender()); - - if (action) { - m_viewManager->setPlaySelectionMode(action->isChecked()); - } else { - m_viewManager->setPlaySelectionMode(!m_viewManager->getPlaySelectionMode()); - } -} - -void -MainWindow::currentPaneChanged(Pane *p) -{ - updateMenuStates(); - updateVisibleRangeDisplay(p); - emit newCurrentPane(p); -} - -void -MainWindow::currentLayerChanged(Pane *p, Layer *) -{ - updateMenuStates(); - updateVisibleRangeDisplay(p); -} - -void -MainWindow::toolNavigateSelected() -{ - m_viewManager->setToolMode(ViewManager::NavigateMode); -} - -void -MainWindow::toolSelectSelected() -{ - m_viewManager->setToolMode(ViewManager::SelectMode); -} - -void -MainWindow::toolEditSelected() -{ - m_viewManager->setToolMode(ViewManager::EditMode); -} - -void -MainWindow::toolDrawSelected() -{ - m_viewManager->setToolMode(ViewManager::DrawMode); -} - -//void -//MainWindow::toolTextSelected() -//{ -// m_viewManager->setToolMode(ViewManager::TextMode); -//} - -void -MainWindow::selectAll() -{ - if (!getMainModel()) return; - m_viewManager->setSelection(Selection(getMainModel()->getStartFrame(), - getMainModel()->getEndFrame())); -} - -void -MainWindow::selectToStart() -{ - if (!getMainModel()) return; - m_viewManager->setSelection(Selection(getMainModel()->getStartFrame(), - m_viewManager->getGlobalCentreFrame())); -} - -void -MainWindow::selectToEnd() -{ - if (!getMainModel()) return; - m_viewManager->setSelection(Selection(m_viewManager->getGlobalCentreFrame(), - getMainModel()->getEndFrame())); -} - -void -MainWindow::selectVisible() -{ - Model *model = getMainModel(); - if (!model) return; - - Pane *currentPane = m_paneStack->getCurrentPane(); - if (!currentPane) return; - - size_t startFrame, endFrame; - - if (currentPane->getStartFrame() < 0) startFrame = 0; - else startFrame = currentPane->getStartFrame(); - - if (currentPane->getEndFrame() > model->getEndFrame()) endFrame = model->getEndFrame(); - else endFrame = currentPane->getEndFrame(); - - m_viewManager->setSelection(Selection(startFrame, endFrame)); -} - -void -MainWindow::clearSelection() -{ - m_viewManager->clearSelections(); -} - -void -MainWindow::cut() -{ - Pane *currentPane = m_paneStack->getCurrentPane(); - if (!currentPane) return; - - Layer *layer = currentPane->getSelectedLayer(); - if (!layer) return; - - Clipboard &clipboard = m_viewManager->getClipboard(); - clipboard.clear(); - - MultiSelection::SelectionList selections = m_viewManager->getSelections(); - - CommandHistory::getInstance()->startCompoundOperation(tr("Cut"), true); - - for (MultiSelection::SelectionList::iterator i = selections.begin(); - i != selections.end(); ++i) { - layer->copy(*i, clipboard); - layer->deleteSelection(*i); - } - - CommandHistory::getInstance()->endCompoundOperation(); -} - -void -MainWindow::copy() -{ - Pane *currentPane = m_paneStack->getCurrentPane(); - if (!currentPane) return; - - Layer *layer = currentPane->getSelectedLayer(); - if (!layer) return; - - Clipboard &clipboard = m_viewManager->getClipboard(); - clipboard.clear(); - - MultiSelection::SelectionList selections = m_viewManager->getSelections(); - - for (MultiSelection::SelectionList::iterator i = selections.begin(); - i != selections.end(); ++i) { - layer->copy(*i, clipboard); - } -} - -void -MainWindow::paste() -{ - Pane *currentPane = m_paneStack->getCurrentPane(); - if (!currentPane) return; - - //!!! if we have no current layer, we should create one of the most - // appropriate type - - Layer *layer = currentPane->getSelectedLayer(); - if (!layer) return; - - Clipboard &clipboard = m_viewManager->getClipboard(); - Clipboard::PointList contents = clipboard.getPoints(); -/* - long minFrame = 0; - bool have = false; - for (int i = 0; i < contents.size(); ++i) { - if (!contents[i].haveFrame()) continue; - if (!have || contents[i].getFrame() < minFrame) { - minFrame = contents[i].getFrame(); - have = true; - } - } - - long frameOffset = long(m_viewManager->getGlobalCentreFrame()) - minFrame; - - layer->paste(clipboard, frameOffset); -*/ - layer->paste(clipboard, 0, true); -} - -void -MainWindow::deleteSelected() -{ - if (m_paneStack->getCurrentPane() && - m_paneStack->getCurrentPane()->getSelectedLayer()) { - - MultiSelection::SelectionList selections = - m_viewManager->getSelections(); - - for (MultiSelection::SelectionList::iterator i = selections.begin(); - i != selections.end(); ++i) { - - m_paneStack->getCurrentPane()->getSelectedLayer()->deleteSelection(*i); - } - } -} - -void -MainWindow::insertInstant() -{ - int frame = m_viewManager->getPlaybackFrame(); - insertInstantAt(frame); -} - -void -MainWindow::insertInstantsAtBoundaries() -{ - MultiSelection::SelectionList selections = m_viewManager->getSelections(); - for (MultiSelection::SelectionList::iterator i = selections.begin(); - i != selections.end(); ++i) { - size_t start = i->getStartFrame(); - size_t end = i->getEndFrame(); - if (start != end) { - insertInstantAt(i->getStartFrame()); - insertInstantAt(i->getEndFrame()); - } - } -} - -void -MainWindow::insertInstantAt(size_t frame) -{ - Pane *pane = m_paneStack->getCurrentPane(); - if (!pane) { - return; - } - - Layer *layer = dynamic_cast<TimeInstantLayer *> - (pane->getSelectedLayer()); - - if (!layer) { - for (int i = pane->getLayerCount(); i > 0; --i) { - layer = dynamic_cast<TimeInstantLayer *>(pane->getLayer(i - 1)); - if (layer) break; - } - - if (!layer) { - CommandHistory::getInstance()->startCompoundOperation - (tr("Add Point"), true); - layer = m_document->createEmptyLayer(LayerFactory::TimeInstants); - if (layer) { - m_document->addLayerToView(pane, layer); - m_paneStack->setCurrentLayer(pane, layer); - } - CommandHistory::getInstance()->endCompoundOperation(); - } - } - - if (layer) { - - Model *model = layer->getModel(); - SparseOneDimensionalModel *sodm = dynamic_cast<SparseOneDimensionalModel *> - (model); - - if (sodm) { - SparseOneDimensionalModel::Point point - (frame, QString("%1").arg(sodm->getPointCount() + 1)); - CommandHistory::getInstance()->addCommand - (new SparseOneDimensionalModel::AddPointCommand(sodm, point, - tr("Add Points")), - true, true); // bundled - } - } -} - -void -MainWindow::importAudio() -{ - QString path = getOpenFileName(FileFinder::AudioFile); - - if (path != "") { - if (openAudioFile(path, ReplaceMainModel) == FileOpenFailed) { - QMessageBox::critical(this, tr("Failed to open file"), - tr("Audio file \"%1\" could not be opened").arg(path)); - } - } -} - -void -MainWindow::importMoreAudio() -{ - QString path = getOpenFileName(FileFinder::AudioFile); - - if (path != "") { - if (openAudioFile(path, CreateAdditionalModel) == FileOpenFailed) { - QMessageBox::critical(this, tr("Failed to open file"), - tr("Audio file \"%1\" could not be opened").arg(path)); - } - } -} - -void -MainWindow::exportAudio() -{ - if (!getMainModel()) return; - - QString path = getSaveFileName(FileFinder::AudioFile); - - if (path == "") return; - - bool ok = false; - QString error; - - MultiSelection ms = m_viewManager->getSelection(); - MultiSelection::SelectionList selections = m_viewManager->getSelections(); - - bool multiple = false; - - MultiSelection *selectionToWrite = 0; - - if (selections.size() == 1) { - - QStringList items; - items << tr("Export the selected region only") - << tr("Export the whole audio file"); - - bool ok = false; - QString item = ListInputDialog::getItem - (this, tr("Select region to export"), - tr("Which region from the original audio file do you want to export?"), - items, 0, &ok); - - if (!ok || item.isEmpty()) return; - - if (item == items[0]) selectionToWrite = &ms; - - } else if (selections.size() > 1) { - - QStringList items; - items << tr("Export the selected regions into a single audio file") - << tr("Export the selected regions into separate files") - << tr("Export the whole audio file"); - - QString item = ListInputDialog::getItem - (this, tr("Select region to export"), - tr("Multiple regions of the original audio file are selected.\nWhat do you want to export?"), - items, 0, &ok); - - if (!ok || item.isEmpty()) return; - - if (item == items[0]) { - - selectionToWrite = &ms; - - } else if (item == items[1]) { - - multiple = true; - - int n = 1; - QString base = path; - base.replace(".wav", ""); - - for (MultiSelection::SelectionList::iterator i = selections.begin(); - i != selections.end(); ++i) { - - MultiSelection subms; - subms.setSelection(*i); - - QString subpath = QString("%1.%2.wav").arg(base).arg(n); - ++n; - - if (QFileInfo(subpath).exists()) { - error = tr("Fragment file %1 already exists, aborting").arg(subpath); - break; - } - - WavFileWriter subwriter(subpath, - getMainModel()->getSampleRate(), - getMainModel()->getChannelCount()); - subwriter.writeModel(getMainModel(), &subms); - ok = subwriter.isOK(); - - if (!ok) { - error = subwriter.getError(); - break; - } - } - } - } - - if (!multiple) { - WavFileWriter writer(path, - getMainModel()->getSampleRate(), - getMainModel()->getChannelCount()); - writer.writeModel(getMainModel(), selectionToWrite); - ok = writer.isOK(); - error = writer.getError(); - } - - if (ok) { - if (!multiple) { - m_recentFiles.addFile(path); - } - } else { - QMessageBox::critical(this, tr("Failed to write file"), error); - } -} - -void -MainWindow::importLayer() -{ - Pane *pane = m_paneStack->getCurrentPane(); - - if (!pane) { - // shouldn't happen, as the menu action should have been disabled - std::cerr << "WARNING: MainWindow::importLayer: no current pane" << std::endl; - return; - } - - if (!getMainModel()) { - // shouldn't happen, as the menu action should have been disabled - std::cerr << "WARNING: MainWindow::importLayer: No main model -- hence no default sample rate available" << std::endl; - return; - } - - QString path = getOpenFileName(FileFinder::LayerFile); - - if (path != "") { - - if (openLayerFile(path) == FileOpenFailed) { - QMessageBox::critical(this, tr("Failed to open file"), - tr("File %1 could not be opened.").arg(path)); - return; - } - } -} - -MainWindow::FileOpenStatus -MainWindow::openLayerFile(QString path) -{ - return openLayerFile(path, path); -} - -MainWindow::FileOpenStatus -MainWindow::openLayerFile(QString path, QString location) -{ - Pane *pane = m_paneStack->getCurrentPane(); - - if (!pane) { - // shouldn't happen, as the menu action should have been disabled - std::cerr << "WARNING: MainWindow::openLayerFile: no current pane" << std::endl; - return FileOpenFailed; - } - - if (!getMainModel()) { - // shouldn't happen, as the menu action should have been disabled - std::cerr << "WARNING: MainWindow::openLayerFile: No main model -- hence no default sample rate available" << std::endl; - return FileOpenFailed; - } - - bool realFile = (location == path); - - if (path.endsWith(".svl") || path.endsWith(".xml")) { - - PaneCallback callback(this); - QFile file(path); - - if (!file.open(QIODevice::ReadOnly | QIODevice::Text)) { - std::cerr << "ERROR: MainWindow::openLayerFile(" - << location.toStdString() - << "): Failed to open file for reading" << std::endl; - return FileOpenFailed; - } - - SVFileReader reader(m_document, callback, location); - reader.setCurrentPane(pane); - - QXmlInputSource inputSource(&file); - reader.parse(inputSource); - - if (!reader.isOK()) { - std::cerr << "ERROR: MainWindow::openLayerFile(" - << location.toStdString() - << "): Failed to read XML file: " - << reader.getErrorString().toStdString() << std::endl; - return FileOpenFailed; - } - - m_recentFiles.addFile(location); - - if (realFile) { - registerLastOpenedFilePath(FileFinder::LayerFile, path); // for file dialog - } - - return FileOpenSucceeded; - - } else { - - Model *model = DataFileReaderFactory::load(path, getMainModel()->getSampleRate()); - - if (model) { - - Layer *newLayer = m_document->createImportedLayer(model); - - if (newLayer) { - - m_document->addLayerToView(pane, newLayer); - m_recentFiles.addFile(location); - - if (realFile) { - registerLastOpenedFilePath(FileFinder::LayerFile, path); // for file dialog - } - - return FileOpenSucceeded; - } - } - } - - return FileOpenFailed; -} - -void -MainWindow::exportLayer() -{ - Pane *pane = m_paneStack->getCurrentPane(); - if (!pane) return; - - Layer *layer = pane->getSelectedLayer(); - if (!layer) return; - - Model *model = layer->getModel(); - if (!model) return; - - QString path = getSaveFileName(FileFinder::LayerFile); - - if (path == "") return; - - if (QFileInfo(path).suffix() == "") path += ".svl"; - - QString error; - - if (path.endsWith(".xml") || path.endsWith(".svl")) { - - QFile file(path); - if (!file.open(QIODevice::WriteOnly | QIODevice::Text)) { - error = tr("Failed to open file %1 for writing").arg(path); - } else { - QTextStream out(&file); - out << "<?xml version=\"1.0\" encoding=\"UTF-8\"?>\n" - << "<!DOCTYPE sonic-visualiser>\n" - << "<sv>\n" - << " <data>\n"; - - model->toXml(out, " "); - - out << " </data>\n" - << " <display>\n"; - - layer->toXml(out, " "); - - out << " </display>\n" - << "</sv>\n"; - } - - } else { - - CSVFileWriter writer(path, model, - (path.endsWith(".csv") ? "," : "\t")); - writer.write(); - - if (!writer.isOK()) { - error = writer.getError(); - } - } - - if (error != "") { - QMessageBox::critical(this, tr("Failed to write file"), error); - } else { - m_recentFiles.addFile(path); - } -} - -void -MainWindow::exportImage() -{ - Pane *pane = m_paneStack->getCurrentPane(); - if (!pane) return; - - QString path = getSaveFileName(FileFinder::ImageFile); - - if (path == "") return; - - if (QFileInfo(path).suffix() == "") path += ".png"; - - bool haveSelection = m_viewManager && !m_viewManager->getSelections().empty(); - - QSize total, visible, selected; - total = pane->getImageSize(); - visible = pane->getImageSize(pane->getFirstVisibleFrame(), - pane->getLastVisibleFrame()); - - size_t sf0 = 0, sf1 = 0; - - if (haveSelection) { - MultiSelection::SelectionList selections = m_viewManager->getSelections(); - sf0 = selections.begin()->getStartFrame(); - MultiSelection::SelectionList::iterator e = selections.end(); - --e; - sf1 = e->getEndFrame(); - selected = pane->getImageSize(sf0, sf1); - } - - QStringList items; - items << tr("Export the whole pane (%1x%2 pixels)") - .arg(total.width()).arg(total.height()); - items << tr("Export the visible area only (%1x%2 pixels)") - .arg(visible.width()).arg(visible.height()); - if (haveSelection) { - items << tr("Export the selection extent (%1x%2 pixels)") - .arg(selected.width()).arg(selected.height()); - } else { - items << tr("Export the selection extent"); - } - - QSettings settings; - settings.beginGroup("MainWindow"); - int deflt = settings.value("lastimageexportregion", 0).toInt(); - if (deflt == 2 && !haveSelection) deflt = 1; - if (deflt == 0 && total.width() > 32767) deflt = 1; - - ListInputDialog *lid = new ListInputDialog - (this, tr("Select region to export"), - tr("Which region of the current pane do you want to export as an image?"), - items, deflt); - - if (!haveSelection) { - lid->setItemAvailability(2, false); - } - if (total.width() > 32767) { // appears to be the limit of a QImage - lid->setItemAvailability(0, false); - lid->setFootnote(tr("Note: the whole pane is too wide to be exported as a single image.")); - } - - bool ok = lid->exec(); - QString item = lid->getCurrentString(); - delete lid; - - if (!ok || item.isEmpty()) return; - - settings.setValue("lastimageexportregion", deflt); - - QImage *image = 0; - - if (item == items[0]) { - image = pane->toNewImage(); - } else if (item == items[1]) { - image = pane->toNewImage(pane->getFirstVisibleFrame(), - pane->getLastVisibleFrame()); - } else if (haveSelection) { - image = pane->toNewImage(sf0, sf1); - } - - if (!image) return; - - if (!image->save(path, "PNG")) { - QMessageBox::critical(this, tr("Failed to save image file"), - tr("Failed to save image file %1").arg(path)); - } - - delete image; -} - -MainWindow::FileOpenStatus -MainWindow::openAudioFile(QString path, AudioFileOpenMode mode) -{ - return openAudioFile(path, path, mode); -} - -MainWindow::FileOpenStatus -MainWindow::openAudioFile(QString path, QString location, AudioFileOpenMode mode) -{ - if (!(QFileInfo(path).exists() && - QFileInfo(path).isFile() && - QFileInfo(path).isReadable())) { - return FileOpenFailed; - } - - m_openingAudioFile = true; - - WaveFileModel *newModel = new WaveFileModel(path, location); - - if (!newModel->isOK()) { - delete newModel; - m_openingAudioFile = false; - return FileOpenFailed; - } - - bool setAsMain = true; - static bool prevSetAsMain = true; - - bool realFile = (location == path); - - if (mode == CreateAdditionalModel) setAsMain = false; - else if (mode == AskUser) { - if (m_document->getMainModel()) { - - QStringList items; - items << tr("Replace the existing main waveform") - << tr("Load this file into a new waveform pane"); - - bool ok = false; - QString item = ListInputDialog::getItem - (this, tr("Select target for import"), - tr("You already have an audio waveform loaded.\nWhat would you like to do with the new audio file?"), - items, prevSetAsMain ? 0 : 1, &ok); - - if (!ok || item.isEmpty()) { - delete newModel; - m_openingAudioFile = false; - return FileOpenCancelled; - } - - setAsMain = (item == items[0]); - prevSetAsMain = setAsMain; - } - } - - if (setAsMain) { - - Model *prevMain = getMainModel(); - if (prevMain) { - m_playSource->removeModel(prevMain); - PlayParameterRepository::getInstance()->removeModel(prevMain); - } - - PlayParameterRepository::getInstance()->addModel(newModel); - - m_document->setMainModel(newModel); - setupMenus(); - - if (m_sessionFile == "") { - setWindowTitle(tr("Sound Access: %1") - .arg(QFileInfo(location).fileName())); - CommandHistory::getInstance()->clear(); - CommandHistory::getInstance()->documentSaved(); - m_documentModified = false; - } else { - setWindowTitle(tr("Sound Access: %1 [%2]") - .arg(QFileInfo(m_sessionFile).fileName()) - .arg(QFileInfo(location).fileName())); - if (m_documentModified) { - m_documentModified = false; - documentModified(); // so as to restore "(modified)" window title - } - } - - if (realFile) m_audioFile = path; - - } else { // !setAsMain - - CommandHistory::getInstance()->startCompoundOperation - (tr("Import \"%1\"").arg(QFileInfo(location).fileName()), true); - - m_document->addImportedModel(newModel); - - AddPaneCommand *command = new AddPaneCommand(this); - CommandHistory::getInstance()->addCommand(command); - - Pane *pane = command->getPane(); - - if (!m_timeRulerLayer) { - m_timeRulerLayer = m_document->createMainModelLayer - (LayerFactory::TimeRuler); - } - - m_document->addLayerToView(pane, m_timeRulerLayer); - - Layer *newLayer = m_document->createImportedLayer(newModel); - - if (newLayer) { - m_document->addLayerToView(pane, newLayer); - } - - CommandHistory::getInstance()->endCompoundOperation(); - } - - updateMenuStates(); - m_recentFiles.addFile(location); - if (realFile) { - registerLastOpenedFilePath(FileFinder::AudioFile, path); // for file dialog - } - m_openingAudioFile = false; - - return FileOpenSucceeded; -} - -void -MainWindow::createPlayTarget() -{ - if (m_playTarget) return; - - m_playTarget = AudioTargetFactory::createCallbackTarget(m_playSource); - if (!m_playTarget) { - QMessageBox::warning - (this, tr("Couldn't open audio device"), - tr("Could not open an audio device for playback.\nAudio playback will not be available during this session.\n"), - QMessageBox::Ok, 0); - } - connect(m_fader, SIGNAL(valueChanged(float)), - m_playTarget, SLOT(setOutputGain(float))); -} - -WaveFileModel * -MainWindow::getMainModel() -{ - if (!m_document) return 0; - return m_document->getMainModel(); -} - -const WaveFileModel * -MainWindow::getMainModel() const -{ - if (!m_document) return 0; - return m_document->getMainModel(); -} - -void -MainWindow::newSession() -{ - if (!checkSaveModified()) return; - - closeSession(); - createDocument(); - createMultiPaneLayerContainer(); - - Pane *pane = m_paneStack->addPane(); - - connect(pane, SIGNAL(contextHelpChanged(const QString &)), - this, SLOT(contextHelpChanged(const QString &))); - - if (!m_timeRulerLayer) { - m_timeRulerLayer = m_document->createMainModelLayer - (LayerFactory::TimeRuler); - } - - m_document->addLayerToView(pane, m_timeRulerLayer); - - Layer *waveform = m_document->createMainModelLayer(LayerFactory::Waveform); - m_document->addLayerToView(pane, waveform); - - m_overview->registerView(pane); - - m_EasaierManager->newSession(); - - CommandHistory::getInstance()->clear(); - CommandHistory::getInstance()->documentSaved(); - documentRestored(); - updateMenuStates(); + addToolBar(connectionStatus); + + toolNavigateSelected(); +} + +void +MainWindow::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 haveMainModel = + (getMainModel() != 0); + bool havePlayTarget = + (m_playTarget != 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)); + bool haveCurrentColour3DPlot = + (haveCurrentLayer && + dynamic_cast<Colour3DPlotLayer *>(currentLayer)); + bool haveClipboardContents = + (m_viewManager && + !m_viewManager->getClipboard().empty()); + + emit canAddPane(haveMainModel); + emit canDeleteCurrentPane(haveCurrentPane); + emit canZoom(haveMainModel && haveCurrentPane); + emit canScroll(haveMainModel && haveCurrentPane); + emit canAddLayer(haveMainModel && haveCurrentPane); + emit canImportMoreAudio(haveMainModel); + emit canImportLayer(haveMainModel && haveCurrentPane); + emit canExportAudio(haveMainModel); + emit canExportLayer(haveMainModel && + (haveCurrentEditableLayer || haveCurrentColour3DPlot)); + emit canExportImage(haveMainModel && haveCurrentPane); + emit canDeleteCurrentLayer(haveCurrentLayer); + emit canRenameLayer(haveCurrentLayer); + emit canEditLayer(haveCurrentEditableLayer); + emit canSelect(haveMainModel && haveCurrentPane); + emit canPlay(/*!!! haveMainModel && */ havePlayTarget); + emit canFfwd(haveCurrentTimeInstantsLayer || haveCurrentTimeValueLayer); + emit canRewind(haveCurrentTimeInstantsLayer || haveCurrentTimeValueLayer); + emit canPaste(haveCurrentEditableLayer && haveClipboardContents); + emit canInsertInstant(haveCurrentPane); + emit canInsertInstantsAtBoundaries(haveCurrentPane && haveSelection); + emit canPlaySelection(haveMainModel && havePlayTarget && haveSelection); + emit canClearSelection(haveSelection); + emit canEditSelection(haveSelection && haveCurrentEditableLayer); + emit canSave(m_sessionFile != "" && m_documentModified); + emit canAddFilter(haveMainModel); +} + +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() +{ +// std::cerr << "MainWindow::documentModified" << std::endl; + + if (!m_documentModified) { + setWindowTitle(tr("%1 (modified)").arg(windowTitle())); + } + + m_documentModified = true; + updateMenuStates(); +} + +void +MainWindow::documentRestored() +{ +// std::cerr << "MainWindow::documentRestored" << std::endl; + + if (m_documentModified) { + QString wt(windowTitle()); + wt.replace(tr(" (modified)"), ""); + setWindowTitle(wt); + } + + m_documentModified = false; + updateMenuStates(); +} + +void +MainWindow::playLoopToggled() +{ + QAction *action = dynamic_cast<QAction *>(sender()); + + if (action) { + m_viewManager->setPlayLoopMode(action->isChecked()); + } else { + m_viewManager->setPlayLoopMode(!m_viewManager->getPlayLoopMode()); + } +} + +void +MainWindow::playSelectionToggled() +{ + QAction *action = dynamic_cast<QAction *>(sender()); + + if (action) { + m_viewManager->setPlaySelectionMode(action->isChecked()); + } else { + m_viewManager->setPlaySelectionMode(!m_viewManager->getPlaySelectionMode()); + } +} + +void +MainWindow::currentPaneChanged(Pane *p) +{ + updateMenuStates(); + updateVisibleRangeDisplay(p); + emit newCurrentPane(p); +} + +void +MainWindow::currentLayerChanged(Pane *p, Layer *) +{ + updateMenuStates(); + updateVisibleRangeDisplay(p); +} + +void +MainWindow::toolNavigateSelected() +{ + m_viewManager->setToolMode(ViewManager::NavigateMode); +} + +void +MainWindow::toolSelectSelected() +{ + m_viewManager->setToolMode(ViewManager::SelectMode); +} + +void +MainWindow::toolEditSelected() +{ + m_viewManager->setToolMode(ViewManager::EditMode); +} + +void +MainWindow::toolDrawSelected() +{ + m_viewManager->setToolMode(ViewManager::DrawMode); +} + +//void +//MainWindow::toolTextSelected() +//{ +// m_viewManager->setToolMode(ViewManager::TextMode); +//} + +void +MainWindow::selectAll() +{ + if (!getMainModel()) return; + m_viewManager->setSelection(Selection(getMainModel()->getStartFrame(), + getMainModel()->getEndFrame())); +} + +void +MainWindow::selectToStart() +{ + if (!getMainModel()) return; + m_viewManager->setSelection(Selection(getMainModel()->getStartFrame(), + m_viewManager->getGlobalCentreFrame())); +} + +void +MainWindow::selectToEnd() +{ + if (!getMainModel()) return; + m_viewManager->setSelection(Selection(m_viewManager->getGlobalCentreFrame(), + getMainModel()->getEndFrame())); +} + +void +MainWindow::selectVisible() +{ + Model *model = getMainModel(); + if (!model) return; + + Pane *currentPane = m_paneStack->getCurrentPane(); + if (!currentPane) return; + + size_t startFrame, endFrame; + + if (currentPane->getStartFrame() < 0) startFrame = 0; + else startFrame = currentPane->getStartFrame(); + + if (currentPane->getEndFrame() > model->getEndFrame()) endFrame = model->getEndFrame(); + else endFrame = currentPane->getEndFrame(); + + m_viewManager->setSelection(Selection(startFrame, endFrame)); +} + +void +MainWindow::clearSelection() +{ + m_viewManager->clearSelections(); +} + +void +MainWindow::cut() +{ + Pane *currentPane = m_paneStack->getCurrentPane(); + if (!currentPane) return; + + Layer *layer = currentPane->getSelectedLayer(); + if (!layer) return; + + Clipboard &clipboard = m_viewManager->getClipboard(); + clipboard.clear(); + + MultiSelection::SelectionList selections = m_viewManager->getSelections(); + + CommandHistory::getInstance()->startCompoundOperation(tr("Cut"), true); + + for (MultiSelection::SelectionList::iterator i = selections.begin(); + i != selections.end(); ++i) { + layer->copy(*i, clipboard); + layer->deleteSelection(*i); + } + + CommandHistory::getInstance()->endCompoundOperation(); +} + +void +MainWindow::copy() +{ + Pane *currentPane = m_paneStack->getCurrentPane(); + if (!currentPane) return; + + Layer *layer = currentPane->getSelectedLayer(); + if (!layer) return; + + Clipboard &clipboard = m_viewManager->getClipboard(); + clipboard.clear(); + + MultiSelection::SelectionList selections = m_viewManager->getSelections(); + + for (MultiSelection::SelectionList::iterator i = selections.begin(); + i != selections.end(); ++i) { + layer->copy(*i, clipboard); + } +} + +void +MainWindow::paste() +{ + Pane *currentPane = m_paneStack->getCurrentPane(); + if (!currentPane) return; + + //!!! if we have no current layer, we should create one of the most + // appropriate type + + Layer *layer = currentPane->getSelectedLayer(); + if (!layer) return; + + Clipboard &clipboard = m_viewManager->getClipboard(); + Clipboard::PointList contents = clipboard.getPoints(); +/* + long minFrame = 0; + bool have = false; + for (int i = 0; i < contents.size(); ++i) { + if (!contents[i].haveFrame()) continue; + if (!have || contents[i].getFrame() < minFrame) { + minFrame = contents[i].getFrame(); + have = true; + } + } + + long frameOffset = long(m_viewManager->getGlobalCentreFrame()) - minFrame; + + layer->paste(clipboard, frameOffset); +*/ + layer->paste(clipboard, 0, true); +} + +void +MainWindow::deleteSelected() +{ + if (m_paneStack->getCurrentPane() && + m_paneStack->getCurrentPane()->getSelectedLayer()) { + + MultiSelection::SelectionList selections = + m_viewManager->getSelections(); + + for (MultiSelection::SelectionList::iterator i = selections.begin(); + i != selections.end(); ++i) { + + m_paneStack->getCurrentPane()->getSelectedLayer()->deleteSelection(*i); + } + } +} + +void +MainWindow::insertInstant() +{ + int frame = m_viewManager->getPlaybackFrame(); + insertInstantAt(frame); +} + +void +MainWindow::insertInstantsAtBoundaries() +{ + MultiSelection::SelectionList selections = m_viewManager->getSelections(); + for (MultiSelection::SelectionList::iterator i = selections.begin(); + i != selections.end(); ++i) { + size_t start = i->getStartFrame(); + size_t end = i->getEndFrame(); + if (start != end) { + insertInstantAt(i->getStartFrame()); + insertInstantAt(i->getEndFrame()); + } + } +} + +void +MainWindow::insertInstantAt(size_t frame) +{ + Pane *pane = m_paneStack->getCurrentPane(); + if (!pane) { + return; + } + + Layer *layer = dynamic_cast<TimeInstantLayer *> + (pane->getSelectedLayer()); + + if (!layer) { + for (int i = pane->getLayerCount(); i > 0; --i) { + layer = dynamic_cast<TimeInstantLayer *>(pane->getLayer(i - 1)); + if (layer) break; + } + + if (!layer) { + CommandHistory::getInstance()->startCompoundOperation + (tr("Add Point"), true); + layer = m_document->createEmptyLayer(LayerFactory::TimeInstants); + if (layer) { + m_document->addLayerToView(pane, layer); + m_paneStack->setCurrentLayer(pane, layer); + } + CommandHistory::getInstance()->endCompoundOperation(); + } + } + + if (layer) { + + Model *model = layer->getModel(); + SparseOneDimensionalModel *sodm = dynamic_cast<SparseOneDimensionalModel *> + (model); + + if (sodm) { + SparseOneDimensionalModel::Point point + (frame, QString("%1").arg(sodm->getPointCount() + 1)); + CommandHistory::getInstance()->addCommand + (new SparseOneDimensionalModel::AddPointCommand(sodm, point, + tr("Add Points")), + true, true); // bundled + } + } +} + +void +MainWindow::importAudio() +{ + QString path = getOpenFileName(FileFinder::AudioFile); + + if (path != "") { + if (openAudioFile(path, ReplaceMainModel) == FileOpenFailed) { + QMessageBox::critical(this, tr("Failed to open file"), + tr("Audio file \"%1\" could not be opened").arg(path)); + } + } +} + +void +MainWindow::importMoreAudio() +{ + QString path = getOpenFileName(FileFinder::AudioFile); + + if (path != "") { + if (openAudioFile(path, CreateAdditionalModel) == FileOpenFailed) { + QMessageBox::critical(this, tr("Failed to open file"), + tr("Audio file \"%1\" could not be opened").arg(path)); + } + } +} + +void +MainWindow::exportAudio() +{ + if (!getMainModel()) return; + + QString path = getSaveFileName(FileFinder::AudioFile); + + if (path == "") return; + + bool ok = false; + QString error; + + MultiSelection ms = m_viewManager->getSelection(); + MultiSelection::SelectionList selections = m_viewManager->getSelections(); + + bool multiple = false; + + MultiSelection *selectionToWrite = 0; + + if (selections.size() == 1) { + + QStringList items; + items << tr("Export the selected region only") + << tr("Export the whole audio file"); + + bool ok = false; + QString item = ListInputDialog::getItem + (this, tr("Select region to export"), + tr("Which region from the original audio file do you want to export?"), + items, 0, &ok); + + if (!ok || item.isEmpty()) return; + + if (item == items[0]) selectionToWrite = &ms; + + } else if (selections.size() > 1) { + + QStringList items; + items << tr("Export the selected regions into a single audio file") + << tr("Export the selected regions into separate files") + << tr("Export the whole audio file"); + + QString item = ListInputDialog::getItem + (this, tr("Select region to export"), + tr("Multiple regions of the original audio file are selected.\nWhat do you want to export?"), + items, 0, &ok); + + if (!ok || item.isEmpty()) return; + + if (item == items[0]) { + + selectionToWrite = &ms; + + } else if (item == items[1]) { + + multiple = true; + + int n = 1; + QString base = path; + base.replace(".wav", ""); + + for (MultiSelection::SelectionList::iterator i = selections.begin(); + i != selections.end(); ++i) { + + MultiSelection subms; + subms.setSelection(*i); + + QString subpath = QString("%1.%2.wav").arg(base).arg(n); + ++n; + + if (QFileInfo(subpath).exists()) { + error = tr("Fragment file %1 already exists, aborting").arg(subpath); + break; + } + + WavFileWriter subwriter(subpath, + getMainModel()->getSampleRate(), + getMainModel()->getChannelCount()); + subwriter.writeModel(getMainModel(), &subms); + ok = subwriter.isOK(); + + if (!ok) { + error = subwriter.getError(); + break; + } + } + } + } + + if (!multiple) { + WavFileWriter writer(path, + getMainModel()->getSampleRate(), + getMainModel()->getChannelCount()); + writer.writeModel(getMainModel(), selectionToWrite); + ok = writer.isOK(); + error = writer.getError(); + } + + if (ok) { + if (!multiple) { + m_recentFiles.addFile(path); + } + } else { + QMessageBox::critical(this, tr("Failed to write file"), error); + } +} + +void +// Ivan Damnjanovic 09-10/2007 added video file import +MainWindow::importVideo() +{ + QString path = getOpenFileName(FileFinder::VideoFile); + + if (path != "") { + if (openVideoFile(path, ReplaceMainModel) == FileOpenFailed) { + QMessageBox::critical(this, tr("Failed to open file"), + tr("Video file \"%1\" could not be opened").arg(path)); + } + } +} +// +void +MainWindow::importLayer() +{ + Pane *pane = m_paneStack->getCurrentPane(); + + if (!pane) { + // shouldn't happen, as the menu action should have been disabled + std::cerr << "WARNING: MainWindow::importLayer: no current pane" << std::endl; + return; + } + + if (!getMainModel()) { + // shouldn't happen, as the menu action should have been disabled + std::cerr << "WARNING: MainWindow::importLayer: No main model -- hence no default sample rate available" << std::endl; + return; + } + + QString path = getOpenFileName(FileFinder::LayerFile); + + if (path != "") { + + if (openLayerFile(path) == FileOpenFailed) { + QMessageBox::critical(this, tr("Failed to open file"), + tr("File %1 could not be opened.").arg(path)); + return; + } + } +} + +MainWindow::FileOpenStatus +MainWindow::openLayerFile(QString path) +{ + return openLayerFile(path, path); +} + +MainWindow::FileOpenStatus +MainWindow::openLayerFile(QString path, QString location) +{ + Pane *pane = m_paneStack->getCurrentPane(); + + if (!pane) { + // shouldn't happen, as the menu action should have been disabled + std::cerr << "WARNING: MainWindow::openLayerFile: no current pane" << std::endl; + return FileOpenFailed; + } + + if (!getMainModel()) { + // shouldn't happen, as the menu action should have been disabled + std::cerr << "WARNING: MainWindow::openLayerFile: No main model -- hence no default sample rate available" << std::endl; + return FileOpenFailed; + } + + bool realFile = (location == path); + + if (path.endsWith(".svl") || path.endsWith(".xml")) { + + PaneCallback callback(this); + QFile file(path); + + if (!file.open(QIODevice::ReadOnly | QIODevice::Text)) { + std::cerr << "ERROR: MainWindow::openLayerFile(" + << location.toStdString() + << "): Failed to open file for reading" << std::endl; + return FileOpenFailed; + } + + SVFileReader reader(m_document, callback, location); + reader.setCurrentPane(pane); + + QXmlInputSource inputSource(&file); + reader.parse(inputSource); + + if (!reader.isOK()) { + std::cerr << "ERROR: MainWindow::openLayerFile(" + << location.toStdString() + << "): Failed to read XML file: " + << reader.getErrorString().toStdString() << std::endl; + return FileOpenFailed; + } + + m_recentFiles.addFile(location); + + if (realFile) { + registerLastOpenedFilePath(FileFinder::LayerFile, path); // for file dialog + } + + return FileOpenSucceeded; + + } else { + + Model *model = DataFileReaderFactory::load(path, getMainModel()->getSampleRate()); + + if (model) { + + Layer *newLayer = m_document->createImportedLayer(model); + + if (newLayer) { + + m_document->addLayerToView(pane, newLayer); + m_recentFiles.addFile(location); + + if (realFile) { + registerLastOpenedFilePath(FileFinder::LayerFile, path); // for file dialog + } + + return FileOpenSucceeded; + } + } + } + + return FileOpenFailed; +} + +void +MainWindow::exportLayer() +{ + Pane *pane = m_paneStack->getCurrentPane(); + if (!pane) return; + + Layer *layer = pane->getSelectedLayer(); + if (!layer) return; + + Model *model = layer->getModel(); + if (!model) return; + + QString path = getSaveFileName(FileFinder::LayerFile); + + if (path == "") return; + + if (QFileInfo(path).suffix() == "") path += ".svl"; + + QString error; + + if (path.endsWith(".xml") || path.endsWith(".svl")) { + + QFile file(path); + if (!file.open(QIODevice::WriteOnly | QIODevice::Text)) { + error = tr("Failed to open file %1 for writing").arg(path); + } else { + QTextStream out(&file); + out << "<?xml version=\"1.0\" encoding=\"UTF-8\"?>\n" + << "<!DOCTYPE sonic-visualiser>\n" + << "<sv>\n" + << " <data>\n"; + + model->toXml(out, " "); + + out << " </data>\n" + << " <display>\n"; + + layer->toXml(out, " "); + + out << " </display>\n" + << "</sv>\n"; + } + + } else { + + CSVFileWriter writer(path, model, + (path.endsWith(".csv") ? "," : "\t")); + writer.write(); + + if (!writer.isOK()) { + error = writer.getError(); + } + } + + if (error != "") { + QMessageBox::critical(this, tr("Failed to write file"), error); + } else { + m_recentFiles.addFile(path); + } +} + +void +MainWindow::exportImage() +{ + Pane *pane = m_paneStack->getCurrentPane(); + if (!pane) return; + + QString path = getSaveFileName(FileFinder::ImageFile); + + if (path == "") return; + + if (QFileInfo(path).suffix() == "") path += ".png"; + + bool haveSelection = m_viewManager && !m_viewManager->getSelections().empty(); + + QSize total, visible, selected; + total = pane->getImageSize(); + visible = pane->getImageSize(pane->getFirstVisibleFrame(), + pane->getLastVisibleFrame()); + + size_t sf0 = 0, sf1 = 0; + + if (haveSelection) { + MultiSelection::SelectionList selections = m_viewManager->getSelections(); + sf0 = selections.begin()->getStartFrame(); + MultiSelection::SelectionList::iterator e = selections.end(); + --e; + sf1 = e->getEndFrame(); + selected = pane->getImageSize(sf0, sf1); + } + + QStringList items; + items << tr("Export the whole pane (%1x%2 pixels)") + .arg(total.width()).arg(total.height()); + items << tr("Export the visible area only (%1x%2 pixels)") + .arg(visible.width()).arg(visible.height()); + if (haveSelection) { + items << tr("Export the selection extent (%1x%2 pixels)") + .arg(selected.width()).arg(selected.height()); + } else { + items << tr("Export the selection extent"); + } + + QSettings settings; + settings.beginGroup("MainWindow"); + int deflt = settings.value("lastimageexportregion", 0).toInt(); + if (deflt == 2 && !haveSelection) deflt = 1; + if (deflt == 0 && total.width() > 32767) deflt = 1; + + ListInputDialog *lid = new ListInputDialog + (this, tr("Select region to export"), + tr("Which region of the current pane do you want to export as an image?"), + items, deflt); + + if (!haveSelection) { + lid->setItemAvailability(2, false); + } + if (total.width() > 32767) { // appears to be the limit of a QImage + lid->setItemAvailability(0, false); + lid->setFootnote(tr("Note: the whole pane is too wide to be exported as a single image.")); + } + + bool ok = lid->exec(); + QString item = lid->getCurrentString(); + delete lid; + + if (!ok || item.isEmpty()) return; + + settings.setValue("lastimageexportregion", deflt); + + QImage *image = 0; + + if (item == items[0]) { + image = pane->toNewImage(); + } else if (item == items[1]) { + image = pane->toNewImage(pane->getFirstVisibleFrame(), + pane->getLastVisibleFrame()); + } else if (haveSelection) { + image = pane->toNewImage(sf0, sf1); + } + + if (!image) return; + + if (!image->save(path, "PNG")) { + QMessageBox::critical(this, tr("Failed to save image file"), + tr("Failed to save image file %1").arg(path)); + } + + delete image; +} + +// Ivan Damnjanovic 09-10/2007 added video file import +MainWindow::FileOpenStatus +MainWindow::openVideoFile(QString path, AudioFileOpenMode mode) +{ + return openAudioFile(path, path, mode); +} +// +MainWindow::FileOpenStatus +MainWindow::openAudioFile(QString path, AudioFileOpenMode mode) +{ + return openAudioFile(path, path, mode); +} + +MainWindow::FileOpenStatus +MainWindow::openAudioFile(QString path, QString location, AudioFileOpenMode mode) +{ + if (!(QFileInfo(path).exists() && + QFileInfo(path).isFile() && + QFileInfo(path).isReadable())) { + return FileOpenFailed; + } + + m_openingAudioFile = true; + + WaveFileModel *newModel = new WaveFileModel(path, location); + + if (!newModel->isOK()) { + delete newModel; + m_openingAudioFile = false; + return FileOpenFailed; + } + + bool setAsMain = true; + static bool prevSetAsMain = true; + + bool realFile = (location == path); + + if (mode == CreateAdditionalModel) setAsMain = false; + else if (mode == AskUser) { + if (m_document->getMainModel()) { + + QStringList items; + items << tr("Replace the existing main waveform") + << tr("Load this file into a new waveform pane"); + + bool ok = false; + QString item = ListInputDialog::getItem + (this, tr("Select target for import"), + tr("You already have an audio waveform loaded.\nWhat would you like to do with the new audio file?"), + items, prevSetAsMain ? 0 : 1, &ok); + + if (!ok || item.isEmpty()) { + delete newModel; + m_openingAudioFile = false; + return FileOpenCancelled; + } + + setAsMain = (item == items[0]); + prevSetAsMain = setAsMain; + } + } + + if (setAsMain) { + + Model *prevMain = getMainModel(); + if (prevMain) { + m_playSource->removeModel(prevMain); + PlayParameterRepository::getInstance()->removeModel(prevMain); + } + + PlayParameterRepository::getInstance()->addModel(newModel); + + m_document->setMainModel(newModel); + setupMenus(); + + if (m_sessionFile == "") { + setWindowTitle(tr("Sound Access: %1") + .arg(QFileInfo(location).fileName())); + CommandHistory::getInstance()->clear(); + CommandHistory::getInstance()->documentSaved(); + m_documentModified = false; + } else { + setWindowTitle(tr("Sound Access: %1 [%2]") + .arg(QFileInfo(m_sessionFile).fileName()) + .arg(QFileInfo(location).fileName())); + if (m_documentModified) { + m_documentModified = false; + documentModified(); // so as to restore "(modified)" window title + } + } + + if (realFile) m_audioFile = path; + + } else { // !setAsMain + + CommandHistory::getInstance()->startCompoundOperation + (tr("Import \"%1\"").arg(QFileInfo(location).fileName()), true); + + m_document->addImportedModel(newModel); + + AddPaneCommand *command = new AddPaneCommand(this); + CommandHistory::getInstance()->addCommand(command); + + Pane *pane = command->getPane(); + + if (!m_timeRulerLayer) { + m_timeRulerLayer = m_document->createMainModelLayer + (LayerFactory::TimeRuler); + } + + m_document->addLayerToView(pane, m_timeRulerLayer); + + Layer *newLayer = m_document->createImportedLayer(newModel); + + if (newLayer) { + m_document->addLayerToView(pane, newLayer); + } + + CommandHistory::getInstance()->endCompoundOperation(); + } + + updateMenuStates(); + m_recentFiles.addFile(location); + if (realFile) { + registerLastOpenedFilePath(FileFinder::AudioFile, path); // for file dialog + } + m_openingAudioFile = false; + + return FileOpenSucceeded; +} + +void +MainWindow::createPlayTarget() +{ + if (m_playTarget) return; + + m_playTarget = AudioTargetFactory::createCallbackTarget(m_playSource); + if (!m_playTarget) { + QMessageBox::warning + (this, tr("Couldn't open audio device"), + tr("Could not open an audio device for playback.\nAudio playback will not be available during this session.\n"), + QMessageBox::Ok, 0); + } + connect(m_fader, SIGNAL(valueChanged(float)), + m_playTarget, SLOT(setOutputGain(float))); +} + +WaveFileModel * +MainWindow::getMainModel() +{ + if (!m_document) return 0; + return m_document->getMainModel(); +} + +const WaveFileModel * +MainWindow::getMainModel() const +{ + if (!m_document) return 0; + return m_document->getMainModel(); +} + +void +MainWindow::newSession() +{ + if (!checkSaveModified()) return; + + closeSession(); + createDocument(); + createMultiPaneLayerContainer(); + + Pane *pane = m_paneStack->addPane(); + + connect(pane, SIGNAL(contextHelpChanged(const QString &)), + this, SLOT(contextHelpChanged(const QString &))); + + if (!m_timeRulerLayer) { + m_timeRulerLayer = m_document->createMainModelLayer + (LayerFactory::TimeRuler); + } + + m_document->addLayerToView(pane, m_timeRulerLayer); + + Layer *waveform = m_document->createMainModelLayer(LayerFactory::Waveform); + m_document->addLayerToView(pane, waveform); + + m_overview->registerView(pane); + + m_EasaierManager->newSession(); + + CommandHistory::getInstance()->clear(); + CommandHistory::getInstance()->documentSaved(); + documentRestored(); + updateMenuStates(); //m_properties = new PropertyStack(0, m_paneStack->getCurrentPane()); //connect(m_properties, SIGNAL(removeSelectedItem()), this, SLOT(deleteCurrentLayer())); - //m_toolBox->insertItem(0,"Layers", m_properties); -} - -void -MainWindow::createDocument() -{ - m_document = new Document; - - connect(m_document, SIGNAL(layerAdded(Layer *)), - this, SLOT(layerAdded(Layer *))); - connect(m_document, SIGNAL(layerRemoved(Layer *)), - this, SLOT(layerRemoved(Layer *))); - connect(m_document, SIGNAL(layerAboutToBeDeleted(Layer *)), - this, SLOT(layerAboutToBeDeleted(Layer *))); - connect(m_document, SIGNAL(layerInAView(Layer *, bool)), - this, SLOT(layerInAView(Layer *, bool))); - - connect(m_document, SIGNAL(modelAdded(Model *)), - this, SLOT(modelAdded(Model *))); - connect(m_document, SIGNAL(mainModelChanged(WaveFileModel *)), - this, SLOT(mainModelChanged(WaveFileModel *))); - connect(m_document, SIGNAL(modelAboutToBeDeleted(Model *)), - this, SLOT(modelAboutToBeDeleted(Model *))); - + //m_toolBox->insertItem(0,"Layers", m_properties); +} + +void +MainWindow::createDocument() +{ + m_document = new Document; + + connect(m_document, SIGNAL(layerAdded(Layer *)), + this, SLOT(layerAdded(Layer *))); + connect(m_document, SIGNAL(layerRemoved(Layer *)), + this, SLOT(layerRemoved(Layer *))); + connect(m_document, SIGNAL(layerAboutToBeDeleted(Layer *)), + this, SLOT(layerAboutToBeDeleted(Layer *))); + connect(m_document, SIGNAL(layerInAView(Layer *, bool)), + this, SLOT(layerInAView(Layer *, bool))); + + connect(m_document, SIGNAL(modelAdded(Model *)), + this, SLOT(modelAdded(Model *))); + connect(m_document, SIGNAL(mainModelChanged(WaveFileModel *)), + this, SLOT(mainModelChanged(WaveFileModel *))); + connect(m_document, SIGNAL(modelAboutToBeDeleted(Model *)), + this, SLOT(modelAboutToBeDeleted(Model *))); + connect(m_document, SIGNAL(audioSourceInfoAdded(AudioSourceInfoModel *)), - this, SLOT(audioSourceInfoAdded(AudioSourceInfoModel *))); - - connect(m_document, SIGNAL(modelGenerationFailed(QString)), - this, SLOT(modelGenerationFailed(QString))); - connect(m_document, SIGNAL(modelRegenerationFailed(QString, QString)), - this, SLOT(modelRegenerationFailed(QString, QString))); - - connect(m_document, SIGNAL(newFilterAdded(Filter *)), - m_filterPropertyStack, SLOT(filterAdded(Filter *))); - connect(m_document, SIGNAL(filterRemoved(QString)), - m_filterPropertyStack, SLOT(filterRemoved(QString))); - - m_playSource->setRealTimeFilterStack(m_document->getRealTimeFilterStack()); - -} - -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); - } - - if (m_document) - m_document->removeAllFilters(); - + this, SLOT(audioSourceInfoAdded(AudioSourceInfoModel *))); + + connect(m_document, SIGNAL(modelGenerationFailed(QString)), + this, SLOT(modelGenerationFailed(QString))); + connect(m_document, SIGNAL(modelRegenerationFailed(QString, QString)), + this, SLOT(modelRegenerationFailed(QString, QString))); + + connect(m_document, SIGNAL(newFilterAdded(Filter *)), + m_filterPropertyStack, SLOT(filterAdded(Filter *))); + connect(m_document, SIGNAL(filterRemoved(QString)), + m_filterPropertyStack, SLOT(filterRemoved(QString))); + + m_playSource->setRealTimeFilterStack(m_document->getRealTimeFilterStack()); + +} + +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); + } + + if (m_document) + m_document->removeAllFilters(); + m_infoWidget->reset(); m_searchWidget->reset(); - m_resultsWidget->reset(); - - delete m_document; - m_document = 0; - m_viewManager->clearSelections(); - m_timeRulerLayer = 0; // document owned this - - m_sessionFile = ""; - setWindowTitle(tr("Sound Access")); - - CommandHistory::getInstance()->clear(); - CommandHistory::getInstance()->documentSaved(); - documentRestored(); - - m_EasaierManager->closeSession(); - - m_toolBox->removeItem(0); - m_multiPaneLayerContainer = 0; - - TempDirectory::getInstance()->cleanup(); -} - -void -MainWindow::openSession() -{ - if (!checkSaveModified()) return; - - QString orig = m_audioFile; - if (orig == "") orig = "."; - else orig = QFileInfo(orig).absoluteDir().canonicalPath(); - - QString path = getOpenFileName(FileFinder::SessionFile); - - if (path.isEmpty()) return; - - if (openSessionFile(path) == FileOpenFailed) { - QMessageBox::critical(this, tr("Failed to open file"), - tr("Session file \"%1\" could not be opened").arg(path)); - } -} - -void -MainWindow::openSomething() -{ - QString orig = m_audioFile; - if (orig == "") orig = "."; - else orig = QFileInfo(orig).absoluteDir().canonicalPath(); - - bool canImportLayer = (getMainModel() != 0 && - m_paneStack != 0 && - m_paneStack->getCurrentPane() != 0); - - QString path = getOpenFileName(FileFinder::AnyFile); - - if (path.isEmpty()) return; - - if (path.endsWith(".sv")) { - - if (!checkSaveModified()) return; - - if (openSessionFile(path) == FileOpenFailed) { - QMessageBox::critical(this, tr("Failed to open file"), - tr("Session file \"%1\" could not be opened").arg(path)); - } - - } else { - - if (openAudioFile(path, AskUser) == FileOpenFailed) { - - if (!canImportLayer || (openLayerFile(path) == FileOpenFailed)) { - - QMessageBox::critical(this, tr("Failed to open file"), - tr("File \"%1\" could not be opened").arg(path)); - } - } - } -} - -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; - - if (openURL(QUrl(text)) == FileOpenFailed) { - QMessageBox::critical(this, tr("Failed to open location"), - tr("URL \"%1\" could not be opened").arg(text)); - } -} - -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; - - QUrl url(path); - if (RemoteFile::canHandleScheme(url)) { - openURL(url); - return; - } - - if (path.endsWith("sv")) { - - if (!checkSaveModified()) return; - - if (openSessionFile(path) == FileOpenFailed) { - QMessageBox::critical(this, tr("Failed to open file"), - tr("Session file \"%1\" could not be opened").arg(path)); - } - - } else { - - if (openAudioFile(path, AskUser) == FileOpenFailed) { - - bool canImportLayer = (getMainModel() != 0 && - m_paneStack != 0 && - m_paneStack->getCurrentPane() != 0); - - if (!canImportLayer || (openLayerFile(path) == FileOpenFailed)) { - - QMessageBox::critical(this, tr("Failed to open file"), - tr("File \"%1\" could not be opened").arg(path)); - } - } - } -} - -MainWindow::FileOpenStatus -MainWindow::openURL(QUrl url) -{ - if (url.scheme().toLower() == "file") { - return openSomeFile(url.toLocalFile()); - } else if (!RemoteFile::canHandleScheme(url)) { - QMessageBox::critical(this, tr("Unsupported scheme in URL"), - tr("The URL scheme \"%1\" is not supported") - .arg(url.scheme())); - return FileOpenFailed; - } else { - RemoteFile rf(url); - rf.wait(); - if (!rf.isOK()) { - QMessageBox::critical(this, tr("File download failed"), - tr("Failed to download URL \"%1\": %2") - .arg(url.toString()).arg(rf.getErrorString())); - return FileOpenFailed; - } - FileOpenStatus status; - if ((status = openSomeFile(rf.getLocalFilename(), url.toString())) != - FileOpenSucceeded) { - rf.deleteLocalFile(); - } - return status; - } -} - -MainWindow::FileOpenStatus -MainWindow::openSomeFile(QString path, AudioFileOpenMode mode) -{ - return openSomeFile(path, path, mode); -} - -MainWindow::FileOpenStatus -MainWindow::openSomeFile(QString path, QString location, - AudioFileOpenMode mode) -{ - FileOpenStatus status; - - bool canImportLayer = (getMainModel() != 0 && - m_paneStack != 0 && - m_paneStack->getCurrentPane() != 0); - - if ((status = openAudioFile(path, location, mode)) != FileOpenFailed) { - return status; - } else if ((status = openSessionFile(path, location)) != FileOpenFailed) { - return status; - } else if (!canImportLayer) { - return FileOpenFailed; - } else if ((status = openLayerFile(path, location)) != FileOpenFailed) { - return status; - } else { - return FileOpenFailed; - } -} - -MainWindow::FileOpenStatus -MainWindow::openSessionFile(QString path) -{ - return openSessionFile(path, path); -} - -MainWindow::FileOpenStatus -MainWindow::openSessionFile(QString path, QString location) -{ - BZipFileDevice bzFile(path); - if (!bzFile.open(QIODevice::ReadOnly)) { - std::cerr << "Failed to open session file \"" << location.toStdString() - << "\": " << bzFile.errorString().toStdString() << std::endl; - return FileOpenFailed; - } - - if (!checkSaveModified()) return FileOpenCancelled; - - QString error; - closeSession(); - createDocument(); - createMultiPaneLayerContainer(); - PaneCallback callback(this); - m_viewManager->clearSelections(); - - SVFileReader reader(m_document, callback, location); - QXmlInputSource inputSource(&bzFile); - reader.parse(inputSource); - - if (!reader.isOK()) { - error = tr("SV XML file read error:\n%1").arg(reader.getErrorString()); - } - - bzFile.close(); - - bool ok = (error == ""); - - bool realFile = (location == path); - - if (ok) { - - setWindowTitle(tr("Sound Access: %1") - .arg(QFileInfo(location).fileName())); - - if (realFile) m_sessionFile = path; - - setupMenus(); - CommandHistory::getInstance()->clear(); - CommandHistory::getInstance()->documentSaved(); - m_documentModified = false; - updateMenuStates(); - - m_recentFiles.addFile(location); - - if (realFile) { - registerLastOpenedFilePath(FileFinder::SessionFile, path); // for file dialog - } - - } else { - setWindowTitle(tr("Sound Access")); - } + m_resultsWidget->reset(); + + delete m_document; + m_document = 0; + m_viewManager->clearSelections(); + m_timeRulerLayer = 0; // document owned this + + m_sessionFile = ""; + setWindowTitle(tr("Sound Access")); + + CommandHistory::getInstance()->clear(); + CommandHistory::getInstance()->documentSaved(); + documentRestored(); + + m_EasaierManager->closeSession(); + + m_toolBox->removeItem(0); + m_multiPaneLayerContainer = 0; + + TempDirectory::getInstance()->cleanup(); +} + +void +MainWindow::openSession() +{ + if (!checkSaveModified()) return; + + QString orig = m_audioFile; + if (orig == "") orig = "."; + else orig = QFileInfo(orig).absoluteDir().canonicalPath(); + + QString path = getOpenFileName(FileFinder::SessionFile); + + if (path.isEmpty()) return; + + if (openSessionFile(path) == FileOpenFailed) { + QMessageBox::critical(this, tr("Failed to open file"), + tr("Session file \"%1\" could not be opened").arg(path)); + } +} + +void +MainWindow::openSomething() +{ + QString orig = m_audioFile; + if (orig == "") orig = "."; + else orig = QFileInfo(orig).absoluteDir().canonicalPath(); + + bool canImportLayer = (getMainModel() != 0 && + m_paneStack != 0 && + m_paneStack->getCurrentPane() != 0); + + QString path = getOpenFileName(FileFinder::AnyFile); + + if (path.isEmpty()) return; + + if (path.endsWith(".sv")) { + + if (!checkSaveModified()) return; + + if (openSessionFile(path) == FileOpenFailed) { + QMessageBox::critical(this, tr("Failed to open file"), + tr("Session file \"%1\" could not be opened").arg(path)); + } + + } else { + + if (openAudioFile(path, AskUser) == FileOpenFailed) { + + if (!canImportLayer || (openLayerFile(path) == FileOpenFailed)) { + + QMessageBox::critical(this, tr("Failed to open file"), + tr("File \"%1\" could not be opened").arg(path)); + } + } + } +} + +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; + + if (openURL(QUrl(text)) == FileOpenFailed) { + QMessageBox::critical(this, tr("Failed to open location"), + tr("URL \"%1\" could not be opened").arg(text)); + } +} + +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; + + QUrl url(path); + if (RemoteFile::canHandleScheme(url)) { + openURL(url); + return; + } + + if (path.endsWith("sv")) { + + if (!checkSaveModified()) return; + + if (openSessionFile(path) == FileOpenFailed) { + QMessageBox::critical(this, tr("Failed to open file"), + tr("Session file \"%1\" could not be opened").arg(path)); + } + + } else { + + if (openAudioFile(path, AskUser) == FileOpenFailed) { + + bool canImportLayer = (getMainModel() != 0 && + m_paneStack != 0 && + m_paneStack->getCurrentPane() != 0); + + if (!canImportLayer || (openLayerFile(path) == FileOpenFailed)) { + + QMessageBox::critical(this, tr("Failed to open file"), + tr("File \"%1\" could not be opened").arg(path)); + } + } + } +} + +MainWindow::FileOpenStatus +MainWindow::openURL(QUrl url) +{ + if (url.scheme().toLower() == "file") { + return openSomeFile(url.toLocalFile()); + } else if (!RemoteFile::canHandleScheme(url)) { + QMessageBox::critical(this, tr("Unsupported scheme in URL"), + tr("The URL scheme \"%1\" is not supported") + .arg(url.scheme())); + return FileOpenFailed; + } else { + RemoteFile rf(url); + rf.wait(); + if (!rf.isOK()) { + QMessageBox::critical(this, tr("File download failed"), + tr("Failed to download URL \"%1\": %2") + .arg(url.toString()).arg(rf.getErrorString())); + return FileOpenFailed; + } + FileOpenStatus status; + if ((status = openSomeFile(rf.getLocalFilename(), url.toString())) != + FileOpenSucceeded) { + rf.deleteLocalFile(); + } + return status; + } +} + +MainWindow::FileOpenStatus +MainWindow::openSomeFile(QString path, AudioFileOpenMode mode) +{ + return openSomeFile(path, path, mode); +} + +MainWindow::FileOpenStatus +MainWindow::openSomeFile(QString path, QString location, + AudioFileOpenMode mode) +{ + FileOpenStatus status; + + bool canImportLayer = (getMainModel() != 0 && + m_paneStack != 0 && + m_paneStack->getCurrentPane() != 0); + + if ((status = openAudioFile(path, location, mode)) != FileOpenFailed) { + return status; + } else if ((status = openSessionFile(path, location)) != FileOpenFailed) { + return status; + } else if (!canImportLayer) { + return FileOpenFailed; + } else if ((status = openLayerFile(path, location)) != FileOpenFailed) { + return status; + } else { + return FileOpenFailed; + } +} + +MainWindow::FileOpenStatus +MainWindow::openSessionFile(QString path) +{ + return openSessionFile(path, path); +} + +MainWindow::FileOpenStatus +MainWindow::openSessionFile(QString path, QString location) +{ + BZipFileDevice bzFile(path); + if (!bzFile.open(QIODevice::ReadOnly)) { + std::cerr << "Failed to open session file \"" << location.toStdString() + << "\": " << bzFile.errorString().toStdString() << std::endl; + return FileOpenFailed; + } + + if (!checkSaveModified()) return FileOpenCancelled; + + QString error; + closeSession(); + createDocument(); + createMultiPaneLayerContainer(); + PaneCallback callback(this); + m_viewManager->clearSelections(); + + SVFileReader reader(m_document, callback, location); + QXmlInputSource inputSource(&bzFile); + reader.parse(inputSource); + + if (!reader.isOK()) { + error = tr("SV XML file read error:\n%1").arg(reader.getErrorString()); + } + + bzFile.close(); + + bool ok = (error == ""); + + bool realFile = (location == path); + + if (ok) { + + setWindowTitle(tr("Sound Access: %1") + .arg(QFileInfo(location).fileName())); + + if (realFile) m_sessionFile = path; + + setupMenus(); + CommandHistory::getInstance()->clear(); + CommandHistory::getInstance()->documentSaved(); + m_documentModified = false; + updateMenuStates(); + + m_recentFiles.addFile(location); + + if (realFile) { + registerLastOpenedFilePath(FileFinder::SessionFile, path); // for file dialog + } + + } else { + setWindowTitle(tr("Sound Access")); + } m_EasaierManager->newSession(); @@ -3091,213 +3123,213 @@ //connect(m_properties, SIGNAL(removeSelectedItem()), this, SLOT(deleteCurrentLayer())); //m_toolBox->insertItem(0,"Layers", m_properties); - - return ok ? FileOpenSucceeded : FileOpenFailed; -} - -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(); - - e->accept(); - return; -} - -bool -MainWindow::commitData(bool mayAskUser) -{ - if (mayAskUser) { - return checkSaveModified(); - } 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); - - if (button == QMessageBox::Yes) { - saveEasaierSession(); - 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("Sound Access: %1") - .arg(QFileInfo(path).fileName())); - m_sessionFile = path; - CommandHistory::getInstance()->documentSaved(); - documentRestored(); - m_recentFiles.addFile(path); - } -} - -bool -MainWindow::saveSessionFile(QString path) -{ - BZipFileDevice bzFile(path); - if (!bzFile.open(QIODevice::WriteOnly)) { - std::cerr << "Failed to open session file \"" << path.toStdString() - << "\" for writing: " - << bzFile.errorString().toStdString() << std::endl; - return false; - } - - QApplication::setOverrideCursor(QCursor(Qt::WaitCursor)); - - QTextStream out(&bzFile); - toXml(out); - out.flush(); - - QApplication::restoreOverrideCursor(); - - if (!bzFile.isOK()) { - QMessageBox::critical(this, tr("Failed to write file"), - tr("Failed to write to file \"%1\": %2") - .arg(path).arg(bzFile.errorString())); - bzFile.close(); - return false; - } - - bzFile.close(); - return true; -} - -void -MainWindow::toXml(QTextStream &out) -{ - QString indent(" "); - - out << "<?xml version=\"1.0\" encoding=\"UTF-8\"?>\n"; - out << "<!DOCTYPE sonic-visualiser>\n"; - out << "<sv>\n"; - - m_document->toXml(out, "", ""); - - out << "<display>\n"; - - out << QString(" <window width=\"%1\" height=\"%2\"/>\n") - .arg(width()).arg(height()); - - for (int i = 0; i < m_paneStack->getPaneCount(); ++i) { - - Pane *pane = m_paneStack->getPane(i); - - if (pane) { - pane->toXml(out, indent); - } - } - - out << "</display>\n"; - - m_viewManager->getSelection().toXml(out); - - out << "</sv>\n"; -} - + + return ok ? FileOpenSucceeded : FileOpenFailed; +} + +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(); + + e->accept(); + return; +} + +bool +MainWindow::commitData(bool mayAskUser) +{ + if (mayAskUser) { + return checkSaveModified(); + } 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); + + if (button == QMessageBox::Yes) { + saveEasaierSession(); + 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("Sound Access: %1") + .arg(QFileInfo(path).fileName())); + m_sessionFile = path; + CommandHistory::getInstance()->documentSaved(); + documentRestored(); + m_recentFiles.addFile(path); + } +} + +bool +MainWindow::saveSessionFile(QString path) +{ + BZipFileDevice bzFile(path); + if (!bzFile.open(QIODevice::WriteOnly)) { + std::cerr << "Failed to open session file \"" << path.toStdString() + << "\" for writing: " + << bzFile.errorString().toStdString() << std::endl; + return false; + } + + QApplication::setOverrideCursor(QCursor(Qt::WaitCursor)); + + QTextStream out(&bzFile); + toXml(out); + out.flush(); + + QApplication::restoreOverrideCursor(); + + if (!bzFile.isOK()) { + QMessageBox::critical(this, tr("Failed to write file"), + tr("Failed to write to file \"%1\": %2") + .arg(path).arg(bzFile.errorString())); + bzFile.close(); + return false; + } + + bzFile.close(); + return true; +} + +void +MainWindow::toXml(QTextStream &out) +{ + QString indent(" "); + + out << "<?xml version=\"1.0\" encoding=\"UTF-8\"?>\n"; + out << "<!DOCTYPE sonic-visualiser>\n"; + out << "<sv>\n"; + + m_document->toXml(out, "", ""); + + out << "<display>\n"; + + out << QString(" <window width=\"%1\" height=\"%2\"/>\n") + .arg(width()).arg(height()); + + for (int i = 0; i < m_paneStack->getPaneCount(); ++i) { + + Pane *pane = m_paneStack->getPane(i); + + if (pane) { + pane->toXml(out, indent); + } + } + + out << "</display>\n"; + + m_viewManager->getSelection().toXml(out); + + out << "</sv>\n"; +} + void MainWindow::toEasaierXml(QTextStream &out) { QString s; @@ -3329,1587 +3361,1597 @@ s += "</easaierSession>\n"; out << s; +} + +Pane * +MainWindow::addPaneToStack() +{ + AddPaneCommand *command = new AddPaneCommand(this); + CommandHistory::getInstance()->addCommand(command); + return command->getPane(); +} + +void +MainWindow::zoomIn() +{ + Pane *currentPane = m_paneStack->getCurrentPane(); + if (currentPane) currentPane->zoom(true); +} + +void +MainWindow::zoomOut() +{ + Pane *currentPane = m_paneStack->getCurrentPane(); + if (currentPane) currentPane->zoom(false); +} + +void +MainWindow::zoomToFit() +{ + Pane *currentPane = m_paneStack->getCurrentPane(); + if (!currentPane) return; + + Model *model = getMainModel(); + if (!model) return; + + size_t start = model->getStartFrame(); + size_t end = model->getEndFrame(); + size_t pixels = currentPane->width(); + size_t zoomLevel = (end - start) / pixels; + + currentPane->setZoomLevel(zoomLevel); + currentPane->setStartFrame(start); +} + +void +MainWindow::zoomDefault() +{ + Pane *currentPane = m_paneStack->getCurrentPane(); + if (currentPane) currentPane->setZoomLevel(1024); +} + +void +MainWindow::scrollLeft() +{ + Pane *currentPane = m_paneStack->getCurrentPane(); + if (currentPane) currentPane->scroll(false, false); +} + +void +MainWindow::jumpLeft() +{ + Pane *currentPane = m_paneStack->getCurrentPane(); + if (currentPane) currentPane->scroll(false, true); +} + +void +MainWindow::scrollRight() +{ + Pane *currentPane = m_paneStack->getCurrentPane(); + if (currentPane) currentPane->scroll(true, false); +} + +void +MainWindow::jumpRight() +{ + Pane *currentPane = m_paneStack->getCurrentPane(); + if (currentPane) currentPane->scroll(true, true); +} + +void +MainWindow::showNoOverlays() +{ + m_viewManager->setOverlayMode(ViewManager::NoOverlays); +} + +void +MainWindow::showMinimalOverlays() +{ + m_viewManager->setOverlayMode(ViewManager::MinimalOverlays); +} + +void +MainWindow::showStandardOverlays() +{ + m_viewManager->setOverlayMode(ViewManager::StandardOverlays); +} + +void +MainWindow::showAllOverlays() +{ + m_viewManager->setOverlayMode(ViewManager::AllOverlays); +} + +void +MainWindow::toggleZoomWheels() +{ + if (m_viewManager->getZoomWheelsEnabled()) { + m_viewManager->setZoomWheelsEnabled(false); + } else { + m_viewManager->setZoomWheelsEnabled(true); + } +} + +void +MainWindow::togglePropertyBoxes() +{ + if (m_paneStack->getLayoutStyle() == PaneStack::NoPropertyStacks) { + if (Preferences::getInstance()->getPropertyBoxLayout() == + Preferences::VerticallyStacked) { + m_paneStack->setLayoutStyle(PaneStack::PropertyStackPerPaneLayout); + } else { + m_paneStack->setLayoutStyle(PaneStack::SinglePropertyStackLayout); + } + } else { + m_paneStack->setLayoutStyle(PaneStack::NoPropertyStacks); + } +} + +void +MainWindow::toggleStatusBar() +{ + QSettings settings; + settings.beginGroup("MainWindow"); + bool sb = settings.value("showstatusbar", true).toBool(); + + if (sb) { + statusBar()->hide(); + } else { + statusBar()->show(); + } + + settings.setValue("showstatusbar", !sb); + + settings.endGroup(); +} + +void +MainWindow::preferenceChanged(PropertyContainer::PropertyName name) +{ + if (name == "Property Box Layout") { + if (m_paneStack->getLayoutStyle() != PaneStack::NoPropertyStacks) { + if (Preferences::getInstance()->getPropertyBoxLayout() == + Preferences::VerticallyStacked) { + m_paneStack->setLayoutStyle(PaneStack::PropertyStackPerPaneLayout); + } else { + m_paneStack->setLayoutStyle(PaneStack::SinglePropertyStackLayout); + } + } + } +} + +void +MainWindow::play() +{ + if (m_playSource->isPlaying()) { + stop(); + } else { + playbackFrameChanged(m_viewManager->getPlaybackFrame()); + m_playSource->play(m_viewManager->getPlaybackFrame()); + } +} +// Ivan Damnjanovic 16/10/2007 functions to access data needed for video player +unsigned long +MainWindow::Get_CurAudioTime() +{ + return (m_viewManager->getPlaybackFrame()/(getMainModel()->getSampleRate()/1000)); } - -Pane * -MainWindow::addPaneToStack() -{ - AddPaneCommand *command = new AddPaneCommand(this); - CommandHistory::getInstance()->addCommand(command); - return command->getPane(); -} - -void -MainWindow::zoomIn() -{ - Pane *currentPane = m_paneStack->getCurrentPane(); - if (currentPane) currentPane->zoom(true); -} - -void -MainWindow::zoomOut() -{ - Pane *currentPane = m_paneStack->getCurrentPane(); - if (currentPane) currentPane->zoom(false); -} - -void -MainWindow::zoomToFit() -{ - Pane *currentPane = m_paneStack->getCurrentPane(); - if (!currentPane) return; - - Model *model = getMainModel(); - if (!model) return; - - size_t start = model->getStartFrame(); - size_t end = model->getEndFrame(); - size_t pixels = currentPane->width(); - size_t zoomLevel = (end - start) / pixels; - - currentPane->setZoomLevel(zoomLevel); - currentPane->setStartFrame(start); -} - -void -MainWindow::zoomDefault() -{ - Pane *currentPane = m_paneStack->getCurrentPane(); - if (currentPane) currentPane->setZoomLevel(1024); -} - -void -MainWindow::scrollLeft() -{ - Pane *currentPane = m_paneStack->getCurrentPane(); - if (currentPane) currentPane->scroll(false, false); -} - -void -MainWindow::jumpLeft() -{ - Pane *currentPane = m_paneStack->getCurrentPane(); - if (currentPane) currentPane->scroll(false, true); -} - -void -MainWindow::scrollRight() -{ - Pane *currentPane = m_paneStack->getCurrentPane(); - if (currentPane) currentPane->scroll(true, false); -} - -void -MainWindow::jumpRight() -{ - Pane *currentPane = m_paneStack->getCurrentPane(); - if (currentPane) currentPane->scroll(true, true); -} - -void -MainWindow::showNoOverlays() -{ - m_viewManager->setOverlayMode(ViewManager::NoOverlays); -} - -void -MainWindow::showMinimalOverlays() -{ - m_viewManager->setOverlayMode(ViewManager::MinimalOverlays); -} - -void -MainWindow::showStandardOverlays() -{ - m_viewManager->setOverlayMode(ViewManager::StandardOverlays); -} - -void -MainWindow::showAllOverlays() -{ - m_viewManager->setOverlayMode(ViewManager::AllOverlays); -} - -void -MainWindow::toggleZoomWheels() -{ - if (m_viewManager->getZoomWheelsEnabled()) { - m_viewManager->setZoomWheelsEnabled(false); - } else { - m_viewManager->setZoomWheelsEnabled(true); - } -} - -void -MainWindow::togglePropertyBoxes() -{ - if (m_paneStack->getLayoutStyle() == PaneStack::NoPropertyStacks) { - if (Preferences::getInstance()->getPropertyBoxLayout() == - Preferences::VerticallyStacked) { - m_paneStack->setLayoutStyle(PaneStack::PropertyStackPerPaneLayout); - } else { - m_paneStack->setLayoutStyle(PaneStack::SinglePropertyStackLayout); - } - } else { - m_paneStack->setLayoutStyle(PaneStack::NoPropertyStacks); - } -} - -void -MainWindow::toggleStatusBar() -{ - QSettings settings; - settings.beginGroup("MainWindow"); - bool sb = settings.value("showstatusbar", true).toBool(); - - if (sb) { - statusBar()->hide(); - } else { - statusBar()->show(); - } - - settings.setValue("showstatusbar", !sb); - - settings.endGroup(); -} - -void -MainWindow::preferenceChanged(PropertyContainer::PropertyName name) -{ - if (name == "Property Box Layout") { - if (m_paneStack->getLayoutStyle() != PaneStack::NoPropertyStacks) { - if (Preferences::getInstance()->getPropertyBoxLayout() == - Preferences::VerticallyStacked) { - m_paneStack->setLayoutStyle(PaneStack::PropertyStackPerPaneLayout); - } else { - m_paneStack->setLayoutStyle(PaneStack::SinglePropertyStackLayout); - } - } - } -} - -void -MainWindow::play() -{ - if (m_playSource->isPlaying()) { - stop(); - } else { - playbackFrameChanged(m_viewManager->getPlaybackFrame()); - m_playSource->play(m_viewManager->getPlaybackFrame()); - } -} - -void -MainWindow::ffwd() -{ - if (!getMainModel()) return; - - int frame = m_viewManager->getPlaybackFrame(); - ++frame; - - Pane *pane = m_paneStack->getCurrentPane(); - if (!pane) return; - - Layer *layer = pane->getSelectedLayer(); - - if (!dynamic_cast<TimeInstantLayer *>(layer) && - !dynamic_cast<TimeValueLayer *>(layer)) return; - - size_t resolution = 0; - if (!layer->snapToFeatureFrame(pane, frame, resolution, Layer::SnapRight)) { - frame = getMainModel()->getEndFrame(); - } - - m_viewManager->setPlaybackFrame(frame); -} - -void -MainWindow::ffwdEnd() -{ - if (!getMainModel()) return; - m_viewManager->setPlaybackFrame(getMainModel()->getEndFrame()); -} - -void -MainWindow::rewind() -{ - if (!getMainModel()) return; - - int frame = m_viewManager->getPlaybackFrame(); - if (frame > 0) --frame; - - Pane *pane = m_paneStack->getCurrentPane(); - if (!pane) return; - - Layer *layer = pane->getSelectedLayer(); - - if (!dynamic_cast<TimeInstantLayer *>(layer) && - !dynamic_cast<TimeValueLayer *>(layer)) return; - - size_t resolution = 0; - if (!layer->snapToFeatureFrame(pane, frame, resolution, Layer::SnapLeft)) { - frame = getMainModel()->getEndFrame(); - } - - m_viewManager->setPlaybackFrame(frame); -} - -void -MainWindow::rewindStart() -{ - if (!getMainModel()) return; - m_viewManager->setPlaybackFrame(getMainModel()->getStartFrame()); -} - -void -MainWindow::stop() -{ - m_playSource->stop(); - - if (m_paneStack && m_paneStack->getCurrentPane()) { - updateVisibleRangeDisplay(m_paneStack->getCurrentPane()); - } else { - m_myStatusMessage = ""; - statusBar()->showMessage(""); - } -} - -void -MainWindow::addPane() -{ - QObject *s = sender(); - QAction *action = dynamic_cast<QAction *>(s); - - if (!action) { - std::cerr << "WARNING: MainWindow::addPane: sender is not an action" - << std::endl; - return; - } - - PaneActionMap::iterator i = m_paneActions.find(action); - - if (i == m_paneActions.end()) { - std::cerr << "WARNING: MainWindow::addPane: unknown action " - << action->objectName().toStdString() << std::endl; - return; - } - - addPane(i->second, action->text()); -} - -void -MainWindow::addPane(const PaneConfiguration &configuration, QString text) -{ - CommandHistory::getInstance()->startCompoundOperation(text, true); - - AddPaneCommand *command = new AddPaneCommand(this); - CommandHistory::getInstance()->addCommand(command); - - Pane *pane = command->getPane(); - - if (configuration.layer == LayerFactory::Spectrum) { - pane->setPlaybackFollow(PlaybackScrollContinuous); - pane->setFollowGlobalZoom(false); - pane->setZoomLevel(512); - } - - if (configuration.layer != LayerFactory::TimeRuler && - configuration.layer != LayerFactory::Spectrum) { - - if (!m_timeRulerLayer) { -// std::cerr << "no time ruler layer, creating one" << std::endl; - m_timeRulerLayer = m_document->createMainModelLayer - (LayerFactory::TimeRuler); - } - -// std::cerr << "adding time ruler layer " << m_timeRulerLayer << std::endl; - - m_document->addLayerToView(pane, m_timeRulerLayer); - } - - Layer *newLayer = m_document->createLayer(configuration.layer); - - Model *suggestedModel = configuration.sourceModel; - Model *model = 0; - - if (suggestedModel) { - - // check its validity - std::vector<Model *> inputModels = m_document->getTransformInputModels(); - for (size_t j = 0; j < inputModels.size(); ++j) { - if (inputModels[j] == suggestedModel) { - model = suggestedModel; - break; - } - } - - if (!model) { - std::cerr << "WARNING: Model " << (void *)suggestedModel - << " appears in pane action map, but is not reported " - << "by document as a valid transform source" << std::endl; - } - } - - if (!model) model = m_document->getMainModel(); - - m_document->setModel(newLayer, model); - - m_document->setChannel(newLayer, configuration.channel); - m_document->addLayerToView(pane, newLayer); - - m_paneStack->setCurrentPane(pane); - m_paneStack->setCurrentLayer(pane, newLayer); - -// std::cerr << "MainWindow::addPane: global centre frame is " -// << m_viewManager->getGlobalCentreFrame() << std::endl; -// pane->setCentreFrame(m_viewManager->getGlobalCentreFrame()); - - CommandHistory::getInstance()->endCompoundOperation(); - - updateMenuStates(); -} - -MainWindow::AddPaneCommand::AddPaneCommand(MainWindow *mw) : - m_mw(mw), - m_pane(0), - m_prevCurrentPane(0), - m_added(false) -{ -} - -MainWindow::AddPaneCommand::~AddPaneCommand() -{ - if (m_pane && !m_added) { - m_mw->m_paneStack->deletePane(m_pane); - } -} - -QString -MainWindow::AddPaneCommand::getName() const -{ - return tr("Add Pane"); -} - -void -MainWindow::AddPaneCommand::execute() -{ - if (!m_pane) { - m_prevCurrentPane = m_mw->m_paneStack->getCurrentPane(); - m_pane = m_mw->m_paneStack->addPane(); - - connect(m_pane, SIGNAL(contextHelpChanged(const QString &)), - m_mw, SLOT(contextHelpChanged(const QString &))); - } else { - m_mw->m_paneStack->showPane(m_pane); - } - - m_mw->m_paneStack->setCurrentPane(m_pane); - m_mw->m_overview->registerView(m_pane); - m_added = true; -} - -void -MainWindow::AddPaneCommand::unexecute() -{ - m_mw->m_paneStack->hidePane(m_pane); - m_mw->m_paneStack->setCurrentPane(m_prevCurrentPane); - m_mw->m_overview->unregisterView(m_pane); - m_added = false; -} - -MainWindow::RemovePaneCommand::RemovePaneCommand(MainWindow *mw, Pane *pane) : - m_mw(mw), - m_pane(pane), - m_added(true) -{ -} - -MainWindow::RemovePaneCommand::~RemovePaneCommand() -{ - if (m_pane && !m_added) { - m_mw->m_paneStack->deletePane(m_pane); - } -} - -QString -MainWindow::RemovePaneCommand::getName() const -{ - return tr("Remove Pane"); -} - -void -MainWindow::RemovePaneCommand::execute() -{ - m_prevCurrentPane = m_mw->m_paneStack->getCurrentPane(); - m_mw->m_paneStack->hidePane(m_pane); - m_mw->m_overview->unregisterView(m_pane); - m_added = false; -} - -void -MainWindow::RemovePaneCommand::unexecute() -{ - m_mw->m_paneStack->showPane(m_pane); - m_mw->m_paneStack->setCurrentPane(m_prevCurrentPane); - m_mw->m_overview->registerView(m_pane); - m_added = true; -} - -void -MainWindow::addLayer() -{ - QObject *s = sender(); - QAction *action = dynamic_cast<QAction *>(s); - - if (!action) { - std::cerr << "WARNING: MainWindow::addLayer: sender is not an action" - << std::endl; - return; - } - - Pane *pane = m_paneStack->getCurrentPane(); - - if (!pane) { - std::cerr << "WARNING: MainWindow::addLayer: no current pane" << std::endl; - return; - } - - ExistingLayerActionMap::iterator ei = m_existingLayerActions.find(action); - - if (ei != m_existingLayerActions.end()) { - Layer *newLayer = ei->second; - m_document->addLayerToView(pane, newLayer); - m_paneStack->setCurrentLayer(pane, newLayer); - return; - } - - ei = m_sliceActions.find(action); - - if (ei != m_sliceActions.end()) { - Layer *newLayer = m_document->createLayer(LayerFactory::Slice); -// document->setModel(newLayer, ei->second->getModel()); - SliceableLayer *source = dynamic_cast<SliceableLayer *>(ei->second); - SliceLayer *dest = dynamic_cast<SliceLayer *>(newLayer); - if (source && dest) { - dest->setSliceableModel(source->getSliceableModel()); - connect(source, SIGNAL(sliceableModelReplaced(const Model *, const Model *)), - dest, SLOT(sliceableModelReplaced(const Model *, const Model *))); - connect(m_document, SIGNAL(modelAboutToBeDeleted(Model *)), - dest, SLOT(modelAboutToBeDeleted(Model *))); - } - m_document->addLayerToView(pane, newLayer); - m_paneStack->setCurrentLayer(pane, newLayer); - return; - } - - TransformActionMap::iterator i = m_transformActions.find(action); - - if (i == m_transformActions.end()) { - - LayerActionMap::iterator i = m_layerActions.find(action); - - if (i == m_layerActions.end()) { - std::cerr << "WARNING: MainWindow::addLayer: unknown action " - << action->objectName().toStdString() << std::endl; - return; - } - - LayerFactory::LayerType type = i->second; - - LayerFactory::LayerTypeSet emptyTypes = - LayerFactory::getInstance()->getValidEmptyLayerTypes(); - - Layer *newLayer; - - if (emptyTypes.find(type) != emptyTypes.end()) { - - newLayer = m_document->createEmptyLayer(type); - m_toolActions[ViewManager::DrawMode]->trigger(); - - } else { - - newLayer = m_document->createMainModelLayer(type); - } - - m_document->addLayerToView(pane, newLayer); - m_paneStack->setCurrentLayer(pane, newLayer); - - return; - } - - TransformId transform = i->second; - TransformFactory *factory = TransformFactory::getInstance(); - - QString configurationXml; - - int channel = -1; - // pick up the default channel from any existing layers on the same pane - for (int j = 0; j < pane->getLayerCount(); ++j) { - int c = LayerFactory::getInstance()->getChannel(pane->getLayer(j)); - if (c != -1) { - channel = c; - break; - } - } - - // We always ask for configuration, even if the plugin isn't - // supposed to be configurable, because we need to let the user - // change the execution context (block size etc). - - PluginTransform::ExecutionContext context(channel); - - std::vector<Model *> candidateInputModels = - m_document->getTransformInputModels(); - - Model *inputModel = factory->getConfigurationForTransform(transform, - candidateInputModels, - context, - configurationXml, - m_playSource); - if (!inputModel) return; - -// std::cerr << "MainWindow::addLayer: Input model is " << inputModel << " \"" << inputModel->objectName().toStdString() << "\"" << std::endl; - - Layer *newLayer = m_document->createDerivedLayer(transform, - inputModel, - context, - configurationXml); - - if (newLayer) { - m_document->addLayerToView(pane, newLayer); - m_document->setChannel(newLayer, context.channel); - m_recentTransforms.add(transform); - m_paneStack->setCurrentLayer(pane, newLayer); - } - - updateMenuStates(); -} - -void -MainWindow::deleteCurrentPane() -{ - CommandHistory::getInstance()->startCompoundOperation - (tr("Delete Pane"), true); - - Pane *pane = m_paneStack->getCurrentPane(); - if (pane) { - while (pane->getLayerCount() > 0) { - Layer *layer = pane->getLayer(0); - if (layer) { - m_document->removeLayerFromView(pane, layer); - } else { - break; - } - } - - RemovePaneCommand *command = new RemovePaneCommand(this, pane); - CommandHistory::getInstance()->addCommand(command); - } - - CommandHistory::getInstance()->endCompoundOperation(); - - updateMenuStates(); -} - -void -MainWindow::deleteCurrentLayer() -{ - Pane *pane = m_paneStack->getCurrentPane(); - if (pane) { - Layer *layer = pane->getSelectedLayer(); - if (layer) { - m_document->removeLayerFromView(pane, layer); - } - } - updateMenuStates(); -} - -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); - setupExistingLayersMenus(); - } - } - } -} - -void -MainWindow::playSpeedChanged(int position) -{ - PlaySpeedRangeMapper mapper(0, 200); - - float percent = m_playSpeed->mappedValue(); - - float factor = mapper.getFactorForValue(percent); - -// float factor = mapper.getFactorForPosition(position); -// float percent = mapper.getValueForPosition(position); - - std::cerr << "speed = " << position << " percent = " << percent << " factor = " << factor << std::endl; - -//!!! bool slow = (position < 100); - bool something = (position != 100); -/*!!! - int pc = lrintf(percent); - - m_playSpeed->setToolTip(tr("Playback speed: %1%2%") - .arg(!slow ? "+" : "") - .arg(pc)); -*/ - m_playSharpen->setEnabled(something); - m_playMono->setEnabled(something); - bool sharpen = (something && m_playSharpen->isChecked()); - bool mono = (something && m_playMono->isChecked()); - m_playSource->setTimeStretch(factor, sharpen, mono); -} - -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::playbackFrameChanged(unsigned long frame) -{ - if (!(m_playSource && m_playSource->isPlaying()) || !getMainModel()) return; - - RealTime now = RealTime::frame2RealTime - (frame, getMainModel()->getSampleRate()); - - if (now.sec == m_lastPlayStatusSec) return; - - RealTime then = RealTime::frame2RealTime - (m_playSource->getPlayEndFrame(), getMainModel()->getSampleRate()); - - QString nowStr; - QString thenStr; - QString remainingStr; - - if (then.sec > 10) { - nowStr = now.toSecText().c_str(); - thenStr = then.toSecText().c_str(); - remainingStr = (then - now).toSecText().c_str(); - m_lastPlayStatusSec = now.sec; - } else { - nowStr = now.toText(true).c_str(); - thenStr = then.toText(true).c_str(); - remainingStr = (then - now).toText(true).c_str(); - } - - m_myStatusMessage = tr("Playing: %1 of %2 (%3 remaining)") - .arg(nowStr).arg(thenStr).arg(remainingStr); - - statusBar()->showMessage(m_myStatusMessage); -} - -void -MainWindow::globalCentreFrameChanged(unsigned long ) -{ - if ((m_playSource && m_playSource->isPlaying()) || !getMainModel()) return; - Pane *p = 0; - if (!m_paneStack || !(p = m_paneStack->getCurrentPane())) return; - if (!p->getFollowGlobalPan()) return; - updateVisibleRangeDisplay(p); -} - -void -MainWindow::viewCentreFrameChanged(View *v, unsigned long ) -{ - if ((m_playSource && m_playSource->isPlaying()) || !getMainModel()) return; - Pane *p = 0; - if (!m_paneStack || !(p = m_paneStack->getCurrentPane())) return; - if (v == p) updateVisibleRangeDisplay(p); -} - -void -MainWindow::viewZoomLevelChanged(View *v, unsigned long , bool ) -{ - if ((m_playSource && m_playSource->isPlaying()) || !getMainModel()) return; - Pane *p = 0; - if (!m_paneStack || !(p = m_paneStack->getCurrentPane())) return; - if (v == p) updateVisibleRangeDisplay(p); -} - -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::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("Audio effects plugin auditioning has been disabled\ndue to a processing overload.")); -} - -void -MainWindow::layerAdded(Layer *) -{ -// std::cerr << "MainWindow::layerAdded(" << layer << ")" << std::endl; -// setupExistingLayersMenu(); - updateMenuStates(); -} - -void -MainWindow::layerRemoved(Layer *) -{ -// std::cerr << "MainWindow::layerRemoved(" << layer << ")" << std::endl; - setupExistingLayersMenus(); - updateMenuStates(); -} - -void -MainWindow::layerAboutToBeDeleted(Layer *layer) -{ -// std::cerr << "MainWindow::layerAboutToBeDeleted(" << layer << ")" << std::endl; - if (layer == m_timeRulerLayer) { -// std::cerr << "(this is the time ruler layer)" << std::endl; - m_timeRulerLayer = 0; - } -} - -void -MainWindow::layerInAView(Layer *layer, bool inAView) -{ -// std::cerr << "MainWindow::layerInAView(" << layer << "," << inAView << ")" << std::endl; - - // Check whether we need to add or remove model from play source - Model *model = layer->getModel(); - if (model) { - if (inAView) { - m_playSource->addModel(model); - } else { - bool found = false; - for (int i = 0; i < m_paneStack->getPaneCount(); ++i) { - Pane *pane = m_paneStack->getPane(i); - if (!pane) continue; - for (int j = 0; j < pane->getLayerCount(); ++j) { - Layer *pl = pane->getLayer(j); - if (pl && pl->getModel() == model) { - found = true; - break; - } - } - if (found) break; - } - if (!found) m_playSource->removeModel(model); - } - } - - setupExistingLayersMenus(); - updateMenuStates(); -} - -void -MainWindow::modelAdded(Model *model) -{ -// std::cerr << "MainWindow::modelAdded(" << model << ")" << std::endl; - m_playSource->addModel(model); - if (dynamic_cast<DenseTimeValueModel *>(model)) { - setupPaneAndLayerMenus(); - } -} - -void -MainWindow::mainModelChanged(WaveFileModel *model) -{ -// std::cerr << "MainWindow::mainModelChanged(" << model << ")" << std::endl; - updateDescriptionLabel(); - m_panLayer->setModel(model); - if (model) m_viewManager->setMainModelSampleRate(model->getSampleRate()); - if (model && !m_playTarget && m_audioOutput) createPlayTarget(); - - updateMenuStates(); -} - -void -MainWindow::modelAboutToBeDeleted(Model *model) -{ -// std::cerr << "MainWindow::modelAboutToBeDeleted(" << model << ")" << std::endl; - m_playSource->removeModel(model); - FFTDataServer::modelAboutToBeDeleted(model); -} - -void -MainWindow::modelGenerationFailed(QString transformName) -{ - QMessageBox::warning - (this, - tr("Failed to generate layer"), - tr("Failed to generate a derived layer.\n\nThe layer transform \"%1\" failed.\n\nThis probably means that a plugin failed to initialise, perhaps because it\nrejected the processing block size that was requested.") - .arg(transformName), - QMessageBox::Ok, 0); -} - -void -MainWindow::modelRegenerationFailed(QString layerName, QString transformName) -{ - QMessageBox::warning - (this, - tr("Failed to regenerate layer"), - tr("Failed to regenerate derived layer \"%1\".\n\nThe layer transform \"%2\" failed to run.\n\nThis probably means the layer used a plugin that is not currently available.") - .arg(layerName).arg(transformName), - QMessageBox::Ok, 0); -} - -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::propertyStacksResized() -{ -/* - std::cerr << "MainWindow::propertyStacksResized" << std::endl; - Pane *pane = m_paneStack->getCurrentPane(); - if (pane && m_overview) { - std::cerr << "Fixed width: " << pane->width() << std::endl; - m_overview->setFixedWidth(pane->width()); - } -*/ -} - -void -MainWindow::showLayerTree() -{ - QTreeView *view = new QTreeView(); - LayerTreeModel *tree = new LayerTreeModel(m_paneStack); - view->expand(tree->index(0, 0, QModelIndex())); - view->setModel(tree); - view->show(); -} - -void -MainWindow::pollOSC() -{ - if (!m_oscQueue || m_oscQueue->isEmpty()) return; - std::cerr << "MainWindow::pollOSC: have " << m_oscQueue->getMessagesAvailable() << " messages" << std::endl; - - if (m_openingAudioFile) return; - - OSCMessage message = m_oscQueue->readMessage(); - - if (message.getTarget() != 0) { - return; //!!! for now -- this class is target 0, others not handled yet - } - - handleOSCMessage(message); -} - -void -MainWindow::handleOSCMessage(const OSCMessage &message) -{ - std::cerr << "MainWindow::handleOSCMessage: thread id = " - << QThread::currentThreadId() << std::endl; - - // This large function should really be abstracted out. - - if (message.getMethod() == "open") { - - if (message.getArgCount() == 1 && - message.getArg(0).canConvert(QVariant::String)) { - QString path = message.getArg(0).toString(); - if (openSomeFile(path, ReplaceMainModel) != FileOpenSucceeded) { - std::cerr << "MainWindow::handleOSCMessage: File open failed for path \"" - << path.toStdString() << "\"" << std::endl; - } - //!!! we really need to spin here and not return until the - // file has been completely decoded... - } - - } else if (message.getMethod() == "openadditional") { - - if (message.getArgCount() == 1 && - message.getArg(0).canConvert(QVariant::String)) { - QString path = message.getArg(0).toString(); - if (openSomeFile(path, CreateAdditionalModel) != FileOpenSucceeded) { - std::cerr << "MainWindow::handleOSCMessage: File open failed for path \"" - << path.toStdString() << "\"" << std::endl; - } - } - - } else if (message.getMethod() == "recent" || - message.getMethod() == "last") { - - int n = 0; - if (message.getMethod() == "recent" && - message.getArgCount() == 1 && - message.getArg(0).canConvert(QVariant::Int)) { - n = message.getArg(0).toInt() - 1; - } - std::vector<QString> recent = m_recentFiles.getRecent(); - if (n >= 0 && n < int(recent.size())) { - if (openSomeFile(recent[n], ReplaceMainModel) != FileOpenSucceeded) { - std::cerr << "MainWindow::handleOSCMessage: File open failed for path \"" - << recent[n].toStdString() << "\"" << std::endl; - } - } - - } else if (message.getMethod() == "save") { - - QString path; - if (message.getArgCount() == 1 && - message.getArg(0).canConvert(QVariant::String)) { - path = message.getArg(0).toString(); - if (QFileInfo(path).exists()) { - std::cerr << "MainWindow::handleOSCMessage: Refusing to overwrite existing file in save" << std::endl; - } else { - saveSessionFile(path); - } - } - - } else if (message.getMethod() == "export") { - - QString path; - if (getMainModel()) { - if (message.getArgCount() == 1 && - message.getArg(0).canConvert(QVariant::String)) { - path = message.getArg(0).toString(); - if (QFileInfo(path).exists()) { - std::cerr << "MainWindow::handleOSCMessage: Refusing to overwrite existing file in export" << std::endl; - } else { - WavFileWriter writer(path, - getMainModel()->getSampleRate(), - getMainModel()->getChannelCount()); - MultiSelection ms = m_viewManager->getSelection(); - if (!ms.getSelections().empty()) { - writer.writeModel(getMainModel(), &ms); - } else { - writer.writeModel(getMainModel()); - } - } - } - } - - } else if (message.getMethod() == "jump" || - message.getMethod() == "play") { - - if (getMainModel()) { - - unsigned long frame = m_viewManager->getPlaybackFrame(); - bool selection = false; - bool play = (message.getMethod() == "play"); - - if (message.getArgCount() == 1) { - - if (message.getArg(0).canConvert(QVariant::String) && - message.getArg(0).toString() == "selection") { - - selection = true; - - } else if (message.getArg(0).canConvert(QVariant::String) && - message.getArg(0).toString() == "end") { - - frame = getMainModel()->getEndFrame(); - - } else if (message.getArg(0).canConvert(QVariant::Double)) { - - double time = message.getArg(0).toDouble(); - if (time < 0.0) time = 0.0; - - frame = lrint(time * getMainModel()->getSampleRate()); - } - } - - if (frame > getMainModel()->getEndFrame()) { - frame = getMainModel()->getEndFrame(); - } - - if (play) { - m_viewManager->setPlaySelectionMode(selection); - } - - if (selection) { - MultiSelection::SelectionList sl = m_viewManager->getSelections(); - if (!sl.empty()) { - frame = sl.begin()->getStartFrame(); - } - } - - m_viewManager->setPlaybackFrame(frame); - - if (play && !m_playSource->isPlaying()) { - m_playSource->play(frame); - } - } - - } else if (message.getMethod() == "stop") { - - if (m_playSource->isPlaying()) m_playSource->stop(); - - } else if (message.getMethod() == "loop") { - - if (message.getArgCount() == 1 && - message.getArg(0).canConvert(QVariant::String)) { - - QString str = message.getArg(0).toString(); - if (str == "on") { - m_viewManager->setPlayLoopMode(true); - } else if (str == "off") { - m_viewManager->setPlayLoopMode(false); - } - } - - } else if (message.getMethod() == "select" || - message.getMethod() == "addselect") { - - if (getMainModel()) { - - int f0 = getMainModel()->getStartFrame(); - int f1 = getMainModel()->getEndFrame(); - - bool done = false; - - if (message.getArgCount() == 2 && - message.getArg(0).canConvert(QVariant::Double) && - message.getArg(1).canConvert(QVariant::Double)) { - - double t0 = message.getArg(0).toDouble(); - double t1 = message.getArg(1).toDouble(); - if (t1 < t0) { double temp = t0; t0 = t1; t1 = temp; } - if (t0 < 0.0) t0 = 0.0; - if (t1 < 0.0) t1 = 0.0; - - f0 = lrint(t0 * getMainModel()->getSampleRate()); - f1 = lrint(t1 * getMainModel()->getSampleRate()); - - Pane *pane = m_paneStack->getCurrentPane(); - Layer *layer = 0; - if (pane) layer = pane->getSelectedLayer(); - if (layer) { - size_t resolution; - layer->snapToFeatureFrame(pane, f0, resolution, - Layer::SnapLeft); - layer->snapToFeatureFrame(pane, f1, resolution, - Layer::SnapRight); - } - - } else if (message.getArgCount() == 1 && - message.getArg(0).canConvert(QVariant::String)) { - - QString str = message.getArg(0).toString(); - if (str == "none") { - m_viewManager->clearSelections(); - done = true; - } - } - - if (!done) { - if (message.getMethod() == "select") { - m_viewManager->setSelection(Selection(f0, f1)); - } else { - m_viewManager->addSelection(Selection(f0, f1)); - } - } - } - - } else if (message.getMethod() == "add") { - - if (getMainModel()) { - - if (message.getArgCount() >= 1 && - message.getArg(0).canConvert(QVariant::String)) { - - int channel = -1; - if (message.getArgCount() == 2 && - message.getArg(0).canConvert(QVariant::Int)) { - channel = message.getArg(0).toInt(); - if (channel < -1 || - channel > int(getMainModel()->getChannelCount())) { - std::cerr << "WARNING: MainWindow::handleOSCMessage: channel " - << channel << " out of range" << std::endl; - channel = -1; - } - } - - QString str = message.getArg(0).toString(); - - LayerFactory::LayerType type = - LayerFactory::getInstance()->getLayerTypeForName(str); - - if (type == LayerFactory::UnknownLayer) { - std::cerr << "WARNING: MainWindow::handleOSCMessage: unknown layer " - << "type " << str.toStdString() << std::endl; - } else { - - PaneConfiguration configuration(type, - getMainModel(), - channel); - - addPane(configuration, - tr("Add %1 Pane") - .arg(LayerFactory::getInstance()-> - getLayerPresentationName(type))); - } - } - } - - } else if (message.getMethod() == "undo") { - - CommandHistory::getInstance()->undo(); - - } else if (message.getMethod() == "redo") { - - CommandHistory::getInstance()->redo(); - - } else if (message.getMethod() == "set") { - - if (message.getArgCount() == 2 && - message.getArg(0).canConvert(QVariant::String) && - message.getArg(1).canConvert(QVariant::Double)) { - - QString property = message.getArg(0).toString(); - float value = (float)message.getArg(1).toDouble(); - - if (property == "gain") { - if (value < 0.0) value = 0.0; - m_fader->setValue(value); - if (m_playTarget) m_playTarget->setOutputGain(value); - } else if (property == "speedup") { - m_playSpeed->setMappedValue(value); - } else if (property == "overlays") { - if (value < 0.5) { - m_viewManager->setOverlayMode(ViewManager::NoOverlays); - } else if (value < 1.5) { - m_viewManager->setOverlayMode(ViewManager::MinimalOverlays); - } else if (value < 2.5) { - m_viewManager->setOverlayMode(ViewManager::StandardOverlays); - } else { - m_viewManager->setOverlayMode(ViewManager::AllOverlays); - } - } else if (property == "zoomwheels") { - m_viewManager->setZoomWheelsEnabled(value > 0.5); - } else if (property == "propertyboxes") { - bool toggle = ((value < 0.5) != - (m_paneStack->getLayoutStyle() == PaneStack::NoPropertyStacks)); - if (toggle) togglePropertyBoxes(); - } - - } else { - PropertyContainer *container = 0; - Pane *pane = m_paneStack->getCurrentPane(); - if (pane && - message.getArgCount() == 3 && - message.getArg(0).canConvert(QVariant::String) && - message.getArg(1).canConvert(QVariant::String) && - message.getArg(2).canConvert(QVariant::String)) { - if (message.getArg(0).toString() == "pane") { - container = pane->getPropertyContainer(0); - } else if (message.getArg(0).toString() == "layer") { - container = pane->getSelectedLayer(); - } - } - if (container) { - QString nameString = message.getArg(1).toString(); - QString valueString = message.getArg(2).toString(); - container->setPropertyWithCommand(nameString, valueString); - } - } - - } else if (message.getMethod() == "setcurrent") { - - int paneIndex = -1, layerIndex = -1; - bool wantLayer = false; - - if (message.getArgCount() >= 1 && - message.getArg(0).canConvert(QVariant::Int)) { - - paneIndex = message.getArg(0).toInt() - 1; - - if (message.getArgCount() >= 2 && - message.getArg(1).canConvert(QVariant::Int)) { - wantLayer = true; - layerIndex = message.getArg(1).toInt() - 1; - } - } - - if (paneIndex >= 0 && paneIndex < m_paneStack->getPaneCount()) { - Pane *pane = m_paneStack->getPane(paneIndex); - m_paneStack->setCurrentPane(pane); - if (layerIndex >= 0 && layerIndex < pane->getLayerCount()) { - Layer *layer = pane->getLayer(layerIndex); - m_paneStack->setCurrentLayer(pane, layer); - } else if (wantLayer && layerIndex == -1) { - m_paneStack->setCurrentLayer(pane, 0); - } - } - - } else if (message.getMethod() == "delete") { - - if (message.getArgCount() == 1 && - message.getArg(0).canConvert(QVariant::String)) { - - QString target = message.getArg(0).toString(); - - if (target == "pane") { - - deleteCurrentPane(); - - } else if (target == "layer") { - - deleteCurrentLayer(); - - } else { - - std::cerr << "WARNING: MainWindow::handleOSCMessage: Unknown delete target " << target.toStdString() << std::endl; - } - } - - } else if (message.getMethod() == "zoom") { - - if (message.getArgCount() == 1) { - if (message.getArg(0).canConvert(QVariant::String) && - message.getArg(0).toString() == "in") { - zoomIn(); - } else if (message.getArg(0).canConvert(QVariant::String) && - message.getArg(0).toString() == "out") { - zoomOut(); - } else if (message.getArg(0).canConvert(QVariant::String) && - message.getArg(0).toString() == "default") { - zoomDefault(); - } else if (message.getArg(0).canConvert(QVariant::Double)) { - double level = message.getArg(0).toDouble(); - Pane *currentPane = m_paneStack->getCurrentPane(); - if (level < 1.0) level = 1.0; - if (currentPane) currentPane->setZoomLevel(lrint(level)); - } - } - - } else if (message.getMethod() == "zoomvertical") { - - Pane *pane = m_paneStack->getCurrentPane(); - Layer *layer = 0; - if (pane && pane->getLayerCount() > 0) { - layer = pane->getLayer(pane->getLayerCount() - 1); - } - int defaultStep = 0; - int steps = 0; - if (layer) { - steps = layer->getVerticalZoomSteps(defaultStep); - if (message.getArgCount() == 1 && steps > 0) { - if (message.getArg(0).canConvert(QVariant::String) && - message.getArg(0).toString() == "in") { - int step = layer->getCurrentVerticalZoomStep() + 1; - if (step < steps) layer->setVerticalZoomStep(step); - } else if (message.getArg(0).canConvert(QVariant::String) && - message.getArg(0).toString() == "out") { - int step = layer->getCurrentVerticalZoomStep() - 1; - if (step >= 0) layer->setVerticalZoomStep(step); - } else if (message.getArg(0).canConvert(QVariant::String) && - message.getArg(0).toString() == "default") { - layer->setVerticalZoomStep(defaultStep); - } - } else if (message.getArgCount() == 2) { - if (message.getArg(0).canConvert(QVariant::Double) && - message.getArg(1).canConvert(QVariant::Double)) { - double min = message.getArg(0).toDouble(); - double max = message.getArg(1).toDouble(); - layer->setDisplayExtents(min, max); - } - } - } - - } else if (message.getMethod() == "quit") { - - m_abandoning = true; - close(); - - } else if (message.getMethod() == "resize") { - - if (message.getArgCount() == 2) { - - int width = 0, height = 0; - - if (message.getArg(1).canConvert(QVariant::Int)) { - - height = message.getArg(1).toInt(); - - if (message.getArg(0).canConvert(QVariant::String) && - message.getArg(0).toString() == "pane") { - - Pane *pane = m_paneStack->getCurrentPane(); - if (pane) pane->resize(pane->width(), height); - - } else if (message.getArg(0).canConvert(QVariant::Int)) { - - width = message.getArg(0).toInt(); - resize(width, height); - } - } - } - - } else if (message.getMethod() == "transform") { - - Pane *pane = m_paneStack->getCurrentPane(); - - if (getMainModel() && - pane && - message.getArgCount() == 1 && - message.getArg(0).canConvert(QVariant::String)) { - - TransformId transform = message.getArg(0).toString(); - - Layer *newLayer = m_document->createDerivedLayer - (transform, - getMainModel(), - TransformFactory::getInstance()->getDefaultContextForTransform - (transform, getMainModel()), - ""); - - if (newLayer) { - m_document->addLayerToView(pane, newLayer); - m_recentTransforms.add(transform); - m_paneStack->setCurrentLayer(pane, newLayer); - } - } - - } else { - std::cerr << "WARNING: MainWindow::handleOSCMessage: Unknown or unsupported " - << "method \"" << message.getMethod().toStdString() - << "\"" << std::endl; - } - -} - -void -MainWindow::preferences() -{ - if (!m_preferencesDialog.isNull()) { - m_preferencesDialog->show(); - m_preferencesDialog->raise(); - return; - } - - m_preferencesDialog = new PreferencesDialog(this); - - // DeleteOnClose is safe here, because m_preferencesDialog is a - // QPointer that will be zeroed when the dialog is deleted. We - // use it in preference to leaving the dialog lying around because - // if you Cancel the dialog, it resets the preferences state - // without resetting its own widgets, so its state will be - // incorrect when next shown unless we construct it afresh - m_preferencesDialog->setAttribute(Qt::WA_DeleteOnClose); - - m_preferencesDialog->show(); -} - -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::inProgressSelectionChanged() -{ - Pane *currentPane = 0; - if (m_paneStack) currentPane = m_paneStack->getCurrentPane(); - if (currentPane) updateVisibleRangeDisplay(currentPane); -} - -void -MainWindow::contextHelpChanged(const QString &s) -{ - if (s == "" && m_myStatusMessage != "") { - statusBar()->showMessage(m_myStatusMessage); - return; - } - statusBar()->showMessage(s); -} - -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::openHelpUrl(QString url) -{ - // This method mostly lifted from Qt Assistant source code - - QProcess *process = new QProcess(this); - connect(process, SIGNAL(finished(int)), process, SLOT(deleteLater())); - - QStringList args; - -#ifdef Q_OS_MAC - args.append(url); - process->start("open", args); -#else -#ifdef Q_OS_WIN32 - - QString pf(getenv("ProgramFiles")); - QString command = pf + QString("\\Internet Explorer\\IEXPLORE.EXE"); - - args.append(url); - process->start(command, args); - -#else -#ifdef Q_WS_X11 - if (!qgetenv("KDE_FULL_SESSION").isEmpty()) { - args.append("exec"); - args.append(url); - process->start("kfmclient", args); - } else if (!qgetenv("BROWSER").isEmpty()) { - args.append(url); - process->start(qgetenv("BROWSER"), args); - } else { - args.append(url); - process->start("firefox", args); - } -#endif -#endif -#endif -} - -void -MainWindow::about() -{ - bool debug = false; - QString version = "(unknown version)"; - -#ifdef BUILD_DEBUG - debug = true; -#endif -#ifdef SA_VERSION -#ifdef SVNREV - version = tr("Release %1 : Revision %2").arg(SA_VERSION).arg(SVNREV); -#else - version = tr("Release %1").arg(SA_VERSION); -#endif -#else -#ifdef SVNREV - version = tr("Unreleased : Revision %1").arg(SVNREV); -#endif -#endif - - QString aboutText; - +bool MainWindow::isAudioPlaying() +{ + return (m_playSource->isPlaying()); +} +// +void +MainWindow::ffwd() +{ + if (!getMainModel()) return; + + int frame = m_viewManager->getPlaybackFrame(); + ++frame; + + Pane *pane = m_paneStack->getCurrentPane(); + if (!pane) return; + + Layer *layer = pane->getSelectedLayer(); + + if (!dynamic_cast<TimeInstantLayer *>(layer) && + !dynamic_cast<TimeValueLayer *>(layer)) return; + + size_t resolution = 0; + if (!layer->snapToFeatureFrame(pane, frame, resolution, Layer::SnapRight)) { + frame = getMainModel()->getEndFrame(); + } + + m_viewManager->setPlaybackFrame(frame); +} + +void +MainWindow::ffwdEnd() +{ + if (!getMainModel()) return; + m_viewManager->setPlaybackFrame(getMainModel()->getEndFrame()); +} + +void +MainWindow::rewind() +{ + if (!getMainModel()) return; + + int frame = m_viewManager->getPlaybackFrame(); + if (frame > 0) --frame; + + Pane *pane = m_paneStack->getCurrentPane(); + if (!pane) return; + + Layer *layer = pane->getSelectedLayer(); + + if (!dynamic_cast<TimeInstantLayer *>(layer) && + !dynamic_cast<TimeValueLayer *>(layer)) return; + + size_t resolution = 0; + if (!layer->snapToFeatureFrame(pane, frame, resolution, Layer::SnapLeft)) { + frame = getMainModel()->getEndFrame(); + } + + m_viewManager->setPlaybackFrame(frame); +} + +void +MainWindow::rewindStart() +{ + if (!getMainModel()) return; + m_viewManager->setPlaybackFrame(getMainModel()->getStartFrame()); +} + +void +MainWindow::stop() +{ + m_playSource->stop(); + + if (m_paneStack && m_paneStack->getCurrentPane()) { + updateVisibleRangeDisplay(m_paneStack->getCurrentPane()); + } else { + m_myStatusMessage = ""; + statusBar()->showMessage(""); + } +} + +void +MainWindow::addPane() +{ + QObject *s = sender(); + QAction *action = dynamic_cast<QAction *>(s); + + if (!action) { + std::cerr << "WARNING: MainWindow::addPane: sender is not an action" + << std::endl; + return; + } + + PaneActionMap::iterator i = m_paneActions.find(action); + + if (i == m_paneActions.end()) { + std::cerr << "WARNING: MainWindow::addPane: unknown action " + << action->objectName().toStdString() << std::endl; + return; + } + + addPane(i->second, action->text()); +} + +void +MainWindow::addPane(const PaneConfiguration &configuration, QString text) +{ + CommandHistory::getInstance()->startCompoundOperation(text, true); + + AddPaneCommand *command = new AddPaneCommand(this); + CommandHistory::getInstance()->addCommand(command); + + Pane *pane = command->getPane(); + + if (configuration.layer == LayerFactory::Spectrum) { + pane->setPlaybackFollow(PlaybackScrollContinuous); + pane->setFollowGlobalZoom(false); + pane->setZoomLevel(512); + } + + if (configuration.layer != LayerFactory::TimeRuler && + configuration.layer != LayerFactory::Spectrum) { + + if (!m_timeRulerLayer) { +// std::cerr << "no time ruler layer, creating one" << std::endl; + m_timeRulerLayer = m_document->createMainModelLayer + (LayerFactory::TimeRuler); + } + +// std::cerr << "adding time ruler layer " << m_timeRulerLayer << std::endl; + + m_document->addLayerToView(pane, m_timeRulerLayer); + } + + Layer *newLayer = m_document->createLayer(configuration.layer); + + Model *suggestedModel = configuration.sourceModel; + Model *model = 0; + + if (suggestedModel) { + + // check its validity + std::vector<Model *> inputModels = m_document->getTransformInputModels(); + for (size_t j = 0; j < inputModels.size(); ++j) { + if (inputModels[j] == suggestedModel) { + model = suggestedModel; + break; + } + } + + if (!model) { + std::cerr << "WARNING: Model " << (void *)suggestedModel + << " appears in pane action map, but is not reported " + << "by document as a valid transform source" << std::endl; + } + } + + if (!model) model = m_document->getMainModel(); + + m_document->setModel(newLayer, model); + + m_document->setChannel(newLayer, configuration.channel); + m_document->addLayerToView(pane, newLayer); + + m_paneStack->setCurrentPane(pane); + m_paneStack->setCurrentLayer(pane, newLayer); + +// std::cerr << "MainWindow::addPane: global centre frame is " +// << m_viewManager->getGlobalCentreFrame() << std::endl; +// pane->setCentreFrame(m_viewManager->getGlobalCentreFrame()); + + CommandHistory::getInstance()->endCompoundOperation(); + + updateMenuStates(); +} + +MainWindow::AddPaneCommand::AddPaneCommand(MainWindow *mw) : + m_mw(mw), + m_pane(0), + m_prevCurrentPane(0), + m_added(false) +{ +} + +MainWindow::AddPaneCommand::~AddPaneCommand() +{ + if (m_pane && !m_added) { + m_mw->m_paneStack->deletePane(m_pane); + } +} + +QString +MainWindow::AddPaneCommand::getName() const +{ + return tr("Add Pane"); +} + +void +MainWindow::AddPaneCommand::execute() +{ + if (!m_pane) { + m_prevCurrentPane = m_mw->m_paneStack->getCurrentPane(); + m_pane = m_mw->m_paneStack->addPane(); + + connect(m_pane, SIGNAL(contextHelpChanged(const QString &)), + m_mw, SLOT(contextHelpChanged(const QString &))); + } else { + m_mw->m_paneStack->showPane(m_pane); + } + + m_mw->m_paneStack->setCurrentPane(m_pane); + m_mw->m_overview->registerView(m_pane); + m_added = true; +} + +void +MainWindow::AddPaneCommand::unexecute() +{ + m_mw->m_paneStack->hidePane(m_pane); + m_mw->m_paneStack->setCurrentPane(m_prevCurrentPane); + m_mw->m_overview->unregisterView(m_pane); + m_added = false; +} + +MainWindow::RemovePaneCommand::RemovePaneCommand(MainWindow *mw, Pane *pane) : + m_mw(mw), + m_pane(pane), + m_added(true) +{ +} + +MainWindow::RemovePaneCommand::~RemovePaneCommand() +{ + if (m_pane && !m_added) { + m_mw->m_paneStack->deletePane(m_pane); + } +} + +QString +MainWindow::RemovePaneCommand::getName() const +{ + return tr("Remove Pane"); +} + +void +MainWindow::RemovePaneCommand::execute() +{ + m_prevCurrentPane = m_mw->m_paneStack->getCurrentPane(); + m_mw->m_paneStack->hidePane(m_pane); + m_mw->m_overview->unregisterView(m_pane); + m_added = false; +} + +void +MainWindow::RemovePaneCommand::unexecute() +{ + m_mw->m_paneStack->showPane(m_pane); + m_mw->m_paneStack->setCurrentPane(m_prevCurrentPane); + m_mw->m_overview->registerView(m_pane); + m_added = true; +} + +void +MainWindow::addLayer() +{ + QObject *s = sender(); + QAction *action = dynamic_cast<QAction *>(s); + + if (!action) { + std::cerr << "WARNING: MainWindow::addLayer: sender is not an action" + << std::endl; + return; + } + + Pane *pane = m_paneStack->getCurrentPane(); + + if (!pane) { + std::cerr << "WARNING: MainWindow::addLayer: no current pane" << std::endl; + return; + } + + ExistingLayerActionMap::iterator ei = m_existingLayerActions.find(action); + + if (ei != m_existingLayerActions.end()) { + Layer *newLayer = ei->second; + m_document->addLayerToView(pane, newLayer); + m_paneStack->setCurrentLayer(pane, newLayer); + return; + } + + ei = m_sliceActions.find(action); + + if (ei != m_sliceActions.end()) { + Layer *newLayer = m_document->createLayer(LayerFactory::Slice); +// document->setModel(newLayer, ei->second->getModel()); + SliceableLayer *source = dynamic_cast<SliceableLayer *>(ei->second); + SliceLayer *dest = dynamic_cast<SliceLayer *>(newLayer); + if (source && dest) { + dest->setSliceableModel(source->getSliceableModel()); + connect(source, SIGNAL(sliceableModelReplaced(const Model *, const Model *)), + dest, SLOT(sliceableModelReplaced(const Model *, const Model *))); + connect(m_document, SIGNAL(modelAboutToBeDeleted(Model *)), + dest, SLOT(modelAboutToBeDeleted(Model *))); + } + m_document->addLayerToView(pane, newLayer); + m_paneStack->setCurrentLayer(pane, newLayer); + return; + } + + TransformActionMap::iterator i = m_transformActions.find(action); + + if (i == m_transformActions.end()) { + + LayerActionMap::iterator i = m_layerActions.find(action); + + if (i == m_layerActions.end()) { + std::cerr << "WARNING: MainWindow::addLayer: unknown action " + << action->objectName().toStdString() << std::endl; + return; + } + + LayerFactory::LayerType type = i->second; + + LayerFactory::LayerTypeSet emptyTypes = + LayerFactory::getInstance()->getValidEmptyLayerTypes(); + + Layer *newLayer; + + if (emptyTypes.find(type) != emptyTypes.end()) { + + newLayer = m_document->createEmptyLayer(type); + m_toolActions[ViewManager::DrawMode]->trigger(); + + } else { + + newLayer = m_document->createMainModelLayer(type); + } + + m_document->addLayerToView(pane, newLayer); + m_paneStack->setCurrentLayer(pane, newLayer); + + return; + } + + TransformId transform = i->second; + TransformFactory *factory = TransformFactory::getInstance(); + + QString configurationXml; + + int channel = -1; + // pick up the default channel from any existing layers on the same pane + for (int j = 0; j < pane->getLayerCount(); ++j) { + int c = LayerFactory::getInstance()->getChannel(pane->getLayer(j)); + if (c != -1) { + channel = c; + break; + } + } + + // We always ask for configuration, even if the plugin isn't + // supposed to be configurable, because we need to let the user + // change the execution context (block size etc). + + PluginTransform::ExecutionContext context(channel); + + std::vector<Model *> candidateInputModels = + m_document->getTransformInputModels(); + + Model *inputModel = factory->getConfigurationForTransform(transform, + candidateInputModels, + context, + configurationXml, + m_playSource); + if (!inputModel) return; + +// std::cerr << "MainWindow::addLayer: Input model is " << inputModel << " \"" << inputModel->objectName().toStdString() << "\"" << std::endl; + + Layer *newLayer = m_document->createDerivedLayer(transform, + inputModel, + context, + configurationXml); + + if (newLayer) { + m_document->addLayerToView(pane, newLayer); + m_document->setChannel(newLayer, context.channel); + m_recentTransforms.add(transform); + m_paneStack->setCurrentLayer(pane, newLayer); + } + + updateMenuStates(); +} + +void +MainWindow::deleteCurrentPane() +{ + CommandHistory::getInstance()->startCompoundOperation + (tr("Delete Pane"), true); + + Pane *pane = m_paneStack->getCurrentPane(); + if (pane) { + while (pane->getLayerCount() > 0) { + Layer *layer = pane->getLayer(0); + if (layer) { + m_document->removeLayerFromView(pane, layer); + } else { + break; + } + } + + RemovePaneCommand *command = new RemovePaneCommand(this, pane); + CommandHistory::getInstance()->addCommand(command); + } + + CommandHistory::getInstance()->endCompoundOperation(); + + updateMenuStates(); +} + +void +MainWindow::deleteCurrentLayer() +{ + Pane *pane = m_paneStack->getCurrentPane(); + if (pane) { + Layer *layer = pane->getSelectedLayer(); + if (layer) { + m_document->removeLayerFromView(pane, layer); + } + } + updateMenuStates(); +} + +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); + setupExistingLayersMenus(); + } + } + } +} + +void +MainWindow::playSpeedChanged(int position) +{ + PlaySpeedRangeMapper mapper(0, 200); + + float percent = m_playSpeed->mappedValue(); + + float factor = mapper.getFactorForValue(percent); + +// float factor = mapper.getFactorForPosition(position); +// float percent = mapper.getValueForPosition(position); + + std::cerr << "speed = " << position << " percent = " << percent << " factor = " << factor << std::endl; + +//!!! bool slow = (position < 100); + bool something = (position != 100); +/*!!! + int pc = lrintf(percent); + + m_playSpeed->setToolTip(tr("Playback speed: %1%2%") + .arg(!slow ? "+" : "") + .arg(pc)); +*/ + m_playSharpen->setEnabled(something); + m_playMono->setEnabled(something); + bool sharpen = (something && m_playSharpen->isChecked()); + bool mono = (something && m_playMono->isChecked()); + m_playSource->setTimeStretch(factor, sharpen, mono); +} + +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::playbackFrameChanged(unsigned long frame) +{ + if (!(m_playSource && m_playSource->isPlaying()) || !getMainModel()) return; + + RealTime now = RealTime::frame2RealTime + (frame, getMainModel()->getSampleRate()); + + if (now.sec == m_lastPlayStatusSec) return; + + RealTime then = RealTime::frame2RealTime + (m_playSource->getPlayEndFrame(), getMainModel()->getSampleRate()); + + QString nowStr; + QString thenStr; + QString remainingStr; + + if (then.sec > 10) { + nowStr = now.toSecText().c_str(); + thenStr = then.toSecText().c_str(); + remainingStr = (then - now).toSecText().c_str(); + m_lastPlayStatusSec = now.sec; + } else { + nowStr = now.toText(true).c_str(); + thenStr = then.toText(true).c_str(); + remainingStr = (then - now).toText(true).c_str(); + } + + m_myStatusMessage = tr("Playing: %1 of %2 (%3 remaining)") + .arg(nowStr).arg(thenStr).arg(remainingStr); + + statusBar()->showMessage(m_myStatusMessage); +} + +void +MainWindow::globalCentreFrameChanged(unsigned long ) +{ + if ((m_playSource && m_playSource->isPlaying()) || !getMainModel()) return; + Pane *p = 0; + if (!m_paneStack || !(p = m_paneStack->getCurrentPane())) return; + if (!p->getFollowGlobalPan()) return; + updateVisibleRangeDisplay(p); +} + +void +MainWindow::viewCentreFrameChanged(View *v, unsigned long ) +{ + if ((m_playSource && m_playSource->isPlaying()) || !getMainModel()) return; + Pane *p = 0; + if (!m_paneStack || !(p = m_paneStack->getCurrentPane())) return; + if (v == p) updateVisibleRangeDisplay(p); +} + +void +MainWindow::viewZoomLevelChanged(View *v, unsigned long , bool ) +{ + if ((m_playSource && m_playSource->isPlaying()) || !getMainModel()) return; + Pane *p = 0; + if (!m_paneStack || !(p = m_paneStack->getCurrentPane())) return; + if (v == p) updateVisibleRangeDisplay(p); +} + +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::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("Audio effects plugin auditioning has been disabled\ndue to a processing overload.")); +} + +void +MainWindow::layerAdded(Layer *) +{ +// std::cerr << "MainWindow::layerAdded(" << layer << ")" << std::endl; +// setupExistingLayersMenu(); + updateMenuStates(); +} + +void +MainWindow::layerRemoved(Layer *) +{ +// std::cerr << "MainWindow::layerRemoved(" << layer << ")" << std::endl; + setupExistingLayersMenus(); + updateMenuStates(); +} + +void +MainWindow::layerAboutToBeDeleted(Layer *layer) +{ +// std::cerr << "MainWindow::layerAboutToBeDeleted(" << layer << ")" << std::endl; + if (layer == m_timeRulerLayer) { +// std::cerr << "(this is the time ruler layer)" << std::endl; + m_timeRulerLayer = 0; + } +} + +void +MainWindow::layerInAView(Layer *layer, bool inAView) +{ +// std::cerr << "MainWindow::layerInAView(" << layer << "," << inAView << ")" << std::endl; + + // Check whether we need to add or remove model from play source + Model *model = layer->getModel(); + if (model) { + if (inAView) { + m_playSource->addModel(model); + } else { + bool found = false; + for (int i = 0; i < m_paneStack->getPaneCount(); ++i) { + Pane *pane = m_paneStack->getPane(i); + if (!pane) continue; + for (int j = 0; j < pane->getLayerCount(); ++j) { + Layer *pl = pane->getLayer(j); + if (pl && pl->getModel() == model) { + found = true; + break; + } + } + if (found) break; + } + if (!found) m_playSource->removeModel(model); + } + } + + setupExistingLayersMenus(); + updateMenuStates(); +} + +void +MainWindow::modelAdded(Model *model) +{ +// std::cerr << "MainWindow::modelAdded(" << model << ")" << std::endl; + m_playSource->addModel(model); + if (dynamic_cast<DenseTimeValueModel *>(model)) { + setupPaneAndLayerMenus(); + } +} + +void +MainWindow::mainModelChanged(WaveFileModel *model) +{ +// std::cerr << "MainWindow::mainModelChanged(" << model << ")" << std::endl; + updateDescriptionLabel(); + m_panLayer->setModel(model); + if (model) m_viewManager->setMainModelSampleRate(model->getSampleRate()); + if (model && !m_playTarget && m_audioOutput) createPlayTarget(); + + updateMenuStates(); +} + +void +MainWindow::modelAboutToBeDeleted(Model *model) +{ +// std::cerr << "MainWindow::modelAboutToBeDeleted(" << model << ")" << std::endl; + m_playSource->removeModel(model); + FFTDataServer::modelAboutToBeDeleted(model); +} + +void +MainWindow::modelGenerationFailed(QString transformName) +{ + QMessageBox::warning + (this, + tr("Failed to generate layer"), + tr("Failed to generate a derived layer.\n\nThe layer transform \"%1\" failed.\n\nThis probably means that a plugin failed to initialise, perhaps because it\nrejected the processing block size that was requested.") + .arg(transformName), + QMessageBox::Ok, 0); +} + +void +MainWindow::modelRegenerationFailed(QString layerName, QString transformName) +{ + QMessageBox::warning + (this, + tr("Failed to regenerate layer"), + tr("Failed to regenerate derived layer \"%1\".\n\nThe layer transform \"%2\" failed to run.\n\nThis probably means the layer used a plugin that is not currently available.") + .arg(layerName).arg(transformName), + QMessageBox::Ok, 0); +} + +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::propertyStacksResized() +{ +/* + std::cerr << "MainWindow::propertyStacksResized" << std::endl; + Pane *pane = m_paneStack->getCurrentPane(); + if (pane && m_overview) { + std::cerr << "Fixed width: " << pane->width() << std::endl; + m_overview->setFixedWidth(pane->width()); + } +*/ +} + +void +MainWindow::showLayerTree() +{ + QTreeView *view = new QTreeView(); + LayerTreeModel *tree = new LayerTreeModel(m_paneStack); + view->expand(tree->index(0, 0, QModelIndex())); + view->setModel(tree); + view->show(); +} + +void +MainWindow::pollOSC() +{ + if (!m_oscQueue || m_oscQueue->isEmpty()) return; + std::cerr << "MainWindow::pollOSC: have " << m_oscQueue->getMessagesAvailable() << " messages" << std::endl; + + if (m_openingAudioFile) return; + + OSCMessage message = m_oscQueue->readMessage(); + + if (message.getTarget() != 0) { + return; //!!! for now -- this class is target 0, others not handled yet + } + + handleOSCMessage(message); +} + +void +MainWindow::handleOSCMessage(const OSCMessage &message) +{ + std::cerr << "MainWindow::handleOSCMessage: thread id = " + << QThread::currentThreadId() << std::endl; + + // This large function should really be abstracted out. + + if (message.getMethod() == "open") { + + if (message.getArgCount() == 1 && + message.getArg(0).canConvert(QVariant::String)) { + QString path = message.getArg(0).toString(); + if (openSomeFile(path, ReplaceMainModel) != FileOpenSucceeded) { + std::cerr << "MainWindow::handleOSCMessage: File open failed for path \"" + << path.toStdString() << "\"" << std::endl; + } + //!!! we really need to spin here and not return until the + // file has been completely decoded... + } + + } else if (message.getMethod() == "openadditional") { + + if (message.getArgCount() == 1 && + message.getArg(0).canConvert(QVariant::String)) { + QString path = message.getArg(0).toString(); + if (openSomeFile(path, CreateAdditionalModel) != FileOpenSucceeded) { + std::cerr << "MainWindow::handleOSCMessage: File open failed for path \"" + << path.toStdString() << "\"" << std::endl; + } + } + + } else if (message.getMethod() == "recent" || + message.getMethod() == "last") { + + int n = 0; + if (message.getMethod() == "recent" && + message.getArgCount() == 1 && + message.getArg(0).canConvert(QVariant::Int)) { + n = message.getArg(0).toInt() - 1; + } + std::vector<QString> recent = m_recentFiles.getRecent(); + if (n >= 0 && n < int(recent.size())) { + if (openSomeFile(recent[n], ReplaceMainModel) != FileOpenSucceeded) { + std::cerr << "MainWindow::handleOSCMessage: File open failed for path \"" + << recent[n].toStdString() << "\"" << std::endl; + } + } + + } else if (message.getMethod() == "save") { + + QString path; + if (message.getArgCount() == 1 && + message.getArg(0).canConvert(QVariant::String)) { + path = message.getArg(0).toString(); + if (QFileInfo(path).exists()) { + std::cerr << "MainWindow::handleOSCMessage: Refusing to overwrite existing file in save" << std::endl; + } else { + saveSessionFile(path); + } + } + + } else if (message.getMethod() == "export") { + + QString path; + if (getMainModel()) { + if (message.getArgCount() == 1 && + message.getArg(0).canConvert(QVariant::String)) { + path = message.getArg(0).toString(); + if (QFileInfo(path).exists()) { + std::cerr << "MainWindow::handleOSCMessage: Refusing to overwrite existing file in export" << std::endl; + } else { + WavFileWriter writer(path, + getMainModel()->getSampleRate(), + getMainModel()->getChannelCount()); + MultiSelection ms = m_viewManager->getSelection(); + if (!ms.getSelections().empty()) { + writer.writeModel(getMainModel(), &ms); + } else { + writer.writeModel(getMainModel()); + } + } + } + } + + } else if (message.getMethod() == "jump" || + message.getMethod() == "play") { + + if (getMainModel()) { + + unsigned long frame = m_viewManager->getPlaybackFrame(); + bool selection = false; + bool play = (message.getMethod() == "play"); + + if (message.getArgCount() == 1) { + + if (message.getArg(0).canConvert(QVariant::String) && + message.getArg(0).toString() == "selection") { + + selection = true; + + } else if (message.getArg(0).canConvert(QVariant::String) && + message.getArg(0).toString() == "end") { + + frame = getMainModel()->getEndFrame(); + + } else if (message.getArg(0).canConvert(QVariant::Double)) { + + double time = message.getArg(0).toDouble(); + if (time < 0.0) time = 0.0; + + frame = lrint(time * getMainModel()->getSampleRate()); + } + } + + if (frame > getMainModel()->getEndFrame()) { + frame = getMainModel()->getEndFrame(); + } + + if (play) { + m_viewManager->setPlaySelectionMode(selection); + } + + if (selection) { + MultiSelection::SelectionList sl = m_viewManager->getSelections(); + if (!sl.empty()) { + frame = sl.begin()->getStartFrame(); + } + } + + m_viewManager->setPlaybackFrame(frame); + + if (play && !m_playSource->isPlaying()) { + m_playSource->play(frame); + } + } + + } else if (message.getMethod() == "stop") { + + if (m_playSource->isPlaying()) m_playSource->stop(); + + } else if (message.getMethod() == "loop") { + + if (message.getArgCount() == 1 && + message.getArg(0).canConvert(QVariant::String)) { + + QString str = message.getArg(0).toString(); + if (str == "on") { + m_viewManager->setPlayLoopMode(true); + } else if (str == "off") { + m_viewManager->setPlayLoopMode(false); + } + } + + } else if (message.getMethod() == "select" || + message.getMethod() == "addselect") { + + if (getMainModel()) { + + int f0 = getMainModel()->getStartFrame(); + int f1 = getMainModel()->getEndFrame(); + + bool done = false; + + if (message.getArgCount() == 2 && + message.getArg(0).canConvert(QVariant::Double) && + message.getArg(1).canConvert(QVariant::Double)) { + + double t0 = message.getArg(0).toDouble(); + double t1 = message.getArg(1).toDouble(); + if (t1 < t0) { double temp = t0; t0 = t1; t1 = temp; } + if (t0 < 0.0) t0 = 0.0; + if (t1 < 0.0) t1 = 0.0; + + f0 = lrint(t0 * getMainModel()->getSampleRate()); + f1 = lrint(t1 * getMainModel()->getSampleRate()); + + Pane *pane = m_paneStack->getCurrentPane(); + Layer *layer = 0; + if (pane) layer = pane->getSelectedLayer(); + if (layer) { + size_t resolution; + layer->snapToFeatureFrame(pane, f0, resolution, + Layer::SnapLeft); + layer->snapToFeatureFrame(pane, f1, resolution, + Layer::SnapRight); + } + + } else if (message.getArgCount() == 1 && + message.getArg(0).canConvert(QVariant::String)) { + + QString str = message.getArg(0).toString(); + if (str == "none") { + m_viewManager->clearSelections(); + done = true; + } + } + + if (!done) { + if (message.getMethod() == "select") { + m_viewManager->setSelection(Selection(f0, f1)); + } else { + m_viewManager->addSelection(Selection(f0, f1)); + } + } + } + + } else if (message.getMethod() == "add") { + + if (getMainModel()) { + + if (message.getArgCount() >= 1 && + message.getArg(0).canConvert(QVariant::String)) { + + int channel = -1; + if (message.getArgCount() == 2 && + message.getArg(0).canConvert(QVariant::Int)) { + channel = message.getArg(0).toInt(); + if (channel < -1 || + channel > int(getMainModel()->getChannelCount())) { + std::cerr << "WARNING: MainWindow::handleOSCMessage: channel " + << channel << " out of range" << std::endl; + channel = -1; + } + } + + QString str = message.getArg(0).toString(); + + LayerFactory::LayerType type = + LayerFactory::getInstance()->getLayerTypeForName(str); + + if (type == LayerFactory::UnknownLayer) { + std::cerr << "WARNING: MainWindow::handleOSCMessage: unknown layer " + << "type " << str.toStdString() << std::endl; + } else { + + PaneConfiguration configuration(type, + getMainModel(), + channel); + + addPane(configuration, + tr("Add %1 Pane") + .arg(LayerFactory::getInstance()-> + getLayerPresentationName(type))); + } + } + } + + } else if (message.getMethod() == "undo") { + + CommandHistory::getInstance()->undo(); + + } else if (message.getMethod() == "redo") { + + CommandHistory::getInstance()->redo(); + + } else if (message.getMethod() == "set") { + + if (message.getArgCount() == 2 && + message.getArg(0).canConvert(QVariant::String) && + message.getArg(1).canConvert(QVariant::Double)) { + + QString property = message.getArg(0).toString(); + float value = (float)message.getArg(1).toDouble(); + + if (property == "gain") { + if (value < 0.0) value = 0.0; + m_fader->setValue(value); + if (m_playTarget) m_playTarget->setOutputGain(value); + } else if (property == "speedup") { + m_playSpeed->setMappedValue(value); + } else if (property == "overlays") { + if (value < 0.5) { + m_viewManager->setOverlayMode(ViewManager::NoOverlays); + } else if (value < 1.5) { + m_viewManager->setOverlayMode(ViewManager::MinimalOverlays); + } else if (value < 2.5) { + m_viewManager->setOverlayMode(ViewManager::StandardOverlays); + } else { + m_viewManager->setOverlayMode(ViewManager::AllOverlays); + } + } else if (property == "zoomwheels") { + m_viewManager->setZoomWheelsEnabled(value > 0.5); + } else if (property == "propertyboxes") { + bool toggle = ((value < 0.5) != + (m_paneStack->getLayoutStyle() == PaneStack::NoPropertyStacks)); + if (toggle) togglePropertyBoxes(); + } + + } else { + PropertyContainer *container = 0; + Pane *pane = m_paneStack->getCurrentPane(); + if (pane && + message.getArgCount() == 3 && + message.getArg(0).canConvert(QVariant::String) && + message.getArg(1).canConvert(QVariant::String) && + message.getArg(2).canConvert(QVariant::String)) { + if (message.getArg(0).toString() == "pane") { + container = pane->getPropertyContainer(0); + } else if (message.getArg(0).toString() == "layer") { + container = pane->getSelectedLayer(); + } + } + if (container) { + QString nameString = message.getArg(1).toString(); + QString valueString = message.getArg(2).toString(); + container->setPropertyWithCommand(nameString, valueString); + } + } + + } else if (message.getMethod() == "setcurrent") { + + int paneIndex = -1, layerIndex = -1; + bool wantLayer = false; + + if (message.getArgCount() >= 1 && + message.getArg(0).canConvert(QVariant::Int)) { + + paneIndex = message.getArg(0).toInt() - 1; + + if (message.getArgCount() >= 2 && + message.getArg(1).canConvert(QVariant::Int)) { + wantLayer = true; + layerIndex = message.getArg(1).toInt() - 1; + } + } + + if (paneIndex >= 0 && paneIndex < m_paneStack->getPaneCount()) { + Pane *pane = m_paneStack->getPane(paneIndex); + m_paneStack->setCurrentPane(pane); + if (layerIndex >= 0 && layerIndex < pane->getLayerCount()) { + Layer *layer = pane->getLayer(layerIndex); + m_paneStack->setCurrentLayer(pane, layer); + } else if (wantLayer && layerIndex == -1) { + m_paneStack->setCurrentLayer(pane, 0); + } + } + + } else if (message.getMethod() == "delete") { + + if (message.getArgCount() == 1 && + message.getArg(0).canConvert(QVariant::String)) { + + QString target = message.getArg(0).toString(); + + if (target == "pane") { + + deleteCurrentPane(); + + } else if (target == "layer") { + + deleteCurrentLayer(); + + } else { + + std::cerr << "WARNING: MainWindow::handleOSCMessage: Unknown delete target " << target.toStdString() << std::endl; + } + } + + } else if (message.getMethod() == "zoom") { + + if (message.getArgCount() == 1) { + if (message.getArg(0).canConvert(QVariant::String) && + message.getArg(0).toString() == "in") { + zoomIn(); + } else if (message.getArg(0).canConvert(QVariant::String) && + message.getArg(0).toString() == "out") { + zoomOut(); + } else if (message.getArg(0).canConvert(QVariant::String) && + message.getArg(0).toString() == "default") { + zoomDefault(); + } else if (message.getArg(0).canConvert(QVariant::Double)) { + double level = message.getArg(0).toDouble(); + Pane *currentPane = m_paneStack->getCurrentPane(); + if (level < 1.0) level = 1.0; + if (currentPane) currentPane->setZoomLevel(lrint(level)); + } + } + + } else if (message.getMethod() == "zoomvertical") { + + Pane *pane = m_paneStack->getCurrentPane(); + Layer *layer = 0; + if (pane && pane->getLayerCount() > 0) { + layer = pane->getLayer(pane->getLayerCount() - 1); + } + int defaultStep = 0; + int steps = 0; + if (layer) { + steps = layer->getVerticalZoomSteps(defaultStep); + if (message.getArgCount() == 1 && steps > 0) { + if (message.getArg(0).canConvert(QVariant::String) && + message.getArg(0).toString() == "in") { + int step = layer->getCurrentVerticalZoomStep() + 1; + if (step < steps) layer->setVerticalZoomStep(step); + } else if (message.getArg(0).canConvert(QVariant::String) && + message.getArg(0).toString() == "out") { + int step = layer->getCurrentVerticalZoomStep() - 1; + if (step >= 0) layer->setVerticalZoomStep(step); + } else if (message.getArg(0).canConvert(QVariant::String) && + message.getArg(0).toString() == "default") { + layer->setVerticalZoomStep(defaultStep); + } + } else if (message.getArgCount() == 2) { + if (message.getArg(0).canConvert(QVariant::Double) && + message.getArg(1).canConvert(QVariant::Double)) { + double min = message.getArg(0).toDouble(); + double max = message.getArg(1).toDouble(); + layer->setDisplayExtents(min, max); + } + } + } + + } else if (message.getMethod() == "quit") { + + m_abandoning = true; + close(); + + } else if (message.getMethod() == "resize") { + + if (message.getArgCount() == 2) { + + int width = 0, height = 0; + + if (message.getArg(1).canConvert(QVariant::Int)) { + + height = message.getArg(1).toInt(); + + if (message.getArg(0).canConvert(QVariant::String) && + message.getArg(0).toString() == "pane") { + + Pane *pane = m_paneStack->getCurrentPane(); + if (pane) pane->resize(pane->width(), height); + + } else if (message.getArg(0).canConvert(QVariant::Int)) { + + width = message.getArg(0).toInt(); + resize(width, height); + } + } + } + + } else if (message.getMethod() == "transform") { + + Pane *pane = m_paneStack->getCurrentPane(); + + if (getMainModel() && + pane && + message.getArgCount() == 1 && + message.getArg(0).canConvert(QVariant::String)) { + + TransformId transform = message.getArg(0).toString(); + + Layer *newLayer = m_document->createDerivedLayer + (transform, + getMainModel(), + TransformFactory::getInstance()->getDefaultContextForTransform + (transform, getMainModel()), + ""); + + if (newLayer) { + m_document->addLayerToView(pane, newLayer); + m_recentTransforms.add(transform); + m_paneStack->setCurrentLayer(pane, newLayer); + } + } + + } else { + std::cerr << "WARNING: MainWindow::handleOSCMessage: Unknown or unsupported " + << "method \"" << message.getMethod().toStdString() + << "\"" << std::endl; + } + +} + +void +MainWindow::preferences() +{ + if (!m_preferencesDialog.isNull()) { + m_preferencesDialog->show(); + m_preferencesDialog->raise(); + return; + } + + m_preferencesDialog = new PreferencesDialog(this); + + // DeleteOnClose is safe here, because m_preferencesDialog is a + // QPointer that will be zeroed when the dialog is deleted. We + // use it in preference to leaving the dialog lying around because + // if you Cancel the dialog, it resets the preferences state + // without resetting its own widgets, so its state will be + // incorrect when next shown unless we construct it afresh + m_preferencesDialog->setAttribute(Qt::WA_DeleteOnClose); + + m_preferencesDialog->show(); +} + +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::inProgressSelectionChanged() +{ + Pane *currentPane = 0; + if (m_paneStack) currentPane = m_paneStack->getCurrentPane(); + if (currentPane) updateVisibleRangeDisplay(currentPane); +} + +void +MainWindow::contextHelpChanged(const QString &s) +{ + if (s == "" && m_myStatusMessage != "") { + statusBar()->showMessage(m_myStatusMessage); + return; + } + statusBar()->showMessage(s); +} + +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::openHelpUrl(QString url) +{ + // This method mostly lifted from Qt Assistant source code + + QProcess *process = new QProcess(this); + connect(process, SIGNAL(finished(int)), process, SLOT(deleteLater())); + + QStringList args; + +#ifdef Q_OS_MAC + args.append(url); + process->start("open", args); +#else +#ifdef Q_OS_WIN32 + + QString pf(getenv("ProgramFiles")); + QString command = pf + QString("\\Internet Explorer\\IEXPLORE.EXE"); + + args.append(url); + process->start(command, args); + +#else +#ifdef Q_WS_X11 + if (!qgetenv("KDE_FULL_SESSION").isEmpty()) { + args.append("exec"); + args.append(url); + process->start("kfmclient", args); + } else if (!qgetenv("BROWSER").isEmpty()) { + args.append(url); + process->start(qgetenv("BROWSER"), args); + } else { + args.append(url); + process->start("firefox", args); + } +#endif +#endif +#endif +} + +void +MainWindow::about() +{ + bool debug = false; + QString version = "(unknown version)"; + +#ifdef BUILD_DEBUG + debug = true; +#endif +#ifdef SA_VERSION +#ifdef SVNREV + version = tr("Release %1 : Revision %2").arg(SA_VERSION).arg(SVNREV); +#else + version = tr("Release %1").arg(SA_VERSION); +#endif +#else +#ifdef SVNREV + version = tr("Unreleased : Revision %1").arg(SVNREV); +#endif +#endif + + QString aboutText; + aboutText += tr("<h3>About Sound Access</h3>"); aboutText += tr("<p>Sound Access is a program for viewing and exploring multimedia archives for semantic multimedia analysis and retrieval.</p>"); aboutText += tr("<p>EASAIER (EU-FP6-IST-033902) frontend client</p>"); @@ -4918,84 +4960,84 @@ .arg(version) .arg(debug ? tr("Debug") : tr("Release")); - aboutText += tr("<p>Based on Sonic Visualizer Copyright © 2005 - 2007 Chris Cannam and Queen Mary, University of London (Version %1)</p>").arg(SV_VERSION); - -#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>Linked with"); -#ifndef QT_SHARED - aboutText += tr("<br>Qt (v%1) © Trolltech AS").arg(QT_VERSION_STR); -#endif -#ifdef HAVE_JACK -#ifdef JACK_VERSION - aboutText += tr("<br>JACK audio output (v%1) © Paul Davis and Jack O'Quin").arg(JACK_VERSION); -#else - aboutText += tr("<br>JACK audio output © Paul Davis and Jack O'Quin"); -#endif -#endif -#ifdef HAVE_PORTAUDIO - aboutText += tr("<br>PortAudio audio output © Ross Bencina and Phil Burk"); -#endif -#ifdef HAVE_OGGZ -#ifdef OGGZ_VERSION - aboutText += tr("<br>Ogg file decoder (oggz v%1, fishsound v%2) © CSIRO Australia").arg(OGGZ_VERSION).arg(FISHSOUND_VERSION); -#else - aboutText += tr("<br>Ogg file decoder © CSIRO Australia"); -#endif -#endif -#ifdef HAVE_MAD -#ifdef MAD_VERSION - aboutText += tr("<br>MAD mp3 decoder (v%1) © Underbit Technologies Inc").arg(MAD_VERSION); -#else - aboutText += tr("<br>MAD mp3 decoder © Underbit Technologies Inc"); -#endif -#endif -#ifdef HAVE_SAMPLERATE -#ifdef SAMPLERATE_VERSION - aboutText += tr("<br>libsamplerate (v%1) © Erik de Castro Lopo").arg(SAMPLERATE_VERSION); -#else - aboutText += tr("<br>libsamplerate © Erik de Castro Lopo"); -#endif -#endif -#ifdef HAVE_SNDFILE -#ifdef SNDFILE_VERSION - aboutText += tr("<br>libsndfile (v%1) © Erik de Castro Lopo").arg(SNDFILE_VERSION); -#else - aboutText += tr("<br>libsndfile © Erik de Castro Lopo"); -#endif -#endif -#ifdef HAVE_FFTW3F -#ifdef FFTW3_VERSION - aboutText += tr("<br>FFTW3 (v%1) © Matteo Frigo and MIT").arg(FFTW3_VERSION); -#else - aboutText += tr("<br>FFTW3 © Matteo Frigo and MIT"); -#endif -#endif -#ifdef HAVE_VAMP - aboutText += tr("<br>Vamp plugin support (API v%1, host SDK v%2) © Chris Cannam").arg(VAMP_API_VERSION).arg(VAMP_SDK_VERSION); -#endif - aboutText += tr("<br>LADSPA plugin support (API v%1) © Richard Furse, Paul Davis, Stefan Westerfeld").arg(LADSPA_VERSION); - aboutText += tr("<br>DSSI plugin support (API v%1) © Chris Cannam, Steve Harris, Sean Bolton").arg(DSSI_VERSION); -#ifdef HAVE_LIBLO -#ifdef LIBLO_VERSION - aboutText += tr("<br>liblo Lite OSC library (v%1) © Steve Harris").arg(LIBLO_VERSION); -#else - aboutText += tr("<br>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 += tr("<p>Based on Sonic Visualizer Copyright © 2005 - 2007 Chris Cannam and Queen Mary, University of London (Version %1)</p>").arg(SV_VERSION); + +#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>Linked with"); +#ifndef QT_SHARED + aboutText += tr("<br>Qt (v%1) © Trolltech AS").arg(QT_VERSION_STR); +#endif +#ifdef HAVE_JACK +#ifdef JACK_VERSION + aboutText += tr("<br>JACK audio output (v%1) © Paul Davis and Jack O'Quin").arg(JACK_VERSION); +#else + aboutText += tr("<br>JACK audio output © Paul Davis and Jack O'Quin"); +#endif +#endif +#ifdef HAVE_PORTAUDIO + aboutText += tr("<br>PortAudio audio output © Ross Bencina and Phil Burk"); +#endif +#ifdef HAVE_OGGZ +#ifdef OGGZ_VERSION + aboutText += tr("<br>Ogg file decoder (oggz v%1, fishsound v%2) © CSIRO Australia").arg(OGGZ_VERSION).arg(FISHSOUND_VERSION); +#else + aboutText += tr("<br>Ogg file decoder © CSIRO Australia"); +#endif +#endif +#ifdef HAVE_MAD +#ifdef MAD_VERSION + aboutText += tr("<br>MAD mp3 decoder (v%1) © Underbit Technologies Inc").arg(MAD_VERSION); +#else + aboutText += tr("<br>MAD mp3 decoder © Underbit Technologies Inc"); +#endif +#endif +#ifdef HAVE_SAMPLERATE +#ifdef SAMPLERATE_VERSION + aboutText += tr("<br>libsamplerate (v%1) © Erik de Castro Lopo").arg(SAMPLERATE_VERSION); +#else + aboutText += tr("<br>libsamplerate © Erik de Castro Lopo"); +#endif +#endif +#ifdef HAVE_SNDFILE +#ifdef SNDFILE_VERSION + aboutText += tr("<br>libsndfile (v%1) © Erik de Castro Lopo").arg(SNDFILE_VERSION); +#else + aboutText += tr("<br>libsndfile © Erik de Castro Lopo"); +#endif +#endif +#ifdef HAVE_FFTW3F +#ifdef FFTW3_VERSION + aboutText += tr("<br>FFTW3 (v%1) © Matteo Frigo and MIT").arg(FFTW3_VERSION); +#else + aboutText += tr("<br>FFTW3 © Matteo Frigo and MIT"); +#endif +#endif +#ifdef HAVE_VAMP + aboutText += tr("<br>Vamp plugin support (API v%1, host SDK v%2) © Chris Cannam").arg(VAMP_API_VERSION).arg(VAMP_SDK_VERSION); +#endif + aboutText += tr("<br>LADSPA plugin support (API v%1) © Richard Furse, Paul Davis, Stefan Westerfeld").arg(LADSPA_VERSION); + aboutText += tr("<br>DSSI plugin support (API v%1) © Chris Cannam, Steve Harris, Sean Bolton").arg(DSSI_VERSION); +#ifdef HAVE_LIBLO +#ifdef LIBLO_VERSION + aboutText += tr("<br>liblo Lite OSC library (v%1) © Steve Harris").arg(LIBLO_VERSION); +#else + aboutText += tr("<br>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 += tr("<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>" @@ -5003,14 +5045,14 @@ "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 Sound Access"), aboutText); -} - + QMessageBox::about(this, tr("About Sound Access"), aboutText); +} + void MainWindow::connectionSettings() { ConnectionSettings* connection = new ConnectionSettings(m_httpClient); -} - +} + void MainWindow::styleSetting() { m_gallery.show(); @@ -5250,8 +5292,8 @@ QApplication::setOverrideCursor(QCursor(Qt::WaitCursor)); - QTextStream out(&file); - toEasaierXml(out); + QTextStream out(&file); + toEasaierXml(out); out.flush(); QApplication::restoreOverrideCursor(); @@ -5330,11 +5372,11 @@ } void MainWindow::createMultiPaneLayerContainer(){ - m_multiPaneLayerContainer = new MultiPaneLayerContainer; - connect(m_paneStack, SIGNAL(newPaneAdded(Pane*)),m_multiPaneLayerContainer,SLOT(paneAdded(Pane*))); - connect(m_paneStack, SIGNAL(paneDeleted(Pane*)),m_multiPaneLayerContainer,SLOT(paneRemoved(Pane*))); - connect(m_multiPaneLayerContainer, SIGNAL(removeSelectedItem()), this, SLOT(deleteCurrentLayer())); - connect(this, SIGNAL(newCurrentPane(Pane*)), m_multiPaneLayerContainer, SLOT(currentPaneChanged(Pane*))); + m_multiPaneLayerContainer = new MultiPaneLayerContainer; + connect(m_paneStack, SIGNAL(newPaneAdded(Pane*)),m_multiPaneLayerContainer,SLOT(paneAdded(Pane*))); + connect(m_paneStack, SIGNAL(paneDeleted(Pane*)),m_multiPaneLayerContainer,SLOT(paneRemoved(Pane*))); + connect(m_multiPaneLayerContainer, SIGNAL(removeSelectedItem()), this, SLOT(deleteCurrentLayer())); + connect(this, SIGNAL(newCurrentPane(Pane*)), m_multiPaneLayerContainer, SLOT(currentPaneChanged(Pane*))); connect(m_multiPaneLayerContainer, SIGNAL(propertyContainerSelected(View *, PropertyContainer *)), m_paneStack, SLOT(propertyContainerSelected(View *, PropertyContainer *))); m_toolBox->insertItem(0,"Layers", m_multiPaneLayerContainer); @@ -5342,26 +5384,26 @@ void MainWindow::setupFiltersMenu() { - if (m_mainMenusCreated) return; - - QAction *action = 0; - QMenu *menu = 0; - - menu = menuBar()->addMenu(tr("&Filter")); - menu->setTearOffEnabled(true); + if (m_mainMenusCreated) return; + + QAction *action = 0; + QMenu *menu = 0; + + menu = menuBar()->addMenu(tr("&Filter")); + menu->setTearOffEnabled(true); - RealTimeFilterFactory::FilterTypeSet filterTypes = RealTimeFilterFactory::getInstance()->getFilterTypes(); - RealTimeFilterFactory::FilterTypeSet::iterator iter; - - for (iter = filterTypes.begin(); iter != filterTypes.end(); iter++) { - action = new QAction(RealTimeFilterFactory::getInstance()->getFilterLabel(*iter), this); - action->setObjectName(RealTimeFilterFactory::getInstance()->getFilterLabel(*iter)); - connect(action, SIGNAL(triggered()), this, SLOT(addFilter())); - connect(this, SIGNAL(canAddFilter(bool)), action, SLOT(setEnabled(bool))); - menu->addAction(action); + RealTimeFilterFactory::FilterTypeSet filterTypes = RealTimeFilterFactory::getInstance()->getFilterTypes(); + RealTimeFilterFactory::FilterTypeSet::iterator iter; + + for (iter = filterTypes.begin(); iter != filterTypes.end(); iter++) { + action = new QAction(RealTimeFilterFactory::getInstance()->getFilterLabel(*iter), this); + action->setObjectName(RealTimeFilterFactory::getInstance()->getFilterLabel(*iter)); + connect(action, SIGNAL(triggered()), this, SLOT(addFilter())); + connect(this, SIGNAL(canAddFilter(bool)), action, SLOT(setEnabled(bool))); + menu->addAction(action); } - connect(m_filterPropertyStack, SIGNAL(removeFilter(QString)), this, SLOT(removeFilter(QString))); + connect(m_filterPropertyStack, SIGNAL(removeFilter(QString)), this, SLOT(removeFilter(QString))); }