summaryrefslogtreecommitdiffabout
path: root/libkcal
Side-by-side diff
Diffstat (limited to 'libkcal') (more/less context) (ignore whitespace changes)
-rw-r--r--libkcal/kincidenceformatter.cpp24
-rw-r--r--libkcal/recurrence.cpp2
2 files changed, 17 insertions, 9 deletions
diff --git a/libkcal/kincidenceformatter.cpp b/libkcal/kincidenceformatter.cpp
index 6d07d4c..0d9c3f4 100644
--- a/libkcal/kincidenceformatter.cpp
+++ b/libkcal/kincidenceformatter.cpp
@@ -1,338 +1,344 @@
#include "kincidenceformatter.h"
#include <kstaticdeleter.h>
#include <kglobal.h>
#include <klocale.h>
#ifdef DEKTOP_VERSION
#include <kabc/stdaddressbook.h>
#define size count
#endif
KIncidenceFormatter* KIncidenceFormatter::mInstance = 0;
static KStaticDeleter<KIncidenceFormatter> insd;
QString KIncidenceFormatter::getFormattedText( Incidence * inc )
{
// #ifndef QT_NO_INPUTDIALOG
// return QInputDialog::getItem( caption, label, items, current, editable );
// #else
// return QString::null;
// #endif
mText = "";
if ( inc->type() == "Event" )
setEvent((Event *) inc );
else if ( inc->type() == "Todo" )
setTodo((Todo *) inc );
return mText;
}
KIncidenceFormatter* KIncidenceFormatter::instance()
{
if (!mInstance) {
mInstance = insd.setObject(new KIncidenceFormatter());
}
return mInstance;
}
KIncidenceFormatter::~KIncidenceFormatter()
{
if (mInstance == this)
mInstance = insd.setObject(0);
//qDebug("KIncidenceFormatter::~KIncidenceFormatter ");
}
KIncidenceFormatter::KIncidenceFormatter()
{
mColorMode = 0;
}
void KIncidenceFormatter::setEvent(Event *event)
{
int mode = 0;
mCurrentIncidence = event;
bool shortDate = true;
if ( mode == 0 ) {
addTag("h3",event->summary());
}
else {
if ( mColorMode == 1 ) {
mText +="<font color=\"#00A000\">";
}
if ( mColorMode == 2 ) {
mText +="<font color=\"#C00000\">";
}
// mText +="<font color=\"#F00000\">" + i18n("O-due!") + "</font>";
if ( mode == 1 ) {
addTag("h2",i18n( "Local: " ) +event->summary());
} else {
addTag("h2",i18n( "Remote: " ) +event->summary());
}
addTag("h3",i18n( "Last modified: " ) + KGlobal::locale()->formatDateTime(event->lastModified(),shortDate, true ) );
if ( mColorMode )
mText += "</font>";
}
if (event->cancelled ()) {
mText +="<font color=\"#B00000\">";
addTag("i",i18n("This event has been cancelled!"));
mText.append("<br>");
mText += "</font>";
}
if (!event->location().isEmpty()) {
addTag("b",i18n("Location: "));
mText.append(event->location()+"<br>");
}
if (event->doesFloat()) {
if (event->isMultiDay()) {
mText.append(i18n("<p><b>From:</b> %1 </p><p><b>To:</b> %2</p>")
.arg(event->dtStartDateStr(shortDate))
.arg(event->dtEndDateStr(shortDate)));
} else {
mText.append(i18n("<p><b>On:</b> %1</p>").arg(event->dtStartDateStr( shortDate )));
}
} else {
if (event->isMultiDay()) {
mText.append(i18n("<p><b>From:</b> %1</p> ")
.arg(event->dtStartStr( shortDate)));
mText.append(i18n("<p><b>To:</b> %1</p>")
.arg(event->dtEndStr(shortDate)));
} else {
mText.append(i18n("<p><b>On:</b> %1</p> ")
.arg(event->dtStartDateStr( shortDate )));
mText.append(i18n("<p><b>From:</b> %1 <b>To:</b> %2</p>")
.arg(event->dtStartTimeStr())
.arg(event->dtEndTimeStr()));
}
}
if (event->recurrence()->doesRecur()) {
QString recurText = event->recurrence()->recurrenceText();
addTag("p","<em>" + i18n("This is a %1 recurring event.").arg(recurText ) + "</em>");
- bool last;
+
+ bool ok;
QDate start = QDate::currentDate();
- QDate next;
- next = event->recurrence()->getPreviousDate( start , &last );
- if ( !last ) {
- next = event->recurrence()->getNextDate( start.addDays( - 1 ) );
- addTag("p",i18n("Next recurrence is on: ")+ KGlobal::locale()->formatDate( next, shortDate ) );
- //addTag("p", KGlobal::locale()->formatDate( next, shortDate ));
+ QDateTime next;
+ next = event->getNextOccurence( QDateTime::currentDateTime() , &ok );
+ if ( ok ) {
+ addTag("p",i18n("<b>Next recurrence is on:</b>") );
+ addTag("p", KGlobal::locale()->formatDate( next.date(), shortDate ));
+
} else {
- addTag("p",i18n("<b>Last recurrence was on:</b>") );
- addTag("p", KGlobal::locale()->formatDate( next, shortDate ));
+ bool last;
+ QDate nextd;
+ nextd = event->recurrence()->getPreviousDate( QDate::currentDate() , &last );
+ if ( last ) {
+ addTag("p",i18n("<b>Last recurrence was on:</b>") );
+ addTag("p", KGlobal::locale()->formatDate( nextd, shortDate ));
+ }
}
}
if (event->isAlarmEnabled()) {
Alarm *alarm =event->alarms().first() ;
QDateTime t = alarm->time();
int min = t.secsTo( event->dtStart() )/60;
QString s =i18n("(%1 min before)").arg( min );
addTag("p",i18n("<b>Alarm on: </b>") + s + ": "+KGlobal::locale()->formatDateTime( t, shortDate ));
//addTag("p", KGlobal::locale()->formatDateTime( t, shortDate ));
//addTag("p",s);
}
addTag("p",i18n("<b>Access: </b>") +event->secrecyStr() );
// mText.append(event->secrecyStr()+"<br>");
formatCategories(event);
if (!event->description().isEmpty()) {
addTag("p",i18n("<b>Details: </b>"));
addTag("p",event->description());
}
formatReadOnly(event);
formatAttendees(event);
}
void KIncidenceFormatter::setTodo(Todo *event )
{
int mode = 0;
mCurrentIncidence = event;
bool shortDate = true;
if (mode == 0 )
addTag("h3",event->summary());
else {
if ( mColorMode == 1 ) {
mText +="<font color=\"#00A000\">";
}
if ( mColorMode == 2 ) {
mText +="<font color=\"#B00000\">";
}
if ( mode == 1 ) {
addTag("h2",i18n( "Local: " ) +event->summary());
} else {
addTag("h2",i18n( "Remote: " ) +event->summary());
}
addTag("h3",i18n( "Last modified: " ) + KGlobal::locale()->formatDateTime(event->lastModified(),shortDate, true ) );
if ( mColorMode )
mText += "</font>";
}
if ( event->percentComplete() == 100 && event->hasCompletedDate() ) {
mText +="<font color=\"#B00000\">";
addTag("i", i18n("<p><i>Completed on %1</i></p>").arg( event->completedStr(shortDate) ) );
mText += "</font>";
} else {
mText.append(i18n("<p><i>%1 % completed</i></p>")
.arg(event->percentComplete()));
}
if (event->cancelled ()) {
mText +="<font color=\"#B00000\">";
addTag("i",i18n("This todo has been cancelled!"));
mText.append("<br>");
mText += "</font>";
}
if (!event->location().isEmpty()) {
addTag("b",i18n("Location: "));
mText.append(event->location()+"<br>");
}
if (event->hasDueDate()) {
mText.append(i18n("<p><b>Due on:</b> %1</p>").arg(event->dtDueStr(shortDate)));
}
mText.append(i18n("<p><b>Priority:</b> %2</p>")
.arg(QString::number(event->priority())));
addTag("p",i18n("<b>Access: </b>") +event->secrecyStr() );
formatCategories(event);
if (!event->description().isEmpty()) {
addTag("p",i18n("<b>Details: </b>"));
addTag("p",event->description());
}
formatReadOnly(event);
formatAttendees(event);
}
void KIncidenceFormatter::setJournal(Journal* )
{
}
void KIncidenceFormatter::formatCategories(Incidence *event)
{
if (!event->categoriesStr().isEmpty()) {
addTag("p",i18n("<b>Categories: </b>")+event->categoriesStr() );
//mText.append(event->categoriesStr());
}
}
void KIncidenceFormatter::addTag(const QString & tag,const QString & text)
{
int number=text.contains("\n");
QString str = "<" + tag + ">";
QString tmpText=text;
QString tmpStr=str;
if(number !=-1)
{
if (number > 0) {
int pos=0;
QString tmp;
for(int i=0;i<=number;i++) {
pos=tmpText.find("\n");
tmp=tmpText.left(pos);
tmpText=tmpText.right(tmpText.length()-pos-1);
tmpStr+=tmp+"<br>";
}
}
else tmpStr += tmpText;
tmpStr+="</" + tag + ">";
mText.append(tmpStr);
}
else
{
str += text + "</" + tag + ">";
mText.append(str);
}
}
void KIncidenceFormatter::formatAttendees(Incidence *event)
{
QPtrList<Attendee> attendees = event->attendees();
if (attendees.count()) {
QString iconPath = KGlobal::iconLoader()->iconPath("mailappt",KIcon::Small);
QString NOiconPath = KGlobal::iconLoader()->iconPath("nomailappt",KIcon::Small);
addTag("h3",i18n("Organizer"));
mText.append("<ul><li>");
#if 0
//ndef KORG_NOKABC
KABC::AddressBook *add_book = KABC::StdAddressBook::self();
KABC::Addressee::List addressList;
addressList = add_book->findByEmail(event->organizer());
KABC::Addressee o = addressList.first();
if (!o.isEmpty() && addressList.size()<2) {
mText += "<a href=\"uid:" + o.uid() + "\">";
mText += o.formattedName();
mText += "</a>\n";
} else {
mText.append(event->organizer());
}
#else
mText.append(event->organizer());
#endif
if (iconPath) {
mText += " <a href=\"mailto:" + event->organizer() + "\">";
mText += "<IMG src=\"" + iconPath + "\">";
mText += "</a>\n";
}
mText.append("</li></ul>");
addTag("h3",i18n("Attendees"));
Attendee *a;
mText.append("<ul>");
for(a=attendees.first();a;a=attendees.next()) {
#if 0
//ndef KORG_NOKABC
if (a->name().isEmpty()) {
addressList = add_book->findByEmail(a->email());
KABC::Addressee o = addressList.first();
if (!o.isEmpty() && addressList.size()<2) {
mText += "<a href=\"uid:" + o.uid() + "\">";
mText += o.formattedName();
mText += "</a>\n";
} else {
mText += "<li>";
mText.append(a->email());
mText += "\n";
}
} else {
mText += "<li><a href=\"uid:" + a->uid() + "\">";
if (!a->name().isEmpty()) mText += a->name();
else mText += a->email();
mText += "</a>\n";
}
#else
//qDebug("nokabc ");
mText += "<li><a href=\"uid:" + a->uid() + "\">";
if (!a->name().isEmpty()) mText += a->name();
else mText += a->email();
mText += "</a>\n";
#endif
if (!a->email().isEmpty()) {
if (iconPath) {
mText += "<a href=\"mailto:" + a->name() +" "+ "<" + a->email() + ">" + "\">";
if ( a->RSVP() )
mText += "<IMG src=\"" + iconPath + "\">";
else
mText += "<IMG src=\"" + NOiconPath + "\">";
mText += "</a>\n";
}
}
if (a->status() != Attendee::NeedsAction )
mText +="[" + a->statusStr() + "] ";
if (a->role() == Attendee::Chair )
mText +="(" + a->roleStr().left(1) + ".)";
}
mText.append("</li></ul>");
}
}
void KIncidenceFormatter::formatReadOnly(Incidence *event)
{
if (event->isReadOnly()) {
addTag("p","<em>(" + i18n("read-only") + ")</em>");
}
}
diff --git a/libkcal/recurrence.cpp b/libkcal/recurrence.cpp
index e84f672..5181eaf 100644
--- a/libkcal/recurrence.cpp
+++ b/libkcal/recurrence.cpp
@@ -114,1823 +114,1825 @@ bool Recurrence::operator==( const Recurrence& r2 ) const
|| mRecurStart != r2.mRecurStart
|| mFloats != r2.mFloats
|| mRecurReadOnly != r2.mRecurReadOnly
|| mRecurExDatesCount != r2.mRecurExDatesCount )
return false;
// no need to compare mCompat* and mParent
// OK to compare the pointers
switch ( recurs )
{
case rWeekly:
return rDays == r2.rDays
&& rWeekStart == r2.rWeekStart;
case rMonthlyPos: {
QPtrList<rMonthPos> MonthPositions = rMonthPositions;
QPtrList<rMonthPos> MonthPositions2 = r2.rMonthPositions;
if ( !MonthPositions.count() )
return false;
if ( !MonthPositions2.count() )
return false;
return MonthPositions.first()->rPos == MonthPositions2.first()->rPos;
}
case rMonthlyDay: {
QPtrList<int> MonthDays = rMonthDays ;
QPtrList<int> MonthDays2 = r2.rMonthDays ;
if ( !MonthDays.count() )
return false;
if ( !MonthDays2.count() )
return false;
return *MonthDays.first() == *MonthDays2.first() ;
}
case rYearlyPos: {
QPtrList<int> YearNums = rYearNums;
QPtrList<int> YearNums2 = r2.rYearNums;
if ( *YearNums.first() != *YearNums2.first() )
return false;
QPtrList<rMonthPos> MonthPositions = rMonthPositions;
QPtrList<rMonthPos> MonthPositions2 = r2.rMonthPositions;
if ( !MonthPositions.count() )
return false;
if ( !MonthPositions2.count() )
return false;
return MonthPositions.first()->rPos == MonthPositions2.first()->rPos;
}
case rYearlyMonth: {
QPtrList<int> YearNums = rYearNums;
QPtrList<int> YearNums2 = r2.rYearNums;
return ( *YearNums.first() == *YearNums2.first() && mFeb29YearlyType == r2.mFeb29YearlyType);
}
case rYearlyDay: {
QPtrList<int> YearNums = rYearNums;
QPtrList<int> YearNums2 = r2.rYearNums;
return ( *YearNums.first() == *YearNums2.first() );
}
case rNone:
case rMinutely:
case rHourly:
case rDaily:
default:
return true;
}
}
/*
bool Recurrence::compareLists( const QPtrList<int> &l1 ,const QPtrList<int> &l2)
{
if ( l1.count() != l2.count() )
return false;
int count = l1.count();
int i;
for ( i = 0; i < count ; ++i ) {
// if ( l1.at(i) != l2.at(i) )
return false;
qDebug("compüare ");
}
return true;
}
*/
QString Recurrence::recurrenceText() const
{
QString recurText = i18n("No");
if ( recurs == Recurrence::rMinutely )
recurText = i18n("minutely");
else if ( recurs == Recurrence::rHourly )
recurText = i18n("hourly");
else if ( recurs == Recurrence::rDaily )
recurText = i18n("daily");
else if ( recurs == Recurrence::rWeekly )
recurText = i18n("weekly");
else if ( recurs == Recurrence::rMonthlyPos )
recurText = i18n("monthly");
else if ( recurs == Recurrence::rMonthlyDay )
recurText = i18n("day-monthly");
else if ( recurs == Recurrence::rYearlyMonth )
recurText = i18n("month-yearly");
else if ( recurs == Recurrence::rYearlyDay )
recurText = i18n("day-yearly");
else if ( recurs == Recurrence::rYearlyPos )
recurText = i18n("position-yearly");
return recurText;
}
void Recurrence::setCompatVersion(int version)
{
mCompatVersion = version ? version : INT_MAX;
}
ushort Recurrence::doesRecur() const
{
return recurs;
}
bool Recurrence::recursOnPure(const QDate &qd) const
{
switch(recurs) {
case rMinutely:
return recursSecondly(qd, rFreq*60);
case rHourly:
return recursSecondly(qd, rFreq*3600);
case rDaily:
return recursDaily(qd);
case rWeekly:
return recursWeekly(qd);
case rMonthlyPos:
case rMonthlyDay:
return recursMonthly(qd);
case rYearlyMonth:
return recursYearlyByMonth(qd);
case rYearlyDay:
return recursYearlyByDay(qd);
case rYearlyPos:
return recursYearlyByPos(qd);
default:
return false;
case rNone:
return false;
} // case
return false;
}
bool Recurrence::recursAtPure(const QDateTime &dt) const
{
switch(recurs) {
case rMinutely:
return recursMinutelyAt(dt, rFreq);
case rHourly:
return recursMinutelyAt(dt, rFreq*60);
default:
if (dt.time() != mRecurStart.time())
return false;
switch(recurs) {
case rDaily:
return recursDaily(dt.date());
case rWeekly:
return recursWeekly(dt.date());
case rMonthlyPos:
case rMonthlyDay:
return recursMonthly(dt.date());
case rYearlyMonth:
return recursYearlyByMonth(dt.date());
case rYearlyDay:
return recursYearlyByDay(dt.date());
case rYearlyPos:
return recursYearlyByPos(dt.date());
default:
return false;
case rNone:
return false;
}
} // case
return false;
}
QDate Recurrence::endDate() const
{
int count = 0;
QDate end;
if (recurs != rNone) {
if (rDuration < 0)
return QDate(); // infinite recurrence
if (rDuration == 0)
return rEndDateTime.date();
// The end date is determined by the recurrence count
QDate dStart = mRecurStart.date();
switch (recurs)
{
case rMinutely:
return mRecurStart.addSecs((rDuration-1+mRecurExDatesCount)*rFreq*60).date();
case rHourly:
return mRecurStart.addSecs((rDuration-1+mRecurExDatesCount)*rFreq*3600).date();
case rDaily:
return dStart.addDays((rDuration-1+mRecurExDatesCount)*rFreq);
case rWeekly:
count = weeklyCalc(END_DATE_AND_COUNT, end);
break;
case rMonthlyPos:
case rMonthlyDay:
count = monthlyCalc(END_DATE_AND_COUNT, end);
break;
case rYearlyMonth:
count = yearlyMonthCalc(END_DATE_AND_COUNT, end);
break;
case rYearlyDay:
count = yearlyDayCalc(END_DATE_AND_COUNT, end);
break;
case rYearlyPos:
count = yearlyPosCalc(END_DATE_AND_COUNT, end);
break;
default:
// catch-all. Should never get here.
kdDebug(5800) << "Control should never reach here in endDate()!" << endl;
break;
}
}
if (!count)
return QDate(); // error - there is no recurrence
return end;
}
QDateTime Recurrence::endDateTime() const
{
int count = 0;
QDate end;
if (recurs != rNone) {
if (rDuration < 0)
return QDateTime(); // infinite recurrence
if (rDuration == 0)
return rEndDateTime;
// The end date is determined by the recurrence count
QDate dStart = mRecurStart.date();
switch (recurs)
{
case rMinutely:
return mRecurStart.addSecs((rDuration-1+mRecurExDatesCount)*rFreq*60);
case rHourly:
return mRecurStart.addSecs((rDuration-1+mRecurExDatesCount)*rFreq*3600);
case rDaily:
return dStart.addDays((rDuration-1+mRecurExDatesCount)*rFreq);
case rWeekly:
count = weeklyCalc(END_DATE_AND_COUNT, end);
break;
case rMonthlyPos:
case rMonthlyDay:
count = monthlyCalc(END_DATE_AND_COUNT, end);
break;
case rYearlyMonth:
count = yearlyMonthCalc(END_DATE_AND_COUNT, end);
break;
case rYearlyDay:
count = yearlyDayCalc(END_DATE_AND_COUNT, end);
break;
case rYearlyPos:
count = yearlyPosCalc(END_DATE_AND_COUNT, end);
break;
default:
// catch-all. Should never get here.
kdDebug(5800) << "Control should never reach here in endDate()!" << endl;
break;
}
}
if (!count)
return QDateTime(); // error - there is no recurrence
return QDateTime(end, mRecurStart.time());
}
int Recurrence::durationTo(const QDate &date) const
{
QDate d = date;
return recurCalc(COUNT_TO_DATE, d);
}
int Recurrence::durationTo(const QDateTime &datetime) const
{
QDateTime dt = datetime;
return recurCalc(COUNT_TO_DATE, dt);
}
void Recurrence::unsetRecurs()
{
if (mRecurReadOnly) return;
recurs = rNone;
rMonthPositions.clear();
rMonthDays.clear();
rYearNums.clear();
}
void Recurrence::setRecurStart(const QDateTime &start)
{
mRecurStart = start;
mFloats = false;
switch (recurs)
{
case rMinutely:
case rHourly:
break;
case rDaily:
case rWeekly:
case rMonthlyPos:
case rMonthlyDay:
case rYearlyMonth:
case rYearlyDay:
case rYearlyPos:
default:
rEndDateTime.setTime(start.time());
break;
}
}
void Recurrence::setRecurStart(const QDate &start)
{
mRecurStart.setDate(start);
mRecurStart.setTime(QTime(0,0,0));
switch (recurs)
{
case rMinutely:
case rHourly:
break;
case rDaily:
case rWeekly:
case rMonthlyPos:
case rMonthlyDay:
case rYearlyMonth:
case rYearlyDay:
case rYearlyPos:
default:
mFloats = true;
break;
}
}
void Recurrence::setFloats(bool f)
{
switch (recurs)
{
case rDaily:
case rWeekly:
case rMonthlyPos:
case rMonthlyDay:
case rYearlyMonth:
case rYearlyDay:
case rYearlyPos:
break;
case rMinutely:
case rHourly:
default:
return; // can't set sub-daily to floating
}
mFloats = f;
if (f) {
mRecurStart.setTime(QTime(0,0,0));
rEndDateTime.setTime(QTime(0,0,0));
}
}
int Recurrence::frequency() const
{
return rFreq;
}
int Recurrence::duration() const
{
return rDuration;
}
void Recurrence::setDuration(int _rDuration)
{
if (mRecurReadOnly) return;
if (_rDuration > 0) {
rDuration = _rDuration;
// Compatibility mode is only needed when reading the calendar in ICalFormatImpl,
// so explicitly setting the duration means no backwards compatibility is needed.
mCompatDuration = 0;
}
}
QString Recurrence::endDateStr(bool shortfmt) const
{
return KGlobal::locale()->formatDate(rEndDateTime.date(),shortfmt);
}
const QBitArray &Recurrence::days() const
{
return rDays;
}
const QPtrList<Recurrence::rMonthPos> &Recurrence::monthPositions() const
{
return rMonthPositions;
}
const QPtrList<Recurrence::rMonthPos> &Recurrence::yearMonthPositions() const
{
return rMonthPositions;
}
const QPtrList<int> &Recurrence::monthDays() const
{
return rMonthDays;
}
void Recurrence::setMinutely(int _rFreq, int _rDuration)
{
if (mRecurReadOnly || _rDuration == 0 || _rDuration < -1)
return;
setDailySub(rMinutely, _rFreq, _rDuration);
}
void Recurrence::setMinutely(int _rFreq, const QDateTime &_rEndDateTime)
{
if (mRecurReadOnly) return;
rEndDateTime = _rEndDateTime;
setDailySub(rMinutely, _rFreq, 0);
}
void Recurrence::setHourly(int _rFreq, int _rDuration)
{
if (mRecurReadOnly || _rDuration == 0 || _rDuration < -1)
return;
setDailySub(rHourly, _rFreq, _rDuration);
}
void Recurrence::setHourly(int _rFreq, const QDateTime &_rEndDateTime)
{
if (mRecurReadOnly) return;
rEndDateTime = _rEndDateTime;
setDailySub(rHourly, _rFreq, 0);
}
void Recurrence::setDaily(int _rFreq, int _rDuration)
{
if (mRecurReadOnly || _rDuration == 0 || _rDuration < -1)
return;
setDailySub(rDaily, _rFreq, _rDuration);
}
void Recurrence::setDaily(int _rFreq, const QDate &_rEndDate)
{
if (mRecurReadOnly) return;
rEndDateTime.setDate(_rEndDate);
rEndDateTime.setTime(mRecurStart.time());
setDailySub(rDaily, _rFreq, 0);
}
void Recurrence::setWeekly(int _rFreq, const QBitArray &_rDays,
int _rDuration, int _rWeekStart)
{
if (mRecurReadOnly || _rDuration == 0 || _rDuration < -1)
return;
recurs = rWeekly;
rFreq = _rFreq;
rDays = _rDays;
rWeekStart = _rWeekStart;
rDuration = _rDuration;
if (mCompatVersion < 310 && _rDuration > 0) {
// Backwards compatibility for KDE < 3.1.
// rDuration was set to the number of time periods to recur,
// with week start always on a Monday.
// Convert this to the number of occurrences.
mCompatDuration = _rDuration;
int weeks = ((mCompatDuration-1+mRecurExDatesCount)*7) + (7 - mRecurStart.date().dayOfWeek());
QDate end(mRecurStart.date().addDays(weeks * rFreq));
rDuration = INT_MAX; // ensure that weeklyCalc() does its job correctly
rDuration = weeklyCalc(COUNT_TO_DATE, end);
} else {
mCompatDuration = 0;
}
rMonthPositions.clear();
rMonthDays.clear();
if (mParent) mParent->updated();
}
void Recurrence::setWeekly(int _rFreq, const QBitArray &_rDays,
const QDate &_rEndDate, int _rWeekStart)
{
if (mRecurReadOnly) return;
recurs = rWeekly;
rFreq = _rFreq;
rDays = _rDays;
rWeekStart = _rWeekStart;
rEndDateTime.setDate(_rEndDate);
rEndDateTime.setTime(mRecurStart.time());
rDuration = 0; // set to 0 because there is an end date
mCompatDuration = 0;
rMonthPositions.clear();
rMonthDays.clear();
rYearNums.clear();
if (mParent) mParent->updated();
}
void Recurrence::setMonthly(short type, int _rFreq, int _rDuration)
{
if (mRecurReadOnly || _rDuration == 0 || _rDuration < -1)
return;
recurs = type;
rFreq = _rFreq;
rDuration = _rDuration;
if (mCompatVersion < 310)
mCompatDuration = (_rDuration > 0) ? _rDuration : 0;
rYearNums.clear();
if (mParent) mParent->updated();
}
void Recurrence::setMonthly(short type, int _rFreq,
const QDate &_rEndDate)
{
if (mRecurReadOnly) return;
recurs = type;
rFreq = _rFreq;
rEndDateTime.setDate(_rEndDate);
rEndDateTime.setTime(mRecurStart.time());
rDuration = 0; // set to 0 because there is an end date
mCompatDuration = 0;
rYearNums.clear();
if (mParent) mParent->updated();
}
void Recurrence::addMonthlyPos(short _rPos, const QBitArray &_rDays)
{
if (recurs == rMonthlyPos)
addMonthlyPos_(_rPos, _rDays);
}
void Recurrence::addMonthlyPos_(short _rPos, const QBitArray &_rDays)
{
if (mRecurReadOnly
|| _rPos == 0 || _rPos > 5 || _rPos < -5) // invalid week number
return;
for (rMonthPos* it = rMonthPositions.first(); it; it = rMonthPositions.next()) {
int itPos = it->negative ? -it->rPos : it->rPos;
if (_rPos == itPos) {
// This week is already in the list.
// Combine the specified days with those in the list.
it->rDays |= _rDays;
if (mParent) mParent->updated();
return;
}
}
// Add the new position to the list
rMonthPos *tmpPos = new rMonthPos;
if (_rPos > 0) {
tmpPos->rPos = _rPos;
tmpPos->negative = false;
} else {
tmpPos->rPos = -_rPos; // take abs()
tmpPos->negative = true;
}
tmpPos->rDays = _rDays;
tmpPos->rDays.detach();
rMonthPositions.append(tmpPos);
if (mCompatVersion < 310 && mCompatDuration > 0) {
// Backwards compatibility for KDE < 3.1.
// rDuration was set to the number of time periods to recur.
// Convert this to the number of occurrences.
int monthsAhead = (mCompatDuration-1+mRecurExDatesCount) * rFreq;
int month = mRecurStart.date().month() - 1 + monthsAhead;
QDate end(mRecurStart.date().year() + month/12, month%12 + 1, 31);
rDuration = INT_MAX; // ensure that recurCalc() does its job correctly
rDuration = recurCalc(COUNT_TO_DATE, end);
}
if (mParent) mParent->updated();
}
void Recurrence::addMonthlyDay(short _rDay)
{
if (mRecurReadOnly || (recurs != rMonthlyDay && recurs != rYearlyMonth)
|| _rDay == 0 || _rDay > 31 || _rDay < -31) // invalid day number
return;
for (int* it = rMonthDays.first(); it; it = rMonthDays.next()) {
if (_rDay == *it)
return; // this day is already in the list - avoid duplication
}
int *tmpDay = new int;
*tmpDay = _rDay;
rMonthDays.append(tmpDay);
if (mCompatVersion < 310 && mCompatDuration > 0) {
// Backwards compatibility for KDE < 3.1.
// rDuration was set to the number of time periods to recur.
// Convert this to the number of occurrences.
int monthsAhead = (mCompatDuration-1+mRecurExDatesCount) * rFreq;
int month = mRecurStart.date().month() - 1 + monthsAhead;
QDate end(mRecurStart.date().year() + month/12, month%12 + 1, 31);
rDuration = INT_MAX; // ensure that recurCalc() does its job correctly
rDuration = recurCalc(COUNT_TO_DATE, end);
}
if (mParent) mParent->updated();
}
void Recurrence::setYearly(int type, int _rFreq, int _rDuration)
{
if (mRecurReadOnly || _rDuration == 0 || _rDuration < -1)
return;
if (mCompatVersion < 310)
mCompatDuration = (_rDuration > 0) ? _rDuration : 0;
setYearly_(type, mFeb29YearlyDefaultType, _rFreq, _rDuration);
}
void Recurrence::setYearly(int type, int _rFreq, const QDate &_rEndDate)
{
if (mRecurReadOnly) return;
rEndDateTime.setDate(_rEndDate);
rEndDateTime.setTime(mRecurStart.time());
mCompatDuration = 0;
setYearly_(type, mFeb29YearlyDefaultType, _rFreq, 0);
}
void Recurrence::setYearlyByDate(Feb29Type type, int _rFreq, int _rDuration)
{
if (mRecurReadOnly || _rDuration == 0 || _rDuration < -1)
return;
if (mCompatVersion < 310)
mCompatDuration = (_rDuration > 0) ? _rDuration : 0;
setYearly_(rYearlyMonth, type, _rFreq, _rDuration);
}
void Recurrence::setYearlyByDate(Feb29Type type, int _rFreq, const QDate &_rEndDate)
{
if (mRecurReadOnly) return;
rEndDateTime.setDate(_rEndDate);
rEndDateTime.setTime(mRecurStart.time());
mCompatDuration = 0;
setYearly_(rYearlyMonth, type, _rFreq, 0);
}
void Recurrence::addYearlyMonthPos(short _rPos, const QBitArray &_rDays)
{
if (recurs == rYearlyPos)
addMonthlyPos_(_rPos, _rDays);
}
const QPtrList<int> &Recurrence::yearNums() const
{
return rYearNums;
}
void Recurrence::addYearlyMonth(short _rPos )
{
if (mRecurReadOnly || recurs != rYearlyMonth) // invalid day/month number
return;
rMonthPos *tmpPos = new rMonthPos;
if ( _rPos > 0) {
tmpPos->rPos = _rPos;
tmpPos->negative = false;
} else {
tmpPos->rPos = -_rPos; // take abs()
tmpPos->negative = true;
}
rMonthPositions.append(tmpPos);
}
void Recurrence::addYearlyNum(short _rNum)
{
if (mRecurReadOnly
|| (recurs != rYearlyMonth && recurs != rYearlyDay && recurs != rYearlyPos)
|| _rNum <= 0) // invalid day/month number
return;
if (mCompatVersion < 310 && mCompatRecurs == rYearlyDay) {
// Backwards compatibility for KDE < 3.1.
// Dates were stored as day numbers, with a fiddle to take account of leap years.
// Convert the day number to a month.
if (_rNum <= 0 || _rNum > 366 || (_rNum == 366 && mRecurStart.date().daysInYear() < 366))
return; // invalid day number
_rNum = QDate(mRecurStart.date().year(), 1, 1).addDays(_rNum - 1).month();
} else
if ((recurs == rYearlyMonth || recurs == rYearlyPos) && _rNum > 12
|| recurs == rYearlyDay && _rNum > 366)
return; // invalid day number
uint i = 0;
for (int* it = rYearNums.first(); it && _rNum >= *it; it = rYearNums.next()) {
if (_rNum == *it)
return; // this day/month is already in the list - avoid duplication
++i;
}
int *tmpNum = new int;
*tmpNum = _rNum;
rYearNums.insert(i, tmpNum); // insert the day/month in a sorted position
if (mCompatVersion < 310 && mCompatDuration > 0) {
// Backwards compatibility for KDE < 3.1.
// rDuration was set to the number of time periods to recur.
// Convert this to the number of occurrences.
QDate end(mRecurStart.date().year() + (mCompatDuration-1+mRecurExDatesCount)*rFreq, 12, 31);
rDuration = INT_MAX; // ensure that recurCalc() does its job correctly
rDuration = recurCalc(COUNT_TO_DATE, end);
}
if (mParent) mParent->updated();
}
QDateTime Recurrence::getNextDateTime(const QDateTime &preDateTime, bool *last) const
{
if (last)
*last = false;
int freq;
switch (recurs)
{
case rMinutely:
freq = rFreq * 60;
break;
case rHourly:
freq = rFreq * 3600;
break;
case rDaily:
case rWeekly:
case rMonthlyPos:
case rMonthlyDay:
case rYearlyMonth:
case rYearlyDay:
case rYearlyPos: {
QDate preDate = preDateTime.date();
if (!mFloats && mRecurStart.time() > preDateTime.time())
preDate = preDate.addDays(-1);
return QDateTime(getNextDateNoTime(preDate, last), mRecurStart.time());
}
default:
return QDateTime();
}
// It's a sub-daily recurrence
if (preDateTime < mRecurStart)
return mRecurStart;
int count = mRecurStart.secsTo(preDateTime) / freq + 2;
if (rDuration > 0) {
if (count > rDuration)
return QDateTime();
if (last && count == rDuration)
*last = true;
}
QDateTime endtime = mRecurStart.addSecs((count - 1)*freq);
if (rDuration == 0) {
if (endtime > rEndDateTime)
return QDateTime();
if (last && endtime == rEndDateTime)
*last = true;
}
return endtime;
}
QDate Recurrence::getNextDate(const QDate &preDate, bool *last) const
{
if (last)
*last = false;
switch (recurs)
{
case rMinutely:
case rHourly:
return getNextDateTime(QDateTime(preDate, QTime(23,59,59)), last).date();
case rDaily:
case rWeekly:
case rMonthlyPos:
case rMonthlyDay:
case rYearlyMonth:
case rYearlyDay:
case rYearlyPos:
+ qDebug("Recurrence::getNextDate: MAY BE BROKEN ");
return getNextDateNoTime(preDate, last);
default:
return QDate();
}
}
QDateTime Recurrence::getPreviousDateTime(const QDateTime &afterDateTime, bool *last) const
{
if (last)
*last = false;
int freq;
switch (recurs)
{
case rMinutely:
freq = rFreq * 60;
break;
case rHourly:
freq = rFreq * 3600;
break;
case rDaily:
case rWeekly:
case rMonthlyPos:
case rMonthlyDay:
case rYearlyMonth:
case rYearlyDay:
case rYearlyPos: {
QDate afterDate = afterDateTime.date();
if (!mFloats && mRecurStart.time() < afterDateTime.time())
afterDate = afterDate.addDays(1);
return QDateTime(getPreviousDateNoTime(afterDate, last), mRecurStart.time());
}
default:
return QDateTime();
}
// It's a sub-daily recurrence
if (afterDateTime <= mRecurStart)
return QDateTime();
int count = (mRecurStart.secsTo(afterDateTime) - 1) / freq + 1;
if (rDuration > 0) {
if (count > rDuration)
count = rDuration;
if (last && count == rDuration)
*last = true;
}
QDateTime endtime = mRecurStart.addSecs((count - 1)*freq);
if (rDuration == 0) {
if (endtime > rEndDateTime)
endtime = rEndDateTime;
if (last && endtime == rEndDateTime)
*last = true;
}
return endtime;
}
QDate Recurrence::getPreviousDate(const QDate &afterDate, bool *last) const
{
if (last)
*last = false;
switch (recurs)
{
case rMinutely:
case rHourly:
return getPreviousDateTime(QDateTime(afterDate, QTime(0,0,0)), last).date();
case rDaily:
case rWeekly:
case rMonthlyPos:
case rMonthlyDay:
case rYearlyMonth:
case rYearlyDay:
case rYearlyPos:
return getPreviousDateNoTime(afterDate, last);
default:
return QDate();
}
}
/***************************** PROTECTED FUNCTIONS ***************************/
bool Recurrence::recursSecondly(const QDate &qd, int secondFreq) const
{
if ((qd >= mRecurStart.date()) &&
((rDuration > 0) && (qd <= endDate()) ||
((rDuration == 0) && (qd <= rEndDateTime.date())) ||
(rDuration == -1))) {
// The date queried falls within the range of the event.
if (secondFreq < 24*3600)
return true; // the event recurs at least once each day
int after = mRecurStart.secsTo(QDateTime(qd));
if (after / secondFreq != (after + 24*3600) / secondFreq)
return true;
}
return false;
}
bool Recurrence::recursMinutelyAt(const QDateTime &dt, int minuteFreq) const
{
if ((dt >= mRecurStart) &&
((rDuration > 0) && (dt <= endDateTime()) ||
((rDuration == 0) && (dt <= rEndDateTime)) ||
(rDuration == -1))) {
// The time queried falls within the range of the event.
if (((mRecurStart.secsTo(dt) / 60) % minuteFreq) == 0)
return true;
}
return false;
}
bool Recurrence::recursDaily(const QDate &qd) const
{
QDate dStart = mRecurStart.date();
if ((dStart.daysTo(qd) % rFreq) == 0) {
// The date is a day which recurs
if (qd >= dStart
&& ((rDuration > 0 && qd <= endDate()) ||
(rDuration == 0 && qd <= rEndDateTime.date()) ||
rDuration == -1)) {
// The date queried falls within the range of the event.
return true;
}
}
return false;
}
bool Recurrence::recursWeekly(const QDate &qd) const
{
QDate dStart = mRecurStart.date();
if ((dStart.daysTo(qd)/7) % rFreq == 0) {
// The date is in a week which recurs
if (qd >= dStart
&& ((rDuration > 0 && qd <= endDate()) ||
(rDuration == 0 && qd <= rEndDateTime.date()) ||
rDuration == -1)) {
// The date queried falls within the range of the event.
// check if the bits set match today.
int i = qd.dayOfWeek()-1;
if (rDays.testBit((uint) i))
return true;
}
}
return false;
}
bool Recurrence::recursMonthly(const QDate &qd) const
{
QDate dStart = mRecurStart.date();
int year = qd.year();
int month = qd.month();
int day = qd.day();
// calculate how many months ahead this date is from the original
// event's date
int monthsAhead = (year - dStart.year()) * 12 + (month - dStart.month());
if ((monthsAhead % rFreq) == 0) {
// The date is in a month which recurs
if (qd >= dStart
&& ((rDuration > 0 && qd <= endDate()) ||
(rDuration == 0 && qd <= rEndDateTime.date()) ||
rDuration == -1)) {
// The date queried falls within the range of the event.
QValueList<int> days;
int daysInMonth = qd.daysInMonth();
if (recurs == rMonthlyDay)
getMonthlyDayDays(days, daysInMonth);
else if (recurs == rMonthlyPos)
getMonthlyPosDays(days, daysInMonth, QDate(year, month, 1).dayOfWeek());
for (QValueList<int>::Iterator it = days.begin(); it != days.end(); ++it) {
if (*it == day)
return true;
}
// no dates matched
}
}
return false;
}
bool Recurrence::recursYearlyByMonth(const QDate &qd) const
{
QDate dStart = mRecurStart.date();
int startDay = dStart.day();
int qday = qd.day();
int qmonth = qd.month();
int qyear = qd.year();
bool match = (qday == startDay);
if (!match && startDay == 29 && dStart.month() == 2) {
// It's a recurrence on February 29th
switch (mFeb29YearlyType) {
case rFeb28:
if (qday == 28 && qmonth == 2 && !QDate::leapYear(qyear))
match = true;
break;
case rMar1:
if (qday == 1 && qmonth == 3 && !QDate::leapYear(qyear)) {
qmonth = 2;
match = true;
}
break;
case rFeb29:
break;
}
}
if (match) {
// The day of the month matches. Calculate how many years ahead
// this date is from the original event's date.
int yearsAhead = (qyear - dStart.year());
if (yearsAhead % rFreq == 0) {
// The date is in a year which recurs
if (qd >= dStart
&& ((rDuration > 0 && qd <= endDate()) ||
(rDuration == 0 && qd <= rEndDateTime.date()) ||
rDuration == -1)) {
// The date queried falls within the range of the event.
int i = qmonth;
for (QPtrListIterator<int> qlin(rYearNums); qlin.current(); ++qlin) {
if (i == *qlin.current())
return true;
}
}
}
}
return false;
}
bool Recurrence::recursYearlyByPos(const QDate &qd) const
{
QDate dStart = mRecurStart.date();
int year = qd.year();
int month = qd.month();
int day = qd.day();
// calculate how many years ahead this date is from the original
// event's date
int yearsAhead = (year - dStart.year());
if (yearsAhead % rFreq == 0) {
// The date is in a year which recurs
if (qd >= dStart
&& ((rDuration > 0 && qd <= endDate()) ||
(rDuration == 0 && qd <= rEndDateTime.date()) ||
rDuration == -1)) {
// The date queried falls within the range of the event.
for (QPtrListIterator<int> qlin(rYearNums); qlin.current(); ++qlin) {
if (month == *qlin.current()) {
// The month recurs
QValueList<int> days;
getMonthlyPosDays(days, qd.daysInMonth(), QDate(year, month, 1).dayOfWeek());
for (QValueList<int>::Iterator it = days.begin(); it != days.end(); ++it) {
if (*it == day)
return true;
}
}
}
}
}
return false;
}
bool Recurrence::recursYearlyByDay(const QDate &qd) const
{
QDate dStart = mRecurStart.date();
// calculate how many years ahead this date is from the original
// event's date
int yearsAhead = (qd.year() - dStart.year());
if (yearsAhead % rFreq == 0) {
// The date is in a year which recurs
if (qd >= dStart
&& ((rDuration > 0 && qd <= endDate()) ||
(rDuration == 0 && qd <= rEndDateTime.date()) ||
rDuration == -1)) {
// The date queried falls within the range of the event.
int i = qd.dayOfYear();
for (QPtrListIterator<int> qlin(rYearNums); qlin.current(); ++qlin) {
if (i == *qlin.current())
return true;
}
}
}
return false;
}
/* Get the date of the next recurrence, after the specified date.
* If 'last' is non-null, '*last' is set to true if the next recurrence is the
* last recurrence, else false.
* Reply = date of next recurrence, or invalid date if none.
*/
QDate Recurrence::getNextDateNoTime(const QDate &preDate, bool *last) const
{
+
if (last)
*last = false;
QDate dStart = mRecurStart.date();
if (preDate < dStart)
return dStart;
QDate earliestDate = preDate.addDays(1);
QDate nextDate;
switch (recurs) {
case rDaily:
nextDate = dStart.addDays((dStart.daysTo(preDate)/rFreq + 1) * rFreq);
break;
case rWeekly: {
QDate start = dStart.addDays(1 - dStart.dayOfWeek()); // start of week for dStart
int earliestDayOfWeek = earliestDate.dayOfWeek();
int weeksAhead = start.daysTo(earliestDate) / 7;
int notThisWeek = weeksAhead % rFreq; // zero if this week is a recurring week
weeksAhead -= notThisWeek; // latest week which recurred
int weekday = 0;
// First check for any remaining day this week, if this week is a recurring week
if (!notThisWeek)
weekday = getFirstDayInWeek(earliestDayOfWeek);
// Check for a day in the next scheduled week
if (!weekday && earliestDayOfWeek > 1)
weekday = getFirstDayInWeek(rWeekStart) + rFreq*7;
if (weekday)
nextDate = start.addDays(weeksAhead*7 + weekday - 1);
break;
}
case rMonthlyDay:
case rMonthlyPos: {
int startYear = dStart.year();
int startMonth = dStart.month(); // 1..12
int earliestYear = earliestDate.year();
int monthsAhead = (earliestYear - startYear)*12 + earliestDate.month() - startMonth;
int notThisMonth = monthsAhead % rFreq; // zero if this month is a recurring month
monthsAhead -= notThisMonth; // latest month which recurred
// Check for the first later day in the current month
if (!notThisMonth)
nextDate = getFirstDateInMonth(earliestDate);
if (!nextDate.isValid() && earliestDate.day() > 1) {
// Check for a day in the next scheduled month
int months = startMonth - 1 + monthsAhead + rFreq;
nextDate = getFirstDateInMonth(QDate(startYear + months/12, months%12 + 1, 1));
}
break;
}
case rYearlyMonth:
case rYearlyPos:
case rYearlyDay: {
int startYear = dStart.year();
int yearsAhead = earliestDate.year() - startYear;
int notThisYear = yearsAhead % rFreq; // zero if this year is a recurring year
yearsAhead -= notThisYear; // latest year which recurred
// Check for the first later date in the current year
if (!notThisYear)
nextDate = getFirstDateInYear(earliestDate);
// Check for a date in the next scheduled year
if (!nextDate.isValid() && earliestDate.dayOfYear() > 1)
nextDate = getFirstDateInYear(QDate(startYear + yearsAhead + rFreq, 1, 1));
break;
}
case rNone:
default:
return QDate();
}
if (rDuration >= 0 && nextDate.isValid()) {
// Check that the date found is within the range of the recurrence
QDate end = endDate();
if (nextDate > end)
return QDate();
if (last && nextDate == end)
*last = true;
}
return nextDate;
}
/* Get the date of the last previous recurrence, before the specified date.
* Reply = date of previous recurrence, or invalid date if none.
*/
QDate Recurrence::getPreviousDateNoTime(const QDate &afterDate, bool *last) const
{
if (last)
*last = false;
QDate dStart = mRecurStart.date();
QDate latestDate = afterDate.addDays(-1);
if (latestDate < dStart)
return QDate();
QDate prevDate;
switch (recurs) {
case rDaily:
prevDate = dStart.addDays((dStart.daysTo(latestDate) / rFreq) * rFreq);
break;
case rWeekly: {
QDate start = dStart.addDays(1 - dStart.dayOfWeek()); // start of week for dStart
int latestDayOfWeek = latestDate.dayOfWeek();
int weeksAhead = start.daysTo(latestDate) / 7;
int notThisWeek = weeksAhead % rFreq; // zero if this week is a recurring week
weeksAhead -= notThisWeek; // latest week which recurred
int weekday = 0;
// First check for any previous day this week, if this week is a recurring week
if (!notThisWeek)
weekday = getLastDayInWeek(latestDayOfWeek);
// Check for a day in the previous scheduled week
if (!weekday) {
int weekEnd = (rWeekStart + 5)%7 + 1;
if (latestDayOfWeek < weekEnd) {
if (!notThisWeek)
weeksAhead -= rFreq;
weekday = getLastDayInWeek(weekEnd);
}
}
if (weekday)
prevDate = start.addDays(weeksAhead*7 + weekday - 1);
break;
}
case rMonthlyDay:
case rMonthlyPos: {
int startYear = dStart.year();
int startMonth = dStart.month(); // 1..12
int latestYear = latestDate.year();
int monthsAhead = (latestYear - startYear)*12 + latestDate.month() - startMonth;
int notThisMonth = monthsAhead % rFreq; // zero if this month is a recurring month
monthsAhead -= notThisMonth; // latest month which recurred
// Check for the last earlier day in the current month
if (!notThisMonth)
prevDate = getLastDateInMonth(latestDate);
if (!prevDate.isValid() && latestDate.day() < latestDate.daysInMonth()) {
// Check for a day in the previous scheduled month
if (!notThisMonth)
monthsAhead -= rFreq;
int months = startMonth + monthsAhead; // get the month after the one that recurs
prevDate = getLastDateInMonth(QDate(startYear + months/12, months%12 + 1, 1).addDays(-1));
}
break;
}
case rYearlyMonth:
case rYearlyPos:
case rYearlyDay: {
int startYear = dStart.year();
int yearsAhead = latestDate.year() - startYear;
int notThisYear = yearsAhead % rFreq; // zero if this year is a recurring year
yearsAhead -= notThisYear; // latest year which recurred
// Check for the first later date in the current year
if (!notThisYear)
prevDate = getLastDateInYear(latestDate);
if (!prevDate.isValid() && latestDate.dayOfYear() < latestDate.daysInYear()) {
// Check for a date in the next scheduled year
if (!notThisYear)
yearsAhead -= rFreq;
prevDate = getLastDateInYear(QDate(startYear + yearsAhead, 12, 31));
}
break;
}
case rNone:
default:
return QDate();
}
if (prevDate.isValid()) {
// Check that the date found is within the range of the recurrence
if (prevDate < dStart)
return QDate();
if (rDuration >= 0) {
QDate end = endDate();
if (prevDate >= end) {
if (last)
*last = true;
return end;
}
}
}
return prevDate;
}
void Recurrence::setDailySub(short type, int freq, int duration)
{
recurs = type;
rFreq = freq;
rDuration = duration;
rMonthPositions.clear();
rMonthDays.clear();
rYearNums.clear();
if (type != rDaily)
mFloats = false; // sub-daily types can't be floating
if (mParent) mParent->updated();
}
void Recurrence::setYearly_(short type, Feb29Type feb29type, int freq, int duration)
{
recurs = type;
if (mCompatVersion < 310 && type == rYearlyDay) {
mCompatRecurs = rYearlyDay;
recurs = rYearlyMonth; // convert old yearly-by-day to yearly-by-month
feb29type = rMar1; // retain the same day number in the year
}
mFeb29YearlyType = feb29type;
rFreq = freq;
rDuration = duration;
if (type != rYearlyPos)
rMonthPositions.clear();
rMonthDays.clear();
if (mParent) mParent->updated();
}
int Recurrence::recurCalc(PeriodFunc func, QDateTime &endtime) const
{
QDate enddate = endtime.date();
switch (func) {
case END_DATE_AND_COUNT:
if (rDuration < 0) {
endtime = QDateTime();
return 0; // infinite recurrence
}
if (rDuration == 0) {
endtime = rEndDateTime;
func = COUNT_TO_DATE;
}
break;
case COUNT_TO_DATE:
// Count recurrences up to and including the specified date/time.
if (endtime < mRecurStart)
return 0;
if (rDuration == 0 && endtime > rEndDateTime)
enddate = rEndDateTime.date();
else if (!mFloats && mRecurStart.time() > endtime.time())
enddate = enddate.addDays(-1);
break;
case NEXT_AFTER_DATE:
// Find next recurrence AFTER endtime
if (endtime < mRecurStart) {
endtime = mRecurStart;
return 1;
}
if (rDuration == 0 && endtime >= rEndDateTime) {
endtime = QDateTime();
return 0;
}
if (!mFloats && mRecurStart.time() > endtime.time())
enddate = enddate.addDays(-1);
break;
default:
endtime = QDateTime();
return 0;
}
int count = 0; // default = error
bool timed = false;
switch (recurs) {
case rMinutely:
timed = true;
count = secondlyCalc(func, endtime, rFreq*60);
break;
case rHourly:
timed = true;
count = secondlyCalc(func, endtime, rFreq*3600);
break;
case rDaily:
count = dailyCalc(func, enddate);
break;
case rWeekly:
count = weeklyCalc(func, enddate);
break;
case rMonthlyPos:
case rMonthlyDay:
count = monthlyCalc(func, enddate);
break;
case rYearlyMonth:
count = yearlyMonthCalc(func, enddate);
break;
case rYearlyPos:
count = yearlyPosCalc(func, enddate);
break;
case rYearlyDay:
count = yearlyDayCalc(func, enddate);
break;
default:
break;
}
switch (func) {
case END_DATE_AND_COUNT:
case NEXT_AFTER_DATE:
if (count == 0)
endtime = QDateTime();
else if (!timed) {
endtime.setDate(enddate);
endtime.setTime(mRecurStart.time());
}
break;
case COUNT_TO_DATE:
break;
}
return count;
}
int Recurrence::recurCalc(PeriodFunc func, QDate &enddate) const
{
QDateTime endtime(enddate, QTime(23,59,59));
switch (func) {
case END_DATE_AND_COUNT:
if (rDuration < 0) {
enddate = QDate();
return 0; // infinite recurrence
}
if (rDuration == 0) {
enddate = rEndDateTime.date();
func = COUNT_TO_DATE;
}
break;
case COUNT_TO_DATE:
// Count recurrences up to and including the specified date.
if (enddate < mRecurStart.date())
return 0;
if (rDuration == 0 && enddate > rEndDateTime.date()) {
enddate = rEndDateTime.date();
endtime.setDate(enddate);
}
break;
case NEXT_AFTER_DATE:
if (enddate < mRecurStart.date()) {
enddate = mRecurStart.date();
return 1;
}
if (rDuration == 0 && enddate >= rEndDateTime.date()) {
enddate = QDate();
return 0;
}
break;
default:
enddate = QDate();
return 0;
}
int count = 0; // default = error
bool timed = false;
switch (recurs) {
case rMinutely:
timed = true;
count = secondlyCalc(func, endtime, rFreq*60);
break;
case rHourly:
timed = true;
count = secondlyCalc(func, endtime, rFreq*3600);
break;
case rDaily:
count = dailyCalc(func, enddate);
break;
case rWeekly:
count = weeklyCalc(func, enddate);
break;
case rMonthlyPos:
case rMonthlyDay:
count = monthlyCalc(func, enddate);
break;
case rYearlyMonth:
count = yearlyMonthCalc(func, enddate);
break;
case rYearlyPos:
count = yearlyPosCalc(func, enddate);
break;
case rYearlyDay:
count = yearlyDayCalc(func, enddate);
break;
default:
break;
}
switch (func) {
case END_DATE_AND_COUNT:
case NEXT_AFTER_DATE:
if (count == 0)
endtime = QDate();
else if (timed)
enddate = endtime.date();
break;
case COUNT_TO_DATE:
break;
}
return count;
}
/* Find count and, depending on 'func', the end date/time of a secondly recurrence.
* Reply = total number of occurrences up to 'endtime', or 0 if error.
* If 'func' = END_DATE_AND_COUNT or NEXT_AFTER_DATE, 'endtime' is updated to the
* recurrence end date/time.
*/
int Recurrence::secondlyCalc(PeriodFunc func, QDateTime &endtime, int freq) const
{
switch (func) {
case END_DATE_AND_COUNT:
endtime = mRecurStart.addSecs((rDuration + mRecurExDatesCount - 1) * freq);
return rDuration + mRecurExDatesCount;
case COUNT_TO_DATE: {
int n = mRecurStart.secsTo(endtime)/freq + 1;
if (rDuration > 0 && n > rDuration + mRecurExDatesCount)
return rDuration + mRecurExDatesCount;
return n;
}
case NEXT_AFTER_DATE: {
int count = mRecurStart.secsTo(endtime) / freq + 2;
if (rDuration > 0 && count > rDuration)
return 0;
endtime = mRecurStart.addSecs((count - 1)*freq);
return count;
}
}
return 0;
}
/* Find count and, depending on 'func', the end date of a daily recurrence.
* Reply = total number of occurrences up to 'enddate', or 0 if error.
* If 'func' = END_DATE_AND_COUNT or NEXT_AFTER_DATE, 'enddate' is updated to the
* recurrence end date.
*/
int Recurrence::dailyCalc(PeriodFunc func, QDate &enddate) const
{
QDate dStart = mRecurStart.date();
switch (func) {
case END_DATE_AND_COUNT:
enddate = dStart.addDays((rDuration + mRecurExDatesCount - 1) * rFreq);
return rDuration + mRecurExDatesCount;
case COUNT_TO_DATE: {
int n = dStart.daysTo(enddate)/rFreq + 1;
if (rDuration > 0 && n > rDuration + mRecurExDatesCount)
return rDuration + mRecurExDatesCount;
return n;
}
case NEXT_AFTER_DATE: {
int count = dStart.daysTo(enddate) / rFreq + 2;
if (rDuration > 0 && count > rDuration)
return 0;
enddate = dStart.addDays((count - 1)*rFreq);
return count;
}
}
return 0;
}
/* Find count and, depending on 'func', the end date of a weekly recurrence.
* Reply = total number of occurrences up to 'enddate', or 0 if error.
* If 'func' = END_DATE_AND_COUNT or NEXT_AFTER_DATE, 'enddate' is updated to the
* recurrence end date.
*/
int Recurrence::weeklyCalc(PeriodFunc func, QDate &enddate) const
{
int daysPerWeek = 0;
for (int i = 0; i < 7; ++i) {
if (rDays.testBit((uint)i))
++daysPerWeek;
}
if (!daysPerWeek)
return 0; // there are no days to recur on
switch (func) {
case END_DATE_AND_COUNT:
return weeklyCalcEndDate(enddate, daysPerWeek);
case COUNT_TO_DATE:
return weeklyCalcToDate(enddate, daysPerWeek);
case NEXT_AFTER_DATE:
return weeklyCalcNextAfter(enddate, daysPerWeek);
}
return 0;
}
int Recurrence::weeklyCalcEndDate(QDate &enddate, int daysPerWeek) const
{
int startDayOfWeek = mRecurStart.date().dayOfWeek(); // 1..7
int countGone = 0;
int daysGone = 0;
uint countTogo = rDuration + mRecurExDatesCount;
if (startDayOfWeek != rWeekStart) {
// Check what remains of the start week
for (int i = startDayOfWeek - 1; i != rWeekStart - 1; i = (i + 1) % 7) {
++daysGone;
if (rDays.testBit((uint)i)) {
++countGone;
if (--countTogo == 0)
break;
}
}
daysGone += 7 * (rFreq - 1);
}
if (countTogo) {
// Skip the remaining whole weeks
// Leave at least 1 recurrence remaining, in order to get its date
int wholeWeeks = (countTogo - 1) / daysPerWeek;
daysGone += wholeWeeks * 7 * rFreq;
countGone += wholeWeeks * daysPerWeek;
countTogo -= wholeWeeks * daysPerWeek;
// Check the last week in the recurrence
for (int i = rWeekStart - 1; ; i = (i + 1) % 7) {
++daysGone;
if (rDays.testBit((uint)i)) {
++countGone;
if (--countTogo == 0)
break;
}
}
}
enddate = mRecurStart.date().addDays(daysGone);
return countGone;
}
int Recurrence::weeklyCalcToDate(const QDate &enddate, int daysPerWeek) const
{
QDate dStart = mRecurStart.date();
int startDayOfWeek = dStart.dayOfWeek(); // 1..7
int countGone = 0;
int daysGone = 0;
int totalDays = dStart.daysTo(enddate) + 1;
int countMax = (rDuration > 0) ? rDuration + mRecurExDatesCount : INT_MAX;
if (startDayOfWeek != rWeekStart) {
// Check what remains of the start week
for (int i = startDayOfWeek - 1; i != rWeekStart - 1; i = (i + 1) % 7) {
if (rDays.testBit((uint)i)) {
if (++countGone >= countMax)
return countMax;
}
if (++daysGone == totalDays)
return countGone;
}
daysGone += 7 * (rFreq - 1);
if (daysGone >= totalDays)
return countGone;
}
// Skip the remaining whole weeks
int wholeWeeks = (totalDays - daysGone) / 7;
countGone += (wholeWeeks / rFreq) * daysPerWeek;
if (countGone >= countMax)
return countMax;
daysGone += wholeWeeks * 7;
if (daysGone >= totalDays // have we reached the end date?
|| wholeWeeks % rFreq) // is end week a recurrence week?
return countGone;
// Check the last week in the recurrence
for (int i = rWeekStart - 1; ; i = (i + 1) % 7) {
if (rDays.testBit((uint)i)) {
if (++countGone >= countMax)
return countMax;
}
if (++daysGone == totalDays)
return countGone;
}
return countGone;
}
int Recurrence::weeklyCalcNextAfter(QDate &enddate, int daysPerWeek) const
{
QDate dStart = mRecurStart.date();
int startDayOfWeek = dStart.dayOfWeek(); // 1..7
int totalDays = dStart.daysTo(enddate) + 1;
uint countTogo = (rDuration > 0) ? rDuration + mRecurExDatesCount : UINT_MAX;
int countGone = 0;
int daysGone = 0;
int recurWeeks;
if (startDayOfWeek != rWeekStart) {
// Check what remains of the start week
for (int i = startDayOfWeek - 1; i != rWeekStart - 1; i = (i + 1) % 7) {
++daysGone;
if (rDays.testBit((uint)i)) {
++countGone;
if (daysGone > totalDays)
goto ex;
if (--countTogo == 0)
return 0;
}
}
daysGone += 7 * (rFreq - 1);
}
// Skip the remaining whole weeks
recurWeeks = (totalDays - daysGone) / (7 * rFreq);
if (recurWeeks) {
int n = recurWeeks * daysPerWeek;
if (static_cast<uint>(n) > countTogo)
return 0; // reached end of recurrence
countGone += n;
countTogo -= n;
daysGone += recurWeeks * 7 * rFreq;
}
// Check the last week or two in the recurrence
for ( ; ; ) {
for (int i = rWeekStart - 1; ; i = (i + 1) % 7) {
++daysGone;
if (rDays.testBit((uint)i)) {
++countGone;
if (daysGone > totalDays)
goto ex;
if (--countTogo == 0)
return 0;
}
}
daysGone += 7 * (rFreq - 1);
}
ex:
enddate = dStart.addDays(daysGone);
return countGone;
}
/* Find count and, depending on 'func', the end date of a monthly recurrence.
* Reply = total number of occurrences up to 'enddate', or 0 if error.
* If 'func' = END_DATE_AND_COUNT or NEXT_AFTER_DATE, 'enddate' is updated to the
* recurrence end date.
*/
struct Recurrence::MonthlyData {
const Recurrence *recurrence;
int year; // current year
int month; // current month 0..11
int day; // current day of month 1..31
bool varies; // true if recurring days vary between different months
private:
QValueList<int> days28, days29, days30, days31; // recurring days in months of each length
QValueList<int> *recurDays[4];
public:
MonthlyData(const Recurrence* r, const QDate &date)
: recurrence(r), year(date.year()), month(date.month()-1), day(date.day())
{ recurDays[0] = &days28;
recurDays[1] = &days29;
recurDays[2] = &days30;
recurDays[3] = &days31;
varies = (recurrence->recurs == rMonthlyPos)
? true : recurrence->getMonthlyDayDays(days31, 31);
}
const QValueList<int>* dayList() const {
if (!varies)
return &days31;
QDate startOfMonth(year, month + 1, 1);
int daysInMonth = startOfMonth.daysInMonth();
QValueList<int>* days = recurDays[daysInMonth - 28];
if (recurrence->recurs == rMonthlyPos)
recurrence->getMonthlyPosDays(*days, daysInMonth, startOfMonth.dayOfWeek());
else if (days->isEmpty())
recurrence->getMonthlyDayDays(*days, daysInMonth);
return days;
}
int yearMonth() const { return year*12 + month; }
void addMonths(int diff) { month += diff; year += month / 12; month %= 12; }
QDate date() const { return QDate(year, month + 1, day); }
};
int Recurrence::monthlyCalc(PeriodFunc func, QDate &enddate) const
{
if (recurs == rMonthlyPos && rMonthPositions.isEmpty()
|| recurs == rMonthlyDay && rMonthDays.isEmpty())
return 0;
MonthlyData data(this, mRecurStart.date());
switch (func) {
case END_DATE_AND_COUNT:
return monthlyCalcEndDate(enddate, data);
case COUNT_TO_DATE:
return monthlyCalcToDate(enddate, data);
case NEXT_AFTER_DATE:
return monthlyCalcNextAfter(enddate, data);
}
return 0;
}
int Recurrence::monthlyCalcEndDate(QDate &enddate, MonthlyData &data) const
{
uint countTogo = rDuration + mRecurExDatesCount;
int countGone = 0;
QValueList<int>::ConstIterator it;
const QValueList<int>* days = data.dayList();
if (data.day > 1) {
// Check what remains of the start month
for (it = days->begin(); it != days->end(); ++it) {
if (*it >= data.day) {
++countGone;
if (--countTogo == 0) {
data.day = *it;
break;
}
}
}
if (countTogo) {
data.day = 1;
data.addMonths(rFreq);
}
}
if (countTogo) {
if (data.varies) {
// The number of recurrence days varies from month to month,
// so we need to check month by month.
for ( ; ; ) {
days = data.dayList();
uint n = days->count(); // number of recurrence days in this month
if (n >= countTogo)
break;
countTogo -= n;
countGone += n;
data.addMonths(rFreq);
}
} else {
// The number of recurrences is the same every month,
// so skip the month-by-month check.
// Skip the remaining whole months, but leave at least
// 1 recurrence remaining, in order to get its date.
int daysPerMonth = days->count();
int wholeMonths = (countTogo - 1) / daysPerMonth;
data.addMonths(wholeMonths * rFreq);
countGone += wholeMonths * daysPerMonth;
countTogo -= wholeMonths * daysPerMonth;
}
if (countTogo) {
// Check the last month in the recurrence
for (it = days->begin(); it != days->end(); ++it) {
++countGone;
if (--countTogo == 0) {
data.day = *it;
break;
}
}
}
}
enddate = data.date();
return countGone;
}
int Recurrence::monthlyCalcToDate(const QDate &enddate, MonthlyData &data) const
{
int countGone = 0;
int countMax = (rDuration > 0) ? rDuration + mRecurExDatesCount : INT_MAX;
int endYear = enddate.year();
int endMonth = enddate.month() - 1; // zero-based
int endDay = enddate.day();
int endYearMonth = endYear*12 + endMonth;
QValueList<int>::ConstIterator it;
const QValueList<int>* days = data.dayList();
if (data.day > 1) {
// Check what remains of the start month
for (it = days->begin(); it != days->end(); ++it) {
if (*it >= data.day) {
if (data.yearMonth() == endYearMonth && *it > endDay)
return countGone;
if (++countGone >= countMax)
return countMax;
}
}
data.day = 1;
data.addMonths(rFreq);
}
if (data.varies) {
// The number of recurrence days varies from month to month,
// so we need to check month by month.
while (data.yearMonth() < endYearMonth) {
countGone += data.dayList()->count();
if (countGone >= countMax)
return countMax;
data.addMonths(rFreq);
}
days = data.dayList();
} else {
// The number of recurrences is the same every month,