• Skip to content
  • Skip to link menu
KDE 4.2 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 //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 {
00147   Q_ASSERT(m_renderer);
00148 
00149   connect(m_renderer->doc()->history(), SIGNAL(editDone(KateEditInfo*)), SLOT(slotEditDone(KateEditInfo*)));
00150 }
00151 
00152 void KateLayoutCache::updateViewCache(const KTextEditor::Cursor& startPos, int newViewLineCount, int viewLinesScrolled)
00153 {
00154   //kDebug( 13033 ) << startPos << " nvlc " << newViewLineCount << " vls " << viewLinesScrolled;
00155 
00156   int oldViewLineCount = m_textLayouts.count();
00157   if (newViewLineCount == -1)
00158     newViewLineCount = oldViewLineCount;
00159 
00160   enableLayoutCache = true;
00161 
00162   int realLine = m_renderer->doc()->getRealLine(startPos.line());
00163   int _viewLine = 0;
00164 
00165   if (wrap()) {
00166     // TODO check these assumptions are ok... probably they don't give much speedup anyway?
00167     if (startPos == m_startPos && m_textLayouts.count()) {
00168       _viewLine = m_textLayouts.first().viewLine();
00169 
00170     } else if (viewLinesScrolled > 0 && viewLinesScrolled < m_textLayouts.count()) {
00171       _viewLine = m_textLayouts[viewLinesScrolled].viewLine();
00172 
00173     } else {
00174       KateLineLayoutPtr l = line(realLine);
00175       if (l) {
00176         Q_ASSERT(l->isValid());
00177         Q_ASSERT(l->length() >= startPos.column() || m_renderer->view()->wrapCursor());
00178 
00179         for (; _viewLine < l->viewLineCount(); ++_viewLine) {
00180           const KateTextLayout& t = l->viewLine(_viewLine);
00181           if (t.startCol() >= startPos.column() || _viewLine == l->viewLineCount() - 1)
00182             goto foundViewLine;
00183         }
00184 
00185         // FIXME FIXME need to calculate past-end-of-line position here...
00186         Q_ASSERT(false);
00187 
00188         foundViewLine:
00189         Q_ASSERT(true);
00190       }
00191     }
00192   }
00193 
00194   m_startPos = startPos;
00195 
00196   // Move the text layouts if we've just scrolled...
00197   if (viewLinesScrolled != 0) {
00198     // loop backwards if we've just scrolled up...
00199     bool forwards = viewLinesScrolled >= 0 ? true : false;
00200     for (int z = forwards ? 0 : m_textLayouts.count() - 1; forwards ? (z < m_textLayouts.count()) : (z >= 0); forwards ? z++ : z--) {
00201       int oldZ = z + viewLinesScrolled;
00202       if (oldZ >= 0 && oldZ < m_textLayouts.count())
00203         m_textLayouts[z] = m_textLayouts[oldZ];
00204     }
00205   }
00206 
00207   // Resize functionality
00208   if (newViewLineCount > oldViewLineCount) {
00209     m_textLayouts.reserve(newViewLineCount);
00210 
00211   } else if (newViewLineCount < oldViewLineCount) {
00212     /* FIXME reintroduce... check we're not missing any
00213     int lastLine = m_textLayouts[newSize - 1].line();
00214     for (int i = oldSize; i < newSize; i++) {
00215       const KateTextLayout& layout = m_textLayouts[i];
00216       if (layout.line() > lastLine && !layout.viewLine())
00217         layout.kateLineLayout()->layout()->setCacheEnabled(false);
00218     }*/
00219     m_textLayouts.resize(newViewLineCount);
00220   }
00221 
00222   KateLineLayoutPtr l = line(realLine);
00223   for (int i = 0; i < newViewLineCount; ++i) {
00224     if (!l) {
00225       if (i < m_textLayouts.count()) {
00226         if (m_textLayouts[i].isValid())
00227           m_textLayouts[i] = KateTextLayout::invalid();
00228       } else {
00229         m_textLayouts.append(KateTextLayout::invalid());
00230       }
00231       continue;
00232     }
00233 
00234     Q_ASSERT(l->isValid());
00235     Q_ASSERT(_viewLine < l->viewLineCount());
00236 
00237     if (i < m_textLayouts.count()) {
00238       bool dirty = false;
00239       if (m_textLayouts[i].line() != realLine || m_textLayouts[i].viewLine() != _viewLine || (!m_textLayouts[i].isValid() && l->viewLine(_viewLine).isValid()))
00240         dirty = true;
00241       m_textLayouts[i] = l->viewLine(_viewLine);
00242       if (dirty)
00243         m_textLayouts[i].setDirty(true);
00244 
00245     } else {
00246       m_textLayouts.append(l->viewLine(_viewLine));
00247     }
00248 
00249     //kDebug( 13033 ) << "Laid out line " << realLine << " (" << l << "), viewLine " << _viewLine << " (" << m_textLayouts[i].kateLineLayout().data() << ")";
00250     //m_textLayouts[i].debugOutput();
00251 
00252     _viewLine++;
00253 
00254     if (_viewLine > l->viewLineCount() - 1) {
00255       int virtualLine = l->virtualLine() + 1;
00256       realLine = m_renderer->doc()->getRealLine(virtualLine);
00257       _viewLine = 0;
00258       l = line(realLine, virtualLine);
00259     }
00260   }
00261 
00262   enableLayoutCache = false;
00263 }
00264 
00265 KateLineLayoutPtr KateLayoutCache::line( int realLine, int virtualLine ) const
00266 {
00267   if (m_lineLayouts.contains(realLine)) {
00268     KateLineLayoutPtr l = m_lineLayouts[realLine];
00269 
00270     if (virtualLine != -1)
00271       l->setVirtualLine(virtualLine);
00272 
00273     if (!l->isValid())
00274     {
00275       l->setUsePlainTextLine (acceptDirtyLayouts());
00276       l->textLine (!acceptDirtyLayouts());
00277       m_renderer->layoutLine(l, wrap() ? m_viewWidth : -1, enableLayoutCache);
00278     }
00279     else if (l->isLayoutDirty() && !acceptDirtyLayouts())
00280     {
00281       // reset textline
00282       l->setUsePlainTextLine (false);
00283       l->textLine (true);
00284       m_renderer->layoutLine(l, wrap() ? m_viewWidth : -1, enableLayoutCache);
00285     }
00286     
00287     Q_ASSERT(l->isValid() && (!l->isLayoutDirty() || acceptDirtyLayouts()));
00288 
00289     return l;
00290   }
00291 
00292   if (realLine < 0 || realLine >= m_renderer->doc()->lines())
00293     return KateLineLayoutPtr();
00294 
00295   KateLineLayoutPtr l(new KateLineLayout(m_renderer->doc()));
00296   l->setLine(realLine, virtualLine);
00297   
00298   // Mark it dirty, because it may not have the syntax highlighting applied
00299   // mark this here, to allow layoutLine to use plainLines...
00300   if (acceptDirtyLayouts())
00301     l->setUsePlainTextLine (true);
00302   
00303   m_renderer->layoutLine(l, wrap() ? m_viewWidth : -1, enableLayoutCache);
00304   Q_ASSERT(l->isValid());
00305   
00306   if (acceptDirtyLayouts())
00307     l->setLayoutDirty (true);
00308   
00309   m_lineLayouts.insert(realLine, l);
00310   return l;
00311 }
00312 
00313 KateLineLayoutPtr KateLayoutCache::line( const KTextEditor::Cursor & realCursor ) const
00314 {
00315   return line(realCursor.line());
00316 }
00317 
00318 KateTextLayout KateLayoutCache::textLayout( const KTextEditor::Cursor & realCursor ) const
00319 {
00320   /*if (realCursor >= viewCacheStart() && (realCursor < viewCacheEnd() || realCursor == viewCacheEnd() && !m_textLayouts.last().wrap()))
00321     foreach (const KateTextLayout& l, m_textLayouts)
00322       if (l.line() == realCursor.line() && (l.endCol() < realCursor.column() || !l.wrap()))
00323         return l;*/
00324 
00325   return line(realCursor.line())->viewLine(viewLine(realCursor));
00326 }
00327 
00328 KateTextLayout KateLayoutCache::textLayout( uint realLine, int _viewLine ) const
00329 {
00330   /*if (m_textLayouts.count() && (realLine >= m_textLayouts.first().line() && _viewLine >= m_textLayouts.first().viewLine()) &&
00331       (realLine <= m_textLayouts.last().line() && _viewLine <= m_textLayouts.first().viewLine()))
00332     foreach (const KateTextLayout& l, m_textLayouts)
00333       if (l.line() == realLine && l.viewLine() == _viewLine)
00334         return const_cast<KateTextLayout&>(l);*/
00335 
00336   return line(realLine)->viewLine(_viewLine);
00337 }
00338 
00339 KateTextLayout & KateLayoutCache::viewLine( int _viewLine ) const
00340 {
00341   Q_ASSERT(_viewLine >= 0 && _viewLine < m_textLayouts.count());
00342   return m_textLayouts[_viewLine];
00343 }
00344 
00345 int KateLayoutCache::viewCacheLineCount( ) const
00346 {
00347   return m_textLayouts.count();
00348 }
00349 
00350 KTextEditor::Cursor KateLayoutCache::viewCacheStart( ) const
00351 {
00352   return m_textLayouts.count() ? m_textLayouts.first().start() : KTextEditor::Cursor();
00353 }
00354 
00355 KTextEditor::Cursor KateLayoutCache::viewCacheEnd( ) const
00356 {
00357   return m_textLayouts.count() ? m_textLayouts.last().end() : KTextEditor::Cursor();
00358 }
00359 
00360 int KateLayoutCache::viewWidth( ) const
00361 {
00362   return m_viewWidth;
00363 }
00364 
00370 int KateLayoutCache::viewLine(const KTextEditor::Cursor& realCursor) const
00371 {
00372   if (realCursor.column() == 0) return 0;
00373 
00374   KateLineLayoutPtr thisLine = line(realCursor.line());
00375 
00376   for (int i = 0; i < thisLine->viewLineCount(); ++i) {
00377     const KateTextLayout& l = thisLine->viewLine(i);
00378     if (realCursor.column() >= l.startCol() && realCursor.column() < l.endCol())
00379       return i;
00380   }
00381 
00382   return thisLine->viewLineCount() - 1;
00383 }
00384 
00385 int KateLayoutCache::displayViewLine(const KTextEditor::Cursor& virtualCursor, bool limitToVisible) const
00386 {
00387   KTextEditor::Cursor work = viewCacheStart();
00388   work.setLine(m_renderer->doc()->getVirtualLine(work.line()));
00389 
00390   if (!work.isValid())
00391     return virtualCursor.line();
00392 
00393   int limit = m_textLayouts.count();
00394 
00395   // Efficient non-word-wrapped path
00396   if (!m_renderer->view()->dynWordWrap()) {
00397     int ret = virtualCursor.line() - work.line();
00398     if (limitToVisible && (ret < 0 || ret > limit))
00399       return -1;
00400     else
00401       return ret;
00402   }
00403 
00404   if (work == virtualCursor) {
00405     return 0;
00406   }
00407 
00408   int ret = -(int)viewLine(work);
00409   bool forwards = (work < virtualCursor) ? true : false;
00410 
00411   // FIXME switch to using ranges? faster?
00412   if (forwards) {
00413     while (work.line() != virtualCursor.line()) {
00414       ret += viewLineCount(m_renderer->doc()->getRealLine(work.line()));
00415       work.setLine(work.line() + 1);
00416       if (limitToVisible && ret > limit)
00417         return -1;
00418     }
00419   } else {
00420     while (work.line() != virtualCursor.line()) {
00421       work.setLine(work.line() - 1);
00422       ret -= viewLineCount(m_renderer->doc()->getRealLine(work.line()));
00423       if (limitToVisible && ret < 0)
00424         return -1;
00425     }
00426   }
00427 
00428   // final difference
00429   KTextEditor::Cursor realCursor = virtualCursor;
00430   realCursor.setLine(m_renderer->doc()->getRealLine(realCursor.line()));
00431   if (realCursor.column() == -1) realCursor.setColumn(m_renderer->doc()->lineLength(realCursor.line()));
00432   ret += viewLine(realCursor);
00433 
00434   if (limitToVisible && (ret < 0 || ret > limit))
00435     return -1;
00436 
00437   return ret;
00438 }
00439 
00440 int KateLayoutCache::lastViewLine(int realLine) const
00441 {
00442   if (!m_renderer->view()->dynWordWrap()) return 0;
00443 
00444   KateLineLayoutPtr l = line(realLine);
00445   Q_ASSERT(l);
00446   return l->viewLineCount() - 1;
00447 }
00448 
00449 int KateLayoutCache::viewLineCount(int realLine) const
00450 {
00451   return lastViewLine(realLine) + 1;
00452 }
00453 
00454 void KateLayoutCache::viewCacheDebugOutput( ) const
00455 {
00456   kDebug( 13033 ) << "Printing values for " << m_textLayouts.count() << " lines:";
00457   if (m_textLayouts.count())
00458   foreach (const KateTextLayout& t, m_textLayouts)
00459     if (t.isValid())
00460       t.debugOutput();
00461     else
00462       kDebug( 13033 ) << "Line Invalid.";
00463 }
00464 
00465 void KateLayoutCache::slotEditDone(KateEditInfo* edit)
00466 {
00467   int fromLine = edit->oldRange().start().line();
00468   int toLine = edit->oldRange().end().line();
00469   int shiftAmount = edit->translate().line();
00470 
00471   m_lineLayouts.slotEditDone(fromLine, toLine, shiftAmount);
00472 }
00473 
00474 void KateLayoutCache::clear( )
00475 {
00476   m_textLayouts.clear();
00477   m_lineLayouts.clear();
00478   m_startPos = KTextEditor::Cursor(-1,-1);
00479 }
00480 
00481 void KateLayoutCache::setViewWidth( int width )
00482 {
00483   bool wider = width > m_viewWidth;
00484 
00485   m_viewWidth = width;
00486 
00487   m_lineLayouts.clear();
00488   m_startPos = KTextEditor::Cursor(-1,-1);
00489 
00490   // Only get rid of layouts that we have to
00491   if (wider) {
00492     m_lineLayouts.viewWidthIncreased();
00493   } else {
00494     m_lineLayouts.viewWidthDecreased(width);
00495   }
00496 }
00497 
00498 bool KateLayoutCache::wrap( ) const
00499 {
00500   return m_wrap;
00501 }
00502 
00503 void KateLayoutCache::setWrap( bool wrap )
00504 {
00505   m_wrap = wrap;
00506   clear();
00507 }
00508 
00509 void KateLayoutCache::relayoutLines( int startRealLine, int endRealLine )
00510 {
00511   if (startRealLine > endRealLine)
00512     kWarning() << "start" << startRealLine << "before end" << endRealLine;
00513 
00514   m_lineLayouts.relayoutLines(startRealLine, endRealLine);
00515 }
00516 
00517 bool KateLayoutCache::acceptDirtyLayouts() const
00518 {
00519   if (m_acceptDirtyLayouts.hasLocalData())
00520     return *m_acceptDirtyLayouts.localData();
00521 
00522   return false;
00523 }
00524 
00525 void KateLayoutCache::setAcceptDirtyLayouts(bool accept)
00526 {
00527   if (!m_acceptDirtyLayouts.hasLocalData())
00528     m_acceptDirtyLayouts.setLocalData(new bool);
00529 
00530   *m_acceptDirtyLayouts.localData() = accept;
00531 }
00532 
00533 #include "katelayoutcache.moc"
00534 
00535 // 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
  • Kross
  • KUtils
  • Nepomuk
  • Plasma
  • Solid
  • Sonnet
  • ThreadWeaver
Generated for kdelibs by doxygen 1.5.7
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