• Skip to content
  • Skip to link menu
KDE 4.3 API Reference
  • KDE API Reference
  • kdelibs
  • Sitemap
  • Contact Us
 

Kate

katerenderer.cpp

Go to the documentation of this file.
00001 /* This file is part of the KDE libraries
00002    Copyright (C) 2007 Mirko Stocker <me@misto.ch>
00003    Copyright (C) 2003-2005 Hamish Rodda <rodda@kde.org>
00004    Copyright (C) 2001 Christoph Cullmann <cullmann@kde.org>
00005    Copyright (C) 2001 Joseph Wenninger <jowenn@kde.org>
00006    Copyright (C) 1999 Jochen Wilhelmy <digisnap@cs.tu-berlin.de>
00007 
00008    This library is free software; you can redistribute it and/or
00009    modify it under the terms of the GNU Library General Public
00010    License version 2 as published by the Free Software Foundation.
00011 
00012    This library is distributed in the hope that it will be useful,
00013    but WITHOUT ANY WARRANTY; without even the implied warranty of
00014    MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
00015    Library General Public License for more details.
00016 
00017    You should have received a copy of the GNU Library General Public License
00018    along with this library; see the file COPYING.LIB.  If not, write to
00019    the Free Software Foundation, Inc., 51 Franklin Street, Fifth Floor,
00020    Boston, MA 02110-1301, USA.
00021 */
00022 
00023 #include "katerenderer.h"
00024 
00025 #include "katedocument.h"
00026 #include "kateconfig.h"
00027 #include "katehighlight.h"
00028 #include "kateview.h"
00029 #include "katerenderrange.h"
00030 #include "katetextlayout.h"
00031 
00032 #include <limits.h>
00033 
00034 #include <kdebug.h>
00035 
00036 #include <QtGui/QPainter>
00037 #include <QtGui/QTextLine>
00038 #include <QtCore/QStack>
00039 #include <QtGui/QBrush>
00040 
00041 static const QChar tabChar('\t');
00042 static const QChar spaceChar(' ');
00043 
00044 KateRenderer::KateRenderer(KateDocument* doc, KateView *view)
00045   : m_doc(doc), m_view (view), m_caretStyle(KateRenderer::Line)
00046     , m_drawCaret(true)
00047     , m_showSelections(true)
00048     , m_showTabs(true)
00049     , m_showSpaces(true)
00050     , m_printerFriendly(false)
00051     , m_dynamicRegion(doc)
00052 {
00053   m_config = new KateRendererConfig (this);
00054 
00055   m_tabWidth = m_doc->config()->tabWidth();
00056   m_indentWidth = m_doc->config()->indentationWidth();
00057 
00058   updateAttributes ();
00059 }
00060 
00061 KateRenderer::~KateRenderer()
00062 {
00063   delete m_config;
00064 }
00065 
00066 void KateRenderer::updateAttributes ()
00067 {
00068   m_attributes = m_doc->highlight()->attributes (config()->schema ());
00069 }
00070 
00071 KTextEditor::Attribute::Ptr KateRenderer::attribute(uint pos) const
00072 {
00073   if (pos < (uint)m_attributes.count())
00074     return m_attributes[pos];
00075 
00076   return m_attributes[0];
00077 }
00078 
00079 KTextEditor::Attribute::Ptr KateRenderer::specificAttribute( int context ) const
00080 {
00081   if (context >= 0 && context < m_attributes.count())
00082     return m_attributes[context];
00083 
00084   return m_attributes[0];
00085 }
00086 
00087 void KateRenderer::setDrawCaret(bool drawCaret)
00088 {
00089   m_drawCaret = drawCaret;
00090 }
00091 
00092 void KateRenderer::setCaretStyle(KateRenderer::caretStyles style)
00093 {
00094   m_caretStyle = style;
00095 }
00096 
00097 void KateRenderer::setShowTabs(bool showTabs)
00098 {
00099   m_showTabs = showTabs;
00100 }
00101 
00102 void KateRenderer::setShowTrailingSpaces(bool showSpaces)
00103 {
00104   m_showSpaces = showSpaces;
00105 }
00106 
00107 void KateRenderer::setTabWidth(int tabWidth)
00108 {
00109   m_tabWidth = tabWidth;
00110 }
00111 
00112 bool KateRenderer::showIndentLines() const
00113 {
00114   return m_config->showIndentationLines();
00115 }
00116 
00117 void KateRenderer::setShowIndentLines(bool showIndentLines)
00118 {
00119   m_config->setShowIndentationLines(showIndentLines);
00120 }
00121 
00122 void KateRenderer::setIndentWidth(int indentWidth)
00123 {
00124   m_indentWidth = indentWidth;
00125 }
00126 
00127 void KateRenderer::setShowSelections(bool showSelections)
00128 {
00129   m_showSelections = showSelections;
00130 }
00131 
00132 void KateRenderer::increaseFontSizes()
00133 {
00134   QFont f ( config()->font () );
00135   f.setPointSize (f.pointSize ()+1);
00136 
00137   config()->setFont (f);
00138 }
00139 
00140 void KateRenderer::decreaseFontSizes()
00141 {
00142   QFont f ( config()->font () );
00143 
00144   if ((f.pointSize ()-1) > 0)
00145     f.setPointSize (f.pointSize ()-1);
00146 
00147   config()->setFont (f);
00148 }
00149 
00150 bool KateRenderer::isPrinterFriendly() const
00151 {
00152   return m_printerFriendly;
00153 }
00154 
00155 void KateRenderer::setPrinterFriendly(bool printerFriendly)
00156 {
00157   m_printerFriendly = printerFriendly;
00158   setShowTabs(false);
00159   setShowTrailingSpaces(false);
00160   setShowSelections(false);
00161   setDrawCaret(false);
00162 }
00163 
00164 void KateRenderer::paintTextLineBackground(QPainter& paint, KateLineLayoutPtr layout, int currentViewLine, int xStart, int xEnd)
00165 {
00166   if (isPrinterFriendly())
00167     return;
00168 
00169   // Normal background color
00170   QColor backgroundColor( config()->backgroundColor() );
00171 
00172   // paint the current line background if we're on the current line
00173   QColor currentLineColor = 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( layout->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 
00210   // Draw line background
00211   paint.fillRect(0, 0, xEnd - xStart, config()->fontMetrics().height() * layout->viewLineCount(), backgroundColor);
00212 
00213   // paint the current line background if we're on the current line
00214   if (currentViewLine != -1) {
00215     if (markCount) {
00216       markRed /= markCount;
00217       markGreen /= markCount;
00218       markBlue /= markCount;
00219       currentLineColor.setRgb(
00220         int((currentLineColor.red() * 0.9) + (markRed * 0.1)),
00221         int((currentLineColor.green() * 0.9) + (markGreen * 0.1)),
00222         int((currentLineColor.blue() * 0.9) + (markBlue * 0.1))
00223       );
00224     }
00225 
00226     paint.fillRect(0, config()->fontMetrics().height() * currentViewLine, xEnd - xStart, config()->fontMetrics().height(), currentLineColor);
00227   }
00228 }
00229 
00230 void KateRenderer::paintTabstop(QPainter &paint, qreal x, qreal y)
00231 {
00232   QPen penBackup( paint.pen() );
00233   QPen pen( config()->tabMarkerColor() );
00234   pen.setWidth(qMax(1u, spaceWidth() / 10));
00235   paint.setPen( pen );
00236   paint.setRenderHint(QPainter::Antialiasing, false);
00237 
00238   int dist = spaceWidth() * 0.3;
00239   QPoint points[8];
00240   points[0] = QPoint(x - dist, y - dist);
00241   points[1] = QPoint(x, y);
00242   points[2] = QPoint(x, y);
00243   points[3] = QPoint(x - dist, y + dist);
00244   x += spaceWidth() / 3.0;
00245   points[4] = QPoint(x - dist, y - dist);
00246   points[5] = QPoint(x, y);
00247   points[6] = QPoint(x, y);
00248   points[7] = QPoint(x - dist, y + dist);
00249   paint.drawLines(points, 4);
00250   paint.setPen( penBackup );
00251 }
00252 
00253 void KateRenderer::paintTrailingSpace(QPainter &paint, qreal x, qreal y)
00254 {
00255   QPen penBackup( paint.pen() );
00256   QPen pen( config()->tabMarkerColor() );
00257   pen.setWidthF(spaceWidth() / 3.5);
00258   pen.setCapStyle(Qt::RoundCap);
00259   paint.setPen( pen );
00260 
00261   paint.drawPoint( QPointF(x, y) );
00262   paint.setPen( penBackup );
00263 }
00264 
00265 void KateRenderer::paintIndentMarker(QPainter &paint, uint x, uint row)
00266 {
00267   QPen penBackup( paint.pen() );
00268   paint.setPen( config()->tabMarkerColor() );
00269 
00270   const int height = config()->fontMetrics().height();
00271   const int top = 0;
00272   const int bottom = height-1;
00273   const int h = bottom - top + 1;
00274 
00275   // Dot padding.
00276   int pad = 0;
00277   if(row & 1 && h & 1) pad = 1;
00278 
00279   for(int i = top; i <= bottom; i++)
00280   {
00281     if((i + pad) & 1)
00282     {
00283       paint.drawPoint(x + 2, i);
00284     }
00285   }
00286 
00287   paint.setPen( penBackup );
00288 }
00289 
00290 QList<QTextLayout::FormatRange> KateRenderer::decorationsForLine( const KateTextLine::Ptr& textLine, int line, bool selectionsOnly, KateRenderRange* completionHighlight, bool completionSelected ) const
00291 {
00292   QList<QTextLayout::FormatRange> newHighlight;
00293 
00294   // Don't compute the highlighting if there isn't going to be any highlighting
00295   if (selectionsOnly || textLine->attributesList().count() || m_view->externalHighlights().count() || m_view->internalHighlights().count() || m_doc->documentHighlights().count()) {
00296     RenderRangeList renderRanges;
00297 
00298     // Add the inbuilt highlighting to the list
00299     NormalRenderRange* inbuiltHighlight = new NormalRenderRange();
00300     const QVector<int> &al = textLine->attributesList();
00301     for (int i = 0; i+2 < al.count(); i += 3) {
00302       inbuiltHighlight->addRange(new KTextEditor::Range(KTextEditor::Cursor(line, al[i]), al[i+1]), specificAttribute(al[i+2]));
00303     }
00304     renderRanges.append(inbuiltHighlight);
00305 
00306     if (!completionHighlight) {
00307       // Add arbitrary highlighting ranges to the list
00308       renderRanges.appendRanges(m_view->internalHighlights(), selectionsOnly, view());
00309       renderRanges.appendRanges(m_view->externalHighlights(), selectionsOnly, view());
00310 
00311     } else {
00312       // Add the code completion arbitrary highlight to the list
00313       renderRanges.append(completionHighlight);
00314     }
00315 
00316     // Add selection highlighting if we're creating the selection decorations
00317     if ((selectionsOnly && showSelections() && m_view->selection()) || (completionHighlight && completionSelected) || m_view->blockSelection()) {
00318       NormalRenderRange* selectionHighlight = new NormalRenderRange();
00319 
00320       // Set up the selection background attribute TODO: move this elsewhere, eg. into the config?
00321       static KTextEditor::Attribute::Ptr backgroundAttribute;
00322       if (!backgroundAttribute)
00323         backgroundAttribute = KTextEditor::Attribute::Ptr(new KTextEditor::Attribute());
00324 
00325       backgroundAttribute->setBackground(config()->selectionColor());
00326 
00327       // Create a range for the current selection
00328       if (completionHighlight && completionSelected)
00329         selectionHighlight->addRange(new KTextEditor::Range(line, 0, line + 1, 0), backgroundAttribute);
00330       else
00331         if(m_view->blockSelection() && m_view->selectionRange().overlapsLine(line))
00332           selectionHighlight->addRange(new KTextEditor::Range(line, m_view->selectionRange().start().column(), line, m_view->selectionRange().end().column()), backgroundAttribute);
00333         else
00334           selectionHighlight->addRange(new KTextEditor::Range(m_view->selectionRange()), backgroundAttribute);
00335 
00336       renderRanges.append(selectionHighlight);
00337     }
00338 
00339     KTextEditor::Cursor currentPosition, endPosition;
00340 
00341     // Calculate the range which we need to iterate in order to get the highlighting for just this line
00342     if (selectionsOnly) {
00343       if(m_view->blockSelection()) {
00344         int startColumn = m_view->selectionRange().start().column();
00345         int endColumn = m_view->selectionRange().end().column();
00346         currentPosition = KTextEditor::Cursor(line, qMin(startColumn, endColumn));
00347         endPosition = KTextEditor::Cursor(line, qMax(startColumn, endColumn));
00348       } else {
00349         KTextEditor::Range rangeNeeded = m_view->selectionRange().encompass(m_dynamicRegion.boundingRange());
00350         rangeNeeded &= KTextEditor::Range(line, 0, line + 1, 0);
00351 
00352         currentPosition = qMax(KTextEditor::Cursor(line, 0), rangeNeeded.start());
00353         endPosition = qMin(KTextEditor::Cursor(line + 1, 0), rangeNeeded.end());
00354       }
00355     } else {
00356       currentPosition = KTextEditor::Cursor(line, 0);
00357       endPosition = KTextEditor::Cursor(line + 1, 0);
00358     }
00359 
00360     // Main iterative loop.  This walks through each set of highlighting ranges, and stops each
00361     // time the highlighting changes.  It then creates the corresponding QTextLayout::FormatRanges.
00362     while (currentPosition < endPosition) {
00363       renderRanges.advanceTo(currentPosition);
00364 
00365       if (!renderRanges.hasAttribute()) {
00366         // No attribute, don't need to create a FormatRange for this text range
00367         currentPosition = renderRanges.nextBoundary();
00368         continue;
00369       }
00370 
00371       KTextEditor::Cursor nextPosition = renderRanges.nextBoundary();
00372 
00373       // Create the format range and populate with the correct start, length and format info
00374       QTextLayout::FormatRange fr;
00375       fr.start = currentPosition.column();
00376 
00377       if (nextPosition < endPosition || endPosition.line() <= line) {
00378         fr.length = nextPosition.column() - currentPosition.column();
00379 
00380       } else {
00381         // +1 to force background drawing at the end of the line when it's warranted
00382         fr.length = textLine->length() - currentPosition.column() + 1;
00383       }
00384 
00385       KTextEditor::Attribute::Ptr a = renderRanges.generateAttribute();
00386       if(a) {
00387         fr.format = *a;
00388 
00389         if(selectionsOnly) {
00390           if(m_view->blockSelection()) {
00391             int minSelectionColumn = qMin(m_view->selectionRange().start().column(), m_view->selectionRange().end().column());
00392             int maxSelectionColumn = qMax(m_view->selectionRange().start().column(), m_view->selectionRange().end().column());
00393 
00394             if(currentPosition.column() >= minSelectionColumn && currentPosition.column() < maxSelectionColumn)
00395               assignSelectionBrushesFromAttribute(fr, *a);
00396 
00397           } else if (m_view->selection() && m_view->selectionRange().contains(currentPosition)) {
00398             assignSelectionBrushesFromAttribute(fr, *a);
00399           }
00400         }
00401       }
00402 
00403       newHighlight.append(fr);
00404 
00405       currentPosition = nextPosition;
00406     }
00407 
00408     if (completionHighlight)
00409       // Don't delete external completion render range
00410       renderRanges.removeAll(completionHighlight);
00411 
00412     qDeleteAll(renderRanges);
00413   }
00414 
00415   return newHighlight;
00416 }
00417 
00418 void KateRenderer::assignSelectionBrushesFromAttribute(QTextLayout::FormatRange& target, const KTextEditor::Attribute& attribute) const
00419 {
00420   if(attribute.hasProperty(KTextEditor::Attribute::SelectedForeground)) {
00421     target.format.setForeground(attribute.selectedForeground());
00422   }
00423   if(attribute.hasProperty(KTextEditor::Attribute::SelectedBackground)) {
00424     target.format.setBackground(attribute.selectedBackground());
00425   }
00426 }
00427 
00428 /*
00429 The ultimate line painting function.
00430 Currently missing features:
00431 - draw indent lines
00432 */
00433 void KateRenderer::paintTextLine(QPainter& paint, KateLineLayoutPtr range, int xStart, int xEnd, const KTextEditor::Cursor* cursor)
00434 {
00435   Q_ASSERT(range->isValid());
00436 
00437 //   kDebug( 13033 )<<"KateRenderer::paintTextLine";
00438 
00439   // font data
00440   const QFontMetrics& fm = config()->fontMetrics();
00441 
00442   int currentViewLine = -1;
00443   if (cursor && cursor->line() == range->line())
00444     currentViewLine = range->viewLineForColumn(cursor->column());
00445 
00446   paintTextLineBackground(paint, range, currentViewLine, xStart, xEnd);
00447 
00448   // Draws the dashed underline at the start of a folded block of text.
00449   if (range->startsInvisibleBlock()) {
00450     paint.setRenderHint(QPainter::Antialiasing, false);
00451     QPen pen(config()->wordWrapMarkerColor());
00452     pen.setCosmetic(true);
00453     pen.setStyle(Qt::DashLine);
00454     QVector<qreal> dash = pen.dashPattern(); // workaround for N226156
00455     pen.setDashOffset(xStart);
00456     pen.setDashPattern(dash);
00457     paint.setPen(pen);
00458     paint.drawLine(0, (fm.height() * range->viewLineCount()) - 1, xEnd - xStart, (fm.height() * range->viewLineCount()) - 1);
00459   }
00460 
00461   if (range->layout()) {
00462     QVector<QTextLayout::FormatRange> additionalFormats;
00463     if (range->length() > 0) {
00464       // We may have changed the pen, be absolutely sure it gets set back to
00465       // normal foreground color before drawing text for text that does not
00466       // set the pen color
00467       paint.setPen(attribute(KateExtendedAttribute::dsNormal)->foreground().color());
00468       // Draw the text :)
00469       if (m_dynamicRegion.boundingRange().isValid() || (m_view->selection() && showSelections() && m_view->selectionRange().overlapsLine(range->line()))) {
00470         // FIXME toVector() may be a performance issue
00471         additionalFormats = decorationsForLine(range->textLine(), range->line(), true).toVector();
00472         range->layout()->draw(&paint, QPoint(-xStart,0), additionalFormats);
00473 
00474       } else {
00475         range->layout()->draw(&paint, QPoint(-xStart,0));
00476       }
00477     }
00478 
00479     QBrush backgroundBrush;
00480     bool backgroundBrushSet = false;
00481 
00482     // Loop each individual line for additional text decoration etc.
00483     QListIterator<QTextLayout::FormatRange> it = range->layout()->additionalFormats();
00484     QVectorIterator<QTextLayout::FormatRange> it2 = additionalFormats;
00485     for (int i = 0; i < range->viewLineCount(); ++i) {
00486       KateTextLayout line = range->viewLine(i);
00487 
00488       // Determine the background to use, if any, for the end of this view line
00489       backgroundBrushSet = false;
00490       while (it2.hasNext()) {
00491         const QTextLayout::FormatRange& fr = it2.peekNext();
00492         if (fr.start > line.endCol())
00493           goto backgroundDetermined;
00494 
00495         if (fr.start + fr.length > line.endCol()) {
00496           if (fr.format.hasProperty(QTextFormat::BackgroundBrush)) {
00497             backgroundBrushSet = true;
00498             backgroundBrush = fr.format.background();
00499           }
00500 
00501           goto backgroundDetermined;
00502         }
00503 
00504         it2.next();
00505       }
00506 
00507       while (it.hasNext()) {
00508         const QTextLayout::FormatRange& fr = it.peekNext();
00509         if (fr.start > line.endCol())
00510           break;
00511 
00512         if (fr.start + fr.length > line.endCol()) {
00513           if (fr.format.hasProperty(QTextFormat::BackgroundBrush)) {
00514             backgroundBrushSet = true;
00515             backgroundBrush = fr.format.background();
00516           }
00517 
00518           break;
00519         }
00520 
00521         it.next();
00522       }
00523 
00524       backgroundDetermined:
00525 
00526       // Draw selection or background color outside of areas where text is rendered
00527       if (!m_printerFriendly ) {
00528         bool draw = false;
00529         QBrush drawBrush;
00530         if (m_view->selection() && !m_view->blockSelection() && m_view->lineEndSelected(line.end(true))) {
00531           draw = true;
00532           drawBrush = config()->selectionColor();
00533         } else if (backgroundBrushSet && !m_view->blockSelection()) {
00534           draw = true;
00535           drawBrush = backgroundBrush;
00536         }
00537 
00538         if (draw) {
00539           int fillStartX = line.endX() - line.startX() + line.xOffset() - xStart;
00540           int fillStartY = fm.height() * i;
00541           int width= xEnd - xStart - fillStartX;
00542           int height= fm.height();
00543 
00544           // reverse X for right-aligned lines
00545           if (range->layout()->textOption().alignment() == Qt::AlignRight)
00546             fillStartX = 0;
00547 
00548           QRect area(fillStartX, fillStartY, width, height);
00549           paint.fillRect(area, drawBrush);
00550         }
00551       }
00552       // Draw indent lines
00553       if (showIndentLines() && i == 0)
00554       {
00555         const int w = spaceWidth();
00556         const int lastIndentColumn = range->textLine()->indentDepth(m_tabWidth);
00557 
00558         for (int x = m_indentWidth; x < lastIndentColumn; x += m_indentWidth)
00559         {
00560           paintIndentMarker(paint, x * w + 1 - xStart, range->line());
00561         }
00562       }
00563 
00564       // Draw tab stops and trailing spaces
00565       if (showTabs() || showTrailingSpaces()) {
00566         const QString& text = range->textLine()->string();
00567         int y = fm.height() * i + fm.ascent() - fm.strikeOutPos();
00568 
00569         if (showTabs()) {
00570           int tabIndex = text.indexOf(tabChar, line.startCol());
00571           while (tabIndex != -1 && tabIndex < line.endCol()) {
00572             paintTabstop(paint, line.lineLayout().cursorToX(tabIndex) - xStart + spaceWidth()/2.0, y);
00573             tabIndex = text.indexOf(tabChar, tabIndex + 1);
00574           }
00575         }
00576 
00577         if (showTrailingSpaces()) {
00578           int spaceIndex = line.endCol() - 1;
00579           int trailingPos = range->textLine()->lastChar();
00580           if (trailingPos < 0)
00581             trailingPos = 0;
00582           if (spaceIndex >= trailingPos) {
00583             while (spaceIndex >= line.startCol() && text.at(spaceIndex).isSpace()) {
00584               if (text.at(spaceIndex) != '\t' || !showTabs())
00585                 paintTrailingSpace(paint, line.lineLayout().cursorToX(spaceIndex) - xStart + spaceWidth()/2.0, y);
00586               --spaceIndex;
00587             }
00588           }
00589         }
00590       }
00591     }
00592 
00593     // draw word-wrap-honor-indent filling
00594     if ( (range->viewLineCount() > 1)  && range->shiftX() && (range->shiftX() > xStart) )
00595     {
00596       if (backgroundBrushSet)
00597         paint.fillRect(0, fm.height(), range->shiftX() - xStart, fm.height() * (range->viewLineCount() - 1),
00598           backgroundBrush);
00599       paint.fillRect(0, fm.height(), range->shiftX() - xStart, fm.height() * (range->viewLineCount() - 1),
00600         QBrush(config()->wordWrapMarkerColor(), Qt::Dense4Pattern));
00601     }
00602 
00603     // Draw caret
00604     if (drawCaret() && cursor && range->includesCursor(*cursor)) {
00605       // Make the caret the desired width
00606       int caretWidth = 2;
00607       QTextLine line = range->layout()->lineForTextPosition(cursor->column());
00608       if (caretStyle() == Block || (m_view->viInputMode() && m_view->getCurrentViMode() != InsertMode)) {
00609         if (line.isValid() && cursor->column() < range->length()) {
00610           caretWidth = int(line.cursorToX(cursor->column() + 1) - line.cursorToX(cursor->column()));
00611           if (caretWidth < 0)
00612             caretWidth = -caretWidth;
00613 
00614         } else {
00615           caretWidth = spaceWidth();
00616         }
00617       }
00618 
00619       QColor c;
00620       // Could actually use the real highlighting system for this... would be slower but more accurate for corner cases
00621       if (m_caretOverrideColor.isValid()) {
00622         c = m_caretOverrideColor;
00623 
00624       } else {
00625         // search for the FormatRange that includes the cursor
00626         foreach (const QTextLayout::FormatRange &r, range->layout()->additionalFormats()) {
00627           if ( (r.start <= cursor->column() ) && ( (r.start + r.length)  > cursor->column()) ) {
00628             // check for Qt::NoBrush, as the returned color is black() and no invalid QColor
00629             QBrush foregroundBrush = r.format.foreground();
00630             if (foregroundBrush != Qt::NoBrush) {
00631               c = r.format.foreground().color();
00632             }
00633             break;
00634           }
00635         }
00636 
00637         // still no color found, fall back to default style
00638         if (!c.isValid())
00639             c = attribute(KateExtendedAttribute::dsNormal)->foreground().color();
00640       }
00641 
00642       // make it possible to see the selected character in the vi input mode's normal/visual mode
00643       if (m_view->viInputMode() && m_view->getCurrentViMode() != InsertMode ) {
00644         c.setAlpha(128);
00645       }
00646 
00647       if (cursor->column() <= range->length()) {
00648         paint.save();
00649         paint.setPen(QPen(c, caretWidth));
00650 
00651         // Clip the caret - Qt's caret has a habit of intruding onto other lines
00652         paint.setClipRect(0, line.lineNumber() * fm.height(), xEnd - xStart, fm.height());
00653 
00654         range->layout()->drawCursor(&paint, QPoint(-xStart,0), cursor->column(), caretWidth);
00655 
00656         paint.restore();
00657 
00658       } else {
00659         // Off the end of the line... must be block mode. Draw the caret ourselves.
00660         const KateTextLayout& lastLine = range->viewLine(range->viewLineCount() - 1);
00661         int x = range->widthOfLastLine() + spaceWidth() * (cursor->column() - range->length());
00662         if ( (x >= xStart) && (x <= xEnd))
00663           paint.fillRect(x-xStart, (int)lastLine.lineLayout().y(), caretWidth, fm.height(), c);
00664       }
00665     }
00666   }
00667 
00668   // show word wrap marker if desirable
00669   if ((!isPrinterFriendly()) && config()->wordWrapMarker() && QFontInfo(config()->font()).fixedPitch())
00670   {
00671     paint.setPen( config()->wordWrapMarkerColor() );
00672     int _x = m_doc->config()->wordWrapAt() * fm.width('x') - xStart;
00673     paint.drawLine( _x,0,_x,fm.height() );
00674   }
00675 }
00676 
00677 const QFont& KateRenderer::currentFont() const
00678 {
00679   return config()->font();
00680 }
00681 
00682 const QFontMetrics& KateRenderer::currentFontMetrics() const
00683 {
00684   return config()->fontMetrics();
00685 }
00686 
00687 uint KateRenderer::fontHeight()
00688 {
00689   return config()->fontMetrics().height();
00690 }
00691 
00692 uint KateRenderer::documentHeight()
00693 {
00694   return m_doc->lines() * fontHeight();
00695 }
00696 
00697 bool KateRenderer::getSelectionBounds(int line, int lineLength, int &start, int &end) const
00698 {
00699   bool hasSel = false;
00700 
00701   if (m_view->selection() && !m_view->blockSelectionMode())
00702   {
00703     if (m_view->lineIsSelection(line))
00704     {
00705       start = m_view->selectionRange().start().column();
00706       end = m_view->selectionRange().end().column();
00707       hasSel = true;
00708     }
00709     else if (line == m_view->selectionRange().start().line())
00710     {
00711       start = m_view->selectionRange().start().column();
00712       end = lineLength;
00713       hasSel = true;
00714     }
00715     else if (m_view->selectionRange().containsLine(line))
00716     {
00717       start = 0;
00718       end = lineLength;
00719       hasSel = true;
00720     }
00721     else if (line == m_view->selectionRange().end().line())
00722     {
00723       start = 0;
00724       end = m_view->selectionRange().end().column();
00725       hasSel = true;
00726     }
00727   }
00728   else if (m_view->lineHasSelected(line))
00729   {
00730     start = m_view->selectionRange().start().column();
00731     end = m_view->selectionRange().end().column();
00732     hasSel = true;
00733   }
00734 
00735   if (start > end) {
00736     int temp = end;
00737     end = start;
00738     start = temp;
00739   }
00740 
00741   return hasSel;
00742 }
00743 
00744 void KateRenderer::updateConfig ()
00745 {
00746   // update the attibute list pointer
00747   updateAttributes ();
00748 
00749   if (m_view)
00750     m_view->updateRendererConfig();
00751 }
00752 
00753 uint KateRenderer::spaceWidth() const
00754 {
00755   return config()->fontMetrics().width(spaceChar);
00756 }
00757 
00758 void KateRenderer::layoutLine(KateLineLayoutPtr lineLayout, int maxwidth, bool cacheLayout) const
00759 {
00760   // if maxwidth == -1 we have no wrap
00761 
00762   KateTextLine::Ptr textLine = lineLayout->textLine();
00763   Q_ASSERT(textLine);
00764 
00765   QTextLayout* l = lineLayout->layout();
00766   if (!l) {
00767     l = new QTextLayout(textLine->string(), config()->font());
00768   } else {
00769     l->setText(textLine->string());
00770     l->setFont(config()->font());
00771   }
00772 
00773   l->setCacheEnabled(cacheLayout);
00774 
00775   // Initial setup of the QTextLayout.
00776 
00777   // Tab width
00778   QTextOption opt;
00779   opt.setFlags(QTextOption::IncludeTrailingSpaces);
00780   opt.setTabStop(m_tabWidth * config()->fontMetrics().width(spaceChar));
00781   opt.setWrapMode(QTextOption::WrapAtWordBoundaryOrAnywhere);
00782 
00783   // Find the first strong character in the string.
00784   // If it is an RTL character, set the base layout direction of the string to RTL.
00785   //
00786   // See http://www.unicode.org/reports/tr9/#The_Paragraph_Level (Sections P2 & P3).
00787   // Qt's text renderer ("scribe") version 4.2 assumes a "higher-level protocol"
00788   // (such as KatePart) will specify the paragraph level, so it does not apply P2 & P3
00789   // by itself. If this ever change in Qt, the next code block could be removed.
00790   if (isLineRightToLeft(lineLayout)) {
00791       opt.setAlignment( Qt::AlignRight );
00792       opt.setTextDirection( Qt::RightToLeft );
00793   }
00794   else {
00795       opt.setAlignment( Qt::AlignLeft );
00796       opt.setTextDirection( Qt::LeftToRight );
00797   }
00798 
00799   l->setTextOption(opt);
00800 
00801   // Syntax highlighting, inbuilt and arbitrary
00802   l->setAdditionalFormats(decorationsForLine(textLine, lineLayout->line()));
00803 
00804   // Begin layouting
00805   l->beginLayout();
00806 
00807   int height = 0;
00808   int shiftX = 0;
00809 
00810   bool needShiftX = (maxwidth != -1)
00811                  && (m_view->config()->dynWordWrapAlignIndent() > 0);
00812 
00813   forever {
00814     QTextLine line = l->createLine();
00815     if (!line.isValid())
00816       break;
00817 
00818     if (maxwidth > 0)
00819       line.setLineWidth(maxwidth);
00820 
00821     line.setPosition(QPoint(line.lineNumber() ? shiftX : 0, height));
00822 
00823     if (needShiftX) {
00824       needShiftX = false;
00825       // Determine x offset for subsequent-lines-of-paragraph indenting
00826       int pos = textLine->nextNonSpaceChar(0);
00827 
00828       if (pos > 0) {
00829         shiftX = (int)line.cursorToX(pos);
00830       }
00831 
00832       // check for too deep shift value and limit if necessary
00833       if (shiftX > ((double)maxwidth / 100 * m_view->config()->dynWordWrapAlignIndent()))
00834         shiftX = 0;
00835 
00836       // if shiftX > 0, the maxwidth has to adapted
00837       maxwidth -= shiftX;
00838 
00839       lineLayout->setShiftX(shiftX);
00840     }
00841 
00842     height += config()->fontMetrics().height();
00843   }
00844 
00845   l->endLayout();
00846 
00847   lineLayout->setLayout(l);
00848 }
00849 
00850 
00851 // 1) QString::isRightToLeft() sux
00852 // 2) QString::isRightToLeft() is marked as internal (WTF?)
00853 // 3) QString::isRightToLeft() does not seem to work on my setup
00854 // 4) isStringRightToLeft() should behave much better than QString::isRightToLeft() therefore:
00855 // 5) isStringRightToLeft() kicks ass
00856 bool KateRenderer::isLineRightToLeft( KateLineLayoutPtr lineLayout ) const
00857 {
00858   QString s = lineLayout->textLine()->string();
00859   int i = 0;
00860 
00861   // borrowed from QString::updateProperties()
00862   while( i != s.length() )
00863   {
00864     QChar c = s.at(i);
00865 
00866     switch(c.direction()) {
00867       case QChar::DirL:
00868       case QChar::DirLRO:
00869       case QChar::DirLRE:
00870           return false;
00871 
00872       case QChar::DirR:
00873       case QChar::DirAL:
00874       case QChar::DirRLO:
00875       case QChar::DirRLE:
00876           return true;
00877 
00878       default:
00879           break;
00880     }
00881     i ++;
00882   }
00883 
00884    return false;
00885 #if 0
00886   // or should we use the direction of the widget?
00887   QWidget* display = qobject_cast<QWidget*>(view()->parent());
00888   if (!display)
00889     return false;
00890   return display->layoutDirection() == Qt::RightToLeft;
00891 #endif
00892 }
00893 
00894 int KateRenderer::cursorToX(const KateTextLayout& range, int col) const
00895 {
00896   return cursorToX(range, KTextEditor::Cursor(range.line(), col));
00897 }
00898 
00899 int KateRenderer::cursorToX(const KateTextLayout& range, const KTextEditor::Cursor & pos) const
00900 {
00901   Q_ASSERT(range.isValid());
00902 
00903   return (int)range.lineLayout().cursorToX(pos.column());
00904 }
00905 
00906 int KateRenderer::cursorToX(const KateTextLayout& range, const KTextEditor::Cursor & pos, bool returnPastLine) const
00907 {
00908   int x = cursorToX(range, pos);
00909   int over = pos.column() * spaceWidth() - range.width();
00910  
00911   if (returnPastLine && over > 0)
00912     x += over;
00913 
00914   return x;
00915 }
00916 
00917 KTextEditor::Cursor KateRenderer::xToCursor(const KateTextLayout & range, int x, bool returnPastLine ) const
00918 {
00919   Q_ASSERT(range.isValid());
00920   KTextEditor::Cursor ret(range.line(), range.lineLayout().xToCursor(x));
00921 
00922   // TODO wrong for RTL lines?
00923   if (returnPastLine && x > range.width() + range.xOffset())
00924     ret.setColumn(ret.column() + ((x - (range.width() + range.xOffset())) / spaceWidth()));
00925 
00926   return ret;
00927 }
00928 
00929 void KateRenderer::setCaretOverrideColor(const QColor& color)
00930 {
00931   m_caretOverrideColor = color;
00932 }
00933 
00934 // kate: space-indent on; indent-width 2; replace-tabs on;

Kate

Skip menu "Kate"
  • Main Page
  • Namespace List
  • Class Hierarchy
  • Alphabetical List
  • Class List
  • File List
  • Namespace Members
  • Class Members
  • Related Pages

kdelibs

Skip menu "kdelibs"
  • DNSSD
  • Interfaces
  •   KHexEdit
  •   KMediaPlayer
  •   KSpeech
  •   KTextEditor
  • Kate
  • kconf_update
  • KDE3Support
  •   KUnitTest
  • KDECore
  • KDED
  • KDEsu
  • KDEUI
  • KDocTools
  • KFile
  • KHTML
  • KImgIO
  • KInit
  • kio
  • KIOSlave
  • KJS
  •   KJS-API
  •   WTF
  • kjsembed
  • KNewStuff
  • KParts
  • KPty
  • Kross
  • KUtils
  • Nepomuk
  • Plasma
  • Solid
  • Sonnet
  • ThreadWeaver
Generated for kdelibs by doxygen 1.6.1
This website is maintained by Adriaan de Groot and Allen Winter.
KDE® and the K Desktop Environment® logo are registered trademarks of KDE e.V. | Legal