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