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.
tdelibs/kdeui/kcommand.cpp

380 lines
11 KiB

/* This file is part of the KDE project
Copyright (C) 2000 Werner Trobin <trobin@kde.org>
Copyright (C) 2000 David Faure <faure@kde.org>
This library is free software; you can redistribute it and/or
modify it under the terms of the GNU Library General Public
License as published by the Free Software Foundation; either
version 2 of the License, or (at your option) any later version.
This library is distributed in the hope that it will be useful,
but WITHOUT ANY WARRANTY; without even the implied warranty of
MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
Library General Public License for more details.
You should have received a copy of the GNU Library General Public License
along with this library; see the file COPYING.LIB. If not, write to
the Free Software Foundation, Inc., 51 Franklin Street, Fifth Floor,
Boston, MA 02110-1301, USA.
*/
#include "kcommand.h"
#include <kaction.h>
#include <kstdaccel.h>
#include <kstdaction.h>
#include <kdebug.h>
#include <klocale.h>
#include <kpopupmenu.h>
KCommand::~KCommand()
{
}
KMacroCommand::KMacroCommand( const TQString & name ) : KNamedCommand(name)
{
m_commands.setAutoDelete(true);
}
void KMacroCommand::addCommand(KCommand *command)
{
m_commands.append(command);
}
void KMacroCommand::execute()
{
TQPtrListIterator<KCommand> it(m_commands);
for ( ; it.current() ; ++it )
it.current()->execute();
}
void KMacroCommand::unexecute()
{
TQPtrListIterator<KCommand> it(m_commands);
it.toLast();
for ( ; it.current() ; --it )
it.current()->unexecute();
}
class KCommandHistory::KCommandHistoryPrivate {
public:
KCommandHistoryPrivate() {
m_savedAt=-1;
m_present=0;
}
~KCommandHistoryPrivate() {}
int m_savedAt;
KCommand *m_present;
};
////////////
KCommandHistory::KCommandHistory() :
m_undo(0), m_redo(0), m_undoLimit(50), m_redoLimit(30), m_first(false)
{
d=new KCommandHistoryPrivate();
m_commands.setAutoDelete(true);
clear();
}
KCommandHistory::KCommandHistory(KActionCollection * actionCollection, bool withMenus) :
m_undoLimit(50), m_redoLimit(30), m_first(false)
{
d=new KCommandHistoryPrivate();
if (withMenus)
{
KToolBarPopupAction * undo = new KToolBarPopupAction( i18n("&Undo"), "undo",
KStdAccel::shortcut(KStdAccel::Undo), this, TQT_SLOT( undo() ),
actionCollection, KStdAction::stdName( KStdAction::Undo ) );
connect( undo->popupMenu(), TQT_SIGNAL( aboutToShow() ), this, TQT_SLOT( slotUndoAboutToShow() ) );
connect( undo->popupMenu(), TQT_SIGNAL( activated( int ) ), this, TQT_SLOT( slotUndoActivated( int ) ) );
m_undo = undo;
m_undoPopup = undo->popupMenu();
KToolBarPopupAction * redo = new KToolBarPopupAction( i18n("&Redo"), "redo",
KStdAccel::shortcut(KStdAccel::Redo), this, TQT_SLOT( redo() ),
actionCollection, KStdAction::stdName( KStdAction::Redo ) );
connect( redo->popupMenu(), TQT_SIGNAL( aboutToShow() ), this, TQT_SLOT( slotRedoAboutToShow() ) );
connect( redo->popupMenu(), TQT_SIGNAL( activated( int ) ), this, TQT_SLOT( slotRedoActivated( int ) ) );
m_redo = redo;
m_redoPopup = redo->popupMenu();
}
else
{
m_undo = KStdAction::undo( this, TQT_SLOT( undo() ), actionCollection );
m_redo = KStdAction::redo( this, TQT_SLOT( redo() ), actionCollection );
m_undoPopup = 0L;
m_redoPopup = 0L;
}
m_commands.setAutoDelete(true);
clear();
}
KCommandHistory::~KCommandHistory() {
delete d;
}
void KCommandHistory::clear() {
if (m_undo) {
m_undo->setEnabled(false);
m_undo->setText(i18n("&Undo"));
}
if (m_redo) {
m_redo->setEnabled(false);
m_redo->setText(i18n("&Redo"));
}
d->m_present = 0L;
d->m_savedAt=-42;
}
void KCommandHistory::addCommand(KCommand *command, bool execute) {
if(!command)
return;
int index;
if(d->m_present && (index=m_commands.findRef(d->m_present))!=-1) {
if (m_first)
--index;
m_commands.insert(index+1, command);
// truncate history
unsigned int count=m_commands.count();
for(unsigned int i=index+2; i<count; ++i)
m_commands.removeLast();
// check whether we still can reach savedAt
if(index<d->m_savedAt)
d->m_savedAt=-1;
d->m_present=command;
m_first=false;
if (m_undo) {
m_undo->setEnabled(true);
m_undo->setText(i18n("&Undo: %1").arg(d->m_present->name()));
}
if((m_redo) && m_redo->isEnabled()) {
m_redo->setEnabled(false);
m_redo->setText(i18n("&Redo"));
}
clipCommands();
}
else { // either this is the first time we add a Command or something has gone wrong
kdDebug(230) << "Initializing the Command History" << endl;
m_commands.clear();
m_commands.append(command);
d->m_present=command;
if (m_undo) {
m_undo->setEnabled(true);
m_undo->setText(i18n("&Undo: %1").arg(d->m_present->name()));
}
if (m_redo) {
m_redo->setEnabled(false);
m_redo->setText(i18n("&Redo"));
}
m_first=false; // Michael B: yes, that *is* correct :-)
}
if ( execute )
{
command->execute();
emit commandExecuted();
emit commandExecuted(command);
}
}
void KCommandHistory::undo() {
if (m_first || !d->m_present)
return;
d->m_present->unexecute();
emit commandExecuted();
emit commandExecuted(d->m_present);
if (m_redo) {
m_redo->setEnabled(true);
m_redo->setText(i18n("&Redo: %1").arg(d->m_present->name()));
}
int index;
if((index=m_commands.findRef(d->m_present))!=-1 && m_commands.prev()) {
d->m_present=m_commands.current();
if (m_undo) {
m_undo->setEnabled(true);
m_undo->setText(i18n("&Undo: %1").arg(d->m_present->name()));
}
--index;
if(index==d->m_savedAt)
emit documentRestored();
}
else {
if (m_undo) {
m_undo->setEnabled(false);
m_undo->setText(i18n("&Undo"));
}
if(d->m_savedAt==-42)
emit documentRestored();
m_first=true;
}
clipCommands(); // only needed here and in addCommand, NOT in redo
}
void KCommandHistory::redo() {
int index;
if(m_first) {
d->m_present->execute();
emit commandExecuted();
emit commandExecuted(d->m_present);
m_first=false;
m_commands.first();
if(!d->m_savedAt)
emit documentRestored();
}
else if((index=m_commands.findRef(d->m_present))!=-1 && m_commands.next()) {
d->m_present=m_commands.current();
d->m_present->execute();
emit commandExecuted();
emit commandExecuted(d->m_present);
++index;
if(index==d->m_savedAt)
emit documentRestored();
}
if (m_undo) {
m_undo->setEnabled(true);
m_undo->setText(i18n("&Undo: %1").arg(d->m_present->name()));
}
if(m_commands.next()) {
if (m_redo) {
m_redo->setEnabled(true);
m_redo->setText(i18n("&Redo: %1").arg(m_commands.current()->name()));
}
}
else {
if((m_redo) && m_redo->isEnabled()) {
m_redo->setEnabled(false);
m_redo->setText(i18n("&Redo"));
}
}
}
void KCommandHistory::documentSaved() {
if(d->m_present && !m_first)
d->m_savedAt=m_commands.findRef(d->m_present);
else if(!d->m_present && !m_first)
d->m_savedAt=-42; // this value signals that the document has
// been saved with an empty history.
else if(m_first)
d->m_savedAt=-42;
}
void KCommandHistory::setUndoLimit(int limit) {
if(limit>0 && limit!=m_undoLimit) {
m_undoLimit=limit;
clipCommands();
}
}
void KCommandHistory::setRedoLimit(int limit) {
if(limit>0 && limit!=m_redoLimit) {
m_redoLimit=limit;
clipCommands();
}
}
void KCommandHistory::clipCommands() {
int count=m_commands.count();
if(count<=m_undoLimit && count<=m_redoLimit)
return;
int index=m_commands.findRef(d->m_present);
if(index>=m_undoLimit) {
for(int i=0; i<=(index-m_undoLimit); ++i) {
m_commands.removeFirst();
--d->m_savedAt;
if(d->m_savedAt==-1)
d->m_savedAt=-42;
}
index=m_commands.findRef(d->m_present); // calculate the new
count=m_commands.count(); // values (for the redo-branch :)
// make it easier for us... d->m_savedAt==-1 -> invalid
if(d->m_savedAt!=-42 && d->m_savedAt<-1)
d->m_savedAt=-1;
}
// adjust the index if it's the first command
if(m_first)
index=-1;
if((index+m_redoLimit+1)<count) {
if(d->m_savedAt>(index+m_redoLimit))
d->m_savedAt=-1;
for(int i=0; i<(count-(index+m_redoLimit+1)); ++i)
m_commands.removeLast();
}
}
void KCommandHistory::slotUndoAboutToShow()
{
m_undoPopup->clear();
int i = 0;
if (m_commands.findRef(d->m_present)!=-1)
while ( m_commands.current() && i<10 ) // TODO make number of items configurable ?
{
m_undoPopup->insertItem( i18n("Undo: %1").arg(m_commands.current()->name()), i++ );
m_commands.prev();
}
}
void KCommandHistory::slotUndoActivated( int pos )
{
kdDebug(230) << "KCommandHistory::slotUndoActivated " << pos << endl;
for ( int i = 0 ; i < pos+1; ++i )
undo();
}
void KCommandHistory::slotRedoAboutToShow()
{
m_redoPopup->clear();
int i = 0;
if (m_first)
{
d->m_present = m_commands.first();
m_redoPopup->insertItem( i18n("Redo: %1").arg(d->m_present->name()), i++ );
}
if (m_commands.findRef(d->m_present)!=-1 && m_commands.next())
while ( m_commands.current() && i<10 ) // TODO make number of items configurable ?
{
m_redoPopup->insertItem( i18n("Redo: %1").arg(m_commands.current()->name()), i++ );
m_commands.next();
}
}
void KCommandHistory::slotRedoActivated( int pos )
{
kdDebug(230) << "KCommandHistory::slotRedoActivated " << pos << endl;
for ( int i = 0 ; i < pos+1; ++i )
redo();
}
void KCommandHistory::updateActions()
{
if ( m_undo && m_redo )
{
m_undo->setEnabled( !m_first && ( d->m_present ) );
m_redo->setEnabled(m_first || (m_commands.findRef(d->m_present)!=-1 && m_commands.next()));
}
}
void KCommand::virtual_hook( int, void* )
{ /*BASE::virtual_hook( id, data );*/ }
void KNamedCommand::virtual_hook( int id, void* data )
{ KCommand::virtual_hook( id, data ); }
void KMacroCommand::virtual_hook( int id, void* data )
{ KNamedCommand::virtual_hook( id, data ); }
void KCommandHistory::virtual_hook( int, void* )
{ /*BASE::virtual_hook( id, data );*/ }
#include "kcommand.moc"