39 #include "icaltimezones.h"
46 #include <icaltimezone.h>
51 using namespace KCalCore;
58 class KCalCore::Calendar::Private
64 mNewObserver( false ),
65 mObserversEnabled( true ),
67 batchAddingInProgress( false )
70 mFilter = mDefaultFilter;
71 mFilter->setEnabled(
false );
74 mOwner->setName(
"Unknown Name" );
75 mOwner->setEmail(
"unknown@nowhere" );
82 if ( mFilter != mDefaultFilter ) {
85 delete mDefaultFilter;
87 KDateTime::Spec timeZoneIdSpec(
const QString &timeZoneId,
bool view );
94 KDateTime::Spec mTimeSpec;
95 mutable KDateTime::Spec mViewTimeSpec;
98 bool mObserversEnabled;
99 QList<CalendarObserver*> mObservers;
105 QMultiHash<QString, Incidence::Ptr> mOrphans;
106 QMultiHash<QString, Incidence::Ptr> mOrphanUids;
109 QMultiHash<QString, Incidence::Ptr >mNotebookIncidences;
110 QHash<QString, QString>mUidToNotebook;
111 QHash<QString, bool>mNotebooks;
112 QHash<Incidence::Ptr, bool>mIncidenceVisibility;
113 QString mDefaultNotebook;
114 QMap<QString, Incidence::List > mIncidenceRelations;
115 bool batchAddingInProgress;
122 template <
typename K,
typename V>
123 QVector<V> values(
const QMultiHash<K,V> &c )
126 v.reserve( c.size() );
127 for (
typename QMultiHash<K,V>::const_iterator it = c.begin(), end = c.end(); it != end; ++it ) {
128 v.push_back( it.value() );
133 template <
typename K,
typename V>
134 QVector<V> values(
const QMultiHash<K,V> &c,
const K &x )
137 typename QMultiHash<K,V>::const_iterator it = c.find( x );
138 while ( it != c.end() && it.key() == x ) {
139 v.push_back( it.value() );
150 class AddVisitor :
public Visitor
153 AddVisitor( T *r ) : mResource( r ) {}
157 return mResource->addEvent( e );
161 return mResource->addTodo( t );
165 return mResource->addJournal( j );
182 class DeleteVisitor :
public Visitor
185 DeleteVisitor( T *r ) : mResource( r ) {}
189 mResource->deleteEvent( e );
194 mResource->deleteTodo( t );
199 mResource->deleteJournal( j );
213 : d( new KCalCore::
Calendar::Private )
220 : d( new KCalCore::
Calendar::Private )
258 d->mTimeSpec = d->timeZoneIdSpec( timeZoneId,
false );
259 d->mViewTimeSpec = d->mTimeSpec;
260 d->mBuiltInViewTimeZone = d->mBuiltInTimeZone;
266 KDateTime::Spec Calendar::Private::timeZoneIdSpec(
const QString &timeZoneId,
274 if ( timeZoneId == QLatin1String(
"UTC" ) ) {
275 return KDateTime::UTC;
278 if ( !tz.isValid() ) {
280 tz = tzsrc.
parse( icaltimezone_get_builtin_timezone( timeZoneId.toLatin1() ) );
282 mBuiltInViewTimeZone = tz;
284 mBuiltInTimeZone = tz;
287 if ( tz.isValid() ) {
290 return KDateTime::ClockTime;
297 KTimeZone tz = d->mTimeSpec.timeZone();
298 return tz.isValid() ? tz.name() : QString();
309 d->mViewTimeSpec = d->timeZoneIdSpec( timeZoneId,
true );
314 return d->mViewTimeSpec;
319 KTimeZone tz = d->mViewTimeSpec.timeZone();
320 return tz.isValid() ? tz.name() : QString();
325 return d->mTimeZones;
334 if ( d->mTimeZones && ( d->mTimeZones != zones ) ) {
335 delete d->mTimeZones;
338 d->mTimeZones = zones;
347 for ( i = 0, end = ev.count(); i < end; ++i ) {
348 ev[i]->shiftTimes( oldSpec, newSpec );
352 for ( i = 0, end = to.count(); i < end; ++i ) {
353 to[i]->shiftTimes( oldSpec, newSpec );
357 for ( i = 0, end = jo.count(); i < end; ++i ) {
358 jo[i]->shiftTimes( oldSpec, newSpec );
367 d->mFilter = d->mDefaultFilter;
379 QStringList cats, thisCats;
382 for ( Incidence::List::ConstIterator i = rawInc.constBegin();
383 i != rawInc.constEnd(); ++i ) {
384 thisCats = ( *i )->categories();
385 for ( QStringList::ConstIterator si = thisCats.constBegin();
386 si != thisCats.constEnd(); ++si ) {
387 if ( !cats.contains( *si ) ) {
435 Incidence::List::const_iterator it;
436 for ( it = vals.constBegin(); it != vals.constEnd(); ++it ) {
437 if ( ( ( incidence->dtStart() == ( *it )->dtStart() ) ||
438 ( !incidence->dtStart().isValid() && !( *it )->dtStart().isValid() ) ) &&
439 ( incidence->summary() == ( *it )->summary() ) ) {
451 if ( d->mNotebooks.contains( notebook ) ) {
454 d->mNotebooks.insert( notebook, isVisible );
461 if ( !d->mNotebooks.contains( notebook ) ) {
464 d->mNotebooks.insert( notebook, isVisible );
471 if ( !d->mNotebooks.contains( notebook ) ) {
474 return d->mNotebooks.remove( notebook );
480 if ( !d->mNotebooks.contains( notebook ) ) {
490 return d->mDefaultNotebook;
495 return d->mNotebooks.contains( notebook );
500 if ( d->mIncidenceVisibility.contains( incidence ) ) {
501 return d->mIncidenceVisibility[
incidence];
503 const QString nuid =
notebook( incidence );
505 if ( d->mNotebooks.contains( nuid ) ) {
506 rv = d->mNotebooks.value( nuid );
517 d->mNotebookIncidences.clear();
518 d->mUidToNotebook.clear();
519 d->mIncidenceVisibility.clear();
528 if ( !notebook.isEmpty() &&
529 !
incidence( inc->uid(), inc->recurrenceId() ) ) {
530 kWarning() <<
"cannot set notebook until incidence has been added";
534 if ( d->mUidToNotebook.contains( inc->uid() ) ) {
535 QString old = d->mUidToNotebook.value( inc->uid() );
536 if ( !old.isEmpty() && notebook != old ) {
537 if ( inc->hasRecurrenceId() ) {
538 kWarning() <<
"cannot set notebook for child incidences";
543 Incidence::List::Iterator it;
544 for ( it = list.begin(); it != list.end(); ++it ) {
545 d->mNotebookIncidences.remove( old, *it );
546 d->mNotebookIncidences.insert( notebook, *it );
550 d->mNotebookIncidences.remove( old, inc );
553 if ( !notebook.isEmpty() ) {
554 d->mUidToNotebook.insert( inc->uid(),
notebook );
555 d->mNotebookIncidences.insert( notebook, inc );
556 kDebug() <<
"setting notebook" << notebook <<
"for" << inc->uid();
566 return d->mUidToNotebook.value( incidence->uid() );
574 return d->mUidToNotebook.value( uid );
579 return d->mNotebookIncidences.uniqueKeys();
584 if ( notebook.isEmpty() ) {
585 return values( d->mNotebookIncidences );
587 return values( d->mNotebookIncidences, notebook );
597 if ( eventList.isEmpty() ) {
605 eventListSorted = eventList;
606 switch ( sortField ) {
612 qSort( eventListSorted.begin(), eventListSorted.end(), Events::startDateLessThan );
614 qSort( eventListSorted.begin(), eventListSorted.end(), Events::startDateMoreThan );
620 qSort( eventListSorted.begin(), eventListSorted.end(), Events::endDateLessThan );
622 qSort( eventListSorted.begin(), eventListSorted.end(), Events::endDateMoreThan );
628 qSort( eventListSorted.begin(), eventListSorted.end(), Events::summaryLessThan );
630 qSort( eventListSorted.begin(), eventListSorted.end(), Events::summaryMoreThan );
635 return eventListSorted;
640 const KDateTime::Spec &timeSpec,
645 d->mFilter->apply( &el );
652 d->mFilter->apply( &el );
657 const KDateTime::Spec &timeSpec,
658 bool inclusive )
const
661 d->mFilter->apply( &el );
669 d->mFilter->apply( &el );
679 AddVisitor<Calendar> v(
this );
680 return incidence->accept( v, incidence );
690 DeleteVisitor<Calendar> v(
this );
691 const bool result = incidence->accept( v, incidence );
704 const KDateTime::Spec &spec,
707 if ( !incidence || !incidence->recurs() ) {
726 if ( duration > 0 ) {
727 int doneduration = recur->
durationTo( date.addDays( -1 ) );
728 if ( doneduration >= duration ) {
729 kDebug() <<
"The dissociated event already occurred more often"
730 <<
"than it was supposed to ever occur. ERROR!";
740 KDateTime start( ev->dtStart() );
741 int daysTo = start.toTimeSpec( spec ).date().daysTo( date );
743 ev->setDtEnd( ev->dtEnd().addDays( daysTo ) );
746 bool haveOffset =
false;
748 if ( td->hasDueDate() ) {
749 KDateTime due( td->dtDue() );
750 daysTo = due.toTimeSpec( spec ).date().daysTo( date );
751 td->
setDtDue( due.addDays( daysTo ), true );
754 if ( td->hasStartDate() ) {
755 KDateTime start( td->dtStart() );
757 daysTo = start.toTimeSpec( spec ).date().daysTo( date );
759 td->setDtStart( start.addDays( daysTo ) );
763 recur = incidence->recurrence();
766 recur->addExDate( date );
777 const KDateTime &recurrenceId )
const
784 i =
todo( uid, recurrenceId );
789 i =
journal( uid, recurrenceId );
813 Incidence::List::const_iterator it = incidences.begin();
814 for ( ; it != incidences.end(); ++it ) {
815 if ( (*it)->schedulingID() == sid ) {
816 result.append( *it );
825 Incidence::List::const_iterator it = incidences.begin();
826 for ( ; it != incidences.end(); ++it ) {
827 if ( (*it)->schedulingID() == uid ) {
841 if ( todoList.isEmpty() ) {
852 todoListSorted = todoList;
853 switch ( sortField ) {
859 qSort( todoListSorted.begin(), todoListSorted.end(), Todos::startDateLessThan );
861 qSort( todoListSorted.begin(), todoListSorted.end(), Todos::startDateMoreThan );
867 qSort( todoListSorted.begin(), todoListSorted.end(), Todos::dueDateLessThan );
869 qSort( todoListSorted.begin(), todoListSorted.end(), Todos::dueDateMoreThan );
875 qSort( todoListSorted.begin(), todoListSorted.end(), Todos::priorityLessThan );
877 qSort( todoListSorted.begin(), todoListSorted.end(), Todos::priorityMoreThan );
883 qSort( todoListSorted.begin(), todoListSorted.end(), Todos::percentLessThan );
885 qSort( todoListSorted.begin(), todoListSorted.end(), Todos::percentMoreThan );
891 qSort( todoListSorted.begin(), todoListSorted.end(), Todos::summaryLessThan );
893 qSort( todoListSorted.begin(), todoListSorted.end(), Todos::summaryMoreThan );
899 qSort( todoListSorted.begin(), todoListSorted.end(), Todos::createdLessThan );
901 qSort( todoListSorted.begin(), todoListSorted.end(), Todos::createdMoreThan );
906 return todoListSorted;
913 d->mFilter->apply( &tl );
920 d->mFilter->apply( &el );
925 const KDateTime::Spec ×pec,
bool inclusive )
const
928 d->mFilter->apply( &tl );
937 if ( journalList.isEmpty() ) {
943 switch ( sortField ) {
949 qSort( journalListSorted.begin(), journalListSorted.end(), Journals::dateLessThan );
951 qSort( journalListSorted.begin(), journalListSorted.end(), Journals::dateMoreThan );
957 qSort( journalListSorted.begin(), journalListSorted.end(), Journals::summaryLessThan );
959 qSort( journalListSorted.begin(), journalListSorted.end(), Journals::summaryMoreThan );
964 return journalListSorted;
971 d->mFilter->apply( &jl );
978 d->mFilter->apply( &el );
986 if ( !forincidence ) {
990 const QString uid = forincidence->uid();
994 d->mOrphans.remove( uid );
995 for (
int i = 0, end = l.count(); i < end; ++i ) {
996 d->mIncidenceRelations[uid].append( l[i] );
997 d->mOrphanUids.remove( l[i]->uid() );
1001 if ( forincidence->relatedTo().isEmpty() && !forincidence->relatedTo().isEmpty() ) {
1010 forincidence->setRelatedTo( QString() );
1011 kWarning() <<
"hierarchy loop beetween " << forincidence->uid() <<
" and " << parent->uid();
1013 d->mIncidenceRelations[parent->uid()].append( forincidence );
1020 d->mOrphans.insert( forincidence->relatedTo(), forincidence );
1021 d->mOrphanUids.insert( forincidence->uid(), forincidence );
1030 kDebug() <<
"Warning: incidence is 0";
1034 const QString uid = incidence->uid();
1037 if ( !d->mOrphanUids.contains( i->uid() ) ) {
1038 d->mOrphans.insert( uid, i );
1039 d->mOrphanUids.insert( i->uid(), i );
1040 i->setRelatedTo( uid );
1044 const QString parentUid = incidence->relatedTo();
1047 if ( !parentUid.isEmpty() ) {
1048 d->mIncidenceRelations[parentUid].erase(
1049 std::remove( d->mIncidenceRelations[parentUid].begin(),
1050 d->mIncidenceRelations[parentUid].end(),
incidence ),
1051 d->mIncidenceRelations[parentUid].end() );
1055 if ( d->mOrphanUids.remove( uid ) ) {
1065 QStringList relatedToUids;
1069 relatedToUids << incidence->relatedTo();
1070 for ( QMultiHash<QString, Incidence::Ptr>::Iterator it = d->mOrphans.begin();
1071 it != d->mOrphans.end(); ++it ) {
1072 if ( it.value()->uid() == uid ) {
1073 relatedToUids << it.key();
1078 for ( QStringList::const_iterator uidit = relatedToUids.constBegin();
1079 uidit != relatedToUids.constEnd(); ++uidit ) {
1082 QList<Incidence::Ptr> l = d->mOrphans.values( *uidit );
1083 d->mOrphans.remove( *uidit );
1085 if ( i != incidence ) {
1086 tempList.append( i );
1090 for ( Incidence::List::Iterator incit = tempList.begin();
1091 incit != tempList.end(); ++incit ) {
1092 d->mOrphans.insert( *uidit, *incit );
1113 if ( !incidence || incidence->relatedTo().isEmpty() ) {
1115 }
else if ( incidence->relatedTo() == ancestor->uid() ) {
1124 return d->mIncidenceRelations[uid];
1133 Q_UNUSED( modified );
1134 Q_UNUSED( calendar );
1139 Q_UNUSED( incidence );
1144 Q_UNUSED( incidence );
1149 Q_UNUSED( incidence );
1155 Q_UNUSED( incidence );
1164 if ( !d->mObservers.contains( observer ) ) {
1165 d->mObservers.append( observer );
1167 d->mNewObserver =
true;
1176 d->mObservers.removeAll( observer );
1187 if ( modified != d->mModified || d->mNewObserver ) {
1188 d->mNewObserver =
false;
1192 d->mModified = modified;
1198 return d->mModified;
1220 inc->setLastModified( KDateTime::currentUtcDateTime() );
1232 Q_UNUSED( timeSpec );
1241 if ( !d->mObserversEnabled ) {
1256 if ( !d->mObserversEnabled ) {
1271 if ( !d->mObserversEnabled ) {
1286 if ( !d->mObserversEnabled ) {
1307 return d->mProductId;
1318 for ( i = 0, end = events.count(); i < end; ++i ) {
1319 incidences.append( events[i] );
1322 for ( i = 0, end = todos.count(); i < end; ++i ) {
1323 incidences.append( todos[i] );
1326 for ( i = 0, end = journals.count(); i < end; ++i ) {
1327 incidences.append( journals[i] );
1335 Q_UNUSED( incidence );
1341 Q_UNUSED( incidence );
1347 d->mObserversEnabled = enabled;
1351 const KDateTime &from,
const KDateTime &to )
const
1353 KDateTime preTime = from.addSecs( -1 );
1356 for (
int i = 0, iend = alarmlist.count(); i < iend; ++i ) {
1357 if ( alarmlist[i]->enabled() ) {
1358 KDateTime dt = alarmlist[i]->nextRepetition( preTime );
1359 if ( dt.isValid() && dt <= to ) {
1360 kDebug() << incidence->summary() <<
"':" << dt.toString();
1361 alarms.append( alarmlist[i] );
1369 const KDateTime &from,
1370 const KDateTime &to )
const
1373 bool endOffsetValid =
false;
1378 for (
int i = 0, iend = alarmlist.count(); i < iend; ++i ) {
1380 if ( a->enabled() ) {
1381 if ( a->hasTime() ) {
1383 dt = a->nextRepetition( from.addSecs( -1 ) );
1384 if ( !dt.isValid() || dt > to ) {
1392 if ( a->hasStartOffset() ) {
1393 offset = a->startOffset();
1394 }
else if ( a->hasEndOffset() ) {
1395 offset = a->endOffset();
1396 if ( !endOffsetValid ) {
1397 endOffset =
Duration( incidence->dtStart(),
1399 endOffsetValid =
true;
1404 KDateTime alarmStart =
1406 incidence->dtStart() );
1408 if ( alarmStart > to ) {
1411 KDateTime baseStart = incidence->dtStart();
1412 if ( from > alarmStart ) {
1414 baseStart = ( -offset ).end( ( -endOffset ).end( alarmStart ) );
1419 dt = incidence->recurrence()->getNextDateTime( baseStart.addSecs( -1 ) );
1420 if ( !dt.isValid() ||
1421 ( dt = endOffset.
end( offset.
end( dt ) ) ) > to )
1424 if ( !a->repeatCount() ) {
1431 Duration alarmDuration = a->duration();
1432 for ( KDateTime base = baseStart;
1433 ( dt = incidence->recurrence()->getPreviousDateTime( base ) ).isValid();
1435 if ( a->duration().end( dt ) < base ) {
1441 int snooze = a->snoozeTime().
value();
1442 if ( a->snoozeTime().isDaily() ) {
1443 Duration toFromDuration( dt, base );
1444 int toFrom = toFromDuration.
asDays();
1445 if ( a->snoozeTime().end( from ) <= to ||
1446 ( toFromDuration.
isDaily() && toFrom % snooze == 0 ) ||
1447 ( toFrom / snooze + 1 ) * snooze <= toFrom + period.
asDays() ) {
1451 dt = offset.
end( dt ).addDays( ( ( toFrom - 1 ) / snooze + 1 ) * snooze );
1456 int toFrom = dt.secsTo( base );
1458 toFrom % snooze == 0 ||
1459 ( toFrom / snooze + 1 ) * snooze <= toFrom + period.
asSeconds() )
1464 dt = offset.
end( dt ).addSecs( ( ( toFrom - 1 ) / snooze + 1 ) * snooze );
1475 kDebug() << incidence->summary() <<
"':" << dt.toString();
1483 d->batchAddingInProgress =
true;
1488 d->batchAddingInProgress =
false;
1493 return d->batchAddingInProgress;