00001
00002
00003
00004
00005
00006
00007
00008
00009
00010
00011
00012
00013
00014
00015
00016
00017
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
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
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
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
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
00190 Q_ASSERT(false);
00191
00192 foundViewLine:
00193 Q_ASSERT(true);
00194 }
00195 }
00196 }
00197
00198 m_startPos = startPos;
00199
00200
00201 if (viewLinesScrolled != 0) {
00202
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
00212 if (newViewLineCount > oldViewLineCount) {
00213 m_textLayouts.reserve(newViewLineCount);
00214
00215 } else if (newViewLineCount < oldViewLineCount) {
00216
00217
00218
00219
00220
00221
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
00254
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
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
00305
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
00330
00331
00332
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
00341
00342
00343
00344
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
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
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
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
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