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