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

KDE's Doxygen guidelines are available online.