2 This file is part of libkcal.
4 Copyright (c) 1998 Preston Brown
5 Copyright (c) 2000-2004 Cornelius Schumacher <schumacher@kde.org>
6 Copyright (C) 2003-2004 Reinhold Kainhofer <reinhold@kainhofer.com>
8 This library is free software; you can redistribute it and/or
9 modify it under the terms of the GNU Library General Public
10 License as published by the Free Software Foundation; either
11 version 2 of the License, or (at your option) any later version.
13 This library is distributed in the hope that it will be useful,
14 but WITHOUT ANY WARRANTY; without even the implied warranty of
15 MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
16 Library General Public License for more details.
18 You should have received a copy of the GNU Library General Public License
19 along with this library; see the file COPYING.LIB. If not, write to
20 the Free Software Foundation, Inc., 59 Temple Place - Suite 330,
21 Boston, MA 02111-1307, USA.
29 #include "exceptions.h"
30 #include "calfilter.h"
38 mTimeZoneId
= QString::fromLatin1( "UTC" );
44 Calendar::Calendar( const QString
&timeZoneId
)
46 mTimeZoneId
= timeZoneId
;
55 mObserversEnabled
= true;
59 // Setup default filter, which does nothing
60 mDefaultFilter
= new CalFilter
;
61 mFilter
= mDefaultFilter
;
62 mFilter
->setEnabled(false);
64 // initialize random numbers. This is a hack, and not
65 // even that good of one at that.
68 // user information...
69 setOwner( Person( i18n("Unknown Name"), i18n("unknown@nowhere") ) );
72 tmpStr
= KOPrefs::instance()->mTimeZone
;
73 // kdDebug(5800) << "Calendar::Calendar(): TimeZone: " << tmpStr << endl;
74 int dstSetting
= KOPrefs::instance()->mDaylightSavings
;
75 extern long int timezone
;
79 now
= localtime(&curtime
);
80 int hourOff
= - ((timezone
/ 60) / 60);
84 tzStr
.sprintf("%.2d%.2d",
86 abs((timezone
/ 60) % 60));
88 // if no time zone was in the config file, write what we just discovered.
89 if (tmpStr
.isEmpty()) {
90 // KOPrefs::instance()->mTimeZone = tzStr;
95 // if daylight savings has changed since last load time, we need
96 // to rewrite these settings to the config file.
97 if ((now
->tm_isdst
&& !dstSetting
) ||
98 (!now
->tm_isdst
&& dstSetting
)) {
99 KOPrefs::instance()->mTimeZone
= tzStr
;
100 KOPrefs::instance()->mDaylightSavings
= now
->tm_isdst
;
106 // KOPrefs::instance()->writeConfig();
109 Calendar::~Calendar()
111 delete mDefaultFilter
;
114 const Person
&Calendar::getOwner() const
119 void Calendar::setOwner( const Person
&owner
)
126 void Calendar::setTimeZoneId(const QString
&id
)
132 doSetTimeZoneId( id
);
135 QString
Calendar::timeZoneId() const
140 void Calendar::setLocalTime()
149 bool Calendar::isLocalTime() const
154 void Calendar::setFilter(CalFilter
*filter
)
159 mFilter
= mDefaultFilter
;
163 CalFilter
*Calendar::filter()
168 QStringList
Calendar::incidenceCategories()
170 Incidence::List
rawInc( rawIncidences() );
171 QStringList categories
, thisCats
;
172 // TODO: For now just iterate over all incidences. In the future,
173 // the list of categories should be built when reading the file.
174 for ( Incidence::List::ConstIterator i
= rawInc
.constBegin(); i
!= rawInc
.constEnd(); ++i
) {
175 thisCats
= (*i
)->categories();
176 for ( QStringList::ConstIterator si
= thisCats
.constBegin(); si
!= thisCats
.constEnd(); ++si
) {
177 if ( categories
.find( *si
) == categories
.end() ) {
178 categories
.append( *si
);
185 Incidence::List
Calendar::incidences( const QDate
&qdt
)
187 return mergeIncidenceList( events( qdt
), todos( qdt
), journals( qdt
) );
190 Incidence::List
Calendar::incidences()
192 return mergeIncidenceList( events(), todos(), journals() );
195 Incidence::List
Calendar::rawIncidences()
197 return mergeIncidenceList( rawEvents(), rawTodos(), rawJournals() );
200 Event::List
Calendar::sortEvents( Event::List
*eventList
,
201 EventSortField sortField
,
202 SortDirection sortDirection
) {
204 Event::List eventListSorted
;
205 Event::List tempList
, t
;
206 Event::List alphaList
;
207 Event::List::Iterator sortIt
;
208 Event::List::Iterator eit
;
210 // Notice we alphabetically presort Summaries first.
211 // We do this so comparison "ties" stay in a nice order.
213 switch( sortField
) {
214 case EventSortUnsorted
:
215 eventListSorted
= *eventList
;
218 case EventSortStartDate
:
219 alphaList
= sortEvents( eventList
, EventSortSummary
, sortDirection
);
220 for ( eit
= alphaList
.begin(); eit
!= alphaList
.end(); ++eit
) {
221 sortIt
= eventListSorted
.begin();
222 if ( sortDirection
== SortDirectionAscending
) {
223 while ( sortIt
!= eventListSorted
.end() &&
224 (*eit
)->dtStart() >= (*sortIt
)->dtStart() ) {
228 while ( sortIt
!= eventListSorted
.end() &&
229 (*eit
)->dtStart() < (*sortIt
)->dtStart() ) {
233 eventListSorted
.insert( sortIt
, *eit
);
237 case EventSortEndDate
:
238 alphaList
= sortEvents( eventList
, EventSortSummary
, sortDirection
);
239 for ( eit
= alphaList
.begin(); eit
!= alphaList
.end(); ++eit
) {
240 if ( (*eit
)->hasEndDate() ) {
241 sortIt
= eventListSorted
.begin();
242 if ( sortDirection
== SortDirectionAscending
) {
243 while ( sortIt
!= eventListSorted
.end() &&
244 (*eit
)->dtEnd() >= (*sortIt
)->dtEnd() ) {
248 while ( sortIt
!= eventListSorted
.end() &&
249 (*eit
)->dtEnd() < (*sortIt
)->dtEnd() ) {
254 // Keep a list of the Events without End DateTimes
255 tempList
.append( *eit
);
257 eventListSorted
.insert( sortIt
, *eit
);
259 if ( sortDirection
== SortDirectionAscending
) {
260 // Append the list of Events without End DateTimes
261 eventListSorted
+= tempList
;
263 // Prepend the list of Events without End DateTimes
264 tempList
+= eventListSorted
;
265 eventListSorted
= tempList
;
269 case EventSortSummary
:
270 for ( eit
= eventList
->begin(); eit
!= eventList
->end(); ++eit
) {
271 sortIt
= eventListSorted
.begin();
272 if ( sortDirection
== SortDirectionAscending
) {
273 while ( sortIt
!= eventListSorted
.end() &&
274 (*eit
)->summary() >= (*sortIt
)->summary() ) {
278 while ( sortIt
!= eventListSorted
.end() &&
279 (*eit
)->summary() < (*sortIt
)->summary() ) {
283 eventListSorted
.insert( sortIt
, *eit
);
288 return eventListSorted
;
292 Event::List
Calendar::events( const QDate
&date
, bool sorted
)
294 Event::List el
= rawEventsForDate( date
, sorted
);
299 Event::List
Calendar::events( const QDateTime
&qdt
)
301 Event::List el
= rawEventsForDate(qdt
);
306 Event::List
Calendar::events( const QDate
&start
, const QDate
&end
,
309 Event::List el
= rawEvents(start
,end
,inclusive
);
314 Event::List
Calendar::events( EventSortField sortField
, SortDirection sortDirection
)
316 Event::List el
= rawEvents( sortField
, sortDirection
);
321 bool Calendar::addIncidence(Incidence
*i
)
323 Incidence::AddVisitor
<Calendar
> v(this);
328 bool Calendar::deleteIncidence( Incidence
*i
)
330 if ( beginChange( i
) ) {
331 Incidence::DeleteVisitor
<Calendar
> v( this );
332 bool result
= i
->accept( v
);
339 Incidence
*Calendar::dissociateOccurrence( Incidence
*incidence
, QDate date
,
342 if ( !incidence
|| !incidence
->doesRecur() ) return 0;
344 Incidence
*newInc
= incidence
->clone();
346 newInc
->setRelatedTo( incidence
);
347 Recurrence
*recur
= newInc
->recurrence();
349 recur
->unsetRecurs();
351 // Adjust the recurrence for the future incidences. In particular
352 // adjust the "end after n occurences" rules! "No end date" and "end by ..."
353 // don't need to be modified.
354 int duration
= recur
->duration();
355 if ( duration
> 0 ) {
356 int doneduration
= recur
->durationTo( date
.addDays(-1) );
357 if ( doneduration
>= duration
) {
358 kdDebug(5850) << "The dissociated event already occured more often that it was supposed to ever occur. ERROR!" << endl
;
359 recur
->unsetRecurs();
361 recur
->setDuration( duration
- doneduration
);
365 // Adjust the date of the incidence
366 if ( incidence
->type() == "Event" ) {
367 Event
*ev
= static_cast<Event
*>( newInc
);
368 QDateTime
start( ev
->dtStart() );
369 int daysTo
= start
.date().daysTo( date
);
370 ev
->setDtStart( start
.addDays( daysTo
) );
371 ev
->setDtEnd( ev
->dtEnd().addDays( daysTo
) );
372 } else if ( incidence
->type() == "Todo" ) {
373 Todo
*td
= static_cast<Todo
*>( newInc
);
374 bool haveOffset
= false;
376 if ( td
->hasDueDate() ) {
377 QDateTime
due( td
->dtDue() );
378 daysTo
= due
.date().daysTo( date
) ;
379 td
->setDtDue( due
.addDays( daysTo
), true );
382 if ( td
->hasStartDate() ) {
383 QDateTime
start( td
->dtStart() );
384 if ( !haveOffset
) daysTo
= start
.date().daysTo( date
);
385 td
->setDtStart( start
.addDays( daysTo
) );
389 if ( addIncidence( newInc
) ) {
391 incidence
->addExDate( date
);
393 recur
= incidence
->recurrence();
395 // Make sure the recurrence of the past events ends at the corresponding day
396 recur
->setEndDate( date
.addDays(-1) );
406 Incidence
*Calendar::incidence( const QString
& uid
)
408 Incidence
*i
= event( uid
);
416 Incidence
*Calendar::incidenceFromSchedulingID( const QString
&UID
)
418 Incidence::List incidences
= rawIncidences();
419 Incidence::List::iterator it
= incidences
.begin();
420 for ( ; it
!= incidences
.end(); ++it
)
421 if ( (*it
)->schedulingID() == UID
)
422 // Touchdown, and the crowd goes wild
428 Todo::List
Calendar::sortTodos( Todo::List
*todoList
,
429 TodoSortField sortField
,
430 SortDirection sortDirection
)
432 Todo::List todoListSorted
;
433 Todo::List tempList
, t
;
434 Todo::List alphaList
;
435 Todo::List::Iterator sortIt
;
436 Todo::List::Iterator eit
;
438 // Notice we alphabetically presort Summaries first.
439 // We do this so comparison "ties" stay in a nice order.
441 // Note that Todos may not have Start DateTimes nor due DateTimes.
443 switch( sortField
) {
444 case TodoSortUnsorted
:
445 todoListSorted
= *todoList
;
448 case TodoSortStartDate
:
449 alphaList
= sortTodos( todoList
, TodoSortSummary
, sortDirection
);
450 for ( eit
= alphaList
.begin(); eit
!= alphaList
.end(); ++eit
) {
451 if ( (*eit
)->hasStartDate() ) {
452 sortIt
= todoListSorted
.begin();
453 if ( sortDirection
== SortDirectionAscending
) {
454 while ( sortIt
!= todoListSorted
.end() &&
455 (*eit
)->dtStart() >= (*sortIt
)->dtStart() ) {
459 while ( sortIt
!= todoListSorted
.end() &&
460 (*eit
)->dtStart() < (*sortIt
)->dtStart() ) {
464 todoListSorted
.insert( sortIt
, *eit
);
466 // Keep a list of the Todos without Start DateTimes
467 tempList
.append( *eit
);
470 if ( sortDirection
== SortDirectionAscending
) {
471 // Append the list of Todos without Start DateTimes
472 todoListSorted
+= tempList
;
474 // Prepend the list of Todos without Start DateTimes
475 tempList
+= todoListSorted
;
476 todoListSorted
= tempList
;
480 case TodoSortDueDate
:
481 alphaList
= sortTodos( todoList
, TodoSortSummary
, sortDirection
);
482 for ( eit
= alphaList
.begin(); eit
!= alphaList
.end(); ++eit
) {
483 if ( (*eit
)->hasDueDate() ) {
484 sortIt
= todoListSorted
.begin();
485 if ( sortDirection
== SortDirectionAscending
) {
486 while ( sortIt
!= todoListSorted
.end() &&
487 (*eit
)->dtDue() >= (*sortIt
)->dtDue() ) {
491 while ( sortIt
!= todoListSorted
.end() &&
492 (*eit
)->dtDue() < (*sortIt
)->dtDue() ) {
496 todoListSorted
.insert( sortIt
, *eit
);
498 // Keep a list of the Todos without Due DateTimes
499 tempList
.append( *eit
);
502 if ( sortDirection
== SortDirectionAscending
) {
503 // Append the list of Todos without Due DateTimes
504 todoListSorted
+= tempList
;
506 // Prepend the list of Todos without Due DateTimes
507 tempList
+= todoListSorted
;
508 todoListSorted
= tempList
;
512 case TodoSortPriority
:
513 alphaList
= sortTodos( todoList
, TodoSortSummary
, sortDirection
);
514 for ( eit
= alphaList
.begin(); eit
!= alphaList
.end(); ++eit
) {
515 sortIt
= todoListSorted
.begin();
516 if ( sortDirection
== SortDirectionAscending
) {
517 while ( sortIt
!= todoListSorted
.end() &&
518 (*eit
)->priority() >= (*sortIt
)->priority() ) {
522 while ( sortIt
!= todoListSorted
.end() &&
523 (*eit
)->priority() < (*sortIt
)->priority() ) {
527 todoListSorted
.insert( sortIt
, *eit
);
531 case TodoSortPercentComplete
:
532 alphaList
= sortTodos( todoList
, TodoSortSummary
, sortDirection
);
533 for ( eit
= alphaList
.begin(); eit
!= alphaList
.end(); ++eit
) {
534 sortIt
= todoListSorted
.begin();
535 if ( sortDirection
== SortDirectionAscending
) {
536 while ( sortIt
!= todoListSorted
.end() &&
537 (*eit
)->percentComplete() >= (*sortIt
)->percentComplete() ) {
541 while ( sortIt
!= todoListSorted
.end() &&
542 (*eit
)->percentComplete() < (*sortIt
)->percentComplete() ) {
546 todoListSorted
.insert( sortIt
, *eit
);
550 case TodoSortSummary
:
551 for ( eit
= todoList
->begin(); eit
!= todoList
->end(); ++eit
) {
552 sortIt
= todoListSorted
.begin();
553 if ( sortDirection
== SortDirectionAscending
) {
554 while ( sortIt
!= todoListSorted
.end() &&
555 (*eit
)->summary() >= (*sortIt
)->summary() ) {
559 while ( sortIt
!= todoListSorted
.end() &&
560 (*eit
)->summary() < (*sortIt
)->summary() ) {
564 todoListSorted
.insert( sortIt
, *eit
);
569 return todoListSorted
;
572 Todo::List
Calendar::todos( TodoSortField sortField
, SortDirection sortDirection
)
574 Todo::List tl
= rawTodos( sortField
, sortDirection
);
575 mFilter
->apply( &tl
);
579 Todo::List
Calendar::todos( const QDate
&date
)
581 Todo::List el
= rawTodosForDate( date
);
586 Journal::List
Calendar::sortJournals( Journal::List
*journalList
,
587 JournalSortField sortField
,
588 SortDirection sortDirection
)
590 Journal::List journalListSorted
;
591 Journal::List::Iterator sortIt
;
592 Journal::List::Iterator eit
;
594 switch( sortField
) {
595 case JournalSortUnsorted
:
596 journalListSorted
= *journalList
;
599 case JournalSortDate
:
600 for ( eit
= journalList
->begin(); eit
!= journalList
->end(); ++eit
) {
601 sortIt
= journalListSorted
.begin();
602 if ( sortDirection
== SortDirectionAscending
) {
603 while ( sortIt
!= journalListSorted
.end() &&
604 (*eit
)->dtStart() >= (*sortIt
)->dtStart() ) {
608 while ( sortIt
!= journalListSorted
.end() &&
609 (*eit
)->dtStart() < (*sortIt
)->dtStart() ) {
613 journalListSorted
.insert( sortIt
, *eit
);
617 case JournalSortSummary
:
618 for ( eit
= journalList
->begin(); eit
!= journalList
->end(); ++eit
) {
619 sortIt
= journalListSorted
.begin();
620 if ( sortDirection
== SortDirectionAscending
) {
621 while ( sortIt
!= journalListSorted
.end() &&
622 (*eit
)->summary() >= (*sortIt
)->summary() ) {
626 while ( sortIt
!= journalListSorted
.end() &&
627 (*eit
)->summary() < (*sortIt
)->summary() ) {
631 journalListSorted
.insert( sortIt
, *eit
);
636 return journalListSorted
;
639 Journal::List
Calendar::journals( JournalSortField sortField
, SortDirection sortDirection
)
641 Journal::List jl
= rawJournals( sortField
, sortDirection
);
642 mFilter
->apply( &jl
);
646 Journal::List
Calendar::journals( const QDate
&date
)
648 Journal::List el
= rawJournalsForDate( date
);
653 // When this is called, the todo have already been added to the calendar.
654 // This method is only about linking related todos
655 void Calendar::setupRelations( Incidence
*forincidence
)
657 QString uid
= forincidence
->uid();
659 // First, go over the list of orphans and see if this is their parent
660 while( Incidence
* i
= mOrphans
[ uid
] ) {
661 mOrphans
.remove( uid
);
662 i
->setRelatedTo( forincidence
);
663 forincidence
->addRelation( i
);
664 mOrphanUids
.remove( i
->uid() );
667 // Now see about this incidences parent
668 if( !forincidence
->relatedTo() && !forincidence
->relatedToUid().isEmpty() ) {
669 // This incidence has a uid it is related to, but is not registered to it yet
671 Incidence
* parent
= incidence( forincidence
->relatedToUid() );
674 forincidence
->setRelatedTo( parent
);
675 parent
->addRelation( forincidence
);
677 // Not found, put this in the mOrphans list
678 mOrphans
.insert( forincidence
->relatedToUid(), forincidence
);
679 mOrphanUids
.insert( forincidence
->uid(), forincidence
);
684 // If a task with subtasks is deleted, move it's subtasks to the orphans list
685 void Calendar::removeRelations( Incidence
*incidence
)
688 kdDebug(5800) << "Warning: Calendar::removeRelations( 0 )!\n";
692 QString uid
= incidence
->uid();
694 Incidence::List relations
= incidence
->relations();
695 Incidence::List::ConstIterator it
;
696 for( it
= relations
.begin(); it
!= relations
.end(); ++it
) {
698 if( !mOrphanUids
.find( i
->uid() ) ) {
699 mOrphans
.insert( uid
, i
);
700 mOrphanUids
.insert( i
->uid(), i
);
701 i
->setRelatedTo( 0 );
702 i
->setRelatedToUid( uid
);
706 // If this incidence is related to something else, tell that about it
707 if( incidence
->relatedTo() )
708 incidence
->relatedTo()->removeRelation( incidence
);
710 // Remove this one from the orphans list
711 if( mOrphanUids
.remove( uid
) )
712 // This incidence is located in the orphans list - it should be removed
713 if( !( incidence
->relatedTo() != 0 && mOrphans
.remove( incidence
->relatedTo()->uid() ) ) ) {
714 // Removing wasn't that easy
715 for( QDictIterator
<Incidence
> it( mOrphans
); it
.current(); ++it
) {
716 if( it
.current()->uid() == uid
) {
717 mOrphans
.remove( it
.currentKey() );
724 void Calendar::registerObserver( Observer
*observer
)
726 if( !mObservers
.contains( observer
) ) mObservers
.append( observer
);
730 void Calendar::unregisterObserver( Observer
*observer
)
732 mObservers
.remove( observer
);
735 void Calendar::setModified( bool modified
)
737 if ( modified
!= mModified
|| mNewObserver
) {
738 mNewObserver
= false;
740 for( observer
= mObservers
.first(); observer
;
741 observer
= mObservers
.next() ) {
742 observer
->calendarModified( modified
, this );
744 mModified
= modified
;
748 void Calendar::incidenceUpdated( IncidenceBase
*incidence
)
750 // kdDebug(5800) << "CalendarResources::incidenceUpdated( IncidenceBase * ): Not yet implemented\n";
751 incidence
->setSyncStatus( Event::SYNCMOD
);
752 incidence
->setLastModified( QDateTime::currentDateTime() );
753 // we should probably update the revision number here,
754 // or internally in the Event itself when certain things change.
755 // need to verify with ical documentation.
757 // The static_cast is ok as the CalendarLocal only observes Incidence objects
758 notifyIncidenceChanged( static_cast<Incidence
*>( incidence
) );
763 void Calendar::notifyIncidenceAdded( Incidence
*i
)
765 if ( !mObserversEnabled
) return;
768 for( observer
= mObservers
.first(); observer
;
769 observer
= mObservers
.next() ) {
770 observer
->calendarIncidenceAdded( i
);
774 void Calendar::notifyIncidenceChanged( Incidence
*i
)
776 if ( !mObserversEnabled
) return;
779 for( observer
= mObservers
.first(); observer
;
780 observer
= mObservers
.next() ) {
781 observer
->calendarIncidenceChanged( i
);
785 void Calendar::notifyIncidenceDeleted( Incidence
*i
)
787 if ( !mObserversEnabled
) return;
790 for( observer
= mObservers
.first(); observer
;
791 observer
= mObservers
.next() ) {
792 observer
->calendarIncidenceDeleted( i
);
796 void Calendar::setLoadedProductId( const QString
&id
)
798 mLoadedProductId
= id
;
801 QString
Calendar::loadedProductId()
803 return mLoadedProductId
;
806 Incidence::List
Calendar::mergeIncidenceList( const Event::List
&e
,
808 const Journal::List
&j
)
810 Incidence::List incidences
;
812 Event::List::ConstIterator it1
;
813 for( it1
= e
.begin(); it1
!= e
.end(); ++it1
) incidences
.append( *it1
);
815 Todo::List::ConstIterator it2
;
816 for( it2
= t
.begin(); it2
!= t
.end(); ++it2
) incidences
.append( *it2
);
818 Journal::List::ConstIterator it3
;
819 for( it3
= j
.begin(); it3
!= j
.end(); ++it3
) incidences
.append( *it3
);
824 bool Calendar::beginChange( Incidence
* )
829 bool Calendar::endChange( Incidence
* )
834 void Calendar::setObserversEnabled( bool enabled
)
836 mObserversEnabled
= enabled
;
839 #include "calendar.moc"