• 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::updateCursor( const KTextEditor::Cursor& newCursor, bool force, bool center, bool calledExternally )
01853 {
01854   if ( !force && (m_cursor == newCursor) )
01855   {
01856     if ( !m_madeVisible && m_view == m_doc->activeView() )
01857     {
01858       // unfold if required
01859       m_doc->foldingTree()->ensureVisible( newCursor.line() );
01860 
01861       makeVisible ( m_displayCursor, m_displayCursor.column(), false, center, calledExternally );
01862     }
01863 
01864     return;
01865   }
01866 
01867   // unfold if required
01868   m_doc->foldingTree()->ensureVisible( newCursor.line() );
01869 
01870   KTextEditor::Cursor oldDisplayCursor = m_displayCursor;
01871 
01872   m_cursor = newCursor;
01873   m_displayCursor = toVirtualCursor(m_cursor);
01874   
01875   if ( m_view == m_doc->activeView() )
01876     makeVisible ( m_displayCursor, m_displayCursor.column(), false, center, calledExternally );
01877 
01878   updateBracketMarks();
01879 
01880   // It's efficient enough to just tag them both without checking to see if they're on the same view line
01881 /*  kdDebug()<<"oldDisplayCursor:"<<oldDisplayCursor<<endl;
01882   kdDebug()<<"m_displayCursor:"<<m_displayCursor<<endl;*/
01883   tagLine(oldDisplayCursor);
01884   tagLine(m_displayCursor);
01885 
01886   updateMicroFocus();
01887 
01888   if (m_cursorTimer.isActive ())
01889   {
01890     if ( KApplication::cursorFlashTime() > 0 )
01891       m_cursorTimer.start( KApplication::cursorFlashTime() / 2 );
01892     renderer()->setDrawCaret(true);
01893   }
01894 
01895   // Remember the maximum X position if requested
01896   if (m_preserveX)
01897     m_preserveX = false;
01898   else {
01899     QMutexLocker lock(m_doc->smartMutex());
01900     m_preservedX = renderer()->cursorToX(cache()->textLayout(m_cursor), m_cursor, !m_view->wrapCursor());
01901   }
01902 
01903   //kDebug(13030) << "m_preservedX: " << m_preservedX << " (was "<< oldmaxx << "), m_cursorX: " << m_cursorX;
01904   //kDebug(13030) << "Cursor now located at real " << cursor.line << "," << cursor.col << ", virtual " << m_displayCursor.line << ", " << m_displayCursor.col << "; Top is " << startLine() << ", " << startPos().col;
01905 
01906   cursorMoved();
01907 
01908   if(!m_doc->isEditRunning())
01909       m_doc->setUndoDontMerge(true);
01910 
01911   updateDirty(); //paintText(0, 0, width(), height(), true);
01912 
01914   emit m_view->cursorPositionChanged(m_view, m_cursor);
01915 }
01916 
01917 void KateViewInternal::updateBracketMarkAttributes()
01918 {
01919   KTextEditor::Attribute::Ptr bracketFill = KTextEditor::Attribute::Ptr(new KTextEditor::Attribute());
01920   bracketFill->setBackground(m_view->m_renderer->config()->highlightedBracketColor());
01921   bracketFill->setBackgroundFillWhitespace(false);
01922   bracketFill->setFontBold();
01923 
01924   m_bmStart->setAttribute(bracketFill);
01925   m_bmEnd->setAttribute(bracketFill);
01926 
01927   if (m_view->m_renderer->config()->showWholeBracketExpression()) {
01928 
01929     KTextEditor::Attribute::Ptr expressionFill = KTextEditor::Attribute::Ptr(new KTextEditor::Attribute());
01930     expressionFill->setBackground(m_view->m_renderer->config()->highlightedBracketColor());
01931     expressionFill->setBackgroundFillWhitespace(false);
01932 
01933     m_bm->setAttribute(expressionFill);
01934   } else {
01935     m_bm->setAttribute(KTextEditor::Attribute::Ptr(new KTextEditor::Attribute()));
01936   }
01937 }
01938 
01939 void KateViewInternal::updateBracketMarks()
01940 {
01941   bool showWholeBracketExpression = m_view->m_renderer->config()->showWholeBracketExpression();
01942 
01943   QMutexLocker lock(m_doc->smartMutex());
01944   if (m_bmHighlighted) {
01945     view()->removeInternalHighlight(m_bmStart);
01946     view()->removeInternalHighlight(m_bmEnd);
01947     view()->removeInternalHighlight(m_bm);
01948     m_bmHighlighted = false;
01949   }
01950 
01951   if ( m_bm->isValid() ) {
01952     tagRange(*m_bmStart, true);
01953     tagRange(*m_bmEnd, true);
01954     tagRange(*m_bm, true);
01955   }
01956 
01957   // add some limit to this, this is really endless on big files without limit
01958   int maxLines = linesDisplayed () * 3;
01959   m_doc->newBracketMark( m_cursor, *m_bm, maxLines );
01960 
01961   if ( m_bm->isValid() ) {
01962     m_bmStart->start() = m_bm->start();
01963     m_bmStart->end().setPosition(m_bm->start().line(), m_bm->start().column() + 1);
01964 
01965     m_bmEnd->start() = m_bm->end();
01966     m_bmEnd->end().setPosition(m_bm->end().line(), m_bm->end().column() + 1);
01967 
01968     tagRange(*m_bmStart, true);
01969     tagRange(*m_bmEnd, true);
01970     if (showWholeBracketExpression) {
01971       tagRange(*m_bm, true);
01972     }
01973 
01974     view()->addInternalHighlight(m_bmStart);
01975     view()->addInternalHighlight(m_bmEnd);
01976     if (showWholeBracketExpression) {
01977       view()->addInternalHighlight(m_bm);
01978     }
01979     m_bmHighlighted = true;
01980   }
01981 }
01982 
01983 bool KateViewInternal::tagLine(const KTextEditor::Cursor& virtualCursor)
01984 {
01985   QMutexLocker lock(m_doc->smartMutex());
01986   // FIXME may be a more efficient way for this
01987   if ((int)m_doc->getRealLine(virtualCursor.line()) > m_doc->lastLine())
01988     return false;
01989   // End FIXME
01990 
01991   int viewLine = cache()->displayViewLine(virtualCursor, true);
01992   if (viewLine >= 0 && viewLine < cache()->viewCacheLineCount()) {
01993     cache()->viewLine(viewLine).setDirty();
01994     m_leftBorder->update (0, lineToY(viewLine), m_leftBorder->width(), renderer()->fontHeight());
01995     return true;
01996   }
01997   return false;
01998 }
01999 
02000 bool KateViewInternal::tagLines( int start, int end, bool realLines )
02001 {
02002   return tagLines(KTextEditor::Cursor(start, 0), KTextEditor::Cursor(end, -1), realLines);
02003 }
02004 
02005 bool KateViewInternal::tagLines(KTextEditor::Cursor start, KTextEditor::Cursor end, bool realCursors)
02006 {
02007   QMutexLocker lock(m_doc->smartMutex());
02008   if (realCursors)
02009   {
02010     cache()->relayoutLines(start.line(), end.line());
02011 
02012     //kDebug(13030)<<"realLines is true";
02013     start = toVirtualCursor(start);
02014     end = toVirtualCursor(end);
02015 
02016   } else {
02017     cache()->relayoutLines(toRealCursor(start).line(), toRealCursor(end).line());
02018   }
02019 
02020   if (end.line() < startLine())
02021   {
02022     //kDebug(13030)<<"end<startLine";
02023     return false;
02024   }
02025   // Used to be > endLine(), but cache may not be valid when checking, so use a
02026   // less optimal but still adequate approximation (potential overestimation but minimal performance difference)
02027   if (start.line() > startLine() + cache()->viewCacheLineCount())
02028   {
02029     //kDebug(13030)<<"start> endLine"<<start<<" "<<(endLine());
02030     return false;
02031   }
02032 
02033   cache()->updateViewCache(startPos());
02034 
02035   //kDebug(13030) << "tagLines( [" << start << "], [" << end << "] )";
02036 
02037   bool ret = false;
02038 
02039   for (int z = 0; z < cache()->viewCacheLineCount(); z++)
02040   {
02041     KateTextLayout& line = cache()->viewLine(z);
02042     if ((line.virtualLine() > start.line() || (line.virtualLine() == start.line() && line.endCol() >= start.column() && start.column() != -1)) &&
02043         (line.virtualLine() < end.line() || (line.virtualLine() == end.line() && (line.startCol() <= end.column() || end.column() == -1)))) {
02044       ret = true;
02045       break;
02046       //kDebug(13030) << "Tagged line " << line.line();
02047     }
02048   }
02049 
02050   if (!m_view->dynWordWrap())
02051   {
02052     int y = lineToY( start.line() );
02053     // FIXME is this enough for when multiple lines are deleted
02054     int h = (end.line() - start.line() + 2) * renderer()->fontHeight();
02055     if (end.line() >= m_doc->numVisLines() - 1)
02056       h = height();
02057 
02058     m_leftBorder->update (0, y, m_leftBorder->width(), h);
02059   }
02060   else
02061   {
02062     // FIXME Do we get enough good info in editRemoveText to optimise this more?
02063     //bool justTagged = false;
02064     for (int z = 0; z < cache()->viewCacheLineCount(); z++)
02065     {
02066       KateTextLayout& line = cache()->viewLine(z);
02067       if (!line.isValid() ||
02068           ((line.virtualLine() > start.line() || (line.virtualLine() == start.line() && line.endCol() >= start.column() && start.column() != -1)) &&
02069            (line.virtualLine() < end.line() || (line.virtualLine() == end.line() && (line.startCol() <= end.column() || end.column() == -1)))))
02070       {
02071         //justTagged = true;
02072         m_leftBorder->update (0, z * renderer()->fontHeight(), m_leftBorder->width(), m_leftBorder->height());
02073         break;
02074       }
02075       /*else if (justTagged)
02076       {
02077         justTagged = false;
02078         leftBorder->update (0, z * m_doc->viewFont.fontHeight, leftBorder->width(), m_doc->viewFont.fontHeight);
02079         break;
02080       }*/
02081     }
02082   }
02083 
02084   return ret;
02085 }
02086 
02087 bool KateViewInternal::tagRange(const KTextEditor::Range& range, bool realCursors)
02088 {
02089   return tagLines(range.start(), range.end(), realCursors);
02090 }
02091 
02092 void KateViewInternal::tagAll()
02093 {
02094   QMutexLocker lock(m_doc->smartMutex());
02095 
02096   // clear the cache...
02097   cache()->clear ();
02098 
02099   m_leftBorder->updateFont();
02100   m_leftBorder->update();
02101 }
02102 
02103 void KateViewInternal::paintCursor()
02104 {
02105   if (tagLine(m_displayCursor))
02106     updateDirty(); //paintText (0,0,width(), height(), true);
02107 }
02108 
02109 // Point in content coordinates
02110 void KateViewInternal::placeCursor( const QPoint& p, bool keepSelection, bool updateSelection )
02111 {
02112   KateTextLayout thisLine = yToKateTextLayout(p.y());
02113   KTextEditor::Cursor c;
02114   
02115   QMutexLocker lock(m_doc->smartMutex());
02116 
02117   if (!thisLine.isValid()) // probably user clicked below the last line -> use the last line
02118     thisLine = cache()->textLayout(m_doc->lines() - 1, -1);
02119 
02120   c = renderer()->xToCursor(thisLine, startX() + p.x(), !view()->wrapCursor());
02121 
02122   if (c.line () < 0 || c.line() >= m_doc->lines()) {
02123     return;
02124   }
02125   
02126   lock.unlock();
02127   
02128   if (updateSelection)
02129     KateViewInternal::updateSelection( c, keepSelection );
02130 
02131   updateCursor( c );
02132 }
02133 
02134 // Point in content coordinates
02135 bool KateViewInternal::isTargetSelected( const QPoint& p )
02136 {
02137   const KateTextLayout& thisLine = yToKateTextLayout(p.y());
02138   if (!thisLine.isValid())
02139     return false;
02140 
02141   return m_view->cursorSelected(renderer()->xToCursor(thisLine, startX() + p.x(), !view()->wrapCursor()));
02142 }
02143 
02144 //BEGIN EVENT HANDLING STUFF
02145 
02146 bool KateViewInternal::eventFilter( QObject *obj, QEvent *e )
02147 {
02148   if (obj == m_lineScroll)
02149   {
02150     // the second condition is to make sure a scroll on the vertical bar doesn't cause a horizontal scroll ;)
02151     if (e->type() == QEvent::Wheel && m_lineScroll->minimum() != m_lineScroll->maximum())
02152     {
02153       wheelEvent((QWheelEvent*)e);
02154       return true;
02155     }
02156 
02157     // continue processing
02158     return QWidget::eventFilter( obj, e );
02159   }
02160 
02161   switch( e->type() )
02162   {
02163     case QEvent::ChildAdded:
02164     case QEvent::ChildRemoved: {
02165       QChildEvent* c = static_cast<QChildEvent*>(e);
02166       if (c->added()) {
02167         c->child()->installEventFilter(this);
02168         /*foreach (QWidget* child, c->child()->findChildren<QWidget*>())
02169           child->installEventFilter(this);*/
02170 
02171       } else if (c->removed()) {
02172         c->child()->removeEventFilter(this);
02173 
02174         /*foreach (QWidget* child, c->child()->findChildren<QWidget*>())
02175           child->removeEventFilter(this);*/
02176       }
02177     } break;
02178 
02179     case QEvent::ShortcutOverride:
02180     {
02181       QKeyEvent *k = static_cast<QKeyEvent *>(e);
02182 
02183       if (k->key() == Qt::Key_Escape) {
02184         if (m_view->isCompletionActive()) {
02185           m_view->abortCompletion();
02186           k->accept();
02187           //kDebug() << obj << "shortcut override" << k->key() << "aborting completion";
02188           return true;
02189         } else if (m_view->viewBar()->isVisible()) {
02190           m_view->viewBar()->hideCurrentBarWidget();
02191           k->accept();
02192           //kDebug() << obj << "shortcut override" << k->key() << "closing view bar";
02193           return true;
02194         } else if (!m_view->config()->persistentSelection() && m_view->selection()) {
02195           m_view->clearSelection();
02196           k->accept();
02197           //kDebug() << obj << "shortcut override" << k->key() << "clearing selection";
02198           return true;
02199         }
02200       }
02201 
02202       // if vi input mode key stealing is on, override kate shortcuts
02203       if (m_view->viInputMode() && m_view->viInputModeStealKeys() &&  ( m_view->getCurrentViMode() != InsertMode ||
02204               ( m_view->getCurrentViMode() == InsertMode && k->modifiers() == Qt::ControlModifier ) ) ) {
02205         k->accept();
02206         return true;
02207       }
02208 
02209     } break;
02210 
02211     case QEvent::KeyPress:
02212     {
02213       QKeyEvent *k = static_cast<QKeyEvent *>(e);
02214 
02215       // Override all other single key shortcuts which do not use a modifier other than Shift
02216       if (obj == this && (!k->modifiers() || k->modifiers() == Qt::ShiftModifier)) {
02217         keyPressEvent( k );
02218         if (k->isAccepted()) {
02219           //kDebug() << obj << "shortcut override" << k->key() << "using keystroke";
02220           return true;
02221         }
02222       }
02223 
02224       //kDebug() << obj << "shortcut override" << k->key() << "ignoring";
02225     } break;
02226 
02227     case QEvent::DragMove:
02228     {
02229       QPoint currentPoint = ((QDragMoveEvent*) e)->pos();
02230 
02231       QRect doNotScrollRegion( s_scrollMargin, s_scrollMargin,
02232                           width() - s_scrollMargin * 2,
02233                           height() - s_scrollMargin * 2 );
02234 
02235       if ( !doNotScrollRegion.contains( currentPoint ) )
02236       {
02237           startDragScroll();
02238           // Keep sending move events
02239           ( (QDragMoveEvent*)e )->accept( QRect(0,0,0,0) );
02240       }
02241 
02242       dragMoveEvent((QDragMoveEvent*)e);
02243     } break;
02244 
02245     case QEvent::DragLeave:
02246       // happens only when pressing ESC while dragging
02247       stopDragScroll();
02248       break;
02249 
02250     case QEvent::WindowBlocked:
02251       // next focus originates from an internal dialog:
02252       // don't show the modonhd prompt
02253       m_doc->ignoreModifiedOnDiskOnce();
02254       break;
02255 
02256     default:
02257       break;
02258   }
02259 
02260   return QWidget::eventFilter( obj, e );
02261 }
02262 
02263 void KateViewInternal::keyPressEvent( QKeyEvent* e )
02264 {
02265   if( e->key() == Qt::Key_Left && e->modifiers() == Qt::AltModifier ) {
02266     m_view->emitNavigateLeft();
02267     e->setAccepted(true);
02268     return;
02269   }
02270   if( e->key() == Qt::Key_Right && e->modifiers() == Qt::AltModifier ) {
02271     m_view->emitNavigateRight();
02272     e->setAccepted(true);
02273     return;
02274   }
02275   if( e->key() == Qt::Key_Up && e->modifiers() == Qt::AltModifier ) {
02276     m_view->emitNavigateUp();
02277     e->setAccepted(true);
02278     return;
02279   }
02280   if( e->key() == Qt::Key_Down && e->modifiers() == Qt::AltModifier ) {
02281     m_view->emitNavigateDown();
02282     e->setAccepted(true);
02283     return;
02284   }
02285   if( e->key() == Qt::Key_Return && e->modifiers() == Qt::AltModifier ) {
02286     m_view->emitNavigateAccept();
02287     e->setAccepted(true);
02288     return;
02289   }
02290   if( e->key() == Qt::Key_Backspace && e->modifiers() == Qt::AltModifier ) {
02291     m_view->emitNavigateBack();
02292     e->setAccepted(true);
02293     return;
02294   }
02295 
02296   if( e->key() == Qt::Key_Alt && view()->completionWidget()->isCompletionActive() ) {
02297     m_completionItemExpanded = view()->completionWidget()->toggleExpanded(true);
02298     view()->completionWidget()->resetHadNavigation();
02299     m_altDownTime = QTime::currentTime();
02300   }
02301 
02302   // Note: AND'ing with <Shift> is a quick hack to fix Key_Enter
02303   const int key = e->key() | (e->modifiers() & Qt::ShiftModifier);
02304 
02305   if (m_view->isCompletionActive())
02306   {
02307     if( key == Qt::Key_Enter || key == Qt::Key_Return ) {
02308       m_view->completionWidget()->execute();
02309       e->accept();
02310       return;
02311     }
02312   }
02313 
02314   if ( m_view->viInputMode() ) {
02315     if ( !m_view->config()->viInputModeHideStatusBar() ) {
02316       m_view->viModeBar()->clearMessage(); // clear [error] message
02317     }
02318 
02319     if ( getViInputModeManager()->getCurrentViMode() == InsertMode ) {
02320       if ( getViInputModeManager()->handleKeypress( e ) ) {
02321         return;
02322       } else if ( e->modifiers() != Qt::NoModifier && e->modifiers() != Qt::ShiftModifier ) {
02323         // re-post key events not handled if they have a modifier other than shift
02324         QEvent *copy = new QKeyEvent ( e->type(), e->key(), e->modifiers(), e->text(),
02325             e->isAutoRepeat(), e->count() );
02326         QCoreApplication::postEvent( parent(), copy );
02327       }
02328     } else { // !InsertMode
02329       if ( !getViInputModeManager()->handleKeypress( e ) ) {
02330         // we didn't need that keypress, un-steal it :-)
02331         QEvent *copy = new QKeyEvent ( e->type(), e->key(), e->modifiers(), e->text(),
02332             e->isAutoRepeat(), e->count() );
02333         QCoreApplication::postEvent( parent(), copy );
02334       }
02335       m_view->updateViModeBarCmd();
02336       return;
02337     }
02338   }
02339 
02340   if( !m_doc->isReadWrite() )
02341   {
02342     e->ignore();
02343     return;
02344   }
02345 
02346   if ((key == Qt::Key_Return) || (key == Qt::Key_Enter))
02347   {
02348     doReturn();
02349     e->accept();
02350     return;
02351   }
02352 
02353   if (key == Qt::Key_Backspace || key == Qt::SHIFT + Qt::Key_Backspace)
02354   {
02355     //m_view->backspace();
02356     e->accept();
02357 
02358     return;
02359   }
02360 
02361   if  (key == Qt::Key_Tab || key == Qt::SHIFT+Qt::Key_Backtab || key == Qt::Key_Backtab)
02362   {
02363     if (m_doc->invokeTemplateHandler(key)) {
02364       e->accept();
02365       return;
02366     }
02367 
02368     if( key == Qt::Key_Tab )
02369     {
02370       uint tabHandling = m_doc->config()->tabHandling();
02371       // convert tabSmart into tabInsertsTab or tabIndents:
02372       if (tabHandling == KateDocumentConfig::tabSmart)
02373       {
02374         if (m_view->selection())
02375         {
02376           tabHandling = KateDocumentConfig::tabIndents;
02377         }
02378         else
02379         {
02380           // if the cursor is at or before the first non-space character
02381           // or on an empty line,
02382           // Tab indents, otherwise it inserts a tab character.
02383           KateTextLine::Ptr line = m_doc->kateTextLine( m_cursor.line() );
02384           int first = line->firstChar();
02385           if (first < 0 || m_cursor.column() <= first)
02386             tabHandling = KateDocumentConfig::tabIndents;
02387           else
02388             tabHandling = KateDocumentConfig::tabInsertsTab;
02389         }
02390       }
02391 
02392       if (tabHandling == KateDocumentConfig::tabInsertsTab)
02393         m_doc->typeChars( m_view, QString("\t") );
02394       else
02395         m_doc->indent( m_view, m_cursor.line(), 1 );
02396 
02397       e->accept();
02398 
02399       return;
02400     }
02401     else if (m_doc->config()->tabHandling() != KateDocumentConfig::tabInsertsTab)
02402     {
02403       // key == Qt::SHIFT+Qt::Key_Backtab || key == Qt::Key_Backtab
02404       m_doc->indent( m_view, m_cursor.line(), -1 );
02405       e->accept();
02406 
02407       return;
02408     }
02409   }
02410 
02411   if ( !(e->modifiers() & Qt::ControlModifier) && !e->text().isEmpty() && m_doc->typeChars ( m_view, e->text() ) )
02412   {
02413     e->accept();
02414 
02415     return;
02416   }
02417 
02418   // allow composition of AltGr + (q|2|3) on windows
02419   static const int altGR = Qt::ControlModifier | Qt::AltModifier;
02420   if( (e->modifiers() & altGR) == altGR && !e->text().isEmpty() && m_doc->typeChars ( m_view, e->text() ) )
02421   {
02422     e->accept();
02423 
02424     return;
02425   }
02426 
02427   e->ignore();
02428 }
02429 
02430 void KateViewInternal::keyReleaseEvent( QKeyEvent* e )
02431 {
02432   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())) ) {
02433 
02434     view()->completionWidget()->toggleExpanded(false, true);
02435   }
02436 
02437   if (e->key() == Qt::SHIFT)
02438   {
02439     m_shiftKeyPressed = true;
02440   }
02441   else
02442   {
02443     if (m_shiftKeyPressed)
02444     {
02445       m_shiftKeyPressed = false;
02446 
02447       if (m_selChangedByUser)
02448       {
02449         if (m_view->selection())
02450           QApplication::clipboard()->setText(m_view->selectionText (), QClipboard::Selection);
02451 
02452         m_selChangedByUser = false;
02453       }
02454     }
02455   }
02456 
02457   e->ignore();
02458   return;
02459 }
02460 
02461 void KateViewInternal::contextMenuEvent ( QContextMenuEvent * e )
02462 {
02463   // try to show popup menu
02464 
02465   QPoint p = e->pos();
02466 
02467   if ( m_view->m_doc->browserView() )
02468   {
02469     m_view->contextMenuEvent( e );
02470     return;
02471   }
02472 
02473   if ( e->reason() == QContextMenuEvent::Keyboard )
02474   {
02475     makeVisible( m_cursor, 0 );
02476     p = cursorCoordinates(false);
02477     p.rx() -= startX();
02478   }
02479   else if ( ! m_view->selection() || m_view->config()->persistentSelection() )
02480     placeCursor( e->pos() );
02481 
02482   // popup is a qguardedptr now
02483   if (m_view->contextMenu()) {
02484     m_view->contextMenu()->popup( mapToGlobal( p ) );
02485     e->accept ();
02486   }
02487 }
02488 
02489 void KateViewInternal::mousePressEvent( QMouseEvent* e )
02490 {
02491   switch (e->button())
02492   {
02493     case Qt::LeftButton:
02494         m_selChangedByUser = false;
02495 
02496         if (m_possibleTripleClick)
02497         {
02498           m_possibleTripleClick = false;
02499 
02500           m_selectionMode = Line;
02501 
02502           if ( e->modifiers() & Qt::ShiftModifier )
02503           {
02504             updateSelection( m_cursor, true );
02505           }
02506           else
02507           {
02508             m_view->selectLine( m_cursor );
02509           }
02510 
02511           if (m_view->selection())
02512             QApplication::clipboard()->setText(m_view->selectionText (), QClipboard::Selection);
02513 
02514           // Keep the line at the select anchor selected during further
02515           // mouse selection
02516           if ( m_selectAnchor.line() > m_view->selectionRange().start().line() )
02517           {
02518             // Preserve the last selected line
02519             if ( m_selectAnchor == m_view->selectionRange().end() && m_selectAnchor.column() == 0 )
02520               m_selectionCached.start().setPosition( m_selectAnchor.line()-1, 0 );
02521             else
02522               m_selectionCached.start().setPosition( m_selectAnchor.line(), 0 );
02523             m_selectionCached.end() = m_view->selectionRange().end();
02524           }
02525           else
02526           {
02527             // Preserve the first selected line
02528             m_selectionCached.start() = m_view->selectionRange().start();
02529             if ( m_view->selectionRange().end().line() > m_view->selectionRange().start().line() )
02530               m_selectionCached.end().setPosition( m_view->selectionRange().start().line()+1, 0 );
02531             else
02532               m_selectionCached.end() = m_view->selectionRange().end();
02533           }
02534 
02535           // Set cursor to edge of selection... which edge depends on what
02536           // "direction" the selection was made in
02537           if ( m_view->selectionRange().start() < m_selectAnchor
02538                && m_selectAnchor.line() != m_view->selectionRange().start().line() )
02539             updateCursor( m_view->selectionRange().start() );
02540           else
02541             updateCursor( m_view->selectionRange().end() );
02542 
02543           e->accept();
02544           return;
02545         }
02546         else if ( m_selectionMode == Default )
02547         {
02548           m_selectionMode = Mouse;
02549         }
02550 
02551         if ( e->modifiers() & Qt::ShiftModifier )
02552         {
02553           if ( !m_selectAnchor.isValid() )
02554             m_selectAnchor = m_cursor;
02555         }
02556         else
02557         {
02558           m_selectionCached = KTextEditor::Range::invalid();
02559         }
02560 
02561         if( !(e->modifiers() & Qt::ShiftModifier) && isTargetSelected( e->pos() ) )
02562         {
02563           m_dragInfo.state = diPending;
02564           m_dragInfo.start = e->pos();
02565         }
02566         else
02567         {
02568           m_dragInfo.state = diNone;
02569 
02570           if ( e->modifiers() & Qt::ShiftModifier )
02571           {
02572             placeCursor( e->pos(), true, false );
02573             if ( m_selectionCached.start().isValid() )
02574             {
02575               if ( m_cursor < m_selectionCached.start() )
02576                 m_selectAnchor = m_selectionCached.end();
02577               else
02578                 m_selectAnchor = m_selectionCached.start();
02579             }
02580             m_view->setSelection( KTextEditor::Range( m_selectAnchor, m_cursor ) );
02581           }
02582           else
02583           {
02584             placeCursor( e->pos() );
02585           }
02586 
02587           m_scrollX = 0;
02588           m_scrollY = 0;
02589 
02590           m_scrollTimer.start (50);
02591         }
02592 
02593         e->accept ();
02594         break;
02595 
02596     default:
02597       e->ignore ();
02598       break;
02599   }
02600 }
02601 
02602 void KateViewInternal::mouseDoubleClickEvent(QMouseEvent *e)
02603 {
02604   switch (e->button())
02605   {
02606     case Qt::LeftButton:
02607       m_selectionMode = Word;
02608 
02609       if ( e->modifiers() & Qt::ShiftModifier )
02610       {
02611         KTextEditor::Range oldSelection = m_view->selectionRange();
02612 
02613         // Now select the word under the select anchor
02614         int cs, ce;
02615         KateTextLine::Ptr l = m_doc->kateTextLine( m_selectAnchor.line() );
02616 
02617         ce = m_selectAnchor.column();
02618         if ( ce > 0 && m_doc->highlight()->isInWord( l->at(ce) ) ) {
02619           for (; ce < l->length(); ce++ )
02620             if ( !m_doc->highlight()->isInWord( l->at(ce) ) )
02621               break;
02622         }
02623 
02624         cs = m_selectAnchor.column() - 1;
02625         if ( cs < m_doc->lineLength( m_selectAnchor.line() )
02626               && m_doc->highlight()->isInWord( l->at(cs) ) ) {
02627           for ( cs--; cs >= 0; cs-- )
02628             if ( !m_doc->highlight()->isInWord( l->at(cs) ) )
02629               break;
02630         }
02631 
02632         // ...and keep it selected
02633         if (cs+1 < ce)
02634         {
02635           m_selectionCached.start().setPosition( m_selectAnchor.line(), cs+1 );
02636           m_selectionCached.end().setPosition( m_selectAnchor.line(), ce );
02637         }
02638         else
02639         {
02640           m_selectionCached.start() = m_selectAnchor;
02641           m_selectionCached.end() = m_selectAnchor;
02642         }
02643         // Now word select to the mouse cursor
02644         placeCursor( e->pos(), true );
02645       }
02646       else
02647       {
02648         // first clear the selection, otherwise we run into bug #106402
02649         // ...and set the cursor position, for the same reason (otherwise there
02650         // are *other* idiosyncrasies we can't fix without reintroducing said
02651         // bug)
02652         // Parameters: don't redraw, and don't emit selectionChanged signal yet
02653         m_view->clearSelection( false, false );
02654         placeCursor( e->pos() );
02655         m_view->selectWord( m_cursor );
02656 
02657         if (m_view->selection())
02658         {
02659           m_selectAnchor = m_view->selectionRange().start();
02660           m_selectionCached = m_view->selectionRange();
02661         }
02662         else
02663         {
02664           // if we didn't actually select anything, restore the selection mode
02665           // -- see bug #131369 (kling)
02666           m_selectionMode = Default;
02667         }
02668       }
02669 
02670       // Move cursor to end (or beginning) of selected word
02671       if (m_view->selection())
02672       {
02673         QApplication::clipboard()->setText( m_view->selectionText(), QClipboard::Selection );
02674 
02675         // Shift+DC before the "cached" word should move the cursor to the
02676         // beginning of the selection, not the end
02677         if (m_view->selectionRange().start() < m_selectionCached.start())
02678           updateCursor( m_view->selectionRange().start() );
02679         else
02680           updateCursor( m_view->selectionRange().end() );
02681       }
02682 
02683       m_possibleTripleClick = true;
02684       QTimer::singleShot ( QApplication::doubleClickInterval(), this, SLOT(tripleClickTimeout()) );
02685 
02686       m_scrollX = 0;
02687       m_scrollY = 0;
02688 
02689       m_scrollTimer.start (50);
02690 
02691       e->accept ();
02692       break;
02693 
02694     default:
02695       e->ignore ();
02696       break;
02697   }
02698 }
02699 
02700 void KateViewInternal::tripleClickTimeout()
02701 {
02702   m_possibleTripleClick = false;
02703 }
02704 
02705 void KateViewInternal::mouseReleaseEvent( QMouseEvent* e )
02706 {
02707   switch (e->button())
02708   {
02709     case Qt::LeftButton:
02710       m_selectionMode = Default;
02711 //       m_selectionCached.start().setLine( -1 );
02712 
02713       if (m_selChangedByUser)
02714       {
02715         if (m_view->selection()) {
02716           QApplication::clipboard()->setText(m_view->selectionText (), QClipboard::Selection);
02717 
02718           // Set cursor to edge of selection... which edge depends on what
02719           // "direction" the selection was made in
02720           if ( m_view->selectionRange().start() < m_selectAnchor )
02721             updateCursor( m_view->selectionRange().start() );
02722           else
02723             updateCursor( m_view->selectionRange().end() );
02724         }
02725 
02726         m_selChangedByUser = false;
02727       }
02728 
02729       if (m_dragInfo.state == diPending)
02730         placeCursor( e->pos(), e->modifiers() & Qt::ShiftModifier );
02731       else if (m_dragInfo.state == diNone)
02732         m_scrollTimer.stop ();
02733 
02734       m_dragInfo.state = diNone;
02735 
02736       e->accept ();
02737       break;
02738 
02739     case Qt::MidButton:
02740       placeCursor( e->pos() );
02741 
02742       if( m_doc->isReadWrite() )
02743       {
02744         m_doc->paste( m_view, QClipboard::Selection );
02745         repaint();
02746       }
02747 
02748       e->accept ();
02749       break;
02750 
02751     default:
02752       e->ignore ();
02753       break;
02754   }
02755 }
02756 
02757 void KateViewInternal::leaveEvent( QEvent* )
02758 {
02759   m_textHintTimer.stop();
02760 }
02761 
02762 KTextEditor::Cursor KateViewInternal::coordinatesToCursor(const QPoint& _coord) const
02763 {
02764   QPoint coord(_coord);
02765 
02766   KTextEditor::Cursor ret = KTextEditor::Cursor::invalid();
02767 
02768   coord.setX( coord.x() - m_leftBorder->width()  + startX() );
02769 
02770   const KateTextLayout& thisLine = yToKateTextLayout(coord.y());
02771   if (thisLine.isValid())
02772     ret = renderer()->xToCursor(thisLine, coord.x(), !view()->wrapCursor());
02773 
02774   return ret;
02775 }
02776 
02777 void KateViewInternal::mouseMoveEvent( QMouseEvent* e )
02778 {
02779   // FIXME only do this if needing to track mouse movement
02780   const KateTextLayout& thisLine = yToKateTextLayout(e->y());
02781   if (thisLine.isValid()) {
02782     KTextEditor::Cursor newPosition = renderer()->xToCursor(thisLine, e->x(), !view()->wrapCursor());
02783     if (newPosition != m_mouse) {
02784       m_mouse = newPosition;
02785       mouseMoved();
02786     }
02787   } else {
02788     if (m_mouse.isValid()) {
02789       m_mouse = KTextEditor::Cursor::invalid();
02790       mouseMoved();
02791     }
02792   }
02793 
02794   if( e->buttons() & Qt::LeftButton )
02795   {
02796     if (m_dragInfo.state == diPending)
02797     {
02798       // we had a mouse down, but haven't confirmed a drag yet
02799       // if the mouse has moved sufficiently, we will confirm
02800       QPoint p( e->pos() - m_dragInfo.start );
02801 
02802       // we've left the drag square, we can start a real drag operation now
02803       if( p.manhattanLength() > KGlobalSettings::dndEventDelay() )
02804         doDrag();
02805 
02806       return;
02807     }
02808     else if (m_dragInfo.state == diDragging)
02809     {
02810       // Don't do anything after a canceled drag until the user lets go of
02811       // the mouse button!
02812       return;
02813     }
02814 
02815     m_mouseX = e->x();
02816     m_mouseY = e->y();
02817 
02818     m_scrollX = 0;
02819     m_scrollY = 0;
02820     int d = renderer()->fontHeight();
02821 
02822     if (m_mouseX < 0)
02823       m_scrollX = -d;
02824 
02825     if (m_mouseX > width())
02826       m_scrollX = d;
02827 
02828     if (m_mouseY < 0)
02829     {
02830       m_mouseY = 0;
02831       m_scrollY = -d;
02832     }
02833 
02834     if (m_mouseY > height())
02835     {
02836       m_mouseY = height();
02837       m_scrollY = d;
02838     }
02839 
02840     placeCursor( QPoint( m_mouseX, m_mouseY ), true );
02841 
02842   }
02843   else
02844   {
02845     if (isTargetSelected( e->pos() ) ) {
02846       // mouse is over selected text. indicate that the text is draggable by setting
02847       // the arrow cursor as other Qt text editing widgets do
02848       if (m_mouseCursor != Qt::ArrowCursor) {
02849         m_mouseCursor = Qt::ArrowCursor;
02850         setCursor(m_mouseCursor);
02851       }
02852     } else {
02853       // normal text cursor
02854       if (m_mouseCursor != Qt::IBeamCursor) {
02855         m_mouseCursor = Qt::IBeamCursor;
02856         setCursor(m_mouseCursor);
02857       }
02858     }
02859     //We need to check whether the mouse position is actually within the widget,
02860     //because other widgets like the icon border forward their events to this,
02861     //and we will create invalid text hint requests if we don't check
02862     if (m_textHintEnabled && geometry().contains(parentWidget()->mapFromGlobal(e->globalPos())))
02863     {
02864        m_textHintTimer.start(m_textHintTimeout);
02865        m_textHintMouseX=e->x();
02866        m_textHintMouseY=e->y();
02867     }
02868   }
02869 }
02870 
02871 void KateViewInternal::updateDirty( )
02872 {
02873   uint h = renderer()->fontHeight();
02874 
02875   int currentRectStart = -1;
02876   int currentRectEnd = -1;
02877 
02878   QRegion updateRegion;
02879 
02880   {
02881     QMutexLocker lock(m_doc->smartMutex());
02882 
02883     for (int i = 0; i < cache()->viewCacheLineCount(); ++i) {
02884       if (cache()->viewLine(i).isDirty()) {
02885         if (currentRectStart == -1) {
02886           currentRectStart = h * i;
02887           currentRectEnd = h;
02888         } else {
02889           currentRectEnd += h;
02890         }
02891 
02892       } else if (currentRectStart != -1) {
02893         updateRegion += QRect(0, currentRectStart, width(), currentRectEnd);
02894         currentRectStart = -1;
02895         currentRectEnd = -1;
02896       }
02897     }
02898   }
02899   
02900 
02901   if (currentRectStart != -1)
02902     updateRegion += QRect(0, currentRectStart, width(), currentRectEnd);
02903 
02904   if (!updateRegion.isEmpty()) {
02905     if (debugPainting) kDebug( 13030 ) << k_funcinfo << "Update dirty region " << updateRegion;
02906     update(updateRegion);
02907   }
02908 }
02909 
02910 void KateViewInternal::hideEvent(QHideEvent* e)
02911 {
02912   Q_UNUSED(e);
02913   if(m_view->isCompletionActive())
02914     m_view->completionWidget()->abortCompletion();
02915 }
02916 
02917 void KateViewInternal::paintEvent(QPaintEvent *e)
02918 {
02919   QMutexLocker lock(m_doc->smartMutex());
02920 
02921   if (m_smartDirty)
02922     doUpdateView();
02923 
02924   if (debugPainting) kDebug (13030) << "GOT PAINT EVENT: Region" << e->region();
02925 
02926   const QRect& unionRect = e->rect();
02927 
02928   int xStart = startX() + unionRect.x();
02929   int xEnd = xStart + unionRect.width();
02930   uint h = renderer()->fontHeight();
02931   uint startz = (unionRect.y() / h);
02932   uint endz = startz + 1 + (unionRect.height() / h);
02933   uint lineRangesSize = cache()->viewCacheLineCount();
02934 
02935   QPainter paint(this);
02936   paint.setRenderHints (QPainter::Antialiasing);
02937 
02938   // TODO put in the proper places
02939   renderer()->setCaretStyle(m_view->isOverwriteMode() ? KateRenderer::Block : KateRenderer::Line);
02940   renderer()->setShowTabs(m_doc->config()->configFlags() & KateDocumentConfig::cfShowTabs);
02941   renderer()->setShowTrailingSpaces(m_doc->config()->configFlags() & KateDocumentConfig::cfShowSpaces);
02942 
02943   int sy = startz * h;
02944   paint.translate(unionRect.x(), startz * h);
02945 
02946   for (uint z=startz; z <= endz; z++)
02947   {
02948     if ( (z >= lineRangesSize) || (cache()->viewLine(z).line() == -1) )
02949     {
02950       if (!(z >= lineRangesSize))
02951         cache()->viewLine(z).setDirty(false);
02952 
02953       paint.fillRect( 0, 0, unionRect.width(), h, renderer()->config()->backgroundColor() );
02954     }
02955     else
02956     {
02957       //kDebug( 13030 )<<"KateViewInternal::paintEvent(QPaintEvent *e):cache()->viewLine"<<z;
02958       KateTextLayout& thisLine = cache()->viewLine(z);
02959 
02960       /* If viewLine() returns non-zero, then a document line was split
02961          in several visual lines, and we're trying to paint visual line
02962          that is not the first.  In that case, this line was already
02963          painted previously, since KateRenderer::paintTextLine paints
02964          all visual lines.
02965          Except if we're at the start of the region that needs to
02966          be painted -- when no previous calls to paintTextLine were made.  
02967       */         
02968       if (!thisLine.viewLine() || z == startz) {
02969         // Don't bother if we're not in the requested update region
02970         if (!e->region().contains(QRect(unionRect.x(), startz * h, unionRect.width(), h)))
02971           continue;
02972 
02973         //kDebug (13030) << "paint text: line: " << thisLine.line() << " viewLine " << thisLine.viewLine() << " x: " << unionRect.x() << " y: " << sy
02974         //  << " width: " << xEnd-xStart << " height: " << h << endl;
02975 
02976         if (thisLine.viewLine())
02977           paint.translate(QPoint(0, h * - thisLine.viewLine()));
02978 
02979         // The paintTextLine function should be well behaved, but if not, this clipping may be needed
02980         //paint.setClipRect(QRect(xStart, 0, xEnd - xStart, h * (thisLine.kateLineLayout()->viewLineCount())));
02981 
02982         renderer()->paintTextLine(paint, thisLine.kateLineLayout(), xStart, xEnd, &m_cursor);
02983 
02984         //paint.setClipping(false);
02985 
02986         if (thisLine.viewLine())
02987           paint.translate(0, h * thisLine.viewLine());
02988 
02989         thisLine.setDirty(false);
02990       }
02991     }
02992 
02993     paint.translate(0, h);
02994     sy += h;
02995   }
02996 }
02997 
02998 void KateViewInternal::resizeEvent(QResizeEvent* e)
02999 {
03000   bool expandedHorizontally = width() > e->oldSize().width();
03001   bool expandedVertically = height() > e->oldSize().height();
03002   bool heightChanged = height() != e->oldSize().height();
03003 
03004   m_madeVisible = false;
03005 
03006   if (heightChanged) {
03007     setAutoCenterLines(m_autoCenterLines, false);
03008     m_cachedMaxStartPos.setPosition(-1, -1);
03009   }
03010 
03011   if (m_view->dynWordWrap()) {
03012     bool dirtied = false;
03013 
03014     QMutexLocker lock(m_doc->smartMutex());
03015 
03016     for (int i = 0; i < cache()->viewCacheLineCount(); i++) {
03017       // find the first dirty line
03018       // the word wrap updateView algorithm is forced to check all lines after a dirty one
03019       bool lineNeedsRedraw = false;
03020       // If text is dynamically wrapped
03021       if (cache()->viewLine(i).wrap()) {
03022         lineNeedsRedraw = true;
03023       // If text is drawn right-to-left
03024       } else if (const KateLineLayoutPtr& line = cache()->viewLine(i).kateLineLayout()) {
03025         if (QTextLayout* layout = line->layout())
03026           if (layout->textOption().textDirection() == Qt::RightToLeft)
03027             lineNeedsRedraw = true;
03028       // If text would now be off the edge of the view
03029       } else if (!expandedHorizontally && (cache()->viewLine(i).endX() - cache()->viewLine(i).startX()) > width()) {
03030         lineNeedsRedraw = true;
03031       }
03032 
03033       if (lineNeedsRedraw) {
03034         dirtied = true;
03035         cache()->viewLine(i).setDirty();
03036         break;
03037       }
03038     }
03039 
03040     if (dirtied || heightChanged) {
03041       updateView(true);
03042       m_leftBorder->update();
03043     }
03044 
03045     if (width() < e->oldSize().width()) {
03046       if (!m_view->wrapCursor()) {
03047         // May have to restrain cursor to new smaller width...
03048         if (m_cursor.column() > m_doc->lineLength(m_cursor.line())) {
03049           KateTextLayout thisLine = currentLayout();
03050 
03051           KTextEditor::Cursor newCursor(m_cursor.line(), thisLine.endCol() + ((width() - thisLine.xOffset() - thisLine.width()) / renderer()->spaceWidth()) - 1);
03052           lock.unlock();
03053           updateCursor(newCursor);
03054           lock.relock();
03055         }
03056       }
03057     }
03058 
03059   } else {
03060     updateView();
03061 
03062     if (expandedHorizontally && startX() > 0)
03063       scrollColumns(startX() - (width() - e->oldSize().width()));
03064   }
03065 
03066   if (expandedVertically) {
03067     KTextEditor::Cursor max = maxStartPos();
03068     if (startPos() > max)
03069       scrollPos(max);
03070   }
03071 }
03072 
03073 void KateViewInternal::scrollTimeout ()
03074 {
03075   if (m_scrollX || m_scrollY)
03076   {
03077     scrollLines (startPos().line() + (m_scrollY / (int) renderer()->fontHeight()));
03078     placeCursor( QPoint( m_mouseX, m_mouseY ), true );
03079   }
03080 }
03081 
03082 void KateViewInternal::cursorTimeout ()
03083 {
03084   if (!debugPainting && !m_view->viInputMode()) {
03085     renderer()->setDrawCaret(!renderer()->drawCaret());
03086     paintCursor();
03087   }
03088 }
03089 
03090 void KateViewInternal::textHintTimeout ()
03091 {
03092   m_textHintTimer.stop ();
03093 
03094   KateTextLayout thisLine = yToKateTextLayout(m_textHintMouseY);
03095 
03096   if (!thisLine.isValid()) return;
03097 
03098   if (m_textHintMouseX> (lineMaxCursorX(thisLine) - thisLine.startX())) return;
03099 
03100   KTextEditor::Cursor c = thisLine.start();
03101   
03102   {
03103     QMutexLocker lock(m_doc->smartMutex());
03104     c = renderer()->xToCursor(cache()->textLayout(c), startX() + m_textHintMouseX, !view()->wrapCursor());
03105   }
03106 
03107   QString tmp;
03108 
03109   emit m_view->needTextHint(c, tmp);
03110 
03111   if (!tmp.isEmpty()) kDebug(13030)<<"Hint text: "<<tmp;
03112 }
03113 
03114 void KateViewInternal::focusInEvent (QFocusEvent *)
03115 {
03116   if (KApplication::cursorFlashTime() > 0)
03117     m_cursorTimer.start ( KApplication::cursorFlashTime() / 2 );
03118 
03119   paintCursor();
03120 
03121   m_doc->setActiveView( m_view );
03122 
03123   // this will handle focus stuff in kateview
03124   m_view->slotGotFocus ();
03125 }
03126 
03127 void KateViewInternal::focusOutEvent (QFocusEvent *)
03128 {
03129   //if (m_view->isCompletionActive())
03130     //m_view->abortCompletion();
03131 
03132   m_cursorTimer.stop();
03133   m_view->renderer()->setDrawCaret(true);
03134   paintCursor();
03135 
03136   m_textHintTimer.stop();
03137 
03138   m_view->slotLostFocus ();
03139 }
03140 
03141 void KateViewInternal::doDrag()
03142 {
03143   m_dragInfo.state = diDragging;
03144   m_dragInfo.dragObject = new QDrag(this);
03145   QMimeData *mimeData=new QMimeData();
03146   mimeData->setText(m_view->selectionText());
03147   m_dragInfo.dragObject->setMimeData(mimeData);
03148   m_dragInfo.dragObject->start(Qt::MoveAction);
03149 }
03150 
03151 void KateViewInternal::dragEnterEvent( QDragEnterEvent* event )
03152 {
03153   if (event->source()==this) event->setDropAction(Qt::MoveAction);
03154   event->setAccepted( (event->mimeData()->hasText() && m_doc->isReadWrite()) ||
03155                   KUrl::List::canDecode(event->mimeData()) );
03156 }
03157 
03158 void KateViewInternal::fixDropEvent(QDropEvent* event) {
03159   if (event->source()!=this) event->setDropAction(Qt::CopyAction);
03160   else {
03161     Qt::DropAction action=Qt::MoveAction;
03162 #ifdef Q_WS_MAC
03163     if(event->keyboardModifiers() & Qt::AltModifier)
03164         action = Qt::CopyAction;
03165 #else
03166     if (event->keyboardModifiers() & Qt::ControlModifier)
03167         action = Qt::CopyAction;
03168 #endif
03169     event->setDropAction(action);
03170   }
03171 }
03172 
03173 void KateViewInternal::dragMoveEvent( QDragMoveEvent* event )
03174 {
03175   // track the cursor to the current drop location
03176   placeCursor( event->pos(), true, false );
03177 
03178   // important: accept action to switch between copy and move mode
03179   // without this, the text will always be copied.
03180   fixDropEvent(event);
03181 }
03182 
03183 void KateViewInternal::dropEvent( QDropEvent* event )
03184 {
03185   if ( KUrl::List::canDecode(event->mimeData()) ) {
03186 
03187       emit dropEventPass(event);
03188 
03189   } else if ( event->mimeData()->hasText() && m_doc->isReadWrite() ) {
03190 
03191     QString text=event->mimeData()->text();
03192 
03193     // is the source our own document?
03194     bool priv = false;
03195     if (KateViewInternal* vi = qobject_cast<KateViewInternal*>(event->source()))
03196       priv = m_doc->ownedView( vi->m_view );
03197 
03198     // dropped on a text selection area?
03199     bool selected = m_view->cursorSelected(m_cursor);
03200 
03201     if( priv && selected ) {
03202       // this is a drag that we started and dropped on our selection
03203       // ignore this case
03204       return;
03205     }
03206 
03207     fixDropEvent(event);
03208 
03209     // fix the cursor position before editStart(), so that it is correctly
03210     // stored for the undo action
03211     KTextEditor::Cursor targetCursor(m_cursor); // backup current cursor
03212     if ( event->dropAction() != Qt::CopyAction ) {
03213       editSetCursor(m_view->selectionRange().end());
03214     } else {
03215       m_view->clearSelection();
03216     }
03217 
03218     // use one transaction
03219     m_doc->editStart ();
03220 
03221     // on move: remove selected text; on copy: duplicate text
03222     m_doc->insertText(targetCursor, text );
03223 
03224     KateSmartCursor startCursor(targetCursor,m_doc);
03225 
03226     if ( event->dropAction() != Qt::CopyAction )
03227       m_view->removeSelectedText();
03228 
03229     KateSmartCursor endCursor1(startCursor,m_doc);
03230     endCursor1.advance(text.length(),KTextEditor::SmartCursor::ByCharacter);
03231     KTextEditor::Cursor endCursor(endCursor1);
03232     kDebug( 13030 )<<startCursor<<"---("<<text.length()<<")---"<<endCursor;
03233     m_view->setSelection(KTextEditor::Range(startCursor,endCursor));
03234     editSetCursor(endCursor);
03235 
03236     m_doc->editEnd ();
03237 
03238     event->acceptProposedAction();
03239     updateView();
03240   }
03241 
03242   // finally finish drag and drop mode
03243   m_dragInfo.state = diNone;
03244   // important, because the eventFilter`s DragLeave does not occur
03245   stopDragScroll();
03246 }
03247 //END EVENT HANDLING STUFF
03248 
03249 void KateViewInternal::clear()
03250 {
03251   m_startPos = m_displayCursor = m_cursor = KTextEditor::Cursor(0, 0);
03252   updateView(true);
03253 }
03254 
03255 void KateViewInternal::wheelEvent(QWheelEvent* e)
03256 {
03257   if (m_lineScroll->minimum() != m_lineScroll->maximum() && e->orientation() != Qt::Horizontal) {
03258     // React to this as a vertical event
03259     if ( ( e->modifiers() & Qt::ControlModifier ) || ( e->modifiers() & Qt::ShiftModifier ) ) {
03260       if (e->delta() > 0)
03261         scrollPrevPage();
03262       else
03263         scrollNextPage();
03264     } else {
03265       scrollViewLines(-((e->delta() / 120) * QApplication::wheelScrollLines()));
03266     }
03267 
03268   } else if (columnScrollingPossible()) {
03269     QWheelEvent copy = *e;
03270     QApplication::sendEvent(m_columnScroll, &copy);
03271 
03272   } else {
03273     e->ignore();
03274   }
03275 }
03276 
03277 void KateViewInternal::startDragScroll()
03278 {
03279   if ( !m_dragScrollTimer.isActive() ) {
03280     m_dragScrollTimer.start( s_scrollTime );
03281   }
03282 }
03283 
03284 void KateViewInternal::stopDragScroll()
03285 {
03286   m_dragScrollTimer.stop();
03287   updateView();
03288 }
03289 
03290 void KateViewInternal::doDragScroll()
03291 {
03292   QPoint p = this->mapFromGlobal( QCursor::pos() );
03293 
03294   int dx = 0, dy = 0;
03295   if ( p.y() < s_scrollMargin ) {
03296     dy = p.y() - s_scrollMargin;
03297   } else if ( p.y() > height() - s_scrollMargin ) {
03298     dy = s_scrollMargin - (height() - p.y());
03299   }
03300 
03301   if ( p.x() < s_scrollMargin ) {
03302     dx = p.x() - s_scrollMargin;
03303   } else if ( p.x() > width() - s_scrollMargin ) {
03304     dx = s_scrollMargin - (width() - p.x());
03305   }
03306 
03307   dy /= 4;
03308 
03309   if (dy)
03310     scrollLines(startPos().line() + dy);
03311 
03312   if (columnScrollingPossible () && dx)
03313     scrollColumns(qMin (m_startX + dx, m_columnScroll->maximum()));
03314 
03315   if (!dy && !dx)
03316     stopDragScroll();
03317 }
03318 
03319 void KateViewInternal::enableTextHints(int timeout)
03320 {
03321   m_textHintTimeout=timeout;
03322   m_textHintEnabled=true;
03323   m_textHintTimer.start(timeout);
03324 }
03325 
03326 void KateViewInternal::disableTextHints()
03327 {
03328   m_textHintEnabled=false;
03329   m_textHintTimer.stop ();
03330 }
03331 
03332 //BEGIN EDIT STUFF
03333 void KateViewInternal::editStart()
03334 {
03335   editSessionNumber++;
03336 
03337   if (editSessionNumber > 1)
03338     return;
03339 
03340   editIsRunning = true;
03341   editOldCursor = m_cursor;
03342 }
03343 
03344 void KateViewInternal::editEnd(int editTagLineStart, int editTagLineEnd, bool tagFrom)
03345 {
03346    if (editSessionNumber == 0)
03347     return;
03348 
03349   editSessionNumber--;
03350 
03351   if (editSessionNumber > 0)
03352     return;
03353 
03354   if (tagFrom && (editTagLineStart <= int(m_doc->getRealLine(startLine()))))
03355     tagAll();
03356   else
03357     tagLines (editTagLineStart, tagFrom ? qMax(m_doc->lastLine() + 1, editTagLineEnd) : editTagLineEnd, true);
03358 
03359   if (editOldCursor == m_cursor)
03360     updateBracketMarks();
03361 
03362   updateView(true);
03363 
03364   if (editOldCursor != m_cursor || m_view == m_doc->activeView())
03365   {
03366     m_madeVisible = false;
03367     updateCursor ( m_cursor, true );
03368   }
03369 
03370   editIsRunning = false;
03371 }
03372 
03373 void KateViewInternal::editSetCursor (const KTextEditor::Cursor &_cursor)
03374 {
03375   if (m_cursor != _cursor)
03376   {
03377     m_cursor = _cursor;
03378   }
03379 }
03380 //END
03381 
03382 void KateViewInternal::viewSelectionChanged ()
03383 {
03384   if (!m_view->selection())
03385   {
03386     m_selectAnchor = KTextEditor::Cursor::invalid();
03387     // Do NOT nuke the entire range! The reason is that a shift+DC selection
03388     // might (correctly) set the range to be empty (i.e. start() == end()), and
03389     // subsequent dragging might shrink the selection into non-existence. When
03390     // this happens, we use the cached end to restore the cached start so that
03391     // updateSelection is not confused. See also comments in updateSelection.
03392     m_selectionCached.start() = KTextEditor::Cursor::invalid();
03393 //     updateView(true);
03394   }
03395 }
03396 
03397 KateLayoutCache* KateViewInternal::cache( ) const
03398 {
03399   return m_layoutCache;
03400 }
03401 
03402 KTextEditor::Cursor KateViewInternal::toRealCursor( const KTextEditor::Cursor & virtualCursor ) const
03403 {
03404   return KTextEditor::Cursor(m_doc->getRealLine(virtualCursor.line()), virtualCursor.column());
03405 }
03406 
03407 KTextEditor::Cursor KateViewInternal::toVirtualCursor( const KTextEditor::Cursor & realCursor ) const
03408 {
03409   return KTextEditor::Cursor(m_doc->getVirtualLine(realCursor.line()), realCursor.column());
03410 }
03411 
03412 KateRenderer * KateViewInternal::renderer( ) const
03413 {
03414   return m_view->renderer();
03415 }
03416 
03417 void KateViewInternal::dynamicHighlightAdded( KateSmartRange * range )
03418 {
03419   QMutexLocker lock(m_doc->smartMutex());
03420 
03421   DynamicRangeHL* hl = new DynamicRangeHL(range);
03422   hl->isView = view() == sender();
03423 
03424   m_dynamicHighlights.insert(range, hl);
03425 
03426   if (m_mouse.isValid())
03427     // Could be more efficient when there are several ranges around
03428     dynamicMoved(true);
03429 
03430   dynamicMoved(false);
03431 }
03432 
03433 void KateViewInternal::dynamicHighlightRemoved( KateSmartRange * range )
03434 {
03435   QMutexLocker lock(m_doc->smartMutex());
03436 
03437   removeWatcher(range, this);
03438 
03439   delete m_dynamicHighlights.take(range);
03440 }
03441 
03442 void KateViewInternal::rangeDeleted( KateSmartRange * range )
03443 {
03444   QMutexLocker lock(m_doc->smartMutex());
03445 
03446   if (m_dynamicHighlights.contains(range)) {
03447     delete m_dynamicHighlights.take(range);
03448     return;
03449   }
03450 
03451   foreach (DynamicRangeHL* hl, m_dynamicHighlights) {
03452     // FIXME if deletion signal was emitted in proper order, the hasParent hack would not be required
03453     if (hl->mouseAnimations.contains(range))
03454       delete hl->mouseAnimations.take(range);
03455 
03456     if (hl->mouseOver && (hl->mouseOver == range || hl->mouseOver->hasParent(range))) {
03457       hl->mouseOver = static_cast<KateSmartRange*>(range->parentRange());
03458     }
03459 
03460     if (hl->caretAnimations.contains(range))
03461       delete hl->caretAnimations.take(range);
03462 
03463     if (hl->caretOver && (hl->caretOver == range || hl->caretOver->hasParent(range))) {
03464       hl->caretOver = static_cast<KateSmartRange*>(range->parentRange());
03465     }
03466   }
03467 }
03468 
03469 void KateViewInternal::startDynamic( DynamicRangeHL* hl, KateSmartRange* range, KTextEditor::Attribute::ActivationType type )
03470 {
03471   QMutexLocker lock(m_doc->smartMutex());
03472 
03473   if (type == KTextEditor::Attribute::ActivateMouseIn)
03474     range->setMouseOver(true);
03475   else
03476     range->setCaretOver(true);
03477 
03478   if (!range->attribute() || !range->attribute()->dynamicAttribute(type))
03479     return;
03480 
03481   KateDynamicAnimation* anim;
03482   if (hl->isView)
03483     anim = new KateDynamicAnimation(view(), range, type);
03484   else
03485     anim = new KateDynamicAnimation(m_doc, range, type);
03486 
03487   connect(anim, SIGNAL(redraw(KateSmartRange*)), SLOT(updateRange(KateSmartRange*)));
03488 
03489   if (type == KTextEditor::Attribute::ActivateMouseIn)
03490     hl->mouseAnimations.insert(range, anim);
03491   else
03492     hl->caretAnimations.insert(range, anim);
03493 
03494   renderer()->dynamicRegion().addRange(range);
03495 }
03496 
03497 void KateViewInternal::endDynamic( DynamicRangeHL* hl, KateSmartRange* range, KTextEditor::Attribute::ActivationType type )
03498 {
03499   QMutexLocker lock(m_doc->smartMutex());
03500 
03501   if (type == KTextEditor::Attribute::ActivateMouseIn)
03502     range->setMouseOver(false);
03503   else
03504     range->setCaretOver(false);
03505 
03506   if (!range->attribute() || !range->attribute()->dynamicAttribute(type))
03507     return;
03508 
03509   KateDynamicAnimation* anim = 0L;
03510   if (type == KTextEditor::Attribute::ActivateMouseIn) {
03511     Q_ASSERT(hl->mouseAnimations.contains(range));
03512     anim = hl->mouseAnimations.take(range);
03513 
03514   } else {
03515     Q_ASSERT(hl->caretAnimations.contains(range));
03516     anim = hl->caretAnimations.take(range);
03517   }
03518 
03519   if (anim)
03520     anim->finish();
03521 
03522   // it deletes itself
03523   //delete anim;
03524 
03525   // The animation object does this on deletion
03526   // renderer()->dynamicRegion().removeRange(range);
03527 }
03528 
03529 void KateViewInternal::updateRange(KateSmartRange* range)
03530 {
03531   //kDebug( 13030 ) << *range;
03532   tagRange(*range, true);
03533   updateDirty();
03534 }
03535 
03536 void KateViewInternal::dynamicMoved( bool mouse )
03537 {
03538   QMutexLocker lock(m_doc->smartMutex());
03539 
03540   foreach (DynamicRangeHL* hl, m_dynamicHighlights) {
03541     QStack<KTextEditor::SmartRange*> enterStack, exitStack;
03542     KTextEditor::SmartRange* oldRange = mouse ? hl->mouseOver : hl->caretOver;
03543     KTextEditor::SmartRange* newRange;
03544     if (mouse)
03545       newRange = (hl->mouseOver ? hl->mouseOver : hl->top)->deepestRangeContaining(m_mouse, &enterStack, &exitStack);
03546     else
03547       newRange = (hl->caretOver ? hl->caretOver : hl->top)->deepestRangeContaining(m_cursor, &enterStack, &exitStack);
03548 
03549     if (newRange != oldRange) {
03550       if (newRange && !oldRange)
03551         enterStack.prepend(newRange);
03552 
03553       foreach (KTextEditor::SmartRange* exitedRange, exitStack) {
03554         endDynamic(hl, static_cast<KateSmartRange*>(exitedRange), mouse ? KTextEditor::Attribute::ActivateMouseIn : KTextEditor::Attribute::ActivateCaretIn);
03555         static_cast<KateSmartRange*>(exitedRange)->feedbackMouseCaretChange(m_view, mouse, false);
03556       }
03557 
03558       foreach (KTextEditor::SmartRange* enteredRange, enterStack) {
03559         static_cast<KateSmartRange*>(enteredRange)->feedbackMouseCaretChange(m_view, mouse, true);
03560         startDynamic(hl, static_cast<KateSmartRange*>(enteredRange), mouse ? KTextEditor::Attribute::ActivateMouseIn : KTextEditor::Attribute::ActivateCaretIn);
03561       }
03562 
03563       if (mouse)
03564         hl->mouseOver = static_cast<KateSmartRange*>(newRange);
03565       else
03566         hl->caretOver = static_cast<KateSmartRange*>(newRange);
03567     }
03568   }
03569 }
03570 
03571 void KateViewInternal::mouseMoved( )
03572 {
03573   view()->notifyMousePositionChanged(m_mouse);
03574 
03575   dynamicMoved(true);
03576 }
03577 
03578 KateViewInternal::DynamicRangeHL::DynamicRangeHL(KateSmartRange* _top)
03579   : top(_top)
03580   , isView(false)
03581   , caretOver(0L)
03582   , mouseOver(0L)
03583 {
03584 }
03585 
03586 KateViewInternal::DynamicRangeHL::~ DynamicRangeHL( )
03587 {
03588   qDeleteAll(caretAnimations);
03589   qDeleteAll(mouseAnimations);
03590 }
03591 
03592 void KateViewInternal::cursorMoved( )
03593 {
03594   dynamicMoved(false);
03595 }
03596 
03597 bool KateViewInternal::rangeAffectsView(const KTextEditor::Range& range) const
03598 {
03599   if(range.end().line() < m_startPos.line())
03600     return false;
03601   if(range.start().line() > m_startPos.line() + m_visibleLineCount)
03602     return false;
03603   
03604   return true;
03605 }
03606 
03607 void KateViewInternal::relayoutRange( const KTextEditor::Range & range, bool realCursors )
03608 {
03609   int startLine = realCursors ? range.start().line() : toRealCursor(range.start()).line();
03610   int endLine = realCursors ? range.end().line() : toRealCursor(range.end()).line();
03611 
03612 //   kDebug( 13030 )<<"KateViewInternal::relayoutRange(): startLine:"<<startLine<<" endLine:"<<endLine;
03613   cache()->relayoutLines(startLine, endLine);
03614 
03615   const KateSmartRange* krange = dynamic_cast<const KateSmartRange*>(&range);
03616   
03617   if (!m_smartDirty && (rangeAffectsView(range) ||
03618        (krange && rangeAffectsView(KTextEditor::Range(krange->kStart().lastPosition(), krange->kEnd().lastPosition()))))) {
03619     m_smartDirty = true;
03620     emit requestViewUpdateIfSmartDirty();
03621   }
03622 }
03623 
03624 void KateViewInternal::rangePositionChanged( KTextEditor::SmartRange * range )
03625 {
03626   // Try to retrieve old range position because that needs to be tagged too
03627   // FIXME not working yet...
03628   /*if (KateSmartRange* krange = dynamic_cast<KateSmartRange*>(range)) {
03629     KTextEditor::Range oldRange(krange->kStart().lastPosition(), krange->kEnd().lastPosition());
03630     relayoutRange(oldRange);
03631   }*/
03632 //   QMutexLocker lock(m_doc->smartMutex());
03633 
03634   if(range->attribute())
03635     relayoutRange(*range);
03636 }
03637 
03638 void KateViewInternal::rangeDeleted( KTextEditor::SmartRange * range )
03639 {
03640   QMutexLocker lock(m_doc->smartMutex());
03641   
03642   if(range->attribute())
03643     relayoutRange(*range);
03644 }
03645 
03646 void KateViewInternal::childRangeInserted( KTextEditor::SmartRange *, KTextEditor::SmartRange * child )
03647 {
03648   QMutexLocker lock(m_doc->smartMutex());
03649 
03650   if(child->attribute() || child->childRanges().count())
03651     relayoutRange(*child);
03652   
03653   addWatcher(child, this);
03654 }
03655 
03656 void KateViewInternal::rangeAttributeChanged( KTextEditor::SmartRange * range, KTextEditor::Attribute::Ptr currentAttribute, KTextEditor::Attribute::Ptr previousAttribute )
03657 {
03658   QMutexLocker lock(m_doc->smartMutex());
03659   
03660   if (currentAttribute != previousAttribute && !(currentAttribute && previousAttribute && *currentAttribute == *previousAttribute))
03661     relayoutRange(*range);
03662 }
03663 
03664 void KateViewInternal::childRangeRemoved( KTextEditor::SmartRange *, KTextEditor::SmartRange * child )
03665 {
03666   QMutexLocker lock(m_doc->smartMutex());
03667 
03668   if(child->attribute() || child->childRanges().count())
03669     relayoutRange(*child);
03670   removeWatcher(child, this);
03671 }
03672 
03673 void KateViewInternal::addHighlightRange(KTextEditor::SmartRange* range)
03674 {
03675   QMutexLocker lock(m_doc->smartMutex());
03676 
03677   relayoutRange(*range);
03678   ++m_watcherCount3;
03679   addWatcher(range, this);
03680 }
03681 
03682 void KateViewInternal::removeHighlightRange(KTextEditor::SmartRange* range)
03683 {
03684   QMutexLocker lock(m_doc->smartMutex());
03685 
03686   relayoutRange(*range);
03687   --m_watcherCount3;
03688   removeWatcher(range, this);
03689 }
03690 
03691 //BEGIN IM INPUT STUFF
03692 QVariant KateViewInternal::inputMethodQuery ( Qt::InputMethodQuery query ) const
03693 {
03694   switch (query) {
03695     case Qt::ImMicroFocus: {
03696       // Cursor placement code is changed for Asian input method that
03697       // shows candidate window. This behavior is same as Qt/E 2.3.7
03698       // which supports Asian input methods. Asian input methods need
03699       // start point of IM selection text to place candidate window as
03700       // adjacent to the selection text.
03701       KTextEditor::Cursor c = m_cursor;
03702       if (m_imPreedit)
03703         c = m_imPreedit->start();
03704       return QRect(cursorToCoordinate(c, true, false), QSize(0, renderer()->fontHeight()));
03705     }
03706 
03707     case Qt::ImFont:
03708       return renderer()->currentFont();
03709 
03710     case Qt::ImCursorPosition:
03711       if (m_imPreedit)
03712         return m_imPreedit->start().column();
03713       else
03714         return m_cursor.start().column();
03715 
03716     case Qt::ImSurroundingText:
03717       if (KateTextLine::Ptr l = m_doc->kateTextLine(m_cursor.line()))
03718         return l->string();
03719       else
03720         return QString();
03721 
03722     case Qt::ImCurrentSelection:
03723       if (view()->selection())
03724         return view()->selectionText();
03725       else
03726         return QString();
03727   }
03728 
03729   return QWidget::inputMethodQuery(query);
03730 }
03731 
03732 void KateViewInternal::inputMethodEvent(QInputMethodEvent* e)
03733 {
03734   if ( m_doc->readOnly() ) {
03735     e->ignore();
03736     return;
03737   }
03738 
03739   //kDebug( 13030 ) << "Event: cursor" << m_cursor << "commit" << e->commitString() << "preedit" << e->preeditString() << "replacement start" << e->replacementStart() << "length" << e->replacementLength();
03740 
03741   if ( m_view->selection() )
03742     m_view->removeSelectedText();
03743 
03744   bool createdPreedit = false;
03745   if (!m_imPreedit) {
03746     createdPreedit = true;
03747     m_imPreedit = m_view->doc()->smartManager()->newSmartRange(KTextEditor::Range(m_cursor, m_cursor), 0L, KTextEditor::SmartRange::ExpandLeft | KTextEditor::SmartRange::ExpandRight);
03748   }
03749 
03750   if (!m_imPreedit->isEmpty()) {
03751     m_view->doc()->inputMethodStart();
03752     m_view->doc()->removeText(*m_imPreedit);
03753     m_view->doc()->inputMethodEnd();
03754   }
03755 
03756   if (!e->commitString().isEmpty() || e->replacementLength()) {
03757     KTextEditor::Range preeditRange = *m_imPreedit;
03758 
03759     KTextEditor::Cursor start(m_imPreedit->start().line(), m_imPreedit->start().column() + e->replacementStart());
03760     KTextEditor::Cursor removeEnd = start + KTextEditor::Cursor(0, e->replacementLength());
03761 
03762     m_view->doc()->editStart(true);
03763     if (start != removeEnd)
03764       m_view->doc()->removeText(KTextEditor::Range(start, removeEnd));
03765     if (!e->commitString().isEmpty())
03766       m_view->doc()->insertText(start, e->commitString());
03767     m_view->doc()->editEnd();
03768 
03769     // Revert to the same range as above
03770     m_imPreedit->setRange(preeditRange);
03771   }
03772 
03773   if (!e->preeditString().isEmpty()) {
03774     m_view->doc()->inputMethodStart();
03775     m_view->doc()->insertText(m_imPreedit->start(), e->preeditString());
03776     m_view->doc()->inputMethodEnd();
03777     // The preedit range gets automatically repositioned
03778   }
03779 
03780   // Finished this input method context?
03781   if (m_imPreedit && e->preeditString().isEmpty()) {
03782     if (!createdPreedit)
03783       m_view->removeInternalHighlight(m_imPreedit);
03784 
03785     delete m_imPreedit;
03786     m_imPreedit = 0L;
03787 
03788     if ( KApplication::cursorFlashTime() > 0 )
03789       renderer()->setDrawCaret(false);
03790     renderer()->setCaretOverrideColor(QColor());
03791 
03792     return;
03793   }
03794 
03795   KTextEditor::Cursor newCursor = m_cursor;
03796   bool hideCursor = false;
03797   QColor caretColor;
03798 
03799   if (m_imPreedit) {
03800     m_imPreedit->clearAndDeleteChildRanges();
03801 
03802     int decorationColumn = 0;
03803     foreach (const QInputMethodEvent::Attribute &a, e->attributes()) {
03804       if (a.type == QInputMethodEvent::Cursor) {
03805         newCursor = m_imPreedit->start() + KTextEditor::Cursor(0, a.start);
03806         hideCursor = !a.length;
03807         QColor c = qvariant_cast<QColor>(a.value);
03808         if (c.isValid())
03809           caretColor = c;
03810 
03811       } else if (a.type == QInputMethodEvent::TextFormat) {
03812         QTextCharFormat f = qvariant_cast<QTextFormat>(a.value).toCharFormat();
03813         if (f.isValid() && decorationColumn <= a.start) {
03814           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);
03815           KTextEditor::SmartRange* formatRange = m_view->doc()->smartManager()->newSmartRange(fr, m_imPreedit);
03816           KTextEditor::Attribute::Ptr attribute(new KTextEditor::Attribute());
03817           attribute->merge(f);
03818           formatRange->setAttribute(attribute);
03819           decorationColumn = a.start + a.length;
03820         }
03821       }
03822     }
03823 
03824     if (createdPreedit)
03825       m_view->addInternalHighlight(m_imPreedit);
03826   }
03827 
03828   renderer()->setDrawCaret(hideCursor);
03829   renderer()->setCaretOverrideColor(caretColor);
03830 
03831   if (newCursor != m_cursor)
03832     updateCursor(newCursor);
03833 
03834   e->accept();
03835 }
03836 
03837 //END IM INPUT STUFF
03838 
03839 ViMode KateViewInternal::getCurrentViMode()
03840 {
03841   return getViInputModeManager()->getCurrentViMode();
03842 }
03843 
03844 KateViInputModeManager* KateViewInternal::getViInputModeManager()
03845 {
03846   if (!m_viInputModeManager) {
03847     m_viInputModeManager = new KateViInputModeManager(m_view, this);
03848   }
03849 
03850   return m_viInputModeManager;
03851 }
03852 
03853 // 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