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
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
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
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
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
00186 Q_ASSERT(false);
00187
00188 foundViewLine:
00189 Q_ASSERT(true);
00190 }
00191 }
00192 }
00193
00194 m_startPos = startPos;
00195
00196
00197 if (viewLinesScrolled != 0) {
00198
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
00208 if (newViewLineCount > oldViewLineCount) {
00209 m_textLayouts.reserve(newViewLineCount);
00210
00211 } else if (newViewLineCount < oldViewLineCount) {
00212
00213
00214
00215
00216
00217
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
00250
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
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
00299
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
00321
00322
00323
00324
00325 return line(realCursor.line())->viewLine(viewLine(realCursor));
00326 }
00327
00328 KateTextLayout KateLayoutCache::textLayout( uint realLine, int _viewLine ) const
00329 {
00330
00331
00332
00333
00334
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
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
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
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
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