diff options
author | toma <toma@283d02a7-25f6-0310-bc7c-ecb5cbfe19da> | 2009-11-25 17:56:58 +0000 |
---|---|---|
committer | toma <toma@283d02a7-25f6-0310-bc7c-ecb5cbfe19da> | 2009-11-25 17:56:58 +0000 |
commit | 460c52653ab0dcca6f19a4f492ed2c5e4e963ab0 (patch) | |
tree | 67208f7c145782a7e90b123b982ca78d88cc2c87 /libkcal/calendarlocal.cpp | |
download | tdepim-460c52653ab0dcca6f19a4f492ed2c5e4e963ab0.tar.gz tdepim-460c52653ab0dcca6f19a4f492ed2c5e4e963ab0.zip |
Copy the KDE 3.5 branch to branches/trinity for new KDE 3.5 features.
BUG:215923
git-svn-id: svn://anonsvn.kde.org/home/kde/branches/trinity/kdepim@1054174 283d02a7-25f6-0310-bc7c-ecb5cbfe19da
Diffstat (limited to 'libkcal/calendarlocal.cpp')
-rw-r--r-- | libkcal/calendarlocal.cpp | 595 |
1 files changed, 595 insertions, 0 deletions
diff --git a/libkcal/calendarlocal.cpp b/libkcal/calendarlocal.cpp new file mode 100644 index 000000000..a37488f6f --- /dev/null +++ b/libkcal/calendarlocal.cpp @@ -0,0 +1,595 @@ +/* + This file is part of libkcal. + + Copyright (c) 1998 Preston Brown <pbrown@kde.org> + Copyright (c) 2001,2003,2004 Cornelius Schumacher <schumacher@kde.org> + Copyright (C) 2003-2004 Reinhold Kainhofer <reinhold@kainhofer.com> + + This library is free software; you can redistribute it and/or + modify it under the terms of the GNU Library General Public + License as published by the Free Software Foundation; either + version 2 of the License, or (at your option) any later version. + + This library is distributed in the hope that it will be useful, + but WITHOUT ANY WARRANTY; without even the implied warranty of + MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU + Library General Public License for more details. + + You should have received a copy of the GNU Library General Public License + along with this library; see the file COPYING.LIB. If not, write to + the Free Software Foundation, Inc., 51 Franklin Street, Fifth Floor, + Boston, MA 02110-1301, USA. +*/ + +#include <qdatetime.h> +#include <qstring.h> +#include <qptrlist.h> + +#include <kdebug.h> +#include <klocale.h> +#include <kmessagebox.h> + +#include "vcaldrag.h" +#include "vcalformat.h" +#include "icalformat.h" +#include "exceptions.h" +#include "incidence.h" +#include "journal.h" +#include "filestorage.h" + +#include "calendarlocal.h" + +using namespace KCal; + +CalendarLocal::CalendarLocal( const QString &timeZoneId ) + : Calendar( timeZoneId ), mEvents( 47 ) +{ + init(); +} + +void CalendarLocal::init() +{ + mDeletedIncidences.setAutoDelete( true ); + mFileName = QString::null; +} + + +CalendarLocal::~CalendarLocal() +{ + close(); +} + +bool CalendarLocal::load( const QString &fileName, CalFormat *format ) +{ + mFileName = fileName; + FileStorage storage( this, fileName, format ); + return storage.load(); +} + +bool CalendarLocal::reload( const QString &tz ) +{ + const QString filename = mFileName; + save(); + close(); + mFileName = filename; + setTimeZoneId( tz ); + FileStorage storage( this, mFileName ); + return storage.load(); +} + +bool CalendarLocal::save( const QString &fileName, CalFormat *format ) +{ + // Save only if the calendar is either modified, or saved to a + // different file than it was loaded from + if ( mFileName != fileName || isModified() ) { + FileStorage storage( this, fileName, format ); + return storage.save(); + } else { + return true; + } +} + +void CalendarLocal::close() +{ + setObserversEnabled( false ); + mFileName = QString::null; + + deleteAllEvents(); + deleteAllTodos(); + deleteAllJournals(); + + mDeletedIncidences.clear(); + setModified( false ); + + setObserversEnabled( true ); +} + + +bool CalendarLocal::addEvent( Event *event ) +{ + insertEvent( event ); + + event->registerObserver( this ); + + setModified( true ); + + notifyIncidenceAdded( event ); + + return true; +} + +bool CalendarLocal::deleteEvent( Event *event ) +{ +// kdDebug(5800) << "CalendarLocal::deleteEvent" << endl; + + if ( mEvents.remove( event->uid() ) ) { + setModified( true ); + notifyIncidenceDeleted( event ); + mDeletedIncidences.append( event ); + return true; + } else { + kdWarning() << "CalendarLocal::deleteEvent(): Event not found." << endl; + return false; + } +} + +void CalendarLocal::deleteAllEvents() +{ + // kdDebug(5800) << "CalendarLocal::deleteAllEvents" << endl; + QDictIterator<Event> it( mEvents ); + while( it.current() ) { + notifyIncidenceDeleted( it.current() ); + ++it; + } + + mEvents.setAutoDelete( true ); + mEvents.clear(); + mEvents.setAutoDelete( false ); +} + +Event *CalendarLocal::event( const QString &uid ) +{ +// kdDebug(5800) << "CalendarLocal::event(): " << uid << endl; + return mEvents[ uid ]; +} + +bool CalendarLocal::addTodo( Todo *todo ) +{ + mTodoList.append( todo ); + + todo->registerObserver( this ); + + // Set up subtask relations + setupRelations( todo ); + + setModified( true ); + + notifyIncidenceAdded( todo ); + + return true; +} + +bool CalendarLocal::deleteTodo( Todo *todo ) +{ + // Handle orphaned children + removeRelations( todo ); + + if ( mTodoList.removeRef( todo ) ) { + setModified( true ); + notifyIncidenceDeleted( todo ); + mDeletedIncidences.append( todo ); + return true; + } else { + kdWarning() << "CalendarLocal::deleteTodo(): Todo not found." << endl; + return false; + } +} + +void CalendarLocal::deleteAllTodos() +{ + // kdDebug(5800) << "CalendarLocal::deleteAllTodos()\n"; + Todo::List::ConstIterator it; + for( it = mTodoList.begin(); it != mTodoList.end(); ++it ) { + notifyIncidenceDeleted( *it ); + } + + mTodoList.setAutoDelete( true ); + mTodoList.clearAll(); + mTodoList.setAutoDelete( false ); +} + +Todo::List CalendarLocal::rawTodos( TodoSortField sortField, + SortDirection sortDirection ) +{ + return sortTodos( &mTodoList, sortField, sortDirection ); +} + +Todo *CalendarLocal::todo( const QString &uid ) +{ + Todo::List::ConstIterator it; + for ( it = mTodoList.begin(); it != mTodoList.end(); ++it ) { + if ( (*it)->uid() == uid ) return *it; + } + + return 0; +} + +Todo::List CalendarLocal::rawTodosForDate( const QDate &date ) +{ + Todo::List todos; + + Todo::List::ConstIterator it; + for ( it = mTodoList.begin(); it != mTodoList.end(); ++it ) { + Todo *todo = *it; + if ( todo->hasDueDate() && todo->dtDue().date() == date ) { + todos.append( todo ); + } + } + + return todos; +} + +Alarm::List CalendarLocal::alarmsTo( const QDateTime &to ) +{ + return alarms( QDateTime( QDate( 1900, 1, 1 ) ), to ); +} + +Alarm::List CalendarLocal::alarms( const QDateTime &from, const QDateTime &to ) +{ +// kdDebug(5800) << "CalendarLocal::alarms(" << from.toString() << " - " +// << to.toString() << ")" << endl; + + Alarm::List alarms; + + EventDictIterator it( mEvents ); + for( ; it.current(); ++it ) { + Event *e = *it; + if ( e->doesRecur() ) appendRecurringAlarms( alarms, e, from, to ); + else appendAlarms( alarms, e, from, to ); + } + + Todo::List::ConstIterator it2; + for( it2 = mTodoList.begin(); it2 != mTodoList.end(); ++it2 ) { + if (! (*it2)->isCompleted() ) appendAlarms( alarms, *it2, from, to ); + } + + return alarms; +} + +void CalendarLocal::appendAlarms( Alarm::List &alarms, Incidence *incidence, + const QDateTime &from, const QDateTime &to ) +{ + QDateTime preTime = from.addSecs(-1); + Alarm::List::ConstIterator it; + for( it = incidence->alarms().begin(); it != incidence->alarms().end(); + ++it ) { + if ( (*it)->enabled() ) { + QDateTime dt = (*it)->nextRepetition(preTime); + if ( dt.isValid() && dt <= to ) { + kdDebug(5800) << "CalendarLocal::appendAlarms() '" + << incidence->summary() << "': " + << dt.toString() << endl; + alarms.append( *it ); + } + } + } +} + +void CalendarLocal::appendRecurringAlarms( Alarm::List &alarms, + Incidence *incidence, + const QDateTime &from, + const QDateTime &to ) +{ + QDateTime qdt; + int endOffset = 0; + bool endOffsetValid = false; + int period = from.secsTo(to); + Alarm::List::ConstIterator it; + for( it = incidence->alarms().begin(); it != incidence->alarms().end(); + ++it ) { + Alarm *alarm = *it; + if ( alarm->enabled() ) { + if ( alarm->hasTime() ) { + // The alarm time is defined as an absolute date/time + qdt = alarm->nextRepetition( from.addSecs(-1) ); + if ( !qdt.isValid() || qdt > to ) + continue; + } else { + // The alarm time is defined by an offset from the event start or end time. + // Find the offset from the event start time, which is also used as the + // offset from the recurrence time. + int offset = 0; + if ( alarm->hasStartOffset() ) { + offset = alarm->startOffset().asSeconds(); + } else if ( alarm->hasEndOffset() ) { + if ( !endOffsetValid ) { + endOffset = incidence->dtStart().secsTo( incidence->dtEnd() ); + endOffsetValid = true; + } + offset = alarm->endOffset().asSeconds() + endOffset; + } + + // Adjust the 'from' date/time and find the next recurrence at or after it + qdt = incidence->recurrence()->getNextDateTime( from.addSecs(-offset - 1) ); + if ( !qdt.isValid() + || (qdt = qdt.addSecs( offset )) > to ) // remove the adjustment to get the alarm time + { + // The next recurrence is too late. + if ( !alarm->repeatCount() ) + continue; + // The alarm has repetitions, so check whether repetitions of previous + // recurrences fall within the time period. + bool found = false; + qdt = from.addSecs( -offset ); + while ( (qdt = incidence->recurrence()->getPreviousDateTime( qdt )).isValid() ) { + int toFrom = qdt.secsTo( from ) - offset; + if ( toFrom > alarm->duration() ) + break; // this recurrence's last repetition is too early, so give up + // The last repetition of this recurrence is at or after 'from' time. + // Check if a repetition occurs between 'from' and 'to'. + int snooze = alarm->snoozeTime() * 60; // in seconds + if ( period >= snooze + || toFrom % snooze == 0 + || (toFrom / snooze + 1) * snooze <= toFrom + period ) { + found = true; +#ifndef NDEBUG + qdt = qdt.addSecs( offset + ((toFrom-1) / snooze + 1) * snooze ); // for debug output +#endif + break; + } + } + if ( !found ) + continue; + } + } + kdDebug(5800) << "CalendarLocal::appendAlarms() '" << incidence->summary() + << "': " << qdt.toString() << endl; + alarms.append( alarm ); + } + } +} + + +void CalendarLocal::incidenceUpdated( IncidenceBase *incidence ) +{ + incidence->setSyncStatusSilent( Event::SYNCMOD ); + incidence->setLastModified( QDateTime::currentDateTime() ); + // we should probably update the revision number here, + // or internally in the Event itself when certain things change. + // need to verify with ical documentation. + + // The static_cast is ok as the CalendarLocal only observes Incidence objects + notifyIncidenceChanged( static_cast<Incidence *>( incidence ) ); + + setModified( true ); +} + +void CalendarLocal::insertEvent( Event *event ) +{ + QString uid = event->uid(); + if ( mEvents[ uid ] == 0 ) { + mEvents.insert( uid, event ); + } +#ifndef NDEBUG + else // if we already have an event with this UID, it has to be the same event, + // otherwise something's really broken + Q_ASSERT( mEvents[uid] == event ); +#endif +} + +Event::List CalendarLocal::rawEventsForDate( const QDate &qd, + EventSortField sortField, + SortDirection sortDirection ) +{ + Event::List eventList; + + EventDictIterator it( mEvents ); + for( ; it.current(); ++it ) { + Event *event = *it; + + if ( event->doesRecur() ) { + if ( event->isMultiDay() ) { + int extraDays = event->dtStart().date().daysTo( event->dtEnd().date() ); + int i; + for ( i = 0; i <= extraDays; i++ ) { + if ( event->recursOn( qd.addDays( -i ) ) ) { + eventList.append( event ); + break; + } + } + } else { + if ( event->recursOn( qd ) ) + eventList.append( event ); + } + } else { + if ( event->dtStart().date() <= qd && event->dateEnd() >= qd ) { + eventList.append( event ); + } + } + } + + return sortEvents( &eventList, sortField, sortDirection ); +} + +Event::List CalendarLocal::rawEvents( const QDate &start, const QDate &end, + bool inclusive ) +{ + Event::List eventList; + QDate yesterStart = start.addDays(-1); + + // Get non-recurring events + EventDictIterator it( mEvents ); + for( ; it.current(); ++it ) { + Event *event = *it; + + QDate rStart = event->dtStart().date(); + if (end < rStart) { +// kdDebug(5800) << "Skipping event starting after TOI" << endl; + continue; + } + if ( inclusive && rStart < start) { +// kdDebug(5800) << "Skipping event starting before TOI while inclusive" << endl; + continue; + } + + if ( ! event->doesRecur() ) { // non-recurring events + QDate rEnd = event->dtEnd().date(); + if (rEnd < start) { +// kdDebug(5800) << "Skipping event ending before TOI" << endl; + continue; + } + if ( inclusive && end < rEnd ) { +// kdDebug(5800) << "Skipping event ending after TOI while inclusive" << endl; + continue; + } + } else { // recurring events + switch ( event->recurrence()->duration() ) { + case -1: // infinite + if ( inclusive ) { +// kdDebug(5800) << "Skipping infinite event because inclusive" << endl; + continue; + } + break; + case 0: // end date given + default: // count given + QDate rEnd = event->recurrence()->endDate(); + if ( ! rEnd.isValid() ) { +// kdDebug(5800) << "Skipping recurring event without occurences" << endl; + continue; + } + if ( rEnd < start ) { +// kdDebug(5800) << "Skipping recurring event ending before TOI" << endl; + continue; + } + if ( inclusive && end < rEnd ) { +// kdDebug(5800) << "Skipping recurring event ending after TOI while inclusive" << endl; + continue; + } + /* FIXME: too much conversion between QDate and QDateTime makes this useless: + * freebusy(end=QDateTime(day, "00:00:00")) -> + * rawEvents(end=QDate(day)) -> + * durationTo(QDateTime(day, "23:59:59")) + * so events repeating at the end day match and are included. + */ +#if 0 + int durationBeforeStart = event->recurrence()->durationTo(yesterStart); + int durationUntilEnd = event->recurrence()->durationTo(end); + if (durationBeforeStart == durationUntilEnd) { + kdDebug(5800) << "Skipping recurring event without occurences in TOI" << endl; + continue; + } +#endif + break; + } // switch(duration) + } // if(doesRecur) + + eventList.append( event ); + } + + return eventList; +} + +Event::List CalendarLocal::rawEventsForDate( const QDateTime &qdt ) +{ + return rawEventsForDate( qdt.date() ); +} + +Event::List CalendarLocal::rawEvents( EventSortField sortField, SortDirection sortDirection ) +{ + Event::List eventList; + EventDictIterator it( mEvents ); + for( ; it.current(); ++it ) + eventList.append( *it ); + return sortEvents( &eventList, sortField, sortDirection ); +} + +bool CalendarLocal::addJournal(Journal *journal) +{ +// if (journal->dtStart().isValid()) +// kdDebug(5800) << "Adding Journal on " << journal->dtStart().toString() << endl; +// else +// kdDebug(5800) << "Adding Journal without a DTSTART" << endl; + + mJournalList.append(journal); + + journal->registerObserver( this ); + + setModified( true ); + + notifyIncidenceAdded( journal ); + + return true; +} + +bool CalendarLocal::deleteJournal( Journal *journal ) +{ + if ( mJournalList.removeRef( journal ) ) { + setModified( true ); + notifyIncidenceDeleted( journal ); + mDeletedIncidences.append( journal ); + return true; + } else { + kdWarning() << "CalendarLocal::deleteJournal(): Journal not found." << endl; + return false; + } +} + +void CalendarLocal::deleteAllJournals() +{ + Journal::List::ConstIterator it; + for( it = mJournalList.begin(); it != mJournalList.end(); ++it ) { + notifyIncidenceDeleted( *it ); + } + + mJournalList.setAutoDelete( true ); + mJournalList.clearAll(); + mJournalList.setAutoDelete( false ); +} + +Journal *CalendarLocal::journal( const QString &uid ) +{ + Journal::List::ConstIterator it; + for ( it = mJournalList.begin(); it != mJournalList.end(); ++it ) + if ( (*it)->uid() == uid ) + return *it; + + return 0; +} + +Journal::List CalendarLocal::rawJournals( JournalSortField sortField, SortDirection sortDirection ) +{ + return sortJournals( &mJournalList, sortField, sortDirection ); +} + +Journal::List CalendarLocal::rawJournalsForDate( const QDate &date ) +{ + Journal::List journals; + + Journal::List::ConstIterator it; + for ( it = mJournalList.begin(); it != mJournalList.end(); ++it ) { + Journal *journal = *it; + if ( journal->dtStart().date() == date ) { + journals.append( journal ); + } + } + + return journals; +} + +void CalendarLocal::setTimeZoneIdViewOnly( const QString& tz ) +{ + const QString question( i18n("The timezone setting was changed. In order to display the calendar " + "you are looking at in the new timezone, it needs to be saved. Do you want to save the pending " + "changes or rather wait and apply the new timezone on the next reload?" ) ); + int rc = KMessageBox::Yes; + if ( isModified() ) { + rc = KMessageBox::questionYesNo( 0, question, + i18n("Save before applying timezones?"), + KStdGuiItem::save(), + KGuiItem(i18n("Apply Timezone Change on Next Reload")), + "calendarLocalSaveBeforeTimezoneShift"); + } + if ( rc == KMessageBox::Yes ) { + reload( tz ); + } +} |