Index: kcalcore/event.cpp =================================================================== --- kcalcore/event.cpp +++ kcalcore/event.cpp @@ -141,16 +141,18 @@ return; } - update(); - d->mDtEnd = dtEnd; - d->mMultiDayValid = false; - d->mHasEndDate = dtEnd.isValid(); - if (d->mHasEndDate) { - setHasDuration(false); + if (d->mDtEnd != dtEnd || d->mHasEndDate != dtEnd.isValid()) { + update(); + d->mDtEnd = dtEnd; + d->mMultiDayValid = false; + d->mHasEndDate = dtEnd.isValid(); + if (d->mHasEndDate) { + setHasDuration(false); + } + setFieldDirty(FieldDtEnd); + updated(); } - setFieldDirty(FieldDtEnd); - updated(); } KDateTime Event::dtEnd() const Index: kcalcore/incidence.cpp =================================================================== --- kcalcore/incidence.cpp +++ kcalcore/incidence.cpp @@ -392,10 +392,10 @@ void Incidence::setDtStart(const KDateTime &dt) { - if (d->mRecurrence) { + IncidenceBase::setDtStart(dt); + if ((dirtyFields().contains(FieldDtStart)) && d->mRecurrence) { d->mRecurrence->setStartDateTime(dt); } - IncidenceBase::setDtStart(dt); } void Incidence::shiftTimes(const KDateTime::Spec &oldSpec, @@ -451,11 +451,13 @@ if (mReadOnly) { return; } - update(); - d->mSummary = summary; - d->mSummaryIsRich = isRich; - setFieldDirty(FieldSummary); - updated(); + if (d->mSummary != summary || d->mSummaryIsRich != isRich) { + update(); + d->mSummary = summary; + d->mSummaryIsRich = isRich; + setFieldDirty(FieldSummary); + updated(); + } } void Incidence::setSummary(const QString &summary) @@ -928,11 +930,13 @@ return; } - update(); - d->mLocation = location; - d->mLocationIsRich = isRich; - setFieldDirty(FieldLocation); - updated(); + if (d->mLocation != location || d->mLocationIsRich != isRich) { + update(); + d->mLocation = location; + d->mLocationIsRich = isRich; + setFieldDirty(FieldLocation); + updated(); + } } void Incidence::setLocation(const QString &location) Index: kcalcore/incidencebase.cpp =================================================================== --- kcalcore/incidencebase.cpp +++ kcalcore/incidencebase.cpp @@ -311,11 +311,13 @@ kWarning() << "Invalid dtStart"; } - update(); - d->mDtStart = dtStart; - d->mAllDay = dtStart.isDateOnly(); - d->mDirtyFields.insert(FieldDtStart); - updated(); + if (d->mDtStart != dtStart || d->mAllDay != dtStart.isDateOnly()) { + update(); + d->mDtStart = dtStart; + d->mAllDay = dtStart.isDateOnly(); + d->mDirtyFields.insert(FieldDtStart); + updated(); + } } KDateTime IncidenceBase::dtStart() const Index: kcalcore/recurrence.h =================================================================== --- kcalcore/recurrence.h +++ kcalcore/recurrence.h @@ -430,12 +430,16 @@ void addMonthlyPos(short pos, const QBitArray &days); void addMonthlyPos(short pos, ushort day); + void setMonthlyPos(const QList &monthlyDays); + /** Adds a date (e.g. the 15th of each month) to the monthly day * recurrence list. * @param day the date in the month to recur. */ void addMonthlyDate(short day); + void setMonthlyDate(const QList &monthlyDays); + /** Returns list of day positions in months. */ QList monthPositions() const; @@ -475,6 +479,8 @@ */ void addYearlyDay(int day); + void setYearlyDay(const QList &days); + /** Adds date within a yearly recurrence. The month(s) for the recurrence * can be specified with addYearlyMonth(), otherwise the month of the * start date is used. @@ -485,6 +491,8 @@ */ void addYearlyDate(int date); + void setYearlyDate(const QList &dates); + /** Adds month in yearly recurrence. You can specify specific day numbers * within the months (by calling addYearlyDate()) or specific day positions * within the month (by calling addYearlyPos). @@ -492,6 +500,8 @@ */ void addYearlyMonth(short _rNum); + void setYearlyMonth(const QList< int > &months); + /** Adds position within month/year within a yearly recurrence. If months * are specified (via addYearlyMonth()), the parameters are understood as * position within these months, otherwise within the year. @@ -510,6 +520,8 @@ */ void addYearlyPos(short pos, const QBitArray &days); + void setYearlyPos(QList & days); + /** Returns the day numbers within a yearly recurrence. * @return the days of the year for the event. E.g. if the list contains * 60, this means the recurrence happens on day 60 of the year, i.e. Index: kcalcore/recurrence.cpp =================================================================== --- kcalcore/recurrence.cpp +++ kcalcore/recurrence.cpp @@ -474,8 +474,16 @@ if (!rrule) { return; } - rrule->setEndDt(dateTime); - updated(); + + // we have duration end and set a invalid date -> we still have duration ending + if (rrule->duration() > 0 && !dateTime.isValid()) { + return; + } + + if(dateTime != rrule->endDt()) { + rrule->setEndDt(dateTime); + updated(); + } } int Recurrence::duration() const @@ -506,8 +514,11 @@ if (!rrule) { return; } - rrule->setDuration(duration); - updated(); + + if (duration != rrule->duration()) { + rrule->setDuration(duration); + updated(); + } } void Recurrence::shiftTimes(const KDateTime::Spec &oldSpec, const KDateTime::Spec &newSpec) @@ -692,6 +703,11 @@ return 0; } + if ( defaultRRuleConst() && defaultRRuleConst()->recurrenceType() == type && frequency() == freq) { + rrule->setDuration(-1); + return 0; + } + qDeleteAll(d->mRRules); d->mRRules.clear(); updated(); @@ -799,8 +815,27 @@ RecurrenceRule::WDayPos p(pos, day); if (!positions.contains(p)) { positions.append(p); - rrule->setByDays(positions); - updated(); + setMonthlyPos(positions); + } +} + +void Recurrence::setMonthlyPos(const QList &monthlyDays) +{ + if (d->mRecurReadOnly) { + return; + } + + RecurrenceRule *rrule = defaultRRule(true); + if (!rrule) { + return; + } + + //TODO: sort lists + // the position inside the list has no meaning, so sort the list before testing if it changed + + if (monthlyDays != rrule->byDays()) { + rrule->setByDays(monthlyDays); + updated(); } } @@ -818,8 +853,30 @@ QList monthDays = rrule->byMonthDays(); if (!monthDays.contains(day)) { monthDays.append(day); - rrule->setByMonthDays(monthDays); - updated(); + setMonthlyDate(monthDays); + } +} + +void Recurrence::setMonthlyDate(const QList< int > &monthlyDays) +{ + if (d->mRecurReadOnly) { + return; + } + + RecurrenceRule *rrule = defaultRRule(true); + if (!rrule) { + return; + } + + SortableList mD(monthlyDays); + SortableList rbD(rrule->byMonthDays()); + + mD.sortUnique(); + rbD.sortUnique(); + + if (mD != rbD) { + rrule->setByMonthDays(monthlyDays); + updated(); } } @@ -841,6 +898,24 @@ QList days = rrule->byYearDays(); if (!days.contains(day)) { days << day; + setYearlyDay(days); + } +} + +void Recurrence::setYearlyDay(const QList &days) +{ + RecurrenceRule *rrule = defaultRRule(false); // It must already exist! + if (!rrule) { + return; + } + + SortableList d(days); + SortableList bYD(rrule->byYearDays()); + + d.sortUnique(); + bYD.sortUnique(); + + if (d != bYD) { rrule->setByYearDays(days); updated(); } @@ -852,12 +927,22 @@ addMonthlyDate(day); } +void Recurrence::setYearlyDate(const QList &dates) +{ + setMonthlyDate(dates); +} + // day part of date within year, given as position (n-th weekday) void Recurrence::addYearlyPos(short pos, const QBitArray &days) { addMonthlyPos(pos, days); } +void Recurrence::setYearlyPos(QList &days) +{ + setMonthlyPos(days); +} + // month part of date within year void Recurrence::addYearlyMonth(short month) { @@ -873,6 +958,28 @@ QList months = rrule->byMonths(); if (!months.contains(month)) { months << month; + setYearlyMonth(months); + } +} + +void Recurrence::setYearlyMonth(const QList &months) +{ + if (d->mRecurReadOnly) { + return; + } + + RecurrenceRule *rrule = defaultRRule(false); + if (!rrule) { + return; + } + + SortableList m(months); + SortableList bM(rrule->byMonths()); + + m.sortUnique(); + bM.sortUnique(); + + if (m != bM) { rrule->setByMonths(months); updated(); } @@ -1329,9 +1436,13 @@ return; } - d->mExDates = exdates; - d->mExDates.sortUnique(); - updated(); + DateList l = exdates; + l.sortUnique(); + + if (d->mExDates != l) { + d->mExDates = l; + updated(); + } } void Recurrence::addExDate(const QDate &exdate) Index: kcalcore/recurrencerule.cpp =================================================================== --- kcalcore/recurrencerule.cpp +++ kcalcore/recurrencerule.cpp @@ -989,7 +989,9 @@ return; } d->mDateEnd = dateTime; - d->mDuration = 0; // set to 0 because there is an end date/time + if (d->mDateEnd.isValid()) { + d->mDuration = 0; // set to 0 because there is an end date/time + } d->setDirty(); } Index: kcalcore/tests/CMakeLists.txt =================================================================== --- kcalcore/tests/CMakeLists.txt +++ kcalcore/tests/CMakeLists.txt @@ -36,6 +36,7 @@ testcustomproperties testduration testevent + testincidence testexception testfilestorage testfreebusy Index: kcalcore/tests/testevent.h =================================================================== --- kcalcore/tests/testevent.h +++ kcalcore/tests/testevent.h @@ -39,6 +39,7 @@ void testSerializer_data(); void testSerializer(); void testDurationDtEnd(); + void testDtEndChange(); }; #endif Index: kcalcore/tests/testevent.cpp =================================================================== --- kcalcore/tests/testevent.cpp +++ kcalcore/tests/testevent.cpp @@ -240,3 +240,26 @@ } } + +void EventTest::testDtEndChange() +{ + QDate dt = QDate::currentDate(); + Event event1; + event1.setDtStart(KDateTime(dt)); + event1.setDtEnd(KDateTime(dt).addDays(1)); + event1.resetDirtyFields(); + + event1.setDtEnd(KDateTime(dt).addDays(1)); + QVERIFY(event1.dirtyFields().empty()); + + event1.setDtEnd(KDateTime(dt).addDays(2)); + QCOMPARE(event1.dirtyFields(), QSet() << IncidenceBase::FieldDtEnd); + event1.resetDirtyFields(); + + event1.setDtEnd(KDateTime()); + QCOMPARE(event1.dirtyFields(), QSet() << IncidenceBase::FieldDtEnd); + event1.resetDirtyFields(); + + event1.setDtEnd(KDateTime(dt).addDays(2)); + QCOMPARE(event1.dirtyFields(), QSet() << IncidenceBase::FieldDtEnd); +} \ No newline at end of file Index: kcalcore/tests/testincidence.h =================================================================== --- kcalcore/tests/testincidence.h +++ kcalcore/tests/testincidence.h @@ -1,7 +1,7 @@ /* This file is part of the kcalcore library. - Copyright (c) 2006-2008 Allen Winter + Copyright (C) 2015 Sandro Knauß This library is free software; you can redistribute it and/or modify it under the terms of the GNU Library General Public @@ -19,26 +19,28 @@ Boston, MA 02110-1301, USA. */ -#ifndef TESTEVENT_H -#define TESTEVENT_H +#ifndef TESTINCIDENCE_H +#define TESTINCIDENCE_H #include -class EventTest : public QObject +class IncidenceTest : public QObject { Q_OBJECT private Q_SLOTS: - void testSetRoles_data(); - void testSetRoles(); - void testValidity(); - void testCompare(); - void testClone(); - void testCopy(); - void testCopyIncidence(); - void testAssign(); - void testSerializer_data(); - void testSerializer(); - void testDurationDtEnd(); + void testDtStartChange(); + void testSummaryChange(); + void testLocationChange(); + + void testRecurrenceTypeChange(); + void testRecurrenceEndTimeChange(); + void testRecurrenceEndTimeDurationChange(); + void testRecurrenceDurationChange(); + void testRecurrenceExDatesChange(); + void testRecurrenceMonthlyPos(); + void testRecurrenceMonthlyDate(); + void testRecurrenceYearlyDay(); + void testRecurrenceYearlyMonth(); }; #endif Index: kcalcore/tests/testincidence.cpp =================================================================== --- /dev/null +++ kcalcore/tests/testincidence.cpp @@ -0,0 +1,276 @@ +/* + This file is part of the kcalcore library. + + Copyright (C) 2015 Sandro Knauß + + 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. +*/ +#include "testincidence.h" +#include "../event.h" + +#include + +QTEST_KDEMAIN(IncidenceTest, NoGUI) + +Q_DECLARE_METATYPE(KCalCore::Incidence::DateTimeRole) + +using namespace KCalCore; + +void IncidenceTest::testDtStartChange() +{ + QDate dt = QDate::currentDate(); + QTime t = QTime::currentTime(); + Event inc; + inc.setDtStart(KDateTime(dt)); + inc.recurrence()->setDaily(1); + inc.resetDirtyFields(); + + inc.setDtStart(KDateTime(dt)); + QVERIFY(inc.dirtyFields().empty()); + + inc.setDtStart(KDateTime(dt,t)); + QCOMPARE(inc.dirtyFields(), QSet() << IncidenceBase::FieldDtStart << IncidenceBase::FieldRecurrence); + QCOMPARE(inc.recurrence()->startDateTime().time(), t); + inc.resetDirtyFields(); + + inc.setDtStart(KDateTime(dt).addDays(1)); + QCOMPARE(inc.dirtyFields(), QSet() << IncidenceBase::FieldDtStart << IncidenceBase::FieldRecurrence); + QCOMPARE(inc.recurrence()->startDateTime(), KDateTime(dt).addDays(1)); + inc.resetDirtyFields(); + + inc.setDtStart(KDateTime()); + QCOMPARE(inc.dirtyFields(), QSet() << IncidenceBase::FieldDtStart << IncidenceBase::FieldRecurrence); + QCOMPARE(inc.recurrence()->startDateTime(), KDateTime()); + inc.resetDirtyFields(); + + inc.setDtStart(KDateTime(dt).addDays(1)); + QCOMPARE(inc.dirtyFields(), QSet() << IncidenceBase::FieldDtStart << IncidenceBase::FieldRecurrence); + QCOMPARE(inc.recurrence()->startDateTime(), KDateTime(dt).addDays(1)); +} + +void IncidenceTest::testSummaryChange() +{ + Event inc; + inc.setSummary(QLatin1String("bla"), false); + inc.resetDirtyFields(); + + inc.setSummary(QLatin1String("bla"), false); + QVERIFY(inc.dirtyFields().empty()); + + inc.setSummary(QLatin1String("bla2"), false); + QCOMPARE(inc.dirtyFields(), QSet() << IncidenceBase::FieldSummary); + inc.resetDirtyFields(); + + inc.setSummary(QLatin1String("bla2"), true); + QCOMPARE(inc.dirtyFields(), QSet() << IncidenceBase::FieldSummary); +} + +void IncidenceTest::testLocationChange() +{ + + Event inc; + inc.setLocation(QLatin1String("here"), false); + inc.resetDirtyFields(); + + inc.setLocation(QLatin1String("here"), false); + QVERIFY(inc.dirtyFields().empty()); + + inc.setLocation(QLatin1String("there"), false); + QCOMPARE(inc.dirtyFields(), QSet() << IncidenceBase::FieldLocation); + inc.resetDirtyFields(); + + inc.setLocation(QLatin1String("there"), true); + QCOMPARE(inc.dirtyFields(), QSet() << IncidenceBase::FieldLocation); +} + + +void IncidenceTest::testRecurrenceTypeChange() +{ + QDate dt = QDate::currentDate(); + Event inc; + inc.setDtStart(KDateTime(dt)); + KCalCore::Recurrence *r = inc.recurrence(); + r->setDaily(1); + inc.resetDirtyFields(); + + r->setDaily(1); + QVERIFY(inc.dirtyFields().empty()); + + r->setDaily(2); + QCOMPARE(inc.dirtyFields(), QSet() << IncidenceBase::FieldRecurrence); + inc.resetDirtyFields(); + + r->setMonthly(2); + QCOMPARE(inc.dirtyFields(), QSet() << IncidenceBase::FieldRecurrence); +} + +void IncidenceTest::testRecurrenceEndTimeChange() +{ + QDate dt = QDate::currentDate(); + Event inc; + inc.setDtStart(KDateTime(dt)); + KCalCore::Recurrence *r = inc.recurrence(); + r->setDaily(1); + r->setEndDateTime(KDateTime(dt).addDays(1)); + inc.resetDirtyFields(); + + r->setEndDateTime(KDateTime(dt).addDays(1)); + QVERIFY(inc.dirtyFields().empty()); + + r->setEndDateTime(KDateTime(dt).addDays(2)); + QCOMPARE(inc.dirtyFields(), QSet() << IncidenceBase::FieldRecurrence); +} + +void IncidenceTest::testRecurrenceEndTimeDurationChange() +{ + QDate dt = QDate::currentDate(); + Event inc; + inc.setDtStart(KDateTime(dt)); + KCalCore::Recurrence *r = inc.recurrence(); + r->setDaily(1); + inc.resetDirtyFields(); + QCOMPARE(r->duration(), -1); + QVERIFY(!r->endDateTime().isValid()); + + r->setDuration(5); + QVERIFY(r->endDateTime().isValid()); + inc.resetDirtyFields(); + + // duration is set and set enddate to inValid + r->setDuration(5); + r->setEndDateTime(KDateTime()); + QVERIFY(inc.dirtyFields().empty()); + + // now set valid enddate -> set duration to 0 by sideeffect + r->setEndDateTime(KDateTime(dt).addDays(1)); + QCOMPARE(inc.dirtyFields(), QSet() << IncidenceBase::FieldRecurrence); + QCOMPARE(r->duration(), 0); + QCOMPARE(r->endDateTime(), KDateTime(dt).addDays(1)); + + // with valid endDate, now setDuration and aftward set invalid endDate + r->setEndDateTime(KDateTime(dt).addDays(1)); + r->setDuration(5); + inc.resetDirtyFields(); + + r->setEndDateTime(KDateTime()); + QVERIFY(inc.dirtyFields().empty()); + QCOMPARE(r->endDate(), dt.addDays(4)); + QCOMPARE(r->duration(), 5); +} + +void IncidenceTest::testRecurrenceDurationChange() +{ + QDate dt = QDate::currentDate(); + Event inc; + inc.setDtStart(KDateTime(dt)); + KCalCore::Recurrence *r = inc.recurrence(); + r->setDuration(1); + inc.resetDirtyFields(); + + r->setDuration(1); + QVERIFY(inc.dirtyFields().empty()); + + r->setDuration(2); + QCOMPARE(inc.dirtyFields(), QSet() << IncidenceBase::FieldRecurrence); +} + +void IncidenceTest::testRecurrenceExDatesChange() +{ + QDate dt = QDate::currentDate(); + Event inc; + inc.setDtStart(KDateTime(dt)); + KCalCore::Recurrence *r = inc.recurrence(); + r->setExDates(DateList() << dt.addDays(1) << dt.addDays(2)); + inc.resetDirtyFields(); + + r->setExDates(DateList() << dt.addDays(2) << dt.addDays(1)); + QVERIFY(inc.dirtyFields().empty()); + + r->setExDates(DateList() << dt.addDays(1)); + QCOMPARE(inc.dirtyFields(), QSet() << IncidenceBase::FieldRecurrence); +} + +void IncidenceTest::testRecurrenceMonthlyDate() +{ + QDate dt = QDate::currentDate(); + Event inc; + inc.setDtStart(KDateTime(dt)); + KCalCore::Recurrence *r = inc.recurrence(); + r->setMonthly(1); + r->setMonthlyDate(QList() << 1 << 2 << 3); + inc.resetDirtyFields(); + + r->setMonthlyDate(QList() << 3 << 1 << 2); + QVERIFY(inc.dirtyFields().empty()); + + r->setMonthlyDate(QList() << 3 << 1); + QCOMPARE(inc.dirtyFields(), QSet() << IncidenceBase::FieldRecurrence); +} + +void IncidenceTest::testRecurrenceMonthlyPos() +{ + QDate dt = QDate::currentDate(); + RecurrenceRule::WDayPos pos1(1,2); + RecurrenceRule::WDayPos pos2(3,4); + RecurrenceRule::WDayPos pos3(1,2); + Event inc; + inc.setDtStart(KDateTime(dt)); + KCalCore::Recurrence *r = inc.recurrence(); + r->setYearly(1); + r->setMonthlyPos(QList() << pos1 << pos2); + inc.resetDirtyFields(); + + //TODO: test sorting + r->setMonthlyPos(QList() << pos1 << pos2); + QVERIFY(inc.dirtyFields().empty()); + + r->setMonthlyPos(QList() << pos3); + QCOMPARE(inc.dirtyFields(), QSet() << IncidenceBase::FieldRecurrence); +} + +void IncidenceTest::testRecurrenceYearlyDay() +{ + QDate dt = QDate::currentDate(); + Event inc; + inc.setDtStart(KDateTime(dt)); + KCalCore::Recurrence *r = inc.recurrence(); + r->setYearly(1); + r->setYearlyDay(QList() << 1 << 2 << 3); + inc.resetDirtyFields(); + + r->setYearlyDay(QList() << 3 << 1 << 2); + QVERIFY(inc.dirtyFields().empty()); + + r->setYearlyDay(QList() << 3 << 1); + QCOMPARE(inc.dirtyFields(), QSet() << IncidenceBase::FieldRecurrence); +} + +void IncidenceTest::testRecurrenceYearlyMonth() +{ + QDate dt = QDate::currentDate(); + Event inc; + inc.setDtStart(KDateTime(dt)); + KCalCore::Recurrence *r = inc.recurrence(); + r->setYearly(1); + r->setYearlyMonth(QList() << 1 << 2 << 3); + inc.resetDirtyFields(); + + r->setYearlyMonth(QList() << 3 << 1 << 2); + QVERIFY(inc.dirtyFields().empty()); + + r->setYearlyMonth(QList() << 3 << 1); + QCOMPARE(inc.dirtyFields(), QSet() << IncidenceBase::FieldRecurrence); +}