lib

KoCommandHistory.cpp
1/* This file is part of the KDE project
2 Copyright (C) 2000 Werner Trobin <trobin@kde.org>
3 Copyright (C) 2000 David Faure <faure@kde.org>
4
5 This library is free software; you can redistribute it and/or
6 modify it under the terms of the GNU Library General Public
7 License as published by the Free Software Foundation; either
8 version 2 of the License, or (at your option) any later version.
9
10 This library is distributed in the hope that it will be useful,
11 but WITHOUT ANY WARRANTY; without even the implied warranty of
12 MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
13 Library General Public License for more details.
14
15 You should have received a copy of the GNU Library General Public License
16 along with this library; see the file COPYING.LIB. If not, write to
17 the Free Software Foundation, Inc., 51 Franklin Street, Fifth Floor,
18 * Boston, MA 02110-1301, USA.
19*/
20
21#include <tqlabel.h>
22#include <tqlayout.h>
23#include <tqlistbox.h>
24
25#include <tdeaction.h>
26#include <kcommand.h>
27#include <kdebug.h>
28#include <tdelocale.h>
29#include <tdepopupmenu.h>
30#include <tdeshortcutlist.h>
31#include <tdestdaccel.h>
32#include <kstdaction.h>
33
34#include "KoCommandHistory.h"
35
36KoListBox::KoListBox( TQWidget *parent , const char *name , WFlags f)
37 : TQListBox( parent, name, f)
38{
39 setVScrollBarMode( AlwaysOn );
40}
41
42void KoListBox::contentsMouseMoveEvent ( TQMouseEvent * e)
43{
44 TQListBoxItem *item_p = itemAt( contentsToViewport(e->pos()));
45 if ( item_p )
46 {
47 int itemIndex = index( item_p );
48 for ( int i = 0; i<=itemIndex;i++)
49 setSelected ( i, true );
50 for ( int i = itemIndex+1; i<(int)count();i++)
51 setSelected ( i, false );
52 emit changeNumberOfSelectedItem( itemIndex);
53 }
54}
55
56TQSize KoListBox::sizeHint() const
57{
58 return TQSize(TQMIN(maxItemWidth() + verticalScrollBar()->width() + 4, 400),
59 TQMIN(count() * itemHeight() + horizontalScrollBar()->height() + 4,300));
60}
61
62class KoCommandHistory::KoCommandHistoryPrivate {
63public:
64 KoCommandHistoryPrivate() {
65 m_savedAt=-1;
66 m_present=0;
67 }
68 ~KoCommandHistoryPrivate() {}
69 int m_savedAt;
70 KCommand *m_present;
71 KoListBox *m_undoListBox;
72 KoListBox *m_redoListBox;
73 TQLabel *m_undoLabel;
74 TQLabel *m_redoLabel;
75};
76
78
80 m_undo(0), m_redo(0), m_undoLimit(50), m_redoLimit(30), m_first(false)
81{
82 d=new KoCommandHistoryPrivate();
83 m_commands.setAutoDelete(true);
84 clear();
85}
86
87KoCommandHistory::KoCommandHistory(TDEActionCollection * actionCollection, bool withMenus) :
88 m_undoLimit(50), m_redoLimit(30), m_first(false)
89{
90 d=new KoCommandHistoryPrivate();
91 if (withMenus)
92 {
93 TDEToolBarPopupAction * undo = new TDEToolBarPopupAction( i18n("&Undo"), "edit-undo",
94 TDEStdAccel::undo(), this, TQ_SLOT( undo() ),
95 actionCollection, /*KStdAction::stdName( KStdAction::Undo )*/"koffice_undo" );
96 connect( undo->popupMenu(), TQ_SIGNAL( aboutToShow() ), this, TQ_SLOT( slotUndoAboutToShow() ) );
97 connect( undo->popupMenu(), TQ_SIGNAL( activated( int ) ), this, TQ_SLOT( slotUndoActivated( int ) ) );
98 m_undo = undo;
99 m_undoPopup = undo->popupMenu();
100 d->m_undoListBox = new KoListBox( m_undoPopup );
101 d->m_undoListBox->setSelectionMode( TQListBox::Multi );
102
103 m_undoPopup->insertItem(d->m_undoListBox);
104 d->m_undoLabel = new TQLabel( m_undoPopup);
105 m_undoPopup->insertItem(d->m_undoLabel);
106
107 connect( d->m_undoListBox, TQ_SIGNAL( selected( int ) ), this, TQ_SLOT( slotUndoActivated( int ) ) );
108 connect( d->m_undoListBox, TQ_SIGNAL(clicked ( TQListBoxItem *)), this, TQ_SLOT( slotUndoActivated( TQListBoxItem * ) ) );
109
110 connect( d->m_undoListBox, TQ_SIGNAL( changeNumberOfSelectedItem( int )), this, TQ_SLOT( slotChangeUndoNumberOfSelectedItem( int )));
111
112 TDEToolBarPopupAction * redo = new TDEToolBarPopupAction( i18n("&Redo"), "edit-redo",
113 TDEStdAccel::redo(), this, TQ_SLOT( redo() ),
114 actionCollection, /*KStdAction::stdName( KStdAction::Redo )*/
115 "koffice_redo");
116 connect( redo->popupMenu(), TQ_SIGNAL( aboutToShow() ), this, TQ_SLOT( slotRedoAboutToShow() ) );
117 connect( redo->popupMenu(), TQ_SIGNAL( activated( int ) ), this, TQ_SLOT( slotRedoActivated( int ) ) );
118 m_redo = redo;
119 m_redoPopup = redo->popupMenu();
120 d->m_redoListBox = new KoListBox( m_redoPopup );
121 d->m_redoListBox->setSelectionMode( TQListBox::Multi );
122 m_redoPopup->insertItem(d->m_redoListBox);
123
124 d->m_redoLabel = new TQLabel( m_redoPopup);
125 m_redoPopup->insertItem(d->m_redoLabel);
126
127
128 connect( d->m_redoListBox, TQ_SIGNAL( selected( int ) ), this, TQ_SLOT( slotRedoActivated( int ) ) );
129 connect( d->m_redoListBox, TQ_SIGNAL(clicked ( TQListBoxItem *)), this, TQ_SLOT( slotRedoActivated( TQListBoxItem * ) ) );
130 connect( d->m_redoListBox, TQ_SIGNAL( changeNumberOfSelectedItem( int )), this, TQ_SLOT( slotChangeRedoNumberOfSelectedItem( int )));
131
132 }
133 else
134 {
135 m_undo = KStdAction::undo( this, TQ_SLOT( undo() ), actionCollection, "koffice_undo");
136 m_redo = KStdAction::redo( this, TQ_SLOT( redo() ), actionCollection, "koffice_redo");
137 m_undoPopup = 0L;
138 m_redoPopup = 0L;
139 d->m_redoListBox = 0L;
140 d->m_undoListBox = 0L;
141 d->m_redoLabel = 0L;
142 d->m_undoLabel = 0L;
143 }
144 m_commands.setAutoDelete(true);
145 clear();
146}
147
151
153{ return d->m_present;
154}
155
156
158 if (m_undo != 0) {
159 m_undo->setEnabled(false);
160 m_undo->setText(i18n("&Undo"));
161 }
162 if (m_redo != 0) {
163 m_redo->setEnabled(false);
164 m_redo->setText(i18n("&Redo"));
165 }
166 d->m_present = 0L;
167 d->m_savedAt=-42;
168}
169
170void KoCommandHistory::addCommand(KCommand *command, bool execute) {
171
172 if(command==0L)
173 return;
174
175 int index;
176 if(d->m_present!=0L && (index=m_commands.findRef(d->m_present))!=-1) {
177 if (m_first)
178 --index;
179 m_commands.insert(index+1, command);
180 // truncate history
181 unsigned int count=m_commands.count();
182 for(unsigned int i=index+2; i<count; ++i)
183 m_commands.removeLast();
184 // check whether we still can reach savedAt
185 if(index<d->m_savedAt)
186 d->m_savedAt=-1;
187 d->m_present=command;
188 m_first=false;
189 if (m_undo != 0) {
190 m_undo->setEnabled(true);
191 m_undo->setText(i18n("&Undo: %1").arg(d->m_present->name()));
192 }
193 if((m_redo != 0) && m_redo->isEnabled()) {
194 m_redo->setEnabled(false);
195 m_redo->setText(i18n("&Redo"));
196 }
197 clipCommands();
198 }
199 else { // either this is the first time we add a Command or something has gone wrong
200 kdDebug(230) << "KoCommandHistory: Initializing the Command History" << endl;
201 m_commands.clear();
202 m_commands.append(command);
203 d->m_present=command;
204 if (m_undo != 0) {
205 m_undo->setEnabled(true);
206 m_undo->setText(i18n("&Undo: %1").arg(d->m_present->name()));
207 }
208 if (m_redo != 0) {
209 m_redo->setEnabled(false);
210 m_redo->setText(i18n("&Redo"));
211 }
212 m_first=false; // Michael B: yes, that *is* correct :-)
213 }
214 if ( execute )
215 {
216 command->execute();
217 emit commandExecuted();
218 emit commandExecuted(command);
219 }
220}
221
223
224 if (m_first || (d->m_present == 0L))
225 return;
226
227 d->m_present->unexecute();
228 KCommand *commandUndone = d->m_present;
229
230 if (m_redo != 0) {
231 m_redo->setEnabled(true);
232 m_redo->setText(i18n("&Redo: %1").arg(d->m_present->name()));
233 }
234 int index;
235 if((index=m_commands.findRef(d->m_present))!=-1 && m_commands.prev()!=0) {
236 d->m_present=m_commands.current();
237 emit commandExecuted();
238 emit commandExecuted(commandUndone);
239 if (m_undo != 0) {
240 m_undo->setEnabled(true);
241 m_undo->setText(i18n("&Undo: %1").arg(d->m_present->name()));
242 }
243 --index;
244 if(index==d->m_savedAt)
245 emit documentRestored();
246 }
247 else {
248 emit commandExecuted();
249 emit commandExecuted(commandUndone);
250 if (m_undo != 0) {
251 m_undo->setEnabled(false);
252 m_undo->setText(i18n("&Undo"));
253 }
254 if(d->m_savedAt==-42)
255 emit documentRestored();
256 m_first=true;
257 }
258 clipCommands(); // only needed here and in addCommand, NOT in redo
259}
260
262
263 int index;
264 if(m_first) {
265 d->m_present->execute();
266 emit commandExecuted();
267 emit commandExecuted(d->m_present);
268 m_first=false;
269 m_commands.first();
270 if(d->m_savedAt==0)
271 emit documentRestored();
272 }
273 else if((index=m_commands.findRef(d->m_present))!=-1 && m_commands.next()!=0) {
274 d->m_present=m_commands.current();
275 d->m_present->execute();
276 emit commandExecuted();
277 emit commandExecuted(d->m_present);
278 ++index;
279 if(index==d->m_savedAt)
280 emit documentRestored();
281 }
282
283 if (m_undo != 0) {
284 m_undo->setEnabled(true);
285 m_undo->setText(i18n("&Undo: %1").arg(d->m_present->name()));
286 }
287
288 if(m_commands.next()!=0) {
289 if (m_redo != 0) {
290 m_redo->setEnabled(true);
291 m_redo->setText(i18n("&Redo: %1").arg(m_commands.current()->name()));
292 }
293 }
294 else {
295 if((m_redo != 0) && m_redo->isEnabled()) {
296 m_redo->setEnabled(false);
297 m_redo->setText(i18n("&Redo"));
298 }
299 }
300}
301
303 if(d->m_present!=0 && !m_first)
304 d->m_savedAt=m_commands.findRef(d->m_present);
305 else if(d->m_present==0 && !m_first)
306 d->m_savedAt=-42; // this value signals that the document has
307 // been saved with an empty history.
308 else if(m_first)
309 d->m_savedAt=-42;
310}
311
313
314 if(limit>0 && limit!=m_undoLimit) {
315 m_undoLimit=limit;
316 clipCommands();
317 }
318}
319
321
322 if(limit>0 && limit!=m_redoLimit) {
323 m_redoLimit=limit;
324 clipCommands();
325 }
326}
327
328void KoCommandHistory::clipCommands() {
329
330 int count=m_commands.count();
331 if(count<=m_undoLimit && count<=m_redoLimit)
332 return;
333
334 int index=m_commands.findRef(d->m_present);
335 if(index>=m_undoLimit) {
336 for(int i=0; i<=(index-m_undoLimit); ++i) {
337 m_commands.removeFirst();
338 --d->m_savedAt;
339 if(d->m_savedAt==-1)
340 d->m_savedAt=-42;
341 }
342 index=m_commands.findRef(d->m_present); // calculate the new
343 count=m_commands.count(); // values (for the redo-branch :)
344 // make it easier for us... d->m_savedAt==-1 -> invalid
345 if(d->m_savedAt!=-42 && d->m_savedAt<-1)
346 d->m_savedAt=-1;
347 }
348 // adjust the index if it's the first command
349 if(m_first)
350 index=-1;
351 if((index+m_redoLimit+1)<count) {
352 if(d->m_savedAt>(index+m_redoLimit))
353 d->m_savedAt=-1;
354 for(int i=0; i<(count-(index+m_redoLimit+1)); ++i)
355 m_commands.removeLast();
356 }
357}
358
359void KoCommandHistory::slotUndoAboutToShow()
360{
361 d->m_undoListBox->clear();
362 slotChangeUndoNumberOfSelectedItem( -1 );
363 int i = 0;
364 TQStringList lst;
365 if (m_commands.findRef(d->m_present)!=-1)
366 while ( m_commands.current() && i<10 ) // TODO make number of items configurable ?
367 {
368 lst.append(i18n("Undo: %1").arg(m_commands.current()->name()));
369 m_commands.prev();
370 }
371 d->m_undoListBox->insertStringList( lst );
372}
373
374void KoCommandHistory::slotUndoActivated( int pos )
375{
376 kdDebug(230) << "KoCommandHistory::slotUndoActivated " << pos << endl;
377 for ( int i = 0 ; i < pos+1; ++i )
378 undo();
379 m_undoPopup->hide();
380}
381
382void KoCommandHistory::slotUndoActivated( TQListBoxItem * item)
383{
384 if ( item )
385 slotUndoActivated( item->listBox()->index(item));
386}
387
388void KoCommandHistory::slotRedoActivated( TQListBoxItem * item)
389{
390 if ( item )
391 slotRedoActivated( item->listBox()->index(item));
392}
393
394void KoCommandHistory::slotChangeUndoNumberOfSelectedItem( int pos)
395{
396 d->m_undoLabel->setText( i18n("Undo %n action", "Undo %n actions", pos+1) );
397}
398
399void KoCommandHistory::slotChangeRedoNumberOfSelectedItem( int pos)
400{
401 d->m_redoLabel->setText( i18n("Redo %n action", "Redo %n actions", pos+1) );
402}
403
404
405void KoCommandHistory::slotRedoAboutToShow()
406{
407 d->m_redoListBox->clear();
408 slotChangeRedoNumberOfSelectedItem( -1 );
409 TQStringList lst;
410 int i = 0;
411 if (m_first)
412 {
413 d->m_present = m_commands.first();
414 lst.append(i18n("Redo: %1").arg(d->m_present->name()));
415 }
416 if (m_commands.findRef(d->m_present)!=-1 && m_commands.next())
417 while ( m_commands.current() && i<10 ) // TODO make number of items configurable ?
418 {
419 lst.append(i18n("Redo: %1").arg(m_commands.current()->name()));
420 m_commands.next();
421 }
422 d->m_redoListBox->insertStringList( lst );
423
424}
425
426void KoCommandHistory::slotRedoActivated( int pos )
427{
428 kdDebug(230) << "KoCommandHistory::slotRedoActivated " << pos << endl;
429 for ( int i = 0 ; i < pos+1; ++i )
430 redo();
431 m_redoPopup->hide();
432}
433
435{
436 if ( m_undo && m_redo )
437 {
438 m_undo->setEnabled( !m_first && ( d->m_present != 0L ) );
439 m_redo->setEnabled(m_first || (m_commands.findRef(d->m_present)!=-1 && m_commands.next()!=0));
440 }
441}
442
443void KoCommandHistory::virtual_hook( int, void* )
444{ /*BASE::virtual_hook( id, data );*/ }
445
446#include "KoCommandHistory.moc"
virtual void unexecute()=0
virtual void execute()=0
virtual void documentSaved()
Remembers when you saved the document.
void commandExecuted()
Emitted every time a command is executed (whether by addCommand, undo or redo).
void addCommand(KCommand *command, bool execute=true)
Adds a command to the history.
virtual void redo()
Redoes the last undone action.
void setRedoLimit(int limit)
Sets the maximum number of items in the redo history.
virtual void undo()
Undoes the last action.
void clear()
Erases all the undo/redo history.
void updateActions()
Enable or disable the undo and redo actions.
void documentRestored()
Emitted every time we reach the index where you saved the document for the last time.
KoCommandHistory()
Creates a command history, to store commands.
void setUndoLimit(int limit)
Sets the maximum number of items in the undo history.
virtual ~KoCommandHistory()
Destructs the command history object.
KCommand * presentCommand()
TDEAction * undo(const TQObject *recvr, const char *slot, TDEActionCollection *parent, const char *name=0)
TDEAction * redo(const TQObject *recvr, const char *slot, TDEActionCollection *parent, const char *name=0)
const char * name(StdAction id)