Eventviews

monthitem.cpp
1/*
2 SPDX-FileCopyrightText: 2008 Bruno Virlet <bruno.virlet@gmail.com>
3
4 SPDX-License-Identifier: GPL-2.0-or-later WITH Qt-Commercial-exception-1.0
5*/
6
7#include "monthitem.h"
8#include "helper.h"
9#include "monthgraphicsitems.h"
10#include "monthscene.h"
11#include "monthview.h"
12#include "prefs.h"
13#include "prefs_base.h" // Ugly, but needed for the Enums
14
15#include <Akonadi/CalendarUtils>
16#include <Akonadi/EntityTreeModel>
17#include <Akonadi/IncidenceChanger>
18#include <Akonadi/TagCache>
19#include <CalendarSupport/KCalPrefs>
20#include <CalendarSupport/Utils>
21
22#include <KCalUtils/IncidenceFormatter>
23#include <KCalUtils/RecurrenceActions>
24
25#include "calendarview_debug.h"
26#include <KMessageBox>
27
28#include <QDrag>
29
30using namespace EventViews;
31using namespace KCalendarCore;
32
33MonthItem::MonthItem(MonthScene *monthScene)
34 : mMonthScene(monthScene)
35{
36}
37
38MonthItem::~MonthItem()
39{
40 deleteAll();
41}
42
44{
45 qDeleteAll(mMonthGraphicsItemList);
46 mMonthGraphicsItemList.clear();
47}
48
49QWidget *MonthItem::parentWidget() const
50{
51 return mMonthScene ? mMonthScene->monthView() : nullptr;
52}
53
55{
56 // Remove all items
57 qDeleteAll(mMonthGraphicsItemList);
58 mMonthGraphicsItemList.clear();
59
60 const QDate monthStartDate = startDate();
61 const QDate monthEndDate = endDate();
62
63 // For each row of the month view, create an item to build the whole
64 // MonthItem's MonthGraphicsItems.
65 for (QDate d = mMonthScene->mMonthView->actualStartDateTime().date(); d < mMonthScene->mMonthView->actualEndDateTime().date(); d = d.addDays(7)) {
66 QDate end = d.addDays(6);
67
68 int span;
70 if (monthStartDate <= d && monthEndDate >= end) { // MonthItem takes the whole line
71 span = 6;
72 start = d;
73 } else if (monthStartDate >= d && monthEndDate <= end) { // starts and ends on this line
74 start = monthStartDate;
75 span = daySpan();
76 } else if (d <= monthEndDate && monthEndDate <= end) { // MonthItem ends on this line
77 span = mMonthScene->getLeftSpan(monthEndDate);
78 start = d;
79 } else if (d <= monthStartDate && monthStartDate <= end) { // MonthItem begins on this line
80 span = mMonthScene->getRightSpan(monthStartDate);
81 start = monthStartDate;
82 } else { // MonthItem is not on the line
83 continue;
84 }
85
86 // A new item needs to be created
87 auto newItem = new MonthGraphicsItem(this);
88 mMonthGraphicsItemList << newItem;
89 newItem->setStartDate(start);
90 newItem->setDaySpan(span);
91 }
92
93 if (isMoving() || isResizing()) {
94 setZValue(100);
95 } else {
96 setZValue(0);
97 }
98}
99
101{
102 mOverrideDaySpan = daySpan();
103 mOverrideStartDate = startDate();
104 mResizing = true;
105 setZValue(100);
106}
107
109{
110 setZValue(0);
111 mResizing = false; // startDate() and daySpan() return real values again
112
113 if (mOverrideStartDate != startDate() || mOverrideDaySpan != daySpan()) {
114 finalizeResize(mOverrideStartDate, mOverrideStartDate.addDays(mOverrideDaySpan));
115 }
116}
117
119{
120 mOverrideDaySpan = daySpan();
121 mOverrideStartDate = startDate();
122 mMoving = true;
123 setZValue(100);
124}
125
127{
128 setZValue(0);
129 mMoving = false; // startDate() and daySpan() return real values again
130
131 if (mOverrideStartDate != startDate()) {
132 finalizeMove(mOverrideStartDate);
133 }
134}
135
136bool MonthItem::resizeBy(int offsetToPreviousDate)
137{
138 bool ret = false;
139 if (mMonthScene->resizeType() == MonthScene::ResizeLeft) {
140 if (mOverrideDaySpan - offsetToPreviousDate >= 0) {
141 mOverrideStartDate = mOverrideStartDate.addDays(offsetToPreviousDate);
142 mOverrideDaySpan = mOverrideDaySpan - offsetToPreviousDate;
143 ret = true;
144 }
145 } else if (mMonthScene->resizeType() == MonthScene::ResizeRight) {
146 if (mOverrideDaySpan + offsetToPreviousDate >= 0) {
147 mOverrideDaySpan = mOverrideDaySpan + offsetToPreviousDate;
148 ret = true;
149 }
150 }
151
152 if (ret) {
154 }
155 return ret;
156}
157
158void MonthItem::moveBy(int offsetToPreviousDate)
159{
160 mOverrideStartDate = mOverrideStartDate.addDays(offsetToPreviousDate);
162}
163
165{
166 mOverrideStartDate = date;
168}
169
171{
172 for (MonthGraphicsItem *item : std::as_const(mMonthGraphicsItemList)) {
173 item->updateGeometry();
174 }
175}
176
177void MonthItem::setZValue(qreal z)
178{
179 for (MonthGraphicsItem *item : std::as_const(mMonthGraphicsItemList)) {
180 item->setZValue(z);
181 }
182}
183
185{
186 if ((isMoving() || isResizing()) && mOverrideStartDate.isValid()) {
187 return mOverrideStartDate;
188 }
189
190 return realStartDate();
191}
192
194{
195 if ((isMoving() || isResizing()) && mOverrideStartDate.isValid()) {
196 return mOverrideStartDate.addDays(mOverrideDaySpan);
197 }
198
199 return realEndDate();
200}
201
203{
204 if (isMoving() || isResizing()) {
205 return mOverrideDaySpan;
206 }
207 QDateTime start(startDate().startOfDay());
208 QDateTime end(endDate().startOfDay());
209
210 if (start.isValid() && end.isValid()) {
211 return start.daysTo(end);
212 }
213
214 return 0;
215}
216
217bool MonthItem::greaterThan(const MonthItem *e1, const MonthItem *e2)
218{
219 const int leftDaySpan = e1->daySpan();
220 const int rightDaySpan = e2->daySpan();
221 if (leftDaySpan == rightDaySpan) {
222 const QDate leftStartDate = e1->startDate();
223 const QDate rightStartDate = e2->startDate();
224 if (!leftStartDate.isValid() || !rightStartDate.isValid()) {
225 return false;
226 }
227 if (leftStartDate == rightStartDate) {
228 if (e1->allDay() && !e2->allDay()) {
229 return true;
230 }
231 if (!e1->allDay() && e2->allDay()) {
232 return false;
233 }
234 return e1->greaterThanFallback(e2);
235 } else {
236 return leftStartDate > rightStartDate;
237 }
238 }
239 return leftDaySpan > rightDaySpan;
240}
241
242bool MonthItem::greaterThanFallback(const MonthItem *other) const
243{
244 const auto h = qobject_cast<const HolidayMonthItem *>(other);
245
246 // If "other" is a holiday, display it first.
247 return !h;
248}
249
251{
252 if (!startDate().isValid() || !endDate().isValid()) {
253 return;
254 }
255
256 int firstFreeSpace = 0;
257 for (QDate d = startDate(); d <= endDate(); d = d.addDays(1)) {
258 MonthCell *cell = mMonthScene->mMonthCellMap.value(d);
259 if (!cell) {
260 continue; // cell can be null if the item begins outside the month
261 }
262 int firstFreeSpaceTmp = cell->firstFreeSpace();
263 if (firstFreeSpaceTmp > firstFreeSpace) {
264 firstFreeSpace = firstFreeSpaceTmp;
265 }
266 }
267
268 for (QDate d = startDate(); d <= endDate(); d = d.addDays(1)) {
269 MonthCell *cell = mMonthScene->mMonthCellMap.value(d);
270 if (!cell) {
271 continue;
272 }
273 cell->addMonthItem(this, firstFreeSpace);
274 }
275
276 mPosition = firstFreeSpace;
277}
278
279QList<MonthGraphicsItem *> EventViews::MonthItem::monthGraphicsItems() const
280{
281 return mMonthGraphicsItemList;
282}
283
284//-----------------------------------------------------------------
285// INCIDENCEMONTHITEM
286IncidenceMonthItem::IncidenceMonthItem(MonthScene *monthScene,
287 const Akonadi::CollectionCalendar::Ptr &calendar,
288 const Akonadi::Item &aitem,
289 const KCalendarCore::Incidence::Ptr &incidence,
290 QDate recurStartDate)
291 : MonthItem(monthScene)
292 , mCalendar(calendar)
293 , mIncidence(incidence)
294 , mAkonadiItemId(aitem.id())
295{
296 mIsEvent = CalendarSupport::hasEvent(aitem);
297 mIsJournal = CalendarSupport::hasJournal(aitem);
298 mIsTodo = CalendarSupport::hasTodo(aitem);
299
300 KCalendarCore::Incidence::Ptr inc = mIncidence;
301 if (inc->customProperty("KABC", "BIRTHDAY") == QLatin1StringView("YES") || inc->customProperty("KABC", "ANNIVERSARY") == QLatin1StringView("YES")) {
302 const int years = EventViews::yearDiff(inc->dtStart().date(), recurStartDate);
303 if (years > 0) {
304 inc = KCalendarCore::Incidence::Ptr(inc->clone());
305 inc->setReadOnly(false);
306 if (inc->customProperty("KABC", "BIRTHDAY") == QLatin1StringView("YES")) {
307 inc->setDescription(i18ncp("@info/plain a person's age", "1 year old", "%1 years old", years));
308 } else { // anniversary
309 inc->setDescription(i18ncp("@info/plain number of years of marriage", "1 year", "%1 years", years));
310 }
311 inc->setReadOnly(true);
312 mIncidence = inc;
313 }
314 }
315
316 connect(monthScene, &MonthScene::incidenceSelected, this, &IncidenceMonthItem::updateSelection);
317
318 // first set to 0, because it's used in startDate()
319 mRecurDayOffset = 0;
320 const auto incidenceStart = mIncidence->dtStart().toLocalTime().date();
321 if ((mIncidence->recurs() || mIncidence->recurrenceId().isValid()) && incidenceStart.isValid() && recurStartDate.isValid()) {
322 mRecurDayOffset = incidenceStart.daysTo(recurStartDate);
323 }
324}
325
326IncidenceMonthItem::~IncidenceMonthItem() = default;
327
328bool IncidenceMonthItem::greaterThanFallback(const MonthItem *other) const
329{
330 const auto o = qobject_cast<const IncidenceMonthItem *>(other);
331 if (!o) {
332 return MonthItem::greaterThanFallback(other);
333 }
334
335 if (allDay() != o->allDay()) {
336 return allDay();
337 }
338 const KCalendarCore::Incidence::Ptr otherIncidence = o->mIncidence;
339
340 if (mIncidence->dtStart().time() != otherIncidence->dtStart().time()) {
341 return mIncidence->dtStart().time() < otherIncidence->dtStart().time();
342 }
343
344 // as a last resort, compare uids
345 return mIncidence->uid() < otherIncidence->uid();
346}
347
348QDate IncidenceMonthItem::realStartDate() const
349{
350 if (!mIncidence) {
351 return {};
352 }
353
354 const QDateTime dt = mIncidence->dateTime(Incidence::RoleDisplayStart);
355 const QDate start = dt.toLocalTime().date();
356
357 return start.addDays(mRecurDayOffset);
358}
359
360QDate IncidenceMonthItem::realEndDate() const
361{
362 if (!mIncidence) {
363 return {};
364 }
365
366 QDateTime dt = mIncidence->dateTime(KCalendarCore::Incidence::RoleDisplayEnd);
367 if (!mIncidence->allDay() && dt > mIncidence->dateTime(KCalendarCore::Incidence::RoleDisplayStart)) {
368 // If dt's time portion is 00:00:00, the incidence ends on the previous day
369 // unless it also starts at 00:00:00 (a duration of 0).
370 dt = dt.addMSecs(-1);
371 }
372 const QDate end = dt.toLocalTime().date();
373
374 return end.addDays(mRecurDayOffset);
375}
376
377bool IncidenceMonthItem::allDay() const
378{
379 return mIncidence->allDay();
380}
381
382bool IncidenceMonthItem::isMoveable() const
383{
384 return mCalendar->hasRight(Akonadi::Collection::CanChangeItem);
385}
386
387bool IncidenceMonthItem::isResizable() const
388{
389 return mCalendar->hasRight(Akonadi::Collection::CanChangeItem);
390}
391
392void IncidenceMonthItem::finalizeMove(const QDate &newStartDate)
393{
394 Q_ASSERT(isMoveable());
395
396 if (startDate().isValid()) {
397 if (newStartDate.isValid()) {
398 updateDates(startDate().daysTo(newStartDate), startDate().daysTo(newStartDate));
399 } else {
400 if (QDrag *drag = CalendarSupport::createDrag(akonadiItem(), this)) {
401 drag->exec();
402 }
403 }
404 }
405}
406
407void IncidenceMonthItem::finalizeResize(const QDate &newStartDate, const QDate &newEndDate)
408{
409 Q_ASSERT(isResizable());
410
411 if (startDate().isValid() && endDate().isValid() && newStartDate.isValid() && newEndDate.isValid()) {
412 updateDates(startDate().daysTo(newStartDate), endDate().daysTo(newEndDate));
413 }
414}
415
416void IncidenceMonthItem::updateDates(int startOffset, int endOffset)
417{
418 Akonadi::IncidenceChanger *changer = monthScene()->incidenceChanger();
419 if (!changer || (startOffset == 0 && endOffset == 0)) {
420 qCDebug(CALENDARVIEW_LOG) << changer << startOffset << endOffset;
421 return;
422 }
423
424 Akonadi::Item item = akonadiItem();
425 item.setPayload(mIncidence);
426 if (mIncidence->recurs()) {
427 const int res = monthScene()->mMonthView->showMoveRecurDialog(mIncidence, startDate());
428 switch (res) {
430 // All occurrences
431 KCalendarCore::Incidence::Ptr oldIncidence(mIncidence->clone());
432 setNewDates(mIncidence, startOffset, endOffset);
433 changer->modifyIncidence(item, oldIncidence);
434 break;
435 }
436 case KCalUtils::RecurrenceActions::SelectedOccurrence: // Just this occurrence
437 case KCalUtils::RecurrenceActions::FutureOccurrences: { // All future occurrences
438 const bool thisAndFuture = (res == KCalUtils::RecurrenceActions::FutureOccurrences);
439 QDateTime occurrenceDate(mIncidence->dtStart());
440 occurrenceDate.setDate(startDate());
441 KCalendarCore::Incidence::Ptr newIncidence(KCalendarCore::Calendar::createException(mIncidence, occurrenceDate, thisAndFuture));
442 if (newIncidence) {
443 changer->startAtomicOperation(i18nc("@info/plain", "Move occurrence(s)"));
444 setNewDates(newIncidence, startOffset, endOffset);
445 changer->createIncidence(newIncidence, item.parentCollection(), parentWidget());
446 changer->endAtomicOperation();
447 } else {
448 KMessageBox::error(parentWidget(),
449 i18nc("@info",
450 "Unable to add the exception item to the calendar. "
451 "No change will be done."),
452 i18nc("@title:window", "Error Occurred"));
453 }
454 break;
455 }
456 }
457 } else { // Doesn't recur
458 KCalendarCore::Incidence::Ptr oldIncidence(mIncidence->clone());
459 setNewDates(mIncidence, startOffset, endOffset);
460 changer->modifyIncidence(item, oldIncidence);
461 }
462}
463
464void IncidenceMonthItem::updateSelection(const Akonadi::Item &incidence, QDate date)
465{
466 Q_UNUSED(date)
467 setSelected(incidence == akonadiItem());
468}
469
470QString IncidenceMonthItem::text(bool end) const
471{
472 QString ret = mIncidence->summary();
473 if (!allDay() && !mIsJournal && monthScene()->monthView()->preferences()->showTimeInMonthView()) {
474 // Prepend the time str to the text
475 QString timeStr;
476 if (mIsTodo) {
477 KCalendarCore::Todo::Ptr todo = mIncidence.staticCast<Todo>();
478 timeStr = QLocale().toString(todo->dtDue().toLocalTime().time(), QLocale::ShortFormat);
479 } else {
480 if (!end) {
481 QTime time;
482 if (mIncidence->recurs()) {
483 const auto start = mIncidence->dtStart().addDays(mRecurDayOffset).addSecs(-1);
484 time = mIncidence->recurrence()->getNextDateTime(start).toLocalTime().time();
485 } else {
486 time = mIncidence->dtStart().toLocalTime().time();
487 }
488 timeStr = QLocale().toString(time, QLocale::ShortFormat);
489 } else {
490 KCalendarCore::Event::Ptr event = mIncidence.staticCast<Event>();
491 timeStr = QLocale().toString(event->dtEnd().toLocalTime().time(), QLocale::ShortFormat);
492 }
493 }
494 if (!timeStr.isEmpty()) {
495 if (!end) {
496 ret = timeStr + QLatin1Char(' ') + ret;
497 } else {
498 ret = ret + QLatin1Char(' ') + timeStr;
499 }
500 }
501 }
502
503 return ret;
504}
505
506QString IncidenceMonthItem::toolTipText(const QDate &date) const
507{
508 return KCalUtils::IncidenceFormatter::toolTipStr(Akonadi::CalendarUtils::displayName(mCalendar->model(), akonadiItem().parentCollection()),
509 mIncidence,
510 date,
511 true);
512}
513
514QList<QPixmap> IncidenceMonthItem::icons() const
515{
516 QList<QPixmap> ret;
517
518 if (!mIncidence) {
519 return ret;
520 }
521
522 bool specialEvent = false;
523 Akonadi::Item item = akonadiItem();
524
525 const QSet<EventView::ItemIcon> icons = monthScene()->monthView()->preferences()->monthViewIcons();
526
527 QString customIconName;
528 if (icons.contains(EventViews::EventView::CalendarCustomIcon)) {
529 const QString iconName = monthScene()->monthView()->iconForItem(item);
530 if (!iconName.isEmpty() && iconName != QLatin1StringView("view-calendar") && iconName != QLatin1StringView("office-calendar")) {
531 customIconName = iconName;
532 ret << QPixmap(cachedSmallIcon(iconName));
533 }
534 }
535
536 if (mIsEvent) {
537 if (mIncidence->customProperty("KABC", "ANNIVERSARY") == QLatin1StringView("YES")) {
538 specialEvent = true;
539 ret << monthScene()->anniversaryPixmap();
540 } else if (mIncidence->customProperty("KABC", "BIRTHDAY") == QLatin1StringView("YES")) {
541 specialEvent = true;
542 // Disabling birthday icon because it's the birthday agent's icon
543 // and we allow to display the agent's icon now.
544 // ret << monthScene()->birthdayPixmap();
545 }
546
547 // smartins: Disabling the event Pixmap because:
548 // 1. Save precious space so we can read the event's title better.
549 // 2. We don't need a pixmap to tell us an item is an event we
550 // only need one to tell us it's not, as month view was designed for events.
551 // 3. If only to-dos and journals have a pixmap they will be distinguished
552 // from event's much easier.
553
554 // ret << monthScene()->eventPixmap();
555 } else if ((mIsTodo || mIsJournal) && icons.contains(mIsTodo ? EventView::TaskIcon : EventView::JournalIcon)) {
556 QDateTime occurrenceDateTime = mIncidence->dateTime(Incidence::RoleRecurrenceStart).addDays(mRecurDayOffset);
557
558 const QString incidenceIconName = mIncidence->iconName(occurrenceDateTime);
559 if (customIconName != incidenceIconName) {
560 ret << QPixmap(cachedSmallIcon(incidenceIconName));
561 }
562 }
563
564 if (icons.contains(EventView::ReadOnlyIcon) && !mCalendar->hasRight(Akonadi::Collection::CanChangeItem) && !specialEvent) {
565 ret << monthScene()->readonlyPixmap();
566 }
567
568 /* sorry, this looks too cluttered. disable until we can
569 make something prettier; no idea at this time -- allen */
570 if (icons.contains(EventView::ReminderIcon) && mIncidence->hasEnabledAlarms() && !specialEvent) {
571 ret << monthScene()->alarmPixmap();
572 }
573 if (icons.contains(EventView::RecurringIcon) && mIncidence->recurs() && !specialEvent) {
574 ret << monthScene()->recurPixmap();
575 }
576 // TODO: check what to do with Reply
577
578 return ret;
579}
580
581QColor IncidenceMonthItem::catColor() const
582{
583 Q_ASSERT(mIncidence);
584 const auto &prefs = monthScene()->monthView()->preferences();
585
586 const auto &categories = mIncidence->categories();
587 if (categories.isEmpty() || !Akonadi::TagCache::instance()->tagColor(categories.first()).isValid()) {
588 const auto &colorPreference = prefs->monthViewColors();
589 if (colorPreference == PrefsBase::CategoryOnly) {
590 return CalendarSupport::KCalPrefs::instance()->unsetCategoryColor();
591 }
592 return EventViews::resourceColor(mCalendar->collection(), prefs);
593 }
594 return Akonadi::TagCache::instance()->tagColor(categories.first());
595}
596
597QColor IncidenceMonthItem::bgColor() const
598{
599 const auto &prefs = monthScene()->monthView()->preferences();
600
601 if (!prefs->todosUseCategoryColors() && mIsTodo) {
603 Q_ASSERT(todo);
604 if (todo) {
605 // this is dtDue if there's no dtRecurrence
606 const auto dtRecurrence = todo->dtRecurrence().toLocalTime().date();
607 const auto today = QDate::currentDate();
608 if (startDate() >= dtRecurrence) {
609 if (todo->isOverdue() && today > startDate()) {
610 return prefs->todoOverdueColor();
611 }
612 if (today == startDate() && !todo->isCompleted()) {
613 return prefs->todoDueTodayColor();
614 }
615 }
616 }
617 }
618
619 const auto &colorPreference = prefs->monthViewColors();
620 const auto bgDisplaysResource = colorPreference == PrefsBase::MonthItemResourceInsideCategoryOutside || colorPreference == PrefsBase::MonthItemResourceOnly;
621 return bgDisplaysResource ? EventViews::resourceColor(mCalendar->collection(), prefs) : catColor();
622}
623
624QColor IncidenceMonthItem::frameColor() const
625{
626 const auto &prefs = monthScene()->monthView()->preferences();
627 const auto frameDisplaysResource =
628 (prefs->monthViewColors() == PrefsBase::MonthItemResourceOnly || prefs->monthViewColors() == PrefsBase::MonthItemCategoryInsideResourceOutside);
629 const auto frameColor = frameDisplaysResource ? EventViews::resourceColor(mCalendar->collection(), prefs) : catColor();
630 return EventView::itemFrameColor(frameColor, selected());
631}
632
633Akonadi::Item IncidenceMonthItem::akonadiItem() const
634{
635 if (mIncidence) {
636 return mCalendar->item(mIncidence);
637 } else {
638 return {};
639 }
640}
641
642KCalendarCore::Incidence::Ptr IncidenceMonthItem::incidence() const
643{
644 return mIncidence;
645}
646
647Akonadi::Item::Id IncidenceMonthItem::akonadiItemId() const
648{
649 return mAkonadiItemId;
650}
651
652Akonadi::CollectionCalendar::Ptr IncidenceMonthItem::calendar() const
653{
654 return mCalendar;
655}
656
657void IncidenceMonthItem::setNewDates(const KCalendarCore::Incidence::Ptr &incidence, int startOffset, int endOffset)
658{
659 if (mIsTodo) {
660 // For to-dos endOffset is ignored because it will always be == to startOffset because we only
661 // support moving to-dos, not resizing them. There are no multi-day to-dos.
662 // Lets just call it offset to reduce confusion.
663 const int offset = startOffset;
664
665 KCalendarCore::Todo::Ptr todo = incidence.staticCast<Todo>();
666 QDateTime due = todo->dtDue();
667 QDateTime start = todo->dtStart();
668 if (due.isValid()) { // Due has priority over start.
669 // We will only move the due date, unlike events where we move both.
670 due = due.addDays(offset);
671 todo->setDtDue(due);
672
673 if (start.isValid() && start > due) {
674 // Start can't be bigger than due.
675 todo->setDtStart(due);
676 }
677 } else if (start.isValid()) {
678 // So we're displaying a to-do that doesn't have due date, only start...
679 start = start.addDays(offset);
680 todo->setDtStart(start);
681 } else {
682 // This never happens
683 qCWarning(CALENDARVIEW_LOG) << "Move what? uid:" << todo->uid() << "; summary=" << todo->summary();
684 }
685 } else {
686 incidence->setDtStart(incidence->dtStart().addDays(startOffset));
687 if (mIsEvent) {
688 KCalendarCore::Event::Ptr event = incidence.staticCast<Event>();
689 event->setDtEnd(event->dtEnd().addDays(endOffset));
690 }
691 }
692}
693
694//-----------------------------------------------------------------
695// HOLIDAYMONTHITEM
696HolidayMonthItem::HolidayMonthItem(MonthScene *monthScene, QDate date, const QString &name)
697 : HolidayMonthItem(monthScene, date, date, name)
698{
699}
700
701HolidayMonthItem::HolidayMonthItem(MonthScene *monthScene, QDate startDate, QDate endDate, const QString &name)
702 : MonthItem(monthScene)
703 , mStartDate(startDate)
704 , mEndDate(endDate)
705 , mName(name)
706{
707}
708
709HolidayMonthItem::~HolidayMonthItem() = default;
710
711bool HolidayMonthItem::greaterThanFallback(const MonthItem *other) const
712{
713 const auto o = qobject_cast<const HolidayMonthItem *>(other);
714 // always put holidays on top
715 return !o;
716}
717
718void HolidayMonthItem::finalizeMove(const QDate &newStartDate)
719{
720 Q_UNUSED(newStartDate)
721 Q_ASSERT(false);
722}
723
724void HolidayMonthItem::finalizeResize(const QDate &newStartDate, const QDate &newEndDate)
725{
726 Q_UNUSED(newStartDate)
727 Q_UNUSED(newEndDate)
728 Q_ASSERT(false);
729}
730
731QList<QPixmap> HolidayMonthItem::icons() const
732{
733 QList<QPixmap> ret;
734 ret << monthScene()->holidayPixmap();
735
736 return ret;
737}
738
739QColor HolidayMonthItem::bgColor() const
740{
741 // FIXME: Currently, only this value is settable in the options.
742 // There is a monthHolidaysBackgroundColor() option too. Maybe it would be
743 // wise to merge those two.
744 return monthScene()->monthView()->preferences()->agendaHolidaysBackgroundColor();
745}
746
747QColor HolidayMonthItem::frameColor() const
748{
749 return Qt::black;
750}
751
752#include "moc_monthitem.cpp"
Collection & parentCollection()
void setPayload(const T &p)
QColor tagColor(const QString &tagName) const
static TagCache * instance()
static QColor itemFrameColor(const QColor &color, bool selected)
Returns a variation of color that will be used for the border of an agenda or month item.
Keeps information about a month cell.
A MonthGraphicsItem representing a part of an event.
A month item manages different MonthGraphicsItems.
Definition monthitem.h:27
static bool greaterThan(const MonthItem *e1, const MonthItem *e2)
Compares two items to decide which to place in the view first.
void moveTo(QDate date)
Called during a drag to move the item to a particular date.
void setSelected(bool selected)
Sets the selection state of this item.
Definition monthitem.h:210
virtual void finalizeResize(const QDate &newStartDate, const QDate &newEndDate)=0
Called after a resize operation.
void updateGeometry()
Updates geometry of all MonthGraphicsItems.
bool isMoving() const
Returns true if the item is being moved.
Definition monthitem.h:167
void beginMove()
Begin a move.
MonthScene * monthScene() const
Returns the associated month scene to this item.
Definition monthitem.h:121
void endMove()
End a move.
QDate endDate() const
The end date of the incidence, generally realEndDate.
virtual bool allDay() const =0
True if this item last all the day.
void endResize()
End a resize.
void beginResize()
Begin a resize.
virtual QDate realEndDate() const =0
This is the real end date, usually the end date of the incidence.
void deleteAll()
Deletes all MonthGraphicsItem this item handles.
Definition monthitem.cpp:43
void updatePosition()
Find the lowest possible position for this item.
bool selected() const
Returns true if this item is selected.
Definition monthitem.h:105
bool resizeBy(int offsetFromPreviousDate)
Called during resize to resize the item a bit, relative to the previous resize step.
bool isResizing() const
Returns true if the item is being resized.
Definition monthitem.h:175
QDate startDate() const
The start date of the incidence, generally realStartDate.
virtual void finalizeMove(const QDate &newStartDate)=0
Called after a move operation.
void updateMonthGraphicsItems()
Update the monthgraphicsitems.
Definition monthitem.cpp:54
void moveBy(int offsetFromPreviousDate)
Called during move to move the item a bit, relative to the previous move step.
virtual bool greaterThanFallback(const MonthItem *other) const
Compare this event with a second one, if the former function is not able to sort them.
virtual QDate realStartDate() const =0
This is the real start date, usually the start date of the incidence.
int daySpan() const
The number of days this item spans.
QSharedPointer< Calendar > Ptr
static Incidence::Ptr createException(const Incidence::Ptr &incidence, const QDateTime &recurrenceId, bool thisAndFuture=false)
QSharedPointer< Event > Ptr
QSharedPointer< Incidence > Ptr
QSharedPointer< Todo > Ptr
Q_SCRIPTABLE QString start(QString train="")
Q_SCRIPTABLE Q_NOREPLY void start()
QString i18nc(const char *context, const char *text, const TYPE &arg...)
QString i18ncp(const char *context, const char *singular, const char *plural, const TYPE &arg...)
AKONADI_CALENDAR_EXPORT KCalendarCore::Todo::Ptr todo(const Akonadi::Item &item)
AKONADI_CALENDAR_EXPORT QString displayName(Akonadi::ETMCalendar *calendar, const Akonadi::Collection &collection)
Namespace EventViews provides facilities for displaying incidences, including events,...
Definition agenda.h:33
EVENTVIEWS_EXPORT QColor resourceColor(const Akonadi::Collection &collection, const PrefsPtr &preferences)
This method returns the proper resource / subresource color for the view.
Definition helper.cpp:56
QPixmap cachedSmallIcon(const QString &name)
Equivalent to SmallIcon( name ), but uses QPixmapCache.
Definition helper.cpp:83
int yearDiff(QDate start, QDate end)
Returns the number of years between the start QDate and the end QDate (i.e.
Definition helper.cpp:78
KCALUTILS_EXPORT QString toolTipStr(const QString &sourceName, const KCalendarCore::IncidenceBase::Ptr &incidence, QDate date=QDate(), bool richText=true)
void error(QWidget *parent, const QString &text, const QString &title, const KGuiItem &buttonOk, Options options=Notify)
bool isValid(QStringView ifopt)
QString name(StandardAction id)
const QList< QKeySequence > & end()
const QList< QKeySequence > & preferences()
bool isValid() const const
QDate addDays(qint64 ndays) const const
QDate currentDate()
bool isValid(int year, int month, int day)
QDateTime addMSecs(qint64 msecs) const const
QDate date() const const
QDateTime toLocalTime() const const
QMetaObject::Connection connect(const QObject *sender, PointerToMemberFunction signal, Functor functor)
virtual bool event(QEvent *e)
T qobject_cast(QObject *object)
QSharedPointer< X > staticCast() const const
bool isEmpty() const const
This file is part of the KDE documentation.
Documentation copyright © 1996-2025 The KDE developers.
Generated on Fri Apr 25 2025 11:46:21 by doxygen 1.13.2 written by Dimitri van Heesch, © 1997-2006

KDE's Doxygen guidelines are available online.