Kate
kateviewinternal.cpp
Go to the documentation of this file.
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-2007 Hamish Rodda <rodda@kde.org> 00006 Copyright (C) 2003 Anakim Border <aborder@sources.sourceforge.net> 00007 Copyright (C) 2007 Mirko Stocker <me@misto.ch> 00008 Copyright (C) 2007 Matthew Woehlke <mw_triad@users.sourceforge.net> 00009 Copyright (C) 2008 Erlend Hamberg <ehamberg@gmail.com> 00010 00011 Based on: 00012 KWriteView : Copyright (C) 1999 Jochen Wilhelmy <digisnap@cs.tu-berlin.de> 00013 00014 This library is free software; you can redistribute it and/or 00015 modify it under the terms of the GNU Library General Public 00016 License version 2 as published by the Free Software Foundation. 00017 00018 This library is distributed in the hope that it will be useful, 00019 but WITHOUT ANY WARRANTY; without even the implied warranty of 00020 MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU 00021 Library General Public License for more details. 00022 00023 You should have received a copy of the GNU Library General Public License 00024 along with this library; see the file COPYING.LIB. If not, write to 00025 the Free Software Foundation, Inc., 51 Franklin Street, Fifth Floor, 00026 Boston, MA 02110-1301, USA. 00027 */ 00028 00029 #include "kateviewinternal.h" 00030 #include "kateviewinternal.moc" 00031 00032 #include "kateview.h" 00033 #include "katecodefolding.h" 00034 #include "kateviewhelpers.h" 00035 #include "katehighlight.h" 00036 #include "katebuffer.h" 00037 #include "katerenderer.h" 00038 #include "kateconfig.h" 00039 #include "katelayoutcache.h" 00040 #include "katecompletionwidget.h" 00041 #include "kateviinputmodemanager.h" 00042 #include "katevimodebar.h" 00043 #include "katesearchbar.h" 00044 #include "spellcheck/spellingmenu.h" 00045 #include "kateviewaccessible.h" 00046 00047 #include <ktexteditor/movingrange.h> 00048 #include <kcursor.h> 00049 #include <kdebug.h> 00050 #include <kapplication.h> 00051 #include <kglobalsettings.h> 00052 00053 #include <QtCore/QMimeData> 00054 #include <QtGui/QPainter> 00055 #include <QtGui/QLayout> 00056 #include <QtGui/QClipboard> 00057 #include <QtGui/QPixmap> 00058 #include <QtGui/QKeyEvent> 00059 #include <QtCore/QStack> 00060 00061 static const bool debugPainting = false; 00062 00063 KateViewInternal::KateViewInternal(KateView *view) 00064 : QWidget (view) 00065 , editSessionNumber (0) 00066 , editIsRunning (false) 00067 , m_view (view) 00068 , m_cursor(doc()->buffer(), KTextEditor::Cursor(0, 0), Kate::TextCursor::MoveOnInsert) 00069 , m_mouse() 00070 , m_possibleTripleClick (false) 00071 , m_completionItemExpanded (false) 00072 , m_bm(doc()->newMovingRange(KTextEditor::Range::invalid(), KTextEditor::MovingRange::DoNotExpand)) 00073 , m_bmStart(doc()->newMovingRange(KTextEditor::Range::invalid(), KTextEditor::MovingRange::DoNotExpand)) 00074 , m_bmEnd(doc()->newMovingRange(KTextEditor::Range::invalid(), KTextEditor::MovingRange::DoNotExpand)) 00075 , m_dummy (0) 00076 00077 // stay on cursor will avoid that the view scroll around on press return at beginning 00078 , m_startPos (doc()->buffer(), KTextEditor::Cursor (0, 0), Kate::TextCursor::StayOnInsert) 00079 00080 , m_visibleLineCount(0) 00081 , m_madeVisible(false) 00082 , m_shiftKeyPressed (false) 00083 , m_autoCenterLines(0) 00084 , m_minLinesVisible(0) 00085 , m_selChangedByUser (false) 00086 , m_selectAnchor (-1, -1) 00087 , m_selectionMode( Default ) 00088 , m_layoutCache(new KateLayoutCache(renderer(), this)) 00089 , m_preserveX(false) 00090 , m_preservedX(0) 00091 , m_updatingView(true) 00092 , m_cachedMaxStartPos(-1, -1) 00093 , m_dragScrollTimer(this) 00094 , m_scrollTimer (this) 00095 , m_cursorTimer (this) 00096 , m_textHintTimer (this) 00097 , m_textHintEnabled(false) 00098 , m_textHintMouseX(-1) 00099 , m_textHintMouseY(-1) 00100 , m_imPreeditRange(0) 00101 , m_smartDirty(false) 00102 , m_viInputMode(false) 00103 , m_viInputModeManager (0) 00104 { 00105 setMinimumSize (0,0); 00106 setAttribute(Qt::WA_OpaquePaintEvent); 00107 setAttribute(Qt::WA_InputMethodEnabled); 00108 00109 // invalidate m_selectionCached.start(), or keyb selection is screwed initially 00110 m_selectionCached = KTextEditor::Range::invalid(); 00111 00112 // bracket markers are only for this view and should not be printed 00113 m_bm->setView (m_view); 00114 m_bmStart->setView (m_view); 00115 m_bmEnd->setView (m_view); 00116 m_bm->setAttributeOnlyForViews (true); 00117 m_bmStart->setAttributeOnlyForViews (true); 00118 m_bmEnd->setAttributeOnlyForViews (true); 00119 00120 // use z depth defined in moving ranges interface 00121 m_bm->setZDepth (-1000.0); 00122 m_bmStart->setZDepth (-1000.0); 00123 m_bmEnd->setZDepth (-1000.0); 00124 00125 // update mark attributes 00126 updateBracketMarkAttributes(); 00127 00128 // 00129 // scrollbar for lines 00130 // 00131 m_lineScroll = new KateScrollBar(Qt::Vertical, this); 00132 m_lineScroll->show(); 00133 m_lineScroll->setTracking (true); 00134 m_lineScroll->setSizePolicy( QSizePolicy::Fixed, QSizePolicy::Expanding ); 00135 00136 // bottom corner box 00137 m_dummy = new QWidget(m_view); 00138 m_dummy->setFixedSize(m_lineScroll->width(), m_lineScroll->width()); 00139 m_dummy->setSizePolicy( QSizePolicy::Fixed, QSizePolicy::Fixed ); 00140 00141 if (m_view->dynWordWrap()) 00142 m_dummy->hide(); 00143 else 00144 m_dummy->show(); 00145 00146 cache()->setWrap(m_view->dynWordWrap()); 00147 00148 // Hijack the line scroller's controls, so we can scroll nicely for word-wrap 00149 connect(m_lineScroll, SIGNAL(actionTriggered(int)), SLOT(scrollAction(int))); 00150 connect(m_lineScroll, SIGNAL(sliderMoved(int)), SLOT(scrollLines(int))); 00151 connect(m_lineScroll, SIGNAL(sliderMMBMoved(int)), SLOT(scrollLines(int))); 00152 connect(m_lineScroll, SIGNAL(valueChanged(int)), SLOT(scrollLines(int))); 00153 00154 // catch wheel events, completing the hijack 00155 //m_lineScroll->installEventFilter(this); 00156 00157 // 00158 // scrollbar for columns 00159 // 00160 m_columnScroll = new QScrollBar(Qt::Horizontal,m_view); 00161 00162 if (m_view->dynWordWrap()) 00163 m_columnScroll->hide(); 00164 else 00165 m_columnScroll->show(); 00166 00167 m_columnScroll->setTracking(true); 00168 m_startX = 0; 00169 00170 connect(m_columnScroll, SIGNAL(valueChanged(int)), SLOT(scrollColumns(int))); 00171 00172 // 00173 // iconborder ;) 00174 // 00175 m_leftBorder = new KateIconBorder( this, m_view ); 00176 m_leftBorder->show (); 00177 00178 connect( m_leftBorder, SIGNAL(toggleRegionVisibility(unsigned int)), 00179 doc()->foldingTree(), SLOT(toggleRegionVisibility(unsigned int))); 00180 00181 connect( doc()->foldingTree(), SIGNAL(regionVisibilityChangedAt(unsigned int,bool)), 00182 this, SLOT(slotRegionVisibilityChangedAt(unsigned int,bool))); 00183 connect( doc(), SIGNAL(codeFoldingUpdated()), 00184 this, SLOT(slotCodeFoldingChanged()) ); 00185 00186 m_displayCursor.setPosition(0, 0); 00187 00188 setAcceptDrops( true ); 00189 00190 // event filter 00191 installEventFilter(this); 00192 00193 // set initial cursor 00194 m_mouseCursor = Qt::IBeamCursor; 00195 setCursor(m_mouseCursor); 00196 00197 // call mouseMoveEvent also if no mouse button is pressed 00198 setMouseTracking(true); 00199 00200 m_dragInfo.state = diNone; 00201 00202 // timers 00203 connect( &m_dragScrollTimer, SIGNAL( timeout() ), 00204 this, SLOT( doDragScroll() ) ); 00205 00206 connect( &m_scrollTimer, SIGNAL( timeout() ), 00207 this, SLOT( scrollTimeout() ) ); 00208 00209 connect( &m_cursorTimer, SIGNAL( timeout() ), 00210 this, SLOT( cursorTimeout() ) ); 00211 00212 connect( &m_textHintTimer, SIGNAL( timeout() ), 00213 this, SLOT( textHintTimeout() ) ); 00214 00215 // selection changed to set anchor 00216 connect( m_view, SIGNAL( selectionChanged(KTextEditor::View*) ), 00217 this, SLOT( viewSelectionChanged() ) ); 00218 00219 QAccessible::installFactory(accessibleInterfaceFactory); 00220 00221 // update is called in KateView, after construction and layout is over 00222 // but before any other kateviewinternal call 00223 } 00224 00225 KateViewInternal::~KateViewInternal () 00226 { 00227 QAccessible::removeFactory(accessibleInterfaceFactory); 00228 00229 // kill preedit ranges 00230 delete m_imPreeditRange; 00231 qDeleteAll (m_imPreeditRangeChildren); 00232 00233 delete m_viInputModeManager; 00234 00235 // delete bracket markers 00236 delete m_bm; 00237 delete m_bmStart; 00238 delete m_bmEnd; 00239 } 00240 00241 void KateViewInternal::prepareForDynWrapChange() 00242 { 00243 // Which is the current view line? 00244 m_wrapChangeViewLine = cache()->displayViewLine(m_displayCursor, true); 00245 } 00246 00247 void KateViewInternal::dynWrapChanged() 00248 { 00249 if (m_view->dynWordWrap()) 00250 { 00251 m_columnScroll->hide(); 00252 m_dummy->hide(); 00253 00254 } 00255 else 00256 { 00257 // column scrollbar + bottom corner box 00258 m_columnScroll->show(); 00259 m_dummy->show(); 00260 } 00261 00262 cache()->setWrap(m_view->dynWordWrap()); 00263 updateView(); 00264 00265 if (m_view->dynWordWrap()) 00266 scrollColumns(0); 00267 00268 // Determine where the cursor should be to get the cursor on the same view line 00269 if (m_wrapChangeViewLine != -1) { 00270 KTextEditor::Cursor newStart = viewLineOffset(m_displayCursor, -m_wrapChangeViewLine); 00271 makeVisible(newStart, newStart.column(), true); 00272 00273 } else { 00274 update(); 00275 } 00276 } 00277 00278 KTextEditor::Cursor KateViewInternal::endPos() const 00279 { 00280 // Hrm, no lines laid out at all?? 00281 if (!cache()->viewCacheLineCount()) 00282 return KTextEditor::Cursor(); 00283 00284 for (int i = qMin(linesDisplayed() - 1, cache()->viewCacheLineCount() - 1); i >= 0; i--) { 00285 const KateTextLayout& thisLine = cache()->viewLine(i); 00286 00287 if (thisLine.line() == -1) continue; 00288 00289 if (thisLine.virtualLine() >= doc()->numVisLines()) { 00290 // Cache is too out of date 00291 return KTextEditor::Cursor(doc()->numVisLines() - 1, doc()->lineLength(doc()->getRealLine(doc()->numVisLines() - 1))); 00292 } 00293 00294 return KTextEditor::Cursor(thisLine.virtualLine(), thisLine.wrap() ? thisLine.endCol() - 1 : thisLine.endCol()); 00295 } 00296 00297 kDebug(13030) << "WARNING: could not find a lineRange at all"; 00298 return KTextEditor::Cursor(-1, -1); 00299 } 00300 00301 int KateViewInternal::endLine() const 00302 { 00303 return endPos().line(); 00304 } 00305 00306 KateTextLayout KateViewInternal::yToKateTextLayout(int y) const 00307 { 00308 if (y < 0 || y > size().height()) 00309 return KateTextLayout::invalid(); 00310 00311 int range = y / renderer()->lineHeight(); 00312 00313 // lineRanges is always bigger than 0, after the initial updateView call 00314 if (range >= 0 && range < cache()->viewCacheLineCount()) 00315 return cache()->viewLine(range); 00316 00317 return KateTextLayout::invalid(); 00318 } 00319 00320 int KateViewInternal::lineToY(int viewLine) const 00321 { 00322 return (viewLine-startLine()) * renderer()->lineHeight(); 00323 } 00324 00325 void KateViewInternal::slotIncFontSizes() 00326 { 00327 renderer()->increaseFontSizes(); 00328 } 00329 00330 void KateViewInternal::slotDecFontSizes() 00331 { 00332 renderer()->decreaseFontSizes(); 00333 } 00334 00338 void KateViewInternal::scrollLines ( int line ) 00339 { 00340 KTextEditor::Cursor newPos(line, 0); 00341 scrollPos(newPos); 00342 } 00343 00344 // This can scroll less than one true line 00345 void KateViewInternal::scrollViewLines(int offset) 00346 { 00347 KTextEditor::Cursor c = viewLineOffset(startPos(), offset); 00348 scrollPos(c); 00349 00350 bool blocked = m_lineScroll->blockSignals(true); 00351 m_lineScroll->setValue(startLine()); 00352 m_lineScroll->blockSignals(blocked); 00353 } 00354 00355 void KateViewInternal::scrollAction( int action ) 00356 { 00357 switch (action) { 00358 case QAbstractSlider::SliderSingleStepAdd: 00359 scrollNextLine(); 00360 break; 00361 00362 case QAbstractSlider::SliderSingleStepSub: 00363 scrollPrevLine(); 00364 break; 00365 00366 case QAbstractSlider::SliderPageStepAdd: 00367 scrollNextPage(); 00368 break; 00369 00370 case QAbstractSlider::SliderPageStepSub: 00371 scrollPrevPage(); 00372 break; 00373 00374 case QAbstractSlider::SliderToMinimum: 00375 top_home(); 00376 break; 00377 00378 case QAbstractSlider::SliderToMaximum: 00379 bottom_end(); 00380 break; 00381 } 00382 } 00383 00384 void KateViewInternal::scrollNextPage() 00385 { 00386 scrollViewLines(qMax( linesDisplayed() - 1, 0 )); 00387 } 00388 00389 void KateViewInternal::scrollPrevPage() 00390 { 00391 scrollViewLines(-qMax( linesDisplayed() - 1, 0 )); 00392 } 00393 00394 void KateViewInternal::scrollPrevLine() 00395 { 00396 scrollViewLines(-1); 00397 } 00398 00399 void KateViewInternal::scrollNextLine() 00400 { 00401 scrollViewLines(1); 00402 } 00403 00404 KTextEditor::Cursor KateViewInternal::maxStartPos(bool changed) 00405 { 00406 cache()->setAcceptDirtyLayouts(true); 00407 00408 if (m_cachedMaxStartPos.line() == -1 || changed) 00409 { 00410 KTextEditor::Cursor end(doc()->numVisLines() - 1, doc()->lineLength(doc()->getRealLine(doc()->numVisLines() - 1))); 00411 00412 if (m_view->config()->scrollPastEnd()) 00413 m_cachedMaxStartPos = viewLineOffset(end, -m_minLinesVisible); 00414 else 00415 m_cachedMaxStartPos = viewLineOffset(end, -(linesDisplayed() - 1)); 00416 } 00417 00418 cache()->setAcceptDirtyLayouts(false); 00419 00420 return m_cachedMaxStartPos; 00421 } 00422 00423 // c is a virtual cursor 00424 void KateViewInternal::scrollPos(KTextEditor::Cursor& c, bool force, bool calledExternally) 00425 { 00426 if (!force && ((!m_view->dynWordWrap() && c.line() == startLine()) || c == startPos())) 00427 return; 00428 00429 if (c.line() < 0) 00430 c.setLine(0); 00431 00432 KTextEditor::Cursor limit = maxStartPos(); 00433 if (c > limit) { 00434 c = limit; 00435 00436 // Re-check we're not just scrolling to the same place 00437 if (!force && ((!m_view->dynWordWrap() && c.line() == startLine()) || c == startPos())) 00438 return; 00439 } 00440 00441 int viewLinesScrolled = 0; 00442 00443 // only calculate if this is really used and useful, could be wrong here, please recheck 00444 // for larger scrolls this makes 2-4 seconds difference on my xeon with dyn. word wrap on 00445 // try to get it really working ;) 00446 bool viewLinesScrolledUsable = !force 00447 && (c.line() >= startLine() - linesDisplayed() - 1) 00448 && (c.line() <= endLine() + linesDisplayed() + 1); 00449 00450 if (viewLinesScrolledUsable) { 00451 viewLinesScrolled = cache()->displayViewLine(c); 00452 } 00453 00454 m_startPos.setPosition(c); 00455 00456 // set false here but reversed if we return to makeVisible 00457 m_madeVisible = false; 00458 00459 if (viewLinesScrolledUsable) 00460 { 00461 int lines = linesDisplayed(); 00462 if (doc()->numVisLines() < lines) { 00463 KTextEditor::Cursor end(doc()->numVisLines() - 1, doc()->lineLength(doc()->getRealLine(doc()->numVisLines() - 1))); 00464 lines = qMin(linesDisplayed(), cache()->displayViewLine(end) + 1); 00465 } 00466 00467 Q_ASSERT(lines >= 0); 00468 00469 if (!calledExternally && qAbs(viewLinesScrolled) < lines) 00470 { 00471 updateView(false, viewLinesScrolled); 00472 00473 int scrollHeight = -(viewLinesScrolled * (int)renderer()->lineHeight()); 00474 00475 scroll(0, scrollHeight); 00476 m_leftBorder->scroll(0, scrollHeight); 00477 00478 emit m_view->verticalScrollPositionChanged( m_view, c ); 00479 emit m_view->displayRangeChanged(m_view); 00480 return; 00481 } 00482 } 00483 00484 updateView(); 00485 update(); 00486 m_leftBorder->update(); 00487 emit m_view->verticalScrollPositionChanged( m_view, c ); 00488 emit m_view->displayRangeChanged(m_view); 00489 } 00490 00491 void KateViewInternal::scrollColumns ( int x ) 00492 { 00493 if (x == m_startX) 00494 return; 00495 00496 if (x < 0) 00497 x = 0; 00498 00499 int dx = m_startX - x; 00500 m_startX = x; 00501 00502 if (qAbs(dx) < width()) 00503 scroll(dx, 0); 00504 else 00505 update(); 00506 00507 emit m_view->horizontalScrollPositionChanged( m_view ); 00508 emit m_view->displayRangeChanged(m_view); 00509 00510 bool blocked = m_columnScroll->blockSignals(true); 00511 m_columnScroll->setValue(m_startX); 00512 m_columnScroll->blockSignals(blocked); 00513 } 00514 00515 // If changed is true, the lines that have been set dirty have been updated. 00516 void KateViewInternal::updateView(bool changed, int viewLinesScrolled) 00517 { 00518 doUpdateView(changed, viewLinesScrolled); 00519 00520 if (changed) 00521 updateDirty(); 00522 } 00523 00524 void KateViewInternal::doUpdateView(bool changed, int viewLinesScrolled) 00525 { 00526 if(!isVisible() && !viewLinesScrolled ) 00527 return; //When this view is not visible, don't do anything 00528 00529 m_updatingView = true; 00530 00531 bool blocked = m_lineScroll->blockSignals(true); 00532 00533 if (width() != cache()->viewWidth()) { 00534 cache()->setViewWidth(width()); 00535 changed = true; 00536 } 00537 00538 /* It was observed that height() could be negative here -- 00539 when the main Kate view has 0 as size (during creation), 00540 and there frame arount KateViewInternal. In which 00541 case we'd set the view cache to 0 (or less!) lines, and 00542 start allocating huge chunks of data, later. */ 00543 int newSize = (qMax (0, height()) / renderer()->lineHeight()) + 1; 00544 cache()->updateViewCache(startPos(), newSize, viewLinesScrolled); 00545 m_visibleLineCount = newSize; 00546 00547 KTextEditor::Cursor maxStart = maxStartPos(changed); 00548 int maxLineScrollRange = maxStart.line(); 00549 if (m_view->dynWordWrap() && maxStart.column() != 0) 00550 maxLineScrollRange++; 00551 m_lineScroll->setRange(0, maxLineScrollRange); 00552 00553 m_lineScroll->setValue(startPos().line()); 00554 m_lineScroll->setSingleStep(1); 00555 m_lineScroll->setPageStep(qMax (0, height()) / renderer()->lineHeight()); 00556 m_lineScroll->blockSignals(blocked); 00557 00558 if (!m_view->dynWordWrap()) 00559 { 00560 int max = maxLen(startLine()) - width(); 00561 if (max < 0) 00562 max = 0; 00563 00564 // if we lose the ability to scroll horizontally, move view to the far-left 00565 if (max == 0) 00566 { 00567 scrollColumns(0); 00568 } 00569 00570 blocked = m_columnScroll->blockSignals(true); 00571 00572 // disable scrollbar 00573 m_columnScroll->setDisabled (max == 0); 00574 00575 m_columnScroll->setRange(0, max); 00576 00577 m_columnScroll->setValue(m_startX); 00578 00579 // Approximate linescroll 00580 m_columnScroll->setSingleStep(renderer()->config()->fontMetrics().width('a')); 00581 m_columnScroll->setPageStep(width()); 00582 00583 m_columnScroll->blockSignals(blocked); 00584 } 00585 00586 if (m_smartDirty) 00587 m_smartDirty = false; 00588 00589 m_updatingView = false; 00590 } 00591 00596 void KateViewInternal::makeVisible (const KTextEditor::Cursor& c, int endCol, bool force, bool center, bool calledExternally) 00597 { 00598 //kDebug(13030) << "MakeVisible start " << startPos() << " end " << endPos() << " -> request: " << c;// , new start [" << scroll.line << "," << scroll.col << "] lines " << (linesDisplayed() - 1) << " height " << height(); 00599 // if the line is in a folded region, unfold all the way up 00600 //if ( doc()->foldingTree()->findNodeForLine( c.line )->visible ) 00601 // kDebug(13030)<<"line ("<<c.line<<") should be visible"; 00602 00603 if ( force ) 00604 { 00605 KTextEditor::Cursor scroll = c; 00606 scrollPos(scroll, force, calledExternally); 00607 } 00608 else if (center && (c < startPos() || c > endPos())) 00609 { 00610 KTextEditor::Cursor scroll = viewLineOffset(c, -int(linesDisplayed()) / 2); 00611 scrollPos(scroll, false, calledExternally); 00612 } 00613 else if ( c > viewLineOffset(startPos(), linesDisplayed() - m_minLinesVisible - 1) ) 00614 { 00615 KTextEditor::Cursor scroll = viewLineOffset(c, -(linesDisplayed() - m_minLinesVisible - 1)); 00616 scrollPos(scroll, false, calledExternally); 00617 } 00618 else if ( c < viewLineOffset(startPos(), m_minLinesVisible) ) 00619 { 00620 KTextEditor::Cursor scroll = viewLineOffset(c, -m_minLinesVisible); 00621 scrollPos(scroll, false, calledExternally); 00622 } 00623 else 00624 { 00625 // Check to see that we're not showing blank lines 00626 KTextEditor::Cursor max = maxStartPos(); 00627 if (startPos() > max) { 00628 scrollPos(max, max.column(), calledExternally); 00629 } 00630 } 00631 00632 if (!m_view->dynWordWrap() && (endCol != -1 || m_view->wrapCursor())) 00633 { 00634 KTextEditor::Cursor rc = toRealCursor(c); 00635 int sX = renderer()->cursorToX(cache()->textLayout(rc), rc, !m_view->wrapCursor()); 00636 00637 int sXborder = sX-8; 00638 if (sXborder < 0) 00639 sXborder = 0; 00640 00641 if (sX < m_startX) 00642 scrollColumns (sXborder); 00643 else if (sX > m_startX + width()) 00644 scrollColumns (sX - width() + 8); 00645 } 00646 00647 m_madeVisible = !force; 00648 00649 QAccessible::updateAccessibility( this, KateCursorAccessible::ChildId, QAccessible::Focus ); 00650 } 00651 00652 void KateViewInternal::slotRegionVisibilityChangedAt(unsigned int,bool clear_cache) 00653 { 00654 Q_UNUSED(clear_cache) 00655 kDebug(13030); 00656 cache()->clear(); 00657 00658 m_cachedMaxStartPos.setLine(-1); 00659 KTextEditor::Cursor max = maxStartPos(); 00660 if (startPos() > max) 00661 scrollPos(max); 00662 00663 m_preserveX = true; 00664 KTextEditor::Cursor newPos = toRealCursor(toVirtualCursor(m_cursor)); 00665 KateTextLayout newLine = cache()->textLayout(newPos); 00666 newPos = renderer()->xToCursor(newLine, m_preservedX, !m_view->wrapCursor()); 00667 updateCursor(newPos, true); 00668 00669 updateView(); 00670 update(); 00671 m_leftBorder->update(); 00672 emit m_view->displayRangeChanged(m_view); 00673 } 00674 00675 void KateViewInternal::slotCodeFoldingChanged() 00676 { 00677 m_leftBorder->update(); 00678 } 00679 00680 void KateViewInternal::slotRegionBeginEndAddedRemoved(unsigned int) 00681 { 00682 kDebug(13030); 00683 // FIXME: performance problem 00684 m_leftBorder->update(); 00685 } 00686 00687 void KateViewInternal::showEvent ( QShowEvent *e ) 00688 { 00689 updateView (); 00690 00691 QWidget::showEvent (e); 00692 } 00693 00694 int KateViewInternal::linesDisplayed() const 00695 { 00696 int h = height(); 00697 00698 // catch zero heights, even if should not happen 00699 int fh = qMax (1, renderer()->lineHeight()); 00700 00701 // default to 1, there is always one line around.... 00702 // too many places calc with linesDisplayed() - 1 00703 return qMax (1, (h - (h % fh)) / fh); 00704 } 00705 00706 QPoint KateViewInternal::cursorToCoordinate( const KTextEditor::Cursor & cursor, bool realCursor, bool includeBorder ) const 00707 { 00708 int viewLine = cache()->displayViewLine(realCursor ? toVirtualCursor(cursor) : cursor, true); 00709 00710 if (viewLine < 0 || viewLine >= cache()->viewCacheLineCount()) 00711 return QPoint(-1, -1); 00712 00713 int y = (int)viewLine * renderer()->lineHeight(); 00714 00715 KateTextLayout layout = cache()->viewLine(viewLine); 00716 int x = 0; 00717 00718 // only set x value if we have a valid layout (bug #171027) 00719 if (layout.isValid()) 00720 x = (int)layout.lineLayout().cursorToX(cursor.column()); 00721 // else 00722 // kDebug() << "Invalid Layout"; 00723 00724 if (includeBorder) x += m_leftBorder->width(); 00725 00726 x -= startX(); 00727 00728 return QPoint(x, y); 00729 } 00730 00731 QPoint KateViewInternal::cursorCoordinates(bool includeBorder) const 00732 { 00733 return cursorToCoordinate(m_displayCursor, false, includeBorder); 00734 } 00735 00736 KTextEditor::Cursor KateViewInternal::findMatchingBracket() 00737 { 00738 KTextEditor::Cursor c; 00739 00740 if (!m_bm->toRange().isValid()) 00741 return KTextEditor::Cursor::invalid(); 00742 00743 Q_ASSERT(m_bmEnd->toRange().isValid()); 00744 Q_ASSERT(m_bmStart->toRange().isValid()); 00745 00746 if (m_bmStart->toRange().contains(m_cursor) || m_bmStart->end() == m_cursor.toCursor()) { 00747 c = m_bmEnd->end(); 00748 } else if (m_bmEnd->toRange().contains(m_cursor) || m_bmEnd->end() == m_cursor.toCursor()) { 00749 c = m_bmStart->start(); 00750 } else { 00751 // should never happen: a range exists, but the cursor position is 00752 // neither at the start nor at the end... 00753 return KTextEditor::Cursor::invalid(); 00754 } 00755 00756 return c; 00757 } 00758 00759 void KateViewInternal::doReturn() 00760 { 00761 doc()->newLine( m_view ); 00762 updateView(); 00763 } 00764 00765 void KateViewInternal::doSmartNewline() 00766 { 00767 int ln = m_cursor.line(); 00768 Kate::TextLine line = doc()->kateTextLine(ln); 00769 int col = qMin(m_cursor.column(), line->firstChar()); 00770 if (col != -1) { 00771 while (line->length() > col && 00772 !(line->at(col).isLetterOrNumber() || line->at(col) == QLatin1Char('_')) && 00773 col < m_cursor.column()) ++col; 00774 } else { 00775 col = line->length(); // stay indented 00776 } 00777 doc()->editStart(); 00778 doc()->editWrapLine(ln, m_cursor.column()); 00779 doc()->insertText(KTextEditor::Cursor(ln + 1, 0), line->string(0, col)); 00780 doc()->editEnd(); 00781 00782 updateView(); 00783 } 00784 00785 void KateViewInternal::doDelete() 00786 { 00787 doc()->del( m_view, m_cursor ); 00788 } 00789 00790 void KateViewInternal::doBackspace() 00791 { 00792 doc()->backspace( m_view, m_cursor ); 00793 } 00794 00795 void KateViewInternal::doTabulator() 00796 { 00797 doc()->insertTab( m_view, m_cursor ); 00798 } 00799 00800 void KateViewInternal::doTranspose() 00801 { 00802 doc()->transpose( m_cursor ); 00803 } 00804 00805 void KateViewInternal::doDeleteWordLeft() 00806 { 00807 doc()->editStart(); 00808 wordLeft( true ); 00809 KTextEditor::Range selection = m_view->selectionRange(); 00810 m_view->removeSelectedText(); 00811 doc()->editEnd(); 00812 tagRange(selection, true); 00813 updateDirty(); 00814 } 00815 00816 void KateViewInternal::doDeleteWordRight() 00817 { 00818 doc()->editStart(); 00819 wordRight( true ); 00820 KTextEditor::Range selection = m_view->selectionRange(); 00821 m_view->removeSelectedText(); 00822 doc()->editEnd(); 00823 tagRange(selection, true); 00824 updateDirty(); 00825 } 00826 00827 class CalculatingCursor : public KTextEditor::Cursor { 00828 public: 00829 // These constructors constrain their arguments to valid positions 00830 // before only the third one did, but that leads to crashs 00831 // see bug 227449 00832 CalculatingCursor(KateViewInternal* vi) 00833 : KTextEditor::Cursor() 00834 , m_vi(vi) 00835 { 00836 makeValid(); 00837 } 00838 00839 CalculatingCursor(KateViewInternal* vi, const KTextEditor::Cursor& c) 00840 : KTextEditor::Cursor(c) 00841 , m_vi(vi) 00842 { 00843 makeValid(); 00844 } 00845 00846 CalculatingCursor(KateViewInternal* vi, int line, int col) 00847 : KTextEditor::Cursor(line, col) 00848 , m_vi(vi) 00849 { 00850 makeValid(); 00851 } 00852 00853 00854 virtual CalculatingCursor& operator+=( int n ) = 0; 00855 00856 virtual CalculatingCursor& operator-=( int n ) = 0; 00857 00858 CalculatingCursor& operator++() { return operator+=( 1 ); } 00859 00860 CalculatingCursor& operator--() { return operator-=( 1 ); } 00861 00862 void makeValid() { 00863 setLine(qBound( 0, line(), int( doc()->lines() - 1 ) ) ); 00864 if (view()->wrapCursor()) 00865 m_column = qBound( 0, column(), doc()->lineLength( line() ) ); 00866 else 00867 m_column = qMax( 0, column() ); 00868 Q_ASSERT( valid() ); 00869 } 00870 00871 void toEdge( KateViewInternal::Bias bias ) { 00872 if( bias == KateViewInternal::left ) m_column = 0; 00873 else if( bias == KateViewInternal::right ) m_column = doc()->lineLength( line() ); 00874 } 00875 00876 bool atEdge() const { return atEdge( KateViewInternal::left ) || atEdge( KateViewInternal::right ); } 00877 00878 bool atEdge( KateViewInternal::Bias bias ) const { 00879 switch( bias ) { 00880 case KateViewInternal::left: return column() == 0; 00881 case KateViewInternal::none: return atEdge(); 00882 case KateViewInternal::right: return column() >= doc()->lineLength( line() ); 00883 default: Q_ASSERT(false); return false; 00884 } 00885 } 00886 00887 protected: 00888 bool valid() const { 00889 return line() >= 0 && 00890 line() < doc()->lines() && 00891 column() >= 0 && 00892 (!view()->wrapCursor() || column() <= doc()->lineLength( line() )); 00893 } 00894 KateView* view() { return m_vi->m_view; } 00895 const KateView* view() const { return m_vi->m_view; } 00896 KateDocument* doc() { return view()->doc(); } 00897 const KateDocument* doc() const { return view()->doc(); } 00898 KateViewInternal* m_vi; 00899 }; 00900 00901 class BoundedCursor : public CalculatingCursor { 00902 public: 00903 BoundedCursor(KateViewInternal* vi) 00904 : CalculatingCursor( vi ) {} 00905 BoundedCursor(KateViewInternal* vi, const KTextEditor::Cursor& c ) 00906 : CalculatingCursor( vi, c ) {} 00907 BoundedCursor(KateViewInternal* vi, int line, int col ) 00908 : CalculatingCursor( vi, line, col ) {} 00909 virtual CalculatingCursor& operator+=( int n ) { 00910 KateLineLayoutPtr thisLine = m_vi->cache()->line(line()); 00911 if (!thisLine->isValid()) { 00912 kWarning() << "Did not retrieve valid layout for line " << line(); 00913 return *this; 00914 } 00915 00916 const bool wrapCursor = view()->wrapCursor(); 00917 int maxColumn = -1; 00918 if (n >= 0) { 00919 for (int i = 0; i < n; i++) { 00920 if (m_column >= thisLine->length()) { 00921 if (wrapCursor) { 00922 break; 00923 00924 } else if (view()->dynWordWrap()) { 00925 // Don't go past the edge of the screen in dynamic wrapping mode 00926 if (maxColumn == -1) 00927 maxColumn = thisLine->length() + ((m_vi->width() - thisLine->widthOfLastLine()) / m_vi->renderer()->spaceWidth()) - 1; 00928 00929 if (m_column >= maxColumn) { 00930 m_column = maxColumn; 00931 break; 00932 } 00933 00934 ++m_column; 00935 00936 } else { 00937 ++m_column; 00938 } 00939 00940 } else { 00941 m_column = thisLine->layout()->nextCursorPosition(m_column); 00942 } 00943 } 00944 } else { 00945 for (int i = 0; i > n; i--) { 00946 if (m_column >= thisLine->length()) 00947 --m_column; 00948 else if (m_column == 0) 00949 break; 00950 else 00951 m_column = thisLine->layout()->previousCursorPosition(m_column); 00952 } 00953 } 00954 00955 Q_ASSERT( valid() ); 00956 return *this; 00957 } 00958 virtual CalculatingCursor& operator-=( int n ) { 00959 return operator+=( -n ); 00960 } 00961 }; 00962 00963 class WrappingCursor : public CalculatingCursor { 00964 public: 00965 WrappingCursor(KateViewInternal* vi) 00966 : CalculatingCursor( vi) {} 00967 WrappingCursor(KateViewInternal* vi, const KTextEditor::Cursor& c ) 00968 : CalculatingCursor( vi, c ) {} 00969 WrappingCursor(KateViewInternal* vi, int line, int col ) 00970 : CalculatingCursor( vi, line, col ) {} 00971 00972 virtual CalculatingCursor& operator+=( int n ) { 00973 KateLineLayoutPtr thisLine = m_vi->cache()->line(line()); 00974 if (!thisLine->isValid()) { 00975 kWarning() << "Did not retrieve a valid layout for line " << line(); 00976 return *this; 00977 } 00978 00979 if (n >= 0) { 00980 for (int i = 0; i < n; i++) { 00981 if (m_column >= thisLine->length()) { 00982 // Have come to the end of a line 00983 if (line() >= doc()->lines() - 1) 00984 // Have come to the end of the document 00985 break; 00986 00987 // Advance to the beginning of the next line 00988 m_column = 0; 00989 setLine(line() + 1); 00990 00991 // Retrieve the next text range 00992 thisLine = m_vi->cache()->line(line()); 00993 if (!thisLine->isValid()) { 00994 kWarning() << "Did not retrieve a valid layout for line " << line(); 00995 return *this; 00996 } 00997 00998 continue; 00999 } 01000 01001 m_column = thisLine->layout()->nextCursorPosition(m_column); 01002 } 01003 01004 } else { 01005 for (int i = 0; i > n; i--) { 01006 if (m_column == 0) { 01007 // Have come to the start of the document 01008 if (line() == 0) 01009 break; 01010 01011 // Start going back to the end of the last line 01012 setLine(line() - 1); 01013 01014 // Retrieve the next text range 01015 thisLine = m_vi->cache()->line(line()); 01016 if (!thisLine->isValid()) { 01017 kWarning() << "Did not retrieve a valid layout for line " << line(); 01018 return *this; 01019 } 01020 01021 // Finish going back to the end of the last line 01022 m_column = thisLine->length(); 01023 01024 continue; 01025 } 01026 01027 m_column = thisLine->layout()->previousCursorPosition(m_column); 01028 } 01029 } 01030 01031 Q_ASSERT(valid()); 01032 return *this; 01033 } 01034 virtual CalculatingCursor& operator-=( int n ) { 01035 return operator+=( -n ); 01036 } 01037 }; 01038 01039 void KateViewInternal::moveChar( KateViewInternal::Bias bias, bool sel ) 01040 { 01041 KTextEditor::Cursor c; 01042 if ( m_view->wrapCursor() ) { 01043 c = WrappingCursor( this, m_cursor ) += bias; 01044 } else { 01045 c = BoundedCursor( this, m_cursor ) += bias; 01046 } 01047 01048 updateSelection( c, sel ); 01049 updateCursor( c ); 01050 } 01051 01052 void KateViewInternal::cursorLeft( bool sel ) 01053 { 01054 if ( ! m_view->wrapCursor() && m_cursor.column() == 0 ) 01055 return; 01056 01057 moveChar( KateViewInternal::left, sel ); 01058 } 01059 01060 void KateViewInternal::cursorRight( bool sel ) 01061 { 01062 moveChar( KateViewInternal::right, sel ); 01063 } 01064 01065 void KateViewInternal::wordLeft ( bool sel ) 01066 { 01067 WrappingCursor c( this, m_cursor ); 01068 01069 // First we skip backwards all space. 01070 // Then we look up into which category the current position falls: 01071 // 1. a "word" character 01072 // 2. a "non-word" character (except space) 01073 // 3. the beginning of the line 01074 // and skip all preceding characters that fall into this class. 01075 // The code assumes that space is never part of the word character class. 01076 01077 KateHighlighting* h = doc()->highlight(); 01078 if( !c.atEdge( left ) ) { 01079 01080 while( !c.atEdge( left ) && doc()->line( c.line() )[ c.column() - 1 ].isSpace() ) 01081 --c; 01082 } 01083 if( c.atEdge( left ) ) 01084 { 01085 --c; 01086 } 01087 else if( h->isInWord( doc()->line( c.line() )[ c.column() - 1 ] ) ) 01088 { 01089 while( !c.atEdge( left ) && h->isInWord( doc()->line( c.line() )[ c.column() - 1 ] ) ) 01090 --c; 01091 } 01092 else 01093 { 01094 while( !c.atEdge( left ) 01095 && !h->isInWord( doc()->line( c.line() )[ c.column() - 1 ] ) 01096 // in order to stay symmetric to wordLeft() 01097 // we must not skip space preceding a non-word sequence 01098 && !doc()->line( c.line() )[ c.column() - 1 ].isSpace() ) 01099 { 01100 --c; 01101 } 01102 } 01103 01104 updateSelection( c, sel ); 01105 updateCursor( c ); 01106 } 01107 01108 void KateViewInternal::wordRight( bool sel ) 01109 { 01110 WrappingCursor c( this, m_cursor ); 01111 01112 // We look up into which category the current position falls: 01113 // 1. a "word" character 01114 // 2. a "non-word" character (except space) 01115 // 3. the end of the line 01116 // and skip all following characters that fall into this class. 01117 // If the skipped characters are followed by space, we skip that too. 01118 // The code assumes that space is never part of the word character class. 01119 01120 KateHighlighting* h = doc()->highlight(); 01121 if( c.atEdge( right ) ) 01122 { 01123 ++c; 01124 } 01125 else if( h->isInWord( doc()->line( c.line() )[ c.column() ] ) ) 01126 { 01127 while( !c.atEdge( right ) && h->isInWord( doc()->line( c.line() )[ c.column() ] ) ) 01128 ++c; 01129 } 01130 else 01131 { 01132 while( !c.atEdge( right ) 01133 && !h->isInWord( doc()->line( c.line() )[ c.column() ] ) 01134 // we must not skip space, because if that space is followed 01135 // by more non-word characters, we would skip them, too 01136 && !doc()->line( c.line() )[ c.column() ].isSpace() ) 01137 { 01138 ++c; 01139 } 01140 } 01141 01142 while( !c.atEdge( right ) && doc()->line( c.line() )[ c.column() ].isSpace() ) 01143 ++c; 01144 01145 updateSelection( c, sel ); 01146 updateCursor( c ); 01147 } 01148 01149 void KateViewInternal::moveEdge( KateViewInternal::Bias bias, bool sel ) 01150 { 01151 BoundedCursor c( this, m_cursor ); 01152 c.toEdge( bias ); 01153 updateSelection( c, sel ); 01154 updateCursor( c ); 01155 } 01156 01157 void KateViewInternal::home( bool sel ) 01158 { 01159 if (m_view->dynWordWrap() && currentLayout().startCol()) { 01160 // Allow us to go to the real start if we're already at the start of the view line 01161 if (m_cursor.column() != currentLayout().startCol()) { 01162 KTextEditor::Cursor c = currentLayout().start(); 01163 updateSelection( c, sel ); 01164 updateCursor( c ); 01165 return; 01166 } 01167 } 01168 01169 if( !doc()->config()->smartHome() ) { 01170 moveEdge( left, sel ); 01171 return; 01172 } 01173 01174 Kate::TextLine l = doc()->kateTextLine( m_cursor.line() ); 01175 01176 if (!l) 01177 return; 01178 01179 KTextEditor::Cursor c = m_cursor; 01180 int lc = l->firstChar(); 01181 01182 if( lc < 0 || c.column() == lc ) { 01183 c.setColumn(0); 01184 } else { 01185 c.setColumn(lc); 01186 } 01187 01188 updateSelection( c, sel ); 01189 updateCursor( c, true ); 01190 } 01191 01192 void KateViewInternal::end( bool sel ) 01193 { 01194 KateTextLayout layout = currentLayout(); 01195 01196 if (m_view->dynWordWrap() && layout.wrap()) { 01197 // Allow us to go to the real end if we're already at the end of the view line 01198 if (m_cursor.column() < layout.endCol() - 1) { 01199 KTextEditor::Cursor c(m_cursor.line(), layout.endCol() - 1); 01200 updateSelection( c, sel ); 01201 updateCursor( c ); 01202 return; 01203 } 01204 } 01205 01206 if( !doc()->config()->smartHome() ) { 01207 moveEdge( right, sel ); 01208 return; 01209 } 01210 01211 Kate::TextLine l = doc()->kateTextLine( m_cursor.line() ); 01212 01213 if (!l) 01214 return; 01215 01216 // "Smart End", as requested in bugs #78258 and #106970 01217 if (m_cursor.column() == doc()->lineLength(m_cursor.line())) { 01218 KTextEditor::Cursor c = m_cursor; 01219 c.setColumn(l->lastChar() + 1); 01220 updateSelection(c, sel); 01221 updateCursor(c, true); 01222 } else { 01223 moveEdge(right, sel); 01224 } 01225 } 01226 01227 KateTextLayout KateViewInternal::currentLayout() const 01228 { 01229 return cache()->textLayout(m_cursor); 01230 } 01231 01232 KateTextLayout KateViewInternal::previousLayout() const 01233 { 01234 int currentViewLine = cache()->viewLine(m_cursor); 01235 01236 if (currentViewLine) 01237 return cache()->textLayout(m_cursor.line(), currentViewLine - 1); 01238 else 01239 return cache()->textLayout(doc()->getRealLine(m_displayCursor.line() - 1), -1); 01240 } 01241 01242 KateTextLayout KateViewInternal::nextLayout() const 01243 { 01244 int currentViewLine = cache()->viewLine(m_cursor) + 1; 01245 01246 if (currentViewLine >= cache()->line(m_cursor.line())->viewLineCount()) { 01247 currentViewLine = 0; 01248 return cache()->textLayout(doc()->getRealLine(m_displayCursor.line() + 1), currentViewLine); 01249 } else { 01250 return cache()->textLayout(m_cursor.line(), currentViewLine); 01251 } 01252 } 01253 01254 /* 01255 * This returns the cursor which is offset by (offset) view lines. 01256 * This is the main function which is called by code not specifically dealing with word-wrap. 01257 * The opposite conversion (cursor to offset) can be done with cache()->displayViewLine(). 01258 * 01259 * The cursors involved are virtual cursors (ie. equivalent to m_displayCursor) 01260 */ 01261 KTextEditor::Cursor KateViewInternal::viewLineOffset(const KTextEditor::Cursor& virtualCursor, int offset, bool keepX) 01262 { 01263 if (!m_view->dynWordWrap()) { 01264 KTextEditor::Cursor ret(qMin((int)doc()->visibleLines() - 1, virtualCursor.line() + offset), 0); 01265 01266 if (ret.line() < 0) 01267 ret.setLine(0); 01268 01269 if (keepX) { 01270 int realLine = doc()->getRealLine(ret.line()); 01271 KateTextLayout t = cache()->textLayout(realLine, 0); 01272 Q_ASSERT(t.isValid()); 01273 01274 ret.setColumn(renderer()->xToCursor(t, m_preservedX, !m_view->wrapCursor()).column()); 01275 } 01276 01277 return ret; 01278 } 01279 01280 KTextEditor::Cursor realCursor = virtualCursor; 01281 realCursor.setLine(doc()->getRealLine(virtualCursor.line())); 01282 01283 int cursorViewLine = cache()->viewLine(realCursor); 01284 01285 int currentOffset = 0; 01286 int virtualLine = 0; 01287 01288 bool forwards = (offset > 0) ? true : false; 01289 01290 if (forwards) { 01291 currentOffset = cache()->lastViewLine(realCursor.line()) - cursorViewLine; 01292 if (offset <= currentOffset) { 01293 // the answer is on the same line 01294 KateTextLayout thisLine = cache()->textLayout(realCursor.line(), cursorViewLine + offset); 01295 Q_ASSERT(thisLine.virtualLine() == virtualCursor.line()); 01296 return KTextEditor::Cursor(virtualCursor.line(), thisLine.startCol()); 01297 } 01298 01299 virtualLine = virtualCursor.line() + 1; 01300 01301 } else { 01302 offset = -offset; 01303 currentOffset = cursorViewLine; 01304 if (offset <= currentOffset) { 01305 // the answer is on the same line 01306 KateTextLayout thisLine = cache()->textLayout(realCursor.line(), cursorViewLine - offset); 01307 Q_ASSERT(thisLine.virtualLine() == virtualCursor.line()); 01308 return KTextEditor::Cursor(virtualCursor.line(), thisLine.startCol()); 01309 } 01310 01311 virtualLine = virtualCursor.line() - 1; 01312 } 01313 01314 currentOffset++; 01315 01316 while (virtualLine >= 0 && virtualLine < (int)doc()->visibleLines()) 01317 { 01318 int realLine = doc()->getRealLine(virtualLine); 01319 KateLineLayoutPtr thisLine = cache()->line(realLine, virtualLine); 01320 if (!thisLine) 01321 break; 01322 01323 for (int i = 0; i < thisLine->viewLineCount(); ++i) { 01324 if (offset == currentOffset) { 01325 KateTextLayout thisViewLine = thisLine->viewLine(i); 01326 01327 if (!forwards) { 01328 // We actually want it the other way around 01329 int requiredViewLine = cache()->lastViewLine(realLine) - thisViewLine.viewLine(); 01330 if (requiredViewLine != thisViewLine.viewLine()) { 01331 thisViewLine = thisLine->viewLine(requiredViewLine); 01332 } 01333 } 01334 01335 KTextEditor::Cursor ret(virtualLine, thisViewLine.startCol()); 01336 01337 // keep column position 01338 if (keepX) { 01339 KTextEditor::Cursor realCursor = toRealCursor(virtualCursor); 01340 KateTextLayout t = cache()->textLayout(realCursor); 01341 // renderer()->cursorToX(t, realCursor, !m_view->wrapCursor()); 01342 01343 realCursor = renderer()->xToCursor(thisViewLine, m_preservedX, !m_view->wrapCursor()); 01344 ret.setColumn(realCursor.column()); 01345 } 01346 01347 return ret; 01348 } 01349 01350 currentOffset++; 01351 } 01352 01353 if (forwards) 01354 virtualLine++; 01355 else 01356 virtualLine--; 01357 } 01358 01359 // Looks like we were asked for something a bit exotic. 01360 // Return the max/min valid position. 01361 if (forwards) 01362 return KTextEditor::Cursor(doc()->visibleLines() - 1, doc()->lineLength(doc()->getRealLine (doc()->visibleLines() - 1))); 01363 else 01364 return KTextEditor::Cursor(0, 0); 01365 } 01366 01367 int KateViewInternal::lineMaxCursorX(const KateTextLayout& range) 01368 { 01369 if (!m_view->wrapCursor() && !range.wrap()) 01370 return INT_MAX; 01371 01372 int maxX = range.endX(); 01373 01374 if (maxX && range.wrap()) { 01375 QChar lastCharInLine = doc()->kateTextLine(range.line())->at(range.endCol() - 1); 01376 maxX -= renderer()->config()->fontMetrics().width(lastCharInLine); 01377 } 01378 01379 return maxX; 01380 } 01381 01382 int KateViewInternal::lineMaxCol(const KateTextLayout& range) 01383 { 01384 int maxCol = range.endCol(); 01385 01386 if (maxCol && range.wrap()) 01387 maxCol--; 01388 01389 return maxCol; 01390 } 01391 01392 void KateViewInternal::cursorUp(bool sel) 01393 { 01394 if(!sel && m_view->completionWidget()->isCompletionActive()) { 01395 m_view->completionWidget()->cursorUp(); 01396 return; 01397 } 01398 01399 if (m_displayCursor.line() == 0 && (!m_view->dynWordWrap() || cache()->viewLine(m_cursor) == 0)) 01400 return; 01401 01402 m_preserveX = true; 01403 01404 KateTextLayout thisLine = currentLayout(); 01405 // This is not the first line because that is already simplified out above 01406 KateTextLayout pRange = previousLayout(); 01407 01408 // Ensure we're in the right spot 01409 Q_ASSERT(m_cursor.line() == thisLine.line()); 01410 Q_ASSERT(m_cursor.column() >= thisLine.startCol()); 01411 Q_ASSERT(!thisLine.wrap() || m_cursor.column() < thisLine.endCol()); 01412 01413 KTextEditor::Cursor c = renderer()->xToCursor(pRange, m_preservedX, !m_view->wrapCursor()); 01414 01415 updateSelection( c, sel ); 01416 updateCursor( c ); 01417 } 01418 01419 void KateViewInternal::cursorDown(bool sel) 01420 { 01421 if(!sel && m_view->completionWidget()->isCompletionActive()) { 01422 m_view->completionWidget()->cursorDown(); 01423 return; 01424 } 01425 01426 if ((m_displayCursor.line() >= doc()->numVisLines() - 1) && (!m_view->dynWordWrap() || cache()->viewLine(m_cursor) == cache()->lastViewLine(m_cursor.line()))) 01427 return; 01428 01429 m_preserveX = true; 01430 01431 KateTextLayout thisLine = currentLayout(); 01432 // This is not the last line because that is already simplified out above 01433 KateTextLayout nRange = nextLayout(); 01434 01435 // Ensure we're in the right spot 01436 Q_ASSERT((m_cursor.line() == thisLine.line()) && 01437 (m_cursor.column() >= thisLine.startCol()) && 01438 (!thisLine.wrap() || m_cursor.column() < thisLine.endCol())); 01439 01440 KTextEditor::Cursor c = renderer()->xToCursor(nRange, m_preservedX, !m_view->wrapCursor()); 01441 01442 updateSelection(c, sel); 01443 updateCursor(c); 01444 } 01445 01446 void KateViewInternal::cursorToMatchingBracket( bool sel ) 01447 { 01448 KTextEditor::Cursor c = findMatchingBracket(); 01449 01450 if (c.isValid()) { 01451 updateSelection( c, sel ); 01452 updateCursor( c ); 01453 } 01454 } 01455 01456 void KateViewInternal::topOfView( bool sel ) 01457 { 01458 KTextEditor::Cursor c = viewLineOffset(startPos(), m_minLinesVisible); 01459 updateSelection( toRealCursor(c), sel ); 01460 updateCursor( toRealCursor(c) ); 01461 } 01462 01463 void KateViewInternal::bottomOfView( bool sel ) 01464 { 01465 KTextEditor::Cursor c = viewLineOffset(endPos(), -m_minLinesVisible); 01466 updateSelection( toRealCursor(c), sel ); 01467 updateCursor( toRealCursor(c) ); 01468 } 01469 01470 // lines is the offset to scroll by 01471 void KateViewInternal::scrollLines( int lines, bool sel ) 01472 { 01473 KTextEditor::Cursor c = viewLineOffset(m_displayCursor, lines, true); 01474 01475 // Fix the virtual cursor -> real cursor 01476 c.setLine(doc()->getRealLine(c.line())); 01477 01478 updateSelection( c, sel ); 01479 updateCursor( c ); 01480 } 01481 01482 // This is a bit misleading... it's asking for the view to be scrolled, not the cursor 01483 void KateViewInternal::scrollUp() 01484 { 01485 KTextEditor::Cursor newPos = viewLineOffset(startPos(), -1); 01486 scrollPos(newPos); 01487 } 01488 01489 void KateViewInternal::scrollDown() 01490 { 01491 KTextEditor::Cursor newPos = viewLineOffset(startPos(), 1); 01492 scrollPos(newPos); 01493 } 01494 01495 void KateViewInternal::setAutoCenterLines(int viewLines, bool updateView) 01496 { 01497 m_autoCenterLines = viewLines; 01498 m_minLinesVisible = qMin(int((linesDisplayed() - 1)/2), m_autoCenterLines); 01499 if (updateView) 01500 KateViewInternal::updateView(); 01501 } 01502 01503 void KateViewInternal::pageUp( bool sel ) 01504 { 01505 if (m_view->isCompletionActive()) { 01506 m_view->completionWidget()->pageUp(); 01507 return; 01508 } 01509 01510 // remember the view line and x pos 01511 int viewLine = cache()->displayViewLine(m_displayCursor); 01512 bool atTop = startPos().atStartOfDocument(); 01513 01514 // Adjust for an auto-centering cursor 01515 int lineadj = m_minLinesVisible; 01516 01517 int linesToScroll = -qMax( (linesDisplayed() - 1) - lineadj, 0 ); 01518 m_preserveX = true; 01519 01520 if (!doc()->pageUpDownMovesCursor () && !atTop) { 01521 KTextEditor::Cursor newStartPos = viewLineOffset(startPos(), linesToScroll - 1); 01522 scrollPos(newStartPos); 01523 01524 // put the cursor back approximately where it was 01525 KTextEditor::Cursor newPos = toRealCursor(viewLineOffset(newStartPos, viewLine, true)); 01526 01527 KateTextLayout newLine = cache()->textLayout(newPos); 01528 01529 newPos = renderer()->xToCursor(newLine, m_preservedX, !m_view->wrapCursor()); 01530 01531 m_preserveX = true; 01532 updateSelection( newPos, sel ); 01533 updateCursor(newPos); 01534 01535 } else { 01536 scrollLines( linesToScroll, sel ); 01537 } 01538 } 01539 01540 void KateViewInternal::pageDown( bool sel ) 01541 { 01542 if (m_view->isCompletionActive()) { 01543 m_view->completionWidget()->pageDown(); 01544 return; 01545 } 01546 01547 // remember the view line 01548 int viewLine = cache()->displayViewLine(m_displayCursor); 01549 bool atEnd = startPos() >= m_cachedMaxStartPos; 01550 01551 // Adjust for an auto-centering cursor 01552 int lineadj = m_minLinesVisible; 01553 01554 int linesToScroll = qMax( (linesDisplayed() - 1) - lineadj, 0 ); 01555 m_preserveX = true; 01556 01557 if (!doc()->pageUpDownMovesCursor () && !atEnd) { 01558 KTextEditor::Cursor newStartPos = viewLineOffset(startPos(), linesToScroll + 1); 01559 scrollPos(newStartPos); 01560 01561 // put the cursor back approximately where it was 01562 KTextEditor::Cursor newPos = toRealCursor(viewLineOffset(newStartPos, viewLine, true)); 01563 01564 KateTextLayout newLine = cache()->textLayout(newPos); 01565 01566 newPos = renderer()->xToCursor(newLine, m_preservedX, !m_view->wrapCursor()); 01567 01568 m_preserveX = true; 01569 updateSelection( newPos, sel ); 01570 updateCursor(newPos); 01571 01572 } else { 01573 scrollLines( linesToScroll, sel ); 01574 } 01575 } 01576 01577 int KateViewInternal::maxLen(int startLine) 01578 { 01579 Q_ASSERT(!m_view->dynWordWrap()); 01580 01581 int displayLines = (m_view->height() / renderer()->lineHeight()) + 1; 01582 01583 int maxLen = 0; 01584 01585 for (int z = 0; z < displayLines; z++) { 01586 int virtualLine = startLine + z; 01587 01588 if (virtualLine < 0 || virtualLine >= (int)doc()->visibleLines()) 01589 break; 01590 01591 maxLen = qMax(maxLen, cache()->line(doc()->getRealLine(virtualLine))->width()); 01592 } 01593 01594 return maxLen; 01595 } 01596 01597 bool KateViewInternal::columnScrollingPossible () 01598 { 01599 return !m_view->dynWordWrap() && m_columnScroll->isEnabled() && (m_columnScroll->maximum() > 0); 01600 } 01601 01602 void KateViewInternal::top( bool sel ) 01603 { 01604 KTextEditor::Cursor newCursor(0, 0); 01605 01606 newCursor = renderer()->xToCursor(cache()->textLayout(newCursor), m_preservedX, !m_view->wrapCursor()); 01607 01608 updateSelection( newCursor, sel ); 01609 updateCursor( newCursor ); 01610 } 01611 01612 void KateViewInternal::bottom( bool sel ) 01613 { 01614 KTextEditor::Cursor newCursor(doc()->lastLine(), 0); 01615 01616 newCursor = renderer()->xToCursor(cache()->textLayout(newCursor), m_preservedX, !m_view->wrapCursor()); 01617 01618 updateSelection( newCursor, sel ); 01619 updateCursor( newCursor ); 01620 } 01621 01622 void KateViewInternal::top_home( bool sel ) 01623 { 01624 if (m_view->isCompletionActive()) { 01625 m_view->completionWidget()->top(); 01626 return; 01627 } 01628 01629 KTextEditor::Cursor c( 0, 0 ); 01630 updateSelection( c, sel ); 01631 updateCursor( c ); 01632 } 01633 01634 void KateViewInternal::bottom_end( bool sel ) 01635 { 01636 if (m_view->isCompletionActive()) { 01637 m_view->completionWidget()->bottom(); 01638 return; 01639 } 01640 01641 KTextEditor::Cursor c( doc()->lastLine(), doc()->lineLength( doc()->lastLine() ) ); 01642 updateSelection( c, sel ); 01643 updateCursor( c ); 01644 } 01645 01646 void KateViewInternal::updateSelection( const KTextEditor::Cursor& _newCursor, bool keepSel ) 01647 { 01648 KTextEditor::Cursor newCursor = _newCursor; 01649 if( keepSel ) 01650 { 01651 if ( !m_view->selection() || (m_selectAnchor.line() == -1) 01652 //don't kill the selection if we have a persistent selection and 01653 //the cursor is inside or at the boundaries of the selected area 01654 || (m_view->config()->persistentSelection() 01655 && !(m_view->selectionRange().contains(m_cursor) 01656 || m_view->selectionRange().boundaryAtCursor(m_cursor))) ) 01657 { 01658 m_selectAnchor = m_cursor; 01659 setSelection( KTextEditor::Range(m_cursor, newCursor) ); 01660 } 01661 else 01662 { 01663 bool doSelect = true; 01664 switch (m_selectionMode) 01665 { 01666 case Word: 01667 { 01668 // Restore selStartCached if needed. It gets nuked by 01669 // viewSelectionChanged if we drag the selection into non-existence, 01670 // which can legitimately happen if a shift+DC selection is unable to 01671 // set a "proper" (i.e. non-empty) cached selection, e.g. because the 01672 // start was on something that isn't a word. Word select mode relies 01673 // on the cached selection being set properly, even if it is empty 01674 // (i.e. selStartCached == selEndCached). 01675 if ( !m_selectionCached.isValid() ) 01676 m_selectionCached.start() = m_selectionCached.end(); 01677 01678 int c; 01679 if ( newCursor > m_selectionCached.start() ) 01680 { 01681 m_selectAnchor = m_selectionCached.start(); 01682 01683 Kate::TextLine l = doc()->kateTextLine( newCursor.line() ); 01684 01685 c = newCursor.column(); 01686 if ( c > 0 && doc()->highlight()->isInWord( l->at( c-1 ) ) ) { 01687 for ( ; c < l->length(); c++ ) 01688 if ( !doc()->highlight()->isInWord( l->at( c ) ) ) 01689 break; 01690 } 01691 01692 newCursor.setColumn( c ); 01693 } 01694 else if ( newCursor < m_selectionCached.start() ) 01695 { 01696 m_selectAnchor = m_selectionCached.end(); 01697 01698 Kate::TextLine l = doc()->kateTextLine( newCursor.line() ); 01699 01700 c = newCursor.column(); 01701 if ( c > 0 && c < doc()->lineLength( newCursor.line() ) 01702 && doc()->highlight()->isInWord( l->at( c ) ) 01703 && doc()->highlight()->isInWord( l->at( c-1 ) ) ) { 01704 for ( c -= 2; c >= 0; c-- ) 01705 if ( !doc()->highlight()->isInWord( l->at( c ) ) ) 01706 break; 01707 newCursor.setColumn( c+1 ); 01708 } 01709 } 01710 else 01711 doSelect = false; 01712 01713 } 01714 break; 01715 case Line: 01716 if ( !m_selectionCached.isValid() ) { 01717 m_selectionCached = KTextEditor::Range(endLine(), 0, endLine(), 0); 01718 } 01719 if ( newCursor.line() > m_selectionCached.start().line() ) 01720 { 01721 if (newCursor.line() + 1 >= doc()->lines() ) 01722 newCursor.setColumn( doc()->line( newCursor.line() ).length() ); 01723 else 01724 newCursor.setPosition( newCursor.line() + 1, 0 ); 01725 // Grow to include the entire line 01726 m_selectAnchor = m_selectionCached.start(); 01727 m_selectAnchor.setColumn( 0 ); 01728 } 01729 else if ( newCursor.line() < m_selectionCached.start().line() ) 01730 { 01731 newCursor.setColumn( 0 ); 01732 // Grow to include entire line 01733 m_selectAnchor = m_selectionCached.end(); 01734 if ( m_selectAnchor.column() > 0 ) 01735 { 01736 if ( m_selectAnchor.line()+1 >= doc()->lines() ) 01737 m_selectAnchor.setColumn( doc()->line( newCursor.line() ).length() ); 01738 else 01739 m_selectAnchor.setPosition( m_selectAnchor.line() + 1, 0 ); 01740 } 01741 } 01742 else // same line, ignore 01743 doSelect = false; 01744 break; 01745 case Mouse: 01746 { 01747 if ( !m_selectionCached.isValid() ) 01748 break; 01749 01750 if ( newCursor > m_selectionCached.end() ) 01751 m_selectAnchor = m_selectionCached.start(); 01752 else if ( newCursor < m_selectionCached.start() ) 01753 m_selectAnchor = m_selectionCached.end(); 01754 else 01755 doSelect = false; 01756 } 01757 break; 01758 default: /* nothing special to do */; 01759 } 01760 01761 if ( doSelect ) 01762 setSelection( KTextEditor::Range(m_selectAnchor, newCursor) ); 01763 else if ( m_selectionCached.isValid() ) // we have a cached selection, so we restore that 01764 setSelection( m_selectionCached ); 01765 } 01766 01767 m_selChangedByUser = true; 01768 } 01769 else if ( !m_view->config()->persistentSelection() ) 01770 { 01771 m_view->clearSelection(); 01772 01773 m_selectionCached = KTextEditor::Range::invalid(); 01774 } 01775 } 01776 01777 void KateViewInternal::setSelection( const KTextEditor::Range &range ) 01778 { 01779 disconnect(m_view, SIGNAL(selectionChanged(KTextEditor::View*)), this, SLOT(viewSelectionChanged())); 01780 m_view->setSelection(range); 01781 connect(m_view, SIGNAL(selectionChanged(KTextEditor::View*)), this, SLOT(viewSelectionChanged())); 01782 } 01783 01784 void KateViewInternal::moveCursorToSelectionEdge() 01785 { 01786 if (!m_view->selection()) 01787 return; 01788 01789 int tmp = m_minLinesVisible; 01790 m_minLinesVisible = 0; 01791 01792 if ( m_view->selectionRange().start() < m_selectAnchor ) 01793 updateCursor( m_view->selectionRange().start() ); 01794 else 01795 updateCursor( m_view->selectionRange().end() ); 01796 01797 m_minLinesVisible = tmp; 01798 } 01799 01800 void KateViewInternal::updateCursor( const KTextEditor::Cursor& newCursor, bool force, bool center, bool calledExternally ) 01801 { 01802 if ( !force && (m_cursor.toCursor() == newCursor) ) 01803 { 01804 m_displayCursor = toVirtualCursor(newCursor); 01805 if ( !m_madeVisible && m_view == doc()->activeView() ) 01806 { 01807 // unfold if required 01808 doc()->foldingTree()->ensureVisible( newCursor.line() ); 01809 01810 makeVisible ( m_displayCursor, m_displayCursor.column(), false, center, calledExternally ); 01811 } 01812 01813 return; 01814 } 01815 01816 // unfold if required 01817 doc()->foldingTree()->ensureVisible( newCursor.line() ); 01818 01819 KTextEditor::Cursor oldDisplayCursor = m_displayCursor; 01820 01821 m_displayCursor = toVirtualCursor(newCursor); 01822 m_cursor.setPosition( newCursor ); 01823 01824 if ( m_view == doc()->activeView() ) 01825 makeVisible ( m_displayCursor, m_displayCursor.column(), false, center, calledExternally ); 01826 01827 updateBracketMarks(); 01828 01829 // It's efficient enough to just tag them both without checking to see if they're on the same view line 01830 /* kdDebug()<<"oldDisplayCursor:"<<oldDisplayCursor<<endl; 01831 kdDebug()<<"m_displayCursor:"<<m_displayCursor<<endl;*/ 01832 tagLine(oldDisplayCursor); 01833 tagLine(m_displayCursor); 01834 01835 updateMicroFocus(); 01836 01837 if (m_cursorTimer.isActive ()) 01838 { 01839 if ( KApplication::cursorFlashTime() > 0 ) 01840 m_cursorTimer.start( KApplication::cursorFlashTime() / 2 ); 01841 renderer()->setDrawCaret(true); 01842 } 01843 01844 // Remember the maximum X position if requested 01845 if (m_preserveX) 01846 m_preserveX = false; 01847 else 01848 m_preservedX = renderer()->cursorToX(cache()->textLayout(m_cursor), m_cursor, !m_view->wrapCursor()); 01849 01850 //kDebug(13030) << "m_preservedX: " << m_preservedX << " (was "<< oldmaxx << "), m_cursorX: " << m_cursorX; 01851 //kDebug(13030) << "Cursor now located at real " << cursor.line << "," << cursor.col << ", virtual " << m_displayCursor.line << ", " << m_displayCursor.col << "; Top is " << startLine() << ", " << startPos().col; 01852 01853 cursorMoved(); 01854 01855 updateDirty(); //paintText(0, 0, width(), height(), true); 01856 01857 emit m_view->cursorPositionChanged(m_view, m_cursor); 01858 } 01859 01860 void KateViewInternal::updateBracketMarkAttributes() 01861 { 01862 KTextEditor::Attribute::Ptr bracketFill = KTextEditor::Attribute::Ptr(new KTextEditor::Attribute()); 01863 bracketFill->setBackground(m_view->m_renderer->config()->highlightedBracketColor()); 01864 bracketFill->setBackgroundFillWhitespace(false); 01865 if (QFontInfo(renderer()->currentFont()).fixedPitch()) { 01866 // make font bold only for fixed fonts, otherwise text jumps around 01867 bracketFill->setFontBold(); 01868 } 01869 01870 m_bmStart->setAttribute(bracketFill); 01871 m_bmEnd->setAttribute(bracketFill); 01872 01873 if (m_view->m_renderer->config()->showWholeBracketExpression()) { 01874 KTextEditor::Attribute::Ptr expressionFill = KTextEditor::Attribute::Ptr(new KTextEditor::Attribute()); 01875 expressionFill->setBackground(m_view->m_renderer->config()->highlightedBracketColor()); 01876 expressionFill->setBackgroundFillWhitespace(false); 01877 01878 m_bm->setAttribute(expressionFill); 01879 } else { 01880 m_bm->setAttribute(KTextEditor::Attribute::Ptr(new KTextEditor::Attribute())); 01881 } 01882 } 01883 01884 void KateViewInternal::updateBracketMarks() 01885 { 01886 // add some limit to this, this is really endless on big files without limit 01887 int maxLines = 5000; 01888 KTextEditor::Range newRange; 01889 doc()->newBracketMark( m_cursor, newRange, maxLines ); 01890 01891 // new range valid, then set ranges to it 01892 if (newRange.isValid ()) { 01893 // modify full range 01894 m_bm->setRange (newRange); 01895 01896 // modify start and end ranges 01897 m_bmStart->setRange (KTextEditor::Range (m_bm->start(), KTextEditor::Cursor (m_bm->start().line(), m_bm->start().column() + 1))); 01898 m_bmEnd->setRange (KTextEditor::Range (m_bm->end(), KTextEditor::Cursor (m_bm->end().line(), m_bm->end().column() + 1))); 01899 return; 01900 } 01901 01902 // new range was invalid 01903 m_bm->setRange (KTextEditor::Range::invalid()); 01904 m_bmStart->setRange (KTextEditor::Range::invalid()); 01905 m_bmEnd->setRange (KTextEditor::Range::invalid()); 01906 } 01907 01908 bool KateViewInternal::tagLine(const KTextEditor::Cursor& virtualCursor) 01909 { 01910 // FIXME may be a more efficient way for this 01911 if ((int)doc()->getRealLine(virtualCursor.line()) > doc()->lastLine()) 01912 return false; 01913 // End FIXME 01914 01915 int viewLine = cache()->displayViewLine(virtualCursor, true); 01916 if (viewLine >= 0 && viewLine < cache()->viewCacheLineCount()) { 01917 cache()->viewLine(viewLine).setDirty(); 01918 m_leftBorder->update (0, lineToY(viewLine), m_leftBorder->width(), renderer()->lineHeight()); 01919 return true; 01920 } 01921 return false; 01922 } 01923 01924 bool KateViewInternal::tagLines( int start, int end, bool realLines ) 01925 { 01926 return tagLines(KTextEditor::Cursor(start, 0), KTextEditor::Cursor(end, -1), realLines); 01927 } 01928 01929 bool KateViewInternal::tagLines(KTextEditor::Cursor start, KTextEditor::Cursor end, bool realCursors) 01930 { 01931 if (realCursors) 01932 { 01933 cache()->relayoutLines(start.line(), end.line()); 01934 01935 //kDebug(13030)<<"realLines is true"; 01936 start = toVirtualCursor(start); 01937 end = toVirtualCursor(end); 01938 01939 } else { 01940 cache()->relayoutLines(toRealCursor(start).line(), toRealCursor(end).line()); 01941 } 01942 01943 if (end.line() < startLine()) 01944 { 01945 //kDebug(13030)<<"end<startLine"; 01946 return false; 01947 } 01948 // Used to be > endLine(), but cache may not be valid when checking, so use a 01949 // less optimal but still adequate approximation (potential overestimation but minimal performance difference) 01950 if (start.line() > startLine() + cache()->viewCacheLineCount()) 01951 { 01952 //kDebug(13030)<<"start> endLine"<<start<<" "<<(endLine()); 01953 return false; 01954 } 01955 01956 cache()->updateViewCache(startPos()); 01957 01958 //kDebug(13030) << "tagLines( [" << start << "], [" << end << "] )"; 01959 01960 bool ret = false; 01961 01962 for (int z = 0; z < cache()->viewCacheLineCount(); z++) 01963 { 01964 KateTextLayout& line = cache()->viewLine(z); 01965 if ((line.virtualLine() > start.line() || (line.virtualLine() == start.line() && line.endCol() >= start.column() && start.column() != -1)) && 01966 (line.virtualLine() < end.line() || (line.virtualLine() == end.line() && (line.startCol() <= end.column() || end.column() == -1)))) { 01967 ret = true; 01968 break; 01969 //kDebug(13030) << "Tagged line " << line.line(); 01970 } 01971 } 01972 01973 if (!m_view->dynWordWrap()) 01974 { 01975 int y = lineToY( start.line() ); 01976 // FIXME is this enough for when multiple lines are deleted 01977 int h = (end.line() - start.line() + 2) * renderer()->lineHeight(); 01978 if (end.line() >= doc()->numVisLines() - 1) 01979 h = height(); 01980 01981 m_leftBorder->update (0, y, m_leftBorder->width(), h); 01982 } 01983 else 01984 { 01985 // FIXME Do we get enough good info in editRemoveText to optimize this more? 01986 //bool justTagged = false; 01987 for (int z = 0; z < cache()->viewCacheLineCount(); z++) 01988 { 01989 KateTextLayout& line = cache()->viewLine(z); 01990 if (!line.isValid() || 01991 ((line.virtualLine() > start.line() || (line.virtualLine() == start.line() && line.endCol() >= start.column() && start.column() != -1)) && 01992 (line.virtualLine() < end.line() || (line.virtualLine() == end.line() && (line.startCol() <= end.column() || end.column() == -1))))) 01993 { 01994 //justTagged = true; 01995 m_leftBorder->update (0, z * renderer()->lineHeight(), m_leftBorder->width(), m_leftBorder->height()); 01996 break; 01997 } 01998 /*else if (justTagged) 01999 { 02000 justTagged = false; 02001 leftBorder->update (0, z * doc()->viewFont.fontHeight, leftBorder->width(), doc()->viewFont.fontHeight); 02002 break; 02003 }*/ 02004 } 02005 } 02006 02007 return ret; 02008 } 02009 02010 bool KateViewInternal::tagRange(const KTextEditor::Range& range, bool realCursors) 02011 { 02012 return tagLines(range.start(), range.end(), realCursors); 02013 } 02014 02015 void KateViewInternal::tagAll() 02016 { 02017 // clear the cache... 02018 cache()->clear (); 02019 02020 m_leftBorder->updateFont(); 02021 m_leftBorder->update(); 02022 } 02023 02024 void KateViewInternal::paintCursor() 02025 { 02026 if (tagLine(m_displayCursor)) 02027 updateDirty(); //paintText (0,0,width(), height(), true); 02028 } 02029 02030 // Point in content coordinates 02031 void KateViewInternal::placeCursor( const QPoint& p, bool keepSelection, bool updateSelection ) 02032 { 02033 KateTextLayout thisLine = yToKateTextLayout(p.y()); 02034 KTextEditor::Cursor c; 02035 02036 if (!thisLine.isValid()) // probably user clicked below the last line -> use the last line 02037 thisLine = cache()->textLayout(doc()->lines() - 1, -1); 02038 02039 c = renderer()->xToCursor(thisLine, startX() + p.x(), !m_view->wrapCursor()); 02040 02041 if (c.line () < 0 || c.line() >= doc()->lines()) { 02042 return; 02043 } 02044 02045 if (updateSelection) 02046 KateViewInternal::updateSelection( c, keepSelection ); 02047 02048 int tmp = m_minLinesVisible; 02049 m_minLinesVisible = 0; 02050 updateCursor( c ); 02051 m_minLinesVisible = tmp; 02052 02053 if (updateSelection && keepSelection) 02054 moveCursorToSelectionEdge(); 02055 } 02056 02057 // Point in content coordinates 02058 bool KateViewInternal::isTargetSelected( const QPoint& p ) 02059 { 02060 const KateTextLayout& thisLine = yToKateTextLayout(p.y()); 02061 if (!thisLine.isValid()) 02062 return false; 02063 02064 return m_view->cursorSelected(renderer()->xToCursor(thisLine, startX() + p.x(), !m_view->wrapCursor())); 02065 } 02066 02067 //BEGIN EVENT HANDLING STUFF 02068 02069 bool KateViewInternal::eventFilter( QObject *obj, QEvent *e ) 02070 { 02071 if (obj == m_lineScroll) 02072 { 02073 // the second condition is to make sure a scroll on the vertical bar doesn't cause a horizontal scroll ;) 02074 if (e->type() == QEvent::Wheel && m_lineScroll->minimum() != m_lineScroll->maximum()) 02075 { 02076 wheelEvent((QWheelEvent*)e); 02077 return true; 02078 } 02079 02080 // continue processing 02081 return QWidget::eventFilter( obj, e ); 02082 } 02083 02084 switch( e->type() ) 02085 { 02086 case QEvent::ChildAdded: 02087 case QEvent::ChildRemoved: { 02088 QChildEvent* c = static_cast<QChildEvent*>(e); 02089 if (c->added()) { 02090 c->child()->installEventFilter(this); 02091 /*foreach (QWidget* child, c->child()->findChildren<QWidget*>()) 02092 child->installEventFilter(this);*/ 02093 02094 } else if (c->removed()) { 02095 c->child()->removeEventFilter(this); 02096 02097 /*foreach (QWidget* child, c->child()->findChildren<QWidget*>()) 02098 child->removeEventFilter(this);*/ 02099 } 02100 } break; 02101 02102 case QEvent::ShortcutOverride: 02103 { 02104 QKeyEvent *k = static_cast<QKeyEvent *>(e); 02105 02106 if (k->key() == Qt::Key_Escape && k->modifiers() == Qt::NoModifier) { 02107 if (m_view->isCompletionActive()) { 02108 m_view->abortCompletion(); 02109 k->accept(); 02110 //kDebug() << obj << "shortcut override" << k->key() << "aborting completion"; 02111 return true; 02112 } else if (m_view->bottomViewBar()->isVisible()) { 02113 m_view->bottomViewBar()->hideCurrentBarWidget(); 02114 k->accept(); 02115 //kDebug() << obj << "shortcut override" << k->key() << "closing view bar"; 02116 return true; 02117 } else if (!m_view->config()->persistentSelection() && m_view->selection()) { 02118 m_view->clearSelection(); 02119 k->accept(); 02120 //kDebug() << obj << "shortcut override" << k->key() << "clearing selection"; 02121 return true; 02122 } else if (m_view->hasSearchBar()) { 02123 // hide search&replace highlights 02124 m_view->searchBar()->clearHighlights(); 02125 k->accept(); 02126 return true; 02127 } 02128 } 02129 02130 // if vi input mode key stealing is on, override kate shortcuts 02131 if (m_view->viInputMode() && m_view->viInputModeStealKeys() && ( m_view->getCurrentViMode() != InsertMode || 02132 ( m_view->getCurrentViMode() == InsertMode && k->modifiers() == Qt::ControlModifier ) ) ) { 02133 k->accept(); 02134 return true; 02135 } 02136 02137 } break; 02138 02139 case QEvent::KeyPress: 02140 { 02141 QKeyEvent *k = static_cast<QKeyEvent *>(e); 02142 02143 // Override all other single key shortcuts which do not use a modifier other than Shift 02144 if (obj == this && (!k->modifiers() || k->modifiers() == Qt::ShiftModifier)) { 02145 keyPressEvent( k ); 02146 if (k->isAccepted()) { 02147 //kDebug() << obj << "shortcut override" << k->key() << "using keystroke"; 02148 return true; 02149 } 02150 } 02151 02152 //kDebug() << obj << "shortcut override" << k->key() << "ignoring"; 02153 } break; 02154 02155 case QEvent::DragMove: 02156 { 02157 QPoint currentPoint = ((QDragMoveEvent*) e)->pos(); 02158 02159 QRect doNotScrollRegion( s_scrollMargin, s_scrollMargin, 02160 width() - s_scrollMargin * 2, 02161 height() - s_scrollMargin * 2 ); 02162 02163 if ( !doNotScrollRegion.contains( currentPoint ) ) 02164 { 02165 startDragScroll(); 02166 // Keep sending move events 02167 ( (QDragMoveEvent*)e )->accept( QRect(0,0,0,0) ); 02168 } 02169 02170 dragMoveEvent((QDragMoveEvent*)e); 02171 } break; 02172 02173 case QEvent::DragLeave: 02174 // happens only when pressing ESC while dragging 02175 stopDragScroll(); 02176 break; 02177 02178 case QEvent::WindowBlocked: 02179 // next focus originates from an internal dialog: 02180 // don't show the modonhd prompt 02181 if (isVisible()) { 02182 doc()->ignoreModifiedOnDiskOnce(); 02183 } 02184 break; 02185 02186 default: 02187 break; 02188 } 02189 02190 return QWidget::eventFilter( obj, e ); 02191 } 02192 02193 void KateViewInternal::keyPressEvent( QKeyEvent* e ) 02194 { 02195 if( e->key() == Qt::Key_Left && e->modifiers() == Qt::AltModifier ) { 02196 m_view->emitNavigateLeft(); 02197 e->setAccepted(true); 02198 return; 02199 } 02200 if( e->key() == Qt::Key_Right && e->modifiers() == Qt::AltModifier ) { 02201 m_view->emitNavigateRight(); 02202 e->setAccepted(true); 02203 return; 02204 } 02205 if( e->key() == Qt::Key_Up && e->modifiers() == Qt::AltModifier ) { 02206 m_view->emitNavigateUp(); 02207 e->setAccepted(true); 02208 return; 02209 } 02210 if( e->key() == Qt::Key_Down && e->modifiers() == Qt::AltModifier ) { 02211 m_view->emitNavigateDown(); 02212 e->setAccepted(true); 02213 return; 02214 } 02215 if( e->key() == Qt::Key_Return && e->modifiers() == Qt::AltModifier ) { 02216 m_view->emitNavigateAccept(); 02217 e->setAccepted(true); 02218 return; 02219 } 02220 if( e->key() == Qt::Key_Backspace && e->modifiers() == Qt::AltModifier ) { 02221 m_view->emitNavigateBack(); 02222 e->setAccepted(true); 02223 return; 02224 } 02225 02226 if( e->key() == Qt::Key_Alt && m_view->completionWidget()->isCompletionActive() ) { 02227 m_completionItemExpanded = m_view->completionWidget()->toggleExpanded(true); 02228 m_view->completionWidget()->resetHadNavigation(); 02229 m_altDownTime = QTime::currentTime(); 02230 } 02231 02232 // Note: AND'ing with <Shift> is a quick hack to fix Key_Enter 02233 const int key = e->key() | (e->modifiers() & Qt::ShiftModifier); 02234 02235 if (m_view->isCompletionActive()) 02236 { 02237 if( key == Qt::Key_Enter || key == Qt::Key_Return ) { 02238 m_view->completionWidget()->execute(); 02239 e->accept(); 02240 return; 02241 } 02242 } 02243 02244 if ( m_view->viInputMode() ) { 02245 if ( !m_view->config()->viInputModeHideStatusBar() ) { 02246 m_view->viModeBar()->clearMessage(); // clear [error] message 02247 } 02248 02249 if ( getViInputModeManager()->getCurrentViMode() == InsertMode 02250 || getViInputModeManager()->getCurrentViMode() == ReplaceMode ) { 02251 if ( getViInputModeManager()->handleKeypress( e ) ) { 02252 return; 02253 } else if ( e->modifiers() != Qt::NoModifier && e->modifiers() != Qt::ShiftModifier ) { 02254 // re-post key events not handled if they have a modifier other than shift 02255 QEvent *copy = new QKeyEvent ( e->type(), e->key(), e->modifiers(), e->text(), 02256 e->isAutoRepeat(), e->count() ); 02257 QCoreApplication::postEvent( parent(), copy ); 02258 } 02259 } else { // !InsertMode 02260 if ( !getViInputModeManager()->handleKeypress( e ) ) { 02261 // we didn't need that keypress, un-steal it :-) 02262 QEvent *copy = new QKeyEvent ( e->type(), e->key(), e->modifiers(), e->text(), 02263 e->isAutoRepeat(), e->count() ); 02264 QCoreApplication::postEvent( parent(), copy ); 02265 } 02266 m_view->updateViModeBarCmd(); 02267 return; 02268 } 02269 } 02270 02271 if( !doc()->isReadWrite() ) 02272 { 02273 e->ignore(); 02274 return; 02275 } 02276 02277 if ((key == Qt::Key_Return) || (key == Qt::Key_Enter)) 02278 { 02279 doReturn(); 02280 e->accept(); 02281 return; 02282 } 02283 02284 if (key == Qt::Key_Backspace || key == Qt::SHIFT + Qt::Key_Backspace) 02285 { 02286 //m_view->backspace(); 02287 e->accept(); 02288 02289 return; 02290 } 02291 02292 if (key == Qt::Key_Tab || key == Qt::SHIFT+Qt::Key_Backtab || key == Qt::Key_Backtab) 02293 { 02294 if(m_view->completionWidget()->isCompletionActive()) 02295 { 02296 e->accept(); 02297 m_view->completionWidget()->tab(key != Qt::Key_Tab); 02298 return; 02299 } 02300 02301 if( key == Qt::Key_Tab ) 02302 { 02303 uint tabHandling = doc()->config()->tabHandling(); 02304 // convert tabSmart into tabInsertsTab or tabIndents: 02305 if (tabHandling == KateDocumentConfig::tabSmart) 02306 { 02307 // multiple lines selected 02308 if (m_view->selection() && !m_view->selectionRange().onSingleLine()) 02309 { 02310 tabHandling = KateDocumentConfig::tabIndents; 02311 } 02312 02313 // otherwise: take look at cursor position 02314 else 02315 { 02316 // if the cursor is at or before the first non-space character 02317 // or on an empty line, 02318 // Tab indents, otherwise it inserts a tab character. 02319 Kate::TextLine line = doc()->kateTextLine( m_cursor.line() ); 02320 int first = line->firstChar(); 02321 if (first < 0 || m_cursor.column() <= first) 02322 tabHandling = KateDocumentConfig::tabIndents; 02323 else 02324 tabHandling = KateDocumentConfig::tabInsertsTab; 02325 } 02326 } 02327 02328 if (tabHandling == KateDocumentConfig::tabInsertsTab) 02329 doc()->typeChars( m_view, QString("\t") ); 02330 else 02331 doc()->indent( m_view->selection() ? m_view->selectionRange() : KTextEditor::Range(m_cursor.line(), 0, m_cursor.line(), 0), 1 ); 02332 02333 e->accept(); 02334 02335 return; 02336 } 02337 else if (doc()->config()->tabHandling() != KateDocumentConfig::tabInsertsTab) 02338 { 02339 // key == Qt::SHIFT+Qt::Key_Backtab || key == Qt::Key_Backtab 02340 doc()->indent( m_view->selection() ? m_view->selectionRange() : KTextEditor::Range(m_cursor.line(), 0, m_cursor.line(), 0), -1 ); 02341 e->accept(); 02342 02343 return; 02344 } 02345 } 02346 02347 if ( !(e->modifiers() & Qt::ControlModifier) && !e->text().isEmpty() && doc()->typeChars ( m_view, e->text() ) ) 02348 { 02349 e->accept(); 02350 02351 return; 02352 } 02353 02354 // allow composition of AltGr + (q|2|3) on windows 02355 static const int altGR = Qt::ControlModifier | Qt::AltModifier; 02356 if( (e->modifiers() & altGR) == altGR && !e->text().isEmpty() && doc()->typeChars ( m_view, e->text() ) ) 02357 { 02358 e->accept(); 02359 02360 return; 02361 } 02362 02363 e->ignore(); 02364 } 02365 02366 void KateViewInternal::keyReleaseEvent( QKeyEvent* e ) 02367 { 02368 if( e->key() == Qt::Key_Alt && m_view->completionWidget()->isCompletionActive() && ((m_completionItemExpanded && (m_view->completionWidget()->hadNavigation() || m_altDownTime.msecsTo(QTime::currentTime()) > 300)) || (!m_completionItemExpanded && !m_view->completionWidget()->hadNavigation())) ) { 02369 02370 m_view->completionWidget()->toggleExpanded(false, true); 02371 } 02372 02373 if ((e->modifiers() & Qt::SHIFT) == Qt::SHIFT) 02374 { 02375 m_shiftKeyPressed = true; 02376 } 02377 else 02378 { 02379 if (m_shiftKeyPressed) 02380 { 02381 m_shiftKeyPressed = false; 02382 02383 if (m_selChangedByUser) 02384 { 02385 if (m_view->selection()) 02386 QApplication::clipboard()->setText(m_view->selectionText (), QClipboard::Selection); 02387 02388 m_selChangedByUser = false; 02389 } 02390 } 02391 } 02392 02393 e->ignore(); 02394 return; 02395 } 02396 02397 void KateViewInternal::contextMenuEvent ( QContextMenuEvent * e ) 02398 { 02399 // try to show popup menu 02400 02401 QPoint p = e->pos(); 02402 02403 if ( doc()->browserView() ) 02404 { 02405 m_view->contextMenuEvent( e ); 02406 return; 02407 } 02408 02409 if ( e->reason() == QContextMenuEvent::Keyboard ) 02410 { 02411 makeVisible( m_displayCursor, 0 ); 02412 p = cursorCoordinates(false); 02413 p.rx() -= startX(); 02414 } 02415 else if ( ! m_view->selection() || m_view->config()->persistentSelection() ) 02416 placeCursor( e->pos() ); 02417 02418 // popup is a qguardedptr now 02419 if (m_view->contextMenu()) { 02420 m_view->spellingMenu()->setUseMouseForMisspelledRange((e->reason() == QContextMenuEvent::Mouse)); 02421 m_view->contextMenu()->popup( mapToGlobal( p ) ); 02422 e->accept (); 02423 } 02424 } 02425 02426 void KateViewInternal::mousePressEvent( QMouseEvent* e ) 02427 { 02428 switch (e->button()) 02429 { 02430 case Qt::LeftButton: 02431 m_selChangedByUser = false; 02432 02433 if (m_possibleTripleClick) 02434 { 02435 m_possibleTripleClick = false; 02436 02437 m_selectionMode = Line; 02438 02439 if ( e->modifiers() & Qt::ShiftModifier ) 02440 { 02441 updateSelection( m_cursor, true ); 02442 } 02443 else 02444 { 02445 m_view->selectLine( m_cursor ); 02446 if (m_view->selection()) 02447 m_selectAnchor = m_view->selectionRange().start(); 02448 } 02449 02450 if (m_view->selection()) 02451 QApplication::clipboard()->setText(m_view->selectionText (), QClipboard::Selection); 02452 02453 // Keep the line at the select anchor selected during further 02454 // mouse selection 02455 if ( m_selectAnchor.line() > m_view->selectionRange().start().line() ) 02456 { 02457 // Preserve the last selected line 02458 if ( m_selectAnchor == m_view->selectionRange().end() && m_selectAnchor.column() == 0 ) 02459 m_selectionCached.start().setPosition( m_selectAnchor.line()-1, 0 ); 02460 else 02461 m_selectionCached.start().setPosition( m_selectAnchor.line(), 0 ); 02462 m_selectionCached.end() = m_view->selectionRange().end(); 02463 } 02464 else 02465 { 02466 // Preserve the first selected line 02467 m_selectionCached.start() = m_view->selectionRange().start(); 02468 if ( m_view->selectionRange().end().line() > m_view->selectionRange().start().line() ) 02469 m_selectionCached.end().setPosition( m_view->selectionRange().start().line()+1, 0 ); 02470 else 02471 m_selectionCached.end() = m_view->selectionRange().end(); 02472 } 02473 02474 moveCursorToSelectionEdge(); 02475 02476 m_scrollX = 0; 02477 m_scrollY = 0; 02478 m_scrollTimer.start (50); 02479 02480 e->accept(); 02481 return; 02482 } 02483 else if ( m_selectionMode == Default ) 02484 { 02485 m_selectionMode = Mouse; 02486 } 02487 02488 if ( e->modifiers() & Qt::ShiftModifier ) 02489 { 02490 if ( !m_selectAnchor.isValid() ) 02491 m_selectAnchor = m_cursor; 02492 } 02493 else 02494 { 02495 m_selectionCached = KTextEditor::Range::invalid(); 02496 } 02497 02498 if( !(e->modifiers() & Qt::ShiftModifier) && isTargetSelected( e->pos() ) ) 02499 { 02500 m_dragInfo.state = diPending; 02501 m_dragInfo.start = e->pos(); 02502 } 02503 else 02504 { 02505 m_dragInfo.state = diNone; 02506 02507 if ( e->modifiers() & Qt::ShiftModifier ) 02508 { 02509 placeCursor( e->pos(), true, false ); 02510 if ( m_selectionCached.start().isValid() ) 02511 { 02512 if ( m_cursor.toCursor() < m_selectionCached.start() ) 02513 m_selectAnchor = m_selectionCached.end(); 02514 else 02515 m_selectAnchor = m_selectionCached.start(); 02516 } 02517 setSelection( KTextEditor::Range( m_selectAnchor, m_cursor ) ); 02518 } 02519 else 02520 { 02521 placeCursor( e->pos() ); 02522 } 02523 02524 m_scrollX = 0; 02525 m_scrollY = 0; 02526 02527 m_scrollTimer.start (50); 02528 } 02529 02530 e->accept (); 02531 break; 02532 02533 default: 02534 e->ignore (); 02535 break; 02536 } 02537 } 02538 02539 void KateViewInternal::mouseDoubleClickEvent(QMouseEvent *e) 02540 { 02541 switch (e->button()) 02542 { 02543 case Qt::LeftButton: 02544 m_selectionMode = Word; 02545 02546 if ( e->modifiers() & Qt::ShiftModifier ) 02547 { 02548 KTextEditor::Range oldSelection = m_view->selectionRange(); 02549 02550 // Now select the word under the select anchor 02551 int cs, ce; 02552 Kate::TextLine l = doc()->kateTextLine( m_selectAnchor.line() ); 02553 02554 ce = m_selectAnchor.column(); 02555 if ( ce > 0 && doc()->highlight()->isInWord( l->at(ce) ) ) { 02556 for (; ce < l->length(); ce++ ) 02557 if ( !doc()->highlight()->isInWord( l->at(ce) ) ) 02558 break; 02559 } 02560 02561 cs = m_selectAnchor.column() - 1; 02562 if ( cs < doc()->lineLength( m_selectAnchor.line() ) 02563 && doc()->highlight()->isInWord( l->at(cs) ) ) { 02564 for ( cs--; cs >= 0; cs-- ) 02565 if ( !doc()->highlight()->isInWord( l->at(cs) ) ) 02566 break; 02567 } 02568 02569 // ...and keep it selected 02570 if (cs+1 < ce) 02571 { 02572 m_selectionCached.start().setPosition( m_selectAnchor.line(), cs+1 ); 02573 m_selectionCached.end().setPosition( m_selectAnchor.line(), ce ); 02574 } 02575 else 02576 { 02577 m_selectionCached.start() = m_selectAnchor; 02578 m_selectionCached.end() = m_selectAnchor; 02579 } 02580 // Now word select to the mouse cursor 02581 placeCursor( e->pos(), true ); 02582 } 02583 else 02584 { 02585 // first clear the selection, otherwise we run into bug #106402 02586 // ...and set the cursor position, for the same reason (otherwise there 02587 // are *other* idiosyncrasies we can't fix without reintroducing said 02588 // bug) 02589 // Parameters: don't redraw, and don't emit selectionChanged signal yet 02590 m_view->clearSelection( false, false ); 02591 placeCursor( e->pos() ); 02592 m_view->selectWord( m_cursor ); 02593 cursorToMatchingBracket(true); 02594 02595 if (m_view->selection()) 02596 { 02597 m_selectAnchor = m_view->selectionRange().start(); 02598 m_selectionCached = m_view->selectionRange(); 02599 } 02600 else 02601 { 02602 m_selectAnchor = m_cursor; 02603 m_selectionCached = KTextEditor::Range(m_cursor, m_cursor); 02604 } 02605 } 02606 02607 // Move cursor to end (or beginning) of selected word 02608 if (m_view->selection()) 02609 QApplication::clipboard()->setText( m_view->selectionText(), QClipboard::Selection ); 02610 02611 moveCursorToSelectionEdge(); 02612 m_possibleTripleClick = true; 02613 QTimer::singleShot ( QApplication::doubleClickInterval(), this, SLOT(tripleClickTimeout()) ); 02614 02615 m_scrollX = 0; 02616 m_scrollY = 0; 02617 02618 m_scrollTimer.start (50); 02619 02620 e->accept (); 02621 break; 02622 02623 default: 02624 e->ignore (); 02625 break; 02626 } 02627 } 02628 02629 void KateViewInternal::tripleClickTimeout() 02630 { 02631 m_possibleTripleClick = false; 02632 } 02633 02634 void KateViewInternal::mouseReleaseEvent( QMouseEvent* e ) 02635 { 02636 switch (e->button()) 02637 { 02638 case Qt::LeftButton: 02639 m_selectionMode = Default; 02640 // m_selectionCached.start().setLine( -1 ); 02641 02642 if (m_selChangedByUser) 02643 { 02644 if (m_view->selection()) { 02645 QApplication::clipboard()->setText(m_view->selectionText (), QClipboard::Selection); 02646 } 02647 moveCursorToSelectionEdge(); 02648 02649 m_selChangedByUser = false; 02650 } 02651 02652 if (m_dragInfo.state == diPending) 02653 placeCursor( e->pos(), e->modifiers() & Qt::ShiftModifier ); 02654 else if (m_dragInfo.state == diNone) 02655 m_scrollTimer.stop (); 02656 02657 m_dragInfo.state = diNone; 02658 02659 e->accept (); 02660 break; 02661 02662 case Qt::MidButton: 02663 placeCursor( e->pos() ); 02664 02665 if( doc()->isReadWrite() ) 02666 { 02667 doc()->paste( m_view, QClipboard::Selection ); 02668 repaint(); 02669 } 02670 02671 e->accept (); 02672 break; 02673 02674 default: 02675 e->ignore (); 02676 break; 02677 } 02678 } 02679 02680 void KateViewInternal::leaveEvent( QEvent* ) 02681 { 02682 m_textHintTimer.stop(); 02683 } 02684 02685 KTextEditor::Cursor KateViewInternal::coordinatesToCursor(const QPoint& _coord) const 02686 { 02687 QPoint coord(_coord); 02688 02689 KTextEditor::Cursor ret = KTextEditor::Cursor::invalid(); 02690 02691 coord.setX( coord.x() - m_leftBorder->width() + startX() ); 02692 02693 const KateTextLayout& thisLine = yToKateTextLayout(coord.y()); 02694 if (thisLine.isValid()) 02695 ret = renderer()->xToCursor(thisLine, coord.x(), !m_view->wrapCursor()); 02696 02697 return ret; 02698 } 02699 02700 void KateViewInternal::mouseMoveEvent( QMouseEvent* e ) 02701 { 02702 // FIXME only do this if needing to track mouse movement 02703 const KateTextLayout& thisLine = yToKateTextLayout(e->y()); 02704 if (thisLine.isValid()) { 02705 KTextEditor::Cursor newPosition = renderer()->xToCursor(thisLine, e->x(), !m_view->wrapCursor()); 02706 if (newPosition != m_mouse) { 02707 m_mouse = newPosition; 02708 mouseMoved(); 02709 } 02710 } else { 02711 if (m_mouse.isValid()) { 02712 m_mouse = KTextEditor::Cursor::invalid(); 02713 mouseMoved(); 02714 } 02715 } 02716 02717 if( e->buttons() & Qt::LeftButton ) 02718 { 02719 if (m_dragInfo.state == diPending) 02720 { 02721 // we had a mouse down, but haven't confirmed a drag yet 02722 // if the mouse has moved sufficiently, we will confirm 02723 QPoint p( e->pos() - m_dragInfo.start ); 02724 02725 // we've left the drag square, we can start a real drag operation now 02726 if( p.manhattanLength() > KGlobalSettings::dndEventDelay() ) 02727 doDrag(); 02728 02729 return; 02730 } 02731 else if (m_dragInfo.state == diDragging) 02732 { 02733 // Don't do anything after a canceled drag until the user lets go of 02734 // the mouse button! 02735 return; 02736 } 02737 02738 m_mouseX = e->x(); 02739 m_mouseY = e->y(); 02740 02741 m_scrollX = 0; 02742 m_scrollY = 0; 02743 int d = renderer()->lineHeight(); 02744 02745 if (m_mouseX < 0) 02746 m_scrollX = -d; 02747 02748 if (m_mouseX > width()) 02749 m_scrollX = d; 02750 02751 if (m_mouseY < 0) 02752 { 02753 m_mouseY = 0; 02754 m_scrollY = -d; 02755 } 02756 02757 if (m_mouseY > height()) 02758 { 02759 m_mouseY = height(); 02760 m_scrollY = d; 02761 } 02762 02763 placeCursor( QPoint( m_mouseX, m_mouseY ), true ); 02764 02765 } 02766 else 02767 { 02768 if (isTargetSelected( e->pos() ) ) { 02769 // mouse is over selected text. indicate that the text is draggable by setting 02770 // the arrow cursor as other Qt text editing widgets do 02771 if (m_mouseCursor != Qt::ArrowCursor) { 02772 m_mouseCursor = Qt::ArrowCursor; 02773 setCursor(m_mouseCursor); 02774 } 02775 } else { 02776 // normal text cursor 02777 if (m_mouseCursor != Qt::IBeamCursor) { 02778 m_mouseCursor = Qt::IBeamCursor; 02779 setCursor(m_mouseCursor); 02780 } 02781 } 02782 //We need to check whether the mouse position is actually within the widget, 02783 //because other widgets like the icon border forward their events to this, 02784 //and we will create invalid text hint requests if we don't check 02785 if (m_textHintEnabled && geometry().contains(parentWidget()->mapFromGlobal(e->globalPos()))) 02786 { 02787 m_textHintTimer.start(m_textHintTimeout); 02788 m_textHintMouseX=e->x(); 02789 m_textHintMouseY=e->y(); 02790 } 02791 } 02792 } 02793 02794 void KateViewInternal::updateDirty( ) 02795 { 02796 uint h = renderer()->lineHeight(); 02797 02798 int currentRectStart = -1; 02799 int currentRectEnd = -1; 02800 02801 QRegion updateRegion; 02802 02803 { 02804 for (int i = 0; i < cache()->viewCacheLineCount(); ++i) { 02805 if (cache()->viewLine(i).isDirty()) { 02806 if (currentRectStart == -1) { 02807 currentRectStart = h * i; 02808 currentRectEnd = h; 02809 } else { 02810 currentRectEnd += h; 02811 } 02812 02813 } else if (currentRectStart != -1) { 02814 updateRegion += QRect(0, currentRectStart, width(), currentRectEnd); 02815 currentRectStart = -1; 02816 currentRectEnd = -1; 02817 } 02818 } 02819 } 02820 02821 02822 if (currentRectStart != -1) 02823 updateRegion += QRect(0, currentRectStart, width(), currentRectEnd); 02824 02825 if (!updateRegion.isEmpty()) { 02826 if (debugPainting) kDebug( 13030 ) << k_funcinfo << "Update dirty region " << updateRegion; 02827 update(updateRegion); 02828 } 02829 } 02830 02831 void KateViewInternal::hideEvent(QHideEvent* e) 02832 { 02833 Q_UNUSED(e); 02834 if(m_view->isCompletionActive()) 02835 m_view->completionWidget()->abortCompletion(); 02836 } 02837 02838 void KateViewInternal::paintEvent(QPaintEvent *e) 02839 { 02840 if (m_smartDirty) 02841 doUpdateView(); 02842 02843 if (debugPainting) kDebug (13030) << "GOT PAINT EVENT: Region" << e->region(); 02844 02845 const QRect& unionRect = e->rect(); 02846 02847 int xStart = startX() + unionRect.x(); 02848 int xEnd = xStart + unionRect.width(); 02849 uint h = renderer()->lineHeight(); 02850 uint startz = (unionRect.y() / h); 02851 uint endz = startz + 1 + (unionRect.height() / h); 02852 uint lineRangesSize = cache()->viewCacheLineCount(); 02853 02854 QPainter paint(this); 02855 paint.setRenderHints (QPainter::Antialiasing); 02856 02857 // TODO put in the proper places 02858 renderer()->setCaretStyle(m_view->isOverwriteMode() ? KateRenderer::Block : KateRenderer::Line); 02859 renderer()->setShowTabs(doc()->config()->showTabs()); 02860 renderer()->setShowTrailingSpaces(doc()->config()->showSpaces()); 02861 02862 int sy = startz * h; 02863 paint.translate(unionRect.x(), startz * h); 02864 02865 for (uint z=startz; z <= endz; z++) 02866 { 02867 if ( (z >= lineRangesSize) || (cache()->viewLine(z).line() == -1) ) 02868 { 02869 if (!(z >= lineRangesSize)) 02870 cache()->viewLine(z).setDirty(false); 02871 02872 paint.fillRect( 0, 0, unionRect.width(), h, renderer()->config()->backgroundColor() ); 02873 } 02874 else 02875 { 02876 //kDebug( 13030 )<<"KateViewInternal::paintEvent(QPaintEvent *e):cache()->viewLine"<<z; 02877 KateTextLayout& thisLine = cache()->viewLine(z); 02878 02879 /* If viewLine() returns non-zero, then a document line was split 02880 in several visual lines, and we're trying to paint visual line 02881 that is not the first. In that case, this line was already 02882 painted previously, since KateRenderer::paintTextLine paints 02883 all visual lines. 02884 Except if we're at the start of the region that needs to 02885 be painted -- when no previous calls to paintTextLine were made. 02886 */ 02887 if (!thisLine.viewLine() || z == startz) { 02888 // Don't bother if we're not in the requested update region 02889 if (!e->region().contains(QRect(unionRect.x(), startz * h, unionRect.width(), h))) 02890 continue; 02891 02892 //kDebug (13030) << "paint text: line: " << thisLine.line() << " viewLine " << thisLine.viewLine() << " x: " << unionRect.x() << " y: " << sy 02893 // << " width: " << xEnd-xStart << " height: " << h << endl; 02894 02895 if (thisLine.viewLine()) 02896 paint.translate(QPoint(0, h * - thisLine.viewLine())); 02897 02898 // The paintTextLine function should be well behaved, but if not, this clipping may be needed 02899 //paint.setClipRect(QRect(xStart, 0, xEnd - xStart, h * (thisLine.kateLineLayout()->viewLineCount()))); 02900 02901 KTextEditor::Cursor pos = m_cursor; 02902 renderer()->paintTextLine(paint, thisLine.kateLineLayout(), xStart, xEnd, &pos); 02903 02904 //paint.setClipping(false); 02905 02906 if (thisLine.viewLine()) 02907 paint.translate(0, h * thisLine.viewLine()); 02908 02909 thisLine.setDirty(false); 02910 } 02911 } 02912 02913 paint.translate(0, h); 02914 sy += h; 02915 } 02916 } 02917 02918 void KateViewInternal::resizeEvent(QResizeEvent* e) 02919 { 02920 bool expandedHorizontally = width() > e->oldSize().width(); 02921 bool expandedVertically = height() > e->oldSize().height(); 02922 bool heightChanged = height() != e->oldSize().height(); 02923 02924 m_madeVisible = false; 02925 02926 if (heightChanged) { 02927 setAutoCenterLines(m_autoCenterLines, false); 02928 m_cachedMaxStartPos.setPosition(-1, -1); 02929 } 02930 02931 if (m_view->dynWordWrap()) { 02932 bool dirtied = false; 02933 02934 for (int i = 0; i < cache()->viewCacheLineCount(); i++) { 02935 // find the first dirty line 02936 // the word wrap updateView algorithm is forced to check all lines after a dirty one 02937 KateTextLayout viewLine = cache()->viewLine(i); 02938 02939 if (viewLine.wrap() || viewLine.isRightToLeft() || viewLine.width() > width()) { 02940 dirtied = true; 02941 viewLine.setDirty(); 02942 break; 02943 } 02944 } 02945 02946 if (dirtied || heightChanged) { 02947 updateView(true); 02948 m_leftBorder->update(); 02949 } 02950 02951 if (width() < e->oldSize().width()) { 02952 if (!m_view->wrapCursor()) { 02953 // May have to restrain cursor to new smaller width... 02954 if (m_cursor.column() > doc()->lineLength(m_cursor.line())) { 02955 KateTextLayout thisLine = currentLayout(); 02956 02957 KTextEditor::Cursor newCursor(m_cursor.line(), thisLine.endCol() + ((width() - thisLine.xOffset() - thisLine.width()) / renderer()->spaceWidth()) - 1); 02958 updateCursor(newCursor); 02959 } 02960 } 02961 } 02962 02963 } else { 02964 updateView(); 02965 02966 if (expandedHorizontally && startX() > 0) 02967 scrollColumns(startX() - (width() - e->oldSize().width())); 02968 } 02969 02970 if (expandedVertically) { 02971 KTextEditor::Cursor max = maxStartPos(); 02972 if (startPos() > max) 02973 scrollPos(max); 02974 } 02975 emit m_view->displayRangeChanged(m_view); 02976 } 02977 02978 void KateViewInternal::scrollTimeout () 02979 { 02980 if (m_scrollX || m_scrollY) 02981 { 02982 scrollLines (startPos().line() + (m_scrollY / (int) renderer()->lineHeight())); 02983 placeCursor( QPoint( m_mouseX, m_mouseY ), true ); 02984 } 02985 } 02986 02987 void KateViewInternal::cursorTimeout () 02988 { 02989 if (!debugPainting && !m_view->viInputMode()) { 02990 renderer()->setDrawCaret(!renderer()->drawCaret()); 02991 paintCursor(); 02992 } 02993 } 02994 02995 void KateViewInternal::textHintTimeout () 02996 { 02997 m_textHintTimer.stop (); 02998 02999 KateTextLayout thisLine = yToKateTextLayout(m_textHintMouseY); 03000 03001 if (!thisLine.isValid()) return; 03002 03003 if (m_textHintMouseX> (lineMaxCursorX(thisLine) - thisLine.startX())) return; 03004 03005 KTextEditor::Cursor c = renderer()->xToCursor(cache()->textLayout(thisLine.start()), startX() + m_textHintMouseX, !m_view->wrapCursor()); 03006 03007 QString tmp; 03008 03009 emit m_view->needTextHint(c, tmp); 03010 03011 if (!tmp.isEmpty()) kDebug(13030)<<"Hint text: "<<tmp; 03012 } 03013 03014 void KateViewInternal::focusInEvent (QFocusEvent *) 03015 { 03016 if (KApplication::cursorFlashTime() > 0) 03017 m_cursorTimer.start ( KApplication::cursorFlashTime() / 2 ); 03018 03019 paintCursor(); 03020 03021 doc()->setActiveView( m_view ); 03022 03023 // this will handle focus stuff in kateview 03024 m_view->slotGotFocus (); 03025 } 03026 03027 void KateViewInternal::focusOutEvent (QFocusEvent *) 03028 { 03029 //if (m_view->isCompletionActive()) 03030 //m_view->abortCompletion(); 03031 03032 m_cursorTimer.stop(); 03033 m_view->renderer()->setDrawCaret(true); 03034 paintCursor(); 03035 03036 m_textHintTimer.stop(); 03037 03038 m_view->slotLostFocus (); 03039 } 03040 03041 void KateViewInternal::doDrag() 03042 { 03043 m_dragInfo.state = diDragging; 03044 m_dragInfo.dragObject = new QDrag(this); 03045 QMimeData *mimeData=new QMimeData(); 03046 mimeData->setText(m_view->selectionText()); 03047 m_dragInfo.dragObject->setMimeData(mimeData); 03048 m_dragInfo.dragObject->start(Qt::MoveAction); 03049 } 03050 03051 void KateViewInternal::dragEnterEvent( QDragEnterEvent* event ) 03052 { 03053 if (event->source()==this) event->setDropAction(Qt::MoveAction); 03054 event->setAccepted( (event->mimeData()->hasText() && doc()->isReadWrite()) || 03055 KUrl::List::canDecode(event->mimeData()) ); 03056 } 03057 03058 void KateViewInternal::fixDropEvent(QDropEvent* event) { 03059 if (event->source()!=this) event->setDropAction(Qt::CopyAction); 03060 else { 03061 Qt::DropAction action=Qt::MoveAction; 03062 #ifdef Q_WS_MAC 03063 if(event->keyboardModifiers() & Qt::AltModifier) 03064 action = Qt::CopyAction; 03065 #else 03066 if (event->keyboardModifiers() & Qt::ControlModifier) 03067 action = Qt::CopyAction; 03068 #endif 03069 event->setDropAction(action); 03070 } 03071 } 03072 03073 void KateViewInternal::dragMoveEvent( QDragMoveEvent* event ) 03074 { 03075 // track the cursor to the current drop location 03076 placeCursor( event->pos(), true, false ); 03077 03078 // important: accept action to switch between copy and move mode 03079 // without this, the text will always be copied. 03080 fixDropEvent(event); 03081 } 03082 03083 void KateViewInternal::dropEvent( QDropEvent* event ) 03084 { 03085 if ( KUrl::List::canDecode(event->mimeData()) ) { 03086 03087 emit dropEventPass(event); 03088 03089 } else if ( event->mimeData()->hasText() && doc()->isReadWrite() ) { 03090 03091 QString text=event->mimeData()->text(); 03092 03093 // is the source our own document? 03094 bool priv = false; 03095 if (KateViewInternal* vi = qobject_cast<KateViewInternal*>(event->source())) 03096 priv = doc()->ownedView( vi->m_view ); 03097 03098 // dropped on a text selection area? 03099 bool selected = m_view->cursorSelected(m_cursor); 03100 03101 fixDropEvent(event); 03102 03103 if( priv && selected && event->dropAction() != Qt::CopyAction ) { 03104 // this is a drag that we started and dropped on our selection 03105 // ignore this case 03106 return; 03107 } 03108 03109 // fix the cursor position before editStart(), so that it is correctly 03110 // stored for the undo action 03111 KTextEditor::Cursor targetCursor(m_cursor); // backup current cursor 03112 int selectionWidth = m_view->selectionRange().columnWidth(); // for block selection 03113 int selectionHeight = m_view->selectionRange().numberOfLines(); // for block selection 03114 03115 if ( event->dropAction() != Qt::CopyAction ) { 03116 editSetCursor(m_view->selectionRange().end()); 03117 } else { 03118 m_view->clearSelection(); 03119 } 03120 03121 // use one transaction 03122 doc()->editStart (); 03123 03124 // on move: remove selected text; on copy: duplicate text 03125 doc()->insertText(targetCursor, text, m_view->blockSelection()); 03126 03127 Kate::TextCursor startCursor(doc()->buffer(), targetCursor, KTextEditor::MovingCursor::MoveOnInsert); 03128 03129 if ( event->dropAction() != Qt::CopyAction ) 03130 m_view->removeSelectedText(); 03131 03132 Kate::TextCursor endCursor1(doc()->buffer(), startCursor, KTextEditor::MovingCursor::MoveOnInsert); 03133 03134 if ( !m_view->blockSelection() ) { 03135 endCursor1.move(text.length()); 03136 } else { 03137 endCursor1.setColumn(startCursor.column()+selectionWidth); 03138 endCursor1.setLine(startCursor.line()+selectionHeight); 03139 } 03140 03141 KTextEditor::Cursor endCursor(endCursor1); 03142 kDebug( 13030 )<<startCursor<<"---("<<text.length()<<")---"<<endCursor; 03143 setSelection(KTextEditor::Range(startCursor,endCursor)); 03144 editSetCursor(endCursor); 03145 03146 doc()->editEnd (); 03147 03148 event->acceptProposedAction(); 03149 updateView(); 03150 } 03151 03152 // finally finish drag and drop mode 03153 m_dragInfo.state = diNone; 03154 // important, because the eventFilter`s DragLeave does not occur 03155 stopDragScroll(); 03156 } 03157 //END EVENT HANDLING STUFF 03158 03159 void KateViewInternal::clear() 03160 { 03161 m_startPos.setPosition (0, 0); 03162 m_displayCursor = KTextEditor::Cursor(0, 0); 03163 m_cursor.setPosition(0, 0); 03164 updateView(true); 03165 } 03166 03167 void KateViewInternal::wheelEvent(QWheelEvent* e) 03168 { 03169 if (m_lineScroll->minimum() != m_lineScroll->maximum() && e->orientation() != Qt::Horizontal) { 03170 // React to this as a vertical event 03171 if ( ( e->modifiers() & Qt::ControlModifier ) || ( e->modifiers() & Qt::ShiftModifier ) ) { 03172 if (e->delta() > 0) 03173 scrollPrevPage(); 03174 else 03175 scrollNextPage(); 03176 } else { 03177 QApplication::sendEvent(m_lineScroll, e); 03178 } 03179 03180 } else if (columnScrollingPossible()) { 03181 QWheelEvent copy = *e; 03182 QApplication::sendEvent(m_columnScroll, ©); 03183 03184 } else { 03185 e->ignore(); 03186 } 03187 } 03188 03189 void KateViewInternal::startDragScroll() 03190 { 03191 if ( !m_dragScrollTimer.isActive() ) { 03192 m_dragScrollTimer.start( s_scrollTime ); 03193 } 03194 } 03195 03196 void KateViewInternal::stopDragScroll() 03197 { 03198 m_dragScrollTimer.stop(); 03199 updateView(); 03200 } 03201 03202 void KateViewInternal::doDragScroll() 03203 { 03204 QPoint p = this->mapFromGlobal( QCursor::pos() ); 03205 03206 int dx = 0, dy = 0; 03207 if ( p.y() < s_scrollMargin ) { 03208 dy = p.y() - s_scrollMargin; 03209 } else if ( p.y() > height() - s_scrollMargin ) { 03210 dy = s_scrollMargin - (height() - p.y()); 03211 } 03212 03213 if ( p.x() < s_scrollMargin ) { 03214 dx = p.x() - s_scrollMargin; 03215 } else if ( p.x() > width() - s_scrollMargin ) { 03216 dx = s_scrollMargin - (width() - p.x()); 03217 } 03218 03219 dy /= 4; 03220 03221 if (dy) 03222 scrollLines(startPos().line() + dy); 03223 03224 if (columnScrollingPossible () && dx) 03225 scrollColumns(qMin (m_startX + dx, m_columnScroll->maximum())); 03226 03227 if (!dy && !dx) 03228 stopDragScroll(); 03229 } 03230 03231 void KateViewInternal::enableTextHints(int timeout) 03232 { 03233 m_textHintTimeout=timeout; 03234 m_textHintEnabled=true; 03235 m_textHintTimer.start(timeout); 03236 } 03237 03238 void KateViewInternal::disableTextHints() 03239 { 03240 m_textHintEnabled=false; 03241 m_textHintTimer.stop (); 03242 } 03243 03244 //BEGIN EDIT STUFF 03245 void KateViewInternal::editStart() 03246 { 03247 editSessionNumber++; 03248 03249 if (editSessionNumber > 1) 03250 return; 03251 03252 editIsRunning = true; 03253 editOldCursor = m_cursor; 03254 } 03255 03256 void KateViewInternal::editEnd(int editTagLineStart, int editTagLineEnd, bool tagFrom) 03257 { 03258 if (editSessionNumber == 0) 03259 return; 03260 03261 editSessionNumber--; 03262 03263 if (editSessionNumber > 0) 03264 return; 03265 03266 doc()->buffer().ensureHighlighted(endLine()); 03267 03268 // fix start position, might have moved from column 0 03269 m_startPos.setPosition (m_startPos.line(), 0); 03270 03271 if (tagFrom && (editTagLineStart <= int(doc()->getRealLine(startLine())))) 03272 tagAll(); 03273 else 03274 tagLines (editTagLineStart, tagFrom ? qMax(doc()->lastLine() + 1, editTagLineEnd) : editTagLineEnd, true); 03275 03276 if (editOldCursor == m_cursor.toCursor()) 03277 updateBracketMarks(); 03278 03279 updateView(true); 03280 03281 if (editOldCursor != m_cursor.toCursor() || m_view == doc()->activeView()) 03282 { 03283 m_madeVisible = false; 03284 updateCursor ( m_cursor, true ); 03285 } 03286 03287 editIsRunning = false; 03288 } 03289 03290 void KateViewInternal::editSetCursor (const KTextEditor::Cursor &_cursor) 03291 { 03292 if (m_cursor.toCursor() != _cursor) 03293 { 03294 m_cursor.setPosition(_cursor); 03295 } 03296 } 03297 //END 03298 03299 void KateViewInternal::viewSelectionChanged () 03300 { 03301 m_selectAnchor = KTextEditor::Cursor::invalid(); 03302 // Do NOT nuke the entire range! The reason is that a shift+DC selection 03303 // might (correctly) set the range to be empty (i.e. start() == end()), and 03304 // subsequent dragging might shrink the selection into non-existence. When 03305 // this happens, we use the cached end to restore the cached start so that 03306 // updateSelection is not confused. See also comments in updateSelection. 03307 m_selectionCached.start() = KTextEditor::Cursor::invalid(); 03308 // updateView(true); 03309 } 03310 03311 KateLayoutCache* KateViewInternal::cache( ) const 03312 { 03313 return m_layoutCache; 03314 } 03315 03316 KTextEditor::Cursor KateViewInternal::toRealCursor( const KTextEditor::Cursor & virtualCursor ) const 03317 { 03318 return KTextEditor::Cursor(doc()->getRealLine(virtualCursor.line()), virtualCursor.column()); 03319 } 03320 03321 KTextEditor::Cursor KateViewInternal::toVirtualCursor( const KTextEditor::Cursor & realCursor ) const 03322 { 03323 return KTextEditor::Cursor(doc()->getVirtualLine(realCursor.line()), realCursor.column()); 03324 } 03325 03326 KateRenderer * KateViewInternal::renderer( ) const 03327 { 03328 return m_view->renderer(); 03329 } 03330 03331 void KateViewInternal::mouseMoved( ) 03332 { 03333 m_view->notifyMousePositionChanged(m_mouse); 03334 m_view->updateRangesIn (KTextEditor::Attribute::ActivateMouseIn); 03335 } 03336 03337 void KateViewInternal::cursorMoved( ) 03338 { 03339 m_view->updateRangesIn (KTextEditor::Attribute::ActivateCaretIn); 03340 } 03341 03342 bool KateViewInternal::rangeAffectsView(const KTextEditor::Range& range, bool realCursors) const 03343 { 03344 int startLine = m_startPos.line(); 03345 int endLine = startLine + (int)m_visibleLineCount; 03346 03347 if ( realCursors ) { 03348 startLine = (int)doc()->getRealLine(startLine); 03349 endLine = (int)doc()->getRealLine(endLine); 03350 } 03351 03352 return (range.end().line() >= startLine) || (range.start().line() <= endLine); 03353 } 03354 03355 //BEGIN IM INPUT STUFF 03356 QVariant KateViewInternal::inputMethodQuery ( Qt::InputMethodQuery query ) const 03357 { 03358 switch (query) { 03359 case Qt::ImMicroFocus: { 03360 // Cursor placement code is changed for Asian input method that 03361 // shows candidate window. This behavior is same as Qt/E 2.3.7 03362 // which supports Asian input methods. Asian input methods need 03363 // start point of IM selection text to place candidate window as 03364 // adjacent to the selection text. 03365 KTextEditor::Cursor c = m_imPreeditRange ? m_imPreeditRange->start() : m_cursor; 03366 return QRect (cursorToCoordinate(c, true, false), QSize(0, renderer()->lineHeight())); 03367 } 03368 03369 case Qt::ImFont: 03370 return renderer()->currentFont(); 03371 03372 case Qt::ImCursorPosition: 03373 return m_imPreeditRange ? m_imPreeditRange->start().column() : 0; 03374 03375 case Qt::ImSurroundingText: 03376 if (Kate::TextLine l = doc()->kateTextLine(m_cursor.line())) 03377 return l->string(); 03378 else 03379 return QString(); 03380 03381 case Qt::ImCurrentSelection: 03382 if (m_view->selection()) 03383 return m_view->selectionText(); 03384 else 03385 return QString(); 03386 default: 03387 /* values: ImMaximumTextLength and ImAnchorPosition */ 03388 break; 03389 } 03390 03391 return QWidget::inputMethodQuery(query); 03392 } 03393 03394 void KateViewInternal::inputMethodEvent(QInputMethodEvent* e) 03395 { 03396 if ( doc()->readOnly() ) { 03397 e->ignore(); 03398 return; 03399 } 03400 03401 //kDebug( 13030 ) << "Event: cursor" << m_cursor << "commit" << e->commitString() << "preedit" << e->preeditString() << "replacement start" << e->replacementStart() << "length" << e->replacementLength(); 03402 03403 bool createdPreedit = false; 03404 if (!m_imPreeditRange) { 03405 createdPreedit = true; 03406 m_imPreeditRange = doc()->newMovingRange (KTextEditor::Range(m_cursor, m_cursor), KTextEditor::MovingRange::ExpandLeft | KTextEditor::MovingRange::ExpandRight); 03407 } 03408 03409 if (!m_imPreeditRange->toRange().isEmpty()) { 03410 doc()->inputMethodStart(); 03411 doc()->removeText(*m_imPreeditRange); 03412 doc()->inputMethodEnd(); 03413 } 03414 03415 if (!e->commitString().isEmpty() || e->replacementLength()) { 03416 m_view->removeSelectedText(); 03417 03418 KTextEditor::Range preeditRange = *m_imPreeditRange; 03419 03420 KTextEditor::Cursor start(m_imPreeditRange->start().line(), m_imPreeditRange->start().column() + e->replacementStart()); 03421 KTextEditor::Cursor removeEnd = start + KTextEditor::Cursor(0, e->replacementLength()); 03422 03423 doc()->editStart(); 03424 if (start != removeEnd) 03425 doc()->removeText(KTextEditor::Range(start, removeEnd)); 03426 if (!e->commitString().isEmpty()) { 03427 // if the input method event is text that should be inserted, call KateDocument::typeChars() 03428 // with the text. that method will handle the input and take care of overwrite mode, etc. 03429 doc()->typeChars(m_view, e->commitString()); 03430 } 03431 doc()->editEnd(); 03432 03433 // Revert to the same range as above 03434 m_imPreeditRange->setRange(preeditRange); 03435 } 03436 03437 if (!e->preeditString().isEmpty()) { 03438 doc()->inputMethodStart(); 03439 doc()->insertText(m_imPreeditRange->start(), e->preeditString()); 03440 doc()->inputMethodEnd(); 03441 // The preedit range gets automatically repositioned 03442 } 03443 03444 // Finished this input method context? 03445 if (m_imPreeditRange && e->preeditString().isEmpty()) { 03446 // delete the range and reset the pointer 03447 delete m_imPreeditRange; 03448 m_imPreeditRange = 0L; 03449 qDeleteAll (m_imPreeditRangeChildren); 03450 m_imPreeditRangeChildren.clear (); 03451 03452 if ( KApplication::cursorFlashTime() > 0 ) 03453 renderer()->setDrawCaret(false); 03454 renderer()->setCaretOverrideColor(QColor()); 03455 03456 e->accept(); 03457 return; 03458 } 03459 03460 KTextEditor::Cursor newCursor = m_cursor; 03461 bool hideCursor = false; 03462 QColor caretColor; 03463 03464 if (m_imPreeditRange) { 03465 qDeleteAll (m_imPreeditRangeChildren); 03466 m_imPreeditRangeChildren.clear (); 03467 03468 int decorationColumn = 0; 03469 foreach (const QInputMethodEvent::Attribute &a, e->attributes()) { 03470 if (a.type == QInputMethodEvent::Cursor) { 03471 newCursor = m_imPreeditRange->start() + KTextEditor::Cursor(0, a.start); 03472 hideCursor = !a.length; 03473 QColor c = qvariant_cast<QColor>(a.value); 03474 if (c.isValid()) 03475 caretColor = c; 03476 03477 } else if (a.type == QInputMethodEvent::TextFormat) { 03478 QTextCharFormat f = qvariant_cast<QTextFormat>(a.value).toCharFormat(); 03479 if (f.isValid() && decorationColumn <= a.start) { 03480 KTextEditor::Range fr(m_imPreeditRange->start().line(), m_imPreeditRange->start().column() + a.start, m_imPreeditRange->start().line(), m_imPreeditRange->start().column() + a.start + a.length); 03481 KTextEditor::MovingRange* formatRange = doc()->newMovingRange (fr); 03482 KTextEditor::Attribute::Ptr attribute(new KTextEditor::Attribute()); 03483 attribute->merge(f); 03484 formatRange->setAttribute(attribute); 03485 decorationColumn = a.start + a.length; 03486 m_imPreeditRangeChildren.push_back (formatRange); 03487 } 03488 } 03489 } 03490 } 03491 03492 renderer()->setDrawCaret(hideCursor); 03493 renderer()->setCaretOverrideColor(caretColor); 03494 03495 if (newCursor != m_cursor.toCursor()) 03496 updateCursor(newCursor); 03497 03498 e->accept(); 03499 } 03500 03501 //END IM INPUT STUFF 03502 03503 ViMode KateViewInternal::getCurrentViMode() 03504 { 03505 return getViInputModeManager()->getCurrentViMode(); 03506 } 03507 03508 KateViInputModeManager* KateViewInternal::getViInputModeManager() 03509 { 03510 if (!m_viInputModeManager) { 03511 m_viInputModeManager = new KateViInputModeManager(m_view, this); 03512 } 03513 03514 return m_viInputModeManager; 03515 } 03516 03517 // kate: space-indent on; indent-width 2; replace-tabs on;
KDE 4.6 API Reference