• Skip to content
  • Skip to link menu
KDE 4.3 API Reference
  • KDE API Reference
  • KDE-PIM Libraries
  • Sitemap
  • Contact Us
 

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() << "CalendarLocal::deleteEvent(): 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() << "CalendarLocal::deleteTodo(): 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       appendAlarms( alarmList, t, from, to );
00351     }
00352   }
00353 
00354   return alarmList;
00355 }
00356 
00357 //@cond PRIVATE
00358 void CalendarLocal::Private::insertEvent( Event *event )
00359 {
00360   QString uid = event->uid();
00361   if ( !mEvents.contains( uid ) ) {
00362     mEvents.insert( uid, event );
00363     if ( !event->recurs() && !event->isMultiDay() ) {
00364       mEventsForDate.insert( event->dtStart().date().toString(), event );
00365     }
00366   } else {
00367 #ifdef NDEBUG
00368     // if we already have an event with this UID, it must be the same event,
00369     // otherwise something's really broken
00370     Q_ASSERT( mEvents.value( uid ) == event );
00371 #endif
00372   }
00373 }
00374 //@endcond
00375 
00376 void CalendarLocal::incidenceUpdated( IncidenceBase *incidence )
00377 {
00378   KDateTime nowUTC = KDateTime::currentUtcDateTime();
00379   incidence->setLastModified( nowUTC );
00380   // we should probably update the revision number here,
00381   // or internally in the Event itself when certain things change.
00382   // need to verify with ical documentation.
00383 
00384   if ( incidence->type() == "Event" ) {
00385     Event *event = static_cast<Event*>( incidence );
00386     removeIncidenceFromMultiHashByUID<Event *>(
00387       d->mEventsForDate, event->dtStart().date().toString(), event->uid() );
00388     if ( !event->recurs() && !event->isMultiDay() ) {
00389       d->mEventsForDate.insert( event->dtStart().date().toString(), event );
00390     }
00391   } else if ( incidence->type() == "Todo" ) {
00392     Todo *todo = static_cast<Todo*>( incidence );
00393     removeIncidenceFromMultiHashByUID<Todo *>(
00394       d->mTodosForDate, todo->dtDue().date().toString(), todo->uid() );
00395     if ( todo->hasDueDate() ) {
00396       d->mTodosForDate.insert( todo->dtDue().date().toString(), todo );
00397     }
00398   } else if ( incidence->type() == "Journal" ) {
00399     Journal *journal = static_cast<Journal*>( incidence );
00400     removeIncidenceFromMultiHashByUID<Journal *>(
00401       d->mJournalsForDate, journal->dtStart().date().toString(), journal->uid() );
00402     d->mJournalsForDate.insert( journal->dtStart().date().toString(), journal );
00403   } else {
00404     Q_ASSERT( false );
00405   }
00406 
00407   // The static_cast is ok as the CalendarLocal only observes Incidence objects
00408   notifyIncidenceChanged( static_cast<Incidence *>( incidence ) );
00409 
00410   setModified( true );
00411 }
00412 
00413 Event::List CalendarLocal::rawEventsForDate( const QDate &date,
00414                                              const KDateTime::Spec &timespec,
00415                                              EventSortField sortField,
00416                                              SortDirection sortDirection )
00417 {
00418   Event::List eventList;
00419   Event *ev;
00420 
00421   // Find the hash for the specified date
00422   QString dateStr = date.toString();
00423   QMultiHash<QString, Event *>::const_iterator it = d->mEventsForDate.constFind( dateStr );
00424   // Iterate over all non-recurring, single-day events that start on this date
00425   KDateTime::Spec ts = timespec.isValid() ? timespec : timeSpec();
00426   KDateTime kdt( date, ts );
00427   while ( it != d->mEventsForDate.constEnd() && it.key() == dateStr ) {
00428     ev = it.value();
00429     KDateTime end( ev->dtEnd().toTimeSpec( ev->dtStart() ) );
00430     if ( ev->allDay() ) {
00431       end.setDateOnly( true );
00432     } else {
00433       end = end.addSecs( -1 );
00434     }
00435     if ( end >= kdt ) {
00436       eventList.append( ev );
00437     }
00438     ++it;
00439   }
00440 
00441   // Iterate over all events. Look for recurring events that occur on this date
00442   QHashIterator<QString, Event *>i( d->mEvents );
00443   while ( i.hasNext() ) {
00444     i.next();
00445     ev = i.value();
00446     if ( ev->recurs() ) {
00447       if ( ev->isMultiDay() ) {
00448         int extraDays = ev->dtStart().date().daysTo( ev->dtEnd().date() );
00449         for ( int i = 0; i <= extraDays; ++i ) {
00450           if ( ev->recursOn( date.addDays( -i ), ts ) ) {
00451             eventList.append( ev );
00452             break;
00453           }
00454         }
00455       } else {
00456         if ( ev->recursOn( date, ts ) ) {
00457           eventList.append( ev );
00458         }
00459       }
00460     } else {
00461       if ( ev->isMultiDay() ) {
00462         if ( ev->dtStart().date() <= date && ev->dtEnd().date() >= date ) {
00463           eventList.append( ev );
00464         }
00465       }
00466     }
00467   }
00468 
00469   return sortEvents( &eventList, sortField, sortDirection );
00470 }
00471 
00472 Event::List CalendarLocal::rawEvents( const QDate &start, const QDate &end,
00473                                       const KDateTime::Spec &timespec, bool inclusive )
00474 {
00475   Event::List eventList;
00476   KDateTime::Spec ts = timespec.isValid() ? timespec : timeSpec();
00477   KDateTime st( start, ts );
00478   KDateTime nd( end, ts );
00479   KDateTime yesterStart = st.addDays( -1 );
00480 
00481   // Get non-recurring events
00482   QHashIterator<QString, Event *>i( d->mEvents );
00483   Event *event;
00484   while ( i.hasNext() ) {
00485     i.next();
00486     event = i.value();
00487     KDateTime rStart = event->dtStart();
00488     if ( nd < rStart ) {
00489       continue;
00490     }
00491     if ( inclusive && rStart < st ) {
00492       continue;
00493     }
00494 
00495     if ( !event->recurs() ) { // non-recurring events
00496       KDateTime rEnd = event->dtEnd();
00497       if ( rEnd < st ) {
00498         continue;
00499       }
00500       if ( inclusive && nd < rEnd ) {
00501         continue;
00502       }
00503     } else { // recurring events
00504       switch( event->recurrence()->duration() ) {
00505       case -1: // infinite
00506         if ( inclusive ) {
00507           continue;
00508         }
00509         break;
00510       case 0: // end date given
00511       default: // count given
00512         KDateTime rEnd( event->recurrence()->endDate(), ts );
00513         if ( !rEnd.isValid() ) {
00514           continue;
00515         }
00516         if ( rEnd < st ) {
00517           continue;
00518         }
00519         if ( inclusive && nd < rEnd ) {
00520           continue;
00521         }
00522         break;
00523       } // switch(duration)
00524     } //if(recurs)
00525 
00526     eventList.append( event );
00527   }
00528 
00529   return eventList;
00530 }
00531 
00532 Event::List CalendarLocal::rawEventsForDate( const KDateTime &kdt )
00533 {
00534   return rawEventsForDate( kdt.date(), kdt.timeSpec() );
00535 }
00536 
00537 Event::List CalendarLocal::rawEvents( EventSortField sortField,
00538                                       SortDirection sortDirection )
00539 {
00540   Event::List eventList;
00541   QHashIterator<QString, Event *>i( d->mEvents );
00542   while ( i.hasNext() ) {
00543     i.next();
00544     eventList.append( i.value() );
00545   }
00546   return sortEvents( &eventList, sortField, sortDirection );
00547 }
00548 
00549 bool CalendarLocal::addJournal( Journal *journal )
00550 {
00551   d->insertJournal( journal );
00552 
00553   journal->registerObserver( this );
00554 
00555   setModified( true );
00556 
00557   notifyIncidenceAdded( journal );
00558 
00559   return true;
00560 }
00561 
00562 //@cond PRIVATE
00563 void CalendarLocal::Private::insertJournal( Journal *journal )
00564 {
00565   QString uid = journal->uid();
00566   if ( !mJournals.contains( uid ) ) {
00567     mJournals.insert( uid, journal );
00568     mJournalsForDate.insert( journal->dtStart().date().toString(), journal );
00569   } else {
00570 #ifndef NDEBUG
00571     // if we already have an journal with this UID, it must be the same journal,
00572     // otherwise something's really broken
00573     Q_ASSERT( mJournals.value( uid ) == journal );
00574 #endif
00575   }
00576 }
00577 //@endcond
00578 
00579 bool CalendarLocal::deleteJournal( Journal *journal )
00580 {
00581   if ( d->mJournals.remove( journal->uid() ) ) {
00582     setModified( true );
00583     notifyIncidenceDeleted( journal );
00584     removeIncidenceFromMultiHashByUID<Journal *>(
00585       d->mJournalsForDate, journal->dtStart().date().toString(), journal->uid() );
00586     return true;
00587   } else {
00588     kWarning() << "CalendarLocal::deleteJournal(): Journal not found.";
00589     return false;
00590   }
00591 }
00592 
00593 void CalendarLocal::deleteAllJournals()
00594 {
00595   QHashIterator<QString, Journal *>i( d->mJournals );
00596   while ( i.hasNext() ) {
00597     i.next();
00598     notifyIncidenceDeleted( i.value() );
00599     // suppress update notifications for the relation removal triggered
00600     // by the following deletions
00601     i.value()->startUpdates();
00602   }
00603   qDeleteAll( d->mJournals );
00604   d->mJournals.clear();
00605   d->mJournalsForDate.clear();
00606 }
00607 
00608 Journal *CalendarLocal::journal( const QString &uid )
00609 {
00610   return d->mJournals.value( uid );
00611 }
00612 
00613 Journal::List CalendarLocal::rawJournals( JournalSortField sortField,
00614                                           SortDirection sortDirection )
00615 {
00616   Journal::List journalList;
00617   QHashIterator<QString, Journal *>i( d->mJournals );
00618   while ( i.hasNext() ) {
00619     i.next();
00620     journalList.append( i.value() );
00621   }
00622   return sortJournals( &journalList, sortField, sortDirection );
00623 }
00624 
00625 Journal::List CalendarLocal::rawJournalsForDate( const QDate &date )
00626 {
00627   Journal::List journalList;
00628   Journal *j;
00629 
00630   QString dateStr = date.toString();
00631   QMultiHash<QString, Journal *>::const_iterator it = d->mJournalsForDate.constFind( dateStr );
00632 
00633   while ( it != d->mJournalsForDate.constEnd() && it.key() == dateStr ) {
00634     j = it.value();
00635     journalList.append( j );
00636     ++it;
00637   }
00638   return journalList;
00639 }

KCal Library

Skip menu "KCal Library"
  • Main Page
  • Namespace List
  • Class Hierarchy
  • Alphabetical List
  • Class List
  • File List
  • Namespace Members
  • Class Members
  • Related Pages

KDE-PIM Libraries

Skip menu "KDE-PIM Libraries"
  • akonadi
  • kabc
  • kblog
  • kcal
  • kholidays
  • kimap
  • kioslave
  •   imap4
  •   mbox
  • kldap
  • kmime
  • kpimidentities
  • kpimtextedit
  •   richtextbuilders
  • kpimutils
  • kresources
  • ktnef
  • kxmlrpcclient
  • mailtransport
  • microblog
  • qgpgme
  • syndication
  •   atom
  •   rdf
  •   rss2
Generated for KDE-PIM Libraries by doxygen 1.6.1
This website is maintained by Adriaan de Groot and Allen Winter.
KDE® and the K Desktop Environment® logo are registered trademarks of KDE e.V. | Legal