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

KCalCore Library

  • kcalcore
calendar.cpp
Go to the documentation of this file.
1 /*
2  This file is part of the kcalcore library.
3 
4  Copyright (c) 1998 Preston Brown <pbrown@kde.org>
5  Copyright (c) 2000-2004 Cornelius Schumacher <schumacher@kde.org>
6  Copyright (C) 2003-2004 Reinhold Kainhofer <reinhold@kainhofer.com>
7  Copyright (c) 2006 David Jarvie <software@astrojar.org.uk>
8 
9  This library is free software; you can redistribute it and/or
10  modify it under the terms of the GNU Library General Public
11  License as published by the Free Software Foundation; either
12  version 2 of the License, or (at your option) any later version.
13 
14  This library is distributed in the hope that it will be useful,
15  but WITHOUT ANY WARRANTY; without even the implied warranty of
16  MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
17  Library General Public License for more details.
18 
19  You should have received a copy of the GNU Library General Public License
20  along with this library; see the file COPYING.LIB. If not, write to
21  the Free Software Foundation, Inc., 51 Franklin Street, Fifth Floor,
22  Boston, MA 02110-1301, USA.
23 */
37 #include "calendar.h"
38 #include "calfilter.h"
39 #include "icaltimezones.h"
40 #include "sorting.h"
41 #include "visitor.h"
42 
43 #include <KDebug>
44 
45 extern "C" {
46  #include <icaltimezone.h>
47 }
48 
49 #include <algorithm> // for std::remove()
50 
51 using namespace KCalCore;
52 
57 //@cond PRIVATE
58 class KCalCore::Calendar::Private
59 {
60  public:
61  Private()
62  : mTimeZones( new ICalTimeZones ),
63  mModified( false ),
64  mNewObserver( false ),
65  mObserversEnabled( true ),
66  mDefaultFilter( new CalFilter ),
67  batchAddingInProgress( false )
68  {
69  // Setup default filter, which does nothing
70  mFilter = mDefaultFilter;
71  mFilter->setEnabled( false );
72 
73  mOwner = Person::Ptr( new Person() );
74  mOwner->setName( "Unknown Name" );
75  mOwner->setEmail( "unknown@nowhere" );
76  }
77 
78  ~Private()
79  {
80  delete mTimeZones;
81  mTimeZones = 0;
82  if ( mFilter != mDefaultFilter ) {
83  delete mFilter;
84  }
85  delete mDefaultFilter;
86  }
87  KDateTime::Spec timeZoneIdSpec( const QString &timeZoneId, bool view );
88 
89  QString mProductId;
90  Person::Ptr mOwner;
91  ICalTimeZones *mTimeZones; // collection of time zones used in this calendar
92  ICalTimeZone mBuiltInTimeZone; // cached time zone lookup
93  ICalTimeZone mBuiltInViewTimeZone; // cached viewing time zone lookup
94  KDateTime::Spec mTimeSpec;
95  mutable KDateTime::Spec mViewTimeSpec;
96  bool mModified;
97  bool mNewObserver;
98  bool mObserversEnabled;
99  QList<CalendarObserver*> mObservers;
100 
101  CalFilter *mDefaultFilter;
102  CalFilter *mFilter;
103 
104  // These lists are used to put together related To-dos
105  QMultiHash<QString, Incidence::Ptr> mOrphans;
106  QMultiHash<QString, Incidence::Ptr> mOrphanUids;
107 
108  // Lists for associating incidences to notebooks
109  QMultiHash<QString, Incidence::Ptr >mNotebookIncidences;
110  QHash<QString, QString>mUidToNotebook;
111  QHash<QString, bool>mNotebooks; // name to visibility
112  QHash<Incidence::Ptr, bool>mIncidenceVisibility; // incidence -> visibility
113  QString mDefaultNotebook; // uid of default notebook
114  QMap<QString, Incidence::List > mIncidenceRelations;
115  bool batchAddingInProgress;
116 
117 };
118 
122 template <typename K, typename V>
123 QVector<V> values( const QMultiHash<K,V> &c )
124 {
125  QVector<V> v;
126  v.reserve( c.size() );
127  for ( typename QMultiHash<K,V>::const_iterator it = c.begin(), end = c.end(); it != end; ++it ) {
128  v.push_back( it.value() );
129  }
130  return v;
131 }
132 
133 template <typename K, typename V>
134 QVector<V> values( const QMultiHash<K,V> &c, const K &x )
135 {
136  QVector<V> v;
137  typename QMultiHash<K,V>::const_iterator it = c.find( x );
138  while ( it != c.end() && it.key() == x ) {
139  v.push_back( it.value() );
140  ++it;
141  }
142  return v;
143 }
144 
149 template<class T>
150 class AddVisitor : public Visitor
151 {
152  public:
153  AddVisitor( T *r ) : mResource( r ) {}
154 
155  bool visit( Event::Ptr e )
156  {
157  return mResource->addEvent( e );
158  }
159  bool visit( Todo::Ptr t )
160  {
161  return mResource->addTodo( t );
162  }
163  bool visit( Journal::Ptr j )
164  {
165  return mResource->addJournal( j );
166  }
167  bool visit( FreeBusy::Ptr )
168  {
169  return false;
170  }
171 
172  private:
173  T *mResource;
174 };
175 
181 template<class T>
182 class DeleteVisitor : public Visitor
183 {
184  public:
185  DeleteVisitor( T *r ) : mResource( r ) {}
186 
187  bool visit( Event::Ptr e )
188  {
189  mResource->deleteEvent( e );
190  return true;
191  }
192  bool visit( Todo::Ptr t )
193  {
194  mResource->deleteTodo( t );
195  return true;
196  }
197  bool visit( Journal::Ptr j )
198  {
199  mResource->deleteJournal( j );
200  return true;
201  }
202  bool visit( FreeBusy::Ptr )
203  {
204  return false;
205  }
206 
207  private:
208  T *mResource;
209 };
210 //@endcond
211 
212 Calendar::Calendar( const KDateTime::Spec &timeSpec )
213  : d( new KCalCore::Calendar::Private )
214 {
215  d->mTimeSpec = timeSpec;
216  d->mViewTimeSpec = timeSpec;
217 }
218 
219 Calendar::Calendar( const QString &timeZoneId )
220  : d( new KCalCore::Calendar::Private )
221 {
222  setTimeZoneId( timeZoneId );
223 }
224 
225 Calendar::~Calendar()
226 {
227  delete d;
228 }
229 
230 Person::Ptr Calendar::owner() const
231 {
232  return d->mOwner;
233 }
234 
235 void Calendar::setOwner( const Person::Ptr &owner )
236 {
237  Q_ASSERT( owner );
238  d->mOwner = owner;
239  setModified( true );
240 }
241 
242 void Calendar::setTimeSpec( const KDateTime::Spec &timeSpec )
243 {
244  d->mTimeSpec = timeSpec;
245  d->mBuiltInTimeZone = ICalTimeZone();
246  setViewTimeSpec( timeSpec );
247 
248  doSetTimeSpec( d->mTimeSpec );
249 }
250 
251 KDateTime::Spec Calendar::timeSpec() const
252 {
253  return d->mTimeSpec;
254 }
255 
256 void Calendar::setTimeZoneId( const QString &timeZoneId )
257 {
258  d->mTimeSpec = d->timeZoneIdSpec( timeZoneId, false );
259  d->mViewTimeSpec = d->mTimeSpec;
260  d->mBuiltInViewTimeZone = d->mBuiltInTimeZone;
261 
262  doSetTimeSpec( d->mTimeSpec );
263 }
264 
265 //@cond PRIVATE
266 KDateTime::Spec Calendar::Private::timeZoneIdSpec( const QString &timeZoneId,
267  bool view )
268 {
269  if ( view ) {
270  mBuiltInViewTimeZone = ICalTimeZone();
271  } else {
272  mBuiltInTimeZone = ICalTimeZone();
273  }
274  if ( timeZoneId == QLatin1String( "UTC" ) ) {
275  return KDateTime::UTC;
276  }
277  ICalTimeZone tz = mTimeZones->zone( timeZoneId );
278  if ( !tz.isValid() ) {
279  ICalTimeZoneSource tzsrc;
280  tz = tzsrc.parse( icaltimezone_get_builtin_timezone( timeZoneId.toLatin1() ) );
281  if ( view ) {
282  mBuiltInViewTimeZone = tz;
283  } else {
284  mBuiltInTimeZone = tz;
285  }
286  }
287  if ( tz.isValid() ) {
288  return tz;
289  } else {
290  return KDateTime::ClockTime;
291  }
292 }
293 //@endcond
294 
295 QString Calendar::timeZoneId() const
296 {
297  KTimeZone tz = d->mTimeSpec.timeZone();
298  return tz.isValid() ? tz.name() : QString();
299 }
300 
301 void Calendar::setViewTimeSpec( const KDateTime::Spec &timeSpec ) const
302 {
303  d->mViewTimeSpec = timeSpec;
304  d->mBuiltInViewTimeZone = ICalTimeZone();
305 }
306 
307 void Calendar::setViewTimeZoneId( const QString &timeZoneId ) const
308 {
309  d->mViewTimeSpec = d->timeZoneIdSpec( timeZoneId, true );
310 }
311 
312 KDateTime::Spec Calendar::viewTimeSpec() const
313 {
314  return d->mViewTimeSpec;
315 }
316 
317 QString Calendar::viewTimeZoneId() const
318 {
319  KTimeZone tz = d->mViewTimeSpec.timeZone();
320  return tz.isValid() ? tz.name() : QString();
321 }
322 
323 ICalTimeZones *Calendar::timeZones() const
324 {
325  return d->mTimeZones;
326 }
327 
328 void Calendar::setTimeZones( ICalTimeZones *zones )
329 {
330  if ( !zones ) {
331  return;
332  }
333 
334  if ( d->mTimeZones && ( d->mTimeZones != zones ) ) {
335  delete d->mTimeZones;
336  d->mTimeZones = 0;
337  }
338  d->mTimeZones = zones;
339 }
340 
341 void Calendar::shiftTimes( const KDateTime::Spec &oldSpec, const KDateTime::Spec &newSpec )
342 {
343  setTimeSpec( newSpec );
344 
345  int i, end;
346  Event::List ev = events();
347  for ( i = 0, end = ev.count(); i < end; ++i ) {
348  ev[i]->shiftTimes( oldSpec, newSpec );
349  }
350 
351  Todo::List to = todos();
352  for ( i = 0, end = to.count(); i < end; ++i ) {
353  to[i]->shiftTimes( oldSpec, newSpec );
354  }
355 
356  Journal::List jo = journals();
357  for ( i = 0, end = jo.count(); i < end; ++i ) {
358  jo[i]->shiftTimes( oldSpec, newSpec );
359  }
360 }
361 
362 void Calendar::setFilter( CalFilter *filter )
363 {
364  if ( filter ) {
365  d->mFilter = filter;
366  } else {
367  d->mFilter = d->mDefaultFilter;
368  }
369 }
370 
371 CalFilter *Calendar::filter() const
372 {
373  return d->mFilter;
374 }
375 
376 QStringList Calendar::categories() const
377 {
378  Incidence::List rawInc( rawIncidences() );
379  QStringList cats, thisCats;
380  // @TODO: For now just iterate over all incidences. In the future,
381  // the list of categories should be built when reading the file.
382  for ( Incidence::List::ConstIterator i = rawInc.constBegin();
383  i != rawInc.constEnd(); ++i ) {
384  thisCats = ( *i )->categories();
385  for ( QStringList::ConstIterator si = thisCats.constBegin();
386  si != thisCats.constEnd(); ++si ) {
387  if ( !cats.contains( *si ) ) {
388  cats.append( *si );
389  }
390  }
391  }
392  return cats;
393 }
394 
395 Incidence::List Calendar::incidences( const QDate &date ) const
396 {
397  return mergeIncidenceList( events( date ), todos( date ), journals( date ) );
398 }
399 
400 Incidence::List Calendar::incidences() const
401 {
402  return mergeIncidenceList( events(), todos(), journals() );
403 }
404 
405 Incidence::List Calendar::rawIncidences() const
406 {
407  return mergeIncidenceList( rawEvents(), rawTodos(), rawJournals() );
408 }
409 
410 Incidence::List Calendar::instances( const Incidence::Ptr &incidence ) const
411 {
412  if ( incidence ) {
413  Event::List elist;
414  Todo::List tlist;
415  Journal::List jlist;
416 
417  if ( incidence->type() == Incidence::TypeEvent ) {
418  elist = eventInstances( incidence );
419  } else if ( incidence->type() == Incidence::TypeTodo ) {
420  tlist = todoInstances( incidence );
421  } else if ( incidence->type() == Incidence::TypeJournal ) {
422  jlist = journalInstances( incidence );
423  }
424  return mergeIncidenceList( elist, tlist, jlist );
425  } else {
426  return Incidence::List();
427  }
428 }
429 
430 Incidence::List Calendar::duplicates( const Incidence::Ptr &incidence )
431 {
432  if ( incidence ) {
433  Incidence::List list;
434  Incidence::List vals = values( d->mNotebookIncidences );
435  Incidence::List::const_iterator it;
436  for ( it = vals.constBegin(); it != vals.constEnd(); ++it ) {
437  if ( ( ( incidence->dtStart() == ( *it )->dtStart() ) ||
438  ( !incidence->dtStart().isValid() && !( *it )->dtStart().isValid() ) ) &&
439  ( incidence->summary() == ( *it )->summary() ) ) {
440  list.append( *it );
441  }
442  }
443  return list;
444  } else {
445  return Incidence::List();
446  }
447 }
448 
449 bool Calendar::addNotebook( const QString &notebook, bool isVisible )
450 {
451  if ( d->mNotebooks.contains( notebook ) ) {
452  return false;
453  } else {
454  d->mNotebooks.insert( notebook, isVisible );
455  return true;
456  }
457 }
458 
459 bool Calendar::updateNotebook( const QString &notebook, bool isVisible )
460 {
461  if ( !d->mNotebooks.contains( notebook ) ) {
462  return false;
463  } else {
464  d->mNotebooks.insert( notebook, isVisible );
465  return true;
466  }
467 }
468 
469 bool Calendar::deleteNotebook( const QString &notebook )
470 {
471  if ( !d->mNotebooks.contains( notebook ) ) {
472  return false;
473  } else {
474  return d->mNotebooks.remove( notebook );
475  }
476 }
477 
478 bool Calendar::setDefaultNotebook( const QString &notebook )
479 {
480  if ( !d->mNotebooks.contains( notebook ) ) {
481  return false;
482  } else {
483  d->mDefaultNotebook = notebook;
484  return true;
485  }
486 }
487 
488 QString Calendar::defaultNotebook() const
489 {
490  return d->mDefaultNotebook;
491 }
492 
493 bool Calendar::hasValidNotebook( const QString &notebook ) const
494 {
495  return d->mNotebooks.contains( notebook );
496 }
497 
498 bool Calendar::isVisible( const Incidence::Ptr &incidence ) const
499 {
500  if ( d->mIncidenceVisibility.contains( incidence ) ) {
501  return d->mIncidenceVisibility[incidence];
502  }
503  const QString nuid = notebook( incidence );
504  bool rv;
505  if ( d->mNotebooks.contains( nuid ) ) {
506  rv = d->mNotebooks.value( nuid );
507  } else {
508  // NOTE returns true also for nonexisting notebooks for compatibility
509  rv = true;
510  }
511  d->mIncidenceVisibility[incidence] = rv;
512  return rv;
513 }
514 
515 void Calendar::clearNotebookAssociations()
516 {
517  d->mNotebookIncidences.clear();
518  d->mUidToNotebook.clear();
519  d->mIncidenceVisibility.clear();
520 }
521 
522 bool Calendar::setNotebook( const Incidence::Ptr &inc, const QString &notebook )
523 {
524  if ( !inc ) {
525  return false;
526  }
527 
528  if ( !notebook.isEmpty() &&
529  !incidence( inc->uid(), inc->recurrenceId() ) ) {
530  kWarning() << "cannot set notebook until incidence has been added";
531  return false;
532  }
533 
534  if ( d->mUidToNotebook.contains( inc->uid() ) ) {
535  QString old = d->mUidToNotebook.value( inc->uid() );
536  if ( !old.isEmpty() && notebook != old ) {
537  if ( inc->hasRecurrenceId() ) {
538  kWarning() << "cannot set notebook for child incidences";
539  return false;
540  }
541  // Move all possible children also.
542  Incidence::List list = instances( inc );
543  Incidence::List::Iterator it;
544  for ( it = list.begin(); it != list.end(); ++it ) {
545  d->mNotebookIncidences.remove( old, *it );
546  d->mNotebookIncidences.insert( notebook, *it );
547  }
548  notifyIncidenceChanged( inc ); // for removing from old notebook
549  // don not remove from mUidToNotebook to keep deleted incidences
550  d->mNotebookIncidences.remove( old, inc );
551  }
552  }
553  if ( !notebook.isEmpty() ) {
554  d->mUidToNotebook.insert( inc->uid(), notebook );
555  d->mNotebookIncidences.insert( notebook, inc );
556  kDebug() << "setting notebook" << notebook << "for" << inc->uid();
557  notifyIncidenceChanged( inc ); // for inserting into new notebook
558  }
559 
560  return true;
561 }
562 
563 QString Calendar::notebook( const Incidence::Ptr &incidence ) const
564 {
565  if ( incidence ) {
566  return d->mUidToNotebook.value( incidence->uid() );
567  } else {
568  return QString();
569  }
570 }
571 
572 QString Calendar::notebook( const QString &uid ) const
573 {
574  return d->mUidToNotebook.value( uid );
575 }
576 
577 QStringList Calendar::notebooks() const
578 {
579  return d->mNotebookIncidences.uniqueKeys();
580 }
581 
582 Incidence::List Calendar::incidences( const QString &notebook ) const
583 {
584  if ( notebook.isEmpty() ) {
585  return values( d->mNotebookIncidences );
586  } else {
587  return values( d->mNotebookIncidences, notebook );
588  }
589 }
590 
592 Event::List Calendar::sortEvents( const Event::List &eventList,
593  EventSortField sortField,
594  SortDirection sortDirection )
595 {
596 
597  if ( eventList.isEmpty() ) {
598  return Event::List();
599  }
600 
601  Event::List eventListSorted;
602 
603  // Notice we alphabetically presort Summaries first.
604  // We do this so comparison "ties" stay in a nice order.
605  eventListSorted = eventList;
606  switch ( sortField ) {
607  case EventSortUnsorted:
608  break;
609 
610  case EventSortStartDate:
611  if ( sortDirection == SortDirectionAscending ) {
612  qSort( eventListSorted.begin(), eventListSorted.end(), Events::startDateLessThan );
613  } else {
614  qSort( eventListSorted.begin(), eventListSorted.end(), Events::startDateMoreThan );
615  }
616  break;
617 
618  case EventSortEndDate:
619  if ( sortDirection == SortDirectionAscending ) {
620  qSort( eventListSorted.begin(), eventListSorted.end(), Events::endDateLessThan );
621  } else {
622  qSort( eventListSorted.begin(), eventListSorted.end(), Events::endDateMoreThan );
623  }
624  break;
625 
626  case EventSortSummary:
627  if ( sortDirection == SortDirectionAscending ) {
628  qSort( eventListSorted.begin(), eventListSorted.end(), Events::summaryLessThan );
629  } else {
630  qSort( eventListSorted.begin(), eventListSorted.end(), Events::summaryMoreThan );
631  }
632  break;
633  }
634 
635  return eventListSorted;
636 
637 }
638 
639 Event::List Calendar::events( const QDate &date,
640  const KDateTime::Spec &timeSpec,
641  EventSortField sortField,
642  SortDirection sortDirection ) const
643 {
644  Event::List el = rawEventsForDate( date, timeSpec, sortField, sortDirection );
645  d->mFilter->apply( &el );
646  return el;
647 }
648 
649 Event::List Calendar::events( const KDateTime &dt ) const
650 {
651  Event::List el = rawEventsForDate( dt );
652  d->mFilter->apply( &el );
653  return el;
654 }
655 
656 Event::List Calendar::events( const QDate &start, const QDate &end,
657  const KDateTime::Spec &timeSpec,
658  bool inclusive ) const
659 {
660  Event::List el = rawEvents( start, end, timeSpec, inclusive );
661  d->mFilter->apply( &el );
662  return el;
663 }
664 
665 Event::List Calendar::events( EventSortField sortField,
666  SortDirection sortDirection ) const
667 {
668  Event::List el = rawEvents( sortField, sortDirection );
669  d->mFilter->apply( &el );
670  return el;
671 }
672 
673 bool Calendar::addIncidence( const Incidence::Ptr &incidence )
674 {
675  if ( !incidence ) {
676  return false;
677  }
678 
679  AddVisitor<Calendar> v( this );
680  return incidence->accept( v, incidence );
681 }
682 
683 bool Calendar::deleteIncidence( const Incidence::Ptr &incidence )
684 {
685  if ( !incidence ) {
686  return false;
687  }
688 
689  if ( beginChange( incidence ) ) {
690  DeleteVisitor<Calendar> v( this );
691  const bool result = incidence->accept( v, incidence );
692  endChange( incidence );
693  return result;
694  } else {
695  return false;
696  }
697 }
698 
699 // Dissociate a single occurrence or all future occurrences from a recurring
700 // sequence. The new incidence is returned, but not automatically inserted
701 // into the calendar, which is left to the calling application.
702 Incidence::Ptr Calendar::dissociateOccurrence( const Incidence::Ptr &incidence,
703  const QDate &date,
704  const KDateTime::Spec &spec,
705  bool single )
706 {
707  if ( !incidence || !incidence->recurs() ) {
708  return Incidence::Ptr();
709  }
710 
711  Incidence::Ptr newInc( incidence->clone() );
712  newInc->recreate();
713  // Do not call setRelatedTo() when dissociating recurring to-dos, otherwise the new to-do
714  // will appear as a child. Originally, we planned to set a relation with reltype SIBLING
715  // when dissociating to-dos, but currently kcalcore only supports reltype PARENT.
716  // We can uncomment the following line when we support the PARENT reltype.
717  //newInc->setRelatedTo( incidence );
718  Recurrence *recur = newInc->recurrence();
719  if ( single ) {
720  recur->clear();
721  } else {
722  // Adjust the recurrence for the future incidences. In particular adjust
723  // the "end after n occurrences" rules! "No end date" and "end by ..."
724  // don't need to be modified.
725  int duration = recur->duration();
726  if ( duration > 0 ) {
727  int doneduration = recur->durationTo( date.addDays( -1 ) );
728  if ( doneduration >= duration ) {
729  kDebug() << "The dissociated event already occurred more often"
730  << "than it was supposed to ever occur. ERROR!";
731  recur->clear();
732  } else {
733  recur->setDuration( duration - doneduration );
734  }
735  }
736  }
737  // Adjust the date of the incidence
738  if ( incidence->type() == Incidence::TypeEvent ) {
739  Event::Ptr ev = newInc.staticCast<Event>();
740  KDateTime start( ev->dtStart() );
741  int daysTo = start.toTimeSpec( spec ).date().daysTo( date );
742  ev->setDtStart( start.addDays( daysTo ) );
743  ev->setDtEnd( ev->dtEnd().addDays( daysTo ) );
744  } else if ( incidence->type() == Incidence::TypeTodo ) {
745  Todo::Ptr td = newInc.staticCast<Todo>();
746  bool haveOffset = false;
747  int daysTo = 0;
748  if ( td->hasDueDate() ) {
749  KDateTime due( td->dtDue() );
750  daysTo = due.toTimeSpec( spec ).date().daysTo( date );
751  td->setDtDue( due.addDays( daysTo ), true );
752  haveOffset = true;
753  }
754  if ( td->hasStartDate() ) {
755  KDateTime start( td->dtStart() );
756  if ( !haveOffset ) {
757  daysTo = start.toTimeSpec( spec ).date().daysTo( date );
758  }
759  td->setDtStart( start.addDays( daysTo ) );
760  haveOffset = true;
761  }
762  }
763  recur = incidence->recurrence();
764  if ( recur ) {
765  if ( single ) {
766  recur->addExDate( date );
767  } else {
768  // Make sure the recurrence of the past events ends
769  // at the corresponding day
770  recur->setEndDate( date.addDays(-1) );
771  }
772  }
773  return newInc;
774 }
775 
776 Incidence::Ptr Calendar::incidence( const QString &uid,
777  const KDateTime &recurrenceId ) const
778 {
779  Incidence::Ptr i = event( uid, recurrenceId );
780  if ( i ) {
781  return i;
782  }
783 
784  i = todo( uid, recurrenceId );
785  if ( i ) {
786  return i;
787  }
788 
789  i = journal( uid, recurrenceId );
790  return i;
791 }
792 
793 Incidence::Ptr Calendar::deleted( const QString &uid, const KDateTime &recurrenceId ) const
794 {
795  Incidence::Ptr i = deletedEvent( uid, recurrenceId );
796  if ( i ) {
797  return i;
798  }
799 
800  i = deletedTodo( uid, recurrenceId );
801  if ( i ) {
802  return i;
803  }
804 
805  i = deletedJournal( uid, recurrenceId );
806  return i;
807 }
808 
809 Incidence::List Calendar::incidencesFromSchedulingID( const QString &sid ) const
810 {
811  Incidence::List result;
812  const Incidence::List incidences = rawIncidences();
813  Incidence::List::const_iterator it = incidences.begin();
814  for ( ; it != incidences.end(); ++it ) {
815  if ( (*it)->schedulingID() == sid ) {
816  result.append( *it );
817  }
818  }
819  return result;
820 }
821 
822 Incidence::Ptr Calendar::incidenceFromSchedulingID( const QString &uid ) const
823 {
824  const Incidence::List incidences = rawIncidences();
825  Incidence::List::const_iterator it = incidences.begin();
826  for ( ; it != incidences.end(); ++it ) {
827  if ( (*it)->schedulingID() == uid ) {
828  // Touchdown, and the crowd goes wild
829  return *it;
830  }
831  }
832  // Not found
833  return Incidence::Ptr();
834 }
835 
837 Todo::List Calendar::sortTodos( const Todo::List &todoList,
838  TodoSortField sortField,
839  SortDirection sortDirection )
840 {
841  if ( todoList.isEmpty() ) {
842  return Todo::List();
843  }
844 
845  Todo::List todoListSorted;
846 
847  // Notice we alphabetically presort Summaries first.
848  // We do this so comparison "ties" stay in a nice order.
849 
850  // Note that To-dos may not have Start DateTimes nor due DateTimes.
851 
852  todoListSorted = todoList;
853  switch ( sortField ) {
854  case TodoSortUnsorted:
855  break;
856 
857  case TodoSortStartDate:
858  if ( sortDirection == SortDirectionAscending ) {
859  qSort( todoListSorted.begin(), todoListSorted.end(), Todos::startDateLessThan );
860  } else {
861  qSort( todoListSorted.begin(), todoListSorted.end(), Todos::startDateMoreThan );
862  }
863  break;
864 
865  case TodoSortDueDate:
866  if ( sortDirection == SortDirectionAscending ) {
867  qSort( todoListSorted.begin(), todoListSorted.end(), Todos::dueDateLessThan );
868  } else {
869  qSort( todoListSorted.begin(), todoListSorted.end(), Todos::dueDateMoreThan );
870  }
871  break;
872 
873  case TodoSortPriority:
874  if ( sortDirection == SortDirectionAscending ) {
875  qSort( todoListSorted.begin(), todoListSorted.end(), Todos::priorityLessThan );
876  } else {
877  qSort( todoListSorted.begin(), todoListSorted.end(), Todos::priorityMoreThan );
878  }
879  break;
880 
881  case TodoSortPercentComplete:
882  if ( sortDirection == SortDirectionAscending ) {
883  qSort( todoListSorted.begin(), todoListSorted.end(), Todos::percentLessThan );
884  } else {
885  qSort( todoListSorted.begin(), todoListSorted.end(), Todos::percentMoreThan );
886  }
887  break;
888 
889  case TodoSortSummary:
890  if ( sortDirection == SortDirectionAscending ) {
891  qSort( todoListSorted.begin(), todoListSorted.end(), Todos::summaryLessThan );
892  } else {
893  qSort( todoListSorted.begin(), todoListSorted.end(), Todos::summaryMoreThan );
894  }
895  break;
896 
897  case TodoSortCreated:
898  if ( sortDirection == SortDirectionAscending ) {
899  qSort( todoListSorted.begin(), todoListSorted.end(), Todos::createdLessThan );
900  } else {
901  qSort( todoListSorted.begin(), todoListSorted.end(), Todos::createdMoreThan );
902  }
903  break;
904  }
905 
906  return todoListSorted;
907 }
908 
909 Todo::List Calendar::todos( TodoSortField sortField,
910  SortDirection sortDirection ) const
911 {
912  Todo::List tl = rawTodos( sortField, sortDirection );
913  d->mFilter->apply( &tl );
914  return tl;
915 }
916 
917 Todo::List Calendar::todos( const QDate &date ) const
918 {
919  Todo::List el = rawTodosForDate( date );
920  d->mFilter->apply( &el );
921  return el;
922 }
923 
924 Todo::List Calendar::todos( const QDate &start, const QDate &end,
925  const KDateTime::Spec &timespec, bool inclusive ) const
926 {
927  Todo::List tl = rawTodos( start, end, timespec, inclusive );
928  d->mFilter->apply( &tl );
929  return tl;
930 }
931 
933 Journal::List Calendar::sortJournals( const Journal::List &journalList,
934  JournalSortField sortField,
935  SortDirection sortDirection )
936 {
937  if ( journalList.isEmpty() ) {
938  return Journal::List();
939  }
940 
941  Journal::List journalListSorted = journalList;
942 
943  switch ( sortField ) {
944  case JournalSortUnsorted:
945  break;
946 
947  case JournalSortDate:
948  if ( sortDirection == SortDirectionAscending ) {
949  qSort( journalListSorted.begin(), journalListSorted.end(), Journals::dateLessThan );
950  } else {
951  qSort( journalListSorted.begin(), journalListSorted.end(), Journals::dateMoreThan );
952  }
953  break;
954 
955  case JournalSortSummary:
956  if ( sortDirection == SortDirectionAscending ) {
957  qSort( journalListSorted.begin(), journalListSorted.end(), Journals::summaryLessThan );
958  } else {
959  qSort( journalListSorted.begin(), journalListSorted.end(), Journals::summaryMoreThan );
960  }
961  break;
962  }
963 
964  return journalListSorted;
965 }
966 
967 Journal::List Calendar::journals( JournalSortField sortField,
968  SortDirection sortDirection ) const
969 {
970  Journal::List jl = rawJournals( sortField, sortDirection );
971  d->mFilter->apply( &jl );
972  return jl;
973 }
974 
975 Journal::List Calendar::journals( const QDate &date ) const
976 {
977  Journal::List el = rawJournalsForDate( date );
978  d->mFilter->apply( &el );
979  return el;
980 }
981 
982 // When this is called, the to-dos have already been added to the calendar.
983 // This method is only about linking related to-dos.
984 void Calendar::setupRelations( const Incidence::Ptr &forincidence )
985 {
986  if ( !forincidence ) {
987  return;
988  }
989 
990  const QString uid = forincidence->uid();
991 
992  // First, go over the list of orphans and see if this is their parent
993  Incidence::List l = values( d->mOrphans, uid );
994  d->mOrphans.remove( uid );
995  for ( int i = 0, end = l.count(); i < end; ++i ) {
996  d->mIncidenceRelations[uid].append( l[i] );
997  d->mOrphanUids.remove( l[i]->uid() );
998  }
999 
1000  // Now see about this incidences parent
1001  if ( forincidence->relatedTo().isEmpty() && !forincidence->relatedTo().isEmpty() ) {
1002  // Incidence has a uid it is related to but is not registered to it yet.
1003  // Try to find it
1004  Incidence::Ptr parent = incidence( forincidence->relatedTo() );
1005  if ( parent ) {
1006  // Found it
1007 
1008  // look for hierarchy loops
1009  if ( isAncestorOf( forincidence, parent ) ) {
1010  forincidence->setRelatedTo( QString() );
1011  kWarning() << "hierarchy loop beetween " << forincidence->uid() << " and " << parent->uid();
1012  } else {
1013  d->mIncidenceRelations[parent->uid()].append( forincidence );
1014  }
1015  } else {
1016  // Not found, put this in the mOrphans list
1017  // Note that the mOrphans dict might contain multiple entries with the
1018  // same key! which are multiple children that wait for the parent
1019  // incidence to be inserted.
1020  d->mOrphans.insert( forincidence->relatedTo(), forincidence );
1021  d->mOrphanUids.insert( forincidence->uid(), forincidence );
1022  }
1023  }
1024 }
1025 
1026 // If a to-do with sub-to-dos is deleted, move it's sub-to-dos to the orphan list
1027 void Calendar::removeRelations( const Incidence::Ptr &incidence )
1028 {
1029  if ( !incidence ) {
1030  kDebug() << "Warning: incidence is 0";
1031  return;
1032  }
1033 
1034  const QString uid = incidence->uid();
1035 
1036  foreach ( Incidence::Ptr i, d->mIncidenceRelations[uid] ) {
1037  if ( !d->mOrphanUids.contains( i->uid() ) ) {
1038  d->mOrphans.insert( uid, i );
1039  d->mOrphanUids.insert( i->uid(), i );
1040  i->setRelatedTo( uid );
1041  }
1042  }
1043 
1044  const QString parentUid = incidence->relatedTo();
1045 
1046  // If this incidence is related to something else, tell that about it
1047  if ( !parentUid.isEmpty() ) {
1048  d->mIncidenceRelations[parentUid].erase(
1049  std::remove( d->mIncidenceRelations[parentUid].begin(),
1050  d->mIncidenceRelations[parentUid].end(), incidence ),
1051  d->mIncidenceRelations[parentUid].end() );
1052  }
1053 
1054  // Remove this one from the orphans list
1055  if ( d->mOrphanUids.remove( uid ) ) {
1056  // This incidence is located in the orphans list - it should be removed
1057  // Since the mOrphans dict might contain the same key (with different
1058  // child incidence pointers!) multiple times, take care that we remove
1059  // the correct one. So we need to remove all items with the given
1060  // parent UID, and readd those that are not for this item. Also, there
1061  // might be other entries with differnet UID that point to this
1062  // incidence (this might happen when the relatedTo of the item is
1063  // changed before its parent is inserted. This might happen with
1064  // groupware servers....). Remove them, too
1065  QStringList relatedToUids;
1066 
1067  // First, create a list of all keys in the mOrphans list which point
1068  // to the removed item
1069  relatedToUids << incidence->relatedTo();
1070  for ( QMultiHash<QString, Incidence::Ptr>::Iterator it = d->mOrphans.begin();
1071  it != d->mOrphans.end(); ++it ) {
1072  if ( it.value()->uid() == uid ) {
1073  relatedToUids << it.key();
1074  }
1075  }
1076 
1077  // now go through all uids that have one entry that point to the incidence
1078  for ( QStringList::const_iterator uidit = relatedToUids.constBegin();
1079  uidit != relatedToUids.constEnd(); ++uidit ) {
1080  Incidence::List tempList;
1081  // Remove all to get access to the remaining entries
1082  QList<Incidence::Ptr> l = d->mOrphans.values( *uidit );
1083  d->mOrphans.remove( *uidit );
1084  foreach ( Incidence::Ptr i, l ) {
1085  if ( i != incidence ) {
1086  tempList.append( i );
1087  }
1088  }
1089  // Readd those that point to a different orphan incidence
1090  for ( Incidence::List::Iterator incit = tempList.begin();
1091  incit != tempList.end(); ++incit ) {
1092  d->mOrphans.insert( *uidit, *incit );
1093  }
1094  }
1095  }
1096 
1097  // Make sure the deleted incidence doesn't relate to a non-deleted incidence,
1098  // since that would cause trouble in MemoryCalendar::close(), as the deleted
1099  // incidences are destroyed after the non-deleted incidences. The destructor
1100  // of the deleted incidences would then try to access the already destroyed
1101  // non-deleted incidence, which would segfault.
1102  //
1103  // So in short: Make sure dead incidences don't point to alive incidences
1104  // via the relation.
1105  //
1106  // This crash is tested in MemoryCalendarTest::testRelationsCrash().
1107 // incidence->setRelatedTo( Incidence::Ptr() );
1108 }
1109 
1110 bool Calendar::isAncestorOf( const Incidence::Ptr &ancestor,
1111  const Incidence::Ptr &incidence ) const
1112 {
1113  if ( !incidence || incidence->relatedTo().isEmpty() ) {
1114  return false;
1115  } else if ( incidence->relatedTo() == ancestor->uid() ) {
1116  return true;
1117  } else {
1118  return isAncestorOf( ancestor, this->incidence( incidence->relatedTo() ) );
1119  }
1120 }
1121 
1122 Incidence::List Calendar::relations( const QString &uid ) const
1123 {
1124  return d->mIncidenceRelations[uid];
1125 }
1126 
1127 Calendar::CalendarObserver::~CalendarObserver()
1128 {
1129 }
1130 
1131 void Calendar::CalendarObserver::calendarModified( bool modified, Calendar *calendar )
1132 {
1133  Q_UNUSED( modified );
1134  Q_UNUSED( calendar );
1135 }
1136 
1137 void Calendar::CalendarObserver::calendarIncidenceAdded( const Incidence::Ptr &incidence )
1138 {
1139  Q_UNUSED( incidence );
1140 }
1141 
1142 void Calendar::CalendarObserver::calendarIncidenceChanged( const Incidence::Ptr &incidence )
1143 {
1144  Q_UNUSED( incidence );
1145 }
1146 
1147 void Calendar::CalendarObserver::calendarIncidenceDeleted( const Incidence::Ptr &incidence )
1148 {
1149  Q_UNUSED( incidence );
1150 }
1151 
1152 void
1153 Calendar::CalendarObserver::calendarIncidenceAdditionCanceled( const Incidence::Ptr &incidence )
1154 {
1155  Q_UNUSED( incidence );
1156 }
1157 
1158 void Calendar::registerObserver( CalendarObserver *observer )
1159 {
1160  if ( !observer ) {
1161  return;
1162  }
1163 
1164  if ( !d->mObservers.contains( observer ) ) {
1165  d->mObservers.append( observer );
1166  } else {
1167  d->mNewObserver = true;
1168  }
1169 }
1170 
1171 void Calendar::unregisterObserver( CalendarObserver *observer )
1172 {
1173  if ( !observer ) {
1174  return;
1175  } else {
1176  d->mObservers.removeAll( observer );
1177  }
1178 }
1179 
1180 bool Calendar::isSaving() const
1181 {
1182  return false;
1183 }
1184 
1185 void Calendar::setModified( bool modified )
1186 {
1187  if ( modified != d->mModified || d->mNewObserver ) {
1188  d->mNewObserver = false;
1189  foreach ( CalendarObserver *observer, d->mObservers ) {
1190  observer->calendarModified( modified, this );
1191  }
1192  d->mModified = modified;
1193  }
1194 }
1195 
1196 bool Calendar::isModified() const
1197 {
1198  return d->mModified;
1199 }
1200 
1201 bool Calendar::save()
1202 {
1203  return true;
1204 }
1205 
1206 bool Calendar::reload()
1207 {
1208  return true;
1209 }
1210 
1211 void Calendar::incidenceUpdated( const QString &uid, const KDateTime &recurrenceId )
1212 {
1213 
1214  Incidence::Ptr inc = incidence( uid, recurrenceId );
1215 
1216  if ( !inc ) {
1217  return;
1218  }
1219 
1220  inc->setLastModified( KDateTime::currentUtcDateTime() );
1221  // we should probably update the revision number here,
1222  // or internally in the Event itself when certain things change.
1223  // need to verify with ical documentation.
1224 
1225  notifyIncidenceChanged( inc );
1226 
1227  setModified( true );
1228 }
1229 
1230 void Calendar::doSetTimeSpec( const KDateTime::Spec &timeSpec )
1231 {
1232  Q_UNUSED( timeSpec );
1233 }
1234 
1235 void Calendar::notifyIncidenceAdded( const Incidence::Ptr &incidence )
1236 {
1237  if ( !incidence ) {
1238  return;
1239  }
1240 
1241  if ( !d->mObserversEnabled ) {
1242  return;
1243  }
1244 
1245  foreach ( CalendarObserver *observer, d->mObservers ) {
1246  observer->calendarIncidenceAdded( incidence );
1247  }
1248 }
1249 
1250 void Calendar::notifyIncidenceChanged( const Incidence::Ptr &incidence )
1251 {
1252  if ( !incidence ) {
1253  return;
1254  }
1255 
1256  if ( !d->mObserversEnabled ) {
1257  return;
1258  }
1259 
1260  foreach ( CalendarObserver *observer, d->mObservers ) {
1261  observer->calendarIncidenceChanged( incidence );
1262  }
1263 }
1264 
1265 void Calendar::notifyIncidenceDeleted( const Incidence::Ptr &incidence )
1266 {
1267  if ( !incidence ) {
1268  return;
1269  }
1270 
1271  if ( !d->mObserversEnabled ) {
1272  return;
1273  }
1274 
1275  foreach ( CalendarObserver *observer, d->mObservers ) {
1276  observer->calendarIncidenceDeleted( incidence );
1277  }
1278 }
1279 
1280 void Calendar::notifyIncidenceAdditionCanceled( const Incidence::Ptr &incidence )
1281 {
1282  if ( !incidence ) {
1283  return;
1284  }
1285 
1286  if ( !d->mObserversEnabled ) {
1287  return;
1288  }
1289 
1290  foreach ( CalendarObserver *observer, d->mObservers ) {
1291  observer->calendarIncidenceAdditionCanceled( incidence );
1292  }
1293 }
1294 
1295 void Calendar::customPropertyUpdated()
1296 {
1297  setModified( true );
1298 }
1299 
1300 void Calendar::setProductId( const QString &id )
1301 {
1302  d->mProductId = id;
1303 }
1304 
1305 QString Calendar::productId() const
1306 {
1307  return d->mProductId;
1308 }
1309 
1311 Incidence::List Calendar::mergeIncidenceList( const Event::List &events,
1312  const Todo::List &todos,
1313  const Journal::List &journals )
1314 {
1315  Incidence::List incidences;
1316 
1317  int i, end;
1318  for ( i = 0, end = events.count(); i < end; ++i ) {
1319  incidences.append( events[i] );
1320  }
1321 
1322  for ( i = 0, end = todos.count(); i < end; ++i ) {
1323  incidences.append( todos[i] );
1324  }
1325 
1326  for ( i = 0, end = journals.count(); i < end; ++i ) {
1327  incidences.append( journals[i] );
1328  }
1329 
1330  return incidences;
1331 }
1332 
1333 bool Calendar::beginChange( const Incidence::Ptr &incidence )
1334 {
1335  Q_UNUSED( incidence );
1336  return true;
1337 }
1338 
1339 bool Calendar::endChange( const Incidence::Ptr &incidence )
1340 {
1341  Q_UNUSED( incidence );
1342  return true;
1343 }
1344 
1345 void Calendar::setObserversEnabled( bool enabled )
1346 {
1347  d->mObserversEnabled = enabled;
1348 }
1349 
1350 void Calendar::appendAlarms( Alarm::List &alarms, const Incidence::Ptr &incidence,
1351  const KDateTime &from, const KDateTime &to ) const
1352 {
1353  KDateTime preTime = from.addSecs( -1 );
1354 
1355  Alarm::List alarmlist = incidence->alarms();
1356  for ( int i = 0, iend = alarmlist.count(); i < iend; ++i ) {
1357  if ( alarmlist[i]->enabled() ) {
1358  KDateTime dt = alarmlist[i]->nextRepetition( preTime );
1359  if ( dt.isValid() && dt <= to ) {
1360  kDebug() << incidence->summary() << "':" << dt.toString();
1361  alarms.append( alarmlist[i] );
1362  }
1363  }
1364  }
1365 }
1366 
1367 void Calendar::appendRecurringAlarms( Alarm::List &alarms,
1368  const Incidence::Ptr &incidence,
1369  const KDateTime &from,
1370  const KDateTime &to ) const
1371 {
1372  KDateTime dt;
1373  bool endOffsetValid = false;
1374  Duration endOffset( 0 );
1375  Duration period( from, to );
1376 
1377  Alarm::List alarmlist = incidence->alarms();
1378  for ( int i = 0, iend = alarmlist.count(); i < iend; ++i ) {
1379  Alarm::Ptr a = alarmlist[i];
1380  if ( a->enabled() ) {
1381  if ( a->hasTime() ) {
1382  // The alarm time is defined as an absolute date/time
1383  dt = a->nextRepetition( from.addSecs( -1 ) );
1384  if ( !dt.isValid() || dt > to ) {
1385  continue;
1386  }
1387  } else {
1388  // Alarm time is defined by an offset from the event start or end time.
1389  // Find the offset from the event start time, which is also used as the
1390  // offset from the recurrence time.
1391  Duration offset( 0 );
1392  if ( a->hasStartOffset() ) {
1393  offset = a->startOffset();
1394  } else if ( a->hasEndOffset() ) {
1395  offset = a->endOffset();
1396  if ( !endOffsetValid ) {
1397  endOffset = Duration( incidence->dtStart(),
1398  incidence->dateTime( Incidence::RoleAlarmEndOffset ) );
1399  endOffsetValid = true;
1400  }
1401  }
1402 
1403  // Find the incidence's earliest alarm
1404  KDateTime alarmStart =
1405  offset.end( a->hasEndOffset() ? incidence->dateTime( Incidence::RoleAlarmEndOffset ) :
1406  incidence->dtStart() );
1407 // KDateTime alarmStart = incidence->dtStart().addSecs( offset );
1408  if ( alarmStart > to ) {
1409  continue;
1410  }
1411  KDateTime baseStart = incidence->dtStart();
1412  if ( from > alarmStart ) {
1413  alarmStart = from; // don't look earlier than the earliest alarm
1414  baseStart = ( -offset ).end( ( -endOffset ).end( alarmStart ) );
1415  }
1416 
1417  // Adjust the 'alarmStart' date/time and find the next recurrence at or after it.
1418  // Treate the two offsets separately in case one is daily and the other not.
1419  dt = incidence->recurrence()->getNextDateTime( baseStart.addSecs( -1 ) );
1420  if ( !dt.isValid() ||
1421  ( dt = endOffset.end( offset.end( dt ) ) ) > to ) // adjust 'dt' to get the alarm time
1422  {
1423  // The next recurrence is too late.
1424  if ( !a->repeatCount() ) {
1425  continue;
1426  }
1427 
1428  // The alarm has repetitions, so check whether repetitions of previous
1429  // recurrences fall within the time period.
1430  bool found = false;
1431  Duration alarmDuration = a->duration();
1432  for ( KDateTime base = baseStart;
1433  ( dt = incidence->recurrence()->getPreviousDateTime( base ) ).isValid();
1434  base = dt ) {
1435  if ( a->duration().end( dt ) < base ) {
1436  break; // this recurrence's last repetition is too early, so give up
1437  }
1438 
1439  // The last repetition of this recurrence is at or after 'alarmStart' time.
1440  // Check if a repetition occurs between 'alarmStart' and 'to'.
1441  int snooze = a->snoozeTime().value(); // in seconds or days
1442  if ( a->snoozeTime().isDaily() ) {
1443  Duration toFromDuration( dt, base );
1444  int toFrom = toFromDuration.asDays();
1445  if ( a->snoozeTime().end( from ) <= to ||
1446  ( toFromDuration.isDaily() && toFrom % snooze == 0 ) ||
1447  ( toFrom / snooze + 1 ) * snooze <= toFrom + period.asDays() ) {
1448  found = true;
1449 #ifndef NDEBUG
1450  // for debug output
1451  dt = offset.end( dt ).addDays( ( ( toFrom - 1 ) / snooze + 1 ) * snooze );
1452 #endif
1453  break;
1454  }
1455  } else {
1456  int toFrom = dt.secsTo( base );
1457  if ( period.asSeconds() >= snooze ||
1458  toFrom % snooze == 0 ||
1459  ( toFrom / snooze + 1 ) * snooze <= toFrom + period.asSeconds() )
1460  {
1461  found = true;
1462 #ifndef NDEBUG
1463  // for debug output
1464  dt = offset.end( dt ).addSecs( ( ( toFrom - 1 ) / snooze + 1 ) * snooze );
1465 #endif
1466  break;
1467  }
1468  }
1469  }
1470  if ( !found ) {
1471  continue;
1472  }
1473  }
1474  }
1475  kDebug() << incidence->summary() << "':" << dt.toString();
1476  alarms.append( a );
1477  }
1478  }
1479 }
1480 
1481 void Calendar::startBatchAdding()
1482 {
1483  d->batchAddingInProgress = true;
1484 }
1485 
1486 void Calendar::endBatchAdding()
1487 {
1488  d->batchAddingInProgress = false;
1489 }
1490 
1491 bool Calendar::batchAdding() const
1492 {
1493  return d->batchAddingInProgress;
1494 }
1495 
1496 void Calendar::virtual_hook( int id, void *data )
1497 {
1498  Q_UNUSED( id );
1499  Q_UNUSED( data );
1500  Q_ASSERT( false );
1501 }
1502 
This file is part of the KDE documentation.
Documentation copyright © 1996-2013 The KDE developers.
Generated on Sat Jul 13 2013 01:24:50 by doxygen 1.8.3.1 written by Dimitri van Heesch, © 1997-2006

KDE's Doxygen guidelines are available online.

KCalCore Library

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

kdepimlibs-4.10.5 API Reference

Skip menu "kdepimlibs-4.10.5 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