[locale] Re-do locale module with new TZ data

- The Config object now uses the re-done models and timezone data
- most of the properties of the locale Config are unchanged
- much less complication in extracting data from the zones model
This commit is contained in:
Adriaan de Groot 2020-08-06 01:27:03 +02:00
parent d814a3dba8
commit 626dd038da
7 changed files with 108 additions and 178 deletions

View File

@ -148,17 +148,11 @@ loadLocales( const QString& localeGenPath )
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 >() )
, m_regionModel( std::make_unique< CalamaresUtils::Locale::RegionsModel >() )
, m_zonesModel( std::make_unique< CalamaresUtils::Locale::ZonesModel >() )
, m_regionalZonesModel( std::make_unique< CalamaresUtils::Locale::RegionalZonesModel >( m_zonesModel.get() ) )
{
// Slightly unusual: connect to our *own* signals. Wherever the language
// or the location is changed, these signals are emitted, so hook up to
@ -208,12 +202,6 @@ Config::Config( QObject* parent )
Config::~Config() {}
const CalamaresUtils::Locale::CStringPairList&
Config::timezoneData() const
{
return ::timezoneData();
}
void
Config::setCurrentLocation()
{
@ -223,7 +211,8 @@ Config::setCurrentLocation()
}
}
void Config::setCurrentLocation(const QString& regionzone)
void
Config::setCurrentLocation( const QString& regionzone )
{
auto r = CalamaresUtils::GeoIP::splitTZString( regionzone );
if ( r.isValid() )
@ -236,8 +225,7 @@ void
Config::setCurrentLocation( const QString& regionName, const QString& zoneName )
{
using namespace CalamaresUtils::Locale;
auto* region = timezoneData().find< TZRegion >( regionName );
auto* zone = region ? region->zones().find< TZZone >( zoneName ) : nullptr;
auto* zone = m_zonesModel->find( regionName, zoneName );
if ( zone )
{
setCurrentLocation( zone );
@ -250,7 +238,7 @@ Config::setCurrentLocation( const QString& regionName, const QString& zoneName )
}
void
Config::setCurrentLocation( const CalamaresUtils::Locale::TZZone* location )
Config::setCurrentLocation( const CalamaresUtils::Locale::TimeZoneData* location )
{
if ( location != m_currentLocation )
{
@ -459,7 +447,7 @@ Calamares::JobList
Config::createJobs()
{
Calamares::JobList list;
const CalamaresUtils::Locale::TZZone* location = currentLocation();
const auto* location = currentLocation();
if ( location )
{

View File

@ -37,18 +37,20 @@ class Config : public QObject
{
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* regionModel READ regionModel CONSTANT FINAL )
Q_PROPERTY( CalamaresUtils::Locale::RegionsModel* regionModel READ regionModel CONSTANT FINAL )
Q_PROPERTY( CalamaresUtils::Locale::ZonesModel* zonesModel READ zonesModel CONSTANT FINAL )
Q_PROPERTY( QAbstractItemModel* regionalZonesModel READ regionalZonesModel CONSTANT FINAL )
Q_PROPERTY( const CalamaresUtils::Locale::TZZone* currentLocation READ currentLocation WRITE setCurrentLocation
NOTIFY currentLocationChanged )
Q_PROPERTY(
const CalamaresUtils::Locale::TimeZoneData* currentLocation READ currentLocation 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 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
@ -61,15 +63,6 @@ public:
void setConfigurationMap( const QVariantMap& );
Calamares::JobList createJobs();
// 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
@ -85,9 +78,16 @@ public:
/// The human-readable summary of what the module will do
QString prettyStatus() const;
// A long list of locale codes (e.g. en_US.UTF-8)
const QStringList& supportedLocales() const { return m_localeGenLines; }
CalamaresUtils::Locale::CStringListModel* regionModel() const { return m_regionModel.get(); }
CalamaresUtils::Locale::CStringListModel* zonesModel() const { return m_zonesModel.get(); }
// All the regions (Africa, America, ...)
CalamaresUtils::Locale::RegionsModel* regionModel() const { return m_regionModel.get(); }
// All of the timezones in the world, according to zone.tab
CalamaresUtils::Locale::ZonesModel* zonesModel() const { return m_zonesModel.get(); }
// This model can be filtered by region
CalamaresUtils::Locale::RegionalZonesModel* regionalZonesModel() const { return m_regionalZonesModel.get(); }
const CalamaresUtils::Locale::TimeZoneData* currentLocation() const { return m_currentLocation; }
/// Special case, set location from starting timezone if not already set
void setCurrentLocation();
@ -111,20 +111,17 @@ public Q_SLOTS:
* names a zone within that region.
*/
void setCurrentLocation( const QString& region, const QString& zone );
/** @brief Sets a location by pointer
/** @brief Sets a location by pointer to zone data.
*
* 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 );
void setCurrentLocation( const CalamaresUtils::Locale::TimeZoneData* tz );
QString currentLanguageCode() const { return localeConfiguration().language(); }
QString currentLCCode() const { return localeConfiguration().lc_numeric; }
signals:
void currentLocationChanged( const CalamaresUtils::Locale::TZZone* location ) const;
void currentLocationChanged( const CalamaresUtils::Locale::TimeZoneData* location ) const;
void currentLocationStatusChanged( const QString& ) const;
void currentLanguageStatusChanged( const QString& ) const;
void currentLCStatusChanged( const QString& ) const;
@ -137,12 +134,11 @@ private:
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;
std::unique_ptr< CalamaresUtils::Locale::RegionsModel > m_regionModel;
std::unique_ptr< CalamaresUtils::Locale::ZonesModel > m_zonesModel;
std::unique_ptr< CalamaresUtils::Locale::RegionalZonesModel > m_regionalZonesModel;
/// The location, points into the timezone data
const CalamaresUtils::Locale::TZZone* m_currentLocation = nullptr;
const CalamaresUtils::Locale::TimeZoneData* m_currentLocation = nullptr;
/** @brief Specific locale configurations
*

View File

@ -43,7 +43,7 @@ LocalePage::LocalePage( Config* config, QWidget* parent )
QBoxLayout* mainLayout = new QVBoxLayout;
QBoxLayout* tzwLayout = new QHBoxLayout;
m_tzWidget = new TimeZoneWidget( config->timezoneData(), this );
m_tzWidget = new TimeZoneWidget( m_config->zonesModel(), this );
tzwLayout->addStretch();
tzwLayout->addWidget( m_tzWidget );
tzwLayout->addStretch();
@ -102,6 +102,7 @@ LocalePage::LocalePage( Config* config, QWidget* parent )
// Set up the location before connecting signals, to avoid a signal
// storm as various parts interact.
m_regionCombo->setModel( m_config->regionModel() );
m_zoneCombo->setModel( m_config->regionalZonesModel() );
locationChanged( m_config->currentLocation() ); // doesn't inform TZ widget
m_tzWidget->setCurrentLocation( m_config->currentLocation() );
@ -112,7 +113,7 @@ LocalePage::LocalePage( Config* config, QWidget* parent )
connect( m_tzWidget,
&TimeZoneWidget::locationChanged,
config,
QOverload< const CalamaresUtils::Locale::TZZone* >::of( &Config::setCurrentLocation ) );
QOverload< const CalamaresUtils::Locale::TimeZoneData* >::of( &Config::setCurrentLocation ) );
connect( m_regionCombo, QOverload< int >::of( &QComboBox::currentIndexChanged ), this, &LocalePage::regionChanged );
connect( m_zoneCombo, QOverload< int >::of( &QComboBox::currentIndexChanged ), this, &LocalePage::zoneChanged );
@ -152,35 +153,26 @@ LocalePage::regionChanged( int currentIndex )
{
using namespace CalamaresUtils::Locale;
Q_UNUSED( currentIndex )
QString selectedRegion = m_regionCombo->currentData().toString();
TZRegion* region = m_config->timezoneData().find< TZRegion >( selectedRegion );
if ( !region )
QString selectedRegion = m_regionCombo->itemData( currentIndex ).toString();
{
return;
cSignalBlocker z( m_zoneCombo );
m_config->regionalZonesModel()->setRegion( selectedRegion );
}
{
cSignalBlocker b( m_zoneCombo );
m_zoneCombo->setModel( new CStringListModel( region->zones() ) );
}
m_zoneCombo->currentIndexChanged( m_zoneCombo->currentIndex() );
m_zoneCombo->currentIndexChanged( 0 );
}
void
LocalePage::zoneChanged( int currentIndex )
{
Q_UNUSED( currentIndex )
if ( !m_blockTzWidgetSet )
{
m_config->setCurrentLocation( m_regionCombo->currentData().toString(), m_zoneCombo->currentData().toString() );
m_config->setCurrentLocation( m_regionCombo->currentData().toString(),
m_zoneCombo->itemData( currentIndex ).toString() );
}
}
void
LocalePage::locationChanged( const CalamaresUtils::Locale::TZZone* location )
LocalePage::locationChanged( const CalamaresUtils::Locale::TimeZoneData* location )
{
if ( !location )
{

View File

@ -53,7 +53,7 @@ private:
void regionChanged( int currentIndex );
void zoneChanged( int currentIndex );
void locationChanged( const CalamaresUtils::Locale::TZZone* location );
void locationChanged( const CalamaresUtils::Locale::TimeZoneData* location );
void changeLocale();
void changeFormats();

View File

@ -22,6 +22,7 @@
#include "timezonewidget/TimeZoneImage.h"
#include "locale/TimeZone.h"
#include "utils/Logger.h"
#include <QtTest/QtTest>
@ -115,37 +116,35 @@ LocaleTests::testTZImages()
//
//
using namespace CalamaresUtils::Locale;
const CStringPairList& regions = TZRegion::fromZoneTab();
const ZonesModel m;
int overlapcount = 0;
for ( const auto* pr : regions )
for ( auto it = m.begin(); it; ++it )
{
const TZRegion* region = dynamic_cast< const TZRegion* >( pr );
QVERIFY( region );
QString region = m.data( m.index( it.index() ), ZonesModel::RegionRole ).toString();
QString zoneName = m.data( m.index( it.index() ), ZonesModel::KeyRole ).toString();
QVERIFY( !region.isEmpty() );
QVERIFY( !zoneName.isEmpty() );
const auto* zone = m.find( region, zoneName );
const auto* iterzone = *it;
Logger::setupLogLevel( Logger::LOGDEBUG );
cDebug() << "Region" << region->region() << "zones #" << region->zones().count();
Logger::setupLogLevel( Logger::LOGERROR );
QVERIFY( iterzone );
QVERIFY( zone );
QCOMPARE( zone, iterzone );
QCOMPARE( zone->zone(), zoneName );
QCOMPARE( zone->region(), region );
const auto zones = region->zones();
QVERIFY( zones.count() > 0 );
for ( const auto* pz : zones )
int overlap = 0;
auto pos = images.getLocationPosition( zone->longitude(), zone->latitude() );
QVERIFY( images.index( pos, overlap ) >= 0 );
QVERIFY( overlap > 0 ); // At least one image contains the spot
if ( overlap > 1 )
{
const TZZone* zone = dynamic_cast< const TZZone* >( pz );
QVERIFY( zone );
int overlap = 0;
auto pos = images.getLocationPosition( zone->longitude(), zone->latitude() );
QVERIFY( images.index( pos, overlap ) >= 0 );
QVERIFY( overlap > 0 ); // At least one image contains the spot
if ( overlap > 1 )
{
Logger::setupLogLevel( Logger::LOGDEBUG );
cDebug() << Logger::SubEntry << "Zone" << zone->zone() << pos;
(void)images.index( pos, overlap );
Logger::setupLogLevel( Logger::LOGERROR );
overlapcount++;
}
Logger::setupLogLevel( Logger::LOGDEBUG );
cDebug() << Logger::SubEntry << "Zone" << zone->zone() << pos;
(void)images.index( pos, overlap );
Logger::setupLogLevel( Logger::LOGERROR );
overlapcount++;
}
}
@ -168,12 +167,17 @@ operator<( const QPoint& l, const QPoint& r )
}
void
listAll( const QPoint& p, const CalamaresUtils::Locale::CStringPairList& zones )
listAll( const QPoint& p, const CalamaresUtils::Locale::ZonesModel& zones )
{
using namespace CalamaresUtils::Locale;
for ( const auto* pz : zones )
for ( auto it = zones.begin(); it; ++it )
{
const TZZone* zone = dynamic_cast< const TZZone* >( pz );
const auto* zone = *it;
if ( !zone )
{
cError() << Logger::SubEntry << "NULL zone";
return;
}
if ( p == TimeZoneImageList::getLocationPosition( zone->longitude(), zone->latitude() ) )
{
cError() << Logger::SubEntry << zone->zone();
@ -185,78 +189,36 @@ void
LocaleTests::testTZLocations()
{
using namespace CalamaresUtils::Locale;
const CStringPairList& regions = TZRegion::fromZoneTab();
ZonesModel zones;
int overlapcount = 0;
for ( const auto* pr : regions )
for ( auto it = zones.begin(); it; ++it )
{
const TZRegion* region = dynamic_cast< const TZRegion* >( pr );
QVERIFY( region );
Logger::setupLogLevel( Logger::LOGDEBUG );
cDebug() << "Region" << region->region() << "zones #" << region->zones().count();
Logger::setupLogLevel( Logger::LOGERROR );
std::set< QPoint > occupied;
const auto zones = region->zones();
QVERIFY( zones.count() > 0 );
for ( const auto* pz : zones )
{
const TZZone* zone = dynamic_cast< const TZZone* >( pz );
QVERIFY( zone );
const auto* zone = *it;
QVERIFY( zone );
auto pos = TimeZoneImageList::getLocationPosition( zone->longitude(), zone->latitude() );
if ( occupied.find( pos ) != occupied.end() )
{
cError() << "Zone" << zone->zone() << "occupies same spot as ..";
listAll( pos, zones );
overlapcount++;
}
occupied.insert( pos );
auto pos = TimeZoneImageList::getLocationPosition( zone->longitude(), zone->latitude() );
if ( occupied.find( pos ) != occupied.end() )
{
cError() << "Zone" << zone->zone() << "occupies same spot as ..";
listAll( pos, zones );
overlapcount++;
}
occupied.insert( pos );
}
QEXPECT_FAIL( "", "TZ Images contain pixel-overlaps", Continue );
QCOMPARE( overlapcount, 0 );
}
const CalamaresUtils::Locale::TZZone*
findZone( const QString& name )
{
using namespace CalamaresUtils::Locale;
const CStringPairList& regions = TZRegion::fromZoneTab();
for ( const auto* pr : regions )
{
const TZRegion* region = dynamic_cast< const TZRegion* >( pr );
if ( !region )
{
continue;
}
const auto zones = region->zones();
for ( const auto* pz : zones )
{
const TZZone* zone = dynamic_cast< const TZZone* >( pz );
if ( !zone )
{
continue;
}
if ( zone->zone() == name )
{
return zone;
}
}
}
return nullptr;
}
void
LocaleTests::testSpecificLocations()
{
const auto* gibraltar = findZone( "Gibraltar" );
const auto* ceuta = findZone( "Ceuta" );
CalamaresUtils::Locale::ZonesModel zones;
const auto* gibraltar = zones.find( "Europe", "Gibraltar" );
const auto* ceuta = zones.find( "Africa", "Ceuta" );
QVERIFY( gibraltar );
QVERIFY( ceuta );

View File

@ -35,13 +35,13 @@
#endif
static QPoint
getLocationPosition( const CalamaresUtils::Locale::TZZone* l )
getLocationPosition( const CalamaresUtils::Locale::TimeZoneData* l )
{
return TimeZoneImageList::getLocationPosition( l->longitude(), l->latitude() );
}
TimeZoneWidget::TimeZoneWidget( const CalamaresUtils::Locale::CStringPairList& zones, QWidget* parent )
TimeZoneWidget::TimeZoneWidget( const CalamaresUtils::Locale::ZonesModel* zones, QWidget* parent )
: QWidget( parent )
, timeZoneImages( TimeZoneImageList::fromQRC() )
, m_zonesData( zones )
@ -65,7 +65,7 @@ TimeZoneWidget::TimeZoneWidget( const CalamaresUtils::Locale::CStringPairList& z
void
TimeZoneWidget::setCurrentLocation( const CalamaresUtils::Locale::TZZone* location )
TimeZoneWidget::setCurrentLocation( const TimeZoneData* location )
{
if ( location == m_currentLocation )
{
@ -190,32 +190,24 @@ TimeZoneWidget::mousePressEvent( QMouseEvent* event )
{
return;
}
// Set nearest location
int nX = 999999, mX = event->pos().x();
int nY = 999999, mY = event->pos().y();
using namespace CalamaresUtils::Locale;
const TZZone* closest = nullptr;
for ( const auto* region_p : m_zonesData )
const TimeZoneData* closest = nullptr;
for ( auto it = m_zonesData->begin(); it; ++it )
{
const auto* region = dynamic_cast< const TZRegion* >( region_p );
if ( region )
const auto* zone = *it;
if ( zone )
{
for ( const auto* zone_p : region->zones() )
{
const auto* zone = dynamic_cast< const TZZone* >( zone_p );
if ( zone )
{
QPoint locPos = TimeZoneImageList::getLocationPosition( zone->longitude(), zone->latitude() );
QPoint locPos = TimeZoneImageList::getLocationPosition( zone->longitude(), zone->latitude() );
if ( ( abs( mX - locPos.x() ) + abs( mY - locPos.y() ) < abs( mX - nX ) + abs( mY - nY ) ) )
{
closest = zone;
nX = locPos.x();
nY = locPos.y();
}
}
if ( ( abs( mX - locPos.x() ) + abs( mY - locPos.y() ) < abs( mX - nX ) + abs( mY - nY ) ) )
{
closest = zone;
nX = locPos.x();
nY = locPos.y();
}
}
}

View File

@ -51,28 +51,28 @@ class TimeZoneWidget : public QWidget
{
Q_OBJECT
public:
using TZZone = CalamaresUtils::Locale::TZZone;
using TimeZoneData = CalamaresUtils::Locale::TimeZoneData;
explicit TimeZoneWidget( const CalamaresUtils::Locale::CStringPairList& zones, QWidget* parent = nullptr );
explicit TimeZoneWidget( const CalamaresUtils::Locale::ZonesModel* zones, QWidget* parent = nullptr );
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 TimeZoneData* location );
signals:
/** @brief The location has changed by mouse click */
void locationChanged( const TZZone* location );
void locationChanged( const TimeZoneData* location );
private:
QFont font;
QImage background, pin, currentZoneImage;
TimeZoneImageList timeZoneImages;
const CalamaresUtils::Locale::CStringPairList& m_zonesData;
const TZZone* m_currentLocation = nullptr; // Not owned by me
const CalamaresUtils::Locale::ZonesModel* m_zonesData;
const TimeZoneData* m_currentLocation = nullptr; // Not owned by me
void paintEvent( QPaintEvent* event );
void mousePressEvent( QMouseEvent* event );