Merge branch 'issue-1391' into calamares

This commit is contained in:
Adriaan de Groot 2020-07-24 11:57:07 +02:00
commit a3e528aae3
26 changed files with 824 additions and 934 deletions

View File

@ -1,5 +1,5 @@
/* === This file is part of Calamares - <https://github.com/calamares> === /* === This file is part of Calamares - <https://github.com/calamares> ===
* *
* SPDX-FileCopyrightText: 2014-2015 Teo Mrnjavac <teo@kde.org> * SPDX-FileCopyrightText: 2014-2015 Teo Mrnjavac <teo@kde.org>
* SPDX-FileCopyrightText: 2017-2018 Adriaan de Groot <groot@kde.org> * SPDX-FileCopyrightText: 2017-2018 Adriaan de Groot <groot@kde.org>
* *
@ -36,8 +36,8 @@ using CalamaresUtils::operator""_MiB;
namespace Calamares namespace Calamares
{ {
GlobalStorage::GlobalStorage() GlobalStorage::GlobalStorage( QObject* parent )
: QObject( nullptr ) : QObject( parent )
{ {
} }

View File

@ -1,5 +1,5 @@
/* === This file is part of Calamares - <https://github.com/calamares> === /* === This file is part of Calamares - <https://github.com/calamares> ===
* *
* SPDX-FileCopyrightText: 2014-2015 Teo Mrnjavac <teo@kde.org> * SPDX-FileCopyrightText: 2014-2015 Teo Mrnjavac <teo@kde.org>
* SPDX-FileCopyrightText: 2017-2018 Adriaan de Groot <groot@kde.org> * SPDX-FileCopyrightText: 2017-2018 Adriaan de Groot <groot@kde.org>
* *
@ -39,7 +39,7 @@ class GlobalStorage : public QObject
{ {
Q_OBJECT Q_OBJECT
public: public:
explicit GlobalStorage(); explicit GlobalStorage( QObject* parent = nullptr );
//NOTE: thread safety is guaranteed by JobQueue, which executes jobs one by one. //NOTE: thread safety is guaranteed by JobQueue, which executes jobs one by one.
// If at any time jobs become concurrent, this class must be made thread-safe. // If at any time jobs become concurrent, this class must be made thread-safe.

View File

@ -1,5 +1,5 @@
/* === This file is part of Calamares - <https://github.com/calamares> === /* === This file is part of Calamares - <https://github.com/calamares> ===
* *
* SPDX-FileCopyrightText: 2014-2015 Teo Mrnjavac <teo@kde.org> * SPDX-FileCopyrightText: 2014-2015 Teo Mrnjavac <teo@kde.org>
* SPDX-FileCopyrightText: 2018 Adriaan de Groot <groot@kde.org> * SPDX-FileCopyrightText: 2018 Adriaan de Groot <groot@kde.org>
* *
@ -170,7 +170,7 @@ JobQueue::globalStorage() const
JobQueue::JobQueue( QObject* parent ) JobQueue::JobQueue( QObject* parent )
: QObject( parent ) : QObject( parent )
, m_thread( new JobThread( this ) ) , m_thread( new JobThread( this ) )
, m_storage( new GlobalStorage() ) , m_storage( new GlobalStorage( this ) )
{ {
Q_ASSERT( !s_instance ); Q_ASSERT( !s_instance );
s_instance = this; s_instance = this;

View File

@ -1,5 +1,5 @@
/* === This file is part of Calamares - <https://github.com/calamares> === /* === This file is part of Calamares - <https://github.com/calamares> ===
* *
* SPDX-FileCopyrightText: 2019 Adriaan de Groot <groot@kde.org> * SPDX-FileCopyrightText: 2019 Adriaan de Groot <groot@kde.org>
* *
* Calamares is free software: you can redistribute it and/or modify * Calamares is free software: you can redistribute it and/or modify
@ -168,7 +168,11 @@ Manager::checkHasInternet()
{ {
hasInternet = synchronousPing( d->m_hasInternetUrl ); hasInternet = synchronousPing( d->m_hasInternetUrl );
} }
d->m_hasInternet = hasInternet; if ( hasInternet != d->m_hasInternet )
{
d->m_hasInternet = hasInternet;
emit hasInternetChanged( hasInternet );
}
return hasInternet; return hasInternet;
} }

View File

@ -1,5 +1,5 @@
/* === This file is part of Calamares - <https://github.com/calamares> === /* === This file is part of Calamares - <https://github.com/calamares> ===
* *
* SPDX-FileCopyrightText: 2019 Adriaan de Groot <groot@kde.org> * SPDX-FileCopyrightText: 2019 Adriaan de Groot <groot@kde.org>
* *
* Calamares is free software: you can redistribute it and/or modify * Calamares is free software: you can redistribute it and/or modify
@ -98,9 +98,10 @@ struct RequestStatus
QDebug& operator<<( QDebug& s, const RequestStatus& e ); QDebug& operator<<( QDebug& s, const RequestStatus& e );
class DLLEXPORT Manager : QObject class DLLEXPORT Manager : public QObject
{ {
Q_OBJECT Q_OBJECT
Q_PROPERTY( bool hasInternet READ hasInternet NOTIFY hasInternetChanged FINAL )
Manager(); Manager();
@ -133,6 +134,16 @@ public:
/// @brief Set the URL which is used for the general "is there internet" check. /// @brief Set the URL which is used for the general "is there internet" check.
void setCheckHasInternetUrl( const QUrl& url ); void setCheckHasInternetUrl( const QUrl& url );
/** @brief Do a network request asynchronously.
*
* Returns a pointer to the reply-from-the-request.
* This may be a nullptr if an error occurs immediately.
* The caller is responsible for cleaning up the reply (eventually).
*/
QNetworkReply* asynchronousGet( const QUrl& url, const RequestOptions& options = RequestOptions() );
public Q_SLOTS:
/** @brief Do an explicit check for internet connectivity. /** @brief Do an explicit check for internet connectivity.
* *
* This **may** do a ping to the configured check URL, but can also * This **may** do a ping to the configured check URL, but can also
@ -148,13 +159,13 @@ public:
*/ */
bool hasInternet(); bool hasInternet();
/** @brief Do a network request asynchronously. signals:
/** @brief Indicates that internet connectivity status has changed
* *
* Returns a pointer to the reply-from-the-request. * The value is that returned from hasInternet() -- @c true when there
* This may be a nullptr if an error occurs immediately. * is connectivity, @c false otherwise.
* The caller is responsible for cleaning up the reply (eventually).
*/ */
QNetworkReply* asynchronousGet( const QUrl& url, const RequestOptions& options = RequestOptions() ); void hasInternetChanged( bool );
private: private:
class Private; class Private;

View File

@ -1,5 +1,5 @@
/* === This file is part of Calamares - <https://github.com/calamares> === /* === This file is part of Calamares - <https://github.com/calamares> ===
* *
* SPDX-FileCopyrightText: 2020 Adriaan de Groot <groot@kde.org> * SPDX-FileCopyrightText: 2020 Adriaan de Groot <groot@kde.org>
* *
* Calamares is free software: you can redistribute it and/or modify * Calamares is free software: you can redistribute it and/or modify
@ -24,6 +24,7 @@
#define UTILS_RAII_H #define UTILS_RAII_H
#include <QObject> #include <QObject>
#include <QSignalBlocker>
#include <type_traits> #include <type_traits>
@ -44,4 +45,21 @@ struct cqDeleter
} }
}; };
/// @brief Sets a bool to @p value and resets to !value on destruction
template < bool value >
struct cBoolSetter
{
bool& m_b;
cBoolSetter( bool& b )
: m_b( b )
{
m_b = value;
}
~cBoolSetter() { m_b = !value; }
};
/// @brief Blocks signals on a QObject until destruction
using cSignalBlocker = QSignalBlocker;
#endif #endif

View File

@ -103,10 +103,36 @@ public:
RequirementsModel* requirementsModel() { return m_requirementsModel; } RequirementsModel* requirementsModel() { return m_requirementsModel; }
signals: signals:
/** @brief Emitted when all the module **configuration** has been read
*
* This indicates that all of the module.desc files have been
* loaded; bad ones are silently skipped, so this just indicates
* that the module manager is ready for the next phase (loading).
*/
void initDone(); void initDone();
void modulesLoaded(); /// All of the modules were loaded successfully /** @brief Emitted when all the modules are loaded successfully
void modulesFailed( QStringList ); /// .. or not *
// Below, see RequirementsChecker documentation * Each module listed in the settings is loaded. Modules are loaded
* only once, even when instantiated multiple times. If all of
* the listed modules are successfully loaded, this signal is
* emitted (otherwise, it isn't, so you need to wait for **both**
* of the signals).
*
* If this is emitted (i.e. all modules have loaded) then the next
* phase, requirements checking, can be started.
*/
void modulesLoaded();
/** @brief Emitted if any modules failed to load
*
* Modules that failed to load (for any reason) are listed by
* instance key (e.g. "welcome@welcome", "shellprocess@mycustomthing").
*/
void modulesFailed( QStringList );
/** @brief Emitted after all requirements have been checked
*
* The bool value indicates if all of the **mandatory** requirements
* are satisfied (e.g. whether installation can continue).
*/
void requirementsComplete( bool ); void requirementsComplete( bool );
private slots: private slots:

View File

@ -23,6 +23,7 @@
#include "JobQueue.h" #include "JobQueue.h"
#include "Settings.h" #include "Settings.h"
#include "ViewManager.h" #include "ViewManager.h"
#include "network/Manager.h"
#include "utils/Dirs.h" #include "utils/Dirs.h"
#include "utils/Logger.h" #include "utils/Logger.h"
@ -242,6 +243,10 @@ registerQmlModels()
"io.calamares.core", 1, 0, "Global", []( QQmlEngine*, QJSEngine* ) -> QObject* { "io.calamares.core", 1, 0, "Global", []( QQmlEngine*, QJSEngine* ) -> QObject* {
return Calamares::JobQueue::instance()->globalStorage(); return Calamares::JobQueue::instance()->globalStorage();
} ); } );
qmlRegisterSingletonType< CalamaresUtils::Network::Manager >(
"io.calamares.core", 1, 0, "Network", []( QQmlEngine*, QJSEngine* ) -> QObject* {
return &CalamaresUtils::Network::Manager::instance();
} );
} }
} }

View File

@ -1,7 +1,8 @@
/* === This file is part of Calamares - <https://github.com/calamares> === /* === This file is part of Calamares - <https://github.com/calamares> ===
* *
* Copyright 2019-2020, Adriaan de Groot <groot@kde.org> * SPDX-FileCopyrightText: 2020 Adriaan de Groot <groot@kde.org>
* Copyright 2020, Camilo Higuita <milo.h@aol.com> * SPDX-License-Identifier: GPL-3.0-or-later
* License-Filename: LICENSE
* *
* Calamares is free software: you can redistribute it and/or modify * Calamares is free software: you can redistribute it and/or modify
* it under the terms of the GNU General Public License as published by * it under the terms of the GNU General Public License as published by
@ -19,85 +20,43 @@
#include "Config.h" #include "Config.h"
#include "LCLocaleDialog.h"
#include "SetTimezoneJob.h" #include "SetTimezoneJob.h"
#include "timezonewidget/timezonewidget.h"
#include "GlobalStorage.h" #include "GlobalStorage.h"
#include "JobQueue.h" #include "JobQueue.h"
#include "Settings.h" #include "Settings.h"
#include "locale/Label.h" #include "locale/Label.h"
#include "locale/TimeZone.h" #include "modulesystem/ModuleManager.h"
#include "utils/CalamaresUtilsGui.h" #include "network/Manager.h"
#include "utils/Logger.h" #include "utils/Logger.h"
#include "utils/Retranslator.h" #include "utils/Variant.h"
#include <QDebug>
#include <QFile> #include <QFile>
#include <QProcess> #include <QProcess>
#include <QTimeZone>
Config::Config( QObject* parent ) /** @brief Load supported locale keys
: QObject( parent ) *
, m_regionList( CalamaresUtils::Locale::TZRegion::fromZoneTab() ) * If i18n/SUPPORTED exists, read the lines from that and return those
, m_regionModel( new CalamaresUtils::Locale::CStringListModel( m_regionList ) ) * as supported locales; otherwise, try the file at @p localeGenPath
, m_zonesModel( new CalamaresUtils::Locale::CStringListModel() ) * and get lines from that. Failing both, try the output of `locale -a`.
, m_blockTzWidgetSet( false ) *
* This gives us a list of locale identifiers (e.g. en_US.UTF-8), which
* are not particularly human-readable.
*
* Only UTF-8 locales are returned (even if the system claims to support
* other, non-UTF-8, locales).
*/
static QStringList
loadLocales( const QString& localeGenPath )
{ {
connect( m_regionModel, &CalamaresUtils::Locale::CStringListModel::currentIndexChanged, [&]() { QStringList localeGenLines;
m_zonesModel->setList( static_cast< const CalamaresUtils::Locale::TZRegion* >(
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 ) )
{
m_regionModel->setCurrentIndex( m_regionModel->indexOf( initialRegion ) );
m_zonesModel->setList( region->zones() );
m_zonesModel->setCurrentIndex( m_zonesModel->indexOf( initialZone ) );
}
else
{
m_regionModel->setCurrentIndex( m_regionModel->indexOf( "America" ) );
m_zonesModel->setList(
static_cast< const TZRegion* >( m_regionModel->item( m_regionModel->currentIndex() ) )->zones() );
m_zonesModel->setCurrentIndex( m_zonesModel->indexOf( "New_York" ) );
}
// Some distros come with a meaningfully commented and easy to parse locale.gen, // 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 // 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 // supported locales. We first try that one, and if it doesn't exist, we fall back
// to parsing the lines from locale.gen // to parsing the lines from locale.gen
m_localeGenLines.clear(); localeGenLines.clear();
QFile supported( "/usr/share/i18n/SUPPORTED" ); QFile supported( "/usr/share/i18n/SUPPORTED" );
QByteArray ba; QByteArray ba;
@ -109,7 +68,7 @@ Config::setLocaleInfo( const QString& initialRegion, const QString& initialZone,
const auto lines = ba.split( '\n' ); const auto lines = ba.split( '\n' );
for ( const QByteArray& line : lines ) for ( const QByteArray& line : lines )
{ {
m_localeGenLines.append( QString::fromLatin1( line.simplified() ) ); localeGenLines.append( QString::fromLatin1( line.simplified() ) );
} }
} }
else else
@ -150,11 +109,11 @@ Config::setLocaleInfo( const QString& initialRegion, const QString& initialZone,
continue; continue;
} }
m_localeGenLines.append( lineString ); localeGenLines.append( lineString );
} }
} }
if ( m_localeGenLines.isEmpty() ) if ( localeGenLines.isEmpty() )
{ {
cWarning() << "cannot acquire a list of available locales." cWarning() << "cannot acquire a list of available locales."
<< "The locale and localecfg modules will be broken as long as this " << "The locale and localecfg modules will be broken as long as this "
@ -164,168 +123,388 @@ Config::setLocaleInfo( const QString& initialRegion, const QString& initialZone,
<< "* a well-formed" << "* a well-formed"
<< ( localeGenPath.isEmpty() ? QLatin1String( "/etc/locale.gen" ) : localeGenPath ) << "\n\tOR" << ( localeGenPath.isEmpty() ? QLatin1String( "/etc/locale.gen" ) : localeGenPath ) << "\n\tOR"
<< "* a complete pre-compiled locale-gen database which allows complete locale -a output."; << "* 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. return localeGenLines; // 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 // Assuming we have a list of supported locales, we usually only want UTF-8 ones
// because it's not 1995. // because it's not 1995.
for ( auto it = m_localeGenLines.begin(); it != m_localeGenLines.end(); ) auto notUtf8 = []( const QString& s ) {
{ return !s.contains( "UTF-8", Qt::CaseInsensitive ) && !s.contains( "utf8", Qt::CaseInsensitive );
if ( !it->contains( "UTF-8", Qt::CaseInsensitive ) && !it->contains( "utf8", Qt::CaseInsensitive ) ) };
{ auto it = std::remove_if( localeGenLines.begin(), localeGenLines.end(), notUtf8 );
it = m_localeGenLines.erase( it ); localeGenLines.erase( it, localeGenLines.end() );
}
else
{
++it;
}
}
// We strip " UTF-8" from "en_US.UTF-8 UTF-8" because it's redundant redundant. // 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 ) // Also simplify whitespace.
{ auto unredundant = []( QString& s ) {
if ( it->endsWith( " UTF-8" ) ) if ( s.endsWith( " UTF-8" ) )
{ {
it->chop( 6 ); s.chop( 6 );
} }
*it = it->simplified(); s = s.simplified();
} };
updateGlobalStorage(); std::for_each( localeGenLines.begin(), localeGenLines.end(), unredundant );
updateLocaleLabels();
return localeGenLines;
}
static inline const CalamaresUtils::Locale::CStringPairList&
timezoneData()
{
return CalamaresUtils::Locale::TZRegion::fromZoneTab();
}
Config::Config( QObject* parent )
: QObject( parent )
, m_regionModel( std::make_unique< CalamaresUtils::Locale::CStringListModel >( ::timezoneData() ) )
, m_zonesModel( std::make_unique< CalamaresUtils::Locale::CStringListModel >() )
{
// Slightly unusual: connect to our *own* signals. Wherever the language
// or the location is changed, these signals are emitted, so hook up to
// them to update global storage accordingly. This simplifies code:
// we don't need to call an update-GS method, or introduce an intermediate
// update-thing-and-GS method. And everywhere where we **do** change
// language or location, we already emit the signal.
connect( this, &Config::currentLanguageCodeChanged, [&]() {
auto* gs = Calamares::JobQueue::instance()->globalStorage();
gs->insert( "locale", m_selectedLocaleConfiguration.toBcp47() );
} );
connect( this, &Config::currentLCCodeChanged, [&]() {
auto* gs = Calamares::JobQueue::instance()->globalStorage();
// Update GS localeConf (the LC_ variables)
auto map = localeConfiguration().toMap();
QVariantMap vm;
for ( auto it = map.constBegin(); it != map.constEnd(); ++it )
{
vm.insert( it.key(), it.value() );
}
gs->insert( "localeConf", vm );
} );
connect( this, &Config::currentLocationChanged, [&]() {
auto* gs = Calamares::JobQueue::instance()->globalStorage();
// Update the GS region and zone (and possibly the live timezone)
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() );
if ( locationChanged && m_adjustLiveTimezone )
{
QProcess::execute( "timedatectl", // depends on systemd
{ "set-timezone", location->region() + '/' + location->zone() } );
}
} );
auto prettyStatusNotify = [&]() { emit prettyStatusChanged( prettyStatus() ); };
connect( this, &Config::currentLanguageStatusChanged, prettyStatusNotify );
connect( this, &Config::currentLCStatusChanged, prettyStatusNotify );
connect( this, &Config::currentLocationStatusChanged, prettyStatusNotify );
}
Config::~Config() {}
const CalamaresUtils::Locale::CStringPairList&
Config::timezoneData() const
{
return ::timezoneData();
} }
void void
Config::updateGlobalLocale() Config::setCurrentLocation()
{ {
auto* gs = Calamares::JobQueue::instance()->globalStorage(); if ( !m_currentLocation && m_startingTimezone.isValid() )
const QString bcp47 = m_selectedLocaleConfiguration.toBcp47(); {
gs->insert( "locale", bcp47 ); setCurrentLocation( m_startingTimezone.first, m_startingTimezone.second );
}
}
void Config::setCurrentLocation(const QString& regionzone)
{
auto r = CalamaresUtils::GeoIP::splitTZString( regionzone );
if ( r.isValid() )
{
setCurrentLocation( r.first, r.second );
}
} }
void void
Config::updateGlobalStorage() Config::setCurrentLocation( const QString& regionName, const QString& zoneName )
{ {
auto* gs = Calamares::JobQueue::instance()->globalStorage(); using namespace CalamaresUtils::Locale;
auto* region = timezoneData().find< TZRegion >( regionName );
const auto* location = currentLocation(); auto* zone = region ? region->zones().find< TZZone >( zoneName ) : nullptr;
bool locationChanged = ( location->region() != gs->value( "locationRegion" ) ) if ( zone )
|| ( location->zone() != gs->value( "locationZone" ) );
#ifdef DEBUG_TIMEZONES
if ( locationChanged )
{ {
cDebug() << "Location changed" << gs->value( "locationRegion" ) << ',' << gs->value( "locationZone" ) << "to" setCurrentLocation( zone );
<< location->region() << ',' << location->zone();
} }
#endif else
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 // Recursive, but America/New_York always exists.
{ "set-timezone", location->region() + '/' + location->zone() } ); setCurrentLocation( QStringLiteral( "America" ), QStringLiteral( "New_York" ) );
} }
#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 void
Config::updateLocaleLabels() Config::setCurrentLocation( const CalamaresUtils::Locale::TZZone* location )
{ {
LocaleConfiguration lc if ( location != m_currentLocation )
= m_selectedLocaleConfiguration.isEmpty() ? guessLocaleConfiguration() : m_selectedLocaleConfiguration; {
auto labels = prettyLocaleStatus( lc ); m_currentLocation = location;
emit prettyStatusChanged(); // Overwrite those settings that have not been made explicit.
} auto newLocale = automaticLocaleConfiguration();
if ( !m_selectedLocaleConfiguration.explicit_lang )
{
m_selectedLocaleConfiguration.setLanguage( newLocale.language() );
emit currentLanguageStatusChanged( currentLanguageStatus() );
}
if ( !m_selectedLocaleConfiguration.explicit_lc )
{
m_selectedLocaleConfiguration.lc_numeric = newLocale.lc_numeric;
m_selectedLocaleConfiguration.lc_time = newLocale.lc_time;
m_selectedLocaleConfiguration.lc_monetary = newLocale.lc_monetary;
m_selectedLocaleConfiguration.lc_paper = newLocale.lc_paper;
m_selectedLocaleConfiguration.lc_name = newLocale.lc_name;
m_selectedLocaleConfiguration.lc_address = newLocale.lc_address;
m_selectedLocaleConfiguration.lc_telephone = newLocale.lc_telephone;
m_selectedLocaleConfiguration.lc_measurement = newLocale.lc_measurement;
m_selectedLocaleConfiguration.lc_identification = newLocale.lc_identification;
emit currentLCStatusChanged( currentLCStatus() );
std::pair< QString, QString > }
Config::prettyLocaleStatus( const LocaleConfiguration& lc ) const emit currentLocationChanged( m_currentLocation );
{ }
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 LocaleConfiguration
Config::guessLocaleConfiguration() const Config::automaticLocaleConfiguration() const
{ {
// Special case: no location has been set at **all**
if ( !currentLocation() )
{
return LocaleConfiguration();
}
return LocaleConfiguration::fromLanguageAndLocation( return LocaleConfiguration::fromLanguageAndLocation(
QLocale().name(), m_localeGenLines, currentLocation() ? currentLocation()->country() : "" ); QLocale().name(), supportedLocales(), currentLocation()->country() );
} }
QMap< QString, QString > LocaleConfiguration
Config::localesMap() Config::localeConfiguration() const
{ {
return m_selectedLocaleConfiguration.isEmpty() ? guessLocaleConfiguration().toMap() return m_selectedLocaleConfiguration.isEmpty() ? automaticLocaleConfiguration() : m_selectedLocaleConfiguration;
: m_selectedLocaleConfiguration.toMap(); }
void
Config::setLanguageExplicitly( const QString& language )
{
m_selectedLocaleConfiguration.setLanguage( language );
m_selectedLocaleConfiguration.explicit_lang = true;
emit currentLanguageStatusChanged( currentLanguageStatus() );
emit currentLanguageCodeChanged( currentLanguageCode() );
}
void
Config::setLCLocaleExplicitly( const QString& locale )
{
// TODO: improve the granularity of this setting.
m_selectedLocaleConfiguration.lc_numeric = locale;
m_selectedLocaleConfiguration.lc_time = locale;
m_selectedLocaleConfiguration.lc_monetary = locale;
m_selectedLocaleConfiguration.lc_paper = locale;
m_selectedLocaleConfiguration.lc_name = locale;
m_selectedLocaleConfiguration.lc_address = locale;
m_selectedLocaleConfiguration.lc_telephone = locale;
m_selectedLocaleConfiguration.lc_measurement = locale;
m_selectedLocaleConfiguration.lc_identification = locale;
m_selectedLocaleConfiguration.explicit_lc = true;
emit currentLCStatusChanged( currentLCStatus() );
emit currentLCCodeChanged( currentLCCode() );
}
QString
Config::currentLocationStatus() const
{
return tr( "Set timezone to %1/%2." ).arg( m_currentLocation->region(), m_currentLocation->zone() );
}
static inline QString
localeLabel( const QString& s )
{
using CalamaresUtils::Locale::Label;
Label lang( s, Label::LabelFormat::AlwaysWithCountry );
return lang.label();
}
QString
Config::currentLanguageStatus() const
{
return tr( "The system language will be set to %1." )
.arg( localeLabel( m_selectedLocaleConfiguration.language() ) );
}
QString
Config::currentLCStatus() const
{
return tr( "The numbers and dates locale will be set to %1." )
.arg( localeLabel( m_selectedLocaleConfiguration.lc_numeric ) );
} }
QString QString
Config::prettyStatus() const Config::prettyStatus() const
{ {
QString status; QStringList l { currentLocationStatus(), currentLanguageStatus(), currentLCStatus() };
status += tr( "Set timezone to %1/%2.<br/>" ) return l.join( QStringLiteral( "<br/>" ) );
.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 + "<br/>";
status += labels.second + "<br/>";
return status;
} }
static inline void
const CalamaresUtils::Locale::TZZone* getLocaleGenLines( const QVariantMap& configurationMap, QStringList& localeGenLines )
Config::currentLocation() const
{ {
return static_cast< const CalamaresUtils::Locale::TZZone* >( m_zonesModel->item( m_zonesModel->currentIndex() ) ); QString localeGenPath = CalamaresUtils::getString( configurationMap, "localeGenPath" );
if ( localeGenPath.isEmpty() )
{
localeGenPath = QStringLiteral( "/etc/locale.gen" );
}
localeGenLines = loadLocales( localeGenPath );
}
static inline void
getAdjustLiveTimezone( const QVariantMap& configurationMap, bool& adjustLiveTimezone )
{
adjustLiveTimezone = CalamaresUtils::getBool(
configurationMap, "adjustLiveTimezone", Calamares::Settings::instance()->doChroot() );
#ifdef DEBUG_TIMEZONES
if ( m_adjustLiveTimezone )
{
cWarning() << "Turning off live-timezone adjustments because debugging is on.";
adjustLiveTimezone = false;
}
#endif
#ifdef __FreeBSD__
if ( adjustLiveTimezone )
{
cWarning() << "Turning off live-timezone adjustments on FreeBSD.";
adjustLiveTimezone = false;
}
#endif
}
static inline void
getStartingTimezone( const QVariantMap& configurationMap, CalamaresUtils::GeoIP::RegionZonePair& startingTimezone )
{
QString region = CalamaresUtils::getString( configurationMap, "region" );
QString zone = CalamaresUtils::getString( configurationMap, "zone" );
if ( !region.isEmpty() && !zone.isEmpty() )
{
startingTimezone = CalamaresUtils::GeoIP::RegionZonePair( region, zone );
}
else
{
startingTimezone
= CalamaresUtils::GeoIP::RegionZonePair( QStringLiteral( "America" ), QStringLiteral( "New_York" ) );
}
if ( CalamaresUtils::getBool( configurationMap, "useSystemTimezone", false ) )
{
auto systemtz = CalamaresUtils::GeoIP::splitTZString( QTimeZone::systemTimeZoneId() );
if ( systemtz.isValid() )
{
cDebug() << "Overriding configured timezone" << startingTimezone << "with system timezone" << systemtz;
startingTimezone = systemtz;
}
}
}
static inline void
getGeoIP( const QVariantMap& configurationMap, std::unique_ptr< CalamaresUtils::GeoIP::Handler >& geoip )
{
bool ok = false;
QVariantMap map = CalamaresUtils::getSubMap( configurationMap, "geoip", ok );
if ( ok )
{
QString url = CalamaresUtils::getString( map, "url" );
QString style = CalamaresUtils::getString( map, "style" );
QString selector = CalamaresUtils::getString( map, "selector" );
geoip = std::make_unique< CalamaresUtils::GeoIP::Handler >( style, url, selector );
if ( !geoip->isValid() )
{
cWarning() << "GeoIP Style" << style << "is not recognized.";
}
}
}
void
Config::setConfigurationMap( const QVariantMap& configurationMap )
{
getLocaleGenLines( configurationMap, m_localeGenLines );
getAdjustLiveTimezone( configurationMap, m_adjustLiveTimezone );
getStartingTimezone( configurationMap, m_startingTimezone );
getGeoIP( configurationMap, m_geoip );
if ( m_geoip && m_geoip->isValid() )
{
connect(
Calamares::ModuleManager::instance(), &Calamares::ModuleManager::modulesLoaded, this, &Config::startGeoIP );
}
}
Calamares::JobList
Config::createJobs()
{
Calamares::JobList list;
const CalamaresUtils::Locale::TZZone* location = currentLocation();
if ( location )
{
Calamares::Job* j = new SetTimezoneJob( location->region(), location->zone() );
list.append( Calamares::job_ptr( j ) );
}
return list;
}
void
Config::startGeoIP()
{
if ( m_geoip && m_geoip->isValid() )
{
auto& network = CalamaresUtils::Network::Manager::instance();
if ( network.hasInternet() || network.synchronousPing( m_geoip->url() ) )
{
using Watcher = QFutureWatcher< CalamaresUtils::GeoIP::RegionZonePair >;
m_geoipWatcher = std::make_unique< Watcher >();
m_geoipWatcher->setFuture( m_geoip->query() );
connect( m_geoipWatcher.get(), &Watcher::finished, this, &Config::completeGeoIP );
}
}
}
void
Config::completeGeoIP()
{
if ( !currentLocation() )
{
auto r = m_geoipWatcher->result();
if ( r.isValid() )
{
m_startingTimezone = r;
}
else
{
cWarning() << "GeoIP returned invalid result.";
}
}
else
{
cWarning() << "GeoIP result ignored because a location is already set.";
}
m_geoipWatcher.reset();
m_geoip.reset();
} }

View File

@ -1,7 +1,8 @@
/* === This file is part of Calamares - <https://github.com/calamares> === /* === This file is part of Calamares - <https://github.com/calamares> ===
* *
* Copyright 2019-2020, Adriaan de Groot <groot@kde.org> * SPDX-FileCopyrightText: 2020 Adriaan de Groot <groot@kde.org>
* Copyright 2020, Camilo Higuita <milo.h@aol.com> * SPDX-License-Identifier: GPL-3.0-or-later
* License-Filename: LICENSE
* *
* Calamares is free software: you can redistribute it and/or modify * Calamares is free software: you can redistribute it and/or modify
* it under the terms of the GNU General Public License as published by * it under the terms of the GNU General Public License as published by
@ -23,9 +24,11 @@
#include "LocaleConfiguration.h" #include "LocaleConfiguration.h"
#include "Job.h" #include "Job.h"
#include "geoip/Handler.h"
#include "geoip/Interface.h"
#include "locale/TimeZone.h" #include "locale/TimeZone.h"
#include <QAbstractListModel> #include <QFutureWatcher>
#include <QObject> #include <QObject>
#include <memory> #include <memory>
@ -33,53 +36,150 @@
class Config : public QObject class Config : public QObject
{ {
Q_OBJECT Q_OBJECT
Q_PROPERTY( const QStringList& supportedLocales READ supportedLocales CONSTANT FINAL )
Q_PROPERTY( CalamaresUtils::Locale::CStringListModel* zonesModel READ zonesModel CONSTANT FINAL ) Q_PROPERTY( CalamaresUtils::Locale::CStringListModel* zonesModel READ zonesModel CONSTANT FINAL )
Q_PROPERTY( CalamaresUtils::Locale::CStringListModel* regionModel READ regionModel CONSTANT FINAL ) Q_PROPERTY( CalamaresUtils::Locale::CStringListModel* regionModel READ regionModel CONSTANT FINAL )
Q_PROPERTY( const CalamaresUtils::Locale::TZZone* currentLocation READ currentLocation WRITE setCurrentLocation
NOTIFY currentLocationChanged )
// Status are complete, human-readable, messages
Q_PROPERTY( QString currentLocationStatus READ currentLocationStatus NOTIFY currentLanguageStatusChanged )
Q_PROPERTY( QString currentLanguageStatus READ currentLanguageStatus NOTIFY currentLanguageStatusChanged )
Q_PROPERTY( QString currentLCStatus READ currentLCStatus NOTIFY currentLCStatusChanged )
// Code are internal identifiers, like "en_US.UTF-8"
Q_PROPERTY( QString currentLanguageCode READ currentLanguageCode WRITE setLanguageExplicitly NOTIFY currentLanguageCodeChanged )
Q_PROPERTY( QString currentLCCode READ currentLCCode WRITE setLCLocaleExplicitly NOTIFY currentLCCodeChanged )
// This is a long human-readable string with all three statuses
Q_PROPERTY( QString prettyStatus READ prettyStatus NOTIFY prettyStatusChanged FINAL ) Q_PROPERTY( QString prettyStatus READ prettyStatus NOTIFY prettyStatusChanged FINAL )
public: public:
Config( QObject* parent = nullptr ); Config( QObject* parent = nullptr );
~Config(); ~Config();
CalamaresUtils::Locale::CStringListModel* regionModel() const;
CalamaresUtils::Locale::CStringListModel* zonesModel() const;
void setLocaleInfo( const QString& initialRegion, const QString& initialZone, const QString& localeGenPath );
void setConfigurationMap( const QVariantMap& );
Calamares::JobList createJobs(); Calamares::JobList createJobs();
QMap< QString, QString > localesMap();
// Underlying data for the models
const CalamaresUtils::Locale::CStringPairList& timezoneData() const;
/** @brief The currently selected location (timezone)
*
* The location is a pointer into the date that timezoneData() returns.
*/
const CalamaresUtils::Locale::TZZone* currentLocation() const { return m_currentLocation; }
/// locale configuration (LC_* and LANG) based solely on the current location.
LocaleConfiguration automaticLocaleConfiguration() const;
/// locale configuration that takes explicit settings into account
LocaleConfiguration localeConfiguration() const;
/// The human-readable description of what timezone is used
QString currentLocationStatus() const;
/// The human-readable description of what language is used
QString currentLanguageStatus() const;
/// The human-readable description of what locale (LC_*) is used
QString currentLCStatus() const;
/// The human-readable summary of what the module will do
QString prettyStatus() const; QString prettyStatus() const;
private: const QStringList& supportedLocales() const { return m_localeGenLines; }
CalamaresUtils::Locale::CStringPairList m_regionList; CalamaresUtils::Locale::CStringListModel* regionModel() const { return m_regionModel.get(); }
CalamaresUtils::Locale::CStringListModel* m_regionModel; CalamaresUtils::Locale::CStringListModel* zonesModel() const { return m_zonesModel.get(); }
CalamaresUtils::Locale::CStringListModel* m_zonesModel;
LocaleConfiguration m_selectedLocaleConfiguration; /// Special case, set location from starting timezone if not already set
void setCurrentLocation();
QStringList m_localeGenLines; public Q_SLOTS:
int m_currentRegion = -1; /// Set a language by user-choice, overriding future location changes
void setLanguageExplicitly( const QString& language );
/// Set LC (formats) by user-choice, overriding future location changes
void setLCLocaleExplicitly( const QString& locale );
bool m_blockTzWidgetSet; /** @brief Sets a location by full name
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, * @p regionzone should be an identifier from zone.tab, e.g. "Africa/Abidjan",
* and writes it to GS *locale* key (as a string, in BCP47 format). * which is split into regon and zone. Invalid names will **not**
* change the actual location.
*/ */
void updateGlobalLocale(); void setCurrentLocation( const QString& regionzone );
void updateGlobalStorage(); /** @brief Sets a location by split name
void updateLocaleLabels(); *
* @p region should be "America" or the like, while @p zone
* names a zone within that region.
*/
void setCurrentLocation( const QString& region, const QString& zone );
/** @brief Sets a location by pointer
*
* Pointer should be within the same model as the widget uses.
* This can update the locale configuration -- the automatic one
* follows the current location, and otherwise only explicitly-set
* values will ignore changes to the location.
*/
void setCurrentLocation( const CalamaresUtils::Locale::TZZone* location );
const CalamaresUtils::Locale::TZZone* currentLocation() const; QString currentLanguageCode() const { return localeConfiguration().language(); }
QString currentLCCode() const { return localeConfiguration().lc_numeric; }
signals: signals:
void prettyStatusChanged(); void currentLocationChanged( const CalamaresUtils::Locale::TZZone* location ) const;
void currentLocationStatusChanged( const QString& ) const;
void currentLanguageStatusChanged( const QString& ) const;
void currentLCStatusChanged( const QString& ) const;
void prettyStatusChanged( const QString& ) const;
void currentLanguageCodeChanged( const QString& ) const;
void currentLCCodeChanged( const QString& ) const;
private:
/// A list of supported locale identifiers (e.g. "en_US.UTF-8")
QStringList m_localeGenLines;
/// The regions (America, Asia, Europe ..)
std::unique_ptr< CalamaresUtils::Locale::CStringListModel > m_regionModel;
/// The zones for the current region (e.g. America/New_York)
std::unique_ptr< CalamaresUtils::Locale::CStringListModel > m_zonesModel;
/// The location, points into the timezone data
const CalamaresUtils::Locale::TZZone* m_currentLocation = nullptr;
/** @brief Specific locale configurations
*
* "Automatic" locale configuration based on the location (e.g.
* Europe/Amsterdam means Dutch language and Dutch locale) leave
* this empty; if the user explicitly sets something, then
* this configuration is non-empty and takes precedence over the
* automatic location settings (so a user in Amsterdam can still
* pick Ukranian settings, for instance).
*/
LocaleConfiguration m_selectedLocaleConfiguration;
/** @brief Should we adjust the "live" timezone when the location changes?
*
* In the Widgets UI, clicking around on the world map adjusts the
* timezone, and the live system can be made to follow that.
*/
bool m_adjustLiveTimezone;
/** @brief The initial timezone (region, zone) specified in the config.
*
* This may be overridden by setting *useSystemTimezone* or by
* GeoIP settings.
*/
CalamaresUtils::GeoIP::RegionZonePair m_startingTimezone;
/** @brief Handler for GeoIP lookup (if configured)
*
* The GeoIP lookup needs to be started at some suitable time,
* by explicitly calling *TODO*
*/
std::unique_ptr< CalamaresUtils::GeoIP::Handler > m_geoip;
// Implementation details for doing GeoIP lookup
void startGeoIP();
void completeGeoIP();
std::unique_ptr< QFutureWatcher< CalamaresUtils::GeoIP::RegionZonePair > > m_geoipWatcher;
}; };

View File

@ -37,7 +37,7 @@ LocaleConfiguration::LocaleConfiguration( const QString& localeName, const QStri
lc_numeric = lc_time = lc_monetary = lc_paper = lc_name = lc_address = lc_telephone = lc_measurement lc_numeric = lc_time = lc_monetary = lc_paper = lc_name = lc_address = lc_telephone = lc_measurement
= lc_identification = formatsName; = lc_identification = formatsName;
(void)setLanguage( localeName ); setLanguage( localeName );
} }
@ -83,7 +83,7 @@ LocaleConfiguration::fromLanguageAndLocation( const QString& languageLocale,
if ( language == "pt" || language == "zh" ) if ( language == "pt" || language == "zh" )
{ {
QString proposedLocale = QString( "%1_%2" ).arg( language ).arg( countryCode ); QString proposedLocale = QString( "%1_%2" ).arg( language ).arg( countryCode );
foreach ( QString line, linesForLanguage ) for ( const QString& line : linesForLanguage )
{ {
if ( line.contains( proposedLocale ) ) if ( line.contains( proposedLocale ) )
{ {

View File

@ -26,36 +26,53 @@
class LocaleConfiguration class LocaleConfiguration
{ {
public: public: // TODO: private (but need to be public for tests)
/// @brief Create an empty locale, with nothing set /** @brief Create a locale with everything set to the given @p localeName
explicit LocaleConfiguration(); *
/// @brief Create a locale with everything set to the given @p localeName * Consumers should use fromLanguageAndLocation() instead.
*/
explicit LocaleConfiguration( const QString& localeName /* "en_US.UTF-8" */ ) explicit LocaleConfiguration( const QString& localeName /* "en_US.UTF-8" */ )
: LocaleConfiguration( localeName, localeName ) : LocaleConfiguration( localeName, localeName )
{ {
} }
/// @brief Create a locale with language and formats separate /** @brief Create a locale with language and formats separate
*
* Consumers should use fromLanguageAndLocation() instead.
*/
explicit LocaleConfiguration( const QString& localeName, const QString& formatsName ); explicit LocaleConfiguration( const QString& localeName, const QString& formatsName );
/// @brief Create an empty locale, with nothing set
explicit LocaleConfiguration();
/** @brief Create a "sensible" locale configuration for @p language and @p countryCode
*
* This method applies some heuristics to pick a good locale (from the list
* @p availableLocales), along with a good language (for instance, in
* large countries with many languages, picking a generally used one).
*/
static LocaleConfiguration static LocaleConfiguration
fromLanguageAndLocation( const QString& language, const QStringList& availableLocales, const QString& countryCode ); fromLanguageAndLocation( const QString& language, const QStringList& availableLocales, const QString& countryCode );
/// Is this an empty (default-constructed and not modified) configuration?
bool isEmpty() const; bool isEmpty() const;
/** @brief sets lang and the BCP47 representation /** @brief sets language to @p localeName
* *
* Note that the documentation how this works is in packages.conf * The language may be regionalized, e.g. "nl_BE". Both the language
* (with region) and BCP47 representation (without region, lowercase)
* are updated. The BCP47 representation is used by the packages module.
* See also `packages.conf` for a discussion of how this is used.
*/ */
void setLanguage( const QString& localeName ); void setLanguage( const QString& localeName );
/// Current language (including region)
QString language() const { return m_lang; } QString language() const { return m_lang; }
/// Current language (lowercase, BCP47 format, no region)
// Note that the documentation how this works is in packages.conf
QString toBcp47() const { return m_languageLocaleBcp47; } QString toBcp47() const { return m_languageLocaleBcp47; }
QMap< QString, QString > toMap() const; QMap< QString, QString > toMap() const;
// These become all uppercase in locale.conf, but we keep them lowercase here to // These become all uppercase in locale.conf, but we keep them lowercase here to
// avoid confusion with locale.h. // avoid confusion with <locale.h>, which defines (e.g.) LC_NUMERIC macro.
QString lc_numeric, lc_time, lc_monetary, lc_paper, lc_name, lc_address, lc_telephone, lc_measurement, QString lc_numeric, lc_time, lc_monetary, lc_paper, lc_name, lc_address, lc_telephone, lc_measurement,
lc_identification; lc_identification;

View File

@ -19,37 +19,30 @@
#include "LocalePage.h" #include "LocalePage.h"
#include "SetTimezoneJob.h" #include "Config.h"
#include "LCLocaleDialog.h"
#include "timezonewidget/timezonewidget.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/CalamaresUtilsGui.h"
#include "utils/Logger.h" #include "utils/Logger.h"
#include "utils/RAII.h"
#include "utils/Retranslator.h" #include "utils/Retranslator.h"
#include <QBoxLayout> #include <QBoxLayout>
#include <QComboBox> #include <QComboBox>
#include <QFile>
#include <QLabel> #include <QLabel>
#include <QProcess> #include <QPointer>
#include <QPushButton> #include <QPushButton>
LocalePage::LocalePage( QWidget* parent ) LocalePage::LocalePage( Config* config, QWidget* parent )
: QWidget( parent ) : QWidget( parent )
, m_regionList( CalamaresUtils::Locale::TZRegion::fromZoneTab() ) , m_config( config )
, m_regionModel( std::make_unique< CalamaresUtils::Locale::CStringListModel >( m_regionList ) )
, m_blockTzWidgetSet( false ) , m_blockTzWidgetSet( false )
{ {
QBoxLayout* mainLayout = new QVBoxLayout; QBoxLayout* mainLayout = new QVBoxLayout;
QBoxLayout* tzwLayout = new QHBoxLayout; QBoxLayout* tzwLayout = new QHBoxLayout;
m_tzWidget = new TimeZoneWidget( this ); m_tzWidget = new TimeZoneWidget( config->timezoneData(), this );
tzwLayout->addStretch(); tzwLayout->addStretch();
tzwLayout->addWidget( m_tzWidget ); tzwLayout->addWidget( m_tzWidget );
tzwLayout->addStretch(); tzwLayout->addStretch();
@ -105,9 +98,24 @@ LocalePage::LocalePage( QWidget* parent )
setMinimumWidth( m_tzWidget->width() ); setMinimumWidth( m_tzWidget->width() );
setLayout( mainLayout ); setLayout( mainLayout );
// Set up the location before connecting signals, to avoid a signal
// storm as various parts interact.
m_regionCombo->setModel( m_config->regionModel() );
locationChanged( m_config->currentLocation() ); // doesn't inform TZ widget
m_tzWidget->setCurrentLocation( m_config->currentLocation() );
connect( config, &Config::currentLCStatusChanged, m_formatsLabel, &QLabel::setText );
connect( config, &Config::currentLanguageStatusChanged, m_localeLabel, &QLabel::setText );
connect( config, &Config::currentLocationChanged, m_tzWidget, &TimeZoneWidget::setCurrentLocation );
connect( config, &Config::currentLocationChanged, this, &LocalePage::locationChanged );
connect( m_tzWidget,
&TimeZoneWidget::locationChanged,
config,
QOverload< const CalamaresUtils::Locale::TZZone* >::of( &Config::setCurrentLocation ) );
connect( m_regionCombo, QOverload< int >::of( &QComboBox::currentIndexChanged ), this, &LocalePage::regionChanged ); connect( m_regionCombo, QOverload< int >::of( &QComboBox::currentIndexChanged ), this, &LocalePage::regionChanged );
connect( m_zoneCombo, QOverload< int >::of( &QComboBox::currentIndexChanged ), this, &LocalePage::zoneChanged ); connect( m_zoneCombo, QOverload< int >::of( &QComboBox::currentIndexChanged ), this, &LocalePage::zoneChanged );
connect( m_tzWidget, &TimeZoneWidget::locationChanged, this, &LocalePage::locationChanged );
connect( m_localeChangeButton, &QPushButton::clicked, this, &LocalePage::changeLocale ); connect( m_localeChangeButton, &QPushButton::clicked, this, &LocalePage::changeLocale );
connect( m_formatsChangeButton, &QPushButton::clicked, this, &LocalePage::changeFormats ); connect( m_formatsChangeButton, &QPushButton::clicked, this, &LocalePage::changeFormats );
@ -115,10 +123,7 @@ LocalePage::LocalePage( QWidget* parent )
} }
LocalePage::~LocalePage() LocalePage::~LocalePage() {}
{
qDeleteAll( m_regionList );
}
void void
@ -128,175 +133,8 @@ LocalePage::updateLocaleLabels()
m_zoneLabel->setText( tr( "Zone:" ) ); m_zoneLabel->setText( tr( "Zone:" ) );
m_localeChangeButton->setText( tr( "&Change..." ) ); m_localeChangeButton->setText( tr( "&Change..." ) );
m_formatsChangeButton->setText( tr( "&Change..." ) ); m_formatsChangeButton->setText( tr( "&Change..." ) );
m_localeLabel->setText( m_config->currentLanguageStatus() );
LocaleConfiguration lc m_formatsLabel->setText( m_config->currentLCStatus() );
= m_selectedLocaleConfiguration.isEmpty() ? guessLocaleConfiguration() : m_selectedLocaleConfiguration;
auto labels = prettyLocaleStatus( lc );
m_localeLabel->setText( labels.first );
m_formatsLabel->setText( labels.second );
}
void
LocalePage::init( const QString& initialRegion, const QString& initialZone, const QString& localeGenPath )
{
using namespace CalamaresUtils::Locale;
m_regionCombo->setModel( m_regionModel.get() );
m_regionCombo->currentIndexChanged( m_regionCombo->currentIndex() );
auto* region = m_regionList.find< TZRegion >( initialRegion );
if ( region && region->zones().find< TZZone >( initialZone ) )
{
m_tzWidget->setCurrentLocation( initialRegion, initialZone );
}
else
{
m_tzWidget->setCurrentLocation( "America", "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.
auto notUtf8 = []( const QString& s ) {
return !s.contains( "UTF-8", Qt::CaseInsensitive ) && !s.contains( "utf8", Qt::CaseInsensitive );
};
auto it = std::remove_if( m_localeGenLines.begin(), m_localeGenLines.end(), notUtf8 );
m_localeGenLines.erase( it, m_localeGenLines.end() );
// We strip " UTF-8" from "en_US.UTF-8 UTF-8" because it's redundant redundant.
// Also simplify whitespace.
auto unredundant = []( QString& s ) {
if ( s.endsWith( " UTF-8" ) )
{
s.chop( 6 );
}
s = s.simplified();
};
std::for_each( m_localeGenLines.begin(), m_localeGenLines.end(), unredundant );
updateGlobalStorage();
}
std::pair< QString, QString >
LocalePage::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() ) );
}
QString
LocalePage::prettyStatus() const
{
QString status;
status += tr( "Set timezone to %1/%2.<br/>" ).arg( m_regionCombo->currentText() ).arg( m_zoneCombo->currentText() );
LocaleConfiguration lc
= m_selectedLocaleConfiguration.isEmpty() ? guessLocaleConfiguration() : m_selectedLocaleConfiguration;
auto labels = prettyLocaleStatus( lc );
status += labels.first + "<br/>";
status += labels.second + "<br/>";
return status;
}
Calamares::JobList
LocalePage::createJobs()
{
QList< Calamares::job_ptr > list;
const CalamaresUtils::Locale::TZZone* location = m_tzWidget->currentLocation();
Calamares::Job* j = new SetTimezoneJob( location->region(), location->zone() );
list.append( Calamares::job_ptr( j ) );
return list;
}
QMap< QString, QString >
LocalePage::localesMap()
{
return m_selectedLocaleConfiguration.isEmpty() ? guessLocaleConfiguration().toMap()
: m_selectedLocaleConfiguration.toMap();
} }
@ -304,82 +142,10 @@ void
LocalePage::onActivate() LocalePage::onActivate()
{ {
m_regionCombo->setFocus(); m_regionCombo->setFocus();
if ( m_selectedLocaleConfiguration.isEmpty() || !m_selectedLocaleConfiguration.explicit_lang )
{
auto newLocale = guessLocaleConfiguration();
m_selectedLocaleConfiguration.setLanguage( newLocale.language() );
updateGlobalLocale();
updateLocaleLabels();
}
}
LocaleConfiguration
LocalePage::guessLocaleConfiguration() const
{
return LocaleConfiguration::fromLanguageAndLocation(
QLocale().name(), m_localeGenLines, m_tzWidget->currentLocation()->country() );
}
void
LocalePage::updateGlobalLocale()
{
auto* gs = Calamares::JobQueue::instance()->globalStorage();
const QString bcp47 = m_selectedLocaleConfiguration.toBcp47();
gs->insert( "locale", bcp47 );
}
void
LocalePage::updateGlobalStorage()
{
auto* gs = Calamares::JobQueue::instance()->globalStorage();
const auto* location = m_tzWidget->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(); updateLocaleLabels();
} }
void void
LocalePage::regionChanged( int currentIndex ) LocalePage::regionChanged( int currentIndex )
{ {
@ -388,15 +154,17 @@ LocalePage::regionChanged( int currentIndex )
Q_UNUSED( currentIndex ) Q_UNUSED( currentIndex )
QString selectedRegion = m_regionCombo->currentData().toString(); QString selectedRegion = m_regionCombo->currentData().toString();
TZRegion* region = m_regionList.find< TZRegion >( selectedRegion ); TZRegion* region = m_config->timezoneData().find< TZRegion >( selectedRegion );
if ( !region ) if ( !region )
{ {
return; return;
} }
m_zoneCombo->blockSignals( true ); {
m_zoneCombo->setModel( new CStringListModel( region->zones() ) ); cSignalBlocker b( m_zoneCombo );
m_zoneCombo->blockSignals( false ); m_zoneCombo->setModel( new CStringListModel( region->zones() ) );
}
m_zoneCombo->currentIndexChanged( m_zoneCombo->currentIndex() ); m_zoneCombo->currentIndexChanged( m_zoneCombo->currentIndex() );
} }
@ -405,16 +173,19 @@ LocalePage::zoneChanged( int currentIndex )
{ {
Q_UNUSED( currentIndex ) Q_UNUSED( currentIndex )
if ( !m_blockTzWidgetSet ) if ( !m_blockTzWidgetSet )
m_tzWidget->setCurrentLocation( m_regionCombo->currentData().toString(), {
m_zoneCombo->currentData().toString() ); m_config->setCurrentLocation( m_regionCombo->currentData().toString(), m_zoneCombo->currentData().toString() );
}
updateGlobalStorage();
} }
void void
LocalePage::locationChanged( const CalamaresUtils::Locale::TZZone* location ) LocalePage::locationChanged( const CalamaresUtils::Locale::TZZone* location )
{ {
m_blockTzWidgetSet = true; if ( !location )
{
return;
}
cBoolSetter< true > b( m_blockTzWidgetSet );
// Set region index // Set region index
int index = m_regionCombo->findData( location->region() ); int index = m_regionCombo->findData( location->region() );
@ -433,58 +204,35 @@ LocalePage::locationChanged( const CalamaresUtils::Locale::TZZone* location )
} }
m_zoneCombo->setCurrentIndex( index ); m_zoneCombo->setCurrentIndex( index );
m_blockTzWidgetSet = false;
updateGlobalStorage();
} }
void void
LocalePage::changeLocale() LocalePage::changeLocale()
{ {
LCLocaleDialog* dlg QPointer< LCLocaleDialog > dlg(
= new LCLocaleDialog( m_selectedLocaleConfiguration.isEmpty() ? guessLocaleConfiguration().language() new LCLocaleDialog( m_config->localeConfiguration().language(), m_config->supportedLocales(), this ) );
: m_selectedLocaleConfiguration.language(),
m_localeGenLines,
this );
dlg->exec(); dlg->exec();
if ( dlg->result() == QDialog::Accepted && !dlg->selectedLCLocale().isEmpty() ) if ( dlg && dlg->result() == QDialog::Accepted && !dlg->selectedLCLocale().isEmpty() )
{ {
m_selectedLocaleConfiguration.setLanguage( dlg->selectedLCLocale() ); m_config->setLanguageExplicitly( dlg->selectedLCLocale() );
m_selectedLocaleConfiguration.explicit_lang = true; updateLocaleLabels();
this->updateGlobalLocale();
this->updateLocaleLabels();
} }
dlg->deleteLater(); delete dlg;
} }
void void
LocalePage::changeFormats() LocalePage::changeFormats()
{ {
LCLocaleDialog* dlg QPointer< LCLocaleDialog > dlg(
= new LCLocaleDialog( m_selectedLocaleConfiguration.isEmpty() ? guessLocaleConfiguration().lc_numeric new LCLocaleDialog( m_config->localeConfiguration().lc_numeric, m_config->supportedLocales(), this ) );
: m_selectedLocaleConfiguration.lc_numeric,
m_localeGenLines,
this );
dlg->exec(); dlg->exec();
if ( dlg->result() == QDialog::Accepted && !dlg->selectedLCLocale().isEmpty() ) if ( dlg && dlg->result() == QDialog::Accepted && !dlg->selectedLCLocale().isEmpty() )
{ {
// TODO: improve the granularity of this setting. m_config->setLCLocaleExplicitly( dlg->selectedLCLocale() );
m_selectedLocaleConfiguration.lc_numeric = dlg->selectedLCLocale(); updateLocaleLabels();
m_selectedLocaleConfiguration.lc_time = dlg->selectedLCLocale();
m_selectedLocaleConfiguration.lc_monetary = dlg->selectedLCLocale();
m_selectedLocaleConfiguration.lc_paper = dlg->selectedLCLocale();
m_selectedLocaleConfiguration.lc_name = dlg->selectedLCLocale();
m_selectedLocaleConfiguration.lc_address = dlg->selectedLCLocale();
m_selectedLocaleConfiguration.lc_telephone = dlg->selectedLCLocale();
m_selectedLocaleConfiguration.lc_measurement = dlg->selectedLCLocale();
m_selectedLocaleConfiguration.lc_identification = dlg->selectedLCLocale();
m_selectedLocaleConfiguration.explicit_lc = true;
this->updateLocaleLabels();
} }
dlg->deleteLater(); delete dlg;
} }

View File

@ -32,39 +32,23 @@
class QComboBox; class QComboBox;
class QLabel; class QLabel;
class QPushButton; class QPushButton;
class Config;
class TimeZoneWidget; class TimeZoneWidget;
class LocalePage : public QWidget class LocalePage : public QWidget
{ {
Q_OBJECT Q_OBJECT
public: public:
explicit LocalePage( QWidget* parent = nullptr ); explicit LocalePage( class Config* config, QWidget* parent = nullptr );
virtual ~LocalePage(); virtual ~LocalePage();
void init( const QString& initialRegion, const QString& initialZone, const QString& localeGenPath );
QString prettyStatus() const;
Calamares::JobList createJobs();
QMap< QString, QString > localesMap();
void onActivate(); void onActivate();
private: private:
LocaleConfiguration guessLocaleConfiguration() const; /// @brief Non-owning pointer to the ViewStep's config
Config* m_config;
// 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(); void updateLocaleLabels();
void regionChanged( int currentIndex ); void regionChanged( int currentIndex );
@ -73,10 +57,6 @@ private:
void changeLocale(); void changeLocale();
void changeFormats(); void changeFormats();
// Dynamically, QList< TZRegion* >
CalamaresUtils::Locale::CStringPairList m_regionList;
std::unique_ptr< CalamaresUtils::Locale::CStringListModel > m_regionModel;
TimeZoneWidget* m_tzWidget; TimeZoneWidget* m_tzWidget;
QComboBox* m_regionCombo; QComboBox* m_regionCombo;
QComboBox* m_zoneCombo; QComboBox* m_zoneCombo;
@ -88,9 +68,6 @@ private:
QLabel* m_formatsLabel; QLabel* m_formatsLabel;
QPushButton* m_formatsChangeButton; QPushButton* m_formatsChangeButton;
LocaleConfiguration m_selectedLocaleConfiguration;
QStringList m_localeGenLines;
bool m_blockTzWidgetSet; bool m_blockTzWidgetSet;
}; };

View File

@ -34,7 +34,6 @@
#include <QBoxLayout> #include <QBoxLayout>
#include <QLabel> #include <QLabel>
#include <QtConcurrent/QtConcurrentRun>
CALAMARES_PLUGIN_FACTORY_DEFINITION( LocaleViewStepFactory, registerPlugin< LocaleViewStep >(); ) CALAMARES_PLUGIN_FACTORY_DEFINITION( LocaleViewStepFactory, registerPlugin< LocaleViewStep >(); )
@ -44,7 +43,7 @@ LocaleViewStep::LocaleViewStep( QObject* parent )
, m_widget( new QWidget() ) , m_widget( new QWidget() )
, m_actualWidget( nullptr ) , m_actualWidget( nullptr )
, m_nextEnabled( false ) , m_nextEnabled( false )
, m_geoip( nullptr ) , m_config( std::make_unique< Config >() )
{ {
QBoxLayout* mainLayout = new QHBoxLayout; QBoxLayout* mainLayout = new QHBoxLayout;
m_widget->setLayout( mainLayout ); m_widget->setLayout( mainLayout );
@ -66,11 +65,11 @@ LocaleViewStep::~LocaleViewStep()
void void
LocaleViewStep::setUpPage() LocaleViewStep::setUpPage()
{ {
m_config->setCurrentLocation();
if ( !m_actualWidget ) if ( !m_actualWidget )
{ {
m_actualWidget = new LocalePage(); m_actualWidget = new LocalePage( m_config.get() );
} }
m_actualWidget->init( m_startingTimezone.first, m_startingTimezone.second, m_localeGenPath );
m_widget->layout()->addWidget( m_actualWidget ); m_widget->layout()->addWidget( m_actualWidget );
ensureSize( m_actualWidget->sizeHint() ); ensureSize( m_actualWidget->sizeHint() );
@ -80,20 +79,6 @@ LocaleViewStep::setUpPage()
} }
void
LocaleViewStep::fetchGeoIpTimezone()
{
if ( m_geoip && m_geoip->isValid() )
{
m_startingTimezone = m_geoip->get();
if ( !m_startingTimezone.isValid() )
{
cWarning() << "GeoIP lookup at" << m_geoip->url() << "failed.";
}
}
}
QString QString
LocaleViewStep::prettyName() const LocaleViewStep::prettyName() const
{ {
@ -104,7 +89,7 @@ LocaleViewStep::prettyName() const
QString QString
LocaleViewStep::prettyStatus() const LocaleViewStep::prettyStatus() const
{ {
return m_prettyStatus; return m_config->prettyStatus();
} }
@ -146,7 +131,7 @@ LocaleViewStep::isAtEnd() const
Calamares::JobList Calamares::JobList
LocaleViewStep::jobs() const LocaleViewStep::jobs() const
{ {
return m_jobs; return m_config->createJobs();
} }
@ -164,83 +149,11 @@ LocaleViewStep::onActivate()
void void
LocaleViewStep::onLeave() LocaleViewStep::onLeave()
{ {
if ( m_actualWidget )
{
m_jobs = m_actualWidget->createJobs();
m_prettyStatus = m_actualWidget->prettyStatus();
auto map = m_actualWidget->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 void
LocaleViewStep::setConfigurationMap( const QVariantMap& configurationMap ) LocaleViewStep::setConfigurationMap( const QVariantMap& configurationMap )
{ {
QString region = CalamaresUtils::getString( configurationMap, "region" ); m_config->setConfigurationMap( configurationMap );
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.";
}
}
}
Calamares::RequirementsList
LocaleViewStep::checkRequirements()
{
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();
} }

View File

@ -20,15 +20,11 @@
#ifndef LOCALEVIEWSTEP_H #ifndef LOCALEVIEWSTEP_H
#define LOCALEVIEWSTEP_H #define LOCALEVIEWSTEP_H
#include "geoip/Handler.h" #include "Config.h"
#include "geoip/Interface.h"
#include "utils/PluginFactory.h"
#include "viewpages/ViewStep.h"
#include "DllMacro.h" #include "DllMacro.h"
#include "utils/PluginFactory.h"
#include <QFutureWatcher> #include "viewpages/ViewStep.h"
#include <QObject>
#include <memory> #include <memory>
@ -60,25 +56,16 @@ public:
void setConfigurationMap( const QVariantMap& configurationMap ) override; void setConfigurationMap( const QVariantMap& configurationMap ) override;
/// @brief Do setup (returns empty list) asynchronously
virtual Calamares::RequirementsList checkRequirements() override;
private slots: private slots:
void setUpPage(); void setUpPage();
private: private:
void fetchGeoIpTimezone();
QWidget* m_widget; QWidget* m_widget;
LocalePage* m_actualWidget; LocalePage* m_actualWidget;
bool m_nextEnabled; bool m_nextEnabled;
QString m_prettyStatus;
CalamaresUtils::GeoIP::RegionZonePair m_startingTimezone; std::unique_ptr< Config > m_config;
QString m_localeGenPath;
Calamares::JobList m_jobs;
std::unique_ptr< CalamaresUtils::GeoIP::Handler > m_geoip;
}; };
CALAMARES_PLUGIN_FACTORY_DECLARATION( LocaleViewStepFactory ) CALAMARES_PLUGIN_FACTORY_DECLARATION( LocaleViewStepFactory )

View File

@ -1,5 +1,5 @@
--- ---
# This settings are used to set your default system time zone. # These settings are used to set your default system time zone.
# Time zones are usually located under /usr/share/zoneinfo and # Time zones are usually located under /usr/share/zoneinfo and
# provided by the 'tzdata' package of your Distribution. # provided by the 'tzdata' package of your Distribution.
# #
@ -11,20 +11,41 @@
# the locale page can be set through keys *region* and *zone*. # the locale page can be set through keys *region* and *zone*.
# If either is not set, defaults to America/New_York. # If either is not set, defaults to America/New_York.
# #
# Note that useSystemTimezone and GeoIP settings can change the
# starting time zone.
#
region: "America" region: "America"
zone: "New_York" zone: "New_York"
# Instead of using *region* and *zone* specified above,
# you can use the system's notion of the timezone, instead.
# This can help if your system is automatically configured with
# a sensible TZ rather than chasing a fixed default.
#
# The default is false.
#
# useSystemTimezone: true
# Should changing the system location (e.g. clicking around on the timezone
# map) immediately reflect the changed timezone in the live system?
# By default, installers (with a target system) do, and setup (e.g. OEM
# configuration) does not, but you can switch it on here (or off, if
# you think it's annoying in the installer).
#
# Note that not all systems support live adjustment.
#
# adjustLiveTimezone: true
# System locales are detected in the following order: # System locales are detected in the following order:
# #
# - /usr/share/i18n/SUPPORTED # - /usr/share/i18n/SUPPORTED
# - localeGenPath (defaults to /etc/locale.gen if not set) # - localeGenPath (defaults to /etc/locale.gen if not set)
# - 'locale -a' output # - `locale -a` output
# #
# Enable only when your Distribution is using an # Enable only when your Distribution is using a
# custom path for locale.gen # custom path for locale.gen
# #
#localeGenPath: "PATH_TO/locale.gen" #localeGenPath: "/etc/locale.gen"
# GeoIP based Language settings: Leave commented out to disable GeoIP. # GeoIP based Language settings: Leave commented out to disable GeoIP.
# #

View File

@ -4,7 +4,35 @@ $id: https://calamares.io/schemas/locale
additionalProperties: false additionalProperties: false
type: object type: object
properties: properties:
"region": { type: str } region: { type: string,
"zone": { type: str } enum: [
"localeGenPath": { type: string, required: true } Africa,
"geoipUrl": { type: str } America,
Antarctica,
Arctic,
Asia,
Atlantic,
Australia,
Europe,
Indian,
Pacific
]
}
zone: { type: string }
useSystemTimezone: { type: boolean, default: false }
adjustLiveTimezone: { type: boolean, default: true }
localeGenPath: { type: string }
# TODO: refactor, this is reused in welcome
geoip:
additionalProperties: false
type: object
properties:
style: { type: string, enum: [ none, fixed, xml, json ] }
url: { type: string }
selector: { type: string }
required: [ style, url, selector ]
required: [ region, zone ]

View File

@ -25,9 +25,9 @@
#include <cmath> #include <cmath>
static const char* zoneNames[] static const char* zoneNames[]
= { "0.0", "1.0", "2.0", "3.0", "3.5", "4.0", "4.5", "5.0", "5.5", "5.75", "6.0", "6.5", "7.0", = { "0.0", "1.0", "2.0", "3.0", "3.5", "4.0", "4.5", "5.0", "5.5", "5.75", "6.0", "6.5", "7.0",
"8.0", "9.0", "9.5", "10.0", "10.5", "11.0", "12.0", "12.75", "13.0", "-1.0", "-2.0", "-3.0", "8.0", "9.0", "9.5", "10.0", "10.5", "11.0", "12.0", "12.75", "13.0", "-1.0", "-2.0", "-3.0", "-3.5",
"-3.5", "-4.0", "-4.5", "-5.0", "-5.5", "-6.0", "-7.0", "-8.0", "-9.0", "-9.5", "-10.0", "-11.0" }; "-4.0", "-4.5", "-5.0", "-5.5", "-6.0", "-7.0", "-8.0", "-9.0", "-9.5", "-10.0", "-11.0" };
static_assert( TimeZoneImageList::zoneCount == ( sizeof( zoneNames ) / sizeof( zoneNames[ 0 ] ) ), static_assert( TimeZoneImageList::zoneCount == ( sizeof( zoneNames ) / sizeof( zoneNames[ 0 ] ) ),
"Incorrect number of zones" ); "Incorrect number of zones" );

View File

@ -34,9 +34,17 @@
#define ZONE_NAME QStringLiteral( "zone" ) #define ZONE_NAME QStringLiteral( "zone" )
#endif #endif
TimeZoneWidget::TimeZoneWidget( QWidget* parent ) static QPoint
getLocationPosition( const CalamaresUtils::Locale::TZZone* l )
{
return TimeZoneImageList::getLocationPosition( l->longitude(), l->latitude() );
}
TimeZoneWidget::TimeZoneWidget( const CalamaresUtils::Locale::CStringPairList& zones, QWidget* parent )
: QWidget( parent ) : QWidget( parent )
, timeZoneImages( TimeZoneImageList::fromQRC() ) , timeZoneImages( TimeZoneImageList::fromQRC() )
, m_zonesData( zones )
{ {
setMouseTracking( false ); setMouseTracking( false );
setCursor( Qt::PointingHandCursor ); setCursor( Qt::PointingHandCursor );
@ -57,27 +65,13 @@ TimeZoneWidget::TimeZoneWidget( QWidget* parent )
void void
TimeZoneWidget::setCurrentLocation( QString regionName, QString zoneName ) TimeZoneWidget::setCurrentLocation( const CalamaresUtils::Locale::TZZone* location )
{ {
using namespace CalamaresUtils::Locale; if ( location == m_currentLocation )
const auto& regions = TZRegion::fromZoneTab();
auto* region = regions.find< TZRegion >( regionName );
if ( !region )
{ {
return; return;
} }
auto* zone = region->zones().find< TZZone >( zoneName );
if ( zone )
{
setCurrentLocation( zone );
}
}
void
TimeZoneWidget::setCurrentLocation( const CalamaresUtils::Locale::TZZone* location )
{
m_currentLocation = location; m_currentLocation = location;
// Set zone // Set zone
@ -93,7 +87,6 @@ TimeZoneWidget::setCurrentLocation( const CalamaresUtils::Locale::TZZone* locati
// Repaint widget // Repaint widget
repaint(); repaint();
emit locationChanged( m_currentLocation );
} }
@ -101,11 +94,18 @@ TimeZoneWidget::setCurrentLocation( const CalamaresUtils::Locale::TZZone* locati
//### Private //### Private
//### //###
struct PainterEnder
{
QPainter& p;
~PainterEnder() { p.end(); }
};
void void
TimeZoneWidget::paintEvent( QPaintEvent* ) TimeZoneWidget::paintEvent( QPaintEvent* )
{ {
QFontMetrics fontMetrics( font ); QFontMetrics fontMetrics( font );
QPainter painter( this ); QPainter painter( this );
PainterEnder painter_end { painter };
painter.setRenderHint( QPainter::Antialiasing ); painter.setRenderHint( QPainter::Antialiasing );
painter.setFont( font ); painter.setFont( font );
@ -116,6 +116,11 @@ TimeZoneWidget::paintEvent( QPaintEvent* )
// Draw zone image // Draw zone image
painter.drawImage( 0, 0, currentZoneImage ); painter.drawImage( 0, 0, currentZoneImage );
if ( !m_currentLocation )
{
return;
}
#ifdef DEBUG_TIMEZONES #ifdef DEBUG_TIMEZONES
QPoint point = getLocationPosition( m_currentLocation ); QPoint point = getLocationPosition( m_currentLocation );
// Draw latitude lines // Draw latitude lines
@ -175,8 +180,6 @@ TimeZoneWidget::paintEvent( QPaintEvent* )
painter.setPen( Qt::white ); painter.setPen( Qt::white );
painter.drawText( rect.x() + 5, rect.bottom() - 4, m_currentLocation ? m_currentLocation->tr() : QString() ); painter.drawText( rect.x() + 5, rect.bottom() - 4, m_currentLocation ? m_currentLocation->tr() : QString() );
#endif #endif
painter.end();
} }
@ -194,7 +197,7 @@ TimeZoneWidget::mousePressEvent( QMouseEvent* event )
using namespace CalamaresUtils::Locale; using namespace CalamaresUtils::Locale;
const TZZone* closest = nullptr; const TZZone* closest = nullptr;
for ( const auto* region_p : TZRegion::fromZoneTab() ) for ( const auto* region_p : m_zonesData )
{ {
const auto* region = dynamic_cast< const TZRegion* >( region_p ); const auto* region = dynamic_cast< const TZRegion* >( region_p );
if ( region ) if ( region )
@ -222,6 +225,6 @@ TimeZoneWidget::mousePressEvent( QMouseEvent* event )
// Set zone image and repaint widget // Set zone image and repaint widget
setCurrentLocation( closest ); setCurrentLocation( closest );
// Emit signal // Emit signal
emit locationChanged( m_currentLocation ); emit locationChanged( closest );
} }
} }

View File

@ -31,32 +31,48 @@
#include <QFont> #include <QFont>
#include <QWidget> #include <QWidget>
/** @brief The TimeZoneWidget shows a map and reports where clicks happen
*
* This widget shows a map (unspecified whether it's a whole world map
* or can show regionsvia some kind of internal state). Mouse clicks are
* translated into timezone locations (e.g. the zone for America/New_York).
*
* The current location can be changed programmatically, by name
* or through a pointer to a location. If a pointer is used, take care
* that the pointer is to a zone in the same model as used by the
* widget.
*
* When a location is chosen -- by mouse click or programmatically --
* the locationChanged() signal is emitted with the new location.
*
* NOTE: the widget currently uses the globally cached TZRegion::fromZoneTab()
*/
class TimeZoneWidget : public QWidget class TimeZoneWidget : public QWidget
{ {
Q_OBJECT Q_OBJECT
public: public:
using TZZone = CalamaresUtils::Locale::TZZone; using TZZone = CalamaresUtils::Locale::TZZone;
explicit TimeZoneWidget( QWidget* parent = nullptr ); explicit TimeZoneWidget( const CalamaresUtils::Locale::CStringPairList& zones, QWidget* parent = nullptr );
void setCurrentLocation( QString region, QString zone ); public Q_SLOTS:
/** @brief Sets a location by pointer
*
* Pointer should be within the same model as the widget uses.
*/
void setCurrentLocation( const TZZone* location ); void setCurrentLocation( const TZZone* location );
const TZZone* currentLocation() { return m_currentLocation; }
signals: signals:
/** @brief The location has changed by mouse click */
void locationChanged( const TZZone* location ); void locationChanged( const TZZone* location );
private: private:
QFont font; QFont font;
QImage background, pin, currentZoneImage; QImage background, pin, currentZoneImage;
TimeZoneImageList timeZoneImages; TimeZoneImageList timeZoneImages;
const TZZone* m_currentLocation = nullptr; // Not owned by me
QPoint getLocationPosition( const TZZone* l ) const CalamaresUtils::Locale::CStringPairList& m_zonesData;
{ const TZZone* m_currentLocation = nullptr; // Not owned by me
return timeZoneImages.getLocationPosition( l->longitude(), l->latitude() );
}
void paintEvent( QPaintEvent* event ); void paintEvent( QPaintEvent* event );
void mousePressEvent( QMouseEvent* event ); void mousePressEvent( QMouseEvent* event );

View File

@ -19,74 +19,20 @@
#include "LocaleQmlViewStep.h" #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/Logger.h"
#include "utils/Variant.h"
#include "utils/Yaml.h"
#include "Branding.h"
#include "modulesystem/ModuleManager.h"
#include <QQmlEngine>
#include <QFutureWatcher>
#include <QPixmap>
#include <QVariant>
CALAMARES_PLUGIN_FACTORY_DEFINITION( LocaleQmlViewStepFactory, registerPlugin< LocaleQmlViewStep >(); ) CALAMARES_PLUGIN_FACTORY_DEFINITION( LocaleQmlViewStepFactory, registerPlugin< LocaleQmlViewStep >(); )
LocaleQmlViewStep::LocaleQmlViewStep( QObject* parent ) LocaleQmlViewStep::LocaleQmlViewStep( QObject* parent )
: Calamares::QmlViewStep( parent ) : Calamares::QmlViewStep( parent )
, m_config( new Config( this ) ) , m_config( std::make_unique< Config >( this ) )
, m_nextEnabled( false )
, m_geoip( nullptr )
{ {
emit nextStatusChanged( m_nextEnabled );
} }
QObject* QObject*
LocaleQmlViewStep::getConfig() LocaleQmlViewStep::getConfig()
{ {
return m_config; return m_config.get();
}
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()
{
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 QString
@ -95,17 +41,21 @@ LocaleQmlViewStep::prettyName() const
return tr( "Location" ); return tr( "Location" );
} }
QString
LocaleQmlViewStep::prettyStatus() const
{
return m_config->prettyStatus();
}
bool bool
LocaleQmlViewStep::isNextEnabled() const LocaleQmlViewStep::isNextEnabled() const
{ {
// TODO: should return true
return true; return true;
} }
bool bool
LocaleQmlViewStep::isBackEnabled() const LocaleQmlViewStep::isBackEnabled() const
{ {
// TODO: should return true (it's weird that you are not allowed to have welcome *after* anything
return true; return true;
} }
@ -113,7 +63,6 @@ LocaleQmlViewStep::isBackEnabled() const
bool bool
LocaleQmlViewStep::isAtBeginning() const LocaleQmlViewStep::isAtBeginning() const
{ {
// TODO: adjust to "pages" in the QML
return true; return true;
} }
@ -121,79 +70,18 @@ LocaleQmlViewStep::isAtBeginning() const
bool bool
LocaleQmlViewStep::isAtEnd() const LocaleQmlViewStep::isAtEnd() const
{ {
// TODO: adjust to "pages" in the QML
return true; return true;
} }
Calamares::JobList Calamares::JobList
LocaleQmlViewStep::jobs() const LocaleQmlViewStep::jobs() const
{ {
return m_jobs; return m_config->createJobs();
} }
void LocaleQmlViewStep::onActivate() void
LocaleQmlViewStep::setConfigurationMap( const QVariantMap& configurationMap )
{ {
// TODO no sure if it is needed at all or for the abstract class to start something m_config->setConfigurationMap( configurationMap );
} Calamares::QmlViewStep::setConfigurationMap( configurationMap ); // call parent implementation last
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
} }

View File

@ -20,14 +20,10 @@
#define LOCALE_QMLVIEWSTEP_H #define LOCALE_QMLVIEWSTEP_H
#include "Config.h" #include "Config.h"
#include "geoip/Handler.h"
#include "geoip/Interface.h" #include "DllMacro.h"
#include "utils/PluginFactory.h" #include "utils/PluginFactory.h"
#include "viewpages/QmlViewStep.h" #include "viewpages/QmlViewStep.h"
#include <DllMacro.h>
#include <QFutureWatcher>
#include <QObject>
#include <memory> #include <memory>
@ -39,6 +35,7 @@ public:
explicit LocaleQmlViewStep( QObject* parent = nullptr ); explicit LocaleQmlViewStep( QObject* parent = nullptr );
QString prettyName() const override; QString prettyName() const override;
QString prettyStatus() const override;
bool isNextEnabled() const override; bool isNextEnabled() const override;
bool isBackEnabled() const override; bool isBackEnabled() const override;
@ -47,28 +44,12 @@ public:
bool isAtEnd() const override; bool isAtEnd() const override;
Calamares::JobList jobs() const override; Calamares::JobList jobs() const override;
void onActivate() override;
void onLeave() override;
void setConfigurationMap( const QVariantMap& configurationMap ) override; void setConfigurationMap( const QVariantMap& configurationMap ) override;
QObject* getConfig() override; QObject* getConfig() override;
virtual Calamares::RequirementsList checkRequirements() override;
private: private:
// TODO: a generic QML viewstep should return a config object from a method std::unique_ptr< Config > m_config;
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 ) CALAMARES_PLUGIN_FACTORY_DECLARATION( LocaleQmlViewStepFactory )

View File

@ -74,6 +74,7 @@ Column {
var tz2 = responseJSON.timezoneId var tz2 = responseJSON.timezoneId
tzText.text = "Timezone: " + tz2 tzText.text = "Timezone: " + tz2
config.setCurrentLocation(tz2)
} }
} }
@ -126,7 +127,7 @@ Column {
anchorPoint.x: image.width/4 anchorPoint.x: image.width/4
anchorPoint.y: image.height anchorPoint.y: image.height
coordinate: QtPositioning.coordinate( coordinate: QtPositioning.coordinate(
map.center.latitude, map.center.latitude,
map.center.longitude) map.center.longitude)
//coordinate: QtPositioning.coordinate(40.730610, -73.935242) // New York //coordinate: QtPositioning.coordinate(40.730610, -73.935242) // New York
@ -156,7 +157,7 @@ Column {
map.center.longitude = coordinate.longitude map.center.longitude = coordinate.longitude
getTz(); getTz();
console.log(coordinate.latitude, coordinate.longitude) console.log(coordinate.latitude, coordinate.longitude)
} }
} }
@ -199,7 +200,7 @@ Column {
} }
Rectangle { Rectangle {
width: parent.width width: parent.width
height: 100 height: 100
anchors.horizontalCenter: parent.horizontalCenter anchors.horizontalCenter: parent.horizontalCenter

View File

@ -32,10 +32,6 @@ Item {
anchors.fill: parent anchors.fill: parent
} }
//Needs to come from Locale config
property var confLang: "en_US.UTF8"
property var confLocale: "nl_NL.UTF8"
Rectangle { Rectangle {
id: textArea id: textArea
x: 28 x: 28
@ -57,7 +53,7 @@ Item {
width: 240 width: 240
wrapMode: Text.WordWrap wrapMode: Text.WordWrap
text: qsTr("<h1>Languages</h1> </br> text: qsTr("<h1>Languages</h1> </br>
The system locale setting affects the language and character set for some command line user interface elements. The current setting is <strong>%1</strong>.").arg(confLang) The system locale setting affects the language and character set for some command line user interface elements. The current setting is <strong>%1</strong>.").arg(config.currentLanguageCode)
font.pointSize: 10 font.pointSize: 10
} }
} }
@ -76,8 +72,7 @@ Item {
id: list1 id: list1
focus: true focus: true
// bogus entries, need to come from Locale config model: config.supportedLocales
model: ["en_GB.UTF-8 UTF-8", "en_US.UTF-8 UTF-8 ", "nl_NL.UTF-8 UTF-8", "en_GB.UTF-8 UTF-8", "en_US.UTF-8 UTF-8 ", "nl_NL.UTF-8 UTF-8", "en_GB.UTF-8 UTF-8", "en_US.UTF-8 UTF-8 ", "nl_NL.UTF-8 UTF-8", "en_GB.UTF-8 UTF-8", "en_US.UTF-8 UTF-8 ", "nl_NL.UTF-8 UTF-8", "en_GB.UTF-8 UTF-8", "en_US.UTF-8 UTF-8 ", "nl_NL.UTF-8 UTF-8"]
currentIndex: 1 currentIndex: 1
highlight: Rectangle { highlight: Rectangle {
@ -95,17 +90,17 @@ Item {
} }
onClicked: { onClicked: {
list1.currentIndex = index list1.currentIndex = index
confLang = list1.currentIndex
} }
} }
} }
onCurrentItemChanged: { config.currentLanguageCode = model[currentIndex] } /* This works because model is a stringlist */
} }
} }
} }
} }
Column { Column {
id: i18n id: lc_numeric
x: 430 x: 430
y: 40 y: 40
@ -118,7 +113,7 @@ Item {
width: 240 width: 240
wrapMode: Text.WordWrap wrapMode: Text.WordWrap
text: qsTr("<h1>Locales</h1> </br> text: qsTr("<h1>Locales</h1> </br>
The system locale setting affects the language and character set for some command line user interface elements. The current setting is <strong>%1</strong>.").arg(confLocale) The system locale setting affects the numbers and dates format. The current setting is <strong>%1</strong>.").arg(config.currentLCCode)
font.pointSize: 10 font.pointSize: 10
} }
} }
@ -139,7 +134,7 @@ Item {
focus: true focus: true
// bogus entries, need to come from Locale config // bogus entries, need to come from Locale config
model: ["en_GB.UTF-8 UTF-8", "en_US.UTF-8 UTF-8 ", "nl_NL.UTF-8 UTF-8", "en_GB.UTF-8 UTF-8", "en_US.UTF-8 UTF-8 ", "nl_NL.UTF-8 UTF-8", "en_GB.UTF-8 UTF-8", "en_US.UTF-8 UTF-8 ", "nl_NL.UTF-8 UTF-8", "en_GB.UTF-8 UTF-8", "en_US.UTF-8 UTF-8 ", "nl_NL.UTF-8 UTF-8", "en_GB.UTF-8 UTF-8", "en_US.UTF-8 UTF-8 ", "nl_NL.UTF-8 UTF-8"] model: config.supportedLocales
currentIndex: 2 currentIndex: 2
highlight: Rectangle { highlight: Rectangle {
@ -154,11 +149,10 @@ Item {
cursorShape: Qt.PointingHandCursor cursorShape: Qt.PointingHandCursor
onClicked: { onClicked: {
list2.currentIndex = index list2.currentIndex = index
confLocale = list1.currentIndex
} }
} }
} }
onCurrentItemChanged: console.debug(currentIndex) onCurrentItemChanged: { config.currentLCCode = model[currentIndex]; } /* This works because model is a stringlist */
} }
} }
} }

View File

@ -31,41 +31,14 @@ Page {
property var confLang: "American English" property var confLang: "American English"
property var confLocale: "Nederland" property var confLocale: "Nederland"
//Needs to come from .conf/geoip
property var hasInternet: true
function getInt(format) {
var requestURL = "https://example.org/";
var xhr = new XMLHttpRequest;
xhr.onreadystatechange = function() {
if (xhr.readyState === XMLHttpRequest.DONE) {
if (xhr.status !== 200) {
console.log("Disconnected!!");
var connected = false
hasInternet = connected
return;
}
else {
console.log("Connected!!");
}
}
}
xhr.open("GET", requestURL, true);
xhr.send();
}
Component.onCompleted: {
getInt();
}
Loader { Loader {
id: image id: image
anchors.horizontalCenter: parent.horizontalCenter anchors.horizontalCenter: parent.horizontalCenter
width: parent.width width: parent.width
height: parent.height / 1.28 height: parent.height / 1.28
source: (hasInternet) ? "Map.qml" : "Offline.qml" // Network is in io.calamares.core
source: Network.hasInternet ? "Map.qml" : "Offline.qml"
} }
RowLayout { RowLayout {
@ -95,7 +68,7 @@ Page {
Label { Label {
Layout.fillWidth: true Layout.fillWidth: true
wrapMode: Text.WordWrap wrapMode: Text.WordWrap
text: qsTr("System language set to %1").arg(confLang) text: config.currentLanguageStatus
} }
Kirigami.Separator { Kirigami.Separator {
Layout.fillWidth: true Layout.fillWidth: true
@ -103,7 +76,7 @@ Page {
Label { Label {
Layout.fillWidth: true Layout.fillWidth: true
wrapMode: Text.WordWrap wrapMode: Text.WordWrap
text: qsTr("Numbers and dates locale set to %1").arg(confLocale) text: config.currentLCStatus
} }
} }
Button { Button {