kate Library API Documentation

kateviewinternal.cpp

00001 /* This file is part of the KDE libraries 00002 Copyright (C) 2002 John Firebaugh <jfirebaugh@kde.org> 00003 Copyright (C) 2002 Joseph Wenninger <jowenn@kde.org> 00004 Copyright (C) 2002,2003 Christoph Cullmann <cullmann@kde.org> 00005 Copyright (C) 2002,2003 Hamish Rodda <rodda@kde.org> 00006 Copyright (C) 2003 Anakim Border <aborder@sources.sourceforge.net> 00007 00008 Based on: 00009 KWriteView : Copyright (C) 1999 Jochen Wilhelmy <digisnap@cs.tu-berlin.de> 00010 00011 This library is free software; you can redistribute it and/or 00012 modify it under the terms of the GNU Library General Public 00013 License version 2 as published by the Free Software Foundation. 00014 00015 This library is distributed in the hope that it will be useful, 00016 but WITHOUT ANY WARRANTY; without even the implied warranty of 00017 MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU 00018 Library General Public License for more details. 00019 00020 You should have received a copy of the GNU Library General Public License 00021 along with this library; see the file COPYING.LIB. If not, write to 00022 the Free Software Foundation, Inc., 59 Temple Place - Suite 330, 00023 Boston, MA 02111-1307, USA. 00024 */ 00025 00026 #include "kateviewinternal.h" 00027 #include "kateviewinternal.moc" 00028 00029 #include "kateview.h" 00030 #include "katedocument.h" 00031 #include "katecodefoldinghelpers.h" 00032 #include "kateviewhelpers.h" 00033 #include "katehighlight.h" 00034 #include "katesupercursor.h" 00035 #include "katerenderer.h" 00036 #include "katecodecompletion.h" 00037 #include "kateconfig.h" 00038 00039 #include <kcursor.h> 00040 #include <kdebug.h> 00041 #include <kapplication.h> 00042 #include <kglobalsettings.h> 00043 #include <kurldrag.h> 00044 00045 #include <qstyle.h> 00046 #include <qdragobject.h> 00047 #include <qpopupmenu.h> 00048 #include <qdropsite.h> 00049 #include <qpainter.h> 00050 #include <qlayout.h> 00051 #include <qclipboard.h> 00052 #include <qpixmap.h> 00053 00054 KateViewInternal::KateViewInternal(KateView *view, KateDocument *doc) 00055 : QWidget (view, "", Qt::WStaticContents | Qt::WRepaintNoErase | Qt::WResizeNoErase ) 00056 , editSessionNumber (0) 00057 , editIsRunning (false) 00058 , m_view (view) 00059 , m_doc (doc) 00060 , cursor (doc, true, 0, 0, this) 00061 , possibleTripleClick (false) 00062 , m_dummy (0) 00063 , m_startPos(0,0) 00064 , m_oldStartPos(0,0) 00065 , m_madeVisible(false) 00066 , m_shiftKeyPressed (false) 00067 , m_autoCenterLines (false) 00068 , m_columnScrollDisplayed(false) 00069 , m_selChangedByUser (false) 00070 , selectAnchor (-1, -1) 00071 , m_preserveMaxX(false) 00072 , m_currentMaxX(0) 00073 , m_usePlainLines(false) 00074 , m_updatingView(true) 00075 , m_cachedMaxStartPos(-1, -1) 00076 , m_dragScrollTimer(this) 00077 , m_scrollTimer (this) 00078 , m_cursorTimer (this) 00079 , m_textHintTimer (this) 00080 , m_suppressColumnScrollBar(false) 00081 , m_textHintEnabled(false) 00082 , m_textHintMouseX(-1) 00083 , m_textHintMouseY(-1) 00084 , m_imPreeditStartLine(0) 00085 , m_imPreeditStart(0) 00086 , m_imPreeditLength(0) 00087 { 00088 setMinimumSize (0,0); 00089 00090 // cursor 00091 cursor.setMoveOnInsert (true); 00092 00093 // invalidate selStartCached, or keyb selection is screwed initially 00094 selStartCached.setLine( -1 ); 00095 // 00096 // scrollbar for lines 00097 // 00098 m_lineScroll = new KateScrollBar(QScrollBar::Vertical, this); 00099 m_lineScroll->show(); 00100 m_lineScroll->setTracking (true); 00101 00102 m_lineLayout = new QVBoxLayout(); 00103 m_colLayout = new QHBoxLayout(); 00104 00105 m_colLayout->addWidget(m_lineScroll); 00106 m_lineLayout->addLayout(m_colLayout); 00107 00108 if (!m_view->dynWordWrap()) 00109 { 00110 // bottom corner box 00111 m_dummy = new QWidget(m_view); 00112 m_dummy->setFixedHeight(style().scrollBarExtent().width()); 00113 m_dummy->show(); 00114 m_lineLayout->addWidget(m_dummy); 00115 } 00116 00117 // Hijack the line scroller's controls, so we can scroll nicely for word-wrap 00118 connect(m_lineScroll, SIGNAL(prevPage()), SLOT(scrollPrevPage())); 00119 connect(m_lineScroll, SIGNAL(nextPage()), SLOT(scrollNextPage())); 00120 00121 connect(m_lineScroll, SIGNAL(prevLine()), SLOT(scrollPrevLine())); 00122 connect(m_lineScroll, SIGNAL(nextLine()), SLOT(scrollNextLine())); 00123 00124 connect(m_lineScroll, SIGNAL(sliderMoved(int)), SLOT(scrollLines(int))); 00125 connect(m_lineScroll, SIGNAL(sliderMMBMoved(int)), SLOT(scrollLines(int))); 00126 00127 // catch wheel events, completing the hijack 00128 m_lineScroll->installEventFilter(this); 00129 00130 // 00131 // scrollbar for columns 00132 // 00133 m_columnScroll = new QScrollBar(QScrollBar::Horizontal,m_view); 00134 m_columnScroll->hide(); 00135 m_columnScroll->setTracking(true); 00136 m_startX = 0; 00137 m_oldStartX = 0; 00138 00139 connect( m_columnScroll, SIGNAL( valueChanged (int) ), 00140 this, SLOT( scrollColumns (int) ) ); 00141 00142 // 00143 // iconborder ;) 00144 // 00145 leftBorder = new KateIconBorder( this, m_view ); 00146 leftBorder->show (); 00147 00148 connect( leftBorder, SIGNAL(toggleRegionVisibility(unsigned int)), 00149 m_doc->foldingTree(), SLOT(toggleRegionVisibility(unsigned int))); 00150 00151 connect( doc->foldingTree(), SIGNAL(regionVisibilityChangedAt(unsigned int)), 00152 this, SLOT(slotRegionVisibilityChangedAt(unsigned int))); 00153 connect( doc, SIGNAL(codeFoldingUpdated()), 00154 this, SLOT(slotCodeFoldingChanged()) ); 00155 00156 displayCursor.setPos(0, 0); 00157 cursor.setPos(0, 0); 00158 cXPos = 0; 00159 00160 setAcceptDrops( true ); 00161 setBackgroundMode( NoBackground ); 00162 00163 // event filter 00164 installEventFilter(this); 00165 00166 // im 00167 setInputMethodEnabled(true); 00168 00169 setInputMethodEnabled(true); 00170 00171 // set cursor 00172 setCursor( KCursor::ibeamCursor() ); 00173 00174 dragInfo.state = diNone; 00175 00176 // timers 00177 connect( &m_dragScrollTimer, SIGNAL( timeout() ), 00178 this, SLOT( doDragScroll() ) ); 00179 00180 connect( &m_scrollTimer, SIGNAL( timeout() ), 00181 this, SLOT( scrollTimeout() ) ); 00182 00183 connect( &m_cursorTimer, SIGNAL( timeout() ), 00184 this, SLOT( cursorTimeout() ) ); 00185 00186 connect( &m_textHintTimer, SIGNAL( timeout() ), 00187 this, SLOT( textHintTimeout() ) ); 00188 00189 // selection changed to set anchor 00190 connect( m_doc, SIGNAL( selectionChanged() ), 00191 this, SLOT( docSelectionChanged() ) ); 00192 00193 00194 // this is a work arround for RTL desktops 00195 // should be changed in kde 3.3 00196 // BTW: this comment has been "ported" from 3.1.X tree 00197 // any hacker with BIDI knowlege is welcomed to fix kate problems :) 00198 if (QApplication::reverseLayout()){ 00199 m_view->m_grid->addMultiCellWidget(leftBorder, 0, 1, 2, 2); 00200 m_view->m_grid->addMultiCellWidget(m_columnScroll, 1, 1, 0, 1); 00201 m_view->m_grid->addMultiCellLayout(m_lineLayout, 0, 0, 0, 0); 00202 } 00203 else{ 00204 m_view->m_grid->addMultiCellLayout(m_lineLayout, 0, 1, 2, 2); 00205 m_view->m_grid->addMultiCellWidget(m_columnScroll, 1, 1, 0, 1); 00206 m_view->m_grid->addWidget(leftBorder, 0, 0); 00207 } 00208 00209 updateView (); 00210 } 00211 00212 KateViewInternal::~KateViewInternal () 00213 { 00214 } 00215 00216 void KateViewInternal::prepareForDynWrapChange() 00217 { 00218 // Which is the current view line? 00219 m_wrapChangeViewLine = displayViewLine(displayCursor, true); 00220 } 00221 00222 void KateViewInternal::dynWrapChanged() 00223 { 00224 if (m_view->dynWordWrap()) 00225 { 00226 delete m_dummy; 00227 m_dummy = 0; 00228 m_columnScroll->hide(); 00229 m_columnScrollDisplayed = false; 00230 00231 } 00232 else 00233 { 00234 // bottom corner box 00235 m_dummy = new QWidget(m_view); 00236 m_dummy->setFixedSize( style().scrollBarExtent().width(), 00237 style().scrollBarExtent().width() ); 00238 m_dummy->show(); 00239 m_lineLayout->addWidget(m_dummy); 00240 } 00241 00242 tagAll(); 00243 updateView(); 00244 00245 if (m_view->dynWordWrap()) 00246 scrollColumns(0); 00247 00248 // Determine where the cursor should be to get the cursor on the same view line 00249 if (m_wrapChangeViewLine != -1) { 00250 KateTextCursor newStart = viewLineOffset(displayCursor, -m_wrapChangeViewLine); 00251 00252 // Account for the scrollbar in non-dyn-word-wrap mode 00253 if (!m_view->dynWordWrap() && scrollbarVisible(newStart.line())) { 00254 int lines = linesDisplayed() - 1; 00255 00256 if (m_view->height() != height()) 00257 lines++; 00258 00259 if (newStart.line() + lines == displayCursor.line()) 00260 newStart = viewLineOffset(displayCursor, 1 - m_wrapChangeViewLine); 00261 } 00262 00263 makeVisible(newStart, newStart.col(), true); 00264 00265 } else { 00266 update(); 00267 } 00268 } 00269 00270 KateTextCursor KateViewInternal::endPos() const 00271 { 00272 int viewLines = linesDisplayed() - 1; 00273 00274 if (viewLines < 0) { 00275 kdDebug(13030) << "WARNING: viewLines wrong!" << endl; 00276 viewLines = 0; 00277 } 00278 00279 // Check to make sure that lineRanges isn't invalid 00280 if (!lineRanges.count() || lineRanges[0].line == -1 || viewLines >= (int)lineRanges.count()) { 00281 // Switch off use of the cache 00282 return KateTextCursor(m_doc->numVisLines() - 1, m_doc->lineLength(m_doc->getRealLine(m_doc->numVisLines() - 1))); 00283 } 00284 00285 for (int i = viewLines; i >= 0; i--) { 00286 KateLineRange& thisRange = lineRanges[i]; 00287 00288 if (thisRange.line == -1) continue; 00289 00290 if (thisRange.virtualLine >= (int)m_doc->numVisLines()) { 00291 // Cache is too out of date 00292 return KateTextCursor(m_doc->numVisLines() - 1, m_doc->lineLength(m_doc->getRealLine(m_doc->numVisLines() - 1))); 00293 } 00294 00295 return KateTextCursor(thisRange.virtualLine, thisRange.wrap ? thisRange.endCol - 1 : thisRange.endCol); 00296 } 00297 00298 Q_ASSERT(false); 00299 kdDebug(13030) << "WARNING: could not find a lineRange at all" << endl; 00300 return KateTextCursor(-1, -1); 00301 } 00302 00303 uint KateViewInternal::endLine() const 00304 { 00305 return endPos().line(); 00306 } 00307 00308 KateLineRange KateViewInternal::yToKateLineRange(uint y) const 00309 { 00310 return lineRanges[y / m_view->renderer()->fontHeight()]; 00311 } 00312 00313 int KateViewInternal::lineToY(uint viewLine) const 00314 { 00315 return (viewLine-startLine()) * m_view->renderer()->fontHeight(); 00316 } 00317 00318 void KateViewInternal::slotIncFontSizes() 00319 { 00320 m_view->renderer()->increaseFontSizes(); 00321 } 00322 00323 void KateViewInternal::slotDecFontSizes() 00324 { 00325 m_view->renderer()->decreaseFontSizes(); 00326 } 00327 00331 void KateViewInternal::scrollLines ( int line ) 00332 { 00333 KateTextCursor newPos(line, 0); 00334 scrollPos(newPos); 00335 } 00336 00337 // This can scroll less than one true line 00338 void KateViewInternal::scrollViewLines(int offset) 00339 { 00340 KateTextCursor c = viewLineOffset(startPos(), offset); 00341 scrollPos(c); 00342 00343 m_lineScroll->blockSignals(true); 00344 m_lineScroll->setValue(startLine()); 00345 m_lineScroll->blockSignals(false); 00346 } 00347 00348 void KateViewInternal::scrollNextPage() 00349 { 00350 scrollViewLines(QMAX( linesDisplayed() - 1, 0 )); 00351 } 00352 00353 void KateViewInternal::scrollPrevPage() 00354 { 00355 scrollViewLines(-QMAX( linesDisplayed() - 1, 0 )); 00356 } 00357 00358 void KateViewInternal::scrollPrevLine() 00359 { 00360 scrollViewLines(-1); 00361 } 00362 00363 void KateViewInternal::scrollNextLine() 00364 { 00365 scrollViewLines(1); 00366 } 00367 00368 KateTextCursor KateViewInternal::maxStartPos(bool changed) 00369 { 00370 m_usePlainLines = true; 00371 00372 if (m_cachedMaxStartPos.line() == -1 || changed) 00373 { 00374 KateTextCursor end(m_doc->numVisLines() - 1, m_doc->lineLength(m_doc->getRealLine(m_doc->numVisLines() - 1))); 00375 00376 m_cachedMaxStartPos = viewLineOffset(end, -(linesDisplayed() - 1)); 00377 } 00378 00379 // If we're not dynamic word-wrapping, the horizontal scrollbar is hidden and will appear, increment the maxStart by 1 00380 if (!m_view->dynWordWrap() && m_columnScroll->isHidden() && scrollbarVisible(m_cachedMaxStartPos.line())) 00381 { 00382 KateTextCursor end(m_doc->numVisLines() - 1, m_doc->lineLength(m_doc->getRealLine(m_doc->numVisLines() - 1))); 00383 00384 return viewLineOffset(end, -linesDisplayed()); 00385 } 00386 00387 m_usePlainLines = false; 00388 00389 return m_cachedMaxStartPos; 00390 } 00391 00392 // c is a virtual cursor 00393 void KateViewInternal::scrollPos(KateTextCursor& c, bool force, bool calledExternally) 00394 { 00395 if (!force && ((!m_view->dynWordWrap() && c.line() == (int)startLine()) || c == startPos())) 00396 return; 00397 00398 if (c.line() < 0) 00399 c.setLine(0); 00400 00401 KateTextCursor limit = maxStartPos(); 00402 if (c > limit) { 00403 c = limit; 00404 00405 // overloading this variable, it's not used in non-word wrap 00406 // used to set the lineScroll to the max value 00407 if (m_view->dynWordWrap()) 00408 m_suppressColumnScrollBar = true; 00409 00410 // Re-check we're not just scrolling to the same place 00411 if (!force && ((!m_view->dynWordWrap() && c.line() == (int)startLine()) || c == startPos())) 00412 return; 00413 } 00414 00415 int viewLinesScrolled = displayViewLine(c); 00416 00417 m_oldStartPos = m_startPos; 00418 m_startPos = c; 00419 00420 // set false here but reversed if we return to makeVisible 00421 m_madeVisible = false; 00422 00423 if (!force) { 00424 int lines = linesDisplayed(); 00425 if ((int)m_doc->numVisLines() < lines) { 00426 KateTextCursor end(m_doc->numVisLines() - 1, m_doc->lineLength(m_doc->getRealLine(m_doc->numVisLines() - 1))); 00427 lines = QMIN((int)linesDisplayed(), displayViewLine(end) + 1); 00428 } 00429 00430 Q_ASSERT(lines >= 0); 00431 00432 if (!calledExternally && QABS(viewLinesScrolled) < lines) 00433 { 00434 updateView(false, viewLinesScrolled); 00435 00436 int scrollHeight = -(viewLinesScrolled * m_view->renderer()->fontHeight()); 00437 int scrollbarWidth = style().scrollBarExtent().width(); 00438 00439 // 00440 // updates are for working around the scrollbar leaving blocks in the view 00441 // 00442 scroll(0, scrollHeight); 00443 update(0, height()+scrollHeight-scrollbarWidth, width(), 2*scrollbarWidth); 00444 00445 leftBorder->scroll(0, scrollHeight); 00446 leftBorder->update(0, leftBorder->height()+scrollHeight-scrollbarWidth, leftBorder->width(), 2*scrollbarWidth); 00447 00448 return; 00449 } 00450 } 00451 00452 updateView(); 00453 update(); 00454 leftBorder->update(); 00455 } 00456 00457 void KateViewInternal::scrollColumns ( int x ) 00458 { 00459 if (x == m_startX) 00460 return; 00461 00462 if (x < 0) 00463 x = 0; 00464 00465 int dx = m_startX - x; 00466 m_oldStartX = m_startX; 00467 m_startX = x; 00468 00469 if (QABS(dx) < width()) 00470 scroll(dx, 0); 00471 else 00472 update(); 00473 00474 m_columnScroll->blockSignals(true); 00475 m_columnScroll->setValue(m_startX); 00476 m_columnScroll->blockSignals(false); 00477 } 00478 00479 // If changed is true, the lines that have been set dirty have been updated. 00480 void KateViewInternal::updateView(bool changed, int viewLinesScrolled) 00481 { 00482 m_updatingView = true; 00483 00484 uint contentLines = m_doc->visibleLines(); 00485 00486 m_lineScroll->blockSignals(true); 00487 00488 KateTextCursor maxStart = maxStartPos(changed); 00489 int maxLineScrollRange = maxStart.line(); 00490 if (m_view->dynWordWrap() && maxStart.col() != 0) 00491 maxLineScrollRange++; 00492 m_lineScroll->setRange(0, maxLineScrollRange); 00493 00494 if (m_view->dynWordWrap() && m_suppressColumnScrollBar) { 00495 m_suppressColumnScrollBar = false; 00496 m_lineScroll->setValue(maxStart.line()); 00497 } else { 00498 m_lineScroll->setValue(startPos().line()); 00499 } 00500 m_lineScroll->setSteps(1, height() / m_view->renderer()->fontHeight()); 00501 m_lineScroll->blockSignals(false); 00502 00503 uint oldSize = lineRanges.size (); 00504 uint newSize = (height() / m_view->renderer()->fontHeight()) + 1; 00505 if (oldSize != newSize) { 00506 lineRanges.resize((height() / m_view->renderer()->fontHeight()) + 1); 00507 if (newSize > oldSize) { 00508 static KateLineRange blank; 00509 for (uint i = oldSize; i < newSize; i++) { 00510 lineRanges[i] = blank; 00511 } 00512 } 00513 } 00514 00515 if (oldSize < lineRanges.size ()) 00516 { 00517 for (uint i=oldSize; i < lineRanges.size(); i++) 00518 lineRanges[i].dirty = true; 00519 } 00520 00521 // Move the lineRanges data if we've just scrolled... 00522 if (viewLinesScrolled != 0) { 00523 // loop backwards if we've just scrolled up... 00524 bool forwards = viewLinesScrolled >= 0 ? true : false; 00525 for (uint z = forwards ? 0 : lineRanges.count() - 1; z < lineRanges.count(); forwards ? z++ : z--) { 00526 uint oldZ = z + viewLinesScrolled; 00527 if (oldZ < lineRanges.count()) { 00528 lineRanges[z] = lineRanges[oldZ]; 00529 } else { 00530 lineRanges[z].dirty = true; 00531 } 00532 } 00533 } 00534 00535 if (m_view->dynWordWrap()) 00536 { 00537 KateTextCursor realStart = startPos(); 00538 realStart.setLine(m_doc->getRealLine(realStart.line())); 00539 00540 KateLineRange startRange = range(realStart); 00541 uint line = startRange.virtualLine; 00542 int realLine = startRange.line; 00543 uint oldLine = line; 00544 int startCol = startRange.startCol; 00545 int startX = startRange.startX; 00546 int endX = startRange.startX; 00547 int shiftX = startRange.startCol ? startRange.shiftX : 0; 00548 bool wrap = false; 00549 int newViewLine = startRange.viewLine; 00550 // z is the current display view line 00551 KateTextLine::Ptr text = textLine(realLine); 00552 00553 bool alreadyDirty = false; 00554 00555 for (uint z = 0; z < lineRanges.size(); z++) 00556 { 00557 if (oldLine != line) { 00558 realLine = (int)m_doc->getRealLine(line); 00559 00560 if (z) 00561 lineRanges[z-1].startsInvisibleBlock = (realLine != lineRanges[z-1].line + 1); 00562 00563 text = textLine(realLine); 00564 startCol = 0; 00565 startX = 0; 00566 endX = 0; 00567 shiftX = 0; 00568 newViewLine = 0; 00569 oldLine = line; 00570 } 00571 00572 if (line >= contentLines || !text) 00573 { 00574 if (lineRanges[z].line != -1) 00575 lineRanges[z].dirty = true; 00576 00577 lineRanges[z].clear(); 00578 00579 line++; 00580 } 00581 else 00582 { 00583 if (lineRanges[z].line != realLine || lineRanges[z].startCol != startCol) 00584 alreadyDirty = lineRanges[z].dirty = true; 00585 00586 if (lineRanges[z].dirty || changed || alreadyDirty) { 00587 alreadyDirty = true; 00588 00589 lineRanges[z].virtualLine = line; 00590 lineRanges[z].line = realLine; 00591 lineRanges[z].startsInvisibleBlock = false; 00592 00593 int tempEndX = 0; 00594 00595 int endCol = m_view->renderer()->textWidth(text, startCol, width() - shiftX, &wrap, &tempEndX); 00596 00597 endX += tempEndX; 00598 00599 if (wrap) 00600 { 00601 if (m_view->config()->dynWordWrapAlignIndent() > 0) 00602 { 00603 if (startX == 0) 00604 { 00605 int pos = text->nextNonSpaceChar(0); 00606 00607 if (pos > 0) 00608 shiftX = m_view->renderer()->textWidth(text, pos); 00609 00610 if (shiftX > ((double)width() / 100 * m_view->config()->dynWordWrapAlignIndent())) 00611 shiftX = 0; 00612 } 00613 } 00614 00615 if ((lineRanges[z].startX != startX) || (lineRanges[z].endX != endX) || 00616 (lineRanges[z].startCol != startCol) || (lineRanges[z].endCol != endCol) || 00617 (lineRanges[z].shiftX != shiftX)) 00618 lineRanges[z].dirty = true; 00619 00620 lineRanges[z].startCol = startCol; 00621 lineRanges[z].endCol = endCol; 00622 lineRanges[z].startX = startX; 00623 lineRanges[z].endX = endX; 00624 lineRanges[z].viewLine = newViewLine; 00625 lineRanges[z].wrap = true; 00626 00627 startCol = endCol; 00628 startX = endX; 00629 } 00630 else 00631 { 00632 if ((lineRanges[z].startX != startX) || (lineRanges[z].endX != endX) || 00633 (lineRanges[z].startCol != startCol) || (lineRanges[z].endCol != endCol)) 00634 lineRanges[z].dirty = true; 00635 00636 lineRanges[z].startCol = startCol; 00637 lineRanges[z].endCol = endCol; 00638 lineRanges[z].startX = startX; 00639 lineRanges[z].endX = endX; 00640 lineRanges[z].viewLine = newViewLine; 00641 lineRanges[z].wrap = false; 00642 00643 line++; 00644 } 00645 00646 lineRanges[z].shiftX = shiftX; 00647 00648 } else { 00649 // The cached data is still intact 00650 if (lineRanges[z].wrap) { 00651 startCol = lineRanges[z].endCol; 00652 startX = lineRanges[z].endX; 00653 endX = lineRanges[z].endX; 00654 } else { 00655 line++; 00656 } 00657 shiftX = lineRanges[z].shiftX; 00658 } 00659 } 00660 newViewLine++; 00661 } 00662 } 00663 else 00664 { 00665 uint z = 0; 00666 00667 for(; (z + startLine() < contentLines) && (z < lineRanges.size()); z++) 00668 { 00669 if (lineRanges[z].dirty || lineRanges[z].line != (int)m_doc->getRealLine(z + startLine())) { 00670 lineRanges[z].dirty = true; 00671 00672 lineRanges[z].line = m_doc->getRealLine( z + startLine() ); 00673 if (z) 00674 lineRanges[z-1].startsInvisibleBlock = (lineRanges[z].line != lineRanges[z-1].line + 1); 00675 00676 lineRanges[z].virtualLine = z + startLine(); 00677 lineRanges[z].startCol = 0; 00678 lineRanges[z].endCol = m_doc->lineLength(lineRanges[z].line); 00679 lineRanges[z].startX = 0; 00680 lineRanges[z].endX = m_view->renderer()->textWidth( textLine( lineRanges[z].line ), -1 ); 00681 lineRanges[z].shiftX = 0; 00682 lineRanges[z].viewLine = 0; 00683 lineRanges[z].wrap = false; 00684 } 00685 else if (z && lineRanges[z-1].dirty) 00686 { 00687 lineRanges[z-1].startsInvisibleBlock = (lineRanges[z].line != lineRanges[z-1].line + 1); 00688 } 00689 } 00690 00691 for (; z < lineRanges.size(); z++) 00692 { 00693 if (lineRanges[z].line != -1) 00694 lineRanges[z].dirty = true; 00695 00696 lineRanges[z].clear(); 00697 } 00698 00699 if (scrollbarVisible(startLine())) 00700 { 00701 m_columnScroll->blockSignals(true); 00702 00703 int max = maxLen(startLine()) - width(); 00704 if (max < 0) 00705 max = 0; 00706 00707 m_columnScroll->setRange(0, max); 00708 00709 m_columnScroll->setValue(m_startX); 00710 00711 // Approximate linescroll 00712 m_columnScroll->setSteps(m_view->renderer()->config()->fontMetrics()->width('a'), width()); 00713 00714 m_columnScroll->blockSignals(false); 00715 00716 if (!m_columnScroll->isVisible () && !m_suppressColumnScrollBar) 00717 { 00718 m_columnScroll->show(); 00719 m_columnScrollDisplayed = true; 00720 } 00721 } 00722 else if (m_columnScroll->isVisible () && !m_suppressColumnScrollBar && (startX() == 0)) 00723 { 00724 m_columnScroll->hide(); 00725 m_columnScrollDisplayed = false; 00726 } 00727 } 00728 00729 m_updatingView = false; 00730 00731 if (changed) 00732 paintText(0, 0, width(), height(), true); 00733 } 00734 00735 void KateViewInternal::paintText (int x, int y, int width, int height, bool paintOnlyDirty) 00736 { 00737 //kdDebug() << k_funcinfo << x << " " << y << " " << width << " " << height << " " << paintOnlyDirty << endl; 00738 int xStart = startX() + x; 00739 int xEnd = xStart + width; 00740 uint h = m_view->renderer()->fontHeight(); 00741 uint startz = (y / h); 00742 uint endz = startz + 1 + (height / h); 00743 uint lineRangesSize = lineRanges.size(); 00744 00745 static QPixmap drawBuffer; 00746 00747 if (drawBuffer.width() < KateViewInternal::width() || drawBuffer.height() < (int)h) 00748 drawBuffer.resize(KateViewInternal::width(), (int)h); 00749 00750 if (drawBuffer.isNull()) 00751 return; 00752 00753 QPainter paint(this); 00754 QPainter paintDrawBuffer(&drawBuffer); 00755 00756 // TODO put in the proper places 00757 m_view->renderer()->setCaretStyle(m_view->isOverwriteMode() ? KateRenderer::Replace : KateRenderer::Insert); 00758 m_view->renderer()->setShowTabs(m_doc->configFlags() & KateDocument::cfShowTabs); 00759 00760 for (uint z=startz; z <= endz; z++) 00761 { 00762 if ( (z >= lineRangesSize) || ((lineRanges[z].line == -1) && (!paintOnlyDirty || lineRanges[z].dirty)) ) 00763 { 00764 if (!(z >= lineRangesSize)) 00765 lineRanges[z].dirty = false; 00766 00767 paint.fillRect( x, z * h, width, h, m_view->renderer()->config()->backgroundColor() ); 00768 } 00769 else if (!paintOnlyDirty || lineRanges[z].dirty) 00770 { 00771 lineRanges[z].dirty = false; 00772 00773 m_view->renderer()->paintTextLine(paintDrawBuffer, &lineRanges[z], xStart, xEnd, &cursor, &bm); 00774 00775 paint.drawPixmap (x, z * h, drawBuffer, 0, 0, width, h); 00776 } 00777 } 00778 } 00779 00784 void KateViewInternal::makeVisible (const KateTextCursor& c, uint endCol, bool force, bool center, bool calledExternally) 00785 { 00786 //kdDebug() << "MakeVisible start [" << startPos().line << "," << startPos().col << "] end [" << endPos().line << "," << endPos().col << "] -> request: [" << c.line << "," << c.col << "]" <<endl;// , new start [" << scroll.line << "," << scroll.col << "] lines " << (linesDisplayed() - 1) << " height " << height() << endl; 00787 // if the line is in a folded region, unfold all the way up 00788 //if ( m_doc->foldingTree()->findNodeForLine( c.line )->visible ) 00789 // kdDebug()<<"line ("<<c.line<<") should be visible"<<endl; 00790 00791 if ( force ) 00792 { 00793 KateTextCursor scroll = c; 00794 scrollPos(scroll, force, calledExternally); 00795 } 00796 else if (center && (c < startPos() || c > endPos())) 00797 { 00798 KateTextCursor scroll = viewLineOffset(c, -int(linesDisplayed()) / 2); 00799 scrollPos(scroll, false, calledExternally); 00800 } 00801 else if ( c > viewLineOffset(endPos(), -m_minLinesVisible) ) 00802 { 00803 KateTextCursor scroll = viewLineOffset(c, -(linesDisplayed() - m_minLinesVisible - 1)); 00804 00805 if (!m_view->dynWordWrap() && m_columnScroll->isHidden()) 00806 if (scrollbarVisible(scroll.line())) 00807 scroll.setLine(scroll.line() + 1); 00808 00809 scrollPos(scroll, false, calledExternally); 00810 } 00811 else if ( c < viewLineOffset(startPos(), m_minLinesVisible) ) 00812 { 00813 KateTextCursor scroll = viewLineOffset(c, -m_minLinesVisible); 00814 scrollPos(scroll, false, calledExternally); 00815 } 00816 else 00817 { 00818 // Check to see that we're not showing blank lines 00819 KateTextCursor max = maxStartPos(); 00820 if (startPos() > max) { 00821 scrollPos(max, max.col(), calledExternally); 00822 } 00823 } 00824 00825 if (!m_view->dynWordWrap() && endCol != (uint)-1) 00826 { 00827 int sX = (int)m_view->renderer()->textWidth (textLine( m_doc->getRealLine( c.line() ) ), c.col() ); 00828 00829 int sXborder = sX-8; 00830 if (sXborder < 0) 00831 sXborder = 0; 00832 00833 if (sX < m_startX) 00834 scrollColumns (sXborder); 00835 else if (sX > m_startX + width()) 00836 scrollColumns (sX - width() + 8); 00837 } 00838 00839 m_madeVisible = !force; 00840 } 00841 00842 void KateViewInternal::slotRegionVisibilityChangedAt(unsigned int) 00843 { 00844 kdDebug(13030) << "slotRegionVisibilityChangedAt()" << endl; 00845 m_cachedMaxStartPos.setLine(-1); 00846 KateTextCursor max = maxStartPos(); 00847 if (startPos() > max) 00848 scrollPos(max); 00849 00850 updateView(); 00851 update(); 00852 leftBorder->update(); 00853 } 00854 00855 void KateViewInternal::slotCodeFoldingChanged() 00856 { 00857 leftBorder->update(); 00858 } 00859 00860 void KateViewInternal::slotRegionBeginEndAddedRemoved(unsigned int) 00861 { 00862 kdDebug(13030) << "slotRegionBeginEndAddedRemoved()" << endl; 00863 // FIXME: performance problem 00864 leftBorder->update(); 00865 } 00866 00867 void KateViewInternal::showEvent ( QShowEvent *e ) 00868 { 00869 updateView (); 00870 00871 QWidget::showEvent (e); 00872 } 00873 00874 uint KateViewInternal::linesDisplayed() const 00875 { 00876 int h = height(); 00877 int fh = m_view->renderer()->fontHeight(); 00878 00879 return (h - (h % fh)) / fh; 00880 } 00881 00882 QPoint KateViewInternal::cursorCoordinates() 00883 { 00884 int viewLine = displayViewLine(displayCursor, true); 00885 00886 if (viewLine == -1) 00887 return QPoint(-1, -1); 00888 00889 uint y = viewLine * m_view->renderer()->fontHeight(); 00890 uint x = cXPos - m_startX - lineRanges[viewLine].startX + leftBorder->width() + lineRanges[viewLine].xOffset(); 00891 00892 return QPoint(x, y); 00893 } 00894 00895 void KateViewInternal::doReturn() 00896 { 00897 KateTextCursor c = cursor; 00898 m_doc->newLine( c, this ); 00899 updateCursor( c ); 00900 updateView(); 00901 } 00902 00903 void KateViewInternal::doDelete() 00904 { 00905 m_doc->del( cursor ); 00906 } 00907 00908 void KateViewInternal::doBackspace() 00909 { 00910 m_doc->backspace( cursor ); 00911 } 00912 00913 void KateViewInternal::doPaste() 00914 { 00915 m_doc->paste( m_view ); 00916 } 00917 00918 void KateViewInternal::doTranspose() 00919 { 00920 m_doc->transpose( cursor ); 00921 } 00922 00923 void KateViewInternal::doDeleteWordLeft() 00924 { 00925 wordLeft( true ); 00926 m_doc->removeSelectedText(); 00927 update(); 00928 } 00929 00930 void KateViewInternal::doDeleteWordRight() 00931 { 00932 wordRight( true ); 00933 m_doc->removeSelectedText(); 00934 update(); 00935 } 00936 00937 class CalculatingCursor : public KateTextCursor { 00938 public: 00939 CalculatingCursor(KateViewInternal* vi) 00940 : KateTextCursor() 00941 , m_vi(vi) 00942 { 00943 Q_ASSERT(valid()); 00944 } 00945 00946 CalculatingCursor(KateViewInternal* vi, const KateTextCursor& c) 00947 : KateTextCursor(c) 00948 , m_vi(vi) 00949 { 00950 Q_ASSERT(valid()); 00951 } 00952 00953 // This one constrains its arguments to valid positions 00954 CalculatingCursor(KateViewInternal* vi, uint line, uint col) 00955 : KateTextCursor(line, col) 00956 , m_vi(vi) 00957 { 00958 makeValid(); 00959 } 00960 00961 00962 virtual CalculatingCursor& operator+=( int n ) = 0; 00963 00964 virtual CalculatingCursor& operator-=( int n ) = 0; 00965 00966 CalculatingCursor& operator++() { return operator+=( 1 ); } 00967 00968 CalculatingCursor& operator--() { return operator-=( 1 ); } 00969 00970 void makeValid() { 00971 m_line = QMAX( 0, QMIN( int( m_vi->m_doc->numLines() - 1 ), line() ) ); 00972 if (m_vi->m_doc->wrapCursor()) 00973 m_col = QMAX( 0, QMIN( m_vi->m_doc->lineLength( line() ), col() ) ); 00974 else 00975 m_col = QMAX( 0, col() ); 00976 Q_ASSERT( valid() ); 00977 } 00978 00979 void toEdge( Bias bias ) { 00980 if( bias == left ) m_col = 0; 00981 else if( bias == right ) m_col = m_vi->m_doc->lineLength( line() ); 00982 } 00983 00984 bool atEdge() const { return atEdge( left ) || atEdge( right ); } 00985 00986 bool atEdge( Bias bias ) const { 00987 switch( bias ) { 00988 case left: return col() == 0; 00989 case none: return atEdge(); 00990 case right: return col() == m_vi->m_doc->lineLength( line() ); 00991 default: Q_ASSERT(false); return false; 00992 } 00993 } 00994 00995 protected: 00996 bool valid() const { 00997 return line() >= 0 && 00998 uint( line() ) < m_vi->m_doc->numLines() && 00999 col() >= 0 && 01000 (!m_vi->m_doc->wrapCursor() || col() <= m_vi->m_doc->lineLength( line() )); 01001 } 01002 KateViewInternal* m_vi; 01003 }; 01004 01005 class BoundedCursor : public CalculatingCursor { 01006 public: 01007 BoundedCursor(KateViewInternal* vi) 01008 : CalculatingCursor( vi ) {}; 01009 BoundedCursor(KateViewInternal* vi, const KateTextCursor& c ) 01010 : CalculatingCursor( vi, c ) {}; 01011 BoundedCursor(KateViewInternal* vi, uint line, uint col ) 01012 : CalculatingCursor( vi, line, col ) {}; 01013 virtual CalculatingCursor& operator+=( int n ) { 01014 m_col += n; 01015 01016 if (n > 0 && m_vi->m_view->dynWordWrap()) { 01017 // Need to constrain to current visible text line for dynamic wrapping mode 01018 if (m_col > m_vi->m_doc->lineLength(m_line)) { 01019 KateLineRange currentRange = m_vi->range(*this); 01020 01021 int endX; 01022 bool crap; 01023 m_vi->m_view->renderer()->textWidth(m_vi->textLine(m_line), currentRange.startCol, m_vi->width() - currentRange.xOffset(), &crap, &endX); 01024 endX += (m_col - currentRange.endCol + 1) * m_vi->m_view->renderer()->spaceWidth(); 01025 01026 // Constraining if applicable NOTE: some code duplication in KateViewInternal::resize() 01027 if (endX >= m_vi->width() - currentRange.xOffset()) { 01028 m_col -= n; 01029 if ( uint( line() ) < m_vi->m_doc->numLines() - 1 ) { 01030 m_line++; 01031 m_col = 0; 01032 } 01033 } 01034 } 01035 01036 } else if (n < 0 && col() < 0 && line() > 0 ) { 01037 m_line--; 01038 m_col = m_vi->m_doc->lineLength( line() ); 01039 } 01040 01041 m_col = QMAX( 0, col() ); 01042 01043 Q_ASSERT( valid() ); 01044 return *this; 01045 } 01046 virtual CalculatingCursor& operator-=( int n ) { 01047 return operator+=( -n ); 01048 } 01049 }; 01050 01051 class WrappingCursor : public CalculatingCursor { 01052 public: 01053 WrappingCursor(KateViewInternal* vi) 01054 : CalculatingCursor( vi) {}; 01055 WrappingCursor(KateViewInternal* vi, const KateTextCursor& c ) 01056 : CalculatingCursor( vi, c ) {}; 01057 WrappingCursor(KateViewInternal* vi, uint line, uint col ) 01058 : CalculatingCursor( vi, line, col ) {}; 01059 01060 virtual CalculatingCursor& operator+=( int n ) { 01061 if( n < 0 ) return operator-=( -n ); 01062 int len = m_vi->m_doc->lineLength( line() ); 01063 if( col() + n <= len ) { 01064 m_col += n; 01065 } else if( uint( line() ) < m_vi->m_doc->numLines() - 1 ) { 01066 n -= len - col() + 1; 01067 m_col = 0; 01068 m_line++; 01069 operator+=( n ); 01070 } else { 01071 m_col = len; 01072 } 01073 Q_ASSERT( valid() ); 01074 return *this; 01075 } 01076 virtual CalculatingCursor& operator-=( int n ) { 01077 if( n < 0 ) return operator+=( -n ); 01078 if( col() - n >= 0 ) { 01079 m_col -= n; 01080 } else if( line() > 0 ) { 01081 n -= col() + 1; 01082 m_line--; 01083 m_col = m_vi->m_doc->lineLength( line() ); 01084 operator-=( n ); 01085 } else { 01086 m_col = 0; 01087 } 01088 Q_ASSERT( valid() ); 01089 return *this; 01090 } 01091 }; 01092 01093 void KateViewInternal::moveChar( Bias bias, bool sel ) 01094 { 01095 KateTextCursor c; 01096 if ( m_doc->wrapCursor() ) { 01097 c = WrappingCursor( this, cursor ) += bias; 01098 } else { 01099 c = BoundedCursor( this, cursor ) += bias; 01100 } 01101 updateSelection( c, sel ); 01102 updateCursor( c ); 01103 } 01104 01105 void KateViewInternal::cursorLeft( bool sel ) { moveChar( left, sel ); } 01106 void KateViewInternal::cursorRight( bool sel ) { moveChar( right, sel ); } 01107 01108 void KateViewInternal::moveWord( Bias bias, bool sel ) 01109 { 01110 // This matches the word-moving in QTextEdit, QLineEdit etc. 01111 01112 WrappingCursor c( this, cursor ); 01113 if( !c.atEdge( bias ) ) { 01114 KateHighlighting* h = m_doc->highlight(); 01115 01116 bool moved = false; 01117 while( !c.atEdge( bias ) && !h->isInWord( m_doc->textLine( c.line() )[ c.col() - (bias == left ? 1 : 0) ] ) ) 01118 { 01119 c += bias; 01120 moved = true; 01121 } 01122 01123 if ( bias != right || !moved ) 01124 { 01125 while( !c.atEdge( bias ) && h->isInWord( m_doc->textLine( c.line() )[ c.col() - (bias == left ? 1 : 0) ] ) ) 01126 c += bias; 01127 if ( bias == right ) 01128 { 01129 while ( !c.atEdge( bias ) && m_doc->textLine( c.line() )[ c.col() ].isSpace() ) 01130 c+= bias; 01131 } 01132 } 01133 01134 } else { 01135 c += bias; 01136 } 01137 updateSelection( c, sel ); 01138 updateCursor( c ); 01139 } 01140 01141 void KateViewInternal::wordLeft ( bool sel ) { moveWord( left, sel ); } 01142 void KateViewInternal::wordRight( bool sel ) { moveWord( right, sel ); } 01143 01144 void KateViewInternal::moveEdge( Bias bias, bool sel ) 01145 { 01146 BoundedCursor c( this, cursor ); 01147 c.toEdge( bias ); 01148 updateSelection( c, sel ); 01149 updateCursor( c ); 01150 } 01151 01152 void KateViewInternal::home( bool sel ) 01153 { 01154 if (m_view->dynWordWrap() && currentRange().startCol) { 01155 // Allow us to go to the real start if we're already at the start of the view line 01156 if (cursor.col() != currentRange().startCol) { 01157 KateTextCursor c(cursor.line(), currentRange().startCol); 01158 updateSelection( c, sel ); 01159 updateCursor( c ); 01160 return; 01161 } 01162 } 01163 01164 if( !(m_doc->configFlags() & KateDocument::cfSmartHome) ) { 01165 moveEdge( left, sel ); 01166 return; 01167 } 01168 01169 KateTextCursor c = cursor; 01170 int lc = textLine( c.line() )->firstChar(); 01171 01172 if( lc < 0 || c.col() == lc ) { 01173 c.setCol(0); 01174 } else { 01175 c.setCol(lc); 01176 } 01177 01178 updateSelection( c, sel ); 01179 updateCursor( c ); 01180 } 01181 01182 void KateViewInternal::end( bool sel ) 01183 { 01184 if (m_view->dynWordWrap() && currentRange().wrap) { 01185 // Allow us to go to the real end if we're already at the end of the view line 01186 if (cursor.col() < currentRange().endCol - 1) { 01187 KateTextCursor c(cursor.line(), currentRange().endCol - 1); 01188 updateSelection( c, sel ); 01189 updateCursor( c ); 01190 return; 01191 } 01192 } 01193 01194 moveEdge( right, sel ); 01195 } 01196 01197 KateLineRange KateViewInternal::range(int realLine, const KateLineRange* previous) 01198 { 01199 // look at the cache first 01200 if (!m_updatingView && realLine >= lineRanges[0].line && realLine <= lineRanges[lineRanges.count() - 1].line) 01201 for (uint i = 0; i < lineRanges.count(); i++) 01202 if (realLine == lineRanges[i].line) 01203 if (!m_view->dynWordWrap() || (!previous && lineRanges[i].startCol == 0) || (previous && lineRanges[i].startCol == previous->endCol)) 01204 return lineRanges[i]; 01205 01206 // Not in the cache, we have to create it 01207 KateLineRange ret; 01208 01209 KateTextLine::Ptr text = textLine(realLine); 01210 if (!text) { 01211 return KateLineRange(); 01212 } 01213 01214 if (!m_view->dynWordWrap()) { 01215 Q_ASSERT(!previous); 01216 ret.line = realLine; 01217 ret.virtualLine = m_doc->getVirtualLine(realLine); 01218 ret.startCol = 0; 01219 ret.endCol = m_doc->lineLength(realLine); 01220 ret.startX = 0; 01221 ret.endX = m_view->renderer()->textWidth(text, -1); 01222 ret.viewLine = 0; 01223 ret.wrap = false; 01224 return ret; 01225 } 01226 01227 ret.endCol = (int)m_view->renderer()->textWidth(text, previous ? previous->endCol : 0, width() - (previous ? previous->shiftX : 0), &ret.wrap, &ret.endX); 01228 01229 Q_ASSERT(ret.endCol > ret.startCol); 01230 01231 ret.line = realLine; 01232 01233 if (previous) { 01234 ret.virtualLine = previous->virtualLine; 01235 ret.startCol = previous->endCol; 01236 ret.startX = previous->endX; 01237 ret.endX += previous->endX; 01238 ret.shiftX = previous->shiftX; 01239 ret.viewLine = previous->viewLine + 1; 01240 01241 } else { 01242 // TODO worthwhile optimising this to get the data out of the initial textWidth call? 01243 if (m_view->config()->dynWordWrapAlignIndent() > 0) { 01244 int pos = text->nextNonSpaceChar(0); 01245 01246 if (pos > 0) 01247 ret.shiftX = m_view->renderer()->textWidth(text, pos); 01248 01249 if (ret.shiftX > ((double)width() / 100 * m_view->config()->dynWordWrapAlignIndent())) 01250 ret.shiftX = 0; 01251 } 01252 01253 ret.virtualLine = m_doc->getVirtualLine(realLine); 01254 ret.startCol = 0; 01255 ret.startX = 0; 01256 ret.viewLine = 0; 01257 } 01258 01259 return ret; 01260 } 01261 01262 KateLineRange KateViewInternal::currentRange() 01263 { 01264 // Q_ASSERT(m_view->dynWordWrap()); 01265 01266 return range(cursor); 01267 } 01268 01269 KateLineRange KateViewInternal::previousRange() 01270 { 01271 uint currentViewLine = viewLine(cursor); 01272 01273 if (currentViewLine) 01274 return range(cursor.line(), currentViewLine - 1); 01275 else 01276 return range(m_doc->getRealLine(displayCursor.line() - 1), -1); 01277 } 01278 01279 KateLineRange KateViewInternal::nextRange() 01280 { 01281 uint currentViewLine = viewLine(cursor) + 1; 01282 01283 if (currentViewLine >= viewLineCount(cursor.line())) { 01284 currentViewLine = 0; 01285 return range(cursor.line() + 1, currentViewLine); 01286 } else { 01287 return range(cursor.line(), currentViewLine); 01288 } 01289 } 01290 01291 KateLineRange KateViewInternal::range(const KateTextCursor& realCursor) 01292 { 01293 // Q_ASSERT(m_view->dynWordWrap()); 01294 01295 KateLineRange thisRange; 01296 bool first = true; 01297 01298 do { 01299 thisRange = range(realCursor.line(), first ? 0L : &thisRange); 01300 first = false; 01301 } while (thisRange.wrap && !(realCursor.col() >= thisRange.startCol && realCursor.col() < thisRange.endCol) && thisRange.startCol != thisRange.endCol); 01302 01303 return thisRange; 01304 } 01305 01306 KateLineRange KateViewInternal::range(uint realLine, int viewLine) 01307 { 01308 // Q_ASSERT(m_view->dynWordWrap()); 01309 01310 KateLineRange thisRange; 01311 bool first = true; 01312 01313 do { 01314 thisRange = range(realLine, first ? 0L : &thisRange); 01315 first = false; 01316 } while (thisRange.wrap && viewLine != thisRange.viewLine && thisRange.startCol != thisRange.endCol); 01317 01318 if (viewLine != -1 && viewLine != thisRange.viewLine) 01319 kdDebug(13030) << "WARNING: viewLine " << viewLine << " of line " << realLine << " does not exist." << endl; 01320 01321 return thisRange; 01322 } 01323 01329 uint KateViewInternal::viewLine(const KateTextCursor& realCursor) 01330 { 01331 if (!m_view->dynWordWrap()) return 0; 01332 01333 if (realCursor.col() == 0) return 0; 01334 01335 KateLineRange thisRange; 01336 bool first = true; 01337 01338 do { 01339 thisRange = range(realCursor.line(), first ? 0L : &thisRange); 01340 first = false; 01341 } while (thisRange.wrap && !(realCursor.col() >= thisRange.startCol && realCursor.col() < thisRange.endCol) && thisRange.startCol != thisRange.endCol); 01342 01343 return thisRange.viewLine; 01344 } 01345 01346 int KateViewInternal::displayViewLine(const KateTextCursor& virtualCursor, bool limitToVisible) 01347 { 01348 KateTextCursor work = startPos(); 01349 01350 int limit = linesDisplayed(); 01351 01352 // Efficient non-word-wrapped path 01353 if (!m_view->dynWordWrap()) { 01354 int ret = virtualCursor.line() - startLine(); 01355 if (limitToVisible && (ret < 0 || ret > limit)) 01356 return -1; 01357 else 01358 return ret; 01359 } 01360 01361 if (work == virtualCursor) { 01362 return 0; 01363 } 01364 01365 int ret = -viewLine(work); 01366 bool forwards = (work < virtualCursor) ? true : false; 01367 01368 // FIXME switch to using ranges? faster? 01369 if (forwards) { 01370 while (work.line() != virtualCursor.line()) { 01371 ret += viewLineCount(m_doc->getRealLine(work.line())); 01372 work.setLine(work.line() + 1); 01373 if (limitToVisible && ret > limit) 01374 return -1; 01375 } 01376 } else { 01377 while (work.line() != virtualCursor.line()) { 01378 work.setLine(work.line() - 1); 01379 ret -= viewLineCount(m_doc->getRealLine(work.line())); 01380 if (limitToVisible && ret < 0) 01381 return -1; 01382 } 01383 } 01384 01385 // final difference 01386 KateTextCursor realCursor = virtualCursor; 01387 realCursor.setLine(m_doc->getRealLine(realCursor.line())); 01388 if (realCursor.col() == -1) realCursor.setCol(m_doc->lineLength(realCursor.line())); 01389 ret += viewLine(realCursor); 01390 01391 if (limitToVisible && (ret < 0 || ret > limit)) 01392 return -1; 01393 01394 return ret; 01395 } 01396 01397 uint KateViewInternal::lastViewLine(uint realLine) 01398 { 01399 if (!m_view->dynWordWrap()) return 0; 01400 01401 KateLineRange thisRange; 01402 bool first = true; 01403 01404 do { 01405 thisRange = range(realLine, first ? 0L : &thisRange); 01406 first = false; 01407 } while (thisRange.wrap && thisRange.startCol != thisRange.endCol); 01408 01409 return thisRange.viewLine; 01410 } 01411 01412 uint KateViewInternal::viewLineCount(uint realLine) 01413 { 01414 return lastViewLine(realLine) + 1; 01415 } 01416 01417 /* 01418 * This returns the cursor which is offset by (offset) view lines. 01419 * This is the main function which is called by code not specifically dealing with word-wrap. 01420 * The opposite conversion (cursor to offset) can be done with displayViewLine. 01421 * 01422 * The cursors involved are virtual cursors (ie. equivalent to displayCursor) 01423 */ 01424 KateTextCursor KateViewInternal::viewLineOffset(const KateTextCursor& virtualCursor, int offset, bool keepX) 01425 { 01426 if (!m_view->dynWordWrap()) { 01427 KateTextCursor ret(QMIN((int)m_doc->visibleLines() - 1, virtualCursor.line() + offset), 0); 01428 01429 if (ret.line() < 0) 01430 ret.setLine(0); 01431 01432 if (keepX) { 01433 int realLine = m_doc->getRealLine(ret.line()); 01434 ret.setCol(m_doc->lineLength(realLine) - 1); 01435 01436 if (m_currentMaxX > cXPos) 01437 cXPos = m_currentMaxX; 01438 01439 if (m_doc->wrapCursor()) 01440 cXPos = QMIN(cXPos, (int)m_view->renderer()->textWidth(textLine(realLine), m_doc->lineLength(realLine))); 01441 01442 m_view->renderer()->textWidth(ret, cXPos); 01443 } 01444 01445 return ret; 01446 } 01447 01448 KateTextCursor realCursor = virtualCursor; 01449 realCursor.setLine(m_doc->getRealLine(virtualCursor.line())); 01450 01451 uint cursorViewLine = viewLine(realCursor); 01452 01453 int currentOffset = 0; 01454 int virtualLine = 0; 01455 01456 bool forwards = (offset > 0) ? true : false; 01457 01458 if (forwards) { 01459 currentOffset = lastViewLine(realCursor.line()) - cursorViewLine; 01460 if (offset <= currentOffset) { 01461 // the answer is on the same line 01462 KateLineRange thisRange = range(realCursor.line(), cursorViewLine + offset); 01463 Q_ASSERT(thisRange.virtualLine == virtualCursor.line()); 01464 return KateTextCursor(virtualCursor.line(), thisRange.startCol); 01465 } 01466 01467 virtualLine = virtualCursor.line() + 1; 01468 01469 } else { 01470 offset = -offset; 01471 currentOffset = cursorViewLine; 01472 if (offset <= currentOffset) { 01473 // the answer is on the same line 01474 KateLineRange thisRange = range(realCursor.line(), cursorViewLine - offset); 01475 Q_ASSERT(thisRange.virtualLine == virtualCursor.line()); 01476 return KateTextCursor(virtualCursor.line(), thisRange.startCol); 01477 } 01478 01479 virtualLine = virtualCursor.line() - 1; 01480 } 01481 01482 currentOffset++; 01483 01484 while (virtualLine >= 0 && virtualLine < (int)m_doc->visibleLines()) 01485 { 01486 KateLineRange thisRange; 01487 bool first = true; 01488 int realLine = m_doc->getRealLine(virtualLine); 01489 01490 do { 01491 thisRange = range(realLine, first ? 0L : &thisRange); 01492 first = false; 01493 01494 if (offset == currentOffset) { 01495 if (!forwards) { 01496 // We actually want it the other way around 01497 int requiredViewLine = lastViewLine(realLine) - thisRange.viewLine; 01498 if (requiredViewLine != thisRange.viewLine) { 01499 thisRange = range(realLine, requiredViewLine); 01500 } 01501 } 01502 01503 KateTextCursor ret(virtualLine, thisRange.startCol); 01504 01505 // keep column position 01506 if (keepX) { 01507 ret.setCol(thisRange.endCol - 1); 01508 KateTextCursor realCursorTemp(m_doc->getRealLine(virtualCursor.line()), virtualCursor.col()); 01509 int visibleX = m_view->renderer()->textWidth(realCursorTemp) - range(realCursorTemp).startX; 01510 int xOffset = thisRange.startX; 01511 01512 if (m_currentMaxX > visibleX) 01513 visibleX = m_currentMaxX; 01514 01515 cXPos = xOffset + visibleX; 01516 01517 cXPos = QMIN(cXPos, lineMaxCursorX(thisRange)); 01518 01519 m_view->renderer()->textWidth(ret, cXPos); 01520 } 01521 01522 return ret; 01523 } 01524 01525 currentOffset++; 01526 01527 } while (thisRange.wrap); 01528 01529 if (forwards) 01530 virtualLine++; 01531 else 01532 virtualLine--; 01533 } 01534 01535 // Looks like we were asked for something a bit exotic. 01536 // Return the max/min valid position. 01537 if (forwards) 01538 return KateTextCursor(m_doc->visibleLines() - 1, m_doc->lineLength(m_doc->visibleLines() - 1)); 01539 else 01540 return KateTextCursor(0, 0); 01541 } 01542 01543 int KateViewInternal::lineMaxCursorX(const KateLineRange& range) 01544 { 01545 if (!m_doc->wrapCursor() && !range.wrap) 01546 return INT_MAX; 01547 01548 int maxX = range.endX; 01549 01550 if (maxX && range.wrap) { 01551 QChar lastCharInLine = textLine(range.line)->getChar(range.endCol - 1); 01552 maxX -= m_view->renderer()->config()->fontMetrics()->width(lastCharInLine); 01553 } 01554 01555 return maxX; 01556 } 01557 01558 int KateViewInternal::lineMaxCol(const KateLineRange& range) 01559 { 01560 int maxCol = range.endCol; 01561 01562 if (maxCol && range.wrap) 01563 maxCol--; 01564 01565 return maxCol; 01566 } 01567 01568 void KateViewInternal::cursorUp(bool sel) 01569 { 01570 if (displayCursor.line() == 0 && (!m_view->dynWordWrap() || viewLine(cursor) == 0)) 01571 return; 01572 01573 int newLine = cursor.line(), newCol = 0, xOffset = 0, startCol = 0; 01574 m_preserveMaxX = true; 01575 01576 if (m_view->dynWordWrap()) { 01577 // Dynamic word wrapping - navigate on visual lines rather than real lines 01578 KateLineRange thisRange = currentRange(); 01579 // This is not the first line because that is already simplified out above 01580 KateLineRange pRange = previousRange(); 01581 01582 // Ensure we're in the right spot 01583 Q_ASSERT((cursor.line() == thisRange.line) && 01584 (cursor.col() >= thisRange.startCol) && 01585 (!thisRange.wrap || cursor.col() < thisRange.endCol)); 01586 01587 // VisibleX is the distance from the start of the text to the cursor on the current line. 01588 int visibleX = m_view->renderer()->textWidth(cursor) - thisRange.startX; 01589 int currentLineVisibleX = visibleX; 01590 01591 // Translate to new line 01592 visibleX += thisRange.xOffset(); 01593 visibleX -= pRange.xOffset(); 01594 01595 // Limit to >= 0 01596 visibleX = QMAX(0, visibleX); 01597 01598 startCol = pRange.startCol; 01599 xOffset = pRange.startX; 01600 newLine = pRange.line; 01601 01602 // Take into account current max X (ie. if the current line was smaller 01603 // than the last definitely specified width) 01604 if (thisRange.xOffset() && !pRange.xOffset() && currentLineVisibleX == 0) // Special case for where xOffset may be > m_currentMaxX 01605 visibleX = m_currentMaxX; 01606 else if (visibleX < m_currentMaxX - pRange.xOffset()) 01607 visibleX = m_currentMaxX - pRange.xOffset(); 01608 01609 cXPos = xOffset + visibleX; 01610 01611 cXPos = QMIN(cXPos, lineMaxCursorX(pRange)); 01612 01613 newCol = QMIN((int)m_view->renderer()->textPos(newLine, visibleX, startCol), lineMaxCol(pRange)); 01614 01615 } else { 01616 newLine = m_doc->getRealLine(displayCursor.line() - 1); 01617 01618 if ((m_doc->wrapCursor()) && m_currentMaxX > cXPos) 01619 cXPos = m_currentMaxX; 01620 } 01621 01622 KateTextCursor c(newLine, newCol); 01623 m_view->renderer()->textWidth(c, cXPos); 01624 01625 updateSelection( c, sel ); 01626 updateCursor( c ); 01627 } 01628 01629 void KateViewInternal::cursorDown(bool sel) 01630 { 01631 if ((displayCursor.line() >= (int)m_doc->numVisLines() - 1) && (!m_view->dynWordWrap() || viewLine(cursor) == lastViewLine(cursor.line()))) 01632 return; 01633 01634 int newLine = cursor.line(), newCol = 0, xOffset = 0, startCol = 0; 01635 m_preserveMaxX = true; 01636 01637 if (m_view->dynWordWrap()) { 01638 // Dynamic word wrapping - navigate on visual lines rather than real lines 01639 KateLineRange thisRange = currentRange(); 01640 // This is not the last line because that is already simplified out above 01641 KateLineRange nRange = nextRange(); 01642 01643 // Ensure we're in the right spot 01644 Q_ASSERT((cursor.line() == thisRange.line) && 01645 (cursor.col() >= thisRange.startCol) && 01646 (!thisRange.wrap || cursor.col() < thisRange.endCol)); 01647 01648 // VisibleX is the distance from the start of the text to the cursor on the current line. 01649 int visibleX = m_view->renderer()->textWidth(cursor) - thisRange.startX; 01650 int currentLineVisibleX = visibleX; 01651 01652 // Translate to new line 01653 visibleX += thisRange.xOffset(); 01654 visibleX -= nRange.xOffset(); 01655 01656 // Limit to >= 0 01657 visibleX = QMAX(0, visibleX); 01658 01659 if (!thisRange.wrap) { 01660 newLine = m_doc->getRealLine(displayCursor.line() + 1); 01661 } else { 01662 startCol = thisRange.endCol; 01663 xOffset = thisRange.endX; 01664 } 01665 01666 // Take into account current max X (ie. if the current line was smaller 01667 // than the last definitely specified width) 01668 if (thisRange.xOffset() && !nRange.xOffset() && currentLineVisibleX == 0) // Special case for where xOffset may be > m_currentMaxX 01669 visibleX = m_currentMaxX; 01670 else if (visibleX < m_currentMaxX - nRange.xOffset()) 01671 visibleX = m_currentMaxX - nRange.xOffset(); 01672 01673 cXPos = xOffset + visibleX; 01674 01675 cXPos = QMIN(cXPos, lineMaxCursorX(nRange)); 01676 01677 newCol = QMIN((int)m_view->renderer()->textPos(newLine, visibleX, startCol), lineMaxCol(nRange)); 01678 01679 } else { 01680 newLine = m_doc->getRealLine(displayCursor.line() + 1); 01681 01682 if ((m_doc->wrapCursor()) && m_currentMaxX > cXPos) 01683 cXPos = m_currentMaxX; 01684 } 01685 01686 KateTextCursor c(newLine, newCol); 01687 m_view->renderer()->textWidth(c, cXPos); 01688 01689 updateSelection(c, sel); 01690 updateCursor(c); 01691 } 01692 01693 void KateViewInternal::cursorToMatchingBracket( bool sel ) 01694 { 01695 KateTextCursor start( cursor ), end; 01696 01697 if( !m_doc->findMatchingBracket( start, end ) ) 01698 return; 01699 01700 // The cursor is now placed just to the left of the matching bracket. 01701 // If it's an ending bracket, put it to the right (so we can easily 01702 // get back to the original bracket). 01703 if( end > start ) 01704 end.setCol(end.col() + 1); 01705 01706 updateSelection( end, sel ); 01707 updateCursor( end ); 01708 } 01709 01710 void KateViewInternal::topOfView( bool sel ) 01711 { 01712 KateTextCursor c = viewLineOffset(startPos(), m_minLinesVisible); 01713 updateSelection( c, sel ); 01714 updateCursor( c ); 01715 } 01716 01717 void KateViewInternal::bottomOfView( bool sel ) 01718 { 01719 // FIXME account for wordwrap 01720 KateTextCursor c = viewLineOffset(endPos(), -m_minLinesVisible); 01721 updateSelection( c, sel ); 01722 updateCursor( c ); 01723 } 01724 01725 // lines is the offset to scroll by 01726 void KateViewInternal::scrollLines( int lines, bool sel ) 01727 { 01728 KateTextCursor c = viewLineOffset(displayCursor, lines, true); 01729 01730 // Fix the virtual cursor -> real cursor 01731 c.setLine(m_doc->getRealLine(c.line())); 01732 01733 updateSelection( c, sel ); 01734 updateCursor( c ); 01735 } 01736 01737 // This is a bit misleading... it's asking for the view to be scrolled, not the cursor 01738 void KateViewInternal::scrollUp() 01739 { 01740 KateTextCursor newPos = viewLineOffset(m_startPos, -1); 01741 scrollPos(newPos); 01742 } 01743 01744 void KateViewInternal::scrollDown() 01745 { 01746 KateTextCursor newPos = viewLineOffset(m_startPos, 1); 01747 scrollPos(newPos); 01748 } 01749 01750 void KateViewInternal::setAutoCenterLines(int viewLines, bool updateView) 01751 { 01752 m_autoCenterLines = viewLines; 01753 m_minLinesVisible = QMIN(int((linesDisplayed() - 1)/2), m_autoCenterLines); 01754 if (updateView) 01755 KateViewInternal::updateView(); 01756 } 01757 01758 void KateViewInternal::pageUp( bool sel ) 01759 { 01760 // remember the view line and x pos 01761 int viewLine = displayViewLine(displayCursor); 01762 bool atTop = (startPos().line() == 0 && startPos().col() == 0); 01763 01764 // Adjust for an auto-centering cursor 01765 int lineadj = 2 * m_minLinesVisible; 01766 int cursorStart = (linesDisplayed() - 1) - viewLine; 01767 if (cursorStart < m_minLinesVisible) 01768 lineadj -= m_minLinesVisible - cursorStart; 01769 01770 int linesToScroll = -QMAX( (linesDisplayed() - 1) - lineadj, 0 ); 01771 m_preserveMaxX = true; 01772 01773 // don't scroll the full view in case the scrollbar appears 01774 if (!m_view->dynWordWrap()) { 01775 if (scrollbarVisible(startLine() + linesToScroll + viewLine)) { 01776 if (!m_columnScrollDisplayed) { 01777 linesToScroll++; 01778 } 01779 } else { 01780 if (m_columnScrollDisplayed) { 01781 linesToScroll--; 01782 } 01783 } 01784 } 01785 01786 if (!m_doc->pageUpDownMovesCursor () && !atTop) { 01787 int xPos = m_view->renderer()->textWidth(cursor) - currentRange().startX; 01788 01789 KateTextCursor newStartPos = viewLineOffset(startPos(), linesToScroll - 1); 01790 scrollPos(newStartPos); 01791 01792 // put the cursor back approximately where it was 01793 KateTextCursor newPos = viewLineOffset(newStartPos, viewLine, true); 01794 newPos.setLine(m_doc->getRealLine(newPos.line())); 01795 01796 KateLineRange newLine = range(newPos); 01797 01798 if (m_currentMaxX - newLine.xOffset() > xPos) 01799 xPos = m_currentMaxX - newLine.xOffset(); 01800 01801 cXPos = QMIN(newLine.startX + xPos, lineMaxCursorX(newLine)); 01802 01803 m_view->renderer()->textWidth( newPos, cXPos ); 01804 01805 m_preserveMaxX = true; 01806 updateSelection( newPos, sel ); 01807 updateCursor(newPos); 01808 01809 } else { 01810 scrollLines( linesToScroll, sel ); 01811 } 01812 } 01813 01814 void KateViewInternal::pageDown( bool sel ) 01815 { 01816 // remember the view line 01817 int viewLine = displayViewLine(displayCursor); 01818 bool atEnd = startPos() >= m_cachedMaxStartPos; 01819 01820 // Adjust for an auto-centering cursor 01821 int lineadj = 2 * m_minLinesVisible; 01822 int cursorStart = m_minLinesVisible - viewLine; 01823 if (cursorStart > 0) 01824 lineadj -= cursorStart; 01825 01826 int linesToScroll = QMAX( (linesDisplayed() - 1) - lineadj, 0 ); 01827 m_preserveMaxX = true; 01828 01829 // don't scroll the full view in case the scrollbar appears 01830 if (!m_view->dynWordWrap()) { 01831 if (scrollbarVisible(startLine() + linesToScroll + viewLine - (linesDisplayed() - 1))) { 01832 if (!m_columnScrollDisplayed) { 01833 linesToScroll--; 01834 } 01835 } else { 01836 if (m_columnScrollDisplayed) { 01837 linesToScroll--; 01838 } 01839 } 01840 } 01841 01842 if (!m_doc->pageUpDownMovesCursor () && !atEnd) { 01843 int xPos = m_view->renderer()->textWidth(cursor) - currentRange().startX; 01844 01845 KateTextCursor newStartPos = viewLineOffset(startPos(), linesToScroll + 1); 01846 scrollPos(newStartPos); 01847 01848 // put the cursor back approximately where it was 01849 KateTextCursor newPos = viewLineOffset(newStartPos, viewLine, true); 01850 newPos.setLine(m_doc->getRealLine(newPos.line())); 01851 01852 KateLineRange newLine = range(newPos); 01853 01854 if (m_currentMaxX - newLine.xOffset() > xPos) 01855 xPos = m_currentMaxX - newLine.xOffset(); 01856 01857 cXPos = QMIN(newLine.startX + xPos, lineMaxCursorX(newLine)); 01858 01859 m_view->renderer()->textWidth( newPos, cXPos ); 01860 01861 m_preserveMaxX = true; 01862 updateSelection( newPos, sel ); 01863 updateCursor(newPos); 01864 01865 } else { 01866 scrollLines( linesToScroll, sel ); 01867 } 01868 } 01869 01870 bool KateViewInternal::scrollbarVisible(uint startLine) 01871 { 01872 return maxLen(startLine) > width() - 8; 01873 } 01874 01875 int KateViewInternal::maxLen(uint startLine) 01876 { 01877 // Q_ASSERT(!m_view->dynWordWrap()); 01878 01879 int displayLines = (m_view->height() / m_view->renderer()->fontHeight()) + 1; 01880 01881 int maxLen = 0; 01882 01883 for (int z = 0; z < displayLines; z++) { 01884 int virtualLine = startLine + z; 01885 01886 if (virtualLine < 0 || virtualLine >= (int)m_doc->visibleLines()) 01887 break; 01888 01889 KateLineRange thisRange = range((int)m_doc->getRealLine(virtualLine)); 01890 01891 maxLen = QMAX(maxLen, thisRange.endX); 01892 } 01893 01894 return maxLen; 01895 } 01896 01897 void KateViewInternal::top( bool sel ) 01898 { 01899 KateTextCursor c( 0, cursor.col() ); 01900 m_view->renderer()->textWidth( c, cXPos ); 01901 updateSelection( c, sel ); 01902 updateCursor( c ); 01903 } 01904 01905 void KateViewInternal::bottom( bool sel ) 01906 { 01907 KateTextCursor c( m_doc->lastLine(), cursor.col() ); 01908 m_view->renderer()->textWidth( c, cXPos ); 01909 updateSelection( c, sel ); 01910 updateCursor( c ); 01911 } 01912 01913 void KateViewInternal::top_home( bool sel ) 01914 { 01915 KateTextCursor c( 0, 0 ); 01916 updateSelection( c, sel ); 01917 updateCursor( c ); 01918 } 01919 01920 void KateViewInternal::bottom_end( bool sel ) 01921 { 01922 KateTextCursor c( m_doc->lastLine(), m_doc->lineLength( m_doc->lastLine() ) ); 01923 updateSelection( c, sel ); 01924 updateCursor( c ); 01925 } 01926 01927 void KateViewInternal::updateSelection( const KateTextCursor& _newCursor, bool keepSel ) 01928 { 01929 KateTextCursor newCursor = _newCursor; 01930 if( keepSel ) 01931 { 01932 if ( !m_doc->hasSelection() || (selectAnchor.line() == -1) 01933 || ((m_doc->configFlags() & KateDocument::cfPersistent) 01934 && ((cursor < m_doc->selectStart) || (cursor > m_doc->selectEnd))) ) 01935 { 01936 selectAnchor = cursor; 01937 m_doc->setSelection( cursor, newCursor ); 01938 } 01939 else 01940 { 01941 bool doSelect = true; 01942 switch (m_selectionMode) 01943 { 01944 case Word: 01945 { 01946 bool same = ( newCursor.line() == selStartCached.line() ); 01947 uint c; 01948 if ( newCursor.line() > selStartCached.line() || 01949 ( same && newCursor.col() > selEndCached.col() ) ) 01950 { 01951 selectAnchor = selStartCached; 01952 01953 KateTextLine::Ptr l = m_doc->kateTextLine( newCursor.line() ); 01954 01955 for ( c = newCursor.col(); c < l->length(); c++ ) 01956 if ( !m_doc->m_highlight->isInWord( l->getChar( c ) ) ) 01957 break; 01958 01959 newCursor.setCol( c ); 01960 } 01961 else if ( newCursor.line() < selStartCached.line() || 01962 ( same && newCursor.col() < selStartCached.col() ) ) 01963 { 01964 selectAnchor = selEndCached; 01965 01966 KateTextLine::Ptr l = m_doc->kateTextLine( newCursor.line() ); 01967 01968 for ( c = newCursor.col(); c > 0; c-- ) 01969 if ( !m_doc->m_highlight->isInWord( l->getChar( c ) ) ) 01970 break; 01971 01972 newCursor.setCol( c+1 ); 01973 } 01974 else 01975 doSelect = false; 01976 01977 } 01978 break; 01979 case Line: 01980 if ( newCursor.line() > selStartCached.line() ) 01981 { 01982 selectAnchor = selStartCached; 01983 newCursor.setCol( m_doc->textLine( newCursor.line() ).length() ); 01984 } 01985 else if ( newCursor.line() < selStartCached.line() ) 01986 { 01987 selectAnchor = selEndCached; 01988 newCursor.setCol( 0 ); 01989 } 01990 else // same line, ignore 01991 doSelect = false; 01992 break; 01993 default: // *allways* keep original selection for mouse 01994 { 01995 if ( selStartCached.line() < 0 ) // invalid 01996 break; 01997 01998 if ( newCursor.line() > selEndCached.line() || 01999 ( newCursor.line() == selEndCached.line() && 02000 newCursor.col() > selEndCached.col() ) ) 02001 selectAnchor = selStartCached; 02002 02003 else if ( newCursor.line() < selStartCached.line() || 02004 ( newCursor.line() == selStartCached.line() && 02005 newCursor.col() < selStartCached.col() ) ) 02006 selectAnchor = selEndCached; 02007 02008 else 02009 doSelect = false; 02010 } 02011 // break; 02012 } 02013 02014 if ( doSelect ) 02015 m_doc->setSelection( selectAnchor, newCursor); 02016 else if ( selStartCached.line() > 0 ) // we have a cached selectino, so we restore that 02017 m_doc->setSelection( selStartCached, selEndCached ); 02018 } 02019 02020 m_selChangedByUser = true; 02021 } 02022 else if ( !(m_doc->configFlags() & KateDocument::cfPersistent) ) 02023 m_doc->clearSelection(); 02024 } 02025 02026 void KateViewInternal::updateCursor( const KateTextCursor& newCursor, bool force, bool center, bool calledExternally ) 02027 { 02028 KateTextLine::Ptr l = textLine( newCursor.line() ); 02029 02030 if ( !force && (cursor == newCursor) ) 02031 { 02032 if ( !m_madeVisible ) 02033 { 02034 // unfold if required 02035 if ( l && ! l->isVisible() ) 02036 m_doc->foldingTree()->ensureVisible( newCursor.line() ); 02037 02038 makeVisible ( displayCursor, displayCursor.col(), false, center, calledExternally ); 02039 } 02040 02041 return; 02042 } 02043 02044 // remove trailing spaces ### really not nice here, unless it is *really* nessecary 02045 // if ( m_doc->isReadWrite() && cursor.line() != newCursor.line() ) 02046 // m_doc->removeTrailingSpace( cursor.line() ); 02047 02048 // unfold if required 02049 if ( l && ! l->isVisible() ) 02050 m_doc->foldingTree()->ensureVisible( newCursor.line() ); 02051 02052 KateTextCursor oldDisplayCursor = displayCursor; 02053 02054 cursor.setPos (newCursor); 02055 displayCursor.setPos (m_doc->getVirtualLine(cursor.line()), cursor.col()); 02056 02057 cXPos = m_view->renderer()->textWidth( cursor ); 02058 makeVisible ( displayCursor, displayCursor.col(), false, center, calledExternally ); 02059 02060 updateBracketMarks(); 02061 02062 // It's efficient enough to just tag them both without checking to see if they're on the same view line 02063 tagLine(oldDisplayCursor); 02064 tagLine(displayCursor); 02065 02066 QPoint cursorP = cursorCoordinates(); 02067 setMicroFocusHint( cursorP.x(), cursorP.y(), 0, m_view->renderer()->fontHeight() ); 02068 02069 if (m_cursorTimer.isActive ()) 02070 { 02071 if ( KApplication::cursorFlashTime() > 0 ) 02072 m_cursorTimer.start( KApplication::cursorFlashTime() / 2 ); 02073 m_view->renderer()->setDrawCaret(true); 02074 } 02075 02076 // Remember the maximum X position if requested 02077 if (m_preserveMaxX) 02078 m_preserveMaxX = false; 02079 else 02080 if (m_view->dynWordWrap()) 02081 m_currentMaxX = m_view->renderer()->textWidth(displayCursor) - currentRange().startX + currentRange().xOffset(); 02082 else 02083 m_currentMaxX = cXPos; 02084 02085 //kdDebug() << "m_currentMaxX: " << m_currentMaxX << " (was "<< oldmaxx << "), cXPos: " << cXPos << endl; 02086 //kdDebug(13030) << "Cursor now located at real " << cursor.line << "," << cursor.col << ", virtual " << displayCursor.line << ", " << displayCursor.col << "; Top is " << startLine() << ", " << startPos().col << "; Old top is " << m_oldStartPos.line << ", " << m_oldStartPos.col << endl; 02087 02088 paintText(0, 0, width(), height(), true); 02089 02090 emit m_view->cursorPositionChanged(); 02091 } 02092 02093 void KateViewInternal::updateBracketMarks() 02094 { 02095 if ( bm.isValid() ) { 02096 KateTextCursor bmStart(m_doc->getVirtualLine(bm.start().line()), bm.start().col()); 02097 KateTextCursor bmEnd(m_doc->getVirtualLine(bm.end().line()), bm.end().col()); 02098 tagLine(bmStart); 02099 tagLine(bmEnd); 02100 } 02101 02102 m_doc->newBracketMark( cursor, bm ); 02103 02104 if ( bm.isValid() ) { 02105 KateTextCursor bmStart(m_doc->getVirtualLine(bm.start().line()), bm.start().col()); 02106 KateTextCursor bmEnd(m_doc->getVirtualLine(bm.end().line()), bm.end().col()); 02107 tagLine(bmStart); 02108 tagLine(bmEnd); 02109 } 02110 } 02111 02112 bool KateViewInternal::tagLine(const KateTextCursor& virtualCursor) 02113 { 02114 int viewLine = displayViewLine(virtualCursor, true); 02115 if (viewLine >= 0 && viewLine < (int)lineRanges.count()) { 02116 lineRanges[viewLine].dirty = true; 02117 leftBorder->update (0, lineToY(viewLine), leftBorder->width(), m_view->renderer()->fontHeight()); 02118 return true; 02119 } 02120 return false; 02121 } 02122 02123 bool KateViewInternal::tagLines( int start, int end, bool realLines ) 02124 { 02125 return tagLines(KateTextCursor(start, 0), KateTextCursor(end, -1), realLines); 02126 } 02127 02128 bool KateViewInternal::tagLines(KateTextCursor start, KateTextCursor end, bool realCursors) 02129 { 02130 if (realCursors) 02131 { 02132 //kdDebug()<<"realLines is true"<<endl; 02133 start.setLine(m_doc->getVirtualLine( start.line() )); 02134 end.setLine(m_doc->getVirtualLine( end.line() )); 02135 } 02136 02137 if (end.line() < (int)startLine()) 02138 { 02139 //kdDebug()<<"end<startLine"<<endl; 02140 return false; 02141 } 02142 if (start.line() > (int)endLine()) 02143 { 02144 //kdDebug()<<"start> endLine"<<start<<" "<<((int)endLine())<<endl; 02145 return false; 02146 } 02147 02148 //kdDebug(13030) << "tagLines( [" << start.line << "," << start.col << "], [" << end.line << "," << end.col << "] )\n"; 02149 02150 bool ret = false; 02151 02152 for (uint z = 0; z < lineRanges.size(); z++) 02153 { 02154 if ((lineRanges[z].virtualLine > start.line() || (lineRanges[z].virtualLine == start.line() && lineRanges[z].endCol >= start.col() && start.col() != -1)) && (lineRanges[z].virtualLine < end.line() || (lineRanges[z].virtualLine == end.line() && (lineRanges[z].startCol <= end.col() || end.col() == -1)))) { 02155 ret = lineRanges[z].dirty = true; 02156 //kdDebug() << "Tagged line " << lineRanges[z].line << endl; 02157 } 02158 } 02159 02160 if (!m_view->dynWordWrap()) 02161 { 02162 int y = lineToY( start.line() ); 02163 // FIXME is this enough for when multiple lines are deleted 02164 int h = (end.line() - start.line() + 2) * m_view->renderer()->fontHeight(); 02165 if (end.line() == (int)m_doc->numVisLines() - 1) 02166 h = height(); 02167 02168 leftBorder->update (0, y, leftBorder->width(), h); 02169 } 02170 else 02171 { 02172 // FIXME Do we get enough good info in editRemoveText to optimise this more? 02173 //bool justTagged = false; 02174 for (uint z = 0; z < lineRanges.size(); z++) 02175 { 02176 if ((lineRanges[z].virtualLine > start.line() || (lineRanges[z].virtualLine == start.line() && lineRanges[z].endCol >= start.col() && start.col() != -1)) && (lineRanges[z].virtualLine < end.line() || (lineRanges[z].virtualLine == end.line() && (lineRanges[z].startCol <= end.col() || end.col() == -1)))) 02177 { 02178 //justTagged = true; 02179 leftBorder->update (0, z * m_view->renderer()->fontHeight(), leftBorder->width(), leftBorder->height()); 02180 break; 02181 } 02182 /*else if (justTagged) 02183 { 02184 justTagged = false; 02185 leftBorder->update (0, z * m_doc->viewFont.fontHeight, leftBorder->width(), m_doc->viewFont.fontHeight); 02186 break; 02187 }*/ 02188 } 02189 } 02190 02191 return ret; 02192 } 02193 02194 void KateViewInternal::tagAll() 02195 { 02196 //kdDebug(13030) << "tagAll()" << endl; 02197 for (uint z = 0; z < lineRanges.size(); z++) 02198 { 02199 lineRanges[z].dirty = true; 02200 } 02201 02202 leftBorder->updateFont(); 02203 leftBorder->update (); 02204 } 02205 02206 void KateViewInternal::paintCursor() 02207 { 02208 if (tagLine(displayCursor)) 02209 paintText (0,0,width(), height(), true); 02210 } 02211 02212 // Point in content coordinates 02213 void KateViewInternal::placeCursor( const QPoint& p, bool keepSelection, bool updateSelection ) 02214 { 02215 KateLineRange thisRange = yToKateLineRange(p.y()); 02216 02217 if (thisRange.line == -1) { 02218 for (int i = (p.y() / m_view->renderer()->fontHeight()); i >= 0; i--) { 02219 thisRange = lineRanges[i]; 02220 if (thisRange.line != -1) 02221 break; 02222 } 02223 Q_ASSERT(thisRange.line != -1); 02224 } 02225 02226 int realLine = thisRange.line; 02227 int visibleLine = thisRange.virtualLine; 02228 uint startCol = thisRange.startCol; 02229 02230 visibleLine = QMAX( 0, QMIN( visibleLine, int(m_doc->numVisLines()) - 1 ) ); 02231 02232 KateTextCursor c(realLine, 0); 02233 02234 int x = QMIN(QMAX(0, p.x() - thisRange.xOffset()), lineMaxCursorX(thisRange) - thisRange.startX); 02235 02236 m_view->renderer()->textWidth( c, startX() + x, startCol); 02237 02238 if (updateSelection) 02239 KateViewInternal::updateSelection( c, keepSelection ); 02240 updateCursor( c ); 02241 } 02242 02243 // Point in content coordinates 02244 bool KateViewInternal::isTargetSelected( const QPoint& p ) 02245 { 02246 KateLineRange thisRange = yToKateLineRange(p.y()); 02247 02248 KateTextLine::Ptr l = textLine( thisRange.line ); 02249 if( !l ) 02250 return false; 02251 02252 int col = m_view->renderer()->textPos( l, p.x() - thisRange.xOffset(), thisRange.startCol, false ); 02253 02254 return m_doc->lineColSelected( thisRange.line, col ); 02255 } 02256 02257 // 02258 // BEGIN EVENT HANDLING STUFF !!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!! 02259 // 02260 02261 bool KateViewInternal::eventFilter( QObject *obj, QEvent *e ) 02262 { 02263 if (obj == m_lineScroll) 02264 { 02265 // the second condition is to make sure a scroll on the vertical bar doesn't cause a horizontal scroll ;) 02266 if (e->type() == QEvent::Wheel && m_lineScroll->minValue() != m_lineScroll->maxValue()) 02267 { 02268 wheelEvent((QWheelEvent*)e); 02269 return true; 02270 } 02271 02272 // continue processing 02273 return QWidget::eventFilter( obj, e ); 02274 } 02275 02276 switch( e->type() ) 02277 { 02278 case QEvent::KeyPress: 02279 { 02280 QKeyEvent *k = (QKeyEvent *)e; 02281 02282 if ((k->key() == Qt::Key_Escape) && !(m_doc->configFlags() & KateDocument::cfPersistent) ) 02283 { 02284 m_doc->clearSelection(); 02285 return true; 02286 } 02287 else if ( !((k->state() & ControlButton) || (k->state() & AltButton)) ) 02288 { 02289 keyPressEvent( k ); 02290 return k->isAccepted(); 02291 } 02292 02293 } break; 02294 02295 case QEvent::DragMove: 02296 { 02297 QPoint currentPoint = ((QDragMoveEvent*) e)->pos(); 02298 02299 QRect doNotScrollRegion( scrollMargin, scrollMargin, 02300 width() - scrollMargin * 2, 02301 height() - scrollMargin * 2 ); 02302 02303 if ( !doNotScrollRegion.contains( currentPoint ) ) 02304 { 02305 startDragScroll(); 02306 // Keep sending move events 02307 ( (QDragMoveEvent*)e )->accept( QRect(0,0,0,0) ); 02308 } 02309 02310 dragMoveEvent((QDragMoveEvent*)e); 02311 } break; 02312 02313 case QEvent::DragLeave: 02314 stopDragScroll(); 02315 break; 02316 02317 default: 02318 break; 02319 } 02320 02321 return QWidget::eventFilter( obj, e ); 02322 } 02323 02324 void KateViewInternal::keyPressEvent( QKeyEvent* e ) 02325 { 02326 KKey key(e); 02327 02328 if (key == Qt::Key_Left) 02329 { 02330 m_view->cursorLeft(); 02331 e->accept(); 02332 return; 02333 } 02334 02335 if (key == Qt::Key_Right) 02336 { 02337 m_view->cursorRight(); 02338 e->accept(); 02339 return; 02340 } 02341 02342 if (key == Qt::Key_Down) 02343 { 02344 m_view->down(); 02345 e->accept(); 02346 return; 02347 } 02348 02349 if (key == Qt::Key_Up) 02350 { 02351 m_view->up(); 02352 e->accept(); 02353 return; 02354 } 02355 02356 if( !m_doc->isReadWrite() ) 02357 { 02358 e->ignore(); 02359 return; 02360 } 02361 02362 if ((key == Qt::Key_Return) || (key == Qt::Key_Enter)) 02363 { 02364 m_view->keyReturn(); 02365 e->accept(); 02366 return; 02367 } 02368 02369 if ((key == SHIFT + Qt::Key_Return) || (key == SHIFT + Qt::Key_Enter)) 02370 { 02371 uint ln = cursor.line(); 02372 KateTextLine::Ptr line = m_doc->kateTextLine( ln ); 02373 int pos = line->firstChar(); 02374 if (pos != -1) { 02375 while ((int)line->length() > pos && !line->getChar(pos).isLetterOrNumber()) ++pos; 02376 } else { 02377 pos = line->length(); // stay indented 02378 } 02379 m_doc->insertText( cursor.line(), line->length(), "\n" + line->string(0, pos) ); 02380 cursor.setPos(ln + 1, pos); 02381 updateCursor(cursor, true); 02382 updateView(); 02383 e->accept(); 02384 return; 02385 } 02386 02387 if (key == Qt::Key_Backspace || key == SHIFT + Qt::Key_Backspace) 02388 { 02389 m_view->backspace(); 02390 e->accept(); 02391 return; 02392 } 02393 02394 if (key == Qt::Key_Delete) 02395 { 02396 m_view->keyDelete(); 02397 e->accept(); 02398 return; 02399 } 02400 02401 if( (key == Qt::Key_Tab || key == SHIFT+Qt::Key_Backtab || key == Qt::Key_Backtab) 02402 && (m_doc->configFlags() & KateDocumentConfig::cfTabIndents) ) 02403 { 02404 if( key == Qt::Key_Tab ) 02405 { 02406 if (m_doc->hasSelection() || (m_doc->configFlags() & KateDocumentConfig::cfTabIndentsMode)) 02407 m_doc->indent( m_view, cursor.line(), 1 ); 02408 else if (m_doc->configFlags() & KateDocumentConfig::cfTabInsertsTab) 02409 m_doc->typeChars ( m_view, QString ("\t") ); 02410 else 02411 m_doc->insertIndentChars ( m_view ); 02412 02413 e->accept(); 02414 return; 02415 } 02416 02417 if (key == SHIFT+Qt::Key_Backtab || key == Qt::Key_Backtab) 02418 { 02419 m_doc->indent( m_view, cursor.line(), -1 ); 02420 e->accept(); 02421 return; 02422 } 02423 } 02424 02425 if ( !(e->state() & ControlButton) && !(e->state() & AltButton) 02426 && m_doc->typeChars ( m_view, e->text() ) ) 02427 { 02428 e->accept(); 02429 return; 02430 } 02431 02432 e->ignore(); 02433 } 02434 02435 void KateViewInternal::keyReleaseEvent( QKeyEvent* e ) 02436 { 02437 KKey key(e); 02438 02439 if (key == SHIFT) 02440 m_shiftKeyPressed = true; 02441 else 02442 { 02443 if (m_shiftKeyPressed) 02444 { 02445 m_shiftKeyPressed = false; 02446 02447 if (m_selChangedByUser) 02448 { 02449 QApplication::clipboard()->setSelectionMode( true ); 02450 m_doc->copy(); 02451 QApplication::clipboard()->setSelectionMode( false ); 02452 02453 m_selChangedByUser = false; 02454 } 02455 } 02456 } 02457 02458 e->ignore(); 02459 return; 02460 } 02461 02462 void KateViewInternal::mousePressEvent( QMouseEvent* e ) 02463 { 02464 switch (e->button()) 02465 { 02466 case LeftButton: 02467 m_selChangedByUser = false; 02468 02469 if (possibleTripleClick) 02470 { 02471 possibleTripleClick = false; 02472 02473 m_selectionMode = Line; 02474 02475 if ( e->state() & Qt::ShiftButton ) 02476 { 02477 updateSelection( cursor, true ); 02478 } 02479 else 02480 { 02481 m_doc->selectLine( cursor ); 02482 } 02483 02484 QApplication::clipboard()->setSelectionMode( true ); 02485 m_doc->copy(); 02486 QApplication::clipboard()->setSelectionMode( false ); 02487 02488 selStartCached = m_doc->selectStart; 02489 selEndCached = m_doc->selectEnd; 02490 02491 cursor.setCol(0); 02492 updateCursor( cursor ); 02493 return; 02494 } 02495 02496 if ( e->state() & Qt::ShiftButton ) 02497 { 02498 selStartCached = m_doc->selectStart; 02499 selEndCached = m_doc->selectEnd; 02500 } 02501 else 02502 selStartCached.setLine( -1 ); // invalidate 02503 02504 if( isTargetSelected( e->pos() ) ) 02505 { 02506 dragInfo.state = diPending; 02507 dragInfo.start = e->pos(); 02508 } 02509 else 02510 { 02511 dragInfo.state = diNone; 02512 02513 placeCursor( e->pos(), e->state() & ShiftButton ); 02514 02515 scrollX = 0; 02516 scrollY = 0; 02517 02518 m_scrollTimer.start (50); 02519 } 02520 02521 e->accept (); 02522 break; 02523 02524 // try to show popup menu 02525 case RightButton: 02526 if ( ! isTargetSelected( e->pos() ) ) 02527 placeCursor( e->pos() ); 02528 02529 // popup is a qguardedptr now 02530 if (m_view->popup()) 02531 m_view->popup()->popup( mapToGlobal( e->pos() ) ); 02532 02533 e->accept (); 02534 break; 02535 02536 default: 02537 e->ignore (); 02538 break; 02539 } 02540 } 02541 02542 void KateViewInternal::mouseDoubleClickEvent(QMouseEvent *e) 02543 { 02544 switch (e->button()) 02545 { 02546 case LeftButton: 02547 m_selectionMode = Word; 02548 02549 if ( e->state() & Qt::ShiftButton ) 02550 { 02551 selStartCached = m_doc->selectStart; 02552 selEndCached = m_doc->selectEnd; 02553 updateSelection( cursor, true ); 02554 } 02555 else 02556 { 02557 m_doc->selectWord( cursor ); 02558 } 02559 02560 // Move cursor to end of selected word 02561 if (m_doc->hasSelection()) 02562 { 02563 QApplication::clipboard()->setSelectionMode( true ); 02564 m_doc->copy(); 02565 QApplication::clipboard()->setSelectionMode( false ); 02566 02567 cursor.setPos(m_doc->selectEnd); 02568 updateCursor( cursor ); 02569 02570 selStartCached = m_doc->selectStart; 02571 selEndCached = m_doc->selectEnd; 02572 } 02573 02574 possibleTripleClick = true; 02575 QTimer::singleShot ( QApplication::doubleClickInterval(), this, SLOT(tripleClickTimeout()) ); 02576 02577 e->accept (); 02578 break; 02579 02580 default: 02581 e->ignore (); 02582 break; 02583 } 02584 } 02585 02586 void KateViewInternal::tripleClickTimeout() 02587 { 02588 possibleTripleClick = false; 02589 } 02590 02591 void KateViewInternal::mouseReleaseEvent( QMouseEvent* e ) 02592 { 02593 switch (e->button()) 02594 { 02595 case LeftButton: 02596 m_selectionMode = Default; 02597 selStartCached.setLine( -1 ); 02598 02599 if (m_selChangedByUser) 02600 { 02601 QApplication::clipboard()->setSelectionMode( true ); 02602 m_doc->copy(); 02603 QApplication::clipboard()->setSelectionMode( false ); 02604 02605 m_selChangedByUser = false; 02606 } 02607 02608 if (dragInfo.state == diPending) 02609 placeCursor( e->pos() ); 02610 else if (dragInfo.state == diNone) 02611 m_scrollTimer.stop (); 02612 02613 dragInfo.state = diNone; 02614 02615 e->accept (); 02616 break; 02617 02618 case MidButton: 02619 placeCursor( e->pos() ); 02620 02621 if( m_doc->isReadWrite() ) 02622 { 02623 QApplication::clipboard()->setSelectionMode( true ); 02624 doPaste(); 02625 QApplication::clipboard()->setSelectionMode( false ); 02626 } 02627 02628 e->accept (); 02629 break; 02630 02631 default: 02632 e->ignore (); 02633 break; 02634 } 02635 } 02636 02637 void KateViewInternal::mouseMoveEvent( QMouseEvent* e ) 02638 { 02639 if( e->state() & LeftButton ) 02640 { 02641 if (dragInfo.state == diPending) 02642 { 02643 // we had a mouse down, but haven't confirmed a drag yet 02644 // if the mouse has moved sufficiently, we will confirm 02645 QPoint p( e->pos() - dragInfo.start ); 02646 02647 // we've left the drag square, we can start a real drag operation now 02648 if( p.manhattanLength() > KGlobalSettings::dndEventDelay() ) 02649 doDrag(); 02650 02651 return; 02652 } 02653 02654 mouseX = e->x(); 02655 mouseY = e->y(); 02656 02657 scrollX = 0; 02658 scrollY = 0; 02659 int d = m_view->renderer()->fontHeight(); 02660 02661 if (mouseX < 0) 02662 scrollX = -d; 02663 02664 if (mouseX > width()) 02665 scrollX = d; 02666 02667 if (mouseY < 0) 02668 { 02669 mouseY = 0; 02670 scrollY = -d; 02671 } 02672 02673 if (mouseY > height()) 02674 { 02675 mouseY = height(); 02676 scrollY = d; 02677 } 02678 02679 placeCursor( QPoint( mouseX, mouseY ), true ); 02680 02681 } 02682 else 02683 { 02684 if (m_textHintEnabled) 02685 { 02686 m_textHintTimer.start(m_textHintTimeout); 02687 m_textHintMouseX=e->x(); 02688 m_textHintMouseY=e->y(); 02689 } 02690 } 02691 } 02692 02693 void KateViewInternal::paintEvent(QPaintEvent *e) 02694 { 02695 paintText(e->rect().x(), e->rect().y(), e->rect().width(), e->rect().height()); 02696 } 02697 02698 void KateViewInternal::resizeEvent(QResizeEvent* e) 02699 { 02700 bool expandedHorizontally = width() > e->oldSize().width(); 02701 bool expandedVertically = height() > e->oldSize().height(); 02702 bool heightChanged = height() != e->oldSize().height(); 02703 02704 m_madeVisible = false; 02705 02706 if (heightChanged) { 02707 setAutoCenterLines(m_autoCenterLines, false); 02708 m_cachedMaxStartPos.setPos(-1, -1); 02709 } 02710 02711 if (m_view->dynWordWrap()) { 02712 bool dirtied = false; 02713 02714 for (uint i = 0; i < lineRanges.count(); i++) { 02715 // find the first dirty line 02716 // the word wrap updateView algorithm is forced to check all lines after a dirty one 02717 if (lineRanges[i].wrap || 02718 (!expandedHorizontally && (lineRanges[i].endX - lineRanges[i].startX) > width())) { 02719 dirtied = lineRanges[i].dirty = true; 02720 break; 02721 } 02722 } 02723 02724 if (dirtied || heightChanged) { 02725 updateView(true); 02726 leftBorder->update(); 02727 } 02728 02729 if (width() < e->oldSize().width()) { 02730 if (!m_doc->wrapCursor()) { 02731 // May have to restrain cursor to new smaller width... 02732 if (cursor.col() > m_doc->lineLength(cursor.line())) { 02733 KateLineRange thisRange = currentRange(); 02734 02735 KateTextCursor newCursor(cursor.line(), thisRange.endCol + ((width() - thisRange.xOffset() - (thisRange.endX - thisRange.startX)) / m_view->renderer()->spaceWidth()) - 1); 02736 updateCursor(newCursor); 02737 } 02738 } 02739 } 02740 02741 } else { 02742 updateView(); 02743 02744 if (expandedHorizontally && startX() > 0) 02745 scrollColumns(startX() - (width() - e->oldSize().width())); 02746 } 02747 02748 if (expandedVertically) { 02749 KateTextCursor max = maxStartPos(); 02750 if (startPos() > max) 02751 scrollPos(max); 02752 } 02753 } 02754 02755 void KateViewInternal::scrollTimeout () 02756 { 02757 if (scrollX || scrollY) 02758 { 02759 scrollLines (startPos().line() + (scrollY / (int)m_view->renderer()->fontHeight())); 02760 placeCursor( QPoint( mouseX, mouseY ), true ); 02761 } 02762 } 02763 02764 void KateViewInternal::cursorTimeout () 02765 { 02766 m_view->renderer()->setDrawCaret(!m_view->renderer()->drawCaret()); 02767 paintCursor(); 02768 } 02769 02770 void KateViewInternal::textHintTimeout () 02771 { 02772 m_textHintTimer.stop (); 02773 02774 KateLineRange thisRange = yToKateLineRange(m_textHintMouseY); 02775 02776 if (thisRange.line == -1) return; 02777 02778 if (m_textHintMouseX> (lineMaxCursorX(thisRange) - thisRange.startX)) return; 02779 02780 int realLine = thisRange.line; 02781 int startCol = thisRange.startCol; 02782 02783 KateTextCursor c(realLine, 0); 02784 m_view->renderer()->textWidth( c, startX() + m_textHintMouseX, startCol); 02785 02786 QString tmp; 02787 02788 emit m_view->needTextHint(c.line(), c.col(), tmp); 02789 02790 if (!tmp.isEmpty()) kdDebug(13030)<<"Hint text: "<<tmp<<endl; 02791 } 02792 02793 void KateViewInternal::focusInEvent (QFocusEvent *) 02794 { 02795 if (KApplication::cursorFlashTime() > 0) 02796 m_cursorTimer.start ( KApplication::cursorFlashTime() / 2 ); 02797 02798 if (m_textHintEnabled) 02799 m_textHintTimer.start( m_textHintTimeout ); 02800 02801 paintCursor(); 02802 02803 m_doc->m_activeView = m_view; 02804 02805 emit m_view->gotFocus( m_view ); 02806 } 02807 02808 void KateViewInternal::focusOutEvent (QFocusEvent *) 02809 { 02810 if( ! m_view->m_codeCompletion->codeCompletionVisible() ) 02811 { 02812 m_cursorTimer.stop(); 02813 02814 m_view->renderer()->setDrawCaret(true); 02815 paintCursor(); 02816 emit m_view->lostFocus( m_view ); 02817 } 02818 02819 m_textHintTimer.stop(); 02820 } 02821 02822 void KateViewInternal::doDrag() 02823 { 02824 dragInfo.state = diDragging; 02825 dragInfo.dragObject = new QTextDrag(m_doc->selection(), this); 02826 dragInfo.dragObject->dragCopy(); 02827 } 02828 02829 void KateViewInternal::dragEnterEvent( QDragEnterEvent* event ) 02830 { 02831 event->accept( (QTextDrag::canDecode(event) && m_doc->isReadWrite()) || 02832 KURLDrag::canDecode(event) ); 02833 } 02834 02835 void KateViewInternal::dragMoveEvent( QDragMoveEvent* event ) 02836 { 02837 // track the cursor to the current drop location 02838 placeCursor( event->pos(), true, false ); 02839 } 02840 02841 void KateViewInternal::dropEvent( QDropEvent* event ) 02842 { 02843 if ( KURLDrag::canDecode(event) ) { 02844 02845 emit dropEventPass(event); 02846 02847 } else if ( QTextDrag::canDecode(event) && m_doc->isReadWrite() ) { 02848 02849 QString text; 02850 02851 if (!QTextDrag::decode(event, text)) 02852 return; 02853 02854 // is the source our own document? 02855 bool priv = false; 02856 if (event->source() && event->source()->inherits("KateViewInternal")) 02857 priv = m_doc->ownedView( ((KateViewInternal*)(event->source()))->m_view ); 02858 02859 // dropped on a text selection area? 02860 bool selected = isTargetSelected( event->pos() ); 02861 02862 if( priv && selected ) { 02863 // this is a drag that we started and dropped on our selection 02864 // ignore this case 02865 return; 02866 } 02867 02868 // atm only copy the text, no move 02869 m_doc->insertText( cursor.line(), cursor.col(), text ); 02870 placeCursor( event->pos() ); 02871 02872 updateView(); 02873 } 02874 } 02875 02876 void KateViewInternal::imStartEvent( QIMEvent *e ) 02877 { 02878 if ( m_doc->m_bReadOnly ) { 02879 e->ignore(); 02880 return; 02881 } 02882 02883 if ( m_doc->hasSelection() ) 02884 m_doc->removeSelectedText(); 02885 02886 m_imPreeditStartLine = cursor.line(); 02887 m_imPreeditStart = cursor.col(); 02888 m_imPreeditLength = 0; 02889 02890 m_doc->setIMSelectionValue( m_imPreeditStartLine, m_imPreeditStart, 0, 0, 0, true ); 02891 } 02892 02893 void KateViewInternal::imComposeEvent( QIMEvent *e ) 02894 { 02895 if ( m_doc->m_bReadOnly ) { 02896 e->ignore(); 02897 return; 02898 } 02899 02900 if ( m_imPreeditLength > 0 ) { 02901 m_doc->removeText( cursor.line(), m_imPreeditStart, 02902 cursor.line(), m_imPreeditStart + m_imPreeditLength ); 02903 } 02904 02905 m_doc->setIMSelectionValue( m_imPreeditStartLine, m_imPreeditStart, m_imPreeditStart + e->text().length(), 02906 m_imPreeditStart + e->cursorPos(), m_imPreeditStart + e->cursorPos() + e->selectionLength(), 02907 true ); 02908 02909 m_doc->insertText( cursor.line(), cursor.col(), e->text() ); 02910 02911 updateView( true ); 02912 updateCursor( cursor, true ); 02913 m_imPreeditLength = e->text().length(); 02914 } 02915 02916 void KateViewInternal::imEndEvent( QIMEvent *e ) 02917 { 02918 if ( m_doc->m_bReadOnly ) { 02919 e->ignore(); 02920 return; 02921 } 02922 02923 if ( m_imPreeditLength > 0 ) { 02924 m_doc->removeText( cursor.line(), m_imPreeditStart, 02925 cursor.line(), m_imPreeditStart + m_imPreeditLength ); 02926 } 02927 02928 m_doc->setIMSelectionValue( m_imPreeditStartLine, m_imPreeditStart, 0, 0, 0, false ); 02929 02930 if ( e->text().length() > 0 ) { 02931 m_doc->insertText( cursor.line(), cursor.col(), e->text() ); 02932 02933 if ( !m_cursorTimer.isActive() && KApplication::cursorFlashTime() > 0 ) 02934 m_cursorTimer.start ( KApplication::cursorFlashTime() / 2 ); 02935 02936 updateView( true ); 02937 updateCursor( cursor, true ); 02938 02939 } 02940 02941 m_imPreeditStart = 0; 02942 m_imPreeditLength = 0; 02943 } 02944 02945 // 02946 // END EVENT HANDLING STUFF !!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!! 02947 // 02948 02949 void KateViewInternal::clear() 02950 { 02951 cursor.setPos(0, 0); 02952 displayCursor.setPos(0, 0); 02953 } 02954 02955 void KateViewInternal::wheelEvent(QWheelEvent* e) 02956 { 02957 if (m_lineScroll->minValue() != m_lineScroll->maxValue() && e->orientation() != Qt::Horizontal) { 02958 // React to this as a vertical event 02959 if ( ( e->state() & ControlButton ) || ( e->state() & ShiftButton ) ) { 02960 if (e->delta() > 0) 02961 scrollPrevPage(); 02962 else 02963 scrollNextPage(); 02964 } else { 02965 scrollViewLines(-((e->delta() / 120) * QApplication::wheelScrollLines())); 02966 // maybe a menu was opened or a bubbled window title is on us -> we shall erase it 02967 update(); 02968 leftBorder->update(); 02969 } 02970 02971 } else if (!m_columnScroll->isHidden()) { 02972 QWheelEvent copy = *e; 02973 QApplication::sendEvent(m_columnScroll, &copy); 02974 02975 } else { 02976 e->ignore(); 02977 } 02978 } 02979 02980 void KateViewInternal::startDragScroll() 02981 { 02982 if ( !m_dragScrollTimer.isActive() ) { 02983 m_suppressColumnScrollBar = true; 02984 m_dragScrollTimer.start( scrollTime ); 02985 } 02986 } 02987 02988 void KateViewInternal::stopDragScroll() 02989 { 02990 m_suppressColumnScrollBar = false; 02991 m_dragScrollTimer.stop(); 02992 updateView(); 02993 } 02994 02995 void KateViewInternal::doDragScroll() 02996 { 02997 QPoint p = this->mapFromGlobal( QCursor::pos() ); 02998 02999 int dx = 0, dy = 0; 03000 if ( p.y() < scrollMargin ) { 03001 dy = p.y() - scrollMargin; 03002 } else if ( p.y() > height() - scrollMargin ) { 03003 dy = scrollMargin - (height() - p.y()); 03004 } 03005 if ( p.x() < scrollMargin ) { 03006 dx = p.x() - scrollMargin; 03007 } else if ( p.x() > width() - scrollMargin ) { 03008 dx = scrollMargin - (width() - p.x()); 03009 } 03010 dy /= 4; 03011 03012 if (dy) 03013 scrollLines(startPos().line() + dy); 03014 if (dx) 03015 scrollColumns(m_startX + dx); 03016 if (!dy && !dx) 03017 stopDragScroll(); 03018 } 03019 03020 void KateViewInternal::enableTextHints(int timeout) 03021 { 03022 m_textHintTimeout=timeout; 03023 m_textHintEnabled=true; 03024 m_textHintTimer.start(timeout); 03025 } 03026 03027 void KateViewInternal::disableTextHints() 03028 { 03029 m_textHintEnabled=false; 03030 m_textHintTimer.stop (); 03031 } 03032 03033 // BEGIN EDIT STUFF 03034 void KateViewInternal::editStart() 03035 { 03036 editSessionNumber++; 03037 03038 if (editSessionNumber > 1) 03039 return; 03040 03041 editIsRunning = true; 03042 editOldCursor = cursor; 03043 } 03044 03045 void KateViewInternal::editEnd(int editTagLineStart, int editTagLineEnd, bool tagFrom) 03046 { 03047 if (editSessionNumber == 0) 03048 return; 03049 03050 editSessionNumber--; 03051 03052 if (editSessionNumber > 0) 03053 return; 03054 03055 if (tagFrom && (editTagLineStart <= int(m_doc->getRealLine(startLine())))) 03056 tagAll(); 03057 else 03058 tagLines (editTagLineStart, tagFrom ? m_doc->lastLine() : editTagLineEnd, true); 03059 03060 if (editOldCursor == cursor) 03061 updateBracketMarks(); 03062 03063 if (m_imPreeditLength <= 0) 03064 updateView(true); 03065 03066 if ((editOldCursor != cursor) && (m_imPreeditLength <= 0)) 03067 { 03068 m_madeVisible = false; 03069 updateCursor ( cursor, true ); 03070 } 03071 else if ( m_view->isActive() ) 03072 { 03073 makeVisible(displayCursor, displayCursor.col()); 03074 } 03075 03076 editIsRunning = false; 03077 } 03078 03079 void KateViewInternal::editSetCursor (const KateTextCursor &cursor) 03080 { 03081 if (this->cursor != cursor) 03082 { 03083 this->cursor.setPos (cursor); 03084 } 03085 } 03086 // END 03087 03088 void KateViewInternal::docSelectionChanged () 03089 { 03090 if (!m_doc->hasSelection()) 03091 selectAnchor.setPos (-1, -1); 03092 } 03093 03094 // BEGIN KateScrollBar 03095 KateScrollBar::KateScrollBar (Orientation orientation, KateViewInternal* parent, const char* name) 03096 : QScrollBar (orientation, parent->m_view, name) 03097 , m_middleMouseDown (false) 03098 , m_view(parent->m_view) 03099 , m_doc(parent->m_doc) 03100 , m_viewInternal(parent) 03101 , m_topMargin(-1) 03102 , m_bottomMargin(-1) 03103 , m_savVisibleLines(0) 03104 , m_showMarks(false) 03105 { 03106 connect(this, SIGNAL(valueChanged(int)), SLOT(sliderMaybeMoved(int))); 03107 connect(m_doc, SIGNAL(marksChanged()), this, SLOT(marksChanged())); 03108 03109 m_lines.setAutoDelete(true); 03110 } 03111 03112 void KateScrollBar::mousePressEvent(QMouseEvent* e) 03113 { 03114 if (e->button() == MidButton) 03115 m_middleMouseDown = true; 03116 03117 QScrollBar::mousePressEvent(e); 03118 03119 redrawMarks(); 03120 } 03121 03122 void KateScrollBar::mouseReleaseEvent(QMouseEvent* e) 03123 { 03124 QScrollBar::mouseReleaseEvent(e); 03125 03126 m_middleMouseDown = false; 03127 03128 redrawMarks(); 03129 } 03130 03131 void KateScrollBar::mouseMoveEvent(QMouseEvent* e) 03132 { 03133 QScrollBar::mouseMoveEvent(e); 03134 03135 if (e->state() | LeftButton) 03136 redrawMarks(); 03137 } 03138 03139 void KateScrollBar::paintEvent(QPaintEvent *e) 03140 { 03141 QScrollBar::paintEvent(e); 03142 redrawMarks(); 03143 } 03144 03145 void KateScrollBar::resizeEvent(QResizeEvent *e) 03146 { 03147 QScrollBar::resizeEvent(e); 03148 recomputeMarksPositions(); 03149 } 03150 03151 void KateScrollBar::styleChange(QStyle &s) 03152 { 03153 QScrollBar::styleChange(s); 03154 m_topMargin = -1; 03155 recomputeMarksPositions(); 03156 } 03157 03158 void KateScrollBar::valueChange() 03159 { 03160 QScrollBar::valueChange(); 03161 redrawMarks(); 03162 } 03163 03164 void KateScrollBar::rangeChange() 03165 { 03166 QScrollBar::rangeChange(); 03167 recomputeMarksPositions(); 03168 } 03169 03170 void KateScrollBar::marksChanged() 03171 { 03172 recomputeMarksPositions(true); 03173 } 03174 03175 void KateScrollBar::redrawMarks() 03176 { 03177 if (!m_showMarks) 03178 return; 03179 03180 QPainter painter(this); 03181 QRect rect = sliderRect(); 03182 for (QIntDictIterator<QColor> it(m_lines); it.current(); ++it) 03183 { 03184 if (it.currentKey() < rect.top() || it.currentKey() > rect.bottom()) 03185 { 03186 painter.setPen(*it.current()); 03187 painter.drawLine(0, it.currentKey(), width(), it.currentKey()); 03188 } 03189 } 03190 } 03191 03192 void KateScrollBar::recomputeMarksPositions(bool forceFullUpdate) 03193 { 03194 if (m_topMargin == -1) 03195 watchScrollBarSize(); 03196 03197 m_lines.clear(); 03198 m_savVisibleLines = m_doc->visibleLines(); 03199 03200 int realHeight = frameGeometry().height() - m_topMargin - m_bottomMargin; 03201 03202 QPtrList<KTextEditor::Mark> marks = m_doc->marks(); 03203 KateCodeFoldingTree *tree = m_doc->foldingTree(); 03204 03205 for (KTextEditor::Mark *mark = marks.first(); mark; mark = marks.next()) 03206 { 03207 uint line = mark->line; 03208 03209 if (tree) 03210 { 03211 KateCodeFoldingNode *node = tree->findNodeForLine(line); 03212 03213 while (node) 03214 { 03215 if (!node->visible) 03216 line = tree->getStartLine(node); 03217 node = node->parentNode; 03218 } 03219 } 03220 03221 line = m_doc->getVirtualLine(line); 03222 03223 double d = (double)line / (m_savVisibleLines - 1); 03224 m_lines.insert(m_topMargin + (int)(d * realHeight), 03225 new QColor(KateRendererConfig::global()->lineMarkerColor((KTextEditor::MarkInterface::MarkTypes)mark->type))); 03226 } 03227 03228 if (forceFullUpdate) 03229 update(); 03230 else 03231 redrawMarks(); 03232 } 03233 03234 void KateScrollBar::watchScrollBarSize() 03235 { 03236 int savMax = maxValue(); 03237 setMaxValue(0); 03238 QRect rect = sliderRect(); 03239 setMaxValue(savMax); 03240 03241 m_topMargin = rect.top(); 03242 m_bottomMargin = frameGeometry().height() - rect.bottom(); 03243 } 03244 03245 void KateScrollBar::sliderMaybeMoved(int value) 03246 { 03247 if (m_middleMouseDown) 03248 emit sliderMMBMoved(value); 03249 } 03250 03251 KateTextLine::Ptr KateViewInternal::textLine( int realLine ) 03252 { 03253 if (m_usePlainLines) 03254 return m_doc->plainKateTextLine(realLine); 03255 else 03256 return m_doc->kateTextLine(realLine); 03257 } 03258 03259 // END 03260 03261 // kate: space-indent on; indent-width 2; replace-tabs on;
KDE Logo
This file is part of the documentation for kate Library Version 3.3.0.
Documentation copyright © 1996-2004 the KDE developers.
Generated on Wed Sep 29 09:45:09 2004 by doxygen 1.3.8 written by Dimitri van Heesch, © 1997-2003