00001
00002
00003
00004
00005
00006
00007
00008
00009
00010
00011
00012
00013
00014
00015
00016
00017
00018
00019
00020
00021
00022
00035 #include "calendarlocal.h"
00036
00037 #include "incidence.h"
00038 #include "event.h"
00039 #include "todo.h"
00040 #include "journal.h"
00041 #include "filestorage.h"
00042 #include <QtCore/QDate>
00043 #include <QtCore/QHash>
00044 #include <QtCore/QMultiHash>
00045 #include <QtCore/QString>
00046
00047 #include <kdebug.h>
00048 #include <kdatetime.h>
00049 #include <klocale.h>
00050 #include <kmessagebox.h>
00051
00052 using namespace KCal;
00053
00058
00059 class KCal::CalendarLocal::Private
00060 {
00061 public:
00062 Private()
00063 {
00064 mDeletedIncidences.setAutoDelete( true );
00065 }
00066 QString mFileName;
00067 CalFormat *mFormat;
00068
00069 QHash<QString, Event *>mEvents;
00070 QMultiHash<QString, Event *>mEventsForDate;
00071 QHash<QString, Todo *>mTodos;
00072 QMultiHash<QString, Todo*>mTodosForDate;
00073 QHash<QString, Journal *>mJournals;
00074 QMultiHash<QString, Journal *>mJournalsForDate;
00075 Incidence::List mDeletedIncidences;
00076
00077 void insertEvent( Event *event );
00078 void insertTodo( Todo *todo );
00079 void insertJournal( Journal *journal );
00080 };
00081
00082
00083 namespace {
00084 template <typename T>
00085 void removeIncidenceFromMultiHashByUID( QMultiHash< QString, T >& container,
00086 const QString &key,
00087 const QString &uid )
00088 {
00089 const QList<T> values = container.values( key );
00090 QListIterator<T> it(values);
00091 while ( it.hasNext() ) {
00092 T const inc = it.next();
00093 if ( inc->uid() == uid ) {
00094 container.remove( key, inc );
00095 }
00096 }
00097 }
00098 }
00099
00100
00101 CalendarLocal::CalendarLocal( const KDateTime::Spec &timeSpec )
00102 : Calendar( timeSpec ),
00103 d( new KCal::CalendarLocal::Private )
00104 {
00105 }
00106
00107 CalendarLocal::CalendarLocal( const QString &timeZoneId )
00108 : Calendar( timeZoneId ),
00109 d( new KCal::CalendarLocal::Private )
00110 {
00111 }
00112
00113 CalendarLocal::~CalendarLocal()
00114 {
00115 close();
00116 delete d;
00117 }
00118
00119 bool CalendarLocal::load( const QString &fileName, CalFormat *format )
00120 {
00121 d->mFileName = fileName;
00122 FileStorage storage( this, fileName, format );
00123 return storage.load();
00124 }
00125
00126 bool CalendarLocal::reload()
00127 {
00128 const QString filename = d->mFileName;
00129 save();
00130 close();
00131 d->mFileName = filename;
00132 FileStorage storage( this, d->mFileName );
00133 return storage.load();
00134 }
00135
00136 bool CalendarLocal::save()
00137 {
00138 if ( d->mFileName.isEmpty() ) {
00139 return false;
00140 }
00141
00142 if ( isModified() ) {
00143 FileStorage storage( this, d->mFileName, d->mFormat );
00144 return storage.save();
00145 } else {
00146 return true;
00147 }
00148 }
00149
00150 bool CalendarLocal::save( const QString &fileName, CalFormat *format )
00151 {
00152
00153
00154 if ( d->mFileName != fileName || isModified() ) {
00155 FileStorage storage( this, fileName, format );
00156 return storage.save();
00157 } else {
00158 return true;
00159 }
00160 }
00161
00162 void CalendarLocal::close()
00163 {
00164 setObserversEnabled( false );
00165 d->mFileName.clear();
00166
00167 deleteAllEvents();
00168 deleteAllTodos();
00169 deleteAllJournals();
00170
00171 d->mDeletedIncidences.clearAll();
00172 setModified( false );
00173
00174 setObserversEnabled( true );
00175 }
00176
00177 bool CalendarLocal::addEvent( Event *event )
00178 {
00179 d->insertEvent( event );
00180
00181 event->registerObserver( this );
00182
00183 setModified( true );
00184
00185 notifyIncidenceAdded( event );
00186
00187 return true;
00188 }
00189
00190 bool CalendarLocal::deleteEvent( Event *event )
00191 {
00192 const QString uid = event->uid();
00193 if ( d->mEvents.remove( uid ) ) {
00194 setModified( true );
00195 notifyIncidenceDeleted( event );
00196 d->mDeletedIncidences.append( event );
00197 if ( !event->recurs() ) {
00198 removeIncidenceFromMultiHashByUID<Event *>(
00199 d->mEventsForDate, event->dtStart().date().toString(), event->uid() );
00200 }
00201 return true;
00202 } else {
00203 kWarning() << "CalendarLocal::deleteEvent(): Event not found.";
00204 return false;
00205 }
00206 }
00207
00208 void CalendarLocal::deleteAllEvents()
00209 {
00210 QHashIterator<QString, Event *>i( d->mEvents );
00211 while ( i.hasNext() ) {
00212 i.next();
00213 notifyIncidenceDeleted( i.value() );
00214
00215
00216 i.value()->startUpdates();
00217 }
00218 qDeleteAll( d->mEvents );
00219 d->mEvents.clear();
00220 d->mEventsForDate.clear();
00221 }
00222
00223 Event *CalendarLocal::event( const QString &uid )
00224 {
00225 return d->mEvents.value( uid );
00226 }
00227
00228 bool CalendarLocal::addTodo( Todo *todo )
00229 {
00230 d->insertTodo( todo );
00231
00232 todo->registerObserver( this );
00233
00234
00235 setupRelations( todo );
00236
00237 setModified( true );
00238
00239 notifyIncidenceAdded( todo );
00240
00241 return true;
00242 }
00243
00244
00245 void CalendarLocal::Private::insertTodo( Todo *todo )
00246 {
00247 QString uid = todo->uid();
00248 if ( !mTodos.contains( uid ) ) {
00249 mTodos.insert( uid, todo );
00250 if ( todo->hasDueDate() ) {
00251 mTodosForDate.insert( todo->dtDue().date().toString(), todo );
00252 }
00253
00254 } else {
00255 #ifndef NDEBUG
00256
00257
00258 Q_ASSERT( mTodos.value( uid ) == todo );
00259 #endif
00260 }
00261 }
00262
00263
00264 bool CalendarLocal::deleteTodo( Todo *todo )
00265 {
00266
00267 removeRelations( todo );
00268
00269 if ( d->mTodos.remove( todo->uid() ) ) {
00270 setModified( true );
00271 notifyIncidenceDeleted( todo );
00272 d->mDeletedIncidences.append( todo );
00273 if ( todo->hasDueDate() ) {
00274 removeIncidenceFromMultiHashByUID<Todo *>(
00275 d->mTodosForDate, todo->dtDue().date().toString(), todo->uid() );
00276 }
00277 return true;
00278 } else {
00279 kWarning() << "CalendarLocal::deleteTodo(): Todo not found.";
00280 return false;
00281 }
00282 }
00283
00284 void CalendarLocal::deleteAllTodos()
00285 {
00286 QHashIterator<QString, Todo *>i( d->mTodos );
00287 while ( i.hasNext() ) {
00288 i.next();
00289 notifyIncidenceDeleted( i.value() );
00290
00291
00292 i.value()->startUpdates();
00293 }
00294 qDeleteAll( d->mTodos );
00295 d->mTodos.clear();
00296 d->mTodosForDate.clear();
00297 }
00298
00299 Todo *CalendarLocal::todo( const QString &uid )
00300 {
00301 return d->mTodos.value( uid );
00302 }
00303
00304 Todo::List CalendarLocal::rawTodos( TodoSortField sortField,
00305 SortDirection sortDirection )
00306 {
00307 Todo::List todoList;
00308 QHashIterator<QString, Todo *>i( d->mTodos );
00309 while ( i.hasNext() ) {
00310 i.next();
00311 todoList.append( i.value() );
00312 }
00313 return sortTodos( &todoList, sortField, sortDirection );
00314 }
00315
00316 Todo::List CalendarLocal::rawTodosForDate( const QDate &date )
00317 {
00318 Todo::List todoList;
00319 Todo *t;
00320
00321 QString dateStr = date.toString();
00322 QMultiHash<QString, Todo *>::iterator it = d->mTodosForDate.find( dateStr );
00323 while ( it != d->mTodosForDate.end() && it.key() == dateStr ) {
00324 t = it.value();
00325 todoList.append( t );
00326 ++it;
00327 }
00328 return todoList;
00329 }
00330
00331 Alarm::List CalendarLocal::alarmsTo( const KDateTime &to )
00332 {
00333 return alarms( KDateTime( QDate( 1900, 1, 1 ) ), to );
00334 }
00335
00336 Alarm::List CalendarLocal::alarms( const KDateTime &from, const KDateTime &to )
00337 {
00338 Alarm::List alarmList;
00339 QHashIterator<QString, Event *>ie( d->mEvents );
00340 Event *e;
00341 while ( ie.hasNext() ) {
00342 ie.next();
00343 e = ie.value();
00344 if ( e->recurs() ) {
00345 appendRecurringAlarms( alarmList, e, from, to );
00346 } else {
00347 appendAlarms( alarmList, e, from, to );
00348 }
00349 }
00350
00351 QHashIterator<QString, Todo *>it( d->mTodos );
00352 Todo *t;
00353 while ( it.hasNext() ) {
00354 it.next();
00355 t = it.value();
00356 if (! t->isCompleted() ) {
00357 appendAlarms( alarmList, t, from, to );
00358 }
00359 }
00360
00361 return alarmList;
00362 }
00363
00364
00365 void CalendarLocal::Private::insertEvent( Event *event )
00366 {
00367 QString uid = event->uid();
00368 if ( !mEvents.contains( uid ) ) {
00369 mEvents.insert( uid, event );
00370 if ( !event->recurs() && !event->isMultiDay() ) {
00371 mEventsForDate.insert( event->dtStart().date().toString(), event );
00372 }
00373 } else {
00374 #ifdef NDEBUG
00375
00376
00377 Q_ASSERT( mEvents.value( uid ) == event );
00378 #endif
00379 }
00380 }
00381
00382
00383 void CalendarLocal::incidenceUpdated( IncidenceBase *incidence )
00384 {
00385 KDateTime nowUTC = KDateTime::currentUtcDateTime();
00386 incidence->setLastModified( nowUTC );
00387
00388
00389
00390
00391 if ( incidence->type() == "Event" ) {
00392 Event *event = static_cast<Event*>( incidence );
00393 removeIncidenceFromMultiHashByUID<Event *>(
00394 d->mEventsForDate, event->dtStart().date().toString(), event->uid() );
00395 if ( !event->recurs() && !event->isMultiDay() ) {
00396 d->mEventsForDate.insert( event->dtStart().date().toString(), event );
00397 }
00398 } else if ( incidence->type() == "Todo" ) {
00399 Todo *todo = static_cast<Todo*>( incidence );
00400 removeIncidenceFromMultiHashByUID<Todo *>(
00401 d->mTodosForDate, todo->dtDue().date().toString(), todo->uid() );
00402 if ( todo->hasDueDate() ) {
00403 d->mTodosForDate.insert( todo->dtDue().date().toString(), todo );
00404 }
00405 } else if ( incidence->type() == "Journal" ) {
00406 Journal *journal = static_cast<Journal*>( incidence );
00407 removeIncidenceFromMultiHashByUID<Journal *>(
00408 d->mJournalsForDate, journal->dtStart().date().toString(), journal->uid() );
00409 d->mJournalsForDate.insert( journal->dtStart().date().toString(), journal );
00410 } else {
00411 Q_ASSERT( false );
00412 }
00413
00414
00415 notifyIncidenceChanged( static_cast<Incidence *>( incidence ) );
00416
00417 setModified( true );
00418 }
00419
00420 Event::List CalendarLocal::rawEventsForDate( const QDate &date,
00421 const KDateTime::Spec ×pec,
00422 EventSortField sortField,
00423 SortDirection sortDirection )
00424 {
00425 Event::List eventList;
00426 Event *ev;
00427
00428
00429 QString dateStr = date.toString();
00430 QMultiHash<QString, Event *>::iterator it = d->mEventsForDate.find( dateStr );
00431
00432 KDateTime::Spec ts = timespec.isValid() ? timespec : timeSpec();
00433 KDateTime kdt( date, ts );
00434 while ( it != d->mEventsForDate.end() && it.key() == dateStr ) {
00435 ev = it.value();
00436 KDateTime end( ev->dtEnd().toTimeSpec( ev->dtStart() ) );
00437 if ( ev->allDay() ) {
00438 end.setDateOnly( true );
00439 } else {
00440 end = end.addSecs( -1 );
00441 }
00442 if ( end >= kdt ) {
00443 eventList.append( ev );
00444 }
00445 ++it;
00446 }
00447
00448
00449 QHashIterator<QString, Event *>i( d->mEvents );
00450 while ( i.hasNext() ) {
00451 i.next();
00452 ev = i.value();
00453 if ( ev->recurs() ) {
00454 if ( ev->isMultiDay() ) {
00455 int extraDays = ev->dtStart().date().daysTo( ev->dtEnd().date() );
00456 for ( int i = 0; i <= extraDays; i++ ) {
00457 if ( ev->recursOn( date.addDays( -i ), ts ) ) {
00458 eventList.append( ev );
00459 break;
00460 }
00461 }
00462 } else {
00463 if ( ev->recursOn( date, ts ) ) {
00464 eventList.append( ev );
00465 }
00466 }
00467 } else {
00468 if ( ev->isMultiDay() ) {
00469 if ( ev->dtStart().date() <= date && ev->dtEnd().date() >= date ) {
00470 eventList.append( ev );
00471 }
00472 }
00473 }
00474 }
00475
00476 return sortEvents( &eventList, sortField, sortDirection );
00477 }
00478
00479 Event::List CalendarLocal::rawEvents( const QDate &start, const QDate &end,
00480 const KDateTime::Spec ×pec, bool inclusive )
00481 {
00482 Event::List eventList;
00483 KDateTime::Spec ts = timespec.isValid() ? timespec : timeSpec();
00484 KDateTime st( start, ts );
00485 KDateTime nd( end, ts );
00486 KDateTime yesterStart = st.addDays( -1 );
00487
00488
00489 QHashIterator<QString, Event *>i( d->mEvents );
00490 Event *event;
00491 while ( i.hasNext() ) {
00492 i.next();
00493 event = i.value();
00494 KDateTime rStart = event->dtStart();
00495 if ( nd < rStart ) {
00496 continue;
00497 }
00498 if ( inclusive && rStart < st ) {
00499 continue;
00500 }
00501
00502 if ( !event->recurs() ) {
00503 KDateTime rEnd = event->dtEnd();
00504 if ( rEnd < st ) {
00505 continue;
00506 }
00507 if ( inclusive && nd < rEnd ) {
00508 continue;
00509 }
00510 } else {
00511 switch( event->recurrence()->duration() ) {
00512 case -1:
00513 if ( inclusive ) {
00514 continue;
00515 }
00516 break;
00517 case 0:
00518 default:
00519 KDateTime rEnd( event->recurrence()->endDate(), ts );
00520 if ( !rEnd.isValid() ) {
00521 continue;
00522 }
00523 if ( rEnd < st ) {
00524 continue;
00525 }
00526 if ( inclusive && nd < rEnd ) {
00527 continue;
00528 }
00529 break;
00530 }
00531 }
00532
00533 eventList.append( event );
00534 }
00535
00536 return eventList;
00537 }
00538
00539 Event::List CalendarLocal::rawEventsForDate( const KDateTime &kdt )
00540 {
00541 return rawEventsForDate( kdt.date(), kdt.timeSpec() );
00542 }
00543
00544 Event::List CalendarLocal::rawEvents( EventSortField sortField,
00545 SortDirection sortDirection )
00546 {
00547 Event::List eventList;
00548 QHashIterator<QString, Event *>i( d->mEvents );
00549 while ( i.hasNext() ) {
00550 i.next();
00551 eventList.append( i.value() );
00552 }
00553 return sortEvents( &eventList, sortField, sortDirection );
00554 }
00555
00556 bool CalendarLocal::addJournal( Journal *journal )
00557 {
00558 d->insertJournal( journal );
00559
00560 journal->registerObserver( this );
00561
00562 setModified( true );
00563
00564 notifyIncidenceAdded( journal );
00565
00566 return true;
00567 }
00568
00569
00570 void CalendarLocal::Private::insertJournal( Journal *journal )
00571 {
00572 QString uid = journal->uid();
00573 if ( !mJournals.contains( uid ) ) {
00574 mJournals.insert( uid, journal );
00575 mJournalsForDate.insert( journal->dtStart().date().toString(), journal );
00576 } else {
00577 #ifndef NDEBUG
00578
00579
00580 Q_ASSERT( mJournals.value( uid ) == journal );
00581 #endif
00582 }
00583 }
00584
00585
00586 bool CalendarLocal::deleteJournal( Journal *journal )
00587 {
00588 if ( d->mJournals.remove( journal->uid() ) ) {
00589 setModified( true );
00590 notifyIncidenceDeleted( journal );
00591 d->mDeletedIncidences.append( journal );
00592 removeIncidenceFromMultiHashByUID<Journal *>(
00593 d->mJournalsForDate, journal->dtStart().date().toString(), journal->uid() );
00594 return true;
00595 } else {
00596 kWarning() << "CalendarLocal::deleteJournal(): Journal not found.";
00597 return false;
00598 }
00599 }
00600
00601 void CalendarLocal::deleteAllJournals()
00602 {
00603 QHashIterator<QString, Journal *>i( d->mJournals );
00604 while ( i.hasNext() ) {
00605 i.next();
00606 notifyIncidenceDeleted( i.value() );
00607
00608
00609 i.value()->startUpdates();
00610 }
00611 qDeleteAll( d->mJournals );
00612 d->mJournals.clear();
00613 d->mJournalsForDate.clear();
00614 }
00615
00616 Journal *CalendarLocal::journal( const QString &uid )
00617 {
00618 return d->mJournals.value( uid );
00619 }
00620
00621 Journal::List CalendarLocal::rawJournals( JournalSortField sortField,
00622 SortDirection sortDirection )
00623 {
00624 Journal::List journalList;
00625 QHashIterator<QString, Journal *>i( d->mJournals );
00626 while ( i.hasNext() ) {
00627 i.next();
00628 journalList.append( i.value() );
00629 }
00630 return sortJournals( &journalList, sortField, sortDirection );
00631 }
00632
00633 Journal::List CalendarLocal::rawJournalsForDate( const QDate &date )
00634 {
00635 Journal::List journalList;
00636 Journal *j;
00637
00638 QString dateStr = date.toString();
00639 QMultiHash<QString, Journal *>::iterator it = d->mJournalsForDate.find( dateStr );
00640
00641 while ( it != d->mJournalsForDate.end() && it.key() == dateStr ) {
00642 j = it.value();
00643 journalList.append( j );
00644 ++it;
00645 }
00646 return journalList;
00647 }