• 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>
27 using namespace KCalCore;
28 
29 #include <KDebug>
30 #include <KLocalizedString>
31 #include <KMessageBox>
32 
33 using namespace KCalUtils;
34 
35 //@cond PRIVATE
36 struct KCalUtils::Scheduler::Private
37 {
38 public:
39  Private() : mFreeBusyCache(0)
40  {
41  }
42  FreeBusyCache *mFreeBusyCache;
43 };
44 //@endcond
45 
46 Scheduler::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 
53 Scheduler::~Scheduler()
54 {
55  delete mFormat;
56  delete d;
57 }
58 
59 void Scheduler::setFreeBusyCache(FreeBusyCache *c)
60 {
61  d->mFreeBusyCache = c;
62 }
63 
64 FreeBusyCache *Scheduler::freeBusyCache() const
65 {
66  return d->mFreeBusyCache;
67 }
68 
69 bool 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 
98 bool Scheduler::deleteTransaction(const IncidenceBase::Ptr &)
99 {
100  return true;
101 }
102 
103 bool 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 
148 bool 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 
265 bool Scheduler::acceptAdd(const IncidenceBase::Ptr &incidence,
266  ScheduleMessage::Status /* status */)
267 {
268  deleteTransaction(incidence);
269  return false;
270 }
271 
272 bool 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 
358 bool Scheduler::acceptDeclineCounter(const IncidenceBase::Ptr &incidence,
359  ScheduleMessage::Status status)
360 {
361  Q_UNUSED(status);
362  deleteTransaction(incidence);
363  return false;
364 }
365 
366 bool 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 
527 bool 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 
535 bool Scheduler::acceptCounter(const IncidenceBase::Ptr &incidence, ScheduleMessage::Status status)
536 {
537  Q_UNUSED(status);
538  deleteTransaction(incidence);
539  return false;
540 }
541 
542 bool 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::Ptr
QSharedPointer< Attendee > Ptr
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::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::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::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::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-2022 The KDE developers.
Generated on Thu Jan 20 2022 00:00:00 by doxygen 1.9.1 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