• Skip to content
  • Skip to link menu
KDE 4.7 API Reference
  • KDE API Reference
  • kdelibs
  • KDE Home
  • Contact Us
 

KDE3Support

k3command.cpp
Go to the documentation of this file.
00001 /* This file is part of the KDE project
00002    Copyright (C) 2000 Werner Trobin <trobin@kde.org>
00003    Copyright (C) 2000,2006 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., 51 Franklin Street, Fifth Floor,
00018    Boston, MA 02110-1301, USA.
00019 */
00020 
00021 #include "k3command.h"
00022 #include <kaction.h>
00023 #include <kactioncollection.h>
00024 #include <kstandardshortcut.h>
00025 #include <kstandardaction.h>
00026 #include <kdebug.h>
00027 #include <kicon.h>
00028 #include <klocale.h>
00029 #include <kmenu.h>
00030 
00031 #include "ktoolbarpopupaction.h"
00032 
00033 K3Command::K3Command()
00034     : d( 0 )
00035 {
00036 }
00037 
00038 K3Command::~K3Command()
00039 {
00040 }
00041 
00042 class K3NamedCommand::Private
00043 {
00044     public:
00045         QString name;
00046 };
00047 
00048 K3NamedCommand::K3NamedCommand( const QString &name )
00049     : K3Command(),
00050       d( new Private )
00051 {
00052     d->name = name;
00053 }
00054 
00055 K3NamedCommand::~K3NamedCommand()
00056 {
00057     delete d;
00058 }
00059 
00060 QString K3NamedCommand::name() const
00061 {
00062     return d->name;
00063 }
00064 
00065 void K3NamedCommand::setName( const QString &name )
00066 {
00067     d->name = name;
00068 }
00069 
00070 class K3MacroCommand::Private
00071 {
00072     public:
00073         QList<K3Command *> commands;
00074 };
00075 
00076 K3MacroCommand::K3MacroCommand( const QString & name )
00077     : K3NamedCommand(name),
00078       d( new Private )
00079 {
00080 }
00081 
00082 K3MacroCommand::~K3MacroCommand()
00083 {
00084     qDeleteAll( d->commands );
00085     delete d;
00086 }
00087 
00088 void K3MacroCommand::addCommand( K3Command *command )
00089 {
00090     d->commands.append(command);
00091 }
00092 
00093 void K3MacroCommand::execute()
00094 {
00095     QListIterator<K3Command *> it( d->commands );
00096     while ( it.hasNext() ) {
00097         it.next()->execute();
00098     }
00099 }
00100 
00101 void K3MacroCommand::unexecute()
00102 {
00103     QListIterator<K3Command *> it( d->commands );
00104     it.toBack();
00105     while ( it.hasPrevious() ) {
00106         it.previous()->unexecute();
00107     }
00108 }
00109 
00110 const QList<K3Command *> K3MacroCommand::commands() const
00111 {
00112     return d->commands;
00113 }
00114 
00115 
00116 class K3CommandHistory::K3CommandHistoryPrivate {
00117 public:
00118     K3CommandHistoryPrivate()
00119         : m_undoLimit(50), m_redoLimit(30),
00120         m_savedAt(-1), m_current(-1) {
00121     }
00122     ~K3CommandHistoryPrivate() {
00123         qDeleteAll( m_commands );
00124     }
00125 
00126     QList<K3Command *> m_commands;
00127     int m_undoLimit, m_redoLimit;
00128 
00129     int m_savedAt;
00130     int m_current;
00131     /*
00132     If m_commands contains:  <c0> <c1> <c2> <c3>
00133 
00134     m_current = 1 means we are between <c1> and <c2>, i.e. undo would unexecute c1.
00135     So m_current is the index of the current undo command, m_current+1 the current redo command if any.
00136 
00137     Adding a command at this point would delete <c2> and <c3>.
00138        m_current compared to the commands:  -1 <c0> 0 <c1> 1 <c2> 2.
00139 
00140     m_savedAt = 1 means that we where at m_current == 1 when the document was saved.
00141     m_savedAt = -1 means that the document was saved with an empty history (initial state, too).
00142     m_savedAt = -2 means that the document wasn't saved in the current visible history
00143          (this happens when the undo history got truncated)
00144     */
00145 };
00146 
00148 
00149 K3CommandHistory::K3CommandHistory() :
00150     d( new K3CommandHistoryPrivate )
00151 {
00152     clear();
00153 }
00154 
00155 K3CommandHistory::K3CommandHistory(KActionCollection * actionCollection, bool withMenus) :
00156     d( new K3CommandHistoryPrivate )
00157 {
00158     if (withMenus)
00159     {
00160         // TODO instead of a menu this should show a listbox like koffice's KoCommandHistory does,
00161         // so that it's clearer that N actions will be undone together, not just action number N.
00162 
00163         // TODO also move this out of K3CommandHistory, to make it core-only.
00164 
00165         new K3UndoRedoAction( K3UndoRedoAction::Undo, actionCollection, this );
00166         new K3UndoRedoAction( K3UndoRedoAction::Redo, actionCollection, this );
00167     }
00168     else
00169     {
00170         actionCollection->addAction(KStandardAction::Undo, this, SLOT(undo()));
00171         actionCollection->addAction(KStandardAction::Redo, this, SLOT(redo()));
00172     }
00173     clear();
00174 }
00175 
00176 K3CommandHistory::~K3CommandHistory() {
00177     delete d;
00178 }
00179 
00180 void K3CommandHistory::clear() {
00181     qDeleteAll( d->m_commands );
00182     d->m_commands.clear();
00183     d->m_current = -1;
00184     d->m_savedAt = -1;
00185     emit commandHistoryChanged();
00186 }
00187 
00188 void K3CommandHistory::addCommand(K3Command *command, bool execute) {
00189     if ( !command )
00190         return;
00191 
00192     ++d->m_current;
00193     d->m_commands.insert( d->m_current, command );
00194     // Truncate history
00195     int count = d->m_commands.count();
00196     for ( int i = d->m_current + 1; i < count; ++i )
00197         delete d->m_commands.takeLast();
00198 
00199     // Check whether we still can reach savedAt
00200     if ( d->m_current < d->m_savedAt )
00201         d->m_savedAt = -2;
00202 
00203     clipCommands();
00204 
00205     if ( execute )
00206     {
00207         command->execute();
00208         emit commandExecuted(command);
00209     }
00210 }
00211 
00212 K3Command * K3CommandHistory::presentCommand() const
00213 {
00214     if ( d->m_current >= 0 )
00215         return d->m_commands[ d->m_current ];
00216     return 0;
00217 }
00218 
00219 void K3CommandHistory::undo() {
00220     Q_ASSERT( d->m_current >= 0 );
00221 
00222     K3Command* command = d->m_commands[ d->m_current ];
00223 
00224     command->unexecute();
00225     emit commandExecuted( command );
00226 
00227     --d->m_current;
00228 
00229     if ( d->m_current == d->m_savedAt )
00230         emit documentRestored();
00231 
00232     clipCommands(); // only needed here and in addCommand, NOT in redo
00233 }
00234 
00235 void K3CommandHistory::redo() {
00236     K3Command* command = d->m_commands[ d->m_current + 1 ];
00237     command->execute();
00238     emit commandExecuted( command );
00239 
00240     ++d->m_current;
00241 
00242     if ( d->m_current == d->m_savedAt )
00243         emit documentRestored();
00244 
00245     emit commandHistoryChanged();
00246 }
00247 
00248 void K3CommandHistory::documentSaved() {
00249     d->m_savedAt = d->m_current;
00250 }
00251 
00252 void K3CommandHistory::setUndoLimit(int limit) {
00253     if ( limit>0 && limit != d->m_undoLimit ) {
00254         d->m_undoLimit = limit;
00255         clipCommands();
00256     }
00257 }
00258 
00259 void K3CommandHistory::setRedoLimit(int limit) {
00260     if ( limit>0 && limit != d->m_redoLimit ) {
00261         d->m_redoLimit = limit;
00262         clipCommands();
00263     }
00264 }
00265 
00266 void K3CommandHistory::clipCommands() {
00267     int count = d->m_commands.count();
00268     if ( count <= d->m_undoLimit && count <= d->m_redoLimit ) {
00269         emit commandHistoryChanged();
00270         return;
00271     }
00272 
00273     if ( d->m_current >= d->m_undoLimit ) {
00274         const int toRemove = (d->m_current - d->m_undoLimit) + 1;
00275         for ( int i = 0; i < toRemove; ++i ) {
00276             delete d->m_commands.takeFirst();
00277             --d->m_savedAt;
00278             --d->m_current;
00279         }
00280         Q_ASSERT( d->m_current >= -1 );
00281         count = d->m_commands.count(); // update count for the redo branch below
00282         if ( d->m_savedAt < 0 )
00283             d->m_savedAt = -1; // savedAt went out of the history
00284     }
00285 
00286     if ( d->m_current + d->m_redoLimit + 1 < count ) {
00287         if ( d->m_savedAt > (d->m_current + d->m_redoLimit) )
00288             d->m_savedAt = -1;
00289         const int toRemove = count - (d->m_current + d->m_redoLimit + 1);
00290         for ( int i = 0; i< toRemove; ++i )
00291             delete d->m_commands.takeLast();
00292     }
00293     emit commandHistoryChanged();
00294 }
00295 
00296 void K3CommandHistory::updateActions()
00297 {
00298     // it hasn't changed, but this updates all actions connected to this command history.
00299     emit commandHistoryChanged();
00300 }
00301 
00302 bool K3CommandHistory::isUndoAvailable() const
00303 {
00304     return d->m_current >= 0;
00305 }
00306 
00307 bool K3CommandHistory::isRedoAvailable() const
00308 {
00309     return d->m_current < d->m_commands.count() - 1;
00310 }
00311 
00312 QList<K3Command *> K3CommandHistory::undoCommands( int maxCommands ) const
00313 {
00314     QList<K3Command *> lst;
00315     for ( int i = d->m_current; i >= 0; --i ) {
00316         lst.append( d->m_commands[i] );
00317         if ( maxCommands > 0 && lst.count() == maxCommands )
00318             break;
00319     }
00320     return lst;
00321 }
00322 
00323 QList<K3Command *> K3CommandHistory::redoCommands( int maxCommands ) const
00324 {
00325     QList<K3Command *> lst;
00326     for ( int i = d->m_current + 1; i < d->m_commands.count(); ++i )
00327     {
00328         lst.append( d->m_commands[i] );
00329         if ( maxCommands > 0 && lst.count() == maxCommands )
00330             break;
00331     }
00332     return lst;
00333 }
00334 
00335 int K3CommandHistory::undoLimit() const
00336 {
00337      return d->m_undoLimit;
00338 }
00339 
00340 int K3CommandHistory::redoLimit() const
00341 {
00342      return d->m_redoLimit;
00343 }
00344 
00345 class K3UndoRedoAction::Private
00346 {
00347     public:
00348         Private( K3UndoRedoAction::Type type, K3CommandHistory* commandHistory)
00349             : type( type ),
00350               commandHistory( commandHistory )
00351         {
00352         }
00353 
00354         Type type;
00355         K3CommandHistory* commandHistory;
00356 };
00357 
00358 
00359 
00360 K3UndoRedoAction::K3UndoRedoAction( Type type, KActionCollection* actionCollection, K3CommandHistory* commandHistory )
00361     : KToolBarPopupAction( KIcon( type == Undo ? "edit-undo" : "edit-redo" ),
00362                            QString(), // text is set in clear() on start
00363                            actionCollection),
00364       d( new Private( type, commandHistory ) )
00365 {
00366     setShortcut( KStandardShortcut::shortcut( type == Undo ? KStandardShortcut::Undo : KStandardShortcut::Redo ) );
00367     if ( d->type == Undo ) {
00368         connect( this, SIGNAL(triggered(bool)), d->commandHistory, SLOT(undo()) );
00369     } else {
00370         connect( this, SIGNAL(triggered(bool)), d->commandHistory, SLOT(redo()) );
00371     }
00372     connect( this->menu(), SIGNAL(aboutToShow()), this, SLOT(slotAboutToShow()) );
00373     connect( this->menu(), SIGNAL(triggered(QAction*)), this, SLOT(slotActionTriggered(QAction*)) );
00374 
00375     connect( d->commandHistory, SIGNAL(commandHistoryChanged()), this, SLOT(slotCommandHistoryChanged()) );
00376     slotCommandHistoryChanged();
00377     actionCollection->addAction(KStandardAction::name(type == Undo ? KStandardAction::Undo : KStandardAction::Redo),
00378                                 this);
00379 }
00380 
00381 void K3UndoRedoAction::slotAboutToShow()
00382 {
00383     menu()->clear();
00384     // TODO make number of items configurable ?
00385     const int maxCommands = 9;
00386     if ( d->type == Undo ) {
00387         const QList<K3Command *> commands = d->commandHistory->undoCommands( maxCommands );
00388         for (int i = 0; i < commands.count(); ++i) {
00389             QAction *action = menu()->addAction( i18n("Undo: %1", commands[i]->name()) );
00390             action->setData( i );
00391         }
00392     } else {
00393         const QList<K3Command *> commands = d->commandHistory->redoCommands( maxCommands );
00394         for (int i = 0; i < commands.count(); ++i) {
00395             QAction *action = menu()->addAction( i18n("Redo: %1", commands[i]->name()) );
00396             action->setData( i );
00397         }
00398     }
00399 }
00400 
00401 void K3UndoRedoAction::slotActionTriggered( QAction *action )
00402 {
00403     const int pos = action->data().toInt();
00404     //kDebug(230) << pos;
00405     if ( d->type == Undo ) {
00406         for ( int i = 0 ; i < pos+1; ++i ) {
00407             d->commandHistory->undo();
00408         }
00409     } else {
00410         for ( int i = 0 ; i < pos+1; ++i ) {
00411             d->commandHistory->redo();
00412         }
00413     }
00414 }
00415 
00416 void K3UndoRedoAction::slotCommandHistoryChanged()
00417 {
00418     const bool isUndo = d->type == Undo;
00419     const bool enabled = isUndo ? d->commandHistory->isUndoAvailable() : d->commandHistory->isRedoAvailable();
00420     setEnabled(enabled);
00421     if (!enabled) {
00422         setText(isUndo ? i18n("&Undo") : i18n("&Redo"));
00423     } else {
00424         if (isUndo) {
00425             K3Command* presentCommand = d->commandHistory->presentCommand();
00426             Q_ASSERT(presentCommand);
00427             setText(i18n("&Undo: %1", presentCommand->name()));
00428         } else {
00429             K3Command* redoCommand = d->commandHistory->redoCommands(1).first();
00430             setText(i18n("&Redo: %1", redoCommand->name()));
00431         }
00432     }
00433 }
00434 
00435 
00436 void K3Command::virtual_hook( int, void* )
00437 { /*BASE::virtual_hook( id, data );*/ }
00438 
00439 void K3NamedCommand::virtual_hook( int id, void* data )
00440 { K3Command::virtual_hook( id, data ); }
00441 
00442 void K3MacroCommand::virtual_hook( int id, void* data )
00443 { K3NamedCommand::virtual_hook( id, data ); }
00444 
00445 #include "k3command.moc"

KDE3Support

Skip menu "KDE3Support"
  • Main Page
  • Namespace List
  • Namespace Members
  • Alphabetical List
  • Class List
  • Class Hierarchy
  • Class Members
  • File List
  • File Members
  • Related Pages

kdelibs

Skip menu "kdelibs"
  • DNSSD
  • Interfaces
  •   KHexEdit
  •   KMediaPlayer
  •   KSpeech
  •   KTextEditor
  • kconf_update
  • KDE3Support
  •   KUnitTest
  • KDECore
  • KDED
  • KDEsu
  • KDEUI
  • KDEWebKit
  • KDocTools
  • KFile
  • KHTML
  • KImgIO
  • KInit
  • kio
  • KIOSlave
  • KJS
  •   KJS-API
  •   WTF
  • kjsembed
  • KNewStuff
  • KParts
  • KPty
  • Kross
  • KUnitConversion
  • KUtils
  • Nepomuk
  • Plasma
  • Solid
  • Sonnet
  • ThreadWeaver
Generated for kdelibs by doxygen 1.7.5
This website is maintained by Adriaan de Groot and Allen Winter.
KDE® and the K Desktop Environment® logo are registered trademarks of KDE e.V. | Legal