• 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   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       m_renderer->layoutLine(l, wrap() ? m_viewWidth : -1, enableLayoutCache);
00275 
00276     else if (l->isLayoutDirty() && !acceptDirtyLayouts())
00277       m_renderer->layoutLine(l, wrap() ? m_viewWidth : -1, enableLayoutCache);
00278 
00279     Q_ASSERT(l->isValid() && (!l->isLayoutDirty() || acceptDirtyLayouts()));
00280 
00281     return l;
00282   }
00283 
00284   if (realLine < 0 || realLine >= m_renderer->doc()->lines())
00285     return KateLineLayoutPtr();
00286 
00287   KateLineLayoutPtr l(new KateLineLayout(m_renderer->doc()));
00288   l->setLine(realLine, virtualLine);
00289   m_renderer->layoutLine(l, wrap() ? m_viewWidth : -1, enableLayoutCache);
00290   Q_ASSERT(l->isValid());
00291 
00292   if (acceptDirtyLayouts())
00293     // Mark it dirty, because it may not have the syntax highlighting applied
00294     l->setLayoutDirty(true);
00295 
00296   m_lineLayouts.insert(realLine, l);
00297   return l;
00298 }
00299 
00300 KateLineLayoutPtr KateLayoutCache::line( const KTextEditor::Cursor & realCursor ) const
00301 {
00302   return line(realCursor.line());
00303 }
00304 
00305 KateTextLayout KateLayoutCache::textLayout( const KTextEditor::Cursor & realCursor ) const
00306 {
00307   /*if (realCursor >= viewCacheStart() && (realCursor < viewCacheEnd() || realCursor == viewCacheEnd() && !m_textLayouts.last().wrap()))
00308     foreach (const KateTextLayout& l, m_textLayouts)
00309       if (l.line() == realCursor.line() && (l.endCol() < realCursor.column() || !l.wrap()))
00310         return l;*/
00311 
00312   return line(realCursor.line())->viewLine(viewLine(realCursor));
00313 }
00314 
00315 KateTextLayout KateLayoutCache::textLayout( uint realLine, int _viewLine ) const
00316 {
00317   /*if (m_textLayouts.count() && (realLine >= m_textLayouts.first().line() && _viewLine >= m_textLayouts.first().viewLine()) &&
00318       (realLine <= m_textLayouts.last().line() && _viewLine <= m_textLayouts.first().viewLine()))
00319     foreach (const KateTextLayout& l, m_textLayouts)
00320       if (l.line() == realLine && l.viewLine() == _viewLine)
00321         return const_cast<KateTextLayout&>(l);*/
00322 
00323   return line(realLine)->viewLine(_viewLine);
00324 }
00325 
00326 KateTextLayout & KateLayoutCache::viewLine( int _viewLine ) const
00327 {
00328   Q_ASSERT(_viewLine >= 0 && _viewLine < m_textLayouts.count());
00329   return m_textLayouts[_viewLine];
00330 }
00331 
00332 int KateLayoutCache::viewCacheLineCount( ) const
00333 {
00334   return m_textLayouts.count();
00335 }
00336 
00337 KTextEditor::Cursor KateLayoutCache::viewCacheStart( ) const
00338 {
00339   return m_textLayouts.count() ? m_textLayouts.first().start() : KTextEditor::Cursor();
00340 }
00341 
00342 KTextEditor::Cursor KateLayoutCache::viewCacheEnd( ) const
00343 {
00344   return m_textLayouts.count() ? m_textLayouts.last().end() : KTextEditor::Cursor();
00345 }
00346 
00347 int KateLayoutCache::viewWidth( ) const
00348 {
00349   return m_viewWidth;
00350 }
00351 
00357 int KateLayoutCache::viewLine(const KTextEditor::Cursor& realCursor) const
00358 {
00359   if (realCursor.column() == 0) return 0;
00360 
00361   KateLineLayoutPtr thisLine = line(realCursor.line());
00362 
00363   for (int i = 0; i < thisLine->viewLineCount(); ++i) {
00364     const KateTextLayout& l = thisLine->viewLine(i);
00365     if (realCursor.column() >= l.startCol() && realCursor.column() < l.endCol())
00366       return i;
00367   }
00368 
00369   return thisLine->viewLineCount() - 1;
00370 }
00371 
00372 int KateLayoutCache::displayViewLine(const KTextEditor::Cursor& virtualCursor, bool limitToVisible) const
00373 {
00374   KTextEditor::Cursor work = viewCacheStart();
00375   work.setLine(m_renderer->doc()->getVirtualLine(work.line()));
00376 
00377   if (!work.isValid())
00378     return virtualCursor.line();
00379 
00380   int limit = m_textLayouts.count();
00381 
00382   // Efficient non-word-wrapped path
00383   if (!m_renderer->view()->dynWordWrap()) {
00384     int ret = virtualCursor.line() - work.line();
00385     if (limitToVisible && (ret < 0 || ret > limit))
00386       return -1;
00387     else
00388       return ret;
00389   }
00390 
00391   if (work == virtualCursor) {
00392     return 0;
00393   }
00394 
00395   int ret = -(int)viewLine(work);
00396   bool forwards = (work < virtualCursor) ? true : false;
00397 
00398   // FIXME switch to using ranges? faster?
00399   if (forwards) {
00400     while (work.line() != virtualCursor.line()) {
00401       ret += viewLineCount(m_renderer->doc()->getRealLine(work.line()));
00402       work.setLine(work.line() + 1);
00403       if (limitToVisible && ret > limit)
00404         return -1;
00405     }
00406   } else {
00407     while (work.line() != virtualCursor.line()) {
00408       work.setLine(work.line() - 1);
00409       ret -= viewLineCount(m_renderer->doc()->getRealLine(work.line()));
00410       if (limitToVisible && ret < 0)
00411         return -1;
00412     }
00413   }
00414 
00415   // final difference
00416   KTextEditor::Cursor realCursor = virtualCursor;
00417   realCursor.setLine(m_renderer->doc()->getRealLine(realCursor.line()));
00418   if (realCursor.column() == -1) realCursor.setColumn(m_renderer->doc()->lineLength(realCursor.line()));
00419   ret += viewLine(realCursor);
00420 
00421   if (limitToVisible && (ret < 0 || ret > limit))
00422     return -1;
00423 
00424   return ret;
00425 }
00426 
00427 int KateLayoutCache::lastViewLine(int realLine) const
00428 {
00429   if (!m_renderer->view()->dynWordWrap()) return 0;
00430 
00431   KateLineLayoutPtr l = line(realLine);
00432   Q_ASSERT(l);
00433   return l->viewLineCount() - 1;
00434 }
00435 
00436 int KateLayoutCache::viewLineCount(int realLine) const
00437 {
00438   return lastViewLine(realLine) + 1;
00439 }
00440 
00441 void KateLayoutCache::viewCacheDebugOutput( ) const
00442 {
00443   kDebug( 13033 ) << "Printing values for " << m_textLayouts.count() << " lines:";
00444   if (m_textLayouts.count())
00445   foreach (const KateTextLayout& t, m_textLayouts)
00446     if (t.isValid())
00447       t.debugOutput();
00448     else
00449       kDebug( 13033 ) << "Line Invalid.";
00450 }
00451 
00452 void KateLayoutCache::slotEditDone(KateEditInfo* edit)
00453 {
00454   int fromLine = edit->oldRange().start().line();
00455   int toLine = edit->oldRange().end().line();
00456   int shiftAmount = edit->translate().line();
00457 
00458   m_lineLayouts.slotEditDone(fromLine, toLine, shiftAmount);
00459 }
00460 
00461 void KateLayoutCache::clear( )
00462 {
00463   m_textLayouts.clear();
00464   m_lineLayouts.clear();
00465   m_startPos = KTextEditor::Cursor(-1,-1);
00466 }
00467 
00468 void KateLayoutCache::setViewWidth( int width )
00469 {
00470   bool wider = width > m_viewWidth;
00471 
00472   m_viewWidth = width;
00473 
00474   m_lineLayouts.clear();
00475   m_startPos = KTextEditor::Cursor(-1,-1);
00476 
00477   // Only get rid of layouts that we have to
00478   if (wider) {
00479     m_lineLayouts.viewWidthIncreased();
00480   } else {
00481     m_lineLayouts.viewWidthDecreased(width);
00482   }
00483 }
00484 
00485 bool KateLayoutCache::wrap( ) const
00486 {
00487   return m_wrap;
00488 }
00489 
00490 void KateLayoutCache::setWrap( bool wrap )
00491 {
00492   m_wrap = wrap;
00493   clear();
00494 }
00495 
00496 void KateLayoutCache::relayoutLines( int startRealLine, int endRealLine )
00497 {
00498   if (startRealLine > endRealLine)
00499     kWarning() << "start" << startRealLine << "before end" << endRealLine;
00500 
00501   m_lineLayouts.relayoutLines(startRealLine, endRealLine);
00502 }
00503 
00504 bool KateLayoutCache::acceptDirtyLayouts() const
00505 {
00506   if (m_acceptDirtyLayouts.hasLocalData())
00507     return *m_acceptDirtyLayouts.localData();
00508 
00509   return false;
00510 }
00511 
00512 void KateLayoutCache::setAcceptDirtyLayouts(bool accept)
00513 {
00514   if (!m_acceptDirtyLayouts.hasLocalData())
00515     m_acceptDirtyLayouts.setLocalData(new bool);
00516 
00517   *m_acceptDirtyLayouts.localData() = accept;
00518 }
00519 
00520 #include "katelayoutcache.moc"
00521 
00522 // 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.6
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