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