Eventviews

whatsnextview.cpp
1/*
2 This file is part of KOrganizer.
3
4 SPDX-FileCopyrightText: 2001 Cornelius Schumacher <schumacher@kde.org>
5
6 SPDX-License-Identifier: GPL-2.0-or-later WITH Qt-Commercial-exception-1.0
7*/
8
9#include "whatsnextview.h"
10using namespace Qt::Literals::StringLiterals;
11
12#include "calendarview_debug.h"
13
14#include <Akonadi/CalendarUtils>
15#include <CalendarSupport/KCalPrefs>
16#include <CalendarSupport/Utils>
17
18#include <KCalUtils/IncidenceFormatter>
19
20#include <QBoxLayout>
21#include <QUrlQuery>
22
23#include <optional>
24
25using namespace EventViews;
26void WhatsNextTextBrowser::doSetSource(const QUrl &name, QTextDocument::ResourceType type)
27{
28 Q_UNUSED(type);
29 if (name.scheme() == "event"_L1) {
30 Q_EMIT showIncidence(name);
31 } else if (name.scheme() == "todo"_L1) {
32 Q_EMIT showIncidence(name);
33 } else {
35 }
36}
37
38WhatsNextView::WhatsNextView(QWidget *parent)
39 : EventView(parent)
40 , mView(new WhatsNextTextBrowser(this))
41{
42 connect(mView, &WhatsNextTextBrowser::showIncidence, this, &WhatsNextView::showIncidence);
43
44 QBoxLayout *topLayout = new QVBoxLayout(this);
45 topLayout->setContentsMargins({});
46 topLayout->addWidget(mView);
47}
48
49WhatsNextView::~WhatsNextView() = default;
50
52{
53 return mStartDate.daysTo(mEndDate);
54}
55
56void WhatsNextView::createTaskRow(KIconLoader *kil)
57{
58 QString ipath;
59 kil->loadIcon(QStringLiteral("view-calendar-tasks"), KIconLoader::NoGroup, 22, KIconLoader::DefaultState, QStringList(), &ipath);
60 mText += "<h2><img src=\""_L1;
61 mText += ipath;
62 mText += QLatin1StringView(R"(" width="22" height="22">)");
63 mText += i18n("To-dos:") + "</h2>\n"_L1;
64 mText += "<ul>\n"_L1;
65}
66
67void WhatsNextView::updateView()
68{
70 QString ipath;
71 kil->loadIcon(QStringLiteral("office-calendar"), KIconLoader::NoGroup, 32, KIconLoader::DefaultState, QStringList(), &ipath);
72
73 mText = QStringLiteral("<table width=\"100%\">\n");
74 mText += "<tr bgcolor=\"#3679AD\"><td><h1>"_L1;
75 mText += "<img src=\""_L1;
76 mText += ipath;
77 mText += QLatin1StringView(R"(" width="32" height="32">)");
78 mText += "<font color=\"white\"> "_L1;
79 mText += i18n("What's Next?") + "</font></h1>"_L1;
80 mText += "</td></tr>\n<tr><td>"_L1;
81
82 mText += "<h2>"_L1;
83 if (mStartDate.daysTo(mEndDate) < 1) {
84 mText += QLocale::system().toString(mStartDate);
85 } else {
86 mText += i18nc("date from - to", "%1 - %2", QLocale::system().toString(mStartDate), QLocale::system().toString(mEndDate));
87 }
88 mText += "</h2>\n"_L1;
89
91 for (const auto &calendar : calendars()) {
92 events += calendar->events(mStartDate, mEndDate, QTimeZone::systemTimeZone(), false);
93 }
94
96 if (!events.isEmpty()) {
97 mText += "<p></p>"_L1;
98 kil->loadIcon(QStringLiteral("view-calendar-day"), KIconLoader::NoGroup, 22, KIconLoader::DefaultState, QStringList(), &ipath);
99 mText += "<h2><img src=\""_L1;
100 mText += ipath;
101 mText += QLatin1StringView(R"(" width="22" height="22">)");
102 mText += i18n("Events:") + "</h2>\n"_L1;
103 mText += "<table>\n"_L1;
104 for (const KCalendarCore::Event::Ptr &ev : std::as_const(events)) {
105 const auto calendar = calendar3(ev);
106 if (!ev->recurs()) {
107 appendEvent(calendar, ev);
108 } else {
109 KCalendarCore::Recurrence *recur = ev->recurrence();
110 int duration = ev->dtStart().secsTo(ev->dtEnd());
112 QDateTime end = start.addSecs(duration);
113 QDateTime endDate(mEndDate, QTime(23, 59, 59), QTimeZone::LocalTime);
114 if (end.date() >= mStartDate) {
115 appendEvent(calendar, ev, start.toLocalTime(), end.toLocalTime());
116 }
117 const auto times = recur->timesInInterval(start, endDate);
118 int count = times.count();
119 if (count > 0) {
120 int i = 0;
121 if (times[0] == start) {
122 ++i; // start has already been appended
123 }
124 if (!times[count - 1].isValid()) {
125 --count; // list overflow
126 }
127 for (; i < count && times[i].date() <= mEndDate; ++i) {
128 appendEvent(calendar, ev, times[i].toLocalTime());
129 }
130 }
131 }
132 }
133 mText += "</table>\n"_L1;
134 }
135
136 mTodos.clear();
138 for (const auto &calendar : calendars()) {
140 }
141 if (!todos.isEmpty()) {
142 bool taskHeaderWasCreated = false;
143 for (const KCalendarCore::Todo::Ptr &todo : std::as_const(todos)) {
144 const auto calendar = calendar3(todo);
145 if (!todo->isCompleted() && todo->hasDueDate() && todo->dtDue().date() <= mEndDate) {
146 if (!taskHeaderWasCreated) {
147 createTaskRow(kil);
148 taskHeaderWasCreated = true;
149 }
150 appendTodo(calendar, todo);
151 }
152 }
153 bool gotone = false;
154 int priority = 1;
155 while (!gotone && priority <= 9) {
156 for (const KCalendarCore::Todo::Ptr &todo : std::as_const(todos)) {
157 const auto calendar = calendar3(todo);
158 if (!todo->isCompleted() && (todo->priority() == priority)) {
159 if (!taskHeaderWasCreated) {
160 createTaskRow(kil);
161 taskHeaderWasCreated = true;
162 }
163 appendTodo(calendar, todo);
164 gotone = true;
165 }
166 }
167 priority++;
168 }
169 if (taskHeaderWasCreated) {
170 mText += "</ul>\n"_L1;
171 }
172 }
173
174 QStringList myEmails(CalendarSupport::KCalPrefs::instance()->allEmails());
175 int replies = 0;
176 events.clear();
177 for (const auto &calendar : calendars()) {
178 events += calendar->events(QDate::currentDate(), QDate(2975, 12, 6), QTimeZone::systemTimeZone());
179 }
180 for (const KCalendarCore::Event::Ptr &ev : std::as_const(events)) {
181 const auto calendar = calendar3(ev);
182 KCalendarCore::Attendee me = ev->attendeeByMails(myEmails);
183 if (!me.isNull()) {
185 if (replies == 0) {
186 mText += "<p></p>"_L1;
187 kil->loadIcon(QStringLiteral("mail-reply-sender"), KIconLoader::NoGroup, 22, KIconLoader::DefaultState, QStringList(), &ipath);
188 mText += "<h2><img src=\""_L1;
189 mText += ipath;
190 mText += QLatin1StringView(R"(" width="22" height="22">)");
191 mText += i18n("Events and to-dos that need a reply:") + "</h2>\n"_L1;
192 mText += "<table>\n"_L1;
193 }
194 replies++;
195 appendEvent(calendar, ev);
196 }
197 }
198 }
199
200 todos.clear();
201 for (const auto &calendar : calendars()) {
202 todos += calendar->todos();
203 }
204 for (const KCalendarCore::Todo::Ptr &to : std::as_const(todos)) {
205 const auto calendar = calendar3(to);
206 KCalendarCore::Attendee me = to->attendeeByMails(myEmails);
207 if (!me.isNull()) {
209 if (replies == 0) {
210 mText += "<p></p>"_L1;
211 kil->loadIcon(QStringLiteral("mail-reply-sender"), KIconLoader::NoGroup, 22, KIconLoader::DefaultState, QStringList(), &ipath);
212 mText += "<h2><img src=\""_L1;
213 mText += ipath;
214 mText += QLatin1StringView(R"(" width="22" height="22">)");
215 mText += i18n("Events and to-dos that need a reply:") + "</h2>\n"_L1;
216 mText += "<table>\n"_L1;
217 }
218 replies++;
219 appendEvent(calendar, to);
220 }
221 }
222 }
223 if (replies > 0) {
224 mText += "</table>\n"_L1;
225 }
226
227 mText += "</td></tr>\n</table>\n"_L1;
228
229 mView->setText(mText);
230}
231
232void WhatsNextView::showDates(const QDate &start, const QDate &end, const QDate &)
233{
234 mStartDate = start;
235 mEndDate = end;
236 updateView();
237}
238
239void WhatsNextView::showIncidences(const Akonadi::Item::List &incidenceList, const QDate &date)
240{
241 Q_UNUSED(incidenceList)
242 Q_UNUSED(date)
243}
244
245void WhatsNextView::changeIncidenceDisplay(const Akonadi::Item &, Akonadi::IncidenceChanger::ChangeType)
246{
247 updateView();
248}
249
250void WhatsNextView::appendEvent(const Akonadi::CollectionCalendar::Ptr &calendar,
251 const KCalendarCore::Incidence::Ptr &incidence,
252 const QDateTime &start,
253 const QDateTime &end)
254{
255 mText += "<tr><td><b>"_L1;
257 auto starttime = start.toLocalTime();
258 if (!starttime.isValid()) {
259 starttime = event->dtStart().toLocalTime();
260 }
261 auto endtime = end.toLocalTime();
262 if (!endtime.isValid()) {
263 endtime = starttime.addSecs(event->dtStart().secsTo(event->dtEnd()));
264 }
265
266 if (starttime.date().daysTo(endtime.date()) >= 1) {
267 if (event->allDay()) {
268 mText += i18nc("date from - to",
269 "%1 - %2",
270 QLocale().toString(starttime.date(), QLocale::ShortFormat),
271 QLocale().toString(endtime.date(), QLocale::ShortFormat));
272 } else {
273 mText +=
274 i18nc("date from - to", "%1 - %2", QLocale().toString(starttime, QLocale::ShortFormat), QLocale().toString(endtime, QLocale::ShortFormat));
275 }
276 } else {
277 if (event->allDay()) {
278 mText += QLocale().toString(starttime.date(), QLocale::ShortFormat);
279 } else {
280 mText += i18nc("date, from - to",
281 "%1, %2 - %3",
282 QLocale().toString(starttime.date(), QLocale::ShortFormat),
283 QLocale().toString(starttime.time(), QLocale::ShortFormat),
284 QLocale().toString(endtime.time(), QLocale::ShortFormat));
285 }
286 }
287 }
288 mText += "</b></td>"_L1;
289 const QString proto = incidence->type() == KCalendarCore::Incidence::TypeTodo ? QStringLiteral("todo") : QStringLiteral("event");
290 mText += QStringLiteral(R"(<td><a href="%1:%2?itemId=%3&calendarId=%5">%4</a></td>)")
291 .arg(proto, incidence->uid(), incidence->customProperty("VOLATILE", "AKONADI-ID"), incidence->summary())
292 .arg(calendar->collection().id());
293 mText += "</tr>\n"_L1;
294}
295
296void WhatsNextView::appendTodo(const Akonadi::CollectionCalendar::Ptr &calendar, const KCalendarCore::Incidence::Ptr &incidence)
297{
298 Akonadi::Item aitem = calendar->item(incidence);
299 if (mTodos.contains(aitem)) {
300 return;
301 }
302 mTodos.append(aitem);
303 mText += "<li>"_L1;
304 mText += QStringLiteral(R"(<a href="todo:%1?itemId=%2&calendarId=%4">%3</a>)")
305 .arg(incidence->uid(), incidence->customProperty("VOLATILE", "AKONADI-ID"), incidence->summary())
306 .arg(calendar->collection().id());
307
309 if (todo->hasDueDate()) {
310 mText += i18nc("to-do due date", " (Due: %1)", KCalUtils::IncidenceFormatter::dateTimeToString(todo->dtDue(), todo->allDay()));
311 }
312 mText += "</li>\n"_L1;
313 }
314}
315
316static std::optional<Akonadi::Item::Id> idFromQuery(const QUrlQuery &query, const QString &queryValue)
317{
318 const auto strVal = query.queryItemValue(queryValue);
319 if (strVal.isEmpty()) {
320 return {};
321 }
322
323 bool ok = false;
324 const auto intVal = strVal.toLongLong(&ok);
325 if (!ok) {
326 return {};
327 }
328
329 return intVal;
330}
331
332void WhatsNextView::showIncidence(const QUrl &uri)
333{
334 QUrlQuery query(uri);
335 const auto itemId = idFromQuery(query, QStringLiteral("itemId"));
336 const auto calendarId = idFromQuery(query, QStringLiteral("calendarId"));
337
338 if (!itemId || !calendarId) {
339 qCWarning(CALENDARVIEW_LOG) << "Invalid URI:" << uri;
340 return;
341 }
342
343 auto calendar = calendarForCollection(*calendarId);
344 if (!calendar) {
345 qCWarning(CALENDARVIEW_LOG) << "Calendar for collection " << *calendarId << " not present in current view";
346 return;
347 }
348
349 auto item = calendar->item(*itemId);
350 if (!item.isValid()) {
351 qCWarning(CALENDARVIEW_LOG) << "Item " << *itemId << " not found in collection " << *calendarId;
352 return;
353 }
354
356}
357
358#include "moc_whatsnextview.cpp"
EventView is the abstract base class from which all other calendar views for event data are derived.
Definition eventview.h:67
void showIncidenceSignal(const Akonadi::Item &)
instructs the receiver to show the incidence in read-only mode.
int currentDateCount() const override
Returns the number of currently shown dates.
PartStat status() const
static Event::List sortEvents(Event::List &&eventList, EventSortField sortField, SortDirection sortDirection)
QList< QDateTime > timesInInterval(const QDateTime &start, const QDateTime &end) const
QDateTime getPreviousDateTime(const QDateTime &afterDateTime) const
static KIconLoader * global()
QPixmap loadIcon(const QString &name, KIconLoader::Group group, int size=0, int state=KIconLoader::DefaultState, const QStringList &overlays=QStringList(), QString *path_store=nullptr, bool canReturnNull=false) const
Q_SCRIPTABLE Q_NOREPLY void start()
QString i18nc(const char *context, const char *text, const TYPE &arg...)
QString i18n(const char *text, const TYPE &arg...)
AKONADI_CALENDAR_EXPORT KCalendarCore::Incidence::Ptr incidence(const Akonadi::Item &item)
AKONADI_CALENDAR_EXPORT KCalendarCore::Todo::Ptr todo(const Akonadi::Item &item)
char * toString(const EngineQuery &query)
Namespace EventViews provides facilities for displaying incidences, including events,...
Definition agenda.h:33
KSERVICE_EXPORT KService::List query(FilterFunc filterFunc)
KCALUTILS_EXPORT QString dateTimeToString(const QDateTime &date, bool dateOnly=false, bool shortfmt=true)
bool isValid(QStringView ifopt)
QString name(StandardAction id)
const QList< QKeySequence > & end()
void addWidget(QWidget *widget, int stretch, Qt::Alignment alignment)
QDate currentDate()
qint64 daysTo(QDate d) const const
void setContentsMargins(const QMargins &margins)
void append(QList< T > &&value)
void clear()
bool contains(const AT &value) const const
bool isEmpty() const const
QLocale system()
QString toString(QDate date, FormatType format) const const
Q_EMITQ_EMIT
QSharedPointer< X > dynamicCast() const const
QString arg(Args &&... args) const const
void setSource(const QUrl &url, QTextDocument::ResourceType type)
void setText(const QString &text)
QFuture< ArgsType< Signal > > connect(Sender *sender, Signal signal)
QTimeZone systemTimeZone()
virtual bool event(QEvent *event) override
This file is part of the KDE documentation.
Documentation copyright © 1996-2024 The KDE developers.
Generated on Fri Oct 11 2024 12:12:24 by doxygen 1.12.0 written by Dimitri van Heesch, © 1997-2006

KDE's Doxygen guidelines are available online.