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