/* This file is part of the KDE libraries Copyright (C) 2003 Hamish Rodda Copyright (C) 2001 Christoph Cullmann Copyright (C) 2001 Joseph Wenninger Copyright (C) 1999 Jochen Wilhelmy This library is free software; you can redistribute it and/or modify it under the terms of the GNU Library General Public License version 2 as published by the Free Software Foundation. This library is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU Library General Public License for more details. You should have received a copy of the GNU Library General Public License along with this library; see the file COPYING.LIB. If not, write to the Free Software Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA. */ #include "katerenderer.h" #include "katelinerange.h" #include "katedocument.h" #include "katearbitraryhighlight.h" #include "kateconfig.h" #include "katehighlight.h" #include "katefactory.h" #include "kateview.h" #include #include #include KateRenderer::KateRenderer(KateDocument* doc, KateView *view) : m_doc(doc), m_view (view), m_caretStyle(KateRenderer::Insert) , m_drawCaret(true) , m_showSelections(true) , m_showTabs(true) , m_printerFriendly(false) { KateFactory::self()->registerRenderer ( this ); m_config = new KateRendererConfig (this); m_tabWidth = m_doc->config()->tabWidth(); m_indentWidth = m_tabWidth; if (m_doc->config()->configFlags() & KateDocumentConfig::cfSpaceIndent) { m_indentWidth = m_doc->config()->indentationWidth(); } updateAttributes (); } KateRenderer::~KateRenderer() { delete m_config; KateFactory::self()->deregisterRenderer ( this ); } void KateRenderer::updateAttributes () { m_schema = config()->schema (); m_attributes = m_doc->highlight()->attributes (m_schema); } KateAttribute* KateRenderer::attribute(uint pos) { if (pos < m_attributes->size()) return &m_attributes->at(pos); return &m_attributes->at(0); } void KateRenderer::setDrawCaret(bool drawCaret) { m_drawCaret = drawCaret; } void KateRenderer::setCaretStyle(KateRenderer::caretStyles style) { m_caretStyle = style; } void KateRenderer::setShowTabs(bool showTabs) { m_showTabs = showTabs; } void KateRenderer::setTabWidth(int tabWidth) { m_tabWidth = tabWidth; } bool KateRenderer::showIndentLines() const { return m_config->showIndentationLines(); } void KateRenderer::setShowIndentLines(bool showIndentLines) { m_config->setShowIndentationLines(showIndentLines); } void KateRenderer::setIndentWidth(int indentWidth) { m_indentWidth = m_tabWidth; if (m_doc->config()->configFlags() & KateDocumentConfig::cfSpaceIndent) { m_indentWidth = indentWidth; } } void KateRenderer::setShowSelections(bool showSelections) { m_showSelections = showSelections; } void KateRenderer::increaseFontSizes() { TQFont f ( *config()->font () ); f.setPointSize (f.pointSize ()+1); config()->setFont (f); } void KateRenderer::decreaseFontSizes() { TQFont f ( *config()->font () ); if ((f.pointSize ()-1) > 0) f.setPointSize (f.pointSize ()-1); config()->setFont (f); } bool KateRenderer::isPrinterFriendly() const { return m_printerFriendly; } void KateRenderer::setPrinterFriendly(bool printerFriendly) { m_printerFriendly = printerFriendly; setShowTabs(false); setShowSelections(false); setDrawCaret(false); } bool KateRenderer::paintTextLineBackground(TQPainter& paint, int line, bool isCurrentLine, int xStart, int xEnd) { if (isPrinterFriendly()) return false; // font data KateFontStruct *fs = config()->fontStruct(); // Normal background color TQColor backgroundColor( config()->backgroundColor() ); bool selectionPainted = false; if (showSelections() && m_view->lineSelected(line)) { backgroundColor = config()->selectionColor(); selectionPainted = true; } else { // paint the current line background if we're on the current line if (isCurrentLine) backgroundColor = config()->highlightedLineColor(); // Check for mark background int markRed = 0, markGreen = 0, markBlue = 0, markCount = 0; // Retrieve marks for this line uint mrk = m_doc->mark( line ); if (mrk) { for (uint bit = 0; bit < 32; bit++) { KTextEditor::MarkInterface::MarkTypes markType = (KTextEditor::MarkInterface::MarkTypes)(1<lineMarkerColor(markType); if (markColor.isValid()) { markCount++; markRed += markColor.red(); markGreen += markColor.green(); markBlue += markColor.blue(); } } } // for } // Marks if (markCount) { markRed /= markCount; markGreen /= markCount; markBlue /= markCount; backgroundColor.setRgb( int((backgroundColor.red() * 0.9) + (markRed * 0.1)), int((backgroundColor.green() * 0.9) + (markGreen * 0.1)), int((backgroundColor.blue() * 0.9) + (markBlue * 0.1)) ); } } // background preprocessing // Draw line background paint.fillRect(0, 0, xEnd - xStart, fs->fontHeight, backgroundColor); return selectionPainted; } void KateRenderer::paintWhitespaceMarker(TQPainter &paint, uint x, uint y) { TQPen penBackup( paint.pen() ); paint.setPen( config()->tabMarkerColor() ); paint.drawPoint(x, y); paint.drawPoint(x + 1, y); paint.drawPoint(x, y - 1); paint.setPen( penBackup ); } void KateRenderer::paintIndentMarker(TQPainter &paint, uint x, uint row) { TQPen penBackup( paint.pen() ); paint.setPen( config()->tabMarkerColor() ); const int top = paint.window().top(); const int bottom = paint.window().bottom(); const int h = bottom - top + 1; // Dot padding. int pad = 0; if(row & 1 && h & 1) pad = 1; for(int i = top; i <= bottom; i++) { if((i + pad) & 1) { paint.drawPoint(x + 2, i); } } paint.setPen( penBackup ); } void KateRenderer::paintTextLine(TQPainter& paint, const KateLineRange* range, int xStart, int xEnd, const KateTextCursor* cursor, const KateBracketRange* bracketmark) { int line = range->line; // textline KateTextLine::Ptr textLine = m_doc->kateTextLine(line); if (!textLine) return; bool showCursor = drawCaret() && cursor && range->includesCursor(*cursor); KateSuperRangeList& superRanges = m_doc->arbitraryHL()->rangesIncluding(range->line, 0); int minIndent = 0; // A bit too verbose for my tastes // Re-write a bracketmark class? put into its own function? add more helper constructors to the range stuff? // Also, need a light-weight arbitraryhighlightrange class for static stuff KateArbitraryHighlightRange* bracketStartRange (0L); KateArbitraryHighlightRange* bracketEndRange (0L); if (bracketmark && bracketmark->isValid()) { if (range->includesCursor(bracketmark->start())) { KateTextCursor startend = bracketmark->start(); startend.setCol(startend.col()+1); bracketStartRange = new KateArbitraryHighlightRange(m_doc, bracketmark->start(), startend); bracketStartRange->setBGColor(config()->highlightedBracketColor()); bracketStartRange->setBold(true); superRanges.append(bracketStartRange); } if (range->includesCursor(bracketmark->end())) { KateTextCursor endend = bracketmark->end(); endend.setCol(endend.col()+1); bracketEndRange = new KateArbitraryHighlightRange(m_doc, bracketmark->end(), endend); bracketEndRange->setBGColor(config()->highlightedBracketColor()); bracketEndRange->setBold(true); superRanges.append(bracketEndRange); } Q_ASSERT(bracketmark->start().line() <= bracketmark->end().line()); if (bracketmark->start().line() < line && bracketmark->end().line() >= line) { minIndent = bracketmark->getMinIndent(); } } // length, chars + raw attribs uint len = textLine->length(); uint oldLen = len; // should the cursor be painted (if it is in the current xstart - xend range) bool cursorVisible = false; int cursorMaxWidth = 0; // font data KateFontStruct * fs = config()->fontStruct(); // Paint selection background as the whole line is selected // selection startcol/endcol calc bool hasSel = false; uint startSel = 0; uint endSel = 0; // was the selection background already completely painted ? bool selectionPainted = false; bool isCurrentLine = (cursor && range->includesCursor(*cursor)); selectionPainted = paintTextLineBackground(paint, line, isCurrentLine, xStart, xEnd); if (selectionPainted) { hasSel = true; startSel = 0; endSel = len + 1; } int startcol = range->startCol; if (startcol > (int)len) startcol = len; if (startcol < 0) startcol = 0; int endcol = range->wrap ? range->endCol : -1; if (endcol < 0) len = len - startcol; else len = endcol - startcol; // text attribs font/style data KateAttribute* attr = m_doc->highlight()->attributes(m_schema)->data(); const TQColor *cursorColor = &attr[0].textColor(); // Start arbitrary highlighting KateTextCursor currentPos(line, startcol); superRanges.firstBoundary(¤tPos); if (showSelections() && !selectionPainted) hasSel = getSelectionBounds(line, oldLen, startSel, endSel); // Draws the dashed underline at the start of a folded block of text. if (range->startsInvisibleBlock) { paint.setPen(TQPen(config()->wordWrapMarkerColor(), 1, Qt::DashLine)); paint.drawLine(0, fs->fontHeight - 1, xEnd - xStart, fs->fontHeight - 1); } // draw word-wrap-honor-indent filling if (range->xOffset() && range->xOffset() > xStart) { paint.fillRect(0, 0, range->xOffset() - xStart, fs->fontHeight, TQBrush(config()->wordWrapMarkerColor(), TQBrush::DiagCrossPattern)); } // painting loop uint xPos = range->xOffset(); int cursorXPos = 0; // Optimisation to quickly draw an empty line of text if (len < 1) { if (showCursor && (cursor->col() >= int(startcol))) { cursorVisible = true; cursorXPos = xPos + cursor->col() * fs->myFontMetrics.width(TQChar(' ')); } } else { bool isIMSel = false; bool isIMEdit = false; bool isSel = false; KateAttribute customHL; const TQColor *curColor = 0; const TQColor *oldColor = 0; KateAttribute* oldAt = &attr[0]; uint oldXPos = xPos; uint xPosAfter = xPos; KateAttribute currentHL; uint blockStartCol = startcol; uint curCol = startcol; uint nextCol = curCol + 1; // text + attrib data from line const uchar *textAttributes = textLine->attributes (); bool noAttribs = !textAttributes; // adjust to startcol ;) textAttributes = textAttributes + startcol; uint atLen = m_doc->highlight()->attributes(m_schema)->size(); // Determine if we have trailing whitespace and store the column // if lastChar == -1, set to 0, if lastChar exists, increase by one uint trailingWhitespaceColumn = textLine->lastChar() + 1; const uint lastIndentColumn = textLine->firstChar(); // Could be precomputed. const uint spaceWidth = fs->width (TQChar(' '), false, false, m_tabWidth); // Get current x position. int curPos = textLine->cursorX(curCol, m_tabWidth); while (curCol - startcol < len) { // make sure curPos is updated correctly. // ### if uncommented, causes an O(n^2) behaviour //Q_ASSERT(curPos == textLine->cursorX(curCol, m_tabWidth)); TQChar curChar = textLine->string()[curCol]; // Decide if this character is a tab - we treat the spacing differently // TODO: move tab width calculation elsewhere? bool isTab = curChar == TQChar('\t'); // Determine current syntax highlighting attribute // A bit legacy but doesn't need to change KateAttribute* curAt = (noAttribs || ((*textAttributes) >= atLen)) ? &attr[0] : &attr[*textAttributes]; // X position calculation. Incorrect for fonts with non-zero leftBearing() and rightBearing() results. // TODO: make internal charWidth() function, use TQFontMetrics::charWidth(). xPosAfter += curAt->width(*fs, curChar, m_tabWidth); // Tab special treatment, move to charWidth(). if (isTab) xPosAfter -= (xPosAfter % curAt->width(*fs, curChar, m_tabWidth)); // Only draw after the starting X value // Haha, this was always wrong, due to the use of individual char width calculations...?? :( if ((int)xPosAfter >= xStart) { // Determine if we're in a selection and should be drawing it isSel = (showSelections() && hasSel && (curCol >= startSel) && (curCol < endSel)); // input method edit area isIMEdit = m_view && m_view->isIMEdit( line, curCol ); // input method selection isIMSel = m_view && m_view->isIMSelection( line, curCol ); // Determine current color, taking into account selection curColor = isSel ? &(curAt->selectedTextColor()) : &(curAt->textColor()); // Incorporate in arbitrary highlighting if (curAt != oldAt || curColor != oldColor || (superRanges.count() && superRanges.currentBoundary() && *(superRanges.currentBoundary()) == currentPos)) { if (superRanges.count() && superRanges.currentBoundary() && *(superRanges.currentBoundary()) == currentPos) customHL = KateArbitraryHighlightRange::merge(superRanges.rangesIncluding(currentPos)); KateAttribute hl = customHL; hl += *curAt; // use default highlighting color if we haven't defined one above. if (!hl.itemSet(KateAttribute::TextColor)) hl.setTextColor(*curColor); if (!isSel) paint.setPen(hl.textColor()); else paint.setPen(hl.selectedTextColor()); paint.setFont(hl.font(*currentFont())); if (superRanges.currentBoundary() && *(superRanges.currentBoundary()) == currentPos) superRanges.nextBoundary(); currentHL = hl; } // Determine whether we can delay painting to draw a block of similarly formatted // characters or not // Reasons for NOT delaying the drawing until the next character // You have to detect the change one character in advance. // TODO: KateAttribute::canBatchRender() bool renderNow = false; if ((isTab) // formatting has changed OR || (superRanges.count() && superRanges.currentBoundary() && *(superRanges.currentBoundary()) == KateTextCursor(line, nextCol)) // it is the end of the line OR || (curCol - startcol >= len - 1) // the rest of the line is trailing whitespace OR || (curCol + 1 >= trailingWhitespaceColumn) // indentation lines OR || (showIndentLines() && curCol < lastIndentColumn) // the x position is past the end OR || ((int)xPos > xEnd) // it is a different attribute OR || (!noAttribs && curAt != &attr[*(textAttributes+1)]) // the selection boundary was crossed OR || (isSel != (hasSel && (nextCol >= startSel) && (nextCol < endSel))) // the next char is a tab (removed the "and this isn't" because that's dealt with above) // i.e. we have to draw the current text so the tab can be rendered as above. || (textLine->string()[nextCol] == TQChar('\t')) // input method edit area || ( m_view && (isIMEdit != m_view->isIMEdit( line, nextCol )) ) // input method selection || ( m_view && (isIMSel != m_view->isIMSelection( line, nextCol )) ) ) { renderNow = true; } if (renderNow) { if (!isPrinterFriendly()) { bool paintBackground = true; uint width = xPosAfter - oldXPos; TQColor fillColor; if (isIMSel && !isTab) { // input method selection fillColor = m_view->colorGroup().color(TQColorGroup::Foreground); } else if (isIMEdit && !isTab) { // XIM support // input method edit area const TQColorGroup& cg = m_view->colorGroup(); int h1, s1, v1, h2, s2, v2; TQColor(cg.color( TQColorGroup::Base )).hsv( &h1, &s1, &v1 ); TQColor(cg.color( TQColorGroup::Background )).hsv( &h2, &s2, &v2 ); fillColor.setHsv( h1, s1, ( v1 + v2 ) / 2 ); } else if (!selectionPainted && (isSel || currentHL.itemSet(KateAttribute::BGColor))) { if (isSel) { fillColor = config()->selectionColor(); // If this is the last block of text, fill up to the end of the line if the // selection stretches that far if ((curCol >= len - 1) && m_view->lineEndSelected (line, endcol)) width = xEnd - oldXPos; } else { fillColor = currentHL.bgColor(); } } else { paintBackground = false; } if (paintBackground) paint.fillRect(oldXPos - xStart, 0, width, fs->fontHeight, fillColor); if (isIMSel && paintBackground && !isTab) { paint.save(); paint.setPen( m_view->colorGroup().color( TQColorGroup::BrightText ) ); } // Draw indentation markers. if (showIndentLines() && curCol < lastIndentColumn) { // Draw multiple guides when tab width greater than indent width. const int charWidth = isTab ? m_tabWidth - curPos % m_tabWidth : 1; // Do not draw indent guides on the first line. int i = 0; if (curPos == 0 || curPos % m_indentWidth > 0) i = m_indentWidth - curPos % m_indentWidth; for (; i < charWidth; i += m_indentWidth) { // In most cases this is done one or zero times. paintIndentMarker(paint, xPos - xStart + i * spaceWidth, line); // Draw highlighted line. if (curPos+i == minIndent) { paintIndentMarker(paint, xPos - xStart + 1 + i * spaceWidth, line+1); } } } } // or we will see no text ;) int y = fs->fontAscent; // make sure we redraw the right character groups on attrib/selection changes // Special case... de-special case some of it if (isTab || (curCol >= trailingWhitespaceColumn)) { // Draw spaces too, because it might be eg. underlined static TQString spaces; if (int(spaces.length()) != m_tabWidth) spaces.fill(' ', m_tabWidth); paint.drawText(oldXPos-xStart, y, isTab ? spaces : TQString(" ")); if (showTabs()) { // trailing spaces and tabs may also have to be different options. // if( curCol >= lastIndentColumn ) paintWhitespaceMarker(paint, xPos - xStart, y); } // variable advancement blockStartCol = nextCol; oldXPos = xPosAfter; } else { // Here's where the money is... paint.drawText(oldXPos-xStart, y, textLine->string(), blockStartCol, nextCol-blockStartCol); // Draw preedit's underline if (isIMEdit) { TQRect r( oldXPos - xStart, 0, xPosAfter - oldXPos, fs->fontHeight ); paint.drawLine( r.bottomLeft(), r.bottomRight() ); } // Put pen color back if (isIMSel) paint.restore(); // We're done drawing? if ((int)xPos > xEnd) break; // variable advancement blockStartCol = nextCol; oldXPos = xPosAfter; //oldS = s+1; } } // renderNow // determine cursor X position if (showCursor && (cursor->col() == int(curCol))) { cursorVisible = true; cursorXPos = xPos; cursorMaxWidth = xPosAfter - xPos; cursorColor = &curAt->textColor(); } } // xPosAfter >= xStart else { // variable advancement blockStartCol = nextCol; oldXPos = xPosAfter; } // increase xPos xPos = xPosAfter; // increase attribs pos textAttributes++; // to only switch font/color if needed oldAt = curAt; oldColor = curColor; // col move curCol++; nextCol++; currentPos.setCol(currentPos.col() + 1); // Update the current indentation pos. if (isTab) { curPos += m_tabWidth - (curPos % m_tabWidth); } else { curPos++; } } // If this line has a partial selection that's the start of a multi-line selection, // we have to fill areas on the right side of the text with the selection color. if (showSelections() && hasSel && !selectionPainted && xStart >= (int)xPos && m_view->lineEndSelected(line, -1)) { paint.fillRect(0, 0, xEnd-xStart, fs->fontHeight, config()->selectionColor()); } // Determine cursor position (if it is not within the range being drawn) if (showCursor && (cursor->col() >= int(curCol))) { cursorVisible = true; cursorXPos = xPos + (cursor->col() - int(curCol)) * fs->myFontMetrics.width(TQChar(' ')); cursorMaxWidth = xPosAfter - xPos; cursorColor = &oldAt->textColor(); } } // Paint cursor if (cursorVisible) { uint cursorWidth = (caretStyle() == Replace && (cursorMaxWidth > 2)) ? cursorMaxWidth : 2; paint.fillRect(cursorXPos-xStart, 0, cursorWidth, fs->fontHeight, *cursorColor); } // show word wrap marker if desirable if (!isPrinterFriendly() && config()->wordWrapMarker() && fs->fixedPitch()) { paint.setPen( config()->wordWrapMarkerColor() ); int _x = m_doc->config()->wordWrapAt() * fs->myFontMetrics.width('x') - xStart; paint.drawLine( _x,0,_x,fs->fontHeight ); } // cleanup ;) delete bracketStartRange; delete bracketEndRange; } uint KateRenderer::textWidth(const KateTextLine::Ptr &textLine, int cursorCol) { if (!textLine) return 0; const int len = textLine->length(); if (cursorCol < 0) cursorCol = len; KateFontStruct *fs = config()->fontStruct(); const TQChar *unicode = textLine->text(); const TQString &textString = textLine->string(); int x = 0; int width; for (int z = 0; z < cursorCol; z++) { KateAttribute* a = attribute(textLine->attribute(z)); if (z < len) { width = a->width(*fs, textString, z, m_tabWidth); } else { // DF: commented out. It happens all the time. //Q_ASSERT(!m_doc->wrapCursor()); width = a->width(*fs, TQChar(' '), m_tabWidth); } x += width; if (z < len && unicode[z] == TQChar('\t')) x -= x % width; } return x; } uint KateRenderer::textWidth(const KateTextLine::Ptr &textLine, uint startcol, uint maxwidth, bool *needWrap, int *endX) { KateFontStruct *fs = config()->fontStruct(); uint x = 0; uint endcol = startcol; int endX2 = 0; int lastWhiteSpace = -1; int lastWhiteSpaceX = -1; // used to not wrap a solitary word off the first line, ie. the // first line should not wrap until some characters have been displayed if possible bool foundNonWhitespace = startcol != 0; bool foundWhitespaceAfterNonWhitespace = startcol != 0; *needWrap = false; const uint len = textLine->length(); const TQChar *unicode = textLine->text(); const TQString &textString = textLine->string(); uint z = startcol; for (; z < len; z++) { KateAttribute* a = attribute(textLine->attribute(z)); int width = a->width(*fs, textString, z, m_tabWidth); Q_ASSERT(width); x += width; // How should tabs be treated when they word-wrap on a print-out? // if startcol != 0, this messes up (then again, word wrapping messes up anyway) if (unicode[z] == TQChar('\t')) x -= x % width; if (unicode[z].isSpace()) { lastWhiteSpace = z+1; lastWhiteSpaceX = x; if (foundNonWhitespace) foundWhitespaceAfterNonWhitespace = true; } else { if (!foundWhitespaceAfterNonWhitespace) { foundNonWhitespace = true; lastWhiteSpace = z+1; lastWhiteSpaceX = x; } } if (x <= maxwidth) { if (lastWhiteSpace > -1) { endcol = lastWhiteSpace; endX2 = lastWhiteSpaceX; } else { endcol = z+1; endX2 = x; } } else if (z == startcol) { // require a minimum of 1 character advancement per call, even if it means drawing gets cut off // (geez gideon causes troubles with starting the views very small) endcol = z+1; endX2 = x; } if (x >= maxwidth) { *needWrap = true; break; } } if (*needWrap) { if (endX) *endX = endX2; return endcol; } else { if (endX) *endX = x; return z+1; } } uint KateRenderer::textWidth(const KateTextCursor &cursor) { int line = kMin(kMax(0, cursor.line()), (int)m_doc->numLines() - 1); int col = kMax(0, cursor.col()); return textWidth(m_doc->kateTextLine(line), col); } uint KateRenderer::textWidth( KateTextCursor &cursor, int xPos, uint startCol) { bool wrapCursor = m_view->wrapCursor(); int x, oldX; KateFontStruct *fs = config()->fontStruct(); if (cursor.line() < 0) cursor.setLine(0); if (cursor.line() > (int)m_doc->lastLine()) cursor.setLine(m_doc->lastLine()); KateTextLine::Ptr textLine = m_doc->kateTextLine(cursor.line()); if (!textLine) return 0; const uint len = textLine->length(); const TQChar *unicode = textLine->text(); const TQString &textString = textLine->string(); x = oldX = 0; uint z = startCol; while (x < xPos && (!wrapCursor || z < len)) { oldX = x; KateAttribute* a = attribute(textLine->attribute(z)); int width = 0; if (z < len) width = a->width(*fs, textString, z, m_tabWidth); else width = a->width(*fs, TQChar(' '), m_tabWidth); x += width; if (z < len && unicode[z] == TQChar('\t')) x -= x % width; z++; } if (xPos - oldX < x - xPos && z > 0) { z--; x = oldX; } cursor.setCol(z); return x; } const TQFont *KateRenderer::currentFont() { return config()->font(); } const TQFontMetrics* KateRenderer::currentFontMetrics() { return config()->fontMetrics(); } uint KateRenderer::textPos(uint line, int xPos, uint startCol, bool nearest) { return textPos(m_doc->kateTextLine(line), xPos, startCol, nearest); } uint KateRenderer::textPos(const KateTextLine::Ptr &textLine, int xPos, uint startCol, bool nearest) { Q_ASSERT(textLine); if (!textLine) return 0; KateFontStruct *fs = config()->fontStruct(); int x, oldX; x = oldX = 0; uint z = startCol; const uint len = textLine->length(); const TQString &textString = textLine->string(); while ( (x < xPos) && (z < len)) { oldX = x; KateAttribute* a = attribute(textLine->attribute(z)); x += a->width(*fs, textString, z, m_tabWidth); z++; } if ( ( (! nearest) || xPos - oldX < x - xPos ) && z > 0 ) { z--; // newXPos = oldX; }// else newXPos = x; return z; } uint KateRenderer::fontHeight() { return config()->fontStruct ()->fontHeight; } uint KateRenderer::documentHeight() { return m_doc->numLines() * fontHeight(); } bool KateRenderer::getSelectionBounds(uint line, uint lineLength, uint &start, uint &end) { bool hasSel = false; if (m_view->hasSelection() && !m_view->blockSelectionMode()) { if (m_view->lineIsSelection(line)) { start = m_view->selStartCol(); end = m_view->selEndCol(); hasSel = true; } else if ((int)line == m_view->selStartLine()) { start = m_view->selStartCol(); end = lineLength; hasSel = true; } else if ((int)line == m_view->selEndLine()) { start = 0; end = m_view->selEndCol(); hasSel = true; } } else if (m_view->lineHasSelected(line)) { start = m_view->selStartCol(); end = m_view->selEndCol(); hasSel = true; } if (start > end) { int temp = end; end = start; start = temp; } return hasSel; } void KateRenderer::updateConfig () { // update the attibute list pointer updateAttributes (); if (m_view) m_view->updateRendererConfig(); } uint KateRenderer::spaceWidth() { return attribute(0)->width(*config()->fontStruct(), TQChar(' '), m_tabWidth); } // kate: space-indent on; indent-width 2; replace-tabs on;