update
[kdegraphics.git] / kolourpaint / commands / kpCommandHistoryBase.cpp
blobd0d3d0c4bc074cf93100f507f6873850e937d56e
2 /*
3 Copyright (c) 2003-2007 Clarence Dang <dang@kde.org>
4 All rights reserved.
6 Redistribution and use in source and binary forms, with or without
7 modification, are permitted provided that the following conditions
8 are met:
10 1. Redistributions of source code must retain the above copyright
11 notice, this list of conditions and the following disclaimer.
12 2. Redistributions in binary form must reproduce the above copyright
13 notice, this list of conditions and the following disclaimer in the
14 documentation and/or other materials provided with the distribution.
16 THIS SOFTWARE IS PROVIDED BY THE AUTHOR ``AS IS'' AND ANY EXPRESS OR
17 IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES
18 OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED.
19 IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR ANY DIRECT, INDIRECT,
20 INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT
21 NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
22 DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
23 THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
24 (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF
25 THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
29 #define DEBUG_KP_COMMAND_HISTORY 0
32 #include <kpCommandHistoryBase.h>
34 #include <climits>
36 #include <qdatetime.h>
37 #include <qlinkedlist.h>
39 #include <kapplication.h>
40 #include <kconfig.h>
41 #include <kdebug.h>
42 #include <kglobal.h>
43 #include <klocale.h>
44 #include <kmenu.h>
45 #include <kstandardshortcut.h>
46 #include <kstandardaction.h>
47 #include <ktoolbarpopupaction.h>
48 #include <kactioncollection.h>
49 #include <kconfiggroup.h>
51 #include <kpCommand.h>
52 #include <kpCommandEnvironment.h>
53 #include <kpDefs.h>
54 #include <kpDocument.h>
55 #include <kpMainWindow.h>
56 #include <kpTool.h>
59 //template <typename T>
60 static void ClearPointerList (QLinkedList <kpCommand *> *listPtr)
62 if (!listPtr)
63 return;
65 qDeleteAll (listPtr->begin (), listPtr->end ());
67 listPtr->clear ();
71 struct kpCommandHistoryBasePrivate
76 kpCommandHistoryBase::kpCommandHistoryBase (bool doReadConfig,
77 KActionCollection *ac)
78 : d (new kpCommandHistoryBasePrivate ())
80 m_actionUndo = new KToolBarPopupAction (KIcon ("edit-undo"), undoActionText (), this);
81 ac->addAction (KStandardAction::name (KStandardAction::Undo), m_actionUndo);
82 m_actionUndo->setShortcuts (KStandardShortcut::shortcut (KStandardShortcut::Undo));
83 connect (m_actionUndo, SIGNAL(triggered(bool)), this, SLOT (undo ()));
85 m_actionRedo = new KToolBarPopupAction (KIcon ("edit-redo"), redoActionText (), this);
86 ac->addAction (KStandardAction::name (KStandardAction::Redo), m_actionRedo);
87 m_actionRedo->setShortcuts (KStandardShortcut::shortcut (KStandardShortcut::Redo));
88 connect (m_actionRedo, SIGNAL(triggered(bool)), this, SLOT (redo ()));
91 m_actionUndo->setEnabled (false);
92 m_actionRedo->setEnabled (false);
95 connect (m_actionUndo->menu (), SIGNAL (triggered (QAction *)),
96 this, SLOT (undoUpToNumber (QAction *)));
97 connect (m_actionRedo->menu (), SIGNAL (triggered (QAction *)),
98 this, SLOT (redoUpToNumber (QAction *)));
101 m_undoMinLimit = 10;
102 m_undoMaxLimit = 500;
103 m_undoMaxLimitSizeLimit = 16 * 1048576;
106 m_documentRestoredPosition = 0;
109 if (doReadConfig)
110 readConfig ();
113 kpCommandHistoryBase::~kpCommandHistoryBase ()
115 ::ClearPointerList (&m_undoCommandList);
116 ::ClearPointerList (&m_redoCommandList);
118 delete d;
122 // public
123 int kpCommandHistoryBase::undoLimit () const
125 return undoMinLimit ();
128 // public
129 void kpCommandHistoryBase::setUndoLimit (int limit)
131 setUndoMinLimit (limit);
135 // public
136 int kpCommandHistoryBase::undoMinLimit () const
138 return m_undoMinLimit;
141 // public
142 void kpCommandHistoryBase::setUndoMinLimit (int limit)
144 #if DEBUG_KP_COMMAND_HISTORY
145 kDebug () << "kpCommandHistoryBase::setUndoMinLimit("
146 << limit << ")"
147 << endl;
148 #endif
150 if (limit < 1 || limit > 5000/*"ought to be enough for anybody"*/)
152 kError () << "kpCommandHistoryBase::setUndoMinLimit("
153 << limit << ")"
154 << endl;
155 return;
158 if (limit == m_undoMinLimit)
159 return;
161 m_undoMinLimit = limit;
162 trimCommandListsUpdateActions ();
166 // public
167 int kpCommandHistoryBase::undoMaxLimit () const
169 return m_undoMaxLimit;
172 // public
173 void kpCommandHistoryBase::setUndoMaxLimit (int limit)
175 #if DEBUG_KP_COMMAND_HISTORY
176 kDebug () << "kpCommandHistoryBase::setUndoMaxLimit("
177 << limit << ")"
178 << endl;
179 #endif
181 if (limit < 1 || limit > 5000/*"ought to be enough for anybody"*/)
183 kError () << "kpCommandHistoryBase::setUndoMaxLimit("
184 << limit << ")"
185 << endl;
186 return;
189 if (limit == m_undoMaxLimit)
190 return;
192 m_undoMaxLimit = limit;
193 trimCommandListsUpdateActions ();
197 // public
198 kpCommandSize::SizeType kpCommandHistoryBase::undoMaxLimitSizeLimit () const
200 return m_undoMaxLimitSizeLimit;
203 // public
204 void kpCommandHistoryBase::setUndoMaxLimitSizeLimit (kpCommandSize::SizeType sizeLimit)
206 #if DEBUG_KP_COMMAND_HISTORY
207 kDebug () << "kpCommandHistoryBase::setUndoMaxLimitSizeLimit("
208 << sizeLimit << ")"
209 << endl;
210 #endif
212 if (sizeLimit < 0 ||
213 sizeLimit > (500 * 1048576)/*"ought to be enough for anybody"*/)
215 kError () << "kpCommandHistoryBase::setUndoMaxLimitSizeLimit("
216 << sizeLimit << ")"
217 << endl;
218 return;
221 if (sizeLimit == m_undoMaxLimitSizeLimit)
222 return;
224 m_undoMaxLimitSizeLimit = sizeLimit;
225 trimCommandListsUpdateActions ();
229 // public
230 void kpCommandHistoryBase::readConfig ()
232 #if DEBUG_KP_COMMAND_HISTORY
233 kDebug () << "kpCommandHistoryBase::readConfig()";
234 #endif
235 KConfigGroup cfg (KGlobal::config (), kpSettingsGroupUndoRedo);
237 setUndoMinLimit (cfg.readEntry (kpSettingUndoMinLimit, undoMinLimit ()));
238 setUndoMaxLimit (cfg.readEntry (kpSettingUndoMaxLimit, undoMaxLimit ()));
239 setUndoMaxLimitSizeLimit (
240 cfg.readEntry <kpCommandSize::SizeType> (kpSettingUndoMaxLimitSizeLimit,
241 undoMaxLimitSizeLimit ()));
243 trimCommandListsUpdateActions ();
246 // public
247 void kpCommandHistoryBase::writeConfig ()
249 #if DEBUG_KP_COMMAND_HISTORY
250 kDebug () << "kpCommandHistoryBase::writeConfig()";
251 #endif
252 KConfigGroup cfg (KGlobal::config (), kpSettingsGroupUndoRedo);
254 cfg.writeEntry (kpSettingUndoMinLimit, undoMinLimit ());
255 cfg.writeEntry (kpSettingUndoMaxLimit, undoMaxLimit ());
256 cfg.writeEntry <kpCommandSize::SizeType> (
257 kpSettingUndoMaxLimitSizeLimit, undoMaxLimitSizeLimit ());
259 cfg.sync ();
263 // public
264 void kpCommandHistoryBase::addCommand (kpCommand *command, bool execute)
266 #if DEBUG_KP_COMMAND_HISTORY
267 kDebug () << "kpCommandHistoryBase::addCommand("
268 << command
269 << ",execute=" << execute << ")"
270 << endl;
271 #endif
273 if (execute)
274 command->execute ();
276 m_undoCommandList.push_front (command);
277 ::ClearPointerList (&m_redoCommandList);
279 #if DEBUG_KP_COMMAND_HISTORY
280 kDebug () << "\tdocumentRestoredPosition=" << m_documentRestoredPosition
281 << endl;
282 #endif
283 if (m_documentRestoredPosition != INT_MAX)
285 if (m_documentRestoredPosition > 0)
286 m_documentRestoredPosition = INT_MAX;
287 else
288 m_documentRestoredPosition--;
289 #if DEBUG_KP_COMMAND_HISTORY
290 kDebug () << "\t\tdocumentRestoredPosition=" << m_documentRestoredPosition
291 << endl;
292 #endif
295 trimCommandListsUpdateActions ();
298 // public
299 void kpCommandHistoryBase::clear ()
301 #if DEBUG_KP_COMMAND_HISTORY
302 kDebug () << "kpCommandHistoryBase::clear()";
303 #endif
305 ::ClearPointerList (&m_undoCommandList);
306 ::ClearPointerList (&m_redoCommandList);
308 m_documentRestoredPosition = 0;
310 updateActions ();
314 // protected slot
315 void kpCommandHistoryBase::undoInternal ()
317 #if DEBUG_KP_COMMAND_HISTORY
318 kDebug () << "kpCommandHistoryBase::undoInternal()";
319 #endif
321 kpCommand *undoCommand = nextUndoCommand ();
322 if (!undoCommand)
323 return;
325 undoCommand->unexecute ();
328 m_undoCommandList.erase (m_undoCommandList.begin ());
329 m_redoCommandList.push_front (undoCommand);
332 #if DEBUG_KP_COMMAND_HISTORY
333 kDebug () << "\tdocumentRestoredPosition=" << m_documentRestoredPosition
334 << endl;
335 #endif
336 if (m_documentRestoredPosition != INT_MAX)
338 m_documentRestoredPosition++;
339 if (m_documentRestoredPosition == 0)
340 emit documentRestored ();
341 #if DEBUG_KP_COMMAND_HISTORY
342 kDebug () << "\t\tdocumentRestoredPosition=" << m_documentRestoredPosition
343 << endl;
344 #endif
348 // protected slot
349 void kpCommandHistoryBase::redoInternal ()
351 #if DEBUG_KP_COMMAND_HISTORY
352 kDebug () << "kpCommandHistoryBase::redoInternal()";
353 #endif
355 kpCommand *redoCommand = nextRedoCommand ();
356 if (!redoCommand)
357 return;
359 redoCommand->execute ();
362 m_redoCommandList.erase (m_redoCommandList.begin ());
363 m_undoCommandList.push_front (redoCommand);
366 #if DEBUG_KP_COMMAND_HISTORY
367 kDebug () << "\tdocumentRestoredPosition=" << m_documentRestoredPosition
368 << endl;
369 #endif
370 if (m_documentRestoredPosition != INT_MAX)
372 m_documentRestoredPosition--;
373 if (m_documentRestoredPosition == 0)
374 emit documentRestored ();
375 #if DEBUG_KP_COMMAND_HISTORY
376 kDebug () << "\t\tdocumentRestoredPosition=" << m_documentRestoredPosition
377 << endl;
378 #endif
383 // public slot virtual
384 void kpCommandHistoryBase::undo ()
386 #if DEBUG_KP_COMMAND_HISTORY
387 kDebug () << "kpCommandHistoryBase::undo()";
388 #endif
390 undoInternal ();
391 trimCommandListsUpdateActions ();
394 // public slot virtual
395 void kpCommandHistoryBase::redo ()
397 #if DEBUG_KP_COMMAND_HISTORY
398 kDebug () << "kpCommandHistoryBase::redo()";
399 #endif
401 redoInternal ();
402 trimCommandListsUpdateActions ();
406 // public slot virtual
407 void kpCommandHistoryBase::undoUpToNumber (QAction *which)
409 #if DEBUG_KP_COMMAND_HISTORY
410 kDebug () << "kpCommandHistoryBase::undoUpToNumber(" << which << ")";
411 #endif
413 for (int i = 0;
414 i <= which->data().toInt() && !m_undoCommandList.isEmpty ();
415 i++)
417 undoInternal ();
420 trimCommandListsUpdateActions ();
423 // public slot virtual
424 void kpCommandHistoryBase::redoUpToNumber (QAction *which)
426 #if DEBUG_KP_COMMAND_HISTORY
427 kDebug () << "kpCommandHistoryBase::redoUpToNumber(" << which << ")";
428 #endif
430 for (int i = 0;
431 i <= which->data().toInt() && !m_redoCommandList.isEmpty ();
432 i++)
434 redoInternal ();
437 trimCommandListsUpdateActions ();
441 // protected
442 QString kpCommandHistoryBase::undoActionText () const
444 kpCommand *undoCommand = nextUndoCommand ();
446 if (undoCommand)
447 return i18n ("&Undo: %1", undoCommand->name ());
448 else
449 return i18n ("&Undo");
452 // protected
453 QString kpCommandHistoryBase::redoActionText () const
455 kpCommand *redoCommand = nextRedoCommand ();
457 if (redoCommand)
458 return i18n ("&Redo: %1", redoCommand->name ());
459 else
460 return i18n ("&Redo");
464 // protected
465 QString kpCommandHistoryBase::undoActionToolTip () const
467 kpCommand *undoCommand = nextUndoCommand ();
469 if (undoCommand)
470 return i18n ("Undo: %1", undoCommand->name ());
471 else
472 return i18n ("Undo");
475 // protected
476 QString kpCommandHistoryBase::redoActionToolTip () const
478 kpCommand *redoCommand = nextRedoCommand ();
480 if (redoCommand)
481 return i18n ("Redo: %1", redoCommand->name ());
482 else
483 return i18n ("Redo");
487 // protected
488 void kpCommandHistoryBase::trimCommandListsUpdateActions ()
490 #if DEBUG_KP_COMMAND_HISTORY
491 kDebug () << "kpCommandHistoryBase::trimCommandListsUpdateActions()";
492 #endif
494 trimCommandLists ();
495 updateActions ();
498 // protected
499 void kpCommandHistoryBase::trimCommandList (QLinkedList <kpCommand *> *commandList)
501 #if DEBUG_KP_COMMAND_HISTORY
502 kDebug () << "kpCommandHistoryBase::trimCommandList()";
503 QTime timer; timer.start ();
504 #endif
506 if (!commandList)
508 kError () << "kpCommandHistoryBase::trimCommandList() passed 0 commandList"
509 << endl;
510 return;
514 #if DEBUG_KP_COMMAND_HISTORY
515 kDebug () << "\tsize=" << commandList->size ()
516 << " undoMinLimit=" << m_undoMinLimit
517 << " undoMaxLimit=" << m_undoMaxLimit
518 << " undoMaxLimitSizeLimit=" << m_undoMaxLimitSizeLimit
519 << endl;
520 #endif
521 if ((int) commandList->size () <= m_undoMinLimit)
523 #if DEBUG_KP_COMMAND_HISTORY
524 kDebug () << "\t\tsize under undoMinLimit - done";
525 #endif
526 return;
530 #if DEBUG_KP_COMMAND_HISTORY && 0
531 kDebug () << "\tsize over undoMinLimit - iterating thru cmds:";
532 #endif
534 QLinkedList <kpCommand *>::iterator it = commandList->begin ();
535 int upto = 0;
537 kpCommandSize::SizeType sizeSoFar = 0;
539 while (it != commandList->end ())
541 bool advanceIt = true;
543 if (sizeSoFar <= m_undoMaxLimitSizeLimit)
545 sizeSoFar += (*it)->size ();
548 #if DEBUG_KP_COMMAND_HISTORY && 0
549 kDebug () << "\t\t" << upto << ":"
550 << " name='" << (*it)->name ()
551 << "' size=" << (*it)->size ()
552 << " sizeSoFar=" << sizeSoFar
553 << endl;
554 #endif
556 if (upto >= m_undoMinLimit)
558 if (upto >= m_undoMaxLimit ||
559 sizeSoFar > m_undoMaxLimitSizeLimit)
561 #if DEBUG_KP_COMMAND_HISTORY && 0
562 kDebug () << "\t\t\tkill";
563 #endif
564 delete (*it);
565 it = m_undoCommandList.erase (it);
566 advanceIt = false;
570 if (advanceIt)
571 it++;
572 upto++;
575 #if DEBUG_KP_COMMAND_HISTORY
576 kDebug () << "\ttook " << timer.elapsed () << "ms";
577 #endif
580 // protected
581 void kpCommandHistoryBase::trimCommandLists ()
583 #if DEBUG_KP_COMMAND_HISTORY
584 kDebug () << "kpCommandHistoryBase::trimCommandLists()";
585 #endif
587 trimCommandList (&m_undoCommandList);
588 trimCommandList (&m_redoCommandList);
590 #if DEBUG_KP_COMMAND_HISTORY
591 kDebug () << "\tdocumentRestoredPosition=" << m_documentRestoredPosition
592 << endl;
593 #endif
594 if (m_documentRestoredPosition != INT_MAX)
596 #if DEBUG_KP_COMMAND_HISTORY
597 kDebug () << "\t\tundoCmdList.size=" << m_undoCommandList.size ()
598 << " redoCmdList.size=" << m_redoCommandList.size ()
599 << endl;
600 #endif
601 if (m_documentRestoredPosition > (int) m_redoCommandList.size () ||
602 -m_documentRestoredPosition > (int) m_undoCommandList.size ())
604 #if DEBUG_KP_COMMAND_HISTORY
605 kDebug () << "\t\t\tinvalidate documentRestoredPosition";
606 #endif
607 m_documentRestoredPosition = INT_MAX;
613 static void populatePopupMenu (KMenu *popupMenu,
614 const QString &undoOrRedo,
615 const QLinkedList <kpCommand *> &commandList)
617 if (!popupMenu)
618 return;
620 popupMenu->clear ();
622 QLinkedList <kpCommand *>::const_iterator it = commandList.begin ();
623 int i = 0;
624 while (i < 10 && it != commandList.end ())
626 QAction *action = new QAction(i18n ("%1: %2", undoOrRedo, (*it)->name ()), popupMenu);
627 action->setData(i);
628 popupMenu->addAction (action);
629 i++, it++;
632 if (it != commandList.end ())
634 // TODO: maybe have a scrollview show all the items instead, like KOffice in KDE 3
635 // LOCOMPAT: should be centered text.
636 popupMenu->addTitle (i18np ("%1 more item", "%1 more items",
637 commandList.size () - i));
642 // protected
643 void kpCommandHistoryBase::updateActions ()
645 #if DEBUG_KP_COMMAND_HISTORY
646 kDebug () << "kpCommandHistoryBase::updateActions()";
647 #endif
649 m_actionUndo->setEnabled ((bool) nextUndoCommand ());
650 // Don't want to keep changing toolbar text.
651 // TODO: As a bad side-effect, the menu doesn't have "Undo: <action>"
652 // anymore. In any case, the KDE4 KToolBarPopupAction
653 // sucks in menus as it forces the clicking of a submenu. IMO,
654 // there should be no submenu in the menu.
655 //m_actionUndo->setText (undoActionText ());
657 // But in icon mode, a tooltip with context is useful.
658 m_actionUndo->setToolTip (undoActionToolTip ());
659 #if DEBUG_KP_COMMAND_HISTORY
660 QTime timer; timer.start ();
661 #endif
662 populatePopupMenu (qobject_cast<KMenu*> (m_actionUndo->menu ()),
663 i18n ("Undo"),
664 m_undoCommandList);
665 #if DEBUG_KP_COMMAND_HISTORY
666 kDebug () << "\tpopuplatePopupMenu undo=" << timer.elapsed ()
667 << "ms" << endl;;
668 #endif
670 m_actionRedo->setEnabled ((bool) nextRedoCommand ());
671 // Don't want to keep changing toolbar text.
672 // TODO: As a bad side-effect, the menu doesn't have "Undo: <action>"
673 // anymore. In any case, the KDE4 KToolBarPopupAction
674 // sucks in menus as it forces the clicking of a submenu. IMO,
675 // there should be no submenu in the menu.
676 //m_actionRedo->setText (redoActionText ());
678 // But in icon mode, a tooltip with context is useful.
679 m_actionRedo->setToolTip (redoActionToolTip ());
680 #if DEBUG_KP_COMMAND_HISTORY
681 timer.restart ();
682 #endif
683 populatePopupMenu (qobject_cast<KMenu*> (m_actionRedo->menu ()),
684 i18n ("Redo"),
685 m_redoCommandList);
686 #if DEBUG_KP_COMMAND_HISTORY
687 kDebug () << "\tpopuplatePopupMenu redo=" << timer.elapsed ()
688 << "ms" << endl;
689 #endif
693 // public
694 kpCommand *kpCommandHistoryBase::nextUndoCommand () const
696 if (m_undoCommandList.isEmpty ())
697 return 0;
699 return m_undoCommandList.first ();
702 // public
703 kpCommand *kpCommandHistoryBase::nextRedoCommand () const
705 if (m_redoCommandList.isEmpty ())
706 return 0;
708 return m_redoCommandList.first ();
712 // public
713 void kpCommandHistoryBase::setNextUndoCommand (kpCommand *command)
715 #if DEBUG_KP_COMMAND_HISTORY
716 kDebug () << "kpCommandHistoryBase::setNextUndoCommand("
717 << command
718 << ")"
719 << endl;
720 #endif
722 if (m_undoCommandList.isEmpty ())
723 return;
726 delete *m_undoCommandList.begin ();
727 *m_undoCommandList.begin () = command;
730 trimCommandListsUpdateActions ();
734 // public slot virtual
735 void kpCommandHistoryBase::documentSaved ()
737 #if DEBUG_KP_COMMAND_HISTORY
738 kDebug () << "kpCommandHistoryBase::documentSaved()";
739 #endif
741 m_documentRestoredPosition = 0;
745 #include <kpCommandHistoryBase.moc>