diff --git a/src/agenda/agendaitem.cpp b/src/agenda/agendaitem.cpp index 9b03b91..1afca17 100644 --- a/src/agenda/agendaitem.cpp +++ b/src/agenda/agendaitem.cpp @@ -1,1374 +1,1344 @@ /* Copyright (c) 2000,2001,2003 Cornelius Schumacher Copyright (C) 2003-2004 Reinhold Kainhofer 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 "agendaitem.h" #include "eventview.h" #include "viewcalendar.h" #include "helper.h" #include "prefs.h" #include "prefs_base.h" // for enums #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include using namespace KCalCore; using namespace EventViews; //----------------------------------------------------------------------------- QPixmap *AgendaItem::alarmPxmp = nullptr; QPixmap *AgendaItem::recurPxmp = nullptr; QPixmap *AgendaItem::readonlyPxmp = nullptr; QPixmap *AgendaItem::replyPxmp = nullptr; QPixmap *AgendaItem::groupPxmp = nullptr; QPixmap *AgendaItem::groupPxmpTent = nullptr; QPixmap *AgendaItem::organizerPxmp = nullptr; QPixmap *AgendaItem::eventPxmp = nullptr; //----------------------------------------------------------------------------- AgendaItem::AgendaItem(EventView *eventView, const MultiViewCalendar::Ptr &calendar, const KCalCore::Incidence::Ptr &item, int itemPos, int itemCount, const QDateTime &qd, bool isSelected, QWidget *parent) : QWidget(parent) , mEventView(eventView) , mCalendar(calendar) , mIncidence(item) , mOccurrenceDateTime(qd) , mValid(true) , mCloned(false) , mSelected(isSelected) , mSpecialEvent(false) { if (!mIncidence) { mValid = false; return; } mIncidence = Incidence::Ptr(mIncidence->clone()); if (mIncidence->customProperty("KABC", "BIRTHDAY") == QLatin1String("YES") || mIncidence->customProperty("KABC", "ANNIVERSARY") == QLatin1String("YES")) { const int years = EventViews::yearDiff(mIncidence->dtStart().date(), qd.toLocalTime().date()); if (years > 0) { mIncidence->setReadOnly(false); mIncidence->setSummary(i18np("%2 (1 year)", "%2 (%1 years)", years, mIncidence->summary())); mIncidence->setReadOnly(true); mCloned = true; } } mLabelText = mIncidence->summary(); mIconAlarm = false; mIconRecur = false; mIconReadonly = false; mIconReply = false; mIconGroup = false; mIconGroupTent = false; mIconOrganizer = false; mMultiItemInfo = nullptr; mStartMoveInfo = nullptr; mItemPos = itemPos; mItemCount = itemCount; QPalette pal = palette(); pal.setColor(QPalette::Window, Qt::transparent); setPalette(pal); setCellXY(0, 0, 1); setCellXRight(0); setMouseTracking(true); mResourceColor = QColor(); updateIcons(); setAcceptDrops(true); } AgendaItem::~AgendaItem() { } void AgendaItem::updateIcons() { if (!mValid) { return; } mIconReadonly = mIncidence->isReadOnly(); mIconRecur = mIncidence->recurs() || mIncidence->hasRecurrenceId(); mIconAlarm = mIncidence->hasEnabledAlarms(); if (mIncidence->attendeeCount() > 1) { if (mEventView->kcalPreferences()->thatIsMe(mIncidence->organizer()->email())) { mIconReply = false; mIconGroup = false; mIconGroupTent = false; mIconOrganizer = true; } else { KCalCore::Attendee::Ptr me = mIncidence->attendeeByMails(mEventView->kcalPreferences()->allEmails()); if (me) { if (me->status() == KCalCore::Attendee::NeedsAction && me->RSVP()) { mIconReply = true; mIconGroup = false; mIconGroupTent = false; mIconOrganizer = false; } else if (me->status() == KCalCore::Attendee::Tentative) { mIconReply = false; mIconGroup = false; mIconGroupTent = true; mIconOrganizer = false; } else { mIconReply = false; mIconGroup = true; mIconGroupTent = false; mIconOrganizer = false; } } else { mIconReply = false; mIconGroup = true; mIconGroupTent = false; mIconOrganizer = false; } } } update(); } void AgendaItem::select(bool selected) { if (mSelected != selected) { mSelected = selected; update(); } } bool AgendaItem::dissociateFromMultiItem() { if (!isMultiItem()) { return false; } AgendaItem::QPtr firstItem = firstMultiItem(); if (firstItem == this) { firstItem = nextMultiItem(); } AgendaItem::QPtr lastItem = lastMultiItem(); if (lastItem == this) { lastItem = prevMultiItem(); } AgendaItem::QPtr prevItem = prevMultiItem(); AgendaItem::QPtr nextItem = nextMultiItem(); if (prevItem) { prevItem->setMultiItem(firstItem, prevItem->prevMultiItem(), nextItem, lastItem); } if (nextItem) { nextItem->setMultiItem(firstItem, prevItem, nextItem->prevMultiItem(), lastItem); } delete mMultiItemInfo; mMultiItemInfo = nullptr; return true; } void AgendaItem::setIncidence(const KCalCore::Incidence::Ptr &incidence) { mValid = false; if (incidence) { mValid = true; mIncidence = incidence; mLabelText = mIncidence->summary(); updateIcons(); } } /* Return height of item in units of agenda cells */ int AgendaItem::cellHeight() const { return mCellYBottom - mCellYTop + 1; } /* Return height of item in units of agenda cells */ int AgendaItem::cellWidth() const { return mCellXRight - mCellXLeft + 1; } void AgendaItem::setOccurrenceDateTime(const QDateTime &qd) { mOccurrenceDateTime = qd; } QDate AgendaItem::occurrenceDate() const { return mOccurrenceDateTime.toLocalTime().date(); } void AgendaItem::setCellXY(int X, int YTop, int YBottom) { mCellXLeft = X; mCellYTop = YTop; mCellYBottom = YBottom; } void AgendaItem::setCellXRight(int XRight) { mCellXRight = XRight; } void AgendaItem::setCellX(int XLeft, int XRight) { mCellXLeft = XLeft; mCellXRight = XRight; } void AgendaItem::setCellY(int YTop, int YBottom) { mCellYTop = YTop; mCellYBottom = YBottom; } void AgendaItem::setMultiItem(const AgendaItem::QPtr &first, const AgendaItem::QPtr &prev, const AgendaItem::QPtr &next, const AgendaItem::QPtr &last) { if (!mMultiItemInfo) { mMultiItemInfo = new MultiItemInfo; } mMultiItemInfo->mFirstMultiItem = first; mMultiItemInfo->mPrevMultiItem = prev; mMultiItemInfo->mNextMultiItem = next; mMultiItemInfo->mLastMultiItem = last; } bool AgendaItem::isMultiItem() const { return mMultiItemInfo; } AgendaItem::QPtr AgendaItem::prependMoveItem(const AgendaItem::QPtr &e) { if (!e) { return nullptr; } AgendaItem::QPtr first = nullptr, last = nullptr; if (isMultiItem()) { first = mMultiItemInfo->mFirstMultiItem; last = mMultiItemInfo->mLastMultiItem; } if (!first) { first = this; } if (!last) { last = this; } e->setMultiItem(nullptr, nullptr, first, last); first->setMultiItem(e, e, first->nextMultiItem(), first->lastMultiItem()); AgendaItem::QPtr tmp = first->nextMultiItem(); while (tmp) { tmp->setMultiItem(e, tmp->prevMultiItem(), tmp->nextMultiItem(), tmp->lastMultiItem()); tmp = tmp->nextMultiItem(); } if (mStartMoveInfo && !e->moveInfo()) { e->mStartMoveInfo = new MultiItemInfo(*mStartMoveInfo); // e->moveInfo()->mFirstMultiItem = moveInfo()->mFirstMultiItem; // e->moveInfo()->mLastMultiItem = moveInfo()->mLastMultiItem; e->moveInfo()->mPrevMultiItem = nullptr; e->moveInfo()->mNextMultiItem = first; } if (first && first->moveInfo()) { first->moveInfo()->mPrevMultiItem = e; } return e; } AgendaItem::QPtr AgendaItem::appendMoveItem(const AgendaItem::QPtr &e) { if (!e) { return nullptr; } AgendaItem::QPtr first = nullptr, last = nullptr; if (isMultiItem()) { first = mMultiItemInfo->mFirstMultiItem; last = mMultiItemInfo->mLastMultiItem; } if (!first) { first = this; } if (!last) { last = this; } e->setMultiItem(first, last, nullptr, nullptr); AgendaItem::QPtr tmp = first; while (tmp) { tmp->setMultiItem(tmp->firstMultiItem(), tmp->prevMultiItem(), tmp->nextMultiItem(), e); tmp = tmp->nextMultiItem(); } last->setMultiItem(last->firstMultiItem(), last->prevMultiItem(), e, e); if (mStartMoveInfo && !e->moveInfo()) { e->mStartMoveInfo = new MultiItemInfo(*mStartMoveInfo); // e->moveInfo()->mFirstMultiItem = moveInfo()->mFirstMultiItem; // e->moveInfo()->mLastMultiItem = moveInfo()->mLastMultiItem; e->moveInfo()->mPrevMultiItem = last; e->moveInfo()->mNextMultiItem = nullptr; } if (last && last->moveInfo()) { last->moveInfo()->mNextMultiItem = e; } return e; } AgendaItem::QPtr AgendaItem::removeMoveItem(const AgendaItem::QPtr &e) { if (isMultiItem()) { AgendaItem::QPtr first = mMultiItemInfo->mFirstMultiItem; AgendaItem::QPtr next, prev; AgendaItem::QPtr last = mMultiItemInfo->mLastMultiItem; if (!first) { first = this; } if (!last) { last = this; } if (first == e) { first = first->nextMultiItem(); first->setMultiItem(nullptr, nullptr, first->nextMultiItem(), first->lastMultiItem()); } if (last == e) { last = last->prevMultiItem(); last->setMultiItem(last->firstMultiItem(), last->prevMultiItem(), nullptr, nullptr); } AgendaItem::QPtr tmp = first; if (first == last) { delete mMultiItemInfo; tmp = nullptr; mMultiItemInfo = nullptr; } while (tmp) { next = tmp->nextMultiItem(); prev = tmp->prevMultiItem(); if (e == next) { next = next->nextMultiItem(); } if (e == prev) { prev = prev->prevMultiItem(); } tmp->setMultiItem((tmp == first) ? nullptr : first, (tmp == prev) ? nullptr : prev, (tmp == next) ? nullptr : next, (tmp == last) ? nullptr : last); tmp = tmp->nextMultiItem(); } } return e; } void AgendaItem::startMove() { AgendaItem::QPtr first = this; if (isMultiItem() && mMultiItemInfo->mFirstMultiItem) { first = mMultiItemInfo->mFirstMultiItem; } first->startMovePrivate(); } void AgendaItem::startMovePrivate() { mStartMoveInfo = new MultiItemInfo; mStartMoveInfo->mStartCellXLeft = mCellXLeft; mStartMoveInfo->mStartCellXRight = mCellXRight; mStartMoveInfo->mStartCellYTop = mCellYTop; mStartMoveInfo->mStartCellYBottom = mCellYBottom; if (mMultiItemInfo) { mStartMoveInfo->mFirstMultiItem = mMultiItemInfo->mFirstMultiItem; mStartMoveInfo->mLastMultiItem = mMultiItemInfo->mLastMultiItem; mStartMoveInfo->mPrevMultiItem = mMultiItemInfo->mPrevMultiItem; mStartMoveInfo->mNextMultiItem = mMultiItemInfo->mNextMultiItem; } else { mStartMoveInfo->mFirstMultiItem = nullptr; mStartMoveInfo->mLastMultiItem = nullptr; mStartMoveInfo->mPrevMultiItem = nullptr; mStartMoveInfo->mNextMultiItem = nullptr; } if (isMultiItem() && mMultiItemInfo->mNextMultiItem) { mMultiItemInfo->mNextMultiItem->startMovePrivate(); } } void AgendaItem::resetMove() { if (mStartMoveInfo) { if (mStartMoveInfo->mFirstMultiItem) { mStartMoveInfo->mFirstMultiItem->resetMovePrivate(); } else { resetMovePrivate(); } } } void AgendaItem::resetMovePrivate() { if (mStartMoveInfo) { mCellXLeft = mStartMoveInfo->mStartCellXLeft; mCellXRight = mStartMoveInfo->mStartCellXRight; mCellYTop = mStartMoveInfo->mStartCellYTop; mCellYBottom = mStartMoveInfo->mStartCellYBottom; // if we don't have mMultiItemInfo, the item didn't span two days before, // and wasn't moved over midnight, either, so we don't have to reset // anything. Otherwise, restore from mMoveItemInfo if (mMultiItemInfo) { // It was already a multi-day info mMultiItemInfo->mFirstMultiItem = mStartMoveInfo->mFirstMultiItem; mMultiItemInfo->mPrevMultiItem = mStartMoveInfo->mPrevMultiItem; mMultiItemInfo->mNextMultiItem = mStartMoveInfo->mNextMultiItem; mMultiItemInfo->mLastMultiItem = mStartMoveInfo->mLastMultiItem; if (!mStartMoveInfo->mFirstMultiItem) { // This was the first multi-item when the move started, delete all previous AgendaItem::QPtr toDel = mStartMoveInfo->mPrevMultiItem; AgendaItem::QPtr nowDel = nullptr; while (toDel) { nowDel = toDel; if (nowDel->moveInfo()) { toDel = nowDel->moveInfo()->mPrevMultiItem; } Q_EMIT removeAgendaItem(nowDel); } mMultiItemInfo->mFirstMultiItem = nullptr; mMultiItemInfo->mPrevMultiItem = nullptr; } if (!mStartMoveInfo->mLastMultiItem) { // This was the last multi-item when the move started, delete all next AgendaItem::QPtr toDel = mStartMoveInfo->mNextMultiItem; AgendaItem::QPtr nowDel = nullptr; while (toDel) { nowDel = toDel; if (nowDel->moveInfo()) { toDel = nowDel->moveInfo()->mNextMultiItem; } Q_EMIT removeAgendaItem(nowDel); } mMultiItemInfo->mLastMultiItem = nullptr; mMultiItemInfo->mNextMultiItem = nullptr; } if (mStartMoveInfo->mFirstMultiItem == nullptr && mStartMoveInfo->mLastMultiItem == nullptr) { // it was a single-day event before we started the move. delete mMultiItemInfo; mMultiItemInfo = nullptr; } } delete mStartMoveInfo; mStartMoveInfo = nullptr; } Q_EMIT showAgendaItem(this); if (nextMultiItem()) { nextMultiItem()->resetMovePrivate(); } } void AgendaItem::endMove() { AgendaItem::QPtr first = firstMultiItem(); if (!first) { first = this; } first->endMovePrivate(); } void AgendaItem::endMovePrivate() { if (mStartMoveInfo) { // if first, delete all previous if (!firstMultiItem() || firstMultiItem() == this) { AgendaItem::QPtr toDel = mStartMoveInfo->mPrevMultiItem; AgendaItem::QPtr nowDel = nullptr; while (toDel) { nowDel = toDel; if (nowDel->moveInfo()) { toDel = nowDel->moveInfo()->mPrevMultiItem; } Q_EMIT removeAgendaItem(nowDel); } } // if last, delete all next if (!lastMultiItem() || lastMultiItem() == this) { AgendaItem::QPtr toDel = mStartMoveInfo->mNextMultiItem; AgendaItem::QPtr nowDel = nullptr; while (toDel) { nowDel = toDel; if (nowDel->moveInfo()) { toDel = nowDel->moveInfo()->mNextMultiItem; } Q_EMIT removeAgendaItem(nowDel); } } // also delete the moving info delete mStartMoveInfo; mStartMoveInfo = nullptr; if (nextMultiItem()) { nextMultiItem()->endMovePrivate(); } } } void AgendaItem::moveRelative(int dx, int dy) { int newXLeft = cellXLeft() + dx; int newXRight = cellXRight() + dx; int newYTop = cellYTop() + dy; int newYBottom = cellYBottom() + dy; setCellXY(newXLeft, newYTop, newYBottom); setCellXRight(newXRight); } void AgendaItem::expandTop(int dy, const bool allowOverLimit) { int newYTop = cellYTop() + dy; int newYBottom = cellYBottom(); if (newYTop > newYBottom && !allowOverLimit) { newYTop = newYBottom; } setCellY(newYTop, newYBottom); } void AgendaItem::expandBottom(int dy) { int newYTop = cellYTop(); int newYBottom = cellYBottom() + dy; if (newYBottom < newYTop) { newYBottom = newYTop; } setCellY(newYTop, newYBottom); } void AgendaItem::expandLeft(int dx) { int newXLeft = cellXLeft() + dx; int newXRight = cellXRight(); if (newXLeft > newXRight) { newXLeft = newXRight; } setCellX(newXLeft, newXRight); } void AgendaItem::expandRight(int dx) { int newXLeft = cellXLeft(); int newXRight = cellXRight() + dx; if (newXRight < newXLeft) { newXRight = newXLeft; } setCellX(newXLeft, newXRight); } void AgendaItem::dragEnterEvent(QDragEnterEvent *e) { const QMimeData *md = e->mimeData(); if (KCalUtils::ICalDrag::canDecode(md) || KCalUtils::VCalDrag::canDecode(md)) { // TODO: Allow dragging events/todos onto other events to create a relation e->ignore(); return; } if (KContacts::VCardDrag::canDecode(md) || md->hasText()) { e->accept(); } else { e->ignore(); } } void AgendaItem::addAttendee(const QString &newAttendee) { if (!mValid) { return; } QString name, email; KEmailAddress::extractEmailAddressAndName(newAttendee, email, name); if (!(name.isEmpty() && email.isEmpty())) { mIncidence->addAttendee(KCalCore::Attendee::Ptr(new KCalCore::Attendee(name, email))); KMessageBox::information( this, i18n("Attendee \"%1\" added to the calendar item \"%2\"", KEmailAddress::normalizedAddress(name, email, QString()), text()), i18n("Attendee added"), QStringLiteral("AttendeeDroppedAdded")); } } void AgendaItem::dropEvent(QDropEvent *e) { // TODO: Organize this better: First check for attachment // (not only file, also any other url!), then if it's a vcard, // otherwise check for attendees, then if the data is binary, // add a binary attachment. if (!mValid) { return; } const QMimeData *md = e->mimeData(); bool decoded = md->hasText(); QString text = md->text(); if (decoded && text.startsWith(QLatin1String("file:"))) { mIncidence->addAttachment(KCalCore::Attachment::Ptr(new KCalCore::Attachment(text))); return; } KContacts::Addressee::List list; if (KContacts::VCardDrag::fromMimeData(md, list)) { for (const KContacts::Addressee &addressee : qAsConst(list)) { QString em(addressee.fullEmail()); if (em.isEmpty()) { em = addressee.realName(); } addAttendee(em); } } } QList &AgendaItem::conflictItems() { return mConflictItems; } void AgendaItem::setConflictItems(const QList &ci) { mConflictItems = ci; for (QList::iterator it = mConflictItems.begin(), end(mConflictItems.end()); it != end; ++it) { (*it)->addConflictItem(this); } } void AgendaItem::addConflictItem(const AgendaItem::QPtr &ci) { if (!mConflictItems.contains(ci)) { mConflictItems.append(ci); } } QString AgendaItem::label() const { return mLabelText; } bool AgendaItem::overlaps(CellItem *o) const { AgendaItem::QPtr other = static_cast(o); if (cellXLeft() <= other->cellXRight() && cellXRight() >= other->cellXLeft()) { if ((cellYTop() <= other->cellYBottom()) && (cellYBottom() >= other->cellYTop())) { return true; } } return false; } static void conditionalPaint(QPainter *p, bool condition, int &x, int y, int ft, const QPixmap &pxmp) { if (condition) { p->drawPixmap(x, y, pxmp); x += pxmp.width() + ft; } } void AgendaItem::paintIcon(QPainter *p, int &x, int y, int ft) { QString iconName; if (mIncidence->customProperty("KABC", "ANNIVERSARY") == QLatin1String("YES")) { mSpecialEvent = true; iconName = QStringLiteral("view-calendar-wedding-anniversary"); } else if (mIncidence->customProperty("KABC", "BIRTHDAY") == QLatin1String("YES")) { mSpecialEvent = true; // We don't draw icon. The icon is drawn already, because it's the Akonadi::Collection's icon } conditionalPaint(p, !iconName.isEmpty(), x, y, ft, cachedSmallIcon(iconName)); } void AgendaItem::paintIcons(QPainter *p, int &x, int y, int ft) { if (!mEventView->preferences()->enableAgendaItemIcons()) { return; } paintIcon(p, x, y, ft); QSet icons = mEventView->preferences()->agendaViewIcons(); if (icons.contains(EventViews::EventView::CalendarCustomIcon)) { const QString iconName = mCalendar->iconForIncidence(mIncidence); if (!iconName.isEmpty() && iconName != QLatin1String("view-calendar") && iconName != QLatin1String("office-calendar")) { conditionalPaint(p, true, x, y, ft, QIcon::fromTheme(iconName).pixmap(16, 16)); } } const bool isTodo = mIncidence && mIncidence->type() == Incidence::TypeTodo; if (isTodo && icons.contains(EventViews::EventView::TaskIcon)) { const QString iconName = mIncidence->iconName(mOccurrenceDateTime.toLocalTime()); conditionalPaint(p, !mSpecialEvent, x, y, ft, QIcon::fromTheme(iconName).pixmap(16, 16)); } if (icons.contains(EventView::RecurringIcon)) { conditionalPaint(p, mIconRecur && !mSpecialEvent, x, y, ft, *recurPxmp); } if (icons.contains(EventView::ReminderIcon)) { conditionalPaint(p, mIconAlarm && !mSpecialEvent, x, y, ft, *alarmPxmp); } if (icons.contains(EventView::ReadOnlyIcon)) { conditionalPaint(p, mIconReadonly && !mSpecialEvent, x, y, ft, *readonlyPxmp); } if (icons.contains(EventView::ReplyIcon)) { conditionalPaint(p, mIconReply, x, y, ft, *replyPxmp); } if (icons.contains(EventView::AttendingIcon)) { conditionalPaint(p, mIconGroup, x, y, ft, *groupPxmp); } if (icons.contains(EventView::TentativeIcon)) { conditionalPaint(p, mIconGroupTent, x, y, ft, *groupPxmpTent); } if (icons.contains(EventView::OrganizerIcon)) { conditionalPaint(p, mIconOrganizer, x, y, ft, *organizerPxmp); } } void AgendaItem::paintEvent(QPaintEvent *ev) { if (!mValid) { return; } QRect visRect = visibleRegion().boundingRect(); // when scrolling horizontally in the side-by-side view, the repainted area is clipped // to the newly visible area, which is a problem since the content changes when visRect // changes, so repaint the full item in that case if (ev->rect() != visRect && visRect.isValid() && ev->rect().isValid()) { update(visRect); return; } QPainter p(this); p.setRenderHint(QPainter::Antialiasing); const int fmargin = 0; // frame margin const int ft = 1; // frame thickness for layout, see drawRoundedRect(), // keep multiple of 2 const int margin = 5 + ft + fmargin; // frame + space between frame and content // General idea is to always show the icons (even in the all-day events). // This creates a consistent feeling for the user when the view mode // changes and therefore the available width changes. // Also look at #17984 if (!alarmPxmp) { alarmPxmp = new QPixmap(QIcon::fromTheme(QStringLiteral("task-reminder")).pixmap(16, 16)); recurPxmp = new QPixmap(QIcon::fromTheme(QStringLiteral("appointment-recurring")).pixmap(16, 16)); readonlyPxmp = new QPixmap(QIcon::fromTheme(QStringLiteral("object-locked")).pixmap(16, 16)); replyPxmp = new QPixmap(QIcon::fromTheme(QStringLiteral("mail-reply-sender")).pixmap(16, 16)); groupPxmp = new QPixmap(QIcon::fromTheme(QStringLiteral("meeting-attending")).pixmap(16, 16)); groupPxmpTent = new QPixmap(QIcon::fromTheme(QStringLiteral( "meeting-attending-tentative")).pixmap(16, 16)); organizerPxmp = new QPixmap(QIcon::fromTheme(QStringLiteral("meeting-organizer")).pixmap(16, 16)); } - QColor bgColor; + const auto categoryColor = getCategoryColor(); + const auto resourceColor = mResourceColor.isValid() ? mResourceColor : categoryColor; + const auto frameColor = getFrameColor(resourceColor, categoryColor); + const auto bgBaseColor = getBackgroundColor(resourceColor, categoryColor); + const auto bgColor = mSelected ? bgBaseColor.lighter(EventView::BRIGHTNESS_FACTOR) : bgBaseColor; + const auto textColor = EventViews::getTextColor(bgColor); - if (CalendarSupport::hasTodo(mIncidence) - && !mEventView->preferences()->todosUseCategoryColors()) { - Todo::Ptr todo = CalendarSupport::todo(mIncidence); - Q_ASSERT(todo); - const QDate dueDate = todo->dtDue().toLocalTime().date(); - const QDate today = QDate::currentDate(); - const QDate occurrenceDate = this->occurrenceDate(); - if (todo->isOverdue() && today >= occurrenceDate) { - bgColor = mEventView->preferences()->todoOverdueColor(); - } else if (dueDate == today && dueDate == occurrenceDate) { - bgColor = mEventView->preferences()->todoDueTodayColor(); - } - } - - QColor categoryColor; - const QStringList categories = mIncidence->categories(); - QString cat; - if (!categories.isEmpty()) { - cat = categories.first(); - } - - categoryColor = cat.isEmpty() ? CalendarSupport::KCalPrefs::instance()->unsetCategoryColor() - : CalendarSupport::KCalPrefs::instance()->categoryColor(cat); - - QColor resourceColor = mResourceColor; - if (!resourceColor.isValid()) { - resourceColor = categoryColor; - } - - QColor frameColor; - // TODO PrefsBase enums should probably be redefined in Prefs - if (mEventView->preferences()->agendaViewColors() == PrefsBase::ResourceOnly - || mEventView->preferences()->agendaViewColors() - == PrefsBase::CategoryInsideResourceOutside) { - frameColor = bgColor.isValid() ? bgColor : resourceColor; - } else { - frameColor = bgColor.isValid() ? bgColor : categoryColor; - } - - if (!bgColor.isValid()) { - if (mEventView->preferences()->agendaViewColors() == PrefsBase::ResourceOnly - || mEventView->preferences()->agendaViewColors() - == PrefsBase::ResourceInsideCategoryOutside) { - bgColor = resourceColor; - } else { - bgColor = categoryColor; - } - } - - if (cat.isEmpty() - && mEventView->preferences()->agendaViewColors() - == PrefsBase::ResourceInsideCategoryOutside) { - frameColor = bgColor; - } - - if (cat.isEmpty() - && mEventView->preferences()->agendaViewColors() - == PrefsBase::CategoryInsideResourceOutside) { - bgColor = frameColor; - } - - frameColor = EventView::itemFrameColor(frameColor, mSelected); - - if (!CalendarSupport::KCalPrefs::instance()->hasCategoryColor(cat)) { - categoryColor = resourceColor; - } - - if (!bgColor.isValid()) { - bgColor = categoryColor; - } - - if (mSelected) { - bgColor = bgColor.lighter(EventView::BRIGHTNESS_FACTOR); - } - - const QColor textColor = EventViews::getTextColor(bgColor); p.setPen(textColor); p.setFont(mEventView->preferences()->agendaViewFont()); QFontMetrics fm = p.fontMetrics(); const int singleLineHeight = fm.boundingRect(mLabelText).height(); const bool roundTop = !prevMultiItem(); const bool roundBottom = !nextMultiItem(); drawRoundedRect(&p, QRect(fmargin, fmargin, width() - fmargin * 2, height() - fmargin * 2), mSelected, bgColor, true, ft, roundTop, roundBottom); // calculate the height of the full version (case 4) to test whether it is // possible QString shortH; QString longH; if (!isMultiItem()) { shortH = QLocale().toString(mIncidence->dateTime( KCalCore::Incidence::RoleDisplayStart).toLocalTime().time(), QLocale::ShortFormat); if (CalendarSupport::hasEvent(mIncidence)) { longH = i18n("%1 - %2", shortH, QLocale().toString(mIncidence->dateTime(KCalCore::Incidence::RoleEnd). toLocalTime().time(), QLocale::ShortFormat)); } else { longH = shortH; } } else if (!mMultiItemInfo->mFirstMultiItem) { shortH = QLocale().toString(mIncidence->dtStart().toLocalTime().time(), QLocale::ShortFormat); longH = shortH; } else { shortH = QLocale().toString(mIncidence->dateTime( KCalCore::Incidence::RoleEnd).toLocalTime().time(), QLocale::ShortFormat); longH = i18n("- %1", shortH); } KWordWrap ww = KWordWrap::formatText( fm, QRect(0, 0, width() - (2 * margin), -1), 0, mLabelText); int th = ww.boundingRect().height(); int hlHeight = qMax(fm.boundingRect(longH).height(), qMax(alarmPxmp->height(), qMax(recurPxmp->height(), qMax(readonlyPxmp->height(), qMax(replyPxmp->height(), qMax(groupPxmp->height(), organizerPxmp->height())))))); const bool completelyRenderable = th < (height() - 2 * ft - 2 - hlHeight); // case 1: do not draw text when not even a single line fits // Don't do this any more, always try to print out the text. // Even if it's just a few pixel, one can still guess the whole // text from just four pixels' height! if ( //( singleLineHeight > height() - 4 ) || (width() < 16)) { int x = qRound((width() - 16) / 2.0); paintIcon(&p, x /*by-ref*/, margin, ft); return; } // case 2: draw a single line when no more space if ((2 * singleLineHeight) > (height() - 2 * margin)) { int x = margin, txtWidth; if (mIncidence->allDay()) { x += visRect.left(); const int y = qRound((height() - 16) / 2.0); paintIcons(&p, x, y, ft); txtWidth = visRect.right() - margin - x; } else { const int y = qRound((height() - 16) / 2.0); paintIcons(&p, x, y, ft); txtWidth = width() - margin - x; } const int y = ((height() - singleLineHeight) / 2) + fm.ascent(); KWordWrap::drawFadeoutText(&p, x, y, txtWidth, mLabelText); return; } // case 3: enough for 2-5 lines, but not for the header. // Also used for the middle days in multi-events if (((!completelyRenderable) && ((height() - (2 * margin)) <= (5 * singleLineHeight))) || (isMultiItem() && mMultiItemInfo->mNextMultiItem && mMultiItemInfo->mFirstMultiItem)) { int x = margin, txtWidth; if (mIncidence->allDay()) { x += visRect.left(); paintIcons(&p, x, margin, ft); txtWidth = visRect.right() - margin - x; } else { paintIcons(&p, x, margin, ft); txtWidth = width() - margin - x; } ww = KWordWrap::formatText( fm, QRect(0, 0, txtWidth, (height() - (2 * margin))), 0, mLabelText); ww.drawText(&p, x, margin, Qt::AlignHCenter | KWordWrap::FadeOut); return; } // case 4: paint everything, with header: // consists of (vertically) ft + headline&icons + ft + text + margin int y = 2 * ft + hlHeight; if (completelyRenderable) { y += (height() - (2 * ft) - margin - hlHeight - th) / 2; } int x = margin, txtWidth, hTxtWidth, eventX; if (mIncidence->allDay()) { shortH.clear(); longH.clear(); if (const KCalCore::Event::Ptr event = CalendarSupport::event(mIncidence)) { if (event->isMultiDay(QTimeZone::systemTimeZone())) { // multi-day, all-day event shortH = i18n("%1 - %2", QLocale().toString(mIncidence->dtStart().toLocalTime().date()), QLocale().toString(mIncidence->dateTime(KCalCore::Incidence::RoleEnd). toLocalTime().date())); longH = shortH; // paint headline drawRoundedRect( &p, QRect(fmargin, fmargin, width() - fmargin * 2, -fmargin * 2 + margin + hlHeight), mSelected, frameColor, false, ft, roundTop, false); } else { // single-day, all-day event // paint headline drawRoundedRect( &p, QRect(fmargin, fmargin, width() - fmargin * 2, -fmargin * 2 + margin + hlHeight), mSelected, frameColor, false, ft, roundTop, false); } } else { // to-do // paint headline drawRoundedRect( &p, QRect(fmargin, fmargin, width() - fmargin * 2, -fmargin * 2 + margin + hlHeight), mSelected, frameColor, false, ft, roundTop, false); } x += visRect.left(); eventX = x; txtWidth = visRect.right() - margin - x; paintIcons(&p, x, margin / 2, ft); hTxtWidth = visRect.right() - margin - x; } else { // paint headline drawRoundedRect( &p, QRect(fmargin, fmargin, width() - fmargin * 2, -fmargin * 2 + margin + hlHeight), mSelected, frameColor, false, ft, roundTop, false); txtWidth = width() - margin - x; eventX = x; paintIcons(&p, x, margin / 2, ft); hTxtWidth = width() - margin - x; } QString headline; int hw = fm.boundingRect(longH).width(); if (hw > hTxtWidth) { headline = shortH; hw = fm.boundingRect(shortH).width(); if (hw < txtWidth) { x += (hTxtWidth - hw) / 2; } } else { headline = longH; x += (hTxtWidth - hw) / 2; } p.setBackground(QBrush(frameColor)); p.setPen(EventViews::getTextColor(frameColor)); KWordWrap::drawFadeoutText(&p, x, (margin + hlHeight + fm.ascent()) / 2 - 2, hTxtWidth, headline); // draw event text ww = KWordWrap::formatText( fm, QRect(0, 0, txtWidth, height() - margin - y), 0, mLabelText); p.setBackground(QBrush(bgColor)); p.setPen(textColor); QString ws = ww.wrappedString(); if (ws.leftRef(ws.length() - 1).indexOf(QLatin1Char('\n')) >= 0) { ww.drawText(&p, eventX, y, Qt::AlignLeft | KWordWrap::FadeOut); } else { ww.drawText(&p, eventX + (txtWidth - ww.boundingRect().width() - 2 * margin) / 2, y, Qt::AlignHCenter | KWordWrap::FadeOut); } } void AgendaItem::drawRoundedRect(QPainter *p, const QRect &rect, bool selected, const QColor &bgColor, bool frame, int ft, bool roundTop, bool roundBottom) { Q_UNUSED(ft); if (!mValid) { return; } QRect r = rect; r.adjust(0, 0, 1, 1); p->save(); QPainterPath path; bool shrinkWidth = r.width() < 16; bool shrinkHeight = r.height() < 16; qreal rnd = 2.1; int sw = shrinkWidth ? 10 : 11; int sh = shrinkHeight ? 10 : 11; QRectF tr(r.x() + r.width() - sw - rnd, r.y() + rnd, sw, sh); QRectF tl(r.x() + rnd, r.y() + rnd, sw, sh); QRectF bl(r.x() + rnd, r.y() + r.height() - sh - 1 - rnd, sw, sh); QRectF br(r.x() + r.width() - sw - rnd, r.y() + r.height() - sh - 1 - rnd, sw, sh); if (roundTop) { path.moveTo(tr.topRight()); path.arcTo(tr, 0.0, 90.0); path.lineTo(tl.topRight()); path.arcTo(tl, 90.0, 90.0); } else { path.moveTo(tr.topRight()); path.lineTo(tl.topLeft()); } if (roundBottom) { path.lineTo(bl.topLeft()); path.arcTo(bl, 180.0, 90.0); path.lineTo(br.bottomLeft()); path.arcTo(br, 270.0, 90.0); } else { path.lineTo(bl.bottomLeft()); path.lineTo(br.bottomRight()); } path.closeSubpath(); // header if (!frame) { QLinearGradient gradient(QPointF(r.x(), r.y()), QPointF(r.x(), r.height())); if (selected) { QColor top = bgColor.darker(250); top.setAlpha(40); gradient.setColorAt(0, top); gradient.setColorAt(1, QColor(255, 255, 255, 30)); } else { gradient.setColorAt(0, QColor(255, 255, 255, 90)); gradient.setColorAt(1, QColor(0, 0, 0, 10)); } p->setBrush(bgColor); p->setPen(Qt::NoPen); p->drawPath(path); p->setBrush(gradient); p->setPen(Qt::NoPen); p->drawPath(path); QPixmap separator; QString key(QStringLiteral("ko_hsep")); if (!QPixmapCache::find(key, separator)) { separator = QPixmap(QStringLiteral(":/headerSeparator.png")); QPixmapCache::insert(key, separator); } p->fillRect(QRect(r.x() + 3, r.y() + r.height() - 2, r.x() + r.width() - 4, 2), QBrush(separator)); p->restore(); return; } QLinearGradient gradient(QPointF(r.x(), r.y()), QPointF(r.x(), r.height())); if (r.height() > 50) { if (mIncidence->allDay() && mIncidence->dtStart() == mIncidence->dateTime(KCalCore::Incidence::RoleEnd) && CalendarSupport::hasEvent(mIncidence)) { gradient.setColorAt(0, bgColor.lighter(130)); qreal t = 1.0 - (r.height() - 18.0) / r.height(); gradient.setColorAt(t, bgColor.lighter(115)); qreal b = (r.height() - 20.0) / r.height(); gradient.setColorAt(b, bgColor); } else { gradient.setColorAt(0, bgColor.lighter(115)); qreal b = (r.height() - 20.0) / r.height(); gradient.setColorAt(b, bgColor); } gradient.setColorAt(1, bgColor.darker(110)); } else { if (mIncidence->allDay() && mIncidence->dtStart() == mIncidence->dateTime(KCalCore::Incidence::RoleEnd) && !CalendarSupport::hasTodo(mIncidence)) { gradient.setColorAt(0, bgColor.lighter(130)); gradient.setColorAt(0.35, bgColor.lighter(115)); gradient.setColorAt(0.65, bgColor); } else { gradient.setColorAt(0, bgColor.lighter(115)); gradient.setColorAt(0.65, bgColor); } gradient.setColorAt(1, bgColor.darker(110)); } p->setBrush(gradient); p->setPen(Qt::NoPen); p->drawPath(path); p->setRenderHint(QPainter::Antialiasing, false); if (r.width() - 16 > 0) { QPixmap topLines; QString key(QStringLiteral("ko_t")); if (!QPixmapCache::find(key, topLines)) { topLines = QPixmap(QStringLiteral(":/topLines.png")); QPixmapCache::insert(key, topLines); } p->setBrushOrigin(r.x() + 8, r.y()); p->fillRect(QRect(r.x() + 8, r.y(), r.width() - 16, 5), QBrush(topLines)); QPixmap bottomLines; key = QStringLiteral("ko_b"); if (!QPixmapCache::find(key, bottomLines)) { bottomLines = QPixmap(QStringLiteral(":/bottomLines.png")); QPixmapCache::insert(key, bottomLines); } p->setBrushOrigin(r.x() + 8, r.y() + r.height() - 6); p->fillRect(QRect(r.x() + 8, r.y() + r.height() - 6, r.width() - 16, 6), QBrush(bottomLines)); } if (r.height() - 16 > 0) { QPixmap leftLines; QString key(QStringLiteral("ko_l")); if (!QPixmapCache::find(key, leftLines)) { leftLines = QPixmap(QStringLiteral(":/leftLines.png")); QPixmapCache::insert(key, leftLines); } p->setBrushOrigin(r.x(), r.y() + 8); p->fillRect(QRect(r.x(), r.y() + 8, 5, r.height() - 16), QBrush(leftLines)); QPixmap rightLines; key = QStringLiteral("ko_r"); if (!QPixmapCache::find(key, rightLines)) { rightLines = QPixmap(QStringLiteral(":/rightLines.png")); QPixmapCache::insert(key, rightLines); } p->setBrushOrigin(r.x() + r.width() - 5, r.y() + 8); p->fillRect(QRect(r.x() + r.width() - 5, r.y() + 8, 5, r.height() - 16), QBrush(rightLines)); } // don't overlap the edges int lw = shrinkWidth ? r.width() / 2 : 8; int rw = shrinkWidth ? r.width() - lw : 8; int th = shrinkHeight ? r.height() / 2 : 8; int bh = shrinkHeight ? r.height() - th : 8; // keep the bottom round for items which ending at 00:15 if (shrinkHeight && !roundTop && roundBottom && r.height() > 3) { bh += th - 3; th = 3; } QPixmap topLeft; QString key = roundTop ? QStringLiteral("ko_tl") : QStringLiteral("ko_rtl"); if (!QPixmapCache::find(key, topLeft)) { topLeft = roundTop ? QPixmap(QStringLiteral(":/roundTopLeft.png")) : QPixmap(QStringLiteral( ":/rectangularTopLeft.png")); QPixmapCache::insert(key, topLeft); } p->drawPixmap(r.x(), r.y(), topLeft, 0, 0, lw, th); QPixmap topRight; key = roundTop ? QStringLiteral("ko_tr") : QStringLiteral("ko_rtr"); if (!QPixmapCache::find(key, topRight)) { topRight = roundTop ? QPixmap(QStringLiteral(":/roundTopRight.png")) : QPixmap(QStringLiteral( ":/rectangularTopRight.png")); QPixmapCache::insert(key, topRight); } p->drawPixmap(r.x() + r.width() - rw, r.y(), topRight, 8 - rw, 0, rw, th); QPixmap bottomLeft; key = roundBottom ? QStringLiteral("ko_bl") : QStringLiteral("ko_rbl"); if (!QPixmapCache::find(key, bottomLeft)) { bottomLeft = roundBottom ? QPixmap(QStringLiteral(":/roundBottomLeft.png")) : QPixmap(QStringLiteral(":/rectangularBottomLeft.png")); QPixmapCache::insert(key, bottomLeft); } p->drawPixmap(r.x(), r.y() + r.height() - bh, bottomLeft, 0, 8 - bh, lw, bh); QPixmap bottomRight; key = roundBottom ? QStringLiteral("ko_br") : QStringLiteral("ko_rbr"); if (!QPixmapCache::find(key, bottomRight)) { bottomRight = roundBottom ? QPixmap(QStringLiteral(":/roundBottomRight.png")) : QPixmap(QStringLiteral(":/rectangularBottomRight.png")); QPixmapCache::insert(key, bottomRight); } p->drawPixmap(r.x() + r.width() - rw, r.y() + r.height() - bh, bottomRight, 8 - rw, 8 - bh, rw, 8); p->restore(); } +QColor AgendaItem::getCategoryColor() const +{ + const QStringList &categories = mIncidence->categories(); + if (categories.isEmpty() || !CalendarSupport::KCalPrefs::instance()->hasCategoryColor(categories.first())) { + const auto colorPreference = mEventView->preferences()->agendaViewColors(); + if (colorPreference == PrefsBase::CategoryOnly || !mResourceColor.isValid()) { + return CalendarSupport::KCalPrefs::instance()->unsetCategoryColor(); + } + return mResourceColor; + } + return CalendarSupport::KCalPrefs::instance()->categoryColor(categories.first()); +} + +QColor AgendaItem::getFrameColor(const QColor &resourceColor, const QColor &categoryColor) const +{ + const auto colorPreference = mEventView->preferences()->agendaViewColors(); + const bool frameDisplaysCategory = (colorPreference == PrefsBase::CategoryOnly + || colorPreference == PrefsBase::ResourceInsideCategoryOutside); + return frameDisplaysCategory ? categoryColor : resourceColor; +} + +QColor AgendaItem::getBackgroundColor(const QColor &resourceColor, const QColor &categoryColor) const +{ + if (CalendarSupport::hasTodo(mIncidence) && !mEventView->preferences()->todosUseCategoryColors()) { + Todo::Ptr todo = CalendarSupport::todo(mIncidence); + Q_ASSERT(todo); + const QDate dueDate = todo->dtDue().toLocalTime().date(); + const QDate today = QDate::currentDate(); + const QDate occurrenceDate = this->occurrenceDate(); + if (todo->isOverdue() && today >= occurrenceDate) { + return mEventView->preferences()->todoOverdueColor(); + } else if (dueDate == today && dueDate == occurrenceDate) { + return mEventView->preferences()->todoDueTodayColor(); + } + } + const auto colorPreference = mEventView->preferences()->agendaViewColors(); + const bool bgDisplaysCategory = (colorPreference == PrefsBase::CategoryOnly + || colorPreference == PrefsBase::CategoryInsideResourceOutside); + return bgDisplaysCategory ? categoryColor : resourceColor; +} + bool AgendaItem::eventFilter(QObject *obj, QEvent *event) { if (event->type() == QEvent::Paint) { return mValid; } else { // standard event processing return QObject::eventFilter(obj, event); } } bool AgendaItem::event(QEvent *event) { if (event->type() == QEvent::ToolTip) { if (!mEventView->preferences()->enableToolTips()) { return true; } else if (mValid) { QHelpEvent *helpEvent = static_cast(event); QToolTip::showText( helpEvent->globalPos(), KCalUtils::IncidenceFormatter::toolTipStr( mCalendar->displayName(mIncidence), mIncidence, occurrenceDate(), true), this); } } return QWidget::event(event); } diff --git a/src/agenda/agendaitem.h b/src/agenda/agendaitem.h index 06150b5..6e5bc9f 100644 --- a/src/agenda/agendaitem.h +++ b/src/agenda/agendaitem.h @@ -1,320 +1,324 @@ /* Copyright (c) 2000,2001,2003 Cornelius Schumacher Copyright (C) 2003-2004 Reinhold Kainhofer 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_AGENDAITEM_H #define EVENTVIEWS_AGENDAITEM_H #include "viewcalendar.h" #include #include #include #include #include #include namespace EventViews { class AgendaItem; class EventView; struct MultiItemInfo { int mStartCellXLeft, mStartCellXRight; int mStartCellYTop, mStartCellYBottom; QPointer mFirstMultiItem; QPointer mPrevMultiItem; QPointer mNextMultiItem; QPointer mLastMultiItem; }; /** @class AgendaItem @brief This class describes the widgets that represent the various calendar items in the agenda view The AgendaItem has to make sure that it receives all mouse events, which are to be used for dragging and resizing. That means it has to be installed as event filter for its children, if it has children, and it has to pass mouse events from the children to itself. See eventFilter(). Some comments on the movement of multi-day items: Basically, the agenda items are arranged in two implicit double-linked lists. The mMultiItemInfo works like before to describe the currently viewed multi-item. When moving, new events might need to be added to the beginning or the end of the multi-item sequence, or events might need to be hidden. I cannot just delete this items, since I have to restore/show them if the move is reset (i.e. if a drag started). So internally, I keep another doubly-linked list which is longer than the one defined by mMultiItemInfo, but includes the multi-item sequence, too. The mStartMoveInfo stores the first and last item of the multi-item sequence when the move started. The prev and next members of mStartMoveInfo are used for that longer sequence including all (shown and hidden) items. */ class AgendaItem : public QWidget, public CalendarSupport::CellItem { Q_OBJECT public: typedef QPointer QPtr; typedef QList List; AgendaItem(EventView *eventView, const MultiViewCalendar::Ptr &calendar, const KCalCore::Incidence::Ptr &incidence, int itemPos, int itemCount, const QDateTime &qd, bool isSelected, QWidget *parent); ~AgendaItem() override; Q_REQUIRED_RESULT int cellXLeft() const { return mCellXLeft; } Q_REQUIRED_RESULT int cellXRight() const { return mCellXRight; } Q_REQUIRED_RESULT int cellYTop() const { return mCellYTop; } Q_REQUIRED_RESULT int cellYBottom() const { return mCellYBottom; } Q_REQUIRED_RESULT int cellHeight() const; Q_REQUIRED_RESULT int cellWidth() const; Q_REQUIRED_RESULT int itemPos() const { return mItemPos; } Q_REQUIRED_RESULT int itemCount() const { return mItemCount; } void setCellXY(int X, int YTop, int YBottom); void setCellY(int YTop, int YBottom); void setCellX(int XLeft, int XRight); void setCellXRight(int XRight); /** Start movement */ void startMove(); /** Reset to original values */ void resetMove(); /** End the movement (i.e. clean up) */ void endMove(); void moveRelative(int dx, int dy); /** * Expands the item's top. * * @param dy delta y, number of units to be added to mCellYTop * @param allowOverLimit If false, the new mCellYTop can't be bigger than * mCellYBottom, instead, it gets mCellYBottom's value. * If true, @p dy is always added, regardless if mCellYTop * becomes bigger than mCellYBottom, this is useful when * moving items because it guarantees expandTop and the * following expandBottom call add the same value. */ void expandTop(int dy, const bool allowOverLimit = false); void expandBottom(int dy); void expandLeft(int dx); void expandRight(int dx); Q_REQUIRED_RESULT bool isMultiItem() const; AgendaItem::QPtr prevMoveItem() const { return (mStartMoveInfo) ? (mStartMoveInfo->mPrevMultiItem) : nullptr; } AgendaItem::QPtr nextMoveItem() const { return (mStartMoveInfo) ? (mStartMoveInfo->mNextMultiItem) : nullptr; } MultiItemInfo *moveInfo() const { return mStartMoveInfo; } void setMultiItem(const AgendaItem::QPtr &first, const AgendaItem::QPtr &prev, const AgendaItem::QPtr &next, const AgendaItem::QPtr &last); AgendaItem::QPtr prependMoveItem(const AgendaItem::QPtr &); AgendaItem::QPtr appendMoveItem(const AgendaItem::QPtr &); AgendaItem::QPtr removeMoveItem(const AgendaItem::QPtr &); AgendaItem::QPtr firstMultiItem() const { return (mMultiItemInfo) ? (mMultiItemInfo->mFirstMultiItem) : nullptr; } AgendaItem::QPtr prevMultiItem() const { return (mMultiItemInfo) ? (mMultiItemInfo->mPrevMultiItem) : nullptr; } AgendaItem::QPtr nextMultiItem() const { return (mMultiItemInfo) ? (mMultiItemInfo->mNextMultiItem) : nullptr; } AgendaItem::QPtr lastMultiItem() const { return (mMultiItemInfo) ? (mMultiItemInfo->mLastMultiItem) : nullptr; } Q_REQUIRED_RESULT bool dissociateFromMultiItem(); void setIncidence(const KCalCore::Incidence::Ptr &incidence); const KCalCore::Incidence::Ptr &incidence() const { return mIncidence; } Q_REQUIRED_RESULT QDateTime occurrenceDateTime() const { return mOccurrenceDateTime; } Q_REQUIRED_RESULT QDate occurrenceDate() const; // /** Update the date of this item's occurrence (not in the event) */ void setOccurrenceDateTime(const QDateTime &qd); void setText(const QString &text) { mLabelText = text; } Q_REQUIRED_RESULT QString text() const { return mLabelText; } QList &conflictItems(); void setConflictItems(const QList &); void addConflictItem(const AgendaItem::QPtr &ci); QString label() const override; /** Tells whether this item overlaps item @p o */ bool overlaps(CellItem *o) const override; void setResourceColor(const QColor &color) { mResourceColor = color; } Q_REQUIRED_RESULT QColor resourceColor() const { return mResourceColor; } Q_SIGNALS: void removeAgendaItem(const AgendaItem::QPtr &); void showAgendaItem(const AgendaItem::QPtr &); public Q_SLOTS: void updateIcons(); void select(bool selected = true); void addAttendee(const QString &); protected: bool eventFilter(QObject *obj, QEvent *event) override; bool event(QEvent *event) override; void dragEnterEvent(QDragEnterEvent *e) override; void dropEvent(QDropEvent *e) override; void paintEvent(QPaintEvent *e) override; /** private movement functions. startMove needs to be called of only one of * the multitems. it will then loop through the whole series using * startMovePrivate. Same for resetMove and endMove */ void startMovePrivate(); void resetMovePrivate(); void endMovePrivate(); // Variables to remember start position MultiItemInfo *mStartMoveInfo = nullptr; //Color of the resource QColor mResourceColor; private: void paintIcon(QPainter *p, int &x, int y, int ft); // paint all visible icons void paintIcons(QPainter *p, int &x, int y, int ft); void drawRoundedRect(QPainter *p, const QRect &rect, bool selected, const QColor &bgcolor, bool frame, int ft, bool roundTop, bool roundBottom); + Q_REQUIRED_RESULT QColor getCategoryColor() const; + Q_REQUIRED_RESULT QColor getFrameColor(const QColor &resourceColor, const QColor &categoryColor) const; + Q_REQUIRED_RESULT QColor getBackgroundColor(const QColor &resourceColor, const QColor &categoryColor) const; + int mCellXLeft, mCellXRight; int mCellYTop, mCellYBottom; EventView *mEventView = nullptr; MultiViewCalendar::Ptr mCalendar; KCalCore::Incidence::Ptr mIncidence; QDateTime mOccurrenceDateTime; bool mValid; bool mCloned; QString mLabelText; bool mSelected; bool mIconAlarm, mIconRecur, mIconReadonly; bool mIconReply, mIconGroup, mIconGroupTent; bool mIconOrganizer, mSpecialEvent; // For incidences that expand through more than 1 day // Will be 1 for single day incidences int mItemPos; int mItemCount; // Multi item pointers MultiItemInfo *mMultiItemInfo = nullptr; QList mConflictItems; static QPixmap *alarmPxmp; static QPixmap *recurPxmp; static QPixmap *readonlyPxmp; static QPixmap *replyPxmp; static QPixmap *groupPxmp; static QPixmap *groupPxmpTent; static QPixmap *organizerPxmp; static QPixmap *eventPxmp; static QPixmap *todoPxmp; static QPixmap *completedPxmp; }; } #endif