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