1 /*
2 SPDX-FileCopyrightText: 1998 Preston Brown <pbrown@kde.org>
3 SPDX-FileCopyrightText: 2003 Reinhold Kainhofer <reinhold@kainhofer.com>
4 SPDX-FileCopyrightText: 2003 Cornelius Schumacher <schumacher@kde.org>
5 SPDX-FileCopyrightText: 2008 Ron Goodheart <rong.dev@gmail.com>
6 SPDX-FileCopyrightText: 2010-2021 Laurent Montel <montel@kde.org>
7 SPDX-FileCopyrightText: 2012-2013 Allen Winter <winter@kde.org>
8
9 SPDX-License-Identifier: GPL-2.0-or-later WITH Qt-Commercial-exception-1.0
10 */
11
12 #include "calprintdefaultplugins.h"
13 #include "kcalprefs.h"
14 #include "utils.h"
15
16 #include <cmath>
17
18 #include <Akonadi/Item>
19
20 #include <KCalendarCore/Visitor>
21
22 #include <KCalUtils/IncidenceFormatter>
23 #include <KCalUtils/Stringify>
24
25 #include <KConfigGroup>
26
27 #include <QPainter>
28
29 using namespace CalendarSupport;
30
31 /**************************************************************
32 * Print Incidence
33 **************************************************************/
34
CalPrintIncidence()35 CalPrintIncidence::CalPrintIncidence()
36 : CalPrintPluginBase()
37 {
38 }
39
~CalPrintIncidence()40 CalPrintIncidence::~CalPrintIncidence()
41 {
42 }
43
createConfigWidget(QWidget * w)44 QWidget *CalPrintIncidence::createConfigWidget(QWidget *w)
45 {
46 return new CalPrintIncidenceConfig(w);
47 }
48
readSettingsWidget()49 void CalPrintIncidence::readSettingsWidget()
50 {
51 auto cfg = dynamic_cast<CalPrintIncidenceConfig *>((QWidget *)mConfigWidget);
52 if (cfg) {
53 mUseColors = cfg->mColors->isChecked();
54 mPrintFooter = cfg->mPrintFooter->isChecked();
55 mShowOptions = cfg->mShowDetails->isChecked();
56 mShowSubitemsNotes = cfg->mShowSubitemsNotes->isChecked();
57 mShowAttendees = cfg->mShowAttendees->isChecked();
58 mShowAttachments = cfg->mShowAttachments->isChecked();
59 mShowNoteLines = cfg->mShowNoteLines->isChecked();
60 }
61 }
62
setSettingsWidget()63 void CalPrintIncidence::setSettingsWidget()
64 {
65 auto cfg = dynamic_cast<CalPrintIncidenceConfig *>((QWidget *)mConfigWidget);
66 if (cfg) {
67 cfg->mColors->setChecked(mUseColors);
68 cfg->mPrintFooter->setChecked(mPrintFooter);
69 cfg->mShowDetails->setChecked(mShowOptions);
70 cfg->mShowSubitemsNotes->setChecked(mShowSubitemsNotes);
71 cfg->mShowAttendees->setChecked(mShowAttendees);
72 cfg->mShowAttachments->setChecked(mShowAttachments);
73 cfg->mShowNoteLines->setChecked(mShowNoteLines);
74 }
75 }
76
doLoadConfig()77 void CalPrintIncidence::doLoadConfig()
78 {
79 CalPrintPluginBase::doLoadConfig();
80 if (mConfig) {
81 KConfigGroup grp(mConfig, groupName());
82 mShowOptions = grp.readEntry("Show Options", false);
83 mShowSubitemsNotes = grp.readEntry("Show Subitems and Notes", false);
84 mShowAttendees = grp.readEntry("Use Attendees", false);
85 mShowAttachments = grp.readEntry("Use Attachments", false);
86 }
87 setSettingsWidget();
88 }
89
doSaveConfig()90 void CalPrintIncidence::doSaveConfig()
91 {
92 readSettingsWidget();
93 if (mConfig) {
94 KConfigGroup grp(mConfig, groupName());
95 grp.writeEntry("Show Options", mShowOptions);
96 grp.writeEntry("Show Subitems and Notes", mShowSubitemsNotes);
97 grp.writeEntry("Use Attendees", mShowAttendees);
98 grp.writeEntry("Use Attachments", mShowAttachments);
99 }
100 CalPrintPluginBase::doSaveConfig();
101 }
102
103 class TimePrintStringsVisitor : public KCalendarCore::Visitor
104 {
105 public:
TimePrintStringsVisitor()106 TimePrintStringsVisitor()
107 {
108 }
109
act(KCalendarCore::IncidenceBase::Ptr incidence)110 bool act(KCalendarCore::IncidenceBase::Ptr incidence)
111 {
112 return incidence->accept(*this, incidence);
113 }
114
115 QString mStartCaption, mStartString;
116 QString mEndCaption, mEndString;
117 QString mDurationCaption, mDurationString;
118
119 protected:
visit(const KCalendarCore::Event::Ptr & event)120 bool visit(const KCalendarCore::Event::Ptr &event) override
121 {
122 if (event->dtStart().isValid()) {
123 mStartCaption = i18n("Start date: ");
124 mStartString = KCalUtils::IncidenceFormatter::dateTimeToString(event->dtStart(), event->allDay(), false);
125 } else {
126 mStartCaption = i18n("No start date");
127 mStartString.clear();
128 }
129
130 if (event->hasEndDate()) {
131 mEndCaption = i18n("End date: ");
132 mEndString = KCalUtils::IncidenceFormatter::dateTimeToString(event->dtEnd(), event->allDay(), false);
133 } else if (event->hasDuration()) {
134 mEndCaption = i18n("Duration: ");
135 int mins = event->duration().asSeconds() / 60;
136 if (mins >= 60) {
137 mEndString += i18np("1 hour ", "%1 hours ", mins / 60);
138 }
139 if (mins % 60 > 0) {
140 mEndString += i18np("1 minute ", "%1 minutes ", mins % 60);
141 }
142 } else {
143 mEndCaption = i18n("No end date");
144 mEndString.clear();
145 }
146 return true;
147 }
148
visit(const KCalendarCore::Todo::Ptr & todo)149 bool visit(const KCalendarCore::Todo::Ptr &todo) override
150 {
151 if (todo->hasStartDate()) {
152 mStartCaption = i18n("Start date: ");
153 mStartString = KCalUtils::IncidenceFormatter::dateTimeToString(todo->dtStart(), todo->allDay(), false);
154 } else {
155 mStartCaption = i18n("No start date");
156 mStartString.clear();
157 }
158
159 if (todo->hasDueDate()) {
160 mEndCaption = i18n("Due date: ");
161 mEndString = KCalUtils::IncidenceFormatter::dateTimeToString(todo->dtDue(), todo->allDay(), false);
162 } else {
163 mEndCaption = i18n("No due date");
164 mEndString.clear();
165 }
166 return true;
167 }
168
visit(const KCalendarCore::Journal::Ptr & journal)169 bool visit(const KCalendarCore::Journal::Ptr &journal) override
170 {
171 mStartCaption = i18n("Start date: ");
172 mStartString = KCalUtils::IncidenceFormatter::dateTimeToString(journal->dtStart(), journal->allDay(), false);
173 mEndCaption.clear();
174 mEndString.clear();
175 return true;
176 }
177
visit(const KCalendarCore::FreeBusy::Ptr & fb)178 bool visit(const KCalendarCore::FreeBusy::Ptr &fb) override
179 {
180 Q_UNUSED(fb)
181 return true;
182 }
183 };
184
printCaptionAndText(QPainter & p,QRect box,const QString & caption,const QString & text,const QFont & captionFont,const QFont & textFont)185 int CalPrintIncidence::printCaptionAndText(QPainter &p, QRect box, const QString &caption, const QString &text, const QFont &captionFont, const QFont &textFont)
186 {
187 QFontMetrics captionFM(captionFont);
188 int textWd = captionFM.horizontalAdvance(caption);
189 QRect textRect(box);
190
191 QFont oldFont(p.font());
192 p.setFont(captionFont);
193 p.drawText(box, Qt::AlignLeft | Qt::AlignTop | Qt::TextSingleLine, caption);
194
195 if (!text.isEmpty()) {
196 textRect.setLeft(textRect.left() + textWd);
197 p.setFont(textFont);
198 p.drawText(textRect, Qt::AlignLeft | Qt::AlignTop | Qt::TextSingleLine, text);
199 }
200 p.setFont(oldFont);
201 return textRect.bottom();
202 }
203
print(QPainter & p,int width,int height)204 void CalPrintIncidence::print(QPainter &p, int width, int height)
205 {
206 QFont oldFont(p.font());
207 QFont textFont(QStringLiteral("sans-serif"), 11, QFont::Normal);
208 QFont captionFont(QStringLiteral("sans-serif"), 11, QFont::Bold);
209 p.setFont(textFont);
210 int lineHeight = p.fontMetrics().lineSpacing();
211 QString cap;
212 QString txt;
213
214 KCalendarCore::Incidence::List::ConstIterator it;
215 for (it = mSelectedIncidences.constBegin(); it != mSelectedIncidences.constEnd(); ++it) {
216 // don't do anything on a 0-pointer!
217 if (!(*it)) {
218 continue;
219 }
220 if (it != mSelectedIncidences.constBegin()) {
221 mPrinter->newPage();
222 }
223
224 const bool isJournal = ((*it)->type() == KCalendarCore::Incidence::TypeJournal);
225
226 // PAGE Layout (same for landscape and portrait! astonishingly, it looks good with both!):
227 // +-----------------------------------+
228 // | Header: Summary |
229 // +===================================+
230 // | start: ______ end: _________ |
231 // | repeats: ___________________ |
232 // | reminder: __________________ |
233 // +-----------------------------------+
234 // | Location: ______________________ |
235 // +------------------------+----------+
236 // | Description: | Notes or |
237 // | | Subitems |
238 // | | |
239 // | | |
240 // | | |
241 // | | |
242 // | | |
243 // | | |
244 // | | |
245 // | | |
246 // +------------------------+----------+
247 // | Attachments: | Settings |
248 // | | |
249 // +------------------------+----------+
250 // | Attendees: |
251 // | |
252 // +-----------------------------------+
253 // | Categories: _____________________ |
254 // +-----------------------------------+
255
256 QRect box(0, 0, width, height);
257 QRect titleBox(box);
258 titleBox.setHeight(headerHeight());
259 QColor headerColor = mUseColors ? categoryBgColor(*it) : QColor();
260 // Draw summary as header, no small calendars in title bar, expand height if needed
261 int titleBottom = drawHeader(p, (*it)->summary(), QDate(), QDate(), titleBox, true, headerColor);
262 titleBox.setBottom(titleBottom);
263
264 QRect timesBox(titleBox);
265 timesBox.setTop(titleBox.bottom() + padding());
266 timesBox.setHeight(height / 8);
267
268 TimePrintStringsVisitor stringVis;
269 int h = timesBox.top();
270 if (stringVis.act(*it)) {
271 QRect textRect(timesBox.left() + padding(), timesBox.top() + padding(), 0, lineHeight);
272 textRect.setRight(timesBox.center().x());
273 h = printCaptionAndText(p, textRect, stringVis.mStartCaption, stringVis.mStartString, captionFont, textFont);
274
275 textRect.setLeft(textRect.right());
276 textRect.setRight(timesBox.right() - padding());
277 h = qMax(printCaptionAndText(p, textRect, stringVis.mEndCaption, stringVis.mEndString, captionFont, textFont), h);
278 }
279
280 // Recurrence Printing
281 if ((*it)->recurs()) {
282 QRect recurBox(timesBox.left() + padding(), h + padding(), timesBox.right() - padding(), lineHeight);
283 KCalendarCore::Recurrence *recurs = (*it)->recurrence();
284 QString displayString = KCalUtils::IncidenceFormatter::recurrenceString((*it));
285 // exception dates
286 QString exceptString;
287 if (!recurs->exDates().isEmpty()) {
288 exceptString = i18nc("except for listed dates", " except");
289 for (int i = 0; i < recurs->exDates().size(); ++i) {
290 exceptString.append(QLatin1Char(' '));
291 exceptString.append(QLocale::system().toString(recurs->exDates().at(i), QLocale::ShortFormat));
292 }
293 }
294 displayString.append(exceptString);
295 h = qMax(printCaptionAndText(p, recurBox, i18n("Repeats: "), displayString, captionFont, textFont), h);
296 }
297
298 if (!isJournal) {
299 // Alarms Printing
300 QRect alarmBox(timesBox.left() + padding(), h + padding(), timesBox.right() - padding(), lineHeight);
301 KCalendarCore::Alarm::List alarms = (*it)->alarms();
302 if (alarms.isEmpty()) {
303 cap = i18n("No reminders");
304 txt.clear();
305 } else {
306 cap = i18np("Reminder: ", "%1 reminders: ", alarms.count());
307
308 QStringList alarmStrings;
309 KCalendarCore::Alarm::List::ConstIterator it;
310 alarmStrings.reserve(alarms.count());
311 for (it = alarms.constBegin(); it != alarms.constEnd(); ++it) {
312 KCalendarCore::Alarm::Ptr alarm = *it;
313
314 // Alarm offset, copied from koeditoralarms.cpp:
315 KLocalizedString offsetstr;
316 int offset = 0;
317 if (alarm->hasStartOffset()) {
318 offset = alarm->startOffset().asSeconds();
319 if (offset < 0) {
320 offsetstr = ki18nc("N days/hours/minutes before/after the start/end", "%1 before the start");
321 offset = -offset;
322 } else {
323 offsetstr = ki18nc("N days/hours/minutes before/after the start/end", "%1 after the start");
324 }
325 } else if (alarm->hasEndOffset()) {
326 offset = alarm->endOffset().asSeconds();
327 if (offset < 0) {
328 offsetstr = ki18nc("N days/hours/minutes before/after the start/end", "%1 before the end");
329 offset = -offset;
330 } else {
331 offsetstr = ki18nc("N days/hours/minutes before/after the start/end", "%1 after the end");
332 }
333 }
334
335 offset = offset / 60; // make minutes
336 int useoffset = 0;
337
338 if (offset % (24 * 60) == 0 && offset > 0) { // divides evenly into days?
339 useoffset = offset / (24 * 60);
340 offsetstr = offsetstr.subs(i18np("1 day", "%1 days", useoffset));
341 } else if (offset % 60 == 0 && offset > 0) { // divides evenly into hours?
342 useoffset = offset / 60;
343 offsetstr = offsetstr.subs(i18np("1 hour", "%1 hours", useoffset));
344 } else {
345 useoffset = offset;
346 offsetstr = offsetstr.subs(i18np("1 minute", "%1 minutes", useoffset));
347 }
348 alarmStrings << offsetstr.toString();
349 }
350 txt = alarmStrings.join(i18nc("Spacer for the joined list of categories/tags", ", "));
351 }
352 h = qMax(printCaptionAndText(p, alarmBox, cap, txt, captionFont, textFont), h);
353 }
354 QRect organizerBox(timesBox.left() + padding(), h + padding(), timesBox.right() - padding(), lineHeight);
355 h = qMax(printCaptionAndText(p, organizerBox, i18n("Organizer: "), (*it)->organizer().fullName(), captionFont, textFont), h);
356
357 // Finally, draw the frame around the time information...
358 timesBox.setBottom(qMax(timesBox.bottom(), h + padding()));
359 drawBox(p, BOX_BORDER_WIDTH, timesBox);
360
361 QRect locationBox(timesBox);
362 locationBox.setTop(timesBox.bottom() + padding());
363 locationBox.setHeight(0);
364 int locationBottom = 0;
365 if (!isJournal) {
366 locationBottom = drawBoxWithCaption(p,
367 locationBox,
368 i18n("Location: "),
369 (*it)->location(),
370 /*sameLine=*/true,
371 /*expand=*/true,
372 captionFont,
373 textFont);
374 }
375 locationBox.setBottom(locationBottom);
376
377 // Now start constructing the boxes from the bottom:
378 QRect footerBox(locationBox);
379 footerBox.setBottom(box.bottom());
380 footerBox.setTop(footerBox.bottom() - lineHeight - 2 * padding());
381
382 QRect categoriesBox(footerBox);
383 categoriesBox.setBottom(footerBox.top());
384 categoriesBox.setTop(categoriesBox.bottom() - lineHeight - 2 * padding());
385 QRect attendeesBox(box.left(), categoriesBox.top() - padding() - box.height() / 9, box.width(), box.height() / 9);
386 QRect attachmentsBox(box.left(), attendeesBox.top() - padding() - box.height() / 9, box.width() * 3 / 4 - padding(), box.height() / 9);
387 QRect optionsBox(isJournal ? box.left() : attachmentsBox.right() + padding(), attachmentsBox.top(), 0, 0);
388 optionsBox.setRight(box.right());
389 optionsBox.setBottom(attachmentsBox.bottom());
390 QRect notesBox(optionsBox.left(), isJournal ? (timesBox.bottom() + padding()) : (locationBox.bottom() + padding()), optionsBox.width(), 0);
391 notesBox.setBottom(optionsBox.top() - padding());
392 QRect descriptionBox(notesBox);
393 descriptionBox.setLeft(box.left());
394 descriptionBox.setRight(attachmentsBox.right());
395
396 // Adjust boxes depending on the show options...
397 if (!mShowSubitemsNotes || isJournal) {
398 descriptionBox.setRight(box.right());
399 }
400 if (!mShowAttachments || !mShowAttendees) {
401 descriptionBox.setBottom(attachmentsBox.bottom());
402 optionsBox.setTop(attendeesBox.top());
403 optionsBox.setBottom(attendeesBox.bottom());
404 notesBox.setBottom(attachmentsBox.bottom());
405 if (mShowOptions) {
406 attendeesBox.setRight(attachmentsBox.right());
407 }
408 if (!mShowAttachments && !mShowAttendees) {
409 if (mShowSubitemsNotes) {
410 descriptionBox.setBottom(attendeesBox.bottom());
411 }
412 if (!mShowOptions) {
413 descriptionBox.setBottom(attendeesBox.bottom());
414 notesBox.setBottom(attendeesBox.bottom());
415 }
416 }
417 }
418 if (mShowAttachments && !isJournal) {
419 if (!mShowOptions) {
420 attachmentsBox.setRight(box.right());
421 attachmentsBox.setRight(box.right());
422 }
423 if (!mShowAttendees) {
424 attachmentsBox.setTop(attendeesBox.top());
425 attachmentsBox.setBottom(attendeesBox.bottom());
426 }
427 }
428 int newBottom = drawBoxWithCaption(p,
429 descriptionBox,
430 i18n("Description:"),
431 (*it)->description(),
432 /*sameLine=*/false,
433 /*expand=*/false,
434 captionFont,
435 textFont,
436 (*it)->descriptionIsRich());
437 if (mShowNoteLines) {
438 drawNoteLines(p, descriptionBox, newBottom);
439 }
440
441 Akonadi::Item item = mCalendar->item((*it)->uid());
442 Akonadi::Item::List relations = mCalendar->childItems(item.id());
443
444 if (mShowSubitemsNotes && !isJournal) {
445 if (relations.isEmpty() || (*it)->type() != KCalendarCore::Incidence::TypeTodo) {
446 int notesPosition = drawBoxWithCaption(p,
447 notesBox,
448 i18n("Notes:"),
449 QString(),
450 /*sameLine=*/false,
451 /*expand=*/false,
452 captionFont,
453 textFont);
454 if (mShowNoteLines) {
455 drawNoteLines(p, notesBox, notesPosition);
456 }
457 } else {
458 QString subitemCaption;
459 if (relations.isEmpty()) {
460 subitemCaption = i18n("No Subitems");
461 txt.clear();
462 } else {
463 subitemCaption = i18np("1 Subitem:", "%1 Subitems:", relations.count());
464 }
465
466 QString subitemString;
467 QString statusString;
468 QString datesString;
469 int count = 0;
470 for (const Akonadi::Item &item : std::as_const(relations)) {
471 KCalendarCore::Todo::Ptr todo = CalendarSupport::todo(item);
472 ++count;
473 if (!todo) { // defensive, skip any zero pointers
474 continue;
475 }
476 // format the status
477 statusString = KCalUtils::Stringify::incidenceStatus(todo->status());
478 if (statusString.isEmpty()) {
479 if (todo->status() == KCalendarCore::Incidence::StatusNone) {
480 statusString = i18nc("no status", "none");
481 } else {
482 statusString = i18nc("unknown status", "unknown");
483 }
484 }
485 // format the dates if provided
486 datesString.clear();
487 if (todo->dtStart().isValid()) {
488 datesString +=
489 i18nc("subitem start date", "Start Date: %1\n", QLocale().toString(todo->dtStart().toLocalTime().date(), QLocale::ShortFormat));
490 if (!todo->allDay()) {
491 datesString +=
492 i18nc("subitem start time", "Start Time: %1\n", QLocale().toString(todo->dtStart().toLocalTime().time(), QLocale::ShortFormat));
493 }
494 }
495 if (todo->dateTime(KCalendarCore::Incidence::RoleEnd).isValid()) {
496 subitemString +=
497 i18nc("subitem due date",
498 "Due Date: %1\n",
499 QLocale().toString(todo->dateTime(KCalendarCore::Incidence ::RoleEnd).toLocalTime().date(), QLocale::ShortFormat));
500
501 if (!todo->allDay()) {
502 subitemString +=
503 i18nc("subitem due time",
504 "Due Time: %1\n",
505 QLocale().toString(todo->dateTime(KCalendarCore::Incidence::RoleEnd).toLocalTime().time(), QLocale::ShortFormat));
506 }
507 }
508 subitemString += i18nc("subitem counter", "%1: ", count);
509 subitemString += todo->summary();
510 subitemString += QLatin1Char('\n');
511 if (!datesString.isEmpty()) {
512 subitemString += datesString;
513 subitemString += QLatin1Char('\n');
514 }
515 subitemString += i18nc("subitem Status: statusString", "Status: %1\n", statusString);
516 subitemString += KCalUtils::IncidenceFormatter::recurrenceString(todo) + QLatin1Char('\n');
517 subitemString += i18nc("subitem Priority: N", "Priority: %1\n", QString::number(todo->priority()));
518 subitemString += i18nc("subitem Secrecy: secrecyString", "Secrecy: %1\n", KCalUtils::Stringify::incidenceSecrecy(todo->secrecy()));
519 subitemString += QLatin1Char('\n');
520 }
521 drawBoxWithCaption(p,
522 notesBox,
523 subitemCaption,
524 subitemString,
525 /*sameLine=*/false,
526 /*expand=*/false,
527 captionFont,
528 textFont);
529 }
530 }
531
532 if (mShowAttachments && !isJournal) {
533 const KCalendarCore::Attachment::List attachments = (*it)->attachments();
534 QString attachmentCaption;
535 if (attachments.isEmpty()) {
536 attachmentCaption = i18n("No Attachments");
537 txt.clear();
538 } else {
539 attachmentCaption = i18np("1 Attachment:", "%1 Attachments:", attachments.count());
540 }
541 QString attachmentString;
542 KCalendarCore::Attachment::List::ConstIterator ait = attachments.constBegin();
543 for (; ait != attachments.constEnd(); ++ait) {
544 if (!attachmentString.isEmpty()) {
545 attachmentString += i18nc("Spacer for list of attachments", " ");
546 }
547 attachmentString.append((*ait).label());
548 }
549 drawBoxWithCaption(p,
550 attachmentsBox,
551 attachmentCaption,
552 attachmentString,
553 /*sameLine=*/false,
554 /*expand=*/false,
555 captionFont,
556 textFont);
557 }
558 if (mShowAttendees) {
559 const KCalendarCore::Attendee::List attendees = (*it)->attendees();
560 QString attendeeCaption;
561 if (attendees.isEmpty()) {
562 attendeeCaption = i18n("No Attendees");
563 } else {
564 attendeeCaption = i18np("1 Attendee:", "%1 Attendees:", attendees.count());
565 }
566 QString attendeeString;
567 KCalendarCore::Attendee::List::ConstIterator ait = attendees.constBegin();
568 for (; ait != attendees.constEnd(); ++ait) {
569 if (!attendeeString.isEmpty()) {
570 attendeeString += QLatin1Char('\n');
571 }
572 attendeeString += i18nc(
573 "Formatting of an attendee: "
574 "'Name (Role): Status', e.g. 'Reinhold Kainhofer "
575 "<reinhold@kainhofer.com> (Participant): Awaiting Response'",
576 "%1 (%2): %3",
577 (*ait).fullName(),
578 KCalUtils::Stringify::attendeeRole((*ait).role()),
579 KCalUtils::Stringify::attendeeStatus((*ait).status()));
580 }
581 drawBoxWithCaption(p,
582 attendeesBox,
583 attendeeCaption,
584 attendeeString,
585 /*sameLine=*/false,
586 /*expand=*/false,
587 captionFont,
588 textFont);
589 }
590
591 if (mShowOptions) {
592 QString optionsString;
593 if (!KCalUtils::Stringify::incidenceStatus((*it)->status()).isEmpty()) {
594 optionsString += i18n("Status: %1", KCalUtils::Stringify::incidenceStatus((*it)->status()));
595 optionsString += QLatin1Char('\n');
596 }
597 if (!KCalUtils::Stringify::incidenceSecrecy((*it)->secrecy()).isEmpty()) {
598 optionsString += i18n("Secrecy: %1", KCalUtils::Stringify::incidenceSecrecy((*it)->secrecy()));
599 optionsString += QLatin1Char('\n');
600 }
601 if ((*it)->type() == KCalendarCore::Incidence::TypeEvent) {
602 KCalendarCore::Event::Ptr e = (*it).staticCast<KCalendarCore::Event>();
603 if (e->transparency() == KCalendarCore::Event::Opaque) {
604 optionsString += i18n("Show as: Busy");
605 } else {
606 optionsString += i18n("Show as: Free");
607 }
608 optionsString += QLatin1Char('\n');
609 } else if ((*it)->type() == KCalendarCore::Incidence::TypeTodo) {
610 KCalendarCore::Todo::Ptr t = (*it).staticCast<KCalendarCore::Todo>();
611 if (t->isOverdue()) {
612 optionsString += i18n("This task is overdue!");
613 optionsString += QLatin1Char('\n');
614 }
615 } else if ((*it)->type() == KCalendarCore::Incidence::TypeJournal) {
616 // TODO: Anything Journal-specific?
617 }
618 drawBoxWithCaption(p, optionsBox, i18n("Settings: "), optionsString, /*sameLine=*/false, /*expand=*/false, captionFont, textFont);
619 }
620
621 drawBoxWithCaption(p,
622 categoriesBox,
623 i18n("Tags: "),
624 (*it)->categories().join(i18nc("Spacer for the joined list of categories/tags", ", ")),
625 /*sameLine=*/true,
626 /*expand=*/false,
627 captionFont,
628 textFont);
629
630 if (mPrintFooter) {
631 drawFooter(p, footerBox);
632 }
633 }
634 p.setFont(oldFont);
635 }
636
637 /**************************************************************
638 * Print Timetables
639 **************************************************************/
640
CalPrintTimetable()641 CalPrintTimetable::CalPrintTimetable()
642 : CalPrintPluginBase()
643 {
644 }
645
~CalPrintTimetable()646 CalPrintTimetable::~CalPrintTimetable()
647 {
648 }
649
doLoadConfig()650 void CalPrintTimetable::doLoadConfig()
651 {
652 CalPrintPluginBase::doLoadConfig();
653 if (mConfig) {
654 KConfigGroup grp(mConfig, groupName());
655 QDate dt = QDate::currentDate(); // any valid QDate will do
656 QTime tm1(dayStart());
657 QDateTime startTm(dt, tm1);
658 QDateTime endTm(dt, tm1.addSecs(12 * 60 * 60));
659 mStartTime = grp.readEntry("Start time", startTm).time();
660 mEndTime = grp.readEntry("End time", endTm).time();
661 mIncludeDescription = grp.readEntry("Include description", false);
662 mIncludeCategories = grp.readEntry("Include categories", false);
663 mIncludeTodos = grp.readEntry("Include todos", false);
664 mIncludeAllEvents = grp.readEntry("Include all events", false);
665 mSingleLineLimit = grp.readEntry("Single line limit", false);
666 mExcludeTime = grp.readEntry("Exclude time", false);
667 }
668 }
669
doSaveConfig()670 void CalPrintTimetable::doSaveConfig()
671 {
672 if (mConfig) {
673 KConfigGroup grp(mConfig, groupName());
674 QDateTime dt = QDateTime::currentDateTime(); // any valid QDateTime will do
675 dt.setTime(mStartTime);
676 grp.writeEntry("Start time", dt);
677 dt.setTime(mEndTime);
678 grp.writeEntry("End time", dt);
679 grp.writeEntry("Include description", mIncludeDescription);
680 grp.writeEntry("Include categories", mIncludeCategories);
681 grp.writeEntry("Include todos", mIncludeTodos);
682 grp.writeEntry("Include all events", mIncludeAllEvents);
683 grp.writeEntry("Single line limit", mSingleLineLimit);
684 grp.writeEntry("Exclude time", mExcludeTime);
685 }
686 CalPrintPluginBase::doSaveConfig();
687 }
688
cleanString(const QString & instr)689 static QString cleanString(const QString &instr)
690 {
691 QString ret = instr;
692 return ret.replace(QLatin1Char('\n'), QLatin1Char(' '));
693 }
694
drawAllDayBox(QPainter & p,const KCalendarCore::Event::List & eventList,QDate qd,QRect box,const QList<QDate> & workDays)695 void CalPrintTimetable::drawAllDayBox(QPainter &p,
696 const KCalendarCore::Event::List &eventList,
697 QDate qd,
698 QRect box,
699 const QList<QDate> &workDays)
700 {
701 int lineSpacing = p.fontMetrics().lineSpacing();
702
703 if (!workDays.contains(qd)) {
704 drawShadedBox(p, BOX_BORDER_WIDTH, sHolidayBackground, box);
705 } else {
706 drawBox(p, BOX_BORDER_WIDTH, box);
707 }
708
709 QRect eventBox(box);
710 eventBox.setTop(box.top() + padding());
711 eventBox.setBottom(eventBox.top() + lineSpacing);
712
713 for (const KCalendarCore::Event::Ptr &currEvent : std::as_const(eventList)) {
714 if (!currEvent
715 || !currEvent->allDay()
716 || (mExcludeConfidential && currEvent->secrecy() == KCalendarCore::Incidence::SecrecyConfidential)
717 || (mExcludePrivate && currEvent->secrecy() == KCalendarCore::Incidence::SecrecyPrivate)) {
718 continue;
719 }
720 QString str;
721 if (currEvent->location().isEmpty()) {
722 str = cleanString(currEvent->summary());
723 } else {
724 str = i18nc("summary, location", "%1, %2", cleanString(currEvent->summary()), cleanString(currEvent->location()));
725 }
726 if (mIncludeCategories && !currEvent->categoriesStr().isEmpty()) {
727 str = i18nc("summary, categories", "%1, %2", str, currEvent->categoriesStr());
728 }
729 printEventString(p, eventBox, str);
730 eventBox.setTop(eventBox.bottom());
731 eventBox.setBottom(eventBox.top() + lineSpacing);
732 }
733 }
734
drawTimeTable(QPainter & p,QDate fromDate,QDate toDate,QRect box)735 void CalPrintTimetable::drawTimeTable(QPainter &p,
736 QDate fromDate,
737 QDate toDate,
738 QRect box)
739 {
740 QTime myFromTime = mStartTime;
741 QTime myToTime = mEndTime;
742 int maxAllDayEvents = 0;
743 QDate curDate(fromDate);
744 while (curDate <= toDate) {
745 KCalendarCore::Event::List eventList = mCalendar->events(curDate, QTimeZone::systemTimeZone());
746 const auto holidays = holiday(curDate);
747 int allDayEvents = holiday(curDate).isEmpty() ? 0 : 1;
748 for (const KCalendarCore::Event::Ptr &event : std::as_const(eventList)) {
749 Q_ASSERT(event);
750 if (!event
751 || (mExcludeConfidential && event->secrecy() == KCalendarCore::Incidence::SecrecyConfidential)
752 || (mExcludePrivate && event->secrecy() == KCalendarCore::Incidence::SecrecyPrivate)) {
753 continue;
754 }
755 if (event->allDay()) {
756 allDayEvents += 1;
757 } else if (mIncludeAllEvents) {
758 if (event->dtStart().time() < myFromTime) {
759 myFromTime = event->dtStart().time();
760 }
761 if (event->dtEnd().time() > myToTime) {
762 myToTime = event->dtEnd().time();
763 }
764 }
765 }
766 if (allDayEvents > maxAllDayEvents) {
767 maxAllDayEvents = allDayEvents;
768 }
769 curDate = curDate.addDays(1);
770 }
771
772 p.setFont(QFont(QStringLiteral("sans-serif"), 11, QFont::Normal));
773 const int lineSpacing = p.fontMetrics().lineSpacing();
774
775 int timelineWidth = TIMELINE_WIDTH + padding();
776
777 QRect dowBox(box);
778 dowBox.setLeft(box.left() + timelineWidth);
779 dowBox.setHeight(mSubHeaderHeight);
780 drawDaysOfWeek(p, fromDate, toDate, dowBox);
781
782 int tlTop = dowBox.bottom();
783
784 int alldayHeight = 0;
785 if (maxAllDayEvents > 0) {
786 // Draw the side bar for all-day events.
787 const auto alldayLabel = i18nc("label for timetable all-day boxes", "All day");
788 const QFont oldFont(p.font());
789 p.setFont(QFont(QStringLiteral("sans-serif"), 9, QFont::Normal));
790 const auto labelHeight = p.fontMetrics().horizontalAdvance(alldayLabel) + 2*padding();
791 alldayHeight = std::max(maxAllDayEvents*lineSpacing + 2*padding(), labelHeight);
792 drawVerticalBox(p,
793 BOX_BORDER_WIDTH,
794 QRect(0, tlTop, TIMELINE_WIDTH, alldayHeight),
795 alldayLabel,
796 Qt::AlignHCenter | Qt::AlignVCenter | Qt::TextWordWrap);
797 p.setFont(oldFont);
798 tlTop += alldayHeight +padding();
799 }
800
801 QRect tlBox(box);
802 tlBox.setWidth(TIMELINE_WIDTH);
803 tlBox.setTop(tlTop);
804 drawTimeLine(p, myFromTime, myToTime, tlBox);
805
806 // draw each day
807 curDate = fromDate;
808 int i = 0;
809 double cellWidth = double(dowBox.width() - 1) / double(fromDate.daysTo(toDate) + 1);
810 QRect allDayBox(dowBox.left(), dowBox.bottom(), cellWidth, alldayHeight);
811 const QList<QDate> workDays = CalendarSupport::workDays(fromDate, toDate);
812 while (curDate <= toDate) {
813 KCalendarCore::Event::List eventList =
814 mCalendar->events(curDate, QTimeZone::systemTimeZone(), KCalendarCore::EventSortStartDate, KCalendarCore::SortDirectionAscending);
815
816 allDayBox.setLeft(dowBox.left() + int(i * cellWidth));
817 allDayBox.setRight(dowBox.left() + int((i + 1) * cellWidth));
818 if (maxAllDayEvents > 0) {
819 if (const auto h = holidayEvent(curDate)) {
820 eventList.prepend(h);
821 }
822 drawAllDayBox(p, eventList, curDate, allDayBox, workDays);
823 }
824
825 QRect dayBox(allDayBox);
826 dayBox.setTop(tlTop);
827 dayBox.setBottom(box.bottom());
828 drawAgendaDayBox(p,
829 eventList,
830 curDate,
831 false,
832 myFromTime,
833 myToTime,
834 dayBox,
835 mIncludeDescription,
836 mIncludeCategories,
837 mExcludeTime,
838 workDays);
839
840 ++i;
841 curDate = curDate.addDays(1);
842 }
843 }
844
845 /**************************************************************
846 * Print Day
847 **************************************************************/
848
CalPrintDay()849 CalPrintDay::CalPrintDay()
850 : CalPrintTimetable()
851 {
852 }
853
~CalPrintDay()854 CalPrintDay::~CalPrintDay()
855 {
856 }
857
createConfigWidget(QWidget * w)858 QWidget *CalPrintDay::createConfigWidget(QWidget *w)
859 {
860 return new CalPrintDayConfig(w);
861 }
862
readSettingsWidget()863 void CalPrintDay::readSettingsWidget()
864 {
865 auto cfg = dynamic_cast<CalPrintDayConfig *>((QWidget *)mConfigWidget);
866 if (cfg) {
867 mFromDate = cfg->mFromDate->date();
868 mToDate = cfg->mToDate->date();
869
870 if (cfg->mPrintTypeFilofax->isChecked()) {
871 mDayPrintType = Filofax;
872 } else if (cfg->mPrintTypeTimetable->isChecked()) {
873 mDayPrintType = Timetable;
874 } else {
875 mDayPrintType = SingleTimetable;
876 }
877
878 mStartTime = cfg->mFromTime->time();
879 mEndTime = cfg->mToTime->time();
880 mIncludeAllEvents = cfg->mIncludeAllEvents->isChecked();
881
882 mIncludeDescription = cfg->mIncludeDescription->isChecked();
883 mIncludeCategories = cfg->mIncludeCategories->isChecked();
884 mSingleLineLimit = cfg->mSingleLineLimit->isChecked();
885 mIncludeTodos = cfg->mIncludeTodos->isChecked();
886 mUseColors = cfg->mColors->isChecked();
887 mPrintFooter = cfg->mPrintFooter->isChecked();
888 mShowNoteLines = cfg->mShowNoteLines->isChecked();
889 mExcludeTime = cfg->mExcludeTime->isChecked();
890 mExcludeConfidential = cfg->mExcludeConfidential->isChecked();
891 mExcludePrivate = cfg->mExcludePrivate->isChecked();
892 }
893 }
894
setSettingsWidget()895 void CalPrintDay::setSettingsWidget()
896 {
897 auto cfg = dynamic_cast<CalPrintDayConfig *>((QWidget *)mConfigWidget);
898 if (cfg) {
899 cfg->mFromDate->setDate(mFromDate);
900 cfg->mToDate->setDate(mToDate);
901
902 cfg->mPrintTypeFilofax->setChecked(mDayPrintType == Filofax);
903 cfg->mPrintTypeTimetable->setChecked(mDayPrintType == Timetable);
904 cfg->mPrintTypeSingleTimetable->setChecked(mDayPrintType == SingleTimetable);
905
906 cfg->mFromTime->setTime(mStartTime);
907 cfg->mToTime->setTime(mEndTime);
908 cfg->mIncludeAllEvents->setChecked(mIncludeAllEvents);
909
910 cfg->mIncludeDescription->setChecked(mIncludeDescription);
911 cfg->mIncludeCategories->setChecked(mIncludeCategories);
912 cfg->mSingleLineLimit->setChecked(mSingleLineLimit);
913 cfg->mIncludeTodos->setChecked(mIncludeTodos);
914 cfg->mColors->setChecked(mUseColors);
915 cfg->mPrintFooter->setChecked(mPrintFooter);
916 cfg->mShowNoteLines->setChecked(mShowNoteLines);
917 cfg->mExcludeTime->setChecked(mExcludeTime);
918 cfg->mExcludeConfidential->setChecked(mExcludeConfidential);
919 cfg->mExcludePrivate->setChecked(mExcludePrivate);
920 }
921 }
922
doLoadConfig()923 void CalPrintDay::doLoadConfig()
924 {
925 CalPrintTimetable::doLoadConfig();
926 if (mConfig) {
927 KConfigGroup grp(mConfig, groupName());
928 mDayPrintType = static_cast<eDayPrintType>(grp.readEntry("Print type", static_cast<int>(Timetable)));
929 }
930 setSettingsWidget();
931 }
932
doSaveConfig()933 void CalPrintDay::doSaveConfig()
934 {
935 readSettingsWidget();
936 if (mConfig) {
937 KConfigGroup grp(mConfig, groupName());
938 grp.writeEntry("Print type", int(mDayPrintType));
939 }
940 CalPrintTimetable::doSaveConfig();
941 }
942
setDateRange(const QDate & from,const QDate & to)943 void CalPrintDay::setDateRange(const QDate &from, const QDate &to)
944 {
945 CalPrintPluginBase::setDateRange(from, to);
946 auto cfg = dynamic_cast<CalPrintDayConfig *>((QWidget *)mConfigWidget);
947 if (cfg) {
948 cfg->mFromDate->setDate(from);
949 cfg->mToDate->setDate(to);
950 }
951 }
952
drawDays(QPainter & p,QRect box)953 void CalPrintDay::drawDays(QPainter &p, QRect box)
954 {
955 const int numberOfDays = mFromDate.daysTo(mToDate) + 1;
956 int vcells;
957 const bool portrait = (box.height() > box.width());
958 int cellWidth;
959 if (portrait) {
960 // 2 columns
961 vcells = std::ceil(static_cast<double>(numberOfDays) / 2.0);
962 if (numberOfDays > 1) {
963 cellWidth = box.width() / 2;
964 } else {
965 cellWidth = box.width();
966 }
967 } else {
968 // landscape: N columns
969 vcells = 1;
970 cellWidth = box.width() / numberOfDays;
971 }
972 const int cellHeight = box.height() / vcells;
973 QDate weekDate = mFromDate;
974 for (int i = 0; i < numberOfDays; ++i, weekDate = weekDate.addDays(1)) {
975 const int hpos = i / vcells;
976 const int vpos = i % vcells;
977 const QRect dayBox(box.left() + cellWidth * hpos, box.top() + cellHeight * vpos, cellWidth, cellHeight);
978 drawDayBox(p,
979 weekDate,
980 mStartTime,
981 mEndTime,
982 dayBox,
983 true,
984 true,
985 true,
986 mSingleLineLimit,
987 mIncludeDescription,
988 mIncludeCategories);
989 } // for i through all selected days
990 }
991
print(QPainter & p,int width,int height)992 void CalPrintDay::print(QPainter &p, int width, int height)
993 {
994 QDate curDay(mFromDate);
995
996 QRect headerBox(0, 0, width, headerHeight());
997 QRect footerBox(0, height - footerHeight(), width, footerHeight());
998 height -= footerHeight();
999 QRect daysBox(headerBox);
1000 daysBox.setTop(headerBox.bottom() + padding());
1001 daysBox.setBottom(height);
1002
1003 auto local = QLocale::system();
1004
1005 switch (mDayPrintType) {
1006 case Filofax:
1007 case SingleTimetable: {
1008 QString line1 = local.toString(mFromDate, QLocale::ShortFormat);
1009 QString line2 = local.toString(mToDate, QLocale::ShortFormat);
1010 QString title;
1011 if (mFromDate == mToDate) {
1012 title = line1;
1013 } else {
1014 title = i18nc("date from-to", "%1\u2013%2", line1, line2);
1015 }
1016 drawHeader(p, title, mFromDate, QDate(), headerBox);
1017 if (mDayPrintType == Filofax) {
1018 drawDays(p, daysBox);
1019 } else if (mDayPrintType == SingleTimetable) {
1020 drawTimeTable(p, mFromDate, mToDate, daysBox);
1021 }
1022 if (mPrintFooter) {
1023 drawFooter(p, footerBox);
1024 }
1025 break;
1026 }
1027
1028 case Timetable:
1029 default:
1030 do {
1031 QTime curStartTime(mStartTime);
1032 QTime curEndTime(mEndTime);
1033
1034 // For an invalid time range, simply show one hour, starting at the hour
1035 // before the given start time
1036 if (curEndTime <= curStartTime) {
1037 curStartTime = QTime(curStartTime.hour(), 0, 0);
1038 curEndTime = curStartTime.addSecs(3600);
1039 }
1040
1041 drawHeader(p, local.toString(curDay, QLocale::ShortFormat), curDay, QDate(), headerBox);
1042 drawTimeTable(p, curDay, curDay, daysBox);
1043 if (mPrintFooter) {
1044 drawFooter(p, footerBox);
1045 }
1046
1047 curDay = curDay.addDays(1);
1048 if (curDay <= mToDate) {
1049 mPrinter->newPage();
1050 }
1051 } while (curDay <= mToDate);
1052 } // switch
1053 }
1054
1055 /**************************************************************
1056 * Print Week
1057 **************************************************************/
1058
CalPrintWeek()1059 CalPrintWeek::CalPrintWeek()
1060 : CalPrintTimetable()
1061 {
1062 }
1063
~CalPrintWeek()1064 CalPrintWeek::~CalPrintWeek()
1065 {
1066 }
1067
createConfigWidget(QWidget * w)1068 QWidget *CalPrintWeek::createConfigWidget(QWidget *w)
1069 {
1070 return new CalPrintWeekConfig(w);
1071 }
1072
readSettingsWidget()1073 void CalPrintWeek::readSettingsWidget()
1074 {
1075 auto cfg = dynamic_cast<CalPrintWeekConfig *>((QWidget *)mConfigWidget);
1076 if (cfg) {
1077 mFromDate = cfg->mFromDate->date();
1078 mToDate = cfg->mToDate->date();
1079
1080 if (cfg->mPrintTypeFilofax->isChecked()) {
1081 mWeekPrintType = Filofax;
1082 } else if (cfg->mPrintTypeTimetable->isChecked()) {
1083 mWeekPrintType = Timetable;
1084 } else if (cfg->mPrintTypeSplitWeek->isChecked()) {
1085 mWeekPrintType = SplitWeek;
1086 } else {
1087 mWeekPrintType = Timetable;
1088 }
1089
1090 mStartTime = cfg->mFromTime->time();
1091 mEndTime = cfg->mToTime->time();
1092 mIncludeAllEvents = cfg->mIncludeAllEvents->isChecked();
1093
1094 mShowNoteLines = cfg->mShowNoteLines->isChecked();
1095 mSingleLineLimit = cfg->mSingleLineLimit->isChecked();
1096 mIncludeTodos = cfg->mIncludeTodos->isChecked();
1097 mUseColors = cfg->mColors->isChecked();
1098 mPrintFooter = cfg->mPrintFooter->isChecked();
1099 mIncludeDescription = cfg->mIncludeDescription->isChecked();
1100 mIncludeCategories = cfg->mIncludeCategories->isChecked();
1101 mExcludeTime = cfg->mExcludeTime->isChecked();
1102 mExcludeConfidential = cfg->mExcludeConfidential->isChecked();
1103 mExcludePrivate = cfg->mExcludePrivate->isChecked();
1104 }
1105 }
1106
setSettingsWidget()1107 void CalPrintWeek::setSettingsWidget()
1108 {
1109 auto cfg = dynamic_cast<CalPrintWeekConfig *>((QWidget *)mConfigWidget);
1110 if (cfg) {
1111 cfg->mFromDate->setDate(mFromDate);
1112 cfg->mToDate->setDate(mToDate);
1113
1114 cfg->mPrintTypeFilofax->setChecked(mWeekPrintType == Filofax);
1115 cfg->mPrintTypeTimetable->setChecked(mWeekPrintType == Timetable);
1116 cfg->mPrintTypeSplitWeek->setChecked(mWeekPrintType == SplitWeek);
1117
1118 cfg->mFromTime->setTime(mStartTime);
1119 cfg->mToTime->setTime(mEndTime);
1120 cfg->mIncludeAllEvents->setChecked(mIncludeAllEvents);
1121
1122 cfg->mShowNoteLines->setChecked(mShowNoteLines);
1123 cfg->mSingleLineLimit->setChecked(mSingleLineLimit);
1124 cfg->mIncludeTodos->setChecked(mIncludeTodos);
1125 cfg->mColors->setChecked(mUseColors);
1126 cfg->mPrintFooter->setChecked(mPrintFooter);
1127 cfg->mIncludeDescription->setChecked(mIncludeDescription);
1128 cfg->mIncludeCategories->setChecked(mIncludeCategories);
1129 cfg->mExcludeTime->setChecked(mExcludeTime);
1130 cfg->mExcludeConfidential->setChecked(mExcludeConfidential);
1131 cfg->mExcludePrivate->setChecked(mExcludePrivate);
1132 }
1133 CalPrintTimetable::setSettingsWidget();
1134 }
1135
doLoadConfig()1136 void CalPrintWeek::doLoadConfig()
1137 {
1138 CalPrintTimetable::doLoadConfig();
1139 if (mConfig) {
1140 KConfigGroup grp(mConfig, groupName());
1141 mWeekPrintType = (eWeekPrintType)(grp.readEntry("Print type", (int)Filofax));
1142 }
1143 setSettingsWidget();
1144 }
1145
doSaveConfig()1146 void CalPrintWeek::doSaveConfig()
1147 {
1148 readSettingsWidget();
1149 if (mConfig) {
1150 KConfigGroup grp(mConfig, groupName());
1151 grp.writeEntry("Print type", int(mWeekPrintType));
1152 }
1153 CalPrintTimetable::doSaveConfig();
1154 }
1155
defaultOrientation() const1156 QPageLayout::Orientation CalPrintWeek::defaultOrientation() const
1157 {
1158 if (mWeekPrintType == Filofax) {
1159 return QPageLayout::Portrait;
1160 } else if (mWeekPrintType == SplitWeek) {
1161 return QPageLayout::Portrait;
1162 } else {
1163 return QPageLayout::Landscape;
1164 }
1165 }
1166
setDateRange(const QDate & from,const QDate & to)1167 void CalPrintWeek::setDateRange(const QDate &from, const QDate &to)
1168 {
1169 CalPrintPluginBase::setDateRange(from, to);
1170 auto cfg = dynamic_cast<CalPrintWeekConfig *>((QWidget *)mConfigWidget);
1171 if (cfg) {
1172 cfg->mFromDate->setDate(from);
1173 cfg->mToDate->setDate(to);
1174 }
1175 }
1176
drawWeek(QPainter & p,QDate qd,QRect box)1177 void CalPrintWeek::drawWeek(QPainter &p,
1178 QDate qd,
1179 QRect box)
1180 {
1181 QDate weekDate = qd;
1182 const bool portrait = (box.height() > box.width());
1183 int cellWidth;
1184 int vcells;
1185 if (portrait) {
1186 cellWidth = box.width() / 2;
1187 vcells = 3;
1188 } else {
1189 cellWidth = box.width() / 6;
1190 vcells = 1;
1191 }
1192 const int cellHeight = box.height() / vcells;
1193
1194 // correct begin of week
1195 int weekdayCol = weekdayColumn(qd.dayOfWeek());
1196 weekDate = qd.addDays(-weekdayCol);
1197
1198 for (int i = 0; i < 7; ++i, weekDate = weekDate.addDays(1)) {
1199 // Saturday and sunday share a cell, so we have to special-case sunday
1200 int hpos = ((i < 6) ? i : (i - 1)) / vcells;
1201 int vpos = ((i < 6) ? i : (i - 1)) % vcells;
1202 QRect dayBox(box.left() + cellWidth * hpos,
1203 box.top() + cellHeight * vpos + ((i == 6) ? (cellHeight / 2) : 0),
1204 cellWidth,
1205 (i < 5) ? (cellHeight) : (cellHeight / 2));
1206 drawDayBox(p,
1207 weekDate,
1208 mStartTime,
1209 mEndTime,
1210 dayBox,
1211 true,
1212 true,
1213 true,
1214 mSingleLineLimit,
1215 mIncludeDescription,
1216 mIncludeCategories);
1217 } // for i through all weekdays
1218 }
1219
print(QPainter & p,int width,int height)1220 void CalPrintWeek::print(QPainter &p, int width, int height)
1221 {
1222 QDate curWeek;
1223 QDate fromWeek;
1224 QDate toWeek;
1225
1226 // correct begin and end to first and last day of week
1227 int weekdayCol = weekdayColumn(mFromDate.dayOfWeek());
1228 fromWeek = mFromDate.addDays(-weekdayCol);
1229 weekdayCol = weekdayColumn(mToDate.dayOfWeek());
1230 toWeek = mToDate.addDays(6 - weekdayCol);
1231
1232 curWeek = fromWeek.addDays(6);
1233 auto local = QLocale::system();
1234
1235 QString line1;
1236 QString line2;
1237 QString title;
1238 QRect headerBox(0, 0, width, headerHeight());
1239 QRect footerBox(0, height - footerHeight(), width, footerHeight());
1240 height -= footerHeight();
1241
1242 QRect weekBox(headerBox);
1243 weekBox.setTop(headerBox.bottom() + padding());
1244 weekBox.setBottom(height);
1245
1246 switch (mWeekPrintType) {
1247 case Filofax:
1248 do {
1249 line1 = local.toString(curWeek.addDays(-6), QLocale::ShortFormat);
1250 line2 = local.toString(curWeek, QLocale::ShortFormat);
1251 title = i18nc("date from-to", "%1\u2013%2", line1, line2);
1252 drawHeader(p, title, curWeek.addDays(-6), QDate(), headerBox);
1253
1254 drawWeek(p, curWeek, weekBox);
1255
1256 if (mPrintFooter) {
1257 drawFooter(p, footerBox);
1258 }
1259
1260 curWeek = curWeek.addDays(7);
1261 if (curWeek <= toWeek) {
1262 mPrinter->newPage();
1263 }
1264 } while (curWeek <= toWeek);
1265 break;
1266
1267 case Timetable:
1268 default:
1269 do {
1270 line1 = local.toString(curWeek.addDays(-6), QLocale::ShortFormat);
1271 line2 = local.toString(curWeek, QLocale::ShortFormat);
1272 if (orientation() == QPageLayout::Landscape) {
1273 title = i18nc("date from - to (week number)", "%1\u2013%2 (Week %3)", line1, line2, curWeek.weekNumber());
1274 } else {
1275 title = i18nc("date from - to\\n(week number)", "%1\u2013%2\n(Week %3)", line1, line2, curWeek.weekNumber());
1276 }
1277 drawHeader(p, title, curWeek, QDate(), headerBox);
1278
1279 drawTimeTable(p, fromWeek, curWeek, weekBox);
1280
1281 if (mPrintFooter) {
1282 drawFooter(p, footerBox);
1283 }
1284
1285 fromWeek = fromWeek.addDays(7);
1286 curWeek = fromWeek.addDays(6);
1287 if (curWeek <= toWeek) {
1288 mPrinter->newPage();
1289 }
1290 } while (curWeek <= toWeek);
1291 break;
1292
1293 case SplitWeek: {
1294 QRect weekBox1(weekBox);
1295 // On the left side there are four days (mo-th) plus the timeline,
1296 // on the right there are only three days (fr-su) plus the timeline. Don't
1297 // use the whole width, but rather give them the same width as on the left.
1298 weekBox1.setRight(int((width - TIMELINE_WIDTH) * 3. / 4. + TIMELINE_WIDTH));
1299 do {
1300 QDate endLeft(fromWeek.addDays(3));
1301 int hh = headerHeight();
1302
1303 drawSplitHeaderRight(p, fromWeek, curWeek, QDate(), width, hh);
1304 drawTimeTable(p, fromWeek, endLeft, weekBox);
1305 if (mPrintFooter) {
1306 drawFooter(p, footerBox);
1307 }
1308 mPrinter->newPage();
1309 drawSplitHeaderRight(p, fromWeek, curWeek, QDate(), width, hh);
1310 drawTimeTable(p, endLeft.addDays(1), curWeek, weekBox1);
1311
1312 if (mPrintFooter) {
1313 drawFooter(p, footerBox);
1314 }
1315
1316 fromWeek = fromWeek.addDays(7);
1317 curWeek = fromWeek.addDays(6);
1318 if (curWeek <= toWeek) {
1319 mPrinter->newPage();
1320 }
1321 } while (curWeek <= toWeek);
1322 break;
1323 }
1324 }
1325 }
1326
1327 /**************************************************************
1328 * Print Month
1329 **************************************************************/
1330
CalPrintMonth()1331 CalPrintMonth::CalPrintMonth()
1332 : CalPrintPluginBase()
1333 {
1334 }
1335
~CalPrintMonth()1336 CalPrintMonth::~CalPrintMonth()
1337 {
1338 }
1339
createConfigWidget(QWidget * w)1340 QWidget *CalPrintMonth::createConfigWidget(QWidget *w)
1341 {
1342 return new CalPrintMonthConfig(w);
1343 }
1344
readSettingsWidget()1345 void CalPrintMonth::readSettingsWidget()
1346 {
1347 auto cfg = dynamic_cast<CalPrintMonthConfig *>((QWidget *)mConfigWidget);
1348
1349 if (cfg) {
1350 mFromDate = QDate(cfg->mFromYear->value(), cfg->mFromMonth->currentIndex() + 1, 1);
1351 mToDate = QDate(cfg->mToYear->value(), cfg->mToMonth->currentIndex() + 1, 1);
1352
1353 mWeekNumbers = cfg->mWeekNumbers->isChecked();
1354 mRecurDaily = cfg->mRecurDaily->isChecked();
1355 mRecurWeekly = cfg->mRecurWeekly->isChecked();
1356 mIncludeTodos = cfg->mIncludeTodos->isChecked();
1357 mShowNoteLines = cfg->mShowNoteLines->isChecked();
1358 mSingleLineLimit = cfg->mSingleLineLimit->isChecked();
1359 mUseColors = cfg->mColors->isChecked();
1360 mPrintFooter = cfg->mPrintFooter->isChecked();
1361 mIncludeDescription = cfg->mIncludeDescription->isChecked();
1362 mIncludeCategories = cfg->mIncludeCategories->isChecked();
1363 mExcludeConfidential = cfg->mExcludeConfidential->isChecked();
1364 mExcludePrivate = cfg->mExcludePrivate->isChecked();
1365 }
1366 }
1367
setSettingsWidget()1368 void CalPrintMonth::setSettingsWidget()
1369 {
1370 auto cfg = dynamic_cast<CalPrintMonthConfig *>((QWidget *)mConfigWidget);
1371
1372 if (cfg) {
1373 setDateRange(mFromDate, mToDate);
1374
1375 cfg->mWeekNumbers->setChecked(mWeekNumbers);
1376 cfg->mRecurDaily->setChecked(mRecurDaily);
1377 cfg->mRecurWeekly->setChecked(mRecurWeekly);
1378 cfg->mIncludeTodos->setChecked(mIncludeTodos);
1379 cfg->mShowNoteLines->setChecked(mShowNoteLines);
1380 cfg->mSingleLineLimit->setChecked(mSingleLineLimit);
1381 cfg->mColors->setChecked(mUseColors);
1382 cfg->mPrintFooter->setChecked(mPrintFooter);
1383 cfg->mIncludeDescription->setChecked(mIncludeDescription);
1384 cfg->mIncludeCategories->setChecked(mIncludeCategories);
1385 cfg->mExcludeConfidential->setChecked(mExcludeConfidential);
1386 cfg->mExcludePrivate->setChecked(mExcludePrivate);
1387 }
1388 }
1389
doLoadConfig()1390 void CalPrintMonth::doLoadConfig()
1391 {
1392 CalPrintPluginBase::doLoadConfig();
1393 if (mConfig) {
1394 KConfigGroup grp(mConfig, groupName());
1395 mWeekNumbers = grp.readEntry("Print week numbers", true);
1396 mRecurDaily = grp.readEntry("Print daily incidences", true);
1397 mRecurWeekly = grp.readEntry("Print weekly incidences", true);
1398 mIncludeTodos = grp.readEntry("Include todos", false);
1399 mSingleLineLimit = grp.readEntry("Single line limit", false);
1400 mIncludeDescription = grp.readEntry("Include description", false);
1401 mIncludeCategories = grp.readEntry("Include categories", false);
1402 }
1403 setSettingsWidget();
1404 }
1405
doSaveConfig()1406 void CalPrintMonth::doSaveConfig()
1407 {
1408 readSettingsWidget();
1409 if (mConfig) {
1410 KConfigGroup grp(mConfig, groupName());
1411 grp.writeEntry("Print week numbers", mWeekNumbers);
1412 grp.writeEntry("Print daily incidences", mRecurDaily);
1413 grp.writeEntry("Print weekly incidences", mRecurWeekly);
1414 grp.writeEntry("Include todos", mIncludeTodos);
1415 grp.writeEntry("Single line limit", mSingleLineLimit);
1416 grp.writeEntry("Include description", mIncludeDescription);
1417 grp.writeEntry("Include categories", mIncludeCategories);
1418 }
1419 CalPrintPluginBase::doSaveConfig();
1420 }
1421
setDateRange(const QDate & from,const QDate & to)1422 void CalPrintMonth::setDateRange(const QDate &from, const QDate &to)
1423 {
1424 CalPrintPluginBase::setDateRange(from, to);
1425 auto cfg = dynamic_cast<CalPrintMonthConfig *>((QWidget *)mConfigWidget);
1426 if (cfg) {
1427 cfg->mFromMonth->clear();
1428 for (int i = 0; i < 12; ++i) {
1429 cfg->mFromMonth->addItem(QLocale().monthName(i + 1, QLocale::LongFormat));
1430 }
1431 cfg->mToMonth->clear();
1432 for (int i = 0; i < 12; ++i) {
1433 cfg->mToMonth->addItem(QLocale().monthName(i + 1, QLocale::LongFormat));
1434 }
1435 cfg->mFromMonth->setCurrentIndex(from.month() - 1);
1436 cfg->mFromYear->setValue(to.year());
1437 cfg->mToMonth->setCurrentIndex(mToDate.month() - 1);
1438 cfg->mToYear->setValue(mToDate.year());
1439 }
1440 }
1441
print(QPainter & p,int width,int height)1442 void CalPrintMonth::print(QPainter &p, int width, int height)
1443 {
1444 QDate curMonth;
1445 QDate fromMonth;
1446 QDate toMonth;
1447
1448 fromMonth = mFromDate.addDays(-(mFromDate.day() - 1));
1449 toMonth = mToDate.addDays(mToDate.daysInMonth() - mToDate.day());
1450
1451 curMonth = fromMonth;
1452
1453 QRect headerBox(0, 0, width, headerHeight());
1454 QRect footerBox(0, height - footerHeight(), width, footerHeight());
1455 height -= footerHeight();
1456
1457 QRect monthBox(0, 0, width, height);
1458 monthBox.setTop(headerBox.bottom() + padding());
1459
1460 do {
1461 QString title(i18nc("monthname year", "%1 %2", QLocale::system().monthName(curMonth.month()), QString::number(curMonth.year())));
1462 QDate tmp(fromMonth);
1463 int weekdayCol = weekdayColumn(tmp.dayOfWeek());
1464 tmp = tmp.addDays(-weekdayCol);
1465
1466 drawHeader(p, title, curMonth.addMonths(-1), curMonth.addMonths(1), headerBox);
1467 drawMonthTable(p,
1468 curMonth,
1469 QTime(),
1470 QTime(),
1471 mWeekNumbers,
1472 mRecurDaily,
1473 mRecurWeekly,
1474 mSingleLineLimit,
1475 mIncludeDescription,
1476 mIncludeCategories,
1477 monthBox);
1478
1479 if (mPrintFooter) {
1480 drawFooter(p, footerBox);
1481 }
1482
1483 curMonth = curMonth.addDays(curMonth.daysInMonth());
1484 if (curMonth <= toMonth) {
1485 mPrinter->newPage();
1486 }
1487 } while (curMonth <= toMonth);
1488 }
1489
1490 /**************************************************************
1491 * Print Todos
1492 **************************************************************/
1493
CalPrintTodos()1494 CalPrintTodos::CalPrintTodos()
1495 : CalPrintPluginBase()
1496 {
1497 mTodoSortField = TodoFieldUnset;
1498 mTodoSortDirection = TodoDirectionUnset;
1499 }
1500
~CalPrintTodos()1501 CalPrintTodos::~CalPrintTodos()
1502 {
1503 }
1504
createConfigWidget(QWidget * w)1505 QWidget *CalPrintTodos::createConfigWidget(QWidget *w)
1506 {
1507 return new CalPrintTodoConfig(w);
1508 }
1509
readSettingsWidget()1510 void CalPrintTodos::readSettingsWidget()
1511 {
1512 auto cfg = dynamic_cast<CalPrintTodoConfig *>((QWidget *)mConfigWidget);
1513
1514 if (cfg) {
1515 mPageTitle = cfg->mTitle->text();
1516
1517 if (cfg->mPrintAll->isChecked()) {
1518 mTodoPrintType = TodosAll;
1519 } else if (cfg->mPrintUnfinished->isChecked()) {
1520 mTodoPrintType = TodosUnfinished;
1521 } else if (cfg->mPrintDueRange->isChecked()) {
1522 mTodoPrintType = TodosDueRange;
1523 } else {
1524 mTodoPrintType = TodosAll;
1525 }
1526
1527 mFromDate = cfg->mFromDate->date();
1528 mToDate = cfg->mToDate->date();
1529
1530 mIncludeDescription = cfg->mDescription->isChecked();
1531 mIncludePriority = cfg->mPriority->isChecked();
1532 mIncludeCategories = cfg->mCategories->isChecked();
1533 mIncludeStartDate = cfg->mStartDate->isChecked();
1534 mIncludeDueDate = cfg->mDueDate->isChecked();
1535 mIncludePercentComplete = cfg->mPercentComplete->isChecked();
1536 mConnectSubTodos = cfg->mConnectSubTodos->isChecked();
1537 mStrikeOutCompleted = cfg->mStrikeOutCompleted->isChecked();
1538 mExcludeConfidential = cfg->mExcludeConfidential->isChecked();
1539 mExcludePrivate = cfg->mExcludePrivate->isChecked();
1540
1541 mTodoSortField = (eTodoSortField)cfg->mSortField->currentIndex();
1542 mTodoSortDirection = (eTodoSortDirection)cfg->mSortDirection->currentIndex();
1543
1544 mPrintFooter = cfg->mPrintFooter->isChecked();
1545 }
1546 }
1547
setSettingsWidget()1548 void CalPrintTodos::setSettingsWidget()
1549 {
1550 auto cfg = dynamic_cast<CalPrintTodoConfig *>((QWidget *)mConfigWidget);
1551 if (cfg) {
1552 cfg->mTitle->setText(mPageTitle);
1553
1554 cfg->mPrintAll->setChecked(mTodoPrintType == TodosAll);
1555 cfg->mPrintUnfinished->setChecked(mTodoPrintType == TodosUnfinished);
1556 cfg->mPrintDueRange->setChecked(mTodoPrintType == TodosDueRange);
1557
1558 cfg->mFromDate->setDate(mFromDate);
1559 cfg->mToDate->setDate(mToDate);
1560
1561 cfg->mDescription->setChecked(mIncludeDescription);
1562 cfg->mPriority->setChecked(mIncludePriority);
1563 cfg->mCategories->setChecked(mIncludeCategories);
1564 cfg->mStartDate->setChecked(mIncludeStartDate);
1565 cfg->mDueDate->setChecked(mIncludeDueDate);
1566 cfg->mPercentComplete->setChecked(mIncludePercentComplete);
1567 cfg->mConnectSubTodos->setChecked(mConnectSubTodos);
1568 cfg->mStrikeOutCompleted->setChecked(mStrikeOutCompleted);
1569 cfg->mExcludeConfidential->setChecked(mExcludeConfidential);
1570 cfg->mExcludePrivate->setChecked(mExcludePrivate);
1571
1572 if (mTodoSortField != TodoFieldUnset) {
1573 // do not insert if already done so.
1574 cfg->mSortField->addItem(i18nc("@option sort by title", "Title"));
1575 cfg->mSortField->addItem(i18nc("@option sort by start date/time", "Start Date"));
1576 cfg->mSortField->addItem(i18nc("@option sort by due date/time", "Due Date"));
1577 cfg->mSortField->addItem(i18nc("@option sort by priority", "Priority"));
1578 cfg->mSortField->addItem(i18nc("@option sort by percent completed", "Percent Complete"));
1579 cfg->mSortField->addItem(i18nc("@option sort by tags", "Tags"));
1580 cfg->mSortField->setCurrentIndex(mTodoSortField);
1581 }
1582
1583 if (mTodoSortDirection != TodoDirectionUnset) {
1584 // do not insert if already done so.
1585 cfg->mSortDirection->addItem(i18nc("@option sort in increasing order", "Ascending"));
1586 cfg->mSortDirection->addItem(i18nc("@option sort in descreasing order", "Descending"));
1587 cfg->mSortDirection->setCurrentIndex(mTodoSortDirection);
1588 }
1589
1590 cfg->mPrintFooter->setChecked(mPrintFooter);
1591 }
1592 }
1593
doLoadConfig()1594 void CalPrintTodos::doLoadConfig()
1595 {
1596 CalPrintPluginBase::doLoadConfig();
1597 if (mConfig) {
1598 KConfigGroup grp(mConfig, groupName());
1599 mPageTitle = grp.readEntry("Page title", i18n("To-do list"));
1600 mTodoPrintType = (eTodoPrintType)grp.readEntry("Print type", static_cast<int>(TodosAll));
1601 mIncludeDescription = grp.readEntry("Include description", true);
1602 mIncludePriority = grp.readEntry("Include priority", true);
1603 mIncludeCategories = grp.readEntry("Include categories", true);
1604 mIncludeStartDate = grp.readEntry("Include start date", true);
1605 mIncludeDueDate = grp.readEntry("Include due date", true);
1606 mIncludePercentComplete = grp.readEntry("Include percentage completed", true);
1607 mConnectSubTodos = grp.readEntry("Connect subtodos", true);
1608 mStrikeOutCompleted = grp.readEntry("Strike out completed summaries", true);
1609 mTodoSortField = (eTodoSortField)grp.readEntry("Sort field", static_cast<int>(TodoFieldSummary));
1610 mTodoSortDirection = (eTodoSortDirection)grp.readEntry("Sort direction", static_cast<int>(TodoDirectionAscending));
1611 }
1612 setSettingsWidget();
1613 }
1614
doSaveConfig()1615 void CalPrintTodos::doSaveConfig()
1616 {
1617 readSettingsWidget();
1618 if (mConfig) {
1619 KConfigGroup grp(mConfig, groupName());
1620 grp.writeEntry("Page title", mPageTitle);
1621 grp.writeEntry("Print type", int(mTodoPrintType));
1622 grp.writeEntry("Include description", mIncludeDescription);
1623 grp.writeEntry("Include priority", mIncludePriority);
1624 grp.writeEntry("Include categories", mIncludeCategories);
1625 grp.writeEntry("Include start date", mIncludeStartDate);
1626 grp.writeEntry("Include due date", mIncludeDueDate);
1627 grp.writeEntry("Include percentage completed", mIncludePercentComplete);
1628 grp.writeEntry("Connect subtodos", mConnectSubTodos);
1629 grp.writeEntry("Strike out completed summaries", mStrikeOutCompleted);
1630 grp.writeEntry("Sort field", static_cast<int>(mTodoSortField));
1631 grp.writeEntry("Sort direction", static_cast<int>(mTodoSortDirection));
1632 }
1633 CalPrintPluginBase::doSaveConfig();
1634 }
1635
print(QPainter & p,int width,int height)1636 void CalPrintTodos::print(QPainter &p, int width, int height)
1637 {
1638 int possummary = 100;
1639
1640 QRect headerBox(0, 0, width, headerHeight());
1641 QRect footerBox(0, height - footerHeight(), width, footerHeight());
1642 height -= footerHeight();
1643
1644 // Draw the First Page Header
1645 drawHeader(p, mPageTitle, mFromDate, QDate(), headerBox);
1646
1647 // Estimate widths of some data columns.
1648 QFont oldFont(p.font());
1649 p.setFont(QFont(QStringLiteral("sans-serif"), 10));
1650 const int widDate = p.fontMetrics().boundingRect(QLocale::system().toString(QDate(2222, 12, 22), QLocale::ShortFormat)).width();
1651 const int widPct = p.fontMetrics().boundingRect(i18n("%1%", 100)).width() + 27;
1652
1653 // Draw the Column Headers
1654 int mCurrentLinePos = headerHeight() + 5;
1655 QString outStr;
1656
1657 p.setFont(QFont(QStringLiteral("sans-serif"), 9, QFont::Bold));
1658 int lineSpacing = p.fontMetrics().lineSpacing();
1659 mCurrentLinePos += lineSpacing;
1660 int pospriority = -1;
1661 if (mIncludePriority) {
1662 outStr += i18n("Priority");
1663 pospriority = 0;
1664 p.drawText(pospriority, mCurrentLinePos - 2, outStr);
1665 }
1666
1667 int posSoFar = width; // Position of leftmost optional header.
1668
1669 int posdue = -1;
1670 if (mIncludeDueDate) {
1671 outStr.truncate(0);
1672 outStr += i18nc("@label to-do due date", "Due");
1673 const int widDue = std::max(p.fontMetrics().boundingRect(outStr).width(), widDate);
1674 posdue = posSoFar - widDue;
1675 p.drawText(posdue, mCurrentLinePos - 2, outStr);
1676 posSoFar = posdue;
1677 }
1678
1679 int posStart = -1;
1680 if (mIncludeStartDate) {
1681 outStr.truncate(0);
1682 outStr += i18nc("@label to-do start date", "Start");
1683 const int widStart = std::max(p.fontMetrics().boundingRect(outStr).width(), widDate);
1684 posStart = posSoFar - widStart - 5;
1685 p.drawText(posStart, mCurrentLinePos - 2, outStr);
1686 posSoFar = posStart;
1687 }
1688
1689 int poscomplete = -1;
1690 if (mIncludePercentComplete) {
1691 outStr.truncate(0);
1692 outStr += i18nc("@label to-do percentage complete", "Complete");
1693 const int widComplete = std::max(p.fontMetrics().boundingRect(outStr).width(), widPct);
1694 poscomplete = posSoFar - widComplete - 5;
1695 p.drawText(poscomplete, mCurrentLinePos - 2, outStr);
1696 posSoFar = poscomplete;
1697 }
1698
1699 int posCategories = -1;
1700 if (mIncludeCategories) {
1701 outStr.truncate(0);
1702 outStr += i18nc("@label to-do categories", "Tags");
1703 const int widCats = std::max(p.fontMetrics().boundingRect(outStr).width(), 100); // Arbitrary!
1704 posCategories = posSoFar - widCats - 5;
1705 p.drawText(posCategories, mCurrentLinePos - 2, outStr);
1706 }
1707
1708 p.setFont(QFont(QStringLiteral("sans-serif"), 10));
1709
1710 KCalendarCore::Todo::List todoList;
1711 KCalendarCore::Todo::List tempList;
1712
1713 KCalendarCore::SortDirection sortDirection = KCalendarCore::SortDirectionAscending;
1714 switch (mTodoSortDirection) {
1715 case TodoDirectionAscending:
1716 sortDirection = KCalendarCore::SortDirectionAscending;
1717 break;
1718 case TodoDirectionDescending:
1719 sortDirection = KCalendarCore::SortDirectionDescending;
1720 break;
1721 case TodoDirectionUnset:
1722 break;
1723 }
1724
1725 KCalendarCore::TodoSortField sortField = KCalendarCore::TodoSortSummary;
1726 switch (mTodoSortField) {
1727 case TodoFieldSummary:
1728 sortField = KCalendarCore::TodoSortSummary;
1729 break;
1730 case TodoFieldStartDate:
1731 sortField = KCalendarCore::TodoSortStartDate;
1732 break;
1733 case TodoFieldDueDate:
1734 sortField = KCalendarCore::TodoSortDueDate;
1735 break;
1736 case TodoFieldPriority:
1737 sortField = KCalendarCore::TodoSortPriority;
1738 break;
1739 case TodoFieldPercentComplete:
1740 sortField = KCalendarCore::TodoSortPercentComplete;
1741 break;
1742 case TodoFieldCategories:
1743 sortField = KCalendarCore::TodoSortCategories;
1744 break;
1745 case TodoFieldUnset:
1746 break;
1747 }
1748
1749 // Create list of to-dos which will be printed
1750 todoList = mCalendar->todos(sortField, sortDirection);
1751 switch (mTodoPrintType) {
1752 case TodosAll:
1753 break;
1754 case TodosUnfinished:
1755 for (const KCalendarCore::Todo::Ptr &todo : std::as_const(todoList)) {
1756 Q_ASSERT(todo);
1757 if (!todo->isCompleted()) {
1758 tempList.append(todo);
1759 }
1760 }
1761 todoList = tempList;
1762 break;
1763 case TodosDueRange:
1764 for (const KCalendarCore::Todo::Ptr &todo : std::as_const(todoList)) {
1765 Q_ASSERT(todo);
1766 if (todo->hasDueDate()) {
1767 if (todo->dtDue().date() >= mFromDate && todo->dtDue().date() <= mToDate) {
1768 tempList.append(todo);
1769 }
1770 } else {
1771 tempList.append(todo);
1772 }
1773 }
1774 todoList = tempList;
1775 break;
1776 }
1777
1778 // Print to-dos
1779 int count = 0;
1780 for (const KCalendarCore::Todo::Ptr &todo : std::as_const(todoList)) {
1781 if ((mExcludeConfidential && todo->secrecy() == KCalendarCore::Incidence::SecrecyConfidential)
1782 || (mExcludePrivate && todo->secrecy() == KCalendarCore::Incidence::SecrecyPrivate)) {
1783 continue;
1784 }
1785 // Skip sub-to-dos. They will be printed recursively in drawTodo()
1786 if (todo->relatedTo().isEmpty()) { // review(AKONADI_PORT)
1787 count++;
1788 drawTodo(count,
1789 todo,
1790 p,
1791 sortField,
1792 sortDirection,
1793 mConnectSubTodos,
1794 mStrikeOutCompleted,
1795 mIncludeDescription,
1796 pospriority,
1797 possummary,
1798 posCategories,
1799 posStart,
1800 posdue,
1801 poscomplete,
1802 0,
1803 0,
1804 mCurrentLinePos,
1805 width,
1806 height,
1807 todoList,
1808 nullptr);
1809 }
1810 }
1811
1812 if (mPrintFooter) {
1813 drawFooter(p, footerBox);
1814 }
1815 p.setFont(oldFont);
1816 }
1817