• Skip to content
  • Skip to link menu
KDE 4.1 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.setWidthF(qMax(0.5, spaceWidth() * .1));
00235   pen.setCapStyle(Qt::RoundCap);
00236   paint.setPen( pen );
00237 
00238   // FIXME: optimize for speed!
00239   qreal dist = spaceWidth() * 0.3;
00240   QPointF points[8];
00241   points[0] = QPointF(x - dist, y - dist);
00242   points[1] = QPointF(x, y);
00243   points[2] = QPointF(x, y);
00244   points[3] = QPointF(x - dist, y + dist);
00245   x += spaceWidth() / 3.0;
00246   points[4] = QPointF(x - dist, y - dist);
00247   points[5] = QPointF(x, y);
00248   points[6] = QPointF(x, y);
00249   points[7] = QPointF(x - dist, y + dist);
00250   paint.drawLines(points, 4);
00251   paint.setPen( penBackup );
00252 }
00253 
00254 void KateRenderer::paintTrailingSpace(QPainter &paint, qreal x, qreal y)
00255 {
00256   QPen penBackup( paint.pen() );
00257   QPen pen( config()->tabMarkerColor() );
00258   pen.setWidthF(spaceWidth() / 3.5);
00259   pen.setCapStyle(Qt::RoundCap);
00260   paint.setPen( pen );
00261 
00262   paint.drawPoint( QPointF(x, y) );
00263   paint.setPen( penBackup );
00264 }
00265 
00266 void KateRenderer::paintIndentMarker(QPainter &paint, uint x, uint row)
00267 {
00268   QPen penBackup( paint.pen() );
00269   paint.setPen( config()->tabMarkerColor() );
00270 
00271   const int height = config()->fontMetrics().height();
00272   const int top = 0;
00273   const int bottom = height-1;
00274   const int h = bottom - top + 1;
00275 
00276   // Dot padding.
00277   int pad = 0;
00278   if(row & 1 && h & 1) pad = 1;
00279 
00280   for(int i = top; i <= bottom; i++)
00281   {
00282     if((i + pad) & 1)
00283     {
00284       paint.drawPoint(x + 2, i);
00285     }
00286   }
00287 
00288   paint.setPen( penBackup );
00289 }
00290 
00291 QList<QTextLayout::FormatRange> KateRenderer::decorationsForLine( const KateTextLine::Ptr& textLine, int line, bool selectionsOnly, KateRenderRange* completionHighlight, bool completionSelected ) const
00292 {
00293   QList<QTextLayout::FormatRange> newHighlight;
00294 
00295   // Don't compute the highlighting if there isn't going to be any highlighting
00296   if (selectionsOnly || textLine->attributesList().count() || m_view->externalHighlights().count() || m_view->internalHighlights().count() || m_doc->documentHighlights().count()) {
00297     RenderRangeList renderRanges;
00298 
00299     // Add the inbuilt highlighting to the list
00300     NormalRenderRange* inbuiltHighlight = new NormalRenderRange();
00301     const QVector<int> &al = textLine->attributesList();
00302     for (int i = 0; i+2 < al.count(); i += 3) {
00303       inbuiltHighlight->addRange(new KTextEditor::Range(KTextEditor::Cursor(line, al[i]), al[i+1]), specificAttribute(al[i+2]));
00304     }
00305     renderRanges.append(inbuiltHighlight);
00306 
00307     if (!completionHighlight) {
00308       // Add arbitrary highlighting ranges to the list
00309       renderRanges.appendRanges(m_view->internalHighlights(), selectionsOnly, view());
00310       renderRanges.appendRanges(m_view->externalHighlights(), selectionsOnly, view());
00311       renderRanges.appendRanges(m_doc->documentHighlights(), selectionsOnly, view());
00312 
00313     } else {
00314       // Add the code completion arbitrary highlight to the list
00315       renderRanges.append(completionHighlight);
00316     }
00317 
00318     // Add selection highlighting if we're creating the selection decorations
00319     if ((selectionsOnly && showSelections() && m_view->selection()) || (completionHighlight && completionSelected) || m_view->blockSelection()) {
00320       NormalRenderRange* selectionHighlight = new NormalRenderRange();
00321 
00322       // Set up the selection background attribute TODO: move this elsewhere, eg. into the config?
00323       static KTextEditor::Attribute::Ptr backgroundAttribute;
00324       if (!backgroundAttribute)
00325         backgroundAttribute = KTextEditor::Attribute::Ptr(new KTextEditor::Attribute());
00326 
00327       backgroundAttribute->setBackground(config()->selectionColor());
00328 
00329       // Create a range for the current selection
00330       if (completionHighlight && completionSelected)
00331         selectionHighlight->addRange(new KTextEditor::Range(line, 0, line + 1, 0), backgroundAttribute);
00332       else
00333         if(m_view->blockSelection() && m_view->selectionRange().overlapsLine(line))
00334           selectionHighlight->addRange(new KTextEditor::Range(line, m_view->selectionRange().start().column(), line, m_view->selectionRange().end().column()), backgroundAttribute);
00335         else
00336           selectionHighlight->addRange(new KTextEditor::Range(m_view->selectionRange()), backgroundAttribute);
00337 
00338       renderRanges.append(selectionHighlight);
00339     }
00340 
00341     KTextEditor::Cursor currentPosition, endPosition;
00342 
00343     // Calculate the range which we need to iterate in order to get the highlighting for just this line
00344     if (selectionsOnly) {
00345       if(m_view->blockSelection()) {
00346         int startColumn = m_view->selectionRange().start().column();
00347         int endColumn = m_view->selectionRange().end().column();
00348         currentPosition = KTextEditor::Cursor(line, qMin(startColumn, endColumn));
00349         endPosition = KTextEditor::Cursor(line, qMax(startColumn, endColumn));
00350       } else {
00351         KTextEditor::Range rangeNeeded = m_view->selectionRange().encompass(m_dynamicRegion.boundingRange());
00352         rangeNeeded &= KTextEditor::Range(line, 0, line + 1, 0);
00353 
00354         currentPosition = qMax(KTextEditor::Cursor(line, 0), rangeNeeded.start());
00355         endPosition = qMin(KTextEditor::Cursor(line + 1, 0), rangeNeeded.end());
00356       }
00357     } else {
00358       currentPosition = KTextEditor::Cursor(line, 0);
00359       endPosition = KTextEditor::Cursor(line + 1, 0);
00360     }
00361 
00362     // Main iterative loop.  This walks through each set of highlighting ranges, and stops each
00363     // time the highlighting changes.  It then creates the corresponding QTextLayout::FormatRanges.
00364     while (currentPosition < endPosition) {
00365       renderRanges.advanceTo(currentPosition);
00366 
00367       if (!renderRanges.hasAttribute()) {
00368         // No attribute, don't need to create a FormatRange for this text range
00369         currentPosition = renderRanges.nextBoundary();
00370         continue;
00371       }
00372 
00373       KTextEditor::Cursor nextPosition = renderRanges.nextBoundary();
00374 
00375       // Create the format range and populate with the correct start, length and format info
00376       QTextLayout::FormatRange fr;
00377       fr.start = currentPosition.column();
00378 
00379       if (nextPosition < endPosition || endPosition.line() <= line) {
00380         fr.length = nextPosition.column() - currentPosition.column();
00381 
00382       } else {
00383         // +1 to force background drawing at the end of the line when it's warranted
00384         fr.length = textLine->length() - currentPosition.column() + 1;
00385       }
00386 
00387       KTextEditor::Attribute a = renderRanges.generateAttribute();
00388       fr.format = a;
00389 
00390       if(selectionsOnly) {
00391         if(m_view->blockSelection()) {
00392           int minSelectionColumn = qMin(m_view->selectionRange().start().column(), m_view->selectionRange().end().column());
00393           int maxSelectionColumn = qMax(m_view->selectionRange().start().column(), m_view->selectionRange().end().column());
00394 
00395           if(currentPosition.column() >= minSelectionColumn && currentPosition.column() < maxSelectionColumn)
00396             assignSelectionBrushesFromAttribute(fr, a);
00397 
00398         } else if (m_view->selection() && m_view->selectionRange().contains(currentPosition)) {
00399           assignSelectionBrushesFromAttribute(fr, a);
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.setPen(QPen(config()->wordWrapMarkerColor(), 1, Qt::DashLine));
00451     paint.drawLine(0, (fm.height() * range->viewLineCount()) - 1, xEnd - xStart, (fm.height() * range->viewLineCount()) - 1);
00452   }
00453 
00454   if (range->layout()) {
00455     QVector<QTextLayout::FormatRange> additionalFormats;
00456     if (range->length() > 0) {
00457       // We may have changed the pen, be absolutely sure it gets set back to
00458       // normal foreground color before drawing text for text that does not
00459       // set the pen color
00460       paint.setPen(attribute(KateExtendedAttribute::dsNormal)->foreground().color());
00461       // Draw the text :)
00462       if (m_dynamicRegion.boundingRange().isValid() || (m_view->selection() && showSelections() && m_view->selectionRange().overlapsLine(range->line()))) {
00463         // FIXME toVector() may be a performance issue
00464         additionalFormats = decorationsForLine(range->textLine(), range->line(), true).toVector();
00465         range->layout()->draw(&paint, QPoint(-xStart,0), additionalFormats);
00466 
00467       } else {
00468         range->layout()->draw(&paint, QPoint(-xStart,0));
00469       }
00470     }
00471 
00472     QBrush backgroundBrush;
00473     bool backgroundBrushSet = false;
00474 
00475     // Loop each individual line for additional text decoration etc.
00476     QListIterator<QTextLayout::FormatRange> it = range->layout()->additionalFormats();
00477     QVectorIterator<QTextLayout::FormatRange> it2 = additionalFormats;
00478     for (int i = 0; i < range->viewLineCount(); ++i) {
00479       KateTextLayout line = range->viewLine(i);
00480 
00481       // Determine the background to use, if any, for the end of this view line
00482       backgroundBrushSet = false;
00483       while (it2.hasNext()) {
00484         const QTextLayout::FormatRange& fr = it2.peekNext();
00485         if (fr.start > line.endCol())
00486           goto backgroundDetermined;
00487 
00488         if (fr.start + fr.length > line.endCol()) {
00489           if (fr.format.hasProperty(QTextFormat::BackgroundBrush)) {
00490             backgroundBrushSet = true;
00491             backgroundBrush = fr.format.background();
00492           }
00493 
00494           goto backgroundDetermined;
00495         }
00496 
00497         it2.next();
00498       }
00499 
00500       while (it.hasNext()) {
00501         const QTextLayout::FormatRange& fr = it.peekNext();
00502         if (fr.start > line.endCol())
00503           break;
00504 
00505         if (fr.start + fr.length > line.endCol()) {
00506           if (fr.format.hasProperty(QTextFormat::BackgroundBrush)) {
00507             backgroundBrushSet = true;
00508             backgroundBrush = fr.format.background();
00509           }
00510 
00511           break;
00512         }
00513 
00514         it.next();
00515       }
00516 
00517       backgroundDetermined:
00518 
00519       // Draw selection or background color outside of areas where text is rendered
00520       if (!m_printerFriendly ) {
00521         bool draw = false;
00522         QBrush drawBrush;
00523         if (m_view->selection() && !m_view->blockSelection() && m_view->lineEndSelected(line.end(true))) {
00524           draw = true;
00525           drawBrush = config()->selectionColor();
00526         } else if (backgroundBrushSet && !m_view->blockSelection()) {
00527           draw = true;
00528           drawBrush = backgroundBrush;
00529         }
00530 
00531         if (draw) {
00532           int fillStartX = line.endX() - line.startX() + line.xOffset() - xStart;
00533           int fillStartY = fm.height() * i;
00534           int width= xEnd - xStart - fillStartX;
00535           int height= fm.height();
00536 
00537           // reverse X for right-aligned lines
00538           if (range->layout()->textOption().alignment() == Qt::AlignRight)
00539             fillStartX = 0;
00540 
00541           QRect area(fillStartX, fillStartY, width, height);
00542           paint.fillRect(area, drawBrush);
00543         }
00544       }
00545       // Draw indent lines
00546       if (showIndentLines() && i == 0)
00547       {
00548         const int w = spaceWidth();
00549         const int lastIndentColumn = range->textLine()->indentDepth(m_tabWidth);
00550 
00551         for (int x = m_indentWidth; x < lastIndentColumn; x += m_indentWidth)
00552         {
00553           paintIndentMarker(paint, x * w + 1 - xStart, range->line());
00554         }
00555       }
00556 
00557       // Draw tab stops and trailing spaces
00558       if (showTabs() || showTrailingSpaces()) {
00559         const QString& text = range->textLine()->string();
00560         int y = fm.height() * i + fm.ascent() - fm.strikeOutPos();
00561 
00562         if (showTabs()) {
00563           int tabIndex = text.indexOf(tabChar, line.startCol());
00564           while (tabIndex != -1 && tabIndex < line.endCol()) {
00565             paintTabstop(paint, line.lineLayout().cursorToX(tabIndex) - xStart + spaceWidth()/2.0, y);
00566             tabIndex = text.indexOf(tabChar, tabIndex + 1);
00567           }
00568         }
00569 
00570         if (showTrailingSpaces()) {
00571           int spaceIndex = line.endCol() - 1;
00572           int trailingPos = range->textLine()->lastChar();
00573           if (trailingPos < 0)
00574             trailingPos = 0;
00575           if (spaceIndex >= trailingPos) {
00576             while (spaceIndex >= line.startCol() && text.at(spaceIndex).isSpace()) {
00577               if (text.at(spaceIndex) != '\t' || !showTabs())
00578                 paintTrailingSpace(paint, line.lineLayout().cursorToX(spaceIndex) - xStart + spaceWidth()/2.0, y);
00579               --spaceIndex;
00580             }
00581           }
00582         }
00583       }
00584     }
00585 
00586     // draw word-wrap-honor-indent filling
00587     if ( (range->viewLineCount() > 1)  && range->shiftX() && (range->shiftX() > xStart) )
00588     {
00589       if (backgroundBrushSet)
00590         paint.fillRect(0, fm.height(), range->shiftX() - xStart, fm.height() * (range->viewLineCount() - 1),
00591           backgroundBrush);
00592       paint.fillRect(0, fm.height(), range->shiftX() - xStart, fm.height() * (range->viewLineCount() - 1),
00593         QBrush(config()->wordWrapMarkerColor(), Qt::Dense4Pattern));
00594     }
00595 
00596     // Draw caret
00597     if (drawCaret() && cursor && range->includesCursor(*cursor)) {
00598       // Make the caret the desired width
00599       int caretWidth = 2;
00600       QTextLine line = range->layout()->lineForTextPosition(cursor->column());
00601       if (caretStyle() == Block) {
00602         if (line.isValid() && cursor->column() < range->length()) {
00603           caretWidth = int(line.cursorToX(cursor->column() + 1) - line.cursorToX(cursor->column()));
00604           if (caretWidth < 0)
00605             caretWidth = -caretWidth;
00606 
00607         } else {
00608           caretWidth = spaceWidth();
00609         }
00610       }
00611 
00612       QColor c;
00613       // Could actually use the real highlighting system for this... would be slower but more accurate for corner cases
00614       if (m_caretOverrideColor.isValid()) {
00615         c = m_caretOverrideColor;
00616 
00617       } else {
00618         foreach (const QTextLayout::FormatRange &r, range->layout()->additionalFormats())
00619           if ( (r.start <= cursor->column() ) && ( (r.start + r.length)  > cursor->column()) ) {
00620             c = r.format.foreground().color();
00621             break;
00622           }
00623         if (!c.isValid())
00624           if (range->layout()->additionalFormats().count())
00625             c = range->layout()->additionalFormats().last().format.foreground().color();
00626           else
00627             c = attribute(KateExtendedAttribute::dsNormal)->foreground().color();
00628       }
00629 
00630       if (cursor->column() <= range->length()) {
00631         paint.save();
00632         paint.setPen(QPen(c, caretWidth));
00633 
00634         // Clip the caret - Qt's caret has a habit of intruding onto other lines
00635         paint.setClipRect(0, line.lineNumber() * fm.height(), xEnd - xStart, fm.height());
00636 
00637         range->layout()->drawCursor(&paint, QPoint(-xStart,0), cursor->column(), caretWidth);
00638 
00639         paint.restore();
00640 
00641       } else {
00642         // Off the end of the line... must be block mode. Draw the caret ourselves.
00643         const KateTextLayout& lastLine = range->viewLine(range->viewLineCount() - 1);
00644         int x = range->widthOfLastLine() + spaceWidth() * (cursor->column() - range->length());
00645         if ( (x >= xStart) && (x <= xEnd))
00646           paint.fillRect(x, (int)lastLine.lineLayout().y(), caretWidth, fm.height(), c);
00647       }
00648     }
00649   }
00650 
00651   // show word wrap marker if desirable
00652   if ((!isPrinterFriendly()) && config()->wordWrapMarker() && QFontInfo(config()->font()).fixedPitch())
00653   {
00654     paint.setPen( config()->wordWrapMarkerColor() );
00655     int _x = m_doc->config()->wordWrapAt() * fm.width('x') - xStart;
00656     paint.drawLine( _x,0,_x,fm.height() );
00657   }
00658 }
00659 
00660 const QFont& KateRenderer::currentFont() const
00661 {
00662   return config()->font();
00663 }
00664 
00665 const QFontMetrics& KateRenderer::currentFontMetrics() const
00666 {
00667   return config()->fontMetrics();
00668 }
00669 
00670 uint KateRenderer::fontHeight()
00671 {
00672   return config()->fontMetrics().height();
00673 }
00674 
00675 uint KateRenderer::documentHeight()
00676 {
00677   return m_doc->lines() * fontHeight();
00678 }
00679 
00680 bool KateRenderer::getSelectionBounds(int line, int lineLength, int &start, int &end) const
00681 {
00682   bool hasSel = false;
00683 
00684   if (m_view->selection() && !m_view->blockSelectionMode())
00685   {
00686     if (m_view->lineIsSelection(line))
00687     {
00688       start = m_view->selectionRange().start().column();
00689       end = m_view->selectionRange().end().column();
00690       hasSel = true;
00691     }
00692     else if (line == m_view->selectionRange().start().line())
00693     {
00694       start = m_view->selectionRange().start().column();
00695       end = lineLength;
00696       hasSel = true;
00697     }
00698     else if (m_view->selectionRange().containsLine(line))
00699     {
00700       start = 0;
00701       end = lineLength;
00702       hasSel = true;
00703     }
00704     else if (line == m_view->selectionRange().end().line())
00705     {
00706       start = 0;
00707       end = m_view->selectionRange().end().column();
00708       hasSel = true;
00709     }
00710   }
00711   else if (m_view->lineHasSelected(line))
00712   {
00713     start = m_view->selectionRange().start().column();
00714     end = m_view->selectionRange().end().column();
00715     hasSel = true;
00716   }
00717 
00718   if (start > end) {
00719     int temp = end;
00720     end = start;
00721     start = temp;
00722   }
00723 
00724   return hasSel;
00725 }
00726 
00727 void KateRenderer::updateConfig ()
00728 {
00729   // update the attibute list pointer
00730   updateAttributes ();
00731 
00732   if (m_view)
00733     m_view->updateRendererConfig();
00734 }
00735 
00736 uint KateRenderer::spaceWidth() const
00737 {
00738   return config()->fontMetrics().width(spaceChar);
00739 }
00740 
00741 void KateRenderer::layoutLine(KateLineLayoutPtr lineLayout, int maxwidth, bool cacheLayout) const
00742 {
00743   // if maxwidth == -1 we have no wrap
00744 
00745   Q_ASSERT(lineLayout->textLine());
00746 
00747   QTextLayout* l = lineLayout->layout();
00748   if (!l) {
00749     l = new QTextLayout(lineLayout->textLine()->string(), config()->font());
00750   } else {
00751     l->setText(lineLayout->textLine()->string());
00752     l->setFont(config()->font());
00753   }
00754 
00755   l->setCacheEnabled(cacheLayout);
00756 
00757   // Initial setup of the QTextLayout.
00758 
00759   // Tab width
00760   QTextOption opt;
00761   opt.setFlags(QTextOption::IncludeTrailingSpaces);
00762   opt.setTabStop(m_tabWidth * config()->fontMetrics().width(spaceChar));
00763   opt.setWrapMode(QTextOption::WrapAtWordBoundaryOrAnywhere);
00764 
00765   // Find the first strong character in the string.
00766   // If it is an RTL character, set the base layout direction of the string to RTL.
00767   //
00768   // See http://www.unicode.org/reports/tr9/#The_Paragraph_Level (Sections P2 & P3).
00769   // Qt's text renderer ("scribe") version 4.2 assumes a "higher-level protocol"
00770   // (such as KatePart) will specify the paragraph level, so it does not apply P2 & P3
00771   // by itself. If this ever change in Qt, the next code block could be removed.
00772   if (isLineRightToLeft(lineLayout)) {
00773       opt.setAlignment( Qt::AlignRight );
00774       opt.setTextDirection( Qt::RightToLeft );
00775   }
00776   else {
00777       opt.setAlignment( Qt::AlignLeft );
00778       opt.setTextDirection( Qt::LeftToRight );
00779   }
00780 
00781   l->setTextOption(opt);
00782 
00783   // Syntax highlighting, inbuilt and arbitrary
00784   l->setAdditionalFormats(decorationsForLine(lineLayout->textLine(), lineLayout->line()));
00785 
00786   // Begin layouting
00787   l->beginLayout();
00788 
00789   int height = 0;
00790   int shiftX = 0;
00791 
00792   bool needShiftX = (maxwidth != -1)
00793                  && (m_view->config()->dynWordWrapAlignIndent() > 0);
00794 
00795   forever {
00796     QTextLine line = l->createLine();
00797     if (!line.isValid())
00798       break;
00799 
00800     if (maxwidth > 0)
00801       line.setLineWidth(maxwidth);
00802 
00803     line.setPosition(QPoint(line.lineNumber() ? shiftX : 0, height));
00804 
00805     if (needShiftX) {
00806       needShiftX = false;
00807       // Determine x offset for subsequent-lines-of-paragraph indenting
00808       int pos = lineLayout->textLine()->nextNonSpaceChar(0);
00809 
00810       if (pos > 0) {
00811         shiftX = (int)line.cursorToX(pos);
00812       }
00813 
00814       // check for too deep shift value and limit if necessary
00815       if (shiftX > ((double)maxwidth / 100 * m_view->config()->dynWordWrapAlignIndent()))
00816         shiftX = 0;
00817 
00818       // if shiftX > 0, the maxwidth has to adapted
00819       maxwidth -= shiftX;
00820 
00821       lineLayout->setShiftX(shiftX);
00822     }
00823 
00824     height += config()->fontMetrics().height();
00825   }
00826 
00827   l->endLayout();
00828 
00829   lineLayout->setLayout(l);
00830 }
00831 
00832 
00833 // 1) QString::isRightToLeft() sux
00834 // 2) QString::isRightToLeft() is marked as internal (WTF?)
00835 // 3) QString::isRightToLeft() does not seem to work on my setup
00836 // 4) isStringRightToLeft() should behave much better then QString::isRightToLeft() therefore:
00837 // 5) isStringRightToLeft() kicks ass
00838 bool KateRenderer::isLineRightToLeft( KateLineLayoutPtr lineLayout ) const
00839 {
00840   QString s = lineLayout->textLine()->string();
00841   int i = s.length();
00842   int RTLchars = 0;
00843   int LTRchars = 0;
00844 
00845   // borrowed from QString::updateProperties()
00846   while( i != 0 )
00847   {
00848     QChar c = s.at(i-1);
00849 
00850     switch(c.direction()) {
00851       case QChar::DirL:
00852       case QChar::DirLRO:
00853       case QChar::DirLRE:
00854           LTRchars ++;
00855           break;
00856 
00857       case QChar::DirR:
00858       case QChar::DirAL:
00859       case QChar::DirRLO:
00860       case QChar::DirRLE:
00861           RTLchars ++;
00862           break;
00863 
00864       default:
00865           break;
00866     }
00867     i --;
00868   }
00869 
00870   return (RTLchars > LTRchars);
00871 }
00872 
00873 int KateRenderer::cursorToX(const KateTextLayout& range, int col) const
00874 {
00875   return cursorToX(range, KTextEditor::Cursor(range.line(), col));
00876 }
00877 
00878 int KateRenderer::cursorToX(const KateTextLayout& range, const KTextEditor::Cursor & pos) const
00879 {
00880   Q_ASSERT(range.isValid());
00881 
00882   return (int)range.lineLayout().cursorToX(pos.column());
00883 }
00884 
00885 KTextEditor::Cursor KateRenderer::xToCursor(const KateTextLayout & range, int x, bool returnPastLine ) const
00886 {
00887   Q_ASSERT(range.isValid());
00888 
00889   int adjustedX = x;
00890 
00891   if (adjustedX <  range.xOffset())
00892     adjustedX = range.xOffset();
00893   else if (adjustedX > range.width() + range.xOffset())
00894     adjustedX = range.width() + range.xOffset();
00895 
00896   Q_ASSERT(range.isValid());
00897   KTextEditor::Cursor ret(range.line(), range.lineLayout().xToCursor(adjustedX));
00898 
00899   if (returnPastLine && x > range.width() + range.xOffset())
00900     ret.setColumn(ret.column() + ((x - (range.width() + range.xOffset())) / spaceWidth()));
00901 
00902   return ret;
00903 }
00904 
00905 void KateRenderer::setCaretOverrideColor(const QColor& color)
00906 {
00907   m_caretOverrideColor = color;
00908 }
00909 
00910 // 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
  • Kross
  • KUtils
  • Nepomuk
  • Solid
  • Sonnet
  • ThreadWeaver
Generated for kdelibs by doxygen 1.5.4
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