[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.
This commit is contained in:
parent
05f3fbea05
commit
fce05acf1e
@ -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 )
|
||||
|
@ -23,16 +23,14 @@
|
||||
#include "TimeZone.h"
|
||||
|
||||
#include "utils/Logger.h"
|
||||
#include "utils/String.h"
|
||||
|
||||
#include <QFile>
|
||||
#include <QStringList>
|
||||
#include <QTextStream>
|
||||
|
||||
#include <cstring>
|
||||
|
||||
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() )
|
||||
|
||||
namespace CalamaresUtils
|
||||
{
|
||||
namespace Locale
|
||||
{
|
||||
|
||||
struct Private {
|
||||
};
|
||||
|
||||
static Private* privateInstance()
|
||||
{
|
||||
static Private* s_p = new Private;
|
||||
return s_p;
|
||||
}
|
||||
|
||||
RegionsModel::RegionsModel()
|
||||
: QAbstractListModel()
|
||||
, m_private( privateInstance() )
|
||||
{
|
||||
}
|
||||
|
||||
|
||||
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 )
|
||||
{
|
||||
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
|
||||
{
|
||||
// NOTE: context name must match what's used in zone-extractor.py
|
||||
return QObject::tr( m_human, "tz_names" );
|
||||
}
|
||||
|
||||
|
||||
CStringListModel::CStringListModel( CStringPairList l )
|
||||
: m_list( l )
|
||||
RegionsModel::~RegionsModel()
|
||||
{
|
||||
}
|
||||
|
||||
void
|
||||
CStringListModel::setList( CalamaresUtils::Locale::CStringPairList l )
|
||||
int RegionsModel::rowCount(const QModelIndex& parent) const
|
||||
{
|
||||
beginResetModel();
|
||||
m_list = l;
|
||||
endResetModel();
|
||||
return 0;
|
||||
}
|
||||
|
||||
int
|
||||
CStringListModel::rowCount( const QModelIndex& ) const
|
||||
QVariant RegionsModel::data(const QModelIndex& index, int role) const
|
||||
{
|
||||
return m_list.count();
|
||||
return QVariant();
|
||||
}
|
||||
|
||||
QVariant
|
||||
CStringListModel::data( const QModelIndex& index, int role ) 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();
|
||||
}
|
||||
|
||||
void
|
||||
CStringListModel::setCurrentIndex( int index )
|
||||
{
|
||||
if ( ( index < 0 ) || ( index >= m_list.count() ) )
|
||||
{
|
||||
return;
|
||||
}
|
||||
|
||||
m_currentIndex = index;
|
||||
emit currentIndexChanged();
|
||||
}
|
||||
|
||||
int
|
||||
CStringListModel::currentIndex() const
|
||||
{
|
||||
return m_currentIndex;
|
||||
}
|
||||
|
||||
QHash< int, QByteArray >
|
||||
CStringListModel::roleNames() const
|
||||
{
|
||||
return { { Qt::DisplayRole, "label" }, { Qt::UserRole, "key" } };
|
||||
}
|
||||
|
||||
const CStringPair*
|
||||
CStringListModel::item( int index ) const
|
||||
{
|
||||
if ( ( index < 0 ) || ( index >= m_list.count() ) )
|
||||
{
|
||||
return nullptr;
|
||||
}
|
||||
return m_list[ index ];
|
||||
}
|
||||
|
||||
} // namespace Locale
|
||||
} // namespace CalamaresUtils
|
||||
|
@ -24,182 +24,36 @@
|
||||
|
||||
#include "DllMacro.h"
|
||||
|
||||
#include "utils/Logger.h"
|
||||
|
||||
#include <QAbstractListModel>
|
||||
#include <QObject>
|
||||
#include <QString>
|
||||
|
||||
#include <memory>
|
||||
#include <QVariant>
|
||||
|
||||
namespace CalamaresUtils
|
||||
{
|
||||
namespace Locale
|
||||
{
|
||||
struct Private;
|
||||
|
||||
/** @brief A pair of strings, one human-readable, one a key
|
||||
/** @brief The list of timezone regions
|
||||
*
|
||||
* 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<QString, QString> because there is API that needs
|
||||
* C-style strings.
|
||||
* The regions are a short list of global areas (Africa, America, India ..)
|
||||
* which contain zones.
|
||||
*/
|
||||
class CStringPair : public QObject
|
||||
class DLLEXPORT RegionsModel : public QAbstractListModel
|
||||
{
|
||||
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")
|
||||
*
|
||||
* 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.
|
||||
*/
|
||||
class TZZone : public CStringPair
|
||||
{
|
||||
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
|
||||
|
Loading…
Reference in New Issue
Block a user