• Skip to content
  • Skip to link menu
KDE 4.3 API Reference
  • KDE API Reference
  • kdelibs
  • Sitemap
  • Contact Us
 

Kate

kateundomanager.cpp

Go to the documentation of this file.
00001 /* This file is part of the KDE libraries
00002    Copyright (C) 2009 Bernhard Beschow <bbeschow@cs.tu-berlin.de>
00003 
00004    This library is free software; you can redistribute it and/or
00005    modify it under the terms of the GNU Library General Public
00006    License version 2 as published by the Free Software Foundation.
00007 
00008    This library is distributed in the hope that it will be useful,
00009    but WITHOUT ANY WARRANTY; without even the implied warranty of
00010    MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
00011    Library General Public License for more details.
00012 
00013    You should have received a copy of the GNU Library General Public License
00014    along with this library; see the file COPYING.LIB.  If not, write to
00015    the Free Software Foundation, Inc., 51 Franklin Street, Fifth Floor,
00016    Boston, MA 02110-1301, USA.
00017 */
00018 #include "kateundomanager.h"
00019 
00020 #include "katedocument.h"
00021 #include "kateview.h"
00022 #include "katesearchbar.h"
00023 #include "kateundo.h"
00024 
00025 KateUndoManager::KateUndoManager (KateDocument *doc)
00026   : QObject (doc)
00027   , m_document (doc)
00028   , m_undoComplexMerge (false)
00029   , m_editCurrentUndo (0)
00030   , m_undoDontMerge (false)
00031   , lastUndoGroupWhenSaved(0)
00032   , lastRedoGroupWhenSaved(0)
00033   , docWasSavedWhenUndoWasEmpty(true)
00034   , docWasSavedWhenRedoWasEmpty(true)
00035 {
00036   connect(this, SIGNAL(undoChanged()), m_document, SIGNAL(undoChanged()));
00037   connect(doc, SIGNAL(viewCreated(KTextEditor::Document*, KTextEditor::View*)), SLOT(viewCreated(KTextEditor::Document*, KTextEditor::View*)));
00038 }
00039 
00040 KateUndoManager::~KateUndoManager()
00041 {
00042   delete m_editCurrentUndo;
00043 
00044   // cleanup the undo/redo items, very important, truee :/
00045   qDeleteAll(undoItems);
00046   undoItems.clear();
00047   qDeleteAll(redoItems);
00048   redoItems.clear();
00049 }
00050 
00051 void KateUndoManager::viewCreated (KTextEditor::Document *, KTextEditor::View *newView)
00052 {
00053   connect(newView, SIGNAL(cursorPositionChanged(KTextEditor::View*, const KTextEditor::Cursor&)), SLOT(undoCancel()));
00054 }
00055 
00056 void KateUndoManager::editStart()
00057 {
00058   // editStart() and editEnd() must be called in alternating fashion
00059   Q_ASSERT(m_editCurrentUndo == 0); // make sure to enter a clean state
00060 
00061   // new current undo item
00062   m_editCurrentUndo = new KateUndoGroup(m_document);
00063 
00064   Q_ASSERT(m_editCurrentUndo != 0); // a new undo group must be created by this method
00065 }
00066 
00067 void KateUndoManager::editEnd()
00068 {
00069   // editStart() and editEnd() must be called in alternating fashion
00070   Q_ASSERT(m_editCurrentUndo != 0); // an undo group must have been created by editStart()
00071 
00072     bool changedUndo = false;
00073 
00074     m_editCurrentUndo->editEnd();
00075 
00076     if (m_editCurrentUndo->isEmpty()) {
00077       delete m_editCurrentUndo;
00078     } else if (!m_undoDontMerge
00079         && !undoItems.isEmpty()
00080         && undoItems.last()->merge(m_editCurrentUndo, m_undoComplexMerge)) {
00081       delete m_editCurrentUndo;
00082     } else {
00083       undoItems.append(m_editCurrentUndo);
00084       changedUndo = true;
00085     }
00086 
00087     m_undoDontMerge = false;
00088 
00089     m_editCurrentUndo = 0L;
00090 
00091     if (changedUndo)
00092       emit undoChanged();
00093 
00094   Q_ASSERT(m_editCurrentUndo == 0); // must be 0 after calling this method
00095 }
00096 
00097 void KateUndoManager::slotTextInserted(int line, int col, const QString &s)
00098 {
00099   if (m_editCurrentUndo != 0) // do we care about notifications?
00100     addUndoItem(new KateEditInsertTextUndo(m_document, line, col, s));
00101 }
00102 
00103 void KateUndoManager::slotTextRemoved(int line, int col, const QString &s)
00104 {
00105   if (m_editCurrentUndo != 0) // do we care about notifications?
00106     addUndoItem(new KateEditRemoveTextUndo(m_document, line, col, s));
00107 }
00108 
00109 void KateUndoManager::slotMarkLineAutoWrapped(int line, bool autowrapped)
00110 {
00111   if (m_editCurrentUndo != 0) // do we care about notifications?
00112     addUndoItem(new KateEditMarkLineAutoWrappedUndo(m_document, line, autowrapped));
00113 }
00114 
00115 void KateUndoManager::slotLineWrapped(int line, int col, int pos, bool newLine)
00116 {
00117   if (m_editCurrentUndo != 0) // do we care about notifications?
00118     addUndoItem(new KateEditWrapLineUndo(m_document, line, col, pos, newLine));
00119 }
00120 
00121 void KateUndoManager::slotLineUnWrapped(int line, int col, int length, bool lineRemoved)
00122 {
00123   if (m_editCurrentUndo != 0) // do we care about notifications?
00124     addUndoItem(new KateEditUnWrapLineUndo(m_document, line, col, length, lineRemoved));
00125 }
00126 
00127 void KateUndoManager::slotLineInserted(int line, const QString &s)
00128 {
00129   if (m_editCurrentUndo != 0) // do we care about notifications?
00130     addUndoItem(new KateEditInsertLineUndo(m_document, line, s));
00131 }
00132 
00133 void KateUndoManager::slotLineRemoved(int line, const QString &s)
00134 {
00135   if (m_editCurrentUndo != 0) // do we care about notifications?
00136     addUndoItem(new KateEditRemoveLineUndo(m_document, line, s));
00137 }
00138 
00139 void KateUndoManager::undoCancel()
00140 {
00141   // Don't worry about this when an edit is in progress
00142   if (m_document->isEditRunning())
00143     return;
00144 
00145   m_undoDontMerge = true;
00146 
00147   Q_ASSERT(m_editCurrentUndo == 0);
00148 }
00149 
00150 void KateUndoManager::undoSafePoint() {
00151   Q_ASSERT(m_editCurrentUndo != 0); // call this method only in between editStart() and editEnd()
00152 
00153   m_editCurrentUndo->safePoint();
00154 }
00155 
00156 void KateUndoManager::addUndoItem(KateUndo *undo)
00157 {
00158   Q_ASSERT(undo != 0); // don't add null pointers to our history
00159   Q_ASSERT(m_editCurrentUndo != 0); // make sure there is an undo group for our item
00160 
00161   m_editCurrentUndo->addItem(undo);
00162 
00163   // Clear redo buffer
00164   qDeleteAll(redoItems);
00165   redoItems.clear();
00166 }
00167 
00168 uint KateUndoManager::undoCount () const
00169 {
00170   return undoItems.count ();
00171 }
00172 
00173 uint KateUndoManager::redoCount () const
00174 {
00175   return redoItems.count ();
00176 }
00177 
00178 void KateUndoManager::undo()
00179 {
00180   Q_ASSERT(m_editCurrentUndo == 0); // undo is not supported while we care about notifications (call editEnd() first)
00181 
00182   if (undoItems.count() > 0)
00183   {
00184     //clearSelection ();
00185     /*Disable searchbar highlights due to performance issue
00186      * if undoGroup contains n items, and there're m search highlight regions,
00187      * the total cost is n*m*log(m),
00188      * to undo a simple Replace operation, n=2*m 
00189      * (replace contains both delete and insert undoItem, assume the replaced regions are highlighted),
00190      * cost = 2*m^2*log(m), too high
00191      * since there's a qStableSort inside KTextEditor::SmartRegion::rebuildChildStruct()
00192      */
00193     foreach (KTextEditor::View *v, m_document->views()) {
00194       KateView *view = qobject_cast<KateView*>(v);
00195 
00196       if (view->searchBar(false))
00197         view->searchBar(false)->enableHighlights(false);
00198     }
00199 
00200     undoItems.last()->undo();
00201     redoItems.append (undoItems.last());
00202     undoItems.removeLast ();
00203     updateModified();
00204     emit undoChanged ();
00205   }
00206 }
00207 
00208 void KateUndoManager::redo()
00209 {
00210   Q_ASSERT(m_editCurrentUndo == 0); // redo is not supported while we care about notifications (call editEnd() first)
00211 
00212   if (redoItems.count() > 0)
00213   {
00214     //clearSelection ();
00215     //Disable searchbar highlights due to performance issue, see ::undo()'s comment
00216     foreach (KTextEditor::View *v, m_document->views()) {
00217       KateView *view = qobject_cast<KateView*>(v);
00218 
00219       if (view->searchBar(false))
00220         view->searchBar(false)->enableHighlights(false);
00221     }
00222 
00223     redoItems.last()->redo();
00224     undoItems.append (redoItems.last());
00225     redoItems.removeLast ();
00226     updateModified();
00227 
00228     emit undoChanged ();
00229   }
00230 }
00231 
00232 void KateUndoManager::updateModified()
00233 {
00234   /*
00235   How this works:
00236 
00237     After noticing that there where to many scenarios to take into
00238     consideration when using 'if's to toggle the "Modified" flag
00239     I came up with this baby, flexible and repetitive calls are
00240     minimal.
00241 
00242     A numeric unique pattern is generated by toggling a set of bits,
00243     each bit symbolizes a different state in the Undo Redo structure.
00244 
00245       undoItems.isEmpty() != null          BIT 1
00246       redoItems.isEmpty() != null          BIT 2
00247       docWasSavedWhenUndoWasEmpty == true  BIT 3
00248       docWasSavedWhenRedoWasEmpty == true  BIT 4
00249       lastUndoGroupWhenSavedIsLastUndo     BIT 5
00250       lastUndoGroupWhenSavedIsLastRedo     BIT 6
00251       lastRedoGroupWhenSavedIsLastUndo     BIT 7
00252       lastRedoGroupWhenSavedIsLastRedo     BIT 8
00253 
00254     If you find a new pattern, please add it to the patterns array
00255   */
00256 
00257   unsigned char currentPattern = 0;
00258   const unsigned char patterns[] = {5,16,21,24,26,88,90,93,133,144,149,165};
00259   const unsigned char patternCount = sizeof(patterns);
00260   KateUndoGroup* undoLast = 0;
00261   KateUndoGroup* redoLast = 0;
00262 
00263   if (undoItems.isEmpty())
00264   {
00265     currentPattern |= 1;
00266   }
00267   else
00268   {
00269     undoLast = undoItems.last();
00270   }
00271 
00272   if (redoItems.isEmpty())
00273   {
00274     currentPattern |= 2;
00275   }
00276   else
00277   {
00278     redoLast = redoItems.last();
00279   }
00280 
00281   if (docWasSavedWhenUndoWasEmpty) currentPattern |= 4;
00282   if (docWasSavedWhenRedoWasEmpty) currentPattern |= 8;
00283   if (lastUndoGroupWhenSaved == undoLast) currentPattern |= 16;
00284   if (lastUndoGroupWhenSaved == redoLast) currentPattern |= 32;
00285   if (lastRedoGroupWhenSaved == undoLast) currentPattern |= 64;
00286   if (lastRedoGroupWhenSaved == redoLast) currentPattern |= 128;
00287 
00288   // This will print out the pattern information
00289 
00290   kDebug() << "Pattern:" << static_cast<unsigned int>(currentPattern);
00291 
00292   for (uint patternIndex = 0; patternIndex < patternCount; ++patternIndex)
00293   {
00294     if ( currentPattern == patterns[patternIndex] )
00295     {
00296       m_document->setModified( false );
00297       // (dominik) whenever the doc is not modified, succeeding edits
00298       // should not be merged
00299       setUndoDontMerge(true);
00300       kDebug() << "setting modified to false!";
00301       break;
00302     }
00303   }
00304 }
00305 
00306 void KateUndoManager::clearUndo()
00307 {
00308   qDeleteAll(undoItems);
00309   undoItems.clear ();
00310 
00311   lastUndoGroupWhenSaved = 0;
00312   docWasSavedWhenUndoWasEmpty = false;
00313 
00314   emit undoChanged ();
00315 }
00316 
00317 void KateUndoManager::clearRedo()
00318 {
00319   qDeleteAll(redoItems);
00320   redoItems.clear ();
00321 
00322   lastRedoGroupWhenSaved = 0;
00323   docWasSavedWhenRedoWasEmpty = false;
00324 
00325   emit undoChanged ();
00326 }
00327 
00328 void KateUndoManager::setModified(bool m) {
00329   if ( m == false )
00330   {
00331     if ( ! undoItems.isEmpty() )
00332     {
00333       lastUndoGroupWhenSaved = undoItems.last();
00334     }
00335 
00336     if ( ! redoItems.isEmpty() )
00337     {
00338       lastRedoGroupWhenSaved = redoItems.last();
00339     }
00340 
00341     docWasSavedWhenUndoWasEmpty = undoItems.isEmpty();
00342     docWasSavedWhenRedoWasEmpty = redoItems.isEmpty();
00343   }
00344 }
00345 
00346 void KateUndoManager::updateConfig ()
00347 {
00348   emit undoChanged ();
00349 }
00350 
00351 bool KateUndoManager::undoDontMerge( ) const
00352 {
00353   return m_undoDontMerge;
00354 }
00355 
00356 void KateUndoManager::setAllowComplexMerge(bool allow)
00357 {
00358   m_undoComplexMerge = allow;
00359 }
00360 
00361 bool KateUndoManager::allowComplexMerge() const
00362 {
00363   return m_undoComplexMerge;
00364 }
00365 
00366 void KateUndoManager::setUndoDontMerge(bool dontMerge)
00367 {
00368   m_undoDontMerge = dontMerge;
00369 }
00370 
00371 // kate: space-indent on; indent-width 2; replace-tabs on;

Kate

Skip menu "Kate"
  • 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
  • KPty
  • Kross
  • KUtils
  • Nepomuk
  • Plasma
  • Solid
  • Sonnet
  • ThreadWeaver
Generated for kdelibs by doxygen 1.6.1
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