• Skip to content
  • Skip to link menu
KDE 4.3 API Reference
  • KDE API Reference
  • kdelibs
  • Sitemap
  • Contact Us
 

Kate

kateviewinternal.cpp

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