Mercurial > hg > svcore
view base/MultiViewCommandHistory.cpp @ 16:cc98d496d52b
* 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
author | Chris Cannam |
---|---|
date | Mon, 30 Jan 2006 17:51:56 +0000 |
parents | |
children |
line wrap: on
line source
/* -*- 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 <QRegExp> #include <QMenu> #include <QToolBar> #include <QString> #include <iostream> 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(); } } }