katerenderer.cpp
00001 /* This file is part of the KDE libraries 00002 Copyright (C) 2003 Hamish Rodda <rodda@kde.org> 00003 Copyright (C) 2001 Christoph Cullmann <cullmann@kde.org> 00004 Copyright (C) 2001 Joseph Wenninger <jowenn@kde.org> 00005 Copyright (C) 1999 Jochen Wilhelmy <digisnap@cs.tu-berlin.de> 00006 00007 This library is free software; you can redistribute it and/or 00008 modify it under the terms of the GNU Library General Public 00009 License version 2 as published by the Free Software Foundation. 00010 00011 This library is distributed in the hope that it will be useful, 00012 but WITHOUT ANY WARRANTY; without even the implied warranty of 00013 MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU 00014 Library General Public License for more details. 00015 00016 You should have received a copy of the GNU Library General Public License 00017 along with this library; see the file COPYING.LIB. If not, write to 00018 the Free Software Foundation, Inc., 51 Franklin Street, Fifth Floor, 00019 Boston, MA 02110-1301, USA. 00020 */ 00021 00022 #include "katerenderer.h" 00023 00024 #include "katelinerange.h" 00025 #include "katedocument.h" 00026 #include "katearbitraryhighlight.h" 00027 #include "kateconfig.h" 00028 #include "katehighlight.h" 00029 #include "katefactory.h" 00030 #include "kateview.h" 00031 00032 #include <kdebug.h> 00033 00034 #include <qpainter.h> 00035 #include <qpopupmenu.h> 00036 00037 KateRenderer::KateRenderer(KateDocument* doc, KateView *view) 00038 : m_doc(doc), m_view (view), m_caretStyle(KateRenderer::Insert) 00039 , m_drawCaret(true) 00040 , m_showSelections(true) 00041 , m_showTabs(true) 00042 , m_printerFriendly(false) 00043 { 00044 KateFactory::self()->registerRenderer ( this ); 00045 m_config = new KateRendererConfig (this); 00046 00047 m_tabWidth = m_doc->config()->tabWidth(); 00048 m_indentWidth = m_tabWidth; 00049 if (m_doc->config()->configFlags() & KateDocumentConfig::cfSpaceIndent) 00050 { 00051 m_indentWidth = m_doc->config()->indentationWidth(); 00052 } 00053 00054 updateAttributes (); 00055 } 00056 00057 KateRenderer::~KateRenderer() 00058 { 00059 delete m_config; 00060 KateFactory::self()->deregisterRenderer ( this ); 00061 } 00062 00063 void KateRenderer::updateAttributes () 00064 { 00065 m_schema = config()->schema (); 00066 m_attributes = m_doc->highlight()->attributes (m_schema); 00067 } 00068 00069 KateAttribute* KateRenderer::attribute(uint pos) 00070 { 00071 if (pos < m_attributes->size()) 00072 return &m_attributes->at(pos); 00073 00074 return &m_attributes->at(0); 00075 } 00076 00077 void KateRenderer::setDrawCaret(bool drawCaret) 00078 { 00079 m_drawCaret = drawCaret; 00080 } 00081 00082 void KateRenderer::setCaretStyle(KateRenderer::caretStyles style) 00083 { 00084 m_caretStyle = style; 00085 } 00086 00087 void KateRenderer::setShowTabs(bool showTabs) 00088 { 00089 m_showTabs = showTabs; 00090 } 00091 00092 void KateRenderer::setTabWidth(int tabWidth) 00093 { 00094 m_tabWidth = tabWidth; 00095 } 00096 00097 bool KateRenderer::showIndentLines() const 00098 { 00099 return m_config->showIndentationLines(); 00100 } 00101 00102 void KateRenderer::setShowIndentLines(bool showIndentLines) 00103 { 00104 m_config->setShowIndentationLines(showIndentLines); 00105 } 00106 00107 void KateRenderer::setIndentWidth(int indentWidth) 00108 { 00109 m_indentWidth = m_tabWidth; 00110 if (m_doc->config()->configFlags() & KateDocumentConfig::cfSpaceIndent) 00111 { 00112 m_indentWidth = indentWidth; 00113 } 00114 } 00115 00116 void KateRenderer::setShowSelections(bool showSelections) 00117 { 00118 m_showSelections = showSelections; 00119 } 00120 00121 void KateRenderer::increaseFontSizes() 00122 { 00123 QFont f ( *config()->font () ); 00124 f.setPointSize (f.pointSize ()+1); 00125 00126 config()->setFont (f); 00127 } 00128 00129 void KateRenderer::decreaseFontSizes() 00130 { 00131 QFont f ( *config()->font () ); 00132 00133 if ((f.pointSize ()-1) > 0) 00134 f.setPointSize (f.pointSize ()-1); 00135 00136 config()->setFont (f); 00137 } 00138 00139 bool KateRenderer::isPrinterFriendly() const 00140 { 00141 return m_printerFriendly; 00142 } 00143 00144 void KateRenderer::setPrinterFriendly(bool printerFriendly) 00145 { 00146 m_printerFriendly = printerFriendly; 00147 setShowTabs(false); 00148 setShowSelections(false); 00149 setDrawCaret(false); 00150 } 00151 00152 bool KateRenderer::paintTextLineBackground(QPainter& paint, int line, bool isCurrentLine, int xStart, int xEnd) 00153 { 00154 if (isPrinterFriendly()) 00155 return false; 00156 00157 // font data 00158 KateFontStruct *fs = config()->fontStruct(); 00159 00160 // Normal background color 00161 QColor backgroundColor( config()->backgroundColor() ); 00162 00163 bool selectionPainted = false; 00164 if (showSelections() && m_view->lineSelected(line)) 00165 { 00166 backgroundColor = config()->selectionColor(); 00167 selectionPainted = true; 00168 } 00169 else 00170 { 00171 // paint the current line background if we're on the current line 00172 if (isCurrentLine) 00173 backgroundColor = config()->highlightedLineColor(); 00174 00175 // Check for mark background 00176 int markRed = 0, markGreen = 0, markBlue = 0, markCount = 0; 00177 00178 // Retrieve marks for this line 00179 uint mrk = m_doc->mark( line ); 00180 if (mrk) 00181 { 00182 for (uint bit = 0; bit < 32; bit++) 00183 { 00184 KTextEditor::MarkInterface::MarkTypes markType = (KTextEditor::MarkInterface::MarkTypes)(1<<bit); 00185 if (mrk & markType) 00186 { 00187 QColor markColor = config()->lineMarkerColor(markType); 00188 00189 if (markColor.isValid()) { 00190 markCount++; 00191 markRed += markColor.red(); 00192 markGreen += markColor.green(); 00193 markBlue += markColor.blue(); 00194 } 00195 } 00196 } // for 00197 } // Marks 00198 00199 if (markCount) { 00200 markRed /= markCount; 00201 markGreen /= markCount; 00202 markBlue /= markCount; 00203 backgroundColor.setRgb( 00204 int((backgroundColor.red() * 0.9) + (markRed * 0.1)), 00205 int((backgroundColor.green() * 0.9) + (markGreen * 0.1)), 00206 int((backgroundColor.blue() * 0.9) + (markBlue * 0.1)) 00207 ); 00208 } 00209 } // background preprocessing 00210 00211 // Draw line background 00212 paint.fillRect(0, 0, xEnd - xStart, fs->fontHeight, backgroundColor); 00213 00214 return selectionPainted; 00215 } 00216 00217 void KateRenderer::paintWhitespaceMarker(QPainter &paint, uint x, uint y) 00218 { 00219 QPen penBackup( paint.pen() ); 00220 paint.setPen( config()->tabMarkerColor() ); 00221 paint.drawPoint(x, y); 00222 paint.drawPoint(x + 1, y); 00223 paint.drawPoint(x, y - 1); 00224 paint.setPen( penBackup ); 00225 } 00226 00227 00228 void KateRenderer::paintIndentMarker(QPainter &paint, uint x, uint row) 00229 { 00230 QPen penBackup( paint.pen() ); 00231 paint.setPen( config()->tabMarkerColor() ); 00232 00233 const int top = paint.window().top(); 00234 const int bottom = paint.window().bottom(); 00235 const int h = bottom - top + 1; 00236 00237 // Dot padding. 00238 int pad = 0; 00239 if(row & 1 && h & 1) pad = 1; 00240 00241 for(int i = top; i <= bottom; i++) 00242 { 00243 if((i + pad) & 1) 00244 { 00245 paint.drawPoint(x + 2, i); 00246 } 00247 } 00248 00249 paint.setPen( penBackup ); 00250 } 00251 00252 00253 void KateRenderer::paintTextLine(QPainter& paint, const KateLineRange* range, int xStart, int xEnd, const KateTextCursor* cursor, const KateBracketRange* bracketmark) 00254 { 00255 int line = range->line; 00256 00257 // textline 00258 KateTextLine::Ptr textLine = m_doc->kateTextLine(line); 00259 if (!textLine) 00260 return; 00261 00262 bool showCursor = drawCaret() && cursor && range->includesCursor(*cursor); 00263 00264 KateSuperRangeList& superRanges = m_doc->arbitraryHL()->rangesIncluding(range->line, 0); 00265 00266 int minIndent = 0; 00267 00268 // A bit too verbose for my tastes 00269 // Re-write a bracketmark class? put into its own function? add more helper constructors to the range stuff? 00270 // Also, need a light-weight arbitraryhighlightrange class for static stuff 00271 KateArbitraryHighlightRange* bracketStartRange (0L); 00272 KateArbitraryHighlightRange* bracketEndRange (0L); 00273 if (bracketmark && bracketmark->isValid()) { 00274 if (range->includesCursor(bracketmark->start())) { 00275 KateTextCursor startend = bracketmark->start(); 00276 startend.setCol(startend.col()+1); 00277 bracketStartRange = new KateArbitraryHighlightRange(m_doc, bracketmark->start(), startend); 00278 bracketStartRange->setBGColor(config()->highlightedBracketColor()); 00279 bracketStartRange->setBold(true); 00280 superRanges.append(bracketStartRange); 00281 } 00282 00283 if (range->includesCursor(bracketmark->end())) { 00284 KateTextCursor endend = bracketmark->end(); 00285 endend.setCol(endend.col()+1); 00286 bracketEndRange = new KateArbitraryHighlightRange(m_doc, bracketmark->end(), endend); 00287 bracketEndRange->setBGColor(config()->highlightedBracketColor()); 00288 bracketEndRange->setBold(true); 00289 superRanges.append(bracketEndRange); 00290 } 00291 00292 Q_ASSERT(bracketmark->start().line() <= bracketmark->end().line()); 00293 if (bracketmark->start().line() < line && bracketmark->end().line() >= line) 00294 { 00295 minIndent = bracketmark->getMinIndent(); 00296 } 00297 } 00298 00299 00300 // length, chars + raw attribs 00301 uint len = textLine->length(); 00302 uint oldLen = len; 00303 00304 // should the cursor be painted (if it is in the current xstart - xend range) 00305 bool cursorVisible = false; 00306 int cursorMaxWidth = 0; 00307 00308 // font data 00309 KateFontStruct * fs = config()->fontStruct(); 00310 00311 // Paint selection background as the whole line is selected 00312 // selection startcol/endcol calc 00313 bool hasSel = false; 00314 uint startSel = 0; 00315 uint endSel = 0; 00316 00317 // was the selection background already completely painted ? 00318 bool selectionPainted = false; 00319 bool isCurrentLine = (cursor && range->includesCursor(*cursor)); 00320 selectionPainted = paintTextLineBackground(paint, line, isCurrentLine, xStart, xEnd); 00321 if (selectionPainted) 00322 { 00323 hasSel = true; 00324 startSel = 0; 00325 endSel = len + 1; 00326 } 00327 00328 int startcol = range->startCol; 00329 if (startcol > (int)len) 00330 startcol = len; 00331 00332 if (startcol < 0) 00333 startcol = 0; 00334 00335 int endcol = range->wrap ? range->endCol : -1; 00336 if (endcol < 0) 00337 len = len - startcol; 00338 else 00339 len = endcol - startcol; 00340 00341 // text attribs font/style data 00342 KateAttribute* attr = m_doc->highlight()->attributes(m_schema)->data(); 00343 00344 const QColor *cursorColor = &attr[0].textColor(); 00345 00346 // Start arbitrary highlighting 00347 KateTextCursor currentPos(line, startcol); 00348 superRanges.firstBoundary(¤tPos); 00349 00350 if (showSelections() && !selectionPainted) 00351 hasSel = getSelectionBounds(line, oldLen, startSel, endSel); 00352 00353 // Draws the dashed underline at the start of a folded block of text. 00354 if (range->startsInvisibleBlock) { 00355 paint.setPen(QPen(config()->wordWrapMarkerColor(), 1, Qt::DashLine)); 00356 paint.drawLine(0, fs->fontHeight - 1, xEnd - xStart, fs->fontHeight - 1); 00357 } 00358 00359 // draw word-wrap-honor-indent filling 00360 if (range->xOffset() && range->xOffset() > xStart) 00361 { 00362 paint.fillRect(0, 0, range->xOffset() - xStart, fs->fontHeight, 00363 QBrush(config()->wordWrapMarkerColor(), QBrush::DiagCrossPattern)); 00364 } 00365 00366 // painting loop 00367 uint xPos = range->xOffset(); 00368 int cursorXPos = 0; 00369 00370 // Optimisation to quickly draw an empty line of text 00371 if (len < 1) 00372 { 00373 if (showCursor && (cursor->col() >= int(startcol))) 00374 { 00375 cursorVisible = true; 00376 cursorXPos = xPos + cursor->col() * fs->myFontMetrics.width(QChar(' ')); 00377 } 00378 } 00379 else 00380 { 00381 bool isIMSel = false; 00382 bool isIMEdit = false; 00383 00384 bool isSel = false; 00385 00386 KateAttribute customHL; 00387 00388 const QColor *curColor = 0; 00389 const QColor *oldColor = 0; 00390 00391 KateAttribute* oldAt = &attr[0]; 00392 00393 uint oldXPos = xPos; 00394 uint xPosAfter = xPos; 00395 00396 KateAttribute currentHL; 00397 00398 uint blockStartCol = startcol; 00399 uint curCol = startcol; 00400 uint nextCol = curCol + 1; 00401 00402 // text + attrib data from line 00403 const uchar *textAttributes = textLine->attributes (); 00404 bool noAttribs = !textAttributes; 00405 00406 // adjust to startcol ;) 00407 textAttributes = textAttributes + startcol; 00408 00409 uint atLen = m_doc->highlight()->attributes(m_schema)->size(); 00410 00411 // Determine if we have trailing whitespace and store the column 00412 // if lastChar == -1, set to 0, if lastChar exists, increase by one 00413 uint trailingWhitespaceColumn = textLine->lastChar() + 1; 00414 const uint lastIndentColumn = textLine->firstChar(); 00415 00416 // Could be precomputed. 00417 const uint spaceWidth = fs->width (QChar(' '), false, false, m_tabWidth); 00418 00419 // Get current x position. 00420 int curPos = textLine->cursorX(curCol, m_tabWidth); 00421 00422 while (curCol - startcol < len) 00423 { 00424 // make sure curPos is updated correctly. 00425 // ### if uncommented, causes an O(n^2) behaviour 00426 //Q_ASSERT(curPos == textLine->cursorX(curCol, m_tabWidth)); 00427 00428 QChar curChar = textLine->string()[curCol]; 00429 // Decide if this character is a tab - we treat the spacing differently 00430 // TODO: move tab width calculation elsewhere? 00431 bool isTab = curChar == QChar('\t'); 00432 00433 // Determine current syntax highlighting attribute 00434 // A bit legacy but doesn't need to change 00435 KateAttribute* curAt = (noAttribs || ((*textAttributes) >= atLen)) ? &attr[0] : &attr[*textAttributes]; 00436 00437 // X position calculation. Incorrect for fonts with non-zero leftBearing() and rightBearing() results. 00438 // TODO: make internal charWidth() function, use QFontMetrics::charWidth(). 00439 xPosAfter += curAt->width(*fs, curChar, m_tabWidth); 00440 00441 // Tab special treatment, move to charWidth(). 00442 if (isTab) 00443 xPosAfter -= (xPosAfter % curAt->width(*fs, curChar, m_tabWidth)); 00444 00445 // Only draw after the starting X value 00446 // Haha, this was always wrong, due to the use of individual char width calculations...?? :( 00447 if ((int)xPosAfter >= xStart) 00448 { 00449 // Determine if we're in a selection and should be drawing it 00450 isSel = (showSelections() && hasSel && (curCol >= startSel) && (curCol < endSel)); 00451 00452 // input method edit area 00453 isIMEdit = m_view && m_view->isIMEdit( line, curCol ); 00454 00455 // input method selection 00456 isIMSel = m_view && m_view->isIMSelection( line, curCol ); 00457 00458 // Determine current color, taking into account selection 00459 curColor = isSel ? &(curAt->selectedTextColor()) : &(curAt->textColor()); 00460 00461 // Incorporate in arbitrary highlighting 00462 if (curAt != oldAt || curColor != oldColor || (superRanges.count() && superRanges.currentBoundary() && *(superRanges.currentBoundary()) == currentPos)) { 00463 if (superRanges.count() && superRanges.currentBoundary() && *(superRanges.currentBoundary()) == currentPos) 00464 customHL = KateArbitraryHighlightRange::merge(superRanges.rangesIncluding(currentPos)); 00465 00466 KateAttribute hl = customHL; 00467 00468 hl += *curAt; 00469 00470 // use default highlighting color if we haven't defined one above. 00471 if (!hl.itemSet(KateAttribute::TextColor)) 00472 hl.setTextColor(*curColor); 00473 00474 if (!isSel) 00475 paint.setPen(hl.textColor()); 00476 else 00477 paint.setPen(hl.selectedTextColor()); 00478 00479 paint.setFont(hl.font(*currentFont())); 00480 00481 if (superRanges.currentBoundary() && *(superRanges.currentBoundary()) == currentPos) 00482 superRanges.nextBoundary(); 00483 00484 currentHL = hl; 00485 } 00486 00487 // Determine whether we can delay painting to draw a block of similarly formatted 00488 // characters or not 00489 // Reasons for NOT delaying the drawing until the next character 00490 // You have to detect the change one character in advance. 00491 // TODO: KateAttribute::canBatchRender() 00492 bool renderNow = false; 00493 if ((isTab) 00494 // formatting has changed OR 00495 || (superRanges.count() && superRanges.currentBoundary() && *(superRanges.currentBoundary()) == KateTextCursor(line, nextCol)) 00496 00497 // it is the end of the line OR 00498 || (curCol - startcol >= len - 1) 00499 00500 // the rest of the line is trailing whitespace OR 00501 || (curCol + 1 >= trailingWhitespaceColumn) 00502 00503 // indentation lines OR 00504 || (showIndentLines() && curCol < lastIndentColumn) 00505 00506 // the x position is past the end OR 00507 || ((int)xPos > xEnd) 00508 00509 // it is a different attribute OR 00510 || (!noAttribs && curAt != &attr[*(textAttributes+1)]) 00511 00512 // the selection boundary was crossed OR 00513 || (isSel != (hasSel && (nextCol >= startSel) && (nextCol < endSel))) 00514 00515 // the next char is a tab (removed the "and this isn't" because that's dealt with above) 00516 // i.e. we have to draw the current text so the tab can be rendered as above. 00517 || (textLine->string()[nextCol] == QChar('\t')) 00518 00519 // input method edit area 00520 || ( m_view && (isIMEdit != m_view->isIMEdit( line, nextCol )) ) 00521 00522 // input method selection 00523 || ( m_view && (isIMSel != m_view->isIMSelection( line, nextCol )) ) 00524 ) 00525 { 00526 renderNow = true; 00527 } 00528 00529 if (renderNow) 00530 { 00531 if (!isPrinterFriendly()) 00532 { 00533 bool paintBackground = true; 00534 uint width = xPosAfter - oldXPos; 00535 QColor fillColor; 00536 00537 if (isIMSel && !isTab) 00538 { 00539 // input method selection 00540 fillColor = m_view->colorGroup().color(QColorGroup::Foreground); 00541 } 00542 else if (isIMEdit && !isTab) 00543 { 00544 // XIM support 00545 // input method edit area 00546 const QColorGroup& cg = m_view->colorGroup(); 00547 int h1, s1, v1, h2, s2, v2; 00548 cg.color( QColorGroup::Base ).hsv( &h1, &s1, &v1 ); 00549 cg.color( QColorGroup::Background ).hsv( &h2, &s2, &v2 ); 00550 fillColor.setHsv( h1, s1, ( v1 + v2 ) / 2 ); 00551 } 00552 else if (!selectionPainted && (isSel || currentHL.itemSet(KateAttribute::BGColor))) 00553 { 00554 if (isSel) 00555 { 00556 fillColor = config()->selectionColor(); 00557 00558 // If this is the last block of text, fill up to the end of the line if the 00559 // selection stretches that far 00560 if ((curCol >= len - 1) && m_view->lineEndSelected (line, endcol)) 00561 width = xEnd - oldXPos; 00562 } 00563 else 00564 { 00565 fillColor = currentHL.bgColor(); 00566 } 00567 } 00568 else 00569 { 00570 paintBackground = false; 00571 } 00572 00573 if (paintBackground) 00574 paint.fillRect(oldXPos - xStart, 0, width, fs->fontHeight, fillColor); 00575 00576 if (isIMSel && paintBackground && !isTab) 00577 { 00578 paint.save(); 00579 paint.setPen( m_view->colorGroup().color( QColorGroup::BrightText ) ); 00580 } 00581 00582 // Draw indentation markers. 00583 if (showIndentLines() && curCol < lastIndentColumn) 00584 { 00585 // Draw multiple guides when tab width greater than indent width. 00586 const int charWidth = isTab ? m_tabWidth - curPos % m_tabWidth : 1; 00587 00588 // Do not draw indent guides on the first line. 00589 int i = 0; 00590 if (curPos == 0 || curPos % m_indentWidth > 0) 00591 i = m_indentWidth - curPos % m_indentWidth; 00592 00593 for (; i < charWidth; i += m_indentWidth) 00594 { 00595 // In most cases this is done one or zero times. 00596 paintIndentMarker(paint, xPos - xStart + i * spaceWidth, line); 00597 00598 // Draw highlighted line. 00599 if (curPos+i == minIndent) 00600 { 00601 paintIndentMarker(paint, xPos - xStart + 1 + i * spaceWidth, line+1); 00602 } 00603 } 00604 } 00605 } 00606 00607 // or we will see no text ;) 00608 int y = fs->fontAscent; 00609 00610 // make sure we redraw the right character groups on attrib/selection changes 00611 // Special case... de-special case some of it 00612 if (isTab || (curCol >= trailingWhitespaceColumn)) 00613 { 00614 // Draw spaces too, because it might be eg. underlined 00615 static QString spaces; 00616 if (int(spaces.length()) != m_tabWidth) 00617 spaces.fill(' ', m_tabWidth); 00618 00619 paint.drawText(oldXPos-xStart, y, isTab ? spaces : QString(" ")); 00620 00621 if (showTabs()) 00622 { 00623 // trailing spaces and tabs may also have to be different options. 00624 // if( curCol >= lastIndentColumn ) 00625 paintWhitespaceMarker(paint, xPos - xStart, y); 00626 } 00627 00628 // variable advancement 00629 blockStartCol = nextCol; 00630 oldXPos = xPosAfter; 00631 } 00632 else 00633 { 00634 // Here's where the money is... 00635 paint.drawText(oldXPos-xStart, y, textLine->string(), blockStartCol, nextCol-blockStartCol); 00636 00637 // Draw preedit's underline 00638 if (isIMEdit) { 00639 QRect r( oldXPos - xStart, 0, xPosAfter - oldXPos, fs->fontHeight ); 00640 paint.drawLine( r.bottomLeft(), r.bottomRight() ); 00641 } 00642 00643 // Put pen color back 00644 if (isIMSel) paint.restore(); 00645 00646 // We're done drawing? 00647 if ((int)xPos > xEnd) 00648 break; 00649 00650 // variable advancement 00651 blockStartCol = nextCol; 00652 oldXPos = xPosAfter; 00653 //oldS = s+1; 00654 } 00655 } // renderNow 00656 00657 // determine cursor X position 00658 if (showCursor && (cursor->col() == int(curCol))) 00659 { 00660 cursorVisible = true; 00661 cursorXPos = xPos; 00662 cursorMaxWidth = xPosAfter - xPos; 00663 cursorColor = &curAt->textColor(); 00664 } 00665 } // xPosAfter >= xStart 00666 else 00667 { 00668 // variable advancement 00669 blockStartCol = nextCol; 00670 oldXPos = xPosAfter; 00671 } 00672 00673 // increase xPos 00674 xPos = xPosAfter; 00675 00676 // increase attribs pos 00677 textAttributes++; 00678 00679 // to only switch font/color if needed 00680 oldAt = curAt; 00681 oldColor = curColor; 00682 00683 // col move 00684 curCol++; 00685 nextCol++; 00686 currentPos.setCol(currentPos.col() + 1); 00687 00688 // Update the current indentation pos. 00689 if (isTab) 00690 { 00691 curPos += m_tabWidth - (curPos % m_tabWidth); 00692 } 00693 else 00694 { 00695 curPos++; 00696 } 00697 } 00698 00699 // If this line has a partial selection that's the start of a multi-line selection, 00700 // we have to fill areas on the right side of the text with the selection color. 00701 if (showSelections() && hasSel && !selectionPainted && xStart >= (int)xPos && m_view->lineEndSelected(line, -1)) 00702 { 00703 paint.fillRect(0, 0, xEnd-xStart, fs->fontHeight, config()->selectionColor()); 00704 } 00705 00706 // Determine cursor position (if it is not within the range being drawn) 00707 if (showCursor && (cursor->col() >= int(curCol))) 00708 { 00709 cursorVisible = true; 00710 cursorXPos = xPos + (cursor->col() - int(curCol)) * fs->myFontMetrics.width(QChar(' ')); 00711 cursorMaxWidth = xPosAfter - xPos; 00712 cursorColor = &oldAt->textColor(); 00713 } 00714 } 00715 00716 // Paint cursor 00717 if (cursorVisible) 00718 { 00719 uint cursorWidth = (caretStyle() == Replace && (cursorMaxWidth > 2)) ? cursorMaxWidth : 2; 00720 paint.fillRect(cursorXPos-xStart, 0, cursorWidth, fs->fontHeight, *cursorColor); 00721 } 00722 00723 // show word wrap marker if desirable 00724 if (!isPrinterFriendly() && config()->wordWrapMarker() && fs->fixedPitch()) 00725 { 00726 paint.setPen( config()->wordWrapMarkerColor() ); 00727 int _x = m_doc->config()->wordWrapAt() * fs->myFontMetrics.width('x') - xStart; 00728 paint.drawLine( _x,0,_x,fs->fontHeight ); 00729 } 00730 00731 // cleanup ;) 00732 delete bracketStartRange; 00733 delete bracketEndRange; 00734 } 00735 00736 uint KateRenderer::textWidth(const KateTextLine::Ptr &textLine, int cursorCol) 00737 { 00738 if (!textLine) 00739 return 0; 00740 00741 const int len = textLine->length(); 00742 00743 if (cursorCol < 0) 00744 cursorCol = len; 00745 00746 KateFontStruct *fs = config()->fontStruct(); 00747 00748 const QChar *unicode = textLine->text(); 00749 const QString &textString = textLine->string(); 00750 00751 int x = 0; 00752 int width; 00753 for (int z = 0; z < cursorCol; z++) { 00754 KateAttribute* a = attribute(textLine->attribute(z)); 00755 00756 if (z < len) { 00757 width = a->width(*fs, textString, z, m_tabWidth); 00758 } else { 00759 // DF: commented out. It happens all the time. 00760 //Q_ASSERT(!m_doc->wrapCursor()); 00761 width = a->width(*fs, QChar(' '), m_tabWidth); 00762 } 00763 00764 x += width; 00765 00766 if (z < len && unicode[z] == QChar('\t')) 00767 x -= x % width; 00768 } 00769 00770 return x; 00771 } 00772 00773 uint KateRenderer::textWidth(const KateTextLine::Ptr &textLine, uint startcol, uint maxwidth, bool *needWrap, int *endX) 00774 { 00775 KateFontStruct *fs = config()->fontStruct(); 00776 uint x = 0; 00777 uint endcol = startcol; 00778 int endX2 = 0; 00779 int lastWhiteSpace = -1; 00780 int lastWhiteSpaceX = -1; 00781 00782 // used to not wrap a solitary word off the first line, ie. the 00783 // first line should not wrap until some characters have been displayed if possible 00784 bool foundNonWhitespace = startcol != 0; 00785 bool foundWhitespaceAfterNonWhitespace = startcol != 0; 00786 00787 *needWrap = false; 00788 00789 const uint len = textLine->length(); 00790 const QChar *unicode = textLine->text(); 00791 const QString &textString = textLine->string(); 00792 00793 uint z = startcol; 00794 for (; z < len; z++) 00795 { 00796 KateAttribute* a = attribute(textLine->attribute(z)); 00797 int width = a->width(*fs, textString, z, m_tabWidth); 00798 Q_ASSERT(width); 00799 x += width; 00800 00801 // How should tabs be treated when they word-wrap on a print-out? 00802 // if startcol != 0, this messes up (then again, word wrapping messes up anyway) 00803 if (unicode[z] == QChar('\t')) 00804 x -= x % width; 00805 00806 if (unicode[z].isSpace()) 00807 { 00808 lastWhiteSpace = z+1; 00809 lastWhiteSpaceX = x; 00810 00811 if (foundNonWhitespace) 00812 foundWhitespaceAfterNonWhitespace = true; 00813 } 00814 else 00815 { 00816 if (!foundWhitespaceAfterNonWhitespace) { 00817 foundNonWhitespace = true; 00818 00819 lastWhiteSpace = z+1; 00820 lastWhiteSpaceX = x; 00821 } 00822 } 00823 00824 if (x <= maxwidth) 00825 { 00826 if (lastWhiteSpace > -1) 00827 { 00828 endcol = lastWhiteSpace; 00829 endX2 = lastWhiteSpaceX; 00830 } 00831 else 00832 { 00833 endcol = z+1; 00834 endX2 = x; 00835 } 00836 } 00837 else if (z == startcol) 00838 { 00839 // require a minimum of 1 character advancement per call, even if it means drawing gets cut off 00840 // (geez gideon causes troubles with starting the views very small) 00841 endcol = z+1; 00842 endX2 = x; 00843 } 00844 00845 if (x >= maxwidth) 00846 { 00847 *needWrap = true; 00848 break; 00849 } 00850 } 00851 00852 if (*needWrap) 00853 { 00854 if (endX) 00855 *endX = endX2; 00856 00857 return endcol; 00858 } 00859 else 00860 { 00861 if (endX) 00862 *endX = x; 00863 00864 return z+1; 00865 } 00866 } 00867 00868 uint KateRenderer::textWidth(const KateTextCursor &cursor) 00869 { 00870 int line = kMin(kMax(0, cursor.line()), (int)m_doc->numLines() - 1); 00871 int col = kMax(0, cursor.col()); 00872 00873 return textWidth(m_doc->kateTextLine(line), col); 00874 } 00875 00876 uint KateRenderer::textWidth( KateTextCursor &cursor, int xPos, uint startCol) 00877 { 00878 bool wrapCursor = m_view->wrapCursor(); 00879 int x, oldX; 00880 00881 KateFontStruct *fs = config()->fontStruct(); 00882 00883 if (cursor.line() < 0) cursor.setLine(0); 00884 if (cursor.line() > (int)m_doc->lastLine()) cursor.setLine(m_doc->lastLine()); 00885 KateTextLine::Ptr textLine = m_doc->kateTextLine(cursor.line()); 00886 00887 if (!textLine) return 0; 00888 00889 const uint len = textLine->length(); 00890 const QChar *unicode = textLine->text(); 00891 const QString &textString = textLine->string(); 00892 00893 x = oldX = 0; 00894 uint z = startCol; 00895 while (x < xPos && (!wrapCursor || z < len)) { 00896 oldX = x; 00897 00898 KateAttribute* a = attribute(textLine->attribute(z)); 00899 00900 int width = 0; 00901 00902 if (z < len) 00903 width = a->width(*fs, textString, z, m_tabWidth); 00904 else 00905 width = a->width(*fs, QChar(' '), m_tabWidth); 00906 00907 x += width; 00908 00909 if (z < len && unicode[z] == QChar('\t')) 00910 x -= x % width; 00911 00912 z++; 00913 } 00914 if (xPos - oldX < x - xPos && z > 0) { 00915 z--; 00916 x = oldX; 00917 } 00918 cursor.setCol(z); 00919 return x; 00920 } 00921 00922 const QFont *KateRenderer::currentFont() 00923 { 00924 return config()->font(); 00925 } 00926 00927 const QFontMetrics* KateRenderer::currentFontMetrics() 00928 { 00929 return config()->fontMetrics(); 00930 } 00931 00932 uint KateRenderer::textPos(uint line, int xPos, uint startCol, bool nearest) 00933 { 00934 return textPos(m_doc->kateTextLine(line), xPos, startCol, nearest); 00935 } 00936 00937 uint KateRenderer::textPos(const KateTextLine::Ptr &textLine, int xPos, uint startCol, bool nearest) 00938 { 00939 Q_ASSERT(textLine); 00940 if (!textLine) 00941 return 0; 00942 00943 KateFontStruct *fs = config()->fontStruct(); 00944 00945 int x, oldX; 00946 x = oldX = 0; 00947 00948 uint z = startCol; 00949 const uint len = textLine->length(); 00950 const QString &textString = textLine->string(); 00951 00952 while ( (x < xPos) && (z < len)) { 00953 oldX = x; 00954 00955 KateAttribute* a = attribute(textLine->attribute(z)); 00956 x += a->width(*fs, textString, z, m_tabWidth); 00957 00958 z++; 00959 } 00960 if ( ( (! nearest) || xPos - oldX < x - xPos ) && z > 0 ) { 00961 z--; 00962 // newXPos = oldX; 00963 }// else newXPos = x; 00964 return z; 00965 } 00966 00967 uint KateRenderer::fontHeight() 00968 { 00969 return config()->fontStruct ()->fontHeight; 00970 } 00971 00972 uint KateRenderer::documentHeight() 00973 { 00974 return m_doc->numLines() * fontHeight(); 00975 } 00976 00977 bool KateRenderer::getSelectionBounds(uint line, uint lineLength, uint &start, uint &end) 00978 { 00979 bool hasSel = false; 00980 00981 if (m_view->hasSelection() && !m_view->blockSelectionMode()) 00982 { 00983 if (m_view->lineIsSelection(line)) 00984 { 00985 start = m_view->selStartCol(); 00986 end = m_view->selEndCol(); 00987 hasSel = true; 00988 } 00989 else if ((int)line == m_view->selStartLine()) 00990 { 00991 start = m_view->selStartCol(); 00992 end = lineLength; 00993 hasSel = true; 00994 } 00995 else if ((int)line == m_view->selEndLine()) 00996 { 00997 start = 0; 00998 end = m_view->selEndCol(); 00999 hasSel = true; 01000 } 01001 } 01002 else if (m_view->lineHasSelected(line)) 01003 { 01004 start = m_view->selStartCol(); 01005 end = m_view->selEndCol(); 01006 hasSel = true; 01007 } 01008 01009 if (start > end) { 01010 int temp = end; 01011 end = start; 01012 start = temp; 01013 } 01014 01015 return hasSel; 01016 } 01017 01018 void KateRenderer::updateConfig () 01019 { 01020 // update the attibute list pointer 01021 updateAttributes (); 01022 01023 if (m_view) 01024 m_view->updateRendererConfig(); 01025 } 01026 01027 uint KateRenderer::spaceWidth() 01028 { 01029 return attribute(0)->width(*config()->fontStruct(), QChar(' '), m_tabWidth); 01030 } 01031 01032 // kate: space-indent on; indent-width 2; replace-tabs on;