File indexing completed on 2024-05-12 04:42:52
0001 /* 0002 SPDX-FileCopyrightText: 2019 Volker Krause <vkrause@kde.org> 0003 0004 SPDX-License-Identifier: LGPL-2.0-or-later 0005 */ 0006 0007 #include "stopoverquerymodel.h" 0008 #include "abstractquerymodel_p.h" 0009 #include "logging.h" 0010 #include "datatypes/stopoverutil_p.h" 0011 0012 #include <KPublicTransport/Attribution> 0013 #include <KPublicTransport/Manager> 0014 #include <KPublicTransport/Stopover> 0015 #include <KPublicTransport/StopoverReply> 0016 0017 #include <QDateTime> 0018 #include <QDebug> 0019 0020 using namespace KPublicTransport; 0021 0022 namespace KPublicTransport { 0023 class StopoverQueryModelPrivate : public AbstractQueryModelPrivate 0024 { 0025 public: 0026 void doQuery() override; 0027 void doClearResults() override; 0028 void mergeResults(const std::vector<Stopover> &newDepartures); 0029 0030 std::vector<Stopover> m_departures; 0031 0032 StopoverRequest m_request; 0033 StopoverRequest m_nextRequest; 0034 StopoverRequest m_prevRequest; 0035 0036 Q_DECLARE_PUBLIC(StopoverQueryModel) 0037 }; 0038 } 0039 0040 void StopoverQueryModelPrivate::doQuery() 0041 { 0042 Q_Q(StopoverQueryModel); 0043 if (!m_manager || !m_request.isValid()) { 0044 return; 0045 } 0046 0047 setLoading(true); 0048 m_nextRequest = {}; 0049 m_prevRequest = {}; 0050 Q_EMIT q->canQueryPrevNextChanged(); 0051 0052 auto reply = m_manager->queryStopover(m_request); 0053 monitorReply(reply); 0054 QObject::connect(reply, &KPublicTransport::StopoverReply::finished, q, [reply, q, this]{ 0055 if (reply->error() == KPublicTransport::StopoverReply::NoError) { 0056 m_nextRequest = reply->nextRequest(); 0057 m_prevRequest = reply->previousRequest(); 0058 Q_EMIT q->canQueryPrevNextChanged(); 0059 } 0060 }); 0061 QObject::connect(reply, &KPublicTransport::StopoverReply::updated,q, [reply, this]() { 0062 mergeResults(reply->takeResult()); 0063 }); 0064 } 0065 0066 void StopoverQueryModelPrivate::doClearResults() 0067 { 0068 m_departures.clear(); 0069 } 0070 0071 void StopoverQueryModelPrivate::mergeResults(const std::vector<Stopover> &newDepartures) 0072 { 0073 Q_Q(StopoverQueryModel); 0074 for (const auto &dep : newDepartures) { 0075 auto it = std::lower_bound(m_departures.begin(), m_departures.end(), dep, [this](const auto &lhs, const auto &rhs) { 0076 return StopoverUtil::timeLessThan(m_request, lhs, rhs); 0077 }); 0078 0079 bool found = false; 0080 while (it != m_departures.end() && StopoverUtil::timeEqual(m_request, dep, *it)) { 0081 if (Stopover::isSame(dep, *it)) { 0082 *it = Stopover::merge(*it, dep); 0083 found = true; 0084 const auto row = std::distance(m_departures.begin(), it); 0085 const auto idx = q->index(row, 0); 0086 Q_EMIT q->dataChanged(idx, idx); 0087 break; 0088 } else { 0089 ++it; 0090 } 0091 } 0092 if (found) { 0093 continue; 0094 } 0095 0096 const auto row = std::distance(m_departures.begin(), it); 0097 q->beginInsertRows({}, row, row); 0098 m_departures.insert(it, dep); 0099 q->endInsertRows(); 0100 } 0101 } 0102 0103 0104 StopoverQueryModel::StopoverQueryModel(QObject *parent) 0105 : AbstractQueryModel(new StopoverQueryModelPrivate, parent) 0106 { 0107 connect(this, &AbstractQueryModel::loadingChanged, this, &StopoverQueryModel::canQueryPrevNextChanged); 0108 } 0109 0110 StopoverQueryModel::~StopoverQueryModel() = default; 0111 0112 StopoverRequest StopoverQueryModel::request() const 0113 { 0114 Q_D(const StopoverQueryModel); 0115 return d->m_request; 0116 } 0117 0118 void StopoverQueryModel::setRequest(const StopoverRequest &req) 0119 { 0120 Q_D(StopoverQueryModel); 0121 d->m_request = req; 0122 Q_EMIT requestChanged(); 0123 d->query(); 0124 } 0125 0126 bool StopoverQueryModel::canQueryNext() const 0127 { 0128 Q_D(const StopoverQueryModel); 0129 return !d->m_loading && !d->m_departures.empty() && d->m_nextRequest.isValid(); 0130 } 0131 0132 void StopoverQueryModel::queryNext() 0133 { 0134 Q_D(StopoverQueryModel); 0135 if (!canQueryNext()) { 0136 qCWarning(Log) << "Cannot query next journeys"; 0137 return; 0138 } 0139 0140 d->setLoading(true); 0141 auto reply = d->m_manager->queryStopover(d->m_nextRequest); 0142 d->monitorReply(reply); 0143 QObject::connect(reply, &KPublicTransport::StopoverReply::finished, this, [reply, this] { 0144 Q_D(StopoverQueryModel); 0145 if (reply->error() == KPublicTransport::StopoverReply::NoError) { 0146 d->m_nextRequest = reply->nextRequest(); 0147 } else { 0148 d->m_nextRequest = {}; 0149 } 0150 Q_EMIT canQueryPrevNextChanged(); 0151 }); 0152 QObject::connect(reply, &KPublicTransport::StopoverReply::updated, this, [reply, this]() { 0153 Q_D(StopoverQueryModel); 0154 d->mergeResults(reply->takeResult()); 0155 }); 0156 } 0157 0158 bool StopoverQueryModel::canQueryPrevious() const 0159 { 0160 Q_D(const StopoverQueryModel); 0161 return !d->m_loading && !d->m_departures.empty() && d->m_prevRequest.isValid(); 0162 } 0163 0164 void StopoverQueryModel::queryPrevious() 0165 { 0166 Q_D(StopoverQueryModel); 0167 if (!canQueryPrevious()) { 0168 qCWarning(Log) << "Cannot query previous journeys"; 0169 return; 0170 } 0171 0172 d->setLoading(true); 0173 auto reply = d->m_manager->queryStopover(d->m_prevRequest); 0174 d->monitorReply(reply); 0175 QObject::connect(reply, &KPublicTransport::StopoverReply::finished, this, [reply, this] { 0176 Q_D(StopoverQueryModel); 0177 if (reply->error() == KPublicTransport::StopoverReply::NoError) { 0178 d->m_prevRequest = reply->previousRequest(); 0179 } else { 0180 d->m_prevRequest = {}; 0181 } 0182 Q_EMIT canQueryPrevNextChanged(); 0183 }); 0184 QObject::connect(reply, &KPublicTransport::StopoverReply::updated, this, [reply, this]() { 0185 Q_D(StopoverQueryModel); 0186 d->mergeResults(reply->takeResult()); 0187 }); 0188 } 0189 0190 int StopoverQueryModel::rowCount(const QModelIndex& parent) const 0191 { 0192 Q_D(const StopoverQueryModel); 0193 if (parent.isValid()) { 0194 return 0; 0195 } 0196 return d->m_departures.size(); 0197 } 0198 0199 QVariant StopoverQueryModel::data(const QModelIndex& index, int role) const 0200 { 0201 Q_D(const StopoverQueryModel); 0202 if (!index.isValid()) { 0203 return {}; 0204 } 0205 0206 switch (role) { 0207 case DepartureRole: 0208 return QVariant::fromValue(d->m_departures[index.row()]); 0209 } 0210 0211 return {}; 0212 } 0213 0214 QHash<int, QByteArray> StopoverQueryModel::roleNames() const 0215 { 0216 auto r = QAbstractListModel::roleNames(); 0217 r.insert(DepartureRole, "departure"); 0218 return r; 0219 } 0220 0221 const std::vector<Stopover>& StopoverQueryModel::departures() const 0222 { 0223 Q_D(const StopoverQueryModel); 0224 return d->m_departures; 0225 } 0226 0227 #include "moc_stopoverquerymodel.moc"