|
|
|
/*
|
|
|
|
This file is part of libkcal.
|
|
|
|
|
|
|
|
Copyright (c) 1998 Preston Brown <pbrown@kde.org>
|
|
|
|
Copyright (c) 2000-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.
|
|
|
|
*/
|
|
|
|
/**
|
|
|
|
@file calendar.cpp
|
|
|
|
Provides the main "calendar" object class.
|
|
|
|
|
|
|
|
@author Preston Brown
|
|
|
|
@author Cornelius Schumacher
|
|
|
|
@author Reinhold Kainhofer
|
|
|
|
*/
|
|
|
|
#include <stdlib.h>
|
|
|
|
|
|
|
|
#include <kdebug.h>
|
|
|
|
#include <klocale.h>
|
|
|
|
|
|
|
|
#include "exceptions.h"
|
|
|
|
#include "calfilter.h"
|
|
|
|
|
|
|
|
#include "calendar.h"
|
|
|
|
|
|
|
|
using namespace KCal;
|
|
|
|
|
|
|
|
Calendar::Calendar( const TQString &timeZoneId )
|
|
|
|
{
|
|
|
|
mTimeZoneId = timeZoneId;
|
|
|
|
mLocalTime = false;
|
|
|
|
|
|
|
|
init();
|
|
|
|
}
|
|
|
|
|
|
|
|
void Calendar::init()
|
|
|
|
{
|
|
|
|
mException = 0;
|
|
|
|
mNewObserver = false;
|
|
|
|
mObserversEnabled = true;
|
|
|
|
|
|
|
|
mModified = false;
|
|
|
|
|
|
|
|
// Setup default filter, which does nothing
|
|
|
|
mDefaultFilter = new CalFilter;
|
|
|
|
mFilter = mDefaultFilter;
|
|
|
|
mFilter->setEnabled( false );
|
|
|
|
|
|
|
|
// user information...
|
|
|
|
setOwner( Person( i18n( "Unknown Name" ), i18n( "unknown@nowhere" ) ) );
|
|
|
|
}
|
|
|
|
|
|
|
|
Calendar::~Calendar()
|
|
|
|
{
|
|
|
|
clearException();
|
|
|
|
delete mDefaultFilter;
|
|
|
|
}
|
|
|
|
|
|
|
|
void Calendar::clearException()
|
|
|
|
{
|
|
|
|
delete mException;
|
|
|
|
mException = 0;
|
|
|
|
}
|
|
|
|
|
|
|
|
ErrorFormat *Calendar::exception() const
|
|
|
|
{
|
|
|
|
return mException;
|
|
|
|
}
|
|
|
|
|
|
|
|
void Calendar::setException( ErrorFormat *e )
|
|
|
|
{
|
|
|
|
delete mException;
|
|
|
|
mException = e;
|
|
|
|
}
|
|
|
|
|
|
|
|
const Person &Calendar::getOwner() const
|
|
|
|
{
|
|
|
|
return mOwner;
|
|
|
|
}
|
|
|
|
|
|
|
|
void Calendar::setOwner( const Person &owner )
|
|
|
|
{
|
|
|
|
mOwner = owner;
|
|
|
|
|
|
|
|
setModified( true );
|
|
|
|
}
|
|
|
|
|
|
|
|
void Calendar::setTimeZoneId( const TQString &timeZoneId )
|
|
|
|
{
|
|
|
|
mTimeZoneId = timeZoneId;
|
|
|
|
mLocalTime = false;
|
|
|
|
|
|
|
|
setModified( true );
|
|
|
|
doSetTimeZoneId( timeZoneId );
|
|
|
|
}
|
|
|
|
|
|
|
|
TQString Calendar::timeZoneId() const
|
|
|
|
{
|
|
|
|
return mTimeZoneId;
|
|
|
|
}
|
|
|
|
|
|
|
|
void Calendar::setLocalTime()
|
|
|
|
{
|
|
|
|
mLocalTime = true;
|
|
|
|
mTimeZoneId = "";
|
|
|
|
|
|
|
|
setModified( true );
|
|
|
|
}
|
|
|
|
|
|
|
|
bool Calendar::isLocalTime() const
|
|
|
|
{
|
|
|
|
return mLocalTime;
|
|
|
|
}
|
|
|
|
|
|
|
|
void Calendar::setFilter( CalFilter *filter )
|
|
|
|
{
|
|
|
|
if ( filter ) {
|
|
|
|
mFilter = filter;
|
|
|
|
} else {
|
|
|
|
mFilter = mDefaultFilter;
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
CalFilter *Calendar::filter()
|
|
|
|
{
|
|
|
|
return mFilter;
|
|
|
|
}
|
|
|
|
|
|
|
|
void Calendar::beginBatchAdding()
|
|
|
|
{
|
|
|
|
emit batchAddingBegins();
|
|
|
|
}
|
|
|
|
|
|
|
|
void Calendar::endBatchAdding()
|
|
|
|
{
|
|
|
|
emit batchAddingEnds();
|
|
|
|
}
|
|
|
|
|
|
|
|
TQStringList Calendar::categories()
|
|
|
|
{
|
|
|
|
Incidence::List rawInc( rawIncidences() );
|
|
|
|
TQStringList cats, thisCats;
|
|
|
|
// @TODO: For now just iterate over all incidences. In the future,
|
|
|
|
// the list of categories should be built when reading the file.
|
|
|
|
for ( Incidence::List::ConstIterator i = rawInc.constBegin();
|
|
|
|
i != rawInc.constEnd(); ++i ) {
|
|
|
|
thisCats = (*i)->categories();
|
|
|
|
for ( TQStringList::ConstIterator si = thisCats.constBegin();
|
|
|
|
si != thisCats.constEnd(); ++si ) {
|
|
|
|
if ( cats.find( *si ) == cats.end() ) {
|
|
|
|
cats.append( *si );
|
|
|
|
}
|
|
|
|
}
|
|
|
|
}
|
|
|
|
return cats;
|
|
|
|
}
|
|
|
|
|
|
|
|
Incidence::List Calendar::incidences( const TQDate &date )
|
|
|
|
{
|
|
|
|
return mergeIncidenceList( events( date ), todos( date ), journals( date ) );
|
|
|
|
}
|
|
|
|
|
|
|
|
Incidence::List Calendar::incidences()
|
|
|
|
{
|
|
|
|
return mergeIncidenceList( events(), todos(), journals() );
|
|
|
|
}
|
|
|
|
|
|
|
|
Incidence::List Calendar::rawIncidences()
|
|
|
|
{
|
|
|
|
return mergeIncidenceList( rawEvents(), rawTodos(), rawJournals() );
|
|
|
|
}
|
|
|
|
|
|
|
|
Event::List Calendar::sortEvents( Event::List *eventList,
|
|
|
|
EventSortField sortField,
|
|
|
|
SortDirection sortDirection )
|
|
|
|
{
|
|
|
|
Event::List eventListSorted;
|
|
|
|
Event::List tempList;
|
|
|
|
Event::List alphaList;
|
|
|
|
Event::List::Iterator sortIt;
|
|
|
|
Event::List::Iterator eit;
|
|
|
|
|
|
|
|
// Notice we alphabetically presort Summaries first.
|
|
|
|
// We do this so comparison "ties" stay in a nice order.
|
|
|
|
|
|
|
|
switch( sortField ) {
|
|
|
|
case EventSortUnsorted:
|
|
|
|
eventListSorted = *eventList;
|
|
|
|
break;
|
|
|
|
|
|
|
|
case EventSortStartDate:
|
|
|
|
alphaList = sortEvents( eventList, EventSortSummary, sortDirection );
|
|
|
|
for ( eit = alphaList.begin(); eit != alphaList.end(); ++eit ) {
|
|
|
|
if ( (*eit)->doesFloat() ) {
|
|
|
|
tempList.append( *eit );
|
|
|
|
continue;
|
|
|
|
}
|
|
|
|
sortIt = eventListSorted.begin();
|
|
|
|
if ( sortDirection == SortDirectionAscending ) {
|
|
|
|
while ( sortIt != eventListSorted.end() &&
|
|
|
|
(*eit)->dtStart() >= (*sortIt)->dtStart() ) {
|
|
|
|
++sortIt;
|
|
|
|
}
|
|
|
|
} else {
|
|
|
|
while ( sortIt != eventListSorted.end() &&
|
|
|
|
(*eit)->dtStart() < (*sortIt)->dtStart() ) {
|
|
|
|
++sortIt;
|
|
|
|
}
|
|
|
|
}
|
|
|
|
eventListSorted.insert( sortIt, *eit );
|
|
|
|
}
|
|
|
|
if ( sortDirection == SortDirectionAscending ) {
|
|
|
|
// Prepend the list of all-day Events
|
|
|
|
tempList += eventListSorted;
|
|
|
|
eventListSorted = tempList;
|
|
|
|
} else {
|
|
|
|
// Append the list of all-day Events
|
|
|
|
eventListSorted += tempList;
|
|
|
|
}
|
|
|
|
break;
|
|
|
|
|
|
|
|
case EventSortEndDate:
|
|
|
|
alphaList = sortEvents( eventList, EventSortSummary, sortDirection );
|
|
|
|
for ( eit = alphaList.begin(); eit != alphaList.end(); ++eit ) {
|
|
|
|
if ( (*eit)->hasEndDate() ) {
|
|
|
|
sortIt = eventListSorted.begin();
|
|
|
|
if ( sortDirection == SortDirectionAscending ) {
|
|
|
|
while ( sortIt != eventListSorted.end() &&
|
|
|
|
(*eit)->dtEnd() >= (*sortIt)->dtEnd() ) {
|
|
|
|
++sortIt;
|
|
|
|
}
|
|
|
|
} else {
|
|
|
|
while ( sortIt != eventListSorted.end() &&
|
|
|
|
(*eit)->dtEnd() < (*sortIt)->dtEnd() ) {
|
|
|
|
++sortIt;
|
|
|
|
}
|
|
|
|
}
|
|
|
|
} else {
|
|
|
|
// Keep a list of the Events without End DateTimes
|
|
|
|
tempList.append( *eit );
|
|
|
|
}
|
|
|
|
eventListSorted.insert( sortIt, *eit );
|
|
|
|
}
|
|
|
|
if ( sortDirection == SortDirectionAscending ) {
|
|
|
|
// Append the list of Events without End DateTimes
|
|
|
|
eventListSorted += tempList;
|
|
|
|
} else {
|
|
|
|
// Prepend the list of Events without End DateTimes
|
|
|
|
tempList += eventListSorted;
|
|
|
|
eventListSorted = tempList;
|
|
|
|
}
|
|
|
|
break;
|
|
|
|
|
|
|
|
case EventSortSummary:
|
|
|
|
for ( eit = eventList->begin(); eit != eventList->end(); ++eit ) {
|
|
|
|
sortIt = eventListSorted.begin();
|
|
|
|
if ( sortDirection == SortDirectionAscending ) {
|
|
|
|
while ( sortIt != eventListSorted.end() &&
|
|
|
|
(*eit)->summary() >= (*sortIt)->summary() ) {
|
|
|
|
++sortIt;
|
|
|
|
}
|
|
|
|
} else {
|
|
|
|
while ( sortIt != eventListSorted.end() &&
|
|
|
|
(*eit)->summary() < (*sortIt)->summary() ) {
|
|
|
|
++sortIt;
|
|
|
|
}
|
|
|
|
}
|
|
|
|
eventListSorted.insert( sortIt, *eit );
|
|
|
|
}
|
|
|
|
break;
|
|
|
|
}
|
|
|
|
|
|
|
|
return eventListSorted;
|
|
|
|
}
|
|
|
|
|
|
|
|
Event::List Calendar::sortEventsForDate( Event::List *eventList,
|
|
|
|
const TQDate &date,
|
|
|
|
EventSortField sortField,
|
|
|
|
SortDirection sortDirection )
|
|
|
|
{
|
|
|
|
Event::List eventListSorted;
|
|
|
|
Event::List tempList;
|
|
|
|
Event::List alphaList;
|
|
|
|
Event::List::Iterator sortIt;
|
|
|
|
Event::List::Iterator eit;
|
|
|
|
|
|
|
|
switch( sortField ) {
|
|
|
|
case EventSortStartDate:
|
|
|
|
alphaList = sortEvents( eventList, EventSortSummary, sortDirection );
|
|
|
|
for ( eit = alphaList.begin(); eit != alphaList.end(); ++eit ) {
|
|
|
|
if ( (*eit)->doesFloat() ) {
|
|
|
|
tempList.append( *eit );
|
|
|
|
continue;
|
|
|
|
}
|
|
|
|
sortIt = eventListSorted.begin();
|
|
|
|
if ( sortDirection == SortDirectionAscending ) {
|
|
|
|
while ( sortIt != eventListSorted.end() ) {
|
|
|
|
if ( !(*eit)->doesRecur() ) {
|
|
|
|
if ( (*eit)->dtStart().time() >= (*sortIt)->dtStart().time() ) {
|
|
|
|
++sortIt;
|
|
|
|
} else {
|
|
|
|
break;
|
|
|
|
}
|
|
|
|
} else {
|
|
|
|
if ( (*eit)->recursOn( date ) ) {
|
|
|
|
if ( (*eit)->dtStart().time() >= (*sortIt)->dtStart().time() ) {
|
|
|
|
++sortIt;
|
|
|
|
} else {
|
|
|
|
break;
|
|
|
|
}
|
|
|
|
} else {
|
|
|
|
++sortIt;
|
|
|
|
}
|
|
|
|
}
|
|
|
|
}
|
|
|
|
} else { // descending
|
|
|
|
while ( sortIt != eventListSorted.end() ) {
|
|
|
|
if ( !(*eit)->doesRecur() ) {
|
|
|
|
if ( (*eit)->dtStart().time() < (*sortIt)->dtStart().time() ) {
|
|
|
|
++sortIt;
|
|
|
|
} else {
|
|
|
|
break;
|
|
|
|
}
|
|
|
|
} else {
|
|
|
|
if ( (*eit)->recursOn( date ) ) {
|
|
|
|
if ( (*eit)->dtStart().time() < (*sortIt)->dtStart().time() ) {
|
|
|
|
++sortIt;
|
|
|
|
} else {
|
|
|
|
break;
|
|
|
|
}
|
|
|
|
} else {
|
|
|
|
++sortIt;
|
|
|
|
}
|
|
|
|
}
|
|
|
|
}
|
|
|
|
}
|
|
|
|
eventListSorted.insert( sortIt, *eit );
|
|
|
|
}
|
|
|
|
if ( sortDirection == SortDirectionAscending ) {
|
|
|
|
// Prepend the list of all-day Events
|
|
|
|
tempList += eventListSorted;
|
|
|
|
eventListSorted = tempList;
|
|
|
|
} else {
|
|
|
|
// Append the list of all-day Events
|
|
|
|
eventListSorted += tempList;
|
|
|
|
}
|
|
|
|
break;
|
|
|
|
|
|
|
|
case EventSortEndDate:
|
|
|
|
alphaList = sortEvents( eventList, EventSortSummary, sortDirection );
|
|
|
|
for ( eit = alphaList.begin(); eit != alphaList.end(); ++eit ) {
|
|
|
|
if ( (*eit)->hasEndDate() ) {
|
|
|
|
sortIt = eventListSorted.begin();
|
|
|
|
if ( sortDirection == SortDirectionAscending ) {
|
|
|
|
while ( sortIt != eventListSorted.end() ) {
|
|
|
|
if ( !(*eit)->doesRecur() ) {
|
|
|
|
if ( (*eit)->dtEnd().time() >= (*sortIt)->dtEnd().time() ) {
|
|
|
|
++sortIt;
|
|
|
|
} else {
|
|
|
|
break;
|
|
|
|
}
|
|
|
|
} else {
|
|
|
|
if ( (*eit)->recursOn( date ) ) {
|
|
|
|
if ( (*eit)->dtEnd().time() >= (*sortIt)->dtEnd().time() ) {
|
|
|
|
++sortIt;
|
|
|
|
} else {
|
|
|
|
break;
|
|
|
|
}
|
|
|
|
} else {
|
|
|
|
++sortIt;
|
|
|
|
}
|
|
|
|
}
|
|
|
|
}
|
|
|
|
} else { // descending
|
|
|
|
while ( sortIt != eventListSorted.end() ) {
|
|
|
|
if ( !(*eit)->doesRecur() ) {
|
|
|
|
if ( (*eit)->dtEnd().time() < (*sortIt)->dtEnd().time() ) {
|
|
|
|
++sortIt;
|
|
|
|
} else {
|
|
|
|
break;
|
|
|
|
}
|
|
|
|
} else {
|
|
|
|
if ( (*eit)->recursOn( date ) ) {
|
|
|
|
if ( (*eit)->dtEnd().time() < (*sortIt)->dtEnd().time() ) {
|
|
|
|
++sortIt;
|
|
|
|
} else {
|
|
|
|
break;
|
|
|
|
}
|
|
|
|
} else {
|
|
|
|
++sortIt;
|
|
|
|
}
|
|
|
|
}
|
|
|
|
}
|
|
|
|
}
|
|
|
|
} else {
|
|
|
|
// Keep a list of the Events without End DateTimes
|
|
|
|
tempList.append( *eit );
|
|
|
|
}
|
|
|
|
eventListSorted.insert( sortIt, *eit );
|
|
|
|
}
|
|
|
|
if ( sortDirection == SortDirectionAscending ) {
|
|
|
|
// Prepend the list of Events without End DateTimes
|
|
|
|
tempList += eventListSorted;
|
|
|
|
eventListSorted = tempList;
|
|
|
|
} else {
|
|
|
|
// Append the list of Events without End DateTimes
|
|
|
|
eventListSorted += tempList;
|
|
|
|
}
|
|
|
|
break;
|
|
|
|
|
|
|
|
default:
|
|
|
|
eventListSorted = sortEvents( eventList, sortField, sortDirection );
|
|
|
|
break;
|
|
|
|
}
|
|
|
|
|
|
|
|
return eventListSorted;
|
|
|
|
}
|
|
|
|
|
|
|
|
Event::List Calendar::events( const TQDate &date,
|
|
|
|
EventSortField sortField,
|
|
|
|
SortDirection sortDirection )
|
|
|
|
{
|
|
|
|
Event::List el = rawEventsForDate( date, sortField, sortDirection );
|
|
|
|
mFilter->apply( &el );
|
|
|
|
return el;
|
|
|
|
}
|
|
|
|
|
|
|
|
Event::List Calendar::events( const TQDateTime &qdt )
|
|
|
|
{
|
|
|
|
Event::List el = rawEventsForDate( qdt );
|
|
|
|
mFilter->apply( &el );
|
|
|
|
return el;
|
|
|
|
}
|
|
|
|
|
|
|
|
Event::List Calendar::events( const TQDate &start, const TQDate &end,
|
|
|
|
bool inclusive)
|
|
|
|
{
|
|
|
|
Event::List el = rawEvents( start, end, inclusive );
|
|
|
|
mFilter->apply( &el );
|
|
|
|
return el;
|
|
|
|
}
|
|
|
|
|
|
|
|
Event::List Calendar::events( EventSortField sortField,
|
|
|
|
SortDirection sortDirection )
|
|
|
|
{
|
|
|
|
Event::List el = rawEvents( sortField, sortDirection );
|
|
|
|
mFilter->apply( &el );
|
|
|
|
return el;
|
|
|
|
}
|
|
|
|
|
|
|
|
bool Calendar::addIncidence( Incidence *incidence )
|
|
|
|
{
|
|
|
|
Incidence::AddVisitor<Calendar> v( this );
|
|
|
|
|
|
|
|
return incidence->accept(v);
|
|
|
|
}
|
|
|
|
|
|
|
|
bool Calendar::deleteIncidence( Incidence *incidence )
|
|
|
|
{
|
|
|
|
if ( beginChange( incidence ) ) {
|
|
|
|
if (incidence->hasRecurrenceID()) {
|
|
|
|
// Delete this event's UID from the parent's list of children
|
|
|
|
Incidence *parentIncidence;
|
|
|
|
IncidenceList il = incidence->childIncidences();
|
|
|
|
IncidenceListIterator it;
|
|
|
|
it = il.begin();
|
|
|
|
parentIncidence = this->incidence(*it);
|
|
|
|
parentIncidence->deleteChildIncidence(incidence->uid());
|
|
|
|
}
|
|
|
|
else {
|
|
|
|
// Delete all children as well
|
|
|
|
IncidenceList il = incidence->childIncidences();
|
|
|
|
IncidenceListIterator it;
|
|
|
|
for ( it = il.begin(); it != il.end(); ++it ) {
|
|
|
|
deleteIncidence( this->incidence(*it) );
|
|
|
|
// Avoid a crash, reset the iterator every time the list is modified
|
|
|
|
it = il.begin();
|
|
|
|
}
|
|
|
|
}
|
|
|
|
Incidence::DeleteVisitor<Calendar> v( this );
|
|
|
|
bool result = incidence->accept( v );
|
|
|
|
endChange( incidence );
|
|
|
|
return result;
|
|
|
|
} else
|
|
|
|
return false;
|
|
|
|
}
|
|
|
|
|
|
|
|
/** Dissociate a single occurrence or all future occurrences from a recurring sequence.
|
|
|
|
The new incidence is returned, but not automatically inserted into the calendar,
|
|
|
|
which is left to the calling application */
|
|
|
|
Incidence *Calendar::dissociateOccurrence( Incidence *incidence, TQDate date,
|
|
|
|
bool single )
|
|
|
|
{
|
|
|
|
if ( !incidence || !incidence->doesRecur() )
|
|
|
|
return 0;
|
|
|
|
|
|
|
|
Incidence *newInc = incidence->clone();
|
|
|
|
newInc->recreate();
|
|
|
|
newInc->setHasRecurrenceID(false);
|
|
|
|
// newInc->setRecurrenceID(QString());
|
|
|
|
newInc->setRelatedTo( incidence );
|
|
|
|
Recurrence *recur = newInc->recurrence();
|
|
|
|
if ( single ) {
|
|
|
|
recur->clear();
|
|
|
|
} else {
|
|
|
|
// Adjust the recurrence for the future incidences. In particular
|
|
|
|
// adjust the "end after n occurrences" rules! "No end date" and "end by ..."
|
|
|
|
// don't need to be modified.
|
|
|
|
int duration = recur->duration();
|
|
|
|
if ( duration > 0 ) {
|
|
|
|
int doneduration = recur->durationTo( date.addDays(-1) );
|
|
|
|
if ( doneduration >= duration ) {
|
|
|
|
kdDebug(5850) << "The dissociated event already occurred more often "
|
|
|
|
<< "than it was supposed to ever occur. ERROR!" << endl;
|
|
|
|
recur->clear();
|
|
|
|
} else {
|
|
|
|
recur->setDuration( duration - doneduration );
|
|
|
|
}
|
|
|
|
}
|
|
|
|
}
|
|
|
|
// Adjust the date of the incidence
|
|
|
|
if ( incidence->type() == "Event" ) {
|
|
|
|
Event *ev = static_cast<Event *>( newInc );
|
|
|
|
TQDateTime start( ev->dtStart() );
|
|
|
|
int daysTo = start.date().daysTo( date );
|
|
|
|
ev->setDtStart( start.addDays( daysTo ) );
|
|
|
|
ev->setDtEnd( ev->dtEnd().addDays( daysTo ) );
|
|
|
|
} else if ( incidence->type() == "Todo" ) {
|
|
|
|
Todo *td = static_cast<Todo *>( newInc );
|
|
|
|
bool haveOffset = false;
|
|
|
|
int daysTo = 0;
|
|
|
|
if ( td->hasDueDate() ) {
|
|
|
|
TQDateTime due( td->dtDue() );
|
|
|
|
daysTo = due.date().daysTo( date );
|
|
|
|
td->setDtDue( due.addDays( daysTo ), true );
|
|
|
|
haveOffset = true;
|
|
|
|
}
|
|
|
|
if ( td->hasStartDate() ) {
|
|
|
|
TQDateTime start( td->dtStart() );
|
|
|
|
if ( !haveOffset )
|
|
|
|
daysTo = start.date().daysTo( date );
|
|
|
|
td->setDtStart( start.addDays( daysTo ) );
|
|
|
|
haveOffset = true;
|
|
|
|
}
|
|
|
|
}
|
|
|
|
recur = incidence->recurrence();
|
|
|
|
if ( recur ) {
|
|
|
|
if ( single ) {
|
|
|
|
recur->addExDate( date );
|
|
|
|
} else {
|
|
|
|
// Make sure the recurrence of the past events ends
|
|
|
|
// at the corresponding day
|
|
|
|
recur->setEndDate( date.addDays(-1) );
|
|
|
|
}
|
|
|
|
}
|
|
|
|
return newInc;
|
|
|
|
}
|
|
|
|
|
|
|
|
Incidence *Calendar::incidence( const TQString &uid )
|
|
|
|
{
|
|
|
|
Incidence *i = event( uid );
|
|
|
|
if ( i )
|
|
|
|
return i;
|
|
|
|
i = todo( uid );
|
|
|
|
if ( i )
|
|
|
|
return i;
|
|
|
|
i = journal( uid );
|
|
|
|
return i;
|
|
|
|
}
|
|
|
|
|
|
|
|
Incidence::List Calendar::incidencesFromSchedulingID( const TQString &UID )
|
|
|
|
{
|
|
|
|
Incidence::List result;
|
|
|
|
Incidence::List incidences = rawIncidences();
|
|
|
|
Incidence::List::iterator it = incidences.begin();
|
|
|
|
for ( ; it != incidences.end(); ++it )
|
|
|
|
if ( (*it)->schedulingID() == UID )
|
|
|
|
result.append( *it );
|
|
|
|
return result;
|
|
|
|
}
|
|
|
|
|
|
|
|
Incidence *Calendar::incidenceFromSchedulingID( const TQString &UID )
|
|
|
|
{
|
|
|
|
Incidence::List incidences = rawIncidences();
|
|
|
|
Incidence::List::iterator it = incidences.begin();
|
|
|
|
for ( ; it != incidences.end(); ++it )
|
|
|
|
if ( (*it)->schedulingID() == UID )
|
|
|
|
// Touchdown, and the crowd goes wild
|
|
|
|
return *it;
|
|
|
|
// Not found
|
|
|
|
return 0;
|
|
|
|
}
|
|
|
|
|
|
|
|
Todo::List Calendar::sortTodos( Todo::List *todoList,
|
|
|
|
TodoSortField sortField,
|
|
|
|
SortDirection sortDirection )
|
|
|
|
{
|
|
|
|
Todo::List todoListSorted;
|
|
|
|
Todo::List tempList, t;
|
|
|
|
Todo::List alphaList;
|
|
|
|
Todo::List::Iterator sortIt;
|
|
|
|
Todo::List::Iterator eit;
|
|
|
|
|
|
|
|
// Notice we alphabetically presort Summaries first.
|
|
|
|
// We do this so comparison "ties" stay in a nice order.
|
|
|
|
|
|
|
|
// Note that Todos may not have Start DateTimes nor due DateTimes.
|
|
|
|
|
|
|
|
switch( sortField ) {
|
|
|
|
case TodoSortUnsorted:
|
|
|
|
todoListSorted = *todoList;
|
|
|
|
break;
|
|
|
|
|
|
|
|
case TodoSortStartDate:
|
|
|
|
alphaList = sortTodos( todoList, TodoSortSummary, sortDirection );
|
|
|
|
for ( eit = alphaList.begin(); eit != alphaList.end(); ++eit ) {
|
|
|
|
if ( (*eit)->hasStartDate() ) {
|
|
|
|
sortIt = todoListSorted.begin();
|
|
|
|
if ( sortDirection == SortDirectionAscending ) {
|
|
|
|
while ( sortIt != todoListSorted.end() &&
|
|
|
|
(*eit)->dtStart() >= (*sortIt)->dtStart() ) {
|
|
|
|
++sortIt;
|
|
|
|
}
|
|
|
|
} else {
|
|
|
|
while ( sortIt != todoListSorted.end() &&
|
|
|
|
(*eit)->dtStart() < (*sortIt)->dtStart() ) {
|
|
|
|
++sortIt;
|
|
|
|
}
|
|
|
|
}
|
|
|
|
todoListSorted.insert( sortIt, *eit );
|
|
|
|
} else {
|
|
|
|
// Keep a list of the Todos without Start DateTimes
|
|
|
|
tempList.append( *eit );
|
|
|
|
}
|
|
|
|
}
|
|
|
|
if ( sortDirection == SortDirectionAscending ) {
|
|
|
|
// Append the list of Todos without Start DateTimes
|
|
|
|
todoListSorted += tempList;
|
|
|
|
} else {
|
|
|
|
// Prepend the list of Todos without Start DateTimes
|
|
|
|
tempList += todoListSorted;
|
|
|
|
todoListSorted = tempList;
|
|
|
|
}
|
|
|
|
break;
|
|
|
|
|
|
|
|
case TodoSortDueDate:
|
|
|
|
alphaList = sortTodos( todoList, TodoSortSummary, sortDirection );
|
|
|
|
for ( eit = alphaList.begin(); eit != alphaList.end(); ++eit ) {
|
|
|
|
if ( (*eit)->hasDueDate() ) {
|
|
|
|
sortIt = todoListSorted.begin();
|
|
|
|
if ( sortDirection == SortDirectionAscending ) {
|
|
|
|
while ( sortIt != todoListSorted.end() &&
|
|
|
|
(*eit)->dtDue() >= (*sortIt)->dtDue() ) {
|
|
|
|
++sortIt;
|
|
|
|
}
|
|
|
|
} else {
|
|
|
|
while ( sortIt != todoListSorted.end() &&
|
|
|
|
(*eit)->dtDue() < (*sortIt)->dtDue() ) {
|
|
|
|
++sortIt;
|
|
|
|
}
|
|
|
|
}
|
|
|
|
todoListSorted.insert( sortIt, *eit );
|
|
|
|
} else {
|
|
|
|
// Keep a list of the Todos without Due DateTimes
|
|
|
|
tempList.append( *eit );
|
|
|
|
}
|
|
|
|
}
|
|
|
|
if ( sortDirection == SortDirectionAscending ) {
|
|
|
|
// Append the list of Todos without Due DateTimes
|
|
|
|
todoListSorted += tempList;
|
|
|
|
} else {
|
|
|
|
// Prepend the list of Todos without Due DateTimes
|
|
|
|
tempList += todoListSorted;
|
|
|
|
todoListSorted = tempList;
|
|
|
|
}
|
|
|
|
break;
|
|
|
|
|
|
|
|
case TodoSortPriority:
|
|
|
|
alphaList = sortTodos( todoList, TodoSortSummary, sortDirection );
|
|
|
|
for ( eit = alphaList.begin(); eit != alphaList.end(); ++eit ) {
|
|
|
|
sortIt = todoListSorted.begin();
|
|
|
|
if ( sortDirection == SortDirectionAscending ) {
|
|
|
|
while ( sortIt != todoListSorted.end() &&
|
|
|
|
(*eit)->priority() >= (*sortIt)->priority() ) {
|
|
|
|
++sortIt;
|
|
|
|
}
|
|
|
|
} else {
|
|
|
|
while ( sortIt != todoListSorted.end() &&
|
|
|
|
(*eit)->priority() < (*sortIt)->priority() ) {
|
|
|
|
++sortIt;
|
|
|
|
}
|
|
|
|
}
|
|
|
|
todoListSorted.insert( sortIt, *eit );
|
|
|
|
}
|
|
|
|
break;
|
|
|
|
|
|
|
|
case TodoSortPercentComplete:
|
|
|
|
alphaList = sortTodos( todoList, TodoSortSummary, sortDirection );
|
|
|
|
for ( eit = alphaList.begin(); eit != alphaList.end(); ++eit ) {
|
|
|
|
sortIt = todoListSorted.begin();
|
|
|
|
if ( sortDirection == SortDirectionAscending ) {
|
|
|
|
while ( sortIt != todoListSorted.end() &&
|
|
|
|
(*eit)->percentComplete() >= (*sortIt)->percentComplete() ) {
|
|
|
|
++sortIt;
|
|
|
|
}
|
|
|
|
} else {
|
|
|
|
while ( sortIt != todoListSorted.end() &&
|
|
|
|
(*eit)->percentComplete() < (*sortIt)->percentComplete() ) {
|
|
|
|
++sortIt;
|
|
|
|
}
|
|
|
|
}
|
|
|
|
todoListSorted.insert( sortIt, *eit );
|
|
|
|
}
|
|
|
|
break;
|
|
|
|
|
|
|
|
case TodoSortSummary:
|
|
|
|
for ( eit = todoList->begin(); eit != todoList->end(); ++eit ) {
|
|
|
|
sortIt = todoListSorted.begin();
|
|
|
|
if ( sortDirection == SortDirectionAscending ) {
|
|
|
|
while ( sortIt != todoListSorted.end() &&
|
|
|
|
(*eit)->summary() >= (*sortIt)->summary() ) {
|
|
|
|
++sortIt;
|
|
|
|
}
|
|
|
|
} else {
|
|
|
|
while ( sortIt != todoListSorted.end() &&
|
|
|
|
(*eit)->summary() < (*sortIt)->summary() ) {
|
|
|
|
++sortIt;
|
|
|
|
}
|
|
|
|
}
|
|
|
|
todoListSorted.insert( sortIt, *eit );
|
|
|
|
}
|
|
|
|
break;
|
|
|
|
}
|
|
|
|
|
|
|
|
return todoListSorted;
|
|
|
|
}
|
|
|
|
|
|
|
|
Todo::List Calendar::todos( TodoSortField sortField,
|
|
|
|
SortDirection sortDirection )
|
|
|
|
{
|
|
|
|
Todo::List tl = rawTodos( sortField, sortDirection );
|
|
|
|
mFilter->apply( &tl );
|
|
|
|
return tl;
|
|
|
|
}
|
|
|
|
|
|
|
|
Todo::List Calendar::todos( const TQDate &date )
|
|
|
|
{
|
|
|
|
Todo::List el = rawTodosForDate( date );
|
|
|
|
mFilter->apply( &el );
|
|
|
|
return el;
|
|
|
|
}
|
|
|
|
|
|
|
|
Journal::List Calendar::sortJournals( Journal::List *journalList,
|
|
|
|
JournalSortField sortField,
|
|
|
|
SortDirection sortDirection )
|
|
|
|
{
|
|
|
|
Journal::List journalListSorted;
|
|
|
|
Journal::List::Iterator sortIt;
|
|
|
|
Journal::List::Iterator eit;
|
|
|
|
|
|
|
|
switch( sortField ) {
|
|
|
|
case JournalSortUnsorted:
|
|
|
|
journalListSorted = *journalList;
|
|
|
|
break;
|
|
|
|
|
|
|
|
case JournalSortDate:
|
|
|
|
for ( eit = journalList->begin(); eit != journalList->end(); ++eit ) {
|
|
|
|
sortIt = journalListSorted.begin();
|
|
|
|
if ( sortDirection == SortDirectionAscending ) {
|
|
|
|
while ( sortIt != journalListSorted.end() &&
|
|
|
|
(*eit)->dtStart() >= (*sortIt)->dtStart() ) {
|
|
|
|
++sortIt;
|
|
|
|
}
|
|
|
|
} else {
|
|
|
|
while ( sortIt != journalListSorted.end() &&
|
|
|
|
(*eit)->dtStart() < (*sortIt)->dtStart() ) {
|
|
|
|
++sortIt;
|
|
|
|
}
|
|
|
|
}
|
|
|
|
journalListSorted.insert( sortIt, *eit );
|
|
|
|
}
|
|
|
|
break;
|
|
|
|
|
|
|
|
case JournalSortSummary:
|
|
|
|
for ( eit = journalList->begin(); eit != journalList->end(); ++eit ) {
|
|
|
|
sortIt = journalListSorted.begin();
|
|
|
|
if ( sortDirection == SortDirectionAscending ) {
|
|
|
|
while ( sortIt != journalListSorted.end() &&
|
|
|
|
(*eit)->summary() >= (*sortIt)->summary() ) {
|
|
|
|
++sortIt;
|
|
|
|
}
|
|
|
|
} else {
|
|
|
|
while ( sortIt != journalListSorted.end() &&
|
|
|
|
(*eit)->summary() < (*sortIt)->summary() ) {
|
|
|
|
++sortIt;
|
|
|
|
}
|
|
|
|
}
|
|
|
|
journalListSorted.insert( sortIt, *eit );
|
|
|
|
}
|
|
|
|
break;
|
|
|
|
}
|
|
|
|
|
|
|
|
return journalListSorted;
|
|
|
|
}
|
|
|
|
|
|
|
|
Journal::List Calendar::journals( JournalSortField sortField,
|
|
|
|
SortDirection sortDirection )
|
|
|
|
{
|
|
|
|
Journal::List jl = rawJournals( sortField, sortDirection );
|
|
|
|
mFilter->apply( &jl );
|
|
|
|
return jl;
|
|
|
|
}
|
|
|
|
|
|
|
|
Journal::List Calendar::journals( const TQDate &date )
|
|
|
|
{
|
|
|
|
Journal::List el = rawJournalsForDate( date );
|
|
|
|
mFilter->apply( &el );
|
|
|
|
return el;
|
|
|
|
}
|
|
|
|
|
|
|
|
// When this is called, the todo have already been added to the calendar.
|
|
|
|
// This method is only about linking related todos
|
|
|
|
void Calendar::setupRelations( Incidence *forincidence )
|
|
|
|
{
|
|
|
|
if ( !forincidence ) return;
|
|
|
|
// kdDebug(5850) << "Calendar::setupRelations for incidence " << forincidence << " with UID " << forincidence->uid() << ", summary: " << forincidence->summary() << endl;
|
|
|
|
TQString uid = forincidence->uid();
|
|
|
|
|
|
|
|
// First, go over the list of orphans and see if this is their parent
|
|
|
|
while ( Incidence* i = mOrphans[ uid ] ) {
|
|
|
|
mOrphans.remove( uid );
|
|
|
|
i->setRelatedTo( forincidence );
|
|
|
|
forincidence->addRelation( i );
|
|
|
|
mOrphanUids.remove( i->uid() );
|
|
|
|
}
|
|
|
|
|
|
|
|
// Now see about this incidences parent
|
|
|
|
if ( !forincidence->relatedTo() && !forincidence->relatedToUid().isEmpty() ) {
|
|
|
|
// This incidence has a uid it is related to but is not registered to it yet
|
|
|
|
// Try to find it
|
|
|
|
Incidence* parent = incidence( forincidence->relatedToUid() );
|
|
|
|
if ( parent ) {
|
|
|
|
// Found it
|
|
|
|
forincidence->setRelatedTo( parent );
|
|
|
|
parent->addRelation( forincidence );
|
|
|
|
} else {
|
|
|
|
// Not found, put this in the mOrphans list
|
|
|
|
// Note that the mOrphans dict might have several entries with the same key! That are
|
|
|
|
// multiple children that wait for the parent incidence to be inserted.
|
|
|
|
mOrphans.insert( forincidence->relatedToUid(), forincidence );
|
|
|
|
mOrphanUids.insert( forincidence->uid(), forincidence );
|
|
|
|
}
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
// If a task with subtasks is deleted, move it's subtasks to the orphans list
|
|
|
|
void Calendar::removeRelations( Incidence *incidence )
|
|
|
|
{
|
|
|
|
if( !incidence ) {
|
|
|
|
kdDebug(5800) << "Warning: Calendar::removeRelations( 0 )!\n";
|
|
|
|
return;
|
|
|
|
}
|
|
|
|
|
|
|
|
// kdDebug(5850) << "Calendar::removeRelations for incidence " << forincidence << " with UID " << forincidence->uid() << ", summary: " << forincidence->summary() << endl;
|
|
|
|
TQString uid = incidence->uid();
|
|
|
|
|
|
|
|
Incidence::List relations = incidence->relations();
|
|
|
|
Incidence::List::ConstIterator it;
|
|
|
|
for ( it = relations.begin(); it != relations.end(); ++it ) {
|
|
|
|
Incidence *i = *it;
|
|
|
|
if ( !mOrphanUids.find( i->uid() ) ) {
|
|
|
|
mOrphans.insert( uid, i );
|
|
|
|
mOrphanUids.insert( i->uid(), i );
|
|
|
|
i->setRelatedTo( 0 );
|
|
|
|
i->setRelatedToUid( uid );
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
// If this incidence is related to something else, tell that about it
|
|
|
|
if ( incidence->relatedTo() )
|
|
|
|
incidence->relatedTo()->removeRelation( incidence );
|
|
|
|
|
|
|
|
// Remove this one from the orphans list
|
|
|
|
if ( mOrphanUids.remove( uid ) ) {
|
|
|
|
// This incidence is located in the orphans list - it should be removed
|
|
|
|
// Since the mOrphans dict might contain the same key (with different
|
|
|
|
// child incidence pointers!) multiple times, take care that we remove
|
|
|
|
// the correct one. So we need to remove all items with the given
|
|
|
|
// parent UID, and readd those that are not for this item. Also, there
|
|
|
|
// might be other entries with differnet UID that point to this
|
|
|
|
// incidence (this might happen when the relatedTo of the item is
|
|
|
|
// changed before its parent is inserted. This might happen with
|
|
|
|
// groupware servers....). Remove them, too
|
|
|
|
TQStringList relatedToUids;
|
|
|
|
// First get the list of all keys in the mOrphans list that point to the removed item
|
|
|
|
relatedToUids << incidence->relatedToUid();
|
|
|
|
for ( TQDictIterator<Incidence> it( mOrphans ); it.current(); ++it ) {
|
|
|
|
if ( it.current()->uid() == uid ) {
|
|
|
|
relatedToUids << it.currentKey();
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
// now go through all uids that have one entry that point to the incidence
|
|
|
|
for ( TQStringList::Iterator uidit = relatedToUids.begin();
|
|
|
|
uidit != relatedToUids.end(); ++uidit ) {
|
|
|
|
Incidence::List tempList;
|
|
|
|
// Remove all to get access to the remaining entries
|
|
|
|
while( Incidence* i = mOrphans[ *uidit ] ) {
|
|
|
|
mOrphans.remove( *uidit );
|
|
|
|
if ( i != incidence ) tempList.append( i );
|
|
|
|
}
|
|
|
|
// Readd those that point to a different orphan incidence
|
|
|
|
for ( Incidence::List::Iterator incit = tempList.begin();
|
|
|
|
incit != tempList.end(); ++incit ) {
|
|
|
|
mOrphans.insert( *uidit, *incit );
|
|
|
|
}
|
|
|
|
}
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
void Calendar::registerObserver( Observer *observer )
|
|
|
|
{
|
|
|
|
if( !mObservers.contains( observer ) )
|
|
|
|
mObservers.append( observer );
|
|
|
|
mNewObserver = true;
|
|
|
|
}
|
|
|
|
|
|
|
|
void Calendar::unregisterObserver( Observer *observer )
|
|
|
|
{
|
|
|
|
mObservers.remove( observer );
|
|
|
|
}
|
|
|
|
|
|
|
|
void Calendar::setModified( bool modified )
|
|
|
|
{
|
|
|
|
if ( modified != mModified || mNewObserver ) {
|
|
|
|
mNewObserver = false;
|
|
|
|
Observer *observer;
|
|
|
|
for ( observer = mObservers.first(); observer;
|
|
|
|
observer = mObservers.next() ) {
|
|
|
|
observer->calendarModified( modified, this );
|
|
|
|
}
|
|
|
|
mModified = modified;
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
void Calendar::incidenceUpdated( IncidenceBase *incidence )
|
|
|
|
{
|
|
|
|
incidence->setSyncStatus( Event::SYNCMOD );
|
|
|
|
incidence->setLastModified( TQDateTime::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 Calendar::notifyIncidenceAdded( Incidence *i )
|
|
|
|
{
|
|
|
|
if ( !mObserversEnabled )
|
|
|
|
return;
|
|
|
|
|
|
|
|
Observer *observer;
|
|
|
|
for ( observer = mObservers.first(); observer;
|
|
|
|
observer = mObservers.next() ) {
|
|
|
|
observer->calendarIncidenceAdded( i );
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
void Calendar::notifyIncidenceChanged( Incidence *i )
|
|
|
|
{
|
|
|
|
if ( !mObserversEnabled )
|
|
|
|
return;
|
|
|
|
|
|
|
|
Observer *observer;
|
|
|
|
for ( observer = mObservers.first(); observer;
|
|
|
|
observer = mObservers.next() ) {
|
|
|
|
observer->calendarIncidenceChanged( i );
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
void Calendar::notifyIncidenceDeleted( Incidence *i )
|
|
|
|
{
|
|
|
|
if ( !mObserversEnabled )
|
|
|
|
return;
|
|
|
|
|
|
|
|
Observer *observer;
|
|
|
|
for ( observer = mObservers.first(); observer;
|
|
|
|
observer = mObservers.next() ) {
|
|
|
|
observer->calendarIncidenceDeleted( i );
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
void Calendar::customPropertyUpdated()
|
|
|
|
{
|
|
|
|
setModified( true );
|
|
|
|
}
|
|
|
|
|
|
|
|
void Calendar::setProductId( const TQString &productId )
|
|
|
|
{
|
|
|
|
mProductId = productId;
|
|
|
|
}
|
|
|
|
|
|
|
|
TQString Calendar::productId()
|
|
|
|
{
|
|
|
|
return mProductId;
|
|
|
|
}
|
|
|
|
|
|
|
|
Incidence::List Calendar::mergeIncidenceList( const Event::List &events,
|
|
|
|
const Todo::List &todos,
|
|
|
|
const Journal::List &journals )
|
|
|
|
{
|
|
|
|
Incidence::List incidences;
|
|
|
|
|
|
|
|
Event::List::ConstIterator it1;
|
|
|
|
for ( it1 = events.begin(); it1 != events.end(); ++it1 )
|
|
|
|
incidences.append( *it1 );
|
|
|
|
|
|
|
|
Todo::List::ConstIterator it2;
|
|
|
|
for ( it2 = todos.begin(); it2 != todos.end(); ++it2 )
|
|
|
|
incidences.append( *it2 );
|
|
|
|
|
|
|
|
Journal::List::ConstIterator it3;
|
|
|
|
for ( it3 = journals.begin(); it3 != journals.end(); ++it3 )
|
|
|
|
incidences.append( *it3 );
|
|
|
|
|
|
|
|
return incidences;
|
|
|
|
}
|
|
|
|
|
|
|
|
bool Calendar::beginChange( Incidence * )
|
|
|
|
{
|
|
|
|
return true;
|
|
|
|
}
|
|
|
|
|
|
|
|
bool Calendar::endChange( Incidence * )
|
|
|
|
{
|
|
|
|
return true;
|
|
|
|
}
|
|
|
|
|
|
|
|
void Calendar::setObserversEnabled( bool enabled )
|
|
|
|
{
|
|
|
|
mObserversEnabled = enabled;
|
|
|
|
}
|
|
|
|
|
|
|
|
#include "calendar.moc"
|