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