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 qDeleteAll( d->mEvents );
00216 d->mEvents.clear();
00217 d->mEventsForDate.clear();
00218 }
00219
00220 Event *CalendarLocal::event( const QString &uid )
00221 {
00222 return d->mEvents.value( uid );
00223 }
00224
00225 bool CalendarLocal::addTodo( Todo *todo )
00226 {
00227 d->insertTodo( todo );
00228
00229 todo->registerObserver( this );
00230
00231
00232 setupRelations( todo );
00233
00234 setModified( true );
00235
00236 notifyIncidenceAdded( todo );
00237
00238 return true;
00239 }
00240
00241
00242 void CalendarLocal::Private::insertTodo( Todo *todo )
00243 {
00244 QString uid = todo->uid();
00245 if ( !mTodos.contains( uid ) ) {
00246 mTodos.insert( uid, todo );
00247 if ( todo->hasDueDate() ) {
00248 mTodosForDate.insert( todo->dtDue().date().toString(), todo );
00249 }
00250
00251 } else {
00252 #ifndef NDEBUG
00253
00254
00255 Q_ASSERT( mTodos.value( uid ) == todo );
00256 #endif
00257 }
00258 }
00259
00260
00261 bool CalendarLocal::deleteTodo( Todo *todo )
00262 {
00263
00264 removeRelations( todo );
00265
00266 if ( d->mTodos.remove( todo->uid() ) ) {
00267 setModified( true );
00268 notifyIncidenceDeleted( todo );
00269 d->mDeletedIncidences.append( todo );
00270 if ( todo->hasDueDate() ) {
00271 removeIncidenceFromMultiHashByUID<Todo *>(
00272 d->mTodosForDate, todo->dtDue().date().toString(), todo->uid() );
00273 }
00274 return true;
00275 } else {
00276 kWarning() << "CalendarLocal::deleteTodo(): Todo not found.";
00277 return false;
00278 }
00279 }
00280
00281 void CalendarLocal::deleteAllTodos()
00282 {
00283 QHashIterator<QString, Todo *>i( d->mTodos );
00284 while ( i.hasNext() ) {
00285 i.next();
00286 notifyIncidenceDeleted( i.value() );
00287 }
00288 qDeleteAll( d->mTodos );
00289 d->mTodos.clear();
00290 d->mTodosForDate.clear();
00291 }
00292
00293 Todo *CalendarLocal::todo( const QString &uid )
00294 {
00295 return d->mTodos.value( uid );
00296 }
00297
00298 Todo::List CalendarLocal::rawTodos( TodoSortField sortField,
00299 SortDirection sortDirection )
00300 {
00301 Todo::List todoList;
00302 QHashIterator<QString, Todo *>i( d->mTodos );
00303 while ( i.hasNext() ) {
00304 i.next();
00305 todoList.append( i.value() );
00306 }
00307 return sortTodos( &todoList, sortField, sortDirection );
00308 }
00309
00310 Todo::List CalendarLocal::rawTodosForDate( const QDate &date )
00311 {
00312 Todo::List todoList;
00313 Todo *t;
00314
00315 QString dateStr = date.toString();
00316 QMultiHash<QString, Todo *>::iterator it = d->mTodosForDate.find( dateStr );
00317 while ( it != d->mTodosForDate.end() && it.key() == dateStr ) {
00318 t = it.value();
00319 todoList.append( t );
00320 ++it;
00321 }
00322 return todoList;
00323 }
00324
00325 Alarm::List CalendarLocal::alarmsTo( const KDateTime &to )
00326 {
00327 return alarms( KDateTime( QDate( 1900, 1, 1 ) ), to );
00328 }
00329
00330 Alarm::List CalendarLocal::alarms( const KDateTime &from, const KDateTime &to )
00331 {
00332 Alarm::List alarmList;
00333 QHashIterator<QString, Event *>ie( d->mEvents );
00334 Event *e;
00335 while ( ie.hasNext() ) {
00336 ie.next();
00337 e = ie.value();
00338 if ( e->recurs() ) {
00339 appendRecurringAlarms( alarmList, e, from, to );
00340 } else {
00341 appendAlarms( alarmList, e, from, to );
00342 }
00343 }
00344
00345 QHashIterator<QString, Todo *>it( d->mTodos );
00346 Todo *t;
00347 while ( it.hasNext() ) {
00348 it.next();
00349 t = it.value();
00350 if (! t->isCompleted() ) {
00351 appendAlarms( alarmList, t, from, to );
00352 }
00353 }
00354
00355 return alarmList;
00356 }
00357
00358
00359 void CalendarLocal::Private::insertEvent( Event *event )
00360 {
00361 QString uid = event->uid();
00362 if ( !mEvents.contains( uid ) ) {
00363 mEvents.insert( uid, event );
00364 if ( !event->recurs() && !event->isMultiDay() ) {
00365 mEventsForDate.insert( event->dtStart().date().toString(), event );
00366 }
00367 } else {
00368 #ifdef NDEBUG
00369
00370
00371 Q_ASSERT( mEvents.value( uid ) == event );
00372 #endif
00373 }
00374 }
00375
00376
00377 void CalendarLocal::incidenceUpdated( IncidenceBase *incidence )
00378 {
00379 KDateTime nowUTC = KDateTime::currentUtcDateTime();
00380 incidence->setLastModified( nowUTC );
00381
00382
00383
00384
00385
00386 notifyIncidenceChanged( static_cast<Incidence *>( incidence ) );
00387
00388 setModified( true );
00389 }
00390
00391 Event::List CalendarLocal::rawEventsForDate( const QDate &date,
00392 const KDateTime::Spec ×pec,
00393 EventSortField sortField,
00394 SortDirection sortDirection )
00395 {
00396 Event::List eventList;
00397 Event *ev;
00398
00399
00400 QString dateStr = date.toString();
00401 QMultiHash<QString, Event *>::iterator it = d->mEventsForDate.find( dateStr );
00402
00403 KDateTime::Spec ts = timespec.isValid() ? timespec : timeSpec();
00404 KDateTime kdt( date, ts );
00405 while ( it != d->mEventsForDate.end() && it.key() == dateStr ) {
00406 ev = it.value();
00407 KDateTime end( ev->dtEnd().toTimeSpec( ev->dtStart() ) );
00408 if ( ev->allDay() ) {
00409 end.setDateOnly( true );
00410 } else {
00411 end = end.addSecs( -1 );
00412 }
00413 if ( end >= kdt ) {
00414 eventList.append( ev );
00415 }
00416 ++it;
00417 }
00418
00419
00420 QHashIterator<QString, Event *>i( d->mEvents );
00421 while ( i.hasNext() ) {
00422 i.next();
00423 ev = i.value();
00424 if ( ev->recurs() ) {
00425 if ( ev->isMultiDay() ) {
00426 int extraDays = ev->dtStart().date().daysTo( ev->dtEnd().date() );
00427 for ( int i = 0; i <= extraDays; i++ ) {
00428 if ( ev->recursOn( date.addDays( -i ), ts ) ) {
00429 eventList.append( ev );
00430 break;
00431 }
00432 }
00433 } else {
00434 if ( ev->recursOn( date, ts ) ) {
00435 eventList.append( ev );
00436 }
00437 }
00438 } else {
00439 if ( ev->isMultiDay() ) {
00440 if ( ev->dtStart().date() <= date && ev->dtEnd().date() >= date ) {
00441 eventList.append( ev );
00442 }
00443 }
00444 }
00445 }
00446
00447 return sortEvents( &eventList, sortField, sortDirection );
00448 }
00449
00450 Event::List CalendarLocal::rawEvents( const QDate &start, const QDate &end,
00451 const KDateTime::Spec ×pec, bool inclusive )
00452 {
00453 Event::List eventList;
00454 KDateTime::Spec ts = timespec.isValid() ? timespec : timeSpec();
00455 KDateTime st( start, ts );
00456 KDateTime nd( end, ts );
00457 KDateTime yesterStart = st.addDays( -1 );
00458
00459
00460 QHashIterator<QString, Event *>i( d->mEvents );
00461 Event *event;
00462 while ( i.hasNext() ) {
00463 i.next();
00464 event = i.value();
00465 KDateTime rStart = event->dtStart();
00466 if ( nd < rStart ) {
00467 continue;
00468 }
00469 if ( inclusive && rStart < st ) {
00470 continue;
00471 }
00472
00473 if ( !event->recurs() ) {
00474 KDateTime rEnd = event->dtEnd();
00475 if ( rEnd < st ) {
00476 continue;
00477 }
00478 if ( inclusive && nd < rEnd ) {
00479 continue;
00480 }
00481 } else {
00482 switch( event->recurrence()->duration() ) {
00483 case -1:
00484 if ( inclusive ) {
00485 continue;
00486 }
00487 break;
00488 case 0:
00489 default:
00490 KDateTime rEnd( event->recurrence()->endDate(), ts );
00491 if ( !rEnd.isValid() ) {
00492 continue;
00493 }
00494 if ( rEnd < st ) {
00495 continue;
00496 }
00497 if ( inclusive && nd < rEnd ) {
00498 continue;
00499 }
00500 break;
00501 }
00502 }
00503
00504 eventList.append( event );
00505 }
00506
00507 return eventList;
00508 }
00509
00510 Event::List CalendarLocal::rawEventsForDate( const KDateTime &kdt )
00511 {
00512 return rawEventsForDate( kdt.date(), kdt.timeSpec() );
00513 }
00514
00515 Event::List CalendarLocal::rawEvents( EventSortField sortField,
00516 SortDirection sortDirection )
00517 {
00518 Event::List eventList;
00519 QHashIterator<QString, Event *>i( d->mEvents );
00520 while ( i.hasNext() ) {
00521 i.next();
00522 eventList.append( i.value() );
00523 }
00524 return sortEvents( &eventList, sortField, sortDirection );
00525 }
00526
00527 bool CalendarLocal::addJournal( Journal *journal )
00528 {
00529 d->insertJournal( journal );
00530
00531 journal->registerObserver( this );
00532
00533 setModified( true );
00534
00535 notifyIncidenceAdded( journal );
00536
00537 return true;
00538 }
00539
00540
00541 void CalendarLocal::Private::insertJournal( Journal *journal )
00542 {
00543 QString uid = journal->uid();
00544 if ( !mJournals.contains( uid ) ) {
00545 mJournals.insert( uid, journal );
00546 mJournalsForDate.insert( journal->dtStart().date().toString(), journal );
00547 } else {
00548 #ifndef NDEBUG
00549
00550
00551 Q_ASSERT( mJournals.value( uid ) == journal );
00552 #endif
00553 }
00554 }
00555
00556
00557 bool CalendarLocal::deleteJournal( Journal *journal )
00558 {
00559 if ( d->mJournals.remove( journal->uid() ) ) {
00560 setModified( true );
00561 notifyIncidenceDeleted( journal );
00562 d->mDeletedIncidences.append( journal );
00563 removeIncidenceFromMultiHashByUID<Journal *>(
00564 d->mJournalsForDate, journal->dtStart().date().toString(), journal->uid() );
00565 return true;
00566 } else {
00567 kWarning() << "CalendarLocal::deleteJournal(): Journal not found.";
00568 return false;
00569 }
00570 }
00571
00572 void CalendarLocal::deleteAllJournals()
00573 {
00574 QHashIterator<QString, Journal *>i( d->mJournals );
00575 while ( i.hasNext() ) {
00576 i.next();
00577 notifyIncidenceDeleted( i.value() );
00578 }
00579 qDeleteAll( d->mJournals );
00580 d->mJournals.clear();
00581 d->mJournalsForDate.clear();
00582 }
00583
00584 Journal *CalendarLocal::journal( const QString &uid )
00585 {
00586 return d->mJournals.value( uid );
00587 }
00588
00589 Journal::List CalendarLocal::rawJournals( JournalSortField sortField,
00590 SortDirection sortDirection )
00591 {
00592 Journal::List journalList;
00593 QHashIterator<QString, Journal *>i( d->mJournals );
00594 while ( i.hasNext() ) {
00595 i.next();
00596 journalList.append( i.value() );
00597 }
00598 return sortJournals( &journalList, sortField, sortDirection );
00599 }
00600
00601 Journal::List CalendarLocal::rawJournalsForDate( const QDate &date )
00602 {
00603 Journal::List journalList;
00604 Journal *j;
00605
00606 QString dateStr = date.toString();
00607 QMultiHash<QString, Journal *>::iterator it = d->mJournalsForDate.find( dateStr );
00608
00609 while ( it != d->mJournalsForDate.end() && it.key() == dateStr ) {
00610 j = it.value();
00611 journalList.append( j );
00612 ++it;
00613 }
00614 return journalList;
00615 }