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"
10#include "calendarview_debug.h"
11
12#include <Akonadi/CalendarUtils>
13#include <CalendarSupport/KCalPrefs>
14#include <CalendarSupport/Utils>
15
16#include <KCalUtils/IncidenceFormatter>
17
18#include <QBoxLayout>
19#include <QUrlQuery>
20
21#include <optional>
22
23using namespace EventViews;
24void WhatsNextTextBrowser::doSetSource(const QUrl &name, QTextDocument::ResourceType type)
25{
26 Q_UNUSED(type);
27 if (name.scheme() == QLatin1StringView("event")) {
28 Q_EMIT showIncidence(name);
29 } else if (name.scheme() == QLatin1StringView("todo")) {
30 Q_EMIT showIncidence(name);
31 } else {
33 }
34}
35
36WhatsNextView::WhatsNextView(QWidget *parent)
37 : EventView(parent)
38 , mView(new WhatsNextTextBrowser(this))
39{
40 connect(mView, &WhatsNextTextBrowser::showIncidence, this, &WhatsNextView::showIncidence);
41
42 QBoxLayout *topLayout = new QVBoxLayout(this);
43 topLayout->setContentsMargins(0, 0, 0, 0);
44 topLayout->addWidget(mView);
45}
46
47WhatsNextView::~WhatsNextView() = default;
48
50{
51 return mStartDate.daysTo(mEndDate);
52}
53
54void WhatsNextView::createTaskRow(KIconLoader *kil)
55{
56 QString ipath;
57 kil->loadIcon(QStringLiteral("view-calendar-tasks"), KIconLoader::NoGroup, 22, KIconLoader::DefaultState, QStringList(), &ipath);
58 mText += QLatin1StringView("<h2><img src=\"");
59 mText += ipath;
60 mText += QLatin1StringView(R"(" width="22" height="22">)");
61 mText += i18n("To-dos:") + QLatin1StringView("</h2>\n");
62 mText += QLatin1StringView("<ul>\n");
63}
64
65void WhatsNextView::updateView()
66{
68 QString ipath;
69 kil->loadIcon(QStringLiteral("office-calendar"), KIconLoader::NoGroup, 32, KIconLoader::DefaultState, QStringList(), &ipath);
70
71 mText = QStringLiteral("<table width=\"100%\">\n");
72 mText += QLatin1StringView("<tr bgcolor=\"#3679AD\"><td><h1>");
73 mText += QLatin1StringView("<img src=\"");
74 mText += ipath;
75 mText += QLatin1StringView(R"(" width="32" height="32">)");
76 mText += QLatin1StringView("<font color=\"white\"> ");
77 mText += i18n("What's Next?") + QLatin1StringView("</font></h1>");
78 mText += QLatin1StringView("</td></tr>\n<tr><td>");
79
80 mText += QLatin1StringView("<h2>");
81 if (mStartDate.daysTo(mEndDate) < 1) {
82 mText += QLocale::system().toString(mStartDate);
83 } else {
84 mText += i18nc("date from - to", "%1 - %2", QLocale::system().toString(mStartDate), QLocale::system().toString(mEndDate));
85 }
86 mText += QLatin1StringView("</h2>\n");
87
89 for (const auto &calendar : calendars()) {
90 events += calendar->events(mStartDate, mEndDate, QTimeZone::systemTimeZone(), false);
91 }
92
94 if (!events.isEmpty()) {
95 mText += QLatin1StringView("<p></p>");
96 kil->loadIcon(QStringLiteral("view-calendar-day"), KIconLoader::NoGroup, 22, KIconLoader::DefaultState, QStringList(), &ipath);
97 mText += QLatin1StringView("<h2><img src=\"");
98 mText += ipath;
99 mText += QLatin1StringView(R"(" width="22" height="22">)");
100 mText += i18n("Events:") + QLatin1StringView("</h2>\n");
101 mText += QLatin1StringView("<table>\n");
102 for (const KCalendarCore::Event::Ptr &ev : std::as_const(events)) {
103 const auto calendar = calendar3(ev);
104 if (!ev->recurs()) {
105 appendEvent(calendar, ev);
106 } else {
107 KCalendarCore::Recurrence *recur = ev->recurrence();
108 int duration = ev->dtStart().secsTo(ev->dtEnd());
110 QDateTime end = start.addSecs(duration);
111 QDateTime endDate(mEndDate, QTime(23, 59, 59), Qt::LocalTime);
112 if (end.date() >= mStartDate) {
113 appendEvent(calendar, ev, start.toLocalTime(), end.toLocalTime());
114 }
115 const auto times = recur->timesInInterval(start, endDate);
116 int count = times.count();
117 if (count > 0) {
118 int i = 0;
119 if (times[0] == start) {
120 ++i; // start has already been appended
121 }
122 if (!times[count - 1].isValid()) {
123 --count; // list overflow
124 }
125 for (; i < count && times[i].date() <= mEndDate; ++i) {
126 appendEvent(calendar, ev, times[i].toLocalTime());
127 }
128 }
129 }
130 }
131 mText += QLatin1StringView("</table>\n");
132 }
133
134 mTodos.clear();
136 for (const auto &calendar : calendars()) {
138 }
139 if (!todos.isEmpty()) {
140 bool taskHeaderWasCreated = false;
141 for (const KCalendarCore::Todo::Ptr &todo : std::as_const(todos)) {
142 const auto calendar = calendar3(todo);
143 if (!todo->isCompleted() && todo->hasDueDate() && todo->dtDue().date() <= mEndDate) {
144 if (!taskHeaderWasCreated) {
145 createTaskRow(kil);
146 taskHeaderWasCreated = true;
147 }
148 appendTodo(calendar, todo);
149 }
150 }
151 bool gotone = false;
152 int priority = 1;
153 while (!gotone && priority <= 9) {
154 for (const KCalendarCore::Todo::Ptr &todo : std::as_const(todos)) {
155 const auto calendar = calendar3(todo);
156 if (!todo->isCompleted() && (todo->priority() == priority)) {
157 if (!taskHeaderWasCreated) {
158 createTaskRow(kil);
159 taskHeaderWasCreated = true;
160 }
161 appendTodo(calendar, todo);
162 gotone = true;
163 }
164 }
165 priority++;
166 }
167 if (taskHeaderWasCreated) {
168 mText += QLatin1StringView("</ul>\n");
169 }
170 }
171
172 QStringList myEmails(CalendarSupport::KCalPrefs::instance()->allEmails());
173 int replies = 0;
174 events.clear();
175 for (const auto &calendar : calendars()) {
176 events += calendar->events(QDate::currentDate(), QDate(2975, 12, 6), QTimeZone::systemTimeZone());
177 }
178 for (const KCalendarCore::Event::Ptr &ev : std::as_const(events)) {
179 const auto calendar = calendar3(ev);
180 KCalendarCore::Attendee me = ev->attendeeByMails(myEmails);
181 if (!me.isNull()) {
183 if (replies == 0) {
184 mText += QLatin1StringView("<p></p>");
185 kil->loadIcon(QStringLiteral("mail-reply-sender"), KIconLoader::NoGroup, 22, KIconLoader::DefaultState, QStringList(), &ipath);
186 mText += QLatin1StringView("<h2><img src=\"");
187 mText += ipath;
188 mText += QLatin1StringView(R"(" width="22" height="22">)");
189 mText += i18n("Events and to-dos that need a reply:") + QLatin1StringView("</h2>\n");
190 mText += QLatin1StringView("<table>\n");
191 }
192 replies++;
193 appendEvent(calendar, ev);
194 }
195 }
196 }
197
198 todos.clear();
199 for (const auto &calendar : calendars()) {
200 todos += calendar->todos();
201 }
202 for (const KCalendarCore::Todo::Ptr &to : std::as_const(todos)) {
203 const auto calendar = calendar3(to);
204 KCalendarCore::Attendee me = to->attendeeByMails(myEmails);
205 if (!me.isNull()) {
207 if (replies == 0) {
208 mText += QLatin1StringView("<p></p>");
209 kil->loadIcon(QStringLiteral("mail-reply-sender"), KIconLoader::NoGroup, 22, KIconLoader::DefaultState, QStringList(), &ipath);
210 mText += QLatin1StringView("<h2><img src=\"");
211 mText += ipath;
212 mText += QLatin1StringView(R"(" width="22" height="22">)");
213 mText += i18n("Events and to-dos that need a reply:") + QLatin1StringView("</h2>\n");
214 mText += QLatin1StringView("<table>\n");
215 }
216 replies++;
217 appendEvent(calendar, to);
218 }
219 }
220 }
221 if (replies > 0) {
222 mText += QLatin1StringView("</table>\n");
223 }
224
225 mText += QLatin1StringView("</td></tr>\n</table>\n");
226
227 mView->setText(mText);
228}
229
230void WhatsNextView::showDates(const QDate &start, const QDate &end, const QDate &)
231{
232 mStartDate = start;
233 mEndDate = end;
234 updateView();
235}
236
237void WhatsNextView::showIncidences(const Akonadi::Item::List &incidenceList, const QDate &date)
238{
239 Q_UNUSED(incidenceList)
240 Q_UNUSED(date)
241}
242
243void WhatsNextView::changeIncidenceDisplay(const Akonadi::Item &, Akonadi::IncidenceChanger::ChangeType)
244{
245 updateView();
246}
247
248void WhatsNextView::appendEvent(const Akonadi::CollectionCalendar::Ptr &calendar,
249 const KCalendarCore::Incidence::Ptr &incidence,
250 const QDateTime &start,
251 const QDateTime &end)
252{
253 mText += QLatin1StringView("<tr><td><b>");
255 auto starttime = start.toLocalTime();
256 if (!starttime.isValid()) {
257 starttime = event->dtStart().toLocalTime();
258 }
259 auto endtime = end.toLocalTime();
260 if (!endtime.isValid()) {
261 endtime = starttime.addSecs(event->dtStart().secsTo(event->dtEnd()));
262 }
263
264 if (starttime.date().daysTo(endtime.date()) >= 1) {
265 if (event->allDay()) {
266 mText += i18nc("date from - to",
267 "%1 - %2",
268 QLocale().toString(starttime.date(), QLocale::ShortFormat),
269 QLocale().toString(endtime.date(), QLocale::ShortFormat));
270 } else {
271 mText +=
272 i18nc("date from - to", "%1 - %2", QLocale().toString(starttime, QLocale::ShortFormat), QLocale().toString(endtime, QLocale::ShortFormat));
273 }
274 } else {
275 if (event->allDay()) {
276 mText += QLocale().toString(starttime.date(), QLocale::ShortFormat);
277 } else {
278 mText += i18nc("date, from - to",
279 "%1, %2 - %3",
280 QLocale().toString(starttime.date(), QLocale::ShortFormat),
281 QLocale().toString(starttime.time(), QLocale::ShortFormat),
282 QLocale().toString(endtime.time(), QLocale::ShortFormat));
283 }
284 }
285 }
286 mText += QLatin1StringView("</b></td>");
287 const QString proto = incidence->type() == KCalendarCore::Incidence::TypeTodo ? QStringLiteral("todo") : QStringLiteral("event");
288 mText += QStringLiteral(R"(<td><a href="%1:%2?itemId=%3&calendarId=%5">%4</a></td>)")
289 .arg(proto, incidence->uid(), incidence->customProperty("VOLATILE", "AKONADI-ID"), incidence->summary())
290 .arg(calendar->collection().id());
291 mText += QLatin1StringView("</tr>\n");
292}
293
294void WhatsNextView::appendTodo(const Akonadi::CollectionCalendar::Ptr &calendar, const KCalendarCore::Incidence::Ptr &incidence)
295{
296 Akonadi::Item aitem = calendar->item(incidence);
297 if (mTodos.contains(aitem)) {
298 return;
299 }
300 mTodos.append(aitem);
301 mText += QLatin1StringView("<li>");
302 mText += QStringLiteral(R"(<a href="todo:%1?itemId=%2&calendarId=%4">%3</a>)")
303 .arg(incidence->uid(), incidence->customProperty("VOLATILE", "AKONADI-ID"), incidence->summary())
304 .arg(calendar->collection().id());
305
307 if (todo->hasDueDate()) {
308 mText += i18nc("to-do due date", " (Due: %1)", KCalUtils::IncidenceFormatter::dateTimeToString(todo->dtDue(), todo->allDay()));
309 }
310 mText += QLatin1StringView("</li>\n");
311 }
312}
313
314static std::optional<Akonadi::Item::Id> idFromQuery(const QUrlQuery &query, const QString &queryValue)
315{
316 const auto strVal = query.queryItemValue(queryValue);
317 if (strVal.isEmpty()) {
318 return {};
319 }
320
321 bool ok = false;
322 const auto intVal = strVal.toLongLong(&ok);
323 if (!ok) {
324 return {};
325 }
326
327 return intVal;
328}
329
330void WhatsNextView::showIncidence(const QUrl &uri)
331{
332 QUrlQuery query(uri);
333 const auto itemId = idFromQuery(query, QStringLiteral("itemId"));
334 const auto calendarId = idFromQuery(query, QStringLiteral("calendarId"));
335
336 if (!itemId || !calendarId) {
337 qCWarning(CALENDARVIEW_LOG) << "Invalid URI:" << uri;
338 return;
339 }
340
341 auto calendar = calendarForCollection(*calendarId);
342 if (!calendar) {
343 qCWarning(CALENDARVIEW_LOG) << "Calendar for collection " << *calendarId << " not present in current view";
344 return;
345 }
346
347 auto item = calendar->item(*itemId);
348 if (!item.isValid()) {
349 qCWarning(CALENDARVIEW_LOG) << "Item " << *itemId << " not found in collection " << *calendarId;
350 return;
351 }
352
354}
355
356#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)
const QList< QKeySequence > & end()
QString name(StandardShortcut id)
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
LocalTime
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 Tue Mar 26 2024 11:12:29 by doxygen 1.10.0 written by Dimitri van Heesch, © 1997-2006

KDE's Doxygen guidelines are available online.