libkcal

calendarlocal.cpp

00001 /*
00002     This file is part of libkcal.
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 */
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   // Save only if the calendar is either modified, or saved to a
00083   // different file than it was loaded from
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 //  kdDebug(5800) << "CalendarLocal::deleteEvent" << endl;
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   // kdDebug(5800) << "CalendarLocal::deleteAllEvents" << endl;
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 //  kdDebug(5800) << "CalendarLocal::event(): " << uid << endl;
00153   return mEvents[ uid ];
00154 }
00155 
00156 bool CalendarLocal::addTodo( Todo *todo )
00157 {
00158   mTodoList.append( todo );
00159 
00160   todo->registerObserver( this );
00161 
00162   // Set up subtask relations
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   // Handle orphaned children
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   // kdDebug(5800) << "CalendarLocal::deleteAllTodos()\n";
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.clear();
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 //  kdDebug(5800) << "CalendarLocal::alarms(" << from.toString() << " - "
00240 //                << to.toString() << ")" << endl;
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     if ( (*it)->enabled() ) {
00267       QDateTime dt = (*it)->nextRepetition(preTime);
00268       if ( dt.isValid() && dt <= to ) {
00269         kdDebug(5800) << "CalendarLocal::appendAlarms() '"
00270                       << incidence->summary() << "': "
00271                       << dt.toString() << endl;
00272         alarms.append( *it );
00273       }
00274     }
00275   }
00276 }
00277 
00278 void CalendarLocal::appendRecurringAlarms( Alarm::List &alarms,
00279                                            Incidence *incidence,
00280                                            const QDateTime &from,
00281                                            const QDateTime &to )
00282 {
00283   QDateTime qdt;
00284   int  endOffset = 0;
00285   bool endOffsetValid = false;
00286   int  period = from.secsTo(to);
00287   Alarm::List::ConstIterator it;
00288   for( it = incidence->alarms().begin(); it != incidence->alarms().end();
00289        ++it ) {
00290     Alarm *alarm = *it;
00291     if ( alarm->enabled() ) {
00292       if ( alarm->hasTime() ) {
00293         // The alarm time is defined as an absolute date/time
00294         qdt = alarm->nextRepetition( from.addSecs(-1) );
00295         if ( !qdt.isValid() || qdt > to )
00296           continue;
00297       } else {
00298         // The alarm time is defined by an offset from the event start or end time.
00299         // Find the offset from the event start time, which is also used as the
00300         // offset from the recurrence time.
00301         int offset = 0;
00302         if ( alarm->hasStartOffset() ) {
00303           offset = alarm->startOffset().asSeconds();
00304         } else if ( alarm->hasEndOffset() ) {
00305           if ( !endOffsetValid ) {
00306             endOffset = incidence->dtStart().secsTo( incidence->dtEnd() );
00307             endOffsetValid = true;
00308           }
00309           offset = alarm->endOffset().asSeconds() + endOffset;
00310         }
00311 
00312         // Adjust the 'from' date/time and find the next recurrence at or after it
00313         qdt = incidence->recurrence()->getNextDateTime( from.addSecs(-offset - 1) );
00314         if ( !qdt.isValid()
00315         ||   (qdt = qdt.addSecs( offset )) > to )    // remove the adjustment to get the alarm time
00316         {
00317           // The next recurrence is too late.
00318           if ( !alarm->repeatCount() )
00319             continue;
00320           // The alarm has repetitions, so check whether repetitions of previous
00321           // recurrences fall within the time period.
00322           bool found = false;
00323           qdt = from.addSecs( -offset );
00324           while ( (qdt = incidence->recurrence()->getPreviousDateTime( qdt )).isValid() ) {
00325             int toFrom = qdt.secsTo( from ) - offset;
00326             if ( toFrom > alarm->duration() )
00327               break;     // this recurrence's last repetition is too early, so give up
00328             // The last repetition of this recurrence is at or after 'from' time.
00329             // Check if a repetition occurs between 'from' and 'to'.
00330             int snooze = alarm->snoozeTime() * 60;   // in seconds
00331             if ( period >= snooze
00332             ||   toFrom % snooze == 0
00333             ||   (toFrom / snooze + 1) * snooze <= toFrom + period ) {
00334               found = true;
00335 #ifndef NDEBUG
00336               qdt = qdt.addSecs( offset + ((toFrom-1) / snooze + 1) * snooze );   // for debug output
00337 #endif
00338               break;
00339             }
00340           }
00341           if ( !found )
00342             continue;
00343         }
00344       }
00345       kdDebug(5800) << "CalendarLocal::appendAlarms() '" << incidence->summary()
00346                     << "': " << qdt.toString() << endl;
00347       alarms.append( alarm );
00348     }
00349   }
00350 }
00351 
00352 
00353 void CalendarLocal::incidenceUpdated( IncidenceBase *incidence )
00354 {
00355   incidence->setSyncStatusSilent( Event::SYNCMOD );
00356   incidence->setLastModified( QDateTime::currentDateTime() );
00357   // we should probably update the revision number here,
00358   // or internally in the Event itself when certain things change.
00359   // need to verify with ical documentation.
00360 
00361   // The static_cast is ok as the CalendarLocal only observes Incidence objects
00362   notifyIncidenceChanged( static_cast<Incidence *>( incidence ) );
00363 
00364   setModified( true );
00365 }
00366 
00367 void CalendarLocal::insertEvent( Event *event )
00368 {
00369   QString uid = event->uid();
00370   if ( mEvents[ uid ] == 0 ) {
00371     mEvents.insert( uid, event );
00372   }
00373 #ifndef NDEBUG
00374   else // if we already have an event with this UID, it has to be the same event,
00375       // otherwise something's really broken
00376       Q_ASSERT( mEvents[uid] == event );
00377 #endif
00378 }
00379 
00380 Event::List CalendarLocal::rawEventsForDate( const QDate &qd,
00381                                              EventSortField sortField,
00382                                              SortDirection sortDirection )
00383 {
00384   Event::List eventList;
00385 
00386   EventDictIterator it( mEvents );
00387   for( ; it.current(); ++it ) {
00388     Event *event = *it;
00389 
00390     if ( event->doesRecur() ) {
00391       if ( event->isMultiDay() ) {
00392         int extraDays = event->dtStart().date().daysTo( event->dtEnd().date() );
00393         int i;
00394         for ( i = 0; i <= extraDays; i++ ) {
00395           if ( event->recursOn( qd.addDays( -i ) ) ) {
00396             eventList.append( event );
00397             break;
00398           }
00399         }
00400       } else {
00401         if ( event->recursOn( qd ) )
00402           eventList.append( event );
00403       }
00404     } else {
00405       if ( event->dtStart().date() <= qd && event->dateEnd() >= qd ) {
00406         eventList.append( event );
00407       }
00408     }
00409   }
00410 
00411   return sortEvents( &eventList, sortField, sortDirection );
00412 }
00413 
00414 Event::List CalendarLocal::rawEvents( const QDate &start, const QDate &end,
00415                                           bool inclusive )
00416 {
00417   Event::List eventList;
00418 
00419   // Get non-recurring events
00420   EventDictIterator it( mEvents );
00421   for( ; it.current(); ++it ) {
00422     Event *event = *it;
00423     if ( event->doesRecur() ) {
00424       QDate rStart = event->dtStart().date();
00425       bool found = false;
00426       if ( inclusive ) {
00427         if ( rStart >= start && rStart <= end ) {
00428           // Start date of event is in range. Now check for end date.
00429           // if duration is negative, event recurs forever, so do not include it.
00430           if ( event->recurrence()->duration() == 0 ) {  // End date set
00431             QDate rEnd = event->recurrence()->endDate();
00432             if ( rEnd >= start && rEnd <= end ) {  // End date within range
00433               found = true;
00434             }
00435           } else if ( event->recurrence()->duration() > 0 ) {  // Duration set
00436             // TODO: Calculate end date from duration. Should be done in Event
00437             // For now exclude all events with a duration.
00438           }
00439         }
00440       } else {
00441         if ( rStart <= end ) {  // Start date not after range
00442           if ( rStart >= start ) {  // Start date within range
00443             found = true;
00444           } else if ( event->recurrence()->duration() == -1 ) {  // Recurs forever
00445             found = true;
00446           } else if ( event->recurrence()->duration() == 0 ) {  // End date set
00447             QDate rEnd = event->recurrence()->endDate();
00448             if ( rEnd >= start && rEnd <= end ) {  // End date within range
00449               found = true;
00450             }
00451           } else {  // Duration set
00452             // TODO: Calculate end date from duration. Should be done in Event
00453             // For now include all events with a duration.
00454             found = true;
00455           }
00456         }
00457       }
00458 
00459       if ( found ) eventList.append( event );
00460     } else {
00461       QDate s = event->dtStart().date();
00462       QDate e = event->dtEnd().date();
00463 
00464       if ( inclusive ) {
00465         if ( s >= start && e <= end ) {
00466           eventList.append( event );
00467         }
00468       } else {
00469         if ( s <= end && e >= start ) {
00470           eventList.append( event );
00471         }
00472       }
00473     }
00474   }
00475 
00476   return eventList;
00477 }
00478 
00479 Event::List CalendarLocal::rawEventsForDate( const QDateTime &qdt )
00480 {
00481   return rawEventsForDate( qdt.date() );
00482 }
00483 
00484 Event::List CalendarLocal::rawEvents( EventSortField sortField, SortDirection sortDirection )
00485 {
00486   Event::List eventList;
00487   EventDictIterator it( mEvents );
00488   for( ; it.current(); ++it )
00489     eventList.append( *it );
00490   return sortEvents( &eventList, sortField, sortDirection );
00491 }
00492 
00493 bool CalendarLocal::addJournal(Journal *journal)
00494 {
00495 //   if (journal->dtStart().isValid())
00496 //     kdDebug(5800) << "Adding Journal on " << journal->dtStart().toString() << endl;
00497 //   else
00498 //     kdDebug(5800) << "Adding Journal without a DTSTART" << endl;
00499 
00500   mJournalList.append(journal);
00501 
00502   journal->registerObserver( this );
00503 
00504   setModified( true );
00505 
00506   notifyIncidenceAdded( journal );
00507 
00508   return true;
00509 }
00510 
00511 bool CalendarLocal::deleteJournal( Journal *journal )
00512 {
00513   if ( mJournalList.removeRef( journal ) ) {
00514     setModified( true );
00515     notifyIncidenceDeleted( journal );
00516     mDeletedIncidences.append( journal );
00517     return true;
00518   } else {
00519     kdWarning() << "CalendarLocal::deleteJournal(): Journal not found." << endl;
00520     return false;
00521   }
00522 }
00523 
00524 void CalendarLocal::deleteAllJournals()
00525 {
00526   Journal::List::ConstIterator it;
00527   for( it = mJournalList.begin(); it != mJournalList.end(); ++it ) {
00528     notifyIncidenceDeleted( *it );
00529   }
00530 
00531   mJournalList.setAutoDelete( true );
00532   mJournalList.clear();
00533   mJournalList.setAutoDelete( false );
00534 }
00535 
00536 Journal *CalendarLocal::journal( const QString &uid )
00537 {
00538   Journal::List::ConstIterator it;
00539   for ( it = mJournalList.begin(); it != mJournalList.end(); ++it )
00540     if ( (*it)->uid() == uid )
00541       return *it;
00542 
00543   return 0;
00544 }
00545 
00546 Journal::List CalendarLocal::rawJournals( JournalSortField sortField, SortDirection sortDirection )
00547 {
00548   return sortJournals( &mJournalList, sortField, sortDirection );
00549 }
00550 
00551 Journal::List CalendarLocal::rawJournalsForDate( const QDate &date )
00552 {
00553   Journal::List journals;
00554 
00555   Journal::List::ConstIterator it;
00556   for ( it = mJournalList.begin(); it != mJournalList.end(); ++it ) {
00557     Journal *journal = *it;
00558     if ( journal->dtStart().date() == date ) {
00559       journals.append( journal );
00560     }
00561   }
00562 
00563   return journals;
00564 }
00565 
00566 void CalendarLocal::setTimeZoneIdViewOnly( const QString& tz )
00567 {
00568   const QString question( i18n("The timezone setting was changed. In order to display the calendar "
00569       "you are looking at in the new timezone, it needs to be saved. Do you want to save the pending "
00570       "changes or rather wait and apply the new timezone on the next reload?" ) );
00571   int rc = KMessageBox::Yes;
00572   if ( isModified() ) {
00573     rc = KMessageBox::questionYesNo( 0, question,
00574                                        i18n("Save before applying timezones?"),
00575                                        KStdGuiItem::save(),
00576                                        KGuiItem(i18n("Apply Timezone Change on Next Reload")),
00577                                        "calendarLocalSaveBeforeTimezoneShift");
00578   }
00579   if ( rc == KMessageBox::Yes ) {
00580     reload( tz );
00581   }
00582 }
KDE Home | KDE Accessibility Home | Description of Access Keys