kateviewinternal.cpp
00001 /* This file is part of the KDE libraries 00002 Copyright (C) 2002 John Firebaugh <jfirebaugh@kde.org> 00003 Copyright (C) 2002 Joseph Wenninger <jowenn@kde.org> 00004 Copyright (C) 2002,2003 Christoph Cullmann <cullmann@kde.org> 00005 Copyright (C) 2002,2003 Hamish Rodda <rodda@kde.org> 00006 Copyright (C) 2003 Anakim Border <aborder@sources.sourceforge.net> 00007 00008 Based on: 00009 KWriteView : Copyright (C) 1999 Jochen Wilhelmy <digisnap@cs.tu-berlin.de> 00010 00011 This library is free software; you can redistribute it and/or 00012 modify it under the terms of the GNU Library General Public 00013 License version 2 as published by the Free Software Foundation. 00014 00015 This library is distributed in the hope that it will be useful, 00016 but WITHOUT ANY WARRANTY; without even the implied warranty of 00017 MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU 00018 Library General Public License for more details. 00019 00020 You should have received a copy of the GNU Library General Public License 00021 along with this library; see the file COPYING.LIB. If not, write to 00022 the Free Software Foundation, Inc., 51 Franklin Street, Fifth Floor, 00023 Boston, MA 02110-1301, USA. 00024 */ 00025 00026 #include "kateviewinternal.h" 00027 #include "kateviewinternal.moc" 00028 00029 #include "kateview.h" 00030 #include "katecodefoldinghelpers.h" 00031 #include "kateviewhelpers.h" 00032 #include "katehighlight.h" 00033 #include "katesupercursor.h" 00034 #include "katerenderer.h" 00035 #include "katecodecompletion.h" 00036 #include "kateconfig.h" 00037 00038 #include <kcursor.h> 00039 #include <kdebug.h> 00040 #include <tdeapplication.h> 00041 #include <tdeglobalsettings.h> 00042 #include <kurldrag.h> 00043 00044 #include <tqstyle.h> 00045 #include <tqdragobject.h> 00046 #include <tqpopupmenu.h> 00047 #include <tqdropsite.h> 00048 #include <tqpainter.h> 00049 #include <tqlayout.h> 00050 #include <tqclipboard.h> 00051 #include <tqpixmap.h> 00052 #include <tqvbox.h> 00053 00054 KateViewInternal::KateViewInternal(KateView *view, KateDocument *doc) 00055 : TQWidget (view, "", (WFlags)(WStaticContents | WRepaintNoErase | WResizeNoErase) ) 00056 , editSessionNumber (0) 00057 , editIsRunning (false) 00058 , m_view (view) 00059 , m_doc (doc) 00060 , cursor (doc, true, 0, 0, TQT_TQOBJECT(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(Qt::Vertical, this); 00098 m_lineScroll->show(); 00099 m_lineScroll->setTracking (true); 00100 00101 m_lineLayout = new TQVBoxLayout(); 00102 m_colLayout = new TQHBoxLayout(); 00103 00104 m_colLayout->addWidget(m_lineScroll); 00105 m_lineLayout->addLayout(m_colLayout); 00106 00107 // bottom corner box 00108 m_dummy = new TQWidget(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, TQT_SIGNAL(prevPage()), TQT_SLOT(scrollPrevPage())); 00120 connect(m_lineScroll, TQT_SIGNAL(nextPage()), TQT_SLOT(scrollNextPage())); 00121 00122 connect(m_lineScroll, TQT_SIGNAL(prevLine()), TQT_SLOT(scrollPrevLine())); 00123 connect(m_lineScroll, TQT_SIGNAL(nextLine()), TQT_SLOT(scrollNextLine())); 00124 00125 connect(m_lineScroll, TQT_SIGNAL(sliderMoved(int)), TQT_SLOT(scrollLines(int))); 00126 connect(m_lineScroll, TQT_SIGNAL(sliderMMBMoved(int)), TQT_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 TQScrollBar(Qt::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, TQT_SIGNAL( valueChanged (int) ), 00146 this, TQT_SLOT( scrollColumns (int) ) ); 00147 00148 // 00149 // iconborder ;) 00150 // 00151 leftBorder = new KateIconBorder( this, m_view ); 00152 leftBorder->show (); 00153 00154 connect( leftBorder, TQT_SIGNAL(toggleRegionVisibility(unsigned int)), 00155 m_doc->foldingTree(), TQT_SLOT(toggleRegionVisibility(unsigned int))); 00156 00157 connect( doc->foldingTree(), TQT_SIGNAL(regionVisibilityChangedAt(unsigned int)), 00158 this, TQT_SLOT(slotRegionVisibilityChangedAt(unsigned int))); 00159 connect( doc, TQT_SIGNAL(codeFoldingUpdated()), 00160 this, TQT_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 = TQt::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, TQT_SIGNAL( timeout() ), 00186 this, TQT_SLOT( doDragScroll() ) ); 00187 00188 connect( &m_scrollTimer, TQT_SIGNAL( timeout() ), 00189 this, TQT_SLOT( scrollTimeout() ) ); 00190 00191 connect( &m_cursorTimer, TQT_SIGNAL( timeout() ), 00192 this, TQT_SLOT( cursorTimeout() ) ); 00193 00194 connect( &m_textHintTimer, TQT_SIGNAL( timeout() ), 00195 this, TQT_SLOT( textHintTimeout() ) ); 00196 00197 // selection changed to set anchor 00198 connect( m_view, TQT_SIGNAL( selectionChanged() ), 00199 this, TQT_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 (TQApplication::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 TQPixmap 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 TQPainter paint(this); 00734 TQPainter 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 ( TQShowEvent *e ) 00843 { 00844 updateView (); 00845 00846 TQWidget::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 TQPoint KateViewInternal::cursorCoordinates() 00858 { 00859 int viewLine = displayViewLine(displayCursor, true); 00860 00861 if (viewLine == -1) 00862 return TQPoint(-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 TQPoint(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_b ) m_col = 0; 00980 else if( bias == right_b ) m_col = m_vi->m_doc->lineLength( line() ); 00981 } 00982 00983 bool atEdge() const { return atEdge( left_b ) || atEdge( right_b ); } 00984 00985 bool atEdge( Bias bias ) const { 00986 switch( bias ) { 00987 case left_b: return col() == 0; 00988 case none: return atEdge(); 00989 case right_b: 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_b, 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_b, 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_b ) ) { 01138 01139 while( !c.atEdge( left_b ) && m_doc->textLine( c.line() )[ c.col() - 1 ].isSpace() ) 01140 --c; 01141 } 01142 if( c.atEdge( left_b ) ) 01143 { 01144 --c; 01145 } 01146 else if( h->isInWord( m_doc->textLine( c.line() )[ c.col() - 1 ] ) ) 01147 { 01148 while( !c.atEdge( left_b ) && h->isInWord( m_doc->textLine( c.line() )[ c.col() - 1 ] ) ) 01149 --c; 01150 } 01151 else 01152 { 01153 while( !c.atEdge( left_b ) 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_b ) ) 01181 { 01182 ++c; 01183 } 01184 else if( h->isInWord( m_doc->textLine( c.line() )[ c.col() ] ) ) 01185 { 01186 while( !c.atEdge( right_b ) && h->isInWord( m_doc->textLine( c.line() )[ c.col() ] ) ) 01187 ++c; 01188 } 01189 else 01190 { 01191 while( !c.atEdge( right_b ) 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_b ) && 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 TQKeyEvent e(TQEvent::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_b, 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 TQKeyEvent e(TQEvent::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_b, 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_b, 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 TQChar lastCharInLine = textLine(range.line)->getChar(range.endCol - 1); 01656 01657 if (lastCharInLine == TQChar('\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) == TQChar('\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 TQKeyEvent e(TQEvent::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 TQKeyEvent e(TQEvent::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 TQKeyEvent e(TQEvent::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 TQKeyEvent e(TQEvent::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 TQKeyEvent e(TQEvent::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 TQKeyEvent e(TQEvent::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 ( TDEApplication::cursorFlashTime() > 0 ) 02217 m_cursorTimer.start( TDEApplication::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 TQPoint& 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 TQPoint& 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( TQObject *obj, TQEvent *e ) 02440 { 02441 if (TQT_BASE_OBJECT(obj) == TQT_BASE_OBJECT(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() == TQEvent::Wheel && m_lineScroll->minValue() != m_lineScroll->maxValue()) 02445 { 02446 wheelEvent((TQWheelEvent*)e); 02447 return true; 02448 } 02449 02450 // continue processing 02451 return TQWidget::eventFilter( obj, e ); 02452 } 02453 02454 switch( e->type() ) 02455 { 02456 case TQEvent::KeyPress: 02457 { 02458 TQKeyEvent *k = (TQKeyEvent *)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 TQEvent::DragMove: 02482 { 02483 TQPoint currentPoint = ((TQDragMoveEvent*) e)->pos(); 02484 02485 TQRect 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 ( (TQDragMoveEvent*)e )->accept( TQRect(0,0,0,0) ); 02494 } 02495 02496 dragMoveEvent((TQDragMoveEvent*)e); 02497 } break; 02498 02499 case TQEvent::DragLeave: 02500 // happens only when pressing ESC while dragging 02501 stopDragScroll(); 02502 break; 02503 02504 case TQEvent::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 TQWidget::eventFilter( obj, e ); 02515 } 02516 02517 void KateViewInternal::keyPressEvent( TQKeyEvent* 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, TQString ("\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( TQKeyEvent* 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 TQApplication::clipboard()->setSelectionMode( true ); 02653 m_view->copy(); 02654 TQApplication::clipboard()->setSelectionMode( false ); 02655 02656 m_selChangedByUser = false; 02657 } 02658 } 02659 } 02660 02661 e->ignore(); 02662 return; 02663 } 02664 02665 void KateViewInternal::contextMenuEvent ( TQContextMenuEvent * e ) 02666 { 02667 // try to show popup menu 02668 02669 TQPoint 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() == TQContextMenuEvent::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( TQMouseEvent* e ) 02693 { 02694 switch (e->button()) 02695 { 02696 case Qt::LeftButton: 02697 m_selChangedByUser = false; 02698 02699 if (possibleTripleClick) 02700 { 02701 possibleTripleClick = false; 02702 02703 m_selectionMode = Line; 02704 02705 if ( e->state() & TQt::ShiftButton ) 02706 { 02707 updateSelection( cursor, true ); 02708 } 02709 else 02710 { 02711 m_view->selectLine( cursor ); 02712 } 02713 02714 TQApplication::clipboard()->setSelectionMode( true ); 02715 m_view->copy(); 02716 TQApplication::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() & TQt::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() & TQt::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() & TQt::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(TQMouseEvent *e) 02820 { 02821 switch (e->button()) 02822 { 02823 case Qt::LeftButton: 02824 m_selectionMode = Word; 02825 02826 if ( e->state() & TQt::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 TQApplication::clipboard()->setSelectionMode( true ); 02893 m_view->copy(); 02894 TQApplication::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 TQTimer::singleShot ( TQApplication::doubleClickInterval(), this, TQT_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( TQMouseEvent* e ) 02927 { 02928 switch (e->button()) 02929 { 02930 case Qt::LeftButton: 02931 m_selectionMode = Default; 02932 // selStartCached.setLine( -1 ); 02933 02934 if (m_selChangedByUser) 02935 { 02936 TQApplication::clipboard()->setSelectionMode( true ); 02937 m_view->copy(); 02938 TQApplication::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 Qt::MidButton: 02960 placeCursor( e->pos() ); 02961 02962 if( m_doc->isReadWrite() ) 02963 { 02964 TQApplication::clipboard()->setSelectionMode( true ); 02965 m_view->paste (); 02966 TQApplication::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( TQMouseEvent* e ) 02979 { 02980 if( e->state() & Qt::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 TQPoint 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() > TDEGlobalSettings::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( TQPoint( 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 = TQt::ArrowCursor; 03037 } 03038 } else { 03039 // normal text cursor 03040 if (m_mouseCursor != IbeamCursor) { 03041 setCursor( KCursor::ibeamCursor() ); 03042 m_mouseCursor = TQt::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(TQPaintEvent *e) 03056 { 03057 paintText(e->rect().x(), e->rect().y(), e->rect().width(), e->rect().height()); 03058 } 03059 03060 void KateViewInternal::resizeEvent(TQResizeEvent* 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( TQPoint( 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 TQString 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 (TQFocusEvent *) 03156 { 03157 if (TDEApplication::cursorFlashTime() > 0) 03158 m_cursorTimer.start ( TDEApplication::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 (TQFocusEvent *) 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 TQTextDrag(m_view->selection(), this); 03188 dragInfo.dragObject->drag(); 03189 } 03190 03191 void KateViewInternal::dragEnterEvent( TQDragEnterEvent* event ) 03192 { 03193 event->accept( (TQTextDrag::canDecode(event) && m_doc->isReadWrite()) || 03194 KURLDrag::canDecode(event) ); 03195 } 03196 03197 void KateViewInternal::dragMoveEvent( TQDragMoveEvent* 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( TQDropEvent* event ) 03208 { 03209 if ( KURLDrag::canDecode(event) ) { 03210 03211 emit dropEventPass(event); 03212 03213 } else if ( TQTextDrag::canDecode(event) && m_doc->isReadWrite() ) { 03214 03215 TQString text; 03216 03217 if (!TQTextDrag::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() != TQDropEvent::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(TQWheelEvent* 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) * TQApplication::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 TQWheelEvent copy = *e; 03282 TQApplication::sendEvent(m_columnScroll, ©); 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 TQPoint p = this->mapFromGlobal( TQCursor::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( TQIMEvent *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( TQIMEvent *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( TQIMEvent *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() && TDEApplication::cursorFlashTime() > 0 ) 03484 m_cursorTimer.start ( TDEApplication::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;