CalendarSupport

calprintpluginbase.cpp
1/*
2 SPDX-FileCopyrightText: 1998 Preston Brown <pbrown@kde.org>
3 SPDX-FileCopyrightText: 2003 Reinhold Kainhofer <reinhold@kainhofer.com>
4 SPDX-FileCopyrightText: 2008 Ron Goodheart <rong.dev@gmail.com>
5 SPDX-FileCopyrightText: 2012-2013 Allen Winter <winter@kde.org>
6
7 SPDX-License-Identifier: GPL-2.0-or-later WITH Qt-Commercial-exception-1.0
8*/
9
10#include "calprintpluginbase.h"
11using namespace Qt::Literals::StringLiterals;
12
13#include "cellitem.h"
14#include "kcalprefs.h"
15#include "utils.h"
16
17#include <Akonadi/Item>
18#include <Akonadi/TagCache>
19
20#include "calendarsupport_debug.h"
21#include <KConfig>
22#include <KConfigGroup>
23#include <KWordWrap>
24
25#include <KLocalizedString>
26#include <QAbstractTextDocumentLayout>
27#include <QFrame>
28#include <QLabel>
29#include <QLocale>
30#include <QTextCursor>
31#include <QTextDocument>
32#include <QTextDocumentFragment>
33#include <QTimeZone>
34#include <QVBoxLayout>
35#include <qmath.h> // qCeil krazy:exclude=camelcase since no QMath
36
37using namespace CalendarSupport;
38
39static QString cleanStr(const QString &instr)
40{
41 QString ret = instr;
42 return ret.replace(QLatin1Char('\n'), QLatin1Char(' '));
43}
44
45const QColor CalPrintPluginBase::sHolidayBackground = QColor(244, 244, 244);
46
47/******************************************************************
48 ** The Todo positioning structure **
49 ******************************************************************/
50class CalPrintPluginBase::TodoParentStart
51{
52public:
53 TodoParentStart(QRect pt = QRect(), bool hasLine = false, bool page = true)
54 : mRect(pt)
55 , mHasLine(hasLine)
56 , mSamePage(page)
57 {
58 }
59
60 QRect mRect;
61 bool mHasLine;
62 bool mSamePage;
63};
64
65/******************************************************************
66 ** The Print item **
67 ******************************************************************/
68
69class PrintCellItem : public CellItem
70{
71public:
72 PrintCellItem(const KCalendarCore::Event::Ptr &event, const QDateTime &start, const QDateTime &end)
73 : mEvent(event)
74 , mStart(start)
75 , mEnd(end)
76 {
77 }
78
79 [[nodiscard]] KCalendarCore::Event::Ptr event() const
80 {
81 return mEvent;
82 }
83
84 [[nodiscard]] QString label() const override
85 {
86 return mEvent->summary();
87 }
88
89 [[nodiscard]] QDateTime start() const
90 {
91 return mStart;
92 }
93
94 [[nodiscard]] QDateTime end() const
95 {
96 return mEnd;
97 }
98
99 /** Calculate the start and end date/time of the recurrence that
100 happens on the given day */
101 bool overlaps(CellItem *o) const override
102 {
103 auto other = static_cast<PrintCellItem *>(o);
104 return !(other->start() >= end() || other->end() <= start());
105 }
106
107private:
109 QDateTime mStart, mEnd;
110};
111
112/******************************************************************
113 ** The Print plugin **
114 ******************************************************************/
115
117 : mUseColors(true)
118 , mPrintFooter(true)
119 , mHeaderHeight(-1)
120 , mSubHeaderHeight(SUBHEADER_HEIGHT)
121 , mFooterHeight(-1)
122 , mMargin(MARGIN_SIZE)
123 , mPadding(PADDING_SIZE)
124{
125}
126
127CalPrintPluginBase::~CalPrintPluginBase() = default;
128
130{
131 auto wdg = new QFrame(w);
132 auto layout = new QVBoxLayout(wdg);
133
134 auto title = new QLabel(description(), wdg);
135 QFont titleFont(title->font());
136 titleFont.setPointSize(20);
137 titleFont.setBold(true);
138 title->setFont(titleFont);
139
140 layout->addWidget(title);
141 layout->addWidget(new QLabel(info(), wdg));
142 layout->addSpacing(20);
143 layout->addWidget(new QLabel(i18nc("@label:textbox", "This printing style does not have any configuration options."), wdg));
144 layout->addStretch();
145 return wdg;
146}
147
149{
150 if (!printer) {
151 return;
152 }
153 mPrinter = printer;
154 QPainter p;
155
157
158 p.begin(mPrinter);
159 // TODO: Fix the margins!!!
160 // the painter initially begins at 72 dpi per the Qt docs.
161 // we want half-inch margins.
162 int margins = margin();
163 p.setViewport(margins, margins, p.viewport().width() - 2 * margins, p.viewport().height() - 2 * margins);
164 // QRect vp( p.viewport() );
165 // vp.setRight( vp.right()*2 );
166 // vp.setBottom( vp.bottom()*2 );
167 // p.setWindow( vp );
168 int pageWidth = p.window().width();
169 int pageHeight = p.window().height();
170 // int pageWidth = p.viewport().width();
171 // int pageHeight = p.viewport().height();
172
173 print(p, pageWidth, pageHeight);
174
175 p.end();
176 mPrinter = nullptr;
177}
178
180{
181 if (mConfig) {
182 KConfigGroup group(mConfig, groupName());
183 mConfig->sync();
185 mFromDate = group.readEntry("FromDate", dt).date();
186 mToDate = group.readEntry("ToDate", dt).date();
187 mUseColors = group.readEntry("UseColors", true);
188 mPrintFooter = group.readEntry("PrintFooter", true);
189 mShowNoteLines = group.readEntry("Note Lines", false);
190 mExcludeConfidential = group.readEntry("Exclude confidential", true);
191 mExcludePrivate = group.readEntry("Exclude private", true);
192 } else {
193 qCDebug(CALENDARSUPPORT_LOG) << "No config available in loadConfig!!!!";
194 }
195}
196
198{
199 if (mConfig) {
200 KConfigGroup group(mConfig, groupName());
201 QDateTime dt = QDateTime::currentDateTime(); // any valid QDateTime will do
202 dt.setDate(mFromDate);
203 group.writeEntry("FromDate", dt);
204 dt.setDate(mToDate);
205 group.writeEntry("ToDate", dt);
206 group.writeEntry("UseColors", mUseColors);
207 group.writeEntry("PrintFooter", mPrintFooter);
208 group.writeEntry("Note Lines", mShowNoteLines);
209 group.writeEntry("Exclude confidential", mExcludeConfidential);
210 group.writeEntry("Exclude private", mExcludePrivate);
211 mConfig->sync();
212 } else {
213 qCDebug(CALENDARSUPPORT_LOG) << "No config available in saveConfig!!!!";
214 }
215}
216
218{
219 return mUseColors;
220}
221
222void CalPrintPluginBase::setUseColors(bool useColors)
223{
225}
226
227bool CalPrintPluginBase::printFooter() const
228{
229 return mPrintFooter;
230}
231
232void CalPrintPluginBase::setPrintFooter(bool printFooter)
233{
234 mPrintFooter = printFooter;
235}
236
237QPageLayout::Orientation CalPrintPluginBase::orientation() const
238{
239 return mPrinter ? mPrinter->pageLayout().orientation() : QPageLayout::Portrait;
240}
241
242QColor CalPrintPluginBase::getTextColor(const QColor &c) const
243{
244 double luminance = (c.red() * 0.299) + (c.green() * 0.587) + (c.blue() * 0.114);
245 return (luminance > 128.0) ? QColor(0, 0, 0) : QColor(255, 255, 255);
246}
247
248QTime CalPrintPluginBase::dayStart() const
249{
250 QTime start(8, 0, 0);
251 QDateTime dayBegins = KCalPrefs::instance()->dayBegins();
252 if (dayBegins.isValid()) {
253 start = dayBegins.time();
254 }
255 return start;
256}
257
258void CalPrintPluginBase::setColorsByIncidenceCategory(QPainter &p, const KCalendarCore::Incidence::Ptr &incidence) const
259{
260 QColor bgColor = categoryBgColor(incidence);
261 if (bgColor.isValid()) {
262 p.setBrush(bgColor);
263 }
264 QColor tColor(getTextColor(bgColor));
265 if (tColor.isValid()) {
266 p.setPen(tColor);
267 }
268}
269
270QColor CalPrintPluginBase::categoryColor(const QStringList &categories) const
271{
272 // FIXME: Correctly treat events with multiple categories
273 QColor bgColor;
274 if (!categories.isEmpty()) {
275 bgColor = Akonadi::TagCache::instance()->tagColor(categories.at(0));
276 }
277
278 return bgColor.isValid() ? bgColor : KCalPrefs::instance()->unsetCategoryColor();
279}
280
281QColor CalPrintPluginBase::categoryBgColor(const KCalendarCore::Incidence::Ptr &incidence) const
282{
283 if (incidence) {
284 QColor backColor = categoryColor(incidence->categories());
286 if ((incidence.staticCast<KCalendarCore::Todo>())->isOverdue()) {
287 backColor = QColor(255, 100, 100); // was KOPrefs::instance()->todoOverdueColor();
288 }
289 }
290 return backColor;
291 } else {
292 return {};
293 }
294}
295
296QString CalPrintPluginBase::holidayString(QDate date) const
297{
298 const QStringList lst = holiday(date);
299 return lst.join(i18nc("@item:intext delimiter for joining holiday names", ","));
300}
301
302KCalendarCore::Event::Ptr CalPrintPluginBase::holidayEvent(QDate date) const
303{
304 QString hstring(holidayString(date));
305 if (hstring.isEmpty()) {
306 return {};
307 }
308
309 KCalendarCore::Event::Ptr holiday(new KCalendarCore::Event);
310 holiday->setSummary(hstring);
311 holiday->setCategories(i18n("Holiday"));
312
313 QDateTime kdt(date, QTime(0, 0), QTimeZone::LocalTime);
314 holiday->setDtStart(kdt);
315 holiday->setDtEnd(kdt);
316 holiday->setAllDay(true);
317
318 return holiday;
319}
320
322{
323 if (mHeaderHeight >= 0) {
324 return mHeaderHeight;
325 } else if (orientation() == QPageLayout::Portrait) {
326 return PORTRAIT_HEADER_HEIGHT;
327 } else {
328 return LANDSCAPE_HEADER_HEIGHT;
329 }
330}
331
332void CalPrintPluginBase::setHeaderHeight(const int height)
333{
334 mHeaderHeight = height;
335}
336
337int CalPrintPluginBase::subHeaderHeight() const
338{
339 return mSubHeaderHeight;
340}
341
342void CalPrintPluginBase::setSubHeaderHeight(const int height)
343{
344 mSubHeaderHeight = height;
345}
346
348{
349 if (!mPrintFooter) {
350 return 0;
351 }
352
353 if (mFooterHeight >= 0) {
354 return mFooterHeight;
355 } else if (orientation() == QPageLayout::Portrait) {
356 return PORTRAIT_FOOTER_HEIGHT;
357 } else {
358 return LANDSCAPE_FOOTER_HEIGHT;
359 }
360}
361
362void CalPrintPluginBase::setFooterHeight(const int height)
363{
364 mFooterHeight = height;
365}
366
367int CalPrintPluginBase::margin() const
368{
369 return mMargin;
370}
371
372void CalPrintPluginBase::setMargin(const int margin)
373{
374 mMargin = margin;
375}
376
377int CalPrintPluginBase::padding() const
378{
379 return mPadding;
380}
381
382void CalPrintPluginBase::setPadding(const int padding)
383{
384 mPadding = padding;
385}
386
387int CalPrintPluginBase::borderWidth() const
388{
389 return mBorder;
390}
391
392void CalPrintPluginBase::setBorderWidth(const int borderwidth)
393{
394 mBorder = borderwidth;
395}
396
397void CalPrintPluginBase::drawBox(QPainter &p, int linewidth, QRect rect)
398{
399 QPen pen(p.pen());
400 QPen oldpen(pen);
401 // no border
402 if (linewidth >= 0) {
403 pen.setWidth(linewidth);
404 p.setPen(pen);
405 } else {
406 p.setPen(Qt::NoPen);
407 }
408 p.drawRect(rect);
409 p.setPen(oldpen);
410}
411
412void CalPrintPluginBase::drawShadedBox(QPainter &p, int linewidth, const QBrush &brush, QRect rect)
413{
414 QBrush oldbrush(p.brush());
415 p.setBrush(brush);
416 drawBox(p, linewidth, rect);
417 p.setBrush(oldbrush);
418}
419
420void CalPrintPluginBase::printEventString(QPainter &p, QRect box, const QString &str, int flags)
421{
422 QRect newbox(box);
423 newbox.adjust(3, 1, -1, -1);
424 p.drawText(newbox, (flags == -1) ? (Qt::AlignTop | Qt::AlignLeft | Qt::TextWordWrap) : flags, str);
425}
426
427void CalPrintPluginBase::showEventBox(QPainter &p, int linewidth, QRect box, const KCalendarCore::Incidence::Ptr &incidence, const QString &str, int flags)
428{
429 QPen oldpen(p.pen());
430 QBrush oldbrush(p.brush());
431 QColor bgColor(categoryBgColor(incidence));
432 if (mUseColors && bgColor.isValid()) {
433 p.setBrush(bgColor);
434 } else {
435 p.setBrush(QColor(232, 232, 232));
436 }
437 drawBox(p, (linewidth > 0) ? linewidth : EVENT_BORDER_WIDTH, box);
438 if (mUseColors && bgColor.isValid()) {
439 p.setPen(getTextColor(bgColor));
440 }
441 printEventString(p, box, str, flags);
442 p.setPen(oldpen);
443 p.setBrush(oldbrush);
444}
445
447{
448 drawShadedBox(p, BOX_BORDER_WIDTH, QColor(232, 232, 232), box);
449 QFont oldfont(p.font());
450 p.setFont(QFont(QStringLiteral("sans-serif"), 10, QFont::Bold));
452 p.setFont(oldfont);
453}
454
455void CalPrintPluginBase::drawVerticalBox(QPainter &p, int linewidth, QRect box, const QString &str, int flags)
456{
457 p.save();
458 p.rotate(-90);
459 QRect rotatedBox(-box.top() - box.height(), box.left(), box.height(), box.width());
460 showEventBox(p, linewidth, rotatedBox, KCalendarCore::Incidence::Ptr(), str, (flags == -1) ? Qt::AlignLeft | Qt::AlignVCenter | Qt::TextSingleLine : flags);
461
462 p.restore();
463}
464
465/*
466 * Return value: If expand, bottom of the printed box, otherwise vertical end
467 * of the printed contents inside the box.
468 */
470 QRect allbox,
471 const QString &caption,
472 const QString &contents,
473 bool sameLine,
474 bool expand,
475 const QFont &captionFont,
476 const QFont &textFont,
477 bool richContents)
478{
479 QFont oldFont(p.font());
480 // QFont captionFont( "sans-serif", 11, QFont::Bold );
481 // QFont textFont( "sans-serif", 11, QFont::Normal );
482 // QFont captionFont( "Tahoma", 11, QFont::Bold );
483 // QFont textFont( "Tahoma", 11, QFont::Normal );
484
485 QRect box(allbox);
486
487 // Bounding rectangle for caption, single-line, clip on the right
488 QRect captionBox(box.left() + padding(), box.top() + padding(), 0, 0);
489 p.setFont(captionFont);
490 captionBox = p.boundingRect(captionBox, Qt::AlignLeft | Qt::AlignTop | Qt::TextSingleLine, caption);
491 p.setFont(oldFont);
492 if (captionBox.right() > box.right()) {
493 captionBox.setRight(box.right());
494 }
495 if (expand && captionBox.bottom() + padding() > box.bottom()) {
496 box.setBottom(captionBox.bottom() + padding());
497 }
498
499 // Bounding rectangle for the contents (if any), word break, clip on the bottom
500 QRect textBox(captionBox);
501 if (!contents.isEmpty()) {
502 if (sameLine) {
503 textBox.setLeft(captionBox.right() + padding());
504 } else {
505 textBox.setTop(captionBox.bottom() + padding());
506 }
507 textBox.setRight(box.right());
508 }
509 drawBox(p, BOX_BORDER_WIDTH, box);
510 p.setFont(captionFont);
511 p.drawText(captionBox, Qt::AlignLeft | Qt::AlignTop | Qt::TextSingleLine, caption);
512
513 if (!contents.isEmpty()) {
514 if (sameLine) {
515 QString contentText = toPlainText(contents);
516 p.setFont(textFont);
517 p.drawText(textBox, Qt::AlignLeft | Qt::AlignTop | Qt::TextSingleLine, contentText);
518 } else {
519 QTextDocument rtb;
520 int borderWidth = 2 * BOX_BORDER_WIDTH;
521 if (richContents) {
522 rtb.setHtml(contents);
523 } else {
524 rtb.setPlainText(contents);
525 }
526 int boxHeight = allbox.height();
527 if (!sameLine) {
528 boxHeight -= captionBox.height();
529 }
530 rtb.setPageSize(QSize(textBox.width(), boxHeight));
531 rtb.setDefaultFont(textFont);
532 p.save();
533 p.translate(textBox.x() - borderWidth, textBox.y());
534 QRect clipBox(0, 0, box.width(), boxHeight);
536 ctx.palette.setColor(QPalette::Text, p.pen().color());
537 p.setClipRect(clipBox);
538 ctx.clip = clipBox;
539 rtb.documentLayout()->draw(&p, ctx);
540 p.restore();
541 textBox.setBottom(textBox.y() + rtb.documentLayout()->documentSize().height());
542 }
543 }
544 p.setFont(oldFont);
545
546 if (expand) {
547 return box.bottom();
548 } else {
549 return textBox.bottom();
550 }
551}
552
553int CalPrintPluginBase::drawHeader(QPainter &p, const QString &title, QDate month1, QDate month2, QRect allbox, bool expand, QColor backColor)
554{
555 // print previous month for month view, print current for to-do, day and week
556 int smallMonthWidth = (allbox.width() / 4) - 10;
557 if (smallMonthWidth > 100) {
558 smallMonthWidth = 100;
559 }
560
561 QRect box(allbox);
562 QRect textRect(allbox);
563
564 QFont oldFont(p.font());
565 QFont newFont(QStringLiteral("sans-serif"), (textRect.height() < 60) ? 16 : 18, QFont::Bold);
566 if (expand) {
567 p.setFont(newFont);
568 QRect boundingR = p.boundingRect(textRect, Qt::AlignLeft | Qt::AlignVCenter | Qt::TextWordWrap, title);
569 p.setFont(oldFont);
570 int h = boundingR.height();
571 if (h > allbox.height()) {
572 box.setHeight(h);
573 textRect.setHeight(h);
574 }
575 }
576
577 if (!backColor.isValid()) {
578 backColor = QColor(232, 232, 232);
579 }
580
581 drawShadedBox(p, BOX_BORDER_WIDTH, backColor, box);
582
583 const auto oldPen{p.pen()};
584 p.setPen(getTextColor(backColor));
585
586 // prev month left, current month centered, next month right
587 QRect monthbox2(box.right() - 10 - smallMonthWidth, box.top(), smallMonthWidth, box.height());
588 if (month2.isValid()) {
589 drawSmallMonth(p, QDate(month2.year(), month2.month(), 1), monthbox2);
590 textRect.setRight(monthbox2.left());
591 }
592 QRect monthbox1(box.left() + 10, box.top(), smallMonthWidth, box.height());
593 if (month1.isValid()) {
594 drawSmallMonth(p, QDate(month1.year(), month1.month(), 1), monthbox1);
595 textRect.setLeft(monthbox1.right());
596 }
597
598 // Set the margins
599 p.setFont(newFont);
601
602 p.setPen(oldPen);
603 p.setFont(oldFont);
604
605 return textRect.bottom();
606}
607
609{
610 QFont oldfont(p.font());
611 p.setFont(QFont(QStringLiteral("sans-serif"), 6));
613 p.drawText(footbox, Qt::AlignCenter | Qt::AlignVCenter | Qt::TextSingleLine, i18nc("print date: formatted-datetime", "printed: %1", dateStr));
614 p.setFont(oldfont);
615
616 return footbox.bottom();
617}
618
620{
621 int weekdayCol = weekdayColumn(qd.dayOfWeek());
622 int month = qd.month();
623 QDate monthDate(QDate(qd.year(), qd.month(), 1));
624 // correct begin of week
625 QDate monthDate2(monthDate.addDays(-weekdayCol));
626
627 double cellWidth = double(box.width()) / double(7);
628 int rownr = 3 + (qd.daysInMonth() + weekdayCol - 1) / 7;
629 // 3 Pixel after month name, 2 after day names, 1 after the calendar
630 double cellHeight = (box.height() - 5) / rownr;
631 QFont oldFont(p.font());
632 auto newFont = QFont(QStringLiteral("sans-serif"));
633 newFont.setPixelSize(cellHeight);
634 p.setFont(newFont);
635
636 const QLocale locale;
637
638 // draw the title
639 QRect titleBox(box);
640 titleBox.setHeight(p.fontMetrics().height());
641 p.drawText(titleBox, Qt::AlignTop | Qt::AlignHCenter, locale.standaloneMonthName(month));
642
643 // draw days of week
644 QRect wdayBox(box);
645 wdayBox.setTop(int(box.top() + 3 + cellHeight));
646 wdayBox.setHeight(int(2 * cellHeight) - int(cellHeight));
647
648 for (int col = 0; col < 7; ++col) {
649 const auto dayLetter = locale.standaloneDayName(monthDate2.dayOfWeek(), QLocale::ShortFormat)[0].toUpper();
650 wdayBox.setLeft(int(box.left() + col * cellWidth));
651 wdayBox.setRight(int(box.left() + (col + 1) * cellWidth));
652 p.drawText(wdayBox, Qt::AlignCenter, dayLetter);
653 monthDate2 = monthDate2.addDays(1);
654 }
655
656 // draw separator line
657 int calStartY = wdayBox.bottom() + 2;
658 p.drawLine(box.left(), calStartY, box.right(), calStartY);
659 monthDate = monthDate.addDays(-weekdayCol);
660
661 for (int row = 0; row < (rownr - 2); row++) {
662 for (int col = 0; col < 7; col++) {
663 if (monthDate.month() == month) {
664 QRect dayRect(int(box.left() + col * cellWidth), int(calStartY + row * cellHeight), 0, 0);
665 dayRect.setRight(int(box.left() + (col + 1) * cellWidth));
666 dayRect.setBottom(int(calStartY + (row + 1) * cellHeight));
667 p.drawText(dayRect, Qt::AlignCenter, QString::number(monthDate.day()));
668 }
669 monthDate = monthDate.addDays(1);
670 }
671 }
672 p.setFont(oldFont);
673}
674
675/*
676 * This routine draws a header box over the main part of the calendar
677 * containing the days of the week.
678 */
680{
681 double cellWidth = double(box.width() - 1) / double(fromDate.daysTo(toDate) + 1);
682 QDate cellDate(fromDate);
683 QRect dateBox(box);
684 int i = 0;
685
686 while (cellDate <= toDate) {
687 dateBox.setLeft(box.left() + int(i * cellWidth));
688 dateBox.setRight(box.left() + int((i + 1) * cellWidth));
689 drawDaysOfWeekBox(p, cellDate, dateBox);
690 cellDate = cellDate.addDays(1);
691 ++i;
692 }
693}
694
699
701{
702 drawBox(p, BOX_BORDER_WIDTH, box);
703
704 int totalsecs = fromTime.secsTo(toTime);
705 float minlen = (float)box.height() * 60. / (float)totalsecs;
706 float cellHeight = (60. * (float)minlen);
707 float currY = box.top();
708 // TODO: Don't use half of the width, but less, for the minutes!
709 int xcenter = box.left() + box.width() / 2;
710
711 QTime curTime(fromTime);
712 QTime endTime(toTime);
713 if (fromTime.minute() > 30) {
714 curTime = QTime(fromTime.hour() + 1, 0, 0);
715 } else if (fromTime.minute() > 0) {
716 curTime = QTime(fromTime.hour(), 30, 0);
717 float yy = currY + minlen * (float)fromTime.secsTo(curTime) / 60.;
718 p.drawLine(xcenter, (int)yy, box.right(), (int)yy);
719 curTime = QTime(fromTime.hour() + 1, 0, 0);
720 }
721 currY += (float(fromTime.secsTo(curTime) * minlen) / 60.);
722
723 while (curTime < endTime) {
724 p.drawLine(box.left(), (int)currY, box.right(), (int)currY);
725 int newY = (int)(currY + cellHeight / 2.);
726 QString numStr;
727 if (newY < box.bottom()) {
728 QFont oldFont(p.font());
729 // draw the time:
730 if (!QLocale().timeFormat().contains("AP"_L1)) { // 12h clock
731 p.drawLine(xcenter, (int)newY, box.right(), (int)newY);
732 numStr.setNum(curTime.hour());
733 if (cellHeight > 30) {
734 p.setFont(QFont(QStringLiteral("sans-serif"), 14, QFont::Bold));
735 } else {
736 p.setFont(QFont(QStringLiteral("sans-serif"), 12, QFont::Bold));
737 }
738 p.drawText(box.left() + 4, (int)currY + 2, box.width() / 2 - 2, (int)cellHeight, Qt::AlignTop | Qt::AlignRight, numStr);
739 p.setFont(QFont(QStringLiteral("helvetica"), 10, QFont::Normal));
740 p.drawText(xcenter + 4, (int)currY + 2, box.width() / 2 + 2, (int)(cellHeight / 2) - 3, Qt::AlignTop | Qt::AlignLeft, QStringLiteral("00"));
741 } else {
742 p.drawLine(box.left(), (int)newY, box.right(), (int)newY);
743 QTime time(curTime.hour(), 0);
745 if (box.width() < 60) {
746 p.setFont(QFont(QStringLiteral("sans-serif"), 7, QFont::Bold)); // for weekprint
747 } else {
748 p.setFont(QFont(QStringLiteral("sans-serif"), 12, QFont::Bold)); // for dayprint
749 }
750 p.drawText(box.left() + 2, (int)currY + 2, box.width() - 4, (int)cellHeight / 2 - 3, Qt::AlignTop | Qt::AlignLeft, numStr);
751 }
752 currY += cellHeight;
753 p.setFont(oldFont);
754 } // enough space for half-hour line and time
755 if (curTime.secsTo(endTime) > 3600) {
756 curTime = curTime.addSecs(3600);
757 } else {
758 curTime = endTime;
759 }
760 }
761}
762
764 const KCalendarCore::Event::List &events,
765 QDate qd,
766 bool expandable,
767 QTime fromTime,
768 QTime toTime,
769 QRect oldbox,
770 bool includeDescription,
771 bool includeCategories,
772 bool excludeTime,
773 const QList<QDate> &workDays)
774{
775 QTime myFromTime;
776 QTime myToTime;
777 if (fromTime.isValid()) {
778 myFromTime = fromTime;
779 } else {
780 myFromTime = QTime(0, 0, 0);
781 }
782 if (toTime.isValid()) {
783 myToTime = toTime;
784 } else {
785 myToTime = QTime(23, 59, 59);
786 }
787
788 if (!workDays.contains(qd)) {
789 drawShadedBox(p, BOX_BORDER_WIDTH, sHolidayBackground, oldbox);
790 } else {
791 drawBox(p, BOX_BORDER_WIDTH, oldbox);
792 }
793 QRect box(oldbox);
794 // Account for the border with and cut away that margin from the interior
795 // box.setRight( box.right()-BOX_BORDER_WIDTH );
796
797 if (expandable) {
798 // Adapt start/end times to include complete events
799 for (const KCalendarCore::Event::Ptr &event : std::as_const(events)) {
800 Q_ASSERT(event);
801 if (!event || (mExcludeConfidential && event->secrecy() == KCalendarCore::Incidence::SecrecyConfidential)
802 || (mExcludePrivate && event->secrecy() == KCalendarCore::Incidence::SecrecyPrivate)) {
803 continue;
804 }
805 // skip items without times so that we do not adjust for all day items
806 if (event->allDay()) {
807 continue;
808 }
809 if (event->dtStart().time() < myFromTime) {
810 myFromTime = event->dtStart().time();
811 }
812 if (event->dtEnd().time() > myToTime) {
813 myToTime = event->dtEnd().time();
814 }
815 }
816 }
817
818 // calculate the height of a cell and of a minute
819 int totalsecs = myFromTime.secsTo(myToTime);
820 float minlen = box.height() * 60. / totalsecs;
821 float cellHeight = 60. * minlen;
822 float currY = box.top();
823
824 // print grid:
825 QTime curTime(QTime(myFromTime.hour(), 0, 0));
826 currY += myFromTime.secsTo(curTime) * minlen / 60;
827
828 while (curTime < myToTime && curTime.isValid()) {
829 if (currY > box.top()) {
830 p.drawLine(box.left(), int(currY), box.right(), int(currY));
831 }
832 currY += cellHeight / 2;
833 if ((currY > box.top()) && (currY < box.bottom())) {
834 // enough space for half-hour line
835 QPen oldPen(p.pen());
836 p.setPen(QColor(192, 192, 192));
837 p.drawLine(box.left(), int(currY), box.right(), int(currY));
838 p.setPen(oldPen);
839 }
840 if (curTime.secsTo(myToTime) > 3600) {
841 curTime = curTime.addSecs(3600);
842 } else {
843 curTime = myToTime;
844 }
845 currY += cellHeight / 2;
846 }
847
848 QDateTime startPrintDate = QDateTime(qd, myFromTime);
849 QDateTime endPrintDate = QDateTime(qd, myToTime);
850
851 // Calculate horizontal positions and widths of events taking into account
852 // overlapping events
853
854 QList<CellItem *> cells;
855
856 for (const KCalendarCore::Event::Ptr &event : std::as_const(events)) {
857 if (!event || (mExcludeConfidential && event->secrecy() == KCalendarCore::Incidence::SecrecyConfidential)
858 || (mExcludePrivate && event->secrecy() == KCalendarCore::Incidence::SecrecyPrivate)) {
859 continue;
860 }
861 if (event->allDay()) {
862 continue;
863 }
864 QList<QDateTime> times = event->startDateTimesForDate(qd, QTimeZone::systemTimeZone());
865 cells.reserve(times.count());
866 for (auto it = times.constBegin(); it != times.constEnd(); ++it) {
867 cells.append(new PrintCellItem(event, (*it).toLocalTime(), event->endDateForStart(*it).toLocalTime()));
868 }
869 }
870
871 QListIterator<CellItem *> it1(cells);
872 while (it1.hasNext()) {
873 CellItem *placeItem = it1.next();
874 CellItem::placeItem(cells, placeItem);
875 }
876
877 QListIterator<CellItem *> it2(cells);
878 while (it2.hasNext()) {
879 auto placeItem = static_cast<PrintCellItem *>(it2.next());
880 drawAgendaItem(placeItem, p, startPrintDate, endPrintDate, minlen, box, includeDescription, includeCategories, excludeTime);
881 }
882}
883
884void CalPrintPluginBase::drawAgendaItem(PrintCellItem *item,
885 QPainter &p,
886 const QDateTime &startPrintDate,
887 const QDateTime &endPrintDate,
888 float minlen,
889 QRect box,
890 bool includeDescription,
891 bool includeCategories,
892 bool excludeTime)
893{
894 KCalendarCore::Event::Ptr event = item->event();
895
896 // start/end of print area for event
897 QDateTime startTime = item->start();
898 QDateTime endTime = item->end();
899 if ((startTime < endPrintDate && endTime > startPrintDate) || (endTime > startPrintDate && startTime < endPrintDate)) {
900 if (startTime < startPrintDate) {
901 startTime = startPrintDate;
902 }
903 if (endTime > endPrintDate) {
904 endTime = endPrintDate;
905 }
906 int currentWidth = box.width() / item->subCells();
907 int currentX = box.left() + item->subCell() * currentWidth;
908 int currentYPos = int(box.top() + startPrintDate.secsTo(startTime) * minlen / 60.);
909 int currentHeight = int(box.top() + startPrintDate.secsTo(endTime) * minlen / 60.) - currentYPos;
910
911 QRect eventBox(currentX, currentYPos, currentWidth, currentHeight);
912 QString str;
913 if (excludeTime) {
914 if (event->location().isEmpty()) {
915 str = cleanStr(event->summary());
916 } else {
917 str = i18nc("summary, location", "%1, %2", cleanStr(event->summary()), cleanStr(event->location()));
918 }
919 } else {
920 if (event->location().isEmpty()) {
921 str = i18nc("starttime - endtime summary",
922 "%1-%2 %3",
925 cleanStr(event->summary()));
926 } else {
927 str = i18nc("starttime - endtime summary, location",
928 "%1-%2 %3, %4",
931 cleanStr(event->summary()),
932 cleanStr(event->location()));
933 }
934 }
935 if (includeCategories && !event->categoriesStr().isEmpty()) {
936 str = i18nc("summary, categories", "%1, %2", str, event->categoriesStr());
937 }
938 if (includeDescription && !event->description().isEmpty()) {
939 str += QLatin1Char('\n');
940 if (event->descriptionIsRich()) {
941 str += toPlainText(event->description());
942 } else {
943 str += event->description();
944 }
945 }
946 QFont oldFont(p.font());
947 if (eventBox.height() < 24) {
948 if (eventBox.height() < 12) {
949 if (eventBox.height() < 8) {
950 p.setFont(QFont(QStringLiteral("sans-serif"), 4));
951 } else {
952 p.setFont(QFont(QStringLiteral("sans-serif"), 5));
953 }
954 } else {
955 p.setFont(QFont(QStringLiteral("sans-serif"), 6));
956 }
957 } else {
958 p.setFont(QFont(QStringLiteral("sans-serif"), 8));
959 }
960 showEventBox(p, EVENT_BORDER_WIDTH, eventBox, event, str);
961 p.setFont(oldFont);
962 }
963}
964
966 QDate qd,
967 QTime fromTime,
968 QTime toTime,
969 QRect box,
970 bool fullDate,
971 bool printRecurDaily,
972 bool printRecurWeekly,
973 bool singleLineLimit,
974 bool includeDescription,
975 bool includeCategories)
976{
977 QString dayNumStr;
978 const auto local = QLocale::system();
979
980 QTime myFromTime;
981 QTime myToTime;
982 if (fromTime.isValid()) {
983 myFromTime = fromTime;
984 } else {
985 myFromTime = QTime(0, 0, 0);
986 }
987 if (toTime.isValid()) {
988 myToTime = toTime;
989 } else {
990 myToTime = QTime(23, 59, 59);
991 }
992
993 if (fullDate) {
994 dayNumStr = i18nc("weekday, shortmonthname daynumber",
995 "%1, %2 %3",
996 QLocale::system().dayName(qd.dayOfWeek()),
997 QLocale::system().monthName(qd.month(), QLocale::ShortFormat),
998 QString::number(qd.day()));
999 } else {
1000 dayNumStr = QString::number(qd.day());
1001 }
1002
1003 QRect subHeaderBox(box);
1004 subHeaderBox.setHeight(mSubHeaderHeight);
1005 drawShadedBox(p, BOX_BORDER_WIDTH, p.background(), box);
1006 drawShadedBox(p, 0, QColor(232, 232, 232), subHeaderBox);
1007 drawBox(p, BOX_BORDER_WIDTH, box);
1008 QString hstring(holidayString(qd));
1009 const QFont oldFont(p.font());
1010
1011 QRect headerTextBox(subHeaderBox.adjusted(5, 0, -5, 0));
1012 p.setFont(QFont(QStringLiteral("sans-serif"), 10, QFont::Bold));
1013 QRect dayNumRect;
1014 p.drawText(headerTextBox, Qt::AlignRight | Qt::AlignVCenter, dayNumStr, &dayNumRect);
1015 if (!hstring.isEmpty()) {
1016 p.setFont(QFont(QStringLiteral("sans-serif"), 8, QFont::Bold, true));
1017 QFontMetrics fm(p.font());
1018 hstring = fm.elidedText(hstring, Qt::ElideRight, headerTextBox.width() - dayNumRect.width() - 5);
1019 p.drawText(headerTextBox, Qt::AlignLeft | Qt::AlignVCenter, hstring);
1020 p.setFont(QFont(QStringLiteral("sans-serif"), 10, QFont::Bold));
1021 }
1022
1023 const KCalendarCore::Event::List eventList =
1025
1026 QString timeText;
1027 p.setFont(QFont(QStringLiteral("sans-serif"), 7));
1028
1029 int textY = mSubHeaderHeight; // gives the relative y-coord of the next printed entry
1030 unsigned int visibleEventsCounter = 0;
1031 for (const KCalendarCore::Event::Ptr &currEvent : std::as_const(eventList)) {
1032 Q_ASSERT(currEvent);
1033 if (!currEvent->allDay()) {
1034 if (currEvent->dtEnd().toLocalTime().time() <= myFromTime || currEvent->dtStart().toLocalTime().time() > myToTime) {
1035 continue;
1036 }
1037 }
1038 if ((!printRecurDaily && currEvent->recurrenceType() == KCalendarCore::Recurrence::rDaily)
1039 || (!printRecurWeekly && currEvent->recurrenceType() == KCalendarCore::Recurrence::rWeekly)) {
1040 continue;
1041 }
1043 || (mExcludePrivate && currEvent->secrecy() == KCalendarCore::Incidence::SecrecyPrivate)) {
1044 continue;
1045 }
1046 if (currEvent->allDay() || currEvent->isMultiDay()) {
1047 timeText.clear();
1048 } else {
1049 timeText = local.toString(currEvent->dtStart().toLocalTime().time(), QLocale::ShortFormat) + QLatin1Char(' ');
1050 }
1051 p.save();
1052 if (mUseColors) {
1053 setColorsByIncidenceCategory(p, currEvent);
1054 }
1055 QString summaryStr = currEvent->summary();
1056 if (!currEvent->location().isEmpty()) {
1057 summaryStr = i18nc("summary, location", "%1, %2", summaryStr, currEvent->location());
1058 }
1059 if (includeCategories && !currEvent->categoriesStr().isEmpty()) {
1060 summaryStr = i18nc("summary, categories", "%1, %2", summaryStr, currEvent->categoriesStr());
1061 }
1062 drawIncidence(p, box, timeText, summaryStr, currEvent->description(), textY, singleLineLimit, includeDescription, currEvent->descriptionIsRich());
1063 p.restore();
1064 visibleEventsCounter++;
1065
1066 if (textY >= box.height()) {
1067 const QChar downArrow(0x21e3);
1068
1069 const unsigned int invisibleIncidences = (eventList.count() - visibleEventsCounter) + mCalendar->todos(qd).count();
1070 if (invisibleIncidences > 0) {
1071 const QString warningMsg = QStringLiteral("%1 (%2)").arg(downArrow).arg(invisibleIncidences);
1072
1073 QFontMetrics fm(p.font());
1074 QRect msgRect = fm.boundingRect(warningMsg);
1075 msgRect.setRect(box.right() - msgRect.width() - 2, box.bottom() - msgRect.height() - 2, msgRect.width(), msgRect.height());
1076
1077 p.save();
1078 p.setPen(Qt::red); // krazy:exclude=qenums we don't allow custom print colors
1079 p.drawText(msgRect, Qt::AlignLeft, warningMsg);
1080 p.restore();
1081 }
1082 break;
1083 }
1084 }
1085
1086 if (textY < box.height()) {
1087 KCalendarCore::Todo::List todos = mCalendar->todos(qd);
1088 for (const KCalendarCore::Todo::Ptr &todo : std::as_const(todos)) {
1089 if (!todo->allDay()) {
1090 if ((todo->hasDueDate() && todo->dtDue().toLocalTime().time() <= myFromTime)
1091 || (todo->hasStartDate() && todo->dtStart().toLocalTime().time() > myToTime)) {
1092 continue;
1093 }
1094 }
1095 if ((!printRecurDaily && todo->recurrenceType() == KCalendarCore::Recurrence::rDaily)
1096 || (!printRecurWeekly && todo->recurrenceType() == KCalendarCore::Recurrence::rWeekly)) {
1097 continue;
1098 }
1100 || (mExcludePrivate && todo->secrecy() == KCalendarCore::Incidence::SecrecyPrivate)) {
1101 continue;
1102 }
1103 if (todo->hasStartDate() && !todo->allDay()) {
1104 timeText = QLocale().toString(todo->dtStart().toLocalTime().time(), QLocale::ShortFormat) + QLatin1Char(' ');
1105 } else {
1106 timeText.clear();
1107 }
1108 p.save();
1109 if (mUseColors) {
1110 setColorsByIncidenceCategory(p, todo);
1111 }
1112 QString summaryStr = todo->summary();
1113 if (!todo->location().isEmpty()) {
1114 summaryStr = i18nc("summary, location", "%1, %2", summaryStr, todo->location());
1115 }
1116
1117 QString str;
1118 if (todo->hasDueDate()) {
1119 if (!todo->allDay()) {
1120 str = i18nc("to-do summary (Due: datetime)",
1121 "%1 (Due: %2)",
1122 summaryStr,
1123 QLocale().toString(todo->dtDue().toLocalTime(), QLocale::ShortFormat));
1124 } else {
1125 str = i18nc("to-do summary (Due: date)",
1126 "%1 (Due: %2)",
1127 summaryStr,
1128 QLocale().toString(todo->dtDue().toLocalTime().date(), QLocale::ShortFormat));
1129 }
1130 } else {
1131 str = summaryStr;
1132 }
1133 drawIncidence(p, box, timeText, i18n("To-do: %1", str), todo->description(), textY, singleLineLimit, includeDescription, todo->descriptionIsRich());
1134 p.restore();
1135 }
1136 }
1137 if (mShowNoteLines) {
1138 drawNoteLines(p, box, box.y() + textY);
1139 }
1140
1141 p.setFont(oldFont);
1142}
1143
1144void CalPrintPluginBase::drawIncidence(QPainter &p,
1145 QRect dayBox,
1146 const QString &time,
1147 const QString &summary,
1148 const QString &description,
1149 int &textY,
1150 bool singleLineLimit,
1151 bool includeDescription,
1152 bool richDescription)
1153{
1154 qCDebug(CALENDARSUPPORT_LOG) << "summary =" << summary << ", singleLineLimit=" << singleLineLimit;
1155
1156 int flags = Qt::AlignLeft | Qt::OpaqueMode;
1157 QFontMetrics fm = p.fontMetrics();
1158 const int borderWidth = p.pen().width() + 1;
1159
1160 QString firstLine{time};
1161 if (!firstLine.isEmpty()) {
1162 firstLine += QStringLiteral(" ");
1163 }
1164 firstLine += summary;
1165
1166 if (singleLineLimit) {
1167 if (includeDescription && !description.isEmpty()) {
1168 firstLine += QStringLiteral(". ") + toPlainText(description);
1169 }
1170
1171 int totalHeight = fm.height() + borderWidth;
1172 int textBoxHeight = (totalHeight > (dayBox.height() - textY)) ? dayBox.height() - textY : totalHeight;
1173 QRect boxRect(dayBox.x() + p.pen().width(), dayBox.y() + textY, dayBox.width(), textBoxHeight);
1174 drawBox(p, 1, boxRect);
1175 p.drawText(boxRect.adjusted(3, 0, -3, 0), flags, firstLine);
1176 textY += textBoxHeight;
1177 } else {
1178 QTextDocument textDoc;
1179 QTextCursor textCursor(&textDoc);
1180 textCursor.insertText(firstLine);
1181 if (includeDescription && !description.isEmpty()) {
1182 textCursor.insertText(QStringLiteral("\n"));
1183 if (richDescription) {
1184 textCursor.insertHtml(description);
1185 } else {
1186 textCursor.insertText(toPlainText(description));
1187 }
1188 }
1189
1190 QRect textBox = QRect(dayBox.x(), dayBox.y() + textY + 1, dayBox.width(), dayBox.height() - textY);
1191 textDoc.setPageSize(QSize(textBox.width(), textBox.height()));
1192
1193 textBox.setHeight(textDoc.documentLayout()->documentSize().height());
1194 if (textBox.bottom() > dayBox.bottom()) {
1195 textBox.setBottom(dayBox.bottom());
1196 }
1197
1198 QRect boxRext(dayBox.x() + p.pen().width(), dayBox.y() + textY, dayBox.width(), textBox.height());
1199 drawBox(p, 1, boxRext);
1200
1201 QRect clipRect(0, 0, textBox.width(), textBox.height());
1202 QAbstractTextDocumentLayout::PaintContext ctx;
1203 ctx.palette.setColor(QPalette::Text, p.pen().color());
1204 ctx.clip = clipRect;
1205 p.save();
1206 p.translate(textBox.x(), textBox.y());
1207 p.setClipRect(clipRect);
1208 textDoc.documentLayout()->draw(&p, ctx);
1209 p.restore();
1210
1211 textY += textBox.height();
1212
1213 if (textDoc.pageCount() > 1) {
1214 // show that we have overflowed the box
1215 QPolygon poly(3);
1216 int x = dayBox.x() + dayBox.width();
1217 int y = dayBox.y() + dayBox.height();
1218 poly.setPoint(0, x - 10, y);
1219 poly.setPoint(1, x, y - 10);
1220 poly.setPoint(2, x, y);
1221 QBrush oldBrush(p.brush());
1222 p.setBrush(QBrush(Qt::black));
1223 p.drawPolygon(poly);
1224 p.setBrush(oldBrush);
1225 textY = dayBox.height();
1226 }
1227 }
1228}
1229
1230class MonthEventStruct
1231{
1232public:
1233 MonthEventStruct()
1234 : event(nullptr)
1235 {
1236 }
1237
1238 MonthEventStruct(const QDateTime &s, const QDateTime &e, const KCalendarCore::Event::Ptr &ev)
1239 : start(s)
1240 , end(e)
1241 , event(ev)
1242 {
1243 if (event->allDay()) {
1244 start = QDateTime(start.date(), QTime(0, 0, 0));
1245 end = QDateTime(end.date().addDays(1), QTime(0, 0, 0)).addSecs(-1);
1246 }
1247 }
1248
1249 bool operator<(const MonthEventStruct &mes)
1250 {
1251 return start < mes.start;
1252 }
1253
1254 QDateTime start;
1255 QDateTime end;
1257};
1258
1259void CalPrintPluginBase::drawMonth(QPainter &p, QDate dt, QRect box, int maxdays, int subDailyFlags, int holidaysFlags)
1260{
1261 p.save();
1262 QRect subheaderBox(box);
1263 subheaderBox.setHeight(subHeaderHeight());
1264 QRect borderBox(box);
1265 borderBox.setTop(subheaderBox.bottom() + 1);
1266 drawSubHeaderBox(p, QLocale().standaloneMonthName(dt.month()), subheaderBox);
1267 // correct for half the border width
1268 int correction = (BOX_BORDER_WIDTH /*-1*/) / 2;
1269 QRect daysBox(borderBox);
1270 daysBox.adjust(correction, correction, -correction, -correction);
1271
1272 int daysinmonth = dt.daysInMonth();
1273 if (maxdays <= 0) {
1274 maxdays = daysinmonth;
1275 }
1276
1277 float dayheight = float(daysBox.height()) / float(maxdays);
1278
1279 QColor holidayColor(240, 240, 240);
1280 QColor workdayColor(255, 255, 255);
1281 int dayNrWidth = p.fontMetrics().boundingRect(QStringLiteral("99")).width();
1282
1283 // Fill the remaining space (if a month has less days than others) with a crossed-out pattern
1284 if (daysinmonth < maxdays) {
1285 QRect dayBox(box.left(), daysBox.top() + qRound(dayheight * daysinmonth), box.width(), 0);
1286 dayBox.setBottom(daysBox.bottom());
1287 p.fillRect(dayBox, Qt::DiagCrossPattern);
1288 }
1289 // Backgrounded boxes for each day, plus day numbers
1290 QBrush oldbrush(p.brush());
1291
1292 QList<QDate> workDays;
1293
1294 {
1295 QDate startDate(dt.year(), dt.month(), 1);
1296 QDate endDate(dt.year(), dt.month(), daysinmonth);
1297
1298 workDays = CalendarSupport::workDays(startDate, endDate);
1299 }
1300
1301 for (int d = 0; d < daysinmonth; ++d) {
1302 QDate day(dt.year(), dt.month(), d + 1);
1303 QRect dayBox(daysBox.left() /*+rand()%50*/, daysBox.top() + qRound(dayheight * d), daysBox.width() /*-rand()%50*/, 0);
1304 // FIXME: When using a border width of 0 for event boxes,
1305 // don't let the rectangles overlap, i.e. subtract 1 from the top or bottom!
1306 dayBox.setBottom(daysBox.top() + qRound(dayheight * (d + 1)) - 1);
1307
1308 p.setBrush(workDays.contains(day) ? workdayColor : holidayColor);
1309 p.drawRect(dayBox);
1310 QRect dateBox(dayBox);
1311 dateBox.setWidth(dayNrWidth + 3);
1313 }
1314 p.setBrush(oldbrush);
1315 int xstartcont = box.left() + dayNrWidth + 5;
1316
1317 QDate start(dt.year(), dt.month(), 1);
1318 QDate end = start.addMonths(1);
1319 end = end.addDays(-1);
1320
1321 const KCalendarCore::Event::List events = mCalendar->events(start, end);
1322 QMap<int, QStringList> textEvents;
1323 QList<CellItem *> timeboxItems;
1324
1325 // 1) For multi-day events, show boxes spanning several cells, use CellItem
1326 // print the summary vertically
1327 // 2) For sub-day events, print the concated summaries into the remaining
1328 // space of the box (optional, depending on the given flags)
1329 // 3) Draw some kind of timeline showing free and busy times
1330
1331 // Holidays
1332 // QList<KCalendarCore::Event::Ptr> holidays;
1333 for (QDate d(start); d <= end; d = d.addDays(1)) {
1334 KCalendarCore::Event::Ptr e = holidayEvent(d);
1335 if (e) {
1336 // holidays.append(e);
1337 if (holidaysFlags & TimeBoxes) {
1338 timeboxItems.append(new PrintCellItem(e, QDateTime(d, QTime(0, 0, 0)), QDateTime(d.addDays(1), QTime(0, 0, 0))));
1339 }
1340 if (holidaysFlags & Text) {
1341 textEvents[d.day()] << e->summary();
1342 }
1343 }
1344 }
1345
1346 QList<MonthEventStruct> monthentries;
1347
1348 for (const KCalendarCore::Event::Ptr &e : std::as_const(events)) {
1351 continue;
1352 }
1353 if (e->recurs()) {
1354 if (e->recursOn(start, QTimeZone::systemTimeZone())) {
1355 // This occurrence has possibly started before the beginning of the
1356 // month, so obtain the start date before the beginning of the month
1357 QList<QDateTime> starttimes = e->startDateTimesForDate(start, QTimeZone::systemTimeZone());
1358 for (auto it = starttimes.constBegin(); it != starttimes.constEnd(); ++it) {
1359 monthentries.append(MonthEventStruct((*it).toLocalTime(), e->endDateForStart(*it).toLocalTime(), e));
1360 }
1361 }
1362 // Loop through all remaining days of the month and check if the event
1363 // begins on that day (don't use Event::recursOn, as that will
1364 // also return events that have started earlier. These start dates
1365 // however, have already been treated!
1366 KCalendarCore::Recurrence *recur = e->recurrence();
1367 QDate d1(start.addDays(1));
1368 while (d1 <= end) {
1369 if (recur->recursOn(d1, QTimeZone::systemTimeZone())) {
1370 KCalendarCore::TimeList times(recur->recurTimesOn(d1, QTimeZone::systemTimeZone()));
1371 for (KCalendarCore::TimeList::ConstIterator it = times.constBegin(); it != times.constEnd(); ++it) {
1372 QDateTime d1start(d1, *it, QTimeZone::LocalTime);
1373 monthentries.append(MonthEventStruct(d1start, e->endDateForStart(d1start).toLocalTime(), e));
1374 }
1375 }
1376 d1 = d1.addDays(1);
1377 }
1378 } else {
1379 monthentries.append(MonthEventStruct(e->dtStart().toLocalTime(), e->dtEnd().toLocalTime(), e));
1380 }
1381 }
1382
1383 // TODO: to port the month entries sorting
1384
1385 // qSort( monthentries.begin(), monthentries.end() );
1386
1388 QDateTime endofmonth(end, QTime(0, 0, 0));
1389 endofmonth = endofmonth.addDays(1);
1390 for (; mit != monthentries.constEnd(); ++mit) {
1391 if ((*mit).start.date() == (*mit).end.date()) {
1392 // Show also single-day events as time line boxes
1393 if (subDailyFlags & TimeBoxes) {
1394 timeboxItems.append(new PrintCellItem((*mit).event, (*mit).start, (*mit).end));
1395 }
1396 // Show as text in the box
1397 if (subDailyFlags & Text) {
1398 textEvents[(*mit).start.date().day()] << (*mit).event->summary();
1399 }
1400 } else {
1401 // Multi-day events are always shown as time line boxes
1402 QDateTime thisstart((*mit).start);
1403 QDateTime thisend((*mit).end);
1404 if (thisstart.date() < start) {
1405 thisstart.setDate(start);
1406 }
1407 if (thisend > endofmonth) {
1408 thisend = endofmonth;
1409 }
1410 timeboxItems.append(new PrintCellItem((*mit).event, thisstart, thisend));
1411 }
1412 }
1413
1414 // For Multi-day events, line them up nicely so that the boxes don't overlap
1415 QListIterator<CellItem *> it1(timeboxItems);
1416 while (it1.hasNext()) {
1417 CellItem *placeItem = it1.next();
1418 CellItem::placeItem(timeboxItems, placeItem);
1419 }
1420 QDateTime starttime(start, QTime(0, 0, 0));
1421 int newxstartcont = xstartcont;
1422
1423 QFont oldfont(p.font());
1424 p.setFont(QFont(QStringLiteral("sans-serif"), 7));
1425 while (it1.hasNext()) {
1426 auto placeItem = static_cast<PrintCellItem *>(it1.next());
1427 int minsToStart = starttime.secsTo(placeItem->start()) / 60;
1428 int minsToEnd = starttime.secsTo(placeItem->end()) / 60;
1429
1430 QRect eventBox(xstartcont + placeItem->subCell() * 17,
1431 daysBox.top() + qRound(double(minsToStart * daysBox.height()) / double(maxdays * 24 * 60)),
1432 14,
1433 0);
1434 eventBox.setBottom(daysBox.top() + qRound(double(minsToEnd * daysBox.height()) / double(maxdays * 24 * 60)));
1435 drawVerticalBox(p, 0, eventBox, placeItem->event()->summary());
1436 newxstartcont = qMax(newxstartcont, eventBox.right());
1437 }
1438 xstartcont = newxstartcont;
1439
1440 // For Single-day events, simply print their summaries into the remaining
1441 // space of the day's cell
1442 for (int d = 0; d < daysinmonth; ++d) {
1443 QStringList dayEvents(textEvents[d + 1]);
1444 QString txt = dayEvents.join(", "_L1);
1445 QRect dayBox(xstartcont, daysBox.top() + qRound(dayheight * d), 0, 0);
1446 dayBox.setRight(box.right());
1447 dayBox.setBottom(daysBox.top() + qRound(dayheight * (d + 1)));
1449 }
1450 p.setFont(oldfont);
1451 drawBox(p, BOX_BORDER_WIDTH, borderBox);
1452 p.restore();
1453}
1454
1456 QDate qd,
1457 QTime fromTime,
1458 QTime toTime,
1459 bool weeknumbers,
1460 bool recurDaily,
1461 bool recurWeekly,
1462 bool singleLineLimit,
1463 bool includeDescription,
1464 bool includeCategories,
1465 QRect box)
1466{
1467 int yoffset = mSubHeaderHeight;
1468 int xoffset = 0;
1469 QDate monthDate(QDate(qd.year(), qd.month(), 1));
1470 QDate monthFirst(monthDate);
1471 QDate monthLast(monthDate.addMonths(1).addDays(-1));
1472
1473 int weekdayCol = weekdayColumn(monthDate.dayOfWeek());
1474 monthDate = monthDate.addDays(-weekdayCol);
1475
1476 if (weeknumbers) {
1477 xoffset += 14;
1478 }
1479
1480 int rows = (weekdayCol + qd.daysInMonth() - 1) / 7 + 1;
1481 double cellHeight = (box.height() - yoffset) / (1. * rows);
1482 double cellWidth = (box.width() - xoffset) / 7.;
1483
1484 // Precalculate the grid...
1485 // rows is at most 6, so using 8 entries in the array is fine, too!
1486 int coledges[8];
1487 int rowedges[8];
1488 for (int i = 0; i <= 7; ++i) {
1489 rowedges[i] = int(box.top() + yoffset + i * cellHeight);
1490 coledges[i] = int(box.left() + xoffset + i * cellWidth);
1491 }
1492
1493 if (weeknumbers) {
1494 QFont oldFont(p.font());
1495 QFont newFont(p.font());
1496 newFont.setPointSize(6);
1497 p.setFont(newFont);
1498 QDate weekDate(monthDate);
1499 for (int row = 0; row < rows; ++row) {
1500 int calWeek = weekDate.weekNumber();
1501 QRect rc(box.left(), rowedges[row], coledges[0] - 3 - box.left(), rowedges[row + 1] - rowedges[row]);
1503 weekDate = weekDate.addDays(7);
1504 }
1505 p.setFont(oldFont);
1506 }
1507
1508 QRect daysOfWeekBox(box);
1509 daysOfWeekBox.setHeight(mSubHeaderHeight);
1510 daysOfWeekBox.setLeft(box.left() + xoffset);
1511 drawDaysOfWeek(p, monthDate, monthDate.addDays(6), daysOfWeekBox);
1512
1513 QColor back = p.background().color();
1514 bool darkbg = false;
1515 for (int row = 0; row < rows; ++row) {
1516 for (int col = 0; col < 7; ++col) {
1517 // show days from previous/next month with a grayed background
1518 if ((monthDate < monthFirst) || (monthDate > monthLast)) {
1519 p.setBackground(back.darker(120));
1520 darkbg = true;
1521 }
1522 QRect dayBox(coledges[col], rowedges[row], coledges[col + 1] - coledges[col], rowedges[row + 1] - rowedges[row]);
1523 drawDayBox(p, monthDate, fromTime, toTime, dayBox, false, recurDaily, recurWeekly, singleLineLimit, includeDescription, includeCategories);
1524 if (darkbg) {
1525 p.setBackground(back);
1526 darkbg = false;
1527 }
1528 monthDate = monthDate.addDays(1);
1529 }
1530 }
1531}
1532
1533void CalPrintPluginBase::drawTodoLines(QPainter &p,
1534 const QString &entry,
1535 int x,
1536 int &y,
1537 int width,
1538 int pageHeight,
1539 bool richTextEntry,
1540 QList<TodoParentStart *> &startPoints,
1541 bool connectSubTodos)
1542{
1543 QString plainEntry = (richTextEntry) ? toPlainText(entry) : entry;
1544
1545 QRect textrect(0, 0, width, -1);
1546 int flags = Qt::AlignLeft;
1547 QFontMetrics fm = p.fontMetrics();
1548
1549 QStringList lines = plainEntry.split(QLatin1Char('\n'));
1550 for (int currentLine = 0; currentLine < lines.count(); currentLine++) {
1551 // split paragraphs into lines
1552 KWordWrap ww = KWordWrap::formatText(fm, textrect, flags, lines[currentLine]);
1553 QStringList textLine = ww.wrappedString().split(QLatin1Char('\n'));
1554
1555 // print each individual line
1556 for (int lineCount = 0; lineCount < textLine.count(); lineCount++) {
1557 if (y >= pageHeight) {
1558 if (connectSubTodos) {
1559 for (int i = 0; i < startPoints.size(); ++i) {
1560 TodoParentStart *rct;
1561 rct = startPoints.at(i);
1562 int start = rct->mRect.bottom() + 1;
1563 int center = rct->mRect.left() + (rct->mRect.width() / 2);
1564 int to = y;
1565 if (!rct->mSamePage) {
1566 start = 0;
1567 }
1568 if (rct->mHasLine) {
1569 p.drawLine(center, start, center, to);
1570 }
1571 rct->mSamePage = false;
1572 }
1573 }
1574 y = 0;
1575 mPrinter->newPage();
1576 }
1577 y += fm.height();
1578 p.drawText(x, y, textLine[lineCount]);
1579 }
1580 }
1581}
1582
1584 const KCalendarCore::Todo::Ptr &todo,
1585 QPainter &p,
1588 bool connectSubTodos,
1589 bool strikeoutCompleted,
1590 bool desc,
1591 int posPriority,
1592 int posSummary,
1593 int posCategories,
1594 int posStartDt,
1595 int posDueDt,
1596 int posPercentComplete,
1597 int level,
1598 int x,
1599 int &y,
1600 int width,
1601 int pageHeight,
1602 const KCalendarCore::Todo::List &todoList,
1603 TodoParentStart *r)
1604{
1605 QString outStr;
1606 const auto locale = QLocale::system();
1607 QRect rect;
1608 TodoParentStart startpt;
1609 // This list keeps all starting points of the parent to-dos so the connection
1610 // lines of the tree can easily be drawn (needed if a new page is started)
1611 static QList<TodoParentStart *> startPoints;
1612 if (level < 1) {
1613 startPoints.clear();
1614 }
1615
1616 // Don't print confidential or private items if so configured (sub-items are also ignored!)
1618 || (mExcludePrivate && todo->secrecy() == KCalendarCore::Incidence::SecrecyPrivate)) {
1619 return;
1620 }
1621
1622 QFontMetrics fm = p.fontMetrics();
1623 y += 10;
1624 // Start a new page if the item does not fit on the page any more (only
1625 // first line is checked! Word-wrapped summaries might still overflow!)
1626 if (y + fm.height() >= pageHeight) {
1627 y = 0;
1628 mPrinter->newPage();
1629 // reset the parent start points to indicate not on same page
1630 for (int i = 0; i < startPoints.size(); ++i) {
1631 TodoParentStart *rct;
1632 rct = startPoints.at(i);
1633 rct->mSamePage = false;
1634 }
1635 }
1636
1637 int left = posSummary + (level * 10);
1638
1639 // If this is a sub-to-do, r will not be 0, and we want the LH side
1640 // of the priority line up to the RH side of the parent to-do's priority
1641 int lhs = posPriority;
1642 if (r) {
1643 lhs = r->mRect.right() + 1;
1644 }
1645
1646 outStr.setNum(todo->priority());
1647 rect = p.boundingRect(lhs, y + 10, 5, -1, Qt::AlignCenter, outStr);
1648 // Make it a more reasonable size
1649 rect.setWidth(18);
1650 rect.setHeight(18);
1651 const int top = rect.top();
1652
1653 // Draw a checkbox
1655 p.drawRect(rect);
1656 if (todo->isCompleted()) {
1657 // cross out the rectangle for completed to-dos
1658 p.drawLine(rect.topLeft(), rect.bottomRight());
1659 p.drawLine(rect.topRight(), rect.bottomLeft());
1660 }
1661 lhs = rect.right() + 5;
1662
1663 // Priority
1664 if (posPriority >= 0 && todo->priority() > 0) {
1665 p.drawText(rect, Qt::AlignCenter, outStr);
1666 }
1667 startpt.mRect = rect; // save for later
1668
1669 // Connect the dots
1670 if (r && level > 0 && connectSubTodos) {
1671 int bottom;
1672 int center(r->mRect.left() + (r->mRect.width() / 2));
1673 int to(rect.top() + (rect.height() / 2));
1674 int endx(rect.left());
1675 p.drawLine(center, to, endx, to); // side connector
1676 if (r->mSamePage) {
1677 bottom = r->mRect.bottom() + 1;
1678 } else {
1679 bottom = 0;
1680 }
1681 p.drawLine(center, bottom, center, to);
1682 }
1683
1684 int posSoFar = width; // Position of leftmost optional field.
1685
1686 // due date
1687 if (posDueDt >= 0 && todo->hasDueDate()) {
1688 outStr = locale.toString(todo->dtDue().toLocalTime().date(), QLocale::ShortFormat);
1689 rect = p.boundingRect(posDueDt, top, x + width, -1, Qt::AlignTop | Qt::AlignLeft, outStr);
1690 p.drawText(rect, Qt::AlignTop | Qt::AlignLeft, outStr);
1691 posSoFar = posDueDt;
1692 }
1693
1694 // start date
1695 if (posStartDt >= 0 && todo->hasStartDate()) {
1696 outStr = locale.toString(todo->dtStart().toLocalTime().date(), QLocale::ShortFormat);
1697 rect = p.boundingRect(posStartDt, top, x + width, -1, Qt::AlignTop | Qt::AlignLeft, outStr);
1698 p.drawText(rect, Qt::AlignTop | Qt::AlignLeft, outStr);
1699 posSoFar = posStartDt;
1700 }
1701
1702 // percentage completed
1703 if (posPercentComplete >= 0) {
1704 int lwidth = 24;
1705 int lheight = p.fontMetrics().ascent();
1706 // first, draw the progress bar
1707 int progress = static_cast<int>(((lwidth * todo->percentComplete()) / 100.0 + 0.5));
1708
1710 p.drawRect(posPercentComplete, top, lwidth, lheight);
1711 if (progress > 0) {
1712 p.setBrush(QColor(128, 128, 128));
1713 p.drawRect(posPercentComplete, top, progress, lheight);
1714 }
1715
1716 // now, write the percentage
1717 outStr = i18n("%1%", todo->percentComplete());
1718 rect = p.boundingRect(posPercentComplete + lwidth + 3, top, x + width, -1, Qt::AlignTop | Qt::AlignLeft, outStr);
1719 p.drawText(rect, Qt::AlignTop | Qt::AlignLeft, outStr);
1720 posSoFar = posPercentComplete;
1721 }
1722
1723 // categories
1724 QRect categoriesRect{0, 0, 0, 0};
1725 if (posCategories >= 0) {
1726 outStr = todo->categoriesStr();
1727 outStr.replace(QLatin1Char(','), QLatin1Char('\n'));
1728 rect = p.boundingRect(posCategories, top, posSoFar - posCategories, -1, Qt::TextWordWrap, outStr);
1729 p.drawText(rect, Qt::TextWordWrap, outStr, &categoriesRect);
1730 posSoFar = posCategories;
1731 }
1732
1733 // summary
1734 outStr = todo->summary();
1735 rect = p.boundingRect(lhs, top, posSoFar - lhs - 5, -1, Qt::TextWordWrap, outStr);
1736 QFont oldFont(p.font());
1737 if (strikeoutCompleted && todo->isCompleted()) {
1738 QFont newFont(p.font());
1739 newFont.setStrikeOut(true);
1740 p.setFont(newFont);
1741 }
1742 QRect summaryRect;
1743 p.drawText(rect, Qt::TextWordWrap, outStr, &summaryRect);
1744 p.setFont(oldFont);
1745
1746 y = std::max(categoriesRect.bottom(), summaryRect.bottom());
1747
1748 // description
1749 if (desc && !todo->description().isEmpty()) {
1750 drawTodoLines(p, todo->description(), left, y, width - (left + 10 - x), pageHeight, todo->descriptionIsRich(), startPoints, connectSubTodos);
1751 }
1752
1753 // Make a list of all the sub-to-dos related to this to-do.
1755 for (const KCalendarCore::Incidence::Ptr &incidence : mCalendar->incidences()) {
1756 // In the future, to-dos might also be related to events
1757 // Manually check if the sub-to-do is in the list of to-dos to print
1758 // The problem is that relations() does not apply filters, so
1759 // we need to compare manually with the complete filtered list!
1761 if (!subtodo) {
1762 continue;
1763 }
1764
1765 if (subtodo->relatedTo() != todo->uid()) {
1766 continue;
1767 }
1768
1769#ifdef AKONADI_PORT_DISABLED
1770 if (subtodo && todoList.contains(subtodo)) {
1771#else
1772 bool subtodoOk = false;
1773 for (const KCalendarCore::Todo::Ptr &tt : std::as_const(todoList)) {
1774 if (tt == subtodo) {
1775 subtodoOk = true;
1776 break;
1777 }
1778 }
1779
1780 if (subtodoOk) {
1781#endif
1782 t.append(subtodo);
1783 }
1784 }
1785
1786 // has sub-todos?
1787 startpt.mHasLine = (t.size() > 0);
1788 startPoints.append(&startpt);
1789
1790 // Sort the sub-to-dos and print them
1791#ifdef AKONADI_PORT_DISABLED
1792 KCalendarCore::Todo::List sl = mCalendar->sortTodos(&t, sortField, sortDir);
1793#else
1795 tl.reserve(t.count());
1796 for (const KCalendarCore::Todo::Ptr &todo : std::as_const(t)) {
1797 tl.append(todo);
1798 }
1799 KCalendarCore::Todo::List sl = mCalendar->sortTodos(std::move(tl), sortField, sortDir);
1800#endif
1801
1802 int subcount = 0;
1803 for (const KCalendarCore::Todo::Ptr &isl : std::as_const(sl)) {
1804 count++;
1805 if (++subcount == sl.size()) {
1806 startpt.mHasLine = false;
1807 }
1808 drawTodo(count,
1809 isl,
1810 p,
1811 sortField,
1812 sortDir,
1813 connectSubTodos,
1814 strikeoutCompleted,
1815 desc,
1816 posPriority,
1817 posSummary,
1818 posCategories,
1819 posStartDt,
1820 posDueDt,
1821 posPercentComplete,
1822 level + 1,
1823 x,
1824 y,
1825 width,
1826 pageHeight,
1827 todoList,
1828 &startpt);
1829 }
1830 startPoints.removeAll(&startpt);
1831}
1832
1834{
1835 int w = weekday + 7 - QLocale().firstDayOfWeek();
1836 return w % 7;
1837}
1838
1839void CalPrintPluginBase::drawTextLines(QPainter &p, const QString &entry, int x, int &y, int width, int pageHeight, bool richTextEntry)
1840{
1841 QString plainEntry = (richTextEntry) ? toPlainText(entry) : entry;
1842
1843 QRect textrect(0, 0, width, -1);
1844 int flags = Qt::AlignLeft;
1845 QFontMetrics fm = p.fontMetrics();
1846
1847 QStringList lines = plainEntry.split(QLatin1Char('\n'));
1848 for (int currentLine = 0; currentLine < lines.count(); currentLine++) {
1849 // split paragraphs into lines
1850 KWordWrap ww = KWordWrap::formatText(fm, textrect, flags, lines[currentLine]);
1851 QStringList textLine = ww.wrappedString().split(QLatin1Char('\n'));
1852 // print each individual line
1853 for (int lineCount = 0; lineCount < textLine.count(); lineCount++) {
1854 y += fm.height();
1855 if (y >= pageHeight) {
1856 if (mPrintFooter) {
1857 drawFooter(p, {0, pageHeight, width, footerHeight()});
1858 }
1859 y = fm.height();
1860 mPrinter->newPage();
1861 }
1862 p.drawText(x, y, textLine[lineCount]);
1863 }
1864 }
1865}
1866
1867void CalPrintPluginBase::drawSplitHeaderRight(QPainter &p, QDate fd, QDate td, QDate, int width, int height)
1868{
1869 QFont oldFont(p.font());
1870
1871 QPen oldPen(p.pen());
1872 QPen pen(Qt::black, 4);
1873
1874 QString title;
1875 QLocale locale;
1876 if (fd.month() == td.month()) {
1877 title = i18nc("Date range: Month dayStart - dayEnd",
1878 "%1 %2\u2013%3",
1879 locale.monthName(fd.month(), QLocale::LongFormat),
1880 locale.toString(fd, QStringLiteral("dd")),
1881 locale.toString(td, QStringLiteral("dd")));
1882 } else {
1883 title = i18nc("Date range: monthStart dayStart - monthEnd dayEnd",
1884 "%1 %2\u2013%3 %4",
1885 locale.monthName(fd.month(), QLocale::LongFormat),
1886 locale.toString(fd, QStringLiteral("dd")),
1887 locale.monthName(td.month(), QLocale::LongFormat),
1888 locale.toString(td, QStringLiteral("dd")));
1889 }
1890
1891 if (height < 60) {
1892 p.setFont(QFont(QStringLiteral("Times"), 22));
1893 } else {
1894 p.setFont(QFont(QStringLiteral("Times"), 28));
1895 }
1896
1897 int lineSpacing = p.fontMetrics().lineSpacing();
1898 p.drawText(0, 0, width, lineSpacing, Qt::AlignRight | Qt::AlignTop, title);
1899
1900 title.truncate(0);
1901
1902 p.setPen(pen);
1903 p.drawLine(300, lineSpacing, width, lineSpacing);
1904 p.setPen(oldPen);
1905
1906 if (height < 60) {
1907 p.setFont(QFont(QStringLiteral("Times"), 14, QFont::Bold, true));
1908 } else {
1909 p.setFont(QFont(QStringLiteral("Times"), 18, QFont::Bold, true));
1910 }
1911
1912 title += QString::number(fd.year());
1913 p.drawText(0, lineSpacing + padding(), width, lineSpacing, Qt::AlignRight | Qt::AlignTop, title);
1914
1915 p.setFont(oldFont);
1916}
1917
1919{
1920 int lineHeight = int(p.fontMetrics().lineSpacing() * 1.5);
1921 int linePos = box.y();
1922 int startPos = startY;
1923 // adjust line to start at multiple from top of box for alignment
1924 while (linePos < startPos) {
1925 linePos += lineHeight;
1926 }
1927 QPen oldPen(p.pen());
1929 while (linePos < box.bottom()) {
1930 p.drawLine(box.left() + padding(), linePos, box.right() - padding(), linePos);
1931 linePos += lineHeight;
1932 }
1933 p.setPen(oldPen);
1934}
1935
1936QString CalPrintPluginBase::toPlainText(const QString &htmlText)
1937{
1938 // this converts possible rich text to plain text
1940}
QColor tagColor(const QString &tagName) const
static TagCache * instance()
int drawHeader(QPainter &p, const QString &title, QDate month1, QDate month2, QRect box, bool expand=false, QColor backColor=QColor())
Draw the gray header bar of the printout to the QPainter.
int drawBoxWithCaption(QPainter &p, QRect box, const QString &caption, const QString &contents, bool sameLine, bool expand, const QFont &captionFont, const QFont &textFont, bool richContents=false)
Draw a component box with a heading (printed in bold).
int footerHeight() const
Returns the height of the page footer.
static void drawShadedBox(QPainter &p, int linewidth, const QBrush &brush, QRect rect)
Draw a shaded box with given width at the given coordinates.
bool mExcludePrivate
Whether or not to print incidences with secrecy "private".
void drawAgendaDayBox(QPainter &p, const KCalendarCore::Event::List &eventList, QDate qd, bool expandable, QTime fromTime, QTime toTime, QRect box, bool includeDescription, bool includeCategories, bool excludeTime, const QList< QDate > &workDays)
Draw the agenda box for the day print style (the box showing all events of that day).
void drawTodo(int &count, const KCalendarCore::Todo::Ptr &todo, QPainter &p, KCalendarCore::TodoSortField sortField, KCalendarCore::SortDirection sortDir, bool connectSubTodos, bool strikeoutCompleted, bool desc, int posPriority, int posSummary, int posCategories, int posStartDt, int posDueDt, int posPercentComplete, int level, int x, int &y, int width, int pageHeight, const KCalendarCore::Todo::List &todoList, TodoParentStart *r)
Draws single to-do and its (indented) sub-to-dos, optionally connects them by a tree-like line,...
bool mShowNoteLines
Whether or not to print horizontal lines in note areas.
virtual void print(QPainter &p, int width, int height)=0
Actually do the printing.
void drawVerticalBox(QPainter &p, int linewidth, QRect box, const QString &str, int flags=-1)
Draw an event box with vertical text.
static void drawBox(QPainter &p, int linewidth, QRect rect)
Draw a box with given width at the given coordinates.
void drawDaysOfWeekBox(QPainter &p, QDate qd, QRect box)
Draw a single weekday name in a box inside the given area of the painter.
void showEventBox(QPainter &p, int linewidth, QRect box, const KCalendarCore::Incidence::Ptr &incidence, const QString &str, int flags=-1)
Print the box for the given event with the given string.
void drawTextLines(QPainter &p, const QString &entry, int x, int &y, int width, int pageHeight, bool richTextEntry)
Draws text lines splitting on page boundaries.
int headerHeight() const
Returns the height of the page header.
void drawSmallMonth(QPainter &p, QDate qd, QRect box)
Draw a small calendar with the days of a month into the given area.
void drawMonth(QPainter &p, QDate dt, QRect box, int maxdays=-1, int subDailyFlags=TimeBoxes, int holidaysFlags=Text)
Draw a vertical representation of the month containing the date dt.
void drawSubHeaderBox(QPainter &p, const QString &str, QRect box)
Draw a subheader box with a shaded background and the given string.
void printEventString(QPainter &p, QRect box, const QString &str, int flags=-1)
Print the given string (event summary) in the given rectangle.
int drawFooter(QPainter &p, QRect box)
Draw a page footer containing the printing date and possibly other things, like a page number.
bool mExcludeConfidential
Whether or not to print incidences with secrecy "confidential".
void doSaveConfig() override
Save complete configuration.
void doPrint(QPrinter *printer) override
Start printing.
void doLoadConfig() override
Load complete configuration.
void drawDayBox(QPainter &p, QDate qd, QTime fromTime, QTime toTime, QRect box, bool fullDate=false, bool printRecurDaily=true, bool printRecurWeekly=true, bool singleLineLimit=true, bool includeDescription=false, bool includeCategories=false)
Draw the box containing a list of all events of the given day (with their times, of course).
void drawMonthTable(QPainter &p, QDate qd, QTime fromTime, QTime toTime, bool weeknumbers, bool recurDaily, bool recurWeekly, bool singleLineLimit, bool includeDescription, bool includeCategories, QRect box)
Draw the month table of the month containing the date qd.
void drawDaysOfWeek(QPainter &p, QDate fromDate, QDate toDate, QRect box)
Draw a horizontal bar with the weekday names of the given date range in the given area of the painter...
static int weekdayColumn(int weekday)
Determines the column of the given weekday ( 1=Monday, 7=Sunday ), taking the start of the week setti...
bool mPrintFooter
Whether or not to print a footer at the bottoms of pages.
bool useColors() const
HELPER FUNCTIONS.
void drawNoteLines(QPainter &p, QRect box, int startY)
Draws dotted lines for notes in a box.
void drawTimeLine(QPainter &p, QTime fromTime, QTime toTime, QRect box)
Draw a (vertical) time scale from time fromTime to toTime inside the given area of the painter.
bool mUseColors
Whether or not to use event category colors to draw the events.
QWidget * createConfigWidget(QWidget *) override
Returns widget for configuring the print format.
virtual QString info() const =0
Returns long description of print format.
virtual QString description() const =0
Returns short description of print format.
virtual QString groupName() const =0
Returns KConfig group name where store settings.
QPrinter * mPrinter
The printer object.
QSharedPointer< Event > Ptr
QSharedPointer< Incidence > Ptr
bool recursOn(const QDate &date, const QTimeZone &timeZone) const
TimeList recurTimesOn(const QDate &date, const QTimeZone &timeZone) const
QSharedPointer< Todo > Ptr
QList< Ptr > List
void writeEntry(const char *key, const char *value, WriteConfigFlags pFlags=Normal)
QString readEntry(const char *key, const char *aDefault=nullptr) const
static KWordWrap formatText(QFontMetrics &fm, const QRect &r, int flags, const QString &str, int len=-1)
QString wrappedString() const
Q_SCRIPTABLE QString start(QString train="")
Q_SCRIPTABLE Q_NOREPLY void start()
QString i18nc(const char *context, const char *text, const TYPE &arg...)
QString i18n(const char *text, const TYPE &arg...)
AKONADI_CALENDAR_EXPORT KCalendarCore::Incidence::Ptr incidence(const Akonadi::Item &item)
AKONADI_CALENDAR_EXPORT KCalendarCore::Event::Ptr event(const Akonadi::Item &item)
char * toString(const EngineQuery &query)
const QList< QKeySequence > & end()
bool operator<(const PosRange< Trait > &l, const PosRange< Trait > &r)
virtual QSizeF documentSize() const const=0
virtual void draw(QPainter *painter, const PaintContext &context)=0
const QColor & color() const const
int blue() const const
int green() const const
bool isValid() const const
int red() const const
QDate addDays(qint64 ndays) const const
QDate addMonths(int nmonths) const const
int day() const const
int dayOfWeek() const const
int daysInMonth() const const
qint64 daysTo(QDate d) const const
bool isValid(int year, int month, int day)
int month() const const
int weekNumber(int *yearNumber) const const
int year() const const
QDateTime addDays(qint64 ndays) const const
QDateTime currentDateTime()
QDate date() const const
bool isValid() const const
qint64 secsTo(const QDateTime &other) const const
void setDate(QDate date)
QTime time() const const
QDateTime toLocalTime() const const
void setBold(bool enable)
void setPointSize(int pointSize)
void setStrikeOut(bool enable)
int ascent() const const
QRect boundingRect(QChar ch) const const
QString elidedText(const QString &text, Qt::TextElideMode mode, int width, int flags) const const
int height() const const
int lineSpacing() const const
typedef ConstIterator
void append(QList< T > &&value)
const_reference at(qsizetype i) const const
void clear()
const_iterator constBegin() const const
const_iterator constEnd() const const
bool contains(const AT &value) const const
qsizetype count() const const
bool isEmpty() const const
qsizetype removeAll(const AT &t)
void reserve(qsizetype size)
qsizetype size() const const
bool hasNext() const const
const T & next()
Qt::DayOfWeek firstDayOfWeek() const const
QString monthName(int month, FormatType type) const const
QString standaloneDayName(int day, FormatType type) const const
QString standaloneMonthName(int month, FormatType type) const const
QLocale system()
QString toString(QDate date, FormatType format) const const
const QBrush & background() const const
bool begin(QPaintDevice *device)
QRect boundingRect(const QRect &rectangle, int flags, const QString &text)
const QBrush & brush() const const
void drawLine(const QLine &line)
void drawPolygon(const QPoint *points, int pointCount, Qt::FillRule fillRule)
void drawRect(const QRect &rectangle)
void drawText(const QPoint &position, const QString &text)
bool end()
void fillRect(const QRect &rectangle, QGradient::Preset preset)
const QFont & font() const const
QFontMetrics fontMetrics() const const
const QPen & pen() const const
void restore()
void rotate(qreal angle)
void save()
void setBackground(const QBrush &brush)
void setBrush(Qt::BrushStyle style)
void setClipRect(const QRect &rectangle, Qt::ClipOperation operation)
void setFont(const QFont &font)
void setPen(Qt::PenStyle style)
void setViewport(const QRect &rectangle)
void translate(const QPoint &offset)
QRect viewport() const const
QRect window() const const
QColor color() const const
void setWidth(int width)
int width() const const
void adjust(int dx1, int dy1, int dx2, int dy2)
QRect adjusted(int dx1, int dy1, int dx2, int dy2) const const
int bottom() const const
QPoint bottomLeft() const const
QPoint bottomRight() const const
int height() const const
int left() const const
int right() const const
void setBottom(int y)
void setHeight(int height)
void setLeft(int x)
void setRect(int x, int y, int width, int height)
void setRight(int x)
void setTop(int y)
void setWidth(int width)
int top() const const
QPoint topLeft() const const
QPoint topRight() const const
int width() const const
int x() const const
int y() const const
QSharedPointer< X > dynamicCast() const const
QSharedPointer< X > staticCast() const const
qreal height() const const
QString arg(Args &&... args) const const
void clear()
bool isEmpty() const const
QString number(double n, char format, int precision)
QString & replace(QChar before, QChar after, Qt::CaseSensitivity cs)
QString & setNum(double n, char format, int precision)
QStringList split(QChar sep, Qt::SplitBehavior behavior, Qt::CaseSensitivity cs) const const
QString toUpper() const const
void truncate(qsizetype position)
QString join(QChar separator) const const
AlignTop
OpaqueMode
DiagCrossPattern
ElideRight
TextWordWrap
QTextStream & center(QTextStream &stream)
QTextStream & left(QTextStream &stream)
QAbstractTextDocumentLayout * documentLayout() const const
int pageCount() const const
void setPageSize(const QSizeF &size)
void setDefaultFont(const QFont &font)
void setHtml(const QString &html)
void setPlainText(const QString &text)
QTextDocumentFragment fromHtml(const QString &text, const QTextDocument *resourceProvider)
QString toPlainText() const const
QTime addSecs(int s) const const
int hour() const const
bool isValid(int h, int m, int s, int ms)
int minute() const const
int secsTo(QTime t) const const
QTimeZone systemTimeZone()
This file is part of the KDE documentation.
Documentation copyright © 1996-2025 The KDE developers.
Generated on Fri May 2 2025 11:57:32 by doxygen 1.13.2 written by Dimitri van Heesch, © 1997-2006

KDE's Doxygen guidelines are available online.