diff --git a/src/month/monthitem.cpp b/src/month/monthitem.cpp index a7ba3b5..db041e3 100644 --- a/src/month/monthitem.cpp +++ b/src/month/monthitem.cpp @@ -1,780 +1,766 @@ /* Copyright (c) 2008 Bruno Virlet This program is free software; you can redistribute it and/or modify it under the terms of the GNU General Public License as published by the Free Software Foundation; either version 2 of the License, or (at your option) any later version. This program is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for more details. You should have received a copy of the GNU General Public License along with this program; if not, write to the Free Software Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA. As a special exception, permission is given to link this program with any edition of Qt, and distribute the resulting executable, without including the source code for Qt in the source distribution. */ #include "monthitem.h" #include "helper.h" #include "monthgraphicsitems.h" #include "monthscene.h" #include "monthview.h" #include "prefs.h" #include "prefs_base.h" // Ugly, but needed for the Enums #include #include #include #include #include #include #include "calendarview_debug.h" using namespace EventViews; using namespace KCalCore; MonthItem::MonthItem(MonthScene *monthScene) : mMonthScene(monthScene) , mSelected(false) , mMoving(false) , mResizing(false) { } MonthItem::~MonthItem() { deleteAll(); } void MonthItem::deleteAll() { qDeleteAll(mMonthGraphicsItemList); mMonthGraphicsItemList.clear(); } QWidget *MonthItem::parentWidget() const { return mMonthScene ? mMonthScene->monthView() : nullptr; } void MonthItem::updateMonthGraphicsItems() { // Remove all items qDeleteAll(mMonthGraphicsItemList); mMonthGraphicsItemList.clear(); const QDate monthStartDate = startDate(); const QDate monthEndDate = endDate(); // For each row of the month view, create an item to build the whole // MonthItem's MonthGraphicsItems. for (QDate d = mMonthScene->mMonthView->actualStartDateTime().date(); d < mMonthScene->mMonthView->actualEndDateTime().date(); d = d.addDays(7)) { QDate end = d.addDays(6); int span; QDate start; if (monthStartDate <= d && monthEndDate >= end) { // MonthItem takes the whole line span = 6; start = d; } else if (monthStartDate >= d && monthEndDate <= end) { // starts and ends on this line start = monthStartDate; span = daySpan(); } else if (d <= monthEndDate && monthEndDate <= end) { // MonthItem ends on this line span = mMonthScene->getLeftSpan(monthEndDate); start = d; } else if (d <= monthStartDate && monthStartDate <= end) { // MonthItem begins on this line span = mMonthScene->getRightSpan(monthStartDate); start = monthStartDate; } else { // MonthItem is not on the line continue; } // A new item needs to be created MonthGraphicsItem *newItem = new MonthGraphicsItem(this); mMonthGraphicsItemList << newItem; newItem->setStartDate(start); newItem->setDaySpan(span); } if (isMoving() || isResizing()) { setZValue(100); } else { setZValue(0); } } void MonthItem::beginResize() { mOverrideDaySpan = daySpan(); mOverrideStartDate = startDate(); mResizing = true; setZValue(100); } void MonthItem::endResize() { setZValue(0); mResizing = false; // startDate() and daySpan() return real values again if (mOverrideStartDate != startDate() || mOverrideDaySpan != daySpan()) { finalizeResize(mOverrideStartDate, mOverrideStartDate.addDays(mOverrideDaySpan)); } } void MonthItem::beginMove() { mOverrideDaySpan = daySpan(); mOverrideStartDate = startDate(); mMoving = true; setZValue(100); } void MonthItem::endMove() { setZValue(0); mMoving = false; // startDate() and daySpan() return real values again if (mOverrideStartDate != startDate()) { finalizeMove(mOverrideStartDate); } } bool MonthItem::resizeBy(int offsetToPreviousDate) { bool ret = false; if (mMonthScene->resizeType() == MonthScene::ResizeLeft) { if (mOverrideDaySpan - offsetToPreviousDate >= 0) { mOverrideStartDate = mOverrideStartDate.addDays(offsetToPreviousDate); mOverrideDaySpan = mOverrideDaySpan - offsetToPreviousDate; ret = true; } } else if (mMonthScene->resizeType() == MonthScene::ResizeRight) { if (mOverrideDaySpan + offsetToPreviousDate >= 0) { mOverrideDaySpan = mOverrideDaySpan + offsetToPreviousDate; ret = true; } } if (ret) { updateMonthGraphicsItems(); } return ret; } void MonthItem::moveBy(int offsetToPreviousDate) { mOverrideStartDate = mOverrideStartDate.addDays(offsetToPreviousDate); updateMonthGraphicsItems(); } void MonthItem::updateGeometry() { foreach (MonthGraphicsItem *item, mMonthGraphicsItemList) { item->updateGeometry(); } } void MonthItem::setZValue(qreal z) { foreach (MonthGraphicsItem *item, mMonthGraphicsItemList) { item->setZValue(z); } } QDate MonthItem::startDate() const { if (isMoving() || isResizing()) { return mOverrideStartDate; } return realStartDate(); } QDate MonthItem::endDate() const { if (isMoving() || isResizing()) { return mOverrideStartDate.addDays(mOverrideDaySpan); } return realEndDate(); } int MonthItem::daySpan() const { if (isMoving() || isResizing()) { return mOverrideDaySpan; } QDateTime start(startDate()); QDateTime end(endDate()); if (start.isValid() && end.isValid()) { return start.daysTo(end); } return 0; } bool MonthItem::greaterThan(const MonthItem *e1, const MonthItem *e2) { const QDate leftStartDate = e1->startDate(); const QDate rightStartDate = e2->startDate(); if (!leftStartDate.isValid() || !rightStartDate.isValid()) { return false; } if (leftStartDate == rightStartDate) { const int leftDaySpan = e1->daySpan(); const int rightDaySpan = e2->daySpan(); if (leftDaySpan == rightDaySpan) { if (e1->allDay() && !e2->allDay()) { return true; } if (!e1->allDay() && e2->allDay()) { return false; } return e1->greaterThanFallback(e2); } else { return leftDaySpan > rightDaySpan; } } return leftStartDate < rightStartDate; } bool MonthItem::greaterThanFallback(const MonthItem *other) const { const HolidayMonthItem *h = qobject_cast(other); // If "other" is a holiday, display it first. return !h; } void MonthItem::updatePosition() { if (!startDate().isValid() || !endDate().isValid()) { return; } int firstFreeSpace = 0; for (QDate d = startDate(); d <= endDate(); d = d.addDays(1)) { MonthCell *cell = mMonthScene->mMonthCellMap.value(d); if (!cell) { continue; // cell can be null if the item begins outside the month } int firstFreeSpaceTmp = cell->firstFreeSpace(); if (firstFreeSpaceTmp > firstFreeSpace) { firstFreeSpace = firstFreeSpaceTmp; } } for (QDate d = startDate(); d <= endDate(); d = d.addDays(1)) { MonthCell *cell = mMonthScene->mMonthCellMap.value(d); if (!cell) { continue; } cell->addMonthItem(this, firstFreeSpace); } mPosition = firstFreeSpace; } QList EventViews::MonthItem::monthGraphicsItems() const { return mMonthGraphicsItemList; } //----------------------------------------------------------------- // INCIDENCEMONTHITEM IncidenceMonthItem::IncidenceMonthItem(MonthScene *monthScene, const Akonadi::ETMCalendar::Ptr &calendar, const Akonadi::Item &aitem, const KCalCore::Incidence::Ptr &incidence, const QDate &recurStartDate) : MonthItem(monthScene) , mCalendar(calendar) , mIncidence(incidence) , mAkonadiItemId(aitem.id()) { mIsEvent = CalendarSupport::hasEvent(aitem); mIsJournal = CalendarSupport::hasJournal(aitem); mIsTodo = CalendarSupport::hasTodo(aitem); KCalCore::Incidence::Ptr inc = mIncidence; if (inc->customProperty("KABC", "BIRTHDAY") == QLatin1String("YES") || inc->customProperty("KABC", "ANNIVERSARY") == QLatin1String("YES")) { const int years = EventViews::yearDiff(inc->dtStart().date(), recurStartDate); if (years > 0) { inc = KCalCore::Incidence::Ptr(inc->clone()); inc->setReadOnly(false); inc->setDescription(i18np("%2 1 year", "%2 %1 years", years, i18n("Age:"))); inc->setReadOnly(true); mIncidence = inc; } } connect(monthScene, &MonthScene::incidenceSelected, this, &IncidenceMonthItem::updateSelection); // first set to 0, because it's used in startDate() mRecurDayOffset = 0; if ((mIncidence->recurs() || mIncidence->recurrenceId().isValid()) && startDate().isValid() && recurStartDate.isValid()) { mRecurDayOffset = startDate().daysTo(recurStartDate); } } IncidenceMonthItem::~IncidenceMonthItem() { } bool IncidenceMonthItem::greaterThanFallback(const MonthItem *other) const { const IncidenceMonthItem *o = qobject_cast(other); if (!o) { return MonthItem::greaterThanFallback(other); } if (allDay() != o->allDay()) { return allDay(); } const KCalCore::Incidence::Ptr otherIncidence = o->mIncidence; if (mIncidence->dtStart().time() != otherIncidence->dtStart().time()) { return mIncidence->dtStart().time() < otherIncidence->dtStart().time(); } // as a last resort, compare uids return mIncidence->uid() < otherIncidence->uid(); } QDate IncidenceMonthItem::realStartDate() const { if (!mIncidence) { return QDate(); } const QDateTime dt = mIncidence->dateTime(Incidence::RoleDisplayStart); const QDate start = dt.toLocalTime().date(); return start.addDays(mRecurDayOffset); } QDate IncidenceMonthItem::realEndDate() const { if (!mIncidence) { return QDate(); } const QDateTime dt = mIncidence->dateTime(KCalCore::Incidence::RoleDisplayEnd); const QDate end = dt.toLocalTime().date(); return end.addDays(mRecurDayOffset); } bool IncidenceMonthItem::allDay() const { return mIncidence->allDay(); } bool IncidenceMonthItem::isMoveable() const { return monthScene()->mMonthView->calendar()->hasRight(akonadiItem(), Akonadi::Collection::CanChangeItem); } bool IncidenceMonthItem::isResizable() const { return mIsEvent && monthScene()->mMonthView->calendar()->hasRight( akonadiItem(), Akonadi::Collection::CanChangeItem); } void IncidenceMonthItem::finalizeMove(const QDate &newStartDate) { Q_ASSERT(isMoveable()); if (startDate().isValid() && newStartDate.isValid()) { updateDates(startDate().daysTo(newStartDate), startDate().daysTo(newStartDate)); } } void IncidenceMonthItem::finalizeResize(const QDate &newStartDate, const QDate &newEndDate) { Q_ASSERT(isResizable()); if (startDate().isValid() && endDate().isValid() && newStartDate.isValid() && newEndDate.isValid()) { updateDates(startDate().daysTo(newStartDate), endDate().daysTo(newEndDate)); } } void IncidenceMonthItem::updateDates(int startOffset, int endOffset) { Akonadi::IncidenceChanger *changer = monthScene()->incidenceChanger(); if (!changer || (startOffset == 0 && endOffset == 0)) { qCDebug(CALENDARVIEW_LOG) << changer << startOffset << endOffset; return; } Akonadi::Item item = akonadiItem(); item.setPayload(mIncidence); if (mIncidence->recurs()) { const int res = monthScene()->mMonthView->showMoveRecurDialog(mIncidence, startDate()); switch (res) { case KCalUtils::RecurrenceActions::AllOccurrences: { // All occurrences KCalCore::Incidence::Ptr oldIncidence(mIncidence->clone()); setNewDates(mIncidence, startOffset, endOffset); changer->modifyIncidence(item, oldIncidence); break; } case KCalUtils::RecurrenceActions::SelectedOccurrence: // Just this occurrence case KCalUtils::RecurrenceActions::FutureOccurrences: { // All future occurrences const bool thisAndFuture = (res == KCalUtils::RecurrenceActions::FutureOccurrences); QDateTime occurrenceDate(mIncidence->dtStart()); occurrenceDate.setDate(startDate()); KCalCore::Incidence::Ptr newIncidence(KCalCore::Calendar::createException( mIncidence, occurrenceDate, thisAndFuture)); if (newIncidence) { changer->startAtomicOperation(i18n("Move occurrence(s)")); setNewDates(newIncidence, startOffset, endOffset); changer->createIncidence(newIncidence, item.parentCollection(), parentWidget()); changer->endAtomicOperation(); } else { KMessageBox::sorry( parentWidget(), i18n("Unable to add the exception item to the calendar. " "No change will be done."), i18n("Error Occurred")); } break; } } } else { // Doesn't recur KCalCore::Incidence::Ptr oldIncidence(mIncidence->clone()); setNewDates(mIncidence, startOffset, endOffset); changer->modifyIncidence(item, oldIncidence); } } void IncidenceMonthItem::updateSelection(const Akonadi::Item &incidence, const QDate &date) { Q_UNUSED(date); setSelected(incidence == akonadiItem()); } QString IncidenceMonthItem::text(bool end) const { QString ret = mIncidence->summary(); if (!allDay() && !mIsJournal && monthScene()->monthView()->preferences()->showTimeInMonthView()) { // Prepend the time str to the text QString timeStr; if (mIsTodo) { KCalCore::Todo::Ptr todo = mIncidence.staticCast(); timeStr = QLocale().toString(todo->dtDue().toLocalTime().time(), QLocale::ShortFormat); } else { if (!end) { QTime time; if (mIncidence->recurs()) { const auto start = mIncidence->dtStart().addDays(mRecurDayOffset).addSecs(-1); time = mIncidence->recurrence()->getNextDateTime(start).toLocalTime().time(); } else { time = mIncidence->dtStart().toLocalTime().time(); } timeStr = QLocale().toString(time, QLocale::ShortFormat); } else { KCalCore::Event::Ptr event = mIncidence.staticCast(); timeStr = QLocale().toString( event->dtEnd().toLocalTime().time(), QLocale::ShortFormat); } } if (!timeStr.isEmpty()) { if (!end) { ret = timeStr + QLatin1Char(' ') + ret; } else { ret = ret + QLatin1Char(' ') + timeStr; } } } return ret; } QString IncidenceMonthItem::toolTipText(const QDate &date) const { return KCalUtils::IncidenceFormatter::toolTipStr( CalendarSupport::displayName(mCalendar.data(), akonadiItem().parentCollection()), mIncidence, date, true); } QVector IncidenceMonthItem::icons() const { QVector ret; if (!mIncidence) { return ret; } bool specialEvent = false; Akonadi::Item item = akonadiItem(); const QSet icons = monthScene()->monthView()->preferences()->monthViewIcons(); QString customIconName; if (icons.contains(EventViews::EventView::CalendarCustomIcon)) { const QString iconName = monthScene()->monthView()->iconForItem(item); if (!iconName.isEmpty() && iconName != QLatin1String("view-calendar") && iconName != QLatin1String("office-calendar")) { customIconName = iconName; ret << QPixmap(cachedSmallIcon(iconName)); } } if (mIsEvent) { if (mIncidence->customProperty("KABC", "ANNIVERSARY") == QLatin1String("YES")) { specialEvent = true; ret << monthScene()->anniversaryPixmap(); } else if (mIncidence->customProperty("KABC", "BIRTHDAY") == QLatin1String("YES")) { specialEvent = true; // Disabling birthday icon because it's the birthday agent's icon // and we allow to display the agent's icon now. //ret << monthScene()->birthdayPixmap(); } // smartins: Disabling the event Pixmap because: // 1. Save precious space so we can read the event's title better. // 2. We don't need a pixmap to tell us an item is an event we // only need one to tell us it's not, as month view was designed for events. // 3. If only to-dos and journals have a pixmap they will be distinguished // from event's much easier. // ret << monthScene()->eventPixmap(); } else if ((mIsTodo || mIsJournal) && icons.contains(mIsTodo ? EventView::TaskIcon : EventView::JournalIcon)) { QDateTime occurrenceDateTime = mIncidence->dateTime(Incidence::RoleRecurrenceStart); occurrenceDateTime.setDate(realStartDate()); const QString incidenceIconName = mIncidence->iconName(occurrenceDateTime); if (customIconName != incidenceIconName) { ret << QPixmap(cachedSmallIcon(incidenceIconName)); } } if (icons.contains(EventView::ReadOnlyIcon) && !monthScene()->mMonthView->calendar()->hasRight(item, Akonadi::Collection::CanChangeItem) && !specialEvent) { ret << monthScene()->readonlyPixmap(); } /* sorry, this looks too cluttered. disable until we can make something prettier; no idea at this time -- allen */ if (icons.contains(EventView::ReminderIcon) && mIncidence->hasEnabledAlarms() && !specialEvent) { ret << monthScene()->alarmPixmap(); } if (icons.contains(EventView::RecurringIcon) && mIncidence->recurs() && !specialEvent) { ret << monthScene()->recurPixmap(); } //TODO: check what to do with Reply return ret; } QColor IncidenceMonthItem::catColor() const { Q_ASSERT(mIncidence); - const QStringList categories = mIncidence->categories(); - QString cat; - if (!categories.isEmpty()) { - cat = categories.at(0); - } + const auto &prefs = monthScene()->monthView()->preferences(); - return cat.isEmpty() ? CalendarSupport::KCalPrefs::instance()->unsetCategoryColor() - : CalendarSupport::KCalPrefs::instance()->categoryColor(cat); + const auto &categories = mIncidence->categories(); + if (categories.isEmpty() || !CalendarSupport::KCalPrefs::instance()->hasCategoryColor(categories.first())) { + const auto &colorPreference = prefs->monthViewColors(); + if (colorPreference == PrefsBase::CategoryOnly) { + return CalendarSupport::KCalPrefs::instance()->unsetCategoryColor(); + } + return EventViews::resourceColor(akonadiItem(), prefs); + } + return CalendarSupport::KCalPrefs::instance()->categoryColor(categories.first()); } QColor IncidenceMonthItem::bgColor() const { - QColor bgColor = QColor(); // Default invalid color; + const auto &prefs = monthScene()->monthView()->preferences(); - PrefsPtr prefs = monthScene()->monthView()->preferences(); - if (mIsTodo && !prefs->todosUseCategoryColors()) { + if (!prefs->todosUseCategoryColors() && mIsTodo) { Todo::Ptr todo = CalendarSupport::todo(akonadiItem()); Q_ASSERT(todo); if (todo) { - const QDate dtRecurrence // this is dtDue if there's no dtRecurrence - = todo->dtRecurrence().toLocalTime().date(); - const QDate today = QDate::currentDate(); - if (todo->isOverdue() && today > startDate() && startDate() >= dtRecurrence) { - bgColor = prefs->todoOverdueColor(); - } else if (today == startDate() && !todo->isCompleted() - && startDate() >= dtRecurrence) { - bgColor = prefs->todoDueTodayColor(); + // this is dtDue if there's no dtRecurrence + const auto dtRecurrence = todo->dtRecurrence().toLocalTime().date(); + const auto today = QDate::currentDate(); + if (startDate() >= dtRecurrence) { + if (todo->isOverdue() && today > startDate()) { + return prefs->todoOverdueColor(); + } + if (today == startDate() && !todo->isCompleted()) { + return prefs->todoDueTodayColor(); + } } } } - if (!bgColor.isValid()) { - if (prefs->monthViewColors() == PrefsBase::MonthItemResourceOnly - || prefs->monthViewColors() == PrefsBase::MonthItemResourceInsideCategoryOutside) { - bgColor = EventViews::resourceColor(akonadiItem(), prefs); - } else { - bgColor = catColor(); - } - } - - if (!bgColor.isValid()) { - bgColor = Qt::white; - } - - return bgColor; + const auto &colorPreference = prefs->monthViewColors(); + const auto bgDisplaysResource = colorPreference == PrefsBase::MonthItemResourceInsideCategoryOutside + || colorPreference == PrefsBase::MonthItemResourceOnly; + return bgDisplaysResource ? EventViews::resourceColor(akonadiItem(), prefs) : catColor(); } QColor IncidenceMonthItem::frameColor() const { - QColor frameColor; - - PrefsPtr prefs = monthScene()->monthView()->preferences(); - if (prefs->monthViewColors() == PrefsBase::MonthItemResourceOnly - || prefs->monthViewColors() == PrefsBase::MonthItemCategoryInsideResourceOutside - || (mIncidence->categories().isEmpty() && prefs->monthViewColors() - == PrefsBase::MonthItemResourceInsideCategoryOutside)) { - Q_ASSERT(mIncidence); - frameColor = EventViews::resourceColor(akonadiItem(), prefs); - } else { - frameColor = catColor(); - } - + const auto &prefs = monthScene()->monthView()->preferences(); + const auto frameDisplaysResource = + (prefs->monthViewColors() == PrefsBase::MonthItemResourceOnly + || prefs->monthViewColors() == PrefsBase::MonthItemCategoryInsideResourceOutside); + const auto frameColor = frameDisplaysResource ? EventViews::resourceColor(akonadiItem(), prefs) : catColor(); return EventView::itemFrameColor(frameColor, selected()); } Akonadi::Item IncidenceMonthItem::akonadiItem() const { if (mIncidence) { return monthScene()->mMonthView->calendar()->item(mIncidence); } else { return Akonadi::Item(); } } KCalCore::Incidence::Ptr IncidenceMonthItem::incidence() const { return mIncidence; } Akonadi::Item::Id IncidenceMonthItem::akonadiItemId() const { return mAkonadiItemId; } void IncidenceMonthItem::setNewDates(const KCalCore::Incidence::Ptr &incidence, int startOffset, int endOffset) { if (mIsTodo) { // For to-dos endOffset is ignored because it will always be == to startOffset because we only // support moving to-dos, not resizing them. There are no multi-day to-dos. // Lets just call it offset to reduce confusion. const int offset = startOffset; KCalCore::Todo::Ptr todo = incidence.staticCast(); QDateTime due = todo->dtDue(); QDateTime start = todo->dtStart(); if (due.isValid()) { // Due has priority over start. // We will only move the due date, unlike events where we move both. due = due.addDays(offset); todo->setDtDue(due); if (start.isValid() && start > due) { // Start can't be bigger than due. todo->setDtStart(due); } } else if (start.isValid()) { // So we're displaying a to-do that doesn't have due date, only start... start = start.addDays(offset); todo->setDtStart(start); } else { // This never happens qCWarning(CALENDARVIEW_LOG) << "Move what? uid:" << todo->uid() << "; summary=" << todo->summary(); } } else { incidence->setDtStart(incidence->dtStart().addDays(startOffset)); if (mIsEvent) { KCalCore::Event::Ptr event = incidence.staticCast(); event->setDtEnd(event->dtEnd().addDays(endOffset)); } } } //----------------------------------------------------------------- // HOLIDAYMONTHITEM HolidayMonthItem::HolidayMonthItem(MonthScene *monthScene, const QDate &date, const QString &name) : MonthItem(monthScene) , mDate(date) , mName(name) { } HolidayMonthItem::~HolidayMonthItem() { } bool HolidayMonthItem::greaterThanFallback(const MonthItem *other) const { const HolidayMonthItem *o = qobject_cast(other); if (o) { return MonthItem::greaterThanFallback(other); } // always put holidays on top return false; } void HolidayMonthItem::finalizeMove(const QDate &newStartDate) { Q_UNUSED(newStartDate); Q_ASSERT(false); } void HolidayMonthItem::finalizeResize(const QDate &newStartDate, const QDate &newEndDate) { Q_UNUSED(newStartDate); Q_UNUSED(newEndDate); Q_ASSERT(false); } QVector HolidayMonthItem::icons() const { QVector ret; ret << monthScene()->holidayPixmap(); return ret; } QColor HolidayMonthItem::bgColor() const { // FIXME: Currently, only this value is settable in the options. // There is a monthHolidaysBackgroundColor() option too. Maybe it would be // wise to merge those two. return monthScene()->monthView()->preferences()->agendaHolidaysBackgroundColor(); } QColor HolidayMonthItem::frameColor() const { return Qt::black; } diff --git a/src/month/monthitem.h b/src/month/monthitem.h index bb3645f..03d91ca 100644 --- a/src/month/monthitem.h +++ b/src/month/monthitem.h @@ -1,404 +1,404 @@ /* Copyright (c) 2008 Bruno Virlet This program is free software; you can redistribute it and/or modify it under the terms of the GNU General Public License as published by the Free Software Foundation; either version 2 of the License, or (at your option) any later version. This program is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for more details. You should have received a copy of the GNU General Public License along with this program; if not, write to the Free Software Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA. As a special exception, permission is given to link this program with any edition of Qt, and distribute the resulting executable, without including the source code for Qt in the source distribution. */ #ifndef EVENTVIEWS_MONTHITEM_H #define EVENTVIEWS_MONTHITEM_H #include "eventviews_export.h" #include #include #include #include #include namespace EventViews { class MonthGraphicsItem; class MonthScene; /** * A month item manages different MonthGraphicsItems. */ class EVENTVIEWS_EXPORT MonthItem : public QObject { Q_OBJECT public: explicit MonthItem(MonthScene *monthWidget); ~MonthItem() override; QWidget *parentWidget() const; /** Compares two events The month view displays a list of items. When loading (which occurs each time there is a change), the items are sorted : - smallest date - bigger span - floating - finally, time in the day */ static bool greaterThan(const MonthItem *e1, const MonthItem *e2); /** Compare this event with a second one, if the former function is not able to sort them. */ virtual bool greaterThanFallback(const MonthItem *other) const; /** The start date of the incidence, generally realStartDate. But it reflect changes, even during move. */ Q_REQUIRED_RESULT QDate startDate() const; /** The end date of the incidence, generally realEndDate. But it reflect changes, even during move. */ Q_REQUIRED_RESULT QDate endDate() const; /** The number of days this item spans. */ Q_REQUIRED_RESULT int daySpan() const; /** This is the real start date, usually the start date of the incidence. */ virtual QDate realStartDate() const = 0; /** This is the real end date, usually the end date of the incidence. */ virtual QDate realEndDate() const = 0; /** True if this item last all the day. */ virtual bool allDay() const = 0; /** Updates geometry of all MonthGraphicsItems. */ void updateGeometry(); /** Find the lowest possible position for this item. The position of an item in a cell is it's vertical position. This is used to avoid overlapping of items. An item keeps the same position in every cell it crosses. The position is measured from top to bottom. */ void updatePosition(); /** Returns true if this item is selected. */ Q_REQUIRED_RESULT bool selected() const { return mSelected; } /** Returns the position of the item ( > 0 ). */ Q_REQUIRED_RESULT int position() const { return mPosition; } /** Returns the associated month scene to this item. */ MonthScene *monthScene() const { return mMonthScene; } /** Begin a move. */ void beginMove(); /** End a move. */ void endMove(); /** Begin a resize. */ void beginResize(); /** End a resize. */ void endResize(); /** Called during move to move the item a bit, relative to the previous move step. */ void moveBy(int offsetFromPreviousDate); /** Called during resize to resize the item a bit, relative to the previous resize step. */ bool resizeBy(int offsetFromPreviousDate); /** Returns true if the item is being moved. */ Q_REQUIRED_RESULT bool isMoving() const { return mMoving; } /** Returns true if the item is being resized. */ Q_REQUIRED_RESULT bool isResizing() const { return mResizing; } /** Returns true if the item can be moved. */ virtual bool isMoveable() const = 0; /** Returns true if the item can be resized. */ virtual bool isResizable() const = 0; /** Deletes all MonthGraphicsItem this item handles. Clear the list. */ void deleteAll(); /** Update the monthgraphicsitems This basically deletes and rebuild all the MonthGraphicsItems but tries to do it wisely: - If there is a moving item, it won't be deleted because then the new item won't receive anymore the MouseMove events. - If there is an item on a line where the new state needs an item, it is used and not deleted. This will avoid flickers. */ void updateMonthGraphicsItems(); /** Sets the selection state of this item. */ void setSelected(bool selected) { mSelected = selected; } // METHODS NEEDED TO PAINT ITEMS /** Returns the text to draw in an item. @param end True if the text at the end of an item should be returned. */ virtual QString text(bool end) const = 0; /** Returns the text for the tooltip of the item */ virtual QString toolTipText(const QDate &date) const = 0; /** Returns the background color of the item. */ - virtual QColor bgColor() const = 0; + virtual Q_REQUIRED_RESULT QColor bgColor() const = 0; /** Returns the frame color of the item. */ - virtual QColor frameColor() const = 0; + virtual Q_REQUIRED_RESULT QColor frameColor() const = 0; /** Returns a list of pixmaps to draw next to the items. */ virtual QVector icons() const = 0; QList monthGraphicsItems() const; protected: /** Called after a move operation. */ virtual void finalizeMove(const QDate &newStartDate) = 0; /** Called after a resize operation. */ virtual void finalizeResize(const QDate &newStartDate, const QDate &newEndDate) = 0; private: /** Sets the value of all MonthGraphicsItem to @param z. */ void setZValue(qreal z); QList mMonthGraphicsItemList; MonthScene *mMonthScene = nullptr; bool mSelected; bool mMoving; // during move bool mResizing; // during resize QDate mOverrideStartDate; int mOverrideDaySpan; int mPosition; }; class EVENTVIEWS_EXPORT IncidenceMonthItem : public MonthItem { Q_OBJECT public: IncidenceMonthItem(MonthScene *monthScene, const Akonadi::ETMCalendar::Ptr &calendar, const Akonadi::Item &item, const KCalCore::Incidence::Ptr &incidence, const QDate &recurStartDate = QDate()); ~IncidenceMonthItem() override; KCalCore::Incidence::Ptr incidence() const; Akonadi::Item akonadiItem() const; Akonadi::Item::Id akonadiItemId() const; bool greaterThanFallback(const MonthItem *other) const override; QDate realStartDate() const override; QDate realEndDate() const override; bool allDay() const override; bool isMoveable() const override; bool isResizable() const override; QString text(bool end) const override; QString toolTipText(const QDate &date) const override; QColor bgColor() const override; QColor frameColor() const override; QVector icons() const override; protected: void finalizeMove(const QDate &newStartDate) override; virtual void finalizeResize(const QDate &newStartDate, const QDate &newEndDate) override; protected Q_SLOTS: /** Update the selected state of this item. If will be selected if incidence is the incidence managed by this item. Else it will be deselected. */ void updateSelection(const Akonadi::Item &incidence, const QDate &date); private: void updateDates(int startOffset, int endOffset); void setNewDates(const KCalCore::Incidence::Ptr &incidence, int startOffset, int endOffset); /** Returns the category color for this incidence. */ QColor catColor() const; Akonadi::ETMCalendar::Ptr mCalendar; KCalCore::Incidence::Ptr mIncidence; Akonadi::Item::Id mAkonadiItemId; int mRecurDayOffset; bool mIsEvent, mIsTodo, mIsJournal; }; class EVENTVIEWS_EXPORT HolidayMonthItem : public MonthItem { Q_OBJECT public: HolidayMonthItem(MonthScene *monthScene, const QDate &date, const QString &name); ~HolidayMonthItem() override; bool greaterThanFallback(const MonthItem *other) const override; QDate realStartDate() const override { return mDate; } QDate realEndDate() const override { return mDate; } bool allDay() const override { return true; } bool isMoveable() const override { return false; } bool isResizable() const override { return false; } QString text(bool end) const override { Q_UNUSED(end); return mName; } QString toolTipText(const QDate &) const override { return mName; } QColor bgColor() const override; QColor frameColor() const override; QVector icons() const override; protected: void finalizeMove(const QDate &newStartDate) override; virtual void finalizeResize(const QDate &newStartDate, const QDate &newEndDate) override; private: QDate mDate; QString mName; }; } #endif