File indexing completed on 2024-04-21 03:57:32
0001 /* 0002 SPDX-FileCopyrightText: 2002-2010 Anders Lund <anders@alweb.dk> 0003 0004 Rewritten based on code of: 0005 SPDX-FileCopyrightText: 2002 Michael Goffioul <kdeprint@swing.be> 0006 0007 SPDX-License-Identifier: LGPL-2.0-or-later 0008 */ 0009 0010 #include "printpainter.h" 0011 0012 #include "katebuffer.h" 0013 #include "katedocument.h" 0014 #include "katehighlight.h" 0015 #include "katepartdebug.h" 0016 #include "katerenderer.h" 0017 #include "katetextfolding.h" 0018 #include "katetextlayout.h" 0019 #include "kateview.h" 0020 0021 #include <KLocalizedString> 0022 #include <KUser> 0023 0024 #include <QPainter> 0025 #include <QPrinter> 0026 0027 using namespace KatePrinter; 0028 0029 class KatePrinter::PageLayout 0030 { 0031 public: 0032 PageLayout() 0033 : headerTagList() 0034 , footerTagList() 0035 , selectionRange() 0036 { 0037 } 0038 0039 uint pageWidth = 0; 0040 uint pageHeight = 0; 0041 uint headerWidth = 0; 0042 uint maxWidth = 0; 0043 uint maxHeight = 0; 0044 int xstart = 0; // beginning point for painting lines 0045 int innerMargin = 0; 0046 0047 bool selectionOnly = false; 0048 0049 uint firstline = 0; 0050 uint lastline = 0; 0051 0052 // Header/Footer Page 0053 uint headerHeight = 0; 0054 QStringList headerTagList; 0055 uint footerHeight = 0; 0056 QStringList footerTagList; 0057 0058 KTextEditor::Range selectionRange; 0059 }; 0060 0061 PrintPainter::PrintPainter(KTextEditor::DocumentPrivate *doc, KTextEditor::ViewPrivate *view) 0062 : m_view(view) 0063 , m_doc(doc) 0064 , m_printGuide(false) 0065 , m_printLineNumbers(false) 0066 , m_useHeader(false) 0067 , m_useFooter(false) 0068 , m_useBackground(false) 0069 , m_useBox(false) 0070 , m_useHeaderBackground(false) 0071 , m_useFooterBackground(false) 0072 , m_boxMargin(0) 0073 , m_boxWidth(1) 0074 , m_boxColor(Qt::black) 0075 , m_headerBackground(Qt::lightGray) 0076 , m_headerForeground(Qt::black) 0077 , m_footerBackground(Qt::lightGray) 0078 , m_footerForeground(Qt::black) 0079 , m_fhFont() 0080 , m_headerFormat() 0081 , m_footerFormat() 0082 { 0083 m_renderer = new KateRenderer(m_doc, m_view->renderer()->folding(), m_view); 0084 m_renderer->setPrinterFriendly(true); 0085 0086 updateCache(); 0087 } 0088 0089 PrintPainter::~PrintPainter() 0090 { 0091 delete m_renderer; 0092 } 0093 0094 void PrintPainter::setTextFont(const QFont &font) 0095 { 0096 m_view->rendererConfig()->setFont(font); 0097 } 0098 0099 void PrintPainter::setUseBox(const bool on) 0100 { 0101 m_useBox = on; 0102 setBoxWidth(m_boxWidth); // reset the width 0103 } 0104 0105 void PrintPainter::setBoxWidth(const int width) 0106 { 0107 if (m_useBox) { 0108 m_boxWidth = width; 0109 if (width < 1) { 0110 m_boxWidth = 1; 0111 } 0112 } else { 0113 m_boxWidth = 0; 0114 } 0115 } 0116 0117 void PrintPainter::setBoxColor(const QColor &color) 0118 { 0119 if (color.isValid()) { 0120 m_boxColor = color; 0121 } 0122 } 0123 0124 void PrintPainter::setHeaderBackground(const QColor &color) 0125 { 0126 if (color.isValid()) { 0127 m_headerBackground = color; 0128 } 0129 } 0130 0131 void PrintPainter::setHeaderForeground(const QColor &color) 0132 { 0133 if (color.isValid()) { 0134 m_headerForeground = color; 0135 } 0136 } 0137 0138 void PrintPainter::setFooterBackground(const QColor &color) 0139 { 0140 if (color.isValid()) { 0141 m_footerBackground = color; 0142 } 0143 } 0144 void PrintPainter::setFooterForeground(const QColor &color) 0145 { 0146 if (color.isValid()) { 0147 m_footerForeground = color; 0148 } 0149 } 0150 0151 void PrintPainter::setColorScheme(const QString &scheme) 0152 { 0153 // directly set that for the renderer 0154 m_view->rendererConfig()->setSchema(scheme); 0155 0156 // changed renderer requires cache updates 0157 updateCache(); 0158 } 0159 0160 void PrintPainter::updateCache() 0161 { 0162 m_fontHeight = m_renderer->fontHeight(); 0163 0164 // figure out the horizontal space required 0165 QString s = QStringLiteral("%1 ").arg(m_doc->lines()); 0166 s.fill(QLatin1Char('5'), -1); // some non-fixed fonts haven't equally wide numbers 0167 // FIXME calculate which is actually the widest... 0168 m_lineNumberWidth = m_renderer->currentFontMetrics().boundingRect(s).width(); 0169 } 0170 0171 void PrintPainter::paint(QPrinter *printer) const 0172 { 0173 QPainter painter(printer); 0174 PageLayout pl; 0175 0176 configure(printer, pl); 0177 0178 uint lineCount = pl.firstline; 0179 uint y = 0; 0180 uint currentPage = (printer->fromPage() == 0) ? 1 : printer->fromPage(); 0181 bool pageStarted = true; 0182 uint remainder = 0; 0183 0184 auto &f = m_view->renderer()->folding(); 0185 0186 // On to draw something :-) 0187 while (lineCount <= pl.lastline) { 0188 if (y + m_fontHeight > pl.maxHeight) { 0189 if ((int)currentPage == printer->toPage()) { // we've reached the page break of last page to be printed 0190 break; 0191 } 0192 printer->newPage(); 0193 painter.resetTransform(); 0194 currentPage++; 0195 pageStarted = true; 0196 y = 0; 0197 } 0198 0199 if (pageStarted) { 0200 qCDebug(LOG_KTE) << "Starting new page," << lineCount << "lines up to now."; 0201 paintNewPage(painter, currentPage, y, pl); 0202 pageStarted = false; 0203 painter.translate(pl.xstart, y); 0204 } 0205 0206 const bool skipLine = m_dontPrintFoldedCode && !f.isLineVisible(lineCount); 0207 0208 if (!skipLine && m_printLineNumbers /*&& ! startCol*/) { // don't repeat! 0209 paintLineNumber(painter, lineCount, pl); 0210 } 0211 0212 if (!skipLine) { 0213 paintLine(painter, lineCount, y, remainder, pl); 0214 } 0215 0216 if (!remainder) { 0217 lineCount++; 0218 } 0219 } 0220 0221 painter.end(); 0222 } 0223 0224 void PrintPainter::configure(const QPrinter *printer, PageLayout &pl) const 0225 { 0226 pl.pageHeight = printer->height(); 0227 pl.pageWidth = printer->width(); 0228 pl.headerWidth = printer->width(); 0229 pl.innerMargin = m_useBox ? m_boxMargin : 6; 0230 pl.maxWidth = printer->width(); 0231 pl.maxHeight = (m_useBox ? printer->height() - pl.innerMargin : printer->height()); 0232 pl.selectionOnly = (printer->printRange() == QPrinter::Selection); 0233 pl.lastline = m_doc->lastLine(); 0234 0235 if (m_view && pl.selectionOnly) { 0236 // set a line range from the first selected line to the last 0237 pl.selectionRange = m_view->selectionRange(); 0238 pl.firstline = pl.selectionRange.start().line(); 0239 pl.lastline = pl.selectionRange.end().line(); 0240 } 0241 0242 if (m_printLineNumbers) { 0243 // a small space between the line numbers and the text 0244 int _adj = m_renderer->currentFontMetrics().horizontalAdvance(QStringLiteral("5")); 0245 // adjust available width and set horizontal start point for data 0246 pl.maxWidth -= m_lineNumberWidth + _adj; 0247 pl.xstart += m_lineNumberWidth + _adj; 0248 } 0249 0250 if (m_useHeader || m_useFooter) { 0251 // Set up a tag map 0252 // This retrieves all tags, used or not, but 0253 // none of these operations should be expensive, 0254 // and searching each tag in the format strings is avoided. 0255 QDateTime dt = QDateTime::currentDateTime(); 0256 std::map<QString, QString> tags; 0257 0258 KUser u(KUser::UseRealUserID); 0259 tags[QStringLiteral("u")] = u.loginName(); 0260 0261 tags[QStringLiteral("d")] = QLocale().toString(dt, QLocale::ShortFormat); 0262 tags[QStringLiteral("D")] = QLocale().toString(dt, QLocale::LongFormat); 0263 tags[QStringLiteral("h")] = QLocale().toString(dt.time(), QLocale::ShortFormat); 0264 tags[QStringLiteral("y")] = QLocale().toString(dt.date(), QLocale::ShortFormat); 0265 tags[QStringLiteral("Y")] = QLocale().toString(dt.date(), QLocale::LongFormat); 0266 tags[QStringLiteral("f")] = m_doc->url().fileName(); 0267 tags[QStringLiteral("U")] = m_doc->url().toString(); 0268 if (pl.selectionOnly) { 0269 QString s(i18n("(Selection of) ")); 0270 tags[QStringLiteral("f")].prepend(s); 0271 tags[QStringLiteral("U")].prepend(s); 0272 } 0273 0274 static const QRegularExpression reTags(QStringLiteral("%([dDfUhuyY])")); // TODO check for "%%<TAG>" 0275 0276 if (m_useHeader) { 0277 pl.headerHeight = QFontMetrics(m_fhFont).height(); 0278 if (m_useBox || m_useHeaderBackground) { 0279 pl.headerHeight += pl.innerMargin * 2; 0280 } else { 0281 pl.headerHeight += 1 + QFontMetrics(m_fhFont).leading(); 0282 } 0283 0284 pl.headerTagList = m_headerFormat; 0285 QMutableStringListIterator it(pl.headerTagList); 0286 while (it.hasNext()) { 0287 QString tag = it.next(); 0288 QRegularExpressionMatch match; 0289 int pos = tag.indexOf(reTags, 0, &match); 0290 QString rep; 0291 while (pos > -1) { 0292 rep = tags[match.captured(1)]; 0293 tag.replace((uint)pos, 2, rep); 0294 pos += rep.length(); 0295 pos = tag.indexOf(reTags, pos, &match); 0296 } 0297 it.setValue(tag); 0298 } 0299 } 0300 0301 if (m_useFooter) { 0302 pl.footerHeight = QFontMetrics(m_fhFont).height(); 0303 if (m_useBox || m_useFooterBackground) { 0304 pl.footerHeight += 2 * pl.innerMargin; 0305 } else { 0306 pl.footerHeight += 1; // line only 0307 } 0308 0309 pl.footerTagList = m_footerFormat; 0310 QMutableStringListIterator it(pl.footerTagList); 0311 while (it.hasNext()) { 0312 QString tag = it.next(); 0313 QRegularExpressionMatch match; 0314 int pos = tag.indexOf(reTags, 0, &match); 0315 QString rep; 0316 while (pos > -1) { 0317 rep = tags[match.captured(1)]; 0318 tag.replace((uint)pos, 2, rep); 0319 pos += rep.length(); 0320 pos = tag.indexOf(reTags, pos, &match); 0321 } 0322 it.setValue(tag); 0323 } 0324 0325 pl.maxHeight -= pl.footerHeight; 0326 } 0327 } // if ( useHeader || useFooter ) 0328 0329 if (m_useBackground) { 0330 if (!m_useBox) { 0331 pl.xstart += pl.innerMargin; 0332 pl.maxWidth -= pl.innerMargin * 2; 0333 } 0334 } 0335 0336 if (m_useBox) { 0337 // set maxwidth to something sensible 0338 pl.maxWidth -= (m_boxWidth + pl.innerMargin) * 2; 0339 pl.xstart += m_boxWidth + pl.innerMargin; 0340 // maxheight too.. 0341 pl.maxHeight -= m_boxWidth; 0342 } 0343 0344 int pageHeight = pl.maxHeight; 0345 if (m_useHeader) { 0346 pageHeight -= pl.headerHeight + pl.innerMargin; 0347 } 0348 if (m_useFooter) { 0349 pageHeight -= pl.footerHeight + pl.innerMargin; 0350 } 0351 0352 const int linesPerPage = pageHeight / m_fontHeight; 0353 0354 if (printer->fromPage() > 0) { 0355 pl.firstline = (printer->fromPage() - 1) * linesPerPage; 0356 } 0357 0358 // now that we know the vertical amount of space needed, 0359 // it is possible to calculate the total number of pages 0360 // if needed, that is if any header/footer tag contains "%P". 0361 if (!pl.headerTagList.filter(QStringLiteral("%P")).isEmpty() || !pl.footerTagList.filter(QStringLiteral("%P")).isEmpty()) { 0362 qCDebug(LOG_KTE) << "'%P' found! calculating number of pages..."; 0363 0364 // calculate total layouted lines in the document 0365 int totalLines = 0; 0366 // TODO: right now ignores selection printing 0367 for (unsigned int i = pl.firstline; i <= pl.lastline; ++i) { 0368 KateLineLayout rangeptr(*m_renderer); 0369 rangeptr.setLine(i); 0370 m_renderer->layoutLine(&rangeptr, (int)pl.maxWidth, false); 0371 totalLines += rangeptr.viewLineCount(); 0372 } 0373 0374 const int totalPages = (totalLines / linesPerPage) + ((totalLines % linesPerPage) > 0 ? 1 : 0); 0375 0376 // TODO: add space for guide if required 0377 // if ( useGuide ) 0378 // _lt += (guideHeight + (fontHeight /2)) / fontHeight; 0379 0380 // substitute both tag lists 0381 QString re(QStringLiteral("%P")); 0382 QStringList::Iterator it; 0383 0384 for (it = pl.headerTagList.begin(); it != pl.headerTagList.end(); ++it) { 0385 it->replace(re, QString::number(totalPages)); 0386 } 0387 0388 for (it = pl.footerTagList.begin(); it != pl.footerTagList.end(); ++it) { 0389 (*it).replace(re, QString::number(totalPages)); 0390 } 0391 } 0392 } 0393 0394 void PrintPainter::paintNewPage(QPainter &painter, const uint currentPage, uint &y, const PageLayout &pl) const 0395 { 0396 if (m_useHeader) { 0397 paintHeader(painter, currentPage, y, pl); 0398 } 0399 0400 if (m_useFooter) { 0401 paintFooter(painter, currentPage, pl); 0402 } 0403 0404 if (m_useBackground) { 0405 paintBackground(painter, y, pl); 0406 } 0407 0408 if (m_useBox) { 0409 paintBox(painter, y, pl); 0410 } 0411 0412 if (m_printGuide && currentPage == 1) { 0413 paintGuide(painter, y, pl); 0414 } 0415 } 0416 0417 void PrintPainter::paintHeader(QPainter &painter, const uint currentPage, uint &y, const PageLayout &pl) const 0418 { 0419 painter.save(); 0420 painter.setPen(QPen(m_headerForeground, 0.5)); 0421 painter.setFont(m_fhFont); 0422 0423 if (m_useHeaderBackground) { 0424 painter.fillRect(0, 0, pl.headerWidth, pl.headerHeight, m_headerBackground); 0425 } 0426 0427 if (pl.headerTagList.count() == 3) { 0428 int valign = (m_useBox || m_useHeaderBackground || m_useBackground) ? Qt::AlignVCenter : Qt::AlignTop; 0429 int align = valign | Qt::AlignLeft; 0430 int marg = (m_useBox || m_useHeaderBackground) ? pl.innerMargin : 0; 0431 if (m_useBox) { 0432 marg += m_boxWidth; 0433 } 0434 0435 QString s; 0436 for (int i = 0; i < 3; i++) { 0437 s = pl.headerTagList[i]; 0438 if (s.indexOf(QLatin1String("%p")) != -1) { 0439 s.replace(QLatin1String("%p"), QString::number(currentPage)); 0440 } 0441 0442 painter.drawText(marg, 0, pl.headerWidth - (marg * 2), pl.headerHeight, align, s); 0443 align = valign | (i == 0 ? Qt::AlignHCenter : Qt::AlignRight); 0444 } 0445 } 0446 0447 if (!(m_useHeaderBackground || m_useBox || m_useBackground)) { // draw a 1 px (!?) line to separate header from contents 0448 painter.drawLine(0, pl.headerHeight - 1, pl.headerWidth, pl.headerHeight - 1); 0449 // y += 1; now included in headerHeight 0450 } 0451 0452 painter.restore(); 0453 0454 y += pl.headerHeight + pl.innerMargin; 0455 } 0456 0457 void PrintPainter::paintFooter(QPainter &painter, const uint currentPage, const PageLayout &pl) const 0458 { 0459 painter.save(); 0460 painter.setPen(QPen(m_footerForeground, 0.5)); 0461 painter.setFont(m_fhFont); 0462 0463 if (!(m_useFooterBackground || m_useBox || m_useBackground)) { // draw a 1 px (!?) line to separate footer from contents 0464 painter.drawLine(0, pl.pageHeight - pl.footerHeight - 1, pl.headerWidth, pl.pageHeight - pl.footerHeight - 1); 0465 } 0466 if (m_useFooterBackground) { 0467 painter.fillRect(0, pl.pageHeight - pl.footerHeight, pl.headerWidth, pl.footerHeight, m_footerBackground); 0468 } 0469 0470 if (pl.footerTagList.count() == 3) { 0471 int align = Qt::AlignVCenter | Qt::AlignLeft; 0472 int marg = (m_useBox || m_useFooterBackground) ? pl.innerMargin : 0; 0473 if (m_useBox) { 0474 marg += m_boxWidth; 0475 } 0476 0477 QString s; 0478 for (int i = 0; i < 3; i++) { 0479 s = pl.footerTagList[i]; 0480 if (s.indexOf(QLatin1String("%p")) != -1) { 0481 s.replace(QLatin1String("%p"), QString::number(currentPage)); 0482 } 0483 painter.drawText(marg, pl.pageHeight - pl.footerHeight, pl.headerWidth - (marg * 2), pl.footerHeight, align, s); 0484 align = Qt::AlignVCenter | (i == 0 ? Qt::AlignHCenter : Qt::AlignRight); 0485 } 0486 } 0487 painter.restore(); 0488 } 0489 0490 void PrintPainter::paintGuide(QPainter &painter, uint &y, const PageLayout &pl) const 0491 { 0492 // FIXME - this may span more pages... 0493 // draw a box unless we have boxes, in which case we end with a box line 0494 int _ystart = y; 0495 QString _hlName = m_doc->highlight()->name(); 0496 0497 // list of highlight attributes for the legend 0498 const auto _attributes = m_doc->highlight()->attributesForDefinition(m_view->rendererConfig()->schema()); 0499 const QColor _defaultPen = _attributes.at(0)->foreground().color(); 0500 0501 painter.save(); 0502 painter.setPen(_defaultPen); 0503 0504 int _marg = 0; 0505 if (m_useBox) { 0506 _marg += (2 * m_boxWidth) + (2 * pl.innerMargin); 0507 } else { 0508 if (m_useBackground) { 0509 _marg += 2 * pl.innerMargin; 0510 } 0511 _marg += 1; 0512 y += 1 + pl.innerMargin; 0513 } 0514 0515 // draw a title string 0516 QFont _titleFont = m_renderer->currentFont(); 0517 _titleFont.setBold(true); 0518 painter.setFont(_titleFont); 0519 QRect _r; 0520 painter.drawText(QRect(_marg, y, pl.pageWidth - (2 * _marg), pl.maxHeight - y), 0521 Qt::AlignTop | Qt::AlignHCenter, 0522 i18n("Typographical Conventions for %1", _hlName), 0523 &_r); 0524 const int _w = pl.pageWidth - (_marg * 2) - (pl.innerMargin * 2); 0525 const int _x = _marg + pl.innerMargin; 0526 y += _r.height() + pl.innerMargin; 0527 painter.drawLine(_x, y, _x + _w, y); 0528 y += 1 + pl.innerMargin; 0529 0530 int _widest(0); 0531 for (const KTextEditor::Attribute::Ptr &attribute : std::as_const(_attributes)) { 0532 const QString _name = attribute->name().section(QLatin1Char(':'), 1, 1); 0533 _widest = qMax(QFontMetrics(attribute->font()).boundingRect(_name).width(), _widest); 0534 } 0535 0536 const int _guideCols = _w / (_widest + pl.innerMargin); 0537 0538 // draw attrib names using their styles 0539 const int _cw = _w / _guideCols; 0540 int _i = 0; 0541 0542 _titleFont.setUnderline(true); 0543 QString _currentHlName; 0544 for (const KTextEditor::Attribute::Ptr &attribute : std::as_const(_attributes)) { 0545 QString _hl = attribute->name().section(QLatin1Char(':'), 0, 0); 0546 QString _name = attribute->name().section(QLatin1Char(':'), 1, 1); 0547 if (_hl != _hlName && _hl != _currentHlName) { 0548 _currentHlName = _hl; 0549 if (_i % _guideCols) { 0550 y += m_fontHeight; 0551 } 0552 y += pl.innerMargin; 0553 painter.setFont(_titleFont); 0554 painter.setPen(_defaultPen); 0555 painter.drawText(_x, y, _w, m_fontHeight, Qt::AlignTop, _hl + QLatin1Char(' ') + i18n("text")); 0556 y += m_fontHeight; 0557 _i = 0; 0558 } 0559 0560 painter.setPen(attribute->foreground().color()); 0561 painter.setFont(attribute->font()); 0562 0563 if (attribute->hasProperty(QTextFormat::BackgroundBrush)) { 0564 QRect _rect = QFontMetrics(attribute->font()).boundingRect(_name); 0565 _rect.moveTo(_x + ((_i % _guideCols) * _cw), y); 0566 painter.fillRect(_rect, attribute->background()); 0567 } 0568 0569 painter.drawText((_x + ((_i % _guideCols) * _cw)), y, _cw, m_fontHeight, Qt::AlignTop, _name); 0570 0571 _i++; 0572 if (_i && !(_i % _guideCols)) { 0573 y += m_fontHeight; 0574 } 0575 } 0576 0577 if (_i % _guideCols) { 0578 y += m_fontHeight; // last row not full 0579 } 0580 // draw a box around the legend 0581 painter.setPen(_defaultPen); 0582 0583 if (m_useBox) { 0584 painter.fillRect(0, y + pl.innerMargin, pl.headerWidth, m_boxWidth, m_boxColor); 0585 } else { 0586 _marg -= 1; 0587 painter.drawRect(_marg, _ystart, pl.pageWidth - (2 * _marg), y - _ystart + pl.innerMargin); 0588 } 0589 0590 painter.restore(); 0591 0592 y += (m_useBox ? m_boxWidth : 1) + (pl.innerMargin * 2); 0593 } 0594 0595 void PrintPainter::paintBox(QPainter &painter, uint &y, const PageLayout &pl) const 0596 { 0597 painter.save(); 0598 painter.setPen(QPen(m_boxColor, m_boxWidth)); 0599 painter.drawRect(0, 0, pl.pageWidth, pl.pageHeight); 0600 0601 if (m_useHeader) { 0602 painter.drawLine(0, pl.headerHeight, pl.headerWidth, pl.headerHeight); 0603 } else { 0604 y += pl.innerMargin; 0605 } 0606 0607 if (m_useFooter) { // drawline is not trustable, grr. 0608 painter.fillRect(0, pl.maxHeight + pl.innerMargin, pl.headerWidth, m_boxWidth, m_boxColor); 0609 } 0610 0611 painter.restore(); 0612 } 0613 0614 void PrintPainter::paintBackground(QPainter &painter, const uint y, const PageLayout &pl) const 0615 { 0616 // If we have a box, or the header/footer has backgrounds, we want to paint 0617 // to the border of those. Otherwise just the contents area. 0618 int _y = y; 0619 int _h = pl.maxHeight - y; 0620 if (m_useBox) { 0621 _y -= pl.innerMargin; 0622 _h += 2 * pl.innerMargin; 0623 } else { 0624 if (m_useHeaderBackground) { 0625 _y -= pl.innerMargin; 0626 _h += pl.innerMargin; 0627 } 0628 if (m_useFooterBackground) { 0629 _h += pl.innerMargin; 0630 } 0631 } 0632 painter.fillRect(0, _y, pl.pageWidth, _h, m_view->rendererConfig()->backgroundColor()); 0633 } 0634 0635 void PrintPainter::paintLine(QPainter &painter, const uint line, uint &y, uint &remainder, const PageLayout &pl) const 0636 { 0637 // HA! this is where we print [part of] a line ;]] 0638 KateLineLayout rangeptr(*m_renderer); 0639 rangeptr.setLine(line); 0640 m_renderer->layoutLine(&rangeptr, (int)pl.maxWidth, false); 0641 0642 // selectionOnly: clip non-selection parts and adjust painter position if needed 0643 int _xadjust = 0; 0644 if (pl.selectionOnly) { 0645 if (m_view && m_view->blockSelection()) { 0646 int _x = m_renderer->cursorToX(rangeptr.viewLine(0), pl.selectionRange.start()); 0647 int _x1 = m_renderer->cursorToX(rangeptr.viewLine(rangeptr.viewLineCount() - 1), pl.selectionRange.end()); 0648 _xadjust = _x; 0649 painter.translate(-_xadjust, 0); 0650 painter.setClipRegion(QRegion(_x, 0, _x1 - _x, rangeptr.viewLineCount() * m_fontHeight)); 0651 0652 } else if (line == pl.firstline || line == pl.lastline) { 0653 QRegion region(0, 0, pl.maxWidth, rangeptr.viewLineCount() * m_fontHeight); 0654 0655 if (line == pl.firstline) { 0656 region = region.subtracted(QRegion(0, 0, m_renderer->cursorToX(rangeptr.viewLine(0), pl.selectionRange.start()), m_fontHeight)); 0657 } 0658 0659 if (line == pl.lastline) { 0660 int _x = m_renderer->cursorToX(rangeptr.viewLine(rangeptr.viewLineCount() - 1), pl.selectionRange.end()); 0661 region = region.subtracted(QRegion(_x, 0, pl.maxWidth - _x, m_fontHeight)); 0662 } 0663 0664 painter.setClipRegion(region); 0665 } 0666 } 0667 0668 // If the line is too long (too many 'viewlines') to fit the remaining vertical space, 0669 // clip and adjust the painter position as necessary 0670 int _lines = rangeptr.viewLineCount(); // number of "sublines" to paint. 0671 0672 int proceedLines = _lines; 0673 if (remainder) { 0674 proceedLines = qMin((pl.maxHeight - y) / m_fontHeight, remainder); 0675 0676 painter.translate(0, -(_lines - int(remainder)) * m_fontHeight + 1); 0677 painter.setClipRect(0, 0678 (_lines - int(remainder)) * m_fontHeight + 1, 0679 pl.maxWidth, 0680 proceedLines * m_fontHeight); // ### drop the crosspatch in printerfriendly mode??? 0681 remainder -= proceedLines; 0682 } else if (y + m_fontHeight * _lines > pl.maxHeight) { 0683 remainder = _lines - ((pl.maxHeight - y) / m_fontHeight); 0684 painter.setClipRect(0, 0, pl.maxWidth, (_lines - int(remainder)) * m_fontHeight + 1); // ### drop the crosspatch in printerfriendly mode??? 0685 } else if (!pl.selectionOnly) { 0686 painter.setClipRegion(QRegion()); 0687 painter.setClipping(false); 0688 } 0689 0690 KateRenderer::PaintTextLineFlags flags; 0691 if (!m_dontPrintFoldedCode) { 0692 flags.setFlag(KateRenderer::PaintTextLineFlag::SkipDrawFirstInvisibleLineUnderlined); 0693 } 0694 0695 m_renderer->paintTextLine(painter, &rangeptr, 0, (int)pl.maxWidth, QRectF{}, nullptr, flags); 0696 0697 painter.setClipping(false); 0698 painter.translate(_xadjust, (m_fontHeight * (_lines - remainder))); 0699 0700 y += m_fontHeight * proceedLines; 0701 } 0702 0703 void PrintPainter::paintLineNumber(QPainter &painter, const uint number, const PageLayout &pl) const 0704 { 0705 const int left = ((m_useBox || m_useBackground) ? pl.innerMargin : 0) - pl.xstart; 0706 0707 painter.save(); 0708 painter.setFont(m_renderer->currentFont()); 0709 painter.setPen(m_view->rendererConfig()->lineNumberColor()); 0710 painter.drawText(left, 0, m_lineNumberWidth, m_fontHeight, Qt::AlignRight | Qt::AlignVCenter, QString::number(number + 1)); 0711 painter.restore(); 0712 } 0713 0714 // END PrintPainter