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