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

KDE3Support

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

kdelibs

Skip menu "kdelibs"
  • DNSSD
  • Interfaces
  •   KHexEdit
  •   KMediaPlayer
  •   KSpeech
  •   KTextEditor
  • Kate
  • kconf_update
  • KDE3Support
  •   KUnitTest
  • KDECore
  • KDED
  • KDEsu
  • KDEUI
  • KDocTools
  • KFile
  • KHTML
  • KImgIO
  • KInit
  • kio
  • KIOSlave
  • KJS
  •   KJS-API
  •   WTF
  • kjsembed
  • KNewStuff
  • KParts
  • Kross
  • KUtils
  • Nepomuk
  • Plasma
  • Solid
  • Sonnet
  • ThreadWeaver
Generated for kdelibs by doxygen 1.5.7
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