# HG changeset patch # User Chris Cannam # Date 1138643516 0 # Node ID cc98d496d52b1bf4e08352ce1fb021b22b7188de # Parent 47500c27ac264ce929492584da71aa085f098829 * Add command history class, and basic undo/redo menus. No actual commands to undo/redo yet. Selecting the placeholders sometimes seems to cause a crash, so this looks a little uncertain so far. * Add Rename Layer * Remove models from playback when their layers are removed (and ref counts hit zero) * Don't hang around waiting so much when there's work to be done in the audio buffer fill thread * Put more sensible names on layers generated from transforms * Add basic editing to time-value layer like existing editing in time-instants layer, and make both of them snap to the appropriate resolution during drag diff -r 47500c27ac26 -r cc98d496d52b base/Command.h --- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/base/Command.h Mon Jan 30 17:51:56 2006 +0000 @@ -0,0 +1,22 @@ +/* -*- c-basic-offset: 4 -*- vi:set ts=8 sts=4 sw=4: */ + +/* + A waveform viewer and audio annotation editor. + Chris Cannam, Queen Mary University of London, 2005-2006 + + This is experimental software. Not for distribution. +*/ + +#ifndef _COMMAND_H_ +#define _COMMAND_H_ + +class Command +{ +public: + virtual void execute() = 0; + virtual void unexecute() = 0; + virtual QString name() const = 0; +}; + +#endif + diff -r 47500c27ac26 -r cc98d496d52b base/MultiViewCommandHistory.cpp --- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/base/MultiViewCommandHistory.cpp Mon Jan 30 17:51:56 2006 +0000 @@ -0,0 +1,299 @@ +/* -*- c-basic-offset: 4 -*- vi:set ts=8 sts=4 sw=4: */ + +/* + A waveform viewer and audio annotation editor. + Chris Cannam, Queen Mary University of London, 2005-2006 + + This is experimental software. Not for distribution. +*/ + +/* + This is a modified version of a source file from the Rosegarden + MIDI and audio sequencer and notation editor, copyright 2000-2006 + Chris Cannam, distributed under the GNU General Public License. + + This file contains traces of the KCommandHistory class from the KDE + project, copyright 2000 Werner Trobin and David Faure and + distributed under the GNU Lesser General Public License. +*/ + +#include "MultiViewCommandHistory.h" + +#include "Command.h" + +#include +#include +#include +#include + +#include + +MultiViewCommandHistory::MultiViewCommandHistory() : + m_undoLimit(50), + m_redoLimit(50), + m_savedAt(0) +{ + m_undoAction = new QAction(QIcon(":/icons/undo.png"), tr("&Undo"), this); + m_undoAction->setShortcut(tr("Ctrl+Z")); + connect(m_undoAction, SIGNAL(triggered()), this, SLOT(undo())); + + m_undoMenu = new QMenu(tr("&Undo")); + m_undoAction->setMenu(m_undoMenu); + connect(m_undoMenu, SIGNAL(triggered(QAction *)), + this, SLOT(undoActivated(QAction*))); + + m_redoAction = new QAction(QIcon(":/icons/redo.png"), tr("&Redo"), this); + m_redoAction->setShortcut(tr("Ctrl+Shift+Z")); + connect(m_redoAction, SIGNAL(triggered()), this, SLOT(redo())); + + m_redoMenu = new QMenu(tr("Re&do")); + m_redoAction->setMenu(m_redoMenu); + connect(m_redoMenu, SIGNAL(triggered(QAction *)), + this, SLOT(redoActivated(QAction*))); +} + +MultiViewCommandHistory::~MultiViewCommandHistory() +{ + m_savedAt = -1; + clearStack(m_undoStack); + clearStack(m_redoStack); + + delete m_undoMenu; + delete m_redoMenu; +} + +void +MultiViewCommandHistory::clear() +{ + m_savedAt = -1; + clearStack(m_undoStack); + clearStack(m_redoStack); + updateActions(); +} + +void +MultiViewCommandHistory::registerMenu(QMenu *menu) +{ + menu->addAction(m_undoAction); + menu->addAction(m_redoAction); +} + +void +MultiViewCommandHistory::registerToolbar(QToolBar *toolbar) +{ + toolbar->addAction(m_undoAction); + toolbar->addAction(m_redoAction); +} + +void +MultiViewCommandHistory::addCommand(Command *command, bool execute) +{ + if (!command) return; + + std::cerr << "MVCH::addCommand: " << command->name().toLocal8Bit().data() << std::endl; + + // We can't redo after adding a command + clearStack(m_redoStack); + + // can we reach savedAt? + if ((int)m_undoStack.size() < m_savedAt) m_savedAt = -1; // nope + + m_undoStack.push(command); + clipCommands(); + + if (execute) { + command->execute(); + emit commandExecuted(); + emit commandExecuted(command); + } + +// updateButtons(); + updateActions(); +} + +void +MultiViewCommandHistory::undo() +{ + if (m_undoStack.empty()) return; + + Command *command = m_undoStack.top(); + command->unexecute(); + emit commandExecuted(); + emit commandExecuted(command); + + m_redoStack.push(command); + m_undoStack.pop(); + + clipCommands(); + updateActions(); + + if ((int)m_undoStack.size() == m_savedAt) emit documentRestored(); +} + +void +MultiViewCommandHistory::redo() +{ + if (m_redoStack.empty()) return; + + Command *command = m_redoStack.top(); + command->execute(); + emit commandExecuted(); + emit commandExecuted(command); + + m_undoStack.push(command); + m_redoStack.pop(); + // no need to clip + + updateActions(); +} + +void +MultiViewCommandHistory::setUndoLimit(int limit) +{ + if (limit > 0 && limit != m_undoLimit) { + m_undoLimit = limit; + clipCommands(); + } +} + +void +MultiViewCommandHistory::setRedoLimit(int limit) +{ + if (limit > 0 && limit != m_redoLimit) { + m_redoLimit = limit; + clipCommands(); + } +} + +void +MultiViewCommandHistory::documentSaved() +{ + m_savedAt = m_undoStack.size(); +} + +void +MultiViewCommandHistory::clipCommands() +{ + if ((int)m_undoStack.size() > m_undoLimit) { + m_savedAt -= (m_undoStack.size() - m_undoLimit); + } + + clipStack(m_undoStack, m_undoLimit); + clipStack(m_redoStack, m_redoLimit); +} + +void +MultiViewCommandHistory::clipStack(CommandStack &stack, int limit) +{ + int i; + + if ((int)stack.size() > limit) { + + CommandStack tempStack; + + for (i = 0; i < limit; ++i) { + Command *command = stack.top(); + std::cerr << "MVCH::clipStack: Saving recent command: " << command->name().toLocal8Bit().data() << " at " << command << std::endl; + tempStack.push(stack.top()); + stack.pop(); + } + + clearStack(stack); + + for (i = 0; i < m_undoLimit; ++i) { + stack.push(tempStack.top()); + tempStack.pop(); + } + } +} + +void +MultiViewCommandHistory::clearStack(CommandStack &stack) +{ + while (!stack.empty()) { + Command *command = stack.top(); + std::cerr << "MVCH::clearStack: About to delete command: " << command->name().toLocal8Bit().data() << " at " << command << std::endl; + delete command; + stack.pop(); + } +} + +void +MultiViewCommandHistory::undoActivated(QAction *action) +{ + int pos = m_actionCounts[action]; + for (int i = 0; i <= pos; ++i) { + undo(); + } +} + +void +MultiViewCommandHistory::redoActivated(QAction *action) +{ + int pos = m_actionCounts[action]; + for (int i = 0; i <= pos; ++i) { + redo(); + } +} + +void +MultiViewCommandHistory::updateActions() +{ + if (m_undoStack.empty()) { + m_undoAction->setEnabled(false); + m_undoAction->setText(tr("Nothing to undo")); + } else { + m_undoAction->setEnabled(true); + QString commandName = m_undoStack.top()->name(); + commandName.replace(QRegExp("&"), ""); + QString text = tr("&Undo %1").arg(commandName); + m_undoAction->setText(text); + } + + if (m_redoStack.empty()) { + m_redoAction->setEnabled(false); + m_redoAction->setText(tr("Nothing to redo")); + } else { + m_redoAction->setEnabled(true); + QString commandName = m_redoStack.top()->name(); + commandName.replace(QRegExp("&"), ""); + QString text = tr("Re&do %1").arg(commandName); + m_redoAction->setText(text); + } + + m_actionCounts.clear(); + + for (int undo = 0; undo <= 1; ++undo) { + + QMenu *menu(undo ? m_undoMenu : m_redoMenu); + CommandStack &stack(undo ? m_undoStack : m_redoStack); + + menu->clear(); + + CommandStack tempStack; + int j = 0; + + while (j < 10 && !stack.empty()) { + + Command *command = stack.top(); + tempStack.push(command); + stack.pop(); + + QString commandName = command->name(); + commandName.replace(QRegExp("&"), ""); + + QString text; + if (undo) text = tr("&Undo %1").arg(commandName); + else text = tr("Re&do %1").arg(commandName); + + QAction *action = menu->addAction(text); + m_actionCounts[action] = j++; + } + + while (!tempStack.empty()) { + stack.push(tempStack.top()); + tempStack.pop(); + } + } +} + diff -r 47500c27ac26 -r cc98d496d52b base/MultiViewCommandHistory.h --- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/base/MultiViewCommandHistory.h Mon Jan 30 17:51:56 2006 +0000 @@ -0,0 +1,136 @@ +/* -*- c-basic-offset: 4 -*- vi:set ts=8 sts=4 sw=4: */ + +/* + A waveform viewer and audio annotation editor. + Chris Cannam, Queen Mary University of London, 2005-2006 + + This is experimental software. Not for distribution. +*/ + +/* + This is a modified version of a source file from the Rosegarden + MIDI and audio sequencer and notation editor, copyright 2000-2006 + Chris Cannam, distributed under the GNU General Public License. + + This file contains traces of the KCommandHistory class from the KDE + project, copyright 2000 Werner Trobin and David Faure and + distributed under the GNU Lesser General Public License. +*/ + +#ifndef _MULTI_VIEW_COMMAND_HISTORY_H_ +#define _MULTI_VIEW_COMMAND_HISTORY_H_ + +#include +#include + +#include +#include +#include + +class Command; +class QAction; +class QMenu; +class QToolBar; + +/** + * The MultiViewCommandHistory class stores a list of executed + * commands and maintains Undo and Redo actions synchronised with + * those commands. + * + * MultiViewCommandHistory also allows you to associate more than one + * Undo and Redo action with the same command history, and it keeps + * them all up-to-date at once. This makes it effective in systems + * where multiple views may be editing the same data at once. + */ + +class MultiViewCommandHistory : public QObject +{ + Q_OBJECT + +public: + MultiViewCommandHistory(); + virtual ~MultiViewCommandHistory(); + + void clear(); + + void registerMenu(QMenu *menu); + void registerToolbar(QToolBar *toolbar); + + void addCommand(Command *command, bool execute = true); + + /// Return the maximum number of items in the undo history. + int undoLimit() { return m_undoLimit; } + + /// Set the maximum number of items in the undo history. + void setUndoLimit(int limit); + + /// Return the maximum number of items in the redo history. + int redoLimit() { return m_redoLimit; } + + /// Set the maximum number of items in the redo history. + void setRedoLimit(int limit); + +public slots: + /** + * Checkpoint function that should be called when the document is + * saved. If the undo/redo stack later returns to the point at + * which the document was saved, the documentRestored signal will + * be emitted. + */ + virtual void documentSaved(); + +protected slots: + void undo(); + void redo(); + void undoActivated(QAction *); + void redoActivated(QAction *); + +signals: + /** + * Emitted whenever a command has just been executed, whether by + * addCommand, undo, or redo. + */ + void commandExecuted(Command *); + + /** + * Emitted whenever a command has just been executed, whether by + * addCommand, undo, or redo. + */ + void commandExecuted(); + + /** + * Emitted when the undo/redo stack has reached the same state at + * which the documentSaved slot was last called. + */ + void documentRestored(); + +private: + QAction *m_undoAction; + QAction *m_redoAction; + QMenu *m_undoMenu; + QMenu *m_redoMenu; + + std::map m_actionCounts; + + typedef std::stack CommandStack; + CommandStack m_undoStack; + CommandStack m_redoStack; + + int m_undoLimit; + int m_redoLimit; + int m_savedAt; + + void updateActions(); +// void updateMenus(); + +// void updateButtons(); +// void updateButton(bool undo, const QString &name, CommandStack &stack); +// void updateMenu(bool undo, const QString &name, CommandStack &stack); + void clipCommands(); + + void clipStack(CommandStack &stack, int limit); + void clearStack(CommandStack &stack); +}; + + +#endif diff -r 47500c27ac26 -r cc98d496d52b base/View.cpp --- a/base/View.cpp Mon Jan 30 13:19:42 2006 +0000 +++ b/base/View.cpp Mon Jan 30 17:51:56 2006 +0000 @@ -21,8 +21,9 @@ #include #include +#include -//#define DEBUG_VIEW_WIDGET_PAINT 1 +#define DEBUG_VIEW_WIDGET_PAINT 1 using std::cerr; using std::endl; @@ -195,7 +196,7 @@ size_t View::getEndFrame() const { - return getStartFrame() + (width() * m_zoomLevel) - 1; + return getFrameForX(width()) - 1; } void @@ -255,6 +256,14 @@ } } +View::LayerProgressBar::LayerProgressBar(QWidget *parent) : + QProgressBar(parent) +{ + QFont f(font()); + f.setPointSize(f.pointSize() * 8 / 10); + setFont(f); +} + void View::addLayer(Layer *layer) { @@ -268,7 +277,7 @@ m_progressBars[layer]->setMaximum(100); m_progressBars[layer]->setMinimumWidth(80); m_progressBars[layer]->hide(); - + connect(layer, SIGNAL(layerParametersChanged()), this, SLOT(layerParametersChanged())); connect(layer, SIGNAL(layerNameChanged()), @@ -503,6 +512,9 @@ if (m_playPointerFrame == f) return; bool visible = (getXForFrame(m_playPointerFrame) != getXForFrame(f)); + std::cerr << "old x = " << getXForFrame(m_playPointerFrame) + << ", new x = " << getXForFrame(f) + << ", visible = " << visible << std::endl; size_t oldPlayPointerFrame = m_playPointerFrame; m_playPointerFrame = f; if (!visible) return; diff -r 47500c27ac26 -r cc98d496d52b base/View.h --- a/base/View.h Mon Jan 30 13:19:42 2006 +0000 +++ b/base/View.h Mon Jan 30 17:51:56 2006 +0000 @@ -252,7 +252,7 @@ class LayerProgressBar : public QProgressBar { public: - LayerProgressBar(QWidget *parent) : QProgressBar(parent) { } + LayerProgressBar(QWidget *parent); virtual QString text() const { return m_text; } virtual void setText(QString text) { m_text = text; } protected: diff -r 47500c27ac26 -r cc98d496d52b base/ViewManager.cpp --- a/base/ViewManager.cpp Mon Jan 30 13:19:42 2006 +0000 +++ b/base/ViewManager.cpp Mon Jan 30 17:51:56 2006 +0000 @@ -14,7 +14,7 @@ #include -//#define DEBUG_VIEW_MANAGER 1 +#define DEBUG_VIEW_MANAGER 1 ViewManager::ViewManager() : m_playSource(0), diff -r 47500c27ac26 -r cc98d496d52b transform/TransformFactory.cpp --- a/transform/TransformFactory.cpp Mon Jan 30 13:19:42 2006 +0000 +++ b/transform/TransformFactory.cpp Mon Jan 30 17:51:56 2006 +0000 @@ -33,10 +33,20 @@ TransformFactory::TransformList TransformFactory::getAllTransforms() { + if (m_transforms.empty()) populateTransforms(); + TransformList list; -//!!! list.push_back(BeatDetectTransform::getName()); -// list.push_back(BeatDetectionFunctionTransform::getName()); + for (TransformMap::const_iterator i = m_transforms.begin(); + i != m_transforms.end(); ++i) { + list.push_back(TransformDesc(i->first, i->second)); + } + return list; +} + +void +TransformFactory::populateTransforms() +{ //!!! std::vector fexplugs = FeatureExtractionPluginFactory::getAllPluginIdentifiers(); @@ -62,24 +72,31 @@ plugin->getOutputDescriptors(); if (outputs.size() == 1) { - list.push_back - (TransformDesc - (QString("%1:%2").arg(pluginId).arg(outputs[0].name.c_str()), - pluginDescription)); + m_transforms[QString("%1:%2") + .arg(pluginId) + .arg(outputs[0].name.c_str())] + = pluginDescription; } else { for (size_t j = 0; j < outputs.size(); ++j) { - list.push_back - (TransformDesc - (QString("%1:%2").arg(pluginId).arg(outputs[j].name.c_str()), - QString("%1: %2").arg(pluginDescription) - .arg(outputs[j].description.c_str()))); + m_transforms[QString("%1:%2") + .arg(pluginId) + .arg(outputs[j].name.c_str())] + = QString("%1: %2") + .arg(pluginDescription) + .arg(outputs[j].description.c_str()); } } } } } - - return list; +} + +QString +TransformFactory::getTransformDescription(TransformName name) +{ + if (m_transforms.find(name) != m_transforms.end()) { + return m_transforms[name]; + } else return ""; } Transform * @@ -111,6 +128,7 @@ } if (start && transform) transform->start(); + transform->setObjectName(name); return transform; } diff -r 47500c27ac26 -r cc98d496d52b transform/TransformFactory.h --- a/transform/TransformFactory.h Mon Jan 30 13:19:42 2006 +0000 +++ b/transform/TransformFactory.h Mon Jan 30 17:51:56 2006 +0000 @@ -12,6 +12,8 @@ #include "Transform.h" +#include + class TransformFactory : public QObject { Q_OBJECT @@ -50,6 +52,8 @@ */ Model *transform(TransformName name, Model *inputModel); + QString getTransformDescription(TransformName name); + //!!! Need some way to indicate that the input model has changed / //been deleted so as not to blow up backgrounded transform! -- Or //indeed, if the output model has been deleted -- could equally @@ -65,6 +69,10 @@ Transform *createTransform(TransformName name, Model *inputModel, bool start); + typedef std::map TransformMap; + TransformMap m_transforms; + void populateTransforms(); + static TransformFactory *m_instance; };