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