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     /* Check for hasFocus() to avoid crashes in QXIMInputContext as in bug #131266.
00874     This is only a workaround until somebody can find the real reason of the crash
00875     (probably it's in Qt). */
00876     if (line == -1 || !hasFocus()) 
00877         return;
00878 
00879     KateRenderer *renderer = m_view->renderer();
00880 
00881     // Cursor placement code is changed for Asian input method that
00882     // shows candidate window. This behavior is same as Qt/E 2.3.7
00883     // which supports Asian input methods. Asian input methods need
00884     // start point of IM selection text to place candidate window as
00885     // adjacent to the selection text.
00886     uint preeditStrLen = renderer->textWidth(textLine(m_imPreeditStartLine), cursor.col()) - renderer->textWidth(textLine(m_imPreeditStartLine), m_imPreeditSelStart);
00887     uint x = cXPos - m_startX - lineRanges[line].startX + lineRanges[line].xOffset() - preeditStrLen;
00888     uint y = line * renderer->fontHeight();
00889 
00890     setMicroFocusHint(x, y, 0, renderer->fontHeight());
00891 }
00892 
00893 void KateViewInternal::doReturn()
00894 {
00895   KateTextCursor c = cursor;
00896   m_doc->newLine( c, this );
00897   updateCursor( c );
00898   updateView();
00899 }
00900 
00901 void KateViewInternal::doDelete()
00902 {
00903   m_doc->del( m_view, cursor );
00904   if (m_view->m_codeCompletion->codeCompletionVisible()) {
00905     m_view->m_codeCompletion->updateBox();
00906   }
00907 }
00908 
00909 void KateViewInternal::doBackspace()
00910 {
00911   m_doc->backspace( m_view, cursor );
00912   if (m_view->m_codeCompletion->codeCompletionVisible()) {
00913     m_view->m_codeCompletion->updateBox();
00914   }
00915 }
00916 
00917 void KateViewInternal::doTranspose()
00918 {
00919   m_doc->transpose( cursor );
00920 }
00921 
00922 void KateViewInternal::doDeleteWordLeft()
00923 {
00924   wordLeft( true );
00925   m_view->removeSelectedText();
00926   update();
00927 }
00928 
00929 void KateViewInternal::doDeleteWordRight()
00930 {
00931   wordRight( true );
00932   m_view->removeSelectedText();
00933   update();
00934 }
00935 
00936 class CalculatingCursor : public KateTextCursor {
00937 public:
00938   CalculatingCursor(KateViewInternal* vi)
00939     : KateTextCursor()
00940     , m_vi(vi)
00941   {
00942     Q_ASSERT(valid());
00943   }
00944 
00945   CalculatingCursor(KateViewInternal* vi, const KateTextCursor& c)
00946     : KateTextCursor(c)
00947     , m_vi(vi)
00948   {
00949     Q_ASSERT(valid());
00950   }
00951 
00952   // This one constrains its arguments to valid positions
00953   CalculatingCursor(KateViewInternal* vi, uint line, uint col)
00954     : KateTextCursor(line, col)
00955     , m_vi(vi)
00956   {
00957     makeValid();
00958   }
00959 
00960 
00961   virtual CalculatingCursor& operator+=( int n ) = 0;
00962 
00963   virtual CalculatingCursor& operator-=( int n ) = 0;
00964 
00965   CalculatingCursor& operator++() { return operator+=( 1 ); }
00966 
00967   CalculatingCursor& operator--() { return operator-=( 1 ); }
00968 
00969   void makeValid() {
00970     m_line = kMax( 0, kMin( int( m_vi->m_doc->numLines() - 1 ), line() ) );
00971     if (m_vi->m_view->wrapCursor())
00972       m_col = kMax( 0, kMin( m_vi->m_doc->lineLength( line() ), col() ) );
00973     else
00974       m_col = kMax( 0, col() );
00975     Q_ASSERT( valid() );
00976   }
00977 
00978   void toEdge( Bias bias ) {
00979     if( bias == left ) m_col = 0;
00980     else if( bias == right ) m_col = m_vi->m_doc->lineLength( line() );
00981   }
00982 
00983   bool atEdge() const { return atEdge( left ) || atEdge( right ); }
00984 
00985   bool atEdge( Bias bias ) const {
00986     switch( bias ) {
00987     case left:  return col() == 0;
00988     case none:  return atEdge();
00989     case right: return col() == m_vi->m_doc->lineLength( line() );
00990     default: Q_ASSERT(false); return false;
00991     }
00992   }
00993 
00994 protected:
00995   bool valid() const {
00996     return line() >= 0 &&
00997             uint( line() ) < m_vi->m_doc->numLines() &&
00998             col() >= 0 &&
00999             (!m_vi->m_view->wrapCursor() || col() <= m_vi->m_doc->lineLength( line() ));
01000   }
01001   KateViewInternal* m_vi;
01002 };
01003 
01004 class BoundedCursor : public CalculatingCursor {
01005 public:
01006   BoundedCursor(KateViewInternal* vi)
01007     : CalculatingCursor( vi ) {};
01008   BoundedCursor(KateViewInternal* vi, const KateTextCursor& c )
01009     : CalculatingCursor( vi, c ) {};
01010   BoundedCursor(KateViewInternal* vi, uint line, uint col )
01011     : CalculatingCursor( vi, line, col ) {};
01012   virtual CalculatingCursor& operator+=( int n ) {
01013     m_col += n;
01014 
01015     if (n > 0 && m_vi->m_view->dynWordWrap()) {
01016       // Need to constrain to current visible text line for dynamic wrapping mode
01017       if (m_col > m_vi->m_doc->lineLength(m_line)) {
01018         KateLineRange currentRange = m_vi->range(*this);
01019 
01020         int endX;
01021         bool crap;
01022         m_vi->m_view->renderer()->textWidth(m_vi->textLine(m_line), currentRange.startCol, m_vi->width() - currentRange.xOffset(), &crap, &endX);
01023         endX += (m_col - currentRange.endCol + 1) * m_vi->m_view->renderer()->spaceWidth();
01024 
01025         // Constraining if applicable NOTE: some code duplication in KateViewInternal::resize()
01026         if (endX >= m_vi->width() - currentRange.xOffset()) {
01027           m_col -= n;
01028           if ( uint( line() ) < m_vi->m_doc->numLines() - 1 ) {
01029             m_line++;
01030             m_col = 0;
01031           }
01032         }
01033       }
01034 
01035     } else if (n < 0 && col() < 0 && line() > 0 ) {
01036       m_line--;
01037       m_col = m_vi->m_doc->lineLength( line() );
01038     }
01039 
01040     m_col = kMax( 0, col() );
01041 
01042     Q_ASSERT( valid() );
01043     return *this;
01044   }
01045   virtual CalculatingCursor& operator-=( int n ) {
01046     return operator+=( -n );
01047   }
01048 };
01049 
01050 class WrappingCursor : public CalculatingCursor {
01051 public:
01052   WrappingCursor(KateViewInternal* vi)
01053     : CalculatingCursor( vi) {};
01054   WrappingCursor(KateViewInternal* vi, const KateTextCursor& c )
01055     : CalculatingCursor( vi, c ) {};
01056   WrappingCursor(KateViewInternal* vi, uint line, uint col )
01057     : CalculatingCursor( vi, line, col ) {};
01058 
01059   virtual CalculatingCursor& operator+=( int n ) {
01060     if( n < 0 ) return operator-=( -n );
01061     int len = m_vi->m_doc->lineLength( line() );
01062     if( col() + n <= len ) {
01063       m_col += n;
01064     } else if( uint( line() ) < m_vi->m_doc->numLines() - 1 ) {
01065       n -= len - col() + 1;
01066       m_col = 0;
01067       m_line++;
01068       operator+=( n );
01069     } else {
01070       m_col = len;
01071     }
01072     Q_ASSERT( valid() );
01073     return *this;
01074   }
01075   virtual CalculatingCursor& operator-=( int n ) {
01076     if( n < 0 ) return operator+=( -n );
01077     if( col() - n >= 0 ) {
01078       m_col -= n;
01079     } else if( line() > 0 ) {
01080       n -= col() + 1;
01081       m_line--;
01082       m_col = m_vi->m_doc->lineLength( line() );
01083       operator-=( n );
01084     } else {
01085       m_col = 0;
01086     }
01087     Q_ASSERT( valid() );
01088     return *this;
01089   }
01090 };
01091 
01092 void KateViewInternal::moveChar( Bias bias, bool sel )
01093 {
01094   KateTextCursor c;
01095   if ( m_view->wrapCursor() ) {
01096     c = WrappingCursor( this, cursor ) += bias;
01097   } else {
01098     c = BoundedCursor( this, cursor ) += bias;
01099   }
01100 
01101   updateSelection( c, sel );
01102   updateCursor( c );
01103 }
01104 
01105 void KateViewInternal::cursorLeft(  bool sel )
01106 {
01107   if ( ! m_view->wrapCursor() && cursor.col() == 0 )
01108     return;
01109 
01110   moveChar( left, sel );
01111   if (m_view->m_codeCompletion->codeCompletionVisible()) {
01112     m_view->m_codeCompletion->updateBox();
01113   }
01114 }
01115 
01116 void KateViewInternal::cursorRight( bool sel )
01117 {
01118   moveChar( right, sel );
01119   if (m_view->m_codeCompletion->codeCompletionVisible()) {
01120     m_view->m_codeCompletion->updateBox();
01121   }
01122 }
01123 
01124 void KateViewInternal::wordLeft ( bool sel )
01125 {
01126   WrappingCursor c( this, cursor );
01127 
01128   // First we skip backwards all space.
01129   // Then we look up into which category the current position falls:
01130   // 1. a "word" character
01131   // 2. a "non-word" character (except space)
01132   // 3. the beginning of the line
01133   // and skip all preceding characters that fall into this class.
01134   // The code assumes that space is never part of the word character class.
01135 
01136   KateHighlighting* h = m_doc->highlight();
01137   if( !c.atEdge( left ) ) {
01138 
01139     while( !c.atEdge( left ) && m_doc->textLine( c.line() )[ c.col() - 1 ].isSpace() )
01140       --c;
01141   }
01142   if( c.atEdge( left ) )
01143   {
01144     --c;
01145   }
01146   else if( h->isInWord( m_doc->textLine( c.line() )[ c.col() - 1 ] ) )
01147   {
01148     while( !c.atEdge( left ) && h->isInWord( m_doc->textLine( c.line() )[ c.col() - 1 ] ) )
01149       --c;
01150   }
01151   else
01152   {
01153     while( !c.atEdge( left )
01154            && !h->isInWord( m_doc->textLine( c.line() )[ c.col() - 1 ] )
01155            // in order to stay symmetric to wordLeft()
01156            // we must not skip space preceding a non-word sequence
01157            && !m_doc->textLine( c.line() )[ c.col() - 1 ].isSpace() )
01158     {
01159       --c;
01160     }
01161   }
01162 
01163   updateSelection( c, sel );
01164   updateCursor( c );
01165 }
01166 
01167 void KateViewInternal::wordRight( bool sel )
01168 {
01169   WrappingCursor c( this, cursor );
01170 
01171   // We look up into which category the current position falls:
01172   // 1. a "word" character
01173   // 2. a "non-word" character (except space)
01174   // 3. the end of the line
01175   // and skip all following characters that fall into this class.
01176   // If the skipped characters are followed by space, we skip that too.
01177   // The code assumes that space is never part of the word character class.
01178 
01179   KateHighlighting* h = m_doc->highlight();
01180   if( c.atEdge( right ) )
01181   {
01182     ++c;
01183   }
01184   else if( h->isInWord( m_doc->textLine( c.line() )[ c.col() ] ) )
01185   {
01186     while( !c.atEdge( right ) && h->isInWord( m_doc->textLine( c.line() )[ c.col() ] ) )
01187       ++c;
01188   }
01189   else
01190   {
01191     while( !c.atEdge( right )
01192            && !h->isInWord( m_doc->textLine( c.line() )[ c.col() ] )
01193            // we must not skip space, because if that space is followed
01194            // by more non-word characters, we would skip them, too
01195            && !m_doc->textLine( c.line() )[ c.col() ].isSpace() )
01196     {
01197       ++c;
01198     }
01199   }
01200 
01201   while( !c.atEdge( righ