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