Kate
katetextblock.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 "katetextblock.h" 00022 #include "katetextbuffer.h" 00023 00024 namespace Kate { 00025 00026 TextBlock::TextBlock (TextBuffer *buffer, int startLine) 00027 : m_buffer (buffer) 00028 , m_startLine (startLine) 00029 { 00030 } 00031 00032 TextBlock::~TextBlock () 00033 { 00034 // blocks should be empty before they are deleted! 00035 Q_ASSERT (m_lines.empty()); 00036 Q_ASSERT (m_cursors.empty()); 00037 00038 // it only is a hint for ranges for this block, not the storage of them 00039 } 00040 00041 void TextBlock::setStartLine (int startLine) 00042 { 00043 // allow only valid lines 00044 Q_ASSERT (startLine >= 0); 00045 Q_ASSERT (startLine < m_buffer->lines ()); 00046 00047 m_startLine = startLine; 00048 } 00049 00050 TextLine TextBlock::line (int line) const 00051 { 00052 // right input 00053 Q_ASSERT (line >= startLine ()); 00054 00055 // calc internal line 00056 line = line - startLine (); 00057 00058 // in range 00059 Q_ASSERT (line < m_lines.size ()); 00060 00061 // get text line 00062 return m_lines.at(line); 00063 } 00064 00065 void TextBlock::text (QString &text) const 00066 { 00067 // combine all lines 00068 for (int i = 0; i < m_lines.size(); ++i) { 00069 // not first line, insert \n 00070 if (i > 0 || startLine() > 0) 00071 text.append ('\n'); 00072 00073 text.append (m_lines.at(i)->text ()); 00074 } 00075 } 00076 00077 void TextBlock::wrapLine (const KTextEditor::Cursor &position) 00078 { 00079 // calc internal line 00080 int line = position.line () - startLine (); 00081 00082 // get text 00083 QString &text = m_lines.at(line)->textReadWrite (); 00084 00085 // check if valid column 00086 Q_ASSERT (position.column() >= 0); 00087 Q_ASSERT (position.column() <= text.size()); 00088 00089 // create new line and insert it 00090 m_lines.insert (m_lines.begin() + line + 1, TextLine (new TextLineData())); 00091 00092 // perhaps remove some text from previous line and append it 00093 if (position.column() < text.size ()) { 00094 // text from old line moved first to new one 00095 m_lines.at(line+1)->textReadWrite() = text.right (text.size() - position.column()); 00096 00097 // now remove wrapped text from old line 00098 text.chop (text.size() - position.column()); 00099 } 00100 00104 m_buffer->history().wrapLine (position); 00105 00110 // no cursors will leave or join this block 00111 00112 // no cursors in this block, no work to do.. 00113 if (m_cursors.empty()) 00114 return; 00115 00116 // move all cursors on the line which has the text inserted 00117 // remember all ranges modified 00118 QSet<TextRange *> changedRanges; 00119 foreach (TextCursor *cursor, m_cursors) { 00120 // skip cursors on lines in front of the wrapped one! 00121 if (cursor->lineInBlock() < line) 00122 continue; 00123 00124 // either this is simple, line behind the wrapped one 00125 if (cursor->lineInBlock() > line) { 00126 // patch line of cursor 00127 cursor->m_line++; 00128 } 00129 00130 // this is the wrapped line 00131 else { 00132 // skip cursors with too small column 00133 if (cursor->column() <= position.column()) { 00134 if (cursor->column() < position.column() || !cursor->m_moveOnInsert) 00135 continue; 00136 } 00137 00138 // move cursor 00139 00140 // patch line of cursor 00141 cursor->m_line++; 00142 00143 // patch column 00144 cursor->m_column -= position.column(); 00145 } 00146 00147 // remember range, if any 00148 if (cursor->kateRange()) 00149 changedRanges.insert (cursor->kateRange()); 00150 } 00151 00152 // check validity of all ranges, might invalidate them... 00153 foreach (TextRange *range, changedRanges) 00154 range->checkValidity (); 00155 } 00156 00157 void TextBlock::unwrapLine (int line, TextBlock *previousBlock) 00158 { 00159 // calc internal line 00160 line = line - startLine (); 00161 00162 // two possiblities: either first line of this block or later line 00163 if (line == 0) { 00164 // we need previous block with at least one line 00165 Q_ASSERT (previousBlock); 00166 Q_ASSERT (previousBlock->lines () > 0); 00167 00168 // move last line of previous block to this one, might result in empty block 00169 TextLine oldFirst = m_lines.at(0); 00170 int lastLineOfPreviousBlock = previousBlock->lines ()-1; 00171 TextLine newFirst = previousBlock->m_lines.last(); 00172 m_lines[0] = newFirst; 00173 previousBlock->m_lines.erase (previousBlock->m_lines.begin() + (previousBlock->lines () - 1)); 00174 00175 // append text 00176 int oldSizeOfPreviousLine = newFirst->text().size(); 00177 newFirst->textReadWrite().append (oldFirst->text()); 00178 00179 // patch startLine of this block 00180 --m_startLine; 00181 00185 m_buffer->history().unwrapLine (startLine () + line, oldSizeOfPreviousLine); 00186 00191 // no cursors in this and previous block, no work to do.. 00192 // no need to touch ranges-cache, without cursors, no range could end between this blocks! 00193 if (previousBlock->m_cursors.empty() && m_cursors.empty()) 00194 return; 00195 00196 // move all cursors because of the unwrapped line 00197 // remember all ranges modified 00198 QSet<TextRange *> changedRanges; 00199 foreach (TextCursor *cursor, m_cursors) { 00200 // this is the unwrapped line 00201 if (cursor->lineInBlock() == 0) { 00202 // patch column 00203 cursor->m_column += oldSizeOfPreviousLine; 00204 } 00205 00206 // remember range, if any 00207 if (cursor->kateRange()) 00208 changedRanges.insert (cursor->kateRange()); 00209 } 00210 00211 // move cursors of the moved line from previous block to this block now 00212 QSet<TextCursor *> newPreviousCursors; 00213 QSet<TextRange *> rangesMoved; 00214 foreach (TextCursor *cursor, previousBlock->m_cursors) { 00215 if (cursor->lineInBlock() == lastLineOfPreviousBlock) { 00216 cursor->m_line = 0; 00217 cursor->m_block = this; 00218 m_cursors.insert (cursor); 00219 00220 // remember ranges moved over block boundary 00221 if (cursor->kateRange()) 00222 rangesMoved.insert (cursor->kateRange()); 00223 } 00224 else 00225 newPreviousCursors.insert (cursor); 00226 } 00227 previousBlock->m_cursors = newPreviousCursors; 00228 00229 foreach (TextRange *range, rangesMoved) { 00230 // either now only in new block 00231 if (range->start().line () >= startLine()) 00232 previousBlock->removeRange (range); 00233 00234 // or now in both 00235 updateRange (range); 00236 } 00237 00238 // check validity of all ranges, might invalidate them... 00239 foreach (TextRange *range, changedRanges) 00240 range->checkValidity (); 00241 00242 // be done 00243 return; 00244 } 00245 00246 // easy: just move text to previous line and remove current one 00247 int oldSizeOfPreviousLine = m_lines.at(line-1)->text().size(); 00248 m_lines.at(line-1)->textReadWrite().append (m_lines.at(line)->text()); 00249 m_lines.erase (m_lines.begin () + line); 00250 00254 m_buffer->history().unwrapLine (startLine () + line, oldSizeOfPreviousLine); 00255 00260 // no cursors in this block, no work to do.. 00261 if (m_cursors.empty()) 00262 return; 00263 00264 // move all cursors because of the unwrapped line 00265 // remember all ranges modified 00266 QSet<TextRange *> changedRanges; 00267 foreach (TextCursor *cursor, m_cursors) { 00268 // skip cursors in lines in front of removed one 00269 if (cursor->lineInBlock() < line) 00270 continue; 00271 00272 // this is the unwrapped line 00273 if (cursor->lineInBlock() == line) { 00274 // patch column 00275 cursor->m_column += oldSizeOfPreviousLine; 00276 } 00277 00278 // patch line of cursor 00279 cursor->m_line--; 00280 00281 // remember range, if any 00282 if (cursor->kateRange()) 00283 changedRanges.insert (cursor->kateRange()); 00284 } 00285 00286 // check validity of all ranges, might invalidate them... 00287 foreach (TextRange *range, changedRanges) 00288 range->checkValidity (); 00289 } 00290 00291 void TextBlock::insertText (const KTextEditor::Cursor &position, const QString &text) 00292 { 00293 // calc internal line 00294 int line = position.line () - startLine (); 00295 00296 // get text 00297 QString &textOfLine = m_lines.at(line)->textReadWrite (); 00298 int oldLength = textOfLine.size (); 00299 00300 // check if valid column 00301 Q_ASSERT (position.column() >= 0); 00302 Q_ASSERT (position.column() <= textOfLine.size()); 00303 00304 // insert text 00305 textOfLine.insert (position.column(), text); 00306 00310 m_buffer->history().insertText (position, text.size(), oldLength); 00311 00316 // no cursors in this block, no work to do.. 00317 if (m_cursors.empty()) 00318 return; 00319 00320 // move all cursors on the line which has the text inserted 00321 // remember all ranges modified 00322 QSet<TextRange *> changedRanges; 00323 foreach (TextCursor *cursor, m_cursors) { 00324 // skip cursors not on this line! 00325 if (cursor->lineInBlock() != line) 00326 continue; 00327 00328 // skip cursors with too small column 00329 if (cursor->column() <= position.column()) { 00330 if (cursor->column() < position.column() || !cursor->m_moveOnInsert) 00331 continue; 00332 } 00333 00334 // patch column of cursor 00335 if (cursor->m_column <= oldLength) 00336 cursor->m_column += text.size (); 00337 00338 // special handling if cursor behind the real line, e.g. non-wrapping cursor in block selection mode 00339 else if (cursor->m_column < textOfLine.size()) 00340 cursor->m_column = textOfLine.size(); 00341 00342 // remember range, if any 00343 if (cursor->kateRange()) 00344 changedRanges.insert (cursor->kateRange()); 00345 } 00346 00347 // check validity of all ranges, might invalidate them... 00348 foreach (TextRange *range, changedRanges) 00349 range->checkValidity (); 00350 } 00351 00352 void TextBlock::removeText (const KTextEditor::Range &range, QString &removedText) 00353 { 00354 // calc internal line 00355 int line = range.start().line () - startLine (); 00356 00357 // get text 00358 QString &textOfLine = m_lines.at(line)->textReadWrite (); 00359 int oldLength = textOfLine.size (); 00360 00361 // check if valid column 00362 Q_ASSERT (range.start().column() >= 0); 00363 Q_ASSERT (range.start().column() <= textOfLine.size()); 00364 Q_ASSERT (range.end().column() >= 0); 00365 Q_ASSERT (range.end().column() <= textOfLine.size()); 00366 00367 // get text which will be removed 00368 removedText = textOfLine.mid (range.start().column(), range.end().column() - range.start().column()); 00369 00370 // remove text 00371 textOfLine.remove (range.start().column(), range.end().column() - range.start().column()); 00372 00376 m_buffer->history().removeText (range, oldLength); 00377 00382 // no cursors in this block, no work to do.. 00383 if (m_cursors.empty()) 00384 return; 00385 00386 // move all cursors on the line which has the text removed 00387 // remember all ranges modified 00388 QSet<TextRange *> changedRanges; 00389 foreach (TextCursor *cursor, m_cursors) { 00390 // skip cursors not on this line! 00391 if (cursor->lineInBlock() != line) 00392 continue; 00393 00394 // skip cursors with too small column 00395 if (cursor->column() <= range.start().column()) 00396 continue; 00397 00398 // patch column of cursor 00399 if (cursor->column() <= range.end().column()) 00400 cursor->m_column = range.start().column (); 00401 else 00402 cursor->m_column -= (range.end().column() - range.start().column()); 00403 00404 // remember range, if any 00405 if (cursor->kateRange()) 00406 changedRanges.insert (cursor->kateRange()); 00407 } 00408 00409 // check validity of all ranges, might invalidate them... 00410 foreach (TextRange *range, changedRanges) 00411 range->checkValidity (); 00412 } 00413 00414 void TextBlock::debugPrint (int blockIndex) const 00415 { 00416 // print all blocks 00417 for (int i = 0; i < m_lines.size(); ++i) 00418 printf ("%4d - %4d : %4d : '%s'\n", blockIndex, startLine() + i 00419 , m_lines.at(i)->text().size(), qPrintable (m_lines.at(i)->text())); 00420 } 00421 00422 TextBlock *TextBlock::splitBlock (int fromLine) 00423 { 00424 // half the block 00425 int linesOfNewBlock = lines () - fromLine; 00426 00427 // create and insert new block 00428 TextBlock *newBlock = new TextBlock (m_buffer, startLine() + fromLine); 00429 00430 // move lines 00431 newBlock->m_lines.reserve (linesOfNewBlock); 00432 for (int i = fromLine; i < m_lines.size(); ++i) 00433 newBlock->m_lines.append (m_lines.at(i)); 00434 m_lines.resize (fromLine); 00435 00436 // move cursors 00437 QSet<TextCursor*> oldBlockSet; 00438 QSet<TextRange*> rangesInteresting; 00439 foreach (TextCursor *cursor, m_cursors) { 00440 if (cursor->kateRange()) 00441 rangesInteresting.insert (cursor->kateRange()); 00442 00443 if (cursor->lineInBlock() >= fromLine) { 00444 cursor->m_line = cursor->lineInBlock() - fromLine; 00445 cursor->m_block = newBlock; 00446 newBlock->m_cursors.insert (cursor); 00447 } 00448 else 00449 oldBlockSet.insert (cursor); 00450 } 00451 m_cursors = oldBlockSet; 00452 00453 foreach (TextRange *range, rangesInteresting) { 00454 // only in new block 00455 if (range->start().line () >= newBlock->startLine()) { 00456 removeRange (range); 00457 newBlock->updateRange (range); 00458 } 00459 } 00460 00461 // return the new generated block 00462 return newBlock; 00463 } 00464 00465 void TextBlock::mergeBlock (TextBlock *targetBlock) 00466 { 00467 // move cursors, do this first, now still lines() count is correct for target 00468 foreach (TextCursor *cursor, m_cursors) { 00469 cursor->m_line = cursor->lineInBlock() + targetBlock->lines (); 00470 cursor->m_block = targetBlock; 00471 targetBlock->m_cursors.insert (cursor); 00472 } 00473 m_cursors.clear (); 00474 00475 // move lines 00476 targetBlock->m_lines.reserve (targetBlock->lines() + lines ()); 00477 for (int i = 0; i < m_lines.size(); ++i) 00478 targetBlock->m_lines.append (m_lines.at(i)); 00479 m_lines.clear (); 00480 00481 QList<TextRange*> allRanges = m_uncachedRanges.toList() + m_cachedLineForRanges.keys(); 00482 foreach(TextRange* range, allRanges) { 00483 removeRange(range); 00484 targetBlock->updateRange(range); 00485 } 00486 } 00487 00488 void TextBlock::deleteBlockContent () 00489 { 00490 // kill cursors, if not belonging to a range 00491 QSet<TextCursor *> copy = m_cursors; 00492 foreach (TextCursor *cursor, copy) 00493 if (!cursor->kateRange()) 00494 delete cursor; 00495 00496 // kill lines 00497 m_lines.clear (); 00498 } 00499 00500 void TextBlock::clearBlockContent (TextBlock *targetBlock) 00501 { 00502 // move cursors, if not belonging to a range 00503 QSet<TextCursor *> copy = m_cursors; 00504 foreach (TextCursor *cursor, copy) { 00505 if (!cursor->kateRange()) { 00506 cursor->m_column = 0; 00507 cursor->m_line = 0; 00508 cursor->m_block = targetBlock; 00509 targetBlock->m_cursors.insert (cursor); 00510 m_cursors.remove (cursor); 00511 } 00512 } 00513 00514 // kill lines 00515 m_lines.clear (); 00516 } 00517 00518 void TextBlock::updateRange (TextRange* range) 00519 { 00523 const int startLine = range->startInternal().lineInternal(); 00524 const int endLine = range->endInternal().lineInternal(); 00525 const bool isSingleLine = startLine == endLine; 00526 00530 if(isSingleLine && m_cachedLineForRanges.contains (range) && (m_cachedLineForRanges.value(range) == startLine - m_startLine)) 00531 return; 00532 00536 if(!isSingleLine && m_uncachedRanges.contains (range)) 00537 return; 00538 00542 if(containsRange(range)) 00543 removeRange(range); 00544 00548 if (!isSingleLine) { 00552 m_uncachedRanges.insert(range); 00553 return; 00554 } 00555 00559 const int lineOffset = startLine - m_startLine; 00560 00564 if (m_cachedRangesForLine.size() <= lineOffset) 00565 m_cachedRangesForLine.resize(lineOffset+1); 00566 00570 m_cachedRangesForLine[lineOffset].insert(range); 00571 m_cachedLineForRanges[range] = lineOffset; 00572 } 00573 00574 void TextBlock::removeRange (TextRange* range) 00575 { 00579 if(m_uncachedRanges.remove (range)) { 00583 Q_ASSERT (!m_cachedLineForRanges.contains(range)); 00584 00585 return; 00586 } 00587 00591 QHash<TextRange*, int>::iterator it = m_cachedLineForRanges.find(range); 00592 if (it != m_cachedLineForRanges.end()) { 00596 Q_ASSERT (!m_uncachedRanges.contains(range)); 00597 00601 Q_ASSERT (m_cachedRangesForLine.at(*it).contains(range)); 00602 00606 m_cachedRangesForLine[*it].remove(range); 00607 m_cachedLineForRanges.erase(it); 00608 return; 00609 } 00610 00614 } 00615 00616 }
KDE 4.6 API Reference