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

KCal Library

calendar.cpp

Go to the documentation of this file.
00001 /*
00002   This file is part of the kcal library.
00003 
00004   Copyright (c) 1998 Preston Brown <pbrown@kde.org>
00005   Copyright (c) 2000-2004 Cornelius Schumacher <schumacher@kde.org>
00006   Copyright (C) 2003-2004 Reinhold Kainhofer <reinhold@kainhofer.com>
00007   Copyright (c) 2006 David Jarvie <software@astrojar.org.uk>
00008 
00009   This library is free software; you can redistribute it and/or
00010   modify it under the terms of the GNU Library General Public
00011   License as published by the Free Software Foundation; either
00012   version 2 of the License, or (at your option) any later version.
00013 
00014   This library is distributed in the hope that it will be useful,
00015   but WITHOUT ANY WARRANTY; without even the implied warranty of
00016   MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
00017   Library General Public License for more details.
00018 
00019   You should have received a copy of the GNU Library General Public License
00020   along with this library; see the file COPYING.LIB.  If not, write to
00021   the Free Software Foundation, Inc., 51 Franklin Street, Fifth Floor,
00022   Boston, MA 02110-1301, USA.
00023 */
00038 #include "calendar.h"
00039 #include "exceptions.h"
00040 #include "calfilter.h"
00041 #include "icaltimezones.h"
00042 #include <kdebug.h>
00043 #include <klocale.h>
00044 
00045 extern "C" {
00046   #include <icaltimezone.h>
00047 }
00048 
00049 using namespace KCal;
00050 
00055 //@cond PRIVATE
00056 class KCal::Calendar::Private
00057 {
00058   public:
00059     Private()
00060       : mTimeZones( new ICalTimeZones ),
00061         mModified( false ),
00062         mNewObserver( false ),
00063         mObserversEnabled( true ),
00064         mDefaultFilter( new CalFilter )
00065     {
00066       // Setup default filter, which does nothing
00067       mFilter = mDefaultFilter;
00068       mFilter->setEnabled( false );
00069 
00070       // user information...
00071       mOwner.setName( i18n( "Unknown Name" ) );
00072       mOwner.setEmail( i18n( "unknown@nowhere" ) );
00073     }
00074 
00075     ~Private()
00076     {
00077       delete mTimeZones;
00078       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       if ( (*eit)->dtStart().isDateOnly() ) {
00311         tempList.append( *eit );
00312         continue;
00313       }
00314       sortIt = eventListSorted.begin();
00315       if ( sortDirection == SortDirectionAscending ) {
00316         while ( sortIt != eventListSorted.end() &&
00317                 (*eit)->dtStart() >= (*sortIt)->dtStart() ) {
00318           ++sortIt;
00319         }
00320       } else {
00321         while ( sortIt != eventListSorted.end() &&
00322                 (*eit)->dtStart() < (*sortIt)->dtStart() ) {
00323           ++sortIt;
00324         }
00325       }
00326       eventListSorted.insert( sortIt, *eit );
00327     }
00328     if ( sortDirection == SortDirectionAscending ) {
00329       // Prepend the list of Events without End DateTimes
00330       tempList += eventListSorted;
00331       eventListSorted = tempList;
00332     } else {
00333       // Append the list of Events without End DateTimes
00334       eventListSorted += tempList;
00335     }
00336     break;
00337 
00338   case EventSortEndDate:
00339     alphaList = sortEvents( eventList, EventSortSummary, sortDirection );
00340     for ( eit = alphaList.begin(); eit != alphaList.end(); ++eit ) {
00341       if ( (*eit)->hasEndDate() ) {
00342         sortIt = eventListSorted.begin();
00343         if ( sortDirection == SortDirectionAscending ) {
00344           while ( sortIt != eventListSorted.end() &&
00345                   (*eit)->dtEnd() >= (*sortIt)->dtEnd() ) {
00346             ++sortIt;
00347           }
00348         } else {
00349           while ( sortIt != eventListSorted.end() &&
00350                   (*eit)->dtEnd() < (*sortIt)->dtEnd() ) {
00351             ++sortIt;
00352           }
00353         }
00354       } else {
00355         // Keep a list of the Events without End DateTimes
00356         tempList.append( *eit );
00357       }
00358       eventListSorted.insert( sortIt, *eit );
00359     }
00360     if ( sortDirection == SortDirectionAscending ) {
00361       // Append the list of Events without End DateTimes
00362       eventListSorted += tempList;
00363     } else {
00364       // Prepend the list of Events without End DateTimes
00365       tempList += eventListSorted;
00366       eventListSorted = tempList;
00367     }
00368     break;
00369 
00370   case EventSortSummary:
00371     for ( eit = eventList->begin(); eit != eventList->end(); ++eit ) {
00372       sortIt = eventListSorted.begin();
00373       if ( sortDirection == SortDirectionAscending ) {
00374         while ( sortIt != eventListSorted.end() &&
00375                 (*eit)->summary() >= (*sortIt)->summary() ) {
00376           ++sortIt;
00377         }
00378       } else {
00379         while ( sortIt != eventListSorted.end() &&
00380                 (*eit)->summary() < (*sortIt)->summary() ) {
00381           ++sortIt;
00382         }
00383       }
00384       eventListSorted.insert( sortIt, *eit );
00385     }
00386     break;
00387   }
00388 
00389   return eventListSorted;
00390 
00391 }
00392 
00393 Event::List Calendar::events( const QDate &date,
00394                               const KDateTime::Spec &timespec,
00395                               EventSortField sortField,
00396                               SortDirection sortDirection )
00397 {
00398   Event::List el = rawEventsForDate( date, timespec, sortField, sortDirection );
00399   d->mFilter->apply( &el );
00400   return el;
00401 }
00402 
00403 Event::List Calendar::events( const KDateTime &dt )
00404 {
00405   Event::List el = rawEventsForDate( dt );
00406   d->mFilter->apply( &el );
00407   return el;
00408 }
00409 
00410 Event::List Calendar::events( const QDate &start, const QDate &end,
00411                               const KDateTime::Spec &timespec,
00412                               bool inclusive )
00413 {
00414   Event::List el = rawEvents( start, end, timespec, inclusive );
00415   d->mFilter->apply( &el );
00416   return el;
00417 }
00418 
00419 Event::List Calendar::events( EventSortField sortField,
00420                               SortDirection sortDirection )
00421 {
00422   Event::List el = rawEvents( sortField, sortDirection );
00423   d->mFilter->apply( &el );
00424   return el;
00425 }
00426 
00427 bool Calendar::addIncidence( Incidence *incidence )
00428 {
00429   Incidence::AddVisitor<Calendar> v( this );
00430 
00431   return incidence->accept( v );
00432 }
00433 
00434 bool Calendar::deleteIncidence( Incidence *incidence )
00435 {
00436   if ( beginChange( incidence ) ) {
00437     Incidence::DeleteVisitor<Calendar> v( this );
00438     bool result = incidence->accept( v );
00439     endChange( incidence );
00440     return result;
00441   } else {
00442     return false;
00443   }
00444 }
00445 
00446 // Dissociate a single occurrence or all future occurrences from a recurring
00447 // sequence. The new incidence is returned, but not automatically inserted
00448 // into the calendar, which is left to the calling application.
00449 Incidence *Calendar::dissociateOccurrence( Incidence *incidence,
00450                                            const QDate &date,
00451                                            const KDateTime::Spec &spec,
00452                                            bool single )
00453 {
00454   if ( !incidence || !incidence->recurs() ) {
00455     return 0;
00456   }
00457 
00458   Incidence *newInc = incidence->clone();
00459   newInc->recreate();
00460   newInc->setRelatedTo( incidence );
00461   Recurrence *recur = newInc->recurrence();
00462   if ( single ) {
00463     recur->clear();
00464   } else {
00465     // Adjust the recurrence for the future incidences. In particular adjust
00466     // the "end after n occurrences" rules! "No end date" and "end by ..."
00467     // don't need to be modified.
00468     int duration = recur->duration();
00469     if ( duration > 0 ) {
00470       int doneduration = recur->durationTo( date.addDays( -1 ) );
00471       if ( doneduration >= duration ) {
00472         kDebug() << "The dissociated event already occurred more often"
00473                  << "than it was supposed to ever occur. ERROR!";
00474         recur->clear();
00475       } else {
00476         recur->setDuration( duration - doneduration );
00477       }
00478     }
00479   }
00480   // Adjust the date of the incidence
00481   if ( incidence->type() == "Event" ) {
00482     Event *ev = static_cast<Event *>( newInc );
00483     KDateTime start( ev->dtStart() );
00484     int daysTo = start.toTimeSpec( spec ).date().daysTo( date );
00485     ev->setDtStart( start.addDays( daysTo ) );
00486     ev->setDtEnd( ev->dtEnd().addDays( daysTo ) );
00487   } else if ( incidence->type() == "Todo" ) {
00488     Todo *td = static_cast<Todo *>( newInc );
00489     bool haveOffset = false;
00490     int daysTo = 0;
00491     if ( td->hasDueDate() ) {
00492       KDateTime due( td->dtDue() );
00493       daysTo = due.toTimeSpec( spec ).date().daysTo( date );
00494       td->setDtDue( due.addDays( daysTo ), true );
00495       haveOffset = true;
00496     }
00497     if ( td->hasStartDate() ) {
00498       KDateTime start( td->dtStart() );
00499       if ( !haveOffset ) {
00500         daysTo = start.toTimeSpec( spec ).date().daysTo( date );
00501       }
00502       td->setDtStart( start.addDays( daysTo ) );
00503       haveOffset = true;
00504     }
00505   }
00506   recur = incidence->recurrence();
00507   if ( recur ) {
00508     if ( single ) {
00509       recur->addExDate( date );
00510     } else {
00511       // Make sure the recurrence of the past events ends
00512       // at the corresponding day
00513       recur->setEndDate( date.addDays(-1) );
00514     }
00515   }
00516   return newInc;
00517 }
00518 
00519 Incidence *Calendar::incidence( const QString &uid )
00520 {
00521   Incidence *i = event( uid );
00522   if ( i ) {
00523     return i;
00524   }
00525 
00526   i = todo( uid );
00527   if ( i ) {
00528     return i;
00529   }
00530 
00531   i = journal( uid );
00532   return i;
00533 }
00534 
00535 Incidence::List Calendar::incidencesFromSchedulingID( const QString &sid )
00536 {
00537   Incidence::List result;
00538   const Incidence::List incidences = rawIncidences();
00539   Incidence::List::const_iterator it = incidences.begin();
00540   for ( ; it != incidences.end(); ++it ) {
00541     if ( (*it)->schedulingID() == sid ) {
00542       result.append( *it );
00543     }
00544   }
00545   return result;
00546 }
00547 
00548 Incidence *Calendar::incidenceFromSchedulingID( const QString &UID )
00549 {
00550   const Incidence::List incidences = rawIncidences();
00551   Incidence::List::const_iterator it = incidences.begin();
00552   for ( ; it != incidences.end(); ++it ) {
00553     if ( (*it)->schedulingID() == UID ) {
00554       // Touchdown, and the crowd goes wild
00555       return *it;
00556     }
00557   }
00558   // Not found
00559   return 0;
00560 }
00561 
00562 Todo::List Calendar::sortTodos( Todo::List *todoList,
00563                                 TodoSortField sortField,
00564                                 SortDirection sortDirection )
00565 {
00566   Todo::List todoListSorted;
00567   Todo::List tempList, t;
00568   Todo::List alphaList;
00569   Todo::List::Iterator sortIt;
00570   Todo::List::Iterator eit;
00571 
00572   // Notice we alphabetically presort Summaries first.
00573   // We do this so comparison "ties" stay in a nice order.
00574 
00575   // Note that To-dos may not have Start DateTimes nor due DateTimes.
00576 
00577   switch( sortField ) {
00578   case TodoSortUnsorted:
00579     todoListSorted = *todoList;
00580     break;
00581 
00582   case TodoSortStartDate:
00583     alphaList = sortTodos( todoList, TodoSortSummary, sortDirection );
00584     for ( eit = alphaList.begin(); eit != alphaList.end(); ++eit ) {
00585       if ( (*eit)->hasStartDate() ) {
00586         sortIt = todoListSorted.begin();
00587         if ( sortDirection == SortDirectionAscending ) {
00588           while ( sortIt != todoListSorted.end() &&
00589                   (*eit)->dtStart() >= (*sortIt)->dtStart() ) {
00590             ++sortIt;
00591           }
00592         } else {
00593           while ( sortIt != todoListSorted.end() &&
00594                   (*eit)->dtStart() < (*sortIt)->dtStart() ) {
00595             ++sortIt;
00596           }
00597         }
00598         todoListSorted.insert( sortIt, *eit );
00599       } else {
00600         // Keep a list of the To-dos without Start DateTimes
00601         tempList.append( *eit );
00602       }
00603     }
00604     if ( sortDirection == SortDirectionAscending ) {
00605       // Append the list of To-dos without Start DateTimes
00606       todoListSorted += tempList;
00607     } else {
00608       // Prepend the list of To-dos without Start DateTimes
00609       tempList += todoListSorted;
00610       todoListSorted = tempList;
00611     }
00612     break;
00613 
00614   case TodoSortDueDate:
00615     alphaList = sortTodos( todoList, TodoSortSummary, sortDirection );
00616     for ( eit = alphaList.begin(); eit != alphaList.end(); ++eit ) {
00617       if ( (*eit)->hasDueDate() ) {
00618         sortIt = todoListSorted.begin();
00619         if ( sortDirection == SortDirectionAscending ) {
00620           while ( sortIt != todoListSorted.end() &&
00621                   (*eit)->dtDue() >= (*sortIt)->dtDue() ) {
00622             ++sortIt;
00623           }
00624         } else {
00625           while ( sortIt != todoListSorted.end() &&
00626                   (*eit)->dtDue() < (*sortIt)->dtDue() ) {
00627             ++sortIt;
00628           }
00629         }
00630         todoListSorted.insert( sortIt, *eit );
00631       } else {
00632         // Keep a list of the To-dos without Due DateTimes
00633         tempList.append( *eit );
00634       }
00635     }
00636     if ( sortDirection == SortDirectionAscending ) {
00637       // Append the list of To-dos without Due DateTimes
00638       todoListSorted += tempList;
00639     } else {
00640       // Prepend the list of To-dos without Due DateTimes
00641       tempList += todoListSorted;
00642       todoListSorted = tempList;
00643     }
00644     break;
00645 
00646   case TodoSortPriority:
00647     alphaList = sortTodos( todoList, TodoSortSummary, sortDirection );
00648     for ( eit = alphaList.begin(); eit != alphaList.end(); ++eit ) {
00649       sortIt = todoListSorted.begin();
00650       if ( sortDirection == SortDirectionAscending ) {
00651         while ( sortIt != todoListSorted.end() &&
00652                 (*eit)->priority() >= (*sortIt)->priority() ) {
00653           ++sortIt;
00654         }
00655       } else {
00656         while ( sortIt != todoListSorted.end() &&
00657                 (*eit)->priority() < (*sortIt)->priority() ) {
00658           ++sortIt;
00659         }
00660       }
00661       todoListSorted.insert( sortIt, *eit );
00662     }
00663     break;
00664 
00665   case TodoSortPercentComplete:
00666     alphaList = sortTodos( todoList, TodoSortSummary, sortDirection );
00667     for ( eit = alphaList.begin(); eit != alphaList.end(); ++eit ) {
00668       sortIt = todoListSorted.begin();
00669       if ( sortDirection == SortDirectionAscending ) {
00670         while ( sortIt != todoListSorted.end() &&
00671                 (*eit)->percentComplete() >= (*sortIt)->percentComplete() ) {
00672           ++sortIt;
00673         }
00674       } else {
00675         while ( sortIt != todoListSorted.end() &&
00676                 (*eit)->percentComplete() < (*sortIt)->percentComplete() ) {
00677           ++sortIt;
00678         }
00679       }
00680       todoListSorted.insert( sortIt, *eit );
00681     }
00682     break;
00683 
00684   case TodoSortSummary:
00685     for ( eit = todoList->begin(); eit != todoList->end(); ++eit ) {
00686       sortIt = todoListSorted.begin();
00687       if ( sortDirection == SortDirectionAscending ) {
00688         while ( sortIt != todoListSorted.end() &&
00689                 (*eit)->summary() >= (*sortIt)->summary() ) {
00690           ++sortIt;
00691         }
00692       } else {
00693         while ( sortIt != todoListSorted.end() &&
00694                 (*eit)->summary() < (*sortIt)->summary() ) {
00695           ++sortIt;
00696         }
00697       }
00698       todoListSorted.insert( sortIt, *eit );
00699     }
00700     break;
00701   }
00702 
00703   return todoListSorted;
00704 }
00705 
00706 Todo::List Calendar::todos( TodoSortField sortField,
00707                             SortDirection sortDirection )
00708 {
00709   Todo::List tl = rawTodos( sortField, sortDirection );
00710   d->mFilter->apply( &tl );
00711   return tl;
00712 }
00713 
00714 Todo::List Calendar::todos( const QDate &date )
00715 {
00716   Todo::List el = rawTodosForDate( date );
00717   d->mFilter->apply( &el );
00718   return el;
00719 }
00720 
00721 Journal::List Calendar::sortJournals( Journal::List *journalList,
00722                                       JournalSortField sortField,
00723                                       SortDirection sortDirection )
00724 {
00725   Journal::List journalListSorted;
00726   Journal::List::Iterator sortIt;
00727   Journal::List::Iterator eit;
00728 
00729   switch( sortField ) {
00730   case JournalSortUnsorted:
00731     journalListSorted = *journalList;
00732     break;
00733 
00734   case JournalSortDate:
00735     for ( eit = journalList->begin(); eit != journalList->end(); ++eit ) {
00736       sortIt = journalListSorted.begin();
00737       if ( sortDirection == SortDirectionAscending ) {
00738         while ( sortIt != journalListSorted.end() &&
00739                 (*eit)->dtStart() >= (*sortIt)->dtStart() ) {
00740           ++sortIt;
00741         }
00742       } else {
00743         while ( sortIt != journalListSorted.end() &&
00744                 (*eit)->dtStart() < (*sortIt)->dtStart() ) {
00745           ++sortIt;
00746         }
00747       }
00748       journalListSorted.insert( sortIt, *eit );
00749     }
00750     break;
00751 
00752   case JournalSortSummary:
00753     for ( eit = journalList->begin(); eit != journalList->end(); ++eit ) {
00754       sortIt = journalListSorted.begin();
00755       if ( sortDirection == SortDirectionAscending ) {
00756         while ( sortIt != journalListSorted.end() &&
00757                 (*eit)->summary() >= (*sortIt)->summary() ) {
00758           ++sortIt;
00759         }
00760       } else {
00761         while ( sortIt != journalListSorted.end() &&
00762                 (*eit)->summary() < (*sortIt)->summary() ) {
00763           ++sortIt;
00764         }
00765       }
00766       journalListSorted.insert( sortIt, *eit );
00767     }
00768     break;
00769   }
00770 
00771   return journalListSorted;
00772 }
00773 
00774 Journal::List Calendar::journals( JournalSortField sortField,
00775                                   SortDirection sortDirection )
00776 {
00777   Journal::List jl = rawJournals( sortField, sortDirection );
00778   d->mFilter->apply( &jl );
00779   return jl;
00780 }
00781 
00782 Journal::List Calendar::journals( const QDate &date )
00783 {
00784   Journal::List el = rawJournalsForDate( date );
00785   d->mFilter->apply( &el );
00786   return el;
00787 }
00788 
00789 // When this is called, the to-dos have already been added to the calendar.
00790 // This method is only about linking related to-dos.
00791 void Calendar::setupRelations( Incidence *forincidence )
00792 {
00793   if ( !forincidence ) {
00794     return;
00795   }
00796 
00797   QString uid = forincidence->uid();
00798 
00799   // First, go over the list of orphans and see if this is their parent
00800   QList<Incidence*> l = d->mOrphans.values( uid );
00801   d->mOrphans.remove( uid );
00802   for ( int i = 0, end = l.count();  i < end;  ++i ) {
00803     l[i]->setRelatedTo( forincidence );
00804     forincidence->addRelation( l[i] );
00805     d->mOrphanUids.remove( l[i]->uid() );
00806   }
00807 
00808   // Now see about this incidences parent
00809   if ( !forincidence->relatedTo() && !forincidence->relatedToUid().isEmpty() ) {
00810     // Incidence has a uid it is related to but is not registered to it yet.
00811     // Try to find it
00812     Incidence *parent = incidence( forincidence->relatedToUid() );
00813     if ( parent ) {
00814       // Found it
00815       forincidence->setRelatedTo( parent );
00816       parent->addRelation( forincidence );
00817     } else {
00818       // Not found, put this in the mOrphans list
00819       // Note that the mOrphans dict might contain multiple entries with the
00820       // same key! which are multiple children that wait for the parent
00821       // incidence to be inserted.
00822       d->mOrphans.insert( forincidence->relatedToUid(), forincidence );
00823       d->mOrphanUids.insert( forincidence->uid(), forincidence );
00824     }
00825   }
00826 }
00827 
00828 // If a to-do with sub-to-dos is deleted, move it's sub-to-dos to the orphan list
00829 void Calendar::removeRelations( Incidence *incidence )
00830 {
00831   if ( !incidence ) {
00832     kDebug() << "Warning: incidence is 0";
00833     return;
00834   }
00835 
00836   QString uid = incidence->uid();
00837   foreach ( Incidence *i, incidence->relations() ) {
00838     if ( !d->mOrphanUids.contains( i->uid() ) ) {
00839       d->mOrphans.insert( uid, i );
00840       d->mOrphanUids.insert( i->uid(), i );
00841       i->setRelatedTo( 0 );
00842       i->setRelatedToUid( uid );
00843     }
00844   }
00845 
00846   // If this incidence is related to something else, tell that about it
00847   if ( incidence->relatedTo() ) {
00848     incidence->relatedTo()->removeRelation( incidence );
00849   }
00850 
00851   // Remove this one from the orphans list
00852   if ( d->mOrphanUids.remove( uid ) ) {
00853     // This incidence is located in the orphans list - it should be removed
00854     // Since the mOrphans dict might contain the same key (with different
00855     // child incidence pointers!) multiple times, take care that we remove
00856     // the correct one. So we need to remove all items with the given
00857     // parent UID, and readd those that are not for this item. Also, there
00858     // might be other entries with differnet UID that point to this
00859     // incidence (this might happen when the relatedTo of the item is
00860     // changed before its parent is inserted. This might happen with
00861     // groupware servers....). Remove them, too
00862     QStringList relatedToUids;
00863 
00864     // First, create a list of all keys in the mOrphans list which point
00865     // to the removed item
00866     relatedToUids << incidence->relatedToUid();
00867     for ( QMultiHash<QString, Incidence*>::Iterator it = d->mOrphans.begin();
00868           it != d->mOrphans.end(); ++it ) {
00869       if ( it.value()->uid() == uid ) {
00870         relatedToUids << it.key();
00871       }
00872     }
00873 
00874     // now go through all uids that have one entry that point to the incidence
00875     for ( QStringList::const_iterator uidit = relatedToUids.constBegin();
00876           uidit != relatedToUids.constEnd(); ++uidit ) {
00877       Incidence::List tempList;
00878       // Remove all to get access to the remaining entries
00879       QList<Incidence*> l = d->mOrphans.values( *uidit );
00880       d->mOrphans.remove( *uidit );
00881       foreach ( Incidence *i, l ) {
00882         if ( i != incidence ) {
00883           tempList.append( i );
00884         }
00885       }
00886       // Readd those that point to a different orphan incidence
00887       for ( Incidence::List::Iterator incit = tempList.begin();
00888             incit != tempList.end(); ++incit ) {
00889         d->mOrphans.insert( *uidit, *incit );
00890       }
00891     }
00892   }
00893 }
00894 
00895 void Calendar::CalendarObserver::calendarModified( bool modified, Calendar *calendar )
00896 {
00897   Q_UNUSED( modified );
00898   Q_UNUSED( calendar );
00899 }
00900 
00901 void Calendar::CalendarObserver::calendarIncidenceAdded( Incidence *incidence )
00902 {
00903   Q_UNUSED( incidence );
00904 }
00905 
00906 void Calendar::CalendarObserver::calendarIncidenceChanged( Incidence *incidence )
00907 {
00908   Q_UNUSED( incidence );
00909 }
00910 
00911 void Calendar::CalendarObserver::calendarIncidenceDeleted( Incidence *incidence )
00912 {
00913   Q_UNUSED( incidence );
00914 }
00915 
00916 void Calendar::registerObserver( CalendarObserver *observer )
00917 {
00918   if ( !d->mObservers.contains( observer ) ) {
00919     d->mObservers.append( observer );
00920   }
00921   d->mNewObserver = true;
00922 }
00923 
00924 void Calendar::unregisterObserver( CalendarObserver *observer )
00925 {
00926   d->mObservers.removeAll( observer );
00927 }
00928 
00929 bool Calendar::isSaving()
00930 {
00931   return false;
00932 }
00933 
00934 void Calendar::setModified( bool modified )
00935 {
00936   if ( modified != d->mModified || d->mNewObserver ) {
00937     d->mNewObserver = false;
00938     foreach ( CalendarObserver *observer, d->mObservers ) {
00939       observer->calendarModified( modified, this );
00940     }
00941     d->mModified = modified;
00942   }
00943 }
00944 
00945 bool Calendar::isModified() const
00946 {
00947   return d->mModified;
00948 }
00949 
00950 void Calendar::incidenceUpdated( IncidenceBase *incidence )
00951 {
00952   incidence->setLastModified( KDateTime::currentUtcDateTime() );
00953   // we should probably update the revision number here,
00954   // or internally in the Event itself when certain things change.
00955   // need to verify with ical documentation.
00956 
00957   // The static_cast is ok as the CalendarLocal only observes Incidence objects
00958   notifyIncidenceChanged( static_cast<Incidence *>( incidence ) );
00959 
00960   setModified( true );
00961 }
00962 
00963 void Calendar::doSetTimeSpec( const KDateTime::Spec &timeSpec )
00964 {
00965   Q_UNUSED( timeSpec );
00966 }
00967 
00968 void Calendar::notifyIncidenceAdded( Incidence *i )
00969 {
00970   if ( !d->mObserversEnabled ) {
00971     return;
00972   }
00973 
00974   foreach ( CalendarObserver *observer, d->mObservers ) {
00975     observer->calendarIncidenceAdded( i );
00976   }
00977 }
00978 
00979 void Calendar::notifyIncidenceChanged( Incidence *i )
00980 {
00981   if ( !d->mObserversEnabled ) {
00982     return;
00983   }
00984 
00985   foreach ( CalendarObserver *observer, d->mObservers ) {
00986     observer->calendarIncidenceChanged( i );
00987   }
00988 }
00989 
00990 void Calendar::notifyIncidenceDeleted( Incidence *i )
00991 {
00992   if ( !d->mObserversEnabled ) {
00993     return;
00994   }
00995 
00996   foreach ( CalendarObserver *observer, d->mObservers ) {
00997     observer->calendarIncidenceDeleted( i );
00998   }
00999 }
01000 
01001 void Calendar::customPropertyUpdated()
01002 {
01003   setModified( true );
01004 }
01005 
01006 void Calendar::setProductId( const QString &id )
01007 {
01008   d->mProductId = id;
01009 }
01010 
01011 QString Calendar::productId() const
01012 {
01013   return d->mProductId;
01014 }
01015 
01016 Incidence::List Calendar::mergeIncidenceList( const Event::List &events,
01017                                               const Todo::List &todos,
01018                                               const Journal::List &journals )
01019 {
01020   Incidence::List incidences;
01021 
01022   int i, end;
01023   for ( i = 0, end = events.count();  i < end;  ++i ) {
01024     incidences.append( events[i] );
01025   }
01026 
01027   for ( i = 0, end = todos.count();  i < end;  ++i ) {
01028     incidences.append( todos[i] );
01029   }
01030 
01031   for ( i = 0, end = journals.count();  i < end;  ++i ) {
01032     incidences.append( journals[i] );
01033   }
01034 
01035   return incidences;
01036 }
01037 
01038 bool Calendar::beginChange( Incidence *incidence )
01039 {
01040   Q_UNUSED( incidence );
01041   return true;
01042 }
01043 
01044 bool Calendar::endChange( Incidence *incidence )
01045 {
01046   Q_UNUSED( incidence );
01047   return true;
01048 }
01049 
01050 void Calendar::setObserversEnabled( bool enabled )
01051 {
01052   d->mObserversEnabled = enabled;
01053 }
01054 
01055 void Calendar::appendAlarms( Alarm::List &alarms, Incidence *incidence,
01056                              const KDateTime &from, const KDateTime &to )
01057 {
01058   KDateTime preTime = from.addSecs(-1);
01059 
01060   Alarm::List alarmlist = incidence->alarms();
01061   for ( int i = 0, iend = alarmlist.count();  i < iend;  ++i ) {
01062     if ( alarmlist[i]->enabled() ) {
01063       KDateTime dt = alarmlist[i]->nextRepetition( preTime );
01064       if ( dt.isValid() && dt <= to ) {
01065         kDebug() << incidence->summary() << "':" << dt.toString();
01066         alarms.append( alarmlist[i] );
01067       }
01068     }
01069   }
01070 }
01071 
01072 void Calendar::appendRecurringAlarms( Alarm::List &alarms,
01073                                       Incidence *incidence,
01074                                       const KDateTime &from,
01075                                       const KDateTime &to )
01076 {
01077   KDateTime dt;
01078   bool endOffsetValid = false;
01079   Duration endOffset( 0 );
01080   Duration period( from, to );
01081 
01082   Alarm::List alarmlist = incidence->alarms();
01083   for ( int i = 0, iend = alarmlist.count();  i < iend;  ++i ) {
01084     Alarm *a = alarmlist[i];
01085     if ( a->enabled() ) {
01086       if ( a->hasTime() ) {
01087         // The alarm time is defined as an absolute date/time
01088         dt = a->nextRepetition( from.addSecs(-1) );
01089         if ( !dt.isValid() || dt > to ) {
01090           continue;
01091         }
01092       } else {
01093         // Alarm time is defined by an offset from the event start or end time.
01094         // Find the offset from the event start time, which is also used as the
01095         // offset from the recurrence time.
01096         Duration offset( 0 );
01097         if ( a->hasStartOffset() ) {
01098           offset = a->startOffset();
01099         } else if ( a->hasEndOffset() ) {
01100           offset = a->endOffset();
01101           if ( !endOffsetValid ) {
01102             endOffset = Duration( incidence->dtStart(), incidence->dtEnd() );
01103             endOffsetValid = true;
01104           }
01105         }
01106 
01107         // Find the incidence's earliest alarm
01108         KDateTime alarmStart =
01109           offset.end( a->hasEndOffset() ? incidence->dtEnd() : incidence->dtStart() );
01110 //        KDateTime alarmStart = incidence->dtStart().addSecs( offset );
01111         if ( alarmStart > to ) {
01112           continue;
01113         }
01114         KDateTime baseStart = incidence->dtStart();
01115         if ( from > alarmStart ) {
01116           alarmStart = from;   // don't look earlier than the earliest alarm
01117           baseStart = (-offset).end( (-endOffset).end( alarmStart ) );
01118         }
01119 
01120         // Adjust the 'alarmStart' date/time and find the next recurrence at or after it.
01121         // Treate the two offsets separately in case one is daily and the other not.
01122         dt = incidence->recurrence()->getNextDateTime( baseStart.addSecs(-1) );
01123         if ( !dt.isValid() ||
01124              ( dt = endOffset.end( offset.end( dt ) ) ) > to ) // adjust 'dt' to get the alarm time
01125         {
01126           // The next recurrence is too late.
01127           if ( !a->repeatCount() ) {
01128             continue;
01129           }
01130 
01131           // The alarm has repetitions, so check whether repetitions of previous
01132           // recurrences fall within the time period.
01133           bool found = false;
01134           Duration alarmDuration = a->duration();
01135           for ( KDateTime base = baseStart;
01136                 ( dt = incidence->recurrence()->getPreviousDateTime( base ) ).isValid();
01137                 base = dt ) {
01138             if ( a->duration().end( dt ) < base ) {
01139               break;  // this recurrence's last repetition is too early, so give up
01140             }
01141 
01142             // The last repetition of this recurrence is at or after 'alarmStart' time.
01143             // Check if a repetition occurs between 'alarmStart' and 'to'.
01144             int snooze = a->snoozeTime().value();   // in seconds or days
01145             if ( a->snoozeTime().isDaily() ) {
01146               Duration toFromDuration( dt, base );
01147               int toFrom = toFromDuration.asDays();
01148               if ( a->snoozeTime().end( from ) <= to ||
01149                    ( toFromDuration.isDaily() && toFrom % snooze == 0 ) ||
01150                    ( toFrom / snooze + 1 ) * snooze <= toFrom + period.asDays() ) {
01151                 found = true;
01152 #ifndef NDEBUG
01153                 // for debug output
01154                 dt = offset.end( dt ).addDays( ( ( toFrom - 1 ) / snooze + 1 ) * snooze );
01155 #endif
01156                 break;
01157               }
01158             } else {
01159               int toFrom = dt.secsTo( base );
01160               if ( period.asSeconds() >= snooze ||
01161                    toFrom % snooze == 0 ||
01162                    ( toFrom / snooze + 1 ) * snooze <= toFrom + period.asSeconds() )
01163               {
01164                 found = true;
01165 #ifndef NDEBUG
01166                 // for debug output
01167                 dt = offset.end( dt ).addSecs( ( ( toFrom - 1 ) / snooze + 1 ) * snooze );
01168 #endif
01169                 break;
01170               }
01171             }
01172           }
01173           if ( !found ) {
01174             continue;
01175           }
01176         }
01177       }
01178       kDebug() << incidence->summary() << "':" << dt.toString();
01179       alarms.append( a );
01180     }
01181   }
01182 }
01183 
01184 #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
  •   richtextbuilders
  • kpimutils
  • kresources
  • ktnef
  • kxmlrpcclient
  • mailtransport
  • qgpgme
  • syndication
  •   atom
  •   rdf
  •   rss2
Generated for KDE-PIM Libraries by doxygen 1.5.8
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