Warning, file /libraries/kopeninghours/src/lib/intervalmodel.cpp was not indexed or was modified since last indexation (in which case cross-reference links may be missing, inaccurate or erroneous).
0001 /* 0002 SPDX-FileCopyrightText: 2020 Volker Krause <vkrause@kde.org> 0003 0004 SPDX-License-Identifier: LGPL-2.0-or-later 0005 */ 0006 0007 #include "intervalmodel.h" 0008 0009 #include <KOpeningHours/Display> 0010 #include <KOpeningHours/Interval> 0011 0012 #include <QLocale> 0013 0014 #include <vector> 0015 0016 using namespace KOpeningHours; 0017 0018 namespace KOpeningHours { 0019 0020 struct DayData { 0021 QDate day; 0022 std::vector<Interval> intervals; 0023 }; 0024 0025 class IntervalModelPrivate { 0026 public: 0027 void repopulateModel(); 0028 0029 OpeningHours oh; 0030 std::vector<DayData> m_intervals; 0031 QDate beginDt = QDate::currentDate(); 0032 QDate endDt = QDate::currentDate().addDays(7); 0033 }; 0034 } 0035 0036 static void clipIntervalEnd(Interval &i, const QDateTime &endDt) 0037 { 0038 i.setEnd(i.hasOpenEnd() ? endDt : std::min(i.end(), endDt)); 0039 if (i.end() == endDt && i.hasOpenEndTime()) { // spans in the next day, so this sub-interval isn't open-ended 0040 i.setOpenEndTime(false); 0041 } 0042 } 0043 0044 void IntervalModelPrivate::repopulateModel() 0045 { 0046 m_intervals.clear(); 0047 if (endDt < beginDt || oh.error() != OpeningHours::NoError) { 0048 return; 0049 } 0050 0051 QDate dt = beginDt; 0052 m_intervals.resize(beginDt.daysTo(endDt)); 0053 for (auto &dayData : m_intervals) { 0054 dayData.day = dt; 0055 auto i = oh.interval(QDateTime(dt, {0, 0})); 0056 0057 // clip intervals to the current day, makes displaying much easier 0058 i.setBegin(i.hasOpenBegin() ? QDateTime(dt, {0, 0}) : std::max(i.begin(), QDateTime(dt, {0, 0}))); 0059 dt = dt.addDays(1); 0060 clipIntervalEnd(i, QDateTime(dt, {0, 0})); 0061 0062 dayData.intervals.push_back(i); 0063 while (i.isValid() && i.end() < QDateTime(dt, {0, 0})) { 0064 i = oh.nextInterval(i); 0065 clipIntervalEnd(i, QDateTime(dt, {0, 0})); 0066 dayData.intervals.push_back(i); 0067 } 0068 0069 // fill open end time estimates 0070 for (auto it = dayData.intervals.begin(); it != std::prev(dayData.intervals.end()); ++it) { 0071 auto nextStartDt = QDateTime(dt, {0, 0}); 0072 if (!(*it).hasOpenEndTime() || (*it).state() == Interval::Closed) { 0073 continue; 0074 } 0075 for (auto nextIt = std::next(it); nextIt != dayData.intervals.end(); ++nextIt) { 0076 if ((*nextIt).state() != Interval::Closed) { 0077 nextStartDt = (*nextIt).begin(); 0078 break; 0079 } 0080 } 0081 0082 auto estimatedEnd = nextStartDt == QDateTime(dt, {0, 0}) ? nextStartDt : (*it).end().addSecs((*it).end().secsTo(nextStartDt) / 2); 0083 estimatedEnd = std::min(estimatedEnd, (*it).end().addSecs(4 * 3600)); 0084 (*it).setEstimatedEnd(estimatedEnd); 0085 (*std::next(it)).setBegin(estimatedEnd); 0086 } 0087 } 0088 } 0089 0090 IntervalModel::IntervalModel(QObject *parent) 0091 : QAbstractListModel(parent) 0092 , d(new IntervalModelPrivate) 0093 { 0094 } 0095 0096 IntervalModel::~IntervalModel() = default; 0097 0098 OpeningHours IntervalModel::openingHours() const 0099 { 0100 return d->oh; 0101 } 0102 0103 void IntervalModel::setOpeningHours(const OpeningHours &oh) 0104 { 0105 d->oh = oh; 0106 emit openingHoursChanged(); 0107 0108 beginResetModel(); 0109 d->repopulateModel(); 0110 endResetModel(); 0111 } 0112 0113 QDate IntervalModel::beginDate() const 0114 { 0115 return d->beginDt; 0116 } 0117 0118 void IntervalModel::setBeginDate(QDate beginDate) 0119 { 0120 if (d->beginDt == beginDate) { 0121 return; 0122 } 0123 0124 d->beginDt = beginDate; 0125 emit beginDateChanged(); 0126 0127 beginResetModel(); 0128 d->repopulateModel(); 0129 endResetModel(); 0130 } 0131 0132 QDate IntervalModel::endDate() const 0133 { 0134 return d->endDt; 0135 } 0136 0137 0138 void IntervalModel::setEndDate(QDate endDate) 0139 { 0140 if (d->endDt == endDate) { 0141 return; 0142 } 0143 0144 d->endDt = endDate; 0145 emit endDateChanged(); 0146 0147 beginResetModel(); 0148 d->repopulateModel(); 0149 endResetModel(); 0150 } 0151 0152 int IntervalModel::rowCount(const QModelIndex &parent) const 0153 { 0154 if (parent.isValid()) { 0155 return 0; 0156 } 0157 return d->m_intervals.size(); 0158 } 0159 0160 QVariant IntervalModel::data(const QModelIndex &index, int role) const 0161 { 0162 if (!index.isValid()) { 0163 return {}; 0164 } 0165 0166 switch (role) { 0167 case Qt::DisplayRole: 0168 return QLocale().toString(d->m_intervals[index.row()].day, QLocale::ShortFormat); 0169 case IntervalsRole: 0170 return QVariant::fromValue(d->m_intervals[index.row()].intervals); 0171 case DateRole: 0172 return d->m_intervals[index.row()].day; 0173 case DayBeginTimeRole: 0174 return QDateTime(d->m_intervals[index.row()].day, {0, 0}); 0175 case ShortDayNameRole: 0176 return QLocale().standaloneDayName(d->m_intervals[index.row()].day.dayOfWeek(), QLocale::ShortFormat); 0177 case IsTodayRole: 0178 return d->m_intervals[index.row()].day == QDate::currentDate(); 0179 } 0180 0181 return {}; 0182 } 0183 0184 QHash<int, QByteArray> IntervalModel::roleNames() const 0185 { 0186 auto n = QAbstractListModel::roleNames(); 0187 n.insert(IntervalsRole, "intervals"); 0188 n.insert(DateRole, "date"); 0189 n.insert(DayBeginTimeRole, "dayBegin"); 0190 n.insert(ShortDayNameRole, "shortDayName"); 0191 n.insert(IsTodayRole, "isToday"); 0192 return n; 0193 } 0194 0195 QDate IntervalModel::beginOfWeek(const QDateTime& dt) const 0196 { 0197 auto d = dt.date(); 0198 const auto start = QLocale().firstDayOfWeek(); 0199 if (start < d.dayOfWeek()) { 0200 d = d.addDays(start - d.dayOfWeek()); 0201 } else { 0202 d = d.addDays(start - d.dayOfWeek() - 7); 0203 } 0204 return d; 0205 } 0206 0207 QString IntervalModel::formatTimeColumnHeader(int hour, int minute) const 0208 { 0209 return QLocale().toString(QTime(hour, minute), QLocale::NarrowFormat); 0210 } 0211 0212 QString IntervalModel::currentState() const 0213 { 0214 return Display::currentState(d->oh); 0215 } 0216 0217 #include "moc_intervalmodel.cpp"