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