/*************************************************************************** * Copyright (C) 2006, 2007 by Niklas Knutsson * * nq@altern.org * * * * This program is free software; you can redistribute it and/or modify * * it under the terms of the GNU General Public License as published by * * the Free Software Foundation; either version 2 of the License, or * * (at your option) any later version. * * * * This program 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 General Public License for more details. * * * * You should have received a copy of the GNU General Public License * * along with this program; if not, write to the * * Free Software Foundation, Inc., * * 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA. * ***************************************************************************/ #ifdef HAVE_CONFIG_H # include #endif #include "recurrence.h" #include "budget.h" #include #include #include #include extern QDate addMonths(const QDate &date, int nmonths); extern QDate addYears(const QDate &date, int nyears); int months_between_dates(const QDate &date1, const QDate &date2) { const KCalendarSystem *calSys = KGlobal::locale()->calendar(); if(calSys->year(date1) == calSys->year(date2)) { return calSys->month(date2) - calSys->month(date1); } int months = calSys->monthsInYear(date1) - calSys->month(date1); QDate yeardate; calSys->setYMD(yeardate, calSys->year(date1), 1, 1); yeardate = addYears(yeardate, 1); while(calSys->year(yeardate) != calSys->year(date2)) { months += calSys->monthsInYear(yeardate); yeardate = addYears(yeardate, 1); } months += calSys->month(date2); return months; } int weeks_between_dates(const QDate &date1, const QDate &date2) { const KCalendarSystem *calSys = KGlobal::locale()->calendar(); return (calSys->dayOfWeek(date1) - calSys->dayOfWeek(date2) + date1.daysTo(date2)) / 7; } int get_day_in_month(const QDate &date, int week, int day_of_week) { const KCalendarSystem *calSys = KGlobal::locale()->calendar(); if(week > 0) { int fwd = calSys->dayOfWeek(date); fwd -= calSys->day(date) % 7 - 1; if(fwd <= 0) fwd = 7 + fwd; int day = 1; if(fwd < day_of_week) { day += day_of_week - fwd; } else if(fwd > day_of_week) { day += 7 - (fwd - day_of_week); } day += (week - 1) * 7; if(day > calSys->daysInMonth(date)) return 0; return day; } else { int lwd = calSys->dayOfWeek(date); int day = calSys->daysInMonth(date); lwd += (day - calSys->day(date)) % 7; if(lwd > 7) lwd = lwd - 7; if(lwd > day_of_week) { day -= lwd - day_of_week; } else if(lwd < day_of_week) { day -= 7 - (day_of_week - lwd); } day -= (-week) * 7; if(day < 1) return 0; return day; } } int get_day_in_month(int year, int month, int week, int day_of_week) { const KCalendarSystem *calSys = KGlobal::locale()->calendar(); QDate date; calSys->setYMD(date, year, month, 1); return get_day_in_month(date, week, day_of_week); } Recurrence::Recurrence(Budget *parent_budget) : o_budget(parent_budget) { i_count = -1; } Recurrence::Recurrence(Budget *parent_budget, QDomElement *e, bool *valid) : o_budget(parent_budget) { if(valid) *valid = true; d_startdate = QDate::fromString(e->attribute("startdate"), Qt::ISODate); if(!e->attribute("enddate").isEmpty()) { d_enddate = QDate::fromString(e->attribute("enddate"), Qt::ISODate); } i_count = e->attribute("recurrences", "-1").toInt(); if(valid && !d_startdate.isValid()) { *valid = false; } if(!d_enddate.isValid() && !d_enddate.isNull()) d_enddate = QDate(); for(QDomNode n = e->firstChild(); !n.isNull(); n = n.nextSibling()) { if(n.isElement()) { QDomElement e2 = n.toElement(); if(e2.tagName() == "exception") { QDate date = QDate::fromString(e2.attribute("date"), Qt::ISODate); if(date.isValid() && date >= d_startdate && (d_enddate.isNull() || date <= d_enddate)) { exceptions.append(date); } } } } qHeapSort(exceptions); } Recurrence::Recurrence(const Recurrence *rec) : o_budget(rec->budget()), d_startdate(rec->startDate()), d_enddate(rec->endDate()), i_count(rec->fixedOccurrenceCount()), exceptions(rec->exceptions) {} Recurrence::~Recurrence() {} void Recurrence::updateDates() { if(!d_startdate.isValid()) return; if(!d_enddate.isNull() && i_count <= 0) { d_enddate = prevOccurrence(d_enddate, true); while(exceptions.count() > 0) { if(exceptions.last() == d_enddate) { d_enddate = prevOccurrence(d_enddate, false); exceptions.pop_back(); } else if(exceptions.last() > d_enddate) { exceptions.pop_back(); } else { break; } } } //d_startdate = nextOccurrence(d_startdate, true); while(exceptions.count() > 0) { if(exceptions.first() == d_startdate) { d_startdate = nextOccurrence(d_startdate, false); exceptions.erase(exceptions.begin()); } else if(exceptions.first() < d_startdate) { exceptions.erase(exceptions.begin()); } else { break; } } if(i_count > 0) { d_enddate = QDate(); QDate new_enddate = d_startdate; for(int i = 1; i < i_count; i++) { new_enddate = nextOccurrence(new_enddate); if(new_enddate.isNull()) { i_count = i; break; } d_enddate = new_enddate; } if(d_enddate.isNull()) { d_enddate = d_startdate; } } if(d_enddate.isNull() && nextOccurrence(d_startdate).isNull()) { d_enddate = d_startdate; } } QDate Recurrence::firstOccurrence() const { return d_startdate; } QDate Recurrence::lastOccurrence() const { return d_enddate; } int Recurrence::countOccurrences(const QDate &startdate, const QDate &enddate) const { if(enddate < d_startdate) return 0; if(!d_enddate.isNull() && startdate > d_enddate) return 0; if(i_count > 0 && startdate <= d_startdate && enddate <= d_enddate) return i_count; QDate date1 = nextOccurrence(startdate, true); if(date1.isNull() || date1 > enddate) return 0; int n = 0; do { n++; date1 = nextOccurrence(date1); } while(!date1.isNull() && date1 <= enddate); return n; } int Recurrence::countOccurrences(const QDate &enddate) const { return countOccurrences(d_startdate, enddate); } bool Recurrence::removeOccurrence(const QDate &date) { addException(date); return true; } const QDate &Recurrence::endDate() const { return d_enddate; } const QDate &Recurrence::startDate() const { return d_startdate; } void Recurrence::setEndDate(const QDate &new_end_date) { i_count = -1; d_enddate = new_end_date; if(!new_end_date.isNull()) { d_enddate = prevOccurrence(d_enddate, true); while(exceptions.count() > 0) { if(exceptions.last() == d_enddate) { d_enddate = prevOccurrence(d_enddate, false); exceptions.pop_back(); } else if(exceptions.last() > d_enddate) { exceptions.pop_back(); } else { break; } } } else if(nextOccurrence(d_startdate).isNull()) { d_enddate = d_startdate; } } void Recurrence::setStartDate(const QDate &new_start_date) { d_startdate = new_start_date; bool set_end_date = false; if(!d_enddate.isNull() && d_startdate > d_enddate) { d_enddate = QDate(); set_end_date = true; } //d_startdate = nextOccurrence(d_startdate, true); while(exceptions.count() > 0) { if(exceptions.first() == d_startdate) { d_startdate = nextOccurrence(d_startdate, false); exceptions.erase(exceptions.begin()); } else if(exceptions.first() < d_startdate) { exceptions.erase(exceptions.begin()); } else { break; } } if(i_count > 0) setFixedOccurrenceCount(i_count); else if(set_end_date) d_enddate = d_startdate; if(d_enddate.isNull() && nextOccurrence(d_startdate).isNull()) { d_enddate = d_startdate; } } int Recurrence::fixedOccurrenceCount() const { return i_count; } void Recurrence::setFixedOccurrenceCount(int new_count) { if(new_count <= 0) { i_count = -1; setEndDate(d_enddate); } else { i_count = new_count; d_enddate = QDate(); QDate new_enddate = d_startdate; for(int i = 1; i < i_count; i++) { new_enddate = nextOccurrence(new_enddate); if(new_enddate.isNull()) { i_count = i; break; } d_enddate = new_enddate; } if(d_enddate.isNull()) { d_enddate = d_startdate; } } } void Recurrence::addException(const QDate &date) { if(hasException(date) || !date.isValid()) return; if(date == d_startdate) { d_startdate = nextOccurrence(d_startdate); if(d_startdate.isNull()) d_enddate = QDate(); return; } if(date == d_enddate) { d_enddate = prevOccurrence(d_enddate); if(d_enddate.isNull()) d_startdate = QDate(); return; } exceptions.append(date); qHeapSort(exceptions); } int Recurrence::findException(const QDate &date) const { for(QValueVector::size_type i = 0; i < exceptions.count(); i++) { if(exceptions[i] == date) { return (int) i; } if(exceptions[i] > date) break; } return -1; } bool Recurrence::hasException(const QDate &date) const { return findException(date) >= 0; } bool Recurrence::removeException(const QDate &date) { for(QValueVector::iterator it = exceptions.begin(); it != exceptions.end(); ++it) { if(*it == date) { exceptions.erase(it); return true; } if(*it > date) break; } return false; } void Recurrence::clearExceptions() { exceptions.clear(); } Budget *Recurrence::budget() const {return o_budget;} void Recurrence::save(QDomElement *e) const { e->setAttribute("startdate", d_startdate.toString(Qt::ISODate)); if(d_enddate.isValid()) e->setAttribute("enddate", d_enddate.toString(Qt::ISODate)); for(QValueVector::size_type i = 0; i < exceptions.count(); i++) { QDomElement e2 = e->ownerDocument().createElement("exception"); e2.setAttribute("date", exceptions[i].toString(Qt::ISODate)); e->appendChild(e2); } } DailyRecurrence::DailyRecurrence(Budget *parent_budget) : Recurrence(parent_budget) { i_frequency = 1; } DailyRecurrence::DailyRecurrence(Budget *parent_budget, QDomElement *e, bool *valid) : Recurrence(parent_budget, e, valid) { i_frequency = e->attribute("frequency", "1").toInt(); if(valid && i_frequency < 1) { *valid = false; } updateDates(); } DailyRecurrence::DailyRecurrence(const DailyRecurrence *rec) : Recurrence(rec), i_frequency(rec->frequency()) {} DailyRecurrence::~DailyRecurrence() {} Recurrence *DailyRecurrence::copy() const {return new DailyRecurrence(this);} QDate DailyRecurrence::nextOccurrence(const QDate &date, bool include_equals) const { const KCalendarSystem *calSys = KGlobal::locale()->calendar(); if(include_equals) { if(date == startDate()) return date; } QDate nextdate = date; if(!include_equals) nextdate = calSys->addDays(nextdate, 1); if(!endDate().isNull() && nextdate > endDate()) return QDate(); if(nextdate <= startDate()) return firstOccurrence(); if(i_frequency != 1) { int days = startDate().daysTo(nextdate); if(days % i_frequency != 0) { nextdate = calSys->addDays(nextdate, i_frequency - (days % i_frequency)); } } if(!endDate().isNull() && nextdate > endDate()) return QDate(); if(hasException(nextdate)) return nextOccurrence(nextdate); return nextdate; } QDate DailyRecurrence::prevOccurrence(const QDate &date, bool include_equals) const { const KCalendarSystem *calSys = KGlobal::locale()->calendar(); if(!include_equals) { if(date > endDate()) return lastOccurrence(); } QDate prevdate = date; if(!include_equals) prevdate = calSys->addDays(prevdate, -1); if(prevdate < startDate()) return QDate(); if(prevdate == startDate()) return startDate(); if(i_frequency != 1) { int days = startDate().daysTo(prevdate); if(days % i_frequency != 0) { prevdate = calSys->addDays(prevdate, -(days % i_frequency)); } } if(prevdate < startDate()) return QDate(); if(hasException(prevdate)) return prevOccurrence(prevdate); return prevdate; } RecurrenceType DailyRecurrence::type() const { return RECURRENCE_TYPE_DAILY; } int DailyRecurrence::frequency() const { return i_frequency; } void DailyRecurrence::set(const QDate &new_start_date, const QDate &new_end_date, int new_frequency, int occurrences) { i_frequency = new_frequency; setStartDate(new_start_date); if(occurrences <= 0) setEndDate(new_end_date); else setFixedOccurrenceCount(occurrences); } void DailyRecurrence::save(QDomElement *e) const { Recurrence::save(e); e->setAttribute("frequency", i_frequency); } WeeklyRecurrence::WeeklyRecurrence(Budget *parent_budget) : Recurrence(parent_budget) { i_frequency = 1; b_daysofweek[0] = false; b_daysofweek[1] = false; b_daysofweek[2] = false; b_daysofweek[3] = false; b_daysofweek[4] = false; b_daysofweek[5] = false; b_daysofweek[6] = false; } WeeklyRecurrence::WeeklyRecurrence(Budget *parent_budget, QDomElement *e, bool *valid) : Recurrence(parent_budget, e, valid) { i_frequency = e->attribute("frequency", "1").toInt(); QString days = e->attribute("days"); b_daysofweek[0] = days.find('1') >= 0; b_daysofweek[1] = days.find('2') >= 0; b_daysofweek[2] = days.find('3') >= 0; b_daysofweek[3] = days.find('4') >= 0; b_daysofweek[4] = days.find('5') >= 0; b_daysofweek[5] = days.find('6') >= 0; b_daysofweek[6] = days.find('7') >= 0; if(valid) { bool b = false; for(int i = 0; i < 7; i++) { if(b_daysofweek[i]) { b = true; break; } } if(!b) *valid = false; } if(valid && i_frequency < 1) { *valid = false; } updateDates(); } WeeklyRecurrence::WeeklyRecurrence(const WeeklyRecurrence *rec) : Recurrence(rec), i_frequency(rec->frequency()) { b_daysofweek[0] = rec->dayOfWeek(1); b_daysofweek[1] = rec->dayOfWeek(2); b_daysofweek[2] = rec->dayOfWeek(3); b_daysofweek[3] = rec->dayOfWeek(4); b_daysofweek[4] = rec->dayOfWeek(5); b_daysofweek[5] = rec->dayOfWeek(6); b_daysofweek[6] = rec->dayOfWeek(7); } WeeklyRecurrence::~WeeklyRecurrence() {} Recurrence *WeeklyRecurrence::copy() const {return new WeeklyRecurrence(this);} QDate WeeklyRecurrence::nextOccurrence(const QDate &date, bool include_equals) const { const KCalendarSystem *calSys = KGlobal::locale()->calendar(); if(!include_equals) { if(date < startDate()) return firstOccurrence(); } else { if(date <= startDate()) return firstOccurrence(); } QDate nextdate = date; if(!include_equals) nextdate = calSys->addDays(nextdate, 1); if(!endDate().isNull() && nextdate > endDate()) return QDate(); if(i_frequency != 1 && calSys->weekNumber(nextdate) != calSys->weekNumber(startDate())) { int i = weeks_between_dates(startDate(), nextdate) % i_frequency; if(i != 0) { nextdate = calSys->addDays(nextdate, (i_frequency - i) * 7 - (calSys->dayOfWeek(nextdate) - 1)); } } int dow = calSys->dayOfWeek(nextdate); int i = dow; for(; i <= 7; i++) { if(b_daysofweek[i - 1]) { break; } } if(i > 7) { for(i = 1; i <= 7; i++) { if(b_daysofweek[i - 1]) { break; } } if(i > 7) return QDate(); } if(i < dow) { nextdate = calSys->addDays(nextdate, (i_frequency * 7) + i - dow); } else if(i > dow) { nextdate = calSys->addDays(nextdate, i - dow); } if(!endDate().isNull() && nextdate > endDate()) return QDate(); if(hasException(nextdate)) return nextOccurrence(nextdate); return nextdate; } QDate WeeklyRecurrence::prevOccurrence(const QDate &date, bool include_equals) const { const KCalendarSystem *calSys = KGlobal::locale()->calendar(); if(!include_equals) { if(date > endDate()) return lastOccurrence(); } QDate prevdate = date; if(!include_equals) prevdate = calSys->addDays(prevdate, -1); if(prevdate < startDate()) return QDate(); if(prevdate == startDate()) return startDate(); if(i_frequency != 1 && calSys->weekNumber(prevdate) != calSys->weekNumber(startDate())) { int i = weeks_between_dates(startDate(), prevdate) % i_frequency; if(i != 0) { prevdate = calSys->addDays(prevdate, -(i * 7) + 7 - calSys->dayOfWeek(prevdate)); } } int dow_s = calSys->dayOfWeek(startDate()); bool s_week = calSys->weekNumber(prevdate) == calSys->weekNumber(startDate()); int dow = calSys->dayOfWeek(prevdate); int i = dow; for(; i <= 7; i++) { if(b_daysofweek[i - 1] || (s_week && dow_s == i)) { break; } } if(i > 7) { for(i = 1; i <= 7; i++) { if(b_daysofweek[i - 1] || (s_week && dow_s == i)) { break; } } if(i > 7) return QDate(); } if(i > dow) { prevdate = calSys->addDays(prevdate, -(i_frequency * 7) + dow - i); } else if(i < dow) { prevdate = calSys->addDays(prevdate, dow - i); } if(prevdate < startDate()) return QDate(); if(hasException(prevdate)) return prevOccurrence(prevdate); return prevdate; } RecurrenceType WeeklyRecurrence::type() const { return RECURRENCE_TYPE_WEEKLY; } int WeeklyRecurrence::frequency() const { return i_frequency; } bool WeeklyRecurrence::dayOfWeek(int i) const { if(i >= 1 && i <= 7) return b_daysofweek[i - 1]; return false; } void WeeklyRecurrence::set(const QDate &new_start_date, const QDate &new_end_date, bool d1, bool d2, bool d3, bool d4, bool d5, bool d6, bool d7, int new_frequency, int occurrences) { b_daysofweek[0] = d1; b_daysofweek[1] = d2; b_daysofweek[2] = d3; b_daysofweek[3] = d4; b_daysofweek[4] = d5; b_daysofweek[5] = d6; b_daysofweek[6] = d7; i_frequency = new_frequency; setStartDate(new_start_date); if(occurrences <= 0) setEndDate(new_end_date); else setFixedOccurrenceCount(occurrences); } void WeeklyRecurrence::save(QDomElement *e) const { Recurrence::save(e); e->setAttribute("frequency", i_frequency); QString days; if(b_daysofweek[0]) days += 1; if(b_daysofweek[1]) days += 2; if(b_daysofweek[2]) days += 3; if(b_daysofweek[3]) days += 4; if(b_daysofweek[4]) days += 5; if(b_daysofweek[5]) days += 6; if(b_daysofweek[6]) days += 7; e->setAttribute("days", days); } MonthlyRecurrence::MonthlyRecurrence(Budget *parent_budget) : Recurrence(parent_budget) { i_day = 1; i_frequency = 1; i_week = 1; i_dayofweek = -1; wh_weekendhandling = WEEKEND_HANDLING_NONE; } MonthlyRecurrence::MonthlyRecurrence(Budget *parent_budget, QDomElement *e, bool *valid) : Recurrence(parent_budget, e, valid) { i_day = e->attribute("day", "1").toInt(); i_frequency = e->attribute("frequency", "1").toInt(); i_week = e->attribute("week", "1").toInt(); i_dayofweek = e->attribute("dayofweek", "-1").toInt(); wh_weekendhandling = (WeekendHandling) e->attribute("weekendhandling", "0").toInt(); if(valid && (i_day > 31 || i_day < -27 || i_frequency < 1 || i_week > 5 || i_week < -4 || i_dayofweek > 7)) { *valid = false; } updateDates(); } MonthlyRecurrence::MonthlyRecurrence(const MonthlyRecurrence *rec) : Recurrence(rec), i_frequency(rec->frequency()), i_day(rec->day()), i_week(rec->week()), i_dayofweek(rec->dayOfWeek()), wh_weekendhandling(rec->weekendHandling()) {} MonthlyRecurrence::~MonthlyRecurrence() {} Recurrence *MonthlyRecurrence::copy() const {return new MonthlyRecurrence(this);} QDate MonthlyRecurrence::nextOccurrence(const QDate &date, bool include_equals) const { const KCalendarSystem *calSys = KGlobal::locale()->calendar(); if(!include_equals) { if(date < startDate()) return firstOccurrence(); } else { if(date <= startDate()) return firstOccurrence(); } QDate nextdate = date; if(!include_equals) nextdate = calSys->addDays(nextdate, 1); if(!endDate().isNull() && nextdate > endDate()) return QDate(); int prevday = -1; if(calSys->month(nextdate) == calSys->month(startDate())) { if(i_frequency > 1) prevday = 1; else prevday = calSys->day(nextdate); nextdate = addMonths(nextdate, i_frequency); calSys->setYMD(nextdate, calSys->year(nextdate), calSys->month(nextdate), 1); } else if(i_frequency != 1) { int i = months_between_dates(startDate(), nextdate) % i_frequency; if(i != 0) { if(i_frequency - i > 1) prevday = 1; else prevday = calSys->day(nextdate); nextdate = addMonths(nextdate, i_frequency - i); calSys->setYMD(nextdate, calSys->year(nextdate), calSys->month(nextdate), 1); } } int day = i_day; if(i_dayofweek > 0) day = get_day_in_month(nextdate, i_week, i_dayofweek); else if(i_day < 1) day = calSys->daysInMonth(nextdate) + i_day; if(wh_weekendhandling == WEEKEND_HANDLING_BEFORE) { QDate date; calSys->setYMD(date, calSys->year(nextdate), calSys->month(nextdate), day); int wday = calSys->dayOfWeek(date); if(wday == 6) day -= 1; else if(wday == 7) day -= 2; if(day <= 0 && prevday > 0 && prevday <= calSys->daysInMonth(addMonths(nextdate, -1)) + day) { nextdate = addMonths(nextdate, -1); day = calSys->daysInMonth(nextdate) + day; } } else if(wh_weekendhandling == WEEKEND_HANDLING_AFTER) { QDate date; calSys->setYMD(date, calSys->year(nextdate), calSys->month(nextdate), day); int wday = calSys->dayOfWeek(date); if(wday == 6) day += 2; else if(wday == 7) day += 1; if(day > calSys->daysInMonth(nextdate)) { day -= calSys->daysInMonth(nextdate); nextdate = addMonths(nextdate, 1); calSys->setYMD(nextdate, calSys->year(nextdate), calSys->month(nextdate), day); } } if(day <= 0 || calSys->day(nextdate) > day || day > calSys->daysInMonth(nextdate)) { do { if(i_frequency > 1) prevday = 1; else prevday = calSys->day(nextdate); nextdate = addMonths(nextdate, i_frequency); if(!endDate().isNull() && calSys->month(nextdate) > calSys->month(endDate())) return QDate(); day = i_day; if(i_dayofweek > 0) day = get_day_in_month(nextdate, i_week, i_dayofweek); else if(i_day < 1) day = calSys->daysInMonth(nextdate) + i_day; if(wh_weekendhandling == WEEKEND_HANDLING_BEFORE) { QDate date; calSys->setYMD(date, calSys->year(nextdate), calSys->month(nextdate), day); int wday = calSys->dayOfWeek(date); if(wday == 6) day -= 1; else if(wday == 7) day -= 2; if(day <= 0 && prevday > 0 && prevday <= calSys->daysInMonth(addMonths(nextdate, -1)) + day) { nextdate = addMonths(nextdate, -1); day = calSys->daysInMonth(nextdate) + day; } else { calSys->setYMD(nextdate, calSys->year(nextdate), calSys->month(nextdate), 1); } } else if(wh_weekendhandling == WEEKEND_HANDLING_AFTER) { QDate date; calSys->setYMD(date, calSys->year(nextdate), calSys->month(nextdate), day); int wday = calSys->dayOfWeek(date); if(wday == 6) day += 2; else if(wday == 7) day += 1; if(day > calSys->daysInMonth(nextdate)) { day -= calSys->daysInMonth(nextdate); nextdate = addMonths(nextdate, 1); calSys->setYMD(nextdate, calSys->year(nextdate), calSys->month(nextdate), day); } } } while(day <= 0 || day > calSys->daysInMonth(nextdate)); } calSys->setYMD(nextdate, calSys->year(nextdate), calSys->month(nextdate), day); if(!endDate().isNull() && nextdate > endDate()) return QDate(); if(hasException(nextdate)) return nextOccurrence(nextdate); return nextdate; } QDate MonthlyRecurrence::prevOccurrence(const QDate &date, bool include_equals) const { const KCalendarSystem *calSys = KGlobal::locale()->calendar(); if(!include_equals) { if(date > endDate()) return lastOccurrence(); } QDate prevdate = date; if(!include_equals) prevdate = calSys->addDays(prevdate, -1); if(prevdate < startDate()) return QDate(); if(prevdate == startDate()) return startDate(); int prevday = -1; if(i_frequency != 1 && calSys->month(prevdate) != calSys->month(startDate())) { int i = months_between_dates(startDate(), prevdate) % i_frequency; if(i != 0) { if(i > 1) prevday = 1; else prevday = calSys->day(prevdate); prevdate = addMonths(prevdate, -i); calSys->setYMD(prevdate, calSys->year(prevdate), calSys->month(prevdate), calSys->daysInMonth(prevdate)); } } if(calSys->month(prevdate) == calSys->month(startDate())) return startDate(); int day = i_day; if(i_dayofweek > 0) day = get_day_in_month(prevdate, i_week, i_dayofweek); else if(i_day < 1) day = calSys->daysInMonth(prevdate) + i_day; if(wh_weekendhandling == WEEKEND_HANDLING_BEFORE) { QDate date; calSys->setYMD(date, calSys->year(prevdate), calSys->month(prevdate), day); int wday = calSys->dayOfWeek(date); if(wday == 6) day -= 1; else if(wday == 7) day -= 2; if(day <= 0) { prevdate = addMonths(prevdate, -1); day = calSys->daysInMonth(prevdate) + day; } } else if(wh_weekendhandling == WEEKEND_HANDLING_AFTER) { QDate date; calSys->setYMD(date, calSys->year(prevdate), calSys->month(prevdate), day); int wday = calSys->dayOfWeek(date); if(wday == 6) day += 2; else if(wday == 7) day += 1; if(day > calSys->daysInMonth(prevdate) && prevday > 0 && prevday >= day - calSys->daysInMonth(prevdate)) { day -= calSys->daysInMonth(prevdate); prevdate = addMonths(prevdate, 1); calSys->setYMD(prevdate, calSys->year(prevdate), calSys->month(prevdate), day); } } if(day <= 0 || calSys->day(prevdate) < day) { do { if(i_frequency > 1) prevday = 1; else prevday = calSys->day(prevdate); prevdate = addMonths(prevdate, -i_frequency); if(calSys->month(prevdate) == calSys->month(startDate())) return startDate(); if(calSys->month(prevdate) < calSys->month(startDate())) return QDate(); day = i_day; if(i_dayofweek > 0) day = get_day_in_month(prevdate, i_week, i_dayofweek); else if(i_day < 1) day = calSys->daysInMonth(prevdate) + i_day; if(wh_weekendhandling == WEEKEND_HANDLING_BEFORE) { QDate date; calSys->setYMD(date, calSys->year(prevdate), calSys->month(prevdate), day); int wday = calSys->dayOfWeek(date); if(wday == 6) day -= 1; else if(wday == 7) day -= 2; if(day <= 0) { prevdate = addMonths(prevdate, -1); day = calSys->daysInMonth(prevdate) + day; } } else if(wh_weekendhandling == WEEKEND_HANDLING_AFTER) { QDate date; calSys->setYMD(date, calSys->year(prevdate), calSys->month(prevdate), day); int wday = calSys->dayOfWeek(date); if(wday == 6) day += 2; else if(wday == 7) day += 1; if(day > calSys->daysInMonth(prevdate) && prevday > 0 && prevday >= day - calSys->daysInMonth(prevdate)) { day -= calSys->daysInMonth(prevdate); prevdate = addMonths(prevdate, 1); calSys->setYMD(prevdate, calSys->year(prevdate), calSys->month(prevdate), day); } else { calSys->setYMD(prevdate, calSys->year(prevdate), calSys->month(prevdate), calSys->daysInMonth(prevdate)); } } } while(day <= 0 || day > calSys->daysInMonth(prevdate)); } calSys->setYMD(prevdate, calSys->year(prevdate), calSys->month(prevdate), day); if(prevdate < startDate()) return QDate(); if(hasException(prevdate)) return prevOccurrence(prevdate); return prevdate; } RecurrenceType MonthlyRecurrence::type() const { return RECURRENCE_TYPE_MONTHLY; } int MonthlyRecurrence::frequency() const { return i_frequency; } int MonthlyRecurrence::day() const { return i_day; } WeekendHandling MonthlyRecurrence::weekendHandling() const { return wh_weekendhandling; } int MonthlyRecurrence::week() const { return i_week; } int MonthlyRecurrence::dayOfWeek() const { return i_dayofweek; } void MonthlyRecurrence::setOnDayOfWeek(const QDate &new_start_date, const QDate &new_end_date, int new_dayofweek, int new_week, int new_frequency, int occurrences) { i_week = new_week; i_frequency = new_frequency; i_dayofweek = new_dayofweek; wh_weekendhandling = WEEKEND_HANDLING_NONE; setStartDate(new_start_date); if(occurrences <= 0) setEndDate(new_end_date); else setFixedOccurrenceCount(occurrences); } void MonthlyRecurrence::setOnDay(const QDate &new_start_date, const QDate &new_end_date, int new_day, WeekendHandling new_weekendhandling, int new_frequency, int occurrences) { i_dayofweek = -1; i_day = new_day; i_frequency = new_frequency; wh_weekendhandling = new_weekendhandling; setStartDate(new_start_date); if(occurrences <= 0) setEndDate(new_end_date); else setFixedOccurrenceCount(occurrences); } void MonthlyRecurrence::save(QDomElement *e) const { Recurrence::save(e); if(i_dayofweek <= 0) { e->setAttribute("day", i_day); e->setAttribute("weekendhandling", wh_weekendhandling); } else { e->setAttribute("dayofweek", i_dayofweek); e->setAttribute("week", i_dayofweek); } e->setAttribute("frequency", i_frequency); } YearlyRecurrence::YearlyRecurrence(Budget *parent_budget) : Recurrence(parent_budget) { i_dayofmonth = 1; i_month = 1; i_frequency = 1; i_week = 1; i_dayofweek = -1; i_dayofyear = -1; wh_weekendhandling = WEEKEND_HANDLING_NONE; } YearlyRecurrence::YearlyRecurrence(Budget *parent_budget, QDomElement *e, bool *valid) : Recurrence(parent_budget, e, valid) { i_frequency = e->attribute("frequency", "1").toInt(); i_dayofmonth = e->attribute("dayofmonth", "1").toInt(); i_month = e->attribute("month", "1").toInt(); i_week = e->attribute("week", "1").toInt(); i_dayofweek = e->attribute("dayofweek", "-1").toInt(); i_dayofyear = e->attribute("dayofyear", "-1").toInt(); wh_weekendhandling = (WeekendHandling) e->attribute("weekendhandling", "0").toInt(); if(valid && (i_dayofmonth > 31 || i_frequency < 1 || i_week > 5 || i_week < -4 || i_dayofweek > 7)) { *valid = false; } updateDates(); } YearlyRecurrence::YearlyRecurrence(const YearlyRecurrence *rec) : Recurrence(rec), i_frequency(rec->frequency()), i_dayofmonth(rec->dayOfMonth()), i_month(rec->month()), i_week(rec->week()), i_dayofweek(rec->dayOfWeek()), i_dayofyear(rec->dayOfYear()), wh_weekendhandling(rec->weekendHandling()) {} YearlyRecurrence::~YearlyRecurrence() {} Recurrence *YearlyRecurrence::copy() const {return new YearlyRecurrence(this);} QDate YearlyRecurrence::nextOccurrence(const QDate &date, bool include_equals) const { const KCalendarSystem *calSys = KGlobal::locale()->calendar(); if(!include_equals) { if(date < startDate()) return firstOccurrence(); } else { if(date <= startDate()) return firstOccurrence(); } QDate nextdate = date; if(!include_equals) nextdate = calSys->addDays(nextdate, 1); if(!endDate().isNull() && nextdate > endDate()) return QDate(); if(calSys->year(nextdate) == calSys->year(startDate())) { nextdate = addYears(nextdate, i_frequency); calSys->setYMD(nextdate, calSys->year(nextdate), 1, 1); } else if(i_frequency != 1) { int i = (calSys->year(nextdate) - calSys->year(startDate())) % i_frequency; if(i != 0) { nextdate = addYears(nextdate, i_frequency - i); calSys->setYMD(nextdate, calSys->year(nextdate), 1, 1); } } if(i_dayofyear > 0) { if(calSys->dayOfYear(nextdate) > i_dayofyear) { nextdate = addYears(nextdate, i_frequency); } if(i_dayofyear > calSys->daysInYear(nextdate)) { int i = 10; do { if(i == 0) return QDate(); nextdate = addYears(nextdate, i_frequency); i--; } while(i_dayofyear > calSys->daysInYear(nextdate)); } nextdate = calSys->addDays(nextdate, i_dayofyear - calSys->dayOfYear(nextdate)); } else { int day = i_dayofmonth; if(i_dayofweek > 0) day = get_day_in_month(calSys->year(nextdate), i_month, i_week, i_dayofweek); if(day == 0 || calSys->month(nextdate) > i_month || (calSys->month(nextdate) == i_month && calSys->day(nextdate) > day)) { do { nextdate = addYears(nextdate, i_frequency); day = get_day_in_month(calSys->year(nextdate), i_month, i_week, i_dayofweek); if(!endDate().isNull() && calSys->year(nextdate) > calSys->year(endDate())) return QDate(); } while(day == 0); } if(i_dayofweek <= 0) { calSys->setYMD(nextdate, calSys->year(nextdate), i_month, 1); if(day > calSys->daysInMonth(nextdate)) { int i = 10; do { if(i == 0) return QDate(); nextdate = addYears(nextdate, i_frequency); calSys->setYMD(nextdate, calSys->year(nextdate), i_month, 1); i--; } while(day > calSys->daysInMonth(nextdate)); } } calSys->setYMD(nextdate, calSys->year(nextdate), i_month, day); } if(!endDate().isNull() && nextdate > endDate()) return QDate(); if(hasException(nextdate)) return nextOccurrence(nextdate); return nextdate; } QDate YearlyRecurrence::prevOccurrence(const QDate &date, bool include_equals) const { const KCalendarSystem *calSys = KGlobal::locale()->calendar(); if(!include_equals) { if(date > endDate()) return lastOccurrence(); } QDate prevdate = date; if(!include_equals) prevdate = calSys->addDays(prevdate, -1); if(prevdate < startDate()) return QDate(); if(prevdate == startDate()) return startDate(); if(i_frequency != 1) { int i = (calSys->year(prevdate) - calSys->year(startDate())) % i_frequency; if(i != 0) { prevdate = addYears(prevdate, - i); } } if(calSys->year(prevdate) == calSys->year(startDate())) return startDate(); if(i_dayofyear > 0) { if(calSys->dayOfYear(prevdate) < i_dayofyear) { prevdate = addYears(prevdate, -i_frequency); if(calSys->year(prevdate) == calSys->year(startDate())) return startDate(); } if(i_dayofyear > calSys->daysInYear(prevdate)) { do { prevdate = addYears(prevdate, -i_frequency); if(calSys->year(prevdate) <= calSys->year(startDate())) return startDate(); } while(i_dayofyear > calSys->daysInYear(prevdate)); } prevdate = calSys->addDays(prevdate, i_dayofyear - calSys->dayOfYear(prevdate)); } else { int day = i_dayofmonth; if(i_dayofweek > 0) day = get_day_in_month(calSys->year(prevdate), i_month, i_week, i_dayofweek); if(day <= 0 || calSys->month(prevdate) < i_month || (calSys->month(prevdate) == i_month && calSys->day(prevdate) < day)) { do { prevdate = addYears(prevdate, -i_frequency); if(i_dayofweek > 0) day = get_day_in_month(calSys->year(prevdate), i_month, i_week, i_dayofweek); if(calSys->year(prevdate) == calSys->year(startDate())) return startDate(); } while(day <= 0); } if(i_dayofweek <= 0) { calSys->setYMD(prevdate, calSys->year(prevdate), i_month, 1); if(day > calSys->daysInMonth(prevdate)) { do { prevdate = addYears(prevdate, -i_frequency); calSys->setYMD(prevdate, calSys->year(prevdate), i_month, 1); if(calSys->year(prevdate) <= calSys->year(startDate())) return startDate(); } while(day > calSys->daysInMonth(prevdate)); } } calSys->setYMD(prevdate, calSys->year(prevdate), i_month, day); } if(prevdate < startDate()) return QDate(); if(hasException(prevdate)) return prevOccurrence(prevdate); return prevdate; } RecurrenceType YearlyRecurrence::type() const { return RECURRENCE_TYPE_YEARLY; } int YearlyRecurrence::frequency() const { return i_frequency; } int YearlyRecurrence::dayOfYear() const { return i_dayofyear; } int YearlyRecurrence::month() const { return i_month; } int YearlyRecurrence::dayOfMonth() const { return i_dayofmonth; } WeekendHandling YearlyRecurrence::weekendHandling() const { return wh_weekendhandling; } int YearlyRecurrence::week() const { return i_week; } int YearlyRecurrence::dayOfWeek() const { return i_dayofweek; } void YearlyRecurrence::setOnDayOfWeek(const QDate &new_start_date, const QDate &new_end_date, int new_month, int new_dayofweek, int new_week, int new_frequency, int occurrences) { i_dayofyear = -1; wh_weekendhandling = WEEKEND_HANDLING_NONE; i_dayofweek = new_dayofweek; i_week = new_week; i_month = new_month; i_frequency = new_frequency; setStartDate(new_start_date); if(occurrences <= 0) setEndDate(new_end_date); else setFixedOccurrenceCount(occurrences); } void YearlyRecurrence::setOnDayOfMonth(const QDate &new_start_date, const QDate &new_end_date, int new_month, int new_day, WeekendHandling new_weekendhandling, int new_frequency, int occurrences) { i_dayofweek = -1; i_dayofyear = -1; i_dayofmonth = new_day; i_month = new_month; i_frequency = new_frequency; wh_weekendhandling = new_weekendhandling; setStartDate(new_start_date); if(occurrences <= 0) setEndDate(new_end_date); else setFixedOccurrenceCount(occurrences); } void YearlyRecurrence::setOnDayOfYear(const QDate &new_start_date, const QDate &new_end_date, int new_day, WeekendHandling new_weekendhandling, int new_frequency, int occurrences) { i_dayofyear = new_day; wh_weekendhandling = new_weekendhandling; i_frequency = new_frequency; setStartDate(new_start_date); if(occurrences <= 0) setEndDate(new_end_date); else setFixedOccurrenceCount(occurrences); } void YearlyRecurrence::save(QDomElement *e) const { Recurrence::save(e); if(i_dayofyear > 0) { e->setAttribute("dayofyear", i_dayofweek); e->setAttribute("weekendhandling", wh_weekendhandling); } else if(i_dayofweek > 0) { e->setAttribute("month", i_month); e->setAttribute("dayofweek", i_dayofweek); e->setAttribute("week", i_dayofweek); } else { e->setAttribute("month", i_month); e->setAttribute("dayofmonth", i_dayofmonth); e->setAttribute("weekendhandling", wh_weekendhandling); } e->setAttribute("frequency", i_frequency); }