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;
KDE 4.6 API Reference