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 
00054 KateViewInternal::KateViewInternal(KateView *view, KateDocument *doc)
00055   : QWidget (view, "", Qt::WStaticContents | Qt::WRepaintNoErase | Qt::WResizeNoErase )
00056   , editSessionNumber (0)
00057   , editIsRunning (false)
00058   , m_view (view)
00059   , m_doc (doc)
00060   , cursor (doc, true, 0, 0, this)
00061   , possibleTripleClick (false)
00062   , m_dummy (0)
00063   , m_startPos(0,0)
00064   , m_oldStartPos(0,0)
00065   , m_madeVisible(false)
00066   , m_shiftKeyPressed (false)
00067   , m_autoCenterLines (false)
00068   , m_columnScrollDisplayed(false)
00069   , m_selChangedByUser (false)
00070   , selectAnchor (-1, -1)
00071   , m_preserveMaxX(false)
00072   , m_currentMaxX(0)
00073   , m_usePlainLines(false)
00074   , m_updatingView(true)
00075   , m_cachedMaxStartPos(-1, -1)
00076   , m_dragScrollTimer(this)
00077   , m_scrollTimer (this)
00078   , m_cursorTimer (this)
00079   , m_textHintTimer (this)
00080   , m_suppressColumnScrollBar(false)
00081   , m_textHintEnabled(false)
00082   , m_textHintMouseX(-1)
00083   , m_textHintMouseY(-1)
00084   , m_imPreeditStartLine(0)
00085   , m_imPreeditStart(0)
00086   , m_imPreeditLength(0)
00087 {
00088   setMinimumSize (0,0);
00089 
00090   // cursor
00091   cursor.setMoveOnInsert (true);
00092 
00093   // invalidate selStartCached, or keyb selection is screwed initially
00094   selStartCached.setLine( -1 );
00095   //
00096   // scrollbar for lines
00097   //
00098   m_lineScroll = new KateScrollBar(QScrollBar::Vertical, this);
00099   m_lineScroll->show();
00100   m_lineScroll->setTracking (true);
00101 
00102   m_lineLayout = new QVBoxLayout();
00103   m_colLayout = new QHBoxLayout();
00104 
00105   m_colLayout->addWidget(m_lineScroll);
00106   m_lineLayout->addLayout(m_colLayout);
00107 
00108   if (!m_view->dynWordWrap())
00109   {
00110     // bottom corner box
00111     m_dummy = new QWidget(m_view);
00112     m_dummy->setFixedHeight(style().scrollBarExtent().width());
00113     m_dummy->show();
00114     m_lineLayout->addWidget(m_dummy);
00115   }
00116 
00117   // Hijack the line scroller's controls, so we can scroll nicely for word-wrap
00118   connect(m_lineScroll, SIGNAL(prevPage()), SLOT(scrollPrevPage()));
00119   connect(m_lineScroll, SIGNAL(nextPage()), SLOT(scrollNextPage()));
00120 
00121   connect(m_lineScroll, SIGNAL(prevLine()), SLOT(scrollPrevLine()));
00122   connect(m_lineScroll, SIGNAL(nextLine()), SLOT(scrollNextLine()));
00123 
00124   connect(m_lineScroll, SIGNAL(sliderMoved(int)), SLOT(scrollLines(int)));
00125   connect(m_lineScroll, SIGNAL(sliderMMBMoved(int)), SLOT(scrollLines(int)));
00126 
00127   // catch wheel events, completing the hijack
00128   m_lineScroll->installEventFilter(this);
00129 
00130   //
00131   // scrollbar for columns
00132   //
00133   m_columnScroll = new QScrollBar(QScrollBar::Horizontal,m_view);
00134   m_columnScroll->hide();
00135   m_columnScroll->setTracking(true);
00136   m_startX = 0;
00137   m_oldStartX = 0;
00138 
00139   connect( m_columnScroll, SIGNAL( valueChanged (int) ),
00140            this, SLOT( scrollColumns (int) ) );
00141 
00142   //
00143   // iconborder ;)
00144   //
00145   leftBorder = new KateIconBorder( this, m_view );
00146   leftBorder->show ();
00147 
00148   connect( leftBorder, SIGNAL(toggleRegionVisibility(unsigned int)),
00149            m_doc->foldingTree(), SLOT(toggleRegionVisibility(unsigned int)));
00150 
00151   connect( doc->foldingTree(), SIGNAL(regionVisibilityChangedAt(unsigned int)),
00152            this, SLOT(slotRegionVisibilityChangedAt(unsigned int)));
00153   connect( doc, SIGNAL(codeFoldingUpdated()),
00154            this, SLOT(slotCodeFoldingChanged()) );
00155 
00156   displayCursor.setPos(0, 0);
00157   cursor.setPos(0, 0);
00158   cXPos = 0;
00159 
00160   setAcceptDrops( true );
00161   setBackgroundMode( NoBackground );
00162 
00163   // event filter
00164   installEventFilter(this);
00165 
00166   // im
00167   setInputMethodEnabled(true);
00168 
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 void KateViewInternal::doReturn()
00896 {
00897   KateTextCursor c = cursor;
00898   m_doc->newLine( c, this );
00899   updateCursor( c );
00900   updateView();
00901 }
00902 
00903 void KateViewInternal::doDelete()
00904 {
00905   m_doc->del( cursor );
00906 }
00907 
00908 void KateViewInternal::doBackspace()
00909 {
00910   m_doc->backspace( cursor );
00911 }
00912 
00913 void KateViewInternal::doPaste()
00914 {
00915   m_doc->paste( m_view );
00916 }
00917 
00918 void KateViewInternal::doTranspose()
00919 {
00920   m_doc->transpose( cursor );
00921 }
00922 
00923 void KateViewInternal::doDeleteWordLeft()
00924 {
00925   wordLeft( true );
00926   m_doc->removeSelectedText();
00927   update();
00928 }
00929 
00930 void KateViewInternal::doDeleteWordRight()
00931 {
00932   wordRight( true );
00933   m_doc->removeSelectedText();
00934   update();
00935 }
00936 
00937 class CalculatingCursor : public KateTextCursor {
00938 public:
00939   CalculatingCursor(KateViewInternal* vi)
00940     : KateTextCursor()
00941     , m_vi(vi)
00942   {
00943     Q_ASSERT(valid());
00944   }
00945 
00946   CalculatingCursor(KateViewInternal* vi, const KateTextCursor& c)
00947     : KateTextCursor(c)
00948     , m_vi(vi)
00949   {
00950     Q_ASSERT(valid());
00951   }
00952 
00953   // This one constrains its arguments to valid positions
00954   CalculatingCursor(KateViewInternal* vi, uint line, uint col)
00955     : KateTextCursor(line, col)
00956     , m_vi(vi)
00957   {
00958     makeValid();
00959   }
00960 
00961 
00962   virtual CalculatingCursor& operator+=( int n ) = 0;
00963 
00964   virtual CalculatingCursor& operator-=( int n ) = 0;
00965 
00966   CalculatingCursor& operator++() { return operator+=( 1 ); }
00967 
00968   CalculatingCursor& operator--() { return operator-=( 1 ); }
00969 
00970   void makeValid() {
00971     m_line = QMAX( 0, QMIN( int( m_vi->m_doc->numLines() - 1 ), line() ) );
00972     if (m_vi->m_doc->wrapCursor())
00973       m_col = QMAX( 0, QMIN( m_vi->m_doc->lineLength( line() ), col() ) );
00974     else
00975       m_col = QMAX( 0, col() );
00976     Q_ASSERT( valid() );
00977   }
00978 
00979   void toEdge( Bias bias ) {
00980     if( bias == left ) m_col = 0;
00981     else if( bias == right ) m_col = m_vi->m_doc->lineLength( line() );
00982   }
00983 
00984   bool atEdge() const { return atEdge( left ) || atEdge( right ); }
00985 
00986   bool atEdge( Bias bias ) const {
00987     switch( bias ) {
00988     case left:  return col() == 0;
00989     case none:  return atEdge();
00990     case right: return col() == m_vi->m_doc->lineLength( line() );
00991     default: Q_ASSERT(false); return false;
00992     }
00993   }
00994 
00995 protected:
00996   bool valid() const {
00997     return line() >= 0 &&
00998             uint( line() ) < m_vi->m_doc->numLines() &&
00999             col() >= 0 &&
01000             (!m_vi->m_doc->wrapCursor() || col() <= m_vi->m_doc->lineLength( line() ));
01001   }
01002   KateViewInternal* m_vi;
01003 };
01004 
01005 class BoundedCursor : public CalculatingCursor {
01006 public:
01007   BoundedCursor(KateViewInternal* vi)
01008     : CalculatingCursor( vi ) {};
01009   BoundedCursor(KateViewInternal* vi, const KateTextCursor& c )
01010     : CalculatingCursor( vi, c ) {};
01011   BoundedCursor(KateViewInternal* vi, uint line, uint col )
01012     : CalculatingCursor( vi, line, col ) {};
01013   virtual CalculatingCursor& operator+=( int n ) {
01014     m_col += n;
01015 
01016     if (n > 0 && m_vi->m_view->dynWordWrap()) {
01017       // Need to constrain to current visible text line for dynamic wrapping mode
01018       if (m_col > m_vi->m_doc->lineLength(m_line)) {
01019         KateLineRange currentRange = m_vi->range(*this);
01020 
01021         int endX;
01022         bool crap;
01023         m_vi->m_view->renderer()->textWidth(m_vi->textLine(m_line), currentRange.startCol, m_vi->width() - currentRange.xOffset(), &crap, &endX);
01024         endX += (m_col - currentRange.endCol + 1) * m_vi->m_view->renderer()->spaceWidth();
01025 
01026         // Constraining if applicable NOTE: some code duplication in KateViewInternal::resize()
01027         if (endX >= m_vi->width() - currentRange.xOffset()) {
01028           m_col -= n;
01029           if ( uint( line() ) < m_vi->m_doc->numLines() - 1 ) {
01030             m_line++;
01031             m_col = 0;
01032           }
01033         }
01034       }
01035 
01036     } else if (n < 0 && col() < 0 && line() > 0 ) {
01037       m_line--;
01038       m_col = m_vi->m_doc->lineLength( line() );
01039     }
01040 
01041     m_col = QMAX( 0, col() );
01042 
01043     Q_ASSERT( valid() );
01044     return *this;
01045   }
01046   virtual CalculatingCursor& operator-=( int n ) {
01047     return operator+=( -n );
01048   }
01049 };
01050 
01051 class WrappingCursor : public CalculatingCursor {
01052 public:
01053   WrappingCursor(KateViewInternal* vi)
01054     : CalculatingCursor( vi) {};
01055   WrappingCursor(KateViewInternal* vi, const KateTextCursor& c )
01056     : CalculatingCursor( vi, c ) {};
01057   WrappingCursor(KateViewInternal* vi, uint line, uint col )
01058     : CalculatingCursor( vi, line, col ) {};
01059 
01060   virtual CalculatingCursor& operator+=( int n ) {
01061     if( n < 0 ) return operator-=( -n );
01062     int len = m_vi->m_doc->lineLength( line() );
01063     if( col() + n <= len ) {
01064       m_col += n;
01065     } else if( uint( line() ) < m_vi->m_doc->numLines() - 1 ) {
01066       n -= len - col() + 1;
01067       m_col = 0;
01068       m_line++;
01069       operator+=( n );
01070     } else {
01071       m_col = len;
01072     }
01073     Q_ASSERT( valid() );
01074     return *this;
01075   }
01076   virtual CalculatingCursor& operator-=( int n ) {
01077     if( n < 0 ) return operator+=( -n );
01078     if( col() - n >= 0 ) {
01079       m_col -= n;
01080     } else if( line() > 0 ) {
01081       n -= col() + 1;
01082       m_line--;
01083       m_col = m_vi->m_doc->lineLength( line() );
01084       operator-=( n );
01085     } else {
01086       m_col = 0;
01087     }
01088     Q_ASSERT( valid() );
01089     return *this;
01090   }
01091 };
01092 
01093 void KateViewInternal::moveChar( Bias bias, bool sel )
01094 {
01095   KateTextCursor c;
01096   if ( m_doc->wrapCursor() ) {
01097     c = WrappingCursor( this, cursor ) += bias;
01098   } else {
01099     c = BoundedCursor( this, cursor ) += bias;
01100   }
01101   updateSelection( c, sel );
01102   updateCursor( c );
01103 }
01104 
01105 void KateViewInternal::cursorLeft(  bool sel ) { moveChar( left,  sel ); }
01106 void KateViewInternal::cursorRight( bool sel ) { moveChar( right, sel ); }
01107 
01108 void KateViewInternal::moveWord( Bias bias, bool sel )
01109 {
01110   // This matches the word-moving in QTextEdit, QLineEdit etc.
01111 
01112   WrappingCursor c( this, cursor );
01113   if( !c.atEdge( bias ) ) {
01114     KateHighlighting* h = m_doc->highlight();
01115 
01116     bool moved = false;
01117     while( !c.atEdge( bias ) && !h->isInWord( m_doc->textLine( c.line() )[ c.col() - (bias == left ? 1 : 0) ] ) )
01118     {
01119       c += bias;
01120       moved = true;
01121     }
01122 
01123     if ( bias != right || !moved )
01124     {
01125       while( !c.atEdge( bias ) &&  h->isInWord( m_doc->textLine( c.line() )[ c.col() - (bias == left ? 1 : 0) ] ) )
01126         c += bias;
01127       if ( bias == right )
01128       {
01129         while ( !c.atEdge( bias ) && m_doc->textLine( c.line() )[ c.col() ].isSpace() )
01130           c+= bias;
01131       }
01132     }
01133 
01134   } else {
01135     c += bias;
01136   }
01137   updateSelection( c, sel );
01138   updateCursor( c );
01139 }
01140 
01141 void KateViewInternal::wordLeft ( bool sel ) { moveWord( left,  sel ); }
01142 void KateViewInternal::wordRight( bool sel ) { moveWord( right, sel ); }
01143 
01144 void KateViewInternal::moveEdge( Bias bias, bool sel )
01145 {
01146   BoundedCursor c( this, cursor );
01147   c.toEdge( bias );
01148   updateSelection( c, sel );
01149   updateCursor( c );
01150 }
01151 
01152 void KateViewInternal::home( bool sel )
01153 {
01154   if (m_view->dynWordWrap() && currentRange().startCol) {
01155     // Allow us to go to the real start if we're already at the start of the view line
01156     if (cursor.col() != currentRange().startCol) {
01157       KateTextCursor c(cursor.line(), currentRange().startCol);
01158       updateSelection( c, sel );
01159       updateCursor( c );
01160       return;
01161     }
01162   }
01163 
01164   if( !(m_doc->configFlags() & KateDocument::cfSmartHome) ) {
01165     moveEdge( left, sel );
01166     return;
01167   }
01168 
01169   KateTextCursor c = cursor;
01170   int lc = textLine( c.line() )->firstChar();
01171 
01172   if( lc < 0 || c.col() == lc ) {
01173     c.setCol(0);
01174   } else {
01175     c.setCol(lc);
01176   }
01177 
01178   updateSelection( c, sel );
01179   updateCursor( c );
01180 }
01181 
01182 void KateViewInternal::end( bool sel )
01183 {
01184   if (m_view->dynWordWrap() && currentRange().wrap) {
01185     // Allow us to go to the real end if we're already at the end of the view line
01186     if (cursor.col() < currentRange().endCol - 1) {
01187       KateTextCursor c(cursor.line(), currentRange().endCol - 1);
01188       updateSelection( c, sel );
01189       updateCursor( c );
01190       return;
01191     }
01192   }
01193 
01194   moveEdge( right, sel );
01195 }
01196 
01197 KateLineRange KateViewInternal::range(int realLine, const KateLineRange* previous)
01198 {
01199   // look at the cache first
01200   if (!m_updatingView && realLine >= lineRanges[0].line && realLine <= lineRanges[lineRanges.count() - 1].line)
01201     for (uint i = 0; i < lineRanges.count(); i++)
01202       if (realLine == lineRanges[i].line)
01203         if (!m_view->dynWordWrap() || (!previous && lineRanges[i].startCol == 0) || (previous && lineRanges[i].startCol == previous->endCol))
01204           return lineRanges[i];
01205 
01206   // Not in the cache, we have to create it
01207   KateLineRange ret;
01208 
01209   KateTextLine::Ptr text = textLine(realLine);
01210   if (!text) {
01211     return KateLineRange();
01212   }
01213 
01214   if (!m_view->dynWordWrap()) {
01215     Q_ASSERT(!previous);
01216     ret.line = realLine;
01217     ret.virtualLine = m_doc->getVirtualLine(realLine);
01218     ret.startCol = 0;
01219     ret.endCol = m_doc->lineLength(realLine);
01220     ret.startX = 0;
01221     ret.endX = m_view->renderer()->textWidth(text, -1);
01222     ret.viewLine = 0;
01223     ret.wrap = false;
01224     return ret;
01225   }
01226 
01227   ret.endCol = (int)m_view->renderer()->textWidth(text, previous ? previous->endCol : 0, width() - (previous ? previous->shiftX : 0), &ret.wrap, &ret.endX);
01228 
01229   Q_ASSERT(ret.endCol > ret.startCol);
01230 
01231   ret.line = realLine;
01232 
01233   if (previous) {
01234     ret.virtualLine = previous->virtualLine;
01235     ret.startCol = previous->endCol;
01236     ret.startX = previous->endX;
01237     ret.endX += previous->endX;
01238     ret.shiftX = previous->shiftX;
01239     ret.viewLine = previous->viewLine + 1;
01240 
01241   } else {
01242     // TODO worthwhile optimising this to get the data out of the initial textWidth call?
01243     if (m_view->config()->dynWordWrapAlignIndent() > 0) {
01244       int pos = text->nextNonSpaceChar(0);
01245 
01246       if (pos > 0)
01247         ret.shiftX = m_view->renderer()->textWidth(text, pos);
01248 
01249       if (ret.shiftX > ((double)width() / 100 * m_view->config()->dynWordWrapAlignIndent()))
01250         ret.shiftX = 0;
01251     }
01252 
01253     ret.virtualLine = m_doc->getVirtualLine(realLine);
01254     ret.startCol = 0;
01255     ret.startX = 0;
01256     ret.viewLine = 0;
01257   }
01258 
01259   return ret;
01260 }
01261 
01262 KateLineRange KateViewInternal::currentRange()
01263 {
01264 //  Q_ASSERT(m_view->dynWordWrap());
01265 
01266   return range(cursor);
01267 }
01268 
01269 KateLineRange KateViewInternal::previousRange()
01270 {
01271   uint currentViewLine = viewLine(cursor);
01272 
01273   if (currentViewLine)
01274     return range(cursor.line(), currentViewLine - 1);
01275   else
01276     return range(m_doc->getRealLine(displayCursor.line() - 1), -1);
01277 }
01278 
01279 KateLineRange KateViewInternal::nextRange()
01280 {
01281   uint currentViewLine = viewLine(cursor) + 1;
01282 
01283   if (currentViewLine >= viewLineCount(cursor.line())) {
01284     currentViewLine = 0;
01285     return range(cursor.line() + 1, currentViewLine);
01286   } else {
01287     return range(cursor.line(), currentViewLine);
01288   }
01289 }
01290 
01291 KateLineRange KateViewInternal::range(const KateTextCursor& realCursor)
01292 {
01293 //  Q_ASSERT(m_view->dynWordWrap());
01294 
01295   KateLineRange thisRange;
01296   bool first = true;
01297 
01298   do {
01299     thisRange = range(realCursor.line(), first ? 0L : &thisRange);
01300     first = false;
01301   } while (thisRange.wrap && !(realCursor.col() >= thisRange.startCol && realCursor.col() < thisRange.endCol) && thisRange.startCol != thisRange.endCol);
01302 
01303   return thisRange;
01304 }
01305 
01306 KateLineRange KateViewInternal::range(uint realLine, int viewLine)
01307 {
01308 //  Q_ASSERT(m_view->dynWordWrap());
01309 
01310   KateLineRange thisRange;
01311   bool first = true;
01312 
01313   do {
01314     thisRange = range(realLine, first ? 0L : &thisRange);
01315     first = false;
01316   } while (thisRange.wrap && viewLine != thisRange.viewLine && thisRange.startCol != thisRange.endCol);
01317 
01318   if (viewLine != -1 && viewLine != thisRange.viewLine)
01319     kdDebug(13030) << "WARNING: viewLine " << viewLine << " of line " << realLine << " does not exist." << endl;
01320 
01321   return thisRange;
01322 }
01323 
01329 uint KateViewInternal::viewLine(const KateTextCursor& realCursor)
01330 {
01331   if (!m_view->dynWordWrap()) return 0;
01332 
01333   if (realCursor.col() == 0) return 0;
01334 
01335   KateLineRange thisRange;
01336   bool first = true;
01337 
01338   do {
01339     thisRange = range(realCursor.line(), first ? 0L : &thisRange);
01340     first = false;
01341   } while (thisRange.wrap && !(realCursor.col() >= thisRange.startCol && realCursor.col() < thisRange.endCol) && thisRange.startCol != thisRange.endCol);
01342 
01343   return thisRange.viewLine;
01344 }
01345 
01346 int KateViewInternal::displayViewLine(const KateTextCursor& virtualCursor, bool limitToVisible)
01347 {
01348   KateTextCursor work = startPos();
01349 
01350   int limit = linesDisplayed();
01351 
01352   // Efficient non-word-wrapped path
01353   if (!m_view->dynWordWrap()) {
01354     int ret = virtualCursor.line() - startLine();
01355     if (limitToVisible && (ret < 0 || ret > limit))
01356       return -1;
01357     else
01358       return ret;
01359   }
01360 
01361   if (work == virtualCursor) {
01362     return 0;
01363   }
01364 
01365   int ret = -viewLine(work);
01366   bool forwards = (work < virtualCursor) ? true : false;
01367 
01368   // FIXME switch to using ranges? faster?
01369   if (forwards) {
01370     while (work.line() != virtualCursor.line()) {
01371       ret += viewLineCount(m_doc->getRealLine(work.line()));
01372       work.setLine(work.line() + 1);
01373       if (limitToVisible && ret > limit)
01374         return -1;
01375     }
01376   } else {
01377     while (work.line() != virtualCursor.line()) {
01378       work.setLine(work.line() - 1);
01379       ret -= viewLineCount(m_doc->getRealLine(work.line()));
01380       if (limitToVisible && ret < 0)
01381         return -1;
01382     }
01383   }
01384 
01385   // final difference
01386   KateTextCursor realCursor = virtualCursor;
01387   realCursor.setLine(m_doc->getRealLine(realCursor.line()));
01388   if (realCursor.col() == -1) realCursor.setCol(m_doc->lineLength(realCursor.line()));
01389   ret += viewLine(realCursor);
01390 
01391   if (limitToVisible && (ret < 0 || ret > limit))
01392     return -1;
01393 
01394   return ret;
01395 }
01396 
01397 uint KateViewInternal::lastViewLine(uint realLine)
01398 {
01399   if (!m_view->dynWordWrap()) return 0;
01400 
01401   KateLineRange thisRange;
01402   bool first = true;
01403 
01404   do {
01405     thisRange = range(realLine, first ? 0L : &thisRange);
01406     first = false;
01407   } while (thisRange.wrap && thisRange.startCol != thisRange.endCol);
01408 
01409   return thisRange.viewLine;
01410 }
01411 
01412 uint KateViewInternal::viewLineCount(uint realLine)
01413 {
01414   return lastViewLine(realLine) + 1;
01415 }
01416 
01417 /*
01418  * This returns the cursor which is offset by (offset) view lines.
01419  * This is the main function which is called by code not specifically dealing with word-wrap.
01420  * The opposite conversion (cursor to offset) can be done with displayViewLine.
01421  *
01422  * The cursors involved are virtual cursors (ie. equivalent to displayCursor)
01423  */
01424 KateTextCursor KateViewInternal::viewLineOffset(const KateTextCursor& virtualCursor, int offset, bool keepX)
01425 {
01426   if (!m_view->dynWordWrap()) {
01427     KateTextCursor ret(QMIN((int)m_doc->visibleLines() - 1, virtualCursor.line() + offset), 0);
01428 
01429     if (ret.line() < 0)
01430       ret.setLine(0);
01431 
01432     if (keepX) {
01433       int realLine = m_doc->getRealLine(ret.line());
01434       ret.setCol(m_doc->lineLength(realLine) - 1);
01435 
01436       if (m_currentMaxX > cXPos)
01437         cXPos = m_currentMaxX;
01438 
01439       if (m_doc->wrapCursor())
01440         cXPos = QMIN(cXPos, (int)m_view->renderer()->textWidth(textLine(realLine), m_doc->lineLength(realLine)));
01441 
01442       m_view->renderer()->textWidth(ret, cXPos);
01443     }
01444 
01445     return ret;
01446   }
01447 
01448   KateTextCursor realCursor = virtualCursor;
01449   realCursor.setLine(m_doc->getRealLine(virtualCursor.line()));
01450 
01451   uint cursorViewLine = viewLine(realCursor);
01452 
01453   int currentOffset = 0;
01454   int virtualLine = 0;
01455 
01456   bool forwards = (offset > 0) ? true : false;
01457 
01458   if (forwards) {
01459     currentOffset = lastViewLine(realCursor.line()) - cursorViewLine;
01460     if (offset <= currentOffset) {
01461       // the answer is on the same line
01462       KateLineRange thisRange = range(realCursor.line(), cursorViewLine + offset);
01463       Q_ASSERT(thisRange.virtualLine == virtualCursor.line());
01464       return KateTextCursor(virtualCursor.line(), thisRange.startCol);
01465     }
01466 
01467     virtualLine = virtualCursor.line() + 1;
01468 
01469   } else {
01470     offset = -offset;
01471     currentOffset = cursorViewLine;
01472     if (offset <= currentOffset) {
01473       // the answer is on the same line
01474       KateLineRange thisRange = range(realCursor.line(), cursorViewLine - offset);
01475       Q_ASSERT(thisRange.virtualLine == virtualCursor.line());
01476       return KateTextCursor(virtualCursor.line(), thisRange.startCol);
01477     }
01478 
01479     virtualLine = virtualCursor.line() - 1;
01480   }
01481 
01482   currentOffset++;
01483 
01484   while (virtualLine >= 0 && virtualLine < (int)m_doc->visibleLines())
01485   {
01486     KateLineRange thisRange;
01487     bool first = true;
01488     int realLine = m_doc->getRealLine(virtualLine);
01489 
01490     do {
01491       thisRange = range(realLine, first ? 0L : &thisRange);
01492       first = false;
01493 
01494       if (offset == currentOffset) {
01495         if (!forwards) {
01496           // We actually want it the other way around
01497           int requiredViewLine = lastViewLine(realLine) - thisRange.viewLine;
01498           if (requiredViewLine != thisRange.viewLine) {
01499             thisRange = range(realLine, requiredViewLine);
01500           }
01501         }
01502 
01503         KateTextCursor ret(virtualLine, thisRange.startCol);
01504 
01505         // keep column position
01506         if (keepX) {
01507           ret.setCol(thisRange.endCol - 1);
01508           KateTextCursor realCursorTemp(m_doc->getRealLine(virtualCursor.line()), virtualCursor.col());
01509           int visibleX = m_view->renderer()->textWidth(realCursorTemp) - range(realCursorTemp).startX;
01510           int xOffset = thisRange.startX;
01511 
01512           if (m_currentMaxX > visibleX)
01513             visibleX = m_currentMaxX;
01514 
01515           cXPos = xOffset + visibleX;
01516 
01517           cXPos = QMIN(cXPos, lineMaxCursorX(thisRange));
01518 
01519           m_view->renderer()->textWidth(ret, cXPos);
01520         }
01521 
01522         return ret;
01523       }
01524 
01525       currentOffset++;
01526 
01527     } while (thisRange.wrap);
01528 
01529     if (forwards)
01530       virtualLine++;
01531     else
01532       virtualLine--;
01533   }
01534 
01535   // Looks like we were asked for something a bit exotic.
01536   // Return the max/min valid position.
01537   if (forwards)
01538     return KateTextCursor(m_doc->visibleLines() - 1, m_doc->lineLength(m_doc->visibleLines() - 1));
01539   else
01540     return KateTextCursor(0, 0);
01541 }
01542 
01543 int KateViewInternal::lineMaxCursorX(const KateLineRange& range)
01544 {
01545   if (!m_doc->wrapCursor() && !range.wrap)
01546     return INT_MAX;
01547 
01548   int maxX = range.endX;
01549 
01550   if (maxX && range.wrap) {
01551     QChar lastCharInLine = textLine(range.line)->getChar(range.endCol - 1);
01552     maxX -= m_view->renderer()->config()->fontMetrics()->width(lastCharInLine);
01553   }
01554 
01555   return maxX;
01556 }
01557 
01558 int KateViewInternal::lineMaxCol(const KateLineRange& range)
01559 {
01560   int maxCol = range.endCol;
01561 
01562   if (maxCol && range.wrap)
01563     maxCol--;
01564 
01565   return maxCol;
01566 }
01567 
01568 void KateViewInternal::cursorUp(bool sel)
01569 {
01570   if (displayCursor.line() == 0 && (!m_view->dynWordWrap() || viewLine(cursor) == 0))
01571     return;
01572 
01573   int newLine = cursor.line(), newCol = 0, xOffset = 0, startCol = 0;
01574   m_preserveMaxX = true;
01575 
01576   if (m_view->dynWordWrap()) {
01577     // Dynamic word wrapping - navigate on visual lines rather than real lines
01578     KateLineRange thisRange = currentRange();
01579     // This is not the first line because that is already simplified out above
01580     KateLineRange pRange = previousRange();
01581 
01582     // Ensure we're in the right spot
01583     Q_ASSERT((cursor.line() == thisRange.line) &&
01584              (cursor.col() >= thisRange.startCol) &&
01585              (!thisRange.wrap || cursor.col() < thisRange.endCol));
01586 
01587     // VisibleX is the distance from the start of the text to the cursor on the current line.
01588     int visibleX = m_view->renderer()->textWidth(cursor) - thisRange.startX;
01589     int currentLineVisibleX = visibleX;
01590 
01591     // Translate to new line
01592     visibleX += thisRange.xOffset();
01593     visibleX -= pRange.xOffset();
01594 
01595     // Limit to >= 0
01596     visibleX = QMAX(0, visibleX);
01597 
01598     startCol = pRange.startCol;
01599     xOffset = pRange.startX;
01600     newLine = pRange.line;
01601 
01602     // Take into account current max X (ie. if the current line was smaller
01603     // than the last definitely specified width)
01604     if (thisRange.xOffset() && !pRange.xOffset() && currentLineVisibleX == 0) // Special case for where xOffset may be > m_currentMaxX
01605       visibleX = m_currentMaxX;
01606     else if (visibleX < m_currentMaxX - pRange.xOffset())
01607       visibleX = m_currentMaxX - pRange.xOffset();
01608 
01609     cXPos = xOffset + visibleX;
01610 
01611     cXPos = QMIN(cXPos, lineMaxCursorX(pRange));
01612 
01613     newCol = QMIN((int)m_view->renderer()->textPos(newLine, visibleX, startCol), lineMaxCol(pRange));
01614 
01615   } else {
01616     newLine = m_doc->getRealLine(displayCursor.line() - 1);
01617 
01618     if ((m_doc->wrapCursor()) && m_currentMaxX > cXPos)
01619       cXPos = m_currentMaxX;
01620   }
01621 
01622   KateTextCursor c(newLine, newCol);
01623   m_view->renderer()->textWidth(c, cXPos);
01624 
01625   updateSelection( c, sel );
01626   updateCursor( c );
01627 }
01628 
01629 void KateViewInternal::cursorDown(bool sel)
01630 {
01631   if ((displayCursor.line() >= (int)m_doc->numVisLines() - 1) && (!m_view->dynWordWrap() || viewLine(cursor) == lastViewLine(cursor.line())))
01632     return;
01633 
01634   int newLine = cursor.line(), newCol = 0, xOffset = 0, startCol = 0;
01635   m_preserveMaxX = true;
01636 
01637   if (m_view->dynWordWrap()) {
01638     // Dynamic word wrapping - navigate on visual lines rather than real lines
01639     KateLineRange thisRange = currentRange();
01640     // This is not the last line because that is already simplified out above
01641     KateLineRange nRange = nextRange();
01642 
01643     // Ensure we're in the right spot
01644     Q_ASSERT((cursor.line() == thisRange.line) &&
01645              (cursor.col() >= thisRange.startCol) &&
01646              (!thisRange.wrap || cursor.col() < thisRange.endCol));
01647 
01648     // VisibleX is the distance from the start of the text to the cursor on the current line.
01649     int visibleX = m_view->renderer()->textWidth(cursor) - thisRange.startX;
01650     int currentLineVisibleX = visibleX;
01651 
01652     // Translate to new line
01653     visibleX += thisRange.xOffset();
01654     visibleX -= nRange.xOffset();
01655 
01656     // Limit to >= 0
01657     visibleX = QMAX(0, visibleX);
01658 
01659     if (!thisRange.wrap) {
01660       newLine = m_doc->getRealLine(displayCursor.line() + 1);
01661     } else {
01662       startCol = thisRange.endCol;
01663       xOffset = thisRange.endX;
01664     }
01665 
01666     // Take into account current max X (ie. if the current line was smaller
01667     // than the last definitely specified width)
01668     if (thisRange.xOffset() && !nRange.xOffset() && currentLineVisibleX == 0) // Special case for where xOffset may be > m_currentMaxX
01669       visibleX = m_currentMaxX;
01670     else if (visibleX < m_currentMaxX - nRange.xOffset())
01671       visibleX = m_currentMaxX - nRange.xOffset();
01672 
01673     cXPos = xOffset + visibleX;
01674 
01675     cXPos = QMIN(cXPos, lineMaxCursorX(nRange));
01676 
01677     newCol = QMIN((int)m_view->renderer()->textPos(newLine, visibleX, startCol), lineMaxCol(nRange));
01678 
01679   } else {
01680     newLine = m_doc->getRealLine(displayCursor.line() + 1);
01681 
01682     if ((m_doc->wrapCursor()) && m_currentMaxX > cXPos)
01683       cXPos = m_currentMaxX;
01684   }
01685 
01686   KateTextCursor c(newLine, newCol);
01687   m_view->renderer()->textWidth(c, cXPos);
01688 
01689   updateSelection(c, sel);
01690   updateCursor(c);
01691 }
01692 
01693 void KateViewInternal::cursorToMatchingBracket( bool sel )
01694 {
01695   KateTextCursor start( cursor ), end;
01696 
01697   if( !m_doc->findMatchingBracket( start, end ) )
01698     return;
01699 
01700   // The cursor is now placed just to the left of the matching bracket.
01701   // If it's an ending bracket, put it to the right (so we can easily
01702   // get back to the original bracket).
01703   if( end > start )
01704     end.setCol(end.col() + 1);
01705 
01706   updateSelection( end, sel );
01707   updateCursor( end );
01708 }
01709 
01710 void KateViewInternal::topOfView( bool sel )
01711 {
01712   KateTextCursor c = viewLineOffset(startPos(), m_minLinesVisible);
01713   updateSelection( c, sel );
01714   updateCursor( c );
01715 }
01716 
01717 void KateViewInternal::bottomOfView( bool sel )
01718 {
01719   // FIXME account for wordwrap
01720   KateTextCursor c = viewLineOffset(endPos(), -m_minLinesVisible);
01721   updateSelection( c, sel );
01722   updateCursor( c );
01723 }
01724 
01725 // lines is the offset to scroll by
01726 void KateViewInternal::scrollLines( int lines, bool sel )
01727 {
01728   KateTextCursor c = viewLineOffset(displayCursor, lines, true);
01729 
01730   // Fix the virtual cursor -> real cursor
01731   c.setLine(m_doc->getRealLine(c.line()));
01732 
01733   updateSelection( c, sel );
01734   updateCursor( c );
01735 }
01736 
01737 // This is a bit misleading... it's asking for the view to be scrolled, not the cursor
01738 void KateViewInternal::scrollUp()
01739 {
01740   KateTextCursor newPos = viewLineOffset(m_startPos, -1);
01741   scrollPos(newPos);
01742 }
01743 
01744 void KateViewInternal::scrollDown()
01745 {
01746   KateTextCursor newPos = viewLineOffset(m_startPos, 1);
01747   scrollPos(newPos);
01748 }
01749 
01750 void KateViewInternal::setAutoCenterLines(int viewLines, bool updateView)
01751 {
01752   m_autoCenterLines = viewLines;
01753   m_minLinesVisible = QMIN(int((linesDisplayed() - 1)/2), m_autoCenterLines);
01754   if (updateView)
01755     KateViewInternal::updateView();
01756 }
01757 
01758 void KateViewInternal::pageUp( bool sel )
01759 {
01760   // remember the view line and x pos
01761   int viewLine = displayViewLine(displayCursor);
01762   bool atTop = (startPos().line() == 0 && startPos().col() == 0);
01763 
01764   // Adjust for an auto-centering cursor
01765   int lineadj = 2 * m_minLinesVisible;
01766   int cursorStart = (linesDisplayed() - 1) - viewLine;
01767   if (cursorStart < m_minLinesVisible)
01768     lineadj -= m_minLinesVisible - cursorStart;
01769 
01770   int linesToScroll = -QMAX( (linesDisplayed() - 1) - lineadj, 0 );
01771   m_preserveMaxX = true;
01772 
01773   // don't scroll the full view in case the scrollbar appears
01774   if (!m_view->dynWordWrap()) {
01775     if (scrollbarVisible(startLine() + linesToScroll + viewLine)) {
01776       if (!m_columnScrollDisplayed) {
01777         linesToScroll++;
01778       }
01779     } else {
01780       if (m_columnScrollDisplayed) {
01781         linesToScroll--;
01782       }
01783     }
01784   }
01785 
01786   if (!m_doc->pageUpDownMovesCursor () && !atTop) {
01787     int xPos = m_view->renderer()->textWidth(cursor) - currentRange().startX;
01788 
01789     KateTextCursor newStartPos = viewLineOffset(startPos(), linesToScroll - 1);
01790     scrollPos(newStartPos);
01791 
01792     // put the cursor back approximately where it was
01793     KateTextCursor newPos = viewLineOffset(newStartPos, viewLine, true);
01794     newPos.setLine(m_doc->getRealLine(newPos.line()));
01795 
01796     KateLineRange newLine = range(newPos);
01797 
01798     if (m_currentMaxX - newLine.xOffset() > xPos)
01799       xPos = m_currentMaxX - newLine.xOffset();
01800 
01801     cXPos = QMIN(newLine.startX + xPos, lineMaxCursorX(newLine));
01802 
01803     m_view->renderer()->textWidth( newPos, cXPos );
01804 
01805     m_preserveMaxX = true;
01806     updateSelection( newPos, sel );
01807     updateCursor(newPos);
01808 
01809   } else {
01810     scrollLines( linesToScroll, sel );
01811   }
01812 }
01813 
01814 void KateViewInternal::pageDown( bool sel )
01815 {
01816   // remember the view line
01817   int viewLine = displayViewLine(displayCursor);
01818   bool atEnd = startPos() >= m_cachedMaxStartPos;
01819 
01820   // Adjust for an auto-centering cursor
01821   int lineadj = 2 * m_minLinesVisible;
01822   int cursorStart = m_minLinesVisible - viewLine;
01823   if (cursorStart > 0)
01824     lineadj -= cursorStart;
01825 
01826   int linesToScroll = QMAX( (linesDisplayed() - 1) - lineadj, 0 );
01827   m_preserveMaxX = true;
01828 
01829   // don't scroll the full view in case the scrollbar appears
01830   if (!m_view->dynWordWrap()) {
01831     if (scrollbarVisible(startLine() + linesToScroll + viewLine - (linesDisplayed() - 1))) {
01832       if (!m_columnScrollDisplayed) {
01833         linesToScroll--;
01834       }
01835     } else {
01836       if (m_columnScrollDisplayed) {
01837         linesToScroll--;
01838       }
01839     }
01840   }
01841 
01842   if (!m_doc->pageUpDownMovesCursor () && !atEnd) {
01843     int xPos = m_view->renderer()->textWidth(cursor) - currentRange().startX;
01844 
01845     KateTextCursor newStartPos = viewLineOffset(startPos(), linesToScroll + 1);
01846     scrollPos(newStartPos);
01847 
01848     // put the cursor back approximately where it was
01849     KateTextCursor newPos = viewLineOffset(newStartPos, viewLine, true);
01850     newPos.setLine(m_doc->getRealLine(newPos.line()));
01851 
01852     KateLineRange newLine = range(newPos);
01853 
01854     if (m_currentMaxX - newLine.xOffset() > xPos)
01855       xPos = m_currentMaxX - newLine.xOffset();
01856 
01857     cXPos = QMIN(newLine.startX + xPos, lineMaxCursorX(newLine));
01858 
01859     m_view->renderer()->textWidth( newPos, cXPos );
01860 
01861     m_preserveMaxX = true;
01862     updateSelection( newPos, sel );
01863     updateCursor(newPos);
01864 
01865   } else {
01866     scrollLines( linesToScroll, sel );
01867   }
01868 }
01869 
01870 bool KateViewInternal::scrollbarVisible(uint startLine)
01871 {
01872   return maxLen(startLine) > width() - 8;
01873 }
01874 
01875 int KateViewInternal::maxLen(uint startLine)
01876 {
01877 //  Q_ASSERT(!m_view->dynWordWrap());
01878 
01879   int displayLines = (m_view->height() / m_view->renderer()->fontHeight()) + 1;
01880 
01881   int maxLen = 0;
01882 
01883   for (int z = 0; z < displayLines; z++) {
01884     int virtualLine = startLine + z;
01885 
01886     if (virtualLine < 0 || virtualLine >= (int)m_doc->visibleLines())
01887       break;
01888 
01889     KateLineRange thisRange = range((int)m_doc->getRealLine(virtualLine));
01890 
01891     maxLen = QMAX(maxLen, thisRange.endX);
01892   }
01893 
01894   return maxLen;
01895 }
01896 
01897 void KateViewInternal::top( bool sel )
01898 {
01899   KateTextCursor c( 0, cursor.col() );
01900   m_view->renderer()->textWidth( c, cXPos );
01901   updateSelection( c, sel );
01902   updateCursor( c );
01903 }
01904 
01905 void KateViewInternal::bottom( bool sel )
01906 {
01907   KateTextCursor c( m_doc->lastLine(), cursor.col() );
01908   m_view->renderer()->textWidth( c, cXPos );
01909   updateSelection( c, sel );
01910   updateCursor( c );
01911 }
01912 
01913 void KateViewInternal::top_home( bool sel )
01914 {
01915   KateTextCursor c( 0, 0 );
01916   updateSelection( c, sel );
01917   updateCursor( c );
01918 }
01919 
01920 void KateViewInternal::bottom_end( bool sel )
01921 {
01922   KateTextCursor c( m_doc->lastLine(), m_doc->lineLength( m_doc->lastLine() ) );
01923   updateSelection( c, sel );
01924   updateCursor( c );
01925 }
01926 
01927 void KateViewInternal::updateSelection( const KateTextCursor& _newCursor, bool keepSel )
01928 {
01929   KateTextCursor newCursor = _newCursor;
01930   if( keepSel )
01931   {
01932     if ( !m_doc->hasSelection() || (selectAnchor.line() == -1)
01933          || ((m_doc->configFlags() & KateDocument::cfPersistent)
01934              && ((cursor < m_doc->selectStart) || (cursor > m_doc->selectEnd))) )
01935     {
01936       selectAnchor = cursor;
01937       m_doc->setSelection( cursor, newCursor );
01938     }
01939     else
01940     {
01941       bool doSelect = true;
01942       switch (m_selectionMode)
01943       {
01944         case Word:
01945         {
01946           bool same = ( newCursor.line() == selStartCached.line() );
01947           uint c;
01948           if ( newCursor.line() > selStartCached.line() ||
01949                ( same && newCursor.col() > selEndCached.col() ) )
01950           {
01951             selectAnchor = selStartCached;
01952 
01953             KateTextLine::Ptr l = m_doc->kateTextLine( newCursor.line() );
01954 
01955             for ( c = newCursor.col(); c < l->length(); c++ )
01956               if ( !m_doc->m_highlight->isInWord( l->getChar( c ) ) )
01957                 break;
01958 
01959             newCursor.setCol( c );
01960           }
01961           else if ( newCursor.line() < selStartCached.line() ||
01962                ( same && newCursor.col() < selStartCached.col() ) )
01963           {
01964             selectAnchor = selEndCached;
01965 
01966             KateTextLine::Ptr l = m_doc->kateTextLine( newCursor.line() );
01967 
01968             for ( c = newCursor.col(); c > 0; c-- )
01969               if ( !m_doc->m_highlight->isInWord( l->getChar( c ) ) )
01970                 break;
01971 
01972             newCursor.setCol( c+1 );
01973           }
01974           else
01975             doSelect = false;
01976 
01977         }
01978         break;
01979         case Line:
01980           if ( newCursor.line() > selStartCached.line() )
01981           {
01982             selectAnchor = selStartCached;
01983             newCursor.setCol( m_doc->textLine( newCursor.line() ).length() );
01984           }
01985           else if ( newCursor.line() < selStartCached.line() )
01986           {
01987             selectAnchor = selEndCached;
01988             newCursor.setCol( 0 );
01989           }
01990           else // same line, ignore
01991             doSelect = false;
01992         break;
01993         default: // *allways* keep original selection for mouse
01994         {
01995           if ( selStartCached.line() < 0 ) // invalid
01996             break;
01997 
01998           if ( newCursor.line() > selEndCached.line() ||
01999                ( newCursor.line() == selEndCached.line() &&
02000                  newCursor.col() > selEndCached.col() ) )
02001             selectAnchor = selStartCached;
02002 
02003           else if ( newCursor.line() < selStartCached.line() ||
02004                ( newCursor.line() == selStartCached.line() &&
02005                  newCursor.col() < selStartCached.col() ) )
02006             selectAnchor = selEndCached;
02007 
02008           else
02009             doSelect = false;
02010         }
02011 //         break;
02012       }
02013 
02014       if ( doSelect )
02015         m_doc->setSelection( selectAnchor, newCursor);
02016       else if ( selStartCached.line() > 0 ) // we have a cached selectino, so we restore that
02017         m_doc->setSelection( selStartCached, selEndCached );
02018     }
02019 
02020     m_selChangedByUser = true;
02021   }
02022   else if ( !(m_doc->configFlags() & KateDocument::cfPersistent) )
02023     m_doc->clearSelection();
02024 }
02025 
02026 void KateViewInternal::updateCursor( const KateTextCursor& newCursor, bool force, bool center, bool calledExternally )
02027 {
02028   KateTextLine::Ptr l = textLine( newCursor.line() );
02029 
02030   if ( !force && (cursor == newCursor) )
02031   {
02032     if ( !m_madeVisible )
02033     {
02034       // unfold if required
02035       if ( l && ! l->isVisible() )
02036         m_doc->foldingTree()->ensureVisible( newCursor.line() );
02037 
02038       makeVisible ( displayCursor, displayCursor.col(), false, center, calledExternally );
02039     }
02040 
02041     return;
02042   }
02043 
02044   // remove trailing spaces ### really not nice here, unless it is *really* nessecary
02045 //   if ( m_doc->isReadWrite() && cursor.line() != newCursor.line() )
02046 //     m_doc->removeTrailingSpace( cursor.line() );
02047 
02048   // unfold if required
02049   if ( l && ! l->isVisible() )
02050     m_doc->foldingTree()->ensureVisible( newCursor.line() );
02051 
02052   KateTextCursor oldDisplayCursor = displayCursor;
02053 
02054   cursor.setPos (newCursor);
02055   displayCursor.setPos (m_doc->getVirtualLine(cursor.line()), cursor.col());
02056 
02057   cXPos = m_view->renderer()->textWidth( cursor );
02058   makeVisible ( displayCursor, displayCursor.col(), false, center, calledExternally );
02059 
02060   updateBracketMarks();
02061 
02062   // It's efficient enough to just tag them both without checking to see if they're on the same view line
02063   tagLine(oldDisplayCursor);
02064   tagLine(displayCursor);
02065 
02066   QPoint cursorP = cursorCoordinates();
02067   setMicroFocusHint( cursorP.x(), cursorP.y(), 0, m_view->renderer()->fontHeight() );
02068 
02069   if (m_cursorTimer.isActive ())
02070   {
02071     if ( KApplication::cursorFlashTime() > 0 )
02072       m_cursorTimer.start( KApplication::cursorFlashTime() / 2 );
02073     m_view->renderer()->setDrawCaret(true);
02074   }
02075 
02076   // Remember the maximum X position if requested
02077   if (m_preserveMaxX)
02078     m_preserveMaxX = false;
02079   else
02080     if (m_view->dynWordWrap())
02081       m_currentMaxX = m_view->renderer()->textWidth(displayCursor) - currentRange().startX + currentRange().xOffset();
02082     else
02083       m_currentMaxX = cXPos;
02084 
02085   //kdDebug() << "m_currentMaxX: " << m_currentMaxX << " (was "<< oldmaxx << "), cXPos: " << cXPos << endl;
02086   //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;
02087 
02088   paintText(0, 0, width(), height(), true);
02089 
02090   emit m_view->cursorPositionChanged();
02091 }
02092 
02093 void KateViewInternal::updateBracketMarks()
02094 {
02095   if ( bm.isValid() ) {
02096     KateTextCursor bmStart(m_doc->getVirtualLine(bm.start().line()), bm.start().col());
02097     KateTextCursor bmEnd(m_doc->getVirtualLine(bm.end().line()), bm.end().col());
02098     tagLine(bmStart);
02099     tagLine(bmEnd);
02100   }
02101 
02102   m_doc->newBracketMark( cursor, bm );
02103 
02104   if ( bm.isValid() ) {
02105     KateTextCursor bmStart(m_doc->getVirtualLine(bm.start().line()), bm.start().col());
02106     KateTextCursor bmEnd(m_doc->getVirtualLine(bm.end().line()), bm.end().col());
02107     tagLine(bmStart);
02108     tagLine(bmEnd);
02109   }
02110 }
02111 
02112 bool KateViewInternal::tagLine(const KateTextCursor& virtualCursor)
02113 {
02114   int viewLine = displayViewLine(virtualCursor, true);
02115   if (viewLine >= 0 && viewLine < (int)lineRanges.count()) {
02116     lineRanges[viewLine].dirty = true;
02117     leftBorder->update (0, lineToY(viewLine), leftBorder->width(), m_view->renderer()->fontHeight());
02118     return true;
02119   }
02120   return false;
02121 }
02122 
02123 bool KateViewInternal::tagLines( int start, int end, bool realLines )
02124 {
02125   return tagLines(KateTextCursor(start, 0), KateTextCursor(end, -1), realLines);
02126 }
02127 
02128 bool KateViewInternal::tagLines(KateTextCursor start, KateTextCursor end, bool realCursors)
02129 {
02130   if (realCursors)
02131   {
02132     //kdDebug()<<"realLines is true"<<endl;
02133     start.setLine(m_doc->getVirtualLine( start.line() ));
02134     end.setLine(m_doc->getVirtualLine( end.line() ));
02135   }
02136 
02137   if (end.line() < (int)startLine())
02138   {
02139     //kdDebug()<<"end<startLine"<<endl;
02140     return false;
02141   }
02142   if (start.line() > (int)endLine())
02143   {
02144     //kdDebug()<<"start> endLine"<<start<<" "<<((int)endLine())<<endl;
02145     return false;
02146   }
02147 
02148   //kdDebug(13030) << "tagLines( [" << start.line << "," << start.col << "], [" << end.line << "," << end.col << "] )\n";
02149 
02150   bool ret = false;
02151 
02152   for (uint z = 0; z < lineRanges.size(); z++)
02153   {
02154     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)))) {
02155       ret = lineRanges[z].dirty = true;
02156       //kdDebug() << "Tagged line " << lineRanges[z].line << endl;
02157     }
02158   }
02159 
02160   if (!m_view->dynWordWrap())
02161   {
02162     int y = lineToY( start.line() );
02163     // FIXME is this enough for when multiple lines are deleted
02164     int h = (end.line() - start.line() + 2) * m_view->renderer()->fontHeight();
02165     if (end.line() == (int)m_doc->numVisLines() - 1)
02166       h = height();
02167 
02168     leftBorder->update (0, y, leftBorder->width(), h);
02169   }
02170   else
02171   {
02172     // FIXME Do we get enough good info in editRemoveText to optimise this more?
02173     //bool justTagged = false;
02174     for (uint z = 0; z < lineRanges.size(); z++)
02175     {
02176       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))))
02177       {
02178         //justTagged = true;
02179         leftBorder->update (0, z * m_view->renderer()->fontHeight(), leftBorder->width(), leftBorder->height());
02180         break;
02181       }
02182       /*else if (justTagged)
02183       {
02184         justTagged = false;
02185         leftBorder->update (0, z * m_doc->viewFont.fontHeight, leftBorder->width(), m_doc->viewFont.fontHeight);
02186         break;
02187       }*/
02188     }
02189   }
02190 
02191   return ret;
02192 }
02193 
02194 void KateViewInternal::tagAll()
02195 {
02196   //kdDebug(13030) << "tagAll()" << endl;
02197   for (uint z = 0; z < lineRanges.size(); z++)
02198   {
02199       lineRanges[z].dirty = true;
02200   }
02201 
02202   leftBorder->updateFont();
02203   leftBorder->update ();
02204 }
02205 
02206 void KateViewInternal::paintCursor()
02207 {
02208   if (tagLine(displayCursor))
02209     paintText (0,0,width(), height(), true);
02210 }
02211 
02212 // Point in content coordinates
02213 void KateViewInternal::placeCursor( const QPoint& p, bool keepSelection, bool updateSelection )
02214 {
02215   KateLineRange thisRange = yToKateLineRange(p.y());
02216 
02217   if (thisRange.line == -1) {
02218     for (int i = (p.y() / m_view->renderer()->fontHeight()); i >= 0; i--) {
02219       thisRange = lineRanges[i];
02220       if (thisRange.line != -1)
02221         break;
02222     }
02223     Q_ASSERT(thisRange.line != -1);
02224   }
02225 
02226   int realLine = thisRange.line;
02227   int visibleLine = thisRange.virtualLine;
02228   uint startCol = thisRange.startCol;
02229 
02230   visibleLine = QMAX( 0, QMIN( visibleLine, int(m_doc->numVisLines()) - 1 ) );
02231 
02232   KateTextCursor c(realLine, 0);
02233 
02234   int x = QMIN(QMAX(0, p.x() - thisRange.xOffset()), lineMaxCursorX(thisRange) - thisRange.startX);
02235 
02236   m_view->renderer()->textWidth( c, startX() + x, startCol);
02237 
02238   if (updateSelection)
02239     KateViewInternal::updateSelection( c, keepSelection );
02240   updateCursor( c );
02241 }
02242 
02243 // Point in content coordinates
02244 bool KateViewInternal::isTargetSelected( const QPoint& p )
02245 {
02246   KateLineRange thisRange = yToKateLineRange(p.y());
02247 
02248   KateTextLine::Ptr l = textLine( thisRange.line );
02249   if( !l )
02250     return false;
02251 
02252   int col = m_view->renderer()->textPos( l, p.x() - thisRange.xOffset(), thisRange.startCol, false );
02253 
02254   return m_doc->lineColSelected( thisRange.line, col );
02255 }
02256 
02257 //
02258 // BEGIN EVENT HANDLING STUFF !!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!
02259 //
02260 
02261 bool KateViewInternal::eventFilter( QObject *obj, QEvent *e )
02262 {
02263   if (obj == m_lineScroll)
02264   {
02265     // the second condition is to make sure a scroll on the vertical bar doesn't cause a horizontal scroll ;)
02266     if (e->type() == QEvent::Wheel && m_lineScroll->minValue() != m_lineScroll->maxValue())
02267     {
02268       wheelEvent((QWheelEvent*)e);
02269       return true;
02270     }
02271 
02272     // continue processing
02273     return QWidget::eventFilter( obj, e );
02274   }
02275 
02276   switch( e->type() )
02277   {
02278     case QEvent::KeyPress:
02279     {
02280       QKeyEvent *k = (QKeyEvent *)e;
02281 
02282       if ((k->key() == Qt::Key_Escape) && !(m_doc->configFlags() & KateDocument::cfPersistent) )
02283       {
02284         m_doc->clearSelection();
02285         return true;
02286       }
02287       else if ( !((k->state() & ControlButton) || (k->state() & AltButton)) )
02288       {
02289         keyPressEvent( k );
02290         return k->isAccepted();
02291       }
02292 
02293     } break;
02294 
02295     case QEvent::DragMove:
02296     {
02297       QPoint currentPoint = ((QDragMoveEvent*) e)->pos();
02298 
02299       QRect doNotScrollRegion( scrollMargin, scrollMargin,
02300                           width() - scrollMargin * 2,
02301                           height() - scrollMargin * 2 );
02302 
02303       if ( !doNotScrollRegion.contains( currentPoint ) )
02304       {
02305           startDragScroll();
02306           // Keep sending move events
02307           ( (QDragMoveEvent*)e )->accept( QRect(0,0,0,0) );
02308       }
02309 
02310       dragMoveEvent((QDragMoveEvent*)e);
02311     } break;
02312 
02313     case QEvent::DragLeave:
02314       stopDragScroll();
02315       break;
02316 
02317     default:
02318       break;
02319   }
02320 
02321   return QWidget::eventFilter( obj, e );
02322 }
02323 
02324 void KateViewInternal::keyPressEvent( QKeyEvent* e )
02325 {
02326   KKey key(e);
02327 
02328    if (key == Qt::Key_Left)
02329   {
02330     m_view->cursorLeft();
02331     e->accept();
02332     return;
02333   }
02334 
02335   if (key == Qt::Key_Right)
02336   {
02337     m_view->cursorRight();
02338     e->accept();
02339     return;
02340   }
02341 
02342   if (key == Qt::Key_Down)
02343   {
02344     m_view->down();
02345     e->accept();
02346     return;
02347   }
02348 
02349   if (key == Qt::Key_Up)
02350   {
02351     m_view->up();
02352     e->accept();
02353     return;
02354   }
02355 
02356   if( !m_doc->isReadWrite() )
02357   {
02358     e->ignore();
02359     return;
02360   }
02361 
02362   if ((key == Qt::Key_Return) || (key == Qt::Key_Enter))
02363   {
02364     m_view->keyReturn();
02365     e->accept();
02366     return;
02367   }
02368 
02369   if ((key == SHIFT + Qt::Key_Return) || (key == SHIFT + Qt::Key_Enter))
02370   {
02371     uint ln = cursor.line();
02372     KateTextLine::Ptr line = m_doc->kateTextLine( ln );
02373     int pos = line->firstChar();
02374     if (pos != -1) {
02375       while ((int)line->length() > pos && !line->getChar(pos).isLetterOrNumber()) ++pos;
02376     } else {
02377       pos = line->length(); // stay indented
02378     }
02379     m_doc->insertText( cursor.line(), line->length(), "\n" +  line->string(0, pos) );
02380     cursor.setPos(ln + 1, pos);
02381     updateCursor(cursor, true);
02382     updateView();
02383     e->accept();
02384     return;
02385   }
02386 
02387   if (key == Qt::Key_Backspace || key == SHIFT + Qt::Key_Backspace)
02388   {
02389     m_view->backspace();
02390     e->accept();
02391     return;
02392   }
02393 
02394   if (key == Qt::Key_Delete)
02395   {
02396     m_view->keyDelete();
02397     e->accept();
02398     return;
02399   }
02400 
02401   if( (key == Qt::Key_Tab || key == SHIFT+Qt::Key_Backtab || key == Qt::Key_Backtab)
02402       && (m_doc->configFlags() & KateDocumentConfig::cfTabIndents) )
02403   {
02404     if( key == Qt::Key_Tab )
02405     {
02406       if (m_doc->hasSelection() || (m_doc->configFlags() & KateDocumentConfig::cfTabIndentsMode))
02407         m_doc->indent( m_view, cursor.line(), 1 );
02408       else if (m_doc->configFlags() & KateDocumentConfig::cfTabInsertsTab)
02409         m_doc->typeChars ( m_view, QString ("\t") );
02410       else
02411         m_doc->insertIndentChars ( m_view );
02412 
02413       e->accept();
02414       return;
02415     }
02416 
02417     if (key == SHIFT+Qt::Key_Backtab || key == Qt::Key_Backtab)
02418     {
02419       m_doc->indent( m_view, cursor.line(), -1 );
02420       e->accept();
02421       return;
02422     }
02423   }
02424 
02425   if ( !(e->state() & ControlButton) && !(e->state() & AltButton)
02426        && m_doc->typeChars ( m_view, e->text() ) )
02427   {
02428     e->accept();
02429     return;
02430   }
02431 
02432   e->ignore();
02433 }
02434 
02435 void KateViewInternal::keyReleaseEvent( QKeyEvent* e )
02436 {
02437   KKey key(e);
02438 
02439   if (key == SHIFT)
02440     m_shiftKeyPressed = true;
02441   else
02442   {
02443     if (m_shiftKeyPressed)
02444     {
02445       m_shiftKeyPressed = false;
02446 
02447       if (m_selChangedByUser)
02448       {
02449         QApplication::clipboard()->setSelectionMode( true );
02450         m_doc->copy();
02451         QApplication::clipboard()->setSelectionMode( false );
02452 
02453         m_selChangedByUser = false;
02454       }
02455     }
02456   }
02457 
02458   e->ignore();
02459   return;
02460 }
02461 
02462 void KateViewInternal::mousePressEvent( QMouseEvent* e )
02463 {
02464   switch (e->button())
02465   {
02466     case LeftButton:
02467         m_selChangedByUser = false;
02468 
02469         if (possibleTripleClick)
02470         {
02471           possibleTripleClick = false;
02472 
02473           m_selectionMode = Line;
02474 
02475           if ( e->state() & Qt::ShiftButton )
02476           {
02477             updateSelection( cursor, true );
02478           }
02479           else
02480           {
02481             m_doc->selectLine( cursor );
02482           }
02483 
02484           QApplication::clipboard()->setSelectionMode( true );
02485           m_doc->copy();
02486           QApplication::clipboard()->setSelectionMode( false );
02487 
02488           selStartCached = m_doc->selectStart;
02489           selEndCached = m_doc->selectEnd;
02490 
02491           cursor.setCol(0);
02492           updateCursor( cursor );
02493           return;
02494         }
02495 
02496         if ( e->state() & Qt::ShiftButton )
02497         {
02498           selStartCached = m_doc->selectStart;
02499           selEndCached = m_doc->selectEnd;
02500         }
02501         else
02502           selStartCached.setLine( -1 ); // invalidate
02503 
02504         if( isTargetSelected( e->pos() ) )
02505         {
02506           dragInfo.state = diPending;
02507           dragInfo.start = e->pos();
02508         }
02509         else
02510         {
02511           dragInfo.state = diNone;
02512 
02513           placeCursor( e->pos(), e->state() & ShiftButton );
02514 
02515           scrollX = 0;
02516           scrollY = 0;
02517 
02518           m_scrollTimer.start (50);
02519         }
02520 
02521         e->accept ();
02522         break;
02523 
02524     // try to show popup menu
02525     case RightButton:
02526       if ( ! isTargetSelected( e->pos() ) )
02527         placeCursor( e->pos() );
02528 
02529       // popup is a qguardedptr now
02530       if (m_view->popup())
02531         m_view->popup()->popup( mapToGlobal( e->pos() ) );
02532 
02533       e->accept ();
02534       break;
02535 
02536     default:
02537       e->ignore ();
02538       break;
02539   }
02540 }
02541 
02542 void KateViewInternal::mouseDoubleClickEvent(QMouseEvent *e)
02543 {
02544   switch (e->button())
02545   {
02546     case LeftButton:
02547       m_selectionMode = Word;
02548 
02549       if ( e->state() & Qt::ShiftButton )
02550       {
02551         selStartCached = m_doc->selectStart;
02552         selEndCached = m_doc->selectEnd;
02553         updateSelection( cursor, true );
02554       }
02555       else
02556       {
02557         m_doc->selectWord( cursor );
02558       }
02559 
02560       // Move cursor to end of selected word
02561       if (m_doc->hasSelection())
02562       {
02563         QApplication::clipboard()->setSelectionMode( true );
02564         m_doc->copy();
02565         QApplication::clipboard()->setSelectionMode( false );
02566 
02567         cursor.setPos(m_doc->selectEnd);
02568         updateCursor( cursor );
02569 
02570         selStartCached = m_doc->selectStart;
02571         selEndCached = m_doc->selectEnd;
02572       }
02573 
02574       possibleTripleClick = true;
02575       QTimer::singleShot ( QApplication::doubleClickInterval(), this, SLOT(tripleClickTimeout()) );
02576 
02577       e->accept ();
02578       break;
02579 
02580     default:
02581       e->ignore ();
02582       break;
02583   }
02584 }
02585 
02586 void KateViewInternal::tripleClickTimeout()
02587 {
02588   possibleTripleClick = false;
02589 }
02590 
02591 void KateViewInternal::mouseReleaseEvent( QMouseEvent* e )
02592 {
02593   switch (e->button())
02594   {
02595     case LeftButton:
02596       m_selectionMode = Default;
02597       selStartCached.setLine( -1 );
02598 
02599       if (m_selChangedByUser)
02600       {
02601         QApplication::clipboard()->setSelectionMode( true );
02602         m_doc->copy();
02603         QApplication::clipboard()->setSelectionMode( false );
02604 
02605         m_selChangedByUser = false;
02606       }
02607 
02608       if (dragInfo.state == diPending)
02609         placeCursor( e->pos() );
02610       else if (dragInfo.state == diNone)
02611         m_scrollTimer.stop ();
02612 
02613       dragInfo.state = diNone;
02614 
02615       e->accept ();
02616       break;
02617 
02618     case MidButton:
02619       placeCursor( e->pos() );
02620 
02621       if( m_doc->isReadWrite() )
02622       {
02623         QApplication::clipboard()->setSelectionMode( true );
02624         doPaste();
02625         QApplication::clipboard()->setSelectionMode( false );
02626       }
02627 
02628       e->accept ();
02629       break;
02630 
02631     default:
02632       e->ignore ();
02633       break;
02634   }
02635 }
02636 
02637 void KateViewInternal::mouseMoveEvent( QMouseEvent* e )
02638 {
02639   if( e->state() & LeftButton )
02640   {
02641     if (dragInfo.state == diPending)
02642     {
02643       // we had a mouse down, but haven't confirmed a drag yet
02644       // if the mouse has moved sufficiently, we will confirm
02645       QPoint p( e->pos() - dragInfo.start );
02646 
02647       // we've left the drag square, we can start a real drag operation now
02648       if( p.manhattanLength() > KGlobalSettings::dndEventDelay() )
02649         doDrag();
02650 
02651       return;
02652     }
02653 
02654     mouseX = e->x();
02655     mouseY = e->y();
02656 
02657     scrollX = 0;
02658     scrollY = 0;
02659     int d = m_view->renderer()->fontHeight();
02660 
02661     if (mouseX < 0)
02662       scrollX = -d;
02663 
02664     if (mouseX > width())
02665       scrollX = d;
02666 
02667     if (mouseY < 0)
02668     {
02669       mouseY = 0;
02670       scrollY = -d;
02671     }
02672 
02673     if (mouseY > height())
02674     {
02675       mouseY = height();
02676       scrollY = d;
02677     }
02678 
02679     placeCursor( QPoint( mouseX, mouseY ), true );
02680 
02681   }
02682   else
02683   {
02684     if (m_textHintEnabled)
02685     {
02686        m_textHintTimer.start(m_textHintTimeout);
02687        m_textHintMouseX=e->x();
02688        m_textHintMouseY=e->y();
02689     }
02690   }
02691 }
02692 
02693 void KateViewInternal::paintEvent(QPaintEvent *e)
02694 {
02695   paintText(e->rect().x(), e->rect().y(), e->rect().width(), e->rect().height());
02696 }
02697 
02698 void KateViewInternal::resizeEvent(QResizeEvent* e)
02699 {
02700   bool expandedHorizontally = width() > e->oldSize().width();
02701   bool expandedVertically = height() > e->oldSize().height();
02702   bool heightChanged = height() != e->oldSize().height();
02703 
02704   m_madeVisible = false;
02705 
02706   if (heightChanged) {
02707     setAutoCenterLines(m_autoCenterLines, false);
02708     m_cachedMaxStartPos.setPos(-1, -1);
02709   }
02710 
02711   if (m_view->dynWordWrap()) {
02712     bool dirtied = false;
02713 
02714     for (uint i = 0; i < lineRanges.count(); i++) {
02715       // find the first dirty line
02716       // the word wrap updateView algorithm is forced to check all lines after a dirty one
02717       if (lineRanges[i].wrap ||
02718          (!expandedHorizontally && (lineRanges[i].endX - lineRanges[i].startX) > width())) {
02719         dirtied = lineRanges[i].dirty = true;
02720         break;
02721       }
02722     }
02723 
02724     if (dirtied || heightChanged) {
02725       updateView(true);
02726       leftBorder->update();
02727     }
02728 
02729     if (width() < e->oldSize().width()) {
02730       if (!m_doc->wrapCursor()) {
02731         // May have to restrain cursor to new smaller width...
02732         if (cursor.col() > m_doc->lineLength(cursor.line())) {
02733           KateLineRange thisRange = currentRange();
02734 
02735           KateTextCursor newCursor(cursor.line(), thisRange.endCol + ((width() - thisRange.xOffset() - (thisRange.endX - thisRange.startX)) / m_view->renderer()->spaceWidth()) - 1);
02736           updateCursor(newCursor);
02737         }
02738       }
02739     }
02740 
02741   } else {
02742     updateView();
02743 
02744     if (expandedHorizontally && startX() > 0)
02745       scrollColumns(startX() - (width() - e->oldSize().width()));
02746   }
02747 
02748   if (expandedVertically) {
02749     KateTextCursor max = maxStartPos();
02750     if (startPos() > max)
02751       scrollPos(max);
02752   }
02753 }
02754 
02755 void KateViewInternal::scrollTimeout ()
02756 {
02757   if (scrollX || scrollY)
02758   {
02759     scrollLines (startPos().line() + (scrollY / (int)m_view->renderer()->fontHeight()));
02760     placeCursor( QPoint( mouseX, mouseY ), true );
02761   }
02762 }
02763 
02764 void KateViewInternal::cursorTimeout ()
02765 {
02766   m_view->renderer()->setDrawCaret(!m_view->renderer()->drawCaret());
02767   paintCursor();
02768 }
02769 
02770 void KateViewInternal::textHintTimeout ()
02771 {
02772   m_textHintTimer.stop ();
02773 
02774   KateLineRange thisRange = yToKateLineRange(m_textHintMouseY);
02775 
02776   if (thisRange.line == -1) return;
02777 
02778   if (m_textHintMouseX> (lineMaxCursorX(thisRange) - thisRange.startX)) return;
02779 
02780   int realLine = thisRange.line;
02781   int startCol = thisRange.startCol;
02782 
02783   KateTextCursor c(realLine, 0);
02784   m_view->renderer()->textWidth( c, startX() + m_textHintMouseX, startCol);
02785 
02786   QString tmp;
02787 
02788   emit m_view->needTextHint(c.line(), c.col(), tmp);
02789 
02790   if (!tmp.isEmpty()) kdDebug(13030)<<"Hint text: "<<tmp<<endl;
02791 }
02792 
02793 void KateViewInternal::focusInEvent (QFocusEvent *)
02794 {
02795   if (KApplication::cursorFlashTime() > 0)
02796     m_cursorTimer.start ( KApplication::cursorFlashTime() / 2 );
02797 
02798   if (m_textHintEnabled)
02799     m_textHintTimer.start( m_textHintTimeout );
02800 
02801   paintCursor();
02802 
02803   m_doc->m_activeView = m_view;
02804 
02805   emit m_view->gotFocus( m_view );
02806 }
02807 
02808 void KateViewInternal::focusOutEvent (QFocusEvent *)
02809 {
02810   if( ! m_view->m_codeCompletion->codeCompletionVisible() )
02811   {
02812     m_cursorTimer.stop();
02813 
02814     m_view->renderer()->setDrawCaret(true);
02815     paintCursor();
02816     emit m_view->lostFocus( m_view );
02817   }
02818 
02819   m_textHintTimer.stop();
02820 }
02821 
02822 void KateViewInternal::doDrag()
02823 {
02824   dragInfo.state = diDragging;
02825   dragInfo.dragObject = new QTextDrag(m_doc->selection(), this);
02826   dragInfo.dragObject->dragCopy();
02827 }
02828 
02829 void KateViewInternal::dragEnterEvent( QDragEnterEvent* event )
02830 {
02831   event->accept( (QTextDrag::canDecode(event) && m_doc->isReadWrite()) ||
02832                   KURLDrag::canDecode(event) );
02833 }
02834 
02835 void KateViewInternal::dragMoveEvent( QDragMoveEvent* event )
02836 {
02837   // track the cursor to the current drop location
02838   placeCursor( event->pos(), true, false );
02839 }
02840 
02841 void KateViewInternal::dropEvent( QDropEvent* event )
02842 {
02843   if ( KURLDrag::canDecode(event) ) {
02844 
02845       emit dropEventPass(event);
02846 
02847   } else if ( QTextDrag::canDecode(event) && m_doc->isReadWrite() ) {
02848 
02849     QString text;
02850 
02851     if (!QTextDrag::decode(event, text))
02852       return;
02853 
02854     // is the source our own document?
02855     bool priv = false;
02856     if (event->source() && event->source()->inherits("KateViewInternal"))
02857       priv = m_doc->ownedView( ((KateViewInternal*)(event->source()))->m_view );
02858 
02859     // dropped on a text selection area?
02860     bool selected = isTargetSelected( event->pos() );
02861 
02862     if( priv && selected ) {
02863       // this is a drag that we started and dropped on our selection
02864       // ignore this case
02865       return;
02866     }
02867 
02868     // atm only copy the text, no move
02869     m_doc->insertText( cursor.line(), cursor.col(), text );
02870     placeCursor( event->pos() );
02871 
02872     updateView();
02873   }
02874 }
02875 
02876 void KateViewInternal::imStartEvent( QIMEvent *e )
02877 {
02878   if ( m_doc->m_bReadOnly ) {
02879     e->ignore();
02880     return;
02881   }
02882 
02883   if ( m_doc->hasSelection() )
02884     m_doc->removeSelectedText();
02885 
02886   m_imPreeditStartLine = cursor.line();
02887   m_imPreeditStart = cursor.col();
02888   m_imPreeditLength = 0;
02889 
02890   m_doc->setIMSelectionValue( m_imPreeditStartLine, m_imPreeditStart, 0, 0, 0, true );
02891 }
02892 
02893 void KateViewInternal::imComposeEvent( QIMEvent *e )
02894 {
02895   if ( m_doc->m_bReadOnly ) {
02896     e->ignore();
02897     return;
02898   }
02899 
02900   if ( m_imPreeditLength > 0 ) {
02901     m_doc->removeText( cursor.line(), m_imPreeditStart,
02902                        cursor.line(), m_imPreeditStart + m_imPreeditLength );
02903   }
02904 
02905   m_doc->setIMSelectionValue( m_imPreeditStartLine, m_imPreeditStart, m_imPreeditStart + e->text().length(),
02906                               m_imPreeditStart + e->cursorPos(), m_imPreeditStart + e->cursorPos() + e->selectionLength(),
02907                               true );
02908 
02909   m_doc->insertText( cursor.line(), cursor.col(), e->text() );
02910 
02911   updateView( true );
02912   updateCursor( cursor, true );
02913   m_imPreeditLength = e->text().length();
02914 }
02915 
02916 void KateViewInternal::imEndEvent( QIMEvent *e )
02917 {
02918   if ( m_doc->m_bReadOnly ) {
02919     e->ignore();
02920     return;
02921   }
02922 
02923   if ( m_imPreeditLength > 0 ) {
02924     m_doc->removeText( cursor.line(), m_imPreeditStart,
02925                        cursor.line(), m_imPreeditStart + m_imPreeditLength );
02926   }
02927 
02928   m_doc->setIMSelectionValue( m_imPreeditStartLine, m_imPreeditStart, 0, 0, 0, false );
02929 
02930   if ( e->text().length() > 0 ) {
02931     m_doc->insertText( cursor.line(), cursor.col(), e->text() );
02932 
02933     if ( !m_cursorTimer.isActive() && KApplication::cursorFlashTime() > 0 )
02934       m_cursorTimer.start ( KApplication::cursorFlashTime() / 2 );
02935 
02936     updateView( true );
02937     updateCursor( cursor, true );
02938 
02939   }
02940 
02941   m_imPreeditStart = 0;
02942   m_imPreeditLength = 0;
02943 }
02944 
02945 //
02946 // END EVENT HANDLING STUFF !!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!
02947 //
02948 
02949 void KateViewInternal::clear()
02950 {
02951   cursor.setPos(0, 0);
02952   displayCursor.setPos(0, 0);
02953 }
02954 
02955 void KateViewInternal::wheelEvent(QWheelEvent* e)
02956 {
02957   if (m_lineScroll->minValue() != m_lineScroll->maxValue() && e->orientation() != Qt::Horizontal) {
02958     // React to this as a vertical event
02959     if ( ( e->state() & ControlButton ) || ( e->state() & ShiftButton ) ) {
02960       if (e->delta() > 0)
02961         scrollPrevPage();
02962       else
02963         scrollNextPage();
02964     } else {
02965       scrollViewLines(-((e->delta() / 120) * QApplication::wheelScrollLines()));
02966       // maybe a menu was opened or a bubbled window title is on us -> we shall erase it
02967       update();
02968       leftBorder->update();
02969     }
02970 
02971   } else if (!m_columnScroll->isHidden()) {
02972     QWheelEvent copy = *e;
02973     QApplication::sendEvent(m_columnScroll, &copy);
02974 
02975   } else {
02976     e->ignore();
02977   }
02978 }
02979 
02980 void KateViewInternal::startDragScroll()
02981 {
02982   if ( !m_dragScrollTimer.isActive() ) {
02983     m_suppressColumnScrollBar = true;
02984     m_dragScrollTimer.start( scrollTime );
02985   }
02986 }
02987 
02988 void KateViewInternal::stopDragScroll()
02989 {
02990   m_suppressColumnScrollBar = false;
02991   m_dragScrollTimer.stop();
02992   updateView();
02993 }
02994 
02995 void KateViewInternal::doDragScroll()
02996 {
02997   QPoint p = this->mapFromGlobal( QCursor::pos() );
02998 
02999   int dx = 0, dy = 0;
03000   if ( p.y() < scrollMargin ) {
03001     dy = p.y() - scrollMargin;
03002   } else if ( p.y() > height() - scrollMargin ) {
03003     dy = scrollMargin - (height() - p.y());
03004   }
03005   if ( p.x() < scrollMargin ) {
03006     dx = p.x() - scrollMargin;
03007   } else if ( p.x() > width() - scrollMargin ) {
03008     dx = scrollMargin - (width() - p.x());
03009   }
03010   dy /= 4;
03011 
03012   if (dy)
03013     scrollLines(startPos().line() + dy);
03014   if (dx)
03015     scrollColumns(m_startX + dx);
03016   if (!dy && !dx)
03017     stopDragScroll();
03018 }
03019 
03020 void KateViewInternal::enableTextHints(int timeout)
03021 {
03022   m_textHintTimeout=timeout;
03023   m_textHintEnabled=true;
03024   m_textHintTimer.start(timeout);
03025 }
03026 
03027 void KateViewInternal::disableTextHints()
03028 {
03029   m_textHintEnabled=false;
03030   m_textHintTimer.stop ();
03031 }
03032 
03033 // BEGIN EDIT STUFF
03034 void KateViewInternal::editStart()
03035 {
03036   editSessionNumber++;
03037 
03038   if (editSessionNumber > 1)
03039     return;
03040 
03041   editIsRunning = true;
03042   editOldCursor = cursor;
03043 }
03044 
03045 void KateViewInternal::editEnd(int editTagLineStart, int editTagLineEnd, bool tagFrom)
03046 {
03047    if (editSessionNumber == 0)
03048     return;
03049 
03050   editSessionNumber--;
03051 
03052   if (editSessionNumber > 0)
03053     return;
03054 
03055   if (tagFrom && (editTagLineStart <= int(m_doc->getRealLine(startLine()))))
03056     tagAll();
03057   else
03058     tagLines (editTagLineStart, tagFrom ? m_doc->lastLine() : editTagLineEnd, true);
03059 
03060   if (editOldCursor == cursor)
03061     updateBracketMarks();
03062 
03063   if (m_imPreeditLength <= 0)
03064     updateView(true);
03065 
03066   if ((editOldCursor != cursor) && (m_imPreeditLength <= 0))
03067   {
03068     m_madeVisible = false;
03069     updateCursor ( cursor, true );
03070   }
03071   else if ( m_view->isActive() )
03072   {
03073     makeVisible(displayCursor, displayCursor.col());
03074   }
03075 
03076   editIsRunning = false;
03077 }
03078 
03079 void KateViewInternal::editSetCursor (const KateTextCursor &cursor)
03080 {
03081   if (this->cursor != cursor)
03082   {
03083     this->cursor.setPos (cursor);
03084   }
03085 }
03086 // END
03087 
03088 void KateViewInternal::docSelectionChanged ()
03089 {
03090   if (!m_doc->hasSelection())
03091     selectAnchor.setPos (-1, -1);
03092 }
03093 
03094 // BEGIN KateScrollBar
03095 KateScrollBar::KateScrollBar (Orientation orientation, KateViewInternal* parent, const char* name)
03096   : QScrollBar (orientation, parent->m_view, name)
03097   , m_middleMouseDown (false)
03098   , m_view(parent->m_view)
03099   , m_doc(parent->m_doc)
03100   , m_viewInternal(parent)
03101   , m_topMargin(-1)
03102   , m_bottomMargin(-1)
03103   , m_savVisibleLines(0)
03104   , m_showMarks(false)
03105 {
03106   connect(this, SIGNAL(valueChanged(int)), SLOT(sliderMaybeMoved(int)));
03107   connect(m_doc, SIGNAL(marksChanged()), this, SLOT(marksChanged()));
03108 
03109   m_lines.setAutoDelete(true);
03110 }
03111 
03112 void KateScrollBar::mousePressEvent(QMouseEvent* e)
03113 {
03114   if (e->button() == MidButton)
03115     m_middleMouseDown = true;
03116 
03117   QScrollBar::mousePressEvent(e);
03118 
03119   redrawMarks();
03120 }
03121 
03122 void KateScrollBar::mouseReleaseEvent(QMouseEvent* e)
03123 {
03124   QScrollBar::mouseReleaseEvent(e);
03125 
03126   m_middleMouseDown = false;
03127 
03128   redrawMarks();
03129 }
03130 
03131 void KateScrollBar::mouseMoveEvent(QMouseEvent* e)
03132 {
03133   QScrollBar::mouseMoveEvent(e);
03134 
03135   if (e->state() | LeftButton)
03136     redrawMarks();
03137 }
03138 
03139 void KateScrollBar::paintEvent(QPaintEvent *e)
03140 {
03141   QScrollBar::paintEvent(e);
03142   redrawMarks();
03143 }
03144 
03145 void KateScrollBar::resizeEvent(QResizeEvent *e)
03146 {
03147   QScrollBar::resizeEvent(e);
03148   recomputeMarksPositions();
03149 }
03150 
03151 void KateScrollBar::styleChange(QStyle &s)
03152 {
03153   QScrollBar::styleChange(s);
03154   m_topMargin = -1;
03155   recomputeMarksPositions();
03156 }
03157 
03158 void KateScrollBar::valueChange()
03159 {
03160   QScrollBar::valueChange();
03161   redrawMarks();
03162 }
03163 
03164 void KateScrollBar::rangeChange()
03165 {
03166   QScrollBar::rangeChange();
03167   recomputeMarksPositions();
03168 }
03169 
03170 void KateScrollBar::marksChanged()
03171 {
03172   recomputeMarksPositions(true);
03173 }
03174 
03175 void KateScrollBar::redrawMarks()
03176 {
03177   if (!m_showMarks)
03178     return;
03179 
03180   QPainter painter(this);
03181   QRect rect = sliderRect();
03182   for (QIntDictIterator<QColor> it(m_lines); it.current(); ++it)
03183   {
03184     if (it.currentKey() < rect.top() || it.currentKey() > rect.bottom())
03185     {
03186       painter.setPen(*it.current());
03187       painter.drawLine(0, it.currentKey(), width(), it.currentKey());
03188     }
03189   }
03190 }
03191 
03192 void KateScrollBar::recomputeMarksPositions(bool forceFullUpdate)
03193 {
03194   if (m_topMargin == -1)
03195     watchScrollBarSize();
03196 
03197   m_lines.clear();
03198   m_savVisibleLines = m_doc->visibleLines();
03199 
03200   int realHeight = frameGeometry().height() - m_topMargin - m_bottomMargin;
03201 
03202   QPtrList<KTextEditor::Mark> marks = m_doc->marks();
03203   KateCodeFoldingTree *tree = m_doc->foldingTree();
03204 
03205   for (KTextEditor::Mark *mark = marks.first(); mark; mark = marks.next())
03206   {
03207     uint line = mark->line;
03208 
03209     if (tree)
03210     {
03211       KateCodeFoldingNode *node = tree->findNodeForLine(line);
03212 
03213       while (node)
03214       {
03215         if (!node->visible)
03216           line = tree->getStartLine(node);
03217         node = node->parentNode;
03218       }
03219     }
03220 
03221     line = m_doc->getVirtualLine(line);
03222 
03223     double d = (double)line / (m_savVisibleLines - 1);
03224     m_lines.insert(m_topMargin + (int)(d * realHeight),
03225                    new QColor(KateRendererConfig::global()->lineMarkerColor((KTextEditor::MarkInterface::MarkTypes)mark->type)));
03226   }
03227 
03228   if (forceFullUpdate)
03229     update();
03230   else
03231     redrawMarks();
03232 }
03233 
03234 void KateScrollBar::watchScrollBarSize()
03235 {
03236   int savMax = maxValue();
03237   setMaxValue(0);
03238   QRect rect = sliderRect();
03239   setMaxValue(savMax);
03240 
03241   m_topMargin = rect.top();
03242   m_bottomMargin = frameGeometry().height() - rect.bottom();
03243 }
03244 
03245 void KateScrollBar::sliderMaybeMoved(int value)
03246 {
03247   if (m_middleMouseDown)
03248     emit sliderMMBMoved(value);
03249 }
03250 
03251 KateTextLine::Ptr KateViewInternal::textLine( int realLine )
03252 {
03253   if (m_usePlainLines)
03254     return m_doc->plainKateTextLine(realLine);
03255   else
03256     return m_doc->kateTextLine(realLine);
03257 }
03258 
03259 // END
03260 
03261 // 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.0.
Documentation copyright © 1996-2004 the KDE developers.
Generated on Sat Nov 27 13:52:48 2004 by doxygen 1.3.9.1 written by Dimitri van Heesch, © 1997-2003