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

KCal Library

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

KDE-PIM Libraries

Skip menu "KDE-PIM Libraries"
  • akonadi
  •   contact
  •   kmime
  • kabc
  • kblog
  • kcal
  • kholidays
  • kimap
  • kioslave
  •   imap4
  •   mbox
  •   nntp
  • kldap
  • kmime
  • kontactinterface
  • kpimidentities
  • kpimtextedit
  •   richtextbuilders
  • kpimutils
  • kresources
  • ktnef
  • kxmlrpcclient
  • mailtransport
  • microblog
  • qgpgme
  • syndication
  •   atom
  •   rdf
  •   rss2
Generated for KDE-PIM Libraries by doxygen 1.6.1
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