00001
00002
00003
00004
00005
00006
00007
00008
00009
00010
00011
00012
00013
00014
00015
00016
00017
00018
00019
00020
00021
00022
00023
00024 #include <qdatetime.h>
00025 #include <qstring.h>
00026 #include <qptrlist.h>
00027
00028 #include <kdebug.h>
00029 #include <klocale.h>
00030 #include <kmessagebox.h>
00031
00032 #include "vcaldrag.h"
00033 #include "vcalformat.h"
00034 #include "icalformat.h"
00035 #include "exceptions.h"
00036 #include "incidence.h"
00037 #include "journal.h"
00038 #include "filestorage.h"
00039
00040 #include "calendarlocal.h"
00041
00042 using namespace KCal;
00043
00044 CalendarLocal::CalendarLocal( const QString &timeZoneId )
00045 : Calendar( timeZoneId ), mEvents( 47 )
00046 {
00047 init();
00048 }
00049
00050 void CalendarLocal::init()
00051 {
00052 mDeletedIncidences.setAutoDelete( true );
00053 mFileName = QString::null;
00054 }
00055
00056
00057 CalendarLocal::~CalendarLocal()
00058 {
00059 close();
00060 }
00061
00062 bool CalendarLocal::load( const QString &fileName, CalFormat *format )
00063 {
00064 mFileName = fileName;
00065 FileStorage storage( this, fileName, format );
00066 return storage.load();
00067 }
00068
00069 bool CalendarLocal::reload( const QString &tz )
00070 {
00071 const QString filename = mFileName;
00072 save();
00073 close();
00074 mFileName = filename;
00075 setTimeZoneId( tz );
00076 FileStorage storage( this, mFileName );
00077 return storage.load();
00078 }
00079
00080 bool CalendarLocal::save( const QString &fileName, CalFormat *format )
00081 {
00082
00083
00084 if ( mFileName != fileName || isModified() ) {
00085 FileStorage storage( this, fileName, format );
00086 return storage.save();
00087 } else {
00088 return true;
00089 }
00090 }
00091
00092 void CalendarLocal::close()
00093 {
00094 setObserversEnabled( false );
00095 mFileName = QString::null;
00096
00097 deleteAllEvents();
00098 deleteAllTodos();
00099 deleteAllJournals();
00100
00101 mDeletedIncidences.clear();
00102 setModified( false );
00103
00104 setObserversEnabled( true );
00105 }
00106
00107
00108 bool CalendarLocal::addEvent( Event *event )
00109 {
00110 insertEvent( event );
00111
00112 event->registerObserver( this );
00113
00114 setModified( true );
00115
00116 notifyIncidenceAdded( event );
00117
00118 return true;
00119 }
00120
00121 bool CalendarLocal::deleteEvent( Event *event )
00122 {
00123
00124
00125 if ( mEvents.remove( event->uid() ) ) {
00126 setModified( true );
00127 notifyIncidenceDeleted( event );
00128 mDeletedIncidences.append( event );
00129 return true;
00130 } else {
00131 kdWarning() << "CalendarLocal::deleteEvent(): Event not found." << endl;
00132 return false;
00133 }
00134 }
00135
00136 void CalendarLocal::deleteAllEvents()
00137 {
00138
00139 QDictIterator<Event> it( mEvents );
00140 while( it.current() ) {
00141 notifyIncidenceDeleted( it.current() );
00142 ++it;
00143 }
00144
00145 mEvents.setAutoDelete( true );
00146 mEvents.clear();
00147 mEvents.setAutoDelete( false );
00148 }
00149
00150 Event *CalendarLocal::event( const QString &uid )
00151 {
00152
00153 return mEvents[ uid ];
00154 }
00155
00156 bool CalendarLocal::addTodo( Todo *todo )
00157 {
00158 mTodoList.append( todo );
00159
00160 todo->registerObserver( this );
00161
00162
00163 setupRelations( todo );
00164
00165 setModified( true );
00166
00167 notifyIncidenceAdded( todo );
00168
00169 return true;
00170 }
00171
00172 bool CalendarLocal::deleteTodo( Todo *todo )
00173 {
00174
00175 removeRelations( todo );
00176
00177 if ( mTodoList.removeRef( todo ) ) {
00178 setModified( true );
00179 notifyIncidenceDeleted( todo );
00180 mDeletedIncidences.append( todo );
00181 return true;
00182 } else {
00183 kdWarning() << "CalendarLocal::deleteTodo(): Todo not found." << endl;
00184 return false;
00185 }
00186 }
00187
00188 void CalendarLocal::deleteAllTodos()
00189 {
00190
00191 Todo::List::ConstIterator it;
00192 for( it = mTodoList.begin(); it != mTodoList.end(); ++it ) {
00193 notifyIncidenceDeleted( *it );
00194 }
00195
00196 mTodoList.setAutoDelete( true );
00197 mTodoList.clearAll();
00198 mTodoList.setAutoDelete( false );
00199 }
00200
00201 Todo::List CalendarLocal::rawTodos( TodoSortField sortField,
00202 SortDirection sortDirection )
00203 {
00204 return sortTodos( &mTodoList, sortField, sortDirection );
00205 }
00206
00207 Todo *CalendarLocal::todo( const QString &uid )
00208 {
00209 Todo::List::ConstIterator it;
00210 for ( it = mTodoList.begin(); it != mTodoList.end(); ++it ) {
00211 if ( (*it)->uid() == uid ) return *it;
00212 }
00213
00214 return 0;
00215 }
00216
00217 Todo::List CalendarLocal::rawTodosForDate( const QDate &date )
00218 {
00219 Todo::List todos;
00220
00221 Todo::List::ConstIterator it;
00222 for ( it = mTodoList.begin(); it != mTodoList.end(); ++it ) {
00223 Todo *todo = *it;
00224 if ( todo->hasDueDate() && todo->dtDue().date() == date ) {
00225 todos.append( todo );
00226 }
00227 }
00228
00229 return todos;
00230 }
00231
00232 Alarm::List CalendarLocal::alarmsTo( const QDateTime &to )
00233 {
00234 return alarms( QDateTime( QDate( 1900, 1, 1 ) ), to );
00235 }
00236
00237 Alarm::List CalendarLocal::alarms( const QDateTime &from, const QDateTime &to )
00238 {
00239
00240
00241
00242 Alarm::List alarms;
00243
00244 EventDictIterator it( mEvents );
00245 for( ; it.current(); ++it ) {
00246 Event *e = *it;
00247 if ( e->doesRecur() ) appendRecurringAlarms( alarms, e, from, to );
00248 else appendAlarms( alarms, e, from, to );
00249 }
00250
00251 Todo::List::ConstIterator it2;
00252 for( it2 = mTodoList.begin(); it2 != mTodoList.end(); ++it2 ) {
00253 if (! (*it2)->isCompleted() ) appendAlarms( alarms, *it2, from, to );
00254 }
00255
00256 return alarms;
00257 }
00258
00259 void CalendarLocal::appendAlarms( Alarm::List &alarms, Incidence *incidence,
00260 const QDateTime &from, const QDateTime &to )
00261 {
00262 QDateTime preTime = from.addSecs(-1);
00263 Alarm::List::ConstIterator it;
00264 for( it = incidence->alarms().begin(); it != incidence->alarms().end();
00265 ++it ) {
00266 Alarm *alarm = *it;
00267 if ( alarm->enabled() ) {
00268 QDateTime dt = alarm->nextRepetition( preTime );
00269 if ( dt.isValid() && dt <= to ) {
00270 kdDebug(5800) << "CalendarLocal::appendAlarms() '"
00271 << incidence->summary() << "': "
00272 << dt.toString() << endl;
00273 alarms.append( alarm );
00274 }
00275 }
00276 }
00277 }
00278
00279 void CalendarLocal::appendRecurringAlarms( Alarm::List &alarms,
00280 Incidence *incidence,
00281 const QDateTime &from,
00282 const QDateTime &to )
00283 {
00284 QDateTime qdt;
00285 int endOffset = 0;
00286 bool endOffsetValid = false;
00287 int period = from.secsTo(to);
00288 Alarm::List::ConstIterator it;
00289 for( it = incidence->alarms().begin(); it != incidence->alarms().end();
00290 ++it ) {
00291 Alarm *alarm = *it;
00292 if ( alarm->enabled() ) {
00293 if ( alarm->hasTime() ) {
00294
00295 qdt = alarm->nextRepetition( from.addSecs(-1) );
00296 if ( !qdt.isValid() || qdt > to )
00297 continue;
00298 } else {
00299
00300
00301
00302 int offset = 0;
00303 if ( alarm->hasStartOffset() ) {
00304 offset = alarm->startOffset().asSeconds();
00305 } else if ( alarm->hasEndOffset() ) {
00306 if ( !endOffsetValid ) {
00307 endOffset = incidence->dtStart().secsTo( incidence->dtEnd() );
00308 endOffsetValid = true;
00309 }
00310 offset = alarm->endOffset().asSeconds() + endOffset;
00311 }
00312
00313
00314 qdt = incidence->recurrence()->getNextDateTime( from.addSecs(-offset - 1) );
00315 if ( !qdt.isValid()
00316 || (qdt = qdt.addSecs( offset )) > to )
00317 {
00318
00319 if ( !alarm->repeatCount() )
00320 continue;
00321
00322
00323 bool found = false;
00324 qdt = from.addSecs( -offset );
00325 while ( (qdt = incidence->recurrence()->getPreviousDateTime( qdt )).isValid() ) {
00326 int toFrom = qdt.secsTo( from ) - offset;
00327 if ( toFrom > alarm->duration() )
00328 break;
00329
00330
00331 int snooze = alarm->snoozeTime() * 60;
00332 if ( period >= snooze
00333 || toFrom % snooze == 0
00334 || (toFrom / snooze + 1) * snooze <= toFrom + period ) {
00335 found = true;
00336 #ifndef NDEBUG
00337 qdt = qdt.addSecs( offset + ((toFrom-1) / snooze + 1) * snooze );
00338 #endif
00339 break;
00340 }
00341 }
00342 if ( !found )
00343 continue;
00344 }
00345 }
00346 kdDebug(5800) << "CalendarLocal::appendAlarms() '" << incidence->summary()
00347 << "': " << qdt.toString() << endl;
00348 alarms.append( alarm );
00349 }
00350 }
00351 }
00352
00353
00354 void CalendarLocal::incidenceUpdated( IncidenceBase *incidence )
00355 {
00356 incidence->setSyncStatusSilent( Event::SYNCMOD );
00357 incidence->setLastModified( QDateTime::currentDateTime() );
00358
00359
00360
00361
00362
00363 notifyIncidenceChanged( static_cast<Incidence *>( incidence ) );
00364
00365 setModified( true );
00366 }
00367
00368 void CalendarLocal::insertEvent( Event *event )
00369 {
00370 QString uid = event->uid();
00371 if ( mEvents[ uid ] == 0 ) {
00372 mEvents.insert( uid, event );
00373 }
00374 #ifndef NDEBUG
00375 else
00376
00377 Q_ASSERT( mEvents[uid] == event );
00378 #endif
00379 }
00380
00381 Event::List CalendarLocal::rawEventsForDate( const QDate &qd,
00382 EventSortField sortField,
00383 SortDirection sortDirection )
00384 {
00385 Event::List eventList;
00386
00387 EventDictIterator it( mEvents );
00388 for( ; it.current(); ++it ) {
00389 Event *event = *it;
00390
00391 if ( event->doesRecur() ) {
00392 if ( event->isMultiDay() ) {
00393 int extraDays = event->dtStart().date().daysTo( event->dtEnd().date() );
00394 int i;
00395 for ( i = 0; i <= extraDays; i++ ) {
00396 if ( event->recursOn( qd.addDays( -i ) ) ) {
00397 eventList.append( event );
00398 break;
00399 }
00400 }
00401 } else {
00402 if ( event->recursOn( qd ) )
00403 eventList.append( event );
00404 }
00405 } else {
00406 if ( event->dtStart().date() <= qd && event->dateEnd() >= qd ) {
00407 eventList.append( event );
00408 }
00409 }
00410 }
00411
00412 return sortEvents( &eventList, sortField, sortDirection );
00413 }
00414
00415 Event::List CalendarLocal::rawEvents( const QDate &start, const QDate &end,
00416 bool inclusive )
00417 {
00418 Event::List eventList;
00419 QDate yesterStart = start.addDays(-1);
00420
00421
00422 EventDictIterator it( mEvents );
00423 for( ; it.current(); ++it ) {
00424 Event *event = *it;
00425
00426 QDate rStart = event->dtStart().date();
00427 if (end < rStart) {
00428
00429 continue;
00430 }
00431 if ( inclusive && rStart < start) {
00432
00433 continue;
00434 }
00435
00436 if ( ! event->doesRecur() ) {
00437 QDate rEnd = event->dtEnd().date();
00438 if (rEnd < start) {
00439
00440 continue;
00441 }
00442 if ( inclusive && end < rEnd ) {
00443
00444 continue;
00445 }
00446 } else {
00447 switch ( event->recurrence()->duration() ) {
00448 case -1:
00449 if ( inclusive ) {
00450
00451 continue;
00452 }
00453 break;
00454 case 0:
00455 default:
00456 QDate rEnd = event->recurrence()->endDate();
00457 if ( ! rEnd.isValid() ) {
00458
00459 continue;
00460 }
00461 if ( rEnd < start ) {
00462
00463 continue;
00464 }
00465 if ( inclusive && end < rEnd ) {
00466
00467 continue;
00468 }
00469
00470
00471
00472
00473
00474
00475 #if 0
00476 int durationBeforeStart = event->recurrence()->durationTo(yesterStart);
00477 int durationUntilEnd = event->recurrence()->durationTo(end);
00478 if (durationBeforeStart == durationUntilEnd) {
00479 kdDebug(5800) << "Skipping recurring event without occurences in TOI" << endl;
00480 continue;
00481 }
00482 #endif
00483 break;
00484 }
00485 }
00486
00487 eventList.append( event );
00488 }
00489
00490 return eventList;
00491 }
00492
00493 Event::List CalendarLocal::rawEventsForDate( const QDateTime &qdt )
00494 {
00495 return rawEventsForDate( qdt.date() );
00496 }
00497
00498 Event::List CalendarLocal::rawEvents( EventSortField sortField, SortDirection sortDirection )
00499 {
00500 Event::List eventList;
00501 EventDictIterator it( mEvents );
00502 for( ; it.current(); ++it )
00503 eventList.append( *it );
00504 return sortEvents( &eventList, sortField, sortDirection );
00505 }
00506
00507 bool CalendarLocal::addJournal(Journal *journal)
00508 {
00509
00510
00511
00512
00513
00514 mJournalList.append(journal);
00515
00516 journal->registerObserver( this );
00517
00518 setModified( true );
00519
00520 notifyIncidenceAdded( journal );
00521
00522 return true;
00523 }
00524
00525 bool CalendarLocal::deleteJournal( Journal *journal )
00526 {
00527 if ( mJournalList.removeRef( journal ) ) {
00528 setModified( true );
00529 notifyIncidenceDeleted( journal );
00530 mDeletedIncidences.append( journal );
00531 return true;
00532 } else {
00533 kdWarning() << "CalendarLocal::deleteJournal(): Journal not found." << endl;
00534 return false;
00535 }
00536 }
00537
00538 void CalendarLocal::deleteAllJournals()
00539 {
00540 Journal::List::ConstIterator it;
00541 for( it = mJournalList.begin(); it != mJournalList.end(); ++it ) {
00542 notifyIncidenceDeleted( *it );
00543 }
00544
00545 mJournalList.setAutoDelete( true );
00546 mJournalList.clearAll();
00547 mJournalList.setAutoDelete( false );
00548 }
00549
00550 Journal *CalendarLocal::journal( const QString &uid )
00551 {
00552 Journal::List::ConstIterator it;
00553 for ( it = mJournalList.begin(); it != mJournalList.end(); ++it )
00554 if ( (*it)->uid() == uid )
00555 return *it;
00556
00557 return 0;
00558 }
00559
00560 Journal::List CalendarLocal::rawJournals( JournalSortField sortField, SortDirection sortDirection )
00561 {
00562 return sortJournals( &mJournalList, sortField, sortDirection );
00563 }
00564
00565 Journal::List CalendarLocal::rawJournalsForDate( const QDate &date )
00566 {
00567 Journal::List journals;
00568
00569 Journal::List::ConstIterator it;
00570 for ( it = mJournalList.begin(); it != mJournalList.end(); ++it ) {
00571 Journal *journal = *it;
00572 if ( journal->dtStart().date() == date ) {
00573 journals.append( journal );
00574 }
00575 }
00576
00577 return journals;
00578 }
00579
00580 void CalendarLocal::setTimeZoneIdViewOnly( const QString& tz )
00581 {
00582 const QString question( i18n("The timezone setting was changed. In order to display the calendar "
00583 "you are looking at in the new timezone, it needs to be saved. Do you want to save the pending "
00584 "changes or rather wait and apply the new timezone on the next reload?" ) );
00585 int rc = KMessageBox::Yes;
00586 if ( isModified() ) {
00587 rc = KMessageBox::questionYesNo( 0, question,
00588 i18n("Save before applying timezones?"),
00589 KStdGuiItem::save(),
00590 KGuiItem(i18n("Apply Timezone Change on Next Reload")),
00591 "calendarLocalSaveBeforeTimezoneShift");
00592 }
00593 if ( rc == KMessageBox::Yes ) {
00594 reload( tz );
00595 }
00596 }