00001
00002
00003
00004
00005
00006
00007
00008
00009
00010
00011
00012
00013
00014
00015
00016
00017
00018
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
00132
00133
00134
00135
00136
00137
00138
00139
00140
00141
00142
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
00160
00161
00162
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
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
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();
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();
00281 if ( d->m_savedAt < 0 )
00282 d->m_savedAt = -1;
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
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(),
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
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 { }
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"