/*
* Copyright (C) 2015 Canonical Ltd.
*
* This program is free software: you can redistribute it and/or modify it
* under the terms of the GNU General Public License version 3, as published
* by the Free Software Foundation.
*
* This program is distributed in the hope that it will be useful, but
* WITHOUT ANY WARRANTY; without even the implied warranties of
* MERCHANTABILITY, SATISFACTORY QUALITY, or FITNESS FOR A PARTICULAR
* PURPOSE. See the GNU General Public License for more details.
*
* You should have received a copy of the GNU General Public License along
* with this program. If not, see .
*/
#include
#include
#include
#include
#include "LocalePlugin.h"
#include "timezonemodel.h"
TimeZoneLocationModel::TimeZoneLocationModel(QObject *parent):
QAbstractListModel(parent),
m_workerThread(new TimeZonePopulateWorker())
{
qRegisterMetaType();
m_roleNames[Qt::DisplayRole] = "displayName";
m_roleNames[TimeZoneRole] = "timeZone";
m_roleNames[CityRole] = "city";
m_roleNames[CountryRole] = "country";
m_roleNames[OffsetRole] = "offset";
m_roleNames[LatitudeRole] = "latitude";
m_roleNames[LongitudeRole] = "longitude";
QObject::connect(m_workerThread,
&TimeZonePopulateWorker::resultReady,
this,
&TimeZoneLocationModel::processModelResult);
QObject::connect(m_workerThread,
&TimeZonePopulateWorker::finished,
this,
&TimeZoneLocationModel::store);
QObject::connect(m_workerThread,
&TimeZonePopulateWorker::finished,
m_workerThread,
&QObject::deleteLater);
init();
}
void TimeZoneLocationModel::init()
{
beginResetModel();
m_workerThread->start();
}
void TimeZoneLocationModel::store()
{
m_workerThread = nullptr;
endResetModel();
}
void TimeZoneLocationModel::processModelResult(const TzLocation &location)
{
m_locations.append(location);
}
int TimeZoneLocationModel::rowCount(const QModelIndex &parent) const
{
if (parent.isValid())
return 0;
return m_locations.count();
}
QVariant TimeZoneLocationModel::data(const QModelIndex &index, int role) const
{
if (index.row() >= m_locations.count() || index.row() < 0)
return QVariant();
const TzLocation tz = m_locations.at(index.row());
const QString country(tz.full_country.isEmpty() ? tz.country : tz.full_country);
switch (role) {
case Qt::DisplayRole:
if (!tz.state.isEmpty())
return QStringLiteral("%1, %2, %3").arg(tz.city).arg(tz.state).arg(country);
else
return QStringLiteral("%1, %2").arg(tz.city).arg(country);
case SimpleRole:
return QStringLiteral("%1, %2").arg(tz.city).arg(country);
case TimeZoneRole:
return tz.timezone;
case CountryRole:
return tz.country;
case CityRole:
return tz.city;
case OffsetRole: {
QTimeZone tmp(tz.timezone.toLatin1());
return static_cast(tmp.standardTimeOffset(QDateTime::currentDateTime())) / 3600;
}
case LatitudeRole:
return tz.latitude;
case LongitudeRole:
return tz.longitude;
default:
qWarning() << Q_FUNC_INFO << "Unknown role";
return QVariant();
}
}
QHash TimeZoneLocationModel::roleNames() const
{
return m_roleNames;
}
void TimeZonePopulateWorker::run()
{
buildCityMap();
}
void TimeZonePopulateWorker::buildCityMap()
{
TzDB *tzdb = tz_load_db();
GPtrArray *tz_locations = tz_get_locations(tzdb);
TimeZoneLocationModel::TzLocation tmpTz;
for (guint i = 0; i < tz_locations->len; ++i) {
auto tmp = static_cast(g_ptr_array_index(tz_locations, i));
gchar *en_name, *country, *zone, *state, *full_country;
gdouble latitude;
gdouble longitude;
g_object_get (tmp, "en_name", &en_name,
"country", &country,
"zone", &zone,
"state", &state,
"full_country", &full_country,
"latitude", &latitude,
"longitude", &longitude,
nullptr);
// There are empty entries in the DB
if (g_strcmp0(en_name, "") != 0) {
tmpTz.city = en_name;
tmpTz.country = country;
tmpTz.timezone = zone;
tmpTz.state = state;
tmpTz.full_country = full_country;
tmpTz.latitude = latitude;
tmpTz.longitude = longitude;
Q_EMIT (resultReady(tmpTz));
}
g_free (en_name);
g_free (country);
g_free (zone);
g_free (state);
g_free (full_country);
}
g_ptr_array_free (tz_locations, TRUE);
tz_db_free(tzdb);
}
TimeZoneFilterModel::TimeZoneFilterModel(QObject *parent)
: QSortFilterProxyModel(parent)
{
setDynamicSortFilter(false);
setSortLocaleAware(true);
setSortRole(TimeZoneLocationModel::CityRole);
m_stringMatcher.setCaseSensitivity(Qt::CaseInsensitive);
sort(0);
}
bool TimeZoneFilterModel::filterAcceptsRow(int row, const QModelIndex &parentIndex) const
{
if (!sourceModel()) {
return true;
}
if (!m_filter.isEmpty()) { // filtering by freeform text input, cf setFilter(QString)
const QString city = sourceModel()->index(row, 0, parentIndex).data(TimeZoneLocationModel::CityRole).toString();
if (m_stringMatcher.indexIn(city) == 0) { // match at the beginning of the city name
return true;
}
} else if (!m_country.isEmpty()) { // filter by country code
const QString countryCode = sourceModel()->index(row, 0, parentIndex).data(TimeZoneLocationModel::CountryRole).toString();
return m_country.compare(countryCode, Qt::CaseInsensitive) == 0;
}
return false;
}
QString TimeZoneFilterModel::filter() const
{
return m_filter;
}
void TimeZoneFilterModel::setFilter(const QString &filter)
{
if (filter != m_filter) {
m_filter = filter;
m_stringMatcher.setPattern(m_filter);
Q_EMIT filterChanged();
invalidate();
}
}
QString TimeZoneFilterModel::country() const
{
return m_country;
}
void TimeZoneFilterModel::setCountry(const QString &country)
{
if (m_country == country)
return;
m_country = country;
Q_EMIT countryChanged(country);
}