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 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
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
00308
00309
00310
00311
00312 return line(realCursor.line())->viewLine(viewLine(realCursor));
00313 }
00314
00315 KateTextLayout KateLayoutCache::textLayout( uint realLine, int _viewLine ) const
00316 {
00317
00318
00319
00320
00321
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
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
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
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
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