kdeui Library API Documentation

kcommand.cpp

00001 /* This file is part of the KDE project
00002    Copyright (C) 2000 Werner Trobin <trobin@kde.org>
00003    Copyright (C) 2000 David Faure <faure@kde.org>
00004 
00005    This library is free software; you can redistribute it and/or
00006    modify it under the terms of the GNU Library General Public
00007    License as published by the Free Software Foundation; either
00008    version 2 of the License, or (at your option) any later version.
00009 
00010    This library is distributed in the hope that it will be useful,
00011    but WITHOUT ANY WARRANTY; without even the implied warranty of
00012    MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
00013    Library General Public License for more details.
00014 
00015    You should have received a copy of the GNU Library General Public License
00016    along with this library; see the file COPYING.LIB.  If not, write to
00017    the Free Software Foundation, Inc., 59 Temple Place - Suite 330,
00018    Boston, MA 02111-1307, USA.
00019 */
00020 
00021 #include "kcommand.h"
00022 #include <kaction.h>
00023 #include <kstdaccel.h>
00024 #include <kstdaction.h>
00025 #include <kdebug.h>
00026 #include <klocale.h>
00027 #include <kpopupmenu.h>
00028 
00029 KCommand::~KCommand()
00030 {
00031 }
00032 
00033 KMacroCommand::KMacroCommand( const QString & name ) : KNamedCommand(name)
00034 {
00035     m_commands.setAutoDelete(true);
00036 }
00037 
00038 void KMacroCommand::addCommand(KCommand *command)
00039 {
00040     m_commands.append(command);
00041 }
00042 
00043 void KMacroCommand::execute()
00044 {
00045     QPtrListIterator<KCommand> it(m_commands);
00046     for ( ; it.current() ; ++it )
00047         it.current()->execute();
00048 }
00049 
00050 void KMacroCommand::unexecute()
00051 {
00052     QPtrListIterator<KCommand> it(m_commands);
00053     it.toLast();
00054     for ( ; it.current() ; --it )
00055         it.current()->unexecute();
00056 }
00057 
00058 
00059 class KCommandHistory::KCommandHistoryPrivate {
00060 public:
00061     KCommandHistoryPrivate() {
00062         m_savedAt=-1;
00063         m_present=0;
00064     }
00065     ~KCommandHistoryPrivate() {}
00066     int m_savedAt;
00067     KCommand *m_present;
00068 };
00069 
00071 
00072 KCommandHistory::KCommandHistory() :
00073     m_undo(0), m_redo(0), m_undoLimit(50), m_redoLimit(30), m_first(false)
00074 {
00075     d=new KCommandHistoryPrivate();
00076     m_commands.setAutoDelete(true);
00077     clear();
00078 }
00079 
00080 KCommandHistory::KCommandHistory(KActionCollection * actionCollection, bool withMenus) :
00081     m_undoLimit(50), m_redoLimit(30), m_first(false)
00082 {
00083     d=new KCommandHistoryPrivate();
00084     if (withMenus)
00085     {
00086         KToolBarPopupAction * undo = new KToolBarPopupAction( i18n("&Undo"), "undo",
00087                                           KStdAccel::key(KStdAccel::Undo), this, SLOT( undo() ),
00088                                           actionCollection, KStdAction::stdName( KStdAction::Undo ) );
00089         connect( undo->popupMenu(), SIGNAL( aboutToShow() ), this, SLOT( slotUndoAboutToShow() ) );
00090         connect( undo->popupMenu(), SIGNAL( activated( int ) ), this, SLOT( slotUndoActivated( int ) ) );
00091         m_undo = undo;
00092         m_undoPopup = undo->popupMenu();
00093 
00094         KToolBarPopupAction * redo = new KToolBarPopupAction( i18n("&Redo"), "redo",
00095                                           KStdAccel::key(KStdAccel::Redo), this, SLOT( redo() ),
00096                                           actionCollection, KStdAction::stdName( KStdAction::Redo ) );
00097         connect( redo->popupMenu(), SIGNAL( aboutToShow() ), this, SLOT( slotRedoAboutToShow() ) );
00098         connect( redo->popupMenu(), SIGNAL( activated( int ) ), this, SLOT( slotRedoActivated( int ) ) );
00099         m_redo = redo;
00100         m_redoPopup = redo->popupMenu();
00101     }
00102     else
00103     {
00104         m_undo = KStdAction::undo( this, SLOT( undo() ), actionCollection );
00105         m_redo = KStdAction::redo( this, SLOT( redo() ), actionCollection );
00106         m_undoPopup = 0L;
00107         m_redoPopup = 0L;
00108     }
00109     m_commands.setAutoDelete(true);
00110     clear();
00111 }
00112 
00113 KCommandHistory::~KCommandHistory() {
00114     delete d;
00115 }
00116 
00117 void KCommandHistory::clear() {
00118     if (m_undo != 0) {
00119         m_undo->setEnabled(false);
00120         m_undo->setText(i18n("&Undo"));
00121     }
00122     if (m_redo != 0) {
00123         m_redo->setEnabled(false);
00124         m_redo->setText(i18n("&Redo"));
00125     }
00126     d->m_present = 0L;
00127     d->m_savedAt=-42;
00128 }
00129 
00130 void KCommandHistory::addCommand(KCommand *command, bool execute) {
00131 
00132     if(command==0L)
00133         return;
00134 
00135     int index;
00136     if(d->m_present!=0L && (index=m_commands.findRef(d->m_present))!=-1) {
00137         if (m_first)
00138             --index;
00139         m_commands.insert(index+1, command);
00140         // truncate history
00141         unsigned int count=m_commands.count();
00142         for(unsigned int i=index+2; i<count; ++i)
00143             m_commands.removeLast();
00144         // check whether we still can reach savedAt
00145         if(index<d->m_savedAt)
00146             d->m_savedAt=-1;
00147         d->m_present=command;
00148         m_first=false;
00149         if (m_undo != 0) {
00150             m_undo->setEnabled(true);
00151             m_undo->setText(i18n("&Undo: %1").arg(d->m_present->name()));
00152         }
00153         if((m_redo != 0) && m_redo->isEnabled()) {
00154             m_redo->setEnabled(false);
00155             m_redo->setText(i18n("&Redo"));
00156         }
00157         clipCommands();
00158     }
00159     else { // either this is the first time we add a Command or something has gone wrong
00160         kdDebug(230) << "Initializing the Command History" << endl;
00161         m_commands.clear();
00162         m_commands.append(command);
00163         d->m_present=command;
00164         if (m_undo != 0) {
00165             m_undo->setEnabled(true);
00166             m_undo->setText(i18n("&Undo: %1").arg(d->m_present->name()));
00167         }
00168         if (m_redo != 0) {
00169             m_redo->setEnabled(false);
00170             m_redo->setText(i18n("&Redo"));
00171         }
00172         m_first=false;    // Michael B: yes, that *is* correct :-)
00173     }
00174     if ( execute )
00175     {
00176         command->execute();
00177         emit commandExecuted();
00178     }
00179 }
00180 
00181 void KCommandHistory::undo() {
00182 
00183     if (m_first || (d->m_present == 0L))
00184         return;
00185 
00186     d->m_present->unexecute();
00187     emit commandExecuted();
00188     if (m_redo != 0) {
00189         m_redo->setEnabled(true);
00190         m_redo->setText(i18n("&Redo: %1").arg(d->m_present->name()));
00191     }
00192     int index;
00193     if((index=m_commands.findRef(d->m_present))!=-1 && m_commands.prev()!=0) {
00194         d->m_present=m_commands.current();
00195         if (m_undo != 0) {
00196             m_undo->setEnabled(true);
00197             m_undo->setText(i18n("&Undo: %1").arg(d->m_present->name()));
00198         }
00199         --index;
00200         if(index==d->m_savedAt)
00201             emit documentRestored();
00202     }
00203     else {
00204         if (m_undo != 0) {
00205             m_undo->setEnabled(false);
00206             m_undo->setText(i18n("&Undo"));
00207         }
00208         if(d->m_savedAt==-42)
00209             emit documentRestored();
00210         m_first=true;
00211     }
00212     clipCommands(); // only needed here and in addCommand, NOT in redo
00213 }
00214 
00215 void KCommandHistory::redo() {
00216 
00217     int index;
00218     if(m_first) {
00219         d->m_present->execute();
00220         emit commandExecuted();
00221         m_first=false;
00222         m_commands.first();
00223         if(d->m_savedAt==0)
00224             emit documentRestored();
00225     }
00226     else if((index=m_commands.findRef(d->m_present))!=-1 && m_commands.next()!=0) {
00227         d->m_present=m_commands.current();
00228         d->m_present->execute();
00229         emit commandExecuted();
00230         ++index;
00231         if(index==d->m_savedAt)
00232             emit documentRestored();
00233     }
00234 
00235     if (m_undo != 0) {
00236         m_undo->setEnabled(true);
00237         m_undo->setText(i18n("&Undo: %1").arg(d->m_present->name()));
00238     }
00239 
00240     if(m_commands.next()!=0) {
00241         if (m_redo != 0) {
00242             m_redo->setEnabled(true);
00243             m_redo->setText(i18n("&Redo: %1").arg(m_commands.current()->name()));
00244         }
00245     }
00246     else {
00247         if((m_redo != 0) && m_redo->isEnabled()) {
00248             m_redo->setEnabled(false);
00249             m_redo->setText(i18n("&Redo"));
00250         }
00251     }
00252 }
00253 
00254 void KCommandHistory::documentSaved() {
00255     if(d->m_present!=0 && !m_first)
00256         d->m_savedAt=m_commands.findRef(d->m_present);
00257     else if(d->m_present==0 && !m_first)
00258         d->m_savedAt=-42;  // this value signals that the document has
00259                         // been saved with an empty history.
00260     else if(m_first)
00261         d->m_savedAt=-42;
00262 }
00263 
00264 void KCommandHistory::setUndoLimit(int limit) {
00265 
00266     if(limit>0 && limit!=m_undoLimit) {
00267         m_undoLimit=limit;
00268         clipCommands();
00269     }
00270 }
00271 
00272 void KCommandHistory::setRedoLimit(int limit) {
00273 
00274     if(limit>0 && limit!=m_redoLimit) {
00275         m_redoLimit=limit;
00276         clipCommands();
00277     }
00278 }
00279 
00280 void KCommandHistory::clipCommands() {
00281 
00282     int count=m_commands.count();
00283     if(count<=m_undoLimit && count<=m_redoLimit)
00284         return;
00285 
00286     int index=m_commands.findRef(d->m_present);
00287     if(index>=m_undoLimit) {
00288         for(int i=0; i<=(index-m_undoLimit); ++i) {
00289             m_commands.removeFirst();
00290             --d->m_savedAt;
00291             if(d->m_savedAt==-1)
00292                 d->m_savedAt=-42;
00293         }
00294         index=m_commands.findRef(d->m_present); // calculate the new
00295         count=m_commands.count();            // values (for the redo-branch :)
00296         // make it easier for us... d->m_savedAt==-1 -> invalid
00297         if(d->m_savedAt!=-42 && d->m_savedAt<-1)
00298             d->m_savedAt=-1;
00299     }
00300     // adjust the index if it's the first command
00301     if(m_first)
00302         index=-1;
00303     if((index+m_redoLimit+1)<count) {
00304         if(d->m_savedAt>(index+m_redoLimit))
00305             d->m_savedAt=-1;
00306         for(int i=0; i<(count-(index+m_redoLimit+1)); ++i)
00307             m_commands.removeLast();
00308     }
00309 }
00310 
00311 void KCommandHistory::slotUndoAboutToShow()
00312 {
00313     m_undoPopup->clear();
00314     int i = 0;
00315     if (m_commands.findRef(d->m_present)!=-1)
00316         while ( m_commands.current() && i<10 ) // TODO make number of items configurable ?
00317         {
00318             m_undoPopup->insertItem( i18n("Undo: %1").arg(m_commands.current()->name()), i++ );
00319             m_commands.prev();
00320         }
00321 }
00322 
00323 void KCommandHistory::slotUndoActivated( int pos )
00324 {
00325     kdDebug(230) << "KCommandHistory::slotUndoActivated " << pos << endl;
00326     for ( int i = 0 ; i < pos+1; ++i )
00327         undo();
00328 }
00329 
00330 void KCommandHistory::slotRedoAboutToShow()
00331 {
00332     m_redoPopup->clear();
00333     int i = 0;
00334     if (m_first)
00335     {
00336         d->m_present = m_commands.first();
00337         m_redoPopup->insertItem( i18n("Redo: %1").arg(d->m_present->name()), i++ );
00338     }
00339     if (m_commands.findRef(d->m_present)!=-1 && m_commands.next())
00340         while ( m_commands.current() && i<10 ) // TODO make number of items configurable ?
00341         {
00342             m_redoPopup->insertItem( i18n("Redo: %1").arg(m_commands.current()->name()), i++ );
00343             m_commands.next();
00344         }
00345 }
00346 
00347 void KCommandHistory::slotRedoActivated( int pos )
00348 {
00349     kdDebug(230) << "KCommandHistory::slotRedoActivated " << pos << endl;
00350     for ( int i = 0 ; i < pos+1; ++i )
00351         redo();
00352 }
00353 
00354 void KCommandHistory::updateActions()
00355 {
00356     if ( m_undo && m_redo )
00357     {
00358         m_undo->setEnabled( !m_first && ( d->m_present != 0L ) );
00359         m_redo->setEnabled(m_first || (m_commands.findRef(d->m_present)!=-1 && m_commands.next()!=0));
00360     }
00361 }
00362 
00363 void KCommand::virtual_hook( int, void* )
00364 { /*BASE::virtual_hook( id, data );*/ }
00365 
00366 void KNamedCommand::virtual_hook( int id, void* data )
00367 { KCommand::virtual_hook( id, data ); }
00368 
00369 void KMacroCommand::virtual_hook( int id, void* data )
00370 { KNamedCommand::virtual_hook( id, data ); }
00371 
00372 void KCommandHistory::virtual_hook( int, void* )
00373 { /*BASE::virtual_hook( id, data );*/ }
00374 
00375 #include "kcommand.moc"
KDE Logo
This file is part of the documentation for kdelibs Version 3.1.4.
Documentation copyright © 1996-2002 the KDE developers.
Generated on Sun Feb 27 22:15:02 2005 by doxygen 1.3.4 written by Dimitri van Heesch, © 1997-2001