• Skip to content
  • Skip to link menu
KDE 4.4 API Reference
  • KDE API Reference
  • KDE-PIM Libraries
  • Sitemap
  • Contact Us
 

KCal Library

calendar.cpp

Go to the documentation of this file.
00001 /*
00002   This file is part of the kcal library.
00003 
00004   Copyright (c) 1998 Preston Brown <pbrown@kde.org>
00005   Copyright (c) 2000-2004 Cornelius Schumacher <schumacher@kde.org>
00006   Copyright (C) 2003-2004 Reinhold Kainhofer <reinhold@kainhofer.com>
00007   Copyright (c) 2006 David Jarvie <software@astrojar.org.uk>
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 */
00038 #include "calendar.h"
00039 #include "exceptions.h"
00040 #include "calfilter.h"
00041 #include "icaltimezones.h"
00042 #include <kdebug.h>
00043 #include <klocale.h>
00044 
00045 extern "C" {
00046   #include <icaltimezone.h>
00047 }
00048 
00049 using namespace KCal;
00050 
00055 //@cond PRIVATE
00056 class KCal::Calendar::Private
00057 {
00058   public:
00059     Private()
00060       : mTimeZones( new ICalTimeZones ),
00061         mModified( false ),
00062         mNewObserver( false ),
00063         mObserversEnabled( true ),
00064         mDefaultFilter( new CalFilter )
00065     {
00066       // Setup default filter, which does nothing
00067       mFilter = mDefaultFilter;
00068       mFilter->setEnabled( false );
00069 
00070       // user information...
00071       mOwner.setName( i18n( "Unknown Name" ) );
00072       mOwner.setEmail( i18n( "unknown@nowhere" ) );
00073     }
00074 
00075     ~Private()
00076     {
00077       delete mTimeZones;
00078       if ( mFilter != mDefaultFilter ) {
00079         delete mFilter;
00080       }
00081       delete mDefaultFilter;
00082     }
00083     KDateTime::Spec timeZoneIdSpec( const QString &timeZoneId, bool view );
00084 
00085     QString mProductId;
00086     Person mOwner;
00087     ICalTimeZones *mTimeZones; // collection of time zones used in this calendar
00088     ICalTimeZone mBuiltInTimeZone;   // cached time zone lookup
00089     ICalTimeZone mBuiltInViewTimeZone;   // cached viewing time zone lookup
00090     KDateTime::Spec mTimeSpec;
00091     mutable KDateTime::Spec mViewTimeSpec;
00092     bool mModified;
00093     bool mNewObserver;
00094     bool mObserversEnabled;
00095     QList<CalendarObserver*> mObservers;
00096 
00097     CalFilter *mDefaultFilter;
00098     CalFilter *mFilter;
00099 
00100     // These lists are used to put together related To-dos
00101     QMultiHash<QString, Incidence*> mOrphans;
00102     QMultiHash<QString, Incidence*> mOrphanUids;
00103 };
00104 //@endcond
00105 
00106 Calendar::Calendar( const KDateTime::Spec &timeSpec )
00107   : d( new KCal::Calendar::Private )
00108 {
00109   d->mTimeSpec = timeSpec;
00110   d->mViewTimeSpec = timeSpec;
00111 }
00112 
00113 Calendar::Calendar( const QString &timeZoneId )
00114   : d( new KCal::Calendar::Private )
00115 {
00116   setTimeZoneId( timeZoneId );
00117 }
00118 
00119 Calendar::~Calendar()
00120 {
00121   delete d;
00122 }
00123 
00124 Person Calendar::owner() const
00125 {
00126   return d->mOwner;
00127 }
00128 
00129 void Calendar::setOwner( const Person &owner )
00130 {
00131   d->mOwner = owner;
00132 
00133   setModified( true );
00134 }
00135 
00136 void Calendar::setTimeSpec( const KDateTime::Spec &timeSpec )
00137 {
00138   d->mTimeSpec = timeSpec;
00139   d->mBuiltInTimeZone = ICalTimeZone();
00140   setViewTimeSpec( timeSpec );
00141 
00142   doSetTimeSpec( d->mTimeSpec );
00143 }
00144 
00145 KDateTime::Spec Calendar::timeSpec() const
00146 {
00147   return d->mTimeSpec;
00148 }
00149 
00150 void Calendar::setTimeZoneId( const QString &timeZoneId )
00151 {
00152   d->mTimeSpec = d->timeZoneIdSpec( timeZoneId, false );
00153   d->mViewTimeSpec = d->mTimeSpec;
00154   d->mBuiltInViewTimeZone = d->mBuiltInTimeZone;
00155 
00156   doSetTimeSpec( d->mTimeSpec );
00157 }
00158 
00159 //@cond PRIVATE
00160 KDateTime::Spec Calendar::Private::timeZoneIdSpec( const QString &timeZoneId,
00161                                                    bool view )
00162 {
00163   if ( view ) {
00164     mBuiltInViewTimeZone = ICalTimeZone();
00165   } else {
00166     mBuiltInTimeZone = ICalTimeZone();
00167   }
00168   if ( timeZoneId == QLatin1String( "UTC" ) ) {
00169     return KDateTime::UTC;
00170   }
00171   ICalTimeZone tz = mTimeZones->zone( timeZoneId );
00172   if ( !tz.isValid() ) {
00173     ICalTimeZoneSource tzsrc;
00174     tz = tzsrc.parse( icaltimezone_get_builtin_timezone( timeZoneId.toLatin1() ) );
00175     if ( view ) {
00176       mBuiltInViewTimeZone = tz;
00177     } else {
00178       mBuiltInTimeZone = tz;
00179     }
00180   }
00181   if ( tz.isValid() ) {
00182     return tz;
00183   } else {
00184     return KDateTime::ClockTime;
00185   }
00186 }
00187 //@endcond
00188 
00189 QString Calendar::timeZoneId() const
00190 {
00191   KTimeZone tz = d->mTimeSpec.timeZone();
00192   return tz.isValid() ? tz.name() : QString();
00193 }
00194 
00195 void Calendar::setViewTimeSpec( const KDateTime::Spec &timeSpec ) const
00196 {
00197   d->mViewTimeSpec = timeSpec;
00198   d->mBuiltInViewTimeZone = ICalTimeZone();
00199 }
00200 
00201 void Calendar::setViewTimeZoneId( const QString &timeZoneId ) const
00202 {
00203   d->mViewTimeSpec = d->timeZoneIdSpec( timeZoneId, true );
00204 }
00205 
00206 KDateTime::Spec Calendar::viewTimeSpec() const
00207 {
00208   return d->mViewTimeSpec;
00209 }
00210 
00211 QString Calendar::viewTimeZoneId() const
00212 {
00213   KTimeZone tz = d->mViewTimeSpec.timeZone();
00214   return tz.isValid() ? tz.name() : QString();
00215 }
00216 
00217 ICalTimeZones *Calendar::timeZones() const
00218 {
00219   return d->mTimeZones;
00220 }
00221 
00222 void Calendar::shiftTimes( const KDateTime::Spec &oldSpec,
00223                            const KDateTime::Spec &newSpec )
00224 {
00225   setTimeSpec( newSpec );
00226 
00227   int i, end;
00228   Event::List ev = events();
00229   for ( i = 0, end = ev.count();  i < end;  ++i ) {
00230     ev[i]->shiftTimes( oldSpec, newSpec );
00231   }
00232 
00233   Todo::List to = todos();
00234   for ( i = 0, end = to.count();  i < end;  ++i ) {
00235     to[i]->shiftTimes( oldSpec, newSpec );
00236   }
00237 
00238   Journal::List jo = journals();
00239   for ( i = 0, end = jo.count();  i < end;  ++i ) {
00240     jo[i]->shiftTimes( oldSpec, newSpec );
00241   }
00242 }
00243 
00244 void Calendar::setFilter( CalFilter *filter )
00245 {
00246   if ( filter ) {
00247     d->mFilter = filter;
00248   } else {
00249     d->mFilter = d->mDefaultFilter;
00250   }
00251 }
00252 
00253 CalFilter *Calendar::filter()
00254 {
00255   return d->mFilter;
00256 }
00257 
00258 QStringList Calendar::categories()
00259 {
00260   Incidence::List rawInc( rawIncidences() );
00261   QStringList cats, thisCats;
00262   // @TODO: For now just iterate over all incidences. In the future,
00263   // the list of categories should be built when reading the file.
00264   for ( Incidence::List::ConstIterator i = rawInc.constBegin();
00265         i != rawInc.constEnd(); ++i ) {
00266     thisCats = (*i)->categories();
00267     for ( QStringList::ConstIterator si = thisCats.constBegin();
00268           si != thisCats.constEnd(); ++si ) {
00269       if ( !cats.contains( *si ) ) {
00270         cats.append( *si );
00271       }
00272     }
00273   }
00274   return cats;
00275 }
00276 
00277 Incidence::List Calendar::incidences( const QDate &date )
00278 {
00279   return mergeIncidenceList( events( date ), todos( date ), journals( date ) );
00280 }
00281 
00282 Incidence::List Calendar::incidences()
00283 {
00284   return mergeIncidenceList( events(), todos(), journals() );
00285 }
00286 
00287 Incidence::List Calendar::rawIncidences()
00288 {
00289   return mergeIncidenceList( rawEvents(), rawTodos(), rawJournals() );
00290 }
00291 
00292 Event::List Calendar::sortEvents( Event::List *eventList,
00293                                   EventSortField sortField,
00294                                   SortDirection sortDirection )
00295 {
00296   Event::List eventListSorted;
00297   Event::List tempList, t;
00298   Event::List alphaList;
00299   Event::List::Iterator sortIt;
00300   Event::List::Iterator eit;
00301 
00302   // Notice we alphabetically presort Summaries first.
00303   // We do this so comparison "ties" stay in a nice order.
00304 
00305   switch( sortField ) {
00306   case EventSortUnsorted:
00307     eventListSorted = *eventList;
00308     break;
00309 
00310   case EventSortStartDate:
00311     alphaList = sortEvents( eventList, EventSortSummary, sortDirection );
00312     for ( eit = alphaList.begin(); eit != alphaList.end(); ++eit ) {
00313       if ( (*eit)->dtStart().isDateOnly() ) {
00314         tempList.append( *eit );
00315         continue;
00316       }
00317       sortIt = eventListSorted.begin();
00318       if ( sortDirection == SortDirectionAscending ) {
00319         while ( sortIt != eventListSorted.end() &&
00320                 (*eit)->dtStart() >= (*sortIt)->dtStart() ) {
00321           ++sortIt;
00322         }
00323       } else {
00324         while ( sortIt != eventListSorted.end() &&
00325                 (*eit)->dtStart() < (*sortIt)->dtStart() ) {
00326           ++sortIt;
00327         }
00328       }
00329       eventListSorted.insert( sortIt, *eit );
00330     }
00331     if ( sortDirection == SortDirectionAscending ) {
00332       // Prepend the list of Events without End DateTimes
00333       tempList += eventListSorted;
00334       eventListSorted = tempList;
00335     } else {
00336       // Append the list of Events without End DateTimes
00337       eventListSorted += tempList;
00338     }
00339     break;
00340 
00341   case EventSortEndDate:
00342     alphaList = sortEvents( eventList, EventSortSummary, sortDirection );
00343     for ( eit = alphaList.begin(); eit != alphaList.end(); ++eit ) {
00344       if ( (*eit)->hasEndDate() ) {
00345         sortIt = eventListSorted.begin();
00346         if ( sortDirection == SortDirectionAscending ) {
00347           while ( sortIt != eventListSorted.end() &&
00348                   (*eit)->dtEnd() >= (*sortIt)->dtEnd() ) {
00349             ++sortIt;
00350           }
00351         } else {
00352           while ( sortIt != eventListSorted.end() &&
00353                   (*eit)->dtEnd() < (*sortIt)->dtEnd() ) {
00354             ++sortIt;
00355           }
00356         }
00357       } else {
00358         // Keep a list of the Events without End DateTimes
00359         tempList.append( *eit );
00360       }
00361       eventListSorted.insert( sortIt, *eit );
00362     }
00363     if ( sortDirection == SortDirectionAscending ) {
00364       // Append the list of Events without End DateTimes
00365       eventListSorted += tempList;
00366     } else {
00367       // Prepend the list of Events without End DateTimes
00368       tempList += eventListSorted;
00369       eventListSorted = tempList;
00370     }
00371     break;
00372 
00373   case EventSortSummary:
00374     for ( eit = eventList->begin(); eit != eventList->end(); ++eit ) {
00375       sortIt = eventListSorted.begin();
00376       if ( sortDirection == SortDirectionAscending ) {
00377         while ( sortIt != eventListSorted.end() &&
00378                 (*eit)->summary() >= (*sortIt)->summary() ) {
00379           ++sortIt;
00380         }
00381       } else {
00382         while ( sortIt != eventListSorted.end() &&
00383                 (*eit)->summary() < (*sortIt)->summary() ) {
00384           ++sortIt;
00385         }
00386       }
00387       eventListSorted.insert( sortIt, *eit );
00388     }
00389     break;
00390   }
00391 
00392   return eventListSorted;
00393 
00394 }
00395 
00396 Event::List Calendar::events( const QDate &date,
00397                               const KDateTime::Spec &timeSpec,
00398                               EventSortField sortField,
00399                               SortDirection sortDirection )
00400 {
00401   Event::List el = rawEventsForDate( date, timeSpec, sortField, sortDirection );
00402   d->mFilter->apply( &el );
00403   return el;
00404 }
00405 
00406 Event::List Calendar::events( const KDateTime &dt )
00407 {
00408   Event::List el = rawEventsForDate( dt );
00409   d->mFilter->apply( &el );
00410   return el;
00411 }
00412 
00413 Event::List Calendar::events( const QDate &start, const QDate &end,
00414                               const KDateTime::Spec &timeSpec,
00415                               bool inclusive )
00416 {
00417   Event::List el = rawEvents( start, end, timeSpec, inclusive );
00418   d->mFilter->apply( &el );
00419   return el;
00420 }
00421 
00422 Event::List Calendar::events( EventSortField sortField,
00423                               SortDirection sortDirection )
00424 {
00425   Event::List el = rawEvents( sortField, sortDirection );
00426   d->mFilter->apply( &el );
00427   return el;
00428 }
00429 
00430 bool Calendar::addIncidence( Incidence *incidence )
00431 {
00432   Incidence::AddVisitor<Calendar> v( this );
00433 
00434   return incidence->accept( v );
00435 }
00436 
00437 bool Calendar::deleteIncidence( Incidence *incidence )
00438 {
00439   if ( beginChange( incidence ) ) {
00440     Incidence::DeleteVisitor<Calendar> v( this );
00441     bool result = incidence->accept( v );
00442     endChange( incidence );
00443     return result;
00444   } else {
00445     return false;
00446   }
00447 }
00448 
00449 // Dissociate a single occurrence or all future occurrences from a recurring
00450 // sequence. The new incidence is returned, but not automatically inserted
00451 // into the calendar, which is left to the calling application.
00452 Incidence *Calendar::dissociateOccurrence( Incidence *incidence,
00453                                            const QDate &date,
00454                                            const KDateTime::Spec &spec,
00455                                            bool single )
00456 {
00457   if ( !incidence || !incidence->recurs() ) {
00458     return 0;
00459   }
00460 
00461   Incidence *newInc = incidence->clone();
00462   newInc->recreate();
00463   // Do not call setRelatedTo() when dissociating recurring to-dos, otherwise the new to-do
00464   // will appear as a child.  Originally, we planned to set a relation with reltype SIBLING
00465   // when dissociating to-dos, but currently kcal only supports reltype PARENT.
00466   // We can uncomment the following line when we support the PARENT reltype.
00467   //newInc->setRelatedTo( incidence );
00468   Recurrence *recur = newInc->recurrence();
00469   if ( single ) {
00470     recur->clear();
00471   } else {
00472     // Adjust the recurrence for the future incidences. In particular adjust
00473     // the "end after n occurrences" rules! "No end date" and "end by ..."
00474     // don't need to be modified.
00475     int duration = recur->duration();
00476     if ( duration > 0 ) {
00477       int doneduration = recur->durationTo( date.addDays( -1 ) );
00478       if ( doneduration >= duration ) {
00479         kDebug() << "The dissociated event already occurred more often"
00480                  << "than it was supposed to ever occur. ERROR!";
00481         recur->clear();
00482       } else {
00483         recur->setDuration( duration - doneduration );
00484       }
00485     }
00486   }
00487   // Adjust the date of the incidence
00488   if ( incidence->type() == "Event" ) {
00489     Event *ev = static_cast<Event *>( newInc );
00490     KDateTime start( ev->dtStart() );
00491     int daysTo = start.toTimeSpec( spec ).date().daysTo( date );
00492     ev->setDtStart( start.addDays( daysTo ) );
00493     ev->setDtEnd( ev->dtEnd().addDays( daysTo ) );
00494   } else if ( incidence->type() == "Todo" ) {
00495     Todo *td = static_cast<Todo *>( newInc );
00496     bool haveOffset = false;
00497     int daysTo = 0;
00498     if ( td->hasDueDate() ) {
00499       KDateTime due( td->dtDue() );
00500       daysTo = due.toTimeSpec( spec ).date().daysTo( date );
00501       td->setDtDue( due.addDays( daysTo ), true );
00502       haveOffset = true;
00503     }
00504     if ( td->hasStartDate() ) {
00505       KDateTime start( td->dtStart() );
00506       if ( !haveOffset ) {
00507         daysTo = start.toTimeSpec( spec ).date().daysTo( date );
00508       }
00509       td->setDtStart( start.addDays( daysTo ) );
00510       haveOffset = true;
00511     }
00512   }
00513   recur = incidence->recurrence();
00514   if ( recur ) {
00515     if ( single ) {
00516       recur->addExDate( date );
00517     } else {
00518       // Make sure the recurrence of the past events ends
00519       // at the corresponding day
00520       recur->setEndDate( date.addDays(-1) );
00521     }
00522   }
00523   return newInc;
00524 }
00525 
00526 Incidence *Calendar::incidence( const QString &uid )
00527 {
00528   Incidence *i = event( uid );
00529   if ( i ) {
00530     return i;
00531   }
00532 
00533   i = todo( uid );
00534   if ( i ) {
00535     return i;
00536   }
00537 
00538   i = journal( uid );
00539   return i;
00540 }
00541 
00542 Incidence::List Calendar::incidencesFromSchedulingID( const QString &sid )
00543 {
00544   Incidence::List result;
00545   const Incidence::List incidences = rawIncidences();
00546   Incidence::List::const_iterator it = incidences.begin();
00547   for ( ; it != incidences.end(); ++it ) {
00548     if ( (*it)->schedulingID() == sid ) {
00549       result.append( *it );
00550     }
00551   }
00552   return result;
00553 }
00554 
00555 Incidence *Calendar::incidenceFromSchedulingID( const QString &UID )
00556 {
00557   const Incidence::List incidences = rawIncidences();
00558   Incidence::List::const_iterator it = incidences.begin();
00559   for ( ; it != incidences.end(); ++it ) {
00560     if ( (*it)->schedulingID() == UID ) {
00561       // Touchdown, and the crowd goes wild
00562       return *it;
00563     }
00564   }
00565   // Not found
00566   return 0;
00567 }
00568 
00569 Todo::List Calendar::sortTodos( Todo::List *todoList,
00570                                 TodoSortField sortField,
00571                                 SortDirection sortDirection )
00572 {
00573   Todo::List todoListSorted;
00574   Todo::List tempList, t;
00575   Todo::List alphaList;
00576   Todo::List::Iterator sortIt;
00577   Todo::List::Iterator eit;
00578 
00579   // Notice we alphabetically presort Summaries first.
00580   // We do this so comparison "ties" stay in a nice order.
00581 
00582   // Note that To-dos may not have Start DateTimes nor due DateTimes.
00583 
00584   switch( sortField ) {
00585   case TodoSortUnsorted:
00586     todoListSorted = *todoList;
00587     break;
00588 
00589   case TodoSortStartDate:
00590     alphaList = sortTodos( todoList, TodoSortSummary, sortDirection );
00591     for ( eit = alphaList.begin(); eit != alphaList.end(); ++eit ) {
00592       if ( (*eit)->hasStartDate() ) {
00593         sortIt = todoListSorted.begin();
00594         if ( sortDirection == SortDirectionAscending ) {
00595           while ( sortIt != todoListSorted.end() &&
00596                   (*eit)->dtStart() >= (*sortIt)->dtStart() ) {
00597             ++sortIt;
00598           }
00599         } else {
00600           while ( sortIt != todoListSorted.end() &&
00601                   (*eit)->dtStart() < (*sortIt)->dtStart() ) {
00602             ++sortIt;
00603           }
00604         }
00605         todoListSorted.insert( sortIt, *eit );
00606       } else {
00607         // Keep a list of the To-dos without Start DateTimes
00608         tempList.append( *eit );
00609       }
00610     }
00611     if ( sortDirection == SortDirectionAscending ) {
00612       // Append the list of To-dos without Start DateTimes
00613       todoListSorted += tempList;
00614     } else {
00615       // Prepend the list of To-dos without Start DateTimes
00616       tempList += todoListSorted;
00617       todoListSorted = tempList;
00618     }
00619     break;
00620 
00621   case TodoSortDueDate:
00622     alphaList = sortTodos( todoList, TodoSortSummary, sortDirection );
00623     for ( eit = alphaList.begin(); eit != alphaList.end(); ++eit ) {
00624       if ( (*eit)->hasDueDate() ) {
00625         sortIt = todoListSorted.begin();
00626         if ( sortDirection == SortDirectionAscending ) {
00627           while ( sortIt != todoListSorted.end() &&
00628                   (*eit)->dtDue() >= (*sortIt)->dtDue() ) {
00629             ++sortIt;
00630           }
00631         } else {
00632           while ( sortIt != todoListSorted.end() &&
00633                   (*eit)->dtDue() < (*sortIt)->dtDue() ) {
00634             ++sortIt;
00635           }
00636         }
00637         todoListSorted.insert( sortIt, *eit );
00638       } else {
00639         // Keep a list of the To-dos without Due DateTimes
00640         tempList.append( *eit );
00641       }
00642     }
00643     if ( sortDirection == SortDirectionAscending ) {
00644       // Append the list of To-dos without Due DateTimes
00645       todoListSorted += tempList;
00646     } else {
00647       // Prepend the list of To-dos without Due DateTimes
00648       tempList += todoListSorted;
00649       todoListSorted = tempList;
00650     }
00651     break;
00652 
00653   case TodoSortPriority:
00654     alphaList = sortTodos( todoList, TodoSortSummary, sortDirection );
00655     for ( eit = alphaList.begin(); eit != alphaList.end(); ++eit ) {
00656       sortIt = todoListSorted.begin();
00657       if ( sortDirection == SortDirectionAscending ) {
00658         while ( sortIt != todoListSorted.end() &&
00659                 (*eit)->priority() >= (*sortIt)->priority() ) {
00660           ++sortIt;
00661         }
00662       } else {
00663         while ( sortIt != todoListSorted.end() &&
00664                 (*eit)->priority() < (*sortIt)->priority() ) {
00665           ++sortIt;
00666         }
00667       }
00668       todoListSorted.insert( sortIt, *eit );
00669     }
00670     break;
00671 
00672   case TodoSortPercentComplete:
00673     alphaList = sortTodos( todoList, TodoSortSummary, sortDirection );
00674     for ( eit = alphaList.begin(); eit != alphaList.end(); ++eit ) {
00675       sortIt = todoListSorted.begin();
00676       if ( sortDirection == SortDirectionAscending ) {
00677         while ( sortIt != todoListSorted.end() &&
00678                 (*eit)->percentComplete() >= (*sortIt)->percentComplete() ) {
00679           ++sortIt;
00680         }
00681       } else {
00682         while ( sortIt != todoListSorted.end() &&
00683                 (*eit)->percentComplete() < (*sortIt)->percentComplete() ) {
00684           ++sortIt;
00685         }
00686       }
00687       todoListSorted.insert( sortIt, *eit );
00688     }
00689     break;
00690 
00691   case TodoSortSummary:
00692     for ( eit = todoList->begin(); eit != todoList->end(); ++eit ) {
00693       sortIt = todoListSorted.begin();
00694       if ( sortDirection == SortDirectionAscending ) {
00695         while ( sortIt != todoListSorted.end() &&
00696                 (*eit)->summary() >= (*sortIt)->summary() ) {
00697           ++sortIt;
00698         }
00699       } else {
00700         while ( sortIt != todoListSorted.end() &&
00701                 (*eit)->summary() < (*sortIt)->summary() ) {
00702           ++sortIt;
00703         }
00704       }
00705       todoListSorted.insert( sortIt, *eit );
00706     }
00707     break;
00708   }
00709 
00710   return todoListSorted;
00711 }
00712 
00713 Todo::List Calendar::todos( TodoSortField sortField,
00714                             SortDirection sortDirection )
00715 {
00716   Todo::List tl = rawTodos( sortField, sortDirection );
00717   d->mFilter->apply( &tl );
00718   return tl;
00719 }
00720 
00721 Todo::List Calendar::todos( const QDate &date )
00722 {
00723   Todo::List el = rawTodosForDate( date );
00724   d->mFilter->apply( &el );
00725   return el;
00726 }
00727 
00728 Journal::List Calendar::sortJournals( Journal::List *journalList,
00729                                       JournalSortField sortField,
00730                                       SortDirection sortDirection )
00731 {
00732   Journal::List journalListSorted;
00733   Journal::List::Iterator sortIt;
00734   Journal::List::Iterator eit;
00735 
00736   switch( sortField ) {
00737   case JournalSortUnsorted:
00738     journalListSorted = *journalList;
00739     break;
00740 
00741   case JournalSortDate:
00742     for ( eit = journalList->begin(); eit != journalList->end(); ++eit ) {
00743       sortIt = journalListSorted.begin();
00744       if ( sortDirection == SortDirectionAscending ) {
00745         while ( sortIt != journalListSorted.end() &&
00746                 (*eit)->dtStart() >= (*sortIt)->dtStart() ) {
00747           ++sortIt;
00748         }
00749       } else {
00750         while ( sortIt != journalListSorted.end() &&
00751                 (*eit)->dtStart() < (*sortIt)->dtStart() ) {
00752           ++sortIt;
00753         }
00754       }
00755       journalListSorted.insert( sortIt, *eit );
00756     }
00757     break;
00758 
00759   case JournalSortSummary:
00760     for ( eit = journalList->begin(); eit != journalList->end(); ++eit ) {
00761       sortIt = journalListSorted.begin();
00762       if ( sortDirection == SortDirectionAscending ) {
00763         while ( sortIt != journalListSorted.end() &&
00764                 (*eit)->summary() >= (*sortIt)->summary() ) {
00765           ++sortIt;
00766         }
00767       } else {
00768         while ( sortIt != journalListSorted.end() &&
00769                 (*eit)->summary() < (*sortIt)->summary() ) {
00770           ++sortIt;
00771         }
00772       }
00773       journalListSorted.insert( sortIt, *eit );
00774     }
00775     break;
00776   }
00777 
00778   return journalListSorted;
00779 }
00780 
00781 Journal::List Calendar::journals( JournalSortField sortField,
00782                                   SortDirection sortDirection )
00783 {
00784   Journal::List jl = rawJournals( sortField, sortDirection );
00785   d->mFilter->apply( &jl );
00786   return jl;
00787 }
00788 
00789 Journal::List Calendar::journals( const QDate &date )
00790 {
00791   Journal::List el = rawJournalsForDate( date );
00792   d->mFilter->apply( &el );
00793   return el;
00794 }
00795 
00796 void Calendar::beginBatchAdding()
00797 {
00798   emit batchAddingBegins();
00799 }
00800 
00801 void Calendar::endBatchAdding()
00802 {
00803   emit batchAddingEnds();
00804 }
00805 
00806 // When this is called, the to-dos have already been added to the calendar.
00807 // This method is only about linking related to-dos.
00808 void Calendar::setupRelations( Incidence *forincidence )
00809 {
00810   if ( !forincidence ) {
00811     return;
00812   }
00813 
00814   QString uid = forincidence->uid();
00815 
00816   // First, go over the list of orphans and see if this is their parent
00817   QList<Incidence*> l = d->mOrphans.values( uid );
00818   d->mOrphans.remove( uid );
00819   for ( int i = 0, end = l.count();  i < end;  ++i ) {
00820     l[i]->setRelatedTo( forincidence );
00821     forincidence->addRelation( l[i] );
00822     d->mOrphanUids.remove( l[i]->uid() );
00823   }
00824 
00825   // Now see about this incidences parent
00826   if ( !forincidence->relatedTo() && !forincidence->relatedToUid().isEmpty() ) {
00827     // Incidence has a uid it is related to but is not registered to it yet.
00828     // Try to find it
00829     Incidence *parent = incidence( forincidence->relatedToUid() );
00830     if ( parent ) {
00831       // Found it
00832       forincidence->setRelatedTo( parent );
00833       parent->addRelation( forincidence );
00834     } else {
00835       // Not found, put this in the mOrphans list
00836       // Note that the mOrphans dict might contain multiple entries with the
00837       // same key! which are multiple children that wait for the parent
00838       // incidence to be inserted.
00839       d->mOrphans.insert( forincidence->relatedToUid(), forincidence );
00840       d->mOrphanUids.insert( forincidence->uid(), forincidence );
00841     }
00842   }
00843 }
00844 
00845 // If a to-do with sub-to-dos is deleted, move it's sub-to-dos to the orphan list
00846 void Calendar::removeRelations( Incidence *incidence )
00847 {
00848   if ( !incidence ) {
00849     kDebug() << "Warning: incidence is 0";
00850     return;
00851   }
00852 
00853   QString uid = incidence->uid();
00854   foreach ( Incidence *i, incidence->relations() ) {
00855     if ( !d->mOrphanUids.contains( i->uid() ) ) {
00856       d->mOrphans.insert( uid, i );
00857       d->mOrphanUids.insert( i->uid(), i );
00858       i->setRelatedTo( 0 );
00859       i->setRelatedToUid( uid );
00860     }
00861   }
00862 
00863   // If this incidence is related to something else, tell that about it
00864   if ( incidence->relatedTo() ) {
00865     incidence->relatedTo()->removeRelation( incidence );
00866   }
00867 
00868   // Remove this one from the orphans list
00869   if ( d->mOrphanUids.remove( uid ) ) {
00870     // This incidence is located in the orphans list - it should be removed
00871     // Since the mOrphans dict might contain the same key (with different
00872     // child incidence pointers!) multiple times, take care that we remove
00873     // the correct one. So we need to remove all items with the given
00874     // parent UID, and readd those that are not for this item. Also, there
00875     // might be other entries with differnet UID that point to this
00876     // incidence (this might happen when the relatedTo of the item is
00877     // changed before its parent is inserted. This might happen with
00878     // groupware servers....). Remove them, too
00879     QStringList relatedToUids;
00880 
00881     // First, create a list of all keys in the mOrphans list which point
00882     // to the removed item
00883     relatedToUids << incidence->relatedToUid();
00884     for ( QMultiHash<QString, Incidence*>::Iterator it = d->mOrphans.begin();
00885           it != d->mOrphans.end(); ++it ) {
00886       if ( it.value()->uid() == uid ) {
00887         relatedToUids << it.key();
00888       }
00889     }
00890 
00891     // now go through all uids that have one entry that point to the incidence
00892     for ( QStringList::const_iterator uidit = relatedToUids.constBegin();
00893           uidit != relatedToUids.constEnd(); ++uidit ) {
00894       Incidence::List tempList;
00895       // Remove all to get access to the remaining entries
00896       QList<Incidence*> l = d->mOrphans.values( *uidit );
00897       d->mOrphans.remove( *uidit );
00898       foreach ( Incidence *i, l ) {
00899         if ( i != incidence ) {
00900           tempList.append( i );
00901         }
00902       }
00903       // Readd those that point to a different orphan incidence
00904       for ( Incidence::List::Iterator incit = tempList.begin();
00905             incit != tempList.end(); ++incit ) {
00906         d->mOrphans.insert( *uidit, *incit );
00907       }
00908     }
00909   }
00910 
00911   // Make sure the deleted incidence doesn't relate to a non-deleted incidence,
00912   // since that would cause trouble in CalendarLocal::close(), as the deleted
00913   // incidences are destroyed after the non-deleted incidences. The destructor
00914   // of the deleted incidences would then try to access the already destroyed
00915   // non-deleted incidence, which would segfault.
00916   //
00917   // So in short: Make sure dead incidences don't point to alive incidences
00918   // via the relation.
00919   //
00920   // This crash is tested in CalendarLocalTest::testRelationsCrash().
00921   incidence->setRelatedTo( 0 );
00922 }
00923 
00924 void Calendar::CalendarObserver::calendarModified( bool modified, Calendar *calendar )
00925 {
00926   Q_UNUSED( modified );
00927   Q_UNUSED( calendar );
00928 }
00929 
00930 void Calendar::CalendarObserver::calendarIncidenceAdded( Incidence *incidence )
00931 {
00932   Q_UNUSED( incidence );
00933 }
00934 
00935 void Calendar::CalendarObserver::calendarIncidenceChanged( Incidence *incidence )
00936 {
00937   Q_UNUSED( incidence );
00938 }
00939 
00940 void Calendar::CalendarObserver::calendarIncidenceDeleted( Incidence *incidence )
00941 {
00942   Q_UNUSED( incidence );
00943 }
00944 
00945 void Calendar::registerObserver( CalendarObserver *observer )
00946 {
00947   if ( !d->mObservers.contains( observer ) ) {
00948     d->mObservers.append( observer );
00949   }
00950   d->mNewObserver = true;
00951 }
00952 
00953 void Calendar::unregisterObserver( CalendarObserver *observer )
00954 {
00955   d->mObservers.removeAll( observer );
00956 }
00957 
00958 bool Calendar::isSaving()
00959 {
00960   return false;
00961 }
00962 
00963 void Calendar::setModified( bool modified )
00964 {
00965   if ( modified != d->mModified || d->mNewObserver ) {
00966     d->mNewObserver = false;
00967     foreach ( CalendarObserver *observer, d->mObservers ) {
00968       observer->calendarModified( modified, this );
00969     }
00970     d->mModified = modified;
00971   }
00972 }
00973 
00974 bool Calendar::isModified() const
00975 {
00976   return d->mModified;
00977 }
00978 
00979 void Calendar::incidenceUpdated( IncidenceBase *incidence )
00980 {
00981   incidence->setLastModified( KDateTime::currentUtcDateTime() );
00982   // we should probably update the revision number here,
00983   // or internally in the Event itself when certain things change.
00984   // need to verify with ical documentation.
00985 
00986   // The static_cast is ok as the CalendarLocal only observes Incidence objects
00987   notifyIncidenceChanged( static_cast<Incidence *>( incidence ) );
00988 
00989   setModified( true );
00990 }
00991 
00992 void Calendar::doSetTimeSpec( const KDateTime::Spec &timeSpec )
00993 {
00994   Q_UNUSED( timeSpec );
00995 }
00996 
00997 void Calendar::notifyIncidenceAdded( Incidence *i )
00998 {
00999   if ( !d->mObserversEnabled ) {
01000     return;
01001   }
01002 
01003   foreach ( CalendarObserver *observer, d->mObservers ) {
01004     observer->calendarIncidenceAdded( i );
01005   }
01006 }
01007 
01008 void Calendar::notifyIncidenceChanged( Incidence *i )
01009 {
01010   if ( !d->mObserversEnabled ) {
01011     return;
01012   }
01013 
01014   foreach ( CalendarObserver *observer, d->mObservers ) {
01015     observer->calendarIncidenceChanged( i );
01016   }
01017 }
01018 
01019 void Calendar::notifyIncidenceDeleted( Incidence *i )
01020 {
01021   if ( !d->mObserversEnabled ) {
01022     return;
01023   }
01024 
01025   foreach ( CalendarObserver *observer, d->mObservers ) {
01026     observer->calendarIncidenceDeleted( i );
01027   }
01028 }
01029 
01030 void Calendar::customPropertyUpdated()
01031 {
01032   setModified( true );
01033 }
01034 
01035 void Calendar::setProductId( const QString &id )
01036 {
01037   d->mProductId = id;
01038 }
01039 
01040 QString Calendar::productId() const
01041 {
01042   return d->mProductId;
01043 }
01044 
01045 Incidence::List Calendar::mergeIncidenceList( const Event::List &events,
01046                                               const Todo::List &todos,
01047                                               const Journal::List &journals )
01048 {
01049   Incidence::List incidences;
01050 
01051   int i, end;
01052   for ( i = 0, end = events.count();  i < end;  ++i ) {
01053     incidences.append( events[i] );
01054   }
01055 
01056   for ( i = 0, end = todos.count();  i < end;  ++i ) {
01057     incidences.append( todos[i] );
01058   }
01059 
01060   for ( i = 0, end = journals.count();  i < end;  ++i ) {
01061     incidences.append( journals[i] );
01062   }
01063 
01064   return incidences;
01065 }
01066 
01067 bool Calendar::beginChange( Incidence *incidence )
01068 {
01069   Q_UNUSED( incidence );
01070   return true;
01071 }
01072 
01073 bool Calendar::endChange( Incidence *incidence )
01074 {
01075   Q_UNUSED( incidence );
01076   return true;
01077 }
01078 
01079 void Calendar::setObserversEnabled( bool enabled )
01080 {
01081   d->mObserversEnabled = enabled;
01082 }
01083 
01084 void Calendar::appendAlarms( Alarm::List &alarms, Incidence *incidence,
01085                              const KDateTime &from, const KDateTime &to )
01086 {
01087   KDateTime preTime = from.addSecs(-1);
01088 
01089   Alarm::List alarmlist = incidence->alarms();
01090   for ( int i = 0, iend = alarmlist.count();  i < iend;  ++i ) {
01091     if ( alarmlist[i]->enabled() ) {
01092       KDateTime dt = alarmlist[i]->nextRepetition( preTime );
01093       if ( dt.isValid() && dt <= to ) {
01094         kDebug() << incidence->summary() << "':" << dt.toString();
01095         alarms.append( alarmlist[i] );
01096       }
01097     }
01098   }
01099 }
01100 
01101 void Calendar::appendRecurringAlarms( Alarm::List &alarms,
01102                                       Incidence *incidence,
01103                                       const KDateTime &from,
01104                                       const KDateTime &to )
01105 {
01106   KDateTime dt;
01107   bool endOffsetValid = false;
01108   Duration endOffset( 0 );
01109   Duration period( from, to );
01110 
01111   Alarm::List alarmlist = incidence->alarms();
01112   for ( int i = 0, iend = alarmlist.count();  i < iend;  ++i ) {
01113     Alarm *a = alarmlist[i];
01114     if ( a->enabled() ) {
01115       if ( a->hasTime() ) {
01116         // The alarm time is defined as an absolute date/time
01117         dt = a->nextRepetition( from.addSecs(-1) );
01118         if ( !dt.isValid() || dt > to ) {
01119           continue;
01120         }
01121       } else {
01122         // Alarm time is defined by an offset from the event start or end time.
01123         // Find the offset from the event start time, which is also used as the
01124         // offset from the recurrence time.
01125         Duration offset( 0 );
01126         if ( a->hasStartOffset() ) {
01127           offset = a->startOffset();
01128         } else if ( a->hasEndOffset() ) {
01129           offset = a->endOffset();
01130           if ( !endOffsetValid ) {
01131             endOffset = Duration( incidence->dtStart(), incidence->dtEnd() );
01132             endOffsetValid = true;
01133           }
01134         }
01135 
01136         // Find the incidence's earliest alarm
01137         KDateTime alarmStart =
01138           offset.end( a->hasEndOffset() ? incidence->dtEnd() : incidence->dtStart() );
01139 //        KDateTime alarmStart = incidence->dtStart().addSecs( offset );
01140         if ( alarmStart > to ) {
01141           continue;
01142         }
01143         KDateTime baseStart = incidence->dtStart();
01144         if ( from > alarmStart ) {
01145           alarmStart = from;   // don't look earlier than the earliest alarm
01146           baseStart = (-offset).end( (-endOffset).end( alarmStart ) );
01147         }
01148 
01149         // Adjust the 'alarmStart' date/time and find the next recurrence at or after it.
01150         // Treate the two offsets separately in case one is daily and the other not.
01151         dt = incidence->recurrence()->getNextDateTime( baseStart.addSecs(-1) );
01152         if ( !dt.isValid() ||
01153              ( dt = endOffset.end( offset.end( dt ) ) ) > to ) // adjust 'dt' to get the alarm time
01154         {
01155           // The next recurrence is too late.
01156           if ( !a->repeatCount() ) {
01157             continue;
01158           }
01159 
01160           // The alarm has repetitions, so check whether repetitions of previous
01161           // recurrences fall within the time period.
01162           bool found = false;
01163           Duration alarmDuration = a->duration();
01164           for ( KDateTime base = baseStart;
01165                 ( dt = incidence->recurrence()->getPreviousDateTime( base ) ).isValid();
01166                 base = dt ) {
01167             if ( a->duration().end( dt ) < base ) {
01168               break;  // this recurrence's last repetition is too early, so give up
01169             }
01170 
01171             // The last repetition of this recurrence is at or after 'alarmStart' time.
01172             // Check if a repetition occurs between 'alarmStart' and 'to'.
01173             int snooze = a->snoozeTime().value();   // in seconds or days
01174             if ( a->snoozeTime().isDaily() ) {
01175               Duration toFromDuration( dt, base );
01176               int toFrom = toFromDuration.asDays();
01177               if ( a->snoozeTime().end( from ) <= to ||
01178                    ( toFromDuration.isDaily() && toFrom % snooze == 0 ) ||
01179                    ( toFrom / snooze + 1 ) * snooze <= toFrom + period.asDays() ) {
01180                 found = true;
01181 #ifndef NDEBUG
01182                 // for debug output
01183                 dt = offset.end( dt ).addDays( ( ( toFrom - 1 ) / snooze + 1 ) * snooze );
01184 #endif
01185                 break;
01186               }
01187             } else {
01188               int toFrom = dt.secsTo( base );
01189               if ( period.asSeconds() >= snooze ||
01190                    toFrom % snooze == 0 ||
01191                    ( toFrom / snooze + 1 ) * snooze <= toFrom + period.asSeconds() )
01192               {
01193                 found = true;
01194 #ifndef NDEBUG
01195                 // for debug output
01196                 dt = offset.end( dt ).addSecs( ( ( toFrom - 1 ) / snooze + 1 ) * snooze );
01197 #endif
01198                 break;
01199               }
01200             }
01201           }
01202           if ( !found ) {
01203             continue;
01204           }
01205         }
01206       }
01207       kDebug() << incidence->summary() << "':" << dt.toString();
01208       alarms.append( a );
01209     }
01210   }
01211 }
01212 
01213 #include "calendar.moc"

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