• Skip to content
  • Skip to link menu
KDE 4.3 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   // Do not call setRelatedTo() when dissociating recurring to-dos, otherwise the new to-do
00461   // will appear as a child.  Originally, we planned to set a relation with reltype SIBLING
00462   // when dissociating to-dos, but currently kcal only supports reltype PARENT.
00463   // We can uncomment the following line when we support the PARENT reltype.
00464   //newInc->setRelatedTo( incidence );
00465   Recurrence *recur = newInc->recurrence();
00466   if ( single ) {
00467     recur->clear();
00468   } else {
00469     // Adjust the recurrence for the future incidences. In particular adjust
00470     // the "end after n occurrences" rules! "No end date" and "end by ..."
00471     // don't need to be modified.
00472     int duration = recur->duration();
00473     if ( duration > 0 ) {
00474       int doneduration = recur->durationTo( date.addDays( -1 ) );
00475       if ( doneduration >= duration ) {
00476         kDebug() << "The dissociated event already occurred more often"
00477                  << "than it was supposed to ever occur. ERROR!";
00478         recur->clear();
00479       } else {
00480         recur->setDuration( duration - doneduration );
00481       }
00482     }
00483   }
00484   // Adjust the date of the incidence
00485   if ( incidence->type() == "Event" ) {
00486     Event *ev = static_cast<Event *>( newInc );
00487     KDateTime start( ev->dtStart() );
00488     int daysTo = start.toTimeSpec( spec ).date().daysTo( date );
00489     ev->setDtStart( start.addDays( daysTo ) );
00490     ev->setDtEnd( ev->dtEnd().addDays( daysTo ) );
00491   } else if ( incidence->type() == "Todo" ) {
00492     Todo *td = static_cast<Todo *>( newInc );
00493     bool haveOffset = false;
00494     int daysTo = 0;
00495     if ( td->hasDueDate() ) {
00496       KDateTime due( td->dtDue() );
00497       daysTo = due.toTimeSpec( spec ).date().daysTo( date );
00498       td->setDtDue( due.addDays( daysTo ), true );
00499       haveOffset = true;
00500     }
00501     if ( td->hasStartDate() ) {
00502       KDateTime start( td->dtStart() );
00503       if ( !haveOffset ) {
00504         daysTo = start.toTimeSpec( spec ).date().daysTo( date );
00505       }
00506       td->setDtStart( start.addDays( daysTo ) );
00507       haveOffset = true;
00508     }
00509   }
00510   recur = incidence->recurrence();
00511   if ( recur ) {
00512     if ( single ) {
00513       recur->addExDate( date );
00514     } else {
00515       // Make sure the recurrence of the past events ends
00516       // at the corresponding day
00517       recur->setEndDate( date.addDays(-1) );
00518     }
00519   }
00520   return newInc;
00521 }
00522 
00523 Incidence *Calendar::incidence( const QString &uid )
00524 {
00525   Incidence *i = event( uid );
00526   if ( i ) {
00527     return i;
00528   }
00529 
00530   i = todo( uid );
00531   if ( i ) {
00532     return i;
00533   }
00534 
00535   i = journal( uid );
00536   return i;
00537 }
00538 
00539 Incidence::List Calendar::incidencesFromSchedulingID( const QString &sid )
00540 {
00541   Incidence::List result;
00542   const Incidence::List incidences = rawIncidences();
00543   Incidence::List::const_iterator it = incidences.begin();
00544   for ( ; it != incidences.end(); ++it ) {
00545     if ( (*it)->schedulingID() == sid ) {
00546       result.append( *it );
00547     }
00548   }
00549   return result;
00550 }
00551 
00552 Incidence *Calendar::incidenceFromSchedulingID( const QString &UID )
00553 {
00554   const Incidence::List incidences = rawIncidences();
00555   Incidence::List::const_iterator it = incidences.begin();
00556   for ( ; it != incidences.end(); ++it ) {
00557     if ( (*it)->schedulingID() == UID ) {
00558       // Touchdown, and the crowd goes wild
00559       return *it;
00560     }
00561   }
00562   // Not found
00563   return 0;
00564 }
00565 
00566 Todo::List Calendar::sortTodos( Todo::List *todoList,
00567                                 TodoSortField sortField,
00568                                 SortDirection sortDirection )
00569 {
00570   Todo::List todoListSorted;
00571   Todo::List tempList, t;
00572   Todo::List alphaList;
00573   Todo::List::Iterator sortIt;
00574   Todo::List::Iterator eit;
00575 
00576   // Notice we alphabetically presort Summaries first.
00577   // We do this so comparison "ties" stay in a nice order.
00578 
00579   // Note that To-dos may not have Start DateTimes nor due DateTimes.
00580 
00581   switch( sortField ) {
00582   case TodoSortUnsorted:
00583     todoListSorted = *todoList;
00584     break;
00585 
00586   case TodoSortStartDate:
00587     alphaList = sortTodos( todoList, TodoSortSummary, sortDirection );
00588     for ( eit = alphaList.begin(); eit != alphaList.end(); ++eit ) {
00589       if ( (*eit)->hasStartDate() ) {
00590         sortIt = todoListSorted.begin();
00591         if ( sortDirection == SortDirectionAscending ) {
00592           while ( sortIt != todoListSorted.end() &&
00593                   (*eit)->dtStart() >= (*sortIt)->dtStart() ) {
00594             ++sortIt;
00595           }
00596         } else {
00597           while ( sortIt != todoListSorted.end() &&
00598                   (*eit)->dtStart() < (*sortIt)->dtStart() ) {
00599             ++sortIt;
00600           }
00601         }
00602         todoListSorted.insert( sortIt, *eit );
00603       } else {
00604         // Keep a list of the To-dos without Start DateTimes
00605         tempList.append( *eit );
00606       }
00607     }
00608     if ( sortDirection == SortDirectionAscending ) {
00609       // Append the list of To-dos without Start DateTimes
00610       todoListSorted += tempList;
00611     } else {
00612       // Prepend the list of To-dos without Start DateTimes
00613       tempList += todoListSorted;
00614       todoListSorted = tempList;
00615     }
00616     break;
00617 
00618   case TodoSortDueDate:
00619     alphaList = sortTodos( todoList, TodoSortSummary, sortDirection );
00620     for ( eit = alphaList.begin(); eit != alphaList.end(); ++eit ) {
00621       if ( (*eit)->hasDueDate() ) {
00622         sortIt = todoListSorted.begin();
00623         if ( sortDirection == SortDirectionAscending ) {
00624           while ( sortIt != todoListSorted.end() &&
00625                   (*eit)->dtDue() >= (*sortIt)->dtDue() ) {
00626             ++sortIt;
00627           }
00628         } else {
00629           while ( sortIt != todoListSorted.end() &&
00630                   (*eit)->dtDue() < (*sortIt)->dtDue() ) {
00631             ++sortIt;
00632           }
00633         }
00634         todoListSorted.insert( sortIt, *eit );
00635       } else {
00636         // Keep a list of the To-dos without Due DateTimes
00637         tempList.append( *eit );
00638       }
00639     }
00640     if ( sortDirection == SortDirectionAscending ) {
00641       // Append the list of To-dos without Due DateTimes
00642       todoListSorted += tempList;
00643     } else {
00644       // Prepend the list of To-dos without Due DateTimes
00645       tempList += todoListSorted;
00646       todoListSorted = tempList;
00647     }
00648     break;
00649 
00650   case TodoSortPriority:
00651     alphaList = sortTodos( todoList, TodoSortSummary, sortDirection );
00652     for ( eit = alphaList.begin(); eit != alphaList.end(); ++eit ) {
00653       sortIt = todoListSorted.begin();
00654       if ( sortDirection == SortDirectionAscending ) {
00655         while ( sortIt != todoListSorted.end() &&
00656                 (*eit)->priority() >= (*sortIt)->priority() ) {
00657           ++sortIt;
00658         }
00659       } else {
00660         while ( sortIt != todoListSorted.end() &&
00661                 (*eit)->priority() < (*sortIt)->priority() ) {
00662           ++sortIt;
00663         }
00664       }
00665       todoListSorted.insert( sortIt, *eit );
00666     }
00667     break;
00668 
00669   case TodoSortPercentComplete:
00670     alphaList = sortTodos( todoList, TodoSortSummary, sortDirection );
00671     for ( eit = alphaList.begin(); eit != alphaList.end(); ++eit ) {
00672       sortIt = todoListSorted.begin();
00673       if ( sortDirection == SortDirectionAscending ) {
00674         while ( sortIt != todoListSorted.end() &&
00675                 (*eit)->percentComplete() >= (*sortIt)->percentComplete() ) {
00676           ++sortIt;
00677         }
00678       } else {
00679         while ( sortIt != todoListSorted.end() &&
00680                 (*eit)->percentComplete() < (*sortIt)->percentComplete() ) {
00681           ++sortIt;
00682         }
00683       }
00684       todoListSorted.insert( sortIt, *eit );
00685     }
00686     break;
00687 
00688   case TodoSortSummary:
00689     for ( eit = todoList->begin(); eit != todoList->end(); ++eit ) {
00690       sortIt = todoListSorted.begin();
00691       if ( sortDirection == SortDirectionAscending ) {
00692         while ( sortIt != todoListSorted.end() &&
00693                 (*eit)->summary() >= (*sortIt)->summary() ) {
00694           ++sortIt;
00695         }
00696       } else {
00697         while ( sortIt != todoListSorted.end() &&
00698                 (*eit)->summary() < (*sortIt)->summary() ) {
00699           ++sortIt;
00700         }
00701       }
00702       todoListSorted.insert( sortIt, *eit );
00703     }
00704     break;
00705   }
00706 
00707   return todoListSorted;
00708 }
00709 
00710 Todo::List Calendar::todos( TodoSortField sortField,
00711                             SortDirection sortDirection )
00712 {
00713   Todo::List tl = rawTodos( sortField, sortDirection );
00714   d->mFilter->apply( &tl );
00715   return tl;
00716 }
00717 
00718 Todo::List Calendar::todos( const QDate &date )
00719 {
00720   Todo::List el = rawTodosForDate( date );
00721   d->mFilter->apply( &el );
00722   return el;
00723 }
00724 
00725 Journal::List Calendar::sortJournals( Journal::List *journalList,
00726                                       JournalSortField sortField,
00727                                       SortDirection sortDirection )
00728 {
00729   Journal::List journalListSorted;
00730   Journal::List::Iterator sortIt;
00731   Journal::List::Iterator eit;
00732 
00733   switch( sortField ) {
00734   case JournalSortUnsorted:
00735     journalListSorted = *journalList;
00736     break;
00737 
00738   case JournalSortDate:
00739     for ( eit = journalList->begin(); eit != journalList->end(); ++eit ) {
00740       sortIt = journalListSorted.begin();
00741       if ( sortDirection == SortDirectionAscending ) {
00742         while ( sortIt != journalListSorted.end() &&
00743                 (*eit)->dtStart() >= (*sortIt)->dtStart() ) {
00744           ++sortIt;
00745         }
00746       } else {
00747         while ( sortIt != journalListSorted.end() &&
00748                 (*eit)->dtStart() < (*sortIt)->dtStart() ) {
00749           ++sortIt;
00750         }
00751       }
00752       journalListSorted.insert( sortIt, *eit );
00753     }
00754     break;
00755 
00756   case JournalSortSummary:
00757     for ( eit = journalList->begin(); eit != journalList->end(); ++eit ) {
00758       sortIt = journalListSorted.begin();
00759       if ( sortDirection == SortDirectionAscending ) {
00760         while ( sortIt != journalListSorted.end() &&
00761                 (*eit)->summary() >= (*sortIt)->summary() ) {
00762           ++sortIt;
00763         }
00764       } else {
00765         while ( sortIt != journalListSorted.end() &&
00766                 (*eit)->summary() < (*sortIt)->summary() ) {
00767           ++sortIt;
00768         }
00769       }
00770       journalListSorted.insert( sortIt, *eit );
00771     }
00772     break;
00773   }
00774 
00775   return journalListSorted;
00776 }
00777 
00778 Journal::List Calendar::journals( JournalSortField sortField,
00779                                   SortDirection sortDirection )
00780 {
00781   Journal::List jl = rawJournals( sortField, sortDirection );
00782   d->mFilter->apply( &jl );
00783   return jl;
00784 }
00785 
00786 Journal::List Calendar::journals( const QDate &date )
00787 {
00788   Journal::List el = rawJournalsForDate( date );
00789   d->mFilter->apply( &el );
00790   return el;
00791 }
00792 
00793 // When this is called, the to-dos have already been added to the calendar.
00794 // This method is only about linking related to-dos.
00795 void Calendar::setupRelations( Incidence *forincidence )
00796 {
00797   if ( !forincidence ) {
00798     return;
00799   }
00800 
00801   QString uid = forincidence->uid();
00802 
00803   // First, go over the list of orphans and see if this is their parent
00804   QList<Incidence*> l = d->mOrphans.values( uid );
00805   d->mOrphans.remove( uid );
00806   for ( int i = 0, end = l.count();  i < end;  ++i ) {
00807     l[i]->setRelatedTo( forincidence );
00808     forincidence->addRelation( l[i] );
00809     d->mOrphanUids.remove( l[i]->uid() );
00810   }
00811 
00812   // Now see about this incidences parent
00813   if ( !forincidence->relatedTo() && !forincidence->relatedToUid().isEmpty() ) {
00814     // Incidence has a uid it is related to but is not registered to it yet.
00815     // Try to find it
00816     Incidence *parent = incidence( forincidence->relatedToUid() );
00817     if ( parent ) {
00818       // Found it
00819       forincidence->setRelatedTo( parent );
00820       parent->addRelation( forincidence );
00821     } else {
00822       // Not found, put this in the mOrphans list
00823       // Note that the mOrphans dict might contain multiple entries with the
00824       // same key! which are multiple children that wait for the parent
00825       // incidence to be inserted.
00826       d->mOrphans.insert( forincidence->relatedToUid(), forincidence );
00827       d->mOrphanUids.insert( forincidence->uid(), forincidence );
00828     }
00829   }
00830 }
00831 
00832 // If a to-do with sub-to-dos is deleted, move it's sub-to-dos to the orphan list
00833 void Calendar::removeRelations( Incidence *incidence )
00834 {
00835   if ( !incidence ) {
00836     kDebug() << "Warning: incidence is 0";
00837     return;
00838   }
00839 
00840   QString uid = incidence->uid();
00841   foreach ( Incidence *i, incidence->relations() ) {
00842     if ( !d->mOrphanUids.contains( i->uid() ) ) {
00843       d->mOrphans.insert( uid, i );
00844       d->mOrphanUids.insert( i->uid(), i );
00845       i->setRelatedTo( 0 );
00846       i->setRelatedToUid( uid );
00847     }
00848   }
00849 
00850   // If this incidence is related to something else, tell that about it
00851   if ( incidence->relatedTo() ) {
00852     incidence->relatedTo()->removeRelation( incidence );
00853   }
00854 
00855   // Remove this one from the orphans list
00856   if ( d->mOrphanUids.remove( uid ) ) {
00857     // This incidence is located in the orphans list - it should be removed
00858     // Since the mOrphans dict might contain the same key (with different
00859     // child incidence pointers!) multiple times, take care that we remove
00860     // the correct one. So we need to remove all items with the given
00861     // parent UID, and readd those that are not for this item. Also, there
00862     // might be other entries with differnet UID that point to this
00863     // incidence (this might happen when the relatedTo of the item is
00864     // changed before its parent is inserted. This might happen with
00865     // groupware servers....). Remove them, too
00866     QStringList relatedToUids;
00867 
00868     // First, create a list of all keys in the mOrphans list which point
00869     // to the removed item
00870     relatedToUids << incidence->relatedToUid();
00871     for ( QMultiHash<QString, Incidence*>::Iterator it = d->mOrphans.begin();
00872           it != d->mOrphans.end(); ++it ) {
00873       if ( it.value()->uid() == uid ) {
00874         relatedToUids << it.key();
00875       }
00876     }
00877 
00878     // now go through all uids that have one entry that point to the incidence
00879     for ( QStringList::const_iterator uidit = relatedToUids.constBegin();
00880           uidit != relatedToUids.constEnd(); ++uidit ) {
00881       Incidence::List tempList;
00882       // Remove all to get access to the remaining entries
00883       QList<Incidence*> l = d->mOrphans.values( *uidit );
00884       d->mOrphans.remove( *uidit );
00885       foreach ( Incidence *i, l ) {
00886         if ( i != incidence ) {
00887           tempList.append( i );
00888         }
00889       }
00890       // Readd those that point to a different orphan incidence
00891       for ( Incidence::List::Iterator incit = tempList.begin();
00892             incit != tempList.end(); ++incit ) {
00893         d->mOrphans.insert( *uidit, *incit );
00894       }
00895     }
00896   }
00897 
00898   // Make sure the deleted incidence doesn't relate to a non-deleted incidence, since
00899   // that would cause trouble in CalendarLocal::close(), as the deleted incidences
00900   // are destroyed after the non-deleted incidences. The destructor of the deleted
00901   // incidences would then try to access the already destroyed non-deleted incidence,
00902   // which would segfault.
00903   //
00904   // So in short: Make sure dead incidences don't point to alive incidences via the
00905   // relation.
00906   //
00907   // This crash is tested in CalendarLocalTest::testRelationsCrash().
00908   incidence->setRelatedTo( 0 );
00909 }
00910 
00911 void Calendar::CalendarObserver::calendarModified( bool modified, Calendar *calendar )
00912 {
00913   Q_UNUSED( modified );
00914   Q_UNUSED( calendar );
00915 }
00916 
00917 void Calendar::CalendarObserver::calendarIncidenceAdded( Incidence *incidence )
00918 {
00919   Q_UNUSED( incidence );
00920 }
00921 
00922 void Calendar::CalendarObserver::calendarIncidenceChanged( Incidence *incidence )
00923 {
00924   Q_UNUSED( incidence );
00925 }
00926 
00927 void Calendar::CalendarObserver::calendarIncidenceDeleted( Incidence *incidence )
00928 {
00929   Q_UNUSED( incidence );
00930 }
00931 
00932 void Calendar::registerObserver( CalendarObserver *observer )
00933 {
00934   if ( !d->mObservers.contains( observer ) ) {
00935     d->mObservers.append( observer );
00936   }
00937   d->mNewObserver = true;
00938 }
00939 
00940 void Calendar::unregisterObserver( CalendarObserver *observer )
00941 {
00942   d->mObservers.removeAll( observer );
00943 }
00944 
00945 bool Calendar::isSaving()
00946 {
00947   return false;
00948 }
00949 
00950 void Calendar::setModified( bool modified )
00951 {
00952   if ( modified != d->mModified || d->mNewObserver ) {
00953     d->mNewObserver = false;
00954     foreach ( CalendarObserver *observer, d->mObservers ) {
00955       observer->calendarModified( modified, this );
00956     }
00957     d->mModified = modified;
00958   }
00959 }
00960 
00961 bool Calendar::isModified() const
00962 {
00963   return d->mModified;
00964 }
00965 
00966 void Calendar::incidenceUpdated( IncidenceBase *incidence )
00967 {
00968   incidence->setLastModified( KDateTime::currentUtcDateTime() );
00969   // we should probably update the revision number here,
00970   // or internally in the Event itself when certain things change.
00971   // need to verify with ical documentation.
00972 
00973   // The static_cast is ok as the CalendarLocal only observes Incidence objects
00974   notifyIncidenceChanged( static_cast<Incidence *>( incidence ) );
00975 
00976   setModified( true );
00977 }
00978 
00979 void Calendar::doSetTimeSpec( const KDateTime::Spec &timeSpec )
00980 {
00981   Q_UNUSED( timeSpec );
00982 }
00983 
00984 void Calendar::notifyIncidenceAdded( Incidence *i )
00985 {
00986   if ( !d->mObserversEnabled ) {
00987     return;
00988   }
00989 
00990   foreach ( CalendarObserver *observer, d->mObservers ) {
00991     observer->calendarIncidenceAdded( i );
00992   }
00993 }
00994 
00995 void Calendar::notifyIncidenceChanged( Incidence *i )
00996 {
00997   if ( !d->mObserversEnabled ) {
00998     return;
00999   }
01000 
01001   foreach ( CalendarObserver *observer, d->mObservers ) {
01002     observer->calendarIncidenceChanged( i );
01003   }
01004 }
01005 
01006 void Calendar::notifyIncidenceDeleted( Incidence *i )
01007 {
01008   if ( !d->mObserversEnabled ) {
01009     return;
01010   }
01011 
01012   foreach ( CalendarObserver *observer, d->mObservers ) {
01013     observer->calendarIncidenceDeleted( i );
01014   }
01015 }
01016 
01017 void Calendar::customPropertyUpdated()
01018 {
01019   setModified( true );
01020 }
01021 
01022 void Calendar::setProductId( const QString &id )
01023 {
01024   d->mProductId = id;
01025 }
01026 
01027 QString Calendar::productId() const
01028 {
01029   return d->mProductId;
01030 }
01031 
01032 Incidence::List Calendar::mergeIncidenceList( const Event::List &events,
01033                                               const Todo::List &todos,
01034                                               const Journal::List &journals )
01035 {
01036   Incidence::List incidences;
01037 
01038   int i, end;
01039   for ( i = 0, end = events.count();  i < end;  ++i ) {
01040     incidences.append( events[i] );
01041   }
01042 
01043   for ( i = 0, end = todos.count();  i < end;  ++i ) {
01044     incidences.append( todos[i] );
01045   }
01046 
01047   for ( i = 0, end = journals.count();  i < end;  ++i ) {
01048     incidences.append( journals[i] );
01049   }
01050 
01051   return incidences;
01052 }
01053 
01054 bool Calendar::beginChange( Incidence *incidence )
01055 {
01056   Q_UNUSED( incidence );
01057   return true;
01058 }
01059 
01060 bool Calendar::endChange( Incidence *incidence )
01061 {
01062   Q_UNUSED( incidence );
01063   return true;
01064 }
01065 
01066 void Calendar::setObserversEnabled( bool enabled )
01067 {
01068   d->mObserversEnabled = enabled;
01069 }
01070 
01071 void Calendar::appendAlarms( Alarm::List &alarms, Incidence *incidence,
01072                              const KDateTime &from, const KDateTime &to )
01073 {
01074   KDateTime preTime = from.addSecs(-1);
01075 
01076   Alarm::List alarmlist = incidence->alarms();
01077   for ( int i = 0, iend = alarmlist.count();  i < iend;  ++i ) {
01078     if ( alarmlist[i]->enabled() ) {
01079       KDateTime dt = alarmlist[i]->nextRepetition( preTime );
01080       if ( dt.isValid() && dt <= to ) {
01081         kDebug() << incidence->summary() << "':" << dt.toString();
01082         alarms.append( alarmlist[i] );
01083       }
01084     }
01085   }
01086 }
01087 
01088 void Calendar::appendRecurringAlarms( Alarm::List &alarms,
01089                                       Incidence *incidence,
01090                                       const KDateTime &from,
01091                                       const KDateTime &to )
01092 {
01093   KDateTime dt;
01094   bool endOffsetValid = false;
01095   Duration endOffset( 0 );
01096   Duration period( from, to );
01097 
01098   Alarm::List alarmlist = incidence->alarms();
01099   for ( int i = 0, iend = alarmlist.count();  i < iend;  ++i ) {
01100     Alarm *a = alarmlist[i];
01101     if ( a->enabled() ) {
01102       if ( a->hasTime() ) {
01103         // The alarm time is defined as an absolute date/time
01104         dt = a->nextRepetition( from.addSecs(-1) );
01105         if ( !dt.isValid() || dt > to ) {
01106           continue;
01107         }
01108       } else {
01109         // Alarm time is defined by an offset from the event start or end time.
01110         // Find the offset from the event start time, which is also used as the
01111         // offset from the recurrence time.
01112         Duration offset( 0 );
01113         if ( a->hasStartOffset() ) {
01114           offset = a->startOffset();
01115         } else if ( a->hasEndOffset() ) {
01116           offset = a->endOffset();
01117           if ( !endOffsetValid ) {
01118             endOffset = Duration( incidence->dtStart(), incidence->dtEnd() );
01119             endOffsetValid = true;
01120           }
01121         }
01122 
01123         // Find the incidence's earliest alarm
01124         KDateTime alarmStart =
01125           offset.end( a->hasEndOffset() ? incidence->dtEnd() : incidence->dtStart() );
01126 //        KDateTime alarmStart = incidence->dtStart().addSecs( offset );
01127         if ( alarmStart > to ) {
01128           continue;
01129         }
01130         KDateTime baseStart = incidence->dtStart();
01131         if ( from > alarmStart ) {
01132           alarmStart = from;   // don't look earlier than the earliest alarm
01133           baseStart = (-offset).end( (-endOffset).end( alarmStart ) );
01134         }
01135 
01136         // Adjust the 'alarmStart' date/time and find the next recurrence at or after it.
01137         // Treate the two offsets separately in case one is daily and the other not.
01138         dt = incidence->recurrence()->getNextDateTime( baseStart.addSecs(-1) );
01139         if ( !dt.isValid() ||
01140              ( dt = endOffset.end( offset.end( dt ) ) ) > to ) // adjust 'dt' to get the alarm time
01141         {
01142           // The next recurrence is too late.
01143           if ( !a->repeatCount() ) {
01144             continue;
01145           }
01146 
01147           // The alarm has repetitions, so check whether repetitions of previous
01148           // recurrences fall within the time period.
01149           bool found = false;
01150           Duration alarmDuration = a->duration();
01151           for ( KDateTime base = baseStart;
01152                 ( dt = incidence->recurrence()->getPreviousDateTime( base ) ).isValid();
01153                 base = dt ) {
01154             if ( a->duration().end( dt ) < base ) {
01155               break;  // this recurrence's last repetition is too early, so give up
01156             }
01157 
01158             // The last repetition of this recurrence is at or after 'alarmStart' time.
01159             // Check if a repetition occurs between 'alarmStart' and 'to'.
01160             int snooze = a->snoozeTime().value();   // in seconds or days
01161             if ( a->snoozeTime().isDaily() ) {
01162               Duration toFromDuration( dt, base );
01163               int toFrom = toFromDuration.asDays();
01164               if ( a->snoozeTime().end( from ) <= to ||
01165                    ( toFromDuration.isDaily() && toFrom % snooze == 0 ) ||
01166                    ( toFrom / snooze + 1 ) * snooze <= toFrom + period.asDays() ) {
01167                 found = true;
01168 #ifndef NDEBUG
01169                 // for debug output
01170                 dt = offset.end( dt ).addDays( ( ( toFrom - 1 ) / snooze + 1 ) * snooze );
01171 #endif
01172                 break;
01173               }
01174             } else {
01175               int toFrom = dt.secsTo( base );
01176               if ( period.asSeconds() >= snooze ||
01177                    toFrom % snooze == 0 ||
01178                    ( toFrom / snooze + 1 ) * snooze <= toFrom + period.asSeconds() )
01179               {
01180                 found = true;
01181 #ifndef NDEBUG
01182                 // for debug output
01183                 dt = offset.end( dt ).addSecs( ( ( toFrom - 1 ) / snooze + 1 ) * snooze );
01184 #endif
01185                 break;
01186               }
01187             }
01188           }
01189           if ( !found ) {
01190             continue;
01191           }
01192         }
01193       }
01194       kDebug() << incidence->summary() << "':" << dt.toString();
01195       alarms.append( a );
01196     }
01197   }
01198 }
01199 
01200 #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
  • kholidays
  • kimap
  • kioslave
  •   imap4
  •   mbox
  • kldap
  • kmime
  • kpimidentities
  • kpimtextedit
  •   richtextbuilders
  • kpimutils
  • kresources
  • ktnef
  • kxmlrpcclient
  • mailtransport
  • microblog
  • 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