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