File indexing completed on 2025-01-19 04:52:00

0001 /*
0002     Copyright (c) 2018 Michael Bohlender <michael.bohlender@kdemail.net>
0003     Copyright (c) 2018 Christian Mollekopf <mollekopf@kolabsys.com>
0004     Copyright (c) 2018 RĂ©mi Nicole <minijackson@riseup.net>
0005 
0006     This library is free software; you can redistribute it and/or modify it
0007     under the terms of the GNU Library General Public License as published by
0008     the Free Software Foundation; either version 2 of the License, or (at your
0009     option) any later version.
0010 
0011     This library is distributed in the hope that it will be useful, but WITHOUT
0012     ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
0013     FITNESS FOR A PARTICULAR PURPOSE.  See the GNU Library General Public
0014     License for more details.
0015 
0016     You should have received a copy of the GNU Library General Public License
0017     along with this library; see the file COPYING.LIB.  If not, write to the
0018     Free Software Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA
0019     02110-1301, USA.
0020 */
0021 
0022 #include "perioddayeventmodel.h"
0023 
0024 #include <sink/log.h>
0025 
0026 enum Roles {
0027     Events = EventOccurrenceModel::LastRole,
0028     Date
0029 };
0030 
0031 PeriodDayEventModel::PeriodDayEventModel(QObject *parent)
0032     : QAbstractItemModel(parent)
0033 {
0034 }
0035 
0036 void PeriodDayEventModel::setModel(EventOccurrenceModel *model)
0037 {
0038     beginResetModel();
0039     mSourceModel = model;
0040     auto resetModel = [this] {
0041         beginResetModel();
0042         endResetModel();
0043     };
0044     QObject::connect(model, &QAbstractItemModel::dataChanged, this, resetModel);
0045     QObject::connect(model, &QAbstractItemModel::layoutChanged, this, resetModel);
0046     QObject::connect(model, &QAbstractItemModel::modelReset, this, resetModel);
0047     QObject::connect(model, &QAbstractItemModel::rowsInserted, this, resetModel);
0048     QObject::connect(model, &QAbstractItemModel::rowsMoved, this, resetModel);
0049     QObject::connect(model, &QAbstractItemModel::rowsRemoved, this, resetModel);
0050     endResetModel();
0051 }
0052 
0053 QModelIndex PeriodDayEventModel::index(int row, int column, const QModelIndex &parent) const
0054 {
0055     if (!hasIndex(row, column, parent)) {
0056         return {};
0057     }
0058 
0059     if (!parent.isValid()) {
0060         // Asking for a day
0061         return createIndex(row, column, DAY_ID);
0062     }
0063 
0064     return {};
0065 }
0066 
0067 QModelIndex PeriodDayEventModel::parent(const QModelIndex &index) const
0068 {
0069     if (!index.isValid()) {
0070         return {};
0071     }
0072 
0073     if (index.internalId() == DAY_ID) {
0074         return {};
0075     }
0076 
0077     auto day = index.internalId();
0078 
0079     return this->index(day, 0);
0080 }
0081 
0082 int PeriodDayEventModel::rowCount(const QModelIndex &parent) const
0083 {
0084     if (!parent.isValid() && mSourceModel) {
0085         return mSourceModel->length();
0086     }
0087     return 0;
0088 }
0089 
0090 int PeriodDayEventModel::columnCount(const QModelIndex &) const
0091 {
0092     return 1;
0093 }
0094 
0095 QDateTime PeriodDayEventModel::getStartTimeOfDay(const QDateTime &dateTime, const QDate &today) const
0096 {
0097     if (dateTime.date() != today) {
0098         return QDateTime{today, QTime{0,0}};
0099     }
0100     return dateTime;
0101 }
0102 
0103 QDateTime PeriodDayEventModel::getEndTimeOfDay(const QDateTime &dateTime, const QDate &today) const
0104 {
0105     if (dateTime.date() != today) {
0106         return QDateTime{today, QTime{23, 59, 59}};
0107     }
0108     return dateTime;
0109 }
0110 
0111 QVariant PeriodDayEventModel::data(const QModelIndex &idx, int role) const
0112 {
0113     if (!mSourceModel) {
0114         return {};
0115     }
0116     const auto dayOffset = idx.row();
0117     const QDate startDate = mSourceModel->start();
0118     const auto today = startDate.addDays(dayOffset);
0119     switch (role) {
0120         case Date:
0121             return today;
0122         case Events: {
0123             auto result = QVariantList{};
0124 
0125             QMultiMap<QTime, QModelIndex> sorted;
0126             //Sort events of the day by starting date
0127             for (int row = 0; row < mSourceModel->rowCount(); row++) {
0128                 const auto srcIdx = mSourceModel->index(row, 0, {});
0129                 //filter all-day events (we don't display them here)
0130                 if (srcIdx.data(EventOccurrenceModel::AllDay).toBool()) {
0131                     continue;
0132                 }
0133                 const auto start = srcIdx.data(EventOccurrenceModel::StartTime).toDateTime().toLocalTime();
0134                 const auto end = srcIdx.data(EventOccurrenceModel::EndTime).toDateTime().toLocalTime();
0135                 //not today
0136                 if (end.date() < today || start.date() > today) {
0137                     continue;
0138                 }
0139                 sorted.insert(start.time(), srcIdx);
0140             }
0141 
0142             QMultiMap<QTime, int> indentationStack;
0143             for (auto it = sorted.begin(); it != sorted.end(); it++) {
0144                 const auto srcIdx = it.value();
0145 
0146                 const auto start = getStartTimeOfDay(srcIdx.data(EventOccurrenceModel::StartTime).toDateTime().toLocalTime(), today);
0147                 const auto startTime = start.time();
0148                 const auto end = getEndTimeOfDay(srcIdx.data(EventOccurrenceModel::EndTime).toDateTime().toLocalTime(), today);
0149                 auto endTime = end.time();
0150                 if (!endTime.isValid() || endTime == startTime) {
0151                     //Even without duration we still take some space visually
0152                     endTime = startTime.addSecs(60 * 20);
0153                 }
0154                 const auto duration = startTime.secsTo(endTime) / 3600.0;
0155                 SinkTrace() << "Appending event:" << srcIdx.data(EventOccurrenceModel::Summary) << start << end << duration;
0156 
0157                 //Remove all dates before startTime
0158                 for (auto it = indentationStack.begin(); it != indentationStack.end();) {
0159                     if (it.key() <= startTime) {
0160                         it = indentationStack.erase(it);
0161                     } else {
0162                         ++it;
0163                     }
0164                 }
0165                 const int indentation = indentationStack.size();
0166                 indentationStack.insert(endTime, 0);
0167 
0168 
0169                 result.append(QVariantMap{
0170                     {"text", srcIdx.data(EventOccurrenceModel::Summary)},
0171                     {"startDate", srcIdx.data(EventOccurrenceModel::StartTime)},
0172                     {"description", srcIdx.data(EventOccurrenceModel::Description)},
0173                     {"starts", startTime.hour() + startTime.minute() / 60.},
0174                     {"duration", duration},
0175                     {"color", srcIdx.data(EventOccurrenceModel::Color)},
0176                     {"indentation", indentation},
0177                     {"eventOccurrence", srcIdx.data(EventOccurrenceModel::EventOccurrence)}
0178                 });
0179             }
0180 
0181             return result;
0182         }
0183         default:
0184             Q_ASSERT(false);
0185             return {};
0186     }
0187 }
0188 
0189 QHash<int, QByteArray> PeriodDayEventModel::roleNames() const
0190 {
0191     return {
0192         {Events, "events"},
0193         {Date, "date"}
0194     };
0195 }