kate Library API Documentation

kateviewinternal.cpp

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