• Skip to content
  • Skip to link menu
KDE 4.6 API Reference
  • KDE API Reference
  • kdelibs
  • KDE Home
  • Contact Us
 

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 }

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