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;
KDE 4.6 API Reference