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