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