kate Library API Documentation

kateviewinternal.cpp

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,2003 Hamish Rodda <rodda@kde.org>
00006    Copyright (C) 2003 Anakim Border <aborder@sources.sourceforge.net>
00007 
00008    Based on:
00009      KWriteView : Copyright (C) 1999 Jochen Wilhelmy <digisnap@cs.tu-berlin.de>
00010 
00011    This library is free software; you can redistribute it and/or
00012    modify it under the terms of the GNU Library General Public
00013    License version 2 as published by the Free Software Foundation.
00014 
00015    This library is distributed in the hope that it will be useful,
00016    but WITHOUT ANY WARRANTY; without even the implied warranty of
00017    MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
00018    Library General Public License for more details.
00019 
00020    You should have received a copy of the GNU Library General Public License
00021    along with this library; see the file COPYING.LIB.  If not, write to
00022    the Free Software Foundation, Inc., 59 Temple Place - Suite 330,
00023    Boston, MA 02111-1307, USA.
00024 */
00025 
00026 #include "kateviewinternal.h"
00027 #include "kateviewinternal.moc"
00028 
00029 #include "kateview.h"
00030 #include "katedocument.h"
00031 #include "katecodefoldinghelpers.h"
00032 #include "kateviewhelpers.h"
00033 #include "katehighlight.h"
00034 #include "katesupercursor.h"
00035 #include "katerenderer.h"
00036 #include "katecodecompletion.h"
00037 #include "kateconfig.h"
00038 
00039 #include <kcursor.h>
00040 #include <kdebug.h>
00041 #include <kapplication.h>
00042 #include <kglobalsettings.h>
00043 #include <kurldrag.h>
00044 
00045 #include <qstyle.h>
00046 #include <qdragobject.h>
00047 #include <qpopupmenu.h>
00048 #include <qdropsite.h>
00049 #include <qpainter.h>
00050 #include <qlayout.h>
00051 #include <qclipboard.h>
00052 #include <qpixmap.h>
00053 #include <qvbox.h>
00054 
00055 KateViewInternal::KateViewInternal(KateView *view, KateDocument *doc)
00056   : QWidget (view, "", Qt::WStaticContents | Qt::WRepaintNoErase | Qt::WResizeNoErase )
00057   , editSessionNumber (0)
00058   , editIsRunning (false)
00059   , m_view (view)
00060   , m_doc (doc)
00061   , cursor (doc, true, 0, 0, this)
00062   , possibleTripleClick (false)
00063   , m_dummy (0)
00064   , m_startPos(0,0)
00065   , m_oldStartPos(0,0)
00066   , m_madeVisible(false)
00067   , m_shiftKeyPressed (false)
00068   , m_autoCenterLines (false)
00069   , m_columnScrollDisplayed(false)
00070   , m_selChangedByUser (false)
00071   , selectAnchor (-1, -1)
00072   , m_preserveMaxX(false)
00073   , m_currentMaxX(0)
00074   , m_usePlainLines(false)
00075   , m_updatingView(true)
00076   , m_cachedMaxStartPos(-1, -1)
00077   , m_dragScrollTimer(this)
00078   , m_scrollTimer (this)
00079   , m_cursorTimer (this)
00080   , m_textHintTimer (this)
00081   , m_suppressColumnScrollBar(false)
00082   , m_textHintEnabled(false)
00083   , m_textHintMouseX(-1)
00084   , m_textHintMouseY(-1)
00085   , m_imPreeditStartLine(0)
00086   , m_imPreeditStart(0)
00087   , m_imPreeditLength(0)
00088   , m_imPreeditSelStart(0)
00089 {
00090   setMinimumSize (0,0);
00091 
00092   // cursor
00093   cursor.setMoveOnInsert (true);
00094 
00095   // invalidate selStartCached, or keyb selection is screwed initially
00096   selStartCached.setLine( -1 );
00097   //
00098   // scrollbar for lines
00099   //
00100   m_lineScroll = new KateScrollBar(QScrollBar::Vertical, this);
00101   m_lineScroll->show();
00102   m_lineScroll->setTracking (true);
00103 
00104   m_lineLayout = new QVBoxLayout();
00105   m_colLayout = new QHBoxLayout();
00106 
00107   m_colLayout->addWidget(m_lineScroll);
00108   m_lineLayout->addLayout(m_colLayout);
00109 
00110   if (!m_view->dynWordWrap())
00111   {
00112     // bottom corner box
00113     m_dummy = new QWidget(m_view);
00114     m_dummy->setFixedHeight(style().scrollBarExtent().width());
00115     m_dummy->show();
00116     m_lineLayout->addWidget(m_dummy);
00117   }
00118 
00119   // Hijack the line scroller's controls, so we can scroll nicely for word-wrap
00120   connect(m_lineScroll, SIGNAL(prevPage()), SLOT(scrollPrevPage()));
00121   connect(m_lineScroll, SIGNAL(nextPage()), SLOT(scrollNextPage()));
00122 
00123   connect(m_lineScroll, SIGNAL(prevLine()), SLOT(scrollPrevLine()));
00124   connect(m_lineScroll, SIGNAL(nextLine()), SLOT(scrollNextLine()));
00125 
00126   connect(m_lineScroll, SIGNAL(sliderMoved(int)), SLOT(scrollLines(int)));
00127   connect(m_lineScroll, SIGNAL(sliderMMBMoved(int)), SLOT(scrollLines(int)));
00128 
00129   // catch wheel events, completing the hijack
00130   m_lineScroll->installEventFilter(this);
00131 
00132   //
00133   // scrollbar for columns
00134   //
00135   m_columnScroll = new QScrollBar(QScrollBar::Horizontal,m_view);
00136   m_columnScroll->hide();
00137   m_columnScroll->setTracking(true);
00138   m_startX = 0;
00139   m_oldStartX = 0;
00140 
00141   connect( m_columnScroll, SIGNAL( valueChanged (int) ),
00142            this, SLOT( scrollColumns (int) ) );
00143 
00144   //
00145   // iconborder ;)
00146   //
00147   leftBorder = new KateIconBorder( this, m_view );
00148   leftBorder->show ();
00149 
00150   connect( leftBorder, SIGNAL(toggleRegionVisibility(unsigned int)),
00151            m_doc->foldingTree(), SLOT(toggleRegionVisibility(unsigned int)));
00152 
00153   connect( doc->foldingTree(), SIGNAL(regionVisibilityChangedAt(unsigned int)),
00154            this, SLOT(slotRegionVisibilityChangedAt(unsigned int)));
00155   connect( doc, SIGNAL(codeFoldingUpdated()),
00156            this, SLOT(slotCodeFoldingChanged()) );
00157 
00158   displayCursor.setPos(0, 0);
00159   cursor.setPos(0, 0);
00160   cXPos = 0;
00161 
00162   setAcceptDrops( true );
00163   setBackgroundMode( NoBackground );
00164 
00165   // event filter
00166   installEventFilter(this);
00167 
00168   // im
00169   setInputMethodEnabled(true);
00170 
00171   // set cursor
00172   setCursor( KCursor::ibeamCursor() );
00173 
00174   dragInfo.state = diNone;
00175 
00176   // timers
00177   connect( &m_dragScrollTimer, SIGNAL( timeout() ),
00178              this, SLOT( doDragScroll() ) );
00179 
00180   connect( &m_scrollTimer, SIGNAL( timeout() ),
00181              this, SLOT( scrollTimeout() ) );
00182 
00183   connect( &m_cursorTimer, SIGNAL( timeout() ),
00184              this, SLOT( cursorTimeout() ) );
00185 
00186   connect( &m_textHintTimer, SIGNAL( timeout() ),
00187              this, SLOT( textHintTimeout() ) );
00188 
00189   // selection changed to set anchor
00190   connect( m_doc, SIGNAL( selectionChanged() ),
00191              this, SLOT( docSelectionChanged() ) );
00192 
00193 
00194 // this is a work arround for RTL desktops
00195 // should be changed in kde 3.3
00196 // BTW: this comment has been "ported" from 3.1.X tree
00197 //      any hacker with BIDI knowlege is welcomed to fix kate problems :)
00198   if (QApplication::reverseLayout()){
00199       m_view->m_grid->addMultiCellWidget(leftBorder,     0, 1, 2, 2);
00200       m_view->m_grid->addMultiCellWidget(m_columnScroll, 1, 1, 0, 1);
00201       m_view->m_grid->addMultiCellLayout(m_lineLayout, 0, 0, 0, 0);
00202   }
00203   else{
00204       m_view->m_grid->addMultiCellLayout(m_lineLayout, 0, 1, 2, 2);
00205       m_view->m_grid->addMultiCellWidget(m_columnScroll, 1, 1, 0, 1);
00206       m_view->m_grid->addWidget(leftBorder, 0, 0);
00207   }
00208 
00209   updateView ();
00210 }
00211 
00212 KateViewInternal::~KateViewInternal ()
00213 {
00214 }
00215 
00216 void KateViewInternal::prepareForDynWrapChange()
00217 {
00218   // Which is the current view line?
00219   m_wrapChangeViewLine = displayViewLine(displayCursor, true);
00220 }
00221 
00222 void KateViewInternal::dynWrapChanged()
00223 {
00224   if (m_view->dynWordWrap())
00225   {
00226     delete m_dummy;
00227     m_dummy = 0;
00228     m_columnScroll->hide();
00229     m_columnScrollDisplayed = false;
00230 
00231   }
00232   else
00233   {
00234     // bottom corner box
00235     m_dummy = new QWidget(m_view);
00236     m_dummy->setFixedSize( style().scrollBarExtent().width(),
00237                                   style().scrollBarExtent().width() );
00238     m_dummy->show();
00239     m_lineLayout->addWidget(m_dummy);
00240   }
00241 
00242   tagAll();
00243   updateView();
00244 
00245   if (m_view->dynWordWrap())
00246     scrollColumns(0);
00247 
00248   // Determine where the cursor should be to get the cursor on the same view line
00249   if (m_wrapChangeViewLine != -1) {
00250     KateTextCursor newStart = viewLineOffset(displayCursor, -m_wrapChangeViewLine);
00251 
00252     // Account for the scrollbar in non-dyn-word-wrap mode
00253     if (!m_view->dynWordWrap() && scrollbarVisible(newStart.line())) {
00254       int lines = linesDisplayed() - 1;
00255 
00256       if (m_view->height() != height())
00257         lines++;
00258 
00259       if (newStart.line() + lines == displayCursor.line())
00260         newStart = viewLineOffset(displayCursor, 1 - m_wrapChangeViewLine);
00261     }
00262 
00263     makeVisible(newStart, newStart.col(), true);
00264 
00265   } else {
00266     update();
00267   }
00268 }
00269 
00270 KateTextCursor KateViewInternal::endPos() const
00271 {
00272   int viewLines = linesDisplayed() - 1;
00273 
00274   if (viewLines < 0) {
00275     kdDebug(13030) << "WARNING: viewLines wrong!" << endl;
00276     viewLines = 0;
00277   }
00278 
00279   // Check to make sure that lineRanges isn't invalid
00280   if (!lineRanges.count() || lineRanges[0].line == -1 || viewLines >= (int)lineRanges.count()) {
00281     // Switch off use of the cache
00282     return KateTextCursor(m_doc->numVisLines() - 1, m_doc->lineLength(m_doc->getRealLine(m_doc->numVisLines() - 1)));
00283   }
00284 
00285   for (int i = viewLines; i >= 0; i--) {
00286     KateLineRange& thisRange = lineRanges[i];
00287 
00288     if (thisRange.line == -1) continue;
00289 
00290     if (thisRange.virtualLine >= (int)m_doc->numVisLines()) {
00291       // Cache is too out of date
00292       return KateTextCursor(m_doc->numVisLines() - 1, m_doc->lineLength(m_doc->getRealLine(m_doc->numVisLines() - 1)));
00293     }
00294 
00295     return KateTextCursor(thisRange.virtualLine, thisRange.wrap ? thisRange.endCol - 1 : thisRange.endCol);
00296   }
00297 
00298   Q_ASSERT(false);
00299   kdDebug(13030) << "WARNING: could not find a lineRange at all" << endl;
00300   return KateTextCursor(-1, -1);
00301 }
00302 
00303 uint KateViewInternal::endLine() const
00304 {
00305   return endPos().line();
00306 }
00307 
00308 KateLineRange KateViewInternal::yToKateLineRange(uint y) const
00309 {
00310   return lineRanges[y / m_view->renderer()->fontHeight()];
00311 }
00312 
00313 int KateViewInternal::lineToY(uint viewLine) const
00314 {
00315   return (viewLine-startLine()) * m_view->renderer()->fontHeight();
00316 }
00317 
00318 void KateViewInternal::slotIncFontSizes()
00319 {
00320   m_view->renderer()->increaseFontSizes();
00321 }
00322 
00323 void KateViewInternal::slotDecFontSizes()
00324 {
00325   m_view->renderer()->decreaseFontSizes();
00326 }
00327 
00331 void KateViewInternal::scrollLines ( int line )
00332 {
00333   KateTextCursor newPos(line, 0);
00334   scrollPos(newPos);
00335 }
00336 
00337 // This can scroll less than one true line
00338 void KateViewInternal::scrollViewLines(int offset)
00339 {
00340   KateTextCursor c = viewLineOffset(startPos(), offset);
00341   scrollPos(c);
00342 
00343   m_lineScroll->blockSignals(true);
00344   m_lineScroll->setValue(startLine());
00345   m_lineScroll->blockSignals(false);
00346 }
00347 
00348 void KateViewInternal::scrollNextPage()
00349 {
00350   scrollViewLines(QMAX( linesDisplayed() - 1, 0 ));
00351 }
00352 
00353 void KateViewInternal::scrollPrevPage()
00354 {
00355   scrollViewLines(-QMAX( linesDisplayed() - 1, 0 ));
00356 }
00357 
00358 void KateViewInternal::scrollPrevLine()
00359 {
00360   scrollViewLines(-1);
00361 }
00362 
00363 void KateViewInternal::scrollNextLine()
00364 {
00365   scrollViewLines(1);
00366 }
00367 
00368 KateTextCursor KateViewInternal::maxStartPos(bool changed)
00369 {
00370   m_usePlainLines = true;
00371 
00372   if (m_cachedMaxStartPos.line() == -1 || changed)
00373   {
00374     KateTextCursor end(m_doc->numVisLines() - 1, m_doc->lineLength(m_doc->getRealLine(m_doc->numVisLines() - 1)));
00375 
00376     m_cachedMaxStartPos = viewLineOffset(end, -(linesDisplayed() - 1));
00377   }
00378 
00379   // If we're not dynamic word-wrapping, the horizontal scrollbar is hidden and will appear, increment the maxStart by 1
00380   if (!m_view->dynWordWrap() && m_columnScroll->isHidden() && scrollbarVisible(m_cachedMaxStartPos.line()))
00381   {
00382     KateTextCursor end(m_doc->numVisLines() - 1, m_doc->lineLength(m_doc->getRealLine(m_doc->numVisLines() - 1)));
00383 
00384     return viewLineOffset(end, -linesDisplayed());
00385   }
00386 
00387   m_usePlainLines = false;
00388 
00389   return m_cachedMaxStartPos;
00390 }
00391 
00392 // c is a virtual cursor
00393 void KateViewInternal::scrollPos(KateTextCursor& c, bool force, bool calledExternally)
00394 {
00395   if (!force && ((!m_view->dynWordWrap() && c.line() == (int)startLine()) || c == startPos()))
00396     return;
00397 
00398   if (c.line() < 0)
00399     c.setLine(0);
00400 
00401   KateTextCursor limit = maxStartPos();
00402   if (c > limit) {
00403     c = limit;
00404 
00405     // overloading this variable, it's not used in non-word wrap
00406     // used to set the lineScroll to the max value
00407     if (m_view->dynWordWrap())
00408       m_suppressColumnScrollBar = true;
00409 
00410     // Re-check we're not just scrolling to the same place
00411     if (!force && ((!m_view->dynWordWrap() && c.line() == (int)startLine()) || c == startPos()))
00412       return;
00413   }
00414 
00415   int viewLinesScrolled = displayViewLine(c);
00416 
00417   m_oldStartPos = m_startPos;
00418   m_startPos = c;
00419 
00420   // set false here but reversed if we return to makeVisible
00421   m_madeVisible = false;
00422 
00423   if (!force) {
00424     int lines = linesDisplayed();
00425     if ((int)m_doc->numVisLines() < lines) {
00426       KateTextCursor end(m_doc->numVisLines() - 1, m_doc->lineLength(m_doc->getRealLine(m_doc->numVisLines() - 1)));
00427       lines = QMIN((int)linesDisplayed(), displayViewLine(end) + 1);
00428     }
00429 
00430     Q_ASSERT(lines >= 0);
00431 
00432     if (!calledExternally && QABS(viewLinesScrolled) < lines)
00433     {
00434       updateView(false, viewLinesScrolled);
00435 
00436       int scrollHeight = -(viewLinesScrolled * m_view->renderer()->fontHeight());
00437       int scrollbarWidth = style().scrollBarExtent().width();
00438 
00439       //
00440       // updates are for working around the scrollbar leaving blocks in the view
00441       //
00442       scroll(0, scrollHeight);
00443       update(0, height()+scrollHeight-scrollbarWidth, width(), 2*scrollbarWidth);
00444 
00445       leftBorder->scroll(0, scrollHeight);
00446       leftBorder->update(0, leftBorder->height()+scrollHeight-scrollbarWidth, leftBorder->width(), 2*scrollbarWidth);
00447 
00448       return;
00449     }
00450   }
00451 
00452   updateView();
00453   update();
00454   leftBorder->update();
00455 }
00456 
00457 void KateViewInternal::scrollColumns ( int x )
00458 {
00459   if (x == m_startX)
00460     return;
00461 
00462   if (x < 0)
00463     x = 0;
00464 
00465   int dx = m_startX - x;
00466   m_oldStartX = m_startX;
00467   m_startX = x;
00468 
00469   if (QABS(dx) < width())
00470     scroll(dx, 0);
00471   else
00472     update();
00473 
00474   m_columnScroll->blockSignals(true);
00475   m_columnScroll->setValue(m_startX);
00476   m_columnScroll->blockSignals(false);
00477 }
00478 
00479 // If changed is true, the lines that have been set dirty have been updated.
00480 void KateViewInternal::updateView(bool changed, int viewLinesScrolled)
00481 {
00482   m_updatingView = true;
00483 
00484   uint contentLines = m_doc->visibleLines();
00485 
00486   m_lineScroll->blockSignals(true);
00487 
00488   KateTextCursor maxStart = maxStartPos(changed);
00489   int maxLineScrollRange = maxStart.line();
00490   if (m_view->dynWordWrap() && maxStart.col() != 0)
00491     maxLineScrollRange++;
00492   m_lineScroll->setRange(0, maxLineScrollRange);
00493 
00494   if (m_view->dynWordWrap() && m_suppressColumnScrollBar) {
00495     m_suppressColumnScrollBar = false;
00496     m_lineScroll->setValue(maxStart.line());
00497   } else {
00498     m_lineScroll->setValue(startPos().line());
00499   }
00500   m_lineScroll->setSteps(1, height() / m_view->renderer()->fontHeight());
00501   m_lineScroll->blockSignals(false);
00502 
00503   uint oldSize = lineRanges.size ();
00504   uint newSize = (height() / m_view->renderer()->fontHeight()) + 1;
00505   if (oldSize != newSize) {
00506     lineRanges.resize((height() / m_view->renderer()->fontHeight()) + 1);
00507     if (newSize > oldSize) {
00508       static KateLineRange blank;
00509       for (uint i = oldSize; i < newSize; i++) {
00510         lineRanges[i] = blank;
00511       }
00512     }
00513   }
00514 
00515   if (oldSize < lineRanges.size ())
00516   {
00517     for (uint i=oldSize; i < lineRanges.size(); i++)
00518       lineRanges[i].dirty = true;
00519   }
00520 
00521   // Move the lineRanges data if we've just scrolled...
00522   if (viewLinesScrolled != 0) {
00523     // loop backwards if we've just scrolled up...
00524     bool forwards = viewLinesScrolled >= 0 ? true : false;
00525     for (uint z = forwards ? 0 : lineRanges.count() - 1; z < lineRanges.count(); forwards ? z++ : z--) {
00526       uint oldZ = z + viewLinesScrolled;
00527       if (oldZ < lineRanges.count()) {
00528         lineRanges[z] = lineRanges[oldZ];
00529       } else {
00530         lineRanges[z].dirty = true;
00531       }
00532     }
00533   }
00534 
00535   if (m_view->dynWordWrap())
00536   {
00537     KateTextCursor realStart = startPos();
00538     realStart.setLine(m_doc->getRealLine(realStart.line()));
00539 
00540     KateLineRange startRange = range(realStart);
00541     uint line = startRange.virtualLine;
00542     int realLine = startRange.line;
00543     uint oldLine = line;
00544     int startCol = startRange.startCol;
00545     int startX = startRange.startX;
00546     int endX = startRange.startX;
00547     int shiftX = startRange.startCol ? startRange.shiftX : 0;
00548     bool wrap = false;
00549     int newViewLine = startRange.viewLine;
00550     // z is the current display view line
00551     KateTextLine::Ptr text = textLine(realLine);
00552 
00553     bool alreadyDirty = false;
00554 
00555     for (uint z = 0; z < lineRanges.size(); z++)
00556     {
00557       if (oldLine != line) {
00558         realLine = (int)m_doc->getRealLine(line);
00559 
00560         if (z)
00561           lineRanges[z-1].startsInvisibleBlock = (realLine != lineRanges[z-1].line + 1);
00562 
00563         text = textLine(realLine);
00564         startCol = 0;
00565         startX = 0;
00566         endX = 0;
00567         shiftX = 0;
00568         newViewLine = 0;
00569         oldLine = line;
00570       }
00571 
00572       if (line >= contentLines || !text)
00573       {
00574         if (lineRanges[z].line != -1)
00575           lineRanges[z].dirty = true;
00576 
00577         lineRanges[z].clear();
00578 
00579         line++;
00580       }
00581       else
00582       {
00583         if (lineRanges[z].line != realLine || lineRanges[z].startCol != startCol)
00584           alreadyDirty = lineRanges[z].dirty = true;
00585 
00586         if (lineRanges[z].dirty || changed || alreadyDirty) {
00587           alreadyDirty = true;
00588 
00589           lineRanges[z].virtualLine = line;
00590           lineRanges[z].line = realLine;
00591           lineRanges[z].startsInvisibleBlock = false;
00592 
00593           int tempEndX = 0;
00594 
00595           int endCol = m_view->renderer()->textWidth(text, startCol, width() - shiftX, &wrap, &tempEndX);
00596 
00597           endX += tempEndX;
00598 
00599           if (wrap)
00600           {
00601             if (m_view->config()->dynWordWrapAlignIndent() > 0)
00602             {
00603               if (startX == 0)
00604               {
00605                 int pos = text->nextNonSpaceChar(0);
00606 
00607                 if (pos > 0)
00608                   shiftX = m_view->renderer()->textWidth(text, pos);
00609 
00610                 if (shiftX > ((double)width() / 100 * m_view->config()->dynWordWrapAlignIndent()))
00611                   shiftX = 0;
00612               }
00613             }
00614 
00615             if ((lineRanges[z].startX != startX) || (lineRanges[z].endX != endX) ||
00616                 (lineRanges[z].startCol != startCol) || (lineRanges[z].endCol != endCol) ||
00617                 (lineRanges[z].shiftX != shiftX))
00618               lineRanges[z].dirty = true;
00619 
00620             lineRanges[z].startCol = startCol;
00621             lineRanges[z].endCol = endCol;
00622             lineRanges[z].startX = startX;
00623             lineRanges[z].endX = endX;
00624             lineRanges[z].viewLine = newViewLine;
00625             lineRanges[z].wrap = true;
00626 
00627             startCol = endCol;
00628             startX = endX;
00629           }
00630           else
00631           {
00632             if ((lineRanges[z].startX != startX) || (lineRanges[z].endX != endX) ||
00633                 (lineRanges[z].startCol != startCol) || (lineRanges[z].endCol != endCol))
00634               lineRanges[z].dirty = true;
00635 
00636             lineRanges[z].startCol = startCol;
00637             lineRanges[z].endCol = endCol;
00638             lineRanges[z].startX = startX;
00639             lineRanges[z].endX = endX;
00640             lineRanges[z].viewLine = newViewLine;
00641             lineRanges[z].wrap = false;
00642 
00643             line++;
00644           }
00645 
00646           lineRanges[z].shiftX = shiftX;
00647 
00648         } else {
00649           // The cached data is still intact
00650           if (lineRanges[z].wrap) {
00651             startCol = lineRanges[z].endCol;
00652             startX = lineRanges[z].endX;
00653             endX = lineRanges[z].endX;
00654           } else {
00655             line++;
00656           }
00657           shiftX = lineRanges[z].shiftX;
00658         }
00659       }
00660       newViewLine++;
00661     }
00662   }
00663   else
00664   {
00665     uint z = 0;
00666 
00667     for(; (z + startLine() < contentLines) && (z < lineRanges.size()); z++)
00668     {
00669       if (lineRanges[z].dirty || lineRanges[z].line != (int)m_doc->getRealLine(z + startLine())) {
00670         lineRanges[z].dirty = true;
00671 
00672         lineRanges[z].line = m_doc->getRealLine( z + startLine() );
00673         if (z)
00674           lineRanges[z-1].startsInvisibleBlock = (lineRanges[z].line != lineRanges[z-1].line + 1);
00675 
00676         lineRanges[z].virtualLine = z + startLine();
00677         lineRanges[z].startCol = 0;
00678         lineRanges[z].endCol = m_doc->lineLength(lineRanges[z].line);
00679         lineRanges[z].startX = 0;
00680         lineRanges[z].endX = m_view->renderer()->textWidth( textLine( lineRanges[z].line ), -1 );
00681         lineRanges[z].shiftX = 0;
00682         lineRanges[z].viewLine = 0;
00683         lineRanges[z].wrap = false;
00684       }
00685       else if (z && lineRanges[z-1].dirty)
00686       {
00687         lineRanges[z-1].startsInvisibleBlock = (lineRanges[z].line != lineRanges[z-1].line + 1);
00688       }
00689     }
00690 
00691     for (; z < lineRanges.size(); z++)
00692     {
00693       if (lineRanges[z].line != -1)
00694         lineRanges[z].dirty = true;
00695 
00696       lineRanges[z].clear();
00697     }
00698 
00699     if (scrollbarVisible(startLine()))
00700     {
00701       m_columnScroll->blockSignals(true);
00702 
00703       int max = maxLen(startLine()) - width();
00704       if (max < 0)
00705         max = 0;
00706 
00707       m_columnScroll->setRange(0, max);
00708 
00709       m_columnScroll->setValue(m_startX);
00710 
00711       // Approximate linescroll
00712       m_columnScroll->setSteps(m_view->renderer()->config()->fontMetrics()->width('a'), width());
00713 
00714       m_columnScroll->blockSignals(false);
00715 
00716       if (!m_columnScroll->isVisible ()  && !m_suppressColumnScrollBar)
00717       {
00718         m_columnScroll->show();
00719         m_columnScrollDisplayed = true;
00720       }
00721     }
00722     else if (m_columnScroll->isVisible () && !m_suppressColumnScrollBar && (startX() == 0))
00723     {
00724       m_columnScroll->hide();
00725       m_columnScrollDisplayed = false;
00726     }
00727   }
00728 
00729   m_updatingView = false;
00730 
00731   if (changed)
00732     paintText(0, 0, width(), height(), true);
00733 }
00734 
00735 void KateViewInternal::paintText (int x, int y, int width, int height, bool paintOnlyDirty)
00736 {
00737   //kdDebug() << k_funcinfo << x << " " << y << " " << width << " " << height << " " << paintOnlyDirty << endl;
00738   int xStart = startX() + x;
00739   int xEnd = xStart + width;
00740   uint h = m_view->renderer()->fontHeight();
00741   uint startz = (y / h);
00742   uint endz = startz + 1 + (height / h);
00743   uint lineRangesSize = lineRanges.size();
00744 
00745   static QPixmap drawBuffer;
00746 
00747   if (drawBuffer.width() < KateViewInternal::width() || drawBuffer.height() < (int)h)
00748     drawBuffer.resize(KateViewInternal::width(), (int)h);
00749 
00750   if (drawBuffer.isNull())
00751     return;
00752 
00753   QPainter paint(this);
00754   QPainter paintDrawBuffer(&drawBuffer);
00755 
00756   // TODO put in the proper places
00757   m_view->renderer()->setCaretStyle(m_view->isOverwriteMode() ? KateRenderer::Replace : KateRenderer::Insert);
00758   m_view->renderer()->setShowTabs(m_doc->configFlags() & KateDocument::cfShowTabs);
00759 
00760   for (uint z=startz; z <= endz; z++)
00761   {
00762     if ( (z >= lineRangesSize) || ((lineRanges[z].line == -1) && (!paintOnlyDirty || lineRanges[z].dirty)) )
00763     {
00764       if (!(z >= lineRangesSize))
00765         lineRanges[z].dirty = false;
00766 
00767       paint.fillRect( x, z * h, width, h, m_view->renderer()->config()->backgroundColor() );
00768     }
00769     else if (!paintOnlyDirty || lineRanges[z].dirty)
00770     {
00771       lineRanges[z].dirty = false;
00772 
00773       m_view->renderer()->paintTextLine(paintDrawBuffer, &lineRanges[z], xStart, xEnd, &cursor, &bm);
00774 
00775       paint.drawPixmap (x, z * h, drawBuffer, 0, 0, width, h);
00776     }
00777   }
00778 }
00779 
00784 void KateViewInternal::makeVisible (const KateTextCursor& c, uint endCol, bool force, bool center, bool calledExternally)
00785 {
00786   //kdDebug() << "MakeVisible start [" << startPos().line << "," << startPos().col << "] end [" << endPos().line << "," << endPos().col << "] -> request: [" << c.line << "," << c.col << "]" <<endl;// , new start [" << scroll.line << "," << scroll.col << "] lines " << (linesDisplayed() - 1) << " height " << height() << endl;
00787     // if the line is in a folded region, unfold all the way up
00788     //if ( m_doc->foldingTree()->findNodeForLine( c.line )->visible )
00789     //  kdDebug()<<"line ("<<c.line<<") should be visible"<<endl;
00790 
00791   if ( force )
00792   {
00793     KateTextCursor scroll = c;
00794     scrollPos(scroll, force, calledExternally);
00795   }
00796   else if (center && (c < startPos() || c > endPos()))
00797   {
00798     KateTextCursor scroll = viewLineOffset(c, -int(linesDisplayed()) / 2);
00799     scrollPos(scroll, false, calledExternally);
00800   }
00801   else if ( c > viewLineOffset(endPos(), -m_minLinesVisible) )
00802   {
00803     KateTextCursor scroll = viewLineOffset(c, -(linesDisplayed() - m_minLinesVisible - 1));
00804 
00805     if (!m_view->dynWordWrap() && m_columnScroll->isHidden())
00806       if (scrollbarVisible(scroll.line()))
00807         scroll.setLine(scroll.line() + 1);
00808 
00809     scrollPos(scroll, false, calledExternally);
00810   }
00811   else if ( c < viewLineOffset(startPos(), m_minLinesVisible) )
00812   {
00813     KateTextCursor scroll = viewLineOffset(c, -m_minLinesVisible);
00814     scrollPos(scroll, false, calledExternally);
00815   }
00816   else
00817   {
00818     // Check to see that we're not showing blank lines
00819     KateTextCursor max = maxStartPos();
00820     if (startPos() > max) {
00821       scrollPos(max, max.col(), calledExternally);
00822     }
00823   }
00824 
00825   if (!m_view->dynWordWrap() && endCol != (uint)-1)
00826   {
00827     int sX = (int)m_view->renderer()->textWidth (textLine( m_doc->getRealLine( c.line() ) ), c.col() );
00828 
00829     int sXborder = sX-8;
00830     if (sXborder < 0)
00831       sXborder = 0;
00832 
00833     if (sX < m_startX)
00834       scrollColumns (sXborder);
00835     else if  (sX > m_startX + width())
00836       scrollColumns (sX - width() + 8);
00837   }
00838 
00839   m_madeVisible = !force;
00840 }
00841 
00842 void KateViewInternal::slotRegionVisibilityChangedAt(unsigned int)
00843 {
00844   kdDebug(13030) << "slotRegionVisibilityChangedAt()" << endl;
00845   m_cachedMaxStartPos.setLine(-1);
00846   KateTextCursor max = maxStartPos();
00847   if (startPos() > max)
00848     scrollPos(max);
00849 
00850   updateView();
00851   update();
00852   leftBorder->update();
00853 }
00854 
00855 void KateViewInternal::slotCodeFoldingChanged()
00856 {
00857   leftBorder->update();
00858 }
00859 
00860 void KateViewInternal::slotRegionBeginEndAddedRemoved(unsigned int)
00861 {
00862   kdDebug(13030) << "slotRegionBeginEndAddedRemoved()" << endl;
00863   // FIXME: performance problem
00864   leftBorder->update();
00865 }
00866 
00867 void KateViewInternal::showEvent ( QShowEvent *e )
00868 {
00869   updateView ();
00870 
00871   QWidget::showEvent (e);
00872 }
00873 
00874 uint KateViewInternal::linesDisplayed() const
00875 {
00876   int h = height();
00877   int fh = m_view->renderer()->fontHeight();
00878 
00879   return (h - (h % fh)) / fh;
00880 }
00881 
00882 QPoint KateViewInternal::cursorCoordinates()
00883 {
00884   int viewLine = displayViewLine(displayCursor, true);
00885 
00886   if (viewLine == -1)
00887     return QPoint(-1, -1);
00888 
00889   uint y = viewLine * m_view->renderer()->fontHeight();
00890   uint x = cXPos - m_startX - lineRanges[viewLine].startX + leftBorder->width() + lineRanges[viewLine].xOffset();
00891 
00892   return QPoint(x, y);
00893 }
00894 
00895 
00896 void KateViewInternal::updateMicroFocusHint()
00897 {
00898   int line = displayViewLine(displayCursor, true);
00899   if (line == -1)
00900       return;
00901 
00902 // Cursor placement code is changed for Asian input method that
00903   // shows candidate window. This behavior is same as Qt/E 2.3.7
00904   // which supports Asian input methods. Asian input methods need
00905   // start point of IM selection text to place candidate window as
00906   // adjacent to the selection text.
00907   uint preeditStrLen = m_view->renderer()->textWidth(textLine(m_imPreeditStartLine), cursor.col()) - m_view->renderer()->textWidth(textLine(m_imPreeditStartLine), m_imPreeditSelStart);
00908   uint x = cXPos - m_startX - lineRanges[line].startX + lineRanges[line].xOffset() - preeditStrLen;
00909   uint y = line * m_view->renderer()->fontHeight();
00910 
00911   setMicroFocusHint( x, y, 0, m_view->renderer()->fontHeight() );
00912 }
00913 
00914 
00915 void KateViewInternal::doReturn()
00916 {
00917   KateTextCursor c = cursor;
00918   m_doc->newLine( c, this );
00919   updateCursor( c );
00920   updateView();
00921 }
00922 
00923 void KateViewInternal::doDelete()
00924 {
00925   m_doc->del( cursor );
00926 }
00927 
00928 void KateViewInternal::doBackspace()
00929 {
00930   m_doc->backspace( cursor );
00931 }
00932 
00933 void KateViewInternal::doPaste()
00934 {
00935   m_doc->paste( m_view );
00936 }
00937 
00938 void KateViewInternal::doTranspose()
00939 {
00940   m_doc->transpose( cursor );
00941 }
00942 
00943 void KateViewInternal::doDeleteWordLeft()
00944 {
00945   wordLeft( true );
00946   m_doc->removeSelectedText();
00947   update();
00948 }
00949 
00950 void KateViewInternal::doDeleteWordRight()
00951 {
00952   wordRight( true );
00953   m_doc->removeSelectedText();
00954   update();
00955 }
00956 
00957 class CalculatingCursor : public KateTextCursor {
00958 public:
00959   CalculatingCursor(KateViewInternal* vi)
00960     : KateTextCursor()
00961     , m_vi(vi)
00962   {
00963     Q_ASSERT(valid());
00964   }
00965 
00966   CalculatingCursor(KateViewInternal* vi, const KateTextCursor& c)
00967     : KateTextCursor(c)
00968     , m_vi(vi)
00969   {
00970     Q_ASSERT(valid());
00971   }
00972 
00973   // This one constrains its arguments to valid positions
00974   CalculatingCursor(KateViewInternal* vi, uint line, uint col)
00975     : KateTextCursor(line, col)
00976     , m_vi(vi)
00977   {
00978     makeValid();
00979   }
00980 
00981 
00982   virtual CalculatingCursor& operator+=( int n ) = 0;
00983 
00984   virtual CalculatingCursor& operator-=( int n ) = 0;
00985 
00986   CalculatingCursor& operator++() { return operator+=( 1 ); }
00987 
00988   CalculatingCursor& operator--() { return operator-=( 1 ); }
00989 
00990   void makeValid() {
00991     m_line = QMAX( 0, QMIN( int( m_vi->m_doc->numLines() - 1 ), line() ) );
00992     if (m_vi->m_doc->wrapCursor())
00993       m_col = QMAX( 0, QMIN( m_vi->m_doc->lineLength( line() ), col() ) );
00994     else
00995       m_col = QMAX( 0, col() );
00996     Q_ASSERT( valid() );
00997   }
00998 
00999   void toEdge( Bias bias ) {
01000     if( bias == left ) m_col = 0;
01001     else if( bias == right ) m_col = m_vi->m_doc->lineLength( line() );
01002   }
01003 
01004   bool atEdge() const { return atEdge( left ) || atEdge( right ); }
01005 
01006   bool atEdge( Bias bias ) const {
01007     switch( bias ) {
01008     case left:  return col() == 0;
01009     case none:  return atEdge();
01010     case right: return col() == m_vi->m_doc->lineLength( line() );
01011     default: Q_ASSERT(false); return false;
01012     }
01013   }
01014 
01015 protected:
01016   bool valid() const {
01017     return line() >= 0 &&
01018             uint( line() ) < m_vi->m_doc->numLines() &&
01019             col() >= 0 &&
01020             (!m_vi->m_doc->wrapCursor() || col() <= m_vi->m_doc->lineLength( line() ));
01021   }
01022   KateViewInternal* m_vi;
01023 };
01024 
01025 class BoundedCursor : public CalculatingCursor {
01026 public:
01027   BoundedCursor(KateViewInternal* vi)
01028     : CalculatingCursor( vi ) {};
01029   BoundedCursor(KateViewInternal* vi, const KateTextCursor& c )
01030     : CalculatingCursor( vi, c ) {};
01031   BoundedCursor(KateViewInternal* vi, uint line, uint col )
01032     : CalculatingCursor( vi, line, col ) {};
01033   virtual CalculatingCursor& operator+=( int n ) {
01034     m_col += n;
01035 
01036     if (n > 0 && m_vi->m_view->dynWordWrap()) {
01037       // Need to constrain to current visible text line for dynamic wrapping mode
01038       if (m_col > m_vi->m_doc->lineLength(m_line)) {
01039         KateLineRange currentRange = m_vi->range(*this);
01040 
01041         int endX;
01042         bool crap;
01043         m_vi->m_view->renderer()->textWidth(m_vi->textLine(m_line), currentRange.startCol, m_vi->width() - currentRange.xOffset(), &crap, &endX);
01044         endX += (m_col - currentRange.endCol + 1) * m_vi->m_view->renderer()->spaceWidth();
01045 
01046         // Constraining if applicable NOTE: some code duplication in KateViewInternal::resize()
01047         if (endX >= m_vi->width() - currentRange.xOffset()) {
01048           m_col -= n;
01049           if ( uint( line() ) < m_vi->m_doc->numLines() - 1 ) {
01050             m_line++;
01051             m_col = 0;
01052           }
01053         }
01054       }
01055 
01056     } else if (n < 0 && col() < 0 && line() > 0 ) {
01057       m_line--;
01058       m_col = m_vi->m_doc->lineLength( line() );
01059     }
01060 
01061     m_col = QMAX( 0, col() );
01062 
01063     Q_ASSERT( valid() );
01064     return *this;
01065   }
01066   virtual CalculatingCursor& operator-=( int n ) {
01067     return operator+=( -n );
01068   }
01069 };
01070 
01071 class WrappingCursor : public CalculatingCursor {
01072 public:
01073   WrappingCursor(KateViewInternal* vi)
01074     : CalculatingCursor( vi) {};
01075   WrappingCursor(KateViewInternal* vi, const KateTextCursor& c )
01076     : CalculatingCursor( vi, c ) {};
01077   WrappingCursor(KateViewInternal* vi, uint line, uint col )
01078     : CalculatingCursor( vi, line, col ) {};
01079 
01080   virtual CalculatingCursor& operator+=( int n ) {
01081     if( n < 0 ) return operator-=( -n );
01082     int len = m_vi->m_doc->lineLength( line() );
01083     if( col() + n <= len ) {
01084       m_col += n;
01085     } else if( uint( line() ) < m_vi->m_doc->numLines() - 1 ) {
01086       n -= len - col() + 1;
01087       m_col = 0;
01088       m_line++;
01089       operator+=( n );
01090     } else {
01091       m_col = len;
01092     }
01093     Q_ASSERT( valid() );
01094     return *this;
01095   }
01096   virtual CalculatingCursor& operator-=( int n ) {
01097     if( n < 0 ) return operator+=( -n );
01098     if( col() - n >= 0 ) {
01099       m_col -= n;
01100     } else if( line() > 0 ) {
01101       n -= col() + 1;
01102       m_line--;
01103       m_col = m_vi->m_doc->lineLength( line() );
01104       operator-=( n );
01105     } else {
01106       m_col = 0;
01107     }
01108     Q_ASSERT( valid() );
01109     return *this;
01110   }
01111 };
01112 
01113 void KateViewInternal::moveChar( Bias bias, bool sel )
01114 {
01115   KateTextCursor c;
01116   if ( m_doc->wrapCursor() ) {
01117     c = WrappingCursor( this, cursor ) += bias;
01118   } else {
01119     c = BoundedCursor( this, cursor ) += bias;
01120   }
01121   updateSelection( c, sel );
01122   updateCursor( c );
01123 }
01124 
01125 void KateViewInternal::cursorLeft(  bool sel ) { moveChar( left,  sel ); }
01126 void KateViewInternal::cursorRight( bool sel ) { moveChar( right, sel ); }
01127 
01128 void KateViewInternal::moveWord( Bias bias, bool sel )
01129 {
01130   // This matches the word-moving in QTextEdit, QLineEdit etc.
01131 
01132   WrappingCursor c( this, cursor );
01133   if( !c.atEdge( bias ) ) {
01134     KateHighlighting* h = m_doc->highlight();
01135 
01136     bool moved = false;
01137     while( !c.atEdge( bias ) && !h->isInWord( m_doc->textLine( c.line() )[ c.col() - (bias == left ? 1 : 0) ] ) )
01138     {
01139       c += bias;
01140       moved = true;
01141     }
01142 
01143     if ( bias != right || !moved )
01144     {
01145       while( !c.atEdge( bias ) &&  h->isInWord( m_doc->textLine( c.line() )[ c.col() - (bias == left ? 1 : 0) ] ) )
01146         c += bias;
01147       if ( bias == right )
01148       {
01149         while ( !c.atEdge( bias ) && m_doc->textLine( c.line() )[ c.col() ].isSpace() )
01150           c+= bias;
01151       }
01152     }
01153 
01154   } else {
01155     c += bias;
01156   }
01157   updateSelection( c, sel );
01158   updateCursor( c );
01159 }
01160 
01161 void KateViewInternal::wordLeft ( bool sel ) { moveWord( left,  sel ); }
01162 void KateViewInternal::wordRight( bool sel ) { moveWord( right, sel ); }
01163 
01164 void KateViewInternal::moveEdge( Bias bias, bool sel )
01165 {
01166   BoundedCursor c( this, cursor );
01167   c.toEdge( bias );
01168   updateSelection( c, sel );
01169   updateCursor( c );
01170 }
01171 
01172 void KateViewInternal::home( bool sel )
01173 {
01174   if (m_view->dynWordWrap() && currentRange().startCol) {
01175     // Allow us to go to the real start if we're already at the start of the view line
01176     if (cursor.col() != currentRange().startCol) {
01177       KateTextCursor c(cursor.line(), currentRange().startCol);
01178       updateSelection( c, sel );
01179       updateCursor( c );
01180       return;
01181     }
01182   }
01183 
01184   if( !(m_doc->configFlags() & KateDocument::cfSmartHome) ) {
01185     moveEdge( left, sel );
01186     return;
01187   }
01188 
01189   KateTextCursor c = cursor;
01190   int lc = textLine( c.line() )->firstChar();
01191 
01192   if( lc < 0 || c.col() == lc ) {
01193     c.setCol(0);
01194   } else {
01195     c.setCol(lc);
01196   }
01197 
01198   updateSelection( c, sel );
01199   updateCursor( c );
01200 }
01201 
01202 void KateViewInternal::end( bool sel )
01203 {
01204   if (m_view->dynWordWrap() && currentRange().wrap) {
01205     // Allow us to go to the real end if we're already at the end of the view line
01206     if (cursor.col() < currentRange().endCol - 1) {
01207       KateTextCursor c(cursor.line(), currentRange().endCol - 1);
01208       updateSelection( c, sel );
01209       updateCursor( c );
01210       return;
01211     }
01212   }
01213 
01214   moveEdge( right, sel );
01215 }
01216 
01217 KateLineRange KateViewInternal::range(int realLine, const KateLineRange* previous)
01218 {
01219   // look at the cache first
01220   if (!m_updatingView && realLine >= lineRanges[0].line && realLine <= lineRanges[lineRanges.count() - 1].line)
01221     for (uint i = 0; i < lineRanges.count(); i++)
01222       if (realLine == lineRanges[i].line)
01223         if (!m_view->dynWordWrap() || (!previous && lineRanges[i].startCol == 0) || (previous && lineRanges[i].startCol == previous->endCol))
01224           return lineRanges[i];
01225 
01226   // Not in the cache, we have to create it
01227   KateLineRange ret;
01228 
01229   KateTextLine::Ptr text = textLine(realLine);
01230   if (!text) {
01231     return KateLineRange();
01232   }
01233 
01234   if (!m_view->dynWordWrap()) {
01235     Q_ASSERT(!previous);
01236     ret.line = realLine;
01237     ret.virtualLine = m_doc->getVirtualLine(realLine);
01238     ret.startCol = 0;
01239     ret.endCol = m_doc->lineLength(realLine);
01240     ret.startX = 0;
01241     ret.endX = m_view->renderer()->textWidth(text, -1);
01242     ret.viewLine = 0;
01243     ret.wrap = false;
01244     return ret;
01245   }
01246 
01247   ret.endCol = (int)m_view->renderer()->textWidth(text, previous ? previous->endCol : 0, width() - (previous ? previous->shiftX : 0), &ret.wrap, &ret.endX);
01248 
01249   Q_ASSERT(ret.endCol > ret.startCol);
01250 
01251   ret.line = realLine;
01252 
01253   if (previous) {
01254     ret.virtualLine = previous->virtualLine;
01255     ret.startCol = previous->endCol;
01256     ret.startX = previous->endX;
01257     ret.endX += previous->endX;
01258     ret.shiftX = previous->shiftX;
01259     ret.viewLine = previous->viewLine + 1;
01260 
01261   } else {
01262     // TODO worthwhile optimising this to get the data out of the initial textWidth call?
01263     if (m_view->config()->dynWordWrapAlignIndent() > 0) {
01264       int pos = text->nextNonSpaceChar(0);
01265 
01266       if (pos > 0)
01267         ret.shiftX = m_view->renderer()->textWidth(text, pos);
01268 
01269       if (ret.shiftX > ((double)width() / 100 * m_view->config()->dynWordWrapAlignIndent()))
01270         ret.shiftX = 0;
01271     }
01272 
01273     ret.virtualLine = m_doc->getVirtualLine(realLine);
01274     ret.startCol = 0;
01275     ret.startX = 0;
01276     ret.viewLine = 0;
01277   }
01278 
01279   return ret;
01280 }
01281 
01282 KateLineRange KateViewInternal::currentRange()
01283 {
01284 //  Q_ASSERT(m_view->dynWordWrap());
01285 
01286   return range(cursor);
01287 }
01288 
01289 KateLineRange KateViewInternal::previousRange()
01290 {
01291   uint currentViewLine = viewLine(cursor);
01292 
01293   if (currentViewLine)
01294     return range(cursor.line(), currentViewLine - 1);
01295   else
01296     return range(m_doc->getRealLine(displayCursor.line() - 1), -1);
01297 }
01298 
01299 KateLineRange KateViewInternal::nextRange()
01300 {
01301   uint currentViewLine = viewLine(cursor) + 1;
01302 
01303   if (currentViewLine >= viewLineCount(cursor.line())) {
01304     currentViewLine = 0;
01305     return range(cursor.line() + 1, currentViewLine);
01306   } else {
01307     return range(cursor.line(), currentViewLine);
01308   }
01309 }
01310 
01311 KateLineRange KateViewInternal::range(const KateTextCursor& realCursor)
01312 {
01313 //  Q_ASSERT(m_view->dynWordWrap());
01314 
01315   KateLineRange thisRange;
01316   bool first = true;
01317 
01318   do {
01319     thisRange = range(realCursor.line(), first ? 0L : &thisRange);
01320     first = false;
01321   } while (thisRange.wrap && !(realCursor.col() >= thisRange.startCol && realCursor.col() < thisRange.endCol) && thisRange.startCol != thisRange.endCol);
01322 
01323   return thisRange;
01324 }
01325 
01326 KateLineRange KateViewInternal::range(uint realLine, int viewLine)
01327 {
01328 //  Q_ASSERT(m_view->dynWordWrap());
01329 
01330   KateLineRange thisRange;
01331   bool first = true;
01332 
01333   do {
01334     thisRange = range(realLine, first ? 0L : &thisRange);
01335     first = false;
01336   } while (thisRange.wrap && viewLine != thisRange.viewLine && thisRange.startCol != thisRange.endCol);
01337 
01338   if (viewLine != -1 && viewLine != thisRange.viewLine)
01339     kdDebug(13030) << "WARNING: viewLine " << viewLine << " of line " << realLine << " does not exist." << endl;
01340 
01341   return thisRange;
01342 }
01343 
01349 uint KateViewInternal::viewLine(const KateTextCursor& realCursor)
01350 {
01351   if (!m_view->dynWordWrap()) return 0;
01352 
01353   if (realCursor.col() == 0) return 0;
01354 
01355   KateLineRange thisRange;
01356   bool first = true;
01357 
01358   do {
01359     thisRange = range(realCursor.line(), first ? 0L : &thisRange);
01360     first = false;
01361   } while (thisRange.wrap && !(realCursor.col() >= thisRange.startCol && realCursor.col() < thisRange.endCol) && thisRange.startCol != thisRange.endCol);
01362 
01363   return thisRange.viewLine;
01364 }
01365 
01366 int KateViewInternal::displayViewLine(const KateTextCursor& virtualCursor, bool limitToVisible)
01367 {
01368   KateTextCursor work = startPos();
01369 
01370   int limit = linesDisplayed();
01371 
01372   // Efficient non-word-wrapped path
01373   if (!m_view->dynWordWrap()) {
01374     int ret = virtualCursor.line() - startLine();
01375     if (limitToVisible && (ret < 0 || ret > limit))
01376       return -1;
01377     else
01378       return ret;
01379   }
01380 
01381   if (work == virtualCursor) {
01382     return 0;
01383   }
01384 
01385   int ret = -viewLine(work);
01386   bool forwards = (work < virtualCursor) ? true : false;
01387 
01388   // FIXME switch to using ranges? faster?
01389   if (forwards) {
01390     while (work.line() != virtualCursor.line()) {
01391       ret += viewLineCount(m_doc->getRealLine(work.line()));
01392       work.setLine(work.line() + 1);
01393       if (limitToVisible && ret > limit)
01394         return -1;
01395     }
01396   } else {
01397     while (work.line() != virtualCursor.line()) {
01398       work.setLine(work.line() - 1);
01399       ret -= viewLineCount(m_doc->getRealLine(work.line()));
01400       if (limitToVisible && ret < 0)
01401         return -1;
01402     }
01403   }
01404 
01405   // final difference
01406   KateTextCursor realCursor = virtualCursor;
01407   realCursor.setLine(m_doc->getRealLine(realCursor.line()));
01408   if (realCursor.col() == -1) realCursor.setCol(m_doc->lineLength(realCursor.line()));
01409   ret += viewLine(realCursor);
01410 
01411   if (limitToVisible && (ret < 0 || ret > limit))
01412     return -1;
01413 
01414   return ret;
01415 }
01416 
01417 uint KateViewInternal::lastViewLine(uint realLine)
01418 {
01419   if (!m_view->dynWordWrap()) return 0;
01420 
01421   KateLineRange thisRange;
01422   bool first = true;
01423 
01424   do {
01425     thisRange = range(realLine, first ? 0L : &thisRange);
01426     first = false;
01427   } while (thisRange.wrap && thisRange.startCol != thisRange.endCol);
01428 
01429   return thisRange.viewLine;
01430 }
01431 
01432 uint KateViewInternal::viewLineCount(uint realLine)
01433 {
01434   return lastViewLine(realLine) + 1;
01435 }
01436 
01437 /*
01438  * This returns the cursor which is offset by (offset) view lines.
01439  * This is the main function which is called by code not specifically dealing with word-wrap.
01440  * The opposite conversion (cursor to offset) can be done with displayViewLine.
01441  *
01442  * The cursors involved are virtual cursors (ie. equivalent to displayCursor)
01443  */
01444 KateTextCursor KateViewInternal::viewLineOffset(const KateTextCursor& virtualCursor, int offset, bool keepX)
01445 {
01446   if (!m_view->dynWordWrap()) {
01447     KateTextCursor ret(QMIN((int)m_doc->visibleLines() - 1, virtualCursor.line() + offset), 0);
01448 
01449     if (ret.line() < 0)
01450       ret.setLine(0);
01451 
01452     if (keepX) {
01453       int realLine = m_doc->getRealLine(ret.line());
01454       ret.setCol(m_doc->lineLength(realLine) - 1);
01455 
01456       if (m_currentMaxX > cXPos)
01457         cXPos = m_currentMaxX;
01458 
01459       if (m_doc->wrapCursor())
01460         cXPos = QMIN(cXPos, (int)m_view->renderer()->textWidth(textLine(realLine), m_doc->lineLength(realLine)));
01461 
01462       m_view->renderer()->textWidth(ret, cXPos);
01463     }
01464 
01465     return ret;
01466   }
01467 
01468   KateTextCursor realCursor = virtualCursor;
01469   realCursor.setLine(m_doc->getRealLine(virtualCursor.line()));
01470 
01471   uint cursorViewLine = viewLine(realCursor);
01472 
01473   int currentOffset = 0;
01474   int virtualLine = 0;
01475 
01476   bool forwards = (offset > 0) ? true : false;
01477 
01478   if (forwards) {
01479     currentOffset = lastViewLine(realCursor.line()) - cursorViewLine;
01480     if (offset <= currentOffset) {
01481       // the answer is on the same line
01482       KateLineRange thisRange = range(realCursor.line(), cursorViewLine + offset);
01483       Q_ASSERT(thisRange.virtualLine == virtualCursor.line());
01484       return KateTextCursor(virtualCursor.line(), thisRange.startCol);
01485     }
01486 
01487     virtualLine = virtualCursor.line() + 1;
01488 
01489   } else {
01490     offset = -offset;
01491     currentOffset = cursorViewLine;
01492     if (offset <= currentOffset) {
01493       // the answer is on the same line
01494       KateLineRange thisRange = range(realCursor.line(), cursorViewLine - offset);
01495       Q_ASSERT(thisRange.virtualLine == virtualCursor.line());
01496       return KateTextCursor(virtualCursor.line(), thisRange.startCol);
01497     }
01498 
01499     virtualLine = virtualCursor.line() - 1;
01500   }
01501 
01502   currentOffset++;
01503 
01504   while (virtualLine >= 0 && virtualLine < (int)m_doc->visibleLines())
01505   {
01506     KateLineRange thisRange;
01507     bool first = true;
01508     int realLine = m_doc->getRealLine(virtualLine);
01509 
01510     do {
01511       thisRange = range(realLine, first ? 0L : &thisRange);
01512       first = false;
01513 
01514       if (offset == currentOffset) {
01515         if (!forwards) {
01516           // We actually want it the other way around
01517           int requiredViewLine = lastViewLine(realLine) - thisRange.viewLine;
01518           if (requiredViewLine != thisRange.viewLine) {
01519             thisRange = range(realLine, requiredViewLine);
01520           }
01521         }
01522 
01523         KateTextCursor ret(virtualLine, thisRange.startCol);
01524 
01525         // keep column position
01526         if (keepX) {
01527           ret.setCol(thisRange.endCol - 1);
01528           KateTextCursor realCursorTemp(m_doc->getRealLine(virtualCursor.line()), virtualCursor.col());
01529           int visibleX = m_view->renderer()->textWidth(realCursorTemp) - range(realCursorTemp).startX;
01530           int xOffset = thisRange.startX;
01531 
01532           if (m_currentMaxX > visibleX)
01533             visibleX = m_currentMaxX;
01534 
01535           cXPos = xOffset + visibleX;
01536 
01537           cXPos = QMIN(cXPos, lineMaxCursorX(thisRange));
01538 
01539           m_view->renderer()->textWidth(ret, cXPos);
01540         }
01541 
01542         return ret;
01543       }
01544 
01545       currentOffset++;
01546 
01547     } while (thisRange.wrap);
01548 
01549     if (forwards)
01550       virtualLine++;
01551     else
01552       virtualLine--;
01553   }
01554 
01555   // Looks like we were asked for something a bit exotic.
01556   // Return the max/min valid position.
01557   if (forwards)
01558     return KateTextCursor(m_doc->visibleLines() - 1, m_doc->lineLength(m_doc->visibleLines() - 1));
01559   else
01560     return KateTextCursor(0, 0);
01561 }
01562 
01563 int KateViewInternal::lineMaxCursorX(const KateLineRange& range)
01564 {
01565   if (!m_doc->wrapCursor() && !range.wrap)
01566     return INT_MAX;
01567 
01568   int maxX = range.endX;
01569 
01570   if (maxX && range.wrap) {
01571     QChar lastCharInLine = textLine(range.line)->getChar(range.endCol - 1);
01572     maxX -= m_view->renderer()->config()->fontMetrics()->width(lastCharInLine);
01573   }
01574 
01575   return maxX;
01576 }
01577 
01578 int KateViewInternal::lineMaxCol(const KateLineRange& range)
01579 {
01580   int maxCol = range.endCol;
01581 
01582   if (maxCol && range.wrap)
01583     maxCol--;
01584 
01585   return maxCol;
01586 }
01587 
01588 void KateViewInternal::cursorUp(bool sel)
01589 {
01590   if (displayCursor.line() == 0 && (!m_view->dynWordWrap() || viewLine(cursor) == 0))
01591     return;
01592 
01593   int newLine = cursor.line(), newCol = 0, xOffset = 0, startCol = 0;
01594   m_preserveMaxX = true;
01595 
01596   if (m_view->dynWordWrap()) {
01597     // Dynamic word wrapping - navigate on visual lines rather than real lines
01598     KateLineRange thisRange = currentRange();
01599     // This is not the first line because that is already simplified out above
01600     KateLineRange pRange = previousRange();
01601 
01602     // Ensure we're in the right spot
01603     Q_ASSERT((cursor.line() == thisRange.line) &&
01604              (cursor.col() >= thisRange.startCol) &&
01605              (!thisRange.wrap || cursor.col() < thisRange.endCol));
01606 
01607     // VisibleX is the distance from the start of the text to the cursor on the current line.
01608     int visibleX = m_view->renderer()->textWidth(cursor) - thisRange.startX;
01609     int currentLineVisibleX = visibleX;
01610 
01611     // Translate to new line
01612     visibleX += thisRange.xOffset();
01613     visibleX -= pRange.xOffset();
01614 
01615     // Limit to >= 0
01616     visibleX = QMAX(0, visibleX);
01617 
01618     startCol = pRange.startCol;
01619     xOffset = pRange.startX;
01620     newLine = pRange.line;
01621 
01622     // Take into account current max X (ie. if the current line was smaller
01623     // than the last definitely specified width)
01624     if (thisRange.xOffset() && !pRange.xOffset() && currentLineVisibleX == 0) // Special case for where xOffset may be > m_currentMaxX
01625       visibleX = m_currentMaxX;
01626     else if (visibleX < m_currentMaxX - pRange.xOffset())
01627       visibleX = m_currentMaxX - pRange.xOffset();
01628 
01629     cXPos = xOffset + visibleX;
01630 
01631     cXPos = QMIN(cXPos, lineMaxCursorX(pRange));
01632 
01633     newCol = QMIN((int)m_view->renderer()->textPos(newLine, visibleX, startCol), lineMaxCol(pRange));
01634 
01635   } else {
01636     newLine = m_doc->getRealLine(displayCursor.line() - 1);
01637 
01638     if ((m_doc->wrapCursor()) && m_currentMaxX > cXPos)
01639       cXPos = m_currentMaxX;
01640   }
01641 
01642   KateTextCursor c(newLine, newCol);
01643   m_view->renderer()->textWidth(c, cXPos);
01644 
01645   updateSelection( c, sel );
01646   updateCursor( c );
01647 }
01648 
01649 void KateViewInternal::cursorDown(bool sel)
01650 {
01651   if ((displayCursor.line() >= (int)m_doc->numVisLines() - 1) && (!m_view->dynWordWrap() || viewLine(cursor) == lastViewLine(cursor.line())))
01652     return;
01653 
01654   int newLine = cursor.line(), newCol = 0, xOffset = 0, startCol = 0;
01655   m_preserveMaxX = true;
01656 
01657   if (m_view->dynWordWrap()) {
01658     // Dynamic word wrapping - navigate on visual lines rather than real lines
01659     KateLineRange thisRange = currentRange();
01660     // This is not the last line because that is already simplified out above
01661     KateLineRange nRange = nextRange();
01662 
01663     // Ensure we're in the right spot
01664     Q_ASSERT((cursor.line() == thisRange.line) &&
01665              (cursor.col() >= thisRange.startCol) &&
01666              (!thisRange.wrap || cursor.col() < thisRange.endCol));
01667 
01668     // VisibleX is the distance from the start of the text to the cursor on the current line.
01669     int visibleX = m_view->renderer()->textWidth(cursor) - thisRange.startX;
01670     int currentLineVisibleX = visibleX;
01671 
01672     // Translate to new line
01673     visibleX += thisRange.xOffset();
01674     visibleX -= nRange.xOffset();
01675 
01676     // Limit to >= 0
01677     visibleX = QMAX(0, visibleX);
01678 
01679     if (!thisRange.wrap) {
01680       newLine = m_doc->getRealLine(displayCursor.line() + 1);
01681     } else {
01682       startCol = thisRange.endCol;
01683       xOffset = thisRange.endX;
01684     }
01685 
01686     // Take into account current max X (ie. if the current line was smaller
01687     // than the last definitely specified width)
01688     if (thisRange.xOffset() && !nRange.xOffset() && currentLineVisibleX == 0) // Special case for where xOffset may be > m_currentMaxX
01689       visibleX = m_currentMaxX;
01690     else if (visibleX < m_currentMaxX - nRange.xOffset())
01691       visibleX = m_currentMaxX - nRange.xOffset();
01692 
01693     cXPos = xOffset + visibleX;
01694 
01695     cXPos = QMIN(cXPos, lineMaxCursorX(nRange));
01696 
01697     newCol = QMIN((int)m_view->renderer()->textPos(newLine, visibleX, startCol), lineMaxCol(nRange));
01698 
01699   } else {
01700     newLine = m_doc->getRealLine(displayCursor.line() + 1);
01701 
01702     if ((m_doc->wrapCursor()) && m_currentMaxX > cXPos)
01703       cXPos = m_currentMaxX;
01704   }
01705 
01706   KateTextCursor c(newLine, newCol);
01707   m_view->renderer()->textWidth(c, cXPos);
01708 
01709   updateSelection(c, sel);
01710   updateCursor(c);
01711 }
01712 
01713 void KateViewInternal::cursorToMatchingBracket( bool sel )
01714 {
01715   KateTextCursor start( cursor ), end;
01716 
01717   if( !m_doc->findMatchingBracket( start, end ) )
01718     return;
01719 
01720   // The cursor is now placed just to the left of the matching bracket.
01721   // If it's an ending bracket, put it to the right (so we can easily
01722   // get back to the original bracket).
01723   if( end > start )
01724     end.setCol(end.col() + 1);
01725 
01726   updateSelection( end, sel );
01727   updateCursor( end );
01728 }
01729 
01730 void KateViewInternal::topOfView( bool sel )
01731 {
01732   KateTextCursor c = viewLineOffset(startPos(), m_minLinesVisible);
01733   updateSelection( c, sel );
01734   updateCursor( c );
01735 }
01736 
01737 void KateViewInternal::bottomOfView( bool sel )
01738 {
01739   // FIXME account for wordwrap
01740   KateTextCursor c = viewLineOffset(endPos(), -m_minLinesVisible);
01741   updateSelection( c, sel );
01742   updateCursor( c );
01743 }
01744 
01745 // lines is the offset to scroll by
01746 void KateViewInternal::scrollLines( int lines, bool sel )
01747 {
01748   KateTextCursor c = viewLineOffset(displayCursor, lines, true);
01749 
01750   // Fix the virtual cursor -> real cursor
01751   c.setLine(m_doc->getRealLine(c.line()));
01752 
01753   updateSelection( c, sel );
01754   updateCursor( c );
01755 }
01756 
01757 // This is a bit misleading... it's asking for the view to be scrolled, not the cursor
01758 void KateViewInternal::scrollUp()
01759 {
01760   KateTextCursor newPos = viewLineOffset(m_startPos, -1);
01761   scrollPos(newPos);
01762 }
01763 
01764 void KateViewInternal::scrollDown()
01765 {
01766   KateTextCursor newPos = viewLineOffset(m_startPos, 1);
01767   scrollPos(newPos);
01768 }
01769 
01770 void KateViewInternal::setAutoCenterLines(int viewLines, bool updateView)
01771 {
01772   m_autoCenterLines = viewLines;
01773   m_minLinesVisible = QMIN(int((linesDisplayed() - 1)/2), m_autoCenterLines);
01774   if (updateView)
01775     KateViewInternal::updateView();
01776 }
01777 
01778 void KateViewInternal::pageUp( bool sel )
01779 {
01780   // remember the view line and x pos
01781   int viewLine = displayViewLine(displayCursor);
01782   bool atTop = (startPos().line() == 0 && startPos().col() == 0);
01783 
01784   // Adjust for an auto-centering cursor
01785   int lineadj = 2 * m_minLinesVisible;
01786   int cursorStart = (linesDisplayed() - 1) - viewLine;
01787   if (cursorStart < m_minLinesVisible)
01788     lineadj -= m_minLinesVisible - cursorStart;
01789 
01790   int linesToScroll = -QMAX( (linesDisplayed() - 1) - lineadj, 0 );
01791   m_preserveMaxX = true;
01792 
01793   // don't scroll the full view in case the scrollbar appears
01794   if (!m_view->dynWordWrap()) {
01795     if (scrollbarVisible(startLine() + linesToScroll + viewLine)) {
01796       if (!m_columnScrollDisplayed) {
01797         linesToScroll++;
01798       }
01799     } else {
01800       if (m_columnScrollDisplayed) {
01801         linesToScroll--;
01802       }
01803     }
01804   }
01805 
01806   if (!m_doc->pageUpDownMovesCursor () && !atTop) {
01807     int xPos = m_view->renderer()->textWidth(cursor) - currentRange().startX;
01808 
01809     KateTextCursor newStartPos = viewLineOffset(startPos(), linesToScroll - 1);
01810     scrollPos(newStartPos);
01811 
01812     // put the cursor back approximately where it was
01813     KateTextCursor newPos = viewLineOffset(newStartPos, viewLine, true);
01814     newPos.setLine(m_doc->getRealLine(newPos.line()));
01815 
01816     KateLineRange newLine = range(newPos);
01817 
01818     if (m_currentMaxX - newLine.xOffset() > xPos)
01819       xPos = m_currentMaxX - newLine.xOffset();
01820 
01821     cXPos = QMIN(newLine.startX + xPos, lineMaxCursorX(newLine));
01822 
01823     m_view->renderer()->textWidth( newPos, cXPos );
01824 
01825     m_preserveMaxX = true;
01826     updateSelection( newPos, sel );
01827     updateCursor(newPos);
01828 
01829   } else {
01830     scrollLines( linesToScroll, sel );
01831   }
01832 }
01833 
01834 void KateViewInternal::pageDown( bool sel )
01835 {
01836   // remember the view line
01837   int viewLine = displayViewLine(displayCursor);
01838   bool atEnd = startPos() >= m_cachedMaxStartPos;
01839 
01840   // Adjust for an auto-centering cursor
01841   int lineadj = 2 * m_minLinesVisible;
01842   int cursorStart = m_minLinesVisible - viewLine;
01843   if (cursorStart > 0)
01844     lineadj -= cursorStart;
01845 
01846   int linesToScroll = QMAX( (linesDisplayed() - 1) - lineadj, 0 );
01847   m_preserveMaxX = true;
01848 
01849   // don't scroll the full view in case the scrollbar appears
01850   if (!m_view->dynWordWrap()) {
01851     if (scrollbarVisible(startLine() + linesToScroll + viewLine - (linesDisplayed() - 1))) {
01852       if (!m_columnScrollDisplayed) {
01853         linesToScroll--;
01854       }
01855     } else {
01856       if (m_columnScrollDisplayed) {
01857         linesToScroll--;
01858       }
01859     }
01860   }
01861 
01862   if (!m_doc->pageUpDownMovesCursor () && !atEnd) {
01863     int xPos = m_view->renderer()->textWidth(cursor) - currentRange().startX;
01864 
01865     KateTextCursor newStartPos = viewLineOffset(startPos(), linesToScroll + 1);
01866     scrollPos(newStartPos);
01867 
01868     // put the cursor back approximately where it was
01869     KateTextCursor newPos = viewLineOffset(newStartPos, viewLine, true);
01870     newPos.setLine(m_doc->getRealLine(newPos.line()));
01871 
01872     KateLineRange newLine = range(newPos);
01873 
01874     if (m_currentMaxX - newLine.xOffset() > xPos)
01875       xPos = m_currentMaxX - newLine.xOffset();
01876 
01877     cXPos = QMIN(newLine.startX + xPos, lineMaxCursorX(newLine));
01878 
01879     m_view->renderer()->textWidth( newPos, cXPos );
01880 
01881     m_preserveMaxX = true;
01882     updateSelection( newPos, sel );
01883     updateCursor(newPos);
01884 
01885   } else {
01886     scrollLines( linesToScroll, sel );
01887   }
01888 }
01889 
01890 bool KateViewInternal::scrollbarVisible(uint startLine)
01891 {
01892   return maxLen(startLine) > width() - 8;
01893 }
01894 
01895 int KateViewInternal::maxLen(uint startLine)
01896 {
01897 //  Q_ASSERT(!m_view->dynWordWrap());
01898 
01899   int displayLines = (m_view->height() / m_view->renderer()->fontHeight()) + 1;
01900 
01901   int maxLen = 0;
01902 
01903   for (int z = 0; z < displayLines; z++) {
01904     int virtualLine = startLine + z;
01905 
01906     if (virtualLine < 0 || virtualLine >= (int)m_doc->visibleLines())
01907       break;
01908 
01909     KateLineRange thisRange = range((int)m_doc->getRealLine(virtualLine));
01910 
01911     maxLen = QMAX(maxLen, thisRange.endX);
01912   }
01913 
01914   return maxLen;
01915 }
01916 
01917 void KateViewInternal::top( bool sel )
01918 {
01919   KateTextCursor c( 0, cursor.col() );
01920   m_view->renderer()->textWidth( c, cXPos );
01921   updateSelection( c, sel );
01922   updateCursor( c );
01923 }
01924 
01925 void KateViewInternal::bottom( bool sel )
01926 {
01927   KateTextCursor c( m_doc->lastLine(), cursor.col() );
01928   m_view->renderer()->textWidth( c, cXPos );
01929   updateSelection( c, sel );
01930   updateCursor( c );
01931 }
01932 
01933 void KateViewInternal::top_home( bool sel )
01934 {
01935   KateTextCursor c( 0, 0 );
01936   updateSelection( c, sel );
01937   updateCursor( c );
01938 }
01939 
01940 void KateViewInternal::bottom_end( bool sel )
01941 {
01942   KateTextCursor c( m_doc->lastLine(), m_doc->lineLength( m_doc->lastLine() ) );
01943   updateSelection( c, sel );
01944   updateCursor( c );
01945 }
01946 
01947 void KateViewInternal::updateSelection( const KateTextCursor& _newCursor, bool keepSel )
01948 {
01949   KateTextCursor newCursor = _newCursor;
01950   if( keepSel )
01951   {
01952     if ( !m_doc->hasSelection() || (selectAnchor.line() == -1)
01953          || ((m_doc->configFlags() & KateDocument::cfPersistent)
01954              && ((cursor < m_doc->selectStart) || (cursor > m_doc->selectEnd))) )
01955     {
01956       selectAnchor = cursor;
01957       m_doc->setSelection( cursor, newCursor );
01958     }
01959     else
01960     {
01961       bool doSelect = true;
01962       switch (m_selectionMode)
01963       {
01964         case Word:
01965         {
01966           bool same = ( newCursor.line() == selStartCached.line() );
01967           uint c;
01968           if ( newCursor.line() > selStartCached.line() ||
01969                ( same && newCursor.col() > selEndCached.col() ) )
01970           {
01971             selectAnchor = selStartCached;
01972 
01973             KateTextLine::Ptr l = m_doc->kateTextLine( newCursor.line() );
01974 
01975             for ( c = newCursor.col(); c < l->length(); c++ )
01976               if ( !m_doc->m_highlight->isInWord( l->getChar( c ) ) )
01977                 break;
01978 
01979             newCursor.setCol( c );
01980           }
01981           else if ( newCursor.line() < selStartCached.line() ||
01982                ( same && newCursor.col() < selStartCached.col() ) )
01983           {
01984             selectAnchor = selEndCached;
01985 
01986             KateTextLine::Ptr l = m_doc->kateTextLine( newCursor.line() );
01987 
01988             for ( c = newCursor.col(); c > 0; c-- )
01989               if ( !m_doc->m_highlight->isInWord( l->getChar( c ) ) )
01990                 break;
01991 
01992             newCursor.setCol( c+1 );
01993           }
01994           else
01995             doSelect = false;
01996 
01997         }
01998         break;
01999         case Line:
02000           if ( newCursor.line() > selStartCached.line() )
02001           {
02002             selectAnchor = selStartCached;
02003             newCursor.setCol( m_doc->textLine( newCursor.line() ).length() );
02004           }
02005           else if ( newCursor.line() < selStartCached.line() )
02006           {
02007             selectAnchor = selEndCached;
02008             newCursor.setCol( 0 );
02009           }
02010           else // same line, ignore
02011             doSelect = false;
02012         break;
02013         default: // *allways* keep original selection for mouse
02014         {
02015           if ( selStartCached.line() < 0 ) // invalid
02016             break;
02017 
02018           if ( newCursor.line() > selEndCached.line() ||
02019                ( newCursor.line() == selEndCached.line() &&
02020                  newCursor.col() > selEndCached.col() ) )
02021             selectAnchor = selStartCached;
02022 
02023           else if ( newCursor.line() < selStartCached.line() ||
02024                ( newCursor.line() == selStartCached.line() &&
02025                  newCursor.col() < selStartCached.col() ) )
02026             selectAnchor = selEndCached;
02027 
02028           else
02029             doSelect = false;
02030         }
02031 //         break;
02032       }
02033 
02034       if ( doSelect )
02035         m_doc->setSelection( selectAnchor, newCursor);
02036       else if ( selStartCached.line() > 0 ) // we have a cached selectino, so we restore that
02037         m_doc->setSelection( selStartCached, selEndCached );
02038     }
02039 
02040     m_selChangedByUser = true;
02041   }
02042   else if ( !(m_doc->configFlags() & KateDocument::cfPersistent) )
02043     m_doc->clearSelection();
02044 }
02045 
02046 void KateViewInternal::updateCursor( const KateTextCursor& newCursor, bool force, bool center, bool calledExternally )
02047 {
02048   KateTextLine::Ptr l = textLine( newCursor.line() );
02049 
02050   if ( !force && (cursor == newCursor) )
02051   {
02052     if ( !m_madeVisible )
02053     {
02054       // unfold if required
02055       if ( l && ! l->isVisible() )
02056         m_doc->foldingTree()->ensureVisible( newCursor.line() );
02057 
02058       makeVisible ( displayCursor, displayCursor.col(), false, center, calledExternally );
02059     }
02060 
02061     return;
02062   }
02063 
02064   // remove trailing spaces ### really not nice here, unless it is *really* nessecary
02065 //   if ( m_doc->isReadWrite() && cursor.line() != newCursor.line() )
02066 //     m_doc->removeTrailingSpace( cursor.line() );
02067 
02068   // unfold if required
02069   if ( l && ! l->isVisible() )
02070     m_doc->foldingTree()->ensureVisible( newCursor.line() );
02071 
02072   KateTextCursor oldDisplayCursor = displayCursor;
02073 
02074   cursor.setPos (newCursor);
02075   displayCursor.setPos (m_doc->getVirtualLine(cursor.line()), cursor.col());
02076 
02077   cXPos = m_view->renderer()->textWidth( cursor );
02078   makeVisible ( displayCursor, displayCursor.col(), false, center, calledExternally );
02079 
02080   updateBracketMarks();
02081 
02082   // It's efficient enough to just tag them both without checking to see if they're on the same view line
02083   tagLine(oldDisplayCursor);
02084   tagLine(displayCursor);
02085 
02086   updateMicroFocusHint();
02087 
02088   if (m_cursorTimer.isActive ())
02089   {
02090     if ( KApplication::cursorFlashTime() > 0 )
02091       m_cursorTimer.start( KApplication::cursorFlashTime() / 2 );
02092     m_view->renderer()->setDrawCaret(true);
02093   }
02094 
02095   // Remember the maximum X position if requested
02096   if (m_preserveMaxX)
02097     m_preserveMaxX = false;
02098   else
02099     if (m_view->dynWordWrap())
02100       m_currentMaxX = m_view->renderer()->textWidth(displayCursor) - currentRange().startX + currentRange().xOffset();
02101     else
02102       m_currentMaxX = cXPos;
02103 
02104   //kdDebug() << "m_currentMaxX: " << m_currentMaxX << " (was "<< oldmaxx << "), cXPos: " << cXPos << endl;
02105   //kdDebug(13030) << "Cursor now located at real " << cursor.line << "," << cursor.col << ", virtual " << displayCursor.line << ", " << displayCursor.col << "; Top is " << startLine() << ", " << startPos().col << "; Old top is " << m_oldStartPos.line << ", " << m_oldStartPos.col << endl;
02106 
02107   paintText(0, 0, width(), height(), true);
02108 
02109   emit m_view->cursorPositionChanged();
02110 }
02111 
02112 void KateViewInternal::updateBracketMarks()
02113 {
02114   if ( bm.isValid() ) {
02115     KateTextCursor bmStart(m_doc->getVirtualLine(bm.start().line()), bm.start().col());
02116     KateTextCursor bmEnd(m_doc->getVirtualLine(bm.end().line()), bm.end().col());
02117     tagLine(bmStart);
02118     tagLine(bmEnd);
02119   }
02120 
02121   m_doc->newBracketMark( cursor, bm );
02122 
02123   if ( bm.isValid() ) {
02124     KateTextCursor bmStart(m_doc->getVirtualLine(bm.start().line()), bm.start().col());
02125     KateTextCursor bmEnd(m_doc->getVirtualLine(bm.end().line()), bm.end().col());
02126     tagLine(bmStart);
02127     tagLine(bmEnd);
02128   }
02129 }
02130 
02131 bool KateViewInternal::tagLine(const KateTextCursor& virtualCursor)
02132 {
02133   int viewLine = displayViewLine(virtualCursor, true);
02134   if (viewLine >= 0 && viewLine < (int)lineRanges.count()) {
02135     lineRanges[viewLine].dirty = true;
02136     leftBorder->update (0, lineToY(viewLine), leftBorder->width(), m_view->renderer()->fontHeight());
02137     return true;
02138   }
02139   return false;
02140 }
02141 
02142 bool KateViewInternal::tagLines( int start, int end, bool realLines )
02143 {
02144   return tagLines(KateTextCursor(start, 0), KateTextCursor(end, -1), realLines);
02145 }
02146 
02147 bool KateViewInternal::tagLines(KateTextCursor start, KateTextCursor end, bool realCursors)
02148 {
02149   if (realCursors)
02150   {
02151     //kdDebug()<<"realLines is true"<<endl;
02152     start.setLine(m_doc->getVirtualLine( start.line() ));
02153     end.setLine(m_doc->getVirtualLine( end.line() ));
02154   }
02155 
02156   if (end.line() < (int)startLine())
02157   {
02158     //kdDebug()<<"end<startLine"<<endl;
02159     return false;
02160   }
02161   if (start.line() > (int)endLine())
02162   {
02163     //kdDebug()<<"start> endLine"<<start<<" "<<((int)endLine())<<endl;
02164     return false;
02165   }
02166 
02167   //kdDebug(13030) << "tagLines( [" << start.line << "," << start.col << "], [" << end.line << "," << end.col << "] )\n";
02168 
02169   bool ret = false;
02170 
02171   for (uint z = 0; z < lineRanges.size(); z++)
02172   {
02173     if ((lineRanges[z].virtualLine > start.line() || (lineRanges[z].virtualLine == start.line() && lineRanges[z].endCol >= start.col() && start.col() != -1)) && (lineRanges[z].virtualLine < end.line() || (lineRanges[z].virtualLine == end.line() && (lineRanges[z].startCol <= end.col() || end.col() == -1)))) {
02174       ret = lineRanges[z].dirty = true;
02175       //kdDebug() << "Tagged line " << lineRanges[z].line << endl;
02176     }
02177   }
02178 
02179   if (!m_view->dynWordWrap())
02180   {
02181     int y = lineToY( start.line() );
02182     // FIXME is this enough for when multiple lines are deleted
02183     int h = (end.line() - start.line() + 2) * m_view->renderer()->fontHeight();
02184     if (end.line() == (int)m_doc->numVisLines() - 1)
02185       h = height();
02186 
02187     leftBorder->update (0, y, leftBorder->width(), h);
02188   }
02189   else
02190   {
02191     // FIXME Do we get enough good info in editRemoveText to optimise this more?
02192     //bool justTagged = false;
02193     for (uint z = 0; z < lineRanges.size(); z++)
02194     {
02195       if ((lineRanges[z].virtualLine > start.line() || (lineRanges[z].virtualLine == start.line() && lineRanges[z].endCol >= start.col() && start.col() != -1)) && (lineRanges[z].virtualLine < end.line() || (lineRanges[z].virtualLine == end.line() && (lineRanges[z].startCol <= end.col() || end.col() == -1))))
02196       {
02197         //justTagged = true;
02198         leftBorder->update (0, z * m_view->renderer()->fontHeight(), leftBorder->width(), leftBorder->height());
02199         break;
02200       }
02201       /*else if (justTagged)
02202       {
02203         justTagged = false;
02204         leftBorder->update (0, z * m_doc->viewFont.fontHeight, leftBorder->width(), m_doc->viewFont.fontHeight);
02205         break;
02206       }*/
02207     }
02208   }
02209 
02210   return ret;
02211 }
02212 
02213 void KateViewInternal::tagAll()
02214 {
02215   //kdDebug(13030) << "tagAll()" << endl;
02216   for (uint z = 0; z < lineRanges.size(); z++)
02217   {
02218       lineRanges[z].dirty = true;
02219   }
02220 
02221   leftBorder->updateFont();
02222   leftBorder->update ();
02223 }
02224 
02225 void KateViewInternal::paintCursor()
02226 {
02227   if (tagLine(displayCursor))
02228     paintText (0,0,width(), height(), true);
02229 }
02230 
02231 // Point in content coordinates
02232 void KateViewInternal::placeCursor( const QPoint& p, bool keepSelection, bool updateSelection )
02233 {
02234   KateLineRange thisRange = yToKateLineRange(p.y());
02235 
02236   if (thisRange.line == -1) {
02237     for (int i = (p.y() / m_view->renderer()->fontHeight()); i >= 0; i--) {
02238       thisRange = lineRanges[i];
02239       if (thisRange.line != -1)
02240         break;
02241     }
02242     Q_ASSERT(thisRange.line != -1);
02243   }
02244 
02245   int realLine = thisRange.line;
02246   int visibleLine = thisRange.virtualLine;
02247   uint startCol = thisRange.startCol;
02248 
02249   visibleLine = QMAX( 0, QMIN( visibleLine, int(m_doc->numVisLines()) - 1 ) );
02250 
02251   KateTextCursor c(realLine, 0);
02252 
02253   int x = QMIN(QMAX(0, p.x() - thisRange.xOffset()), lineMaxCursorX(thisRange) - thisRange.startX);
02254 
02255   m_view->renderer()->textWidth( c, startX() + x, startCol);
02256 
02257   if (updateSelection)
02258     KateViewInternal::updateSelection( c, keepSelection );
02259   updateCursor( c );
02260 }
02261 
02262 // Point in content coordinates
02263 bool KateViewInternal::isTargetSelected( const QPoint& p )
02264 {
02265   KateLineRange thisRange = yToKateLineRange(p.y());
02266 
02267   KateTextLine::Ptr l = textLine( thisRange.line );
02268   if( !l )
02269     return false;
02270 
02271   int col = m_view->renderer()->textPos( l, p.x() - thisRange.xOffset(), thisRange.startCol, false );
02272 
02273   return m_doc->lineColSelected( thisRange.line, col );
02274 }
02275 
02276 //
02277 // BEGIN EVENT HANDLING STUFF !!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!
02278 //
02279 
02280 bool KateViewInternal::eventFilter( QObject *obj, QEvent *e )
02281 {
02282   if (obj == m_lineScroll)
02283   {
02284     // the second condition is to make sure a scroll on the vertical bar doesn't cause a horizontal scroll ;)
02285     if (e->type() == QEvent::Wheel && m_lineScroll->minValue() != m_lineScroll->maxValue())
02286     {
02287       wheelEvent((QWheelEvent*)e);
02288       return true;
02289     }
02290 
02291     // continue processing
02292     return QWidget::eventFilter( obj, e );
02293   }
02294 
02295   switch( e->type() )
02296   {
02297     case QEvent::KeyPress:
02298     {
02299       QKeyEvent *k = (QKeyEvent *)e;
02300 
02301       if (m_view->m_codeCompletion->codeCompletionVisible ())
02302       {
02303         kdDebug (13030) << "hint around" << endl;
02304 
02305         if( k->key() == Key_Escape )
02306           m_view->m_codeCompletion->abortCompletion();
02307       }
02308 
02309       if ((k->key() == Qt::Key_Escape) && !(m_doc->configFlags() & KateDocument::cfPersistent) )
02310       {
02311         m_doc->clearSelection();
02312         return true;
02313       }
02314       else if ( !((k->state() & ControlButton) || (k->state() & AltButton)) )
02315       {
02316         keyPressEvent( k );
02317         return k->isAccepted();
02318       }
02319 
02320     } break;
02321 
02322     case QEvent::DragMove:
02323     {
02324       QPoint currentPoint = ((QDragMoveEvent*) e)->pos();
02325 
02326       QRect doNotScrollRegion( scrollMargin, scrollMargin,
02327                           width() - scrollMargin * 2,
02328                           height() - scrollMargin * 2 );
02329 
02330       if ( !doNotScrollRegion.contains( currentPoint ) )
02331       {
02332           startDragScroll();
02333           // Keep sending move events
02334           ( (QDragMoveEvent*)e )->accept( QRect(0,0,0,0) );
02335       }
02336 
02337       dragMoveEvent((QDragMoveEvent*)e);
02338     } break;
02339 
02340     case QEvent::DragLeave:
02341       // happens only when pressing ESC while dragging
02342       stopDragScroll();
02343       break;
02344 
02345     default:
02346       break;
02347   }
02348 
02349   return QWidget::eventFilter( obj, e );
02350 }
02351 
02352 void KateViewInternal::keyPressEvent( QKeyEvent* e )
02353 {
02354   KKey key(e);
02355 
02356   bool codeComp = m_view->m_codeCompletion->codeCompletionVisible ();
02357 
02358   if (codeComp)
02359   {
02360     kdDebug (13030) << "hint around" << endl;
02361 
02362     if( e->key() == Key_Enter || e->key() == Key_Return  ||
02363     (key == SHIFT + Qt::Key_Return) || (key == SHIFT + Qt::Key_Enter)) {
02364       m_view->m_codeCompletion->doComplete();
02365       e->accept();
02366       return;
02367     }
02368 
02369     if( (e->key() == Key_Up)    || (e->key() == Key_Down ) ||
02370         (e->key() == Key_Home ) || (e->key() == Key_End)   ||
02371         (e->key() == Key_Prior) || (e->key() == Key_Next )) {
02372        m_view->m_codeCompletion->handleKey (e);
02373        e->accept();
02374        return;
02375     }
02376   }
02377 
02378   if (key == Qt::Key_Left)
02379   {
02380     m_view->cursorLeft();
02381     e->accept();
02382 
02383     if (codeComp)
02384       m_view->m_codeCompletion->updateBox ();
02385 
02386     return;
02387   }
02388 
02389   if (key == Qt::Key_Right)
02390   {
02391     m_view->cursorRight();
02392     e->accept();
02393 
02394     if (codeComp)
02395       m_view->m_codeCompletion->updateBox ();
02396 
02397     return;
02398   }
02399 
02400   if (key == Qt::Key_Down)
02401   {
02402     m_view->down();
02403     e->accept();
02404     return;
02405   }
02406 
02407   if (key == Qt::Key_Up)
02408   {
02409     m_view->up();
02410     e->accept();
02411     return;
02412   }
02413 
02414   if( !m_doc->isReadWrite() )
02415   {
02416     e->ignore();
02417     return;
02418   }
02419 
02420   if ((key == Qt::Key_Return) || (key == Qt::Key_Enter))
02421   {
02422     m_view->keyReturn();
02423     e->accept();
02424     return;
02425   }
02426 
02427   if ((key == SHIFT + Qt::Key_Return) || (key == SHIFT + Qt::Key_Enter))
02428   {
02429     uint ln = cursor.line();
02430     int col = cursor.col();
02431     KateTextLine::Ptr line = m_doc->kateTextLine( ln );
02432     int pos = line->firstChar();
02433     if (pos > cursor.col()) pos = cursor.col();
02434     if (pos != -1) {
02435       while ((int)line->length() > pos &&
02436              !line->getChar(pos).isLetterOrNumber() &&
02437              pos < cursor.col()) ++pos;
02438     } else {
02439       pos = line->length(); // stay indented
02440     }
02441     m_doc->editStart();
02442     m_doc->insertText( cursor.line(), line->length(), "\n" +  line->string(0, pos)
02443       + line->string().right( line->length() - cursor.col() ) );
02444     cursor.setPos(ln + 1, pos);
02445     if (col < line->length())
02446       m_doc->editRemoveText(ln, col, line->length() - col);
02447     m_doc->editEnd();
02448     updateCursor(cursor, true);
02449     updateView();
02450     e->accept();
02451 
02452     if (codeComp)
02453       m_view->m_codeCompletion->updateBox ();
02454 
02455     return;
02456   }
02457 
02458   if (key == Qt::Key_Backspace || key == SHIFT + Qt::Key_Backspace)
02459   {
02460     m_view->backspace();
02461     e->accept();
02462 
02463     if (codeComp)
02464       m_view->m_codeCompletion->updateBox ();
02465 
02466     return;
02467   }
02468 
02469   if (key == Qt::Key_Delete)
02470   {
02471     m_view->keyDelete();
02472     e->accept();
02473 
02474     if (codeComp)
02475       m_view->m_codeCompletion->updateBox ();
02476 
02477     return;
02478   }
02479 
02480   if( (key == Qt::Key_Tab || key == SHIFT+Qt::Key_Backtab || key == Qt::Key_Backtab)
02481       && (m_doc->configFlags() & KateDocumentConfig::cfTabIndents) )
02482   {
02483     if( key == Qt::Key_Tab )
02484     {
02485       if (m_doc->hasSelection() || (m_doc->configFlags() & KateDocumentConfig::cfTabIndentsMode))
02486         m_doc->indent( m_view, cursor.line(), 1 );
02487       else if (m_doc->configFlags() & KateDocumentConfig::cfTabInsertsTab)
02488         m_doc->typeChars ( m_view, QString ("\t") );
02489       else
02490         m_doc->insertIndentChars ( m_view );
02491 
02492       e->accept();
02493 
02494       if (codeComp)
02495         m_view->m_codeCompletion->updateBox ();
02496 
02497       return;
02498     }
02499 
02500     if (key == SHIFT+Qt::Key_Backtab || key == Qt::Key_Backtab)
02501     {
02502       m_doc->indent( m_view, cursor.line(), -1 );
02503       e->accept();
02504 
02505       if (codeComp)
02506         m_view->m_codeCompletion->updateBox ();
02507 
02508       return;
02509     }
02510   }
02511 
02512   if ( !(e->state() & ControlButton) && !(e->state() & AltButton)
02513        && m_doc->typeChars ( m_view, e->text() ) )
02514   {
02515     e->accept();
02516 
02517     if (codeComp)
02518       m_view->m_codeCompletion->updateBox ();
02519 
02520     return;
02521   }
02522 
02523   e->ignore();
02524 }
02525 
02526 void KateViewInternal::keyReleaseEvent( QKeyEvent* e )
02527 {
02528   KKey key(e);
02529 
02530   if (key == SHIFT)
02531     m_shiftKeyPressed = true;
02532   else
02533   {
02534     if (m_shiftKeyPressed)
02535     {
02536       m_shiftKeyPressed = false;
02537 
02538       if (m_selChangedByUser)
02539       {
02540         QApplication::clipboard()->setSelectionMode( true );
02541         m_doc->copy();
02542         QApplication::clipboard()->setSelectionMode( false );
02543 
02544         m_selChangedByUser = false;
02545       }
02546     }
02547   }
02548 
02549   e->ignore();
02550   return;
02551 }
02552 
02553 void KateViewInternal::mousePressEvent( QMouseEvent* e )
02554 {
02555   switch (e->button())
02556   {
02557     case LeftButton:
02558         m_selChangedByUser = false;
02559 
02560         if (possibleTripleClick)
02561         {
02562           possibleTripleClick = false;
02563 
02564           m_selectionMode = Line;
02565 
02566           if ( e->state() & Qt::ShiftButton )
02567           {
02568             updateSelection( cursor, true );
02569           }
02570           else
02571           {
02572             m_doc->selectLine( cursor );
02573           }
02574 
02575           QApplication::clipboard()->setSelectionMode( true );
02576           m_doc->copy();
02577           QApplication::clipboard()->setSelectionMode( false );
02578 
02579           selStartCached = m_doc->selectStart;
02580           selEndCached = m_doc->selectEnd;
02581 
02582           cursor.setCol(0);
02583           updateCursor( cursor );
02584           return;
02585         }
02586 
02587         if ( e->state() & Qt::ShiftButton )
02588         {
02589           selStartCached = m_doc->selectStart;
02590           selEndCached = m_doc->selectEnd;
02591         }
02592         else
02593           selStartCached.setLine( -1 ); // invalidate
02594 
02595         if( isTargetSelected( e->pos() ) )
02596         {
02597           dragInfo.state = diPending;
02598           dragInfo.start = e->pos();
02599         }
02600         else
02601         {
02602           dragInfo.state = diNone;
02603 
02604           placeCursor( e->pos(), e->state() & ShiftButton );
02605 
02606           scrollX = 0;
02607           scrollY = 0;
02608 
02609           m_scrollTimer.start (50);
02610         }
02611 
02612         e->accept ();
02613         break;
02614 
02615     // try to show popup menu
02616     case RightButton:
02617       if ( ! isTargetSelected( e->pos() ) )
02618         placeCursor( e->pos() );
02619 
02620       // popup is a qguardedptr now
02621       if (m_view->popup())
02622         m_view->popup()->popup( mapToGlobal( e->pos() ) );
02623 
02624       e->accept ();
02625       break;
02626 
02627     default:
02628       e->ignore ();
02629       break;
02630   }
02631 }
02632 
02633 void KateViewInternal::mouseDoubleClickEvent(QMouseEvent *e)
02634 {
02635   switch (e->button())
02636   {
02637     case LeftButton:
02638       m_selectionMode = Word;
02639 
02640       if ( e->state() & Qt::ShiftButton )
02641       {
02642         selStartCached = m_doc->selectStart;
02643         selEndCached = m_doc->selectEnd;
02644         updateSelection( cursor, true );
02645       }
02646       else
02647       {
02648         m_doc->selectWord( cursor );
02649       }
02650 
02651       // Move cursor to end of selected word
02652       if (m_doc->hasSelection())
02653       {
02654         QApplication::clipboard()->setSelectionMode( true );
02655         m_doc->copy();
02656         QApplication::clipboard()->setSelectionMode( false );
02657 
02658         cursor.setPos(m_doc->selectEnd);
02659         updateCursor( cursor );
02660 
02661         selStartCached = m_doc->selectStart;
02662         selEndCached = m_doc->selectEnd;
02663       }
02664 
02665       possibleTripleClick = true;
02666       QTimer::singleShot ( QApplication::doubleClickInterval(), this, SLOT(tripleClickTimeout()) );
02667 
02668       e->accept ();
02669       break;
02670 
02671     default:
02672       e->ignore ();
02673       break;
02674   }
02675 }
02676 
02677 void KateViewInternal::tripleClickTimeout()
02678 {
02679   possibleTripleClick = false;
02680 }
02681 
02682 void KateViewInternal::mouseReleaseEvent( QMouseEvent* e )
02683 {
02684   switch (e->button())
02685   {
02686     case LeftButton:
02687       m_selectionMode = Default;
02688       selStartCached.setLine( -1 );
02689 
02690       if (m_selChangedByUser)
02691       {
02692         QApplication::clipboard()->setSelectionMode( true );
02693         m_doc->copy();
02694         QApplication::clipboard()->setSelectionMode( false );
02695 
02696         m_selChangedByUser = false;
02697       }
02698 
02699       if (dragInfo.state == diPending)
02700         placeCursor( e->pos(), e->state() & ShiftButton );
02701       else if (dragInfo.state == diNone)
02702         m_scrollTimer.stop ();
02703 
02704       dragInfo.state = diNone;
02705 
02706       e->accept ();
02707       break;
02708 
02709     case MidButton:
02710       placeCursor( e->pos() );
02711 
02712       if( m_doc->isReadWrite() )
02713       {
02714         QApplication::clipboard()->setSelectionMode( true );
02715         doPaste();
02716         QApplication::clipboard()->setSelectionMode( false );
02717       }
02718 
02719       e->accept ();
02720       break;
02721 
02722     default:
02723       e->ignore ();
02724       break;
02725   }
02726 }
02727 
02728 void KateViewInternal::mouseMoveEvent( QMouseEvent* e )
02729 {
02730   if( e->state() & LeftButton )
02731   {
02732     if (dragInfo.state == diPending)
02733     {
02734       // we had a mouse down, but haven't confirmed a drag yet
02735       // if the mouse has moved sufficiently, we will confirm
02736       QPoint p( e->pos() - dragInfo.start );
02737 
02738       // we've left the drag square, we can start a real drag operation now
02739       if( p.manhattanLength() > KGlobalSettings::dndEventDelay() )
02740         doDrag();
02741 
02742       return;
02743     }
02744 
02745     mouseX = e->x();
02746     mouseY = e->y();
02747 
02748     scrollX = 0;
02749     scrollY = 0;
02750     int d = m_view->renderer()->fontHeight();
02751 
02752     if (mouseX < 0)
02753       scrollX = -d;
02754 
02755     if (mouseX > width())
02756       scrollX = d;
02757 
02758     if (mouseY < 0)
02759     {
02760       mouseY = 0;
02761       scrollY = -d;
02762     }
02763 
02764     if (mouseY > height())
02765     {
02766       mouseY = height();
02767       scrollY = d;
02768     }
02769 
02770     placeCursor( QPoint( mouseX, mouseY ), true );
02771 
02772   }
02773   else
02774   {
02775     if (m_textHintEnabled)
02776     {
02777        m_textHintTimer.start(m_textHintTimeout);
02778        m_textHintMouseX=e->x();
02779        m_textHintMouseY=e->y();
02780     }
02781   }
02782 }
02783 
02784 void KateViewInternal::paintEvent(QPaintEvent *e)
02785 {
02786   paintText(e->rect().x(), e->rect().y(), e->rect().width(), e->rect().height());
02787 }
02788 
02789 void KateViewInternal::resizeEvent(QResizeEvent* e)
02790 {
02791   bool expandedHorizontally = width() > e->oldSize().width();
02792   bool expandedVertically = height() > e->oldSize().height();
02793   bool heightChanged = height() != e->oldSize().height();
02794 
02795   m_madeVisible = false;
02796 
02797   if (heightChanged) {
02798     setAutoCenterLines(m_autoCenterLines, false);
02799     m_cachedMaxStartPos.setPos(-1, -1);
02800   }
02801 
02802   if (m_view->dynWordWrap()) {
02803     bool dirtied = false;
02804 
02805     for (uint i = 0; i < lineRanges.count(); i++) {
02806       // find the first dirty line
02807       // the word wrap updateView algorithm is forced to check all lines after a dirty one
02808       if (lineRanges[i].wrap ||
02809          (!expandedHorizontally && (lineRanges[i].endX - lineRanges[i].startX) > width())) {
02810         dirtied = lineRanges[i].dirty = true;
02811         break;
02812       }
02813     }
02814 
02815     if (dirtied || heightChanged) {
02816       updateView(true);
02817       leftBorder->update();
02818     }
02819 
02820     if (width() < e->oldSize().width()) {
02821       if (!m_doc->wrapCursor()) {
02822         // May have to restrain cursor to new smaller width...
02823         if (cursor.col() > m_doc->lineLength(cursor.line())) {
02824           KateLineRange thisRange = currentRange();
02825 
02826           KateTextCursor newCursor(cursor.line(), thisRange.endCol + ((width() - thisRange.xOffset() - (thisRange.endX - thisRange.startX)) / m_view->renderer()->spaceWidth()) - 1);
02827           updateCursor(newCursor);
02828         }
02829       }
02830     }
02831 
02832   } else {
02833     updateView();
02834 
02835     if (expandedHorizontally && startX() > 0)
02836       scrollColumns(startX() - (width() - e->oldSize().width()));
02837   }
02838 
02839   if (expandedVertically) {
02840     KateTextCursor max = maxStartPos();
02841     if (startPos() > max)
02842       scrollPos(max);
02843   }
02844 }
02845 
02846 void KateViewInternal::scrollTimeout ()
02847 {
02848   if (scrollX || scrollY)
02849   {
02850     scrollLines (startPos().line() + (scrollY / (int)m_view->renderer()->fontHeight()));
02851     placeCursor( QPoint( mouseX, mouseY ), true );
02852   }
02853 }
02854 
02855 void KateViewInternal::cursorTimeout ()
02856 {
02857   m_view->renderer()->setDrawCaret(!m_view->renderer()->drawCaret());
02858   paintCursor();
02859 }
02860 
02861 void KateViewInternal::textHintTimeout ()
02862 {
02863   m_textHintTimer.stop ();
02864 
02865   KateLineRange thisRange = yToKateLineRange(m_textHintMouseY);
02866 
02867   if (thisRange.line == -1) return;
02868 
02869   if (m_textHintMouseX> (lineMaxCursorX(thisRange) - thisRange.startX)) return;
02870 
02871   int realLine = thisRange.line;
02872   int startCol = thisRange.startCol;
02873 
02874   KateTextCursor c(realLine, 0);
02875   m_view->renderer()->textWidth( c, startX() + m_textHintMouseX, startCol);
02876 
02877   QString tmp;
02878 
02879   emit m_view->needTextHint(c.line(), c.col(), tmp);
02880 
02881   if (!tmp.isEmpty()) kdDebug(13030)<<"Hint text: "<<tmp<<endl;
02882 }
02883 
02884 void KateViewInternal::focusInEvent (QFocusEvent *)
02885 {
02886   if (KApplication::cursorFlashTime() > 0)
02887     m_cursorTimer.start ( KApplication::cursorFlashTime() / 2 );
02888 
02889   if (m_textHintEnabled)
02890     m_textHintTimer.start( m_textHintTimeout );
02891 
02892   paintCursor();
02893 
02894   m_doc->m_activeView = m_view;
02895 
02896   emit m_view->gotFocus( m_view );
02897 }
02898 
02899 void KateViewInternal::focusOutEvent (QFocusEvent *)
02900 {
02901   if( ! m_view->m_codeCompletion->codeCompletionVisible() )
02902   {
02903     m_cursorTimer.stop();
02904 
02905     m_view->renderer()->setDrawCaret(true);
02906     paintCursor();
02907     emit m_view->lostFocus( m_view );
02908   }
02909 
02910   m_textHintTimer.stop();
02911 }
02912 
02913 void KateViewInternal::doDrag()
02914 {
02915   dragInfo.state = diDragging;
02916   dragInfo.dragObject = new QTextDrag(m_doc->selection(), this);
02917   dragInfo.dragObject->drag();
02918 }
02919 
02920 void KateViewInternal::dragEnterEvent( QDragEnterEvent* event )
02921 {
02922   event->accept( (QTextDrag::canDecode(event) && m_doc->isReadWrite()) ||
02923                   KURLDrag::canDecode(event) );
02924 }
02925 
02926 void KateViewInternal::dragMoveEvent( QDragMoveEvent* event )
02927 {
02928   // track the cursor to the current drop location
02929   placeCursor( event->pos(), true, false );
02930 
02931   // important: accept action to switch between copy and move mode
02932   // without this, the text will always be copied.
02933   event->acceptAction();
02934 }
02935 
02936 void KateViewInternal::dropEvent( QDropEvent* event )
02937 {
02938   if ( KURLDrag::canDecode(event) ) {
02939 
02940       emit dropEventPass(event);
02941 
02942   } else if ( QTextDrag::canDecode(event) && m_doc->isReadWrite() ) {
02943 
02944     QString text;
02945 
02946     if (!QTextDrag::decode(event, text))
02947       return;
02948 
02949     // is the source our own document?
02950     bool priv = false;
02951     if (event->source() && event->source()->inherits("KateViewInternal"))
02952       priv = m_doc->ownedView( ((KateViewInternal*)(event->source()))->m_view );
02953 
02954     // dropped on a text selection area?
02955     bool selected = isTargetSelected( event->pos() );
02956 
02957     if( priv && selected ) {
02958       // this is a drag that we started and dropped on our selection
02959       // ignore this case
02960       return;
02961     }
02962 
02963     // use one transaction
02964     m_doc->editStart ();
02965 
02966     // on move: remove selected text; on copy: duplicate text
02967     if ( event->action() != QDropEvent::Copy )
02968       m_doc->removeSelectedText();
02969 
02970     m_doc->insertText( cursor.line(), cursor.col(), text );
02971 
02972     m_doc->editEnd ();
02973 
02974     placeCursor( event->pos() );
02975 
02976     event->acceptAction();
02977     updateView();
02978   }
02979 
02980   // finally finish drag and drop mode
02981   dragInfo.state = diNone;
02982   // important, because the eventFilter`s DragLeave does not occure
02983   stopDragScroll();
02984 }
02985 
02986 void KateViewInternal::imStartEvent( QIMEvent *e )
02987 {
02988   if ( m_doc->m_bReadOnly ) {
02989     e->ignore();
02990     return;
02991   }
02992 
02993   if ( m_doc->hasSelection() )
02994     m_doc->removeSelectedText();
02995 
02996   m_imPreeditStartLine = cursor.line();
02997   m_imPreeditStart = cursor.col();
02998   m_imPreeditLength = 0;
02999   m_imPreeditSelStart = m_imPreeditStart;
03000 
03001   m_doc->setIMSelectionValue( m_imPreeditStartLine, m_imPreeditStart, 0, 0, 0, true );
03002 }
03003 
03004 void KateViewInternal::imComposeEvent( QIMEvent *e )
03005 {
03006   if ( m_doc->m_bReadOnly ) {
03007     e->ignore();
03008     return;
03009   }
03010 
03011   if ( m_imPreeditLength > 0 ) {
03012     m_doc->removeText( cursor.line(), m_imPreeditStart,
03013                        cursor.line(), m_imPreeditStart + m_imPreeditLength );
03014   }
03015 
03016   m_doc->setIMSelectionValue( m_imPreeditStartLine, m_imPreeditStart, m_imPreeditStart + e->text().length(),
03017                               m_imPreeditStart + e->cursorPos(), m_imPreeditStart + e->cursorPos() + e->selectionLength(),
03018                               true );
03019 
03020   m_doc->insertText( cursor.line(), cursor.col(), e->text() );
03021 
03022   updateView( true );
03023   updateCursor( cursor, true );
03024 
03025   m_imPreeditLength = e->text().length();
03026   m_imPreeditSelStart = m_imPreeditStart + e->cursorPos();
03027 }
03028 
03029 void KateViewInternal::imEndEvent( QIMEvent *e )
03030 {
03031   if ( m_doc->m_bReadOnly ) {
03032     e->ignore();
03033     return;
03034   }
03035 
03036   if ( m_imPreeditLength > 0 ) {
03037     m_doc->removeText( cursor.line(), m_imPreeditStart,
03038                        cursor.line(), m_imPreeditStart + m_imPreeditLength );
03039   }
03040 
03041   m_doc->setIMSelectionValue( m_imPreeditStartLine, m_imPreeditStart, 0, 0, 0, false );
03042 
03043   if ( e->text().length() > 0 ) {
03044     m_doc->insertText( cursor.line(), cursor.col(), e->text() );
03045 
03046     if ( !m_cursorTimer.isActive() && KApplication::cursorFlashTime() > 0 )
03047       m_cursorTimer.start ( KApplication::cursorFlashTime() / 2 );
03048 
03049     updateView( true );
03050     updateCursor( cursor, true );
03051 
03052   }
03053 
03054   m_imPreeditStart = 0;
03055   m_imPreeditLength = 0;
03056   m_imPreeditSelStart = 0;
03057 }
03058 
03059 //
03060 // END EVENT HANDLING STUFF !!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!
03061 //
03062 
03063 void KateViewInternal::clear()
03064 {
03065   cursor.setPos(0, 0);
03066   displayCursor.setPos(0, 0);
03067 }
03068 
03069 void KateViewInternal::wheelEvent(QWheelEvent* e)
03070 {
03071   if (m_lineScroll->minValue() != m_lineScroll->maxValue() && e->orientation() != Qt::Horizontal) {
03072     // React to this as a vertical event
03073     if ( ( e->state() & ControlButton ) || ( e->state() & ShiftButton ) ) {
03074       if (e->delta() > 0)
03075         scrollPrevPage();
03076       else
03077         scrollNextPage();
03078     } else {
03079       scrollViewLines(-((e->delta() / 120) * QApplication::wheelScrollLines()));
03080       // maybe a menu was opened or a bubbled window title is on us -> we shall erase it
03081       update();
03082       leftBorder->update();
03083     }
03084 
03085   } else if (!m_columnScroll->isHidden()) {
03086     QWheelEvent copy = *e;
03087     QApplication::sendEvent(m_columnScroll, &copy);
03088 
03089   } else {
03090     e->ignore();
03091   }
03092 }
03093 
03094 void KateViewInternal::startDragScroll()
03095 {
03096   if ( !m_dragScrollTimer.isActive() ) {
03097     m_suppressColumnScrollBar = true;
03098     m_dragScrollTimer.start( scrollTime );
03099   }
03100 }
03101 
03102 void KateViewInternal::stopDragScroll()
03103 {
03104   m_suppressColumnScrollBar = false;
03105   m_dragScrollTimer.stop();
03106   updateView();
03107 }
03108 
03109 void KateViewInternal::doDragScroll()
03110 {
03111   QPoint p = this->mapFromGlobal( QCursor::pos() );
03112 
03113   int dx = 0, dy = 0;
03114   if ( p.y() < scrollMargin ) {
03115     dy = p.y() - scrollMargin;
03116   } else if ( p.y() > height() - scrollMargin ) {
03117     dy = scrollMargin - (height() - p.y());
03118   }
03119   if ( p.x() < scrollMargin ) {
03120     dx = p.x() - scrollMargin;
03121   } else if ( p.x() > width() - scrollMargin ) {
03122     dx = scrollMargin - (width() - p.x());
03123   }
03124   dy /= 4;
03125 
03126   if (dy)
03127     scrollLines(startPos().line() + dy);
03128 
03129   if (!m_view->dynWordWrap() && m_columnScrollDisplayed && dx)
03130     scrollColumns(kMin (m_startX + dx, m_columnScroll->maxValue()));
03131 
03132   if (!dy && !dx)
03133     stopDragScroll();
03134 }
03135 
03136 void KateViewInternal::enableTextHints(int timeout)
03137 {
03138   m_textHintTimeout=timeout;
03139   m_textHintEnabled=true;
03140   m_textHintTimer.start(timeout);
03141 }
03142 
03143 void KateViewInternal::disableTextHints()
03144 {
03145   m_textHintEnabled=false;
03146   m_textHintTimer.stop ();
03147 }
03148 
03149 // BEGIN EDIT STUFF
03150 void KateViewInternal::editStart()
03151 {
03152   editSessionNumber++;
03153 
03154   if (editSessionNumber > 1)
03155     return;
03156 
03157   editIsRunning = true;
03158   editOldCursor = cursor;
03159 }
03160 
03161 void KateViewInternal::editEnd(int editTagLineStart, int editTagLineEnd, bool tagFrom)
03162 {
03163    if (editSessionNumber == 0)
03164     return;
03165 
03166   editSessionNumber--;
03167 
03168   if (editSessionNumber > 0)
03169     return;
03170 
03171   if (tagFrom && (editTagLineStart <= int(m_doc->getRealLine(startLine()))))
03172     tagAll();
03173   else
03174     tagLines (editTagLineStart, tagFrom ? m_doc->lastLine() : editTagLineEnd, true);
03175 
03176   if (editOldCursor == cursor)
03177     updateBracketMarks();
03178 
03179   if (m_imPreeditLength <= 0)
03180     updateView(true);
03181 
03182   if ((editOldCursor != cursor) && (m_imPreeditLength <= 0))
03183   {
03184     m_madeVisible = false;
03185     updateCursor ( cursor, true );
03186   }
03187   else if ( m_view->isActive() )
03188   {
03189     makeVisible(displayCursor, displayCursor.col());
03190   }
03191 
03192   editIsRunning = false;
03193 }
03194 
03195 void KateViewInternal::editSetCursor (const KateTextCursor &cursor)
03196 {
03197   if (this->cursor != cursor)
03198   {
03199     this->cursor.setPos (cursor);
03200   }
03201 }
03202 // END
03203 
03204 void KateViewInternal::docSelectionChanged ()
03205 {
03206   if (!m_doc->hasSelection())
03207     selectAnchor.setPos (-1, -1);
03208 }
03209 
03210 // BEGIN KateScrollBar
03211 KateScrollBar::KateScrollBar (Orientation orientation, KateViewInternal* parent, const char* name)
03212   : QScrollBar (orientation, parent->m_view, name)
03213   , m_middleMouseDown (false)
03214   , m_view(parent->m_view)
03215   , m_doc(parent->m_doc)
03216   , m_viewInternal(parent)
03217   , m_topMargin(-1)
03218   , m_bottomMargin(-1)
03219   , m_savVisibleLines(0)
03220   , m_showMarks(false)
03221 {
03222   connect(this, SIGNAL(valueChanged(int)), SLOT(sliderMaybeMoved(int)));
03223   connect(m_doc, SIGNAL(marksChanged()), this, SLOT(marksChanged()));
03224 
03225   m_lines.setAutoDelete(true);
03226 }
03227 
03228 void KateScrollBar::mousePressEvent(QMouseEvent* e)
03229 {
03230   if (e->button() == MidButton)
03231     m_middleMouseDown = true;
03232 
03233   QScrollBar::mousePressEvent(e);
03234 
03235   redrawMarks();
03236 }
03237 
03238 void KateScrollBar::mouseReleaseEvent(QMouseEvent* e)
03239 {
03240   QScrollBar::mouseReleaseEvent(e);
03241 
03242   m_middleMouseDown = false;
03243 
03244   redrawMarks();
03245 }
03246 
03247 void KateScrollBar::mouseMoveEvent(QMouseEvent* e)
03248 {
03249   QScrollBar::mouseMoveEvent(e);
03250 
03251   if (e->state() | LeftButton)
03252     redrawMarks();
03253 }
03254 
03255 void KateScrollBar::paintEvent(QPaintEvent *e)
03256 {
03257   QScrollBar::paintEvent(e);
03258   redrawMarks();
03259 }
03260 
03261 void KateScrollBar::resizeEvent(QResizeEvent *e)
03262 {
03263   QScrollBar::resizeEvent(e);
03264   recomputeMarksPositions();
03265 }
03266 
03267 void KateScrollBar::styleChange(QStyle &s)
03268 {
03269   QScrollBar::styleChange(s);
03270   m_topMargin = -1;
03271   recomputeMarksPositions();
03272 }
03273 
03274 void KateScrollBar::valueChange()
03275 {
03276   QScrollBar::valueChange();
03277   redrawMarks();
03278 }
03279 
03280 void KateScrollBar::rangeChange()
03281 {
03282   QScrollBar::rangeChange();
03283   recomputeMarksPositions();
03284 }
03285 
03286 void KateScrollBar::marksChanged()
03287 {
03288   recomputeMarksPositions(true);
03289 }
03290 
03291 void KateScrollBar::redrawMarks()
03292 {
03293   if (!m_showMarks)
03294     return;
03295 
03296   QPainter painter(this);
03297   QRect rect = sliderRect();
03298   for (QIntDictIterator<QColor> it(m_lines); it.current(); ++it)
03299   {
03300     if (it.currentKey() < rect.top() || it.currentKey() > rect.bottom())
03301     {
03302       painter.setPen(*it.current());
03303       painter.drawLine(0, it.currentKey(), width(), it.currentKey());
03304     }
03305   }
03306 }
03307 
03308 void KateScrollBar::recomputeMarksPositions(bool forceFullUpdate)
03309 {
03310   if (m_topMargin == -1)
03311     watchScrollBarSize();
03312 
03313   m_lines.clear();
03314   m_savVisibleLines = m_doc->visibleLines();
03315 
03316   int realHeight = frameGeometry().height() - m_topMargin - m_bottomMargin;
03317 
03318   QPtrList<KTextEditor::Mark> marks = m_doc->marks();
03319   KateCodeFoldingTree *tree = m_doc->foldingTree();
03320 
03321   for (KTextEditor::Mark *mark = marks.first(); mark; mark = marks.next())
03322   {
03323     uint line = mark->line;
03324 
03325     if (tree)
03326     {
03327       KateCodeFoldingNode *node = tree->findNodeForLine(line);
03328 
03329       while (node)
03330       {
03331         if (!node->visible)
03332           line = tree->getStartLine(node);
03333         node = node->parentNode;
03334       }
03335     }
03336 
03337     line = m_doc->getVirtualLine(line);
03338 
03339     double d = (double)line / (m_savVisibleLines - 1);
03340     m_lines.insert(m_topMargin + (int)(d * realHeight),
03341                    new QColor(KateRendererConfig::global()->lineMarkerColor((KTextEditor::MarkInterface::MarkTypes)mark->type)));
03342   }
03343 
03344   if (forceFullUpdate)
03345     update();
03346   else
03347     redrawMarks();
03348 }
03349 
03350 void KateScrollBar::watchScrollBarSize()
03351 {
03352   int savMax = maxValue();
03353   setMaxValue(0);
03354   QRect rect = sliderRect();
03355   setMaxValue(savMax);
03356 
03357   m_topMargin = rect.top();
03358   m_bottomMargin = frameGeometry().height() - rect.bottom();
03359 }
03360 
03361 void KateScrollBar::sliderMaybeMoved(int value)
03362 {
03363   if (m_middleMouseDown)
03364     emit sliderMMBMoved(value);
03365 }
03366 
03367 KateTextLine::Ptr KateViewInternal::textLine( int realLine )
03368 {
03369   if (m_usePlainLines)
03370     return m_doc->plainKateTextLine(realLine);
03371   else
03372     return m_doc->kateTextLine(realLine);
03373 }
03374 
03375 // END
03376 
03377 // kate: space-indent on; indent-width 2; replace-tabs on;
KDE Logo
This file is part of the documentation for kate Library Version 3.3.2.
Documentation copyright © 1996-2004 the KDE developers.
Generated on Fri Jul 22 10:18:52 2005 by doxygen 1.3.6 written by Dimitri van Heesch, © 1997-2003