• Skip to content
  • Skip to link menu
KDE 4.6 API Reference
  • KDE API Reference
  • kdelibs
  • KDE Home
  • Contact Us
 

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, &copy);
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;

Kate

Skip menu "Kate"
  • Main Page
  • Namespace List
  • Class Hierarchy
  • Alphabetical List
  • Class List
  • File List
  • Namespace Members
  • Class Members
  • Related Pages

kdelibs

Skip menu "kdelibs"
  • DNSSD
  • Interfaces
  •   KHexEdit
  •   KMediaPlayer
  •   KSpeech
  •   KTextEditor
  • Kate
  • kconf_update
  • KDE3Support
  •   KUnitTest
  • KDECore
  • KDED
  • KDEsu
  • KDEUI
  • KDEWebKit
  • KDocTools
  • KFile
  • KHTML
  • KImgIO
  • KInit
  • kio
  • KIOSlave
  • KJS
  •   KJS-API
  •   WTF
  • kjsembed
  • KNewStuff
  • KParts
  • KPty
  • Kross
  • KUnitConversion
  • KUtils
  • Nepomuk
  • Plasma
  • Solid
  • Sonnet
  • ThreadWeaver
Generated for kdelibs by doxygen 1.7.3
This website is maintained by Adriaan de Groot and Allen Winter.
KDE® and the K Desktop Environment® logo are registered trademarks of KDE e.V. | Legal