• Skip to content
  • Skip to link menu
KDE 4.6 API Reference
  • KDE API Reference
  • kdelibs
  • KDE Home
  • 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-2010 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 <ktexteditor/view.h>
00021 
00022 #include "katedocument.h"
00023 #include "kateundo.h"
00024 
00025 KateUndoManager::KateUndoManager (KateDocument *doc)
00026   : QObject (doc)
00027   , m_document (doc)
00028   , m_undoComplexMerge (false)
00029   , m_isActive (true)
00030   , m_editCurrentUndo (0)
00031   , lastUndoGroupWhenSaved(0)
00032   , lastRedoGroupWhenSaved(0)
00033   , docWasSavedWhenUndoWasEmpty(true)
00034   , docWasSavedWhenRedoWasEmpty(true)
00035 {
00036   connect(this, SIGNAL(undoEnd(KTextEditor::Document*)), this, SIGNAL(undoChanged()));
00037   connect(this, SIGNAL(redoEnd(KTextEditor::Document*)), this, SIGNAL(undoChanged()));
00038 
00039   connect(doc, SIGNAL(viewCreated(KTextEditor::Document*, KTextEditor::View*)), SLOT(viewCreated(KTextEditor::Document*, KTextEditor::View*)));
00040 }
00041 
00042 KateUndoManager::~KateUndoManager()
00043 {
00044   delete m_editCurrentUndo;
00045 
00046   // cleanup the undo/redo items, very important, truee :/
00047   qDeleteAll(undoItems);
00048   undoItems.clear();
00049   qDeleteAll(redoItems);
00050   redoItems.clear();
00051 }
00052 
00053 KTextEditor::Document *KateUndoManager::document()
00054 {
00055   return m_document;
00056 }
00057 
00058 void KateUndoManager::viewCreated (KTextEditor::Document *, KTextEditor::View *newView)
00059 {
00060   connect(newView, SIGNAL(cursorPositionChanged(KTextEditor::View*, const KTextEditor::Cursor&)), SLOT(undoCancel()));
00061 }
00062 
00063 void KateUndoManager::editStart()
00064 {
00065   if (!m_isActive)
00066     return;
00067 
00068   // editStart() and editEnd() must be called in alternating fashion
00069   Q_ASSERT(m_editCurrentUndo == 0); // make sure to enter a clean state
00070 
00071   const KTextEditor::Cursor cursorPosition = activeView() ? activeView()->cursorPosition() : KTextEditor::Cursor::invalid();
00072   const KTextEditor::Range selectionRange = activeView() ? activeView()->selectionRange() : KTextEditor::Range::invalid();
00073 
00074   // new current undo item
00075   m_editCurrentUndo = new KateUndoGroup(this, cursorPosition, selectionRange);
00076 
00077   Q_ASSERT(m_editCurrentUndo != 0); // a new undo group must be created by this method
00078 }
00079 
00080 void KateUndoManager::editEnd()
00081 {
00082   if (!m_isActive)
00083     return;
00084 
00085   // editStart() and editEnd() must be called in alternating fashion
00086   Q_ASSERT(m_editCurrentUndo != 0); // an undo group must have been created by editStart()
00087 
00088   const KTextEditor::Cursor cursorPosition = activeView() ? activeView()->cursorPosition() : KTextEditor::Cursor::invalid();
00089   const KTextEditor::Range selectionRange = activeView() ? activeView()->selectionRange() : KTextEditor::Range::invalid();
00090 
00091   m_editCurrentUndo->editEnd(cursorPosition, selectionRange);
00092 
00093     bool changedUndo = false;
00094 
00095     if (m_editCurrentUndo->isEmpty()) {
00096       delete m_editCurrentUndo;
00097     } else if (!undoItems.isEmpty()
00098         && undoItems.last()->merge(m_editCurrentUndo, m_undoComplexMerge)) {
00099       delete m_editCurrentUndo;
00100     } else {
00101       undoItems.append(m_editCurrentUndo);
00102       changedUndo = true;
00103     }
00104 
00105     m_editCurrentUndo = 0L;
00106 
00107     if (changedUndo)
00108       emit undoChanged();
00109 
00110   Q_ASSERT(m_editCurrentUndo == 0); // must be 0 after calling this method
00111 }
00112 
00113 void KateUndoManager::inputMethodStart()
00114 {
00115   setActive(false);
00116   m_document->editStart();
00117 }
00118 
00119 void KateUndoManager::inputMethodEnd()
00120 {
00121   m_document->editEnd();
00122   setActive(true);
00123 }
00124 
00125 void KateUndoManager::startUndo()
00126 {
00127   setActive(false);
00128   m_document->editStart();
00129 }
00130 
00131 void KateUndoManager::endUndo()
00132 {
00133   m_document->editEnd();
00134   setActive(true);
00135 }
00136 
00137 void KateUndoManager::slotTextInserted(int line, int col, const QString &s)
00138 {
00139   if (m_editCurrentUndo != 0) // do we care about notifications?
00140     addUndoItem(new KateEditInsertTextUndo(m_document, line, col, s));
00141 }
00142 
00143 void KateUndoManager::slotTextRemoved(int line, int col, const QString &s)
00144 {
00145   if (m_editCurrentUndo != 0) // do we care about notifications?
00146     addUndoItem(new KateEditRemoveTextUndo(m_document, line, col, s));
00147 }
00148 
00149 void KateUndoManager::slotMarkLineAutoWrapped(int line, bool autowrapped)
00150 {
00151   if (m_editCurrentUndo != 0) // do we care about notifications?
00152     addUndoItem(new KateEditMarkLineAutoWrappedUndo(m_document, line, autowrapped));
00153 }
00154 
00155 void KateUndoManager::slotLineWrapped(int line, int col, int pos, bool newLine)
00156 {
00157   if (m_editCurrentUndo != 0) // do we care about notifications?
00158     addUndoItem(new KateEditWrapLineUndo(m_document, line, col, pos, newLine));
00159 }
00160 
00161 void KateUndoManager::slotLineUnWrapped(int line, int col, int length, bool lineRemoved)
00162 {
00163   if (m_editCurrentUndo != 0) // do we care about notifications?
00164     addUndoItem(new KateEditUnWrapLineUndo(m_document, line, col, length, lineRemoved));
00165 }
00166 
00167 void KateUndoManager::slotLineInserted(int line, const QString &s)
00168 {
00169   if (m_editCurrentUndo != 0) // do we care about notifications?
00170     addUndoItem(new KateEditInsertLineUndo(m_document, line, s));
00171 }
00172 
00173 void KateUndoManager::slotLineRemoved(int line, const QString &s)
00174 {
00175   if (m_editCurrentUndo != 0) // do we care about notifications?
00176     addUndoItem(new KateEditRemoveLineUndo(m_document, line, s));
00177 }
00178 
00179 void KateUndoManager::undoCancel()
00180 {
00181   // Don't worry about this when an edit is in progress
00182   if (m_document->isEditRunning())
00183     return;
00184 
00185   undoSafePoint();
00186 }
00187 
00188 void KateUndoManager::undoSafePoint() {
00189   KateUndoGroup *undoGroup = m_editCurrentUndo;
00190 
00191   if (undoGroup == 0 && !undoItems.isEmpty())
00192     undoGroup = undoItems.last();
00193 
00194   if (undoGroup == 0)
00195     return;
00196 
00197   undoGroup->safePoint();
00198 }
00199 
00200 void KateUndoManager::addUndoItem(KateUndo *undo)
00201 {
00202   Q_ASSERT(undo != 0); // don't add null pointers to our history
00203   Q_ASSERT(m_editCurrentUndo != 0); // make sure there is an undo group for our item
00204 
00205   m_editCurrentUndo->addItem(undo);
00206 
00207   // Clear redo buffer
00208   qDeleteAll(redoItems);
00209   redoItems.clear();
00210 }
00211 
00212 void KateUndoManager::setActive(bool enabled)
00213 {
00214   Q_ASSERT(m_editCurrentUndo == 0); // must not already be in edit mode
00215   Q_ASSERT(m_isActive != enabled);
00216 
00217   m_isActive = enabled;
00218 
00219   emit isActiveChanged(enabled);
00220 }
00221 
00222 uint KateUndoManager::undoCount () const
00223 {
00224   return undoItems.count ();
00225 }
00226 
00227 uint KateUndoManager::redoCount () const
00228 {
00229   return redoItems.count ();
00230 }
00231 
00232 void KateUndoManager::undo()
00233 {
00234   Q_ASSERT(m_editCurrentUndo == 0); // undo is not supported while we care about notifications (call editEnd() first)
00235 
00236   if (undoItems.count() > 0)
00237   {
00238     emit undoStart(document());
00239 
00240     undoItems.last()->undo(activeView());
00241     redoItems.append (undoItems.last());
00242     undoItems.removeLast ();
00243     updateModified();
00244 
00245     emit undoEnd(document());
00246   }
00247 }
00248 
00249 void KateUndoManager::redo()
00250 {
00251   Q_ASSERT(m_editCurrentUndo == 0); // redo is not supported while we care about notifications (call editEnd() first)
00252 
00253   if (redoItems.count() > 0)
00254   {
00255     emit redoStart(document());
00256 
00257     redoItems.last()->redo(activeView());
00258     undoItems.append (redoItems.last());
00259     redoItems.removeLast ();
00260     updateModified();
00261 
00262     emit redoEnd(document());
00263   }
00264 }
00265 
00266 void KateUndoManager::updateModified()
00267 {
00268   /*
00269   How this works:
00270 
00271     After noticing that there where to many scenarios to take into
00272     consideration when using 'if's to toggle the "Modified" flag
00273     I came up with this baby, flexible and repetitive calls are
00274     minimal.
00275 
00276     A numeric unique pattern is generated by toggling a set of bits,
00277     each bit symbolizes a different state in the Undo Redo structure.
00278 
00279       undoItems.isEmpty() != null          BIT 1
00280       redoItems.isEmpty() != null          BIT 2
00281       docWasSavedWhenUndoWasEmpty == true  BIT 3
00282       docWasSavedWhenRedoWasEmpty == true  BIT 4
00283       lastUndoGroupWhenSavedIsLastUndo     BIT 5
00284       lastUndoGroupWhenSavedIsLastRedo     BIT 6
00285       lastRedoGroupWhenSavedIsLastUndo     BIT 7
00286       lastRedoGroupWhenSavedIsLastRedo     BIT 8
00287 
00288     If you find a new pattern, please add it to the patterns array
00289   */
00290 
00291   unsigned char currentPattern = 0;
00292   const unsigned char patterns[] = {5,16,21,24,26,88,90,93,133,144,149,154,165};
00293   const unsigned char patternCount = sizeof(patterns);
00294   KateUndoGroup* undoLast = 0;
00295   KateUndoGroup* redoLast = 0;
00296 
00297   if (undoItems.isEmpty())
00298   {
00299     currentPattern |= 1;
00300   }
00301   else
00302   {
00303     undoLast = undoItems.last();
00304   }
00305 
00306   if (redoItems.isEmpty())
00307   {
00308     currentPattern |= 2;
00309   }
00310   else
00311   {
00312     redoLast = redoItems.last();
00313   }
00314 
00315   if (docWasSavedWhenUndoWasEmpty) currentPattern |= 4;
00316   if (docWasSavedWhenRedoWasEmpty) currentPattern |= 8;
00317   if (lastUndoGroupWhenSaved == undoLast) currentPattern |= 16;
00318   if (lastUndoGroupWhenSaved == redoLast) currentPattern |= 32;
00319   if (lastRedoGroupWhenSaved == undoLast) currentPattern |= 64;
00320   if (lastRedoGroupWhenSaved == redoLast) currentPattern |= 128;
00321 
00322   // This will print out the pattern information
00323 
00324   kDebug() << "Pattern:" << static_cast<unsigned int>(currentPattern);
00325 
00326   for (uint patternIndex = 0; patternIndex < patternCount; ++patternIndex)
00327   {
00328     if ( currentPattern == patterns[patternIndex] )
00329     {
00330       m_document->setModified( false );
00331       // (dominik) whenever the doc is not modified, succeeding edits
00332       // should not be merged
00333       undoSafePoint();
00334       kDebug() << "setting modified to false!";
00335       break;
00336     }
00337   }
00338 }
00339 
00340 void KateUndoManager::clearUndo()
00341 {
00342   qDeleteAll(undoItems);
00343   undoItems.clear ();
00344 
00345   lastUndoGroupWhenSaved = 0;
00346   docWasSavedWhenUndoWasEmpty = false;
00347 
00348   emit undoChanged ();
00349 }
00350 
00351 void KateUndoManager::clearRedo()
00352 {
00353   qDeleteAll(redoItems);
00354   redoItems.clear ();
00355 
00356   lastRedoGroupWhenSaved = 0;
00357   docWasSavedWhenRedoWasEmpty = false;
00358 
00359   emit undoChanged ();
00360 }
00361 
00362 void KateUndoManager::setModified(bool m) {
00363   if ( m == false )
00364   {
00365     if ( ! undoItems.isEmpty() )
00366     {
00367       lastUndoGroupWhenSaved = undoItems.last();
00368     }
00369 
00370     if ( ! redoItems.isEmpty() )
00371     {
00372       lastRedoGroupWhenSaved = redoItems.last();
00373     }
00374 
00375     docWasSavedWhenUndoWasEmpty = undoItems.isEmpty();
00376     docWasSavedWhenRedoWasEmpty = redoItems.isEmpty();
00377   }
00378 }
00379 
00380 void KateUndoManager::updateConfig ()
00381 {
00382   emit undoChanged ();
00383 }
00384 
00385 void KateUndoManager::setAllowComplexMerge(bool allow)
00386 {
00387   m_undoComplexMerge = allow;
00388 }
00389 
00390 KTextEditor::View* KateUndoManager::activeView()
00391 {
00392   return m_document->activeView();
00393 }
00394 
00395 // 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
  • KDEWebKit
  • KDocTools
  • KFile
  • KHTML
  • KImgIO
  • KInit
  • kio
  • KIOSlave
  • KJS
  •   KJS-API
  •   WTF
  • kjsembed
  • KNewStuff
  • KParts
  • KPty
  • Kross
  • KUnitConversion
  • KUtils
  • Nepomuk
  • Plasma
  • Solid
  • Sonnet
  • ThreadWeaver
Generated for kdelibs by doxygen 1.7.3
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