/* This file is part of the KDE libraries Copyright (C) 2003 Hamish Rodda <rodda@kde.org> Copyright (C) 2001 Christoph Cullmann <cullmann@kde.org> Copyright (C) 2001 Joseph Wenninger <jowenn@kde.org> Copyright (C) 1999 Jochen Wilhelmy <digisnap@cs.tu-berlin.de> 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 <kdebug.h> #include <tqpainter.h> #include <tqpopupmenu.h> 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<<bit); if (mrk & markType) { TQColor markColor = config()->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->tqcolorGroup().color(TQColorGroup::Foreground); } else if (isIMEdit && !isTab) { // XIM support // input method edit area const TQColorGroup& cg = m_view->tqcolorGroup(); int h1, s1, v1, h2, s2, v2; cg.color( TQColorGroup::Base ).hsv( &h1, &s1, &v1 ); 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->tqcolorGroup().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 *tqunicode = 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 && tqunicode[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 *tqunicode = 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 (tqunicode[z] == TQChar('\t')) x -= x % width; if (tqunicode[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 *tqunicode = 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 && tqunicode[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; tqreplace-tabs on;