You can not select more than 25 topics
Topics must start with a letter or number, can include dashes ('-') and can be up to 35 characters long.
387 lines
9.4 KiB
387 lines
9.4 KiB
/* -*- c-basic-offset: 4 indent-tabs-mode: nil -*- vi:set ts=8 sts=4 sw=4: */
|
|
|
|
/*
|
|
Rosegarden
|
|
A MIDI and audio sequencer and musical notation editor.
|
|
|
|
This program is Copyright 2000-2008
|
|
Guillaume Laurent <glaurent@telegraph-road.org>,
|
|
Chris Cannam <cannam@all-day-breakfast.com>,
|
|
Richard Bown <richard.bown@ferventsoftware.com>
|
|
|
|
The moral rights of Guillaume Laurent, Chris Cannam, and Richard
|
|
Bown to claim authorship of this work have been asserted.
|
|
|
|
Other copyrights also apply to some parts of this work. Please
|
|
see the AUTHORS file and individual file headers for details.
|
|
|
|
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 "MultiViewCommandHistory.h"
|
|
|
|
#include <klocale.h>
|
|
#include "misc/Debug.h"
|
|
#include <kactioncollection.h>
|
|
#include <kaction.h>
|
|
#include <kcommand.h>
|
|
#include <kstdaction.h>
|
|
#include <tqobject.h>
|
|
#include <tqpopupmenu.h>
|
|
#include <tqregexp.h>
|
|
#include <tqstring.h>
|
|
#include <kpopupmenu.h>
|
|
|
|
|
|
namespace Rosegarden
|
|
{
|
|
|
|
MultiViewCommandHistory::MultiViewCommandHistory() :
|
|
m_undoLimit(50),
|
|
m_redoLimit(50),
|
|
m_savedAt(0)
|
|
{
|
|
// nothing
|
|
}
|
|
|
|
MultiViewCommandHistory::~MultiViewCommandHistory()
|
|
{
|
|
m_savedAt = -1;
|
|
clearStack(m_undoStack);
|
|
clearStack(m_redoStack);
|
|
}
|
|
|
|
void
|
|
MultiViewCommandHistory::clear()
|
|
{
|
|
m_savedAt = -1;
|
|
clearStack(m_undoStack);
|
|
clearStack(m_redoStack);
|
|
}
|
|
|
|
void
|
|
MultiViewCommandHistory::attachView(KActionCollection *collection)
|
|
{
|
|
if (m_views.find(collection) != m_views.end())
|
|
return ;
|
|
|
|
RG_DEBUG << "MultiViewCommandHistory::attachView() : setting up undo/redo actions\n";
|
|
|
|
KToolBarPopupAction *undo = dynamic_cast<KToolBarPopupAction*>(collection->action(KStdAction::stdName(KStdAction::Undo)));
|
|
|
|
if (undo) {
|
|
connect(undo, TQT_SIGNAL(activated()),
|
|
this, TQT_SLOT(slotUndo()));
|
|
|
|
connect
|
|
(undo->popupMenu(),
|
|
TQT_SIGNAL(aboutToShow()),
|
|
this,
|
|
TQT_SLOT(slotUndoAboutToShow()));
|
|
|
|
connect
|
|
(undo->popupMenu(),
|
|
TQT_SIGNAL(activated(int)),
|
|
this,
|
|
TQT_SLOT(slotUndoActivated(int)));
|
|
}
|
|
|
|
KToolBarPopupAction *redo = dynamic_cast<KToolBarPopupAction*>(collection->action(KStdAction::stdName(KStdAction::Redo)));
|
|
|
|
if (redo) {
|
|
|
|
connect(redo, TQT_SIGNAL(activated()),
|
|
this, TQT_SLOT(slotRedo()));
|
|
|
|
connect
|
|
(redo->popupMenu(),
|
|
TQT_SIGNAL(aboutToShow()),
|
|
this,
|
|
TQT_SLOT(slotRedoAboutToShow()));
|
|
|
|
connect
|
|
(redo->popupMenu(),
|
|
TQT_SIGNAL(activated(int)),
|
|
this,
|
|
TQT_SLOT(slotRedoActivated(int)));
|
|
}
|
|
|
|
m_views.insert(collection);
|
|
updateButtons();
|
|
|
|
}
|
|
|
|
void
|
|
MultiViewCommandHistory::detachView(KActionCollection *collection)
|
|
{
|
|
ViewSet::iterator i = m_views.find(collection);
|
|
if (i != m_views.end())
|
|
m_views.erase(collection);
|
|
}
|
|
|
|
void
|
|
MultiViewCommandHistory::addCommand(KCommand *command, bool execute)
|
|
{
|
|
if (!command)
|
|
return ;
|
|
|
|
RG_DEBUG << "MultiViewCommandHistory::addCommand: " << command->name() << 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();
|
|
}
|
|
|
|
void
|
|
MultiViewCommandHistory::slotUndo()
|
|
{
|
|
if (m_undoStack.empty())
|
|
return ;
|
|
|
|
KCommand *command = m_undoStack.top();
|
|
command->unexecute();
|
|
emit commandExecuted();
|
|
emit commandExecuted(command);
|
|
|
|
m_redoStack.push(command);
|
|
m_undoStack.pop();
|
|
|
|
clipCommands();
|
|
updateButtons();
|
|
|
|
if ((int)m_undoStack.size() == m_savedAt)
|
|
emit documentRestored();
|
|
}
|
|
|
|
void
|
|
MultiViewCommandHistory::slotRedo()
|
|
{
|
|
if (m_redoStack.empty())
|
|
return ;
|
|
|
|
KCommand *command = m_redoStack.top();
|
|
command->execute();
|
|
emit commandExecuted();
|
|
emit commandExecuted(command);
|
|
|
|
m_undoStack.push(command);
|
|
m_redoStack.pop();
|
|
// no need to clip
|
|
updateButtons();
|
|
}
|
|
|
|
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) {
|
|
KCommand *togo = stack.top();
|
|
KNamedCommand *named = dynamic_cast<KNamedCommand *>(togo);
|
|
if (named) {
|
|
RG_DEBUG << "MVCH::clipStack: Saving recent command: " << named->name() << " at " << togo << endl;
|
|
} else {
|
|
RG_DEBUG << "MVCH::clipStack: Saving recent unnamed command" << " at " << togo << 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()) {
|
|
KCommand *togo = stack.top();
|
|
KNamedCommand *named = dynamic_cast<KNamedCommand *>(togo);
|
|
if (named) {
|
|
RG_DEBUG << "MVCH::clearStack: About to delete command: " << named->name() << " at " << togo << endl;
|
|
} else {
|
|
RG_DEBUG << "MVCH::clearStack: About to delete unnamed command" << " at " << togo << endl;
|
|
}
|
|
delete togo;
|
|
stack.pop();
|
|
}
|
|
}
|
|
|
|
void
|
|
MultiViewCommandHistory::slotUndoActivated(int pos)
|
|
{
|
|
for (int i = 0 ; i <= pos; ++i)
|
|
slotUndo();
|
|
}
|
|
|
|
void
|
|
MultiViewCommandHistory::slotRedoActivated(int pos)
|
|
{
|
|
for (int i = 0 ; i <= pos; ++i)
|
|
slotRedo();
|
|
}
|
|
|
|
void
|
|
MultiViewCommandHistory::slotUndoAboutToShow()
|
|
{
|
|
updateMenu(true, KStdAction::stdName(KStdAction::Undo), m_undoStack);
|
|
}
|
|
|
|
void
|
|
MultiViewCommandHistory::slotRedoAboutToShow()
|
|
{
|
|
updateMenu(false, KStdAction::stdName(KStdAction::Redo), m_redoStack);
|
|
}
|
|
|
|
void
|
|
MultiViewCommandHistory::updateButtons()
|
|
{
|
|
updateButton(true, KStdAction::stdName(KStdAction::Undo), m_undoStack);
|
|
updateButton(false, KStdAction::stdName(KStdAction::Redo), m_redoStack);
|
|
}
|
|
|
|
void
|
|
MultiViewCommandHistory::updateButton(bool undo,
|
|
const TQString &name,
|
|
CommandStack &stack)
|
|
{
|
|
for (ViewSet::iterator i = m_views.begin(); i != m_views.end(); ++i) {
|
|
|
|
KAction *action = (*i)->action(name);
|
|
if (!action)
|
|
continue;
|
|
TQString text;
|
|
|
|
if (stack.empty()) {
|
|
action->setEnabled(false);
|
|
if (undo)
|
|
text = i18n("Nothing to undo");
|
|
else
|
|
text = i18n("Nothing to redo");
|
|
action->setText(text);
|
|
} else {
|
|
action->setEnabled(true);
|
|
TQString commandName = stack.top()->name();
|
|
commandName.replace(TQRegExp("&"), "");
|
|
commandName.replace(TQRegExp("\\.\\.\\.$"), "");
|
|
if (undo)
|
|
text = i18n("Und&o %1").tqarg(commandName);
|
|
else
|
|
text = i18n("Re&do %1").tqarg(commandName);
|
|
action->setText(text);
|
|
}
|
|
}
|
|
}
|
|
|
|
void
|
|
MultiViewCommandHistory::updateMenu(bool undo,
|
|
const TQString &name,
|
|
CommandStack &stack)
|
|
{
|
|
for (ViewSet::iterator i = m_views.begin(); i != m_views.end(); ++i) {
|
|
|
|
KAction *action = (*i)->action(name);
|
|
if (!action)
|
|
continue;
|
|
|
|
KToolBarPopupAction *popupAction =
|
|
dynamic_cast<KToolBarPopupAction *>(action);
|
|
if (!popupAction)
|
|
continue;
|
|
|
|
TQPopupMenu *menu = popupAction->popupMenu();
|
|
if (!menu)
|
|
continue;
|
|
menu->clear();
|
|
|
|
CommandStack tempStack;
|
|
int j = 0;
|
|
|
|
while (j < 10 && !stack.empty()) {
|
|
|
|
KCommand *command = stack.top();
|
|
tempStack.push(command);
|
|
stack.pop();
|
|
|
|
TQString commandName = command->name();
|
|
commandName.replace(TQRegExp("&"), "");
|
|
commandName.replace(TQRegExp("\\.\\.\\.$"), "");
|
|
|
|
TQString text;
|
|
if (undo)
|
|
text = i18n("Und&o %1").tqarg(commandName);
|
|
else
|
|
text = i18n("Re&do %1").tqarg(commandName);
|
|
menu->insertItem(text, j++);
|
|
}
|
|
|
|
while (!tempStack.empty()) {
|
|
stack.push(tempStack.top());
|
|
tempStack.pop();
|
|
}
|
|
}
|
|
}
|
|
|
|
}
|
|
#include "MultiViewCommandHistory.moc"
|