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 }
00052
00053 bool KateLineLayoutMap::contains(int i) const
00054 {
00055 LineLayoutMap::const_iterator it =
00056 qBinaryFind(m_lineLayouts.constBegin(), m_lineLayouts.constEnd(), LineLayoutPair(i,KateLineLayoutPtr()), lessThan);
00057 return (it != m_lineLayouts.constEnd());
00058 }
00059
00060 void KateLineLayoutMap::insert(int realLine, const KateLineLayoutPtr& lineLayoutPtr)
00061 {
00062 LineLayoutMap::iterator it =
00063 qBinaryFind(m_lineLayouts.begin(), m_lineLayouts.end(), LineLayoutPair(realLine,KateLineLayoutPtr()), lessThan);
00064 if (it != m_lineLayouts.end()) {
00065 (*it).second = lineLayoutPtr;
00066 } else {
00067 it = qUpperBound(m_lineLayouts.begin(), m_lineLayouts.end(), LineLayoutPair(realLine,KateLineLayoutPtr()), lessThan);
00068 m_lineLayouts.insert(it, LineLayoutPair(realLine, lineLayoutPtr));
00069 }
00070 }
00071
00072 void KateLineLayoutMap::viewWidthIncreased()
00073 {
00074 LineLayoutMap::iterator it = m_lineLayouts.begin();
00075 for ( ; it != m_lineLayouts.end(); ++it) {
00076 if ((*it).second->isValid() && (*it).second->viewLineCount() > 1)
00077 (*it).second->invalidateLayout();
00078 }
00079 }
00080
00081 void KateLineLayoutMap::viewWidthDecreased(int newWidth)
00082 {
00083 LineLayoutMap::iterator it = m_lineLayouts.begin();
00084 for ( ; it != m_lineLayouts.end(); ++it) {
00085 if ((*it).second->isValid()
00086 && ((*it).second->viewLineCount() > 1 || (*it).second->width() > newWidth))
00087 (*it).second->invalidateLayout();
00088 }
00089 }
00090
00091 void KateLineLayoutMap::relayoutLines(int startRealLine, int endRealLine)
00092 {
00093 LineLayoutMap::iterator start =
00094 qLowerBound(m_lineLayouts.begin(), m_lineLayouts.end(), LineLayoutPair(startRealLine, KateLineLayoutPtr()), lessThan);
00095 LineLayoutMap::iterator end =
00096 qUpperBound(start, m_lineLayouts.end(), LineLayoutPair(endRealLine, KateLineLayoutPtr()), lessThan);
00097
00098 while (start != end) {
00099 (*start).second->setLayoutDirty();
00100 ++start;
00101 }
00102 }
00103
00104 void KateLineLayoutMap::slotEditDone(int fromLine, int toLine, int shiftAmount)
00105 {
00106 LineLayoutMap::iterator start =
00107 qLowerBound(m_lineLayouts.begin(), m_lineLayouts.end(), LineLayoutPair(fromLine, KateLineLayoutPtr()), lessThan);
00108 LineLayoutMap::iterator end =
00109 qUpperBound(start, m_lineLayouts.end(), LineLayoutPair(toLine, KateLineLayoutPtr()), lessThan);
00110 LineLayoutMap::iterator it;
00111
00112 if (shiftAmount != 0) {
00113 for (it = end; it != m_lineLayouts.end(); ++it) {
00114 (*it).first += shiftAmount;
00115 (*it).second->setLine((*it).second->line() + shiftAmount);
00116 }
00117
00118 for (it = start; it != end; ++it) {
00119 (*it).second->clear();
00120 }
00121
00122 m_lineLayouts.erase(start, end);
00123 } else {
00124 for (it = start; it != end; ++it) {
00125 (*it).second->setLayoutDirty();
00126 }
00127 }
00128 }
00129
00130
00131 KateLineLayoutPtr& KateLineLayoutMap::operator[](int i)
00132 {
00133 LineLayoutMap::iterator it =
00134 qBinaryFind(m_lineLayouts.begin(), m_lineLayouts.end(), LineLayoutPair(i, KateLineLayoutPtr()), lessThan);
00135 return (*it).second;
00136 }
00137
00138
00139 KateLayoutCache::KateLayoutCache(KateRenderer* renderer, QObject* parent)
00140 : QObject(parent)
00141 , m_renderer(renderer)
00142 , m_startPos(-1,-1)
00143 , m_viewWidth(0)
00144 , m_wrap(false)
00145 {
00146 Q_ASSERT(m_renderer);
00147
00148 connect(m_renderer->doc()->history(), SIGNAL(editDone(KateEditInfo*)), SLOT(slotEditDone(KateEditInfo*)));
00149 }
00150
00151 void KateLayoutCache::updateViewCache(const KTextEditor::Cursor& startPos, int newViewLineCount, int viewLinesScrolled)
00152 {
00153
00154
00155 int oldViewLineCount = m_textLayouts.count();
00156 if (newViewLineCount == -1)
00157 newViewLineCount = oldViewLineCount;
00158
00159 enableLayoutCache = true;
00160
00161 int realLine = m_renderer->doc()->getRealLine(startPos.line());
00162 int _viewLine = 0;
00163
00164 if (wrap()) {
00165
00166 if (startPos == m_startPos && m_textLayouts.count()) {
00167 _viewLine = m_textLayouts.first().viewLine();
00168
00169 } else if (viewLinesScrolled > 0 && viewLinesScrolled < m_textLayouts.count()) {
00170 _viewLine = m_textLayouts[viewLinesScrolled].viewLine();
00171
00172 } else {
00173 KateLineLayoutPtr l = line(realLine);
00174 if (l) {
00175 Q_ASSERT(l->isValid());
00176 Q_ASSERT(l->length() >= startPos.column() || m_renderer->view()->wrapCursor());
00177
00178 for (; _viewLine < l->viewLineCount(); ++_viewLine) {
00179 const KateTextLayout& t = l->viewLine(_viewLine);
00180 if (t.startCol() >= startPos.column() || _viewLine == l->viewLineCount() - 1)
00181 goto foundViewLine;
00182 }
00183
00184
00185 Q_ASSERT(false);
00186
00187 foundViewLine:
00188 Q_ASSERT(true);
00189 }
00190 }
00191 }
00192
00193 m_startPos = startPos;
00194
00195
00196 if (viewLinesScrolled != 0) {
00197
00198 bool forwards = viewLinesScrolled >= 0 ? true : false;
00199 for (int z = forwards ? 0 : m_textLayouts.count() - 1; forwards ? (z < m_textLayouts.count()) : (z >= 0); forwards ? z++ : z--) {
00200 int oldZ = z + viewLinesScrolled;
00201 if (oldZ >= 0 && oldZ < m_textLayouts.count())
00202 m_textLayouts[z] = m_textLayouts[oldZ];
00203 }
00204 }
00205
00206
00207 if (newViewLineCount > oldViewLineCount) {
00208 m_textLayouts.reserve(newViewLineCount);
00209
00210 } else if (newViewLineCount < oldViewLineCount) {
00211
00212
00213
00214
00215
00216
00217
00218 m_textLayouts.resize(newViewLineCount);
00219 }
00220
00221 KateLineLayoutPtr l = line(realLine);
00222 for (int i = 0; i < newViewLineCount; ++i) {
00223 if (!l) {
00224 if (i < m_textLayouts.count()) {
00225 if (m_textLayouts[i].isValid())
00226 m_textLayouts[i] = KateTextLayout::invalid();
00227 } else {
00228 m_textLayouts.append(KateTextLayout::invalid());
00229 }
00230 continue;
00231 }
00232
00233 Q_ASSERT(l->isValid());
00234 Q_ASSERT(_viewLine < l->viewLineCount());
00235
00236 if (i < m_textLayouts.count()) {
00237 bool dirty = false;
00238 if (m_textLayouts[i].line() != realLine || m_textLayouts[i].viewLine() != _viewLine || (!m_textLayouts[i].isValid() && l->viewLine(_viewLine).isValid()))
00239 dirty = true;
00240 m_textLayouts[i] = l->viewLine(_viewLine);
00241 if (dirty)
00242 m_textLayouts[i].setDirty(true);
00243
00244 } else {
00245 m_textLayouts.append(l->viewLine(_viewLine));
00246 }
00247
00248
00249
00250
00251 _viewLine++;
00252
00253 if (_viewLine > l->viewLineCount() - 1) {
00254 int virtualLine = l->virtualLine() + 1;
00255 realLine = m_renderer->doc()->getRealLine(virtualLine);
00256 _viewLine = 0;
00257 l = line(realLine, virtualLine);
00258 }
00259 }
00260
00261 enableLayoutCache = false;
00262 }
00263
00264 KateLineLayoutPtr KateLayoutCache::line( int realLine, int virtualLine ) const
00265 {
00266 if (m_lineLayouts.contains(realLine)) {
00267 KateLineLayoutPtr l = m_lineLayouts[realLine];
00268
00269 if (virtualLine != -1)
00270 l->setVirtualLine(virtualLine);
00271
00272 if (!l->isValid())
00273 m_renderer->layoutLine(l, wrap() ? m_viewWidth : -1, enableLayoutCache);
00274
00275 else if (l->isLayoutDirty() && !acceptDirtyLayouts())
00276 m_renderer->layoutLine(l, wrap() ? m_viewWidth : -1, enableLayoutCache);
00277
00278 Q_ASSERT(l->isValid() && (!l->isLayoutDirty() || acceptDirtyLayouts()));
00279
00280 return l;
00281 }
00282
00283 if (realLine < 0 || realLine >= m_renderer->doc()->lines())
00284 return KateLineLayoutPtr();
00285
00286 KateLineLayoutPtr l(new KateLineLayout(m_renderer->doc()));
00287 l->setLine(realLine, virtualLine);
00288 m_renderer->layoutLine(l, wrap() ? m_viewWidth : -1, enableLayoutCache);
00289 Q_ASSERT(l->isValid());
00290
00291 if (acceptDirtyLayouts())
00292
00293 l->setLayoutDirty(true);
00294
00295 m_lineLayouts.insert(realLine, l);
00296 return l;
00297 }
00298
00299 KateLineLayoutPtr KateLayoutCache::line( const KTextEditor::Cursor & realCursor ) const
00300 {
00301 return line(realCursor.line());
00302 }
00303
00304 KateTextLayout KateLayoutCache::textLayout( const KTextEditor::Cursor & realCursor ) const
00305 {
00306
00307
00308
00309
00310
00311 return line(realCursor.line())->viewLine(viewLine(realCursor));
00312 }
00313
00314 KateTextLayout KateLayoutCache::textLayout( uint realLine, int _viewLine ) const
00315 {
00316
00317
00318
00319
00320
00321
00322 return line(realLine)->viewLine(_viewLine);
00323 }
00324
00325 KateTextLayout & KateLayoutCache::viewLine( int _viewLine ) const
00326 {
00327 Q_ASSERT(_viewLine >= 0 && _viewLine < m_textLayouts.count());
00328 return m_textLayouts[_viewLine];
00329 }
00330
00331 int KateLayoutCache::viewCacheLineCount( ) const
00332 {
00333 return m_textLayouts.count();
00334 }
00335
00336 KTextEditor::Cursor KateLayoutCache::viewCacheStart( ) const
00337 {
00338 return m_textLayouts.count() ? m_textLayouts.first().start() : KTextEditor::Cursor();
00339 }
00340
00341 KTextEditor::Cursor KateLayoutCache::viewCacheEnd( ) const
00342 {
00343 return m_textLayouts.count() ? m_textLayouts.last().end() : KTextEditor::Cursor();
00344 }
00345
00346 int KateLayoutCache::viewWidth( ) const
00347 {
00348 return m_viewWidth;
00349 }
00350
00356 int KateLayoutCache::viewLine(const KTextEditor::Cursor& realCursor) const
00357 {
00358 if (realCursor.column() == 0) return 0;
00359
00360 KateLineLayoutPtr thisLine = line(realCursor.line());
00361
00362 for (int i = 0; i < thisLine->viewLineCount(); ++i) {
00363 const KateTextLayout& l = thisLine->viewLine(i);
00364 if (realCursor.column() >= l.startCol() && realCursor.column() < l.endCol())
00365 return i;
00366 }
00367
00368 return thisLine->viewLineCount() - 1;
00369 }
00370
00371 int KateLayoutCache::displayViewLine(const KTextEditor::Cursor& virtualCursor, bool limitToVisible) const
00372 {
00373 KTextEditor::Cursor work = viewCacheStart();
00374 work.setLine(m_renderer->doc()->getVirtualLine(work.line()));
00375
00376 if (!work.isValid())
00377 return virtualCursor.line();
00378
00379 int limit = m_textLayouts.count();
00380
00381
00382 if (!m_renderer->view()->dynWordWrap()) {
00383 int ret = virtualCursor.line() - work.line();
00384 if (limitToVisible && (ret < 0 || ret > limit))
00385 return -1;
00386 else
00387 return ret;
00388 }
00389
00390 if (work == virtualCursor) {
00391 return 0;
00392 }
00393
00394 int ret = -(int)viewLine(work);
00395 bool forwards = (work < virtualCursor) ? true : false;
00396
00397
00398 if (forwards) {
00399 while (work.line() != virtualCursor.line()) {
00400 ret += viewLineCount(m_renderer->doc()->getRealLine(work.line()));
00401 work.setLine(work.line() + 1);
00402 if (limitToVisible && ret > limit)
00403 return -1;
00404 }
00405 } else {
00406 while (work.line() != virtualCursor.line()) {
00407 work.setLine(work.line() - 1);
00408 ret -= viewLineCount(m_renderer->doc()->getRealLine(work.line()));
00409 if (limitToVisible && ret < 0)
00410 return -1;
00411 }
00412 }
00413
00414
00415 KTextEditor::Cursor realCursor = virtualCursor;
00416 realCursor.setLine(m_renderer->doc()->getRealLine(realCursor.line()));
00417 if (realCursor.column() == -1) realCursor.setColumn(m_renderer->doc()->lineLength(realCursor.line()));
00418 ret += viewLine(realCursor);
00419
00420 if (limitToVisible && (ret < 0 || ret > limit))
00421 return -1;
00422
00423 return ret;
00424 }
00425
00426 int KateLayoutCache::lastViewLine(int realLine) const
00427 {
00428 if (!m_renderer->view()->dynWordWrap()) return 0;
00429
00430 KateLineLayoutPtr l = line(realLine);
00431 Q_ASSERT(l);
00432 return l->viewLineCount() - 1;
00433 }
00434
00435 int KateLayoutCache::viewLineCount(int realLine) const
00436 {
00437 return lastViewLine(realLine) + 1;
00438 }
00439
00440 void KateLayoutCache::viewCacheDebugOutput( ) const
00441 {
00442 kDebug( 13033 ) << "Printing values for " << m_textLayouts.count() << " lines:";
00443 if (m_textLayouts.count())
00444 foreach (const KateTextLayout& t, m_textLayouts)
00445 if (t.isValid())
00446 t.debugOutput();
00447 else
00448 kDebug( 13033 ) << "Line Invalid.";
00449 }
00450
00451 void KateLayoutCache::slotEditDone(KateEditInfo* edit)
00452 {
00453 int fromLine = edit->oldRange().start().line();
00454 int toLine = edit->oldRange().end().line();
00455 int shiftAmount = edit->translate().line();
00456
00457 m_lineLayouts.slotEditDone(fromLine, toLine, shiftAmount);
00458 }
00459
00460 void KateLayoutCache::clear( )
00461 {
00462 m_textLayouts.clear();
00463 m_lineLayouts.clear();
00464 m_startPos = KTextEditor::Cursor(-1,-1);
00465 }
00466
00467 void KateLayoutCache::setViewWidth( int width )
00468 {
00469 bool wider = width > m_viewWidth;
00470
00471 m_viewWidth = width;
00472
00473 m_lineLayouts.clear();
00474 m_startPos = KTextEditor::Cursor(-1,-1);
00475
00476
00477 if (wider) {
00478 m_lineLayouts.viewWidthIncreased();
00479 } else {
00480 m_lineLayouts.viewWidthDecreased(width);
00481 }
00482 }
00483
00484 bool KateLayoutCache::wrap( ) const
00485 {
00486 return m_wrap;
00487 }
00488
00489 void KateLayoutCache::setWrap( bool wrap )
00490 {
00491 m_wrap = wrap;
00492 clear();
00493 }
00494
00495 void KateLayoutCache::relayoutLines( int startRealLine, int endRealLine )
00496 {
00497 if (startRealLine > endRealLine)
00498 kWarning() << "start" << startRealLine << "before end" << endRealLine;
00499
00500 m_lineLayouts.relayoutLines(startRealLine, endRealLine);
00501 }
00502
00503 bool KateLayoutCache::acceptDirtyLayouts() const
00504 {
00505 if (m_acceptDirtyLayouts.hasLocalData())
00506 return *m_acceptDirtyLayouts.localData();
00507
00508 return false;
00509 }
00510
00511 void KateLayoutCache::setAcceptDirtyLayouts(bool accept)
00512 {
00513 if (!m_acceptDirtyLayouts.hasLocalData())
00514 m_acceptDirtyLayouts.setLocalData(new bool);
00515
00516 *m_acceptDirtyLayouts.localData() = accept;
00517 }
00518
00519 #include "katelayoutcache.moc"
00520
00521