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

KCalCore Library

calendar.cpp
Go to the documentation of this file.
00001 /*
00002   This file is part of the kcalcore 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 */
00037 #include "calendar.h"
00038 #include "calfilter.h"
00039 #include "icaltimezones.h"
00040 #include "sorting.h"
00041 #include "visitor.h"
00042 
00043 #include <KDebug>
00044 
00045 extern "C" {
00046   #include <icaltimezone.h>
00047 }
00048 
00049 #include <algorithm>  // for std::remove()
00050 
00051 using namespace KCalCore;
00052 
00057 //@cond PRIVATE
00058 class KCalCore::Calendar::Private
00059 {
00060   public:
00061     Private()
00062       : mTimeZones( new ICalTimeZones ),
00063         mModified( false ),
00064         mNewObserver( false ),
00065         mObserversEnabled( true ),
00066         mDefaultFilter( new CalFilter ),
00067         batchAddingInProgress( false )
00068     {
00069       // Setup default filter, which does nothing
00070       mFilter = mDefaultFilter;
00071       mFilter->setEnabled( false );
00072 
00073       mOwner = Person::Ptr( new Person() );
00074       mOwner->setName( "Unknown Name" );
00075       mOwner->setEmail( "unknown@nowhere" );
00076     }
00077 
00078     ~Private()
00079     {
00080       delete mTimeZones;
00081       mTimeZones = 0;
00082       if ( mFilter != mDefaultFilter ) {
00083         delete mFilter;
00084       }
00085       delete mDefaultFilter;
00086     }
00087     KDateTime::Spec timeZoneIdSpec( const QString &timeZoneId, bool view );
00088 
00089     QString mProductId;
00090     Person::Ptr mOwner;
00091     ICalTimeZones *mTimeZones; // collection of time zones used in this calendar
00092     ICalTimeZone mBuiltInTimeZone;   // cached time zone lookup
00093     ICalTimeZone mBuiltInViewTimeZone;   // cached viewing time zone lookup
00094     KDateTime::Spec mTimeSpec;
00095     mutable KDateTime::Spec mViewTimeSpec;
00096     bool mModified;
00097     bool mNewObserver;
00098     bool mObserversEnabled;
00099     QList<CalendarObserver*> mObservers;
00100 
00101     CalFilter *mDefaultFilter;
00102     CalFilter *mFilter;
00103 
00104     // These lists are used to put together related To-dos
00105     QMultiHash<QString, Incidence::Ptr> mOrphans;
00106     QMultiHash<QString, Incidence::Ptr> mOrphanUids;
00107 
00108     // Lists for associating incidences to notebooks
00109     QMultiHash<QString, Incidence::Ptr >mNotebookIncidences;
00110     QHash<QString, QString>mUidToNotebook;
00111     QHash<QString, bool>mNotebooks; // name to visibility
00112     QHash<Incidence::Ptr, bool>mIncidenceVisibility; // incidence -> visibility
00113     QString mDefaultNotebook; // uid of default notebook
00114     QMap<QString, Incidence::List > mIncidenceRelations;
00115     bool batchAddingInProgress;
00116 
00117 };
00118 
00122 template <typename K, typename V>
00123 QVector<V> values( const QMultiHash<K,V> &c )
00124 {
00125   QVector<V> v;
00126   v.reserve( c.size() );
00127   for ( typename QMultiHash<K,V>::const_iterator it = c.begin(), end = c.end(); it != end; ++it ) {
00128     v.push_back( it.value() );
00129   }
00130   return v;
00131 }
00132 
00133 template <typename K, typename V>
00134 QVector<V> values( const QMultiHash<K,V> &c, const K &x )
00135 {
00136   QVector<V> v;
00137   typename QMultiHash<K,V>::const_iterator it = c.find( x );
00138   while ( it != c.end() && it.key() == x ) {
00139     v.push_back( it.value() );
00140     ++it;
00141   }
00142   return v;
00143 }
00144 
00149 template<class T>
00150 class AddVisitor : public Visitor
00151 {
00152   public:
00153     AddVisitor( T *r ) : mResource( r ) {}
00154 
00155     bool visit( Event::Ptr e )
00156     {
00157       return mResource->addEvent( e );
00158     }
00159     bool visit( Todo::Ptr t )
00160     {
00161       return mResource->addTodo( t );
00162     }
00163     bool visit( Journal::Ptr j )
00164     {
00165       return mResource->addJournal( j );
00166     }
00167     bool visit( FreeBusy::Ptr )
00168     {
00169       return false;
00170     }
00171 
00172   private:
00173     T *mResource;
00174 };
00175 
00181 template<class T>
00182 class DeleteVisitor : public Visitor
00183 {
00184   public:
00185     DeleteVisitor( T *r ) : mResource( r ) {}
00186 
00187     bool visit( Event::Ptr e )
00188     {
00189       mResource->deleteEvent( e );
00190       return true;
00191     }
00192     bool visit( Todo::Ptr t )
00193     {
00194       mResource->deleteTodo( t );
00195       return true;
00196     }
00197     bool visit( Journal::Ptr j )
00198     {
00199       mResource->deleteJournal( j );
00200       return true;
00201     }
00202     bool visit( FreeBusy::Ptr )
00203     {
00204       return false;
00205     }
00206 
00207   private:
00208     T *mResource;
00209 };
00210 //@endcond
00211 
00212 Calendar::Calendar( const KDateTime::Spec &timeSpec )
00213   : d( new KCalCore::Calendar::Private )
00214 {
00215   d->mTimeSpec = timeSpec;
00216   d->mViewTimeSpec = timeSpec;
00217 }
00218 
00219 Calendar::Calendar( const QString &timeZoneId )
00220   : d( new KCalCore::Calendar::Private )
00221 {
00222   setTimeZoneId( timeZoneId );
00223 }
00224 
00225 Calendar::~Calendar()
00226 {
00227   delete d;
00228 }
00229 
00230 Person::Ptr Calendar::owner() const
00231 {
00232   return d->mOwner;
00233 }
00234 
00235 void Calendar::setOwner( const Person::Ptr &owner )
00236 {
00237   Q_ASSERT( owner );
00238   d->mOwner = owner;
00239   setModified( true );
00240 }
00241 
00242 void Calendar::setTimeSpec( const KDateTime::Spec &timeSpec )
00243 {
00244   d->mTimeSpec = timeSpec;
00245   d->mBuiltInTimeZone = ICalTimeZone();
00246   setViewTimeSpec( timeSpec );
00247 
00248   doSetTimeSpec( d->mTimeSpec );
00249 }
00250 
00251 KDateTime::Spec Calendar::timeSpec() const
00252 {
00253   return d->mTimeSpec;
00254 }
00255 
00256 void Calendar::setTimeZoneId( const QString &timeZoneId )
00257 {
00258   d->mTimeSpec = d->timeZoneIdSpec( timeZoneId, false );
00259   d->mViewTimeSpec = d->mTimeSpec;
00260   d->mBuiltInViewTimeZone = d->mBuiltInTimeZone;
00261 
00262   doSetTimeSpec( d->mTimeSpec );
00263 }
00264 
00265 //@cond PRIVATE
00266 KDateTime::Spec Calendar::Private::timeZoneIdSpec( const QString &timeZoneId,
00267                                                    bool view )
00268 {
00269   if ( view ) {
00270     mBuiltInViewTimeZone = ICalTimeZone();
00271   } else {
00272     mBuiltInTimeZone = ICalTimeZone();
00273   }
00274   if ( timeZoneId == QLatin1String( "UTC" ) ) {
00275     return KDateTime::UTC;
00276   }
00277   ICalTimeZone tz = mTimeZones->zone( timeZoneId );
00278   if ( !tz.isValid() ) {
00279     ICalTimeZoneSource tzsrc;
00280     tz = tzsrc.parse( icaltimezone_get_builtin_timezone( timeZoneId.toLatin1() ) );
00281     if ( view ) {
00282       mBuiltInViewTimeZone = tz;
00283     } else {
00284       mBuiltInTimeZone = tz;
00285     }
00286   }
00287   if ( tz.isValid() ) {
00288     return tz;
00289   } else {
00290     return KDateTime::ClockTime;
00291   }
00292 }
00293 //@endcond
00294 
00295 QString Calendar::timeZoneId() const
00296 {
00297   KTimeZone tz = d->mTimeSpec.timeZone();
00298   return tz.isValid() ? tz.name() : QString();
00299 }
00300 
00301 void Calendar::setViewTimeSpec( const KDateTime::Spec &timeSpec ) const
00302 {
00303   d->mViewTimeSpec = timeSpec;
00304   d->mBuiltInViewTimeZone = ICalTimeZone();
00305 }
00306 
00307 void Calendar::setViewTimeZoneId( const QString &timeZoneId ) const
00308 {
00309   d->mViewTimeSpec = d->timeZoneIdSpec( timeZoneId, true );
00310 }
00311 
00312 KDateTime::Spec Calendar::viewTimeSpec() const
00313 {
00314   return d->mViewTimeSpec;
00315 }
00316 
00317 QString Calendar::viewTimeZoneId() const
00318 {
00319   KTimeZone tz = d->mViewTimeSpec.timeZone();
00320   return tz.isValid() ? tz.name() : QString();
00321 }
00322 
00323 ICalTimeZones *Calendar::timeZones() const
00324 {
00325   return d->mTimeZones;
00326 }
00327 
00328 void Calendar::setTimeZones( ICalTimeZones *zones )
00329 {
00330   if ( !zones ) {
00331     return;
00332   }
00333 
00334   if ( d->mTimeZones && ( d->mTimeZones != zones ) ) {
00335     delete d->mTimeZones;
00336     d->mTimeZones = 0;
00337   }
00338   d->mTimeZones = zones;
00339 }
00340 
00341 void Calendar::shiftTimes( const KDateTime::Spec &oldSpec, const KDateTime::Spec &newSpec )
00342 {
00343   setTimeSpec( newSpec );
00344 
00345   int i, end;
00346   Event::List ev = events();
00347   for ( i = 0, end = ev.count();  i < end;  ++i ) {
00348     ev[i]->shiftTimes( oldSpec, newSpec );
00349   }
00350 
00351   Todo::List to = todos();
00352   for ( i = 0, end = to.count();  i < end;  ++i ) {
00353     to[i]->shiftTimes( oldSpec, newSpec );
00354   }
00355 
00356   Journal::List jo = journals();
00357   for ( i = 0, end = jo.count();  i < end;  ++i ) {
00358     jo[i]->shiftTimes( oldSpec, newSpec );
00359   }
00360 }
00361 
00362 void Calendar::setFilter( CalFilter *filter )
00363 {
00364   if ( filter ) {
00365     d->mFilter = filter;
00366   } else {
00367     d->mFilter = d->mDefaultFilter;
00368   }
00369 }
00370 
00371 CalFilter *Calendar::filter() const
00372 {
00373   return d->mFilter;
00374 }
00375 
00376 QStringList Calendar::categories() const
00377 {
00378   Incidence::List rawInc( rawIncidences() );
00379   QStringList cats, thisCats;
00380   // @TODO: For now just iterate over all incidences. In the future,
00381   // the list of categories should be built when reading the file.
00382   for ( Incidence::List::ConstIterator i = rawInc.constBegin();
00383         i != rawInc.constEnd(); ++i ) {
00384     thisCats = (*i)->categories();
00385     for ( QStringList::ConstIterator si = thisCats.constBegin();
00386           si != thisCats.constEnd(); ++si ) {
00387       if ( !cats.contains( *si ) ) {
00388         cats.append( *si );
00389       }
00390     }
00391   }
00392   return cats;
00393 }
00394 
00395 Incidence::List Calendar::incidences( const QDate &date ) const
00396 {
00397   return mergeIncidenceList( events( date ), todos( date ), journals( date ) );
00398 }
00399 
00400 Incidence::List Calendar::incidences() const
00401 {
00402   return mergeIncidenceList( events(), todos(), journals() );
00403 }
00404 
00405 Incidence::List Calendar::rawIncidences() const
00406 {
00407   return mergeIncidenceList( rawEvents(), rawTodos(), rawJournals() );
00408 }
00409 
00410 Incidence::List Calendar::instances( const Incidence::Ptr &incidence ) const
00411 {
00412   if ( incidence ) {
00413     Event::List elist;
00414     Todo::List tlist;
00415     Journal::List jlist;
00416 
00417     if ( incidence->type() == Incidence::TypeEvent ) {
00418       elist = eventInstances( incidence );
00419     } else if ( incidence->type() == Incidence::TypeTodo ) {
00420       tlist = todoInstances( incidence );
00421     } else if ( incidence->type() == Incidence::TypeJournal ) {
00422       jlist = journalInstances( incidence );
00423     }
00424     return mergeIncidenceList( elist, tlist, jlist );
00425   } else {
00426     return Incidence::List();
00427   }
00428 }
00429 
00430 Incidence::List Calendar::duplicates( const Incidence::Ptr &incidence )
00431 {
00432   if ( incidence ) {
00433     Incidence::List list;
00434     Incidence::List vals = values( d->mNotebookIncidences );
00435     Incidence::List::const_iterator it;
00436     for ( it = vals.constBegin(); it != vals.constEnd(); ++it ) {
00437       if ( ( ( incidence->dtStart() == (*it)->dtStart() ) ||
00438              ( !incidence->dtStart().isValid() && !(*it)->dtStart().isValid() ) ) &&
00439            ( incidence->summary() == (*it)->summary() ) ) {
00440         list.append( *it );
00441       }
00442     }
00443     return list;
00444   } else {
00445     return Incidence::List();
00446   }
00447 }
00448 
00449 bool Calendar::addNotebook( const QString &notebook, bool isVisible )
00450 {
00451   if ( d->mNotebooks.contains( notebook ) ) {
00452     return false;
00453   } else {
00454     d->mNotebooks.insert( notebook, isVisible );
00455     return true;
00456   }
00457 }
00458 
00459 bool Calendar::updateNotebook( const QString &notebook, bool isVisible )
00460 {
00461   if ( !d->mNotebooks.contains( notebook ) ) {
00462     return false;
00463   } else {
00464     d->mNotebooks.insert( notebook, isVisible );
00465     return true;
00466   }
00467 }
00468 
00469 bool Calendar::deleteNotebook( const QString &notebook )
00470 {
00471   if ( !d->mNotebooks.contains( notebook ) ) {
00472     return false;
00473   } else {
00474     return d->mNotebooks.remove( notebook );
00475   }
00476 }
00477 
00478 bool Calendar::setDefaultNotebook( const QString &notebook )
00479 {
00480   if ( !d->mNotebooks.contains( notebook ) ) {
00481     return false;
00482   } else {
00483     d->mDefaultNotebook = notebook;
00484     return true;
00485   }
00486 }
00487 
00488 QString Calendar::defaultNotebook() const
00489 {
00490   return d->mDefaultNotebook;
00491 }
00492 
00493 bool Calendar::hasValidNotebook( const QString &notebook ) const
00494 {
00495   return d->mNotebooks.contains( notebook );
00496 }
00497 
00498 bool Calendar::isVisible( const Incidence::Ptr &incidence ) const
00499 {
00500   if ( d->mIncidenceVisibility.contains( incidence ) ) {
00501     return d->mIncidenceVisibility[incidence];
00502   }
00503   const QString nuid = notebook( incidence );
00504   bool rv;
00505   if ( d->mNotebooks.contains( nuid ) ) {
00506     rv = d->mNotebooks.value( nuid );
00507   } else {
00508     // NOTE returns true also for nonexisting notebooks for compatibility
00509     rv = true;
00510   }
00511   d->mIncidenceVisibility[incidence] = rv;
00512   return rv;
00513 }
00514 
00515 void Calendar::clearNotebookAssociations()
00516 {
00517   d->mNotebookIncidences.clear();
00518   d->mUidToNotebook.clear();
00519   d->mIncidenceVisibility.clear();
00520 }
00521 
00522 bool Calendar::setNotebook( const Incidence::Ptr &inc, const QString &notebook )
00523 {
00524   if ( !inc ) {
00525     return false;
00526   }
00527 
00528   if ( !notebook.isEmpty() &&
00529        !incidence( inc->uid(), inc->recurrenceId() ) ) {
00530     kWarning() << "cannot set notebook until incidence has been added";
00531     return false;
00532   }
00533 
00534   if ( d->mUidToNotebook.contains( inc->uid() ) ) {
00535     QString old = d->mUidToNotebook.value( inc->uid() );
00536     if ( !old.isEmpty() && notebook != old ) {
00537       if ( inc->hasRecurrenceId() ) {
00538         kWarning() << "cannot set notebook for child incidences";
00539         return false;
00540       }
00541       // Move all possible children also.
00542       Incidence::List list = instances( inc );
00543       Incidence::List::Iterator it;
00544       for ( it = list.begin(); it != list.end(); ++it ) {
00545         d->mNotebookIncidences.remove( old, *it );
00546         d->mNotebookIncidences.insert( notebook, *it );
00547       }
00548       notifyIncidenceChanged( inc ); // for removing from old notebook
00549       // don not remove from mUidToNotebook to keep deleted incidences
00550       d->mNotebookIncidences.remove( old, inc );
00551     }
00552   }
00553   if ( !notebook.isEmpty() ) {
00554     d->mUidToNotebook.insert( inc->uid(), notebook );
00555     d->mNotebookIncidences.insert( notebook, inc );
00556     kDebug() << "setting notebook" << notebook << "for" << inc->uid();
00557     notifyIncidenceChanged( inc ); // for inserting into new notebook
00558   }
00559 
00560   return true;
00561 }
00562 
00563 QString Calendar::notebook( const Incidence::Ptr &incidence ) const
00564 {
00565   if ( incidence ) {
00566     return d->mUidToNotebook.value( incidence->uid() );
00567   } else {
00568     return QString();
00569   }
00570 }
00571 
00572 QString Calendar::notebook( const QString &uid ) const
00573 {
00574   return d->mUidToNotebook.value( uid );
00575 }
00576 
00577 QStringList Calendar::notebooks() const
00578 {
00579   return d->mNotebookIncidences.uniqueKeys();
00580 }
00581 
00582 Incidence::List Calendar::incidences( const QString &notebook ) const
00583 {
00584   if ( notebook.isEmpty() ) {
00585     return values( d->mNotebookIncidences );
00586   } else {
00587     return values( d->mNotebookIncidences, notebook );
00588   }
00589 }
00590 
00592 Event::List Calendar::sortEvents( const Event::List &eventList,
00593                                   EventSortField sortField,
00594                                   SortDirection sortDirection )
00595 {
00596 
00597   if ( eventList.isEmpty() ) {
00598     return Event::List();
00599   }
00600 
00601   Event::List eventListSorted;
00602 
00603   // Notice we alphabetically presort Summaries first.
00604   // We do this so comparison "ties" stay in a nice order.
00605   eventListSorted = eventList;
00606   switch( sortField ) {
00607   case EventSortUnsorted:
00608     break;
00609 
00610   case EventSortStartDate:
00611     if ( sortDirection == SortDirectionAscending ) {
00612       qSort( eventListSorted.begin(), eventListSorted.end(), Events::startDateLessThan );
00613     } else {
00614       qSort( eventListSorted.begin(), eventListSorted.end(), Events::startDateMoreThan );
00615     }
00616     break;
00617 
00618   case EventSortEndDate:
00619     if ( sortDirection == SortDirectionAscending ) {
00620       qSort( eventListSorted.begin(), eventListSorted.end(), Events::endDateLessThan );
00621     } else {
00622       qSort( eventListSorted.begin(), eventListSorted.end(), Events::endDateMoreThan );
00623     }
00624     break;
00625 
00626   case EventSortSummary:
00627     if ( sortDirection == SortDirectionAscending ) {
00628       qSort( eventListSorted.begin(), eventListSorted.end(), Events::summaryLessThan );
00629     } else {
00630       qSort( eventListSorted.begin(), eventListSorted.end(), Events::summaryMoreThan );
00631     }
00632     break;
00633   }
00634 
00635   return eventListSorted;
00636 
00637 }
00638 
00639 Event::List Calendar::events( const QDate &date,
00640                               const KDateTime::Spec &timeSpec,
00641                               EventSortField sortField,
00642                               SortDirection sortDirection ) const
00643 {
00644   Event::List el = rawEventsForDate( date, timeSpec, sortField, sortDirection );
00645   d->mFilter->apply( &el );
00646   return el;
00647 }
00648 
00649 Event::List Calendar::events( const KDateTime &dt ) const
00650 {
00651   Event::List el = rawEventsForDate( dt );
00652   d->mFilter->apply( &el );
00653   return el;
00654 }
00655 
00656 Event::List Calendar::events( const QDate &start, const QDate &end,
00657                               const KDateTime::Spec &timeSpec,
00658                               bool inclusive ) const
00659 {
00660   Event::List el = rawEvents( start, end, timeSpec, inclusive );
00661   d->mFilter->apply( &el );
00662   return el;
00663 }
00664 
00665 Event::List Calendar::events( EventSortField sortField,
00666                               SortDirection sortDirection ) const
00667 {
00668   Event::List el = rawEvents( sortField, sortDirection );
00669   d->mFilter->apply( &el );
00670   return el;
00671 }
00672 
00673 bool Calendar::addIncidence( const Incidence::Ptr &incidence )
00674 {
00675   if ( !incidence ) {
00676     return false;
00677   }
00678 
00679   AddVisitor<Calendar> v( this );
00680   return incidence->accept( v, incidence );
00681 }
00682 
00683 bool Calendar::deleteIncidence( const Incidence::Ptr &incidence )
00684 {
00685   if ( !incidence ) {
00686     return false;
00687   }
00688 
00689   if ( beginChange( incidence ) ) {
00690     DeleteVisitor<Calendar> v( this );
00691     const bool result = incidence->accept( v, incidence );
00692     endChange( incidence );
00693     return result;
00694   } else {
00695     return false;
00696   }
00697 }
00698 
00699 // Dissociate a single occurrence or all future occurrences from a recurring
00700 // sequence. The new incidence is returned, but not automatically inserted
00701 // into the calendar, which is left to the calling application.
00702 Incidence::Ptr Calendar::dissociateOccurrence( const Incidence::Ptr &incidence,
00703                                                const QDate &date,
00704                                                const KDateTime::Spec &spec,
00705                                                bool single )
00706 {
00707   if ( !incidence || !incidence->recurs() ) {
00708     return Incidence::Ptr();
00709   }
00710 
00711   Incidence::Ptr newInc( incidence->clone() );
00712   newInc->recreate();
00713   // Do not call setRelatedTo() when dissociating recurring to-dos, otherwise the new to-do
00714   // will appear as a child.  Originally, we planned to set a relation with reltype SIBLING
00715   // when dissociating to-dos, but currently kcalcore only supports reltype PARENT.
00716   // We can uncomment the following line when we support the PARENT reltype.
00717   //newInc->setRelatedTo( incidence );
00718   Recurrence *recur = newInc->recurrence();
00719   if ( single ) {
00720     recur->clear();
00721   } else {
00722     // Adjust the recurrence for the future incidences. In particular adjust
00723     // the "end after n occurrences" rules! "No end date" and "end by ..."
00724     // don't need to be modified.
00725     int duration = recur->duration();
00726     if ( duration > 0 ) {
00727       int doneduration = recur->durationTo( date.addDays( -1 ) );
00728       if ( doneduration >= duration ) {
00729         kDebug() << "The dissociated event already occurred more often"
00730                  << "than it was supposed to ever occur. ERROR!";
00731         recur->clear();
00732       } else {
00733         recur->setDuration( duration - doneduration );
00734       }
00735     }
00736   }
00737   // Adjust the date of the incidence
00738   if ( incidence->type() == Incidence::TypeEvent ) {
00739     Event::Ptr ev = newInc.staticCast<Event>();
00740     KDateTime start( ev->dtStart() );
00741     int daysTo = start.toTimeSpec( spec ).date().daysTo( date );
00742     ev->setDtStart( start.addDays( daysTo ) );
00743     ev->setDtEnd( ev->dtEnd().addDays( daysTo ) );
00744   } else if ( incidence->type() == Incidence::TypeTodo ) {
00745     Todo::Ptr td = newInc.staticCast<Todo>();
00746     bool haveOffset = false;
00747     int daysTo = 0;
00748     if ( td->hasDueDate() ) {
00749       KDateTime due( td->dtDue() );
00750       daysTo = due.toTimeSpec( spec ).date().daysTo( date );
00751       td->setDtDue( due.addDays( daysTo ), true );
00752       haveOffset = true;
00753     }
00754     if ( td->hasStartDate() ) {
00755       KDateTime start( td->dtStart() );
00756       if ( !haveOffset ) {
00757         daysTo = start.toTimeSpec( spec ).date().daysTo( date );
00758       }
00759       td->setDtStart( start.addDays( daysTo ) );
00760       haveOffset = true;
00761     }
00762   }
00763   recur = incidence->recurrence();
00764   if ( recur ) {
00765     if ( single ) {
00766       recur->addExDate( date );
00767     } else {
00768       // Make sure the recurrence of the past events ends
00769       // at the corresponding day
00770       recur->setEndDate( date.addDays(-1) );
00771     }
00772   }
00773   return newInc;
00774 }
00775 
00776 Incidence::Ptr Calendar::incidence( const QString &uid,
00777                                     const KDateTime &recurrenceId ) const
00778 {
00779   Incidence::Ptr i = event( uid, recurrenceId );
00780   if ( i ) {
00781     return i;
00782   }
00783 
00784   i = todo( uid, recurrenceId );
00785   if ( i ) {
00786     return i;
00787   }
00788 
00789   i = journal( uid, recurrenceId );
00790   return i;
00791 }
00792 
00793 Incidence::Ptr Calendar::deleted( const QString &uid, const KDateTime &recurrenceId ) const
00794 {
00795   Incidence::Ptr i = deletedEvent( uid, recurrenceId );
00796   if ( i ) {
00797     return i;
00798   }
00799 
00800   i = deletedTodo( uid, recurrenceId );
00801   if ( i ) {
00802     return i;
00803   }
00804 
00805   i = deletedJournal( uid, recurrenceId );
00806   return i;
00807 }
00808 
00809 Incidence::List Calendar::incidencesFromSchedulingID( const QString &sid ) const
00810 {
00811   Incidence::List result;
00812   const Incidence::List incidences = rawIncidences();
00813   Incidence::List::const_iterator it = incidences.begin();
00814   for ( ; it != incidences.end(); ++it ) {
00815     if ( (*it)->schedulingID() == sid ) {
00816       result.append( *it );
00817     }
00818   }
00819   return result;
00820 }
00821 
00822 Incidence::Ptr Calendar::incidenceFromSchedulingID( const QString &uid ) const
00823 {
00824   const Incidence::List incidences = rawIncidences();
00825   Incidence::List::const_iterator it = incidences.begin();
00826   for ( ; it != incidences.end(); ++it ) {
00827     if ( (*it)->schedulingID() == uid ) {
00828       // Touchdown, and the crowd goes wild
00829       return *it;
00830     }
00831   }
00832   // Not found
00833   return Incidence::Ptr();
00834 }
00835 
00837 Todo::List Calendar::sortTodos( const Todo::List &todoList,
00838                                 TodoSortField sortField,
00839                                 SortDirection sortDirection )
00840 {
00841   if ( todoList.isEmpty() ) {
00842     return Todo::List();
00843   }
00844 
00845   Todo::List todoListSorted;
00846 
00847   // Notice we alphabetically presort Summaries first.
00848   // We do this so comparison "ties" stay in a nice order.
00849 
00850   // Note that To-dos may not have Start DateTimes nor due DateTimes.
00851 
00852   todoListSorted = todoList;
00853   switch( sortField ) {
00854   case TodoSortUnsorted:
00855     break;
00856 
00857   case TodoSortStartDate:
00858     if ( sortDirection == SortDirectionAscending ) {
00859       qSort( todoListSorted.begin(), todoListSorted.end(), Todos::startDateLessThan );
00860     } else {
00861       qSort( todoListSorted.begin(), todoListSorted.end(), Todos::startDateMoreThan );
00862     }
00863     break;
00864 
00865   case TodoSortDueDate:
00866     if ( sortDirection == SortDirectionAscending ) {
00867       qSort( todoListSorted.begin(), todoListSorted.end(), Todos::dueDateLessThan );
00868     } else {
00869       qSort( todoListSorted.begin(), todoListSorted.end(), Todos::dueDateMoreThan );
00870     }
00871     break;
00872 
00873   case TodoSortPriority:
00874     if ( sortDirection == SortDirectionAscending ) {
00875       qSort( todoListSorted.begin(), todoListSorted.end(), Todos::priorityLessThan );
00876     } else {
00877       qSort( todoListSorted.begin(), todoListSorted.end(), Todos::priorityMoreThan );
00878     }
00879     break;
00880 
00881   case TodoSortPercentComplete:
00882     if ( sortDirection == SortDirectionAscending ) {
00883       qSort( todoListSorted.begin(), todoListSorted.end(), Todos::percentLessThan );
00884     } else {
00885       qSort( todoListSorted.begin(), todoListSorted.end(), Todos::percentMoreThan );
00886     }
00887     break;
00888 
00889   case TodoSortSummary:
00890     if ( sortDirection == SortDirectionAscending ) {
00891       qSort( todoListSorted.begin(), todoListSorted.end(), Todos::summaryLessThan );
00892     } else {
00893       qSort( todoListSorted.begin(), todoListSorted.end(), Todos::summaryMoreThan );
00894     }
00895     break;
00896 
00897   case TodoSortCreated:
00898     if ( sortDirection == SortDirectionAscending ) {
00899       qSort( todoListSorted.begin(), todoListSorted.end(), Todos::createdLessThan );
00900     } else {
00901       qSort( todoListSorted.begin(), todoListSorted.end(), Todos::createdMoreThan );
00902     }
00903     break;
00904   }
00905 
00906   return todoListSorted;
00907 }
00908 
00909 Todo::List Calendar::todos( TodoSortField sortField,
00910                             SortDirection sortDirection ) const
00911 {
00912   Todo::List tl = rawTodos( sortField, sortDirection );
00913   d->mFilter->apply( &tl );
00914   return tl;
00915 }
00916 
00917 Todo::List Calendar::todos( const QDate &date ) const
00918 {
00919   Todo::List el = rawTodosForDate( date );
00920   d->mFilter->apply( &el );
00921   return el;
00922 }
00923 
00924 Todo::List Calendar::todos( const QDate &start, const QDate &end,
00925                             const KDateTime::Spec &timespec, bool inclusive ) const
00926 {
00927   Todo::List tl = rawTodos( start, end, timespec, inclusive );
00928   d->mFilter->apply( &tl );
00929   return tl;
00930 }
00931 
00933 Journal::List Calendar::sortJournals( const Journal::List &journalList,
00934                                       JournalSortField sortField,
00935                                       SortDirection sortDirection )
00936 {
00937   if ( journalList.isEmpty() ) {
00938     return Journal::List();
00939   }
00940 
00941   Journal::List journalListSorted = journalList;
00942 
00943   switch( sortField ) {
00944   case JournalSortUnsorted:
00945     break;
00946 
00947   case JournalSortDate:
00948     if ( sortDirection == SortDirectionAscending ) {
00949       qSort( journalListSorted.begin(), journalListSorted.end(), Journals::dateLessThan );
00950     } else {
00951       qSort( journalListSorted.begin(), journalListSorted.end(), Journals::dateMoreThan );
00952     }
00953     break;
00954 
00955   case JournalSortSummary:
00956     if ( sortDirection == SortDirectionAscending ) {
00957       qSort( journalListSorted.begin(), journalListSorted.end(), Journals::summaryLessThan );
00958     } else {
00959       qSort( journalListSorted.begin(), journalListSorted.end(), Journals::summaryMoreThan );
00960     }
00961     break;
00962   }
00963 
00964   return journalListSorted;
00965 }
00966 
00967 Journal::List Calendar::journals( JournalSortField sortField,
00968                                   SortDirection sortDirection ) const
00969 {
00970   Journal::List jl = rawJournals( sortField, sortDirection );
00971   d->mFilter->apply( &jl );
00972   return jl;
00973 }
00974 
00975 Journal::List Calendar::journals( const QDate &date ) const
00976 {
00977   Journal::List el = rawJournalsForDate( date );
00978   d->mFilter->apply( &el );
00979   return el;
00980 }
00981 
00982 // When this is called, the to-dos have already been added to the calendar.
00983 // This method is only about linking related to-dos.
00984 void Calendar::setupRelations( const Incidence::Ptr &forincidence )
00985 {
00986   if ( !forincidence ) {
00987     return;
00988   }
00989 
00990   const QString uid = forincidence->uid();
00991 
00992   // First, go over the list of orphans and see if this is their parent
00993   Incidence::List l = values( d->mOrphans, uid );
00994   d->mOrphans.remove( uid );
00995   for ( int i = 0, end = l.count();  i < end;  ++i ) {
00996     d->mIncidenceRelations[uid].append( l[i] );
00997     d->mOrphanUids.remove( l[i]->uid() );
00998   }
00999 
01000   // Now see about this incidences parent
01001   if ( forincidence->relatedTo().isEmpty() && !forincidence->relatedTo().isEmpty() ) {
01002     // Incidence has a uid it is related to but is not registered to it yet.
01003     // Try to find it
01004     Incidence::Ptr parent = incidence( forincidence->relatedTo() );
01005     if ( parent ) {
01006       // Found it
01007 
01008       // look for hierarchy loops
01009       if ( isAncestorOf( forincidence, parent ) ) {
01010         forincidence->setRelatedTo( QString() );
01011         kWarning() << "hierarchy loop beetween " << forincidence->uid() << " and " << parent->uid();
01012       } else {
01013         d->mIncidenceRelations[parent->uid()].append( forincidence );
01014       }
01015     } else {
01016       // Not found, put this in the mOrphans list
01017       // Note that the mOrphans dict might contain multiple entries with the
01018       // same key! which are multiple children that wait for the parent
01019       // incidence to be inserted.
01020       d->mOrphans.insert( forincidence->relatedTo(), forincidence );
01021       d->mOrphanUids.insert( forincidence->uid(), forincidence );
01022     }
01023   }
01024 }
01025 
01026 // If a to-do with sub-to-dos is deleted, move it's sub-to-dos to the orphan list
01027 void Calendar::removeRelations( const Incidence::Ptr &incidence )
01028 {
01029   if ( !incidence ) {
01030     kDebug() << "Warning: incidence is 0";
01031     return;
01032   }
01033 
01034   const QString uid = incidence->uid();
01035 
01036   foreach ( Incidence::Ptr i, d->mIncidenceRelations[uid] ) {
01037     if ( !d->mOrphanUids.contains( i->uid() ) ) {
01038       d->mOrphans.insert( uid, i );
01039       d->mOrphanUids.insert( i->uid(), i );
01040       i->setRelatedTo( uid );
01041     }
01042   }
01043 
01044   const QString parentUid = incidence->relatedTo();
01045 
01046   // If this incidence is related to something else, tell that about it
01047   if ( !parentUid.isEmpty() ) {
01048     d->mIncidenceRelations[parentUid].erase(
01049       std::remove( d->mIncidenceRelations[parentUid].begin(),
01050                    d->mIncidenceRelations[parentUid].end(), incidence ),
01051       d->mIncidenceRelations[parentUid].end() );
01052   }
01053 
01054   // Remove this one from the orphans list
01055   if ( d->mOrphanUids.remove( uid ) ) {
01056     // This incidence is located in the orphans list - it should be removed
01057     // Since the mOrphans dict might contain the same key (with different
01058     // child incidence pointers!) multiple times, take care that we remove
01059     // the correct one. So we need to remove all items with the given
01060     // parent UID, and readd those that are not for this item. Also, there
01061     // might be other entries with differnet UID that point to this
01062     // incidence (this might happen when the relatedTo of the item is
01063     // changed before its parent is inserted. This might happen with
01064     // groupware servers....). Remove them, too
01065     QStringList relatedToUids;
01066 
01067     // First, create a list of all keys in the mOrphans list which point
01068     // to the removed item
01069     relatedToUids << incidence->relatedTo();
01070     for ( QMultiHash<QString, Incidence::Ptr>::Iterator it = d->mOrphans.begin();
01071           it != d->mOrphans.end(); ++it ) {
01072       if ( it.value()->uid() == uid ) {
01073         relatedToUids << it.key();
01074       }
01075     }
01076 
01077     // now go through all uids that have one entry that point to the incidence
01078     for ( QStringList::const_iterator uidit = relatedToUids.constBegin();
01079           uidit != relatedToUids.constEnd(); ++uidit ) {
01080       Incidence::List tempList;
01081       // Remove all to get access to the remaining entries
01082       QList<Incidence::Ptr> l = d->mOrphans.values( *uidit );
01083       d->mOrphans.remove( *uidit );
01084       foreach ( Incidence::Ptr i, l ) {
01085         if ( i != incidence ) {
01086           tempList.append( i );
01087         }
01088       }
01089       // Readd those that point to a different orphan incidence
01090       for ( Incidence::List::Iterator incit = tempList.begin();
01091             incit != tempList.end(); ++incit ) {
01092         d->mOrphans.insert( *uidit, *incit );
01093       }
01094     }
01095   }
01096 
01097   // Make sure the deleted incidence doesn't relate to a non-deleted incidence,
01098   // since that would cause trouble in MemoryCalendar::close(), as the deleted
01099   // incidences are destroyed after the non-deleted incidences. The destructor
01100   // of the deleted incidences would then try to access the already destroyed
01101   // non-deleted incidence, which would segfault.
01102   //
01103   // So in short: Make sure dead incidences don't point to alive incidences
01104   // via the relation.
01105   //
01106   // This crash is tested in MemoryCalendarTest::testRelationsCrash().
01107 //  incidence->setRelatedTo( Incidence::Ptr() );
01108 }
01109 
01110 bool Calendar::isAncestorOf( const Incidence::Ptr &ancestor,
01111                              const Incidence::Ptr &incidence ) const
01112 {
01113   if ( !incidence || incidence->relatedTo().isEmpty() ) {
01114     return false;
01115   } else if ( incidence->relatedTo() == ancestor->uid() ) {
01116     return true;
01117   } else {
01118     return isAncestorOf( ancestor, this->incidence( incidence->relatedTo() ) );
01119   }
01120 }
01121 
01122 Incidence::List Calendar::relations( const QString &uid ) const
01123 {
01124   return d->mIncidenceRelations[uid];
01125 }
01126 
01127 Calendar::CalendarObserver::~CalendarObserver()
01128 {
01129 }
01130 
01131 void Calendar::CalendarObserver::calendarModified( bool modified, Calendar *calendar )
01132 {
01133   Q_UNUSED( modified );
01134   Q_UNUSED( calendar );
01135 }
01136 
01137 void Calendar::CalendarObserver::calendarIncidenceAdded( const Incidence::Ptr &incidence )
01138 {
01139   Q_UNUSED( incidence );
01140 }
01141 
01142 void Calendar::CalendarObserver::calendarIncidenceChanged( const Incidence::Ptr &incidence )
01143 {
01144   Q_UNUSED( incidence );
01145 }
01146 
01147 void Calendar::CalendarObserver::calendarIncidenceDeleted( const Incidence::Ptr &incidence )
01148 {
01149   Q_UNUSED( incidence );
01150 }
01151 
01152 void
01153 Calendar::CalendarObserver::calendarIncidenceAdditionCanceled( const Incidence::Ptr &incidence )
01154 {
01155   Q_UNUSED( incidence );
01156 }
01157 
01158 void Calendar::registerObserver( CalendarObserver *observer )
01159 {
01160   if ( !observer ) {
01161     return;
01162   }
01163 
01164   if ( !d->mObservers.contains( observer ) ) {
01165     d->mObservers.append( observer );
01166   } else {
01167     d->mNewObserver = true;
01168   }
01169 }
01170 
01171 void Calendar::unregisterObserver( CalendarObserver *observer )
01172 {
01173   if ( !observer ) {
01174     return;
01175   } else {
01176     d->mObservers.removeAll( observer );
01177   }
01178 }
01179 
01180 bool Calendar::isSaving() const
01181 {
01182   return false;
01183 }
01184 
01185 void Calendar::setModified( bool modified )
01186 {
01187   if ( modified != d->mModified || d->mNewObserver ) {
01188     d->mNewObserver = false;
01189     foreach ( CalendarObserver *observer, d->mObservers ) {
01190       observer->calendarModified( modified, this );
01191     }
01192     d->mModified = modified;
01193   }
01194 }
01195 
01196 bool Calendar::isModified() const
01197 {
01198   return d->mModified;
01199 }
01200 
01201 bool Calendar::save()
01202 {
01203   return true;
01204 }
01205 
01206 bool Calendar::reload()
01207 {
01208   return true;
01209 }
01210 
01211 void Calendar::incidenceUpdated( const QString &uid, const KDateTime &recurrenceId )
01212 {
01213 
01214   Incidence::Ptr inc = incidence( uid, recurrenceId );
01215 
01216   if ( !inc ) {
01217     return;
01218   }
01219 
01220   inc->setLastModified( KDateTime::currentUtcDateTime() );
01221   // we should probably update the revision number here,
01222   // or internally in the Event itself when certain things change.
01223   // need to verify with ical documentation.
01224 
01225   notifyIncidenceChanged( inc );
01226 
01227   setModified( true );
01228 }
01229 
01230 void Calendar::doSetTimeSpec( const KDateTime::Spec &timeSpec )
01231 {
01232   Q_UNUSED( timeSpec );
01233 }
01234 
01235 void Calendar::notifyIncidenceAdded( const Incidence::Ptr &incidence )
01236 {
01237   if ( !incidence ) {
01238     return;
01239   }
01240 
01241   if ( !d->mObserversEnabled ) {
01242     return;
01243   }
01244 
01245   foreach ( CalendarObserver *observer, d->mObservers ) {
01246     observer->calendarIncidenceAdded( incidence );
01247   }
01248 }
01249 
01250 void Calendar::notifyIncidenceChanged( const Incidence::Ptr &incidence )
01251 {
01252   if ( !incidence ) {
01253     return;
01254   }
01255 
01256   if ( !d->mObserversEnabled ) {
01257     return;
01258   }
01259 
01260   foreach ( CalendarObserver *observer, d->mObservers ) {
01261     observer->calendarIncidenceChanged( incidence );
01262   }
01263 }
01264 
01265 void Calendar::notifyIncidenceDeleted( const Incidence::Ptr &incidence )
01266 {
01267   if ( !incidence ) {
01268     return;
01269   }
01270 
01271   if ( !d->mObserversEnabled ) {
01272     return;
01273   }
01274 
01275   foreach ( CalendarObserver *observer, d->mObservers ) {
01276     observer->calendarIncidenceDeleted( incidence );
01277   }
01278 }
01279 
01280 void Calendar::notifyIncidenceAdditionCanceled( const Incidence::Ptr &incidence )
01281 {
01282   if ( !incidence ) {
01283     return;
01284   }
01285 
01286   if ( !d->mObserversEnabled ) {
01287     return;
01288   }
01289 
01290   foreach ( CalendarObserver *observer, d->mObservers ) {
01291     observer->calendarIncidenceAdditionCanceled( incidence );
01292   }
01293 }
01294 
01295 void Calendar::customPropertyUpdated()
01296 {
01297   setModified( true );
01298 }
01299 
01300 void Calendar::setProductId( const QString &id )
01301 {
01302   d->mProductId = id;
01303 }
01304 
01305 QString Calendar::productId() const
01306 {
01307   return d->mProductId;
01308 }
01309 
01311 Incidence::List Calendar::mergeIncidenceList( const Event::List &events,
01312                                               const Todo::List &todos,
01313                                               const Journal::List &journals )
01314 {
01315   Incidence::List incidences;
01316 
01317   int i, end;
01318   for ( i = 0, end = events.count();  i < end;  ++i ) {
01319     incidences.append( events[i] );
01320   }
01321 
01322   for ( i = 0, end = todos.count();  i < end;  ++i ) {
01323     incidences.append( todos[i] );
01324   }
01325 
01326   for ( i = 0, end = journals.count();  i < end;  ++i ) {
01327     incidences.append( journals[i] );
01328   }
01329 
01330   return incidences;
01331 }
01332 
01333 bool Calendar::beginChange( const Incidence::Ptr &incidence )
01334 {
01335   Q_UNUSED( incidence );
01336   return true;
01337 }
01338 
01339 bool Calendar::endChange( const Incidence::Ptr &incidence )
01340 {
01341   Q_UNUSED( incidence );
01342   return true;
01343 }
01344 
01345 void Calendar::setObserversEnabled( bool enabled )
01346 {
01347   d->mObserversEnabled = enabled;
01348 }
01349 
01350 void Calendar::appendAlarms( Alarm::List &alarms, const Incidence::Ptr &incidence,
01351                              const KDateTime &from, const KDateTime &to ) const
01352 {
01353   KDateTime preTime = from.addSecs(-1);
01354 
01355   Alarm::List alarmlist = incidence->alarms();
01356   for ( int i = 0, iend = alarmlist.count();  i < iend;  ++i ) {
01357     if ( alarmlist[i]->enabled() ) {
01358       KDateTime dt = alarmlist[i]->nextRepetition( preTime );
01359       if ( dt.isValid() && dt <= to ) {
01360         kDebug() << incidence->summary() << "':" << dt.toString();
01361         alarms.append( alarmlist[i] );
01362       }
01363     }
01364   }
01365 }
01366 
01367 void Calendar::appendRecurringAlarms( Alarm::List &alarms,
01368                                       const Incidence::Ptr &incidence,
01369                                       const KDateTime &from,
01370                                       const KDateTime &to ) const
01371 {
01372   KDateTime dt;
01373   bool endOffsetValid = false;
01374   Duration endOffset( 0 );
01375   Duration period( from, to );
01376 
01377   Alarm::List alarmlist = incidence->alarms();
01378   for ( int i = 0, iend = alarmlist.count();  i < iend;  ++i ) {
01379     Alarm::Ptr a = alarmlist[i];
01380     if ( a->enabled() ) {
01381       if ( a->hasTime() ) {
01382         // The alarm time is defined as an absolute date/time
01383         dt = a->nextRepetition( from.addSecs(-1) );
01384         if ( !dt.isValid() || dt > to ) {
01385           continue;
01386         }
01387       } else {
01388         // Alarm time is defined by an offset from the event start or end time.
01389         // Find the offset from the event start time, which is also used as the
01390         // offset from the recurrence time.
01391         Duration offset( 0 );
01392         if ( a->hasStartOffset() ) {
01393           offset = a->startOffset();
01394         } else if ( a->hasEndOffset() ) {
01395           offset = a->endOffset();
01396           if ( !endOffsetValid ) {
01397             endOffset = Duration( incidence->dtStart(),
01398                                   incidence->dateTime( Incidence::RoleAlarmEndOffset ) );
01399             endOffsetValid = true;
01400           }
01401         }
01402 
01403         // Find the incidence's earliest alarm
01404         KDateTime alarmStart =
01405           offset.end( a->hasEndOffset() ? incidence->dateTime( Incidence::RoleAlarmEndOffset ) :
01406                                           incidence->dtStart() );
01407 //        KDateTime alarmStart = incidence->dtStart().addSecs( offset );
01408         if ( alarmStart > to ) {
01409           continue;
01410         }
01411         KDateTime baseStart = incidence->dtStart();
01412         if ( from > alarmStart ) {
01413           alarmStart = from;   // don't look earlier than the earliest alarm
01414           baseStart = (-offset).end( (-endOffset).end( alarmStart ) );
01415         }
01416 
01417         // Adjust the 'alarmStart' date/time and find the next recurrence at or after it.
01418         // Treate the two offsets separately in case one is daily and the other not.
01419         dt = incidence->recurrence()->getNextDateTime( baseStart.addSecs(-1) );
01420         if ( !dt.isValid() ||
01421              ( dt = endOffset.end( offset.end( dt ) ) ) > to ) // adjust 'dt' to get the alarm time
01422         {
01423           // The next recurrence is too late.
01424           if ( !a->repeatCount() ) {
01425             continue;
01426           }
01427 
01428           // The alarm has repetitions, so check whether repetitions of previous
01429           // recurrences fall within the time period.
01430           bool found = false;
01431           Duration alarmDuration = a->duration();
01432           for ( KDateTime base = baseStart;
01433                 ( dt = incidence->recurrence()->getPreviousDateTime( base ) ).isValid();
01434                 base = dt ) {
01435             if ( a->duration().end( dt ) < base ) {
01436               break;  // this recurrence's last repetition is too early, so give up
01437             }
01438 
01439             // The last repetition of this recurrence is at or after 'alarmStart' time.
01440             // Check if a repetition occurs between 'alarmStart' and 'to'.
01441             int snooze = a->snoozeTime().value();   // in seconds or days
01442             if ( a->snoozeTime().isDaily() ) {
01443               Duration toFromDuration( dt, base );
01444               int toFrom = toFromDuration.asDays();
01445               if ( a->snoozeTime().end( from ) <= to ||
01446                    ( toFromDuration.isDaily() && toFrom % snooze == 0 ) ||
01447                    ( toFrom / snooze + 1 ) * snooze <= toFrom + period.asDays() ) {
01448                 found = true;
01449 #ifndef NDEBUG
01450                 // for debug output
01451                 dt = offset.end( dt ).addDays( ( ( toFrom - 1 ) / snooze + 1 ) * snooze );
01452 #endif
01453                 break;
01454               }
01455             } else {
01456               int toFrom = dt.secsTo( base );
01457               if ( period.asSeconds() >= snooze ||
01458                    toFrom % snooze == 0 ||
01459                    ( toFrom / snooze + 1 ) * snooze <= toFrom + period.asSeconds() )
01460               {
01461                 found = true;
01462 #ifndef NDEBUG
01463                 // for debug output
01464                 dt = offset.end( dt ).addSecs( ( ( toFrom - 1 ) / snooze + 1 ) * snooze );
01465 #endif
01466                 break;
01467               }
01468             }
01469           }
01470           if ( !found ) {
01471             continue;
01472           }
01473         }
01474       }
01475       kDebug() << incidence->summary() << "':" << dt.toString();
01476       alarms.append( a );
01477     }
01478   }
01479 }
01480 
01481 void Calendar::startBatchAdding()
01482 {
01483   d->batchAddingInProgress = true;
01484 }
01485 
01486 void Calendar::endBatchAdding()
01487 {
01488   d->batchAddingInProgress = false;
01489 }
01490 
01491 bool Calendar::batchAdding() const
01492 {
01493   return d->batchAddingInProgress;
01494 }
01495 
01496 void Calendar::virtual_hook( int id, void *data )
01497 {
01498   Q_UNUSED( id );
01499   Q_UNUSED( data );
01500   Q_ASSERT( false );
01501 }
01502 
01503 #include "calendar.moc"
This file is part of the KDE documentation.
Documentation copyright © 1996-2012 The KDE developers.
Generated on Mon May 14 2012 04:35:38 by doxygen 1.7.5 written by Dimitri van Heesch, © 1997-2006

KDE's Doxygen guidelines are available online.

KCalCore Library

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

kdepimlibs-4.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