• Skip to content
  • Skip to link menu
KDE 4.7 API Reference
  • KDE API Reference
  • KDE-PIM Libraries
  • KDE Home
  • Contact Us
 

KCalCore Library

recurrence.cpp
00001 /*
00002   This file is part of kcalcore library.
00003 
00004   Copyright (c) 1998 Preston Brown <pbrown@kde.org>
00005   Copyright (c) 2001 Cornelius Schumacher <schumacher@kde.org>
00006   Copyright (c) 2002,2006 David Jarvie <software@astrojar.org.uk>
00007   Copyright (C) 2005 Reinhold Kainhofer <kainhofer@kde.org>
00008 
00009   This library is free software; you can redistribute it and/or
00010   modify it under the terms of the GNU Library General Public
00011   License as published by the Free Software Foundation; either
00012   version 2 of the License, or (at your option) any later version.
00013 
00014   This library is distributed in the hope that it will be useful,
00015   but WITHOUT ANY WARRANTY; without even the implied warranty of
00016   MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
00017   Library General Public License for more details.
00018 
00019   You should have received a copy of the GNU Library General Public License
00020   along with this library; see the file COPYING.LIB.  If not, write to
00021   the Free Software Foundation, Inc., 51 Franklin Street, Fifth Floor,
00022   Boston, MA 02110-1301, USA.
00023 */
00024 #include "recurrence.h"
00025 
00026 #include <KDebug>
00027 
00028 #include <QtCore/QBitArray>
00029 
00030 using namespace KCalCore;
00031 
00032 //@cond PRIVATE
00033 class KCalCore::Recurrence::Private
00034 {
00035   public:
00036     Private()
00037       : mCachedType( rMax ),
00038         mAllDay( false ),
00039         mRecurReadOnly( false )
00040     {
00041     }
00042 
00043     Private( const Private &p )
00044       : mRDateTimes( p.mRDateTimes ),
00045         mRDates( p.mRDates ),
00046         mExDateTimes( p.mExDateTimes ),
00047         mExDates( p.mExDates ),
00048         mStartDateTime( p.mStartDateTime ),
00049         mCachedType( p.mCachedType ),
00050         mAllDay( p.mAllDay ),
00051         mRecurReadOnly( p.mRecurReadOnly )
00052     {
00053     }
00054 
00055     bool operator==( const Private &p ) const;
00056 
00057     RecurrenceRule::List mExRules;
00058     RecurrenceRule::List mRRules;
00059     DateTimeList mRDateTimes;
00060     DateList mRDates;
00061     DateTimeList mExDateTimes;
00062     DateList mExDates;
00063     KDateTime mStartDateTime;    // date/time of first recurrence
00064     QList<RecurrenceObserver*> mObservers;
00065 
00066     // Cache the type of the recurrence with the old system (e.g. MonthlyPos)
00067     mutable ushort mCachedType;
00068 
00069     bool mAllDay;                // the recurrence has no time, just a date
00070     bool mRecurReadOnly;
00071 };
00072 
00073 bool Recurrence::Private::operator==( const Recurrence::Private &p ) const
00074 {
00075   kDebug() << mStartDateTime << p.mStartDateTime;
00076 
00077   if ( ( mStartDateTime != p.mStartDateTime &&
00078          ( mStartDateTime.isValid() || p.mStartDateTime.isValid() ) ) ||
00079        mAllDay != p.mAllDay ||
00080        mRecurReadOnly != p.mRecurReadOnly ||
00081        mExDates != p.mExDates ||
00082        mExDateTimes != p.mExDateTimes ||
00083        mRDates != p.mRDates ||
00084        mRDateTimes != p.mRDateTimes ) {
00085     return false;
00086   }
00087 
00088 // Compare the rrules, exrules! Assume they have the same order... This only
00089 // matters if we have more than one rule (which shouldn't be the default anyway)
00090   int i;
00091   int end = mRRules.count();
00092   if ( end != p.mRRules.count() ) {
00093     return false;
00094   }
00095   for ( i = 0;  i < end;  ++i ) {
00096     if ( *mRRules[i] != *p.mRRules[i] ) {
00097       return false;
00098     }
00099   }
00100   end = mExRules.count();
00101   if ( end != p.mExRules.count() ) {
00102     return false;
00103   }
00104   for ( i = 0;  i < end;  ++i ) {
00105     if ( *mExRules[i] != *p.mExRules[i] ) {
00106       return false;
00107     }
00108   }
00109   return true;
00110 }
00111 //@endcond
00112 
00113 Recurrence::Recurrence()
00114   : d( new KCalCore::Recurrence::Private() )
00115 {
00116 }
00117 
00118 Recurrence::Recurrence( const Recurrence &r )
00119   : RecurrenceRule::RuleObserver(),
00120     d( new KCalCore::Recurrence::Private( *r.d ) )
00121 {
00122   int i, end;
00123   for ( i = 0, end = r.d->mRRules.count();  i < end;  ++i ) {
00124     RecurrenceRule *rule = new RecurrenceRule( *r.d->mRRules[i] );
00125     d->mRRules.append( rule );
00126     rule->addObserver( this );
00127   }
00128   for ( i = 0, end = r.d->mExRules.count();  i < end;  ++i ) {
00129     RecurrenceRule *rule = new RecurrenceRule( *r.d->mExRules[i] );
00130     d->mExRules.append( rule );
00131     rule->addObserver( this );
00132   }
00133 }
00134 
00135 Recurrence::~Recurrence()
00136 {
00137   qDeleteAll( d->mExRules );
00138   qDeleteAll( d->mRRules );
00139   delete d;
00140 }
00141 
00142 bool Recurrence::operator==( const Recurrence &recurrence ) const
00143 {
00144   return *d == *recurrence.d;
00145 }
00146 
00147 Recurrence &Recurrence::operator=( const Recurrence &recurrence )
00148 {
00149   // check for self assignment
00150   if ( &recurrence == this ) {
00151     return *this;
00152   }
00153 
00154   *d = *recurrence.d;
00155   return *this;
00156 }
00157 
00158 void Recurrence::addObserver( RecurrenceObserver *observer )
00159 {
00160   if ( !d->mObservers.contains( observer ) ) {
00161     d->mObservers.append( observer );
00162   }
00163 }
00164 
00165 void Recurrence::removeObserver( RecurrenceObserver *observer )
00166 {
00167   if ( d->mObservers.contains( observer ) ) {
00168     d->mObservers.removeAll( observer );
00169   }
00170 }
00171 
00172 KDateTime Recurrence::startDateTime() const
00173 {
00174   return d->mStartDateTime;
00175 }
00176 
00177 bool Recurrence::allDay() const
00178 {
00179   return d->mAllDay;
00180 }
00181 
00182 void Recurrence::setAllDay( bool allDay )
00183 {
00184   if ( d->mRecurReadOnly || allDay == d->mAllDay ) {
00185     return;
00186   }
00187 
00188   d->mAllDay = allDay;
00189   for ( int i = 0, end = d->mRRules.count();  i < end;  ++i ) {
00190     d->mRRules[i]->setAllDay( allDay );
00191   }
00192   for ( int i = 0, end = d->mExRules.count();  i < end;  ++i ) {
00193     d->mExRules[i]->setAllDay( allDay );
00194   }
00195   updated();
00196 }
00197 
00198 RecurrenceRule *Recurrence::defaultRRule( bool create ) const
00199 {
00200   if ( d->mRRules.isEmpty() ) {
00201     if ( !create || d->mRecurReadOnly ) {
00202       return 0;
00203     }
00204     RecurrenceRule *rrule = new RecurrenceRule();
00205     rrule->setStartDt( startDateTime() );
00206     const_cast<KCalCore::Recurrence*>(this)->addRRule( rrule );
00207     return rrule;
00208   } else {
00209     return d->mRRules[0];
00210   }
00211 }
00212 
00213 RecurrenceRule *Recurrence::defaultRRuleConst() const
00214 {
00215   return d->mRRules.isEmpty() ? 0 : d->mRRules[0];
00216 }
00217 
00218 void Recurrence::updated()
00219 {
00220   // recurrenceType() re-calculates the type if it's rMax
00221   d->mCachedType = rMax;
00222   for ( int i = 0, end = d->mObservers.count();  i < end;  ++i ) {
00223     if ( d->mObservers[i] ) {
00224       d->mObservers[i]->recurrenceUpdated( this );
00225     }
00226   }
00227 }
00228 
00229 bool Recurrence::recurs() const
00230 {
00231   return !d->mRRules.isEmpty() || !d->mRDates.isEmpty() || !d->mRDateTimes.isEmpty();
00232 }
00233 
00234 ushort Recurrence::recurrenceType() const
00235 {
00236   if ( d->mCachedType == rMax ) {
00237     d->mCachedType = recurrenceType( defaultRRuleConst() );
00238   }
00239   return d->mCachedType;
00240 }
00241 
00242 ushort Recurrence::recurrenceType( const RecurrenceRule *rrule )
00243 {
00244   if ( !rrule ) {
00245     return rNone;
00246   }
00247   RecurrenceRule::PeriodType type = rrule->recurrenceType();
00248 
00249   // BYSETPOS, BYWEEKNUMBER and BYSECOND were not supported in old versions
00250   if ( !rrule->bySetPos().isEmpty() ||
00251        !rrule->bySeconds().isEmpty() ||
00252        !rrule->byWeekNumbers().isEmpty() ) {
00253     return rOther;
00254   }
00255 
00256   // It wasn't possible to set BYMINUTES, BYHOUR etc. by the old code. So if
00257   // it's set, it's none of the old types
00258   if ( !rrule->byMinutes().isEmpty() || !rrule->byHours().isEmpty() ) {
00259     return rOther;
00260   }
00261 
00262   // Possible combinations were:
00263   // BYDAY: with WEEKLY, MONTHLY, YEARLY
00264   // BYMONTHDAY: with MONTHLY, YEARLY
00265   // BYMONTH: with YEARLY
00266   // BYYEARDAY: with YEARLY
00267   if ( ( !rrule->byYearDays().isEmpty() && type != RecurrenceRule::rYearly ) ||
00268        ( !rrule->byMonths().isEmpty() && type != RecurrenceRule::rYearly ) ) {
00269     return rOther;
00270   }
00271   if ( !rrule->byDays().isEmpty() ) {
00272     if ( type != RecurrenceRule::rYearly &&
00273          type != RecurrenceRule::rMonthly &&
00274          type != RecurrenceRule::rWeekly ) {
00275       return rOther;
00276     }
00277   }
00278 
00279   switch ( type ) {
00280   case RecurrenceRule::rNone:
00281     return rNone;
00282   case RecurrenceRule::rMinutely:
00283     return rMinutely;
00284   case RecurrenceRule::rHourly:
00285     return rHourly;
00286   case RecurrenceRule::rDaily:
00287     return rDaily;
00288   case RecurrenceRule::rWeekly:
00289     return rWeekly;
00290   case RecurrenceRule::rMonthly:
00291   {
00292     if ( rrule->byDays().isEmpty() ) {
00293       return rMonthlyDay;
00294     } else if ( rrule->byMonthDays().isEmpty() ) {
00295       return rMonthlyPos;
00296     } else {
00297       return rOther; // both position and date specified
00298     }
00299   }
00300   case RecurrenceRule::rYearly:
00301   {
00302     // Possible combinations:
00303     //   rYearlyMonth: [BYMONTH &] BYMONTHDAY
00304     //   rYearlyDay: BYYEARDAY
00305     //   rYearlyPos: [BYMONTH &] BYDAY
00306     if ( !rrule->byDays().isEmpty() ) {
00307       // can only by rYearlyPos
00308       if ( rrule->byMonthDays().isEmpty() && rrule->byYearDays().isEmpty() ) {
00309         return rYearlyPos;
00310       } else {
00311         return rOther;
00312       }
00313     } else if ( !rrule->byYearDays().isEmpty() ) {
00314       // Can only be rYearlyDay
00315       if ( rrule->byMonths().isEmpty() && rrule->byMonthDays().isEmpty() ) {
00316         return rYearlyDay;
00317       } else {
00318         return rOther;
00319       }
00320     } else {
00321       return rYearlyMonth;
00322     }
00323     break;
00324   }
00325   default: return rOther;
00326   }
00327   return rOther;
00328 }
00329 
00330 bool Recurrence::recursOn( const QDate &qd, const KDateTime::Spec &timeSpec ) const
00331 {
00332   // Don't waste time if date is before the start of the recurrence
00333   if ( KDateTime( qd, QTime( 23, 59, 59 ), timeSpec ) < d->mStartDateTime ) {
00334     return false;
00335   }
00336 
00337   // First handle dates. Exrules override
00338   if ( d->mExDates.containsSorted( qd ) ) {
00339     return false;
00340   }
00341 
00342   int i, end;
00343   TimeList tms;
00344   // For all-day events a matching exrule excludes the whole day
00345   // since exclusions take precedence over inclusions, we know it can't occur on that day.
00346   if ( allDay() ) {
00347     for ( i = 0, end = d->mExRules.count();  i < end;  ++i ) {
00348       if ( d->mExRules[i]->recursOn( qd, timeSpec ) ) {
00349         return false;
00350       }
00351     }
00352   }
00353 
00354   if ( d->mRDates.containsSorted( qd ) ) {
00355     return true;
00356   }
00357 
00358   // Check if it might recur today at all.
00359   bool recurs = ( startDate() == qd );
00360   for ( i = 0, end = d->mRDateTimes.count();  i < end && !recurs;  ++i ) {
00361     recurs = ( d->mRDateTimes[i].toTimeSpec( timeSpec ).date() == qd );
00362   }
00363   for ( i = 0, end = d->mRRules.count();  i < end && !recurs;  ++i ) {
00364     recurs = d->mRRules[i]->recursOn( qd, timeSpec );
00365   }
00366   // If the event wouldn't recur at all, simply return false, don't check ex*
00367   if ( !recurs ) {
00368     return false;
00369   }
00370 
00371   // Check if there are any times for this day excluded, either by exdate or exrule:
00372   bool exon = false;
00373   for ( i = 0, end = d->mExDateTimes.count();  i < end && !exon;  ++i ) {
00374     exon = ( d->mExDateTimes[i].toTimeSpec( timeSpec ).date() == qd );
00375   }
00376   if ( !allDay() ) {     // we have already checked all-day times above
00377     for ( i = 0, end = d->mExRules.count();  i < end && !exon;  ++i ) {
00378       exon = d->mExRules[i]->recursOn( qd, timeSpec );
00379     }
00380   }
00381 
00382   if ( !exon ) {
00383     // Simple case, nothing on that day excluded, return the value from before
00384     return recurs;
00385   } else {
00386     // Harder part: I don't think there is any way other than to calculate the
00387     // whole list of items for that day.
00388 //TODO: consider whether it would be more efficient to call
00389 //      Rule::recurTimesOn() instead of Rule::recursOn() from the start
00390     TimeList timesForDay( recurTimesOn( qd, timeSpec ) );
00391     return !timesForDay.isEmpty();
00392   }
00393 }
00394 
00395 bool Recurrence::recursAt( const KDateTime &dt ) const
00396 {
00397   // Convert to recurrence's time zone for date comparisons, and for more efficient time comparisons
00398   KDateTime dtrecur = dt.toTimeSpec( d->mStartDateTime.timeSpec() );
00399 
00400   // if it's excluded anyway, don't bother to check if it recurs at all.
00401   if ( d->mExDateTimes.containsSorted( dtrecur ) ||
00402        d->mExDates.containsSorted( dtrecur.date() ) ) {
00403     return false;
00404   }
00405   int i, end;
00406   for ( i = 0, end = d->mExRules.count();  i < end;  ++i ) {
00407     if ( d->mExRules[i]->recursAt( dtrecur ) ) {
00408       return false;
00409     }
00410   }
00411 
00412   // Check explicit recurrences, then rrules.
00413   if ( startDateTime() == dtrecur || d->mRDateTimes.containsSorted( dtrecur ) ) {
00414     return true;
00415   }
00416   for ( i = 0, end = d->mRRules.count();  i < end;  ++i ) {
00417     if ( d->mRRules[i]->recursAt( dtrecur ) ) {
00418       return true;
00419     }
00420   }
00421 
00422   return false;
00423 }
00424 
00428 KDateTime Recurrence::endDateTime() const
00429 {
00430   DateTimeList dts;
00431   dts << startDateTime();
00432   if ( !d->mRDates.isEmpty() ) {
00433     dts << KDateTime( d->mRDates.last(), QTime( 0, 0, 0 ), d->mStartDateTime.timeSpec() );
00434   }
00435   if ( !d->mRDateTimes.isEmpty() ) {
00436     dts << d->mRDateTimes.last();
00437   }
00438   for ( int i = 0, end = d->mRRules.count();  i < end;  ++i ) {
00439     KDateTime rl( d->mRRules[i]->endDt() );
00440     // if any of the rules is infinite, the whole recurrence is
00441     if ( !rl.isValid() ) {
00442       return KDateTime();
00443     }
00444     dts << rl;
00445   }
00446   dts.sortUnique();
00447   return dts.isEmpty() ? KDateTime() : dts.last();
00448 }
00449 
00453 QDate Recurrence::endDate() const
00454 {
00455   KDateTime end( endDateTime() );
00456   return end.isValid() ? end.date() : QDate();
00457 }
00458 
00459 void Recurrence::setEndDate( const QDate &date )
00460 {
00461   KDateTime dt( date, d->mStartDateTime.time(), d->mStartDateTime.timeSpec() );
00462   if ( allDay() ) {
00463     dt.setTime( QTime( 23, 59, 59 ) );
00464   }
00465   setEndDateTime( dt );
00466 }
00467 
00468 void Recurrence::setEndDateTime( const KDateTime &dateTime )
00469 {
00470   if ( d->mRecurReadOnly ) {
00471     return;
00472   }
00473   RecurrenceRule *rrule = defaultRRule( true );
00474   if ( !rrule ) {
00475     return;
00476   }
00477   rrule->setEndDt( dateTime );
00478   updated();
00479 }
00480 
00481 int Recurrence::duration() const
00482 {
00483   RecurrenceRule *rrule = defaultRRuleConst();
00484   return rrule ? rrule->duration() : 0;
00485 }
00486 
00487 int Recurrence::durationTo( const KDateTime &datetime ) const
00488 {
00489   // Emulate old behavior: This is just an interface to the first rule!
00490   RecurrenceRule *rrule = defaultRRuleConst();
00491   return rrule ? rrule->durationTo( datetime ) : 0;
00492 }
00493 
00494 int Recurrence::durationTo( const QDate &date ) const
00495 {
00496   return durationTo( KDateTime( date, QTime( 23, 59, 59 ), d->mStartDateTime.timeSpec() ) );
00497 }
00498 
00499 void Recurrence::setDuration( int duration )
00500 {
00501   if ( d->mRecurReadOnly ) {
00502     return;
00503   }
00504 
00505   RecurrenceRule *rrule = defaultRRule( true );
00506   if ( !rrule ) {
00507     return;
00508   }
00509   rrule->setDuration( duration );
00510   updated();
00511 }
00512 
00513 void Recurrence::shiftTimes( const KDateTime::Spec &oldSpec, const KDateTime::Spec &newSpec )
00514 {
00515   if ( d->mRecurReadOnly ) {
00516     return;
00517   }
00518 
00519   d->mStartDateTime = d->mStartDateTime.toTimeSpec( oldSpec );
00520   d->mStartDateTime.setTimeSpec( newSpec );
00521 
00522   int i, end;
00523   for ( i = 0, end = d->mRDateTimes.count();  i < end;  ++i ) {
00524     d->mRDateTimes[i] = d->mRDateTimes[i].toTimeSpec( oldSpec );
00525     d->mRDateTimes[i].setTimeSpec( newSpec );
00526   }
00527   for ( i = 0, end = d->mExDateTimes.count();  i < end;  ++i ) {
00528     d->mExDateTimes[i] = d->mExDateTimes[i].toTimeSpec( oldSpec );
00529     d->mExDateTimes[i].setTimeSpec( newSpec );
00530   }
00531   for ( i = 0, end = d->mRRules.count();  i < end;  ++i ) {
00532     d->mRRules[i]->shiftTimes( oldSpec, newSpec );
00533   }
00534   for ( i = 0, end = d->mExRules.count();  i < end;  ++i ) {
00535     d->mExRules[i]->shiftTimes( oldSpec, newSpec );
00536   }
00537 }
00538 
00539 void Recurrence::unsetRecurs()
00540 {
00541   if ( d->mRecurReadOnly ) {
00542     return;
00543   }
00544   d->mRRules.clear();
00545   updated();
00546 }
00547 
00548 void Recurrence::clear()
00549 {
00550   if ( d->mRecurReadOnly ) {
00551     return;
00552   }
00553   d->mRRules.clear();
00554   d->mExRules.clear();
00555   d->mRDates.clear();
00556   d->mRDateTimes.clear();
00557   d->mExDates.clear();
00558   d->mExDateTimes.clear();
00559   d->mCachedType = rMax;
00560   updated();
00561 }
00562 
00563 void Recurrence::setRecurReadOnly( bool readOnly )
00564 {
00565   d->mRecurReadOnly = readOnly;
00566 }
00567 
00568 bool Recurrence::recurReadOnly() const
00569 {
00570   return d->mRecurReadOnly;
00571 }
00572 
00573 QDate Recurrence::startDate() const
00574 {
00575   return d->mStartDateTime.date();
00576 }
00577 
00578 void Recurrence::setStartDateTime( const KDateTime &start )
00579 {
00580   if ( d->mRecurReadOnly ) {
00581     return;
00582   }
00583   d->mStartDateTime = start;
00584   setAllDay( start.isDateOnly() );   // set all RRULEs and EXRULEs
00585 
00586   int i, end;
00587   for ( i = 0, end = d->mRRules.count();  i < end;  ++i ) {
00588     d->mRRules[i]->setStartDt( start );
00589   }
00590   for ( i = 0, end = d->mExRules.count();  i < end;  ++i ) {
00591     d->mExRules[i]->setStartDt( start );
00592   }
00593   updated();
00594 }
00595 
00596 int Recurrence::frequency() const
00597 {
00598   RecurrenceRule *rrule = defaultRRuleConst();
00599   return rrule ? rrule->frequency() : 0;
00600 }
00601 
00602 // Emulate the old behaviour. Make this methods just an interface to the
00603 // first rrule
00604 void Recurrence::setFrequency( int freq )
00605 {
00606   if ( d->mRecurReadOnly || freq <= 0 ) {
00607     return;
00608   }
00609 
00610   RecurrenceRule *rrule = defaultRRule( true );
00611   if ( rrule ) {
00612     rrule->setFrequency( freq );
00613   }
00614   updated();
00615 }
00616 
00617 // WEEKLY
00618 
00619 int Recurrence::weekStart() const
00620 {
00621   RecurrenceRule *rrule = defaultRRuleConst();
00622   return rrule ? rrule->weekStart() : 1;
00623 }
00624 
00625 // Emulate the old behavior
00626 QBitArray Recurrence::days() const
00627 {
00628   QBitArray days( 7 );
00629   days.fill( 0 );
00630   RecurrenceRule *rrule = defaultRRuleConst();
00631   if ( rrule ) {
00632     QList<RecurrenceRule::WDayPos> bydays = rrule->byDays();
00633     for ( int i = 0; i < bydays.size(); ++i ) {
00634       if ( bydays.at(i).pos() == 0 ) {
00635         days.setBit( bydays.at( i ).day() - 1 );
00636       }
00637     }
00638   }
00639   return days;
00640 }
00641 
00642 // MONTHLY
00643 
00644 // Emulate the old behavior
00645 QList<int> Recurrence::monthDays() const
00646 {
00647   RecurrenceRule *rrule = defaultRRuleConst();
00648   if ( rrule ) {
00649     return rrule->byMonthDays();
00650   } else {
00651     return QList<int>();
00652   }
00653 }
00654 
00655 // Emulate the old behavior
00656 QList<RecurrenceRule::WDayPos> Recurrence::monthPositions() const
00657 {
00658   RecurrenceRule *rrule = defaultRRuleConst();
00659   return rrule ? rrule->byDays() : QList<RecurrenceRule::WDayPos>();
00660 }
00661 
00662 // YEARLY
00663 
00664 QList<int> Recurrence::yearDays() const
00665 {
00666   RecurrenceRule *rrule = defaultRRuleConst();
00667   return rrule ? rrule->byYearDays() : QList<int>();
00668 }
00669 
00670 QList<int> Recurrence::yearDates() const
00671 {
00672   return monthDays();
00673 }
00674 
00675 QList<int> Recurrence::yearMonths() const
00676 {
00677   RecurrenceRule *rrule = defaultRRuleConst();
00678   return rrule ? rrule->byMonths() : QList<int>();
00679 }
00680 
00681 QList<RecurrenceRule::WDayPos> Recurrence::yearPositions() const
00682 {
00683   return monthPositions();
00684 }
00685 
00686 RecurrenceRule *Recurrence::setNewRecurrenceType( RecurrenceRule::PeriodType type, int freq )
00687 {
00688   if ( d->mRecurReadOnly || freq <= 0 ) {
00689     return 0;
00690   }
00691 
00692   d->mRRules.clear();
00693   updated();
00694   RecurrenceRule *rrule = defaultRRule( true );
00695   if ( !rrule ) {
00696     return 0;
00697   }
00698   rrule->setRecurrenceType( type );
00699   rrule->setFrequency( freq );
00700   rrule->setDuration( -1 );
00701   return rrule;
00702 }
00703 
00704 void Recurrence::setMinutely( int _rFreq )
00705 {
00706   if ( setNewRecurrenceType( RecurrenceRule::rMinutely, _rFreq ) ) {
00707     updated();
00708   }
00709 }
00710 
00711 void Recurrence::setHourly( int _rFreq )
00712 {
00713   if ( setNewRecurrenceType( RecurrenceRule::rHourly, _rFreq ) ) {
00714     updated();
00715   }
00716 }
00717 
00718 void Recurrence::setDaily( int _rFreq )
00719 {
00720   if ( setNewRecurrenceType( RecurrenceRule::rDaily, _rFreq ) ) {
00721     updated();
00722   }
00723 }
00724 
00725 void Recurrence::setWeekly( int freq, int weekStart )
00726 {
00727   RecurrenceRule *rrule = setNewRecurrenceType( RecurrenceRule::rWeekly, freq );
00728   if ( !rrule ) {
00729     return;
00730   }
00731   rrule->setWeekStart( weekStart );
00732   updated();
00733 }
00734 
00735 void Recurrence::setWeekly( int freq, const QBitArray &days, int weekStart )
00736 {
00737   setWeekly( freq, weekStart );
00738   addMonthlyPos( 0, days );
00739 }
00740 
00741 void Recurrence::addWeeklyDays( const QBitArray &days )
00742 {
00743   addMonthlyPos( 0, days );
00744 }
00745 
00746 void Recurrence::setMonthly( int freq )
00747 {
00748   if ( setNewRecurrenceType( RecurrenceRule::rMonthly, freq ) ) {
00749     updated();
00750   }
00751 }
00752 
00753 void Recurrence::addMonthlyPos( short pos, const QBitArray &days )
00754 {
00755   // Allow 53 for yearly!
00756   if ( d->mRecurReadOnly || pos > 53 || pos < -53 ) {
00757     return;
00758   }
00759 
00760   RecurrenceRule *rrule = defaultRRule( false );
00761   if ( !rrule ) {
00762     return;
00763   }
00764   bool changed = false;
00765   QList<RecurrenceRule::WDayPos> positions = rrule->byDays();
00766 
00767   for ( int i = 0; i < 7; ++i ) {
00768     if ( days.testBit( i ) ) {
00769       RecurrenceRule::WDayPos p( pos, i + 1 );
00770       if ( !positions.contains( p ) ) {
00771         changed = true;
00772         positions.append( p );
00773       }
00774     }
00775   }
00776   if ( changed ) {
00777     rrule->setByDays( positions );
00778     updated();
00779   }
00780 }
00781 
00782 void Recurrence::addMonthlyPos( short pos, ushort day )
00783 {
00784   // Allow 53 for yearly!
00785   if ( d->mRecurReadOnly || pos > 53 || pos < -53 ) {
00786     return;
00787   }
00788 
00789   RecurrenceRule *rrule = defaultRRule( false );
00790   if ( !rrule ) {
00791     return;
00792   }
00793   QList<RecurrenceRule::WDayPos> positions = rrule->byDays();
00794 
00795   RecurrenceRule::WDayPos p( pos, day );
00796   if ( !positions.contains( p ) ) {
00797     positions.append( p );
00798     rrule->setByDays( positions );
00799     updated();
00800   }
00801 }
00802 
00803 void Recurrence::addMonthlyDate( short day )
00804 {
00805   if ( d->mRecurReadOnly || day > 31 || day < -31 ) {
00806     return;
00807   }
00808 
00809   RecurrenceRule *rrule = defaultRRule( true );
00810   if ( !rrule ) {
00811     return;
00812   }
00813 
00814   QList<int> monthDays = rrule->byMonthDays();
00815   if ( !monthDays.contains( day ) ) {
00816     monthDays.append( day );
00817     rrule->setByMonthDays( monthDays );
00818     updated();
00819   }
00820 }
00821 
00822 void Recurrence::setYearly( int freq )
00823 {
00824   if ( setNewRecurrenceType( RecurrenceRule::rYearly, freq ) ) {
00825     updated();
00826   }
00827 }
00828 
00829 // Daynumber within year
00830 void Recurrence::addYearlyDay( int day )
00831 {
00832   RecurrenceRule *rrule = defaultRRule( false ); // It must already exist!
00833   if ( !rrule ) {
00834     return;
00835   }
00836 
00837   QList<int> days = rrule->byYearDays();
00838   if ( !days.contains( day ) ) {
00839     days << day;
00840     rrule->setByYearDays( days );
00841     updated();
00842   }
00843 }
00844 
00845 // day part of date within year
00846 void Recurrence::addYearlyDate( int day )
00847 {
00848   addMonthlyDate( day );
00849 }
00850 
00851 // day part of date within year, given as position (n-th weekday)
00852 void Recurrence::addYearlyPos( short pos, const QBitArray &days )
00853 {
00854   addMonthlyPos( pos, days );
00855 }
00856 
00857 // month part of date within year
00858 void Recurrence::addYearlyMonth( short month )
00859 {
00860   if ( d->mRecurReadOnly || month < 1 || month > 12 ) {
00861     return;
00862   }
00863 
00864   RecurrenceRule *rrule = defaultRRule( false );
00865   if ( !rrule ) {
00866     return;
00867   }
00868 
00869   QList<int> months = rrule->byMonths();
00870   if ( !months.contains(month) ) {
00871     months << month;
00872     rrule->setByMonths( months );
00873     updated();
00874   }
00875 }
00876 
00877 TimeList Recurrence::recurTimesOn( const QDate &date, const KDateTime::Spec &timeSpec ) const
00878 {
00879 // kDebug() << "recurTimesOn(" << date << ")";
00880   int i, end;
00881   TimeList times;
00882 
00883   // The whole day is excepted
00884   if ( d->mExDates.containsSorted( date ) ) {
00885     return times;
00886   }
00887 
00888   // EXRULE takes precedence over RDATE entries, so for all-day events,
00889   // a matching excule also excludes the whole day automatically
00890   if ( allDay() ) {
00891     for ( i = 0, end = d->mExRules.count();  i < end;  ++i ) {
00892       if ( d->mExRules[i]->recursOn( date, timeSpec ) ) {
00893         return times;
00894       }
00895     }
00896   }
00897 
00898   KDateTime dt = startDateTime().toTimeSpec( timeSpec );
00899   if ( dt.date() == date ) {
00900     times << dt.time();
00901   }
00902 
00903   bool foundDate = false;
00904   for ( i = 0, end = d->mRDateTimes.count();  i < end;  ++i ) {
00905     dt = d->mRDateTimes[i].toTimeSpec( timeSpec );
00906     if ( dt.date() == date ) {
00907       times << dt.time();
00908       foundDate = true;
00909     } else if (foundDate) break; // <= Assume that the rdatetime list is sorted
00910   }
00911   for ( i = 0, end = d->mRRules.count();  i < end;  ++i ) {
00912     times += d->mRRules[i]->recurTimesOn( date, timeSpec );
00913   }
00914   times.sortUnique();
00915 
00916   foundDate = false;
00917   TimeList extimes;
00918   for ( i = 0, end = d->mExDateTimes.count();  i < end;  ++i ) {
00919     dt = d->mExDateTimes[i].toTimeSpec( timeSpec );
00920     if ( dt.date() == date ) {
00921       extimes << dt.time();
00922       foundDate = true;
00923     } else if (foundDate) break;
00924   }
00925   if ( !allDay() ) {     // we have already checked all-day times above
00926     for ( i = 0, end = d->mExRules.count();  i < end;  ++i ) {
00927       extimes += d->mExRules[i]->recurTimesOn( date, timeSpec );
00928     }
00929   }
00930   extimes.sortUnique();
00931 
00932   int st = 0;
00933   for ( i = 0, end = extimes.count();  i < end;  ++i ) {
00934     int j = times.removeSorted( extimes[i], st );
00935     if ( j >= 0 ) {
00936       st = j;
00937     }
00938   }
00939   return times;
00940 }
00941 
00942 DateTimeList Recurrence::timesInInterval( const KDateTime &start, const KDateTime &end ) const
00943 {
00944   int i, count;
00945   DateTimeList times;
00946   for ( i = 0, count = d->mRRules.count();  i < count;  ++i ) {
00947     times += d->mRRules[i]->timesInInterval( start, end );
00948   }
00949 
00950   // add rdatetimes that fit in the interval
00951   for ( i = 0, count = d->mRDateTimes.count();  i < count;  ++i ) {
00952     if ( d->mRDateTimes[i] >= start && d->mRDateTimes[i] <= end ) {
00953       times += d->mRDateTimes[i];
00954     }
00955   }
00956 
00957   // add rdates that fit in the interval
00958   KDateTime kdt( d->mStartDateTime );
00959   for ( i = 0, count = d->mRDates.count();  i < count;  ++i ) {
00960     kdt.setDate( d->mRDates[i] );
00961     if ( kdt >= start && kdt <= end ) {
00962       times += kdt;
00963     }
00964   }
00965 
00966   // Recurrence::timesInInterval(...) doesn't explicitly add mStartDateTime to the list
00967   // of times to be returned. It calls mRRules[i]->timesInInterval(...) which include
00968   // mStartDateTime.
00969   // So, If we have rdates/rdatetimes but don't have any rrule we must explicitly
00970   // add mStartDateTime to the list, otherwise we won't see the first occurrence.
00971   if ( ( !d->mRDates.isEmpty() || !d->mRDateTimes.isEmpty() ) &&
00972        d->mRRules.isEmpty() &&
00973        start <= d->mStartDateTime &&
00974        end >= d->mStartDateTime ) {
00975     times += d->mStartDateTime;
00976   }
00977 
00978   times.sortUnique();
00979 
00980   // Remove excluded times
00981   int idt = 0;
00982   int enddt = times.count();
00983   for ( i = 0, count = d->mExDates.count();  i < count && idt < enddt;  ++i ) {
00984     while ( idt < enddt && times[idt].date() < d->mExDates[i] ) ++idt;
00985     while ( idt < enddt && times[idt].date() == d->mExDates[i] ) {
00986       times.removeAt(idt);
00987       --enddt;
00988     }
00989   }
00990   DateTimeList extimes;
00991   for ( i = 0, count = d->mExRules.count();  i < count;  ++i ) {
00992     extimes += d->mExRules[i]->timesInInterval( start, end );
00993   }
00994   extimes += d->mExDateTimes;
00995   extimes.sortUnique();
00996 
00997   int st = 0;
00998   for ( i = 0, count = extimes.count();  i < count;  ++i ) {
00999     int j = times.removeSorted( extimes[i], st );
01000     if ( j >= 0 ) {
01001       st = j;
01002     }
01003   }
01004 
01005   return times;
01006 }
01007 
01008 KDateTime Recurrence::getNextDateTime( const KDateTime &preDateTime ) const
01009 {
01010   KDateTime nextDT = preDateTime;
01011   // prevent infinite loops, e.g. when an exrule extinguishes an rrule (e.g.
01012   // the exrule is identical to the rrule). If an occurrence is found, break
01013   // out of the loop by returning that KDateTime
01014 // TODO_Recurrence: Is a loop counter of 1000 really okay? I mean for secondly
01015 // recurrence, an exdate might exclude more than 1000 intervals!
01016   int loop = 0;
01017   while ( loop < 1000 ) {
01018     // Outline of the algo:
01019     //   1) Find the next date/time after preDateTime when the event could recur
01020     //     1.0) Add the start date if it's after preDateTime
01021     //     1.1) Use the next occurrence from the explicit RDATE lists
01022     //     1.2) Add the next recurrence for each of the RRULEs
01023     //   2) Take the earliest recurrence of these = KDateTime nextDT
01024     //   3) If that date/time is not excluded, either explicitly by an EXDATE or
01025     //      by an EXRULE, return nextDT as the next date/time of the recurrence
01026     //   4) If it's excluded, start all at 1), but starting at nextDT (instead
01027     //      of preDateTime). Loop at most 1000 times.
01028     ++loop;
01029     // First, get the next recurrence from the RDate lists
01030     DateTimeList dates;
01031     if ( nextDT < startDateTime() ) {
01032       dates << startDateTime();
01033     }
01034 
01035     int end;
01036     // Assume that the rdatetime list is sorted
01037     int i = d->mRDateTimes.findGT( nextDT );
01038     if ( i >= 0 ) {
01039       dates << d->mRDateTimes[i];
01040     }
01041 
01042     KDateTime kdt( startDateTime() );
01043     for ( i = 0, end = d->mRDates.count();  i < end;  ++i ) {
01044       kdt.setDate( d->mRDates[i] );
01045       if ( kdt > nextDT ) {
01046         dates << kdt;
01047         break;
01048       }
01049     }
01050 
01051     // Add the next occurrences from all RRULEs.
01052     for ( i = 0, end = d->mRRules.count();  i < end;  ++i ) {
01053       KDateTime dt = d->mRRules[i]->getNextDate( nextDT );
01054       if ( dt.isValid() ) {
01055         dates << dt;
01056       }
01057     }
01058 
01059     // Take the first of these (all others can't be used later on)
01060     dates.sortUnique();
01061     if ( dates.isEmpty() ) {
01062       return KDateTime();
01063     }
01064     nextDT = dates.first();
01065 
01066     // Check if that date/time is excluded explicitly or by an exrule:
01067     if ( !d->mExDates.containsSorted( nextDT.date() ) &&
01068          !d->mExDateTimes.containsSorted( nextDT ) ) {
01069       bool allowed = true;
01070       for ( i = 0, end = d->mExRules.count();  i < end;  ++i ) {
01071         allowed = allowed && !( d->mExRules[i]->recursAt( nextDT ) );
01072       }
01073       if ( allowed ) {
01074         return nextDT;
01075       }
01076     }
01077   }
01078 
01079   // Couldn't find a valid occurrences in 1000 loops, something is wrong!
01080   return KDateTime();
01081 }
01082 
01083 KDateTime Recurrence::getPreviousDateTime( const KDateTime &afterDateTime ) const
01084 {
01085   KDateTime prevDT = afterDateTime;
01086   // prevent infinite loops, e.g. when an exrule extinguishes an rrule (e.g.
01087   // the exrule is identical to the rrule). If an occurrence is found, break
01088   // out of the loop by returning that KDateTime
01089   int loop = 0;
01090   while ( loop < 1000 ) {
01091     // Outline of the algo:
01092     //   1) Find the next date/time after preDateTime when the event could recur
01093     //     1.1) Use the next occurrence from the explicit RDATE lists
01094     //     1.2) Add the next recurrence for each of the RRULEs
01095     //   2) Take the earliest recurrence of these = KDateTime nextDT
01096     //   3) If that date/time is not excluded, either explicitly by an EXDATE or
01097     //      by an EXRULE, return nextDT as the next date/time of the recurrence
01098     //   4) If it's excluded, start all at 1), but starting at nextDT (instead
01099     //      of preDateTime). Loop at most 1000 times.
01100     ++loop;
01101     // First, get the next recurrence from the RDate lists
01102     DateTimeList dates;
01103     if ( prevDT > startDateTime() ) {
01104       dates << startDateTime();
01105     }
01106 
01107     int i = d->mRDateTimes.findLT( prevDT );
01108     if ( i >= 0 ) {
01109       dates << d->mRDateTimes[i];
01110     }
01111 
01112     KDateTime kdt( startDateTime() );
01113     for ( i = d->mRDates.count();  --i >= 0; ) {
01114       kdt.setDate( d->mRDates[i] );
01115       if ( kdt < prevDT ) {
01116         dates << kdt;
01117         break;
01118       }
01119     }
01120 
01121     // Add the previous occurrences from all RRULEs.
01122     int end;
01123     for ( i = 0, end = d->mRRules.count();  i < end;  ++i ) {
01124       KDateTime dt = d->mRRules[i]->getPreviousDate( prevDT );
01125       if ( dt.isValid() ) {
01126         dates << dt;
01127       }
01128     }
01129 
01130     // Take the last of these (all others can't be used later on)
01131     dates.sortUnique();
01132     if ( dates.isEmpty() ) {
01133       return KDateTime();
01134     }
01135     prevDT = dates.last();
01136 
01137     // Check if that date/time is excluded explicitly or by an exrule:
01138     if ( !d->mExDates.containsSorted( prevDT.date() ) &&
01139          !d->mExDateTimes.containsSorted( prevDT ) ) {
01140       bool allowed = true;
01141       for ( i = 0, end = d->mExRules.count();  i < end;  ++i ) {
01142         allowed = allowed && !( d->mExRules[i]->recursAt( prevDT ) );
01143       }
01144       if ( allowed ) {
01145         return prevDT;
01146       }
01147     }
01148   }
01149 
01150   // Couldn't find a valid occurrences in 1000 loops, something is wrong!
01151   return KDateTime();
01152 }
01153 
01154 /***************************** PROTECTED FUNCTIONS ***************************/
01155 
01156 RecurrenceRule::List Recurrence::rRules() const
01157 {
01158   return d->mRRules;
01159 }
01160 
01161 void Recurrence::addRRule( RecurrenceRule *rrule )
01162 {
01163   if ( d->mRecurReadOnly || !rrule ) {
01164     return;
01165   }
01166 
01167   rrule->setAllDay( d->mAllDay );
01168   d->mRRules.append( rrule );
01169   rrule->addObserver( this );
01170   updated();
01171 }
01172 
01173 void Recurrence::removeRRule( RecurrenceRule *rrule )
01174 {
01175   if (d->mRecurReadOnly) {
01176     return;
01177   }
01178 
01179   d->mRRules.removeAll( rrule );
01180   rrule->removeObserver( this );
01181   updated();
01182 }
01183 
01184 void Recurrence::deleteRRule( RecurrenceRule *rrule )
01185 {
01186   if (d->mRecurReadOnly) {
01187     return;
01188   }
01189 
01190   d->mRRules.removeAll( rrule );
01191   delete rrule;
01192   updated();
01193 }
01194 
01195 RecurrenceRule::List Recurrence::exRules() const
01196 {
01197   return d->mExRules;
01198 }
01199 
01200 void Recurrence::addExRule( RecurrenceRule *exrule )
01201 {
01202   if ( d->mRecurReadOnly || !exrule ) {
01203     return;
01204   }
01205 
01206   exrule->setAllDay( d->mAllDay );
01207   d->mExRules.append( exrule );
01208   exrule->addObserver( this );
01209   updated();
01210 }
01211 
01212 void Recurrence::removeExRule( RecurrenceRule *exrule )
01213 {
01214   if ( d->mRecurReadOnly ) {
01215     return;
01216   }
01217 
01218   d->mExRules.removeAll( exrule );
01219   exrule->removeObserver( this );
01220   updated();
01221 }
01222 
01223 void Recurrence::deleteExRule( RecurrenceRule *exrule )
01224 {
01225   if ( d->mRecurReadOnly ) {
01226     return;
01227   }
01228 
01229   d->mExRules.removeAll( exrule );
01230   delete exrule;
01231   updated();
01232 }
01233 
01234 DateTimeList Recurrence::rDateTimes() const
01235 {
01236   return d->mRDateTimes;
01237 }
01238 
01239 void Recurrence::setRDateTimes( const DateTimeList &rdates )
01240 {
01241   if ( d->mRecurReadOnly ) {
01242     return;
01243   }
01244 
01245   d->mRDateTimes = rdates;
01246   d->mRDateTimes.sortUnique();
01247   updated();
01248 }
01249 
01250 void Recurrence::addRDateTime( const KDateTime &rdate )
01251 {
01252   if ( d->mRecurReadOnly ) {
01253     return;
01254   }
01255 
01256   d->mRDateTimes.insertSorted( rdate );
01257   updated();
01258 }
01259 
01260 DateList Recurrence::rDates() const
01261 {
01262   return d->mRDates;
01263 }
01264 
01265 void Recurrence::setRDates( const DateList &rdates )
01266 {
01267   if ( d->mRecurReadOnly ) {
01268     return;
01269   }
01270 
01271   d->mRDates = rdates;
01272   d->mRDates.sortUnique();
01273   updated();
01274 }
01275 
01276 void Recurrence::addRDate( const QDate &rdate )
01277 {
01278   if ( d->mRecurReadOnly ) {
01279     return;
01280   }
01281 
01282   d->mRDates.insertSorted( rdate );
01283   updated();
01284 }
01285 
01286 DateTimeList Recurrence::exDateTimes() const
01287 {
01288   return d->mExDateTimes;
01289 }
01290 
01291 void Recurrence::setExDateTimes( const DateTimeList &exdates )
01292 {
01293   if ( d->mRecurReadOnly ) {
01294     return;
01295   }
01296 
01297   d->mExDateTimes = exdates;
01298   d->mExDateTimes.sortUnique();
01299 }
01300 
01301 void Recurrence::addExDateTime( const KDateTime &exdate )
01302 {
01303   if ( d->mRecurReadOnly ) {
01304     return;
01305   }
01306 
01307   d->mExDateTimes.insertSorted( exdate );
01308   updated();
01309 }
01310 
01311 DateList Recurrence::exDates() const
01312 {
01313   return d->mExDates;
01314 }
01315 
01316 void Recurrence::setExDates( const DateList &exdates )
01317 {
01318   if ( d->mRecurReadOnly ) {
01319     return;
01320   }
01321 
01322   d->mExDates = exdates;
01323   d->mExDates.sortUnique();
01324   updated();
01325 }
01326 
01327 void Recurrence::addExDate( const QDate &exdate )
01328 {
01329   if ( d->mRecurReadOnly ) {
01330     return;
01331   }
01332 
01333   d->mExDates.insertSorted( exdate );
01334   updated();
01335 }
01336 
01337 void Recurrence::recurrenceChanged( RecurrenceRule * )
01338 {
01339   updated();
01340 }
01341 
01342 // %%%%%%%%%%%%%%%%%% end:Recurrencerule %%%%%%%%%%%%%%%%%%
01343 
01344 void Recurrence::dump() const
01345 {
01346   kDebug();
01347 
01348   int i;
01349   int count = d->mRRules.count();
01350   kDebug() << "  -)" << count << "RRULEs:";
01351   for ( i = 0;  i < count;  ++i ) {
01352     kDebug() << "    -) RecurrenceRule: ";
01353     d->mRRules[i]->dump();
01354   }
01355   count = d->mExRules.count();
01356   kDebug() << "  -)" << count << "EXRULEs:";
01357   for ( i = 0;  i < count;  ++i ) {
01358     kDebug() << "    -) ExceptionRule :";
01359     d->mExRules[i]->dump();
01360   }
01361 
01362   count = d->mRDates.count();
01363   kDebug() << endl << "  -)" << count << "Recurrence Dates:";
01364   for ( i = 0;  i < count;  ++i ) {
01365     kDebug() << "    " << d->mRDates[i];
01366   }
01367   count = d->mRDateTimes.count();
01368   kDebug() << endl << "  -)" << count << "Recurrence Date/Times:";
01369   for ( i = 0;  i < count;  ++i ) {
01370     kDebug() << "    " << d->mRDateTimes[i].dateTime();
01371   }
01372   count = d->mExDates.count();
01373   kDebug() << endl << "  -)" << count << "Exceptions Dates:";
01374   for ( i = 0;  i < count;  ++i ) {
01375     kDebug() << "    " << d->mExDates[i];
01376   }
01377   count = d->mExDateTimes.count();
01378   kDebug() << endl << "  -)" << count << "Exception Date/Times:";
01379   for ( i = 0;  i < count;  ++i ) {
01380     kDebug() << "    " << d->mExDateTimes[i].dateTime();
01381   }
01382 }
01383 
01384 Recurrence::RecurrenceObserver::~RecurrenceObserver()
01385 {
01386 }

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

KDE-PIM Libraries

Skip menu "KDE-PIM Libraries"
  • akonadi
  •   contact
  •   kmime
  • kabc
  • kblog
  • kcal
  • kcalcore
  • kcalutils
  • kholidays
  • kimap
  • kioslave
  •   imap4
  •   mbox
  •   nntp
  • kldap
  • kmbox
  • kmime
  • kontactinterface
  • kpimidentities
  • kpimtextedit
  •   richtextbuilders
  • kpimutils
  • kresources
  • ktnef
  • kxmlrpcclient
  • mailtransport
  • microblog
  • qgpgme
  • syndication
  •   atom
  •   rdf
  •   rss2
Generated for KDE-PIM Libraries by doxygen 1.7.5
This website is maintained by Adriaan de Groot and Allen Winter.
KDE® and the K Desktop Environment® logo are registered trademarks of KDE e.V. | Legal