Mercurial > hg > svcore
diff base/CommandHistory.cpp @ 17:2fb933f88604
* Update some copyright notice dates
* Add commands for basic editing operations in time-instants and time-value layers
author | Chris Cannam |
---|---|
date | Tue, 31 Jan 2006 15:57:25 +0000 |
parents | base/MultiViewCommandHistory.cpp@cc98d496d52b |
children | 2b6412c1e724 |
line wrap: on
line diff
--- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/base/CommandHistory.cpp Tue Jan 31 15:57:25 2006 +0000 @@ -0,0 +1,333 @@ +/* -*- 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 "CommandHistory.h" + +#include "Command.h" + +#include <QRegExp> +#include <QMenu> +#include <QToolBar> +#include <QString> + +#include <iostream> + +CommandHistory *CommandHistory::m_instance = 0; + +CommandHistory::CommandHistory() : + 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_undoMenuAction = new QAction(QIcon(":/icons/undo.png"), tr("&Undo"), this); + connect(m_undoMenuAction, SIGNAL(triggered()), this, SLOT(undo())); + + m_undoMenu = new QMenu(tr("&Undo")); + m_undoMenuAction->setMenu(m_undoMenu); + connect(m_undoMenu, SIGNAL(triggered(QAction *)), + this, SLOT(undoActivated(QAction*))); + + m_redoAction = new QAction(QIcon(":/icons/redo.png"), tr("Re&do"), this); + m_redoAction->setShortcut(tr("Ctrl+Shift+Z")); + connect(m_redoAction, SIGNAL(triggered()), this, SLOT(redo())); + + m_redoMenuAction = new QAction(QIcon(":/icons/redo.png"), tr("Re&do"), this); + connect(m_redoMenuAction, SIGNAL(triggered()), this, SLOT(redo())); + + m_redoMenu = new QMenu(tr("Re&do")); + m_redoMenuAction->setMenu(m_redoMenu); + connect(m_redoMenu, SIGNAL(triggered(QAction *)), + this, SLOT(redoActivated(QAction*))); +} + +CommandHistory::~CommandHistory() +{ + m_savedAt = -1; + clearStack(m_undoStack); + clearStack(m_redoStack); + + delete m_undoMenu; + delete m_redoMenu; +} + +CommandHistory * +CommandHistory::getInstance() +{ + if (!m_instance) m_instance = new CommandHistory(); + return m_instance; +} + +void +CommandHistory::clear() +{ + m_savedAt = -1; + clearStack(m_undoStack); + clearStack(m_redoStack); + updateActions(); +} + +void +CommandHistory::registerMenu(QMenu *menu) +{ + menu->addAction(m_undoAction); + menu->addAction(m_redoAction); +} + +void +CommandHistory::registerToolbar(QToolBar *toolbar) +{ + toolbar->addAction(m_undoMenuAction); + toolbar->addAction(m_redoMenuAction); +} + +void +CommandHistory::addCommand(Command *command, bool execute) +{ + if (!command) return; + + std::cerr << "MVCH::addCommand: " << command->getName().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 even if we aren't executing the command, because + // someone must have executed it for this to make any sense + emit commandExecuted(); + emit commandExecuted(command); + + updateActions(); +} + +void +CommandHistory::addExecutedCommand(Command *command) +{ + addCommand(command, false); +} + +void +CommandHistory::addCommandAndExecute(Command *command) +{ + addCommand(command, true); +} + +void +CommandHistory::undo() +{ + if (m_undoStack.empty()) return; + + Command *command = m_undoStack.top(); + command->unexecute(); + emit commandExecuted(); + emit commandUnexecuted(command); + + m_redoStack.push(command); + m_undoStack.pop(); + + clipCommands(); + updateActions(); + + if ((int)m_undoStack.size() == m_savedAt) emit documentRestored(); +} + +void +CommandHistory::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 +CommandHistory::setUndoLimit(int limit) +{ + if (limit > 0 && limit != m_undoLimit) { + m_undoLimit = limit; + clipCommands(); + } +} + +void +CommandHistory::setRedoLimit(int limit) +{ + if (limit > 0 && limit != m_redoLimit) { + m_redoLimit = limit; + clipCommands(); + } +} + +void +CommandHistory::documentSaved() +{ + m_savedAt = m_undoStack.size(); +} + +void +CommandHistory::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 +CommandHistory::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->getName().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 +CommandHistory::clearStack(CommandStack &stack) +{ + while (!stack.empty()) { + Command *command = stack.top(); + std::cerr << "MVCH::clearStack: About to delete command: " << command->getName().toLocal8Bit().data() << " at " << command << std::endl; + delete command; + stack.pop(); + } +} + +void +CommandHistory::undoActivated(QAction *action) +{ + int pos = m_actionCounts[action]; + for (int i = 0; i <= pos; ++i) { + undo(); + } +} + +void +CommandHistory::redoActivated(QAction *action) +{ + int pos = m_actionCounts[action]; + for (int i = 0; i <= pos; ++i) { + redo(); + } +} + +void +CommandHistory::updateActions() +{ + m_actionCounts.clear(); + + for (int undo = 0; undo <= 1; ++undo) { + + QAction *action(undo ? m_undoAction : m_redoAction); + QAction *menuAction(undo ? m_undoMenuAction : m_redoMenuAction); + QMenu *menu(undo ? m_undoMenu : m_redoMenu); + CommandStack &stack(undo ? m_undoStack : m_redoStack); + + if (stack.empty()) { + + QString text(undo ? tr("Nothing to undo") : tr("Nothing to redo")); + + action->setEnabled(false); + action->setText(text); + + menuAction->setEnabled(false); + menuAction->setText(text); + + } else { + + action->setEnabled(true); + menuAction->setEnabled(true); + + QString commandName = stack.top()->getName(); + commandName.replace(QRegExp("&"), ""); + + QString text = (undo ? tr("&Undo %1") : tr("Re&do %1")) + .arg(commandName); + + action->setText(text); + menuAction->setText(text); + } + + menu->clear(); + + CommandStack tempStack; + int j = 0; + + while (j < 10 && !stack.empty()) { + + Command *command = stack.top(); + tempStack.push(command); + stack.pop(); + + QString commandName = command->getName(); + 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(); + } + } +} +