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