• Skip to content
  • Skip to link menu
KDE 3.5 API Reference
  • KDE API Reference
  • API Reference
  • Sitemap
  • Contact Us
 

Kate

kateviewinternal.cpp

Go to the documentation of this file.
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( right ) && m_doc->textLine( c.line() )[ c.col() ].isSpace() )
01202     ++c;
01203 
01204   updateSelection( c, sel );
01205   updateCursor( c );
01206 }
01207 
01208 void KateViewInternal::moveEdge( Bias bias, bool sel )
01209 {
01210   BoundedCursor c( this, cursor );
01211   c.toEdge( bias );
01212   updateSelection( c, sel );
01213   updateCursor( c );
01214 }
01215 
01216 void KateViewInternal::home( bool sel )
01217 {
01218   if (m_view->m_codeCompletion->codeCompletionVisible()) {
01219     QKeyEvent e(QEvent::KeyPress, Qt::Key_Home, 0, 0);
01220     m_view->m_codeCompletion->handleKey(&e);
01221     return;
01222   }
01223 
01224   if (m_view->dynWordWrap() && currentRange().startCol) {
01225     // Allow us to go to the real start if we're already at the start of the view line
01226     if (cursor.col() != currentRange().startCol) {
01227       KateTextCursor c(cursor.line(), currentRange().startCol);
01228       updateSelection( c, sel );
01229       updateCursor( c );
01230       return;
01231     }
01232   }
01233 
01234   if( !(m_doc->configFlags() & KateDocument::cfSmartHome) ) {
01235     moveEdge( left, sel );
01236     return;
01237   }
01238 
01239   KateTextLine::Ptr l = textLine( cursor.line() );
01240 
01241   if (!l)
01242     return;
01243 
01244   KateTextCursor c = cursor;
01245   int lc = l->firstChar();
01246 
01247   if( lc < 0 || c.col() == lc ) {
01248     c.setCol(0);
01249   } else {
01250     c.setCol(lc);
01251   }
01252 
01253   updateSelection( c, sel );
01254   updateCursor( c, true );
01255 }
01256 
01257 void KateViewInternal::end( bool sel )
01258 {
01259   if (m_view->m_codeCompletion->codeCompletionVisible()) {
01260     QKeyEvent e(QEvent::KeyPress, Qt::Key_End, 0, 0);
01261     m_view->m_codeCompletion->handleKey(&e);
01262     return;
01263   }
01264 
01265   KateLineRange range = currentRange();
01266 
01267   if (m_view->dynWordWrap() && range.wrap) {
01268     // Allow us to go to the real end if we're already at the end of the view line
01269     if (cursor.col() < range.endCol - 1) {
01270       KateTextCursor c(cursor.line(), range.endCol - 1);
01271       updateSelection( c, sel );
01272       updateCursor( c );
01273       return;
01274     }
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 
01290   // If the cursor is already the real end, jump to last non-space character.
01291   // Otherwise, go to the real end ... obviously.
01292   if (c.col() == m_doc->lineLength(c.line())) {
01293     c.setCol(l->lastChar() + 1);
01294     updateSelection(c, sel);
01295     updateCursor(c, true);
01296   } else {
01297     moveEdge(right, sel);
01298   }
01299 }
01300 
01301 KateLineRange KateViewInternal::range(int realLine, const KateLineRange* previous)
01302 {
01303   // look at the cache first
01304   if (!m_updatingView && realLine >= lineRanges[0].line && realLine <= lineRanges[lineRanges.count() - 1].line)
01305     for (uint i = 0; i < lineRanges.count(); i++)
01306       if (realLine == lineRanges[i].line)
01307         if (!m_view->dynWordWrap() || (!previous && lineRanges[i].startCol == 0) || (previous && lineRanges[i].startCol == previous->endCol))
01308           return lineRanges[i];
01309 
01310   // Not in the cache, we have to create it
01311   KateLineRange ret;
01312 
01313   KateTextLine::Ptr text = textLine(realLine);
01314   if (!text) {
01315     return KateLineRange();
01316   }
01317 
01318   if (!m_view->dynWordWrap()) {
01319     Q_ASSERT(!previous);
01320     ret.line = realLine;
01321     ret.virtualLine = m_doc->getVirtualLine(realLine);
01322     ret.startCol = 0;
01323     ret.endCol = m_doc->lineLength(realLine);
01324     ret.startX = 0;
01325     ret.endX = m_view->renderer()->textWidth(text, -1);
01326     ret.viewLine = 0;
01327     ret.wrap = false;
01328     return ret;
01329   }
01330 
01331   ret.endCol = (int)m_view->renderer()->textWidth(text, previous ? previous->endCol : 0, width() - (previous ? previous->shiftX : 0), &ret.wrap, &ret.endX);
01332 
01333   Q_ASSERT(ret.endCol > ret.startCol);
01334 
01335   ret.line = realLine;
01336 
01337   if (previous) {
01338     ret.virtualLine = previous->virtualLine;
01339     ret.startCol = previous->endCol;
01340     ret.startX = previous->endX;
01341     ret.endX += previous->endX;
01342     ret.shiftX = previous->shiftX;
01343     ret.viewLine = previous->viewLine + 1;
01344 
01345   } else {
01346     // TODO worthwhile optimising this to get the data out of the initial textWidth call?
01347     if (m_view->config()->dynWordWrapAlignIndent() > 0) {
01348       int pos = text->nextNonSpaceChar(0);
01349 
01350       if (pos > 0)
01351         ret.shiftX = m_view->renderer()->textWidth(text, pos);
01352 
01353       if (ret.shiftX > ((double)width() / 100 * m_view->config()->dynWordWrapAlignIndent()))
01354         ret.shiftX = 0;
01355     }
01356 
01357     ret.virtualLine = m_doc->getVirtualLine(realLine);
01358     ret.startCol = 0;
01359     ret.startX = 0;
01360     ret.viewLine = 0;
01361   }
01362 
01363   return ret;
01364 }
01365 
01366 KateLineRange KateViewInternal::currentRange()
01367 {
01368 //  Q_ASSERT(m_view->dynWordWrap());
01369 
01370   return range(cursor);
01371 }
01372 
01373 KateLineRange KateViewInternal::previousRange()
01374 {
01375   uint currentViewLine = viewLine(cursor);
01376 
01377   if (currentViewLine)
01378     return range(cursor.line(), currentViewLine - 1);
01379   else
01380     return range(m_doc->getRealLine(displayCursor.line() - 1), -1);
01381 }
01382 
01383 KateLineRange KateViewInternal::nextRange()
01384 {
01385   uint currentViewLine = viewLine(cursor) + 1;
01386 
01387   if (currentViewLine >= viewLineCount(cursor.line())) {
01388     currentViewLine = 0;
01389     return range(cursor.line() + 1, currentViewLine);
01390   } else {
01391     return range(cursor.line(), currentViewLine);
01392   }
01393 }
01394 
01395 KateLineRange KateViewInternal::range(const KateTextCursor& realCursor)
01396 {
01397 //  Q_ASSERT(m_view->dynWordWrap());
01398 
01399   KateLineRange thisRange;
01400   bool first = true;
01401 
01402   do {
01403     thisRange = range(realCursor.line(), first ? 0L : &thisRange);
01404     first = false;
01405   } while (thisRange.wrap && !(realCursor.col() >= thisRange.startCol && realCursor.col() < thisRange.endCol) && thisRange.startCol != thisRange.endCol);
01406 
01407   return thisRange;
01408 }
01409 
01410 KateLineRange KateViewInternal::range(uint realLine, int viewLine)
01411 {
01412 //  Q_ASSERT(m_view->dynWordWrap());
01413 
01414   KateLineRange thisRange;
01415   bool first = true;
01416 
01417   do {
01418     thisRange = range(realLine, first ? 0L : &thisRange);
01419     first = false;
01420   } while (thisRange.wrap && viewLine != thisRange.viewLine && thisRange.startCol != thisRange.endCol);
01421 
01422   if (viewLine != -1 && viewLine != thisRange.viewLine)
01423     kdDebug(13030) << "WARNING: viewLine " << viewLine << " of line " << realLine << " does not exist." << endl;
01424 
01425   return thisRange;
01426 }
01427 
01433 uint KateViewInternal::viewLine(const KateTextCursor& realCursor)
01434 {
01435   if (!m_view->dynWordWrap()) return 0;
01436 
01437   if (realCursor.col() == 0) return 0;
01438 
01439   KateLineRange thisRange;
01440   bool first = true;
01441 
01442   do {
01443     thisRange = range(realCursor.line(), first ? 0L : &thisRange);
01444     first = false;
01445   } while (thisRange.wrap && !(realCursor.col() >= thisRange.startCol && realCursor.col() < thisRange.endCol) && thisRange.startCol != thisRange.endCol);
01446 
01447   return thisRange.viewLine;
01448 }
01449 
01450 int KateViewInternal::displayViewLine(const KateTextCursor& virtualCursor, bool limitToVisible)
01451 {
01452   KateTextCursor work = startPos();
01453 
01454   int limit = linesDisplayed();
01455 
01456   // Efficient non-word-wrapped path
01457   if (!m_view->dynWordWrap()) {
01458     int ret = virtualCursor.line() - startLine();
01459     if (limitToVisible && (ret < 0 || ret > limit))
01460       return -1;
01461     else
01462       return ret;
01463   }
01464 
01465   if (work == virtualCursor) {
01466     return 0;
01467   }
01468 
01469   int ret = -(int)viewLine(work);
01470   bool forwards = (work < virtualCursor) ? true : false;
01471 
01472   // FIXME switch to using ranges? faster?
01473   if (forwards) {
01474     while (work.line() != virtualCursor.line()) {
01475       ret += viewLineCount(m_doc->getRealLine(work.line()));
01476       work.setLine(work.line() + 1);
01477       if (limitToVisible && ret > limit)
01478         return -1;
01479     }
01480   } else {
01481     while (work.line() != virtualCursor.line()) {
01482       work.setLine(work.line() - 1);
01483       ret -= viewLineCount(m_doc->getRealLine(work.line()));
01484       if (limitToVisible && ret < 0)
01485         return -1;
01486     }
01487   }
01488 
01489   // final difference
01490   KateTextCursor realCursor = virtualCursor;
01491   realCursor.setLine(m_doc->getRealLine(realCursor.line()));
01492   if (realCursor.col() == -1) realCursor.setCol(m_doc->lineLength(realCursor.line()));
01493   ret += viewLine(realCursor);
01494 
01495   if (limitToVisible && (ret < 0 || ret > limit))
01496     return -1;
01497 
01498   return ret;
01499 }
01500 
01501 uint KateViewInternal::lastViewLine(uint realLine)
01502 {
01503   if (!m_view->dynWordWrap()) return 0;
01504 
01505   KateLineRange thisRange;
01506   bool first = true;
01507 
01508   do {
01509     thisRange = range(realLine, first ? 0L : &thisRange);
01510     first = false;
01511   } while (thisRange.wrap && thisRange.startCol != thisRange.endCol);
01512 
01513   return thisRange.viewLine;
01514 }
01515 
01516 uint KateViewInternal::viewLineCount(uint realLine)
01517 {
01518   return lastViewLine(realLine) + 1;
01519 }
01520 
01521 /*
01522  * This returns the cursor which is offset by (offset) view lines.
01523  * This is the main function which is called by code not specifically dealing with word-wrap.
01524  * The opposite conversion (cursor to offset) can be done with displayViewLine.
01525  *
01526  * The cursors involved are virtual cursors (ie. equivalent to displayCursor)
01527  */
01528 KateTextCursor KateViewInternal::viewLineOffset(const KateTextCursor& virtualCursor, int offset, bool keepX)
01529 {
01530   if (!m_view->dynWordWrap()) {
01531     KateTextCursor ret(kMin((int)m_doc->visibleLines() - 1, virtualCursor.line() + offset), 0);
01532 
01533     if (ret.line() < 0)
01534       ret.setLine(0);
01535 
01536     if (keepX) {
01537       int realLine = m_doc->getRealLine(ret.line());
01538       ret.setCol(m_doc->lineLength(realLine) - 1);
01539 
01540       if (m_currentMaxX > cXPos)
01541         cXPos = m_currentMaxX;
01542 
01543       if (m_view->wrapCursor())
01544         cXPos = kMin(cXPos, (int)m_view->renderer()->textWidth(textLine(realLine), m_doc->lineLength(realLine)));
01545 
01546       m_view->renderer()->textWidth(ret, cXPos);
01547     }
01548 
01549     return ret;
01550   }
01551 
01552   KateTextCursor realCursor = virtualCursor;
01553   realCursor.setLine(m_doc->getRealLine(virtualCursor.line()));
01554 
01555   uint cursorViewLine = viewLine(realCursor);
01556 
01557   int currentOffset = 0;
01558   int virtualLine = 0;
01559 
01560   bool forwards = (offset > 0) ? true : false;
01561 
01562   if (forwards) {
01563     currentOffset = lastViewLine(realCursor.line()) - cursorViewLine;
01564     if (offset <= currentOffset) {
01565       // the answer is on the same line
01566       KateLineRange thisRange = range(realCursor.line(), cursorViewLine + offset);
01567       Q_ASSERT(thisRange.virtualLine == virtualCursor.line());
01568       return KateTextCursor(virtualCursor.line(), thisRange.startCol);
01569     }
01570 
01571     virtualLine = virtualCursor.line() + 1;
01572 
01573   } else {
01574     offset = -offset;
01575     currentOffset = cursorViewLine;
01576     if (offset <= currentOffset) {
01577       // the answer is on the same line
01578       KateLineRange thisRange = range(realCursor.line(), cursorViewLine - offset);
01579       Q_ASSERT(thisRange.virtualLine == virtualCursor.line());
01580       return KateTextCursor(virtualCursor.line(), thisRange.startCol);
01581     }
01582 
01583     virtualLine = virtualCursor.line() - 1;
01584   }
01585 
01586   currentOffset++;
01587 
01588   while (virtualLine >= 0 && virtualLine < (int)m_doc->visibleLines())
01589   {
01590     KateLineRange thisRange;
01591     bool first = true;
01592     int realLine = m_doc->getRealLine(virtualLine);
01593 
01594     do {
01595       thisRange = range(realLine, first ? 0L : &thisRange);
01596       first = false;
01597 
01598       if (offset == currentOffset) {
01599         if (!forwards) {
01600           // We actually want it the other way around
01601           int requiredViewLine = lastViewLine(realLine) - thisRange.viewLine;
01602           if (requiredViewLine != thisRange.viewLine) {
01603             thisRange = range(realLine, requiredViewLine);
01604           }
01605         }
01606 
01607         KateTextCursor ret(virtualLine, thisRange.startCol);
01608 
01609         // keep column position
01610         if (keepX) {
01611           ret.setCol(thisRange.endCol - 1);
01612           KateTextCursor realCursorTemp(m_doc->getRealLine(virtualCursor.line()), virtualCursor.col());
01613           int visibleX = m_view->renderer()->textWidth(realCursorTemp) - range(realCursorTemp).startX;
01614           int xOffset = thisRange.startX;
01615 
01616           if (m_currentMaxX > visibleX)
01617             visibleX = m_currentMaxX;
01618 
01619           cXPos = xOffset + visibleX;
01620 
01621           cXPos = kMin(cXPos, lineMaxCursorX(thisRange));
01622 
01623           m_view->renderer()->textWidth(ret, cXPos);
01624         }
01625 
01626         return ret;
01627       }
01628 
01629       currentOffset++;
01630 
01631     } while (thisRange.wrap);
01632 
01633     if (forwards)
01634       virtualLine++;
01635     else
01636       virtualLine--;
01637   }
01638 
01639   // Looks like we were asked for something a bit exotic.
01640   // Return the max/min valid position.
01641   if (forwards)
01642     return KateTextCursor(m_doc->visibleLines() - 1, m_doc->lineLength(m_doc->visibleLines() - 1));
01643   else
01644     return KateTextCursor(0, 0);
01645 }
01646 
01647 int KateViewInternal::lineMaxCursorX(const KateLineRange& range)
01648 {
01649   if (!m_view->wrapCursor() && !range.wrap)
01650     return INT_MAX;
01651 
01652   int maxX = range.endX;
01653 
01654   if (maxX && range.wrap) {
01655     QChar lastCharInLine = textLine(range.line)->getChar(range.endCol - 1);
01656 
01657     if (lastCharInLine == QChar('\t')) {
01658       int lineSize = 0;
01659       int lastTabSize = 0;
01660       for(int i = range.startCol; i < range.endCol; i++) {
01661         if (textLine(range.line)->getChar(i) == QChar('\t')) {
01662           lastTabSize = m_view->tabWidth() - (lineSize % m_view->tabWidth());
01663           lineSize += lastTabSize;
01664         } else {
01665           lineSize++;
01666         }
01667       }
01668       maxX -= lastTabSize * m_view->renderer()->spaceWidth();
01669     } else {
01670       maxX -= m_view->renderer()->config()->fontMetrics()->width(lastCharInLine);
01671     }
01672   }
01673 
01674   return maxX;
01675 }
01676 
01677 int KateViewInternal::lineMaxCol(const KateLineRange& range)
01678 {
01679   int maxCol = range.endCol;
01680 
01681   if (maxCol && range.wrap)
01682     maxCol--;
01683 
01684   return maxCol;
01685 }
01686 
01687 void KateViewInternal::cursorUp(bool sel)
01688 {
01689   if (m_view->m_codeCompletion->codeCompletionVisible()) {
01690     QKeyEvent e(QEvent::KeyPress, Qt::Key_Up, 0, 0);
01691     m_view->m_codeCompletion->handleKey(&e);
01692     return;
01693   }
01694 
01695   if (displayCursor.line() == 0 && (!m_view->dynWordWrap() || viewLine(cursor) == 0))
01696     return;
01697 
01698   int newLine = cursor.line(), newCol = 0, xOffset = 0, startCol = 0;
01699   m_preserveMaxX = true;
01700 
01701   if (m_view->dynWordWrap()) {
01702     // Dynamic word wrapping - navigate on visual lines rather than real lines
01703     KateLineRange thisRange = currentRange();
01704     // This is not the first line because that is already simplified out above
01705     KateLineRange pRange = previousRange();
01706 
01707     // Ensure we're in the right spot
01708     Q_ASSERT((cursor.line() == thisRange.line) &&
01709              (cursor.col() >= thisRange.startCol) &&
01710              (!thisRange.wrap || cursor.col() < thisRange.endCol));
01711 
01712     // VisibleX is the distance from the start of the text to the cursor on the current line.
01713     int visibleX = m_view->renderer()->textWidth(cursor) - thisRange.startX;
01714     int currentLineVisibleX = visibleX;
01715 
01716     // Translate to new line
01717     visibleX += thisRange.xOffset();
01718     visibleX -= pRange.xOffset();
01719 
01720     // Limit to >= 0
01721     visibleX = kMax(0, visibleX);
01722 
01723     startCol = pRange.startCol;
01724     xOffset = pRange.startX;
01725     newLine = pRange.line;
01726 
01727     // Take into account current max X (ie. if the current line was smaller
01728     // than the last definitely specified width)
01729     if (thisRange.xOffset() && !pRange.xOffset() && currentLineVisibleX == 0) // Special case for where xOffset may be > m_currentMaxX
01730       visibleX = m_currentMaxX;
01731     else if (visibleX < m_currentMaxX - pRange.xOffset())
01732       visibleX = m_currentMaxX - pRange.xOffset();
01733 
01734     cXPos = xOffset + visibleX;
01735 
01736     cXPos = kMin(cXPos, lineMaxCursorX(pRange));
01737 
01738     newCol = kMin((int)m_view->renderer()->textPos(newLine, visibleX, startCol), lineMaxCol(pRange));
01739 
01740   } else {
01741     newLine = m_doc->getRealLine(displayCursor.line() - 1);
01742 
01743     if ((m_view->wrapCursor()) && m_currentMaxX > cXPos)
01744       cXPos = m_currentMaxX;
01745   }
01746 
01747   KateTextCursor c(newLine, newCol);
01748   m_view->renderer()->textWidth(c, cXPos);
01749 
01750   updateSelection( c, sel );
01751   updateCursor( c );
01752 }
01753 
01754 void KateViewInternal::cursorDown(bool sel)
01755 {
01756   if (m_view->m_codeCompletion->codeCompletionVisible()) {
01757     QKeyEvent e(QEvent::KeyPress, Qt::Key_Down, 0, 0);
01758     m_view->m_codeCompletion->handleKey(&e);
01759     return;
01760   }
01761 
01762   if ((displayCursor.line() >= (int)m_doc->numVisLines() - 1) && (!m_view->dynWordWrap() || viewLine(cursor) == lastViewLine(cursor.line())))
01763     return;
01764 
01765   int newLine = cursor.line(), newCol = 0, xOffset = 0, startCol = 0;
01766   m_preserveMaxX = true;
01767 
01768   if (m_view->dynWordWrap()) {
01769     // Dynamic word wrapping - navigate on visual lines rather than real lines
01770     KateLineRange thisRange = currentRange();
01771     // This is not the last line because that is already simplified out above
01772     KateLineRange nRange = nextRange();
01773 
01774     // Ensure we're in the right spot
01775     Q_ASSERT((cursor.line() == thisRange.line) &&
01776              (cursor.col() >= thisRange.startCol) &&
01777              (!thisRange.wrap || cursor.col() < thisRange.endCol));
01778 
01779     // VisibleX is the distance from the start of the text to the cursor on the current line.
01780     int visibleX = m_view->renderer()->textWidth(cursor) - thisRange.startX;
01781     int currentLineVisibleX = visibleX;
01782 
01783     // Translate to new line
01784     visibleX += thisRange.xOffset();
01785     visibleX -= nRange.xOffset();
01786 
01787     // Limit to >= 0
01788     visibleX = kMax(0, visibleX);
01789 
01790     if (!thisRange.wrap) {
01791       newLine = m_doc->getRealLine(displayCursor.line() + 1);
01792     } else {
01793       startCol = thisRange.endCol;
01794       xOffset = thisRange.endX;
01795     }
01796 
01797     // Take into account current max X (ie. if the current line was smaller
01798     // than the last definitely specified width)
01799     if (thisRange.xOffset() && !nRange.xOffset() && currentLineVisibleX == 0) // Special case for where xOffset may be > m_currentMaxX
01800       visibleX = m_currentMaxX;
01801     else if (visibleX < m_currentMaxX - nRange.xOffset())
01802       visibleX = m_currentMaxX - nRange.xOffset();
01803 
01804     cXPos = xOffset + visibleX;
01805 
01806     cXPos = kMin(cXPos, lineMaxCursorX(nRange));
01807 
01808     newCol = kMin((int)m_view->renderer()->textPos(newLine, visibleX, startCol), lineMaxCol(nRange));
01809 
01810   } else {
01811     newLine = m_doc->getRealLine(displayCursor.line() + 1);
01812 
01813     if ((m_view->wrapCursor()) && m_currentMaxX > cXPos)
01814       cXPos = m_currentMaxX;
01815   }
01816 
01817   KateTextCursor c(newLine, newCol);
01818   m_view->renderer()->textWidth(c, cXPos);
01819 
01820   updateSelection(c, sel);
01821   updateCursor(c);
01822 }
01823 
01824 void KateViewInternal::cursorToMatchingBracket( bool sel )
01825 {
01826   KateTextCursor start( cursor ), end;
01827 
01828   if( !m_doc->findMatchingBracket( start, end ) )
01829     return;
01830 
01831   // The cursor is now placed just to the left of the matching bracket.
01832   // If it's an ending bracket, put it to the right (so we can easily
01833   // get back to the original bracket).
01834   if( end > start )
01835     end.setCol(end.col() + 1);
01836 
01837   updateSelection( end, sel );
01838   updateCursor( end );
01839 }
01840 
01841 void KateViewInternal::topOfView( bool sel )
01842 {
01843   KateTextCursor c = viewLineOffset(startPos(), m_minLinesVisible);
01844   updateSelection( c, sel );
01845   updateCursor( c );
01846 }
01847 
01848 void KateViewInternal::bottomOfView( bool sel )
01849 {
01850   // FIXME account for wordwrap
01851   KateTextCursor c = viewLineOffset(endPos(), -m_minLinesVisible);
01852   updateSelection( c, sel );
01853   updateCursor( c );
01854 }
01855 
01856 // lines is the offset to scroll by
01857 void KateViewInternal::scrollLines( int lines, bool sel )
01858 {
01859   KateTextCursor c = viewLineOffset(displayCursor, lines, true);
01860 
01861   // Fix the virtual cursor -> real cursor
01862   c.setLine(m_doc->getRealLine(c.line()));
01863 
01864   updateSelection( c, sel );
01865   updateCursor( c );
01866 }
01867 
01868 // This is a bit misleading... it's asking for the view to be scrolled, not the cursor
01869 void KateViewInternal::scrollUp()
01870 {
01871   KateTextCursor newPos = viewLineOffset(m_startPos, -1);
01872   scrollPos(newPos);
01873 }
01874 
01875 void KateViewInternal::scrollDown()
01876 {
01877   KateTextCursor newPos = viewLineOffset(m_startPos, 1);
01878   scrollPos(newPos);
01879 }
01880 
01881 void KateViewInternal::setAutoCenterLines(int viewLines, bool updateView)
01882 {
01883   m_autoCenterLines = viewLines;
01884   m_minLinesVisible = kMin(int((linesDisplayed() - 1)/2), m_autoCenterLines);
01885   if (updateView)
01886     KateViewInternal::updateView();
01887 }
01888 
01889 void KateViewInternal::pageUp( bool sel )
01890 {
01891   if (m_view->m_codeCompletion->codeCompletionVisible()) {
01892     QKeyEvent e(QEvent::KeyPress, Qt::Key_PageUp, 0, 0);
01893     m_view->m_codeCompletion->handleKey(&e);
01894     return;
01895   }
01896 
01897   // remember the view line and x pos
01898   int viewLine = displayViewLine(displayCursor);
01899   bool atTop = (startPos().line() == 0 && startPos().col() == 0);
01900 
01901   // Adjust for an auto-centering cursor
01902   int lineadj = 2 * m_minLinesVisible;
01903   int cursorStart = (linesDisplayed() - 1) - viewLine;
01904   if (cursorStart < m_minLinesVisible)
01905     lineadj -= m_minLinesVisible - cursorStart;
01906 
01907   int linesToScroll = -kMax( ((int)linesDisplayed() - 1) - lineadj, 0 );
01908   m_preserveMaxX = true;
01909 
01910   if (!m_doc->pageUpDownMovesCursor () && !atTop) {
01911     int xPos = m_view->renderer()->textWidth(cursor) - currentRange().startX;
01912 
01913     KateTextCursor newStartPos = viewLineOffset(startPos(), linesToScroll - 1);
01914     scrollPos(newStartPos);
01915 
01916     // put the cursor back approximately where it was
01917     KateTextCursor newPos = viewLineOffset(newStartPos, viewLine, true);
01918     newPos.setLine(m_doc->getRealLine(newPos.line()));
01919 
01920     KateLineRange newLine = range(newPos);
01921 
01922     if (m_currentMaxX - newLine.xOffset() > xPos)
01923       xPos = m_currentMaxX - newLine.xOffset();
01924 
01925     cXPos = kMin(newLine.startX + xPos, lineMaxCursorX(newLine));
01926 
01927     m_view->renderer()->textWidth( newPos, cXPos );
01928 
01929     m_preserveMaxX = true;
01930     updateSelection( newPos, sel );
01931     updateCursor(newPos);
01932 
01933   } else {
01934     scrollLines( linesToScroll, sel );
01935   }
01936 }
01937 
01938 void KateViewInternal::pageDown( bool sel )
01939 {
01940   if (m_view->m_codeCompletion->codeCompletionVisible()) {
01941     QKeyEvent e(QEvent::KeyPress, Qt::Key_PageDown, 0, 0);
01942     m_view->m_codeCompletion->handleKey(&e);
01943     return;
01944   }
01945 
01946   // remember the view line
01947   int viewLine = displayViewLine(displayCursor);
01948   bool atEnd = startPos() >= m_cachedMaxStartPos;
01949 
01950   // Adjust for an auto-centering cursor
01951   int lineadj = 2 * m_minLinesVisible;
01952   int cursorStart = m_minLinesVisible - viewLine;
01953   if (cursorStart > 0)
01954     lineadj -= cursorStart;
01955 
01956   int linesToScroll = kMax( ((int)linesDisplayed() - 1) - lineadj, 0 );
01957   m_preserveMaxX = true;
01958 
01959   if (!m_doc->pageUpDownMovesCursor () && !atEnd) {
01960     int xPos = m_view->renderer()->textWidth(cursor) - currentRange().startX;
01961 
01962     KateTextCursor newStartPos = viewLineOffset(startPos(), linesToScroll + 1);
01963     scrollPos(newStartPos);
01964 
01965     // put the cursor back approximately where it was
01966     KateTextCursor newPos = viewLineOffset(newStartPos, viewLine, true);
01967     newPos.setLine(m_doc->getRealLine(newPos.line()));
01968 
01969     KateLineRange newLine = range(newPos);
01970 
01971     if (m_currentMaxX - newLine.xOffset() > xPos)
01972       xPos = m_currentMaxX - newLine.xOffset();
01973 
01974     cXPos = kMin(newLine.startX + xPos, lineMaxCursorX(newLine));
01975 
01976     m_view->renderer()->textWidth( newPos, cXPos );
01977 
01978     m_preserveMaxX = true;
01979     updateSelection( newPos, sel );
01980     updateCursor(newPos);
01981 
01982   } else {
01983     scrollLines( linesToScroll, sel );
01984   }
01985 }
01986 
01987 int KateViewInternal::maxLen(uint startLine)
01988 {
01989 //  Q_ASSERT(!m_view->dynWordWrap());
01990 
01991   int displayLines = (m_view->height() / m_view->renderer()->fontHeight()) + 1;
01992 
01993   int maxLen = 0;
01994 
01995   for (int z = 0; z < displayLines; z++) {
01996     int virtualLine = startLine + z;
01997 
01998     if (virtualLine < 0 || virtualLine >= (int)m_doc->visibleLines())
01999       break;
02000 
02001     KateLineRange thisRange = range((int)m_doc->getRealLine(virtualLine));
02002 
02003     maxLen = kMax(maxLen, thisRange.endX);
02004   }
02005 
02006   return maxLen;
02007 }
02008 
02009 void KateViewInternal::top( bool sel )
02010 {
02011   KateTextCursor c( 0, cursor.col() );
02012   m_view->renderer()->textWidth( c, cXPos );
02013   updateSelection( c, sel );
02014   updateCursor( c );
02015 }
02016 
02017 void KateViewInternal::bottom( bool sel )
02018 {
02019   KateTextCursor c( m_doc->lastLine(), cursor.col() );
02020   m_view->renderer()->textWidth( c, cXPos );
02021   updateSelection( c, sel );
02022   updateCursor( c );
02023 }
02024 
02025 void KateViewInternal::top_home( bool sel )
02026 {
02027   if (m_view->m_codeCompletion->codeCompletionVisible()) {
02028     QKeyEvent e(QEvent::KeyPress, Qt::Key_Home, 0, 0);
02029     m_view->m_codeCompletion->handleKey(&e);
02030     return;
02031   }
02032   KateTextCursor c( 0, 0 );
02033   updateSelection( c, sel );
02034   updateCursor( c );
02035 }
02036 
02037 void KateViewInternal::bottom_end( bool sel )
02038 {
02039   if (m_view->m_codeCompletion->codeCompletionVisible()) {
02040     QKeyEvent e(QEvent::KeyPress, Qt::Key_End, 0, 0);
02041     m_view->m_codeCompletion->handleKey(&e);
02042     return;
02043   }
02044   KateTextCursor c( m_doc->lastLine(), m_doc->lineLength( m_doc->lastLine() ) );
02045   updateSelection( c, sel );
02046   updateCursor( c );
02047 }
02048 
02049 void KateViewInternal::updateSelection( const KateTextCursor& _newCursor, bool keepSel )
02050 {
02051   KateTextCursor newCursor = _newCursor;
02052   if( keepSel )
02053   {
02054     if ( !m_view->hasSelection() || (selectAnchor.line() == -1)
02055          || (m_view->config()->persistentSelection()
02056              && ((cursor < m_view->selectStart) || (cursor > m_view->selectEnd))) )
02057     {
02058       selectAnchor = cursor;
02059       m_view->setSelection( cursor, newCursor );
02060     }
02061     else
02062     {
02063       bool doSelect = true;
02064       switch (m_selectionMode)
02065       {
02066         case Word:
02067         {
02068           // Restore selStartCached if needed. It gets nuked by
02069           // viewSelectionChanged if we drag the selection into non-existence,
02070           // which can legitimately happen if a shift+DC selection is unable to
02071           // set a "proper" (i.e. non-empty) cached selection, e.g. because the
02072           // start was on something that isn't a word. Word select mode relies
02073           // on the cached selection being set properly, even if it is empty
02074           // (i.e. selStartCached == selEndCached).
02075           if ( selStartCached.line() == -1 )
02076             selStartCached = selEndCached;
02077 
02078           int c;
02079           if ( newCursor > selEndCached )
02080           {
02081             selectAnchor = selStartCached;
02082 
02083             KateTextLine::Ptr l = m_doc->kateTextLine( newCursor.line() );
02084 
02085             c = newCursor.col();
02086             if ( c > 0 && m_doc->highlight()->isInWord( l->getChar( c-1 ) ) ) {
02087               for (; c < l->length(); c++ )
02088                 if ( !m_doc->highlight()->isInWord( l->getChar( c ) ) )
02089                   break;
02090             }
02091 
02092             newCursor.setCol( c );
02093           }
02094           else if ( newCursor < selStartCached )
02095           {
02096             selectAnchor = selEndCached;
02097 
02098             KateTextLine::Ptr l = m_doc->kateTextLine( newCursor.line() );
02099 
02100             c = newCursor.col();
02101             if ( c > 0 && c < m_doc->textLine( newCursor.line() ).length()
02102                  && m_doc->highlight()->isInWord( l->getChar( c ) )
02103                  && m_doc->highlight()->isInWord( l->getChar( c-1 ) ) ) {
02104               for ( c -= 2; c >= 0; c-- )
02105                 if ( !m_doc->highlight()->isInWord( l->getChar( c ) ) )
02106                   break;
02107               newCursor.setCol( c+1 );
02108             }
02109 
02110           }
02111           else
02112             doSelect = false;
02113 
02114         }
02115         break;
02116         case Line:
02117           if ( newCursor.line() > selStartCached.line() )
02118           {
02119             if ( newCursor.line()+1 >= m_doc->numLines() )
02120               newCursor.setCol( m_doc->textLine( newCursor.line() ).length() );
02121             else
02122               newCursor.setPos( newCursor.line() + 1, 0 );
02123             // Grow to include entire line
02124             selectAnchor = selStartCached;
02125             selectAnchor.setCol( 0 );
02126           }
02127           else if ( newCursor.line() < selStartCached.line() )
02128           {
02129             newCursor.setCol( 0 );
02130             // Grow to include entire line
02131             selectAnchor = selEndCached;
02132             if ( selectAnchor.col() > 0 )
02133             {
02134               if ( selectAnchor.line()+1 >= m_doc->numLines() )
02135                 selectAnchor.setCol( m_doc->textLine( selectAnchor.line() ).length() );
02136               else
02137                 selectAnchor.setPos( selectAnchor.line() + 1, 0 );
02138             }
02139           }
02140           else // same line, ignore
02141             doSelect = false;
02142         break;
02143         case Mouse:
02144         {
02145           if ( selStartCached.line() < 0 ) // invalid
02146             break;
02147 
02148           if ( newCursor > selEndCached )
02149             selectAnchor = selStartCached;
02150           else if ( newCursor < selStartCached )
02151             selectAnchor = selEndCached;
02152           else
02153             doSelect = false;
02154         }
02155         break;
02156         default:
02157         {
02158           if ( selectAnchor.line() < 0 ) // invalid
02159             break;
02160         }
02161       }
02162 
02163       if ( doSelect )
02164         m_view->setSelection( selectAnchor, newCursor);
02165       else if ( selStartCached.line() >= 0 ) // we have a cached selection, so we restore that
02166         m_view->setSelection( selStartCached, selEndCached );
02167     }
02168 
02169     m_selChangedByUser = true;
02170   }
02171   else if ( !m_view->config()->persistentSelection() )
02172   {
02173     m_view->clearSelection();
02174     selStartCached.setLine( -1 );
02175     selectAnchor.setLine( -1 );
02176   }
02177 }
02178 
02179 void KateViewInternal::updateCursor( const KateTextCursor& newCursor, bool force, bool center, bool calledExternally )
02180 {
02181   if ( !force && (cursor == newCursor) )
02182   {
02183     if ( !m_madeVisible && m_view == m_doc->activeView() )
02184     {
02185       // unfold if required
02186       m_doc->foldingTree()->ensureVisible( newCursor.line() );
02187 
02188       makeVisible ( displayCursor, displayCursor.col(), false, center, calledExternally );
02189     }
02190 
02191     return;
02192   }
02193 
02194   // unfold if required
02195   m_doc->foldingTree()->ensureVisible( newCursor.line() );
02196 
02197   KateTextCursor oldDisplayCursor = displayCursor;
02198 
02199   cursor.setPos (newCursor);
02200   displayCursor.setPos (m_doc->getVirtualLine(cursor.line()), cursor.col());
02201 
02202   cXPos = m_view->renderer()->textWidth( cursor );
02203   if (m_view == m_doc->activeView())
02204     makeVisible ( displayCursor, displayCursor.col(), false, center, calledExternally );
02205 
02206   updateBracketMarks();
02207 
02208   // It's efficient enough to just tag them both without checking to see if they're on the same view line
02209   tagLine(oldDisplayCursor);
02210   tagLine(displayCursor);
02211 
02212   updateMicroFocusHint();
02213 
02214   if (m_cursorTimer.isActive ())
02215   {
02216     if ( KApplication::cursorFlashTime() > 0 )
02217       m_cursorTimer.start( KApplication::cursorFlashTime() / 2 );
02218     m_view->renderer()->setDrawCaret(true);
02219   }
02220 
02221   // Remember the maximum X position if requested
02222   if (m_preserveMaxX)
02223     m_preserveMaxX = false;
02224   else
02225     if (m_view->dynWordWrap())
02226       m_currentMaxX = m_view->renderer()->textWidth(displayCursor) - currentRange().startX + currentRange().xOffset();
02227     else
02228       m_currentMaxX = cXPos;
02229 
02230   //kdDebug() << "m_currentMaxX: " << m_currentMaxX << " (was "<< oldmaxx << "), cXPos: " << cXPos << endl;
02231   //kdDebug(13030) << "Cursor now located at real " << cursor.line << "," << cursor.col << ", virtual " << displayCursor.line << ", " << displayCursor.col << "; Top is " << startLine() << ", " << startPos().col <<  endl;
02232 
02233   paintText(0, 0, width(), height(), true);
02234 
02235   emit m_view->cursorPositionChanged();
02236 }
02237 
02238 void KateViewInternal::updateBracketMarks()
02239 {
02240   if ( bm.isValid() ) {
02241     KateTextCursor bmStart(m_doc->getVirtualLine(bm.start().line()), bm.start().col());
02242     KateTextCursor bmEnd(m_doc->getVirtualLine(bm.end().line()), bm.end().col());
02243 
02244     if( bm.getMinIndent() != 0 )
02245     {
02246       // @@ Do this only when cursor near start/end.
02247       if( bmStart > bmEnd )
02248       {
02249         tagLines(bmEnd, bmStart);
02250       }
02251       else
02252       {
02253         tagLines(bmStart, bmEnd);
02254       }
02255     }
02256     else
02257     {
02258       tagLine(bmStart);
02259       tagLine(bmEnd);
02260     }
02261   }
02262 
02263   // add some limit to this, this is really endless on big files without limit
02264   int maxLines = linesDisplayed () * 3;
02265   m_doc->newBracketMark( cursor, bm, maxLines );
02266 
02267   if ( bm.isValid() ) {
02268     KateTextCursor bmStart(m_doc->getVirtualLine(bm.start().line()), bm.start().col());
02269     KateTextCursor bmEnd(m_doc->getVirtualLine(bm.end().line()), bm.end().col());
02270 
02271     if( bm.getMinIndent() != 0 )
02272     {
02273       // @@ Do this only when cursor near start/end.
02274       if( bmStart > bmEnd )
02275       {
02276         tagLines(bmEnd, bmStart);
02277       }
02278       else
02279       {
02280         tagLines(bmStart, bmEnd);
02281       }
02282     }
02283     else
02284     {
02285       tagLine(bmStart);
02286       tagLine(bmEnd);
02287     }
02288   }
02289 }
02290 
02291 bool KateViewInternal::tagLine(const KateTextCursor& virtualCursor)
02292 {
02293   int viewLine = displayViewLine(virtualCursor, true);
02294   if (viewLine >= 0 && viewLine < (int)lineRanges.count()) {
02295     lineRanges[viewLine].dirty = true;
02296     leftBorder->update (0, lineToY(viewLine), leftBorder->width(), m_view->renderer()->fontHeight());
02297     return true;
02298   }
02299   return false;
02300 }
02301 
02302 bool KateViewInternal::tagLines( int start, int end, bool realLines )
02303 {
02304   return tagLines(KateTextCursor(start, 0), KateTextCursor(end, -1), realLines);
02305 }
02306 
02307 bool KateViewInternal::tagLines(KateTextCursor start, KateTextCursor end, bool realCursors)
02308 {
02309   if (realCursors)
02310   {
02311     //kdDebug()<<"realLines is true"<<endl;
02312     start.setLine(m_doc->getVirtualLine( start.line() ));
02313     end.setLine(m_doc->getVirtualLine( end.line() ));
02314   }
02315 
02316   if (end.line() < (int)startLine())
02317   {
02318     //kdDebug()<<"end<startLine"<<endl;
02319     return false;
02320   }
02321   if (start.line() > (int)endLine())
02322   {
02323     //kdDebug()<<"start> endLine"<<start<<" "<<((int)endLine())<<endl;
02324     return false;
02325   }
02326 
02327   //kdDebug(13030) << "tagLines( [" << start.line << "," << start.col << "], [" << end.line << "," << end.col << "] )\n";
02328 
02329   bool ret = false;
02330 
02331   for (uint z = 0; z < lineRanges.size(); z++)
02332   {
02333     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)))) {
02334       ret = lineRanges[z].dirty = true;
02335       //kdDebug() << "Tagged line " << lineRanges[z].line << endl;
02336     }
02337   }
02338 
02339   if (!m_view->dynWordWrap())
02340   {
02341     int y = lineToY( start.line() );
02342     // FIXME is this enough for when multiple lines are deleted
02343     int h = (end.line() - start.line() + 2) * m_view->renderer()->fontHeight();
02344     if (end.line() == (int)m_doc->numVisLines() - 1)
02345       h = height();
02346 
02347     leftBorder->update (0, y, leftBorder->width(), h);
02348   }
02349   else
02350   {
02351     // FIXME Do we get enough good info in editRemoveText to optimise this more?
02352     //bool justTagged = false;
02353     for (uint z = 0; z < lineRanges.size(); z++)
02354     {
02355       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))))
02356       {
02357         //justTagged = true;
02358         leftBorder->update (0, z * m_view->renderer()->fontHeight(), leftBorder->width(), leftBorder->height());
02359         break;
02360       }
02361       /*else if (justTagged)
02362       {
02363         justTagged = false;
02364         leftBorder->update (0, z * m_doc->viewFont.fontHeight, leftBorder->width(), m_doc->viewFont.fontHeight);
02365         break;
02366       }*/
02367     }
02368   }
02369 
02370   return ret;
02371 }
02372 
02373 void KateViewInternal::tagAll()
02374 {
02375   //kdDebug(13030) << "tagAll()" << endl;
02376   for (uint z = 0; z < lineRanges.size(); z++)
02377   {
02378       lineRanges[z].dirty = true;
02379   }
02380 
02381   leftBorder->updateFont();
02382   leftBorder->update ();
02383 }
02384 
02385 void KateViewInternal::paintCursor()
02386 {
02387   if (tagLine(displayCursor))
02388     paintText (0,0,width(), height(), true);
02389 }
02390 
02391 // Point in content coordinates
02392 void KateViewInternal::placeCursor( const QPoint& p, bool keepSelection, bool updateSelection )
02393 {
02394   KateLineRange thisRange = yToKateLineRange(p.y());
02395 
02396   if (thisRange.line == -1) {
02397     for (int i = (p.y() / m_view->renderer()->fontHeight()); i >= 0; i--) {
02398       thisRange = lineRanges[i];
02399       if (thisRange.line != -1)
02400         break;
02401     }
02402     Q_ASSERT(thisRange.line != -1);
02403   }
02404 
02405   int realLine = thisRange.line;
02406   int visibleLine = thisRange.virtualLine;
02407   uint startCol = thisRange.startCol;
02408 
02409   visibleLine = kMax( 0, kMin( visibleLine, int(m_doc->numVisLines()) - 1 ) );
02410 
02411   KateTextCursor c(realLine, 0);
02412 
02413   int x = kMin(kMax(-m_startX, p.x() - thisRange.xOffset()), lineMaxCursorX(thisRange) - thisRange.startX);
02414 
02415   m_view->renderer()->textWidth( c, startX() + x, startCol);
02416 
02417   if (updateSelection)
02418     KateViewInternal::updateSelection( c, keepSelection );
02419 
02420   updateCursor( c );
02421 }
02422 
02423 // Point in content coordinates
02424 bool KateViewInternal::isTargetSelected( const QPoint& p )
02425 {
02426   KateLineRange thisRange = yToKateLineRange(p.y());
02427 
02428   KateTextLine::Ptr l = textLine( thisRange.line );
02429   if( !l )
02430     return false;
02431 
02432   int col = m_view->renderer()->textPos( l, startX() + p.x() - thisRange.xOffset(), thisRange.startCol, false );
02433 
02434   return m_view->lineColSelected( thisRange.line, col );
02435 }
02436 
02437 //BEGIN EVENT HANDLING STUFF
02438 
02439 bool KateViewInternal::eventFilter( QObject *obj, QEvent *e )
02440 {
02441   if (obj == m_lineScroll)
02442   {
02443     // the second condition is to make sure a scroll on the vertical bar doesn't cause a horizontal scroll ;)
02444     if (e->type() == QEvent::Wheel && m_lineScroll->minValue() != m_lineScroll->maxValue())
02445     {
02446       wheelEvent((QWheelEvent*)e);
02447       return true;
02448     }
02449 
02450     // continue processing
02451     return QWidget::eventFilter( obj, e );
02452   }
02453 
02454   switch( e->type() )
02455   {
02456     case QEvent::KeyPress:
02457     {
02458       QKeyEvent *k = (QKeyEvent *)e;
02459 
02460       if (m_view->m_codeCompletion->codeCompletionVisible ())
02461       {
02462         kdDebug (13030) << "hint around" << endl;
02463 
02464         if( k->key() == Key_Escape )
02465           m_view->m_codeCompletion->abortCompletion();
02466       }
02467 
02468       if ((k->key() == Qt::Key_Escape) && !m_view->config()->persistentSelection() )
02469       {
02470         m_view->clearSelection();
02471         return true;
02472       }
02473       else if ( !((k->state() & ControlButton) || (k->state() & AltButton)) )
02474       {
02475         keyPressEvent( k );
02476         return k->isAccepted();
02477       }
02478 
02479     } break;
02480 
02481     case QEvent::DragMove:
02482     {
02483       QPoint currentPoint = ((QDragMoveEvent*) e)->pos();
02484 
02485       QRect doNotScrollRegion( scrollMargin, scrollMargin,
02486                           width() - scrollMargin * 2,
02487                           height() - scrollMargin * 2 );
02488 
02489       if ( !doNotScrollRegion.contains( currentPoint ) )
02490       {
02491           startDragScroll();
02492           // Keep sending move events
02493           ( (QDragMoveEvent*)e )->accept( QRect(0,0,0,0) );
02494       }
02495 
02496       dragMoveEvent((QDragMoveEvent*)e);
02497     } break;
02498 
02499     case QEvent::DragLeave:
02500       // happens only when pressing ESC while dragging
02501       stopDragScroll();
02502       break;
02503 
02504     case QEvent::WindowBlocked:
02505       // next focus originates from an internal dialog:
02506       // don't show the modonhd prompt
02507       m_doc->m_isasking = -1;
02508       break;
02509 
02510     default:
02511       break;
02512   }
02513 
02514   return QWidget::eventFilter( obj, e );
02515 }
02516 
02517 void KateViewInternal::keyPressEvent( QKeyEvent* e )
02518 {
02519   KKey key(e);
02520 
02521   bool codeComp = m_view->m_codeCompletion->codeCompletionVisible ();
02522 
02523   if (codeComp)
02524   {
02525     kdDebug (13030) << "hint around" << endl;
02526 
02527     if( e->key() == Key_Enter || e->key() == Key_Return  ||
02528     (key == SHIFT + Qt::Key_Return) || (key == SHIFT + Qt::Key_Enter)) {
02529       m_view->m_codeCompletion->doComplete();
02530       e->accept();
02531       return;
02532     }
02533   }
02534 
02535   if( !m_doc->isReadWrite() )
02536   {
02537     e->ignore();
02538     return;
02539   }
02540 
02541   if ((key == Qt::Key_Return) || (key == Qt::Key_Enter))
02542   {
02543     m_view->keyReturn();
02544     e->accept();
02545     return;
02546   }
02547 
02548   if ((key == SHIFT + Qt::Key_Return) || (key == SHIFT + Qt::Key_Enter))
02549   {
02550     uint ln = cursor.line();
02551     int col = cursor.col();
02552     KateTextLine::Ptr line = m_doc->kateTextLine( ln );
02553     int pos = line->firstChar();
02554     if (pos > cursor.col()) pos = cursor.col();
02555     if (pos != -1) {
02556       while ((int)line->length() > pos &&
02557              !line->getChar(pos).isLetterOrNumber() &&
02558              pos < cursor.col()) ++pos;
02559     } else {
02560       pos = line->length(); // stay indented
02561     }
02562     m_doc->editStart();
02563     m_doc->insertText( cursor.line(), line->length(), "\n" +  line->string(0, pos)
02564       + line->string().right( line->length() - cursor.col() ) );
02565     cursor.setPos(ln + 1, pos);
02566     if (col < int(line->length()))
02567       m_doc->editRemoveText(ln, col, line->length() - col);
02568     m_doc->editEnd();
02569     updateCursor(cursor, true);
02570     updateView();
02571     e->accept();
02572 
02573     return;
02574   }
02575 
02576   if (key == Qt::Key_Backspace || key == SHIFT + Qt::Key_Backspace)
02577   {
02578     m_view->backspace();
02579     e->accept();
02580 
02581     if (codeComp)
02582       m_view->m_codeCompletion->updateBox ();
02583 
02584     return;
02585   }
02586 
02587   if  (key == Qt::Key_Tab || key == SHIFT+Qt::Key_Backtab || key == Qt::Key_Backtab)
02588   {
02589     if (m_doc->invokeTabInterceptor(key)) {
02590       e->accept();
02591       return;
02592     } else
02593     if (m_doc->configFlags() & KateDocumentConfig::cfTabIndents)
02594     {
02595       if( key == Qt::Key_Tab )
02596       {
02597         if (m_view->hasSelection() || (m_doc->configFlags() & KateDocumentConfig::cfTabIndentsMode))
02598           m_doc->indent( m_view, cursor.line(), 1 );
02599         else if (m_doc->configFlags() & KateDocumentConfig::cfTabInsertsTab)
02600           m_doc->typeChars ( m_view, QString ("\t") );
02601         else
02602           m_doc->insertIndentChars ( m_view );
02603 
02604         e->accept();
02605 
02606         if (codeComp)
02607           m_view->m_codeCompletion->updateBox ();
02608 
02609         return;
02610       }
02611 
02612       if (key == SHIFT+Qt::Key_Backtab || key == Qt::Key_Backtab)
02613       {
02614         m_doc->indent( m_view, cursor.line(), -1 );
02615         e->accept();
02616 
02617         if (codeComp)
02618           m_view->m_codeCompletion->updateBox ();
02619 
02620         return;
02621       }
02622     }
02623 }
02624   if ( !(e->state() & ControlButton) && !(e->state() & AltButton)
02625        && m_doc->typeChars ( m_view, e->text() ) )
02626   {
02627     e->accept();
02628 
02629     if (codeComp)
02630       m_view->m_codeCompletion->updateBox ();
02631 
02632     return;
02633   }
02634 
02635   e->ignore();
02636 }
02637 
02638 void KateViewInternal::keyReleaseEvent( QKeyEvent* e )
02639 {
02640   KKey key(e);
02641 
02642   if (key == SHIFT)
02643     m_shiftKeyPressed = true;
02644   else
02645   {
02646     if (m_shiftKeyPressed)
02647     {
02648       m_shiftKeyPressed = false;
02649 
02650       if (m_selChangedByUser)
02651       {
02652         QApplication::clipboard()->setSelectionMode( true );
02653         m_view->copy();
02654         QApplication::clipboard()->setSelectionMode( false );
02655 
02656         m_selChangedByUser = false;
02657       }
02658     }
02659   }
02660 
02661   e->ignore();
02662   return;
02663 }
02664 
02665 void KateViewInternal::contextMenuEvent ( QContextMenuEvent * e )
02666 {
02667   // try to show popup menu
02668 
02669   QPoint p = e->pos();
02670 
02671   if ( m_view->m_doc->browserView() )
02672   {
02673     m_view->contextMenuEvent( e );
02674     return;
02675   }
02676 
02677   if ( e->reason() == QContextMenuEvent::Keyboard )
02678   {
02679     makeVisible( cursor, 0 );
02680     p = cursorCoordinates();
02681   }
02682   else if ( ! m_view->hasSelection() || m_view->config()->persistentSelection() )
02683     placeCursor( e->pos() );
02684 
02685   // popup is a qguardedptr now
02686   if (m_view->popup()) {
02687     m_view->popup()->popup( mapToGlobal( p ) );
02688     e->accept ();
02689   }
02690 }
02691 
02692 void KateViewInternal::mousePressEvent( QMouseEvent* e )
02693 {
02694   switch (e->button())
02695   {
02696     case LeftButton:
02697         m_selChangedByUser = false;
02698 
02699         if (possibleTripleClick)
02700         {
02701           possibleTripleClick = false;
02702 
02703           m_selectionMode = Line;
02704 
02705           if ( e->state() & Qt::ShiftButton )
02706           {
02707             updateSelection( cursor, true );
02708           }
02709           else
02710           {
02711             m_view->selectLine( cursor );
02712           }
02713 
02714           QApplication::clipboard()->setSelectionMode( true );
02715           m_view->copy();
02716           QApplication::clipboard()->setSelectionMode( false );
02717 
02718           // Keep the line at the select anchor selected during further
02719           // mouse selection
02720           if ( selectAnchor.line() > m_view->selectStart.line() )
02721           {
02722             // Preserve the last selected line
02723             if ( selectAnchor == m_view->selectEnd && selectAnchor.col() == 0 )
02724               selStartCached = KateTextCursor( selectAnchor.line()-1, 0 );
02725             else
02726               selStartCached = KateTextCursor( selectAnchor.line(), 0 );
02727             selEndCached = m_view->selectEnd;
02728           }
02729           else
02730           {
02731             // Preserve the first selected line
02732             selStartCached = m_view->selectStart;
02733             if ( m_view->selectEnd.line() > m_view->selectStart.line() )
02734               selEndCached = KateTextCursor( m_view->selectStart.line()+1, 0 );
02735             else
02736               selEndCached = m_view->selectEnd;
02737           }
02738 
02739           // Set cursor to edge of selection... which edge depends on what
02740           // "direction" the selection was made in
02741           if ( m_view->selectStart < selectAnchor
02742                && selectAnchor.line() != m_view->selectStart.line() )
02743             updateCursor( m_view->selectStart );
02744           else
02745             updateCursor( m_view->selectEnd );
02746 
02747           e->accept ();
02748           return;
02749         }
02750         else if (m_selectionMode == Default)
02751         {
02752           m_selectionMode = Mouse;
02753         }
02754 
02755         if ( e->state() & Qt::ShiftButton )
02756         {
02757           if (selectAnchor.line() < 0)
02758             selectAnchor = cursor;
02759         }
02760         else
02761         {
02762           selStartCached.setLine( -1 ); // invalidate
02763         }
02764 
02765         if( !( e->state() & Qt::ShiftButton ) && isTargetSelected( e->pos() ) )
02766         {
02767           dragInfo.state = diPending;
02768           dragInfo.start = e->pos();
02769         }
02770         else
02771         {
02772           dragInfo.state = diNone;
02773 
02774           if ( e->state() & Qt::ShiftButton )
02775           {
02776             placeCursor( e->pos(), true, false );
02777             if ( selStartCached.line() >= 0 )
02778             {
02779               if ( cursor > selEndCached )
02780               {
02781                 m_view->setSelection( selStartCached, cursor );
02782                 selectAnchor = selStartCached;
02783               }
02784               else if ( cursor < selStartCached )
02785               {
02786                 m_view->setSelection( cursor, selEndCached );
02787                 selectAnchor = selEndCached;
02788               }
02789               else
02790               {
02791                 m_view->setSelection( selStartCached, cursor );
02792               }
02793             }
02794             else
02795             {
02796               m_view->setSelection( selectAnchor, cursor );
02797             }
02798           }
02799           else
02800           {
02801             placeCursor( e->pos() );
02802           }
02803 
02804           scrollX = 0;
02805           scrollY = 0;
02806 
02807           m_scrollTimer.start (50);
02808         }
02809 
02810         e->accept ();
02811         break;
02812 
02813     default:
02814       e->ignore ();
02815       break;
02816   }
02817 }
02818 
02819 void KateViewInternal::mouseDoubleClickEvent(QMouseEvent *e)
02820 {
02821   switch (e->button())
02822   {
02823     case LeftButton:
02824       m_selectionMode = Word;
02825 
02826       if ( e->state() & Qt::ShiftButton )
02827       {
02828         KateTextCursor oldSelectStart = m_view->selectStart;
02829         KateTextCursor oldSelectEnd = m_view->selectEnd;
02830 
02831         // Now select the word under the select anchor
02832         int cs, ce;
02833         KateTextLine::Ptr l = m_doc->kateTextLine( selectAnchor.line() );
02834 
02835         ce = selectAnchor.col();
02836         if ( ce > 0 && m_doc->highlight()->isInWord( l->getChar( ce ) ) ) {
02837           for (; ce < l->length(); ce++ )
02838             if ( !m_doc->highlight()->isInWord( l->getChar( ce ) ) )
02839               break;
02840         }
02841 
02842         cs = selectAnchor.col() - 1;
02843         if ( cs < m_doc->textLine( selectAnchor.line() ).length()
02844               && m_doc->highlight()->isInWord( l->getChar( cs ) ) ) {
02845           for ( cs--; cs >= 0; cs-- )
02846             if ( !m_doc->highlight()->isInWord( l->getChar( cs ) ) )
02847               break;
02848         }
02849 
02850         // ...and keep it selected
02851         if (cs+1 < ce)
02852         {
02853           selStartCached = KateTextCursor( selectAnchor.line(), cs+1 );
02854           selEndCached = KateTextCursor( selectAnchor.line(), ce );
02855         }
02856         else
02857         {
02858           selStartCached = selectAnchor;
02859           selEndCached = selectAnchor;
02860         }
02861         // Now word select to the mouse cursor
02862         placeCursor( e->pos(), true );
02863       }
02864       else
02865       {
02866         // first clear the selection, otherwise we run into bug #106402
02867         // ...and set the cursor position, for the same reason (otherwise there
02868         // are *other* idiosyncrasies we can't fix without reintroducing said
02869         // bug)
02870         // Parameters: 1st false: don't redraw
02871         //             2nd false: don't emit selectionChanged signals, as
02872         //             selectWord() emits this already
02873         m_view->clearSelection( false, false );
02874         placeCursor( e->pos() );
02875         m_view->selectWord( cursor );
02876         if (m_view->hasSelection())
02877         {
02878           selectAnchor = selStartCached = m_view->selectStart;
02879           selEndCached = m_view->selectEnd;
02880         }
02881         else
02882         {
02883           // if we didn't actually select anything, restore the selection mode
02884           // -- see bug #131369 (kling)
02885           m_selectionMode = Default;
02886         }
02887       }
02888 
02889       // Move cursor to end (or beginning) of selected word
02890       if (m_view->hasSelection())
02891       {
02892         QApplication::clipboard()->setSelectionMode( true );
02893         m_view->copy();
02894         QApplication::clipboard()->setSelectionMode( false );
02895 
02896         // Shift+DC before the "cached" word should move the cursor to the
02897         // beginning of the selection, not the end
02898         if (m_view->selectStart < selStartCached)
02899           updateCursor( m_view->selectStart );
02900         else
02901           updateCursor( m_view->selectEnd );
02902       }
02903 
02904       possibleTripleClick = true;
02905       QTimer::singleShot ( QApplication::doubleClickInterval(), this, SLOT(tripleClickTimeout()) );
02906 
02907       scrollX = 0;
02908       scrollY = 0;
02909 
02910       m_scrollTimer.start (50);
02911 
02912       e->accept ();
02913       break;
02914 
02915     default:
02916       e->ignore ();
02917       break;
02918   }
02919 }
02920 
02921 void KateViewInternal::tripleClickTimeout()
02922 {
02923   possibleTripleClick = false;
02924 }
02925 
02926 void KateViewInternal::mouseReleaseEvent( QMouseEvent* e )
02927 {
02928   switch (e->button())
02929   {
02930     case LeftButton:
02931       m_selectionMode = Default;
02932 //       selStartCached.setLine( -1 );
02933 
02934       if (m_selChangedByUser)
02935       {
02936         QApplication::clipboard()->setSelectionMode( true );
02937         m_view->copy();
02938         QApplication::clipboard()->setSelectionMode( false );
02939         // Set cursor to edge of selection... which edge depends on what
02940         // "direction" the selection was made in
02941         if ( m_view->selectStart < selectAnchor )
02942           updateCursor( m_view->selectStart );
02943         else
02944           updateCursor( m_view->selectEnd );
02945 
02946         m_selChangedByUser = false;
02947       }
02948 
02949       if (dragInfo.state == diPending)
02950         placeCursor( e->pos(), e->state() & ShiftButton );
02951       else if (dragInfo.state == diNone)
02952         m_scrollTimer.stop ();
02953 
02954       dragInfo.state = diNone;
02955 
02956       e->accept ();
02957       break;
02958 
02959     case MidButton:
02960       placeCursor( e->pos() );
02961 
02962       if( m_doc->isReadWrite() )
02963       {
02964         QApplication::clipboard()->setSelectionMode( true );
02965         m_view->paste ();
02966         QApplication::clipboard()->setSelectionMode( false );
02967       }
02968 
02969       e->accept ();
02970       break;
02971 
02972     default:
02973       e->ignore ();
02974       break;
02975   }
02976 }
02977 
02978 void KateViewInternal::mouseMoveEvent( QMouseEvent* e )
02979 {
02980   if( e->state() & LeftButton )
02981   {
02982     if (dragInfo.state == diPending)
02983     {
02984       // we had a mouse down, but haven't confirmed a drag yet
02985       // if the mouse has moved sufficiently, we will confirm
02986       QPoint p( e->pos() - dragInfo.start );
02987 
02988       // we've left the drag square, we can start a real drag operation now
02989       if( p.manhattanLength() > KGlobalSettings::dndEventDelay() )
02990         doDrag();
02991 
02992       return;
02993     }
02994     else if (dragInfo.state == diDragging)
02995     {
02996       // Don't do anything after a canceled drag until the user lets go of
02997       // the mouse button!
02998       return;
02999     }
03000 
03001     mouseX = e->x();
03002     mouseY = e->y();
03003 
03004     scrollX = 0;
03005     scrollY = 0;
03006     int d = m_view->renderer()->fontHeight();
03007 
03008     if (mouseX < 0)
03009       scrollX = -d;
03010 
03011     if (mouseX > width())
03012       scrollX = d;
03013 
03014     if (mouseY < 0)
03015     {
03016       mouseY = 0;
03017       scrollY = -d;
03018     }
03019 
03020     if (mouseY > height())
03021     {
03022       mouseY = height();
03023       scrollY = d;
03024     }
03025 
03026     placeCursor( QPoint( mouseX, mouseY ), true );
03027 
03028   }
03029   else
03030   {
03031     if (isTargetSelected( e->pos() ) ) {
03032       // mouse is over selected text. indicate that the text is draggable by setting
03033       // the arrow cursor as other Qt text editing widgets do
03034       if (m_mouseCursor != ArrowCursor) {
03035         setCursor( KCursor::arrowCursor() );
03036         m_mouseCursor = ArrowCursor;
03037       }
03038     } else {
03039       // normal text cursor
03040       if (m_mouseCursor != IbeamCursor) {
03041         setCursor( KCursor::ibeamCursor() );
03042         m_mouseCursor = IbeamCursor;
03043       }
03044     }
03045 
03046     if (m_textHintEnabled)
03047     {
03048        m_textHintTimer.start(m_textHintTimeout);
03049        m_textHintMouseX=e->x();
03050        m_textHintMouseY=e->y();
03051     }
03052   }
03053 }
03054 
03055 void KateViewInternal::paintEvent(QPaintEvent *e)
03056 {
03057   paintText(e->rect().x(), e->rect().y(), e->rect().width(), e->rect().height());
03058 }
03059 
03060 void KateViewInternal::resizeEvent(QResizeEvent* e)
03061 {
03062   bool expandedHorizontally = width() > e->oldSize().width();
03063   bool expandedVertically = height() > e->oldSize().height();
03064   bool heightChanged = height() != e->oldSize().height();
03065 
03066   m_madeVisible = false;
03067 
03068   if (heightChanged) {
03069     setAutoCenterLines(m_autoCenterLines, false);
03070     m_cachedMaxStartPos.setPos(-1, -1);
03071   }
03072 
03073   if (m_view->dynWordWrap()) {
03074     bool dirtied = false;
03075 
03076     for (uint i = 0; i < lineRanges.count(); i++) {
03077       // find the first dirty line
03078       // the word wrap updateView algorithm is forced to check all lines after a dirty one
03079       if (lineRanges[i].wrap ||
03080          (!expandedHorizontally && (lineRanges[i].endX - lineRanges[i].startX) > width())) {
03081         dirtied = lineRanges[i].dirty = true;
03082         break;
03083       }
03084     }
03085 
03086     if (dirtied || heightChanged) {
03087       updateView(true);
03088       leftBorder->update();
03089     }
03090 
03091     if (width() < e->oldSize().width()) {
03092       if (!m_view->wrapCursor()) {
03093         // May have to restrain cursor to new smaller width...
03094         if (cursor.col() > m_doc->lineLength(cursor.line())) {
03095           KateLineRange thisRange = currentRange();
03096 
03097           KateTextCursor newCursor(cursor.line(), thisRange.endCol + ((width() - thisRange.xOffset() - (thisRange.endX - thisRange.startX)) / m_view->renderer()->spaceWidth()) - 1);
03098           updateCursor(newCursor);
03099         }
03100       }
03101     }
03102 
03103   } else {
03104     updateView();
03105 
03106     if (expandedHorizontally && startX() > 0)
03107       scrollColumns(startX() - (width() - e->oldSize().width()));
03108   }
03109 
03110   if (expandedVertically) {
03111     KateTextCursor max = maxStartPos();
03112     if (startPos() > max)
03113       scrollPos(max);
03114   }
03115 }
03116 
03117 void KateViewInternal::scrollTimeout ()
03118 {
03119   if (scrollX || scrollY)
03120   {
03121     scrollLines (startPos().line() + (scrollY / (int)m_view->renderer()->fontHeight()));
03122     placeCursor( QPoint( mouseX, mouseY ), true );
03123   }
03124 }
03125 
03126 void KateViewInternal::cursorTimeout ()
03127 {
03128   m_view->renderer()->setDrawCaret(!m_view->renderer()->drawCaret());
03129   paintCursor();
03130 }
03131 
03132 void KateViewInternal::textHintTimeout ()
03133 {
03134   m_textHintTimer.stop ();
03135 
03136   KateLineRange thisRange = yToKateLineRange(m_textHintMouseY);
03137 
03138   if (thisRange.line == -1) return;
03139 
03140   if (m_textHintMouseX> (lineMaxCursorX(thisRange) - thisRange.startX)) return;
03141 
03142   int realLine = thisRange.line;
03143   int startCol = thisRange.startCol;
03144 
03145   KateTextCursor c(realLine, 0);
03146   m_view->renderer()->textWidth( c, startX() + m_textHintMouseX, startCol);
03147 
03148   QString tmp;
03149 
03150   emit m_view->needTextHint(c.line(), c.col(), tmp);
03151 
03152   if (!tmp.isEmpty()) kdDebug(13030)<<"Hint text: "<<tmp<<endl;
03153 }
03154 
03155 void KateViewInternal::focusInEvent (QFocusEvent *)
03156 {
03157   if (KApplication::cursorFlashTime() > 0)
03158     m_cursorTimer.start ( KApplication::cursorFlashTime() / 2 );
03159 
03160   if (m_textHintEnabled)
03161     m_textHintTimer.start( m_textHintTimeout );
03162 
03163   paintCursor();
03164 
03165   m_doc->setActiveView( m_view );
03166 
03167   emit m_view->gotFocus( m_view );
03168 }
03169 
03170 void KateViewInternal::focusOutEvent (QFocusEvent *)
03171 {
03172   if( m_view->renderer() && ! m_view->m_codeCompletion->codeCompletionVisible() )
03173   {
03174     m_cursorTimer.stop();
03175 
03176     m_view->renderer()->setDrawCaret(true);
03177     paintCursor();
03178     emit m_view->lostFocus( m_view );
03179   }
03180 
03181   m_textHintTimer.stop();
03182 }
03183 
03184 void KateViewInternal::doDrag()
03185 {
03186   dragInfo.state = diDragging;
03187   dragInfo.dragObject = new QTextDrag(m_view->selection(), this);
03188   dragInfo.dragObject->drag();
03189 }
03190 
03191 void KateViewInternal::dragEnterEvent( QDragEnterEvent* event )
03192 {
03193   event->accept( (QTextDrag::canDecode(event) && m_doc->isReadWrite()) ||
03194                   KURLDrag::canDecode(event) );
03195 }
03196 
03197 void KateViewInternal::dragMoveEvent( QDragMoveEvent* event )
03198 {
03199   // track the cursor to the current drop location
03200   placeCursor( event->pos(), true, false );
03201 
03202   // important: accept action to switch between copy and move mode
03203   // without this, the text will always be copied.
03204   event->acceptAction();
03205 }
03206 
03207 void KateViewInternal::dropEvent( QDropEvent* event )
03208 {
03209   if ( KURLDrag::canDecode(event) ) {
03210 
03211       emit dropEventPass(event);
03212 
03213   } else if ( QTextDrag::canDecode(event) && m_doc->isReadWrite() ) {
03214 
03215     QString text;
03216 
03217     if (!QTextDrag::decode(event, text))
03218       return;
03219 
03220     // is the source our own document?
03221     bool priv = false;
03222     if (event->source() && event->source()->inherits("KateViewInternal"))
03223       priv = m_doc->ownedView( ((KateViewInternal*)(event->source()))->m_view );
03224 
03225     // dropped on a text selection area?
03226     bool selected = isTargetSelected( event->pos() );
03227 
03228     if( priv && selected ) {
03229       // this is a drag that we started and dropped on our selection
03230       // ignore this case
03231       return;
03232     }
03233 
03234     // use one transaction
03235     m_doc->editStart ();
03236 
03237     // on move: remove selected text; on copy: duplicate text
03238     if ( event->action() != QDropEvent::Copy )
03239       m_view->removeSelectedText();
03240 
03241     m_doc->insertText( cursor.line(), cursor.col(), text );
03242 
03243     m_doc->editEnd ();
03244 
03245     placeCursor( event->pos() );
03246 
03247     event->acceptAction();
03248     updateView();
03249   }
03250 
03251   // finally finish drag and drop mode
03252   dragInfo.state = diNone;
03253   // important, because the eventFilter`s DragLeave does not occur
03254   stopDragScroll();
03255 }
03256 //END EVENT HANDLING STUFF
03257 
03258 void KateViewInternal::clear()
03259 {
03260   cursor.setPos(0, 0);
03261   displayCursor.setPos(0, 0);
03262 }
03263 
03264 void KateViewInternal::wheelEvent(QWheelEvent* e)
03265 {
03266   if (m_lineScroll->minValue() != m_lineScroll->maxValue() && e->orientation() != Qt::Horizontal) {
03267     // React to this as a vertical event
03268     if ( ( e->state() & ControlButton ) || ( e->state() & ShiftButton ) ) {
03269       if (e->delta() > 0)
03270         scrollPrevPage();
03271       else
03272         scrollNextPage();
03273     } else {
03274       scrollViewLines(-((e->delta() / 120) * QApplication::wheelScrollLines()));
03275       // maybe a menu was opened or a bubbled window title is on us -> we shall erase it
03276       update();
03277       leftBorder->update();
03278     }
03279 
03280   } else if (columnScrollingPossible()) {
03281     QWheelEvent copy = *e;
03282     QApplication::sendEvent(m_columnScroll, &copy);
03283 
03284   } else {
03285     e->ignore();
03286   }
03287 }
03288 
03289 void KateViewInternal::startDragScroll()
03290 {
03291   if ( !m_dragScrollTimer.isActive() ) {
03292     m_dragScrollTimer.start( scrollTime );
03293   }
03294 }
03295 
03296 void KateViewInternal::stopDragScroll()
03297 {
03298   m_dragScrollTimer.stop();
03299   updateView();
03300 }
03301 
03302 void KateViewInternal::doDragScroll()
03303 {
03304   QPoint p = this->mapFromGlobal( QCursor::pos() );
03305 
03306   int dx = 0, dy = 0;
03307   if ( p.y() < scrollMargin ) {
03308     dy = p.y() - scrollMargin;
03309   } else if ( p.y() > height() - scrollMargin ) {
03310     dy = scrollMargin - (height() - p.y());
03311   }
03312 
03313   if ( p.x() < scrollMargin ) {
03314     dx = p.x() - scrollMargin;
03315   } else if ( p.x() > width() - scrollMargin ) {
03316     dx = scrollMargin - (width() - p.x());
03317   }
03318 
03319   dy /= 4;
03320 
03321   if (dy)
03322     scrollLines(startPos().line() + dy);
03323 
03324   if (columnScrollingPossible () && dx)
03325     scrollColumns(kMin (m_startX + dx, m_columnScroll->maxValue()));
03326 
03327   if (!dy && !dx)
03328     stopDragScroll();
03329 }
03330 
03331 void KateViewInternal::enableTextHints(int timeout)
03332 {
03333   m_textHintTimeout=timeout;
03334   m_textHintEnabled=true;
03335   m_textHintTimer.start(timeout);
03336 }
03337 
03338 void KateViewInternal::disableTextHints()
03339 {
03340   m_textHintEnabled=false;
03341   m_textHintTimer.stop ();
03342 }
03343 
03344 bool KateViewInternal::columnScrollingPossible ()
03345 {
03346   return !m_view->dynWordWrap() && m_columnScroll->isEnabled() && (m_columnScroll->maxValue() > 0);
03347 }
03348 
03349 //BEGIN EDIT STUFF
03350 void KateViewInternal::editStart()
03351 {
03352   editSessionNumber++;
03353 
03354   if (editSessionNumber > 1)
03355     return;
03356 
03357   editIsRunning = true;
03358   editOldCursor = cursor;
03359 }
03360 
03361 void KateViewInternal::editEnd(int editTagLineStart, int editTagLineEnd, bool tagFrom)
03362 {
03363    if (editSessionNumber == 0)
03364     return;
03365 
03366   editSessionNumber--;
03367 
03368   if (editSessionNumber > 0)
03369     return;
03370 
03371   if (tagFrom && (editTagLineStart <= int(m_doc->getRealLine(startLine()))))
03372     tagAll();
03373   else
03374     tagLines (editTagLineStart, tagFrom ? m_doc->lastLine() : editTagLineEnd, true);
03375 
03376   if (editOldCursor == cursor)
03377     updateBracketMarks();
03378 
03379   if (m_imPreeditLength <= 0)
03380     updateView(true);
03381 
03382   if ((editOldCursor != cursor) && (m_imPreeditLength <= 0))
03383   {
03384     m_madeVisible = false;
03385     updateCursor ( cursor, true );
03386   }
03387   else if ( m_view == m_doc->activeView() )
03388   {
03389     makeVisible(displayCursor, displayCursor.col());
03390   }
03391 
03392   editIsRunning = false;
03393 }
03394 
03395 void KateViewInternal::editSetCursor (const KateTextCursor &cursor)
03396 {
03397   if (this->cursor != cursor)
03398   {
03399     this->cursor.setPos (cursor);
03400   }
03401 }
03402 //END
03403 
03404 void KateViewInternal::viewSelectionChanged ()
03405 {
03406   if (!m_view->hasSelection())
03407   {
03408     selectAnchor.setPos (-1, -1);
03409     selStartCached.setPos (-1, -1);
03410   }
03411 }
03412 
03413 //BEGIN IM INPUT STUFF
03414 void KateViewInternal::imStartEvent( QIMEvent *e )
03415 {
03416   if ( m_doc->m_bReadOnly ) {
03417     e->ignore();
03418     return;
03419   }
03420 
03421   if ( m_view->hasSelection() )
03422     m_view->removeSelectedText();
03423 
03424   m_imPreeditStartLine = cursor.line();
03425   m_imPreeditStart = cursor.col();
03426   m_imPreeditLength = 0;
03427   m_imPreeditSelStart = m_imPreeditStart;
03428 
03429   m_view->setIMSelectionValue( m_imPreeditStartLine, m_imPreeditStart, 0, 0, 0, true );
03430 }
03431 
03432 void KateViewInternal::imComposeEvent( QIMEvent *e )
03433 {
03434   if ( m_doc->m_bReadOnly ) {
03435     e->ignore();
03436     return;
03437   }
03438 
03439   // remove old preedit
03440   if ( m_imPreeditLength > 0 ) {
03441     cursor.setPos( m_imPreeditStartLine, m_imPreeditStart );
03442     m_doc->removeText( m_imPreeditStartLine, m_imPreeditStart,
03443                        m_imPreeditStartLine, m_imPreeditStart + m_imPreeditLength );
03444   }
03445 
03446   m_imPreeditLength = e->text().length();
03447   m_imPreeditSelStart = m_imPreeditStart + e->cursorPos();
03448 
03449   // update selection
03450   m_view->setIMSelectionValue( m_imPreeditStartLine, m_imPreeditStart, m_imPreeditStart + m_imPreeditLength,
03451                               m_imPreeditSelStart, m_imPreeditSelStart + e->selectionLength(),
03452                               true );
03453 
03454   // insert new preedit
03455   m_doc->insertText( m_imPreeditStartLine, m_imPreeditStart, e->text() );
03456 
03457 
03458   // update cursor
03459   cursor.setPos( m_imPreeditStartLine, m_imPreeditSelStart );
03460   updateCursor( cursor, true );
03461 
03462   updateView( true );
03463 }
03464 
03465 void KateViewInternal::imEndEvent( QIMEvent *e )
03466 {
03467   if ( m_doc->m_bReadOnly ) {
03468     e->ignore();
03469     return;
03470   }
03471 
03472   if ( m_imPreeditLength > 0 ) {
03473     cursor.setPos( m_imPreeditStartLine, m_imPreeditStart );
03474     m_doc->removeText( m_imPreeditStartLine, m_imPreeditStart,
03475                        m_imPreeditStartLine, m_imPreeditStart + m_imPreeditLength );
03476   }
03477 
03478   m_view->setIMSelectionValue( m_imPreeditStartLine, m_imPreeditStart, 0, 0, 0, false );
03479 
03480   if ( e->text().length() > 0 ) {
03481     m_doc->insertText( cursor.line(), cursor.col(), e->text() );
03482 
03483     if ( !m_cursorTimer.isActive() && KApplication::cursorFlashTime() > 0 )
03484       m_cursorTimer.start ( KApplication::cursorFlashTime() / 2 );
03485 
03486     updateView( true );
03487     updateCursor( cursor, true );
03488   }
03489 
03490   m_imPreeditStart = 0;
03491   m_imPreeditLength = 0;
03492   m_imPreeditSelStart = 0;
03493 }
03494 //END IM INPUT STUFF
03495 
03496 // kate: space-indent on; indent-width 2; replace-tabs on;

Kate

Skip menu "Kate"
  • Main Page
  • Namespace List
  • Class Hierarchy
  • Alphabetical List
  • Class List
  • File List
  • Namespace Members
  • Class Members
  • Related Pages

API Reference

Skip menu "API Reference"
  • dcop
  • DNSSD
  • interfaces
  • Kate
  • kconf_update
  • KDECore
  • KDED
  • kdefx
  • KDEsu
  • kdeui
  • KDocTools
  • KHTML
  • KImgIO
  • KInit
  • kio
  • kioslave
  • KJS
  • KNewStuff
  • KParts
  • KUtils
Generated for API Reference by doxygen 1.5.9
This website is maintained by Adriaan de Groot and Allen Winter.
KDE® and the K Desktop Environment® logo are registered trademarks of KDE e.V. | Legal