• Skip to content
  • Skip to link menu
KDE 4.1 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 }
00052 
00053 bool KateLineLayoutMap::contains(int i) const
00054 {
00055   LineLayoutMap::const_iterator it =
00056     qBinaryFind(m_lineLayouts.constBegin(), m_lineLayouts.constEnd(), LineLayoutPair(i,KateLineLayoutPtr()), lessThan);
00057   return (it != m_lineLayouts.constEnd());
00058 }
00059 
00060 void KateLineLayoutMap::insert(int realLine, const KateLineLayoutPtr& lineLayoutPtr)
00061 {
00062   LineLayoutMap::iterator it =
00063     qBinaryFind(m_lineLayouts.begin(), m_lineLayouts.end(), LineLayoutPair(realLine,KateLineLayoutPtr()), lessThan);
00064   if (it != m_lineLayouts.end()) {
00065     (*it).second = lineLayoutPtr;
00066   } else {
00067     it = qUpperBound(m_lineLayouts.begin(), m_lineLayouts.end(), LineLayoutPair(realLine,KateLineLayoutPtr()), lessThan);
00068     m_lineLayouts.insert(it, LineLayoutPair(realLine, lineLayoutPtr));
00069   }
00070 }
00071 
00072 void KateLineLayoutMap::viewWidthIncreased()
00073 {
00074   LineLayoutMap::iterator it = m_lineLayouts.begin();
00075   for ( ; it != m_lineLayouts.end(); ++it) {
00076     if ((*it).second->isValid() && (*it).second->viewLineCount() > 1)
00077       (*it).second->invalidateLayout();
00078   }
00079 }
00080 
00081 void KateLineLayoutMap::viewWidthDecreased(int newWidth)
00082 {
00083   LineLayoutMap::iterator it = m_lineLayouts.begin();
00084   for ( ; it != m_lineLayouts.end(); ++it) {
00085     if ((*it).second->isValid()
00086         && ((*it).second->viewLineCount() > 1 || (*it).second->width() > newWidth))
00087       (*it).second->invalidateLayout();
00088   }
00089 }
00090 
00091 void KateLineLayoutMap::relayoutLines(int startRealLine, int endRealLine)
00092 {
00093   LineLayoutMap::iterator start =
00094       qLowerBound(m_lineLayouts.begin(), m_lineLayouts.end(), LineLayoutPair(startRealLine, KateLineLayoutPtr()), lessThan);
00095   LineLayoutMap::iterator end =
00096       qUpperBound(start, m_lineLayouts.end(), LineLayoutPair(endRealLine, KateLineLayoutPtr()), lessThan);
00097 
00098   while (start != end) {
00099     (*start).second->setLayoutDirty();
00100     ++start;
00101   }
00102 }
00103 
00104 void KateLineLayoutMap::slotEditDone(int fromLine, int toLine, int shiftAmount)
00105 {
00106   LineLayoutMap::iterator start =
00107       qLowerBound(m_lineLayouts.begin(), m_lineLayouts.end(), LineLayoutPair(fromLine, KateLineLayoutPtr()), lessThan);
00108   LineLayoutMap::iterator end =
00109       qUpperBound(start, m_lineLayouts.end(), LineLayoutPair(toLine, KateLineLayoutPtr()), lessThan);
00110   LineLayoutMap::iterator it;
00111 
00112   if (shiftAmount != 0) {
00113     for (it = end; it != m_lineLayouts.end(); ++it) {
00114       (*it).first += shiftAmount;
00115       (*it).second->setLine((*it).second->line() + shiftAmount);
00116     }
00117 
00118     for (it = start; it != end; ++it) {
00119       (*it).second->clear();
00120     }
00121 
00122     m_lineLayouts.erase(start, end);
00123   } else {
00124     for (it = start; it != end; ++it) {
00125       (*it).second->setLayoutDirty();
00126     }
00127   }
00128 }
00129 
00130 
00131 KateLineLayoutPtr& KateLineLayoutMap::operator[](int i)
00132 {
00133   LineLayoutMap::iterator it =
00134     qBinaryFind(m_lineLayouts.begin(), m_lineLayouts.end(), LineLayoutPair(i, KateLineLayoutPtr()), lessThan);
00135   return (*it).second;
00136 }
00137 //END KateLineLayoutMap
00138 
00139 KateLayoutCache::KateLayoutCache(KateRenderer* renderer, QObject* parent)
00140   : QObject(parent)
00141   , m_renderer(renderer)
00142   , m_startPos(-1,-1)
00143   , m_viewWidth(0)
00144   , m_wrap(false)
00145 {
00146   Q_ASSERT(m_renderer);
00147 
00148   connect(m_renderer->doc()->history(), SIGNAL(editDone(KateEditInfo*)), SLOT(slotEditDone(KateEditInfo*)));
00149 }
00150 
00151 void KateLayoutCache::updateViewCache(const KTextEditor::Cursor& startPos, int newViewLineCount, int viewLinesScrolled)
00152 {
00153   //kDebug( 13033 ) << startPos << " nvlc " << newViewLineCount << " vls " << viewLinesScrolled;
00154 
00155   int oldViewLineCount = m_textLayouts.count();
00156   if (newViewLineCount == -1)
00157     newViewLineCount = oldViewLineCount;
00158 
00159   enableLayoutCache = true;
00160 
00161   int realLine = m_renderer->doc()->getRealLine(startPos.line());
00162   int _viewLine = 0;
00163 
00164   if (wrap()) {
00165     // TODO check these assumptions are ok... probably they don't give much speedup anyway?
00166     if (startPos == m_startPos && m_textLayouts.count()) {
00167       _viewLine = m_textLayouts.first().viewLine();
00168 
00169     } else if (viewLinesScrolled > 0 && viewLinesScrolled < m_textLayouts.count()) {
00170       _viewLine = m_textLayouts[viewLinesScrolled].viewLine();
00171 
00172     } else {
00173       KateLineLayoutPtr l = line(realLine);
00174       if (l) {
00175         Q_ASSERT(l->isValid());
00176         Q_ASSERT(l->length() >= startPos.column() || m_renderer->view()->wrapCursor());
00177 
00178         for (; _viewLine < l->viewLineCount(); ++_viewLine) {
00179           const KateTextLayout& t = l->viewLine(_viewLine);
00180           if (t.startCol() >= startPos.column() || _viewLine == l->viewLineCount() - 1)
00181             goto foundViewLine;
00182         }
00183 
00184         // FIXME FIXME need to calculate past-end-of-line position here...
00185         Q_ASSERT(false);
00186 
00187         foundViewLine:
00188         Q_ASSERT(true);
00189       }
00190     }
00191   }
00192 
00193   m_startPos = startPos;
00194 
00195   // Move the text layouts if we've just scrolled...
00196   if (viewLinesScrolled != 0) {
00197     // loop backwards if we've just scrolled up...
00198     bool forwards = viewLinesScrolled >= 0 ? true : false;
00199     for (int z = forwards ? 0 : m_textLayouts.count() - 1; forwards ? (z < m_textLayouts.count()) : (z >= 0); forwards ? z++ : z--) {
00200       int oldZ = z + viewLinesScrolled;
00201       if (oldZ >= 0 && oldZ < m_textLayouts.count())
00202         m_textLayouts[z] = m_textLayouts[oldZ];
00203     }
00204   }
00205 
00206   // Resize functionality
00207   if (newViewLineCount > oldViewLineCount) {
00208     m_textLayouts.reserve(newViewLineCount);
00209 
00210   } else if (newViewLineCount < oldViewLineCount) {
00211     /* FIXME reintroduce... check we're not missing any
00212     int lastLine = m_textLayouts[newSize - 1].line();
00213     for (int i = oldSize; i < newSize; i++) {
00214       const KateTextLayout& layout = m_textLayouts[i];
00215       if (layout.line() > lastLine && !layout.viewLine())
00216         layout.kateLineLayout()->layout()->setCacheEnabled(false);
00217     }*/
00218     m_textLayouts.resize(newViewLineCount);
00219   }
00220 
00221   KateLineLayoutPtr l = line(realLine);
00222   for (int i = 0; i < newViewLineCount; ++i) {
00223     if (!l) {
00224       if (i < m_textLayouts.count()) {
00225         if (m_textLayouts[i].isValid())
00226           m_textLayouts[i] = KateTextLayout::invalid();
00227       } else {
00228         m_textLayouts.append(KateTextLayout::invalid());
00229       }
00230       continue;
00231     }
00232 
00233     Q_ASSERT(l->isValid());
00234     Q_ASSERT(_viewLine < l->viewLineCount());
00235 
00236     if (i < m_textLayouts.count()) {
00237       bool dirty = false;
00238       if (m_textLayouts[i].line() != realLine || m_textLayouts[i].viewLine() != _viewLine || (!m_textLayouts[i].isValid() && l->viewLine(_viewLine).isValid()))
00239         dirty = true;
00240       m_textLayouts[i] = l->viewLine(_viewLine);
00241       if (dirty)
00242         m_textLayouts[i].setDirty(true);
00243 
00244     } else {
00245       m_textLayouts.append(l->viewLine(_viewLine));
00246     }
00247 
00248     //kDebug( 13033 ) << "Laid out line " << realLine << " (" << l << "), viewLine " << _viewLine << " (" << m_textLayouts[i].kateLineLayout().data() << ")";
00249     //m_textLayouts[i].debugOutput();
00250 
00251     _viewLine++;
00252 
00253     if (_viewLine > l->viewLineCount() - 1) {
00254       int virtualLine = l->virtualLine() + 1;
00255       realLine = m_renderer->doc()->getRealLine(virtualLine);
00256       _viewLine = 0;
00257       l = line(realLine, virtualLine);
00258     }
00259   }
00260 
00261   enableLayoutCache = false;
00262 }
00263 
00264 KateLineLayoutPtr KateLayoutCache::line( int realLine, int virtualLine ) const
00265 {
00266   if (m_lineLayouts.contains(realLine)) {
00267     KateLineLayoutPtr l = m_lineLayouts[realLine];
00268 
00269     if (virtualLine != -1)
00270       l->setVirtualLine(virtualLine);
00271 
00272     if (!l->isValid())
00273       m_renderer->layoutLine(l, wrap() ? m_viewWidth : -1, enableLayoutCache);
00274 
00275     else if (l->isLayoutDirty() && !acceptDirtyLayouts())
00276       m_renderer->layoutLine(l, wrap() ? m_viewWidth : -1, enableLayoutCache);
00277 
00278     Q_ASSERT(l->isValid() && (!l->isLayoutDirty() || acceptDirtyLayouts()));
00279 
00280     return l;
00281   }
00282 
00283   if (realLine < 0 || realLine >= m_renderer->doc()->lines())
00284     return KateLineLayoutPtr();
00285 
00286   KateLineLayoutPtr l(new KateLineLayout(m_renderer->doc()));
00287   l->setLine(realLine, virtualLine);
00288   m_renderer->layoutLine(l, wrap() ? m_viewWidth : -1, enableLayoutCache);
00289   Q_ASSERT(l->isValid());
00290 
00291   if (acceptDirtyLayouts())
00292     // Mark it dirty, because it may not have the syntax highlighting applied
00293     l->setLayoutDirty(true);
00294 
00295   m_lineLayouts.insert(realLine, l);
00296   return l;
00297 }
00298 
00299 KateLineLayoutPtr KateLayoutCache::line( const KTextEditor::Cursor & realCursor ) const
00300 {
00301   return line(realCursor.line());
00302 }
00303 
00304 KateTextLayout KateLayoutCache::textLayout( const KTextEditor::Cursor & realCursor ) const
00305 {
00306   /*if (realCursor >= viewCacheStart() && (realCursor < viewCacheEnd() || realCursor == viewCacheEnd() && !m_textLayouts.last().wrap()))
00307     foreach (const KateTextLayout& l, m_textLayouts)
00308       if (l.line() == realCursor.line() && (l.endCol() < realCursor.column() || !l.wrap()))
00309         return l;*/
00310 
00311   return line(realCursor.line())->viewLine(viewLine(realCursor));
00312 }
00313 
00314 KateTextLayout KateLayoutCache::textLayout( uint realLine, int _viewLine ) const
00315 {
00316   /*if (m_textLayouts.count() && (realLine >= m_textLayouts.first().line() && _viewLine >= m_textLayouts.first().viewLine()) &&
00317       (realLine <= m_textLayouts.last().line() && _viewLine <= m_textLayouts.first().viewLine()))
00318     foreach (const KateTextLayout& l, m_textLayouts)
00319       if (l.line() == realLine && l.viewLine() == _viewLine)
00320         return const_cast<KateTextLayout&>(l);*/
00321 
00322   return line(realLine)->viewLine(_viewLine);
00323 }
00324 
00325 KateTextLayout & KateLayoutCache::viewLine( int _viewLine ) const
00326 {
00327   Q_ASSERT(_viewLine >= 0 && _viewLine < m_textLayouts.count());
00328   return m_textLayouts[_viewLine];
00329 }
00330 
00331 int KateLayoutCache::viewCacheLineCount( ) const
00332 {
00333   return m_textLayouts.count();
00334 }
00335 
00336 KTextEditor::Cursor KateLayoutCache::viewCacheStart( ) const
00337 {
00338   return m_textLayouts.count() ? m_textLayouts.first().start() : KTextEditor::Cursor();
00339 }
00340 
00341 KTextEditor::Cursor KateLayoutCache::viewCacheEnd( ) const
00342 {
00343   return m_textLayouts.count() ? m_textLayouts.last().end() : KTextEditor::Cursor();
00344 }
00345 
00346 int KateLayoutCache::viewWidth( ) const
00347 {
00348   return m_viewWidth;
00349 }
00350 
00356 int KateLayoutCache::viewLine(const KTextEditor::Cursor& realCursor) const
00357 {
00358   if (realCursor.column() == 0) return 0;
00359 
00360   KateLineLayoutPtr thisLine = line(realCursor.line());
00361 
00362   for (int i = 0; i < thisLine->viewLineCount(); ++i) {
00363     const KateTextLayout& l = thisLine->viewLine(i);
00364     if (realCursor.column() >= l.startCol() && realCursor.column() < l.endCol())
00365       return i;
00366   }
00367 
00368   return thisLine->viewLineCount() - 1;
00369 }
00370 
00371 int KateLayoutCache::displayViewLine(const KTextEditor::Cursor& virtualCursor, bool limitToVisible) const
00372 {
00373   KTextEditor::Cursor work = viewCacheStart();
00374   work.setLine(m_renderer->doc()->getVirtualLine(work.line()));
00375 
00376   if (!work.isValid())
00377     return virtualCursor.line();
00378 
00379   int limit = m_textLayouts.count();
00380 
00381   // Efficient non-word-wrapped path
00382   if (!m_renderer->view()->dynWordWrap()) {
00383     int ret = virtualCursor.line() - work.line();
00384     if (limitToVisible && (ret < 0 || ret > limit))
00385       return -1;
00386     else
00387       return ret;
00388   }
00389 
00390   if (work == virtualCursor) {
00391     return 0;
00392   }
00393 
00394   int ret = -(int)viewLine(work);
00395   bool forwards = (work < virtualCursor) ? true : false;
00396 
00397   // FIXME switch to using ranges? faster?
00398   if (forwards) {
00399     while (work.line() != virtualCursor.line()) {
00400       ret += viewLineCount(m_renderer->doc()->getRealLine(work.line()));
00401       work.setLine(work.line() + 1);
00402       if (limitToVisible && ret > limit)
00403         return -1;
00404     }
00405   } else {
00406     while (work.line() != virtualCursor.line()) {
00407       work.setLine(work.line() - 1);
00408       ret -= viewLineCount(m_renderer->doc()->getRealLine(work.line()));
00409       if (limitToVisible && ret < 0)
00410         return -1;
00411     }
00412   }
00413 
00414   // final difference
00415   KTextEditor::Cursor realCursor = virtualCursor;
00416   realCursor.setLine(m_renderer->doc()->getRealLine(realCursor.line()));
00417   if (realCursor.column() == -1) realCursor.setColumn(m_renderer->doc()->lineLength(realCursor.line()));
00418   ret += viewLine(realCursor);
00419 
00420   if (limitToVisible && (ret < 0 || ret > limit))
00421     return -1;
00422 
00423   return ret;
00424 }
00425 
00426 int KateLayoutCache::lastViewLine(int realLine) const
00427 {
00428   if (!m_renderer->view()->dynWordWrap()) return 0;
00429 
00430   KateLineLayoutPtr l = line(realLine);
00431   Q_ASSERT(l);
00432   return l->viewLineCount() - 1;
00433 }
00434 
00435 int KateLayoutCache::viewLineCount(int realLine) const
00436 {
00437   return lastViewLine(realLine) + 1;
00438 }
00439 
00440 void KateLayoutCache::viewCacheDebugOutput( ) const
00441 {
00442   kDebug( 13033 ) << "Printing values for " << m_textLayouts.count() << " lines:";
00443   if (m_textLayouts.count())
00444   foreach (const KateTextLayout& t, m_textLayouts)
00445     if (t.isValid())
00446       t.debugOutput();
00447     else
00448       kDebug( 13033 ) << "Line Invalid.";
00449 }
00450 
00451 void KateLayoutCache::slotEditDone(KateEditInfo* edit)
00452 {
00453   int fromLine = edit->oldRange().start().line();
00454   int toLine = edit->oldRange().end().line();
00455   int shiftAmount = edit->translate().line();
00456 
00457   m_lineLayouts.slotEditDone(fromLine, toLine, shiftAmount);
00458 }
00459 
00460 void KateLayoutCache::clear( )
00461 {
00462   m_textLayouts.clear();
00463   m_lineLayouts.clear();
00464   m_startPos = KTextEditor::Cursor(-1,-1);
00465 }
00466 
00467 void KateLayoutCache::setViewWidth( int width )
00468 {
00469   bool wider = width > m_viewWidth;
00470 
00471   m_viewWidth = width;
00472 
00473   m_lineLayouts.clear();
00474   m_startPos = KTextEditor::Cursor(-1,-1);
00475 
00476   // Only get rid of layouts that we have to
00477   if (wider) {
00478     m_lineLayouts.viewWidthIncreased();
00479   } else {
00480     m_lineLayouts.viewWidthDecreased(width);
00481   }
00482 }
00483 
00484 bool KateLayoutCache::wrap( ) const
00485 {
00486   return m_wrap;
00487 }
00488 
00489 void KateLayoutCache::setWrap( bool wrap )
00490 {
00491   m_wrap = wrap;
00492   clear();
00493 }
00494 
00495 void KateLayoutCache::relayoutLines( int startRealLine, int endRealLine )
00496 {
00497   if (startRealLine > endRealLine)
00498     kWarning() << "start" << startRealLine << "before end" << endRealLine;
00499 
00500   m_lineLayouts.relayoutLines(startRealLine, endRealLine);
00501 }
00502 
00503 bool KateLayoutCache::acceptDirtyLayouts() const
00504 {
00505   if (m_acceptDirtyLayouts.hasLocalData())
00506     return *m_acceptDirtyLayouts.localData();
00507 
00508   return false;
00509 }
00510 
00511 void KateLayoutCache::setAcceptDirtyLayouts(bool accept)
00512 {
00513   if (!m_acceptDirtyLayouts.hasLocalData())
00514     m_acceptDirtyLayouts.setLocalData(new bool);
00515 
00516   *m_acceptDirtyLayouts.localData() = accept;
00517 }
00518 
00519 #include "katelayoutcache.moc"
00520 
00521 // 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
  • Solid
  • Sonnet
  • ThreadWeaver
Generated for kdelibs by doxygen 1.5.4
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