From fce05acf1efd696dfaf0207a539a39a6cc6e3843 Mon Sep 17 00:00:00 2001 From: Adriaan de Groot Date: Wed, 5 Aug 2020 15:11:52 +0200 Subject: [PATCH] [libcalamares] Rip out all the TZ models - The models are overly complicated: **overall** there is just one list of timezones, and we need various views on that list. Start over with an empty model of regions. --- src/libcalamares/locale/Tests.cpp | 38 ----- src/libcalamares/locale/TimeZone.cpp | 226 +++------------------------ src/libcalamares/locale/TimeZone.h | 166 ++------------------ 3 files changed, 30 insertions(+), 400 deletions(-) diff --git a/src/libcalamares/locale/Tests.cpp b/src/libcalamares/locale/Tests.cpp index 10b4ad056..16b7d11a7 100644 --- a/src/libcalamares/locale/Tests.cpp +++ b/src/libcalamares/locale/Tests.cpp @@ -249,32 +249,6 @@ LocaleTests::testSimpleZones() { using namespace CalamaresUtils::Locale; - { - TZRegion r; - QVERIFY( r.tr().isEmpty() ); - } - { - TZZone n; - QVERIFY( n.tr().isEmpty() ); - } - { - TZZone r0( "xAmsterdam" ); - QCOMPARE( r0.tr(), QStringLiteral( "xAmsterdam" ) ); - TZZone r1( r0 ); - QCOMPARE( r0.tr(), QStringLiteral( "xAmsterdam" ) ); - QCOMPARE( r1.tr(), QStringLiteral( "xAmsterdam" ) ); - TZZone r2( std::move( r0 ) ); - QCOMPARE( r2.tr(), QStringLiteral( "xAmsterdam" ) ); - QCOMPARE( r0.tr(), QString() ); - } - { - TZZone r0( nullptr ); - QVERIFY( r0.tr().isEmpty() ); - TZZone r1( r0 ); - QVERIFY( r1.tr().isEmpty() ); - TZZone r2( std::move( r0 ) ); - QVERIFY( r2.tr().isEmpty() ); - } } void @@ -282,18 +256,6 @@ LocaleTests::testComplexZones() { using namespace CalamaresUtils::Locale; - { - TZZone r0( "America/New_York" ); - TZZone r1( "America/New York" ); - - QCOMPARE( r0.tr(), r1.tr() ); - QCOMPARE( r0.tr(), QStringLiteral( "America/New York" ) ); - } - { - TZZone r( "zxc,;*_vm" ); - QVERIFY( !r.tr().isEmpty() ); - QCOMPARE( r.tr(), QStringLiteral( "zxc,;* vm" ) ); // Only _ is special - } } QTEST_GUILESS_MAIN( LocaleTests ) diff --git a/src/libcalamares/locale/TimeZone.cpp b/src/libcalamares/locale/TimeZone.cpp index 772a242fb..37f94b6f2 100644 --- a/src/libcalamares/locale/TimeZone.cpp +++ b/src/libcalamares/locale/TimeZone.cpp @@ -23,16 +23,14 @@ #include "TimeZone.h" #include "utils/Logger.h" -#include "utils/String.h" - -#include -#include -#include - -#include static const char TZ_DATA_FILE[] = "/usr/share/zoneinfo/zone.tab"; +/** @brief Turns a string longitude or latitude notation into a double + * + * This handles strings like "+4230+00131" from zone.tab, + * which is degrees-and-minutes notation, and + means north or east. + */ static double getRightGeoLocation( QString str ) { @@ -61,28 +59,6 @@ getRightGeoLocation( QString str ) return sign * num; } - -namespace CalamaresUtils -{ -namespace Locale -{ - - -CStringPair::CStringPair( CStringPair&& t ) - : m_human( nullptr ) - , m_key() -{ - // My pointers are initialized to nullptr - std::swap( m_human, t.m_human ); - std::swap( m_key, t.m_key ); -} - -CStringPair::CStringPair( const CStringPair& t ) - : m_human( t.m_human ? strdup( t.m_human ) : nullptr ) - , m_key( t.m_key ) -{ -} - /** @brief Massage an identifier into a human-readable form * * Makes a copy of @p s, caller must free() it. @@ -110,204 +86,42 @@ munge( const char* s ) return t; } -CStringPair::CStringPair( const char* s1 ) - : m_human( s1 ? munge( s1 ) : nullptr ) - , m_key( s1 ? QString( s1 ) : QString() ) -{ -} - - -CStringPair::~CStringPair() -{ - free( m_human ); -} - - -QString -TZRegion::tr() const -{ - // NOTE: context name must match what's used in zone-extractor.py - return QObject::tr( m_human, "tz_regions" ); -} - -TZRegion::~TZRegion() -{ - qDeleteAll( m_zones ); -} - -const CStringPairList& -TZRegion::fromZoneTab() -{ - static CStringPairList zoneTab = TZRegion::fromFile( TZ_DATA_FILE ); - return zoneTab; -} - -CStringPairList -TZRegion::fromFile( const char* fileName ) -{ - CStringPairList model; - - QFile file( fileName ); - if ( !file.open( QIODevice::ReadOnly | QIODevice::Text ) ) - { - return model; - } - - TZRegion* thisRegion = nullptr; - QTextStream in( &file ); - while ( !in.atEnd() ) - { - QString line = in.readLine().trimmed().split( '#', SplitKeepEmptyParts ).first().trimmed(); - if ( line.isEmpty() ) - { - continue; - } - - QStringList list = line.split( QRegExp( "[\t ]" ), SplitSkipEmptyParts ); - if ( list.size() < 3 ) - { - continue; - } - - QStringList timezoneParts = list.at( 2 ).split( '/', SplitSkipEmptyParts ); - if ( timezoneParts.size() < 2 ) - { - continue; - } - - QString region = timezoneParts.first().trimmed(); - if ( region.isEmpty() ) - { - continue; - } - - auto keyMatch = [®ion]( const CStringPair* r ) { return r->key() == region; }; - auto it = std::find_if( model.begin(), model.end(), keyMatch ); - if ( it != model.end() ) - { - thisRegion = dynamic_cast< TZRegion* >( *it ); - } - else - { - thisRegion = new TZRegion( region.toUtf8().data() ); - model.append( thisRegion ); - } - - QString countryCode = list.at( 0 ).trimmed(); - if ( countryCode.size() != 2 ) - { - continue; - } - - timezoneParts.removeFirst(); - thisRegion->m_zones.append( - new TZZone( region, timezoneParts.join( '/' ).toUtf8().constData(), countryCode, list.at( 1 ) ) ); - } - - auto sorter = []( const CStringPair* l, const CStringPair* r ) { return *l < *r; }; - std::sort( model.begin(), model.end(), sorter ); - for ( auto& it : model ) - { - TZRegion* r = dynamic_cast< TZRegion* >( it ); - if ( r ) - { - std::sort( r->m_zones.begin(), r->m_zones.end(), sorter ); - } - } - - return model; -} -TZZone::TZZone( const QString& region, const char* zoneName, const QString& country, QString position ) - : CStringPair( zoneName ) - , m_region( region ) - , m_country( country ) +namespace CalamaresUtils { - int cooSplitPos = position.indexOf( QRegExp( "[-+]" ), 1 ); - if ( cooSplitPos > 0 ) - { - m_latitude = getRightGeoLocation( position.mid( 0, cooSplitPos ) ); - m_longitude = getRightGeoLocation( position.mid( cooSplitPos ) ); - } -} - -QString -TZZone::tr() const +namespace Locale { - // NOTE: context name must match what's used in zone-extractor.py - return QObject::tr( m_human, "tz_names" ); -} +struct Private { +}; -CStringListModel::CStringListModel( CStringPairList l ) - : m_list( l ) +static Private* privateInstance() { + static Private* s_p = new Private; + return s_p; } -void -CStringListModel::setList( CalamaresUtils::Locale::CStringPairList l ) +RegionsModel::RegionsModel() +: QAbstractListModel() +, m_private( privateInstance() ) { - beginResetModel(); - m_list = l; - endResetModel(); } -int -CStringListModel::rowCount( const QModelIndex& ) const +RegionsModel::~RegionsModel() { - return m_list.count(); } -QVariant -CStringListModel::data( const QModelIndex& index, int role ) const +int RegionsModel::rowCount(const QModelIndex& parent) const { - if ( ( role != Qt::DisplayRole ) && ( role != Qt::UserRole ) ) - { - return QVariant(); - } - - if ( !index.isValid() ) - { - return QVariant(); - } - - const auto* item = m_list.at( index.row() ); - return item ? ( role == Qt::DisplayRole ? item->tr() : item->key() ) : QVariant(); + return 0; } -void -CStringListModel::setCurrentIndex( int index ) +QVariant RegionsModel::data(const QModelIndex& index, int role) const { - if ( ( index < 0 ) || ( index >= m_list.count() ) ) - { - return; - } - - m_currentIndex = index; - emit currentIndexChanged(); -} - -int -CStringListModel::currentIndex() const -{ - return m_currentIndex; + return QVariant(); } -QHash< int, QByteArray > -CStringListModel::roleNames() const -{ - return { { Qt::DisplayRole, "label" }, { Qt::UserRole, "key" } }; -} -const CStringPair* -CStringListModel::item( int index ) const -{ - if ( ( index < 0 ) || ( index >= m_list.count() ) ) - { - return nullptr; - } - return m_list[ index ]; -} } // namespace Locale } // namespace CalamaresUtils diff --git a/src/libcalamares/locale/TimeZone.h b/src/libcalamares/locale/TimeZone.h index 05820817a..a34e03248 100644 --- a/src/libcalamares/locale/TimeZone.h +++ b/src/libcalamares/locale/TimeZone.h @@ -24,182 +24,36 @@ #include "DllMacro.h" -#include "utils/Logger.h" - #include #include -#include - -#include +#include namespace CalamaresUtils { namespace Locale { +struct Private; -/** @brief A pair of strings, one human-readable, one a key - * - * Given an identifier-like string (e.g. "New_York"), makes - * a human-readable version of that and keeps a copy of the - * identifier itself. - * - * This explicitly uses const char* instead of just being - * QPair because there is API that needs - * C-style strings. - */ -class CStringPair : public QObject -{ - Q_OBJECT -public: - /// @brief An empty pair - CStringPair() {} - /// @brief Given an identifier, create the pair - explicit CStringPair( const char* s1 ); - CStringPair( CStringPair&& t ); - CStringPair( const CStringPair& ); - virtual ~CStringPair(); - - /// @brief Give the localized human-readable form - virtual QString tr() const = 0; - QString key() const { return m_key; } - - bool operator<( const CStringPair& other ) const { return m_key < other.m_key; } - -protected: - char* m_human = nullptr; - QString m_key; -}; - -class CStringPairList : public QList< CStringPair* > -{ -public: - template < typename T > - T* find( const QString& key ) const - { - for ( auto* p : *this ) - { - if ( p->key() == key ) - { - return dynamic_cast< T* >( p ); - } - } - return nullptr; - } -}; - -/** @brief Timezone regions (e.g. "America") - * - * A region has a key and a human-readable name, but also - * a collection of associated timezone zones (TZZone, below). - * This class is not usually constructed, but uses fromFile() - * to load a complete tree structure of timezones. - */ -class TZRegion : public CStringPair -{ - Q_OBJECT -public: - using CStringPair::CStringPair; - virtual ~TZRegion() override; - TZRegion( const TZRegion& ) = delete; - QString tr() const override; - - QString region() const { return key(); } - - /** @brief Create list from a zone.tab-like file - * - * Returns a list of all the regions; each region has a list - * of zones within that region. Dyamically, the items in the - * returned list are TZRegions; their zones dynamically are - * TZZones even though all those lists have type CStringPairList. - * - * The list owns the regions, and the regions own their own list of zones. - * When getting rid of the list, remember to qDeleteAll() on it. - */ - static CStringPairList fromFile( const char* fileName ); - /// @brief Calls fromFile with the standard zone.tab name - static const CStringPairList& fromZoneTab(); - - const CStringPairList& zones() const { return m_zones; } - -private: - CStringPairList m_zones; -}; - -/** @brief Specific timezone zones (e.g. "New_York", "New York") +/** @brief The list of timezone regions * - * A timezone zone lives in a region, and has some associated - * data like the country (used to map likely languages) and latitude - * and longitude information. + * The regions are a short list of global areas (Africa, America, India ..) + * which contain zones. */ -class TZZone : public CStringPair +class DLLEXPORT RegionsModel : public QAbstractListModel { Q_OBJECT -public: - using CStringPair::CStringPair; - QString tr() const override; - - TZZone( const QString& region, const char* zoneName, const QString& country, QString position ); - - QString region() const { return m_region; } - QString zone() const { return key(); } - QString country() const { return m_country; } - double latitude() const { return m_latitude; } - double longitude() const { return m_longitude; } - -protected: - QString m_region; - QString m_country; - double m_latitude = 0.0, m_longitude = 0.0; -}; -class CStringListModel : public QAbstractListModel -{ - Q_OBJECT - Q_PROPERTY( int currentIndex READ currentIndex WRITE setCurrentIndex NOTIFY currentIndexChanged ) + RegionsModel(); public: - /// @brief Create empty model - CStringListModel() {} - /// @brief Create model from list (non-owning) - CStringListModel( CStringPairList ); + virtual ~RegionsModel() override; + static RegionsModel* instance(); int rowCount( const QModelIndex& parent ) const override; - QVariant data( const QModelIndex& index, int role ) const override; - const CStringPair* item( int index ) const; - QHash< int, QByteArray > roleNames() const override; - - void setCurrentIndex( int index ); - int currentIndex() const; - - void setList( CStringPairList ); - - inline int indexOf( const QString& key ) - { - const auto it = std::find_if( - m_list.constBegin(), m_list.constEnd(), [&]( const CalamaresUtils::Locale::CStringPair* item ) -> bool { - return item->key() == key; - } ); - - if ( it != m_list.constEnd() ) - { - // distance() is usually a long long - return int( std::distance( m_list.constBegin(), it ) ); - } - else - { - return -1; - } - } - - private: - CStringPairList m_list; - int m_currentIndex = -1; - -signals: - void currentIndexChanged(); + Private *m_private; }; } // namespace Locale