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

KCal Library

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

KDE's Doxygen guidelines are available online.

KCal Library

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

kdepimlibs-4.8.3 API Reference

Skip menu "kdepimlibs-4.8.3 API Reference"
  • akonadi
  •   contact
  •   kmime
  • kabc
  • kalarmcal
  • 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
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