2024-08-20 16:54:05 +07:00
|
|
|
|
#include "apiCountryModel.h"
|
|
|
|
|
|
|
|
|
|
|
|
#include <QJsonObject>
|
2026-03-13 13:06:32 +04:00
|
|
|
|
#include <QSettings>
|
|
|
|
|
|
#include <utility>
|
2024-08-20 16:54:05 +07:00
|
|
|
|
|
2025-02-19 22:58:04 +07:00
|
|
|
|
#include "core/api/apiDefs.h"
|
2024-08-20 16:54:05 +07:00
|
|
|
|
#include "logger.h"
|
|
|
|
|
|
|
|
|
|
|
|
namespace
|
|
|
|
|
|
{
|
|
|
|
|
|
Logger logger("ApiCountryModel");
|
2025-02-22 14:42:09 +07:00
|
|
|
|
constexpr QLatin1String countryConfig("country_config");
|
2026-03-13 13:06:32 +04:00
|
|
|
|
constexpr QLatin1String regionEurope("Europe");
|
|
|
|
|
|
constexpr QLatin1String regionAmerica("America");
|
|
|
|
|
|
constexpr QLatin1String regionAsia("Asia");
|
|
|
|
|
|
constexpr QLatin1String regionOceaniaAfrica("Oceania and Africa");
|
|
|
|
|
|
constexpr QLatin1String regionOther("Other");
|
|
|
|
|
|
|
|
|
|
|
|
struct RegionRowData
|
|
|
|
|
|
{
|
|
|
|
|
|
bool isRegionHeader = false;
|
|
|
|
|
|
QString regionName;
|
|
|
|
|
|
bool isExpanded = true;
|
|
|
|
|
|
int sourceIndex = -1;
|
|
|
|
|
|
QString countryName;
|
|
|
|
|
|
QString sourceCountryName;
|
|
|
|
|
|
QString countryCode;
|
|
|
|
|
|
QString countryImageCode;
|
|
|
|
|
|
};
|
|
|
|
|
|
|
|
|
|
|
|
QString resolveRegionByIsoCode(const QString &isoCode)
|
|
|
|
|
|
{
|
|
|
|
|
|
static const QHash<QString, QString> isoToRegion = {
|
|
|
|
|
|
{"BE", regionEurope},
|
|
|
|
|
|
{"EE", regionEurope},
|
|
|
|
|
|
{"FI", regionEurope},
|
|
|
|
|
|
{"FR", regionEurope},
|
|
|
|
|
|
{"GE", regionEurope},
|
|
|
|
|
|
{"DE", regionEurope},
|
|
|
|
|
|
{"NL", regionEurope},
|
|
|
|
|
|
{"PL", regionEurope},
|
|
|
|
|
|
{"RU", regionEurope},
|
|
|
|
|
|
{"ES", regionEurope},
|
|
|
|
|
|
{"SE", regionEurope},
|
|
|
|
|
|
{"CH", regionEurope},
|
|
|
|
|
|
{"TR", regionEurope},
|
|
|
|
|
|
{"BR", regionAmerica},
|
|
|
|
|
|
{"CA", regionAmerica},
|
|
|
|
|
|
{"US", regionAmerica},
|
|
|
|
|
|
{"AE", regionAsia},
|
|
|
|
|
|
{"JP", regionAsia},
|
|
|
|
|
|
{"KZ", regionAsia},
|
|
|
|
|
|
{"KR", regionAsia},
|
|
|
|
|
|
{"SG", regionAsia},
|
|
|
|
|
|
{"AU", regionOceaniaAfrica},
|
|
|
|
|
|
{"NZ", regionOceaniaAfrica},
|
|
|
|
|
|
{"ZA", regionOceaniaAfrica},
|
|
|
|
|
|
};
|
|
|
|
|
|
|
|
|
|
|
|
return isoToRegion.value(isoCode, regionOther);
|
|
|
|
|
|
}
|
2024-08-20 16:54:05 +07:00
|
|
|
|
}
|
|
|
|
|
|
|
2026-03-13 13:06:32 +04:00
|
|
|
|
class ApiCountryModel::RegionRowsModel : public QAbstractListModel
|
2024-08-20 16:54:05 +07:00
|
|
|
|
{
|
2026-03-13 13:06:32 +04:00
|
|
|
|
public:
|
|
|
|
|
|
enum Roles {
|
|
|
|
|
|
RowTypeRole = Qt::UserRole + 1,
|
|
|
|
|
|
RegionNameRole,
|
|
|
|
|
|
IsExpandedRole,
|
|
|
|
|
|
SourceIndexRole,
|
|
|
|
|
|
CountryNameRole,
|
|
|
|
|
|
SourceCountryNameRole,
|
|
|
|
|
|
CountryCodeRole,
|
|
|
|
|
|
CountryImageCodeRole
|
|
|
|
|
|
};
|
|
|
|
|
|
|
|
|
|
|
|
explicit RegionRowsModel(QObject *parent = nullptr) : QAbstractListModel(parent) {}
|
|
|
|
|
|
|
|
|
|
|
|
int rowCount(const QModelIndex &parent = QModelIndex()) const override
|
|
|
|
|
|
{
|
|
|
|
|
|
Q_UNUSED(parent)
|
|
|
|
|
|
return m_rows.size();
|
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
QVariant data(const QModelIndex &index, int role = Qt::DisplayRole) const override
|
|
|
|
|
|
{
|
|
|
|
|
|
if (!index.isValid() || index.row() < 0 || index.row() >= m_rows.size()) {
|
|
|
|
|
|
return QVariant();
|
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
const RegionRowData &row = m_rows.at(index.row());
|
|
|
|
|
|
switch (role) {
|
|
|
|
|
|
case RowTypeRole:
|
|
|
|
|
|
return row.isRegionHeader ? "region" : "country";
|
|
|
|
|
|
case RegionNameRole:
|
|
|
|
|
|
return row.regionName;
|
|
|
|
|
|
case IsExpandedRole:
|
|
|
|
|
|
return row.isExpanded;
|
|
|
|
|
|
case SourceIndexRole:
|
|
|
|
|
|
return row.sourceIndex;
|
|
|
|
|
|
case CountryNameRole:
|
|
|
|
|
|
return row.countryName;
|
|
|
|
|
|
case SourceCountryNameRole:
|
|
|
|
|
|
return row.sourceCountryName;
|
|
|
|
|
|
case CountryCodeRole:
|
|
|
|
|
|
return row.countryCode;
|
|
|
|
|
|
case CountryImageCodeRole:
|
|
|
|
|
|
return row.countryImageCode;
|
|
|
|
|
|
default:
|
|
|
|
|
|
return QVariant();
|
|
|
|
|
|
}
|
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
QHash<int, QByteArray> roleNames() const override
|
|
|
|
|
|
{
|
|
|
|
|
|
QHash<int, QByteArray> roles;
|
|
|
|
|
|
roles[RowTypeRole] = "rowType";
|
|
|
|
|
|
roles[RegionNameRole] = "regionName";
|
|
|
|
|
|
roles[IsExpandedRole] = "isExpanded";
|
|
|
|
|
|
roles[SourceIndexRole] = "sourceIndex";
|
|
|
|
|
|
roles[CountryNameRole] = "countryName";
|
|
|
|
|
|
roles[SourceCountryNameRole] = "sourceCountryName";
|
|
|
|
|
|
roles[CountryCodeRole] = "countryCode";
|
|
|
|
|
|
roles[CountryImageCodeRole] = "countryImageCode";
|
|
|
|
|
|
return roles;
|
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
void setRows(QVector<RegionRowData> &&rows)
|
|
|
|
|
|
{
|
|
|
|
|
|
beginResetModel();
|
|
|
|
|
|
m_rows = std::move(rows);
|
|
|
|
|
|
endResetModel();
|
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
private:
|
|
|
|
|
|
QVector<RegionRowData> m_rows;
|
|
|
|
|
|
};
|
|
|
|
|
|
|
|
|
|
|
|
ApiCountryModel::ApiCountryModel(QObject *parent)
|
|
|
|
|
|
: QAbstractListModel(parent), m_regionRowsModel(std::make_unique<RegionRowsModel>(this))
|
|
|
|
|
|
{
|
|
|
|
|
|
loadRegionExpansionState();
|
|
|
|
|
|
rebuildGroupedRegions();
|
2024-08-20 16:54:05 +07:00
|
|
|
|
}
|
|
|
|
|
|
|
2026-03-13 13:06:32 +04:00
|
|
|
|
ApiCountryModel::~ApiCountryModel() = default;
|
|
|
|
|
|
|
2024-08-20 16:54:05 +07:00
|
|
|
|
int ApiCountryModel::rowCount(const QModelIndex &parent) const
|
|
|
|
|
|
{
|
|
|
|
|
|
Q_UNUSED(parent)
|
|
|
|
|
|
return m_countries.size();
|
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
QVariant ApiCountryModel::data(const QModelIndex &index, int role) const
|
|
|
|
|
|
{
|
2026-03-13 13:06:32 +04:00
|
|
|
|
if (!index.isValid() || index.row() < 0 || index.row() >= static_cast<int>(rowCount())) {
|
2024-08-20 16:54:05 +07:00
|
|
|
|
return QVariant();
|
2026-03-13 13:06:32 +04:00
|
|
|
|
}
|
2024-08-20 16:54:05 +07:00
|
|
|
|
|
2026-03-13 13:06:32 +04:00
|
|
|
|
const CountryInfo &countryInfo = m_countries.at(index.row());
|
|
|
|
|
|
const IssuedConfigInfo issuedConfigInfo = m_issuedConfigs.value(countryInfo.countryCode);
|
|
|
|
|
|
const bool isIssued = issuedConfigInfo.sourceType == countryConfig;
|
2024-08-20 16:54:05 +07:00
|
|
|
|
|
|
|
|
|
|
switch (role) {
|
2026-03-13 13:06:32 +04:00
|
|
|
|
case CountryCodeRole:
|
2025-02-19 22:58:04 +07:00
|
|
|
|
return countryInfo.countryCode;
|
2026-03-13 13:06:32 +04:00
|
|
|
|
case CountryNameRole:
|
2025-02-19 22:58:04 +07:00
|
|
|
|
return countryInfo.countryName;
|
2026-03-13 13:06:32 +04:00
|
|
|
|
case CountryImageCodeRole:
|
2025-02-19 22:58:04 +07:00
|
|
|
|
return countryInfo.countryCode.toUpper();
|
2026-03-13 13:06:32 +04:00
|
|
|
|
case IsIssuedRole:
|
2025-02-20 13:44:19 +07:00
|
|
|
|
return isIssued;
|
2026-03-13 13:06:32 +04:00
|
|
|
|
case IsWorkerExpiredRole:
|
2025-02-28 18:17:43 +03:00
|
|
|
|
return issuedConfigInfo.lastDownloaded < issuedConfigInfo.workerLastUpdated;
|
2026-03-13 13:06:32 +04:00
|
|
|
|
default:
|
|
|
|
|
|
return QVariant();
|
2025-02-28 18:17:43 +03:00
|
|
|
|
}
|
2024-08-20 16:54:05 +07:00
|
|
|
|
}
|
|
|
|
|
|
|
2025-02-15 15:29:53 +07:00
|
|
|
|
void ApiCountryModel::updateModel(const QJsonArray &countries, const QString ¤tCountryCode)
|
2024-08-20 16:54:05 +07:00
|
|
|
|
{
|
|
|
|
|
|
beginResetModel();
|
|
|
|
|
|
|
2025-02-19 22:58:04 +07:00
|
|
|
|
m_countries.clear();
|
2026-03-13 13:06:32 +04:00
|
|
|
|
for (int i = 0; i < countries.size(); ++i) {
|
2025-02-19 22:58:04 +07:00
|
|
|
|
CountryInfo countryInfo;
|
2026-03-13 13:06:32 +04:00
|
|
|
|
const QJsonObject countryObject = countries.at(i).toObject();
|
2025-02-19 22:58:04 +07:00
|
|
|
|
|
|
|
|
|
|
countryInfo.countryName = countryObject.value(apiDefs::key::serverCountryName).toString();
|
|
|
|
|
|
countryInfo.countryCode = countryObject.value(apiDefs::key::serverCountryCode).toString();
|
|
|
|
|
|
|
|
|
|
|
|
if (countryInfo.countryCode == currentCountryCode) {
|
2024-08-20 16:54:05 +07:00
|
|
|
|
m_currentIndex = i;
|
|
|
|
|
|
emit currentIndexChanged(m_currentIndex);
|
|
|
|
|
|
}
|
2025-02-19 22:58:04 +07:00
|
|
|
|
m_countries.push_back(countryInfo);
|
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
endResetModel();
|
2026-03-13 13:06:32 +04:00
|
|
|
|
rebuildGroupedRegions();
|
2025-02-19 22:58:04 +07:00
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
void ApiCountryModel::updateIssuedConfigsInfo(const QJsonArray &issuedConfigs)
|
|
|
|
|
|
{
|
|
|
|
|
|
beginResetModel();
|
|
|
|
|
|
|
2025-02-22 14:42:09 +07:00
|
|
|
|
m_issuedConfigs.clear();
|
2026-03-13 13:06:32 +04:00
|
|
|
|
for (int i = 0; i < issuedConfigs.size(); ++i) {
|
2025-02-19 22:58:04 +07:00
|
|
|
|
IssuedConfigInfo issuedConfigInfo;
|
2026-03-13 13:06:32 +04:00
|
|
|
|
const QJsonObject issuedConfigObject = issuedConfigs.at(i).toObject();
|
2025-02-19 22:58:04 +07:00
|
|
|
|
|
2025-02-22 14:42:09 +07:00
|
|
|
|
if (issuedConfigObject.value(apiDefs::key::sourceType).toString() != countryConfig) {
|
|
|
|
|
|
continue;
|
|
|
|
|
|
}
|
|
|
|
|
|
|
2025-02-19 22:58:04 +07:00
|
|
|
|
issuedConfigInfo.installationUuid = issuedConfigObject.value(apiDefs::key::installationUuid).toString();
|
|
|
|
|
|
issuedConfigInfo.workerLastUpdated = issuedConfigObject.value(apiDefs::key::workerLastUpdated).toString();
|
|
|
|
|
|
issuedConfigInfo.lastDownloaded = issuedConfigObject.value(apiDefs::key::lastDownloaded).toString();
|
|
|
|
|
|
issuedConfigInfo.sourceType = issuedConfigObject.value(apiDefs::key::sourceType).toString();
|
|
|
|
|
|
issuedConfigInfo.osVersion = issuedConfigObject.value(apiDefs::key::osVersion).toString();
|
|
|
|
|
|
|
|
|
|
|
|
m_issuedConfigs.insert(issuedConfigObject.value(apiDefs::key::serverCountryCode).toString(), issuedConfigInfo);
|
2024-08-20 16:54:05 +07:00
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
endResetModel();
|
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
int ApiCountryModel::getCurrentIndex()
|
|
|
|
|
|
{
|
|
|
|
|
|
return m_currentIndex;
|
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
void ApiCountryModel::setCurrentIndex(const int i)
|
|
|
|
|
|
{
|
|
|
|
|
|
m_currentIndex = i;
|
|
|
|
|
|
emit currentIndexChanged(m_currentIndex);
|
|
|
|
|
|
}
|
|
|
|
|
|
|
2026-03-13 13:06:32 +04:00
|
|
|
|
QString ApiCountryModel::searchText() const
|
|
|
|
|
|
{
|
|
|
|
|
|
return m_searchText;
|
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
void ApiCountryModel::setSearchText(const QString &text)
|
|
|
|
|
|
{
|
|
|
|
|
|
if (m_searchText == text) {
|
|
|
|
|
|
return;
|
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
m_searchText = text;
|
|
|
|
|
|
emit searchTextChanged();
|
|
|
|
|
|
rebuildGroupedRegions();
|
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
QAbstractListModel *ApiCountryModel::regionRowsModel() const
|
|
|
|
|
|
{
|
|
|
|
|
|
return m_regionRowsModel.get();
|
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
bool ApiCountryModel::hasVisibleRegions() const
|
|
|
|
|
|
{
|
|
|
|
|
|
return m_regionRowsModel && m_regionRowsModel->rowCount() > 0;
|
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
bool ApiCountryModel::isRegionExpanded(const QString ®ionName) const
|
|
|
|
|
|
{
|
|
|
|
|
|
if (isSearchActive()) {
|
|
|
|
|
|
return true;
|
|
|
|
|
|
}
|
|
|
|
|
|
return m_regionsExpanded.contains(regionName) ? m_regionsExpanded.value(regionName) : true;
|
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
void ApiCountryModel::toggleRegionExpanded(const QString ®ionName)
|
|
|
|
|
|
{
|
|
|
|
|
|
if (regionName.isEmpty() || isSearchActive()) {
|
|
|
|
|
|
return;
|
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
const bool currentValue = isRegionExpanded(regionName);
|
|
|
|
|
|
m_regionsExpanded.insert(regionName, !currentValue);
|
|
|
|
|
|
saveRegionExpansionState();
|
|
|
|
|
|
rebuildGroupedRegions();
|
|
|
|
|
|
}
|
|
|
|
|
|
|
2024-08-20 16:54:05 +07:00
|
|
|
|
QHash<int, QByteArray> ApiCountryModel::roleNames() const
|
|
|
|
|
|
{
|
|
|
|
|
|
QHash<int, QByteArray> roles;
|
|
|
|
|
|
roles[CountryNameRole] = "countryName";
|
|
|
|
|
|
roles[CountryCodeRole] = "countryCode";
|
2024-10-24 19:12:53 +04:00
|
|
|
|
roles[CountryImageCodeRole] = "countryImageCode";
|
2025-02-20 13:44:19 +07:00
|
|
|
|
roles[IsIssuedRole] = "isIssued";
|
2025-02-28 18:17:43 +03:00
|
|
|
|
roles[IsWorkerExpiredRole] = "isWorkerExpired";
|
2024-08-20 16:54:05 +07:00
|
|
|
|
return roles;
|
|
|
|
|
|
}
|
2026-03-13 13:06:32 +04:00
|
|
|
|
|
|
|
|
|
|
QString ApiCountryModel::normalizeCountryCode(const QString &countryCode) const
|
|
|
|
|
|
{
|
|
|
|
|
|
return countryCode.trimmed().toUpper();
|
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
QString ApiCountryModel::extractCountryIsoCode(const QString &countryCode) const
|
|
|
|
|
|
{
|
|
|
|
|
|
const QString normalizedCode = normalizeCountryCode(countryCode);
|
|
|
|
|
|
for (int i = 0; i + 1 < normalizedCode.size(); ++i) {
|
|
|
|
|
|
const QChar first = normalizedCode.at(i);
|
|
|
|
|
|
const QChar second = normalizedCode.at(i + 1);
|
|
|
|
|
|
if (first.isUpper() && second.isUpper()) {
|
|
|
|
|
|
return normalizedCode.mid(i, 2);
|
|
|
|
|
|
}
|
|
|
|
|
|
}
|
|
|
|
|
|
return normalizedCode;
|
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
QString ApiCountryModel::normalizeCountryName(const QString &countryName) const
|
|
|
|
|
|
{
|
|
|
|
|
|
return countryName.trimmed().toLower();
|
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
QString ApiCountryModel::normalizeSearchComparableText(const QString &textValue) const
|
|
|
|
|
|
{
|
|
|
|
|
|
QString normalizedText = normalizeCountryName(textValue);
|
|
|
|
|
|
normalizedText.replace(QChar(0x0451), QChar(0x0435)); // ё -> е
|
|
|
|
|
|
normalizedText.replace(QChar(0x0439), QChar(0x0438)); // й -> и
|
|
|
|
|
|
|
|
|
|
|
|
QString result;
|
|
|
|
|
|
result.reserve(normalizedText.size());
|
|
|
|
|
|
for (int i = 0; i < normalizedText.size(); ++i) {
|
|
|
|
|
|
const QChar currentChar = normalizedText.at(i);
|
|
|
|
|
|
if (currentChar.isSpace()) {
|
|
|
|
|
|
const QChar prevChar = i > 0 ? normalizedText.at(i - 1) : QChar();
|
|
|
|
|
|
const QChar nextChar = i + 1 < normalizedText.size() ? normalizedText.at(i + 1) : QChar();
|
|
|
|
|
|
const bool hasSpaceNeighbor = prevChar.isSpace() || nextChar.isSpace();
|
|
|
|
|
|
if (hasSpaceNeighbor) {
|
|
|
|
|
|
result.append(currentChar);
|
|
|
|
|
|
}
|
|
|
|
|
|
continue;
|
|
|
|
|
|
}
|
|
|
|
|
|
const bool isSeparator = currentChar == '.' || currentChar == '-';
|
|
|
|
|
|
|
|
|
|
|
|
if (!isSeparator) {
|
|
|
|
|
|
result.append(currentChar);
|
|
|
|
|
|
continue;
|
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
const QChar prevChar = i > 0 ? normalizedText.at(i - 1) : QChar();
|
|
|
|
|
|
const QChar nextChar = i + 1 < normalizedText.size() ? normalizedText.at(i + 1) : QChar();
|
|
|
|
|
|
const bool hasSeparatorNeighbor = prevChar == '.' || prevChar == '-' || nextChar == '.' || nextChar == '-';
|
|
|
|
|
|
if (hasSeparatorNeighbor) {
|
|
|
|
|
|
result.append(currentChar);
|
|
|
|
|
|
}
|
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
return result;
|
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
bool ApiCountryModel::isCountryMatchingSearch(const QString &countryName, const QString &sourceCountryCode,
|
|
|
|
|
|
const QString &normalizedSearchText) const
|
|
|
|
|
|
{
|
|
|
|
|
|
if (normalizedSearchText.isEmpty()) {
|
|
|
|
|
|
return true;
|
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
const QString normalizedCountryName = normalizeSearchComparableText(countryName);
|
|
|
|
|
|
const QString normalizedSourceCountryCode = normalizeCountryCode(sourceCountryCode).toLower();
|
|
|
|
|
|
|
|
|
|
|
|
return normalizedCountryName.startsWith(normalizedSearchText)
|
|
|
|
|
|
|| normalizedSourceCountryCode.startsWith(normalizedSearchText);
|
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
QString ApiCountryModel::getDisplayCountryName(const QString &countryName) const
|
|
|
|
|
|
{
|
|
|
|
|
|
const QString p2pPrefix = "[P2P] ";
|
|
|
|
|
|
if (countryName.startsWith(p2pPrefix)) {
|
|
|
|
|
|
return countryName.mid(p2pPrefix.size()) + " [P2P]";
|
|
|
|
|
|
}
|
|
|
|
|
|
return countryName;
|
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
void ApiCountryModel::rebuildGroupedRegions()
|
|
|
|
|
|
{
|
|
|
|
|
|
QVector<RegionRowData> rows;
|
|
|
|
|
|
const QString normalizedSearchText = normalizeSearchComparableText(m_searchText);
|
|
|
|
|
|
const QStringList orderedRegions = {
|
|
|
|
|
|
regionEurope,
|
|
|
|
|
|
regionAmerica,
|
|
|
|
|
|
regionAsia,
|
|
|
|
|
|
regionOceaniaAfrica,
|
|
|
|
|
|
regionOther,
|
|
|
|
|
|
};
|
|
|
|
|
|
|
|
|
|
|
|
QHash<QString, QVector<RegionRowData>> groupedCountries;
|
|
|
|
|
|
for (int sourceIndex = 0; sourceIndex < m_countries.size(); ++sourceIndex) {
|
|
|
|
|
|
const CountryInfo &sourceCountry = m_countries.at(sourceIndex);
|
|
|
|
|
|
if (!isCountryMatchingSearch(sourceCountry.countryName, sourceCountry.countryCode, normalizedSearchText)) {
|
|
|
|
|
|
continue;
|
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
const QString regionName = resolveRegionByIsoCode(extractCountryIsoCode(sourceCountry.countryCode));
|
|
|
|
|
|
RegionRowData countryRow;
|
|
|
|
|
|
countryRow.isRegionHeader = false;
|
|
|
|
|
|
countryRow.regionName = regionName;
|
|
|
|
|
|
countryRow.sourceIndex = sourceIndex;
|
|
|
|
|
|
countryRow.countryName = getDisplayCountryName(sourceCountry.countryName);
|
|
|
|
|
|
countryRow.sourceCountryName = sourceCountry.countryName;
|
|
|
|
|
|
countryRow.countryCode = sourceCountry.countryCode;
|
|
|
|
|
|
countryRow.countryImageCode = extractCountryIsoCode(sourceCountry.countryCode);
|
|
|
|
|
|
groupedCountries[regionName].push_back(std::move(countryRow));
|
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
for (const QString ®ionName : orderedRegions) {
|
|
|
|
|
|
QVector<RegionRowData> countries = groupedCountries.value(regionName);
|
|
|
|
|
|
if (countries.isEmpty()) {
|
|
|
|
|
|
continue;
|
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
const bool expanded = isRegionExpanded(regionName);
|
|
|
|
|
|
|
|
|
|
|
|
RegionRowData headerRow;
|
|
|
|
|
|
headerRow.isRegionHeader = true;
|
|
|
|
|
|
headerRow.regionName = regionName;
|
|
|
|
|
|
headerRow.isExpanded = expanded;
|
|
|
|
|
|
rows.push_back(std::move(headerRow));
|
|
|
|
|
|
|
|
|
|
|
|
if (expanded) {
|
|
|
|
|
|
for (RegionRowData &countryRow : countries) {
|
|
|
|
|
|
countryRow.isExpanded = expanded;
|
|
|
|
|
|
rows.push_back(std::move(countryRow));
|
|
|
|
|
|
}
|
|
|
|
|
|
}
|
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
m_regionRowsModel->setRows(std::move(rows));
|
|
|
|
|
|
emit regionRowsChanged();
|
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
void ApiCountryModel::loadRegionExpansionState()
|
|
|
|
|
|
{
|
|
|
|
|
|
QSettings settings;
|
|
|
|
|
|
const QVariantMap stored = settings.value("PageSettingsApiAvailableCountries/regionsExpanded").toMap();
|
|
|
|
|
|
m_regionsExpanded.clear();
|
|
|
|
|
|
for (auto it = stored.constBegin(); it != stored.constEnd(); ++it) {
|
|
|
|
|
|
m_regionsExpanded.insert(it.key(), it.value().toBool());
|
|
|
|
|
|
}
|
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
void ApiCountryModel::saveRegionExpansionState() const
|
|
|
|
|
|
{
|
|
|
|
|
|
QVariantMap stored;
|
|
|
|
|
|
for (auto it = m_regionsExpanded.constBegin(); it != m_regionsExpanded.constEnd(); ++it) {
|
|
|
|
|
|
stored.insert(it.key(), it.value());
|
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
QSettings settings;
|
|
|
|
|
|
settings.setValue("PageSettingsApiAvailableCountries/regionsExpanded", stored);
|
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
bool ApiCountryModel::isSearchActive() const
|
|
|
|
|
|
{
|
|
|
|
|
|
return !normalizeSearchComparableText(m_searchText).isEmpty();
|
|
|
|
|
|
}
|