# HG changeset patch # User Chris Cannam # Date 1309114561 -3600 # Node ID 4bccf2014f80b102f6b79df4dda21a5e21478897 # Parent fa0c3e79ea8e9cd1effff8fd7afa06950c9e0fa9# Parent e3644cd9cf992cce2a48c2fac0598032de59123f Merge from branch "templating" diff -r fa0c3e79ea8e -r 4bccf2014f80 main/MainWindow.cpp --- a/main/MainWindow.cpp Fri Jun 24 12:17:44 2011 +0100 +++ b/main/MainWindow.cpp Sun Jun 26 19:56:01 2011 +0100 @@ -30,6 +30,7 @@ #include "framework/TransformUserConfigurator.h" #include "view/ViewManager.h" #include "base/Preferences.h" +#include "base/ResourceFinder.h" #include "layer/WaveformLayer.h" #include "layer/TimeRulerLayer.h" #include "layer/TimeInstantLayer.h" @@ -112,6 +113,8 @@ #include #include #include +#include +#include #include #include @@ -137,6 +140,7 @@ m_sliceMenu(0), m_recentFilesMenu(0), m_recentTransformsMenu(0), + m_templatesMenu(0), m_rightButtonMenu(0), m_rightButtonLayerMenu(0), m_rightButtonTransformsMenu(0), @@ -158,7 +162,8 @@ m_preferencesDialog(0), m_layerTreeDialog(0), m_activityLog(new ActivityLog()), - m_keyReference(new KeyReference()) + m_keyReference(new KeyReference()), + m_templateWatcher(0) { Profiler profiler("MainWindow::MainWindow"); @@ -388,7 +393,7 @@ m_keyReference->registerShortcut(action); menu->addAction(action); toolbar->addAction(action); - +/* icon = il.load("fileopensession"); action = new QAction(icon, tr("&Open Session..."), this); action->setShortcut(tr("Ctrl+O")); @@ -396,14 +401,39 @@ connect(action, SIGNAL(triggered()), this, SLOT(openSession())); m_keyReference->registerShortcut(action); menu->addAction(action); - +*/ icon = il.load("fileopen"); icon.addPixmap(il.loadPixmap("fileopen-22")); - action = new QAction(icon, tr("&Open..."), this); + action->setShortcut(tr("Ctrl+O")); action->setStatusTip(tr("Open a session file, audio file, or layer")); connect(action, SIGNAL(triggered()), this, SLOT(openSomething())); toolbar->addAction(action); + menu->addAction(action); + + // We want this one to go on the toolbar now, if we add it at all, + // but on the menu later + QAction *iaction = new QAction(tr("&Import More Audio..."), this); + iaction->setShortcut(tr("Ctrl+I")); + iaction->setStatusTip(tr("Import an extra audio file into a new pane")); + connect(iaction, SIGNAL(triggered()), this, SLOT(importMoreAudio())); + connect(this, SIGNAL(canImportMoreAudio(bool)), iaction, SLOT(setEnabled(bool))); + m_keyReference->registerShortcut(iaction); + + 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())); + m_keyReference->registerShortcut(action); + menu->addAction(action); + + m_recentFilesMenu = menu->addMenu(tr("Open &Recent")); + m_recentFilesMenu->setTearOffEnabled(true); + setupRecentFilesMenu(); + connect(&m_recentFiles, SIGNAL(recentChanged()), + this, SLOT(setupRecentFilesMenu())); + + menu->addSeparator(); icon = il.load("filesave"); icon.addPixmap(il.loadPixmap("filesave-22")); @@ -427,6 +457,7 @@ menu->addSeparator(); +/* icon = il.load("fileopenaudio"); action = new QAction(icon, tr("&Import Audio File..."), this); action->setShortcut(tr("Ctrl+I")); @@ -434,14 +465,10 @@ connect(action, SIGNAL(triggered()), this, SLOT(importAudio())); m_keyReference->registerShortcut(action); 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))); - m_keyReference->registerShortcut(action); - menu->addAction(action); +*/ + + // the Import action we made earlier + menu->addAction(iaction); action = new QAction(tr("&Export Audio File..."), this); action->setStatusTip(tr("Export selection as an audio file")); @@ -475,22 +502,28 @@ 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())); - m_keyReference->registerShortcut(action); + QString templatesMenuLabel = tr("Apply Session Template"); + m_templatesMenu = menu->addMenu(templatesMenuLabel); + m_templatesMenu->setTearOffEnabled(true); + // We need to have a main model for this option to be useful: + // canExportAudio captures that + connect(this, SIGNAL(canExportAudio(bool)), m_templatesMenu, SLOT(setEnabled(bool))); + + // Set up the menu in a moment, after m_manageTemplatesAction constructed + + action = new QAction(tr("Export Session as Template..."), this); + connect(action, SIGNAL(triggered()), this, SLOT(saveSessionAsTemplate())); + // We need to have something in the session for this to be useful: + // canDeleteCurrentLayer captures that + connect(this, SIGNAL(canExportAudio(bool)), action, SLOT(setEnabled(bool))); 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(); + m_manageTemplatesAction = new QAction(tr("Manage Exported Templates"), this); + connect(m_manageTemplatesAction, SIGNAL(triggered()), this, SLOT(manageSavedTemplates())); + menu->addAction(m_manageTemplatesAction); + + setupTemplatesMenu(); + action = new QAction(tr("&Preferences..."), this); action->setStatusTip(tr("Adjust the application preferences")); connect(action, SIGNAL(triggered()), this, SLOT(preferences())); @@ -1617,6 +1650,57 @@ } void +MainWindow::setupTemplatesMenu() +{ + m_templatesMenu->clear(); + + QAction *defaultAction = new QAction(tr("Standard Waveform"), this); + defaultAction->setObjectName("default"); + connect(defaultAction, SIGNAL(triggered()), this, SLOT(applyTemplate())); + m_templatesMenu->addAction(defaultAction); + + m_templatesMenu->addSeparator(); + + QAction *action = 0; + + QStringList templates = ResourceFinder().getResourceFiles("templates", "svt"); + + bool havePersonal = false; + + // (ordered by name) + std::set byName; + foreach (QString t, templates) { + if (!t.startsWith(":")) havePersonal = true; + byName.insert(QFileInfo(t).baseName()); + } + + foreach (QString t, byName) { + if (t.toLower() == "default") continue; + action = new QAction(t, this); + connect(action, SIGNAL(triggered()), this, SLOT(applyTemplate())); + m_templatesMenu->addAction(action); + } + + if (!templates.empty()) m_templatesMenu->addSeparator(); + + if (!m_templateWatcher) { + m_templateWatcher = new QFileSystemWatcher(this); + m_templateWatcher->addPath(ResourceFinder().getResourceSaveDir("templates")); + connect(m_templateWatcher, SIGNAL(directoryChanged(const QString &)), + this, SLOT(setupTemplatesMenu())); + } + + QAction *setDefaultAction = new QAction(tr("Choose Default Template..."), this); + setDefaultAction->setObjectName("set_default_template"); + connect(setDefaultAction, SIGNAL(triggered()), this, SLOT(preferences())); + m_templatesMenu->addSeparator(); + m_templatesMenu->addAction(setDefaultAction); + + m_manageTemplatesAction->setEnabled(havePersonal); +} + + +void MainWindow::setupRecentTransformsMenu() { m_recentTransformsMenu->clear(); @@ -2149,7 +2233,7 @@ QString path = getOpenFileName(FileFinder::AudioFile); if (path != "") { - if (openAudio(path, ReplaceMainModel) == FileOpenFailed) { + if (openAudio(path, ReplaceSession) == FileOpenFailed) { emit hideSplash(); QMessageBox::critical(this, tr("Failed to open file"), tr("File open failed

Audio file \"%1\" could not be opened").arg(path)); @@ -2673,7 +2757,7 @@ if (path.isEmpty()) return; - FileOpenStatus status = open(path, AskUser); + FileOpenStatus status = open(path, ReplaceSession); if (status == FileOpenFailed) { emit hideSplash(); @@ -2705,7 +2789,7 @@ if (text.isEmpty()) return; - FileOpenStatus status = open(text); + FileOpenStatus status = open(text, AskUser); if (status == FileOpenFailed) { emit hideSplash(); @@ -2733,7 +2817,7 @@ QString path = action->text(); if (path == "") return; - FileOpenStatus status = open(path); + FileOpenStatus status = open(path, ReplaceSession); if (status == FileOpenFailed) { emit hideSplash(); @@ -2747,6 +2831,70 @@ } void +MainWindow::applyTemplate() +{ + QObject *s = sender(); + QAction *action = qobject_cast(s); + + if (!action) { + std::cerr << "WARNING: MainWindow::applyTemplate: sender is not an action" + << std::endl; + return; + } + + QString n = action->objectName(); + if (n == "") n = action->text(); + + if (n == "") { + std::cerr << "WARNING: MainWindow::applyTemplate: sender has no name" + << std::endl; + return; + } + + QString mainModelLocation; + WaveFileModel *mm = getMainModel(); + if (mm) mainModelLocation = mm->getLocation(); + if (mainModelLocation != "") { + openAudio(mainModelLocation, ReplaceSession, n); + } else { + openSessionTemplate(n); + } +} + +void +MainWindow::saveSessionAsTemplate() +{ + QString name = QInputDialog::getText + (this, tr("Enter template name"), + tr("Please enter a name for the saved template:")); + if (name == "") return; + + name.replace(QRegExp("[^\\w\\s\\.\"'-]"), "_"); + + ResourceFinder rf; + QString dir = rf.getResourceSaveDir("templates"); + QString filename = QString("%1/%2.svt").arg(dir).arg(name); + if (QFile(filename).exists()) { + if (QMessageBox::warning(this, + tr("Template file exists"), + tr("Template file exists

The template \"%1\" already exists.
Overwrite it?").arg(name), + QMessageBox::Ok | QMessageBox::Cancel, + QMessageBox::Cancel) != QMessageBox::Ok) { + return; + } + } + + saveSessionTemplate(filename); +} + +void +MainWindow::manageSavedTemplates() +{ + ResourceFinder rf; + QDesktopServices::openUrl("file:" + rf.getResourceSaveDir("templates")); +} + +void MainWindow::paneAdded(Pane *pane) { if (m_overview) m_overview->registerView(pane); @@ -3914,9 +4062,15 @@ void MainWindow::preferences() { + bool goToTemplateTab = + (sender() && sender()->objectName() == "set_default_template"); + if (!m_preferencesDialog.isNull()) { m_preferencesDialog->show(); m_preferencesDialog->raise(); + if (goToTemplateTab) { + m_preferencesDialog->switchToTab(PreferencesDialog::TemplateTab); + } return; } @@ -3931,6 +4085,9 @@ m_preferencesDialog->setAttribute(Qt::WA_DeleteOnClose); m_preferencesDialog->show(); + if (goToTemplateTab) { + m_preferencesDialog->switchToTab(PreferencesDialog::TemplateTab); + } } void diff -r fa0c3e79ea8e -r 4bccf2014f80 main/MainWindow.h --- a/main/MainWindow.h Fri Jun 24 12:17:44 2011 +0100 +++ b/main/MainWindow.h Sun Jun 26 19:56:01 2011 +0100 @@ -58,6 +58,7 @@ class KeyReference; class Labeller; class ActivityLog; +class QFileSystemWatcher; class MainWindow : public MainWindowBase { @@ -83,6 +84,7 @@ virtual void openSomething(); virtual void openLocation(); virtual void openRecentFile(); + virtual void applyTemplate(); virtual void exportAudio(); virtual void importLayer(); virtual void exportLayer(); @@ -140,6 +142,7 @@ virtual void setupRecentFilesMenu(); virtual void setupRecentTransformsMenu(); + virtual void setupTemplatesMenu(); virtual void playSpeedChanged(int); virtual void playSoloToggled(); @@ -169,6 +172,9 @@ virtual void midiEventsAvailable(); virtual void playStatusChanged(bool); + virtual void saveSessionAsTemplate(); + virtual void manageSavedTemplates(); + virtual void website(); virtual void help(); virtual void about(); @@ -190,6 +196,7 @@ QMenu *m_sliceMenu; QMenu *m_recentFilesMenu; QMenu *m_recentTransformsMenu; + QMenu *m_templatesMenu; QMenu *m_rightButtonMenu; QMenu *m_rightButtonLayerMenu; QMenu *m_rightButtonTransformsMenu; @@ -206,6 +213,7 @@ QAction *m_playAction; QAction *m_playSelectionAction; QAction *m_playLoopAction; + QAction *m_manageTemplatesAction; bool m_soloModified; bool m_prevSolo; @@ -221,6 +229,8 @@ ActivityLog *m_activityLog; KeyReference *m_keyReference; + QFileSystemWatcher *m_templateWatcher; + struct LayerConfiguration { LayerConfiguration(LayerFactory::LayerType _layer = LayerFactory::TimeRuler, diff -r fa0c3e79ea8e -r 4bccf2014f80 main/PreferencesDialog.cpp --- a/main/PreferencesDialog.cpp Fri Jun 24 12:17:44 2011 +0100 +++ b/main/PreferencesDialog.cpp Sun Jun 26 19:56:01 2011 +0100 @@ -31,12 +31,16 @@ #include #include #include +#include #include +#include + #include "widgets/WindowTypeSelector.h" #include "widgets/IconLoader.h" #include "base/Preferences.h" #include "audioio/AudioTargetFactory.h" +#include "base/ResourceFinder.h" PreferencesDialog::PreferencesDialog(QWidget *parent, Qt::WFlags flags) : QDialog(parent, flags), @@ -50,10 +54,10 @@ QGridLayout *grid = new QGridLayout; setLayout(grid); - QTabWidget *tab = new QTabWidget; - grid->addWidget(tab, 0, 0); + m_tabs = new QTabWidget; + grid->addWidget(m_tabs, 0, 0); - tab->setTabPosition(QTabWidget::North); + m_tabs->setTabPosition(QTabWidget::North); // Create this first, as slots that get called from the ctor will // refer to it @@ -257,7 +261,8 @@ subgrid->setRowStretch(row, 10); - tab->addTab(frame, tr("&General")); + m_tabOrdering[GeneralTab] = m_tabs->count(); + m_tabs->addTab(frame, tr("&General")); // Appearance tab @@ -295,7 +300,8 @@ subgrid->setRowStretch(row, 10); - tab->addTab(frame, tr("&Appearance")); + m_tabOrdering[AppearanceTab] = m_tabs->count(); + m_tabs->addTab(frame, tr("&Appearance")); // Analysis tab @@ -328,7 +334,53 @@ subgrid->setRowStretch(row, 10); - tab->addTab(frame, tr("Anal&ysis")); + m_tabOrdering[AnalysisTab] = m_tabs->count(); + m_tabs->addTab(frame, tr("Anal&ysis")); + + // Template tab + + frame = new QFrame; + subgrid = new QGridLayout; + frame->setLayout(subgrid); + row = 0; + + subgrid->addWidget(new QLabel(tr("Default session template for audio files:")), row++, 0); + + QListWidget *lw = new QListWidget(); + subgrid->addWidget(lw, row, 0); + subgrid->setRowStretch(row, 10); + row++; + + settings.beginGroup("MainWindow"); + m_currentTemplate = settings.value("sessiontemplate", "").toString(); + settings.endGroup(); + + lw->addItem(tr("Standard Waveform")); + if (m_currentTemplate == "" || m_currentTemplate == "default") { + lw->setCurrentRow(lw->count()-1); + } + m_templates.push_back(""); + + QStringList templates = ResourceFinder().getResourceFiles("templates", "svt"); + + std::set byName; + foreach (QString t, templates) { + byName.insert(QFileInfo(t).baseName()); + } + + foreach (QString t, byName) { + if (t.toLower() == "default") continue; + m_templates.push_back(t); + lw->addItem(t); + if (m_currentTemplate == t) { + lw->setCurrentRow(lw->count()-1); + } + } + + connect(lw, SIGNAL(currentRowChanged(int)), this, SLOT(defaultTemplateChanged(int))); + + m_tabOrdering[TemplateTab] = m_tabs->count(); + m_tabs->addTab(frame, tr("Session &Template")); QDialogButtonBox *bb = new QDialogButtonBox(Qt::Horizontal); grid->addWidget(bb, 1, 0); @@ -351,6 +403,14 @@ } void +PreferencesDialog::switchToTab(Tab t) +{ + if (m_tabOrdering.contains(t)) { + m_tabs->setCurrentIndex(m_tabOrdering[t]); + } +} + +void PreferencesDialog::windowTypeChanged(WindowType type) { m_windowType = type; @@ -417,6 +477,13 @@ } void +PreferencesDialog::defaultTemplateChanged(int i) +{ + m_currentTemplate = m_templates[i]; + m_applyButton->setEnabled(true); +} + +void PreferencesDialog::tempDirRootChanged(QString r) { m_tempDirRoot = r; @@ -488,10 +555,15 @@ AudioTargetFactory::getInstance()->getCallbackTargetNames(); QSettings settings; + settings.beginGroup("Preferences"); settings.setValue("audio-target", devices[m_audioDevice]); settings.endGroup(); + settings.beginGroup("MainWindow"); + settings.setValue("sessiontemplate", m_currentTemplate); + settings.endGroup(); + m_applyButton->setEnabled(false); if (m_changesOnRestart) { diff -r fa0c3e79ea8e -r 4bccf2014f80 main/PreferencesDialog.h --- a/main/PreferencesDialog.h Fri Jun 24 12:17:44 2011 +0100 +++ b/main/PreferencesDialog.h Sun Jun 26 19:56:01 2011 +0100 @@ -17,12 +17,14 @@ #define _PREFERENCES_DIALOG_H_ #include +#include #include "base/Window.h" class WindowTypeSelector; class QPushButton; class QLineEdit; +class QTabWidget; class PreferencesDialog : public QDialog { @@ -32,6 +34,14 @@ PreferencesDialog(QWidget *parent = 0, Qt::WFlags flags = 0); virtual ~PreferencesDialog(); + enum Tab { + GeneralTab, + AppearanceTab, + AnalysisTab, + TemplateTab + }; + void switchToTab(Tab tab); + public slots: void applicationClosing(bool quickly); @@ -49,6 +59,7 @@ void timeToTextModeChanged(int mode); void viewFontSizeChanged(int sz); void showSplashChanged(int state); + void defaultTemplateChanged(int); void tempDirButtonClicked(); @@ -60,7 +71,13 @@ WindowTypeSelector *m_windowTypeSelector; QPushButton *m_applyButton; + QTabWidget *m_tabs; + QMap m_tabOrdering; + QLineEdit *m_tempDirRootEdit; + + QString m_currentTemplate; + QStringList m_templates; WindowType m_windowType; int m_spectrogramSmoothing; diff -r fa0c3e79ea8e -r 4bccf2014f80 main/main.cpp --- a/main/main.cpp Fri Jun 24 12:17:44 2011 +0100 +++ b/main/main.cpp Sun Jun 26 19:56:01 2011 +0100 @@ -468,7 +468,7 @@ } if (status != MainWindow::FileOpenSucceeded) { if (!haveMainModel) { - status = m_mainWindow->open(path, MainWindow::ReplaceMainModel); + status = m_mainWindow->open(path, MainWindow::ReplaceSession); if (status == MainWindow::FileOpenSucceeded) { haveMainModel = true; } diff -r fa0c3e79ea8e -r 4bccf2014f80 samples/silent.wav Binary file samples/silent.wav has changed diff -r fa0c3e79ea8e -r 4bccf2014f80 sonic-visualiser.qrc --- a/sonic-visualiser.qrc Fri Jun 24 12:17:44 2011 +0100 +++ b/sonic-visualiser.qrc Sun Jun 26 19:56:01 2011 +0100 @@ -100,10 +100,15 @@ samples/kick.wav samples/organ.wav samples/piano.wav + samples/silent.wav samples/snare.wav samples/stick.wav samples/strike.wav samples/tap.wav + templates/default.svt + templates/Scrolling Waveforms.svt + templates/Spectrograms.svt + templates/Waveform and Melodic Range Spectrogram.svt i18n/sonic-visualiser_ru.qm i18n/sonic-visualiser_en_GB.qm i18n/sonic-visualiser_en_US.qm diff -r fa0c3e79ea8e -r 4bccf2014f80 templates/Scrolling Waveforms.svt --- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/templates/Scrolling Waveforms.svt Sun Jun 26 19:56:01 2011 +0100 @@ -0,0 +1,31 @@ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + diff -r fa0c3e79ea8e -r 4bccf2014f80 templates/Spectrograms.svt --- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/templates/Spectrograms.svt Sun Jun 26 19:56:01 2011 +0100 @@ -0,0 +1,32 @@ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + diff -r fa0c3e79ea8e -r 4bccf2014f80 templates/Waveform and Melodic Range Spectrogram.svt --- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/templates/Waveform and Melodic Range Spectrogram.svt Sun Jun 26 19:56:01 2011 +0100 @@ -0,0 +1,25 @@ + + + + + + + + + + + + + + + + + + + + + + + + + diff -r fa0c3e79ea8e -r 4bccf2014f80 templates/default.svt --- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/templates/default.svt Sun Jun 26 19:56:01 2011 +0100 @@ -0,0 +1,14 @@ + + + + + + + + + + + + + +