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() << "CalendarLocal::deleteEvent(): 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() << "CalendarLocal::deleteTodo(): 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 appendAlarms( alarmList, t, from, to );
00351 }
00352 }
00353
00354 return alarmList;
00355 }
00356
00357
00358 void CalendarLocal::Private::insertEvent( Event *event )
00359 {
00360 QString uid = event->uid();
00361 if ( !mEvents.contains( uid ) ) {
00362 mEvents.insert( uid, event );
00363 if ( !event->recurs() && !event->isMultiDay() ) {
00364 mEventsForDate.insert( event->dtStart().date().toString(), event );
00365 }
00366 } else {
00367 #ifdef NDEBUG
00368
00369
00370 Q_ASSERT( mEvents.value( uid ) == event );
00371 #endif
00372 }
00373 }
00374
00375
00376 void CalendarLocal::incidenceUpdated( IncidenceBase *incidence )
00377 {
00378 KDateTime nowUTC = KDateTime::currentUtcDateTime();
00379 incidence->setLastModified( nowUTC );
00380
00381
00382
00383
00384 if ( incidence->type() == "Event" ) {
00385 Event *event = static_cast<Event*>( incidence );
00386 removeIncidenceFromMultiHashByUID<Event *>(
00387 d->mEventsForDate, event->dtStart().date().toString(), event->uid() );
00388 if ( !event->recurs() && !event->isMultiDay() ) {
00389 d->mEventsForDate.insert( event->dtStart().date().toString(), event );
00390 }
00391 } else if ( incidence->type() == "Todo" ) {
00392 Todo *todo = static_cast<Todo*>( incidence );
00393 removeIncidenceFromMultiHashByUID<Todo *>(
00394 d->mTodosForDate, todo->dtDue().date().toString(), todo->uid() );
00395 if ( todo->hasDueDate() ) {
00396 d->mTodosForDate.insert( todo->dtDue().date().toString(), todo );
00397 }
00398 } else if ( incidence->type() == "Journal" ) {
00399 Journal *journal = static_cast<Journal*>( incidence );
00400 removeIncidenceFromMultiHashByUID<Journal *>(
00401 d->mJournalsForDate, journal->dtStart().date().toString(), journal->uid() );
00402 d->mJournalsForDate.insert( journal->dtStart().date().toString(), journal );
00403 } else {
00404 Q_ASSERT( false );
00405 }
00406
00407
00408 notifyIncidenceChanged( static_cast<Incidence *>( incidence ) );
00409
00410 setModified( true );
00411 }
00412
00413 Event::List CalendarLocal::rawEventsForDate( const QDate &date,
00414 const KDateTime::Spec ×pec,
00415 EventSortField sortField,
00416 SortDirection sortDirection )
00417 {
00418 Event::List eventList;
00419 Event *ev;
00420
00421
00422 QString dateStr = date.toString();
00423 QMultiHash<QString, Event *>::const_iterator it = d->mEventsForDate.constFind( dateStr );
00424
00425 KDateTime::Spec ts = timespec.isValid() ? timespec : timeSpec();
00426 KDateTime kdt( date, ts );
00427 while ( it != d->mEventsForDate.constEnd() && it.key() == dateStr ) {
00428 ev = it.value();
00429 KDateTime end( ev->dtEnd().toTimeSpec( ev->dtStart() ) );
00430 if ( ev->allDay() ) {
00431 end.setDateOnly( true );
00432 } else {
00433 end = end.addSecs( -1 );
00434 }
00435 if ( end >= kdt ) {
00436 eventList.append( ev );
00437 }
00438 ++it;
00439 }
00440
00441
00442 QHashIterator<QString, Event *>i( d->mEvents );
00443 while ( i.hasNext() ) {
00444 i.next();
00445 ev = i.value();
00446 if ( ev->recurs() ) {
00447 if ( ev->isMultiDay() ) {
00448 int extraDays = ev->dtStart().date().daysTo( ev->dtEnd().date() );
00449 for ( int i = 0; i <= extraDays; ++i ) {
00450 if ( ev->recursOn( date.addDays( -i ), ts ) ) {
00451 eventList.append( ev );
00452 break;
00453 }
00454 }
00455 } else {
00456 if ( ev->recursOn( date, ts ) ) {
00457 eventList.append( ev );
00458 }
00459 }
00460 } else {
00461 if ( ev->isMultiDay() ) {
00462 if ( ev->dtStart().date() <= date && ev->dtEnd().date() >= date ) {
00463 eventList.append( ev );
00464 }
00465 }
00466 }
00467 }
00468
00469 return sortEvents( &eventList, sortField, sortDirection );
00470 }
00471
00472 Event::List CalendarLocal::rawEvents( const QDate &start, const QDate &end,
00473 const KDateTime::Spec ×pec, bool inclusive )
00474 {
00475 Event::List eventList;
00476 KDateTime::Spec ts = timespec.isValid() ? timespec : timeSpec();
00477 KDateTime st( start, ts );
00478 KDateTime nd( end, ts );
00479 KDateTime yesterStart = st.addDays( -1 );
00480
00481
00482 QHashIterator<QString, Event *>i( d->mEvents );
00483 Event *event;
00484 while ( i.hasNext() ) {
00485 i.next();
00486 event = i.value();
00487 KDateTime rStart = event->dtStart();
00488 if ( nd < rStart ) {
00489 continue;
00490 }
00491 if ( inclusive && rStart < st ) {
00492 continue;
00493 }
00494
00495 if ( !event->recurs() ) {
00496 KDateTime rEnd = event->dtEnd();
00497 if ( rEnd < st ) {
00498 continue;
00499 }
00500 if ( inclusive && nd < rEnd ) {
00501 continue;
00502 }
00503 } else {
00504 switch( event->recurrence()->duration() ) {
00505 case -1:
00506 if ( inclusive ) {
00507 continue;
00508 }
00509 break;
00510 case 0:
00511 default:
00512 KDateTime rEnd( event->recurrence()->endDate(), ts );
00513 if ( !rEnd.isValid() ) {
00514 continue;
00515 }
00516 if ( rEnd < st ) {
00517 continue;
00518 }
00519 if ( inclusive && nd < rEnd ) {
00520 continue;
00521 }
00522 break;
00523 }
00524 }
00525
00526 eventList.append( event );
00527 }
00528
00529 return eventList;
00530 }
00531
00532 Event::List CalendarLocal::rawEventsForDate( const KDateTime &kdt )
00533 {
00534 return rawEventsForDate( kdt.date(), kdt.timeSpec() );
00535 }
00536
00537 Event::List CalendarLocal::rawEvents( EventSortField sortField,
00538 SortDirection sortDirection )
00539 {
00540 Event::List eventList;
00541 QHashIterator<QString, Event *>i( d->mEvents );
00542 while ( i.hasNext() ) {
00543 i.next();
00544 eventList.append( i.value() );
00545 }
00546 return sortEvents( &eventList, sortField, sortDirection );
00547 }
00548
00549 bool CalendarLocal::addJournal( Journal *journal )
00550 {
00551 d->insertJournal( journal );
00552
00553 journal->registerObserver( this );
00554
00555 setModified( true );
00556
00557 notifyIncidenceAdded( journal );
00558
00559 return true;
00560 }
00561
00562
00563 void CalendarLocal::Private::insertJournal( Journal *journal )
00564 {
00565 QString uid = journal->uid();
00566 if ( !mJournals.contains( uid ) ) {
00567 mJournals.insert( uid, journal );
00568 mJournalsForDate.insert( journal->dtStart().date().toString(), journal );
00569 } else {
00570 #ifndef NDEBUG
00571
00572
00573 Q_ASSERT( mJournals.value( uid ) == journal );
00574 #endif
00575 }
00576 }
00577
00578
00579 bool CalendarLocal::deleteJournal( Journal *journal )
00580 {
00581 if ( d->mJournals.remove( journal->uid() ) ) {
00582 setModified( true );
00583 notifyIncidenceDeleted( journal );
00584 removeIncidenceFromMultiHashByUID<Journal *>(
00585 d->mJournalsForDate, journal->dtStart().date().toString(), journal->uid() );
00586 return true;
00587 } else {
00588 kWarning() << "CalendarLocal::deleteJournal(): Journal not found.";
00589 return false;
00590 }
00591 }
00592
00593 void CalendarLocal::deleteAllJournals()
00594 {
00595 QHashIterator<QString, Journal *>i( d->mJournals );
00596 while ( i.hasNext() ) {
00597 i.next();
00598 notifyIncidenceDeleted( i.value() );
00599
00600
00601 i.value()->startUpdates();
00602 }
00603 qDeleteAll( d->mJournals );
00604 d->mJournals.clear();
00605 d->mJournalsForDate.clear();
00606 }
00607
00608 Journal *CalendarLocal::journal( const QString &uid )
00609 {
00610 return d->mJournals.value( uid );
00611 }
00612
00613 Journal::List CalendarLocal::rawJournals( JournalSortField sortField,
00614 SortDirection sortDirection )
00615 {
00616 Journal::List journalList;
00617 QHashIterator<QString, Journal *>i( d->mJournals );
00618 while ( i.hasNext() ) {
00619 i.next();
00620 journalList.append( i.value() );
00621 }
00622 return sortJournals( &journalList, sortField, sortDirection );
00623 }
00624
00625 Journal::List CalendarLocal::rawJournalsForDate( const QDate &date )
00626 {
00627 Journal::List journalList;
00628 Journal *j;
00629
00630 QString dateStr = date.toString();
00631 QMultiHash<QString, Journal *>::const_iterator it = d->mJournalsForDate.constFind( dateStr );
00632
00633 while ( it != d->mJournalsForDate.constEnd() && it.key() == dateStr ) {
00634 j = it.value();
00635 journalList.append( j );
00636 ++it;
00637 }
00638 return journalList;
00639 }