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