• Skip to content
  • Skip to link menu
  • KDE API Reference
  • kdepimlibs-4.14.10 API Reference
  • KDE Home
  • Contact Us
 

KCalUtils Library

  • kcalutils
scheduler.cpp
1/*
2 This file is part of the kcalutils library.
3
4 Copyright (c) 2001,2004 Cornelius Schumacher <schumacher@kde.org>
5 Copyright (C) 2004 Reinhold Kainhofer <reinhold@kainhofer.com>
6
7 This library is free software; you can redistribute it and/or
8 modify it under the terms of the GNU Library General Public
9 License as published by the Free Software Foundation; either
10 version 2 of the License, or (at your option) any later version.
11
12 This library is distributed in the hope that it will be useful,
13 but WITHOUT ANY WARRANTY; without even the implied warranty of
14 MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
15 Library General Public License for more details.
16
17 You should have received a copy of the GNU Library General Public License
18 along with this library; see the file COPYING.LIB. If not, write to
19 the Free Software Foundation, Inc., 51 Franklin Street, Fifth Floor,
20 Boston, MA 02110-1301, USA.
21*/
22#include "scheduler.h"
23#include "stringify.h"
24
25#include <kcalcore/icalformat.h>
26#include <kcalcore/freebusycache.h>
27using namespace KCalCore;
28
29#include <KDebug>
30#include <KLocalizedString>
31#include <KMessageBox>
32
33using namespace KCalUtils;
34
35//@cond PRIVATE
36struct KCalUtils::Scheduler::Private
37{
38public:
39 Private() : mFreeBusyCache(0)
40 {
41 }
42 FreeBusyCache *mFreeBusyCache;
43};
44//@endcond
45
46Scheduler::Scheduler(const Calendar::Ptr &calendar) : d(new KCalUtils::Scheduler::Private)
47{
48 mCalendar = calendar;
49 mFormat = new ICalFormat();
50 mFormat->setTimeSpec(calendar->timeSpec());
51}
52
53Scheduler::~Scheduler()
54{
55 delete mFormat;
56 delete d;
57}
58
59void Scheduler::setFreeBusyCache(FreeBusyCache *c)
60{
61 d->mFreeBusyCache = c;
62}
63
64FreeBusyCache *Scheduler::freeBusyCache() const
65{
66 return d->mFreeBusyCache;
67}
68
69bool Scheduler::acceptTransaction(const IncidenceBase::Ptr &incidence, iTIPMethod method,
70 ScheduleMessage::Status status, const QString &email)
71{
72 kDebug() << "method=" << ScheduleMessage::methodName(method); //krazy:exclude=kdebug
73
74 switch (method) {
75 case iTIPPublish:
76 return acceptPublish(incidence, status, method);
77 case iTIPRequest:
78 return acceptRequest(incidence, status, email);
79 case iTIPAdd:
80 return acceptAdd(incidence, status);
81 case iTIPCancel:
82 return acceptCancel(incidence, status, email);
83 case iTIPDeclineCounter:
84 return acceptDeclineCounter(incidence, status);
85 case iTIPReply:
86 return acceptReply(incidence, status, method);
87 case iTIPRefresh:
88 return acceptRefresh(incidence, status);
89 case iTIPCounter:
90 return acceptCounter(incidence, status);
91 default:
92 break;
93 }
94 deleteTransaction(incidence);
95 return false;
96}
97
98bool Scheduler::deleteTransaction(const IncidenceBase::Ptr &)
99{
100 return true;
101}
102
103bool Scheduler::acceptPublish(const IncidenceBase::Ptr &newIncBase, ScheduleMessage::Status status,
104 iTIPMethod method)
105{
106 if (newIncBase->type() == IncidenceBase::TypeFreeBusy) {
107 return acceptFreeBusy(newIncBase, method);
108 }
109
110 bool res = false;
111
112 kDebug() << "status=" << Stringify::scheduleMessageStatus(status); //krazy:exclude=kdebug
113
114 Incidence::Ptr newInc = newIncBase.staticCast<Incidence>() ;
115 Incidence::Ptr calInc = mCalendar->incidence(newIncBase->uid());
116 switch (status) {
117 case ScheduleMessage::Unknown:
118 case ScheduleMessage::PublishNew:
119 case ScheduleMessage::PublishUpdate:
120 if (calInc && newInc) {
121 if ((newInc->revision() > calInc->revision()) ||
122 (newInc->revision() == calInc->revision() &&
123 newInc->lastModified() > calInc->lastModified())) {
124 const QString oldUid = calInc->uid();
125
126 if (calInc->type() != newInc->type()) {
127 kError() << "assigning different incidence types";
128 } else {
129 IncidenceBase *ci = calInc.data();
130 IncidenceBase *ni = newInc.data();
131 *ci = *ni;
132 calInc->setSchedulingID(newInc->uid(), oldUid);
133 res = true;
134 }
135 }
136 }
137 break;
138 case ScheduleMessage::Obsolete:
139 res = true;
140 break;
141 default:
142 break;
143 }
144 deleteTransaction(newIncBase);
145 return res;
146}
147
148bool Scheduler::acceptRequest(const IncidenceBase::Ptr &incidence,
149 ScheduleMessage::Status status,
150 const QString &email)
151{
152 Incidence::Ptr inc = incidence.staticCast<Incidence>() ;
153 if (!inc) {
154 kWarning() << "Accept what?";
155 return false;
156 }
157 if (inc->type() == IncidenceBase::TypeFreeBusy) {
158 // reply to this request is handled in korganizer's incomingdialog
159 return true;
160 }
161
162 const Incidence::List existingIncidences = mCalendar->incidencesFromSchedulingID(inc->uid());
163 kDebug() << "status=" << Stringify::scheduleMessageStatus(status) //krazy:exclude=kdebug
164 << ": found " << existingIncidences.count()
165 << " incidences with schedulingID " << inc->schedulingID()
166 << "; uid was = " << inc->uid();
167
168 if (existingIncidences.isEmpty()) {
169 // Perfectly normal if the incidence doesn't exist. This is probably
170 // a new invitation.
171 kDebug() << "incidence not found; calendar = " << mCalendar.data()
172 << "; incidence count = " << mCalendar->incidences().count();
173 }
174 Incidence::List::ConstIterator incit = existingIncidences.begin();
175 for (; incit != existingIncidences.end() ; ++incit) {
176 Incidence::Ptr existingIncidence = *incit;
177 kDebug() << "Considering this found event ("
178 << (existingIncidence->isReadOnly() ? "readonly" : "readwrite")
179 << ") :" << mFormat->toString(existingIncidence);
180 // If it's readonly, we can't possible update it.
181 if (existingIncidence->isReadOnly()) {
182 continue;
183 }
184 if (existingIncidence->revision() <= inc->revision()) {
185 // The new incidence might be an update for the found one
186 bool isUpdate = true;
187 // Code for new invitations:
188 // If you think we could check the value of "status" to be RequestNew: we can't.
189 // It comes from a similar check inside libical, where the event is compared to
190 // other events in the calendar. But if we have another version of the event around
191 // (e.g. shared folder for a group), the status could be RequestNew, Obsolete or Updated.
192 kDebug() << "looking in " << existingIncidence->uid() << "'s attendees";
193 // This is supposed to be a new request, not an update - however we want to update
194 // the existing one to handle the "clicking more than once on the invitation" case.
195 // So check the attendee status of the attendee.
196 const Attendee::List attendees = existingIncidence->attendees();
197 Attendee::List::ConstIterator ait;
198 for (ait = attendees.begin(); ait != attendees.end(); ++ait) {
199 if ((*ait)->email() == email && (*ait)->status() == Attendee::NeedsAction) {
200 // This incidence wasn't created by me - it's probably in a shared folder
201 // and meant for someone else, ignore it.
202 kDebug() << "ignoring " << existingIncidence->uid()
203 << " since I'm still NeedsAction there";
204 isUpdate = false;
205 break;
206 }
207 }
208 if (isUpdate) {
209 if (existingIncidence->revision() == inc->revision() &&
210 existingIncidence->lastModified() > inc->lastModified()) {
211 // This isn't an update - the found incidence was modified more recently
212 kDebug() << "This isn't an update - the found incidence was modified more recently";
213 deleteTransaction(existingIncidence);
214 return false;
215 }
216 kDebug() << "replacing existing incidence " << existingIncidence->uid();
217 bool res = true;
218 const QString oldUid = existingIncidence->uid();
219 if (existingIncidence->type() != inc->type()) {
220 kError() << "assigning different incidence types";
221 res = false;
222 } else {
223 IncidenceBase *existingIncidenceBase = existingIncidence.data();
224 IncidenceBase *incBase = inc.data();
225 *existingIncidenceBase = *incBase;
226 existingIncidence->setSchedulingID(inc->uid(), oldUid);
227 }
228 deleteTransaction(incidence);
229 return res;
230 }
231 } else {
232 // This isn't an update - the found incidence has a bigger revision number
233 kDebug() << "This isn't an update - the found incidence has a bigger revision number";
234 deleteTransaction(incidence);
235 return false;
236 }
237 }
238
239 // Move the uid to be the schedulingID and make a unique UID
240 inc->setSchedulingID(inc->uid(), CalFormat::createUniqueId());
241 // notify the user in case this is an update and we didn't find the to-be-updated incidence
242 if (existingIncidences.count() == 0 && inc->revision() > 0) {
243 KMessageBox::information(
244 0,
245 i18nc("@info",
246 "<para>You accepted an invitation update, but an earlier version of the "
247 "item could not be found in your calendar.</para>"
248 "<para>This may have occurred because:<list>"
249 "<item>the organizer did not include you in the original invitation</item>"
250 "<item>you did not accept the original invitation yet</item>"
251 "<item>you deleted the original invitation from your calendar</item>"
252 "<item>you no longer have access to the calendar containing the invitation</item>"
253 "</list></para>"
254 "<para>This is not a problem, but we thought you should know.</para>"),
255 i18nc("@title", "Cannot find invitation to be updated"), QLatin1String("AcceptCantFindIncidence"));
256 }
257 kDebug() << "Storing new incidence with scheduling uid=" << inc->schedulingID()
258 << " and uid=" << inc->uid();
259 const bool result = mCalendar->addIncidence(inc);
260
261 deleteTransaction(incidence);
262 return result;
263}
264
265bool Scheduler::acceptAdd(const IncidenceBase::Ptr &incidence,
266 ScheduleMessage::Status /* status */)
267{
268 deleteTransaction(incidence);
269 return false;
270}
271
272bool Scheduler::acceptCancel(const IncidenceBase::Ptr &incidence,
273 ScheduleMessage::Status status,
274 const QString &attendee)
275{
276 Incidence::Ptr inc = incidence.staticCast<Incidence>();
277 if (!inc) {
278 return false;
279 }
280
281 if (inc->type() == IncidenceBase::TypeFreeBusy) {
282 // reply to this request is handled in korganizer's incomingdialog
283 return true;
284 }
285
286 const Incidence::List existingIncidences = mCalendar->incidencesFromSchedulingID(inc->uid());
287 kDebug() << "Scheduler::acceptCancel="
288 << Stringify::scheduleMessageStatus(status) //krazy2:exclude=kdebug
289 << ": found " << existingIncidences.count()
290 << " incidences with schedulingID " << inc->schedulingID();
291
292 bool ret = false;
293 Incidence::List::ConstIterator incit = existingIncidences.begin();
294 for (; incit != existingIncidences.end() ; ++incit) {
295 Incidence::Ptr i = *incit;
296 kDebug() << "Considering this found event ("
297 << (i->isReadOnly() ? "readonly" : "readwrite")
298 << ") :" << mFormat->toString(i);
299
300 // If it's readonly, we can't possible remove it.
301 if (i->isReadOnly()) {
302 continue;
303 }
304
305 // Code for new invitations:
306 // We cannot check the value of "status" to be RequestNew because
307 // "status" comes from a similar check inside libical, where the event
308 // is compared to other events in the calendar. But if we have another
309 // version of the event around (e.g. shared folder for a group), the
310 // status could be RequestNew, Obsolete or Updated.
311 kDebug() << "looking in " << i->uid() << "'s attendees";
312
313 // This is supposed to be a new request, not an update - however we want
314 // to update the existing one to handle the "clicking more than once
315 // on the invitation" case. So check the attendee status of the attendee.
316 bool isMine = true;
317 const Attendee::List attendees = i->attendees();
318 Attendee::List::ConstIterator ait;
319 for (ait = attendees.begin(); ait != attendees.end(); ++ait) {
320 if ((*ait)->email() == attendee &&
321 (*ait)->status() == Attendee::NeedsAction) {
322 // This incidence wasn't created by me - it's probably in a shared
323 // folder and meant for someone else, ignore it.
324 kDebug() << "ignoring " << i->uid()
325 << " since I'm still NeedsAction there";
326 isMine = false;
327 break;
328 }
329 }
330
331 if (isMine) {
332 kDebug() << "removing existing incidence " << i->uid();
333 if (i->type() == IncidenceBase::TypeEvent) {
334 Event::Ptr event = mCalendar->event(i->uid());
335 ret = (event && mCalendar->deleteEvent(event));
336 } else if (i->type() == IncidenceBase::TypeTodo) {
337 Todo::Ptr todo = mCalendar->todo(i->uid());
338 ret = (todo && mCalendar->deleteTodo(todo));
339 }
340 deleteTransaction(incidence);
341 return ret;
342 }
343 }
344
345 // in case we didn't find the to-be-removed incidence
346 if (existingIncidences.count() > 0 && inc->revision() > 0) {
347 KMessageBox::error(
348 0,
349 i18nc("@info",
350 "The event or task could not be removed from your calendar. "
351 "Maybe it has already been deleted or is not owned by you. "
352 "Or it might belong to a read-only or disabled calendar."));
353 }
354 deleteTransaction(incidence);
355 return ret;
356}
357
358bool Scheduler::acceptDeclineCounter(const IncidenceBase::Ptr &incidence,
359 ScheduleMessage::Status status)
360{
361 Q_UNUSED(status);
362 deleteTransaction(incidence);
363 return false;
364}
365
366bool Scheduler::acceptReply(const IncidenceBase::Ptr &incidence, ScheduleMessage::Status status,
367 iTIPMethod method)
368{
369 Q_UNUSED(status);
370 if (incidence->type() == IncidenceBase::TypeFreeBusy) {
371 return acceptFreeBusy(incidence, method);
372 }
373 bool ret = false;
374 Event::Ptr ev = mCalendar->event(incidence->uid());
375 Todo::Ptr to = mCalendar->todo(incidence->uid());
376
377 // try harder to find the correct incidence
378 if (!ev && !to) {
379 const Incidence::List list = mCalendar->incidences();
380 for (Incidence::List::ConstIterator it=list.constBegin(), end=list.constEnd();
381 it != end; ++it) {
382 if ((*it)->schedulingID() == incidence->uid()) {
383 ev = (*it).dynamicCast<Event>();
384 to = (*it).dynamicCast<Todo>();
385 break;
386 }
387 }
388 }
389
390 if (ev || to) {
391 //get matching attendee in calendar
392 kDebug() << "match found!";
393 Attendee::List attendeesIn = incidence->attendees();
394 Attendee::List attendeesEv;
395 Attendee::List attendeesNew;
396 if (ev) {
397 attendeesEv = ev->attendees();
398 }
399 if (to) {
400 attendeesEv = to->attendees();
401 }
402 Attendee::List::ConstIterator inIt;
403 Attendee::List::ConstIterator evIt;
404 for (inIt = attendeesIn.constBegin(); inIt != attendeesIn.constEnd(); ++inIt) {
405 Attendee::Ptr attIn = *inIt;
406 bool found = false;
407 for (evIt = attendeesEv.constBegin(); evIt != attendeesEv.constEnd(); ++evIt) {
408 Attendee::Ptr attEv = *evIt;
409 if (attIn->email().toLower() == attEv->email().toLower()) {
410 //update attendee-info
411 kDebug() << "update attendee";
412 attEv->setStatus(attIn->status());
413 attEv->setDelegate(attIn->delegate());
414 attEv->setDelegator(attIn->delegator());
415 ret = true;
416 found = true;
417 }
418 }
419 if (!found && attIn->status() != Attendee::Declined) {
420 attendeesNew.append(attIn);
421 }
422 }
423
424 bool attendeeAdded = false;
425 for (Attendee::List::ConstIterator it = attendeesNew.constBegin();
426 it != attendeesNew.constEnd(); ++it) {
427 Attendee::Ptr attNew = *it;
428 QString msg =
429 i18nc("@info", "%1 wants to attend %2 but was not invited.",
430 attNew->fullName(),
431 (ev ? ev->summary() : to->summary()));
432 if (!attNew->delegator().isEmpty()) {
433 msg = i18nc("@info", "%1 wants to attend %2 on behalf of %3.",
434 attNew->fullName(),
435 (ev ? ev->summary() : to->summary()), attNew->delegator());
436 }
437 if (KMessageBox::questionYesNo(
438 0, msg, i18nc("@title", "Uninvited attendee"),
439 KGuiItem(i18nc("@option", "Accept Attendance")),
440 KGuiItem(i18nc("@option", "Reject Attendance"))) != KMessageBox::Yes) {
441 Incidence::Ptr cancel = incidence.dynamicCast<Incidence>();
442 if (cancel) {
443 cancel->addComment(
444 i18nc("@info",
445 "The organizer rejected your attendance at this meeting."));
446 }
447 performTransaction(incidence, iTIPCancel, attNew->fullName());
448 // ### can't delete cancel here because it is aliased to incidence which
449 // is accessed in the next loop iteration (CID 4232)
450 // delete cancel;
451 continue;
452 }
453
454 Attendee::Ptr a(new Attendee(attNew->name(), attNew->email(), attNew->RSVP(),
455 attNew->status(), attNew->role(), attNew->uid()));
456
457 a->setDelegate(attNew->delegate());
458 a->setDelegator(attNew->delegator());
459 if (ev) {
460 ev->addAttendee(a);
461 } else if (to) {
462 to->addAttendee(a);
463 }
464 ret = true;
465 attendeeAdded = true;
466 }
467
468 // send update about new participants
469 if (attendeeAdded) {
470 bool sendMail = false;
471 if (ev || to) {
472 if (KMessageBox::questionYesNo(
473 0,
474 i18nc("@info",
475 "An attendee was added to the incidence. "
476 "Do you want to email the attendees an update message?"),
477 i18nc("@title", "Attendee Added"),
478 KGuiItem(i18nc("@option", "Send Messages")),
479 KGuiItem(i18nc("@option", "Do Not Send"))) == KMessageBox::Yes) {
480 sendMail = true;
481 }
482 }
483
484 if (ev) {
485 ev->setRevision(ev->revision() + 1);
486 if (sendMail) {
487 performTransaction(ev, iTIPRequest);
488 }
489 }
490 if (to) {
491 to->setRevision(to->revision() + 1);
492 if (sendMail) {
493 performTransaction(to, iTIPRequest);
494 }
495 }
496 }
497
498 if (ret) {
499 // We set at least one of the attendees, so the incidence changed
500 // Note: This should not result in a sequence number bump
501 if (ev) {
502 ev->updated();
503 } else if (to) {
504 to->updated();
505 }
506 }
507 if (to) {
508 // for VTODO a REPLY can be used to update the completion status of
509 // a to-do. see RFC2446 3.4.3
510 Todo::Ptr update = incidence.dynamicCast<Todo>();
511 Q_ASSERT(update);
512 if (update && (to->percentComplete() != update->percentComplete())) {
513 to->setPercentComplete(update->percentComplete());
514 to->updated();
515 }
516 }
517 } else {
518 kError() << "No incidence for scheduling.";
519 }
520
521 if (ret) {
522 deleteTransaction(incidence);
523 }
524 return ret;
525}
526
527bool Scheduler::acceptRefresh(const IncidenceBase::Ptr &incidence, ScheduleMessage::Status status)
528{
529 Q_UNUSED(status);
530 // handled in korganizer's IncomingDialog
531 deleteTransaction(incidence);
532 return false;
533}
534
535bool Scheduler::acceptCounter(const IncidenceBase::Ptr &incidence, ScheduleMessage::Status status)
536{
537 Q_UNUSED(status);
538 deleteTransaction(incidence);
539 return false;
540}
541
542bool Scheduler::acceptFreeBusy(const IncidenceBase::Ptr &incidence, iTIPMethod method)
543{
544 if (!d->mFreeBusyCache) {
545 kError() << "Scheduler: no FreeBusyCache.";
546 return false;
547 }
548
549 FreeBusy::Ptr freebusy = incidence.staticCast<FreeBusy>();
550
551 kDebug() << "freeBusyDirName:" << freeBusyDir();
552
553 Person::Ptr from;
554 if (method == iTIPPublish) {
555 from = freebusy->organizer();
556 }
557 if ((method == iTIPReply) && (freebusy->attendeeCount() == 1)) {
558 Attendee::Ptr attendee = freebusy->attendees().first();
559 from->setName(attendee->name());
560 from->setEmail(attendee->email());
561 }
562
563 if (!d->mFreeBusyCache->saveFreeBusy(freebusy, from)) {
564 return false;
565 }
566
567 deleteTransaction(incidence);
568 return true;
569}
KCalCore::Attendee
KCalCore::Attendee::List
QVector< Ptr > List
KCalCore::Attendee::NeedsAction
NeedsAction
KCalCore::Attendee::Declined
Declined
KCalCore::Attendee::Ptr
QSharedPointer< Attendee > Ptr
KCalCore::CalFormat::createUniqueId
static QString createUniqueId()
KCalCore::Calendar::Ptr
QSharedPointer< Calendar > Ptr
KCalCore::Event
KCalCore::Event::Ptr
QSharedPointer< Event > Ptr
KCalCore::FreeBusyCache
KCalCore::FreeBusy
KCalCore::FreeBusy::Ptr
QSharedPointer< FreeBusy > Ptr
KCalCore::ICalFormat
KCalCore::ICalFormat::setTimeSpec
void setTimeSpec(const KDateTime::Spec &timeSpec)
KCalCore::ICalFormat::toString
QString toString(const Calendar::Ptr &calendar, const QString &notebook=QString(), bool deleted=false)
KCalCore::IncidenceBase
KCalCore::IncidenceBase::TypeFreeBusy
TypeFreeBusy
KCalCore::IncidenceBase::TypeEvent
TypeEvent
KCalCore::IncidenceBase::TypeTodo
TypeTodo
KCalCore::IncidenceBase::addComment
void addComment(const QString &comment)
KCalCore::IncidenceBase::Ptr
QSharedPointer< IncidenceBase > Ptr
KCalCore::IncidenceBase::attendees
Attendee::List attendees() const
KCalCore::Incidence
KCalCore::Incidence::List
QVector< Ptr > List
KCalCore::Incidence::Ptr
QSharedPointer< Incidence > Ptr
KCalCore::Person::Ptr
QSharedPointer< Person > Ptr
KCalCore::ScheduleMessage::Status
Status
KCalCore::ScheduleMessage::PublishNew
PublishNew
KCalCore::ScheduleMessage::PublishUpdate
PublishUpdate
KCalCore::ScheduleMessage::Unknown
Unknown
KCalCore::ScheduleMessage::Obsolete
Obsolete
KCalCore::ScheduleMessage::methodName
static QString methodName(iTIPMethod method)
KCalCore::Todo
KCalCore::Todo::Ptr
QSharedPointer< Todo > Ptr
KCalUtils::Scheduler
This class provides an encapsulation of iTIP transactions (RFC 2446).
Definition scheduler.h:45
KCalUtils::Scheduler::setFreeBusyCache
void setFreeBusyCache(KCalCore::FreeBusyCache *)
Sets the free/busy cache used to store free/busy information.
Definition scheduler.cpp:59
KCalUtils::Scheduler::freeBusyCache
KCalCore::FreeBusyCache * freeBusyCache() const
Returns the free/busy cache.
Definition scheduler.cpp:64
KCalUtils::Scheduler::freeBusyDir
virtual QString freeBusyDir()=0
Returns the directory where the free-busy information is stored.
KCalUtils::Scheduler::performTransaction
virtual bool performTransaction(const KCalCore::IncidenceBase::Ptr &incidence, KCalCore::iTIPMethod method)=0
Performs iTIP transaction on incidence.
KCalUtils::Scheduler::Scheduler
Scheduler(const KCalCore::Calendar::Ptr &calendar)
Creates a scheduler for calendar specified as argument.
Definition scheduler.cpp:46
KCalUtils::Scheduler::acceptTransaction
bool acceptTransaction(const KCalCore::IncidenceBase::Ptr &incidence, KCalCore::iTIPMethod method, KCalCore::ScheduleMessage::Status status, const QString &email=QString())
Accepts the transaction.
Definition scheduler.cpp:69
freebusycache.h
icalformat.h
KCalCore
KCalCore::iTIPMethod
iTIPMethod
KCalCore::iTIPRefresh
iTIPRefresh
KCalCore::iTIPCounter
iTIPCounter
KCalCore::iTIPDeclineCounter
iTIPDeclineCounter
KCalCore::iTIPCancel
iTIPCancel
KCalCore::iTIPPublish
iTIPPublish
KCalCore::iTIPAdd
iTIPAdd
KCalCore::iTIPReply
iTIPReply
KCalCore::iTIPRequest
iTIPRequest
stringify.h
This file is part of the API for handling calendar data and provides static functions for formatting ...
This file is part of the KDE documentation.
Documentation copyright © 1996-2024 The KDE developers.
Generated on Wed Jan 24 2024 00:00:00 by doxygen 1.10.0 written by Dimitri van Heesch, © 1997-2006

KDE's Doxygen guidelines are available online.

KCalUtils Library

Skip menu "KCalUtils Library"
  • Main Page
  • Namespace List
  • Namespace Members
  • Alphabetical List
  • Class List
  • Class Members
  • File List
  • Related Pages

kdepimlibs-4.14.10 API Reference

Skip menu "kdepimlibs-4.14.10 API Reference"
  • akonadi
  •   contact
  •   kmime
  •   socialutils
  • kabc
  • kalarmcal
  • kblog
  • kcal
  • kcalcore
  • kcalutils
  • kholidays
  • kimap
  • kioslave
  •   imap4
  •   mbox
  •   nntp
  • kldap
  • kmbox
  • kmime
  • kontactinterface
  • kpimidentities
  • kpimtextedit
  • kpimutils
  • kresources
  • ktnef
  • kxmlrpcclient
  • mailtransport
  • microblog
  • qgpgme
  • syndication
  •   atom
  •   rdf
  •   rss2
Report problems with this website to our bug tracking system.
Contact the specific authors with questions and comments about the page contents.

KDE® and the K Desktop Environment® logo are registered trademarks of KDE e.V. | Legal