diff --git a/framework/src/CMakeLists.txt b/framework/src/CMakeLists.txt index 900a477f..fb5f24ea 100644 --- a/framework/src/CMakeLists.txt +++ b/framework/src/CMakeLists.txt @@ -1,95 +1,96 @@ find_package(Qt5 COMPONENTS REQUIRED Core Concurrent Quick Qml WebEngineWidgets Test WebEngine Gui) find_package(KF5Mime 4.87.0 CONFIG REQUIRED) find_package(KF5CalendarCore CONFIG REQUIRED) find_package(Sink 0.7.0 CONFIG REQUIRED) find_package(KAsync CONFIG REQUIRED) find_package(Gpgme REQUIRED) find_package(KF5Codecs CONFIG REQUIRED) find_package(KF5Contacts CONFIG REQUIRED) include(GenerateExportHeader) set(CMAKE_CXX_VISIBILITY_PRESET default) set(CMAKE_CXX_STANDARD_REQUIRED 14) include_directories(. domain/mime/mimetreeparser domain/ domain/mime) add_library(kubeframework SHARED settings/settings.cpp domain/maillistmodel.cpp domain/folderlistmodel.cpp domain/perioddayeventmodel.cpp + domain/eventmodel.cpp domain/composercontroller.cpp domain/modeltest.cpp domain/retriever.cpp domain/outboxmodel.cpp domain/identitiesmodel.cpp domain/recepientautocompletionmodel.cpp domain/settings/accountsettings.cpp domain/selector.cpp domain/completer.cpp domain/mouseproxy.cpp domain/contactcontroller.cpp domain/controller.cpp domain/peoplemodel.cpp domain/textdocumenthandler.cpp domain/mime/htmlutils.cpp domain/mime/messageparser.cpp domain/mime/attachmentmodel.cpp domain/mime/partmodel.cpp domain/mime/mailtemplates.cpp accounts/accountfactory.cpp accounts/accountsmodel.cpp fabric.cpp sinkfabric.cpp kubeimage.cpp clipboardproxy.cpp krecursivefilterproxymodel.cpp startupcheck.cpp keyring.cpp domainobjectcontroller.cpp extensionmodel.cpp viewhighlighter.cpp file.cpp logmodel.cpp entitymodel.cpp ) generate_export_header(kubeframework BASE_NAME Kube EXPORT_FILE_NAME kube_export.h) set_target_properties(kubeframework PROPERTIES ENABLE_EXPORTS 1 WINDOWS_EXPORT_ALL_SYMBOLS 1 ) target_link_libraries(kubeframework sink kube_otp mailcrypto Qt5::Core Qt5::Quick Qt5::Qml Qt5::WebEngineWidgets Qt5::Test Qt5::WebEngine Qt5::Gui KF5::Codecs KF5::Contacts KF5::CalendarCore KAsync ) install(TARGETS kubeframework DESTINATION ${KDE_INSTALL_TARGETS_DEFAULT_ARGS}) add_library(frameworkplugin SHARED frameworkplugin.cpp) target_link_libraries(frameworkplugin kubeframework ) install(TARGETS frameworkplugin DESTINATION ${FRAMEWORK_INSTALL_DIR}) set(BUILD_TESTING ON) add_subdirectory(tests) add_subdirectory(domain/mime) add_subdirectory(domain/mime/tests) add_subdirectory(domain/mime/mimetreeparser) add_subdirectory(domain/settings/tests) feature_summary(WHAT ALL FATAL_ON_MISSING_REQUIRED_PACKAGES) diff --git a/framework/src/domain/eventmodel.cpp b/framework/src/domain/eventmodel.cpp new file mode 100644 index 00000000..a3856378 --- /dev/null +++ b/framework/src/domain/eventmodel.cpp @@ -0,0 +1,206 @@ +/* + 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 "eventmodel.h" + +#include +#include +#include + +#include +#include +#include + +#include +#include +#include + +#include + +using Event = Sink::ApplicationDomain::Event; +using Calendar = Sink::ApplicationDomain::Calendar; + + +EventModel::EventModel(QObject *parent) + : QAbstractItemModel(parent), + mCalendarCache{EntityCache::Ptr::create()}, + mCalendar{new KCalCore::MemoryCalendar{QTimeZone::systemTimeZone()}} +{ + mRefreshTimer.setSingleShot(true); + QObject::connect(&mRefreshTimer, &QTimer::timeout, this, &EventModel::updateFromSource); +} + +void EventModel::updateQuery(const QDate &start, const QDate &end, const QSet &calendarFilter) +{ + qWarning() << "Update query"; + mCalendarFilter = calendarFilter; + Sink::Query query; + query.setFlags(Sink::Query::LiveQuery); + query.request(); + query.request(); + query.request(); + query.request(); + query.request(); + query.request(); + query.request(); + + query.filter(Sink::Query::Comparator(QVariantList{start, end}, Sink::Query::Comparator::Overlap)); + + mSourceModel = Sink::Store::loadModel(query); + + QObject::connect(mSourceModel.data(), &QAbstractItemModel::dataChanged, this, &EventModel::refreshView); + QObject::connect(mSourceModel.data(), &QAbstractItemModel::layoutChanged, this, &EventModel::refreshView); + QObject::connect(mSourceModel.data(), &QAbstractItemModel::modelReset, this, &EventModel::refreshView); + QObject::connect(mSourceModel.data(), &QAbstractItemModel::rowsInserted, this, &EventModel::refreshView); + QObject::connect(mSourceModel.data(), &QAbstractItemModel::rowsMoved, this, &EventModel::refreshView); + QObject::connect(mSourceModel.data(), &QAbstractItemModel::rowsRemoved, this, &EventModel::refreshView); + + refreshView(); +} + +void EventModel::refreshView() +{ + if (!mRefreshTimer.isActive()) { + //Instant update, but then only refresh every 50ms max. + updateFromSource(); + mRefreshTimer.start(50); + } +} + +void EventModel::updateFromSource() +{ + beginResetModel(); + + mEvents.clear(); + + for (int i = 0; i < mSourceModel->rowCount(); ++i) { + auto event = mSourceModel->index(i, 0).data(Sink::Store::DomainObjectRole).value(); + if (!mCalendarFilter.contains(event->getCalendar())) { + continue; + } + + //Parse the event + auto icalEvent = KCalCore::ICalFormat().readIncidence(event->getIcal()).dynamicCast(); + if(!icalEvent) { + SinkWarning() << "Invalid ICal to process, ignoring..."; + continue; + } + + if (icalEvent->recurs()) { + const auto duration = icalEvent->hasDuration() ? icalEvent->duration().asSeconds() : 0; + KCalCore::OccurrenceIterator occurrenceIterator{*mCalendar, icalEvent, QDateTime{mStart, {0, 0, 0}}, QDateTime{mEnd, {12, 59, 59}}}; + while (occurrenceIterator.hasNext()) { + occurrenceIterator.next(); + const auto start = occurrenceIterator.occurrenceStartDate(); + const auto end = start.addSecs(duration); + if (start.date() < mEnd && end.date() >= mStart) { + mEvents.append({start, end, occurrenceIterator.incidence(), getColor(event->getCalendar())}); + } + } + } else { + if (icalEvent->dtStart().date() < mEnd && icalEvent->dtEnd().date() >= mStart) { + mEvents.append({icalEvent->dtStart(), icalEvent->dtEnd(), icalEvent, getColor(event->getCalendar())}); + } + } + } + + endResetModel(); +} + +QModelIndex EventModel::index(int row, int column, const QModelIndex &parent) const +{ + if (!hasIndex(row, column, parent)) { + return {}; + } + + if (!parent.isValid()) { + return createIndex(row, column); + } + return {}; +} + +QModelIndex EventModel::parent(const QModelIndex &index) const +{ + return {}; +} + +int EventModel::rowCount(const QModelIndex &parent) const +{ + if (!parent.isValid()) { + return mEvents.size(); + } + return 0; +} + +int EventModel::columnCount(const QModelIndex &parent) const +{ + return 1; +} + +QByteArray EventModel::getColor(const QByteArray &calendar) const +{ + const auto color = mCalendarCache->getProperty(calendar, "color").toByteArray(); + if (color.isEmpty()) { + qWarning() << "Failed to get color for calendar " << calendar; + } + return color; +} + +// QDateTime EventModel::getStartTimeOfDay(const QDateTime &dateTime, int day) const +// { +// if (bucketOf(dateTime.date()) < day) { +// return QDateTime{mPeriodStart.addDays(day), QTime{0,0}}; +// } +// return dateTime; +// } + +// QDateTime EventModel::getEndTimeOfDay(const QDateTime &dateTime, int day) const +// { +// if (bucketOf(dateTime.date()) > day) { +// return QDateTime{mPeriodStart.addDays(day), QTime{23, 59, 59}}; +// } +// return dateTime; +// } + +QVariant EventModel::data(const QModelIndex &idx, int role) const +{ + if (!hasIndex(idx.row(), idx.column())) { + return {}; + } + auto event = mEvents.at(idx.row()); + auto icalEvent = event.incidence; + switch (role) { + case Summary: + return icalEvent->summary(); + case Description: + return icalEvent->description(); + case StartTime: + return event.start; + case EndTime: + return event.end; + case Color: + return event.color; + default: + SinkWarning() << "Unknown role for event:" << QMetaEnum::fromType().valueToKey(role); + return {}; + } +} + diff --git a/framework/src/domain/eventmodel.h b/framework/src/domain/eventmodel.h new file mode 100644 index 00000000..19307a67 --- /dev/null +++ b/framework/src/domain/eventmodel.h @@ -0,0 +1,87 @@ +/* + 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. +*/ + +#pragma once +#include "kube_export.h" + +#include +#include +#include +#include +#include +#include + +namespace KCalCore { + class MemoryCalendar; + class Incidence; +} +class EntityCacheInterface; + +class KUBE_EXPORT EventModel : public QAbstractItemModel +{ + Q_OBJECT +public: + enum Roles { + Summary = Qt::UserRole + 1, + Description, + StartTime, + EndTime, + Color + }; + Q_ENUM(Roles); + EventModel(QObject *parent = nullptr); + ~EventModel() = default; + + QModelIndex index(int row, int column, const QModelIndex &parent = {}) const override; + QModelIndex parent(const QModelIndex &index) const override; + + int rowCount(const QModelIndex &parent) const override; + int columnCount(const QModelIndex &parent) const override; + + QVariant data(const QModelIndex &index, int role) const override; + + void updateQuery(const QDate &start, const QDate &end, const QSet &calendarFilter); + +private: + void updateQuery(); + + void refreshView(); + void updateFromSource(); + QByteArray getColor(const QByteArray &calendar) const; + + QSharedPointer mSourceModel; + QSet mCalendarFilter; + QDate mStart; + QDate mEnd; + QSharedPointer mCalendarCache; + + QSharedPointer mCalendar; + QTimer mRefreshTimer; + + struct Occurrence { + QDateTime start; + QDateTime end; + QSharedPointer incidence; + QByteArray color; + }; + + QList mEvents; +};