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