kate Library API Documentation

kateviewinternal.cpp

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