diff --git a/src/list/listview.cpp b/src/list/listview.cpp index 2b0a4d9..e566718 100644 --- a/src/list/listview.cpp +++ b/src/list/listview.cpp @@ -1,591 +1,591 @@ /* Copyright (c) 1999 Preston Brown Copyright (c) 2000,2001 Cornelius Schumacher Copyright (c) 2003-2004 Reinhold Kainhofer Copyright (c) 2010 Sérgio Martins Copyright (c) 2012-2013 Allen Winter 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. */ //TODO: put a reminder and/or recurs icon on the item? #include "listview.h" #include "helper.h" #include #include #include #include #include #include #include #include #include "calendarview_debug.h" #include #include #include #include using namespace EventViews; using namespace KCalCore; using namespace KCalUtils; enum { Summary_Column = 0, StartDateTime_Column, EndDateTime_Column, Categories_Column, Dummy_EOF_Column // Dummy enum value for iteration purposes only. Always keep at the end. }; static QString cleanSummary(const QString &summary, const QDateTime &next) { QString retStr = summary; retStr.replace(QLatin1Char('\n'), QLatin1Char(' ')); if (next.isValid()) { const QString dateStr = QLocale().toString(next.date(), QLocale::ShortFormat); retStr = i18nc("%1 is an item summary. %2 is the date when this item reoccurs", "%1 (next: %2)", retStr, dateStr); } return retStr; } class ListViewItem : public QTreeWidgetItem { public: ListViewItem(const Akonadi::Item &incidence, QTreeWidget *parent) : QTreeWidgetItem(parent), mTreeWidget(parent), mIncidence(incidence) { } bool operator<(const QTreeWidgetItem &other) const override; const QTreeWidget *mTreeWidget = nullptr; const Akonadi::Item mIncidence; QDateTime start; QDateTime end; }; bool ListViewItem::operator<(const QTreeWidgetItem &other) const { const ListViewItem *otheritem = static_cast(&other); switch (treeWidget()->sortColumn()) { case StartDateTime_Column: { return otheritem->start < start; } case EndDateTime_Column: { Incidence::Ptr thisInc = CalendarSupport::incidence(mIncidence); const auto thisEnd = thisInc->dateTime(Incidence::RoleEnd).toLocalZone().dateTime(); Incidence::Ptr otherInc = CalendarSupport::incidence(otheritem->mIncidence); const auto otherEnd = otherInc->dateTime(Incidence::RoleEnd).toLocalZone().dateTime(); return otherEnd < thisEnd; } default: return QTreeWidgetItem::operator < (other); } } class Q_DECL_HIDDEN ListView::Private { public: Private() { } ~Private() { } void addIncidences(const Akonadi::ETMCalendar::Ptr &calendar, const KCalCore::Incidence::List &incidenceList, const QDate &date); void addIncidence(const Akonadi::ETMCalendar::Ptr &calendar, const KCalCore::Incidence::Ptr &, const QDate &date); void addIncidence(const Akonadi::ETMCalendar::Ptr &calendar, const Akonadi::Item &, const QDate &date); ListViewItem *getItemForIncidence(const Akonadi::Item &); QTreeWidget *mTreeWidget = nullptr; ListViewItem *mActiveItem = nullptr; QHash mItems; QHash mDateList; QDate mStartDate; QDate mEndDate; DateList mSelectedDates; // if it's non interactive we disable context menu, and incidence editing bool mIsNonInteractive; class ListItemVisitor; }; /** This class provides the initialization of a ListViewItem for calendar components using the Incidence::Visitor. */ class ListView::Private::ListItemVisitor : public KCalCore::Visitor { public: ListItemVisitor(ListViewItem *item, QDate dt) : mItem(item), mStartDate(dt) { } ~ListItemVisitor() { } bool visit(const Event::Ptr &) override; bool visit(const Todo::Ptr &) override; bool visit(const Journal::Ptr &) override; bool visit(const FreeBusy::Ptr &) override { // to inhibit hidden virtual compile warning return true; } private: ListViewItem *mItem = nullptr; QDate mStartDate; }; bool ListView::Private::ListItemVisitor::visit(const Event::Ptr &e) { QIcon eventPxmp; if (e->customProperty("KABC", "ANNIVERSARY") == QLatin1String("YES")) { eventPxmp = QIcon::fromTheme(QStringLiteral("view-calendar-wedding-anniversary")); } else if (e->customProperty("KABC", "BIRTHDAY") == QLatin1String("YES")) { eventPxmp = QIcon::fromTheme(QStringLiteral("view-calendar-birthday")); } else { eventPxmp = QIcon::fromTheme(e->iconName()); } mItem->setIcon(Summary_Column, eventPxmp); QDateTime next; mItem->start = e->dtStart().toLocalZone().dateTime(); mItem->end = e->dtEnd().toLocalZone().dateTime(); if (e->recurs()) { const int duration = e->dtStart().secsTo(e->dtEnd()); - KDateTime kdt(mStartDate, QTime(0,0,0)); + QDateTime kdt(mStartDate, QTime(0,0,0)); kdt = kdt.addSecs(-1); - mItem->start = e->recurrence()->getNextDateTime(kdt).toLocalZone().dateTime(); + mItem->start = e->recurrence()->getNextDateTime(kdt).toLocalTime(); mItem->end = mItem->start.addSecs(duration); - next = e->recurrence()->getNextDateTime(KDateTime(mItem->start)).toLocalZone().dateTime(); + next = e->recurrence()->getNextDateTime(mItem->start).toLocalTime(); } mItem->setText(Summary_Column, cleanSummary(e->summary(), next)); if (e->allDay()) { mItem->setText(StartDateTime_Column, QLocale().toString(mItem->start.date(), QLocale::ShortFormat)); mItem->setText(EndDateTime_Column, QLocale().toString(mItem->end.date(), QLocale::ShortFormat)); } else { mItem->setText(StartDateTime_Column, QLocale().toString(mItem->start, QLocale::ShortFormat)); mItem->setText(EndDateTime_Column, QLocale().toString(mItem->end, QLocale::ShortFormat)); } mItem->setText(Categories_Column, e->categoriesStr()); return true; } bool ListView::Private::ListItemVisitor::visit(const Todo::Ptr &t) { mItem->setIcon(Summary_Column, QIcon::fromTheme(t->iconName())); mItem->setText(Summary_Column, cleanSummary(t->summary(), QDateTime())); if (t->hasStartDate()) { if (t->allDay()) mItem->setText(StartDateTime_Column, QLocale().toString(t->dtStart().toLocalZone().date(), QLocale::ShortFormat)); else mItem->setText(StartDateTime_Column, QLocale().toString(t->dtStart().toLocalZone().dateTime(), QLocale::ShortFormat)); } else { mItem->setText(StartDateTime_Column, QStringLiteral("---")); } if (t->hasDueDate()) { if (t->allDay()) mItem->setText(EndDateTime_Column, QLocale().toString(t->dtDue().toLocalZone().date(), QLocale::ShortFormat)); else mItem->setText(EndDateTime_Column, QLocale().toString(t->dtDue().toLocalZone().dateTime(), QLocale::ShortFormat)); } else { mItem->setText(EndDateTime_Column, QStringLiteral("---")); } mItem->setText(Categories_Column, t->categoriesStr()); return true; } bool ListView::Private::ListItemVisitor::visit(const Journal::Ptr &j) { static const QPixmap jrnalPxmp = SmallIcon(j->iconName()); mItem->setIcon(Summary_Column, jrnalPxmp); if (j->summary().isEmpty()) { mItem->setText(Summary_Column, cleanSummary(j->description().section(QLatin1Char('\n'), 0, 0), QDateTime())); } else { mItem->setText(Summary_Column, cleanSummary(j->summary(), QDateTime())); } if (j->allDay()) mItem->setText(StartDateTime_Column, QLocale().toString(j->dtStart().toLocalZone().date(), QLocale::ShortFormat)); else mItem->setText(StartDateTime_Column, QLocale().toString(j->dtStart().toLocalZone().dateTime(), QLocale::ShortFormat)); return true; } ListView::ListView(const Akonadi::ETMCalendar::Ptr &calendar, QWidget *parent, bool nonInteractive) : EventView(parent), d(new Private()) { setCalendar(calendar); d->mActiveItem = nullptr; d->mIsNonInteractive = nonInteractive; d->mTreeWidget = new QTreeWidget(this); d->mTreeWidget->setColumnCount(4); d->mTreeWidget->setSortingEnabled(true); d->mTreeWidget->headerItem()->setText(Summary_Column, i18n("Summary")); d->mTreeWidget->headerItem()->setText(StartDateTime_Column, i18n("Start Date/Time")); d->mTreeWidget->headerItem()->setText(EndDateTime_Column, i18n("End Date/Time")); d->mTreeWidget->headerItem()->setText(Categories_Column, i18n("Categories")); d->mTreeWidget->setWordWrap(true); d->mTreeWidget->setAllColumnsShowFocus(true); d->mTreeWidget->setContextMenuPolicy(Qt::CustomContextMenu); d->mTreeWidget->setRootIsDecorated(false); QBoxLayout *layoutTop = new QVBoxLayout(this); layoutTop->setMargin(0); layoutTop->addWidget(d->mTreeWidget); QObject::connect(d->mTreeWidget, SIGNAL(doubleClicked(QModelIndex)), SLOT(defaultItemAction(QModelIndex))); QObject::connect(d->mTreeWidget, &QWidget::customContextMenuRequested, this, &ListView::popupMenu); QObject::connect(d->mTreeWidget, &QTreeWidget::itemSelectionChanged, this, &ListView::processSelectionChange); // TODO //d->mTreeWidget->restoreLayout( KOGlobals::self()->config(), "ListView Layout" ); d->mSelectedDates.append(QDate::currentDate()); updateView(); } ListView::~ListView() { delete d; } int ListView::currentDateCount() const { return d->mSelectedDates.count(); } Akonadi::Item::List ListView::selectedIncidences() const { Akonadi::Item::List eventList; QTreeWidgetItem *item = d->mTreeWidget->selectedItems().isEmpty() ? nullptr : d->mTreeWidget->selectedItems().first(); if (item) { ListViewItem *i = static_cast(item); eventList.append(i->mIncidence); } return eventList; } DateList ListView::selectedIncidenceDates() const { return d->mSelectedDates; } void ListView::updateView() { static int maxLen = 38; /* Set the width of the summary column to show 'maxlen' chars, at most */ int width = qMin(maxLen * fontMetrics().averageCharWidth(), maxLen * 12); width += 24; //for the icon d->mTreeWidget->setColumnWidth(Summary_Column, width); for (int col = StartDateTime_Column; col < Dummy_EOF_Column; ++col) { d->mTreeWidget->resizeColumnToContents(col); } d->mTreeWidget->sortItems(StartDateTime_Column, Qt::DescendingOrder); } void ListView::showDates(const QDate &start, const QDate &end, const QDate &preferredMonth) { Q_UNUSED(preferredMonth); clear(); d->mStartDate = start; d->mEndDate = end; const QString startStr = QLocale().toString(start, QLocale::ShortFormat); const QString endStr = QLocale().toString(end, QLocale::ShortFormat); d->mTreeWidget->headerItem()->setText(Summary_Column, i18n("Summary [%1 - %2]", startStr, endStr)); QDate date = start; while (date <= end) { d->addIncidences(calendar(), calendar()->incidences(date), date); d->mSelectedDates.append(date); date = date.addDays(1); } updateView(); Q_EMIT incidenceSelected(Akonadi::Item(), QDate()); } void ListView::showAll() { d->addIncidences(calendar(), calendar()->incidences(), QDate()); } void ListView::Private::addIncidences(const Akonadi::ETMCalendar::Ptr &calendar, const KCalCore::Incidence::List &incidences, const QDate &date) { for (const KCalCore::Incidence::Ptr &incidence : incidences) { addIncidence(calendar, incidence, date); } } void ListView::Private::addIncidence(const Akonadi::ETMCalendar::Ptr &calendar, const Akonadi::Item &item, const QDate &date) { Q_ASSERT(calendar); if (item.isValid() && item.hasPayload()) { addIncidence(calendar, item.payload(), date); } } void ListView::Private::addIncidence(const Akonadi::ETMCalendar::Ptr &calendar, const KCalCore::Incidence::Ptr &incidence, const QDate &date) { if (!incidence) { return; } Akonadi::Item aitem = calendar->item(incidence); if (!aitem.isValid() || mItems.contains(aitem.id())) { return; } mDateList.insert(aitem.id(), date); mItems.insert(aitem.id(), aitem); Incidence::Ptr tinc = incidence; if (tinc->customProperty("KABC", "BIRTHDAY") == QLatin1String("YES") || tinc->customProperty("KABC", "ANNIVERSARY") == QLatin1String("YES")) { const int years = EventViews::yearDiff(tinc->dtStart().date(), mEndDate); if (years > 0) { tinc = Incidence::Ptr(incidence->clone()); tinc->setReadOnly(false); tinc->setSummary(i18np("%2 (1 year)", "%2 (%1 years)", years, cleanSummary(incidence->summary(), QDateTime()))); tinc->setReadOnly(true); } } ListViewItem *item = new ListViewItem(aitem, mTreeWidget); // set tooltips for (int col = 0; col < Dummy_EOF_Column; ++col) { item->setToolTip(col, IncidenceFormatter::toolTipStr( CalendarSupport::displayName(calendar.data(), aitem.parentCollection()), incidence)); } ListItemVisitor v(item, mStartDate); if (!tinc->accept(v, tinc)) { delete item; return; } item->setData(0, Qt::UserRole, QVariant(aitem.id())); } void ListView::showIncidences(const Akonadi::Item::List &itemList, const QDate &date) { clear(); d->addIncidences(calendar(), CalendarSupport::incidencesFromItems(itemList), date); updateView(); // After new creation of list view no events are selected. Q_EMIT incidenceSelected(Akonadi::Item(), date); } void ListView::changeIncidenceDisplay(const Akonadi::Item &aitem, int action) { const Incidence::Ptr incidence = CalendarSupport::incidence(aitem); ListViewItem *item = nullptr; QDate f = d->mSelectedDates.first(); QDate l = d->mSelectedDates.last(); QDate date; if (CalendarSupport::hasTodo(aitem)) { date = CalendarSupport::todo(aitem)->dtDue().toLocalZone().date(); } else { date = incidence->dtStart().toLocalZone().date(); } switch (action) { case Akonadi::IncidenceChanger::ChangeTypeCreate: { if (date >= f && date <= l) { d->addIncidence(calendar(), aitem, date); } break; } case Akonadi::IncidenceChanger::ChangeTypeModify: { item = d->getItemForIncidence(aitem); if (item) { delete item; d->mItems.remove(aitem.id()); d->mDateList.remove(aitem.id()); } if (date >= f && date <= l) { d->addIncidence(calendar(), aitem, date); } break; } case Akonadi::IncidenceChanger::ChangeTypeDelete: { item = d->getItemForIncidence(aitem); delete item; break; } default: qCDebug(CALENDARVIEW_LOG) << "Illegal action" << action; } } ListViewItem *ListView::Private::getItemForIncidence(const Akonadi::Item &aitem) { int index = 0; while (QTreeWidgetItem *it = mTreeWidget->topLevelItem(index)) { ListViewItem *item = static_cast(it); if (item->mIncidence.id() == aitem.id()) { return item; } ++index; } return nullptr; } void ListView::defaultItemAction(const QModelIndex &index) { if (!d->mIsNonInteractive) { // Get the first column, it has our Akonadi::Id const QModelIndex col0Idx = d->mTreeWidget->model()->index(index.row(), 0); Akonadi::Item::Id id = d->mTreeWidget->model()->data(col0Idx, Qt::UserRole).toLongLong(); defaultAction(d->mItems.value(id)); } } void ListView::defaultItemAction(const Akonadi::Item::Id id) { if (!d->mIsNonInteractive) { defaultAction(d->mItems.value(id)); } } void ListView::popupMenu(const QPoint &point) { d->mActiveItem = static_cast(d->mTreeWidget->itemAt(point)); if (d->mActiveItem && !d->mIsNonInteractive) { const Akonadi::Item aitem = d->mActiveItem->mIncidence; // FIXME: For recurring incidences we don't know the date of this // occurrence, there's no reference to it at all! Q_EMIT showIncidencePopupSignal(aitem, CalendarSupport::incidence(aitem)->dtStart().date()); } else { Q_EMIT showNewEventPopupSignal(); } } void ListView::readSettings(KConfig *config) { KConfigGroup cfgGroup = config->group("ListView Layout"); const QByteArray state = cfgGroup.readEntry("ViewState", QByteArray()); d->mTreeWidget->header()->restoreState(state); } void ListView::writeSettings(KConfig *config) { const QByteArray state = d->mTreeWidget->header()->saveState(); KConfigGroup cfgGroup = config->group("ListView Layout"); cfgGroup.writeEntry("ViewState", state); } void ListView::processSelectionChange() { if (!d->mIsNonInteractive) { ListViewItem *item; if (d->mTreeWidget->selectedItems().isEmpty()) { item = nullptr; } else { item = static_cast(d->mTreeWidget->selectedItems().first()); } if (!item) { Q_EMIT incidenceSelected(Akonadi::Item(), QDate()); } else { Q_EMIT incidenceSelected(item->mIncidence, d->mDateList.value(item->mIncidence.id())); } } } void ListView::clearSelection() { d->mTreeWidget->clearSelection(); } void ListView::clear() { d->mSelectedDates.clear(); d->mTreeWidget->clear(); d->mDateList.clear(); d->mItems.clear(); } QSize ListView::sizeHint() const { const QSize s = EventView::sizeHint(); return QSize(s.width() + style()->pixelMetric(QStyle::PM_ScrollBarExtent) + 1, s.height()); } diff --git a/src/month/monthitem.cpp b/src/month/monthitem.cpp index 0efbd24..1ce1cca 100644 --- a/src/month/monthitem.cpp +++ b/src/month/monthitem.cpp @@ -1,776 +1,776 @@ /* 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 #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 KDateTime dt = mIncidence->dateTime(Incidence::RoleDisplayStart); const QDate start = dt.isDateOnly() ? dt.date() : dt.toLocalZone().date(); return start.addDays(mRecurDayOffset); } QDate IncidenceMonthItem::realEndDate() const { if (!mIncidence) { return QDate(); } const KDateTime dt = mIncidence->dateTime(KCalCore::Incidence::RoleDisplayEnd); const QDate end = dt.isDateOnly() ? dt.date() : dt.toLocalZone().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(KCalCore::k2q(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().toLocalZone().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).toLocalZone().time(); + time = mIncidence->recurrence()->getNextDateTime(KCalCore::k2q(start)).toLocalTime().time(); } else { time = mIncidence->dtStart().toLocalZone().time(); } timeStr = QLocale().toString(time, QLocale::ShortFormat); } else { KCalCore::Event::Ptr event = mIncidence.staticCast(); timeStr = QLocale().toString(event->dtEnd().toLocalZone().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)) { KDateTime 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); } return cat.isEmpty() ? CalendarSupport::KCalPrefs::instance()->unsetCategoryColor() : CalendarSupport::KCalPrefs::instance()->categoryColor(cat); } QColor IncidenceMonthItem::bgColor() const { QColor bgColor = QColor(); // Default invalid color; PrefsPtr prefs = monthScene()->monthView()->preferences(); if (mIsTodo && !prefs->todosUseCategoryColors()) { Todo::Ptr todo = CalendarSupport::todo(akonadiItem()); Q_ASSERT(todo); if (todo) { const QDate dtRecurrence = // this is dtDue if there's no dtRecurrence todo->dtRecurrence().toLocalZone().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(); } } } 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; } 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(); } 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(); KDateTime due = todo->dtDue(); KDateTime 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/timespent/timespentview.cpp b/src/timespent/timespentview.cpp index 2ca4d76..d1c5c3b 100644 --- a/src/timespent/timespentview.cpp +++ b/src/timespent/timespentview.cpp @@ -1,240 +1,243 @@ /* This file is part of KOrganizer. Copyright (c) 2007 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 "timespentview.h" #include #include #include #include +#include + #include #include #include #include using namespace EventViews; namespace EventViews { class TimeSpentWidget : public QWidget { public: TimeSpentWidget(TimeSpentView *parent) : QWidget(parent), mTimeSpentView(parent) {} ~TimeSpentWidget() {} void paintEvent(QPaintEvent *e) override { QPainter p(this); p.fillRect(e->rect(), Qt::white); int margin = 10; int y = 90; p.fillRect(QRect(5, 5, width(), 35), QColor(54, 121, 173)); // sync with kowhatsnextview QPen oldPen = p.pen(); QFont oldFont = p.font(); QFont font = p.font(); font.setPointSize(25); font.setBold(true); p.setFont(font); p.setPen(QColor(Qt::white)); p.drawText(QPoint(25, 35), i18n("Time Tracker")); p.setPen(oldPen); p.setFont(oldFont); QString dateText; if (mTimeSpentView->mStartDate.daysTo(mTimeSpentView->mEndDate) < 1) { dateText = QLocale::system().toString(mTimeSpentView->mStartDate); } else { dateText = i18nc("Date from - to", "%1 - %2", QLocale::system().toString(mTimeSpentView->mStartDate), QLocale::system().toString(mTimeSpentView->mEndDate)); } font.setPointSize(20); font.setBold(true); p.setFont(font); p.drawText(QPoint(margin, 60), dateText); p.setPen(oldPen); p.setFont(oldFont); QMap secondsSpent; int total = 0; foreach (const KCalCore::Event::Ptr &e, mEventList) { Q_ASSERT(e); KDateTime selectedStart(mTimeSpentView->mStartDate, QTime(0, 0), e->dtStart().timeSpec()); KDateTime selectedEnd(mTimeSpentView->mEndDate.addDays(1), QTime(0, 0), e->dtEnd().timeSpec()); KDateTime start; KDateTime end; // duration of all occurrences added int totalDuration = 0; if (e->recurs()) { int eventDuration = e->dtStart().secsTo(e->dtEnd()); // timesInInterval only return events that have their start inside the interval // so we resize the interval by -eventDuration - KCalCore::DateTimeList times = e->recurrence()->timesInInterval( - selectedStart.addSecs(-eventDuration), selectedEnd); + const auto times = e->recurrence()->timesInInterval( + KCalCore::k2q(selectedStart).addSecs(-eventDuration), KCalCore::k2q(selectedEnd)); - foreach (const KDateTime &kdt, times) { + foreach (const QDateTime &dt, times) { + const auto kdt = KCalCore::q2k(dt); // either the event's start or the event's end must be in the view's interval if (kdt >= selectedStart || kdt.addSecs(eventDuration) >= selectedStart) { start = kdt > selectedStart ? kdt : selectedStart; end = kdt.addSecs(eventDuration) < selectedEnd ? kdt.addSecs(eventDuration) : selectedEnd; totalDuration += start.secsTo(end); } } } else { // The event's start can be before the view's start date or end after the view's end start = e->dtStart() > selectedStart ? e->dtStart() : selectedStart; end = e->dtEnd() < selectedEnd ? e->dtEnd() : selectedEnd; totalDuration += start.secsTo(end); } if (totalDuration == 0) { continue; } if (!e->categories().isEmpty()) { foreach (const QString &s, e->categories()) { secondsSpent[ s ] += totalDuration; } } else { secondsSpent[ i18n("No category") ] += totalDuration; } total += totalDuration; } QMapIterator i(secondsSpent); QFontMetrics fm = p.fontMetrics(); int lineHeight = fm.boundingRect(QStringLiteral("No category")).height(); int totalLineHeight = lineHeight + 2; // vertical margin included while (i.hasNext()) { i.next(); // bar const QColor color = CalendarSupport::KCalPrefs::instance()->categoryColor(i.key()); const int length = static_cast(((double) i.value()) / total * (width() - 3 * margin)); QPainterPath path(QPoint(margin, y)); path.lineTo(margin + length, y); if (length < margin) { path.lineTo(margin + length, y + lineHeight); } else { path.arcTo(QRect(margin + length, y, 2 * margin, lineHeight), +90, -180); } path.lineTo(margin, y + lineHeight); path.closeSubpath(); p.setBrush(color); p.drawPath(path); // text int totHr, perHr; if (total > 0) { totHr = i.value() / (60 * 60); perHr = static_cast(((double) i.value() * 100 / total)); } else { totHr = 0; perHr = 0; } p.drawText(QRect(margin + 2, y + 2, width() - 2 * margin, lineHeight), i.key() + QLatin1String(": ") + i18ncp("number of hours spent", "%1 hour", "%1 hours", totHr) + i18nc("percent of hours spent", " (%1%)", perHr)); y += totalLineHeight; } } KCalCore::Event::List mEventList; TimeSpentView *mTimeSpentView = nullptr; }; } TimeSpentView::TimeSpentView(QWidget *parent) : EventView(parent) { mView = new TimeSpentWidget(this); QBoxLayout *topLayout = new QVBoxLayout(this); topLayout->setMargin(0); topLayout->addWidget(mView); } TimeSpentView::~TimeSpentView() { } int TimeSpentView::currentDateCount() const { return mStartDate.daysTo(mEndDate); } void TimeSpentView::showDates(const QDate &start, const QDate &end, const QDate &) { mStartDate = start; mEndDate = end; updateView(); } void TimeSpentView::showIncidences(const Akonadi::Item::List &incidenceList, const QDate &date) { Q_UNUSED(incidenceList); Q_UNUSED(date); } void TimeSpentView::changeIncidenceDisplay(const Akonadi::Item &, Akonadi::IncidenceChanger::ChangeType) { updateView(); } void TimeSpentView::updateView() { mView->mEventList = calendar()->events(mStartDate, mEndDate, QTimeZone::systemTimeZone()); mView->repaint(); } diff --git a/src/whatsnext/whatsnextview.cpp b/src/whatsnext/whatsnextview.cpp index 7d58ee3..c63ee82 100644 --- a/src/whatsnext/whatsnextview.cpp +++ b/src/whatsnext/whatsnextview.cpp @@ -1,343 +1,344 @@ /* This file is part of KOrganizer. Copyright (c) 2001 Cornelius Schumacher 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 "whatsnextview.h" #include #include #include #include +#include #include #include using namespace EventViews; void WhatsNextTextBrowser::setSource(const QUrl &name) { QString uri = name.toString(); if (uri.startsWith(QStringLiteral("event:"))) { Q_EMIT showIncidence(uri); } else if (uri.startsWith(QStringLiteral("todo:"))) { Q_EMIT showIncidence(uri); } else { QTextBrowser::setSource(QUrl(uri)); } } WhatsNextView::WhatsNextView(QWidget *parent) : EventView(parent) { mView = new WhatsNextTextBrowser(this); connect(mView, &WhatsNextTextBrowser::showIncidence, this, &WhatsNextView::showIncidence); QBoxLayout *topLayout = new QVBoxLayout(this); topLayout->setMargin(0); topLayout->addWidget(mView); } WhatsNextView::~WhatsNextView() { } int WhatsNextView::currentDateCount() const { return mStartDate.daysTo(mEndDate); } void WhatsNextView::createTaskRow(KIconLoader *kil) { QString ipath; kil->loadIcon(QStringLiteral("view-calendar-tasks"), KIconLoader::NoGroup, 22, KIconLoader::DefaultState, QStringList(), &ipath); mText += QLatin1String("

"); mText += i18n("To-dos:") + QLatin1String("

\n"); mText += QLatin1String("
    \n"); } void WhatsNextView::updateView() { KIconLoader *kil = KIconLoader::global(); QString ipath; kil->loadIcon(QStringLiteral("office-calendar"), KIconLoader::NoGroup, 32, KIconLoader::DefaultState, QStringList(), &ipath); mText = QStringLiteral("\n"); mText += QLatin1String("\n\n

    "); mText += QLatin1String(""); mText += QLatin1String(" "); mText += i18n("What's Next?") + QLatin1String("

    "); mText += QLatin1String("
    "); mText += QLatin1String("

    "); if (mStartDate.daysTo(mEndDate) < 1) { mText += QLocale::system().toString(mStartDate); } else { mText += i18nc( "date from - to", "%1 - %2", QLocale::system().toString(mStartDate), QLocale::system().toString(mEndDate)); } mText += QLatin1String("

    \n"); KCalCore::Event::List events; events = calendar()->events(mStartDate, mEndDate, QTimeZone::systemTimeZone(), false); events = calendar()->sortEvents(events, KCalCore::EventSortStartDate, KCalCore::SortDirectionAscending); if (!events.isEmpty()) { mText += QLatin1String("

    "); kil->loadIcon(QStringLiteral("view-calendar-day"), KIconLoader::NoGroup, 22, KIconLoader::DefaultState, QStringList(), &ipath); mText += QLatin1String("

    "); mText += i18n("Events:") + QLatin1String("

    \n"); mText += QLatin1String("\n"); Q_FOREACH (const KCalCore::Event::Ptr &ev, events) { if (!ev->recurs()) { appendEvent(ev); } else { KCalCore::Recurrence *recur = ev->recurrence(); int duration = ev->dtStart().secsTo(ev->dtEnd()); - KDateTime start = recur->getPreviousDateTime(KDateTime(mStartDate, QTime(), KDateTime::LocalZone)); + KDateTime start = KCalCore::q2k(recur->getPreviousDateTime(QDateTime(mStartDate, QTime(), Qt::LocalTime))); KDateTime end = start.addSecs(duration); KDateTime endDate(mEndDate, QTime(23, 59, 59), KDateTime::LocalZone); if (end.date() >= mStartDate) { appendEvent(ev, start.toLocalZone().dateTime(), end.toLocalZone().dateTime()); } - KCalCore::DateTimeList times = recur->timesInInterval(start, endDate); + const auto times = recur->timesInInterval(KCalCore::k2q(start), KCalCore::k2q(endDate)); int count = times.count(); if (count > 0) { int i = 0; - if (times[0] == start) { + if (times[0] == KCalCore::k2q(start)) { ++i; // start has already been appended } if (!times[count - 1].isValid()) { --count; // list overflow } for (; i < count && times[i].date() <= mEndDate; ++i) { - appendEvent(ev, times[i].toLocalZone().dateTime()); + appendEvent(ev, times[i].toLocalTime()); } } } } mText += QLatin1String("
    \n"); } mTodos.clear(); KCalCore::Todo::List todos = calendar()->todos(KCalCore::TodoSortDueDate, KCalCore::SortDirectionAscending); if (!todos.isEmpty()) { bool taskHeaderWasCreated = false; Q_FOREACH (const KCalCore::Todo::Ptr &todo, todos) { if (!todo->isCompleted() && todo->hasDueDate() && todo->dtDue().date() <= mEndDate) { if (!taskHeaderWasCreated) { createTaskRow(kil); taskHeaderWasCreated = true; } appendTodo(todo); } } bool gotone = false; int priority = 1; while (!gotone && priority <= 9) { Q_FOREACH (const KCalCore::Todo::Ptr &todo, todos) { if (!todo->isCompleted() && (todo->priority() == priority)) { if (!taskHeaderWasCreated) { createTaskRow(kil); taskHeaderWasCreated = true; } appendTodo(todo); gotone = true; } } priority++; } if (taskHeaderWasCreated) { mText += QLatin1String("\n"); } } QStringList myEmails(CalendarSupport::KCalPrefs::instance()->allEmails()); int replies = 0; events = calendar()->events(QDate::currentDate(), QDate(2975, 12, 6), QTimeZone::systemTimeZone()); Q_FOREACH (const KCalCore::Event::Ptr &ev, events) { KCalCore::Attendee::Ptr me = ev->attendeeByMails(myEmails); if (me != nullptr) { if (me->status() == KCalCore::Attendee::NeedsAction && me->RSVP()) { if (replies == 0) { mText += QLatin1String("

    "); kil->loadIcon(QStringLiteral("mail-reply-sender"), KIconLoader::NoGroup, 22, KIconLoader::DefaultState, QStringList(), &ipath); mText += QLatin1String("

    "); mText += i18n("Events and to-dos that need a reply:") + QLatin1String("

    \n"); mText += QLatin1String("\n"); } replies++; appendEvent(ev); } } } todos = calendar()->todos(); Q_FOREACH (const KCalCore::Todo::Ptr &to, todos) { KCalCore::Attendee::Ptr me = to->attendeeByMails(myEmails); if (me != nullptr) { if (me->status() == KCalCore::Attendee::NeedsAction && me->RSVP()) { if (replies == 0) { mText += QLatin1String("

    "); kil->loadIcon(QStringLiteral("mail-reply-sender"), KIconLoader::NoGroup, 22, KIconLoader::DefaultState, QStringList(), &ipath); mText += QLatin1String("

    "); mText += i18n("Events and to-dos that need a reply:") + QLatin1String("

    \n"); mText += QLatin1String("
    \n"); } replies++; appendEvent(to); } } } if (replies > 0) { mText += QLatin1String("
    \n"); } mText += QLatin1String("
    \n"); mView->setText(mText); } void WhatsNextView::showDates(const QDate &start, const QDate &end, const QDate &) { mStartDate = start; mEndDate = end; updateView(); } void WhatsNextView::showIncidences(const Akonadi::Item::List &incidenceList, const QDate &date) { Q_UNUSED(incidenceList); Q_UNUSED(date); } void WhatsNextView::changeIncidenceDisplay(const Akonadi::Item &, Akonadi::IncidenceChanger::ChangeType) { updateView(); } void WhatsNextView::appendEvent(const KCalCore::Incidence::Ptr &incidence, const QDateTime &start, const QDateTime &end) { mText += QLatin1String(""); if (const KCalCore::Event::Ptr event = incidence.dynamicCast()) { auto starttime = start.toLocalTime(); if (!starttime.isValid()) { starttime = event->dtStart().dateTime().toLocalTime(); } auto endtime = end.toLocalTime(); if (!endtime.isValid()) { endtime = starttime.addSecs(event->dtStart().secsTo(event->dtEnd())); } if (starttime.date().daysTo(endtime.date()) >= 1) { if (event->allDay()) mText += i18nc("date from - to", "%1 - %2", QLocale().toString(starttime.date(), QLocale::ShortFormat), QLocale().toString(endtime.date(), QLocale::ShortFormat)); else mText += i18nc("date from - to", "%1 - %2", QLocale().toString(starttime, QLocale::ShortFormat), QLocale().toString(endtime, QLocale::ShortFormat)); } else { if (event->allDay()) mText += QLocale().toString(starttime.date(), QLocale::ShortFormat); else mText += i18nc("date, from - to", "%1, %2 - %3", QLocale().toString(starttime.date(), QLocale::ShortFormat), QLocale().toString(starttime.time(), QLocale::ShortFormat), QLocale().toString(endtime.time(), QLocale::ShortFormat)); } } mText += QLatin1String("type() == KCalCore::Incidence::TypeEvent) { mText += QLatin1String("href=\"event:"); } if (incidence->type() == KCalCore::Incidence::TypeTodo) { mText += QLatin1String("href=\"todo:"); } mText += incidence->uid() + QLatin1String("\">"); mText += incidence->summary(); mText += QLatin1String("\n"); } void WhatsNextView::appendTodo(const KCalCore::Incidence::Ptr &incidence) { Akonadi::Item aitem = calendar()->item(incidence); if (mTodos.contains(aitem)) { return; } mTodos.append(aitem); mText += QLatin1String("
  • uid() + QLatin1String("\">"); mText += incidence->summary(); mText += QLatin1String(""); if (const KCalCore::Todo::Ptr todo = CalendarSupport::todo(aitem)) { if (todo->hasDueDate()) { mText += i18nc("to-do due date", " (Due: %1)", KCalUtils::IncidenceFormatter::dateTimeToString( todo->dtDue().dateTime(), todo->allDay())); } mText += QLatin1String("
  • \n"); } } void WhatsNextView::showIncidence(const QString &uid) { Akonadi::Item item; Akonadi::ETMCalendar::Ptr cal = calendar(); if (!cal) { return; } if (uid.startsWith(QStringLiteral("event:"))) { item = cal->item(uid.mid(6)); } else if (uid.startsWith(QStringLiteral("todo:"))) { item = cal->item(uid.mid(5)); } if (item.isValid()) { Q_EMIT showIncidenceSignal(item); } }