diff --git a/framework/src/domain/perioddayeventmodel.cpp b/framework/src/domain/perioddayeventmodel.cpp index 17bbf588..3f46788c 100644 --- a/framework/src/domain/perioddayeventmodel.cpp +++ b/framework/src/domain/perioddayeventmodel.cpp @@ -1,195 +1,195 @@ /* Copyright (c) 2018 Michael Bohlender Copyright (c) 2018 Christian Mollekopf Copyright (c) 2018 RĂ©mi Nicole 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 "perioddayeventmodel.h" #include enum Roles { Events = EventOccurrenceModel::LastRole, Date }; PeriodDayEventModel::PeriodDayEventModel(QObject *parent) : QAbstractItemModel(parent) { } void PeriodDayEventModel::setModel(EventOccurrenceModel *model) { beginResetModel(); mSourceModel = model; auto resetModel = [this] { beginResetModel(); endResetModel(); }; QObject::connect(model, &QAbstractItemModel::dataChanged, this, resetModel); QObject::connect(model, &QAbstractItemModel::layoutChanged, this, resetModel); QObject::connect(model, &QAbstractItemModel::modelReset, this, resetModel); QObject::connect(model, &QAbstractItemModel::rowsInserted, this, resetModel); QObject::connect(model, &QAbstractItemModel::rowsMoved, this, resetModel); QObject::connect(model, &QAbstractItemModel::rowsRemoved, this, resetModel); endResetModel(); } QModelIndex PeriodDayEventModel::index(int row, int column, const QModelIndex &parent) const { if (!hasIndex(row, column, parent)) { return {}; } if (!parent.isValid()) { // Asking for a day return createIndex(row, column, DAY_ID); } return {}; } QModelIndex PeriodDayEventModel::parent(const QModelIndex &index) const { if (!index.isValid()) { return {}; } if (index.internalId() == DAY_ID) { return {}; } auto day = index.internalId(); return this->index(day, 0); } int PeriodDayEventModel::rowCount(const QModelIndex &parent) const { if (!parent.isValid() && mSourceModel) { return mSourceModel->length(); } return 0; } int PeriodDayEventModel::columnCount(const QModelIndex &) const { return 1; } QDateTime PeriodDayEventModel::getStartTimeOfDay(const QDateTime &dateTime, const QDate &today) const { if (dateTime.date() != today) { return QDateTime{today, QTime{0,0}}; } return dateTime; } QDateTime PeriodDayEventModel::getEndTimeOfDay(const QDateTime &dateTime, const QDate &today) const { if (dateTime.date() != today) { return QDateTime{today, QTime{23, 59, 59}}; } return dateTime; } QVariant PeriodDayEventModel::data(const QModelIndex &idx, int role) const { if (!mSourceModel) { return {}; } const auto dayOffset = idx.row(); const QDate startDate = mSourceModel->start(); const auto today = startDate.addDays(dayOffset); switch (role) { case Date: return today; case Events: { auto result = QVariantList{}; QMultiMap sorted; //Sort events of the day by starting date for (int row = 0; row < mSourceModel->rowCount(); row++) { const auto srcIdx = mSourceModel->index(row, 0, {}); //filter all-day events (we don't display them here) if (srcIdx.data(EventOccurrenceModel::AllDay).toBool()) { continue; } const auto start = srcIdx.data(EventOccurrenceModel::StartTime).toDateTime().toLocalTime(); const auto end = srcIdx.data(EventOccurrenceModel::EndTime).toDateTime().toLocalTime(); //not today if (end.date() < today || start.date() > today) { continue; } sorted.insert(start.time(), srcIdx); } QMap indentationStack; for (auto it = sorted.begin(); it != sorted.end(); it++) { const auto srcIdx = it.value(); const auto start = getStartTimeOfDay(srcIdx.data(EventOccurrenceModel::StartTime).toDateTime().toLocalTime(), today); const auto startTime = start.time(); const auto end = getEndTimeOfDay(srcIdx.data(EventOccurrenceModel::EndTime).toDateTime().toLocalTime(), today); auto endTime = end.time(); - if (!endTime.isValid()) { + if (!endTime.isValid() || endTime == startTime) { //Even without duration we still take some space visually endTime = startTime.addSecs(60 * 20); } const auto duration = startTime.secsTo(endTime) / 3600.0; SinkTrace() << "Appending event:" << srcIdx.data(EventOccurrenceModel::Summary) << start << end << duration; //Remove all dates before startTime for (auto it = indentationStack.begin(); it != indentationStack.end();) { if (it.key() <= startTime) { it = indentationStack.erase(it); } else { ++it; } } const int indentation = indentationStack.size(); indentationStack.insert(endTime, 0); result.append(QVariantMap{ {"text", srcIdx.data(EventOccurrenceModel::Summary)}, {"startDate", srcIdx.data(EventOccurrenceModel::StartTime)}, {"description", srcIdx.data(EventOccurrenceModel::Description)}, {"starts", startTime.hour() + startTime.minute() / 60.}, {"duration", duration}, {"color", srcIdx.data(EventOccurrenceModel::Color)}, {"indentation", indentation}, {"eventOccurrence", srcIdx.data(EventOccurrenceModel::EventOccurrence)} }); } return result; } default: Q_ASSERT(false); return {}; } } QHash PeriodDayEventModel::roleNames() const { return { {Events, "events"}, {Date, "date"} }; } diff --git a/framework/src/tests/eventoccurrencemodeltest.cpp b/framework/src/tests/eventoccurrencemodeltest.cpp index cdc915b6..c2228ba7 100644 --- a/framework/src/tests/eventoccurrencemodeltest.cpp +++ b/framework/src/tests/eventoccurrencemodeltest.cpp @@ -1,145 +1,163 @@ #include #include #include #include #include #include #include #include #include "eventoccurrencemodel.h" #include "multidayeventmodel.h" #include "perioddayeventmodel.h" class EventOccurrenceModelTest : public QObject { Q_OBJECT private slots: void initTestCase() { Sink::Test::initTest(); } void testEventOccurrenceModel() { Sink::ApplicationDomain::DummyResource::create("account1"); using namespace Sink::ApplicationDomain; auto account = ApplicationDomainType::createEntity(); Sink::Store::create(account).exec().waitForFinished(); auto resource = Sink::ApplicationDomain::DummyResource::create(account.identifier()); Sink::Store::create(resource).exec().waitForFinished(); auto calendar1 = ApplicationDomainType::createEntity(resource.identifier()); Sink::Store::create(calendar1).exec().waitForFinished(); const QDateTime start{{2018, 04, 17}, {6, 0, 0}}; { auto event1 = ApplicationDomainType::createEntity(resource.identifier()); auto calcoreEvent = QSharedPointer::create(); calcoreEvent->setUid("event1"); calcoreEvent->setSummary("summary1"); calcoreEvent->setDescription("description"); calcoreEvent->setDtStart(start); calcoreEvent->setDuration(3600); calcoreEvent->setAllDay(false); event1.setIcal(KCalCore::ICalFormat().toICalString(calcoreEvent).toUtf8()); event1.setCalendar(calendar1); Sink::Store::create(event1).exec().waitForFinished(); } { auto event2 = ApplicationDomainType::createEntity(resource.identifier()); auto calcoreEvent = QSharedPointer::create(); calcoreEvent->setUid("event2"); calcoreEvent->setSummary("summary2"); calcoreEvent->setDescription("description"); calcoreEvent->setDtStart(start.addDays(1)); calcoreEvent->setDuration(3600); calcoreEvent->setAllDay(false); calcoreEvent->recurrence()->setDaily(1); event2.setIcal(KCalCore::ICalFormat().toICalString(calcoreEvent).toUtf8()); event2.setCalendar(calendar1); Sink::Store::create(event2).exec().waitForFinished(); } { auto event3 = ApplicationDomainType::createEntity(resource.identifier()); auto calcoreEvent = QSharedPointer::create(); calcoreEvent->setUid("event3"); calcoreEvent->setSummary("summary3"); calcoreEvent->setDtStart(start.addDays(1)); calcoreEvent->setDuration(3600); calcoreEvent->setAllDay(false); event3.setIcal(KCalCore::ICalFormat().toICalString(calcoreEvent).toUtf8()); event3.setCalendar(calendar1); Sink::Store::create(event3).exec().waitForFinished(); } { auto event4 = ApplicationDomainType::createEntity(resource.identifier()); auto calcoreEvent = QSharedPointer::create(); - calcoreEvent->setUid("event3"); - calcoreEvent->setSummary("summary3"); + calcoreEvent->setUid("event4"); + calcoreEvent->setSummary("summary4"); calcoreEvent->setDtStart(start.addDays(2)); calcoreEvent->setAllDay(true); event4.setIcal(KCalCore::ICalFormat().toICalString(calcoreEvent).toUtf8()); event4.setCalendar(calendar1); Sink::Store::create(event4).exec().waitForFinished(); } + { + auto event1 = ApplicationDomainType::createEntity(resource.identifier()); + auto calcoreEvent = QSharedPointer::create(); + calcoreEvent->setUid("event5"); + calcoreEvent->setSummary("summary5"); + calcoreEvent->setDescription("description"); + calcoreEvent->setDtStart(start); + calcoreEvent->setDuration(3600); + calcoreEvent->setAllDay(false); + event1.setIcal(KCalCore::ICalFormat().toICalString(calcoreEvent).toUtf8()); + event1.setCalendar(calendar1); + Sink::Store::create(event1).exec().waitForFinished(); + } Sink::ResourceControl::flushMessageQueue(resource.identifier()).exec().waitForFinished(); { - const int expectedNumberOfOccurreces = 9; + const int expectedNumberOfOccurreces = 10; const int numberOfDays = 7; EventOccurrenceModel model; model.setStart(start.date()); model.setLength(numberOfDays); model.setCalendarFilter({calendar1.identifier()}); QTRY_COMPARE(model.rowCount({}), expectedNumberOfOccurreces); auto countEvents = [&] (const QVariantList &lines) { int count = 0; for (const auto &line : lines) { count += line.toList().size(); } return count; }; //Check the multidayevent model { MultiDayEventModel multiDayModel; multiDayModel.setModel(&model); QTRY_COMPARE(multiDayModel.rowCount({}), 1); QTRY_COMPARE(countEvents(multiDayModel.index(0, 0, {}).data(multiDayModel.roleNames().key("events")).value()), expectedNumberOfOccurreces); //Count lines QTRY_COMPARE(multiDayModel.index(0, 0, {}).data(multiDayModel.roleNames().key("events")).value().size(), 3); } { PeriodDayEventModel multiDayModel; multiDayModel.setModel(&model); QTRY_COMPARE(multiDayModel.rowCount({}), numberOfDays); { const auto events = multiDayModel.index(0, 0, {}).data(multiDayModel.roleNames().key("events")).value(); - QCOMPARE(events.size(), 1); + QCOMPARE(events.size(), 2); + QCOMPARE(events[0].toMap()["indentation"].toInt(), 0); + QCOMPARE(events[1].toMap()["indentation"].toInt(), 1); } { const auto events = multiDayModel.index(1, 0, {}).data(multiDayModel.roleNames().key("events")).value(); QCOMPARE(events.size(), 2); + QCOMPARE(events[0].toMap()["indentation"].toInt(), 0); + QCOMPARE(events[1].toMap()["indentation"].toInt(), 1); } { const auto events = multiDayModel.index(2, 0, {}).data(multiDayModel.roleNames().key("events")).value(); QCOMPARE(events.size(), 1); + QCOMPARE(events[0].toMap()["indentation"].toInt(), 0); } } model.setCalendarFilter({}); QTRY_COMPARE(model.rowCount({}), 0); } } }; QTEST_MAIN(EventOccurrenceModelTest) #include "eventoccurrencemodeltest.moc"