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 <tqdatetime.h>
00025 #include <tqstring.h>
00026 #include <tqptrlist.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 TQString &timeZoneId )
00045 : Calendar( timeZoneId ), mEvents( 47 )
00046 {
00047 init();
00048 }
00049
00050 void CalendarLocal::init()
00051 {
00052 mDeletedIncidences.setAutoDelete( true );
00053 mFileName = TQString::null;
00054 }
00055
00056
00057 CalendarLocal::~CalendarLocal()
00058 {
00059 close();
00060 }
00061
00062 bool CalendarLocal::load( const TQString &fileName, CalFormat *format )
00063 {
00064 mFileName = fileName;
00065 FileStorage storage( this, fileName, format );
00066 return storage.load();
00067 }
00068
00069 bool CalendarLocal::reload( const TQString &tz )
00070 {
00071 const TQString 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 TQString &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 = TQString::null;
00096
00097 deleteAllEvents();
00098 deleteAllTodos();
00099 deleteAllJournals();
00100
00101 mDeletedIncidences.clear();
00102 setModified( false );
00103
00104 setObserversEnabled( true );
00105 }
00106
00107 void CalendarLocal::closeEvents()
00108 {
00109 setObserversEnabled( false );
00110 mFileName = TQString::null;
00111
00112 deleteAllEvents();
00113
00114 mDeletedIncidences.clear();
00115 setModified( false );
00116
00117 setObserversEnabled( true );
00118 }
00119
00120 void CalendarLocal::closeTodos()
00121 {
00122 setObserversEnabled( false );
00123 mFileName = TQString::null;
00124
00125 deleteAllTodos();
00126
00127 mDeletedIncidences.clear();
00128 setModified( false );
00129
00130 setObserversEnabled( true );
00131 }
00132
00133 void CalendarLocal::closeJournals()
00134 {
00135 setObserversEnabled( false );
00136 mFileName = TQString::null;
00137
00138 deleteAllJournals();
00139
00140 mDeletedIncidences.clear();
00141 setModified( false );
00142
00143 setObserversEnabled( true );
00144 }
00145
00146
00147 bool CalendarLocal::addEvent( Event *event )
00148 {
00149 insertEvent( event );
00150
00151 event->registerObserver( this );
00152
00153 setModified( true );
00154
00155 notifyIncidenceAdded( event );
00156
00157 return true;
00158 }
00159
00160 bool CalendarLocal::deleteEvent( Event *event )
00161 {
00162
00163
00164 if ( mEvents.remove( event->uid() ) ) {
00165 setModified( true );
00166 notifyIncidenceDeleted( event );
00167 mDeletedIncidences.append( event );
00168
00169 if (!event->hasRecurrenceID()) {
00170 deleteChildEvents(event);
00171 }
00172 return true;
00173 } else {
00174 kdWarning() << "CalendarLocal::deleteEvent(): Event not found." << endl;
00175 return false;
00176 }
00177 }
00178
00179 bool CalendarLocal::deleteChildEvents( Event *event )
00180 {
00181 EventDictIterator it( mEvents );
00182 for( ; it.current(); ++it ) {
00183 Event *e = *it;
00184 if (e->uid() == event->uid()) {
00185 if ( e->hasRecurrenceID() ) {
00186 deleteEvent(( e ));
00187 }
00188 }
00189 }
00190
00191 return true;
00192 }
00193
00194 void CalendarLocal::deleteAllEvents()
00195 {
00196
00197 TQDictIterator<Event> it( mEvents );
00198 while( it.current() ) {
00199 notifyIncidenceDeleted( it.current() );
00200 ++it;
00201 }
00202
00203 mEvents.setAutoDelete( true );
00204 mEvents.clear();
00205 mEvents.setAutoDelete( false );
00206 }
00207
00208 Event *CalendarLocal::event( const TQString &uid )
00209 {
00210
00211 return mEvents[ uid ];
00212 }
00213
00214 bool CalendarLocal::addTodo( Todo *todo )
00215 {
00216 mTodoList.append( todo );
00217
00218 todo->registerObserver( this );
00219
00220
00221 setupRelations( todo );
00222
00223 setModified( true );
00224
00225 notifyIncidenceAdded( todo );
00226
00227 return true;
00228 }
00229
00230 bool CalendarLocal::deleteTodo( Todo *todo )
00231 {
00232
00233 removeRelations( todo );
00234
00235 if ( mTodoList.removeRef( todo ) ) {
00236 setModified( true );
00237 notifyIncidenceDeleted( todo );
00238 mDeletedIncidences.append( todo );
00239
00240 if (!todo->hasRecurrenceID()) {
00241 deleteChildTodos(todo);
00242 }
00243 return true;
00244 } else {
00245 kdWarning() << "CalendarLocal::deleteTodo(): Todo not found." << endl;
00246 return false;
00247 }
00248 }
00249
00250 bool CalendarLocal::deleteChildTodos( Todo *todo )
00251 {
00252 Todo::List::ConstIterator it;
00253 for( it = mTodoList.begin(); it != mTodoList.end(); ++it ) {
00254 Todo *t = *it;
00255 if (t->uid() == todo->uid()) {
00256 if ( t->hasRecurrenceID() ) {
00257 deleteTodo(( t ));
00258 }
00259 }
00260 }
00261
00262 return true;
00263 }
00264
00265 void CalendarLocal::deleteAllTodos()
00266 {
00267
00268 Todo::List::ConstIterator it;
00269 for( it = mTodoList.begin(); it != mTodoList.end(); ++it ) {
00270 notifyIncidenceDeleted( *it );
00271 }
00272
00273 mTodoList.setAutoDelete( true );
00274 mTodoList.clearAll();
00275 mTodoList.setAutoDelete( false );
00276 }
00277
00278 Todo::List CalendarLocal::rawTodos( TodoSortField sortField,
00279 SortDirection sortDirection )
00280 {
00281 return sortTodos( &mTodoList, sortField, sortDirection );
00282 }
00283
00284 Todo *CalendarLocal::todo( const TQString &uid )
00285 {
00286 Todo::List::ConstIterator it;
00287 for ( it = mTodoList.begin(); it != mTodoList.end(); ++it ) {
00288 if ( (*it)->uid() == uid ) return *it;
00289 }
00290
00291 return 0;
00292 }
00293
00294 Todo::List CalendarLocal::rawTodosForDate( const TQDate &date )
00295 {
00296 Todo::List todos;
00297
00298 Todo::List::ConstIterator it;
00299 for ( it = mTodoList.begin(); it != mTodoList.end(); ++it ) {
00300 Todo *todo = *it;
00301 if ( todo->hasDueDate() && todo->dtDue().date() == date ) {
00302 todos.append( todo );
00303 }
00304 }
00305
00306 return todos;
00307 }
00308
00309 Alarm::List CalendarLocal::alarmsTo( const TQDateTime &to )
00310 {
00311 return alarms( TQDateTime( TQDate( 1900, 1, 1 ) ), to );
00312 }
00313
00314 Alarm::List CalendarLocal::alarms( const TQDateTime &from, const TQDateTime &to )
00315 {
00316
00317
00318
00319 Alarm::List alarms;
00320
00321 EventDictIterator it( mEvents );
00322 for( ; it.current(); ++it ) {
00323 Event *e = *it;
00324 if ( e->doesRecur() ) appendRecurringAlarms( alarms, e, from, to );
00325 else appendAlarms( alarms, e, from, to );
00326 }
00327
00328 Todo::List::ConstIterator it2;
00329 for( it2 = mTodoList.begin(); it2 != mTodoList.end(); ++it2 ) {
00330 Todo *t = *it2;
00331 if ( t->isCompleted() ) {
00332 continue;
00333 }
00334 if ( t->doesRecur() ) appendRecurringAlarms( alarms, t, from, to );
00335 else appendAlarms( alarms, t, from, to );
00336 }
00337
00338 return alarms;
00339 }
00340
00341 void CalendarLocal::appendAlarms( Alarm::List &alarms, Incidence *incidence,
00342 const TQDateTime &from, const TQDateTime &to )
00343 {
00344 TQDateTime preTime = from.addSecs(-1);
00345 Alarm::List::ConstIterator it;
00346 for( it = incidence->alarms().begin(); it != incidence->alarms().end();
00347 ++it ) {
00348 Alarm *alarm = *it;
00349 if ( alarm->enabled() ) {
00350 TQDateTime dt = alarm->nextRepetition( preTime );
00351 if ( dt.isValid() && dt <= to ) {
00352 kdDebug(5800) << "CalendarLocal::appendAlarms() '"
00353 << incidence->summary() << "': "
00354 << dt.toString() << endl;
00355 alarms.append( alarm );
00356 }
00357 }
00358 }
00359 }
00360
00361 void CalendarLocal::appendRecurringAlarms( Alarm::List &alarms,
00362 Incidence *incidence,
00363 const TQDateTime &from,
00364 const TQDateTime &to )
00365 {
00366 TQDateTime dt;
00367 Duration endOffset( 0 );
00368 bool endOffsetValid = false;
00369 Duration period( from, to );
00370
00371 Event *e = static_cast<Event *>( incidence );
00372 Todo *t = static_cast<Todo *>( incidence );
00373
00374 Alarm::List::ConstIterator it;
00375 for( it = incidence->alarms().begin(); it != incidence->alarms().end();
00376 ++it ) {
00377 Alarm *alarm = *it;
00378 if ( alarm->enabled() ) {
00379 if ( alarm->hasTime() ) {
00380
00381 dt = alarm->nextRepetition( from.addSecs(-1) );
00382 if ( !dt.isValid() || dt > to ) {
00383 continue;
00384 }
00385 } else {
00386
00387
00388
00389 Duration offset( 0 );
00390 if ( alarm->hasStartOffset() ) {
00391 offset = alarm->startOffset().asSeconds();
00392 } else if ( alarm->hasEndOffset() ) {
00393 offset = alarm->endOffset().asSeconds();
00394 if ( !endOffsetValid ) {
00395 if ( incidence->type() == "Event" ) {
00396 endOffset = Duration( e->dtStart(), e->dtEnd() );
00397 endOffsetValid = true;
00398 } else if ( incidence->type() == "Todo" &&
00399 t->hasStartDate() && t->hasDueDate() ) {
00400 endOffset = Duration( t->dtStart(), t->dtDue() );
00401 endOffsetValid = true;
00402 }
00403 }
00404 }
00405
00406
00407 TQDateTime alarmStart;
00408 if ( incidence->type() == "Event" ) {
00409 alarmStart =
00410 offset.end( alarm->hasEndOffset() ? e->dtEnd() : e->dtStart() );
00411 } else if ( incidence->type() == "Todo" ) {
00412 alarmStart =
00413 offset.end( alarm->hasEndOffset() ? t->dtDue() : t->dtStart() );
00414 }
00415
00416 if ( alarmStart.isValid() && alarmStart > to ) {
00417 continue;
00418 }
00419
00420 TQDateTime baseStart;
00421 if ( incidence->type() == "Event" ) {
00422 baseStart = e->dtStart();
00423 } else if ( incidence->type() == "Todo" ) {
00424 baseStart = t->dtDue();
00425 }
00426 if ( alarmStart.isValid() && from > alarmStart ) {
00427 alarmStart = from;
00428 baseStart = (-offset).end( (-endOffset).end( alarmStart ) );
00429 }
00430
00431
00432
00433
00434 dt = incidence->recurrence()->getNextDateTime( baseStart.addSecs(-1) );
00435 if ( !dt.isValid() ||
00436 ( dt = endOffset.end( offset.end( dt ) ) ) > to )
00437 {
00438
00439 if ( !alarm->repeatCount() ) {
00440 continue;
00441 }
00442
00443
00444
00445 bool found = false;
00446 Duration alarmDuration = alarm->duration();
00447 for ( TQDateTime base = baseStart;
00448 ( dt = incidence->recurrence()->getPreviousDateTime( base ) ).isValid();
00449 base = dt ) {
00450 if ( alarm->duration().end( dt ) < base ) {
00451 break;
00452 }
00453
00454
00455
00456
00457 int snooze = alarm->snoozeTime().value();
00458 if ( alarm->snoozeTime().isDaily() ) {
00459 Duration toFromDuration( dt, base );
00460 int toFrom = toFromDuration.asDays();
00461 if ( alarm->snoozeTime().end( from ) <= to ||
00462 ( toFromDuration.isDaily() && toFrom % snooze == 0 ) ||
00463 ( toFrom / snooze + 1 ) * snooze <= toFrom + period.asDays() ) {
00464 found = true;
00465 #ifndef NDEBUG
00466
00467 dt = offset.end( dt ).addDays( ( ( toFrom - 1 ) / snooze + 1 ) * snooze );
00468 #endif
00469 break;
00470 }
00471 } else {
00472 int toFrom = dt.secsTo( base );
00473 if ( period.asSeconds() >= snooze ||
00474 toFrom % snooze == 0 ||
00475 ( toFrom / snooze + 1 ) * snooze <= toFrom + period.asSeconds() )
00476 {
00477 found = true;
00478 #ifndef NDEBUG
00479
00480 dt = offset.end( dt ).addSecs( ( ( toFrom - 1 ) / snooze + 1 ) * snooze );
00481 #endif
00482 break;
00483 }
00484 }
00485 }
00486 if ( !found ) {
00487 continue;
00488 }
00489 }
00490 }
00491 kdDebug(5800) << "CalendarLocal::appendAlarms() '" << incidence->summary()
00492 << "': " << dt.toString() << endl;
00493 alarms.append( alarm );
00494 }
00495 }
00496 }
00497
00498
00499 void CalendarLocal::incidenceUpdated( IncidenceBase *incidence )
00500 {
00501 incidence->setSyncStatusSilent( Event::SYNCMOD );
00502 incidence->setLastModified( TQDateTime::currentDateTime() );
00503
00504
00505
00506
00507
00508 notifyIncidenceChanged( static_cast<Incidence *>( incidence ) );
00509
00510 setModified( true );
00511 }
00512
00513 void CalendarLocal::insertEvent( Event *event )
00514 {
00515 TQString uid = event->uid();
00516 if ( mEvents[ uid ] == 0 ) {
00517 mEvents.insert( uid, event );
00518 }
00519 #ifndef NDEBUG
00520 else
00521
00522 Q_ASSERT( mEvents[uid] == event );
00523 #endif
00524 }
00525
00526 Event::List CalendarLocal::rawEventsForDate( const TQDate &qd,
00527 EventSortField sortField,
00528 SortDirection sortDirection )
00529 {
00530 Event::List eventList;
00531
00532 EventDictIterator it( mEvents );
00533 for( ; it.current(); ++it ) {
00534 Event *event = *it;
00535
00536 if ( event->doesRecur() ) {
00537 if ( event->isMultiDay() ) {
00538 int extraDays = event->dtStart().date().daysTo( event->dtEnd().date() );
00539 int i;
00540 for ( i = 0; i <= extraDays; i++ ) {
00541 if ( event->recursOn( qd.addDays( -i ), this ) ) {
00542 eventList.append( event );
00543 break;
00544 }
00545 }
00546 } else {
00547 if ( event->recursOn( qd, this ) )
00548 eventList.append( event );
00549 }
00550 } else {
00551 if ( event->dtStart().date() <= qd && event->dateEnd() >= qd ) {
00552 eventList.append( event );
00553 }
00554 }
00555 }
00556
00557 return sortEventsForDate( &eventList, qd, sortField, sortDirection );
00558 }
00559
00560 Event::List CalendarLocal::rawEvents( const TQDate &start, const TQDate &end,
00561 bool inclusive )
00562 {
00563 Event::List eventList;
00564 TQDate yesterStart = start.addDays(-1);
00565
00566
00567 EventDictIterator it( mEvents );
00568 for( ; it.current(); ++it ) {
00569 Event *event = *it;
00570
00571 TQDate rStart = event->dtStart().date();
00572 if (end < rStart) {
00573
00574 continue;
00575 }
00576 if ( inclusive && rStart < start) {
00577
00578 continue;
00579 }
00580
00581 if ( ! event->doesRecur() ) {
00582 TQDate rEnd = event->dtEnd().date();
00583 if (rEnd < start) {
00584
00585 continue;
00586 }
00587 if ( inclusive && end < rEnd ) {
00588
00589 continue;
00590 }
00591 } else {
00592 switch ( event->recurrence()->duration() ) {
00593 case -1:
00594 if ( inclusive ) {
00595
00596 continue;
00597 }
00598 break;
00599 case 0:
00600 default:
00601 TQDate rEnd = event->recurrence()->endDate();
00602 if ( ! rEnd.isValid() ) {
00603
00604 continue;
00605 }
00606 if ( rEnd < start ) {
00607
00608 continue;
00609 }
00610 if ( inclusive && end < rEnd ) {
00611
00612 continue;
00613 }
00614
00615
00616
00617
00618
00619
00620 #if 0
00621 int durationBeforeStart = event->recurrence()->durationTo(yesterStart);
00622 int durationUntilEnd = event->recurrence()->durationTo(end);
00623 if (durationBeforeStart == durationUntilEnd) {
00624 kdDebug(5800) << "Skipping recurring event without occurences in TOI" << endl;
00625 continue;
00626 }
00627 #endif
00628 break;
00629 }
00630 }
00631
00632 eventList.append( event );
00633 }
00634
00635 return eventList;
00636 }
00637
00638 Event::List CalendarLocal::rawEventsForDate( const TQDateTime &qdt )
00639 {
00640 return rawEventsForDate( qdt.date() );
00641 }
00642
00643 Event::List CalendarLocal::rawEvents( EventSortField sortField, SortDirection sortDirection )
00644 {
00645 Event::List eventList;
00646 EventDictIterator it( mEvents );
00647 for( ; it.current(); ++it )
00648 eventList.append( *it );
00649 return sortEvents( &eventList, sortField, sortDirection );
00650 }
00651
00652 bool CalendarLocal::addJournal(Journal *journal)
00653 {
00654
00655
00656
00657
00658
00659 mJournalList.append(journal);
00660
00661 journal->registerObserver( this );
00662
00663 setModified( true );
00664
00665 notifyIncidenceAdded( journal );
00666
00667 return true;
00668 }
00669
00670 bool CalendarLocal::deleteJournal( Journal *journal )
00671 {
00672 if ( mJournalList.removeRef( journal ) ) {
00673 setModified( true );
00674 notifyIncidenceDeleted( journal );
00675 mDeletedIncidences.append( journal );
00676
00677 if (!journal->hasRecurrenceID()) {
00678 deleteChildJournals(journal);
00679 }
00680 return true;
00681 } else {
00682 kdWarning() << "CalendarLocal::deleteJournal(): Journal not found." << endl;
00683 return false;
00684 }
00685 }
00686
00687 bool CalendarLocal::deleteChildJournals( Journal *journal )
00688 {
00689 Journal::List::ConstIterator it;
00690 for( it = mJournalList.begin(); it != mJournalList.end(); ++it ) {
00691 Journal *j = *it;
00692 if (j->uid() == journal->uid()) {
00693 if ( j->hasRecurrenceID() ) {
00694 deleteJournal(( j ));
00695 }
00696 }
00697 }
00698
00699 return true;
00700 }
00701
00702 void CalendarLocal::deleteAllJournals()
00703 {
00704 Journal::List::ConstIterator it;
00705 for( it = mJournalList.begin(); it != mJournalList.end(); ++it ) {
00706 notifyIncidenceDeleted( *it );
00707 }
00708
00709 mJournalList.setAutoDelete( true );
00710 mJournalList.clearAll();
00711 mJournalList.setAutoDelete( false );
00712 }
00713
00714 Journal *CalendarLocal::journal( const TQString &uid )
00715 {
00716 Journal::List::ConstIterator it;
00717 for ( it = mJournalList.begin(); it != mJournalList.end(); ++it )
00718 if ( (*it)->uid() == uid )
00719 return *it;
00720
00721 return 0;
00722 }
00723
00724 Journal::List CalendarLocal::rawJournals( JournalSortField sortField, SortDirection sortDirection )
00725 {
00726 return sortJournals( &mJournalList, sortField, sortDirection );
00727 }
00728
00729 Journal::List CalendarLocal::rawJournalsForDate( const TQDate &date )
00730 {
00731 Journal::List journals;
00732
00733 Journal::List::ConstIterator it;
00734 for ( it = mJournalList.begin(); it != mJournalList.end(); ++it ) {
00735 Journal *journal = *it;
00736 if ( journal->dtStart().date() == date ) {
00737 journals.append( journal );
00738 }
00739 }
00740
00741 return journals;
00742 }
00743
00744 void CalendarLocal::setTimeZoneIdViewOnly( const TQString& tz )
00745 {
00746 const TQString question( i18n("The timezone setting was changed. In order to display the calendar "
00747 "you are looking at in the new timezone, it needs to be saved. Do you want to save the pending "
00748 "changes or rather wait and apply the new timezone on the next reload?" ) );
00749 int rc = KMessageBox::Yes;
00750 if ( isModified() ) {
00751 rc = KMessageBox::questionYesNo( 0, question,
00752 i18n("Save before applying timezones?"),
00753 KStdGuiItem::save(),
00754 KGuiItem(i18n("Apply Timezone Change on Next Reload")),
00755 "calendarLocalSaveBeforeTimezoneShift");
00756 }
00757 if ( rc == KMessageBox::Yes ) {
00758 reload( tz );
00759 }
00760 }