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

Kate

katelayoutcache.cpp

Go to the documentation of this file.
00001 /*  This file is part of the KDE libraries and the Kate part.
00002  *
00003  *  Copyright (C) 2005 Hamish Rodda <rodda@kde.org>
00004  *  Copyright (C) 2008 Dominik Haumann <dhaumann kde org>
00005  *
00006  *  This library is free software; you can redistribute it and/or
00007  *  modify it under the terms of the GNU Library General Public
00008  *  License as published by the Free Software Foundation; either
00009  *  version 2 of the License, or (at your option) any later version.
00010  *
00011  *  This library is distributed in the hope that it will be useful,
00012  *  but WITHOUT ANY WARRANTY; without even the implied warranty of
00013  *  MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
00014  *  Library General Public License for more details.
00015  *
00016  *  You should have received a copy of the GNU Library General Public License
00017  *  along with this library; see the file COPYING.LIB.  If not, write to
00018  *  the Free Software Foundation, Inc., 51 Franklin Street, Fifth Floor,
00019  *  Boston, MA 02110-1301, USA.
00020  */
00021 
00022 #include "katelayoutcache.h"
00023 #include "katelayoutcache.moc"
00024 
00025 #include <QtAlgorithms>
00026 
00027 #include "katerenderer.h"
00028 #include "kateview.h"
00029 #include "katedocument.h"
00030 #include "katebuffer.h"
00031 
00032 static bool enableLayoutCache = false;
00033 
00034 //BEGIN KateLineLayoutMap
00035 KateLineLayoutMap::KateLineLayoutMap()
00036 {
00037 }
00038 
00039 KateLineLayoutMap::~KateLineLayoutMap()
00040 {
00041 }
00042 
00043 bool lessThan(const KateLineLayoutMap::LineLayoutPair& lhs,
00044               const KateLineLayoutMap::LineLayoutPair& rhs)
00045 {
00046   return lhs.first < rhs.first;
00047 }
00048 
00049 void KateLineLayoutMap::clear()
00050 {
00051   m_lineLayouts.clear();
00052 }
00053 
00054 bool KateLineLayoutMap::contains(int i) const
00055 {
00056   LineLayoutMap::const_iterator it =
00057     qBinaryFind(m_lineLayouts.constBegin(), m_lineLayouts.constEnd(), LineLayoutPair(i,KateLineLayoutPtr()), lessThan);
00058   return (it != m_lineLayouts.constEnd());
00059 }
00060 
00061 void KateLineLayoutMap::insert(int realLine, const KateLineLayoutPtr& lineLayoutPtr)
00062 {
00063   LineLayoutMap::iterator it =
00064     qBinaryFind(m_lineLayouts.begin(), m_lineLayouts.end(), LineLayoutPair(realLine,KateLineLayoutPtr()), lessThan);
00065   if (it != m_lineLayouts.end()) {
00066     (*it).second = lineLayoutPtr;
00067   } else {
00068     it = qUpperBound(m_lineLayouts.begin(), m_lineLayouts.end(), LineLayoutPair(realLine,KateLineLayoutPtr()), lessThan);
00069     m_lineLayouts.insert(it, LineLayoutPair(realLine, lineLayoutPtr));
00070   }
00071 }
00072 
00073 void KateLineLayoutMap::viewWidthIncreased()
00074 {
00075   LineLayoutMap::iterator it = m_lineLayouts.begin();
00076   for ( ; it != m_lineLayouts.end(); ++it) {
00077     if ((*it).second->isValid() && (*it).second->viewLineCount() > 1)
00078       (*it).second->invalidateLayout();
00079   }
00080 }
00081 
00082 void KateLineLayoutMap::viewWidthDecreased(int newWidth)
00083 {
00084   LineLayoutMap::iterator it = m_lineLayouts.begin();
00085   for ( ; it != m_lineLayouts.end(); ++it) {
00086     if ((*it).second->isValid()
00087         && ((*it).second->viewLineCount() > 1 || (*it).second->width() > newWidth))
00088       (*it).second->invalidateLayout();
00089   }
00090 }
00091 
00092 void KateLineLayoutMap::relayoutLines(int startRealLine, int endRealLine)
00093 {
00094   LineLayoutMap::iterator start =
00095       qLowerBound(m_lineLayouts.begin(), m_lineLayouts.end(), LineLayoutPair(startRealLine, KateLineLayoutPtr()), lessThan);
00096   LineLayoutMap::iterator end =
00097       qUpperBound(start, m_lineLayouts.end(), LineLayoutPair(endRealLine, KateLineLayoutPtr()), lessThan);
00098 
00099   while (start != end) {
00100     (*start).second->setLayoutDirty();
00101     ++start;
00102   }
00103 }
00104 
00105 void KateLineLayoutMap::slotEditDone(int fromLine, int toLine, int shiftAmount)
00106 {
00107   LineLayoutMap::iterator start =
00108       qLowerBound(m_lineLayouts.begin(), m_lineLayouts.end(), LineLayoutPair(fromLine, KateLineLayoutPtr()), lessThan);
00109   LineLayoutMap::iterator end =
00110       qUpperBound(start, m_lineLayouts.end(), LineLayoutPair(toLine, KateLineLayoutPtr()), lessThan);
00111   LineLayoutMap::iterator it;
00112 
00113   if (shiftAmount != 0) {
00114     for (it = end; it != m_lineLayouts.end(); ++it) {
00115       (*it).first += shiftAmount;
00116       (*it).second->setLine((*it).second->line() + shiftAmount);
00117     }
00118 
00119     for (it = start; it != end; ++it) {
00120       (*it).second->clear();
00121     }
00122 
00123     m_lineLayouts.erase(start, end);
00124   } else {
00125     for (it = start; it != end; ++it) {
00126       (*it).second->setLayoutDirty();
00127     }
00128   }
00129 }
00130 
00131 
00132 KateLineLayoutPtr& KateLineLayoutMap::operator[](int i)
00133 {
00134   LineLayoutMap::iterator it =
00135     qBinaryFind(m_lineLayouts.begin(), m_lineLayouts.end(), LineLayoutPair(i, KateLineLayoutPtr()), lessThan);
00136   return (*it).second;
00137 }
00138 //END KateLineLayoutMap
00139 
00140 KateLayoutCache::KateLayoutCache(KateRenderer* renderer, QObject* parent)
00141   : QObject(parent)
00142   , m_renderer(renderer)
00143   , m_startPos(-1,-1)
00144   , m_viewWidth(0)
00145   , m_wrap(false)
00146   , m_acceptDirtyLayouts (false)
00147 {
00148   Q_ASSERT(m_renderer);
00149   
00153   connect(&m_renderer->doc()->buffer(), SIGNAL(lineWrapped(const KTextEditor::Cursor&)), this, SLOT(wrapLine(const KTextEditor::Cursor&)));
00154   connect(&m_renderer->doc()->buffer(), SIGNAL(lineUnwrapped(int)), this, SLOT(unwrapLine(int)));
00155   connect(&m_renderer->doc()->buffer(), SIGNAL(textInserted(const KTextEditor::Cursor &, const QString &)), this, SLOT(insertText(const KTextEditor::Cursor &, const QString &)));
00156   connect(&m_renderer->doc()->buffer(), SIGNAL(textRemoved(const KTextEditor::Range &, const QString &)), this, SLOT(removeText(const KTextEditor::Range &)));
00157 }
00158 
00159 void KateLayoutCache::updateViewCache(const KTextEditor::Cursor& startPos, int newViewLineCount, int viewLinesScrolled)
00160 {
00161   //kDebug( 13033 ) << startPos << " nvlc " << newViewLineCount << " vls " << viewLinesScrolled;
00162 
00163   int oldViewLineCount = m_textLayouts.count();
00164   if (newViewLineCount == -1)
00165     newViewLineCount = oldViewLineCount;
00166 
00167   enableLayoutCache = true;
00168 
00169   int realLine = m_renderer->doc()->getRealLine(startPos.line());
00170   int _viewLine = 0;
00171 
00172   if (wrap()) {
00173     // TODO check these assumptions are ok... probably they don't give much speedup anyway?
00174     if (startPos == m_startPos && m_textLayouts.count()) {
00175       _viewLine = m_textLayouts.first().viewLine();
00176 
00177     } else if (viewLinesScrolled > 0 && viewLinesScrolled < m_textLayouts.count()) {
00178       _viewLine = m_textLayouts[viewLinesScrolled].viewLine();
00179 
00180     } else {
00181       KateLineLayoutPtr l = line(realLine);
00182       if (l) {
00183         Q_ASSERT(l->isValid());
00184         Q_ASSERT(l->length() >= startPos.column() || m_renderer->view()->wrapCursor());
00185 
00186         for (; _viewLine < l->viewLineCount(); ++_viewLine) {
00187           const KateTextLayout& t = l->viewLine(_viewLine);
00188           if (t.startCol() >= startPos.column() || _viewLine == l->viewLineCount() - 1)
00189             goto foundViewLine;
00190         }
00191 
00192         // FIXME FIXME need to calculate past-end-of-line position here...
00193         Q_ASSERT(false);
00194 
00195         foundViewLine:
00196         Q_ASSERT(true);
00197       }
00198     }
00199   }
00200 
00201   m_startPos = startPos;
00202 
00203   // Move the text layouts if we've just scrolled...
00204   if (viewLinesScrolled != 0) {
00205     // loop backwards if we've just scrolled up...
00206     bool forwards = viewLinesScrolled >= 0 ? true : false;
00207     for (int z = forwards ? 0 : m_textLayouts.count() - 1; forwards ? (z < m_textLayouts.count()) : (z >= 0); forwards ? z++ : z--) {
00208       int oldZ = z + viewLinesScrolled;
00209       if (oldZ >= 0 && oldZ < m_textLayouts.count())
00210         m_textLayouts[z] = m_textLayouts[oldZ];
00211     }
00212   }
00213 
00214   // Resize functionality
00215   if (newViewLineCount > oldViewLineCount) {
00216     m_textLayouts.reserve(newViewLineCount);
00217 
00218   } else if (newViewLineCount < oldViewLineCount) {
00219     /* FIXME reintroduce... check we're not missing any
00220     int lastLine = m_textLayouts[newSize - 1].line();
00221     for (int i = oldSize; i < newSize; i++) {
00222       const KateTextLayout& layout = m_textLayouts[i];
00223       if (layout.line() > lastLine && !layout.viewLine())
00224         layout.kateLineLayout()->layout()->setCacheEnabled(false);
00225     }*/
00226     m_textLayouts.resize(newViewLineCount);
00227   }
00228 
00229   KateLineLayoutPtr l = line(realLine);
00230   for (int i = 0; i < newViewLineCount; ++i) {
00231     if (!l) {
00232       if (i < m_textLayouts.count()) {
00233         if (m_textLayouts[i].isValid())
00234           m_textLayouts[i] = KateTextLayout::invalid();
00235       } else {
00236         m_textLayouts.append(KateTextLayout::invalid());
00237       }
00238       continue;
00239     }
00240 
00241     Q_ASSERT(l->isValid());
00242     Q_ASSERT(_viewLine < l->viewLineCount());
00243 
00244     if (i < m_textLayouts.count()) {
00245       bool dirty = false;
00246       if (m_textLayouts[i].line() != realLine || m_textLayouts[i].viewLine() != _viewLine || (!m_textLayouts[i].isValid() && l->viewLine(_viewLine).isValid()))
00247         dirty = true;
00248       m_textLayouts[i] = l->viewLine(_viewLine);
00249       if (dirty)
00250         m_textLayouts[i].setDirty(true);
00251 
00252     } else {
00253       m_textLayouts.append(l->viewLine(_viewLine));
00254     }
00255 
00256     //kDebug( 13033 ) << "Laid out line " << realLine << " (" << l << "), viewLine " << _viewLine << " (" << m_textLayouts[i].kateLineLayout().data() << ")";
00257     //m_textLayouts[i].debugOutput();
00258 
00259     _viewLine++;
00260 
00261     if (_viewLine > l->viewLineCount() - 1) {
00262       int virtualLine = l->virtualLine() + 1;
00263       realLine = m_renderer->doc()->getRealLine(virtualLine);
00264       _viewLine = 0;
00265       if (realLine < m_renderer->doc()->lines()) {
00266         l = line(realLine, virtualLine);
00267       } else {
00268         l = 0;
00269       }
00270     }
00271   }
00272 
00273   enableLayoutCache = false;
00274 }
00275 
00276 KateLineLayoutPtr KateLayoutCache::line( int realLine, int virtualLine )
00277 {
00278   if (m_lineLayouts.contains(realLine)) {
00279     KateLineLayoutPtr l = m_lineLayouts[realLine];
00280 
00281     if (virtualLine != -1)
00282       l->setVirtualLine(virtualLine);
00283 
00284     if (!l->isValid())
00285     {
00286       l->setUsePlainTextLine (acceptDirtyLayouts());
00287       l->textLine (!acceptDirtyLayouts());
00288       m_renderer->layoutLine(l, wrap() ? m_viewWidth : -1, enableLayoutCache);
00289     }
00290     else if (l->isLayoutDirty() && !acceptDirtyLayouts())
00291     {
00292       // reset textline
00293       l->setUsePlainTextLine (false);
00294       l->textLine (true);
00295       m_renderer->layoutLine(l, wrap() ? m_viewWidth : -1, enableLayoutCache);
00296     }
00297 
00298     Q_ASSERT(l->isValid() && (!l->isLayoutDirty() || acceptDirtyLayouts()));
00299 
00300     return l;
00301   }
00302 
00303   if (realLine < 0 || realLine >= m_renderer->doc()->lines())
00304     return KateLineLayoutPtr();
00305 
00306   KateLineLayoutPtr l(new KateLineLayout(m_renderer->doc()));
00307   l->setLine(realLine, virtualLine);
00308 
00309   // Mark it dirty, because it may not have the syntax highlighting applied
00310   // mark this here, to allow layoutLine to use plainLines...
00311   if (acceptDirtyLayouts())
00312     l->setUsePlainTextLine (true);
00313 
00314   m_renderer->layoutLine(l, wrap() ? m_viewWidth : -1, enableLayoutCache);
00315   Q_ASSERT(l->isValid());
00316 
00317   if (acceptDirtyLayouts())
00318     l->setLayoutDirty (true);
00319 
00320   m_lineLayouts.insert(realLine, l);
00321   return l;
00322 }
00323 
00324 KateLineLayoutPtr KateLayoutCache::line( const KTextEditor::Cursor & realCursor )
00325 {
00326   return line(realCursor.line());
00327 }
00328 
00329 KateTextLayout KateLayoutCache::textLayout( const KTextEditor::Cursor & realCursor )
00330 {
00331   /*if (realCursor >= viewCacheStart() && (realCursor < viewCacheEnd() || realCursor == viewCacheEnd() && !m_textLayouts.last().wrap()))
00332     foreach (const KateTextLayout& l, m_textLayouts)
00333       if (l.line() == realCursor.line() && (l.endCol() < realCursor.column() || !l.wrap()))
00334         return l;*/
00335 
00336   return line(realCursor.line())->viewLine(viewLine(realCursor));
00337 }
00338 
00339 KateTextLayout KateLayoutCache::textLayout( uint realLine, int _viewLine )
00340 {
00341   /*if (m_textLayouts.count() && (realLine >= m_textLayouts.first().line() && _viewLine >= m_textLayouts.first().viewLine()) &&
00342       (realLine <= m_textLayouts.last().line() && _viewLine <= m_textLayouts.first().viewLine()))
00343     foreach (const KateTextLayout& l, m_textLayouts)
00344       if (l.line() == realLine && l.viewLine() == _viewLine)
00345         return const_cast<KateTextLayout&>(l);*/
00346 
00347   return line(realLine)->viewLine(_viewLine);
00348 }
00349 
00350 KateTextLayout & KateLayoutCache::viewLine( int _viewLine )
00351 {
00352   Q_ASSERT(_viewLine >= 0 && _viewLine < m_textLayouts.count());
00353   return m_textLayouts[_viewLine];
00354 }
00355 
00356 int KateLayoutCache::viewCacheLineCount( ) const
00357 {
00358   return m_textLayouts.count();
00359 }
00360 
00361 KTextEditor::Cursor KateLayoutCache::viewCacheStart( ) const
00362 {
00363   return m_textLayouts.count() ? m_textLayouts.first().start() : KTextEditor::Cursor();
00364 }
00365 
00366 KTextEditor::Cursor KateLayoutCache::viewCacheEnd( ) const
00367 {
00368   return m_textLayouts.count() ? m_textLayouts.last().end() : KTextEditor::Cursor();
00369 }
00370 
00371 int KateLayoutCache::viewWidth( ) const
00372 {
00373   return m_viewWidth;
00374 }
00375 
00381 int KateLayoutCache::viewLine(const KTextEditor::Cursor& realCursor)
00382 {
00383   if (realCursor.column() == 0) return 0;
00384 
00385   KateLineLayoutPtr thisLine = line(realCursor.line());
00386 
00387   for (int i = 0; i < thisLine->viewLineCount(); ++i) {
00388     const KateTextLayout& l = thisLine->viewLine(i);
00389     if (realCursor.column() >= l.startCol() && realCursor.column() < l.endCol())
00390       return i;
00391   }
00392 
00393   return thisLine->viewLineCount() - 1;
00394 }
00395 
00396 int KateLayoutCache::displayViewLine(const KTextEditor::Cursor& virtualCursor, bool limitToVisible)
00397 {
00398   KTextEditor::Cursor work = viewCacheStart();
00399   work.setLine(m_renderer->doc()->getVirtualLine(work.line()));
00400 
00401   if (!work.isValid())
00402     return virtualCursor.line();
00403 
00404   int limit = m_textLayouts.count();
00405 
00406   // Efficient non-word-wrapped path
00407   if (!m_renderer->view()->dynWordWrap()) {
00408     int ret = virtualCursor.line() - work.line();
00409     if (limitToVisible && (ret < 0 || ret > limit))
00410       return -1;
00411     else
00412       return ret;
00413   }
00414 
00415   if (work == virtualCursor) {
00416     return 0;
00417   }
00418 
00419   int ret = -(int)viewLine(viewCacheStart());
00420   bool forwards = (work < virtualCursor);
00421 
00422   // FIXME switch to using ranges? faster?
00423   if (forwards) {
00424     while (work.line() != virtualCursor.line()) {
00425       ret += viewLineCount(m_renderer->doc()->getRealLine(work.line()));
00426       work.setLine(work.line() + 1);
00427       if (limitToVisible && ret > limit)
00428         return -1;
00429     }
00430   } else {
00431     while (work.line() != virtualCursor.line()) {
00432       work.setLine(work.line() - 1);
00433       ret -= viewLineCount(m_renderer->doc()->getRealLine(work.line()));
00434       if (limitToVisible && ret < 0)
00435         return -1;
00436     }
00437   }
00438 
00439   // final difference
00440   KTextEditor::Cursor realCursor = virtualCursor;
00441   realCursor.setLine(m_renderer->doc()->getRealLine(realCursor.line()));
00442   if (realCursor.column() == -1) realCursor.setColumn(m_renderer->doc()->lineLength(realCursor.line()));
00443   ret += viewLine(realCursor);
00444 
00445   if (limitToVisible && (ret < 0 || ret > limit))
00446     return -1;
00447 
00448   return ret;
00449 }
00450 
00451 int KateLayoutCache::lastViewLine(int realLine)
00452 {
00453   if (!m_renderer->view()->dynWordWrap()) return 0;
00454 
00455   KateLineLayoutPtr l = line(realLine);
00456   Q_ASSERT(l);
00457   return l->viewLineCount() - 1;
00458 }
00459 
00460 int KateLayoutCache::viewLineCount(int realLine)
00461 {
00462   return lastViewLine(realLine) + 1;
00463 }
00464 
00465 void KateLayoutCache::viewCacheDebugOutput( ) const
00466 {
00467   kDebug( 13033 ) << "Printing values for " << m_textLayouts.count() << " lines:";
00468   if (m_textLayouts.count())
00469   foreach (const KateTextLayout& t, m_textLayouts)
00470     if (t.isValid())
00471       t.debugOutput();
00472     else
00473       kDebug( 13033 ) << "Line Invalid.";
00474 }
00475 
00476 void KateLayoutCache::wrapLine (const KTextEditor::Cursor &position)
00477 {
00478    m_lineLayouts.slotEditDone (position.line(), position.line() + 1, 1);
00479 }
00480 
00481 void KateLayoutCache::unwrapLine (int line)
00482 {
00483    m_lineLayouts.slotEditDone (line - 1, line, -1);
00484 }
00485 
00486 void KateLayoutCache::insertText (const KTextEditor::Cursor &position, const QString &)
00487 {
00488    m_lineLayouts.slotEditDone(position.line(), position.line(), 0);
00489 }
00490 
00491 void KateLayoutCache::removeText (const KTextEditor::Range &range)
00492 {
00493    m_lineLayouts.slotEditDone(range.start().line(), range.start().line(), 0);
00494 }
00495 
00496 void KateLayoutCache::clear( )
00497 {
00498   m_textLayouts.clear();
00499   m_lineLayouts.clear();
00500   m_startPos = KTextEditor::Cursor(-1,-1);
00501 }
00502 
00503 void KateLayoutCache::setViewWidth( int width )
00504 {
00505   bool wider = width > m_viewWidth;
00506 
00507   m_viewWidth = width;
00508 
00509   m_lineLayouts.clear();
00510   m_startPos = KTextEditor::Cursor(-1,-1);
00511 
00512   // Only get rid of layouts that we have to
00513   if (wider) {
00514     m_lineLayouts.viewWidthIncreased();
00515   } else {
00516     m_lineLayouts.viewWidthDecreased(width);
00517   }
00518 }
00519 
00520 bool KateLayoutCache::wrap( ) const
00521 {
00522   return m_wrap;
00523 }
00524 
00525 void KateLayoutCache::setWrap( bool wrap )
00526 {
00527   m_wrap = wrap;
00528   clear();
00529 }
00530 
00531 void KateLayoutCache::relayoutLines( int startRealLine, int endRealLine )
00532 {
00533   if (startRealLine > endRealLine)
00534     kWarning() << "start" << startRealLine << "before end" << endRealLine;
00535 
00536   m_lineLayouts.relayoutLines(startRealLine, endRealLine);
00537 }
00538 
00539 bool KateLayoutCache::acceptDirtyLayouts()
00540 {
00541   return m_acceptDirtyLayouts;
00542 }
00543 
00544 void KateLayoutCache::setAcceptDirtyLayouts(bool accept)
00545 {
00546   m_acceptDirtyLayouts = accept;
00547 }
00548 
00549 // 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