summaryrefslogtreecommitdiffstats
path: root/kdeui/kcommand.cpp
diff options
context:
space:
mode:
Diffstat (limited to 'kdeui/kcommand.cpp')
-rw-r--r--kdeui/kcommand.cpp379
1 files changed, 379 insertions, 0 deletions
diff --git a/kdeui/kcommand.cpp b/kdeui/kcommand.cpp
new file mode 100644
index 000000000..bf133c609
--- /dev/null
+++ b/kdeui/kcommand.cpp
@@ -0,0 +1,379 @@
+/* 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 QString & name ) : KNamedCommand(name)
+{
+ m_commands.setAutoDelete(true);
+}
+
+void KMacroCommand::addCommand(KCommand *command)
+{
+ m_commands.append(command);
+}
+
+void KMacroCommand::execute()
+{
+ QPtrListIterator<KCommand> it(m_commands);
+ for ( ; it.current() ; ++it )
+ it.current()->execute();
+}
+
+void KMacroCommand::unexecute()
+{
+ QPtrListIterator<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, SLOT( undo() ),
+ actionCollection, KStdAction::stdName( KStdAction::Undo ) );
+ connect( undo->popupMenu(), SIGNAL( aboutToShow() ), this, SLOT( slotUndoAboutToShow() ) );
+ connect( undo->popupMenu(), SIGNAL( activated( int ) ), this, SLOT( slotUndoActivated( int ) ) );
+ m_undo = undo;
+ m_undoPopup = undo->popupMenu();
+
+ KToolBarPopupAction * redo = new KToolBarPopupAction( i18n("&Redo"), "redo",
+ KStdAccel::shortcut(KStdAccel::Redo), this, SLOT( redo() ),
+ actionCollection, KStdAction::stdName( KStdAction::Redo ) );
+ connect( redo->popupMenu(), SIGNAL( aboutToShow() ), this, SLOT( slotRedoAboutToShow() ) );
+ connect( redo->popupMenu(), SIGNAL( activated( int ) ), this, SLOT( slotRedoActivated( int ) ) );
+ m_redo = redo;
+ m_redoPopup = redo->popupMenu();
+ }
+ else
+ {
+ m_undo = KStdAction::undo( this, SLOT( undo() ), actionCollection );
+ m_redo = KStdAction::redo( this, 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"