File indexing completed on 2025-02-02 05:02:29
0001 /* 0002 SPDX-FileCopyrightText: 2023 Volker Krause <vkrause@kde.org> 0003 SPDX-License-Identifier: LGPL-2.0-or-later 0004 */ 0005 0006 #include "journeysectionmodel.h" 0007 #include "logging.h" 0008 0009 #include <KPublicTransport/Stopover> 0010 0011 #include <QDebug> 0012 0013 JourneySectionModel::JourneySectionModel(QObject *parent) 0014 : QAbstractListModel(parent) 0015 { 0016 connect(this, &JourneySectionModel::departureTrailingSegmentLengthChanged, this, [this]() { 0017 if (!m_data.empty()) { 0018 Q_EMIT dataChanged(index(0, 0), index(0, 0)); 0019 } 0020 }); 0021 connect(this, &JourneySectionModel::arrivalLeadingSegmentLengthChanged, this, [this]() { 0022 if (!m_data.empty()) { 0023 Q_EMIT dataChanged(index(rowCount() - 1, 0), index(rowCount() - 1, 0)); 0024 } 0025 }); 0026 0027 connect(this, &JourneySectionModel::showProgressChanged, this, [this]() { 0028 if (!m_data.empty()) { 0029 Q_EMIT dataChanged(index(0, 0), index(rowCount() - 1, 0)); 0030 } 0031 Q_EMIT journeySectionChanged(); 0032 }); 0033 } 0034 0035 JourneySectionModel::~JourneySectionModel() = default; 0036 0037 KPublicTransport::JourneySection JourneySectionModel::journeySection() const 0038 { 0039 return m_journey; 0040 } 0041 0042 void JourneySectionModel::setJourneySection(const KPublicTransport::JourneySection& section) 0043 { 0044 // is this an update to the current state? if so, try to avoid resetting the model 0045 if (KPublicTransport::JourneySection::isSame(m_journey, section) && m_journey.intermediateStops().size() == section.intermediateStops().size()) { 0046 m_journey = section; 0047 Q_EMIT dataChanged(index(0, 0), index(rowCount() - 1, 0)); 0048 Q_EMIT journeySectionChanged(); 0049 return; 0050 } 0051 0052 beginResetModel(); 0053 m_journey = section; 0054 m_data.clear(); 0055 m_data.resize(m_journey.intermediateStops().size()); 0056 endResetModel(); 0057 Q_EMIT journeySectionChanged(); 0058 } 0059 0060 int JourneySectionModel::rowCount(const QModelIndex &parent) const 0061 { 0062 if (parent.isValid()) { 0063 return 0; 0064 } 0065 return m_journey.intermediateStops().size(); 0066 } 0067 0068 0069 QVariant JourneySectionModel::data(const QModelIndex &index, int role) const 0070 { 0071 if (!checkIndex(index)) { 0072 return {}; 0073 } 0074 0075 switch (role) { 0076 case LeadingSegmentLengthRole: 0077 return m_data[index.row()].leadingLength; 0078 case TrailingSegmentLengthtRole: 0079 return m_data[index.row()].trailingLength; 0080 case LeadingSegmentProgressRole: 0081 return leadingProgress(index.row()); 0082 case TrailingSegmentProgressRole: 0083 return trailingProgress(index.row()); 0084 case StopoverRole: 0085 { 0086 auto stop = m_journey.intermediateStops()[index.row()]; 0087 if (stop.route().line().mode() == KPublicTransport::Line::Unknown) { 0088 stop.setRoute(m_journey.route()); 0089 } 0090 return stop; 0091 } 0092 case StopoverPassedRole: 0093 return stopoverPassed(index.row()); 0094 } 0095 0096 return {}; 0097 } 0098 0099 bool JourneySectionModel::setData(const QModelIndex &index, const QVariant &value, int role) 0100 { 0101 const auto length = value.toFloat(); 0102 if (length <= 0.0f) { 0103 return false; 0104 } 0105 0106 qCDebug(Log) << index << value << role; 0107 switch (role) { 0108 case LeadingSegmentLengthRole: 0109 m_data[index.row()].leadingLength = length; 0110 break; 0111 case TrailingSegmentLengthtRole: 0112 m_data[index.row()].trailingLength = length; 0113 break; 0114 default: 0115 return false; 0116 } 0117 Q_EMIT dataChanged(index, index); 0118 if (index.row() > 0) { 0119 Q_EMIT dataChanged(index.sibling(index.row() - 1, 0), index.sibling(index.row() - 1, 0)); 0120 } 0121 if (index.row() < rowCount() - 1) { 0122 Q_EMIT dataChanged(index.sibling(index.row() + 1, 0), index.sibling(index.row() + 1, 0)); 0123 } 0124 return true; 0125 } 0126 0127 QHash<int, QByteArray> JourneySectionModel::roleNames() const 0128 { 0129 auto n = QAbstractListModel::roleNames(); 0130 n.insert(LeadingSegmentLengthRole, "leadingLength"); 0131 n.insert(TrailingSegmentLengthtRole, "trailingLength"); 0132 n.insert(LeadingSegmentProgressRole, "leadingProgress"); 0133 n.insert(TrailingSegmentProgressRole, "trailingProgress"); 0134 n.insert(StopoverRole, "stopover"); 0135 n.insert(StopoverPassedRole, "stopoverPassed"); 0136 return n; 0137 } 0138 0139 float JourneySectionModel::departureTrailingProgress() const 0140 { 0141 return trailingProgress(-1); 0142 } 0143 0144 float JourneySectionModel::arrivalLeadingProgress() const 0145 { 0146 return leadingProgress(rowCount()); 0147 } 0148 0149 bool JourneySectionModel::departed() const 0150 { 0151 return stopoverPassed(-1); 0152 } 0153 0154 bool JourneySectionModel::arrived() const 0155 { 0156 return stopoverPassed(rowCount()); 0157 } 0158 0159 KPublicTransport::Stopover JourneySectionModel::stopoverForRow(int row) const 0160 { 0161 if (row < 0) { 0162 return m_journey.departure(); 0163 } 0164 if (row >= 0 && row < rowCount()) { 0165 return m_journey.intermediateStops()[row]; 0166 } 0167 return m_journey.arrival(); 0168 } 0169 0170 static QDateTime departureTime(const KPublicTransport::Stopover &stop) 0171 { 0172 return stop.hasExpectedDepartureTime() ? stop.expectedDepartureTime() : stop.scheduledDepartureTime(); 0173 } 0174 0175 static QDateTime arrivalTime(const KPublicTransport::Stopover &stop) 0176 { 0177 if (stop.hasExpectedArrivalTime()) { 0178 return stop.expectedArrivalTime(); 0179 } 0180 if (stop.scheduledArrivalTime().isValid()) { 0181 return stop.scheduledArrivalTime(); 0182 } 0183 return departureTime(stop); 0184 } 0185 0186 float JourneySectionModel::leadingProgress(int row) const 0187 { 0188 if (!m_showProgress) { 0189 return 0.0f; 0190 } 0191 0192 const auto now = currentDateTime(); 0193 const auto stop = stopoverForRow(row); 0194 if (arrivalTime(stop) <= now) { 0195 qCDebug(Log) << row << stop.stopPoint().name() << "already passed" << arrivalTime(stop); 0196 return 1.0f; 0197 } 0198 0199 const auto prevStop = stopoverForRow(row - 1); 0200 if (departureTime(prevStop) >= now) { 0201 qCDebug(Log) << row << stop.stopPoint().name() << "not passed yet"; 0202 return 0.0f; 0203 } 0204 0205 const float totalTime = departureTime(prevStop).secsTo(arrivalTime(stop)); 0206 const float progressTime = departureTime(prevStop).secsTo(now); 0207 0208 const float prevLength = row > 0 ? m_data[row - 1].trailingLength : m_departureTrailingLength; 0209 const float leadingLength = row >= rowCount() ? m_arrivalLeadingLength : m_data[row].leadingLength; 0210 const float totalLength = leadingLength + prevLength; 0211 0212 const float progressLength = totalLength * (progressTime / totalTime); 0213 qCDebug(Log) << row << stop.stopPoint().name() << totalTime << progressTime << totalLength << progressLength << prevLength << (progressLength < prevLength ? 0.0f : ((progressLength - prevLength) / leadingLength)); 0214 return progressLength < prevLength ? 0.0f : ((progressLength - prevLength) / leadingLength); 0215 } 0216 0217 float JourneySectionModel::trailingProgress(int row) const 0218 { 0219 if (!m_showProgress) { 0220 return 0.0f; 0221 } 0222 0223 const auto now = currentDateTime(); 0224 const auto stop = stopoverForRow(row); 0225 if (departureTime(stop) >= now) { 0226 qCDebug(Log) << row << stop.stopPoint().name()<< "not passed yet"; 0227 return 0.0f; 0228 } 0229 0230 const auto nextStop = stopoverForRow(row + 1); 0231 if (arrivalTime(nextStop) <= now) { 0232 qCDebug(Log) << row << stop.stopPoint().name()<< "already passed"; 0233 return 1.0f; 0234 } 0235 0236 const float totalTime = departureTime(stop).secsTo(arrivalTime(nextStop)); 0237 const float progressTime = departureTime(stop).secsTo(now); 0238 0239 const float nextLength = row < rowCount() - 1 ? m_data[row + 1].leadingLength : m_arrivalLeadingLength; 0240 const float trailingLength = row < 0 ? m_departureTrailingLength : m_data[row].trailingLength; 0241 const float totalLength = trailingLength + nextLength; 0242 0243 const float progressLength = totalLength * (progressTime / totalTime); 0244 qCDebug(Log) << row << stop.stopPoint().name()<< totalTime << progressTime << totalLength << progressLength << nextLength << (progressLength > trailingLength ? 1.0f : (progressLength / trailingLength)); 0245 return progressLength > trailingLength ? 1.0f : (progressLength / trailingLength); 0246 } 0247 0248 float JourneySectionModel::stopoverPassed(int row) const 0249 { 0250 if (!m_showProgress) { 0251 return false; 0252 } 0253 0254 const auto now = currentDateTime(); 0255 const auto stop = stopoverForRow(row); 0256 return arrivalTime(stop) <= now; 0257 } 0258 0259 void JourneySectionModel::setCurrentDateTime(const QDateTime& dt) 0260 { 0261 m_unitTestTime = dt; 0262 } 0263 0264 QDateTime JourneySectionModel::currentDateTime() const 0265 { 0266 if (Q_UNLIKELY(m_unitTestTime.isValid())) { 0267 return m_unitTestTime; 0268 } 0269 return QDateTime::currentDateTime(); 0270 } 0271 0272 #include "moc_journeysectionmodel.cpp"