File indexing completed on 2024-05-19 05:55:49

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