KOpeningHours

intervalmodel.cpp
1/*
2 SPDX-FileCopyrightText: 2020 Volker Krause <vkrause@kde.org>
3
4 SPDX-License-Identifier: LGPL-2.0-or-later
5*/
6
7#include "intervalmodel.h"
8
9#include <KOpeningHours/Display>
10#include <KOpeningHours/Interval>
11
12#include <QLocale>
13
14#include <vector>
15
16using namespace KOpeningHours;
17
18namespace KOpeningHours {
19
20struct DayData {
21 QDate day;
22 std::vector<Interval> intervals;
23};
24
25class IntervalModelPrivate {
26public:
27 void repopulateModel();
28
29 OpeningHours oh;
30 std::vector<DayData> m_intervals;
31 QDate beginDt = QDate::currentDate();
32 QDate endDt = QDate::currentDate().addDays(7);
33};
34}
35
36static void clipIntervalEnd(Interval &i, const QDateTime &endDt)
37{
38 i.setEnd(i.hasOpenEnd() ? endDt : std::min(i.end(), endDt));
39 if (i.end() == endDt && i.hasOpenEndTime()) { // spans in the next day, so this sub-interval isn't open-ended
40 i.setOpenEndTime(false);
41 }
42}
43
44void IntervalModelPrivate::repopulateModel()
45{
46 m_intervals.clear();
47 if (endDt < beginDt || oh.error() != OpeningHours::NoError) {
48 return;
49 }
50
51 QDate dt = beginDt;
52 m_intervals.resize(beginDt.daysTo(endDt));
53 for (auto &dayData : m_intervals) {
54 dayData.day = dt;
55 auto i = oh.interval(QDateTime(dt, {0, 0}));
56
57 // clip intervals to the current day, makes displaying much easier
58 i.setBegin(i.hasOpenBegin() ? QDateTime(dt, {0, 0}) : std::max(i.begin(), QDateTime(dt, {0, 0})));
59 dt = dt.addDays(1);
60 clipIntervalEnd(i, QDateTime(dt, {0, 0}));
61
62 dayData.intervals.push_back(i);
63 while (i.isValid() && i.end() < QDateTime(dt, {0, 0})) {
64 i = oh.nextInterval(i);
65 clipIntervalEnd(i, QDateTime(dt, {0, 0}));
66 dayData.intervals.push_back(i);
67 }
68
69 // fill open end time estimates
70 for (auto it = dayData.intervals.begin(); it != std::prev(dayData.intervals.end()); ++it) {
71 auto nextStartDt = QDateTime(dt, {0, 0});
72 if (!(*it).hasOpenEndTime() || (*it).state() == Interval::Closed) {
73 continue;
74 }
75 for (auto nextIt = std::next(it); nextIt != dayData.intervals.end(); ++nextIt) {
76 if ((*nextIt).state() != Interval::Closed) {
77 nextStartDt = (*nextIt).begin();
78 break;
79 }
80 }
81
82 auto estimatedEnd = nextStartDt == QDateTime(dt, {0, 0}) ? nextStartDt : (*it).end().addSecs((*it).end().secsTo(nextStartDt) / 2);
83 estimatedEnd = std::min(estimatedEnd, (*it).end().addSecs(4 * 3600));
84 (*it).setEstimatedEnd(estimatedEnd);
85 (*std::next(it)).setBegin(estimatedEnd);
86 }
87 }
88}
89
90IntervalModel::IntervalModel(QObject *parent)
91 : QAbstractListModel(parent)
92 , d(new IntervalModelPrivate)
93{
94}
95
96IntervalModel::~IntervalModel() = default;
97
99{
100 return d->oh;
101}
102
103void IntervalModel::setOpeningHours(const OpeningHours &oh)
104{
105 d->oh = oh;
106 emit openingHoursChanged();
107
109 d->repopulateModel();
111}
112
114{
115 return d->beginDt;
116}
117
118void IntervalModel::setBeginDate(QDate beginDate)
119{
120 if (d->beginDt == beginDate) {
121 return;
122 }
123
124 d->beginDt = beginDate;
125 emit beginDateChanged();
126
128 d->repopulateModel();
130}
131
133{
134 return d->endDt;
135}
136
137
138void IntervalModel::setEndDate(QDate endDate)
139{
140 if (d->endDt == endDate) {
141 return;
142 }
143
144 d->endDt = endDate;
145 emit endDateChanged();
146
148 d->repopulateModel();
150}
151
152int IntervalModel::rowCount(const QModelIndex &parent) const
153{
154 if (parent.isValid()) {
155 return 0;
156 }
157 return d->m_intervals.size();
158}
159
160QVariant IntervalModel::data(const QModelIndex &index, int role) const
161{
162 if (!index.isValid()) {
163 return {};
164 }
165
166 switch (role) {
167 case Qt::DisplayRole:
168 return QLocale().toString(d->m_intervals[index.row()].day, QLocale::ShortFormat);
169 case IntervalsRole:
170 return QVariant::fromValue(d->m_intervals[index.row()].intervals);
171 case DateRole:
172 return d->m_intervals[index.row()].day;
173 case DayBeginTimeRole:
174 return QDateTime(d->m_intervals[index.row()].day, {0, 0});
175 case ShortDayNameRole:
176 return QLocale().standaloneDayName(d->m_intervals[index.row()].day.dayOfWeek(), QLocale::ShortFormat);
177 case IsTodayRole:
178 return d->m_intervals[index.row()].day == QDate::currentDate();
179 }
180
181 return {};
182}
183
184QHash<int, QByteArray> IntervalModel::roleNames() const
185{
187 n.insert(IntervalsRole, "intervals");
188 n.insert(DateRole, "date");
189 n.insert(DayBeginTimeRole, "dayBegin");
190 n.insert(ShortDayNameRole, "shortDayName");
191 n.insert(IsTodayRole, "isToday");
192 return n;
193}
194
196{
197 auto d = dt.date();
198 const auto start = QLocale().firstDayOfWeek();
199 if (start < d.dayOfWeek()) {
200 d = d.addDays(start - d.dayOfWeek());
201 } else {
202 d = d.addDays(start - d.dayOfWeek() - 7);
203 }
204 return d;
205}
206
208{
209 return QLocale().toString(QTime(hour, minute), QLocale::NarrowFormat);
210}
211
213{
214 return Display::currentState(d->oh);
215}
216
217#include "moc_intervalmodel.cpp"
static Q_INVOKABLE QString currentState(const KOpeningHours::OpeningHours &oh)
Localized description of the current opening state, and upcoming transitions.
Definition display.cpp:17
QDate endDate
End of the date range to show in this model.
QString currentState
Description of the current status as a translated human-readable string.
@ DateRole
The date represented by the current row.
@ IsTodayRole
true if the row represents the current day.
@ DayBeginTimeRole
Same as DateRole, but as a date/time object.
@ IntervalsRole
All intervals in the current row.
@ ShortDayNameRole
Localized short day name for the current row.
Q_INVOKABLE QDate beginOfWeek(const QDateTime &dt) const
Returns the day the week containing dt begins, based on the current locale.
QDate beginDate
Begin of the date range to show in this model.
KOpeningHours::OpeningHours openingHours
The opening hours expression shown in this model.
Q_INVOKABLE QString formatTimeColumnHeader(int hour, int minute) const
Localized formatting for time column headers.
A time interval for which an opening hours expression has been evaluated.
Definition interval.h:25
bool isValid() const
Default constructed empty/invalid interval.
Definition interval.cpp:60
An OSM opening hours specification.
Q_INVOKABLE KOpeningHours::Interval interval(const QDateTime &dt) const
Returns the interval containing dt.
Q_INVOKABLE KOpeningHours::Interval nextInterval(const KOpeningHours::Interval &interval) const
Returns the interval immediately following interval.
@ NoError
there is no error, the expression is valid and can be used
Q_SCRIPTABLE Q_NOREPLY void start()
OSM opening hours parsing and evaluation.
Definition display.h:16
const QList< QKeySequence > & begin()
const QList< QKeySequence > & end()
virtual QHash< int, QByteArray > roleNames() const const
virtual QModelIndex index(int row, int column, const QModelIndex &parent) const const override
QDate addDays(qint64 ndays) const const
QDate currentDate()
qint64 daysTo(QDate d) const const
QDate date() const const
Qt::DayOfWeek firstDayOfWeek() const const
QString standaloneDayName(int day, FormatType type) const const
QString toString(QDate date, FormatType format) const const
bool isValid() const const
int row() const const
QObject * parent() const const
DisplayRole
QVariant fromValue(T &&value)
This file is part of the KDE documentation.
Documentation copyright © 1996-2024 The KDE developers.
Generated on Tue Mar 26 2024 11:13:17 by doxygen 1.10.0 written by Dimitri van Heesch, © 1997-2006

KDE's Doxygen guidelines are available online.