Kate
katetexthistory.cpp
Go to the documentation of this file.
00001 /* This file is part of the Kate project. 00002 * 00003 * Copyright (C) 2010 Christoph Cullmann <cullmann@kde.org> 00004 * 00005 * This library is free software; you can redistribute it and/or 00006 * modify it under the terms of the GNU Library General Public 00007 * License as published by the Free Software Foundation; either 00008 * version 2 of the License, or (at your option) any later version. 00009 * 00010 * This library is distributed in the hope that it will be useful, 00011 * but WITHOUT ANY WARRANTY; without even the implied warranty of 00012 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU 00013 * Library General Public License for more details. 00014 * 00015 * You should have received a copy of the GNU Library General Public License 00016 * along with this library; see the file COPYING.LIB. If not, write to 00017 * the Free Software Foundation, Inc., 51 Franklin Street, Fifth Floor, 00018 * Boston, MA 02110-1301, USA. 00019 */ 00020 00021 #include "katetexthistory.h" 00022 #include "katetextbuffer.h" 00023 00024 namespace Kate { 00025 00026 TextHistory::TextHistory (TextBuffer &buffer) 00027 : m_buffer (buffer) 00028 , m_lastSavedRevision (-1) 00029 , m_firstHistoryEntryRevision (0) 00030 { 00031 // just call clear to init 00032 clear (); 00033 } 00034 00035 TextHistory::~TextHistory () 00036 { 00037 } 00038 00039 qint64 TextHistory::revision () const 00040 { 00041 // just output last revisions of buffer 00042 return m_buffer.revision (); 00043 } 00044 00045 void TextHistory::clear () 00046 { 00047 // reset last saved revision 00048 m_lastSavedRevision = -1; 00049 00050 // remove all history entries and add no-change dummy for first revision 00051 m_historyEntries.clear (); 00052 m_historyEntries.push_back (Entry ()); 00053 00054 // first entry will again belong to first revision 00055 m_firstHistoryEntryRevision = 0; 00056 } 00057 00058 void TextHistory::setLastSavedRevision () 00059 { 00060 // current revision was succesful saved 00061 m_lastSavedRevision = revision (); 00062 } 00063 00064 void TextHistory::wrapLine (const KTextEditor::Cursor &position) 00065 { 00066 // create and add new entry 00067 Entry entry; 00068 entry.type = Entry::WrapLine; 00069 entry.line = position.line (); 00070 entry.column = position.column (); 00071 addEntry (entry); 00072 } 00073 00074 void TextHistory::unwrapLine (int line, int oldLineLength) 00075 { 00076 // create and add new entry 00077 Entry entry; 00078 entry.type = Entry::UnwrapLine; 00079 entry.line = line; 00080 entry.column = 0; 00081 entry.oldLineLength = oldLineLength; 00082 addEntry (entry); 00083 } 00084 00085 void TextHistory::insertText (const KTextEditor::Cursor &position, int length, int oldLineLength) 00086 { 00087 // create and add new entry 00088 Entry entry; 00089 entry.type = Entry::InsertText; 00090 entry.line = position.line (); 00091 entry.column = position.column (); 00092 entry.length = length; 00093 entry.oldLineLength = oldLineLength; 00094 addEntry (entry); 00095 } 00096 00097 void TextHistory::removeText (const KTextEditor::Range &range, int oldLineLength) 00098 { 00099 // create and add new entry 00100 Entry entry; 00101 entry.type = Entry::RemoveText; 00102 entry.line = range.start().line (); 00103 entry.column = range.start().column (); 00104 entry.length = range.end().column() - range.start().column(); 00105 entry.oldLineLength = oldLineLength; 00106 addEntry (entry); 00107 } 00108 00109 void TextHistory::addEntry (const Entry &entry) 00110 { 00114 Q_ASSERT (!m_historyEntries.empty ()); 00115 00120 if ((m_historyEntries.size () == 1) && !m_historyEntries.first().referenceCounter) { 00124 m_firstHistoryEntryRevision = revision () + 1; 00125 00129 m_historyEntries.first() = entry; 00130 00134 return; 00135 } 00136 00140 m_historyEntries.push_back (entry); 00141 } 00142 00143 void TextHistory::lockRevision (qint64 revision) 00144 { 00148 Q_ASSERT (!m_historyEntries.empty ()); 00149 Q_ASSERT (revision >= m_firstHistoryEntryRevision); 00150 Q_ASSERT (revision < (m_firstHistoryEntryRevision + m_historyEntries.size())); 00151 00155 Entry &entry = m_historyEntries[revision - m_firstHistoryEntryRevision]; 00156 ++entry.referenceCounter; 00157 } 00158 00159 void TextHistory::unlockRevision (qint64 revision) 00160 { 00164 Q_ASSERT (!m_historyEntries.empty ()); 00165 Q_ASSERT (revision >= m_firstHistoryEntryRevision); 00166 Q_ASSERT (revision < (m_firstHistoryEntryRevision + m_historyEntries.size())); 00167 00171 Entry &entry = m_historyEntries[revision - m_firstHistoryEntryRevision]; 00172 Q_ASSERT (entry.referenceCounter); 00173 --entry.referenceCounter; 00174 00178 if (!entry.referenceCounter) { 00182 int unreferencedEdits = 0; 00183 for (int i = 0; i + 1 < m_historyEntries.size(); ++i) { 00184 if (m_historyEntries.at(i).referenceCounter) 00185 break; 00186 00187 // remember deleted count 00188 ++unreferencedEdits; 00189 } 00190 00194 if (unreferencedEdits > 0) { 00195 // remove stuff from history 00196 m_historyEntries.erase (m_historyEntries.begin(), m_historyEntries.begin() + unreferencedEdits); 00197 00198 // patch first entry revision 00199 m_firstHistoryEntryRevision += unreferencedEdits; 00200 } 00201 } 00202 } 00203 00204 void TextHistory::Entry::transformCursor (int &cursorLine, int &cursorColumn, bool moveOnInsert) const 00205 { 00213 if (line > cursorLine) 00214 return; 00215 00219 switch (type) { 00223 case WrapLine: 00227 if (cursorLine == line) { 00231 if (cursorColumn <= column) { 00232 if (cursorColumn < column || !moveOnInsert) 00233 return; 00234 } 00235 00239 cursorColumn = cursorColumn - column; 00240 } 00241 00245 cursorLine += 1; 00246 return; 00247 00251 case UnwrapLine: 00255 if (cursorLine == line) 00256 cursorColumn += oldLineLength; 00257 00261 cursorLine -= 1; 00262 return; 00263 00267 case InsertText: 00271 if (cursorLine != line) 00272 return; 00273 00274 // skip cursors with too small column 00275 if (cursorColumn <= column) 00276 if (cursorColumn < column || !moveOnInsert) 00277 return; 00278 00279 // patch column of cursor 00280 if (cursorColumn <= oldLineLength) 00281 cursorColumn += length; 00282 00283 // special handling if cursor behind the real line, e.g. non-wrapping cursor in block selection mode 00284 else if (cursorColumn < oldLineLength + length) 00285 cursorColumn = oldLineLength + length; 00286 00287 return; 00288 00292 case RemoveText: 00296 if (cursorLine != line) 00297 return; 00298 00299 // skip cursors with too small column 00300 if (cursorColumn <= column) 00301 return; 00302 00303 // patch column of cursor 00304 if (cursorColumn <= column + length) 00305 cursorColumn = column; 00306 else 00307 cursorColumn -= length; 00308 00309 return; 00310 00314 default: 00315 return; 00316 } 00317 } 00318 00319 void TextHistory::Entry::reverseTransformCursor (int &cursorLine, int &cursorColumn, bool moveOnInsert) const 00320 { 00324 switch (type) { 00328 case WrapLine: 00332 if (cursorLine <= line) 00333 return; 00334 00338 if (cursorLine == line + 1) { 00342 cursorColumn = cursorColumn + column; 00343 } 00344 00348 cursorLine -= 1; 00349 return; 00350 00354 case UnwrapLine: 00358 if (cursorLine < line - 1) 00359 return; 00360 00364 if (cursorLine == line - 1) { 00368 if (cursorColumn <= oldLineLength) { 00369 if (cursorColumn < oldLineLength || !moveOnInsert) 00370 return; 00371 } 00372 00373 cursorColumn -= oldLineLength; 00374 } 00375 00379 cursorLine += 1; 00380 return; 00381 00385 case InsertText: 00389 if (cursorLine != line) 00390 return; 00391 00392 // skip cursors with too small column 00393 if (cursorColumn <= column) 00394 return; 00395 00396 // patch column of cursor 00397 if (cursorColumn - length < column) 00398 cursorColumn = column; 00399 else 00400 cursorColumn -= length; 00401 00402 return; 00403 00407 case RemoveText: 00411 if (cursorLine != line) 00412 return; 00413 00414 // skip cursors with too small column 00415 if (cursorColumn <= column) 00416 if (cursorColumn < column || !moveOnInsert) 00417 return; 00418 00419 // patch column of cursor 00420 if (cursorColumn <= oldLineLength) 00421 cursorColumn += length; 00422 00423 // special handling if cursor behind the real line, e.g. non-wrapping cursor in block selection mode 00424 else if (cursorColumn < oldLineLength + length) 00425 cursorColumn = oldLineLength + length; 00426 return; 00427 00431 default: 00432 return; 00433 } 00434 } 00435 00436 void TextHistory::transformCursor (int& line, int& column, KTextEditor::MovingCursor::InsertBehavior insertBehavior, qint64 fromRevision, qint64 toRevision) 00437 { 00441 if (fromRevision == -1) 00442 fromRevision = revision (); 00443 00444 if (toRevision == -1) 00445 toRevision = revision (); 00446 00450 if (fromRevision == toRevision) 00451 return; 00452 00456 Q_ASSERT (!m_historyEntries.empty ()); 00457 Q_ASSERT (fromRevision != toRevision); 00458 Q_ASSERT (fromRevision >= m_firstHistoryEntryRevision); 00459 Q_ASSERT (fromRevision < (m_firstHistoryEntryRevision + m_historyEntries.size())); 00460 Q_ASSERT (toRevision >= m_firstHistoryEntryRevision); 00461 Q_ASSERT (toRevision < (m_firstHistoryEntryRevision + m_historyEntries.size())); 00462 00466 bool moveOnInsert = insertBehavior == KTextEditor::MovingCursor::MoveOnInsert; 00467 00471 if (toRevision > fromRevision) { 00472 for (int rev = fromRevision - m_firstHistoryEntryRevision + 1; rev <= (toRevision - m_firstHistoryEntryRevision); ++rev) { 00473 const Entry &entry = m_historyEntries.at(rev); 00474 entry.transformCursor (line, column, moveOnInsert); 00475 } 00476 } else { 00477 for (int rev = fromRevision - m_firstHistoryEntryRevision; rev >= (toRevision - m_firstHistoryEntryRevision + 1); --rev) { 00478 const Entry &entry = m_historyEntries.at(rev); 00479 entry.reverseTransformCursor (line, column, moveOnInsert); 00480 } 00481 } 00482 } 00483 00484 void TextHistory::transformRange (KTextEditor::Range &range, KTextEditor::MovingRange::InsertBehaviors insertBehaviors, KTextEditor::MovingRange::EmptyBehavior emptyBehavior, qint64 fromRevision, qint64 toRevision) 00485 { 00489 bool invalidateIfEmpty = emptyBehavior == KTextEditor::MovingRange::InvalidateIfEmpty; 00490 if (invalidateIfEmpty && range.end() <= range.start()) { 00491 range = KTextEditor::Range::invalid(); 00492 return; 00493 } 00494 00498 if (fromRevision == -1) 00499 fromRevision = revision (); 00500 00501 if (toRevision == -1) 00502 toRevision = revision (); 00503 00507 if (fromRevision == toRevision) 00508 return; 00509 00513 Q_ASSERT (!m_historyEntries.empty ()); 00514 Q_ASSERT (fromRevision != toRevision); 00515 Q_ASSERT (fromRevision >= m_firstHistoryEntryRevision); 00516 Q_ASSERT (fromRevision < (m_firstHistoryEntryRevision + m_historyEntries.size())); 00517 Q_ASSERT (toRevision >= m_firstHistoryEntryRevision); 00518 Q_ASSERT (toRevision < (m_firstHistoryEntryRevision + m_historyEntries.size())); 00519 00524 // first: copy cursors, without range association 00525 int startLine = range.start().line(), startColumn = range.start().column(), endLine = range.end().line(), endColumn = range.end().column(); 00526 00527 bool moveOnInsertStart = !(insertBehaviors & KTextEditor::MovingRange::ExpandLeft); 00528 bool moveOnInsertEnd = (insertBehaviors & KTextEditor::MovingRange::ExpandRight); 00529 00533 if (toRevision > fromRevision) { 00534 for (int rev = fromRevision - m_firstHistoryEntryRevision + 1; rev <= (toRevision - m_firstHistoryEntryRevision); ++rev) { 00535 const Entry &entry = m_historyEntries.at(rev); 00536 00537 entry.transformCursor (startLine, startColumn, moveOnInsertStart); 00538 00539 entry.transformCursor (endLine, endColumn, moveOnInsertEnd); 00540 00541 // got empty? 00542 if(endLine < startLine || (endLine == startLine && endColumn <= startColumn)) 00543 { 00544 if (invalidateIfEmpty) { 00545 range = KTextEditor::Range::invalid(); 00546 return; 00547 } 00548 else{ 00549 // else normalize them 00550 endLine = startLine; 00551 endColumn = startColumn; 00552 } 00553 } 00554 } 00555 } else { 00556 for (int rev = fromRevision - m_firstHistoryEntryRevision ; rev >= (toRevision - m_firstHistoryEntryRevision + 1); --rev) { 00557 const Entry &entry = m_historyEntries.at(rev); 00558 00559 entry.reverseTransformCursor (startLine, startColumn, moveOnInsertStart); 00560 00561 entry.reverseTransformCursor (endLine, endColumn, moveOnInsertEnd); 00562 00563 // got empty? 00564 if(endLine < startLine || (endLine == startLine && endColumn <= startColumn)) 00565 { 00566 if (invalidateIfEmpty) { 00567 range = KTextEditor::Range::invalid(); 00568 return; 00569 } 00570 else{ 00571 // else normalize them 00572 endLine = startLine; 00573 endColumn = startColumn; 00574 } 00575 } 00576 } 00577 } 00578 00579 // now, copy cursors back 00580 range.start().setLine(startLine); 00581 range.start().setColumn(startColumn); 00582 range.end().setLine(endLine); 00583 range.end().setColumn(endColumn); 00584 00585 } 00586 00587 }
KDE 4.6 API Reference