diff --git a/src/modules/locale/CMakeLists.txt b/src/modules/locale/CMakeLists.txt index cae2ea41c..2643ff548 100644 --- a/src/modules/locale/CMakeLists.txt +++ b/src/modules/locale/CMakeLists.txt @@ -13,6 +13,7 @@ calamares_add_plugin( locale EXPORT_MACRO PLUGINDLLEXPORT_PRO SOURCES ${geoip_src} + Config.cpp LCLocaleDialog.cpp LocaleConfiguration.cpp LocalePage.cpp diff --git a/src/modules/locale/Config.cpp b/src/modules/locale/Config.cpp new file mode 100644 index 000000000..9bbbd5807 --- /dev/null +++ b/src/modules/locale/Config.cpp @@ -0,0 +1,314 @@ +/* === This file is part of Calamares - === + * + * Copyright 2019-2020, Adriaan de Groot + * + * 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 . + */ + +#include "Config.h" + +#include +#include + +#include "SetTimezoneJob.h" +#include "timezonewidget/timezonewidget.h" + +#include "GlobalStorage.h" +#include "JobQueue.h" +#include "LCLocaleDialog.h" +#include "Settings.h" + +#include "locale/Label.h" +#include "locale/TimeZone.h" +#include "utils/CalamaresUtilsGui.h" +#include "utils/Logger.h" +#include "utils/Retranslator.h" + +Config::Config(QObject *parent) : QObject(parent) +, m_regionList( CalamaresUtils::Locale::TZRegion::fromZoneTab() ) +, m_regionModel( new CalamaresUtils::Locale::CStringListModel ( m_regionList ) ) +, m_zonesModel( new CalamaresUtils::Locale::CStringListModel ( ) ) +, m_blockTzWidgetSet( false ) +{ + connect(m_regionModel, &CalamaresUtils::Locale::CStringListModel::currentIndexChanged, [&]() + { + m_zonesModel->setList(static_cast(m_regionModel->item(m_regionModel->currentIndex()))->zones()); + updateLocaleLabels(); + }); + + connect(m_zonesModel, &CalamaresUtils::Locale::CStringListModel::currentIndexChanged, [&]() + { + updateLocaleLabels(); + }); +} + +Config::~Config() +{ + qDeleteAll( m_regionList ); +} + +CalamaresUtils::Locale::CStringListModel * +Config::zonesModel() const +{ + return m_zonesModel; +} + +CalamaresUtils::Locale::CStringListModel * +Config::regionModel() const +{ + return m_regionModel; +} + +void +Config::setLocaleInfo(const QString& initialRegion, const QString& initialZone, const QString& localeGenPath) +{ + using namespace CalamaresUtils::Locale; + + cDebug()<< "REGION MODEL SIZE" << initialRegion << initialZone; + auto* region = m_regionList.find< TZRegion >( initialRegion ); + if ( region && region->zones().find< TZZone >( initialZone ) ) + { + this->m_regionModel->setCurrentIndex(m_regionModel->indexOf(initialRegion)); + m_zonesModel->setList(region->zones()); + this->m_zonesModel->setCurrentIndex(m_zonesModel->indexOf(initialZone)); + } + else + { + this->m_regionModel->setCurrentIndex(m_regionModel->indexOf("America")); + m_zonesModel->setList(static_cast(m_regionModel->item(m_regionModel->currentIndex()))->zones()); + this->m_zonesModel->setCurrentIndex(m_zonesModel->indexOf("New_York")); + + } + + // Some distros come with a meaningfully commented and easy to parse locale.gen, + // and others ship a separate file /usr/share/i18n/SUPPORTED with a clean list of + // supported locales. We first try that one, and if it doesn't exist, we fall back + // to parsing the lines from locale.gen + m_localeGenLines.clear(); + QFile supported( "/usr/share/i18n/SUPPORTED" ); + QByteArray ba; + + if ( supported.exists() && supported.open( QIODevice::ReadOnly | QIODevice::Text ) ) + { + ba = supported.readAll(); + supported.close(); + + const auto lines = ba.split( '\n' ); + for ( const QByteArray& line : lines ) + { + m_localeGenLines.append( QString::fromLatin1( line.simplified() ) ); + } + } + else + { + QFile localeGen( localeGenPath ); + if ( localeGen.open( QIODevice::ReadOnly | QIODevice::Text ) ) + { + ba = localeGen.readAll(); + localeGen.close(); + } + else + { + cWarning() << "Cannot open file" << localeGenPath + << ". Assuming the supported languages are already built into " + "the locale archive."; + QProcess localeA; + localeA.start( "locale", QStringList() << "-a" ); + localeA.waitForFinished(); + ba = localeA.readAllStandardOutput(); + } + const auto lines = ba.split( '\n' ); + for ( const QByteArray& line : lines ) + { + if ( line.startsWith( "## " ) || line.startsWith( "# " ) || line.simplified() == "#" ) + { + continue; + } + + QString lineString = QString::fromLatin1( line.simplified() ); + if ( lineString.startsWith( "#" ) ) + { + lineString.remove( '#' ); + } + lineString = lineString.simplified(); + + if ( lineString.isEmpty() ) + { + continue; + } + + m_localeGenLines.append( lineString ); + } + } + + if ( m_localeGenLines.isEmpty() ) + { + cWarning() << "cannot acquire a list of available locales." + << "The locale and localecfg modules will be broken as long as this " + "system does not provide" + << "\n\t " + << "* a well-formed" << supported.fileName() << "\n\tOR" + << "* a well-formed" + << ( localeGenPath.isEmpty() ? QLatin1String( "/etc/locale.gen" ) : localeGenPath ) << "\n\tOR" + << "* a complete pre-compiled locale-gen database which allows complete locale -a output."; + return; // something went wrong and there's nothing we can do about it. + } + + // Assuming we have a list of supported locales, we usually only want UTF-8 ones + // because it's not 1995. + for ( auto it = m_localeGenLines.begin(); it != m_localeGenLines.end(); ) + { + if ( !it->contains( "UTF-8", Qt::CaseInsensitive ) && !it->contains( "utf8", Qt::CaseInsensitive ) ) + { + it = m_localeGenLines.erase( it ); + } + else + { + ++it; + } + } + + // We strip " UTF-8" from "en_US.UTF-8 UTF-8" because it's redundant redundant. + for ( auto it = m_localeGenLines.begin(); it != m_localeGenLines.end(); ++it ) + { + if ( it->endsWith( " UTF-8" ) ) + { + it->chop( 6 ); + } + *it = it->simplified(); + } + updateGlobalStorage(); + updateLocaleLabels(); +} + +void Config::updateGlobalLocale() +{ + auto* gs = Calamares::JobQueue::instance()->globalStorage(); + const QString bcp47 = m_selectedLocaleConfiguration.toBcp47(); + gs->insert( "locale", bcp47 ); +} + +void Config::updateGlobalStorage() +{ + auto* gs = Calamares::JobQueue::instance()->globalStorage(); + + const auto* location = currentLocation(); + bool locationChanged = ( location->region() != gs->value( "locationRegion" ) ) + || ( location->zone() != gs->value( "locationZone" ) ); + + gs->insert( "locationRegion", location->region() ); + gs->insert( "locationZone", location->zone() ); + + updateGlobalLocale(); + + // If we're in chroot mode (normal install mode), then we immediately set the + // timezone on the live system. When debugging timezones, don't bother. + #ifndef DEBUG_TIMEZONES + if ( locationChanged && Calamares::Settings::instance()->doChroot() ) + { + QProcess::execute( "timedatectl", // depends on systemd + { "set-timezone", location->region() + '/' + location->zone() } ); + } + #endif + + // Preserve those settings that have been made explicit. + auto newLocale = guessLocaleConfiguration(); + if ( !m_selectedLocaleConfiguration.isEmpty() && m_selectedLocaleConfiguration.explicit_lang ) + { + newLocale.setLanguage( m_selectedLocaleConfiguration.language() ); + } + if ( !m_selectedLocaleConfiguration.isEmpty() && m_selectedLocaleConfiguration.explicit_lc ) + { + newLocale.lc_numeric = m_selectedLocaleConfiguration.lc_numeric; + newLocale.lc_time = m_selectedLocaleConfiguration.lc_time; + newLocale.lc_monetary = m_selectedLocaleConfiguration.lc_monetary; + newLocale.lc_paper = m_selectedLocaleConfiguration.lc_paper; + newLocale.lc_name = m_selectedLocaleConfiguration.lc_name; + newLocale.lc_address = m_selectedLocaleConfiguration.lc_address; + newLocale.lc_telephone = m_selectedLocaleConfiguration.lc_telephone; + newLocale.lc_measurement = m_selectedLocaleConfiguration.lc_measurement; + newLocale.lc_identification = m_selectedLocaleConfiguration.lc_identification; + } + newLocale.explicit_lang = m_selectedLocaleConfiguration.explicit_lang; + newLocale.explicit_lc = m_selectedLocaleConfiguration.explicit_lc; + + m_selectedLocaleConfiguration = newLocale; + updateLocaleLabels(); +} + +void +Config::updateLocaleLabels() +{ + LocaleConfiguration lc + = m_selectedLocaleConfiguration.isEmpty() ? guessLocaleConfiguration() : m_selectedLocaleConfiguration; + auto labels = prettyLocaleStatus( lc ); + emit prettyStatusChanged(); +} + + +std::pair +Config::prettyLocaleStatus(const LocaleConfiguration& lc) const +{ + using CalamaresUtils::Locale::Label; + + Label lang( lc.language(), Label::LabelFormat::AlwaysWithCountry ); + Label num( lc.lc_numeric, Label::LabelFormat::AlwaysWithCountry ); + + return std::make_pair< QString, QString >( + tr( "The system language will be set to %1." ).arg( lang.label() ), + tr( "The numbers and dates locale will be set to %1." ).arg( num.label() ) ); +} + +Calamares::JobList Config::createJobs() +{ + QList< Calamares::job_ptr > list; + const CalamaresUtils::Locale::TZZone* location = currentLocation(); + + Calamares::Job* j = new SetTimezoneJob( location->region(), location->zone() ); + list.append( Calamares::job_ptr( j ) ); + + return list; +} + +LocaleConfiguration Config::guessLocaleConfiguration() const +{ + return LocaleConfiguration::fromLanguageAndLocation( + QLocale().name(), m_localeGenLines, currentLocation() ? currentLocation()->country() : "" ); +} + +QMap Config::localesMap() +{ + return m_selectedLocaleConfiguration.isEmpty() ? guessLocaleConfiguration().toMap() + : m_selectedLocaleConfiguration.toMap(); +} + +QString Config::prettyStatus() const +{ + QString status; + status += tr( "Set timezone to %1/%2.
" ).arg( m_regionModel->item(m_regionModel->currentIndex())->tr() ).arg( m_zonesModel->item(m_zonesModel->currentIndex())->tr() ); + + LocaleConfiguration lc + = m_selectedLocaleConfiguration.isEmpty() ? guessLocaleConfiguration() : m_selectedLocaleConfiguration; + auto labels = prettyLocaleStatus( lc ); + status += labels.first + "
"; + status += labels.second + "
"; + + return status; +} + + +const CalamaresUtils::Locale::TZZone * Config::currentLocation() const +{ + return static_cast(m_zonesModel->item(m_zonesModel->currentIndex())); +} diff --git a/src/modules/locale/Config.h b/src/modules/locale/Config.h new file mode 100644 index 000000000..2af83b398 --- /dev/null +++ b/src/modules/locale/Config.h @@ -0,0 +1,84 @@ +/* === This file is part of Calamares - === + * + * Copyright 2019-2020, Adriaan de Groot + * + * 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 . + */ + +#ifndef LOCALE_CONFIG_H +#define LOCALE_CONFIG_H + +#include +#include +#include +#include "Job.h" +#include "locale/TimeZone.h" +#include "LocaleConfiguration.h" +#include "timezonewidget/localeglobal.h" +#include + +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 diff --git a/src/modules/localeq/CMakeLists.txt b/src/modules/localeq/CMakeLists.txt new file mode 100644 index 000000000..99fac2a7e --- /dev/null +++ b/src/modules/localeq/CMakeLists.txt @@ -0,0 +1,42 @@ +# 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 ) + diff --git a/src/modules/localeq/LocaleQmlViewStep.cpp b/src/modules/localeq/LocaleQmlViewStep.cpp new file mode 100644 index 000000000..8376b4f1d --- /dev/null +++ b/src/modules/localeq/LocaleQmlViewStep.cpp @@ -0,0 +1,203 @@ +/* === This file is part of Calamares - === + * + * Copyright 2014-2015, Teo Mrnjavac + * Copyright 2018,2020 Adriaan de Groot + * + * 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 . + */ + +#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 +#include +#include +#include + +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 ); +} diff --git a/src/modules/localeq/LocaleQmlViewStep.h b/src/modules/localeq/LocaleQmlViewStep.h new file mode 100644 index 000000000..0639274d6 --- /dev/null +++ b/src/modules/localeq/LocaleQmlViewStep.h @@ -0,0 +1,76 @@ +/* === This file is part of Calamares - === + * + * Copyright 2019-2020 Adriaan de Groot + * + * 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 . + */ + +#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 + +#include +#include + +#include + +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 diff --git a/src/modules/localeq/localeq.conf b/src/modules/localeq/localeq.conf new file mode 100644 index 000000000..4beb4fe85 --- /dev/null +++ b/src/modules/localeq/localeq.conf @@ -0,0 +1,97 @@ +--- +# 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. "") 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 / 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 +# ``` +# Europe/Brussels +# ``` +# +# 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 diff --git a/src/modules/localeq/localeq.qml b/src/modules/localeq/localeq.qml new file mode 100644 index 000000000..b186a0081 --- /dev/null +++ b/src/modules/localeq/localeq.qml @@ -0,0 +1,113 @@ +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: + { + _regionListView.model.currentIndex = index + _stackView.push(_zonesListComponent) + } + } + + footer: RowLayout + { + width: parent.width + z: 99999 + Button + { + Layout.fillWidth: true + text: qsTr("Timezones") + icon.name: "go-previous" + onClicked: control.stackView.push(_zonesListComponent) + } + } + } + } + + + Component + { + id: _zonesListComponent + + Item + { + property string title: qsTr("Timezone") + property string subtitle: _locale.Config.prettyStatus + property string message: "" + ListViewTemplate + { + id: _zonesListView + anchors.centerIn: parent + implicitWidth: Math.min(parent.width, 500) + implicitHeight: Math.min(contentHeight, 500) + currentIndex: model.currentIndex + model: _locale.Config.zonesModel + + delegate: ListItemDelegate + { + id: _delegate + label1.text: model.label + onClicked: + { + _zonesListView.model.currentIndex = index + positionViewAtIndex(index, ListView.Center) + } + } + + footer: RowLayout + { + width: parent.width + z: 99999 + + Button + { + Layout.fillWidth: true + icon.name: "go-previous" + text: qsTr("Regions") + onClicked: control.stackView.pop() + } + } + } + } + } +} +