File indexing completed on 2026-07-12 13:30:35

0001 // SPDX-FileCopyrightText: 2020 Han Young <hanyoung@protonmail.com>
0002 // SPDX-FileCopyrightText: 2020-2022 Devin Lin <espidev@gmail.com>
0003 // SPDX-License-Identifier: GPL-2.0-or-later
0004 
0005 #include "weatherlocationlistmodel.h"
0006 
0007 #include <QJsonArray>
0008 #include <QQmlEngine>
0009 
0010 #include <KConfigGroup>
0011 #include <KSharedConfig>
0012 
0013 #include <KWeatherCore/LocationQueryReply>
0014 
0015 #include "global.h"
0016 #include "kweathersettings.h"
0017 #include "weatherlocation.h"
0018 
0019 WeatherLocationListModel::WeatherLocationListModel(QObject *parent)
0020     : QAbstractListModel{parent}
0021 {
0022     load();
0023 }
0024 
0025 WeatherLocationListModel *WeatherLocationListModel::inst()
0026 {
0027     static WeatherLocationListModel *singleton = new WeatherLocationListModel;
0028     return singleton;
0029 }
0030 
0031 void WeatherLocationListModel::load()
0032 {
0033     beginResetModel();
0034 
0035     // load locations from kconfig
0036     auto config = KWeatherSettings::self()->config()->group(Kweather::WEATHER_LOCATIONS_CFG_GROUP);
0037     auto locations = config.groupList();
0038     for (const auto &location : locations) {
0039         auto location_ptr = WeatherLocation::load(location);
0040         if (location_ptr)
0041             m_locations.push_back(location_ptr);
0042     }
0043 
0044     // sort locations by index, correcting any issues with the stored index
0045 
0046     QList<WeatherLocation *> sorted, unsorted;
0047     for (int i = 0; i < (int)m_locations.size(); ++i) {
0048         sorted.push_back(nullptr);
0049     }
0050 
0051     // loop through the initial locations and fill in the indicies in sorted
0052     for (auto loc : m_locations) {
0053         auto index = loc->index();
0054 
0055         if (index < 0 || index >= (int)sorted.size() || sorted[index] != nullptr) {
0056             unsorted.push_back(loc);
0057         } else {
0058             sorted[index] = loc;
0059         }
0060     }
0061     // add unsorted locations in positions unfilled
0062     for (auto loc : unsorted) {
0063         for (int i = 0; i < (int)sorted.size(); ++i) {
0064             if (!sorted[i]) {
0065                 sorted[i] = loc;
0066                 break;
0067             }
0068         }
0069     }
0070     // move into original array
0071     for (int i = 0; i < (int)m_locations.size(); ++i) {
0072         m_locations[i] = sorted[i];
0073     }
0074 
0075     Q_EMIT locationsChanged();
0076     endResetModel();
0077 }
0078 
0079 void WeatherLocationListModel::saveOrder()
0080 {
0081     auto i{0};
0082     for (auto loc : m_locations) {
0083         loc->saveOrder(i);
0084         i++;
0085     }
0086 }
0087 
0088 int WeatherLocationListModel::count() const
0089 {
0090     return m_locations.size();
0091 }
0092 
0093 int WeatherLocationListModel::rowCount(const QModelIndex &parent) const
0094 {
0095     if (parent.isValid()) {
0096         return 0;
0097     }
0098     return m_locations.size();
0099 }
0100 
0101 QVariant WeatherLocationListModel::data(const QModelIndex &index, int role) const
0102 {
0103     if (!checkIndex(index)) {
0104         return QVariant();
0105     }
0106     if (role != LocationRole) {
0107         return QVariant();
0108     }
0109 
0110     auto *location = m_locations[index.row()];
0111     return location ? QVariant::fromValue(location) : QVariant();
0112 }
0113 
0114 QHash<int, QByteArray> WeatherLocationListModel::roleNames() const
0115 {
0116     return {{LocationRole, "location"}};
0117 }
0118 
0119 void WeatherLocationListModel::insert(int index, WeatherLocation *weatherLocation)
0120 {
0121     if ((index < 0) || (index > static_cast<int>(m_locations.size()))) {
0122         return;
0123     }
0124 
0125     beginInsertRows(QModelIndex(), index, index);
0126 
0127     QQmlEngine::setObjectOwnership(weatherLocation, QQmlEngine::CppOwnership);
0128     m_locations.insert(m_locations.begin() + index, weatherLocation);
0129 
0130     Q_EMIT locationsChanged();
0131     endInsertRows();
0132 
0133     saveOrder();
0134     weatherLocation->save();
0135 }
0136 
0137 void WeatherLocationListModel::remove(int index)
0138 {
0139     if ((index < 0) || (index >= static_cast<int>(m_locations.size()))) {
0140         return;
0141     }
0142 
0143     beginRemoveRows(QModelIndex(), index, index);
0144 
0145     auto location = m_locations.at(index);
0146     m_locations.erase(m_locations.begin() + index);
0147     location->deleteConfig();
0148     location->deleteLater();
0149 
0150     Q_EMIT locationsChanged();
0151     endRemoveRows();
0152 
0153     saveOrder();
0154 }
0155 
0156 void WeatherLocationListModel::move(int oldIndex, int newIndex)
0157 {
0158     int locationsSize = m_locations.size();
0159     if (oldIndex < 0 || oldIndex >= locationsSize || newIndex < 0 || newIndex >= locationsSize) {
0160         return;
0161     }
0162     if (newIndex > oldIndex) {
0163         ++newIndex;
0164     }
0165 
0166     beginMoveRows(QModelIndex(), oldIndex, oldIndex, QModelIndex(), newIndex);
0167     if (newIndex > oldIndex) {
0168         auto *location = m_locations.at(oldIndex);
0169         m_locations.insert(newIndex, location);
0170         m_locations.takeAt(oldIndex);
0171     } else {
0172         auto *location = m_locations.takeAt(oldIndex);
0173         m_locations.insert(newIndex, location);
0174     }
0175     endMoveRows();
0176     Q_EMIT locationsChanged();
0177 
0178     saveOrder();
0179 }
0180 
0181 void WeatherLocationListModel::addLocation(const KWeatherCore::LocationQueryResult &ret)
0182 {
0183     const auto &locId = ret.geonameId();
0184     const auto &locName = ret.toponymName();
0185     auto lat = ret.latitude();
0186     auto lon = ret.longitude();
0187 
0188     // add location
0189     auto *location = new WeatherLocation(locId, locName, QString(), lat, lon);
0190 
0191     insert(m_locations.size(), location);
0192 }
0193 
0194 void WeatherLocationListModel::requestCurrentLocation()
0195 {
0196     static KWeatherCore::LocationQuery *geoPtr = nullptr;
0197     if (!geoPtr) {
0198         geoPtr = new KWeatherCore::LocationQuery(this);
0199     }
0200 
0201     auto reply = geoPtr->locate();
0202     connect(reply, &KWeatherCore::LocationQueryReply::finished, this, [reply, this]() {
0203         reply->deleteLater();
0204         if (reply->error() != KWeatherCore::LocationQueryReply::NoError) {
0205             Q_EMIT networkErrorCreatingDefault();
0206         } else {
0207             addCurrentLocation(reply->result().at(0));
0208         }
0209     });
0210 }
0211 
0212 void WeatherLocationListModel::addCurrentLocation(const KWeatherCore::LocationQueryResult &ret)
0213 {
0214     auto location = new WeatherLocation(ret.geonameId(), ret.toponymName(), QString(), ret.latitude(), ret.longitude());
0215 
0216     insert(0, location);
0217     Q_EMIT successfullyCreatedDefault();
0218 }
0219 
0220 QList<WeatherLocation *> &WeatherLocationListModel::locations()
0221 {
0222     return m_locations;
0223 }