• Skip to content
  • Skip to link menu
KDE 4.3 API Reference
  • KDE API Reference
  • kdelibs
  • Sitemap
  • Contact Us
 

Kate

katelayoutcache.cpp

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