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:
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.
256 std::map<QString, QString> tags;
257
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();
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();
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
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
Backend of KTextEditor::Document related public KTextEditor interfaces.
int lastLine() const
gets the last line number (lines() - 1)
int lines() const override
Get the count of lines of the document.
An object representing a section of text, from one Cursor to another.
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.
const QFont & currentFont() const
Access currently used font.
int cursorToX(const KateTextLayout &range, int col, bool returnPastLine=false) const
Returns the x position of cursor col on the line range.
@ SkipDrawFirstInvisibleLineUnderlined
Skip drawing the dashed underline at the start of a folded block of text?
const QFontMetricsF & currentFontMetrics() const
Access currently used font metrics.
void layoutLine(KateLineLayout *line, int maxwidth=-1, bool cacheLayout=false) const
Text width & height calculation functions...
void paintTextLine(QPainter &paint, KateLineLayout *range, int xStart, int xEnd, const QRectF &textClipRect=QRectF(), const KTextEditor::Cursor *cursor=nullptr, PaintTextLineFlags flags=PaintTextLineFlags())
This is the ultimate function to perform painting of a text line.
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)
QRect boundingRect(QChar ch) const const
int height() const const
int leading() const const
QRectF boundingRect(QChar ch) const const
qreal horizontalAdvance(QChar ch) const const
QString toString(QDate date, FormatType format) const const
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)
int width() const const
qreal width() const const
QString arg(Args &&... args) const const
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)
QString fileName(ComponentFormattingOptions options) const const
QString toString(FormattingOptions options) const const
This file is part of the KDE documentation.
Documentation copyright © 1996-2024 The KDE developers.
Generated on Mon Nov 18 2024 12:11:27 by doxygen 1.12.0 written by Dimitri van Heesch, © 1997-2006

KDE's Doxygen guidelines are available online.