KTextEditor

printpainter.cpp
1/*
2 SPDX-FileCopyrightText: 2002-2010 Anders Lund <anders@alweb.dk>
3
4 Rewritten based on code of:
5 SPDX-FileCopyrightText: 2002 Michael Goffioul <kdeprint@swing.be>
6
7 SPDX-License-Identifier: LGPL-2.0-or-later
8*/
9
10#include "printpainter.h"
11
12#include "katebuffer.h"
13#include "katedocument.h"
14#include "katehighlight.h"
15#include "katepartdebug.h"
16#include "katerenderer.h"
17#include "katetextfolding.h"
18#include "katetextlayout.h"
19#include "kateview.h"
20
21#include <KLocalizedString>
22#include <KUser>
23
24#include <QPainter>
25#include <QPrinter>
26
27using namespace KatePrinter;
28
29class KatePrinter::PageLayout
30{
31public:
32 PageLayout()
33 : headerTagList()
34 , footerTagList()
35 , selectionRange()
36 {
37 }
38
39 uint pageWidth = 0;
40 uint pageHeight = 0;
41 uint headerWidth = 0;
42 uint maxWidth = 0;
43 uint maxHeight = 0;
44 int xstart = 0; // beginning point for painting lines
45 int innerMargin = 0;
46
47 bool selectionOnly = false;
48
49 uint firstline = 0;
50 uint lastline = 0;
51
52 // Header/Footer Page
53 uint headerHeight = 0;
54 QStringList headerTagList;
55 uint footerHeight = 0;
56 QStringList footerTagList;
57
58 KTextEditor::Range selectionRange;
59};
60
61PrintPainter::PrintPainter(KTextEditor::DocumentPrivate *doc, KTextEditor::ViewPrivate *view)
62 : m_view(view)
63 , m_doc(doc)
64 , m_printGuide(false)
65 , m_printLineNumbers(false)
66 , m_useHeader(false)
67 , m_useFooter(false)
68 , m_useBackground(false)
69 , m_useBox(false)
70 , m_useHeaderBackground(false)
71 , m_useFooterBackground(false)
72 , m_boxMargin(0)
73 , m_boxWidth(1)
74 , m_boxColor(Qt::black)
75 , m_headerBackground(Qt::lightGray)
76 , m_headerForeground(Qt::black)
77 , m_footerBackground(Qt::lightGray)
78 , m_footerForeground(Qt::black)
79 , m_fhFont()
80 , m_headerFormat()
81 , m_footerFormat()
82{
83 m_renderer = new KateRenderer(doc, view->renderer()->folding(), view);
84 m_renderer->setPrinterFriendly(true);
85
86 updateCache();
87}
88
89PrintPainter::~PrintPainter()
90{
91 delete m_renderer;
92}
93
94void PrintPainter::setTextFont(const QFont &font)
95{
96 m_renderer->config()->setFont(font);
97}
98
99void PrintPainter::setUseBox(const bool on)
100{
101 m_useBox = on;
102 setBoxWidth(m_boxWidth); // reset the width
103}
104
105void PrintPainter::setBoxWidth(const int width)
106{
107 if (m_useBox) {
108 m_boxWidth = width;
109 if (width < 1) {
110 m_boxWidth = 1;
111 }
112 } else {
113 m_boxWidth = 0;
114 }
115}
116
117void PrintPainter::setBoxColor(const QColor &color)
118{
119 if (color.isValid()) {
120 m_boxColor = color;
121 }
122}
123
124void PrintPainter::setHeaderBackground(const QColor &color)
125{
126 if (color.isValid()) {
127 m_headerBackground = color;
128 }
129}
130
131void PrintPainter::setHeaderForeground(const QColor &color)
132{
133 if (color.isValid()) {
134 m_headerForeground = color;
135 }
136}
137
138void PrintPainter::setFooterBackground(const QColor &color)
139{
140 if (color.isValid()) {
141 m_footerBackground = color;
142 }
143}
144void PrintPainter::setFooterForeground(const QColor &color)
145{
146 if (color.isValid()) {
147 m_footerForeground = color;
148 }
149}
150
151void PrintPainter::setColorScheme(const QString &scheme)
152{
153 // directly set that for the renderer
154 m_renderer->config()->setSchema(scheme);
155
156 // changed renderer requires cache updates
157 updateCache();
158}
159
160void PrintPainter::updateCache()
161{
162 m_fontHeight = m_renderer->fontHeight();
163
164 // figure out the horizontal space required
165 QString s = QStringLiteral("%1 ").arg(m_doc->lines());
166 s.fill(QLatin1Char('5'), -1); // some non-fixed fonts haven't equally wide numbers
167 // FIXME calculate which is actually the widest...
168 m_lineNumberWidth = m_renderer->currentFontMetrics().boundingRect(s).width();
169}
170
171void PrintPainter::paint(QPrinter *printer) const
172{
173 QPainter painter(printer);
174 PageLayout pl;
175
176 configure(printer, pl);
177
178 uint lineCount = pl.firstline;
179 uint y = 0;
180 uint currentPage = (printer->fromPage() == 0) ? 1 : printer->fromPage();
181 bool pageStarted = true;
182 uint remainder = 0;
183
184 auto &f = m_renderer->folding();
185
186 // On to draw something :-)
187 while (lineCount <= pl.lastline) {
188 if (y + m_fontHeight > pl.maxHeight) {
189 if ((int)currentPage == printer->toPage()) { // we've reached the page break of last page to be printed
190 break;
191 }
192 printer->newPage();
193 painter.resetTransform();
194 currentPage++;
195 pageStarted = true;
196 y = 0;
197 }
198
199 if (pageStarted) {
200 qCDebug(LOG_KTE) << "Starting new page," << lineCount << "lines up to now.";
201 paintNewPage(painter, currentPage, y, pl);
202 pageStarted = false;
203 painter.translate(pl.xstart, y);
204 }
205
206 const bool skipLine = m_dontPrintFoldedCode && !f.isLineVisible(lineCount);
207
208 if (!skipLine && m_printLineNumbers /*&& ! startCol*/) { // don't repeat!
209 paintLineNumber(painter, lineCount, pl);
210 }
211
212 if (!skipLine) {
213 paintLine(painter, lineCount, y, remainder, pl);
214 }
215
216 if (!remainder) {
217 lineCount++;
218 }
219 }
220
221 painter.end();
222}
223
224void PrintPainter::configure(const QPrinter *printer, PageLayout &pl) const
225{
226 pl.pageHeight = printer->height();
227 pl.pageWidth = printer->width();
228 pl.headerWidth = printer->width();
229 pl.innerMargin = m_useBox ? m_boxMargin : 6;
230 pl.maxWidth = printer->width();
231 pl.maxHeight = (m_useBox ? printer->height() - pl.innerMargin : printer->height());
232 pl.selectionOnly = (printer->printRange() == QPrinter::Selection);
233 pl.lastline = m_doc->lastLine();
234
235 if (m_view && pl.selectionOnly) {
236 // set a line range from the first selected line to the last
237 pl.selectionRange = m_view->selectionRange();
238 pl.firstline = pl.selectionRange.start().line();
239 pl.lastline = pl.selectionRange.end().line();
240 }
241
242 if (m_printLineNumbers) {
243 // a small space between the line numbers and the text
244 int _adj = m_renderer->currentFontMetrics().horizontalAdvance(QStringLiteral("5"));
245 // adjust available width and set horizontal start point for data
246 pl.maxWidth -= m_lineNumberWidth + _adj;
247 pl.xstart += m_lineNumberWidth + _adj;
248 }
249
250 if (m_useHeader || m_useFooter) {
251 // Set up a tag map
252 // This retrieves all tags, used or not, but
253 // none of these operations should be expensive,
254 // and searching each tag in the format strings is avoided.
255 QDateTime dt = QDateTime::currentDateTime();
256 std::map<QString, QString> tags;
257
258 KUser u(KUser::UseRealUserID);
259 tags[QStringLiteral("u")] = u.loginName();
260
261 tags[QStringLiteral("d")] = QLocale().toString(dt, QLocale::ShortFormat);
262 tags[QStringLiteral("D")] = QLocale().toString(dt, QLocale::LongFormat);
263 tags[QStringLiteral("h")] = QLocale().toString(dt.time(), QLocale::ShortFormat);
264 tags[QStringLiteral("y")] = QLocale().toString(dt.date(), QLocale::ShortFormat);
265 tags[QStringLiteral("Y")] = QLocale().toString(dt.date(), QLocale::LongFormat);
266 tags[QStringLiteral("f")] = m_doc->url().fileName();
267 tags[QStringLiteral("U")] = m_doc->url().toString();
268 if (pl.selectionOnly) {
269 QString s(i18n("(Selection of) "));
270 tags[QStringLiteral("f")].prepend(s);
271 tags[QStringLiteral("U")].prepend(s);
272 }
273
274 static const QRegularExpression reTags(QStringLiteral("%([dDfUhuyY])")); // TODO check for "%%<TAG>"
275
276 if (m_useHeader) {
277 pl.headerHeight = QFontMetrics(m_fhFont).height();
278 if (m_useBox || m_useHeaderBackground) {
279 pl.headerHeight += pl.innerMargin * 2;
280 } else {
281 pl.headerHeight += 1 + QFontMetrics(m_fhFont).leading();
282 }
283
284 pl.headerTagList = m_headerFormat;
285 QMutableStringListIterator it(pl.headerTagList);
286 while (it.hasNext()) {
287 QString tag = it.next();
288 QRegularExpressionMatch match;
289 int pos = tag.indexOf(reTags, 0, &match);
290 QString rep;
291 while (pos > -1) {
292 rep = tags[match.captured(1)];
293 tag.replace((uint)pos, 2, rep);
294 pos += rep.length();
295 pos = tag.indexOf(reTags, pos, &match);
296 }
297 it.setValue(tag);
298 }
299 }
300
301 if (m_useFooter) {
302 pl.footerHeight = QFontMetrics(m_fhFont).height();
303 if (m_useBox || m_useFooterBackground) {
304 pl.footerHeight += 2 * pl.innerMargin;
305 } else {
306 pl.footerHeight += 1; // line only
307 }
308
309 pl.footerTagList = m_footerFormat;
310 QMutableStringListIterator it(pl.footerTagList);
311 while (it.hasNext()) {
312 QString tag = it.next();
313 QRegularExpressionMatch match;
314 int pos = tag.indexOf(reTags, 0, &match);
315 QString rep;
316 while (pos > -1) {
317 rep = tags[match.captured(1)];
318 tag.replace((uint)pos, 2, rep);
319 pos += rep.length();
320 pos = tag.indexOf(reTags, pos, &match);
321 }
322 it.setValue(tag);
323 }
324
325 pl.maxHeight -= pl.footerHeight;
326 }
327 } // if ( useHeader || useFooter )
328
329 if (m_useBackground) {
330 if (!m_useBox) {
331 pl.xstart += pl.innerMargin;
332 pl.maxWidth -= pl.innerMargin * 2;
333 }
334 }
335
336 if (m_useBox) {
337 // set maxwidth to something sensible
338 pl.maxWidth -= (m_boxWidth + pl.innerMargin) * 2;
339 pl.xstart += m_boxWidth + pl.innerMargin;
340 // maxheight too..
341 pl.maxHeight -= m_boxWidth;
342 }
343
344 int pageHeight = pl.maxHeight;
345 if (m_useHeader) {
346 pageHeight -= pl.headerHeight + pl.innerMargin;
347 }
348 if (m_useFooter) {
349 pageHeight -= pl.footerHeight + pl.innerMargin;
350 }
351
352 const int linesPerPage = pageHeight / m_fontHeight;
353
354 if (printer->fromPage() > 0) {
355 pl.firstline = (printer->fromPage() - 1) * linesPerPage;
356 }
357
358 // now that we know the vertical amount of space needed,
359 // it is possible to calculate the total number of pages
360 // if needed, that is if any header/footer tag contains "%P".
361 if (!pl.headerTagList.filter(QStringLiteral("%P")).isEmpty() || !pl.footerTagList.filter(QStringLiteral("%P")).isEmpty()) {
362 qCDebug(LOG_KTE) << "'%P' found! calculating number of pages...";
363
364 // calculate total layouted lines in the document
365 int totalLines = 0;
366 // TODO: right now ignores selection printing
367 for (unsigned int i = pl.firstline; i <= pl.lastline; ++i) {
368 KateLineLayout rangeptr(*m_renderer);
369 rangeptr.setLine(i);
370 m_renderer->layoutLine(&rangeptr, (int)pl.maxWidth, false);
371 totalLines += rangeptr.viewLineCount();
372 }
373
374 const int totalPages = (totalLines / linesPerPage) + ((totalLines % linesPerPage) > 0 ? 1 : 0);
375
376 // TODO: add space for guide if required
377 // if ( useGuide )
378 // _lt += (guideHeight + (fontHeight /2)) / fontHeight;
379
380 // substitute both tag lists
381 QString re(QStringLiteral("%P"));
383
384 for (it = pl.headerTagList.begin(); it != pl.headerTagList.end(); ++it) {
385 it->replace(re, QString::number(totalPages));
386 }
387
388 for (it = pl.footerTagList.begin(); it != pl.footerTagList.end(); ++it) {
389 (*it).replace(re, QString::number(totalPages));
390 }
391 }
392}
393
394void PrintPainter::paintNewPage(QPainter &painter, const uint currentPage, uint &y, const PageLayout &pl) const
395{
396 if (m_useHeader) {
397 paintHeader(painter, currentPage, y, pl);
398 }
399
400 if (m_useFooter) {
401 paintFooter(painter, currentPage, pl);
402 }
403
404 if (m_useBackground) {
405 paintBackground(painter, y, pl);
406 }
407
408 if (m_useBox) {
409 paintBox(painter, y, pl);
410 }
411
412 if (m_printGuide && currentPage == 1) {
413 paintGuide(painter, y, pl);
414 }
415}
416
417void PrintPainter::paintHeader(QPainter &painter, const uint currentPage, uint &y, const PageLayout &pl) const
418{
419 painter.save();
420 painter.setPen(QPen(m_headerForeground, 0.5));
421 painter.setFont(m_fhFont);
422
423 if (m_useHeaderBackground) {
424 painter.fillRect(0, 0, pl.headerWidth, pl.headerHeight, m_headerBackground);
425 }
426
427 if (pl.headerTagList.count() == 3) {
428 int valign = (m_useBox || m_useHeaderBackground || m_useBackground) ? Qt::AlignVCenter : Qt::AlignTop;
429 int align = valign | Qt::AlignLeft;
430 int marg = (m_useBox || m_useHeaderBackground) ? pl.innerMargin : 0;
431 if (m_useBox) {
432 marg += m_boxWidth;
433 }
434
435 QString s;
436 for (int i = 0; i < 3; i++) {
437 s = pl.headerTagList[i];
438 if (s.indexOf(QLatin1String("%p")) != -1) {
439 s.replace(QLatin1String("%p"), QString::number(currentPage));
440 }
441
442 painter.drawText(marg, 0, pl.headerWidth - (marg * 2), pl.headerHeight, align, s);
443 align = valign | (i == 0 ? Qt::AlignHCenter : Qt::AlignRight);
444 }
445 }
446
447 if (!(m_useHeaderBackground || m_useBox || m_useBackground)) { // draw a 1 px (!?) line to separate header from contents
448 painter.drawLine(0, pl.headerHeight - 1, pl.headerWidth, pl.headerHeight - 1);
449 // y += 1; now included in headerHeight
450 }
451
452 painter.restore();
453
454 y += pl.headerHeight + pl.innerMargin;
455}
456
457void PrintPainter::paintFooter(QPainter &painter, const uint currentPage, const PageLayout &pl) const
458{
459 painter.save();
460 painter.setPen(QPen(m_footerForeground, 0.5));
461 painter.setFont(m_fhFont);
462
463 if (!(m_useFooterBackground || m_useBox || m_useBackground)) { // draw a 1 px (!?) line to separate footer from contents
464 painter.drawLine(0, pl.pageHeight - pl.footerHeight - 1, pl.headerWidth, pl.pageHeight - pl.footerHeight - 1);
465 }
466 if (m_useFooterBackground) {
467 painter.fillRect(0, pl.pageHeight - pl.footerHeight, pl.headerWidth, pl.footerHeight, m_footerBackground);
468 }
469
470 if (pl.footerTagList.count() == 3) {
471 int align = Qt::AlignVCenter | Qt::AlignLeft;
472 int marg = (m_useBox || m_useFooterBackground) ? pl.innerMargin : 0;
473 if (m_useBox) {
474 marg += m_boxWidth;
475 }
476
477 QString s;
478 for (int i = 0; i < 3; i++) {
479 s = pl.footerTagList[i];
480 if (s.indexOf(QLatin1String("%p")) != -1) {
481 s.replace(QLatin1String("%p"), QString::number(currentPage));
482 }
483 painter.drawText(marg, pl.pageHeight - pl.footerHeight, pl.headerWidth - (marg * 2), pl.footerHeight, align, s);
484 align = Qt::AlignVCenter | (i == 0 ? Qt::AlignHCenter : Qt::AlignRight);
485 }
486 }
487 painter.restore();
488}
489
490void PrintPainter::paintGuide(QPainter &painter, uint &y, const PageLayout &pl) const
491{
492 // FIXME - this may span more pages...
493 // draw a box unless we have boxes, in which case we end with a box line
494 int _ystart = y;
495 QString _hlName = m_doc->highlight()->name();
496
497 // list of highlight attributes for the legend
498 const auto _attributes = m_doc->highlight()->attributesForDefinition(m_renderer->config()->schema());
499 const QColor _defaultPen = _attributes.at(0)->foreground().color();
500
501 painter.save();
502 painter.setPen(_defaultPen);
503
504 int _marg = 0;
505 if (m_useBox) {
506 _marg += (2 * m_boxWidth) + (2 * pl.innerMargin);
507 } else {
508 if (m_useBackground) {
509 _marg += 2 * pl.innerMargin;
510 }
511 _marg += 1;
512 y += 1 + pl.innerMargin;
513 }
514
515 // draw a title string
516 QFont _titleFont = m_renderer->currentFont();
517 _titleFont.setBold(true);
518 painter.setFont(_titleFont);
519 QRect _r;
520 painter.drawText(QRect(_marg, y, pl.pageWidth - (2 * _marg), pl.maxHeight - y),
522 i18n("Typographical Conventions for %1", _hlName),
523 &_r);
524 const int _w = pl.pageWidth - (_marg * 2) - (pl.innerMargin * 2);
525 const int _x = _marg + pl.innerMargin;
526 y += _r.height() + pl.innerMargin;
527 painter.drawLine(_x, y, _x + _w, y);
528 y += 1 + pl.innerMargin;
529
530 int _widest(0);
531 for (const KTextEditor::Attribute::Ptr &attribute : std::as_const(_attributes)) {
532 const QString _name = attribute->name().section(QLatin1Char(':'), 1, 1);
533 _widest = qMax(QFontMetrics(attribute->font()).boundingRect(_name).width(), _widest);
534 }
535
536 const int _guideCols = _w / (_widest + pl.innerMargin);
537
538 // draw attrib names using their styles
539 const int _cw = _w / _guideCols;
540 int _i = 0;
541
542 _titleFont.setUnderline(true);
543 QString _currentHlName;
544 for (const KTextEditor::Attribute::Ptr &attribute : std::as_const(_attributes)) {
545 QString _hl = attribute->name().section(QLatin1Char(':'), 0, 0);
546 QString _name = attribute->name().section(QLatin1Char(':'), 1, 1);
547 if (_hl != _hlName && _hl != _currentHlName) {
548 _currentHlName = _hl;
549 if (_i % _guideCols) {
550 y += m_fontHeight;
551 }
552 y += pl.innerMargin;
553 painter.setFont(_titleFont);
554 painter.setPen(_defaultPen);
555 painter.drawText(_x, y, _w, m_fontHeight, Qt::AlignTop, _hl + QLatin1Char(' ') + i18n("text"));
556 y += m_fontHeight;
557 _i = 0;
558 }
559
560 painter.setPen(attribute->foreground().color());
561 painter.setFont(attribute->font());
562
563 if (attribute->hasProperty(QTextFormat::BackgroundBrush)) {
564 QRect _rect = QFontMetrics(attribute->font()).boundingRect(_name);
565 _rect.moveTo(_x + ((_i % _guideCols) * _cw), y);
566 painter.fillRect(_rect, attribute->background());
567 }
568
569 painter.drawText((_x + ((_i % _guideCols) * _cw)), y, _cw, m_fontHeight, Qt::AlignTop, _name);
570
571 _i++;
572 if (_i && !(_i % _guideCols)) {
573 y += m_fontHeight;
574 }
575 }
576
577 if (_i % _guideCols) {
578 y += m_fontHeight; // last row not full
579 }
580 // draw a box around the legend
581 painter.setPen(_defaultPen);
582
583 if (m_useBox) {
584 painter.fillRect(0, y + pl.innerMargin, pl.headerWidth, m_boxWidth, m_boxColor);
585 } else {
586 _marg -= 1;
587 painter.drawRect(_marg, _ystart, pl.pageWidth - (2 * _marg), y - _ystart + pl.innerMargin);
588 }
589
590 painter.restore();
591
592 y += (m_useBox ? m_boxWidth : 1) + (pl.innerMargin * 2);
593}
594
595void PrintPainter::paintBox(QPainter &painter, uint &y, const PageLayout &pl) const
596{
597 painter.save();
598 painter.setPen(QPen(m_boxColor, m_boxWidth));
599 painter.drawRect(0, 0, pl.pageWidth, pl.pageHeight);
600
601 if (m_useHeader) {
602 painter.drawLine(0, pl.headerHeight, pl.headerWidth, pl.headerHeight);
603 } else {
604 y += pl.innerMargin;
605 }
606
607 if (m_useFooter) { // drawline is not trustable, grr.
608 painter.fillRect(0, pl.maxHeight + pl.innerMargin, pl.headerWidth, m_boxWidth, m_boxColor);
609 }
610
611 painter.restore();
612}
613
614void PrintPainter::paintBackground(QPainter &painter, const uint y, const PageLayout &pl) const
615{
616 // If we have a box, or the header/footer has backgrounds, we want to paint
617 // to the border of those. Otherwise just the contents area.
618 int _y = y;
619 int _h = pl.maxHeight - y;
620 if (m_useBox) {
621 _y -= pl.innerMargin;
622 _h += 2 * pl.innerMargin;
623 } else {
624 if (m_useHeaderBackground) {
625 _y -= pl.innerMargin;
626 _h += pl.innerMargin;
627 }
628 if (m_useFooterBackground) {
629 _h += pl.innerMargin;
630 }
631 }
632 painter.fillRect(0, _y, pl.pageWidth, _h, m_renderer->config()->backgroundColor());
633}
634
635void PrintPainter::paintLine(QPainter &painter, const uint line, uint &y, uint &remainder, const PageLayout &pl) const
636{
637 // HA! this is where we print [part of] a line ;]]
638 KateLineLayout rangeptr(*m_renderer);
639 rangeptr.setLine(line);
640 m_renderer->layoutLine(&rangeptr, (int)pl.maxWidth, false);
641
642 // selectionOnly: clip non-selection parts and adjust painter position if needed
643 int _xadjust = 0;
644 if (pl.selectionOnly) {
645 if (m_view && m_view->blockSelection()) {
646 int _x = m_renderer->cursorToX(rangeptr.viewLine(0), pl.selectionRange.start());
647 int _x1 = m_renderer->cursorToX(rangeptr.viewLine(rangeptr.viewLineCount() - 1), pl.selectionRange.end());
648 _xadjust = _x;
649 painter.translate(-_xadjust, 0);
650 painter.setClipRegion(QRegion(_x, 0, _x1 - _x, rangeptr.viewLineCount() * m_fontHeight));
651
652 } else if (line == pl.firstline || line == pl.lastline) {
653 QRegion region(0, 0, pl.maxWidth, rangeptr.viewLineCount() * m_fontHeight);
654
655 if (line == pl.firstline) {
656 int l = rangeptr.viewLineForColumn(pl.selectionRange.start().column());
657 // clip viewlines above the selection
658 for (int vl = 0; vl < l; ++vl) {
659 region = region.subtracted(QRegion(0, vl * m_fontHeight, pl.maxWidth, m_fontHeight));
660 }
661 region = region.subtracted(QRegion(0, 0, m_renderer->cursorToX(rangeptr.viewLine(l), pl.selectionRange.start()), m_fontHeight));
662 }
663
664 if (line == pl.lastline) {
665 int vl = rangeptr.viewLineForColumn(pl.selectionRange.end().column());
666 int x = m_renderer->cursorToX(rangeptr.viewLine(vl), pl.selectionRange.end());
667 // clip the unnecessary portion of this line
668 region = region.subtracted(QRegion(x, (vl)*m_fontHeight, pl.maxWidth - x, m_fontHeight));
669 vl++;
670 // and the viewlines below
671 for (; vl < rangeptr.viewLineCount(); ++vl) {
672 region = region.subtracted(QRegion(0, vl * m_fontHeight, pl.maxWidth, m_fontHeight));
673 }
674 }
675
676 painter.setClipRegion(region);
677 }
678 }
679
680 // If the line is too long (too many 'viewlines') to fit the remaining vertical space,
681 // clip and adjust the painter position as necessary
682 int _lines = rangeptr.viewLineCount(); // number of "sublines" to paint.
683
684 int proceedLines = _lines;
685 if (remainder) {
686 proceedLines = qMin((pl.maxHeight - y) / m_fontHeight, remainder);
687
688 painter.translate(0, -(_lines - int(remainder)) * m_fontHeight + 1);
689 painter.setClipRect(0,
690 (_lines - int(remainder)) * m_fontHeight + 1,
691 pl.maxWidth,
692 proceedLines * m_fontHeight); // ### drop the crosspatch in printerfriendly mode???
693 remainder -= proceedLines;
694 } else if (y + m_fontHeight * _lines > pl.maxHeight) {
695 remainder = _lines - ((pl.maxHeight - y) / m_fontHeight);
696 painter.setClipRect(0, 0, pl.maxWidth, (_lines - int(remainder)) * m_fontHeight + 1); // ### drop the crosspatch in printerfriendly mode???
697 } else if (!pl.selectionOnly) {
698 painter.setClipRegion(QRegion());
699 painter.setClipping(false);
700 }
701
702 KateRenderer::PaintTextLineFlags flags;
703 if (!m_dontPrintFoldedCode) {
705 }
706
707 m_renderer->paintTextLine(painter, &rangeptr, 0, (int)pl.maxWidth, QRectF{}, nullptr, flags);
708
709 painter.setClipping(false);
710 painter.translate(_xadjust, (m_fontHeight * (_lines - remainder)));
711
712 y += m_fontHeight * proceedLines;
713}
714
715void PrintPainter::paintLineNumber(QPainter &painter, const uint number, const PageLayout &pl) const
716{
717 const int left = ((m_useBox || m_useBackground) ? pl.innerMargin : 0) - pl.xstart;
718
719 painter.save();
720 painter.setFont(m_renderer->currentFont());
721 painter.setPen(m_renderer->config()->lineNumberColor());
722 painter.drawText(left, 0, m_lineNumberWidth, m_fontHeight, Qt::AlignRight | Qt::AlignVCenter, QString::number(number + 1));
723 painter.restore();
724}
725
726// END PrintPainter
QExplicitlySharedDataPointer< Attribute > Ptr
Shared data pointer for Attribute.
Definition attribute.h:56
Backend of KTextEditor::Document related public KTextEditor interfaces.
Handles all of the work of rendering the text (used for the views and printing)
Kate::TextFolding & folding() const
Returns the folding info to which this renderer is bound.
@ SkipDrawFirstInvisibleLineUnderlined
Skip drawing the dashed underline at the start of a folded block of text?
QString i18n(const char *text, const TYPE &arg...)
KCOREADDONS_EXPORT Result match(QStringView pattern, QStringView str)
bool isValid() const const
QDateTime currentDateTime()
QDate date() const const
QTime time() const const
QFlags< T > & setFlag(Enum flag, bool on)
void setBold(bool enable)
void setUnderline(bool enable)
typedef Iterator
int height() const const
int width() const const
void drawLine(const QLine &line)
void drawRect(const QRect &rectangle)
void drawText(const QPoint &position, const QString &text)
void fillRect(const QRect &rectangle, QGradient::Preset preset)
void restore()
void save()
void setClipRect(const QRect &rectangle, Qt::ClipOperation operation)
void setClipRegion(const QRegion &region, Qt::ClipOperation operation)
void setClipping(bool enable)
void setFont(const QFont &font)
void setPen(Qt::PenStyle style)
void translate(const QPoint &offset)
int fromPage() const const
virtual bool newPage() override
PrintRange printRange() const const
int toPage() const const
int height() const const
void moveTo(const QPoint &position)
QString & fill(QChar ch, qsizetype size)
qsizetype indexOf(QChar ch, qsizetype from, Qt::CaseSensitivity cs) const const
qsizetype length() const const
QString number(double n, char format, int precision)
QString & replace(QChar before, QChar after, Qt::CaseSensitivity cs)
QString section(QChar sep, qsizetype start, qsizetype end, SectionFlags flags) const const
AlignVCenter
QTextStream & left(QTextStream &stream)
This file is part of the KDE documentation.
Documentation copyright © 1996-2025 The KDE developers.
Generated on Fri Jan 24 2025 11:55:24 by doxygen 1.13.2 written by Dimitri van Heesch, © 1997-2006

KDE's Doxygen guidelines are available online.