00001
00002
00003
00004
00005
00006
00007
00008
00009
00010
00011
00012
00013
00014
00015
00016
00017
00018
00019
00020
00021
00022
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>
00050
00051 using namespace KCalCore;
00052
00057
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
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;
00092 ICalTimeZone mBuiltInTimeZone;
00093 ICalTimeZone mBuiltInViewTimeZone;
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
00105 QMultiHash<QString, Incidence::Ptr> mOrphans;
00106 QMultiHash<QString, Incidence::Ptr> mOrphanUids;
00107
00108
00109 QMultiHash<QString, Incidence::Ptr >mNotebookIncidences;
00110 QHash<QString, QString>mUidToNotebook;
00111 QHash<QString, bool>mNotebooks;
00112 QHash<Incidence::Ptr, bool>mIncidenceVisibility;
00113 QString mDefaultNotebook;
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
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
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
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
00381
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 ¬ebook, 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 ¬ebook, 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 ¬ebook )
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 ¬ebook )
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 ¬ebook ) 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
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 ¬ebook )
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
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 );
00549
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 );
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 ¬ebook ) 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
00604
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
00700
00701
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
00714
00715
00716
00717
00718 Recurrence *recur = newInc->recurrence();
00719 if ( single ) {
00720 recur->clear();
00721 } else {
00722
00723
00724
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
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
00769
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
00829 return *it;
00830 }
00831 }
00832
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
00848
00849
00850
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 ×pec, 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
00983
00984 void Calendar::setupRelations( const Incidence::Ptr &forincidence )
00985 {
00986 if ( !forincidence ) {
00987 return;
00988 }
00989
00990 const QString uid = forincidence->uid();
00991
00992
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
01001 if ( forincidence->relatedTo().isEmpty() && !forincidence->relatedTo().isEmpty() ) {
01002
01003
01004 Incidence::Ptr parent = incidence( forincidence->relatedTo() );
01005 if ( parent ) {
01006
01007
01008
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
01017
01018
01019
01020 d->mOrphans.insert( forincidence->relatedTo(), forincidence );
01021 d->mOrphanUids.insert( forincidence->uid(), forincidence );
01022 }
01023 }
01024 }
01025
01026
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
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
01055 if ( d->mOrphanUids.remove( uid ) ) {
01056
01057
01058
01059
01060
01061
01062
01063
01064
01065 QStringList relatedToUids;
01066
01067
01068
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
01078 for ( QStringList::const_iterator uidit = relatedToUids.constBegin();
01079 uidit != relatedToUids.constEnd(); ++uidit ) {
01080 Incidence::List tempList;
01081
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
01090 for ( Incidence::List::Iterator incit = tempList.begin();
01091 incit != tempList.end(); ++incit ) {
01092 d->mOrphans.insert( *uidit, *incit );
01093 }
01094 }
01095 }
01096
01097
01098
01099
01100
01101
01102
01103
01104
01105
01106
01107
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
01222
01223
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
01383 dt = a->nextRepetition( from.addSecs(-1) );
01384 if ( !dt.isValid() || dt > to ) {
01385 continue;
01386 }
01387 } else {
01388
01389
01390
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
01404 KDateTime alarmStart =
01405 offset.end( a->hasEndOffset() ? incidence->dateTime( Incidence::RoleAlarmEndOffset ) :
01406 incidence->dtStart() );
01407
01408 if ( alarmStart > to ) {
01409 continue;
01410 }
01411 KDateTime baseStart = incidence->dtStart();
01412 if ( from > alarmStart ) {
01413 alarmStart = from;
01414 baseStart = (-offset).end( (-endOffset).end( alarmStart ) );
01415 }
01416
01417
01418
01419 dt = incidence->recurrence()->getNextDateTime( baseStart.addSecs(-1) );
01420 if ( !dt.isValid() ||
01421 ( dt = endOffset.end( offset.end( dt ) ) ) > to )
01422 {
01423
01424 if ( !a->repeatCount() ) {
01425 continue;
01426 }
01427
01428
01429
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;
01437 }
01438
01439
01440
01441 int snooze = a->snoozeTime().value();
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
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
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"