KCal Library
calendarlocal.cpp
Go to the documentation of this file.
00001 /* 00002 This file is part of the kcal library. 00003 00004 Copyright (c) 1998 Preston Brown <pbrown@kde.org> 00005 Copyright (c) 2001,2003,2004 Cornelius Schumacher <schumacher@kde.org> 00006 Copyright (C) 2003-2004 Reinhold Kainhofer <reinhold@kainhofer.com> 00007 00008 This library is free software; you can redistribute it and/or 00009 modify it under the terms of the GNU Library General Public 00010 License as published by the Free Software Foundation; either 00011 version 2 of the License, or (at your option) any later version. 00012 00013 This library is distributed in the hope that it will be useful, 00014 but WITHOUT ANY WARRANTY; without even the implied warranty of 00015 MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU 00016 Library General Public License for more details. 00017 00018 You should have received a copy of the GNU Library General Public License 00019 along with this library; see the file COPYING.LIB. If not, write to 00020 the Free Software Foundation, Inc., 51 Franklin Street, Fifth Floor, 00021 Boston, MA 02110-1301, USA. 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 //@cond PRIVATE 00059 class KCal::CalendarLocal::Private 00060 { 00061 public: 00062 Private() {} 00063 QString mFileName; // filename where calendar is stored 00064 CalFormat *mFormat; // calendar format 00065 00066 QHash<QString, Event *>mEvents; // hash on uids of all Events 00067 QMultiHash<QString, Event *>mEventsForDate;// on start dates of non-recurring, single-day Events 00068 QHash<QString, Todo *>mTodos; // hash on uids of all Todos 00069 QMultiHash<QString, Todo*>mTodosForDate;// on due dates for all Todos 00070 QHash<QString, Journal *>mJournals; // hash on uids of all Journals 00071 QMultiHash<QString, Journal *>mJournalsForDate; // on dates of all Journals 00072 00073 void insertEvent( Event *event ); 00074 void insertTodo( Todo *todo ); 00075 void insertJournal( Journal *journal ); 00076 }; 00077 00078 // helper 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 //@endcond 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 // Save only if the calendar is either modified, or saved to a 00149 // different file than it was loaded from 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() << "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 // suppress update notifications for the relation removal triggered 00209 // by the following deletions 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 // Set up sub-to-do relations 00229 setupRelations( todo ); 00230 00231 setModified( true ); 00232 00233 notifyIncidenceAdded( todo ); 00234 00235 return true; 00236 } 00237 00238 //@cond PRIVATE 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 // if we already have an to-do with this UID, it must be the same to-do, 00251 // otherwise something's really broken 00252 Q_ASSERT( mTodos.value( uid ) == todo ); 00253 #endif 00254 } 00255 } 00256 //@endcond 00257 00258 bool CalendarLocal::deleteTodo( Todo *todo ) 00259 { 00260 // Handle orphaned children 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() << "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 // suppress update notifications for the relation removal triggered 00284 // by the following deletions 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 if ( t->recurs() ) { 00351 appendRecurringAlarms( alarmList, t, from, to ); 00352 } else { 00353 appendAlarms( alarmList, t, from, to ); 00354 } 00355 } 00356 } 00357 00358 return alarmList; 00359 } 00360 00361 //@cond PRIVATE 00362 void CalendarLocal::Private::insertEvent( Event *event ) 00363 { 00364 QString uid = event->uid(); 00365 if ( !mEvents.contains( uid ) ) { 00366 mEvents.insert( uid, event ); 00367 if ( !event->recurs() && !event->isMultiDay() ) { 00368 mEventsForDate.insert( event->dtStart().date().toString(), event ); 00369 } 00370 } else { 00371 #ifdef NDEBUG 00372 // if we already have an event with this UID, it must be the same event, 00373 // otherwise something's really broken 00374 Q_ASSERT( mEvents.value( uid ) == event ); 00375 #endif 00376 } 00377 } 00378 //@endcond 00379 00380 void CalendarLocal::incidenceUpdated( IncidenceBase *incidence ) 00381 { 00382 KDateTime nowUTC = KDateTime::currentUtcDateTime(); 00383 incidence->setLastModified( nowUTC ); 00384 // we should probably update the revision number here, 00385 // or internally in the Event itself when certain things change. 00386 // need to verify with ical documentation. 00387 00388 if ( incidence->type() == "Event" ) { 00389 Event *event = static_cast<Event*>( incidence ); 00390 removeIncidenceFromMultiHashByUID<Event *>( 00391 d->mEventsForDate, event->dtStart().date().toString(), event->uid() ); 00392 if ( !event->recurs() && !event->isMultiDay() ) { 00393 d->mEventsForDate.insert( event->dtStart().date().toString(), event ); 00394 } 00395 } else if ( incidence->type() == "Todo" ) { 00396 Todo *todo = static_cast<Todo*>( incidence ); 00397 removeIncidenceFromMultiHashByUID<Todo *>( 00398 d->mTodosForDate, todo->dtDue().date().toString(), todo->uid() ); 00399 if ( todo->hasDueDate() ) { 00400 d->mTodosForDate.insert( todo->dtDue().date().toString(), todo ); 00401 } 00402 } else if ( incidence->type() == "Journal" ) { 00403 Journal *journal = static_cast<Journal*>( incidence ); 00404 removeIncidenceFromMultiHashByUID<Journal *>( 00405 d->mJournalsForDate, journal->dtStart().date().toString(), journal->uid() ); 00406 d->mJournalsForDate.insert( journal->dtStart().date().toString(), journal ); 00407 } else { 00408 Q_ASSERT( false ); 00409 } 00410 00411 // The static_cast is ok as the CalendarLocal only observes Incidence objects 00412 notifyIncidenceChanged( static_cast<Incidence *>( incidence ) ); 00413 00414 setModified( true ); 00415 } 00416 00417 Event::List CalendarLocal::rawEventsForDate( const QDate &date, 00418 const KDateTime::Spec ×pec, 00419 EventSortField sortField, 00420 SortDirection sortDirection ) 00421 { 00422 Event::List eventList; 00423 Event *ev; 00424 00425 // Find the hash for the specified date 00426 QString dateStr = date.toString(); 00427 QMultiHash<QString, Event *>::const_iterator it = d->mEventsForDate.constFind( dateStr ); 00428 // Iterate over all non-recurring, single-day events that start on this date 00429 KDateTime::Spec ts = timespec.isValid() ? timespec : timeSpec(); 00430 KDateTime kdt( date, ts ); 00431 while ( it != d->mEventsForDate.constEnd() && it.key() == dateStr ) { 00432 ev = it.value(); 00433 KDateTime end( ev->dtEnd().toTimeSpec( ev->dtStart() ) ); 00434 if ( ev->allDay() ) { 00435 end.setDateOnly( true ); 00436 } else { 00437 end = end.addSecs( -1 ); 00438 } 00439 if ( end >= kdt ) { 00440 eventList.append( ev ); 00441 } 00442 ++it; 00443 } 00444 00445 // Iterate over all events. Look for recurring events that occur on this date 00446 QHashIterator<QString, Event *>i( d->mEvents ); 00447 while ( i.hasNext() ) { 00448 i.next(); 00449 ev = i.value(); 00450 if ( ev->recurs() ) { 00451 if ( ev->isMultiDay() ) { 00452 int extraDays = ev->dtStart().date().daysTo( ev->dtEnd().date() ); 00453 for ( int i = 0; i <= extraDays; ++i ) { 00454 if ( ev->recursOn( date.addDays( -i ), ts ) ) { 00455 eventList.append( ev ); 00456 break; 00457 } 00458 } 00459 } else { 00460 if ( ev->recursOn( date, ts ) ) { 00461 eventList.append( ev ); 00462 } 00463 } 00464 } else { 00465 if ( ev->isMultiDay() ) { 00466 if ( ev->dtStart().date() <= date && ev->dtEnd().date() >= date ) { 00467 eventList.append( ev ); 00468 } 00469 } 00470 } 00471 } 00472 00473 return sortEventsForDate( &eventList, date, timespec, sortField, sortDirection ); 00474 } 00475 00476 Event::List CalendarLocal::rawEvents( const QDate &start, const QDate &end, 00477 const KDateTime::Spec ×pec, bool inclusive ) 00478 { 00479 Event::List eventList; 00480 KDateTime::Spec ts = timespec.isValid() ? timespec : timeSpec(); 00481 KDateTime st( start, ts ); 00482 KDateTime nd( end, ts ); 00483 KDateTime yesterStart = st.addDays( -1 ); 00484 00485 // Get non-recurring events 00486 QHashIterator<QString, Event *>i( d->mEvents ); 00487 Event *event; 00488 while ( i.hasNext() ) { 00489 i.next(); 00490 event = i.value(); 00491 KDateTime rStart = event->dtStart(); 00492 if ( nd < rStart ) { 00493 continue; 00494 } 00495 if ( inclusive && rStart < st ) { 00496 continue; 00497 } 00498 00499 if ( !event->recurs() ) { // non-recurring events 00500 KDateTime rEnd = event->dtEnd(); 00501 if ( rEnd < st ) { 00502 continue; 00503 } 00504 if ( inclusive && nd < rEnd ) { 00505 continue; 00506 } 00507 } else { // recurring events 00508 switch( event->recurrence()->duration() ) { 00509 case -1: // infinite 00510 if ( inclusive ) { 00511 continue; 00512 } 00513 break; 00514 case 0: // end date given 00515 default: // count given 00516 KDateTime rEnd( event->recurrence()->endDate(), ts ); 00517 if ( !rEnd.isValid() ) { 00518 continue; 00519 } 00520 if ( rEnd < st ) { 00521 continue; 00522 } 00523 if ( inclusive && nd < rEnd ) { 00524 continue; 00525 } 00526 break; 00527 } // switch(duration) 00528 } //if(recurs) 00529 00530 eventList.append( event ); 00531 } 00532 00533 return eventList; 00534 } 00535 00536 Event::List CalendarLocal::rawEventsForDate( const KDateTime &kdt ) 00537 { 00538 return rawEventsForDate( kdt.date(), kdt.timeSpec() ); 00539 } 00540 00541 Event::List CalendarLocal::rawEvents( EventSortField sortField, 00542 SortDirection sortDirection ) 00543 { 00544 Event::List eventList; 00545 QHashIterator<QString, Event *>i( d->mEvents ); 00546 while ( i.hasNext() ) { 00547 i.next(); 00548 eventList.append( i.value() ); 00549 } 00550 return sortEvents( &eventList, sortField, sortDirection ); 00551 } 00552 00553 bool CalendarLocal::addJournal( Journal *journal ) 00554 { 00555 d->insertJournal( journal ); 00556 00557 journal->registerObserver( this ); 00558 00559 setModified( true ); 00560 00561 notifyIncidenceAdded( journal ); 00562 00563 return true; 00564 } 00565 00566 //@cond PRIVATE 00567 void CalendarLocal::Private::insertJournal( Journal *journal ) 00568 { 00569 QString uid = journal->uid(); 00570 if ( !mJournals.contains( uid ) ) { 00571 mJournals.insert( uid, journal ); 00572 mJournalsForDate.insert( journal->dtStart().date().toString(), journal ); 00573 } else { 00574 #ifndef NDEBUG 00575 // if we already have an journal with this UID, it must be the same journal, 00576 // otherwise something's really broken 00577 Q_ASSERT( mJournals.value( uid ) == journal ); 00578 #endif 00579 } 00580 } 00581 //@endcond 00582 00583 bool CalendarLocal::deleteJournal( Journal *journal ) 00584 { 00585 if ( d->mJournals.remove( journal->uid() ) ) { 00586 setModified( true ); 00587 notifyIncidenceDeleted( journal ); 00588 removeIncidenceFromMultiHashByUID<Journal *>( 00589 d->mJournalsForDate, journal->dtStart().date().toString(), journal->uid() ); 00590 return true; 00591 } else { 00592 kWarning() << "Journal not found."; 00593 return false; 00594 } 00595 } 00596 00597 void CalendarLocal::deleteAllJournals() 00598 { 00599 QHashIterator<QString, Journal *>i( d->mJournals ); 00600 while ( i.hasNext() ) { 00601 i.next(); 00602 notifyIncidenceDeleted( i.value() ); 00603 // suppress update notifications for the relation removal triggered 00604 // by the following deletions 00605 i.value()->startUpdates(); 00606 } 00607 qDeleteAll( d->mJournals ); 00608 d->mJournals.clear(); 00609 d->mJournalsForDate.clear(); 00610 } 00611 00612 Journal *CalendarLocal::journal( const QString &uid ) 00613 { 00614 return d->mJournals.value( uid ); 00615 } 00616 00617 Journal::List CalendarLocal::rawJournals( JournalSortField sortField, 00618 SortDirection sortDirection ) 00619 { 00620 Journal::List journalList; 00621 QHashIterator<QString, Journal *>i( d->mJournals ); 00622 while ( i.hasNext() ) { 00623 i.next(); 00624 journalList.append( i.value() ); 00625 } 00626 return sortJournals( &journalList, sortField, sortDirection ); 00627 } 00628 00629 Journal::List CalendarLocal::rawJournalsForDate( const QDate &date ) 00630 { 00631 Journal::List journalList; 00632 Journal *j; 00633 00634 QString dateStr = date.toString(); 00635 QMultiHash<QString, Journal *>::const_iterator it = d->mJournalsForDate.constFind( dateStr ); 00636 00637 while ( it != d->mJournalsForDate.constEnd() && it.key() == dateStr ) { 00638 j = it.value(); 00639 journalList.append( j ); 00640 ++it; 00641 } 00642 return journalList; 00643 }
This file is part of the KDE documentation.
Documentation copyright © 1996-2012 The KDE developers.
Generated on Thu Aug 2 2012 15:25:52 by doxygen 1.7.5 written by Dimitri van Heesch, © 1997-2006
Documentation copyright © 1996-2012 The KDE developers.
Generated on Thu Aug 2 2012 15:25:52 by doxygen 1.7.5 written by Dimitri van Heesch, © 1997-2006
KDE's Doxygen guidelines are available online.