Commit a33d9f5a authored by Adriaan de Groot's avatar Adriaan de Groot

Merge branch 'milohr-qml-locale'

FIXES #1353
parents ec800e42 e3b4689c
......@@ -33,9 +33,7 @@ Label::Label( const QString& locale, LabelFormat format, QObject* parent )
: QObject( parent )
, m_locale( Label::getLocale( locale ) )
, m_localeId( locale.isEmpty() ? m_locale.name() : locale )
{
//: language[name] (country[name])
QString longFormat = QObject::tr( "%1 (%2)" );
QString languageName = m_locale.nativeLanguageName();
......
......@@ -40,10 +40,6 @@ class Label : public QObject
{
Q_OBJECT
Q_PROPERTY( QString label READ label CONSTANT FINAL )
Q_PROPERTY( QString englishLabel READ englishLabel CONSTANT FINAL )
Q_PROPERTY( QString localeId MEMBER m_localeId CONSTANT FINAL )
public:
/** @brief Formatting option for label -- add (country) to label. */
enum class LabelFormat
......@@ -65,6 +61,7 @@ public:
LabelFormat format = LabelFormat::IfNeededWithCountry,
QObject* parent = nullptr );
/** @brief Define a sorting order.
*
* Locales are sorted by their id, which means the ISO 2-letter code + country.
......
......@@ -239,7 +239,13 @@ CStringListModel::CStringListModel( CStringPairList l )
{
}
CStringListModel::~CStringListModel() {}
void
CStringListModel::setList( CalamaresUtils::Locale::CStringPairList l )
{
beginResetModel();
m_list = l;
endResetModel();
}
int
CStringListModel::rowCount( const QModelIndex& ) const
......@@ -264,6 +270,30 @@ CStringListModel::data( const QModelIndex& index, int role ) const
return item ? ( role == Qt::DisplayRole ? item->tr() : item->key() ) : QVariant();
}
void
CStringListModel::setCurrentIndex( int index )
{
if ( ( index < 0 ) || ( index >= m_list.count() ) )
{
return;
}
m_currentIndex = index;
emit currentIndexChanged();
}
int
CStringListModel::currentIndex() const
{
return m_currentIndex;
}
QHash< int, QByteArray >
CStringListModel::roleNames() const
{
return { { Qt::DisplayRole, "label" }, { Qt::UserRole, "key" } };
}
const CStringPair*
CStringListModel::item( int index ) const
{
......
......@@ -44,8 +44,9 @@ namespace Locale
* QPair<QString, QString> because there is API that needs
* C-style strings.
*/
class CStringPair
class CStringPair : public QObject
{
Q_OBJECT
public:
/// @brief An empty pair
CStringPair() {}
......@@ -86,6 +87,7 @@ public:
/// @brief A pair of strings for timezone regions (e.g. "America")
class TZRegion : public CStringPair
{
Q_OBJECT
public:
using CStringPair::CStringPair;
virtual ~TZRegion() override;
......@@ -117,6 +119,7 @@ private:
/// @brief A pair of strings for specific timezone names (e.g. "New_York")
class TZZone : public CStringPair
{
Q_OBJECT
public:
using CStringPair::CStringPair;
QString tr() const override;
......@@ -137,21 +140,52 @@ protected:
class CStringListModel : public QAbstractListModel
{
Q_OBJECT
Q_PROPERTY( int currentIndex READ currentIndex WRITE setCurrentIndex NOTIFY currentIndexChanged )
public:
/// @brief Create empty model
CStringListModel();
CStringListModel() {}
/// @brief Create model from list (non-owning)
CStringListModel( CStringPairList );
virtual ~CStringListModel() override;
int rowCount( const QModelIndex& parent ) const override;
QVariant data( const QModelIndex& index, int role ) const override;
const CStringPair* item( int index ) const;
QHash< int, QByteArray > roleNames() const override;
void setCurrentIndex( int index );
int currentIndex() const;
void setList( CStringPairList );
inline int indexOf( const QString& key )
{
const auto it = std::find_if(
m_list.constBegin(), m_list.constEnd(), [&]( const CalamaresUtils::Locale::CStringPair* item ) -> bool {
return item->key() == key;
} );
if ( it != m_list.constEnd() )
{
// distance() is usually a long long
return int( std::distance( m_list.constBegin(), it ) );
}
else
{
return -1;
}
}
private:
CStringPairList m_list;
int m_currentIndex = -1;
signals:
void currentIndexChanged();
};
} // namespace Locale
......
......@@ -13,6 +13,7 @@ calamares_add_plugin( locale
EXPORT_MACRO PLUGINDLLEXPORT_PRO
SOURCES
${geoip_src}
Config.cpp
LCLocaleDialog.cpp
LocaleConfiguration.cpp
LocalePage.cpp
......
This diff is collapsed.
/* === This file is part of Calamares - <https://github.com/calamares> ===
*
* Copyright 2019-2020, Adriaan de Groot <groot@kde.org>
* Copyright 2020, Camilo Higuita <milo.h@aol.com>
*
* Calamares is free software: you can redistribute it and/or modify
* it under the terms of the GNU General Public License as published by
* the Free Software Foundation, either version 3 of the License, or
* (at your option) any later version.
*
* Calamares is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY 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 Calamares. If not, see <http://www.gnu.org/licenses/>.
*/
#ifndef LOCALE_CONFIG_H
#define LOCALE_CONFIG_H
#include "LocaleConfiguration.h"
#include "timezonewidget/localeglobal.h"
#include "Job.h"
#include "locale/TimeZone.h"
#include <QAbstractListModel>
#include <QObject>
#include <memory>
class Config : public QObject
{
Q_OBJECT
Q_PROPERTY( CalamaresUtils::Locale::CStringListModel* zonesModel READ zonesModel CONSTANT FINAL )
Q_PROPERTY( CalamaresUtils::Locale::CStringListModel* regionModel READ regionModel CONSTANT FINAL )
Q_PROPERTY( QString prettyStatus READ prettyStatus NOTIFY prettyStatusChanged FINAL )
public:
Config( QObject* parent = nullptr );
~Config();
CalamaresUtils::Locale::CStringListModel* regionModel() const;
CalamaresUtils::Locale::CStringListModel* zonesModel() const;
void setLocaleInfo( const QString& initialRegion, const QString& initialZone, const QString& localeGenPath );
Calamares::JobList createJobs();
QMap< QString, QString > localesMap();
QString prettyStatus() const;
private:
CalamaresUtils::Locale::CStringPairList m_regionList;
CalamaresUtils::Locale::CStringListModel* m_regionModel;
CalamaresUtils::Locale::CStringListModel* m_zonesModel;
LocaleConfiguration m_selectedLocaleConfiguration;
QStringList m_localeGenLines;
int m_currentRegion = -1;
bool m_blockTzWidgetSet;
LocaleConfiguration guessLocaleConfiguration() const;
// For the given locale config, return two strings describing
// the settings for language and numbers.
std::pair< QString, QString > prettyLocaleStatus( const LocaleConfiguration& ) const;
/** @brief Update the GS *locale* key with the selected system language.
*
* This uses whatever is set in m_selectedLocaleConfiguration as the language,
* and writes it to GS *locale* key (as a string, in BCP47 format).
*/
void updateGlobalLocale();
void updateGlobalStorage();
void updateLocaleLabels();
const CalamaresUtils::Locale::TZZone* currentLocation() const;
signals:
void prettyStatusChanged();
};
#endif
# When debugging the timezone widget, add this debugging definition
# to have a debugging-friendly timezone widget, debug logging,
# and no intrusive timezone-setting while clicking around.
option( DEBUG_TIMEZONES "Debug-friendly timezone widget." OFF )
if( DEBUG_TIMEZONES )
add_definitions( -DDEBUG_TIMEZONES )
endif()
set( _locale ${CMAKE_CURRENT_SOURCE_DIR}/../locale )
include_directories( ${PROJECT_BINARY_DIR}/src/libcalamaresui ${CMAKE_CURRENT_SOURCE_DIR}/../../libcalamares ${_locale} )
calamares_add_plugin( localeq
TYPE viewmodule
EXPORT_MACRO PLUGINDLLEXPORT_PRO
SOURCES
${geoip_src}
LocaleQmlViewStep.cpp
${_locale}/LocaleConfiguration.cpp
${_locale}/Config.cpp
${_locale}/SetTimezoneJob.cpp
${_locale}/timezonewidget/localeglobal.cpp
RESOURCES
${_locale}/locale.qrc
LINK_PRIVATE_LIBRARIES
calamaresui
Qt5::Network
${geoip_libs}
${YAMLCPP_LIBRARY}
SHARED_LIB
)
# add_executable( localeqmltest qmlmain.cpp Config.cpp LocaleQmlViewStep.cpp LocaleConfiguration.cpp timezonewidget/localeglobal.cpp SetTimezoneJob.cpp ${geoip_src} )
# target_link_libraries( localeqmltest PRIVATE calamaresui Qt5::Core Qt5::Network Qt5::DBus ${geoip_libs})
# set_target_properties( localeqmltest
# PROPERTIES
# ENABLE_EXPORTS TRUE
# RUNTIME_OUTPUT_NAME localeqmltest
# )
# calamares_automoc( localeqmltest )
# calamares_autouic( localeqmltest )
/* === This file is part of Calamares - <https://github.com/calamares> ===
*
* Copyright 2014-2015, Teo Mrnjavac <teo@kde.org>
* Copyright 2018,2020 Adriaan de Groot <groot@kde.org>
*
* Calamares is free software: you can redistribute it and/or modify
* it under the terms of the GNU General Public License as published by
* the Free Software Foundation, either version 3 of the License, or
* (at your option) any later version.
*
* Calamares is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY 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 Calamares. If not, see <http://www.gnu.org/licenses/>.
*/
#include "LocaleQmlViewStep.h"
#include "GlobalStorage.h"
#include "JobQueue.h"
#include "geoip/Handler.h"
#include "network/Manager.h"
#include "utils/CalamaresUtilsGui.h"
#include "utils/Logger.h"
#include "utils/Variant.h"
#include "utils/Yaml.h"
#include "timezonewidget/localeglobal.h"
#include "Branding.h"
#include "modulesystem/ModuleManager.h"
#include <QQmlEngine>
#include <QFutureWatcher>
#include <QPixmap>
#include <QVariant>
CALAMARES_PLUGIN_FACTORY_DEFINITION( LocaleQmlViewStepFactory, registerPlugin< LocaleQmlViewStep >(); )
LocaleQmlViewStep::LocaleQmlViewStep( QObject* parent )
: Calamares::QmlViewStep( parent )
, m_config( new Config( this ) )
, m_nextEnabled( false )
, m_geoip( nullptr )
{
emit nextStatusChanged( m_nextEnabled );
}
QObject*
LocaleQmlViewStep::getConfig()
{
return m_config;
}
void
LocaleQmlViewStep::fetchGeoIpTimezone()
{
if ( m_geoip && m_geoip->isValid() )
{
m_startingTimezone = m_geoip->get();
if ( !m_startingTimezone.isValid() )
{
cWarning() << "GeoIP lookup at" << m_geoip->url() << "failed.";
}
}
m_config->setLocaleInfo(m_startingTimezone.first, m_startingTimezone.second, m_localeGenPath);
}
Calamares::RequirementsList LocaleQmlViewStep::checkRequirements()
{
LocaleGlobal::init();
if ( m_geoip && m_geoip->isValid() )
{
auto& network = CalamaresUtils::Network::Manager::instance();
if ( network.hasInternet() )
{
fetchGeoIpTimezone();
}
else
{
if ( network.synchronousPing( m_geoip->url() ) )
{
fetchGeoIpTimezone();
}
}
}
return Calamares::RequirementsList();
}
QString
LocaleQmlViewStep::prettyName() const
{
return tr( "Location" );
}
bool
LocaleQmlViewStep::isNextEnabled() const
{
// TODO: should return true
return true;
}
bool
LocaleQmlViewStep::isBackEnabled() const
{
// TODO: should return true (it's weird that you are not allowed to have welcome *after* anything
return true;
}
bool
LocaleQmlViewStep::isAtBeginning() const
{
// TODO: adjust to "pages" in the QML
return true;
}
bool
LocaleQmlViewStep::isAtEnd() const
{
// TODO: adjust to "pages" in the QML
return true;
}
Calamares::JobList
LocaleQmlViewStep::jobs() const
{
return m_jobs;
}
void LocaleQmlViewStep::onActivate()
{
// TODO no sure if it is needed at all or for the abstract class to start something
}
void LocaleQmlViewStep::onLeave()
{
if ( true )
{
m_jobs = m_config->createJobs();
// m_prettyStatus = m_actualWidget->prettyStatus();
auto map = m_config->localesMap();
QVariantMap vm;
for ( auto it = map.constBegin(); it != map.constEnd(); ++it )
{
vm.insert( it.key(), it.value() );
}
Calamares::JobQueue::instance()->globalStorage()->insert( "localeConf", vm );
}
else
{
m_jobs.clear();
Calamares::JobQueue::instance()->globalStorage()->remove( "localeConf" );
}
}
void LocaleQmlViewStep::setConfigurationMap(const QVariantMap& configurationMap)
{
QString region = CalamaresUtils::getString( configurationMap, "region" );
QString zone = CalamaresUtils::getString( configurationMap, "zone" );
if ( !region.isEmpty() && !zone.isEmpty() )
{
m_startingTimezone = CalamaresUtils::GeoIP::RegionZonePair( region, zone );
}
else
{
m_startingTimezone
= CalamaresUtils::GeoIP::RegionZonePair( QStringLiteral( "America" ), QStringLiteral( "New_York" ) );
}
m_localeGenPath = CalamaresUtils::getString( configurationMap, "localeGenPath" );
if ( m_localeGenPath.isEmpty() )
{
m_localeGenPath = QStringLiteral( "/etc/locale.gen" );
}
bool ok = false;
QVariantMap geoip = CalamaresUtils::getSubMap( configurationMap, "geoip", ok );
if ( ok )
{
QString url = CalamaresUtils::getString( geoip, "url" );
QString style = CalamaresUtils::getString( geoip, "style" );
QString selector = CalamaresUtils::getString( geoip, "selector" );
m_geoip = std::make_unique< CalamaresUtils::GeoIP::Handler >( style, url, selector );
if ( !m_geoip->isValid() )
{
cWarning() << "GeoIP Style" << style << "is not recognized.";
}
}
checkRequirements();
Calamares::QmlViewStep::setConfigurationMap( configurationMap ); // call parent implementation last
setContextProperty( "Localeq", m_config );
}
/* === This file is part of Calamares - <https://github.com/calamares> ===
*
* Copyright 2019-2020 Adriaan de Groot <groot@kde.org>
*
* Calamares is free software: you can redistribute it and/or modify
* it under the terms of the GNU General Public License as published by
* the Free Software Foundation, either version 3 of the License, or
* (at your option) any later version.
*
* Calamares is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY 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 Calamares. If not, see <http://www.gnu.org/licenses/>.
*/
#ifndef LOCALE_QMLVIEWSTEP_H
#define LOCALE_QMLVIEWSTEP_H
#include "Config.h"
#include "geoip/Handler.h"
#include "geoip/Interface.h"
#include "utils/PluginFactory.h"
#include "viewpages/QmlViewStep.h"
#include <DllMacro.h>
#include <QFutureWatcher>
#include <QObject>
#include <memory>
class PLUGINDLLEXPORT LocaleQmlViewStep : public Calamares::QmlViewStep
{
Q_OBJECT
public:
explicit LocaleQmlViewStep( QObject* parent = nullptr );
QString prettyName() const override;
bool isNextEnabled() const override;
bool isBackEnabled() const override;
bool isAtBeginning() const override;
bool isAtEnd() const override;
Calamares::JobList jobs() const override;
void onActivate() override;
void onLeave() override;
void setConfigurationMap( const QVariantMap& configurationMap ) override;
QObject* getConfig() override;
virtual Calamares::RequirementsList checkRequirements() override;
private:
// TODO: a generic QML viewstep should return a config object from a method
Config *m_config;
bool m_nextEnabled;
QString m_prettyStatus;
CalamaresUtils::GeoIP::RegionZonePair m_startingTimezone;
QString m_localeGenPath;
Calamares::JobList m_jobs;
std::unique_ptr< CalamaresUtils::GeoIP::Handler > m_geoip;
void fetchGeoIpTimezone();
};
CALAMARES_PLUGIN_FACTORY_DECLARATION( LocaleQmlViewStepFactory )
#endif
---
# This settings are used to set your default system time zone.
# Time zones are usually located under /usr/share/zoneinfo and
# provided by the 'tzdata' package of your Distribution.
#
# Distributions using systemd can list available
# time zones by using the timedatectl command.
# timedatectl list-timezones
#
# The starting timezone (e.g. the pin-on-the-map) when entering
# the locale page can be set through keys *region* and *zone*.
# If either is not set, defaults to America/New_York.
#
region: "America"
zone: "New_York"
# System locales are detected in the following order:
#
# - /usr/share/i18n/SUPPORTED
# - localeGenPath (defaults to /etc/locale.gen if not set)
# - 'locale -a' output
#
# Enable only when your Distribution is using an
# custom path for locale.gen
#
#localeGenPath: "PATH_TO/locale.gen"
# GeoIP based Language settings: Leave commented out to disable GeoIP.
#
# GeoIP needs a working Internet connection.
# This can be managed from `welcome.conf` by adding
# internet to the list of required conditions.
#
# The configuration
# is in three parts: a *style*, which can be "json" or "xml"
# depending on the kind of data returned by the service, and
# a *url* where the data is retrieved, and an optional *selector*
# to pick the right field out of the returned data (e.g. field
# name in JSON or element name in XML).
#
# The default selector (when the setting is blank) is picked to
# work with existing JSON providers (which use "time_zone") and
# Ubiquity's XML providers (which use "TimeZone").
#
# If the service configured via *url* uses
# a different attribute name (e.g. "timezone") in JSON or a
# different element tag (e.g. "<Time_Zone>") in XML, set this
# string to the name or tag to be used.
#
# In JSON:
# - if the string contains "." characters, this is used as a
# multi-level selector, e.g. "a.b" will select the timezone
# from data "{a: {b: "Europe/Amsterdam" } }".
# - each part of the string split by "." characters is used as
# a key into the JSON data.
# In XML:
# - all elements with the named tag (e.g. all TimeZone) elements
# from the document are checked; the first one with non-empty
# text value is used.
#
#
# An HTTP(S) request is made to *url*. The request should return
# valid data in a suitable format, depending on *style*;
# generally this includes a string value with the timezone
# in <region>/<zone> format. For services that return data which
# does not follow the conventions of "suitable data" described
# below, *selector* may be used to pick different data.
#
# Note that this example URL works, but the service is shutting
# down in June 2018.
#
# Suitable JSON data looks like
# ```
# {"time_zone":"America/New_York"}
# ```
# Suitable XML data looks like
# ```
# <Response><TimeZone>Europe/Brussels</TimeZone></Response>
# ```
#
# To accommodate providers of GeoIP timezone data with peculiar timezone
# naming conventions, the following cleanups are performed automatically:
# - backslashes are removed
# - spaces are replaced with _
#
# Legacy settings "geoipStyle", "geoipUrl" and "geoipSelector"
# in the top-level are still supported, but I'd advise against.
#
# To disable GeoIP checking, either comment-out the entire geoip section,
# or set the *style* key to an unsupported format (e.g. `none`).
# Also, note the analogous feature in src/modules/welcome/welcome.conf.
#
geoip:
style: "json"
url: "https://geoip.kde.org/v1/calamares"
selector: "" # leave blank for the default
import io.calamares.modules 1.0 as Modules
import io.calamares.ui 1.0
import QtQuick 2.10
import QtQuick.Controls 2.10
import QtQuick.Layouts 1.3
import org.kde.kirigami 2.7 as Kirigami
import QtGraphicalEffects 1.0
ResponsiveBase
{
id: control
Modules.Locale //locale handler
{
id: _locale
}
title: stackView.currentItem.title
subtitle: stackView.currentItem.subtitle
message: stackView.currentItem.message
stackView.initialItem: Item
{
id: _regionsListComponent
property string title: qsTr("Region")
property string subtitle: qsTr("Pick your preferred region or use the default one based on your current location")
property string message: qsTr("Select your preferred zone within your location to continue with the installation")
ListViewTemplate
{
id: _regionListView
anchors.centerIn: parent
implicitWidth: Math.min(parent.width, 500)
implicitHeight: Math.min(contentHeight, 500)
currentIndex: model.currentIndex
model: _locale.Config.regionModel
delegate: ListItemDelegate
{
id: _delegate
label1.text: model.label
onClicked:
{